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. For a parameterized read, set B to the documented key code, mouse view, clock-byte index, wait duration or pitch before in.

The emulator connects the ports below to the Terminal, Screen, keyboard, mouse, program clock, and Audio. 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, 0x51 waits for the next frame, and 0x61 plays a note and waits for its requested duration. 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.

Console

0x10 - 0x14

One port per output format. Console output goes to the Terminal and, in drawing mode, to the Screen’s text cursor in 8 by 8 cells. In TRS-80 mode it goes only to the Terminal.

Character

port 0x10
In
Reads a Terminal line one character at a time, pausing when no input is buffered. Pressing Enter ends the line with newline 0x0A. Using a Screen, Keyboard or Mouse port in drawing mode, other than a display-mode command, switches character input to the Screen for the rest of the run. Click the Screen to focus it before typing. Each read then takes one keystroke; Enter still returns 0x0A. Typed characters are echoed at the text cursor while in drawing mode.
Out
Prints the byte as a Latin-1 character to the Terminal and, in drawing mode, at the Screen’s text cursor. 0x0A prints a newline. At the bottom, text scrolls the whole Screen up one cell row, graphics included. In TRS-80 mode output goes only to the Terminal.
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

Switches to the TRS-80 display. Draw by storing bytes in video memory; see that section for the memory map, character encoding and keyboard matrix.

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

Pictures in Files

A program can draw a picture kept in a File of the project, such as a sprite sheet. Open a BMP File and choose Pixels under the editor to draw in it, or make one with New picture in the Explorer. In a Z80 project a new picture is 8-bit and stored top-down, and its 256-entry palette is the 3-3-2 colors above, so each pixel byte is already the color byte the pen takes. #insert "sprite.bmp" copies the whole File into the program when it is assembled: its pixels start after the label in front of it by the offset held in the File's bytes 10 to 13, which is 1078 in an 8-bit picture New picture made (a 54-byte header and the 1,024-byte palette), and each row takes the width rounded up to a multiple of 4 bytes. What you draw is in the program at the next Build.

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.
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. The Enter key code is distinct from the newline character 0x0A returned by CHAR.

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

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

Delay the program, synchronize animation frames, or read elapsed time. Waits leave the editor responsive: Stop still works and the Screen can repaint.

Wait

port 0x50
In
Waits B hundredths of a second, then answers 0. In a testcase the wait completes at once and advances the virtual clock by the requested duration.
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. In a testcase the wait completes at once and advances the virtual clock.
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. This is elapsed time, not a calendar clock. 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.

Audio

0x60 - 0x64

Play notes with a pitch, instrument sound, and volume, each numbered from 0 to 127, and a duration in milliseconds. General MIDI is the standard list of instrument sounds. Write a pitch to NOTE_PLAY (0x60) to start a note and continue immediately, allowing notes to overlap as a chord. Read NOTE_PLAY_WAIT (0x61) with the pitch in B to play a note and wait for its requested duration; a sequence of these reads can play a melody. Testcases are silent, and waits complete immediately while advancing the virtual clock.

Sound

The audio ports play notes with a pitch, instrument, volume, and duration. General MIDI is a standard list of instrument sounds; instrument numbers here start at 0, with 0 for acoustic grand piano, 40 for violin, 56 for trumpet, and 73 for flute. No MIDI file or external instrument is needed. Each increase of 1 in pitch moves up one semitone (one piano-key step, counting white and black keys): 60 is middle C, 62 is D, 64 is E, and 67 is G.

For tones, noise and volume envelopes controlled through registers, the Sound chip at ports 0xA0 to 0xA2 is an emulated AY-3-8910 with three square-wave channels, noise and an envelope. A program can use the audio ports, the Sound chip, or both.

At most 64 notes sound at once; starting another stops the oldest. The editor supplies a General MIDI sound bank, a collection of instrument sounds. The browser loads it automatically on the first note or MIDI-file request after opening or reloading the editor page; that request waits for loading. The bank is shared across later runs and projects.

After a program requests sound, the Sound button appears in the execution dock as a mute/unmute control. It sets Play sound in Preferences, where Sound volume is also available. Both settings apply across projects. Muting leaves program-visible timing and results unchanged.

Play a note

