docs(xie): the ROM's XImage entry points and the 1-bit format #11

Merged
arti merged 1 commits from opencode.siil.arti.ee/vt1200:fix/issue-4-ximage into master 2026-11-13 11:56:26 +02:00
Collaborator

Closes #4

dwt/notes/GUI_APPS.md gains §2.2.1 (the XImage entry points, the XImage
layout the ROM actually uses, and the format contract) and §2.2.2 (the
measured run). The probe is dwt/gui/xshow.asm + dwt/gui/xshow.lua; the run
log, the PNG and a dependency-free pixel dump are under dwt/gui/evidence/.

Entry points

Bit addresses; Ghidra = bits / 8. Derived with tools/smart34010/dis34 and
then exercised live (see the run below).

Xlib function bits Ghidra shape
XCreateImage(d,visual,depth,format,offset,data,w,h,pad,bpl) 0x3F9A0490 0x07F34092 10 args, calloc(1,0x58), fills the struct
XPutImage(d,drawable,gc,image,sx,sy,dx,dy,w,h) 0x3F9D0370 0x07F3A06E 10 args; request opcode 72 (0x48), length 6
XDestroyImage(image) 0x3F9A0D80 0x07F341B0 1 arg; free(data), free(obdata), free(image)
XGetImage(d,drawable,x,y,w,h,plane_mask,format) 0x3FD556B0 0x07FAAAD6 8 args; request opcode 73 (0x49), length 5

The request bytes are pinned mechanically:

$ tools/smart34010/dis34 -l -o -b 3F9CEE00 -s 0xF9DC0 vt1200_v22.bin | head -3
3f9cee00:	movi   0x48, a7	; 09c7 0048
3f9cee20:	movb   a7, *a11	; 8ceb
3f9cee30:	movk   6, a5	; 18c5

$ tools/smart34010/dis34 -l -o -b 3FD55830 -s 0x16AB06 vt1200_v22.bin | head -3
3fd55830:	movi   0x49, a11	; 09cb 0049
3fd55850:	movb   a11, *a10	; 8d6a
3fd55860:	movk   5, a7	; 18a7

dwt/notes/GUI_APPS.md §2.2.1 carries the same dis34 invocations (and the
two more that pin XCreateImage/XDestroyImage) so the names can be re-checked.

The format contract

  • XCreateImage inherits byte_order, bitmap_unit and bitmap_bit_order
    from the Display (dpy+0x1A0, +0x1C0, +0x200), so a client does not have
    to guess them. bits_per_pixel starts at 1; only format == ZPixmap (2)
    replaces it from the display's pixmap-format list. XYBitmap (0) and XYPixmap
    (1) are always 1 bit per pixel.
  • The only refusal in XPutImage is a ZPixmap of depth != 1 whose
    image->bits_per_pixel differs from the server's for that depth. It prints
    the ROM's own message and returns without sending the request.
  • XPutImage: bits_per_pixel conversion unimplemented. (0x3F9D0E80, printed at
    0x3F9D0B40) is literal: there is no conversion path. It cannot fire for a
    1-bit XYBitmap.
  • XGetImage: Can't get requested memory (0x3FD567A0, printed at
    0x3FD55D30) is the client-side malloc for the reply data failing
    (0x3FD55C70 allocates 4 * reply.length), not a server refusal.

The run

Probe: a create_process child (COFF loader, no network, no rdebug) opens its
own connection, creates a 400x200 window at 100,100, creates a 32x32 XYBitmap
(depth=1, format=0, LSBFirst, bitmap_unit=32, bitmap_pad=32,
bytes_per_line=0 so XCreateImage computes 4), puts it at window (20,20),
syncs twice, destroys it. A second XPutImage of the same image with h=16 at
(20,60) is the control.

$ HOME=/tmp/opencode/task4-home VT1200_SKIP_LEVEL1=1 VT1200_MONITOR=VR150 \
  XSHOW_AT=2500 XSHOW_END=2700 XSHOW_SNAP=2650 XSHOW_OUT=$PWD/dwt/gui/evidence \
  /home/arti/code/k-space/mame0289/vt1200 vt1200 -rompath harness/mame/roms \
  -video none -sound none -nothrottle -skip_gameinfo \
  -nvram_directory /tmp/opencode/task4-run/nvram \
  -cfg_directory /tmp/opencode/task4-run/cfg \
  -debug -debugger none -autoboot_script dwt/gui/xshow.lua -autoboot_delay 0

XSHOW: f2650 mirror stage=00000008 display=0093C910 window=00400001 gc=00987D70 image=00988130 putret=00000080 syncret=00000001 strlen=00000004
XSHOW: final image@00988130 width=00000020 height=00000020 xoffset=00000000 format=00000000 data=018D0000 byte_order=00000000 bitmap_unit=00000020 bitmap_bit_order=00000000 bitmap_pad=00000020 depth=00000001 bytes_per_line=00000004 bits_per_pixel=00000001
XSHOW: final dpy@0093C910 byte_order=00000000 bitmap_unit=00000020 scanline_pad=00000020 bitmap_bit_order=00000000
XSHOW: final pixmap_formats n=1 list=00367D90
XSHOW: final   fmt[0] depth=00000001 bits_per_pixel=00000001 pad=00000020

