Changelog
All notable changes to Emu198x will be documented in this file.
Format loosely based on Keep a Changelog, not strictly. Versions follow Semantic Versioning.
[0.44.2] - 2026-10-23
The 68000 now keeps the real chip’s timing: its instructions, bus accesses, exceptions and interrupts take the clocks the hardware takes. On the Amiga, CIA accesses wait for the E clock as they do on a real machine.
Fixed
- Give 68000 instructions their real clock counts
- Put each 68000 bus access where the real chip makes it
- Give 68000 exceptions their real bus sequence and timing
- Give 68000 interrupts their real 44-clock sequence
- Stop undefined addressing modes panicking the 680x0 cores
- Make Amiga CIA accesses wait for the E clock
- Make CIA accesses through the GVP A530 wait for the E clock
- Decode Amiga CIA space as the hardware does, and end CIA accesses on time
- Preserve live floppy disk-change notifications on the Amiga
- Give the Apple app’s Amiga the A501 512K expansion
[0.44.1] - 2026-10-22
Fixed
- Stop Amiga run-to-address and breakpoints missing their target
- Make the 68000 trap ILLEGAL instead of running it as TAS
- Make the 68000 take trace exceptions
- Stop odd blitter modulos shearing Amiga graphics
- Show the right address for 68000 PC-relative operands
- Pace FDC head movement at the programmed step rate
[0.44.0] - 2026-10-21
Added
- Model a chain of up to four Amiga floppy drives
- Wire the Amiga machines to four floppy drive positions
- Let the Amiga runtime load disks into DF1 to DF3
- Fit up to four floppy drives from the Amiga command line
- Give the C64 a blank tape to SAVE onto through Fleet
- Play C64 tapes on Apple TV
- Play Amiga disks on Apple TV
- Fill the Apple TV app with Dizzy on every system with one command
Fixed
- Reject raw observation disagreements in DTL research evidence
- Reject raw observation disagreements in SK research evidence
- Keep the contents of C64 tapes loaded through Fleet
- Build the workspace again after the 0.43.0 release
- Reject Format Track when supplied READY is absent
[0.43.0] - 2026-10-21
Added
- Play 128K versions of games on Apple TV
- Fill the Apple TV app’s local content with one command
- Run Codemasters cartridges on the Master System and Game Gear
- Play NES, Master System and Game Gear cartridges on Apple TV
- Autoload C64 tapes in Fleet and take media before boot
Fixed
- Reject read commands when the physical drive is not ready
- Continue multitrack reads on side one
- Honor DTL while consuming the complete read field
- Prefer preserved EDSK versions on repeated reads
- Stop data reads before a missing data address mark
- Breaking — Honour programmed FDC transfer mode and byte interrupts. Spectrum native snapshots advance v9 to v10 and reject v9 before payload decode, as explicitly approved. CPC is unchanged. New/reset controllers keep the prior non-DMA compatibility fallback; hardware startup/reset ND, timer accuracy and DMA pins remain unqualified.
[0.42.0] - 2026-10-21
Added
- Play the Spectrum in a native macOS app
- Report each machine’s pixel aspect to native hosts
- Draw the Mac picture with even pixels at the machine’s true aspect
- Let a native host’s controller drive the Spectrum’s Kempston joystick
- Let native hosts autoload a Spectrum tape
- Start a Spectrum tape from Swift without a keyboard
- Play Spectrum programs on an Apple TV
- Route controller buttons to any joystick port or key
- Choose controller presets for each game on Apple TV
- Let Apple hosts ask the runtime about the running machine
- Press a game’s joystick-select key for controller players
- Show box art in the Apple TV library
Fixed
- Show normal Ace characters white on black
- Stop Apple host sound breaking up after ten minutes of play
- Answer Kempston reads on every port with A5 low
- Get past every Dizzy title screen with a controller alone
- Honour Read ID readiness and completion interrupts
[0.41.0] - 2026-10-20
Added
- Let native Apple hosts drive the emulator through a C interface
- Package the Apple bridge for Xcode on macOS and Apple TV
- Run the emulator from Swift on its own real-time thread
Fixed
- Breaking — Separate +3 drive status from inserted media. Spectrum native snapshots advance v8 to v9 and reject older versions before payload decode, as explicitly approved. CPC and portable imports are unchanged.
- Honor write protection at the final data-field deadline
[0.40.0] - 2026-10-20
Fixed
- Report +3 two-side status from its write-protect wiring
Docs
- Breaking — Explain Spectrum native save v8 compatibility. Spectrum native saves use version 8; version 7 and earlier are rejected without migration. Keep the matching older emulator to resume those saves.
[0.39.2] - 2026-10-20
Fixed
- Report READY attention and preserve the digital SIS state
- Preserve logical FDC positions across drive aliases
- Retain FDC drive busy until SIS reads status
- Report not-ready recalibrate as abnormal seek end
[0.39.1] - 2026-10-20
Fixed
- Acknowledge FDC interrupt when SIS is issued
[0.39.0] - 2026-10-20
Added
- Publish the browser player to npm from each release
Fixed
- Report represented ID CRC errors from Read ID
- Preserve spaces when entering Atom BASIC programs
- Preserve typed semicolons on the SVI-328
- Let Ctrl-BREAK boot Einstein programs through runtime input
- Give Escape to the emulated keyboard instead of quitting
- Make Jupiter Ace ROM sound follow the I/O speaker latch
- Type ZX80 quotation marks with the ROM’s Shift-Y key
[0.38.3] - 2026-10-19
Fixed
- Stop disk reads before payload when the ID CRC is bad
[0.38.2] - 2026-10-19
Fixed
- Preserve encountered Read Track ID CRC errors on interruption
[0.38.1] - 2026-10-19
Fixed
- Preserve the C64 load end pointer for PRG unpackers
- Prevent cumulative drift in +3 disk read timing
- Explain failed media opens in a native dialog
- Match recorded head and size before reading sector data
- Preserve requested ID head in disk read results
[0.38.0] - 2026-10-18
Added
- Search a Spectrum tape for the previous or next block
Fixed
- Breaking — Retain the current sector and encountered errors during reads. Spectrum native snapshots use version 6 and reject version 5 before payload decoding. Approved explicitly; CPC stays unchanged. Add 108 read-progress/result snapshot checkpoints and preserve the released controller fixture as legacy-rejection evidence. Tracks #1831.
- Abort reads when their selected disk changes
[0.37.0] - 2026-10-17
Added
- (web-player) Show the tape controls on the 48K
- Rewind a Spectrum tape to its start, in the browser too
Fixed
- Finish no-pending Sense Interrupt after its ST0 byte
- Keep a stopped tape in phase when it plays again
- (spectrum-web) Run ahead only while the ROM is loading from tape
- Explain failed quick-loads in a native dialog
- Pace disk read requests and report missed byte slots
- Stage the first disk read request after the pre-data interval
[0.36.0] - 2026-10-17
Added
- Preserve deleted data marks through disk writes and exports
- Read represented track sectors in physical order
- Compare disk sectors with paced Scan commands
- (spectrum-web) Let a page stop and restart the tape
Fixed
- Retain the written data mark when the first disk byte overruns
- Reject oversized disk read size codes before allocating
[0.35.5] - 2026-10-16
Fixed
- Report end of cylinder when reads finish without terminal count
[0.35.4] - 2026-10-16
Fixed
- Reject reads from mismatching cylinder headers
[0.35.3] - 2026-10-16
Fixed
- Return invalid-command status from the µPD765A
- Report missing sector IDs instead of successful Read ID
- Reject Seek when the selected drive is initially not ready
[0.35.2] - 2026-10-16
Fixed
- Keep Spectrum family runtimes off the startup stack
[0.35.1] - 2026-10-16
Fixed
- Honor Spectrum machine and firmware selection at MCP startup
[0.35.0] - 2026-10-16
Added
- Export the live Spectrum disk through shared media tools
[0.34.2] - 2026-10-16
Changed
- Publish an unsigned producer source-lock receipt beside each native emulator archive. Packaging requires the committed Cargo.lock to remain unchanged, and qualification checks source, lock, target, producer run and archive/executable identities before hosting. Receipt checks also run after macOS notarization. These records do not establish the compiled dependency/features graph or a receipt signing identity. Emulator behaviour is unchanged. (#1782)
[0.34.1] - 2026-10-15
Fixed
- Deliver Paula loop requests as the final sample word starts
[0.34.0] - 2026-10-14
Added
- Format bounded +3 tracks and preserve in-flight state
[0.33.0] - 2026-10-14
Fixed
- Breaking — Preserve Spectrum +3 ROM disk writes. Spectrum runtime snapshots use version 5 to preserve live modified disk images, protection and in-flight write state. Version 4 states are rejected before decoding. This compatibility boundary was explicitly approved; CPC snapshots remain unchanged.
[0.32.5] - 2026-10-13
Fixed
- Restore Spectrum SZX snapshots through scripts and MCP
- Reject truncated Spectrum SNA snapshots
[0.32.4] - 2026-10-13
Fixed
- Restore Amstrad Spectrum paging after reset
[0.32.3] - 2026-10-13
Fixed
- Unlock Spectrum 128K paging on reset
[0.32.2] - 2026-10-13
Fixed
- Preserve NES background alignment across frame boundaries
[0.32.1] - 2026-10-13
Fixed
- Preserve Spectrum frame progression after debugger stepping
[0.32.0] - 2026-10-12
Added
- Connect Sega software to an optional host UART
Fixed
- Align Atari 7800 runtime time with the native oscillator
- Preserve MARIA fetch timing and pending state
- Stretch Atari 7800 CPU clocks for peripheral accesses
- Sample Atari 7800 CPU transactions in their bus phase
- Preserve MARIA native DMA handoffs and descriptor timing
- Display MARIA pixels through timed line buffers
- Release MARIA DMA after scanline cutoff
- Preserve MARIA control writes through DMA restart
- Preserve pixels and timing through MARIA address holes
- Release MARIA WSYNC through native raster and CPU phases
- Run Atari 7800 through native MARIA bus and raster stages
- Require native MARIA timing for rendering
- Preserve POKEY audio through variable clocks and restores
- Deliver MARIA interrupts through sampled clock stages
- Match ROM BASIC bytes across native and browser loaders
[0.31.1] - 2026-10-11
Fixed
- Preserve Paula samples across audio attachment changes
- Use updated Paula periods on simultaneous channel expiry
- Preserve short Paula signals and audio across restore
- Preserve nominal Paula sample amplitudes
- Prevent audible aliases in Amiga audio output
- Deliver every Amiga bitplane pointer register write
- Detect C64 keys when scanning from port B to port A
- Resolve C64 keyboard ghost paths and competing port outputs
- Preserve C64 SHIFT LOCK contact and output strength
- Start VIA timer 2 after its load cycle
- Keep VIA Timer 2 counting after timeout
- Preserve C64 light-pen coordinates across frame wrap
- Preserve drive interrupts on alternate VIA port reads
- Assert VIA handshake outputs on port transfers
- Deliver Oric VIA output changes to the sound chip
- Preserve external pin levels in AY port B readback
- Keep MARIA borders in step with scanline colours
- Preserve POKEY channel polarity with filters disabled
- Sample POKEY noise only on channel timer events
- Retain POKEY filter state across disable and re-enable
- Restore POKEY high-speed pitch and timer IRQ rates
- Decode VIC-20 I/O mirrors and overlapping chip selects
- Return FF from unused VIC-II registers
- Preserve the C64C bank-switch fetch transient
- Preserve MC6809 data in mixed-width register transfers
- Execute MC6809 inherent aliases with the correct discarded reads
- Put MC6809 register instruction reads on the correct bus address
- Reproduce MC6809 decimal-adjust overflow
- Execute verified undocumented MC6809 accumulator operations
- Reject truncated SN76489 states before changing registers
- Remove the extra clock from SN76489 tone periods
- Preserve the full TI zero-period tone countdown
- Clock TI slaved noise from the tone output edge
- Deliver MTX frame interrupts on the selected CTC edge
- Keep Sord M5 PAL audio at the runtime sample rate
- Preserve SMS sprite overflow while the display is blanked
- Load NES 2.0 exponent-sized ROM payloads correctly
- Inspect Sunsoft-4 ROM nametables accurately
- Make NES screen inspection follow nametable mirroring
- Load NES trainers into cartridge RAM before execution
- Restore the Sord M5 upper I/O aliases
- Route SG-1000 player-two controls to their wired bits
- Preserve full 6845 sync pulses across raster boundaries
- Enforce 6845 register widths and light-pen write protection
[0.31.0] - 2026-10-07
Added
- Expose the Oric’s expansion-port MAP and ROMDIS lines, so a peripheral can read and write the 16 KB of RAM under the ROM. MAP exposes the RAM, as Mike Brown’s ULA guide describes; ROMDIS alone only silences the ROM. The 48K Oric-1 now has that RAM too. (#344)
Changed
- Snapshot compatibility: C64 snapshots now use layout 17, Oric snapshots layout 6 and Amiga snapshots layout 60. Earlier layouts are rejected because they lack the new VIC-II shifter, expansion-port and Paula state. (#1660, #1661, #344)
- On the Oric, a write under the ROM no longer reaches the RAM beneath it unless MAP is asserted, as on the hardware. (#344)
Fixed
- Start the 6522 VIA’s timer 1 one cycle after a T1C-H write, as the data sheet specifies. This fixes VIA-timed code on the VIC-20, PET, Oric, BBC Micro, Atom and the 1541/1571 drives: VIC-20 stable-raster programs now settle, and a 1571 load of Bruce Lee that used to stall now completes. (#1642)
- Apply the C64 VIC-II’s mode bits at the shifter output, so a mid-line mode
change lands at a fixed dot whatever the scroll, and show the last
character’s background in an opened side border. VICE’s
modesplitandbordertests now match exactly on both the 6569 and 8565. A multicolour “01” pixel now counts as background for sprite priority and collisions. (#1660, #1661) - Report the VIC-20 NTSC raster line 37 cycles before it is drawn, and hold line 0 back for 33 cycles, as real 6560s do. (#1643)
- Draw the BBC Micro’s MODE 7 as wide as the other modes: each teletext cell is one microsecond, and the 40 columns fill the picture. (#1623)
- Keep Paula playing correctly across DMA mode changes and DMA start-up cancellation. (mode changes, start-up)
Known issues
- Seven Amiga game-catalogue entries still need a reviewed re-capture after the 0.30.0 display and audio timing changes. (#1662)
[0.30.0] - 2026-10-07
Added
- Drive the C64 user port from host devices. Every pin is exposed, the CIA
serial port shifts in both directions, and CNT can clock the timers. VICE’s
cia-sp-testandcia-icr-testprograms now pass. (#797)
Changed
- Snapshot compatibility: Amiga snapshots now use layout 59 and C64 snapshots use layout 15. Earlier layouts are rejected because they lack the new display-blanking, Paula, CIA serial and VIC-II scroll state. (Amiga blanking, Paula, #797, #1620)
Fixed
- Read the Spectrum’s tape input in port
$FEbit 6 the right way up on the 16K/48K, and give each later model its real level with no tape: Issue 3 read-back on the 128K and +2, and 0 on the +2A, +2B and +3, whose EAR input is separate from MIC and the speaker. The speaker bit now holds the tape bit high, as the ULA’s bias does. (#1637) - Read bits 7 and 5 of port
$FEas 0 on the Timex TC2048, TC2068 and TS2068, as the TS2068 Technical Manual specifies. (#1640) - Apply a mid-line write to the C64’s horizontal scroll register at the VIC-II’s next character load, so split-screen scroll effects match VICE exactly on both the 6569 and 8565. (#1620)
- Blank the Amiga’s display edges where the real chips do: the OCS right edge at the Denise comparator, AGA vertical blanking until the horizontal release edge, and Lisa’s blanking across counter resets. (OCS, AGA, Lisa)
- Time Paula’s audio as the hardware does: short and zero periods, modulation and DMA requests on byte transitions, DMA interrupts on their scheduled sample edges, and manual playback with early stops. (periods, modulation, interrupts, manual playback)
Known issues
- Seven Amiga game-catalogue entries moved with the display and audio timing changes above and have not yet been re-captured, so the nightly catalogue reports them. (#1662)
[0.29.0] - 2026-10-07
Added
- Install each of the 30 released machines separately through Homebrew, for
example
brew install emu198x/tap/emu198x-spectrum. Packages cover macOS and Linux on ARM64 and x86-64. Both Linux architectures install and exercise the generated formulae before the tap is updated. (#1651) - Select signal, monitor and modern-display modes for Spectrum, C64, NES and Amiga output. Captures retain electrical source timing alongside frames; raw screenshot pixels and default display behaviour remain unchanged. (video output)
- Choose an Atari 800XL cartridge’s banking scheme with
--cart-type, or thecart_typefield in scripts and MCP. The explicit choice takes precedence over the cartridge header, known-image lookup and size-based default. (#1401)
Changed
- Snapshot compatibility: Amiga snapshots now use layout 54 and VIC-20 snapshots use layout 7. Earlier layouts are rejected because they lack the timed chip state needed to resume accurately. (Amiga timing, VIC-20 timing)
Fixed
- Preserve Amiga chip timing through DMA arbitration, register delivery and display output, including the full superhires raster and stable interlace presentation. The 68020 also retains prefetched longwords across instruction and bus cycles. (Amiga timing, 68020 prefetch)
- Recognise known headerless Atari OSS cartridges and apply their board’s bank switching and reset state, so BASIC XL/XE, MAC/65 and Action! reach their prompt or editor instead of running the wrong bank. (#1401)
- Latch the VIC-20 text-row count at the top of the frame and its column count at the start of each line. Mid-frame and mid-line writes no longer alter portions of the picture that should retain the earlier count. (#362)
- Restore C64 electrical output immediately when loading a snapshot, without waiting for rendering to resume. (snapshot restore)
[0.28.1] - 2026-10-06
Changed
- Spectrum-family snapshots saved by earlier versions no longer load, because the machines now save the tape’s end-of-tape state
Fixed
- (spectrum) After a tape stops, the EAR input is released a frame later, as FUSE does, on all seven Spectrum-family machines; before, the 48K dropped it instantly and the others held it high for ever, so games like Operation Wolf and Bubble Bobble played a faint ripple instead of silence (#1633)
- (spectrum) A held beeper level gives a flat, silent output instead of a ~81-sample ripple from uneven sample bins (#1634)
[0.28.0] - 2026-10-06
Highlights
- C64C grey dot. The PAL and NTSC C64C now use the HMOS-II VIC-II (8565/8562), which shows one light-grey dot where a colour register changes; VICE’s
greydotmatches exactly on both chips, and 13 more of VICE’s C64C test programs now match where none did (#796) - Game Boy mid-scanline timing reaches 23 of 24 Mealybug tests against the DMG-CPU B the emulator models, using the gate-level GateBoy simulation as a second reference (#316)
- BBC Micro teletext. MODE 7 has the SAA5050’s character rounding, double height, flashing and concealed text, with hold-graphics fixes; the BBC picture now holds both interlaced fields (#383)
Changed
- The BBC Micro’s picture is 640×512 (both interlaced fields) and its window opens at scale 1, the same size relationship as the Amiga’s
- The
emu198x-mos-sid-6581crate changes its public API (published separately as 0.9.0):clock_noisemust followclock_accumulatoron the same cycle, andset_pulse_widthandosc3are new - Snapshots saved by earlier versions of the C64, BBC Micro, Master System and Game Gear no longer load, because those machines now save more state
Fixed
- (sid) The noise register shifts two cycles after its clock edge, the pulse comparator answers a cycle late, the 8580’s OSC3 reads triangle and sawtooth a cycle late, and the chip powers up with the accumulator and noise values real SIDs show; VICE’s
noisewriteback,oscinit,detectandosc_topbitprograms now pass (#1606) - (sid) On the 6581, sawtooth combined with another waveform pulls the accumulator’s top bit low, as real chips do (#1606)
- (master-system) VRAM and CRAM writes that come faster than the VDP’s free memory slots are lost, as on a real Master System II; Game Gear CRAM writes are unaffected (#1614)
- (game-boy) Mid-scanline writes to SCY and the LCDC fetch bits reach the background fetcher a dot earlier, and a window-off write stops the fetch on the dot the hardware does (#316)
[0.27.2] - 2026-10-06
Highlights
- C64 raster timing now matches VICE. The CPU saw each raster line change two cycles early and colour writes reached the screen two cycles late; the two errors cancelled for raster-IRQ code and hid each other. Fixed together, VICE’s
screenpos,videomode,sequencer-bug,gfxfetch,spritecrunch,spritedmaandgreydottest programs now match exactly, andvicii_timingrises from 84.7% to 96.8%. The 8565 grey dot itself follows separately (#796) - Game Boy mid-scanline effects. The PPU’s pixel pipeline is modelled per dot, so writes to SCX, SCY, the palettes and LCDC land on the right pixel; Mealybug’s DMG tests go from 1 of 24 to 15 of 24 (#316)
Changed
- Snapshots saved by earlier versions of the C64, BBC Micro, Game Boy, ColecoVision, MSX, SG-1000, Sord M5, SV-328, Memotech MTX and Tatung Einstein no longer load, because those machines now save more state
- Built with Rust 1.99
Fixed
- (c64) The light pen is wired to CIA 1, as programs such as VICE’s
spritefetchbugexpect - (c64) Sprites re-match on lines 306–311 and
$D011/$D017writes take effect on the cycle the CPU writes them - (sid) The 8580’s combined waveforms come from sampled chip tables instead of a bitwise AND, and noise output reads the die-photo register taps (#769)
- (sid) Combining noise with another waveform writes zeros back into the noise register, which locks it until TEST refills it, as on real chips (#769)
- (bbc) The hardware cursor is drawn, at its real width in each mode and in MODE 7, and flashing colours flash (#384)
- (bbc) The 6845 cursor blinks every 16 or 32 fields; it blinked at half speed
- (bbc) The Video ULA, ROM select and VIAs answer across their whole SHEILA blocks, not only at their first address
- (game-boy) A game that skips the boot ROM finds the Nintendo logo and ® tile in VRAM, as the real boot ROM leaves them
- (tms9918) A CPU that writes to VRAM faster than the chip’s access windows allow loses the write, as on hardware; this covers the ColecoVision, MSX, SG-1000, Sord M5, SV-328, Memotech MTX and Einstein (#1145)
[0.27.1] - 2026-10-06
Changed
- The
emu198x-mos-sid-6581crate changes its public API (published separately as 0.8.0):clock_accumulatortakes the chip model,Voicegains state, andwrite_controlis new - Snapshots of the C64, Atari 800XL and Atari 5200 saved by earlier versions no longer load, because those machines now save more state
Fixed
- (spectrum) Loading a
.snasnapshot runs from the snapshot’s PC, whatever the CPU was doing before; an instruction left in flight could jump to$0000, and a halted CPU stayed halted (#1564) - (bbc) An access to a 1 MHz device costs 2 or 3 CPU cycles depending on the 1 MHz clock’s phase, as the Advanced User Guide describes (#385)
- (bbc) The VIA timers count at 1 MHz; they ran at double speed, so
TIMEgained 1,996 centiseconds in 10 seconds (#1596) - (bbc) The SN76489 runs at its 4 MHz clock; it ran at half speed and produced half the samples it should (#1599)
- (sid) Pulse output is high at or above the pulse width, not below it, so every pulse voice had its duty cycle inverted
- (sid) Ring modulation folds the triangle when the source voice’s top bit is clear, and sawtooth blocks it (#777)
- (sid) The TEST bit fills the noise register with ones over time instead of resetting it; write-only registers read back the fading bus value; an idle voice holds then fades its last output (#777)
- (antic) A mid-line CHBASE font change takes effect two cycles after the write, and glyphs use only the CHBASE bits the mode decodes (#1412)
[0.27.0] - 2026-10-05
Added
- (dragon) The Dragon 64 has a working 6551 serial port. BASIC’s
PRINT#-2and the ROM’s own serial routines send and receive through a host-side byte queue (#330) - Every machine that makes sound declares
audio-output, and CI checks each declaration against the sound the machine actually emits (#1369)
Changed
- Snapshots of the BBC Micro, Amstrad CPC, Commodore PET, Oric and Dragon saved by earlier versions no longer load, because those machines now save more state
Fixed
- (c64) A program SAVEd to tape loads back with the stock KERNAL; pulses were recorded half a wave out of phase (#1565)
- (bbc) The display is drawn from the 6845’s live outputs: scrolled screens keep their bottom rows, MODE 7 scrolls in order, and MODEs 4–6 run at 50 Hz instead of 100 (#163, #164)
- (bbc) MODE 7 colour codes such as
CHR$129colour teletext text instead of showing as spaces - (oric) HIRES displays at all, its text window sits below the bitmap, and serial attributes 24–31 switch between 50 Hz and 60 Hz frames (#341)
- The Scorpion ZS-256 boots to its menu (#51)
- (spectrum) The 48K keeps every capability its profile declares; the live machine dropped
basic-program-load,memory-watch,port-io,tape-autoloadandvariant-switch
[0.26.1] - 2026-10-05
Added
--no-audioruns any desktop emulator without opening an audio device
Fixed
- Desktop emulators no longer exit when the host has no audio output device; they warn once and run silent at full speed (#1576)
- Choosing a tape on a Spectrum that has already run now restarts it and loads the tape, in the browser player and through MCP and scripts (#1569)
- (nes) A gated triangle holds its current step instead of dropping to 0 (#1567)
- (nes) Audio is emitted at a true 48 kHz; it ran about 1.9% sharp on NTSC and 1% on PAL (#1568)
- The GPU presenter draws on a browser canvas, and falls back to WebGL2 when WebGPU has no adapter (#1436)
[0.26.0] - 2026-10-05
Highlights
- Signed and notarised macOS builds. Every macOS emulator is signed with a Developer ID and notarised by Apple, so a downloaded build opens without a Gatekeeper warning.
- An application icon on every desktop emulator: the 198x stacked tile in
the window, the Windows taskbar and
.exe, the macOS Dock, and a PNG in the Linux archives. - A shared browser player that needs no firmware from the reader. The ZX Spectrum 48K starts on the genuine ROM, included with Amstrad’s permission.
- Z80 and Spectrum timing checked against die-derived traces: bus strobes, contention, interrupts and the floating bus.
- Atari 8-bit and 5200 video accuracy across ANTIC, GTIA and POKEY.
Added
- Publish browser program loading and assembly inspection APIs
- (web) Add a shared firmware-free browser player
- (web-player) Start the Spectrum 48K without asking readers for firmware
- Give the desktop emulators an application icon
- Sign and notarise the macOS release builds
Fixed
Z80 and ZX Spectrum
- Preserve PC-derived WZ on repeated Z80 output
- Execute complete Z80 IM 0 instruction streams
- Align Z80 memory and I/O strobe edges with die-derived traces
- Preserve 48K I/O contention with corrected Z80 edges
- Preserve CPU edge phase in Spectrum floating-bus reads
- Defer late Z80 NMI edges without losing short pulses
- Sample Z80 IRQ level at the measured deadline
- Reconcile Spectrum bus phases with Z80 IRQ deadline
- Apply page-aware I/O contention lookups on Sinclair 128K
- Select the grey +2 interrupt phase on construction and restore
- (spectrum-web) Refuse any 48K ROM but the genuine image in the npm build
- (spectrum-web) Ship the unmodified 48K ROM as @emu198x/zx-spectrum 0.4.1
Atari 8-bit and 5200
- Retain black GTIA players and missiles in priority selection
- Resolve GTIA priority through colour-enable signals
- Apply GTIA hires luminance and paired PF2 collisions
- Correct GTIA register semantics and snapshot validation
- Retain the full GTIA picture and edge collisions
- Read ANTIC VCOUNT from the live master clock
- Retain Atari GTIA sprite shift data and divider phase
- Align Atari 5200 DMA checks with the current CPU slot
- Preserve Atari WSYNC write cycles and GTIA propagation
- Wrap ANTIC display-list reads and defer jump targets
- Capture Atari phantom PM DMA from the previous bus slot
- (atari) Retain GTIA hires admission across PRIOR transitions
- Align Atari bus service and delayed WSYNC assertion
- Wrap ANTIC playfield DMA within the LMS page
- Clock ANTIC NMI events and retain edges during RDY holds
- Correct POKEY RANDOM sequence and initialisation phase
- Retain ANTIC display instruction and scroll state
- Sample ANTIC vertical scrolling at separate timing slots
- Model ANTIC NMI pulses through the CPU vector window
- Preserve hires graphics and collisions through ANTIC vertical blank
- Clock ANTIC playfield DMA and retain shared line RAM
Other machines
- (vic20) Load each region’s own KERNAL so NTSC frames its picture
- (runtime-nintendo-game-boy) Describe run timestamps as M-cycles
- (runtime-atari-800xl) Describe runtime time as colour clocks
[0.25.0] - 2026-09-09
Added
- Profile Spectrum execution costs by source line
- Preserve Spectrum banks in cycle profiles
- Profile Amstrad Spectrum all-RAM paging
- Profile the Spectrum 16K and Spectrum+
- Profile Pentagon and Scorpion ROM overlays
- Profile Timex HOME and extension memory
- Report exclusive routine execution costs
- Expose Z80 call and return observation events
- Report inclusive routine costs and observed calls
- Compare static cycle ranges with measured execution
[0.24.0] - 2026-09-08
Added
- (shell) Register MCP tools from the machine profile
- (shell) Run load_basic_program and autoload_tape through machine hooks
- (shell) Move the variant catalogue and firmware convention into the runtime
- (c64) Resolve models and ROMs through the runtime catalogue, gaining set_machine
- (zx81) Share the runtime variant catalogue across launch and automation
- (amiga) Distinguish machine configurations and expose NTSC presets
- (zx80) Share configuration selection across launch and automation
- Share Ace and MTX runtime catalogues across launch and automation
- Migrate CPC and Einstein to shared firmware catalogues
- Migrate M5 and SVI-328 to runtime catalogues
- Migrate Atom and Electron to runtime catalogues
- Migrate Oric and Aquarius to runtime catalogues
- Migrate SG-1000 and ColecoVision runtime catalogues
- Migrate Atari 5200 to the runtime firmware catalogue
- Share Sega runtime catalogues across console and handheld
- Migrate BBC Micro to the runtime firmware catalogue
- Migrate MSX regional profiles to the runtime catalogue
- Migrate Atari console regional runtime catalogues
- Migrate PET display profiles to the runtime catalogue
- Migrate VIC-20 regional runtime catalogue
- Migrate Atari 800XL to the runtime catalogue
- Migrate Dragon to the runtime catalogue
- Migrate Game Boy profiles to the runtime catalogue
- Expose NES regions through the runtime catalogue
Fixed
- (c64) Report invalid ROM overrides during snapshot startup
[0.23.0] - 2026-09-07
Changed
- Release archives for Linux and macOS are
.tar.gzinstead of.tar.xz. Anything that downloads an archive by name needs the new extension; this is why the version moves a minor step. Windows stays on.zip. The archives are about half as large again and take seconds rather than minutes to build;tar xfhandles either. - Every Z80 machine drives its CPU through one shared cadence (
common-z80-machine) instead of a hand-rolled loop per machine, the loop nine machines once got wrong. No emulated behaviour changes: each of the thirteen machines was checked byte-for-byte against its previous binary with real firmware, reports and screenshots alike.
[0.22.2] - 2026-09-07
Fixed
- (aquarius) One budgeted frame is one machine frame. The runtime built an NTSC machine while every mode budgeted a PAL frame, so
--frames Nran and reported 2N frames and the window ran at double speed. - (bbc-micro) The window opens on
os.rom, the MOS file the headless modes already used, instead of failing without--mos.
Changed
- Every machine binary runs on one shared launcher, so flags, help layout, exit codes and mode selection are the same across all 30 machines. Usage errors exit 2 and point at
--help. Every flag is accepted in every mode: region, model and firmware flags now reach MCP and windowed mode where some binaries ignored them, and positional cartridge paths work headlessly as well as in the window. - MCP
run_frameson the MTX, Einstein, Oric, Electron, BBC Micro and Dragon uses each machine’s real frame length; the old MCP-only budgets overshot the frame. - Amiga
--disk … --mcpinserts the disk instead of warning that the flag has no effect. - Release archives are built without cross-crate LTO: same binary size and emulation speed within noise, and a fraction of the build time.
[0.22.1] - 2026-09-06
Fixed
- Allow Spectrum keyword chords in host keyboard mode
- Use left Option or Alt for Spectrum keyword chords
[0.22.0] - 2026-09-05
Added
- (web) Load a tape the way the machine does, and not in real time
- Export Spectrum tape recordings from the desktop UI
- Translate host keyboard characters for Spectrum entry
- (web) Let a page ask the machine what it is doing
[0.21.0] - 2026-09-02
Added
- (web) The Spectrum runs in a browser, published as
@emu198x/zx-spectrum. A page creates the machine, hands it bytes and a canvas, and gets a working 48K Spectrum — 89 KB gzipped with the ROM inside it, which is small enough to sit in a lesson page rather than behind a link to somewhere else. Snapshots load from.snaor.z80bytes, keys arrive as physical DOM codes so a reader’s keyboard layout does not change which Spectrum key they press, and audio drains into a Web Audio worklet through a bounded buffer that drops rather than drifts. The pacing runs off elapsed time, not the animation frame: the Spectrum’s 50.08 Hz against a 60 Hz display would otherwise run the machine 20% fast, and a backgrounded tab would bank up an unbounded catch-up - (web)
emu198x-webcarries none of that per system. It is generic overFamilyRuntime, so the next machine to reach the browser brings a thin wrapper rather than a second copy of the shell - (web) The 48K ROM ships inside the published package, under the permission Amstrad granted for the Sinclair ROMs in 1999. That permission covers the 48K, 128K, +2, +2A and +3 firmware and no more — the ZX80, ZX81, Interface 1 and 2, Timex, Pentagon and Scorpion ROMs sit outside it, so a browser build of those machines cannot bundle firmware the way this one does. Recorded in test-rom-policy.md with the conditions the permission carries, and with the caveat that a 26-year-old usenet post is the whole of it
- (video) The wgpu presenter attaches to any surface target rather than only to a window, which is what let the browser share it
Changed
- CI proves the two builds agree. A native run and a headless Chrome run of the same crate must reach the same golden frame hash after the same input, so the browser build cannot quietly drift from the emulator this project tests
[0.20.1] - 2026-09-02
Fixed
- (spectrum) Loading a BASIC program on the 48K works again.
load_basic_programtapped ENTER to clear the copyright banner and then read row 23 immediately, catching the screen after the ROM had cleared the line and before it repainted the cursor, so every run failed with “48K BASIC prompt was not ready” and a blank row. It now waits for the prompt, which unblocks headless screenshot and video capture of any Spectrum BASIC program.
[0.20.0] - 2026-09-02
Added
- (atari) Display list interrupt writes reach the line they were aimed at. ANTIC rendered a whole mode line at the start of the scan line, so a colour, CHBASE or HSCROL write made in the DLI’s horizontal blank showed up one line late and every raster-split display was off by a row. ANTIC now begins the line at cycle 0 but fetches its playfield at the CPU cycle where the playfield’s first colour clock is displayed — 32, 24 or 22 for narrow, normal and wide — so writes before that cycle land on the line and later ones on the next. One sample per line, not a byte-by-byte fetch; writes inside the fetch window still take the whole line. Verified with a DLI timing probe cartridge, checked in and rebuilt by CI, and unchanged output on the DLI-driven titles tried. The 800XL and 5200 share the change
[0.19.1] - 2026-09-02
Fixed
- (atari) GTIA modes 9, 10 and 11 render. PRIOR bits 6-7 selected a mode but the playfield was still drawn as plain mode F; GTIA now pairs ANTIC’s pixels two colour clocks at a time into a nibble and colours each pixel per mode: 16 luminances of the background hue (9), one of nine colour registers with the picture one clock right and its last pixel spilling into the border (10), or 16 hues at the background luminance with nibble 0 black (11). The border and blank lines take the mode too. Checked against a probe cartridge of our own, row by row including the border
- (atari) GTIA has all sixteen luminances. The palette was a 128-entry TIA table that dropped the luminance’s low bit, so mode 9’s sixteen shades came out as eight pairs. The NTSC and PAL tables are now 256 entries generated from atari800’s colour model at its standard preset; every colour moves, mostly darker, as atari800’s black level is 16. The 800XL and 5200 share the change
[0.19.0] - 2026-09-02
Added
- (atari) Bank-switched cartridges on the 800XL: XEGS (32 KB to 1 MB,
8 KB banks at $8000 with the last bank fixed at $A000), MegaCart
(16 KB to 1 MB, 16 KB banks filling $8000-$BFFF) and the OSS one-chip
and two-chip language carts (4 KB banked at $A000, fixed at $B000,
selected by the address of any $D5xx access). A
CARTheader picks the scheme; headerless images load flat up to 16 KB and as XEGS from 32 KB up. Ballblazer and Ace of Aces boot from their TOSEC images..carfiles are recognised inside zips
Fixed
- (atari) ANTIC’s write-only registers read as $FF and NMIST’s unused bits as 1, as on the chip. Ace of Aces enables its interrupts by reading NMIEN back and masking it, which only works when the read returns $FF; it used to hang on a jiffy clock the OS never advanced. A VBI now clears the DLI status bit as it sets its own, and vice versa
[0.18.1] - 2026-09-01
Fixed
- (atari) Run player/missile DMA on every displayed line. ANTIC only fetched P/M data while drawing a mode line, so sprites over blank display-list instructions, below the JVB, or with the display list off never reached GTIA. Enabling player DMA now enables missile DMA with it, as the hardware does. Both fixes reach the 5200 too
- (atari) Test P/M DMA through the machine: an asm198x-built cartridge boots the 800XL with no OS ROM and the framebuffer is checked pixel by pixel for both P/M layouts, players-only DMA, GRACTL off and VDELAY
[0.18.0] - 2026-09-01
Added
- (atari) Read ATR disk images — the
format-atari-8bit-atrcrate parses the header, sector size and density, and hands out sectors by number - (atari) Give POKEY’s serial port a wire: SKCTL, SEROUT, SERIN and the serial interrupts now drive a real bit stream, clocked from the timer that runs it
- (atari) Put a disk drive on the SIO bus — the 800XL answers status, read and write command frames for D1:, so the OS boots a disk
- (atari) Load a disk with
--disk PATHin the GUI, headless script mode and MCP; a.zipholding one.atrworks unpacked. The drive keeps its disk through a hard reset, writes and all, anddisk.loadedreports whether D1: is occupied
Fixed
- (atari) Make
--no-basictake effect with the real XL OS. The OS cold start decides from OPTION whether BASIC is in and writes PORTB itself, so the preset bit was ignored; the machine now holds OPTION for the OS’s first second after a cold start with BASIC off
[0.17.0] - 2026-09-01
Added
- (mtx) Parse .mtx tape images and .RUN programs
- (atari) Honour GRACTL and VDELAY in the render path
- (atari) Latch the triggers when GRACTL asks
- (atari) Give overlapping players their third colour
- (atari) Implement ANTIC fine scrolling — HSCROL and VSCROL now move the playfield, verified against real firmware
- (atari) Steal the DMA cycles ANTIC actually takes, at the fetch positions the hardware uses rather than a count spread across the line
Fixed
- (atari) Draw ANTIC modes 8, 9 and A across the full playfield width — GRAPHICS 3, 4 and 5 were confined to the left quarter or half of the screen
- (atari) Let the 800XL type shifted characters; KBCODE’s Shift and Control
bits were swapped, so every
(,"or?reached the OS as a command - (atari) Stop a full-line ANTIC DMA budget underflowing the stall window
- (atari) Let ANTIC fetch through the machine’s live memory, so a display list or character set changed mid-frame is visible on that frame
- (atari) Read AUDCTL’s bits from the end the register starts at; six of the eight were transposed
- (atari) Clock POKEY’s serial output from the timer that drives it, in place of two hand-tuned delays
[0.16.0] - 2026-09-01
Added
- (aquarius) Give the machine its voice
- (pet) Give the machine its voice
- (pet) Let the PET load a program instead of only typing one
- (jupiter-ace) Load .ace snapshots
Fixed
- (vic-20) Give a $1201 PRG the whole 8K expansion block
- Breaking — (vic-20) Model each RAM expansion cartridge independently
- (aquarius) Move the speaker to the pin the hardware puts it on
[0.15.0] - 2026-09-01
Added
- Plug an ESP-AT modem into the C64 user port
- Teach the modem Hayes dialing so C64-era clients can connect
- Emulate the Ultimate Command Interface network target
- (uci-net) Answer network commands the way the hardware does
[0.14.0] - 2026-08-31
Added
- (atari-800xl) Load XEX executables
[0.13.3] - 2026-08-31
Fixed
- Wire SVI cartridge bank control
[0.13.2] - 2026-08-31
Fixed
- (spectrum) Terminate TZX playback at end of file
- (atari) Implement GTIA PRIOR schemes
[0.13.1] - 2026-08-31
Fixed
- Complete allocation-free SID draining
- Model VIC-20 ESP reconnect state
[0.13.0] - 2026-08-31
Added
- Add allocation-free SID buffer drain
[0.12.2] - 2026-08-31
Fixed
- (atari) Verify audible POKEY output
[0.12.1] - 2026-08-31
Fixed
- (einstein) Remove fictional NTSC configuration
[0.12.0] - 2026-08-30
Added
- Persist Master System cartridge saves
- (vic-20) Emulate ESP-AT baud changes
[0.11.1] - 2026-08-30
Fixed
- Implement Master System cartridge SRAM
[0.11.0] - 2026-08-30
Added
- Add VIC-20 cycle serial adapter
- Bridge VIC-20 ESP-AT to TCP
Fixed
- Integrate Jupiter Ace beeper samples
[0.10.0] - 2026-08-30
Added
- Expose VIC-20 serial user-port pins
Fixed
- (aquarius) Require character ROM firmware
- (master-system) Parse cartridge headers
- (svi-328) Add cassette media path
- (atari-7800) Synthesize TIA audio
[0.9.0] - 2026-08-30
Added
- (oric) Load TAP cassettes through VIA
[0.8.2] - 2026-08-30
Fixed
- Align 128K floating-bus reads with hardware
- Model 6845 zero sync widths by variant
- Implement SMS memory control port
- Expose SMS region through TH readback
- Render Oric serial text attributes
[0.8.1] - 2026-08-30
Fixed
- Identify unsupported Amiga IPF disks
- Expose Einstein disk media through runtime
- Detect MSX MegaROM mappers on media load
- Model the standard MSX M1 wait state
- Preserve distinct ZX80 and ZX81 picture origins
- Select the GTIA palette by television standard
- Correct Atari GTIA and ANTIC edge cases
- Correct POKEY linked-channel dividers
- Implement MARIA Kangaroo transparency mode
- Correct Jupiter Ace expansion map
[0.8.0] - 2026-08-30
Added
- VIC-20 programs now load through the standard media slot, selecting the canonical 3 KiB, unexpanded, or 8 KiB RAM configuration from the PRG load address before starting BASIC.
- VIC-20 generic CRT and raw BLK5 cartridges now map through the standard cartridge slot, persist across resets and snapshots, and cold-start through the real KERNAL probe. Static multi-block CRT images are supported; hardware with bank-switching I/O remains explicitly rejected.
Fixed
- PET 80-column models now clock the CRTC at its real 2 MHz character rate.
- The 6845 cursor now follows its programmed raster range and blink mode.
- Acorn Electron sound now uses the ULA’s correct pitch divider and source mux.
- VIC-20 display fetches now honour the programmable screen, colour-RAM, and character-memory bases across the VIC-I’s 14-bit bus; the existing register-driven geometry and screen origin therefore work with relocated displays as well as the KERNAL defaults.
[0.7.1] - 2026-08-29
Fixed
- (sm83) Make ISA disassembly upgrades reviewable
- (pokey) Correct distortion polynomial gates
- Expose the VIC-20 live raster register
- Route SVI PSG audio to the host
- Correct the Oric AY VIA decode
[0.7.0] - 2026-08-27
Added
- (atari-5200) Load headered dumps and Bounty Bob’s bank switching
- (spectrum) Trace I/O ports on the Spectrum and its clones
- (spectrum) —frames, —screenshot and —audio-capture
- (bbc,electron) Type the shifted legends, so
*and"work - (bbc,electron,dragon) Let a script start and stop the tape
- (keyboard) Type the shifted legends on the Dragon, VIC-20, MSX and SVI-328
- (einstein) Type the shifted legends
- (pet) Let the PET type a subtraction
- (mtx) Let the MTX type its shifted legends
- (aquarius) Let the Aquarius type its shifted legends, and fix its boot test
- (zx80, zx81) Let both Sinclair machines type their shifted legends
- (spectrum) Let the Spectrum type its Symbol Shift legends
- Publish six chip cores as emu198x-* crates
Fixed
- (atari) Pulse ANTIC’s NMI so a DLI stops eating the VBI
- (atari) Report the television standard from GTIA’s PAL register
- (shell) Name the argument an MCP tool does not take
- (shell) MCP mode loads the media named on the command line
- (amiga-adf) Say which container it is, not that the size is wrong
- (nes) One scripted frame is one recorded frame
- (dragon) Play the tape the file holds, not a reconstruction of it
- (dragon) Stop the tape smoke reporting success on a scan it declined
- (electron) Sample the IRQ line every cycle, not once a scanline
- (shell) Refuse a character the machine cannot type, do not count it
- (bbc) Decode the video ULA the way the hardware does
- (shell) Place the layout helper before the test module
- (bbc,electron,oric) Step instructions through the same hardware as a frame
- (pet) Give the PET the rest of its keyboard, and stop cursor-right typing ]
- (oric) Let the Oric type its shifted legends, and tell # from backslash
- (ace) Stop the Jupiter Ace typing the wrong letters
- (zx81) Stop dropping every keystroke after the first
- (m5) Name the M5’s keycaps after what they type, and reach the shifted ones
- (atom) Stop dropping every second keystroke
- (spectrum) Make the Super HALT Invaders test reach a HALT
- (build) Record the test-skip dev-dependency in Cargo.lock
- (build) Give the Einstein dev-dependency a version
- (build) Give the last four path dependencies a version
- (status) Identify shipping machines by their binary, not by prefix
- (docs) Make relative doc references resolve, and keep them resolving
- (docs) Answer “does this link resolve” from git, not from the disk it ran on
- (6845) Wrap the horizontal counter instead of overflowing it
Chore
- Breaking — (atari-5200) Drop the PAL profile — the 5200 shipped NTSC only. The
atari-5200-palmachine profile is removed, and--region palis rejected.--region ntsckeeps working.
[0.6.0] - 2026-08-25
Added
- (amiga) Decode the extended ROM window, and boot AROS with it
- Generate the ZX80 picture with the CPU, not a borrowed ULA
- Give the ZX80’s synthetic firmware a display routine of its own
- Offer the ZX80’s 16 KB RAM pack as a profile
- Parse ZX80 .o images and wire the cassette line to the bus
- Load ZX80 tapes through the ROM’s own cassette loader
- Free-run the ZX80’s line clock and lock it to the sync
- (zx80) Load a cassette from the headless runner
- (ui) Derive pixel aspect from the raster instead of the crop
- (shell) Name the active line counts, and check them against the VIC-II
- (ui) State what the display was, not just how fast the pixels came
- (shell) Put the display on the machine so the audit can see it
- (game-boy) A synthetic cartridge carrying the Emu198x plate
- (nes) A synthetic cartridge, and the plate carries a real colour
- (atari-5200) A synthetic cartridge, and a BIOS that hands over to it
- (atari-7800) A synthetic cartridge, and one zone per scanline
- (atari-2600) A synthetic cartridge drawn by chasing the beam
- (shell) Put the framebuffer extent on the query surface
- (dragon) Take the shared —script session surface
- (zx81) Generate the display from the bus, not from the display file
- (zx80) Put “was a picture generated” on the query surface
- (zx81) Add the 60 Hz board as a selectable variant
- (zx81) Parse .p/.p81 tape images
- (zx81) Load .p images off the cassette line
- (zx81) Fit each board its own RAM, and make the TS1000 real
- (tape) Put tape position on the query surface
- (zx81) WRX hi-res, and fix the tape test that SLOW mode invalidated
- (zx80) Give the board a television-standard strap
- (zx81) Give the machine its voice, which is the display
- (sega-vdp) Magnify sprites when R1 bit 0 is set
- (sega-vdp) Apply the 315-5124’s address-bus mask bits
- (sega-vdp) The 315-5124’s magnification quirk, and Mode 4’s status fill
- (sega-master-system) Ship the early machine as its own model
- (sega-vdp) Mode 4’s 224 and 240-line displays
- (sega-vdp) Give the H counter a value, and port $3F a way to latch it
- (sms) The Light Phaser
- (shell) Absolute aim input, and the Light Phaser on a mouse
- (shell) Say so when a run paints nothing
Fixed
- Measure the ZX80 beam position instead of a zeroed clock
- Report ZX80 paint geometry per frame, not since boot
- Record every frame a machine emits, and don’t mux silence away
- (zx80) Let a cassette sit in the deck without playing
- (zx80, zx81) Stop showing PAL pixels square
- (ui) Re-derive pixel aspect when the machine changes under it
- (spectrum) Stop showing every variant’s pixels square
- (shell) Calibrate the NTSC active line against four published ratios
- (tms9918) Stop showing seven machines’ pixels square
- (sms, nes, c64, 2600) Derive pixel aspect; state it for the handhelds
- (atari, vdg, vic-20) Derive pixel aspect from the chip clocks
- (bbc, electron, cpc, oric, ace, aquarius) Derive pixel aspect
- (amiga) Derive pixel aspect; leave the PET where it belongs
- (zx80, zx81) Show the 288 lines a set shows, not 240
- (amiga) State the display through the variant enum
- (dragon) State the rate the overscan framebuffer actually fills at
- (atari) Size the framebuffer to the region’s field, not to PAL’s
- (vdp) Give every PAL machine the 288 lines its set shows
- (jupiter-ace) Take the 288 lines a PAL set shows
- (acorn-atom) Hold the 288 lines a PAL set shows, not the VDG’s 243
- (2600, cpc) Take the whole field; classify the BBC and Electron
- (vic-20) Four pixels a cycle, not eight
- (aquarius, oric) Draw the border one has, classify the one the other blanks
- (aquarius) Draw the 25 rows the hardware scans, not 24 from the wrong end
- (video) Derive the horizontal border too, and classify every core
- (ui) Open windows at the size the machine will report
- (sega-vdp) Clock the PAL VDP from the Master System’s own master
- (video) Place each picture where its chip scans it, not in the middle
- (gtia) Composite the window, not the normal playfield
- (vic-i) Draw the display where the registers put it
- (sms) Clock a PAL Master System’s Z80 from its own master
- (vic-i) Draw multicolour cells instead of colouring them wrong
- (einstein) The ROM window is the lower 32K, not the low 8K
- (c64) Preserve far-edge VIC-II C-data state
- (z80) Collapse held Spectrum I/O strobes
- (z80) Run one native frame per request
- (amiga) Pace UI at one field per frame
- (amiga) Freeze same-CCK DMA ownership
- (amiga) Keep NTSC floppy at 300 RPM
- (z80) Pin the MTX and Einstein frame budget to the real frame
- (shell) Preserve exact native frame counts
- (jupiter-ace) Run 208 T-states per line, not 207
- (zx81) Report the television strap on port bit 6
- (zx81) Budget the shortest frame, not the field backstop
- (zx81) Report the active line count for the board’s region
- (zx81) Form the pattern address as the ULA does
- (zx80, zx81) Place the picture from the ROM’s pad, not a nominal frame
- (zx81, z80) Let the machine reach SLOW mode at all
- (zx81) Size the window from the board strap, not a constant
- (tms9918) The fifth-sprite flag needs the frame flag clear
- (tms9918) Coincidence covers sprites off the edges of the screen
- (sega-vdp) Honour both of R0’s scroll and column-mask bits
- (sega-vdp) Latch the vertical scroll once a frame
- (sega-vdp) The line counter is checked on the first line of vblank
- (sega-vdp) Drive CRAM through the chip’s measured output levels
- (sega-vdp) A sprite Y near the top of the range hangs off the screen
- (sms) The light gun sees the border, not just the picture
- (atari-5200) Pick the 16 KB cartridge layout from evidence, not size
[0.5.0] - 2026-08-19
Added
- (status) Add the system registry that joins the repo’s four vocabularies
- (status) Collect per-machine test evidence from what actually ran
- (sega) Ship the Game Gear from its own crate
- (status) Make evidence collection the test job, not a second pass
- (status) Restore the status canon as generated pages checked for drift
- (sega) Prove the Master System and Game Gear boot, without a commercial ROM
- Prove the remaining five firmware-free machines boot
- (spectrum) Evidence the Spectrum’s boot on every pull request
- (spectrum) Evidence the whole Sinclair/Amstrad line’s boot in CI
- Prove the six TMS9918 machines start, without their firmware
- (msx) Boot real firmware, using a BIOS nobody needs permission for
- (c64) Boot real firmware, using ROMs nobody needs permission for
- (800xl) Boot real firmware, using an OS nobody needs permission for
- Prove the VIC-20, Atari 5200 and Amiga start, without their firmware
- Prove the Acorn Atom and Oric Atmos start, without their firmware
- Prove the Aquarius and Jupiter Ace start, without their firmware
- Prove the CPC and BBC Micro start, without their firmware
- Prove the Dragon 32 starts, without its firmware
- Prove the Electron and PET start, without their firmware
- Prove the ZX80 and ZX81 start, closing the set at 30 of 30
Fixed
- (sega-vdp) Show the Game Gear’s LCD, not the Master System’s television
- (spectrum) Resolve the provisioned ROM, and stop the goldens passing on nothing
- Record machine-msx’s test-skip dev-dep in the lockfile
- Record the 86 skips that were still passing in silence
- Catch the guard forms the checker walked past
- (zx8x) Take character bitmaps from I, not from ROM $0000
- Record the ZX80/ZX81 test-skip dev-deps in the lockfile
Refactor
- Breaking — Retire support_tier.
MachineProfile::support_tierand theSupportTierenum are removed, and thesession.profile.support_tierquery path no longer resolves. Scripts and MCP clients reading that path will need to drop it; nothing replaces it, because a tier nobody maintained is not information worth preserving. A tier could earn its way back with a stated meaning per rung, a mechanism that moves it, and a consumer that reads it — it had none of the three.
[0.4.0] - 2026-08-18
Added
- Cross-check the Debug198x banked model against real 128K paging
- Make an unreadable space shape reportable, not just survivable
Fixed
- Decline an ambiguous source file rather than guessing which one
[0.3.0] - 2026-08-18
Added
- (cpc) Load games from tape
- (cpc) Add the CPC runtime, so the machine can be driven
- (cpc) Add the CPC frontend, so the machine is runnable
- (nes) Expose OAM and per-scanline sprite counts, so dropout is measurable
- (spectrum) Autoload tape on the 128K family, not just the 48K
- (cpc) Read the screen back as text
- (cpc) Add a 6128 model with the PAL’s banked RAM
- (cpc) Model the Gate Array’s /WAIT stretching
- Read Debug198x sidecars for symbolised debugging
Fixed
- (denise) Repair the dual-playfield priority test, which never armed a playfield
- Make the Atari 800XL MCP tests run and give memory_read its advertised default
- (spectrum) Let a caller pin which ROMs the Spectrum boots
- (nes) Stop the debug PPU trapping when sprite size changes mid-line
- (dragon) Make a missing golden fail instead of passing quietly
- (video) Encode captures at a constant quantiser, not CRF
- (audio) Emit whole frames, so a quiet machine still has an audio track
- (cpc) Fit the 464’s HD6845S, which reads its start address back
- (z80) Restore WZ = PC + 1 on the INIR/INDR repeat path
- (spectrum) Move the 48K floating-bus read origin to 14335
- Breaking — Widen the I/O trace port to the full 16-bit address bus.
emu198x_shell::IoEvent::portis nowu16rather thanu8. Every consumer is in this workspace; machines with their ownu8event type need no change, as the conversion widens. - Correct the Debug198x banked-paging model
Performance
- (debug) Stop re-rendering the framebuffer once per stepped instruction
[0.2.3] - 2026-08-15
Added
- (cpc) Generate interrupts in the Gate Array from the CRTC’s HSync
- (cpc) Boot the CPC464 firmware to its own blue-and-yellow screen
- (cpc) Render the display at the dot clock
- (cpc) Let the CPC be typed at
Fixed
- (6845) Start VSync at the beginning of row R7, not its end
- (bbc) Point the tape test at the UEF that was there all along
- (uef) Default the tape test to the UEF we already vendor
- (cpc) Report VSync on PPI port B, where programs look for it
- (c64) Stop type_string dropping characters, and wire load_basic_program
[0.2.2] - 2026-08-14
Added
- (spectrum) Read .szx snapshots
- (spectrum) Expose the CPU on the query surface
- (cpc) Add the Amstrad Gate Array’s video modes and palette
- (release) Generate the changelog from commits, not from packages
Fixed
- (z80) Hold the M1 opcode strobes to the rising edge of T3
- (z80) Make the M1 refresh strobe a full clock wide
- (z80) Hold /RFSH to the start of the next machine cycle
- (z80) Hold the memory read strobes to the end of T3
- (z80) Present each M-cycle’s address on its own T1 rise
- (z80) Hold the memory write strobes to the end of T3
- (z80) Hold the I/O strobes from T2 fall to the end of the cycle
- (z80) Give the not-taken displacement cycle a read’s pins
- (z80) Stop driving IR during internal cycles
- (ula) Arm the contention gate on the edge that drops /MREQ
- (spectrum) Derive the floating-bus sample instant from the I/O M-cycle
- (ula) Phase-lock the contention window to the ULA’s fetch group
- (ula) Open the contention window at the fetch cycle, not the fetch
- (shell) Refuse a snapshot extension we do not write, and wait for the BASIC prompt
- (spectrum) Charge +2A contention from a measured mask
- (spectrum) Charge each port class the lookups FUSE charges it
- (z80) Sample /INT at the instruction boundary, not a half-cycle early
- (sega) Tick the Z80 twice per T-state, and feed /INT before the tick
- (msx,coleco,svi) Tick the Z80 twice per T-state, and feed /INT before it
- (sord-m5,mtx) Tick the Z80 twice per T-state on the CTC-vectored machines
- (einstein) Tick the Z80 twice per T-state, making the 4 MHz claim true
- (zx80,zx81) Tick the Z80 twice per T-state against a T-state ULA
- (aquarius) Tick the Z80 twice per T-state
- (release) Let every crate’s work reach the suite changelog
- (release) Process every crate so its commits can reach the changelog
- (release) Write the suite changelog from the workspace, not from one machine
The per-system binaries stay at 0.x for now; library crates published to crates.io may hit their own 1.0 on their own schedules.
Unreleased
Fixed
- (amiga) Paula applies period/volume attachment at the correct byte transitions, retains one DMA request during underflow, and loads late-arriving sample words at high-byte entry.
- (amiga) Paula honours short audio periods and treats period zero as 65,536 colour clocks, instead of clamping both to 124. Period writes retain the current sample interval.
- (amiga) Lisa observes programmed blanking edges during horizontal counter resets, preventing missed start/stop events from blanking or exposing most of the next line.
- (amiga) OCS fixed horizontal blanking masks the right-edge tail at Denise’s actual comparator, preserving its level across counter resets.
- (amiga) Lisa retains vertical blanking until the selected horizontal edge, preventing border colour from appearing early on the first visible line in AGA programmed-blanking modes.
Changed
- Amiga saves use version 57 to preserve Paula’s full period countdown along with the existing blanking stages. Version 56 and earlier saves no longer load.
0.2.1 - 2026-08-11
Added
- (spectrum) boot any variant headlessly with —machine
Other
- make the release able to ship binaries again
- Correct Amiga DMA ownership and media persistence
- inherit the Emu198x suite version
- declare the last two silent guards, both in src test modules
- give every fixture guard a voice, across the workspace
- Ship higher-CPU Amiga profiles
0.1.0 — 2026-05-23
Initial public release. Six per-system native verifier shells, each shipping as its own binary for macOS (arm64 + x86_64), Linux x86_64, and Windows x86_64.
What works
- Sinclair ZX Spectrum 48K — real Z80 + ULA-driven machine loop; TAP/TZX loading with autoload and cycle-faithful tape turbo; live beeper + tape audio; real-software regressions including Manic Miner and Jet Set Willy. Other Spectrum variants (16K, 128K, +2, +2A/B, +3) exist as crates and are in active work.
- Commodore 64 — live 6502 / CIA / VIC-II / SID board loop; KERNAL
boots to
READY.; TAP-backed datasette with autoload; host-side.prg,.bas,.d64,.t64import paths; optional live 1541 drive-8 with realD64media insertion (read-only; write path is post-launch). - Nintendo Entertainment System — live 2A03 / 2C02 / APU machine loop;
iNES cartridge loading with 14 mappers (NROM, MMC1, UxROM, CNROM, MMC3,
MMC5, AxROM, Color Dreams, VRC2a, Action 53, BxROM, NINA-001, Sunsoft-4,
Camerica);
nestestpasses 8991/8991. - Commodore Amiga A500 OCS PAL — live board loop over
motorola-68000, Agnus, Denise, Paula, Gary, dual 8520 CIAs, keyboard, DF0 floppy; live Paula audio; Kickstart 1.3 boots; Workbench 1.3 and 2.04 (ECS A500+) desktop. A1200 / AGA work is mid-flight and not yet shipping. - Nintendo Game Boy — live DMG-family CPU / PPU / APU machine loop;
raw/lcd/crtvideo presenter modes; headless cartridge runner with.savbattery-RAM sidecars; Blargg and mooneye-style verification gates. - Dragon 32 — real BASIC ROM boot over
motorola-6809, dual MC6821 PIAs, MC6883 SAM, MC6847 VDG; CAS media, ROM / DGN cartridges, DragonDOS VDK disks (read-only), PC-Dragon PAK snapshots; 11/12 application smoke matches against patched XRoar reference frames.
Verification
- Z80 — 100% Tom Harte, ZEXDOC, ZEXALL pass
- 6502 — 100% Tom Harte
- 68000 — 100% Tom Harte (1,000,058 vectors)
- 627/629 NES ROMs survive 300 frames in the local-archive smoke matrix
- Per-system 10-title catalogue infrastructure (
emu198x-catalogue) covers Spectrum, C64, NES, Amiga via TOML manifests
Modes
Each per-system binary supports three modes:
--ui(default) — native interactive shell withwgpuvideo,cpalaudio,gilrsgamepad input,winitwindowing--script— headless JSON-driven runner for screenshots, snapshots, capture, regression--mcp— JSON-RPC 2.0 MCP server over stdio, for Claude Code / other MCP hosts
Not in this release
Stated honestly upfront so nothing surprises:
- Spectrum variants beyond 48K are work-in-progress
- Amiga A600 / A1200 / A3000 / A4000 / CDTV / CD32 (AGA chipset is mid-flight)
- Game Boy Color, Super Game Boy, link cable
- Dragon 64, CoCo line, DragonDOS write path, OS-9
- NES Famicom Disk System, Zapper, Game Genie, mapper coverage past 14
- C64 cartridge (CRT) support, REU, mouse / paddles, 1541 write path, C128
- Pentagon / Scorpion / Timex Spectrum variants (crates exist, deferred)
- Any system not in the six above (Atari 2600, BBC Micro, MSX, Master System, etc. — these are Wave 2+ per the roadmap)
Documentation
- README — what the project is, how to build, how to obtain ROMs legally, per-system runner examples
- public docs site — system status, MCP integration, capture, scripting, and accuracy progress
CONTRIBUTING.md,CODE_OF_CONDUCT.md,SECURITY.md
Notes
- ROMs are not bundled. The README’s “Getting ROMs” section covers each platform’s legal acquisition path (Cloanto Amiga Forever, Cloanto C64 Forever, World of Spectrum’s Sinclair-permitted set, etc.).
- License is GPL-2.0-or-later workspace-wide.
- Project lives in the 198x family alongside Code Like It’s 198x.