From 1139572509ba019d0d7afe6eec59c118499f6f92 Mon Sep 17 00:00:00 2001 From: Garry Jeromson Date: Fri, 30 Jan 2026 23:29:00 +0100 Subject: [PATCH 1/2] Fix SBC carry flag calculation 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 --- src/cpu/65816-ops.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/cpu/65816-ops.c b/src/cpu/65816-ops.c index 248bcc8..2af39d1 100644 --- a/src/cpu/65816-ops.c +++ b/src/cpu/65816-ops.c @@ -2266,6 +2266,8 @@ void i_sbc(CPU_t *cpu, memory_t *mem, uint8_t size, uint8_t cycles, CPU_Addr_Mod { uint8_t val = _get_mem_byte(mem, addr, cpu->setacc); uint16_t al, alb; + uint8_t original_a = cpu->C & 0xff; // Save original A for carry calculation + uint8_t original_c = cpu->P.C; // Save original carry for carry calculation // Binary mode calculation alb = (cpu->C & 0xff) - val + cpu->P.C - 1; @@ -2300,14 +2302,18 @@ void i_sbc(CPU_t *cpu, memory_t *mem, uint8_t size, uint8_t cycles, CPU_Addr_Mod // C and V are based on the binary result cpu->P.V = ((int16_t)alb < -128 || (int16_t)alb > 127) ? 1 : 0; - cpu->P.C = (alb >= 0x100) ? 1 : 0; + // For SBC, carry is set if no borrow occurred: A >= M + (1 - C_in) + // Using original values saved at start of function + cpu->P.C = ((uint16_t)original_a + original_c > val) ? 1 : 0; } else // 16-bit { uint16_t val; uint32_t al, alb; - + uint16_t original_a = cpu->C; // Save original A for carry calculation + uint8_t original_c = cpu->P.C; // Save original carry for carry calculation + if (mode == CPU_ADDR_DP || mode == CPU_ADDR_DPX || mode == CPU_ADDR_IMMD || mode == CPU_ADDR_SR) { @@ -2365,7 +2371,9 @@ void i_sbc(CPU_t *cpu, memory_t *mem, uint8_t size, uint8_t cycles, CPU_Addr_Mod // C and V are based on the binary result cpu->P.V = ((int32_t)alb < -32768 || (int32_t)alb > 32767) ? 1 : 0; - cpu->P.C = (alb >= 0x10000) ? 1 : 0; + // For SBC, carry is set if no borrow occurred: A >= M + (1 - C_in) + // Using original values saved at start of function + cpu->P.C = ((uint32_t)original_a + original_c > val) ? 1 : 0; cpu->cycles += 1; if (mode == CPU_ADDR_IMMD) From 89ae6f584671e394f14e92e2d6b21f99c1dd60a4 Mon Sep 17 00:00:00 2001 From: Garry Jeromson Date: Fri, 6 Feb 2026 17:34:55 +0100 Subject: [PATCH 2/2] Fix V (overflow) flag calculation in ADC and SBC 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) --- src/cpu/65816-ops.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/cpu/65816-ops.c b/src/cpu/65816-ops.c index 2af39d1..feceb20 100644 --- a/src/cpu/65816-ops.c +++ b/src/cpu/65816-ops.c @@ -34,7 +34,10 @@ void i_adc(CPU_t *cpu, memory_t *mem, uint8_t size, uint8_t cycles, CPU_Addr_Mod else // Binary mode { al = (cpu->C & 0xff) + val + cpu->P.C; - cpu->P.V = ((int16_t)al < -128 || (int16_t)al > 127) ? 1 : 0; + // V flag: check if signed result overflows 8-bit signed range + // Must sign-extend operands before computing to get correct signed result + 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; } // 1f @@ -93,7 +96,10 @@ void i_adc(CPU_t *cpu, memory_t *mem, uint8_t size, uint8_t cycles, CPU_Addr_Mod else { al = cpu->C + val + cpu->P.C; - cpu->P.V = ((int32_t)al < -32768 || (int32_t)al > 32767) ? 1 : 0; + // V flag: check if signed result overflows 16-bit signed range + // Must sign-extend operands before computing to get correct signed result + int32_t signed_al = (int16_t)cpu->C + (int16_t)val + cpu->P.C; + cpu->P.V = (signed_al < -32768 || signed_al > 32767) ? 1 : 0; } // 1f cpu->C = al & 0xffff; @@ -2301,7 +2307,12 @@ void i_sbc(CPU_t *cpu, memory_t *mem, uint8_t size, uint8_t cycles, CPU_Addr_Mod cpu->P.Z = ((al & 0xff) == 0) ? 1 : 0; // C and V are based on the binary result - cpu->P.V = ((int16_t)alb < -128 || (int16_t)alb > 127) ? 1 : 0; + // V flag: check if signed result overflows 8-bit signed range + // Must sign-extend operands before computing to get correct signed result + { + int16_t signed_alb = (int8_t)original_a - (int8_t)val + original_c - 1; + cpu->P.V = (signed_alb < -128 || signed_alb > 127) ? 1 : 0; + } // For SBC, carry is set if no borrow occurred: A >= M + (1 - C_in) // Using original values saved at start of function cpu->P.C = ((uint16_t)original_a + original_c > val) ? 1 : 0; @@ -2370,7 +2381,12 @@ void i_sbc(CPU_t *cpu, memory_t *mem, uint8_t size, uint8_t cycles, CPU_Addr_Mod cpu->P.Z = ((al & 0xffff) == 0) ? 1 : 0; // C and V are based on the binary result - cpu->P.V = ((int32_t)alb < -32768 || (int32_t)alb > 32767) ? 1 : 0; + // V flag: check if signed result overflows 16-bit signed range + // Must sign-extend operands before computing to get correct signed result + { + int32_t signed_alb = (int16_t)original_a - (int16_t)val + original_c - 1; + cpu->P.V = (signed_alb < -32768 || signed_alb > 32767) ? 1 : 0; + } // For SBC, carry is set if no borrow occurred: A >= M + (1 - C_in) // Using original values saved at start of function cpu->P.C = ((uint32_t)original_a + original_c > val) ? 1 : 0;