docs(loader): the per-process XG block must exist before XOpenDisplay #10

Merged
arti merged 1 commits from opencode.siil.arti.ee/vt1200:fix/issue-2-xg-block into master 2026-11-13 11:55:46 +02:00
Collaborator

Closes #2

A create_process child has no per-process Xlib context (XG). This makes
windows and drawing work while events silently fail, so the docs now state the
addresses, the block, and the ordering rule; dwt/loader/xg.c provides the
install helper and was built and run.

The mechanism, from the ROM

0x3F708700 is the current-process helper — it is nothing but
move @0x1b3900,a8,1; rets:

$ tools/smart34010/dis34 -l -o -b 3F708700 \
    -s $(( (0x3F708700-0x3F200000)/8 )) vt1200_v22.bin
3f708700:	move   @0x1b3900, a8, 1
3f708730:	rets

XNextEvent is 0x3F9C1CF0; its dequeue path loads XG twice and manages the
free list at XG+0xCC0 bits (= byte +0x198):

$ tools/smart34010/dis34 -l -o -b 3F9C23C0 \
    -s $(( (0x3F9C23C0-0x3F200000)/8 )) vt1200_v22.bin
3f9c23c0:	calla  0x3f708700
3f9c23f0:	move   *a8(0x100), a7, 1
3f9c2410:	move   *a7, a7, 1
3f9c2420:	move   *a7(0xcc0), *a11(0x0), 1
...
3f9c24b0:	move   a11, *a7(0xcc0), 1

The enqueue helper 0x3F9B89A0 (called from XNextEvent at 0x3F9C2180) does
the same three loads and takes a node from the same free list:

$ tools/smart34010/dis34 -l -o -b 3F9B89A0 \
    -s $(( (0x3F9B89A0-0x3F200000)/8 )) vt1200_v22.bin
3f9b8a80:	calla  0x3f708700
3f9b8ab0:	move   *a8(0x100), a7, 1
3f9b8ad0:	move   *a7, a7, 1
3f9b8ae0:	move   *a7(0xcc0), a11, 1
...
3f9b8b70:	move   *a11(0x0), *a7(0xcc0), 1

So proc = *(u32 *)0x001B3900, pp = *(u32 *)(proc + 0x100), XG = *(u32 *)pp,
free list at XG + 0xCC0 bits. In a fresh child XG is 0, so the whole XG
structure is read from low RAM.

Docs

  • dwt/notes/GUI_APPS.md: new §10.5 (renumbering the old §10.5–10.7 to
    §10.6–10.8), plus a step 0 in §4's minimum sequence and a pointer in the
    process table.
  • dwt/loader/README.md: new section before Build, and xg.c in the file table.

Both say: zeroed ≥1 KiB block (static vt_u32 vt_xg[256] in termtris), store
its address into *(u32 *)pp, and do it before XOpenDisplay. The symptom
is stated as measured: windows and drawing work, XNextEvent returns zeroed
records and never a real event.

Helper, built and run

dwt/loader/xg.c implements int vt_install_xg(void); it returns 0 if
*(0x001B3900) or *(proc+0x100) is not a plausible RAM pointer, otherwise
zeroes a 1 KiB block, stores it, and returns 1.

Demonstrated with the unmodified termtris smoke client, linked against the
loader-tree helper:

$ apps/termtris/build.sh apps/termtris/tests/smoke.c dwt/loader/xg.c
...
.bss       0    01a00000    0000a700
                  01a08700    00002000     XG.obj (.bss)

