Carl
all lessons

Showing it off — the display

A program that runs but never produces a picture is just a long sequence of voltage changes. The video chip takes a region of memory and turns it into something you can actually look at. It's the most demanding peripheral on the board, and the rules around talking to it are the reason "how to draw a screen" became its own engineering specialty.

cpu 6502 video display framebuffer vblank

You now have a CPU running programs, ROM holding them, RAM holding the working values, the clock keeping the beat, and an I/O controller handling inputs and outputs. The system runs. But the CPU could compute the answer to every question in the universe and you’d never know — none of those chips produces light. Nothing is visible.

Time for the most demanding chip on the board: the video chip.

The video chip is the system’s mirror

The video chip is what shows off what everything else has been up to. Computed a value? The video chip will display it. Loaded a sprite? The video chip will draw it. Played a game for an hour? The video chip showed every frame of it.

On a Commodore 64 it’s a chip called the VIC-II, on the Apple II it’s a small array of TTL chips doing similar work, on the NES it’s the PPU. They all share the same job: take a chunk of memory that contains pixel data (or character codes, or sprite info), and convert it into the voltage waveforms that make a TV or monitor show a picture.

That last word matters: continuously. The video chip never stops. Sixty times a second (in NTSC) it has to deliver a complete picture, top to bottom, left to right, on schedule, every single time. Miss a frame and the screen tears or flickers visibly. There is no “later.”

Memory-mapped video — same trick, more demanding

Here’s the seam you already know. The video chip connects to the CPU’s address bus and data bus exactly like RAM and the I/O controller do. Some range of addresses belongs to it. Whatever bytes the CPU writes into that range become the picture the video chip draws.

The simplest version: a framebuffer. Suppose the video chip owns addresses $2000 through $3FFF — that’s 8 KB. If it’s wired so that each byte represents 8 pixels in a row of a black-and-white display, then 8 KB × 8 pixels = 64 KB of pixels = a roughly 320×200 monochrome screen. The CPU writes a 1 bit into a byte and a pixel turns white. Writes a 0 and it turns black. That’s the whole graphics interface.

LDA #%11110000   ; pattern: left half white, right half black
STA $2000        ; → those 8 pixels at the top-left of the screen
                 ;   change in whatever's-the-next-frame

You wrote 1 byte to memory and 8 pixels changed. Same STA from lesson 1. The “screen” is just a window onto memory, refreshed sixty times a second by the video chip.

The race — the video chip never stops reading

Here’s where the video chip earns its reputation. RAM and ROM only respond when the CPU talks to them — they sit quietly until addressed. The video chip is reading from its memory range all the time, every microsecond, because it has to keep delivering pixels to the screen.

That creates a problem. If the CPU writes a byte into the framebuffer while the video chip is in the middle of drawing the row that contains that byte, the result is unpredictable. Half the row is the old image, half is the new image. Visually: tearing. Sometimes flicker. Sometimes a sprite that “shears” diagonally as it moves.

The fact that the CPU and video chip are both reading and writing into the same memory at the same time is one of the original problems of real-time graphics. It hasn’t gone away. It’s still the reason modern OSes have a “vsync” setting in your game’s options menu.

Vertical blank — the safe window

The way the video chip redraws the screen has structure. It walks left-to-right across each row, top-to-bottom across the rows, and when it hits the bottom of the screen it has to snap its electron beam back to the top before starting the next frame. That snap-back takes a brief moment — a millisecond or so — during which the video chip isn’t drawing anything.

This window is called the vertical blank (or “vblank”). And it’s the moment the CPU has been waiting for.

The pattern, on every system from a 6502 up through modern GPUs:

main_loop:
  ... compute the next frame's worth of stuff ...
  ... wait for vblank ...
  ... write all changes to the framebuffer ...
  jmp main_loop

The CPU prepares the new frame off-screen — usually in a separate buffer in RAM — and waits until vblank arrives. Then it copies the new frame into the video chip’s memory as fast as it can, finishing before vblank ends and the video chip starts drawing again. Result: a clean frame, no tearing, no flicker.

How does the CPU know vblank just started? The video chip tells it. Either:

  • It sets a flag in one of its memory-mapped registers, which the CPU can poll. (Slow, wastes cycles.)
  • It fires an interrupt — usually NMI, since missing vblank is catastrophic — and the CPU’s vblank handler does the framebuffer copy. This is what NES games use.

That second option is exactly the NMI use we hinted at in lesson 4. The “missing the deadline drops a frame” pattern is literally what NMI was put on the chip for.

Sprites and tile maps — the trick that made games possible

A 320×200 framebuffer of pixel bytes takes 8 KB of memory if it’s black-and-white, more if it’s color. On a 6502 system with maybe 64 KB of total memory shared with everything else, you couldn’t afford to update every pixel of the screen every frame. The CPU isn’t fast enough.

The video chip (in its real-life forms — VIC-II, PPU, etc.) had clever ways around this:

  • Tile maps. Instead of storing every pixel, store a grid of which 8×8 tile goes in each cell. Each tile is 8 bytes (monochrome) or 16 bytes (with a 2-bit color palette). To “draw” a level, you write tile indices into a small grid in memory — maybe 32×30 = 960 bytes. The chip looks up the actual pixels in another fixed table. Updating a tile cell costs one byte.
  • Sprites. Up to N (eight, typically) hardware-managed objects that float over the background. Each sprite has a position, an index into a graphic data area, and maybe a color. Move a sprite by writing 2 bytes (its X and Y position). The chip handles all the per-pixel work of compositing it over the tile map.
  • Smooth scrolling. A register that says “shift the whole background N pixels to the left.” One byte writes you a side- scrolling Mario. Whole genres of games depended on this single feature working in hardware.

These tricks turned what would have been impossible (a million pixel writes per second) into something the 6502 could plausibly do: a few hundred bytes of writes per frame, the chip doing the heavy lifting for free. The video chip was where most of the cleverness in 1980s game programming lived.

What’s next

You now have a working system. The CPU executes. ROM holds the program. RAM holds the data. The clock keeps everyone on the beat. The I/O controller talks to the outside world and pulls the CPU’s attention when something happens. The video chip shows it all off.

That’s a complete computer.

There’s one more chip we promised to introduce — the co-processor. It’s the answer to “the ISR has to be quick but the work has to happen now”: hand the work off, let the CPU keep running the main program, deal with the result whenever the co-processor returns. Math co-processors, sound chips that generate audio independently, DMA controllers that can move bytes without bothering the CPU — they’re all the same pattern in different costumes.

After that, the series turns from “what is each piece” to “how do they all work together to run real code” — and that’s where your 6502 simulator picks up the story.