From 1fe5c943fd82e351f8fd7a37cbe0fd3e8481fb71 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 11 Oct 2018 09:54:26 +0200 Subject: [PATCH] adding singed integer datatypes --- compiler/examples/test.p8 | 30 +- compiler/src/prog8/ast/AST.kt | 64 ++++ compiler/src/prog8/ast/StmtReorderer.kt | 8 +- compiler/src/prog8/compiler/Compiler.kt | 200 +++++----- .../src/prog8/functions/BuiltinFunctions.kt | 56 +-- .../src/prog8/optimizing/ConstantFolding.kt | 51 ++- compiler/src/prog8/stackvm/Memory.kt | 45 ++- compiler/src/prog8/stackvm/Program.kt | 10 +- compiler/src/prog8/stackvm/StackVm.kt | 353 ++++++++++++------ compiler/src/prog8/stackvm/Value.kt | 77 ++-- compiler/test/StackVMOpcodeTests.kt | 168 +++++++-- 11 files changed, 717 insertions(+), 345 deletions(-) diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index ab29585bb..124260855 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -1,15 +1,39 @@ -%zeropage full -%zpreserved $40,200 -%zpreserved 100,250 +%option enable_floats ~ main { + const uword width=320 + const uword height=200 + sub start() { + byte[3] barr1 = [-2,-1,2] + byte[3] barr2 = [-22,-1, 3] + ubyte[3] barr3 = [-2,-1,2] ; @ todo error + ubyte[3] barr4 = [1,2,33] + ubyte[3] barr5 = [1,2,-33] ; @todo error + word[3] warr1 = [-2,-1,2] + ;word[3] warr2 = [-2,-1,2, 3453] ; @todo ok + uword[3] warr3 = [-2,-1,2] + uword[3] warr4 = [1,2,33.w] + uword[3] warr5 = [1,2,-33] ; @todo error + + byte b1 = 50 * 2 + byte b2 = -50 * 2 + ubyte ub1 = 50 * 2 + word w1 = 999 * 2 + word w2 = -999 * 2 + uword uw1 = 999 * 2 + float f1 = 999*2 + float f2 = -999*2 return } + sub toscreenx(x: float, z: float) -> word { + return floor(x/(4.2+z) * flt(height)) + width // 2 + } + } diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index fe94a16f5..fc3f64c77 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -1062,6 +1062,70 @@ class LiteralValue(val type: DataType, throw ExpressionError("cannot compare type $type with ${other.type}", other.position) } + + fun intoDatatype(targettype: DataType): LiteralValue { + if(type==targettype) + return this + when(type) { + DataType.UBYTE -> { + if(targettype==DataType.BYTE && bytevalue!! <= 127) + return LiteralValue(targettype, bytevalue=bytevalue, position=position) + if(targettype==DataType.WORD || targettype==DataType.UWORD) + return LiteralValue(targettype, wordvalue=bytevalue!!.toInt(), position=position) + if(targettype==DataType.FLOAT) + return LiteralValue(targettype, floatvalue=bytevalue!!.toDouble(), position=position) + } + DataType.BYTE -> { + if(targettype==DataType.UBYTE && bytevalue!! >= 0) + return LiteralValue(targettype, bytevalue=bytevalue, position=position) + if(targettype==DataType.UWORD && bytevalue!! >= 0) + return LiteralValue(targettype, wordvalue=bytevalue.toInt(), position=position) + if(targettype==DataType.WORD) + return LiteralValue(targettype, wordvalue=bytevalue!!.toInt(), position=position) + if(targettype==DataType.FLOAT) + return LiteralValue(targettype, floatvalue=bytevalue!!.toDouble(), position=position) + } + DataType.UWORD -> { + if(targettype==DataType.BYTE && wordvalue!! <= 127) + return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position) + if(targettype==DataType.UBYTE && wordvalue!! <= 255) + return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position) + if(targettype==DataType.WORD && wordvalue!! <= 32767) + return LiteralValue(targettype, wordvalue=wordvalue, position=position) + if(targettype==DataType.FLOAT) + return LiteralValue(targettype, floatvalue=wordvalue!!.toDouble(), position=position) + } + DataType.WORD -> { + if(targettype==DataType.BYTE && wordvalue!! in -128..127) + return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position) + if(targettype==DataType.UBYTE && wordvalue!! in 0..255) + return LiteralValue(targettype, bytevalue=wordvalue.toShort(), position=position) + if(targettype==DataType.UWORD && wordvalue!! >=0) + return LiteralValue(targettype, wordvalue=wordvalue, position=position) + if(targettype==DataType.FLOAT) + return LiteralValue(targettype, floatvalue=wordvalue!!.toDouble(), position=position) + } + DataType.FLOAT -> { + if(floor(floatvalue!!)==floatvalue) { + val value = floatvalue.toInt() + if (targettype == DataType.BYTE && value in -128..127) + return LiteralValue(targettype, bytevalue = value.toShort(), position = position) + if (targettype == DataType.UBYTE && value in 0..255) + return LiteralValue(targettype, bytevalue = value.toShort(), position = position) + if (targettype == DataType.WORD && value in -32768..32767) + return LiteralValue(targettype, wordvalue = value, position = position) + if (targettype == DataType.UWORD && value in 0..65535) + return LiteralValue(targettype, wordvalue = value, position = position) + } + } + DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { + if(targettype in StringDatatypes) + return this + } + else -> {} + } + throw FatalAstException("invalid type conversion from $this to $targettype") + } } diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index aec20e9b6..f03f1824f 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -105,10 +105,16 @@ class StatementReorderer(private val namespace: INameScope, private val heap: He if(scope !in vardeclsToAdd) vardeclsToAdd[scope] = mutableListOf() vardeclsToAdd[scope]!!.add(decl.asDefaultValueDecl()) + val declvalue = decl.value!! + val value = + if(declvalue is LiteralValue) + declvalue.intoDatatype(decl.datatype) + else + declvalue return VariableInitializationAssignment( AssignTarget(null, IdentifierReference(decl.scopedname.split("."), decl.position), null, decl.position), null, - decl.value!!, + value, decl.position ) } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index ad20da8b9..5cab73ff5 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -200,102 +200,126 @@ class StackVmProgram(val name: String, val heap: HeapValues) { private fun optimizeDataConversionAndUselessDiscards() { // - push value followed by a data type conversion -> push the value in the correct type and remove the conversion // - push something followed by a discard -> remove both + val instructionsToReplace = mutableMapOf() + + fun optimizeDiscardAfterPush(index0: Int, index1: Int, ins1: Instruction) { + if (ins1.opcode == Opcode.DISCARD_FLOAT || ins1.opcode == Opcode.DISCARD_WORD || ins1.opcode == Opcode.DISCARD_BYTE) { + instructionsToReplace[index0] = Instruction(Opcode.NOP) + instructionsToReplace[index1] = Instruction(Opcode.NOP) + } + } + + fun optimizeFloatConversion(index0: Int, index1: Int, ins1: Instruction) { + when (ins1.opcode) { + Opcode.LSB, + Opcode.MSB, + Opcode.B2WORD, + Opcode.UB2UWORD, + Opcode.MSB2WORD, + Opcode.B2FLOAT, + Opcode.UB2FLOAT, + Opcode.UW2FLOAT, + Opcode.W2FLOAT -> throw CompilerException("invalid conversion following a float") + Opcode.DISCARD_FLOAT -> { + instructionsToReplace[index0] = Instruction(Opcode.NOP) + instructionsToReplace[index1] = Instruction(Opcode.NOP) + } + Opcode.DISCARD_BYTE, Opcode.DISCARD_WORD -> throw CompilerException("invalid discard type following a float") + else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}") + } + } + + fun optimizeWordConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) { + when (ins1.opcode) { + Opcode.LSB -> { + val ins = Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, ins0.arg!!.integerValue() and 255)) + instructionsToReplace[index0] = ins + instructionsToReplace[index1] = Instruction(Opcode.NOP) + } + Opcode.MSB -> { + val ins = Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, ins0.arg!!.integerValue() ushr 8 and 255)) + instructionsToReplace[index0] = ins + instructionsToReplace[index1] = Instruction(Opcode.NOP) + } + Opcode.B2WORD, + Opcode.UB2UWORD, + Opcode.MSB2WORD, + Opcode.B2FLOAT, + Opcode.UB2FLOAT -> throw CompilerException("invalid conversion following a word") + Opcode.W2FLOAT, Opcode.UW2FLOAT -> { + val ins = Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, ins0.arg!!.integerValue().toDouble())) + instructionsToReplace[index0] = ins + instructionsToReplace[index1] = Instruction(Opcode.NOP) + } + Opcode.DISCARD_WORD -> { + instructionsToReplace[index0] = Instruction(Opcode.NOP) + instructionsToReplace[index1] = Instruction(Opcode.NOP) + } + Opcode.DISCARD_BYTE, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte") + else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}") + } + } + + fun optimizeByteConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) { + when (ins1.opcode) { + Opcode.LSB -> instructionsToReplace[index1] = Instruction(Opcode.NOP) + Opcode.MSB -> throw CompilerException("msb of a byte") + Opcode.UB2UWORD -> { + val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, ins0.arg!!.integerValue())) + instructionsToReplace[index0] = ins + instructionsToReplace[index1] = Instruction(Opcode.NOP) + } + Opcode.B2WORD -> { + val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, ins0.arg!!.integerValue())) + instructionsToReplace[index0] = ins + instructionsToReplace[index1] = Instruction(Opcode.NOP) + } + Opcode.MSB2WORD -> { + val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 256 * ins0.arg!!.integerValue())) + instructionsToReplace[index0] = ins + instructionsToReplace[index1] = Instruction(Opcode.NOP) + } + Opcode.B2FLOAT, Opcode.UB2FLOAT -> { + val ins = Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, ins0.arg!!.integerValue().toDouble())) + instructionsToReplace[index0] = ins + instructionsToReplace[index1] = Instruction(Opcode.NOP) + } + Opcode.W2FLOAT, Opcode.UW2FLOAT -> throw CompilerException("invalid conversion following a byte") + Opcode.DISCARD_BYTE -> { + instructionsToReplace[index0] = Instruction(Opcode.NOP) + instructionsToReplace[index1] = Instruction(Opcode.NOP) + } + Opcode.DISCARD_WORD, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte") + else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}") + } + } val typeConversionOpcodes = setOf( Opcode.LSB, Opcode.MSB, Opcode.B2WORD, + Opcode.UB2UWORD, Opcode.MSB2WORD, Opcode.B2FLOAT, + Opcode.UB2FLOAT, Opcode.W2FLOAT, + Opcode.UW2FLOAT, Opcode.DISCARD_BYTE, Opcode.DISCARD_WORD, Opcode.DISCARD_FLOAT ) - - val instructionsToReplace = mutableMapOf() - this.instructions.asSequence().withIndex().windowed(2).toList().forEach { if(it[1].value.opcode in typeConversionOpcodes) { when(it[0].value.opcode) { - Opcode.PUSH_BYTE -> when (it[1].value.opcode) { - Opcode.LSB -> instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - Opcode.MSB -> throw CompilerException("msb of a byte") - Opcode.B2WORD -> { - val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, it[0].value.arg!!.integerValue())) - instructionsToReplace[it[0].index] = ins - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - Opcode.MSB2WORD -> { - val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 256 * it[0].value.arg!!.integerValue())) - instructionsToReplace[it[0].index] = ins - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - Opcode.B2FLOAT -> { - val ins = Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, it[0].value.arg!!.integerValue().toDouble())) - instructionsToReplace[it[0].index] = ins - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - Opcode.W2FLOAT -> throw CompilerException("invalid conversion following a byte") - Opcode.DISCARD_BYTE -> { - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - Opcode.DISCARD_WORD, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte") - else -> {} - } - Opcode.PUSH_WORD -> when (it[1].value.opcode) { - Opcode.LSB -> { - val ins = Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, it[0].value.arg!!.integerValue() and 255)) - instructionsToReplace[it[0].index] = ins - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - Opcode.MSB -> { - val ins = Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, it[0].value.arg!!.integerValue() ushr 8 and 255)) - instructionsToReplace[it[0].index] = ins - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - Opcode.B2WORD, - Opcode.MSB2WORD, - Opcode.B2FLOAT -> throw CompilerException("invalid conversion following a word") - Opcode.W2FLOAT -> { - val ins = Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, it[0].value.arg!!.integerValue().toDouble())) - instructionsToReplace[it[0].index] = ins - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - Opcode.DISCARD_WORD -> { - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - Opcode.DISCARD_BYTE, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte") - else -> {} - } - Opcode.PUSH_FLOAT -> when (it[1].value.opcode) { - Opcode.LSB, - Opcode.MSB, - Opcode.B2WORD, - Opcode.MSB2WORD, - Opcode.B2FLOAT, - Opcode.W2FLOAT -> throw CompilerException("invalid conversion following a float") - Opcode.DISCARD_FLOAT -> { - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - Opcode.DISCARD_BYTE, Opcode.DISCARD_WORD -> throw CompilerException("invalid discard type following a float") - else -> {} - } + Opcode.PUSH_BYTE -> optimizeByteConversion(it[0].index, it[0].value, it[1].index, it[1].value) + Opcode.PUSH_WORD -> optimizeWordConversion(it[0].index, it[0].value, it[1].index, it[1].value) + Opcode.PUSH_FLOAT -> optimizeFloatConversion(it[0].index, it[1].index, it[1].value) Opcode.PUSH_VAR_FLOAT, Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_BYTE, - Opcode.PUSH_MEM_BYTE, - Opcode.PUSH_MEM_WORD, - Opcode.PUSH_MEM_FLOAT -> when (it[1].value.opcode) { - Opcode.DISCARD_FLOAT, Opcode.DISCARD_WORD, Opcode.DISCARD_BYTE -> { - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - else -> {} - } + Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB, + Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW, + Opcode.PUSH_MEM_FLOAT -> optimizeDiscardAfterPush(it[0].index, it[1].index, it[1].value) else -> {} } } @@ -817,9 +841,9 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, else -> { val lv = expr.constValue(namespace, heap) ?: throw CompilerException("constant expression required, not $expr") when(lv.type) { - DataType.UBYTE, DataType.BYTE -> stackvmProg.instr(Opcode.PUSH_BYTE, Value(DataType.UBYTE, lv.bytevalue!!)) - DataType.UWORD, DataType.WORD -> stackvmProg.instr(Opcode.PUSH_WORD, Value(DataType.UWORD, lv.wordvalue!!)) - DataType.FLOAT -> stackvmProg.instr(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, lv.floatvalue!!)) + DataType.UBYTE, DataType.BYTE -> stackvmProg.instr(Opcode.PUSH_BYTE, Value(lv.type, lv.bytevalue!!)) + DataType.UWORD, DataType.WORD -> stackvmProg.instr(Opcode.PUSH_WORD, Value(lv.type, lv.wordvalue!!)) + DataType.FLOAT -> stackvmProg.instr(Opcode.PUSH_FLOAT, Value(lv.type, 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}") @@ -847,7 +871,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, when(givenDt) { DataType.UBYTE -> when(targetDt) { DataType.UWORD, DataType.WORD -> stackvmProg.instr(Opcode.UB2UWORD) - DataType.FLOAT -> stackvmProg.instr(Opcode.B2FLOAT) + DataType.FLOAT -> stackvmProg.instr(Opcode.UB2FLOAT) else -> {} } DataType.BYTE -> when(targetDt) { @@ -883,8 +907,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, throw CompilerException("const ref should have been const-folded away") VarDeclType.MEMORY -> { when (target.datatype) { - DataType.UBYTE -> stackvmProg.instr(Opcode.PUSH_MEM_BYTE, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!)) - DataType.UWORD -> stackvmProg.instr(Opcode.PUSH_MEM_WORD, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!)) + DataType.UBYTE -> stackvmProg.instr(Opcode.PUSH_MEM_UB, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!)) + DataType.BYTE-> stackvmProg.instr(Opcode.PUSH_MEM_B, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!)) + DataType.UWORD -> stackvmProg.instr(Opcode.PUSH_MEM_UW, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!)) + DataType.WORD -> stackvmProg.instr(Opcode.PUSH_MEM_W, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!)) DataType.FLOAT -> stackvmProg.instr(Opcode.PUSH_MEM_FLOAT, Value(DataType.UWORD, (target.value as LiteralValue).asNumericValue!!)) else -> TODO("invalid datatype for memory variable expression: $target") } @@ -929,8 +955,10 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, // 1 argument, type determines the exact opcode to use val arg = args.single() when (arg.resultingDatatype(namespace, heap)) { - DataType.UBYTE -> stackvmProg.instr(Opcode.B2FLOAT) - DataType.UWORD -> stackvmProg.instr(Opcode.W2FLOAT) + DataType.UBYTE -> stackvmProg.instr(Opcode.UB2FLOAT) + DataType.BYTE -> stackvmProg.instr(Opcode.B2FLOAT) + DataType.UWORD -> stackvmProg.instr(Opcode.UW2FLOAT) + DataType.WORD -> stackvmProg.instr(Opcode.W2FLOAT) DataType.FLOAT -> stackvmProg.instr(Opcode.NOP) else -> throw CompilerException("wrong datatype for flt()") } diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index aa9caadeb..b46d7e0cb 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -35,13 +35,13 @@ val BuiltinFunctions = mapOf( "deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toDegrees) }, "avg" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), DataType.FLOAT, ::builtinAvg), "abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.FLOAT, ::builtinAbs), - "round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), null) { a, p, n, h -> oneDoubleArgOutputInt(a, p, n, h, Math::round) }, // type depends on arg - "floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), null) { a, p, n, h -> oneDoubleArgOutputInt(a, p, n, h, Math::floor) }, // type depends on arg - "ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), null) { a, p, n, h -> oneDoubleArgOutputInt(a, p, n, h, Math::ceil) }, // type depends on arg + "round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.WORD) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::round) }, + "floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.WORD) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::floor) }, + "ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.WORD) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::ceil) }, "max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.max()!! }}, // type depends on args "min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.min()!! }}, // type depends on args "sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.sum() }}, // type depends on args - "len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes.toList())), null, ::builtinLen), // type depends on args + "len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes.toList())), DataType.UWORD, ::builtinLen), "any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.any { v -> v != 0.0} }}, "all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes.toList())), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.all { v -> v != 0.0} }}, "lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { x: Int -> x and 255 }}, @@ -142,17 +142,6 @@ fun builtinFunctionReturnType(function: String, args: List, namespa DataType.MATRIX_B -> DataType.BYTE } } - "round", "floor", "ceil" -> { - val dt=args.single().resultingDatatype(namespace, heap) - when(dt) { - DataType.UBYTE -> DataType.UBYTE - DataType.BYTE -> DataType.BYTE - DataType.UWORD -> DataType.UWORD - DataType.WORD -> DataType.WORD - DataType.FLOAT -> DataType.WORD - else -> null - } - } "sum" -> { val dt=datatypeFromListArg(args.single()) when(dt) { @@ -167,27 +156,6 @@ fun builtinFunctionReturnType(function: String, args: List, namespa DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.UWORD } } - "len" -> { - // len of a str is always 0..255 so always a byte, - // len of other things is assumed to need a word (even though the actual length could be less than 256) - val arg = args.single() - when(arg) { - is IdentifierReference -> { - val stmt = arg.targetStatement(namespace) - when(stmt) { - is VarDecl -> { - val value = stmt.value - if(value is LiteralValue) { - if(value.isString) return DataType.UBYTE // strings are 0..255 - } - } - } - DataType.UWORD // assume other lengths are words for now. - } - is LiteralValue -> throw FatalAstException("len of literalvalue should have been const-folded away already") - else -> DataType.UWORD - } - } else -> throw FatalAstException("unknown result type for builtin function $function") } } @@ -207,15 +175,13 @@ private fun oneDoubleArg(args: List, position: Position, namespace: return numericLiteral(function(float), args[0].position) } -private fun oneDoubleArgOutputInt(args: List, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Double)->Number): LiteralValue { +private fun oneDoubleArgOutputWord(args: List, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Double)->Number): LiteralValue { if(args.size!=1) throw SyntaxError("built-in function requires one floating point argument", position) val constval = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException() - val float: Double = when(constval.type) { - DataType.UBYTE, DataType.UWORD, DataType.FLOAT -> constval.asNumericValue!!.toDouble() - else -> throw SyntaxError("built-in function requires one floating point argument", position) - } - return numericLiteral(function(float).toInt(), args[0].position) + if(constval.type!=DataType.FLOAT) + throw SyntaxError("built-in function requires one floating point argument", position) + return LiteralValue(DataType.WORD, wordvalue=function(constval.asNumericValue!!.toDouble()).toInt(), position=args[0].position) } private fun oneIntArgOutputInt(args: List, position: Position, namespace:INameScope, heap: HeapValues, function: (arg: Int)->Number): LiteralValue { @@ -384,15 +350,15 @@ private fun builtinLen(args: List, position: Position, namespace:IN return when(argument.type) { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.MATRIX_UB, DataType.MATRIX_B -> { val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).array!!.size - numericLiteral(arraySize, args[0].position) + LiteralValue(DataType.UWORD, wordvalue=arraySize, position=args[0].position) } DataType.ARRAY_F -> { val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).doubleArray!!.size - numericLiteral(arraySize, args[0].position) + LiteralValue(DataType.UWORD, wordvalue=arraySize, position=args[0].position) } DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { val str = argument.strvalue ?: heap.get(argument.heapId!!).str!! - numericLiteral(str.length, args[0].position) + LiteralValue(DataType.UWORD, wordvalue=str.length, position=args[0].position) } DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, diff --git a/compiler/src/prog8/optimizing/ConstantFolding.kt b/compiler/src/prog8/optimizing/ConstantFolding.kt index 79101a722..15776793b 100644 --- a/compiler/src/prog8/optimizing/ConstantFolding.kt +++ b/compiler/src/prog8/optimizing/ConstantFolding.kt @@ -44,7 +44,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV } } in IntegerDatatypes -> { - // vardecl: for byte/word vars, convert char/string of length 1 initialization values to integer + // vardecl: for byte/word vars, convert char/string of length 1 initialization values to ubyte integer val literal = decl.value as? LiteralValue if (literal != null && literal.isString && literal.strvalue?.length == 1) { val petscii = Petscii.encodePetscii(literal.strvalue)[0] @@ -426,7 +426,6 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV return super.process(range) } - // todo datatypes override fun process(literalValue: LiteralValue): LiteralValue { if(literalValue.strvalue!=null) { // intern the string; move it into the heap @@ -439,11 +438,23 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV } } else if(literalValue.arrayvalue!=null) { val newArray = literalValue.arrayvalue.map { it.process(this) }.toTypedArray() - var arrayDt = DataType.ARRAY_UB - if(newArray.any { it.resultingDatatype(namespace, heap) == DataType.UWORD }) - arrayDt = DataType.ARRAY_UW - if(newArray.any { it.resultingDatatype(namespace, heap) == DataType.FLOAT }) - arrayDt = DataType.ARRAY_F + val arrayDt = + if(newArray.any { it.resultingDatatype(namespace, heap) == DataType.FLOAT }) + DataType.ARRAY_F + else { + if (newArray.any { it.resultingDatatype(namespace, heap) in setOf(DataType.BYTE, DataType.WORD) }) { + // we have signed values + if (newArray.any { it.resultingDatatype(namespace, heap) == DataType.WORD }) + DataType.ARRAY_W + else + DataType.ARRAY_B + } else { + // only unsigned values + if (newArray.any { it.resultingDatatype(namespace, heap) == DataType.UWORD }) + DataType.ARRAY_UW + else DataType.ARRAY_UB + } + } // if the values are all constants, the array is moved to the heap val allElementsAreConstant = newArray.fold(true) { c, expr-> c and (expr is LiteralValue)} @@ -454,10 +465,21 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV return super.process(literalValue) } if(arrayDt==DataType.ARRAY_UB || arrayDt==DataType.MATRIX_UB) { + // all values should be ubytes + val integerArray = litArray.map { (it as LiteralValue).bytevalue } + if (integerArray.any { it == null || it.toInt() !in 0..255 }) { + addError(ExpressionError("byte array elements must all be integers 0..255", literalValue.position)) + return super.process(literalValue) + } + val array = integerArray.mapNotNull { it?.toInt() }.toIntArray() + val heapId = heap.add(arrayDt, array) + val newValue = LiteralValue(arrayDt, heapId = heapId, position = literalValue.position) + return super.process(newValue) + } else if(arrayDt==DataType.ARRAY_B || arrayDt==DataType.MATRIX_B) { // all values should be bytes val integerArray = litArray.map { (it as LiteralValue).bytevalue } - if(integerArray.any { it==null || it.toInt() !in 0..255 }) { - addError(ExpressionError("byte array elements must all be integers 0..255", literalValue.position)) + if(integerArray.any { it==null || it.toInt() !in -128..127 }) { + addError(ExpressionError("byte array elements must all be integers -128..127", literalValue.position)) return super.process(literalValue) } val array = integerArray.mapNotNull { it?.toInt() }.toIntArray() @@ -475,6 +497,17 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV val heapId = heap.add(arrayDt, array) val newValue = LiteralValue(DataType.ARRAY_UW, heapId=heapId, position = literalValue.position) return super.process(newValue) + } else if(arrayDt==DataType.ARRAY_W) { + // all values should be bytes or words + val integerArray = litArray.map { (it as LiteralValue).asIntegerValue } + if(integerArray.any {it==null || it !in -32768..32767 }) { + addError(ExpressionError("word array elements must all be integers -32768..32767", literalValue.position)) + return super.process(literalValue) + } + val array = integerArray.filterNotNull().toIntArray() + val heapId = heap.add(arrayDt, array) + val newValue = LiteralValue(DataType.ARRAY_W, heapId=heapId, position = literalValue.position) + return super.process(newValue) } else if(arrayDt==DataType.ARRAY_F) { // all values should be bytes, words or floats val doubleArray = litArray.map { (it as LiteralValue).asNumericValue?.toDouble() } diff --git a/compiler/src/prog8/stackvm/Memory.kt b/compiler/src/prog8/stackvm/Memory.kt index 7499f132e..fc6c3fa2f 100644 --- a/compiler/src/prog8/stackvm/Memory.kt +++ b/compiler/src/prog8/stackvm/Memory.kt @@ -2,29 +2,62 @@ package prog8.stackvm import prog8.compiler.target.c64.Mflpt5 import prog8.compiler.target.c64.Petscii +import kotlin.math.abs class Memory { private val mem = ShortArray(65536) // shorts because byte is signed and we store values 0..255 - fun getByte(address: Int): Short { + fun getUByte(address: Int): Short { return mem[address] } - fun setByte(address: Int, value: Short) { - if(value<0 || value>255) throw VmExecutionException("byte value not 0..255") + fun getSByte(address: Int): Short { + val ubyte = getUByte(address) + if(ubyte <= 127) + return ubyte + return (-((ubyte.toInt() xor 255)+1)).toShort() // 2's complement + } + + fun setUByte(address: Int, value: Short) { + if(value !in 0..255) + throw VmExecutionException("ubyte value out of range") mem[address] = value } - fun getWord(address: Int): Int { + fun setSByte(address: Int, value: Short) { + if(value !in -128..127) throw VmExecutionException("byte value out of range") + if(value>=0) + setUByte(address, value) + else + setUByte(address, ((abs(value.toInt()) xor 255)+1).toShort()) // 2's complement + } + + fun getUWord(address: Int): Int { return mem[address] + 256*mem[address+1] } - fun setWord(address: Int, value: Int) { - if(value<0 || value>65535) throw VmExecutionException("word value not 0..65535") + fun getSWord(address: Int): Int { + val uword = getUWord(address) + if(uword <= 32767) + return uword + return -((uword xor 65535)+1) // 2's complement + } + + fun setUWord(address: Int, value: Int) { + if(value !in 0..65535) + throw VmExecutionException("uword value out of range") mem[address] = value.and(255).toShort() mem[address+1] = (value / 256).toShort() } + fun setSWord(address: Int, value: Int) { + if(value !in -32768..32767) throw VmExecutionException("word value out of range") + if(value>=0) + setUWord(address, value) + else + setUWord(address, (abs(value) xor 65535)+1) // 2's complement + } + fun setFloat(address: Int, value: Double) { val mflpt5 = Mflpt5.fromNumber(value) mem[address] = mflpt5.b0 diff --git a/compiler/src/prog8/stackvm/Program.kt b/compiler/src/prog8/stackvm/Program.kt index e818689e2..0331c0d38 100644 --- a/compiler/src/prog8/stackvm/Program.kt +++ b/compiler/src/prog8/stackvm/Program.kt @@ -142,8 +142,10 @@ class Program (val name: String, } val (type, valueStr) = args.split(':') return when(type) { - "b" -> Value(DataType.UBYTE, valueStr.toShort(16)) - "w" -> Value(DataType.UWORD, valueStr.toInt(16)) + "b" -> Value(DataType.BYTE, valueStr.toShort(16)) + "ub" -> Value(DataType.UBYTE, valueStr.toShort(16)) + "w" -> Value(DataType.WORD, valueStr.toInt(16)) + "uw" -> Value(DataType.UWORD, valueStr.toInt(16)) "f" -> Value(DataType.FLOAT, valueStr.toDouble()) "heap" -> { val heapId = valueStr.toInt() @@ -165,9 +167,9 @@ class Program (val name: String, throw VmExecutionException("missing value type character") val type = DataType.valueOf(typeStr.toUpperCase()) val value = when(type) { - DataType.UBYTE -> Value(DataType.UBYTE, valueStr.substring(2).toShort(16)) + DataType.UBYTE -> Value(DataType.UBYTE, valueStr.substring(3).toShort(16)) DataType.BYTE -> Value(DataType.BYTE, valueStr.substring(2).toShort(16)) - DataType.UWORD -> Value(DataType.UWORD, valueStr.substring(2).toInt(16)) + DataType.UWORD -> Value(DataType.UWORD, valueStr.substring(3).toInt(16)) DataType.WORD -> Value(DataType.WORD, valueStr.substring(2).toInt(16)) DataType.FLOAT -> Value(DataType.FLOAT, valueStr.substring(2).toDouble()) DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> { diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index af98ea355..e3baf3451 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -1,6 +1,6 @@ package prog8.stackvm -import prog8.ast.DataType +import prog8.ast.* import prog8.compiler.HeapValues import prog8.compiler.target.c64.Petscii import java.io.File @@ -14,22 +14,26 @@ enum class Opcode { PUSH_BYTE, // push byte value PUSH_WORD, // push word value (or 'address' of string / array / matrix) PUSH_FLOAT, // push float value - PUSH_MEM_BYTE, // push byte value from memory to stack - PUSH_MEM_WORD, // push word value from memory to stack + PUSH_MEM_B, // push byte value from memory to stack + PUSH_MEM_UB, // push byte value from memory to stack + PUSH_MEM_W, // push word value from memory to stack + PUSH_MEM_UW, // push word value from memory to stack PUSH_MEM_FLOAT, // push float value from memory to stack - PUSH_VAR_BYTE, // push byte variable - PUSH_VAR_WORD, // push word variable + PUSH_VAR_BYTE, // push byte variable (ubyte, byte) + PUSH_VAR_WORD, // push word variable (uword, word) PUSH_VAR_FLOAT, // push float variable // popping values off the (evaluation) stack, possibly storing them in another location DISCARD_BYTE, // discard top byte value DISCARD_WORD, // discard top word value DISCARD_FLOAT, // discard top float value - POP_MEM_BYTE, // pop byte value into destination memory address - POP_MEM_WORD, // pop word value into destination memory address + POP_MEM_B, // pop byte value into destination memory address + POP_MEM_UB, // pop byte value into destination memory address + POP_MEM_W, // pop word value into destination memory address + POP_MEM_UW, // pop word value into destination memory address POP_MEM_FLOAT, // pop float value into destination memory address - POP_VAR_BYTE, // pop byte value into variable - POP_VAR_WORD, // pop word value into variable + POP_VAR_BYTE, // pop byte value into variable (byte, ubyte) + POP_VAR_WORD, // pop word value into variable (word, uword) POP_VAR_FLOAT, // pop float value into variable // optimized copying of one var to another (replaces push+pop) @@ -433,11 +437,19 @@ class StackVm(private var traceOutputFile: String?) { for (value in meminit.value) { when(value.type) { DataType.UBYTE -> { - mem.setByte(address, value.integerValue().toShort()) + mem.setUByte(address, value.integerValue().toShort()) + address += 1 + } + DataType.BYTE -> { + mem.setSByte(address, value.integerValue().toShort()) address += 1 } DataType.UWORD -> { - mem.setWord(address, value.integerValue()) + mem.setUWord(address, value.integerValue()) + address += 2 + } + DataType.WORD -> { + mem.setSWord(address, value.integerValue()) address += 2 } DataType.FLOAT -> { @@ -474,24 +486,32 @@ class StackVm(private var traceOutputFile: String?) { when (ins.opcode) { Opcode.NOP -> {} Opcode.PUSH_BYTE -> { - checkDt(ins.arg, DataType.UBYTE) + checkDt(ins.arg, setOf(DataType.UBYTE, DataType.BYTE)) evalstack.push(ins.arg) } Opcode.PUSH_WORD -> { - checkDt(ins.arg, setOf(DataType.UWORD, DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.MATRIX_UB)) + checkDt(ins.arg, setOf(DataType.UWORD, DataType.WORD) + IterableDatatypes) evalstack.push(ins.arg) } Opcode.PUSH_FLOAT -> { checkDt(ins.arg, DataType.FLOAT) evalstack.push(ins.arg) } - Opcode.PUSH_MEM_BYTE -> { + Opcode.PUSH_MEM_UB -> { val address = ins.arg!!.integerValue() - evalstack.push(Value(DataType.UBYTE, mem.getByte(address))) + evalstack.push(Value(DataType.UBYTE, mem.getUByte(address))) } - Opcode.PUSH_MEM_WORD -> { + Opcode.PUSH_MEM_B -> { val address = ins.arg!!.integerValue() - evalstack.push(Value(DataType.UWORD, mem.getWord(address))) + evalstack.push(Value(DataType.BYTE, mem.getSByte(address))) + } + Opcode.PUSH_MEM_UW -> { + val address = ins.arg!!.integerValue() + evalstack.push(Value(DataType.UWORD, mem.getUWord(address))) + } + Opcode.PUSH_MEM_W -> { + val address = ins.arg!!.integerValue() + evalstack.push(Value(DataType.WORD, mem.getSWord(address))) } Opcode.PUSH_MEM_FLOAT -> { val address = ins.arg!!.integerValue() @@ -509,17 +529,29 @@ class StackVm(private var traceOutputFile: String?) { val value = evalstack.pop() checkDt(value, DataType.FLOAT) } - Opcode.POP_MEM_BYTE -> { + Opcode.POP_MEM_UB -> { val value = evalstack.pop() checkDt(value, DataType.UBYTE) val address = ins.arg!!.integerValue() - mem.setByte(address, value.integerValue().toShort()) + mem.setUByte(address, value.integerValue().toShort()) } - Opcode.POP_MEM_WORD -> { + Opcode.POP_MEM_B -> { + val value = evalstack.pop() + checkDt(value, DataType.BYTE) + val address = ins.arg!!.integerValue() + mem.setSByte(address, value.integerValue().toShort()) + } + Opcode.POP_MEM_UW -> { val value = evalstack.pop() checkDt(value, DataType.UWORD) val address = ins.arg!!.integerValue() - mem.setWord(address, value.integerValue()) + mem.setUWord(address, value.integerValue()) + } + Opcode.POP_MEM_W -> { + val value = evalstack.pop() + checkDt(value, DataType.WORD) + val address = ins.arg!!.integerValue() + mem.setSWord(address, value.integerValue()) } Opcode.POP_MEM_FLOAT -> { val value = evalstack.pop() @@ -539,6 +571,18 @@ class StackVm(private var traceOutputFile: String?) { checkDt(second, DataType.UWORD) evalstack.push(second.add(top)) } + Opcode.ADD_B -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.BYTE) + checkDt(second, DataType.BYTE) + evalstack.push(second.add(top)) + } + Opcode.ADD_W -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.WORD) + checkDt(second, DataType.WORD) + evalstack.push(second.add(top)) + } Opcode.ADD_F -> { val (top, second) = evalstack.pop2() checkDt(top, DataType.FLOAT) @@ -557,6 +601,18 @@ class StackVm(private var traceOutputFile: String?) { checkDt(second, DataType.UWORD) evalstack.push(second.sub(top)) } + Opcode.SUB_B -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.BYTE) + checkDt(second, DataType.BYTE) + evalstack.push(second.sub(top)) + } + Opcode.SUB_W -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.WORD) + checkDt(second, DataType.WORD) + evalstack.push(second.sub(top)) + } Opcode.SUB_F -> { val (top, second) = evalstack.pop2() checkDt(top, DataType.FLOAT) @@ -575,6 +631,18 @@ class StackVm(private var traceOutputFile: String?) { checkDt(second, DataType.UWORD) evalstack.push(second.mul(top)) } + Opcode.MUL_B -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.BYTE) + checkDt(second, DataType.BYTE) + evalstack.push(second.mul(top)) + } + Opcode.MUL_W -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.WORD) + checkDt(second, DataType.WORD) + evalstack.push(second.mul(top)) + } Opcode.MUL_F -> { val (top, second) = evalstack.pop2() checkDt(top, DataType.FLOAT) @@ -593,6 +661,18 @@ class StackVm(private var traceOutputFile: String?) { checkDt(second, DataType.UWORD) evalstack.push(second.div(top)) } + Opcode.DIV_B -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.BYTE) + checkDt(second, DataType.BYTE) + evalstack.push(second.div(top)) + } + Opcode.DIV_W -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.WORD) + checkDt(second, DataType.WORD) + evalstack.push(second.div(top)) + } Opcode.DIV_F -> { val (top, second) = evalstack.pop2() checkDt(top, DataType.FLOAT) @@ -611,6 +691,18 @@ class StackVm(private var traceOutputFile: String?) { checkDt(second, DataType.UWORD) evalstack.push(second.floordiv(top)) } + Opcode.FLOORDIV_B -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.BYTE) + checkDt(second, DataType.BYTE) + evalstack.push(second.floordiv(top)) + } + Opcode.FLOORDIV_W -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.WORD) + checkDt(second, DataType.WORD) + evalstack.push(second.floordiv(top)) + } Opcode.FLOORDIV_F -> { val (top, second) = evalstack.pop2() checkDt(top, DataType.FLOAT) @@ -629,6 +721,18 @@ class StackVm(private var traceOutputFile: String?) { checkDt(second, DataType.UWORD) evalstack.push(second.remainder(top)) } + Opcode.REMAINDER_B -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.BYTE) + checkDt(second, DataType.BYTE) + evalstack.push(second.remainder(top)) + } + Opcode.REMAINDER_W -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.WORD) + checkDt(second, DataType.WORD) + evalstack.push(second.remainder(top)) + } Opcode.REMAINDER_F -> { val (top, second) = evalstack.pop2() checkDt(top, DataType.FLOAT) @@ -647,6 +751,18 @@ class StackVm(private var traceOutputFile: String?) { checkDt(second, DataType.UWORD) evalstack.push(second.pow(top)) } + Opcode.POW_B -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.BYTE) + checkDt(second, DataType.BYTE) + evalstack.push(second.pow(top)) + } + Opcode.POW_W -> { + val (top, second) = evalstack.pop2() + checkDt(top, DataType.WORD) + checkDt(second, DataType.WORD) + evalstack.push(second.pow(top)) + } Opcode.POW_F -> { val (top, second) = evalstack.pop2() checkDt(top, DataType.FLOAT) @@ -655,12 +771,12 @@ class StackVm(private var traceOutputFile: String?) { } Opcode.NEG_B -> { val v = evalstack.pop() - checkDt(v, DataType.UBYTE) + checkDt(v, DataType.BYTE) evalstack.push(v.neg()) } Opcode.NEG_W -> { val v = evalstack.pop() - checkDt(v, DataType.UWORD) + checkDt(v, DataType.WORD) evalstack.push(v.neg()) } Opcode.NEG_F -> { @@ -839,82 +955,85 @@ class StackVm(private var traceOutputFile: String?) { Opcode.CLI -> P_irqd = false Opcode.TERMINATE -> throw VmTerminationException("terminate instruction") Opcode.BREAKPOINT -> throw VmBreakpointException() + Opcode.LINE -> { + sourceLine = ins.callLabel!! + } Opcode.SHL_MEM_BYTE -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UBYTE, mem.getByte(addr)) + val value = Value(DataType.UBYTE, mem.getUByte(addr)) val newValue = value.shl() - mem.setByte(addr, newValue.integerValue().toShort()) + mem.setUByte(addr, newValue.integerValue().toShort()) } Opcode.SHL_MEM_WORD -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UWORD, mem.getWord(addr)) + val value = Value(DataType.UWORD, mem.getUWord(addr)) val newValue = value.shl() - mem.setWord(addr, newValue.integerValue()) + mem.setUWord(addr, newValue.integerValue()) } Opcode.SHR_MEM_BYTE -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UBYTE, mem.getByte(addr)) + val value = Value(DataType.UBYTE, mem.getUByte(addr)) val newValue = value.shr() - mem.setByte(addr, newValue.integerValue().toShort()) + mem.setUByte(addr, newValue.integerValue().toShort()) } Opcode.SHR_MEM_WORD -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UWORD, mem.getWord(addr)) + val value = Value(DataType.UWORD, mem.getUWord(addr)) val newValue = value.shr() - mem.setWord(addr, newValue.integerValue()) + mem.setUWord(addr, newValue.integerValue()) } Opcode.ROL_MEM_BYTE -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UBYTE, mem.getByte(addr)) + val value = Value(DataType.UBYTE, mem.getUByte(addr)) val (newValue, newCarry) = value.rol(P_carry) - mem.setByte(addr, newValue.integerValue().toShort()) + mem.setUByte(addr, newValue.integerValue().toShort()) P_carry = newCarry } Opcode.ROL_MEM_WORD -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UWORD, mem.getWord(addr)) + val value = Value(DataType.UWORD, mem.getUWord(addr)) val (newValue, newCarry) = value.rol(P_carry) - mem.setWord(addr, newValue.integerValue()) + mem.setUWord(addr, newValue.integerValue()) P_carry = newCarry } Opcode.ROR_MEM_BYTE -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UBYTE, mem.getByte(addr)) + val value = Value(DataType.UBYTE, mem.getUByte(addr)) val (newValue, newCarry) = value.ror(P_carry) - mem.setByte(addr, newValue.integerValue().toShort()) + mem.setUByte(addr, newValue.integerValue().toShort()) P_carry = newCarry } Opcode.ROR_MEM_WORD -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UWORD, mem.getWord(addr)) + val value = Value(DataType.UWORD, mem.getUWord(addr)) val (newValue, newCarry) = value.ror(P_carry) - mem.setWord(addr, newValue.integerValue()) + mem.setUWord(addr, newValue.integerValue()) P_carry = newCarry } Opcode.ROL2_MEM_BYTE -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UBYTE, mem.getByte(addr)) + val value = Value(DataType.UBYTE, mem.getUByte(addr)) val newValue = value.rol2() - mem.setByte(addr, newValue.integerValue().toShort()) + mem.setUByte(addr, newValue.integerValue().toShort()) } Opcode.ROL2_MEM_WORD -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UWORD, mem.getWord(addr)) + val value = Value(DataType.UWORD, mem.getUWord(addr)) val newValue = value.rol2() - mem.setWord(addr, newValue.integerValue()) + mem.setUWord(addr, newValue.integerValue()) } Opcode.ROR2_MEM_BYTE -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UBYTE, mem.getByte(addr)) + val value = Value(DataType.UBYTE, mem.getUByte(addr)) val newValue = value.ror2() - mem.setByte(addr, newValue.integerValue().toShort()) + mem.setUByte(addr, newValue.integerValue().toShort()) } Opcode.ROR2_MEM_WORD -> { val addr = ins.arg!!.integerValue() - val value = Value(DataType.UWORD, mem.getWord(addr)) + val value = Value(DataType.UWORD, mem.getUWord(addr)) val newValue = value.ror2() - mem.setWord(addr, newValue.integerValue()) + mem.setUWord(addr, newValue.integerValue()) } Opcode.JUMP -> {} // do nothing; the next instruction is wired up already to the jump target @@ -940,7 +1059,7 @@ class StackVm(private var traceOutputFile: String?) { } Opcode.PUSH_VAR_BYTE -> { val value = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") - checkDt(value, DataType.UBYTE) + checkDt(value, setOf(DataType.UBYTE, DataType.BYTE)) evalstack.push(value) } Opcode.PUSH_VAR_WORD -> { @@ -955,30 +1074,38 @@ class StackVm(private var traceOutputFile: String?) { } Opcode.POP_VAR_BYTE -> { val value = evalstack.pop() - checkDt(value, DataType.UBYTE) + checkDt(value, setOf(DataType.UBYTE, DataType.BYTE)) val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") - checkDt(variable, DataType.UBYTE) + checkDt(variable, setOf(DataType.UBYTE, DataType.BYTE)) + if(value.type!=variable.type) + throw VmExecutionException("datatype mismatch") variables[ins.callLabel!!] = value } Opcode.POP_VAR_WORD -> { val value = evalstack.pop() - checkDt(value, setOf(DataType.UWORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) + checkDt(value, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") - checkDt(variable, setOf(DataType.UWORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) + checkDt(variable, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) + if(value.type!=variable.type) + throw VmExecutionException("datatype mismatch") variables[ins.callLabel!!] = value } Opcode.COPY_VAR_BYTE -> { val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}") - checkDt(source, DataType.UBYTE) - checkDt(dest, DataType.UBYTE) + checkDt(source, setOf(DataType.UBYTE, DataType.BYTE)) + checkDt(dest, setOf(DataType.UBYTE, DataType.BYTE)) + if(dest.type!=source.type) + throw VmExecutionException("datatype mismatch") variables[ins.callLabel2!!] = source } Opcode.COPY_VAR_WORD -> { val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}") - checkDt(source, setOf(DataType.UWORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) - checkDt(dest, setOf(DataType.UWORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) + checkDt(source, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) + checkDt(dest, setOf(DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) + if(dest.type!=source.type) + throw VmExecutionException("datatype mismatch") variables[ins.callLabel2!!] = source } Opcode.COPY_VAR_FLOAT -> { @@ -1327,8 +1454,13 @@ class StackVm(private var traceOutputFile: String?) { } Opcode.B2WORD -> { val byte = evalstack.pop() - checkDt(byte, DataType.UBYTE) - evalstack.push(Value(DataType.UWORD, byte.integerValue())) + checkDt(byte, DataType.BYTE) + evalstack.push(Value(DataType.WORD, byte.integerValue())) + } + Opcode.UB2UWORD -> { + val ubyte = evalstack.pop() + checkDt(ubyte, DataType.UBYTE) + evalstack.push(Value(DataType.UWORD, ubyte.integerValue())) } Opcode.MSB2WORD -> { val byte = evalstack.pop() @@ -1336,30 +1468,38 @@ class StackVm(private var traceOutputFile: String?) { evalstack.push(Value(DataType.UWORD, byte.integerValue() * 256)) } Opcode.B2FLOAT -> { + val byte = evalstack.pop() + checkDt(byte, DataType.BYTE) + evalstack.push(Value(DataType.FLOAT, byte.integerValue())) + } + Opcode.UB2FLOAT -> { val byte = evalstack.pop() checkDt(byte, DataType.UBYTE) evalstack.push(Value(DataType.FLOAT, byte.integerValue())) } Opcode.W2FLOAT -> { - val byte = evalstack.pop() - checkDt(byte, DataType.UWORD) - evalstack.push(Value(DataType.FLOAT, byte.integerValue())) + val wrd = evalstack.pop() + checkDt(wrd, DataType.UWORD) + evalstack.push(Value(DataType.FLOAT, wrd.integerValue())) } - Opcode.LINE -> { - sourceLine = ins.callLabel!! + Opcode.UW2FLOAT -> { + val uwrd = evalstack.pop() + checkDt(uwrd, DataType.UWORD) + evalstack.push(Value(DataType.FLOAT, uwrd.integerValue())) } Opcode.READ_INDEXED_VAR_BYTE -> { // 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.UWORD) { - // assume the variable is a pointer (address) and get the byte value from that memory location - evalstack.push(Value(DataType.UBYTE, mem.getByte(variable.integerValue()))) + // assume the variable is a pointer (address) and get the ubyte value from that memory location + evalstack.push(Value(DataType.UBYTE, mem.getUByte(variable.integerValue()))) } else { // get indexed byte element from the array val array = heap.get(variable.heapId) when(array.type) { DataType.ARRAY_UB, DataType.MATRIX_UB -> evalstack.push(Value(DataType.UBYTE, array.array!![index])) + DataType.ARRAY_B, DataType.MATRIX_B -> evalstack.push(Value(DataType.BYTE, array.array!![index])) DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> evalstack.push(Value(DataType.UBYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0])) else -> throw VmExecutionException("not a proper array/matrix/string variable with byte elements") } @@ -1371,13 +1511,15 @@ class StackVm(private var traceOutputFile: String?) { val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") if(variable.type==DataType.UWORD) { // assume the variable is a pointer (address) and get the word value from that memory location - evalstack.push(Value(DataType.UWORD, mem.getWord(variable.integerValue()))) + evalstack.push(Value(DataType.UWORD, mem.getUWord(variable.integerValue()))) } else { // get indexed word element from the array val array = heap.get(variable.heapId) - if(array.type!=DataType.ARRAY_UW) - throw VmExecutionException("not a proper array var with word elements") - evalstack.push(Value(DataType.UWORD, array.array!![index])) + when(array.type){ + DataType.ARRAY_UW -> evalstack.push(Value(DataType.UWORD, array.array!![index])) + DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, array.array!![index])) + else -> throw VmExecutionException("not a proper array var with word elements") + } } } Opcode.READ_INDEXED_VAR_FLOAT -> { @@ -1399,18 +1541,17 @@ class StackVm(private var traceOutputFile: String?) { // 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.UBYTE) + checkDt(value, setOf(DataType.UBYTE, DataType.BYTE)) val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") if(variable.type==DataType.UWORD) { // assume the variable is a pointer (address) and write the byte value to that memory location - mem.setByte(variable.integerValue(), value.integerValue().toShort()) + mem.setUByte(variable.integerValue(), value.integerValue().toShort()) } else { // set indexed byte element in the array val array = heap.get(variable.heapId) when(array.type) { - DataType.ARRAY_UB, DataType.MATRIX_UB -> { - array.array!![index] = value.integerValue() - } + DataType.ARRAY_UB, DataType.MATRIX_UB -> array.array!![index] = value.integerValue() + DataType.ARRAY_B, DataType.MATRIX_B -> array.array!![index] = value.integerValue() DataType.STR, DataType.STR_P, DataType.STR_S, @@ -1427,17 +1568,20 @@ class StackVm(private var traceOutputFile: String?) { // 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.UWORD) + checkDt(value, setOf(DataType.UWORD, DataType.WORD)) val variable = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") if(variable.type==DataType.UWORD) { // assume the variable is a pointer (address) and write the word value to that memory location - mem.setWord(variable.integerValue(), value.integerValue()) + mem.setUWord(variable.integerValue(), value.integerValue()) } else { // set indexed word element in the array val array = heap.get(variable.heapId) - if(array.type!=DataType.ARRAY_UW) - throw VmExecutionException("not a proper array var with word elements") - array.array!![index] = value.integerValue() + when(array.type) { + DataType.ARRAY_UW -> array.array!![index] = value.integerValue() + DataType.ARRAY_W -> array.array!![index] = value.integerValue() + else -> throw VmExecutionException("not a proper array var with word elements") + } + } } Opcode.WRITE_INDEXED_VAR_FLOAT -> { @@ -1457,24 +1601,7 @@ class StackVm(private var traceOutputFile: String?) { array.doubleArray!![index] = value.numericValue().toDouble() } } - Opcode.ADD_B -> TODO() - Opcode.ADD_W -> TODO() - Opcode.SUB_B -> TODO() - Opcode.SUB_W -> TODO() - Opcode.MUL_B -> TODO() - Opcode.MUL_W -> TODO() - Opcode.DIV_B -> TODO() - Opcode.DIV_W -> TODO() - Opcode.FLOORDIV_B -> TODO() - Opcode.FLOORDIV_W -> TODO() - Opcode.REMAINDER_B -> TODO() - Opcode.REMAINDER_W -> TODO() - Opcode.POW_B -> TODO() - Opcode.POW_W -> TODO() - Opcode.UB2UWORD -> TODO() - Opcode.UB2FLOAT -> TODO() - Opcode.UW2FLOAT -> TODO() - else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}") + //else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}") } if(traceOutput!=null) { @@ -1491,7 +1618,7 @@ class StackVm(private var traceOutputFile: String?) { when (syscall) { Syscall.WRITE_MEMCHR -> { val address = evalstack.pop().integerValue() - print(Petscii.decodePetscii(listOf(mem.getByte(address)), true)) + print(Petscii.decodePetscii(listOf(mem.getUByte(address)), true)) } Syscall.WRITE_MEMSTR -> { val address = evalstack.pop().integerValue() @@ -1550,7 +1677,7 @@ class StackVm(private var traceOutputFile: String?) { 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.UWORD, evalstack.pop().numericValue().toDouble().roundToInt())) + Syscall.FUNC_ROUND -> evalstack.push(Value(DataType.WORD, evalstack.pop().numericValue().toDouble().roundToInt())) Syscall.FUNC_ABS -> { val value = evalstack.pop() val absValue= @@ -1574,25 +1701,15 @@ class StackVm(private var traceOutputFile: String?) { 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.UBYTE -> Value(DataType.UBYTE, value.numericValue()) - DataType.UWORD -> Value(DataType.UWORD, value.numericValue()) - DataType.FLOAT -> Value(DataType.UWORD, floor(value.numericValue().toDouble())) - else -> throw VmExecutionException("cannot get floor of $value") - } - evalstack.push(result) + if(value.type in NumericDatatypes) + evalstack.push(Value(DataType.WORD, floor(value.numericValue().toDouble()).toInt())) + else throw VmExecutionException("cannot get floor of $value") } Syscall.FUNC_CEIL -> { val value = evalstack.pop() - val result = - when(value.type) { - DataType.UBYTE -> Value(DataType.UBYTE, value.numericValue()) - DataType.UWORD -> Value(DataType.UWORD, value.numericValue()) - DataType.FLOAT -> Value(DataType.UWORD, ceil(value.numericValue().toDouble())) - else -> throw VmExecutionException("cannot get ceil of $value") - } - evalstack.push(result) + if(value.type in NumericDatatypes) + evalstack.push(Value(DataType.WORD, ceil(value.numericValue().toDouble()).toInt())) + else throw VmExecutionException("cannot get ceil of $value") } Syscall.FUNC_MAX -> { val iterable = evalstack.pop() @@ -1697,9 +1814,9 @@ class StackVm(private var traceOutputFile: String?) { val jiffies = min((timestamp-bootTime)*60/1000, 24*3600*60-1) // update the C-64 60hz jiffy clock in the ZP addresses: - mem.setByte(0x00a0, (jiffies ushr 16).toShort()) - mem.setByte(0x00a1, (jiffies ushr 8 and 255).toShort()) - mem.setByte(0x00a2, (jiffies and 255).toShort()) + mem.setUByte(0x00a0, (jiffies ushr 16).toShort()) + mem.setUByte(0x00a1, (jiffies ushr 8 and 255).toShort()) + mem.setUByte(0x00a2, (jiffies and 255).toShort()) if(irqStartInstruction!=null) { try { diff --git a/compiler/src/prog8/stackvm/Value.kt b/compiler/src/prog8/stackvm/Value.kt index 4efeec104..b56a9b96e 100644 --- a/compiler/src/prog8/stackvm/Value.kt +++ b/compiler/src/prog8/stackvm/Value.kt @@ -18,19 +18,27 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) { init { when(type) { DataType.UBYTE -> { - byteval = (numericvalueOrHeapId.toInt() and 255).toShort() // ubyte wrap around 0..255 + if(numericvalueOrHeapId.toInt() !in 0..255) + throw VmExecutionException("value out of range: $numericvalueOrHeapId") + byteval = numericvalueOrHeapId.toShort() asBooleanValue = byteval != (0.toShort()) } DataType.BYTE -> { - byteval = limitByte(numericvalueOrHeapId.toInt()) + if(numericvalueOrHeapId.toInt() !in -128..127) + throw VmExecutionException("value out of range: $numericvalueOrHeapId") + byteval = numericvalueOrHeapId.toShort() asBooleanValue = byteval != (0.toShort()) } DataType.UWORD -> { - wordval = numericvalueOrHeapId.toInt() and 65535 // uword wrap around 0..65535 + if(numericvalueOrHeapId.toInt() !in 0..65535) + throw VmExecutionException("value out of range: $numericvalueOrHeapId") + wordval = numericvalueOrHeapId.toInt() asBooleanValue = wordval != 0 } DataType.WORD -> { - wordval = limitWord(numericvalueOrHeapId.toInt()) + if(numericvalueOrHeapId.toInt() !in -32768..32767) + throw VmExecutionException("value out of range: $numericvalueOrHeapId") + wordval = numericvalueOrHeapId.toInt() asBooleanValue = wordval != 0 } DataType.FLOAT -> { @@ -46,35 +54,22 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) { } } - companion object { - fun limitByte(value: Int): Short { - var bval: Int - if(value < 0) { - bval = -(abs(value) and 127) - if(bval==0) bval=-128 - } - else - bval = value and 127 - return bval.toShort() - } - fun limitWord(value: Int): Int { - var bval: Int - if(value < 0) { - bval = -(abs(value) and 32767) - if(bval==0) bval=-32768 - } - else - bval = value and 32767 - return bval - } - } - override fun toString(): String { return when(type) { DataType.UBYTE -> "ub:%02x".format(byteval) - DataType.BYTE -> "b:%02x".format(byteval) + DataType.BYTE -> { + if(byteval!!<0) + "b:-%02x".format(abs(byteval!!.toInt())) + else + "b:%02x".format(byteval) + } DataType.UWORD -> "uw:%04x".format(wordval) - DataType.WORD -> "w:%04x".format(wordval) + DataType.WORD -> { + if(wordval!!<0) + "w:-%04x".format(abs(wordval!!)) + else + "w:%04x".format(wordval) + } DataType.FLOAT -> "f:$floatval" else -> "heap:$heapId" } @@ -125,8 +120,8 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) { if(result.toDouble() < 0 ) { return when(leftDt) { DataType.UBYTE, DataType.UWORD -> throw VmExecutionException("arithmetic error: cannot store a negative value in a $leftDt") - DataType.BYTE -> Value(DataType.BYTE, limitByte(result.toInt())) - DataType.WORD -> Value(DataType.WORD, limitWord(result.toInt())) + DataType.BYTE -> Value(DataType.BYTE, result.toInt()) + DataType.WORD -> Value(DataType.WORD, result.toInt()) DataType.FLOAT -> Value(DataType.FLOAT, result) else -> throw VmExecutionException("$op on non-numeric type") } @@ -134,9 +129,9 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) { return when(leftDt) { DataType.UBYTE -> Value(DataType.UBYTE, result.toInt() and 255) - DataType.BYTE -> Value(DataType.BYTE, limitByte(result.toInt())) + DataType.BYTE -> Value(DataType.BYTE, result.toInt()) DataType.UWORD -> Value(DataType.UWORD, result.toInt() and 65535) - DataType.WORD -> Value(DataType.WORD, limitWord(result.toInt())) + DataType.WORD -> Value(DataType.WORD, result.toInt()) DataType.FLOAT -> Value(DataType.FLOAT, result) else -> throw VmExecutionException("$op on non-numeric type") } @@ -221,12 +216,20 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) { fun shl(): Value { val v = integerValue() - return Value(type, v shl 1) + if(type==DataType.UBYTE) + return Value(type, (v shl 1) and 255) + if(type==DataType.UWORD) + return Value(type, (v shl 1) and 65535) + throw VmExecutionException("invalid type for shl: $type") } fun shr(): Value { val v = integerValue() - return Value(type, v ushr 1) + if(type==DataType.UBYTE) + return Value(type, (v ushr 1) and 255) + if(type==DataType.UWORD) + return Value(type, (v ushr 1) and 65535) + throw VmExecutionException("invalid type for shr: $type") } fun rol(carry: Boolean): Pair { @@ -307,8 +310,8 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) { fun neg(): Value { return when(type) { - DataType.UBYTE -> Value(DataType.UBYTE, -(byteval!!)) - DataType.UWORD -> Value(DataType.UWORD, -(wordval!!)) + DataType.BYTE -> Value(DataType.BYTE, -(byteval!!)) + DataType.WORD -> Value(DataType.WORD, -(wordval!!)) DataType.FLOAT -> Value(DataType.FLOAT, -(floatval)!!) else -> throw VmExecutionException("neg can only work on byte/word/float") } diff --git a/compiler/test/StackVMOpcodeTests.kt b/compiler/test/StackVMOpcodeTests.kt index f05bc7869..0f41506a8 100644 --- a/compiler/test/StackVMOpcodeTests.kt +++ b/compiler/test/StackVMOpcodeTests.kt @@ -141,27 +141,51 @@ class TestStackVmOpcodes { @Test fun testPushMem() { val ins = mutableListOf( - Instruction(Opcode.PUSH_MEM_BYTE, Value(DataType.UWORD, 0x2000)), - Instruction(Opcode.PUSH_MEM_WORD, Value(DataType.UWORD, 0x3000)), - Instruction(Opcode.PUSH_MEM_FLOAT, Value(DataType.UWORD, 0x4000)) + Instruction(Opcode.PUSH_MEM_B, Value(DataType.UWORD, 0x2000)), + Instruction(Opcode.PUSH_MEM_UB, Value(DataType.UWORD, 0x3000)), + Instruction(Opcode.PUSH_MEM_W, Value(DataType.UWORD, 0x4000)), + Instruction(Opcode.PUSH_MEM_UW, Value(DataType.UWORD, 0x5000)), + Instruction(Opcode.PUSH_MEM_FLOAT, Value(DataType.UWORD, 0x6000)) ) - val mem=mapOf(0x2000 to listOf(Value(DataType.UWORD, 0x42ea)), - 0x3000 to listOf(Value(DataType.UWORD, 0x42ea)), - 0x4000 to listOf(Value(DataType.FLOAT, 42.25))) + val mem=mapOf(0x2000 to listOf(Value(DataType.UWORD, 0xc2ca)), + 0x3000 to listOf(Value(DataType.UWORD, 0xc2ca)), + 0x4000 to listOf(Value(DataType.UWORD, 0xc2ca)), + 0x5000 to listOf(Value(DataType.UWORD, 0xc2ca)), + 0x6000 to listOf(Value(DataType.FLOAT, 42.25))) vm.load(makeProg(ins, mem=mem), null) - assertEquals(0xea, vm.mem.getByte(0x2000)) - assertEquals(0x42, vm.mem.getByte(0x2001)) - assertEquals(0xea, vm.mem.getByte(0x3000)) - assertEquals(0x42, vm.mem.getByte(0x3001)) - assertEquals(0x42ea, vm.mem.getWord(0x2000)) - assertEquals(0x42ea, vm.mem.getWord(0x3000)) - assertEquals(42.25, vm.mem.getFloat(0x4000)) + assertEquals(0xca, vm.mem.getUByte(0x2000)) + assertEquals(0xc2, vm.mem.getUByte(0x2001)) + assertEquals(0xca, vm.mem.getUByte(0x3000)) + assertEquals(0xc2, vm.mem.getUByte(0x3001)) + assertEquals(0xca, vm.mem.getUByte(0x4000)) + assertEquals(0xc2, vm.mem.getUByte(0x4001)) + assertEquals(0xca, vm.mem.getUByte(0x5000)) + assertEquals(0xc2, vm.mem.getUByte(0x5001)) + assertEquals(-54, vm.mem.getSByte(0x2000)) + assertEquals(-62, vm.mem.getSByte(0x2001)) + assertEquals(-54, vm.mem.getSByte(0x3000)) + assertEquals(-62, vm.mem.getSByte(0x3001)) + assertEquals(-54, vm.mem.getSByte(0x4000)) + assertEquals(-62, vm.mem.getSByte(0x4001)) + assertEquals(-54, vm.mem.getSByte(0x5000)) + assertEquals(-62, vm.mem.getSByte(0x5001)) + assertEquals(0xc2ca, vm.mem.getUWord(0x2000)) + assertEquals(0xc2ca, vm.mem.getUWord(0x3000)) + assertEquals(0xc2ca, vm.mem.getUWord(0x4000)) + assertEquals(0xc2ca, vm.mem.getUWord(0x5000)) + assertEquals(-15670, vm.mem.getSWord(0x2000)) + assertEquals(-15670, vm.mem.getSWord(0x3000)) + assertEquals(-15670, vm.mem.getSWord(0x4000)) + assertEquals(-15670, vm.mem.getSWord(0x5000)) + assertEquals(42.25, vm.mem.getFloat(0x6000)) assertThat(vm.evalstack, empty()) - vm.step(3) - assertEquals(3, vm.evalstack.size) + vm.step(5) + assertEquals(5, vm.evalstack.size) assertEquals(Value(DataType.FLOAT, 42.25), vm.evalstack.pop()) - assertEquals(Value(DataType.UWORD, 0x42ea), vm.evalstack.pop()) - assertEquals(Value(DataType.UBYTE, 0xea), vm.evalstack.pop()) + assertEquals(Value(DataType.UWORD, 0xc2ca), vm.evalstack.pop()) + assertEquals(Value(DataType.WORD, -15670), vm.evalstack.pop()) + assertEquals(Value(DataType.UBYTE, 0xca), vm.evalstack.pop()) + assertEquals(Value(DataType.BYTE, -54), vm.evalstack.pop()) } @Test @@ -199,20 +223,32 @@ class TestStackVmOpcodes { fun testPopMem() { val ins = mutableListOf( Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, 42.25)), - Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0x42ea)), - Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 123)), - Instruction(Opcode.POP_MEM_BYTE, Value(DataType.UWORD, 0x2000)), - Instruction(Opcode.POP_MEM_WORD, Value(DataType.UWORD, 0x3000)), + Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0xc2ca)), + Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, -23456)), + Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)), + Instruction(Opcode.PUSH_BYTE, Value(DataType.BYTE, -55)), + Instruction(Opcode.POP_MEM_B, Value(DataType.UWORD, 0x2000)), + Instruction(Opcode.POP_MEM_UB, Value(DataType.UWORD, 0x2001)), + Instruction(Opcode.POP_MEM_W, Value(DataType.UWORD, 0x3000)), + Instruction(Opcode.POP_MEM_UW, Value(DataType.UWORD, 0x3002)), Instruction(Opcode.POP_MEM_FLOAT, Value(DataType.UWORD, 0x4000))) vm.load(makeProg(ins), null) - assertEquals(0, vm.mem.getWord(0x2000)) - assertEquals(0, vm.mem.getWord(0x3000)) + assertEquals(0, vm.mem.getUWord(0x2000)) + assertEquals(0, vm.mem.getUWord(0x2001)) + assertEquals(0, vm.mem.getUWord(0x3000)) + assertEquals(0, vm.mem.getUWord(0x3002)) assertEquals(0.0, vm.mem.getFloat(0x4000)) assertThat(vm.evalstack, empty()) - vm.step(6) + vm.step(11) assertThat(vm.evalstack, empty()) - assertEquals(123, vm.mem.getByte(0x2000)) - assertEquals(0x42ea, vm.mem.getWord(0x3000)) + assertEquals(201, vm.mem.getUByte(0x2000)) + assertEquals(177, vm.mem.getUByte(0x2001)) + assertEquals(-55, vm.mem.getSByte(0x2000)) + assertEquals(-79, vm.mem.getSByte(0x2001)) + assertEquals(42080, vm.mem.getUWord(0x3000)) + assertEquals(0xc2ca, vm.mem.getUWord(0x3002)) + assertEquals(-23456, vm.mem.getSWord(0x3000)) + assertEquals(-15670, vm.mem.getSWord(0x3002)) assertEquals(42.25, vm.mem.getFloat(0x4000)) } @@ -418,15 +454,27 @@ class TestStackVmOpcodes { @Test fun testNeg() { - testUnaryOperator(Value(DataType.UBYTE, 12), Opcode.NEG_B, Value(DataType.UBYTE, 244)) - testUnaryOperator(Value(DataType.UWORD, 1234), Opcode.NEG_W, Value(DataType.UWORD, 64302)) + testUnaryOperator(Value(DataType.BYTE, 12), Opcode.NEG_B, Value(DataType.BYTE, -12)) + testUnaryOperator(Value(DataType.WORD, 1234), Opcode.NEG_W, Value(DataType.WORD, -1234)) testUnaryOperator(Value(DataType.FLOAT, 123.456), Opcode.NEG_F, Value(DataType.FLOAT, -123.456)) + assertFailsWith { + testUnaryOperator(Value(DataType.UBYTE, 12), Opcode.NEG_B, Value(DataType.UBYTE, 244)) + } + assertFailsWith { + testUnaryOperator(Value(DataType.UWORD, 1234), Opcode.NEG_W, Value(DataType.UWORD, 64302)) + } } @Test fun testInv() { testUnaryOperator(Value(DataType.UBYTE, 123), Opcode.INV_BYTE, Value(DataType.UBYTE, 0x84)) testUnaryOperator(Value(DataType.UWORD, 4044), Opcode.INV_WORD, Value(DataType.UWORD, 0xf033)) + assertFailsWith { + testUnaryOperator(Value(DataType.BYTE, 123), Opcode.INV_BYTE, Value(DataType.BYTE, 0x84)) + } + assertFailsWith { + testUnaryOperator(Value(DataType.WORD, 4044), Opcode.INV_WORD, Value(DataType.WORD, 0xf033)) + } } @Test @@ -464,13 +512,29 @@ class TestStackVmOpcodes { @Test fun testB2Word() { val ins = mutableListOf( - Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0xea31)), - Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 0x45)), + Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, 0x7a31)), + Instruction(Opcode.PUSH_BYTE, Value(DataType.BYTE, 127)), Instruction(Opcode.B2WORD), Instruction(Opcode.B2WORD) ) vm.load(makeProg(ins), null) vm.step(3) + assertEquals(Value(DataType.WORD, 127), vm.evalstack.pop()) + assertFailsWith { + vm.step(1) + } + } + + @Test + fun testUB2Uword() { + val ins = mutableListOf( + Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0xea31)), + Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 0x45)), + Instruction(Opcode.UB2UWORD), + Instruction(Opcode.UB2UWORD) + ) + vm.load(makeProg(ins), null) + vm.step(3) assertEquals(Value(DataType.UWORD, 0x0045), vm.evalstack.pop()) assertFailsWith { vm.step(1) @@ -496,14 +560,30 @@ class TestStackVmOpcodes { @Test fun testB2Float() { val ins = mutableListOf( - Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0xea31)), - Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 123)), + Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, 0x7a31)), + Instruction(Opcode.PUSH_BYTE, Value(DataType.BYTE, 127)), Instruction(Opcode.B2FLOAT), Instruction(Opcode.B2FLOAT) ) vm.load(makeProg(ins), null) vm.step(3) - assertEquals(Value(DataType.FLOAT, 123.0), vm.evalstack.pop()) + assertEquals(Value(DataType.FLOAT, 127.0), vm.evalstack.pop()) + assertFailsWith { + vm.step(1) + } + } + + @Test + fun testUB2Float() { + val ins = mutableListOf( + Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 0xea31)), + Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)), + Instruction(Opcode.UB2FLOAT), + Instruction(Opcode.UB2FLOAT) + ) + vm.load(makeProg(ins), null) + vm.step(3) + assertEquals(Value(DataType.FLOAT, 177.0), vm.evalstack.pop()) assertFailsWith { vm.step(1) } @@ -512,14 +592,30 @@ class TestStackVmOpcodes { @Test fun testW2Float() { val ins = mutableListOf( - Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 11)), - Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 12345)), + Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)), + Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 52345)), Instruction(Opcode.W2FLOAT), Instruction(Opcode.W2FLOAT) ) vm.load(makeProg(ins), null) vm.step(3) - assertEquals(Value(DataType.FLOAT, 12345.0), vm.evalstack.pop()) + assertEquals(Value(DataType.FLOAT, 52345.0), vm.evalstack.pop()) + assertFailsWith { + vm.step(1) + } + } + + @Test + fun testUW2Float() { + val ins = mutableListOf( + Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, 177)), + Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 52345)), + Instruction(Opcode.UW2FLOAT), + Instruction(Opcode.UW2FLOAT) + ) + vm.load(makeProg(ins), null) + vm.step(3) + assertEquals(Value(DataType.FLOAT, 52345.0), vm.evalstack.pop()) assertFailsWith { vm.step(1) }