4

I'm perusing the Apple I version of BASIC, later known as Integer BASIC on the II. In several places I see...

ADC     #$00

or

SBC     #$00

Unless I'm mistaken, these mean add/subtract zero. What might the purpose of this be? I thought it might be something to do with carry testing, but none of the instances have obviously related branches or compares.

1
  • LDA does just load, not subtract. Or am I reading something wrong?
    – Raffzahn
    Commented Feb 26, 2020 at 14:20

4 Answers 4

14

Unless I'm mistaken, these mean add/subtract zero.

Almost. It means add/subtract zero plus carry.

So if the Carry flag is set, then adc #$00 will increment the accumulator by one. Otherwise, the same instruction will leave the accumulator alone.

Separately, the adc instruction may also set or clear the carry flags, if incrementing the accumulator meant that it rolled back round to 0.

What might the purpose of this be?

Adding an 8 bit value and a 16 bit value for example. My answer to this question explains that you can add two-byte values on the 6502 by adding the two values one byte at a time.

To do that, you'd first clear the carry flag, adc value, and sta some_location_low. Then, the carry flag tells you if a carry needs to be added to or subtracted from the next byte. That's what's happening in the Se1bc routine, which subtracts 2 from some value and then stores the pointer somewhere.

What's 2 in hexadecimal? It's 0x0002. Here you can see that one of the bytes is equal to 0. That just needs to be added to the upper 8 bits of the 16 bit value because this will implicitly add the carry from the lower 8 bits.

9
  • 1
    I'm missing something: why would A+/-0 change the value of carry? Commented Feb 26, 2020 at 12:56
  • 2
    @MauryMarkowitz The carry flag is set by a previous adc or sbc. By adding 0, carry is implicitly added as well. I hope my next edit will make this point clearer Commented Feb 26, 2020 at 13:00
  • 1
    The carry flag is set by some previous instruction. There are ADC/SBC, but as well all compares (CMP/CPX/CPY) and the shifts (ASL/LSR/ROL/ROR). So an ADC #$00 will include the carry set bei either of these - oh, and the obvious CLC/SEC of course.
    – Raffzahn
    Commented Feb 26, 2020 at 14:26
  • 1
    @Raffzahn In the source code linked ot by OP, that previous instruction happens to be another sbc or adc, as it's doing basically 16-bit additions and subtractions. Commented Feb 26, 2020 at 14:29
  • 2
    @Raffzahn, Interesting, it seems that to fully understand all the cases will need more time than I have right now. I invite you to write an answer yourself if you fancy it; your answers are always a delight to read. Commented Feb 26, 2020 at 14:54
4

ADC and SBC add/subtract their parameter, plus/minus the value in the carry bit, to the accumulator. This means that you need to know the value in the carry before you start. If you want to add three to the accumulator, a typical sequence might be:

        CLC
        ADC #3

As mentioned in another answer, you use the carry to "carry through" the, well, carry when adding multibyte numbers:

        CLC
        LDA xlo
        ADC ylo
        STA resultlo    ; sets carry if xlo+ylo >$FF, clears otherwise
        LDA xhi
        ADC yhi         ; previously set/cleared carry added in here
        STA resulthi

But it gets a bit more sophisticated than that. Since there is no "add/subtract without carry/borrow" instruction, you always need to take into account the state of the carry when you do an addition or subtraction. But rather than blindly putting in CLC and SEC instructions everywhere, you often can take advantage of what you know about the code before the addition to use the existing state of the carry.

The simplest case is when you are adding and know the carry must be clear:

        BCS somewhere   ; if carry set, go somewhere else
        LDA someloc     ; does not affect carry flag
        ;               ; no need to CLC here; carry must be set if we got here!
        ADC #3

Even trickier, you might subtract 1 from the constant you add if you know the carry will be set, letting the carry provide that extra 1:

        BCC elsewhere   ; if carry clear, go somewhere else
        LDA someloc
        ;               ; carry must be set if we reached this point
        ADC #MYCONST-1  ; subtract 1 from MYCONST because set carry adds 1

There are also cases where the carry explicitly contains the data you're trying to add. Say we're repeatedly reading an input value from a device and want to count the number of times the lowest bit was set (rather than clear):

        LDA input       ; we want to add contents of bit 0 of this input
        ROR             ; rotate bit 0 into carry
        LDA count
        ADC #0          ; add carry bit to count
        STA count

All of this applies similarly to SBC, except that on the 6502 the carry bit becomes a "borrow bit"; you set it before subtracting and it will still be set if there was no underflow. (Other CPUs, such as the 6800, work the other way around when subtracting.)

1
3

I see... ADC #$00 or SBC #$00. Unless I'm mistaken, these mean add/subtract zero.

Yes - and taking care of a previous carry.

ADC -> Add with Carry SBC -> Subtract with Carry (*1)

What might the purpose of this be?

ADC adds the content of the carry flag as well. So an ADC #$00 simply adds just the carry flag to A.

The carry flag can be set by several instruction:

  • Arithmetic (ADC, SBC)
  • Compares (CMP/CPX/CPY)
  • The shifts (ASL/LSR/ROL/ROR).

and the obvious

  • direct manipulation (CLC/SEC) (*2)

I thought it might be something to do with carry testing,

It's kind of. It's like adding testing if carry is set and when true adding 1 to A.


Within the source there are several examples where addition/subtraction of immediate zero is used to incooperate carry:

  • In line 938-941 Carry comes clearly from previous ADC
  • Line 360-365 shows the same for use with SBC
  • In line 520-22 Carry is delivered from a subroutine
  • in line 1922-23 Carry comes from a CMP

*1 - Technically it should have been called Subtract with Borrow or SBB, as carry acts as borrow when subtracting. See as well this Wiki entry about Carry flag vs. borrow flag.

*2 - This is, BTW, a hidden gem. One always sees CLC/SEC before the start of an addition/subtraction, which lead easy to a hard coded impression it's the only usage. But it works as well the other way. Setting Carry before an addition will works as an implied offset of one. Quite handy in address calculation. Or certain formulas.

0

As an aside, the PowerPC has dedicated instructions for these operations: addze (add to zero extended) and addme (add to minus-one extended). They are specifically for propagating the carry out from an extended-width arithmetic operation to the next higher machine word.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .