improved WITH syntax for instruction sequences
This commit is contained in:
parent
531e5607a5
commit
475f2e4591
2 changed files with 178 additions and 144 deletions
126
pdpasmhelper.py
126
pdpasmhelper.py
|
@ -28,6 +28,9 @@
|
||||||
# are focused around helping to create hand-constructed test code.
|
# are focused around helping to create hand-constructed test code.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
|
||||||
|
|
||||||
class PDP11InstructionAssembler:
|
class PDP11InstructionAssembler:
|
||||||
B6MODES = {}
|
B6MODES = {}
|
||||||
_rnames = [(f"R{_i}", _i) for _i in range(8)] + [("SP", 6), ("PC", 7)]
|
_rnames = [(f"R{_i}", _i) for _i in range(8)] + [("SP", 6), ("PC", 7)]
|
||||||
|
@ -40,19 +43,11 @@ class PDP11InstructionAssembler:
|
||||||
B6MODES[f"@-({_rn})"] = 0o50 | _i # autodecr deferred
|
B6MODES[f"@-({_rn})"] = 0o50 | _i # autodecr deferred
|
||||||
del _i, _rn, _rnames
|
del _i, _rn, _rnames
|
||||||
|
|
||||||
def __init__(self):
|
@classmethod
|
||||||
self.activeblock = None
|
def newsequence(cls):
|
||||||
|
return _Sequence()
|
||||||
|
|
||||||
def startblock(self):
|
def append_seq(self, insts):
|
||||||
self.activeblock = []
|
|
||||||
|
|
||||||
def addtoblock(self, insts):
|
|
||||||
if self.activeblock is not None:
|
|
||||||
self.activeblock += insts
|
|
||||||
return insts
|
|
||||||
|
|
||||||
def endblock(self):
|
|
||||||
insts, self.activeblock = self.activeblock, None
|
|
||||||
return insts
|
return insts
|
||||||
|
|
||||||
def immediate_value(self, s):
|
def immediate_value(self, s):
|
||||||
|
@ -149,72 +144,97 @@ class PDP11InstructionAssembler:
|
||||||
b6 = self.B6MODES['(' + s[1]]
|
b6 = self.B6MODES['(' + s[1]]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise valerr() from None
|
raise valerr() from None
|
||||||
|
seq = [mode | (b6 & 0o07), idxval]
|
||||||
|
try:
|
||||||
|
self.append_seq(seq)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
return seq
|
||||||
|
|
||||||
return [mode | (b6 & 0o07), idxval]
|
# no-op here, but overridden in _Sequence to track generated instructions
|
||||||
|
def _seqwords(self, seq):
|
||||||
|
return seq
|
||||||
|
|
||||||
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 [ operation | src6 << 6 | dst6, *src_i, *dst_i ]
|
return self._seqwords([operation | src6 << 6 | dst6, *src_i, *dst_i])
|
||||||
|
|
||||||
def _1op(self, operation, dst):
|
def _1op(self, operation, dst):
|
||||||
dst6, *dst_i = self.operand_parser(dst)
|
dst6, *dst_i = self.operand_parser(dst)
|
||||||
return [ operation | dst6, *dst_i ]
|
return self._seqwords([operation | dst6, *dst_i])
|
||||||
|
|
||||||
def mov(self, src, dst):
|
def mov(self, src, dst):
|
||||||
return self.addtoblock(self._2op(0o010000, src, dst))
|
return self._2op(0o010000, src, dst)
|
||||||
|
|
||||||
|
def cmp(self, src, dst):
|
||||||
|
return self._2op(0o020000, src, dst)
|
||||||
|
|
||||||
|
def add(self, src, dst):
|
||||||
|
return self._2op(0o060000, src, dst)
|
||||||
|
|
||||||
|
def sub(self, src, dst):
|
||||||
|
return self._2op(0o160000, src, dst)
|
||||||
|
|
||||||
def clr(self, dst):
|
def clr(self, dst):
|
||||||
return self.addtoblock(self._1op(0o005000, dst))
|
return self._1op(0o005000, dst)
|
||||||
|
|
||||||
def inc(self, dst):
|
def inc(self, dst):
|
||||||
return self.addtoblock(self._1op(0o005200, dst))
|
return self._1op(0o005200, dst)
|
||||||
|
|
||||||
def halt(self):
|
def halt(self):
|
||||||
return self.addtoblock([0])
|
return self.literal(0)
|
||||||
|
|
||||||
xxx = """
|
def mtpi(self, dst):
|
||||||
|
return self._1op(0o006600, dst)
|
||||||
|
|
||||||
0o012706, 0o20000, # put system stack at 8k and works down
|
def mfpi(self, src):
|
||||||
|
return self._1op(0o006500, src)
|
||||||
|
|
||||||
0o012737, 0o22222, 0o20000,
|
def mtpd(self, dst):
|
||||||
0o012737, 0o33333, 0o20002,
|
return self._1op(0o106600, dst)
|
||||||
0o012737, 0o44444, 0o40000,
|
|
||||||
|
|
||||||
# point both kernel seg 0 PARs to physical zero
|
def mfpd(self, src):
|
||||||
0o005037, cn.KISA0, # CLR $KISA0
|
return self._1op(0o106500, src)
|
||||||
0o005037, cn.KDSA0, # CLR $KDSA0
|
|
||||||
|
|
||||||
# kernel seg 7 D space PAR to I/O page (at 22-bit location)
|
def trap(self, tnum):
|
||||||
0o012737, 0o017760000 >> 6, cn.KDSA0 + (7 * 2),
|
return self.literal(0o104400 | tnum)
|
||||||
|
|
||||||
# user I seg 0 to 0o20000, user D seg 0 to 0o40000
|
# generally used for instructions not implemented by an explicit method
|
||||||
0o012737, 0o20000 >> 6, cn.UISA0,
|
# Allows for one (or none) operand in the low 6 bits
|
||||||
0o012737, 0o40000 >> 6, cn.UDSA0,
|
def literal(self, inst, oprnd=None, /):
|
||||||
|
if oprnd is not None:
|
||||||
# set the PDRs for segment zero
|
return self._1op(inst, oprnd)
|
||||||
|
else:
|
||||||
0o012703, 0o077406, # MOV #77406,R3
|
return self._seqwords(inst)
|
||||||
# 77406 = PDR<2:0> = ACF = 0o110 = read/write
|
|
||||||
# PLF<14:8> =0o0774 = full length (128*64 bytes = 8K)
|
|
||||||
0o010337, cn.KISD0, # MOV R3,KISD0 ...
|
|
||||||
0o010337, cn.KDSD0,
|
|
||||||
0o010337, cn.UISD0,
|
|
||||||
0o010337, cn.UDSD0,
|
|
||||||
# PDR for segment 7
|
|
||||||
0o010337, cn.KDSD0 + (7 * 2),
|
|
||||||
|
|
||||||
|
|
||||||
# set previous mode to USER, keeping current mode KERNEL, pri 7
|
# this is used for WITH ... it is mostly for the notational convenience
|
||||||
0o012737, (p.KERNEL << 14) | (p.USER << 12) | (7 << 5),
|
# of being able to do thing like
|
||||||
self.ioaddr(p, p.PS_OFFS),
|
# with ASM.sequence() as u:
|
||||||
|
# u.mov('r2','r6')
|
||||||
|
# u.trap(0)
|
||||||
|
# user_mode_instructions = u.sequence()
|
||||||
|
|
||||||
# turn on 22-bit mode, unibus mapping, and I/D sep for k & u
|
class _Sequence(PDP11InstructionAssembler, contextlib.AbstractContextManager):
|
||||||
0o012737, 0o000065, self.ioaddr(p, p.mmu.MMR3_OFFS),
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self._seq = []
|
||||||
|
|
||||||
# turn on relocation mode ... yeehah! (MMR0 known zero here)
|
def __enter__(self):
|
||||||
0o005237, self.ioaddr(p, p.mmu.MMR0_OFFS), # INC MMR0
|
return self
|
||||||
)
|
|
||||||
|
|
||||||
"""
|
def __exit__(self, *args, **kwargs):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _seqwords(self, seq):
|
||||||
|
"""seq can be an iterable, or a naked (integer) instruction."""
|
||||||
|
if self._seq is not None:
|
||||||
|
try:
|
||||||
|
self._seq += seq
|
||||||
|
except TypeError:
|
||||||
|
self._seq += [seq]
|
||||||
|
return seq
|
||||||
|
|
||||||
|
def sequence(self):
|
||||||
|
return self._seq
|
||||||
|
|
168
pdptests.py
168
pdptests.py
|
@ -29,6 +29,7 @@ import random
|
||||||
|
|
||||||
from pdpasmhelper import PDP11InstructionAssembler as ASM
|
from pdpasmhelper import PDP11InstructionAssembler as ASM
|
||||||
|
|
||||||
|
|
||||||
class TestMethods(unittest.TestCase):
|
class TestMethods(unittest.TestCase):
|
||||||
|
|
||||||
PDPLOGLEVEL = 'INFO'
|
PDPLOGLEVEL = 'INFO'
|
||||||
|
@ -85,6 +86,7 @@ class TestMethods(unittest.TestCase):
|
||||||
ns.UDSA0 = ns.UISA0 + 0o20
|
ns.UDSA0 = ns.UISA0 + 0o20
|
||||||
|
|
||||||
ns.MMR0 = cls.ioaddr(p, p.mmu.MMR0_OFFS)
|
ns.MMR0 = cls.ioaddr(p, p.mmu.MMR0_OFFS)
|
||||||
|
ns.MMR3 = cls.ioaddr(p, p.mmu.MMR3_OFFS)
|
||||||
|
|
||||||
return ns
|
return ns
|
||||||
|
|
||||||
|
@ -96,13 +98,17 @@ class TestMethods(unittest.TestCase):
|
||||||
# User Data space seg 0 points to physical 0o40000
|
# User Data space seg 0 points to physical 0o40000
|
||||||
# and turns on the MMU
|
# and turns on the MMU
|
||||||
#
|
#
|
||||||
|
# premmu is an optional list of instructions to execute
|
||||||
|
# before turning on the MMU
|
||||||
|
#
|
||||||
|
# postmmu is an optional list of instructions to execute
|
||||||
|
# after turning on the MMU
|
||||||
|
#
|
||||||
|
|
||||||
def simplemapped_pdp(self, p=None, addons=[]):
|
def simplemapped_pdp(self, p=None, *, premmu=[], postmmu=[]):
|
||||||
if p is None:
|
if p is None:
|
||||||
p = self.make_pdp()
|
p = self.make_pdp()
|
||||||
|
|
||||||
asm = ASM()
|
|
||||||
|
|
||||||
cn = self.usefulconstants()
|
cn = self.usefulconstants()
|
||||||
|
|
||||||
# this is a table of instructions that ...
|
# this is a table of instructions that ...
|
||||||
|
@ -120,126 +126,132 @@ class TestMethods(unittest.TestCase):
|
||||||
# These instructions will be placed at 2K in memory
|
# These instructions will be placed at 2K in memory
|
||||||
#
|
#
|
||||||
|
|
||||||
asm.startblock()
|
with ASM.newsequence() as a:
|
||||||
asm.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
|
||||||
asm.mov(0o22222, asm.ptr(0o20000))
|
a.mov(0o22222, a.ptr(0o20000))
|
||||||
asm.mov(0o33333, asm.ptr(0o20002))
|
a.mov(0o33333, a.ptr(0o20002))
|
||||||
asm.mov(0o44444, asm.ptr(0o40000))
|
a.mov(0o44444, a.ptr(0o40000))
|
||||||
|
|
||||||
# point both kernel seg 0 PARs to physical zero
|
# point both kernel seg 0 PARs to physical zero
|
||||||
asm.clr(asm.ptr(cn.KISA0))
|
a.clr(a.ptr(cn.KISA0))
|
||||||
asm.clr(asm.ptr(cn.KDSA0))
|
a.clr(a.ptr(cn.KDSA0))
|
||||||
|
|
||||||
# kernel seg 7 D space PAR to I/O page (at 22-bit location)
|
# kernel seg 7 D space PAR to I/O page (at 22-bit location)
|
||||||
asm.mov(0o017760000 >> 6, asm.ptr(cn.KDSA0 + (7 * 2)))
|
a.mov(0o017760000 >> 6, a.ptr(cn.KDSA0 + (7 * 2)))
|
||||||
|
|
||||||
# user I seg 0 to 0o20000, user D seg 0 to 0o40000
|
# user I seg 0 to 0o20000, user D seg 0 to 0o40000
|
||||||
asm.mov(0o20000 >> 6, asm.ptr(cn.UISA0))
|
a.mov(0o20000 >> 6, a.ptr(cn.UISA0))
|
||||||
asm.mov(0o40000 >> 6, asm.ptr(cn.UDSA0))
|
a.mov(0o40000 >> 6, a.ptr(cn.UDSA0))
|
||||||
|
|
||||||
# set the PDRs for segment zero
|
# set the PDRs for segment zero
|
||||||
asm.mov(0o077406, 'r3')
|
a.mov(0o077406, 'r3')
|
||||||
# 77406 = PDR<2:0> = ACF = 0o110 = read/write
|
# 77406 = PDR<2:0> = ACF = 0o110 = read/write
|
||||||
# PLF<14:8> =0o0774 = full length (128*64 bytes = 8K)
|
# PLF<14:8> =0o0774 = full length (128*64 bytes = 8K)
|
||||||
|
|
||||||
asm.mov('r3', asm.ptr(cn.KISD0))
|
a.mov('r3', a.ptr(cn.KISD0))
|
||||||
asm.mov('r3', asm.ptr(cn.KDSD0))
|
a.mov('r3', a.ptr(cn.KDSD0))
|
||||||
asm.mov('r3', asm.ptr(cn.UISD0))
|
a.mov('r3', a.ptr(cn.UISD0))
|
||||||
asm.mov('r3', asm.ptr(cn.UDSD0))
|
a.mov('r3', a.ptr(cn.UDSD0))
|
||||||
|
|
||||||
# PDR for segment 7
|
# PDR for segment 7
|
||||||
asm.mov('r3', asm.ptr(cn.KDSD0 + (7 * 2)))
|
a.mov('r3', a.ptr(cn.KDSD0 + (7 * 2)))
|
||||||
|
|
||||||
# set previous mode to USER, keeping current mode KERNEL, pri 7
|
# set previous mode to USER, keeping current mode KERNEL, pri 7
|
||||||
asm.mov((p.KERNEL << 14) | (p.USER << 12) | (7 << 5),
|
a.mov((p.KERNEL << 14) | (p.USER << 12) | (7 << 5),
|
||||||
asm.ptr(self.ioaddr(p, p.PS_OFFS)))
|
a.ptr(self.ioaddr(p, p.PS_OFFS)))
|
||||||
|
|
||||||
# turn on 22-bit mode, unibus mapping, and I/D sep for k & u
|
# turn on 22-bit mode, unibus mapping, and I/D sep for k & u
|
||||||
asm.mov(0o000065, asm.ptr(self.ioaddr(p, p.mmu.MMR3_OFFS)))
|
a.mov(0o000065, a.ptr(cn.MMR3))
|
||||||
|
|
||||||
# turn on relocation mode ... yeehah! (MMR0 known zero here)
|
# Instructions supplied by caller, to be execute before
|
||||||
asm.inc(asm.ptr(self.ioaddr(p, p.mmu.MMR0_OFFS)))
|
# enabling the MMU. They are "literals" since they have
|
||||||
|
# already been assembled.
|
||||||
|
for w in premmu:
|
||||||
|
a.literal(w)
|
||||||
|
|
||||||
|
# turn on relocation mode ...
|
||||||
|
a.inc(a.ptr(cn.MMR0))
|
||||||
|
|
||||||
asm.addtoblock(addons)
|
# and the post-MMU instructions
|
||||||
asm.halt()
|
for w in postmmu:
|
||||||
setup_instructions = asm.endblock()
|
a.literal(w)
|
||||||
|
a.halt()
|
||||||
|
|
||||||
instloc = 0o4000 # 2K
|
instloc = 0o4000 # 2K
|
||||||
|
self.loadphysmem(p, a.sequence(), instloc)
|
||||||
self.loadphysmem(p, setup_instructions, 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
|
||||||
def test_mfpi(self):
|
def test_mfpi(self):
|
||||||
# ((r0, ..., rN) results, (instructions)), ...
|
|
||||||
tvecs = (
|
|
||||||
|
|
||||||
# r1=2, mfpi (r1) -> r0; expect r0 = 33333
|
tvecs = []
|
||||||
((0o33333,), (0o012701, 0o02, 0o006511, 0o012600)),
|
|
||||||
|
|
||||||
# r1=0, mfpi (r1) -> r0; expect r0 = 22222
|
for result, r1tval in ((0o33333, 2), (0o22222, 0)):
|
||||||
((0o22222,), (0o012701, 0o00, 0o006511, 0o012600)),
|
# r1=r1tval, mfpi (r1) -> r0; expect r0 = result
|
||||||
)
|
with ASM.newsequence() as a:
|
||||||
|
a.mov(r1tval, 'r1')
|
||||||
|
a.mfpi('(r1)')
|
||||||
|
a.mov('(sp)+', 'r0')
|
||||||
|
tvecs.append((result, a.sequence()))
|
||||||
|
|
||||||
for rslts, insts in tvecs:
|
for result, insts in tvecs:
|
||||||
with self.subTest(rslts=rslts, insts=insts):
|
with self.subTest(result=result, insts=insts):
|
||||||
p, pc = self.simplemapped_pdp(addons=insts)
|
p, pc = self.simplemapped_pdp(postmmu=insts)
|
||||||
p.run(pc=pc)
|
p.run(pc=pc)
|
||||||
for rN, v in enumerate(rslts):
|
self.assertEqual(p.r[0], result)
|
||||||
self.assertEqual(p.r[rN], v)
|
|
||||||
|
|
||||||
def test_mfpxsp(self):
|
def test_mfpxsp(self):
|
||||||
cn = self.usefulconstants()
|
cn = self.usefulconstants()
|
||||||
insts = (
|
|
||||||
# gotta turn mapping back off for these...
|
|
||||||
0o005037, cn.MMR0, # CLR MMR0
|
|
||||||
0o012737, 0o14000, 0o34, # mov $14000,*#34
|
|
||||||
0o005037, 0o36, # clear *#36 .. perfectly fine PSW
|
|
||||||
|
|
||||||
0o012700, 0o20000, # mov #20000,r0
|
# these two instructions are needed as literals in the test sequence
|
||||||
0o012720, 0o010206, # put into user 0: mov r2,r6
|
with ASM.newsequence() as u:
|
||||||
0o012720, 0o104400, # put into user 2: trap 0
|
u.mov('r2', 'r6')
|
||||||
|
u.trap(0)
|
||||||
|
user_mode_instructions = u.sequence()
|
||||||
|
|
||||||
0o012702, 0o123456, # put 123456 into R2
|
with ASM.newsequence() as premmu:
|
||||||
0o012746, 0o140340, # push user-ish PSW onto kernel stack
|
ts = premmu # just for brevity...
|
||||||
0o005046, # new user PC == 0
|
ts.mov(0o14000, ts.ptr(0o34)) # set vector 034 to 14000
|
||||||
0o005237, cn.MMR0, # back on with the mapping!
|
ts.clr(ts.ptr(0o36)) # PSW for trap - zero work
|
||||||
|
ts.mov(0o20000, 'r0') # mov #20000,r0
|
||||||
|
|
||||||
0o000006, # RTT -- goes to user mode, addr 0
|
for uinst in user_mode_instructions:
|
||||||
)
|
ts.mov(uinst, '(r0)+')
|
||||||
|
ts.mov(0o123456, 'r2') # mov #123456,r2
|
||||||
|
ts.mov(0o140340, '-(sp)') # push user-ish PSW to K stack
|
||||||
|
ts.clr('-(sp)') # new user PC = 0
|
||||||
|
|
||||||
p, pc = self.simplemapped_pdp(addons=insts)
|
with ASM.newsequence() as postmmu:
|
||||||
|
postmmu.literal(6) # RTT - goes to user mode, addr 0
|
||||||
|
|
||||||
|
p, pc = self.simplemapped_pdp(premmu=premmu.sequence(),
|
||||||
|
postmmu=postmmu.sequence())
|
||||||
|
|
||||||
# put the trap handler at 14000 as expected
|
# put the trap handler at 14000 as expected
|
||||||
traph = (
|
with ASM.newsequence() as th:
|
||||||
0o106506, # mfpd sp
|
th.mfpd('sp')
|
||||||
0o012603, # pop stack into r3
|
th.mov('(sp)+', 'r3')
|
||||||
0
|
th.halt()
|
||||||
)
|
self.loadphysmem(p, th.sequence(), 0o14000)
|
||||||
|
|
||||||
self.loadphysmem(p, traph, 0o14000)
|
|
||||||
p.instlog = True
|
|
||||||
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):
|
||||||
# need an instance just for the constants, meh
|
cn = self.usefulconstants()
|
||||||
px = self.make_pdp()
|
|
||||||
tvecs = (
|
tvecs = (
|
||||||
((0o1717,), (0o012746, 0o1717, 0o006637, 0o02,
|
(0o1717, (0o012746, 0o1717, 0o006637, 0o02,
|
||||||
# turn MMU back off (!)
|
# turn MMU back off (!)
|
||||||
0o005037, self.ioaddr(px, px.mmu.MMR0_OFFS),
|
0o005037, cn.MMR0,
|
||||||
0o013700, 0o20002)),
|
0o013700, 0o20002)),
|
||||||
)
|
)
|
||||||
for rslts, insts in tvecs:
|
for r0result, insts in tvecs:
|
||||||
with self.subTest(rslts=rslts, insts=insts):
|
with self.subTest(r0result=r0result, insts=insts):
|
||||||
p, pc = self.simplemapped_pdp(addons=insts)
|
p, pc = self.simplemapped_pdp(postmmu=insts)
|
||||||
p.run(pc=pc)
|
p.run(pc=pc)
|
||||||
for rN, v in enumerate(rslts):
|
self.assertEqual(p.r[0], r0result)
|
||||||
self.assertEqual(p.r[rN], v)
|
|
||||||
|
|
||||||
def test_add_sub(self):
|
def test_add_sub(self):
|
||||||
p = self.make_pdp()
|
p = self.make_pdp()
|
||||||
|
@ -258,10 +270,12 @@ class TestMethods(unittest.TestCase):
|
||||||
add_loc = testloc
|
add_loc = testloc
|
||||||
sub_loc = testloc + 4
|
sub_loc = testloc + 4
|
||||||
|
|
||||||
p.physmem[add_loc >> 1] = 0o060001 # ADD R0,R1
|
for addsub, loc in (('add', add_loc), ('sub', sub_loc)):
|
||||||
p.physmem[(add_loc >> 1) + 1] = 0
|
with ASM.newsequence() as a:
|
||||||
p.physmem[sub_loc >> 1] = 0o160001 # SUB R0,R1
|
getattr(a, addsub)('r0', 'r1')
|
||||||
p.physmem[(sub_loc >> 1) + 1] = 0
|
a.halt()
|
||||||
|
for offs, inst in enumerate(a.sequence()):
|
||||||
|
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:
|
||||||
with self.subTest(r0=r0, r1=r1, op="add"):
|
with self.subTest(r0=r0, r1=r1, op="add"):
|
||||||
|
|
Loading…
Add table
Reference in a new issue