Skip to content
Lesson 4 of 13

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, V1 with 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, VF subtracts 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, V1 ends 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 - 9 is -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.

Up nextStack & JumpsInstructions

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…