forked from arti/vt1200
The tablet was opt-in (VT1200_TABLET=1) with the mouse as the default. Invert
it: the tablet is selected with no environment, and VT1200_MOUSE=1 (a
value-aware inverse switch, so VT1200_MOUSE=0 selects the tablet) restores the
VSXXX-AA mouse.
The real terminal almost certainly has a mouse, so this is a DELIBERATE
divergence from the target machine, documented in README.md alongside
VT1200_SKIP_LEVEL1 and VT1200_IO_PATH_STUB. It is on by default because the OS
is only reachable through the skip gate and there the tablet's absolute reports
are what make the Terminal Manager usable.
Baselines re-measured, both unchanged:
* no-env progress.lua at 200 frames: PC=3FDCE400 samples=198 (3 PCs) -> same.
DUART2-B is never addressed in that window (the T/R exchange is OS-phase
only), so the reply bytes A0 04 00 00 vs A0 02 00 00 and the 5- vs 3-byte
framing do not reach the CPU.
* VT1200_SKIP_LEVEL1=1 frame 3000: pixel-identical for tablet and
VT1200_MOUSE=1 (magick AE=0); both reproduce the committed
tablet_cursor_*.png frames exactly.
VT1200_TABLET is now a no-op; run.sh's tablet mode is reported to the owner for
rewording (its comments are his).
393 lines
20 KiB
Markdown
393 lines
20 KiB
Markdown
# 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`).
|
||
|
||
## Working conventions
|
||
|
||
Five rules. Each of them has already cost this project real time, which is why they
|
||
are written down rather than assumed.
|
||
|
||
1. **Never search from `/`.** A filesystem-wide `find /` or `grep -r` is slow, visible
|
||
to whoever is at the keyboard, and never necessary. Use `rg` with an **explicit
|
||
path** — this directory, or `/home/arti/code/k-space/mame0289/src` (within which
|
||
`src/devices/machine`, `src/devices/bus`, `src/osd/modules/netdev` cover most
|
||
questions). Never `/usr`, `/opt`, `/home` at large, `/proc` or `/sys`. Worth
|
||
knowing why this keeps happening: `scn2681.cpp` **does not exist** (the SCN2681
|
||
lives in `mc68681.*`), so a search for it walks the entire disk before giving up.
|
||
|
||
2. **Stage only your own paths.** `git add <path> [<path>…]` — never `git add -A`,
|
||
never `git commit -a`. Several workers share this repository at once, and a `-A`
|
||
sweeps someone else's in-flight files into your commit. That happened twice in one
|
||
day. If it happens, unstage and `git commit --amend`, and say so.
|
||
|
||
3. **One sample is not evidence.** "It never paints", "the driver only models 256 KiB
|
||
of RAM", and "it is stuck" were all *wrong*, and all three came from a single
|
||
observation. Before concluding, either **compare across time** (several frames,
|
||
several minutes, several runs) or **read the source**. A claim about behaviour
|
||
needs more than one look; a claim about the code needs the code.
|
||
|
||
4. **Changing MAME requires a quote and a second reader.** Plausible-sounding
|
||
sentences get assembled to justify a number that was really found by trial and
|
||
error — one such edit was reverted after an independent reader found the datasheet
|
||
said the opposite. So: quote the sentence or the source line, add a **revert note**,
|
||
and have someone else read the citation before it is believed.
|
||
|
||
5. **Do not fake it.** No responder that feeds the firmware the answer a test wants.
|
||
Where a real device is unknown, model what the documentation actually supports and
|
||
**report the gap**; where a stand-in is genuinely unavoidable, make it **opt-in and
|
||
name it for what it is** — see `VT1200_IO_PATH_STUB` and `VT1200_SKIP_LEVEL1`, both
|
||
of which are documented divergences, not quiet assumptions. And the **ROM stays
|
||
byte-identical** (`md5 b46a3e442ccb86134d2356bd70d22fd2`): patch at runtime if you
|
||
must, never the file.
|
||
|
||
```
|
||
harness/mame/
|
||
├── vt1200.cpp # the MAME driver (symlinked into the MAME tree)
|
||
├── build.sh # clone-safe build wrapper
|
||
├── *.lua # probe / capture / dump scripts (many; see the reports)
|
||
└── roms/vt1200/ # MAME ROM dir (symlink to ../../vt1200_v22.bin)
|
||
```
|
||
|
||
Reports in this directory:
|
||
|
||
| file | what it records |
|
||
|---|---|
|
||
| `GATE_CLEANUP_REPORT.md` | promoting the working config to the default; retiring the settled env gates |
|
||
| `TABLET_REPORT.md` | the VSXXX-AB tablet: the self-test device-code ID, the 5-byte absolute report, and absolute host-pointer input |
|
||
| `RENDER_REPORT.md` | the active-pixel scanline renderer, and the geometry it derived |
|
||
| `DUART_REPORT.md` | the DUARTs: wiring, the ISR-driven transmit, the datasheet-verified model edits |
|
||
| `BRINGUP_LOG.md` | the whole bring-up narrative, **including withdrawn claims** — read it sceptically |
|
||
| `ROMPATCH_REPORT.md` | runtime ROM patching (debug only — see the caveat) |
|
||
|
||
## 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, 1, 3>`, i.e. the **address-map unit is 1 bit**
|
||
(a 16-bit word spans 16 address units). Source: `tms34010.h` — note the
|
||
TMS34020 differs (`memory_access<32, 2, 3>`), so this is *not* interchangeable
|
||
with the 34020. 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.
|
||
|
||
### What is modelled by default
|
||
|
||
This **changed** — see `GATE_CLEANUP_REPORT.md`. The working configuration used
|
||
to be a six-variable incantation; it is now the default, and a bare
|
||
`vt1200 vt1200` with **no environment** runs it.
|
||
|
||
* Video RAM at bit `0x04000000` — required for the level-1 VIDEO MEMORY test.
|
||
* The board-window handshake model (two-tick busy bit).
|
||
* MAME's `SCN2681` DUART at **DUART1** (`0x0640000`, host/printer), with input
|
||
port **IP4–IP6 held high** — that is how the driver reads the VR150 monitor
|
||
switch (`0x70`). The level-1 host-port transmit is **ISR-driven**, so a real
|
||
DUART is required here, not a convenience.
|
||
* MAME's `SCN2681` at **DUART2** (`0x0620000`, keyboard/mouse). This one is a
|
||
*faithfulness* fix, not a convenience — the real board has one. Without it the
|
||
board write-back latch answers a read of DUART2's **write-only** IMR with its
|
||
own written value, so the OS services a phantom interrupt that can never clear.
|
||
* The **real input devices** on DUART2, driven by MAME's host input: a
|
||
**DEC LK401 keyboard** (channel A, 4800 8N1) and a pointing device on
|
||
channel B (4800 8O1) — a **DEC VSXXX-AB tablet** (**the default**, absolute)
|
||
or, with `VT1200_MOUSE=1`, a **DEC VSXXX-AA mouse** (relative). All are
|
||
modelled from `reference/keyboard-mouse/PROTOCOL.md` and wired to the SCN2681
|
||
at the bit level (`rx_a_w`/`rx_b_w` and `a_tx_cb`/`b_tx_cb`). They answer
|
||
`FD`/`T` because that is the protocol, not to satisfy a test. See
|
||
`INPUT_REPORT.md` and `TABLET_REPORT.md`.
|
||
* A mono palette (pens 0/1 = black/white) and an active-pixel scanline renderer
|
||
that reads VRAM. See `RENDER_REPORT.md`.
|
||
|
||
A bare run gets through POST and then **fails level 0's I/O data path test**, so it
|
||
lands in the interactive diagnostics menu (`VERSION 2.08 - SELECTED TEST TYPE:`)
|
||
instead of reaching level 1.
|
||
|
||
That test wants **10 bytes** on DUART2 channel A and nothing is attached, so this
|
||
is an honest failure. **It used to "pass" by accident:** the write-back latch
|
||
returned `0x0099` from DUART2's status register, and bit 0 of `0x99` is `RXRDY` —
|
||
stuck asserted — so the firmware's drain loop counted ten phantom `0x8C` bytes.
|
||
|
||
**It is not satisfiable by any documented device** — see
|
||
`reference/level0-10byte-puzzle.md`. The routine writes exactly one byte (`0xE1`,
|
||
"disable auto-repeat", which has no reply), sets `MR2A=0x07` so the channel is in
|
||
**normal mode, not loopback** (confirmed statically *and* from a live access log),
|
||
and checks **only the count**: the received byte is stored and never compared, and
|
||
there is no expected-value table. The keyboard's `FD`/power-up reply is 4 bytes,
|
||
the mouse's `T` reply is 4, the tablet's 5 — none is 10. A faithful LK401 would not
|
||
help either: the init sequence's receiver-reset **flushes the FIFO**.
|
||
|
||
The real terminal *does* pass this test, so the bytes exist on real hardware — they
|
||
are simply not inferable from the ROM, and the only honest way to close the question
|
||
is to observe a real keyboard port at power-up. **No faithful responder can be
|
||
derived from any document.**
|
||
|
||
So instead there is an **opt-in stand-in**, `VT1200_IO_PATH_STUB=1`. Read what it
|
||
is carefully, because it is easy to misread:
|
||
|
||
* It is **not a keyboard model** and it is not any documented device. It stands in
|
||
for an **unknown test fixture on the keyboard port**.
|
||
* Its bytes are **arbitrary** (`0x00`). Nothing compares them: the routine stores
|
||
the received byte and never reads it back, and there is no expected-value table,
|
||
so only the **count** matters. Feeding a pattern would imply a protocol that does
|
||
not exist.
|
||
* It triggers on the exact thing the test does: the one `0xE1` write to DUART2-A
|
||
`THRA` (handler reg 3, window `0x0620000`, store at bit `3FDCE8B0`), then feeds
|
||
**10 bytes** back on the same channel's receive path via the SCN2681's own
|
||
`rx_fifo_push()`. It replies only while the channel is **polled** (`IMR = 0x00`,
|
||
the level-0 condition), so it cannot inject into the interrupt-enabled OS phase
|
||
or cause an interrupt there.
|
||
* It is **off by default**. A bare run still fails level 0 exactly as before and
|
||
says so. This is deliberate: the faithful configuration stays the default.
|
||
|
||
With the stub on the boot clears level 0 and reaches level 1, where the DUART
|
||
tests fail: `HOST PORT - 15`, then `PRINTER PORT - 16`. **It does not park at
|
||
`HOST PORT - 15`.** Each failure is reported, the board LED blinks once (finite),
|
||
the firmware prints `TYPE 'GO' TO CONTINUE` (which nothing answers and which does
|
||
not wait), and the runner **continues**. It then enables DUART1's counter-ready
|
||
interrupt and **wedges in a DUART1 interrupt storm** — that is the faithful
|
||
ceiling, not `HOST PORT - 15`. It does **not** produce a booted OS. See
|
||
`LEVEL1_CONTINUE_REPORT.md`.
|
||
|
||
There is **no infinite board-LED loop** on this path. `3F208060`/`3F208650` are
|
||
never executed here, and the boot loop's `3F204440 jane L3F208060` is never
|
||
reached (the level-1 runner never returns). The blink that is seen is the finite
|
||
`L3F208660` called by the error handler. A level-1 failure is fatal only through
|
||
`L3FDF1010 → L3FDF1270`, the genuinely infinite blink, which is entered when the
|
||
status word `*A13(0xFFA0)` is nonzero; the DUART failures leave it zero.
|
||
|
||
### How far the OS gets
|
||
|
||
With `VT1200_SKIP_LEVEL1=1` (below) the OS boots to a **running X11 server** and on
|
||
to the **Terminal Manager**: the root-window weave renders, the cursor is handed
|
||
over to the terminal's own mouse cursor, and the V2.2 box appears — title bar,
|
||
Messages, Ethernet/IP/LAT, a **Memory bar**, and "All Diagnostics Passed".
|
||
|
||
Confirmed in the GUI and from frame dumps: `skip_frame3000.png` (the weave) and
|
||
`duart2_default_tm_frame3000.png` (the Terminal Manager). The real terminal shows
|
||
the same sequence — see `reference/vt1200-boot-video/`.
|
||
|
||
The OS's keyboard and mouse drivers **initialise and speak the real protocol**: it
|
||
programs DUART2 (`MR1A=0x13`, `MR2A=0x07`, `CSRA/CSRB=0x99`, `ACR=0x60`,
|
||
`IMR=0x33`) and sends `FD` plus LED/click/bell/auto-repeat to the keyboard and `'T'`
|
||
to the mouse. **Input now works**: the LK401 and VSXXX-AA device models answer
|
||
those commands, the OS reads `01 00 00 00` / `A0 02 00 00`, switches the mouse to
|
||
stream mode, and the X11 cursor moves with the mouse while keycodes reach the OS.
|
||
See `INPUT_REPORT.md` (the cursor evidence is the `input_cursor_*.png` frames).
|
||
|
||
The channel-B device is **by default** the **VSXXX-AB tablet**: it identifies
|
||
as device code `0100`, so the OS switches to 5-byte absolute reports, and the
|
||
emulated cursor follows the **host pointer's absolute position** (driven through
|
||
`IPT_LIGHTGUN_X/Y`, i.e. MAME needs `-lightgun -lightgun_device mouse`). See
|
||
`TABLET_REPORT.md` (evidence: `tablet_cursor_*.png`). **`VT1200_MOUSE=1`** puts
|
||
the VSXXX-AA mouse back (device code `0010`, 3-byte relative reports). The mouse
|
||
is the *faithful* part — the real terminal almost certainly has a mouse — so the
|
||
tablet default is a **deliberate divergence** (below).
|
||
|
||
Not modelled: **LANCE Ethernet**. It does not block the X11 bring-up.
|
||
|
||
### Getting past level 1
|
||
|
||
`VT1200_SKIP_LEVEL1=1` drives DUART1's **IP3** high, which the firmware takes as
|
||
a signal to jump to the OS cold-start and skip **all 15** level-1 tests. Note
|
||
what this is: a *modelled board input*, and the real machine reads IP3 **low**.
|
||
So a level-1-skipped run is evidence about the **OS, graphics and crash
|
||
behaviour**, and is **not** evidence about the real terminal's POST.
|
||
|
||
The skip is deliberately **not** the default: the real machine runs level 1, so
|
||
the faithful configuration stays the default and the switch is how we diverge.
|
||
|
||
### Deliberate divergences from the target machine
|
||
|
||
Three harness behaviours are **not** what the real terminal does. They exist so
|
||
the emulator can be driven and inspected, and they are collected here so a
|
||
reader cannot mistake the harness for the machine:
|
||
|
||
| divergence | default | what it does | how to restore the real machine |
|
||
|---|---|---|---|
|
||
| **VSXXX-AB tablet** | **on** | puts the tablet, not the mouse, on DUART2-B; absolute reports let the host pointer drive the cursor positionally | `VT1200_MOUSE=1` |
|
||
| `VT1200_SKIP_LEVEL1` | off | drives DUART1 IP3 high, jumping past all 15 level-1 tests to the OS cold-start | leave it unset |
|
||
| `VT1200_IO_PATH_STUB` | off | feeds 10 bytes on DUART2-A after the level-0 `0xE1` write, so the count-only I/O data path test passes | leave it unset |
|
||
|
||
The tablet is the one that is **on by default** because the OS is only reachable
|
||
through the skip gate, and there the tablet's absolute pointer is what makes the
|
||
Terminal Manager usable. It is a **value-aware inverse switch**: the default
|
||
lives in the code, not in a wrapper script setting `VT1200_TABLET`, so
|
||
`VT1200_MOUSE=1` (or `VT1200_MOUSE=0` for the tablet) always flips it either way.
|
||
|
||
## Environment knobs (19; down from 33)
|
||
|
||
Almost all are opt-in and off by default — there is no longer a "working
|
||
incantation" to remember, because everything the firmware needs to run is now the
|
||
default. The one exception is that the **tablet is on by default**; `VT1200_MOUSE`
|
||
is the switch that restores the faithful mouse (see the divergences above).
|
||
|
||
Instrumentation: `VT1200_DUARTLOG`, `VT1200_BOARD_LOG`, `VT1200_BOARD_MAP`,
|
||
`VT1200_IRQLOG`, `VT1200_LOG_UNMAPPED`, `VT1200_DPYLOG` (+`_FRAMES`),
|
||
`VT1200_DUMP_FRAME` (+`_FRAMES`), `VT1200_DUMP_RAM` (+`_FRAMES`),
|
||
`VT1200_DUMP_VRAM` (+`_FRAMES`), `VT1200_DUMP_DIR`, `VT1200_NO_RENDER`,
|
||
`VT1200_INPUT_LOG` (per-byte keyboard/mouse/tablet TX/RX trace, with emulated
|
||
time). Behaviour switches: `VT1200_SKIP_LEVEL1`, `VT1200_MOUSE` (put the
|
||
VSXXX-AA mouse (relative) back on DUART2-B; the VSXXX-AB tablet is the default —
|
||
either pointing device needs MAME's `-lightgun -lightgun_device mouse` to follow
|
||
the host pointer), `VT1200_IO_PATH_STUB` (the level-0 test-fixture stand-in
|
||
described above — not a keyboard model, off by default).
|
||
|
||
## Regression baseline
|
||
|
||
`progress.lua` at 200 frames, **no env**:
|
||
|
||
```
|
||
PC=3FDCE400 samples=198 (3 distinct PCs)
|
||
```
|
||
|
||
This has moved **twice**, both deliberately:
|
||
|
||
1. `samples=199` (1 PC) → `samples=53` (78 PCs): the working configuration became
|
||
the default, so a bare run stopped wedging at the hardware-ready gate and began
|
||
executing real POST work.
|
||
2. `samples=53` (78 PCs) → `samples=198` (3 PCs): DUART2 became a real `SCN2681`.
|
||
Level 0's I/O data path test now **fails honestly** instead of being satisfied
|
||
by the write-back latch's phantom `RXRDY`, so the run parks in the diagnostics
|
||
menu. That looks like a regression and is not one — it is the removal of a
|
||
false pass.
|
||
|
||
**The tablet-default change did NOT move this baseline** (198 → 198), and that is
|
||
expected, not an oversight. The tablet changes what DUART2-B carries on the wire
|
||
— its `T` self-test reply is `A0 04 00 00` versus the mouse's `A0 02 00 00`, and
|
||
it then frames 5-byte absolute reports instead of 3-byte relative ones — but at
|
||
200 frames a **no-env** run is still parked in the level-0 diagnostics menu and
|
||
has never addressed DUART2-B at all: the `T`/`R` exchange happens only in the OS
|
||
phase, which the no-env run does not reach. The only DUART2-B traffic in the
|
||
window is the device's unsolicited power-up self-test, which nothing clocks in.
|
||
The OS-phase render is unchanged too: with `VT1200_SKIP_LEVEL1=1` the frame-3000
|
||
dump is **pixel-identical** for the tablet and for `VT1200_MOUSE=1`
|
||
(`magick compare -metric AE` = 0), and both reproduce the committed
|
||
`tablet_cursor_*.png` frames exactly.
|
||
|
||
**The no-env value is unchanged by `VT1200_IO_PATH_STUB`** — stub off is the
|
||
default, so the `samples=198 (3 PCs)` line above still holds. The stub is a separate
|
||
observed configuration:
|
||
|
||
```
|
||
# VT1200_IO_PATH_STUB=1, progress.lua at 200 frames
|
||
PC=3FDCE400 samples=53 (75 distinct PCs)
|
||
```
|
||
|
||
That is a genuine move, not a false pass: with the count-only level-0 test
|
||
satisfied, the run leaves the level-0 wait and executes level-0/level-1 work. It
|
||
is **not** parked at `HOST PORT - 15`: the console transcript continues
|
||
(`LEVEL1/2 TESTING **`, `LEVEL1/2 ERROR -> HOST PORT - 15`,
|
||
`LEVEL1/2 ERROR -> PRINTER PORT - 16`, `TYPE 'GO' TO CONTINUE`), and the run ends
|
||
in a DUART1 counter-ready **interrupt storm** (from frame ~2839; PC in the DUART1
|
||
INT1 ISR, polling ISR reg 5 = `0x08`). See `LEVEL1_CONTINUE_REPORT.md`. It is
|
||
recorded as a *different* baseline because it is opt-in; the default must stay
|
||
`samples=198`.
|
||
|
||
It is a change detector, not a specification. If it moves without you meaning it
|
||
to, find out why.
|
||
|
||
## Build
|
||
|
||
MAME source is required. A shallow 0.289 checkout is large and slow to fetch.
|
||
|
||
```sh
|
||
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.)
|
||
|
||
```sh
|
||
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.
|
||
|
||
### Drive the keyboard and mouse (`input_inject.lua`)
|
||
|
||
The LK401/VSXXX-AA models read MAME's host input, so they can be driven from
|
||
Lua for a headless acceptance run:
|
||
|
||
```sh
|
||
M=/home/arti/code/k-space/mame0289/vt1200
|
||
VT1200_SKIP_LEVEL1=1 \
|
||
VT1200_DUMP_FRAMES=4400,4600 VT1200_DUMP_DIR=/tmp \
|
||
$M vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \
|
||
-debug -debugger none \
|
||
-autoboot_script harness/mame/input_inject.lua -autoboot_delay 0
|
||
```
|
||
|
||
`input_inject.lua` walks the mouse from frame 4400 and taps `A` at frame 4450.
|
||
`input_probe.lua` prints every device port/field tag. See `INPUT_REPORT.md`.
|
||
|
||
### 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.
|