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.
- hdma
cgb - 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.
- Vram
Bank cgb - 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_ bank cgb - Run
fwithbankmapped at0x8000, then put back the one that was there.