6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Nov 22, 2024 3:01 pm

All times are UTC




Post new topic Reply to topic  [ 39 posts ]  Go to page 1, 2, 3  Next
Author Message
PostPosted: Sat Apr 26, 2014 4:08 pm 
Offline

Joined: Sun Feb 23, 2014 1:45 pm
Posts: 12
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. :(


Top
 Profile  
Reply with quote  
PostPosted: Sat Apr 26, 2014 6:40 pm 
Online
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
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:
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."

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 27, 2014 5:46 am 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 411
Location: Minnesota
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.


Top
 Profile  
Reply with quote  
PostPosted: Mon Apr 28, 2014 12:15 pm 
Offline

Joined: Tue Jul 24, 2012 2:27 am
Posts: 679
The best path to understanding SBC is to understand the binary representation of negative numbers (2's complement).

Code:
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.

_________________
WFDis Interactive 6502 Disassembler
AcheronVM: A Reconfigurable 16-bit Virtual CPU for the 6502 Microprocessor


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 10, 2017 9:40 pm 
Offline
User avatar

Joined: Sun Sep 08, 2013 10:24 am
Posts: 740
Location: A missile silo somewhere under southern England
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]


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 10, 2017 10:12 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
> - 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?


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 10, 2017 10:39 pm 
Offline
User avatar

Joined: Sun Sep 08, 2013 10:24 am
Posts: 740
Location: A missile silo somewhere under southern England
Quote:
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).

Quote:
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]


Last edited by banedon on Sun Dec 10, 2017 11:19 pm, edited 2 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 10, 2017 10:59 pm 
Offline
User avatar

Joined: Sun Sep 08, 2013 10:24 am
Posts: 740
Location: A missile silo somewhere under southern England
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:
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.


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 11, 2017 7:35 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
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


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 11, 2017 9:56 am 
Offline

Joined: Sat Jul 28, 2012 11:41 am
Posts: 442
Location: Wiesbaden, Germany
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.

_________________
6502 sources on GitHub: https://github.com/Klaus2m5


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 11, 2017 1:13 pm 
Offline

Joined: Wed Oct 06, 2010 9:05 am
Posts: 95
Location: Palma, Spain
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.


Top
 Profile  
Reply with quote  
PostPosted: Mon Dec 11, 2017 1:31 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
(Why did I have to suggest something really complicated??!)


Top
 Profile  
Reply with quote  
PostPosted: Tue Dec 12, 2017 9:56 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
This is my SBC code.

I jump through hoops to calculate the Overflow properly. But the basic math is simple.


Code:
    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.


Top
 Profile  
Reply with quote  
PostPosted: Wed Dec 13, 2017 10:18 am 
Offline

Joined: Wed Oct 06, 2010 9:05 am
Posts: 95
Location: Palma, Spain
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:
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;
}


Top
 Profile  
Reply with quote  
PostPosted: Wed Dec 13, 2017 4:24 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1949
Location: Sacramento, CA, USA
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=mike+chambers#p35119

Mike B.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 39 posts ]  Go to page 1, 2, 3  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 24 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: