Page 1 of 1
SBC help
Posted: Fri Dec 11, 2015 7:38 pm
by mreiland
Hey guys, I've got SBC working correctly, but I don't understand why.
I'm working off of the following document:
http://teaching.idallen.com/dat2343/10f ... erflow.txt
Here's pseudocodehere's my implementation
Code: Select all
var a = cpu.A;
var b = mem.read(addr);
int16 diff = a - b - !cpu.C
cpu.A = (uint8)diff;
set_zs(cpu.A);
cpu.C = diff >= 0x00;
cpu.V = ((a^b)&0x80) != 0 && ((a^cpu.A)&0x80) != 0;
I got this from another emulator, and it's working properly (comparing against nestest.log shows it's correct), but I don't understand why.
1. In the statement 'int16 diff = a - b - !cpu.C', I don't understand why we're subtracting from the opposite of the carry flag.
2. In the statement 'cpu.C = diff >= 0x00', I don't understand why checking if diff >= 0 is sufficient to test for underflow. I replaced it with "cpu.C = (b-!cpu.C) >= a" and it's not doing the right thing, even though that appears to be a restatement of the original.
What am I missing?
Re: SBC help
Posted: Fri Dec 11, 2015 7:54 pm
by lidnariq
mreiland wrote:1. In the statement 'int16 diff = a - b - !cpu.C', I don't understand why we're subtracting from the opposite of the carry flag.
Shortest answer: The Carry flag is the NotBorrow flag.
Think about how 2s complement arithmetic works.
b'0011' + b'1001' = b'1100' ... regardless of whether the second number is "9" or "-7".
Similarly, b'1110' + b'1110' = b'1100' ... but if we're dealing with 4-bit 2s complement, the 16s bit is actually the C bit. Whether that means that there was a carry, or that there wasn't a borrow, depends on what the numbers originally meant.
EDIT: screwed up compl{i/e}ment
Re: SBC help
Posted: Fri Dec 11, 2015 10:31 pm
by tepples
SBC works by taking the
ones' complement of the operand. Adding the carry flag turns it back into
two's complement.
In fact, SBC is the same as an ADC that inverts all bits of the operand. This means you could take your SBC code and turn it into ADC with one changed line, or vice versa:
Re: SBC help
Posted: Sat Dec 12, 2015 12:59 am
by Disch
+1 @ tepples.
My SBC implementation:
Re: SBC help
Posted: Fri Jan 08, 2016 1:08 am
by mreiland
I'm just now getting back to this due to the holidays (happy belated holidays everyone!).
I think part of my issue is it's been so long since I've worked directly with binary and two's complement that I no longer *think* in them. I understand what they are, their pros and cons, etc, but I'm slow.
I understand ADC perfectly, including why 6502 programmers clear the carry bit before addition. ADd with Carry allows you to support multi-byte addition, but the initial byte necessarily needs the carry flag cleared.
I get it, it's fairly simple and it's solid in my head.
I even get why the complement works. The one's complement of x = -x-1. 6502 programmers are going to set the carry flag, which turns it into an ADC of (y+(-x)), so it's a simple addition. Got it.
But for some damned reason, I can't seem to fully wrap my head around the carry bit when it comes to actually *subtracting*. I get that 6502 programmers are going to set the carry flag initially when calling SDC, but I'm not seeing how that carries across to multiple bytes. I'm also not seeing why the 'cpu.C = diff >= 0' statement is correct.
lindariq, I've read over your comment several times and I'm not quite getting the "no borrow happened". I know in signed arithmetic the carry can be effectively ignored since carrying out of the MSB doesn't represent an error, but I'm not fully understanding your statement.
I could simply implement it using the complement strategy and move on, but I'm the type that prefers to fully understand things.
Something just isn't clicking in my head and I know it's due to having been away from byte arithmetic for so long. Perhaps someone is seeing what I'm missing?
Or maybe an example multi-byte SDC for me to work through on paper will get it to click.
Or even calling me a fool and linking to another explanation will help it click. It may be I just need to see it from another perspective (or three) before my mind is able to see it from all sides and I feel comfortable saying I understand what's going on.
Re: SBC help
Posted: Fri Jan 08, 2016 5:47 am
by tokumaru
This is all about the meaning of the carry flag for each instruction:
ADC:
C clear: overflow didn't happen;
C set: overflow happened;
SBC:
C clear: borrow happened;
C set: borrow didn't happen;
When starting a multi-byte addition, you start from the "no overflow" state (i.e. CLC), and any possible overflows will cause an extra unit to be added to the next byte.
When starting a multi-byte subtraction, you start from the "no borrow" state (i.e. SEC), and any possible borrows will cause one extra unit to be subtracted from the next byte.
Re: SBC help
Posted: Fri Jan 08, 2016 7:00 am
by Movax12
To put it another way:
BEFORE an addition with carry, you could think of carry as bit 8 from the previous addition, but it will now be added to bit 0 of the current addition.
AFTER an addition with carry, carry holds bit 8, and will be cleared or set to reflect what bit 8 would be (If there was a bit 8 for the accumulator.)
Subtraction is similar, though not as immediately intuitive.
Re: SBC help
Posted: Fri Jan 08, 2016 7:36 am
by tokumaru
Movax12 wrote:BEFORE an addition with carry, you could think of carry as bit 8 from the previous addition, but it will now be added to bit 0 of the current addition.
AFTER an addition with carry, carry holds bit 8, and will be cleared or set to reflect what bit 8 would be (If there was a bit 8 for the accumulator.)
And when subtracting, the carry is the ones' complement (i.e. the inverse) of bit 8 from the result of the previous subtraction. It makes sense, considering that a subtraction is just an addition that uses the number's ones' complement.
Re: SBC help
Posted: Fri Jan 08, 2016 7:39 pm
by mreiland
And when subtracting, the carry is the ones' complement (i.e. the inverse) of bit 8 from the result of the previous subtraction. It makes sense, considering that a subtraction is just an addition that uses the number's ones' complement.
that helps, I think I have it. It also makes it more clear why the 'cpu.C = diff >= 0x00'. In essence a borrow is only going to occur if b+!cpu.C > a, so that's all it's really testing for.
Thanks for your patience guys.