# MIT License # # Copyright (c) 2023 Neil Webber # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # op07 instructions # 6 simple instructions appear in the op07 space: 070 .. 074 and 077. # In these the first operand is restricted to being only a register # (because the three bits usually used for 'mode' are part of # these opcodes). The destination is still a full 6-bit specification. # # op075 is used for floating point instruction encoding (FP instructions # are also found in other nooks and crannies) # # op076 is the commercial instruction set # from pdptraps import PDPTraps def op070_mul(cpu, inst): dstreg = (inst & 0o000700) >> 6 r = cpu.r[dstreg] src = cpu.operandx(inst & 0o077) # unlike add/subtract, need to explicitly treat as signed. # The right results require sign extending both 16 bit operands to # 32 bits, multiplying them, then taking the bottom 32 bits of the result. # It may not be obvious why this works; see google. if r >= 32768: r |= 0xFFFF0000 if src >= 32768: src |= 0xFFFF0000 m = (src * r) & 0xFFFFFFFF # the result is stored: # high 16 bits in dstreg # low 16 bits in dstreg|1 # and if dstreg is odd ONLY the low 16 bits are stored # This just stores both but in careful order cpu.r[dstreg] = (m >> 16) & 0xFFFF cpu.r[dstreg | 1] = m & 0xFFFF cpu.psw_n = m & 0x80000000 if cpu.psw_n: cpu.psw_c = (m < 0xFFFF8000) else: cpu.psw_c = (m >= 32768) cpu.psw_z = (m == 0) cpu.psw_v = 0 def op071_div(cpu, inst): dstreg = (inst & 0o000700) >> 6 if (dstreg & 1): raise PDPTraps.ReservedInstruction # dstreg must be even dividend = (cpu.r[dstreg] << 16) | cpu.r[dstreg | 1] divisor = cpu.operandx(inst & 0o077) if divisor == 0: cpu.psw_n = 0 cpu.psw_z = 1 cpu.psw_v = 1 cpu.psw_c = 1 elif divisor == 0o177777 and dividend == 0x80000000: # maxneg / -1 == too big cpu.psw_n = 0 cpu.psw_z = 0 cpu.psw_v = 1 cpu.psw_c = 0 else: # convert both numbers to positive equivalents # and track sign info manually if dividend & cpu.SIGN32: dividend = 4*1024*1024*1024 - dividend ddendposneg = -1 else: ddendposneg = 1 posneg = ddendposneg if divisor & cpu.SIGN16: divisor = 65536 - divisor posneg = -posneg q, rem = divmod(dividend, divisor) q *= posneg if q > 32767 or q < -32768: cpu.psw_n = (q < 0) cpu.psw_z = 0 cpu.psw_v = 1 cpu.psw_c = 0 else: if ddendposneg < 0: rem = -rem cpu.psw_n = (q < 0) cpu.psw_z = (q == 0) cpu.psw_v = 0 cpu.psw_c = 0 cpu.r[dstreg] = q & cpu.MASK16 cpu.r[dstreg | 1] = rem & cpu.MASK16 def op072_ash(cpu, inst): dstreg = (inst & 0o000700) >> 6 r = cpu.r[dstreg] shift = cpu.operandx(inst & 0o077) & 0o077 r = _shifter(cpu, r, shift, opsize=2) cpu.r[dstreg] = r def op073_ashc(cpu, inst): dstreg = (inst & 0o000700) >> 6 r = (cpu.r[dstreg] << 16) | cpu.r[dstreg | 1] shift = cpu.operandx(inst & 0o077) & 0o077 r = _shifter(cpu, r, shift, opsize=4) cpu.r[dstreg] = (r >> 16) & cpu.MASK16 cpu.r[dstreg | 1] = r & cpu.MASK16 # this is the heart of ash and ashc def _shifter(cpu, value, shift, *, opsize): """Returns shifted value and sets condition codes.""" signmask = cpu.SIGN16 signextend = 0xFFFFFFFF0000 if opsize == 4: signmask <<= 16 signextend <<= 16 vsign = value & signmask if shift == 0: cpu.psw_n = vsign cpu.psw_z = (value == 0) cpu.psw_v = 0 cpu.psw_c = 0 # per 1981 PDP11 Processor Handbook return value elif shift > 31: # right shift # sign extend if appropriate, so the sign propagates if vsign: value |= signextend # right shift by 1 less, to capture bottom bit for C value >>= (63 - shift) # yes 63, see ^^^^^^^^^^^^^^^ cbit = (value & 1) value >>= 1 else: # shift by 1 less, again to capture cbit value <<= (shift - 1) cbit = value & signmask value <<= 1 value &= (signmask | (signmask - 1)) cpu.psw_n = (value & signmask) cpu.psw_z = (value == 0) cpu.psw_v = (cpu.psw_n != vsign) cpu.psw_c = cbit return value def op074_xor(cpu, inst): srcreg = (inst & 0o000700) >> 6 r = cpu.r[srcreg] s, xb6 = cpu.operandx(inst & 0o077, rmw=True) r ^= s cpu.psw_n = (r & cpu.SIGN16) cpu.psw_z = (r == 0) cpu.psw_v = 0 cpu.operandx(xb6, r) def op077_sob(cpu, inst): srcreg = (inst & 0o000700) >> 6 r = (cpu.r[srcreg] - 1) & 0xffff if r != 0: # technically if this instruction occurs low enough in memory # this PC subtraction could wrap, so be technically correct & mask cpu.r[cpu.PC] = (cpu.r[cpu.PC] - 2 * (inst & 0o077)) & cpu.MASK16 cpu.r[srcreg] = r # dispatch on the next digit after the 07 part... op07_dispatch_table = ( op070_mul, op071_div, op072_ash, op073_ashc, op074_xor, None, # various float instructions, not implemented None, # CIS instructions, not implmented op077_sob)