Help with ADC/SBC and Carry/Overflow Flags

Programming the 6502 microprocessor and its relatives in assembly and other languages.
RichTW
Posts: 95
Joined: 06 Oct 2010
Location: Palma, Spain

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by RichTW »

Thanks Mike! I already have the BCD versions too, I just didn't post them as it looked like whartung wasn't concerned about them (maybe targetting NES or something?).

Code: Select all

void cpu_6502_base::do_adc_bcd(const byte val)
{
	const uint lo_bin = (a & 0x0FU) + (val & 0x0FU) + c;
	const uint lo = (lo_bin >= 0x0AU) ? ((lo_bin + 0x06U) & 0x0FU) + 0x10U : lo_bin;
	const uint hilo_bin = (a & 0xF0U) + (val & 0xF0U) + lo;
	const uint hilo = (hilo_bin >= 0xA0U) ? hilo_bin + 0x60U : hilo_bin;

	n = !!(hilo_bin & 0x80U);
	v = !!((a ^ hilo_bin) & (val ^ hilo_bin) & 0x80U);
	z = !((a + val + c) & 0xFFU);
	c = (hilo_bin >= 0xA0U);
	a = hilo & 0xFFU;
}

void cpu_6502_base::do_sbc_bcd(const byte val)
{
	const uint lo_bin = (a & 0x0FU) + (val & 0x0FU) + c;
	const uint lo = (lo_bin < 0x10U) ? ((lo_bin + 0x0AU) & 0x0FU) : lo_bin;
	const uint hilo_bin = (a & 0xF0U) + (val & 0xF0U) + lo;
	const uint hilo = (hilo_bin < 0x100U) ? hilo_bin + 0xA0U : hilo_bin;

	n = !!(hilo_bin & 0x80U);
	v = !!((a ^ hilo_bin) & (val ^ hilo_bin) & 0x80U);
	z = !((a + val + c) & 0xFFU);
	c = hilo_bin >> 8;
	a = hilo & 0xFFU;
}
Nearly the same logic, but not quite the same enough!

Just looking at your code in that other thread: does that also set all the flags correctly like the NMOS 6502 (and also give the correct results with non-BCD inputs)? If so, that is some proper magic going on there!
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by barrym95838 »

RichTW wrote:
... Just looking at your code in that other thread: does that also set all the flags correctly like the NMOS 6502 (and also give the correct results with non-BCD inputs)? If so, that is some proper magic going on there!
I never performed a thorough check to see if my attempt behaved exactly like an NMOS 6502 for all possible inputs and outputs, but I would be very surprised if it did.

Mike B.
User avatar
banedon
Posts: 742
Joined: 08 Sep 2013
Location: A missile silo somewhere under southern England

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by banedon »

Does anyone have any ADC / SBC test routines that I can run against my code? Everything checks out that I've thrown at it, but I'd like to be sure.
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by BigEd »

Try Bruce's tests, or Wolfgang's tests maybe.
http://visual6502.org/wiki/index.php?ti ... stPrograms
User avatar
banedon
Posts: 742
Joined: 08 Sep 2013
Location: A missile silo somewhere under southern England

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by banedon »

Thanks BigEd. Will give them a go.
litwr
Posts: 188
Joined: 09 Jul 2016

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by litwr »

The only 6502 generates the inverted carry flag after SBC and CMP. I am curious about this. Does anybody know a reason for this? I am aware that it is a bit easier to implement but one additional NOT logic gate is not a problem even in the super-simple 6502. I hope if it is still not known somebody will ask Bill Mensch about this. Thank you.
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by BigEd »

I suspect if we called Carry by an extended name: Carry/notBorrow, in analogy with Read/notWrite, we'd find this less of an issue.

The important thing about ADC and SBC is that they should be easy to use for multi byte arithmetic, so the sense of Carry/notBorrow as an input should be the same as the sense of the output.

We SEC before an initial SBC because the neutral value of Carry/notBorrow for a subtraction is to be set, signifying no borrow. And this is natural, when we implement subtract by inverting one input to the ALU: to subtract 00, we add FF, and if we want that to have no effect, we need to put in an additional 1.

It's very natural having constructed an adder to implement subtraction by complementing one input.

I suspect these conventions spring directly from the 1950s machines, where it was enough if the machine could be instructed to do what was needed, no matter how unintuitive the commands might be.

