Files
arti 6338095683 tools: add as34/ld34 TMS34010 binutils, ported to 64-bit Linux
Andrew Morton's Unix-hosted assembler/linker for the TMS34010, fetched from the gsptools archive and patched to build on x86-64. The K&R sources silently truncate pointers there: implicit int parameter declarations and fixed-argument printf wrappers. The patch fixes those and the flex 2.6 yy_init mismatch.

hello.as34.g is harness/hello-world/hello.asm in as34 syntax. as34/ld34 reproduce the GSPA-built payload byte-for-byte (82 bytes, md5 db91788991ebba476b55a11127a36475), and the MAME hello.lua run reports the same markers, the same 1024-word VRAM block and a byte-identical frame.

build.sh downloads the upstream tarball at build time (sha256 pinned); the tree is not vendored.
2026-10-22 14:19:14 +03:00

105 lines
4.4 KiB
Markdown

# as34 / ld34 — Morton's Unix-hosted TMS34010 binutils
`as34` (assembler) and `ld34` (linker) are Andrew Morton's 1990s TMS34010
tools, shipped with his GCC 2.5.x port. They are **not TI tools** and not
related to `GSPA`/`GSPLNK`; they run natively on Linux, with no DOSBox.
`build.sh` fetches the original `gsp.tar.gz` from the Wayback Machine,
applies `as34-port.patch`, and builds three programs:
| tool | what it does |
|---|---|
| `as34` | assembles `.g` source to a `.go` object (`LINKMAGIC 0x3abf782e`) |
| `ld34` | links `.go` objects to `g.out` (same linkhdr container), with a map |
| `dis34` | disassembles `g.out` (kept for inspection; the repo's `tools/smart34010/dis34` is a better independent decoder) |
The compiled `.text` is extracted from `g.out` by `extract.py`. `g.out` is
**not COFF** — `ld34/coff.c` is dead code that is neither compiled nor called.
The live writer is `ld34/header.c`, which emits the same packed `linkhdr`
format that `as34` produces.
## Build
```sh
tools/as34/build.sh
```
Everything is fetched and built under `tools/as34/build/` (git-ignored;
override with `AS34_BUILD_DIR`). The tools end up at:
```
tools/as34/build/src/gsp/binutils/as34/as34
tools/as34/build/src/gsp/binutils/ld34/ld34
tools/as34/build/src/gsp/binutils/ld34/dis34
```
The tarball is 2.1 MB and is an **uncompressed tar with a `.tar.gz` name** —
that is how the archive is stored. Its SHA-256 is pinned in `build.sh`.
## Test (byte comparison, then MAME)
```sh
tools/as34/test.sh
REPO=$PWD MAME=/home/arti/code/k-space/mame0289/vt1200 tools/as34/test.sh
```
`hello.as34.g` is `harness/hello-world/hello.asm` ported to `as34` syntax.
The result is **byte-identical** to the GSPA-built
`harness/hello-world/payload.bin` (82 bytes, md5
`db91788991ebba476b55a11127a36475`), and the MAME run reports the same
markers, the same 1024-word VRAM block and the same frame as the original
payload.
### Syntax differences from GSPA
| | GSPA | as34 |
|---|---|---|
| hex | `0DEADh` | `>DEAD` (also `0xdead`) |
| immediate width | `movi x, a0, L` | no `L`/`W` suffix; `movi` is always the 32-bit form |
| 16-bit immediate add | `addi` | `addiw` — as34's `addi` is the **32-bit** immediate form |
| immediate order | same (`op src, dst`) | same |
Everything else — labels, `@absolute`, `*a2+`, `dsjs`, bit addresses, field
specifiers — is unchanged. `dsjs` displacement encoding matches GSPA (checked
against `tools/smart34010/dis34`).
## What the port patch changes
The code is K&R C written for 32-bit SunOS. On x86-64 the original build
produces truncated pointers, so the patch is not cosmetic:
* **Pointer parameters declared implicitly as `int`** (`Xwrite`, `openfiles`,
`emitslcode`/`emitnslcode`/`emitswcode`/`emitnswcode`). On a 32-bit host
the implicit `int` happened to be pointer-sized; on x86-64 it truncates.
* **K&R printf wrappers** (`amprintf`, `ameprintf`, `amfprintf`, `eprintf`)
declared `p1…p6` as `int`, truncating `%s` arguments. Rewritten as real
variadic functions with `va_list`.
* **`imalloc`** likewise made variadic, and declared in `proto.h` (included
with `-include` for the assembler's translation units).
* **flex 2.6**: `yyGetReady()` used to set `yy_init = 1`; with the modern
skeleton that leaves `yy_c_buf_p` NULL and segfaults. It is now a no-op —
`YY_INPUT`/`am_input` still supplies the line buffer.
* `abort(1)` → `abort()`; `hash()` uses unsigned arithmetic; two
`exit(eprintf(...))` calls split.
## Upstream quirks worth knowing
* **`-e` takes its argument attached**: `-e_start`. The space form
(`-e _start`) is broken in the original parser: it sets the entry name to
the empty string and treats `_start` as an object file.
* **The container format depends on the host `long` size.** `link.h` stores
addresses and sizes as `long`; this build writes 8-byte fields, so the
header is 156 bytes rather than the historical 80. `as34`, `ld34` and
`extract.py` all agree, but these files are not interchangeable with
32-bit-host `g.out` files. Making the format stable would mean changing
`long` to `int32_t` in `link.h`.
* **`ld34` runs `as34` from `PATH`** when generating C++ ctor/dtor tables.
`test.sh` passes `-c` to skip that.
* `as34` is **not** GSPA-compatible at the token level (see the table above).
## Licence
The upstream tarball contains GPLv2-era GNU binutils code plus Morton's own
sources. It is downloaded at build time, not vendored here; the patch is the
only derived file committed.