diff --git a/machine.py b/machine.py index 90594bb..d29545b 100644 --- a/machine.py +++ b/machine.py @@ -26,7 +26,7 @@ from types import SimpleNamespace from pdptraps import PDPTrap, PDPTraps from mmu import MemoryMgmt -from unibus import UNIBUS, UNIBUS_1170 +from unibus import UNIBUS, UNIBUS_1170, BusCycle from breakpoints import StepsBreakpoint, PCBreakpoint, XInfo from op4 import op4_dispatch_table @@ -113,6 +113,9 @@ class PDP11: # the console switches (read) and LEDs (write) 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 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 @@ -238,6 +241,7 @@ class PDP11: for attrname, offs in (('psw', self.PS_OFFS), ('stack_limit_register', self.STACKLIM_OFFS), ('swleds', self.SWLEDS_OFFS), + ('breakreg', self.MICROPROG_BREAK_REG), ('lowersize', self.LOWERSIZE_OFFS), ('uppersize', self.UPPERSIZE_OFFS), ('systemID', self.SYSTEMID_OFFS), @@ -248,6 +252,7 @@ class PDP11: # console switches (read) and blinken lights (write) self.swleds = 0 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. # (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).""" 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 # and includes ODD addresses (!!!) # [ 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 self._syncregs() - # regset regnum r/w (value None or not) - match ((addr & 0o10) >> 3, addr & 0o07, value): - case (0, 6, None): + # regset regnum r/w + match ((addr & 0o10) >> 3, addr & 0o07, cycle): + case (0, 6, BusCycle.READ16): return self.stackpointers[self.KERNEL] - case (0, 6, newksp): - self.stackpointers[self.KERNEL] = newksp - case (1, 6, None): + case (0, 6, BusCycle.WRITE16): + self.stackpointers[self.KERNEL] = value + case (1, 6, BusCycle.READ16): return self.stackpointers[self.SUPERVISOR] - case (1, 6, newssp): - self.stackpointers[self.SUPERVISOR] = newssp - case (1, 7, None): + case (1, 6, BusCycle.WRITE16): + self.stackpointers[self.SUPERVISOR] = value + case (1, 7, BusCycle.READ16): return self.stackpointers[self.USER] - case (1, 7, newusp): - self.stackpointers[self.USER] = newusp - case (regset, regnum, None): + case (1, 7, BusCycle.WRITE16): + self.stackpointers[self.USER] = value + case (regset, regnum, BusCycle.READ16): return self.registerfiles[regset][regnum] - case (regset, regnum, _): + case (regset, regnum, WRITE16): 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 # then reestablish it as r[6]. Can just do this unconditionally diff --git a/pdptests.py b/pdptests.py index bd53831..a31e1e5 100644 --- a/pdptests.py +++ b/pdptests.py @@ -1739,6 +1739,26 @@ class TestMethods(unittest.TestCase): with self.assertRaises(PDPTraps.AddressError): 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): # Test for # https://github.com/outofmbufs/python-pdp11-emulator/issues/14