Files
vt1200/reference/level1-bypass-analysis.md

365 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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?
> **CORRECTED 2026-10-16 — the answer below is WRONG.** Kept as the record of what
> we believed; the corrected account is `harness/mame/LEVEL1_CONTINUE_REPORT.md`.
>
> **A level-1 failure is NOT fatal.** The runner reports it and **continues**. The
> stub run (`VT1200_IO_PATH_STUB=1`) shows: `HOST PORT - 15` fails at frame 484 →
> a **finite** blink (`L3F208660`) → `TYPE 'GO' TO CONTINUE` at 1534 → continues →
> `PRINTER PORT - 16` fails at 1627 → blinks → continues at 2759 and **draws the X11
> weave** → then wedges at 2839, on a DUART1 *counter* interrupt storm.
>
> Measured: `0x3F204440`, `0x3F208060`, `0x3F208650` and the infinite blink
> `L3FDF1270` each fire **0 times in 20,000 frames**, and the runner never returns
> to the boot loop. The blink actually seen (`0x3F2088B0/…`) is inside the
> **finite** `L3F208660`.
>
> The real fatal route is `L3FDF1010 → L3FDF1270`, entered only when the status word
> `*A13(0xFFA0)` is **nonzero** — and the DUART path leaves it zero.
>
> `TYPE 'GO' TO CONTINUE` is **printed, not waited on**: nothing answers it, no
> keypress is read, and no timeout is involved. The "it continues after some time"
> seen on screen is simply the ~1050-frame blink (**~15 s** at 72 Hz).
>
> This matters beyond the correction: level 1 is **not** the hard blocker this
> document assumed, so the skip gate is a fast path rather than a necessity.
**The superseded answer was:**
**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
```