LD Is Not LET
Meet the registers — the handful of physical, 8-bit stores inside the CPU. Move a value from A into B and back again, and watch the border prove it made the trip.
Last time you sent a number to the border and met the build-run loop. You used a register — A — without properly meeting it. Let's meet it now, because this is the first place the machine stops behaving like the language you came from.
In BASIC, LET a = 5 gave you a variable, and you could have as many as you liked — score, lives, name$, on and on. The CPU is not so generous. It has a handful of registers — small stores inside the chip itself — and that's all. Their names are fixed: A, B, C, D, E, H, L. You don't declare new ones. You learn to orchestrate the few you have.
And each one holds a single 8-bit number: 0 to 255, nothing larger. A register isn't a roomy named box; it's a tiny slot with a hard limit. That constraint shapes everything you'll write.
What you'll see by the end
A cyan border — colour 5. But the colour isn't the point this time. The point is how the 5 got there: it travelled from A into B, survived us wiping A, and came back. The border is just the proof you can see. The real show is in the register view — open it in your emulator and watch A and B change step by step.
LD just moves a number
There's one new idea here, and it's LD — "load". Despite the name, it doesn't "load" in any grand sense; all it does is move a number. Into a register from a literal (ld a, 5), or from one register into another (ld b, a). That's the whole instruction.
To prove a value moves between registers — instead of just trusting it — we send 5 on a round trip:
; ============================================================================
; PRIMER — Beat 2: LD Is Not LET
; ============================================================================
; In BASIC, LET a = 5 gave you as many named variables as you liked. The CPU
; doesn't work that way. It has a *handful* of registers — physical stores
; inside the chip, each holding one 8-bit number (0 to 255). Their names are
; fixed: A, B, C, D, E, H, L. You don't make more; you orchestrate these few.
;
; LD means "load", and all it ever does is move a number — into a register,
; or from one register to another. To prove a value really moves, we take it
; on a round trip and watch it come back:
;
; 1. Put 5 into A. (5 = cyan)
; 2. Copy A into B. (now B holds 5 too)
; 3. Wipe A back to 0. (A = 0; if we stopped here the border'd be black)
; 4. Copy B back into A. (A = 5 again — the 5 was safe in B all along)
; 5. Show A on the border. (cyan proves B kept our 5)
;
; Open the register view in Emu198x and watch A and B change at each step.
; The border is just the visible proof; the registers are the lesson.
; ============================================================================
org 32768
start:
ld a, 5 ; put 5 into A (5 = cyan)
ld b, a ; copy A into B (B now holds 5 too)
ld a, 0 ; wipe A (A = 0; B still holds 5)
ld a, b ; copy B back into A (A = 5 again, from B)
out ($FE), a ; show A on the border (only A can do OUT)
.loop:
halt
jr .loop
end start
This is last unit's program with three lines added in the middle. Follow A and B down the steps:
| After… | A | B |
|---|---|---|
ld a, 5 | 5 | — |
ld b, a | 5 | 5 |
ld a, 0 | 0 | 5 |
ld a, b | 5 | 5 |
We deliberately wipe A to 0, then bring the value back from B. The border ends up cyan — so the 5 must have been safe in B the whole time. That's the proof the copy was real.
Why copy back into A just to show it? Because the out instruction only ever sends A — never B or the others. It's one of the chip's fixed rules; just accept it for now. It's also what makes the round trip prove anything: the 5 had to come home through B.
Assemble and run
Same loop as before — change, build, run:
pasmonext --sna registers.asm primer.sna
Load primer.sna in your emulator with a register view open, and watch A and B move through the table above before the border settles on cyan.
Try this: too big to fit
Change ld a, 5 to ld a, 300, assemble, and run. Now look at A in the register view: it reads 44, not 300. A register holds 8 bits — 0 to 255 — and 300 doesn't fit, so the machine keeps only the low 8 bits (300 − 256 = 44). And notice what didn't happen: no error, no warning. The assembler let it through; the machine kept the bits that fit and carried on. The tools won't catch this for you — a theme we'll return to.
Try this: a longer trip
Send a value through three registers before showing it — A into B, B into C, then C back into A and out it. It's the round trip with one more hop (and a different colour to tell them apart):
| 1 | 1 | ; ============================================================================ | |
| 2 | - | ; PRIMER — Beat 2: LD Is Not LET | |
| 2 | + | ; PRIMER — Beat 2, "Try this: a longer trip" | |
| 3 | 3 | ; ============================================================================ | |
| 4 | - | ; In BASIC, LET a = 5 gave you as many named variables as you liked. The CPU | |
| 5 | - | ; doesn't work that way. It has a *handful* of registers — physical stores | |
| 6 | - | ; inside the chip, each holding one 8-bit number (0 to 255). Their names are | |
| 7 | - | ; fixed: A, B, C, D, E, H, L. You don't make more; you orchestrate these few. | |
| 8 | - | ; | |
| 9 | - | ; LD means "load", and all it ever does is move a number — into a register, | |
| 10 | - | ; or from one register to another. To prove a value really moves, we take it | |
| 11 | - | ; on a round trip and watch it come back: | |
| 12 | - | ; | |
| 13 | - | ; 1. Put 5 into A. (5 = cyan) | |
| 14 | - | ; 2. Copy A into B. (now B holds 5 too) | |
| 15 | - | ; 3. Wipe A back to 0. (A = 0; if we stopped here the border'd be black) | |
| 16 | - | ; 4. Copy B back into A. (A = 5 again — the 5 was safe in B all along) | |
| 17 | - | ; 5. Show A on the border. (cyan proves B kept our 5) | |
| 18 | - | ; | |
| 19 | - | ; Open the register view in Emu198x and watch A and B change at each step. | |
| 20 | - | ; The border is just the visible proof; the registers are the lesson. | |
| 4 | + | ; Send a value through THREE registers before showing it: | |
| 5 | + | ; A -> B -> C -> back to A -> border. | |
| 6 | + | ; Predict A, B and C at each step, then watch them in the register view. | |
| 7 | + | ; (6 is yellow.) | |
| 21 | 8 | ; ============================================================================ | |
| 22 | 9 | | |
| ... | |||
| 24 | 11 | | |
| 25 | 12 | start: | |
| 26 | - | ld a, 5 ; put 5 into A (5 = cyan) | |
| 27 | - | ld b, a ; copy A into B (B now holds 5 too) | |
| 28 | - | ld a, 0 ; wipe A (A = 0; B still holds 5) | |
| 29 | - | ld a, b ; copy B back into A (A = 5 again, from B) | |
| 30 | - | out ($FE), a ; show A on the border (only A can do OUT) | |
| 13 | + | ld a, 6 ; put 6 into A (6 = yellow) | |
| 14 | + | ld b, a ; copy A into B | |
| 15 | + | ld c, b ; copy B into C | |
| 16 | + | ld a, c ; copy C back into A | |
| 17 | + | out ($FE), a ; show A on the border (only A can do OUT) | |
| 31 | 18 | | |
| 32 | 19 | .loop: |
Before you run it, predict A, B and C at each step — then open your emulator's register view and check. (6 is yellow.)
When it's wrong, see why
pasmonextsays "Expected 'A' but…" on theoutline. You tried toouta register other thanA. OnlyAcan doout ($FE), a— route the value back intoAfirst.- The border is black, not cyan. The
ld a, bstep is missing or mistyped, soAwas still 0 (from the wipe) when you showed it. Put the round trip back exactly as shown. - The border's the wrong colour. Check the number in
ld a, …— and remember the border reads only the low 3 bits, so values above 7 repeat the eight colours.
What you've learnt
A register is a physical, finite 8-bit store — the CPU has only a handful (A B C D E H L) — and LD moves numbers between them.
What's next
This isn't a Spectrum quirk: nearly every CPU is built around a small set of registers — some have more, some fewer. You're learning how machines work, not a Spectrum trick. Next — Everything Is a Number — we look at what those bytes you're moving around are, and discover the machine doesn't know a letter from a colour from a number. They're all just bytes.