← Back to Courses
module
15

How the screen actually works

Why this matters

Everything you have put on screen so far went through a ROM routine. You loaded a character into A, asked the system to print it, and something happened. What that something was has been a black box.

It is time to open it, because a game cannot afford the box. The routines are somebody else's code doing work you did not ask for, and a game redrawing its whole screen every frame needs to be doing exactly the work it chose and no more.

This section has no code in it. It is the map you need before the next ones make any sense.

A second computer for the screen

The picture on the Einstein's display is not produced by the Z80. It is produced by a separate chip - a Video Display Processor, a Texas Instruments TMS9129 - which does nothing else.

The VDP has its own memory: sixteen kilobytes of it, called VRAM, and this is the important part - VRAM is not part of the Z80's address space. It is not the 64K you have been working in. The Z80 cannot see it, cannot address it, and cannot execute from it.

What the VDP does is simple and relentless. Every frame, it reads its memory, works out what the picture should look like, and generates the video signal. It never stops and it never asks. Change a byte in VRAM and the display changes, because the next time round the VDP reads the new byte.

Your program does not draw. It edits a description of the screen, and the hardware turns that description into a picture, over and over, whether you are paying attention or not.

That is a genuinely different way of thinking about a display, and it is worth sitting with for a moment. There is no "draw" command and nothing to flush or refresh. There is a region of memory whose contents are the screen.

Getting at it

Since VRAM is not in the Z80's address space, nothing you learned in S11 reaches it:

        LD HL,3800h
        LD (HL),A       ; writes to ordinary RAM. The screen does not change.

That address exists in the Z80's memory and has nothing to do with the identically-numbered address in VRAM. Two separate memories that happen to use the same numbers.

Everything reaches VRAM through two ports, and ports are a part of the Z80 you have not met yet. OUT sends a byte to one; IN reads one back. On the Einstein:

  • Port 9 takes control information - which VRAM address you want next, or which of the VDP's registers you want to change.
  • Port 8 carries the data itself.

So writing to VRAM is a small conversation: tell port 9 the address, then send bytes to port 8. The VDP keeps count for you, so a run of bytes needs the address only once.

That indirection is the price of the VDP being a separate chip with its own memory, and it is why every drawing routine you write will start with a couple of OUT instructions.

The chip's modes

The VDP can interpret its memory more than one way, and which way it is set to changes the meaning of every byte in it - a mode built around 32 x 24 cells reads the same bytes quite differently from one built around a plain bitmap.

The obvious guess about a machine showing 40 columns of text is that it is using whatever mode this chip has for text.

The Einstein is not.

What the Einstein actually does

The Einstein runs in Graphics II, arranged as a bitmap.

Graphics II gives every one of the 32 x 24 cells its own pattern definition - eight bytes describing an 8 x 8 block of pixels. Fill the name table with 0, 1, 2, 3 and so on, so that each cell points at a different pattern, and the patterns stop being a font and become a straightforward map of the screen. 256 pixels across, 192 down, every one of them individually yours.

That is exactly what MOS sets up before your program ever runs, and it has three consequences that explain things you have already seen.

The text on screen is pixels. MOS draws its font into the bitmap, six pixels wide, which is how forty columns fit across a screen that has no 40-column mode. When you print a character, the ROM works out which pixels to change. There are no character cells involved at all.

Which is why you could draw a line over the text in S10 without setting anything up. The display was already a bitmap, so the ROM's line routine had somewhere to draw, and the text stayed put because it is made of the same pixels.

And it is why the ROM keeps a copy of the text elsewhere. A bitmap cannot be read back as characters - once a letter is pixels, it is just pixels. So MOS maintains its own list of what characters it has printed where. That is the buffer the line-input call in S9 handed back to you, complete with the prompt and a row of trailing spaces. It was reading its own notes.

Why MOS draws characters rather than choosing them

Store the font once as patterns, and printing a character could become one byte into the name table - pick a shape, put it in a cell, done. That is how a tile-based text system usually works, and it is why S17 can make printing nearly free once you take the name table over yourself.

That trick needs a character to occupy one cell of its own. Here, it does not: a cell is eight pixels wide, and MOS's font is six.

 pixels   0  1  2  3  4  5  6  7 | 8  9 10 11 12 13 14 15 | 16 ...
 cells    |------- cell 0 -------|------- cell 1 ---------|
 chars    |---- char 0 ----|---- char 1 ----|---- char 2 -...

Character 0 happens to fit inside cell 0. Character 1 starts at pixel 6 - which is inside cell 0 - and finishes in cell 1. Character 2 straddles cells 1 and 2. The alignment only comes back round every four characters, because four sixes is twenty-four pixels, which is three whole cells.