Stage 8 = synced (1 start, 2 display, 3 window, 4 mapped, 5 gc, 6 image,
7 putimage). The window shows the full 32-row image at screen 122..153 and the
16-row control at 162..177:

xshow

The row check is reproducible without ImageMagick/PIL:

$ python3 dwt/gui/evidence/png_rows.py dwt/gui/evidence/xshow_frame2650.png 118 118 40 64
 122 ....################################....
 123 .....#..................................
 ...
 152 ..................................#.....
 153 ....################################....
 ...
 162 ....################################....
 163 .....#..................................
 ...
 177 ...................#....................

The run was repeated with a snapshot 30 frames later (xshow_frame2680.png);
the decoded rows are identical and the two PNGs have the same md5
(ab9d63844aa5a3dd9d281a6bee3c76c2), so the picture is the window's state and
not a transient.

Measured, and not

Measured: the ROM's XCreateImage builds the struct above from the ten
arguments + the display's orders; a 1-bit XYBitmap is painted exactly w x h
pixels at the requested destination; a smaller h is honoured; XDestroyImage
runs and the process survives. The display advertises exactly one pixmap format
(depth 1, bpp 1, pad 32).

Inferred / not measured: XGetImage is documented from disassembly only —
the probe does not call it. Orders other than LSBFirst are supposed to work
(the 1-bit copier picks a normalization routine keyed by
(bitmap_unit, bitmap_bit_order, byte_order) for image and display,
0x3F9CB7C0–0x3F9CBB20), but only LSBFirst/32 was run. A non-1-bit ZPixmap is
untested: the server has no such visual, so only the bpp-mismatch path above is
established statically.

ROM untouched: md5 b46a3e442ccb86134d2356bd70d22fd2. The run uses the
documented VT1200_SKIP_LEVEL1=1 divergence and private cfg/nvram directories.

