Skip to content

Repository files navigation

NARC — Williams Z-unit for MiSTer FPGA

v1.0 — MiSTer core for NARC rev 7.00 (Williams, 1988). The RTL is derived from the proven Midway Y-unit work in the Smash TV project, with the Z-unit video, ROM layout, memory map, controls, and dual-6809 sound hardware implemented explicitly.

ROM images are not tracked in this repository.

v1.0

The core boots, plays, and has been confirmed on real MiSTer hardware: video, controls, YM2151 music, digitised speech, and the operator menu all work. The build closes timing across all four corners with no violations.

Everything below is the evidence behind that, followed by the limitations that are genuinely still open — read Known limitations before filing a bug.

Fixed for v1.0 and confirmed on hardware:

  • Diagnostic tone after every sound-board reset. The master 6809's NMI was asserted and released on a fixed timeout with no retry, while cpu_nreset still lagged sound_board_rst by the RAM/ROM scrub tail — and the 6809 cannot recognise NMI until its first post-reset LDS retires. Commands landing in that window were dropped, including the tone-suppression writes GSPSND.ASM issues after SNDRES. The assert is now deferred until the CPU is out of reset and settled.
  • Slow, low-pitched speech. The 64-entry direct-mapped sound-ROM cache aliased the 6809 interrupt vectors onto the CVSD handler, so every sample-rate interrupt missed twice and froze the E clock shared by both CPUs and the audio generators. $C000-$FFFF is un-banked on both 6809s and holds the vectors and every handler, so that window is now mirrored into M10K and filled from the ROM download; the cache only ever holds banked sample data. E-clock ratio 0.79 → 0.994, stall duty 0.57%.
  • The design did not fit. It needed 104,291 of 41,910 ALMs because four arrays that ramstyle claimed were block RAM were silently implemented in logic. See Resource strategy.

Current status

Subsystem Evidence
ROM/NVRAM loader Rev-7 hashes match MAME; ROM routing and 32 KiB CMOS restore/upload pass, and save upload pauses at a bus-idle CPU boundary without reset. Region counters rearm per download, so a second load in one FPGA configuration restores correctly
TMS34010 program path Full 512 KiB at 0xffc00000; external fetch matches the MAME PC trace, including the NARC-used signed offset-to-offset MOVE form
Z-unit VRAM/video 262144x13 packed storage with TMS4461-style serial-row scanout, RGB555 palette, source-proven raster geometry, DPYADR line updates, and DPYTAP/ORG scan-mapping tests pass. Scanout consumes zero logical random-port cycles. Horizontal resolution restored 2026-07-30 and confirmed on hardware — the core was scanning out 256 duplicated columns instead of 512, merging small font strokes. Cause was NOT in this core: sys/gamma_corr.sv edge-detects ce_pix, so with our 2-of-3 CE_LEVEL enable it kept one pixel per burst and dropped the other. Fixed by passing .GAMMA(0) to arcade_video; written up for upstream in doc/MISTER_GAMMA_CE_LEVEL_BUG.md
Sound Both 6809E boards boot through shared SDRAM; directed tests and the real-gameplay run exercise YM2151, both DACs, CVSD, slave commands, talkback/SYNC, and render stable nonzero 48 kHz stereo PCM. The host RESET line (SOUND bit 10) is implemented and proven by sim/snd/tb_narc_sound_host_reset.sv. CVSD freeze-gate defect fixed 2026-07-30 and confirmed on hardwarehc55516 evaluated frozen from the new digit instead of the buffered one, so a large explosion sat at the rails for 96% of its duration and left a +30461 DC residue on the mixer bus until board reset. Bit-exact against a MAME golden model over 5734 transitions (sim/snd/tb_hc55516_narc_diff.cpp). Reset-tone and speech-pitch defects fixed 2026-08-04 and confirmed on hardware (see v1.0); NMI delivery is proven by tracing the master into its handler at C345 and on to the slave, which previously never happened. Both 6809 self-tests pass in this core — slave RAM plus all eight slave ROM banks ($A0-$A7) and the master's four ($54-$57) checksum exactly, verified statically against the ROM images — so the tones heard at reset were always the pass dings, never an error code
Controls Cabinet mapping is taken from the rev-7 SWTAB; the coin-door ADVANCE and HIGH SCORE RESET buttons and the AUTO-UP/MANUAL-DOWN service switch are exposed, so the operator menu is reachable
One-stick SDRAM Loader, sound, main/CMOS, program, and six-word GFX clients pass arbitration, refresh, byte-enable, row-crossing, and held-request tests
Full-core Verilator Real-ROM RAMCHECK clears into ROMCHECK and real gameplay; the matched MAME 0.288 replay reaches INPLAY, samples 198 active controls, and produces exactly six matched gunshot pairs with zero illegal/unmapped/memory failures
FPGA build v1.0 full compile 2026-08-04 succeeds with 0 errors at 33,907/41,910 ALMs (81%), 526/553 RAM blocks (95%) and 4,113/4,191 LABs (98%). Multicorner TimeQuest (4 corners) reports zero violations: worst setup +0.083 ns, worst hold +0.040 ns, recovery +4.204, removal +0.399, minimum pulse width +0.398, all TNS zero. SEED 6 is pinned in the .qsf. LABs, not ALMs, are the binding resource at 98% — judge headroom by the LAB figure. Closing the HDMI domain also required returning ascal's pal1_mem and o_linf0..3 to block RAM — see sys/FORK.md; forcing them into MLAB is what made the scaler path fail setup and corrupt pixels on hardware

