diff --git a/harness/mame/NETWORK_REPORT.md b/harness/mame/NETWORK_REPORT.md index 130c789..f58b331 100644 --- a/harness/mame/NETWORK_REPORT.md +++ b/harness/mame/NETWORK_REPORT.md @@ -4777,3 +4777,56 @@ the screen blank while it reprograms the video window. If a faster return than that is ever needed, the alternative is not a reboot but a hand-back of the chip while the OS keeps running (section 64): the display and the OS survive, the network does not. + +## 73. The real reboot target: the OS cold-start entry, not the reset vector + +Section 72.4 established that the firmware has no reboot routine and that +jumping at the reset vector wedges the machine (0 for 2). But the reset vector +is the *wrong* entry: it is the start of POST, which reprograms the board window +from a state a payload has already changed. The firmware's own boot has a second +entry, the **OS cold start at `0x3F5788C0`** — the address the boot gate at +`0x3F2042D0` branches to when it skips the level-1 batch (`vt1200.cpp:134`). + +That entry starts by fixing exactly what a payload breaks +(`rom:0x07eaf118`, bit `0x3F5788C0`): + +``` +07eaf118 DINT +07eaf11a CLR A10 +07eaf11c SETF 0x0,0x0,0x1 +07eaf11e SETF 0x10,0x0,0x0 +07eaf120 CLR A0 +07eaf122 MOVE A0,@0x5900000 ; LANCE RAP = 0 +07eaf128 MOVI 0x4,A0 +07eaf12c MOVE A0,@0x5800000 ; CSR0 = 0x0004 = STOP <- quiet the chip +07eaf132 MOVI 0xC,A0 +07eaf136 MOVE A0,@0xc00000b0 ; board window = 0x0C +07eaf140 MOVI 0x3f57aa70,A2 ; then re-program the board tables +07eaf146 MOVI 0xc0000080,A3 +07eaf14c MOVE *A2+,*A3+,0x0 ; (3 words) +07eaf156 MOVI 0x3f57aaa0,A2 ; and the next table to 0xc0000130 +``` + +`DINT`, `CSR0=STOP`, board window — in that order. This is the path that is +designed to run when the machine is handed over, and it is *fast*: no POST. + +`lance_cmd.c` exposes it as command 6, `osboot`. + +### 73.1 MAME: the OS re-initialises the chip + +`VT1200_LANCE_DMA_LOG`/`VT1200_LANCELOG` shows the handoff and the vendor's own +re-init, with the payload's last access and the OS's first access 130 µs apart: + +``` +t=11.823527 W RDP val=0048 pc=018C0E40 <- last payload access +t=11.823657 R RDP val=02F3 pc=3F9D5BC0 <- OS LANCE handler +t=11.828933 W RDP val=0004 pc=3F578940 <- CSR0 = STOP, inside 0x3F5788C0 +t=11.828960 W RDP val=0004 pc=3FDDCAE0 <- OS driver (io5800_5900_init) +``` + +and the OS keeps servicing the chip afterwards (`pc=3F9D*`, 134 accesses in the +window). So the cold start restores the LANCE and the OS continues. + +### 73.2 Real unit + +Result recorded in 73.3 once the unit run completes. diff --git a/harness/udpstream/build/lance_cmd.coff b/harness/udpstream/build/lance_cmd.coff index 7d6b6f5..e5aa1bc 100644 Binary files a/harness/udpstream/build/lance_cmd.coff and b/harness/udpstream/build/lance_cmd.coff differ diff --git a/harness/udpstream/lance_cmd.c b/harness/udpstream/lance_cmd.c index 556ee69..0440443 100644 --- a/harness/udpstream/lance_cmd.c +++ b/harness/udpstream/lance_cmd.c @@ -445,6 +445,18 @@ int main() } wb(TXBUF + 19 * 8, (wlen >> 8) & 0xFF); wb(TXBUF + 20 * 8, wlen & 0xFF); + } else if (cmd == 6) { + /* OS cold start: the entry POST branches to at 0x3F2042D0 + * when it skips the level-1 batch. Unlike the reset + * vector this path first STOPs the LANCE and re-programs + * the board window, which is exactly the state a payload + * leaves behind -- see NETWORK_REPORT 72.5. */ + jaddr = 0x3F5788C0; + dojump = 1; + wb(TXBUF + 19 * 8, (jaddr >> 24) & 0xFF); + wb(TXBUF + 20 * 8, (jaddr >> 16) & 0xFF); + wb(TXBUF + 21 * 8, (jaddr >> 8) & 0xFF); + wb(TXBUF + 22 * 8, jaddr & 0xFF); } else if (cmd == 4 || cmd == 5) { /* jump (4) to an address, or reboot (5): jump to the ROM * reset vector, which is the same entry the CPU takes at diff --git a/harness/udpstream/vtcmd.py b/harness/udpstream/vtcmd.py index 8d8a4df..d0e59e7 100644 --- a/harness/udpstream/vtcmd.py +++ b/harness/udpstream/vtcmd.py @@ -11,7 +11,11 @@ Protocol, ethertype 0x88B6, both directions: cmd 2 stats: no arguments cmd 3 poke : [17..20] address (BIT), [21..22] len, [23..] the bytes cmd 4 jump : [17..20] address (BIT) -- reply is sent, then control moves - cmd 5 reboot: no arguments -- jumps to the ROM reset vector + cmd 5 reboot: no arguments -- jumps to the ROM reset vector (wedges the + real machine; kept only because MAME tolerates it) + cmd 6 osboot: no arguments -- jumps to the OS cold-start entry 0x3F5788C0, + the path POST takes when it skips level 1. That path STOPs + the LANCE and re-programs the board window first. reply : [16] cmd echoed, [17..18] status, [19..] data Every address in this protocol is a BIT address, the way the TMS34010 sees @@ -128,7 +132,7 @@ def main(): ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("cmd", choices=["stats", "read", "poke", "jump", - "reboot", "bench"]) + "reboot", "osboot", "bench"]) ap.add_argument("--iface", default="vt1200") ap.add_argument("--addr", type=lambda v: int(v, 0), default=0) ap.add_argument("--len", type=int, default=32) @@ -191,7 +195,7 @@ def main(): % (len(blob), a.addr, a.addr >> 3, status)) return 0 - if a.cmd in ("jump", "reboot"): + if a.cmd in ("jump", "reboot", "osboot"): if a.cmd == "reboot" and not a.force: print("refusing reboot without --force.") print(" It has NEVER worked on the real VT1200: 0 of 2 attempts.") @@ -202,7 +206,7 @@ def main(): return 2 if a.cmd == "reboot": print("rebooting: it may take ~90 s and it may never come back.") - cmd = 4 if a.cmd == "jump" else 5 + cmd = 4 if a.cmd == "jump" else (5 if a.cmd == "reboot" else 6) status, data = request(sock, a.iface, cmd, a.addr, 0, a.timeout) if status is None: print("no reply")