## Understanding overflow flag for ADC on the 6502

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Petruza
Posts: 311
Joined: Mon Dec 22, 2008 10:45 pm
Location: Argentina

### Understanding overflow flag for ADC on the 6502

obelisk.demon.co.uk/6502 says 'Overflow Flag Set if sign bit is incorrect' which is vague and I really don't see clearly what means that 'sign bit is incorrect'.

The 6502 documentation by _Bnu, in the other hand, is very specific, it says that the overflow flag after an ADC without decimal mode is this:

Code: Select all

``(!((AC ^ src) & 0x80) && ((AC ^ temp) & 0x80))``
(AC is accumulator, src is a value from memory, temp is AC + src + carry, and 0x80 is bit 7 set )

Ok, I understand bitwise operations, but I don't get the logic behind this expression.

So the ultimate question is, conceptually, when should the overflow flag be set for ADC and when should it be unset? (what is a correct sign?)

I could just paste that expression in my code and it may work perfectly, but I'd like to understand what my code is doing.

PS: the carry flag is set when the addition overflows. So the overflow flag doesn't actually mean overflow, it means something else.

Disch
Posts: 1849
Joined: Wed Nov 10, 2004 6:47 pm
Carry indicates unsigned overflow
Overflow indicates signed overflow.

When you figure that \$81 can be -127 and \$FF can be -1..

When adding two unsigned numbers results in > \$FF, C is set
When adding two signed numbers results in > 127 (\$7F) or < -128 (\$80), V is set

In emulation, this can be easily checked by looking at the high bits. Basically:

Overflow is set if:
Positive + Positive = Negative
or
Negative + Negative = Positive

Overflow is cleared in all other instances.

Here's example code:

Code: Select all

``````// A = Accumulator before the addition
// v = the value adding to the accumulator
// s = the sum of the addition (A+v+C)

if( (A ^ s) & (v ^ s) & 0x80 )
Set_V();
else
Clear_V();
``````
EDIT:

err... duh, you already posted example code.

Anyway as for how the code works...

Pos+Pos=Neg and Neg+Neg=Pos

Both conditions have the "sum" as the odd one out. So if the sign of the sum matches either the sign of A or the sign of v, then you don't overflow.

So in my example code:

Code: Select all

``if( (A ^ s) & (v ^ s) & 0x80 )``
All we're really interested in is the high bit... so ignore all the other bits in A,v,s.

A^s will have the high bit set if the signs mismatch
ditto for v^s

so... (A^s) & (v^s) ... the AND operation check to make sure both of those conditions had the high bit set (ie: s high bit didn't match A or v). If the AND results in the high bit set, the we know that we have overflow

The following & 0x80 just extracts the high bit.
Last edited by Disch on Tue Apr 27, 2010 8:22 pm, edited 1 time in total.

tokumaru
Posts: 11756
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil
As I understand it, the carry flag can detect overflows and underflows of unsigned numbers, while the V flag is used for signed numbers.

When you add 1 to 127, the result is 128, which is correct if you treat the numbers as unsigned, but in signed numbers 128 is actually -128, so that's an overflow. Same goes for underflows: if you subtract 1 from -128 the result is -129, which doesn't fit in a byte. Crossing between 0 and 255 (-1) should be OK though, if the numbers are signed.

IIRC, there is a simple rule for setting the V flag, but I can't remember what it is (I program games, not emulators!) so I'll let someone else give it to you.

EDIT: Too late! =)

Petruza
Posts: 311
Joined: Mon Dec 22, 2008 10:45 pm
Location: Argentina

blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:
This has been discussed many times before, with lots of useful explanations and code. I highly recommend you seek out these previous discussions.

Petruza
Posts: 311
Joined: Mon Dec 22, 2008 10:45 pm
Location: Argentina
I actually made some calculations and both algorithms throw different results.
Which one is right? ( Disch's or _Bnu's )

Reference:
A: Accumulator
M: Value from memory
C: Carry
V: Overflow flag from Disch's algorithm
V2: Overflow flag from _Bnu's algorithm
+: sign bit 7 is set
-: sign bit 7 is reset

