From 017d079178f4d912164fcc7ff9553b81011bfe94 Mon Sep 17 00:00:00 2001 From: Neil Webber Date: Sun, 10 Sep 2023 18:06:26 -0600 Subject: [PATCH] checkpoint with sequence accumulation --- pdpasmhelper.py | 182 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 143 insertions(+), 39 deletions(-) diff --git a/pdpasmhelper.py b/pdpasmhelper.py index 1a28e39..66e8de1 100644 --- a/pdpasmhelper.py +++ b/pdpasmhelper.py @@ -29,53 +29,104 @@ # class PDP11InstructionAssembler: + B6MODES = {} + _rnames = [(f"R{_i}", _i) for _i in range(8)] + [("SP", 6), ("PC", 7)] + for _rn, _i in _rnames: + B6MODES[f"{_rn}"] = _i # register direct + B6MODES[f"({_rn})"] = 0o10 | _i # register indirect + B6MODES[f"({_rn})+"] = 0o20 | _i # autoincrement + B6MODES[f"@({_rn})+"] = 0o30 | _i # autoincr deferred + B6MODES[f"-({_rn})"] = 0o40 | _i # autodecrement + B6MODES[f"@-({_rn})"] = 0o50 | _i # autodecr deferred + del _i, _rn, _rnames + def __init__(self): - # crude but so so easy .. translate string to 6-bit operand mode - self.B6MODES = {} + self.activeblock = None - for i in range(8): - rnames = [f"R{i}"] - if i == 6: - rnames.append("SP") - elif i == 7: - rnames.append("PC") + def startblock(self): + self.activeblock = [] - for rn in rnames: - self.B6MODES[f"{rn}"] = i # register direct - self.B6MODES[f"({rn})"] = 0o10 | i # register indirect - self.B6MODES[f"({rn})+"] = 0o20 | i # autoincrement - self.B6MODES[f"@({rn})+"] = 0o30 | i # autoincr deferred - self.B6MODES[f"-({rn})"] = 0o40 | i # autodecrement - self.B6MODES[f"@-({rn})"] = 0o50 | i # autodecr deferred - # X(Rn) and @X(Rn) cannot be parsed this simple lookup way + def addtoblock(self, insts): + if self.activeblock is not None: + self.activeblock += insts + return insts + + def endblock(self): + insts, self.activeblock = self.activeblock, None + return insts + + def immediate_value(self, s): + base = 8 + if s[-1] == '.': + base = 10 + s = s[:-1] + val = int(s, base) + + # as a convenience, allow negative values and convert them + if val < 0 and val >= -32768: + val += 65536 + if val > 65535 or val < 0: + raise ValueError(f"illegal value '{s}' = {val}") + return val + + # this is a notational convenience to create a f'*${i].' string + # for an operand that is an immediate deferred (i.e., numeric pointer) + def ptr(self, i): + return f'*${i}.' def operand_parser(self, operand_string, /): - """Parse operand_string ('r1', '-(sp)', '4(r5)', etc). + """Parse operand_string ('r1', '-(sp)', '4(r5)', $177776, etc). Returns: sequence: [6 bit code, additional words ...] Raises ValueError for syntax errors. + Literals that should become (pc)+ (mode 0o27) must start with '$' + They will be octal unless they end with a '.' + + Literals that are pointers and should become @(pc)+ must + start with '*$' and will be octal unless they end with a '.' + + An integer, i, can be passed in directly; it is becomes f"${i}." """ # NOTE: Not all forms implemented yet. See FUNCTIONALITY DISCLAIMER. - # normalize the operand - operand = operand_string.upper() - s = operand.split() # this also removes leading/trailing space - if len(s) > 1: # but if there were middle spaces... no good - raise ValueError(f"more than one word in '{operand_string}'") + # for convenience + def valerr(): + return ValueError(f"cannot parse '{operand_string}'") + + # normalize the operand, upper case for strings, turn ints back + # into their corresponding string (roundabout, but easiest) + try: + operand = operand_string.upper() + except AttributeError: + operand = f"${operand_string}." + + # bail out if spaces in middle, and remove spaces at ends + s = operand.split() + if len(s) > 1: + raise valerr() operand = s[0] - # operand should be fully normalized: upper case, no spaces. + # operand now fully normalized: upper case, no spaces. + # the first/easiest to try is to see if it is an immediate. + # It will (must) start with either '$', or '*$' if so. + try: + if operand[0] == '$': + return [0o27, self.immediate_value(operand[1:])] + elif operand.startswith('*$'): + return [0o37, self.immediate_value(operand[2:])] + except ValueError: + raise valerr() from None + + # wasn't immediate, see if it matches the precomputed modes try: return [self.B6MODES[operand]] except KeyError: pass - # for convenience - def valerr(): - return ValueError(f"cannot parse '{operand_string}'") + # last chance: X(Rn) and @X(rn) # see if X(Rn) or @X(Rn)... if operand[0] == '@': @@ -88,17 +139,7 @@ class PDP11InstructionAssembler: s = operand.split('(') if len(s) != 2: raise valerr() - idxstr = s[0] - - # idxstr can only end with a digit or a '.' ... in particular - base = 8 - if idxstr[-1] == '.': - base = 10 - idxstr = idxstr[:-1] - elif not idxstr[-1].isdigit(): - raise valerr() - - idxval = int(idxstr, base) + idxval = self.immediate_value(s[0]) # the back end of this, with the '(' put back on, # must end with ')' and must parse @@ -111,6 +152,69 @@ class PDP11InstructionAssembler: return [mode | (b6 & 0o07), idxval] - - + def _2op(self, operation, src, dst): + src6, *src_i = self.operand_parser(src) + dst6, *dst_i = self.operand_parser(dst) + return [ operation | src6 << 6 | dst6, *src_i, *dst_i ] + + def _1op(self, operation, dst): + dst6, *dst_i = self.operand_parser(dst) + return [ operation | dst6, *dst_i ] + + def mov(self, src, dst): + return self.addtoblock(self._2op(0o010000, src, dst)) + + def clr(self, dst): + return self.addtoblock(self._1op(0o005000, dst)) + + def inc(self, dst): + return self.addtoblock(self._1op(0o005200, dst)) + + def halt(self): + return self.addtoblock([0]) + + xxx = """ + + 0o012706, 0o20000, # put system stack at 8k and works down + + 0o012737, 0o22222, 0o20000, + 0o012737, 0o33333, 0o20002, + 0o012737, 0o44444, 0o40000, + + # point both kernel seg 0 PARs to physical zero + 0o005037, cn.KISA0, # CLR $KISA0 + 0o005037, cn.KDSA0, # CLR $KDSA0 + + # kernel seg 7 D space PAR to I/O page (at 22-bit location) + 0o012737, 0o017760000 >> 6, cn.KDSA0 + (7 * 2), + + # user I seg 0 to 0o20000, user D seg 0 to 0o40000 + 0o012737, 0o20000 >> 6, cn.UISA0, + 0o012737, 0o40000 >> 6, cn.UDSA0, + + # set the PDRs for segment zero + + 0o012703, 0o077406, # MOV #77406,R3 + # 77406 = PDR<2:0> = ACF = 0o110 = read/write + # PLF<14:8> =0o0774 = full length (128*64 bytes = 8K) + 0o010337, cn.KISD0, # MOV R3,KISD0 ... + 0o010337, cn.KDSD0, + 0o010337, cn.UISD0, + 0o010337, cn.UDSD0, + # PDR for segment 7 + 0o010337, cn.KDSD0 + (7 * 2), + + + # set previous mode to USER, keeping current mode KERNEL, pri 7 + 0o012737, (p.KERNEL << 14) | (p.USER << 12) | (7 << 5), + self.ioaddr(p, p.PS_OFFS), + + # turn on 22-bit mode, unibus mapping, and I/D sep for k & u + 0o012737, 0o000065, self.ioaddr(p, p.mmu.MMR3_OFFS), + + # turn on relocation mode ... yeehah! (MMR0 known zero here) + 0o005237, self.ioaddr(p, p.mmu.MMR0_OFFS), # INC MMR0 + ) + +"""