OWNER'S MANUAL
There is no framebuffer. The picture only exists while the beam is drawing it. Each scanline takes 76 CPU cycles, and each cycle draws 3 pixels. The first 68 pixels of every line are HBLANK, which is invisible, and the next 160 are on screen. Lines 0–191 are visible. Lines 192–261 are VBLANK, the time for game logic.
Recipe: sta VSYNC waits for line 0. Then set colours and do sta WSYNC once per line, 192 times. After that, run your game logic and loop.
All register names below are already defined in the assembler. The playfield is 20 blocks, each 4px wide: PF0 bits 3→0, then PF1 bits 7→0, then PF2 bits 7→0, from left to right. The right half repeats the left half, or mirrors it if PFCTRL bit 0 is set. The sprite is 8 bits with each bit 2px wide, and bit 7 is on the left.
BRGB-11 palette (2048 colours): colour byte RRRGGGBB, plus the fine register (BGCOLH/PFCOLH/SPRCOLH) bits 0-2, which add the lowest R, G, and B bits → R4 G4 B3. Hi-res playfield: set PFCTRL bit 1, and the playfield then reads 20 bytes (1px per bit, 160px) from PFPTRL/PFPTRH. With mirroring, it reads 10 bytes. DAC: write raw 8-bit samples to DAC, where 128 is silence. Writing once per line gives ~15.7kHz audio. Sound frequency: 4 × CPU clock (4,778,880) / 76 / (F+1) Hz. LOAD ROM accepts any file as raw bytes, mapped to $F000–$FFFF.
PALETTE (click to copy)
ASSEMBLER
label: NAME = expr .org .byte .word .text "hi" #<lbl #>lbl $hex %bin 'c'. The ROM lives at $F000–$FFFF. If you don't write a reset vector, it points to the first .org. BRK pauses the machine, so the CPU gets a coffee break.