Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Build MARS.COM

on:
push:
branches: ["**"]
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Cache the JWasm toolchain
uses: actions/cache@v4
with:
path: .toolchain
key: jwasm-${{ hashFiles('versions.env') }}

- name: Install mtools (needed to build the FreeDOS floppy image)
run: sudo apt-get update -qq && sudo apt-get install -y -qq mtools

- name: Assemble MARS.COM and verify reproducibility
run: bash scripts/build.sh --check

- name: Run the test suite
run: bash tests/run-tests.sh

- name: Report size and checksum
run: |
size=$(wc -c < build/MARS.COM | tr -d ' ')
sha=$(sha256sum build/MARS.COM | cut -d' ' -f1)
{
echo "### MARS.COM"
echo ""
echo "| | |"
echo "|---|---|"
echo "| Size | $size bytes |"
echo "| SHA-256 | \`$sha\` |"
} >> "$GITHUB_STEP_SUMMARY"

- uses: actions/upload-artifact@v4
with:
name: MARS.COM
path: build/MARS.COM
if-no-files-found: error
54 changes: 54 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Deploy Pages

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

# Let a running deploy finish rather than cancelling it mid-flight.
concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Cache the JWasm toolchain
uses: actions/cache@v4
with:
path: .toolchain
key: jwasm-${{ hashFiles('versions.env') }}

- name: Install mtools (needed to build the FreeDOS floppy image)
run: sudo apt-get update -qq && sudo apt-get install -y -qq mtools

- name: Build the site
run: bash scripts/build-site.sh

- uses: actions/configure-pages@v5

- uses: actions/upload-pages-artifact@v3
with:
path: _site

deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Build outputs — MARS.ASM is the single source of truth
build/
_site/
.toolchain/
MARS.COM
*.jsdos

# Fetched dependencies
node_modules/
*.tgz

# OS turds
.DS_Store
31 changes: 31 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Thin wrappers over scripts/. Every target is also runnable directly.
.PHONY: all build image v86 site serve test clean help

all: site

help: ## List the available targets
@grep -hE '^[a-z-]+:.*##' $(MAKEFILE_LIST) \
| sed -e 's/:.*##/\t/' \
| awk -F'\t' '{ printf " \033[1m%-8s\033[0m %s\n", $$1, $$2 }'

build: ## Assemble MARS.ASM into build/MARS.COM
@bash scripts/build.sh

image: build ## Build the bootable FreeDOS floppy (build/mars.img)
@bash scripts/make-image.sh

v86: ## Fetch the pinned v86 runtime and BIOS blobs
@bash scripts/fetch-v86.sh

site: ## Compose the deployable site into _site/
@bash scripts/build-site.sh

serve: ## Build the site and serve it at http://localhost:8080
@bash scripts/serve.sh

test: ## Run the test suite
@bash tests/run-tests.sh

clean: ## Remove build outputs (keeps the cached toolchain)
@rm -rf build _site
@echo "Removed build/ and _site/ (run 'rm -rf .toolchain' to drop the assembler too)"
88 changes: 83 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,89 @@
# MARS landscape

A comprehensive study of the outstanding code example from 1993 - the martian landscape renderer by Tim J. Clarke.
A comprehensive study of the outstanding code example from 1993 — the martian
landscape renderer by Tim J. Clarke.

Original code has been disassembled, rewritten and reduced from **5649** bytes to... **1517 bytes**! :)
Original code has been disassembled, rewritten and reduced from **5649** bytes
to about **1.5 kB**! :)

**Tim, if you read this, please contact me! I was trying to reach you, but I couldn't...**
**Tim, if you read this, please contact me! I was trying to reach you, but I
couldn't...**

