checkpoint on asm support with WITH
This commit is contained in:
parent
aba26d3d5e
commit
d4c80db5c4
2 changed files with 62 additions and 53 deletions
|
@ -28,7 +28,7 @@
|
||||||
# are focused around helping to create hand-constructed test code.
|
# are focused around helping to create hand-constructed test code.
|
||||||
#
|
#
|
||||||
|
|
||||||
import contextlib
|
from contextlib import AbstractContextManager
|
||||||
|
|
||||||
|
|
||||||
class PDP11InstructionAssembler:
|
class PDP11InstructionAssembler:
|
||||||
|
@ -43,12 +43,10 @@ class PDP11InstructionAssembler:
|
||||||
B6MODES[f"@-({_rn})"] = 0o50 | _i # autodecr deferred
|
B6MODES[f"@-({_rn})"] = 0o50 | _i # autodecr deferred
|
||||||
del _i, _rn, _rnames
|
del _i, _rn, _rnames
|
||||||
|
|
||||||
|
# see _InstBlock for explanation of 'with' syntax use
|
||||||
@classmethod
|
@classmethod
|
||||||
def newsequence(cls):
|
def instruction_block(cls):
|
||||||
return _Sequence()
|
return _InstBlock()
|
||||||
|
|
||||||
def append_seq(self, insts):
|
|
||||||
return insts
|
|
||||||
|
|
||||||
def immediate_value(self, s):
|
def immediate_value(self, s):
|
||||||
base = 8
|
base = 8
|
||||||
|
@ -145,23 +143,28 @@ class PDP11InstructionAssembler:
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise valerr() from None
|
raise valerr() from None
|
||||||
seq = [mode | (b6 & 0o07), idxval]
|
seq = [mode | (b6 & 0o07), idxval]
|
||||||
try:
|
return self._seqwords(seq)
|
||||||
self.append_seq(seq)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
return seq
|
|
||||||
|
|
||||||
# no-op here, but overridden in _Sequence to track generated instructions
|
# no-op here, but overridden in _Sequence to track generated instructions
|
||||||
def _seqwords(self, seq):
|
def _seqwords(self, seq):
|
||||||
return seq
|
return seq
|
||||||
|
|
||||||
|
# All 2 operand instructions end up here eventually
|
||||||
def _2op(self, operation, src, dst):
|
def _2op(self, operation, src, dst):
|
||||||
src6, *src_i = self.operand_parser(src)
|
src6, *src_i = self.operand_parser(src)
|
||||||
dst6, *dst_i = self.operand_parser(dst)
|
dst6, *dst_i = self.operand_parser(dst)
|
||||||
return self._seqwords([operation | src6 << 6 | dst6, *src_i, *dst_i])
|
return self._seqwords([operation | src6 << 6 | dst6, *src_i, *dst_i])
|
||||||
|
|
||||||
|
# All 1 operand instructions end up here eventually
|
||||||
|
# This also supports 0 operand "literals" (which are typically
|
||||||
|
# instructions that have been hand-assembled another way)
|
||||||
def _1op(self, operation, dst):
|
def _1op(self, operation, dst):
|
||||||
dst6, *dst_i = self.operand_parser(dst)
|
"""dst can be None for, essentially, a _0op."""
|
||||||
|
if dst is None:
|
||||||
|
dst6 = 0
|
||||||
|
dst_i = []
|
||||||
|
else:
|
||||||
|
dst6, *dst_i = self.operand_parser(dst)
|
||||||
return self._seqwords([operation | dst6, *dst_i])
|
return self._seqwords([operation | dst6, *dst_i])
|
||||||
|
|
||||||
def mov(self, src, dst):
|
def mov(self, src, dst):
|
||||||
|
@ -203,26 +206,34 @@ class PDP11InstructionAssembler:
|
||||||
def trap(self, tnum):
|
def trap(self, tnum):
|
||||||
return self.literal(0o104400 | tnum)
|
return self.literal(0o104400 | tnum)
|
||||||
|
|
||||||
# generally used for instructions not implemented by an explicit method
|
|
||||||
# Allows for one (or none) operand in the low 6 bits
|
|
||||||
def literal(self, inst, oprnd=None, /):
|
def literal(self, inst, oprnd=None, /):
|
||||||
if oprnd is not None:
|
"""For hand-assembled instructions. Also allows 1 operand."""
|
||||||
return self._1op(inst, oprnd)
|
return self._1op(inst, oprnd)
|
||||||
else:
|
|
||||||
return self._seqwords(inst)
|
|
||||||
|
|
||||||
|
|
||||||
# this is used for WITH ... it is mostly for the notational convenience
|
# This provides a convenience for just calling the native methods
|
||||||
# of being able to do thing like
|
# while accumulating a list of instructions. For better or for worse,
|
||||||
# with ASM.sequence() as u:
|
# instead of:
|
||||||
# u.mov('r2','r6')
|
# insts = (a.mov('r1', 'r2'), a.clr('r0'), ... etc)
|
||||||
# u.trap(0)
|
#
|
||||||
# user_mode_instructions = u.sequence()
|
# a context manager can be used to write it this way:
|
||||||
|
#
|
||||||
|
# with ASM.instruction_block() as a:
|
||||||
|
# a.mov('r1', 'r2')
|
||||||
|
# a.clr('r0')
|
||||||
|
# ...
|
||||||
|
# etc
|
||||||
|
#
|
||||||
|
# and then the instructions are obtained via a.instruction_block()
|
||||||
|
# (not a typo; the WITH is a class method and ^^^^^ is an instance method)
|
||||||
|
#
|
||||||
|
# This is sometimes handy if conditional computation or other gyrations
|
||||||
|
# are needed in the gathering of the instructions
|
||||||
|
|
||||||
class _Sequence(PDP11InstructionAssembler, contextlib.AbstractContextManager):
|
class _InstBlock(PDP11InstructionAssembler, AbstractContextManager):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._seq = []
|
self._instblock = []
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
@ -232,12 +243,11 @@ class _Sequence(PDP11InstructionAssembler, contextlib.AbstractContextManager):
|
||||||
|
|
||||||
def _seqwords(self, seq):
|
def _seqwords(self, seq):
|
||||||
"""seq can be an iterable, or a naked (integer) instruction."""
|
"""seq can be an iterable, or a naked (integer) instruction."""
|
||||||
if self._seq is not None:
|
try:
|
||||||
try:
|
self._instblock += seq
|
||||||
self._seq += seq
|
except TypeError:
|
||||||
except TypeError:
|
self._instblock += [seq]
|
||||||
self._seq += [seq]
|
|
||||||
return seq
|
return seq
|
||||||
|
|
||||||
def sequence(self):
|
def instruction_block(self):
|
||||||
return self._seq
|
return self._instblock
|
||||||
|
|
39
pdptests.py
39
pdptests.py
|
@ -126,7 +126,7 @@ class TestMethods(unittest.TestCase):
|
||||||
# These instructions will be placed at 2K in memory
|
# These instructions will be placed at 2K in memory
|
||||||
#
|
#
|
||||||
|
|
||||||
with ASM.newsequence() as a:
|
with ASM.instruction_block() as a:
|
||||||
a.mov(0o20000, 'sp') # start system stack at 8k
|
a.mov(0o20000, 'sp') # start system stack at 8k
|
||||||
|
|
||||||
# write the constants as described above
|
# write the constants as described above
|
||||||
|
@ -180,7 +180,7 @@ class TestMethods(unittest.TestCase):
|
||||||
a.halt()
|
a.halt()
|
||||||
|
|
||||||
instloc = 0o4000 # 2K
|
instloc = 0o4000 # 2K
|
||||||
self.loadphysmem(p, a.sequence(), instloc)
|
self.loadphysmem(p, a.instruction_block(), instloc)
|
||||||
return p, instloc
|
return p, instloc
|
||||||
|
|
||||||
# these tests end up testing a other stuff too of course, including MMU
|
# these tests end up testing a other stuff too of course, including MMU
|
||||||
|
@ -190,11 +190,11 @@ class TestMethods(unittest.TestCase):
|
||||||
|
|
||||||
for result, r1tval in ((0o33333, 2), (0o22222, 0)):
|
for result, r1tval in ((0o33333, 2), (0o22222, 0)):
|
||||||
# r1=r1tval, mfpi (r1) -> r0; expect r0 = result
|
# r1=r1tval, mfpi (r1) -> r0; expect r0 = result
|
||||||
with ASM.newsequence() as a:
|
with ASM.instruction_block() as a:
|
||||||
a.mov(r1tval, 'r1')
|
a.mov(r1tval, 'r1')
|
||||||
a.mfpi('(r1)')
|
a.mfpi('(r1)')
|
||||||
a.mov('(sp)+', 'r0')
|
a.mov('(sp)+', 'r0')
|
||||||
tvecs.append((result, a.sequence()))
|
tvecs.append((result, a.instruction_block()))
|
||||||
|
|
||||||
for result, insts in tvecs:
|
for result, insts in tvecs:
|
||||||
with self.subTest(result=result, insts=insts):
|
with self.subTest(result=result, insts=insts):
|
||||||
|
@ -205,13 +205,12 @@ class TestMethods(unittest.TestCase):
|
||||||
def test_mfpxsp(self):
|
def test_mfpxsp(self):
|
||||||
cn = self.usefulconstants()
|
cn = self.usefulconstants()
|
||||||
|
|
||||||
# these two instructions are needed as literals in the test sequence
|
with ASM.instruction_block() as u:
|
||||||
with ASM.newsequence() as u:
|
|
||||||
u.mov('r2', 'r6')
|
u.mov('r2', 'r6')
|
||||||
u.trap(0)
|
u.trap(0)
|
||||||
user_mode_instructions = u.sequence()
|
user_mode_instructions = u.instruction_block()
|
||||||
|
|
||||||
with ASM.newsequence() as premmu:
|
with ASM.instruction_block() as premmu:
|
||||||
ts = premmu # just for brevity...
|
ts = premmu # just for brevity...
|
||||||
ts.mov(0o14000, ts.ptr(0o34)) # set vector 034 to 14000
|
ts.mov(0o14000, ts.ptr(0o34)) # set vector 034 to 14000
|
||||||
ts.clr(ts.ptr(0o36)) # PSW for trap - zero work
|
ts.clr(ts.ptr(0o36)) # PSW for trap - zero work
|
||||||
|
@ -223,31 +222,31 @@ class TestMethods(unittest.TestCase):
|
||||||
ts.mov(0o140340, '-(sp)') # push user-ish PSW to K stack
|
ts.mov(0o140340, '-(sp)') # push user-ish PSW to K stack
|
||||||
ts.clr('-(sp)') # new user PC = 0
|
ts.clr('-(sp)') # new user PC = 0
|
||||||
|
|
||||||
with ASM.newsequence() as postmmu:
|
with ASM.instruction_block() as postmmu:
|
||||||
postmmu.literal(6) # RTT - goes to user mode, addr 0
|
postmmu.literal(6) # RTT - goes to user mode, addr 0
|
||||||
|
|
||||||
p, pc = self.simplemapped_pdp(premmu=premmu.sequence(),
|
p, pc = self.simplemapped_pdp(premmu=premmu.instruction_block(),
|
||||||
postmmu=postmmu.sequence())
|
postmmu=postmmu.instruction_block())
|
||||||
|
|
||||||
# put the trap handler at 14000 as expected
|
# put the trap handler at 14000 as expected
|
||||||
with ASM.newsequence() as th:
|
with ASM.instruction_block() as th:
|
||||||
th.mfpd('sp')
|
th.mfpd('sp')
|
||||||
th.mov('(sp)+', 'r3')
|
th.mov('(sp)+', 'r3')
|
||||||
th.halt()
|
th.halt()
|
||||||
self.loadphysmem(p, th.sequence(), 0o14000)
|
self.loadphysmem(p, th.instruction_block(), 0o14000)
|
||||||
p.run(pc=pc)
|
p.run(pc=pc)
|
||||||
self.assertEqual(p.r[2], p.r[3])
|
self.assertEqual(p.r[2], p.r[3])
|
||||||
|
|
||||||
def test_mtpi(self):
|
def test_mtpi(self):
|
||||||
cn = self.usefulconstants()
|
cn = self.usefulconstants()
|
||||||
|
|
||||||
with ASM.newsequence() as ts:
|
with ASM.instruction_block() as ts:
|
||||||
ts.mov(0o1717, '-(sp)') # pushing 0o1717
|
ts.mov(0o1717, '-(sp)') # pushing 0o1717
|
||||||
ts.mtpi(ts.ptr(0o02)) # and MTPI it to user location 2
|
ts.mtpi(ts.ptr(0o02)) # and MTPI it to user location 2
|
||||||
ts.clr(ts.ptr(cn.MMR0)) # turn MMU back off
|
ts.clr(ts.ptr(cn.MMR0)) # turn MMU back off
|
||||||
ts.mov(ts.ptr(0o20002), 'r0') # r0 = (020002)
|
ts.mov(ts.ptr(0o20002), 'r0') # r0 = (020002)
|
||||||
|
|
||||||
tvecs = ((0o1717, ts.sequence()),)
|
tvecs = ((0o1717, ts.instruction_block()),)
|
||||||
|
|
||||||
for r0result, insts in tvecs:
|
for r0result, insts in tvecs:
|
||||||
with self.subTest(r0result=r0result, insts=insts):
|
with self.subTest(r0result=r0result, insts=insts):
|
||||||
|
@ -273,10 +272,10 @@ class TestMethods(unittest.TestCase):
|
||||||
sub_loc = testloc + 4
|
sub_loc = testloc + 4
|
||||||
|
|
||||||
for addsub, loc in (('add', add_loc), ('sub', sub_loc)):
|
for addsub, loc in (('add', add_loc), ('sub', sub_loc)):
|
||||||
with ASM.newsequence() as a:
|
with ASM.instruction_block() as a:
|
||||||
getattr(a, addsub)('r0', 'r1')
|
getattr(a, addsub)('r0', 'r1')
|
||||||
a.halt()
|
a.halt()
|
||||||
for offs, inst in enumerate(a.sequence()):
|
for offs, inst in enumerate(a.instruction_block()):
|
||||||
p.physmem[(loc >> 1) + offs] = inst
|
p.physmem[(loc >> 1) + offs] = inst
|
||||||
|
|
||||||
for r0, r1, added, a_nzvc, subbed, s_nzvc in testvecs:
|
for r0, r1, added, a_nzvc, subbed, s_nzvc in testvecs:
|
||||||
|
@ -320,7 +319,7 @@ class TestMethods(unittest.TestCase):
|
||||||
# various condition code tests
|
# various condition code tests
|
||||||
p = self.make_pdp()
|
p = self.make_pdp()
|
||||||
|
|
||||||
with ASM.newsequence() as ts:
|
with ASM.instruction_block() as ts:
|
||||||
# program is:
|
# program is:
|
||||||
# CLR R0
|
# CLR R0
|
||||||
# BEQ 1f
|
# BEQ 1f
|
||||||
|
@ -358,7 +357,6 @@ class TestMethods(unittest.TestCase):
|
||||||
ts.mov(ts.ptr(0o5000), 'r1')
|
ts.mov(ts.ptr(0o5000), 'r1')
|
||||||
ts.mov(ts.ptr(0o5002), 'r2')
|
ts.mov(ts.ptr(0o5002), 'r2')
|
||||||
|
|
||||||
# CMP R1,R2 BLE
|
|
||||||
ts.cmp('r1', 'r2')
|
ts.cmp('r1', 'r2')
|
||||||
ts.literal(0o003401) # BLE 1f
|
ts.literal(0o003401) # BLE 1f
|
||||||
ts.halt()
|
ts.halt()
|
||||||
|
@ -370,7 +368,7 @@ class TestMethods(unittest.TestCase):
|
||||||
ts.dec('r0')
|
ts.dec('r0')
|
||||||
ts.halt()
|
ts.halt()
|
||||||
|
|
||||||
insts = ts.sequence()
|
insts = ts.instruction_block()
|
||||||
instloc = 0o4000
|
instloc = 0o4000
|
||||||
self.loadphysmem(p, insts, instloc)
|
self.loadphysmem(p, insts, instloc)
|
||||||
|
|
||||||
|
@ -405,6 +403,7 @@ class TestMethods(unittest.TestCase):
|
||||||
def test_unscc(self):
|
def test_unscc(self):
|
||||||
# more stuff like test_cc but specifically testing unsigned Bxx codes
|
# more stuff like test_cc but specifically testing unsigned Bxx codes
|
||||||
p = self.make_pdp()
|
p = self.make_pdp()
|
||||||
|
|
||||||
insts = (
|
insts = (
|
||||||
# program is:
|
# program is:
|
||||||
# CLR R0
|
# CLR R0
|
||||||
|
|
Loading…
Add table
Reference in a new issue