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.
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.