Skip to content

Video VRAM and the DMA blitter

codingncaffeine edited this page Jun 28, 2026 · 1 revision

Video: VRAM & the DMA blitter

The Game.com screen is 200×160 pixels, 2 bits per pixel (four grayscale levels), refreshing at ~59.73 Hz. Two things about the video hardware surprise people: the framebuffer is stored column-major, and most drawing goes through a hardware blitter rather than direct pixel writes.

The palette

There are five physical shade levels; the LCDC register's bits [5:4] pick which four of the five the two-bit pixel values map to. The shades, warmest (LCD "off") to darkest:

Index Approx. RGB
4 DF FF 8F lightest (a warm LCD green-white)
3 8F CF 8F
2 6F 8F 4F
1 0F 4F 2F
0 00 00 00 darkest

VRAM layout (the column-major surprise)

VRAM is 16 KB at 0xA000, split into two 8 KB pages (the LCDC register selects which one is displayed, enabling double-buffering). The catch is the orientation: each display column is a contiguous 40-byte strip (160 pixels packed 4-per-byte, MSB first), and the strips are spaced 40 bytes apart. In other words, to find pixel (x, y):

byte    = VRAM[page_base + 40 * x + (y >> 2)]
shade   = (byte >> ((3 - (y & 3)) * 2)) & 3

If you assume a normal row-major framebuffer, you get a transposed, scrambled image — a classic "why is everything rotated?" moment.

The DMA blitter

VRAM is write-only to the CPU, and games rarely poke it directly. Instead the SM8521 has a hardware DMA blitter that copies rectangular blocks into VRAM, programmed through a set of registers (control, source/destination X/Y, width/height, palette remap, source bank). Writing the control register with its start bit kicks off the blit, which runs to completion.

It supports four transfer modes:

  • VRAM → VRAM (move/scroll on-screen blocks)
  • ROM → VRAM (draw sprites/tiles straight from cartridge or kernel ROM)
  • Extended RAM → VRAM
  • VRAM → Extended RAM

Each pixel is read (2 bits), optionally skipped in transparent mode, remapped through a palette byte, and written into the destination's 4-pixels-per-byte packing — with X able to run forwards or backwards and Y able to count up or down. Getting this blitter right is what makes the marquee titles (the ones with real sprite work) render correctly.

When a blit finishes it raises the DMA interrupt — see Interrupts & Timers.

Clone this wiki locally