port 0x60
In
Returns 0xFF; this port only accepts writes.
Out
Writing a pitch starts a note and continues immediately, so notes can overlap. Pitch runs from 0 to 127: 60 is middle C, 62 is D, 64 is E, and each step is one semitone. The note uses the instrument, volume, and duration last set through the other audio ports. A pitch of 128 or more becomes 60, as in MARS service 31. The first note or MIDI-file request after opening or reloading the editor page waits while the browser loads the sound bank, the collection of instrument sounds; later runs share it.
Example
        .org 0x8000
        ld a, 60        ; C
        out (0x60), a
        ld a, 64        ; E, sounding with it
        out (0x60), a
        ld a, 67        ; G: a C major chord
        out (0x60), a
        halt
shows
No text output; plays a C major chord on the piano for one second, continuing after the program ends.

Play a note and wait

port 0x61
In
Read with in r,(c) and the pitch in B to play a note using the instrument, volume, and duration already set. Waits for the requested duration, then returns 0, like MARS service 33. A sequence of these reads can play a melody. Muting or stopping the audible note does not shorten the wait. Testcases are silent; the wait completes immediately and advances the virtual clock by the duration.
Out
Ignored.
Example
        .org 0x8000
        ld b, 0
        ld a, 150       ; 150 ms per note
        ld c, 0x64
        out (c), a
        ld c, 0x61
        ld b, 60        ; C
        in a, (c)
        ld b, 64        ; then E
        in a, (c)
        ld b, 67        ; then G
        in a, (c)
        ld a, '!'
        out (0x10), a
        halt
prints
!

Instrument

port 0x62
In
Returns 0xFF; this port only accepts writes.
Out
Selects the instrument sound for later notes from the General MIDI list, numbered from 0 to 127: 0 is acoustic grand piano, 40 violin, 56 trumpet, and 73 flute. Values of 128 or more select 0 (piano), as in MARS. Every run starts at 0 (piano).
Example
        .org 0x8000
        ld a, 40        ; violin
        out (0x62), a
        ld a, 69        ; A above middle C
        out (0x60), a
        halt
shows
No text output; plays an A on the violin for one second.

Volume

port 0x63
In
Returns 0xFF; this port only accepts writes.
Out
Sets the volume for later notes, from 0 (silent) to 127 (loudest). Values of 128 or more become 100, the MARS default. Every run starts at 100.
Example
        .org 0x8000
        ld a, 30        ; quietly
        out (0x63), a
        ld a, 60
        out (0x60), a
        halt
shows
No text output; plays a quiet middle C.

Duration

port 0x64
In
Returns 0xFF; this port only accepts writes.
Out
Sets the duration for later notes in milliseconds. Use out (c),r with C = 0x64: B supplies the high byte and the written register supplies the low byte, giving a duration up to 65,535 milliseconds. Every run starts at 1000 milliseconds, the MARS default.
Example
        .org 0x8000
        ld b, 0x07      ; 0x07D0 = 2000 ms
        ld a, 0xd0
        ld c, 0x64
        out (c), a
        ld a, 48        ; a low C for two seconds
        out (0x60), a
        halt
shows
No text output; plays a low C for two seconds.

Sound chip

0xA0 - 0xA2

The AY-3-8910 is the sound chip used in the MSX, ZX Spectrum 128 and Amstrad CPC. Here it uses the MSX register ports and 1.7897725 MHz clock, so compatible AY register programming uses the same port addresses and periods. It has three square-wave tone channels, A, B and C. Each channel can mix its tone with noise and use either a fixed volume or an envelope that changes the volume over time. All channels share one noise generator and one envelope.

Select a register through 0xA0, then write it through 0xA1 or read it through 0xA2. Two-byte periods use the lower-numbered register for the low byte.

RegistersBits keptMeaning
0/1, 2/3, 4/58 low + 4 highTone periods for A, B and C, 0 to 4095
65Shared noise period, 0 to 31
78Mixer: bits 0–2 disable the tones of A/B/C; bits 3–5 disable their noise. A 0 bit enables that source. Bits 6/7 are I/O-direction controls; they are retained on readback but have no effect here because the I/O ports are unconnected.
8, 9, 105Volumes for A, B and C: bit 4 clear uses bits 0–3 as fixed volume, 0 to 15; bit 4 set follows the shared envelope instead
11/128 low + 8 highShared envelope period, 0 to 65,535
134Shared envelope shape, 0 to 15; every write restarts it for all channels following the envelope
14, 15noneUnconnected I/O ports: read 0xFF, writes ignored

