add test for (user mode) PC wrap-around
This commit is contained in:
parent
4651d137f7
commit
379aeca487
1 changed files with 98 additions and 1 deletions
99
pdptests.py
99
pdptests.py
|
@ -40,7 +40,7 @@ from pdpasmhelper import InstructionBlock
|
||||||
|
|
||||||
class TestMethods(unittest.TestCase):
|
class TestMethods(unittest.TestCase):
|
||||||
|
|
||||||
PDPLOGLEVEL = 'WARNING'
|
PDPLOGLEVEL = 'DEBUG'
|
||||||
|
|
||||||
# used to create various instances, collects all the options
|
# used to create various instances, collects all the options
|
||||||
# detail into this one place... mostly this is about loglevel
|
# detail into this one place... mostly this is about loglevel
|
||||||
|
@ -200,6 +200,59 @@ class TestMethods(unittest.TestCase):
|
||||||
self.loadphysmem(p, a, instloc)
|
self.loadphysmem(p, a, instloc)
|
||||||
return p, instloc
|
return p, instloc
|
||||||
|
|
||||||
|
def u64mapped_pdp(self, p=None, /):
|
||||||
|
# Make a PDP11 with:
|
||||||
|
# * kernel space mapped to first 56K of memory (straight through)
|
||||||
|
# * top of kernel space mapped to I/O page
|
||||||
|
# * 64KB of user space mapped to phys 64K-128K
|
||||||
|
#
|
||||||
|
# No I/D separation.
|
||||||
|
#
|
||||||
|
if p is None:
|
||||||
|
p = self.make_pdp()
|
||||||
|
|
||||||
|
cn = self.usefulconstants()
|
||||||
|
|
||||||
|
a = InstructionBlock()
|
||||||
|
a.mov(0o160000, 'sp') # start system stack at top of K mem
|
||||||
|
|
||||||
|
# kernel segs 0 .. 6 to physical zero .. 56K
|
||||||
|
a.mov(cn.KISA0, 'r4')
|
||||||
|
for i in range(7):
|
||||||
|
a.mov((i * 8192) >> 6, '(r4)+')
|
||||||
|
|
||||||
|
# kernel seg 7 to I/O page
|
||||||
|
a.mov(0o776000 >> 6, '(r4)')
|
||||||
|
|
||||||
|
# user segs 0 .. 7 to physical 64K .. 128K
|
||||||
|
a.mov(cn.UISA0, 'r4')
|
||||||
|
for i in range(8):
|
||||||
|
a.mov((65536 + (i * 8192)) >> 6, '(r4)+')
|
||||||
|
|
||||||
|
# set K and U PDRs
|
||||||
|
# 77406 = PDR<2:0> = ACF = 0o110 = read/write
|
||||||
|
# PLF<14:8> =0o0774 = full length (128*64 bytes = 8K)
|
||||||
|
|
||||||
|
a.mov(0o77406, 'r0')
|
||||||
|
|
||||||
|
a.mov(cn.KISD0, 'r4')
|
||||||
|
for i in range(8):
|
||||||
|
a.mov('r0', '(r4)+')
|
||||||
|
|
||||||
|
a.mov(cn.UISD0, 'r4')
|
||||||
|
for i in range(8):
|
||||||
|
a.mov('r0', '(r4)+')
|
||||||
|
|
||||||
|
# turn on relocation mode ...
|
||||||
|
a.inc(a.ptr(cn.MMR0))
|
||||||
|
|
||||||
|
# and halt
|
||||||
|
a.literal(0)
|
||||||
|
|
||||||
|
instloc = 0o4000 # 2K
|
||||||
|
self.loadphysmem(p, a, instloc)
|
||||||
|
return p, instloc
|
||||||
|
|
||||||
# test a typical sequence to set up a stack
|
# test a typical sequence to set up a stack
|
||||||
# This is really a test of label support in pdpasmhelper
|
# This is really a test of label support in pdpasmhelper
|
||||||
def test_fwdlabrel(self):
|
def test_fwdlabrel(self):
|
||||||
|
@ -1118,6 +1171,50 @@ class TestMethods(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(good, h.hexdigest())
|
self.assertEqual(good, h.hexdigest())
|
||||||
|
|
||||||
|
def test_pcwrap(self):
|
||||||
|
# tests that the PC correctly wraps 0177776 --> 0
|
||||||
|
p, a = self.u64mapped_pdp()
|
||||||
|
p.run(pc=a)
|
||||||
|
|
||||||
|
# put machine into USER mode, clear all registers (on principle)
|
||||||
|
# NOTE: DO NOT BASH p.psw_curmode directly, that bypasses all
|
||||||
|
# the KSP vs USP magic
|
||||||
|
p.psw = (p.USER << 14)
|
||||||
|
|
||||||
|
for i in range(8):
|
||||||
|
p.r[i] = 0
|
||||||
|
|
||||||
|
# jam a MOV R2,R3 instruction at the end of user space
|
||||||
|
# and a MOV R3,R4 instruction at location 0
|
||||||
|
# The test verifies these correctly wrapped around
|
||||||
|
p.mmu.wordRW(0o177776, 0o010203)
|
||||||
|
p.mmu.wordRW(0, 0o010304)
|
||||||
|
|
||||||
|
# NOTE ... when PC reaches location 2, it executes a HALT (0).
|
||||||
|
# This causes a ReservedInstruction trap in user mode. Since
|
||||||
|
# none of the vectors have been filled in, control will transfer
|
||||||
|
# (in KERNEL mode, because PSW in vector is zero) to location 0,
|
||||||
|
# where it will find another HALT instruction (i.e., 0) and stop.
|
||||||
|
# So all this works without making more scaffolding for that...
|
||||||
|
|
||||||
|
# this magic number MUST be negative because the N bit is
|
||||||
|
# also checked in the saved PSW check below.
|
||||||
|
magic = 0o123321
|
||||||
|
p.r[2] = magic
|
||||||
|
p.run(pc=0o177776)
|
||||||
|
|
||||||
|
# both MOV instructions should have executed
|
||||||
|
self.assertEqual(p.r[2], magic)
|
||||||
|
self.assertEqual(p.r[3], magic)
|
||||||
|
self.assertEqual(p.r[4], magic)
|
||||||
|
|
||||||
|
# the ReservedInstruction trap should have put the (user) PC
|
||||||
|
# value which is 2 past the halt instruction (i.e., 4) on stack
|
||||||
|
self.assertEqual(p.mmu.wordRW(0o160000 - 4), 4)
|
||||||
|
|
||||||
|
# and the saved PSW which should be plain user mode, N-bit
|
||||||
|
self.assertEqual(p.mmu.wordRW(0o160000 - 2), (p.USER << 14) | 0o10)
|
||||||
|
|
||||||
def test_trap(self):
|
def test_trap(self):
|
||||||
# test some traps
|
# test some traps
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue