Step 1 of 5 · Reading · ~3 min
Read
Production
Putting It Together
Every opcode you wrote in the last six lessons is now in one file. What is left is the part around them: the loop that drives the CPU at the right pace, and the one instruction, CXKK, that needs something from outside the machine. After this lesson your emulator runs whole programs, not single instructions.
One frame of the machine
A CHIP-8 game is built around the 60 Hz timers, so the natural unit of emulation is a frame: a sixtieth of a second. Each frame does the same four things in the same order.
power on RAM zeroed, font at 0x050, ROM at 0x200, PC = 0x200
one frame 1. run N instructions (the CPU's share of 1/60 s)
2. tick DT and ST down by one (the 60 Hz clock)
3. show the screen (host)
4. read the keypad for the next frame (host)
N is the CPU speed divided by 60. ROMs were written for machines running somewhere between 500 and 700 instructions per second, which is 8 to 12 per frame; many emulators make it adjustable because a game tuned for one speed plays too fast or too slow on another. What must not depend on N is time: however many instructions a frame holds, the timers tick exactly once.
In the harness a frame is FRAME n: run n instructions, then one tick. A trace on a program this lesson's tests do not use. The ROM is 6002 F015 7101 1204: set DT to 2, then loop forever adding 1 to V1. Three FRAME 4 commands give:
frame instructions run V1 DT after tick
1 LD, LD, ADD, JP 1 1
2 ADD, JP, ADD, JP 3 0
3 ADD, JP, ADD, JP 5 0
Frame 1 spent two of its four instructions on setup, so it counted less. The timer reached 0 after two ticks regardless.
CXKK needs a random number
CXKK sets VX to a random byte ANDed with KK. A real emulator calls its random number generator. A test cannot, because the expected output would change from run to run, so the harness lets the test supply the "random" bytes: RAND 3A 7F queues two bytes, and each CXKK takes the oldest queued byte. An empty queue yields 0. The mask is applied afterwards, so RND V1, 0x0F fed 0xB7 leaves 0x07. This is also how real test setups work: the generator is a dependency you inject.
Testing a whole emulator
A single failing opcode surfaces as a wrong picture many frames later. Work from small to large: run the IBM logo (display, jumps, ANNN), then programs that exercise one family each. The community test ROMs by Timendus (the CHIP-8 test suite, with the IBM logo, Corax+, a flags test and a keypad test) print their own pass or fail marks and are the standard next step once this harness passes. When something is wrong, find the first frame where your screen differs and read the instructions just before it.
Common mistakes
- Decrementing the timers inside the instruction loop instead of once per frame.
- Ticking before running the instructions in a frame; the order in the harness is run, then tick.
- Forgetting to mask the random byte with KK, or taking the newest queued byte instead of the oldest.
- Treating an empty random queue as an error instead of 0.
Your exercise
The starter contains every instruction except CXKK, and parses RAND and FRAME without acting on them. Add the three, then run the larger programs in the tests: they use calls, the stack, memory, drawing, the keypad and the timers together.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…