Allow zero bytes between blocks in LDA

This commit is contained in:
Neil Webber 2024-04-11 16:40:07 -05:00
parent 6ef31cb034
commit 1eba151bdb

59
boot.py
View file

@ -117,14 +117,25 @@ def boot_bin(p, fname, /, *, addr=0, deposit_only=False,
return addr if deposit_only else None return addr if deposit_only else None
def _must_read_n(f, n): def _must_read_n(f, n, zeroskip=False):
"""read exactly n bytes from f; raise exception if can't. n==0 allowed.""" """read exactly n bytes from f; raise exception if can't. n==0 allowed.
if n == 0: # read(0) works but feels dirty If zeroskip is True (default: False), zero bytes will be discarded.
return bytes() It is not legal to zeroskip with n == 0.
b = f.read(n) """
if len(b) != n:
raise ValueError(f"needed {n} bytes; got {len(b)}") if zeroskip and n == 0:
raise ValueError("zeroskip and n == 0")
b = bytes()
if n > 0:
while zeroskip and (b := f.read(1)) == b'\00':
pass
# b has one byte or none in it, depending on zeroskip
b += f.read(n - len(b))
if len(b) != n:
raise ValueError(f"needed {n} bytes; got {len(b)}")
return b return b
@ -138,23 +149,30 @@ def _byte_phys_write(p, b, addr):
b = bytes([p.physRW(addr) & 255]) + b b = bytes([p.physRW(addr) & 255]) + b
if len(b) & 1: if len(b) & 1:
b += bytes([(p.physRW(addr+len(b)-1) >> 8) & 255]) b += bytes([(p.physRW(addr+len(b)-1) >> 8) & 255])
words = [(b[i+1] << 8) | b[i] for i in range(0, len(b), 2)] words = [(b[i+1] << 8) | b[i] for i in range(0, len(b), 2)]
p.physRW_N(addr, len(words), words) p.physRW_N(addr, len(words), words)
def load_lda_f(p, f): def load_lda_f(p, f):
"""Read open file f as an 'absolute loader' (.LDA, sometimes .BIC) file """Read and load open file f as an 'absolute loader' file
This is the same format that simh defines for its load (binary) command. (.LDA, sometimes .BIC file). This is the same format that simh
defines for its load (binary) command.
Returns: the address specified in the END block (per LDA docs) Returns: the address specified in the END block (per LDA docs)
Any file format errors or I/O errors will raise an exception. Any file format errors or I/O errors will raise an exception.
""" """
# An LDA/BIC/absolute loader file may start with an arbitrary number # Archived DEC documentation says an LDA/absolute loader file may start
# of zero bytes (possible w/paper tape). Any leading zero bytes will be # with an arbitrary number of zero bytes. SIMH says that ANY block
# ignored. After that it should be repeated blocks: # (not just the first) may have arbitrary zeros in front of it, and
# testing reveals that SIMH indeed allows (ignores) such zeros.
#
# Such zeros were probably more common in the (real) paper tape days.
# They are supported here simply because SIMH does too.
#
# The file is a sequence of blocks in header/data/checksum format:
# [HEADER] -- 6 bytes # [HEADER] -- 6 bytes
# [DATA] -- size determined by header, can be zero length # [DATA] -- size determined by header, can be zero length
# [CHECKSUM] -- 1 byte # [CHECKSUM] -- 1 byte
@ -167,6 +185,9 @@ def load_lda_f(p, f):
# addr-lsb -- lower 8 bits of address # addr-lsb -- lower 8 bits of address
# addr-msb -- upper 8 bits of address # addr-msb -- upper 8 bits of address
# #
# As mentioned, runs of zeros in prior to such a header (which starts
# with a 1) are ignored.
#
# 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.
@ -175,12 +196,8 @@ def load_lda_f(p, f):
# an "END" block that terminates the format. The address indicated # an "END" block that terminates the format. The address indicated
# is the "start address" and is returned. # is the "start address" and is returned.
while (header := _must_read_n(f, 1)) == 0:
pass
header += _must_read_n(f, 5) # form the rest of the first header
while True: while 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")
@ -196,14 +213,14 @@ def load_lda_f(p, f):
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: if count == 6: # END block with no data
return addr return addr
_byte_phys_write(p, b, addr) _byte_phys_write(p, b, addr)
header = _must_read_n(f, 6)
assert False, "unreachable" assert False, "unreachable"
def boot_lda(p, fname, /, *, force_run=True): def boot_lda(p, fname, /, *, force_run=True):
"""Load and boot an LDA/BIC/absolute-loader file. """Load and boot an LDA/BIC/absolute-loader file.
@ -228,7 +245,7 @@ def boot_lda(p, fname, /, *, force_run=True):
p.run(pc=addr) p.run(pc=addr)
return rawaddr return rawaddr
def make_unix_machine(*, loglevel='INFO', drivenames=[]): def make_unix_machine(*, loglevel='INFO', drivenames=[]):
p = PDP1170(loglevel=loglevel) p = PDP1170(loglevel=loglevel)