Closes #4 `dwt/notes/GUI_APPS.md` gains **§2.2.1** (the XImage entry points, the XImage layout the ROM actually uses, and the format contract) and **§2.2.2** (the measured run). The probe is `dwt/gui/xshow.asm` + `dwt/gui/xshow.lua`; the run log, the PNG and a dependency-free pixel dump are under `dwt/gui/evidence/`. ## Entry points Bit addresses; Ghidra = bits / 8. Derived with `tools/smart34010/dis34` and then exercised live (see the run below). | Xlib function | bits | Ghidra | shape | |---|---|---|---| | `XCreateImage(d,visual,depth,format,offset,data,w,h,pad,bpl)` | `0x3F9A0490` | `0x07F34092` | 10 args, `calloc(1,0x58)`, fills the struct | | `XPutImage(d,drawable,gc,image,sx,sy,dx,dy,w,h)` | `0x3F9D0370` | `0x07F3A06E` | 10 args; request opcode **72 (0x48)**, length 6 | | `XDestroyImage(image)` | `0x3F9A0D80` | `0x07F341B0` | 1 arg; free(data), free(obdata), free(image) | | `XGetImage(d,drawable,x,y,w,h,plane_mask,format)` | `0x3FD556B0` | `0x07FAAAD6` | 8 args; request opcode **73 (0x49)**, length 5 | The request bytes are pinned mechanically: ``` $ tools/smart34010/dis34 -l -o -b 3F9CEE00 -s 0xF9DC0 vt1200_v22.bin | head -3 3f9cee00: movi 0x48, a7 ; 09c7 0048 3f9cee20: movb a7, *a11 ; 8ceb 3f9cee30: movk 6, a5 ; 18c5 $ tools/smart34010/dis34 -l -o -b 3FD55830 -s 0x16AB06 vt1200_v22.bin | head -3 3fd55830: movi 0x49, a11 ; 09cb 0049 3fd55850: movb a11, *a10 ; 8d6a 3fd55860: movk 5, a7 ; 18a7 ``` `dwt/notes/GUI_APPS.md` §2.2.1 carries the same `dis34` invocations (and the two more that pin `XCreateImage`/`XDestroyImage`) so the names can be re-checked. ## The format contract * `XCreateImage` inherits `byte_order`, `bitmap_unit` and `bitmap_bit_order` from the Display (`dpy+0x1A0`, `+0x1C0`, `+0x200`), so a client does not have to guess them. `bits_per_pixel` starts at **1**; only `format == ZPixmap` (2) replaces it from the display's pixmap-format list. **XYBitmap (0) and XYPixmap (1) are always 1 bit per pixel.** * The only refusal in `XPutImage` is a ZPixmap of depth != 1 whose `image->bits_per_pixel` differs from the server's for that depth. It prints the ROM's own message and returns **without sending the request**. * `XPutImage: bits_per_pixel conversion unimplemented.` (`0x3F9D0E80`, printed at `0x3F9D0B40`) is literal: there is no conversion path. It cannot fire for a 1-bit XYBitmap. * `XGetImage: Can't get requested memory` (`0x3FD567A0`, printed at `0x3FD55D30`) is the **client-side malloc for the reply data** failing (`0x3FD55C70` allocates `4 * reply.length`), not a server refusal. ## The run Probe: a `create_process` child (COFF loader, no network, no rdebug) opens its own connection, creates a 400x200 window at 100,100, creates a 32x32 **XYBitmap** (`depth=1`, `format=0`, LSBFirst, `bitmap_unit=32`, `bitmap_pad=32`, `bytes_per_line=0` so `XCreateImage` computes 4), puts it at window (20,20), syncs twice, destroys it. A second `XPutImage` of the same image with `h=16` at (20,60) is the control. ``` $ HOME=/tmp/opencode/task4-home VT1200_SKIP_LEVEL1=1 VT1200_MONITOR=VR150 \ XSHOW_AT=2500 XSHOW_END=2700 XSHOW_SNAP=2650 XSHOW_OUT=$PWD/dwt/gui/evidence \ /home/arti/code/k-space/mame0289/vt1200 vt1200 -rompath harness/mame/roms \ -video none -sound none -nothrottle -skip_gameinfo \ -nvram_directory /tmp/opencode/task4-run/nvram \ -cfg_directory /tmp/opencode/task4-run/cfg \ -debug -debugger none -autoboot_script dwt/gui/xshow.lua -autoboot_delay 0 XSHOW: f2650 mirror stage=00000008 display=0093C910 window=00400001 gc=00987D70 image=00988130 putret=00000080 syncret=00000001 strlen=00000004 XSHOW: final image@00988130 width=00000020 height=00000020 xoffset=00000000 format=00000000 data=018D0000 byte_order=00000000 bitmap_unit=00000020 bitmap_bit_order=00000000 bitmap_pad=00000020 depth=00000001 bytes_per_line=00000004 bits_per_pixel=00000001 XSHOW: final dpy@0093C910 byte_order=00000000 bitmap_unit=00000020 scanline_pad=00000020 bitmap_bit_order=00000000 XSHOW: final pixmap_formats n=1 list=00367D90 XSHOW: final fmt[0] depth=00000001 bits_per_pixel=00000001 pad=00000020 ``` Stage 8 = synced (1 start, 2 display, 3 window, 4 mapped, 5 gc, 6 image, 7 putimage). The window shows the full 32-row image at screen 122..153 and the 16-row control at 162..177: ![xshow](https://git.wut.ee/opencode.siil.arti.ee/vt1200/raw/branch/fix/issue-4-ximage/dwt/gui/evidence/xshow_frame2650.png) The row check is reproducible without ImageMagick/PIL: ``` $ python3 dwt/gui/evidence/png_rows.py dwt/gui/evidence/xshow_frame2650.png 118 118 40 64 122 ....################################.... 123 .....#.................................. ... 152 ..................................#..... 153 ....################################.... ... 162 ....################################.... 163 .....#.................................. ... 177 ...................#.................... ``` The run was repeated with a snapshot 30 frames later (`xshow_frame2680.png`); the decoded rows are identical and the two PNGs have the same md5 (`ab9d63844aa5a3dd9d281a6bee3c76c2`), so the picture is the window's state and not a transient. ## Measured, and not **Measured:** the ROM's `XCreateImage` builds the struct above from the ten arguments + the display's orders; a 1-bit XYBitmap is painted exactly `w x h` pixels at the requested destination; a smaller `h` is honoured; `XDestroyImage` runs and the process survives. The display advertises exactly one pixmap format (depth 1, bpp 1, pad 32). **Inferred / not measured:** `XGetImage` is documented from disassembly only — the probe does not call it. Orders other than LSBFirst are *supposed* to work (the 1-bit copier picks a normalization routine keyed by `(bitmap_unit, bitmap_bit_order, byte_order)` for image and display, `0x3F9CB7C0`–`0x3F9CBB20`), but only LSBFirst/32 was run. A non-1-bit ZPixmap is untested: the server has no such visual, so only the bpp-mismatch path above is established statically. ROM untouched: `md5 b46a3e442ccb86134d2356bd70d22fd2`. The run uses the documented `VT1200_SKIP_LEVEL1=1` divergence and private cfg/nvram directories.
opencode.siil.arti.ee added 1 commit 2026-11-13 11:36:32 +02:00
opencode.siil.arti.ee force-pushed fix/issue-4-ximage from 561aeb24db to 2c2ba3bd5f 2026-11-13 11:36:32 +02:00 Compare
arti merged commit 717f5ba10e into master 2026-11-13 11:56:26 +02:00
arti deleted branch fix/issue-4-ximage 2026-11-13 11:56:26 +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#11