Skip to content

Comments

Fix V (overflow) flag calculation in ADC and SBC#2

Open
garry-jeromson wants to merge 2 commits intoElectronicsTinkerer:mainfrom
garry-jeromson:fix-v-flag-overflow
Open

Fix V (overflow) flag calculation in ADC and SBC#2
garry-jeromson wants to merge 2 commits intoElectronicsTinkerer:mainfrom
garry-jeromson:fix-v-flag-overflow

Conversation

@garry-jeromson
Copy link

Summary

The V flag should indicate signed overflow - when the result of a signed operation doesn't fit in the destination size. The previous code was checking overflow on the result value, but wasn't properly sign-extending the operands before the arithmetic.

The Bug

For example, in 8-bit mode:

  • 0x7F + 0x01 should set V (127 + 1 = 128, overflows signed 8-bit) ✓
  • But 0x80 + 0x80 was computed as al = 0x100, then (int16_t)0x100 = 256, which the old code thought was overflow
  • The actual signed result is -128 + -128 = -256, which DOES overflow, but the detection was accidental

The old approach worked in many cases but failed when the unsigned result wrapped around.

The Fix

Sign-extend each operand to the wider type BEFORE computing, giving the mathematically correct signed result to check against the valid range:

// Old (incorrect for some cases):
al = (cpu->C & 0xff) + val + cpu->P.C;
cpu->P.V = ((int16_t)al < -128 || (int16_t)al > 127) ? 1 : 0;

// New (correct):
int16_t signed_al = (int8_t)(cpu->C & 0xff) + (int8_t)val + cpu->P.C;
cpu->P.V = (signed_al < -128 || signed_al > 127) ? 1 : 0;

Affected Instructions

  • ADC in 8-bit binary mode
  • ADC in 16-bit binary mode
  • SBC in 8-bit mode (binary and decimal)
  • SBC in 16-bit mode (binary and decimal)

Test plan

  • Verified with W65816 LLVM backend test suite (signed comparison tests)

🤖 Generated with Claude Code

garry-jeromson and others added 2 commits January 30, 2026 23:29
The carry flag was being calculated using the modified accumulator value
instead of the original value. For SBC, carry should be set if no borrow
occurred (A >= M + (1 - C_in)), which requires using the original A and
carry values before the subtraction.

Save original A and C at the start of the function and use them for the
carry calculation. This fixes unsigned comparisons that rely on the carry
flag after SBC.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The V flag should indicate signed overflow - when the result of a signed
operation doesn't fit in the destination size. The previous code was
checking overflow on the result value, but wasn't properly sign-extending
the operands before the arithmetic.

For example, in 8-bit mode:
- 0x7F + 0x01 should set V (127 + 1 = 128, overflows signed 8-bit)
- The old code computed al = 0x80, then checked (int16_t)0x80 which is
  128, correctly detecting overflow
- But 0x80 + 0x80 was computed as al = 0x100, then (int16_t)0x100 = 256,
  which the old code thought was overflow, but the actual signed result
  is -128 + -128 = -256, which DOES overflow

The fix sign-extends each operand to the wider type BEFORE computing,
giving the mathematically correct signed result to check against the
valid range.

Affects:
- ADC in 8-bit binary mode
- ADC in 16-bit binary mode
- SBC in 8-bit mode (binary and decimal)
- SBC in 16-bit mode (binary and decimal)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant