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

out

Prints the byte as a character (Latin-1) and draws it at the Screen’s text cursor. 0x0A prints a newline.

in

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
prints Hello!

0x01 Unsigned number

out

Prints the byte as an unsigned decimal number, 0 to 255.

in

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
input 21
prints 42

0x02 Signed number

out

Prints the byte as a signed decimal number, -128 to 127.

in

Same as the unsigned number port.

        .org 0x8000
        ld a, 5
        sub 10
        out (2), a
        halt
prints -5

0x03 Hexadecimal

out

Prints the byte as two upper case hexadecimal digits.

in

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
prints FF

0x04 16 bit number

out

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.

in

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
prints 1000

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.

0

One pixel at (X, Y) in the pen color. The pen width does not apply.

1

A line from (X, Y) to (X2, Y2) in the pen color, leaving the drawing position at (X2, Y2).

2

A line from the drawing position to (X, Y), which becomes the new position: a polyline costs one command per point.

3

Moves the drawing position to (X, Y) without drawing.

4

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.

5

The same rectangle, outline only.

6

The ellipse inscribed in that rectangle, filled and outlined like it.

7

The same ellipse, outline only.

8

Spreads the fill color from (X, Y) over every pixel of the color that was there, four ways.

9

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.

10

Resizes the Screen to X by Y pixels and clears it; a coordinate of 0 means 256, the largest size a byte cannot hold.

11

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.

12

Double buffering off: drawing appears immediately. The off-screen image is dropped without being shown.

13

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 black
0x03 blue
0x1C green
0x1F cyan
0xE0 red
0xE3 magenta
0xFC yellow
0xFF white
0xF0 orange
0x92 gray
0x49 dark gray

0x10 Pen color

out

Sets the color of pixels, lines, outlines and text, as one 3-3-2 byte (RRRGGGBB).

in

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
shows one red pixel at (100, 50).

0x11 Fill color

out

Sets the color the inside of a filled shape, a flood fill and a clear use, as one 3-3-2 byte.

in

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
shows a green square with a white outline, 60 by 60 pixels, at (20, 20).

0x12 Pen width

out

Sets how many pixels wide lines and outlines are, at least 1. A width of 0 is read as 1.

in

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
shows a thick white diagonal line across the Screen.

0x13 X

out

The first X coordinate: the pixel, the start of a line, the left edge of a shape, the width of a resize.

in

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
shows a line from the middle of the Screen to (200, 20).

0x14 Y

out

The first Y coordinate, the companion of the X port, and the height of a resize.

in

The byte last written.

0x15 X2

out

The second X coordinate: the end of a line, the right edge of a shape. Unused by the one-point commands.

in

The byte last written.

0x16 Y2

out

The second Y coordinate, the companion of the X2 port.

in

The byte last written.

0x17 Command

out

Runs one drawing operation on the coordinates, colors and pen width already set. The commands are listed above.

in

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
shows a yellow ellipse with a white outline on a blue Screen.

0x18 Pixel color

out

Ignored: the pixel is drawn with command 0, in the pen color.

in

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
prints E0

0x19 Text cursor column

out

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.

in

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"
prints HELLO
shows the same text on the Screen, at the eighth row.

0x1A Text cursor row

out

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.

in

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

out

Ignored.

in

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
input hi
shows the first character typed on the Screen, echoed back. Click the Screen first: that is where the keys go.

0x21 Key state

out

Ignored.

in

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
shows an R printed once the right arrow is held down on the focused Screen.

0x22 Last key pressed

out

Ignored.

in

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
shows the code of the first key pressed on the focused Screen, in hexadecimal.

0x23 Last key released

out

Ignored.

in

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.

0 the pointer and the buttons right now
1 the state at the last button release
2 the state at the last button press, with the double-click flag

0x30 Mouse X

out

Ignored.

in

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
shows the pointer position printed as "x,y" as soon as the left button is held over the Screen.

0x31 Mouse Y

out

Ignored.

in

The Y of the view selected by B, in logical Screen pixels from the top edge.

0x32 Mouse buttons

out

Ignored.

in

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

out

Ignored.

in

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

out

Ignored.

in

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
prints !

0x41 Frame sync

out

Ignored.

in

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
prints ..........

0x42 Elapsed time

out

Ignored.

in

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
shows the hundredths of a second the program has been running, about 10.