A tone's frequency is 1,789,772.5 ÷ (16 × period) Hz: period 254 is about 440 Hz, the A above middle C. The noise generator advances at the same clock ÷ (16 × noise period) rate; smaller periods give faster, brighter noise. Tone, noise and envelope periods of 0 act as 1, while still reading back as 0.

One complete envelope rise or fall takes 256 × envelope period ÷ 1,789,772.5 seconds. The shape selects what follows that first rise or fall:

ShapeFirst movementThen
0–3Fall from full volume to 0Hold at 0
4–7Rise from 0 to full volumeDrop to 0 and hold
8FallRepeat falling ramps
9FallHold at 0
10FallAlternate rising and falling ramps
11FallJump to full volume and hold
12RiseRepeat rising ramps
13RiseHold at full volume
14RiseAlternate falling and rising ramps
15RiseDrop to 0 and hold

Changing the noise period affects every channel with noise enabled. Writing register 13 retriggers every channel using the envelope, even if its shape value is unchanged.

NoteCC♯DD♯EFF♯GG♯AA♯B
Period from middle C428404381360339320302285269254240226

An octave lower doubles the tone period and an octave higher halves it. Every Build starts with every register 0, so every channel is silent. The chip keeps sounding while the program is paused or stopped at a breakpoint, and falls silent when the program ends. Testcases are silent and retain the same register readback.

Select a Sound chip register

port 0xA0
In
Returns 0xFF; this port only accepts writes.
Out
Selects the register, 0 to 15, that the next writes to SOUND_CHIP_WRITE (0xA1) and reads from SOUND_CHIP_READ (0xA2) reach; it stays selected until the next write here. Registers 14 and 15 are the chip’s two I/O ports, which the MSX wires to its joysticks: nothing is attached here, so they read 0xFF and writes to them are ignored. A number of 16 or more selects nothing, as on the chip.
Example
        .org 0x8000
        ld a, 0         ; register 0: channel A's period, low byte
        out (0xa0), a
        ld a, 254       ; 254 is 440 Hz
        out (0xa1), a
        ld a, 1         ; register 1: the period's high four bits
        out (0xa0), a
        xor a
        out (0xa1), a
        ld a, 7         ; register 7: the mixer
        out (0xa0), a
        ld a, 0xbe      ; only channel A's tone on
        out (0xa1), a
        ld a, 8         ; register 8: channel A's volume
        out (0xa0), a
        ld a, 15        ; the loudest
        out (0xa1), a
        ld c, 0x50
        ld b, 100       ; let it sound for a second
        in a, (c)
        halt
shows
No text output; plays a square wave at 440 Hz for a second, which stops when the program ends.

Write a Sound chip register

port 0xA1
In
Returns 0xFF; this port only accepts writes.
Out
Writes the selected register. Each keeps only its own bits: four of a tone period’s high register, five of the noise period and of a volume, four of the envelope shape. Writing the envelope shape, register 13, restarts the envelope, even with the shape it already has; this retriggers all channels following the shared envelope.
Example
        .org 0x8000
        ld hl, chord    ; pairs of a register and its value
        ld b, 10        ; ten of them
next:   ld a, (hl)
        out (0xa0), a
        inc hl
        ld a, (hl)
        out (0xa1), a
        inc hl
        djnz next
        ld c, 0x50
        ld b, 200       ; two seconds
        in a, (c)
        halt
chord:  .byte 0, 0xac, 1, 1     ; A: period 428, middle C
        .byte 2, 0x53, 3, 1     ; B: period 339, E
        .byte 4, 0x1d, 5, 1     ; C: period 285, G
        .byte 7, 0xb8           ; the three tones on, no noise
        .byte 8, 12, 9, 12      ; volumes of A and B
        .byte 10, 12            ; and of C
shows
No text output; plays a C major chord on the three channels for two seconds.

Read a Sound chip register

port 0xA2
In
Returns the selected register as the chip holds it: what was last written, less the bits the register does not keep. Registers 14 and 15, and a number of 16 or more, read 0xFF.
Out
Ignored.
Example
        .org 0x8000
        ld a, 1         ; channel A's period, high bits
        out (0xa0), a
        ld a, 0xff
        out (0xa1), a
        in a, (0xa2)    ; the register keeps four bits
        out (0x11), a
        halt
prints
15