Files

15 KiB
Raw Permalink Blame History

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 (Ghidra 0x07F5A912), only referenced from the dump routine FUN_rom_07f5a026 (0x07F5A026).

  • It reads the pointer @0x27C190 and prints @+0x00, +0x20, … +0x100 as test_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 0x27C190 writes zero:

    07F5A38E SUB A9,A9
    07F5A394 MOVE A9,@0x27C190,0x1     ; clears the pointer
    

    A 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 0x3F4A4C30 fires at frame 569 with d@0xCB640 = 0.
  • Write @0xCB640 = 0x8000 at frame 568, continue to frame 1200: list[0] = 0x3F4AB090, and the frame shows Ethernet 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 = 1 at frame 1200: 0x3F578C50 (0x07EAF18A, OS restart) is reached at frame 3054 with @0xCB640 still 0x8000; the boot diagnostics breakpoint 0x3F3E1050 does 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 (0x07E5E6BC is 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 Session item or the Quit Control button; both are session-ending actions. The quit flag @0xB70E0 and the 0x07E5CD00 → 0x07EAF18A restart 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 RECEIVED and Memory Path Error - xx are 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.