mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-31 12:05:05 +02:00
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/.
339 lines
16 KiB
Markdown
339 lines
16 KiB
Markdown
# 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
|
||
```
|