Files
vt1200/xie/evidence/XIE_IMAGE_DISPLAYED.md
arti d8d54bb990 feat(xie): a client-chosen XIE image is displayed on the VT1200
The XIE path now runs end to end: XieCreateImage -> XieCreatePhoto ->
XiePutTile -> XieExport(photomap -> depth-1 pixmap) -> XCopyArea(pixmap ->
window), and the client's tile appears on screen at the client's own window
position, pixel-exact.

TWO FAULTS BEHIND THE LONG DEAD END

1. Device model (harness/mame/vt1200.cpp, opt-in VT1200_ACCEL=1).  The
   accelerator's output port 0x5600200 returns the pixel stream the firmware
   pushed into the FIFO -- the firmware then stores it into the drawable itself
   (file 0x125528: MOVE *B6,*B1 ; ADDK 0x10,B1).  The FIFO read pointer was
   never reset between transfers, so consecutive exports read stale words and
   the same fill produced 759 then 516 set pixels.  Reset m_fifo_in/out on the
   data-register write; the export is now deterministic.

2. Measurement, not the device.
   - dwt/screenshot.py without --real reads 1024x1024 at a 128-byte row stride,
     but the VR319 default programs 1280x1024 at a 2048-bit (256-byte) pitch.
     That is the "shifted" picture; --real is the correct read.
   - The earlier "0 differing bytes over 128 KiB" VRAM comparison stepped peek
     by 1024 *bit* addresses, so it scanned rows 0..63 only -- never the window
     at rows 200..263.  Over the real raster the white/black diff is 576 bytes
     spanning exactly those rows.
   - New instrument: decclient does a server-side XGetImage readback of the
     pixmap and window and counts set pixels.  That separated "the server never
     drew it" from "the framebuffer view is wrong".

EVIDENCE (reproduced twice, the second time from a fresh machine)

  no export vs 0xFF : 4096 / 4096 pixels differ
  no export vs 0x00 :    0 / 4096   (the window background is black)
  0xFF      vs 0x00 : 4096 / 4096
  0xAA      vs 0x0F : 2048 / 4096   (the two stripe phases)

Tile byte 0xAA reproduces as 1-pixel vertical stripes, 0x0F as 4-pixel, with
no shear -- so the placement is right and the pixels are the client's.

Contents: xie/ holds DEC XIE 2.3 headers, 84 man pages and complete
client+server source; a libXie.a and decclient built from DEC's own code; the
XIE probes; and evidence/ with every measurement, including explicit
retractions of the earlier wrong readings (VRAM_IDENTICAL_FOR_BOTH_FILLS and
friends).  xie/evidence/XIE_IMAGE_DISPLAYED.md is the report; frames are in
xie/evidence/png/.

The accelerator model stays opt-in: it answers the status poll and relays the
pixel stream, but performs no compositing, so it is a stand-in and the verified
default configuration is unchanged (a no-env run still parks in the level-0
diagnostics menu at PC=3FDCE400).
2026-11-01 23:22:25 +02:00

5.7 KiB

A client-chosen XIE image is displayed on the VT1200

Result: the tile a client sends through XIE appears on the terminal's screen, at the client's window position, pixel-exact.

Evidence frames: xie/evidence/png/xie_window_tiles.png (5 tiles side by side), xie_white_real.png, xie_black_real.png, xie_fill_aa.png, xie_fill_0f.png, xie_baseline_noexport.png.

win_* crops are 64x64 at +150+200 == the client window's X geometry

no export vs 0xFF : 4096 / 4096 pixels differ
no export vs 0x00 :    0 / 4096   (window background is black)
0xFF      vs 0x00 : 4096 / 4096
0xAA      vs 0x0F : 2048 / 4096   (half - the two stripe phases)
0x0F      vs none : 2048 / 4096

Patterns are drawn pixel-exact: tile byte 0xAA reproduces as 1-pixel vertical stripes, 0x0F as 4-pixel vertical stripes, perfectly vertical (no shear).

What was actually broken

Two independent faults, one in the device model and one in the measurement.

1. Device model: the accelerator's output FIFO read pointer was never reset

accel_r returns the pixel stream on 0x5600200:

file 0x125528:  MOVE *B6,*B1      ; engine output port -> *B1 (staging)
                ADDK 0x10,B1      ; next word
                DSJS A11,0x125528

m_fifo_in / m_fifo_out were never reset, so consecutive exports read words left over from the previous one: the same fill produced 759, then 516 set pixels in different runs. Reset both pointers on the data-register write, which is what starts a transfer:

