Running a Multiuser PDP-8 Emulation as a Service

Running a Multiuser PDP-8 Emulation as a Service

Created: 10th Sep 2026

Introduction

This article describes how to setup a PDP-8 emulation on a standalone Linux machine, running TSS/8 the muliuser operating system using real serial devices.

The Linux machine used in this example small footprint Lenova device running Fedora Linux 43 and open-simh v4.0-0. The machine has a single hardware serial port fitted.

In this example the console is set up for use with the hardware serial terminal and up to four users can connect using a Brainboxes four port USB to Serial device. These ports are connected to the SIMH emulated PDP-8 TTIX device.

Configuration

The pdp8.ini file shown below will produce the machine configuration specified below. Note that the machine is setup for running TSS/8 the muliuser operating system.

Actually connected and doing something:

Present (the controller/interface exists) but nothing loaded — the equivalent of an empty drive bay:

Not fitted at all (hardware options this particular machine doesn't have):

pdp8.ini

; ============================================================
; pdp8.ini — TSS/8 timesharing configuration
; ============================================================

; --- CPU ---
set cpu 32k		; 32KW of core memory (max addressable with MC8/KM8E extension)
set cpu eae		; enable Extended Arithmetic Element (KE8E) - hardware MUL/DIV
set cpu noidl		; disable host-CPU idle detection; TSS/8 is multi-user/interactive
; and its idle loop pattern isn't one SIMH auto-detects well

; TSC (TSC8-75) provides timeshare control for DEC's ETOS operating
; system - unrelated to TSS/8, which does its own multiplexing via
; the KM8E memory-extension hardware. Leave disabled for TSS/8.
set TSC disabled

; The FPP8A was a real add-on hardware coprocessor for floating-point math — it existed
; on some physical ; PDP-8/E systems, but it's a pure performance accessory. TSS/8 itself
; has no dependency on it; the OS boots and runs its monitor entirely without it. Whether
; you'd want it depends only on what user programs you plan to run under TSS/8:
;
;  BASIC-8, FOCAL    — do their own software floating point, don't call the FPP hardware at all.
;  FORTRAN D / ALGOL — these are the languages that could, in principle, use FPP-accelerated
;                      math on real hardware.
set FPP disabled

; --- Console teletype (TTI/TTO) ---
; KSR, uppercase-only, mark parity is the simulator default for TTI/TTO;
; set tti 7b            ; would allow lowercase/full 7-bit ASCII through

; --- Extra terminals (TTIX/TTOX) — the timesharing lines ---
; 4 lines, attached as a Telnet listener on port 4000 so additional users
; can log in as separate TSS/8 jobs
;attach ttix 4000

; 4 real RS232 terminals via USB-serial adapter, 1200 baud 7E1
; lines 0 and 1 have modem protocols enabled, lines 3 and 4 don't 
set ttix lines=4
attach -V ttix Line=0,modem,connect=/dev/pdp8-term1;1200-7e1
attach -V ttix Line=1,modem,connect=/dev/pdp8-term2;1200-7e1
attach -V ttix Line=2,nomodem,connect=/dev/pdp8-term3;1200-7e1
attach -V ttix Line=3,nomodem,connect=/dev/pdp8-term4;1200-7e1


; --- RF08 fixed-head disk — TSS/8's system/swap disk ---
set rf enabled
attach rf tss8_rf.dsk

; --- add a tape for use as a backup
attach ptp backup.pt

; --- add DEC tapes
attach dt0 tape0.tap
set dt0 writeenabled

attach dt1 tape1.tap
set dt1 writeenabled

attach dt2 tape2.tap
set dt2 writeenabled

attach dt3 tape3.tap
set dt3 writeenabled

; console detached from tty - admin-only telnet, separate from user lines
;set console telnet=6000
;set console telnet=buffered
; set console speed 1200

; serial console
set console serial=/dev/ttyS0;1200-7e1

; The set remote command opens a second listener purely for feeding SCP
; commands (like exit) to the simulator over the network, independent of
; the emulated machine's console. This is used by the Systemd service
; to cleanly shut down everything.
set remote telnet=6001

; --- Devices present but unused for this TSS/8 session ---
set df disabled        ; DF32 disk - not used by this TSS/8 image
set rl disabled        ; RL8A - not fitted in this configuration
; PTR/PTP/LPT/RK/RX/DT/MT/CT/DPY are left at their defaults (enabled,
; not attached) - TSS/8 doesn't need them for this boot, but they're
; available if you later want to attach paper tape, an RK05 pack, etc.

; automate the unattended boot sequence
expect "ETC?" send "START\r"; continue
expect "MONTH-DAY-YEAR" send "01-01-85\r"; continue
expect "HR:MIN" send "00:00\r"; continue

; --- Load the bootstrap and start ---
load tss8_init.bin
run 24200

Configuring Serial Ports

In the example described here, four serial ports have been added to the server by a four port serial port device made by Brainboses. And can be seen in the output of the lsusb command.

# lsusb

Bus 001 Device 004: ID 05d1:7031 Brainboxes, Ltd USB2Serial 4xRS232
Bus 001 Device 005: ID 05d1:7032 Brainboxes, Ltd USB2Serial 4xRS232
Bus 001 Device 006: ID 05d1:7033 Brainboxes, Ltd USB2Serial 4xRS232
Bus 001 Device 007: ID 05d1:7034 Brainboxes, Ltd USB2Serial 4xRS232

To ensure they all bound correctly to the appropriate driver (ftdi_sio in this case), and came up in a defined order, a udev rule was created.

This was placed un a file called /etc/udev/rules.d/99-brainboxes.rules.

# Brainboxes USB2Serial 4xRS232 — bind ftdi_sio and create stable symlinks
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="05d1", ATTRS{idProduct}=="7031", RUN+="/sbin/modprobe ftdi_sio" RUN+="/bin/sh -c 'echo 05d1 7031 > /sys/bus/usb-serial/drivers/ftdi_sio/new_id'"
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="05d1", ATTRS{idProduct}=="7032", RUN+="/sbin/modprobe ftdi_sio" RUN+="/bin/sh -c 'echo 05d1 7032 > /sys/bus/usb-serial/drivers/ftdi_sio/new_id'"
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="05d1", ATTRS{idProduct}=="7033", RUN+="/sbin/modprobe ftdi_sio" RUN+="/bin/sh -c 'echo 05d1 7033 > /sys/bus/usb-serial/drivers/ftdi_sio/new_id'"
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="05d1", ATTRS{idProduct}=="7034", RUN+="/sbin/modprobe ftdi_sio" RUN+="/bin/sh -c 'echo 05d1 7034 > /sys/bus/usb-serial/drivers/ftdi_sio/new_id'"

# Stable symlinks keyed to each port's unique serial number
SUBSYSTEM=="tty", ATTRS{idVendor}=="05d1", ATTRS{serial}=="R5CL0R2E", SYMLINK+="pdp8-term1", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/pdp8-term1"
SUBSYSTEM=="tty", ATTRS{idVendor}=="05d1", ATTRS{serial}=="R5UCARK7", SYMLINK+="pdp8-term2", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/pdp8-term2"
SUBSYSTEM=="tty", ATTRS{idVendor}=="05d1", ATTRS{serial}=="R5UCARK8", SYMLINK+="pdp8-term3", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/pdp8-term3"
SUBSYSTEM=="tty", ATTRS{idVendor}=="05d1", ATTRS{serial}=="R5CL0R2G", SYMLINK+="pdp8-term4", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/pdp8-term4"

This can be applied with the following.

sudo udevadm control --reload-rules
sudo udevadm trigger

The serial ports now turned up as

root@s1:/etc/udev/rules.d# ls -la /dev/pdp8-term*

lrwxrwxrwx 1 root root 7 Sep  9 19:26 help -> ttyUSB0
lrwxrwxrwx 1 root root 7 Sep  9 19:26 /dev/pdp8-term2 -> ttyUSB1
lrwxrwxrwx 1 root root 7 Sep  9 19:26 /dev/pdp8-term3 -> ttyUSB2
lrwxrwxrwx 1 root root 7 Sep  9 19:26 /dev/pdp8-term4 -> ttyUSB3

As a sanity check reboot the machine and then do a check e.g. Not that in my case the ttyUSB names have been assigned in a different order but SIMH TTIX has attached its connections to the correct ports.

root@s1:/home/john# ls -la /dev/pdp8-term*

lrwxrwxrwx 1 root root 7 Sep 10 13:43 /dev/pdp8-term1 -> ttyUSB0
lrwxrwxrwx 1 root root 7 Sep 10 13:43 /dev/pdp8-term2 -> ttyUSB3
lrwxrwxrwx 1 root root 7 Sep 10 13:43 /dev/pdp8-term3 -> ttyUSB1
lrwxrwxrwx 1 root root 7 Sep 10 13:43 /dev/pdp8-term4 -> ttyUSB2
journalctl -u pdp8.service --since "13:43" | grep -i ttix

Sep 10 13:44:02 s1.glasstty.local pdp8[977]: ./tss8.ini-38> attach ttix Line=0,/dev/pdp8-term1;1200-7e1
Sep 10 13:44:02 s1.glasstty.local pdp8[977]: ./tss8.ini-39> attach ttix Line=1,/dev/pdp8-term2;1200-7e1
Sep 10 13:44:02 s1.glasstty.local pdp8[977]: ./tss8.ini-40> attach ttix Line=2,/dev/pdp8-term3;1200-7e1
Sep 10 13:44:02 s1.glasstty.local pdp8[977]: ./tss8.ini-41> attach ttix Line=3,/dev/pdp8-term4;1200-7e1

Running as a service

Create a pdp8 user and add to the dialout group.

# useradd --system --no-create-home --shell /sbin/nologin --comment "PDP-8 TSS/8 simulator" pdp8
# usermod -aG dialout pdp8

Create a unit file for the service as /etc/systemd/system/pdp8.service.

[Unit]
Description=PDP-8 TSS/8 timesharing simulator
After=network.target
After=dev-pdp8\x2dterm1.device dev-pdp8\x2dterm2.device dev-pdp8\x2dterm3.device dev-pdp8\x2dterm4.device
Wants=dev-pdp8\x2dterm1.device dev-pdp8\x2dterm2.device dev-pdp8\x2dterm3.device dev-pdp8\x2dterm4.device

[Service]
Type=simple
User=pdp8
Group=pdp8
WorkingDirectory=/opt/pdp8
ExecStart=/opt/pdp8/bin/pdp8 tss8.ini
ExecStop=/bin/sh -c 'printf "exit\r\n" | timeout 5 /usr/bin/nc localhost 6001'
Restart=on-failure
RestartSec=5
TimeoutStopSec=10
SupplementaryGroups=dialout

[Install]
WantedBy=multi-user.target

Start the service.

# systemctl start pdp8.service

Check its status

# systemctl status pdp8.service

If all looks OK and you can connect to the remte console (see below), then enable it so that it starts automatically on a system boot.

# systemctl enable pdp8.service

Connect to the Remote Console

From a local machine connect using TELNET e.g

$ telnet <server name/ip addreaa> 6001

You should see the following response.

Connected to the PDP-8 simulator REM-CON device

PDP-8 Remote Console
Enter single commands or to enter multiple command mode enter the ^E character
Simulator Running...

Entering the SHOW SERIAL command should return the following. Note that the SIMH> prompt only appears when the command is typed in. DO NOT use Ctrl-E to get to the prompt.

sim> show serial
Serial devices:
 ser0	/dev/pdp8-term1 (1200-7e1)
 ser1	/dev/pdp8-term2 (1200-7e1)
 ser2	/dev/pdp8-term3 (1200-7e1)
 ser3	/dev/pdp8-term4 (1200-7e1)
 ser4	/dev/ttyS0     
 ser5	/dev/ttyS4     
 ser6	/dev/ttyUSB0   
 ser7	/dev/ttyUSB1   
 ser8	/dev/ttyUSB2   
 ser9	/dev/ttyUSB3   
Open Serial Devices:
 TTIX	Ln00 /dev/pdp8-term1;1200-7e1 (1200-7e1)	Config: 1200-7e1
 TTIX	Ln-1 /dev/pdp8-term2;1200-7e1 (1200-7e1)	Config: 1200-7e1
 TTIX	Ln-2 /dev/pdp8-term3;1200-7e1 (1200-7e1)	Config: 1200-7e1
 TTIX	Ln-3 /dev/pdp8-term4;1200-7e1 (1200-7e1)	Config: 1200-7e1
 CON-TELNET	Ln00 /dev/ttyS0;1200-7e1	Config: 1200-7e1

Transferring Text Files to the Machine

Create a Paper Tape File

A paper tape file can be created using the txt2ptp utility e.g.

txt2ptp <lander.bas > lander.ptp

Attack the new file to the high speed reader e.g. (from SIMH> prompt or pdp8.ini file)

ATTACH lander.ptp PTR

Cut and Paste

Use character delay an line delay of around 30ms and 200ms respectively when sending text files from a terminal emulation program.

Transferring Directly

Alternatively text can be be send directctly using tio e.g.

cat /home/john/Downloads/LunarLander.txt | tio /dev/ttyS0 --baudrate 1200 --databits 7 --parity even --output-delay 30 --output-line-delay 200

The tio package can also be used as a serial terminal e.g.

tio /dev/ttyS0 --baudrate 1200 --databits 7 --parity even --output-delay 30 --output-line-delay 200