MAME Emulation Gaps
MAME Emulation Gaps
The KN5000 driver boots, plays and runs its UI. What it cannot do is start the way real hardware starts or install a firmware update from a floppy — and those two are the same problem seen from two ends, because everything the update path touches is also everything the first-stage bootloader owns.
This page inventories what the emulator does not model, distinguishes what is a fidelity defect from what is merely a shortcut that happens to land in the right place, and gives the order in which the three prerequisites have to be built.
Every claim here was checked against the MAME sources in
kn7000_mame/src/andmame/src/in August 2026. Line numbers drift; the file and symbol names are the durable part.
What the emulator does today
| Aspect | Hardware | MAME | Verdict |
|---|---|---|---|
| Address decode | six programmable chip-select blocks | registers stored, never consulted | not modelled |
| Reset entry | table-data bootloader at 0xFFB4E8 |
program flash at 0xEF03C6 |
wrong path, right end state |
| Program flash IC4/IC6 | writable flash pair | .rom() region |
not modelled |
| Table data IC1/IC3 | mask ROM or flash — unresolved | .rom() region |
unknown |
| Custom data IC19 | AM29F400/800B-family flash | amd_29f800b_16bit flash device |
modelled — JEDEC autoselect returns device ID 0x2258 |
| FDC | µPD72068GF | UPD72067 device |
approximation |
| Floppy formats | 1.44 MB PC-format discs | MFM containers only, no FLOPPY_PC_FORMAT |
cannot mount .img |
| Sub-CPU DRAM strap | Port G bit 0 selects chip-select-3 sizing | hardcoded, sub-CPU port G unimplemented | one variant silently chosen |
| Region code | 4 values on a two-pin strap | AREA dip offers 3 |
Region 4 unreachable |
| HD-AE5000 window | firmware treats 0x280000 as banked |
flat 512 KB .rom() |
possibly a superset never exercised |
Gap 1 — the FDC and its disc formats
The cheapest blocker, and the one that gates any end-to-end test.
kn5000.cpp registers the floppy connector with
floppy_image_device::default_mfm_floppy_formats. That helper calls
format_registration::add_mfm_containers(), which adds MFI/HFE/TD0/IMD/86F/D88/CQM/DSK —
and not FLOPPY_PC_FORMAT. Only add_pc_formats() adds that. So the driver cannot
open a raw 1.44 MB .img, which is exactly the form every archived Technics update disc
takes. Mainline additionally still defaults the drive to 35dd, which cannot read an HD
disc at all; the working branch already uses 35hd.
Switching to default_pc_floppy_formats is a one-line change and is a prerequisite for
everything below.
Second, smaller issue. The driver instantiates UPD72067 with the comment that the
real part is a µPD72068GF-3B9. MAME’s own precedent for a 72068 is elsewhere
(akai/mpc2000.cpp uses UPD72069 with the comment “actually UPD72068, which is
software-identical”). The two device classes differ in auxcmd_w: upd72067_device
switches on data & 0x0f and its source comment says only the minimum needed for one
other driver’s diagnostic was implemented, whereas upd72069_device decodes the full byte
including the “control internal mode” data-rate command. Whether that matters here is
unmeasured — it depends on which aux bytes the KN5000 firmware actually writes to
0x110008, and nobody has logged a boot with a disc operation. Graded strong that the
device choice is questionable, undecided whether it is load-bearing.
Gap 2 — real flash devices
Three flash populations exist on the main board. IC19 (custom data) is a real flash
device; the program and table-data pairs are .rom(), by design.
| Window | Devices | Bus | MAME |
|---|---|---|---|
0x300000-0x3FFFFF |
IC19, one ×16 part (or two on Region 4) | 16-bit | amd_29f800b_16bit flash |
0x800000-0x9FFFFF |
pair of ×16 dies (table data) | 32-bit | .rom() |
0xE00000-0xFFFFFF |
pair of ×16 dies (program) | 32-bit | .rom() |
IC19 is an exact-match device. amd_29f800b_16bit_device is 0x100000 bytes, 16-bit,
manufacturer MFG_AMD, device ID 0x2258 — the right size, the right bus width, and an ID
the KN5000’s Flash_IdentifyAndValidateChip explicitly accepts. Its AMD command decoder
accepts the address form the firmware emits: the firmware unlocks at base + 0xAAAA and
base + 0x5554 (byte) = word 0x5555/0x2AAA, and intelfsh.cpp matches on
(address & 0xffff) == 0x5555 / 0x2aaa. With the device in place, Flash_InitAllBanks’s
boot-time JEDEC autoselect stores a valid device ID (0x2258) at DRAM 0x0205E0 instead of
the 0xFFFF a .rom() region reads back — the prerequisite for the firmware-update path.
(FUJITSU_29LV800B, ID 0x225b, would be rejected by this firmware, so the wrong
device silently breaks chip identification.)
The program and table-data pairs stay .rom() on purpose. Only custom data carries a
JEDEC ID the resident firmware consumes. Flash_InitAllBanks runs the autoselect on
table data too but reads and discards the result, and the program flash is never put
in command mode by resident firmware (the floppy updater reaches it by a different path).
Converting either would add regression risk for no boot-consumed benefit.
A trap for whoever extends this to the other two. Preloading an
intelfsh16device from a ROM region byte-swaps the contents.nvram_default()readsm_region->as_u16(offs/2)and stores it big-endian (m_data[offs] = v >> 8), whileread_raw()returnsm_data[offset*2] | (m_data[offset*2+1] << 8). AROM_REGION16_LEpreload of the IC19 dump therefore reads back wrong.
Open question that changes the shape of the work: are IC1/IC3 mask ROM or flash? The
service-manual parts list calls them ROM, but IC4/IC6 (program) and IC19 (known flash) all
share the same house number QV1GFKN5KAX1, the firmware has erase/program/verify handlers
targeting 0x800000, and the update-disc catalogue includes “Table DATA FILE” types.
Graded plausible, unresolved — and worth asking someone who can read the chip markings.
Is IC19 a top-boot or bottom-boot part? The firmware accepts both. MAME’s 16-bit AMD device is bottom-boot, so a drop-in gives bottom-boot geometry; the distinction only matters once the firmware starts erasing.
Gap 3 — the dynamic memory map
tmp94c241.cpp stores MSAR, MAMR and BnCS and never consults them: bNcs_w() is a
bare COMBINE_DATA, mamr_w/msar_w are plain assignments, and the members appear only in
the constructor, save_item(), device_reset() and the accessors. device_reset() fills
the arrays with 0xFF and sets m_block_cs[2] = 0x1000; //FIXME!. BnCSL/BnCSH are
mapped write-only, BEXCS is not mapped, and the DRAM-controller/PMEMCR registers at
0x160–0x167 are not mapped at all — so those firmware writes fall through the internal
map into the driver’s work DRAM.
The consequence for the KN5000 is that reset reads the vector from program[0x1FFF00] and
control starts at 0xEF03C6. The table-data first-stage bootloader never executes.
Everything reachable only through it is dead code under emulation: the boot FDC driver
(0x9FD8A5–0x9FEA9C), the 16-bit flash routines (0x9FB812+), Detect_Disk_Type
(0x9FBFC4), Boot_CheckDiskPresent, Boot_ProbeExternalDevice and the whole boot
CP-serial stack — none of which any regression would ever catch a mistake in.
The static map happens to equal the post-handover steady state, so this is accidentally
correct rather than actively wrong. One known deviation: only the upper of the program
flash’s two mirrors is modelled; the 0xC00000–0xDFFFFF mirror that the reconstruction
predicts is absent. (A cheap check before relying on the “accidentally correct” framing:
scan both 2 MB images for 24-bit immediates in 0xC00000–0xDFFFFF.)
What implementing it would take
Do not build a full chip-select decoder yet. The exact MSAR/MAMR decode rule is
not established — no datasheet exists on this project’s machines, and two mutually
incompatible calibrations were each reported as confirmed before the contradiction was
noticed. A decoder built on the wrong rule will not reproduce the one behaviour that
matters. See
TMP94C241 Memory Controller.
The one behaviour that matters is a single 4 MB window swap at the boot handover. A
memory_view with two states — table-data-high and program-high — reproduces it without
committing to a decode rule, and is the pragmatic option.
Two things will need rework when it lands:
- The driver already performs runtime installs:
machine_start()callsm_extension->program_map(m_maincpu->space(AS_PROGRAM)), and the HD-AE5000 card doesspace.install_device(0x000000, 0x2fffff, …)against the root space. If any of that range becomes the interior of a view it will be shadowed, and the install must move into the view slot or be re-applied on every switch. - The interrupt vector table is ROM-resident at
0xFFFF00and is never copied to RAM, so it swaps identity with the ROM. The view has to switch code and vectors atomically; a bootloader interrupt taken one cycle late would dispatch into the wrong ROM.
Resolve this contradiction first
There are two complete copies of the flash-update subsystem — one in the table-data
bootloader around 0x9FA000–0x9FFFFF, one in the program flash — and each can only
program the chip the other executes from. That is a natural consequence of the single CS2
swap: pre-handover 0x800000 is the program pair, post-handover it is the table pair.
But the program-flash copy of the type-007 handler decompresses stream 1 (the 2 MB main
program) to 0x800000, which post-handover is the table-data pair. Either that handler
is unreachable in practice — updates always enter through the bootloader, which owns the
boot FDC driver — or a second remap exists that nobody has found. Settle this before
designing the view, because a second remap would change its shape.
Dependency order
[1] FDC container formats (one line; nothing else can be tested without it)
│
├──> [2] IC19 flash device -- in place (amd_29f800b_16bit); the
│ 0x800000/0xE00000 pairs stay .rom() by design (see Gap 2)
│ │
│ └──> [4] run a type-007 install end to end from
│ kn5000_v10_disk.img, and diff the resulting
│ IC19 image against the predicted post-install
│ content (93,203 B stream + 68 x 0x00 + 37,801 x 0xE5)
│
└──> [3] update-mode entry (see below -- the real blocker)
[5] dynamic memory map (memory_view, two states)
- NOT a prerequisite for [4]: the type-7 handler that MAME can reach
lives in the program flash, which the static map already provides
- IS a prerequisite for a faithful cold boot and for exercising the
bootloader's own copy of the updater
- blocked on: resolving the two-updaters contradiction above, and
ideally on obtaining the TMP94C241 datasheet
Gap 3.5 — entering update mode is the real unresolved blocker. The update path is gated
on a sentinel byte at ROM 0xFFFFE8 being 0xFF (read by Get_Firmware_Version, 0xFFFEE5,
and tested in Boot_RunSelfTest, 0xEF0529) and, on hardware, on a panel button held through power-on. Which button, and
how to hold it from reset through the HLE’d kn5000_cpanel_device, is not established. This
is the same class of problem as the still-unsolved KN7000 self-test entry.
Even with all of that, the boot path has further gates that MAME cannot currently reach:
Boot_CheckDiskPresent (Port D bit 6, active low), BootSerial_Init, and
Boot_ProbeExternalDevice needing to return device class 4.
Secondary gaps
- Region 4.
Detect_Region_Codereads Port H bits 2 and 1;00= Region 4. The driver’sAREAdip offers only0x02/0x04/0x06, so Region 4 has never run under emulation. Its branches assume the IC19 slot holds two 512 KB devices with a+0x80000command base, and skip the HD-AE5000 factory init. Deferrable (the default is not Region 4) but it should be recorded in the driver. - Sub-CPU DRAM strap. Port G bit 0 selects
MAMR30x1F/0x0FandB3CSH0x8A/0x89— a genuine two-board-variant DRAM configuration. MAME hardcodes 1 MB at0x000000and does not configure sub-CPU port G at all, so the emulator silently picks one revision. - Sub-CPU CS2 window. MAME maps IC30 as 128 KB at
0xFE0000. Under one reading of the decode rule the CPU’s own chip select covers only 64 KB at0xFF0000— and all 4,352 non-0xFFbytes of the dump lie in that upper 64 KB, while the 2 KB read at0xFE0000came back blank. Worth confirming against the schematic before the full re-dump, because it bounds how much of IC30 can matter. - HD-AE5000 window banking. The firmware programs
0x280000in 8 × 128 KB banks selected through the 8255 PPI at0x160000, and reads anhkt_signature at0x2FFFC0with bank 7 selected.hdae5000.cppmaps a flat read-only 512 KB with no bank register. Possibly a superset never exercised in normal operation; unverified. - Bus widths.
BnCSHalso carries per-block data-bus width. MAME’s TLCS-900 core fixes the external width for the whole space viam_am8_16, and the KN5000 driver never callsset_am8_16for either CPU. Whether any block is genuinely 8-bit, and whether it matters, is unmeasured. - Keybed service test modes: not established. The factory keybed diagnostics (Tests 3–8,
mode codes
0xF5–0xFC) are entered by holding one key pair at power-on;SelfTest_FirmwareVersionCheckasks the sub-CPU for the held-key bitmap over the inter-CPU link (command0xF002) once the payload is running, and only Test 4 needs the checking device. Under emulation the sub-CPU sees the keybed throughkn5000_tonegen_device’s event FIFO (push_keybed_event,m_keybed_queue), whichkeybed_scanfeeds every millisecond from machine start; whether the sub-CPU’s reply then reflects keys held from power-on has not been measured, and no keybed test mode is on record as entered. These self-tests are among the cheapest fidelity evidence available, so one measured run settles it. See Test Modes.
Acceptance tests worth writing
Two of these are exact and mechanical, which is unusual and worth exploiting:
- Install replay. Boot with
kn5000_v10_disk.imgin the drive, run a type-007 update, then dump0x3E0000–0x3FFFFFand compare against the predicted post-install image: 93,203 bytes of SLIDE4K stream, 68 bytes of0x00, 37,801 bytes of0xE5. Any deviation is a bug in the emulated flash or FDC, not an interpretation dispute. - Bootloader liveness. Once the memory view exists, assert that the CPU executes at
0xFFB4E8before0xEF050F, and that a read of0xFFFEDCreturnsFF FF FF FFbefore theMSAR2store and1B 0F 05 EFafter it.