Emulated chips and hardware blocks

What real silicon (or silicon-equivalent logic) zxplay_go models, where each model lives, which style it uses, and what its oracle is. Styles are defined in emulation-patterns.md; Next-specific FPGA blocks are summarised here and detailed in next-fpga.md.

Summary table

Chip / blockReal partPackageStyleOracle
CPUZilog Z80A / Z80N (FPGA)pkg/z80cycle-accurate, per-opcode T-stateszexdoc/zexall, Sean Young, FUSE, GHDL t80n gate golden
ULA (48K/128K/+2A/+3)Ferranti ULA / Amstrad gate arraypkg/ula + pkg/memoryframe render + recorded events; cycle-accurate contentionFUSE, zxula_timing.vhd, Ramsoft floating-bus paper
PSGAY-3-8912 / YM2149pkg/aycycle-faithful FPGA port, sample-rate synthesisaudio/ym2149.vhd + golden test, measured volume curves
Turbosound3 × AY (Next)pkg/ay (Engine)chip-select bank over the AY core$FFFD select protocol, NR$06
Beeper / tape outULA bit 4/3pkg/ulaevent-timed box filter + DC blockerhardware behaviour (capacitor coupling)
DACsSpecDrum, Covox, Next 4-ch, Soundrivepkg/audiodac, pkg/next/dacevent-timed box filterdac golden test
SAA1099Philips SAA1099 (SAM)pkg/saa1099datasheet-modelled, float phase accumulatorsdatasheet only (no open reference core)
FDC (+3)NEC µPD765Apkg/plus3fdcI/O-advanced 3-phase command machinedatasheet; weak sectors + Speedlock heuristic
FDC (Beta/TR-DOS)WD1793pkg/betadiskI/O-advanceddatasheet tests
FDC (DISCiPLE)WD1772pkg/discipleI/O-advanceddatasheet; WD1772-vs-1793 differences modelled
FDC (SAM)WD1772pkg/samI/O-advancedmodelled on betadisk structure
Microdrive + IF1 ULASinclair IF1pkg/if1, pkg/microdrivetape-loop with pulse-accurate GAP/SYNCFUSE if1.c (near line-for-line), libspectrum .mdr
MultifaceRomantic Robot MF1/128/3pkg/multifacetwo models: integration model + clock-exact FPGA core (the Next's port pair drives the FPGA core via pkg/next/mf.go)multiface.vhd + GHDL golden
RTCDS1307 on i2cpkg/next/rtcregister-accurate; host clock; NVRAM persistedDS1307 datasheet; NextZXOS bit-bang traffic
SD cardSPI-mode SD/SDHCpkg/next/sdcardprotocol-accurate SPI state machine, CSD v1/v2, CRC16SD spec + SPI golden test + boot behaviour
ESP Wi-FiESP8266 AT firmwarepkg/next/uart (ports $133B-$163B via ULA.SetNextUART)deliberate stub (AT responder, no networking; NR$A8/$A9 are the ESP GPIO regs, not the UART)scope decision
Kempston joystickpkg/ulaport $1F bitmapclassic behaviour
Megadrive pad (Next)3/6-button MD controllerpkg/ula (MDJoyLeft/MDExtraState), pkg/next (NR$B2)left pad only: 12-bit i_JOY vector fed by the host gamepad; ports $1F/$37 per NR$05 routing. The pad's own 3-state select/multiplex protocol is not modelled — the FPGA presents the decoded vector, so there is nothing to multiplexzxnext.vhd:90, :3472-3507, :6206-6215
Kempston mousepkg/kempmouse (classic), pkg/next/mouse (Next $FADF/$FBDF/$FFDF, NR$0A DPI/reverse)free-running 8-bit countersport decode masks; ps2_mouse.v + zxnext.vhd:3541-3560
ZX Printerpkg/zxprintercycle-accurate drum timingROM polling behaviour
SAM ASICMGT ASICpkg/samline-accurate lazy renderer + contention tablesSAM technical docs; spec tests
ZX80/81 "ULA"discrete logic + Z80 trickspkg/zx8xCPU-generated display (NOP substitution)hardware documentation; spec tests
Next FPGA blocksTBBlue Xilinx corepkg/next/*VHDL transcription + goldenszxnext.vhd et al. (see next-fpga.md)

CPU — Z80 and Z80N (pkg/z80)

One monolithic CPU struct: main/alternate registers, IX/IY/SP/PC, I/R, IFF1/IFF2, IM, WZ (MEMPTR), flag lookup tables, and the timing state. Decode is hand-written nested switches (base, CB, ED, DD, FD, DDCB, FDCB), no code generation and no jump tables. Each case adds its own T-state total; converted opcodes use FUSE-style per-cycle helpers instead.

Faithfulness points:

Conformance: TestZexdoc/TestZexall (Cringle exercisers under a flat 64K + BDOS trap), MEMPTR/timing/interrupt test batteries, the gate-level golden.

ULA — video, ports, tape (pkg/ula)

The ULA type is the I/O hub for every machine and the classic video renderer. Video: once-per-frame render of the 256×192 bitmap (Spectrum address interleave, attributes, FLASH every 16 frames) inside a 320×240 border (320×256 on the Next — the FPGA's wide frame, paper at 32,32), with mid-frame border changes replayed per scanline. On classic models the PAPER renders from beam-time scanline captures: the CPU's ExecuteFrame polls ula.CaptureScanlines as the counter crosses each line's fetch window, so a rendered row holds what the ULA fetched when the beam passed — not end-of-frame memory. Beam-racers need this: Arkanoid XOR-erases its bat in the vblank and redraws it next frame just ahead of the beam, so the bat exists on the CRT every frame while being absent from memory at the frame boundary (#194). Uncaptured lines (single-step paths, first frame, ModelNext) fall back to live memory. Timex hi-res (port $FF) and the Next paths hang off the same render.

Port $FE: keyboard scan AND-plane, EAR from the tape edge stream, MIC and speaker bits recorded as audio events, border writes recorded with scanline positions. The floating bus is the canonical Ramsoft/FUSE algorithm (returns $FF on +2A/+3/Next): the fetch window is the FIRST 128 T of each paper line from the top-left-pixel time — 48K 14336, 128K/+2 14362 (libspectrum timings.c) — with bitmap/attr pairs on slots 2..5 of each 8, computed on the RAW frame-relative T counter (the same grid contention anchors on; never on the audio flush's frameStartTstate, whose per-frame overshoot jitter desyncs the two). Frame timing: 224 T/line on 48K, 228 on everything later, per-model totals from pkg/roms.

Tape: .tap/.tzx as a pulse stream (pilot/sync/bit timings, turbo blocks), advanced per port read so edge-timed loaders work; a ROM LD-BYTES trap provides fast loading; the $FE-read rate detects active loading for the tape-turbo mode. Tape time rides the MONOTONIC reference clock (SetTapeRefClockcpu.RefTstates, not the frame-wrapping raw counter), so the lazy per-read catch-up never drops the inter-frame gap for sparse-polling loaders — and the audio flush's once-per-frame idempotence guard rides the same clock: compared on the wrapping raw counter, two frames ending on the same residue read as "no CPU time has passed" and a whole frame of beeper audio was silently dropped (deterministic beeper loops repeat residues constantly — a sustained BEEP lost 17% of its frames on the 48K, 2% on the 128K; the Next's monotonic mem.RefTstates was never affected); inter-block pause chunks consume time WITHOUT toggling EAR (silence has no edges); and the loader-activity auto-pause (tapeFrameHook, shared by desktop / wasm / headless loops) parks an unpolled deck within 1.5 s — wider than the 48 ROM's 1 s read-free LD-START settle delay — and resumes it losslessly when a loader polls again (#192, Hewson custom loaders). A second fast trap at LD-EDGE-1 ($05E7, which also covers the $05E3 LD-EDGE-2 entry via its fall-through) emulates the ROM's edge-sampling loop in O(1) for custom loaders the block trap can never serve: it advances the CPU clock by exactly the T-states the loop would burn and computes B on the same 59 T sample grid, so bit discrimination (the B count) and loader timing checks are preserved — the emulated timeline is unchanged, only the host cost collapses (tapeTrapLDEdge, byte-exactness + timeline-neutrality pinned by TestLDEdgeTrapByteExact and the corpus goldens). Loaders that copy the routine into RAM (Speedlock class) still interpret at real time under the turbo.

Contention lives in pkg/memory (pattern {6,5,4,3,2,1,0,0}, display window only, per-model enables/anchors: 48K 14335 on 224 T lines, 128K/+2 14361 on 228 — one T before the matching floating-bus window, so the two grids stay in phase). Port I/O is cycle-exact: the CPU's ioIn/ioOut helpers charge the 4 T I/O machine cycle and call ContendPortEarly/ContendPortLate (holds ONLY, FUSE's ula_contend_port_early/late shapes per Sean Young §4.2, including the +2A/+3 no-port-contention rule) — the port READ samples the bus at the cycle's 4th T-state (IN r,(C): instruction T+11) and the port WRITE lands after its 1st. Beam-racers need all three grids to agree: Arkanoid paces its 50Hz game loop by phase-locking floating-bus polls via a contended screen write, and any grid mismatch made it misread "raster left the paper" mid-frame and run 2-3 game updates per frame (#194, TestArkanoidBallSpeed).

Sound

Floppy controllers and storage

All four FDCs are I/O-advanced (see patterns doc):

The Next storage stack (divMMC, SPI SD card, FAT32 builder, esxDOS) is covered in next-fpga.md.

Multiface 1/128/3 (pkg/multiface)

Two models coexist: multiface.go is the integration model the peripheral manager uses (NMI button, ROM/RAM overlay, session-scoped paging freeze so MF port pokes cannot corrupt host paging, A7-based page-in/out with the MF3 inversion, invisibility). core.go is a clock-exact transcription of the Next FPGA's multiface.vhd, golden- tested, used where FPGA-exact behaviour matters (paging read-back ports $7F3F/$1F3F on the Next).

Input and printer

Machines with their own display logic

Formats as "virtual hardware"