diff --git a/boot.py b/boot.py index 726da58..a42b53f 100644 --- a/boot.py +++ b/boot.py @@ -1,6 +1,27 @@ +import time +from machine import PDP1170 +from kw11 import KW11 +from kl11 import KL11 +from rp import RPRM -def boot_hp(p, /, *, addr=0o10000): - """Read the first 1KB of drive 0 into location 'addr'.""" + +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 @@ -29,12 +50,33 @@ def boot_hp(p, /, *, addr=0o10000): for o, w in enumerate(program_insts): p.physRW(addr + o + o, w) - return addr + 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_file(p, fname, /, *, addr=0, little_endian=True, skipwords=8): - """Read a raw binary image of pdp11 data into location 'addr'.""" +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() @@ -65,20 +107,50 @@ def boot_file(p, fname, /, *, addr=0, little_endian=True, skipwords=8): for a, w in enumerate(words[skipwords:]): p.physmem[a] = w - return p + p.r[p.PC] = addr + if not deposit_only: + p.run() + return addr if deposit_only else None + + +def boot_unix(p=None, loglevel='INFO'): + + if p is None: + p = PDP1170(loglevel='INFO') + + p.associate_device(KW11(p.ub), 'KW') # line clock + p.associate_device(KL11(p.ub), 'KL') # console + p.associate_device(RPRM(p.ub), 'RP') # disk drive + + # 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) + + +# USE: +# python3 boot.py +# +# to start up unix (or whatever system is on the drive) if __name__ == "__main__": - import time - from machine import PDP1170 - p = PDP1170(loglevel='INFO') - pc = boot_hp(p) - print("starting PDP11; telnet/nc to localhost:1170 to connect to console") - print("There will be no prompt; type 'boot' to start boot program") - p.run(pc=pc) - # technically need to confirm the drive is RDY, i.e., the read - # completed, but using a delay is a lot simpler and works fine. - # In real life, humans would have manipulated console switches to - # start execution at location 0, which is also a source of delay. :) - time.sleep(0.05) - p.run(pc=0) + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument('--debug', action='store_true') + args = parser.parse_args() + + if args.debug: + boot_unix(loglevel='DEBUG') + else: + boot_unix()