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

View File

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

View File

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

View File

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