if ((off & 0xff) == 0x05) { m_accel_rdy = true; m_accel_done = false; m_accel_cnt = 0;
                            m_fifo_in = m_fifo_out = 0; }

After that the export is deterministic and correct:

fill=0xff -> pixmap after export: 4096/4096 pixels set (first row 11111111)
fill=0x00 -> pixmap after export:    0/4096 pixels set (first row 00000000)

2. Measurement: two wrong reads, and one bad conclusion built on them

  • dwt/screenshot.py without --real reads 1024x1024 at a 128-byte row stride. The firmware at the VR319 default programs a 1280x1024 display with a 2048-bit (256-byte) row pitch, so that read shears the picture. That is the "shifted" look: use --real (1280 wide, 256-byte stride).
  • The earlier "0 differing bytes over 128 KiB" VRAM comparison was not 128 KiB of VRAM. It stepped peek by 1024 bit addresses, i.e. 128 bytes, over 0..0x20000 bit addresses: 16 KiB of bytes, rows 0..63. The window is at rows 200..263, so the test never looked at it. Re-run over the whole raster (0x200000 bit addresses = 256 KiB of bytes) it reports 576 differing bytes spanning exactly rows 200..263.
  • New instrument: server-side XGetImage. decclient now reads the pixmap and the window back from the server and counts set pixels. This distinguishes "the server never drew it" from "the server drew it but the framebuffer view is wrong" - the distinction that was missing for the whole investigation.

The working request sequence

XieCreateImage -> XieCreatePhoto(photomap) -> XiePutTile
XieExport(photomap -> depth-1 Pixmap)
XCopyArea(pixmap -> window)

Export must target a pixmap, not the window, and the copy is a plain core-X XCopyArea - exactly what DEC's own IDS client does (ids_build_xie_pipe.c: XieExport(Photo_, CPix_(xiedat), RPixGC_(phoctx), ...)). See DEC_IDS_SEQUENCE_PIXMAP.txt.

decclient argv:

argv[4] path
(absent) export straight to the window
1 export to a pixmap + XCopyArea (the working path)
2 XieBindMapToFlo / XieCrop / XieTapFlo pipeline (SIGSEGVs: needs the IDS context)
9 control: window mapped, no export (baseline)

Reproduce

M=/home/arti/code/k-space/mame0289/vt1200
(sudo python3 -u harness/serve.py 192.168.99.1 --bootp &)
setsid env VT1200_SKIP_LEVEL1=1 VT1200_ACCEL=1 \
  $M vt1200 -rompath harness/mame/roms -sound none -skip_gameinfo \
  -networkprovider taptun -video none -seconds_to_run 500 \
  -cfg_directory harness/mame/cfg-net -debug -debugger none -debuglog \
  -autoboot_script harness/mame/xie_fix.lua -autoboot_delay 0 &

# baseline, then two fills; screenshot with the REAL geometry each time
./xie/build/dec/decclient 192.168.99.2:0 00 3 9      # no export
./xie/build/dec/decclient 192.168.99.2:0 ff 3 1      # 0xFF
./xie/build/dec/decclient 192.168.99.2:0 00 3 1      # 0x00

python3 dwt/screenshot.py --host 192.168.99.2 --real --out /tmp/shot.png
magick /tmp/shot.png -crop 64x64+150+200 +repage /tmp/win.png

Verified twice, the second time from a freshly started machine.

Corrections to earlier evidence in this directory

  • VRAM_IDENTICAL_FOR_BOTH_FILLS.txt - withdrawn: the dump covered rows 0..63, not the picture. The tile is at rows 200..263 and does differ by fill.
  • CORRECTION_TILE_NOT_IN_VRAM.txt, STAGING_FINAL.txt, STAGING_NOT_FILL_DEPENDENT.txt, FILL_DATA_REACHES_CPU_ONLY.txt - the measurement of 0x004FE790 as "reused heap" was correct, but the conclusion "nothing downstream is fill-dependent" was wrong: the buffer is transient and the data does reach the drawable.
  • ACCEL_TRACE_DIFF.txt - still correct, and not a defect: only the data FIFO is fill-dependent because the destination is passed per call (the drawable's address is in B1, set up by the caller), not written to a register.

Status of the accelerator model

VT1200_ACCEL=1 remains opt-in; the verified default configuration is unchanged (no-env run still parks in the level-0 diagnostics menu at PC=3FDCE400). The model is a status/data handshake plus a 8 KiB output FIFO - it is not a real accelerator, and it is honest about that: it performs no compositing, and the pixels reach the screen because the firmware itself copies the engine's output words into the drawable at B1.