From a74312451a3d25202250cd1e4c265d8b8e60a247 Mon Sep 17 00:00:00 2001
From: Neil Webber <neil@webber.com>
Date: Sat, 16 Sep 2023 22:19:36 -0500
Subject: [PATCH] downtest and bonus test work in mmu updown

---
 pdptests.py | 149 ++++++++++++++++++++++++++++++++--------------------
 1 file changed, 92 insertions(+), 57 deletions(-)

diff --git a/pdptests.py b/pdptests.py
index ad880ad..b6ad2dc 100644
--- a/pdptests.py
+++ b/pdptests.py
@@ -669,7 +669,9 @@ class TestMethods(unittest.TestCase):
         # to execute this in isolation just to generate the instructions
         # and use them in other simulators (e.g., SIMH)
         #
-        # Returns a tuple (t, u, k) each being an InstructionBlock
+        # Returns three tuples:
+        #  (taddr, t), (uaddr, u), (k addr, k)
+        # where the t/u/k elements are the instruction blocks.
 
         cn = self.usefulconstants()
 
@@ -681,8 +683,8 @@ class TestMethods(unittest.TestCase):
         # taddr and kaddr MUST be in this first 8K of memory (if only
         # because the mapping setup doesn't map anything else)
         #
-        # USER I space is mapped to uaddr which can be any 8K boundary
-        # 0o20000 and beyond.
+        # USER I space is mapped to uaddr which can be any 64-byte
+        # (i.e., mappable to user 0) boundary 0o20000 and beyond.
         #
         # All 64K of USER D space is mapped to 64K of physical memory
         # from uphysdata to uphysdata + 64K, but with a bizarre page
@@ -713,6 +715,11 @@ class TestMethods(unittest.TestCase):
         #   using ED=1 ("dirbit" = 0o10) segments grow downwards, with the
         #   same 0, 16, 32 .. progression (of valid "blocks") but they
         #   are at the end of the segments.
+        #
+        # This test can be rightly criticized as unnecessarily complex;
+        # once the math has been shown correct for a few important length
+        # cases to consider, the possibility of further bugs is remote.
+        # Nevertheless, here it is.
 
         # this code will go at taddr
         with ASM() as tr:
@@ -724,7 +731,7 @@ class TestMethods(unittest.TestCase):
             # gets to use r2-r5:
             #      r2: expected good flag (initialized elsewhere)
             #      r3: determined good flag (by trap entry)
-            #      r5: TESTTABLE pointer (initialized elsewhere)
+            #      r5: test table data pointer (initialized elsewhere)
             tr.label('UTrap')
 
             # first determine if trap0 (bad) or trap1 (good)
@@ -832,7 +839,9 @@ class TestMethods(unittest.TestCase):
 
         # The kernel-mode code that drives the whole test
         with ASM() as k:
-            k.mov(0o20000, 'sp')      # start system stack at 8k
+            # location 0o17776 is used to remember the table start r5...
+            k.mov(0o17776, 'sp')      # start system stack at 8k - 2
+
             # KERNEL I SPACE
             #    PAR 0 to physical 0
             #    PAR 7 to physical 760000 and 22bit not turned on
@@ -923,6 +932,8 @@ class TestMethods(unittest.TestCase):
             # create the test table, just push it onto the stack (yeehah!)
             k.mov(0, '-(sp)')           # this is a PAD (not really needed)
             k.mov(0o666, '-(sp)')       # this is a sentinel
+            k.mov(0, '-(sp)')
+            k.mov(0, '-(sp)')
             k.mov(0o176100, '-(sp)')
             k.mov(1, '-(sp)')
             k.mov(0o160000, '-(sp)')
@@ -956,6 +967,7 @@ class TestMethods(unittest.TestCase):
 
             # the test table for the trap handler is now here:
             k.mov('sp', 'r5')
+            k.mov('sp', '*$17776')   # it is also stored here for re-use
             # test starts in the region at the start of the table
             k.mov('(r5)', 'r2')
 
@@ -973,12 +985,53 @@ class TestMethods(unittest.TestCase):
             k.clr('r0')                # user test expects r0 to start zero
             k.rtt()
 
-            # these instructions will be used to switch over
-            # to the DOWN phase of the test. Similar to the UP but
-            # don't have to do the PARs (they stay the same) and the
-            # pln calculations are different.
+            # this is where the DOWN test starts.
+            k.label('DOWNTEST')
 
-            k.label('DOWN')
+            # re-establish initial kernel stack
+            k.mov(0o17776, 'sp')
+
+            # Redo the entire test table for the down address cases
+            # these were precomputed from the algorithm for setting PDRs
+            k.mov(0, '-(sp)')
+            k.mov(0o666, '-(sp)')
+            k.mov(0, '-(sp)')
+            k.mov(1, '-(sp)')
+            k.mov(0o161700, '-(sp)')
+            k.mov(0, '-(sp)')
+            k.mov(0o160000, '-(sp)')
+            k.mov(1, '-(sp)')
+            k.mov(0o143700, '-(sp)')
+            k.mov(0, '-(sp)')
+            k.mov(0o140000, '-(sp)')
+            k.mov(1, '-(sp)')
+            k.mov(0o125700, '-(sp)')
+            k.mov(0, '-(sp)')
+            k.mov(0o120000, '-(sp)')
+            k.mov(1, '-(sp)')
+            k.mov(0o107700, '-(sp)')
+            k.mov(0, '-(sp)')
+            k.mov(0o100000, '-(sp)')
+            k.mov(1, '-(sp)')
+            k.mov(0o71700, '-(sp)')
+            k.mov(0, '-(sp)')
+            k.mov(0o60000, '-(sp)')
+            k.mov(1, '-(sp)')
+            k.mov(0o53700, '-(sp)')
+            k.mov(0, '-(sp)')
+            k.mov(0o40000, '-(sp)')
+            k.mov(1, '-(sp)')
+            k.mov(0o35700, '-(sp)')
+            k.mov(0, '-(sp)')
+            k.mov(0o20000, '-(sp)')
+            k.mov(1, '-(sp)')
+            k.mov(0o17700, '-(sp)')
+            k.mov(1, '-(sp)')
+
+            k.mov('sp', 'r5')    # r5 is where the table starts
+            k.mov('sp', '*$17776')   # it is also stored here for re-use
+
+            # fiddle the PDRs (PARs stay the same) for the down configuration
             k.mov(cn.UDSD0, 'r3')
             k.clr('r0')
             k.label('PARloopDOWN')
