python-pdp1170/op000.py
2023-10-16 09:09:25 -05:00

137 lines
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.
from pdptraps import PDPTraps
# ... and even further down the op decode rabbit hole we go!
# These are the decodes for opcodes starting 0o000
from branches import branch
def op_halt(cpu, inst):
if cpu.psw_curmode != cpu.KERNEL:
# strange trap, but that's what it says
raise PDPTraps.AddressError(cpu.CPUERR_BITS.ILLHALT)
cpu.halted = True
def op_reset(cpu, inst):
cpu.logger.debug("RESET INSTRUCTION")
cpu.ub.resetbus()
def op_wait(cpu, inst):
cpu.logger.debug("WAIT")
cpu.ub.intmgr.waitstate(cpu.psw_pri) # will pend until an interrupt
def op_rtt(cpu, inst):
cpu.r[cpu.PC] = cpu.stackpop()
cpu.psw = cpu.stackpop()
def op_02xx(cpu, inst):
x5 = (inst & 0o70)
if x5 == 0o00:
op_rts(cpu, inst)
elif x5 == 0o30:
op_spl(cpu, inst)
elif x5 >= 0o40:
op_xcc(cpu, inst)
else:
raise PDPTraps.ReservedInstruction
def op_spl(cpu, inst):
"""SPL; note that this is a no-op (!) not a trap in non-kernel mode."""
if cpu.psw_curmode == cpu.KERNEL:
cpu.psw = (cpu.psw & ~ (0o07 << 5)) | ((inst & 0o07) << 5)
def op_rts(cpu, inst):
Rn = (inst & 0o07)
cpu.r[cpu.PC] = cpu.r[Rn] # will be a no-op for RTS PC
cpu.r[Rn] = cpu.stackpop()
def op_jmp(cpu, inst):
# same justEA/operand non-indirection discussion as in JSR (see)
cpu.r[cpu.PC] = cpu.operandx(inst & 0o77, justEA=True)
def op_swab(cpu, inst):
"""SWAB swap bytes."""
val, xb6 = cpu.operandx(inst & 0o77, rmw=True)
val = ((val >> 8) & cpu.MASK8) | ((val & cpu.MASK8) << 8)
cpu.psw_n = val & cpu.SIGN16
# note this screwy definition, per the handbook
cpu.psw_z = ((val & cpu.MASK8) == 0)
cpu.psw_v = 0
cpu.psw_c = 0
cpu.operandx(xb6, val)
def op_xcc(cpu, inst):
"""XCC - all variations of set/clear condition codes."""
setclr = inst & 0o020 # set it or clear it
if inst & 0o10:
cpu.psw_n = setclr
if inst & 0o04:
cpu.psw_z = setclr
if inst & 0o02:
cpu.psw_v = setclr
if inst & 0o01:
cpu.psw_c = setclr
def op000_dispatcher(cpu, inst):
match (inst & 0o0700):
case 0o0000:
if inst == 0:
op_halt(cpu, inst)
elif inst == 6 or inst == 2: # RTI and RTT are identical!!
op_rtt(cpu, inst)
elif inst == 1:
op_wait(cpu, inst)
elif inst == 3:
raise PDPTraps.BPT
elif inst == 4:
raise PDPTraps.IOT
elif inst == 5:
op_reset(cpu, inst)
case 0o0100:
op_jmp(cpu, inst)
case 0o0200:
op_02xx(cpu, inst)
case 0o0300:
op_swab(cpu, inst)
# note that 2 bits of the branch offset sneak into this match
case 0o0400 | 0o0500 | 0o0600 | 0o0700:
branch(cpu, inst, lambda n, z, v, c: True)