Add a reproducible build and a browser-playable page - #1
Open
mpasternak wants to merge 13 commits into
Open
Conversation
Records the approved design: JWasm-based reproducible build of MARS.ASM into MARS.COM, and a GitHub Pages site that runs it via a self-hosted, build-time-pinned js-dos 8.4.1. Includes verified findings that shaped the design: MARS.ASM is MASM dialect (not NASM), JWasm assembles it to 1550 bytes with no errors, and js-dos 8.4.1 needs no SharedArrayBuffer, so it deploys on Pages without COOP/COEP headers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
Adds the task-by-task implementation plan, then fixes seven defects found in a self-review of both documents: Plan: corrected assertion counts in three tasks (they under- and over-counted, which would send an executor hunting a phantom bug); closed a hole where test-lib.sh recorded nothing when die() behaved correctly; replaced a brittle basename check with a file-existence check so the repo can be cloned to any directory name; and rewrote a self-referential note about the third-party-origin assertion. Spec: documented versions.env, scripts/lib.sh and tests/ which the plan introduced but the spec never named; replaced the ambiguous "under 64 KB" with the true .COM ceiling of 65280 bytes (0xFF00, PSP-adjusted); and aligned the verification section with the plan, which enforces the pinned size and SHA-256 in CI rather than merely recording them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
Resolves an assembler from $JWASM, then PATH, then builds JWasm v2.20 from source into .toolchain/. A --docker flag covers hosts with no compiler, and --check enforces the pinned size and SHA-256. JWasm's GccUnix.mak targets Linux/FreeBSD and does not build on macOS, which the plan did not anticipate. Two fixes are applied automatically on Darwin: an include shim so <malloc.h> resolves to <stdlib.h> (injected via inc_dirs rather than editing 58 source files), and removal of the GNU-only -s and -Wl,-Map link flags that Apple's ld rejects. Both are confined to the gitignored .toolchain/ checkout. The macOS/clang/ARM64 build produces a MARS.COM byte-identical to the Linux/gcc/aarch64 build, so the pinned SHA is a real reproducibility gate rather than a description of one machine. Build output now goes to a log file instead of /dev/null, so a failure is diagnosable without re-running the build by hand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
The bundle is a ZIP whose root becomes drive C:. js-dos does not auto-mount, so [autoexec] performs the mount itself, matching the layout js-dos's own bundle generator emits. machine=vgaonly is pinned because the renderer drives VGA mode 13h and reprograms the palette DAC; DOSBox's default svga_s3 shows artifacts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
Downloads the js-dos npm tarball during the build and extracts only the five files the page needs, so the repo stays lean and the deployed page serves everything from its own origin with no CDN dependency at view time. Ships the plain dosbox backend and skips dosbox-x, cutting the staged payload from roughly 17 MB to 2.0 MB. MARS only needs machine=vgaonly, which plain DOSBox supports. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
Composes _site/ from the built binary, the .jsdos bundle, the fetched emulator assets and the static page sources, so nothing is loaded from a third-party origin at view time. A test asserts that invariant directly. Adds .nojekyll because GitHub Pages otherwise runs Jekyll, which strips dot-prefixed paths and would mangle the asset directories. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
Browser testing revealed the asset subset was too aggressive: js-dos unpacks the .jsdos bundle (a ZIP) with wlibzip at runtime, so omitting wlibzip.js and wlibzip.wasm made the emulator 404 before DOS could boot. Both are now fetched and asserted by the test. serve.sh builds and serves over HTTP because js-dos loads the bundle with fetch(), which browsers block on file:// origins. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
Adds a CI workflow that runs build.sh --check plus the test suite on every push and PR, and a Pages workflow that builds the site and deploys it. Documents the build, the browser player, the macOS caveat, and the 33-byte gap between upstream's reported 1517 bytes and this build's 1550. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
Adds a reproducible JWasm build of MARS.ASM and a GitHub Pages site that runs the resulting MARS.COM in the browser via a self-hosted js-dos. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
js-dos crashed repeatedly in Firefox with "Backend crashed, cause: ...", which comes from the Emscripten DOSBox module's exception handler in wdosbox.js — the DOSBox core itself was throwing. Switching to em-dosbox or DOSee would not have helped: they wrap the same DOSBox core. v86 emulates real PC hardware instead of emulating the DOS API, so it has entirely different failure modes, and its Pentium-level instruction set covers the 32-bit code MARS needs (MARS.ASM declares .386 and uses 32-bit registers on 35 lines). The page now boots the 720 KB FreeDOS floppy from v86's own demos, with MARS.COM, CuteMouse and an AUTOEXEC.BAT injected at build time via mtools. The mouse driver is required because MARS calls int 33h, a service DOSBox provided internally but hardware emulation does not; MARS checks for the driver and degrades gracefully, so a missing driver costs camera control rather than the whole demo. Both external downloads are checksum-pinned so a rebuild either reproduces the same image or fails loudly. Also adds a favicon, which was the last remaining 404 on the page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
A push-time security review flagged fetch-v86.sh for supply-chain integrity, correctly: it pulled seabios.bin and vgabios.bin from the "master" branch of copy/v86 with no verification at all, while make-image.sh already checksum-pinned both of its downloads. The BIOS blobs are executed by the emulator, and a mutable branch ref can change under us at any time. Pins V86_BIOS_REF to an immutable commit and adds SHA-256 verification for both BIOS blobs and the v86 npm tarball, so every artifact that is fetched and then executed is pinned by content rather than by name. Hoists verify_sha into scripts/lib.sh so the two fetchers share one implementation instead of make-image.sh carrying a private copy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Cv1JoZyJdqYiCXiVSKDQP
Adds a build that assembles MARS.ASM into MARS.COM, and an optional GitHub Pages site that runs the result in a browser. The build uses JWasm, pinned by tag, because MARS.ASM is MASM/TASM dialect and NASM cannot assemble it. Output is a 1550-byte MARS.COM, byte-identical under JWasm v2.20 and v2.21 and across macOS/clang/ARM64 and Linux/gcc. MARS.ASM stays the single source of truth; the binary is never committed. The page boots a real emulated PC via v86 rather than emulating DOS: the 720 KB FreeDOS floppy from v86's own demos, with MARS.COM, the CuteMouse driver and an AUTOEXEC.BAT injected at build time. The mouse driver is needed because MARS calls int 33h, which hardware emulation does not provide on its own. Every third-party download is checksum-pinned.
Owner
|
Hi, nice browser version! MARS has been reborn once again. The build recipe is simply the original assembly source code, followed by: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hi! Thank you for this repository — the annotated disassembly is a genuinely lovely piece of work, and the reduction from 5649 to ~1.5 kB is remarkable.
▶ See it running first
Before reading any of the diff, you can just try it — this branch deployed to my fork's GitHub Pages:
https://dtz-labs.github.io/MARS.COM/
Click the screen to capture the mouse; moving it pans the camera. (That URL is only there so you can see it working — nothing in the PR points at my fork. If you merge this, the same workflow publishes it under your own account at
https://matrix-toolbox.github.io/MARS.COM/.)The repository ships the assembly source, but there is currently no build recipe and no way to see the demo without a DOS machine. This PR adds both. Everything here is optional and separable — please take only the parts you want, or none of them.
What it adds
A reproducible build (
scripts/build.sh,make build)MARS.ASMis MASM/TASM dialect (.model tiny,org 100h,COMMENT #blocks), so NASM cannot assemble it. The build uses JWasm, pinned by tag inversions.env. Because the model is tiny, the assembler emits the.COMimage directly — no link step.If no assembler is on
PATHthe script builds JWasm from source into a gitignored.toolchain/;--dockercovers hosts with no compiler. JWasm'sGccUnix.maktargets Linux/FreeBSD, so on macOS the script applies two portability fixes automatically (an include shim for<malloc.h>, and stripping the GNU-only-s/-Wl,-Maplink flags).MARS.ASMstays the single source of truth — the binary is never committed.A browser-playable page (
scripts/build-site.sh, GitHub Pages workflow)Rather than emulating DOS, the page boots a real emulated PC with v86: the 720 kB FreeDOS floppy from v86's own demos, with
MARS.COM, the CuteMouse driver and anAUTOEXEC.BATinjected at build time viamtools.The mouse driver is not incidental — MARS calls
int 33h, which DOSBox-based players implement internally but hardware emulation does not provide on its own. (MARS checks for the driver and degrades gracefully, so without it the landscape still renders and only the camera stops responding — nicely defensive code.)I first tried a DOSBox-based player, but it crashed repeatedly in Firefox from inside the DOSBox core, so I moved to hardware emulation instead.
Every third-party download — the v86 runtime, both BIOS blobs, the FreeDOS image, the mouse driver — is pinned by SHA-256, and the BIOS blobs are pinned to an immutable commit rather than a branch. Nothing is loaded from a third-party origin at page-view time.
CI verifies the build and runs the test suite on every push.
One question for you
The README says 1517 bytes, but this toolchain reliably produces 1550 (
sha256:10a1bb6c…), byte-identical under JWasm v2.20 and v2.21 and across macOS/clang/ARM64 and Linux/gcc. I documented both numbers rather than guess which is right — do you remember which assembler and options you used? I would happily match the build to reproduce your exact 1517 bytes.Notes
.386matters: there are 35 lines using 32-bit registers, so the binary needs a 386+. Worth stating explicitly somewhere, I think.mtools; building just the binary does not.Happy to split this into smaller PRs, drop the Pages part, or adjust anything to your taste.