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.
RGB-332 palette (256 colours): one colour byte RRRGGGBB. The old …COLH fine registers are still there but are ignored. 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: 62880 / (F+1) Hz (divisor clock = 76×262×60 / 19) (all sound chip frequency registers, including noise pitch). LOAD ROM accepts any file as raw bytes, mapped to $C000–$FFFF (16KB, smaller files are mirrored).
MEMORY MAP: $0000–$00FF zero page · $0100–$01FF stack · $0200–$9FFF RAM · $A000–$A0FF BEAM chip I/O (64 regs, mirrored) · $A100–$BFFF open bus (reads $FF, the void stares back) · $C000–$FFFF cartridge ROM.
HIRES: VMODE 1 = 320×192 1bpp bitmap (40 bytes per line from page VBASEH, PFCOL on BGCOL). VMODE 2 = 40×24 text (chars at VBASEH, 8×8 font at FONTH, one colour byte per cell at ATTRH). The mode is read live, so switch it mid-frame for split screens. FLOPPY: set FDTRK, FDSEC, FDDMAL/H, write 1 (read) or 2 (write) to FDCMD, check FDSTAT. KEYBOARD: read ASCII from KBD. Load cart 16 for a full DOS.
PALETTE (click to copy)
ASSEMBLER
label: NAME = expr .org .byte .word .text "hi" #<lbl #>lbl $hex %bin 'c'. The ROM lives at $C000–$FFFF. If you don't write a reset vector, it points to the first .org. BRK works like a real 6502: it pushes PC+2 and P, sets I, and jumps through the IRQ vector at $FFFE. If you don't set that vector, it points to an RTI at $FFF9.