Page 2 of 2
Re: Why does ADC add one if carry flag set?
Posted: Tue Jun 07, 2022 12:26 am
by jeffythedragonslayer
I don't find it at all illogical to expect fewer multi-precision additions when the word length doubles. Just my $.02 worth...
-- Jeff
this
I hope you're not discouraged, jeffythedragonslayer, by the strongly-expressed opinions upthread. It's not how we usually conduct ourselves here.
thanks
Re: Why does ADC add one if carry flag set?
Posted: Tue Jun 14, 2022 12:24 pm
by Sheep64
Discarding add without carry was a very bold move. It makes the processor smaller - at the expense of making your program larger and slower. Regardless, there are opportunities to skip CLC before ADC (or SEC before SBC). I'll start with an example in C where arrays of 80 elements are added. You might type:
Code: Select all
for(x=0;x<80;x++) {
f[x]=d[x]+e[x];
}
In 6502 assembly, if you want to take 80 numbers in page $04, add them to 80 numbers in page $05 and store the results in page $06, it may preferable to iterate backwards:
Code: Select all
LDX #79
loop:
CLC ; <----- this use of CLC might be optimized.
LDA $0400,X ; d[x]
ADC $0500,X ; e[x]
STA $0600,X ; f[x]
DEX ; --x
BPL loop ; x>=0
In this example, RegX will start at 79 and decrease. The last iteration has RegX=0 and iteration stops when top bit of RegX is not zero. If you definitely know that the values will be small, it is possible to move CLC outside of the loop. In this case, CLC is only executed once. The small result from any addition will clear the carry flag before the next addition occurs. However, if this assumption changes, the optimized loop will completely foobar your data. One bit of precision is enough to cause integer overflow.
If you want a fantastically vivid example of integer overflow, find the video of the first Ariane 5 rocket launch. Then remember to dimension your data correctly.
Re: Why does ADC add one if carry flag set?
Posted: Mon Jun 20, 2022 2:02 am
by jeffythedragonslayer
That's good to know, Sheep64. As for why SBC needs SEC instead of CLC, I combed through this thread and think I can summarize why now:
viewtopic.php?f=2&t=2944&hilit=inverted+carry
The carry flag serves double duty as the borrow flag when doing subtraction. Subtraction on the 6502 is implemented by inverting the subtrahend and then sending that to the ALU, which performs an addition. Now, the way to invert a number in two's complement is to invert all the bits and then add one. To save some transistors, the 6502 does not add one for you automatically, so you need to manually set this flag manually with the SEC instruction. This is a more efficient use of transistors compared to always adding one in hardware and then undoing that when there is a borrow.
Also, I think there is a typo in Eyes & Lichty page 497:
To avoid subtracting the carry flag from the result, either you must be sure it is set or you must explicity set it (using SEC) prior to executing the SBC instruction.
I think it should instead start with "To avoid subtracting one from the result," because the ALU actually adds the carry/borrow flag to the result.
Re: Why does ADC add one if carry flag set?
Posted: Mon Jun 20, 2022 12:51 pm
by BigEd
Mmm, indeed that could be clearer. For the subtraction you're thinking of (not for the usual case!), you need to clear the carry first. For a multi-byte subtraction, in your case you'd clear the carry before starting out, but the whole point of the carry is that it then proprogates from each byte to the next higher byte.
Edit: added clarification
(Personally, I've never worried too much about trying to think of it as a borrow or a not-borrow or similar - I just remember firmly that it's obvious enough to CLC before an ADC, and so, for reasons, it's also correct to SEC before SBC.)
As I can never quite readily remember subtraction without working it out, I popped over to
Easy6502 and put in this program to illustrate:
Code: Select all
LDX #0
SEC
LDA #$02
SBC #$01
TAX
CLC
LDA #$02
SBC #$01
TAY
After execution, we see X=01 and Y=00. Which tells us that the C=0 caused an extra 1 to be subtracted. Which indeed one could think of as a borrow.
Re: Why does ADC add one if carry flag set?
Posted: Sat Jul 09, 2022 4:21 pm
by Sheep64
I hope you're not discouraged, jeffythedragonslayer, by the strongly-expressed opinions upthread. It's not how we usually conduct ourselves here.
Ignore BigEd. This is *exactly* how the 6502 Forum is conducted when a 65816 feature is questioned.
As I was recently reminded when I suggested use of REP to simultaneously set C=0 or D=0 when setting M=0 or X=0, 65816 flags are particularly touchy subject.
Re: Why does ADC add one if carry flag set?
Posted: Mon Jan 02, 2023 12:03 am
by jeffythedragonslayer
BTW, the Super FX chip has ADD without carry.
Re: Why does ADC add one if carry flag set?
Posted: Mon Jan 02, 2023 1:20 am
by Dr Jefyll
This is *exactly* how the 6502 Forum is conducted when a 65816 feature is questioned. [...] 65816 flags are particularly touchy subject.
The forum is a collection of individuals, Sheep64, even including yourself. I hope you don't find us to be
unanimously touchy!

Overall, I think the decorum is pretty darn good! I prefer to keep a pretty relaxed outlook, myself...
-- Jeff
Re: Why does ADC add one if carry flag set?
Posted: Mon Jan 02, 2023 6:44 am
by barnacle
But the original question: Why does ADC add one if carry flag set? still remains... The obvious short answer is that, er, it's the function of the instruction, but that takes you back to how to design an adder. Text below assumes two's complement maths; it's not the only way!
Do you need an adder at all? No - it's quite feasible to have a processor which only has a single SUB (without carry) instruction in the ALU and no adder even if it generates a carry output - to add, you need to invert one of the operands before it is subtracted from the other (which is a little slow and repetitive in the absence of other ALU operations, but possible) and if you want to include the carry at any stage, that's a separate step. It hurts the head less and is (I think) more efficient if the subtracter is an adder but it still gets pretty tedious.
But the basic building block for a multibit adder is a single bit adder with three inputs and two outputs: A, B, and carry in, and C and carry out. Eight of those in a row give you an eight bit adder, with a carry input correctly propagated along the chain (that's the slowest path through the adder logic, so you might use a different approach for the carry at longer word lengths).
To add, it's obvious: provide the two inputs and the carry as required (if done with two instructions, the microcode needs to manage the value of the carry either to force it clear or to use a stored flag); and to subtract, invert the subtrahend, *add* it to the minuend, and add one. Adding one is already built into the adder using the carry input, so a simple way is just to use that: lo and behold, only two instructions are required. ADC and SBC. In both cases, the carry is included, and for SBC the carry input and output are inverted, with the same bit of circuitry and no worrying about the carry. It just happens. So to add, the carry flag is initially cleared; to subtract it is initially set.
Obviously this way multiprecision arithmetic is simpler because the carry ripples through the calculation without any programmer input apart from the initial setting or clearing of the carry flag; it also slightly simplifies the logic if the ALU is used to provide address increments in two or more bytes (though I have a vague idea that the 6502 has sixteen bit adders to manage the various indirect and indexed addressing modes, so perhaps not relevant here).
Neil
Re: Why does ADC add one if carry flag set?
Posted: Tue Jan 03, 2023 3:58 am
by JimBoyd
The carry flag can be used for more than just multi byte arithmetic. Sometimes it's useful to set the carry flag before ADC.
Here is a hand translated code fragment which skips over an inline count byte and string in Forth. The Y-register has a value of zero when this routine is entered.
Code: Select all
LDA (IP)Y
SEC
ADC IP
STA IP
BCC RESUME
INC IP+1
RESUME: JMP NEXT
And the original from my Forth system.
Code: Select all
IP )Y LDA SEC
IP ADC IP STA
CS IF IP 1+ INC THEN
NEXT JMP END-CODE