15 KiB
ILDEC: a highly portable minimal DEC PDP-8/L emulator
About
ILDEC (pronounced roughly like ill-deck) is a project aimed at creating a working and practical PDP-8 platform emulator in as little amount of code as possible for as many popular programming runtimes as possible.
The name itself doesn't have an official meaning. Common interpretations include but are not limited to:
- "Import, Load, Deposit, Examine, Continue" (5 most commonly used interactive commands in the emulator)
- "Interpreted/Interactive Land of DEC/Loader for DEC"
- "Inexpensive, Lazy DEC"
- "In Lieu of DeCastro" (PDP-8/L was the last of his designs before leaving for Data General)
- "It Lives through Digital Environmental Collapses" (see the FAQ section)
- "Instruction Logic to Ditch Extra Complexity"
- "Inefficient Luxferre's Decision to Educate Consumers"
- ... and so on.
Supported PDP-8 implementations
ILDEC is most closely emulating the unextended version of PDP-8/L (and, to some extent, PDP-8/S), as it implements the absolute minimum required to run OS-less PDP-8 software packages like FOCAL-69. As a deliberate design decision, it does not support extensions like memory bank switching, EAE (Extended Arithmetic Element) with MQ (Multiplier/Quotient) register, or any device controls other than interrupts, teleprinter and keyboard.
See the ISA documentation for all the details about the supported registers, memory and instruction set. This document may also be very helpful if you want to design your own PDP-8 emulator from scratch or just port ILDEC to a new runtime.
Implementations
This is the list of all known implementations of the ILDEC specification, both the author's and third-party.
The list is divided into four categories: CLI, GUI (including Web), embedded and library-only implementations. In case an implementation can be used in several niches, it may appear in several categories.
Forks and mods are allowed and even encouraged!
Official Python implementation: ildec.py
- Categories: CLI, Embedded, Library
- Compatible with: CPython, MicroPython, CircuitPython (only tested on a LilyGO T-Deck Plus in the T-DeckARD environment, see below for installation details), Brython (as a library)
Usage
Depending on the implementation, the way to interact with ILDEC may differ, but generally all implementations that aren't library-only must expose the same set of commands.
CLI implementations
In case of CLI, the emulator must accept the following optional command-line parameters (in this very order):
- preloaded tape file name (string, default `` (empty)) in the
.octformat (see below) - starting PC (program counter) address (4 digits, octal, default
0200) - starting SR (switch register) state (4 digits, octal, default
0000)
Upon starting, the emulator must present an optional welcome banner, then the initial machine state (see below), then a DOS-like prompt (> ), and accept either a single 4-digit octal number as a command (and update the machine state by changing the SR (switch register) value) or any of the following (case-insensitive) single-character commands:
l: load the SR value into the PC registerd: deposit the value in SR under the memory address which is in PC, then run the examine command (which auto-increments PC)e: examine (display) the value at the current address in PC, then increment PCc: continue/start running the program starting at the address in PCs: step through one instruction at a time (execute it and then increment PC)r: reset the machine (clear every internal state and register except the memory contents, then restore the initial PC and SR values if they were provided upon emulator start)n: (new, nuke) fully clear the memory contents, effectively reinitializing the emulator from scratchi: import a tape in the.octformat into the machine memoryx: export current machine memory into the.octformat (zero-valued locations are not exported)q: quit the emulator.
Additionally, you may press Ctrl+D (or Shift+Vol on the T-Deck) to halt execution at any time and return to the emulator command interface.
After running any command and/or halting execution, the emulator must print a new machine state before printing the prompt. The state looks like this:
PC: [PC] | L AC: [L] [AC] | SR: [switches]
As an example, this is the initial state that gets displayed when starting a CLI implementation of ILDEC with no parameters:
PC: 0200 | L AC: 0 0000 | SR: 0000
And this is how the state should change after successfully running the bundled test routine which prints 31 dots when all tests pass:
> i
File path: soft/test.oct
Memory imported from soft/test.oct
PC: 0200 | L AC: 0 0000 | SR: 0000
> c
...............................
PC: 1376 | L AC: 0 0000 | SR: 0000
>```
### GUI/Web implementations
While there's no unified way of doing this, any GUI implementation of ILDEC, including Web-based ones, must fully provide functional equivalents of all interactive commands, parameters and displays mentioned in the CLI section.
The designs may vary from just embedding the same CLI into a web page to full graphical simulation with switches and blinkenlights.
### Embedded implementations
In case the embedded target platform provides any sort of serial terminal interface, the emulator must implement the same UI as for the CLI applications, with the exception of preloading the initial state from command-line parameters.
Here's how to install and run ILDEC as an applet on the [T-DeckARD](https://codeberg.org/luxferre/t-deckard) CircuitPython-based environment for LilyGO T-Deck (Plus):
1. Mount your CircuitPython device volume.
2. Create a directory for your PDP-8 tapes in the device volume root, `data/ildec` is the recommended name but you're free to choose any other.
3. Copy `python/ildec.py` from this repo into the `app/` directory on the device.
4. Copy all the `soft/*.oct` files from this repo into the directory you created on step 2 (e.f. `data/ildec`).
5. Unmount and eject the volume.
Now, from the T-DeckARD REPL, you can import the emulator with `from app.ildec import ildec` and then run it by typing `ildec()`, optionally passing the parameters in the form `(filename, PC, SR)`, e.g. `ildec("data/ildec/test.oct", 0o200, 0o1234)` to preload the tape, specify the starting PC and switch register state.
From the interpreter, you can use all the same commands as the usual CLI, including `i` for import and `x` for export.
### Library-only implementations
A non-UI implementation of ILDEC must offer the user a way to inspect and customize the emulator state (memory, PC, AC, L, SR) on the fly and expose methods for continuous running, stepping through and resetting the machine. Also, if possible, a method to load `.oct` tape images is recommended to implement.
## Tape format (`.oct`)
To load (and save) memory images, ILDEC uses an extremely simple `.oct` format that is purely ASCII plain text and resembles old `.lst` listing formats. Each line contains a pair of two 4-digit octal values, `[address]` and `[content]`, separated by a colon and (optional) whitespace: `[address]: [content]`. Everything after the `[content]` value up to the next newline must be ignored by the reader.
Here's a short example of a program starting from the address 0000 in the `.oct` format:
0000: 7301 0001: 7001 0002: 7430 0003: 7402 0004: 5001
To facilitate converting from popular `.bin` and `.rim` DEC tape formats into ILDEC's `.oct`, the `conv` directory of this repository contains two Python scripts: [bin2oct.py](conv/bin2oct.py) and [rim2oct.py](conv/rim2oct.py). It also contains a script called [oct2rim.py](conv/oct2rim.py) in case you need to export an `.oct` tape into other PDP-8 emulation environments as a `.rim` image.
## Bundled programs
ILDEC repo contains a few examples of PDP-8 software that you can try running on the emulator. For now, the following programs are bundled:
- [Test program](soft/test.oct): the one you should run first. Tests the entire emulation logic. Loads at the (default) address 0200. If the tests are successful, it must print 31 dots and halt at the PC address 1376.
- [Hellorld](soft/hellorld.oct): a program that just prints `HELLORLD!` and then halts. Tribute to [Usagi Electric](https://www.youtube.com/@UsagiElectric) (David Lovett).
- [FOCAL-69 interpreter](soft/focal69.oct): probably the most complete FOCAL programming language version that can run on an unextended PDP-8/L.
## FAQ
### Why bother with PDP-8 emulation in 2026?
Among the mass-produced, non-"esoteric" architectures, PDP-8 was stucturally one of the simplest. For instance, the bit-serial version, PDP-8/S, was made just with 519 logic gates. As a result, 1) this architecture, while being mainstream enough, is one of the easiest to rebuild from scratch in an event of technological collapse, 2) its instruction set (including all the essential IOT and OPR combos) is still smaller than the modern RISC-V (which is considered minimalist on its own). Combined with fixed instruction length (just like in RISC-V, by the way) and clean instruction bit layout (just unlike RISC-V, see e.g. the bit order for its `JAL` instruction), all this makes PDP-8 a perfect target for studying and emulation. ILDEC is a project of mine that serves both of these purposes: studying and emulation.
### What is the rationale behind this project then? Don't we already have [SIMH](https://github.com/simh/simh) / [OpenSIMH](https://opensimh.org/) and other good, accurate and well-documented emulators? Why only emulate the unextended (4K) PDP-8/L with the bare minimum of peripherals?
Most existing PDP-8 emulators are good enough for the purpose of historical software preservation, because they not only support all existing extensions and peripherals to run all the advanced stuff that was ever written for this hardware, but also replicate the full machine logic down to the original clock speeds if you want. And I think that's really cool and something that true retro fans will really appreciate, especially considering that the Open SIMH project is led by a former DEC engineer.
However, all that comes at a cost of increased complexity. SIMH code may be clean but it's just a lot to grasp. One of the main ILDEC's goals is to give you a **practical** (not necessarily historically accurate) recreation of the simplest commercially successful architecture of the past in the smallest possible amount of code while being as portable as possible. For example, the first ILDEC implementation was [written in Python](python/ildec.py) and contains just 191 SLOC, and that includes all the CLI interaction and some compatibility tweaks for MicroPython and CircuitPython + T-DeckARD runtimes. The core CPU logic (all supported instruction groups, indirection, autoincrements, interrupts, I/O etc) is well under 90 SLOC, and the code is very straightforward and understandable. In fact, using any implementation hosted in this repo as a reference, especially in combination with the [ISA document](pdp8_isa_ildec.md), is enough for anyone to recreate their own virtual PDP-8/L from scratch in a very short amount of time in their favorite programming language.
### Why isn't FORTRAN-4K compiler bundled?
While it says FORTRAN-4K, it doesn't make a lot of sense to run it on a 4K system. From the official documentation:
> Compiler and symbol table requires locations 0003-7577 (7574(8) locations)
So, you'll be only left with 0200 (128) words of memory. What will you compile with that?
Meanwhile, in FOCAL-69, if we believe the same official docs, you already have about 700 words free with all math functions enabled, but can unload logarithms, exponents and trigonometry if you don't need them and get up to 1100 words for the (tokenized) program, which is quite a lot.
### Where can I find more software for PDP-8/L?
The main problem with software collections for retro machines is that they don't always specify which exact models any particular program works on. Some trusted sources are:
- Mike Douglas's [DeRamp](https://deramp.com/downloads/mfe_archive/011-Digital%20Equipment%20Corporation/01%20DEC%20PDP-8%20Family%20Software/): contains a lot of relevant software and other useful materials for all PDP-8 generations.
- Vince Slyngstad's [so-much-stuff](https://so-much-stuff.com/pdp8/papertape/papertape.php): download the TAR archive from the link at the bottom, then use `labels.txt` file(s) for navigating through them. A lot of stuff there though either seems to be corrupted or requires more advanced hardware than PDP-8/L.
- [BitSavers](https://bitsavers.org/bits/DEC/pdp8/): not a lot, and most stuff also requires more advanced hardware, but still something.
### How do I write my own software for PDP-8/L?
Since this machine doesn't have any technical means of running an OS like DMS, TSS/8 or OS/8, your most viable options for writing programs for it are either FOCAL-69 (bundled in the repo) on the emulated machine itself, or some kind of PAL-III or MACRO-8 symbolic assemblers on the host side. I recommend two assembler options available nowadays.
The first (and mostly recommended) option is the `pal.c` single-file assembler, which you can download, build and install like this:
```sh
curl https://homepage.divms.uiowa.edu/~jones/pdp8/pal.txt > pal.c
cc -O2 -s -o pal pal.c
sudo cp pal /usr/local/bin/ # optional step
This assembler outputs .lst and .bin files. The latter can be fed into conv/bin2oct.py script to produce the .oct file loadable by ILDEC. Alternatively, you can generate a .oct file directly from the .lst file using AWK:
awk '$2~/^[0-7]+/{printf "%04d: %04d\n",$2,$3}' input.lst > output.oct
The second option is pdpnasm, but it's so minimal that it might not understand some I/O-specific mnemonics and you might have to use direct octal notation for them instead.
To convert pdpnasm's output .po files into ILDEC's .oct files, you can use a small helper shell script called po2oct.sh in this repo. Since both formats are plaintext, the conversion is fairly trivial.
What do I need to do if I want to port ILDEC to a yet unsupported platform?
Simple: first, implement the emulation core according to the ISA document, then implement the UI and .oct format support according to the above guidelines. Once your emulator is compatible with ILDEC on the ISA, command and format levels, you may fully call it an ILDEC port.
Credits
Created by Luxferre in 2026, released into the public domain with no warranties.
In memoriam Edson DeCastro (1938 – 2024).
Special thanks to Nino Ivanov for sharing a complete FOCAL-69 tape image.