
Everything so far has been Pascal: the language keeps you away from the machine underneath, which is mostly a kindness. But Turbo also leaves two doors open, straight into the Einstein - its memory and its chips. Through them, this section reaches the video chip and changes the shape of a character, so that Warehouse's crates can look like crates instead of $.
The Z80 reaches 65,536 bytes of memory, numbered from 0 to 65535 - $FFFF in hexadecimal. Turbo gives you all of it as an array of bytes called Mem: Mem[5] is the byte at address 5, and
Mem[A] := 7;
puts 7 into the byte at address A. Be careful where: that byte might be part of your program, or of Turbo.
Addr(X) gives the address of the variable X - where Turbo keeps it. Put the two together and you can look inside a variable:
X := 1234;
A := Addr(X);
Writeln(Mem[A], ' ', Mem[A + 1]);
prints 210 4. An Integer takes two bytes, and the Einstein keeps the low byte first: 4 * 256 + 210 = 1234.
Addresses above 32767 are too big for an Integer, so they come out negative: the X above was at -5126. That is still the right address - Mem takes it happily - and in hexadecimal it can be written as it is: Mem[$FFFF] is the very last byte.
The Z80 talks to the Einstein's other chips - the video chip, the sound chip (which also reads the keyboard) and the rest - through ports, numbered from 0 to 255. Turbo gives them as another array, Port:
Port[9] := $40; { $40 to port 9 }
B := Port[8]; { a byte from port 8 }
The Einstein's picture is made by its video chip, which has 16K of memory of its own. The Z80 cannot reach that memory directly; it asks the chip through two ports:
$40 added if you are going to write there.The shapes of the characters - the font - live in the chip's memory, eight bytes to a character, starting at $1800. Character C's shape is at $1800 + C * 8. Each byte is one row of the character, top to bottom, and each bit is one dot, from the left:
........ 0 the letter A,
...#.... 16 read from the
..#.#... 40 video chip
.#...#.. 68
.#####.. 124
.#...#.. 68
.#...#.. 68
........ 0
Only the left-hand six dots of each row show: that is how forty characters fit across the screen. The two right-hand bits could be anything.
Write eight bytes of your own at a character's place, and from then on, every time that character is printed, your shape appears instead.
Which character? The Einstein has 256, and the ones from 128 to 255 are graphics shapes that ordinary text never uses - 128 starts out blank - so they are the ones to take. Redefine a letter instead and every one of those letters, everywhere, changes.
Two rules, both important:
program Crate;
type
Pattern = array[0..7] of Byte;
const
Box: Pattern =
($FC,$84,$CC,$B4,$B4,$CC,$84,$FC);
var
I: Integer;
procedure Define(Code: Integer;
Shape: Pattern);
var
A, I: Integer;
begin
A := $1800 + Code * 8;
Port[9] := A mod 256;
Port[9] := A div 256 + $40;
for I := 0 to 7 do
Port[8] := Shape[I];
end;
begin
Writeln('Before: ', Chr(128));
Define(128, Box);
Writeln('After: ', Chr(128));
for I := 1 to 10 do
Write(Chr(128), ' ');
Writeln;
end.
Box is the crate, row by row. In dots:
######.. $FC
#....#.. $84
##..##.. $CC
#.##.#.. $B4
#.##.#.. $B4
##..##.. $CC
#....#.. $84
######.. $FC
Define splits the address into its low byte (mod 256) and high byte (div 256), adds $40 to the high byte because it is going to write, and sends the eight rows.
Starting from
The Einstein freshly restarted, Turbo started, Y, a new work file called CRATE.
What you should see

Nothing after Before:, because character 128 starts out blank; a crate after After:; and a row of ten crates. Run it a second time, and the Before: line has a crate too: the shape stayed.
$FC to $FF. Does the crate look any different? Why not?Define(128, Box) to Define(65, Box), and every Chr(128) to 'A'. Run it, look at the After: line - and then at the menu. (Restart the Einstein to get your A back.)Writeln('Defining ', Code); inside Define, just after the two Port[9] lines. What happens to the crate?26.1 Ask for a key, then read that character's eight bytes from the video chip and draw them as # and ., with each byte's value beside it. (Reading needs the address without $40 - and read all eight before you print anything.)
26.2 Design a brick for the walls and a warehouse worker, and draw a small walled room with the worker and a crate in it.
26.3 Put 1000 in an Integer and print its two bytes with Mem, then put them back together.
Worked solutions are in Appendix II.
| Symptom | Cause |
|---|---|
| The character does not change | Something was printed between setting the address and sending the bytes. |
| The shape is right but only some of the ones on screen changed | Only characters printed after Define use the new shape. Define first, then draw. |
| Letters everywhere have turned into your shape | You redefined a letter, not a character from 128 up. Restart the Einstein. |
| The right-hand side of the shape is missing | Only the left six dots of each row show. |
The Einstein does something strange after a Mem write | The byte belonged to something else. Restart it, and write only to your own variables. |
Mem[A] is the byte at address A, to read or write; Addr(X) is where X is; an Integer is two bytes, low byte first. Port[N] sends to or reads from port N. The video chip takes an address on port 9 (low byte, then high byte plus $40 to write) and data on port 8. A character's shape is eight bytes at $1800 + code * 8, six dots to a row; write your own over characters 128 and up, print nothing in between, and everything printed afterwards uses it until the Einstein is restarted.
S27, The Warehouse On Screen - Part three begins: the game itself, one piece at a time.