call/return

This commit is contained in:
Irmen de Jong
2018-03-02 02:49:43 +01:00
parent 8e7c504a88
commit 0603b93f47
4 changed files with 62 additions and 36 deletions

View File

@@ -2,6 +2,7 @@
%block b1 %block b1
%vardefs %vardefs
var byte teller 1 var byte teller 1
var byte numbertoprint 0
const byte one 1 const byte one 1
const byte thousand 100000 const byte thousand 100000
var array_byte newlinestr 2 [32 32] var array_byte newlinestr 2 [32 32]
@@ -17,17 +18,20 @@ back:
push teller push teller
push one push one
add add
pop teller dup
push teller dup
pop teller
call 1 printnumber
push thousand
cmp_lt
jump_if_true back
return 0
printnumber:
syscall decimalstr_signed syscall decimalstr_signed
syscall printstr syscall printstr
push newlinestr push newlinestr
syscall printstr syscall printstr
push teller return 0
push thousand
cmp_lt
jump_if_true back
return
%end_instructions %end_instructions
%subblocks %subblocks

View File

@@ -11,6 +11,8 @@ class Opcode(enum.IntEnum):
POP = 13 POP = 13
POP2 = 14 POP2 = 14
POP3 = 15 POP3 = 15
DUP = 16
DUP2 = 17
ADD = 50 ADD = 50
SUB = 51 SUB = 51
MUL = 52 MUL = 52

View File

@@ -116,6 +116,8 @@ class Parser:
args = parts[1].split() args = parts[1].split()
else: else:
args = [] args = []
if opcode in (Opcode.CALL, Opcode.RETURN):
args[0] = int(args[0]) # the number of arguments/parameters
return Instruction(opcode, args, None, None) return Instruction(opcode, args, None, None)
while not self.source[self.lineno].startswith("%"): while not self.source[self.lineno].startswith("%"):
@@ -175,7 +177,8 @@ l1:
nop nop
push c1 push c1
push2 c1 cws push2 c1 cws
return call 3 l1
return 2
%end_instructions %end_instructions
%subblocks %subblocks
@@ -198,7 +201,7 @@ l1:
nop nop
l1: l1:
nop nop
return return 99
%end_instructions %end_instructions
%end_block ; b3 %end_block ; b3
""" """

View File

