Step 1 of 5 · Reading · ~4 min
Read
Instructions
ALU & Skip Instructions
After this lesson your emulator can compute with two registers and decide whether to skip the next instruction. Those are the two things every game needs: arithmetic for scores and positions, and a conditional that turns "is the ball at the wall?" into a different path through the ROM.
How this and the next lessons are tested
From here on the input is a real program. Line 1 is the ROM as a hex dump (two digits per byte, spaces ignored); every other line is a command for the machine. RUN n executes n instructions and SHOW prints the state:
6A05 7A01 8AA4
RUN 3
SHOW
PC=206 I=000 SP=0 DT=00 ST=00
V=00 00 00 00 00 00 00 00 00 00 0C 00 00 00 00 00
The ROM sits at 0x200. PC is the next address to fetch, and V lists V0 to VF as two hex digits each. In that trace 6A05 set VA to 5, 7A01 made it 6, and 8AA4 added VA to itself for 0C.
The 8XYn family
The opcode is four nibbles: 8, then X (the destination), then Y (the source), then n, which picks the operation.
n name effect
0 LD VX = VY
1 OR VX = VX | VY
2 AND VX = VX & VY
3 XOR VX = VX ^ VY
4 ADD VX = VX + VY (low 8 bits), VF = carry
5 SUB VX = VX - VY (mod 256), VF = 1 if no borrow
6 SHR VF = lowest bit of VX, then VX = VX >> 1
7 SUBN VX = VY - VX (mod 256), VF = 1 if no borrow
E SHL VF = top bit of VX, then VX = VX << 1 (low 8 bits)
Any other n (8 to D, F) is not an instruction; ignore it.
VF is a result, not a side effect
Four of those operations produce a flag. Work through ADD with VX = F0 and VY = 20: the true sum is 0x110, which needs nine bits. VX keeps the low eight, 0x10, and VF becomes 1.
"No borrow" means the subtraction did not wrap: SUB sets VF to 1 when VX >= VY, including when they are equal. Cowgod's reference writes "VX > VY", and a ROM that compares equal values will tell you which one the hardware did. With VX = 05 and VY = 09 the result is 0xFC and VF is 0.
Two ordering rules decide whether the tricky cases work:
- Read both operands first, write VX, and set VF last. When X is F, say
ADD VF, V1with VF = FF and V1 = 01, the sum 0x100 lands in VF as 00 and then the carry overwrites it with 1. The flag wins. - When Y is F you read VF before the flag is set, so
SUB V0, VFsubtracts the old VF.
Logic operations leave VF alone in this course. The original COSMAC VIP reset it to 0, and the quirks lesson shows how to switch that on. Shifts act on VX in place and ignore VY here; the COSMAC behaviour is also a quirk for later.
Skips
Fetching an instruction already moved PC past it. A skip moves PC two bytes further, so the next instruction is fetched from 4 bytes after the skip instruction:
200: 6A05 VA = 5
202: 3A05 VA == 05? yes -> PC was 204, becomes 206
204: 6B09 skipped
206: 6C01 runs next
3XKK skip if VX == KK 4XKK skip if VX != KK
5XY0 skip if VX == VY 9XY0 skip if VX != VY
5XYn and 9XYn with n other than 0 do not exist; treat them as no-ops that skip nothing.
Common mistakes
- Writing VF before VX, so
ADD VF, V1ends with the sum in VF instead of the carry. - Using
>for SUB, which clears VF when the operands are equal. - Forgetting to wrap subtraction to 8 bits. In most languages
5 - 9is -4, not 0xFC. - Skipping one byte, or skipping four, instead of exactly one instruction.
- Shifting VY into VX. The result looks plausible until VY differs from VX.
Your exercise
The starter already parses the ROM, runs LD VX, KK and ADD VX, KK, and prints the state. You add the nine 8XYn operations and the four skip opcodes. The most likely failure is the X = F case, so test one before you submit.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…