Open SIMH's computer history simulators, built with the Zig build system

README.md

simh #

Open SIMH, the computer history simulators, built with the Zig build system.

This project builds all 79 of Open SIMH's simulators, from the PDP-1 to the VAX 8600 and the IBM 650 to the AT&T 3B2, with zig build in place of upstream's makefile and CMake. Upstream's source is pulled in as a Zig package dependency, pinned to one commit. Nothing is vendored here and nothing is patched.

Source #

git clone https://git.jcollie.dev/jeff/simh.git

It is mirrored on Tangled at https://tangled.org/jcollie.dev/simh, and on Radicle as rad:z3JiuVAJRjprhsKfBqKfJycWW1D9Z:

rad clone rad:z3JiuVAJRjprhsKfBqKfJycWW1D9Z

Requirements #

  • Zig 0.17
  • Optional, each one found through pkg-config and each one able to be switched off (see Build options):
    • SDL2, libpng and zlib, for the graphical displays
    • PCRE2 and zlib, for regular expressions in EXPECT
    • libedit, for command line editing and history
    • libvdeplug, for VDE networking (off unless asked for)

Zig compiles the C itself, so no separate C compiler is needed. libpcap is not needed to build: simh loads it with dlopen when a network device is attached to a host interface, and its headers come from the libpcap source named in build.zig.zon.

Building #

zig build

Every simulator lands in zig-out/bin/, under upstream's names: vax, pdp11, pdp10-ka, i650, altairz80 and so on. As upstream does, the VAX simulator is installed a second time as microvax3900.

To build just one or a few, name them:

zig build -Dsimulator=vax -Dsimulator=pdp11

Each simulator has a run step, with anything after -- passed through:

zig build run-pdp11 -- boot.ini

Tests #

zig build test            # all of them
zig build test-vax        # one

Each simulator is tested the way upstream's CTest does it: the register sanity check, then the simulator's own script from its tests directory, if it has one. 27 simulators have a script, and those exercise real software: the VAX tests run DEC's hardware diagnostics, and the i650 test assembles and runs programs from punched card decks.

Some scripts write files beside themselves, so each simulator directory a test uses is first copied into the build cache by tools/copy_tree.zig. The scripts run on that copy and never touch the fetched package.

Build options #

Option Default Description
-Dsimulator=NAME all Build only this simulator; may be given more than once.
-Dvideo true Graphical displays, with SDL2 and libpng.
-Dregex true Regular expressions in EXPECT, with PCRE2 and zlib.
-Deditline true Command line editing and history, with libedit.
-Dnetwork true Ethernet device support. Turning it off turns off the four below.
-Dpcap true Host Ethernet through libpcap, loaded at run time.
-Dslirp true Built-in user-mode NAT networking.
-Dtap true on Linux TUN/TAP networking.
-Dvde false Virtual Distributed Ethernet, with libvdeplug.
-Daio true Asynchronous I/O on threads, which the network devices want.
-Dsanitize false Compile the C with the undefined behavior sanitizer. simh relies on wrapping arithmetic and shifts, so expect it to stop almost at once.
-Doptimize=... Debug Standard Zig optimization mode, e.g. ReleaseSafe or ReleaseFast.
-Dtarget=... native Standard Zig cross-compilation target.

Only Linux has been built and tested. build.zig has the macOS and Windows differences that upstream's CMake has, but neither has been tried.

SHOW VERSION reports the upstream commit, which GitHub's archive fills in through export-subst, and Build Tool: Zig 0.17.0.

What is left out #

  • BESM6's panel display. Upstream compiles the BESM6 panel only when CMake finds a Cyrillic TrueType font on the build machine and bakes its path into the binary, and that cannot be reproducible. besm6 is built without it, just as upstream builds it when no font is found.
  • Upstream's optional extras: the PDP-10 KA panel lights and PiDP-10 front panel, the Windows GUI resources for the IBM 1130, the frontpaneltest program, and the BuildROMs tool. The ROM headers that BuildROMs would write are already in upstream's tree.

How the build is put together #

simulators.zon is the table of simulators: each one's sources, include directories, definitions, the features it asks for, and its test script. Upstream generates a CMakeLists.txt in each simulator directory from its makefile, and tools/generate.py reads those back into this table. Following upstream is therefore three steps: move the pin in build.zig.zon, then regenerate the table from the fetched package and the Nix expression from the manifest.

url=https://github.com/open-simh/simh/archive/<commit>.tar.gz
nix develop -c zig fetch --save=simh "$url"
nix develop -c zig build --fetch
python3 tools/generate.py zig-pkg/"$(nix develop -c zig fetch "$url")" > simulators.zon
nix develop -c zon2nix --17 --nix=build.zig.zon.nix build.zig.zon

zig build --fetch unpacks the dependencies into zig-pkg/, each in a directory named by its hash, and zig fetch without --save prints that hash.

build.zig follows upstream's CMake in building the simulator framework (scp.c and the sim_*.c device libraries) as a static library once for each combination of 64-bit data, 64-bit addresses, video and asynchronous I/O that some simulator asks for. Each simulator is linked against the one it needs. The built-in SLiRP NAT stack is one more static library, shared by all of them.

Upstream's own compiler settings carry over: -std=gnu99, -U__STRICT_ANSI__ and -fno-strict-overflow. Zig adds two of its own that have to be undone. It turns on the undefined behavior sanitizer in Debug, which simh trips immediately, so the sanitizer is off unless -Dsanitize asks for it. And it makes __DATE__ and __TIME__ an error in release builds, which SHOW VERSION uses. That error is switched off. Clang takes both values from SOURCE_DATE_EPOCH when that is set, as it is in a Nix build, so the result is still reproducible there.

Nix #

nix build            # every simulator, with zig build test in the check phase
nix develop          # Zig 0.17, pkg-config, the libraries, zon2nix and reuse

The package builds with Zig's --system mode, using the dependencies that zon2nix wrote out to build.zig.zon.nix, so the build itself fetches nothing. Zig 0.17 comes from nixpkgs.

License #

The build files here are under the MIT license and follow the REUSE specification. Open SIMH itself, which is fetched and not included, is under its own MIT-style license.

References cited #

The entries are kept in the simh collection in Zotero.