Algorithms used: ( if I coded them wrong, sorry! )

Disch's:

Code: Select all

``\$v = (int) (bool) ( ( \$a ^ \$adc ) & ( \$m ^ \$adc ) & 0x80 );``
_Bnu's:

Code: Select all

``(int) (bool) ( ( ( \$a ^ \$m ) & 0x80 ) && ( ( \$a ^ \$adc ) & 0x80 ) );``

blargg wrote:This has been discussed many times before, with lots of useful explanations and code. I highly recommend you seek out these previous discussions.
I'm sorry, you're right. But either I suck at forum searching or some posters suck at writing meaningful thread titles. I suspect a little of both ;D

Edit: Disch: I see that you already posted the same algorith, so it's most likely the right one. I'll go with that.

ReaperSMS
Posts: 174
Joined: Sun Sep 19, 2004 11:07 pm
Contact:
Your table calculations are wrong, as you forgot the ! in Bnu's.
Disch's, in english, is (overflow if the sign of the result does not match the sign of either input), Bnu's is (overflow if the sign of the inputs are the same, and do not match the sign of the result).

for the case of FF+00+0, result is 0, FF. A & 80 = 1, M & 80 = 0, R & 80 = 1. !(A^M) & (A^R) & 80 = !1 & 0 = 0 & 0 = 0
A^R & M^R & 80 = 0 & 1 = 0
FF+00+1: A=1, M=0, R=0
!(A^M) & A^R = !1 & 0 = 0
A^R & M^R = 1^0 & 0^0 = 1 & 0 = 0

etc.

Petruza
Posts: 311
Joined: Mon Dec 22, 2008 10:45 pm
Location: Argentina
Oh, I didn't see that bastard `!`
Thanks!

blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:
Just be glad the NES doesn't have a decimal mode. The flag setting for that differs between the 6502, 65C02, and 65816. It's a nightmare.

Hamburgler
Posts: 36
Joined: Wed Jul 04, 2007 8:40 am
To stir the pot more: Which one is faster? :)

koitsu
Posts: 4218
Joined: Sun Sep 19, 2004 9:28 pm
Thing to note: _Bnu did not write that document, he simply corrected all the spelling errors. So if there's mistakes in the technical aspects, don't hold him responsible. :-)

Petruza
Posts: 311
Joined: Mon Dec 22, 2008 10:45 pm
Location: Argentina
blargg wrote:Just be glad the NES doesn't have a decimal mode. The flag setting for that differs between the 6502, 65C02, and 65816. It's a nightmare.
Halleluya!
koitsu wrote:Thing to note: _Bnu did not write that document, he simply corrected all the spelling errors. So if there's mistakes in the technical aspects, don't hold him responsible.
And who wrote that document? there has to be someone I can sue for that!

blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:
Hamburgler wrote:To stir the pot more: Which one is faster? :)
To counter that stirring, ADC and SBC are not instructions that are used very often.

Petruza
Posts: 311
Joined: Mon Dec 22, 2008 10:45 pm
Location: Argentina
I wouldn't care too much about the speed of a few bitwise operations, I'll surely have worst bottlenecks in other parts of the emulator.

ehguacho
Posts: 83
Joined: Tue Mar 09, 2010 11:12 pm
Location: Rosario, Argentina
Contact:
i guess the clearest way to set or reset the V flag in ADC and SBC instructiones is by testing if the result is major than 0x7f. it works in both instructions.

Code: Select all

``````auxbyte = ACC;
ACC += (Mem[++PC] + C_FLAG);
if(auxbyte + Mem[PC] + C_FLAG > 127) V_FLAG = 1;
else V_FLAG = 0;``````
SBC Immediate:

Code: Select all

``````auxbyte = ACC;
ACC -= (Mem[++PC] - C_FLAG);
if(auxbyte - Mem[PC] - C_FLAG > 127) V_FLAG = 1;
else V_FLAG = 0;``````
of course this is not the faster way, but is the easiest way to the understand how V flag works
sorry about my english, i'm from argentina...

http://nestate.uuuq.com