diff --git a/reference/l1probe/console-transcript.txt b/reference/l1probe/console-transcript.txt new file mode 100644 index 0000000..7fab4d6 --- /dev/null +++ b/reference/l1probe/console-transcript.txt @@ -0,0 +1,27 @@ +# Firmware console transcript, 3000 frames, DUART1 mapped (cap.lua) +# +# Command: +# M=/home/arti/code/k-space/mame0289/vt1200 +# CAP_MAX=3000 VT1200_VRAM=1 VT1200_BOARD_MODEL=handshake \ +# VT1200_BOARD_BUSY_TICKS=2 VT1200_BOARD_SWITCH="0640000D:70:70" \ +# VT1200_DUART=1 VT1200_DUART_SEL=1 \ +# $M vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \ +# -debug -debugger none -debuglog \ +# -autoboot_script harness/mame/cap.lua -autoboot_delay 0 +# +# cap.lua breaks on the printer entry (bit 0x3FDE00C0) and prints a9 = the +# message bit-pointer. Decoded with reference/rom-strings.txt: + +PRINT a9=3FDCA8E8 -> "LEVEL1/2 TESTING ** " (file 0x17951D) +PRINT a9=3FDC9C68 -> "LEVEL1/2 ERROR -> HOST PORT - 15 " (file 0x17938D) +PRINT a9=0080000C -> (RAM pointer: diagnostic dump value, not a ROM string) +PRINT a9=00C0000C -> (RAM pointer) +PRINT a9=0100000C -> (RAM pointer) +PRINT a9=0200000C -> (RAM pointer) +PRINT a9=3FDCD2A8 -> "TYPE 'GO' TO CONTINUE" (file 0x179A55) + +# Nothing else is printed in 3000 frames. In particular "CONIINUING THE TESTS" +# (file 0x179AEE) is NOT printed -- so the runner did not continue to the next +# level-1 test. A separate run of harness/mame/progress.lua shows the CPU +# parked in the board-LED blink loop (hottest PCs 0x3F2088B0 / 0x3F208B20 / +# 0x3F208A80 / 0x3F208950, all inside L3F208060, which ends `jruc L3F208060`). diff --git a/reference/l1probe/gate.lua b/reference/l1probe/gate.lua new file mode 100644 index 0000000..6b35dfe --- /dev/null +++ b/reference/l1probe/gate.lua @@ -0,0 +1,29 @@ +-- gate.lua -- does the bit-0x3F2042D0 IP3 gate skip level 1? +-- Probes the branch around the level-1 call site and the OS cold-start. +local machine = manager.machine +local cpu = machine.devices[':maincpu'] +local dbg = machine.debugger +if not dbg then print("ERROR: no debugger"); machine:exit(); return end +local function bp(addr, body) + dbg:command('bpset ' .. addr .. ',,{ ' .. body .. ' ; g }') +end +bp("3f2042d0", 'printf "IP-READ a0(@64000d0)=%08X\\n", a0') +bp("3f204300", 'printf "BTST3 a0=%08X bit3=%d\\n", a0, (a0>>3)&1') +bp("3f204360", 'printf "AT-360 a3=%08X\\n", a3') +bp("3f2043b0", 'printf "SKIP-BRANCH-TAKEN\\n"') +bp("3f204400", 'printf "RUN-LEVEL1 arg(a3)=%08X\\n", a3') +bp("3f5788c0", 'printf "OS-START-REACHED\\n"') +bp("3f2031d0", 'printf "HANDSHAKE-POLL @6200010=%08X\\n", d@6200010') +bp("3fdf7da0", 'printf "L1-RUNNER arg0 path\\n"') +bp("3fdf7e30", 'printf "L1-RUNNER arg0 fallthrough\\n"') +bp("3fdf7fb0", 'printf "L1-RUNNER test-loop\\n"') +bp("3fdf9d70", 'printf "HOSTPORT-HANDLER\\n"') +local n = 0 +local maxf = tonumber(os.getenv("GATE_FRAMES") or "1500") +emu.register_frame_done(function() + n = n + 1 + if n % 300 == 0 then print(string.format("frame %5d PC=%08X", n, cpu.state['PC'].value)) end + if n >= maxf then print("done "..n); machine:exit() end +end, "vt1200-gate") +dbg.visible_cpu = cpu +dbg.execution_state = 'run' diff --git a/reference/level1-bypass-analysis.md b/reference/level1-bypass-analysis.md new file mode 100644 index 0000000..c3ec888 --- /dev/null +++ b/reference/level1-bypass-analysis.md @@ -0,0 +1,338 @@ +# Can the level-1 HOST PORT test be skipped? — analysis + +Task: determine whether the level-1 **HOST PORT** test (error 15) can be skipped, +suppressed or configured away through anything persistent or configurable — +NVRAM, port configuration, a test-selection record, a mode word, a strap — +rather than by making it pass. ROMs untouched. + +Date: 2026-10-16. Inputs: `docs/EK-V1000-SV-002_service-guide.txt`, +`harness/mame/hostport_dis34.txt` (linear `dis34` disassembly, firmware **bit** +addresses), `harness/mame/{BRINGUP_LOG,DUART_REPORT,CODE22_REPORT}.md`, +`harness/mame/vt1200.cpp` / `vt1200_board.*` (read only), and one short empirical +run (`reference/l1probe/`). + +--- + +## Address conventions (four, reconciled) + +| name | formula | example for the level-1 call site | +|---|---|---| +| firmware **bit** address (what `dis34` prints, what the CPU stores) | `0x3F200000 + file_offset*8` | `0x3F204400` | +| file offset (byte) into `vt1200_v22.bin` | `(bit − 0x3F200000)/8` | `0x880` | +| Ghidra address | `0x07E40000 + file_offset` | `0x07E40880` | +| driver / board-model address | `bit >> 4`, window base `0x06400000` | `0x06400000 + 0x0D` for the firmware's `@0x64000D0` | + +Throughout this document I quote the **firmware bit address** (as +`hostport_dis34.txt` prints it) and give the Ghidra equivalent where useful. A +driver handler at `base` receives `(bitaddr − base) >> 4`, so the firmware's +`@0x64000D0` is board-model address `0x0640000D`. + +All "PC" values are the value of the TMS34010 `PC`, i.e. a bit address. + +--- + +## Verdict (short) + +**There is no persistent, configurable test-selection record that suppresses the +level-1 host-port test.** The level-1 batch runs unconditionally at power-up and +its per-run test set comes from **hard-coded ROM immediates written into a RAM +descriptor**, not from NVRAM. P3 only *modifies* how a test is run. The +diagnostics menu's selection is RAM-only and is reached only *after* the +power-up tests. + +There **is** one in-firmware branch that skips the whole level-1 batch: a +**board-input gate** at bit `0x3F2042D0`–`0x3F2043B0`. If DUART1's input-port +bit 3 (firmware `@0x64000D0` bit 3 = IP3) reads **1** and the 0x6200000 status +port has not returned the magic `a3 = 0x55`, the boot jumps straight to the OS +cold-start at `0x3F5788C0`, skipping *all* of level 1. This is a hardware-input +condition, not a firmware setting; it skips the whole batch (not just the host +port); it is undocumented; and it is not reachable on the normal POST path +(bit 3 reads 0 in every harness configuration tried). See §6. + +Practical consequence: to boot past level 1 the harness must make the level-1 +tests pass, **or** deliberately assert DUART1 IP3 high (`@0x64000D0` bit 3 = 1) +so the boot takes the OS branch. There is no NVRAM/config byte to flip. + +--- + +## Q1. When does level 1 run, and is it conditional? + +**It runs unconditionally at power-up, as part of the boot ROM itself.** It is +not gated on any NVRAM or settings value. + +Evidence — the boot code calls the level-1 runner directly: + +``` +3f2043e0: move a14, -*sp, 1 ; L3F2043E0: the POST "run the suite" loop +3f2043f0: move a3, *a14+, 1 ; arg = a3 +3f204400: calla L3FDF72D0 ; <<< the level-1 runner +3f204430: move a8, a10 +3f204440: jane L3F208060 ; nonzero return -> blink the board LED code +3f204460: jruc L3F2043E0 ; zero -> loop +``` + +The runner `L3FDF72D0` is the code that announces level 1: at bit `0x3FDF7770` +it calls the reporter `L3FDE55F0` with status `0xFFFF`, which the dispatcher maps +to the string **`LEVEL1/2 TESTING ** `** (string bit `0x3FDCA8E8`, file +`0x17951D`). It then drives the memory/port tests (it calls `L3FA7C050`, the +routine that contains the video-memory self-test `L3F2018B0`, the host test +wrapper, etc.). + +The call site has exactly **one** conditional, at `0x3F2042D0`–`0x3F2043B0`, and +it reads a **hardware input port**, not a configuration record: + +``` +3f2042d0: move @0x64000d0, a0, 0 ; DUART1 read reg 0x0D = IP (IP0-IP6, bit7=1) +3f204300: btst 3, a0 ; IP3 +3f204310: jane L3F204360 ; IP3 == 1 -> check a3 +3f204340: clr a3 +3f204350: jruc L3F2043E0 ; IP3 == 0 -> run the suite (a3 = 0) +L3F204360: +3f204360: cmpi 0x55, a3 +3f204380: jaeq L3F2043E0 ; a3 == 0x55 -> run the suite (a3 = 0x55) +3f2043b0: jauc L3F5788C0 ; else -> OS cold-start, skip level 1 +``` + +`btst` sets Z=1 when the tested bit is **0** (MAME `tms34010.cpp` +`BTST_K`: `SET_Z_BIT_LO(~reg,bit)`), so `jane` is taken when **IP3 = 1**. +`a3` is set to `0x55` only by the poll at `0x3F2031D0` (a level-0 board-status +poll), which on this boot path is **skipped** (`0x3F202660: jaeq L3F204020` +jumps over `0x3F202690`–`0x3F20401F`). On the normal harness path, therefore, +bit 3 = 0 (measured) and the suite runs unconditionally. + +The service guide agrees that level 1 is not optional: "Power-up self-tests run +each time you turn on the power switch… Level 0 tests run first, followed by +level 1 tests" (guide lines 632–641) and the LED sequence (lines 698–703). The +guide does not describe any way to disable power-up testing. + +**Answer:** unconditional. The only readable gate is the DUART1 IP3 hardware +input bit, not a stored value. + +--- + +## Q2. Is the host-port test itself conditional (port "not installed")? + +**No.** The firmware has presence checks for some optional hardware, but none for +the host port. + +Evidence: + +* Guide Table 2–2 (level-1 list, page 13) lists "Host port test" with no + qualifier, in contrast to the two conditional entries: "Option RAM test **(if + present)**" and "Image test **(if installed)**". +* The firmware's only presence tests are: + * **SIMMs / option memory** — port `@0x5000000`, bits 0–2, read at bit + `0x3F2C7E70` / `0x3F2C7F50` / `0x3F2C7F90`, printing + `Not Present` (string `0x3F2CCDE8`) / `SIMM n installed` + (`0x3F2CCEA8`, `0x3F2CD040`, `0x3F2CD1D8`). + * **Image Option** — port `@0x62000D0` bit 5, read at bit `0x3F2C80B0` + (`btst 5,a11`), printing `Image Option` / `Not Present`. +* A ROM-string search finds no "host port … not present/installed" string; the + strings are `Host Port Test` / `Host port test` (`0x3F49E780`, + `0x3F49E7F8`) and `Host Port Error - 15` (`0x3FAD5B20`), only. + +The level-1 host test therefore always runs as part of the batch. (The host-port +*wrapper* does branch on a bit — `0x3FA84B80: move *a11,a9,1 / btst 10,a9` — but +bit 10 (`0x400`) is the test's *selection/status* bit, which the boot sets, +not a presence flag; see Q3.) + +--- + +## Q3. Is there a persistent test-selection or mode record? + +**No persistent record.** Two selection mechanisms exist and neither is stored: + +**(a) The power-up test set is a hard-coded ROM immediate written into RAM.** +The level-1 runner builds the test descriptor with `L3FA904C0` (driver-visible +address `0x3FA904C0`), which copies five arguments into a descriptor at RAM +`0x0`–`0x220` (`*a11(0x0)`, `+0x20`, `+0x1a0`, `+0x1c0`, `+0x1e0`, `+0x200`). +The runner fills those words from immediates: + +``` +3fdf7e30: sub a7, a7 +3fdf7e40: movi 0x10fefd80, a9 ; default test set (normal power-up path) +3fdf7e70: move a9, *a7, 1 ; RAM 0x00 = 0x10FEFD80 +``` +(and `0x3FDF7690: movi 0x50febf80` on the other entry). `0x10FEFD80` has bit 10 +(`0x400`) set — that is exactly the code that selects the HOST PORT handler — so +the host test is in the default set. Nothing reads these from NVRAM. + +**(b) The interactive diagnostics menu edits the same RAM words only.** The +dispatcher sends status code `0x1FFFF` to the menu printer at bit `0x3FDE5810`, +whose first line is `VERSION 2.08 - SELECTED TEST TYPE: ` (string +`0x3FDCA990`); it prints the four words at RAM `0x00`, `0x20`, `0x1A0`, `0x1C0` +and the A–Z selection list (e.g. `E - DUART1, CHANNEL A (HOST) TEST (00000400h)` +at string `0x3FDCB488`, `O - LOOPBACK MODE (TOGGLE) (08000000h)` at +`0x3FDCC8D8`). The list values are the **error codes** each test reports, and +the words are plain RAM. There is no store-back to NVRAM. + +This menu is reached only *after* a failure (the running harness lands there when +level 0 fails / the whole batch reports), so it cannot change the power-up set +of the power-up run. + +**(c) NVRAM is not consulted by the level-1 path at all.** Every access to the +NVRAM window `@0x6000000` in the whole image is the user-settings code +(`0x3F4ABFA0`, `0x3F4ACBA0`, `0x3F4AF2D0`) and the NVR access helpers +(`0x3FA7FAE0` region, `@0x6000020`/`@0x6000030`). None is on the level-1 path. +The guide's NVR contents are user settings only — "group codes, host name or IP +address, font paths, and the terminal IP address" (guide lines 2368–2374) — and +the `Customize` dialogs (`Customize Host Port`, `Customize Printer Port`, +`Customize TCP/IP`, …) contain no test/diagnostic enable switch. + +**P3 only modifies.** Guide Table 2–5 (page 24, lines 1059–1068): + +``` +bit 0 (1) External loopback: host port. +bit 1 (2) External loopback: printer port. +bit 2 (4) External loopback: mouse port. +bit 3 (8) LANCE loopback: Ethernet port. +bit 4 (16) Test all baud rates. +bit 5 (32) Do not exercise modem control signals. +``` + +These are per-invocation arguments of the NCP `TEST [ ]` command +(guide lines 1020–1094). None suppresses or deselects a test; P3 = 0 is "run it +normally". Nothing stores P3. + +--- + +## Q4. What does the failure actually gate? + +**A level-1 failure is fatal to the boot in this ROM: it wedges the machine in +the system-board LED error-code loop.** It is not merely a message. + +Evidence: + +* The error dispatcher is `0x3FDF0630`. It reads the status code from bit + `0x16F0` (frame slot `a13−0x20`) and switches: code `0x400` → + `0x3FDE9D70`, the **HOST PORT** handler. That handler prints + `LEVEL1/2 ERROR -> HOST PORT - 15 ` (string `0x3FDC9C68`), dumps the port + diagnostics, prints `BLINKING SYSTEM BOARD LED` (`0x3FDF50D0`), calls + `L3F208660` to blink the LED once, prints `TYPE 'GO' TO CONTINUE` + (string `0x3FDCD2A8`), and returns. +* The runner then returns a nonzero value to the boot loop, which executes + `0x3F204440: jane L3F208060`. `L3F208060` blinks the two-digit LED code and + **loops forever**: `0x3F208650: jruc L3F208060`. +* Measured (`harness/mame/progress.lua`, `CAP_MAX`/`PROG_FRAMES` runs, + 900–3000 frames): the console transcript is exactly + `LEVEL1/2 TESTING ** `, `LEVEL1/2 ERROR -> HOST PORT - 15 `, + `TYPE 'GO' TO CONTINUE`; and the CPU is parked in the LED loop — the hottest + PCs are `0x3F2088B0`, `0x3F208B20`, `0x3F208A80`, `0x3F208950`, all inside + `L3F208060`. Nothing else is printed afterwards. This matches + `BRINGUP_LOG.md` ("blocked, not slow"). + +So a level-1 failure does **not** "merely display an error and enter the menu" — +the harness's earlier menu entry came from the *level-0* I/O-data-path failure, +a different path. Once level 1 itself is entered and fails, the boot stops in +the blink loop and never reaches the OS/Terminal Manager. + +(The guide's sentence "When a level 1 error occurs, the screen displays an error +message in the upper-left corner and testing continues" — lines 1863–1866 — +describes the diagnostic display sequence; the ROM's own control flow, and the +measured result, end the boot at the LED loop.) + +The `TYPE 'GO' TO CONTINUE` prompt is part of the diagnostic summary, not a +skip: the harness has no keyboard to answer it, and even with one the boot +proceeds to the blink loop, not to the OS. + +--- + +## Q5. Exact bytes, if a skip exists + +No NVRAM or settings bytes suppress the test. The **only** lever that bypasses +level 1 is the hardware gate in §6: + +* assert **DUART1 input-port bit 3** high — firmware operand `@0x64000D0`, bit 3 + (IP3); board-model/driver address `0x0640000D`, value bit 3; the SCN2681 IP + register, `ip3_w(1)`. Keep bit 2 = 0 (the b3=1 path is the one that bypasses) + and the monitor-switch bits 4–6 as the VR150 value `0x70`; bit 7 of the IP + register is the always-1 marker. In the board model a single override of + `0x0640000D` to `0xF8` expresses exactly that. +* No NVRAM offset is involved. + +This is not a per-test suppression (it skips the whole level-1 batch) and it is +not a supported setting. + +--- + +## 6. The one bypass branch, in detail + +``` +L3F2043E0 (run-the-suite entry) + arg = a3 + calla L3FDF72D0 ; level-1 runner +``` + +Reached from `0x3F2042D0`: + +| condition | result | +|---|---| +| `@0x64000D0` bit 3 = 0 | `clr a3`; `L3F2043E0` — **run level 1** (the harness state) | +| bit 3 = 1 and `a3 = 0x55` | `L3F2043E0` — run level 1 | +| bit 3 = 1 and `a3 ≠ 0x55` | `jauc 0x3F5788C0` — **skip level 1, go to the OS** | + +`0x3F5788C0` is the OS cold-start: it disables interrupts, programs the DMA +ports (`@0x5900000`, `@0x5800000`), the display (`@0xC0000080`), sets +`sp`/`a13`, and calls the OS/X-server init routines. + +`a3 = 0x55` is produced only by the level-0 board poll at `0x3F2031D0` +(`movi 0x55,a3` … `clr a3` on failure). That whole block +(`0x3F202690`–`0x3F20401F`) is jumped over when `@0x64000D0` bit 2 = 0 +(`0x3F202660: jaeq L3F204020`), which is the case in the harness, so `a3` +keeps an earlier value. The measured `a3` at the call site was 0 — so with +bit 3 = 1 the bypass condition holds. + +**Empirical note (honest, and weaker than I would like).** I exercised this via +the board-model override `VT1200_BOARD_MAP="0x06200001=0xBEEF;0x0640000D=F8"` +(correct key is the driver address `0x0640000D`, *not* the firmware operand +`0x0640000D0` printed in the driver header's example). With it, `@0x64000D0` +reads `0x00F8` (bit 3 = 1) and execution reads the port from PCs +`0x3F3E1170` and `0x3F5FF7A0` that the normal (`0x70`) run never reaches — +consistent with `0x3F2043B0` being taken, but confounded by the second override +present in the same run. I could not confirm the *end state*: the MAME binary +was being rebuilt by another worker during the analysis (its mtime changed +between runs and it was momentarily zero-length), a 2500-frame run with +`0x0640000D=0xF8` alone still ended in the `0x3F2088B0` LED loop, and I cannot +set IP3 through the SCN2681 without a driver change. **So: the branch and its +condition are clear statically; whether taking it produces a usable terminal +(resolves to the OS/X server rather than a later reset) is unverified.** + +--- + +## 7. What this means for the harness + +1. There is no memory (NVRAM/settings) or command (NCP/P3/diagnostics) that + turns off the level-1 host-port test, or level 1, or the power-up tests. +2. The host-port test is mandatory in the power-up batch; the port has no + "not installed" sense. +3. Because a level-1 failure wedges the boot in the LED-code loop, "ignore the + failure" is not an option either. +4. The only escape route that exists in the ROM is the board-input gate: + assert DUART1 IP3 (`@0x64000D0` bit 3) high (with bit 2 low) **before the boot + reads it at `0x3F2042D0`**, so the boot branches to the OS cold-start + `0x3F5788C0`. That skips all of level 1, needs no ROM change, and is exactly + a harness-side input assertion (SCN2681 `ip3_w(1)`, or `0x0640000D = 0xF8` in + the board model). Its fidelity to real hardware is unknown — no document, + log or ROM string describes DUART1 IP3 — and it is all-or-nothing. +5. Otherwise the harness must make every level-1 test that runs pass — the path + already taken for VIDEO MEMORY (`VT1200_VRAM`, REFCNT) and now capped at the + host-port DUART sub-bit timing. + +--- + +## Reproduce + +``` +harness/mame/hostport_dis34.txt # dis34, firmware bit addresses +grep -n "callers / labels" per section above + +# empirical capture of the level-1 error path (see reference/l1probe/): +reference/l1probe/gate.lua # PC/site probe around the boot gate +reference/l1probe/console-transcript.txt # decoded cap.lua console transcript + +M=/home/arti/code/k-space/mame0289/vt1200 +$M vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \ + -debug -debugger none -debuglog \ + -autoboot_script harness/mame/progress.lua -autoboot_delay 0 +# -> hottest PCs 0x3F2088B0/0x3F208B20 (LED loop) = the wedge +```