Skip to content

Palette LUT — NTSC / PAL switching

hdl/palette_lut.sv is a 256-entry × 24-bit RAM. The address is the 8-bit Atari hue:luma value coming out of color_resolver (bits[7:4] = hue 0..15, bits[3:0] = luma 0..15). The data is straight RGB888.

All sixteen luminances are distinct. A colour register has no bit 0 — gtia_reg_file masks it on the write, where the hardware drops it — so register colours only ever reach the eight even entries. GTIA mode 9 does not go through a register: the screen nibble lands straight in the luminance field, which is why that mode gives sixteen shades where the registers give eight, and why the odd entries have to exist.

The TMDS / DVI scan-out path reads palette_lut[resolved_color] once per pixel-clock and feeds the 24-bit result into the encoder.

Atari’s NTSC and PAL hardware encode the colour-burst phase differently. The hue → carrier-phase mapping is rotated, the saturation is slightly different, and PAL’s alternating-line phase reversal kills the chroma-distortion issue NTSC has. Result: the same hue:luma byte produces visibly different colours on a real NTSC vs PAL Atari. Programs that target one region picked their hue values to look right under that region’s palette.

To stay colour-accurate, we need both reference palettes available.

The LUT is fully writeable at runtime via the chiplet-ext registers:

$D483 PAL_R write red byte
$D484 PAL_G write green byte
$D485 PAL_B write blue byte
$D486 PAL_IDX write 8-bit Atari index — commits {R,G,B} into entry IDX

Two delivery options for the boot defaults:

  1. Synth-time bake-in. Pass INIT_FILE to palette_lut:

    palette_lut #(.INIT_FILE("hdl/palette/atari_ntsc.hex")) u_lut (...);

    Two reference tables ship in the repo:

    • hdl/palette/atari_ntsc.hex — NTSC-shaped colour wheel
    • hdl/palette/atari_pal.hex — PAL-shaped colour wheel

    Pick whichever matches the dominant target region. The other one ships in firmware as a 768-byte blob and gets paged in via the chiplet-ext writes when the user toggles region.

  2. Pure firmware-driven. Leave INIT_FILE empty (LUT comes up black at /G_RST), and have the PS-side boot firmware push 256 entries via writes to $D483-$D486 during the cold-boot sequence. Region toggle = re-push the other table (≈ 1 ms at 1.79 MHz bus).

We default to the synth-time bake-in since it gives a sensible power-on display before any firmware has run.

atari_ntsc.hex / atari_pal.hex are plain $readmemh-compatible hex files: 256 lines, one 24-bit RGB888 per line, MSB = R, LSB = B. Header lines (//) are tolerated by $readmemh so the files are self-documenting.

The two reference tables in this repo are derived approximations based on the NTSC and PAL hue-rotation tables widely quoted in the Atari hardware manuals + Altirra. They aren’t claimed to be pixel-exact reproductions of any specific real silicon’s analogue colour-burst output — the chiplet-ext write path lets you replace them with whatever your favourite reference says.