-
Notifications
You must be signed in to change notification settings - Fork 14
MiSTer Quick Reference Guide
This page is the MiSTer lens for MEGA65 core porters. Before you can port a
MiSTer core with MiSTer2MEGA65 (M2M), you have to be able to read that core — and
its source assumes you already understand MiSTer's world: its on-screen menu, its
configuration string, the emu module, the services the ARM provides, the way video
and disks and ROMs flow. This guide teaches that world from the ground up, on MiSTer's
own terms, for a developer who may never have owned a MiSTer.
It is a companion, not the main event. The actual porting work — every M2M file you edit, every wire you connect — lives in The Ultimate MiSTer2MEGA65 Porting Guide. Read this page first to build the mental model; then that guide turns the model into concrete steps. Wherever a MiSTer concept has a MEGA65 counterpart, this page gives you a one-line pointer to the article that owns it rather than re-explaining it here.
- New to MiSTer? Read top to bottom once. You will come out able to open any core's repository and know what you are looking at.
-
Already fluent? Jump to the tables — the
emuports, theCONF_STRgrammar, and the Rosetta-stone table — and move on to the porting guide. -
Conventions: MiSTer signal and file names are in
monospace. A→points at the MEGA65/M2M equivalent. "The ARM" and "the HPS" both mean MiSTer's Linux co-processor.
MiSTer is an FPGA platform built on the Terasic DE10-Nano board. That board carries an Intel/Altera Cyclone V — but not a plain FPGA. It is a SoC: an FPGA fabric and, on the same chip, an ARM Cortex-A9 running Linux, called the HPS (Hard Processor System). This detail is the single most important thing to internalize, because it explains both how MiSTer is built and why M2M has to exist at all.
The division of labor is:
- The retro machine — the C64, the Amiga, the arcade board — is FPGA logic. This is "the core".
- Almost everything around it is done by the ARM running Linux (a program called
Main_MiSTer): drawing the on-screen menu, browsing the SD card, loading ROMs, mounting disk images, reading USB gamepads, keeping the clock. On the FPGA side, a framework calledsysprovides the hardware endpoints for all of that (video scaling, HDMI, audio DACs, and the bridge to the ARM).
flowchart TB
ARM["ARM + Linux (HPS) — Main_MiSTer: menu, file browser, SD, ROM load, disk mounting"]
SYS["sys/ — the MiSTer framework (HAL): sys_top.v, hps_io.sv, video, audio"]
EMU["emu — your glue logic (the root .sv file)"]
RTL["rtl/ — the actual retro machine (CPU, video chip, sound chip, ...)"]
ARM <-->|HPS_BUS| SYS
SYS --> EMU
EMU --> RTL
Now the punchline. The MEGA65 is a pure FPGA machine — a Xilinx Artix-7, and no
ARM, no Linux. So when you move a MiSTer core to the MEGA65, the retro machine comes
across almost unchanged, but every service the ARM used to provide has to be rebuilt in
pure FPGA. That is exactly what M2M is: it replaces the ARM+Linux+sys stack with a small
QNICE softcore CPU running the M2M Shell firmware, plus the M2M/ framework logic.
QNICE is M2M's stand-in for MiSTer's ARM.
Hold that picture and the whole port becomes a translation exercise. Here is the translation table you will keep coming back to.
| On MiSTer | On the MEGA65, under M2M | Where the MEGA65 side is documented |
|---|---|---|
ARM + Linux (HPS), running Main_MiSTer
|
QNICE softcore + M2M Shell firmware | The Ultimate MiSTer2MEGA65 Porting Guide §1.2.4 |
sys/ framework (sys_top.v, hps_io.sv, video, audio) |
M2M/ framework (framework.vhd + board top) |
Ultimate Guide §1.2.2 |
emu — the root .sv glue module |
your main.vhd + mega65.vhd
|
Ultimate Guide §1.1.2, Appendix A + B |
CONF_STR + the status[] menu register |
config.vhd (OPTM_ITEMS/OPTM_GROUPS) + OSM control bits |
On‐Screen‐Menu (OSM), config.vhd Switches and Settings |
ioctl file/ROM download |
CRT/ROM loading over the QNICE device bus | Devices, Ultimate Guide §1.3.2 |
sd/img disk-image block protocol |
vdrives.vhd + QNICE FAT32 |
Devices, Ultimate Guide §1.3.3 |
ps2_key / ps2_mouse
|
80-key matrix scan + your keyboard.vhd
|
Ultimate Guide §1.3.4 |
| DDR3 / SDRAM | HyperRAM (Avalon-MM); SDRAM is a roadmap item | Devices, Ultimate Guide §1.4.3 |
video_mixer / video_freak / ascal
|
the M2M av_pipeline (same ascal scaler) |
Video pipeline and output, Ultimate Guide §1.3.5 |
audio_out filters |
signed 16-bit PCM into the framework's filters | Ultimate Guide §1.3.6 |
RTC[64:0] |
main_rtc_i |
Ultimate Guide §1.3.9 |
Quartus pll / pll_cfg
|
your clk.vhd MMCM |
Ultimate Guide §1.3.7 |
| (QNICE Shell memory budget) | — | Shell memory layout |
Everything below is a slower walk through the left-hand column.
MiSTer is a disciplined project: every core's GitHub repository has the same shape, so once you can read one you can read them all. Three things matter.
-
rtl/— the actual machine. The hardware description of the retro computer or arcade board: CPU, video chip, sound chip, glue logic, often the floppy/disk controller, the core's clock definition (rtl/pll/), and frequently the ROMs. This is the part you carry over to the MEGA65, largely unchanged. For the C64 core,rtl/holdsfpga64_sid_iec.vhd(the C64 itself), the VIC-II, the SID, the T65 CPU,iec_drive/(the 1541/1581), androms/. -
sys/— the MiSTer framework. MiSTer's hardware abstraction layer:sys_top.v,hps_io.sv(the bridge to the ARM), the video pipeline (video_mixer.sv,video_freak.sv,scandoubler.v,ascal.vhd,osd.v), audio (audio_out.v), and the framework PLLs. You never carry anything fromsys/into an M2M port — M2M provides an equivalent for all of it. You readsys/only to understand what a service does. -
The root
<Core>.sv— the glue, and your oracle. Every core has exactly one SystemVerilog file in the repository root, named after the core (c64.sv,Minimig.sv,Gameboy.sv, …). It contains a single module, always calledemu. This is where the retro machine meets the framework:emuinstantiates the machine fromrtl/and wires it to everything the framework provides. It is the most important file of your whole port — keep it open at all times. When you are ever unsure "what do I connect to input X?", the answer is in<Core>.sv.
The inversion that surprises everyone. You might expect the core author to write the
FPGA top-level. They do not. Quartus is pointed at the framework's sys/sys_top.v as the
top, and that instantiates the author's emu. Your core is a plug-in, not the root. M2M
mirrors this exactly: the board top and framework.vhd instantiate your MEGA65_Core.
You do not synthesize
emuand you do not portsys_top.v. You re-createemu's glue role by hand in M2M'smain.vhd, and you instantiate the machine module fromrtl/— notemu— inside it.
Two more files you will meet:
-
rtl/pll/(a Quartus PLL). The core's clock plan (see §9). It will not synthesize in Vivado; you re-create its frequencies in your ownclk.vhd. -
.mrafiles (arcade only). Arcade cores don't ship a ready-made ROM; instead an XML recipe assembles it on the fly from MAME zip files (see §6). The MEGA65 has no such machinery, so arcade ROMs need special handling.
What you lift vs. what you drop: lift rtl/ (the machine) and the logic inside emu
that conditions the machine's signals. Drop all of sys/, sys_top.v, the emu port
shell itself, and the Quartus project files — M2M replaces each of those.
emu's port list is the standardized contract between any MiSTer core and the MiSTer
framework. Learn its groups and you have learned what services exist, because each group
maps to exactly one row of the Rosetta table.
In today's template the ports are pulled in from sys/emu_ports.vh via an `include,
so the master list lives there; many older cores (and most of the online examples) spell
the same ports inline inside <Core>.sv. The content is identical either way. Here are the
groups, with the load-bearing details called out.
| Group | Dir | Key signals | What it is |
|---|---|---|---|
| Clock / reset | in |
CLK_50M, RESET
|
The only system clock handed in is 50 MHz (a fixed CLK_AUDIO is also supplied — see the Audio row); the core derives the rest with a PLL. RESET is the async power-on / "bye-bye" reset. |
| HPS bus | inout | HPS_BUS |
One opaque bus carrying everything to/from the ARM. Passed straight to hps_io (see §5). |
| Video | out |
CLK_VIDEO, CE_PIXEL, VGA_R/G/B (8-bit), VGA_HS/VS, VGA_DE, VGA_F1, VGA_SL, VIDEO_ARX/ARY
|
24-bit RGB + syncs + data-enable + pixel clock-enable. See the conventions below. |
| HDMI geometry | mixed |
HDMI_WIDTH, HDMI_HEIGHT, HDMI_FREEZE
|
Framework tells the core the current HDMI canvas; core can freeze the image. |
| Audio | mixed |
CLK_AUDIO, AUDIO_L/R (16-bit), AUDIO_S, AUDIO_MIX
|
Signed/unsigned 16-bit stereo PCM + a mono-mix level (see §7). |
| LEDs / buttons | out |
LED_USER, LED_POWER, LED_DISK, BUTTONS
|
Activity/power/disk LEDs; the core can also simulate the OSD/user button presses. |
| DDR3 | mixed | DDRAM_* |
High-latency, big DDR3 RAM. Used by the HDMI scaler and by cores needing lots of RAM. |
| SDRAM | mixed | SDRAM_* |
Lower-latency external SDRAM (needs an add-on board). The classic memory for retro cores. |
| Misc board I/O | mixed |
SD_* (SPI), ADC_BUS, UART_*, USER_IN/OUT, OSD_STATUS
|
Second SD card, ADC (tape), physical serial, user port, and a flag that is high while the OSD menu is open. |
Some ports only appear when a core opts into them with a `ifdef — the MISTER_FB
DDR3 framebuffer path and the MISTER_DUAL_SDRAM second SDRAM chip are the common ones.
The first thing a typical emu does is tie off everything it doesn't use (e.g. zero the
whole DDRAM interface, tristate the SD-SPI). Those tie-offs are a gift: they tell you at a
glance which services you can ignore in your port.
Video conventions you must respect (they carry straight over to M2M):
-
CLK_VIDEOvsCE_PIXEL. The core streams pixels onCLK_VIDEObut only on the cycles whereCE_PIXELis high.CLK_VIDEOusually equals the system clock; lower pixel rates are expressed by pulsingCE_PIXEL, not by making a slower clock. If the pixel rate equalsCLK_VIDEO, holdCE_PIXELhigh. That pattern — one fast clock plus an enable pulsed at the slower rate — is the central idea of Clocking and Clock Domain Crossing. -
VGA_DE = ~(VBlank | HBlank). MiSTer emits a single active-display-enable that is the inverse of blanking. (M2M instead wants the two blanking signals directly — a small but important difference; see §7.) - Syncs are positive polarity.
-
AUDIO_Sdeclares whether your samples are signed. Get it wrong and audio is garbled.
The
emuport list is a reference; the definitive per-group MEGA65 mapping — which M2M port each one becomes — is in the Ultimate Guide's Appendix B (mega65.vhdport groups).
Press F12 on a MiSTer and an On-Screen Display menu drops down: a list of options, toggles, sub-pages, and "load file" / "mount disk" entries. It is how the end user chooses PAL vs. NTSC, mounts a disk into drive 8, loads a cartridge, picks a scandoubler filter, and so on. If you have never seen one, picture the MEGA65's own Options menu — M2M's OSM is its direct descendant.
Here is the crucial part: the whole menu is generated from a single string baked into the
core, called CONF_STR. The ARM reads that string and builds the menu; when the user
changes something, the choice is written into a wide register called status that the
core reads. Nothing about the menu lives in Linux config files — it is all in the core, in
one string.
CONF_STR is, in the words of one porter, the core's requirements document: the most
compact, most reliable statement of what the core needs from its environment. Every option
line names a status[] bit the core consumes and tells you, by its label, what that bit
means. Every F/S line declares a kind of file or disk image the core can load. A
status bit you wire wrong is one of the classic "the core synthesizes but won't boot" bugs —
so reading CONF_STR fluently is a core skill.
Menu choices land in status, a wide register hps_io hands to the core. Its width has
grown across MiSTer generations — older cores (including the C64-era hps_io that M2M's
vdrives was derived from) use status[63:0]; modern cores use a 128-bit status — so
don't hard-code a single width in your head. status[0] is reserved for Soft Reset. The
core reads bits straight out: wire ntsc = status[2];.
CONF_STR is one long string, each entry ending in a semicolon. Bit positions use an
"extended hex" alphabet — 0–9 then A–V — so a single character addresses one of 32
bit positions. Upper-case letters address the low 32 status bits; the lower-case twin adds
32 (so o0 is status[32], t3 is a trigger on status[35], and so on).
| Token | Meaning |
|---|---|
<Name>;; (first entry) |
The core name (menu title). |
O<n>[<m>],Label,Opt0,Opt1,… |
Option cycler bound to status[n] (or the range status[m:n]). Lower-case o = +32. |
T<n>,Label |
Trigger — pulses status[n] high momentarily (e.g. reset). Lower-case t = +32. |
R<n>,Label |
Same as T, but also closes the OSD afterwards. Lower-case r = +32. |
F[<idx>],<EXTS>,Label |
Load a file (ROM/cartridge/BIOS). Arrives over ioctl with this index. (An FS variant also supports save files.) |
FC[<idx>],<EXTS>,Label |
Like F, but remember the file and auto-load it at the next start. |
S<slot>,<EXTS>,Label |
Mount a disk image on virtual-drive <slot> (0–3). Drives the sd/img protocol (§5). |
P<n>,Title / P<n>…
|
Define menu page n; prefix later entries with P<n> to file them under it. |
- or -,Text
|
A separator line (with optional static text / heading). |
D<n> / d<n> (prefix) |
Disable (grey out) the entry when menu-mask bit n is set / not set. |
H<n> / h<n> (prefix) |
Hide the entry when menu-mask bit n is set / not set. |
J[1],Btn1,… |
Joystick button names (up to 12). J1 locks the keyboard into joystick-emulation mode. |
jn,… / jp,…
|
Default joystick mapping, by name / by position (relative to a SNES pad). |
V,<string> |
Version string appended to the core name. |
DIP / CHEAT / C / I,…
|
Arcade DIP-switch menu (from the MRA) / cheat menu / OSD info lines. |
Prefixes stack in a fixed order: the disable/hide prefix (d/D/h/H) comes first, then
the page prefix P<n>, then the option itself — e.g. d5P1o2,Vertical Crop,… means "an
option on status[34], on page 1, greyed out unless menu-mask bit 5 is set." Get the order
wrong and the entry silently misbehaves. J/jn/jp/V must come last in the string.
From the C64 core (c64.sv):
"H7S0,D64G64T64D81,Mount #8;"
Reading it: S0 = a disk-image mount on virtual-drive slot 0; the extensions D64, G64,
T64, D81 are the file types it accepts; H7 hides the entry when menu-mask bit 7 is
set (the core raises that mask bit from runtime state). That one line is what drives the
entire img_mounted / sd_lba[0] disk protocol into the C64's iec_drive.
M2M has no status word. Instead, each menu line is its own bit in a 256-bit register,
declared as text in config.vhd. So a MiSTer multi-value field like O45,Aspect Ratio,… (four choices packed into status[5:4]) typically becomes a small submenu of
single-select lines on the MEGA65, and a plain on/off O bit becomes one checkbox line.
How to build that menu — groups, radio buttons, wiring a menu bit to your VHDL — is the job
of On‐Screen‐Menu (OSM) and config.vhd Switches and Settings, and the mechanical
CONF_STR → config.vhd translation is the Ultimate Guide's §1.3.1. This page's job was
only to teach you to read the MiSTer side.
hps_io is the module every core instantiates next to emu; it is the FPGA-side endpoint
of the ARM, and it hands the core every service the Linux side provides. On the MEGA65 the
QNICE softcore plus the Shell firmware play this exact role. Reading a core's hps_io
instantiation is how you inventory which services the core actually uses.
A quick tour of the service groups:
-
The menu (
status). Covered in §4 —status, plusstatus_in/status_set(the core can push a value back) andstatus_menumask(drives thed/Hshow/hide prefixes). -
ioctl— file / ROM download. How ROMs, cartridges, BIOSes and tapes get into the core. Covered below. -
sd/img— the disk-image block protocol. How mounted disk images are read and written, block by block. Covered below — this is the one M2M reproduces most faithfully. -
Keyboard & mouse.
ps2_key[10:0]delivers PS/2 scan codes: bits[7:0]= code,[8]= extended,[9]= pressed,[10]= a toggle that flips on every event.ps2_mouseis analogous. → M2M does not speak PS/2; it hands you a key-matrix index and you writekeyboard.vhd(§9). -
Joysticks / paddles / spinners.
joystick_0..5(up to six pads; directions in bits[3:0], buttons from bit 4),paddle_0..5, analog sticks, spinners. → the MEGA65 has two physical joystick ports, which you map ontojoystick_0/joystick_1. -
RTC[64:0]andTIMESTAMP[32:0]. Wall-clock time (MSM6242B BCD layout) and Unix time from Linux, sent once at startup; the core keeps ticking from there. →main_rtc_i, same 65-bit width, sourced from M2M's own I2C real-time clock. -
Odds and ends.
buttons,forced_scandoubler,gamma_bus, andEXT_BUS. That last one is a red flag worth memorizing:EXT_BUS(with a companionhps_ext.v) is a core-specific side-channel that bypasses the standard services. The Amiga core uses it for its keyboard, mouse and HDD (IDE) — so a core with anEXT_BUSneeds extra analysis, and its inputs won't map through the normal paths.
Two
hps_iogenerations exist in the wild. The current MiSTer template's copy is newer (128-bitstatus, up to 10 virtual drives, split left/right analog sticks); the copy vendored into older ports — the one M2M's disk logic was derived from — is older (64-bitstatus, up to 4 drives). The protocols (ioctl,sd/img,ps2_key,RTC) are the same shape in both; only widths and a few extras differ.
When the user picks a file for an F entry, the ARM streams it into the core, byte by byte:
-
ioctl_downloadis high for the duration of the transfer. -
ioctl_indexsays whichFentry triggered it (theF1inCONF_STR→ index 1), so the core can route the bytes to the right destination. -
ioctl_addrcounts up,ioctl_doutcarries each byte/word,ioctl_wrstrobes it. -
ioctl_waitlets the core apply back-pressure when it can't keep up.
A textbook consumer looks like load_prg = ioctl_index=='h01; load_rom = ioctl_index==8; —
the core decodes the index into load targets. ROMs, cartridges, tapes and even custom filter
files all arrive over this one channel.
→ On the MEGA65 there is no linear ioctl address stream; instead each loadable object is
its own QNICE device, and the Shell streams files into it. See Devices and the
Ultimate Guide's §1.3.2.
This is the most important protocol to understand, because M2M reimplements it almost
bit-for-bit in vdrives.vhd — which is the single best piece of news in the whole port.
The core's own drive emulation (the C64's iec_drive, say) speaks this protocol; whoever is
on the other end serves the actual file.
Mounting. When the user mounts an image via an S entry, hps_io pulses the drive's bit
in img_mounted and presents img_size (in bytes) and img_readonly. The core latches
those. Unmounting is the same strobe with img_size == 0 — a convention worth
remembering, because M2M uses it too.
Block access. To read a sector the core puts the block address on sd_lba[drive], raises
its bit in sd_rd, and waits; the server raises sd_ack and streams the bytes over
sd_buff_addr / sd_buff_dout / sd_buff_wr into the core's sector buffer. Writing is the
mirror image: raise sd_wr, and the core feeds bytes back on sd_buff_din. The block size
is 128 << BLKSZ bytes (so the default BLKSZ=2 is 512 bytes; the C64 uses BLKSZ=1 = 256).
→ M2M's vdrives.vhd presents this identical interface to your drive; the QNICE Shell plays
the role of the ARM, reading and writing the disk-image file on the SD card and caching it in
RAM. See Devices and the Ultimate Guide's §1.3.3. (M2M supports up to 15 virtual drives,
8-bit data only — it does not implement hps_io's 16-bit "WIDE" mode.)
Arcade cores are the exception to almost everything above. An arcade core's .rbf
(compiled bitstream) is hidden from the menu; the user browses .mra files instead. An
MRA is an XML recipe that names which .rbf to load and, crucially, assembles the game
ROM on the fly from MAME .zip files — concatenating, interleaving and patching ROM parts,
then verifying the result against an MD5. DIP switches also come from the MRA (delivered over
ioctl with index 254), not from status bits.
→ The MEGA65 has no MAME-zip assembly and no .mra/.rbf browsing. An arcade core that
relies on this needs its ROM pre-assembled and its DIP switches surfaced through the MEGA65's
own menu — a genuine porting gap to plan for, not a service M2M provides.
A MiSTer core outputs a native retro video signal — often a 15 kHz analog RGB signal,
exactly what the original machine produced. The framework then does the modern work: a
scandoubler for VGA monitors, and a polyphase scaler (ascal.vhd) that upscales to a
fixed HDMI mode. A key design rule follows from this: the core does not build in its own
scandoubler, because 15 kHz purists want the raw signal and the framework handles the rest.
What the core provides is RGB + separate H/V sync + VGA_DE + CE_PIXEL on CLK_VIDEO
(plus VGA_F1 for interlace, below). Three things a porter must know:
-
M2M wants the blanking signals, not
VGA_DE. MiSTer collapses blanking into the singleVGA_DE = ~(VBlank | HBlank); M2M instead wantsvideo_hblank_oandvideo_vblank_odirectly (active-high). Very often the core already produces them internally and only merged them at the last step, so you can tap the earlier signals. -
The scaler is shared heritage. M2M uses the same temlib
ascal.vhdMiSTer uses, so the video contract carries over cleanly. What M2M adds around it (analog VGA + HDMI) lives in Video pipeline and output and the Ultimate Guide's §1.3.5. -
Interlace via
VGA_F1. Machines like the Amiga can switch to an interlaced (laced) mode. MiSTer signals which field is current onVGA_F1, andascalcan weave the two fields into one full-height frame. Mainline M2M does not yet expose a field signal — it is an AExp-pioneered V2.1.0 roadmap item (avideo_fl_iinput that defaults to'0', so progressive cores are unaffected). If your core is interlaced, know this is on the horizon, not in the released framework.
Worked example — how the C64 makes its blanking. fpga64_sid_iec outputs only raw
hsync/vsync + RGB; it produces no blanking. MiSTer generates blanking with a small helper
module, video_sync, which also crops the visible raster to the MiSTer-standard 384×270
window. The MEGA65 port reuses the very same video_sync module, moved into main.vhd's
clock domain, to produce video_hblank_o/video_vblank_o. (A trap to note: there are two
video_sync.vhd files — the core-local one must win over the framework's.) This is the
typical shape of a video port: find where the core's signal is complete — RGB + HS + VS +
HBlank + VBlank — and tap it there.
Audio is refreshingly simple. The core drives signed 16-bit stereo PCM on
AUDIO_L/AUDIO_R, sets AUDIO_S = 1 to say the samples are signed, and can request a
mono down-mix via AUDIO_MIX. → On the MEGA65 this becomes main_audio_left_o /
main_audio_right_o, signed(15 downto 0) — the same signed 16-bit contract.
Two details save you grief:
-
The audio clock differs. MiSTer's
CLK_AUDIOis documented as 24.576 MHz; M2M's audio clock is 12.288 MHz (half). Don't copy a hard-coded 24.576 MHz assumption across. -
The optional IIR filter. MiSTer offers a gentle low-pass "audio improvement" filter
whose biquad coefficients live in
sys/sys_top.v. M2M runs the same filter in the framework and expects those coefficients as constants in yourglobals.vhd, toggled by a menu bit. The mapping is the Ultimate Guide's §1.3.6.
If a core mixes several sources (the C64 blends SID with an OPL expander, a DAC and tape
noise), that mixing happens inside emu before AUDIO_L/R. You reproduce as much of it as
you want in main.vhd; a first port can start with just the primary sound chip and add the
rest later.
MiSTer is rich in RAM, and this is where the MEGA65's constraints bite hardest, so it pays to understand what a core actually needs.
On MiSTer, a core has two external memories to reach for:
- SDRAM — lower latency, on an add-on board. The classic choice for a core's fast RAM (C64 REU, Amiga chip/fast RAM, arcade work RAM).
- DDR3 — huge but high-latency, shared with the ARM. Used for the HDMI scaler's framebuffer, HPS file transfer, and non-time-critical bulk storage.
A core signals which it uses simply by wiring SDRAM_* and/or DDRAM_*; if emu tristates
the SDRAM interface, the core needs no SDRAM board.
On the MEGA65 the picture is very different. All boards share one Artix-7 with roughly 1.6 MB of fast on-chip Block RAM (BRAM) and 8 MB of HyperRAM — and the HyperRAM is slower (a read has real latency) and shared with the video scaler, which can hold it for a long burst. So the porting questions are: how much fast RAM/ROM does the core truly need, and does it fit?
A rule of thumb that has held up well: the framework itself uses ~200 KB of BRAM, so if the core's fast RAM + ROM totals under ~1.4 MB, it maps to BRAM straightforwardly and you barely touch the original. Above that you must get clever — split the needs between BRAM (fast) and HyperRAM (slow/bulk), or choose a smaller machine configuration. The Amiga is the cautionary tale: a minimal A500 (512 KB chip + 512 KB slow + 256 KB Kickstart = 1.25 MB) fits BRAM with almost nothing to spare, so its disk image and the scaler framebuffer both live in HyperRAM, and adding an IDE hard disk simply does not fit.
→ DDR3/SDRAM both map to HyperRAM, driven as an Avalon-MM master (hr_core_*), usually
through a CDC FIFO and a read cache — that hop from your core clock into the 100 MHz HyperRAM
clock is itself a clock-domain crossing (see Clocking and Clock Domain Crossing). The details, plus the roadmap for using the newer boards'
SDRAM as a second memory, are in Devices and the Ultimate Guide's §1.4.
Keyboard. MiSTer hands the core PS/2 scan codes on ps2_key. M2M does not: it scans the
MEGA65's own keyboard and hands you a key-matrix index (0–79) plus a pressed flag. You
write a small keyboard.vhd that reshapes those 80 keys into whatever format the core's
keyboard logic expects. This is one of the few pieces of glue with no MiSTer counterpart to
copy — see the Ultimate Guide's §1.3.4.
Joysticks, paddles, mice. The MEGA65's two physical joystick ports map onto
joystick_0/joystick_1; paddles and 1351-style mice ride the analog POT lines. Amiga-style
quadrature mice come in over the same analog path. The framework debounces these for you.
Clocking. The core gets only CLK_50M as a system clock (plus a fixed CLK_AUDIO) and
makes everything else with its pll. Two
lessons transfer directly:
-
Hit the original frequency to the Hertz. "Close enough" clocks cause subtle, maddening
compatibility damage — off-pitch audio, broken tape loaders, wrong CIA timers. Read the
core's
rtl/pll/files to learn the exact target frequencies, and reproduce them in your MMCM. The value also feedsCORE_CLK_SPEEDinglobals.vhd, which drift-free timers rely on. -
Dynamic reconfiguration is a decision point. Some cores retune the PLL at runtime — the
C64 nudges its clock for PAL vs. NTSC, and some arcade cores overclock a percent or so
(Arkanoid, about 1.4%) to bring their refresh into spec for 60 Hz displays. This is not
portable as-is. M2M cores usually synthesize one fixed clock plan per video standard, or
instantiate parallel MMCMs and switch between them glitch-free. When you first bring a core
up, pick one standard (say PAL), ignore the rest, and add complexity later. The Ultimate
Guide's §1.3.7 covers
clk.vhd. For the ideas underneath all of this — clock enables, hitting the frequency exactly, dynamic reconfiguration, and the clock-domain-crossing traps that arrive with M2M's several clocks — the Clocking and Clock Domain Crossing article walks them from first principles.
Reset. The canonical core reset is wire reset = RESET | status[0] | buttons[1]; — the
framework's power-on reset, OR the soft-reset menu bit, OR a physical button. One sharp edge:
if a core uses DDR3, accessing it during RESET causes a hard hang — a warning straight
from MiSTer's porting docs — so DDR3 requests must be held off until after reset completes. → M2M gives you a hard reset
(main_reset_m2m_i) and a soft reset (main_reset_core_i) and pauses the core while the menu
is open (main_pause_core_i).
Whenever you meet a new core, run this drill before touching any MEGA65 file. It is the
fastest way to build the "what does emu drive into the machine, and why" picture that the
whole port depends on.
-
Find the actual machine. Search
emu's body for an instantiation whose name is not framework-ish. For the C64 it isfpga64_sid_iec; Apple-II →apple2_top; Game Boy →gb; Amiga →minimig(sometimes the machine is split across several sibling instances). This module — notemu— is what you will instantiate inmain.vhd. -
Find the PLL, extract the clock plan. Read the
pllinstantiation and thertl/pll/pll_*.vconfig for the exact frequencies. Note whether there is apll_cfgdoing runtime reconfiguration (a porting decision point). -
Read
CONF_STR. For every line decide, mentally, whether the MEGA65 will replicate the option, hard-wire it to a constant, or drop it. This is your work list. -
Inventory the
hps_ioservices. Which ports are actually connected? Each connected one —ioctl,sd/img,ps2_key,RTC— is a service you must re-provide. -
Trace video. Follow the machine's
hsync/vsync/RGB until the signal is complete (RGB + HS + VS + HBlank + VBlank). That is your tap point for M2M. -
Trace audio, then inventory memory. Find what feeds
AUDIO_L/Rand whetherAUDIO_Ssays signed; then searchrtl/for every SDRAM, DDR3 and BRAM/ROM instance and add up the fast-memory total against §8's rule of thumb.
A worked, C64-specific version of this drill is the Ultimate Guide's §1.1.5.
The two M2M reference cores span the range of what porting throws at you. The C64 (C64MEGA65) walks the standard path; the Amiga (the AExp project, Minimig-AGA) hits almost every hard case at once. Seeing them side by side tells you what "normal" looks like and where surprises live.
| Concern | C64 — the standard path | Amiga — the hard cases |
|---|---|---|
| Configuration |
CONF_STR status bits → config.vhd
|
almost nothing in CONF_STR; config lives in Minimig's own userio handshake, replayed by a VHDL state machine |
| Disk images |
sd/img blocks → vdrives.vhd (writable D64) |
raw MFM flux over a side-channel — vdrives can't be used; needs a custom track engine + a firmware write-back hook |
| ROM load |
ioctl → CRT/ROM loader |
same (Kickstart is a normal auto-loaded ROM) |
| Keyboard | PS/2 → matrix keyboard.vhd
|
raw Amiga scancodes over a side-channel; no PS/2 |
| Interlace | never | laced screens mid-demo — needs the new field-flag path into ascal
|
| Memory | 64 KB + ROMs, fits BRAM easily | 1.25 MB fits BRAM only in the minimal A500; image + framebuffer go to HyperRAM |
| Framework changes | none | two features being upstreamed into M2M V2.1.0 (interlace field flag; a core-I/O firmware hook) |
The lesson isn't the specifics — it's the mindset: when a framework service doesn't fit,
separate its mechanism from its pattern. The Amiga floppy rejects the vdrives
mechanism (block-level SD access) but keeps the vdrives pattern (a RAM-cached image,
lazily flushed to SD in the background, with a dirty-LED and a prevent-unmount interlock).
Recognizing when to copy the pattern rather than the wire is the heart of porting the awkward
cores.
You now have the MiSTer mental model. Turn it into a port:
- The Ultimate MiSTer2MEGA65 Porting Guide — your main companion from here. Its Part I maps every MiSTer service to its M2M replacement; Parts II–IV walk the port phase by phase, catalog the Quartus-to-Vivado fixes, and give you a debugging playbook.
-
Building the menu: On‐Screen‐Menu (OSM), Welcome and Help Screens,
config.vhd Switches and Settings — everything the
CONF_STR→config.vhdtranslation needs. - Reference: Clocking and Clock Domain Crossing (clocks, clock enables, and safe clock-domain crossing), Devices (the QNICE device bus, ROM loading, virtual drives, HyperRAM), Shell memory layout, and Video pipeline and output.
And for the authoritative MiSTer-side detail, MiSTer's own developer documentation is the source of record — especially Porting Cores, Core Configuration String, Overview of emu module, and sys - hps_io. We port from MiSTer, but those pages describe the world we are porting from — and this guide is your map to it.