.png)
Every character you have printed went through RST 8. Someone else's code
worked out which pixels to change, and you never saw it happen.
This section is where that stops. You are going to put a shape on the screen that you designed, in a colour you chose, without asking the ROM for anything
That last part is the whole trick behind tile-based games, and by the end of this section you will have done it.
S15 established the shape of the problem: the picture lives in the VDP's own memory, and the Z80 cannot address it. Everything goes through two ports.
Ports are a part of the Z80 you have not used yet. They are a second, much smaller numbered space - nothing to do with memory - used for talking to hardware. Two instructions reach it:
OUT (9),A ; send what is in A to port 9
IN A,(9) ; read port 9 into A
Only A can carry the byte, which is one more reason it is the busiest
register you have.
On the Einstein:
VRAM holds 16K, so an address needs fourteen bits, and a port carries eight. So an address goes in two halves, both to port 9:
First, the low eight bits of the address.
Then a byte made of three things: bit 7 clear, meaning you are addressing VRAM rather than a register; bit 6 set, meaning you intend to write; and in the bottom six bits, the top six bits of the address.
Bit 6 set is 40h, so that second byte is always 40h plus the high part of
the address:
LD A,<low byte of address>
OUT (9),A
LD A,<40h + high part>
OUT (9),A
; now every byte you send to port 8 goes into VRAM, one after another
The VDP keeps count. After the two setup bytes, each byte you send to port 8 lands at the next address along, so a run of data needs the address only once.
Address 0000. Low byte 0. High part 0. So 0 then 40h.
Address 1800. Low byte 0. The high part is 18h. So 0 then
40h + 18h = 58h.
Address 3B00. Low byte 0. The high part is 3Bh. So 0 then
40h + 3Bh = 7Bh.
The pattern never changes: low byte, then 40h plus the top six bits. In
hex it is barely arithmetic - 1800 becomes 58, 3B00 becomes 7B, and
the low byte is just the low byte.
S15 laid out the four regions. Three of them matter now.
| VRAM | What it holds |
|---|---|
0000-07FF |
Patterns - the shapes, 8 bytes each |
2000-27FF |
Colours - two per pattern row |
3800-3AFF |
The name table - which pattern each cell shows |
A cell on screen is 8 x 8 pixels, and there are 32 across and 24 down. To make a cell show something you need all three: a shape, its colours, and an entry in the name table pointing at it.
One limit for this section. Those addresses are right for cells in the top eight rows of the screen. Rows lower down read their patterns from somewhere else, for a reason that needs a section of its own. Stay in the top third for now and everything here works.
A pattern is eight bytes, one per row, one bit per pixel. A 1 shows in one colour and a 0 in the other.
So draw it on squared paper and read off the rows:
. . . # # . . . 00011000
. . # # # # . . 00111100
. # # # # # # . 01111110
# # # # # # # # 11111111
# # # # # # # # 11111111
. # # # # # # . 01111110
. . # # # # . . 00111100
. . . # # . . . 00011000
And you can write it in binary, which keeps the picture visible in your source:
shape: DEFB 00011000b
DEFB 00111100b
DEFB 01111110b
DEFB 11111111b
DEFB 11111111b
DEFB 01111110b
DEFB 00111100b
DEFB 00011000b
That is a diamond, and you can see that it is a diamond, which you cannot with
18h, 3Ch, 7Eh, FFh. This is the one place in the whole course where binary
literals earn their keep.
Here is the thing that makes this section easy, and it is worth understanding rather than just copying.
Right now the name table is a list of ascending numbers. Cell 0 shows pattern 0, cell 1 shows pattern 1, cell 34 shows pattern 34, all the way up. That is what makes MOS's screen a bitmap - every cell has a private pattern, so changing a pattern changes exactly one cell.
Which means you do not need to touch the name table to see your shape. Overwrite pattern 34 and cell 34 changes. Cell 34 is row 1, column 2, counting from zero - a blank part of the boot screen.
The cell at row r, column c is cell number r * 32 + c, and pattern n
lives at n * 8. So pattern 34 is at 34 * 8 = 272 = 0110h.
A pattern's two colours do not come from register 7. They come from the
colour table, laid out exactly like the patterns - eight bytes per pattern,
one per row - starting at 2000.
Each byte is two nibbles. The high nibble colours the 1 bits of that row, the
low nibble colours the 0 bits. So 90h means the shape is colour 9, light
red, and everything around it is colour 0.
Colour 0 is transparent, not black. Where a pattern is transparent you see straight through to the backdrop, which is the blue the screen already is.
So pattern 34's colours go at 2000 + 272 = 2110h.
ORG 256
; --- the shape, into pattern 34
LD A,10h ; low byte of 0110h
OUT (9),A
LD A,41h ; 40h + 01h
OUT (9),A
LD HL,shape
LD B,8
send: LD A,(HL)
OUT (8),A
INC HL
DEC B
JR NZ,send
; --- its colours, into the colour table
LD A,10h ; low byte of 2110h
OUT (9),A
LD A,61h ; 40h + 21h
OUT (9),A
LD A,90h ; light red shape, transparent around it
LD B,8
col: OUT (8),A
DEC B
JR NZ,col
; --- and show the same pattern in a second cell
LD A,24h ; low byte of 3824h - cell 36
OUT (9),A
LD A,78h ; 40h + 38h
OUT (9),A
LD A,34 ; cell 36 now uses pattern 34
OUT (8),A
RET
shape: DEFB 00011000b
DEFB 00111100b
DEFB 01111110b
DEFB 11111111b
DEFB 11111111b
DEFB 01111110b
DEFB 00111100b
DEFB 00011000b
What you should see
Two light red diamonds, side by side with a gap between them, on the second row of the screen. The boot text is untouched.

