Z80 Input/Output
A Z80 has no system calls. Programs reach the outside world with in and out, which address one of 256 ports: out (n), a sends A to port n, in a, (n) reads a byte back. The (c) forms (out (c), r and in r, (c)) take the port number from C,
which lets a program compute it, and put B on the high byte of the address bus, which is how
a read carries a parameter: the key code, the mouse view, the byte of the clock, the length
of a wait.
The emulator connects the ports below to the terminal, the screen, the keyboard, the mouse
and the clock. Every other port behaves like an empty bus: writes are dropped and reads
answer 0xFF. Reading a connected port with nothing to read pauses the program
until there is something, so in never fails, it only waits.
Console 0x00 - 0x04
The Terminal: one port per output format, and the input reads that pause the program until a line (or a keystroke on the Screen) is available. What is printed here is also drawn at the Screen’s text cursor, in 8 by 8 cells.
0x00 Character
Prints the byte as a character (Latin-1) and draws it at the Screen’s text cursor. 0x0A prints a newline.
Returns the next character of the input line, pausing for input when the line has been consumed. The line ends with a newline character (0x0A). Once the program has touched a Screen, Keyboard or Mouse port the characters come from the focused Screen instead, one keystroke at a time, and are echoed at the text cursor.
.org 0x8000
ld hl, msg
loop: ld a, (hl)
or a
jr z, done
out (0), a
inc hl
jr loop
done: halt
msg: .asciz "Hello!", 10 ; 10 is the newline: strings keep \n literally
0x01 Unsigned number
Prints the byte as an unsigned decimal number, 0 to 255.
Reads a line, parses it as a decimal number and returns its low byte. Stops the program with an error when the line is not a number.
.org 0x8000
in a, (1) ; ask for a number
add a, a ; double it
out (1), a ; print it
halt
0x02 Signed number
Prints the byte as a signed decimal number, -128 to 127.
Same as the unsigned number port.
.org 0x8000
ld a, 5
sub 10
out (2), a
halt
0x03 Hexadecimal
Prints the byte as two upper case hexadecimal digits.
Reads a line, parses it as a hexadecimal number (0x, $ prefix or h suffix accepted) and returns its low byte.
.org 0x8000
ld a, 255
out (3), a
halt
0x04 16 bit number
Prints the 16 bit number made of the high byte of the port address (register B when using out (c),r) and the byte written, as an unsigned decimal number.
Same as the unsigned number port.
.org 0x8000
ld hl, 1000
ld b, h ; high byte goes on the address bus
ld c, 4 ; port number
out (c), l ; prints HL
halt
Screen 0x10 - 0x1A
Drawing. Set the colors and the coordinates, then write one command to the command port; every coordinate is a byte, so the Screen is at most 256 by 256 pixels and is 256 by 192 until the program resizes it. Colors are one 3-3-2 byte.
Commands
Written to the command port, 0x17. One write runs one operation
on the coordinates and colors already set.
One pixel at (X, Y) in the pen color. The pen width does not apply.
A line from (X, Y) to (X2, Y2) in the pen color, leaving the drawing position at (X2, Y2).
A line from the drawing position to (X, Y), which becomes the new position: a polyline costs one command per point.
Moves the drawing position to (X, Y) without drawing.
A rectangle from (X, Y) to (X2, Y2), filled with the fill color and outlined with the pen. The right and bottom edges are excluded, as they are in EASy68K.
The same rectangle, outline only.
The ellipse inscribed in that rectangle, filled and outlined like it.
The same ellipse, outline only.
Spreads the fill color from (X, Y) over every pixel of the color that was there, four ways.
Fills the whole Screen with the fill color, homes the text cursor, and adopts that color as the background, so scrolled text rows and a later resize match what is on screen.
Resizes the Screen to X by Y pixels and clears it; a coordinate of 0 means 256, the largest size a byte cannot hold.
Double buffering on: drawing goes to an off-screen copy of the visible image until PRESENT, so an animation never shows a half-drawn frame.
Double buffering off: drawing appears immediately. The off-screen image is dropped without being shown.
Shows the off-screen image. With double buffering off it only asks for a repaint.
Colors
A color is one byte: three bits of red in bits 7-5, three of green in bits
4-2 and two of blue in bits 1-0. Each field is stretched over the screen's
eight bits by repeating it, so 0xFF is white and 0xE0 pure red.
0x00 black0x03 blue0x1C green0x1F cyan0xE0 red0xE3 magenta0xFC yellow0xFF white0xF0 orange0x92 gray0x49 dark gray0x10 Pen color
Sets the color of pixels, lines, outlines and text, as one 3-3-2 byte (RRRGGGBB).
The current pen color, back in 3-3-2.
.org 0x8000
ld a, 0xE0 ; 111 000 00: pure red
out (0x10), a ; pen color
ld a, 100
out (0x13), a ; X
ld a, 50
out (0x14), a ; Y
xor a ; command 0: pixel
out (0x17), a
halt
0x11 Fill color
Sets the color the inside of a filled shape, a flood fill and a clear use, as one 3-3-2 byte.
The current fill color, back in 3-3-2.
.org 0x8000
ld a, 0x1C ; 000 111 00: pure green
out (0x11), a ; fill color
ld a, 0xFF
out (0x10), a ; white pen for the outline
ld a, 20
out (0x13), a
out (0x14), a ; from (20, 20)
ld a, 80
out (0x15), a
out (0x16), a ; to (80, 80), right and bottom excluded
ld a, 4 ; command 4: filled rectangle
out (0x17), a
halt
0x12 Pen width
Sets how many pixels wide lines and outlines are, at least 1. A width of 0 is read as 1.
The current pen width.
.org 0x8000
ld a, 5
out (0x12), a ; five pixels wide
ld a, 10
out (0x13), a
out (0x14), a ; from (10, 10)
ld a, 200
out (0x15), a
ld a, 150
out (0x16), a ; to (200, 150)
ld a, 1 ; command 1: line
out (0x17), a
halt
0x13 X
The first X coordinate: the pixel, the start of a line, the left edge of a shape, the width of a resize.
The byte last written.
.org 0x8000
ld a, 128
out (0x13), a
ld a, 96
out (0x14), a ; (128, 96), the middle of the default Screen
ld a, 3 ; command 3: move to
out (0x17), a
ld a, 200
out (0x13), a
ld a, 20
out (0x14), a
ld a, 2 ; command 2: line to
out (0x17), a
halt
0x14 Y
The first Y coordinate, the companion of the X port, and the height of a resize.
The byte last written.
0x15 X2
The second X coordinate: the end of a line, the right edge of a shape. Unused by the one-point commands.
The byte last written.
0x16 Y2
The second Y coordinate, the companion of the X2 port.
The byte last written.
0x17 Command
Runs one drawing operation on the coordinates, colors and pen width already set. The commands are listed above.
The number of the last command run, 0 before the first one.
.org 0x8000
ld a, 0x03 ; 000 000 11: pure blue
out (0x11), a ; fill color
ld a, 9 ; command 9: clear to the fill color
out (0x17), a
ld a, 0xFC ; 111 111 00: yellow
out (0x11), a
ld a, 60
out (0x13), a
ld a, 40
out (0x14), a
ld a, 196
out (0x15), a
ld a, 152
out (0x16), a
ld a, 6 ; command 6: filled ellipse
out (0x17), a
halt
0x18 Pixel color
Ignored: the pixel is drawn with command 0, in the pen color.
The color of the pixel at (X, Y) as a 3-3-2 byte, read from the image being drawn on — the off-screen one while double buffering. Outside the Screen it reads as the background color.
.org 0x8000
ld a, 0xE0
out (0x10), a ; red pen
ld a, 8
out (0x13), a
out (0x14), a ; (8, 8)
xor a ; command 0: pixel
out (0x17), a
in a, (0x18) ; read the color back
out (3), a ; print it as hexadecimal
halt
0x19 Text cursor column
Moves the text cursor to a column, in 8 by 8 character cells: 0 to 31 on the default Screen. Out of range values are clamped.
The column the text cursor is on.
.org 0x8000
ld a, 12
out (0x19), a ; column 12
ld a, 8
out (0x1A), a ; row 8
ld hl, msg
loop: ld a, (hl)
or a
jr z, done
out (0), a ; the character port draws at the cursor
inc hl
jr loop
done: halt
msg: .asciz "HELLO"
0x1A Text cursor row
Moves the text cursor to a row, in 8 by 8 character cells: 0 to 23 on the default Screen. Out of range values are clamped.
The row the text cursor is on. Text scrolls the whole Screen up one cell row at the bottom, graphics included.
Keyboard 0x20 - 0x23
Input from the focused Screen, polled: whether a typed character is waiting (read it from the character port), whether a given key is held, and the last key pressed and released. Key codes are EASy68K’s, the same table every environment here uses: letters and digits are the ASCII code of their capital, 0x25 to 0x28 are the arrows left, up, right and down, 0x20 is the space bar and 0x0D is Enter.
0x20 Typed input available
Ignored.
1 when a character typed on the focused Screen is waiting to be read from the character port, else 0. During a testcase it answers for the scripted input instead, so a program that polls before reading works in both.
.org 0x8000
wait: in a, (0x20) ; anything typed?
or a
jr z, wait ; poll until there is
in a, (0x00) ; take the character
out (0x00), a ; print it
halt
0x21 Key state
Ignored.
1 while the key whose code is in B is held down, else 0. This is how a game reads WASD or the arrows: it never consumes anything, and a key held over several reads answers 1 every time.
.org 0x8000
ld c, 0x21 ; the key state port
ld b, 0x27 ; the right arrow
wait: in a, (c)
or a
jr z, wait ; poll until it is held
ld a, 'R'
out (0), a
halt
0x22 Last key pressed
Ignored.
The code of the last key pressed on the Screen, 0 before the first press. It persists, so a program that polls slowly still sees the key.
.org 0x8000
loop: in a, (0x22) ; the last key pressed
or a
jr z, loop
out (3), a ; print its code in hexadecimal
halt
0x23 Last key released
Ignored.
The code of the last key released, 0 before the first release. With the last-pressed port it is the pair EASy68K’s task 19 answers with.
Mouse 0x30 - 0x33
Pointing input over the Screen, polled, in the same logical pixels drawing uses. B selects which of the three views a read answers with: the current state, the state at the last button release, or the state at the last button press.
Views
Put one of these in B before reading a mouse port.
0x30 Mouse X
Ignored.
The X of the view selected by B (0 current, 1 last release, 2 last press), in logical Screen pixels from the left edge, independently of how the panel is zoomed.
.org 0x8000
ld c, 0x32 ; the buttons port
ld b, 0 ; view 0: the current state
wait: in a, (c)
and 1 ; the left button
jr z, wait
ld c, 0x30
in a, (c) ; X
out (1), a
ld a, ','
out (0), a
ld c, 0x31
in a, (c) ; Y
out (1), a
halt
0x31 Mouse Y
Ignored.
The Y of the view selected by B, in logical Screen pixels from the top edge.
0x32 Mouse buttons
Ignored.
The buttons and modifiers of the view selected by B: bit 0 left, bit 1 right, bit 2 middle, bit 3 the double-click flag (only in the last-press view), bit 4 Shift, bit 5 Alt, bit 6 Ctrl.
0x33 Mouse event count
Ignored.
For the current view, how many mouse events have happened, as a byte that wraps around; for the two snapshot views, the count at the moment of the snapshot, 0 when it has not happened yet. Comparing it with the previous read is how a program tells a new click from the one it already handled.
Program time 0x40 - 0x42
Waiting and elapsed time. A wait and a frame sync are reads that suspend the program without blocking the editor: the machine re-executes the in when the time has passed, so Stop still answers and the Screen still repaints. Testcases run on a virtual clock, where waits complete at once and time starts at zero.
0x40 Wait
Ignored.
Waits B hundredths of a second, then answers 0. The editor stays responsive and Stop still works: the machine simply re-executes the in when the time is up. In a testcase the wait completes at once.
.org 0x8000
ld c, 0x40
ld b, 25 ; a quarter of a second
in a, (c)
ld a, '!'
out (0), a
halt
0x41 Frame sync
Ignored.
Waits for the next animation frame, then answers 0. One read per frame is how an animation runs at the display’s own pace instead of as fast as the host can go; with double buffering, present the frame first and sync afterwards.
.org 0x8000
ld b, 10 ; ten frames
loop: push bc
in a, (0x41) ; wait for the next frame
ld a, '.'
out (0), a
pop bc
djnz loop
halt
0x42 Elapsed time
Ignored.
One byte of the number of hundredths of a second since the run started, selected by B: 0 the lowest byte, 3 the highest. A testcase reads a virtual clock that starts at zero and only moves when the program waits, so a test is reproducible.
.org 0x8000
ld c, 0x40
ld b, 10 ; wait a tenth of a second
in a, (c)
ld c, 0x42
ld b, 0 ; the lowest byte of the elapsed hundredths
in a, (c)
out (1), a
halt