python-pdp1170/op4.py
folkert van heusden dfdec49007
According to page 67 of PDP1120_Handbook_1972.pdf, BIC and BICB shall
clear the V flag when invoked.
2024-03-28 22:56:11 +01:00

234 lines
7.5 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.
#
# TOP LEVEL OP CODE DISPATCH AND INSTRUCTION IMPLEMENTATION
#
# NOTES:
#
# DISPATCH
# 2-operand instructions are implemented here and are dispatched
# off the top-4 bits of the instruction (hence "op4" name).
#
# The other instructions are encoded into the 0o00, 0o07, and 0o10
# portions of this top-4 bit address space. They are dispatched
# via d3dispatch and respective tables from other modules.
#
# BYTE vs WORD operations:
# Most of them come in two flavors - word and byte, with the
# top-bit distinguishing. This is communicated to the implementation
# functions via "opsize=1" or "opsize=2" when a single function can
# implement both variations. Note that MOV/MOVB are specifically
# optimized, separately, for performance.
#
# PSW updates:
# All instructions must be careful to do their final result writes
# AFTER setting the PSW, because the PSW is addressible via memory
# (a write to unibus 777776) and such a write is supposed to override
# the otherwise-native instruction CC results.
#
# dispatchers to next level for 00, 07, and 10 instructions:
from op00 import op00_dispatch_table
from op07 import op07_dispatch_table
from op10 import op10_dispatch_table
from pdptraps import PDPTraps
def d3dispatcher(d3table, cpu, inst):
opf = d3table[(inst & 0o7000) >> 9]
if opf is None:
raise PDPTraps.ReservedInstruction
opf(cpu, inst)
# This is ALWAYS a 16-bit MOV
def op01_mov(cpu, inst):
"""MOV src,dst -- always 16 bits"""
# avoid call to the more-general operandx for mode 0, direct register.
# This optimization is a substantial speed up for register MOVs.
if (inst & 0o7000) == 0:
val = cpu.r[(inst & 0o700) >> 6]
else:
val = cpu.operandx((inst & 0o7700) >> 6)
cpu.psw_v = 0 # per manual; V is cleared
cpu.psw_z = (val == 0)
cpu.psw_n = (val > 32767)
# same optimization on the write side.
if (inst & 0o70) == 0:
cpu.r[(inst & 0o07)] = val
else:
cpu.operandx(inst & 0o0077, val)
# This is ALWAYS an 8-bit MOVB
def op11_movb(cpu, inst):
"""MOVB src,dst -- always 8 bits"""
# avoid call to the more-general operandx for mode 0, direct register.
# This optimization is a substantial speed up for register MOVs.
if (inst & 0o7000) == 0:
val = cpu.r[(inst & 0o700) >> 6] & 0o377
else:
val = cpu.operandx((inst & 0o7700) >> 6, opsize=1)
cpu.psw_v = 0
cpu.psw_z = (val == 0)
cpu.psw_n = (val & 0o200)
# avoid call to the more-general operandx for mode 0, direct register
# not only as an optimization, but because unlike other byte operations
# in register-direct mode, MOVB does a sign-extend.
dst = inst & 0o0077
if (dst < 8): # i.e., mode 0
if val > 127:
val |= 0o177400
cpu.r[dst] = val
else:
cpu.operandx(dst, val, opsize=1)
def op02_cmp(cpu, inst, opsize=2):
"""CMP(B) src,dst"""
src = cpu.operandx((inst & 0o7700) >> 6, opsize=opsize)
dst = cpu.operandx(inst & 0o0077, opsize=opsize)
# note: this is other order than SUB
t = (src - dst) & cpu.MASK816[opsize]
cpu.psw_c = (src < dst)
signbit = cpu.SIGN816[opsize]
cpu.psw_n = (t & signbit)
cpu.psw_z = (t == 0)
# definition of V is: operands were of opposite signs and the sign
# of the destination was the same as the sign of the result
src_sign = src & signbit
dst_sign = dst & signbit
t_sign = t & signbit
cpu.psw_v = (dst_sign == t_sign) and (src_sign != dst_sign)
def op03_bit(cpu, inst, opsize=2):
"""BIT(B) src,dst"""
src = cpu.operandx((inst & 0o7700) >> 6, opsize=opsize)
dst = cpu.operandx(inst & 0o0077, opsize=opsize)
t = dst & src
cpu.psw_n = t & cpu.SIGN816[opsize]
cpu.psw_z = (t == 0)
cpu.psw_v = 0
# cpu.logger.debug(f"BIT: {src=}, {dst=}, PSW={oct(cpu.psw)}")
def op04_bic(cpu, inst, opsize=2):
"""BIC(B) src,dst"""
src = cpu.operandx((inst & 0o7700) >> 6, opsize=opsize)
dst, xb6 = cpu.operandx(inst & 0o0077, opsize=opsize, rmw=True)
dst &= ~src
cpu.psw_n = dst & cpu.SIGN816[opsize]
cpu.psw_z = (dst == 0)
cpu.psw_v = 0
cpu.operandx(xb6, dst, opsize=opsize)
def op05_bis(cpu, inst, opsize=2):
"""BIS(B) src,dst"""
src = cpu.operandx((inst & 0o7700) >> 6, opsize=opsize)
dst, xb6 = cpu.operandx(inst & 0o0077, opsize=opsize, rmw=True)
dst |= src
cpu.psw_n = dst & cpu.SIGN816[opsize]
cpu.psw_z = (dst == 0)
cpu.psw_v = 0
cpu.operandx(xb6, dst, opsize=opsize)
def op06_add(cpu, inst):
"""ADD src,dst"""
src = cpu.operandx((inst & 0o7700) >> 6)
dst, xb6 = cpu.operandx(inst & 0o0077, rmw=True)
t = src + dst
cpu.psw_c = (t > cpu.MASK16)
if cpu.psw_c:
t &= cpu.MASK16
cpu.psw_n = (t & cpu.SIGN16)
cpu.psw_z = (t == 0)
# definition of V is: operands were of the same signs and the
# sign of the result is different.
src_sign = src & cpu.SIGN16
dst_sign = dst & cpu.SIGN16
t_sign = t & cpu.SIGN16
cpu.psw_v = (dst_sign != t_sign) and (src_sign == dst_sign)
cpu.operandx(xb6, t)
def op16_sub(cpu, inst):
"""SUB src,dst"""
src = cpu.operandx((inst & 0o7700) >> 6)
dst, xb6 = cpu.operandx(inst & 0o0077, rmw=True)
t = (dst - src) & cpu.MASK16 # note: this is opposite of CMP
cpu.psw_n = (t & cpu.SIGN16)
cpu.psw_z = (t == 0)
# definition of V is: operands were of opposite signs and the sign
# of the source was the same as the sign of the result
src_sign = src & cpu.SIGN16
dst_sign = dst & cpu.SIGN16
t_sign = t & cpu.SIGN16
cpu.psw_v = (src_sign == t_sign) and (src_sign != dst_sign)
cpu.psw_c = (src > dst)
cpu.operandx(xb6, t)
def op17_reserved(cpu, inst):
raise PDPTraps.ReservedInstruction
op4_dispatch_table = (
lambda c, i: d3dispatcher(op00_dispatch_table, c, i),
op01_mov,
op02_cmp,
op03_bit,
op04_bic,
op05_bis,
op06_add,
lambda c, i: d3dispatcher(op07_dispatch_table, c, i),
lambda c, i: d3dispatcher(op10_dispatch_table, c, i),
op11_movb, # NOTE: optimized; not mov+lambda
lambda c, i: op02_cmp(c, i, opsize=1), # 12
lambda c, i: op03_bit(c, i, opsize=1), # 13
lambda c, i: op04_bic(c, i, opsize=1), # 14
lambda c, i: op05_bis(c, i, opsize=1), # 15
op16_sub,
op17_reserved) # 17 reserved