SBC help

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
mreiland
Posts: 29
Joined: Wed Dec 17, 2014 3:28 am

SBC help

Post by mreiland » Fri Dec 11, 2015 7:38 pm

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?

lidnariq
Posts: 9848
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: SBC help

Post by lidnariq » Fri Dec 11, 2015 7:54 pm

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
Last edited by lidnariq on Sat Dec 12, 2015 1:41 am, edited 1 time in total.

tepples
Posts: 22145
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SBC help

Post by tepples » Fri Dec 11, 2015 10:31 pm

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:

Code: Select all

  var b = 0xFF ^ mem.read(addr);

User avatar
Disch
Posts: 1849
Joined: Wed Nov 10, 2004 6:47 pm

Re: SBC help

Post by Disch » Sat Dec 12, 2015 12:59 am

+1 @ tepples.

My SBC implementation:

Code: Select all

void     SBC(u8 val)                 { ADC(~val);        }

mreiland
Posts: 29
Joined: Wed Dec 17, 2014 3:28 am

Re: SBC help

Post by mreiland » Fri Jan 08, 2016 1:08 am

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.

User avatar
tokumaru
Posts: 11907
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: SBC help

Post by tokumaru » Fri Jan 08, 2016 5:47 am

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.

User avatar
Movax12
Posts: 529
Joined: Sun Jan 02, 2011 11:50 am

Re: SBC help

Post by Movax12 » Fri Jan 08, 2016 7:00 am

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.

User avatar
tokumaru
Posts: 11907
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: SBC help

Post by tokumaru » Fri Jan 08, 2016 7:36 am

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.

mreiland
Posts: 29
Joined: Wed Dec 17, 2014 3:28 am

Re: SBC help

Post by mreiland » Fri Jan 08, 2016 7:39 pm

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.

Post Reply