Skip to content

Step 1 of 5 · Reading · ~2 min

Read

Production

ROM Loader & Memory Map

A .ch8 file is the simplest binary format there is: raw bytes, copied verbatim into RAM at 0x200. No magic, no header, no checksums.

python

That's the entire loader.

The memory map (Cowgod's reference)

0x000 - 0x04F   font sprites (16 hex digits x 5 bytes each = 80 bytes)
0x050 - 0x1FF   reserved (was the original interpreter on COSMAC VIP)
0x200 - 0xE9F   program / data
0xEA0 - 0xEFF   call stack (16 levels x 2 bytes)  -- COSMAC convention
0xF00 - 0xFFF   display refresh buffer (64x32 / 8 = 256 bytes)

Modern emulators don't have to honor the 0xEA0+ layout — they keep the stack and display as host-side arrays — but the font and the 0x200 start address are not negotiable. Every ROM ever written assumes them.

Who writes the font?

You do. At startup, before any ROM is loaded:

python

Each digit is 4 pixels wide and 5 rows tall. Encoded as 5 bytes; only the high nibble of each byte carries pixels (low nibble is always 0). Look at digit 0:

1111 0000   F0   ####
1001 0000   90   #  #
1001 0000   90   #  #
1001 0000   90   #  #
1111 0000   F0   ####

The ROM calls FX29 with a digit (0..F) in VX; that op sets I to the address of the corresponding sprite. Then DXYN with N=5 draws it.

How big can a ROM be?

3584 bytes (0xFFF - 0x200 + 1). Most are 200-1500. The biggest stock CHIP-8 game I've seen is just under 3 KiB. SCHIP keeps the same limit; XO-CHIP extends RAM to 64 KiB with bank-switch opcodes.

Validation

There is no magic, no checksum. A robust loader can:

  • Reject ROMs > 3584 bytes (won't fit).
  • Warn on odd byte counts (probably truncated, but legal).
  • Bail if the file doesn't exist.

That's it. The rest is trust.

Up nextQuirks: COSMAC vs ModernProduction

Discussion

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

Sign in to post a comment or reply.

Loading…