Notice how the pattern was sent: the address once, then eight bytes in a loop,
using the same LD A,(HL) / INC HL walk from S11. The VDP advanced its own
address each time.
The second diamond is the important one.
You wrote the shape once. Then one byte into the name table - LD A,34 -
made a second cell show it. Not a copy: the same eight bytes, displayed twice.
Write 34 into thirty more name table entries and you get thirty more
diamonds, at no cost in memory and almost none in time.
That is a tile, and it is why tile-based games can redraw a whole screen inside a single frame. A screen is not a picture; it is a list of which shape goes where, and the shapes are defined once.
Compare it with what you have been doing. Printing a character means the ROM computing pixel addresses and writing eight bytes per character. Placing a tile means writing one byte.
LD A,34 to LD A,35. Where does the second diamond go, and what
happened to whatever cell 36 was showing before?24h to 25h. Which cell
changes now?90h to F0h in the colour write. What colour are the diamonds, and
what happened to the space around them?90h to 94h. Both nibbles now do something. Describe what you see
before you work out why.3800 + 300 - which is in row 9.
The diamond does not appear. That is not a mistake in your arithmetic, and
the next section is about why.16.1 - Your own shape. Design something on an 8 x 8 grid - an arrow, a letter, a spaceship - and put it on screen. Write it in binary so you can read it back.
16.2 - A row of them. Show your shape across a whole row of the screen by
writing its pattern number into consecutive name table entries. One address
setup and a loop of OUT (8),A will do it, since the VDP advances the address
for you.
16.3 - Two shapes. Define a second pattern in a different pattern number, give it a different colour, and put both on screen. Then alternate them along a row.
16.4 - A striped cell. Make a pattern where every row is 10101010b and
give it a colour byte with two different nibbles. Look closely at the result
and explain which nibble coloured which pixels.
16.5 - Find a letter in the pattern table. Pick a cell on screen that
contains part of a letter - row 5, column 0 is the T of TATUNG - work out
its pattern address, and read the eight bytes back. Print each as eight
characters, a # for a 1 bit and a . for a 0, one row per line. The letter
will be looking back at you. That is the bitmap, and there is no clearer way
to see it.
16.6 - Read it back the short way. The datasheet says the second address byte is 00h
rather than 40h for a read. Set up a read address, use IN A,(8), and fetch
one of your own pattern bytes back. Check it against what you wrote - and be
aware the VDP needs a moment after the address setup before the data is ready.
| What you see | What it means |
|---|---|
| Nothing changes at all | The second address byte. It needs 40h added for a write - without it the VDP is set up to read. |
| The display corrupts wildly | Bit 7 set in the second byte, which makes it a register write instead. Keep it under 80h. |
| The shape appears somewhere unexpected | Pattern n is at n x 8, not at n. And cell r,c is r * 32 + c - 32 cells per row, not 40, even though text is 40 columns. |
| The shape is invisible but the cell went blank | Both colour nibbles the same, or both transparent. Try 90h. |
| A shape in the lower two thirds of the screen never appears | The pattern banks. Next section. |
| Text stops looking right afterwards | You overwrote a pattern that MOS was using for a character's pixels. Harmless, and it will come back when the screen scrolls. |
OUT (n),A sends a byte to a port; IN A,(n) reads one. Only A can carry
it.40h plus the top six bits. The
address then advances by itself for every byte you send to port 8.n * 8.2000 + n * 8 - high nibble for the 1 bits,
low nibble for the 0 bits. Colour 0 is transparent. Register 7 does not do
this.3800 says which pattern each cell shows. Cell r,c is
entry r * 32 + c.S17. Taking over the screen. Your diamonds are sitting on a screen that still belongs to MOS, and a shape in the lower two thirds will not appear at all. Next you find out why, clear all three tables properly, and build a screen that is entirely yours - which means giving up the ROM's text output for good.