$ HOME=/tmp/opencode/vt1200-task2/home VT1200_SKIP_LEVEL1=1 \
  SMOKE_MARK=0x01a08000 LOADER_AT=2500 LOADER_END=3400 \
  LOADER_BIN=$PWD/dwt/loader/build/loader.bin \
  LOADER_COFF=$PWD/apps/termtris/build/smoke.coff \
  LOADER_OUT=/tmp/opencode/vt1200-task2 \
  /home/arti/code/k-space/mame0289/vt1200 vt1200 \
    -rompath harness/mame/roms -video none -sound none -nothrottle \
    -skip_gameinfo -cfg_directory /tmp/opencode/vt1200-task2/cfg \
    -nvram_directory /tmp/opencode/vt1200-task2/nvram \
    -debug -debugger none -autoboot_script apps/termtris/tests/smoke.lua \
    -autoboot_delay 0
LOADER: result status=0 (OK) ... pcb=003FEE50 nscns=4 len=1349
SMOKE: magic=534D4F4B dpy=00942290 win=00400001 n=9 type=2 keycode=194 lookup_n=1 ch=61 ks=0061 keymark=4B455950

type=2 is KeyPress, keycode=194 is the LK401 A, and keymark=4B455950
is the client's own "KEYP" marker.

Control: the same client with a stub vt_install_xg() that installs nothing:

SMOKE: magic=534D4F4B dpy=00942290 win=00400001 n=10 type=0 keycode=0 lookup_n=0 ch=00 ks=0000 keymark=00000000

The display opens and the window is created/mapped, but the event loop only sees
zeroed records. Full transcript: dwt/loader/evidence/xg_helper_run.log.

Limits

  • The ordering rule (before XOpenDisplay) is the order the working client
    uses; I did not test installing after XOpenDisplay.
  • 1 KiB is the tested block size, not a proven minimum.
  • The measured failure is zero-valued events; the exact low-RAM words that
    become the queue are whatever the previous boot left and are not
    characterised.
  • ROM unchanged: md5 b46a3e442ccb86134d2356bd70d22fd2 vt1200_v22.bin before
    and after the runs.
