Clean-room MPW IIgs toolchain in Python: 65816 assembler, OMF linker, Rez resource compiler
Project description
gsasm
A clean-room Python reimplementation of the MPW IIgs cross-development
toolchain — the AsmIIgs assembler, LinkIIgs linker, the RezIIgs
resource compiler, the MakeBinIIgs/OverlayIIgs/catenate packagers, and
the ExpressLoad relinker — validated byte-for-byte against the Apple IIgs
ROM 03 and the GS/OS System 6.0.1 shipping binaries.
Apple built the IIgs ROM and GS/OS on a 68k Mac under MPW Shell. Running that
chain today means SheepShaver → a Mac OS 7 image → MPW → the GS cross-tools.
gsasm replaces it with pure Python — no emulator, no dependencies outside
the standard library.
Results
Rebuilt from the original source and verified byte-identical to the shipping binaries:
- ROM 03 — all 262,144 bytes
- The GS/OS kernel —
prodos,GS.OS(38,805/38,805),Start.GS.OS,Error.Msg, the Loader, and P8 (ProDOS 8 compatibility kernel, 17,128/17,128 including its overlay packaging) - All 8 buildable FSTs (ProDOS, HFS, Char, High Sierra, DOS 3.3, Pascal, MS-DOS, AppleShare — 111,584/111,584 bytes) and all 12 device drivers (94,948/94,948)
- All 12 mapped toolbox toolsets (186,110/186,110 bytes) — including the
multi-segment ExpressLoad tools (Window/Menu/Control Managers, QDAux,
TextEdit …) with their linker-generated
~JumpTablesegments and full relocation dictionaries - The whole System 6.0.1 System Disk — every one of the 30 files the disk harness rebuilds from source is logically byte-exact, and the packed 800K disk image matches physically, all 819,264 bytes
- Resource forks (
gsrez):Sys.Resources(24,337 bytes, 143 resources including embedded code resources) andEasyMount— the latter byte-exact across both forks, code and resources, from source to shipping file - Instruction encoding: 100% — every instruction in the ~97,000-line corpus encodes to the same bytes as the original listings
- 61/61 ROM objects link-identical — for every module, linking
gsasm's object or Apple's original produces the same load image
Every "proven limit" recorded along the way — a GS.OS "94-byte external floor", "absent" AppleShare sources, "unclosable" ExpressLoad relocation encodings — was eventually falsified as a nameable assembler or harness bug and closed. The full accounting, with the evidence trail for each, is in docs/RESULTS.md.
Byte-exact reproduction also makes the shipping binaries subtractable. For a
worked example — recovering a lost community bug fix from a modified HFS.FST
with no source, by subtracting the original gsasm rebuilds — see
docs/notes/hfs-fst-6.0.4-carry-bug.md.
Install
pip install git+https://github.com/emdeejay/gsasm.git
Or install from a local clone in editable mode:
git clone https://github.com/emdeejay/gsasm.git
cd gsasm
pip install -e .
Requires Python 3.9+. No dependencies outside the standard library.
CLI
gsasm — assembler
gsasm <source.asm> [-I <incdir>] [-d KEY=VAL] [-o <out.obj>]
Assembles an MPW IIgs-dialect 65816 source file and writes an OMF v2 object file.
# assemble a single file
gsasm MyTool.asm -I ./includes -o MyTool.obj
# pass command-line defines (equivalent to the -d flag in MPW IIgs)
gsasm ROMDataMgr.asm -I ./includes -d Big=1
# multiple include directories
gsasm monitor.aii -I ./includes -I ./romsrc/Monitor
Include directories are searched in order for files referenced by INCLUDE
directives. The source file's own directory is not searched implicitly — add
-I . if you need it.
gslink — linker
gslink <file.obj> [-o <out.load>]
Links a single multi-segment OMF object file. All LEXPR/BEXPR/EXPR
relocation records and RELEXPR (relative branch) records are fully evaluated
against the collected GLOBAL symbol table. The output is a single-segment OMF
load file with a flat body.
gslink MyTool.obj # writes MyTool.out
gslink MyTool.obj -o MyTool.load
For multi-object linking — libraries, segment naming/placement, the
LinkIIgs -apw recipe used by the GS/OS build scripts — use
gsasm/linkiigs.py; for ROM bank layout see work/linkrom.py.
gsrez — resource compiler
gsrez <source.r> [-I <incdir>]... [-o <out>] [-t <filetype>] [-c <creator>]
[--read-dir <dir>]... [--meta KEY=VAL]...
Compiles a Rez source file (the MPW IIgs RezIIgs dialect: type templates,
resource bodies, read statements, the C-style preprocessor) into an Apple
IIgs resource fork, written as a raw fork image. TypesIIGS.r-style include
files are searched through -I directories; read files (e.g. linked code
resources) through --read-dir. --meta sets fork-header fields (creation
timestamp etc.) for byte-exact reproduction work.
gsrez sys.resources.r -I ./rincludes --read-dir ./build -o SYS.RESOURCES -t "F9 "
Python API
from gsasm import asm, omf, link
# Assemble (include paths are positional; defines optional)
a = asm.assemble("MyTool.asm", ["./includes"], defines={"Big": 1})
if a.errors:
raise SystemExit("\n".join(a.errors))
# Emit an OMF object
obj_bytes = omf.emit(a)
with open("MyTool.obj", "wb") as f:
f.write(obj_bytes)
# Link to a load file
load_bytes = link.link(obj_bytes)
with open("MyTool.out", "wb") as f:
f.write(load_bytes)
# Parse an existing OMF file
header = omf.parse_header(obj_bytes)
records, _ = omf.parse_records(obj_bytes, header["DISPDATA"],
numlen=header["NUMLEN"])
for offset, record_type, data in records:
print(offset, record_type, data)
Source dialect
gsasm implements the MPW IIgs (AsmIIgs) source dialect as used in the
ROM 03 and System 6.0.1 source trees. Notable features:
- 65816 instruction set — full addressing-mode selection (dp/abs/long,
cross-bank rules),
MVN/MVP,PEA/PEI/PER - Macro engine —
MACRO/ENDM, positional parameters (&1…&n), keyword parameters,WHILE/ENDWHILE,IF/ELSE/ENDIF,GOTO/AGO/AIF,MEXIT,ANOP, builtins (&sysdate,&systime, …) - Directives —
PROC(withTEMPORG/ENTRY/EXPORTforms),ENTRY/EXPORT/IMPORT,RECORD/ENDR(templates and typedDSinstances),WITH,DC/DCB/DS,ORG,SEG,INCLUDE,MSB/LONGA/LONGI/CASE ON|OFF,OBJEND - Label scoping —
@-local labels scoped to the nearest enclosing non-@label (per MPW Assembler Reference p. 17); per-PROCnamespaces - Expression evaluator — MPW operator precedence, byte extraction
(
#<x,#>x,#^x), shifts, the≈one's-complement operator,MSB ONcharacter constants - OMF v2 emitter —
CONST/LCONST/DS,LEXPR/BEXPR/EXPR/RELEXPR,GLOBAL/GEQU,SUPERrelocation dictionaries, cross-segment and import references, faithful record chunking
Sources are read as MacRoman with classic-Mac line endings, matching real MPW files.
Module layout
gsasm/
__init__.py
__main__.py CLI entry points (gsasm, gslink)
m65816.py 65816 opcode table and addressing-mode encoding
expr.py MPW expression evaluator
asm.py Multi-pass assembler: macro engine, symbols, segments
omf.py OMF v2 parser and emitter
link.py Single-object OMF linker
linkiigs.py General LinkIIgs: multi-object, libraries, -apw recipe,
segment naming and placement
makebin.py MakeBinIIgs / OverlayIIgs / catenate packaging
expressload.py ExpressLoad relinker (fast-load format + SUPER records)
Tests
python3 tests/run_fixtures.py
The tests/ fixture suite runs on a bare checkout — no reference material
needed. Each fixture is an original source pinning one discovered dialect or
OMF behavior, with expected bytes minted only while the full golden-corpus
validation passes. See tests/README.md for how blessing
works and why the expected bytes are trustworthy.
Validation harnesses (work/)
The work/ scripts are the differential-validation harnesses used during
development. They compare rebuilt output against captured artifacts of the
original build — Apple's source, listings, objects, and shipping binaries —
which are copyrighted and not included (everything under ref/ and
work/romsrc/ is gitignored; supply your own).
| Script | What it validates |
|---|---|
gate.py |
Runs every harness below against a committed baseline; fails on any regression |
buildrom.py |
Reconstructs rom.03 and verifies it byte-identical to the shipping ROM |
bytecheck.py |
Instruction encoding against the .lst Object Code column |
objcheck.py |
Emitted .obj files record-by-record against the originals |
linkcheck.py |
Differential link: original vs gsasm object through the same linker |
toolcheck.py |
System/Tools/ToolNNN toolsets vs the shipping files |
fstcheck.py |
System/FSTs/* vs the shipping files |
drivercheck.py |
System/Drivers/* vs the shipping files |
kernelcheck.py |
prodos, GS.OS, Start.GS.OS, Error.Msg, P8 |
diskcheck.py |
Whole System 6.0.1 disk-image files (needs the a2til sibling tools; set A2TIL_PATH) |
linkrom.py |
The LinkIIgs-equivalent ROM bank layout |
hfs.py |
Minimal HFS reader for extracting sources from .hfv images |
Background
The ROM 03 and System 6.0.1 sources were assembled with AsmIIgs (circa
1989–1993), Apple's 65816 cross-assembler for MPW, distributed through APDA
with the rest of the MPW IIgs cross-development tools. The tools' own source
code was never published, so this project is a clean-room reimplementation:
behaviour was reverse-engineered from captured .obj and .lst files. Given
source, listings, and objects from a known-good build, every discrepancy
between gsasm's output and the original is a measurable bug, and every
target that passes a differential comparison is proven correct rather than
assumed. The same method then extended, tool by tool, to the rest of the MPW
IIgs chain until whole shipping binaries reproduced.
The OMF (Object Module Format) specification is documented in the Apple IIgs Toolbox Reference and the Apple IIgs GS/OS Reference.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gsasm-0.3.0.tar.gz.
File metadata
- Download URL: gsasm-0.3.0.tar.gz
- Upload date:
- Size: 3.4 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ce4d086d229ee12cc7bba1490c3a81cbc8425860438c0c4bec2ee4b572c0e3d
|
|
| MD5 |
065cdfb5d02e1f7a56c646affecbbbde
|
|
| BLAKE2b-256 |
ffe1681d41cce2a5dba132272b7345feb00cf5f898893f2feb87ce0ca653f454
|
Provenance
The following attestation bundles were made for gsasm-0.3.0.tar.gz:
Publisher:
publish.yml on emdeejay/gsasm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gsasm-0.3.0.tar.gz -
Subject digest:
7ce4d086d229ee12cc7bba1490c3a81cbc8425860438c0c4bec2ee4b572c0e3d - Sigstore transparency entry: 2199476729
- Sigstore integration time:
-
Permalink:
emdeejay/gsasm@91d350f93b89426ab4bb6aa0d479fc031e40c5fc -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/emdeejay
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@91d350f93b89426ab4bb6aa0d479fc031e40c5fc -
Trigger Event:
push
-
Statement type:
File details
Details for the file gsasm-0.3.0-py3-none-any.whl.
File metadata
- Download URL: gsasm-0.3.0-py3-none-any.whl
- Upload date:
- Size: 178.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41c7b2b8910271fe14cad0939430756de3601a284032c3b1f71977169525c58b
|
|
| MD5 |
8b817934996dc7ac9854085eb802b7fa
|
|
| BLAKE2b-256 |
2ea7a91d06a9440f7b092542835ab68ac0fb4cea9db2f83618d8e2f7a6fda724
|
Provenance
The following attestation bundles were made for gsasm-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on emdeejay/gsasm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gsasm-0.3.0-py3-none-any.whl -
Subject digest:
41c7b2b8910271fe14cad0939430756de3601a284032c3b1f71977169525c58b - Sigstore transparency entry: 2199476849
- Sigstore integration time:
-
Permalink:
emdeejay/gsasm@91d350f93b89426ab4bb6aa0d479fc031e40c5fc -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/emdeejay
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@91d350f93b89426ab4bb6aa0d479fc031e40c5fc -
Trigger Event:
push
-
Statement type: