mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-11-01 12:14:56 +02:00
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.
92 lines
4.7 KiB
Markdown
92 lines
4.7 KiB
Markdown
# 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.
|