- Zig 52%
- Nix 25%
- Python 23%
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-configand 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.
besm6is 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
frontpaneltestprogram, and theBuildROMstool. The ROM headers thatBuildROMswould 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.
- Ollie, J. C. (n.d.). zon2nix [Zig]. Retrieved September 22, 2026, from https://github.com/jcollie/zon2nix
- Open SIMH Project. (2026). Open SIMH: The computer history simulation project (Version 87eb7d5e96f9ce0ee6ac183e20160e5c486b0712) [C]. Retrieved October 8, 2026, from https://github.com/open-simh/simh
- The Tcpdump Group. (2026). libpcap: A portable C/C++ library for network traffic capture (Version 1.11.0) [C]. Retrieved October 8, 2026, from https://github.com/the-tcpdump-group/libpcap
- Zig Software Foundation. (2026). Zig 0.17.0: lib/std/Build.zig (Version 0.17.0) [Zig]. https://codeberg.org/ziglang/zig/src/tag/0.17.0/lib/std/Build.zig
- Zig Software Foundation. (2026). Zig 0.17.0: lib/std/Build/Step/Run.zig (Version 0.17.0) [Zig]. https://codeberg.org/ziglang/zig/src/tag/0.17.0/lib/std/Build/Step/Run.zig
- Zig Software Foundation. (2026). Zig 0.17.0: lib/std/Build/Step/WriteFile.zig (Version 0.17.0) [Zig]. https://codeberg.org/ziglang/zig/src/tag/0.17.0/lib/std/Build/Step/WriteFile.zig
- Zig Software Foundation. (2026). Zig 0.17.0: lib/std/Io/Dir.zig (Version 0.17.0) [Zig]. https://codeberg.org/ziglang/zig/src/tag/0.17.0/lib/std/Io/Dir.zig