Files
SDD 1608a68cdb hello-world: the TMS34010 SDK assembles code that runs on the emulated VT1200
An 82-byte payload written in GSPA assembly, linked at bit 01800000h and
loaded into main RAM by harness/mame/hello.lua, executes on the CPU: it
writes DEAD/BEEF and a 1024-iteration counter into RAM and fills a 256x64
block in video RAM, which appears in the frame dump at x 256..511,
y 136..199.  Entry is a debugger breakpoint on the level-0 menu idle loop,
not a delivery mechanism.  Includes the build script, a COFF .text
extractor (gsprom's hex addresses wrap at 64 KB), the run log, RAM excerpt,
frame crops including a no-load control, and HELLO_WORLD_REPORT.md.
2026-10-18 01:20:38 +03:00

54 lines
2.1 KiB
Python
Executable File

#!/usr/bin/env python3
"""Extract the first loadable section of a TI GSP COFF file as raw bytes.
GSPLNK writes a COFF whose section header carries the FULL 32-bit bit
address in `paddr`, so placement does not depend on gsprom's hex output
(whose addresses wrap at 64 KB -- documented in reference/tms34010-sdk.md).
Usage: cofextract.py hello.out payload.bin
Prints: origin=<bit address> size=<bytes> file_offset=<hex>
"""
import struct
import sys
# TI TMS34010 COFF: 20-byte file header, optional header size in the header.
# Section header: name[8] paddr(4) vaddr(4) size(4) scnptr(4) relptr(4)
# lnnoptr(4) nreloc(2) nlnno(2) flags(4) = 40 bytes
def main(out_path, bin_path):
d = open(out_path, "rb").read()
if len(d) < 20:
sys.exit("cofextract: file too short")
magic, nscns, _timdat, _symptr, _nsyms, opthdr, _flags = struct.unpack_from(
"<HHIIIHH", d, 0)
if magic != 0x0090:
sys.exit("cofextract: unexpected COFF magic %#06x" % magic)
base = 20 + opthdr
secs = []
for i in range(nscns):
off = base + i * 40
name = d[off:off + 8].rstrip(b"\0").decode("latin1")
paddr, vaddr, size, scnptr, _relptr, _lnnoptr, _nreloc, _nlnno, flags = \
struct.unpack_from("<IIIIIIHHI", d, off + 8)
secs.append((name, paddr, size, scnptr, flags))
# .text is the only initialized section GSPLNK emits for our payload.
text = [s for s in secs if s[0] == ".text"]
if not text:
sys.exit("cofextract: no .text section")
name, paddr, size, scnptr, _flags = text[0]
# size and paddr are bit units; bytes = size >> 3.
nbytes = size >> 3
payload = d[scnptr:scnptr + nbytes]
if len(payload) != nbytes:
sys.exit("cofextract: truncated section (%d of %d bytes)"
% (len(payload), nbytes))
open(bin_path, "wb").write(payload)
print("origin=%08X size=%d file_offset=%#x md5=%s" % (
paddr, nbytes, scnptr,
__import__("hashlib").md5(payload).hexdigest()))
if __name__ == "__main__":
if len(sys.argv) != 3:
sys.exit("usage: cofextract.py hello.out payload.bin")
main(sys.argv[1], sys.argv[2])