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.

BitGroupBitGroup
0port $FF (Timex)14sprite ports
1$7FFD15Layer 2 $123B
2$DFFD16AY $FFFD / $BFFD
3$1FFD17DAC SD1 $1F/$0F/$4F/$5F
4+3 floating bus18DAC SD2 $F1/$F3/$F9/$FB
5DMA $6B (zxn)19DAC stereo AD $3F/$5F
6Kempston 1 $1F20DAC stereo BC $0F/$4F
7Kempston 2 $3721DAC mono AD $FB
8DivMMC $E322DAC mono BC $B3
9Multiface23DAC mono AD $DF
10I2C $103B/$113B24ULA+ $BF3B/$FF3B
11SPI $E7/$EB25DMA $0B (Z80)
12UART $133B…26$EFF7 (Pentagon)
13mouse $FADF…27CTC $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 $7FFD

ULA and Timex

PortR/WBehaviour
$FE (A0 = 0)RWwrite: border in bits[2:0], MIC, EAR and speaker. Read: keyboard rows plus EAR
$FFRWwrite: 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 red

Paging

These ports are write-only.

PortBehaviour
$7FFDrequires A0 = 1, A15 = 0, and A14 = 1 under +3 timing. Ignored when paging is locked
$1FFDrequires A0 = 1 and A13–A12 = 01; ignored when locked. Bit 0 selects +3 special all-RAM paging
$DFFDrequires 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
$EFF7full 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 $C000

Audio

PortR/WBehaviour
$FFFDRWread: AY data from the selected chip. Write: register select and Turbosound chip select
$BFFDRWAY data write. Under +3 timing a read returns $FFFD
$BFF5RTurbosound chip-select read-back, when Turbosound is enabled
$0F / $4F / $5FWDAC channels 1, 2, 3
$F1 / $F3 / $F9WDAC channels 0, 1, 2
$B3WDAC mono B + C
$DF / $FBWDAC 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 7

Video

PortR/WBehaviour
$123BRWLayer 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
$303BRWread: sprite status. Write: sprite slot and index select
$57Wsprite attribute upload
$5BWsprite pattern upload
$BF3BWULA+ register: mode in bits[7:6] plus index
$FF3BRWULA+ 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 mapped

ULAplus is on its own page, ULAplus.

Nextreg, CTC and DMA

PortR/WBehaviour
$243BRWnextreg select latch
$253BRWnextreg data; see Next Registers
$183B–$1F3BRWCTC, channel taken from address bits[10:8]. A write aborts the current timeslice
$0BRWzxnDMA in Z80 mode
$6BRWzxnDMA 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 MHz

Storage and serial

PortR/WBehaviour
$E3RWDivMMC control: CONMEM, MAPRAM, bank. A read masks off bits 5 and 4, and returns 0 when DivMMC is disabled
$E7WSD card select
$EBRWSD SPI data. A read returns the byte, then clocks $FF out
$103BRWI2C clock line. A read returns $FE OR the line
$113BRWI2C data line. A read returns $FE OR the line
$133BRWUART status; a write transmits a byte
$143BRWUART received byte; a write sets the prescaler’s low bits
$153BRWUART select and prescaler high bits
$163BRWUART 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

PortR/WBehaviour
$1FRKempston 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
$DFRthe 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
$37RKempston 2 / MD pad, same routing rules
$FBDFRmouse X counter
$FFDFRmouse Y counter
$FADFRmouse 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 right

Traps, Multiface and expansion

PortR/WBehaviour
$2xFDRFDC trap; returns $FF
$3xFDRWreads $FF; a write trips the FDC trap when $D8 enables it
$1F / $3F / $9F / $BFRWMultiface paging and control. Which pair enables and which disables follows the Multiface type in $0A bits[7:6]
anyRWthe 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–$2E read path.

See also

← Reference