The current simulation evidence is recorded in doc/VERIFICATION.md. Exact hardware facts and measured resource decisions are in doc/ZUNIT_REFERENCE.md. The inherited Y-unit feasibility history remains in doc/EVALUATION.md.

Hardware being modelled

Item NARC / Williams Z-unit
Main CPU TMS34010, 48 MHz master / 6 MHz effective
Video RAM 512 KiB logical frame buffer, 512x512, 13 live bits per pixel
Display 512x400 visible in a 674x433 raster, 16 MHz pixel stream
Palette 8192 entries, RGB555 (xRRRRRGGGGGBBBBB)
Program 512 KiB populated ROM
Graphics Four 8bpp ROM quarters, 3.75 MiB populated
Sound Two MC6809E CPUs, YM2151, two DAC channels, HC55516 CVSD
Main storage One MiSTer SDRAM module for all ROMs, main RAM and CMOS; frame buffer remains in FPGA block RAM

PCB schematics and recovered PLDs are the hardware authority. Original Williams source and diagnostics are the software-visible authority. MAME's midyunit.cpp, midyunit_m.cpp, midyunit_v.cpp, and williamssound.cpp are regression oracles, not proof of hardware timing. See doc/PCB_TRUTH_PLAN.md for the accuracy boundary.

The original game source under reference/narc-main is the primary oracle for software-visible diagnostic behavior. In particular, DIAG/RAMCHECK.ASM, DIAG/RAMCHIPS.ASM, DIAG/SYSCHECK.ASM, and DIAG/UTILITIE.ASM define the RAM-test address patterns, error branch, pass boundary, and intentional result screen hold used by the full real-SDRAM regression.

Single-SDRAM-module layout

All byte counts below are packed into the one MiSTer SDRAM module as 16-bit words. The ranges are disjoint and fit well inside a standard module:

SDRAM word range Contents Size
0x000000..0x03ffff TMS34010 program ROM 512 KiB
0x400000..0x7fffff Full MAME GFX address space (3.75 MiB populated, remainder zero) 8 MiB
0x800000..0x80ffff Main RAM 128 KiB
0x810000..0x813fff Battery-backed CMOS/NVRAM 32 KiB
0xc00000..0xc8ffff Master and slave sound ROM images (master at +0x000000, slave at +0x048000 words) 1.125 MiB

This table is authoritative against the header comment in rtl/narc_rom_loader.sv; keep the two in step. The ranges above supersede an earlier layout that placed GFX at 0x040000, main RAM at 0x440000 and CMOS at 0x450000. That move cost a real bug worth remembering: the CMOS dirty-detect compare had its base updated to 0x810000 but kept the old limit 0x454000, so it read addr >= 0x810000 && addr < 0x454000 — unsatisfiable for every address. No CMOS write was ever detected and NVRAM never saved. When a region moves, grep for both ends of it.

The controller priority is loader/NVRAM, sound, main/CMOS, program, then GFX. Acknowledgements are stretched across the 96 MHz-to-24 MHz clock ratio so no client can miss a response.

