Z80 Input/Output

Ports

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, Screen, keyboard, mouse, and program clock. Every other port behaves like an empty bus: writes are dropped and reads answer 0xFF. Reads from console ports 0x10–0x14 wait for input; 0x50 waits for its requested delay and 0x51 waits for the next frame. Keyboard-state, mouse, clock-now, and other status reads return immediately, so they can be polled in a loop. Invalid console number input stops the program with an error.

The character port 0x10 reads a Terminal line one byte at a time, ending it with newline 0x0A when you press Enter. Once the program uses a Screen, Keyboard or Mouse port, character input comes from the focused Screen one keystroke at a time; Enter still gives the character port 0x0A. The last-key ports report the EASy68K Enter key code 0x0D. Click the Screen to focus it before typing.

For example, ld c,0x30 / ld b,0 / in a,(c) polls whether a key is waiting; the returned A is 1 or 0. To wait for a character, in a,(0x10) pauses until input is available, then returns the character in A. With the (c) form, C selects the low-byte port and B supplies the high address byte; for parameterized reads, set B to the documented parameter before in.

Console

0x10 - 0x14

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.

Character

port 0x10
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.
Out
Prints the byte as a character (Latin-1) and draws it at the Screen’s text cursor. 0x0A prints a newline.
Example
        .org 0x8000
        ld hl, msg
loop:   ld a, (hl)
        or a
        jr z, done
        out (0x10), a
        inc hl
        jr loop
done:   halt
msg:    .asciz "Hello!", 10   ; 10 is the newline: strings keep \n literally
prints
Hello!

Unsigned number

port 0x11
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.
Out
Prints the byte as an unsigned decimal number, 0 to 255.
Example
        .org 0x8000
        in a, (0x11)       ; ask for a number
        add a, a        ; double it
        out (0x11), a      ; print it
        halt
input
21
prints
42

Signed number

port 0x12
In
Same as the unsigned number port.
Out
Prints the byte as a signed decimal number, -128 to 127.
Example
        .org 0x8000
        ld a, 5
        sub 10
        out (0x12), a
        halt
prints
-5

Hexadecimal

port 0x13
In
Reads a line, parses it as a hexadecimal number (0x, $ prefix or h suffix accepted) and returns its low byte.
Out
Prints the byte as two upper case hexadecimal digits.
Example
        .org 0x8000
        ld a, 255
        out (0x13), a
        halt
prints
FF

16 bit number

port 0x14
In
Same as the unsigned number port.
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.
Example
        .org 0x8000
        ld hl, 1000
        ld b, h         ; high byte goes on the address bus
        ld c, 0x14      ; port number
        out (c), l      ; prints HL
        halt
prints
1000

Screen

0x20 - 0x2A

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.

Screen commands

Written to the command port, 0x27. One write runs one operation on the coordinates and colors already set.

Pixel

command 0

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

Line

command 1

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

Line to

command 2

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

Move to

command 3

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

Rectangle

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

Rectangle outline

command 5

The same rectangle, outline only.

Ellipse

command 6

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

Ellipse outline

command 7

The same ellipse, outline only.

Flood fill

command 8

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

Clear

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

Resize

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

Buffering on

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

Buffering off

command 12

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

Present

command 13

Shows the off-screen image. With double buffering off it only asks for a repaint.

Mode cells

command 14

The TRS-80's memory-mapped display: the Screen becomes 64 by 16 character cells and shows the kilobyte of memory at 0x3C00, one byte per cell. Characters 128 to 191 are 2 by 3 blocks, for 128 by 48 chunky pixels, and the keyboard matrix at 0x3800 answers reads. The drawing commands above are not available in this mode; a program draws by storing bytes. The same mode a ; @screen trs80 comment asks for before the program starts.

Mode drawing

command 15

Back to the drawing commands above. The image stays as it is until something draws on it, and memory at 0x3C00 becomes ordinary RAM again.

The TRS-80 display

Command 14 switches the Screen to the TRS-80 memory-mapped text display. A program can select this mode before it starts with a ; @screen trs80 comment. Drawing commands are unavailable in this mode, and console output goes only to the Terminal: display text by storing bytes in the mapped memory below.

