The terminal's settings NVR was not modelled: 0x6000000 fell inside the video-memory mapping (0x04000000..0x1fffffff), so NVR reads returned stale framebuffer words and writes were lost. That is the owner's 'User settings invalid' plus 'settings do not persist' (and the font path is stored here). Map a persisted NVRAM over the 0x800-word settings block; MAME now saves and reloads it (nvram/vt1200/nvram), verified write->restart->read. No MAME-tree change.
6.7 KiB
The NVR (non-volatile settings RAM) — model gap and fix
Date: 2026-10-16. Firmware vt1200_v22.bin md5
b46a3e442ccb86134d2356bd70d22fd2 (unmodified). No MAME-tree change.
Verdict (short)
- The terminal's NVRAM/settings live at bit
0x6000000(physical byte0xC00000): a 16-bit-wide region whose user-settings block is 0x800 bytes, one byte per 16-bit word (i.e. 0x800 words). - Our model did not model it at all. That address lies inside the driver's
video-memory mapping (
0x04000000..0x1fffffff), so every NVR read returned whatever unrelated VRAM happened to hold and every NVR write went into the volatile VRAM array and was lost on reset/exit. This is the same "write it somewhere that throws the value away" pattern that produced the phantom DUART interrupt and the phantom level-0 pass. - Fix: a persisted
NVRAMdevice mapped over the video-memory entry (map(0x6000000, 0x6007fff).ram().share("nvram")+NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1)). It is saved to / loaded from MAME's nvram directory (~/.mame/nvram/vt1200/nvram, 4096 bytes), so it survives a full restart, not just a reset — mechanically verified below. - The font path is a setting in this NVR (service guide, quoted below), so with the region modelled it has somewhere to live. The font data is not here.
What the service guide says
docs/EK-V1000-SV-002_service-guide.txt:
Nonvolatile RAM (NVR) on the system logic board stores the user's customized settings, including group codes, host name or IP address, font paths, and the terminal IP address.
The terminal stores feature settings in nonvolatile RAM (NVR) memory. NVR retains the settings when the terminal is turned off, so the terminal can restore the settings when power is turned on again.
(There is also a level-0/level-1 "NVR Checksum Test", error 14.)
The firmware's side (address, size, format)
Two routines, both already named in reference/memory-space-map.md row 7:
nvr_user_settings_validate(bit0x3F4ABE50, byte0x07E957CA):MOVI 0x800 / MOVI 0x1a0020 / MOVI 0x6000000 / CALLA io_settings_copy_expand— copies 0x800 bytes from0x6000000into RAM0x1a0020;- sums the bytes, compares the sum against a stored value, then checks a
0x55/0xAAmagic; on a mismatch it printsUser settings invalid.(bit0x3F4B5FC0) orOld user settings layout.(bit0x3F4B5EF0).
- the save routine (bit
0x3F4AC880, byte0x07E95910): runs the validator, computes a checksum over RAM0x1a0020, thenMOVI 0x800 / MOVI 0x1a0020 / MOVI 0x6000000 / CALLA io_settings_copy_linear— copies 0x800 bytes RAM →0x6000000, one settings byte per 16-bit word (io_settings_copy_linear:MOVB *A7,A12thenSETF 0x10andMOVE A12,*A0+, so the word stride is 16 bits).
So 0x800 words is the right size for the settings block; larger reads/writes
were never observed (the only absolute operands pointing here are the two above).
The bug in the harness
program_map maps video memory over the whole 0x04000000..0x1fffffff range,
and 0x06000000 is inside it. There was no entry for it, and the board windows
(0x05800000, 0x05900000, 0x06200000, 0x06400000) and the MAC ROM
(0x06800000) do not cover it. So all NVR traffic fell through to the vram
share: reads returned stale framebuffer words (which fail the checksum), writes
were accepted but evaporated. Nothing wrote a .nv file and nothing was
reloaded, which is exactly the owner's "User settings invalid" + "settings do
not persist".
The fix
// program_map(), registered AFTER the video-memory mapping so it wins:
map(0x6000000, 0x6007fff).ram().share("nvram");
// machine_config:
NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_1);
nvram_device holds the same-named share and writes it to
<nvram_directory>/vt1200/nvram on exit.
Does it persist? — mechanically, yes (verified)
A write through the CPU to NVR word 0x700, then a full MAME restart:
# process 1 (write):
prog:write_u16(0x6007000, 0xBEEF) -> file ~/.mame/nvram/vt1200/nvram @0xE00 = ef be
# process 2 (fresh run):
prog:read_u16(0x6007000) -> 0xBEEF
So the region is saved at exit and reloaded at start — settings would survive a restart, not merely a soft reset. (Baselines unaffected; see below.)
What I could not demonstrate, stated plainly
The OS itself writes the NVR during cold-start: in a VT1200_SKIP_LEVEL1=1 boot
the whole block goes FF…FF (the power-on default) → 00…00 at frame 144. On a
blank NVR that is a zeroed (still-invalid) block, which is why the Terminal
Manager still prints User settings invalid. / System defaults loaded. — that
is the correct first-boot behaviour, not a bug. I could not drive the
Customize → Save Current Settings flow from a headless script, so the
firmware's own save → restart → load round trip is not demonstrated here.
The owner can settle it in one step: set the font path, Save Current
Settings, quit, restart, see whether it comes back. If it does not, the next
suspect is the save path's own initialisation of the block (the frame-144 write),
not the region's existence.
The font cache is not in the NVR
- The font path is a setting here (see the service guide quote). The font
data is not: 2 KiB cannot hold it, and the firmware never touches
0x6000000beyond the 0x800-word settings block. - There is no disk/FS device anywhere in the address map
(
reference/memory-space-map.md): the terminal's storage is main RAM + VRAM + this NVR. A downloaded font would live in main RAM. - The string
Disk full or allocation exceededis used in exactly one place: the message for TFTP error code 3 in the terminal'stftp_readERROR-packet handler (bit~0x3F5EDB30; the error-message table is at file0x1A7028). So on that path the terminal prints TFTP error 3 rather than generating it from a local storage API. Whether the error 3 arrives from the host's TFTP server or from the terminal's own server needs a packet capture; the code does not settle it. (Noteharness/serve.pyis being modified by another worker — that may be the relevant server.)
Baselines (unchanged by this fix)
progress.lua200 frames, no env:samples=198 (3 distinct PCs).VT1200_IO_PATH_STUB=1:samples=53 (75 distinct PCs).VT1200_SKIP_LEVEL1=1, frame-3000 dump reproducesduart2_default_tm_frame3000.png(AE = 1826 — the cursor only).
The .nv file is written under the default nvram directory
(~/.mame/nvram/vt1200/nvram); nothing of the owner's existed there before this
change, and no existing .nv was deleted.