From bf3caaefe1f868be71becc628c2f16c0815ddced Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 13 Mar 2019 22:00:41 +0100 Subject: [PATCH] stackvm now uses a proper instruction pointer call stack instead of instruction linking --- compiler/src/prog8/StackVmMain.kt | 2 +- compiler/src/prog8/ast/StmtReorderer.kt | 2 +- .../compiler/intermediate/Instruction.kt | 2 - .../src/prog8/compiler/intermediate/Opcode.kt | 1 - compiler/src/prog8/stackvm/Program.kt | 70 +----- compiler/src/prog8/stackvm/StackVm.kt | 215 +++++++++++------- compiler/test/StackVMOpcodeTests.kt | 22 +- 7 files changed, 150 insertions(+), 164 deletions(-) diff --git a/compiler/src/prog8/StackVmMain.kt b/compiler/src/prog8/StackVmMain.kt index 10302a8c7..757972e19 100644 --- a/compiler/src/prog8/StackVmMain.kt +++ b/compiler/src/prog8/StackVmMain.kt @@ -18,7 +18,7 @@ fun stackVmMain(args: Array) { } 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 { diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index 5ab5e68a3..ec7e93547 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -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) { diff --git a/compiler/src/prog8/compiler/intermediate/Instruction.kt b/compiler/src/prog8/compiler/intermediate/Instruction.kt index b1d6dfaea..89a9c0451 100644 --- a/compiler/src/prog8/compiler/intermediate/Instruction.kt +++ b/compiler/src/prog8/compiler/intermediate/Instruction.kt @@ -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 { diff --git a/compiler/src/prog8/compiler/intermediate/Opcode.kt b/compiler/src/prog8/compiler/intermediate/Opcode.kt index 2e052b28f..23472e121 100644 --- a/compiler/src/prog8/compiler/intermediate/Opcode.kt +++ b/compiler/src/prog8/compiler/intermediate/Opcode.kt @@ -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, diff --git a/compiler/src/prog8/stackvm/Program.kt b/compiler/src/prog8/stackvm/Program.kt index b4d9a7270..8fdcb3b4a 100644 --- a/compiler/src/prog8/stackvm/Program.kt +++ b/compiler/src/prog8/stackvm/Program.kt @@ -11,7 +11,7 @@ class Program (val name: String, val program: MutableList, val variables: Map, val memoryPointers: Map>, - val labels: Map, + val labels: Map, val memory: Map>, 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() val variables = mutableMapOf() val memoryPointers = mutableMapOf>() - val labels = mutableMapOf() + val labels = mutableMapOf() while(lines.hasNext()) { val (lineNr, line) = lines.next() @@ -53,7 +52,7 @@ class Program (val name: String, program: MutableList, variables: MutableMap, memoryPointers: MutableMap>, - labels: MutableMap) + labels: MutableMap) { 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 - } } diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index acc5f0140..bf8d89194 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -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() private set - var callstack = MyStack() + var callstack = MyStack() private set private var program = listOf() - private var labels = emptyMap() + private var labels = emptyMap() 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() - private var irqStoredCallStack = MyStack() + private var irqStoredCallStack = MyStack() 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 diff --git a/compiler/test/StackVMOpcodeTests.kt b/compiler/test/StackVMOpcodeTests.kt index 51b98750c..78e1d74b6 100644 --- a/compiler/test/StackVMOpcodeTests.kt +++ b/compiler/test/StackVMOpcodeTests.kt @@ -58,7 +58,7 @@ class TestStackVmOpcodes { private fun makeProg(ins: MutableList, vars: Map?=null, memoryPointers: Map>?=null, - labels: Map?=null, + labels: Map?=null, mem: Map>?=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)