From a 1970s perspective, Carry/notBorrow is quite unobjectionable. Only from a much later perspective, when programmers are brought up with high level languages, might it seem unnecessarily clumsy.
BillG
Posts: 710
Joined: 12 Mar 2020
Location: North Tejas

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by BillG »

litwr wrote:
The only 6502 generates the inverted carry flag after SBC and CMP. I am curious about this. Does anybody know a reason for this? I am aware that it is a bit easier to implement but one additional NOT logic gate is not a problem even in the super-simple 6502.
My understanding is that to save transistors, MOS left out subtract circuitry. Instead, they implemented subtraction by adding the inverse of the subtrahend.

Normally, a two's complement number is negated by inverting the bits and adding one.

Instead of more circuitry or an extra cycle to add the one, they decided that if there was a borrow, there was no need to add the one; if there was no borrow, adding one is needed. Inverting the sense of the carry flag allows the reuse of the adc logic already present.
dmsc
Posts: 153
Joined: 17 Sep 2018

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by dmsc »

Hi!
litwr wrote:
The only 6502 generates the inverted carry flag after SBC and CMP. I am curious about this. Does anybody know a reason for this?
It is simply a convention, the CPU designer can choose if "Carry = Borrow" or if "Carry = 1 - Borrow".

The 6502, PIC, ARM and PowerPC are examples of CPU where Carry is the inverted Borrow. The Z80 and x86 are examples of CPU where Carry is used as Borrow. And there is MIPS, that does not have flags at all.

So, I would say that nowadays, most used CPUs use the 6502 convention.
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by barrym95838 »

BillG wrote:
My understanding is that to save transistors, MOS left out subtract circuitry. Instead, they implemented subtraction by adding the inverse of the subtrahend.
My understanding is similar, but I don't know how SBC in BCD mode deals with this little situation, since the circuitry would need to somehow form the nines-complement of the subtrahend, right?

Code: Select all

0000 -> 1001
0001 -> 1000
0010 -> 0111
0011 -> 0110
0100 -> 0101
0101 -> 0100
0110 -> 0011
0111 -> 0010
1000 -> 0001
1001 -> 0000
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by BigEd »

Th 6502's decimal circuits are built as a post-operation adjustment, with inputs (and outputs) including the half-carry. The basic idea, I think, is to add or to subtract 6 from each digit, or leave it alone. Some discussion here: viewtopic.php?f=1&t=4975
litwr
Posts: 188
Joined: 09 Jul 2016

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by litwr »

dmsc wrote:
It is simply a convention, the CPU designer can choose if "Carry = Borrow" or if "Carry = 1 - Borrow".

The 6502, PIC, ARM and PowerPC are examples of CPU where Carry is the inverted Borrow. The Z80 and x86 are examples of CPU where Carry is used as Borrow. And there is MIPS, that does not have flags at all.

So, I would say that nowadays, most used CPUs use the 6502 convention.
Sorry, I missed the ARM and I didn't know that the PIC and PowerPC also inverts the carry. Thank you. I can only add that Moto's (680x, 68k) and DEC's (PDP-11, VAX) processors also use not inverted carry. I didn't know about the carry on the DEC Alpha. The IBM System/360 doesn't invert carry. The Microblaze architecture inverts carry but it can't directly check it that makes work a bit easier.
The inverted carry helps to make division faster. I used it in my division routines for the 6502 and ARM in my pi-spigot project. I dare to think that my divisions are among the fastest known. IMHO it is good to have the inverted carry on a processor without hardware division.
It is interesting, did somebody of the 6502 team know about advantages of the inverted carry? It seems that the 6502 was the first processor with the inverted carry.
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by Chromatix »

I think they just went for the solution with the simplest hardware implementation and the smallest number of required opcodes. That it happens to also be advantageous for certain algorithms is merely a happy accident.
litwr
Posts: 188
Joined: 09 Jul 2016

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by litwr »

Chromatix wrote:
I think they just went for the solution with the simplest hardware implementation and the smallest number of required opcodes. That it happens to also be advantageous for certain algorithms is merely a happy accident.
It might have been so but it is difficult for me to believe that they broke the concept, which was used by IBM, DEC, Intel, and Motorola, just to save 3 transistors.
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: Help with ADC/SBC and Carry/Overflow Flags

Post by BigEd »

The 6502 team were trying to build a better 6800. If they knew from experience that coding multi byte subtraction was a pain on the 6800, and this helped to fixed that, that could be the answer. Similarly, perhaps, the way the 6502 does not update flags on stores, is an optimisation which could come from experience.
Post Reply