The Machine Can Hear You
Read the keyboard the hard, honest way — pick a half-row, read a port, test one bit. Hold a key and watch the border react, every frame. The first time the machine answers to you.
A program that decides things (Beat 7) gets interesting when the thing it decides on is you. So: how does the machine know a key is down?
There's no INKEY$ waiting around for you. You read the keyboard yourself, exactly the way you read any port, and test a single bit. The keys are grouped into half-rows, and which half-row you read is chosen by the address you put in BC:
ld bc, $7FFE— pick the half-row holding SPACE (and SYM SHIFT, M, N, B).in a, (c)— read it intoA. Each bit is one key in that group.bit 0, a— test bit 0, which is SPACE.
There's one twist that catches everyone once: a pressed key reads as 0, not 1. The bit is cleared while the key is held. So bit 0, a sets the zero flag when SPACE is down.
What you'll see by the end
A red border — while SPACE is held. Let go and it springs back to blue. The program reads the key every single frame and repaints the border to match, so the colour is a live readout of whether you're pressing. (The screenshot catches it mid-press; at rest it's blue.)
Reading the key, every frame
; ============================================================================
; PRIMER — Beat 8: The Machine Can Hear You
; ============================================================================
; There's no INKEY$ waiting politely for a keypress. You READ the keyboard,
; the same way you'd read any port, and test one bit.
;
; ld bc, $7FFE -- pick the half-row of keys. The high byte ($7F) chooses
; which group; this one holds SPACE, SYM SHIFT, M, N, B.
; in a, (c) -- read that half-row into A. Each bit is one key.
; bit 0, a -- test bit 0 = SPACE.
;
; The twist: a pressed key reads as 0, not 1 -- the bit is CLEARED while held.
; So `bit 0, a` sets the zero flag when SPACE is DOWN (bit 0 = 0).
;
; We do this every frame: read SPACE, paint the border red while it's held,
; blue while it isn't. The machine is listening, frame after frame.
;
; (One key, one bit. Reading the whole keyboard -- all eight half-rows, several
; keys at once -- is the tiny game's job, where movement needs it.)
; ============================================================================
org 32768
start:
.loop:
ld bc, $7FFE ; the half-row holding SPACE
in a, (c) ; read it (a pressed key's bit = 0)
bit 0, a ; test bit 0 = SPACE
jr z, .down ; zero flag set -> SPACE is held DOWN
ld a, 1 ; SPACE up -> blue
out ($FE), a
jr .next
.down:
ld a, 2 ; SPACE down -> red
out ($FE), a
.next:
halt ; wait for the frame
jr .loop ; ...and read again
end start
It's the test-then-jump from Beat 7, now fed by the keyboard instead of a fixed value. The loop you've had since Beat 1 — halt then jr — finally earns its keep: each time round, it reads SPACE afresh and paints accordingly. That "read, decide, draw, repeat" rhythm is the heartbeat of every game; you're feeling it for the first time here.
Remember the twist: jr z, .down jumps when the zero flag is set, and bit 0, a sets it when bit 0 is clear — so "zero flag set" means "SPACE is down." Pressed is 0. It reads backwards until it doesn't.
One key now; the rest later
This reads exactly one key, one bit. The full keyboard is eight half-rows, and reading several keys at once — up, down, left, right, fire, all in a frame — is a job of its own. We're leaving it here: that belongs to the tiny first game, where moving a character needs it. For now, the machine can hear one key, and that's the whole idea.
Assemble and run
pasmonext --sna read-a-key.asm primer.sna
Load it, then hold SPACE: the border turns red, and releases back to blue when you let go. The machine is answering you in real time.
Try this: a different key
M is in the same half-row as SPACE, on bit 2. Change bit 0, a to bit 2, a and now the border answers to M instead. (SYM SHIFT is bit 1, N is bit 3, B is bit 4 — the same in a, (c), a different bit.)
Try this: invert it
Swap the two colours — ld a, 2 (red) on the "up" path and ld a, 1 (blue) on the "down" path — so the border is red until you press, then goes blue while held. Same reading, opposite reaction. Predict it, then confirm.
When it's wrong, see why
- The border never changes when you press SPACE. Check
ld bc, $7FFEexactly — the high byte$7Fis what selects SPACE's half-row. A wrong high byte reads a different group of keys. - It's backwards — red at rest, blue when pressed. That's the active-low twist biting: pressed is 0. Make sure
jr zis the branch you treat as "pressed". - It reacts once then freezes. The
jr .loopat the end is missing or points at the wrong label, so the read never repeats. It must jump back to.loop, above the read.
What you've learnt
You read a key by reading a port (ld bc, $7FFE / in a, (c)) and testing its bit — and a held key reads as 0, not 1.
What's next
You've repeated work by looping back by hand. Next — Counting Toward Zero — we meet the proper counted loop: load a count, DJNZ, and let the machine do a thing a fixed number of times. The end of typing the same line eight times.