reference: level-1 host-port test — can it be skipped? (analysis, no skip)

Answers the five sub-questions from the service guide, the linear dis34 dump,
the existing reports, and one empirical capture:

* level 1 runs unconditionally at power-up; the boot calls the level-1 runner
  at bit 0x3F204400 (calla L3FDF72D0). The only conditional at the call site is
  the DUART1 IP3 hardware input bit (firmware @0x64000D0 bit 3), not a stored
  value.
* the host-port test has no 'not installed' sense; only SIMMs (@0x5000000) and
  the Image Option (@0x62000D0 bit 5) have presence checks.
* no persistent test-selection record: the power-up test set is a hard-coded
  ROM immediate (0x10FEFD80) written into a RAM descriptor; the interactive
  menu and NCP P3 only modify a run. NVRAM is read only by the user-settings
  code, never on the level-1 path.
* a level-1 failure wedges the boot in the board-LED code loop (0x3F208060,
  jruc itself); transcript = LEVEL1/2 TESTING, HOST PORT - 15, TYPE 'GO' TO
  CONTINUE, then the LED loop.
* the one in-ROM bypass (0x3F2043B0 -> OS cold-start 0x3F5788C0) is a
  board-input gate (IP3 high + no 0x55 signature); it skips all of level 1,
  not just the host port, and is not a firmware setting.

Verdict: no persistent/configurable skip; harness must pass level 1 or assert
DUART1 IP3 (0x0640000D bit 3) to take the OS branch.

Evidence + scratch: reference/l1probe/.
This commit is contained in:
SDD committed 2026-10-16 12:42:35 +03:00
1 parent 571850e564
commit c69d2c295f
3 files changed
+394

No files matched your search

+27
View File
@@ -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`).
+29
View File
@@ -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'
+338
View File
@@ -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 <p1> [<p2> <p3>]` 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
```