python-pdp1170/boot.py
2023-10-17 22:09:33 -05:00

170 lines
5.5 KiB
Python

import time
from machine import PDP1170
from kw11 import KW11
from kl11 import KL11
from dc11 import DC11
from rp import RPRM
import breakpoints
def boot_hp(p, /, *, addr=0o10000, deposit_only=False):
"""Deposit, then run, instructions to read first 1KB of drive 0 --> addr.
RETURN VALUE: addr if deposit_only else None
If no 'addr' given, it defaults to something out of the way.
If not deposit_only (default):
* The instructions are loaded to 'addr'
* They are executed.
* Return value is None.
* NOTE: The next start address depends on what those instructions do.
* TYPICALLY, the next start address will be zero.
If deposit_only:
* The instructions are loaded to 'addr'
* 'addr' is returned.
"""
# this is the sort of thing that would be keyed in from
# the console switches (if the machine was not equipped
# with a boot rom option to hold it instead)
#
# It is a minimalist program, with lots of assumptions, to read 1K
# from block zero of drive 0 into location 0. The execution start
# at zero is done elsewhere.
#
# NOTE WELL: THIS ASSUMES THE MACHINE IS IN RESET CONDITION WHICH
# MEANS MANY OF THE DEVICE REGISTERS ARE ASSUMED TO BE ZERO
#
# MOV #176704,R0 -- note how used
# MOV #177000,-(R0) -- word count - read 1K though boot really 512
# MOV #071,-(R0) -- go!
program_insts = (
0o012700, # MOV #0176704,R0
0o176704,
0o012740, # MOV #177000,-(R0)
0o177000,
0o012740, # MOV #071, -(R0)
0o000071,
0o0, # HALT
)
for o, w in enumerate(program_insts):
p.physRW(addr + o + o, w)
p.r[p.PC] = addr
if not deposit_only:
p.run()
# at this point in real life the user would have to set the switches
# to deposit zero into the PC and then hit start; that takes time
# and means the bootstrap doesn't have to have code to wait for the
# drive to complete the read operation. Instead of adding code to
# the "pretend this was keyed in" program above, this delay works.
time.sleep(0.25)
return addr if deposit_only else None
def boot_bin(p, fname, /, *, addr=0, deposit_only=False,
little_endian=True, skipwords=8):
"""Read a binary file 'fname' into location 'addr' and execute it.
RETURN VALUE: addr if deposit_only else None
NOTE: fname is in the host system, not on an emulated drive.
If no 'addr' given, it defaults to ZERO.
If deposit_only=True, the instructions are not executed.
little_endian (default True) dictates the fname byte order.
skipwords (default 8 -- a.out header) will seek that many 16-bit
words into the file before beginning to load.
"""
with open(fname, 'rb') as f:
bb = f.read()
# Two data format cases:
# 1) little_endian (the default)
#
# The file is truly a binary image of pdp11 format data
# and the words (pairs of bytes) in bb are in little endian
# order. They will be assembled accordingly and the names
# "low" and "hi" make sense.
#
# 2) not little_endian
#
# Presumably the file has been byte-swapped already and
# the words (pairs of bytes) in it are in big endian order.
# They will be assembled accordingly, but the names "low"
# and "hi" are backwards.
#
xi = iter(bb)
words = []
for low in xi:
hi = next(xi)
if little_endian:
words.append((hi << 8) | low)
else:
words.append((low << 8) | hi) # see case 2) above
for a, w in enumerate(words[skipwords:]):
p.physmem[a] = w
p.r[p.PC] = addr
if not deposit_only:
p.run()
return addr if deposit_only else None
def make_unix_machine(*, loglevel='INFO', drivenames=[]):
p = PDP1170(loglevel=loglevel)
p.associate_device(KW11(p.ub), 'KW') # line clock
p.associate_device(KL11(p.ub), 'KL') # console
p.associate_device(RPRM(p.ub, *drivenames), 'RP') # disk drive
p.associate_device(DC11(p.ub), 'DC') # additional serial poirts
return p
def boot_unix(p, runoptions={}):
# load, and execute, the key-in bootstrap
boot_hp(p)
print("Starting PDP11; this window is NOT THE EMULATED PDP-11 CONSOLE.")
print("*** In another window, telnet/nc to localhost:1170 to connect.")
print(" Terminal should be in raw mode. On a mac, this is a good way:")
print(" (stty raw; nc localhost 1170; stty sane)")
print("")
print("There will be no prompt; type 'boot' in your OTHER window")
print("")
print("Then, at the ':' prompt, typically type: hp(0,0)unix")
p.run(pc=0, **runoptions)
# USE:
# python3 boot.py
#
# to start up unix (or whatever system is on the drive)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--debug', action='store_true')
parser.add_argument('--drive', action='append', default=[], dest='drives')
parser.add_argument('--instlog', action='store_true')
args = parser.parse_args()
pdpoptions = {'drivenames': args.drives}
runoptions = {}
if args.debug:
pdpoptions['loglevel'] = 'DEBUG'
if args.instlog:
runoptions['breakpoint'] = breakpoints.Logger()
p = make_unix_machine(**pdpoptions)
boot_unix(p, runoptions=runoptions)