@@ -134,17 +134,19 @@ class Memory:
class CallFrameMarker: class CallFrameMarker:
pass __slots__ = ["returninstruction"]
class ReturnInstruction:
__slots__ = ["instruction"]
def __init__(self, instruction: Instruction) -> None: def __init__(self, instruction: Instruction) -> None:
self.instruction = instruction self.returninstruction = instruction
def __str__(self) -> str:
return repr(self)
def __repr__(self) -> str:
return "<CallFrameMarker returninstruction={:s}>".format(str(self.returninstruction))
StackValueType = Union[bool, int, float, bytearray, array.array, CallFrameMarker, ReturnInstruction] StackValueType = Union[bool, int, float, bytearray, array.array, CallFrameMarker]
class Stack: class Stack:
@@ -176,6 +178,9 @@ class Stack:
self.pop_history.append(z) self.pop_history.append(z)
return x, y, z return x, y, z
def pop_under(self, number: int) -> StackValueType:
return self.stack.pop(-1-number)
def push(self, item: StackValueType) -> None: def push(self, item: StackValueType) -> None:
self._typecheck(item) self._typecheck(item)
self.stack.append(item) self.stack.append(item)
@@ -192,6 +197,9 @@ class Stack:
self._typecheck(third) self._typecheck(third)
self.stack.extend([first, second, third]) self.stack.extend([first, second, third])
def push_under(self, number: int, value: StackValueType) -> None:
self.stack.insert(-number, value)
def peek(self) -> StackValueType: def peek(self) -> StackValueType:
return self.stack[-1] if self.stack else None return self.stack[-1] if self.stack else None
@@ -201,7 +209,7 @@ class Stack:
self.stack[-2] = x self.stack[-2] = x
def _typecheck(self, value: StackValueType): def _typecheck(self, value: StackValueType):
if type(value) not in (bool, int, float, bytearray, array.array, CallFrameMarker, ReturnInstruction): if type(value) not in (bool, int, float, bytearray, array.array, CallFrameMarker):
raise TypeError("invalid item type pushed") raise TypeError("invalid item type pushed")
@@ -223,6 +231,9 @@ class VM:
if method.startswith("opcode_"): if method.startswith("opcode_"):
if not method[7:] in opcode_names: if not method[7:] in opcode_names:
raise RuntimeError("opcode method for undefined opcode " + method) raise RuntimeError("opcode method for undefined opcode " + method)
for oc in Opcode:
if oc not in self.dispatch_table:
raise NotImplementedError("no dispatch entry in table for " + oc.name)
self.memory = Memory() self.memory = Memory()
for start, end in self.readonly_mem_ranges: for start, end in self.readonly_mem_ranges:
self.memory.mark_readonly(start, end) self.memory.mark_readonly(start, end)
@@ -305,15 +316,14 @@ class VM:
elif i.opcode == Opcode.JUMP: elif i.opcode == Opcode.JUMP:
i.next = self.labels[i.args[0]] # jump target i.next = self.labels[i.args[0]] # jump target
elif i.opcode == Opcode.CALL: elif i.opcode == Opcode.CALL:
i.next = self.labels[i.args[0]] # call target i.next = self.labels[i.args[1]] # call target
i.alt_next = nexti # return instruction i.alt_next = nexti # return instruction
else: else:
i.next = nexti i.next = nexti
def run(self) -> None: def run(self) -> None:
self.pc = self.program[0] # first instruction of the main program self.pc = self.program[0] # first instruction of the main program
self.stack.push(ReturnInstruction(None)) # sentinel self.stack.push(CallFrameMarker(None)) # enter the call frame so the timer program can end with a RETURN
self.stack.push(CallFrameMarker()) # enter the call frame so the timer program can end with a RETURN
try: try:
while self.pc is not None: while self.pc is not None:
with self.timer_irq_interlock: with self.timer_irq_interlock:
@@ -346,8 +356,7 @@ class VM:
self.stack = self.timer_stack self.stack = self.timer_stack
self.program = self.timer_program self.program = self.timer_program
self.pc = self.program[0] self.pc = self.program[0]
self.stack.push(ReturnInstruction(None)) # sentinel self.stack.push(CallFrameMarker(None)) # enter the call frame so the timer program can end with a RETURN
self.stack.push(CallFrameMarker()) # enter the call frame so the timer program can end with a RETURN
while self.pc is not None: while self.pc is not None:
next_pc = self.dispatch_table[self.pc.opcode](self, self.pc) next_pc = self.dispatch_table[self.pc.opcode](self, self.pc)
if next_pc: if next_pc:
@@ -362,14 +371,14 @@ class VM:
def debug_stack(self, size: int=5) -> None: def debug_stack(self, size: int=5) -> None:
stack = self.stack.debug_peek(size) stack = self.stack.debug_peek(size)
if len(stack) > 0: if len(stack) > 0:
print("* stack (top {:d}):".format(size)) print("** stack (top {:d}):".format(size))
for i, value in enumerate(reversed(stack), start=1): for i, value in enumerate(reversed(stack), start=1):
print(" {:d}. {:s} {!r}".format(i, type(value).__name__, value)) print(" {:d}. {:s} {:s}".format(i, type(value).__name__, str(value)))
else: else:
print("* stack is empty.") print("** stack is empty.")
if self.stack.pop_history: if self.stack.pop_history:
print("* last {:d} values popped from stack (most recent last):".format(self.stack.pop_history.maxlen)) print("** last {:d} values popped from stack (most recent on top):".format(self.stack.pop_history.maxlen))
pprint.pprint(list(self.stack.pop_history), indent=2, compact=True, width=20) # type: ignore pprint.pprint(list(reversed(self.stack.pop_history)), indent=2, compact=True, width=20) # type: ignore
if self.pc is not None: if self.pc is not None:
print("* instruction:", self.pc) print("* instruction:", self.pc)
@@ -389,6 +398,16 @@ class VM:
self.stack.push(value) self.stack.push(value)
return True return True
def opcode_DUP(self, instruction: Instruction) -> bool:
self.stack.push(self.stack.peek())
return True
def opcode_DUP2(self, instruction: Instruction) -> bool:
x = self.stack.peek()
self.stack.push(x)
self.stack.push(x)
return True
def opcode_PUSH2(self, instruction: Instruction) -> bool: def opcode_PUSH2(self, instruction: Instruction) -> bool:
value1 = self.variables[instruction.args[0]].value value1 = self.variables[instruction.args[0]].value
value2 = self.variables[instruction.args[1]].value value2 = self.variables[instruction.args[1]].value
@@ -497,18 +516,14 @@ class VM:
return True return True
def opcode_CALL(self, instruction: Instruction) -> bool: def opcode_CALL(self, instruction: Instruction) -> bool:
self.stack.push(ReturnInstruction(instruction.alt_next)) # arguments are already on the stack
self.stack.push(CallFrameMarker()) self.stack.push_under(instruction.args[0], CallFrameMarker(instruction.alt_next))
return True return True
def opcode_RETURN(self, instruction: Instruction) -> bool: def opcode_RETURN(self, instruction: Instruction) -> bool:
# unwind the function call frame callframe = self.stack.pop_under(instruction.args[0])
item = self.stack.pop() assert isinstance(callframe, CallFrameMarker)
while not isinstance(item, CallFrameMarker): self.pc = callframe.returninstruction
item = self.stack.pop()
returninstruction = self.stack.pop()
assert isinstance(returninstruction, ReturnInstruction)
self.pc = returninstruction.instruction
return False return False
def opcode_JUMP(self, instruction: Instruction) -> bool: def opcode_JUMP(self, instruction: Instruction) -> bool:
@@ -544,6 +559,8 @@ class VM:
Opcode.POP: opcode_POP, Opcode.POP: opcode_POP,
Opcode.POP2: opcode_POP2, Opcode.POP2: opcode_POP2,
Opcode.POP3: opcode_POP3, Opcode.POP3: opcode_POP3,
Opcode.DUP: opcode_DUP,
Opcode.DUP2: opcode_DUP2,
Opcode.ADD: opcode_ADD, Opcode.ADD: opcode_ADD,
Opcode.SUB: opcode_SUB, Opcode.SUB: opcode_SUB,
Opcode.MUL: opcode_MUL, Opcode.MUL: opcode_MUL,