stackvm now uses a proper instruction pointer call stack instead of instruction linking

This commit is contained in:
Irmen de Jong 2019-03-13 22:00:41 +01:00
parent 1aaf854ef7
commit bf3caaefe1
7 changed files with 150 additions and 164 deletions

View File

@ -18,7 +18,7 @@ fun stackVmMain(args: Array<String>) {
}
val program = Program.load(args.first())
val vm = StackVm(traceOutputFile = "trace.txt") // TODO null
val vm = StackVm(traceOutputFile = null)
val dialog = ScreenDialog()
vm.load(program, dialog.canvas)
EventQueue.invokeLater {

View File

@ -233,7 +233,7 @@ private class VarInitValueCreator: IAstProcessor {
val scope = decl.definingScope()
if(scope !in vardeclsToAdd)
vardeclsToAdd[scope] = mutableListOf()
vardeclsToAdd[scope]!!.add(decl.asDefaultValueDecl(null))
vardeclsToAdd.getValue(scope).add(decl.asDefaultValueDecl(null))
val declvalue = decl.value!!
val value =
if(declvalue is LiteralValue) {

View File

@ -8,8 +8,6 @@ open class Instruction(val opcode: Opcode,
val callLabel: String? = null,
val callLabel2: String? = null)
{
lateinit var next: Instruction
var nextAlt: Instruction? = null
var branchAddress: Int? = null
override fun toString(): String {

View File

@ -244,7 +244,6 @@ enum class Opcode {
JZW, // branch if value is zero (word)
JNZW, // branch if value is not zero (word)
// subroutines
CALL,
RETURN,

View File

@ -11,7 +11,7 @@ class Program (val name: String,
val program: MutableList<Instruction>,
val variables: Map<String, Value>,
val memoryPointers: Map<String, Pair<Int, DataType>>,
val labels: Map<String, Instruction>,
val labels: Map<String, Int>,
val memory: Map<Int, List<Value>>,
val heap: HeapValues)
{
@ -20,7 +20,6 @@ class Program (val name: String,
program.add(LabelInstr("____program_end", false))
program.add(Instruction(Opcode.TERMINATE))
program.add(Instruction(Opcode.NOP))
connect()
}
companion object {
@ -31,7 +30,7 @@ class Program (val name: String,
val program = mutableListOf<Instruction>()
val variables = mutableMapOf<String, Value>()
val memoryPointers = mutableMapOf<String, Pair<Int, DataType>>()
val labels = mutableMapOf<String, Instruction>()
val labels = mutableMapOf<String, Int>()
while(lines.hasNext()) {
val (lineNr, line) = lines.next()
@ -53,7 +52,7 @@ class Program (val name: String,
program: MutableList<Instruction>,
variables: MutableMap<String, Value>,
memoryPointers: MutableMap<String, Pair<Int, DataType>>,
labels: MutableMap<String, Instruction>)
labels: MutableMap<String, Int>)
{
while(true) {
val (_, line) = lines.next()
@ -67,8 +66,10 @@ class Program (val name: String,
loadMemoryPointers(lines, memoryPointers, heap)
else if(line=="%instructions") {
val (blockInstructions, blockLabels) = loadInstructions(lines, heap)
val baseIndex = program.size
program.addAll(blockInstructions)
labels.putAll(blockLabels)
val labelsWithIndex = blockLabels.mapValues { baseIndex+blockInstructions.indexOf(it.value) }
labels.putAll(labelsWithIndex)
}
}
}
@ -272,63 +273,4 @@ class Program (val name: String,
}
}
}
private fun connect() {
// TODO: because of JUMP instructions, the below doesn't work
// you cannot link instructions with just 1 instruction flow
// because JUMPS to another place will cause a RETURN to return back to different locations depending on what's called it...
// probably just need a real instruction pointer (based on index?) and call stack.
val it1 = program.iterator()
val it2 = program.iterator()
it2.next()
while(it1.hasNext() && it2.hasNext()) {
val instr = it1.next()
val nextInstr = it2.next()
when(instr.opcode) {
Opcode.TERMINATE -> instr.next = instr // won't ever execute a next instruction
Opcode.RETURN -> instr.next = instr // kinda a special one, in actuality the return instruction is dynamic
Opcode.JUMP -> {
if(instr.callLabel==null)
throw VmExecutionException("stackVm doesn't support JUMP to memory address")
else
linkJumpTarget(instr)
}
Opcode.BCC, Opcode.BCS, Opcode.BZ, Opcode.BNZ, Opcode.BNEG, Opcode.BPOS, Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW -> {
if(instr.callLabel==null) {
throw VmExecutionException("stackVm doesn't support branch to memory address")
} else {
linkJumpTarget(instr) // branch label
instr.nextAlt = nextInstr // alternate branch
}
}
Opcode.CALL -> {
if(instr.callLabel==null) {
throw VmExecutionException("stackVm doesn't support CALL to memory address")
} else {
linkJumpTarget(instr) // call label
instr.nextAlt = nextInstr // instruction to return to
}
}
else -> instr.next = nextInstr
}
}
}
private fun linkJumpTarget(instr: Instruction) {
val target = labels[instr.callLabel]
if(target==null) {
val memtarget = memoryPointers[instr.callLabel]?.first
if(memtarget==null)
throw VmExecutionException("undefined label: ${instr.callLabel}")
else {
instr.branchAddress=memtarget
}
} else
instr.next = target
}
}

View File

@ -6,6 +6,7 @@ import prog8.compiler.intermediate.Instruction
import prog8.compiler.intermediate.Opcode
import prog8.compiler.intermediate.Value
import prog8.compiler.target.c64.Petscii
import prog8.compiler.toHex
import java.io.File
import java.io.PrintStream
import java.util.*
@ -152,23 +153,24 @@ class StackVm(private var traceOutputFile: String?) {
private set
var evalstack = MyStack<Value>()
private set
var callstack = MyStack<Instruction>()
var callstack = MyStack<Int>()
private set
private var program = listOf<Instruction>()
private var labels = emptyMap<String, Instruction>()
private var labels = emptyMap<String, Int>()
private var heap = HeapValues()
private var traceOutput = if(traceOutputFile!=null) PrintStream(File(traceOutputFile), "utf-8") else null
private var canvas: BitmapScreenPanel? = null
private val rnd = Random()
private val bootTime = System.currentTimeMillis()
private lateinit var currentIns: Instruction
private var irqStartInstruction: Instruction? = null
private var currentInstructionPtr: Int = -1
private var irqStartInstructionPtr: Int = -1
private var registerSaveX: Value = Value(DataType.UBYTE, 0)
var sourceLine: String = ""
private set
fun load(program: Program, canvas: BitmapScreenPanel?) {
this.program = program.program
this.program = program.program + Instruction(Opcode.RETURN) // append a RETURN for use in the IRQ handler
this.labels = program.labels
this.heap = program.heap
this.canvas = canvas
@ -189,8 +191,8 @@ class StackVm(private var traceOutputFile: String?) {
P_carry = false
P_irqd = false
sourceLine = ""
currentIns = this.program[0]
irqStartInstruction = labels["irq.irq"] // set to first instr of irq routine, if any
currentInstructionPtr = 0
irqStartInstructionPtr = labels["irq.irq"] ?: -1 // set to first instr of irq routine, if any
initBlockVars()
}
@ -200,14 +202,14 @@ class StackVm(private var traceOutputFile: String?) {
// this is done by calling the special init subroutine of each block that has one.
val initVarsSubs = labels.filter { it.key.endsWith("."+ initvarsSubName) }
for(init in initVarsSubs) {
currentIns = init.value
currentInstructionPtr = init.value
try {
step(Int.MAX_VALUE)
} catch(x: VmTerminationException) {
// init subroutine finished
}
}
currentIns = program[0]
currentInstructionPtr = 0
}
fun step(instructionCount: Int = 5000) {
@ -216,13 +218,13 @@ class StackVm(private var traceOutputFile: String?) {
val start = System.currentTimeMillis()
for(i:Int in 1..instructionCount) {
try {
currentIns = dispatch(currentIns)
dispatch()
if (evalstack.size > 128)
throw VmExecutionException("too many values on evaluation stack")
if (callstack.size > 128)
throw VmExecutionException("too many nested/recursive calls")
} catch (bp: VmBreakpointException) {
currentIns = currentIns.next
currentInstructionPtr++ // TODO necessary still?
println("breakpoint encountered, source line: $sourceLine")
throw bp
} catch (es: EmptyStackException) {
@ -294,7 +296,8 @@ class StackVm(private var traceOutputFile: String?) {
throw VmExecutionException("unknown variable: $name")
}
private fun dispatch(ins: Instruction) : Instruction {
private fun dispatch() {
val ins = program[currentInstructionPtr]
traceOutput?.println("\n$ins")
when (ins.opcode) {
Opcode.NOP -> {}
@ -976,43 +979,75 @@ class StackVm(private var traceOutputFile: String?) {
mem.setUWord(addr, newValue.integerValue())
setFlags(newValue)
}
Opcode.JUMP -> {} // do nothing; the next instruction is wired up already to the jump target
Opcode.BCS ->
return if(P_carry) ins.next else ins.nextAlt!!
Opcode.BCC ->
return if(P_carry) ins.nextAlt!! else ins.next
Opcode.BZ ->
return if(P_zero) ins.next else ins.nextAlt!!
Opcode.BNZ ->
return if(P_zero) ins.nextAlt!! else ins.next
Opcode.BNEG ->
return if(P_negative) ins.next else ins.nextAlt!!
Opcode.BPOS ->
return if(P_negative) ins.nextAlt!! else ins.next
Opcode.JUMP -> {
currentInstructionPtr = determineBranchInstr(ins)
return
}
Opcode.BCS -> {
if (P_carry) currentInstructionPtr = determineBranchInstr(ins)
else currentInstructionPtr++
return
}
Opcode.BCC -> {
if (P_carry) currentInstructionPtr++
else currentInstructionPtr = determineBranchInstr(ins)
return
}
Opcode.BZ -> {
if (P_zero) currentInstructionPtr = determineBranchInstr(ins)
else currentInstructionPtr++
return
}
Opcode.BNZ -> {
if (P_zero) currentInstructionPtr++
else currentInstructionPtr = determineBranchInstr(ins)
return
}
Opcode.BNEG -> {
if (P_negative) currentInstructionPtr = determineBranchInstr(ins)
else currentInstructionPtr++
return
}
Opcode.BPOS -> {
if (P_negative) currentInstructionPtr++
else currentInstructionPtr = determineBranchInstr(ins)
return
}
Opcode.BVS, Opcode.BVC -> throw VmExecutionException("stackVM doesn't support the 'overflow' cpu flag")
Opcode.JZ -> {
val value = evalstack.pop().integerValue() and 255
return if(value==0) ins.next else ins.nextAlt!!
if (value==0) currentInstructionPtr = determineBranchInstr(ins)
else currentInstructionPtr++
return
}
Opcode.JNZ -> {
val value = evalstack.pop().integerValue() and 255
return if(value!=0) ins.next else ins.nextAlt!!
if (value!=0) currentInstructionPtr = determineBranchInstr(ins)
else currentInstructionPtr++
return
}
Opcode.JZW -> {
val value = evalstack.pop().integerValue()
return if(value==0) ins.next else ins.nextAlt!!
if (value==0) currentInstructionPtr = determineBranchInstr(ins)
else currentInstructionPtr++
return
}
Opcode.JNZW -> {
val value = evalstack.pop().integerValue()
return if(value!=0) ins.next else ins.nextAlt!!
if (value!=0) currentInstructionPtr = determineBranchInstr(ins)
else currentInstructionPtr++
return
}
Opcode.CALL -> {
callstack.push(currentInstructionPtr + 1)
currentInstructionPtr = determineBranchInstr(ins)
return
}
Opcode.CALL ->
callstack.push(ins.nextAlt)
Opcode.RETURN -> {
if(callstack.empty())
throw VmTerminationException("return instruction with empty call stack")
return callstack.pop()
currentInstructionPtr = callstack.pop()
return
}
Opcode.PUSH_VAR_BYTE -> {
val value = getVar(ins.callLabel!!)
@ -1030,18 +1065,18 @@ class StackVm(private var traceOutputFile: String?) {
evalstack.push(value)
}
Opcode.PUSH_REGAX_WORD -> {
val a=variables["A"]!!.integerValue()
val x=variables["X"]!!.integerValue()
val a=variables.getValue("A").integerValue()
val x=variables.getValue("X").integerValue()
evalstack.push(Value(DataType.UWORD, x*256+a))
}
Opcode.PUSH_REGAY_WORD -> {
val a=variables["A"]!!.integerValue()
val y=variables["Y"]!!.integerValue()
val a=variables.getValue("A").integerValue()
val y=variables.getValue("Y").integerValue()
evalstack.push(Value(DataType.UWORD, y*256+a))
}
Opcode.PUSH_REGXY_WORD -> {
val x=variables["X"]!!.integerValue()
val y=variables["Y"]!!.integerValue()
val x=variables.getValue("X").integerValue()
val y=variables.getValue("Y").integerValue()
evalstack.push(Value(DataType.UWORD, y*256+x))
}
Opcode.POP_REGAX_WORD -> {
@ -1581,7 +1616,7 @@ class StackVm(private var traceOutputFile: String?) {
val index = evalstack.pop().integerValue()
val result =
if(ins.callLabel in memoryPointers) {
val variable = memoryPointers[ins.callLabel]!!
val variable = memoryPointers.getValue(ins.callLabel!!)
val address = variable.first + index
when(variable.second) {
DataType.ARRAY_UB -> Value(DataType.UBYTE, mem.getUByte(address))
@ -1612,7 +1647,7 @@ class StackVm(private var traceOutputFile: String?) {
val index = evalstack.pop().integerValue()
val result=
if(ins.callLabel in memoryPointers) {
val variable = memoryPointers[ins.callLabel]!!
val variable = memoryPointers.getValue(ins.callLabel!!)
val address = variable.first + index*2
when(variable.second) {
DataType.ARRAY_UW -> Value(DataType.UWORD, mem.getUWord(address))
@ -1643,7 +1678,7 @@ class StackVm(private var traceOutputFile: String?) {
val index = evalstack.pop().integerValue()
val result =
if(ins.callLabel in memoryPointers) {
val variable = memoryPointers[ins.callLabel]!!
val variable = memoryPointers.getValue(ins.callLabel!!)
val address = variable.first + index*5
if(variable.second==DataType.ARRAY_F)
Value(DataType.FLOAT, mem.getFloat(address))
@ -1789,18 +1824,11 @@ class StackVm(private var traceOutputFile: String?) {
P_carry = evalstack.pop().asBooleanValue
P_irqd = evalstack.pop().asBooleanValue
}
Opcode.RSAVEX -> {
evalstack.push(variables["X"])
println("-----rsaveX called, stacksize ${evalstack.size}") // TODO
}
Opcode.RRESTOREX -> {
println("-----rrestoreX called, stacksize before ${evalstack.size}") // TODO
// TODO called too ofen -> stack error
variables["X"] = evalstack.pop()
}
Opcode.RSAVEX -> registerSaveX = variables.getValue("X")
Opcode.RRESTOREX -> variables["X"] = registerSaveX
Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code $ins")
Opcode.PUSH_ADDR_HEAPVAR -> {
val heapId = variables[ins.callLabel]!!.heapId
val heapId = variables.getValue(ins.callLabel!!).heapId
if(heapId<0)
throw VmExecutionException("expected variable on heap")
evalstack.push(Value(DataType.UWORD, heapId)) // push the "address" of the string or array variable (this is taken care of properly in the assembly code generator)
@ -1834,25 +1862,38 @@ class StackVm(private var traceOutputFile: String?) {
evalstack.printTop(4, traceOutput!!)
}
currentInstructionPtr++
}
private fun determineBranchInstr(ins: Instruction): Int {
if(ins.branchAddress!=null) {
// this is an instruction that jumps to a system routine (memory address)
if(ins.nextAlt==null)
throw VmExecutionException("call to system routine requires nextAlt return instruction set: $ins")
else {
when(ins.callLabel) {
"c64.CLEARSCR" -> {
println(" evalstack (size=${evalstack.size}):")
evalstack.printTop(4, System.out)
canvas?.clearScreen(mem.getUByte(0xd021).toInt())
}
else -> {
TODO("SYSTEM ROUTINE ${ins.callLabel}")
}
TODO("call to a system memory routine at ${ins.branchAddress!!.toHex()}")
throw VmExecutionException("stackVm doesn't support branching to a memory address")
}
return if(ins.callLabel==null)
throw VmExecutionException("requires label to branch to")
else {
when(ins.callLabel) {
"c64.CLEARSCR" -> {
canvas?.clearScreen(mem.getUByte(0xd021).toInt())
callstack.pop()
}
"c64.CHROUT" -> {
// TODO sometimes the character ends up on the wrong screen position because the text-output routines don't update the cursorpos!
val sc=variables.getValue("A").integerValue()
val (x, y) = canvas?.getCursorPos()!!
canvas?.setChar(x, y, sc.toShort())
callstack.pop()
}
"c64.GETIN" -> {
variables["A"] = Value(DataType.UBYTE, 0) // TODO keyboard input
callstack.pop()
}
else -> {
labels.getValue(ins.callLabel)
}
return ins.nextAlt!!
}
} else
return ins.next
}
}
private fun setFlags(value: Value?) {
@ -2150,33 +2191,33 @@ class StackVm(private var traceOutputFile: String?) {
}
Syscall.SYSCALLSTUB -> throw VmExecutionException("unimplemented sysasm called: ${ins.callLabel} Create a Syscall enum for this and implement the vm intercept for it.")
Syscall.SYSASM_c64scr_PLOT -> {
val x = variables["Y"]!!.integerValue()
val y = variables["A"]!!.integerValue()
val x = variables.getValue("Y").integerValue()
val y = variables.getValue("A").integerValue()
canvas?.setCursorPos(x, y)
}
Syscall.SYSASM_c64scr_print -> {
val (x, y) = canvas!!.getCursorPos()
val straddr = variables["A"]!!.integerValue() + 256*variables["Y"]!!.integerValue()
val straddr = variables.getValue("A").integerValue() + 256*variables.getValue("Y").integerValue()
val str = heap.get(straddr).str!!
canvas?.writeText(x, y, str, 1, true)
}
Syscall.SYSASM_c64scr_print_ub -> {
val (x, y) = canvas!!.getCursorPos()
val num = variables["A"]!!.integerValue()
val num = variables.getValue("A").integerValue()
canvas?.writeText(x, y, num.toString(), 1, true)
}
Syscall.SYSASM_c64scr_print_uw -> {
val (x, y) = canvas!!.getCursorPos()
val lo = variables["A"]!!.integerValue()
val hi = variables["Y"]!!.integerValue()
val lo = variables.getValue("A").integerValue()
val hi = variables.getValue("Y").integerValue()
val number = lo+256*hi
canvas?.writeText(x, y, number.toString(), 1, true)
}
Syscall.SYSASM_c64scr_setcc -> {
val x = variables["c64scr.setcc.column"]!!.integerValue()
val y = variables["c64scr.setcc.row"]!!.integerValue()
val char = variables["c64scr.setcc.char"]!!.integerValue()
// val color = variables["c64scr.setcc.color"]!!.integerValue() // text color other than 1 (white) can't be used right now
val x = variables.getValue("c64scr.setcc.column").integerValue()
val y = variables.getValue("c64scr.setcc.row").integerValue()
val char = variables.getValue("c64scr.setcc.char").integerValue()
// val color = variables.getValue("c64scr.setcc.color").integerValue() // text color other than 1 (white) can't be used right now
canvas?.setChar(x, y, char.toShort())
}
else -> throw VmExecutionException("unimplemented syscall $syscall")
@ -2197,7 +2238,7 @@ class StackVm(private var traceOutputFile: String?) {
mem.setUByte(0x00a1, (jiffies ushr 8 and 255).toShort())
mem.setUByte(0x00a2, (jiffies and 255).toShort())
if(irqStartInstruction!=null) {
if(irqStartInstructionPtr>=0) {
try {
// execute the irq routine
this.step(Int.MAX_VALUE)
@ -2211,20 +2252,26 @@ class StackVm(private var traceOutputFile: String?) {
}
private var irqStoredEvalStack = MyStack<Value>()
private var irqStoredCallStack = MyStack<Instruction>()
private var irqStoredCallStack = MyStack<Int>()
private var irqStoredCarry = false
private var irqStoredTraceOutputFile: String? = null
private var irqStoredMainInstruction: Instruction = Instruction(Opcode.TERMINATE)
private var irqStoredMainInstructionPtr = -1
private fun swapIrqExecutionContexts(startingIrq: Boolean) {
if(startingIrq) {
irqStoredMainInstruction = currentIns
irqStoredMainInstructionPtr = currentInstructionPtr
irqStoredCallStack = callstack
irqStoredEvalStack = evalstack
irqStoredCarry = P_carry
irqStoredTraceOutputFile = traceOutputFile
currentIns = irqStartInstruction ?: Instruction(Opcode.RETURN)
if(irqStartInstructionPtr>=0)
currentInstructionPtr = irqStartInstructionPtr
else {
if(program.last().opcode!=Opcode.RETURN)
throw VmExecutionException("last instruction in program should be RETURN for irq handler")
currentInstructionPtr = program.size-1
}
callstack = MyStack()
evalstack = MyStack()
P_carry = false
@ -2234,7 +2281,7 @@ class StackVm(private var traceOutputFile: String?) {
throw VmExecutionException("irq: eval stack is not empty at exit from irq program")
if(callstack.isNotEmpty())
throw VmExecutionException("irq: call stack is not empty at exit from irq program")
currentIns = irqStoredMainInstruction
currentInstructionPtr = irqStoredMainInstructionPtr
callstack = irqStoredCallStack
evalstack = irqStoredEvalStack
P_carry = irqStoredCarry

View File

@ -58,7 +58,7 @@ class TestStackVmOpcodes {
private fun makeProg(ins: MutableList<Instruction>,
vars: Map<String, Value>?=null,
memoryPointers: Map<String, Pair<Int, DataType>>?=null,
labels: Map<String, Instruction>?=null,
labels: Map<String, Int>?=null,
mem: Map<Int, List<Value>>?=null) : Program {
val heap = HeapValues()
return Program("test", ins, vars ?: mapOf(), memoryPointers ?: mapOf(), labels ?: mapOf(), mem ?: mapOf(), heap)
@ -751,7 +751,7 @@ class TestStackVmOpcodes {
Instruction(Opcode.LINE, callLabel = "string1"),
Instruction(Opcode.TERMINATE),
Instruction(Opcode.LINE, callLabel = "string2"))
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
vm.load(makeProg(ins, labels=labels), null)
vm.step(2)
assertEquals("", vm.sourceLine)
@ -771,7 +771,7 @@ class TestStackVmOpcodes {
Instruction(Opcode.LINE, callLabel = "string1"),
Instruction(Opcode.TERMINATE),
Instruction(Opcode.LINE, callLabel = "string2"))
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
vm.load(makeProg(ins, labels=labels), null)
assertFalse(vm.P_carry)
vm.step(2)
@ -792,7 +792,7 @@ class TestStackVmOpcodes {
Instruction(Opcode.LINE, callLabel = "string1"),
Instruction(Opcode.TERMINATE),
Instruction(Opcode.LINE, callLabel = "string2"))
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
vm.load(makeProg(ins, labels=labels), null)
vm.step(2)
assertEquals("", vm.sourceLine)
@ -812,7 +812,7 @@ class TestStackVmOpcodes {
Instruction(Opcode.LINE, callLabel = "string1"),
Instruction(Opcode.TERMINATE),
Instruction(Opcode.LINE, callLabel = "string2"))
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
vm.load(makeProg(ins, labels=labels), null)
vm.step(2)
assertEquals("", vm.sourceLine)
@ -832,7 +832,7 @@ class TestStackVmOpcodes {
Instruction(Opcode.LINE, callLabel = "string1"),
Instruction(Opcode.TERMINATE),
Instruction(Opcode.LINE, callLabel = "string2"))
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
vm.load(makeProg(ins, labels=labels), null)
vm.step(2)
assertEquals("", vm.sourceLine)
@ -852,7 +852,7 @@ class TestStackVmOpcodes {
Instruction(Opcode.LINE, callLabel = "string1"),
Instruction(Opcode.TERMINATE),
Instruction(Opcode.LINE, callLabel = "string2"))
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
vm.load(makeProg(ins, labels=labels), null)
vm.step(2)
assertEquals("", vm.sourceLine)
@ -869,7 +869,7 @@ class TestStackVmOpcodes {
Instruction(Opcode.LINE, callLabel = "string1"),
Instruction(Opcode.TERMINATE),
Instruction(Opcode.LINE, callLabel = "string2"))
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
vm.load(makeProg(ins, labels=labels), null)
vm.step(2)
assertEquals("string2", vm.sourceLine)
@ -889,7 +889,7 @@ class TestStackVmOpcodes {
vm.step(1)
}
vm.callstack.add(ins[2]) // set the LINE opcode as return instruction
vm.callstack.add(2) // set the LINE opcode as return instruction
assertEquals("", vm.sourceLine)
vm.step(2)
assertEquals("string1", vm.sourceLine)
@ -906,12 +906,12 @@ class TestStackVmOpcodes {
Instruction(Opcode.LINE, callLabel = "called"),
Instruction(Opcode.RETURN)
)
val labels = mapOf("label" to ins[3]) // points to the LINE instruction
val labels = mapOf("label" to 3) // points to the LINE instruction
vm.load(makeProg(ins, labels = labels), null)
vm.step(1)
assertEquals("", vm.sourceLine)
assertEquals(1, vm.callstack.size)
assertSame(ins[1], vm.callstack.peek())
assertSame(1, vm.callstack.peek())
vm.step(1)
assertEquals("called", vm.sourceLine)
vm.step(1)