AddressesContents
0x3C00 - 0x3FFFVideo RAM. One byte per character cell, 64 columns by 16 rows, read left to right and top to bottom: the cell at column X of row Y is 0x3C00 + Y*64 + X. Store a byte, see a character. It is ordinary RAM as well, so a program can read back what it drew, and a whole screen can be block copied into it with one ldir.
0x3800 - 0x3BFFKeyboard matrix. Eight rows of eight keys. The low byte of the address selects rows — bit 0 is row 0 — and a read answers every selected row OR’ed together, so ld a,(0x38FF) scans the whole keyboard at once. A bit is 1 while its key is held. The four banks of 256 mirror each other.

Port 0x00 is the joystick input; with no joystick attached, reading it returns 0xFF.

Characters 0x20 to 0x7F are text. Characters 128 to 191 are 2 by 3 blocks of chunky pixels — the low six bits are the blocks, bit 0 top-left then across and down — so the screen is also a 128 by 48 pixel grid. 191 is solid and 128 is blank.

Keyboard rowKeys
0@ A B C D E F G
1H I J K L M N O
2P Q R S T U V W
3X Y Z
40 1 2 3 4 5 6 7
58 9 : ; , - . /
6Enter, Clear, Break, up, down, left, right, space
7Shift

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.

  • black 0x00
  • blue 0x03
  • green 0x1C
  • cyan 0x1F
  • red 0xE0
  • magenta 0xE3
  • yellow 0xFC
  • white 0xFF
  • orange 0xF0
  • gray 0x92
  • dark gray 0x49

Pen color

port 0x20
In
The current pen color, back in 3-3-2.
Out
Sets the color of pixels, lines, outlines and text, as one 3-3-2 byte (RRRGGGBB).
Example
        .org 0x8000
        ld a, 0xE0      ; 111 000 00: pure red
        out (0x20), a   ; pen color
        ld a, 100
        out (0x23), a   ; X
        ld a, 50
        out (0x24), a   ; Y
        xor a           ; command 0: pixel
        out (0x27), a
        halt
shows
one red pixel at (100, 50).

Fill color

port 0x21
In
The current fill color, back in 3-3-2.
Out
Sets the color the inside of a filled shape, a flood fill and a clear use, as one 3-3-2 byte.
Example
        .org 0x8000
        ld a, 0x1C      ; 000 111 00: pure green
        out (0x21), a   ; fill color
        ld a, 0xFF
        out (0x20), a   ; white pen for the outline
        ld a, 20
        out (0x23), a
        out (0x24), a   ; from (20, 20)
        ld a, 80
        out (0x25), a
        out (0x26), a   ; to (80, 80), right and bottom excluded
        ld a, 4         ; command 4: filled rectangle
        out (0x27), a
        halt
shows
a green square with a white outline, 60 by 60 pixels, at (20, 20).

Pen width

port 0x22
In
The current pen width.
Out
Sets how many pixels wide lines and outlines are, at least 1. A width of 0 is read as 1.
Example
        .org 0x8000
        ld a, 5
        out (0x22), a   ; five pixels wide
        ld a, 10
        out (0x23), a
        out (0x24), a   ; from (10, 10)
        ld a, 200
        out (0x25), a
        ld a, 150
        out (0x26), a   ; to (200, 150)
        ld a, 1         ; command 1: line
        out (0x27), a
        halt
shows
a thick white diagonal line across the Screen.

X

port 0x23
In
The byte last written.
Out
The first X coordinate: the pixel, the start of a line, the left edge of a shape, the width of a resize.
Example
        .org 0x8000
        ld a, 128
        out (0x23), a
        ld a, 96
        out (0x24), a   ; (128, 96), the middle of the default Screen
        ld a, 3         ; command 3: move to
        out (0x27), a
        ld a, 200
        out (0x23), a
        ld a, 20
        out (0x24), a
        ld a, 2         ; command 2: line to
        out (0x27), a
        halt
shows
a line from the middle of the Screen to (200, 20).

Y

port 0x24
In
The byte last written.
Out
The first Y coordinate, the companion of the X port, and the height of a resize.

X2

port 0x25
In
The byte last written.
Out
The second X coordinate: the end of a line, the right edge of a shape. Unused by the one-point commands.

Y2

port 0x26
In
The byte last written.
Out
The second Y coordinate, the companion of the X2 port.

Command

