The Display
The CHIP-8 display is a 64×32 monochrome screen. Each pixel is either on or off — no color, no grayscale, just two states.
(0,0) (63,0)
┌──────────────────────────────────────────────┐
│ │ 32 rows
│ │
└──────────────────────────────────────────────┘
(0,31) (63,31)
The framebuffer
#![allow(unused)] fn main() { pub const WIDTH: usize = 64; pub const HEIGHT: usize = 32; pub struct Display { framebuffer: [[bool; WIDTH]; HEIGHT], // [[bool; 64]; 32] } }
A 2D array of booleans. Indexed as framebuffer[y][x] — row first, then column. The array is private; the outside world reads pixels via pixel(x, y) and draws via draw_sprite.
clear()
#![allow(unused)] fn main() { pub fn clear(&mut self) { self.framebuffer = [[false; WIDTH]; HEIGHT]; } }
Sets every pixel to off. Called by the 00E0 CLS opcode.
draw_sprite()
A sprite is a sequence of bytes where each byte represents one 8-pixel row. Bit 7 (MSB) is the leftmost pixel:
byte 0xF0 = 0b11110000:
████░░░░
For each bit in the sprite that is 1, the corresponding pixel on screen is XORed:
screen OFF + sprite 1 = pixel ON (no collision)
screen ON + sprite 1 = pixel OFF (collision!)
screen ON + sprite 0 = pixel ON (unchanged)
XOR means drawing the same sprite twice returns the screen to its original state. If any pixel goes from ON to OFF during a draw, there was a collision — draw_sprite returns true, and the CPU stores that in VF.
Wrapping
Sprites that extend beyond screen edges wrap around:
#![allow(unused)] fn main() { let px = (x + col) % WIDTH; // horizontal wrap let py = (y + row) % HEIGHT; // vertical wrap }
A sprite drawn at x=62 will have its rightmost pixels appear at x=0 and x=1.
Display is owned by the CPU
#![allow(unused)] fn main() { pub struct Cpu { // ... pub display: Display, } }
The DRW opcode calls self.display.draw_sprite(...). The CLS opcode calls self.display.clear(). The main loop reads pixels via cpu.display.pixel(x, y) to build the render buffer.
Rust concepts used here: The 2D boolean array (
[[bool; WIDTH]; HEIGHT]) uses the fixed-size array syntax from Primitive Types and Arrays. Iterating over sprite bytes uses Iterators and Destructuring. See CLS and DRW for the opcodes that drive this module.