Neil Webber's emulator, adapted for json comparison
Find a file
Neil Webber 0c6256571a cleanup
2024-04-23 13:19:34 -05:00
boot.py pseudo-bkpt to look for bogus memory values outside 16 bit range 2024-04-23 13:16:55 -05:00
branches.py Clean up unused BRANCH_NAMES 2024-04-01 11:13:04 -05:00
breakpoints.py cleanup 2024-04-23 13:19:34 -05:00
dc11.py Revamp the unibus device callback system. New version has explicit BusCycle argument in one callback function vs the mishmash of time-evolved methods for reset, bytes, etc 2024-04-11 16:58:41 -05:00
interrupts.py improved prioritization 2023-09-18 21:15:20 -05:00
kl11.py Revamp the unibus device callback system. New version has explicit BusCycle argument in one callback function vs the mishmash of time-evolved methods for reset, bytes, etc 2024-04-11 16:58:41 -05:00
kw11.py Revamp the unibus device callback system. New version has explicit BusCycle argument in one callback function vs the mishmash of time-evolved methods for reset, bytes, etc 2024-04-11 16:58:41 -05:00
LICENSE Initial commit 2023-09-04 12:46:38 -05:00
machine.py accomodate octal formatting in machinestate for logging while still leaving raw values for, e.g., breakpoonts 2024-04-18 17:02:38 -05:00
mmu.py Revamp the unibus device callback system. New version has explicit BusCycle argument in one callback function vs the mishmash of time-evolved methods for reset, bytes, etc 2024-04-11 16:58:41 -05:00
NOTES updates 2023-11-01 09:19:36 -05:00
op00.py INC fix 2024-04-04 22:40:16 +02:00
op000.py According to 2024-03-29 15:00:57 +01:00
op4.py According to page 67 of PDP1120_Handbook_1972.pdf, BIC and BICB shall 2024-03-28 22:56:11 +01:00
op07.py missing import and fix _shifter when NN=0 per 1981 book. Issue Misc #10 2024-04-01 07:56:22 -05:00
op10.py First release 2023-09-04 12:49:58 -05:00
pdpasmhelper.py safeguard literal from allowing bad values into physmem 2024-04-23 08:00:33 -05:00
pdptests.py add check16 to verify no larger than 16 bit values got generated; also reduce footprint of physmem in most cases because of this check 2024-04-23 08:40:51 -05:00
pdptraps.py First release 2023-09-04 12:49:58 -05:00
README.md Acknowledgments 2024-04-01 10:21:51 -05:00
rp.py Revamp the unibus device callback system. New version has explicit BusCycle argument in one callback function vs the mishmash of time-evolved methods for reset, bytes, etc 2024-04-11 16:59:06 -05:00
unibus.py Revamp the unibus device callback system. New version has explicit BusCycle argument in one callback function vs the mishmash of time-evolved methods for reset, bytes, etc 2024-04-11 16:58:41 -05:00

python-pdp11-emulator

Yet another PDP-11 emulator, written in Python. Can boot/run V7 Unix!

Why, oh why??

Let's be clear - SIMH is better in every possible way. And there are also other PDP-11 emulator implementations out there, some in python. I did this as a self-educational project and because I thought it would be fun.

It was educational.

It was fun.

If you like it, great. If you think it is silly, so it goes.

ACKNOWLEDGEMENTS

