This is the cold-start brief for coding agents working on MegaZXNext, the port of the ZX Next MiSTer core (ZXNext_MiSTer) to the MEGA65, built on the MiSTer2MEGA65 (M2M) framework. Naming: the System/Next licence forbids calling systems built from the ZX Next VHDL "ZX Spectrum Next". Our core is "MegaZXNext" / "a ZX Next core for the MEGA65"; never name it "ZX Spectrum Next" in screens, docs or releases (describing the original machine is fine). Read it first: it covers what this repo is, how it is laid out, where it deviates from the stock M2M template, how to build and simulate it, and the gotchas. Spend your context on the task, not on rediscovering the layout.
This file describes this core, not the M2M framework. For framework internals use the upstream guide: https://github.com/sy2002/MiSTer2MEGA65/blob/develop/AGENTS.md and The Ultimate MiSTer2MEGA65 Porting Guide. Where this file and the source disagree, trust the source and fix this file.
- ZX Spectrum Next: an FPGA-based, enhanced ZX Spectrum (Z80N CPU at 3.5/7/14/28 MHz,
Layer 2 / tilemap / sprites, AY×3, DivMMC, Multiface, 2 MB RAM). It boots its firmware
(
TBBLUE.FW,TBBLUE.TBU), ROMs and NextZXOS from its own SD card, which on the MEGA65 (as on MiSTer) is an image file:/zxnext/boot.vhdor/zxnext/boot.imgon the MEGA65's SD card. Core version 3.02.04 (since V1.3; V1.2 and older: 3.02.00, which matches System/Next 24.11). 3.02.04 boots 26.11 (FW 1.46) and 24.11 on the R6. System/Next 26.11 ships core 3.03.00, whose source is not public. - MiSTer core: MiSTer-devel/ZXNext_MISTer,
pinned as the git submodule
CORE/ZXNext_MiSTer(commit7daebd1). Original ZX Next RTL by Alvin Albrecht, Fabio Belavenuto, Victor Trucco et al.; MiSTer port by Sorgelig. - Official ZX Next core: SpectrumNext/ZX_Spectrum_Next_FPGA,
submodule
CORE/ZX_Spectrum_Next_FPGA(branch30204, commitdc8d764, "CORE VERSION 3.02.04"). The MiSTer RTL is upstream60e8343(Sept 2023) plus MiSTer changes;CORE/vhdl/zxnext/zxnext.vhdis the three-way merge of the two (§4). - MEGA65 port: Chris Freeman, 2026, GPL v3. Repo: https://github.com/freemchr/MegaZXNext
- Framework: M2M V2.0.1 (
M2M/tree identical to upstream tagV2.0.1except the files listed in §4: board tops, OSM palette, colour escape), QNICE submodule at2eb27dd(V2.0.1's pin). - Status: R3, R4, R5 and R6 synthesize with Vivado 2026.1 and meet timing (R3: 1 MB, §1). V1.3 is released (V1.2 + core 3.02.04,
System/Next 26.11 boots; OSD in Spectrum colours). V1.3's core changes tested on the R6 with a test build;
the released V1.3 bitstreams were not run on hardware before release. V1.2 was tested on R6 and R3/R3A;
R4/R5 builds included, untested; settings file tested on R6. On R6: NextZXOS boots from
/zxnext/boot.img; its menus, 48K BASIC, the Browser, .TAP loading and a Kempston joystick work. The V0.2 grey screen after resets was reported gone with the V0.3 build (SDRAM read hold timing fix, §3.2). Everything else is unverified until someone runs it on a MEGA65. The maintainer can only test on R6; R4/R5 are untested and need outside testers before they are called working.
The ZX Next's 2 MB of RAM live in the MEGA65's SDRAM, which only exists on R4, R5, R6.
The R3 / R3A has no SDRAM: there the ZX Next gets 1 MB of block RAM (bram_zxn.vhd), the
memory of an unexpanded ZX Next (NextZXOS reports 768K). main.vhd's generic G_RAM_BRAM
(set by mega65.vhd from G_BOARD = "MEGA65_R3") chooses. The R3 build meets timing and was
reported working on an R3 by a tester (the maintainer has no R3): HDMI/VGA modes, scanlines,
image and real-card boot (back slot), Reset ZX Next, joysticks, many games; 2 MB software (e.g.
RogueCraft DX) returns to the NextZXOS menu, as expected. The same tester later reported V1.2 working on an R3A (incl. mouse and F13).
Tested on an R6: an R6 build with
G_RAM_BRAM forced to true (bram_zxn instead of the SDRAM) boots NextZXOS, .MEM shows 768K and
Warhawk Next (Layer 2) runs. 1 MB of block RAM uses
256 of the 365 RAMB36: the R3 build is at 327.5 (V1.3; V1.2: 316).
/
├── AGENTS.md CLAUDE.md this guide (CLAUDE.md imports it)
├── README.md user documentation: installation, keys, known issues
├── AUTHORS credits for everything the core uses
├── VERSIONS.md this core's changelog (M2M's own is upstream)
├── M2M/ the MiSTer2MEGA65 framework, V2.0.1. Read-only EXCEPT the
│ files listed in §4 (board tops, OSM colours). M2M/QNICE = QNICE submodule.
├── CORE/
│ ├── ZXNext_MiSTer/ ← submodule, UNMODIFIED. All RTL is compiled from here (library "zxnext")
│ │ except the files that CORE/vhdl/zxnext/ replaces.
│ ├── vhdl/
│ │ ├── mega65.vhd entity MEGA65_Core: framework↔core contract + SD slot mux + menu decode
│ │ ├── main.vhd core clock domain: zxnext_top_m65 + sdram_zxn + rtc + audio/video glue
│ │ ├── clk.vhd MMCM: 28 / 28∠180° / 14 / 7 / 112 MHz
│ │ ├── globals.vhd CORE_CLK_SPEED = 28 MHz, VGA_DX/DY = 720×576, no vdrives, no ROMs
│ │ ├── config.vhd OSM menu (30 lines), welcome screen, 2 help pages
│ │ ├── keyboard.vhd MEGA65 keyboard → ZX Next 8×7 membrane matrix
│ │ ├── sdram_zxn.vhd SDRAM controller (VHDL translation of MiSTer rtl/mister/sdram.sv)
│ │ ├── bram_zxn.vhd R3: 1 MB of ZX Next RAM in block RAM (no SDRAM on the R3)
│ │ ├── sd_image.vhd the ZX Next's SD card = image file on the MEGA65 card (sector engine)
│ │ ├── sd_card_zxn.vhd virtual SPI SD card (VHDL translation of MiSTer sys/sd_card.sv)
│ │ ├── zxn_dc_blocker.vhd audio DC blocker
│ │ ├── amiga_mouse.vhd Amiga mouse in a joystick port → Kempston mouse
│ │ └── zxnext/ replacements for MiSTer files, compiled into library "zxnext":
│ │ ├── zxnext_top_m65.vhd derived from rtl/mister/zxnext_top.vhd (MEGA65 top of the ZX Next)
│ │ ├── bram_xilinx.vhd replaces rtl/mister/bram.vhd (Altera altsyncram/altdpram → inferred RAM)
│ │ └── t80n.vhd patched copy of rtl/cpu/t80n.vhd (vector-length fix Vivado needs)
│ ├── m2m-rom/ QNICE Shell ROM (m2m-rom.asm: PREP_START mounts the SD card image;
│ │ zxn_mount.asm image/card, zxn_cfg.asm settings file)
│ ├── sim/ GHDL testbenches: sdram_zxn (+ MJoergen's SDRAM model, MIT), sd_image
│ ├── CORE.xdc core clocks, CPU BUFGMUX clocks, multicycles, SD pins (all boards)
│ ├── CORE-SDRAM.xdc SDRAM multicycles, IOB packing, I/O timing (R4/R5/R6 .xpr only)
│ └── CORE-R{3,4,5,6}.xpr Vivado 2026.1 projects (same sources, different top/XDC/G_BOARD)
├── CORE/font/MegaZXNext-16x16.rom OSD font: M2M font + the badge characters (tools/osd_logo.py)
├── release/zxnext/zxncfg empty settings file (31 × 0xFF) for /zxnext/zxncfg on the SD card
├── tools/osd_logo.py badge on the OSD welcome screen: font characters + SCR_LOGO in config.vhd
├── tools/bootscreen/ patches TBBLUE.FW: MegaZXNext logo instead of "MiSTer Next" (Python)
└── doc/
├── m2m/ M2M conventions; exceptions.md = how to re-apply our changes on updates
├── m2m-v2.1.0-upgrade.md plan for moving M2M/ to V2.1.0
├── porting-guide-audit.md audit against the M2M porting guide: open gaps
└── temp/ M2M framework scratch notes, NOT about this core
M2M/vhdl/top_mega65-rX.vhd (PATCHED: R4/R5/R6 SDRAM to CORE; R3-R6 SD outputs via CORE)
├── i_framework : framework.vhd stock M2M; QNICE has both SD slots until the image is mounted
└── CORE : MEGA65_Core (CORE/vhdl/mega65.vhd)
├── clk_gen : clk.vhd MMCM 100 MHz → 700 MHz VCO → 112 / 28 / 28∠180 / 14 / 7 MHz
├── C_DEV_ZXN_IMG registers image position from QNICE (cdc_stable → main_clk)
├── SD slot mux slot with the image: core; otherwise the framework
├── i_main : main.vhd 28 MHz domain
│ ├── i_zxnext_top : zxnext_top_m65 (library zxnext) → the MiSTer ZX Next RTL
│ ├── i_sd_image : sd_image SD0 = virtual card (sd_card_zxn) served from the image
│ ├── gen_sdram.i_sdram : sdram_zxn 112 MHz, 2 MB, ports A (CPU/DMA) and B (Layer 2)
│ │ (R3: gen_bram.i_bram : bram_zxn, 28 MHz, 1 MB)
│ ├── rtc (MiSTer) DS1307 emulation fed by M2M's main_rtc_i
│ ├── keyboard.vhd membrane matrix
│ └── zxn_dc_blocker audio
└── menu decode qnice_* AV-mode outputs from C_MENU_* bits
- One
MMCME2_ADV: VCO = 100 MHz / 5 × 35 = 700 MHz. Outputs: CLKOUT0 = 112 MHz SDRAM (fractional 6.25), CLKOUT1 = 28 MHz main, CLKOUT2 = 28 MHz at 180°, CLKOUT3 = 14 MHz, CLKOUT4 = 7 MHz. All are phase aligned;sdram_zxnrelies on 112 = 4 × 28 being phase aligned. - A
PLLE2_BASE(i_pll_sdram) makessdram_out_clk: the 112 MHz clock, 56.25° (1.40 ns) late. It only clocks the ODDR of the SDRAM clock pin (§3.2). The MMCM can't: only CLKOUT0 is fractional. CORE_CLK_SPEEDinglobals.vhd= 28_000_000 and must match CLKOUT1.- CPU clock: the ZX Next derives a register-driven CPU clock (
CLK_i0, 3.5/7/14 MHz) and switches to 28 MHz via aBUFGMUX_CTRL(zxnext_top_m65.vhd; MiSTer used a fabric clock mux).CORE.xdcdeclarescpu_clk_i0,cpu_clk_div,cpu_clk_28as generated clocks, withcpu_clk_div/cpu_clk_28physically exclusive. Multicycle paths mirror MiSTer'sZXNext.sdc. - CPU speed changes: the ZX Next sizes a memory cycle by its new speed at once
(
memcycle_count_max, the 28 MHz wait state), but the cycle that straddles the switch began at the old speed. MiSTer's mux (and ours at first) could end that opcode fetch before its memory request went out: on R6 NextZXOS lost anINC HLafter itsOUTto nextreg 0x07, the ULA clip window got garbage, 48K BASIC and the Browser showed a black screen.zxnext_top_m65.vhdtherefore switches the BUFGMUX fromzxn_cpu_speed(not the delayedzxn_cpu_speed2) and keepsCLK_i0from rising for 15 CLK_28 cycles after a speed change.make speedinCORE/simtests it (48 speed drops at different phases); run it after touching the CPU clock. These XDC lines name hierarchical cells (CORE/i_main/i_zxnext_top/...,CORE/i_main/gen_sdram.i_sdram/*inCORE-SDRAM.xdc); renaming an instance silently drops the constraint. Check the Vivado critical warnings after any rename. - The video clock handed to M2M is the 28 MHz main clock: the pixel rate is 14 MHz, so
video_ce_ois a 14 MHz enable. VGA_DX/DY = 720×576 is the scandoubled size. hr_high_i/hr_low_i(flicker-free HDMI) are not used: the core clock is fixed.
- R3 / R3A:
bram_zxnis 1 MB of block RAM clocked by CLK_28, which is what the ZX Next was designed for (an SRAM, "transactions complete after one cycle"): port A writes or reads on the rising edge whereram_a_req= '1' and holds the read data; no wait states. Port B reads every cycle (Layer 2 latches 4 cycles after it changes the address). Above 1 MB reads 0xFF and writes are ignored: a mirror would pass NextZXOS's RAM test and corrupt memory later.make speed_bramruns the CPU speed test against it (passes up to 34 ns of data delay; real paths have ~2 ns slack at 17.9 ns). The rest of this section is about the SDRAM (R4/R5/R6). - The 2 MB of ZX Next RAM live in the SDRAM (IS42S16320F-6BL). The core does not use
HyperRAM (
C_HMAP_M2Monly). HyperRAM is still used by M2M for the HDMI scaler. - Read the file header first. Key points: 112 MHz, CL3, tRCD 3, an ACTIVE every 8 cycles,
IOB-registered outputs and DQ input, SDRAM clock = inverted
sdram_out_clkvia ODDR (MJoergen's MEGA65 I/O scheme; he runs it at 166 MHz). Port A (CPU/DMA) has a wait signal. - SDRAM I/O timing is constrained in
CORE-SDRAM.xdc(datasheet tAC/tOH/tIS/tIH, read data sampled CL+1 cycles after the READ). With the plain inverted controller clock (V0.2) the read hold slack was -0.9 ns: at 112 MHz the sample sat just after the data valid window. The 1.4 ns ofsdram_out_clkcentre it. After a build, check thesdram_pin_clkpaths; if you change the clock frequency, CAS latency or capture cycle, re-tuneCLKOUT0_PHASEof the PLL. Port B (Layer 2 fetch) has no wait: the controller learns the Layer 2 request phase and keeps the two cycles before it free. CPU writes invalidate a matching Port B cache line. - Port A is a 56 MHz interface: it is evaluated, and
ram_a_wait/ram_a_dochange, only on the 112 MHz edges that coincide with an edge of CLK_28 (clk_28_iis the phase reference). The inputs come from the ZX Next's combinational memory decode, launched by either CLK_28/CPU edge, and are garbage for up to 2 cycles in between. This is what the CPU↔sdram_clkmulticycles inCORE.xdcassume; keep the two in sync. - Port A must accept a request while it is busy. At 28 MHz the Z80N cannot be stalled by its
own write (WR goes low in T2, when WAIT is sampled), so the next request (e.g. the opcode fetch
after a
CALL's push) can arrive before the write is served.sdram_zxnqueues one such request (a_queued,a_q_*) and keepsram_a_waithigh until it is served. Without it the read returned the written byte and TBBLUE.FW crashed (found on R6 hardware). Test 9 intb_sdram_zxn.vhdcovers it. - DQ is split into
dq_out/dq_oe_n/dq_in. The tri-states are in the patched board tops. - Simulate before touching it:
cd CORE/sim && make(GHDL,--std=08). The testbench uses MJoergen's SDRAM model and checks with--assert-level=error.
- The ZX Next talks SPI to its SD card. As on MiSTer, that card is an image file: SD0 is a
virtual SD card (
sd_card_zxn.vhd, a VHDL translation of MiSTer'ssys/sd_card.sv) whose sectors are served from/zxnext/boot.vhd(or/zxnext/boot.img) on the MEGA65's SD card. SD1 has no card. A MiSTerboot.vhd(fixed VHD = raw image + 512-byte footer) works as is. - Startup: the Shell's
PREP_STARTcallsZXN_MOUNT(CORE/m2m-rom/zxn_mount.asm): it mounts the card, opens the image and walks its FAT chain. Every piece of the file (extent: first virtual sector, first physical sector) goes into the core's 512-entry extent table; then size, extent count and slot go to deviceC_DEV_ZXN_IMG(globals.vhd; register map inmega65.vhd), "mounted" last. Fragmented files are fine (up to 512 pieces). Errors (no card, no image, more than 512 pieces, broken chain) are fatal Shell errors with instructions. The serial log showsZX Next: image at sector 0x…, sectors 0x…, extents 0x…, slot …. - QNICE assembly:
ADD/ADDC(andSUB/SUBC) pairs must be adjacent: anyADDin between, even one that only forms a pointer, changes the carry flag (this bug once put the image 32 MB too early). Test:cd CORE/m2m-rom/test && ./run_test.sh(QNICE emulator, FAT32 test images: carry case, 3 extents, >512 extents, boot.vhd, no image, real card; settings file: saved, card not lent, real card, no file). Needs mtools and dosfstools. The emulator'sRUNtakes decimal unless prefixed0x. - Ownership: until "mounted", QNICE has both slots and the ZX Next is held in a hard reset
(
main.vhd). From then on the core drives the slot that holds the image (mux inmega65.vhd, framework outputs passed through the CORE by the patched board tops) and QNICE must not touch the SD card any more: no file browser, no vdrives. The one exception is the settings file (next bullet). - Direct mode (real ZX Next SD card): if neither image exists but
/TBBLUE.FWdoes,ZXN_MOUNTsets register 8 ofC_DEV_ZXN_IMG(direct) instead of an extent table.main.vhdthen disablessd_imageand wires the ZX Next's SPI (sd_cs0_n, SCK, MOSI, MISO) straight to the slot, as on a real Next (SCK = CPU clock / 2, up to 14 MHz). FAT32 only (QNICE has to read the card first). QNICE test case 5 covers the detection. Reported working in the back slot (R3, R6) and, after the fix below, in the bottom tray too. The earlier "does not boot in the tray" report was the card: FAT32 inside, but MBR partition type0x07(left over from exFAT). QNICE'sf32_mnt_sdaccepts only0x0B/0x0C, so the Shell says "Cannot mount the SD card" before it looks for/TBBLUE.FW; Windows reads such a card fine. Fixed by setting the type to0x0C(Set-Partition -MbrType 12), no reformat. README tells users to format ZX Next cards with the SD Card Formatter (sdcard.org) only, and never to reformat the MEGA65 card (hidden system partition). Possible improvement: show the partition type in the "Cannot mount" error. - Settings file (
zxn_cfg.asm,SAVE_SETTINGS = true,CFG_FILE = /zxnext/zxncfg, 31 bytes, an empty one isrelease/zxnext/zxncfg): the Shell loads it at startup as usual.ZXN_CFG_INIT(PREP_START) then takes the file handle away from the Shell (CONFIG_FILE= 0), so M2M'sROSM_SAVEnever writes while the core owns the card.ZXN_CFG_SAVE(OSM_SEL_POST, only ifM2M$CFM_DATAchanged) borrows the card: register 9 ofC_DEV_ZXN_IMG= 1,sd_imagefinishes its sector, raisespaused_oand holds the CPU (cpu_wait_o), the slot mux inmega65.vhdgives the slot to the framework, the Shell rewrites the file's sector (read-modify-write through the FAT32 library), then register 9 = 0. Timeout 3 s (then not saved). Not in direct mode: the core cannot stop the ZX Next between its own SD commands. Both sims cover it (make sd: pause during a read and a write;run_test.sh). Works on the R6. - Which slot: M2M's
sdmuxuses the back slot if a card is in it, otherwise the bottom tray;ZXN_MOUNTreadsM2M$CSR_SD_ACTIVEand the core then drives that slot. - Sector engine (
sd_image.vhd): virtual sector N is found in the extent table (linear search, a few µs), then read/written with M2M'ssd_controller(sd_spi.vhd, the Shell's own SD controller) at 28 MHz (14 MHz SPI). Sectors beyond the image read as zeros and are not written. Three tries per sector, then the sector is acknowledged anyway and counted (register 6 ofC_DEV_ZXN_IMG). - CPU wait: the Z80N (and DMA) waits while a write is stored (as MiSTer), and while a
prefetched sector that nobody wants any more is still in flight. The latter is not in MiSTer: it
closes a race in
sd_card.sv(CMD12 during a CMD18 prefetch, then CMD17/24) that corrupts writes when the card is slow.tb_sd_image.vhdreproduces it. - Simulate before touching it:
cd CORE/sim && make sd. It uses the ZX Next's realspi_master.vhdand a behavioural SDHC card (CRC16, read latency, injected CRC errors). CORE.xdcsets the SD pins as false paths (SPI is slow and source-synchronous).
keyboard.vhdheader has the complete MEGA65 → ZX Next mapping table. Function keys: F3 = 50/60 Hz, F9 = Multiface NMI, F11 = DivMMC NMI, F13 = cycle CPU speed. Help = OSM.- No PS/2 keyboard: the membrane matrix is driven directly.
- Mouse: an Amiga mouse in either joystick port is the Kempston mouse (
amiga_mouse.vhd, two instances inmain.vhd, port 1 first). It reads the raw joystick pins (patched board tops, §4), not M2M's debounced lines (1 ms would lose fast movement), with a 1 µs glitch filter. Detected when up and down (or left and right) are low at once, which a joystick cannot do; from then on that port is no Kempston joystick until a ZX Next hard reset (long reset press). Quadrature steps are counted at full resolution; nextreg 0x0A DPI picks the 8 bits the ZX Next sees (default /2), bit 3 swaps the buttons. Left button = fire; right/middle = POT X/Y differing by more than 64 from their value at detection.make mouseinCORE/simtests it;make mouse_regchecks that nextreg 0x0A bit 3 (not 2) reachesmain.vhdas the swap. Works on the R6 (Amiga mouse in port 1: both axes, left and right button) and an R3A (MouSTer adapter in Amiga mode, port 1: movement, all three buttons); port 2 untested. - RTC: MiSTer's
rtc(DS1307 over I²C) fed frommain_rtc_i. - Machine ID
0xDA("MiSTer": no flash, no bootstrap, no Pi), board issue 0 (main.vhd). - Power LED green (blue during M2M reset); drive LED yellow = SD activity.
cart_en_o <= '1'is the required R5/R6 joystick-port-2 workaround. Keep it.
- 31 lines (
OPTM_SIZE = 31): HDMI submenu (lines 2–13), VGA submenu (14–21), Flip joysticks (23), HDMI CRT emulation (24), Audio improvements (25), Reset ZX Next (26), About & Help (28), Close (30). Main menu shows 13 lines withOPTM_DY = 13. - Reset ZX Next is an action, not a setting:
main.vhdmakes a soft reset on the rising edge of its bit, and the Shell'sOSM_SEL_POST(m2m-rom.asm) unselects the item again. It can't use the CSR reset: the board tops OR that intomain_reset_m2m, i.e. the ZX Next hard reset. - There is no HDMI Zoom-in item: M2M's
crop.vhdis hard-coded for the C64 picture (320x200 behind 33/35 pixel borders), soqnice_zoom_crop_ois tied to '0'. A ZX Next zoom would need its own crop in the core. - Bit N of the OSM control vector = menu line N =
C_MENU_*inmega65.vhd. Inserting or deleting a line silently shifts every later index. Re-syncC_MENU_*and keepOPTM_SIZE= number of lines inOPTM_ITEMS= number of entries inOPTM_GROUPS. cpu_speed_sw_ionmainis tied to'0': there is no CPU speed menu item (use F13).
MiSTer core (CORE/ZXNext_MiSTer is unmodified; replacements live in CORE/vhdl/):
| Upstream file | Replacement | Why |
|---|---|---|
rtl/mister/zxnext_top.vhd |
CORE/vhdl/zxnext/zxnext_top_m65.vhd |
No PS/2; membrane driven directly; BUFGMUX_CTRL CPU clock with a settle time after speed changes; real 180° clock |
rtl/mister/bram.vhd |
CORE/vhdl/zxnext/bram_xilinx.vhd |
Altera altsyncram/altdpram → inferable VHDL (same entities/ports). .mif init not supported |
rtl/cpu/t80n.vhd |
CORE/vhdl/zxnext/t80n.vhd |
9-bit vs 4-bit mask length mismatch (x"7" → "000000111") |
rtl/mister/sdram.sv |
CORE/vhdl/sdram_zxn.vhd |
VHDL translation for the MEGA65 SDRAM at 112 MHz (see §3.2) |
sys/sd_card.sv |
CORE/vhdl/sd_card_zxn.vhd |
VHDL translation (SDHC, 8-bit buffer, zxnext.dpram), plus idle_o for the CPU wait (see §3.3) |
rtl/zxnext.vhd |
CORE/vhdl/zxnext/zxnext.vhd |
Core 3.02.04: MiSTer's version three-way merged with upstream (base 60e8343, theirs dc8d764 = 3.02.04). Board-issue generates use the constant C_BOARD_ISSUE = 0 (Issue 2-4 logic, 2 MB); only the plain t80n (g_option = 0: no Pi debug, port C, TZX hotkeys); nextreg 0x04 keeps MiSTer's boot reset; csync removed (gone upstream) |
rtl/rom/bootrom.vhd, rtl/device/ctc.vhd, ctc_chan.vhd, rtl/video/sprites.vhd |
same files in CORE/ZX_Spectrum_Next_FPGA |
Upstream 3.02.04 versions (MiSTer had not changed them) |
| (new since 3.02.01) | arbiter_dma.vhd, spi_slave.vhd, pi_debug_control_param.vhd from CORE/ZX_Spectrum_Next_FPGA |
Only instantiated in disabled generates, but needed for analysis |
spi_master.vhd and the CPU (t80n*) stay MiSTer's: MiSTer uses Mike Stirling's SPI master and
Sorgelig's t80n with MEMPTR; upstream's changes to them are not applied.
When bumping a submodule, diff the upstream files above against the version the replacement
was made from and carry over any changes. The .xpr files list each MiSTer RTL file
explicitly (library zxnext), so files that upstream adds or renames must be added by hand
to all four .xprs.
M2M framework (V2.0.1): only M2M/vhdl/top_mega65-r3.vhd, -r4.vhd, -r5.vhd, -r6.vhd are
changed. R4-R6 (a) remove the SDRAM tie-offs, adds the DQ tri-state generate and routes all sdram_*
pins to the CORE; all four (b) route the framework's SD outputs (sd_reset/clk/mosi, sd2_*) through
the CORE's fw_sd*_i ports, which drives the pins (sd_*/sd2_* outputs); MISO and card detect
go from the pins to the framework directly, MISO also to the CORE; and (c) also connect the raw
joystick pins of both ports to the CORE's joy_1_pins_n_i/joy_2_pins_n_i (Amiga mouse). Also changed
(V1.3, from MegaST): the OSM palette comes from OSM_PALETTE in globals.vhd (generic G_OSM_PALETTE
through framework.vhd → av_pipeline → analog/digital_pipeline → video_overlay → vga_osm.vhd), and
M2M/rom/screen.asm SCR$PRINTSTR takes ESC + attribute byte (colours on the welcome screen). Re-apply
all of them when updating M2M. See doc/m2m/exceptions.md and doc/m2m-v2.1.0-upgrade.md.
QNICE: none.
git submodule update --init # CORE/ZXNext_MiSTer and M2M/QNICE
# QNICE toolchain (one-time) and Shell ROM. Vivado also runs CORE/m2m-rom/synth_pre.tcl at synthesis.
( cd M2M/QNICE/tools && ./make-toolchain.sh )
( cd CORE/m2m-rom && ./make_rom.sh )
# Simulations (GHDL): SDRAM controller and SD card image
( cd CORE/sim && make ) # or: make sdram / sd / speed / speed_bram (R3 RAM) / mouse / mouse_reg; make clean
# Bitstream: open CORE/CORE-R6.xpr (or R4/R5) in Vivado 2026.1 →
# Run Synthesis → Run Implementation → Generate Bitstream
# → CORE/CORE-R6.runs/impl_1/mega65_r6.bit
# JTAG: m65 -q CORE/CORE-R6.runs/impl_1/mega65_r6.bit (mega65-tools)
# .cor: bit2core / coretool (see the M2M wiki release page)Vivado 2026.1 lives in ~/Xilinx/2026.1 (source ~/Xilinx/2026.1/Vivado/settings64.sh); it is
not on the PATH by default. On Ubuntu 26.04 Vivado needs libncurses.so.5, which the OS no longer
ships: point LD_LIBRARY_PATH at a private directory with a symlink
libncurses.so.5 -> /usr/lib/x86_64-linux-gnu/libncursesw.so.6 instead of changing the system.
Batch build (headless, same .xpr):
open_project CORE/CORE-R6.xpr
reset_run synth_1
launch_runs impl_1 -to_step write_bitstream -jobs 8
wait_on_run impl_1
open_run impl_1
report_timing_summary -file timing_summary.rpt
check_timing -verbose -file check_timing.rpt- Run long builds detached (
setsid nohup ...). Other agent sessions on the same machine build other cores with Vivado; neverpkill/pgrep -f vivado(it hits their builds, and theirs hit yours). Kill only by the PID you started. - Vivado rewrites the
.xprfiles when it opens them (format, absolute paths). After a build,git checkout -- CORE/*.xprunless you intended a file-list change. runme.logis appended across runs: read only from the last "Start of session".- After every build check: WNS and WHS >= 0, no critical warnings, no block RAM demoted to LUTRAM
(Synth 8-5835). Known and fine:
t80n.vhdRegsH/RegsLimplemented in registers (8-4767). - If you can't run synthesis, say so; don't claim timing results.
Test on hardware with: the normal MEGA65 SD card (FAT32) with an image of a ZX Next SD card
(e.g. MiSTer's boot.vhd, or a FAT32 image holding System/Next 24.11) copied to
/zxnext/boot.vhd or /zxnext/boot.img (it may be fragmented, up to 512 pieces).
- Follow the M2M conventions:
lower_snake_case,_i/_o/_nsuffixes, clock-domain prefixes (qnice_*,main_*,hr_*,video_*); crossing a prefix boundary without a CDC is a bug. 3-space VHDL indentation, aligned:/=>, as in the existing files. - File headers: see
doc/m2m/example-file-headers.md. Modified MiSTer files keep their original header with ours prepended (seet80n.vhd). - "Avoid touching the original": wrap or replace MiSTer files from
CORE/vhdl/, never edit the submodule. Record every replacement in §4 here and indoc/m2m/exceptions.md. - QNICE assembly: no apostrophes in comments (the C preprocessor chokes on them).
M2M/is read-only except the documented top-file patches.
- Changing a clock: edit
clk.vhdandCORE_CLK_SPEED, and checkCORE.xdc(the generated clock names/pins and the multicycles assume 112 = 4 × 28 MHz). sdram_zxnassumes its clock is exactly 4× and phase aligned withCLK_28; Layer 2 timing depends on that.- The CPU↔
sdram_clkmulticycles inCORE-SDRAM.xdc(2 cycles each way, from MiSTer'sZXNext.sdc) and themain_clk→Port A multicycle are only correct because Port A samples and updates on even edges only (§3.2). Anything insdram_zxnthat readsram_a_*_ior drivesram_a_*_oon another edge breaks them silently: STA still passes. The testbench catches it (garbage inputs, odd-edge output check). Renaming the Port A registers (orlast_data, whose clock enable seesram_a_req_ionly through the even-edge queue branch) drops themain_clkmulticycle (it filters by cell name). Don't add more multicycles without proving the capture register really waits that long. C_MENU_*↔ menu line coupling (§3.5). The main menu uses all 13 lines ofOPTM_DY; on M2M V2.1.0 an oversized menu is a fatal Shell error. The settings file is exactlyOPTM_SIZEbytes: adding or removing a menu line makes every user's/zxnext/zxncfgthe wrong size (the Shell then silently uses the defaults and saves nothing). Regeneraterelease/zxnext/zxncfgand tell users to replace theirs in the release notes.- QNICE must not use the SD card after
PREP_START(§3.3): the core drives it. Anything in the Shell that needs files after startup (file browser, vdrives) would collide with it. The settings file is the exception: it borrows the card through register 9 (§3.3); do the same for anything else. - The XDC uses hierarchical names; instance renames drop constraints silently.
doc/temp/*are upstream M2M scratch notes, not documentation of this core.- Vivado 2026.1 has mis-synthesized VHDL procedures that write process variables implicitly (seen
in another port). Pass variables as explicit
inoutparameters; if hardware and simulation disagree, suspect synthesis.
The audit against the Porting Guide is recorded in doc/porting-guide-audit.md. V1.3 (core 3.02.04; R6
tested with a test build, R3/R4/R5 untested) is released. Known issues are listed in README.md and VERSIONS.md.
If the grey screen comes back: CPU trace probe triggered by the DivMMC interrupt handler saving
an SP of 0x26xx to 0x25B8 (i.e. re-entering itself). F13 (CPU speed, nextreg 0x07)
and F9 (NMI menu) reported working on an R3A; F11 (DivMMC NMI) only acts if nextreg 0x06 bit 4 is
set (esxDOS use), untested. Still to do: F11, .DSK, mouse in port 2; R4/R5 testers; maybe show the MBR partition type in the
"Cannot mount the SD card" error.
Boot screen: the firmware shows "MiSTer Next" (picked by the machine ID 0xDA, not in our code).
tools/bootscreen/zxn_bootscreen.py patches a user's TBBLUE.FW to show the MegaZXNext logo
(doc/assets/megazxnext-badge.png, converted by make_logo.py); its header documents the
firmware's screen format. Works on the R6; the firmware maintainers are fine with it. Keep the version in config.vhd (welcome screen,
HELP_1, CORENAME) and VERSIONS.md in sync.
/home/freemchr/github/m2m-porting-lessons/README.md
(https://github.com/freemchr/m2m-porting-lessons) collects what earlier ports (MegaST) learnt the
hard way: memory latency budgets, M2M V2.0.1 bugs, resets, Vivado and QNICE pitfalls, MEGA65
hardware facts, verification and release practice. Read it before porting work or when debugging
framework, Vivado or hardware problems. Where it applies here:
- OSM size: this core's OSM is 720x576 with a 16x16 font, i.e. 45x36 characters. Inside the frame a help line can be at most 43 characters (MegaST's 640x400 limits do not apply).
- Resets:
main_reset_m2m_i/main_reset_core_ialso carry the QNICE CSR reset (lessons §3.4). - Board tops: R3 differs from R4-R6 (no SDRAM, other controllers); its RAM is block RAM (§1, §3.2).
- Release: no ROMs are shipped (the user supplies the ZX Next distribution on the SD card); the
settings file ships empty (
release/zxnext/zxncfg, 31 × 0xFF): put it in the release zip. Usebit2corefrom mega65-tools, not the 4 MB capped copy in M2M. Never call a board "working" before a tester with that board confirmed it. - Hardware debugging: TE0790 JTAG under WSL (
usbipd attach --wsl), BSCANE2 probes instead of ILA (free licence), load test bitstreams over JTAG.
- M2M framework + agent guide: https://github.com/sy2002/MiSTer2MEGA65 (develop:
AGENTS.md) - Porting guide: https://github.com/sy2002/MiSTer2MEGA65/wiki/The-Ultimate-MiSTer2MEGA65-Porting-Guide
- Updating M2M: https://github.com/sy2002/MiSTer2MEGA65/wiki/Updating-M2M-in-an-existing-core
- Reference core: https://github.com/MJoergen/C64MEGA65
- MEGA65 SDRAM controller this one borrows its I/O scheme from: https://github.com/MJoergen/SDRAM
- ZX Next MiSTer core: https://github.com/MiSTer-devel/ZXNext_MISTer
- MEGA65 Discord (M2M channel)