Fix I/O to registers via Unibus addrs; also microbreak register
This commit is contained in:
parent
e418355987
commit
1423e79813
2 changed files with 47 additions and 15 deletions
42
machine.py
42
machine.py
|
@ -26,7 +26,7 @@ from types import SimpleNamespace
|
||||||
|
|
||||||
from pdptraps import PDPTrap, PDPTraps
|
from pdptraps import PDPTrap, PDPTraps
|
||||||
from mmu import MemoryMgmt
|
from mmu import MemoryMgmt
|
||||||
from unibus import UNIBUS, UNIBUS_1170
|
from unibus import UNIBUS, UNIBUS_1170, BusCycle
|
||||||
from breakpoints import StepsBreakpoint, PCBreakpoint, XInfo
|
from breakpoints import StepsBreakpoint, PCBreakpoint, XInfo
|
||||||
|
|
||||||
from op4 import op4_dispatch_table
|
from op4 import op4_dispatch_table
|
||||||
|
@ -113,6 +113,9 @@ class PDP11:
|
||||||
# the console switches (read) and LEDs (write)
|
# the console switches (read) and LEDs (write)
|
||||||
SWLEDS_OFFS = 0o17570
|
SWLEDS_OFFS = 0o17570
|
||||||
|
|
||||||
|
# Microprogram break register - some diagnostic programs look for this
|
||||||
|
MICROPROG_BREAK_REG = 0o17770
|
||||||
|
|
||||||
# this is a superb hack for controlling the logging level for debug
|
# this is a superb hack for controlling the logging level for debug
|
||||||
# this is in the unibus address range reserved for "testers" -- not
|
# this is in the unibus address range reserved for "testers" -- not
|
||||||
# sure what that really is but this is as good a place for it as any
|
# sure what that really is but this is as good a place for it as any
|
||||||
|
@ -238,6 +241,7 @@ class PDP11:
|
||||||
for attrname, offs in (('psw', self.PS_OFFS),
|
for attrname, offs in (('psw', self.PS_OFFS),
|
||||||
('stack_limit_register', self.STACKLIM_OFFS),
|
('stack_limit_register', self.STACKLIM_OFFS),
|
||||||
('swleds', self.SWLEDS_OFFS),
|
('swleds', self.SWLEDS_OFFS),
|
||||||
|
('breakreg', self.MICROPROG_BREAK_REG),
|
||||||
('lowersize', self.LOWERSIZE_OFFS),
|
('lowersize', self.LOWERSIZE_OFFS),
|
||||||
('uppersize', self.UPPERSIZE_OFFS),
|
('uppersize', self.UPPERSIZE_OFFS),
|
||||||
('systemID', self.SYSTEMID_OFFS),
|
('systemID', self.SYSTEMID_OFFS),
|
||||||
|
@ -248,6 +252,7 @@ class PDP11:
|
||||||
# console switches (read) and blinken lights (write)
|
# console switches (read) and blinken lights (write)
|
||||||
self.swleds = 0
|
self.swleds = 0
|
||||||
self.error_register = 0 # CPU Error register per handbook
|
self.error_register = 0 # CPU Error register per handbook
|
||||||
|
self.breakreg = 0 # microprogram break register (diags)
|
||||||
|
|
||||||
# NOTE: The cold machine starts out in stack limit violation.
|
# NOTE: The cold machine starts out in stack limit violation.
|
||||||
# (stack pointer = 0). However, the limit semantics only apply
|
# (stack pointer = 0). However, the limit semantics only apply
|
||||||
|
@ -830,7 +835,7 @@ class PDP1170(PDP11):
|
||||||
"""The other set of registers (the one that is not self.r)."""
|
"""The other set of registers (the one that is not self.r)."""
|
||||||
return self.registerfiles[1 - self.psw_regset]
|
return self.registerfiles[1 - self.psw_regset]
|
||||||
|
|
||||||
def _ioregsets(self, addr, value=None, /):
|
def _ioregsets(self, addr, cycle, /, *, value=None):
|
||||||
# NOTE that the encoding of the register addresses is quite funky
|
# NOTE that the encoding of the register addresses is quite funky
|
||||||
# and includes ODD addresses (!!!)
|
# and includes ODD addresses (!!!)
|
||||||
# [ addresses given relative to I/O page base ]
|
# [ addresses given relative to I/O page base ]
|
||||||
|
@ -859,24 +864,31 @@ class PDP1170(PDP11):
|
||||||
# copy the stack pointer out of its r6 "cache" and dup the pc
|
# copy the stack pointer out of its r6 "cache" and dup the pc
|
||||||
self._syncregs()
|
self._syncregs()
|
||||||
|
|
||||||
# regset regnum r/w (value None or not)
|
# regset regnum r/w
|
||||||
match ((addr & 0o10) >> 3, addr & 0o07, value):
|
match ((addr & 0o10) >> 3, addr & 0o07, cycle):
|
||||||
case (0, 6, None):
|
case (0, 6, BusCycle.READ16):
|
||||||
return self.stackpointers[self.KERNEL]
|
return self.stackpointers[self.KERNEL]
|
||||||
case (0, 6, newksp):
|
case (0, 6, BusCycle.WRITE16):
|
||||||
self.stackpointers[self.KERNEL] = newksp
|
self.stackpointers[self.KERNEL] = value
|
||||||
case (1, 6, None):
|
case (1, 6, BusCycle.READ16):
|
||||||
return self.stackpointers[self.SUPERVISOR]
|
return self.stackpointers[self.SUPERVISOR]
|
||||||
case (1, 6, newssp):
|
case (1, 6, BusCycle.WRITE16):
|
||||||
self.stackpointers[self.SUPERVISOR] = newssp
|
self.stackpointers[self.SUPERVISOR] = value
|
||||||
case (1, 7, None):
|
case (1, 7, BusCycle.READ16):
|
||||||
return self.stackpointers[self.USER]
|
return self.stackpointers[self.USER]
|
||||||
case (1, 7, newusp):
|
case (1, 7, BusCycle.WRITE16):
|
||||||
self.stackpointers[self.USER] = newusp
|
self.stackpointers[self.USER] = value
|
||||||
case (regset, regnum, None):
|
case (regset, regnum, BusCycle.READ16):
|
||||||
return self.registerfiles[regset][regnum]
|
return self.registerfiles[regset][regnum]
|
||||||
case (regset, regnum, _):
|
case (regset, regnum, WRITE16):
|
||||||
self.registerfiles[regset][regnum] = value
|
self.registerfiles[regset][regnum] = value
|
||||||
|
case (regset, regnum, BusCycle.WRITE8):
|
||||||
|
# ? is this legal.
|
||||||
|
# Only the low-order byte would be addressible for even regs
|
||||||
|
# (that live at even addresses) and only the high-order byte
|
||||||
|
# would be addressible for odd registers (living at odd addrs).
|
||||||
|
# Funky! Raising this trap seems a better idea.
|
||||||
|
raise PDPTraps.AddressError(cpuerr=self.CPUERR_BITS.NXM)
|
||||||
|
|
||||||
# if the stack pointer for the current mode was updated
|
# if the stack pointer for the current mode was updated
|
||||||
# then reestablish it as r[6]. Can just do this unconditionally
|
# then reestablish it as r[6]. Can just do this unconditionally
|
||||||
|
|
20
pdptests.py
20
pdptests.py
|
@ -1739,6 +1739,26 @@ class TestMethods(unittest.TestCase):
|
||||||
with self.assertRaises(PDPTraps.AddressError):
|
with self.assertRaises(PDPTraps.AddressError):
|
||||||
p.physRW_N(addr+1, len(words), words)
|
p.physRW_N(addr+1, len(words), words)
|
||||||
|
|
||||||
|
def test_registerio(self):
|
||||||
|
# on most processors the general purpose registers are not really
|
||||||
|
# accessible this way (only accessible via console phys interface)
|
||||||
|
# but in the emulation they are accessible so test them...
|
||||||
|
p = self.make_pdp()
|
||||||
|
startaddr = 0o4000
|
||||||
|
|
||||||
|
IOPAGEBASE = 0o160000 # last 8k of the 16 bit space
|
||||||
|
r0_addr = + p.IOPAGE_REGSETS_OFFS
|
||||||
|
a = InstructionBlock()
|
||||||
|
a.mov(IOPAGEBASE, 'r0')
|
||||||
|
a.mov(f"{p.IOPAGE_REGSETS_OFFS}.(r0)", 'r1')
|
||||||
|
a.mov(7, 'r2') # arbitrary; proving the next instr
|
||||||
|
a.mov('r0', f"{p.IOPAGE_REGSETS_OFFS+2}.(r0)") # should write into r2
|
||||||
|
a.halt()
|
||||||
|
self.loadphysmem(p, a, startaddr)
|
||||||
|
p.run(pc=startaddr)
|
||||||
|
self.assertEqual(p.r[0], p.r[1])
|
||||||
|
self.assertEqual(p.r[0], p.r[2])
|
||||||
|
|
||||||
def test_kl11_bytewrite(self):
|
def test_kl11_bytewrite(self):
|
||||||
# Test for
|
# Test for
|
||||||
# https://github.com/outofmbufs/python-pdp11-emulator/issues/14
|
# https://github.com/outofmbufs/python-pdp11-emulator/issues/14
|
||||||
|
|
Loading…
Add table
Reference in a new issue