diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index ad06c32b9..2cbcc3f0f 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -299,6 +299,107 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } + private fun opcodePush(dt: DataType): Opcode { + return when (dt) { + DataType.BYTE -> Opcode.PUSH + DataType.WORD -> Opcode.PUSH_W + DataType.FLOAT -> Opcode.PUSH_F + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, + DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> Opcode.PUSH_W + } + } + + private fun opcodePushvar(dt: DataType): Opcode { + return when (dt) { + DataType.BYTE -> Opcode.PUSH_VAR + DataType.WORD -> Opcode.PUSH_VAR_W + DataType.FLOAT -> Opcode.PUSH_VAR_F + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, + DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> Opcode.PUSH_VAR_W + } + } + + private fun opcodePushvar(reg: Register): Opcode { + return when(reg) { + Register.A, Register.X, Register.Y -> Opcode.PUSH_VAR + Register.AX, Register.AY, Register.XY -> Opcode.PUSH_VAR_W + } + } + + private fun opcodePopvar(reg: Register): Opcode { + return when(reg) { + Register.A, Register.X, Register.Y -> Opcode.POP_VAR + Register.AX, Register.AY, Register.XY -> Opcode.POP_VAR_W + } + } + + private fun opcodeReadindexedvar(dt: DataType): Opcode { + return when (dt) { + DataType.ARRAY -> Opcode.READ_INDEXED_VAR + DataType.ARRAY_W -> Opcode.READ_INDEXED_VAR_W + DataType.ARRAY_F -> Opcode.READ_INDEXED_VAR_F + else -> throw CompilerException("invalid dt for indexed $dt") + } + } + + private fun opcodeWriteindexedvar(dt: DataType): Opcode { + return when (dt) { + DataType.ARRAY -> Opcode.WRITE_INDEXED_VAR + DataType.ARRAY_W -> Opcode.WRITE_INDEXED_VAR_W + DataType.ARRAY_F -> Opcode.WRITE_INDEXED_VAR_F + else -> throw CompilerException("invalid dt for indexed $dt") + } + } + + private fun opcodeDiscard(dt: DataType): Opcode { + return when(dt) { + DataType.BYTE -> Opcode.DISCARD + DataType.WORD -> Opcode.DISCARD_W + DataType.FLOAT -> Opcode.DISCARD_F + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, + DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> Opcode.DISCARD_W + } + } + + private fun opcodePopvar(dt: DataType): Opcode { + return when (dt) { + DataType.BYTE -> Opcode.POP_VAR + DataType.WORD -> Opcode.POP_VAR_W + DataType.FLOAT -> Opcode.POP_VAR_F + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, + DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> Opcode.POP_VAR_W + } + } + + private fun opcodeDecvar(reg: Register): Opcode { + return when(reg) { + Register.A, Register.X, Register.Y -> Opcode.DEC_VAR + Register.AX, Register.AY, Register.XY -> Opcode.DEC_VAR_W + } + } + + private fun opcodeIncvar(reg: Register): Opcode { + return when(reg) { + Register.A, Register.X, Register.Y -> Opcode.INC_VAR + Register.AX, Register.AY, Register.XY -> Opcode.INC_VAR_W + } + } + + private fun opcodeDecvar(dt: DataType): Opcode { + return when(dt) { + DataType.BYTE -> Opcode.DEC_VAR + DataType.WORD -> Opcode.DEC_VAR_W + else -> throw CompilerException("can't dec type $dt") + } + } + + private fun opcodeIncvar(dt: DataType): Opcode { + return when(dt) { + DataType.BYTE -> Opcode.INC_VAR + DataType.WORD -> Opcode.INC_VAR_W + else -> throw CompilerException("can't inc type $dt") + } + } private fun translate(stmt: Continue) { stackvmProg.line(stmt.position) @@ -413,7 +514,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, private fun translate(expr: IExpression) { when(expr) { - is RegisterExpr -> stackvmProg.instr(Opcode.PUSH_VAR, callLabel = expr.register.toString()) + is RegisterExpr -> { + val opcode = opcodePushvar(expr.register) + stackvmProg.instr(opcode, callLabel = expr.register.toString()) + } is PrefixExpression -> { translate(expr.expression) translatePrefixOperator(expr.operator) @@ -447,17 +551,17 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, val lv = expr.constValue(namespace, heap) ?: throw CompilerException("constant expression required, not $expr") when(lv.type) { DataType.BYTE -> stackvmProg.instr(Opcode.PUSH, Value(DataType.BYTE, lv.bytevalue!!)) - DataType.WORD -> stackvmProg.instr(Opcode.PUSH, Value(DataType.WORD, lv.wordvalue!!)) - DataType.FLOAT -> stackvmProg.instr(Opcode.PUSH, Value(DataType.FLOAT, lv.floatvalue!!)) + DataType.WORD -> stackvmProg.instr(Opcode.PUSH_W, Value(DataType.WORD, lv.wordvalue!!)) + DataType.FLOAT -> stackvmProg.instr(Opcode.PUSH_F, Value(DataType.FLOAT, lv.floatvalue!!)) DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { if(lv.heapId==null) throw CompilerException("string should have been moved into heap ${lv.position}") - stackvmProg.instr(Opcode.PUSH, Value(lv.type, lv.heapId)) + stackvmProg.instr(Opcode.PUSH_W, Value(lv.type, lv.heapId)) } DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> { if(lv.heapId==null) throw CompilerException("array/matrix should have been moved into heap ${lv.position}") - stackvmProg.instr(Opcode.PUSH, Value(lv.type, lv.heapId)) + stackvmProg.instr(Opcode.PUSH_W, Value(lv.type, lv.heapId)) } } } @@ -469,8 +573,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, when (target) { is VarDecl -> { when (target.type) { - VarDeclType.VAR -> - stackvmProg.instr(Opcode.PUSH_VAR, callLabel = target.scopedname) + VarDeclType.VAR -> { + val opcode = opcodePushvar(target.datatype) + stackvmProg.instr(opcode, callLabel = target.scopedname) + } VarDeclType.CONST -> throw CompilerException("const ref should have been const-folded away") VarDeclType.MEMORY -> { @@ -504,8 +610,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, is Subroutine -> { translateSubroutineCall(targetStmt, stmt.arglist) // make sure we clean up the unused result values from the stack. - for(rv in targetStmt.returnvalues) - stackvmProg.instr(Opcode.DISCARD) + for(rv in targetStmt.returnvalues) { + val opcode=opcodeDiscard(rv) + stackvmProg.instr(opcode) + } } else -> throw AstException("invalid call target node type: ${targetStmt::class}") @@ -545,7 +653,8 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, // evaluate the arguments and assign them into the subroutine's argument variables. for(arg in arguments.zip(subroutine.parameters)) { translate(arg.first) - stackvmProg.instr(Opcode.POP_VAR, callLabel = subroutine.scopedname+"."+arg.second.name) + val opcode=opcodePopvar(arg.second.type) + stackvmProg.instr(opcode, callLabel = subroutine.scopedname+"."+arg.second.name) } stackvmProg.instr(Opcode.CALL, callLabel=subroutine.scopedname) } @@ -612,12 +721,11 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } if(write) - stackvmProg.instr(Opcode.WRITE_INDEXED_VAR, callLabel = variableName) + stackvmProg.instr(opcodeWriteindexedvar(variable!!.datatype), callLabel = variableName) else - stackvmProg.instr(Opcode.READ_INDEXED_VAR, callLabel = variableName) + stackvmProg.instr(opcodeReadindexedvar(variable!!.datatype), callLabel = variableName) } - private fun createSyscall(funcname: String) { val function = ( if (funcname.startsWith("_vm_")) @@ -653,20 +761,22 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, stackvmProg.line(stmt.position) when { stmt.target.register!=null -> when(stmt.operator) { - "++" -> stackvmProg.instr(Opcode.INC_VAR, callLabel = stmt.target.register.toString()) - "--" -> stackvmProg.instr(Opcode.DEC_VAR, callLabel = stmt.target.register.toString()) + "++" -> stackvmProg.instr(opcodeIncvar(stmt.target.register!!), callLabel = stmt.target.register.toString()) + "--" -> stackvmProg.instr(opcodeDecvar(stmt.target.register!!), callLabel = stmt.target.register.toString()) } stmt.target.identifier!=null -> { val targetStatement = stmt.target.identifier!!.targetStatement(namespace) as VarDecl when(stmt.operator) { - "++" -> stackvmProg.instr(Opcode.INC_VAR, callLabel = targetStatement.scopedname) - "--" -> stackvmProg.instr(Opcode.DEC_VAR, callLabel = targetStatement.scopedname) + "++" -> stackvmProg.instr(opcodeIncvar(targetStatement.datatype), callLabel = targetStatement.scopedname) + "--" -> stackvmProg.instr(opcodeDecvar(targetStatement.datatype), callLabel = targetStatement.scopedname) } } stmt.target.arrayindexed!=null -> { // todo: generate more efficient bytecode for this? translate(stmt.target.arrayindexed!!, false) - stackvmProg.instr(Opcode.PUSH, Value(stmt.target.arrayindexed!!.resultingDatatype(namespace, heap)!!, 1)) + val one = Value(stmt.target.arrayindexed!!.resultingDatatype(namespace, heap)!!, 1) + val opcode = opcodePush(one.type) + stackvmProg.instr(opcode, one) when(stmt.operator) { "++" -> stackvmProg.instr(Opcode.ADD) "--" -> stackvmProg.instr(Opcode.SUB) @@ -711,11 +821,17 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, stmt.target.identifier!=null -> { val target = stmt.target.identifier!!.targetStatement(namespace)!! when(target) { - is VarDecl -> stackvmProg.instr(Opcode.PUSH_VAR, callLabel = target.scopedname) + is VarDecl -> { + val opcode = opcodePushvar(stmt.target.determineDatatype(namespace, heap, stmt)) + stackvmProg.instr(opcode, callLabel = target.scopedname) + } else -> throw CompilerException("invalid assignment target type ${target::class}") } } - stmt.target.register!=null -> stackvmProg.instr(Opcode.PUSH_VAR, callLabel = stmt.target.register.toString()) + stmt.target.register!=null -> { + val opcode= opcodePushvar(stmt.target.register!!) + stackvmProg.instr(opcode, callLabel = stmt.target.register.toString()) + } stmt.target.arrayindexed!=null -> translate(stmt.target.arrayindexed!!, false) } @@ -727,11 +843,17 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, stmt.target.identifier!=null -> { val target = stmt.target.identifier!!.targetStatement(namespace)!! when(target) { - is VarDecl -> stackvmProg.instr(Opcode.POP_VAR, callLabel = target.scopedname) + is VarDecl -> { + val opcode = opcodePopvar(stmt.target.determineDatatype(namespace, heap, stmt)) + stackvmProg.instr(opcode, callLabel = target.scopedname) + } else -> throw CompilerException("invalid assignment target type ${target::class}") } } - stmt.target.register!=null -> stackvmProg.instr(Opcode.POP_VAR, callLabel = stmt.target.register.toString()) + stmt.target.register!=null -> { + val opcode=opcodePopvar(stmt.target.register!!) + stackvmProg.instr(opcode, callLabel = stmt.target.register.toString()) + } stmt.target.arrayindexed!=null -> translate(stmt.target.arrayindexed!!, true) // write value to it } } @@ -835,13 +957,13 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, } } - private fun translateForOverIterableVar(loop: ForLoop, varDt: DataType, iterableValue: LiteralValue) { - if(varDt==DataType.BYTE && iterableValue.type !in setOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.ARRAY, DataType.MATRIX)) - throw CompilerException("loop variable type doesn't match iterableValue type (byte)") - else if(varDt==DataType.WORD && iterableValue.type != DataType.ARRAY_W) - throw CompilerException("loop variable type doesn't match iterableValue type (word)") - else if(varDt==DataType.FLOAT && iterableValue.type != DataType.ARRAY_F) - throw CompilerException("loop variable type doesn't match iterableValue type (float)") + private fun translateForOverIterableVar(loop: ForLoop, loopvarDt: DataType, iterableValue: LiteralValue) { + if(loopvarDt==DataType.BYTE && iterableValue.type !in setOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.ARRAY, DataType.MATRIX)) + throw CompilerException("loop variable type doesn't match iterableValue type") + else if(loopvarDt==DataType.WORD && iterableValue.type != DataType.ARRAY_W) + throw CompilerException("loop variable type doesn't match iterableValue type") + else if(loopvarDt==DataType.FLOAT && iterableValue.type != DataType.ARRAY_F) + throw CompilerException("loop variable type doesn't match iterableValue type") val numElements: Int val indexVar: String when(iterableValue.type) { @@ -893,8 +1015,9 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, continueStmtLabelStack.push(continueLabel) breakStmtLabelStack.push(breakLabel) - stackvmProg.instr(Opcode.PUSH, Value(if(numElements<=255) DataType.BYTE else DataType.WORD, 0)) - stackvmProg.instr(Opcode.POP_VAR, callLabel = indexVar) + val zero = Value(if(numElements<=255) DataType.BYTE else DataType.WORD, 0) + stackvmProg.instr(opcodePush(zero.type), zero) + stackvmProg.instr(opcodePopvar(zero.type), callLabel = indexVar) stackvmProg.label(loopLabel) val assignTarget = if(loop.loopRegister!=null) AssignTarget(loop.loopRegister, null, null, loop.position) @@ -906,11 +1029,11 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, translate(assignLv) translate(loop.body) stackvmProg.label(continueLabel) - stackvmProg.instr(Opcode.INC_VAR, callLabel = indexVar) + stackvmProg.instr(opcodeIncvar(zero.type), callLabel = indexVar) // TODO: optimize edge cases if last value = 255 or 0 (for bytes) etc. to avoid PUSH / SUB opcodes and make use of the wrapping around of the value. - stackvmProg.instr(Opcode.PUSH, Value(varDt, numElements)) - stackvmProg.instr(Opcode.PUSH_VAR, callLabel = indexVar) + stackvmProg.instr(opcodePush(zero.type), Value(zero.type, numElements)) + stackvmProg.instr(opcodePushvar(zero.type), callLabel = indexVar) stackvmProg.instr(Opcode.SUB) stackvmProg.instr(Opcode.BNZ, callLabel = loopLabel) @@ -948,31 +1071,31 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, continueStmtLabelStack.push(continueLabel) breakStmtLabelStack.push(breakLabel) - stackvmProg.instr(Opcode.PUSH, Value(varDt, range.first)) - stackvmProg.instr(Opcode.POP_VAR, callLabel = varname) + stackvmProg.instr(opcodePush(varDt), Value(varDt, range.first)) + stackvmProg.instr(opcodePopvar(varDt), callLabel = varname) stackvmProg.label(loopLabel) translate(body) stackvmProg.label(continueLabel) when { - range.step==1 -> stackvmProg.instr(Opcode.INC_VAR, callLabel = varname) - range.step==-1 -> stackvmProg.instr(Opcode.DEC_VAR, callLabel = varname) + range.step==1 -> stackvmProg.instr(opcodeIncvar(varDt), callLabel = varname) + range.step==-1 -> stackvmProg.instr(opcodeDecvar(varDt), callLabel = varname) range.step>1 -> { - stackvmProg.instr(Opcode.PUSH_VAR, callLabel = varname) - stackvmProg.instr(Opcode.PUSH, Value(varDt, range.step)) + stackvmProg.instr(opcodePushvar(varDt), callLabel = varname) + stackvmProg.instr(opcodePush(varDt), Value(varDt, range.step)) stackvmProg.instr(Opcode.ADD) - stackvmProg.instr(Opcode.POP_VAR, callLabel = varname) + stackvmProg.instr(opcodePopvar(varDt), callLabel = varname) } range.step<1 -> { - stackvmProg.instr(Opcode.PUSH_VAR, callLabel = varname) - stackvmProg.instr(Opcode.PUSH, Value(varDt, abs(range.step))) + stackvmProg.instr(opcodePushvar(varDt), callLabel = varname) + stackvmProg.instr(opcodePush(varDt), Value(varDt, abs(range.step))) stackvmProg.instr(Opcode.SUB) - stackvmProg.instr(Opcode.POP_VAR, callLabel = varname) + stackvmProg.instr(opcodePopvar(varDt), callLabel = varname) } } // TODO: optimize edge cases if last value = 255 or 0 (for bytes) etc. to avoid PUSH / SUB opcodes and make use of the wrapping around of the value. - stackvmProg.instr(Opcode.PUSH, Value(varDt, range.last+range.step)) - stackvmProg.instr(Opcode.PUSH_VAR, callLabel = varname) + stackvmProg.instr(opcodePush(varDt), Value(varDt, range.last+range.step)) + stackvmProg.instr(opcodePushvar(varDt), callLabel = varname) stackvmProg.instr(Opcode.SUB) stackvmProg.instr(Opcode.BNZ, callLabel = loopLabel) @@ -1083,9 +1206,11 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, postIncr.linkParents(range.parent) translate(postIncr) if(lvTarget.register!=null) - stackvmProg.instr(Opcode.PUSH_VAR, callLabel =lvTarget.register.toString()) - else - stackvmProg.instr(Opcode.PUSH_VAR, callLabel =targetStatement!!.scopedname) + stackvmProg.instr(opcodePushvar(lvTarget.register), callLabel =lvTarget.register.toString()) + else { + val opcode = opcodePushvar(targetStatement!!.datatype) + stackvmProg.instr(opcode, callLabel = targetStatement.scopedname) + } val branch = BranchStatement( BranchCondition.NZ, listOf(Jump(null, null, loopLabel, range.position)), diff --git a/compiler/src/prog8/stackvm/Program.kt b/compiler/src/prog8/stackvm/Program.kt index e49158881..fbc64e41d 100644 --- a/compiler/src/prog8/stackvm/Program.kt +++ b/compiler/src/prog8/stackvm/Program.kt @@ -104,10 +104,7 @@ class Program (val name: String, Instruction(opcode, callLabel = args) } } - Opcode.INC_VAR, Opcode.DEC_VAR, - Opcode.SHR_VAR, Opcode.SHL_VAR, Opcode.ROL_VAR, Opcode.ROR_VAR, - Opcode.ROL2_VAR, Opcode.ROR2_VAR, Opcode.POP_VAR, Opcode.PUSH_VAR, - Opcode.READ_INDEXED_VAR, Opcode.WRITE_INDEXED_VAR -> { + in opcodesWithVarArgument -> { val withoutQuotes = if(args!!.startsWith('"') && args.endsWith('"')) args.substring(1, args.length-1) else args diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 9cc1be409..390eb38d7 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -11,55 +11,66 @@ import kotlin.math.* enum class Opcode { // pushing values on the (evaluation) stack - PUSH, // push constant value (any type) + PUSH, // push byte value + PUSH_W, // push word value (or 'address' of string / array / matrix) + PUSH_F, // push float value PUSH_MEM, // push byte value from memory to stack PUSH_MEM_W, // push word value from memory to stack PUSH_MEM_F, // push float value from memory to stack - PUSH_VAR, // push a variable - DUP, // push topmost value once again + PUSH_VAR, // push byte variable + PUSH_VAR_W, // push word variable + PUSH_VAR_F, // push float variable // popping values off the (evaluation) stack, possibly storing them in another location - DISCARD, // discard top value - POP_MEM, // pop value into destination memory address - POP_VAR, // pop value into variable + DISCARD, // discard top byte value + DISCARD_W, // discard top word value + DISCARD_F, // discard top float value + POP_MEM, // pop byte value into destination memory address + POP_MEM_W, // pop word value into destination memory address + POP_MEM_F, // pop float value into destination memory address + POP_VAR, // pop byte value into variable + POP_VAR_W, // pop word value into variable + POP_VAR_F, // pop float value into variable - // numeric and bitwise arithmetic - ADD, - SUB, - MUL, - DIV, - FLOORDIV, - REMAINDER, - POW, - NEG, - SHL, + // numeric arithmetic + ADD, // todo b/w/f + SUB, // todo b/w/f + MUL, // todo b/w/f + DIV, // todo b/w/f + FLOORDIV, // todo b/w/f + REMAINDER, // todo b/w/f + POW, // todo b/w/f + NEG, // todo b/w/f + + // bit shifts and bitwise arithmetic + SHL, // todo b/w SHL_MEM, SHL_MEM_W, - SHL_VAR, - SHR, + SHL_VAR, // todo b/w + SHR, // todo b/w SHR_MEM, SHR_MEM_W, - SHR_VAR, - ROL, + SHR_VAR, // todo b/w + ROL, // todo b/w ROL_MEM, ROL_MEM_W, - ROL_VAR, - ROR, + ROL_VAR, // todo b/w + ROR, // todo b/w ROR_MEM, ROR_MEM_W, - ROR_VAR, - ROL2, + ROR_VAR, // todo b/w + ROL2, // todo b/w ROL2_MEM, ROL2_MEM_W, - ROL2_VAR, - ROR2, + ROL2_VAR, // todo b/w + ROR2, // todo b/w ROR2_MEM, ROR2_MEM_W, - ROR2_VAR, - BITAND, - BITOR, - BITXOR, - INV, + ROR2_VAR, // todo b/w + BITAND, // todo b/w + BITOR, // todo b/w + BITXOR, // todo b/w + INV, // todo b/w LSB, MSB, @@ -70,32 +81,40 @@ enum class Opcode { W2FLOAT, // convert word into floating point // logical operations - AND, - OR, - XOR, - NOT, + AND, // todo b/w/f ? + OR, // todo b/w/f ? + XOR, // todo b/w/f ? + NOT, // todo b/w/f ? // increment, decrement INC, - INC_MEM, - INC_MEM_W, + INC_W, + INC_F, INC_VAR, + INC_VAR_W, + INC_VAR_F, DEC, - DEC_MEM, - DEC_MEM_W, + DEC_W, + DEC_F, DEC_VAR, + DEC_VAR_W, + DEC_VAR_F, // comparisons - LESS, - GREATER, - LESSEQ, - GREATEREQ, - EQUAL, - NOTEQUAL, + LESS, // todo b/w/f ? + GREATER, // todo b/w/f ? + LESSEQ, // todo b/w/f ? + GREATEREQ, // todo b/w/f ? + EQUAL, // todo b/w/f ? + NOTEQUAL, // todo b/w/f ? // array access READ_INDEXED_VAR, + READ_INDEXED_VAR_W, + READ_INDEXED_VAR_F, WRITE_INDEXED_VAR, + WRITE_INDEXED_VAR_W, + WRITE_INDEXED_VAR_F, // branching JUMP, @@ -114,7 +133,6 @@ enum class Opcode { SYSCALL, // misc - SWAP, SEC, // set carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations SEI, // set irq-disable status flag @@ -125,6 +143,16 @@ enum class Opcode { LINE // track source file line number } +val opcodesWithVarArgument = setOf( + Opcode.INC_VAR, Opcode.INC_VAR_W, Opcode.DEC_VAR, Opcode.DEC_VAR_W, + Opcode.SHR_VAR, Opcode.SHL_VAR, Opcode.ROL_VAR, Opcode.ROR_VAR, + Opcode.ROL2_VAR, Opcode.ROR2_VAR, + Opcode.POP_VAR, Opcode.POP_VAR_W, Opcode.POP_VAR_F, + Opcode.PUSH_VAR, Opcode.PUSH_VAR_W, Opcode.PUSH_VAR_F, + Opcode.READ_INDEXED_VAR, Opcode.READ_INDEXED_VAR_W, Opcode.READ_INDEXED_VAR_F, + Opcode.WRITE_INDEXED_VAR, Opcode.WRITE_INDEXED_VAR_W, Opcode.WRITE_INDEXED_VAR_F + ) + enum class Syscall(val callNr: Short) { WRITE_MEMCHR(10), // print a single char from the memory address popped from stack WRITE_MEMSTR(11), // print a 0-terminated petscii string from the memory address popped from stack @@ -181,10 +209,6 @@ open class Instruction(val opcode: Opcode, override fun toString(): String { val argStr = arg?.toString() ?: "" - val opcodesWithVarArgument = setOf( - Opcode.INC_VAR, Opcode.DEC_VAR, - Opcode.SHR_VAR, Opcode.SHL_VAR, Opcode.ROL_VAR, Opcode.ROR_VAR, - Opcode.ROL2_VAR, Opcode.ROR2_VAR, Opcode.POP_VAR, Opcode.PUSH_VAR) val result = when { opcode==Opcode.LINE -> "_line $callLabel" @@ -341,11 +365,36 @@ class StackVm(private var traceOutputFile: String?) { } } + private fun checkDt(value: Value?, expected: DataType) { + if(value==null) + throw VmExecutionException("expected value") + if(value.type!=expected) + throw VmExecutionException("expected $expected value, found ${value.type}") + } + + private fun checkDt(value: Value?, expected: Set) { + if(value==null) + throw VmExecutionException("expected value") + if(value.type !in expected) + throw VmExecutionException("incompatible type found ${value.type}") + } + private fun dispatch(ins: Instruction) : Instruction { traceOutput?.println("\n$ins") when (ins.opcode) { Opcode.NOP -> {} - Opcode.PUSH -> evalstack.push(ins.arg) + Opcode.PUSH -> { + checkDt(ins.arg, DataType.BYTE) + evalstack.push(ins.arg) + } + Opcode.PUSH_W -> { + checkDt(ins.arg, setOf(DataType.WORD, DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.MATRIX)) + evalstack.push(ins.arg) + } + Opcode.PUSH_F -> { + checkDt(ins.arg, DataType.FLOAT) + evalstack.push(ins.arg) + } Opcode.PUSH_MEM -> { val address = ins.arg!!.integerValue() evalstack.push(Value(DataType.BYTE, mem.getByte(address))) @@ -358,305 +407,161 @@ class StackVm(private var traceOutputFile: String?) { val address = ins.arg!!.integerValue() evalstack.push(Value(DataType.FLOAT, mem.getFloat(address))) } - Opcode.DUP -> evalstack.push(evalstack.peek()) - Opcode.DISCARD -> evalstack.pop() - Opcode.SWAP -> { - val (top, second) = evalstack.pop2() - evalstack.push(top) - evalstack.push(second) + Opcode.DISCARD -> { + val value = evalstack.pop() + checkDt(value, DataType.BYTE) + } + Opcode.DISCARD_W -> { + val value = evalstack.pop() + checkDt(value, DataType.WORD) + } + Opcode.DISCARD_F -> { + val value = evalstack.pop() + checkDt(value, DataType.FLOAT) } Opcode.POP_MEM -> { val value = evalstack.pop() + checkDt(value, DataType.BYTE) val address = ins.arg!!.integerValue() - when (value.type) { - DataType.BYTE -> mem.setByte(address, value.integerValue().toShort()) - DataType.WORD -> mem.setWord(address, value.integerValue()) - DataType.FLOAT -> mem.setFloat(address, value.numericValue().toDouble()) - else -> throw VmExecutionException("can only manipulate byte/word/float on stack") - } + mem.setByte(address, value.integerValue().toShort()) + } + Opcode.POP_MEM_W -> { + val value = evalstack.pop() + checkDt(value, DataType.WORD) + val address = ins.arg!!.integerValue() + mem.setWord(address, value.integerValue()) + } + Opcode.POP_MEM_F -> { + val value = evalstack.pop() + checkDt(value, DataType.FLOAT) + val address = ins.arg!!.integerValue() + mem.setFloat(address, value.numericValue().toDouble()) } Opcode.ADD -> { val (top, second) = evalstack.pop2() + // todo b/w/f evalstack.push(second.add(top)) } Opcode.SUB -> { val (top, second) = evalstack.pop2() + // todo b/w/f evalstack.push(second.sub(top)) } Opcode.MUL -> { val (top, second) = evalstack.pop2() + // todo b/w/f evalstack.push(second.mul(top)) } Opcode.DIV -> { val (top, second) = evalstack.pop2() + // todo b/w/f evalstack.push(second.div(top)) } Opcode.FLOORDIV -> { val (top, second) = evalstack.pop2() + // todo b/w/f evalstack.push(second.floordiv(top)) } Opcode.REMAINDER -> { val (top, second) = evalstack.pop2() + // todo b/w/f evalstack.push(second.remainder(top)) } Opcode.POW -> { val (top, second) = evalstack.pop2() + // todo b/w/f evalstack.push(second.pow(top)) } Opcode.NEG -> { val v = evalstack.pop() + // todo b/w/f evalstack.push(v.neg()) } Opcode.SHL -> { val v = evalstack.pop() + // todo b/w evalstack.push(v.shl()) } Opcode.SHR -> { val v = evalstack.pop() + // todo b/w evalstack.push(v.shr()) } Opcode.ROL -> { val v = evalstack.pop() + // todo b/w val (result, newCarry) = v.rol(P_carry) this.P_carry = newCarry evalstack.push(result) } Opcode.ROL2 -> { val v = evalstack.pop() + // todo b/w evalstack.push(v.rol2()) } Opcode.ROR -> { val v = evalstack.pop() + // todo b/w val (result, newCarry) = v.ror(P_carry) this.P_carry = newCarry evalstack.push(result) } Opcode.ROR2 -> { val v = evalstack.pop() + // todo b/w evalstack.push(v.ror2()) } Opcode.BITAND -> { val (top, second) = evalstack.pop2() + // todo b/w evalstack.push(second.bitand(top)) } Opcode.BITOR -> { val (top, second) = evalstack.pop2() + // todo b/w evalstack.push(second.bitor(top)) } Opcode.BITXOR -> { val (top, second) = evalstack.pop2() + // todo b/w evalstack.push(second.bitxor(top)) } Opcode.INV -> { val v = evalstack.pop() + // todo b/w evalstack.push(v.inv()) } Opcode.INC -> { val v = evalstack.pop() + checkDt(v, DataType.BYTE) + evalstack.push(v.inc()) + } + Opcode.INC_W -> { + val v = evalstack.pop() + checkDt(v, DataType.WORD) + evalstack.push(v.inc()) + } + Opcode.INC_F -> { + val v = evalstack.pop() + checkDt(v, DataType.FLOAT) evalstack.push(v.inc()) } Opcode.DEC -> { val v = evalstack.pop() + checkDt(v, DataType.BYTE) evalstack.push(v.dec()) } - Opcode.SYSCALL -> { - val callId = ins.arg!!.integerValue().toShort() - val syscall = Syscall.values().first { it.callNr == callId } - when (syscall) { - Syscall.WRITE_MEMCHR -> { - val address = evalstack.pop().integerValue() - print(Petscii.decodePetscii(listOf(mem.getByte(address)), true)) - } - Syscall.WRITE_MEMSTR -> { - val address = evalstack.pop().integerValue() - print(mem.getString(address)) - } - Syscall.WRITE_NUM -> { - print(evalstack.pop().numericValue()) - } - Syscall.WRITE_CHAR -> { - print(Petscii.decodePetscii(listOf(evalstack.pop().integerValue().toShort()), true)) - } - Syscall.WRITE_STR -> { - val value = evalstack.pop() - when(value.type){ - DataType.BYTE, DataType.WORD, DataType.FLOAT -> print(value.numericValue()) - DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> print(heap.get(value.heapId).str) - DataType.ARRAY, DataType.ARRAY_W -> print(heap.get(value.heapId).array!!.toList()) - DataType.MATRIX -> print(heap.get(value.heapId).array!!.toList()) - DataType.ARRAY_F -> print(heap.get(value.heapId).doubleArray!!.toList()) - } - } - Syscall.INPUT_STR -> { - val variable = evalstack.pop() - val value = heap.get(variable.heapId) - val maxlen = value.str!!.length - val input = readLine() ?: "" - heap.update(variable.heapId, input.padEnd(maxlen, '\u0000').substring(0, maxlen)) - } - Syscall.GFX_PIXEL -> { - // plot pixel at (x, y, color) from stack - val color = evalstack.pop() - val (y, x) = evalstack.pop2() - canvas?.setPixel(x.integerValue(), y.integerValue(), color.integerValue()) - } - Syscall.GFX_LINE -> { - // draw line at (x1, y1, x2, y2, color) from stack - val color = evalstack.pop() - val (y2, x2) = evalstack.pop2() - val (y1, x1) = evalstack.pop2() - canvas?.drawLine(x1.integerValue(), y1.integerValue(), x2.integerValue(), y2.integerValue(), color.integerValue()) - } - Syscall.GFX_CLEARSCR -> { - val color = evalstack.pop() - canvas?.clearScreen(color.integerValue()) - } - Syscall.GFX_TEXT -> { - val textPtr = evalstack.pop() - val color = evalstack.pop() - val (cy, cx) = evalstack.pop2() - val text = heap.get(textPtr.heapId) - canvas?.writeText(8*cx.integerValue(), 8*cy.integerValue(), text.str!!, color.integerValue()) - } - Syscall.FUNC_RND -> evalstack.push(Value(DataType.BYTE, rnd.nextInt() and 255)) - Syscall.FUNC_RNDW -> evalstack.push(Value(DataType.WORD, rnd.nextInt() and 65535)) - Syscall.FUNC_RNDF -> evalstack.push(Value(DataType.FLOAT, rnd.nextDouble())) - Syscall.FUNC_LEN -> throw VmExecutionException("len() should have been const-folded away everywhere (it's not possible on non-const values)") - Syscall.FUNC_SIN -> evalstack.push(Value(DataType.FLOAT, sin(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_COS -> evalstack.push(Value(DataType.FLOAT, cos(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_ROUND -> evalstack.push(Value(DataType.WORD, evalstack.pop().numericValue().toDouble().roundToInt())) - Syscall.FUNC_ABS -> { - val value = evalstack.pop() - val absValue= - when(value.type) { - DataType.BYTE -> Value(DataType.BYTE, value.numericValue()) - DataType.WORD -> Value(DataType.WORD, value.numericValue()) - DataType.FLOAT -> Value(DataType.FLOAT, value.numericValue()) - else -> throw VmExecutionException("cannot get abs of $value") - } - evalstack.push(absValue) - } - Syscall.FUNC_ACOS -> evalstack.push(Value(DataType.FLOAT, acos(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_ASIN -> evalstack.push(Value(DataType.FLOAT, asin(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_TAN -> evalstack.push(Value(DataType.FLOAT, tan(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_ATAN -> evalstack.push(Value(DataType.FLOAT, atan(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_LN -> evalstack.push(Value(DataType.FLOAT, ln(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_LOG2 -> evalstack.push(Value(DataType.FLOAT, log2(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_LOG10 -> evalstack.push(Value(DataType.FLOAT, log10(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_SQRT -> evalstack.push(Value(DataType.FLOAT, sqrt(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_RAD -> evalstack.push(Value(DataType.FLOAT, Math.toRadians(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_DEG -> evalstack.push(Value(DataType.FLOAT, Math.toDegrees(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_FLOOR -> { - val value = evalstack.pop() - val result = - when(value.type) { - DataType.BYTE -> Value(DataType.BYTE, value.numericValue()) - DataType.WORD -> Value(DataType.WORD, value.numericValue()) - DataType.FLOAT -> Value(DataType.WORD, floor(value.numericValue().toDouble())) - else -> throw VmExecutionException("cannot get floor of $value") - } - evalstack.push(result) - } - Syscall.FUNC_CEIL -> { - val value = evalstack.pop() - val result = - when(value.type) { - DataType.BYTE -> Value(DataType.BYTE, value.numericValue()) - DataType.WORD -> Value(DataType.WORD, value.numericValue()) - DataType.FLOAT -> Value(DataType.WORD, ceil(value.numericValue().toDouble())) - else -> throw VmExecutionException("cannot get ceil of $value") - } - evalstack.push(result) - } - Syscall.FUNC_MAX -> { - val iterable = evalstack.pop() - val value = heap.get(iterable.heapId) - val resultDt = when(iterable.type) { - DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.BYTE - DataType.ARRAY, DataType.MATRIX -> DataType.BYTE - DataType.ARRAY_W -> DataType.WORD - DataType.ARRAY_F -> DataType.FLOAT - DataType.BYTE, DataType.WORD, DataType.FLOAT -> throw VmExecutionException("uniterable value $iterable") - } - if(value.str!=null) { - val result = Petscii.encodePetscii(value.str.max().toString(), true)[0] - evalstack.push(Value(DataType.BYTE, result)) - } else { - val result = value.array!!.max() ?: 0 - evalstack.push(Value(resultDt, result)) - } - } - Syscall.FUNC_MIN -> { - val iterable = evalstack.pop() - val value = heap.get(iterable.heapId) - val resultDt = when(iterable.type) { - DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.BYTE - DataType.ARRAY, DataType.MATRIX -> DataType.BYTE - DataType.ARRAY_W -> DataType.WORD - DataType.ARRAY_F -> DataType.FLOAT - DataType.BYTE, DataType.WORD, DataType.FLOAT -> throw VmExecutionException("uniterable value $iterable") - } - if(value.str!=null) { - val result = Petscii.encodePetscii(value.str.min().toString(), true)[0] - evalstack.push(Value(DataType.BYTE, result)) - } else { - val result = value.array!!.min() ?: 0 - evalstack.push(Value(resultDt, result)) - } - } - Syscall.FUNC_AVG -> { - val iterable = evalstack.pop() - val value = heap.get(iterable.heapId) - if(value.str!=null) - evalstack.push(Value(DataType.FLOAT, Petscii.encodePetscii(value.str, true).average())) - else - evalstack.push(Value(DataType.FLOAT, value.array!!.average())) - } - Syscall.FUNC_SUM -> { - val iterable = evalstack.pop() - val value = heap.get(iterable.heapId) - if(value.str!=null) - evalstack.push(Value(DataType.WORD, Petscii.encodePetscii(value.str, true).sum())) - else - evalstack.push(Value(DataType.WORD, value.array!!.sum())) - } - Syscall.FUNC_ANY -> { - val iterable = evalstack.pop() - val value = heap.get(iterable.heapId) - if (value.str != null) - evalstack.push(Value(DataType.BYTE, if (Petscii.encodePetscii(value.str, true).any { c -> c != 0.toShort() }) 1 else 0)) - else - evalstack.push(Value(DataType.BYTE, if (value.array!!.any{v->v!=0}) 1 else 0)) - } - Syscall.FUNC_ALL -> { - val iterable = evalstack.pop() - val value = heap.get(iterable.heapId) - if (value.str != null) - evalstack.push(Value(DataType.BYTE, if (Petscii.encodePetscii(value.str, true).all { c -> c != 0.toShort() }) 1 else 0)) - else - evalstack.push(Value(DataType.BYTE, if (value.array!!.all{v->v!=0}) 1 else 0)) - } - Syscall.FUNC_STR2BYTE -> { - val strvar = evalstack.pop() - val str = heap.get(strvar.heapId) - val y = str.str!!.trim().trimEnd('\u0000') - evalstack.push(Value(DataType.BYTE, y.toShort())) - } - Syscall.FUNC_STR2WORD -> { - val strvar = evalstack.pop() - val str = heap.get(strvar.heapId) - val y = str.str!!.trim().trimEnd('\u0000') - evalstack.push(Value(DataType.BYTE, y.toInt())) - } - Syscall.FUNC_STR2FLOAT -> { - val strvar = evalstack.pop() - val str = heap.get(strvar.heapId) - val y = str.str!!.trim().trimEnd('\u0000') - evalstack.push(Value(DataType.BYTE, y.toDouble())) - } - } + Opcode.DEC_W -> { + val v = evalstack.pop() + checkDt(v, DataType.WORD) + evalstack.push(v.dec()) } - + Opcode.DEC_F -> { + val v = evalstack.pop() + checkDt(v, DataType.FLOAT) + evalstack.push(v.dec()) + } + Opcode.SYSCALL -> dispatchSyscall(ins) Opcode.SEC -> P_carry = true Opcode.CLC -> P_carry = false Opcode.SEI -> P_irqd = true @@ -664,26 +569,6 @@ class StackVm(private var traceOutputFile: String?) { Opcode.TERMINATE -> throw VmTerminationException("terminate instruction") Opcode.BREAKPOINT -> throw VmBreakpointException() - Opcode.INC_MEM -> { - val addr = ins.arg!!.integerValue() - val newValue = Value(DataType.BYTE, mem.getByte(addr)).inc() - mem.setByte(addr, newValue.integerValue().toShort()) - } - Opcode.INC_MEM_W -> { - val addr = ins.arg!!.integerValue() - val newValue = Value(DataType.WORD, mem.getWord(addr)).inc() - mem.setWord(addr, newValue.integerValue()) - } - Opcode.DEC_MEM -> { - val addr = ins.arg!!.integerValue() - val newValue = Value(DataType.BYTE, mem.getByte(addr)).dec() - mem.setByte(addr, newValue.integerValue().toShort()) - } - Opcode.DEC_MEM_W -> { - val addr = ins.arg!!.integerValue() - val newValue = Value(DataType.WORD, mem.getWord(addr)).dec() - mem.setWord(addr, newValue.integerValue()) - } Opcode.SHL_MEM -> { val addr = ins.arg!!.integerValue() val value = Value(DataType.BYTE, mem.getByte(addr)) @@ -783,204 +668,297 @@ class StackVm(private var traceOutputFile: String?) { return callstack.pop() } Opcode.PUSH_VAR -> { - val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") - evalstack.push(variable) + val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + checkDt(value, DataType.BYTE) + evalstack.push(value) + } + Opcode.PUSH_VAR_W -> { + val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + checkDt(value, DataType.WORD) + evalstack.push(value) + } + Opcode.PUSH_VAR_F -> { + val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + checkDt(value, DataType.FLOAT) + evalstack.push(value) } Opcode.POP_VAR -> { val value = evalstack.pop() + checkDt(value, DataType.BYTE) val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") - if(variable.type!=value.type) - throw VmExecutionException("value datatype ${value.type} is not the same as variable datatype ${variable.type} for var ${ins.callLabel}") + checkDt(variable, DataType.BYTE) + variables[ins.callLabel!!] = value + } + Opcode.POP_VAR_W -> { + val value = evalstack.pop() + checkDt(value, DataType.WORD) + val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + checkDt(variable, DataType.WORD) + variables[ins.callLabel!!] = value + } + Opcode.POP_VAR_F -> { + val value = evalstack.pop() + checkDt(value, DataType.FLOAT) + val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + checkDt(variable, DataType.FLOAT) variables[ins.callLabel!!] = value } Opcode.SHL_VAR -> { val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + // todo b/w variables[ins.callLabel!!] = variable.shl() } Opcode.SHR_VAR -> { val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + // todo b/w variables[ins.callLabel!!] = variable.shr() } Opcode.ROL_VAR -> { val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + // todo b/w val (newValue, newCarry) = variable.rol(P_carry) variables[ins.callLabel!!] = newValue P_carry = newCarry } Opcode.ROR_VAR -> { val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + // todo b/w val (newValue, newCarry) = variable.ror(P_carry) variables[ins.callLabel!!] = newValue P_carry = newCarry } Opcode.ROL2_VAR -> { val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + // todo b/w variables[ins.callLabel!!] = variable.rol2() } Opcode.ROR2_VAR -> { val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + // todo b/w variables[ins.callLabel!!] = variable.ror2() } Opcode.INC_VAR -> { val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + checkDt(variable, DataType.BYTE) + variables[ins.callLabel!!] = variable.inc() + } + Opcode.INC_VAR_W -> { + val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + checkDt(variable, DataType.WORD) + variables[ins.callLabel!!] = variable.inc() + } + Opcode.INC_VAR_F -> { + val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + checkDt(variable, DataType.FLOAT) variables[ins.callLabel!!] = variable.inc() } Opcode.DEC_VAR -> { val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + checkDt(variable, DataType.BYTE) + variables[ins.callLabel!!] = variable.dec() + } + Opcode.DEC_VAR_W -> { + val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + checkDt(variable, DataType.WORD) + variables[ins.callLabel!!] = variable.dec() + } + Opcode.DEC_VAR_F -> { + val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + checkDt(variable, DataType.FLOAT) variables[ins.callLabel!!] = variable.dec() } Opcode.LSB -> { val v = evalstack.pop() + checkDt(v, DataType.WORD) evalstack.push(v.lsb()) } Opcode.MSB -> { val v = evalstack.pop() + checkDt(v, DataType.WORD) evalstack.push(v.msb()) } Opcode.AND -> { val (top, second) = evalstack.pop2() + // todo b/w evalstack.push(second.and(top)) } Opcode.OR -> { val (top, second) = evalstack.pop2() + // todo b/w evalstack.push(second.or(top)) } Opcode.XOR -> { val (top, second) = evalstack.pop2() + // todo b/w evalstack.push(second.xor(top)) } Opcode.NOT -> { val value = evalstack.pop() + // todo b/w evalstack.push(value.not()) } Opcode.LESS -> { val (top, second) = evalstack.pop2() + // todo b/w/f? evalstack.push(Value(DataType.BYTE, if(second < top) 1 else 0)) } Opcode.GREATER -> { val (top, second) = evalstack.pop2() + // todo b/w/f? evalstack.push(Value(DataType.BYTE, if(second > top) 1 else 0)) } Opcode.LESSEQ -> { val (top, second) = evalstack.pop2() + // todo b/w/f? evalstack.push(Value(DataType.BYTE, if(second <= top) 1 else 0)) } Opcode.GREATEREQ -> { val (top, second) = evalstack.pop2() + // todo b/w/f? evalstack.push(Value(DataType.BYTE, if(second >= top) 1 else 0)) } Opcode.EQUAL -> { val (top, second) = evalstack.pop2() + // todo b/w/f? evalstack.push(Value(DataType.BYTE, if(second == top) 1 else 0)) } Opcode.NOTEQUAL -> { val (top, second) = evalstack.pop2() + // todo b/w/f? evalstack.push(Value(DataType.BYTE, if(second != top) 1 else 0)) } Opcode.B2WORD -> { val byte = evalstack.pop() - if(byte.type==DataType.BYTE) { - evalstack.push(Value(DataType.WORD, byte.integerValue())) - } else { - throw VmExecutionException("attempt to make a word from a non-byte value $byte") - } + checkDt(byte, DataType.BYTE) + evalstack.push(Value(DataType.WORD, byte.integerValue())) } Opcode.MSB2WORD -> { val byte = evalstack.pop() - if(byte.type==DataType.BYTE) { - evalstack.push(Value(DataType.WORD, byte.integerValue() * 256)) - } else { - throw VmExecutionException("attempt to make a word from a non-byte value $byte") - } + checkDt(byte, DataType.BYTE) + evalstack.push(Value(DataType.WORD, byte.integerValue() * 256)) } Opcode.B2FLOAT -> { val byte = evalstack.pop() - if(byte.type==DataType.BYTE) { - evalstack.push(Value(DataType.FLOAT, byte.integerValue())) - } else { - throw VmExecutionException("attempt to make a float from a non-byte value $byte") - } + checkDt(byte, DataType.BYTE) + evalstack.push(Value(DataType.FLOAT, byte.integerValue())) } Opcode.W2FLOAT -> { val byte = evalstack.pop() - if(byte.type==DataType.WORD) { - evalstack.push(Value(DataType.FLOAT, byte.integerValue())) - } else { - throw VmExecutionException("attempt to make a float from a non-word value $byte") - } + checkDt(byte, DataType.WORD) + evalstack.push(Value(DataType.FLOAT, byte.integerValue())) } Opcode.LINE -> { sourceLine = ins.callLabel!! } Opcode.READ_INDEXED_VAR -> { - // put the value of variable[index] onto the stack + // put the byte value of variable[index] onto the stack val index = evalstack.pop().integerValue() val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") if(variable.type==DataType.WORD) { // assume the variable is a pointer (address) and get the byte value from that memory location evalstack.push(Value(DataType.BYTE, mem.getByte(variable.integerValue()))) } else { - // get indexed element from the array + // get indexed byte element from the array val array = heap.get(variable.heapId) when(array.type) { DataType.ARRAY, DataType.MATRIX -> evalstack.push(Value(DataType.BYTE, array.array!![index])) - DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, array.array!![index])) - DataType.ARRAY_F -> evalstack.push(Value(DataType.FLOAT, array.doubleArray!![index])) - DataType.STR, - DataType.STR_P, - DataType.STR_S, - DataType.STR_PS -> evalstack.push(Value(DataType.BYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0])) - DataType.BYTE, - DataType.WORD, - DataType.FLOAT -> throw VmExecutionException("not a proper array/matrix var") + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> evalstack.push(Value(DataType.BYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0])) + else -> throw VmExecutionException("not a proper array/matrix/string variable with byte elements") } } } + Opcode.READ_INDEXED_VAR_W -> { + // put the word value of variable[index] onto the stack + val index = evalstack.pop().integerValue() + val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + if(variable.type==DataType.WORD) { + // assume the variable is a pointer (address) and get the word value from that memory location + evalstack.push(Value(DataType.WORD, mem.getWord(variable.integerValue()))) + } else { + // get indexed word element from the array + val array = heap.get(variable.heapId) + if(array.type!=DataType.ARRAY_W) + throw VmExecutionException("not a proper array var with word elements") + evalstack.push(Value(DataType.WORD, array.array!![index])) + } + } + Opcode.READ_INDEXED_VAR_F -> { + // put the f;pat value of variable[index] onto the stack + val index = evalstack.pop().integerValue() + val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + if(variable.type==DataType.WORD) { + // assume the variable is a pointer (address) and get the float value from that memory location + evalstack.push(Value(DataType.WORD, mem.getFloat(variable.integerValue()))) + } else { + // get indexed float element from the array + val array = heap.get(variable.heapId) + if(array.type!=DataType.ARRAY_F) + throw VmExecutionException("not a proper array var with float elements") + evalstack.push(Value(DataType.FLOAT, array.doubleArray!![index])) + } + } Opcode.WRITE_INDEXED_VAR -> { - // store value on the stack in variable[index] (index is on the stack as well) + // store byte value on the stack in variable[index] (index is on the stack as well) val index = evalstack.pop().integerValue() val value = evalstack.pop() + checkDt(value, DataType.BYTE) val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") if(variable.type==DataType.WORD) { // assume the variable is a pointer (address) and write the byte value to that memory location - if(value.type!=DataType.BYTE) - throw VmExecutionException("writing a non-byte value to memory location") mem.setByte(variable.integerValue(), value.integerValue().toShort()) } else { - // set indexed element in the array + // set indexed byte element in the array val array = heap.get(variable.heapId) when(array.type) { DataType.ARRAY, DataType.MATRIX -> { - if(value.type!=DataType.BYTE) - throw VmExecutionException("writing a non-byte value into byte array/matrix") array.array!![index] = value.integerValue() } - DataType.ARRAY_W -> { - if(value.type!=DataType.WORD) - throw VmExecutionException("writing a non-word value into word array") - array.array!![index] = value.integerValue() - } - DataType.ARRAY_F -> { - if(value.type!=DataType.FLOAT) - throw VmExecutionException("writing a non-float value into float array") - array.doubleArray!![index] = value.numericValue().toDouble() - } DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { - if(value.type!=DataType.BYTE) - throw VmExecutionException("writing a non-byte value into a string") val chars = array.str!!.toCharArray() chars[index] = Petscii.decodePetscii(listOf(value.integerValue().toShort()), true)[0] heap.update(variable.heapId, chars.joinToString("")) } - DataType.BYTE, - DataType.WORD, - DataType.FLOAT -> throw VmExecutionException("not a proper array/matrix var") + else -> throw VmExecutionException("not a proper array/matrix/string var with byte elements") } } } + Opcode.WRITE_INDEXED_VAR_W -> { + // store word value on the stack in variable[index] (index is on the stack as well) + val index = evalstack.pop().integerValue() + val value = evalstack.pop() + checkDt(value, DataType.WORD) + val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + if(variable.type==DataType.WORD) { + // assume the variable is a pointer (address) and write the word value to that memory location + mem.setWord(variable.integerValue(), value.integerValue()) + } else { + // set indexed word element in the array + val array = heap.get(variable.heapId) + if(array.type!=DataType.ARRAY_W) + throw VmExecutionException("not a proper array var with word elements") + array.array!![index] = value.integerValue() + } + } + Opcode.WRITE_INDEXED_VAR_F -> { + // store float value on the stack in variable[index] (index is on the stack as well) + val index = evalstack.pop().integerValue() + val value = evalstack.pop() + checkDt(value, DataType.FLOAT) + val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + if(variable.type==DataType.WORD) { + // assume the variable is a pointer (address) and write the float value to that memory location + mem.setFloat(variable.integerValue(), value.numericValue().toDouble()) + } else { + // set indexed float element in the array + val array = heap.get(variable.heapId) + if(array.type!=DataType.ARRAY_F) + throw VmExecutionException("not a proper array var with float elements") + array.doubleArray!![index] = value.numericValue().toDouble() + } + } else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}") } @@ -992,6 +970,204 @@ class StackVm(private var traceOutputFile: String?) { return ins.next } + private fun dispatchSyscall(ins: Instruction) { + val callId = ins.arg!!.integerValue().toShort() + val syscall = Syscall.values().first { it.callNr == callId } + when (syscall) { + Syscall.WRITE_MEMCHR -> { + val address = evalstack.pop().integerValue() + print(Petscii.decodePetscii(listOf(mem.getByte(address)), true)) + } + Syscall.WRITE_MEMSTR -> { + val address = evalstack.pop().integerValue() + print(mem.getString(address)) + } + Syscall.WRITE_NUM -> { + print(evalstack.pop().numericValue()) + } + Syscall.WRITE_CHAR -> { + print(Petscii.decodePetscii(listOf(evalstack.pop().integerValue().toShort()), true)) + } + Syscall.WRITE_STR -> { + val value = evalstack.pop() + when(value.type){ + DataType.BYTE, DataType.WORD, DataType.FLOAT -> print(value.numericValue()) + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> print(heap.get(value.heapId).str) + DataType.ARRAY, DataType.ARRAY_W -> print(heap.get(value.heapId).array!!.toList()) + DataType.MATRIX -> print(heap.get(value.heapId).array!!.toList()) + DataType.ARRAY_F -> print(heap.get(value.heapId).doubleArray!!.toList()) + } + } + Syscall.INPUT_STR -> { + val variable = evalstack.pop() + val value = heap.get(variable.heapId) + val maxlen = value.str!!.length + val input = readLine() ?: "" + heap.update(variable.heapId, input.padEnd(maxlen, '\u0000').substring(0, maxlen)) + } + Syscall.GFX_PIXEL -> { + // plot pixel at (x, y, color) from stack + val color = evalstack.pop() + val (y, x) = evalstack.pop2() + canvas?.setPixel(x.integerValue(), y.integerValue(), color.integerValue()) + } + Syscall.GFX_LINE -> { + // draw line at (x1, y1, x2, y2, color) from stack + val color = evalstack.pop() + val (y2, x2) = evalstack.pop2() + val (y1, x1) = evalstack.pop2() + canvas?.drawLine(x1.integerValue(), y1.integerValue(), x2.integerValue(), y2.integerValue(), color.integerValue()) + } + Syscall.GFX_CLEARSCR -> { + val color = evalstack.pop() + canvas?.clearScreen(color.integerValue()) + } + Syscall.GFX_TEXT -> { + val textPtr = evalstack.pop() + val color = evalstack.pop() + val (cy, cx) = evalstack.pop2() + val text = heap.get(textPtr.heapId) + canvas?.writeText(8*cx.integerValue(), 8*cy.integerValue(), text.str!!, color.integerValue()) + } + Syscall.FUNC_RND -> evalstack.push(Value(DataType.BYTE, rnd.nextInt() and 255)) + Syscall.FUNC_RNDW -> evalstack.push(Value(DataType.WORD, rnd.nextInt() and 65535)) + Syscall.FUNC_RNDF -> evalstack.push(Value(DataType.FLOAT, rnd.nextDouble())) + Syscall.FUNC_LEN -> throw VmExecutionException("len() should have been const-folded away everywhere (it's not possible on non-const values)") + Syscall.FUNC_SIN -> evalstack.push(Value(DataType.FLOAT, sin(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_COS -> evalstack.push(Value(DataType.FLOAT, cos(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_ROUND -> evalstack.push(Value(DataType.WORD, evalstack.pop().numericValue().toDouble().roundToInt())) + Syscall.FUNC_ABS -> { + val value = evalstack.pop() + val absValue= + when(value.type) { + DataType.BYTE -> Value(DataType.BYTE, value.numericValue()) + DataType.WORD -> Value(DataType.WORD, value.numericValue()) + DataType.FLOAT -> Value(DataType.FLOAT, value.numericValue()) + else -> throw VmExecutionException("cannot get abs of $value") + } + evalstack.push(absValue) + } + Syscall.FUNC_ACOS -> evalstack.push(Value(DataType.FLOAT, acos(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_ASIN -> evalstack.push(Value(DataType.FLOAT, asin(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_TAN -> evalstack.push(Value(DataType.FLOAT, tan(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_ATAN -> evalstack.push(Value(DataType.FLOAT, atan(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_LN -> evalstack.push(Value(DataType.FLOAT, ln(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_LOG2 -> evalstack.push(Value(DataType.FLOAT, log2(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_LOG10 -> evalstack.push(Value(DataType.FLOAT, log10(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_SQRT -> evalstack.push(Value(DataType.FLOAT, sqrt(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_RAD -> evalstack.push(Value(DataType.FLOAT, Math.toRadians(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_DEG -> evalstack.push(Value(DataType.FLOAT, Math.toDegrees(evalstack.pop().numericValue().toDouble()))) + Syscall.FUNC_FLOOR -> { + val value = evalstack.pop() + val result = + when(value.type) { + DataType.BYTE -> Value(DataType.BYTE, value.numericValue()) + DataType.WORD -> Value(DataType.WORD, value.numericValue()) + DataType.FLOAT -> Value(DataType.WORD, floor(value.numericValue().toDouble())) + else -> throw VmExecutionException("cannot get floor of $value") + } + evalstack.push(result) + } + Syscall.FUNC_CEIL -> { + val value = evalstack.pop() + val result = + when(value.type) { + DataType.BYTE -> Value(DataType.BYTE, value.numericValue()) + DataType.WORD -> Value(DataType.WORD, value.numericValue()) + DataType.FLOAT -> Value(DataType.WORD, ceil(value.numericValue().toDouble())) + else -> throw VmExecutionException("cannot get ceil of $value") + } + evalstack.push(result) + } + Syscall.FUNC_MAX -> { + val iterable = evalstack.pop() + val value = heap.get(iterable.heapId) + val resultDt = when(iterable.type) { + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.BYTE + DataType.ARRAY, DataType.MATRIX -> DataType.BYTE + DataType.ARRAY_W -> DataType.WORD + DataType.ARRAY_F -> DataType.FLOAT + DataType.BYTE, DataType.WORD, DataType.FLOAT -> throw VmExecutionException("uniterable value $iterable") + } + if(value.str!=null) { + val result = Petscii.encodePetscii(value.str.max().toString(), true)[0] + evalstack.push(Value(DataType.BYTE, result)) + } else { + val result = value.array!!.max() ?: 0 + evalstack.push(Value(resultDt, result)) + } + } + Syscall.FUNC_MIN -> { + val iterable = evalstack.pop() + val value = heap.get(iterable.heapId) + val resultDt = when(iterable.type) { + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.BYTE + DataType.ARRAY, DataType.MATRIX -> DataType.BYTE + DataType.ARRAY_W -> DataType.WORD + DataType.ARRAY_F -> DataType.FLOAT + DataType.BYTE, DataType.WORD, DataType.FLOAT -> throw VmExecutionException("uniterable value $iterable") + } + if(value.str!=null) { + val result = Petscii.encodePetscii(value.str.min().toString(), true)[0] + evalstack.push(Value(DataType.BYTE, result)) + } else { + val result = value.array!!.min() ?: 0 + evalstack.push(Value(resultDt, result)) + } + } + Syscall.FUNC_AVG -> { + val iterable = evalstack.pop() + val value = heap.get(iterable.heapId) + if(value.str!=null) + evalstack.push(Value(DataType.FLOAT, Petscii.encodePetscii(value.str, true).average())) + else + evalstack.push(Value(DataType.FLOAT, value.array!!.average())) + } + Syscall.FUNC_SUM -> { + val iterable = evalstack.pop() + val value = heap.get(iterable.heapId) + if(value.str!=null) + evalstack.push(Value(DataType.WORD, Petscii.encodePetscii(value.str, true).sum())) + else + evalstack.push(Value(DataType.WORD, value.array!!.sum())) + } + Syscall.FUNC_ANY -> { + val iterable = evalstack.pop() + val value = heap.get(iterable.heapId) + if (value.str != null) + evalstack.push(Value(DataType.BYTE, if (Petscii.encodePetscii(value.str, true).any { c -> c != 0.toShort() }) 1 else 0)) + else + evalstack.push(Value(DataType.BYTE, if (value.array!!.any{v->v!=0}) 1 else 0)) + } + Syscall.FUNC_ALL -> { + val iterable = evalstack.pop() + val value = heap.get(iterable.heapId) + if (value.str != null) + evalstack.push(Value(DataType.BYTE, if (Petscii.encodePetscii(value.str, true).all { c -> c != 0.toShort() }) 1 else 0)) + else + evalstack.push(Value(DataType.BYTE, if (value.array!!.all{v->v!=0}) 1 else 0)) + } + Syscall.FUNC_STR2BYTE -> { + val strvar = evalstack.pop() + val str = heap.get(strvar.heapId) + val y = str.str!!.trim().trimEnd('\u0000') + evalstack.push(Value(DataType.BYTE, y.toShort())) + } + Syscall.FUNC_STR2WORD -> { + val strvar = evalstack.pop() + val str = heap.get(strvar.heapId) + val y = str.str!!.trim().trimEnd('\u0000') + evalstack.push(Value(DataType.BYTE, y.toInt())) + } + Syscall.FUNC_STR2FLOAT -> { + val strvar = evalstack.pop() + val str = heap.get(strvar.heapId) + val y = str.str!!.trim().trimEnd('\u0000') + evalstack.push(Value(DataType.BYTE, y.toDouble())) + } + } + } + fun irq(timestamp: Long) { // 60hz IRQ handling if(P_irqd) diff --git a/compiler/test/StackVMOpcodeTests.kt b/compiler/test/StackVMOpcodeTests.kt index 3ff4148e0..40f79a9a2 100644 --- a/compiler/test/StackVMOpcodeTests.kt +++ b/compiler/test/StackVMOpcodeTests.kt @@ -113,9 +113,18 @@ class TestStackVmOpcodes { assertFalse(vm.P_irqd) } + @Test + fun testPushWrongDt() { + val ins = mutableListOf(Instruction(Opcode.PUSH, Value(DataType.WORD, 4299))) + vm.load(makeProg(ins), null) + assertFailsWith { + vm.step(1) + } + } + @Test fun testPush() { - val ins = mutableListOf(Instruction(Opcode.PUSH, Value(DataType.FLOAT, 42.999))) + val ins = mutableListOf(Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, 42.999))) vm.load(makeProg(ins), null) assertThat(vm.evalstack, empty()) vm.step(1) @@ -151,7 +160,7 @@ class TestStackVmOpcodes { @Test fun testPushVar() { - val ins = mutableListOf(Instruction(Opcode.PUSH_VAR, callLabel = "varname")) + val ins = mutableListOf(Instruction(Opcode.PUSH_VAR_F, callLabel = "varname")) vm.load(makeProg(ins, mapOf("varname" to Value(DataType.FLOAT, 42.999))), null) assertEquals(7, vm.variables.size) assertTrue(vm.variables.containsKey("varname")) @@ -165,40 +174,12 @@ class TestStackVmOpcodes { assertEquals(Value(DataType.FLOAT, 42.999), vm.variables["varname"]) } - @Test - fun testDup() { - val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.FLOAT, 42.999)), - Instruction(Opcode.DUP)) - vm.load(makeProg(ins), null) - assertThat(vm.evalstack, empty()) - vm.step(2) - assertEquals(2, vm.evalstack.size) - assertEquals(Value(DataType.FLOAT, 42.999), vm.evalstack.pop()) - assertEquals(Value(DataType.FLOAT, 42.999), vm.evalstack.pop()) - } - - @Test - fun testSwap() { - val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.BYTE, 123)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 9999)), - Instruction(Opcode.SWAP) - ) - vm.load(makeProg(ins), null) - assertThat(vm.evalstack, empty()) - vm.step(3) - assertEquals(2, vm.evalstack.size) - assertEquals(Value(DataType.BYTE, 123), vm.evalstack.pop()) - assertEquals(Value(DataType.WORD, 9999), vm.evalstack.pop()) - } - @Test fun testDiscard() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.FLOAT, 42.999)), - Instruction(Opcode.PUSH, Value(DataType.FLOAT, 3.1415)), - Instruction(Opcode.DISCARD)) + Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, 42.999)), + Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, 3.1415)), + Instruction(Opcode.DISCARD_F)) vm.load(makeProg(ins), null) assertThat(vm.evalstack, empty()) vm.step(2) @@ -211,12 +192,12 @@ class TestStackVmOpcodes { @Test fun testPopMem() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.FLOAT, 42.25)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 0x42ea)), + Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, 42.25)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0x42ea)), Instruction(Opcode.PUSH, Value(DataType.BYTE, 123)), Instruction(Opcode.POP_MEM, Value(DataType.WORD, 0x2000)), - Instruction(Opcode.POP_MEM, Value(DataType.WORD, 0x3000)), - Instruction(Opcode.POP_MEM, Value(DataType.WORD, 0x4000))) + Instruction(Opcode.POP_MEM_W, Value(DataType.WORD, 0x3000)), + Instruction(Opcode.POP_MEM_F, Value(DataType.WORD, 0x4000))) vm.load(makeProg(ins), null) assertEquals(0, vm.mem.getWord(0x2000)) assertEquals(0, vm.mem.getWord(0x3000)) @@ -233,12 +214,12 @@ class TestStackVmOpcodes { @Test fun testPopVar() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.FLOAT, 42.25)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 0x42ea)), + Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, 42.25)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0x42ea)), Instruction(Opcode.PUSH, Value(DataType.BYTE, 123)), Instruction(Opcode.POP_VAR, callLabel = "var1"), - Instruction(Opcode.POP_VAR, callLabel = "var2"), - Instruction(Opcode.POP_VAR, callLabel = "var3")) + Instruction(Opcode.POP_VAR_W, callLabel = "var2"), + Instruction(Opcode.POP_VAR_F, callLabel = "var3")) val vars = mapOf( "var1" to Value(DataType.BYTE, 0), "var2" to Value(DataType.WORD, 0), @@ -252,8 +233,8 @@ class TestStackVmOpcodes { assertEquals(Value(DataType.FLOAT, 42.25), vm.variables["var3"]) val ins2 = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.WORD, 0x42ea)), - Instruction(Opcode.POP_VAR, callLabel = "var1")) + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0x42ea)), + Instruction(Opcode.POP_VAR_W, callLabel = "var1")) val vars2 = mapOf( "var1" to Value(DataType.BYTE, 0) ) @@ -505,65 +486,89 @@ class TestStackVmOpcodes { Value(DataType.BYTE, 20)) val expected = listOf( Value(DataType.BYTE, 0), - Value(DataType.BYTE, 1), Value(DataType.BYTE, 0), Value(DataType.BYTE, 1), Value(DataType.BYTE, 0), Value(DataType.BYTE, 1), Value(DataType.BYTE, 0), + Value(DataType.BYTE, 1), Value(DataType.BYTE, 0) ) val operator = Opcode.NOT - testUnaryOperator(values, operator, expected) + testUnaryOperator(values, operator, expected, listOf(DataType.BYTE, DataType.BYTE,DataType.BYTE,DataType.BYTE,DataType.BYTE,DataType.BYTE,DataType.BYTE,DataType.BYTE)) } @Test fun testInc() { val values = listOf( - Value(DataType.FLOAT, 2022.5), - Value(DataType.WORD, 65535), - Value(DataType.WORD, 999), Value(DataType.BYTE, 255), Value(DataType.BYTE, 99) ) val expected = listOf( - Value(DataType.BYTE, 100), Value(DataType.BYTE, 0), - Value(DataType.WORD, 1000), + Value(DataType.BYTE, 100) + ) + testUnaryOperator(values, Opcode.INC, expected) + + val valuesw = listOf( + Value(DataType.WORD, 65535), + Value(DataType.WORD, 999) + ) + val expectedw = listOf( Value(DataType.WORD, 0), + Value(DataType.WORD, 1000) + ) + testUnaryOperator(valuesw, Opcode.INC_W, expectedw) + + val valuesf = listOf( + Value(DataType.FLOAT, -1.0), + Value(DataType.FLOAT, 2022.5) + ) + val expectedf = listOf( + Value(DataType.FLOAT, 0.0), Value(DataType.FLOAT, 2023.5) ) - val operator = Opcode.INC - testUnaryOperator(values, operator, expected) + testUnaryOperator(valuesf, Opcode.INC_F, expectedf) } @Test fun testDec() { val values = listOf( - Value(DataType.FLOAT, 0.5), - Value(DataType.FLOAT, 123.456), - Value(DataType.WORD, 1000), - Value(DataType.WORD, 0), Value(DataType.BYTE, 100), Value(DataType.BYTE, 0) ) val expected = listOf( - Value(DataType.BYTE, 255), Value(DataType.BYTE, 99), - Value(DataType.WORD, 65535), - Value(DataType.WORD, 999), - Value(DataType.FLOAT, 122.456), - Value(DataType.FLOAT, -0.5) + Value(DataType.BYTE, 255) ) - val operator = Opcode.DEC - testUnaryOperator(values, operator, expected) + testUnaryOperator(values, Opcode.DEC, expected) + + val valuesw = listOf( + Value(DataType.WORD, 1000), + Value(DataType.WORD, 0) + ) + val expectedw = listOf( + Value(DataType.WORD, 999), + Value(DataType.WORD, 65535) + ) + testUnaryOperator(valuesw, Opcode.DEC_W, expectedw) + + val valuesf = listOf( + Value(DataType.FLOAT, 0.5), + Value(DataType.FLOAT, 123.456) + ) + val expectedf = listOf( + Value(DataType.FLOAT, -0.5), + Value(DataType.FLOAT, 122.456) + ) + testUnaryOperator(valuesf, Opcode.DEC_F, expectedf) } @Test fun testNeg() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.FLOAT, 123.456)), + Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, 123.456)), Instruction(Opcode.NEG), Instruction(Opcode.NEG) ) @@ -577,7 +582,7 @@ class TestStackVmOpcodes { assertEquals(Value(DataType.FLOAT, 123.456), vm.evalstack.peek()) val ins2 = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.WORD, 1234)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 1234)), Instruction(Opcode.NEG) ) vm.load(makeProg(ins2), null) @@ -597,7 +602,7 @@ class TestStackVmOpcodes { fun testInv() { val ins = mutableListOf( Instruction(Opcode.PUSH, Value(DataType.BYTE, 123)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 4044)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 4044)), Instruction(Opcode.INV), Instruction(Opcode.INV), Instruction(Opcode.INV) @@ -612,7 +617,7 @@ class TestStackVmOpcodes { assertEquals(Value(DataType.BYTE, 0x84), vm.evalstack.pop()) val ins2 = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.FLOAT, 1234.33)), + Instruction(Opcode.PUSH_W, Value(DataType.FLOAT, 1234.33)), // todo should crash Instruction(Opcode.INV) ) vm.load(makeProg(ins2), null) @@ -624,9 +629,9 @@ class TestStackVmOpcodes { @Test fun testLsb() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.FLOAT, 1.23)), + Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, 1.23)), Instruction(Opcode.PUSH, Value(DataType.BYTE, 0x45)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 0xea31)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0xea31)), Instruction(Opcode.LSB), Instruction(Opcode.LSB), Instruction(Opcode.LSB) @@ -644,9 +649,9 @@ class TestStackVmOpcodes { @Test fun testMsb() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.FLOAT, 1.23)), + Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, 1.23)), Instruction(Opcode.PUSH, Value(DataType.BYTE, 0x45)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 0xea31)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0xea31)), Instruction(Opcode.MSB), Instruction(Opcode.MSB), Instruction(Opcode.MSB) @@ -664,7 +669,7 @@ class TestStackVmOpcodes { @Test fun testB2Word() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.WORD, 0xea31)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0xea31)), Instruction(Opcode.PUSH, Value(DataType.BYTE, 0x45)), Instruction(Opcode.B2WORD), Instruction(Opcode.B2WORD) @@ -680,7 +685,7 @@ class TestStackVmOpcodes { @Test fun testMSB2Word() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.WORD, 0xea31)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0xea31)), Instruction(Opcode.PUSH, Value(DataType.BYTE, 0x45)), Instruction(Opcode.MSB2WORD), Instruction(Opcode.MSB2WORD) @@ -696,7 +701,7 @@ class TestStackVmOpcodes { @Test fun testB2Float() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.WORD, 0xea31)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0xea31)), Instruction(Opcode.PUSH, Value(DataType.BYTE, 123)), Instruction(Opcode.B2FLOAT), Instruction(Opcode.B2FLOAT) @@ -713,7 +718,7 @@ class TestStackVmOpcodes { fun testW2Float() { val ins = mutableListOf( Instruction(Opcode.PUSH, Value(DataType.BYTE, 11)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 12345)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 12345)), Instruction(Opcode.W2FLOAT), Instruction(Opcode.W2FLOAT) ) @@ -725,76 +730,54 @@ class TestStackVmOpcodes { } } - @Test - fun testIncMemAndIncMemW() { - val ins = mutableListOf( - Instruction(Opcode.INC_MEM, Value(DataType.WORD, 0x2000)), - Instruction(Opcode.INC_MEM, Value(DataType.WORD, 0x2001)), - Instruction(Opcode.INC_MEM_W, Value(DataType.WORD, 0x3000)), - Instruction(Opcode.INC_MEM_W, Value(DataType.WORD, 0x3002)) - ) - val mem=mapOf(0x2000 to listOf(Value(DataType.BYTE, 100), Value(DataType.BYTE, 255)), - 0x3000 to listOf(Value(DataType.WORD, 0x42ea), Value(DataType.WORD, 0xffff))) - vm.load(makeProg(ins, mem=mem), null) - vm.step(4) - assertEquals(101, vm.mem.getByte(0x2000)) - assertEquals(0, vm.mem.getByte(0x2001)) - assertEquals(0x42eb, vm.mem.getWord(0x3000)) - assertEquals(0, vm.mem.getWord(0x3002)) - } - @Test fun testIncVar() { val ins = mutableListOf( - Instruction(Opcode.INC_VAR, callLabel ="var1"), + Instruction(Opcode.INC_VAR_W, callLabel ="var1"), Instruction(Opcode.INC_VAR, callLabel ="var2"), - Instruction(Opcode.INC_VAR, callLabel ="var1"), - Instruction(Opcode.INC_VAR, callLabel ="var2")) + Instruction(Opcode.INC_VAR_F, callLabel ="var3"), + Instruction(Opcode.INC_VAR_W, callLabel ="var1"), + Instruction(Opcode.INC_VAR, callLabel ="var2"), + Instruction(Opcode.INC_VAR_F, callLabel ="var3") + ) val vars = mapOf("var1" to Value(DataType.WORD, 65534), - "var2" to Value(DataType.BYTE, 254)) + "var2" to Value(DataType.BYTE, 254), + "var3" to Value(DataType.FLOAT, -1.5) + ) vm.load(makeProg(ins, vars = vars), null) - vm.step(2) + vm.step(3) assertEquals(Value(DataType.WORD, 65535), vm.variables["var1"]) assertEquals(Value(DataType.BYTE, 255), vm.variables["var2"]) - vm.step(2) + assertEquals(Value(DataType.FLOAT, -0.5), vm.variables["var3"]) + vm.step(3) assertEquals(Value(DataType.WORD, 0), vm.variables["var1"]) assertEquals(Value(DataType.BYTE, 0), vm.variables["var2"]) + assertEquals(Value(DataType.FLOAT, 0.5), vm.variables["var3"]) } @Test fun testDecVar() { val ins = mutableListOf( - Instruction(Opcode.DEC_VAR, callLabel = "var1"), + Instruction(Opcode.DEC_VAR_W, callLabel = "var1"), Instruction(Opcode.DEC_VAR, callLabel = "var2"), - Instruction(Opcode.DEC_VAR, callLabel = "var1"), - Instruction(Opcode.DEC_VAR, callLabel = "var2")) + Instruction(Opcode.DEC_VAR_F, callLabel = "var3"), + Instruction(Opcode.DEC_VAR_W, callLabel = "var1"), + Instruction(Opcode.DEC_VAR, callLabel = "var2"), + Instruction(Opcode.DEC_VAR_F, callLabel = "var3") + ) val vars = mapOf("var1" to Value(DataType.WORD,1), - "var2" to Value(DataType.BYTE, 1)) + "var2" to Value(DataType.BYTE, 1), + "var3" to Value(DataType.FLOAT, 1.5) + ) vm.load(makeProg(ins, vars = vars), null) - vm.step(2) + vm.step(3) assertEquals(Value(DataType.WORD, 0), vm.variables["var1"]) assertEquals(Value(DataType.BYTE, 0), vm.variables["var2"]) - vm.step(2) + assertEquals(Value(DataType.FLOAT, 0.5), vm.variables["var3"]) + vm.step(3) assertEquals(Value(DataType.WORD, 65535), vm.variables["var1"]) assertEquals(Value(DataType.BYTE, 255), vm.variables["var2"]) - } - - @Test - fun testDecMemAndDecMemW() { - val ins = mutableListOf( - Instruction(Opcode.DEC_MEM, Value(DataType.WORD, 0x2000)), - Instruction(Opcode.DEC_MEM, Value(DataType.WORD, 0x2001)), - Instruction(Opcode.DEC_MEM_W, Value(DataType.WORD, 0x3000)), - Instruction(Opcode.DEC_MEM_W, Value(DataType.WORD, 0x3002)) - ) - val mem=mapOf(0x2000 to listOf(Value(DataType.BYTE, 100), Value(DataType.BYTE, 0)), - 0x3000 to listOf(Value(DataType.WORD, 0x42ea), Value(DataType.WORD, 0))) - vm.load(makeProg(ins, mem=mem), null) - vm.step(4) - assertEquals(99, vm.mem.getByte(0x2000)) - assertEquals(255, vm.mem.getByte(0x2001)) - assertEquals(0x42e9, vm.mem.getWord(0x3000)) - assertEquals(65535, vm.mem.getWord(0x3002)) + assertEquals(Value(DataType.FLOAT, -0.5), vm.variables["var3"]) } @Test @@ -805,7 +788,7 @@ class TestStackVmOpcodes { Instruction(Opcode.SYSCALL, Value(DataType.BYTE, Syscall.FUNC_RND.callNr)), Instruction(Opcode.SYSCALL, Value(DataType.BYTE, Syscall.FUNC_RND.callNr)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 25544)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 25544)), Instruction(Opcode.SYSCALL, Value(DataType.BYTE, Syscall.FUNC_SIN.callNr)) ) vm.load(makeProg(ins), null) @@ -1050,9 +1033,9 @@ class TestStackVmOpcodes { @Test fun testBZ() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.WORD, 1)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 1)), Instruction(Opcode.BZ, callLabel = "label"), - Instruction(Opcode.PUSH, Value(DataType.WORD, 0)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0)), Instruction(Opcode.BZ, callLabel = "label"), Instruction(Opcode.LINE, callLabel = "string1"), Instruction(Opcode.TERMINATE), @@ -1070,9 +1053,9 @@ class TestStackVmOpcodes { @Test fun testBNZ() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.WORD, 0)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0)), Instruction(Opcode.BNZ, callLabel = "label"), - Instruction(Opcode.PUSH, Value(DataType.WORD, 1)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 1)), Instruction(Opcode.BNZ, callLabel = "label"), Instruction(Opcode.LINE, callLabel = "string1"), Instruction(Opcode.TERMINATE), @@ -1090,11 +1073,11 @@ class TestStackVmOpcodes { @Test fun testBNEG() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.WORD, 0)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0)), Instruction(Opcode.BNEG, callLabel = "label"), - Instruction(Opcode.PUSH, Value(DataType.WORD, 1)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 1)), Instruction(Opcode.BNEG, callLabel = "label"), - Instruction(Opcode.PUSH, Value(DataType.FLOAT, -99)), + Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, -99)), Instruction(Opcode.BNEG, callLabel = "label"), Instruction(Opcode.LINE, callLabel = "string1"), Instruction(Opcode.TERMINATE), @@ -1114,9 +1097,9 @@ class TestStackVmOpcodes { @Test fun testBPOS() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.FLOAT, -99)), + Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, -99)), Instruction(Opcode.BPOS, callLabel = "label"), - Instruction(Opcode.PUSH, Value(DataType.WORD, 0)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0)), Instruction(Opcode.BPOS, callLabel = "label"), Instruction(Opcode.LINE, callLabel = "string1"), Instruction(Opcode.TERMINATE), @@ -1195,9 +1178,9 @@ class TestStackVmOpcodes { @Test fun testSHR() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.FLOAT, 9.99)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 3)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 61005)), + Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, 9.99)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 3)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 61005)), Instruction(Opcode.PUSH, Value(DataType.BYTE, 3)), Instruction(Opcode.PUSH, Value(DataType.BYTE, 249)), Instruction(Opcode.SHR), // 124 @@ -1207,12 +1190,12 @@ class TestStackVmOpcodes { Instruction(Opcode.SHR), // 0 Instruction(Opcode.DISCARD), Instruction(Opcode.SHR), // 30502 - Instruction(Opcode.DISCARD), + Instruction(Opcode.DISCARD_W), Instruction(Opcode.SHR), // 1 Instruction(Opcode.SHR), // 0 Instruction(Opcode.SHR), // 0 - Instruction(Opcode.DISCARD), - Instruction(Opcode.SHR) // error + Instruction(Opcode.DISCARD_W), + Instruction(Opcode.SHR) // error on float ) vm.load(makeProg(ins), null) vm.step(6) @@ -1237,9 +1220,9 @@ class TestStackVmOpcodes { @Test fun testSHL() { val ins = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.FLOAT, 9.99)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 3)), - Instruction(Opcode.PUSH, Value(DataType.WORD, 61005)), + Instruction(Opcode.PUSH_F, Value(DataType.FLOAT, 9.99)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 3)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 61005)), Instruction(Opcode.PUSH, Value(DataType.BYTE, 3)), Instruction(Opcode.PUSH, Value(DataType.BYTE, 249)), Instruction(Opcode.SHL), // 242 @@ -1247,10 +1230,10 @@ class TestStackVmOpcodes { Instruction(Opcode.SHL), // 6 Instruction(Opcode.DISCARD), Instruction(Opcode.SHL), // 56474 - Instruction(Opcode.DISCARD), + Instruction(Opcode.DISCARD_W), Instruction(Opcode.SHL), // 6 - Instruction(Opcode.DISCARD), - Instruction(Opcode.SHL) // error + Instruction(Opcode.DISCARD_W), + Instruction(Opcode.SHL) // error on float ) vm.load(makeProg(ins), null) vm.step(6) @@ -1302,7 +1285,7 @@ class TestStackVmOpcodes { val ins2 = mutableListOf( Instruction(Opcode.CLC), - Instruction(Opcode.PUSH, Value(DataType.WORD, 0b1001001100001101)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0b1001001100001101)), Instruction(Opcode.ROR), // 0b0100100110000110 c=1 Instruction(Opcode.ROR), // 0b1010010011000011 c=0 Instruction(Opcode.ROR), @@ -1367,7 +1350,7 @@ class TestStackVmOpcodes { val ins2 = mutableListOf( Instruction(Opcode.CLC), - Instruction(Opcode.PUSH, Value(DataType.WORD, 0b1001001100001101)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0b1001001100001101)), Instruction(Opcode.ROL), // 0b0010011000011010 c=1 Instruction(Opcode.ROL), // 0b0100110000110101 c=0 Instruction(Opcode.ROL), @@ -1424,7 +1407,7 @@ class TestStackVmOpcodes { assertEquals(Value(DataType.BYTE, 0b10010011), vm.evalstack.peek()) val ins2 = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.WORD, 0b1001001100001101)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0b1001001100001101)), Instruction(Opcode.ROR2), // 0b1100100110000110 Instruction(Opcode.ROR2), // 0b0110010011000011 Instruction(Opcode.ROR2), @@ -1476,7 +1459,7 @@ class TestStackVmOpcodes { assertEquals(Value(DataType.BYTE, 0b10010011), vm.evalstack.peek()) val ins2 = mutableListOf( - Instruction(Opcode.PUSH, Value(DataType.WORD, 0b1001001100001101)), + Instruction(Opcode.PUSH_W, Value(DataType.WORD, 0b1001001100001101)), Instruction(Opcode.ROL2), // 0b0010011000011011 Instruction(Opcode.ROL2), // 0b0100110000110110 Instruction(Opcode.ROL2), @@ -1505,13 +1488,36 @@ class TestStackVmOpcodes { assertEquals(Value(DataType.WORD, 0b1001001100001101), vm.evalstack.peek()) } + + private fun discardOpcode(dt: DataType): Opcode { + return when(dt) { + DataType.BYTE -> Opcode.DISCARD + DataType.WORD -> Opcode.DISCARD_W + DataType.FLOAT -> Opcode.DISCARD_F + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, + DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> Opcode.DISCARD_W + } + } + + private fun pushOpcode(dt: DataType): Opcode { + return when (dt) { + DataType.BYTE -> Opcode.PUSH + DataType.WORD -> Opcode.PUSH_W + DataType.FLOAT -> Opcode.PUSH_F + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, + DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> Opcode.PUSH_W + } + } + private fun testComparisonOperator(values: List, expected: List, operator: Opcode) { assertEquals(values.size, expected.size*2) val ins = mutableListOf() val vars = values.iterator() while(vars.hasNext()) { - ins.add(Instruction(Opcode.PUSH, vars.next())) - ins.add(Instruction(Opcode.PUSH, vars.next())) + var nextvar = vars.next() + ins.add(Instruction(pushOpcode(nextvar.type), nextvar)) + nextvar = vars.next() + ins.add(Instruction(pushOpcode(nextvar.type), nextvar)) ins.add(Instruction(operator)) } vm.load(makeProg(ins), null) @@ -1524,8 +1530,9 @@ class TestStackVmOpcodes { private fun testBinaryOperator(valuesToPush: List, operator: Opcode, expected: List) { assertEquals(valuesToPush.size, expected.size+1) val ins = mutableListOf() - for (value in valuesToPush) - ins.add(Instruction(Opcode.PUSH, value)) + for (value in valuesToPush) { + ins.add(Instruction(pushOpcode(value.type), value)) + } for (i in 1 until valuesToPush.size) ins.add(Instruction(operator)) vm.load(makeProg(ins), null) @@ -1540,19 +1547,23 @@ class TestStackVmOpcodes { } } - private fun testUnaryOperator(valuesToPush: List, operator: Opcode, expected: List) { + private fun testUnaryOperator(valuesToPush: List, operator: Opcode, expected: List, expectedTypes: List?=null) { assertEquals(valuesToPush.size, expected.size) + val typesExpected = expectedTypes?.reversed() ?: expected.reversed().map{it.type} + val ins = mutableListOf() - for (value in valuesToPush) - ins.add(Instruction(Opcode.PUSH, value)) - for (i in 1..valuesToPush.size) { + for (value in valuesToPush) { + ins.add(Instruction(pushOpcode(value.type), value)) + } + for (type in typesExpected) { ins.add(Instruction(operator)) - ins.add(Instruction(Opcode.DISCARD)) + ins.add(Instruction(discardOpcode(type))) } vm.load(makeProg(ins), null) vm.step(valuesToPush.size) assertEquals(valuesToPush.size, vm.evalstack.size) - for (expectedVal in expected) { + + for (expectedVal in expected.reversed()) { vm.step(1) assertEquals(expectedVal, vm.evalstack.peek()) vm.step(1)