A PDP-11/40 emulator and a small compiler (oneshot) for a C-like language targeting it.
Milestone: ED (an early version of the the standard Unix text editor) runs on bare metal via paper tape.
Next: Running Unix itself.
Emulates the PDP-11/40 processor with:
Instructions: MOV, ADD, SUB, CMP, BIT, BIC, BIS, CLR, COM, INC, DEC, NEG, TST, ASL, ASR, ROL, ROR, SWAB, ADC, SBC, MUL, DIV, ASH, ASHC, XOR, JMP, JSR, RTS, RTI, SOB, all branch instructions (BR, BNE, BEQ, BPL, BMI, BCC, BCS, BVC, BVS, BGE, BLT, BGT, BLE, BHI, BLOS), TRAP, EMT, IOT, HALT, WAIT, RESET, SXT, MARK, condition code ops
Addressing modes: Register, register deferred, autoincrement, autodecrement, index, and their deferred variants
Devices: Paper tape reader/punch, teletype (TTY) with interrupt support
./pdp1140 [OPTIONS]
-f FILE load program from file
-a ADDR load address (octal/hex)
-r ADDR run address
-u unlimited cycles
-t FILE paper tape input
-p FILE paper tape output
-i FILE TTY input
-o FILE TTY output
-d debug output
-m dump memory on exit
Compiles a C-like language to PDP-11 machine code. A non-optimizing BCPL inspired language made for throwaway bootstrap code. Hence 'oneshot'.
Types: word_t, byte_t
Statements: variable declarations, const, assignment, deref assignment (@addr = val), if/else, while, return, function calls, inline assembly
Expressions: arithmetic (+, -, *, /, mod), bitwise (&, |), logical (&&, ||), comparison (==, <, >), dereference (@), casts, function calls, strings
Literals: decimal, octal (0o777), character ('a')
./1shot.pl [-i FILE] [-o FILE] [--ast|--checked|--ir|--resolved]
Example:
word_t main() {
word_t x;
x = 42;
return x;
}Inline assembly:
[mov(0o177566, r1), movb(65, at(r1))]Program structure:
0o177776 +------------------+ <- initial SP (highest address)
| (stack) |
| (grows down) |
| |
| v |
+------------------+ <- SP (current stack pointer)
| |
| (free space) |
| |
+------------------+
| string literals |
+------------------+
| user code |
+------------------+
load addr +------------------+
| _start: |
| mov #0o177776,sp|
| jsr pc,main |
| halt |
+------------------+
| ... |
+------------------+
| interrupt vectors|
0o000000 +------------------+ (lowest address)
Stack frame (per function):
higher addresses
^
|
0o177776 +------------------+
| arg N | 4+2*(N-1)(r5)
| ... |
| arg 2 | 6(r5)
| arg 1 | 4(r5)
+------------------+
| return address | 2(r5) (pushed by JSR)
+------------------+
| saved r5 | 0(r5) <-- r5 points here
+------------------+ <- r5 (frame pointer)
| local 1 | -2(r5)
| local 2 | -4(r5)
+------------------+ <- sp
lower addresses
|
v
Calling convention:
- Arguments pushed right-to-left
- Return value in r0
- Caller cleans up arguments
- r5 is frame pointer, saved/restored by callee
make
# uses stdin and stdout for default i/o
# oneshot will compile the absolute loader
# the emulator will load the bytecode at 020000 and start running there
# the absolute loader will start reading tapes/ed.abs and load it into memory
# then when its done, jump to the start address of tapes/ed.abs, you should see a *I printout
./oneshot/1shot.pl -i programs/absolute.1 | \
./_build/bin/pdp1140 --load-addr 020000 --run-addr 020000 --tape-in tapes/ed.abs -u -mI just got this running, the first real bare metal paper tape program that runs and actually does something, but i have no idea what the keybindings are. You can switch modes, if you mess around. Your terminal will be blocked if you run this, as I'm reusing the pipes and selecting on raw inputs to use as teletype interrupts.
- No MMU (Memory Management Unit) - Unix requires kernel/user mode separation and virtual address translation
- No segmentation registers or page tables
- No memory protection (supervisor vs user mode)
- MFPI/MTPI (move from/to previous instruction space)
- MFPD/MTPD (move from/to previous data space)
- SPL (set priority level) - partially handled via PSW writes
- MTPS/MFPS (move to/from processor status) for 11/34+ compatibility
- No RK11/RK05 disk controller
- No RL11/RL02 disk controller
- No TM11 tape drive
- No line clock (KW11-L) for scheduling interrupts
- No separate kernel/user stack pointers
- No previous mode tracking in PSW
- Bus error handling is minimal
- No UNIBUS map for 18-bit addressing