![MARS](https://raw.githubusercontent.com/matrix-toolbox/MARS.COM/main/mars_4_3.png)
![MARS](mars_4_3.png)

It works on DOSBOX and also on genuine x86 machines (mouse is needed). More details can be found [here](https://chaos.if.uj.edu.pl/~wojtek/MARS.COM).
## ▶ Run it in your browser

**<https://matrix-toolbox.github.io/MARS.COM/>**

Click the canvas to capture the mouse — moving it pans the camera.

## Building and running it here

Alongside the annotated assembly, this repository can build the binary and
publish a browser-playable page:

- `scripts/build.sh` — assembles `MARS.ASM` into `MARS.COM`
- `scripts/build-site.sh` — composes the GitHub Pages site
- GitHub Actions workflows that verify the build and deploy the page

`MARS.ASM` remains the single source of truth; the binary is never committed.

## Building

```sh
make build # assemble build/MARS.COM
make serve # build the site and preview at http://localhost:8080
make test # run the test suite
```

`MARS.ASM` is MASM/TASM dialect (`.model tiny`, `org 100h`, `COMMENT #`
blocks), so it needs a MASM-compatible assembler — NASM cannot build it. The
build uses [JWasm](https://github.com/Baron-von-Riedesel/JWasm), pinned in
`versions.env`. If no assembler is on your `PATH`, `build.sh` compiles JWasm
from source into `.toolchain/` automatically. With no C compiler available,
`bash scripts/build.sh --docker` runs the whole build in a container.

On macOS the JWasm build needs two portability fixes (its makefile targets
Linux/FreeBSD); `build.sh` applies them automatically.

Building the site also needs `mtools` (`brew install mtools` /
`apt-get install mtools`) to write `MARS.COM` into the floppy image.

### How the browser player works

The page boots a real emulated PC rather than emulating DOS. `make image`
takes the 720 KB FreeDOS boot floppy used by v86's own demos and injects
`MARS.COM`, the CuteMouse driver, and an `AUTOEXEC.BAT` that loads the driver
and runs the program. Both downloads are checksum-pinned in `versions.env`.

The mouse driver is not optional: MARS calls `int 33h`, which DOSBox-based
players implement internally but hardware emulation does not. Without a
resident driver the landscape still renders — MARS checks for the driver and
degrades gracefully — but the camera never moves.

### Build output

The pinned toolchain produces a **1550-byte** `MARS.COM`
(`sha256:10a1bb6c…`), byte-identical under JWasm v2.20 and v2.21 and across
macOS/clang/ARM64 and Linux/gcc. Upstream's README reports **1517** bytes; the
33-byte difference comes from the original author's toolchain and has not been
reconciled. `scripts/build.sh --check` enforces the 1550-byte result so
unintended changes to `MARS.ASM` are caught.

## Running it natively

It works on DOSBox and on genuine x86 machines (a mouse is needed). Under
DOSBox, always use `-machine vgaonly` — the renderer drives VGA mode 13h and
reprograms the palette DAC, and the default `svga_s3` emulation shows
artifacts. More details are on the
[author's page](https://chaos.if.uj.edu.pl/~wojtek/MARS.COM).

## Credits and licence

- Original martian landscape renderer — **Tim J. Clarke**, 1993
- Disassembly, rewrite and size reduction — **Wojciech Bruzda**, 2021
- Browser emulation — [v86](https://github.com/copy/v86) (BSD), booting
[FreeDOS](https://www.freedos.org/) with the
[CuteMouse](https://cutemouse.sourceforge.net/) driver (GPL)

Released under GPL-3.0, as upstream.
35 changes: 35 additions & 0 deletions scripts/build-site.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# Composes the deployable site from the bootable image, the v86 runtime and
# the static page sources.
#
# Everything the browser loads is copied into _site/, so the deployed page
# makes no third-party requests at view time.
. "$(dirname "${BASH_SOURCE[0]}")/lib.sh"

log "Building MARS.COM"
bash "$REPO_ROOT/scripts/build.sh"

log "Building the bootable floppy image"
bash "$REPO_ROOT/scripts/make-image.sh"

log "Fetching v86 $V86_VERSION"
bash "$REPO_ROOT/scripts/fetch-v86.sh"

log "Composing $SITE_OUT"
rm -rf "$SITE_OUT"
mkdir -p "$SITE_OUT"

cp "$REPO_ROOT/site/index.html" "$SITE_OUT/index.html"
cp "$REPO_ROOT/site/style.css" "$SITE_OUT/style.css"
cp "$REPO_ROOT/site/favicon.svg" "$SITE_OUT/favicon.svg"
cp "$BUILD_DIR/mars.img" "$SITE_OUT/mars.img"
cp "$BUILD_DIR/MARS.COM" "$SITE_OUT/MARS.COM"
cp "$REPO_ROOT/MARS.ASM" "$SITE_OUT/MARS.ASM"
cp "$REPO_ROOT/mars_4_3.png" "$SITE_OUT/mars_4_3.png"
cp -R "$BUILD_DIR/v86" "$SITE_OUT/v86"

# GitHub Pages runs Jekyll by default, which strips paths beginning with a
# dot and can mangle asset directories. .nojekyll turns that off.
touch "$SITE_OUT/.nojekyll"

log "Site ready in $SITE_OUT ($(du -sh "$SITE_OUT" | cut -f1))"
Loading