port 0x27
In
The number of the last command run, 0 before the first one.
Out
Runs one drawing operation on the coordinates, colors and pen width already set. The commands are listed above.
Example
        .org 0x8000
        ld a, 0x03      ; 000 000 11: pure blue
        out (0x21), a   ; fill color
        ld a, 9         ; command 9: clear to the fill color
        out (0x27), a
        ld a, 0xFC      ; 111 111 00: yellow
        out (0x21), a
        ld a, 60
        out (0x23), a
        ld a, 40
        out (0x24), a
        ld a, 196
        out (0x25), a
        ld a, 152
        out (0x26), a
        ld a, 6         ; command 6: filled ellipse
        out (0x27), a
        halt
shows
a yellow ellipse with a white outline on a blue Screen.

Pixel color

port 0x28
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.
Out
Ignored: the pixel is drawn with command 0, in the pen color.
Example
        .org 0x8000
        ld a, 0xE0
        out (0x20), a   ; red pen
        ld a, 8
        out (0x23), a
        out (0x24), a   ; (8, 8)
        xor a           ; command 0: pixel
        out (0x27), a
        in a, (0x28)    ; read the color back
        out (0x13), a      ; print it as hexadecimal
        halt
prints
E0

Text cursor column

port 0x29
In
The column the text cursor is on.
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.
Example
        .org 0x8000
        ld a, 12
        out (0x29), a   ; column 12
        ld a, 8
        out (0x2A), a   ; row 8
        ld hl, msg
loop:   ld a, (hl)
        or a
        jr z, done
        out (0x10), 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.

Text cursor row

port 0x2A
In
The row the text cursor is on. Text scrolls the whole Screen up one cell row at the bottom, graphics included.
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.

Keyboard

0x30 - 0x33

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.

Typed input available

port 0x30
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.
Out
Ignored.
Example
        .org 0x8000
wait:   in a, (0x30)    ; anything typed?
        or a
        jr z, wait      ; poll until there is
        in a, (0x10)    ; take the character
        out (0x10), 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.

Key state

port 0x31
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.
Out
Ignored.
Example
        .org 0x8000
        ld c, 0x31      ; 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 (0x10), a
        halt
shows
an R printed once the right arrow is held down on the focused Screen.

Last key pressed

port 0x32
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.
Out
Ignored.
Example
        .org 0x8000
loop:   in a, (0x32)    ; the last key pressed
        or a
        jr z, loop
        out (0x13), a      ; print its code in hexadecimal
        halt
shows
the code of the first key pressed on the focused Screen, in hexadecimal.

Last key released

port 0x33
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.
Out
Ignored.

Mouse

0x40 - 0x43

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.

Mouse views

Put one of these in B before reading a mouse port.

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

The editor-defined TIME_NOW port retains elapsed hundredths since the run started, as specified by its port map. It is not a calendar clock.

Mouse X

port 0x40
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.
Out
Ignored.
Example
        .org 0x8000
        ld c, 0x42      ; 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, 0x40
        in a, (c)       ; X
        out (0x11), a
        ld a, ','
        out (0x10), a
        ld c, 0x41
        in a, (c)       ; Y
        out (0x11), a
        halt
shows
the pointer position printed as "x,y" as soon as the left button is held over the Screen.

Mouse Y

port 0x41
In
The Y of the view selected by B, in logical Screen pixels from the top edge.
Out
Ignored.

Mouse buttons

port 0x42
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.
Out
Ignored.

Mouse event count

port 0x43
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.
Out
Ignored.

Program time

0x50 - 0x52

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.

Wait

port 0x50
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.
Out
Ignored.
Example
        .org 0x8000
        ld c, 0x50
        ld b, 25        ; a quarter of a second
        in a, (c)
        ld a, '!'
        out (0x10), a
        halt
prints
!

Frame sync

port 0x51
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.
Out
Ignored.
Example
        .org 0x8000
        ld b, 10        ; ten frames
loop:   push bc
        in a, (0x51)    ; wait for the next frame
        ld a, '.'
        out (0x10), a
        pop bc
        djnz loop
        halt
prints
..........

Elapsed time

port 0x52
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.
Out
Ignored.
Example
        .org 0x8000
        ld c, 0x50
        ld b, 10        ; wait a tenth of a second
        in a, (c)
        ld c, 0x52
        ld b, 0         ; the lowest byte of the elapsed hundredths
        in a, (c)
        out (0x11), a
        halt
shows
the hundredths of a second the program has been running, about 10.