Skip to main content

Module ppu

Module ppu 

Source
Expand description

The pixel-processing unit, which draws the screen.

The picture comes from six modules: tile holds the pixels, map names which tile goes in which cell, bg and window place the two layers that read a map, obj carries the sprites drawn over them, and palette decides what a pixel’s colour index becomes.

§Reaching video memory

VRAM and OAM are not always the CPU’s to write. The PPU takes them while it draws, and a write made then is dropped. Every function that writes takes an Access, which records how the caller knows its write will land.

Direct means the caller is already inside a VBlank or has the LCD switched off. It is the cheap one, and Vblank::with and with_lcd_off are what produce it. The window’s length is not enforced, so a closure that runs past the end of a VBlank loses the rest of its writes.

Polled claims nothing about when it is called, and waits for the PPU itself instead. It reaches far more of the frame than VBlank alone, since HBlank recurs on every line, and it blocks until the whole write is through. For reference, every video write in GBDK works this way.

ⓘ
let vblank = unsafe { Vblank::listen() };

// A frame's worth of updates, taken inside the window
vblank.with(|d| {
    map::write(d, bg::map(), col % 32, 0, LEVEL.sub(col, 0, 1, 18));
    obj::set(d, 0, player);
});

// A bulk load: the screen goes blank, and the window has no deadline
ppu::with_lcd_off(|d| tile::write_all(d, 0, &TILESET));

// Nowhere in particular: let the write wait for the PPU itself
tile::write(Access::Polled, 5, &spark);

Work that has to happen between frames belongs before Vblank::with, not inside it: decide what to draw first, then take the window and spend it on writes.

§Writing from a handler

Much of what follows reads a register, changes a bit and writes it back, LCDC most of all: bg, window, obj and tile each own a bit of it and leave the rest alone. A handler doing the same to the same register races whatever it interrupted, and one of the two changes is lost.

palette fares worse. Its colours travel through an index register, set once and stepped by the hardware, so a handler writing a palette of its own sends the rest of the interrupted write into whichever palette it left selected.

So a program driving a raster effect from a STAT handler should keep its other writes to those registers in VBlank, where the two cannot overlap.

§Frame pacing

A program that uses this module links a weak _on_vblank advancing a frame counter. #[gb::rt::interrupt(VBlank)] replaces it, so that handler has to call frame_tick itself or Vblank::wait never returns.

Modules§

bg
The background: a tilemap seen through a window the size of the screen.
hdmacgb
The Game Boy Color’s copier into video memory.
map
Tilemaps: two 32 by 32 grids of tile indices.
obj
Objects: the 40 sprites the PPU draws over the background.
palette
Palettes: what the two bits of a pixel turn into.
tile
Tile data: the 384 slots at 0x8000, sixteen bytes each.
window
The window: a second tilemap laid over the background.

Structs§

Vblank
The frame clock: proof that the VBlank interrupt is reaching the counter.

Enums§

Access
How a write reaches video memory.
VramBankcgb
Which half of the Game Boy Color’s video memory the CPU sees at 0x8000.

Functions§

frame_tick
Advance the frame counter.
with_lcd_off
Turn the LCD off for the length of f.
with_vram_bankcgb
Run f with bank mapped at 0x8000, then put back the one that was there.