@@ -997,11 +1050,12 @@ class TestMethods(unittest.TestCase):
             k.cmp('r0', 16)
             k.blt('PARloopDOWN')
 
-            # this halt will be right before the first run of user mode test
-            k.halt()
+            k.clr('r2')                # the down test starts in 'bad' zone
 
-            k.clr('r0')        # initial loop condition
-            k.clr('(sp)')      # just knows the user loop starts at zero
+            # ok, now ready to start the user program
+            k.mov(0o140340, '-(sp)')   # push user-ish PSW to K stack
+            k.clr('-(sp)')             # new user PC = 0
+            k.clr('r0')                # user test expects r0 to start zero
             k.rtt()
 
             # Now for something extra frosty... relocate just segment 4
@@ -1011,6 +1065,9 @@ class TestMethods(unittest.TestCase):
             # relocation (note: I space because no sep I/D for kernel here)
             k.label('BONUS')
 
+            # recover the r5 stack table beginning
+            k.mov('*$17776', 'r5')
+
             # copy UDSA4 into KISA1 - mapping old segment into kernel space
             k.mov(k.ptr(cn.UDSA0 + 4*2), k.ptr(cn.KISA0 + 2))  # i.e., A1
 
@@ -1032,8 +1089,8 @@ class TestMethods(unittest.TestCase):
             # switch the user page to the new mapping
             k.mov(0o6000, k.ptr(cn.UDSA0 + 4*2))
 
-            # and the standard initialization/resume dance
-            k.halt()
+            # and the standard initialization dance
+            k.clr('r2')                # the down test starts in 'bad' zone
             k.clr('r0')
             k.clr('(sp)')      # just knows the user loop starts at zero
             k.rtt()
@@ -1089,52 +1146,30 @@ class TestMethods(unittest.TestCase):
         uaddr = 0o20000
 
         for addr, b in self._make_updown(taddr, uaddr, kaddr):
+            if addr == kaddr:
+                # need to know DOWNTEST and BONUS
+                downtest = kaddr + (2 * b.getlabel('DOWNTEST'))
+                bonus = kaddr + (2 * b.getlabel('BONUS'))
             self.loadphysmem(p, b.instructions(), addr)
 
-        # finally ready to run the whole shebang!
-        p.run(pc=kaddr)
+        with self.subTest(phase="UP"):
+            # finally ready to run the whole shebang!
+            p.run(pc=kaddr)
 
-        # a halt was encountered, verify r2 is the end sentinel
-        self.assertEqual(p.r[2], 0o666)
-        return
+            # a halt was encountered, verify r2 is the end sentinel
+            self.assertEqual(p.r[2], 0o666)
 
-        # this will be used for both up/down testing, based on goodf
-        def _test(goodf):
-            previous_good = True
+        with self.subTest(phase="DOWN"):
+            # run the down test
+            p.r[2] = 0            # superfluous but makes sure
+            p.run(pc=downtest)
+            self.assertEqual(p.r[2], 0o666)
 
-            for segno in range(8):
-                for o in range(4096):
-                    p.run()            # picks up at rtt pc
-                    physval = (checksum -
-                               ((segno * 8192) + (o * 2))) & 0o177777
-                    if goodf(segno, o*2):
-                        r5_expected = 0o42
-                        r1_expected = physval
-                        if not previous_good:
-                            print(f"bad to good at r0={oct(p.r[0])}, "
-                                  f"addr = {oct(segno*8192 + (2*o))}")
-                            previous_good = True
-                    else:
-                        r5_expected = 0o666
-                        r1_expected = user_noval
-                        if previous_good:
-                            print(f"good to bad at r0={oct(p.r[0])}, "
-                                  f"addr = {oct(segno*8192 + (2*o))}")
-                            previous_good = False
-
-                    self.assertEqual(p.r[1], r1_expected)
-                    self.assertEqual(p.r[5], r5_expected)
-
-        # run the UP test:
-        _test(lambda _segno, _o: _o <= (63 + ((_segno * 64) * 16)))
-
-        # run the code to convert over to DOWN MMU format, and then the test
-        p.run(pc=kernel_addr + (a.getlabel('DOWN') * 2))
-        _test(lambda _segno, _o: _o >= 8192 - (64 + ((_segno * 64) * 16)))
-
-        # last but not least, the BONUS test
-        p.run(pc=kernel_addr + (a.getlabel('BONUS') * 2))
-        _test(lambda _segno, _o: _o >= 8192 - (64 + ((_segno * 64) * 16)))
+        with self.subTest(phase="BONUS"):
+            # and the bonus test
+            p.r[2] = 0            # superfluous but makes sure
+            p.run(pc=bonus)
+            self.assertEqual(p.r[2], 0o666)
 
     def test_ubmap(self):
         p = self.make_pdp()