Building

The project revision is Arcade-NARC and targets the DE10-Nano Cyclone V with the standard MiSTer framework. A full release build uses:

quartus_sh --flow compile Arcade-NARC

Quartus performance policy is stored in Arcade-NARC.qsf, so it applies to GUI, command-line, and automated builds: at most six parallel processors, Fast Fit, normal router timing effort, and the unstable physical-synthesis combinational/duplication passes disabled. Smart Recompile is enabled and build databases are retained. Always review the TimeQuest report before treating an RBF as a release — quartus_sta reports slack but does not judge it, and exits 0 on a design that misses timing. tools/check_timing.sh grades the report and is the gate tools/build_and_deploy.sh refuses to deploy past.

Do not leave programming-file compression off. The DE10-Nano's HPS configuration path only accepts compressed Cyclone V bitstreams; an uncompressed RBF assembles cleanly, passes every report, and then silently fails to configure the FPGA on real hardware. A healthy RBF here is ~4.4 MB and varies build to build; a constant size every build is the tell.

SEED 6 is pinned in the .qsf. Seeds 4 and 5 both produced builds that passed every report and then failed on hardware — see Known limitations. Prefer fixing a constraint over sweeping seeds, but be aware this design has a video path that placement alone can break.

TIMEQUEST_MULTICORNER_ANALYSIS is ON and must stay on for any build that is going to be flashed. With it off TimeQuest analyses only the "Slow 1100mV 100C" model, which is the least pessimistic corner for hold; the fast corner that actually catches DE10-Nano hold failures is never run. Every timing figure quoted in this file is the worst across all analysed corners.

sys/ is a fork. Nine MiSTer framework files carry local changes — two of them functional bug fixes the core depends on, the rest block-RAM trades the one-stick fit needs. Read sys/FORK.md before refreshing sys/ from upstream; a plain drop-in silently halves horizontal resolution.

For simulation, fetch the pinned sound dependencies and apply the idempotent MC6809 reset/NMI patch:

git clone https://github.com/cavnex/mc6809.git ext_snd/mc6809
git -C ext_snd/mc6809 checkout 17e94a6ef163be8b79a9b15b2e814847b6062f0f
git clone https://github.com/jotego/jt51.git ext_snd/jt51
git -C ext_snd/jt51 checkout 4a47f666b67b52b9016f390bcfe3255da0128762
python tools/patch_mc6809.py

The native full-core SDL2 simulator is cached and launched through the machine-wide Safe Verilator wrappers:

.\tools\run_narc_visual.ps1 -Detached

It displays the raw 512x400 surface, keeps keyboard input live, reports frame, checksum/change and PC telemetry in the title, and saves a binary full-state checkpoint with F5 or Ctrl+S. Resume with .\tools\run_narc_visual.ps1 -Detached -Restore build\narc_visual\narc_visual.vltsv. Use -RawFrameDirectory <dir> -RawMaxFrames <n> for exact native-boundary ARGB captures and tools/compare_narc_raw_frames.py for a zero-tolerance comparison. The MAME 0.288 raw reference adapter is .\tools\run_mame_narc_raw_adapter.ps1; both sides require python tools\check_narc_diff_preflight.py to pass first.

The shell helpers derive the repository root automatically; MAME_ROMPATH can override the ROM directory used by sim/snd/diff_mame.sh, and TMS34010_UPSTREAM can override the checkout used by tools/run_tb_suite.sh.

Important directed regressions are under:

  • sim/integ/tb_narc_loader.sv
  • sim/integ/tb_narc_boot.sv
  • sim/integ/tb_narc_prog_ext.sv
  • sim/integ/tb_narc_peripherals.sv
  • sim/integ/tb_narc_run_control.sv
  • sim/integ/tb_narc_save_flow.sv
  • sim/integ/tb_narc_inputs.sv
  • sim/integ/tb_narc_deep.sv
  • sim/integ/tb_boot_sdram_hunt.sv (narc_sdram_hunt_verilator.f)
  • sim/cpu_check/tb_move_off_m2m.sv
  • sim/video_check/tb_narc_video.sv
  • sim/video_check/tb_tms34010_dpyadr.sv
  • sim/video_check/tb_tms34010_scan_address.sv
  • sim/vram_check/tb_narc_vram.sv
  • sim/snd/tb_narc_sound_boot.sv
  • sim/snd/tb_narc_sound_extrom.sv
  • sim/snd/tb_narc_sound_audio.sv
  • sim/snd/tb_hc55516_mame.sv
  • sim/sdram_check/tb_sdram.sv