Closes #2 A `create_process` child has no per-process Xlib context (`XG`). This makes windows and drawing work while events silently fail, so the docs now state the addresses, the block, and the ordering rule; `dwt/loader/xg.c` provides the install helper and was built and run. ## The mechanism, from the ROM `0x3F708700` is the current-process helper — it is nothing but `move @0x1b3900,a8,1; rets`: ``` $ tools/smart34010/dis34 -l -o -b 3F708700 \ -s $(( (0x3F708700-0x3F200000)/8 )) vt1200_v22.bin 3f708700: move @0x1b3900, a8, 1 3f708730: rets ``` `XNextEvent` is `0x3F9C1CF0`; its dequeue path loads `XG` twice and manages the free list at `XG+0xCC0` bits (= byte `+0x198`): ``` $ tools/smart34010/dis34 -l -o -b 3F9C23C0 \ -s $(( (0x3F9C23C0-0x3F200000)/8 )) vt1200_v22.bin 3f9c23c0: calla 0x3f708700 3f9c23f0: move *a8(0x100), a7, 1 3f9c2410: move *a7, a7, 1 3f9c2420: move *a7(0xcc0), *a11(0x0), 1 ... 3f9c24b0: move a11, *a7(0xcc0), 1 ``` The enqueue helper `0x3F9B89A0` (called from `XNextEvent` at `0x3F9C2180`) does the same three loads and takes a node from the same free list: ``` $ tools/smart34010/dis34 -l -o -b 3F9B89A0 \ -s $(( (0x3F9B89A0-0x3F200000)/8 )) vt1200_v22.bin 3f9b8a80: calla 0x3f708700 3f9b8ab0: move *a8(0x100), a7, 1 3f9b8ad0: move *a7, a7, 1 3f9b8ae0: move *a7(0xcc0), a11, 1 ... 3f9b8b70: move *a11(0x0), *a7(0xcc0), 1 ``` So `proc = *(u32 *)0x001B3900`, `pp = *(u32 *)(proc + 0x100)`, `XG = *(u32 *)pp`, free list at `XG + 0xCC0` bits. In a fresh child `XG` is 0, so the whole XG structure is read from low RAM. ## Docs - `dwt/notes/GUI_APPS.md`: new §10.5 (renumbering the old §10.5–10.7 to §10.6–10.8), plus a step 0 in §4's minimum sequence and a pointer in the process table. - `dwt/loader/README.md`: new section before Build, and `xg.c` in the file table. Both say: zeroed ≥1 KiB block (`static vt_u32 vt_xg[256]` in termtris), store its address into `*(u32 *)pp`, and do it **before `XOpenDisplay`**. The symptom is stated as measured: windows and drawing work, `XNextEvent` returns zeroed records and never a real event. ## Helper, built and run `dwt/loader/xg.c` implements `int vt_install_xg(void)`; it returns 0 if `*(0x001B3900)` or `*(proc+0x100)` is not a plausible RAM pointer, otherwise zeroes a 1 KiB block, stores it, and returns 1. Demonstrated with the **unmodified** termtris smoke client, linked against the loader-tree helper: ``` $ apps/termtris/build.sh apps/termtris/tests/smoke.c dwt/loader/xg.c ... .bss 0 01a00000 0000a700 01a08700 00002000 XG.obj (.bss) $ HOME=/tmp/opencode/vt1200-task2/home VT1200_SKIP_LEVEL1=1 \ SMOKE_MARK=0x01a08000 LOADER_AT=2500 LOADER_END=3400 \ LOADER_BIN=$PWD/dwt/loader/build/loader.bin \ LOADER_COFF=$PWD/apps/termtris/build/smoke.coff \ LOADER_OUT=/tmp/opencode/vt1200-task2 \ /home/arti/code/k-space/mame0289/vt1200 vt1200 \ -rompath harness/mame/roms -video none -sound none -nothrottle \ -skip_gameinfo -cfg_directory /tmp/opencode/vt1200-task2/cfg \ -nvram_directory /tmp/opencode/vt1200-task2/nvram \ -debug -debugger none -autoboot_script apps/termtris/tests/smoke.lua \ -autoboot_delay 0 LOADER: result status=0 (OK) ... pcb=003FEE50 nscns=4 len=1349 SMOKE: magic=534D4F4B dpy=00942290 win=00400001 n=9 type=2 keycode=194 lookup_n=1 ch=61 ks=0061 keymark=4B455950 ``` `type=2` is `KeyPress`, `keycode=194` is the LK401 `A`, and `keymark=4B455950` is the client's own "KEYP" marker. Control: the same client with a stub `vt_install_xg()` that installs nothing: ``` SMOKE: magic=534D4F4B dpy=00942290 win=00400001 n=10 type=0 keycode=0 lookup_n=0 ch=00 ks=0000 keymark=00000000 ``` The display opens and the window is created/mapped, but the event loop only sees zeroed records. Full transcript: `dwt/loader/evidence/xg_helper_run.log`. ## Limits - The ordering rule (before `XOpenDisplay`) is the order the working client uses; I did not test installing after `XOpenDisplay`. - 1 KiB is the tested block size, not a proven minimum. - The measured failure is zero-valued events; the exact low-RAM words that become the queue are whatever the previous boot left and are not characterised. - ROM unchanged: `md5 b46a3e442ccb86134d2356bd70d22fd2 vt1200_v22.bin` before and after the runs.
opencode.siil.arti.ee force-pushed fix/issue-2-xg-block from 310a9a3ea5 to a43eba776b 2026-11-13 11:26:08 +02:00 Compare
opencode.siil.arti.ee added 1 commit 2026-11-13 11:26:29 +02:00
opencode.siil.arti.ee force-pushed fix/issue-2-xg-block from a43eba776b to 3f6eb56749 2026-11-13 11:26:29 +02:00 Compare
arti merged commit a06adb1bc8 into master 2026-11-13 11:55:46 +02:00
arti deleted branch fix/issue-2-xg-block 2026-11-13 11:55:46 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: arti/vt1200#10