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.
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, adc — ADd 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:
clc— CLear 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
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
clcbefore the firstadc— the leftover carry added an extra 1. - Your difference is one too low. You forgot the
secbefore the firstsbc. 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 Bits — and, 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.