incorporate booting LDA

This commit is contained in:
Neil Webber 2024-04-14 12:13:08 -05:00
parent 2ef5d03a8c
commit 496f962c6c

79
boot.py
View file

@ -4,11 +4,17 @@ from kw11 import KW11
from kl11 import KL11 from kl11 import KL11
from dc11 import DC11 from dc11 import DC11
from rp import RPRM from rp import RPRM
import breakpoints import breakpoints
def boot_hp(p, /, *, addr=0o10000, deposit_only=False): bootmsg = """Starting PDP11; this window is NOT THE EMULATED PDP-11 CONSOLE.
*** In another window, telnet/nc to localhost:1170 to connect.
Terminal should be in raw mode. On a mac, this is a good way:
(stty raw; nc localhost 1170; stty sane)
"""
def boot_hp(p, /, *, addr=0o10000, deposit_only=False, switches=0):
"""Deposit, then run, instructions to read first 1KB of drive 0 --> addr. """Deposit, then run, instructions to read first 1KB of drive 0 --> addr.
RETURN VALUE: addr if deposit_only else None RETURN VALUE: addr if deposit_only else None
@ -57,13 +63,6 @@ def boot_hp(p, /, *, addr=0o10000, deposit_only=False):
if not deposit_only: if not deposit_only:
p.run() 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 return addr if deposit_only else None
@ -164,6 +163,21 @@ def load_lda_f(p, f):
Any file format errors or I/O errors will raise an exception. Any file format errors or I/O errors will raise an exception.
""" """
# The file is (should be) a sequence of individual blocks (see
# comments in get_lda_block()). The last block must have no data.
while True:
addr, b = get_lda_block(f)
if len(b) == 0:
return addr
_byte_phys_write(p, b, addr)
def get_lda_block(f):
"""Read next block from LDA file. Return tuple: addr, b
If the block was an END block, len(b) will be zero.
"""
# Archived DEC documentation says an LDA/absolute loader file may start # Archived DEC documentation says an LDA/absolute loader file may start
# with an arbitrary number of zero bytes. SIMH says that ANY block # with an arbitrary number of zero bytes. SIMH says that ANY block
# (not just the first) may have arbitrary zeros in front of it, and # (not just the first) may have arbitrary zeros in front of it, and
@ -191,34 +205,24 @@ def load_lda_f(p, f):
# The 'block length' includes the header bytes but not the checksum. # The 'block length' includes the header bytes but not the checksum.
# Thus the lengthof [DATA] is six less than the block length given. # Thus the lengthof [DATA] is six less than the block length given.
# Note that the [DATA] length is allowed to be odd, or zero. # Note that the [DATA] length is allowed to be odd, or zero.
#
# If the [DATA] length is zero (block length 6) the block is
# an "END" block that terminates the format. The address indicated
# is the "start address" and is returned.
while True: header = _must_read_n(f, 6, zeroskip=True)
header = _must_read_n(f, 6, zeroskip=True) if header[0] != 1:
if header[0] != 1: raise ValueError(f"header starts with {header[0]} not 1")
raise ValueError(f"header starts with {header[0]} not 1")
count = (header[3] << 8) | header[2] count = (header[3] << 8) | header[2]
addr = (header[5] << 8) | header[4] addr = (header[5] << 8) | header[4]
if count < 6: if count < 6:
raise ValueError(f"header error, {count=}") raise ValueError(f"header error, {count=}")
b = _must_read_n(f, count-6) b = _must_read_n(f, count-6)
chksum = _must_read_n(f, 1) chksum = _must_read_n(f, 1)
if (sum(header) + sum(b) + sum(chksum)) & 0xFF: if (sum(header) + sum(b) + sum(chksum)) & 0xFF:
raise ValueError(f"checksum mismatch, {header=}") raise ValueError(f"checksum mismatch, {header=}")
if count == 6: # END block with no data return addr, b
return addr
_byte_phys_write(p, b, addr)
assert False, "unreachable"
def boot_lda(p, fname, /, *, force_run=True): def boot_lda(p, fname, /, *, force_run=True):
@ -243,6 +247,7 @@ def boot_lda(p, fname, /, *, force_run=True):
return rawaddr return rawaddr
addr = rawaddr - 1 addr = rawaddr - 1
print(bootmsg)
p.run(pc=addr) p.run(pc=addr)
return rawaddr return rawaddr
@ -262,11 +267,7 @@ def boot_unix(p, runoptions={}):
# load, and execute, the key-in bootstrap # load, and execute, the key-in bootstrap
boot_hp(p) boot_hp(p)
print("Starting PDP11; this window is NOT THE EMULATED PDP-11 CONSOLE.") print(bootmsg)
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("There will be no prompt; type 'boot' in your OTHER window")
print("") print("")
print("Then, at the ':' prompt, typically type: hp(0,0)unix") print("Then, at the ':' prompt, typically type: hp(0,0)unix")
@ -286,6 +287,7 @@ if __name__ == "__main__":
parser.add_argument('--debug', action='store_true') parser.add_argument('--debug', action='store_true')
parser.add_argument('--drive', action='append', default=[], dest='drives') parser.add_argument('--drive', action='append', default=[], dest='drives')
parser.add_argument('--instlog', action='store_true') parser.add_argument('--instlog', action='store_true')
parser.add_argument('--lda', action='store', default=None)
args = parser.parse_args() args = parser.parse_args()
pdpoptions = {'drivenames': args.drives} pdpoptions = {'drivenames': args.drives}
@ -297,4 +299,7 @@ if __name__ == "__main__":
p = make_unix_machine(**pdpoptions) p = make_unix_machine(**pdpoptions)
boot_unix(p, runoptions=runoptions) if args.lda:
boot_lda(p, args.lda)
else:
boot_unix(p, runoptions=runoptions)