mirror of
https://github.com/irmen/prog8.git
synced 2025-08-14 22:27:48 +00:00
call/return
This commit is contained in:
18
testvm.txt
18
testvm.txt
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
"""
|
"""
|
||||||
|
71
tinyvm/vm.py
71
tinyvm/vm.py
@@ -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,
|
||||||
|
Reference in New Issue
Block a user