Special thanks to Folkert van Heusden (https://github.com/folkertvanheusden) for looking at the instructions (especially the condition code results) in detail and finding many bugs.

MORE NOTES

See NOTES file for running commentary on status, etc.

RUNNING A BARE PDP-11

Do it like this:

% python3
>>> from machine import PDP1170
>>> p = PDP1170()

At this point how you jam instructions into memory is up to you.

The PDP-11 registers are in attribute r (e.g., p.r[0] to see R0).

Memory is best read/written using p.mmu.wordRW:

 value = p.mmu.wordRW(some-address)       # reads some-address
 p.mmu.wordRW(some-address, value)        # writes value to some-address

For example:

>>> from machine import PDP1170
>>> p = PDP1170()
>>> p.mmu.wordRW(0o10000, 0o010203)
4227
>>> p.mmu.wordRW(0o10002, 0)
0
>>> p.r[7] = 0o10000
>>> p.r[2] = 2
>>> p.r
[0, 0, 2, 0, 0, 0, 0, 4096]
>>> p.run()
>>> p.r
[0, 0, 2, 2, 0, 0, 0, 4100]

showing a program: MOV R2, R3 / HALT and running it and showing the before and after (as the "2" is copied from R2 to R3)

To boot UNIX:

  • Obtain a disk image. I am using hp.disk0 from the unix7 subdirectory of the systems directory from SIMH.

  • name that file rp.disk ... the name is not configurable in the code yet.

  • You will need two separate shell windows. One will be the running python window, where not much happens once you get started. The other will be your emulated console. In the first window, type this:

    python3 boot.py

You may wish to examine 'boot.py' to see how all this works.

In the second window, you need to telnet to port 1170 to connect to the console emulation. It will be very helpful to be in raw terminal mode too. On the mac I do it like this:

% (stty raw; nc localhost 1170; stty sane)

in this window there will be no prompt. Send your complaints about that to (I assume) DMR's heirs, as you are running the raw 512-byte block-zero bootstrap code at this point from unix7.

Anyhow, there is no prompt. Type:

boot

you will then get a prompt:

Boot
:

If you have the SIMH hp.disk0 as your rp.disk, type "hp(0,0)unix"

I apologize for the hp/rp confusion. Perhaps I will clean this up in a future release.

You will see a single user shell prompt at this point if you are lucky!

Exit the single user shell (type a control-D) to get into multi-user mode. NOTE: The UNIX /etc/ttys file defaults to enabling four additional login lines but the emulation that is implemented (see dc11.py) simply makes them appear to the kernel as "no terminal connected". The (emulated) console is the only serial device supported so far.

Enjoy!

Understanding the code

I'll have to write more about this later; as a first step to getting around:

  • machine.py is the heart of the instruction loop and CPU basics.
  • Actual machine instructions have been split off - start with op4.py and trace the opparsing/dispatch from there.
  • mmu.py implements, surprisingly enough, the PDP-11 MMU system.
  • mmio.py and unibus.py (mostly a stub) handle the memory mapped I/O.
  • rp.py is a primitive disk emulation, good enough for unix.
  • kl11.py is the console
  • kw11.py is the line clock

Note that the disk operations are synchronous (not threaded/asyncio). I have tried going async and it doesn't seem to make much difference. Not sure why just yet but it works well enough in synchronous mode (in other words - when Unix commands a read, the read happens right then and no emulated instructions happen "during" the read ... then the interrupt is fired and unix continues, as if the disk were infinitely fast because no instruction cycles elapsed between GO and the interrupt).

tests

Some (somewhat trivial) unit tests:

python3 pdptests.py

TODO

Known areas that need work include:

  • Need to emulate more devices, especially a DL-11
  • The UNIBUS address space and especially the UBA system is a stub. The disk drive is, in effect, emulated as being on the Massbus and uses its BAE register (which the unix driver sets accordingly) for the 22-bit physical address extension bits.
  • No floating point instructions implemented; not sure how important they are. They were an option, so presumably all code can deal with them not being present.
  • I am not at all convinced I have all the subtleties of traps, synchronous traps, aborts, interrupts, etc correct. I have it good enough for what Unix expects.
  • Explore more regarding asynchronous disk I/O.
  • When running unix, there's an inexplicable pause that happens fairly often. I have ruled out gc as a cause. I am suspicious this has something to do with the reduced clock rate, or a subtle semantic of interrupts/traps/etc.