DIV test and cleanup bp tests

This commit is contained in:
Neil Webber 2023-10-10 21:10:11 -05:00
parent c00a3516d4
commit 8275c2c6d3

View file

@ -28,6 +28,8 @@ from branches import BRANCH_CODES
from pdptraps import PDPTraps from pdptraps import PDPTraps
import unittest import unittest
import random import random
# from ddx import RESULTS
import hashlib
from pdpasmhelper import PDP11InstructionAssembler as ASM from pdpasmhelper import PDP11InstructionAssembler as ASM
@ -533,6 +535,123 @@ class TestMethods(unittest.TestCase):
self.assertEqual(p.r[1], expected_R1) self.assertEqual(p.r[1], expected_R1)
expected_R1 = (expected_R1 - 1) & 0o177777 expected_R1 = (expected_R1 - 1) & 0o177777
def test_div(self):
# test the div instruction
# The 32-bit int in R and R|1 is divided by the src operand
p = self.make_pdp()
with ASM() as a:
# The test cases will be X / Y:
# X : 1, 255, 4096, 10017, 32767, 32768, 32769
# and then those same values with 690000 added to them
# Y : -50 .. 50 but skipping 0 and using a large number
#
# The code is written this way so that the resulting ASM()
# is completely self-contained (does not rely on python to
# drive it). This made it easier to cross-verify w/SIMH
# create the test vector table
xvals = (1, 255, 4096, 10017, 32767, 32768, 32769)
xtable = 0o20000
results = 0o30000
# instead of div by zero, div by this randomish large number
largedivisor = 10017 # has to be 16 bits or less
a.clr(a.ptr(0o177776))
a.mov(xtable, 'r0')
for x in xvals:
# same data but negated
xneg = ((p.MASK32 + 1) - x) & p.MASK32
# same data but 690000 arbitrarily added (to make it 32bit)
xplus = x + 690000
# ...and negated
xplusneg = ((p.MASK32 + 1) - xplus) & p.MASK32
# put all of those into the dividend table
for v in (x, xneg, xplus, xplusneg):
a.mov((v >> 16) & p.MASK16, '(r0)+')
a.mov(v & p.MASK16, '(r0)+')
a.clr('(r0)+') # sentinel
a.clr('(r0)') # sentinel
# test loop. Divisor in r4. Dividend in r2/r3
# xval pointer in r0. Results pointer in r1
a.mov(results, 'r1')
a.mov(-50, 'r4')
a.label('outer')
a.mov(xtable, 'r0')
a.label('inner')
a.mov('(r0)+', 'r2')
a.mov('(r0)+', 'r3')
a.tst('r2')
a.bne('divide')
a.tst('r3')
a.bne('divide')
# hit the sentinel, bump the divisor
# look for the large divisor and forge a zero to get to 1
a.cmp('r4', largedivisor)
a.bne('bump')
a.clr('r4')
a.label('bump')
a.inc('r4')
a.bne('nz')
a.mov(largedivisor, 'r4')
a.br('outer')
a.label('nz')
a.cmp('r4', 50)
a.ble('outer')
a.mov(69, 'r0') # this indicates success
a.halt()
a.label('divide')
# divide instruction hand-assembled
a.literal(0o071204)
# first save the PSW
a.mov(a.ptr(0o177776), '(r1)+')
a.mov('r2', '(r1)+')
a.mov('r3', '(r1)+')
a.br('inner')
self.loadphysmem(p, a, 0o10000)
p.run(pc=0o10000)
# --- this is how the above was exported for use in SIMH ---
# with open('div.ini', 'w') as f:
# for s in a.simh(startaddr=0o10000):
# f.write(s + '\n')
#
# Then that .ini file was loaded into SIMH, the program was run,
# and the output data was downloaded from SIMH. The results are
# huge, so that was sha256 hashed into this one "good" hash value.
good = ('f5e525b90728cb6fc4eecd97ad4b36c'
'995d2e5b8890f7c0531284615ee9958d4')
# so see if the live results hash to the same value
def bytify():
for a in range(0o30000, p.r[1], 2):
m = p.mmu.wordRW(a)
yield m & 0xFF
yield (m >> 8) & 0xFF
h = hashlib.sha256()
h.update(bytes(bytify()))
self.assertEqual(good, h.hexdigest())
def test_trap(self): def test_trap(self):
# test some traps # test some traps
@ -1405,7 +1524,7 @@ class TestMethods(unittest.TestCase):
self.assertEqual(p.r[2], 0o66021) # known magic iteration marker self.assertEqual(p.r[2], 0o66021) # known magic iteration marker
self.assertEqual(p.r[6], 0) # stack should be at zero self.assertEqual(p.r[6], 0) # stack should be at zero
# obtained by running machine code in SIMH # these results obtained from SIMH (running same machine code)
expected_7000 = [ expected_7000 = [
# MARKER SP PC CPUERR MARKER # MARKER SP PC CPUERR MARKER
0o066000, 0o000372, 0o004052, 0o000010, 0o066000, 0o066000, 0o000372, 0o004052, 0o000010, 0o066000,
@ -1529,31 +1648,47 @@ class TestMethods(unittest.TestCase):
self.assertTrue(s75.togo < 0) self.assertTrue(s75.togo < 0)
def test_lookbackbp(self): def test_lookbackbp(self):
# test the steps=N breakpoint capability
p = self.make_pdp() p = self.make_pdp()
maxtest = 100 # dynamically determine (within reason!) # of default lookbacks
maxguess = 5000 # if it's more than this... meh
curguess = 1
startaddr = 0o4000
while curguess < maxguess:
with ASM() as a:
for i in range(curguess):
a.mov(i, 'r0')
a.halt()
self.loadphysmem(p, a, startaddr)
bp = BKP.Lookback()
p.run(pc=startaddr, breakpoint=bp)
# if current == first, there was 1 lookback, that's 1
# But also the halt instruction takes up on; hence +2
n = (p.r[0] - bp.states[0]['R0']) + 2
if n < curguess:
default_lookbacks = n
break
curguess += 1
maxtest = default_lookbacks + 1
with ASM() as a: with ASM() as a:
for i in range(maxtest): for i in range(maxtest):
a.mov(i, 'r0') a.mov(i, 'r0')
a.clr('r0') a.clr('r0')
a.halt() a.halt()
startaddr = 0o4000
self.loadphysmem(p, a, startaddr)
class StepsLookback(BKP.Lookback, BKP.StepsBreakpoint):
pass
for i in range(maxtest): for i in range(maxtest):
bp = StepsLookback(steps=i+1) bp = BKP.Lookback(BKP.StepsBreakpoint(steps=i+1))
bp7 = StepsLookback(lookbacks=7, steps=i+1) bp7 = BKP.Lookback(BKP.StepsBreakpoint(steps=i+1), lookbacks=7)
with self.subTest(i=i): with self.subTest(i=i):
p.run(pc=startaddr, breakpoint=bp) p.run(pc=startaddr, breakpoint=bp)
p.run(pc=startaddr, breakpoint=bp7) p.run(pc=startaddr, breakpoint=bp7)
self.assertEqual(p.r[0], i) self.assertEqual(p.r[0], i)
self.assertEqual(len(bp.states), i+1) if i+1 <= default_lookbacks:
self.assertEqual(len(bp.states), i+1)
else:
self.assertEqual(len(bp.states), default_lookbacks)
self.assertEqual(len(bp7.states), min(i+1, 7)) self.assertEqual(len(bp7.states), min(i+1, 7))
def test_ubmap(self): def test_ubmap(self):