tools/build_narc_roms.py creates simulation-only program, graphics, and sound images from a local MAME set. The MiSTer ROM definition is mra/Narc (rev 7.00).mra.

Resource strategy

Z-unit's 13-bit frame buffer is the dominant on-chip memory. Program ROM, graphics ROM, main RAM, CMOS, and both sound-ROM images use the single external SDRAM module. Both 8 KiB sound work RAMs are split into paired 4 KiB M10K banks. Each 6809's fixed $C000-$FFFF code window is mirrored into its own 16 KiB M10K so vectors and handlers never reach SDRAM. NARC compiles out HQ2x and retains forced/CRT line doubling with two 512x20 buffers, one M10K each. The palette is one physical 8192x16 true-dual-port M10K shared by CPU access and scanout. Both OSD stores use M10K. The measured timing-closed Fast Fit is 33,907 ALMs (81%), 526/553 RAM blocks (95%) and 4,113/4,191 LABs (98%).

LABs are the binding resource, not ALMs. At 98% there is very little room for logic growth even though ALMs read 81%; anything new should go into block RAM rather than logic. A ramstyle attribute is a request, not a guarantee — Quartus can ignore it silently and the only evidence is an Info-level line:

grep "uninferred due to" <build>/quartus.log

Getting this wrong is what put the design 149% over capacity before v1.0: four arrays believed to be block RAM were in registers plus a wide read mux. Quartus 17 also will not infer a multidimensional unpacked array as memory and emits no diagnostic at all — use one flat array per bank.

Known limitations

NVRAM saving is fixed but not hardware-verified. The CMOS dirty-detect compare was unsatisfiable (see the SDRAM layout note above), so high scores and audits never persisted. The fix is in, but it makes a path live that has never executed on hardware: core_pause is driven by nvram_request, so with detection working the CPU now genuinely halts during a save. The always-false compare had been masking the entire save/pause mechanism. If a stall appears at save time, that commit reverts on its own.

A video path depends on placement luck. A build with SEED 5 produced a black game screen on hardware while the game itself ran perfectly — attract audio, coin-up and gameplay all working, and the boot colour from rgb_boot displaying correctly, so the scaler, HDMI and video timing were all alive and only the core's pixel data was dark. Every report was clean: zero negative slack in all four corners, and warnings and global-clock promotions identical to a working build. Refitting the identical RTL at SEED 6 fixed it. Some crossing feeding the pixel path is therefore cut by the framework SDC and holds up only by placement proximity. Verify video on hardware after any build that materially shifts placement — a clean timing report does not cover this. Root-causing the actual crossing and constraining it properly is the top follow-up.

Input latency is inherent, not a defect. Z-unit renders into a frame buffer and samples inputs once per ~55 Hz frame, so the displayed frame is the previously drawn one — the same as original hardware. A build predating all v1.0 work measures identically, so this is not a regression. On the MiSTer side vsync_adjust=2 is the setting that matters; beyond that the remaining variable is the display's own processing of an unusual 55 Hz signal.

This is a ~24 kHz medium-resolution game. The core outputs 512x400 visible in a 674x433 raster at a 16.108 MHz pixel clock — 23.90 kHz horizontal, 55.19 Hz vertical, matching the original medium-resolution cabinet monitor. It is not a 15 kHz game. A 15.7 kHz CRT can carry roughly 284 total lines at this refresh against the 433 required, so driving one needs either a downscale to ~240p (discarding half the vertical resolution) or 480i (which preserves all 400 lines but adds interlace flicker). Both are MiSTer scaler configuration and need no core changes. A tri-sync or multisync monitor that accepts 24 kHz takes the core's native output directly, with rtl/video/crt_adjust.sv available in the OSD for geometry alignment.

Credits

  • TMS34010 CPU: birdybro/TMS34010_sv, MIT, © Kevin Coleman
  • YM2151: Jotego JT51
  • Hardware and ROM reference: the MAME project
  • MiSTer framework and reusable arcade support modules: their respective authors

About

No description, website, or topics provided.

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages