I/O Ports
What the Next decodes in the Z80’s I/O space, what a read returns, and what a write does. The nextreg bank reached through $243B and $253B has its own page, Next Registers.
The page describes the hardware, as the FPGA design defines it. Where a decode needs more than the port number, the condition is stated with the port. The examples are Z80 assembly.
Internal port enables
Most internal port groups are gated by a bit in nextregs $82–$85: bits 0 to 7 in $82, 8 to 15 in $83, 16 to 23 in $84, and 24 to 27 in $85. A group that is not enabled does not decode at all, so the first thing to check when a port appears dead is its enable bit. Nextregs $86–$8A are the matching enables for the expansion bus.
| Bit | Group | Bit | Group |
|---|---|---|---|
| 0 | port $FF (Timex) | 14 | sprite ports |
| 1 | $7FFD | 15 | Layer 2 $123B |
| 2 | $DFFD | 16 | AY $FFFD / $BFFD |
| 3 | $1FFD | 17 | DAC SD1 $1F/$0F/$4F/$5F |
| 4 | +3 floating bus | 18 | DAC SD2 $F1/$F3/$F9/$FB |
| 5 | DMA $6B (zxn) | 19 | DAC stereo AD $3F/$5F |
| 6 | Kempston 1 $1F | 20 | DAC stereo BC $0F/$4F |
| 7 | Kempston 2 $37 | 21 | DAC mono AD $FB |
| 8 | DivMMC $E3 | 22 | DAC mono BC $B3 |
| 9 | Multiface | 23 | DAC mono AD $DF |
| 10 | I2C $103B/$113B | 24 | ULA+ $BF3B/$FF3B |
| 11 | SPI $E7/$EB | 25 | DMA $0B (Z80) |
| 12 | UART $133B… | 26 | $EFF7 (Pentagon) |
| 13 | mouse $FADF… | 27 | CTC $183B–$1F3B |
A reset enables every group, unless nextreg $85 bit 7 has been cleared, in which case the enables survive the reset. Bit 7 is set at power-on. Clearing bit 8 also resets the DivMMC automap, as clearing nextreg $0A bit 4 does.
With nextreg $82 bit 1 cleared, a write to $7FFD pages nothing:
nextreg $82,%11111101 ; every group in $82 but $7FFDULA and Timex
| Port | R/W | Behaviour |
|---|---|---|
$FE (A0 = 0) | RW | write: border in bits[2:0], MIC, EAR and speaker. Read: keyboard rows plus EAR |
$FF | RW | write: Timex mode, passed to the ULA, and the $6A Radastan XOR applied again. Read: the stored value if $08 bit 2 and the enable are set, otherwise the floating bus |
The floating bus is the pixel or attribute byte the ULA is fetching from the active screen bank, 5 or 7 by the $7FFD shadow bit, while the beam is in the display area, and $FF elsewhere. It is returned under 48K and 128K machine timing only; under +3 or Pentagon timing the read returns $FF.
ld a,2
out ($FE),a ; border redPaging
These ports are write-only.
| Port | Behaviour |
|---|---|
$7FFD | requires A0 = 1, A15 = 0, and A14 = 1 under +3 timing. Ignored when paging is locked |
$1FFD | requires A0 = 1 and A13–A12 = 01; ignored when locked. Bit 0 selects +3 special all-RAM paging |
$DFFD | requires A0 = 1 and A15–A12 = 1101. Written when not locked, or in Profi mode. When its enable bit is clear it falls through to $FFFD and writes the AY address instead |
$EFF7 | full low byte $F7. Bit 2 Pentagon-1024, bit 3 all-RAM |
⚠ With $DFFD disabled, a write to it selects an AY register. The two share a decode.
$7FFD bits 0 to 2 choose the 16K bank at $C000, bit 3 the screen in bank 7, bit 4 the ROM, and bit 5 locks paging until a reset:
ld bc,$7FFD
ld a,3
out (c),a ; bank 3 at $C000Audio
| Port | R/W | Behaviour |
|---|---|---|
$FFFD | RW | read: AY data from the selected chip. Write: register select and Turbosound chip select |
$BFFD | RW | AY data write. Under +3 timing a read returns $FFFD |
$BFF5 | R | Turbosound chip-select read-back, when Turbosound is enabled |
$0F / $4F / $5F | W | DAC channels 1, 2, 3 |
$F1 / $F3 / $F9 | W | DAC channels 0, 1, 2 |
$B3 | W | DAC mono B + C |
$DF / $FB | W | DAC mono A + D |
A write to $FFFD is a chip select when the value ANDed with $9C equals $9C. Its low two bits then pick the chip: 11 chip 0, 10 chip 1, 01 chip 2, and 00 chip 0. Any other value selects an AY register.
Every DAC port is gated by $08 bit 3 as well as its own enable bit.
ld bc,$FFFD
ld a,$FE
out (c),a ; Turbosound: chip 1
ld a,7
out (c),a ; then its register 7Video
| Port | R/W | Behaviour |
|---|---|---|
$123B | RW | Layer 2 control. A write with bit 4 clear sets bit 0 write mapping, bit 1 display, bit 2 read mapping, bit 3 shadow and bits[7:6] segment; with bit 4 set, bits[2:0] are the paging offset. A read composes the same fields |
$303B | RW | read: sprite status. Write: sprite slot and index select |
$57 | W | sprite attribute upload |
$5B | W | sprite pattern upload |
$BF3B | W | ULA+ register: mode in bits[7:6] plus index |
$FF3B | RW | ULA+ data. A read returns the palette entry bit-swapped, or the enable flag. Writing the enable also sets nextreg $68 bit 3 |
ld bc,$123B
ld a,%00000010
out (c),a ; Layer 2 displayed, not mappedULAplus is on its own page, ULAplus.
Nextreg, CTC and DMA
| Port | R/W | Behaviour |
|---|---|---|
$243B | RW | nextreg select latch |
$253B | RW | nextreg data; see Next Registers |
$183B–$1F3B | RW | CTC, channel taken from address bits[10:8]. A write aborts the current timeslice |
$0B | RW | zxnDMA in Z80 mode |
$6B | RW | zxnDMA in Next mode |
At 28 MHz the DMA’s memory reads take the same wait state as the CPU’s; see Machine Timing.
ld bc,$243B
ld a,$07
out (c),a ; select nextreg $07
inc b ; BC = $253B
ld a,3
out (c),a ; 28 MHzStorage and serial
| Port | R/W | Behaviour |
|---|---|---|
$E3 | RW | DivMMC control: CONMEM, MAPRAM, bank. A read masks off bits 5 and 4, and returns 0 when DivMMC is disabled |
$E7 | W | SD card select |
$EB | RW | SD SPI data. A read returns the byte, then clocks $FF out |
$103B | RW | I2C clock line. A read returns $FE OR the line |
$113B | RW | I2C data line. A read returns $FE OR the line |
$133B | RW | UART status; a write transmits a byte |
$143B | RW | UART received byte; a write sets the prescaler’s low bits |
$153B | RW | UART select and prescaler high bits |
$163B | RW | UART framing |
MAPRAM is sticky. A write can set it and never clear it; only a hard reset or nextreg $09 bit 3 clears it.
$E7 is a small state machine. The low two bits of the value, combined with the $0A card-swap bit, select a card; the values $FB, $F7 and $7F are recognised whole, and drive both cards’ chip selects. $7F selects the flash, and only in config mode.
The Next has two UARTs, UART 0 wired to the ESP module and UART 1 to the Pi, and $153B selects which one the UART ports address. Both $153B and $163B are readable.
With the I2C data line released high, a read of $113B returns $FF.
Input
| Port | R/W | Behaviour |
|---|---|---|
$1F | R | Kempston 1 / MD pad. $05 decides which physical pad routes here; left and right are combined, Kempston in bits[5:0] and MD in bits[7:6]. The whole low byte is decoded, so $01, $03 or $1D do not reach it, as they would on a 48K |
$DF | R | the same pad as $1F, when the mono DAC on $DF is enabled (port enable bit 23) and the mouse ports are not (bit 13). Any high byte |
$37 | R | Kempston 2 / MD pad, same routing rules |
$FBDF | R | mouse X counter |
$FFDF | R | mouse Y counter |
$FADF | R | mouse buttons, active low, honouring the $0A reverse bit, plus the wheel in bits[7:4] |
The mouse DPI setting, $0A bits[1:0], scales the motion in the mouse’s own receiver, before these counters; see Next Registers.
ld bc,$FBDF
in a,(c) ; mouse X, counting up as it moves rightTraps, Multiface and expansion
| Port | R/W | Behaviour |
|---|---|---|
$2xFD | R | FDC trap; returns $FF |
$3xFD | RW | reads $FF; a write trips the FDC trap when $D8 enables it |
$1F / $3F / $9F / $BF | RW | Multiface paging and control. Which pair enables and which disables follows the Multiface type in $0A bits[7:6] |
| any | RW | the expansion-bus view, disabled by default |
The DivMMC automap traps at $3D00–$3DFF and $1FF8–$1FFF are taken on the instruction fetch, not through a port.
Shared addresses
Several low-byte ports collide: $1F is Kempston 1, a DAC channel and the Multiface; $3F is a DAC channel and the Multiface; $DF is the mouse, a DAC channel and, with the mouse disabled, Kempston 1. The hardware tells them apart with the internal port enables and the machine state, so a program using more than one of them sets those enables deliberately.
Unemulated hardware
- The expansion bus: nothing is attached, so the expansion-bus view is absent.
- The Pi’s I2S input, behind the
$2C–$2Eread path.
See also
- Next Registers: the enable bits, and the registers these ports mirror.
- Machine Timing: which of these ports are contended.