Just use InstructionBlock, no more WITH
This commit is contained in:
parent
33b771e700
commit
df73e5dbf2
1 changed files with 73 additions and 74 deletions
147
pdpasmhelper.py
147
pdpasmhelper.py
|
@ -46,14 +46,6 @@ 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 InstructionBlock for explanation of 'with' syntax use
|
|
||||||
@classmethod
|
|
||||||
def __enter__(cls):
|
|
||||||
return InstructionBlock()
|
|
||||||
|
|
||||||
def __exit__(self, *args, **kwargs):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
if self._fwdrefs:
|
if self._fwdrefs:
|
||||||
raise ValueError(f"unresolved refs: " f"{list(self._fwdrefs)}")
|
raise ValueError(f"unresolved refs: " f"{list(self._fwdrefs)}")
|
||||||
|
@ -354,26 +346,34 @@ class BranchTarget(FwdRef):
|
||||||
# of results from calling the instruction methods.
|
# of results from calling the instruction methods.
|
||||||
#
|
#
|
||||||
# Instead of:
|
# Instead of:
|
||||||
|
# a = PDP11InstructionAssembler()
|
||||||
# insts = (
|
# insts = (
|
||||||
# a.mov('r1', 'r2'),
|
# a.mov('r1', 'r2'),
|
||||||
# a.clr('r0'),
|
# a.clr('r0'),
|
||||||
# etc ...
|
# etc ...
|
||||||
# )
|
# )
|
||||||
#
|
#
|
||||||
# The context manager can be used to write it this way:
|
# An InstructionBlock can be used this way:
|
||||||
#
|
#
|
||||||
# with ASM() as a:
|
# a = InstructionBlock()
|
||||||
# a.mov('r1', 'r2')
|
# a.mov('r1', 'r2')
|
||||||
# a.clr('r0')
|
# a.clr('r0')
|
||||||
# etc ...
|
# etc ...
|
||||||
#
|
#
|
||||||
# which, subject to opinion, may be notationally cleaner/clearer and also
|
# Each call to an instruction method appends that instruction to the block.
|
||||||
# opens the possibility of if/for/etc full programming constructs as needed.
|
# Subject to opinion, this may be notationally cleaner/clearer and also
|
||||||
|
# opens the possibility of if/for/etc full programming constructs in
|
||||||
|
# forming the instruction sequence itself.
|
||||||
#
|
#
|
||||||
# The context manager also supports bare-bones labels, helpful for branches
|
# An InstructionBlock adds simple label support as well, useful for branching.
|
||||||
#
|
#
|
||||||
# If treated as an iterable, returns a list of instruction words.
|
# If care is taken to write position-independent code, an InstructionBlock
|
||||||
# Typically used like this:
|
# can eventually be placed at any arbitrary location in memory. When labels
|
||||||
|
# are used in jmp or jsr instructions, they are automatically assembled
|
||||||
|
# as PC-relative offsets (mode = 0o67) rather than absolute values.
|
||||||
|
#
|
||||||
|
# The current list of instruction words is available by using the
|
||||||
|
# instruction block as an iterable. For example:
|
||||||
#
|
#
|
||||||
# instlist = list(a)
|
# instlist = list(a)
|
||||||
#
|
#
|
||||||
|
@ -383,21 +383,9 @@ class BranchTarget(FwdRef):
|
||||||
# raises a ValueError. This can be suppressed (usually only useful
|
# raises a ValueError. This can be suppressed (usually only useful
|
||||||
# for debugging) by requesting a._instructions()
|
# for debugging) by requesting a._instructions()
|
||||||
#
|
#
|
||||||
#
|
|
||||||
# NOTE: The "with" construct is just notationally convenient; nothing
|
|
||||||
# happens in context exit and the instruction block continues to
|
|
||||||
# be valid afterwards. This:
|
|
||||||
# with ASM() as a:
|
|
||||||
# a.mov('r1', 'r2')
|
|
||||||
#
|
|
||||||
# is completely equivalent to:
|
|
||||||
#
|
|
||||||
# a = InstructionBlock()
|
|
||||||
# a.mov('r1', 'r2')
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
class InstructionBlock(PDP11InstructionAssembler, AbstractContextManager):
|
class InstructionBlock(PDP11InstructionAssembler):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._instblock = []
|
self._instblock = []
|
||||||
|
@ -628,7 +616,18 @@ class InstructionBlock(PDP11InstructionAssembler, AbstractContextManager):
|
||||||
"""Generate lines of SIMH deposit commands."""
|
"""Generate lines of SIMH deposit commands."""
|
||||||
|
|
||||||
for offs, w in enumerate(self):
|
for offs, w in enumerate(self):
|
||||||
yield f"D {oct(startaddr + (2 * offs))[2:]} {oct(w)[2:]}"
|
yield f"D {oct(startaddr + (2 * offs))[2:]} {oct(w)[2:]}\n"
|
||||||
|
|
||||||
|
# This method shows one typical way to use the simh generator
|
||||||
|
#
|
||||||
|
# A .ini file full of deposit ('D') commands starting at startaddr
|
||||||
|
# will be created from the instructions in the InstructionBlock
|
||||||
|
def export_to_simh_ini(self, outfilename, /, *, startaddr=0o10000):
|
||||||
|
with open(outfilename, 'w') as f:
|
||||||
|
for s in self.simh(startaddr=startaddr):
|
||||||
|
f.write(s)
|
||||||
|
# and set the PC to the start address
|
||||||
|
f.write(f"D PC {oct(startaddr)[2:]}\n")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -642,27 +641,27 @@ if __name__ == "__main__":
|
||||||
def test_bne_label_distance(self):
|
def test_bne_label_distance(self):
|
||||||
# this should just execute without any issue
|
# this should just execute without any issue
|
||||||
for i in range(127):
|
for i in range(127):
|
||||||
with ASM() as a:
|
a = InstructionBlock()
|
||||||
a.label('foo')
|
a.label('foo')
|
||||||
for _ in range(i):
|
for _ in range(i):
|
||||||
a.mov('r0', 'r0')
|
a.mov('r0', 'r0')
|
||||||
a.bne('foo')
|
a.bne('foo')
|
||||||
|
|
||||||
# but this should ValueError ... branch too far
|
# but this should ValueError ... branch too far
|
||||||
with ASM() as a:
|
a = InstructionBlock()
|
||||||
a.label('foo')
|
a.label('foo')
|
||||||
for _ in range(128):
|
for _ in range(128):
|
||||||
a.mov('r0', 'r0')
|
a.mov('r0', 'r0')
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
a.bne('foo')
|
a.bne('foo')
|
||||||
|
|
||||||
def test_labelmath_dot(self):
|
def test_labelmath_dot(self):
|
||||||
with ASM()as a:
|
a = InstructionBlock()
|
||||||
a.mov('bozo', 'r0')
|
a.mov('bozo', 'r0')
|
||||||
a.label('B')
|
a.label('B')
|
||||||
a.label('BP2', value='. + 2')
|
a.label('BP2', value='. + 2')
|
||||||
a.clr('r0')
|
a.clr('r0')
|
||||||
a.label('bozo')
|
a.label('bozo')
|
||||||
|
|
||||||
self.assertEqual(a.getlabel('B'), 4)
|
self.assertEqual(a.getlabel('B'), 4)
|
||||||
self.assertEqual(a.getlabel('BP2'), 6)
|
self.assertEqual(a.getlabel('BP2'), 6)
|
||||||
|
@ -670,50 +669,50 @@ if __name__ == "__main__":
|
||||||
self.assertEqual(list(a)[1], 6)
|
self.assertEqual(list(a)[1], 6)
|
||||||
|
|
||||||
def test_labelmath_plus(self):
|
def test_labelmath_plus(self):
|
||||||
with ASM() as a:
|
a = InstructionBlock()
|
||||||
a.label('L1', value=17)
|
a.label('L1', value=17)
|
||||||
a.label('L2', value='L1 + 25.')
|
a.label('L2', value='L1 + 25.')
|
||||||
self.assertEqual(a.getlabel('L2'), 42)
|
self.assertEqual(a.getlabel('L2'), 42)
|
||||||
|
|
||||||
def test_labelmath_minus(self):
|
def test_labelmath_minus(self):
|
||||||
with ASM() as a:
|
a = InstructionBlock()
|
||||||
a.label('L1')
|
a.label('L1')
|
||||||
a.clr('r0')
|
a.clr('r0')
|
||||||
a.label('L2', value='. - L1')
|
a.label('L2', value='. - L1')
|
||||||
self.assertEqual(a.getlabel('L2'), 2)
|
self.assertEqual(a.getlabel('L2'), 2)
|
||||||
|
|
||||||
def test_unresolved(self):
|
def test_unresolved(self):
|
||||||
with ASM() as a:
|
a = InstructionBlock()
|
||||||
a.br('bozo')
|
a.br('bozo')
|
||||||
a.clr('r0')
|
a.clr('r0')
|
||||||
a.mov(a.getlabel('xyzzy'), 'r0')
|
a.mov(a.getlabel('xyzzy'), 'r0')
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
foo = list(a)
|
foo = list(a)
|
||||||
|
|
||||||
def test_nocontext(self):
|
def test_identity(self):
|
||||||
a = InstructionBlock()
|
a = InstructionBlock()
|
||||||
a.mov('r0', 'r1')
|
a.mov('r0', 'r1')
|
||||||
a.br('bozo')
|
a.br('bozo')
|
||||||
a.mov('r1', 'r2')
|
a.mov('r1', 'r2')
|
||||||
a.label('bozo')
|
a.label('bozo')
|
||||||
a.mov('r2', 'r3')
|
a.mov('r2', 'r3')
|
||||||
with ASM() as b:
|
b = InstructionBlock()
|
||||||
b.mov('r0', 'r1')
|
b.mov('r0', 'r1')
|
||||||
b.br('bozo')
|
b.br('bozo')
|
||||||
b.mov('r1', 'r2')
|
b.mov('r1', 'r2')
|
||||||
b.label('bozo')
|
b.label('bozo')
|
||||||
b.mov('r2', 'r3')
|
b.mov('r2', 'r3')
|
||||||
self.assertEqual(list(a), list(b))
|
self.assertEqual(list(a), list(b))
|
||||||
|
|
||||||
def test_sob(self):
|
def test_sob(self):
|
||||||
for i in range(63): # 0..62 because the sob also counts
|
for i in range(63): # 0..62 because the sob also counts
|
||||||
with self.subTest(i=i):
|
with self.subTest(i=i):
|
||||||
with ASM() as a:
|
a = InstructionBlock()
|
||||||
a.label('foosob')
|
a.label('foosob')
|
||||||
for _ in range(i):
|
for _ in range(i):
|
||||||
a.mov('r0', 'r0')
|
a.mov('r0', 'r0')
|
||||||
inst = a.sob(0, 'foosob')
|
inst = a.sob(0, 'foosob')
|
||||||
self.assertEqual(len(inst), 1)
|
self.assertEqual(len(inst), 1)
|
||||||
self.assertEqual(inst[0] & 0o77, i+1)
|
self.assertEqual(inst[0] & 0o77, i+1)
|
||||||
|
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Add table
Reference in a new issue