So there is no single pattern you could store, once, that simply is the letter A sitting in a cell of its own - which cell a character falls across, and where within it, depends on which of the four alignments it lands in. That is why MOS composites each character's pixels into the bitmap at print time instead of pointing a cell at a ready-made glyph.

The font itself ends up at 1800 for a more ordinary reason: that is the one 2K gap the rest of the layout leaves spare (see "What is where", below), and the sprite pattern table needs a 2K-aligned home too, with nowhere else to go. Sprite patterns and the machine's font end up sharing the same 2048 bytes because there was one gap and two things that needed it.

What forty columns costs

A tile-based text screen prints a character by writing one byte into the name table. MOS cannot, because of the misalignment above - printing a character here means working out which bitmap pixels it touches and setting them directly, every time, rather than pointing at a shape that already exists.

For a game, paying that cost on every character is not worth it, which is why S17 takes the name table back and uses real tiles instead. But forty columns of text on a thirty-two column display was not a mistake on MOS's part - it is a trade, made so that the ROM could offer more columns than the hardware's cells provide.

What is where

Sixteen kilobytes of VRAM, laid out like this:

VRAM What lives there
0000-17FF The pixels. Six kilobytes, as 8-byte patterns
2000-37FF Colour, one entry per pattern row
3800-3AFF The name table - which pattern each cell uses
3C00-3FBF MOS's private copy of the text. The VDP never reads it.

The pattern table is the one that matters. To put a pixel on screen you work out which byte of it holds that pixel and which bit within the byte, and set the bit. The arithmetic is not obvious, because the pixels are not stored in the order you would expect, and that is what the next section is for.

Writing FF to the first eight bytes of 0000 puts a solid white 8 x 8 block in the top-left corner of the display. That is the whole mechanism, in one instruction repeated eight times.

Two separate 16K and 64K spaces sharing the same address numbers is easy to picture wrong. Appendix IV - The memory map lays both out side by side, byte by byte, with a spreadsheet version alongside it if you want to scroll through it rather than read it as prose.

Sprites

The VDP has one more trick, and it is the reason this hardware is worth having for a game.

A sprite is a small picture the chip draws over the background, whose position you set by writing two bytes. The background is not disturbed - the VDP composites the sprite on top as it generates each frame - so moving one does not mean redrawing anything underneath.

The chip provides:

  • 32 sprites, each on its own layer, sprite 0 in front.
  • Sizes of 8 x 8, 16 x 16 or 32 x 32 pixels.
  • Positioning to the single pixel, independent of the 8 x 8 cell grid.
  • A coincidence flag, set when any two sprites overlap by even one pixel.

Two limits matter more than they sound. Only four sprites can appear on any one horizontal line - a fifth on the same line simply is not drawn. And the coincidence flag tells you that two sprites touched, not which ones or where; working out what to do about it is your job.

It is worth being precise about what the hardware does for you here, because it is easy to over-read. The VDP handles layering and drawing. It does not handle movement - you change the position bytes yourself, every frame. And it does not handle collisions in any useful sense; it raises a flag and leaves the rest to you.

Why any of this is worth knowing

Because from here the screen stops being something you ask for and becomes something you own.

Everything the rest of this course does with graphics - plotting, drawing, moving things smoothly, getting a whole screen redrawn inside a single frame - is built on the three facts in this section. The picture is a region of memory. That memory belongs to another chip and is reached through two ports. And the arrangement the Einstein happens to be in is a bitmap, which means every pixel is available to you and none of them is anyone else's business.

Summary

  • The picture is produced by a separate chip, a TMS9129, with its own 16KB of memory called VRAM.
  • VRAM is not in the Z80's address space. LD (HL),A cannot reach it. Everything goes through port 9 for control and port 8 for data.
  • The VDP reads VRAM every frame and generates the picture from it. Your program edits a description; it never draws.
  • The chip can interpret VRAM more than one way. The Einstein uses Graphics II as a bitmap, 256 x 192 pixels, with the pixels living at 0000-17FF.
  • MOS's six-pixel characters straddle the display's eight-pixel cells, so no single cell can just point at a ready-made letter - which is why MOS draws each one into the bitmap instead.
  • The text at the prompt is drawn into that bitmap as pixels, six across per character. That is why 40 columns fit, why graphics and text coexist, and why the ROM keeps its own copy of the text at 3C00.
  • The chip provides 32 sprites, drawn over the background, positioned to the pixel - but only four per line, and it moves nothing for you.

Next

S16. Writing to video memory. You know where the picture lives and that two ports are the only way in. Next you use them - designing a shape on squared paper, putting it on screen in a colour you chose, and then showing the same shape in a second place for the cost of a single byte.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.