mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
480 lines
20 KiB
Markdown
480 lines
20 KiB
Markdown
# The COFF loader: GSPLNK images, position dependence, and `create_process`
|
|
|
|
Status: **DONE for the loader half.** A GSPLNK COFF image poked into RAM is
|
|
parsed, its sections are placed at the addresses in their headers, `.bss` is
|
|
zeroed, and the image runs as a first-class process from
|
|
`create_process(entry, "LOADER", 0x6000, 1, 0)`. Two apps prove it: a marker app
|
|
(`MAGIC`/counter cells) and `xhello` (an X window in a frame dump).
|
|
|
|
The transport half is separate. The loader takes an already-received image and
|
|
**chooses no address of its own**: the COFF section headers carry the addresses.
|
|
|
|
All work is emulator-only: no driver edits, no ROM change
|
|
(`md5 b46a3e442ccb86134d2356bd70d22fd2`), private NVRAM/cfg, no network.
|
|
|
|
---
|
|
|
|
## 1. What GSPLNK emits
|
|
|
|
`dwt/loader/coff.py` is the reference reader. All **addresses and sizes in the
|
|
section headers and optional header are bit addresses/bits**; file offsets
|
|
(`scnptr`, `relptr`, `symptr`) are bytes. This is the same model as
|
|
`harness/hello-world/cofextract.py`, confirmed again here.
|
|
|
|
### 1.1 The file header (20 bytes)
|
|
|
|
```
|
|
00000000: 90 00 03 00 de e0 d4 6a 02 01 00 00 0e 00 00 00
|
|
00000010: 1c 00 03 01 08 01 c7 00 90 02 00 00 40 00 00 00
|
|
00000020: 40 00 00 00 00 00 8c 01 00 00 8c 01 00 00 8d 01
|
|
00000030: 2e 74 65 78 74 00 00 00 00 00 8c 01 00 00 8c 01
|
|
00000040: 90 02 00 00 a8 00 00 00 00 00 00 00 00 00 00 00
|
|
00000050: 00 00 00 00 20 00 00 00 2e 64 61 74 61 00 00 00
|
|
00000060: 00 00 8d 01 00 00 8d 01 40 00 00 00 fa 00 00 00
|
|
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00
|
|
00000080: 2e 62 73 73 00 00 00 00 00 80 8d 01 00 80 8d 01
|
|
00000090: 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
000000a0: 00 00 00 00 80 00 00 00 50 05 40 07 e2 09 00 00
|
|
```
|
|
|
|
This is `dwt/loader/build/marker.coff` (510 bytes). The header at 0x00:
|
|
|
|
| offset | size | field | this file |
|
|
|---:|---:|---|---|
|
|
| 0x00 | 2 | magic | `0x0090` |
|
|
| 0x02 | 2 | section count | `3` |
|
|
| 0x04 | 4 | time stamp | `0x6AD4E0DE` |
|
|
| 0x08 | 4 | symbol table offset (bytes) | `0x102` |
|
|
| 0x0C | 4 | symbol count | `14` |
|
|
| 0x10 | 2 | optional header size (bytes) | `0x1c` (28) |
|
|
| 0x12 | 2 | flags | `0x0103` |
|
|
|
|
`0x0100` is `F_RELFLG` (relocation info stripped); `0x0003` is the executable
|
|
flag set. The same magic `0x0090` appears in `.obj` files, whose flags are
|
|
`0x0100` and whose optional header size is 0.
|
|
|
|
### 1.2 The optional header (28 bytes, at 0x14)
|
|
|
|
| offset | size | field | this file |
|
|
|---:|---:|---|---|
|
|
| 0x14 | 2 | magic | `0x0108` |
|
|
| 0x16 | 2 | version | `0x00c7` |
|
|
| 0x18 | 4 | text size (bits) | `0x290` = 656 bits = 82 B |
|
|
| 0x1C | 4 | data size (bits) | `0x40` = 8 B |
|
|
| 0x20 | 4 | bss size (bits) | `0x40` = 8 B |
|
|
| 0x24 | 4 | **entry point (bits)** | `0x018C0000` |
|
|
| 0x28 | 4 | text start (bits) | `0x018C0000` |
|
|
| 0x2C | 4 | data start (bits) | `0x018D0000` |
|
|
|
|
### 1.3 Section headers (40 bytes each, at `20 + opthdr`)
|
|
|
|
| offset | size | field |
|
|
|---:|---:|---|
|
|
| +0 | 8 | name, NUL-padded |
|
|
| +8 | 4 | **paddr (bits)** |
|
|
| +12 | 4 | vaddr (bits) |
|
|
| +16 | 4 | **size (bits)** |
|
|
| +20 | 4 | scnptr (file offset, bytes) |
|
|
| +24 | 4 | relptr (file offset, bytes) |
|
|
| +28 | 4 | line-number pointer |
|
|
| +32 | 2 | nreloc |
|
|
| +34 | 2 | nlnno |
|
|
| +36 | 4 | flags (`0x20` text, `0x40` data, `0x80` bss) |
|
|
|
|
For `marker.coff`:
|
|
|
|
```
|
|
sec 1 .text paddr=018C0000 size=0x290 bits (82 B) scnptr=0xa8 nreloc=0 flags=0x20
|
|
sec 2 .data paddr=018D0000 size=0x40 bits (8 B) scnptr=0xfa nreloc=0 flags=0x40
|
|
sec 3 .bss paddr=018D8000 size=0x40 bits (8 B) scnptr=0 nreloc=0 flags=0x80
|
|
```
|
|
|
|
Note that GSPLNK reorders sections in the output: the input `.obj` order is
|
|
`.text`, `.data`, `.bss`; the linked output puts `.data`, `.bss`, `.text` in
|
|
`xhello.coff`. A loader must not assume `.text` is first.
|
|
|
|
### 1.4 Symbol table and relocations
|
|
|
|
Symbols are 18 bytes: 8-byte name, 4-byte value, 2-byte section number,
|
|
2-byte type, 1-byte storage class, 1-byte aux count. Section symbols have one
|
|
18-byte aux entry (the section size). Names longer than 8 characters live in
|
|
the string table; the 8-byte field is then an offset.
|
|
|
|
Relocations are 10 bytes: 4-byte address (bit address in the section), 4-byte
|
|
symbol index, 2-byte type. Documented types (Assembler Tools Guide, Table A-7):
|
|
|
|
| type | mnemonic |
|
|
|---:|---|
|
|
| `0x10` | `R-RELWORD` (16-bit direct) |
|
|
| `0x11` | `R-RELLONG` (32-bit direct) |
|
|
| `0x18` | `R-OCRLONG` (1's-complement 32-bit) |
|
|
| `0x19` | `R-GSPPCR16` (16-bit relative, words) |
|
|
|
|
The `.obj` has relocations; the linked `-a` output has **none** (see §2).
|
|
|
|
### 1.5 Entry point
|
|
|
|
GSPLNK writes the entry only when `-e` names a **global** symbol. With
|
|
`.def _start` in the source and `-e _start` in the command file, the map says:
|
|
|
|
```
|
|
ENTRY POINT SYMBOL: "_start" address: 018C0000
|
|
```
|
|
|
|
and the optional header entry is `0x018C0000`. Without a global `_start`
|
|
(`dwt/gui/xhello.asm` has a local `_start`), the map says
|
|
`ENTRY POINT SYMBOL: 0`, the optional entry is 0, but `textstart` is still
|
|
`0x01840000`. The loader therefore uses:
|
|
|
|
```
|
|
entry = optional.entry if nonzero, else optional.textstart
|
|
```
|
|
|
|
For the SDK flow, the entry is the first instruction of `.text` (`_start`) when
|
|
no `-e` is given. It is not the raw file offset and not the section index.
|
|
|
|
---
|
|
|
|
## 2. Position-dependent, not relocatable
|
|
|
|
**The linked image is fully resolved and position-dependent.** The loader must
|
|
place each section at the bit address in its header; it cannot load anywhere and
|
|
relocate.
|
|
|
|
Evidence, from `dwt/loader/reldemo.sh` (full output in
|
|
`dwt/loader/evidence/reldemo.txt`):
|
|
|
|
1. Two objects with cross references (`rel_a.asm` calls `ext_func` and loads
|
|
`ext_data`; `rel_b.asm` defines both) carry real relocations in the `.obj`:
|
|
|
|
```
|
|
sec 1 .text ... size=0xa0 bits (20 B) scnptr=0x8c nreloc=2 flags=0x20 [text]
|
|
reloc at 0x10 sym=8 type=0x11 R-RELLONG
|
|
reloc at 0x70 sym=7 type=0x11 R-RELLONG
|
|
```
|
|
|
|
2. Linking those objects with `-a` produces an output whose sections all have
|
|
`nreloc=0`:
|
|
|
|
```
|
|
sec 1 .text paddr=00018c0000 ... nreloc=0
|
|
sec 2 .data paddr=00018d0000 ... nreloc=0
|
|
```
|
|
|
|
3. Linking the **same** objects at two origin sets
|
|
(`018C0000/018D0000/018D8000` and `018E0000/018F0000/018F8000`) produces
|
|
different bytes — the absolute `ext_data`/`ext_func` operands:
|
|
|
|
```
|
|
== .text: 22 vs 22 bytes
|
|
differing bytes: 2
|
|
0x0004: 8d -> 8f
|
|
0x0010: 8c -> 8e
|
|
```
|
|
|
|
The marker app behaves the same way: with `.text` linked at `018C0000` it
|
|
contains the immediate `0x018C0000`; at `018E0000` the same byte changes to
|
|
`0x018E0000`. `movi _start` and `calla` are absolute.
|
|
|
|
4. Linking without `-a` **still** yields `nreloc=0` (and paddr values relative
|
|
to 0, i.e. an unbound image). GSPLNK 2.00 does not emit a relocatable
|
|
executable for this flow; the toolchain's "relocatable" form is the input
|
|
`.obj`, not the linked image.
|
|
|
|
Consequence: the loader copies to the linked address and does **not** malloc
|
|
the segments. It refuses any input COFF that still carries relocation records
|
|
(status 7), rather than guessing at relocation semantics. That also catches the
|
|
easy mistake of feeding the `.obj` instead of the linked `.out`.
|
|
|
|
### What "fixed address" means for the loader
|
|
|
|
The loader checks that the target range lies inside one block of the firmware's
|
|
**global free list** (head at bit `0x1E8AC0`, block header `{u32 size_bits; …;
|
|
u32 next@+0x20}`, user data at `block+0x40`; see `dwt/notes/HEAP.md`). This is a
|
|
**check, not a reservation**: the loader cannot make `malloc` return a chosen
|
|
address. The caller is responsible for choosing a link address that is free
|
|
when the app starts and for not handing that range out later. The loader's own
|
|
page and the COFF buffer are checked separately for overlap.
|
|
|
|
---
|
|
|
|
## 3. ELF: not produced, not supported
|
|
|
|
The 1987 toolchain has **no ELF output**. The Assembler Tools Guide describes
|
|
COFF as the object format and GSPROM as the converter to EPROM formats:
|
|
|
|
> "The object format converter converts a COFF object file into Intel hex,
|
|
> Tektronix hex, or TI-tagged object format."
|
|
|
|
The only "ELF" case-insensitive hits in the assembler/C guides are unrelated
|
|
words (the `CIF` command text). There is no ELF writer, no ELF reader and no
|
|
ELF linker option. The 1990 OS/2 r5.01 tools are also TI COFF, not ELF. So the
|
|
loader targets **COFF only**; an ELF loader would have nothing in this SDK to
|
|
load. (`dwt/loader/evidence/elf_absent.txt` records the check.)
|
|
|
|
---
|
|
|
|
## 4. The loader
|
|
|
|
Source `dwt/loader/loader.asm`, built by `dwt/loader/build.sh` and delivered as
|
|
raw bytes at its linked bit address.
|
|
|
|
| item | value |
|
|
|---|---|
|
|
| loader link/load address | bit `0x01880000` (byte `0x310000`) |
|
|
| loader `.text` size | `0x2620` bits = **1220 bytes** |
|
|
| reserved loader page | bits `0x01880000`..`0x01883200` |
|
|
| descriptor | bit `0x01882C00` |
|
|
| result block | bit `0x01882D00` |
|
|
| return stub (harness only) | bit `0x01883100` |
|
|
| app stack | `create_process` allocates it; the loader passes `0x6000` bytes |
|
|
| `create_process` | bit `0x3F706A40` / Ghidra `0x07EE0D48` |
|
|
|
|
The loader is **position-dependent itself**: its labels are absolute, so the raw
|
|
image must be placed at `0x01880000`. That is acceptable for the current
|
|
delivery (debugger now, transport later); relinking the loader is a build-time
|
|
change, not a runtime one.
|
|
|
|
### 4.1 Interface
|
|
|
|
The loader takes a 16-byte descriptor either at the fixed address or through
|
|
`A8`. Offsets below are **byte** offsets; the assembly uses the equivalent bit
|
|
offsets (`+0`, `+0x10`, `+0x20`, `+0x40`, `+0x60`):
|
|
|
|
```
|
|
DESC + 0x00 u16 0x4C44 "LD" ) both must match
|
|
DESC + 0x02 u16 0x5231 "R1" )
|
|
DESC + 0x04 u32 buf bit address of the COFF image
|
|
DESC + 0x08 u32 len image length in bytes
|
|
DESC + 0x0C u32 mode 1 = return to caller; 0 = debug resume
|
|
```
|
|
|
|
`A8` may be 0 (fixed descriptor) or the descriptor's bit address. Any other
|
|
value falls back to the fixed descriptor if it does not carry the magic. The
|
|
loader reads `buf`, `len` and `mode` once and then parses the COFF.
|
|
|
|
The result block is at `0x01882D00`, 32-bit cells at +0x20 intervals:
|
|
|
|
| offset | meaning |
|
|
|---:|---|
|
|
| `+0x000` | status: 0 = OK, else error 1..10 |
|
|
| `+0x020` | entry passed to `create_process` (bits) |
|
|
| `+0x040` | `.text` paddr (bits) |
|
|
| `+0x060` | `.text` size (bits) |
|
|
| `+0x080` | `.data` paddr / `+0x0A0` size |
|
|
| `+0x0C0` | `.bss` paddr / `+0x0E0` size |
|
|
| `+0x100` | relocation records seen |
|
|
| `+0x120` | PCB returned by `create_process` |
|
|
| `+0x140` | section count |
|
|
| `+0x160` | descriptor length (bytes) |
|
|
| `+0x180` | loader base sanity value `0x01880000` |
|
|
|
|
Status codes: 1 bad descriptor; 2 truncated file/section headers; 3 bad COFF
|
|
magic; 4 section size not a whole number of 16-bit words; 5 target outside RAM;
|
|
6 target overlaps the loader page or the COFF buffer; 7 relocation records
|
|
present; 8 target not inside a global free-list block; 9 no entry point;
|
|
10 `create_process` returned 0.
|
|
|
|
### 4.2 Algorithm
|
|
|
|
1. Save `A14` and `SP`, set 16/32-bit field sizes, read the descriptor.
|
|
2. Read the file header: magic `0x0090`, `nscns`, `opthdr`; check
|
|
`20 + opthdr + nscns*40 <= len`.
|
|
3. Read the optional header: `entry`, else `textstart`.
|
|
4. For each 40-byte section header:
|
|
* record the first `.text`/`.data`/`.bss` address and size from flags;
|
|
* refuse if `nreloc != 0`;
|
|
* require `size % 16 == 0`;
|
|
* check `scnptr + size/8 <= len` when `scnptr != 0`;
|
|
* check `0x100 <= paddr` and `paddr + size <= 0x2000000` (4 MiB of bits);
|
|
* check the range does not overlap `[0x01880000,0x01883200)` or the COFF
|
|
buffer;
|
|
* check the range is inside one global free-list block;
|
|
* if `scnptr != 0`, copy `size/16` words from `buf + scnptr*8` to `paddr`;
|
|
* else store `size/16` zero words at `paddr` (this is `.bss` and also any
|
|
uninitialized named section).
|
|
5. Call `create_process(entry, "LOADER", 0x6000, 1, 0)` with the firmware's
|
|
upward-argument convention (the same shape as `dwt/gui/xlaunch.asm`):
|
|
|
|
```
|
|
move a14, -*sp, 1
|
|
movi ARG_AREA, a14, L
|
|
movi 0, a0, L / move a0,*a14+,1 ; arg4 = 0
|
|
movi 1, a0, L / move a0,*a14+,1 ; arg3 = 1
|
|
movi 6000h, a0, L / move a0,*a14+,1 ; arg2 = stack bytes
|
|
movi appname, a0, L / move a0,*a14+,1 ; arg1 = name
|
|
move entry, a0, 1 / move a0,*a14+,1 ; arg0 = entry (bits)
|
|
calla 3F706A40h
|
|
```
|
|
|
|
6. Store `A8` (the PCB) and the status. If `mode == 1`, restore `SP`/`A14`,
|
|
return the status in `A8` and `rets`. If `mode == 0`, restore the interrupted
|
|
state and `jump` to the PC the harness saved, so the OS idle loop runs and
|
|
schedules the child.
|
|
|
|
### 4.3 Why mode 0 returns to the idle loop
|
|
|
|
A payload that spins forever in the current process starves the firmware's
|
|
scheduler in this OS: the child is created but never runs. The first loader
|
|
version did exactly that (`PC=01882460`, no markers). The fix is to restore the
|
|
hijacked context and resume at the saved PC (the OS idle loop), which yields.
|
|
Mode 1 returns to the caller instead; a transport that returns to its own
|
|
event loop behaves the same way. The debugger harness also builds a return stub
|
|
(`jauc <orig_pc>`) for mode 1, so both modes were exercised without a spin.
|
|
|
|
---
|
|
|
|
## 5. Evidence
|
|
|
|
### 5.1 Marker app: sections placed, `.bss` zeroed, child scheduled
|
|
|
|
`dwt/loader/build/marker.coff`, linked at `.text 018C0000`, `.data 018D0000`,
|
|
`.bss 018D8000`. The harness pre-fills `.bss` with `0xA5A5` before the load.
|
|
`LOADER_MODE=0`:
|
|
|
|
```
|
|
LOADER: result status=0 (OK) entry=018C0000 text=018C0000/656 bits data=018D0000/64 bits bss=018D8000/64 bits relocs=0 pcb=003FFBD0 nscns=3 len=510
|
|
LOADER: placed .text at 018C0000: 82 bytes, 0 differing
|
|
LOADER: placed .data at 018D0000: 8 bytes, 0 differing
|
|
LOADER: marker app: start=018C0000 data=DEADBEEF data2=12345678 bss=00000000 count=619
|
|
```
|
|
|
|
* `start=018C0000` is the app reading its own `_start` label: it ran at the
|
|
linked address.
|
|
* `data=DEADBEEF`, `data2=12345678`: `.data` was copied.
|
|
* `bss=00000000` after an `A5A5` pre-fill: `.bss` was zeroed by the loader.
|
|
* `count` grows every sample: the *child* is live, not a one-shot jump.
|
|
* `pcb=003FFBD0`: `create_process` returned a real PCB.
|
|
|
|
`LOADER_MODE=1` (descriptor + `A8` variants) gives the same status and markers;
|
|
the loader returns to the caller (`dwt/loader/evidence/marker_mode1_run.log`,
|
|
`marker_a8desc_run.log`).
|
|
|
|
### 5.2 `xhello`: an X window from a created process
|
|
|
|
Same loader, `dwt/loader/build/xhello.coff` (entry 0, `textstart=01840000`):
|
|
|
|
```
|
|
LOADER: result status=0 (OK) entry=01840000 text=01840000/3120 bits data=00000000/0 bits bss=00000000/0 bits relocs=0 pcb=003FFBD0 nscns=3 len=774
|
|
LOADER: placed .text at 01840000: 390 bytes, 0 differing
|
|
LOADER: xhello: stage=6 strlen=4 display=006BC910 root=0001006B window=00010001 sync=00060001
|
|
```
|
|
|
|
`stage=6` is the last marker in `xhello.asm` (after `XSync`), and the frame dump
|
|
`dwt/loader/evidence/loader_frame2650.png` shows the black 400x200 window with
|
|
the WM title bar at (100,100), over the Terminal Manager. The `root`/`window`
|
|
cells are unreliable here because `xhello`'s marker cells are only 0x10 bits
|
|
apart and a 32-bit write overlaps the next cell; `stage` and the frame are the
|
|
evidence. (The same overlap exists in the committed `xhello` markers; it does
|
|
not affect execution.)
|
|
|
|
### 5.3 Negative: an unlinked `.obj` is refused
|
|
|
|
`LOADER_COFF=dwt/loader/build/rela.obj` (the two-object demo input):
|
|
|
|
```
|
|
LOADER: result status=7 (relocation records present (unsupported)) entry=00000000 ... relocs=2 pcb=00000000
|
|
LOADER: placed .text at 00000000: 20 bytes, 5 differing
|
|
```
|
|
|
|
The loader saw the two `R-RELLONG` records, refused before copying, and left the
|
|
target memory untouched. This is the correct failure for the transport sending
|
|
a `.obj` rather than the linked `.out`.
|
|
|
|
---
|
|
|
|
## 6. What the TFTP transport must hand it
|
|
|
|
See `dwt/notes/LOADER_CROSS.md` for the append-only interface note. In short:
|
|
|
|
* a RAM buffer holding the **complete GSPLNK COFF**, with its bit address and
|
|
byte length;
|
|
* the loader resident at `0x01880000` (raw `dwt/loader/build/loader.bin`);
|
|
* a descriptor at `0x01882C00` with `buf`, `len`, `mode=1`;
|
|
* a normal `calla 0x01880000` (or `A8 = descriptor address`);
|
|
* the loader chooses the load address from the COFF and returns status in `A8`.
|
|
|
|
The transport does **not** pass a load address, and it must not use
|
|
`mode=0` (that mode resumes the debugger-hijacked PC).
|
|
|
|
---
|
|
|
|
## 7. Limits and failure modes
|
|
|
|
* **Position-dependent.** The image must be linked for the address it will run
|
|
at; the loader never relocates. A moved image is a different link.
|
|
* **No reservation.** The free-list check proves the target is in a free block
|
|
*now*; nothing stops a later `malloc` from splitting that block into the
|
|
app's address. Link apps away from the allocator's low-address growth, as the
|
|
current examples do (the big free block grows from byte `0x12191A` upward;
|
|
the apps are at byte `0x318000`+).
|
|
* **No relocation records.** Status 7. The SDK cannot emit them in a linked
|
|
image anyway.
|
|
* **Word-granular.** Section sizes must be multiples of 16 bits; the copy and
|
|
zero loops move 16-bit words. GSPA/GSPLNK always emit such sizes.
|
|
* **Fixed loader address.** The loader's own code is absolute. A transport that
|
|
wants the loader elsewhere must relink it, not move it.
|
|
* **One process per call, no unload.** `create_process` gets a name string
|
|
`"LOADER"`, a `0x6000`-byte stack and `arg = 0`; the loader has no argv or
|
|
environment channel. `create_process` returns 0 only on failure (status 10).
|
|
* **The COFF buffer must not overlap the loader page or the target sections.**
|
|
The loader checks both; the transport must keep its own receive buffer and
|
|
code out of the target range too (the loader cannot know about them).
|
|
* **Heap-list read.** Only the global free list is walked (the per-arena lists
|
|
are unsafe to follow — `HEAP.md`). If the list is empty or the target is not
|
|
in it, the loader fails with status 8 instead of corrupting memory.
|
|
* **C runtime.** `.text`/`.data`/`.cinit`/named initialized sections are
|
|
copied and `.bss` is zeroed, but the loader does not run the `.cinit` table;
|
|
a C app must still enter through `_c_int00` and do its own autoinit as the
|
|
SDK runtime expects. The loader's `entry` is whatever the COFF says.
|
|
|
|
## 8. Reproduce
|
|
|
|
```sh
|
|
# loader (raw image, fixed address)
|
|
./dwt/loader/build.sh dwt/loader/loader.asm 01880000
|
|
|
|
# marker app: three sections at three addresses
|
|
./dwt/loader/build.sh dwt/loader/marker.asm 018C0000 018D0000 018D8000
|
|
|
|
# xhello COFF (entry stays 0; the loader uses textstart)
|
|
./dwt/loader/build.sh dwt/gui/xhello.asm 01840000
|
|
|
|
# relocation / position-dependence demo
|
|
./dwt/loader/reldemo.sh
|
|
|
|
# debugger-assisted run (no network, private NVRAM/cfg)
|
|
VT1200_SKIP_LEVEL1=1 LOADER_AT=2500 LOADER_END=2620 \
|
|
LOADER_COFF=dwt/loader/build/marker.coff LOADER_OUT=$PWD/dwt/loader/evidence \
|
|
/home/arti/code/k-space/mame0289/vt1200 vt1200 \
|
|
-rompath harness/mame/roms -video none -sound none -nothrottle -skip_gameinfo \
|
|
-nvram_directory $PWD/dwt/loader/run/nvram \
|
|
-cfg_directory $PWD/dwt/loader/run/cfg \
|
|
-debug -debugger none \
|
|
-autoboot_script dwt/loader/loader.lua -autoboot_delay 0
|
|
```
|
|
|
|
`LOADER_MODE=1` exercises the callable/return path; `LOADER_A8_DESC=1` passes
|
|
the descriptor in `A8`; `LOADER_SNAP=1` writes a PNG at `LOADER_END`.
|
|
|
|
## 9. Evidence files
|
|
|
|
| path | contents |
|
|
|---|---|
|
|
| `dwt/loader/loader.asm` | the loader |
|
|
| `dwt/loader/marker.asm` | marker app with `.data`/`.bss` at separate addresses |
|
|
| `dwt/loader/coff.py` | COFF reader / dumper (`summary`, `raw`, `manifest`) |
|
|
| `dwt/loader/build.sh` | SDK build wrapper, keeps the COFF |
|
|
| `dwt/loader/reldemo.sh`, `rel_a.asm`, `rel_b.asm` | relocation / origin demo |
|
|
| `dwt/loader/loader.lua` | debugger-assisted harness (no network) |
|
|
| `dwt/loader/probe_os.lua` | heap-list + register-write probe used to design the harness |
|
|
| `dwt/loader/evidence/coff_layout.txt` | hexdump + parsed headers of marker/xhello/obj |
|
|
| `dwt/loader/evidence/reldemo.txt` | the full relocation/origin output |
|
|
| `dwt/loader/evidence/elf_absent.txt` | the ELF check |
|
|
| `dwt/loader/evidence/marker_run.log` | mode-0 marker run |
|
|
| `dwt/loader/evidence/marker_mode1_run.log` | callable/return run |
|
|
| `dwt/loader/evidence/marker_a8desc_run.log` | descriptor in `A8` |
|
|
| `dwt/loader/evidence/xhello_run.log` | xhello run |
|
|
| `dwt/loader/evidence/loader_frame2650.png` | the xhello window |
|
|
| `dwt/loader/evidence/reloc_refused.log` | `.obj` refused with status 7 |
|