224 lines
6.4 KiB
Python
224 lines
6.4 KiB
Python
# 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
|
|
#
|
|
|
|
|
|
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
|
|
# C is not altered
|
|
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]
|
|
|
|
if r == 1:
|
|
r = 0
|
|
else:
|
|
if r > 0:
|
|
r -= 1
|
|
else:
|
|
r = 0o177777 # 0 means max, that's how SOB is defined
|
|
|
|
# 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)
|