Skip to content
Game 0 Unit 14 of 18 1 hr learning time

Adding and Taking Away

The most ordinary thing of all, finally: maths. adc and sbc on a value — and the carry flag the 6502 makes you mind: clear it before you add, set it before you subtract.

78% of Meet The Machine

You can move values, compare them, loop, branch, index and call — and yet you've never done the most ordinary thing a program does: arithmetic. Before you build a game, the last pieces of plain fluency. This one is the most basic of all: LET X = X + 1.

And here the 6502 has a quirk you'll meet on day one and never forget. There's no "add 1 to A" instruction — no inc a at all. Adding to the accumulator goes through one instruction, adcADd with Carry — and the name is a warning: it always adds the carry flag in too. So before the first add you clear the carry yourself:

  • clcCLear the Carry. Do this before you start adding.
  • adc #1 — add 1 (plus the carry, which you just made sure is 0).
  • sec / sbc #1 — to subtract, SEt the Carry first, then SuBtract with Carry.

Forget the clc and your sum comes out one too high; forget the sec and your difference comes out one too low. It catches everyone once. (The index registers have their own steps — inx / dex / iny / dey — and those don't touch carry at all. It's the accumulator that makes you mind it.) This is the same 6502 as the C64; the carry rule is identical.

What you'll see by the end

The NES screen filled with green.
Green ($2a) that was never loaded but computed — start at $27, clear the carry, add one three times.

A green screen — colour $2a. But we never loaded $2a; we computed it: start at $27, clear the carry, add one three times. In a real game the numbers are a score or a position you don't know ahead of time. Here they're constants so you can check the sum yourself.

Computing a value

; ============================================================================
; Meet the Machine (NES) - Unit 14: Adding and Taking Away
;
; Arithmetic at last. There is no "add 1 to A" - you go through adc, which
; always folds in the carry, so you clear it first. We compute a colour instead
; of loading it: start at $27, clear the carry, add one three times -> $2a.
; ============================================================================

.segment "HEADER"
    .byte "NES", $1a
    .byte 2
    .byte 1
    .byte $00, $00

.segment "CODE"

reset:
    sei
    cld
    ldx #$40
    stx $4017
    ldx #$ff
    txs
    inx
    stx $2000
    stx $2001
    stx $4010
warm1:
    bit $2002
    bpl warm1
warm2:
    bit $2002
    bpl warm2

    ; ----------------------------------------------------------- YOUR CODE START
    lda #$27                ; start at $27
    clc                     ; clear the carry BEFORE adding
    adc #1                  ; $28
    adc #1                  ; $29
    adc #1                  ; $2a  (a green) - computed, not loaded
    ; ------------------------------------------------------------- YOUR CODE END

    sta $00
    bit $2002
    lda #$3f
    sta $2006
    lda #$00
    sta $2006
    lda $00
    sta $2007

forever:
    jmp forever

nmi:
    rti
irq:
    rti

.segment "VECTORS"
    .word nmi
    .word reset
    .word irq

.segment "CHARS"
    .byte $00,$00,$00,$00,$00,$00,$00,$00
    .byte $00,$00,$00,$00,$00,$00,$00,$00
    .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
    .byte $00,$00,$00,$00,$00,$00,$00,$00
    .res 8192 - 32, $00

$27, clc, then adc #1 three times — $28, $29, $2a — and show it. We clear the carry once, up front; after that no add overflows, so the carry stays clear and each adc #1 adds exactly one. Same shape as loading a colour, but the value is worked out, not written down. That's the whole difference between a constant and a calculation.

Assemble and run

ca65 maths.asm -o maths.o && ld65 -C nes.cfg maths.o -o maths.nes

A green screen, arrived at by adding.

Try this: past the edge of a byte

Replace the body with lda #255 then clc / adc #1, and watch A in the register view: it reads 0, not 256. A byte can't hold 256, so it wraps to 0 — and the carry flag is set, the machine flagging "that overflowed." A bcs would fire on it. This is how you'd catch a score rolling over — and, next unit but one, how numbers bigger than a byte are built. (The screen comes up black, because colour $00 is grey-black — the wrap, made visible.)

Try this: taking away

Subtract with sec / sbc: lda #$2c, sec, sbc #2 leaves $2a (green again). Forget the sec and you'd get $29 instead — one short, because the missing carry counts as a borrow. And subtracting past zero wraps the other way: lda #0, sec, sbc #1 gives 255, this time clearing the carry (a borrow happened). Predict each before you run it.

If it doesn't work

  • Your sum is one too high. You forgot the clc before the first adc — the leftover carry added an extra 1.
  • Your difference is one too low. You forgot the sec before the first sbc. Subtraction needs the carry set to mean "no borrow."
  • The screen colour looks "wrong". It isn't — the palette shows only the low six bits, so any result is shown mod 64. Check the register view for the true answer.

What you've learnt

You add with clc / adc and subtract with sec / sbc — the 6502 folds the carry into every one, so you set it up first — and a result past 255 (or below 0) wraps and flips the carry, the machine's overflow signal.

What's next

Sometimes you don't want to change a whole number — just one bit of it. Next — Working With Bitsand, ora and eor let you clear, set and flip individual bits, the everyday way to throw one switch in a byte and leave the other seven alone.