Step 1 of 5 · Reading · ~3 min
Read
Display & Input
Input & Timers
A program that only computes and draws is a screensaver. After this lesson your emulator reads the keypad and keeps time: the two inputs that make a ROM a game. Both are small, and both are famous for one off-by-a-rate bug, so this lesson spends most of its words on where they come from.
The keypad is state, not a stream
The original machine had a 16-key hex pad:
1 2 3 C a common keyboard mapping: 1 2 3 4
4 5 6 D Q W E R
7 8 9 E A S D F
A 0 B F Z X C V
The emulator's job is to remember which of the sixteen keys are currently held. In the test harness two commands change that memory: KEY k presses key k and UP k releases it (k is one hex digit). A ROM then asks about the pad with two skip instructions, and the key is named by the low four bits of a register, not by the instruction itself:
EX9E skip the next instruction if key (VX & 0xF) is down
EXA1 skip the next instruction if key (VX & 0xF) is up
They behave like the skips from the ALU lesson, with a keypad lookup as the condition. A ROM polls them every loop; nothing interrupts it.
Two timers, both counting at 60 Hz
FX07 VX = DT read the delay timer
FX15 DT = VX set the delay timer
FX18 ST = VX set the sound timer
Both timers are bytes that count down by one, sixty times per second, until they reach zero and stay there. A game sets DT to a number of sixtieths of a second and then polls it, which gives it a delay that does not depend on how fast the CPU is. While ST is above zero the machine beeps; this course tracks the value but plays nothing.
The rate is the whole point. The CPU runs hundreds of instructions per second and the timers tick at 60, so the timers are not decremented by instructions. The host owns a 60 Hz clock and tells the machine when a tick has passed. In the harness that is the TICK command; RUN 1000 must leave DT and ST exactly where they were.
A delay loop shows the effect. Starting with DT = 2:
LD V0, 2
LD DT, V0
wait: LD V1, DT read the timer
SE V1, 0 exit when it is zero
JP wait
done: ...
Between ticks the loop spins on V1 = 2. After one TICK it reads 1, after the second it reads 0 and the skip lets the program leave. The loop took two ticks of wall-clock time however many instructions it burned.
Common mistakes
- Decrementing the timers inside the instruction loop, so a faster CPU gets shorter delays.
- Letting a timer go below zero, or wrapping from 0 to 255 so a finished delay restarts.
- Swapping EX9E and EXA1, so every game plays with inverted controls.
- Indexing the keypad with the whole of VX instead of its low nibble.
- Treating a key press as a one-shot event instead of state that stays set until
UP.
Your exercise
The starter now accepts KEY, UP and TICK and keeps the key state for you. You add the tick itself (both timers down by one, never below zero), the two key skips, and the three timer instructions. Run a delay loop to check that your emulator waits on ticks, not on instructions.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…