stacklimit red and yellow now both conform to SIMH behavior

This commit is contained in:
Neil Webber 2023-09-28 09:04:10 -05:00
parent cc4303765d
commit 6c5dbb1e34
2 changed files with 94 additions and 29 deletions

View file

@ -628,8 +628,6 @@ class PDP11:
if self.r[self.SP] < lim: if self.r[self.SP] < lim:
if not (self.straps & self.STRAPBITS.YELLOW): if not (self.straps & self.STRAPBITS.YELLOW):
self.logger.info(f"YELLOW ZONE, {list(map(oct, self.r))}") self.logger.info(f"YELLOW ZONE, {list(map(oct, self.r))}")
# definitely in at least a yellow condition
self.straps |= self.STRAPBITS.YELLOW
# how about red? # how about red?
if self.r[self.SP] + 32 < lim: # uh oh - below the yellow! if self.r[self.SP] + 32 < lim: # uh oh - below the yellow!
@ -638,6 +636,8 @@ class PDP11:
# and this trap is executed # and this trap is executed
self.r[self.SP] = 4 # !! just enough room for... self.r[self.SP] = 4 # !! just enough room for...
raise PDPTraps.AddressError(cpuerr=self.CPUERR_BITS.REDZONE) raise PDPTraps.AddressError(cpuerr=self.CPUERR_BITS.REDZONE)
else:
self.straps |= self.STRAPBITS.YELLOW
def get_synchronous_trap(self, abort_trap): def get_synchronous_trap(self, abort_trap):
"""Return a synchronous trap, or possibly None. """Return a synchronous trap, or possibly None.
@ -724,18 +724,7 @@ class PDP11:
self.psw = newps self.psw = newps
self.psw_prevmode = saved_curmode # i.e., override newps<13:12> self.psw_prevmode = saved_curmode # i.e., override newps<13:12>
prepushSP = self.r[6] self._trappush(self.r[self.PC], saved_psw)
try:
self.stackpush(saved_psw)
self.stackpush(self.r[self.PC])
except PDPTrap as e:
# again this is a pretty egregious error it means the kernel
# stack is not mapped, or the stack pointer is odd, or similar
# very bad mistakes by the kernel code. It is a fatal halt
# NOTE: The stack register is restored
self.logger.info(f"Trap pushing trap onto stack")
self.r[6] = prepushSP
self.halted = self.HALTED_STACK
# The error register records (accumulates) reasons (if given) # The error register records (accumulates) reasons (if given)
self.error_register |= trap.cpuerr self.error_register |= trap.cpuerr
@ -805,6 +794,27 @@ class PDP11:
self.r[6] = self.u16add(self.r[6], 2) self.r[6] = self.u16add(self.r[6], 2)
return w return w
# this is special because stack limit checking is disabled
# during pushes of trap/interrupt frames. Any other types of
# traps during a stack trap push are a fatal CPU halt and represent
# a serious kernel programming error (invalid kernel stack)
def _trappush(self, pc, psw):
try:
# NOTE: The stack pointer is only modified if
# both of these succeed with no mmu/addressing traps
self.mmu.wordRW_KD(self.u16add(self.r[self.SP], -2), psw)
self.mmu.wordRW_KD(self.u16add(self.r[self.SP], -4), pc)
except PDPTrap as e:
# again this is a pretty egregious error it means the kernel
# stack is not mapped, or the stack pointer is odd, or similar
# very bad mistakes by the kernel code. It is a fatal halt
self.logger.info(f"Trap ({e}) pushing trap {trap} onto stack")
self.logger.info(f"Machine state: {self.machinestate()}")
self.logger.info("HALTING")
self.halted = self.HALTED_STACK
else:
self.r[self.SP] = self.u16add(self.r[self.SP], -4)
class PDP1170(PDP11): class PDP1170(PDP11):

View file

