mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
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.
This commit is contained in:
1 parent
f3d26ad687
commit
6338095683
9 files changed
+713
No files matched your search
@@ -0,0 +1 @@
|
||||
build/
|
||||
@@ -0,0 +1,104 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,353 @@
|
||||
--- a/binutils/as34/as.l
|
||||
+++ b/binutils/as34/as.l
|
||||
@@ -268,7 +268,9 @@
|
||||
|
||||
yyGetReady()
|
||||
{
|
||||
-#ifdef BISON
|
||||
- yy_init = 1;
|
||||
-#endif
|
||||
+ /* The original flex 2.5 code set yy_init = 1 here, because the old
|
||||
+ * skeleton created its input buffer some other way. With flex 2.6,
|
||||
+ * setting yy_init without a buffer leaves yy_c_buf_p NULL. Leave the
|
||||
+ * flag alone: yylex initialises normally on first use, and YY_INPUT
|
||||
+ * (am_input) supplies bytes from linebuf, not from yyin. */
|
||||
}
|
||||
--- a/binutils/as34/as.y
|
||||
+++ b/binutils/as34/as.y
|
||||
@@ -96,12 +96,12 @@
|
||||
return 0;
|
||||
}
|
||||
|
||||
-emitslcode(pexpr)
|
||||
+emitslcode(expr *pexpr)
|
||||
{
|
||||
return doemitl(pexpr, 0);
|
||||
}
|
||||
|
||||
-emitnslcode(pexpr)
|
||||
+emitnslcode(expr *pexpr)
|
||||
{
|
||||
return doemitl(pexpr, 1);
|
||||
}
|
||||
@@ -120,11 +120,11 @@
|
||||
emitsl(mode ? ~(pexpr->abspart) : pexpr->abspart);
|
||||
}
|
||||
|
||||
-emitswcode(pexpr)
|
||||
+emitswcode(expr *pexpr)
|
||||
{
|
||||
return doemitw(pexpr, 0);
|
||||
}
|
||||
-emitnswcode(pexpr)
|
||||
+emitnswcode(expr *pexpr)
|
||||
{
|
||||
return doemitw(pexpr, 1);
|
||||
}
|
||||
--- a/binutils/as34/main.c
|
||||
+++ b/binutils/as34/main.c
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <stdio.h>
|
||||
+#include <stdarg.h>
|
||||
#include "link.h"
|
||||
#include "branches.h"
|
||||
|
||||
@@ -46,7 +47,7 @@
|
||||
#define QS_INSQUOTE 1
|
||||
#define QS_INDQUOTE 2
|
||||
|
||||
-char *imalloc(), *mkstr(), *malloc();
|
||||
+#include <stdlib.h>
|
||||
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
@@ -284,7 +285,7 @@
|
||||
for (inci = 0; inci < ninclpaths; inci++)
|
||||
{
|
||||
path = imalloc(strlen(inclpaths[inci]) +
|
||||
- strlen(copyfile) + 2);
|
||||
+ strlen(copyfile) + 2, "include path");
|
||||
sprintf(path, "%s/%s", inclpaths[inci], copyfile);
|
||||
if ((inputfiles[incldepth + 1] = fopen(path, "r"))
|
||||
== NULL)
|
||||
@@ -388,14 +389,15 @@
|
||||
* Error handler
|
||||
*/
|
||||
|
||||
-eprintf(str, p1, p2, p3, p4)
|
||||
-char *str;
|
||||
-int p1, p2, p3, p4;
|
||||
+void eprintf(char *str, ...)
|
||||
{
|
||||
char buf[300];
|
||||
+ va_list ap;
|
||||
|
||||
nerrors++;
|
||||
- sprintf(buf, str, p1, p2, p3, p4);
|
||||
+ va_start(ap, str);
|
||||
+ vsnprintf(buf, sizeof buf, str, ap);
|
||||
+ va_end(ap);
|
||||
ameprintf("%s: %s:%d: %s\n", ourname, filenames[incldepth], linenos[incldepth], buf);
|
||||
if (listing && pass2)
|
||||
{
|
||||
@@ -469,15 +471,18 @@
|
||||
}
|
||||
|
||||
char *
|
||||
-imalloc(nbytes, str, p1, p2)
|
||||
-char *str;
|
||||
+imalloc(int nbytes, char *str, ...)
|
||||
{
|
||||
char *ret;
|
||||
+ va_list ap;
|
||||
+
|
||||
ret = (char *)malloc(nbytes);
|
||||
if (ret == 0)
|
||||
{
|
||||
char buf[300];
|
||||
- sprintf(buf, str, p1, p2);
|
||||
+ va_start(ap, str);
|
||||
+ vsnprintf(buf, sizeof buf, str, ap);
|
||||
+ va_end(ap);
|
||||
eprintf("Cannot allocate memory for %s\n", buf);
|
||||
exit(-1);
|
||||
}
|
||||
@@ -503,53 +508,23 @@
|
||||
VERSION >> 4, VERSION & 15);
|
||||
}
|
||||
|
||||
-amprintf(str, p1, p2, p3, p4, p5, p6)
|
||||
-char *str;
|
||||
+void amprintf(char *str, ...)
|
||||
{
|
||||
-#ifdef A1616
|
||||
- char buf[1000];
|
||||
- register char *cp;
|
||||
+ va_list ap;
|
||||
|
||||
- sprintf(buf, str, p1, p2, p3, p4, p5, p6);
|
||||
- cp = buf;
|
||||
- while (*cp)
|
||||
- {
|
||||
- if (*cp != '\r')
|
||||
- {
|
||||
- if (*cp == '\n')
|
||||
- putchar('\r');
|
||||
- putchar(*cp);
|
||||
- }
|
||||
- cp++;
|
||||
- }
|
||||
-#else
|
||||
- printf(str, p1, p2, p3, p4, p5, p6);
|
||||
-#endif
|
||||
+ va_start(ap, str);
|
||||
+ vprintf(str, ap);
|
||||
+ va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
-ameprintf(str, p1, p2, p3, p4, p5, p6)
|
||||
-char *str;
|
||||
+void ameprintf(char *str, ...)
|
||||
{
|
||||
-#ifdef A1616
|
||||
- char buf[1000];
|
||||
- register char *cp;
|
||||
+ va_list ap;
|
||||
|
||||
- sprintf(buf, str, p1, p2, p3, p4, p5, p6);
|
||||
- cp = buf;
|
||||
- while (*cp)
|
||||
- {
|
||||
- if (*cp != '\r')
|
||||
- {
|
||||
- if (*cp == '\n')
|
||||
- putc('\r', stderr);
|
||||
- putc(*cp, stderr);
|
||||
- }
|
||||
- cp++;
|
||||
- }
|
||||
-#else
|
||||
- fprintf(stderr, str, p1, p2, p3, p4, p5, p6);
|
||||
-#endif
|
||||
+ va_start(ap, str);
|
||||
+ vfprintf(stderr, str, ap);
|
||||
+ va_end(ap);
|
||||
}
|
||||
|
||||
#ifdef A1616
|
||||
--- a/binutils/as34/reloc.c
|
||||
+++ b/binutils/as34/reloc.c
|
||||
@@ -35,7 +35,10 @@
|
||||
greloc areloc;
|
||||
|
||||
if (cursection > 1)
|
||||
- exit(eprintf("Panic in emitreloc"));
|
||||
+ {
|
||||
+ eprintf("Panic in emitreloc");
|
||||
+ exit(1);
|
||||
+ }
|
||||
if (pass2)
|
||||
{
|
||||
areloc.offset = offset;
|
||||
@@ -62,7 +65,7 @@
|
||||
} filesegs[3];
|
||||
|
||||
|
||||
-openfiles(name)
|
||||
+openfiles(char *name)
|
||||
{
|
||||
outfd = creat(name, 0666);
|
||||
filesegs[0].filepos = LINKHDRSIZE;
|
||||
@@ -315,7 +318,7 @@
|
||||
close(outfd);
|
||||
}
|
||||
|
||||
-Xwrite(fd, buf, nbytes)
|
||||
+Xwrite(int fd, char *buf, int nbytes)
|
||||
{
|
||||
if (write(fd, buf, nbytes) != nbytes)
|
||||
{
|
||||
--- a/binutils/as34/support.c
|
||||
+++ b/binutils/as34/support.c
|
||||
@@ -278,5 +278,5 @@
|
||||
char *file;
|
||||
{
|
||||
fprintf(stderr, "PANIC at %s:%d\n", file, line);
|
||||
- abort(1);
|
||||
+ abort();
|
||||
}
|
||||
--- a/binutils/as34/sym.c
|
||||
+++ b/binutils/as34/sym.c
|
||||
@@ -53,7 +53,10 @@
|
||||
register int i;
|
||||
|
||||
if (inname == (char *)0)
|
||||
- exit(eprintf("Panic1 in addsym"));
|
||||
+ {
|
||||
+ eprintf("Panic1 in addsym");
|
||||
+ exit(1);
|
||||
+ }
|
||||
|
||||
s = mksym();
|
||||
s->n.name = inname;
|
||||
--- a/binutils/ld34/gld.h
|
||||
+++ b/binutils/ld34/gld.h
|
||||
@@ -67,11 +67,11 @@
|
||||
arfile *mkaddarfile(arfile **plist, char *name);
|
||||
void readGoFile(gofile *pgofile, int readTextDataRelocStab);
|
||||
void doexit(int code);
|
||||
-void *imalloc();
|
||||
+void *imalloc(size_t, char *, ...);
|
||||
char *mkstr(char *s);
|
||||
-void amprintf();
|
||||
-void ameprintf();
|
||||
-void amfprintf();
|
||||
+void amprintf(char *, ...);
|
||||
+void ameprintf(char *, ...);
|
||||
+void amfprintf(FILE *, char *, ...);
|
||||
void pass1(void);
|
||||
void addtomastersymtab(gofile *pgofile);
|
||||
void addonesym(symbol *psymbol, gofile *pgofile);
|
||||
--- a/binutils/ld34/pass2.c
|
||||
+++ b/binutils/ld34/pass2.c
|
||||
@@ -356,9 +356,9 @@
|
||||
static int
|
||||
hash(char *name)
|
||||
{
|
||||
- register int res = 0;
|
||||
+ register unsigned int res = 0;
|
||||
while (*name)
|
||||
- res = res * 33 + *name++;
|
||||
+ res = res * 33 + (unsigned char)*name++;
|
||||
return res & HASHSIZE;
|
||||
}
|
||||
|
||||
--- a/binutils/ld34/source.c
|
||||
+++ b/binutils/ld34/source.c
|
||||
@@ -90,7 +90,7 @@
|
||||
char *_name = name;
|
||||
|
||||
fixName(&_name);
|
||||
- name = imalloc(strlen(sp->path) + strlen(_name) + 2);
|
||||
+ name = imalloc(strlen(sp->path) + strlen(_name) + 2, "source path");
|
||||
sprintf(name, "%s/%s", sp->path, _name);
|
||||
fd = open(name, O_RDONLY);
|
||||
free(name);
|
||||
--- a/binutils/ld34/utils.c
|
||||
+++ b/binutils/ld34/utils.c
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
+#include <stdarg.h>
|
||||
#include <link.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
@@ -267,16 +268,18 @@
|
||||
}
|
||||
|
||||
void *
|
||||
-imalloc(nbytes, m, p1, p2, p3)
|
||||
-char *m;
|
||||
+imalloc(size_t nbytes, char *m, ...)
|
||||
{
|
||||
char buf[400];
|
||||
char *p;
|
||||
+ va_list ap;
|
||||
|
||||
p = (char *)malloc(nbytes);
|
||||
if (p == 0)
|
||||
{
|
||||
- sprintf(buf, m, p1, p2, p3);
|
||||
+ va_start(ap, m);
|
||||
+ vsnprintf(buf, sizeof buf, m, ap);
|
||||
+ va_end(ap);
|
||||
ameprintf("Not enough memory to allocate %s\n", buf);
|
||||
doexit(-1);
|
||||
}
|
||||
@@ -295,26 +298,34 @@
|
||||
}
|
||||
|
||||
void
|
||||
-amprintf(str, p1, p2, p3, p4, p5, p6)
|
||||
-char *str;
|
||||
+amprintf(char *str, ...)
|
||||
{
|
||||
- amfprintf(stdout, str, p1, p2, p3, p4, p5, p6);
|
||||
+ va_list ap;
|
||||
+
|
||||
+ va_start(ap, str);
|
||||
+ vfprintf(stdout, str, ap);
|
||||
+ va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
-ameprintf(str, p1, p2, p3, p4, p5, p6)
|
||||
-char *str;
|
||||
+ameprintf(char *str, ...)
|
||||
{
|
||||
- amfprintf(stderr, "%s: ", ourname);
|
||||
- amfprintf(stderr, str, p1, p2, p3, p4, p5, p6);
|
||||
+ va_list ap;
|
||||
+
|
||||
+ fprintf(stderr, "%s: ", ourname);
|
||||
+ va_start(ap, str);
|
||||
+ vfprintf(stderr, str, ap);
|
||||
+ va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
-amfprintf(stream, str, p1, p2, p3, p4, p5, p6)
|
||||
-FILE *stream;
|
||||
-char *str;
|
||||
+amfprintf(FILE *stream, char *str, ...)
|
||||
{
|
||||
- fprintf(stream, str, p1, p2, p3, p4, p5, p6);
|
||||
+ va_list ap;
|
||||
+
|
||||
+ va_start(ap, str);
|
||||
+ vfprintf(stream, str, ap);
|
||||
+ va_end(ap);
|
||||
fflush(stream);
|
||||
}
|
||||
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/bin/sh
|
||||
# Build as34 (assembler), ld34 (linker) and dis34 (disassembler) from Andrew
|
||||
# Morton's gsptools, patched to build on a 64-bit Linux host.
|
||||
#
|
||||
# Everything is fetched/built under tools/as34/build (override with
|
||||
# AS34_BUILD_DIR) so the repository tree stays clean.
|
||||
#
|
||||
# The upstream tarball is an UNCOMPRESSED tar with a .tar.gz name; that is
|
||||
# how the Wayback Machine archived it.
|
||||
set -eu
|
||||
|
||||
here=$(cd "$(dirname "$0")" && pwd)
|
||||
builddir=${AS34_BUILD_DIR:-"$here/build"}
|
||||
tarball="$builddir/gsp.tar.gz"
|
||||
srcdir="$builddir/src"
|
||||
treesrc="$srcdir/gsp/binutils"
|
||||
|
||||
TARBALL_SHA256=efff8e59f7e3bf0285c4bd44d63e2357d6c26a85d939d4843d6be3471af9ef46
|
||||
TARBALL_URL="https://web.archive.org/web/20070206220404if_/http://www.zip.com.au/~akpm/gsp.tar.gz"
|
||||
|
||||
mkdir -p "$builddir"
|
||||
|
||||
if [ ! -f "$tarball" ]; then
|
||||
echo "fetching $tarball"
|
||||
curl -fL --max-time 300 -o "$tarball" "$TARBALL_URL"
|
||||
fi
|
||||
|
||||
echo "$TARBALL_SHA256 $tarball" | sha256sum -c -
|
||||
|
||||
if [ ! -d "$treesrc" ]; then
|
||||
mkdir -p "$srcdir"
|
||||
tar xf "$tarball" -C "$srcdir"
|
||||
fi
|
||||
|
||||
if [ ! -f "$srcdir/.patched" ]; then
|
||||
patch -p1 -d "$srcdir/gsp" < "$here/as34-port.patch"
|
||||
touch "$srcdir/.patched"
|
||||
fi
|
||||
|
||||
# -std=gnu17: C23 removed K&R definitions, which this 1998 code uses.
|
||||
# -fcommon: the sources rely on tentative definitions being merged.
|
||||
# as34 needs -DBISON to select the flex/bison input path in as.l.
|
||||
CC_BASE="gcc -std=gnu17 -O2 -fcommon \
|
||||
-Wno-implicit-function-declaration -Wno-int-conversion \
|
||||
-Wno-incompatible-pointer-types -Wno-builtin-declaration-mismatch \
|
||||
-Wno-implicit-int -Wno-return-mismatch -Wno-format -Wno-unused-result"
|
||||
|
||||
echo "building as34"
|
||||
make -C "$treesrc/as34" clean >/dev/null 2>&1 || true
|
||||
make -C "$treesrc/as34" \
|
||||
CC="$CC_BASE -DBISON" \
|
||||
XCFLAGS="-include $here/proto.h"
|
||||
|
||||
echo "building ld34 and dis34"
|
||||
make -C "$treesrc/ld34" clean >/dev/null 2>&1 || true
|
||||
make -C "$treesrc/ld34" ld34 CC="$CC_BASE"
|
||||
make -C "$treesrc/ld34" dis34 CC="$CC_BASE"
|
||||
|
||||
echo
|
||||
echo "built:"
|
||||
ls -l "$treesrc/as34/as34" "$treesrc/ld34/ld34" "$treesrc/ld34/dis34"
|
||||
Binary file not shown.
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Extract the loadable .text bytes from an as34/ld34 g.out file.
|
||||
|
||||
g.out is not COFF. It uses the .go/linkhdr format from binutils/link.h
|
||||
(LINKMAGIC 0x3abf782e), packed with #pragma pack(2).
|
||||
|
||||
IMPORTANT: link.h declares the header fields that hold addresses and sizes
|
||||
as `long`. On the original 32-bit Unix hosts that is 4 bytes; on x86-64 it
|
||||
is 8, so the written header is 156 bytes here instead of the historical 80.
|
||||
as34 and ld34 are both rebuilt with the same compiler, so they agree with
|
||||
each other and with this parser. A stable on-disk format would need `long`
|
||||
changed to int32_t in link.h.
|
||||
|
||||
Layout as written by this 64-bit build (offsets in bytes):
|
||||
|
||||
off field
|
||||
0 short swapped
|
||||
2 long magic
|
||||
10 short flags
|
||||
12 long time
|
||||
20 long textstart file position of text
|
||||
28 long textsize size in BYTES
|
||||
36 long datastart
|
||||
44 long datasize
|
||||
52 long bsssize
|
||||
60 long relocstart
|
||||
68 long nrelocs
|
||||
76 long symtabstart
|
||||
84 long nsyms
|
||||
92 long stabstart
|
||||
100 long nstabs
|
||||
108 long stringstart
|
||||
116 long stringSize
|
||||
124 long textLoadAddress BIT address
|
||||
132 long dataLoadAddress
|
||||
140 long bssLoadAddress
|
||||
148 long entrypoint BIT address
|
||||
"""
|
||||
import hashlib
|
||||
import struct
|
||||
import sys
|
||||
|
||||
LINKMAGIC = 0x3ABF782E
|
||||
|
||||
|
||||
def main(out_path, bin_path):
|
||||
d = open(out_path, "rb").read()
|
||||
fields = struct.unpack_from("<hqh" + "q" * 18, d, 0)
|
||||
(swapped, magic, flags, _time, textstart, textsize,
|
||||
_datastart, _datasize, _bsssize, _relocstart, _nrelocs,
|
||||
_symtabstart, _nsyms, _stabstart, _nstabs,
|
||||
_stringstart, _stringsize,
|
||||
textload, _dataload, _bssload, entry) = fields
|
||||
|
||||
if magic != LINKMAGIC:
|
||||
sys.exit("extract: bad magic %#010x" % magic)
|
||||
if swapped:
|
||||
sys.exit("extract: byte-swapped header not handled")
|
||||
|
||||
payload = d[textstart:textstart + textsize]
|
||||
if len(payload) != textsize:
|
||||
sys.exit("extract: truncated text (%d of %d bytes)"
|
||||
% (len(payload), textsize))
|
||||
|
||||
open(bin_path, "wb").write(payload)
|
||||
print("origin=%08X (bits) size=%d bytes entry=%08X file_offset=%#x md5=%s"
|
||||
% (textload, textsize, entry, textstart,
|
||||
hashlib.md5(payload).hexdigest()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
sys.exit("usage: extract.py hello.out payload.bin")
|
||||
main(sys.argv[1], sys.argv[2])
|
||||
@@ -0,0 +1,47 @@
|
||||
; hello.as34.g -- hello.asm ported to Andrew Morton's as34 syntax.
|
||||
;
|
||||
; Differences from harness/hello-world/hello.asm (TI GSPA syntax):
|
||||
; * hex constants use >DEAD, not 0DEADh
|
||||
; * no L/W immediate-width suffix; as34's movi is always the 32-bit form,
|
||||
; which is what the GSPA source asked for with ", L"
|
||||
; * @absolute operands use the same @ syntax
|
||||
; Everything else (labels, mnemonics, bit addresses, dsjs loops) is unchanged.
|
||||
;
|
||||
; Addresses in operands are BIT addresses, like every TMS34010 pointer.
|
||||
|
||||
.text
|
||||
.globl _start
|
||||
_start:
|
||||
dint ; stay deterministic: no firmware ISR
|
||||
setf 16, 0, 0 ; field 0 = 16 bits, zero-extended
|
||||
setf 32, 0, 1 ; field 1 = 32 bits, zero-extended
|
||||
|
||||
; ---- channel 1: RAM markers -----------------------------------
|
||||
movi >DEAD, a0
|
||||
move a0, @>1810000, 0 ; MAGIC1 (bytes AD DE in a dump)
|
||||
movi >BEEF, a0
|
||||
move a0, @>1810010, 0 ; MAGIC2 (bytes EF BE)
|
||||
|
||||
; ---- channel 2: solid block in video RAM ----------------------
|
||||
movi >4019040, a2 ; 04000000h + 100*1024 + 64
|
||||
movi 64, a3 ; 64 rows
|
||||
movi 16, a4 ; 16 words = 256 pixels per row
|
||||
sub a1, a1 ; counter = 0
|
||||
movi >FFFF, a0
|
||||
row_loop:
|
||||
move a4, a5
|
||||
word_loop:
|
||||
move a0, *a2+, 0 ; store word, postincrement by 16 bits
|
||||
addiw 1, a1 ; count this store
|
||||
dsjs a5, word_loop
|
||||
addiw >300, a2 ; skip 768 bits to the next row
|
||||
dsjs a3, row_loop
|
||||
|
||||
; The counter is 0400h only if all 64*16 stores executed.
|
||||
move a1, @>1810020, 0 ; COUNT
|
||||
|
||||
spin:
|
||||
nop
|
||||
jruc spin
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,8 @@
|
||||
/* Prototypes for functions the original K&R sources declared implicitly.
|
||||
* On a 32-bit host an implicit int return was pointer-sized; on x86-64 it is
|
||||
* not. Included with -include for every translation unit. */
|
||||
char *imalloc(int, char *, ...);
|
||||
char *mkstr(char *);
|
||||
void amprintf(char *, ...);
|
||||
void ameprintf(char *, ...);
|
||||
void eprintf(char *, ...);
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/bin/sh
|
||||
# Assemble hello.as34.g with as34, link it with ld34, extract .text, then
|
||||
# compare against the TI-GSPA-built payload and (optionally) run MAME.
|
||||
#
|
||||
# tools/as34/test.sh
|
||||
# REPO=$PWD MAME=/path/to/vt1200 tools/as34/test.sh
|
||||
#
|
||||
# Without REPO/MAME only the byte comparison runs.
|
||||
set -eu
|
||||
|
||||
here=$(cd "$(dirname "$0")" && pwd)
|
||||
builddir=${AS34_BUILD_DIR:-"$here/build"}
|
||||
as34="$builddir/src/gsp/binutils/as34/as34"
|
||||
ld34="$builddir/src/gsp/binutils/ld34/ld34"
|
||||
if [ ! -x "$as34" ] || [ ! -x "$ld34" ]; then
|
||||
echo "tools not built; run tools/as34/build.sh first" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
work="$builddir/work"
|
||||
mkdir -p "$work" "$work/evidence"
|
||||
cp "$here/hello.as34.g" "$work/hello.as34.g"
|
||||
cd "$work"
|
||||
|
||||
"$as34" hello.as34.g
|
||||
"$ld34" -c -T 1800000 -e_start hello.as34.go -o hello.out -m hello.map
|
||||
python3 "$here/extract.py" hello.out hello.bin
|
||||
|
||||
echo
|
||||
if [ -n "${REPO:-}" ] && [ -f "$REPO/harness/hello-world/payload.bin" ]; then
|
||||
expected="$REPO/harness/hello-world/payload.bin"
|
||||
else
|
||||
expected="$here/expected-payload.bin"
|
||||
fi
|
||||
if cmp hello.bin "$expected"; then
|
||||
echo "PASS: hello.bin is byte-identical to $expected"
|
||||
else
|
||||
echo "FAIL: hello.bin differs from $expected" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "${MAME:-}" ] && [ -n "${REPO:-}" ]; then
|
||||
echo
|
||||
echo "running MAME acceptance test"
|
||||
out=$(cd "$REPO" && HELLO_END=150 HELLO_PAYLOAD="$work/hello.bin" \
|
||||
VT1200_DUMP_FRAME=145 VT1200_DUMP_RAM=145 \
|
||||
VT1200_DUMP_DIR="$work/evidence" \
|
||||
"$MAME" vt1200 -rompath harness/mame/roms -video none -sound none \
|
||||
-nothrottle -debug -debugger none \
|
||||
-autoboot_script harness/mame/hello.lua -autoboot_delay 0 2>&1)
|
||||
echo "$out" | grep -E "MAGIC|COUNT|VRAM block|PC at|lit ="
|
||||
if echo "$out" | grep -q "WRONG"; then
|
||||
echo "FAIL: MAME reported WRONG" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! echo "$out" | grep -q "1024 stores ok"; then
|
||||
echo "FAIL: counter did not reach 0x400" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! echo "$out" | grep -q "VRAM block 1024/1024 words are FFFF, 0 holes"; then
|
||||
echo "FAIL: VRAM block not complete" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "PASS: MAME markers and VRAM block"
|
||||
fi
|
||||
Reference in new issue
Block a user