Files
vt1200/harness/mame
SDD 72a96862c7 docs: causality audit -- the host-port failure is pre-existing, not caused by the two MAME edits
Four-config A/B (IRQ wiring kept in all four; both mc68681 edits reverted in cfg 4):
the console line and L3FA6E4B0's return are identical (HOST PORT - 15 / 6) in cfg 1-4.
Verdict: PRE-EXISTING.  Neither timing edit causes or relocates the failure.

Methodology correction: hp_ret.lua's labels were on the wrong rets addresses.  The real
returns are L3FA6E4B0 -> 6, L3FA71BC0 -> 0x00900040, L3FA753D0 -> 0x00000408,
L3FA767E0 -> 0.  hp_ret2.lua fixes the addresses.

What 6 is: not a device code.  L3FA6E4B0 reduces five descriptor fields (struct+0x60/
0x80/0xa0/0xc0/0xe0, written from the four sub-test calls) to a 5-bit value; 6 = 0b00110
= "L3FA71BC0 nonzero, L3FA753D0 nonzero".  The caller turns any nonzero into 0x400,
which the dispatcher maps to HOST PORT - 15.  Flagged: L3FA753D0 returns 0x408 while all
its own checks pass, so "nonzero" != "failed its checks"; polarity unresolved.

Leads: COUNTER_READY is set by the firmware's own Start-Counter-Command read (reg 0x0E),
is not in IMR, and is not handled by the ISR -- not the cause.  The DUART clock
(3.6864 MHz) could not be confirmed from the service guide.

Variant MAME files kept in harness/mame/mame_edits/.  Regression holds.
2026-10-16 12:13:39 +03:00
..

VT1200 execution harness (MAME)

A minimal MAME machine that loads the VT1200 firmware into a TMS34010 CPU so we can run it under MAME's debugger: instruction trace, breakpoints, single-stepping, and direct memory watch. It is a visibility tool, not an accurate VT1200 emulation.

Everything in this directory is source-of-truth in this repo; the MAME source tree is disposable and lives outside the project (/home/arti/code/k-space/mame0289).

harness/mame/
├── vt1200.cpp        # the MAME driver (symlinked into the MAME tree)
├── build.sh          # clone-safe build wrapper
├── trace.lua         # Lua: trace execution from the reset vector
├── overflow.lua      # Lua: exercise the pcfReadBitmaps n*4 overflow
├── FINDINGS.md       # results, evidence, and what was learned
└── roms/vt1200/      # MAME ROM dir (symlink to ../../vt1200_v22.bin)

Address model (the important part)

The TMS34010 is bit-addressed: a register/PC holds a bit address, and a pointer P refers to physical byte P >> 3. MAME models this with a program space of memory_access<32, 16, 3>, i.e. the address-map unit is 1 bit (a 16-bit word spans 16 address units). Therefore:

  • Address-map entries here are bit addresses, not byte addresses.
  • read_word(0x3f200000) returns ROM bytes 0..1 (physical byte 0x07e40000).
  • The reset vector is read from logical bit 0xffffffe0, which maps to the last 4 bytes of the image (file offset 0x1bfffc).

The driver maps:

region bit range bytes
RAM 0x00000000–0x001fffff 4 MiB at byte 0
ROM 0x3f200000–0x3fffffff 1.75 MiB at byte 0x07e40000
ROM aliases 0x7f…, 0xbf…, 0xff200000–0xffffffff same ROM (top 2 logical bits not decoded)

The 0xff… alias is what makes the reset-vector page resolve. Board peripherals (LANCE Ethernet, DUARTs) are not modelled; their reads return 0xffff and writes are dropped. The terminal will therefore not boot to the X server — that is expected and not the goal.

Build

MAME source is required. A shallow 0.289 checkout is large and slow to fetch.

git clone --depth 1 --branch mame0289 \
    https://github.com/mamedev/mame.git /home/arti/code/k-space/mame0289

harness/mame/build.sh            # symlinks the driver in and runs make

build.sh runs make -j$JOBS SUBTARGET=vt1200 SOURCES=src/mame/dec/vt1200.cpp, which builds a single-driver binary /home/arti/code/k-space/mame0289/vt1200.

The MAME tree is large and the build is memory-hungry: build.sh defaults to -j8, not all cores. Override with JOBS=4 harness/mame/build.sh on a small/low-memory machine. Progress is written to harness/mame/build.log.

Run

Headless execution and inspection use MAME's Lua debugger API from -autoboot_script. (-debugscript only works when a debugger UI drives the console, so it is not usable headless.)

cd /home/arti/code/k-space/vt1200
M=/home/arti/code/k-space/mame0289/vt1200

# trace execution from the reset vector
$M vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \
   -debug -debugger none \
   -autoboot_script harness/mame/trace.lua -autoboot_delay 0

# demonstrate the pcfReadBitmaps n*4 overflow (OVF_N overrides n, decimal)
$M vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \
   -debug -debugger none -debuglog -seconds_to_run 5 \
   -autoboot_script harness/mame/overflow.lua -autoboot_delay 0

The Lua API exposes manager.machine.debugger, device.debug (step, go, bpset, wpset), CPU registers (cpu.state['A0'].value), memory (cpu.spaces['program']:read_u32/write_u32), and frame hooks.

Trace from the reset vector (trace.lua)

Prints the reset PC (expect 3F200040), disassembles the power-on code to reset.dasm, and writes an instruction trace of the first two frames to vt1200-reset.trace.

Exercise the pcfReadBitmaps overflow (overflow.lua)

Instead of booting the parser we jump directly into its arithmetic block with a crafted in-memory PCF_CharInfo:

a9  = 0x00040000   ; fake pCS;    pCS->nChars at a9+0x270
a13 = 0x00060000   ; fake arg blk; n (from pcfReadInt32) at a13+0x20
write_u32(0x00040270, 0)        ; nChars == 0
write_u32(0x00060020, n)        ; n
pc = 3f984e70
break at 3f985060               ; just after 'sll 0x2,a11'

This is possible because the font parser's field moves use field 1, which SETF 0,0,1 leaves at 32 bits, so n is a full 32-bit pcfReadInt32 value. See FINDINGS.md for results and the rest of the analysis.