15 KiB
The bottom diagnostics message box — window, text source, and the Session→Quit re-show
Status: DONE, with one honest emulator caveat (the MAME skip-level1 restart does not bring the Terminal Manager back; the last step is static code + the real unit's observed box, not an end-to-end MAME reproduction).
Address convention: Ghidra = 0x07E40000 + ROM file offset, firmware bit
address = Ghidra × 8. The TMS34010 program space is bit-addressed, so every
firmware address below is a bit address unless it is explicitly a Ghidra
address.
1. Which window is the bottom box?
It is a separate top-level X window owned by the Terminal Manager
(SESSIONMGR_PROC), not a child of the TM window and not a status bar drawn by
the window manager.
Creation code: FUN_rom_07e94880 (Ghidra 0x07E94880, bit 0x3F4A4400).
07E9492A MOVE *A11(0x40),*A14+,0x1 ; parent = screen->root
07E9492E MOVE A7,*A14+,0x1 ; display
07E94930 CALLA 0x07FA8372 ; XCreateSimpleWindow(d, root, ...)
07E94936 MOVE A8,@0xB7C60 ; <-- box window id
07E9494E CALLA 0x07FB2FAA ; XSelectInput(d, win, 0x8000)
07E94976 CALLA 0x07FA70EE ; XChangeWindowAttributes(d, win, 0x200, attrs)
The window is mapped at the end of the builder, 0x07E949FA CALLA 0x07FAC9EC
(XMapWindow(d, @0xB7C60)).
The parent really is the root window: 0x07E948AC loads @0xB78E0 (the TM's
Display *, see GUI_APPS.md), walks display->screens[] (+0x480, +0x440)
and takes screen->root (+0x40) before the XCreateSimpleWindow call.
Live on the real unit (peek, read-only, two samples 2 s apart):
peek 0xB7C60 4 -> 2d 00 10 00 = 0x0010002d
0x0010002d is an X resource id (client base 0x00100000), i.e. a real server
window, not a sentinel. The screenshot
dwt/evidence/real-unit-msgbox-allpassed.png shows the separate wide box at the
bottom, with its own border and no title bar.
Owner/process: the TM's process entry is 0x07E5CDBC (bit 0x3F2E6DE0,
SESSIONMGR_PROC); the box id and all its state are in that process's globals.
2. Where the text comes from
2.1 The message table
The strings are in ROM in a table of 22 records, 20 bytes each, at Ghidra
0x07FE5552 (bit 0x3FF2AA90):
record = 8 zero bytes + [English ptr][French ptr][German ptr] (20 bytes)
The pointer for record r, language L is at
0x3FF2AA90 + r*0xA0 + 0x40 + L*0x20 (the +0x40 skips the 8-byte header;
0x20 bits = 4 bytes per language pointer).
| record | mask bit | English text |
|---|---|---|
| 0 | 7 | NVR Checksum Error - 14 |
| 1 | 8 | Video RAM Error - 22 |
| 2 | 9 | MAIN Memory Error - 13 |
| 3 | 10 | Host Port Error - 15 |
| 4 | 11 | Printer Port Error - 16 |
| 5 | 12 | Keyboard Port Error - 18 |
| 6 | 13 | Mouse Port Error - 19 |
| 7 | 14 | Keyboard Error - 27 |
| 8 | 15 | Ethernet Port Error - 24 |
| 9 | 16 | Mouse Error - 26 |
| 10 | 17 | Video Error - 25 |
| 11 | 18 | Ethernet ROM Error - 23 |
| 12 | 19 | Timer 1 Error - 17 |
| 13 | 20 | Timer 2 Error - 21 |
| 14 | 21 | Optional RAM Simm 1 Error - 13 |
| 15 | 22 | Optional RAM Simm 2 Error - 13 |
| 16 | 23 | Optional RAM Simm 3 Error - 13 |
| 17 | — | UNEXPECTED DIAGNOSTIC ERROR RECEIVED (table entry; not selected by the box builder's bit branches) |
| 18 | mask == 0 | All Diagnostics Passed |
| 19 | bits 0–3 | ROM Checksum Error - 12 |
| 20 | — | Memory Path Error - xx (table entry; not selected by the box builder) |
| 21 | bit 28 | Image Board Error - 43 |
Record 18's English pointer is 0x3F4AB9A0 (Ghidra 0x07E95734), the exact
string in the box. Record 8's is 0x3F4AB090 (Ghidra 0x07E95612).
2.2 The selection code
The builder is FUN_rom_07e94986 (bit 0x3F4A4C30), called from the TM's
session-start path at 0x07E5E6BC. It calls FUN_rom_07e94a08
(bit 0x3F4A5040) to turn the status mask into a list of message pointers:
07E94A16 MOVE @0xCB640,A11,0x1 ; <-- STATUS MASK
07E94A1C JREQ 0x07E94E4C ; mask == 0 -> "All Diagnostics Passed"
...
07E94A3A MOVE @0x19FF10,A7,0x0 ; language index
07E94A40 SLL 0x5,A7 ; *32 bits = *4 bytes
07E94A4C ADDI 0x19E3D0,A7 ; record 0 + 8 (pointer field)
07E94A52 MOVE *A7(0x0),*A9(0x40),0x1 ; message ptr -> list entry +8
Destination list arithmetic (0x07E94A28–0x07E94A32):
SLL 0x7,A9 ; count * 128
SLL 0x5,A7 ; count * 32
ADD A7,A9 ; count * 160 bits = count * 20 bytes
ADDI 0x19F150,A9 ; list base
So the live list is a 20-byte record per error at bit 0x19F150, with
the string pointer at entry + 0x40 bits (8 bytes); the builder appends a
null-pointer terminator. The "All passed" branch uses record 18's pointer
(0x19EF10); the error branches use record 0..16's pointers
(0x19E3D0 … 0x19EDD0), record 19 for bits 0–3 (0x19EFB0), and record 21
for bit 28 (0x19F0F0).
The table is also present as a RAM copy, byte-for-byte:
0x19E390 = record header 0 (bit address), 0x19E3D0 = record 0's English
pointer. (Verified by comparing the dump at frame 3000 with ROM
0x7FE5552/0x7FE555A; the RAM copy begins at byte 0x33C72 in a RAM dump,
i.e. bit 0x19E390.)
Live on the real unit:
peek 0x19F190 4 -> a0 b9 4a 3f = 0x3F4AB9A0
peek 0x19F230 4 -> 00 00 00 00 (null terminator)
peek 0x19E3D0 4 -> f0 aa 4a 3f = record 0 (NVR)
peek 0x19E8D0 4 -> 90 b0 4a 3f = record 8 (Ethernet Port - 24)
peek 0x19EF10 4 -> a0 b9 4a 3f = record 18 (All Passed)
peek 0x3F4AB9A0 24 -> b'All Diagnostics Passed\x00A'
2.3 The status variable and its writer
Status variable: the 32-bit failure mask at bit 0xCB640.
Read by the box builder at 0x07E94A16. Accumulated by the diagnostics test
result handler FUN_rom_07e7bcf6:
07E7BD7C MOVE @0xCB640,A11,0x1
07E7BD82 OR A11,A9 ; A9 = one result bit (1 << test number)
07E7BD84 MOVE A9,@0xCB640,0x1 ; <-- OR the failure bit into the mask
The same routine prints the corresponding table entry at
0x07E7BDA8 ADDI 0x19E390,A11 (record base) and 0x07E7BDB2 CALLA printf, so
the mask and the printed diagnostic report use the same table.
Cleared by the two wrappers:
07E7C212 SUB A11,A11
07E7C214 MOVE A11,@0xCB640,0x1 ; FUN_07e7c20a
...
07E7C276 MOVE A11,@0xCB640,0x1 ; FUN_07e7c26a
FUN_07e7c20a is called from the boot diagnostics block
0x07EAF184 CALLA 0x07E7C20A; FUN_07e7c26a from the trap/error path
0x07EAF526 CALLA 0x07E7C26A. No other instruction writes 0xCB640.
Live on the real unit (two samples):
peek 0xCB640 4 -> 00 00 00 00 (0 -> All Diagnostics Passed)
3. Why Session→Quit re-shows the box
The box is not a boot-only alert. It is created in the TM's session-start path and re-reads the persistent mask every time that path runs:
07E5E676 CALLA 0x07EE0D48 ; create_process(VTE, ...) -- start the session
07E5E6B6 CALLA 0x07FAC9EC ; XMapWindow(session window)
07E5E6BC CALLA 0x07E94986 ; <-- build list + create box window + draw + map
0x07E5E6BC is unconditional: every session start reaches it.
The Session/Quit action sets the TM quit flag @0xB70E0:
07E891D8 MOVE @0xAECDF,A11,0x0 ; confirmation required?
07E891DE JRNE 0x07E891EC
07E891E0 MOVK 0x1,A11
07E891E2 MOVE A11,@0xB70E0,0x1 ; <-- quit flag
(The confirmation path FUN_07e895e2 sets the same flag at 0x07E895F8.)
The TM event loop then exits:
07E5E6DC MOVE @0xB70E0,A11,0x1
07E5E6E2 JRNE 0x07E5F762
...
07E5F762 CALLA 0x07E5CD00 ; close display / sleep / restart
0x07E5CD00 ends with 0x07E5CD90 CALLA 0x07EAF18A — the kernel/OS restart
entry. That entry is after the diagnostics call, and the restart sets
A10 = 1 so the boot block's 0x07EAF182 JRNE 0x07EAF198 skips
0x07EAF184 CALLA 0x07E7C20A:
07EAF180 MOVE A10,A10
07EAF182 JRNE 0x07EAF198 ; skip diagnostics on restart
07EAF184 CALLA 0x07E7C20A ; boot diagnostics (initial boot only)
...
07EAF192 MOVI 0x1,A10
Therefore Session→Quit does not re-run the diagnostics check and does not
clear @0xCB640. It restarts the OS, the restarted SESSIONMGR_PROC runs
the same session-start code, and 0x07E5E6BC recreates the box from the mask
that was already in RAM. On the real unit the mask is 0 (All Diagnostics Passed), so the same box comes back.
On a plain Expose the TM does not rebuild the list; it only repaints it
(0x07E5FAB8 checks that the event window is @0xB7C60, then calls the draw
routine 0x07E94EB0). That is why the box stays put across ordinary repaints,
but the re-creation after Quit is the session-start builder.
4. Live read format
The box text is not stored as ASCII in RAM. The readable state is:
| what | bit address | format |
|---|---|---|
| status mask | 0xCB640 |
32-bit little-endian; 0 = All Passed; bit 15 (0x8000) = Ethernet Port Error - 24; bit 8 = Video RAM Error - 22; … |
| message list | 0x19F150 |
20 bytes/entry; string pointer at entry +0x40 bits (+8 bytes); entry i pointer at 0x19F150 + i*0xA0 + 0x40; null pointer terminates |
| language | 0x19FF10 |
16-bit field; 0 = English (source: 2-bit field at @0xAEC E8, copied at 0x07E94990–0x07E94998) |
| RAM table copy | 0x19E390 |
22 × 20-byte records, same layout as ROM 0x3FF2AA90 |
| box window id | 0xB7C60 |
32-bit X resource id |
Example live reads (dwt/exploit.py-style peek, read-only):
peek 0xCB640 4 -> 00 00 00 00 mask = 0
peek 0x19F190 4 -> a0 b9 4a 3f list[0] = 0x3F4AB9A0
peek 0x3F4AB9A0 24-> "All Diagnostics Passed"
peek 0xB7C60 4 -> 2d 00 10 00 window 0x0010002d
0x19F190 is 0x19F150 + 0x40; no count is stored globally — the builder
returns it in A8, and the draw routine walks the list until the null pointer.
5. What DIAGNOSTIC MAILBOX actually is
DIAGNOSTIC MAILBOX is a debug dump of a separate diagnostics structure,
not the bottom box's source.
-
The format string is
0x3FAD4890(Ghidra0x07F5A912), only referenced from the dump routineFUN_rom_07f5a026(0x07F5A026). -
It reads the pointer
@0x27C190and prints@+0x00, +0x20, … +0x100astest_type,test_mode,test_result,error_ext0..6,diag_hld,memory_bss,test_cnt,test_f_cnt,memory_lth,video_dydx,num_of_lnks, and a memory-error link table. -
No code ever arms it. The only instruction that writes
0x27C190writes zero:07F5A38E SUB A9,A9 07F5A394 MOVE A9,@0x27C190,0x1 ; clears the pointerA separate routine (
0x07F59FC8) fills a fixed buffer at@0x27C1F0, but the dump pointer is never pointed at it. On the real unit:peek 0x27C190 4 -> 00 00 00 00
So the mailbox is a dead/debug report dump; the bottom box never reads it. The
old candidate 0x16F0 (level-1 dispatcher slot) is likewise not referenced by
0x07E94986/0x07E94A08.
6. Evidence
Real unit (read-only; 2026-10-18)
| check | value |
|---|---|
@0xCB640 |
0x00000000 (twice, 2 s apart) |
@0xB7C60 |
0x0010002d (twice) |
@0x19F190 |
0x3F4AB9A0 (twice) |
@0x19F230 |
0 (sentinel) |
@0x19FF10 (16-bit) |
0 (English) |
@0x19E3D0 / @0x19E8D0 / @0x19EF10 |
0x3F4AAAF0 / 0x3F4AB090 / 0x3F4AB9A0 |
@0x27C190 |
0 |
string at 0x3F4AB9A0 |
All Diagnostics Passed |
| screenshot | dwt/evidence/real-unit-msgbox-allpassed.png |
MAME (skip-level1, RAM poke only — ROM untouched)
- Breakpoint on the builder entry
0x3F4A4C30fires at frame 569 withd@0xCB640 = 0. - Write
@0xCB640 = 0x8000at frame 568, continue to frame 1200:list[0] = 0x3F4AB090, and the frame showsEthernet Port Error - 24(dwt/evidence/mame-msgbox-ether24-frame1200.png). - Write
@0xCB640 = 0x1->list[0] = 0x3F4ABB20(ROM Checksum Error - 12). - Write
@0xCB640 = 0x10000000->list[0] = 0x3F4ABC98(Image Board Error - 43). - Write the quit flag
@0xB70E0 = 1at frame 1200:0x3F578C50(0x07EAF18A, OS restart) is reached at frame 3054 with@0xCB640still0x8000; the boot diagnostics breakpoint0x3F3E1050does not fire again. (In the skip-level1 emulator the restarted X server comes back at the root weave but the TM does not recreate its window within 8500 frames — see caveat.) - Control frame with mask 0:
dwt/evidence/mame-msgbox-allpassed-frame3000.png.
7. Caveats
- The MAME skip-level1 restart reproduces the OS restart and the mask
persistence, but not the TM's return. The real unit does return to the TM with
the box (the owner's observation and
dwt/evidence/real-unit-msgbox-allpassed.png). The final rebuild step is therefore static (0x07E5E6BCis unconditional in the session-start path) plus the real-unit observation, not an end-to-end emulator trace. - The exact "Session → Quit" menu item may be the
Abort X Sessionitem or theQuit Controlbutton; both are session-ending actions. The quit flag@0xB70E0and the0x07E5CD00 → 0x07EAF18Arestart are the path proven here. A Quit that only closes the VTE window would leave the box window untouched and the Expose redraw (0x07E5FAB8 → 0x07E94EB0) would repaint the same list; the end result (same text from the same mask) is identical. - The box can hold several messages: the builder appends one record per set
mask bit and terminates with a null pointer. The window height is derived from
the count in
0x07E94880. UNEXPECTED DIAGNOSTIC ERROR RECEIVEDandMemory Path Error - xxare table entries but are not selected by the box builder's current bit branches; they are used by the diagnostics reporter.
8. Reproduce the MAME checks
# box builder location / mask at build time
VT1200_SKIP_LEVEL1=1 BOX_FRAMES=600 \
/home/arti/code/k-space/mame0289/vt1200 vt1200 \
-rompath harness/mame/roms -video none -sound none -nothrottle -skip_gameinfo \
-cfg_directory .vt1200-scratch/msgbox/cfg \
-nvram_directory .vt1200-scratch/msgbox/nvram \
-debug -debugger none -debuglog \
-autoboot_script .vt1200-scratch/msgbox/probe_box.lua -autoboot_delay 0 \
-seconds_to_run 30
# grep BOXBUILD debug.log -> BOXBUILD fr=569 ... mask=00000000
# force a failing bit just before the build
MASK_VAL=0x8000 BOX_FRAMES=1250 VT1200_DUMP_FRAME=1200 \
VT1200_DUMP_DIR=$PWD/.vt1200-scratch/msgbox VT1200_SKIP_LEVEL1=1 \
/home/arti/code/k-space/mame0289/vt1200 vt1200 ... \
-autoboot_script .vt1200-scratch/msgbox/probe_box_mask.lua ...
# frame 1200 shows "Ethernet Port Error - 24"; RAM @0x19F190 = 0x3F4AB090
The probe scripts live in .vt1200-scratch/msgbox/ (scratch, not source of
truth). The ROM is untouched: md5 b46a3e442ccb86134d2356bd70d22fd2.