diff --git a/dwt/loader/.gitignore b/dwt/loader/.gitignore new file mode 100644 index 0000000..456334e --- /dev/null +++ b/dwt/loader/.gitignore @@ -0,0 +1,3 @@ +# Local SDK extraction and private emulator state; never commit. +work/ +run/ diff --git a/dwt/loader/README.md b/dwt/loader/README.md new file mode 100644 index 0000000..331828c --- /dev/null +++ b/dwt/loader/README.md @@ -0,0 +1,60 @@ +# dwt/loader — the COFF loader half of the app loader + +Loads a GSPLNK-linked COFF image that is already in RAM into a **first-class +process** on the VT1200 OS. The transport (TFTP) is separate; this half takes a +buffer and length, picks the addresses from the COFF, and calls +`create_process`. + +Read `dwt/notes/LOADER.md` for the full report and +`dwt/notes/LOADER_CROSS.md` for the transport interface. + +## Files + +| file | purpose | +|---|---| +| `loader.asm` | the loader; linked at bit `0x01880000`, raw `.bin` 1220 B | +| `marker.asm` | acceptance app with `.text`/`.data`/`.bss` at three addresses | +| `coff.py` | TI COFF reader/dumper (`summary`, `raw`, `manifest`) | +| `build.sh` | GSPA/GSPLNK wrapper; keeps the linked COFF | +| `reldemo.sh`, `rel_a.asm`, `rel_b.asm` | relocation and position-dependence demo | +| `loader.lua` | debugger-assisted harness (no network, no rdebug) | +| `probe_os.lua` | heap-list and register-write probe used to design the harness | +| `build/` | linked loader/app COFFs, raw `.bin`s, maps, listings | +| `evidence/` | run logs, COFF dumps, the xhello window frame | + +`work/` (the DOS tools extracted from the SDK floppy) and `run/` (private +NVRAM/cfg) are local scratch and ignored. + +## Build + +```sh +./dwt/loader/build.sh dwt/loader/loader.asm 01880000 +./dwt/loader/build.sh dwt/loader/marker.asm 018C0000 018D0000 018D8000 +./dwt/loader/build.sh dwt/gui/xhello.asm 01840000 +./dwt/loader/reldemo.sh +``` + +`build.sh` takes `SOURCE.asm [TEXT_ORIGIN [DATA_ORIGIN [BSS_ORIGIN]]]` +(origins are **bit** addresses) and writes `build/.coff`, `.bin`, `.map`, +`.lst`, `.obj`. + +## Run (debugger-assisted, no network) + +```sh +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 +``` + +The script pokes the loader at `0x01880000` and the COFF at `LOADER_BUF` +(default `0x01900000`), writes the descriptor, and sets `PC`. It never uses the +network, the rdebug service or a firmware transport. + +Knobs: `LOADER_MODE=1` (callable/return instead of debug-resume), +`LOADER_A8_DESC=1` (descriptor in `A8`), `LOADER_SNAP=1` (PNG at `LOADER_END`), +`LOADER_VERBOSE=1`, `LOADER_TRACE=1`. diff --git a/dwt/loader/build.sh b/dwt/loader/build.sh new file mode 100755 index 0000000..577859e --- /dev/null +++ b/dwt/loader/build.sh @@ -0,0 +1,105 @@ +#!/bin/sh +# Build a VT1200 payload/app with the 1987 TI TMS34010 SDK (GSPA/GSPLNK under +# DOSBox), keeping the linked COFF so dwt/loader/coff.py can inspect it. +# +# dwt/loader/build.sh SOURCE.asm [TEXT_ORIGIN [DATA_ORIGIN [BSS_ORIGIN]]] +# +# Origins are BIT addresses (the TMS34010 and GSPLNK's SECTIONS origins are +# bit addresses). With only TEXT_ORIGIN the linker command binds .text alone, +# exactly like dwt/gui/build.sh. With DATA_ORIGIN/BSS_ORIGIN it binds all +# three sections, which is what the marker app needs. +# +# Outputs, all under dwt/loader/build/: +# .bin the linked .text bytes (convenience; not a COFF) +# .coff the GSPLNK output, byte-for-byte (the loader's real input) +# .map the link map +# .lst the assembler listing (addresses in bits) +# +# ENTRY_SYM= adds `-e `; default is `_start` if the symbol +# exists, otherwise no -e (GSPLNK then writes entry = 0). +set -eu + +ROOT=$(cd "$(dirname "$0")/../.." && pwd) +DIR=$(cd "$(dirname "$0")" && pwd) +SRC=${1:?usage: build.sh SOURCE.asm [TEXT_ORIGIN [DATA_ORIGIN [BSS_ORIGIN]]]} +TEXT=${2:-01800000} +DATA=${3:-} +BSS=${4:-} +NAME=$(basename "$SRC" .asm) +BASE=$(printf '%s' "$NAME" | tr 'a-z' 'A-Z') +WORK="$DIR/work" +OUT="$DIR/build" +IMG="$ROOT/tools/tms34010-sdk/1987-05 TMS34010 SDK/1987-11-06 TMS34010 Simulator-C Tools-ASM Tools.img" +DOSBOX_CONF="$ROOT/harness/sdk-scratch/dosbox-bench.conf" + +mkdir -p "$WORK" "$OUT" + +if [ ! -x "$WORK/GSPA.EXE" ]; then + mcopy -s -n -i "$IMG" "::/A2/*" "$WORK/" + mcopy -s -n -i "$IMG" "::/C2/*" "$WORK/" +fi + +cp "$SRC" "$WORK/$BASE.ASM" + +# TI hex constants must begin with a decimal digit. +tihex() { + case "$1" in + [0-9]*) printf '%s' "$1" ;; + *) printf '0%s' "$1" ;; + esac +} + +TEXT=$(tihex "$TEXT") +ENTRY=${ENTRY_SYM:-_start} + +{ + printf '%s\n' '-a' + printf '%s\n' '-o '"$BASE"'.out' + printf '%s\n' '-m '"$BASE"'.map' + [ -n "${ENTRY:-}" ] && printf '%s\n' '-e '"$ENTRY" + printf '%s\n' "$BASE.obj" + printf '%s\n' 'SECTIONS' + printf '%s\n' '{' + printf '%s\n' " .text ${TEXT}h: {}" + if [ -n "$DATA" ]; then + printf '%s\n' " .data $(tihex "$DATA")h: {}" + fi + if [ -n "$BSS" ]; then + printf '%s\n' " .bss $(tihex "$BSS")h: {}" + fi + printf '%s\n' '}' +} > "$WORK/$BASE.CMD" +sed -i 's/$/\r/' "$WORK/$BASE.CMD" + +rm -f "$WORK/$BASE.OBJ" "$WORK/$BASE.OUT" "$WORK/$BASE.LST" \ + "$WORK/$BASE.MAP" "$WORK/$BASE.HI" "$WORK/$BASE.LO" + +SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \ +timeout 180 dosbox -conf "$DOSBOX_CONF" -noprimaryconf -nolocalconf \ + -c "mount c $WORK" -c "c:" \ + -c "gspa $BASE $BASE.obj $BASE.lst -l" \ + -c "gsplnk $BASE.cmd" \ + -c "exit" -exit >/dev/null 2>&1 + +if [ ! -f "$WORK/$BASE.OUT" ]; then + echo "build.sh: GSPLNK produced no $BASE.OUT" >&2 + sed 's/\r$//' "$WORK/$BASE.MAP" 2>/dev/null | tail -20 >&2 || true + exit 1 +fi +if [ ! -f "$WORK/$BASE.OBJ" ]; then + echo "build.sh: GSPA produced no $BASE.OBJ" >&2 + grep -n '\*\*\*\*\*' "$WORK/$BASE.LST" >&2 || true + exit 1 +fi +if grep -q '\*\*\*\*\*' "$WORK/$BASE.LST"; then + echo "build.sh: assembler diagnostics:" >&2 + grep -n '\*\*\*\*\*' "$WORK/$BASE.LST" >&2 + exit 1 +fi + +cp "$WORK/$BASE.OUT" "$OUT/$NAME.coff" +cp "$WORK/$BASE.OBJ" "$OUT/$NAME.obj" +sed 's/\r$//' "$WORK/$BASE.MAP" > "$OUT/$NAME.map" +cp "$WORK/$BASE.LST" "$OUT/$NAME.lst" +python3 "$ROOT/harness/hello-world/cofextract.py" "$WORK/$BASE.OUT" "$OUT/$NAME.bin" +echo "build.sh: $OUT/$NAME.coff ($(wc -c < "$OUT/$NAME.coff") bytes); .bin $(wc -c < "$OUT/$NAME.bin") bytes at bit $TEXT" diff --git a/dwt/loader/build/loader.bin b/dwt/loader/build/loader.bin new file mode 100644 index 0000000..192fb09 Binary files /dev/null and b/dwt/loader/build/loader.bin differ diff --git a/dwt/loader/build/loader.coff b/dwt/loader/build/loader.coff new file mode 100644 index 0000000..9fcaed5 Binary files /dev/null and b/dwt/loader/build/loader.coff differ diff --git a/dwt/loader/build/loader.lst b/dwt/loader/build/loader.lst new file mode 100644 index 0000000..6bb1c68 --- /dev/null +++ b/dwt/loader/build/loader.lst @@ -0,0 +1,746 @@ +GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 1 + + 0001 ; loader.asm -- COFF loader for the VT1200 / TMS34010. + 0002 ; + 0003 ; Input: a TI GSP COFF image (GSPLNK `-a` output) alrea + 0004 ; 16-byte descriptor (offsets in BITS, since all pointe + 0005 ; addresses; the byte offsets are 0, 2, 4, 8 and 12): + 0006 ; + 0007 ; DESC + 0x00 bits u16 0x4C44 "LD" ) magic; both + 0008 ; DESC + 0x10 bits u16 0x5231 "R1" ) + 0009 ; DESC + 0x20 bits u32 buf bit address of + 0010 ; DESC + 0x40 bits u32 len length in byte + 0011 ; DESC + 0x60 bits u32 mode 1 = return to + 0012 ; 0 = debug resu + 0013 ; + 0014 ; The descriptor pointer is A8. If A8 is zero, or does + 0015 ; descriptor, the fixed descriptor at 0x01882C00 is use + 0016 ; receives a COFF over the network only has to write {b + 0017 ; call the loader entry; it does not choose a load addr + 0018 ; the addresses from the COFF section headers. + 0019 ; + 0020 ; Result block at 0x01882D00 (32-bit cells, bit address + 0021 ; + 0022 ; +0x000 status 0 ok, nonzero = error (see the l + 0023 ; +0x020 entry bit address passed to create_pro + 0024 ; +0x040 text addr .text paddr from the COFF (bits) + 0025 ; +0x060 text size .text size (bits) + 0026 ; +0x080 data addr .data paddr + 0027 ; +0x0A0 data size .data size (bits) + 0028 ; +0x0C0 bss addr .bss paddr + 0029 ; +0x0E0 bss size .bss size (bits) + 0030 ; +0x100 relocs relocation entries seen in the o + 0031 ; +0x120 pcb create_process() return value (0 + 0032 ; +0x140 nscns section count from the file head + 0033 ; +0x160 coflen length from the descriptor (byte + 0034 ; +0x180 version loader base address, 0x01880000 + 0035 ; + 0036 ; The image is POSITION-DEPENDENT: GSPLNK resolves ever + 0037 ; nreloc = 0, so each section is copied to the bit addr + 0038 ; no relocation is applied. A COFF that still carries + 0039 ; refused (status 7) rather than guessed at. + 0040 ; + 0041 ; Safety checks before any copy: + 0042 ; * size is a whole number of 16-bit words; + 0043 ; * the section bytes lie inside the descriptor's (bu + 0044 ; * paddr..paddr+size is inside main RAM (bits 0x100. + 0045 ; * it does not overlap the loader page [01880000,018 + 0046 ; buffer; + 0047 ; * it lies inside one block of the firmware's global + 0048 ; (head at bit 0x1E8AC0, block header {u32 size_bit + 0049 ; user data at block+0x40). This is a check, not a + 0050 ; loader cannot make malloc hand out a chosen addre + 0051 ; + 0052 ; Delivered debugger-assisted for now: poke the raw .bi + 0053 ; the COFF at buf, write the descriptor, then set PC = + 0054 ; fine). mode = 0 resumes at LD_ORIG_PC (the harness s + 0055 ; PC); mode = 1 returns to the caller and passes the st + 0056 ; + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 2 + + 0057 ; Linked at bit 0x01880000; position-dependent itself. + 0058 + 0059 .def _start + 0060 + 0061 01880000 LOADER_BASE equ 01880000h + 0062 01882800 LD_SCRATCH equ 01882800h + 0063 01882C00 DESC_ADDR equ 01882C00h + 0064 01882D00 RESULT_BASE equ 01882D00h + 0065 01882B00 ARG_AREA equ 01882B00h + 0066 01883100 RETURN_STUB equ 01883100h + 0067 + 0068 01882800 LD_SP_AFTER equ LD_SCRATCH+000h + 0069 01882820 LD_A14 equ LD_SCRATCH+020h + 0070 01882840 LD_ENTRY equ LD_SCRATCH+040h + 0071 01882860 LD_BUF equ LD_SCRATCH+060h + 0072 01882880 LD_LEN equ LD_SCRATCH+080h + 0073 018828A0 LD_MODE equ LD_SCRATCH+0A0h + 0074 018828C0 LD_NSCNS equ LD_SCRATCH+0C0h + 0075 018828E0 LD_RELOCS equ LD_SCRATCH+0E0h + 0076 01882900 LD_SEC_PADDR equ LD_SCRATCH+100h + 0077 01882920 LD_SEC_SIZE equ LD_SCRATCH+120h + 0078 01882940 LD_SEC_SCNPTR equ LD_SCRATCH+140h + 0079 01882960 LD_TEXT_ADDR equ LD_SCRATCH+160h + 0080 01882980 LD_TEXT_SIZE equ LD_SCRATCH+180h + 0081 018829A0 LD_DATA_ADDR equ LD_SCRATCH+1A0h + 0082 018829C0 LD_DATA_SIZE equ LD_SCRATCH+1C0h + 0083 018829E0 LD_BSS_ADDR equ LD_SCRATCH+1E0h + 0084 01882A00 LD_BSS_SIZE equ LD_SCRATCH+200h + 0085 01882A20 LD_ORIG_PC equ LD_SCRATCH+220h + 0086 + 0087 01882D00 RESULT_STATUS equ RESULT_BASE+000h + 0088 01882D20 RESULT_ENTRY equ RESULT_BASE+020h + 0089 01882D40 RESULT_TEXT_A equ RESULT_BASE+040h + 0090 01882D60 RESULT_TEXT_S equ RESULT_BASE+060h + 0091 01882D80 RESULT_DATA_A equ RESULT_BASE+080h + 0092 01882DA0 RESULT_DATA_S equ RESULT_BASE+0A0h + 0093 01882DC0 RESULT_BSS_A equ RESULT_BASE+0C0h + 0094 01882DE0 RESULT_BSS_S equ RESULT_BASE+0E0h + 0095 01882E00 RESULT_RELOCS equ RESULT_BASE+100h + 0096 01882E20 RESULT_PCB equ RESULT_BASE+120h + 0097 01882E40 RESULT_NSCNS equ RESULT_BASE+140h + 0098 01882E60 RESULT_LEN equ RESULT_BASE+160h + 0099 01882E80 RESULT_VERSION equ RESULT_BASE+180h + 0100 + 0101 00000000 .text + 0102 00000000 _start: + 0103 00000000 098F mmtm sp, a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13 + 00000010 FFFC + 0104 00000020 078F move sp, @LD_SP_AFTER, 1 + 00000030 01882800 + 0105 00000050 078E move a14, @LD_A14, 1 + 00000060 01882820 + 0106 00000080 0550 setf 16, 0, 0 + 0107 00000090 0740 setf 32, 0, 1 + 0108 + 0109 ; ---- descriptor ------------------------------------ + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 3 + + 0110 ; A8 points at it only if both magic words match; else + 0111 000000A0 4D06 move a8, a6 + 0112 000000B0 4D08 move a8, a8 + 0113 000000C0 CA09 jreq ld_fixed_desc + 0114 000000D0 84C0 move *a6, a0, 0 + 0115 000000E0 0B40 cmpi 4C44h, a0 + 000000F0 B3BB + 0116 00000100 CB05 jrne ld_fixed_desc + 0117 00000110 B4C0 move *a6(10h), a0, 0 + 00000120 0010 + 0118 00000130 0B40 cmpi 5231h, a0 + 00000140 ADCE + 0119 00000150 CA0E jreq ld_have_desc + 0120 00000160 ld_fixed_desc: + 0121 00000160 09E6 movi DESC_ADDR, a6, L + 00000170 01882C00 + 0122 00000190 84C0 move *a6, a0, 0 + 0123 000001A0 0B40 cmpi 4C44h, a0 + 000001B0 B3BB + 0124 000001C0 CB00 jrne ld_bad_desc + 000001D0 01D5 + 0125 000001E0 B4C0 move *a6(10h), a0, 0 + 000001F0 0010 + 0126 00000200 0B40 cmpi 5231h, a0 + 00000210 ADCE + 0127 00000220 CB00 jrne ld_bad_desc + 00000230 01CF + 0128 00000240 ld_have_desc: + 0129 00000240 B6C7 move *a6(20h), a7, 1 ; buf + 00000250 0020 + 0130 00000260 B6C8 move *a6(40h), a8, 1 ; len + 00000270 0040 + 0131 00000280 B6CC move *a6(60h), a12, 1 ; mode + 00000290 0060 + 0132 000002A0 0787 move a7, @LD_BUF, 1 + 000002B0 01882860 + 0133 000002D0 0788 move a8, @LD_LEN, 1 + 000002E0 01882880 + 0134 00000300 078C move a12, @LD_MODE, 1 + 00000310 018828A0 + 0135 00000330 078C move a12, @RESULT_VERSION, 1 ; harmless; overwritten b + 00000340 01882E80 + 0136 00000360 09E0 movi LOADER_BASE, a0, L + 00000370 01880000 + 0137 00000390 0780 move a0, @RESULT_VERSION, 1 + 000003A0 01882E80 + 0138 + 0139 ; clear the counters we report; status stays FF until + 0140 000003C0 09E0 movi 0FFFFFFFFh, a0, L + 000003D0 FFFFFFFF + 0141 000003F0 0780 move a0, @RESULT_STATUS, 1 + 00000400 01882D00 + 0142 00000420 4400 sub a0, a0 + 0143 00000430 0780 move a0, @LD_RELOCS, 1 + 00000440 018828E0 + 0144 00000460 0780 move a0, @LD_TEXT_ADDR, 1 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 4 + + 00000470 01882960 + 0145 00000490 0780 move a0, @LD_TEXT_SIZE, 1 + 000004A0 01882980 + 0146 000004C0 0780 move a0, @LD_DATA_ADDR, 1 + 000004D0 018829A0 + 0147 000004F0 0780 move a0, @LD_DATA_SIZE, 1 + 00000500 018829C0 + 0148 00000520 0780 move a0, @LD_BSS_ADDR, 1 + 00000530 018829E0 + 0149 00000550 0780 move a0, @LD_BSS_SIZE, 1 + 00000560 01882A00 + 0150 00000580 0780 move a0, @RESULT_ENTRY, 1 + 00000590 01882D20 + 0151 000005B0 0780 move a0, @RESULT_TEXT_A, 1 + 000005C0 01882D40 + 0152 000005E0 0780 move a0, @RESULT_TEXT_S, 1 + 000005F0 01882D60 + 0153 00000610 0780 move a0, @RESULT_DATA_A, 1 + 00000620 01882D80 + 0154 00000640 0780 move a0, @RESULT_DATA_S, 1 + 00000650 01882DA0 + 0155 00000670 0780 move a0, @RESULT_BSS_A, 1 + 00000680 01882DC0 + 0156 000006A0 0780 move a0, @RESULT_BSS_S, 1 + 000006B0 01882DE0 + 0157 000006D0 0780 move a0, @RESULT_RELOCS, 1 + 000006E0 01882E00 + 0158 00000700 0780 move a0, @RESULT_PCB, 1 + 00000710 01882E20 + 0159 + 0160 ; ---- file header ----------------------------------- + 0161 00000730 09E0 movi 20, a0, L + 00000740 00000014 + 0162 00000760 4808 cmp a0, a8 + 0163 00000770 C800 jrlo ld_err_short + 00000780 017E + 0164 00000790 84E0 move *a7, a0, 0 ; magic + 0165 000007A0 0B40 cmpi 0090h, a0 + 000007B0 FF6F + 0166 000007C0 CB00 jrne ld_err_magic + 000007D0 017D + 0167 000007E0 B4E9 move *a7(10h), a9, 0 ; nscns (+2 bytes) + 000007F0 0010 + 0168 00000800 0789 move a9, @LD_NSCNS, 1 + 00000810 018828C0 + 0169 00000830 B4E0 move *a7(10h), a0, 0 + 00000840 0010 + 0170 00000850 0789 move a9, @RESULT_NSCNS, 1 + 00000860 01882E40 + 0171 00000880 0788 move a8, @RESULT_LEN, 1 + 00000890 01882E60 + 0172 + 0173 ; section headers = buf + (20 + opthdr + nscns*40) byt + 0174 000008B0 B4E1 move *a7(80h), a1, 0 ; opthdr (+16 bytes) + 000008C0 0080 + 0175 000008D0 4D22 move a9, a2 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 5 + + 0176 000008E0 24A2 sll 5, a2 ; nscns*32 + 0177 000008F0 4D23 move a9, a3 + 0178 00000900 2463 sll 3, a3 ; nscns*8 + 0179 00000910 4062 add a3, a2 ; nscns*40 + 0180 00000920 4022 add a1, a2 + 0181 00000930 0B02 addi 20, a2 ; byte offset of section headers end + 00000940 0014 + 0182 00000950 4848 cmp a2, a8 ; a8 - a2 + 0183 00000960 C800 jrlo ld_err_short + 00000970 015F + 0184 + 0185 ; secbase = buf + (20 + opthdr)*8 bits + 0186 00000980 2461 sll 3, a1 ; opthdr in bits + 0187 00000990 0B01 addi 160, a1 ; 20 bytes in bits + 000009A0 00A0 + 0188 000009B0 40E1 add a7, a1 + 0189 000009C0 4C2A move a1, a10 + 0190 + 0191 ; ---- entry point ----------------------------------- + 0192 ; optional header at buf+20 bytes (0x0A0 bits): magic + 0193 ; +16 bytes (0x080 bits further); textstart at +20 byt + 0194 000009D0 B4E0 move *a7(0A0h), a0, 0 + 000009E0 00A0 + 0195 000009F0 0B40 cmpi 0108h, a0 + 00000A00 FEF7 + 0196 00000A10 CB07 jrne ld_no_opt + 0197 00000A20 B6E5 move *a7(120h), a5, 1 ; entry (bits) + 00000A30 0120 + 0198 00000A40 4CA5 move a5, a5 + 0199 00000A50 CB04 jrne ld_entry_set + 0200 00000A60 B6E5 move *a7(140h), a5, 1 ; textstart (bits) + 00000A70 0140 + 0201 00000A80 C001 jruc ld_entry_set + 0202 00000A90 ld_no_opt: + 0203 00000A90 44A5 sub a5, a5 + 0204 + 0205 00000AA0 ld_entry_set: + 0206 00000AA0 0785 move a5, @LD_ENTRY, 1 + 00000AB0 01882840 + 0207 00000AD0 0785 move a5, @RESULT_ENTRY, 1 + 00000AE0 01882D20 + 0208 + 0209 ; ---- section loop ---------------------------------- + 0210 00000B00 456B sub a11, a11 ; section index + 0211 00000B10 ld_sec_loop: + 0212 00000B10 492B cmp a9, a11 ; a11 - a9 + 0213 00000B20 C900 jrhs ld_sec_done + 00000B30 00DC + 0214 00000B40 B740 move *a10(40h), a0, 1 ; paddr (bits) +8 bytes + 00000B50 0040 + 0215 00000B60 B741 move *a10(80h), a1, 1 ; size (bits) +16 + 00000B70 0080 + 0216 00000B80 B742 move *a10(0A0h), a2, 1 ; scnptr (bytes) +20 + 00000B90 00A0 + 0217 00000BA0 B543 move *a10(100h), a3, 0 ; nreloc +32 + 00000BB0 0100 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 6 + + 0218 00000BC0 B744 move *a10(120h), a4, 1 ; flags +36 + 00000BD0 0120 + 0219 00000BE0 0780 move a0, @LD_SEC_PADDR, 1 + 00000BF0 01882900 + 0220 00000C10 0781 move a1, @LD_SEC_SIZE, 1 + 00000C20 01882920 + 0221 00000C40 0782 move a2, @LD_SEC_SCNPTR, 1 + 00000C50 01882940 + 0222 + 0223 ; record the first text/data/bss section for the resul + 0224 00000C70 4C85 move a4, a5 + 0225 00000C80 0B85 andi 20h, a5 + 00000C90 FFFFFFDF + 0226 00000CB0 4CA5 move a5, a5 + 0227 00000CC0 CA0B jreq ld_not_text + 0228 00000CD0 07A5 move @LD_TEXT_ADDR, a5, 1 + 00000CE0 01882960 + 0229 00000D00 4CA5 move a5, a5 + 0230 00000D10 CB06 jrne ld_not_text + 0231 00000D20 0780 move a0, @LD_TEXT_ADDR, 1 + 00000D30 01882960 + 0232 00000D50 0781 move a1, @LD_TEXT_SIZE, 1 + 00000D60 01882980 + 0233 00000D80 ld_not_text: + 0234 00000D80 4C85 move a4, a5 + 0235 00000D90 0B85 andi 40h, a5 + 00000DA0 FFFFFFBF + 0236 00000DC0 4CA5 move a5, a5 + 0237 00000DD0 CA0B jreq ld_not_data + 0238 00000DE0 07A5 move @LD_DATA_ADDR, a5, 1 + 00000DF0 018829A0 + 0239 00000E10 4CA5 move a5, a5 + 0240 00000E20 CB06 jrne ld_not_data + 0241 00000E30 0780 move a0, @LD_DATA_ADDR, 1 + 00000E40 018829A0 + 0242 00000E60 0781 move a1, @LD_DATA_SIZE, 1 + 00000E70 018829C0 + 0243 00000E90 ld_not_data: + 0244 00000E90 4C85 move a4, a5 + 0245 00000EA0 0B85 andi 80h, a5 + 00000EB0 FFFFFF7F + 0246 00000ED0 4CA5 move a5, a5 + 0247 00000EE0 CA0B jreq ld_not_bss + 0248 00000EF0 07A5 move @LD_BSS_ADDR, a5, 1 + 00000F00 018829E0 + 0249 00000F20 4CA5 move a5, a5 + 0250 00000F30 CB06 jrne ld_not_bss + 0251 00000F40 0780 move a0, @LD_BSS_ADDR, 1 + 00000F50 018829E0 + 0252 00000F70 0781 move a1, @LD_BSS_SIZE, 1 + 00000F80 01882A00 + 0253 00000FA0 ld_not_bss: + 0254 + 0255 ; relocation records: count, then refuse (the linked i + 0256 00000FA0 4C63 move a3, a3 + 0257 00000FB0 CA09 jreq ld_no_reloc + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 7 + + 0258 00000FC0 07A5 move @LD_RELOCS, a5, 1 + 00000FD0 018828E0 + 0259 00000FF0 4065 add a3, a5 + 0260 00001000 0785 move a5, @LD_RELOCS, 1 + 00001010 018828E0 + 0261 00001030 C000 jruc ld_err_reloc + 00001040 0106 + 0262 00001050 ld_no_reloc: + 0263 + 0264 ; size 0: nothing to place; size must be whole 16-bit + 0265 00001050 4C21 move a1, a1 + 0266 00001060 CA00 jreq ld_sec_next + 00001070 0082 + 0267 00001080 4C25 move a1, a5 + 0268 00001090 0B85 andi 0Fh, a5 + 000010A0 FFFFFFF0 + 0269 000010C0 4CA5 move a5, a5 + 0270 000010D0 CB00 jrne ld_err_size + 000010E0 00F0 + 0271 + 0272 ; source range: scnptr + size/8 <= len (when there are + 0273 000010F0 4C42 move a2, a2 + 0274 00001100 CA06 jreq ld_no_src + 0275 00001110 4C25 move a1, a5 + 0276 00001120 2FA5 srl 3, a5 ; size in bytes + 0277 00001130 40A2 add a5, a2 ; scnptr + nbytes + 0278 00001140 4902 cmp a8, a2 ; a8 - a2 + 0279 00001150 C300 jrhi ld_err_short + 00001160 00E0 + 0280 00001170 ld_no_src: + 0281 + 0282 ; target range: 0x100 <= paddr and paddr+size <= 0x200 + 0283 00001170 07A0 move @LD_SEC_PADDR, a0, 1 + 00001180 01882900 + 0284 000011A0 0B40 cmpi 100h, a0 + 000011B0 FEFF + 0285 000011C0 C800 jrlo ld_err_bounds + 000011D0 00E5 + 0286 000011E0 07A1 move @LD_SEC_SIZE, a1, 1 + 000011F0 01882920 + 0287 00001210 4C03 move a0, a3 + 0288 00001220 4023 add a1, a3 ; target end + 0289 00001230 09E5 movi 2000000h, a5, L + 00001240 02000000 + 0290 00001260 48A3 cmp a5, a3 + 0291 00001270 C300 jrhi ld_err_bounds + 00001280 00DA + 0292 + 0293 ; overlap with the loader page [01880000,01883200) + 0294 00001290 09E5 movi 01883200h, a5, L + 000012A0 01883200 + 0295 000012C0 48A0 cmp a5, a0 + 0296 000012D0 C907 jrhs ld_ov1_done + 0297 000012E0 09E5 movi 01880000h, a5, L + 000012F0 01880000 + 0298 00001310 4865 cmp a3, a5 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 8 + + 0299 00001320 C902 jrhs ld_ov1_done + 0300 00001330 C000 jruc ld_err_overlap + 00001340 00D2 + 0301 00001350 ld_ov1_done: + 0302 + 0303 ; overlap with the COFF buffer [buf, buf+len*8) + 0304 00001350 4D05 move a8, a5 + 0305 00001360 2465 sll 3, a5 + 0306 00001370 40E5 add a7, a5 ; buffer end in bits + 0307 00001380 48A0 cmp a5, a0 + 0308 00001390 C904 jrhs ld_ov2_done + 0309 000013A0 4867 cmp a3, a7 + 0310 000013B0 C902 jrhs ld_ov2_done + 0311 000013C0 C000 jruc ld_err_overlap + 000013D0 00C9 + 0312 000013E0 ld_ov2_done: + 0313 + 0314 ; ---- global free-list check ------------------------ + 0315 ; a block is {u32 size_bits; ...; u32 next@+0x20}; dat + 0316 000013E0 0780 move a0, @LD_SEC_PADDR, 1 ; (unchanged, kept for clari + 000013F0 01882900 + 0317 00001410 0783 move a3, @LD_SEC_SIZE, 1 ; temporarily hold target end + 00001420 01882920 + 0318 00001440 07A6 move @1E8AC0h, a6, 1 ; global free-list head (block a + 00001450 001E8AC0 + 0319 00001470 09E5 movi 64, a5, L + 00001480 00000040 + 0320 000014A0 ld_free_walk: + 0321 000014A0 4CC6 move a6, a6 + 0322 000014B0 CA00 jreq ld_err_notfree + 000014C0 00C2 + 0323 000014D0 86C1 move *a6, a1, 1 ; block size in bits + 0324 000014E0 B6C2 move *a6(20h), a2, 1 ; next block + 000014F0 0020 + 0325 00001500 4CC0 move a6, a0 + 0326 00001510 0B00 addi 40h, a0 ; user start + 00001520 0040 + 0327 00001530 07A3 move @LD_SEC_PADDR, a3, 1 + 00001540 01882900 + 0328 00001560 4803 cmp a0, a3 + 0329 00001570 C808 jrlo ld_free_next + 0330 00001580 4CC4 move a6, a4 + 0331 00001590 4024 add a1, a4 ; block end + 0332 000015A0 07A1 move @LD_SEC_SIZE, a1, 1 ; target end (stashed above) + 000015B0 01882920 + 0333 000015D0 4881 cmp a4, a1 + 0334 000015E0 C301 jrhi ld_free_next + 0335 000015F0 C004 jruc ld_free_ok + 0336 00001600 ld_free_next: + 0337 00001600 4C46 move a2, a6 + 0338 00001610 3F05 dsjs a5, ld_free_walk + 0339 00001620 C000 jruc ld_err_notfree + 00001630 00AB + 0340 00001640 ld_free_ok: + 0341 + 0342 ; restore size in LD_SEC_SIZE (the stash overwrote it) + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 9 + + 0343 ; target end - paddr = size + 0344 00001640 07A1 move @LD_SEC_SIZE, a1, 1 + 00001650 01882920 + 0345 00001670 07A0 move @LD_SEC_PADDR, a0, 1 + 00001680 01882900 + 0346 000016A0 4401 sub a0, a1 ; a1 = size again + 0347 000016B0 0781 move a1, @LD_SEC_SIZE, 1 + 000016C0 01882920 + 0348 + 0349 ; ---- place the section ----------------------------- + 0350 000016E0 07A0 move @LD_SEC_SCNPTR, a0, 1 + 000016F0 01882940 + 0351 00001710 4C00 move a0, a0 + 0352 00001720 CA0D jreq ld_zero_sec + 0353 00001730 2460 sll 3, a0 ; file offset -> bit offset + 0354 00001740 40E0 add a7, a0 ; source address + 0355 00001750 07A2 move @LD_SEC_PADDR, a2, 1 + 00001760 01882900 + 0356 00001780 07A1 move @LD_SEC_SIZE, a1, 1 + 00001790 01882920 + 0357 000017B0 2F81 srl 4, a1 ; number of 16-bit words + 0358 000017C0 ld_copy_words: + 0359 000017C0 9404 move *a0+, a4, 0 + 0360 000017D0 9082 move a4, *a2+, 0 + 0361 000017E0 3C61 dsjs a1, ld_copy_words + 0362 000017F0 C00A jruc ld_sec_next + 0363 + 0364 00001800 ld_zero_sec: + 0365 00001800 07A2 move @LD_SEC_PADDR, a2, 1 + 00001810 01882900 + 0366 00001830 07A1 move @LD_SEC_SIZE, a1, 1 + 00001840 01882920 + 0367 00001860 2F81 srl 4, a1 + 0368 00001870 4484 sub a4, a4 + 0369 00001880 ld_zero_words: + 0370 00001880 9082 move a4, *a2+, 0 + 0371 00001890 3C41 dsjs a1, ld_zero_words + 0372 + 0373 000018A0 ld_sec_next: + 0374 000018A0 0B0A addi 140h, a10 ; 40-byte section header = 320 bits + 000018B0 0140 + 0375 000018C0 0B0B addi 1, a11 + 000018D0 0001 + 0376 000018E0 C000 jruc ld_sec_loop + 000018F0 FF21 + 0377 + 0378 00001900 ld_sec_done: + 0379 00001900 07A0 move @LD_TEXT_ADDR, a0, 1 + 00001910 01882960 + 0380 00001930 0780 move a0, @RESULT_TEXT_A, 1 + 00001940 01882D40 + 0381 00001960 07A1 move @LD_TEXT_SIZE, a1, 1 + 00001970 01882980 + 0382 00001990 0781 move a1, @RESULT_TEXT_S, 1 + 000019A0 01882D60 + 0383 000019C0 07A0 move @LD_DATA_ADDR, a0, 1 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 10 + + 000019D0 018829A0 + 0384 000019F0 0780 move a0, @RESULT_DATA_A, 1 + 00001A00 01882D80 + 0385 00001A20 07A1 move @LD_DATA_SIZE, a1, 1 + 00001A30 018829C0 + 0386 00001A50 0781 move a1, @RESULT_DATA_S, 1 + 00001A60 01882DA0 + 0387 00001A80 07A0 move @LD_BSS_ADDR, a0, 1 + 00001A90 018829E0 + 0388 00001AB0 0780 move a0, @RESULT_BSS_A, 1 + 00001AC0 01882DC0 + 0389 00001AE0 07A1 move @LD_BSS_SIZE, a1, 1 + 00001AF0 01882A00 + 0390 00001B10 0781 move a1, @RESULT_BSS_S, 1 + 00001B20 01882DE0 + 0391 00001B40 07A0 move @LD_RELOCS, a0, 1 + 00001B50 018828E0 + 0392 00001B70 0780 move a0, @RESULT_RELOCS, 1 + 00001B80 01882E00 + 0393 + 0394 ; ---- create_process(entry, name, stack, 1, arg) ---- + 0395 00001BA0 07A0 move @LD_ENTRY, a0, 1 + 00001BB0 01882840 + 0396 00001BD0 4C00 move a0, a0 + 0397 00001BE0 CB01 jrne ld_have_entry + 0398 00001BF0 C053 jruc ld_err_noentry + 0399 00001C00 ld_have_entry: + 0400 00001C00 07AE move @LD_A14, a14, 1 + 00001C10 01882820 + 0401 00001C30 07AF move @LD_SP_AFTER, sp, 1 + 00001C40 01882800 + 0402 00001C60 A3CF move a14, -*sp, 1 ; the slot the firmware callee rest + 0403 00001C70 09EE movi ARG_AREA, a14, L + 00001C80 01882B00 + 0404 00001CA0 09E0 movi 0, a0, L + 00001CB0 00000000 + 0405 00001CD0 920E move a0, *a14+, 1 ; arg4 = 0 + 0406 00001CE0 09E0 movi 1, a0, L + 00001CF0 00000001 + 0407 00001D10 920E move a0, *a14+, 1 ; arg3 = 1 + 0408 00001D20 09E0 movi 6000h, a0, L + 00001D30 00006000 + 0409 00001D50 920E move a0, *a14+, 1 ; arg2 = stack bytes + 0410 00001D60 09E0 movi ld_appname, a0, L + 00001D70 000025E0' + 0411 00001D90 920E move a0, *a14+, 1 ; arg1 = name string + 0412 00001DA0 07A0 move @LD_ENTRY, a0, 1 + 00001DB0 01882840 + 0413 00001DD0 920E move a0, *a14+, 1 ; arg0 = entry (bit address) + 0414 00001DE0 0D5F calla 3F706A40h ; create_process + 00001DF0 3F706A40 + 0415 00001E10 07AF move @LD_SP_AFTER, sp, 1 + 00001E20 01882800 + 0416 00001E40 07AE move @LD_A14, a14, 1 + 00001E50 01882820 + 0417 00001E70 0550 setf 16, 0, 0 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 11 + + 0418 00001E80 0740 setf 32, 0, 1 + 0419 00001E90 0788 move a8, @RESULT_PCB, 1 + 00001EA0 01882E20 + 0420 00001EC0 4D08 move a8, a8 + 0421 00001ED0 CA29 jreq ld_err_create + 0422 + 0423 00001EE0 4400 sub a0, a0 + 0424 00001EF0 0780 move a0, @RESULT_STATUS, 1 + 00001F00 01882D00 + 0425 00001F20 C036 jruc ld_finish + 0426 + 0427 ; ---- errors ---------------------------------------- + 0428 00001F30 ld_bad_desc: + 0429 00001F30 09E0 movi 1, a0, L + 00001F40 00000001 + 0430 00001F60 C023 jruc ld_error + 0431 00001F70 ld_err_short: + 0432 00001F70 09E0 movi 2, a0, L + 00001F80 00000002 + 0433 00001FA0 C01F jruc ld_error + 0434 00001FB0 ld_err_magic: + 0435 00001FB0 09E0 movi 3, a0, L + 00001FC0 00000003 + 0436 00001FE0 C01B jruc ld_error + 0437 00001FF0 ld_err_size: + 0438 00001FF0 09E0 movi 4, a0, L + 00002000 00000004 + 0439 00002020 C017 jruc ld_error + 0440 00002030 ld_err_bounds: + 0441 00002030 09E0 movi 5, a0, L + 00002040 00000005 + 0442 00002060 C013 jruc ld_error + 0443 00002070 ld_err_overlap: + 0444 00002070 09E0 movi 6, a0, L + 00002080 00000006 + 0445 000020A0 C00F jruc ld_error + 0446 000020B0 ld_err_reloc: + 0447 000020B0 09E0 movi 7, a0, L + 000020C0 00000007 + 0448 000020E0 C00B jruc ld_error + 0449 000020F0 ld_err_notfree: + 0450 000020F0 09E0 movi 8, a0, L + 00002100 00000008 + 0451 00002120 C007 jruc ld_error + 0452 00002130 ld_err_noentry: + 0453 00002130 09E0 movi 9, a0, L + 00002140 00000009 + 0454 00002160 C003 jruc ld_error + 0455 00002170 ld_err_create: + 0456 00002170 09E0 movi 10, a0, L + 00002180 0000000A + 0457 000021A0 ld_error: + 0458 000021A0 0780 move a0, @RESULT_STATUS, 1 + 000021B0 01882D00 + 0459 000021D0 07A0 move @LD_NSCNS, a0, 1 + 000021E0 018828C0 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 12 + + 0460 00002200 0780 move a0, @RESULT_NSCNS, 1 + 00002210 01882E40 + 0461 00002230 07A0 move @LD_RELOCS, a0, 1 + 00002240 018828E0 + 0462 00002260 0780 move a0, @RESULT_RELOCS, 1 + 00002270 01882E00 + 0463 + 0464 00002290 ld_finish: + 0465 00002290 07A0 move @LD_ENTRY, a0, 1 + 000022A0 01882840 + 0466 000022C0 0780 move a0, @RESULT_ENTRY, 1 + 000022D0 01882D20 + 0467 000022F0 07A0 move @LD_NSCNS, a0, 1 + 00002300 018828C0 + 0468 00002320 0780 move a0, @RESULT_NSCNS, 1 + 00002330 01882E40 + 0469 00002350 07A0 move @LD_LEN, a0, 1 + 00002360 01882880 + 0470 00002380 0780 move a0, @RESULT_LEN, 1 + 00002390 01882E60 + 0471 000023B0 09E0 movi LOADER_BASE, a0, L + 000023C0 01880000 + 0472 000023E0 0780 move a0, @RESULT_VERSION, 1 + 000023F0 01882E80 + 0473 + 0474 ; ---- return or spin -------------------------------- + 0475 00002410 07A0 move @LD_MODE, a0, 1 + 00002420 018828A0 + 0476 00002440 4C00 move a0, a0 + 0477 00002450 CB0C jrne ld_return + 0478 ; mode 0 (debugger entry): restore the interrupted sta + 0479 ; the PC the harness hijacked. Returning to the OS id + 0480 ; scheduler run the created child. + 0481 00002460 07AF move @LD_SP_AFTER, sp, 1 + 00002470 01882800 + 0482 00002490 07AE move @LD_A14, a14, 1 + 000024A0 01882820 + 0483 000024C0 09AF mmfm sp, a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13 + 000024D0 3FFF + 0484 000024E0 07A0 move @LD_ORIG_PC, a0, 1 + 000024F0 01882A20 + 0485 00002510 0160 jump a0 + 0486 + 0487 00002520 ld_return: + 0488 00002520 07AF move @LD_SP_AFTER, sp, 1 + 00002530 01882800 + 0489 00002550 07AE move @LD_A14, a14, 1 + 00002560 01882820 + 0490 00002580 09AF mmfm sp, a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13 + 00002590 3FFF + 0491 000025A0 07A8 move @RESULT_STATUS, a8, 1 ; 0 = ok, else the error co + 000025B0 01882D00 + 0492 000025D0 0960 rets + 0493 + 0494 000025E0 ld_appname: + 0495 000025E0 4C .byte "LOADER", 0 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:19:03 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 13 + + 000025E8 4F + 000025F0 41 + 000025F8 44 + 00002600 45 + 00002608 52 + 00002610 00 + 0496 .end + + No Errors, No Warnings diff --git a/dwt/loader/build/loader.map b/dwt/loader/build/loader.map new file mode 100644 index 0000000..dc7659d --- /dev/null +++ b/dwt/loader/build/loader.map @@ -0,0 +1,35 @@ +***************************************************** +GSP COFF Linker, Version 2.00, 87.300 +***************************************************** +Sun Oct 18 15:19:03 2026 + +OUTPUT FILE NAME: +ENTRY POINT SYMBOL: "_start" address: 01880000 + + +SECTION ALLOCATION MAP + + output attributes/ +section page origin length input sections +-------- ---- ---------- ---------- ---------------- +.data 0 00000000 00000000 + +.bss 0 00000000 00000000 UNINITIALIZED + +.text 0 01880000 00002620 + 01880000 00002620 LOADER.obj (.text) + + +GLOBAL SYMBOLS + +address name address name +-------- ---- -------- ---- +00000000 .bss 00000000 edata +00000000 .data 00000000 .bss +01880000 .text 00000000 end +01880000 _start 00000000 .data +00000000 edata 01880000 _start +00000000 end 01880000 .text +01882620 etext 01882620 etext + +[7 symbols] diff --git a/dwt/loader/build/loader.obj b/dwt/loader/build/loader.obj new file mode 100644 index 0000000..0a33e9e Binary files /dev/null and b/dwt/loader/build/loader.obj differ diff --git a/dwt/loader/build/marker.bin b/dwt/loader/build/marker.bin new file mode 100644 index 0000000..0f33dba Binary files /dev/null and b/dwt/loader/build/marker.bin differ diff --git a/dwt/loader/build/marker.coff b/dwt/loader/build/marker.coff new file mode 100644 index 0000000..f2f5ae8 Binary files /dev/null and b/dwt/loader/build/marker.coff differ diff --git a/dwt/loader/build/marker.lst b/dwt/loader/build/marker.lst new file mode 100644 index 0000000..4c91557 --- /dev/null +++ b/dwt/loader/build/marker.lst @@ -0,0 +1,90 @@ +GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:08:14 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 1 + + 0001 ; marker.asm -- loader acceptance app. + 0002 ; + 0003 ; Built by dwt/loader/build.sh with three separate sect + 0004 ; .text 018C0000h (byte 0x318000) + 0005 ; .data 018D0000h (byte 0x31A000) + 0006 ; .bss 018D8000h (byte 0x31B000) + 0007 ; + 0008 ; It is the *COFF image* that the loader must place; it + 0009 ; raw blob. The loader parses the COFF, copies .text a + 0010 ; and calls create_process() with the image's entry poi + 0011 ; + 0012 ; Evidence cells (32-bit, bit addresses; outside every + 0013 ; 018E0000 _start address at run time (must be 018C0 + 0014 ; 018E0020 d_magic read from .data (must be DEADBEE + 0015 ; 018E0040 d_two read from .data (must be 1234567 + 0016 ; 018E0060 b_zero read from .bss (must be 0; the + 0017 ; 018E0080 b_counter, incremented forever (proves th + 0018 ; + 0019 ; The harness pre-fills the whole .bss range with A5A5A + 0020 ; a zero at 018E0060 is the loader's zero-fill, not a f + 0021 + 0022 .def _start + 0023 00000000 .text + 0024 00000000 _start: + 0025 00000000 0550 setf 16, 0, 0 + 0026 00000010 0740 setf 32, 0, 1 + 0027 + 0028 ; Run-time address of this code: position dependence, + 0029 00000020 09E2 movi _start, a2, L + 00000030 00000000' + 0030 00000050 0782 move a2, @018E0000h, 1 + 00000060 018E0000 + 0031 + 0032 ; Initialised .data must have been copied by the loade + 0033 00000080 07A0 move @d_magic, a0, 1 + 00000090 00000000" + 0034 000000B0 0780 move a0, @018E0020h, 1 + 000000C0 018E0020 + 0035 + 0036 000000E0 07A1 move @d_two, a1, 1 + 000000F0 00000020" + 0037 00000110 0781 move a1, @018E0040h, 1 + 00000120 018E0040 + 0038 + 0039 ; .bss must have been zeroed by the loader (harness pr + 0040 00000140 07A3 move @b_zero, a3, 1 + 00000150 00000000+ + 0041 00000170 0783 move a3, @018E0060h, 1 + 00000180 018E0060 + 0042 + 0043 ; A live counter: a created process, not a one-shot ju + 0044 000001A0 4484 sub a4, a4 + 0045 000001B0 loop: + 0046 000001B0 0B04 addi 1, a4 + 000001C0 0001 + 0047 000001D0 0784 move a4, @b_counter, 1 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:08:14 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 2 + + 000001E0 00000020+ + 0048 00000200 0784 move a4, @018E0080h, 1 + 00000210 018E0080 + 0049 00000230 09E5 movi 2000h, a5, L + 00000240 00002000 + 0050 00000260 delay: + 0051 00000260 3C25 dsjs a5, delay + 0052 00000270 C0F3 jruc loop + 0053 + 0054 ; Kept to leave a call in the image (and a relocation + 0055 .global sub + 0056 00000280 sub: + 0057 00000280 0960 rets + 0058 + 0059 00000000 .data + 0060 00000000 DEADBEEF d_magic: .long 0DEADBEEFh + 0061 00000020 12345678 d_two: .long 012345678h + 0062 + 0063 00000000 .bss b_zero, 32, 1 + 0064 00000020 .bss b_counter, 32, 1 + 0065 + 0066 .end + + No Errors, No Warnings diff --git a/dwt/loader/build/marker.map b/dwt/loader/build/marker.map new file mode 100644 index 0000000..8ce44b2 --- /dev/null +++ b/dwt/loader/build/marker.map @@ -0,0 +1,38 @@ +***************************************************** +GSP COFF Linker, Version 2.00, 87.300 +***************************************************** +Sun Oct 18 15:08:14 2026 + +OUTPUT FILE NAME: +ENTRY POINT SYMBOL: "_start" address: 018C0000 + + +SECTION ALLOCATION MAP + + output attributes/ +section page origin length input sections +-------- ---- ---------- ---------- ---------------- +.text 0 018C0000 00000290 + 018C0000 00000290 MARKER.obj (.text) + +.data 0 018D0000 00000040 + 018D0000 00000040 MARKER.obj (.data) + +.bss 0 018D8000 00000040 UNINITIALIZED + 018D8000 00000040 MARKER.obj (.bss) + + +GLOBAL SYMBOLS + +address name address name +-------- ---- -------- ---- +018D8000 .bss 018C0000 .text +018D0000 .data 018C0000 _start +018C0000 .text 018C0280 sub +018C0000 _start 018C0290 etext +018D0040 edata 018D0000 .data +018D8040 end 018D0040 edata +018C0290 etext 018D8000 .bss +018C0280 sub 018D8040 end + +[8 symbols] diff --git a/dwt/loader/build/marker.obj b/dwt/loader/build/marker.obj new file mode 100644 index 0000000..a57a839 Binary files /dev/null and b/dwt/loader/build/marker.obj differ diff --git a/dwt/loader/build/rela.obj b/dwt/loader/build/rela.obj new file mode 100644 index 0000000..f4c1308 Binary files /dev/null and b/dwt/loader/build/rela.obj differ diff --git a/dwt/loader/build/relabs.out b/dwt/loader/build/relabs.out new file mode 100644 index 0000000..b751ce6 Binary files /dev/null and b/dwt/loader/build/relabs.out differ diff --git a/dwt/loader/build/relabs2.out b/dwt/loader/build/relabs2.out new file mode 100644 index 0000000..e30cb5e Binary files /dev/null and b/dwt/loader/build/relabs2.out differ diff --git a/dwt/loader/build/relb.obj b/dwt/loader/build/relb.obj new file mode 100644 index 0000000..9febb44 Binary files /dev/null and b/dwt/loader/build/relb.obj differ diff --git a/dwt/loader/build/reloc.out b/dwt/loader/build/reloc.out new file mode 100644 index 0000000..cad44e4 Binary files /dev/null and b/dwt/loader/build/reloc.out differ diff --git a/dwt/loader/build/xhello.bin b/dwt/loader/build/xhello.bin new file mode 100644 index 0000000..acd8474 Binary files /dev/null and b/dwt/loader/build/xhello.bin differ diff --git a/dwt/loader/build/xhello.coff b/dwt/loader/build/xhello.coff new file mode 100644 index 0000000..aedb74f Binary files /dev/null and b/dwt/loader/build/xhello.coff differ diff --git a/dwt/loader/build/xhello.lst b/dwt/loader/build/xhello.lst new file mode 100644 index 0000000..5a98a1b --- /dev/null +++ b/dwt/loader/build/xhello.lst @@ -0,0 +1,229 @@ +GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:04:44 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 1 + + 0001 ; xhello.asm -- open a *new* X connection and create/ma + 0002 ; DWT X server. + 0003 ; + 0004 ; Run as a first-class process created by xlaunch.asm ( + 0005 ; the rdebug dispatcher's context: a connect made from + 0006 ; accepted at the service layer but the server never re + 0007 ; XOpenDisplay returns an unusable Display. A fresh pr + 0008 ; + 0009 ; Calling convention (the one the firmware itself uses; + 0010 ; 07F57044 and the TM at 07E5D2B4): + 0011 ; + 0012 ; MOVE A14,-*SP,1 ; reserve the slot the ca + 0013 ; MOVI argN,A0 / MOVE A0,*A14+,1 ; last argument + 0014 ; ... + 0015 ; MOVI arg0,A0 / MOVE A0,*A14+,1 ; first argumen + 0016 ; CALLA target + 0017 ; + 0018 ; Xlib clobbers the CPU's SETF field state, so the ABI' + 0019 ; re-established after every call. The Display pointer + 0020 ; kept in a11/a10 (both callee-saved by every Xlib entr + 0021 ; be overwritten by the new process's stack, a register + 0022 ; + 0023 ; Xlib entry points (Ghidra address -> bit address): + 0024 ; 7F384AE XOpenDisplay(name) -> 3F9C2570 + 0025 ; 7FA8372 XCreateSimpleWindow(...) -> 3FD41B90 + 0026 ; 7FAC8B6 XMapWindow(display,window) -> 3FD645B0 + 0027 ; 7FB3ED6 XSync(display,discard) -> 3FD9F6B0 + 0028 ; 7F68B1A strlen(s) -> 3FB458D0 + 0029 ; + 0030 ; Markers (32-bit cells, bit addresses; diagnostic only + 0031 ; 01890100 display pointer + 0032 ; 01890110 root window + 0033 ; 01890120 window id + 0034 ; 01890130 XSync return + 0035 ; 01890140 stage counter + 0036 ; 01890160 strlen("0::0") -- must be 4; if not, we s + 0037 + 0038 00000000 .text + 0039 00000000 _start: + 0040 00000000 0550 setf 16, 0, 0 + 0041 00000010 0740 setf 32, 0, 1 + 0042 00000020 098F mmtm sp, a0,a1,a2,a3,a4,a5,a6,a7 + 00000030 FF00 + 0043 + 0044 00000040 078F move sp, @01890420h, 1 ; entry SP + 00000050 01890420 + 0045 00000070 078E move a14, @01890400h, 1 ; entry A14 + 00000080 01890400 + 0046 + 0047 000000A0 09E0 movi 1, a0, L + 000000B0 00000001 + 0048 000000D0 0780 move a0, @01890140h, 1 ; stage = 1 + 000000E0 01890140 + 0049 + 0050 ; ---- convention check: strlen("0::0") == 4 --------- + 0051 00000100 A3CF move a14, -*sp, 1 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:04:44 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 2 + + 0052 00000110 09EE movi 01892000h, a14, L + 00000120 01892000 + 0053 00000140 09E0 movi 3FACE948h, a0, L ; ROM "0::0" + 00000150 3FACE948 + 0054 00000170 920E move a0, *a14+, 1 ; arg0 + 0055 00000180 0D5F calla 3FB458D0h ; strlen + 00000190 3FB458D0 + 0056 000001B0 07AF move @01890420h, sp, 1 + 000001C0 01890420 + 0057 000001E0 07AE move @01890400h, a14, 1 + 000001F0 01890400 + 0058 00000210 0550 setf 16, 0, 0 ; Xlib leaves the field sizes changed; + 0059 00000220 0740 setf 32, 0, 1 ; re-establish the ABI's 32-bit field 1 + 0060 00000230 0788 move a8, @01890160h, 1 ; MARK strlen + 00000240 01890160 + 0061 00000260 09E0 movi 4, a0, L + 00000270 00000004 + 0062 00000290 4808 cmp a0, a8 + 0063 000002A0 CB00 jrne done + 000002B0 0091 + 0064 + 0065 000002C0 09E0 movi 2, a0, L + 000002D0 00000002 + 0066 000002F0 0780 move a0, @01890140h, 1 + 00000300 01890140 + 0067 + 0068 ; ---- display = XOpenDisplay("0::0") ---------------- + 0069 00000320 A3CF move a14, -*sp, 1 + 0070 00000330 09EE movi 01892000h, a14, L + 00000340 01892000 + 0071 00000360 09E0 movi 3FACE948h, a0, L + 00000370 3FACE948 + 0072 00000390 920E move a0, *a14+, 1 ; arg0 = name + 0073 000003A0 0D5F calla 3F9C2570h ; XOpenDisplay + 000003B0 3F9C2570 + 0074 000003D0 07AF move @01890420h, sp, 1 + 000003E0 01890420 + 0075 00000400 07AE move @01890400h, a14, 1 + 00000410 01890400 + 0076 00000430 0550 setf 16, 0, 0 + 0077 00000440 0740 setf 32, 0, 1 + 0078 00000450 4D0B move a8, a11 ; keep the Display in a callee-saved re + 0079 00000460 078B move a11, @01890100h, 1 ; MARK display + 00000470 01890100 + 0080 00000490 4D6B move a11, a11 + 0081 000004A0 CA72 jreq done + 0082 + 0083 000004B0 09E0 movi 3, a0, L + 000004C0 00000003 + 0084 000004E0 0780 move a0, @01890140h, 1 + 000004F0 01890140 + 0085 + 0086 ; ---- root = *(*(display+0x480)+0x40) --------------- + 0087 00000510 B76A move *a11(0480h), a10, 1 + 00000520 0480 + 0088 00000530 B74A move *a10(040h), a10, 1 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:04:44 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 3 + + 00000540 0040 + 0089 00000550 078A move a10, @01890110h, 1 ; MARK root + 00000560 01890110 + 0090 + 0091 ; ---- XCreateSimpleWindow(display,root,100,100,400,20 + 0092 00000580 A3CF move a14, -*sp, 1 + 0093 00000590 09EE movi 01892000h, a14, L + 000005A0 01892000 + 0094 000005C0 09E0 movi 0, a0, L + 000005D0 00000000 + 0095 000005F0 920E move a0, *a14+, 1 ; background = 0 (black) + 0096 00000600 920E move a0, *a14+, 1 ; border = 0 + 0097 00000610 09E0 movi 2, a0, L + 00000620 00000002 + 0098 00000640 920E move a0, *a14+, 1 ; border width + 0099 00000650 09E0 movi 200, a0, L + 00000660 000000C8 + 0100 00000680 920E move a0, *a14+, 1 ; height + 0101 00000690 09E0 movi 400, a0, L + 000006A0 00000190 + 0102 000006C0 920E move a0, *a14+, 1 ; width + 0103 000006D0 09E0 movi 100, a0, L + 000006E0 00000064 + 0104 00000700 920E move a0, *a14+, 1 ; y + 0105 00000710 920E move a0, *a14+, 1 ; x + 0106 00000720 934E move a10, *a14+, 1 ; parent = root + 0107 00000730 936E move a11, *a14+, 1 ; display + 0108 00000740 0D5F calla 3FD41B90h ; XCreateSimpleWindow + 00000750 3FD41B90 + 0109 00000770 07AF move @01890420h, sp, 1 + 00000780 01890420 + 0110 000007A0 07AE move @01890400h, a14, 1 + 000007B0 01890400 + 0111 000007D0 0550 setf 16, 0, 0 + 0112 000007E0 0740 setf 32, 0, 1 + 0113 000007F0 4D0A move a8, a10 ; window id (root no longer needed) + 0114 00000800 078A move a10, @01890120h, 1 ; MARK window + 00000810 01890120 + 0115 + 0116 00000830 09E0 movi 4, a0, L + 00000840 00000004 + 0117 00000860 0780 move a0, @01890140h, 1 + 00000870 01890140 + 0118 + 0119 ; ---- XMapWindow(display, window) ------------------- + 0120 00000890 A3CF move a14, -*sp, 1 + 0121 000008A0 09EE movi 01892000h, a14, L + 000008B0 01892000 + 0122 000008D0 934E move a10, *a14+, 1 ; window + 0123 000008E0 936E move a11, *a14+, 1 ; display + 0124 000008F0 0D5F calla 3FD645B0h ; XMapWindow + 00000900 3FD645B0 + 0125 00000920 07AF move @01890420h, sp, 1 + 00000930 01890420 + 0126 00000950 07AE move @01890400h, a14, 1 + 00000960 01890400 + GSP COFF Assembler, Version 2.00, 87.300 Sun Oct 18 15:04:44 2026 + (c) Copyright 1985, 1987, Texas Instruments Inc. + + PAGE 4 + + 0127 00000980 0550 setf 16, 0, 0 + 0128 00000990 0740 setf 32, 0, 1 + 0129 + 0130 000009A0 09E0 movi 5, a0, L + 000009B0 00000005 + 0131 000009D0 0780 move a0, @01890140h, 1 + 000009E0 01890140 + 0132 + 0133 ; ---- XSync(display, 0) ----------------------------- + 0134 00000A00 A3CF move a14, -*sp, 1 + 0135 00000A10 09EE movi 01892000h, a14, L + 00000A20 01892000 + 0136 00000A40 09E0 movi 0, a0, L + 00000A50 00000000 + 0137 00000A70 920E move a0, *a14+, 1 ; discard = 0 + 0138 00000A80 936E move a11, *a14+, 1 ; display + 0139 00000A90 0D5F calla 3FD9F6B0h ; XSync + 00000AA0 3FD9F6B0 + 0140 00000AC0 07AF move @01890420h, sp, 1 + 00000AD0 01890420 + 0141 00000AF0 07AE move @01890400h, a14, 1 + 00000B00 01890400 + 0142 00000B20 0550 setf 16, 0, 0 + 0143 00000B30 0740 setf 32, 0, 1 + 0144 00000B40 0788 move a8, @01890130h, 1 ; MARK sync + 00000B50 01890130 + 0145 + 0146 00000B70 09E0 movi 6, a0, L + 00000B80 00000006 + 0147 00000BA0 0780 move a0, @01890140h, 1 + 00000BB0 01890140 + 0148 + 0149 00000BD0 done: + 0150 00000BD0 09AF mmfm sp, a0,a1,a2,a3,a4,a5,a6,a7 + 00000BE0 00FF + 0151 00000BF0 09E8 movi 0, a8, L + 00000C00 00000000 + 0152 00000C20 0960 rets + 0153 .end + + No Errors, No Warnings diff --git a/dwt/loader/build/xhello.map b/dwt/loader/build/xhello.map new file mode 100644 index 0000000..2cd1ecd --- /dev/null +++ b/dwt/loader/build/xhello.map @@ -0,0 +1,34 @@ +***************************************************** +GSP COFF Linker, Version 2.00, 87.300 +***************************************************** +Sun Oct 18 15:04:44 2026 + +OUTPUT FILE NAME: +ENTRY POINT SYMBOL: 0 + + +SECTION ALLOCATION MAP + + output attributes/ +section page origin length input sections +-------- ---- ---------- ---------- ---------------- +.data 0 00000000 00000000 + +.bss 0 00000000 00000000 UNINITIALIZED + +.text 0 01840000 00000C30 + 01840000 00000C30 XHELLO.obj (.text) + + +GLOBAL SYMBOLS + +address name address name +-------- ---- -------- ---- +00000000 .bss 00000000 edata +00000000 .data 00000000 .bss +01840000 .text 00000000 end +00000000 edata 00000000 .data +00000000 end 01840000 .text +01840C30 etext 01840C30 etext + +[6 symbols] diff --git a/dwt/loader/build/xhello.obj b/dwt/loader/build/xhello.obj new file mode 100644 index 0000000..0b2f24e Binary files /dev/null and b/dwt/loader/build/xhello.obj differ diff --git a/dwt/loader/coff.py b/dwt/loader/coff.py new file mode 100755 index 0000000..4fdc5b6 --- /dev/null +++ b/dwt/loader/coff.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +"""coff.py -- TI TMS34010 GSP COFF reader for the VT1200 loader work. + +GSPLNK 2.00 emits the "TI COFF" described in the 1987 Assembler Tools guide, +Appendix A. All section addresses and sizes in the section headers are in +BITS (the TMS34010 is bit-addressed); file offsets (scnptr/relptr/symptr) are +in BYTES. + +Commands + coff.py summary FILE... dump header, optional header, sections, + symbols (with aux entries) and relocations + coff.py raw ABS1 ABS2 extract each section from two absolute + outputs and report byte differences + coff.py manifest FILE print shell-ish KEY=VALUE lines for one file + +The loader itself parses the same structures in assembly; this script is the +reference and the evidence generator. +""" +import struct +import sys + +MAGIC = 0x0090 +OPTHDR_MAGIC = 0x0108 + +RELOC_NAMES = { + 0x00: "R-ABS", + 0x10: "R-RELWORD", + 0x11: "R-RELLONG", + 0x18: "R-OCRLONG", + 0x19: "R-GSPPCR16", +} + +SECTION_FLAGS = { + 0x20: "text", + 0x40: "data", + 0x80: "bss", +} + + +class CoffError(Exception): + pass + + +def parse(path): + d = open(path, "rb").read() + if len(d) < 20: + raise CoffError("%s: shorter than a COFF file header" % path) + magic, nscns, timdat, symptr, nsyms, opthdr, flags = struct.unpack_from( + " len(d): + raise CoffError("%s: optional header past EOF" % path) + # TI COFF optional header: magic, vstamp, textsize, datasize, bsssize, + # entry, textstart, datastart (all sizes/addresses in bits). + opt = struct.unpack_from(" len(d): + raise CoffError("%s: section header %d past EOF" % (path, i)) + raw = d[off:off + 40] + name = raw[:8].rstrip(b"\0").decode("latin1") + (paddr, vaddr, size, scnptr, relptr, lnnoptr, nreloc, nlnno, + sflags) = struct.unpack_from(" len(d): + raise CoffError("%s: relocation %d past EOF" % (path, r)) + raddr, symidx, rtype = struct.unpack_from(" len(d): + raise CoffError("%s: symbol %d past EOF" % (path, i)) + e = d[off:off + 18] + name = e[:8].rstrip(b"\0").decode("latin1", "replace") + val, secno, stype, sclass, naux = struct.unpack_from(" %02x" % (i, ba[i], bb[i])) + print() + + +def manifest(path): + c = parse(path) + print("COFF=%s" % path) + print("SIZE=%d" % c["size"]) + print("NSECTIONS=%d" % c["nscns"]) + if c["opt"]: + _, _, tsize, dsize, bsize, entry, tstart, dstart = c["opt"] + print("ENTRY=%#010x" % entry) + print("TEXTSTART=%#010x" % tstart) + print("DATASTART=%#010x" % dstart) + print("TEXTSIZE_BITS=%d" % tsize) + print("DATASIZE_BITS=%d" % dsize) + print("BSSSIZE_BITS=%d" % bsize) + for s in c["sections"]: + key = s["name"].lstrip(".").upper() or ("SEC%d" % s["index"]) + print("%s_PADDR=%#010x" % (key, s["paddr"])) + print("%s_SIZE_BITS=%d" % (key, s["size"])) + print("%s_NRELOC=%d" % (key, s["nreloc"])) + + +def main(argv): + if len(argv) < 3: + raise SystemExit(__doc__) + cmd = argv[1] + if cmd == "summary": + for p in argv[2:]: + summary(p) + elif cmd == "raw": + raw(argv[2:]) + elif cmd == "manifest": + manifest(argv[2]) + else: + raise SystemExit(__doc__) + + +if __name__ == "__main__": + main(sys.argv) diff --git a/dwt/loader/evidence/coff_layout.txt b/dwt/loader/evidence/coff_layout.txt new file mode 100644 index 0000000..b49d71a --- /dev/null +++ b/dwt/loader/evidence/coff_layout.txt @@ -0,0 +1,68 @@ +=== marker.coff hexdump (first 176 bytes) === +00000000: 90 00 03 00 de e0 d4 6a 02 01 00 00 0e 00 00 00 .......j........ +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 .text........... +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 .... ....data... +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 .bss............ +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 ........P.@..... + +=== marker.coff parsed === +== dwt/loader/build/marker.coff (510 bytes) + file hdr: magic=0x0090 nscns=3 symptr=0x102 nsyms=14 opthdr=28 flags=0x0103 + optional: magic=0x0108 vstamp=0x00c7 text=0x290 bits data=0x40 bits bss=0x40 bits entry=0x018c0000 textstart=0x018c0000 datastart=0x018d0000 + optional: sizes in bytes text=82 data=8 bss=8; entry in bits=0x18c0000 + sec 1 .text paddr=00018c0000 vaddr=00018c0000 size=0x290 bits (82 B) scnptr=0xa8 nreloc=0 flags=0x20 [text] + sec 2 .data paddr=00018d0000 vaddr=00018d0000 size=0x40 bits (8 B) scnptr=0xfa nreloc=0 flags=0x40 [data] + sec 3 .bss paddr=00018d8000 vaddr=00018d8000 size=0x40 bits (8 B) scnptr=0x0 nreloc=0 flags=0x80 [bss] + sym[0] .text value=00018c0000 sec=1 class=3 naux=1 + sym[2] .data value=00018d0000 sec=2 class=3 naux=1 + sym[4] .bss value=00018d8000 sec=3 class=3 naux=1 + sym[6] sub value=00018c0280 sec=1 class=2 + sym[7] _start value=00018c0000 sec=1 class=2 + sym[8] .text value=00018c0000 sec=1 class=2 + sym[9] etext value=00018c0290 sec=1 class=2 + sym[10] .data value=00018d0000 sec=2 class=2 + sym[11] edata value=00018d0040 sec=2 class=2 + sym[12] .bss value=00018d8000 sec=3 class=2 + sym[13] end value=00018d8040 sec=3 class=2 + +=== xhello.coff parsed === +== dwt/loader/build/xhello.coff (774 bytes) + file hdr: magic=0x0090 nscns=3 symptr=0x22e nsyms=12 opthdr=28 flags=0x0103 + optional: magic=0x0108 vstamp=0x00c7 text=0xc30 bits data=0x0 bits bss=0x0 bits entry=0x00000000 textstart=0x01840000 datastart=0x00000000 + optional: sizes in bytes text=390 data=0 bss=0; entry in bits=0 (no -e global symbol) + sec 1 .data paddr=0000000000 vaddr=0000000000 size=0x0 bits (0 B) scnptr=0xa8 nreloc=0 flags=0x40 [data] + sec 2 .bss paddr=0000000000 vaddr=0000000000 size=0x0 bits (0 B) scnptr=0x0 nreloc=0 flags=0x80 [bss] + sec 3 .text paddr=0001840000 vaddr=0001840000 size=0xc30 bits (390 B) scnptr=0xa8 nreloc=0 flags=0x20 [text] + sym[0] .text value=0001840000 sec=3 class=3 naux=1 + sym[2] .data value=0000000000 sec=1 class=3 naux=1 + sym[4] .bss value=0000000000 sec=2 class=3 naux=1 + sym[6] .text value=0001840000 sec=3 class=2 + sym[7] etext value=0001840c30 sec=3 class=2 + sym[8] .data value=0000000000 sec=1 class=2 + sym[9] edata value=0000000000 sec=1 class=2 + sym[10] .bss value=0000000000 sec=2 class=2 + sym[11] end value=0000000000 sec=2 class=2 + +=== marker.obj (assembler output: relocations present) === +== dwt/loader/build/marker.obj (424 bytes) + file hdr: magic=0x0090 nscns=3 symptr=0x118 nsyms=8 opthdr=0 flags=0x0100 + sec 1 .text paddr=0000000000 vaddr=0000000000 size=0x290 bits (82 B) scnptr=0x8c nreloc=5 flags=0x20 [text] + reloc at 0x30 sym=65535 type=0x11 R-RELLONG + reloc at 0x90 sym=2 type=0x11 R-RELLONG + reloc at 0xf0 sym=2 type=0x11 R-RELLONG + reloc at 0x150 sym=4 type=0x11 R-RELLONG + reloc at 0x1e0 sym=4 type=0x11 R-RELLONG + sec 2 .data paddr=0000000000 vaddr=0000000000 size=0x40 bits (8 B) scnptr=0xde nreloc=0 flags=0x40 [data] + sec 3 .bss paddr=0000000000 vaddr=0000000000 size=0x40 bits (8 B) scnptr=0x0 nreloc=0 flags=0x80 [bss] + sym[0] .text value=0000000000 sec=1 class=3 naux=1 + sym[2] .data value=0000000000 sec=2 class=3 naux=1 + sym[4] .bss value=0000000000 sec=3 class=3 naux=1 + sym[6] sub value=0000000280 sec=1 class=2 + sym[7] _start value=0000000000 sec=1 class=2 + diff --git a/dwt/loader/evidence/elf_absent.txt b/dwt/loader/evidence/elf_absent.txt new file mode 100644 index 0000000..74db905 --- /dev/null +++ b/dwt/loader/evidence/elf_absent.txt @@ -0,0 +1,17 @@ +=== ELF in the 1987 SDK documentation === +grep -ni elf harness/sdk-scratch/asmguide.txt harness/sdk-scratch/cguide.txt +harness/sdk-scratch/cguide.txt:903: • The preprocessor produces self-explanatory error messages. The line +harness/sdk-scratch/cguide.txt:1830: ever, the structure or array itself begins at a 16-bit boundary. In the case +harness/sdk-scratch/asmguide.txt:2047: A label on a line by itself is a valid statement. It assigns the current value of +harness/sdk-scratch/asmguide.txt:2058: When a label appears on a line by itself, it usually points to the instruction on +harness/sdk-scratch/asmguide.txt:4828: listing page. The .title directive itself is not printed, but the line counter is +harness/sdk-scratch/asmguide.txt:9927: TMS34010 simulator; the messages are self explanatory. +harness/sdk-scratch/asmguide.txt:10244: and then executes the contents of buffer 1. A buffer can even reference itself +harness/sdk-scratch/asmguide.txt:11230:Syntax elF +harness/sdk-scratch/asmguide.txt:14123: example, if bytes 18 and 19 are set to 0003h, then F-RELFLG and F-EXEC +harness/sdk-scratch/asmguide.txt:14129: F-RELFLG 0001h Relocation information was stripped from + +The SDK's documented object formats (asmguide §1.2 / §10): +569: The object format converter converts a COFF object file into Intel + +No ELF writer or ELF reader exists in the toolchain: COFF is the only linker output. diff --git a/dwt/loader/evidence/loader_frame2650.png b/dwt/loader/evidence/loader_frame2650.png new file mode 100644 index 0000000..b11e9d8 Binary files /dev/null and b/dwt/loader/evidence/loader_frame2650.png differ diff --git a/dwt/loader/evidence/marker_a8desc_run.log b/dwt/loader/evidence/marker_a8desc_run.log new file mode 100644 index 0000000..c2f9ccc --- /dev/null +++ b/dwt/loader/evidence/marker_a8desc_run.log @@ -0,0 +1,88 @@ +Warning: -video none doesn't make much sense without -seconds_to_run +vt1200: screen_update#1 frame=0 clip_y=0..0 screen=1280x901 +LOADER: payload dwt/loader/build/loader.bin (1220 B) -> bit 01880000 +LOADER: COFF dwt/loader/build/marker.coff (510 B) -> bit 01900000 +LOADER: file magic=0090 nscns=3 opthdr=28 optmagic=0108 entry=018C0000 textstart=018C0000 +LOADER: sec .text paddr=018C0000 size=656 bits (82 B) scnptr=0xa8 flags=0x20 +LOADER: sec .data paddr=018D0000 size=64 bits (8 B) scnptr=0xfa flags=0x40 +LOADER: sec .bss paddr=018D8000 size=64 bits (8 B) scnptr=0 flags=0x80 +vt1200: screen_update#2 frame=0 clip_y=1..900 screen=1280x901 +vt1200: scanline#1 line=1 enabled=1 +vt1200: guard#1 frame=0 dump_frame=-1 dump_ram=-1 +vt1200: screen_update#3 frame=1 clip_y=0..0 screen=1280x901 +vt1200: scanline#2 line=0 enabled=1 +vt1200: screen_update#4 frame=1 clip_y=1..1 screen=1280x901 +vt1200: scanline#3 line=1 enabled=1 +vt1200: scanline#4 line=2 enabled=1 +LOADER: loader poked 610 words, readback 0 bad at 01880000 +LOADER: COFF poked 255 words, readback 0 bad at 01900000 +LOADER: pre-filled .bss 018D8000..018D8040 with A5A5 +LOADER: descriptor @01882C00 magic=4C44/5231 buf=01900000 len=510 mode=1 +LOADER: will resume at 3F579970 after create_process +LOADER: frame 2500: entered loader at 01880000 (caller SP=007F5290) +LOADER: marker counter 1431655765 -> 5 (child running) +LOADER: marker counter 5 -> 11 (child running) +LOADER: marker counter 11 -> 16 (child running) +LOADER: marker counter 16 -> 21 (child running) +LOADER: marker counter 21 -> 26 (child running) +LOADER: marker counter 26 -> 31 (child running) +LOADER: marker counter 31 -> 36 (child running) +LOADER: marker counter 36 -> 41 (child running) +LOADER: marker counter 41 -> 47 (child running) +LOADER: marker counter 47 -> 52 (child running) +LOADER: marker counter 52 -> 57 (child running) +LOADER: marker counter 57 -> 62 (child running) +LOADER: marker counter 62 -> 67 (child running) +LOADER: marker counter 67 -> 72 (child running) +LOADER: marker counter 72 -> 77 (child running) +LOADER: marker counter 77 -> 83 (child running) +LOADER: marker counter 83 -> 88 (child running) +LOADER: marker counter 88 -> 93 (child running) +LOADER: marker counter 93 -> 98 (child running) +LOADER: marker counter 98 -> 103 (child running) +LOADER: marker counter 103 -> 108 (child running) +LOADER: marker counter 108 -> 114 (child running) +LOADER: marker counter 114 -> 119 (child running) +LOADER: marker counter 119 -> 124 (child running) +LOADER: marker counter 124 -> 129 (child running) +LOADER: marker counter 129 -> 134 (child running) +LOADER: marker counter 134 -> 139 (child running) +LOADER: marker counter 139 -> 145 (child running) +LOADER: marker counter 145 -> 150 (child running) +LOADER: marker counter 150 -> 155 (child running) +LOADER: marker counter 155 -> 160 (child running) +LOADER: marker counter 160 -> 165 (child running) +LOADER: marker counter 165 -> 170 (child running) +LOADER: marker counter 170 -> 176 (child running) +LOADER: marker counter 176 -> 181 (child running) +LOADER: marker counter 181 -> 186 (child running) +LOADER: marker counter 186 -> 191 (child running) +LOADER: marker counter 191 -> 196 (child running) +LOADER: marker counter 196 -> 201 (child running) +LOADER: marker counter 201 -> 207 (child running) +LOADER: marker counter 207 -> 212 (child running) +LOADER: marker counter 212 -> 217 (child running) +LOADER: marker counter 217 -> 222 (child running) +LOADER: marker counter 222 -> 227 (child running) +LOADER: marker counter 227 -> 232 (child running) +LOADER: marker counter 232 -> 238 (child running) +LOADER: marker counter 238 -> 243 (child running) +LOADER: marker counter 243 -> 248 (child running) +LOADER: marker counter 248 -> 253 (child running) +LOADER: marker counter 253 -> 258 (child running) +LOADER: marker counter 258 -> 263 (child running) +LOADER: marker counter 263 -> 269 (child running) +LOADER: marker counter 269 -> 274 (child running) +LOADER: marker counter 274 -> 279 (child running) +LOADER: marker counter 279 -> 284 (child running) +LOADER: marker counter 284 -> 289 (child running) +LOADER: marker counter 289 -> 294 (child running) +LOADER: marker counter 294 -> 299 (child running) +LOADER: marker counter 299 -> 305 (child running) +LOADER: marker counter 305 -> 310 (child running) +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=310 +LOADER: PC at inspection = 018C0260 +Average speed: 332.37% (35 seconds) diff --git a/dwt/loader/evidence/marker_mode1_run.log b/dwt/loader/evidence/marker_mode1_run.log new file mode 100644 index 0000000..9920d10 --- /dev/null +++ b/dwt/loader/evidence/marker_mode1_run.log @@ -0,0 +1,128 @@ +Warning: -video none doesn't make much sense without -seconds_to_run +vt1200: screen_update#1 frame=0 clip_y=0..0 screen=1280x901 +LOADER: payload dwt/loader/build/loader.bin (1220 B) -> bit 01880000 +LOADER: COFF dwt/loader/build/marker.coff (510 B) -> bit 01900000 +LOADER: file magic=0090 nscns=3 opthdr=28 optmagic=0108 entry=018C0000 textstart=018C0000 +LOADER: sec .text paddr=018C0000 size=656 bits (82 B) scnptr=0xa8 flags=0x20 +LOADER: sec .data paddr=018D0000 size=64 bits (8 B) scnptr=0xfa flags=0x40 +LOADER: sec .bss paddr=018D8000 size=64 bits (8 B) scnptr=0 flags=0x80 +vt1200: screen_update#2 frame=0 clip_y=1..900 screen=1280x901 +vt1200: scanline#1 line=1 enabled=1 +vt1200: guard#1 frame=0 dump_frame=-1 dump_ram=-1 +vt1200: screen_update#3 frame=1 clip_y=0..0 screen=1280x901 +vt1200: scanline#2 line=0 enabled=1 +vt1200: screen_update#4 frame=1 clip_y=1..1 screen=1280x901 +vt1200: scanline#3 line=1 enabled=1 +vt1200: scanline#4 line=2 enabled=1 +LOADER: loader poked 610 words, readback 0 bad at 01880000 +LOADER: COFF poked 255 words, readback 0 bad at 01900000 +LOADER: pre-filled .bss 018D8000..018D8040 with A5A5 +LOADER: descriptor @01882C00 magic=4C44/5231 buf=01900000 len=510 mode=1 +LOADER: will resume at 3F579970 after create_process +LOADER: frame 2500: entered loader at 01880000 (caller SP=007F5290) +LOADER: marker counter 1431655765 -> 5 (child running) +LOADER: marker counter 5 -> 11 (child running) +LOADER: marker counter 11 -> 16 (child running) +LOADER: marker counter 16 -> 21 (child running) +LOADER: marker counter 21 -> 26 (child running) +LOADER: marker counter 26 -> 31 (child running) +LOADER: marker counter 31 -> 36 (child running) +LOADER: marker counter 36 -> 41 (child running) +LOADER: marker counter 41 -> 47 (child running) +LOADER: marker counter 47 -> 52 (child running) +LOADER: marker counter 52 -> 57 (child running) +LOADER: marker counter 57 -> 62 (child running) +LOADER: marker counter 62 -> 67 (child running) +LOADER: marker counter 67 -> 72 (child running) +LOADER: marker counter 72 -> 77 (child running) +LOADER: marker counter 77 -> 83 (child running) +LOADER: marker counter 83 -> 88 (child running) +LOADER: marker counter 88 -> 93 (child running) +LOADER: marker counter 93 -> 98 (child running) +LOADER: marker counter 98 -> 103 (child running) +LOADER: marker counter 103 -> 108 (child running) +LOADER: marker counter 108 -> 114 (child running) +LOADER: marker counter 114 -> 119 (child running) +LOADER: marker counter 119 -> 124 (child running) +LOADER: marker counter 124 -> 129 (child running) +LOADER: marker counter 129 -> 134 (child running) +LOADER: marker counter 134 -> 139 (child running) +LOADER: marker counter 139 -> 145 (child running) +LOADER: marker counter 145 -> 150 (child running) +LOADER: marker counter 150 -> 155 (child running) +LOADER: marker counter 155 -> 160 (child running) +LOADER: marker counter 160 -> 165 (child running) +LOADER: marker counter 165 -> 170 (child running) +LOADER: marker counter 170 -> 176 (child running) +LOADER: marker counter 176 -> 181 (child running) +LOADER: marker counter 181 -> 186 (child running) +LOADER: marker counter 186 -> 191 (child running) +LOADER: marker counter 191 -> 196 (child running) +LOADER: marker counter 196 -> 201 (child running) +LOADER: marker counter 201 -> 207 (child running) +LOADER: marker counter 207 -> 212 (child running) +LOADER: marker counter 212 -> 217 (child running) +LOADER: marker counter 217 -> 222 (child running) +LOADER: marker counter 222 -> 227 (child running) +LOADER: marker counter 227 -> 232 (child running) +LOADER: marker counter 232 -> 238 (child running) +LOADER: marker counter 238 -> 243 (child running) +LOADER: marker counter 243 -> 248 (child running) +LOADER: marker counter 248 -> 253 (child running) +LOADER: marker counter 253 -> 258 (child running) +LOADER: marker counter 258 -> 263 (child running) +LOADER: marker counter 263 -> 269 (child running) +LOADER: marker counter 269 -> 274 (child running) +LOADER: marker counter 274 -> 279 (child running) +LOADER: marker counter 279 -> 284 (child running) +LOADER: marker counter 284 -> 289 (child running) +LOADER: marker counter 289 -> 294 (child running) +LOADER: marker counter 294 -> 299 (child running) +LOADER: marker counter 299 -> 305 (child running) +LOADER: marker counter 305 -> 310 (child running) +LOADER: marker counter 310 -> 315 (child running) +LOADER: marker counter 315 -> 320 (child running) +LOADER: marker counter 320 -> 325 (child running) +LOADER: marker counter 325 -> 330 (child running) +LOADER: marker counter 330 -> 336 (child running) +LOADER: marker counter 336 -> 341 (child running) +LOADER: marker counter 341 -> 346 (child running) +LOADER: marker counter 346 -> 351 (child running) +LOADER: marker counter 351 -> 356 (child running) +LOADER: marker counter 356 -> 361 (child running) +LOADER: marker counter 361 -> 367 (child running) +LOADER: marker counter 367 -> 372 (child running) +LOADER: marker counter 372 -> 377 (child running) +LOADER: marker counter 377 -> 382 (child running) +LOADER: marker counter 382 -> 387 (child running) +LOADER: marker counter 387 -> 392 (child running) +LOADER: marker counter 392 -> 398 (child running) +LOADER: marker counter 398 -> 403 (child running) +LOADER: marker counter 403 -> 408 (child running) +LOADER: marker counter 408 -> 413 (child running) +LOADER: marker counter 413 -> 418 (child running) +LOADER: marker counter 418 -> 423 (child running) +LOADER: marker counter 423 -> 428 (child running) +LOADER: marker counter 428 -> 434 (child running) +LOADER: marker counter 434 -> 439 (child running) +LOADER: marker counter 439 -> 444 (child running) +LOADER: marker counter 444 -> 449 (child running) +LOADER: marker counter 449 -> 454 (child running) +LOADER: marker counter 454 -> 459 (child running) +LOADER: marker counter 459 -> 465 (child running) +LOADER: marker counter 465 -> 470 (child running) +LOADER: marker counter 470 -> 475 (child running) +LOADER: marker counter 475 -> 480 (child running) +LOADER: marker counter 480 -> 485 (child running) +LOADER: marker counter 485 -> 490 (child running) +LOADER: marker counter 490 -> 495 (child running) +LOADER: marker counter 495 -> 501 (child running) +LOADER: marker counter 501 -> 506 (child running) +LOADER: marker counter 506 -> 511 (child running) +LOADER: marker counter 511 -> 516 (child running) +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=516 +LOADER: PC at inspection = 018C0260 +Average speed: 325.97% (36 seconds) diff --git a/dwt/loader/evidence/marker_run.log b/dwt/loader/evidence/marker_run.log new file mode 100644 index 0000000..e5ba6e6 --- /dev/null +++ b/dwt/loader/evidence/marker_run.log @@ -0,0 +1,128 @@ +Warning: -video none doesn't make much sense without -seconds_to_run +vt1200: screen_update#1 frame=0 clip_y=0..0 screen=1280x901 +LOADER: payload dwt/loader/build/loader.bin (1220 B) -> bit 01880000 +LOADER: COFF dwt/loader/build/marker.coff (510 B) -> bit 01900000 +LOADER: file magic=0090 nscns=3 opthdr=28 optmagic=0108 entry=018C0000 textstart=018C0000 +LOADER: sec .text paddr=018C0000 size=656 bits (82 B) scnptr=0xa8 flags=0x20 +LOADER: sec .data paddr=018D0000 size=64 bits (8 B) scnptr=0xfa flags=0x40 +LOADER: sec .bss paddr=018D8000 size=64 bits (8 B) scnptr=0 flags=0x80 +vt1200: screen_update#2 frame=0 clip_y=1..900 screen=1280x901 +vt1200: scanline#1 line=1 enabled=1 +vt1200: guard#1 frame=0 dump_frame=-1 dump_ram=-1 +vt1200: screen_update#3 frame=1 clip_y=0..0 screen=1280x901 +vt1200: scanline#2 line=0 enabled=1 +vt1200: screen_update#4 frame=1 clip_y=1..1 screen=1280x901 +vt1200: scanline#3 line=1 enabled=1 +vt1200: scanline#4 line=2 enabled=1 +LOADER: loader poked 610 words, readback 0 bad at 01880000 +LOADER: COFF poked 255 words, readback 0 bad at 01900000 +LOADER: pre-filled .bss 018D8000..018D8040 with A5A5 +LOADER: descriptor @01882C00 magic=4C44/5231 buf=01900000 len=510 mode=0 +LOADER: will resume at 3F579970 after create_process +LOADER: frame 2500: entered loader at 01880000 (caller SP=007F52B0) +LOADER: marker counter 1431655765 -> 5 (child running) +LOADER: marker counter 5 -> 11 (child running) +LOADER: marker counter 11 -> 16 (child running) +LOADER: marker counter 16 -> 21 (child running) +LOADER: marker counter 21 -> 26 (child running) +LOADER: marker counter 26 -> 31 (child running) +LOADER: marker counter 31 -> 36 (child running) +LOADER: marker counter 36 -> 41 (child running) +LOADER: marker counter 41 -> 47 (child running) +LOADER: marker counter 47 -> 52 (child running) +LOADER: marker counter 52 -> 57 (child running) +LOADER: marker counter 57 -> 62 (child running) +LOADER: marker counter 62 -> 67 (child running) +LOADER: marker counter 67 -> 72 (child running) +LOADER: marker counter 72 -> 77 (child running) +LOADER: marker counter 77 -> 83 (child running) +LOADER: marker counter 83 -> 88 (child running) +LOADER: marker counter 88 -> 93 (child running) +LOADER: marker counter 93 -> 98 (child running) +LOADER: marker counter 98 -> 103 (child running) +LOADER: marker counter 103 -> 108 (child running) +LOADER: marker counter 108 -> 114 (child running) +LOADER: marker counter 114 -> 119 (child running) +LOADER: marker counter 119 -> 124 (child running) +LOADER: marker counter 124 -> 129 (child running) +LOADER: marker counter 129 -> 134 (child running) +LOADER: marker counter 134 -> 139 (child running) +LOADER: marker counter 139 -> 145 (child running) +LOADER: marker counter 145 -> 150 (child running) +LOADER: marker counter 150 -> 155 (child running) +LOADER: marker counter 155 -> 160 (child running) +LOADER: marker counter 160 -> 165 (child running) +LOADER: marker counter 165 -> 170 (child running) +LOADER: marker counter 170 -> 176 (child running) +LOADER: marker counter 176 -> 181 (child running) +LOADER: marker counter 181 -> 186 (child running) +LOADER: marker counter 186 -> 191 (child running) +LOADER: marker counter 191 -> 196 (child running) +LOADER: marker counter 196 -> 201 (child running) +LOADER: marker counter 201 -> 207 (child running) +LOADER: marker counter 207 -> 212 (child running) +LOADER: marker counter 212 -> 217 (child running) +LOADER: marker counter 217 -> 222 (child running) +LOADER: marker counter 222 -> 227 (child running) +LOADER: marker counter 227 -> 232 (child running) +LOADER: marker counter 232 -> 238 (child running) +LOADER: marker counter 238 -> 243 (child running) +LOADER: marker counter 243 -> 248 (child running) +LOADER: marker counter 248 -> 253 (child running) +LOADER: marker counter 253 -> 258 (child running) +LOADER: marker counter 258 -> 263 (child running) +LOADER: marker counter 263 -> 269 (child running) +LOADER: marker counter 269 -> 274 (child running) +LOADER: marker counter 274 -> 279 (child running) +LOADER: marker counter 279 -> 284 (child running) +LOADER: marker counter 284 -> 289 (child running) +LOADER: marker counter 289 -> 294 (child running) +LOADER: marker counter 294 -> 299 (child running) +LOADER: marker counter 299 -> 305 (child running) +LOADER: marker counter 305 -> 310 (child running) +LOADER: marker counter 310 -> 315 (child running) +LOADER: marker counter 315 -> 320 (child running) +LOADER: marker counter 320 -> 325 (child running) +LOADER: marker counter 325 -> 330 (child running) +LOADER: marker counter 330 -> 336 (child running) +LOADER: marker counter 336 -> 341 (child running) +LOADER: marker counter 341 -> 346 (child running) +LOADER: marker counter 346 -> 351 (child running) +LOADER: marker counter 351 -> 356 (child running) +LOADER: marker counter 356 -> 361 (child running) +LOADER: marker counter 361 -> 367 (child running) +LOADER: marker counter 367 -> 372 (child running) +LOADER: marker counter 372 -> 377 (child running) +LOADER: marker counter 377 -> 382 (child running) +LOADER: marker counter 382 -> 387 (child running) +LOADER: marker counter 387 -> 392 (child running) +LOADER: marker counter 392 -> 398 (child running) +LOADER: marker counter 398 -> 403 (child running) +LOADER: marker counter 403 -> 408 (child running) +LOADER: marker counter 408 -> 413 (child running) +LOADER: marker counter 413 -> 418 (child running) +LOADER: marker counter 418 -> 423 (child running) +LOADER: marker counter 423 -> 428 (child running) +LOADER: marker counter 428 -> 434 (child running) +LOADER: marker counter 434 -> 439 (child running) +LOADER: marker counter 439 -> 444 (child running) +LOADER: marker counter 444 -> 449 (child running) +LOADER: marker counter 449 -> 454 (child running) +LOADER: marker counter 454 -> 459 (child running) +LOADER: marker counter 459 -> 465 (child running) +LOADER: marker counter 465 -> 470 (child running) +LOADER: marker counter 470 -> 475 (child running) +LOADER: marker counter 475 -> 480 (child running) +LOADER: marker counter 480 -> 485 (child running) +LOADER: marker counter 485 -> 490 (child running) +LOADER: marker counter 490 -> 495 (child running) +LOADER: marker counter 495 -> 501 (child running) +LOADER: marker counter 501 -> 506 (child running) +LOADER: marker counter 506 -> 511 (child running) +LOADER: marker counter 511 -> 516 (child running) +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=516 +LOADER: PC at inspection = 018C0260 +Average speed: 333.33% (36 seconds) diff --git a/dwt/loader/evidence/reldemo.txt b/dwt/loader/evidence/reldemo.txt new file mode 100644 index 0000000..08d92a8 --- /dev/null +++ b/dwt/loader/evidence/reldemo.txt @@ -0,0 +1,104 @@ +== /home/arti/code/k-space/vt1200/dwt/loader/build/rela.obj (342 bytes) + file hdr: magic=0x0090 nscns=3 symptr=0xb4 nsyms=9 opthdr=0 flags=0x0100 + sec 1 .text paddr=0000000000 vaddr=0000000000 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 + sec 2 .data paddr=0000000000 vaddr=0000000000 size=0x0 bits (0 B) scnptr=0xa0 nreloc=0 flags=0x40 [data] + sec 3 .bss paddr=0000000000 vaddr=0000000000 size=0x0 bits (0 B) scnptr=0x0 nreloc=0 flags=0x80 [bss] + sym[0] .text value=0000000000 sec=1 class=3 naux=1 + sym[2] .data value=0000000000 sec=2 class=3 naux=1 + sym[4] .bss value=0000000000 sec=3 class=3 naux=1 + sym[6] _start value=0000000000 sec=1 class=2 + sym[7] ext_func value=0000000000 sec=0 class=2 + sym[8] ext_data value=0000000000 sec=0 class=2 + +== /home/arti/code/k-space/vt1200/dwt/loader/build/relb.obj (290 bytes) + file hdr: magic=0x0090 nscns=3 symptr=0x92 nsyms=8 opthdr=0 flags=0x0100 + sec 1 .text paddr=0000000000 vaddr=0000000000 size=0x10 bits (2 B) scnptr=0x8c nreloc=0 flags=0x20 [text] + sec 2 .data paddr=0000000000 vaddr=0000000000 size=0x20 bits (4 B) scnptr=0x8e nreloc=0 flags=0x40 [data] + sec 3 .bss paddr=0000000000 vaddr=0000000000 size=0x0 bits (0 B) scnptr=0x0 nreloc=0 flags=0x80 [bss] + sym[0] .text value=0000000000 sec=1 class=3 naux=1 + sym[2] .data value=0000000000 sec=2 class=3 naux=1 + sym[4] .bss value=0000000000 sec=3 class=3 naux=1 + sym[6] ext_func value=0000000000 sec=1 class=2 + sym[7] ext_data value=0000000000 sec=2 class=2 + +== /home/arti/code/k-space/vt1200/dwt/loader/build/relabs.out (572 bytes) + file hdr: magic=0x0090 nscns=3 symptr=0xc2 nsyms=21 opthdr=28 flags=0x0103 + optional: magic=0x0108 vstamp=0x00c7 text=0xb0 bits data=0x20 bits bss=0x0 bits entry=0x018c0000 textstart=0x018c0000 datastart=0x018d0000 + optional: sizes in bytes text=22 data=4 bss=0; entry in bits=0x18c0000 + sec 1 .text paddr=00018c0000 vaddr=00018c0000 size=0xb0 bits (22 B) scnptr=0xa8 nreloc=0 flags=0x20 [text] + sec 2 .data paddr=00018d0000 vaddr=00018d0000 size=0x20 bits (4 B) scnptr=0xbe nreloc=0 flags=0x40 [data] + sec 3 .bss paddr=00018d8000 vaddr=00018d8000 size=0x0 bits (0 B) scnptr=0x0 nreloc=0 flags=0x80 [bss] + sym[0] .text value=00018c0000 sec=1 class=3 naux=1 + sym[2] .data value=00018d0000 sec=2 class=3 naux=1 + sym[4] .bss value=00018d8000 sec=3 class=3 naux=1 + sym[6] .text value=00018c00a0 sec=1 class=3 naux=1 + sym[8] .data value=00018d0000 sec=2 class=3 naux=1 + sym[10] .bss value=00018d8000 sec=3 class=3 naux=1 + sym[12] _start value=00018c0000 sec=1 class=2 + sym[13] ext_func value=00018c00a0 sec=1 class=2 + sym[14] ext_data value=00018d0000 sec=2 class=2 + sym[15] .text value=00018c0000 sec=1 class=2 + sym[16] etext value=00018c00b0 sec=1 class=2 + sym[17] .data value=00018d0000 sec=2 class=2 + sym[18] edata value=00018d0020 sec=2 class=2 + sym[19] .bss value=00018d8000 sec=3 class=2 + sym[20] end value=00018d8000 sec=3 class=2 + +== /home/arti/code/k-space/vt1200/dwt/loader/build/relabs2.out (572 bytes) + file hdr: magic=0x0090 nscns=3 symptr=0xc2 nsyms=21 opthdr=28 flags=0x0103 + optional: magic=0x0108 vstamp=0x00c7 text=0xb0 bits data=0x20 bits bss=0x0 bits entry=0x018e0000 textstart=0x018e0000 datastart=0x018f0000 + optional: sizes in bytes text=22 data=4 bss=0; entry in bits=0x18e0000 + sec 1 .text paddr=00018e0000 vaddr=00018e0000 size=0xb0 bits (22 B) scnptr=0xa8 nreloc=0 flags=0x20 [text] + sec 2 .data paddr=00018f0000 vaddr=00018f0000 size=0x20 bits (4 B) scnptr=0xbe nreloc=0 flags=0x40 [data] + sec 3 .bss paddr=00018f8000 vaddr=00018f8000 size=0x0 bits (0 B) scnptr=0x0 nreloc=0 flags=0x80 [bss] + sym[0] .text value=00018e0000 sec=1 class=3 naux=1 + sym[2] .data value=00018f0000 sec=2 class=3 naux=1 + sym[4] .bss value=00018f8000 sec=3 class=3 naux=1 + sym[6] .text value=00018e00a0 sec=1 class=3 naux=1 + sym[8] .data value=00018f0000 sec=2 class=3 naux=1 + sym[10] .bss value=00018f8000 sec=3 class=3 naux=1 + sym[12] _start value=00018e0000 sec=1 class=2 + sym[13] ext_func value=00018e00a0 sec=1 class=2 + sym[14] ext_data value=00018f0000 sec=2 class=2 + sym[15] .text value=00018e0000 sec=1 class=2 + sym[16] etext value=00018e00b0 sec=1 class=2 + sym[17] .data value=00018f0000 sec=2 class=2 + sym[18] edata value=00018f0020 sec=2 class=2 + sym[19] .bss value=00018f8000 sec=3 class=2 + sym[20] end value=00018f8000 sec=3 class=2 + +== /home/arti/code/k-space/vt1200/dwt/loader/build/reloc.out (572 bytes) + file hdr: magic=0x0090 nscns=3 symptr=0xc2 nsyms=21 opthdr=28 flags=0x0103 + optional: magic=0x0108 vstamp=0x00c7 text=0xb0 bits data=0x20 bits bss=0x0 bits entry=0x00000000 textstart=0x00000000 datastart=0x000000b0 + optional: sizes in bytes text=22 data=4 bss=0; entry in bits=0 (no -e global symbol) + sec 1 .text paddr=0000000000 vaddr=0000000000 size=0xb0 bits (22 B) scnptr=0xa8 nreloc=0 flags=0x20 [text] + sec 2 .data paddr=00000000b0 vaddr=00000000b0 size=0x20 bits (4 B) scnptr=0xbe nreloc=0 flags=0x40 [data] + sec 3 .bss paddr=00000000d0 vaddr=00000000d0 size=0x0 bits (0 B) scnptr=0x0 nreloc=0 flags=0x80 [bss] + sym[0] .text value=0000000000 sec=1 class=3 naux=1 + sym[2] .data value=00000000b0 sec=2 class=3 naux=1 + sym[4] .bss value=00000000d0 sec=3 class=3 naux=1 + sym[6] .text value=00000000a0 sec=1 class=3 naux=1 + sym[8] .data value=00000000b0 sec=2 class=3 naux=1 + sym[10] .bss value=00000000d0 sec=3 class=3 naux=1 + sym[12] _start value=0000000000 sec=1 class=2 + sym[13] ext_func value=00000000a0 sec=1 class=2 + sym[14] ext_data value=00000000b0 sec=2 class=2 + sym[15] .text value=0000000000 sec=1 class=2 + sym[16] etext value=00000000b0 sec=1 class=2 + sym[17] .data value=00000000b0 sec=2 class=2 + sym[18] edata value=00000000d0 sec=2 class=2 + sym[19] .bss value=00000000d0 sec=3 class=2 + sym[20] end value=00000000d0 sec=3 class=2 + + +== .text: 22 vs 22 bytes + differing bytes: 2 + 0x0004: 8d -> 8f + 0x0010: 8c -> 8e +== .data: 4 vs 4 bytes + differing bytes: 0 +== .bss: 0 vs 0 bytes + differing bytes: 0 + diff --git a/dwt/loader/evidence/reloc_refused.log b/dwt/loader/evidence/reloc_refused.log new file mode 100644 index 0000000..48c59a0 --- /dev/null +++ b/dwt/loader/evidence/reloc_refused.log @@ -0,0 +1,26 @@ +Warning: -video none doesn't make much sense without -seconds_to_run +vt1200: screen_update#1 frame=0 clip_y=0..0 screen=1280x901 +LOADER: payload dwt/loader/build/loader.bin (1220 B) -> bit 01880000 +LOADER: COFF dwt/loader/build/rela.obj (342 B) -> bit 01900000 +LOADER: file magic=0090 nscns=3 opthdr=0 optmagic=none entry=00000000 textstart=00000000 +LOADER: sec .text paddr=00000000 size=160 bits (20 B) scnptr=0x8c flags=0x20 +LOADER: sec .data paddr=00000000 size=0 bits (0 B) scnptr=0xa0 flags=0x40 +LOADER: sec .bss paddr=00000000 size=0 bits (0 B) scnptr=0 flags=0x80 +vt1200: screen_update#2 frame=0 clip_y=1..900 screen=1280x901 +vt1200: scanline#1 line=1 enabled=1 +vt1200: guard#1 frame=0 dump_frame=-1 dump_ram=-1 +vt1200: screen_update#3 frame=1 clip_y=0..0 screen=1280x901 +vt1200: scanline#2 line=0 enabled=1 +vt1200: screen_update#4 frame=1 clip_y=1..1 screen=1280x901 +vt1200: scanline#3 line=1 enabled=1 +vt1200: scanline#4 line=2 enabled=1 +LOADER: loader poked 610 words, readback 0 bad at 01880000 +LOADER: COFF poked 171 words, readback 0 bad at 01900000 +LOADER: pre-filled .bss 00000000..00000000 with A5A5 +LOADER: descriptor @01882C00 magic=4C44/5231 buf=01900000 len=342 mode=0 +LOADER: will resume at 3F579970 after create_process +LOADER: frame 2500: entered loader at 01880000 (caller SP=007F52B0) +LOADER: result status=7 (relocation records present (unsupported)) entry=00000000 text=00000000/0 bits data=00000000/0 bits bss=00000000/0 bits relocs=2 pcb=00000000 nscns=3 len=342 +LOADER: placed .text at 00000000: 20 bytes, 5 differing +LOADER: PC at inspection = 3F5799D0 +Average speed: 335.05% (35 seconds) diff --git a/dwt/loader/evidence/xhello_run.log b/dwt/loader/evidence/xhello_run.log new file mode 100644 index 0000000..93eca69 --- /dev/null +++ b/dwt/loader/evidence/xhello_run.log @@ -0,0 +1,28 @@ +Warning: -video none doesn't make much sense without -seconds_to_run +vt1200: screen_update#1 frame=0 clip_y=0..0 screen=1280x901 +LOADER: payload dwt/loader/build/loader.bin (1220 B) -> bit 01880000 +LOADER: COFF dwt/loader/build/xhello.coff (774 B) -> bit 01900000 +LOADER: file magic=0090 nscns=3 opthdr=28 optmagic=0108 entry=00000000 textstart=01840000 +LOADER: sec .data paddr=00000000 size=0 bits (0 B) scnptr=0xa8 flags=0x40 +LOADER: sec .bss paddr=00000000 size=0 bits (0 B) scnptr=0 flags=0x80 +LOADER: sec .text paddr=01840000 size=3120 bits (390 B) scnptr=0xa8 flags=0x20 +vt1200: screen_update#2 frame=0 clip_y=1..900 screen=1280x901 +vt1200: scanline#1 line=1 enabled=1 +vt1200: guard#1 frame=0 dump_frame=-1 dump_ram=-1 +vt1200: screen_update#3 frame=1 clip_y=0..0 screen=1280x901 +vt1200: scanline#2 line=0 enabled=1 +vt1200: screen_update#4 frame=1 clip_y=1..1 screen=1280x901 +vt1200: scanline#3 line=1 enabled=1 +vt1200: scanline#4 line=2 enabled=1 +LOADER: loader poked 610 words, readback 0 bad at 01880000 +LOADER: COFF poked 387 words, readback 0 bad at 01900000 +LOADER: pre-filled .bss 00000000..00000000 with A5A5 +LOADER: descriptor @01882C00 magic=4C44/5231 buf=01900000 len=774 mode=0 +LOADER: will resume at 3F579970 after create_process +LOADER: frame 2500: entered loader at 01880000 (caller SP=007F52B0) +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 +LOADER: PC at inspection = 3F579970 +LOADER: snapshot /home/arti/code/k-space/vt1200/dwt/loader/evidence/loader_frame2650.png ok=true +Average speed: 344.05% (36 seconds) diff --git a/dwt/loader/loader.asm b/dwt/loader/loader.asm new file mode 100644 index 0000000..2c7f5b2 --- /dev/null +++ b/dwt/loader/loader.asm @@ -0,0 +1,496 @@ +; loader.asm -- COFF loader for the VT1200 / TMS34010. +; +; Input: a TI GSP COFF image (GSPLNK `-a` output) already in RAM, and a +; 16-byte descriptor (offsets in BITS, since all pointers here are bit +; addresses; the byte offsets are 0, 2, 4, 8 and 12): +; +; DESC + 0x00 bits u16 0x4C44 "LD" ) magic; both words must match +; DESC + 0x10 bits u16 0x5231 "R1" ) +; DESC + 0x20 bits u32 buf bit address of the COFF image +; DESC + 0x40 bits u32 len length in bytes +; DESC + 0x60 bits u32 mode 1 = return to the caller (rets) +; 0 = debug resume at LD_ORIG_PC +; +; The descriptor pointer is A8. If A8 is zero, or does not point at a valid +; descriptor, the fixed descriptor at 0x01882C00 is used. A transport that +; receives a COFF over the network only has to write {buf,len,mode} there and +; call the loader entry; it does not choose a load address. The loader takes +; the addresses from the COFF section headers. +; +; Result block at 0x01882D00 (32-bit cells, bit addresses): +; +; +0x000 status 0 ok, nonzero = error (see the ld_err_* labels) +; +0x020 entry bit address passed to create_process +; +0x040 text addr .text paddr from the COFF (bits) +; +0x060 text size .text size (bits) +; +0x080 data addr .data paddr +; +0x0A0 data size .data size (bits) +; +0x0C0 bss addr .bss paddr +; +0x0E0 bss size .bss size (bits) +; +0x100 relocs relocation entries seen in the output COFF +; +0x120 pcb create_process() return value (0 on failure) +; +0x140 nscns section count from the file header +; +0x160 coflen length from the descriptor (bytes) +; +0x180 version loader base address, 0x01880000 +; +; The image is POSITION-DEPENDENT: GSPLNK resolves every reference and emits +; nreloc = 0, so each section is copied to the bit address in its header and +; no relocation is applied. A COFF that still carries relocation records is +; refused (status 7) rather than guessed at. +; +; Safety checks before any copy: +; * size is a whole number of 16-bit words; +; * the section bytes lie inside the descriptor's (buf,len); +; * paddr..paddr+size is inside main RAM (bits 0x100..0x2000000); +; * it does not overlap the loader page [01880000,01883200) or the COFF +; buffer; +; * it 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). This is a check, not a reservation: the +; loader cannot make malloc hand out a chosen address. +; +; Delivered debugger-assisted for now: poke the raw .bin at 0x01880000, write +; the COFF at buf, write the descriptor, then set PC = 0x01880000 (A8 = 0 is +; fine). mode = 0 resumes at LD_ORIG_PC (the harness saves the interrupted +; PC); mode = 1 returns to the caller and passes the status in A8. +; +; Linked at bit 0x01880000; position-dependent itself. + + .def _start + +LOADER_BASE equ 01880000h +LD_SCRATCH equ 01882800h +DESC_ADDR equ 01882C00h +RESULT_BASE equ 01882D00h +ARG_AREA equ 01882B00h +RETURN_STUB equ 01883100h + +LD_SP_AFTER equ LD_SCRATCH+000h +LD_A14 equ LD_SCRATCH+020h +LD_ENTRY equ LD_SCRATCH+040h +LD_BUF equ LD_SCRATCH+060h +LD_LEN equ LD_SCRATCH+080h +LD_MODE equ LD_SCRATCH+0A0h +LD_NSCNS equ LD_SCRATCH+0C0h +LD_RELOCS equ LD_SCRATCH+0E0h +LD_SEC_PADDR equ LD_SCRATCH+100h +LD_SEC_SIZE equ LD_SCRATCH+120h +LD_SEC_SCNPTR equ LD_SCRATCH+140h +LD_TEXT_ADDR equ LD_SCRATCH+160h +LD_TEXT_SIZE equ LD_SCRATCH+180h +LD_DATA_ADDR equ LD_SCRATCH+1A0h +LD_DATA_SIZE equ LD_SCRATCH+1C0h +LD_BSS_ADDR equ LD_SCRATCH+1E0h +LD_BSS_SIZE equ LD_SCRATCH+200h +LD_ORIG_PC equ LD_SCRATCH+220h + +RESULT_STATUS equ RESULT_BASE+000h +RESULT_ENTRY equ RESULT_BASE+020h +RESULT_TEXT_A equ RESULT_BASE+040h +RESULT_TEXT_S equ RESULT_BASE+060h +RESULT_DATA_A equ RESULT_BASE+080h +RESULT_DATA_S equ RESULT_BASE+0A0h +RESULT_BSS_A equ RESULT_BASE+0C0h +RESULT_BSS_S equ RESULT_BASE+0E0h +RESULT_RELOCS equ RESULT_BASE+100h +RESULT_PCB equ RESULT_BASE+120h +RESULT_NSCNS equ RESULT_BASE+140h +RESULT_LEN equ RESULT_BASE+160h +RESULT_VERSION equ RESULT_BASE+180h + + .text +_start: + mmtm sp, a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13 + move sp, @LD_SP_AFTER, 1 + move a14, @LD_A14, 1 + setf 16, 0, 0 + setf 32, 0, 1 + + ; ---- descriptor ----------------------------------------------------- + ; A8 points at it only if both magic words match; else use the fixed one. + move a8, a6 + move a8, a8 + jreq ld_fixed_desc + move *a6, a0, 0 + cmpi 4C44h, a0 + jrne ld_fixed_desc + move *a6(10h), a0, 0 + cmpi 5231h, a0 + jreq ld_have_desc +ld_fixed_desc: + movi DESC_ADDR, a6, L + move *a6, a0, 0 + cmpi 4C44h, a0 + jrne ld_bad_desc + move *a6(10h), a0, 0 + cmpi 5231h, a0 + jrne ld_bad_desc +ld_have_desc: + move *a6(20h), a7, 1 ; buf + move *a6(40h), a8, 1 ; len + move *a6(60h), a12, 1 ; mode + move a7, @LD_BUF, 1 + move a8, @LD_LEN, 1 + move a12, @LD_MODE, 1 + move a12, @RESULT_VERSION, 1 ; harmless; overwritten below + movi LOADER_BASE, a0, L + move a0, @RESULT_VERSION, 1 + + ; clear the counters we report; status stays FF until this loader writes it + movi 0FFFFFFFFh, a0, L + move a0, @RESULT_STATUS, 1 + sub a0, a0 + move a0, @LD_RELOCS, 1 + move a0, @LD_TEXT_ADDR, 1 + move a0, @LD_TEXT_SIZE, 1 + move a0, @LD_DATA_ADDR, 1 + move a0, @LD_DATA_SIZE, 1 + move a0, @LD_BSS_ADDR, 1 + move a0, @LD_BSS_SIZE, 1 + move a0, @RESULT_ENTRY, 1 + move a0, @RESULT_TEXT_A, 1 + move a0, @RESULT_TEXT_S, 1 + move a0, @RESULT_DATA_A, 1 + move a0, @RESULT_DATA_S, 1 + move a0, @RESULT_BSS_A, 1 + move a0, @RESULT_BSS_S, 1 + move a0, @RESULT_RELOCS, 1 + move a0, @RESULT_PCB, 1 + + ; ---- file header ---------------------------------------------------- + movi 20, a0, L + cmp a0, a8 + jrlo ld_err_short + move *a7, a0, 0 ; magic + cmpi 0090h, a0 + jrne ld_err_magic + move *a7(10h), a9, 0 ; nscns (+2 bytes) + move a9, @LD_NSCNS, 1 + move *a7(10h), a0, 0 + move a9, @RESULT_NSCNS, 1 + move a8, @RESULT_LEN, 1 + + ; section headers = buf + (20 + opthdr + nscns*40) bytes; must fit in len + move *a7(80h), a1, 0 ; opthdr (+16 bytes) + move a9, a2 + sll 5, a2 ; nscns*32 + move a9, a3 + sll 3, a3 ; nscns*8 + add a3, a2 ; nscns*40 + add a1, a2 + addi 20, a2 ; byte offset of section headers end + cmp a2, a8 ; a8 - a2 + jrlo ld_err_short + + ; secbase = buf + (20 + opthdr)*8 bits + sll 3, a1 ; opthdr in bits + addi 160, a1 ; 20 bytes in bits + add a7, a1 + move a1, a10 + + ; ---- entry point ---------------------------------------------------- + ; optional header at buf+20 bytes (0x0A0 bits): magic 0x0108; entry at + ; +16 bytes (0x080 bits further); textstart at +20 bytes. + move *a7(0A0h), a0, 0 + cmpi 0108h, a0 + jrne ld_no_opt + move *a7(120h), a5, 1 ; entry (bits) + move a5, a5 + jrne ld_entry_set + move *a7(140h), a5, 1 ; textstart (bits) + jruc ld_entry_set +ld_no_opt: + sub a5, a5 + +ld_entry_set: + move a5, @LD_ENTRY, 1 + move a5, @RESULT_ENTRY, 1 + + ; ---- section loop --------------------------------------------------- + sub a11, a11 ; section index +ld_sec_loop: + cmp a9, a11 ; a11 - a9 + jrhs ld_sec_done + move *a10(40h), a0, 1 ; paddr (bits) +8 bytes + move *a10(80h), a1, 1 ; size (bits) +16 + move *a10(0A0h), a2, 1 ; scnptr (bytes) +20 + move *a10(100h), a3, 0 ; nreloc +32 + move *a10(120h), a4, 1 ; flags +36 + move a0, @LD_SEC_PADDR, 1 + move a1, @LD_SEC_SIZE, 1 + move a2, @LD_SEC_SCNPTR, 1 + + ; record the first text/data/bss section for the result block + move a4, a5 + andi 20h, a5 + move a5, a5 + jreq ld_not_text + move @LD_TEXT_ADDR, a5, 1 + move a5, a5 + jrne ld_not_text + move a0, @LD_TEXT_ADDR, 1 + move a1, @LD_TEXT_SIZE, 1 +ld_not_text: + move a4, a5 + andi 40h, a5 + move a5, a5 + jreq ld_not_data + move @LD_DATA_ADDR, a5, 1 + move a5, a5 + jrne ld_not_data + move a0, @LD_DATA_ADDR, 1 + move a1, @LD_DATA_SIZE, 1 +ld_not_data: + move a4, a5 + andi 80h, a5 + move a5, a5 + jreq ld_not_bss + move @LD_BSS_ADDR, a5, 1 + move a5, a5 + jrne ld_not_bss + move a0, @LD_BSS_ADDR, 1 + move a1, @LD_BSS_SIZE, 1 +ld_not_bss: + + ; relocation records: count, then refuse (the linked image has none) + move a3, a3 + jreq ld_no_reloc + move @LD_RELOCS, a5, 1 + add a3, a5 + move a5, @LD_RELOCS, 1 + jruc ld_err_reloc +ld_no_reloc: + + ; size 0: nothing to place; size must be whole 16-bit words + move a1, a1 + jreq ld_sec_next + move a1, a5 + andi 0Fh, a5 + move a5, a5 + jrne ld_err_size + + ; source range: scnptr + size/8 <= len (when there are bytes) + move a2, a2 + jreq ld_no_src + move a1, a5 + srl 3, a5 ; size in bytes + add a5, a2 ; scnptr + nbytes + cmp a8, a2 ; a8 - a2 + jrhi ld_err_short +ld_no_src: + + ; target range: 0x100 <= paddr and paddr+size <= 0x2000000 bits + move @LD_SEC_PADDR, a0, 1 + cmpi 100h, a0 + jrlo ld_err_bounds + move @LD_SEC_SIZE, a1, 1 + move a0, a3 + add a1, a3 ; target end + movi 2000000h, a5, L + cmp a5, a3 + jrhi ld_err_bounds + + ; overlap with the loader page [01880000,01883200) + movi 01883200h, a5, L + cmp a5, a0 + jrhs ld_ov1_done + movi 01880000h, a5, L + cmp a3, a5 + jrhs ld_ov1_done + jruc ld_err_overlap +ld_ov1_done: + + ; overlap with the COFF buffer [buf, buf+len*8) + move a8, a5 + sll 3, a5 + add a7, a5 ; buffer end in bits + cmp a5, a0 + jrhs ld_ov2_done + cmp a3, a7 + jrhs ld_ov2_done + jruc ld_err_overlap +ld_ov2_done: + + ; ---- global free-list check ---------------------------------------- + ; a block is {u32 size_bits; ...; u32 next@+0x20}; data at block+0x40. + move a0, @LD_SEC_PADDR, 1 ; (unchanged, kept for clarity) + move a3, @LD_SEC_SIZE, 1 ; temporarily hold target end + move @1E8AC0h, a6, 1 ; global free-list head (block address) + movi 64, a5, L +ld_free_walk: + move a6, a6 + jreq ld_err_notfree + move *a6, a1, 1 ; block size in bits + move *a6(20h), a2, 1 ; next block + move a6, a0 + addi 40h, a0 ; user start + move @LD_SEC_PADDR, a3, 1 + cmp a0, a3 + jrlo ld_free_next + move a6, a4 + add a1, a4 ; block end + move @LD_SEC_SIZE, a1, 1 ; target end (stashed above) + cmp a4, a1 + jrhi ld_free_next + jruc ld_free_ok +ld_free_next: + move a2, a6 + dsjs a5, ld_free_walk + jruc ld_err_notfree +ld_free_ok: + + ; restore size in LD_SEC_SIZE (the stash overwrote it) + ; target end - paddr = size + move @LD_SEC_SIZE, a1, 1 + move @LD_SEC_PADDR, a0, 1 + sub a0, a1 ; a1 = size again + move a1, @LD_SEC_SIZE, 1 + + ; ---- place the section --------------------------------------------- + move @LD_SEC_SCNPTR, a0, 1 + move a0, a0 + jreq ld_zero_sec + sll 3, a0 ; file offset -> bit offset + add a7, a0 ; source address + move @LD_SEC_PADDR, a2, 1 + move @LD_SEC_SIZE, a1, 1 + srl 4, a1 ; number of 16-bit words +ld_copy_words: + move *a0+, a4, 0 + move a4, *a2+, 0 + dsjs a1, ld_copy_words + jruc ld_sec_next + +ld_zero_sec: + move @LD_SEC_PADDR, a2, 1 + move @LD_SEC_SIZE, a1, 1 + srl 4, a1 + sub a4, a4 +ld_zero_words: + move a4, *a2+, 0 + dsjs a1, ld_zero_words + +ld_sec_next: + addi 140h, a10 ; 40-byte section header = 320 bits + addi 1, a11 + jruc ld_sec_loop + +ld_sec_done: + move @LD_TEXT_ADDR, a0, 1 + move a0, @RESULT_TEXT_A, 1 + move @LD_TEXT_SIZE, a1, 1 + move a1, @RESULT_TEXT_S, 1 + move @LD_DATA_ADDR, a0, 1 + move a0, @RESULT_DATA_A, 1 + move @LD_DATA_SIZE, a1, 1 + move a1, @RESULT_DATA_S, 1 + move @LD_BSS_ADDR, a0, 1 + move a0, @RESULT_BSS_A, 1 + move @LD_BSS_SIZE, a1, 1 + move a1, @RESULT_BSS_S, 1 + move @LD_RELOCS, a0, 1 + move a0, @RESULT_RELOCS, 1 + + ; ---- create_process(entry, name, stack, 1, arg) -------------------- + move @LD_ENTRY, a0, 1 + move a0, a0 + jrne ld_have_entry + jruc ld_err_noentry +ld_have_entry: + move @LD_A14, a14, 1 + move @LD_SP_AFTER, sp, 1 + move a14, -*sp, 1 ; the slot the firmware callee restores + 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 ld_appname, a0, L + move a0, *a14+, 1 ; arg1 = name string + move @LD_ENTRY, a0, 1 + move a0, *a14+, 1 ; arg0 = entry (bit address) + calla 3F706A40h ; create_process + move @LD_SP_AFTER, sp, 1 + move @LD_A14, a14, 1 + setf 16, 0, 0 + setf 32, 0, 1 + move a8, @RESULT_PCB, 1 + move a8, a8 + jreq ld_err_create + + sub a0, a0 + move a0, @RESULT_STATUS, 1 + jruc ld_finish + + ; ---- errors --------------------------------------------------------- +ld_bad_desc: + movi 1, a0, L + jruc ld_error +ld_err_short: + movi 2, a0, L + jruc ld_error +ld_err_magic: + movi 3, a0, L + jruc ld_error +ld_err_size: + movi 4, a0, L + jruc ld_error +ld_err_bounds: + movi 5, a0, L + jruc ld_error +ld_err_overlap: + movi 6, a0, L + jruc ld_error +ld_err_reloc: + movi 7, a0, L + jruc ld_error +ld_err_notfree: + movi 8, a0, L + jruc ld_error +ld_err_noentry: + movi 9, a0, L + jruc ld_error +ld_err_create: + movi 10, a0, L +ld_error: + move a0, @RESULT_STATUS, 1 + move @LD_NSCNS, a0, 1 + move a0, @RESULT_NSCNS, 1 + move @LD_RELOCS, a0, 1 + move a0, @RESULT_RELOCS, 1 + +ld_finish: + move @LD_ENTRY, a0, 1 + move a0, @RESULT_ENTRY, 1 + move @LD_NSCNS, a0, 1 + move a0, @RESULT_NSCNS, 1 + move @LD_LEN, a0, 1 + move a0, @RESULT_LEN, 1 + movi LOADER_BASE, a0, L + move a0, @RESULT_VERSION, 1 + + ; ---- return or spin ------------------------------------------------- + move @LD_MODE, a0, 1 + move a0, a0 + jrne ld_return + ; mode 0 (debugger entry): restore the interrupted state and resume at + ; the PC the harness hijacked. Returning to the OS idle loop lets the + ; scheduler run the created child. + move @LD_SP_AFTER, sp, 1 + move @LD_A14, a14, 1 + mmfm sp, a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13 + move @LD_ORIG_PC, a0, 1 + jump a0 + +ld_return: + move @LD_SP_AFTER, sp, 1 + move @LD_A14, a14, 1 + mmfm sp, a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13 + move @RESULT_STATUS, a8, 1 ; 0 = ok, else the error code + rets + +ld_appname: + .byte "LOADER", 0 + .end diff --git a/dwt/loader/loader.lua b/dwt/loader/loader.lua new file mode 100644 index 0000000..fe87ab4 --- /dev/null +++ b/dwt/loader/loader.lua @@ -0,0 +1,338 @@ +-- loader.lua -- debugger-assisted COFF loader acceptance harness. +-- +-- Boots the firmware with VT1200_SKIP_LEVEL1=1 (the documented divergence) to +-- the OS, then at frame LOADER_AT pokes: +-- * the loader payload at bit 0x01880000 (raw dwt/loader/build/loader.bin) +-- * a GSPLNK COFF image at LOADER_BUF (default bit 0x01900000) +-- * the loader descriptor at 0x01882C00 +-- and sets PC = 0x01880000. It does not use the network, the rdebug service +-- or any firmware transport: this is the debugger-assisted milestone. +-- +-- The loader itself copies the COFF sections to their linked bit addresses and +-- calls create_process(entry, "LOADER", 0x6000, 1, 0). This script only pokes +-- the input and reads the result; it never writes the app's code or markers. +-- +-- Env: +-- LOADER_BIN loader payload (default dwt/loader/build/loader.bin) +-- LOADER_COFF app COFF (default dwt/loader/build/marker.coff) +-- LOADER_BUF COFF buffer bit address (default 0x01900000) +-- LOADER_AT injection frame (default 2500) +-- LOADER_END inspection/exit frame (default 2700) +-- LOADER_OUT evidence directory (default dwt/loader/evidence) +-- LOADER_SNAP 1 = write a PNG at LOADER_END (default 0) +-- LOADER_VERBOSE 1 = print the result block every 25 frames + +local function hex(v, w) return string.format("%0" .. (w or 8) .. "X", v) end + +local machine = manager.machine +local cpu = machine.devices[':maincpu'] +local dbg = machine.debugger +local prog = cpu.spaces['program'] + +if not dbg then + print("LOADER: debugger not enabled; pass -debug") + machine:exit() + return +end + +local AT = tonumber(os.getenv("LOADER_AT") or "2500") +local ENDF = tonumber(os.getenv("LOADER_END") or "2700") +local BIN = os.getenv("LOADER_BIN") or "dwt/loader/build/loader.bin" +local COFF = os.getenv("LOADER_COFF") or "dwt/loader/build/marker.coff" +local BUF = tonumber(os.getenv("LOADER_BUF") or "0x01900000") +local OUT = os.getenv("LOADER_OUT") or "dwt/loader/evidence" +local SNAP = os.getenv("LOADER_SNAP") == "1" +local MODE = tonumber(os.getenv("LOADER_MODE") or "0") +local A8DESC = os.getenv("LOADER_A8_DESC") == "1" +local VERBOSE = os.getenv("LOADER_VERBOSE") == "1" + +local LOADER_BASE = 0x01880000 +local DESC = 0x01882C00 +local RESULT = 0x01882D00 +local RETURN_STUB = 0x01883100 +local RETURN_SP = 0x01883110 + +local function rd(p) return io.open(p, "rb") end +local function slurp(p) + local f, err = rd(p) + if not f then error("cannot open " .. p .. ": " .. tostring(err)) end + local s = f:read("*a") + f:close() + return s +end + +local function u16(s, o) + return s:byte(o + 1) + s:byte(o + 2) * 256 +end + +local function u32(s, o) + return s:byte(o + 1) + s:byte(o + 2) * 256 + + s:byte(o + 3) * 65536 + s:byte(o + 4) * 16777216 +end + +local function parse_coff(d) + local c = { data = d } + c.magic = u16(d, 0) + c.nscns = u16(d, 2) + c.opthdr = u16(d, 16) + c.sections = {} + if c.opthdr > 0 then + c.optmagic = u16(d, 20) + c.entry = u32(d, 20 + 16) + c.textstart = u32(d, 20 + 20) + c.datastart = u32(d, 20 + 24) + end + local base = 20 + c.opthdr + for i = 0, c.nscns - 1 do + local o = base + i * 40 + local name = d:sub(o + 1, o + 8):gsub("%z", "") + c.sections[#c.sections + 1] = { + name = name, + paddr = u32(d, o + 8), + size = u32(d, o + 16), -- bits + scnptr = u32(d, o + 20), + flags = u32(d, o + 36), + } + end + return c +end + +local function poke_bytes(bitaddr, data) + local n = math.floor((#data + 1) / 2) + for i = 0, n - 1 do + local lo = data:byte(i * 2 + 1) or 0 + local hi = data:byte(i * 2 + 2) or 0 + prog:write_u16(bitaddr + i * 16, lo + hi * 256) + end + return n +end + +local function verify_bytes(bitaddr, data) + local n = math.floor((#data + 1) / 2) + local bad = 0 + for i = 0, n - 1 do + local lo = data:byte(i * 2 + 1) or 0 + local hi = data:byte(i * 2 + 2) or 0 + if prog:read_u16(bitaddr + i * 16) ~= lo + hi * 256 then + bad = bad + 1 + end + end + return n, bad +end + +local function w32(a, v) prog:write_u32(a, v) end +local function r32(a) + local ok, v = pcall(function() return prog:read_u32(a) end) + if ok then return v end + return 0xDEADBEEF +end + +local loader = slurp(BIN) +local coff = slurp(COFF) +local app = parse_coff(coff) + +print(string.format("LOADER: payload %s (%d B) -> bit %s", BIN, #loader, hex(LOADER_BASE))) +print(string.format("LOADER: COFF %s (%d B) -> bit %s", COFF, #coff, hex(BUF))) +print(string.format("LOADER: file magic=%04X nscns=%d opthdr=%d optmagic=%s entry=%s textstart=%s", + app.magic, app.nscns, app.opthdr, + app.optmagic and string.format("%04X", app.optmagic) or "none", + hex(app.entry or 0), hex(app.textstart or 0))) +for _, s in ipairs(app.sections) do + print(string.format("LOADER: sec %-8s paddr=%s size=%d bits (%d B) scnptr=%#x flags=%#x", + s.name, hex(s.paddr), s.size, math.floor(s.size / 8), s.scnptr, s.flags)) +end + +local TRACE = os.getenv("LOADER_TRACE") == "1" + +local frames = 0 +local injected = false +local done = false +local last_count = -1 + +local function status_name(v) + local t = { + [0] = "OK", + [1] = "bad descriptor", + [2] = "truncated COFF / section headers", + [3] = "bad COFF magic", + [4] = "section size not whole words", + [5] = "target outside RAM", + [6] = "target overlaps loader page or COFF buffer", + [7] = "relocation records present (unsupported)", + [8] = "target not in a free-list block", + [9] = "no entry point", + } + return t[v] or ("error " .. tostring(v)) +end + +local function result_dump() + local st = r32(RESULT + 0x000) + print(string.format( + "LOADER: result status=%d (%s) entry=%s text=%s/%d bits data=%s/%d bits bss=%s/%d bits relocs=%d pcb=%s nscns=%d len=%d", + st, status_name(st), hex(r32(RESULT + 0x020)), + hex(r32(RESULT + 0x040)), r32(RESULT + 0x060), + hex(r32(RESULT + 0x080)), r32(RESULT + 0x0A0), + hex(r32(RESULT + 0x0C0)), r32(RESULT + 0x0E0), + r32(RESULT + 0x100), hex(r32(RESULT + 0x120)), + r32(RESULT + 0x140), r32(RESULT + 0x160))) +end + +local function inject() + -- 1. loader itself + local n, bad = 0, 0 + poke_bytes(LOADER_BASE, loader) + n, bad = verify_bytes(LOADER_BASE, loader) + print(string.format("LOADER: loader poked %d words, readback %d bad at %s", + n, bad, hex(LOADER_BASE))) + + -- 2. COFF image + poke_bytes(BUF, coff) + n, bad = verify_bytes(BUF, coff) + print(string.format("LOADER: COFF poked %d words, readback %d bad at %s", + n, bad, hex(BUF))) + + -- 3. pre-fill every .bss section with A5A5, so a loaded zero is meaningful + for _, s in ipairs(app.sections) do + if s.flags & 0x80 ~= 0 then + for i = 0, math.floor(s.size / 16) - 1 do + prog:write_u16(s.paddr + i * 16, 0xA5A5) + end + print(string.format("LOADER: pre-filled .bss %s..%s with A5A5", + hex(s.paddr), hex(s.paddr + s.size))) + end + end + + -- 4. descriptor: magic "LD" "R1", buf, len, mode=0 (spin) + w32(DESC + 0x00, 0) + prog:write_u16(DESC + 0x00, 0x4C44) + prog:write_u16(DESC + 0x10, 0x5231) + w32(DESC + 0x20, BUF) + w32(DESC + 0x40, #coff) + w32(DESC + 0x60, MODE) + print(string.format("LOADER: descriptor @%s magic=%04X/%04X buf=%s len=%d mode=%d", + hex(DESC), prog:read_u16(DESC + 0x00), prog:read_u16(DESC + 0x10), + hex(r32(DESC + 0x20)), r32(DESC + 0x40), r32(DESC + 0x60))) + + -- 5. return stub. mode 0 ignores it (the loader jumps to ORIG_PC + -- itself); mode 1 returns here, and the stub resumes the interrupted + -- PC so the caller yields and the scheduler can run the child. + local orig_pc = cpu.state['PC'].value + if MODE == 1 then + prog:write_u16(RETURN_STUB, 0xC080) -- jauc + prog:write_u16(RETURN_STUB + 0x10, orig_pc & 0xFFFF) + prog:write_u16(RETURN_STUB + 0x20, math.floor(orig_pc / 0x10000) & 0xFFFF) + else + prog:write_u16(RETURN_STUB, 0xC0FF) -- jruc self + end + + -- 6. enter the loader. A8=0 selects the fixed descriptor; SP points at + -- the return slot so mode=1 would return to the spin stub. + -- Leave SP alone for mode 0: the loader pushes onto the current + -- process stack, then restores it and resumes at ORIG_PC. (mode 1 + -- would use the return stub.) + w32(0x01882A20, orig_pc) -- LD_ORIG_PC + print(string.format("LOADER: will resume at %s after create_process", hex(orig_pc))) + cpu.state['A8'].value = A8DESC and DESC or 0 + if MODE == 1 then + -- Simulate a CALLA from the current process: push the return + -- address on the *firmware* stack (a real transport would do this). + local sp = cpu.state['SP'].value - 0x20 + w32(sp, RETURN_STUB) + cpu.state['SP'].value = sp + end + cpu.state['PC'].value = LOADER_BASE + injected = true + print(string.format("LOADER: frame %d: entered loader at %s (caller SP=%s)", + frames, hex(LOADER_BASE), hex(cpu.state['SP'].value))) +end + +local function inspect() + result_dump() + + -- Verify the placement from RAM: .text/.data must equal the file bytes. + for _, s in ipairs(app.sections) do + if s.scnptr ~= 0 and s.size > 0 then + local nb = math.floor(s.size / 8) + local bytes = coff:sub(s.scnptr + 1, s.scnptr + nb) + local _, bad = verify_bytes(s.paddr, bytes) + print(string.format("LOADER: placed %s at %s: %d bytes, %d differing", + s.name, hex(s.paddr), nb, bad)) + end + end + + local base = COFF:match("([^/]+)$") or COFF + if base:find("marker") then + print(string.format("LOADER: marker app: start=%s data=%s data2=%s bss=%s count=%d", + hex(r32(0x018E0000)), hex(r32(0x018E0020)), hex(r32(0x018E0040)), + hex(r32(0x018E0060)), r32(0x018E0080))) + elseif base:find("xhello") then + print(string.format("LOADER: xhello: stage=%d strlen=%d display=%s root=%s window=%s sync=%s", + r32(0x01890140), r32(0x01890160), hex(r32(0x01890100)), + hex(r32(0x01890110)), hex(r32(0x01890120)), hex(r32(0x01890130)))) + end + print(string.format("LOADER: PC at inspection = %s", hex(cpu.state['PC'].value))) +end + +emu.register_frame_done(function() + frames = frames + 1 + if frames == AT and not injected then + inject() + if TRACE then dbg:command("trace /tmp/opencode/ld1.tr") end + end + if injected and TRACE and frames == AT + 1 then + dbg:command("trace off") + print("LOADER: trace written to /tmp/opencode/ld.tr") + end + if injected and not done then + if TRACE and frames <= AT + 40 then + print(string.format("LOADER: trace f=%d PC=%s SP=%s A14=%s status=%s entry=%s", + frames, hex(cpu.state['PC'].value), hex(cpu.state['SP'].value), + hex(cpu.state['A14'].value), hex(r32(RESULT + 0x000)), + hex(r32(RESULT + 0x020)))) + end + local st = r32(RESULT + 0x000) + -- status 0 means OK; nonzero is final too once parsing has begun + if st ~= 0xFFFFFFFF and not done then + -- The loader writes 0 on success. A nonzero status is final + -- only after it has been written once (it is 0 from power-on, + -- so require the loader to have run: PC no longer at entry or + -- the app counter moved). + end + if VERBOSE and frames % 25 == 0 then + result_dump() + end + if base_contains_marker then + local c = r32(0x018E0080) + if c ~= last_count then + if last_count ~= -1 then + print(string.format("LOADER: marker counter %d -> %d (child running)", + last_count, c)) + end + last_count = c + end + end + end + if frames >= ENDF then + if injected then + done = true + inspect() + if SNAP then + local screen = nil + for _, s in pairs(machine.screens) do screen = s break end + if screen then + local out = OUT .. "/loader_frame" .. frames .. ".png" + local ok, err = pcall(function() return screen:snapshot(out) end) + print(string.format("LOADER: snapshot %s ok=%s %s", + out, tostring(ok), tostring(err or ""))) + end + end + end + machine:exit() + end +end, "vt1200-loader") + +-- (Lua has no forward use of a name defined later; set a small flag here.) +base_contains_marker = (COFF:match("([^/]+)$") or ""):find("marker") ~= nil + +dbg.visible_cpu = cpu +dbg.execution_state = 'run' diff --git a/dwt/loader/marker.asm b/dwt/loader/marker.asm new file mode 100644 index 0000000..e610d74 --- /dev/null +++ b/dwt/loader/marker.asm @@ -0,0 +1,66 @@ +; marker.asm -- loader acceptance app. +; +; Built by dwt/loader/build.sh with three separate section origins: +; .text 018C0000h (byte 0x318000) +; .data 018D0000h (byte 0x31A000) +; .bss 018D8000h (byte 0x31B000) +; +; It is the *COFF image* that the loader must place; it is never poked as a +; raw blob. The loader parses the COFF, copies .text and .data, zeroes .bss, +; and calls create_process() with the image's entry point. +; +; Evidence cells (32-bit, bit addresses; outside every app section): +; 018E0000 _start address at run time (must be 018C0000) +; 018E0020 d_magic read from .data (must be DEADBEEF) +; 018E0040 d_two read from .data (must be 12345678) +; 018E0060 b_zero read from .bss (must be 0; the harness pre-fills A5) +; 018E0080 b_counter, incremented forever (proves the child is scheduled) +; +; The harness pre-fills the whole .bss range with A5A5A5A5 before the load, so +; a zero at 018E0060 is the loader's zero-fill, not a fresh-RAM accident. + + .def _start + .text +_start: + setf 16, 0, 0 + setf 32, 0, 1 + + ; Run-time address of this code: position dependence, made visible. + movi _start, a2, L + move a2, @018E0000h, 1 + + ; Initialised .data must have been copied by the loader. + move @d_magic, a0, 1 + move a0, @018E0020h, 1 + + move @d_two, a1, 1 + move a1, @018E0040h, 1 + + ; .bss must have been zeroed by the loader (harness pre-fills A5). + move @b_zero, a3, 1 + move a3, @018E0060h, 1 + + ; A live counter: a created process, not a one-shot jump. + sub a4, a4 +loop: + addi 1, a4 + move a4, @b_counter, 1 + move a4, @018E0080h, 1 + movi 2000h, a5, L +delay: + dsjs a5, delay + jruc loop + + ; Kept to leave a call in the image (and a relocation in the .obj). + .global sub +sub: + rets + + .data +d_magic: .long 0DEADBEEFh +d_two: .long 012345678h + + .bss b_zero, 32, 1 + .bss b_counter, 32, 1 + + .end diff --git a/dwt/loader/probe_os.lua b/dwt/loader/probe_os.lua new file mode 100644 index 0000000..ab81b10 --- /dev/null +++ b/dwt/loader/probe_os.lua @@ -0,0 +1,85 @@ +-- probe_os.lua -- private probe for the COFF loader work. +-- +-- Prints the OS-phase heap free list (global list only, as HEAP.md advises), +-- and tests whether the CPU registers can be written from a frame hook while +-- the machine is running (the debugger is attached with -debug -debugger none). +-- +-- VT1200_SKIP_LEVEL1=1 vt1200 ... -autoboot_script dwt/loader/probe_os.lua +-- +-- Env: PROBE_AT (frame to sample, default 2500), PROBE_END (exit frame, 2520) + +local function hex(v, w) return string.format("%0" .. (w or 8) .. "X", v) end + +local machine = manager.machine +local cpu = machine.devices[':maincpu'] +local dbg = machine.debugger +local prog = cpu.spaces['program'] + +if not dbg then + print("PROBE: no debugger") + machine:exit() + return +end + +local AT = tonumber(os.getenv("PROBE_AT") or "2500") +local ENDF = tonumber(os.getenv("PROBE_END") or "2520") +local frames = 0 +local tested = false + +local function u32(a) + local ok, v = pcall(function() return prog:read_u32(a) end) + if ok then return v end + return 0xdeadbeef +end + +local function heap() + print(string.format("PROBE: free counter @1E8BA0 = %d bits (%d bytes)", + u32(0x1E8BA0), math.floor(u32(0x1E8BA0) / 8))) + local head = u32(0x1E8AC0) + print(string.format("PROBE: global free-list head @1E8AC0 = %s", hex(head))) + local b = head + for i = 0, 15 do + if b == 0 then break end + local size = u32(b) + local nextp = u32(b + 0x20) + print(string.format("PROBE: block[%d] hdr=%s size=%d bits (%d B) user=%s..%s next=%s", + i, hex(b), size, math.floor(size / 8), hex(b + 0x40), + hex(b + size), hex(nextp))) + if nextp == head then break end + b = nextp + end +end + +emu.register_frame_done(function() + frames = frames + 1 + if frames == AT then + print(string.format("PROBE: frame %d PC=%s SP=%s A8=%s A14=%s", + frames, hex(cpu.state['PC'].value), hex(cpu.state['SP'].value), + hex(cpu.state['A8'].value), hex(cpu.state['A14'].value))) + heap() + -- Scratch return stub test: write jruc self at 01880F00, return address + -- at 01880F10, then set SP/PC from the frame hook. + prog:write_u16(0x01880F00, 0xC0FF) -- jruc 01880F00 (short, -1 word) + prog:write_u32(0x01880F10, 0x01880F00) + prog:write_u32(0x01880F20, 0xDEADBEEF) + print(string.format("PROBE: spin word readback=%04X ret=%s", + prog:read_u16(0x01880F00), hex(prog:read_u32(0x01880F10)))) + cpu.state['SP'].value = 0x01880F10 + cpu.state['A8'].value = 0x01880E00 + cpu.state['PC'].value = 0x01880F00 + tested = true + end + if tested and frames == AT + 2 then + print(string.format("PROBE: after 2 frames PC=%s SP=%s A8=%s", + hex(cpu.state['PC'].value), hex(cpu.state['SP'].value), + hex(cpu.state['A8'].value))) + end + if frames >= ENDF then + print(string.format("PROBE: marker 01880F20=%s (should be DEADBEEF, untouched)", + hex(u32(0x01880F20)))) + machine:exit() + end +end, "vt1200-loader-probe") + +dbg.visible_cpu = cpu +dbg.execution_state = 'run' diff --git a/dwt/loader/rel_a.asm b/dwt/loader/rel_a.asm new file mode 100644 index 0000000..e9d088b --- /dev/null +++ b/dwt/loader/rel_a.asm @@ -0,0 +1,12 @@ +; rel_a.asm -- reldemo caller: has a 32-bit data reference and an absolute call +; to a symbol defined in rel_b.asm. + .def _start + .ref ext_func + .ref ext_data + .text +_start: + movi ext_data, a0, L ; 32-bit direct reference (R-RELLONG) + move a0, @018F0000h, 1 ; fixed marker (not a relocation) + calla ext_func ; 32-bit absolute call + rets + .end diff --git a/dwt/loader/rel_b.asm b/dwt/loader/rel_b.asm new file mode 100644 index 0000000..f00f8eb --- /dev/null +++ b/dwt/loader/rel_b.asm @@ -0,0 +1,10 @@ +; rel_b.asm -- reldemo callee/data: ext_func and ext_data are defined here. + .def ext_func + .def ext_data + .text +ext_func: + rets + .data +ext_data: + .long 0CAFEBABEh + .end diff --git a/dwt/loader/reldemo.sh b/dwt/loader/reldemo.sh new file mode 100755 index 0000000..0442cbe --- /dev/null +++ b/dwt/loader/reldemo.sh @@ -0,0 +1,79 @@ +#!/bin/sh +# reldemo.sh -- prove what GSPLNK does with relocations, and that the linked +# image is position-dependent. +# +# 1. assemble two objects that reference each other's symbols (ext_func, +# ext_data); the .obj files carry relocation records. +# 2. link them with `-a` (the normal app flow): the output COFF must have +# nreloc = 0 -- the image is fully resolved. +# 3. link the *same* objects at two different origin sets and diff the .text +# bytes: they differ, so the image is position-dependent. +# 4. link without `-a`: report whether GSPLNK emits a relocatable COFF. +# +# All scratch/output stays under dwt/loader/reldemo/. +set -eu + +ROOT=$(cd "$(dirname "$0")/../.." && pwd) +DIR=$(cd "$(dirname "$0")" && pwd) +WORK="$DIR/work" +OUT="$DIR/build" +IMG="$ROOT/tools/tms34010-sdk/1987-05 TMS34010 SDK/1987-11-06 TMS34010 Simulator-C Tools-ASM Tools.img" +DOSBOX_CONF="$ROOT/harness/sdk-scratch/dosbox-bench.conf" + +mkdir -p "$WORK" "$OUT" +if [ ! -x "$WORK/GSPA.EXE" ]; then + mcopy -s -n -i "$IMG" "::/A2/*" "$WORK/" + mcopy -s -n -i "$IMG" "::/C2/*" "$WORK/" +fi + +cp "$DIR/rel_a.asm" "$WORK/RELA.ASM" +cp "$DIR/rel_b.asm" "$WORK/RELB.ASM" + +run_dos() { + SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \ + timeout 180 dosbox -conf "$DOSBOX_CONF" -noprimaryconf -nolocalconf \ + -c "mount c $WORK" -c "c:" "$@" -c "exit" -exit >/dev/null 2>&1 +} + +printf '%s\n' \ + '-a' '-o RELABS.out' '-m RELABS.map' '-e _start' \ + 'RELA.obj RELB.obj' \ + 'SECTIONS' '{' \ + ' .text 018C0000h: {}' \ + ' .data 018D0000h: {}' \ + ' .bss 018D8000h: {}' \ + '}' > "$WORK/RELABS.CMD" +printf '%s\n' \ + '-a' '-o RELABS2.out' '-m RELABS2.map' '-e _start' \ + 'RELA.obj RELB.obj' \ + 'SECTIONS' '{' \ + ' .text 018E0000h: {}' \ + ' .data 018F0000h: {}' \ + ' .bss 018F8000h: {}' \ + '}' > "$WORK/RELABS2.CMD" +printf '%s\n' \ + '-o RELOC.out' '-m RELOC.map' '-e _start' \ + 'RELA.obj RELB.obj' > "$WORK/RELOC.CMD" +sed -i 's/$/\r/' "$WORK"/*.CMD + +rm -f "$WORK"/REL*.OBJ "$WORK"/REL*.OUT "$WORK"/REL*.LST \ + "$WORK"/REL*.MAP "$WORK"/REL*.HI "$WORK"/REL*.LO +run_dos \ + -c "gspa rela rela.obj -q" \ + -c "gspa relb relb.obj -q" \ + -c "gsplnk relabs.cmd" \ + -c "gsplnk relabs2.cmd" +# A missing relocatable output is itself a result; do not fail the script. +run_dos -c "gsplnk reloc.cmd" || true + +for f in "$WORK"/REL*.OUT; do + [ -f "$f" ] && cp "$f" "$OUT/$(basename "$f" | tr 'A-Z' 'a-z')" +done +for f in "$WORK"/REL*.OBJ; do + [ -f "$f" ] && cp "$f" "$OUT/$(basename "$f" | tr 'A-Z' 'a-z')" +done + +python3 "$DIR/coff.py" summary "$OUT/rela.obj" "$OUT/relb.obj" \ + "$OUT/relabs.out" "$OUT/relabs2.out" "$OUT/reloc.out" +echo +python3 "$DIR/coff.py" raw "$OUT/relabs.out" "$OUT/relabs2.out" diff --git a/dwt/notes/LOADER.md b/dwt/notes/LOADER.md new file mode 100644 index 0000000..ddecf52 --- /dev/null +++ b/dwt/notes/LOADER.md @@ -0,0 +1,479 @@ +# 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 `) 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 | diff --git a/dwt/notes/LOADER_CROSS.md b/dwt/notes/LOADER_CROSS.md new file mode 100644 index 0000000..4199277 --- /dev/null +++ b/dwt/notes/LOADER_CROSS.md @@ -0,0 +1,130 @@ +# LOADER_CROSS — the boundary between the TFTP transport and the COFF loader + +Append-only. Each entry starts with a timestamp header. + +--- + +## 2026-10-18 — COFF loader interface (loader half complete) + +The loader half is implemented and tested with debugger-poked images. This entry +is the contract the transport half must satisfy; it is written by the loader +owner for the transport owner. + +### What the transport hands over + +1. **The complete linked COFF image in RAM.** Not the `.obj`; the GSPLNK `-a` + output (`*.out`), byte-for-byte, 510–774 bytes for the current test apps. + The transport owns the receive buffer and passes its **bit address** and + **byte length**. The loader does not allocate or receive the image itself. +2. **The loader resident at bit `0x01880000`.** The raw + `dwt/loader/build/loader.bin` (1220 bytes, `.text` size `0x2620` bits) is + position-dependent; it must be placed exactly there. A transport that + includes the loader in its own payload must copy it to that address first + (and must not overwrite it). +3. **A descriptor** at bit `0x01882C00` (or at an address passed in `A8`). + Offsets are **bytes**; in the loader's assembly they are bits (+0, +0x10, + +0x20, +0x40, +0x60): + + ``` + DESC + 0x00 u16 0x4C44 "LD" ) both magic words 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 + ``` + + `mode = 1` is the transport mode. `mode = 0` is the debugger mode: the loader + restores the interrupted context and jumps to the PC the harness saved; a + transport must not use it. + +4. **A call**, using the firmware convention the loader itself uses for + `create_process`: + + ``` + move a14, -*sp, 1 + movi DESC_ADDR, a14, L ; no descriptor args to push, but keep A14 + calla 01880000h ; A8 = 0 (fixed descriptor) or DESC_ADDR + ``` + + `A8` may be 0 (use the fixed descriptor at `0x01882C00`) or the descriptor's + bit address. Any other value falls back to the fixed descriptor if it does + not carry the magic. + +### What the loader returns + +* `A8 = RESULT_STATUS`: **0 = success**, otherwise an error code (table below). +* The **result block** at bit `0x01882D00` (32-bit cells at +0x20 intervals): + + | offset | meaning | + |---:|---| + | `+0x000` | status (same value as `A8`) | + | `+0x020` | entry passed to `create_process` (bits) | + | `+0x040` | `.text` paddr / `+0x060` 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 `0x01880000` | + +* On success the app is already a **first-class process** (a PCB exists and the + child is scheduled when the caller yields). The loader does not wait for the + app and does not return its exit status. + +### Address policy: the loader picks, the transport does not + +The transport passes **buffer and length only**. The load address of every +section comes from the COFF section headers (bit addresses); the entry comes +from the optional header (`entry`, else `textstart`). There is no +"load-address" argument. The linked image is position-dependent (GSPLNK +resolves all references and emits `nreloc = 0`), so the transport must not move +the sections after the fact and must not send a `.obj`. + +The loader checks, before copying: + +* section size is a whole number of 16-bit words; +* the section bytes are inside `[buf, buf+len)`; +* `0x100 <= paddr` and `paddr + size <= 0x2000000` bits (4 MiB RAM); +* no overlap with `[0x01880000,0x01883200)` (loader page) or the COFF buffer; +* the range lies inside one block of the **global** free list (head bit + `0x1E8AC0`; block `{u32 size_bits; …; u32 next@+0x20}`, user at `+0x40`). + +The free-list check is a **check, not a reservation**. The transport should +choose app link addresses away from the allocator's growth path (the large free +block starts at byte `0x12191A` and grows upward; the current apps live at byte +`0x318000`+). The transport's own code, stack and receive buffer must also stay +out of the target sections; the loader cannot see them. + +### Status codes + +| code | meaning | +|---:|---| +| 0 | success, child created | +| 1 | bad descriptor (magic) | +| 2 | truncated COFF / section headers past `len` | +| 3 | bad COFF magic (not `0x0090`) | +| 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 (unsupported; the input is probably a `.obj`) | +| 8 | target not inside a global free-list block | +| 9 | no entry point | +| 10 | `create_process` returned 0 | + +### Currently proven + +* Debugger-poked `marker.coff` (`buf=0x01900000`, `len=510`, mode 0 and mode 1, + `A8=0` and `A8=DESC`): status 0, PCB `0x003FFBD0`, markers + `start=018C0000 data=DEADBEEF bss=0`, counter live. +* Debugger-poked `xhello.coff` (`len=774`, mode 0): status 0, entry + `0x01840000`, `stage=6`, window visible in `loader_frame2650.png`. +* `rela.obj` (`len=342`): status 7, `relocs=2`, nothing copied. + +### Files the transport needs + +* `dwt/loader/build/loader.bin` — raw loader to place at `0x01880000`. +* `dwt/loader/coff.py` — the host-side reference parser; the loader's field + offsets match it. +* `dwt/notes/LOADER.md` — the full COFF layout, the position-dependence + evidence and the loader algorithm.