@ -1309,13 +1309,7 @@ class TestMethods(unittest.TestCase):
p.run(pc=aa) p.run(pc=aa)
self.assertEqual(p.r[0], 3) # confirm made it all the way through self.assertEqual(p.r[0], 3) # confirm made it all the way through
def test_stacklim1(self): def _stacklimcode(self, go_red=False):
# Set the stack at the top of the yellow zone and then use it.
# This test should cause loopcount (3) YELLOW synchronous traps.
# Behavior and expected results verified by running identical
# machine code in SIMH
p = self.make_pdp()
# memory usage: # memory usage:
# 0o4000.. is the test code # 0o4000.. is the test code
# 0o6000.. is the trap handler # 0o6000.. is the trap handler
@ -1324,17 +1318,27 @@ class TestMethods(unittest.TestCase):
# r5 is used to walk through the 0o7000+ storage, it is initialized # r5 is used to walk through the 0o7000+ storage, it is initialized
# in the test code and used in the trap handler # in the test code and used in the trap handler
p = self.make_pdp()
with ASM() as tr: with ASM() as tr:
# record... # record...
tr.mov('r2', '(r5)+') # ...separator/entry number tr.mov('r2', '(r5)+') # ...separator/entry number
tr.mov('sp', '(r5)+') # ...the sp tr.mov('sp', '(r5)+') # ...the sp
tr.mov('(sp)', '(r5)+') # ...the trap-saved pc tr.mov('(sp)', '(r5)+') # ...the trap-saved pc
tr.mov(tr.ptr(0o177766), '(r5)+') # ...cpu error register tr.mov(tr.ptr(0o177766), 'r1') # (will be used later)
tr.mov('r2', '(r5)+') # ...separator/entry number tr.mov('r1', '(r5)+') # ...cpu error register
tr.mov('r2', '(r5)+') # ...separator/entry number
# indicate successfully completed the above, bump entry number # indicate successfully completed the above, bump entry number
tr.inc('r2') tr.inc('r2')
# but if RED trap, stop here.
tr.bit(p.CPUERR_BITS.REDZONE, 'r1')
tr.beq('rtt')
tr.halt()
tr.label('rtt')
tr.rtt() tr.rtt()
tra = 0o6000 tra = 0o6000
self.loadphysmem(p, tr.instructions(), tra) self.loadphysmem(p, tr.instructions(), tra)
@ -1355,7 +1359,7 @@ class TestMethods(unittest.TestCase):
a.mov(tra, a.ptr(0o4)) a.mov(tra, a.ptr(0o4))
a.mov(0o340, a.ptr(0o6)) a.mov(0o340, a.ptr(0o6))
loopcount = 3 loopcount = 3 if not go_red else 30 # will never get to 30
a.mov(loopcount, 'r0') a.mov(loopcount, 'r0')
a.label('push') a.label('push')
a.clr('-(sp)') a.clr('-(sp)')
@ -1364,10 +1368,22 @@ class TestMethods(unittest.TestCase):
aa = 0o4000 aa = 0o4000
self.loadphysmem(p, a.instructions(), aa) self.loadphysmem(p, a.instructions(), aa)
p.r[p.PC] = aa
return p
p.run(pc=aa) def test_stacklim1(self):
# Set the stack at the top of the yellow zone and then use it.
# This test should cause loopcount (3) YELLOW synchronous traps.
# Behavior and expected results verified by running identical
# machine code in SIMH
# obtained by running above in SIMH # r5 is used to walk through the 0o7000+ storage, it is initialized
# in the test code and used in the trap handler
p = self._stacklimcode()
p.run()
# obtained by running machine code in SIMH
expected_7000 = [ expected_7000 = [
# MARKER SP PC CPUERR MARKER # MARKER SP PC CPUERR MARKER
0o066000, 0o000372, 0o004052, 0o000010, 0o066000, 0o066000, 0o000372, 0o004052, 0o000010, 0o066000,
@ -1380,6 +1396,45 @@ class TestMethods(unittest.TestCase):
with self.subTest(i=i, val=val): with self.subTest(i=i, val=val):
self.assertEqual(val, p.physmem[recbase + i]) self.assertEqual(val, p.physmem[recbase + i])
def test_stacklim_red(self):
p = self._stacklimcode(go_red=True)
p.run()
# Behavior/results verified by running machine code on SIMH;
# however, SIMH halts the simulation on the red stack trap and
# requires intervention to continue into the actual trap handler.
# Doing that (i.e., "CONTINUE") leads to these same results.
self.assertEqual(p.r[1], 0o14) # RED|YELLOW
self.assertEqual(p.r[2], 0o66021) # known magic iteration marker
self.assertEqual(p.r[6], 0) # stack should be at zero
# obtained by running machine code in SIMH
expected_7000 = [
# MARKER SP PC CPUERR MARKER
0o066000, 0o000372, 0o004052, 0o000010, 0o066000,
0o066001, 0o000370, 0o004052, 0o000010, 0o066001,
0o066002, 0o000366, 0o004052, 0o000010, 0o066002,
0o066003, 0o000364, 0o004052, 0o000010, 0o066003,
0o066004, 0o000362, 0o004052, 0o000010, 0o066004,
0o066005, 0o000360, 0o004052, 0o000010, 0o066005,
0o066006, 0o000356, 0o004052, 0o000010, 0o066006,
0o066007, 0o000354, 0o004052, 0o000010, 0o066007,
0o066010, 0o000352, 0o004052, 0o000010, 0o066010,
0o066011, 0o000350, 0o004052, 0o000010, 0o066011,
0o066012, 0o000346, 0o004052, 0o000010, 0o066012,
0o066013, 0o000344, 0o004052, 0o000010, 0o066013,
0o066014, 0o000342, 0o004052, 0o000010, 0o066014,
0o066015, 0o000340, 0o004052, 0o000010, 0o066015,
0o066016, 0o000336, 0o004052, 0o000010, 0o066016,
0o066017, 0o000334, 0o004052, 0o000010, 0o066017,
0o066020, 0o000000, 0o004052, 0o000014, 0o066020,
0]
recbase = 0o7000//2 # word address in phys mem
for i, val in enumerate(expected_7000):
with self.subTest(i=i, val=val):
self.assertEqual(val, p.physmem[recbase + i])
def test_ubmap(self): def test_ubmap(self):
p = self.make_pdp() p = self.make_pdp()