Files
vt1200/dwt/loader/evidence/hostload/README.md
T
SDD 8588340527 dwt/loader: end-to-end host loader over rdebug
hostload.py drives one rdebug connection: jump(malloc) for the COFF buffer,
poke the COFF, poke the loader at 0x01880000, write the descriptor, call the
loader, and verify status 0 plus app evidence (marker cells or xhello stage 6
and a VRAM screenshot). --tftp uses the existing tftpc.bin fetch instead of a
host poke. Adds evidence for two/three fresh boots and appends the integrate
section to dwt/notes/LOADER.md, including the bit-offset result-block layout
and the create_process clobber of the descriptor.
2026-10-18 15:34:25 +03:00

4.7 KiB

hostload evidence (rdebug -> malloc -> COFF -> loader -> app)

All runs: VT1200_SKIP_LEVEL1=1, private -cfg_directory/-nvram_directory, -networkprovider taptun, BOOTP/TFTP from dwt/tcpip/serve_probe.py on 192.168.99.2. The ROM is untouched (md5 b46a3e442ccb86134d2356bd70d22fd2, re-checked after the runs); no driver edits, no breakpoints.

Driver: dwt/loader/hostload.py. Each log is one complete connection.

Malloc -> poke -> descriptor -> loader -> markers (three boots)

log boot jump(malloc) bit ptr byte status / PCB markers
marker_boot2_run4.log 2 0x007df450 0x0FBE8A 0 / 0x003fc5d0 start/data/data2/bss OK, count moving
marker_boot3.log 3 0x007c7290 0x0F8E52 0 / 0x003ffbd0 start/data/data2/bss OK, count moving
marker_boot4.log 4 0x007c8030 0x0F9006 0 / 0x003ffbd0 start/data/data2/bss OK, count moving
marker_nopokeloader_boot4.log 4 0x007d8970 0x0FB12E 0 / 0x003fd590 loader readback 610 words, 0 differing

The marker values in every run are start=0x018C0000, data=0xDEADBEEF, data2=0x12345678, bss=0 after a fresh boot. The counter changes between two samples (multiple children from successive runs can make it go backwards; a fresh boot is monotonic).

Each log shows the full result block: status 0, entry/text/data/bss addresses and sizes, pcb != 0, nscns=3, coff_len=510, loader_base=0x01880000.

xhello -> X window (two boots)

log boot malloc bit ptr stage screenshot
xhello_boot3.log 3 0x007c82d0 6 hostload_xhello_boot3_xhello.png
xhello_boot4.log 4 0x007c9070 6 hostload_xhello_boot4_xhello.png

stage=6 is set after XSync; strlen=4 is the ABI check. The PNGs (1-bit 1024x1024 VRAM, lossless) show the black 400x200 window with its WM title bar at (100,100) over the Terminal Manager — same visual as dwt/loader/evidence/loader_frame2650.png.

Heap placement (read-only dwt/heapmap.py)

heapmap_boot4.txt shows the global free list after the boot-4 runs and checks the two app .text targets: 0x018C0000 and 0x01840000 are both inside the last free block (byte 0x1719BA..0x400000) with about 950 KiB / 0.97 MiB of headroom to its end. The malloc'd COFF buffers (0x007C…, 0x009F…, 0x00AD…) are in the low blocks, far from the loader page (byte 0x310000) and from both targets. That is why the loader's free-list check (a check, not a reservation) passes on these boots.

--tftp: fetched COFF, no host poke of the COFF (two boots)

log boot tftpc buffer RRQ line
tftp_marker_boot3.log 3 0x009fccd0 15:31:47 ... requested 'dwt/tcpip/probe.bin' (510 bytes)
tftp_marker_boot4.log 4 0x009fccd0 15:32:16 ... requested 'dwt/tcpip/probe.bin' (510 bytes)

(serve_tftp_marker_boot2.log is the boot-2 RRQ from the first, pre-logging run: 15:31:07, buffer 0x00b4ce90; its host-side output was not saved.)

The TFTP server was serving dwt/loader/build/marker.coff for any name (--file dwt/loader/build/marker.coff); tftpc.asm's RRQ name is fixed. Each log shows stage=4 fd=1 total=510 done=0x05A5A5A5, then a peek comparison of the fetched buffer against the local COFF: 510/510 bytes, sha256 519e4724aa537436a35f5e49a3306b3f6bcebdc216a0ce4c103822b0df28d749 (same as the file). The loader then used that buffer's bit address in the descriptor.

Failure that is not a failure

The first integration attempts parsed the result block at byte offsets 0x20, 0x40, ... and saw 0x55555555. The loader's constants are bit offsets (RESULT_BASE+020h = +4 bytes), so the real block is 13 adjacent 32-bit cells (52 bytes) at byte offsets 0x00, 0x04, ... 0x30. The stale/free heap fill beyond byte 0x34 reads 0x55. hostload.py now converts bit offsets to bytes; no loader change was needed.

Descriptor quirk worth knowing

After a successful load, a 16-byte window at 0x01882C00 (the descriptor) is overwritten with 0d 00 00 00 <pcb> <ptr> 00 60 00 00 — the create_process call frame from the loader's ARG_AREA (0x01882B00, 0x100 bits below the descriptor). It is harmless: the loader has already read the descriptor, and the result block at 0x01882D00 is untouched. It does mean a second call must rewrite the descriptor, which hostload.py always does immediately before the jump.

Frame dumps

hostload.py --frame N only waits for vt1200-frame-N.ppm (a MAME VT1200_DUMP_FRAME dump). Frame numbers are boot-relative and the dump must be configured on the MAME side; the load-relative evidence here is the rdebug VRAM screenshot, not a MAME frame dump.