CLS — Clear Screen
00E0 is one of the simplest opcodes in the specification: clear the display. Every pixel goes off. The screen is blank.
Implementing it is one line. But understanding when and why it is used reveals something important about how animation works in CHIP-8.
Encoding and implementation
00E0 is a fixed opcode — no parameters, no register indices, no addresses. It shares the 0x0 prefix with 00EE (RET), so the decode uses a nested match on the full opcode value:
#![allow(unused)] fn main() { 0x0 => match opcode { 0x00E0 => { self.display.clear(); } 0x00EE => { // RET } _ => println!("unknown: {:#06X}", opcode), }, }
And the implementation in the Display module:
#![allow(unused)] fn main() { pub fn clear(&mut self) { self.framebuffer = [[false; WIDTH]; HEIGHT]; } }
A single assignment resets the entire 64×32 boolean array. Rust initializes the whole thing at once — no loop, no index arithmetic.
Why animation needs CLS
The CHIP-8 display is a framebuffer: it remembers its state until something explicitly changes it. If you draw a sprite and then draw it again in a new position, the old one is still there. The DRW opcode uses XOR — drawing the same sprite twice erases it — but that only works if you redraw in exactly the same place. For anything moving freely on screen, the approach is different:
CLS— erase everything- Draw all sprites in their new positions
- Wait for the next frame
- Repeat
Without CLS, sprites leave a trail. With it, each frame is a fresh canvas.
In the test program in main.rs, we do not use CLS because we draw the font digits once at startup and never move them. But in any game — a ball moving, a player walking — the CLS at the top of each frame is essential.
Rust concepts used here: The nested match is introduced in CALL and RET and covered in depth in Pattern Matching. The
[[false; WIDTH]; HEIGHT]array initialization uses the array syntax from Primitive Types and Arrays. See The Display for the framebuffer, and DRW — Drawing Sprites for the other side of the animation loop.