Page 1 of 3
Help with ADC/SBC and Carry/Overflow Flags
Posted: Sat Apr 26, 2014 4:08 pm
by Baka94
I'm so confused with ADC, SBC,
Carry Flag and Overflow Flag that I need some help. I tried looking many places for these, but the information is either unclear, confusing or the information on the sites might differ so greatly that I don't know which one to believe.

Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Sat Apr 26, 2014 6:40 pm
by GARTHWILSON
The easier part is ADC, where to start the addition you normally clear the
carry before adding the low bytes, but after that you leave it alone and let it automatically affect the next bytes as you go up, just as you would do by hand, except we're working with 8-bit bytes, not digits.
Code: Select all
Suppose you have two 24-bit variables A and B to add, and you want to
add A to B and leave the result in B. Each variable has three bytes:
A_hi, A_mid, A_lo
B_hi, B_mid, B_lo
START: CLC ; Clear C only to start.
LDA A_lo
ADC B_lo
STA B_lo
LDA A_mid ; Note that here we don't re-clear C, but just
ADC B_mid ; let it do its job of adding the 9th bit that
STA B_mid ; didn't fit in the result of the previous byte's
; addition.
LDA A_hi
ADC B_hi
STA B_hi
Next up would be subtraction, where the
carry flag serves as a "not-borrow" flag, so you start by
setting it but then leave it alone and let it automatically affect the next bytes as you go up. You still start with they least-significant byte though, just as you would start with the least-significant digit when doing it by hand.
Any of the programming manuals should cover this adequately. There's a list at
http://www.6502.org/tutorials/ .
The more confusing thing is the overflow flag. It tells you if the sign of the result is wrong in signed operations, such that for example you added to positive numbers together, for example $4E and $53 which give $A1 which appears negative since the high bit is set. Bruce has an article on the V flag on this website at
http://www.6502.org/tutorials/vflag.html, "The Overflow (V) Flag Explained."
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Sun Apr 27, 2014 5:46 am
by teamtempest
The ADC instruction always adds together the current contents of the accumulator, the carry flag and a specified memory location.
The accumulator holds an eight-bit value in the range zero to 255.
The carry flag is a one-bit value of either zero or one.
The memory location is specified using one of the many address modes of the ADC instruction (even "immediate" mode means the address immediately succeeding the ADC instruction itself). However it is specified, the memory location holds an eight bit value in the range zero to 255.
The result of every ADC instruction is a nine-bit value in the range zero (0 + 0 + 0) to 511 (255 + 1 + 255). The least significant eight bits (zero to 255) are stored in the accumulator and the most significant bit in the carry flag.
That is what happens. The interpretation of the result depends on what you (the programmer) intend to do with it.
- if you are only interested in an eight-bit (one byte) unsigned result in the range zero to 255, you only care about the value of the carry flag before the addition, when you will probably most often want it to be zero (that is what the CLC instruction is for)
- if you are interested in multi-byte unsigned results, you probably still want the carry flag to explicitly be set to zero at the start, but carefully don't want to disturb it before adding the second (and any other) bytes together, so as to properly propogate that ninth bit into the subsequent additions
- if you are interested in treating the results of addition(s) as signed (eg., -32768 to +32767 for two-byte signed values), then the final states of the negative and overflow flags will be something you care about
The SBC instruction is similar in that in involves the same three entities - the accumulator, the carry flag, and a memory location - but dissimilar in that it is not the value of the carry flag that is subtracted but its complement. After the subtraction the carry flag is set if no borrow was required (the value in the accumulator was at least as large as the value in memory) or clear if a borrow was required (the value in the accumulator was smaller than the value in memory). As with addition, the state of the carry flag after a first subtraction can be used to "propogate" a ninth bit across multiple eight-bit subtractions.
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Mon Apr 28, 2014 12:15 pm
by White Flame
The best path to understanding SBC is to understand the binary representation of negative numbers (2's complement).
Code: Select all
Numbers in binary form:
3 00000011
2 00000010
1 00000001
0 00000000
-1 11111111 (adding 1 to this yields 0, so this is -1)
-2 11111110 (adding 2 to this yields 0, so this is -2)
-3 11111101 (etc)
In order to negate an existing number, you need to flip all the bits and add 1.
You can see that if you have +1, and you want to negate it, if you flip the bits you'll end up with 1111110, which is 2 less than zero and thus not what we want. If you add another 1 to it, then you get 11111111, which is the -1 we want.
Same with 2: 00000010, flip bits to 11111101 (this is -3), and add 1 for 11111110.
For -3, 11111101, flip bits to 00000010 (this is +2), and add 1 for 00000011.
Internally, the SBC instruction flips the bits, but relies on the incoming
carry to add the 1. This allows you to subtract another 1 (ie, not add in the required 1 for negation) when
carry is clear, to propagate subtraction in from other numbers.
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Sun Dec 10, 2017 9:40 pm
by banedon
Sorry to resurrect such an old thread, but I just wanted to confirm my algorithm for SBC in the simulator part of my 6502CA project. So far it seems to kick out the correct results, but just want to be sure.
Please keep in mind that I'm saying that this is how my code deals with the calculation to obtain the correct result, not how an actual 65C02/6502 works.
On the surface it works thus:
result = A - M - (C - 1)
With result being stored in A at the end
In detail:
If A > M (or A = M and C = 1) then:
- result = A minus M calulation
- modify result with: result = result - (C - 1)
- store result in A
- Set C flag = 1
If A < M then:
- result = A minus M calculation
- modify result with: result = result - (C - 1)
- remove the sign from the result (so -4 becomes 4). Invert the bits. Add 1 to the result
- store result in A
- Set C flag = 0
If A = M and C = 0 then:
- result = 255
- store result in A
- Set C = 0
[Where A = Accumulator, M = Memory contents, C = Carry flag]
So... does it pass muster? Or have I misunderstood what has been said in this thread?
[Modified to add the last A=M and C=0 part as tetsing revealed this gave the wrong result, plus fixed a few mistakes]
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Sun Dec 10, 2017 10:12 pm
by BigEd
> - remove the sign from the result (so -4 becomes 4). Invert the bits. Add 1 to the result
Haven't you just put the sign back? Inverting bits and adding one is the same as negation, in 2s complement.
It's always a bit suspicious to see code taking more than one branch for something as primitive as subtraction. It might not be numerically wrong, but it may have overcomplicated things.
Probably for any attempt, running an exhaustive test would be worthwhile.
Is it worth reading some code in existing emulators to see what they do?
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Sun Dec 10, 2017 10:39 pm
by banedon
Haven't you just put the sign back? Inverting bits and adding one is the same as negation, in 2s complement.
And this is why I run my modest coding efforts past other folk

. I was using the Byte datatype for the result which gives a value of 0-255 and doing all the donky work in my code. I could have used SByte as the datatype which gives -128 to 127. *Sigh*. I'm trying this at the moment but getting some errors converting an SByte to a Byte value - despite there being a function for it (test the value -5 and I get overflow - will keep working on it).
It's always a bit suspicious to see code taking more than one branch for something as primitive as subtraction. It might not be numerically wrong, but it may have overcomplicated things.
I tend to agree. I dislike special cases so am still looking at this, but coding wise so far works with what I've put.
With regards testing: Absolutely. I want my simulator to be reliable and usable so will ensure. I'll take a look at other source folks source code as you suggest. Haven't until now as I wanted to see if I could do figure it out without doing that.
[corrections]
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Sun Dec 10, 2017 10:59 pm
by banedon
Wow the Convert.ToByte routine cannot handle negative values when converting SByte to Byte. Seemingly, to do what I need, I would have to convert to a buffer of bits and then inject into a Byte variable. That would take longer than simply doing what I do at the moment which is :
Code: Select all
r = Math.Abs(rs) ' remove the '-' sign form the value. i.e. -5 becomes 5
r = not r ' invert the result
r = r +1 ' add 1 to the inverted result
Where 'rs' is the signed short byte/integer result of the subtraction and 'r' is the final unsigned Data byte of the result to which the A register is set to.
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Mon Dec 11, 2017 7:35 am
by BigEd
Ah, you're using a byte-sized datatype - that might well make the problem more difficult. We found the same when our homebrew CPUs grew from 16 bits to 32 bits - once you're trying to model a 32 bit ALU on a 32 bit host in a high level language, you do have to jump through hoops to compensate for not having a
carry bit - because 33 bits is a good size to model a 32 bit ALU.
However, if your language offers you overflowing arithmetic of the right size, it is possible to perform the subtraction and then inspect the result and the inputs to determine - relatively cheaply - whether the
carry needs attention.
For one example of doing this, see the section
"So we derive the
Carry or Borrow at the ‘eldest’ binary position, without using any inline assembly or special compiler features"
in
http://www.loper-os.org/?p=1913
For another example, see here
https://github.com/hoglet67/PiTubeDirec ... 016.c#L614
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Mon Dec 11, 2017 9:56 am
by Klaus2m5
If you have a working ADC emulation just invert the memory operand prior to ADC, voilà: SBC.
The inversion gives you a one's complement. Adding carry (= not borrow) during ADC transforms this automatically to a two's complement.
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Mon Dec 11, 2017 1:13 pm
by RichTW
Yes, I was going to say the same thing as Klaus. Binary mode SBC is exactly ADC with the one's complement of the operand. Worth noting that this isn't true of decimal mode SBC. Both ADC and SBC need their own special implementations for decimal mode.
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Mon Dec 11, 2017 1:31 pm
by BigEd
(Why did I have to suggest something really complicated??!)
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Tue Dec 12, 2017 9:56 pm
by whartung
This is my SBC code.
I jump through hoops to calculate the Overflow properly. But the basic math is simple.
Code: Select all
public void SBC(int value) {
int result;
int carryValue = (isCarry() ? 0 : 1);
result = acc - value - carryValue;
status &= ~(CARRY_MASK + ZERO_MASK + OVERFLOW_MASK + NEGATIVE_MASK);
if (result == 0) {
status |= ZERO_MASK + CARRY_MASK;
} else if (result > 0) {
status |= CARRY_MASK;
}
if (!isDecimal()) {
int signedAcc = signed(acc);
int signedValue = signed(value);
int signedResult = signedAcc - signedValue - carryValue;
if (signedResult > 127 || signedResult < -128) {
status |= OVERFLOW_MASK;
}
}
setFlagsNZ(result);
acc = result & 0xff;
}
All I can vouch for is that this passes the 6502 test code suite that's floating around.
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Wed Dec 13, 2017 10:18 am
by RichTW
Here's how I handle ADC/SBC in my own simulator in case you're interested (just in binary mode for now). As you can see, ADC and SBC are exactly the same code, and V flag calculation is quite streamlined.
Code: Select all
inline void cpu_6502_base::do_adc()
{
if (d) do_adc_bcd(db); else do_adc_bin(db);
}
inline void cpu_6502_base::do_sbc()
{
if (d) do_sbc_bcd(db ^ 0xFFU); else do_adc_bin(db ^ 0xFFU);
}
inline void cpu_6502_base::do_adc_bin(const byte val)
{
const uint sum = a + val + c;
const byte result = sum & 0xFFU;
c = sum >> 8;
v = !!((a ^ result) & (val ^ result) & 0x80U);
n = !!(result & 0x80U);
z = !result;
a = result;
}
Re: Help with ADC/SBC and Carry/Overflow Flags
Posted: Wed Dec 13, 2017 4:24 pm
by barrym95838
Very tidy, Rich! I like your use of !! there. If you ever get around to the BCD versions, check out my patch for Mike Chambers' code here.
viewtopic.php?f=3&t=3083&p=35119&hilit= ... ers#p35119
Mike B.