From b68f141568cfabc4a779c8975a5923ac6ed14fcc Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 21 Oct 2019 00:11:48 +0200 Subject: [PATCH] some more old code cleanups --- .idea/modules.xml | 4 +- DeprecatedStackVm/src/compiler/Compiler.kt | 2105 -------------- .../src/compiler/intermediate/Instruction.kt | 51 - .../intermediate/IntermediateProgram.kt | 548 ---- .../src/compiler/intermediate/Opcode.kt | 291 -- .../src/prog8/vm/stackvm/Main.kt | 47 - .../src/prog8/vm/stackvm/Program.kt | 302 -- .../src/prog8/vm/stackvm/StackVm.kt | 2438 ----------------- DeprecatedStackVm/test/StackVMOpcodeTests.kt | 1269 --------- .../OldCodeGen.iml | 0 .../src/oldcodegen}/AsmGen.kt | 4 +- .../src/oldcodegen}/AsmPatterns.kt | 5 +- .../src/oldcodegen}/SimpleAsm.kt | 5 +- 13 files changed, 13 insertions(+), 7056 deletions(-) delete mode 100644 DeprecatedStackVm/src/compiler/Compiler.kt delete mode 100644 DeprecatedStackVm/src/compiler/intermediate/Instruction.kt delete mode 100644 DeprecatedStackVm/src/compiler/intermediate/IntermediateProgram.kt delete mode 100644 DeprecatedStackVm/src/compiler/intermediate/Opcode.kt delete mode 100644 DeprecatedStackVm/src/prog8/vm/stackvm/Main.kt delete mode 100644 DeprecatedStackVm/src/prog8/vm/stackvm/Program.kt delete mode 100644 DeprecatedStackVm/src/prog8/vm/stackvm/StackVm.kt delete mode 100644 DeprecatedStackVm/test/StackVMOpcodeTests.kt rename DeprecatedStackVm/DeprecatedStackVm.iml => OldCodeGen/OldCodeGen.iml (100%) rename {DeprecatedStackVm/src/compiler/target/c64/codegen => OldCodeGen/src/oldcodegen}/AsmGen.kt (99%) rename {DeprecatedStackVm/src/compiler/target/c64/codegen => OldCodeGen/src/oldcodegen}/AsmPatterns.kt (99%) rename {DeprecatedStackVm/src/compiler/target/c64/codegen => OldCodeGen/src/oldcodegen}/SimpleAsm.kt (99%) diff --git a/.idea/modules.xml b/.idea/modules.xml index be42de539..122afccff 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,11 +2,11 @@ - + - + \ No newline at end of file diff --git a/DeprecatedStackVm/src/compiler/Compiler.kt b/DeprecatedStackVm/src/compiler/Compiler.kt deleted file mode 100644 index b083e0441..000000000 --- a/DeprecatedStackVm/src/compiler/Compiler.kt +++ /dev/null @@ -1,2105 +0,0 @@ -package prog8.compiler - -import prog8.ast.INameScope -import prog8.ast.Program -import prog8.ast.base.* -import prog8.ast.base.RegisterOrPair.* -import prog8.ast.expressions.* -import prog8.ast.mangledStructMemberName -import prog8.ast.statements.* -import prog8.compiler.intermediate.IntermediateProgram -import prog8.compiler.intermediate.Opcode -import prog8.compiler.intermediate.branchOpcodes -import prog8.functions.BuiltinFunctions -import prog8.parser.tryGetEmbeddedResource -import prog8.vm.RuntimeValue -import prog8.vm.stackvm.Syscall -import java.io.File -import java.nio.file.Path -import kotlin.math.abs - - -class CompilerException(message: String?) : Exception(message) - - -fun Number.toHex(): String { - // 0..15 -> "0".."15" - // 16..255 -> "$10".."$ff" - // 256..65536 -> "$0100".."$ffff" - // negative values are prefixed with '-'. - val integer = this.toInt() - if(integer<0) - return '-' + abs(integer).toHex() - return when (integer) { - in 0 until 16 -> integer.toString() - in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0') - in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0') - else -> throw CompilerException("number too large for 16 bits $this") - } -} - - -data class IntegerOrAddressOf(val integer: Int?, val addressOf: AddressOf?) - - - - -enum class OutputType { - RAW, - PRG -} - -enum class LauncherType { - BASIC, - NONE -} - -enum class ZeropageType { - BASICSAFE, - FLOATSAFE, - KERNALSAFE, - FULL -} - - -data class CompilationOptions(val output: OutputType, - val launcher: LauncherType, - val zeropage: ZeropageType, - val zpReserved: List, - val floats: Boolean) - - -internal class Compiler(private val program: Program) { - - private val prog: IntermediateProgram = IntermediateProgram(program.name, program.definedLoadAddress, program.heap, program.modules.first().source) - private var generatedLabelSequenceNumber = 0 - private val breakStmtLabelStack : Stack = Stack() - private val continueStmtLabelStack : Stack = Stack() - - fun compile(options: CompilationOptions) : IntermediateProgram { - println("Creating stackVM code...") - program.modules.forEach { - it.statements.forEach { stmt-> - if(stmt is Block) - processBlock(stmt) - } - } - return prog - } - - private fun processBlock(block: Block) { - prog.newBlock(block.name, block.address, block.options()) - processVariables(block) - prog.line(block.position) - translate(block.statements) - } - - private fun processVariables(scope: INameScope) { - for(variable in scope.statements.filterIsInstance()) - prog.variable(variable.scopedname, variable) - for(subscope in scope.subScopes()) - processVariables(subscope.value) - } - - private fun translate(statements: List) { - for (stmt: Statement in statements) { - generatedLabelSequenceNumber++ - when (stmt) { - is Label -> translate(stmt) - is Assignment -> translate(stmt) // normal and augmented assignments - is PostIncrDecr -> translate(stmt) - is Jump -> translate(stmt, null) - is FunctionCallStatement -> translate(stmt) - is IfStatement -> translate(stmt) - is BranchStatement -> translate(stmt) - is Break -> translate(stmt) - is Continue -> translate(stmt) - is ForLoop -> translate(stmt) - is WhileLoop -> translate(stmt) - is RepeatLoop -> translate(stmt) - is AnonymousScope -> translate(stmt) - is ReturnFromIrq -> translate(stmt) - is Return -> translate(stmt) - is Directive -> { - when(stmt.directive) { - "%asminclude" -> translateAsmInclude(stmt.args, prog.source) - "%asmbinary" -> translateAsmBinary(stmt.args) - "%breakpoint" -> { - prog.line(stmt.position) - prog.instr(Opcode.BREAKPOINT) - } - } - } - is VarDecl -> {} // skip this, already processed these. - is Subroutine -> translate(stmt) - is NopStatement -> {} - is InlineAssembly -> translate(stmt) - is WhenStatement -> translate(stmt) - is StructDecl -> {} - else -> TODO("translate statement $stmt to stackvm") - } - } - } - - private fun translate(subroutine: Subroutine) { - if(subroutine.asmAddress==null) { - prog.label(subroutine.scopedname, true) - prog.instr(Opcode.START_PROCDEF) - prog.line(subroutine.position) - // note: the caller has already written the arguments into the subroutine's parameter variables. - // note2: don't separate normal and VariableInitializationAssignment here, because the order strictly matters - translate(subroutine.statements) - prog.instr(Opcode.END_PROCDEF) - } else { - // asmsub - if(subroutine.containsCodeOrVars()) - throw CompilerException("kernel subroutines (with memory address) can't have a body: $subroutine") - - prog.memoryPointer(subroutine.scopedname, subroutine.asmAddress, DataType.UBYTE) // the datatype is a bit of a dummy in this case - } - } - private fun opcodePush(dt: DataType): Opcode { - return when (dt) { - in ByteDatatypes -> Opcode.PUSH_BYTE - in WordDatatypes -> Opcode.PUSH_WORD - in IterableDatatypes -> Opcode.PUSH_WORD - DataType.FLOAT -> Opcode.PUSH_FLOAT - else -> throw CompilerException("invalid dt $dt") - } - } - - private fun opcodeAdd(dt: DataType): Opcode { - return when (dt) { - DataType.UBYTE -> Opcode.ADD_UB - DataType.BYTE -> Opcode.ADD_B - DataType.UWORD -> Opcode.ADD_UW - DataType.WORD -> Opcode.ADD_W - DataType.FLOAT -> Opcode.ADD_F - else -> throw CompilerException("invalid dt $dt") - } - } - - private fun opcodeSub(dt: DataType): Opcode { - return when (dt) { - DataType.UBYTE -> Opcode.SUB_UB - DataType.BYTE -> Opcode.SUB_B - DataType.UWORD -> Opcode.SUB_UW - DataType.WORD -> Opcode.SUB_W - DataType.FLOAT -> Opcode.SUB_F - else -> throw CompilerException("invalid dt $dt") - } - } - - private fun opcodeCompare(dt: DataType): Opcode { - return when (dt) { - DataType.UBYTE -> Opcode.CMP_UB - DataType.BYTE -> Opcode.CMP_B - DataType.UWORD -> Opcode.CMP_UW - DataType.WORD -> Opcode.CMP_W - else -> throw CompilerException("invalid dt $dt") - } - } - - private fun opcodePushvar(dt: DataType): Opcode { - return when (dt) { - in ByteDatatypes -> Opcode.PUSH_VAR_BYTE - in WordDatatypes -> Opcode.PUSH_VAR_WORD - in IterableDatatypes -> Opcode.PUSH_ADDR_HEAPVAR - DataType.FLOAT -> Opcode.PUSH_VAR_FLOAT - else -> throw CompilerException("invalid dt $dt") - } - } - - private fun opcodeReadindexedvar(dt: DataType): Opcode { - return when (dt) { - DataType.ARRAY_UB, DataType.ARRAY_B -> Opcode.READ_INDEXED_VAR_BYTE - DataType.ARRAY_UW, DataType.ARRAY_W -> Opcode.READ_INDEXED_VAR_WORD - DataType.ARRAY_F -> Opcode.READ_INDEXED_VAR_FLOAT - DataType.STR, DataType.STR_S -> Opcode.READ_INDEXED_VAR_BYTE - else -> throw CompilerException("invalid dt for indexed access $dt") - } - } - - private fun opcodeWriteindexedvar(dt: DataType): Opcode { - return when (dt) { - DataType.ARRAY_UB, DataType.ARRAY_B -> Opcode.WRITE_INDEXED_VAR_BYTE - DataType.ARRAY_UW, DataType.ARRAY_W -> Opcode.WRITE_INDEXED_VAR_WORD - DataType.ARRAY_F -> Opcode.WRITE_INDEXED_VAR_FLOAT - DataType.STR, DataType.STR_S -> Opcode.WRITE_INDEXED_VAR_BYTE - else -> throw CompilerException("invalid dt for indexed access $dt") - } - } - - private fun opcodeDiscard(dt: DataType): Opcode { - return when(dt) { - in ByteDatatypes -> Opcode.DISCARD_BYTE - in WordDatatypes -> Opcode.DISCARD_WORD - in IterableDatatypes -> Opcode.DISCARD_WORD - DataType.FLOAT -> Opcode.DISCARD_FLOAT - else -> throw CompilerException("invalid dt $dt") - } - } - - private fun opcodePopvar(dt: DataType): Opcode { - return when (dt) { - in ByteDatatypes -> Opcode.POP_VAR_BYTE - in WordDatatypes -> Opcode.POP_VAR_WORD - in IterableDatatypes -> Opcode.POP_VAR_WORD - DataType.FLOAT -> Opcode.POP_VAR_FLOAT - else -> throw CompilerException("invalid dt $dt") - } - } - - private fun opcodePopmem(dt: DataType): Opcode { - return when (dt) { - in ByteDatatypes -> Opcode.POP_MEM_BYTE - in WordDatatypes -> Opcode.POP_MEM_WORD - in IterableDatatypes -> Opcode.POP_MEM_WORD - DataType.FLOAT -> Opcode.POP_MEM_FLOAT - else -> throw CompilerException("invalid dt $dt") - } - } - - private fun opcodeDecvar(dt: DataType): Opcode { - return when(dt) { - DataType.UBYTE -> Opcode.DEC_VAR_UB - DataType.BYTE -> Opcode.DEC_VAR_B - DataType.UWORD -> Opcode.DEC_VAR_UW - DataType.WORD -> Opcode.DEC_VAR_W - DataType.FLOAT -> Opcode.DEC_VAR_F - else -> throw CompilerException("can't dec type $dt") - } - } - - private fun opcodeIncvar(dt: DataType): Opcode { - return when(dt) { - DataType.UBYTE -> Opcode.INC_VAR_UB - DataType.BYTE -> Opcode.INC_VAR_B - DataType.UWORD -> Opcode.INC_VAR_UW - DataType.WORD -> Opcode.INC_VAR_W - DataType.FLOAT -> Opcode.INC_VAR_F - else -> throw CompilerException("can't inc type $dt") - } - } - - private fun opcodeIncArrayindexedVar(dt: DataType): Opcode { - return when(dt) { - DataType.ARRAY_UB -> Opcode.INC_INDEXED_VAR_UB - DataType.ARRAY_B -> Opcode.INC_INDEXED_VAR_B - DataType.ARRAY_UW -> Opcode.INC_INDEXED_VAR_UW - DataType.ARRAY_W -> Opcode.INC_INDEXED_VAR_W - DataType.ARRAY_F -> Opcode.INC_INDEXED_VAR_FLOAT - else -> throw CompilerException("can't inc type $dt") - } - } - - private fun opcodeDecArrayindexedVar(dt: DataType): Opcode { - return when(dt) { - DataType.ARRAY_UB -> Opcode.DEC_INDEXED_VAR_UB - DataType.ARRAY_B -> Opcode.DEC_INDEXED_VAR_B - DataType.ARRAY_UW -> Opcode.DEC_INDEXED_VAR_UW - DataType.ARRAY_W -> Opcode.DEC_INDEXED_VAR_W - DataType.ARRAY_F -> Opcode.DEC_INDEXED_VAR_FLOAT - else -> throw CompilerException("can't dec type $dt") - } - } - - private fun translate(stmt: InlineAssembly) { - // If the inline assembly is the only statement inside a subroutine (except vardecls), - // we can use the name of that subroutine to identify it. - // The compiler could then convert it to a special system call - val sub = stmt.parent as? Subroutine - val scopename = - if(sub!=null && sub.statements.filter{it !is VarDecl }.size==1) - sub.scopedname - else - null - prog.instr(Opcode.INLINE_ASSEMBLY, callLabel=scopename, callLabel2 = stmt.assembly) - } - - private fun translate(stmt: Continue) { - prog.line(stmt.position) - if(continueStmtLabelStack.empty()) - throw CompilerException("continue outside of loop statement block") - val label = continueStmtLabelStack.peek() - prog.instr(Opcode.JUMP, callLabel = label) - } - - private fun translate(stmt: Break) { - prog.line(stmt.position) - if(breakStmtLabelStack.empty()) - throw CompilerException("break outside of loop statement block") - val label = breakStmtLabelStack.peek() - prog.instr(Opcode.JUMP, callLabel = label) - } - - private fun translate(branch: BranchStatement) { - /* - * A branch: IF_CC { stuff } else { other_stuff } - * Which is translated into: - * BCS _stmt_999_else - * stuff - * JUMP _stmt_999_end - * _stmt_999_else: - * other_stuff ;; optional - * _stmt_999_end: - * nop - * - * if the branch statement just contains jumps, more efficient code is generated. - * (just the appropriate branching instruction is outputted!) - */ - if(branch.elsepart.containsNoCodeNorVars() && branch.truepart.containsNoCodeNorVars()) - return - - fun branchOpcode(branch: BranchStatement, complement: Boolean) = - if(complement) { - when (branch.condition) { - BranchCondition.CS -> Opcode.BCC - BranchCondition.CC -> Opcode.BCS - BranchCondition.EQ, BranchCondition.Z -> Opcode.BNZ - BranchCondition.NE, BranchCondition.NZ -> Opcode.BZ - BranchCondition.VS -> Opcode.BVC - BranchCondition.VC -> Opcode.BVS - BranchCondition.MI, BranchCondition.NEG -> Opcode.BPOS - BranchCondition.PL, BranchCondition.POS -> Opcode.BNEG - } - } else { - when (branch.condition) { - BranchCondition.CS -> Opcode.BCS - BranchCondition.CC -> Opcode.BCC - BranchCondition.EQ, BranchCondition.Z -> Opcode.BZ - BranchCondition.NE, BranchCondition.NZ -> Opcode.BNZ - BranchCondition.VS -> Opcode.BVS - BranchCondition.VC -> Opcode.BVC - BranchCondition.MI, BranchCondition.NEG -> Opcode.BNEG - BranchCondition.PL, BranchCondition.POS -> Opcode.BPOS - } - } - - prog.line(branch.position) - val truejump = branch.truepart.statements.first() - val elsejump = branch.elsepart.statements.firstOrNull() - if(truejump is Jump && truejump.address==null && (elsejump ==null || (elsejump is Jump && elsejump.address==null))) { - // optimized code for just conditional jumping - val opcodeTrue = branchOpcode(branch, false) - translate(truejump, opcodeTrue) - if(elsejump is Jump) { - val opcodeFalse = branchOpcode(branch, true) - translate(elsejump, opcodeFalse) - } - } else { - // regular if..else branching - val labelElse = makeLabel(branch, "else") - val labelEnd = makeLabel(branch, "end") - val opcode = branchOpcode(branch, true) - if (branch.elsepart.containsNoCodeNorVars()) { - prog.instr(opcode, callLabel = labelEnd) - translate(branch.truepart) - prog.label(labelEnd) - } else { - prog.instr(opcode, callLabel = labelElse) - translate(branch.truepart) - prog.instr(Opcode.JUMP, callLabel = labelEnd) - prog.label(labelElse) - translate(branch.elsepart) - prog.label(labelEnd) - } - prog.instr(Opcode.NOP) - } - } - - private fun makeLabel(scopeStmt: Statement, postfix: String): String { - generatedLabelSequenceNumber++ - return "${scopeStmt.makeScopedName("")}." - } - - private fun translate(stmt: IfStatement) { - /* - * An IF statement: IF (condition-expression) { stuff } else { other_stuff } - * Which is translated into: - * - * JZ/JZW _stmt_999_else - * stuff - * JUMP _stmt_999_end - * _stmt_999_else: - * other_stuff ;; optional - * _stmt_999_end: - * nop - * - * or when there is no else block: - * - * JZ/JZW _stmt_999_end - * stuff - * _stmt_999_end: - * nop - * - * For if statements with goto's, more efficient code is generated. - */ - prog.line(stmt.position) - translate(stmt.condition) - - val trueGoto = stmt.truepart.statements.singleOrNull() as? Jump - if(trueGoto!=null) { - // optimization for if (condition) goto .... - val conditionJumpOpcode = when(stmt.condition.inferType(program)) { - in ByteDatatypes -> Opcode.JNZ - in WordDatatypes -> Opcode.JNZW - else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt") - } - translate(trueGoto, conditionJumpOpcode) - translate(stmt.elsepart) - return - } - - val conditionJumpOpcode = when(stmt.condition.inferType(program)) { - in ByteDatatypes -> Opcode.JZ - in WordDatatypes -> Opcode.JZW - else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt") - } - val labelEnd = makeLabel(stmt, "end") - if(stmt.elsepart.containsNoCodeNorVars()) { - prog.instr(conditionJumpOpcode, callLabel = labelEnd) - translate(stmt.truepart) - prog.label(labelEnd) - } else { - val labelElse = makeLabel(stmt, "else") - prog.instr(conditionJumpOpcode, callLabel = labelElse) - translate(stmt.truepart) - prog.instr(Opcode.JUMP, callLabel = labelEnd) - prog.label(labelElse) - translate(stmt.elsepart) - prog.label(labelEnd) - } - prog.instr(Opcode.NOP) - } - - private fun translate(expr: Expression) { - when(expr) { - is RegisterExpr -> { - prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = expr.register.name) - } - is PrefixExpression -> { - translate(expr.expression) - translatePrefixOperator(expr.operator, expr.expression.inferType(program)) - } - is BinaryExpression -> { - val leftDt = expr.left.inferType(program) - val rightDt = expr.right.inferType(program) - val (commonDt, _) = expr.commonDatatype(leftDt, rightDt, expr.left, expr.right) - translate(expr.left) - if(leftDt!=commonDt) - convertType(leftDt, commonDt) - translate(expr.right) - if(rightDt!=commonDt) - convertType(rightDt, commonDt) - if(expr.operator=="<<" || expr.operator==">>") - translateBitshiftedOperator(expr.operator, leftDt, expr.right.constValue(program)) - else - translateBinaryOperator(expr.operator, commonDt) - } - is FunctionCall -> { - val target = expr.target.targetStatement(program.namespace) - if(target is BuiltinFunctionStatementPlaceholder) { - // call to a builtin function (some will just be an opcode!) - val funcname = expr.target.nameInSource[0] - translateBuiltinFunctionCall(funcname, expr.arglist) - } else { - if (target is Subroutine) translateSubroutineCall(target, expr.arglist, expr.position) - else TODO("non-builtin-function call to $target") - } - } - is IdentifierReference -> translate(expr) - is ArrayIndexedExpression -> translate(expr, false) - is RangeExpr -> throw CompilerException("it's not possible to just have a range expression that has to be translated") - is TypecastExpression -> translate(expr) - is DirectMemoryRead -> translate(expr) - is AddressOf -> translate(expr) - is StructLiteralValue -> throw CompilerException("a struct Lv should have been flattened as assignments") - else -> { - val lv = expr.constValue(program) ?: throw CompilerException("constant expression required, not $expr") - when(lv.type) { - in ByteDatatypes -> prog.instr(Opcode.PUSH_BYTE, RuntimeValue(lv.type, lv.number.toShort())) - in WordDatatypes -> prog.instr(Opcode.PUSH_WORD, RuntimeValue(lv.type, lv.number.toInt())) - DataType.FLOAT -> prog.instr(Opcode.PUSH_FLOAT, RuntimeValue(lv.type, lv.number.toDouble())) - // TODO what about these ref types: -// in StringDatatypes -> { -// if(lv.heapId==null) -// throw CompilerException("string should have been moved into heap ${lv.position}") -// TODO("push address of string with PUSH_ADDR_HEAPVAR") -// } -// in ArrayDatatypes -> { -// if(lv.heapId==null) -// throw CompilerException("array should have been moved into heap ${lv.position}") -// TODO("push address of array with PUSH_ADDR_HEAPVAR") -// } - else -> throw CompilerException("weird datatype") - } - } - } - } - - - private fun tryConvertType(givenDt: DataType, targetDt: DataType): Boolean { - return try { - convertType(givenDt, targetDt) - true - } catch (x: CompilerException) { - false - } - } - - - private fun convertType(givenDt: DataType, targetDt: DataType) { - // only WIDENS a type, never NARROWS. To avoid loss of precision. - if(givenDt==targetDt) - return - if(givenDt !in NumericDatatypes) - throw CompilerException("converting non-numeric $givenDt") - if(targetDt !in NumericDatatypes) - throw CompilerException("converting $givenDt to non-numeric $targetDt") - when(givenDt) { - DataType.UBYTE -> when(targetDt) { - DataType.UWORD -> prog.instr(Opcode.CAST_UB_TO_UW) - DataType.WORD -> prog.instr(Opcode.CAST_UB_TO_W) - DataType.FLOAT -> prog.instr(Opcode.CAST_UB_TO_F) - else -> {} - } - DataType.BYTE -> when(targetDt) { - DataType.UWORD -> prog.instr(Opcode.CAST_B_TO_UW) - DataType.WORD -> prog.instr(Opcode.CAST_B_TO_W) - DataType.FLOAT -> prog.instr(Opcode.CAST_B_TO_F) - else -> {} - } - DataType.UWORD -> when(targetDt) { - in ByteDatatypes -> throw CompilerException("narrowing type") - DataType.FLOAT -> prog.instr(Opcode.CAST_UW_TO_F) - else -> {} - } - DataType.WORD -> when(targetDt) { - in ByteDatatypes -> throw CompilerException("narrowing type") - DataType.FLOAT -> prog.instr(Opcode.CAST_W_TO_F) - else -> {} - } - DataType.FLOAT -> if(targetDt in IntegerDatatypes) throw CompilerException("narrowing type") - else -> {} - } - } - - private fun translate(identifierRef: IdentifierReference) { - val target = identifierRef.targetStatement(program.namespace) - when (target) { - is VarDecl -> { - when (target.type) { - VarDeclType.VAR -> { - val opcode = opcodePushvar(target.datatype) - prog.instr(opcode, callLabel = target.scopedname) - } - VarDeclType.CONST -> - throw CompilerException("const ref should have been const-folded away") - VarDeclType.MEMORY -> { - when (target.datatype) { - DataType.UBYTE -> prog.instr(Opcode.PUSH_MEM_UB, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number)) - DataType.BYTE-> prog.instr(Opcode.PUSH_MEM_B, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number)) - DataType.UWORD -> prog.instr(Opcode.PUSH_MEM_UW, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number)) - DataType.WORD -> prog.instr(Opcode.PUSH_MEM_W, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number)) - DataType.FLOAT -> prog.instr(Opcode.PUSH_MEM_FLOAT, RuntimeValue(DataType.UWORD, (target.value as NumericLiteralValue).number)) - else -> throw CompilerException("invalid datatype for memory variable expression: $target") - } - } - } - - } - else -> throw CompilerException("expression identifierref should be a vardef, not $target") - } - } - - private fun translate(stmt: FunctionCallStatement) { - prog.line(stmt.position) - val targetStmt = stmt.target.targetStatement(program.namespace)!! - if(targetStmt is BuiltinFunctionStatementPlaceholder) { - val funcname = stmt.target.nameInSource[0] - translateBuiltinFunctionCall(funcname, stmt.arglist) - return - } - - when(targetStmt) { - is Label -> - prog.instr(Opcode.CALL, callLabel = targetStmt.scopedname) - is Subroutine -> { - translateSubroutineCall(targetStmt, stmt.arglist, stmt.position) - // make sure we clean up the unused result values from the stack - for(rv in targetStmt.returntypes) { - val opcode=opcodeDiscard(rv) - prog.instr(opcode) - } - } - else -> - throw AstException("invalid call target node type: ${targetStmt::class}") - } - } - - private fun translateBuiltinFunctionCall(funcname: String, args: List) { - // some builtin functions are implemented directly as vm opcodes - - if(funcname == "swap") { - translateSwap(args) - return - } - - val builtinFuncParams = BuiltinFunctions[funcname]?.parameters - args.forEachIndexed { index, arg -> - // place function argument(s) on the stack - translate(arg) - // cast type if needed - if(builtinFuncParams!=null) { - val paramDts = builtinFuncParams[index].possibleDatatypes - val argDt = arg.inferType(program) - if(argDt !in paramDts) { - for(paramDt in paramDts.sorted()) - if(tryConvertType(argDt, paramDt)) - break - } - } - } - - when (funcname) { - "len" -> { - // 1 argument, type determines the exact syscall to use - val arg=args.single() - when (arg.inferType(program)) { - DataType.STR, DataType.STR_S -> createSyscall("${funcname}_str") - in ArrayDatatypes -> throw CompilerException("len() of an array type should have been const-folded") - else -> throw CompilerException("wrong datatype for len() $arg") - } - } - "any", "all" -> { - // 1 array argument, type determines the exact syscall to use - val arg=args.single() as IdentifierReference - val target=arg.targetVarDecl(program.namespace)!! - val length= RuntimeValue(DataType.UBYTE, target.arraysize!!.size()!!) - prog.instr(Opcode.PUSH_BYTE, length) - when (arg.inferType(program)) { - DataType.ARRAY_B, DataType.ARRAY_UB -> createSyscall("${funcname}_b") - DataType.ARRAY_W, DataType.ARRAY_UW -> createSyscall("${funcname}_w") - DataType.ARRAY_F -> createSyscall("${funcname}_f") - else -> throw CompilerException("wrong datatype for $funcname()") - } - } - "avg" -> { - // 1 array argument, type determines the exact syscall to use - val arg=args.single() as IdentifierReference - val target=arg.targetVarDecl(program.namespace)!! - val length= RuntimeValue(DataType.UBYTE, target.arraysize!!.size()!!) - val arrayDt=arg.inferType(program) - prog.instr(Opcode.PUSH_BYTE, length) - when (arrayDt) { - DataType.ARRAY_UB -> { - createSyscall("sum_ub") - prog.instr(Opcode.CAST_UW_TO_F) // result of sum(ubyte) is uword, so cast - } - DataType.ARRAY_B -> { - createSyscall("sum_b") - prog.instr(Opcode.CAST_W_TO_F) // result of sum(byte) is word, so cast - } - DataType.ARRAY_UW -> { - createSyscall("sum_uw") - prog.instr(Opcode.CAST_UW_TO_F) // result of sum(uword) is uword, so cast - } - DataType.ARRAY_W -> { - createSyscall("sum_w") - prog.instr(Opcode.CAST_W_TO_F) // result of sum(word) is word, so cast - } - DataType.ARRAY_F -> createSyscall("sum_f") - else -> throw CompilerException("wrong datatype for avg") - } - // divide by the number of elements - prog.instr(opcodePush(DataType.FLOAT), RuntimeValue(DataType.FLOAT, length.numericValue())) - prog.instr(Opcode.DIV_F) - } - "min", "max", "sum" -> { - // 1 array argument, type determines the exact syscall to use - val arg=args.single() as IdentifierReference - val target=arg.targetVarDecl(program.namespace)!! - val length= RuntimeValue(DataType.UBYTE, target.arraysize!!.size()!!) - prog.instr(Opcode.PUSH_BYTE, length) - when (arg.inferType(program)) { - DataType.ARRAY_UB -> createSyscall("${funcname}_ub") - DataType.ARRAY_B -> createSyscall("${funcname}_b") - DataType.ARRAY_UW -> createSyscall("${funcname}_uw") - DataType.ARRAY_W -> createSyscall("${funcname}_w") - DataType.ARRAY_F -> createSyscall("${funcname}_f") - else -> throw CompilerException("wrong datatype for $funcname()") - } - } - "abs" -> { - // 1 argument, type determines the exact opcode to use - val arg = args.single() - when (arg.inferType(program)) { - DataType.UBYTE, DataType.UWORD -> {} - DataType.BYTE -> prog.instr(Opcode.ABS_B) - DataType.WORD -> prog.instr(Opcode.ABS_W) - DataType.FLOAT -> prog.instr(Opcode.ABS_F) - else -> throw CompilerException("wrong datatype for $funcname()") - } - } - "msb" -> prog.instr(Opcode.MSB) // note: "lsb" is not a function at all, it's just an alias for the cast "... as ubyte" - "mkword" -> prog.instr(Opcode.MKWORD) - "lsl" -> { - val arg = args.single() - val dt = arg.inferType(program) - when (dt) { - in ByteDatatypes -> prog.instr(Opcode.SHL_BYTE) - in WordDatatypes -> prog.instr(Opcode.SHL_WORD) - else -> throw CompilerException("wrong datatype") - } - // this function doesn't return a value on the stack so we pop it directly into the argument register/variable again - popValueIntoTarget(AssignTarget.fromExpr(arg), dt!!) - } - "lsr" -> { - val arg = args.single() - val dt = arg.inferType(program) - when (dt) { - DataType.UBYTE -> prog.instr(Opcode.SHR_UBYTE) - DataType.BYTE -> prog.instr(Opcode.SHR_SBYTE) - DataType.UWORD -> prog.instr(Opcode.SHR_UWORD) - DataType.WORD -> prog.instr(Opcode.SHR_SWORD) - else -> throw CompilerException("wrong datatype") - } - // this function doesn't return a value on the stack so we pop it directly into the argument register/variable again - popValueIntoTarget(AssignTarget.fromExpr(arg), dt) - } - "rol" -> { - val arg = args.single() - val dt = arg.inferType(program) - when (dt) { - in ByteDatatypes -> prog.instr(Opcode.ROL_BYTE) - in WordDatatypes -> prog.instr(Opcode.ROL_WORD) - else -> throw CompilerException("wrong datatype") - } - // this function doesn't return a value on the stack so we pop it directly into the argument register/variable again - popValueIntoTarget(AssignTarget.fromExpr(arg), dt!!) - } - "ror" -> { - val arg = args.single() - val dt = arg.inferType(program) - when (dt) { - in ByteDatatypes -> prog.instr(Opcode.ROR_BYTE) - in WordDatatypes -> prog.instr(Opcode.ROR_WORD) - else -> throw CompilerException("wrong datatype") - } - // this function doesn't return a value on the stack so we pop it directly into the argument register/variable again - popValueIntoTarget(AssignTarget.fromExpr(arg), dt!!) - } - "rol2" -> { - val arg = args.single() - val dt = arg.inferType(program) - when (dt) { - in ByteDatatypes -> prog.instr(Opcode.ROL2_BYTE) - in WordDatatypes -> prog.instr(Opcode.ROL2_WORD) - else -> throw CompilerException("wrong datatype") - } - // this function doesn't return a value on the stack so we pop it directly into the argument register/variable again - popValueIntoTarget(AssignTarget.fromExpr(arg), dt!!) - } - "ror2" -> { - val arg = args.single() - val dt = arg.inferType(program) - when (dt) { - in ByteDatatypes -> prog.instr(Opcode.ROR2_BYTE) - in WordDatatypes -> prog.instr(Opcode.ROR2_WORD) - else -> throw CompilerException("wrong datatype") - } - // this function doesn't return a value on the stack so we pop it directly into the argument register/variable again - popValueIntoTarget(AssignTarget.fromExpr(arg), dt!!) - } - "set_carry" -> prog.instr(Opcode.SEC) - "clear_carry" -> prog.instr(Opcode.CLC) - "set_irqd" -> prog.instr(Opcode.SEI) - "clear_irqd" -> prog.instr(Opcode.CLI) - "rsave" -> prog.instr(Opcode.RSAVE) - "rrestore" -> prog.instr(Opcode.RRESTORE) - else -> createSyscall(funcname) // call builtin function - } - } - - private fun translateSwap(args: List) { - // swap(x,y) is treated differently, it's not a normal function call - if (args.size != 2) - throw AstException("swap requires 2 arguments") - val dt1 = args[0].inferType(program) - val dt2 = args[1].inferType(program) - if (dt1 != dt2) - throw AstException("swap requires 2 args of identical type") - if (args[0].constValue(program) != null || args[1].constValue(program) != null) - throw AstException("swap requires 2 variables, not constant value(s)") - if(args[0] isSameAs args[1]) - throw AstException("swap should have 2 different args") - if(dt1 !in NumericDatatypes) - throw AstException("swap requires args of numerical type") - - translate(args[0]) - translate(args[1]) - // pop in reverse order - popValueIntoTarget(AssignTarget.fromExpr(args[0]), dt1) - popValueIntoTarget(AssignTarget.fromExpr(args[1]), dt2) - return - } - - private fun translateSubroutineCall(subroutine: Subroutine, arguments: List, callPosition: Position) { - // evaluate the arguments and assign them into the subroutine's argument variables. - var restoreX = Register.X in subroutine.asmClobbers - if(restoreX) - prog.instr(Opcode.RSAVEX) - // We don't bother about saving A and Y. They're considered expendable. - - if(subroutine.isAsmSubroutine) { - restoreX = translateAsmSubCallArguments(subroutine, arguments, callPosition, restoreX) - } else { - // only regular (non-register) arguments - // "assign" the arguments to the locally scoped parameter variables for this subroutine - // (subroutine arguments are not passed via the stack!) - for (arg in arguments.zip(subroutine.parameters)) { - translate(arg.first) - convertType(arg.first.inferType(program), arg.second.type) // convert types of arguments to required parameter type - val opcode = opcodePopvar(arg.second.type) - prog.instr(opcode, callLabel = subroutine.scopedname + "." + arg.second.name) - } - } - prog.instr(Opcode.CALL, callLabel = subroutine.scopedname) - if(restoreX) - prog.instr(Opcode.RRESTOREX) - - if(subroutine.isAsmSubroutine && subroutine.asmReturnvaluesRegisters.isNotEmpty()) { - // the result values of the asm-subroutine that are returned in registers, have to be pushed on the stack - // (in reversed order) otherwise the asm-subroutine can't be used in expressions. - for(rv in subroutine.asmReturnvaluesRegisters.reversed()) { - if(rv.statusflag!=null) { - if (rv.statusflag == Statusflag.Pc) { - prog.instr(Opcode.CARRY_TO_A) - prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = Register.A.name) - } - else TODO("return value in cpu status flag only supports Carry, not $rv ($subroutine)") - } else { - when (rv.registerOrPair) { - A, X, Y -> prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = rv.registerOrPair.name) - AX -> prog.instr(Opcode.PUSH_REGAX_WORD) - AY -> prog.instr(Opcode.PUSH_REGAY_WORD) - XY -> prog.instr(Opcode.PUSH_REGXY_WORD) - null -> {} - } - } - } - } - } - - private fun translateAsmSubCallArguments(subroutine: Subroutine, arguments: List, callPosition: Position, restoreXinitial: Boolean): Boolean { - var restoreX = restoreXinitial - if (subroutine.parameters.size != subroutine.asmParameterRegisters.size) - TODO("no support yet for mix of register and non-register subroutine arguments") - - // only register arguments (or status-flag bits) - var carryParam: Boolean? = null - for (arg in arguments.zip(subroutine.asmParameterRegisters)) { - if (arg.second.statusflag != null) { - if (arg.second.statusflag == Statusflag.Pc) - carryParam = arg.first.constValue(program)!!.asBooleanValue - else - throw CompilerException("no support for status flag parameter: ${arg.second.statusflag}") - } else { - when (arg.second.registerOrPair!!) { - A -> { - val assign = Assignment(AssignTarget(Register.A, null, null, null, callPosition), null, arg.first, callPosition) - assign.linkParents(arguments[0].parent) - translate(assign) - } - X -> { - if (!restoreX) { - prog.instr(Opcode.RSAVEX) - restoreX = true - } - val assign = Assignment(AssignTarget(Register.X, null, null, null, callPosition), null, arg.first, callPosition) - assign.linkParents(arguments[0].parent) - translate(assign) - } - Y -> { - val assign = Assignment(AssignTarget(Register.Y, null, null, null, callPosition), null, arg.first, callPosition) - assign.linkParents(arguments[0].parent) - translate(assign) - } - AX -> { - if (!restoreX) { - prog.instr(Opcode.RSAVEX) - restoreX = true - } - val valueA: Expression - val valueX: Expression - val paramDt = arg.first.inferType(program) - when (paramDt) { - DataType.UBYTE -> { - valueA = arg.first - valueX = NumericLiteralValue.optimalInteger(0, callPosition) - val assignA = Assignment(AssignTarget(Register.A, null, null, null, callPosition), null, valueA, callPosition) - val assignX = Assignment(AssignTarget(Register.X, null, null, null, callPosition), null, valueX, callPosition) - assignA.linkParents(arguments[0].parent) - assignX.linkParents(arguments[0].parent) - translate(assignA) - translate(assignX) - } - in WordDatatypes -> { - translate(arg.first) - prog.instr(Opcode.POP_REGAX_WORD) - } - in StringDatatypes + ArrayDatatypes -> throw CompilerException("string or array arguments should have been converted to their pointer value in the Ast $callPosition") - else -> TODO("pass parameter of type $paramDt in registers AX at $callPosition") - } - } - AY -> { - val valueA: Expression - val valueY: Expression - val paramDt = arg.first.inferType(program) - when (paramDt) { - DataType.UBYTE -> { - valueA = arg.first - valueY = NumericLiteralValue.optimalInteger(0, callPosition) - val assignA = Assignment(AssignTarget(Register.A, null, null, null, callPosition), null, valueA, callPosition) - val assignY = Assignment(AssignTarget(Register.Y, null, null, null, callPosition), null, valueY, callPosition) - assignA.linkParents(arguments[0].parent) - assignY.linkParents(arguments[0].parent) - translate(assignA) - translate(assignY) - } - in WordDatatypes -> { - translate(arg.first) - prog.instr(Opcode.POP_REGAY_WORD) - } - in StringDatatypes + ArrayDatatypes -> throw CompilerException("string or array arguments should have been converted to their pointer value in the Ast $callPosition") - else -> TODO("pass parameter of type $paramDt in registers AY at $callPosition") - } - } - XY -> { - if (!restoreX) { - prog.instr(Opcode.RSAVEX) - restoreX = true - } - val valueX: Expression - val valueY: Expression - val paramDt = arg.first.inferType(program) - when (paramDt) { - DataType.UBYTE -> { - valueX = arg.first - valueY = NumericLiteralValue.optimalInteger(0, callPosition) - val assignX = Assignment(AssignTarget(Register.X, null, null, null, callPosition), null, valueX, callPosition) - val assignY = Assignment(AssignTarget(Register.Y, null, null, null, callPosition), null, valueY, callPosition) - assignX.linkParents(arguments[0].parent) - assignY.linkParents(arguments[0].parent) - translate(assignX) - translate(assignY) - } - in WordDatatypes -> { - translate(arg.first) - prog.instr(Opcode.POP_REGXY_WORD) - } - in StringDatatypes + ArrayDatatypes -> throw CompilerException("string or array arguments should have been converted to their pointer value in the Ast $callPosition") - else -> TODO("pass parameter of type $paramDt in registers XY at $callPosition") - } - } - } - } - } - - // carry is set last, to avoid clobbering it when loading the other parameters - when (carryParam) { - true -> prog.instr(Opcode.SEC) - false -> prog.instr(Opcode.CLC) - } - return restoreX - } - - private fun translateBinaryOperator(operator: String, dt: DataType) { - if(dt !in NumericDatatypes) - throw CompilerException("non-numeric datatype for operator: $dt") - val opcode = when(operator) { - "+" -> { - when(dt) { - DataType.UBYTE -> Opcode.ADD_UB - DataType.BYTE -> Opcode.ADD_B - DataType.UWORD -> Opcode.ADD_UW - DataType.WORD -> Opcode.ADD_W - DataType.FLOAT -> Opcode.ADD_F - else -> throw CompilerException("only byte/word/float possible") - } - } - "-" -> { - when(dt) { - DataType.UBYTE -> Opcode.SUB_UB - DataType.BYTE -> Opcode.SUB_B - DataType.UWORD -> Opcode.SUB_UW - DataType.WORD -> Opcode.SUB_W - DataType.FLOAT -> Opcode.SUB_F - else -> throw CompilerException("only byte/word/float possible") - } - } - "*" -> { - when(dt) { - DataType.UBYTE -> Opcode.MUL_UB - DataType.BYTE -> Opcode.MUL_B - DataType.UWORD -> Opcode.MUL_UW - DataType.WORD -> Opcode.MUL_W - DataType.FLOAT -> Opcode.MUL_F - else -> throw CompilerException("only byte/word/float possible") - } - } - "/" -> { - when(dt) { - DataType.UBYTE -> Opcode.IDIV_UB - DataType.BYTE -> Opcode.IDIV_B - DataType.UWORD -> Opcode.IDIV_UW - DataType.WORD -> Opcode.IDIV_W - DataType.FLOAT -> Opcode.DIV_F - else -> throw CompilerException("only byte/word/float possible") - } - } - "%" -> { - when(dt) { - DataType.UBYTE -> Opcode.REMAINDER_UB - DataType.UWORD -> Opcode.REMAINDER_UW - DataType.BYTE, DataType.WORD -> throw CompilerException("remainder of signed integers is not properly defined/implemented, use unsigned instead") - else -> throw CompilerException("only byte/word operands possible") - } - } - "**" -> { - when(dt) { - in IntegerDatatypes -> throw CompilerException("power operator requires floating points") - DataType.FLOAT -> Opcode.POW_F - else -> throw CompilerException("only numeric datatype possible") - } - } - "&" -> { - when(dt) { - in ByteDatatypes -> Opcode.BITAND_BYTE - in WordDatatypes -> Opcode.BITAND_WORD - else -> throw CompilerException("only byte/word possible") - } - } - "|" -> { - when(dt) { - in ByteDatatypes -> Opcode.BITOR_BYTE - in WordDatatypes -> Opcode.BITOR_WORD - else -> throw CompilerException("only byte/word possible") - } - } - "^" -> { - when(dt) { - in ByteDatatypes -> Opcode.BITXOR_BYTE - in WordDatatypes -> Opcode.BITXOR_WORD - else -> throw CompilerException("only byte/word possible") - } - } - "and" -> { - when(dt) { - in ByteDatatypes -> Opcode.AND_BYTE - in WordDatatypes -> Opcode.AND_WORD - else -> throw CompilerException("only byte/word possible") - } - } - "or" -> { - when(dt) { - in ByteDatatypes -> Opcode.OR_BYTE - in WordDatatypes -> Opcode.OR_WORD - else -> throw CompilerException("only byte/word possible") - } - } - "xor" -> { - when(dt) { - in ByteDatatypes -> Opcode.XOR_BYTE - in WordDatatypes -> Opcode.XOR_WORD - else -> throw CompilerException("only byte/word possible") - } - } - "<" -> { - when(dt) { - DataType.UBYTE -> Opcode.LESS_UB - DataType.BYTE -> Opcode.LESS_B - DataType.UWORD -> Opcode.LESS_UW - DataType.WORD -> Opcode.LESS_W - DataType.FLOAT -> Opcode.LESS_F - else -> throw CompilerException("only byte/word/lfoat possible") - } - } - ">" -> { - when(dt) { - DataType.UBYTE -> Opcode.GREATER_UB - DataType.BYTE -> Opcode.GREATER_B - DataType.UWORD -> Opcode.GREATER_UW - DataType.WORD -> Opcode.GREATER_W - DataType.FLOAT -> Opcode.GREATER_F - else -> throw CompilerException("only byte/word/lfoat possible") - } - } - "<=" -> { - when(dt) { - DataType.UBYTE -> Opcode.LESSEQ_UB - DataType.BYTE -> Opcode.LESSEQ_B - DataType.UWORD -> Opcode.LESSEQ_UW - DataType.WORD -> Opcode.LESSEQ_W - DataType.FLOAT -> Opcode.LESSEQ_F - else -> throw CompilerException("only byte/word/lfoat possible") - } - } - ">=" -> { - when(dt) { - DataType.UBYTE -> Opcode.GREATEREQ_UB - DataType.BYTE -> Opcode.GREATEREQ_B - DataType.UWORD -> Opcode.GREATEREQ_UW - DataType.WORD -> Opcode.GREATEREQ_W - DataType.FLOAT -> Opcode.GREATEREQ_F - else -> throw CompilerException("only byte/word/lfoat possible") - } - } - "==" -> { - when (dt) { - in ByteDatatypes -> Opcode.EQUAL_BYTE - in WordDatatypes -> Opcode.EQUAL_WORD - DataType.FLOAT -> Opcode.EQUAL_F - else -> throw CompilerException("only byte/word/lfoat possible") - } - } - "!=" -> { - when (dt) { - in ByteDatatypes -> Opcode.NOTEQUAL_BYTE - in WordDatatypes -> Opcode.NOTEQUAL_WORD - DataType.FLOAT -> Opcode.NOTEQUAL_F - else -> throw CompilerException("only byte/word/lfoat possible") - } - } - else -> throw FatalAstException("const evaluation for invalid operator $operator") - } - prog.instr(opcode) - } - - private fun translateBitshiftedOperator(operator: String, leftDt: DataType, amount: NumericLiteralValue?) { - if(amount?.number?.toInt() == null) - throw FatalAstException("bitshift operators should only have constant integer value as right operand") - var shifts=amount.number.toInt() - if(shifts<0) - throw FatalAstException("bitshift value should be >= 0") - - prog.removeLastInstruction() // the amount of shifts is not used as a stack value - if(shifts==0) - return - while(shifts>0) { - if(operator==">>") { - when (leftDt) { - DataType.UBYTE -> prog.instr(Opcode.SHIFTEDR_UBYTE) - DataType.BYTE -> prog.instr(Opcode.SHIFTEDR_SBYTE) - DataType.UWORD -> prog.instr(Opcode.SHIFTEDR_UWORD) - DataType.WORD -> prog.instr(Opcode.SHIFTEDR_SWORD) - else -> throw CompilerException("wrong datatype") - } - } else if(operator=="<<") { - when (leftDt) { - in ByteDatatypes -> prog.instr(Opcode.SHIFTEDL_BYTE) - in WordDatatypes -> prog.instr(Opcode.SHIFTEDL_WORD) - else -> throw CompilerException("wrong datatype") - } - } - shifts-- - } - } - - private fun translatePrefixOperator(operator: String, operandDt: DataType?) { - if(operandDt==null) - throw CompilerException("operand datatype not known") - val opcode = when(operator) { - "+" -> Opcode.NOP - "-" -> { - when (operandDt) { - DataType.BYTE -> Opcode.NEG_B - DataType.WORD -> Opcode.NEG_W - DataType.FLOAT -> Opcode.NEG_F - else -> throw CompilerException("only byte/word/foat possible") - } - } - "~" -> { - when(operandDt) { - in ByteDatatypes -> Opcode.INV_BYTE - in WordDatatypes -> Opcode.INV_WORD - else -> throw CompilerException("only byte/word possible") - } - } - "not" -> { - when(operandDt) { - in ByteDatatypes -> Opcode.NOT_BYTE - in WordDatatypes -> Opcode.NOT_WORD - else -> throw CompilerException("only byte/word possible") - } - } - else -> throw FatalAstException("const evaluation for invalid prefix operator $operator") - } - prog.instr(opcode) - } - - private fun translate(arrayindexed: ArrayIndexedExpression, write: Boolean) { - val variable = arrayindexed.identifier.targetVarDecl(program.namespace)!! - translate(arrayindexed.arrayspec.index) - if (write) - prog.instr(opcodeWriteindexedvar(variable.datatype), callLabel = variable.scopedname) - else - prog.instr(opcodeReadindexedvar(variable.datatype), callLabel = variable.scopedname) - } - - private fun createSyscall(funcname: String) { - val function = ( - if (funcname.startsWith("vm_")) - funcname - else - "FUNC_$funcname" - ).toUpperCase() - val callNr = Syscall.valueOf(function).callNr - prog.instr(Opcode.SYSCALL, RuntimeValue(DataType.UBYTE, callNr)) - } - - private fun translate(stmt: Jump, branchOpcode: Opcode?) { - var jumpAddress: RuntimeValue? = null - var jumpLabel: String? = null - - when { - stmt.generatedLabel!=null -> jumpLabel = stmt.generatedLabel - stmt.address!=null -> { - if(branchOpcode in branchOpcodes) - throw CompilerException("cannot branch to address, should use absolute jump instead") - jumpAddress = RuntimeValue(DataType.UWORD, stmt.address) - } - else -> { - val target = stmt.identifier!!.targetStatement(program.namespace)!! - jumpLabel = when(target) { - is Label -> target.scopedname - is Subroutine -> target.scopedname - else -> throw CompilerException("invalid jump target type ${target::class}") - } - } - } - prog.line(stmt.position) - prog.instr(branchOpcode ?: Opcode.JUMP, jumpAddress, callLabel = jumpLabel) - } - - private fun translate(stmt: PostIncrDecr) { - prog.line(stmt.position) - when { - stmt.target.register != null -> when(stmt.operator) { - "++" -> prog.instr(Opcode.INC_VAR_UB, callLabel = stmt.target.register!!.name) - "--" -> prog.instr(Opcode.DEC_VAR_UB, callLabel = stmt.target.register!!.name) - } - stmt.target.identifier != null -> { - val targetStatement = stmt.target.identifier!!.targetVarDecl(program.namespace)!! - when(stmt.operator) { - "++" -> prog.instr(opcodeIncvar(targetStatement.datatype), callLabel = targetStatement.scopedname) - "--" -> prog.instr(opcodeDecvar(targetStatement.datatype), callLabel = targetStatement.scopedname) - } - } - stmt.target.arrayindexed != null -> { - val variable = stmt.target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!! - translate(stmt.target.arrayindexed!!.arrayspec.index) - when(stmt.operator) { - "++" -> prog.instr(opcodeIncArrayindexedVar(variable.datatype), callLabel = variable.scopedname) - "--" -> prog.instr(opcodeDecArrayindexedVar(variable.datatype), callLabel = variable.scopedname) - } - } - stmt.target.memoryAddress != null -> { - val address = stmt.target.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt() - if(address!=null) { - when(stmt.operator) { - "++" -> prog.instr(Opcode.INC_MEMORY, RuntimeValue(DataType.UWORD, address)) - "--" -> prog.instr(Opcode.DEC_MEMORY, RuntimeValue(DataType.UWORD, address)) - } - } else { - translate(stmt.target.memoryAddress!!.addressExpression) - when(stmt.operator) { - "++" -> prog.instr(Opcode.POP_INC_MEMORY) - "--" -> prog.instr(Opcode.POP_DEC_MEMORY) - } - } - } - else -> throw CompilerException("very strange postincrdecr ${stmt.target}") - } - } - - private fun translate(stmt: Assignment) { - prog.line(stmt.position) - if(stmt.value is StructLiteralValue) { - // flatten into individual struct member assignments - val identifier = stmt.target.identifier!! - val identifierName = identifier.nameInSource.single() - val targetVar = identifier.targetVarDecl(program.namespace)!! - val struct = targetVar.struct!! - val sourcevalues = (stmt.value as StructLiteralValue).values - val assignments = struct.statements.zip(sourcevalues).map { member -> - val decl = member.first as VarDecl - val mangled = mangledStructMemberName(identifierName, decl.name) - val idref = IdentifierReference(listOf(mangled), stmt.position) - val assign = Assignment(AssignTarget(null, idref, null, null, stmt.position), - null, member.second, member.second.position) - assign.linkParents(stmt) - assign - } - assignments.forEach { translate(it) } - return - } - - translate(stmt.value) - - val valueDt = stmt.value.inferType(program) - val targetDt = stmt.target.inferType(program, stmt) - if(valueDt!=targetDt) { - // convert value to target datatype if possible - // @todo use convertType()???? - when(targetDt) { - in ByteDatatypes -> - if(valueDt!= DataType.BYTE && valueDt!= DataType.UBYTE) - throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") - DataType.WORD -> { - when (valueDt) { - DataType.UBYTE -> prog.instr(Opcode.CAST_UB_TO_W) - DataType.BYTE -> prog.instr(Opcode.CAST_B_TO_W) - else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") - } - } - DataType.UWORD -> { - when (valueDt) { - DataType.UBYTE -> prog.instr(Opcode.CAST_UB_TO_UW) - DataType.BYTE -> prog.instr(Opcode.CAST_B_TO_UW) - DataType.STR, DataType.STR_S -> pushHeapVarAddress(stmt.value, true) - DataType.ARRAY_B, DataType.ARRAY_UB, DataType.ARRAY_W, DataType.ARRAY_UW, DataType.ARRAY_F -> { - if (stmt.value is IdentifierReference) { - val vardecl = (stmt.value as IdentifierReference).targetVarDecl(program.namespace)!! - prog.removeLastInstruction() - prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname) - } - else - throw CompilerException("can only take address of a literal string value or a string/array variable") - } - else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") - } - } - DataType.FLOAT -> { - when (valueDt) { - DataType.UBYTE -> prog.instr(Opcode.CAST_UB_TO_F) - DataType.BYTE -> prog.instr(Opcode.CAST_B_TO_F) - DataType.UWORD -> prog.instr(Opcode.CAST_UW_TO_F) - DataType.WORD -> prog.instr(Opcode.CAST_W_TO_F) - else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") - } - } - in StringDatatypes -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") - in ArrayDatatypes -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") - else -> throw CompilerException("weird/unknown targetdt") - } - } - - if(stmt.aug_op!=null) - throw CompilerException("augmented assignment should have been converted to regular assignment already") - - // pop the result value back into the assignment target - val datatype = stmt.target.inferType(program, stmt)!! - popValueIntoTarget(stmt.target, datatype) - } - - private fun pushHeapVarAddress(value: Expression, removeLastOpcode: Boolean) { - if (value is IdentifierReference) { - val vardecl = value.targetVarDecl(program.namespace)!! - if(removeLastOpcode) prog.removeLastInstruction() - prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname) - } - else throw CompilerException("can only take address of a literal string value or a string/array variable") - } - - private fun pushFloatAddress(value: Expression) { - if (value is IdentifierReference) { - val vardecl = value.targetVarDecl(program.namespace)!! - prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname) - } - else throw CompilerException("can only take address of a the float as constant literal or variable") - } - - private fun pushStructAddress(value: Expression) { - if (value is IdentifierReference) { - // notice that the mangled name of the first struct member is the start address of this struct var - val vardecl = value.targetVarDecl(program.namespace)!! - val firstStructMember = (vardecl.struct!!.statements.first() as VarDecl).name - val firstVarName = listOf(vardecl.name, firstStructMember) - // find the flattened var that belongs to this first struct member - val firstVar = value.definingScope().lookup(firstVarName, value) as VarDecl - prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = firstVar.scopedname) // TODO - } - else throw CompilerException("can only take address of a the float as constant literal or variable") - } - - private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) { - when { - assignTarget.identifier != null -> { - val target = assignTarget.identifier.targetStatement(program.namespace)!! - if (target is VarDecl) { - when (target.type) { - VarDeclType.VAR -> { - val opcode = opcodePopvar(datatype) - prog.instr(opcode, callLabel = target.scopedname) - } - VarDeclType.MEMORY -> { - val opcode = opcodePopmem(datatype) - val address = target.value?.constValue(program)!!.number.toInt() - prog.instr(opcode, RuntimeValue(DataType.UWORD, address)) - } - VarDeclType.CONST -> throw CompilerException("cannot assign to const") - } - } else throw CompilerException("invalid assignment target type ${target::class}") - } - assignTarget.register != null -> prog.instr(Opcode.POP_VAR_BYTE, callLabel = assignTarget.register.name) - assignTarget.arrayindexed != null -> translate(assignTarget.arrayindexed, true) // write value to it - assignTarget.memoryAddress != null -> { - val address = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt() - if(address!=null) { - // const integer address given - prog.instr(Opcode.POP_MEM_BYTE, arg= RuntimeValue(DataType.UWORD, address)) - } else { - translate(assignTarget.memoryAddress!!) - } - } - else -> throw CompilerException("corrupt assigntarget $assignTarget") - } - } - - private fun translate(stmt: Return) { - // put the return values on the stack, in reversed order. The caller will accept them. - if(stmt.value!=null) - translate(stmt.value!!) - prog.line(stmt.position) - prog.instr(Opcode.RETURN) - } - - private fun translate(stmt: Label) { - prog.label(stmt.scopedname) - } - - private fun translate(loop: ForLoop) { - if(loop.body.containsNoCodeNorVars()) return - prog.line(loop.position) - val loopVarName: String? - val loopRegister: Register? - val loopvalueDt: DataType - - if(loop.loopRegister!=null) { - loopVarName = null - loopRegister = loop.loopRegister - loopvalueDt = DataType.UBYTE - } else { - val loopvar = loop.loopVar!!.targetVarDecl(program.namespace)!! - loopVarName = loopvar.scopedname - loopvalueDt = loopvar.datatype - loopRegister = null - } - - if(loop.iterable is RangeExpr) { - val range = (loop.iterable as RangeExpr).toConstantIntegerRange() - if(range!=null) { - // loop over a range with constant start, last and step values - if (range.isEmpty()) - throw CompilerException("loop over empty range should have been optimized away") - else if (range.count()==1) - throw CompilerException("loop over just 1 value should have been optimized away") - if((range.last-range.first) % range.step != 0) - throw CompilerException("range first and last must be exactly inclusive") - when (loopvalueDt) { - DataType.UBYTE -> { - if (range.first < 0 || range.first > 255 || range.last < 0 || range.last > 255) - throw CompilerException("range out of bounds for ubyte") - } - DataType.UWORD -> { - if (range.first < 0 || range.first > 65535 || range.last < 0 || range.last > 65535) - throw CompilerException("range out of bounds for uword") - } - DataType.BYTE -> { - if (range.first < -128 || range.first > 127 || range.last < -128 || range.last > 127) - throw CompilerException("range out of bounds for byte") - } - DataType.WORD -> { - if (range.first < -32768 || range.first > 32767 || range.last < -32768 || range.last > 32767) - throw CompilerException("range out of bounds for word") - } - else -> throw CompilerException("range must be byte or word") - } - translateForOverConstantRange(loopVarName, loopRegister, loopvalueDt, range, loop.body) - } else { - // loop over a range where one or more of the start, last or step values is not a constant - if(loop.loopRegister!=null) { - translateForOverVariableRange(null, loop.loopRegister, loop.iterable as RangeExpr, loop.body) - } - else { - translateForOverVariableRange(loop.loopVar!!.nameInSource, null, loop.iterable as RangeExpr, loop.body) - } - } - } else { - // ok, must be a literalvalue - when { - loop.iterable is IdentifierReference -> { - val idRef = loop.iterable as IdentifierReference - val vardecl = idRef.targetVarDecl(program.namespace)!! - // TODO check loop over iterable or not -// val iterableValue = vardecl.value as LiteralValue -// if(iterableValue.type !in IterableDatatypes) -// throw CompilerException("loop over something that isn't iterable ${loop.iterable}") - translateForOverIterableVar(loop, loopvalueDt, vardecl.value as ReferenceLiteralValue) - } - // TODO what's this: loop.iterable is LiteralValue -> throw CompilerException("literal value in loop must have been moved to heap already $loop") - else -> throw CompilerException("loopvar is something strange ${loop.iterable}") - } - } - } - - private fun translateForOverIterableVar(loop: ForLoop, loopvarDt: DataType, iterableValue: ReferenceLiteralValue) { - if(loopvarDt== DataType.UBYTE && iterableValue.type !in setOf(DataType.STR, DataType.STR_S, DataType.ARRAY_UB)) - throw CompilerException("loop variable type doesn't match iterableValue type") - else if(loopvarDt== DataType.UWORD && iterableValue.type != DataType.ARRAY_UW) - 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 - when(iterableValue.type) { - !in IterableDatatypes -> throw CompilerException("non-iterableValue type") - DataType.STR, DataType.STR_S -> { - numElements = iterableValue.str!!.length - if(numElements>255) throw CompilerException("string length > 255") - } - DataType.ARRAY_UB, DataType.ARRAY_B, - DataType.ARRAY_UW, DataType.ARRAY_W -> { - numElements = iterableValue.array?.size ?: program.heap.get(iterableValue.heapId!!).arraysize - if(numElements>255) throw CompilerException("string length > 255") - } - DataType.ARRAY_F -> { - numElements = iterableValue.array?.size ?: program.heap.get(iterableValue.heapId!!).arraysize - if(numElements>255) throw CompilerException("string length > 255") - } - else -> throw CompilerException("weird datatype") - } - - if(loop.loopRegister!=null && loop.loopRegister== Register.X) - throw CompilerException("loopVar cannot use X register because that is used as internal stack pointer") - - /** - * indexVar = 0 - * loop: - * LV = iterableValue[indexVar] - * ..body.. - * ..break statement: goto break - * ..continue statement: goto continue - * .. - * continue: - * indexVar++ - * if indexVar!=numElements goto loop - * break: - * nop - */ - val loopLabel = makeLabel(loop, "loop") - val continueLabel = makeLabel(loop, "continue") - val breakLabel = makeLabel(loop, "break") - val indexVarType = if (numElements <= 255) DataType.UBYTE else DataType.UWORD - val indexVar = loop.body.getLabelOrVariable(ForLoop.iteratorLoopcounterVarname) as VarDecl - - continueStmtLabelStack.push(continueLabel) - breakStmtLabelStack.push(breakLabel) - - // set the index var to zero before the loop - prog.instr(opcodePush(indexVarType), RuntimeValue(indexVarType, 0)) - prog.instr(opcodePopvar(indexVarType), callLabel = indexVar.scopedname) - - // loop starts here - prog.label(loopLabel) - val assignTarget = if(loop.loopRegister!=null) - AssignTarget(loop.loopRegister, null, null, null, loop.position) - else - AssignTarget(null, loop.loopVar!!.copy(), null, null, loop.position) - val arrayspec = ArrayIndex(IdentifierReference(listOf(ForLoop.iteratorLoopcounterVarname), loop.position), loop.position) - val assignLv = Assignment( - assignTarget, null, - ArrayIndexedExpression((loop.iterable as IdentifierReference).copy(), arrayspec, loop.position), - loop.position) - assignLv.linkParents(loop.body) - translate(assignLv) - translate(loop.body) - prog.label(continueLabel) - - prog.instr(opcodeIncvar(indexVarType), callLabel = indexVar.scopedname) - prog.instr(opcodePushvar(indexVarType), callLabel = indexVar.scopedname) - prog.instr(opcodeCompare(indexVarType), RuntimeValue(indexVarType, numElements)) - prog.instr(Opcode.BNZ, callLabel = loopLabel) - - prog.label(breakLabel) - prog.instr(Opcode.NOP) - - breakStmtLabelStack.pop() - continueStmtLabelStack.pop() - } - - private fun translateForOverConstantRange(varname: String?, loopregister: Register?, varDt: DataType, range: IntProgression, body: AnonymousScope) { - /** - * for LV in start..last { body } - * (and we already know that the range is not empty, and first and last are exactly inclusive.) - * (also we know that the range's last value is really the exact last occurring value of the range) - * (and finally, start and last are constant integer values) - * -> - * LV = start - * loop: - * ..body.. - * ..break statement: goto break - * ..continue statement: goto continue - * .. - * continue: - * LV++ (if step=1) / LV += step (if step > 1) - * LV-- (if step=-1) / LV -= abs(step) (if step < 1) - * if LV!=(last+step) goto loop - * break: - * nop - */ - val loopLabel = makeLabel(body, "loop") - val continueLabel = makeLabel(body, "continue") - val breakLabel = makeLabel(body, "break") - - continueStmtLabelStack.push(continueLabel) - breakStmtLabelStack.push(breakLabel) - - prog.instr(opcodePush(varDt), RuntimeValue(varDt, range.first)) - val variableLabel = varname ?: loopregister?.name - - prog.instr(opcodePopvar(varDt), callLabel = variableLabel) - prog.label(loopLabel) - translate(body) - prog.label(continueLabel) - val numberOfIncDecsForOptimize = 8 - when { - range.step in 1..numberOfIncDecsForOptimize -> { - repeat(range.step) { - prog.instr(opcodeIncvar(varDt), callLabel = variableLabel) - } - } - range.step in -1 downTo -numberOfIncDecsForOptimize -> { - repeat(abs(range.step)) { - prog.instr(opcodeDecvar(varDt), callLabel = variableLabel) - } - } - range.step>numberOfIncDecsForOptimize -> { - prog.instr(opcodePushvar(varDt), callLabel = variableLabel) - prog.instr(opcodePush(varDt), RuntimeValue(varDt, range.step)) - prog.instr(opcodeAdd(varDt)) - prog.instr(opcodePopvar(varDt), callLabel = variableLabel) - } - range.step { - prog.instr(opcodePushvar(varDt), callLabel = variableLabel) - prog.instr(opcodePush(varDt), RuntimeValue(varDt, abs(range.step))) - prog.instr(opcodeSub(varDt)) - prog.instr(opcodePopvar(varDt), callLabel = variableLabel) - } - } - - if(range.last==0) { - // optimize for the for loop that counts to 0 - prog.instr(if(range.first>0) Opcode.BPOS else Opcode.BNEG, callLabel = loopLabel) - } else { - prog.instr(opcodePushvar(varDt), callLabel = variableLabel) - val checkValue = - when (varDt) { - DataType.UBYTE -> (range.last + range.step) and 255 - DataType.UWORD -> (range.last + range.step) and 65535 - DataType.BYTE, DataType.WORD -> range.last + range.step - else -> throw CompilerException("invalid loop var dt $varDt") - } - prog.instr(opcodeCompare(varDt), RuntimeValue(varDt, checkValue)) - prog.instr(Opcode.BNZ, callLabel = loopLabel) - } - prog.label(breakLabel) - prog.instr(Opcode.NOP) - // note: ending value of loop register / variable is *undefined* after this point! - - breakStmtLabelStack.pop() - continueStmtLabelStack.pop() - } - - private fun translateForOverVariableRange(varname: List?, register: Register?, - range: RangeExpr, body: AnonymousScope) { - /* - * for LV in start..last { body } - * (where at least one of the start, last, step values is not a constant) - * (so we can't make any static assumptions about them) - * -> - * LV = start - * loop: - * if (step > 0) { - * if(LV>last) goto break - * } else { - * if(LV - * LV++ - * if_nz goto loop ;; acts as overflow check - * step == -1 -> - * LV-- - * @todo some condition to check for not overflow , jump to loop - * (not constant or other step: - * LV += step ; @todo implement overflow on the appropriate arithmetic operations - * if_vc goto loop ;; not overflowed - * break: - * nop - */ - fun makeAssignmentTarget(): AssignTarget { - return if(varname!=null) - AssignTarget(null, IdentifierReference(varname, range.position), null, null, range.position) - else - AssignTarget(register, null, null, null, range.position) - } - - val startAssignment = Assignment(makeAssignmentTarget(), null, range.from, range.position) - startAssignment.linkParents(body) - translate(startAssignment) - - val loopLabel = makeLabel(body, "loop") - val continueLabel = makeLabel(body, "continue") - val breakLabel = makeLabel(body, "break") - val literalStepValue = (range.step as? NumericLiteralValue)?.number?.toInt() - - continueStmtLabelStack.push(continueLabel) - breakStmtLabelStack.push(breakLabel) - - prog.label(loopLabel) - if(literalStepValue!=null) { - // Step is a constant. We can optimize some stuff! - val loopVar = - if(varname!=null) - IdentifierReference(varname, range.position) - else - RegisterExpr(register!!, range.position) - - val condition = - if(literalStepValue > 0) { - // if LV > last goto break - BinaryExpression(loopVar, ">", range.to, range.position) - } else { - // if LV < last goto break - BinaryExpression(loopVar, "<", range.to, range.position) - } - val ifstmt = IfStatement(condition, - AnonymousScope(mutableListOf(Jump(null, null, breakLabel, range.position)), range.position), - AnonymousScope(mutableListOf(), range.position), - range.position) - ifstmt.linkParents(body) - translate(ifstmt) - } else { - // Step is a variable. We can't optimize anything... - TODO("for loop with non-constant step comparison of LV, at: ${range.position}") - } - - translate(body) - prog.label(continueLabel) - val lvTarget = makeAssignmentTarget() - lvTarget.linkParents(body) - val targetStatement: VarDecl? = - if(lvTarget.identifier!=null) { - lvTarget.identifier.targetVarDecl(program.namespace) - } else { - null - } - // todo deal with target.arrayindexed? - - fun createLoopCode(step: Int) { - if(step!=1 && step !=-1) - TODO("can't generate code for step other than 1 or -1 right now") - - // LV++ / LV-- - val postIncr = PostIncrDecr(lvTarget, if (step == 1) "++" else "--", range.position) - postIncr.linkParents(body) - translate(postIncr) - if(lvTarget.register!=null) - prog.instr(Opcode.PUSH_VAR_BYTE, callLabel =lvTarget.register.name) - else { - val opcode = opcodePushvar(targetStatement!!.datatype) - prog.instr(opcode, callLabel = targetStatement.scopedname) - } - // TODO: optimize this to use a compare + branch opcode somehow? - val conditionJumpOpcode = when(targetStatement!!.datatype) { - in ByteDatatypes -> Opcode.JNZ - in WordDatatypes -> Opcode.JNZW - else -> throw CompilerException("invalid loopvar datatype (expected byte or word) $lvTarget") - } - prog.instr(conditionJumpOpcode, callLabel = loopLabel) - } - - when (literalStepValue) { - 1 -> createLoopCode(1) - -1 -> createLoopCode(-1) - null -> TODO("variable range forloop non-literal-const step increment, At: ${range.position}") - else -> TODO("variable range forloop step increment not 1 or -1, At: ${range.position}") - } - - prog.label(breakLabel) - prog.instr(Opcode.NOP) - // note: ending value of loop register / variable is *undefined* after this point! - - breakStmtLabelStack.pop() - continueStmtLabelStack.pop() - } - - private fun translate(scope: AnonymousScope) = translate(scope.statements) - - private fun translate(stmt: WhileLoop) - { - /* - * while condition { statements... } -> - * - * goto continue - * loop: - * statements - * break -> goto break - * continue -> goto condition - * continue: - * - * jnz/jnzw loop - * break: - * nop - */ - val loopLabel = makeLabel(stmt, "loop") - val breakLabel = makeLabel(stmt, "break") - val continueLabel = makeLabel(stmt, "continue") - prog.line(stmt.position) - breakStmtLabelStack.push(breakLabel) - continueStmtLabelStack.push(continueLabel) - prog.instr(Opcode.JUMP, callLabel = continueLabel) - prog.label(loopLabel) - translate(stmt.body) - prog.label(continueLabel) - translate(stmt.condition) - val conditionJumpOpcode = when(stmt.condition.inferType(program)) { - in ByteDatatypes -> Opcode.JNZ - in WordDatatypes -> Opcode.JNZW - else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt") - } - prog.instr(conditionJumpOpcode, callLabel = loopLabel) - prog.label(breakLabel) - prog.instr(Opcode.NOP) - breakStmtLabelStack.pop() - continueStmtLabelStack.pop() - } - - private fun translate(stmt: RepeatLoop) - { - /* - * repeat { statements... } until condition -> - * - * loop: - * statements - * break -> goto break - * continue -> goto condition - * condition: - * - * jz/jzw goto loop - * break: - * nop - */ - val loopLabel = makeLabel(stmt, "loop") - val continueLabel = makeLabel(stmt, "continue") - val breakLabel = makeLabel(stmt, "break") - prog.line(stmt.position) - breakStmtLabelStack.push(breakLabel) - continueStmtLabelStack.push(continueLabel) - prog.label(loopLabel) - translate(stmt.body) - prog.label(continueLabel) - translate(stmt.untilCondition) - val conditionJumpOpcode = when(stmt.untilCondition.inferType(program)) { - in ByteDatatypes -> Opcode.JZ - in WordDatatypes -> Opcode.JZW - else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt") - } - prog.instr(conditionJumpOpcode, callLabel = loopLabel) - prog.label(breakLabel) - prog.instr(Opcode.NOP) - breakStmtLabelStack.pop() - continueStmtLabelStack.pop() - } - - private fun translate(expr: TypecastExpression) { - translate(expr.expression) - val sourceDt = expr.expression.inferType(program) ?: throw CompilerException("don't know what type to cast") - if(sourceDt==expr.type) - return - - when(expr.type) { - DataType.UBYTE -> when(sourceDt) { - DataType.UBYTE -> {} - DataType.BYTE -> prog.instr(Opcode.CAST_B_TO_UB) - DataType.UWORD -> prog.instr(Opcode.CAST_UW_TO_UB) - DataType.WORD-> prog.instr(Opcode.CAST_W_TO_UB) - DataType.FLOAT -> prog.instr(Opcode.CAST_F_TO_UB) - else -> throw CompilerException("invalid cast $sourceDt to ${expr.type} -- should be an Ast check") - } - DataType.BYTE -> when(sourceDt) { - DataType.UBYTE -> prog.instr(Opcode.CAST_UB_TO_B) - DataType.BYTE -> {} - DataType.UWORD -> prog.instr(Opcode.CAST_UW_TO_B) - DataType.WORD -> prog.instr(Opcode.CAST_W_TO_B) - DataType.FLOAT -> prog.instr(Opcode.CAST_F_TO_B) - else -> throw CompilerException("invalid cast $sourceDt to ${expr.type} -- should be an Ast check") - } - DataType.UWORD -> when(sourceDt) { - DataType.UBYTE -> prog.instr(Opcode.CAST_UB_TO_UW) - DataType.BYTE -> prog.instr(Opcode.CAST_B_TO_UW) - DataType.UWORD -> {} - DataType.WORD -> prog.instr(Opcode.CAST_W_TO_UW) - DataType.FLOAT -> prog.instr(Opcode.CAST_F_TO_UW) - else -> throw CompilerException("invalid cast $sourceDt to ${expr.type} -- should be an Ast check") - } - DataType.WORD -> when(sourceDt) { - DataType.UBYTE -> prog.instr(Opcode.CAST_UB_TO_W) - DataType.BYTE -> prog.instr(Opcode.CAST_B_TO_W) - DataType.UWORD -> prog.instr(Opcode.CAST_UW_TO_W) - DataType.WORD -> {} - DataType.FLOAT -> prog.instr(Opcode.CAST_F_TO_W) - else -> throw CompilerException("invalid cast $sourceDt to ${expr.type} -- should be an Ast check") - } - DataType.FLOAT -> when(sourceDt) { - DataType.UBYTE -> prog.instr(Opcode.CAST_UB_TO_F) - DataType.BYTE -> prog.instr(Opcode.CAST_B_TO_F) - DataType.UWORD -> prog.instr(Opcode.CAST_UW_TO_F) - DataType.WORD -> prog.instr(Opcode.CAST_W_TO_F) - DataType.FLOAT -> {} - else -> throw CompilerException("invalid cast $sourceDt to ${expr.type} -- should be an Ast check") - } - else -> throw CompilerException("invalid cast $sourceDt to ${expr.type} -- should be an Ast check") - } - } - - private fun translate(memread: DirectMemoryRead) { - // for now, only a single memory location (ubyte) is read at a time. - val address = memread.addressExpression.constValue(program)?.number?.toInt() - if(address!=null) { - prog.instr(Opcode.PUSH_MEM_UB, arg = RuntimeValue(DataType.UWORD, address)) - } else { - translate(memread.addressExpression) - prog.instr(Opcode.PUSH_MEMREAD) - } - } - - private fun translate(memwrite: DirectMemoryWrite) { - // for now, only a single memory location (ubyte) is written at a time. - // TODO inline this function (it's only used once) - val address = memwrite.addressExpression.constValue(program)?.number?.toInt() - if(address!=null) { - prog.instr(Opcode.POP_MEM_BYTE, arg = RuntimeValue(DataType.UWORD, address)) - } else { - translate(memwrite.addressExpression) - prog.instr(Opcode.POP_MEMWRITE) - } - } - - private fun translate(addrof: AddressOf) { - val target = addrof.identifier.targetVarDecl(program.namespace)!! - if(target.datatype in ArrayDatatypes || target.datatype in StringDatatypes || target.datatype== DataType.FLOAT) { - pushHeapVarAddress(addrof.identifier, false) - } - else if(target.datatype== DataType.FLOAT) { - pushFloatAddress(addrof.identifier) - } - else if(target.datatype == DataType.STRUCT) { - pushStructAddress(addrof.identifier) - } - else - throw CompilerException("cannot take memory pointer $addrof") - } - - private fun translate(whenstmt: WhenStatement) { - val conditionDt = whenstmt.condition.inferType(program) - if(conditionDt !in IntegerDatatypes) - throw CompilerException("when condition must be integer") - translate(whenstmt.condition) - if(whenstmt.choices.isEmpty()) { - if (conditionDt in ByteDatatypes) prog.instr(Opcode.DISCARD_BYTE) - else prog.instr(Opcode.DISCARD_WORD) - return - } - - val endOfWhenLabel = makeLabel(whenstmt, "when_end") - - val choiceLabels = mutableListOf() - for(choice in whenstmt.choiceValues(program)) { - if(choice.first==null) { - // the else clause - translate(choice.second.statements) - } else { - val choiceVal = choice.first!!.single() - val rval = RuntimeValue(conditionDt!!, choiceVal) - if (conditionDt in ByteDatatypes) { - prog.instr(Opcode.DUP_B) - prog.instr(opcodeCompare(conditionDt), rval) - } - else { - prog.instr(Opcode.DUP_W) - prog.instr(opcodeCompare(conditionDt), rval) - } - val choiceLabel = makeLabel(whenstmt, "choice_$choiceVal") - choiceLabels.add(choiceLabel) - prog.instr(Opcode.BZ, callLabel = choiceLabel) - } - } - prog.instr(Opcode.JUMP, callLabel = endOfWhenLabel) - - for(choice in whenstmt.choices.zip(choiceLabels)) { - prog.label(choice.second) - translate(choice.first.statements) - prog.instr(Opcode.JUMP, callLabel = endOfWhenLabel) - } - - prog.removeLastInstruction() // remove the last jump, that can fall through to here - prog.label(endOfWhenLabel) - - if (conditionDt in ByteDatatypes) prog.instr(Opcode.DISCARD_BYTE) - else prog.instr(Opcode.DISCARD_WORD) - } - - private fun translateAsmInclude(args: List, source: Path) { - val scopeprefix = if(args[1].str!!.isNotBlank()) "${args[1].str}\t.proc\n" else "" - val scopeprefixEnd = if(args[1].str!!.isNotBlank()) "\t.pend\n" else "" - val filename=args[0].str!! - val sourcecode = loadAsmIncludeFile(filename, source) - - prog.instr(Opcode.INLINE_ASSEMBLY, callLabel=null, callLabel2=scopeprefix+sourcecode+scopeprefixEnd) - } - - private fun translateAsmBinary(args: List) { - val offset = if(args.size>=2) RuntimeValue(DataType.UWORD, args[1].int!!) else null - val length = if(args.size==3) RuntimeValue(DataType.UWORD, args[2].int!!) else null - val filename = args[0].str!! - // reading the actual data is not performed by the compiler but is delegated to the assembler - prog.instr(Opcode.INCLUDE_FILE, offset, length, filename) - } - -} - - -fun loadAsmIncludeFile(filename: String, source: Path): String { - return if (filename.startsWith("library:")) { - val resource = tryGetEmbeddedResource(filename.substring(8)) - ?: throw IllegalArgumentException("library file '$filename' not found") - resource.bufferedReader().use { it.readText() } - } else { - // first try in the isSameAs folder as where the containing file was imported from - val sib = source.resolveSibling(filename) - if (sib.toFile().isFile) - sib.toFile().readText() - else - File(filename).readText() - } -} diff --git a/DeprecatedStackVm/src/compiler/intermediate/Instruction.kt b/DeprecatedStackVm/src/compiler/intermediate/Instruction.kt deleted file mode 100644 index 0fb3e259b..000000000 --- a/DeprecatedStackVm/src/compiler/intermediate/Instruction.kt +++ /dev/null @@ -1,51 +0,0 @@ -package compiler.intermediate - -import prog8.vm.RuntimeValue -import prog8.vm.stackvm.Syscall - -open class Instruction(val opcode: Opcode, - val arg: RuntimeValue? = null, - val arg2: RuntimeValue? = null, - val callLabel: String? = null, - val callLabel2: String? = null) -{ - var branchAddress: Int? = null - - override fun toString(): String { - val argStr = arg?.toString() ?: "" - val result = - when { - opcode== Opcode.LINE -> "_line $callLabel" - opcode== Opcode.INLINE_ASSEMBLY -> { - // inline assembly is not written out (it can't be processed as intermediate language) - // instead, it is converted into a system call that can be intercepted by the vm - if(callLabel!=null) - "syscall SYSASM.$callLabel\n return" - else - "inline_assembly" - } - opcode== Opcode.INCLUDE_FILE -> { - "include_file \"$callLabel\" $arg $arg2" - } - opcode== Opcode.SYSCALL -> { - val syscall = Syscall.values().find { it.callNr==arg!!.numericValue() } - "syscall $syscall" - } - opcode in opcodesWithVarArgument -> { - // opcodes that manipulate a variable - "${opcode.name.toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd() - } - callLabel==null -> "${opcode.name.toLowerCase()} $argStr" - else -> "${opcode.name.toLowerCase()} $callLabel $argStr" - } - .trimEnd() - - return " $result" - } -} - -class LabelInstr(val name: String, val asmProc: Boolean) : Instruction(Opcode.NOP, null, null) { - override fun toString(): String { - return "\n$name:" - } -} diff --git a/DeprecatedStackVm/src/compiler/intermediate/IntermediateProgram.kt b/DeprecatedStackVm/src/compiler/intermediate/IntermediateProgram.kt deleted file mode 100644 index 0999df0bf..000000000 --- a/DeprecatedStackVm/src/compiler/intermediate/IntermediateProgram.kt +++ /dev/null @@ -1,548 +0,0 @@ -package compiler.intermediate - -import prog8.ast.antlr.escape -import prog8.ast.base.* -import prog8.ast.expressions.NumericLiteralValue -import prog8.ast.expressions.ReferenceLiteralValue -import prog8.ast.statements.StructDecl -import prog8.ast.statements.VarDecl -import prog8.ast.statements.ZeropageWish -import prog8.compiler.CompilerException -import prog8.compiler.HeapValues -import prog8.compiler.Zeropage -import prog8.compiler.ZeropageDepletedError -import prog8.vm.RuntimeValue -import java.io.PrintStream -import java.nio.file.Path - - -class IntermediateProgram(val name: String, var loadAddress: Int, val heap: HeapValues, val source: Path) { - - class VariableParameters (val zp: ZeropageWish, val memberOfStruct: StructDecl?) - class Variable(val scopedname: String, val value: RuntimeValue, val params: VariableParameters) - - class ProgramBlock(val name: String, - var address: Int?, - val instructions: MutableList = mutableListOf(), - val variables: MutableList = mutableListOf(), - val memoryPointers: MutableMap> = mutableMapOf(), - val labels: MutableMap = mutableMapOf(), // names are fully scoped - val force_output: Boolean) - - val allocatedZeropageVariables = mutableMapOf>() - val blocks = mutableListOf() - val memory = mutableMapOf>() - private lateinit var currentBlock: ProgramBlock - - fun allocateZeropage(zeropage: Zeropage) { // TODO not used anymore??? - // allocates all @zp marked variables on the zeropage (for all blocks, as long as there is space in the ZP) - var notAllocated = 0 - for(block in blocks) { - val zpVariables = block.variables.filter { it.params.zp==ZeropageWish.REQUIRE_ZEROPAGE || it.params.zp==ZeropageWish.PREFER_ZEROPAGE } - if (zpVariables.isNotEmpty()) { - for (variable in zpVariables) { - if(variable.params.zp==ZeropageWish.NOT_IN_ZEROPAGE || variable.params.memberOfStruct!=null) - throw CompilerException("zp conflict") - try { - val address = zeropage.allocate(variable.scopedname, variable.value.type, null) - allocatedZeropageVariables[variable.scopedname] = Pair(address, variable.value.type) - } catch (x: ZeropageDepletedError) { - printWarning(x.toString() + " variable ${variable.scopedname} type ${variable.value.type}") - notAllocated++ - } - } - } - } - if(notAllocated>0) - printWarning("$notAllocated variables marked for Zeropage could not be allocated there") - } - - fun optimize() { - println("Optimizing stackVM code...") - // remove nops (that are not a label) - for (blk in blocks) { - blk.instructions.removeIf { it.opcode== Opcode.NOP && it !is LabelInstr } - } - - optimizeDataConversionAndUselessDiscards() - optimizeVariableCopying() - optimizeMultipleSequentialLineInstrs() - optimizeCallReturnIntoJump() - optimizeConditionalBranches() - // todo: add more optimizations to intermediate code! - - optimizeRemoveNops() // must be done as the last step - optimizeMultipleSequentialLineInstrs() // once more - optimizeRemoveNops() // once more - } - - private fun optimizeConditionalBranches() { - // conditional branches that consume the value on the stack - // sometimes these are just constant values, so we can statically determine the branch - // or, they are preceded by a NOT instruction so we can simply remove that and flip the branch condition - val pushvalue = setOf(Opcode.PUSH_BYTE, Opcode.PUSH_WORD) - val notvalue = setOf(Opcode.NOT_BYTE, Opcode.NOT_WORD) - val branchOpcodes = setOf(Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW) - for(blk in blocks) { - val instructionsToReplace = mutableMapOf() - blk.instructions.asSequence().withIndex().filter {it.value.opcode!= Opcode.LINE }.windowed(2).toList().forEach { - if (it[1].value.opcode in branchOpcodes) { - if (it[0].value.opcode in pushvalue) { - val value = it[0].value.arg!!.asBoolean - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - val replacement: Instruction = - if (value) { - when (it[1].value.opcode) { - Opcode.JNZ -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel) - Opcode.JNZW -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel) - else -> Instruction(Opcode.NOP) - } - } else { - when (it[1].value.opcode) { - Opcode.JZ -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel) - Opcode.JZW -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel) - else -> Instruction(Opcode.NOP) - } - } - instructionsToReplace[it[1].index] = replacement - } - else if (it[0].value.opcode in notvalue) { - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - val replacement: Instruction = - when (it[1].value.opcode) { - Opcode.JZ -> Instruction(Opcode.JNZ, callLabel = it[1].value.callLabel) - Opcode.JZW -> Instruction(Opcode.JNZW, callLabel = it[1].value.callLabel) - Opcode.JNZ -> Instruction(Opcode.JZ, callLabel = it[1].value.callLabel) - Opcode.JNZW -> Instruction(Opcode.JZW, callLabel = it[1].value.callLabel) - else -> Instruction(Opcode.NOP) - } - instructionsToReplace[it[1].index] = replacement - } - } - } - - for (rins in instructionsToReplace) { - blk.instructions[rins.key] = rins.value - } - } - } - - private fun optimizeRemoveNops() { - // remove nops (that are not a label) - for (blk in blocks) - blk.instructions.removeIf { it.opcode== Opcode.NOP && it !is LabelInstr } - } - - private fun optimizeCallReturnIntoJump() { - // replaces call X followed by return, by jump X - for(blk in blocks) { - val instructionsToReplace = mutableMapOf() - - blk.instructions.asSequence().withIndex().filter {it.value.opcode!= Opcode.LINE }.windowed(2).toList().forEach { - if(it[0].value.opcode== Opcode.CALL && it[1].value.opcode== Opcode.RETURN) { - instructionsToReplace[it[1].index] = Instruction(Opcode.JUMP, callLabel = it[0].value.callLabel) - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - } - } - - for (rins in instructionsToReplace) { - blk.instructions[rins.key] = rins.value - } - } - } - - private fun optimizeMultipleSequentialLineInstrs() { - for(blk in blocks) { - val instructionsToReplace = mutableMapOf() - - blk.instructions.asSequence().withIndex().windowed(2).toList().forEach { - if (it[0].value.opcode == Opcode.LINE && it[1].value.opcode == Opcode.LINE) - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - } - - for (rins in instructionsToReplace) { - blk.instructions[rins.key] = rins.value - } - } - } - - private fun optimizeVariableCopying() { - for(blk in blocks) { - - val instructionsToReplace = mutableMapOf() - - blk.instructions.asSequence().withIndex().windowed(2).toList().forEach { - when (it[0].value.opcode) { - Opcode.PUSH_VAR_BYTE -> - if (it[1].value.opcode == Opcode.POP_VAR_BYTE) { - if (it[0].value.callLabel == it[1].value.callLabel) { - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - } - Opcode.PUSH_VAR_WORD -> - if (it[1].value.opcode == Opcode.POP_VAR_WORD) { - if (it[0].value.callLabel == it[1].value.callLabel) { - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - } - Opcode.PUSH_VAR_FLOAT -> - if (it[1].value.opcode == Opcode.POP_VAR_FLOAT) { - if (it[0].value.callLabel == it[1].value.callLabel) { - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - } - Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB -> - if(it[1].value.opcode == Opcode.POP_MEM_BYTE) { - if(it[0].value.arg == it[1].value.arg) { - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - } - Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW -> - if(it[1].value.opcode == Opcode.POP_MEM_WORD) { - if(it[0].value.arg == it[1].value.arg) { - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - } - Opcode.PUSH_MEM_FLOAT -> - if(it[1].value.opcode == Opcode.POP_MEM_FLOAT) { - if(it[0].value.arg == it[1].value.arg) { - instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) - instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) - } - } - else -> {} - } - } - - for (rins in instructionsToReplace) { - blk.instructions[rins.key] = rins.value - } - } - } - - 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.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} following a float") - } - } - - fun optimizeWordConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) { - when (ins1.opcode) { - Opcode.CAST_UW_TO_B, Opcode.CAST_W_TO_B -> { - val ins = Instruction(Opcode.PUSH_BYTE, ins0.arg!!.cast(DataType.BYTE)) - instructionsToReplace[index0] = ins - instructionsToReplace[index1] = Instruction(Opcode.NOP) - } - Opcode.CAST_W_TO_UB, Opcode.CAST_UW_TO_UB -> { - val ins = Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, ins0.arg!!.integerValue() and 255)) - instructionsToReplace[index0] = ins - instructionsToReplace[index1] = Instruction(Opcode.NOP) - } - Opcode.MSB -> { - val ins = Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, ins0.arg!!.integerValue() ushr 8 and 255)) - instructionsToReplace[index0] = ins - instructionsToReplace[index1] = Instruction(Opcode.NOP) - } - Opcode.CAST_W_TO_F, Opcode.CAST_UW_TO_F -> { - val ins = Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, ins0.arg!!.integerValue().toDouble())) - instructionsToReplace[index0] = ins - instructionsToReplace[index1] = Instruction(Opcode.NOP) - } - Opcode.CAST_UW_TO_W -> { - val cv = ins0.arg!!.cast(DataType.WORD) - instructionsToReplace[index0] = Instruction(Opcode.PUSH_WORD, cv) - instructionsToReplace[index1] = Instruction(Opcode.NOP) - } - Opcode.CAST_W_TO_UW -> { - val cv = ins0.arg!!.cast(DataType.UWORD) - instructionsToReplace[index0] = Instruction(Opcode.PUSH_WORD, cv) - 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} following a word") - } - } - - fun optimizeByteConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) { - when (ins1.opcode) { - Opcode.CAST_B_TO_UB, Opcode.CAST_UB_TO_B, - Opcode.CAST_W_TO_B, Opcode.CAST_W_TO_UB, - Opcode.CAST_UW_TO_B, Opcode.CAST_UW_TO_UB -> instructionsToReplace[index1] = Instruction(Opcode.NOP) - Opcode.MSB -> throw CompilerException("msb of a byte") - Opcode.CAST_UB_TO_UW -> { - val ins = Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, ins0.arg!!.integerValue())) - instructionsToReplace[index0] = ins - instructionsToReplace[index1] = Instruction(Opcode.NOP) - } - Opcode.CAST_B_TO_W -> { - val ins = Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.WORD, ins0.arg!!.integerValue())) - instructionsToReplace[index0] = ins - instructionsToReplace[index1] = Instruction(Opcode.NOP) - } - Opcode.CAST_B_TO_UW -> { - val ins = Instruction(Opcode.PUSH_WORD, ins0.arg!!.cast(DataType.UWORD)) - instructionsToReplace[index0] = ins - instructionsToReplace[index1] = Instruction(Opcode.NOP) - } - Opcode.CAST_UB_TO_W -> { - val ins = Instruction(Opcode.PUSH_WORD, ins0.arg!!.cast(DataType.WORD)) - instructionsToReplace[index0] = ins - instructionsToReplace[index1] = Instruction(Opcode.NOP) - } - Opcode.CAST_B_TO_F, Opcode.CAST_UB_TO_F -> { - val ins = Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, ins0.arg!!.integerValue().toDouble())) - instructionsToReplace[index0] = ins - instructionsToReplace[index1] = Instruction(Opcode.NOP) - } - Opcode.CAST_W_TO_F, Opcode.CAST_UW_TO_F -> 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") - Opcode.MKWORD -> {} - else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}") - } - } - - for(blk in blocks) { - instructionsToReplace.clear() - - val typeConversionOpcodes = setOf( - Opcode.MSB, - Opcode.MKWORD, - Opcode.CAST_UB_TO_B, - Opcode.CAST_UB_TO_UW, - Opcode.CAST_UB_TO_W, - Opcode.CAST_UB_TO_F, - Opcode.CAST_B_TO_UB, - Opcode.CAST_B_TO_UW, - Opcode.CAST_B_TO_W, - Opcode.CAST_B_TO_F, - Opcode.CAST_UW_TO_UB, - Opcode.CAST_UW_TO_B, - Opcode.CAST_UW_TO_W, - Opcode.CAST_UW_TO_F, - Opcode.CAST_W_TO_UB, - Opcode.CAST_W_TO_B, - Opcode.CAST_W_TO_UW, - Opcode.CAST_W_TO_F, - Opcode.CAST_F_TO_UB, - Opcode.CAST_F_TO_B, - Opcode.CAST_F_TO_UW, - Opcode.CAST_F_TO_W, - Opcode.DISCARD_BYTE, - Opcode.DISCARD_WORD, - Opcode.DISCARD_FLOAT - ) - blk.instructions.asSequence().withIndex().windowed(2).toList().forEach { - if (it[1].value.opcode in typeConversionOpcodes) { - when (it[0].value.opcode) { - 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_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 -> { - } - } - } - } - - for (rins in instructionsToReplace) { - blk.instructions[rins.key] = rins.value - } - } - } - - fun variable(scopedname: String, decl: VarDecl) { - when(decl.type) { - VarDeclType.VAR -> { - // var decls that are defined inside of a StructDecl are skipped in the output - // because every occurrence of the members will have a separate mangled vardecl for that occurrence - if(decl.parent is StructDecl) - return - - val valueparams = VariableParameters(decl.zeropage, decl.struct) - val value = when(decl.datatype) { - in NumericDatatypes -> { - RuntimeValue(decl.datatype, (decl.value as NumericLiteralValue).number) - } - in StringDatatypes -> { - val litval = (decl.value as ReferenceLiteralValue) - if(litval.heapId==null) - throw CompilerException("string should already be in the heap") - RuntimeValue(decl.datatype, heapId = litval.heapId) - } - in ArrayDatatypes -> { - val litval = (decl.value as? ReferenceLiteralValue) - if(litval!=null && litval.heapId==null) - throw CompilerException("array should already be in the heap") - if(litval!=null){ - RuntimeValue(decl.datatype, heapId = litval.heapId) - } else { - throw CompilerException("initialization value expected") - } - } - DataType.STRUCT -> { - // struct variables have been flattened already - return - } - else -> throw CompilerException("weird datatype") - } - currentBlock.variables.add(Variable(scopedname, value, valueparams)) - } - VarDeclType.MEMORY -> { - // note that constants are all folded away, but assembly code may still refer to them - val lv = decl.value as NumericLiteralValue - if(lv.type!= DataType.UWORD && lv.type!= DataType.UBYTE) - throw CompilerException("expected integer memory address $lv") - currentBlock.memoryPointers[scopedname] = Pair(lv.number.toInt(), decl.datatype) - } - VarDeclType.CONST -> { - // note that constants are all folded away, but assembly code may still refer to them (if their integers) - // floating point constants are not generated at all!! - val lv = decl.value as NumericLiteralValue - if(lv.type in IntegerDatatypes) - currentBlock.memoryPointers[scopedname] = Pair(lv.number.toInt(), decl.datatype) - } - } - } - - fun instr(opcode: Opcode, arg: RuntimeValue? = null, arg2: RuntimeValue? = null, callLabel: String? = null, callLabel2: String? = null) { - currentBlock.instructions.add(Instruction(opcode, arg, arg2, callLabel, callLabel2)) - } - - fun label(labelname: String, asmProc: Boolean=false) { - val instr = LabelInstr(labelname, asmProc) - currentBlock.instructions.add(instr) - currentBlock.labels[labelname] = instr - } - - fun line(position: Position) { - currentBlock.instructions.add(Instruction(Opcode.LINE, callLabel = "${position.line} ${position.file}")) - } - - fun removeLastInstruction() { - currentBlock.instructions.removeAt(currentBlock.instructions.lastIndex) - } - - fun memoryPointer(name: String, address: Int, datatype: DataType) { - currentBlock.memoryPointers[name] = Pair(address, datatype) - } - - fun newBlock(name: String, address: Int?, options: Set) { - currentBlock = ProgramBlock(name, address, force_output = "force_output" in options) - blocks.add(currentBlock) - } - - fun writeCode(out: PrintStream, embeddedLabels: Boolean=true) { - out.println("; stackVM program code for '$name'") - writeMemory(out) - writeHeap(out) - for(blk in blocks) { - writeBlock(out, blk, embeddedLabels) - } - } - - private fun writeHeap(out: PrintStream) { - out.println("%heap") - heap.allEntries().forEach { - out.print("${it.key} ${it.value.type.name.toLowerCase()} ") - when { - it.value.str!=null -> - out.println("\"${escape(it.value.str!!)}\"") - it.value.array!=null -> { - // this array can contain both normal integers, and pointer values - val arrayvalues = it.value.array!!.map { av -> - when { - av.integer!=null -> av.integer.toString() - av.addressOf!=null -> { - if(av.addressOf.scopedname==null) - throw CompilerException("AddressOf scopedname should have been set") - else - "&${av.addressOf.scopedname}" - } - else -> throw CompilerException("weird array value") - } - } - out.println(arrayvalues) - } - it.value.doubleArray!=null -> - out.println(it.value.doubleArray!!.toList()) - else -> throw CompilerException("invalid heap entry $it") - } - } - out.println("%end_heap") - } - - private fun writeBlock(out: PrintStream, blk: ProgramBlock, embeddedLabels: Boolean) { - out.println("\n%block ${blk.name} ${blk.address?.toString(16) ?: ""}") - - out.println("%variables") - for (variable in blk.variables) { - if(variable.params.zp==ZeropageWish.REQUIRE_ZEROPAGE) - throw CompilerException("zp conflict") - val valuestr = variable.value.toString() - val struct = if(variable.params.memberOfStruct==null) "" else "struct=${variable.params.memberOfStruct.name}" - out.println("${variable.scopedname} ${variable.value.type.name.toLowerCase()} $valuestr zp=${variable.params.zp} s=$struct") - } - out.println("%end_variables") - out.println("%memorypointers") - for (iconst in blk.memoryPointers) { - out.println("${iconst.key} ${iconst.value.second.name.toLowerCase()} uw:${iconst.value.first.toString(16)}") - } - out.println("%end_memorypointers") - out.println("%instructions") - val labels = blk.labels.entries.associateBy({ it.value }) { it.key } - for (instr in blk.instructions) { - if (!embeddedLabels) { - val label = labels[instr] - if (label != null) - out.println("$label:") - } else { - out.println(instr) - } - } - out.println("%end_instructions") - - out.println("%end_block") - } - - private fun writeMemory(out: PrintStream) { - out.println("%memory") - if (memory.isNotEmpty()) - TODO("add support for writing/reading initial memory values") - out.println("%end_memory") - } -} diff --git a/DeprecatedStackVm/src/compiler/intermediate/Opcode.kt b/DeprecatedStackVm/src/compiler/intermediate/Opcode.kt deleted file mode 100644 index f2fbe0c94..000000000 --- a/DeprecatedStackVm/src/compiler/intermediate/Opcode.kt +++ /dev/null @@ -1,291 +0,0 @@ -package compiler.intermediate - -enum class Opcode { - - // pushing values on the (evaluation) stack - PUSH_BYTE, // push byte value - PUSH_WORD, // push word value (or 'address' of string / array) - PUSH_FLOAT, // push float value - PUSH_MEM_B, // push byte value from memory to stack - PUSH_MEM_UB, // push unsigned byte value from memory to stack - PUSH_MEM_W, // push word value from memory to stack - PUSH_MEM_UW, // push unsigned word value from memory to stack - PUSH_MEM_FLOAT, // push float value from memory to stack - PUSH_MEMREAD, // push memory value from address that's on the stack - PUSH_VAR_BYTE, // push byte variable (ubyte, byte) - PUSH_VAR_WORD, // push word variable (uword, word) - PUSH_VAR_FLOAT, // push float variable - PUSH_REGAX_WORD, // push registers A/X as a 16-bit word - PUSH_REGAY_WORD, // push registers A/Y as a 16-bit word - PUSH_REGXY_WORD, // push registers X/Y as a 16-bit word - PUSH_ADDR_HEAPVAR, // push the address of the variable that's on the heap (string or array) - DUP_B, // duplicate the top byte on the stack - DUP_W, // duplicate the top word on the stack - - // 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 (u)byte value into destination memory address - POP_MEM_WORD, // pop (u)word value into destination memory address - POP_MEM_FLOAT, // pop float value into destination memory address - POP_MEMWRITE, // pop address and byte stack and write the byte to the memory address - POP_VAR_BYTE, // pop (u)byte value into variable - POP_VAR_WORD, // pop (u)word value into variable - POP_VAR_FLOAT, // pop float value into variable - POP_REGAX_WORD, // pop uword from stack into A/X registers - POP_REGAY_WORD, // pop uword from stack into A/Y registers - POP_REGXY_WORD, // pop uword from stack into X/Y registers - - // numeric arithmetic - ADD_UB, - ADD_B, - ADD_UW, - ADD_W, - ADD_F, - SUB_UB, - SUB_B, - SUB_UW, - SUB_W, - SUB_F, - MUL_UB, - MUL_B, - MUL_UW, - MUL_W, - MUL_F, - IDIV_UB, - IDIV_B, - IDIV_UW, - IDIV_W, - DIV_F, - REMAINDER_UB, // signed remainder is undefined/unimplemented - REMAINDER_UW, // signed remainder is undefined/unimplemented - POW_F, - NEG_B, - NEG_W, - NEG_F, - ABS_B, - ABS_W, - ABS_F, - - // bit shifts and bitwise arithmetic - SHIFTEDL_BYTE, // shifts stack value rather than in-place mem/var - SHIFTEDL_WORD, // shifts stack value rather than in-place mem/var - SHIFTEDR_UBYTE, // shifts stack value rather than in-place mem/var - SHIFTEDR_SBYTE, // shifts stack value rather than in-place mem/var - SHIFTEDR_UWORD, // shifts stack value rather than in-place mem/var - SHIFTEDR_SWORD, // shifts stack value rather than in-place mem/var - SHL_BYTE, - SHL_WORD, - SHL_MEM_BYTE, - SHL_MEM_WORD, - SHL_VAR_BYTE, - SHL_VAR_WORD, - SHR_UBYTE, - SHR_SBYTE, - SHR_UWORD, - SHR_SWORD, - SHR_MEM_UBYTE, - SHR_MEM_SBYTE, - SHR_MEM_UWORD, - SHR_MEM_SWORD, - SHR_VAR_UBYTE, - SHR_VAR_SBYTE, - SHR_VAR_UWORD, - SHR_VAR_SWORD, - ROL_BYTE, - ROL_WORD, - ROL_MEM_BYTE, - ROL_MEM_WORD, - ROL_VAR_BYTE, - ROL_VAR_WORD, - ROR_BYTE, - ROR_WORD, - ROR_MEM_BYTE, - ROR_MEM_WORD, - ROR_VAR_BYTE, - ROR_VAR_WORD, - ROL2_BYTE, - ROL2_WORD, - ROL2_MEM_BYTE, - ROL2_MEM_WORD, - ROL2_VAR_BYTE, - ROL2_VAR_WORD, - ROR2_BYTE, - ROR2_WORD, - ROR2_MEM_BYTE, - ROR2_MEM_WORD, - ROR2_VAR_BYTE, - ROR2_VAR_WORD, - BITAND_BYTE, - BITAND_WORD, - BITOR_BYTE, - BITOR_WORD, - BITXOR_BYTE, - BITXOR_WORD, - INV_BYTE, - INV_WORD, - - // numeric type conversions - MSB, // note: lsb is equivalent to CAST_UW_TO_UB or CAST_W_TO_UB - MKWORD, // create a word from lsb + msb - CAST_UB_TO_B, - CAST_UB_TO_UW, - CAST_UB_TO_W, - CAST_UB_TO_F, - CAST_B_TO_UB, - CAST_B_TO_UW, - CAST_B_TO_W, - CAST_B_TO_F, - CAST_W_TO_UB, - CAST_W_TO_B, - CAST_W_TO_UW, - CAST_W_TO_F, - CAST_UW_TO_UB, - CAST_UW_TO_B, - CAST_UW_TO_W, - CAST_UW_TO_F, - CAST_F_TO_UB, - CAST_F_TO_B, - CAST_F_TO_UW, - CAST_F_TO_W, - - // logical operations - AND_BYTE, - AND_WORD, - OR_BYTE, - OR_WORD, - XOR_BYTE, - XOR_WORD, - NOT_BYTE, - NOT_WORD, - - // increment, decrement - INC_VAR_B, - INC_VAR_UB, - INC_VAR_W, - INC_VAR_UW, - INC_VAR_F, - DEC_VAR_B, - DEC_VAR_UB, - DEC_VAR_W, - DEC_VAR_UW, - DEC_VAR_F, - INC_MEMORY, // increment direct address - DEC_MEMORY, // decrement direct address - POP_INC_MEMORY, // increment address from stack - POP_DEC_MEMORY, // decrement address from address - - // comparisons - LESS_B, - LESS_UB, - LESS_W, - LESS_UW, - LESS_F, - GREATER_B, - GREATER_UB, - GREATER_W, - GREATER_UW, - GREATER_F, - LESSEQ_B, - LESSEQ_UB, - LESSEQ_W, - LESSEQ_UW, - LESSEQ_F, - GREATEREQ_B, - GREATEREQ_UB, - GREATEREQ_W, - GREATEREQ_UW, - GREATEREQ_F, - EQUAL_BYTE, - EQUAL_WORD, - EQUAL_F, - NOTEQUAL_BYTE, - NOTEQUAL_WORD, - NOTEQUAL_F, - CMP_B, // sets processor status flags based on comparison, instead of pushing a result value - CMP_UB, // sets processor status flags based on comparison, instead of pushing a result value - CMP_W, // sets processor status flags based on comparison, instead of pushing a result value - CMP_UW, // sets processor status flags based on comparison, instead of pushing a result value - - // array access and simple manipulations - READ_INDEXED_VAR_BYTE, - READ_INDEXED_VAR_WORD, - READ_INDEXED_VAR_FLOAT, - WRITE_INDEXED_VAR_BYTE, - WRITE_INDEXED_VAR_WORD, - WRITE_INDEXED_VAR_FLOAT, - INC_INDEXED_VAR_B, - INC_INDEXED_VAR_UB, - INC_INDEXED_VAR_W, - INC_INDEXED_VAR_UW, - INC_INDEXED_VAR_FLOAT, - DEC_INDEXED_VAR_B, - DEC_INDEXED_VAR_UB, - DEC_INDEXED_VAR_W, - DEC_INDEXED_VAR_UW, - DEC_INDEXED_VAR_FLOAT, - - // branching, without consuming a value from the stack - JUMP, - BCS, // branch if carry set - BCC, // branch if carry clear - BZ, // branch if zero flag - BNZ, // branch if not zero flag - BNEG, // branch if negative flag - BPOS, // branch if not negative flag - BVS, // branch if overflow flag - BVC, // branch if not overflow flag - // branching, based on value on the stack (which is consumed) - JZ, // branch if value is zero (byte) - JNZ, // branch if value is not zero (byte) - JZW, // branch if value is zero (word) - JNZW, // branch if value is not zero (word) - - // subroutines - CALL, - RETURN, - SYSCALL, - START_PROCDEF, - END_PROCDEF, - - // misc - 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 - CLI, // clear irq-disable status flag - CARRY_TO_A, // load var/register A with carry status bit - RSAVE, // save all internal registers and status flags - RSAVEX, // save just X (the evaluation stack pointer) - RRESTORE, // restore all internal registers and status flags - RRESTOREX, // restore just X (the evaluation stack pointer) - - NOP, // do nothing - BREAKPOINT, // breakpoint - TERMINATE, // end the program - LINE, // track source file line number - INLINE_ASSEMBLY, // container to hold inline raw assembly code - INCLUDE_FILE // directive to include a file at this position in the memory of the program -} - -val opcodesWithVarArgument = setOf( - Opcode.INC_VAR_B, Opcode.INC_VAR_W, Opcode.DEC_VAR_B, Opcode.DEC_VAR_W, - Opcode.INC_VAR_UB, Opcode.INC_VAR_UW, Opcode.DEC_VAR_UB, Opcode.DEC_VAR_UW, - Opcode.SHR_VAR_SBYTE, Opcode.SHR_VAR_UBYTE, Opcode.SHR_VAR_SWORD, Opcode.SHR_VAR_UWORD, - Opcode.SHL_VAR_BYTE, Opcode.SHL_VAR_WORD, - Opcode.ROL_VAR_BYTE, Opcode.ROL_VAR_WORD, Opcode.ROR_VAR_BYTE, Opcode.ROR_VAR_WORD, - Opcode.ROL2_VAR_BYTE, Opcode.ROL2_VAR_WORD, Opcode.ROR2_VAR_BYTE, Opcode.ROR2_VAR_WORD, - Opcode.POP_VAR_BYTE, Opcode.POP_VAR_WORD, Opcode.POP_VAR_FLOAT, - Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_FLOAT, Opcode.PUSH_ADDR_HEAPVAR, - Opcode.READ_INDEXED_VAR_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.READ_INDEXED_VAR_FLOAT, - Opcode.WRITE_INDEXED_VAR_BYTE, Opcode.WRITE_INDEXED_VAR_WORD, Opcode.WRITE_INDEXED_VAR_FLOAT, - Opcode.INC_INDEXED_VAR_UB, Opcode.INC_INDEXED_VAR_B, Opcode.INC_INDEXED_VAR_UW, - Opcode.INC_INDEXED_VAR_W, Opcode.INC_INDEXED_VAR_FLOAT, - Opcode.DEC_INDEXED_VAR_UB, Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UW, - Opcode.DEC_INDEXED_VAR_W, Opcode.DEC_INDEXED_VAR_FLOAT -) - -val branchOpcodes = setOf( - Opcode.BCS, Opcode.BCC, Opcode.BZ, Opcode.BNZ, - Opcode.BNEG, Opcode.BPOS, Opcode.BVS, Opcode.BVC -) diff --git a/DeprecatedStackVm/src/prog8/vm/stackvm/Main.kt b/DeprecatedStackVm/src/prog8/vm/stackvm/Main.kt deleted file mode 100644 index 754a8773a..000000000 --- a/DeprecatedStackVm/src/prog8/vm/stackvm/Main.kt +++ /dev/null @@ -1,47 +0,0 @@ -package prog8.vm.stackvm - -import prog8.printSoftwareHeader -import prog8.vm.astvm.ScreenDialog -import java.awt.EventQueue -import javax.swing.Timer -import kotlin.system.exitProcess - -fun main(args: Array) { - stackVmMain(args) -} - -fun stackVmMain(args: Array) { - printSoftwareHeader("StackVM") - - if(args.size != 1) { - System.err.println("requires one argument: name of stackvm sourcecode file") - exitProcess(1) - } - - val program = Program.load(args.first()) - val vm = StackVm(traceOutputFile = null) - val dialog = ScreenDialog("StackVM") - vm.load(program, dialog.canvas) - EventQueue.invokeLater { - dialog.pack() - dialog.isVisible = true - dialog.start() - - val programTimer = Timer(10) { a -> - try { - vm.step() - } catch(bp: VmBreakpointException) { - println("Breakpoint: execution halted. Press enter to resume.") - readLine() - } catch (tx: VmTerminationException) { - println("Execution halted: ${tx.message}") - (a.source as Timer).stop() - } - } - - val irqTimer = Timer(1000/60) { a -> vm.irq(a.`when`) } - - programTimer.start() - irqTimer.start() - } -} diff --git a/DeprecatedStackVm/src/prog8/vm/stackvm/Program.kt b/DeprecatedStackVm/src/prog8/vm/stackvm/Program.kt deleted file mode 100644 index 3ef83e18b..000000000 --- a/DeprecatedStackVm/src/prog8/vm/stackvm/Program.kt +++ /dev/null @@ -1,302 +0,0 @@ -package prog8.vm.stackvm - -import prog8.ast.antlr.unescape -import prog8.ast.base.* -import prog8.ast.expressions.AddressOf -import prog8.ast.expressions.IdentifierReference -import prog8.compiler.HeapValues -import prog8.compiler.IntegerOrAddressOf -import prog8.compiler.intermediate.Instruction -import prog8.compiler.intermediate.LabelInstr -import prog8.compiler.intermediate.Opcode -import prog8.compiler.intermediate.opcodesWithVarArgument -import prog8.vm.RuntimeValue -import java.io.File -import java.util.regex.Pattern - - -class Program (val name: String, - val program: MutableList, - val variables: Map, - val memoryPointers: Map>, - val labels: Map, - val memory: Map>, - val heap: HeapValues) -{ - init { - // add end of program marker and some sentinel instructions, to correctly connect all others - program.add(LabelInstr("____program_end", false)) - program.add(Instruction(Opcode.TERMINATE)) - program.add(Instruction(Opcode.NOP)) - } - - companion object { - fun load(filename: String): Program { - val lines = File(filename).readLines().withIndex().iterator() - val memory = mutableMapOf>() - val heap = HeapValues() - val program = mutableListOf() - val variables = mutableMapOf() - val memoryPointers = mutableMapOf>() - val labels = mutableMapOf() - - while(lines.hasNext()) { - val (lineNr, line) = lines.next() - if(line.startsWith(';') || line.isEmpty()) - continue - else if(line=="%memory") - loadMemory(lines, memory) - else if(line=="%heap") - loadHeap(lines, heap) - else if(line.startsWith("%block ")) - loadBlock(lines, heap, program, variables, memoryPointers, labels) - else throw VmExecutionException("syntax error at line ${lineNr + 1}") - } - return Program(filename, program, variables, memoryPointers, labels, memory, heap) - } - - private fun loadBlock(lines: Iterator>, - heap: HeapValues, - program: MutableList, - variables: MutableMap, - memoryPointers: MutableMap>, - labels: MutableMap) - { - while(true) { - val (_, line) = lines.next() - if(line.isEmpty()) - continue - else if(line=="%end_block") - return - else if(line=="%variables") - loadVars(lines, variables) - else if(line=="%memorypointers") - loadMemoryPointers(lines, memoryPointers, heap) - else if(line=="%instructions") { - val (blockInstructions, blockLabels) = loadInstructions(lines, heap) - val baseIndex = program.size - program.addAll(blockInstructions) - val labelsWithIndex = blockLabels.mapValues { baseIndex+blockInstructions.indexOf(it.value) } - labels.putAll(labelsWithIndex) - } - } - } - - private fun loadHeap(lines: Iterator>, heap: HeapValues) { - val splitpattern = Pattern.compile("\\s+") - val heapvalues = mutableListOf>() - while(true) { - val (_, line) = lines.next() - if (line == "%end_heap") - break - val parts = line.split(splitpattern, limit=3) - val value = Triple(parts[0].toInt(), DataType.valueOf(parts[1].toUpperCase()), parts[2]) - heapvalues.add(value) - } - heapvalues.sortedBy { it.first }.forEach { - when(it.second) { - DataType.STR, DataType.STR_S -> heap.addString(it.second, unescape(it.third.substring(1, it.third.length - 1), Position("", 0, 0, 0))) - DataType.ARRAY_UB, DataType.ARRAY_B, - DataType.ARRAY_UW, DataType.ARRAY_W -> { - val numbers = it.third.substring(1, it.third.length-1).split(',') - val intarray = numbers.map{number-> - val num=number.trim() - if(num.startsWith("&")) { - // it's AddressOf - val scopedname = num.substring(1) - val iref = IdentifierReference(scopedname.split('.'), Position("", 0, 0, 0)) - val addrOf = AddressOf(iref, Position("", 0, 0, 0)) - addrOf.scopedname=scopedname - IntegerOrAddressOf(null, addrOf) - } else { - IntegerOrAddressOf(num.toInt(), null) - } - }.toTypedArray() - heap.addIntegerArray(it.second, intarray) - } - DataType.ARRAY_F -> { - val numbers = it.third.substring(1, it.third.length-1).split(',') - val doublearray = numbers.map{number->number.trim().toDouble()}.toDoubleArray() - heap.addDoublesArray(doublearray) - } - in NumericDatatypes -> throw VmExecutionException("invalid heap value type ${it.second}") - else -> throw VmExecutionException("weird datatype") - } - } - } - - private fun loadInstructions(lines: Iterator>, heap: HeapValues): Pair, Map> { - val instructions = mutableListOf() - val labels = mutableMapOf() - val splitpattern = Pattern.compile("\\s+") - val nextInstructionLabels = Stack() // more than one label can occur on the isSameAs line - - while(true) { - val (lineNr, line) = lines.next() - if(line.isEmpty()) - continue - if(line=="%end_instructions") - return Pair(instructions, labels) - if(!line.startsWith(' ') && line.endsWith(':')) { - nextInstructionLabels.push(line.substring(0, line.length-1)) - } else if(line.startsWith(' ')) { - val parts = line.trimStart().split(splitpattern, limit = 2) - val opcodeStr = parts[0].toUpperCase() - val opcode= Opcode.valueOf(if(opcodeStr.startsWith('_')) opcodeStr.substring(1) else opcodeStr) - val args = if(parts.size==2) parts[1] else null - val instruction = when(opcode) { - Opcode.LINE -> Instruction(opcode, null, callLabel = args) - Opcode.JUMP, Opcode.CALL, Opcode.BNEG, Opcode.BPOS, - Opcode.BZ, Opcode.BNZ, Opcode.BCS, Opcode.BCC, - Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW -> { - if(args!!.startsWith('$')) { - Instruction(opcode, RuntimeValue(DataType.UWORD, args.substring(1).toInt(16))) - } else { - Instruction(opcode, callLabel = args) - } - } - in opcodesWithVarArgument -> { - val withoutQuotes = - if(args!!.startsWith('"') && args.endsWith('"')) - args.substring(1, args.length-1) else args - - Instruction(opcode, callLabel = withoutQuotes) - } - Opcode.SYSCALL -> { - if(args!! in syscallNames) { - val call = Syscall.valueOf(args) - Instruction(opcode, RuntimeValue(DataType.UBYTE, call.callNr)) - } else { - val args2 = args.replace('.', '_') - if(args2 in syscallNames) { - val call = Syscall.valueOf(args2) - Instruction(opcode, RuntimeValue(DataType.UBYTE, call.callNr)) - } else { - // the syscall is not yet implemented. emit a stub. - Instruction(Opcode.SYSCALL, RuntimeValue(DataType.UBYTE, Syscall.SYSCALLSTUB.callNr), callLabel = args2) - } - } - } - Opcode.INCLUDE_FILE -> { - val argparts = args!!.split(' ') - val filename = argparts[0] - val offset = if(argparts.size>=2 && argparts[1]!="null") getArgValue(argparts[1], heap) else null - val length = if(argparts.size>=3 && argparts[2]!="null") getArgValue(argparts[2], heap) else null - Instruction(opcode, offset, length, filename) - } - else -> { - Instruction(opcode, getArgValue(args, heap)) - } - } - instructions.add(instruction) - while(nextInstructionLabels.isNotEmpty()) { - val label = nextInstructionLabels.pop() - labels[label] = instruction - } - } else throw VmExecutionException("syntax error at line ${lineNr + 1}") - } - } - - private fun getArgValue(args: String?, heap: HeapValues): RuntimeValue? { - if(args==null) - return null - if(args[0]=='"' && args[args.length-1]=='"') { - throw VmExecutionException("encountered a string arg value, but all strings should already have been moved into the heap") - } - val (type, valueStr) = args.split(':') - return when(type) { - "b" -> RuntimeValue(DataType.BYTE, valueStr.toShort(16)) - "ub" -> RuntimeValue(DataType.UBYTE, valueStr.toShort(16)) - "w" -> RuntimeValue(DataType.WORD, valueStr.toInt(16)) - "uw" -> RuntimeValue(DataType.UWORD, valueStr.toInt(16)) - "f" -> RuntimeValue(DataType.FLOAT, valueStr.toDouble()) - "heap" -> { - val heapId = valueStr.toInt() - RuntimeValue(heap.get(heapId).type, heapId = heapId) - } - else -> throw VmExecutionException("invalid datatype $type") - } - } - - private fun loadVars(lines: Iterator>, - vars: MutableMap) { - val splitpattern = Pattern.compile("\\s+") - while(true) { - val (_, line) = lines.next() - if(line=="%end_variables") - return - val (name, typeStr, valueStr) = line.split(splitpattern, limit = 3) - if(valueStr[0] !='"' && ':' !in valueStr) - throw VmExecutionException("missing value type character") - val value = when(val type = DataType.valueOf(typeStr.toUpperCase())) { - DataType.UBYTE -> RuntimeValue(DataType.UBYTE, valueStr.substring(3).substringBefore(' ').toShort(16))// TODO process ZP and struct info? - DataType.BYTE -> RuntimeValue(DataType.BYTE, valueStr.substring(2).substringBefore(' ').toShort(16))// TODO process ZP and struct info? - DataType.UWORD -> RuntimeValue(DataType.UWORD, valueStr.substring(3).substringBefore(' ').toInt(16))// TODO process ZP and struct info? - DataType.WORD -> RuntimeValue(DataType.WORD, valueStr.substring(2).substringBefore(' ').toInt(16))// TODO process ZP and struct info? - DataType.FLOAT -> RuntimeValue(DataType.FLOAT, valueStr.substring(2).substringBefore(' ').toDouble())// TODO process ZP and struct info? - in StringDatatypes -> { - if(valueStr.startsWith('"') && valueStr.endsWith('"')) - throw VmExecutionException("encountered a var with a string value, but all string values should already have been moved into the heap") - else if(!valueStr.startsWith("heap:")) - throw VmExecutionException("invalid string value, should be a heap reference") - else { - val heapId = valueStr.substring(5).substringBefore(' ').toInt() // TODO process ZP and struct info? - RuntimeValue(type, heapId = heapId) - } - } - in ArrayDatatypes -> { - if(!valueStr.startsWith("heap:")) - throw VmExecutionException("invalid array value, should be a heap reference") - else { - val heapId = valueStr.substring(5).substringBefore(' ').toInt() // TODO process ZP and struct info? - RuntimeValue(type, heapId = heapId) - } - } - else -> throw VmExecutionException("weird datatype") - } - vars[name] = value - } - } - - private fun loadMemoryPointers(lines: Iterator>, - pointers: MutableMap>, - heap: HeapValues) { - val splitpattern = Pattern.compile("\\s+") - while(true) { - val (_, line) = lines.next() - if(line=="%end_memorypointers") - return - val (name, typeStr, valueStr) = line.split(splitpattern, limit = 3) - if(valueStr[0] !='"' && ':' !in valueStr) - throw VmExecutionException("missing value type character") - val type = DataType.valueOf(typeStr.toUpperCase()) - val value = getArgValue(valueStr, heap)!!.integerValue() - pointers[name] = Pair(value, type) - } - } - - private fun loadMemory(lines: Iterator>, memory: MutableMap>): Map> { - while(true) { - val (lineNr, line) = lines.next() - if(line=="%end_memory") - return memory - val address = line.substringBefore(' ').toInt(16) - val rest = line.substringAfter(' ').trim() - if(rest.startsWith('"')) { - TODO("memory init with char/string") - } else { - val valueStrings = rest.split(' ') - val values = mutableListOf() - valueStrings.forEach { - when(it.length) { - 2 -> values.add(RuntimeValue(DataType.UBYTE, it.toShort(16))) - 4 -> values.add(RuntimeValue(DataType.UWORD, it.toInt(16))) - else -> throw VmExecutionException("invalid value at line $lineNr+1") - } - } - memory[address] = values - } - } - } - } -} diff --git a/DeprecatedStackVm/src/prog8/vm/stackvm/StackVm.kt b/DeprecatedStackVm/src/prog8/vm/stackvm/StackVm.kt deleted file mode 100644 index c58fe1cac..000000000 --- a/DeprecatedStackVm/src/prog8/vm/stackvm/StackVm.kt +++ /dev/null @@ -1,2438 +0,0 @@ -package prog8.vm.stackvm - -import prog8.ast.base.* -import prog8.compiler.HeapValues -import prog8.compiler.IntegerOrAddressOf -import prog8.compiler.intermediate.Instruction -import prog8.compiler.intermediate.Opcode -import prog8.compiler.target.c64.Petscii -import prog8.compiler.toHex -import prog8.vm.RuntimeValue -import prog8.vm.astvm.BitmapScreenPanel -import prog8.vm.astvm.Memory -import java.io.File -import java.io.PrintStream -import kotlin.math.* - - -internal enum class Syscall(val callNr: Short) { - VM_WRITE_MEMCHR(10), // print a single char from the memory address popped from stack - VM_WRITE_MEMSTR(11), // print a 0-terminated petscii string from the memory address popped from stack - VM_WRITE_NUM(12), // pop from the evaluation stack and print it as a number - VM_WRITE_CHAR(13), // pop from the evaluation stack and print it as a single petscii character - VM_WRITE_STR(14), // pop from the evaluation stack and print it as a string - VM_INPUT_STR(15), // user input a string onto the stack, with max length (truncated) given by value on stack - VM_GFX_PIXEL(16), // plot a pixel at (x,y,color) pushed on stack in that order - VM_GFX_CLEARSCR(17), // clear the screen with color pushed on stack - VM_GFX_TEXT(18), // write text on screen at cursor position (x,y,color,text) pushed on stack in that order (pixel pos= x*8, y*8) - VM_GFX_LINE(19), // draw line on screen at (x1,y1,x2,y2,color) pushed on stack in that order - - FUNC_SIN(60), - FUNC_SIN8(61), - FUNC_SIN8U(62), - FUNC_SIN16(63), - FUNC_SIN16U(64), - FUNC_COS(65), - FUNC_COS8(66), - FUNC_COS8U(67), - FUNC_COS16(68), - FUNC_COS16U(69), - FUNC_ABS(70), - FUNC_TAN(71), - FUNC_ATAN(72), - FUNC_LN(73), - FUNC_LOG2(74), - FUNC_SQRT16(75), - FUNC_SQRT(76), - FUNC_RAD(77), - FUNC_DEG(78), - FUNC_ROUND(79), - FUNC_FLOOR(80), - FUNC_CEIL(81), - FUNC_RND(89), // push a random byte on the stack - FUNC_RNDW(90), // push a random word on the stack - FUNC_RNDF(91), // push a random float on the stack (between 0.0 and 1.0) - FUNC_LEN_STR(105), - FUNC_LEN_STRS(106), - FUNC_STRLEN(107), - FUNC_ANY_B(109), - FUNC_ANY_W(110), - FUNC_ANY_F(111), - FUNC_ALL_B(112), - FUNC_ALL_W(113), - FUNC_ALL_F(114), - FUNC_MAX_UB(115), - FUNC_MAX_B(116), - FUNC_MAX_UW(117), - FUNC_MAX_W(118), - FUNC_MAX_F(119), - FUNC_MIN_UB(120), - FUNC_MIN_B(121), - FUNC_MIN_UW(122), - FUNC_MIN_W(123), - FUNC_MIN_F(124), - FUNC_SUM_UB(130), - FUNC_SUM_B(131), - FUNC_SUM_UW(132), - FUNC_SUM_W(133), - FUNC_SUM_F(134), - FUNC_MEMCOPY(138), - FUNC_MEMSET(139), - FUNC_MEMSETW(140), - FUNC_READ_FLAGS(141), - - // note: not all builtin functions of the Prog8 language are present as functions: - // some of them are straight opcodes (such as MSB, LSB, LSL, LSR, ROL_BYTE, ROR, ROL2, ROR2, and FLT)! - - // vm intercepts of system routines: - SYSCALLSTUB(200), - SYSASM_c64scr_plot(201), - SYSASM_c64scr_print(202), - SYSASM_c64scr_print_ub0(203), - SYSASM_c64scr_print_ub(204), - SYSASM_c64scr_print_b(205), - SYSASM_c64scr_print_ubhex(206), - SYSASM_c64scr_print_ubbin(207), - SYSASM_c64scr_print_uwbin(208), - SYSASM_c64scr_print_uwhex(209), - SYSASM_c64scr_print_uw0(210), - SYSASM_c64scr_print_uw(211), - SYSASM_c64scr_print_w(212), - SYSASM_c64scr_setcc(213), - SYSASM_c64flt_print_f(214), -} - -internal val syscallNames = enumValues().map { it.name }.toSet() - -internal val syscallsForStackVm = setOf( - Syscall.VM_WRITE_MEMCHR, - Syscall.VM_WRITE_MEMSTR, - Syscall.VM_WRITE_NUM, - Syscall.VM_WRITE_CHAR, - Syscall.VM_WRITE_STR, - Syscall.VM_INPUT_STR, - Syscall.VM_GFX_PIXEL, - Syscall.VM_GFX_CLEARSCR, - Syscall.VM_GFX_TEXT, - Syscall.VM_GFX_LINE -) - -internal class VmExecutionException(msg: String?) : Exception(msg) - -internal class VmTerminationException(msg: String?) : Exception(msg) - -internal class VmBreakpointException : Exception("breakpoint") - -internal class MyStack : Stack() { - fun peek(amount: Int) : List { - return this.toList().subList(max(0, size-amount), size) - } - - fun pop2() : Pair = Pair(pop(), pop()) - - fun printTop(amount: Int, output: PrintStream) { - peek(amount).reversed().forEach { output.println(" $it") } - } -} - -class StackVm(private var traceOutputFile: String?) { - val mem = Memory(::memread, ::memwrite) - var P_carry: Boolean = false - private set - var P_zero: Boolean = true - private set - var P_negative: Boolean = false - private set - var P_irqd: Boolean = false - private set - var variables = mutableMapOf() // all variables (set of all vars used by all blocks/subroutines) key = their fully scoped name - private set - var memoryPointers = mutableMapOf>() // all named pointers - private set - internal var evalstack = MyStack() - private set - internal var callstack = MyStack() - private set - private var program = listOf() - private var labels = emptyMap() - private var heap = HeapValues() - private var traceOutput = if(traceOutputFile!=null) PrintStream(File(traceOutputFile), "utf-8") else null - private var canvas: BitmapScreenPanel? = null - private val rnd = Random() - private val bootTime = System.currentTimeMillis() - private var rtcOffset = bootTime - private var currentInstructionPtr: Int = -1 - private var irqStartInstructionPtr: Int = -1 - private var registerSaveX: RuntimeValue = RuntimeValue(DataType.UBYTE, 0) - var sourceLine: String = "" - private set - - fun memread(address: Int, value: Short): Short { - //println("MEM READ $address -> $value") - return value - } - - fun memwrite(address: Int, value: Short): Short { - if(address==0xa0 || address==0xa1 || address==0xa2) { - // a write to the jiffy clock, update the clock offset for the irq - val time_hi = if(address==0xa0) value else mem.getUByte_DMA(0xa0) - val time_mid = if(address==0xa1) value else mem.getUByte_DMA(0xa1) - val time_lo = if(address==0xa2) value else mem.getUByte_DMA(0xa2) - val jiffies = (time_hi.toInt() shl 16) + (time_mid.toInt() shl 8) + time_lo - rtcOffset = bootTime - (jiffies*1000/60) - } - return value - } - - fun load(program: Program, canvas: BitmapScreenPanel?) { - this.program = program.program + Instruction(Opcode.RETURN) // append a RETURN for use in the IRQ handler - this.labels = program.labels - this.heap = program.heap - this.canvas = canvas - canvas?.requestFocusInWindow() - variables = program.variables.toMutableMap() - memoryPointers = program.memoryPointers.toMutableMap() - - if("A" in variables || "X" in variables || "Y" in variables) - throw VmExecutionException("program contains variable(s) for the reserved registers A/X/Y") - // define the 'registers' - variables["A"] = RuntimeValue(DataType.UBYTE, 0) - variables["X"] = RuntimeValue(DataType.UBYTE, 255) - variables["Y"] = RuntimeValue(DataType.UBYTE, 0) - - initMemory(program.memory) - evalstack.clear() - callstack.clear() - P_carry = false - P_irqd = false - sourceLine = "" - currentInstructionPtr = 0 - irqStartInstructionPtr = labels["irq.irq"] ?: -1 // set to first instr of irq routine, if any - - initBlockVars() - } - - private fun initBlockVars() { - // initialize the global variables in each block. - // this is done by calling the special init subroutine of each block that has one. - val initVarsSubs = labels.filter { it.key.endsWith("."+ initvarsSubName) } - for(init in initVarsSubs) { - currentInstructionPtr = init.value - try { - step(Int.MAX_VALUE) - } catch(x: VmTerminationException) { - // init subroutine finished - } - } - currentInstructionPtr = 0 - } - - fun step(instructionCount: Int = 5000) { - // step is invoked every 1/100 sec - // we execute 5k instructions in one go so we end up doing 0.5 million vm instructions per second - val start = System.currentTimeMillis() - for(i:Int in 1..instructionCount) { - try { - dispatch() - if (evalstack.size > 128) - throw VmExecutionException("too many values on evaluation stack") - if (callstack.size > 128) - throw VmExecutionException("too many nested/recursive calls") - } catch (bp: VmBreakpointException) { - println("breakpoint encountered, source line: $sourceLine") - throw bp - } catch (es: EmptyStackException) { - System.err.println("stack error! source line: $sourceLine") - throw es - } catch (x: RuntimeException) { - System.err.println("runtime error! source line: $sourceLine") - throw x - } - } - val time = System.currentTimeMillis()-start - if(time > 100) { - println("WARNING: vm dispatch step took > 100 msec") - } - } - - private fun initMemory(memory: Map>) { - mem.clear() - - for (meminit in memory) { - var address = meminit.key - for (value in meminit.value) { - when(value.type) { - DataType.UBYTE -> { - mem.setUByte(address, value.integerValue().toShort()) - address += 1 - } - DataType.BYTE -> { - mem.setSByte(address, value.integerValue().toShort()) - address += 1 - } - DataType.UWORD -> { - mem.setUWord(address, value.integerValue()) - address += 2 - } - DataType.WORD -> { - mem.setSWord(address, value.integerValue()) - address += 2 - } - DataType.FLOAT -> { - mem.setFloat(address, value.numericValue().toDouble()) - address += 5 - } - DataType.STR -> { - TODO("mem init with string") - //mem.setString(address, value.stringvalue!!) - //address += value.stringvalue.length+1 - } - else -> throw VmExecutionException("invalid mem datatype ${value.type}") - } - } - } - - // observe the jiffyclock - mem.observe(0xa0, 0xa1, 0xa2) - } - - private fun checkDt(value: RuntimeValue?, vararg expected: DataType) { - if (value == null) - throw VmExecutionException("expected value") - if (value.type !in expected) - throw VmExecutionException("encountered value of wrong type ${value.type} expected ${expected.joinToString("/")}") - } - - - private fun getVar(name: String): RuntimeValue { - val result = variables[name] - if(result!=null) - return result - if(name in memoryPointers) { - throw VmExecutionException("variable is memory-mapped: $name = ${memoryPointers[name]}") - } - throw VmExecutionException("unknown variable: $name") - } - - private fun dispatch() { - val ins = program[currentInstructionPtr] - traceOutput?.println("\n$ins") - when (ins.opcode) { - Opcode.NOP -> {} - Opcode.START_PROCDEF, Opcode.END_PROCDEF -> {} - Opcode.PUSH_BYTE -> { - checkDt(ins.arg, DataType.UBYTE, DataType.BYTE) - evalstack.push(ins.arg) - } - Opcode.PUSH_WORD -> { - checkDt(ins.arg, *(setOf(DataType.UWORD, DataType.WORD) + IterableDatatypes).toTypedArray()) - evalstack.push(ins.arg) - } - Opcode.PUSH_FLOAT -> { - checkDt(ins.arg, DataType.FLOAT) - evalstack.push(ins.arg) - } - Opcode.PUSH_MEM_UB -> { - val address = ins.arg!!.integerValue() - evalstack.push(RuntimeValue(DataType.UBYTE, mem.getUByte(address))) - } - Opcode.PUSH_MEM_B -> { - val address = ins.arg!!.integerValue() - evalstack.push(RuntimeValue(DataType.BYTE, mem.getSByte(address))) - } - Opcode.PUSH_MEM_UW -> { - val address = ins.arg!!.integerValue() - evalstack.push(RuntimeValue(DataType.UWORD, mem.getUWord(address))) - } - Opcode.PUSH_MEM_W -> { - val address = ins.arg!!.integerValue() - evalstack.push(RuntimeValue(DataType.WORD, mem.getSWord(address))) - } - Opcode.PUSH_MEM_FLOAT -> { - val address = ins.arg!!.integerValue() - evalstack.push(RuntimeValue(DataType.FLOAT, mem.getFloat(address))) - } - Opcode.PUSH_MEMREAD -> { - val address = evalstack.pop() - checkDt(address, DataType.UWORD) - TODO("push_memread from $address") - } - Opcode.DISCARD_BYTE -> { - val value = evalstack.pop() - checkDt(value, DataType.UBYTE) - } - Opcode.DISCARD_WORD -> { - val value = evalstack.pop() - checkDt(value, DataType.UWORD) - } - Opcode.DISCARD_FLOAT -> { - val value = evalstack.pop() - checkDt(value, DataType.FLOAT) - } - Opcode.DUP_B -> { - val value = evalstack.peek() - checkDt(value, DataType.BYTE, DataType.UBYTE) - evalstack.push(value) - } - Opcode.DUP_W -> { - val value = evalstack.peek() - checkDt(value, DataType.WORD, DataType.UWORD) - evalstack.push(value) - } - Opcode.POP_MEM_BYTE -> { - val value = evalstack.pop() - checkDt(value, DataType.BYTE, DataType.UBYTE) - val address = ins.arg!!.integerValue() - if(value.type== DataType.BYTE) - mem.setSByte(address, value.integerValue().toShort()) - else - mem.setUByte(address, value.integerValue().toShort()) - setFlags(value) - } - Opcode.POP_MEM_WORD -> { - val value = evalstack.pop() - checkDt(value, DataType.WORD, DataType.UWORD) - val address = ins.arg!!.integerValue() - if(value.type== DataType.WORD) - mem.setSWord(address, value.integerValue()) - else - mem.setUWord(address, value.integerValue()) - setFlags(value) - } - Opcode.POP_MEM_FLOAT -> { - val value = evalstack.pop() - checkDt(value, DataType.FLOAT) - val address = ins.arg!!.integerValue() - mem.setFloat(address, value.numericValue().toDouble()) - setFlags(value) - } - Opcode.POP_MEMWRITE -> { - val address = evalstack.pop() - checkDt(address, DataType.UWORD) - val value = evalstack.pop() - checkDt(value, DataType.UBYTE) - TODO("pop_memwrite $value to $address") - setFlags(value) - } - Opcode.POP_INC_MEMORY -> { - val address = evalstack.pop() - checkDt(address, DataType.UWORD) - TODO("pop_inc_memory $address + flags") - } - Opcode.POP_DEC_MEMORY -> { - val address = evalstack.pop() - checkDt(address, DataType.UWORD) - TODO("pop_dec_memory $address + flags") - } - Opcode.ADD_UB -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val value = second.add(top) - evalstack.push(value) - setFlags(value) - } - Opcode.ADD_UW -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val value=second.add(top) - evalstack.push(value) - setFlags(value) - } - Opcode.ADD_B -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.BYTE) - checkDt(second, DataType.BYTE) - val value=second.add(top) - evalstack.push(value) - setFlags(value) - } - Opcode.ADD_W -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.WORD) - checkDt(second, DataType.WORD) - val value=second.add(top) - evalstack.push(value) - setFlags(value) - } - Opcode.ADD_F -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.FLOAT) - checkDt(second, DataType.FLOAT) - val value=second.add(top) - evalstack.push(value) - setFlags(value) - } - Opcode.SUB_UB -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val value=second.sub(top) - evalstack.push(value) - setFlags(value) - } - Opcode.SUB_UW -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val value=second.sub(top) - evalstack.push(value) - setFlags(value) - } - Opcode.SUB_B -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.BYTE) - checkDt(second, DataType.BYTE) - val value=second.sub(top) - evalstack.push(value) - setFlags(value) - } - Opcode.SUB_W -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.WORD) - checkDt(second, DataType.WORD) - val value=second.sub(top) - evalstack.push(value) - setFlags(value) - } - Opcode.CMP_UB -> { - val value = evalstack.pop() - val other = ins.arg!! - checkDt(value, DataType.UBYTE) - checkDt(other, DataType.UBYTE) - val comparison = value.compareTo(other) - P_zero = comparison==0 - P_negative = comparison<0 - P_carry = comparison>=0 - } - Opcode.CMP_UW -> { - val value = evalstack.pop() - val other = ins.arg!! - checkDt(value, DataType.UWORD) - checkDt(other, DataType.UWORD) - val comparison = value.compareTo(other) - P_zero = comparison==0 - P_negative = comparison<0 - P_carry = comparison>=0 - } - Opcode.CMP_B -> { - val value = evalstack.pop() - val other = ins.arg!! - checkDt(value, DataType.BYTE) - checkDt(other, DataType.BYTE) - val comparison = value.compareTo(other) - P_zero = comparison==0 - P_negative = comparison<0 - P_carry = comparison>=0 - } - Opcode.CMP_W -> { - val value = evalstack.pop() - val other = ins.arg!! - checkDt(value, DataType.WORD) - checkDt(other, DataType.WORD) - val result = value.sub(other) - val comparison = value.compareTo(other) - P_zero = comparison==0 - P_negative = comparison<0 - P_carry = comparison>=0 - } - Opcode.SUB_F -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.FLOAT) - checkDt(second, DataType.FLOAT) - val value=second.sub(top) - evalstack.push(value) - setFlags(value) - } - Opcode.MUL_UB -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val value=second.mul(top) - evalstack.push(value) - setFlags(value) - } - Opcode.MUL_UW -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val value=second.mul(top) - evalstack.push(value) - setFlags(value) - } - Opcode.MUL_B -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.BYTE) - checkDt(second, DataType.BYTE) - val value=second.mul(top) - evalstack.push(value) - setFlags(value) - } - Opcode.MUL_W -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.WORD) - checkDt(second, DataType.WORD) - val value=second.mul(top) - evalstack.push(value) - setFlags(value) - } - Opcode.MUL_F -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.FLOAT) - checkDt(second, DataType.FLOAT) - val value=second.mul(top) - evalstack.push(value) - setFlags(value) - } - Opcode.IDIV_UB -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val value=second.div(top) - evalstack.push(value) - setFlags(value) - } - Opcode.IDIV_UW -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val value=second.div(top) - evalstack.push(value) - setFlags(value) - } - Opcode.IDIV_B -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.BYTE) - checkDt(second, DataType.BYTE) - val value=second.div(top) - evalstack.push(value) - setFlags(value) - } - Opcode.IDIV_W -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.WORD) - checkDt(second, DataType.WORD) - val value=second.div(top) - evalstack.push(value) - setFlags(value) - } - Opcode.DIV_F -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.FLOAT) - checkDt(second, DataType.FLOAT) - val value=second.div(top) - evalstack.push(value) - setFlags(value) - } - Opcode.REMAINDER_UB -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val value=second.remainder(top) - evalstack.push(value) - setFlags(value) - } - Opcode.REMAINDER_UW -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val value=second.remainder(top) - evalstack.push(value) - setFlags(value) - } - Opcode.POW_F -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.FLOAT) - checkDt(second, DataType.FLOAT) - val value=second.pow(top) - evalstack.push(value) - setFlags(value) - } - Opcode.NEG_B -> { - val v = evalstack.pop() - checkDt(v, DataType.BYTE) - val value=v.neg() - evalstack.push(value) - setFlags(value) - } - Opcode.NEG_W -> { - val v = evalstack.pop() - checkDt(v, DataType.WORD) - val value=v.neg() - evalstack.push(value) - setFlags(value) - } - Opcode.NEG_F -> { - val v = evalstack.pop() - checkDt(v, DataType.FLOAT) - val value=v.neg() - evalstack.push(value) - setFlags(value) - } - Opcode.ABS_B -> { - val v = evalstack.pop() - checkDt(v, DataType.BYTE) - val value=v.abs() - evalstack.push(value) - setFlags(value) - } - Opcode.ABS_W -> { - val v = evalstack.pop() - checkDt(v, DataType.WORD) - val value=v.abs() - evalstack.push(value) - setFlags(value) - } - Opcode.ABS_F -> { - val v = evalstack.pop() - checkDt(v, DataType.FLOAT) - val value=v.abs() - evalstack.push(value) - setFlags(value) - } - - Opcode.SHIFTEDL_BYTE, Opcode.SHL_BYTE -> { - val v = evalstack.pop() - checkDt(v, DataType.UBYTE, DataType.BYTE) - P_carry = (v.integerValue() and 0x80)!=0 - val value=v.shl() - evalstack.push(value) - setFlags(value) - } - Opcode.SHIFTEDL_WORD, Opcode.SHL_WORD -> { - val v = evalstack.pop() - checkDt(v, DataType.UWORD, DataType.WORD) - P_carry = (v.integerValue() and 0x8000)!=0 - val value=v.shl() - evalstack.push(value) - setFlags(value) - } - Opcode.SHIFTEDR_UBYTE, Opcode.SHR_UBYTE -> { - val v = evalstack.pop() - checkDt(v, DataType.UBYTE) - P_carry = (v.integerValue() and 0x01)!=0 - val value=v.shr() - evalstack.push(value) - setFlags(value) - } - Opcode.SHIFTEDR_SBYTE, Opcode.SHR_SBYTE -> { - val v = evalstack.pop() - checkDt(v, DataType.BYTE) - P_carry = (v.integerValue() and 0x01)!=0 - val value=v.shr() - evalstack.push(value) - setFlags(value) - } - Opcode.SHIFTEDR_UWORD, Opcode.SHR_UWORD -> { - val v = evalstack.pop() - checkDt(v, DataType.UWORD) - P_carry = (v.integerValue() and 0x01)!=0 - val value=v.shr() - evalstack.push(value) - setFlags(value) - } - Opcode.SHIFTEDR_SWORD, Opcode.SHR_SWORD -> { - val v = evalstack.pop() - checkDt(v, DataType.WORD) - P_carry = (v.integerValue() and 0x01)!=0 - val value=v.shr() - evalstack.push(value) - setFlags(value) - } - Opcode.ROL_BYTE -> { - val v = evalstack.pop() - checkDt(v, DataType.UBYTE) - val (result, newCarry) = v.rol(P_carry) - evalstack.push(result) - setFlags(result) - this.P_carry = newCarry - } - Opcode.ROL_WORD -> { - val v = evalstack.pop() - checkDt(v, DataType.UWORD) - val (result, newCarry) = v.rol(P_carry) - evalstack.push(result) - setFlags(result) - this.P_carry = newCarry - } - Opcode.ROL2_BYTE -> { - val v = evalstack.pop() - checkDt(v, DataType.UBYTE) - val value=v.rol2() - evalstack.push(value) - setFlags(value) - } - Opcode.ROL2_WORD -> { - val v = evalstack.pop() - checkDt(v, DataType.UWORD) - val value=v.rol2() - evalstack.push(value) - setFlags(value) - } - Opcode.ROR_BYTE -> { - val v = evalstack.pop() - checkDt(v, DataType.UBYTE) - val (result, newCarry) = v.ror(P_carry) - evalstack.push(result) - setFlags(result) - this.P_carry = newCarry - } - Opcode.ROR_WORD -> { - val v = evalstack.pop() - checkDt(v, DataType.UWORD) - val (result, newCarry) = v.ror(P_carry) - evalstack.push(result) - setFlags(result) - this.P_carry = newCarry - } - Opcode.ROR2_BYTE -> { - val v = evalstack.pop() - checkDt(v, DataType.UBYTE) - val value=v.ror2() - evalstack.push(value) - setFlags(value) - } - Opcode.ROR2_WORD -> { - val v = evalstack.pop() - checkDt(v, DataType.UWORD) - val value=v.ror2() - evalstack.push(value) - setFlags(value) - } - Opcode.BITAND_BYTE -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val value = second.bitand(top) - evalstack.push(value) - setFlags(value) - } - Opcode.BITAND_WORD -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val value = second.bitand(top) - evalstack.push(value) - setFlags(value) - } - Opcode.BITOR_BYTE -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val value = second.bitor(top) - evalstack.push(value) - setFlags(value) - } - Opcode.BITOR_WORD -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val value = second.bitor(top) - evalstack.push(value) - setFlags(value) - } - Opcode.BITXOR_BYTE -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val value = second.bitxor(top) - evalstack.push(value) - setFlags(value) - } - Opcode.BITXOR_WORD -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val value = second.bitxor(top) - evalstack.push(value) - setFlags(value) - } - Opcode.INV_BYTE -> { - val v = evalstack.pop() - checkDt(v, DataType.UBYTE) - val value = v.inv() - evalstack.push(value) - setFlags(value) - } - Opcode.INV_WORD -> { - val v = evalstack.pop() - checkDt(v, DataType.UWORD) - val value = v.inv() - evalstack.push(value) - setFlags(value) - } - Opcode.SYSCALL -> dispatchSyscall(ins) - Opcode.SEC -> P_carry = true - Opcode.CLC -> P_carry = false - Opcode.SEI -> P_irqd = true - 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 = RuntimeValue(DataType.UBYTE, mem.getUByte(addr)) - val newValue = value.shl() - mem.setUByte(addr, newValue.integerValue().toShort()) - setFlags(newValue) - } - Opcode.SHL_MEM_WORD -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.UWORD, mem.getUWord(addr)) - val newValue = value.shl() - mem.setUWord(addr, newValue.integerValue()) - setFlags(newValue) - } - Opcode.SHR_MEM_UBYTE -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.UBYTE, mem.getUByte(addr)) - val newValue = value.shr() - mem.setUByte(addr, newValue.integerValue().toShort()) - setFlags(newValue) - } - Opcode.SHR_MEM_SBYTE -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.BYTE, mem.getSByte(addr)) - val newValue = value.shr() - mem.setSByte(addr, newValue.integerValue().toShort()) - setFlags(newValue) - } - Opcode.SHR_MEM_UWORD -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.UWORD, mem.getUWord(addr)) - val newValue = value.shr() - mem.setUWord(addr, newValue.integerValue()) - setFlags(newValue) - } - Opcode.SHR_MEM_SWORD -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.WORD, mem.getSWord(addr)) - val newValue = value.shr() - mem.setSWord(addr, newValue.integerValue()) - setFlags(newValue) - } - Opcode.ROL_MEM_BYTE -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.UBYTE, mem.getUByte(addr)) - val (newValue, newCarry) = value.rol(P_carry) - mem.setUByte(addr, newValue.integerValue().toShort()) - setFlags(newValue) - P_carry = newCarry - } - Opcode.ROL_MEM_WORD -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.UWORD, mem.getUWord(addr)) - val (newValue, newCarry) = value.rol(P_carry) - mem.setUWord(addr, newValue.integerValue()) - setFlags(newValue) - P_carry = newCarry - } - Opcode.ROR_MEM_BYTE -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.UBYTE, mem.getUByte(addr)) - val (newValue, newCarry) = value.ror(P_carry) - mem.setUByte(addr, newValue.integerValue().toShort()) - setFlags(newValue) - P_carry = newCarry - } - Opcode.ROR_MEM_WORD -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.UWORD, mem.getUWord(addr)) - val (newValue, newCarry) = value.ror(P_carry) - mem.setUWord(addr, newValue.integerValue()) - setFlags(newValue) - P_carry = newCarry - } - Opcode.ROL2_MEM_BYTE -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.UBYTE, mem.getUByte(addr)) - val newValue = value.rol2() - mem.setUByte(addr, newValue.integerValue().toShort()) - setFlags(newValue) - } - Opcode.ROL2_MEM_WORD -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.UWORD, mem.getUWord(addr)) - val newValue = value.rol2() - mem.setUWord(addr, newValue.integerValue()) - setFlags(newValue) - } - Opcode.ROR2_MEM_BYTE -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.UBYTE, mem.getUByte(addr)) - val newValue = value.ror2() - mem.setUByte(addr, newValue.integerValue().toShort()) - setFlags(newValue) - } - Opcode.ROR2_MEM_WORD -> { - val addr = ins.arg!!.integerValue() - val value = RuntimeValue(DataType.UWORD, mem.getUWord(addr)) - val newValue = value.ror2() - mem.setUWord(addr, newValue.integerValue()) - setFlags(newValue) - } - Opcode.JUMP -> { - currentInstructionPtr = determineBranchInstr(ins) - return - } - Opcode.BCS -> { - if (P_carry) currentInstructionPtr = determineBranchInstr(ins) - else currentInstructionPtr++ - return - } - Opcode.BCC -> { - if (P_carry) currentInstructionPtr++ - else currentInstructionPtr = determineBranchInstr(ins) - return - } - Opcode.BZ -> { - if (P_zero) currentInstructionPtr = determineBranchInstr(ins) - else currentInstructionPtr++ - return - } - Opcode.BNZ -> { - if (P_zero) currentInstructionPtr++ - else currentInstructionPtr = determineBranchInstr(ins) - return - } - Opcode.BNEG -> { - if (P_negative) currentInstructionPtr = determineBranchInstr(ins) - else currentInstructionPtr++ - return - } - Opcode.BPOS -> { - if (P_negative) currentInstructionPtr++ - else currentInstructionPtr = determineBranchInstr(ins) - return - } - Opcode.BVS, Opcode.BVC -> throw VmExecutionException("stackVM doesn't support the 'overflow' cpu flag") - Opcode.JZ -> { - val value = evalstack.pop().integerValue() and 255 - if (value==0) currentInstructionPtr = determineBranchInstr(ins) - else currentInstructionPtr++ - return - } - Opcode.JNZ -> { - val value = evalstack.pop().integerValue() and 255 - if (value!=0) currentInstructionPtr = determineBranchInstr(ins) - else currentInstructionPtr++ - return - } - Opcode.JZW -> { - val value = evalstack.pop().integerValue() - if (value==0) currentInstructionPtr = determineBranchInstr(ins) - else currentInstructionPtr++ - return - } - Opcode.JNZW -> { - val value = evalstack.pop().integerValue() - if (value!=0) currentInstructionPtr = determineBranchInstr(ins) - else currentInstructionPtr++ - return - } - Opcode.CALL -> { - callstack.push(currentInstructionPtr + 1) - currentInstructionPtr = determineBranchInstr(ins) - return - } - Opcode.RETURN -> { - if(callstack.empty()) - throw VmTerminationException("return instruction with empty call stack") - currentInstructionPtr = callstack.pop() - return - } - Opcode.PUSH_VAR_BYTE -> { - val value = getVar(ins.callLabel!!) - checkDt(value, DataType.UBYTE, DataType.BYTE) - evalstack.push(value) - } - Opcode.PUSH_VAR_WORD -> { - val value = getVar(ins.callLabel!!) - checkDt(value, *(setOf(DataType.UWORD, DataType.WORD) + IterableDatatypes).toTypedArray()) - evalstack.push(value) - } - Opcode.PUSH_VAR_FLOAT -> { - val value = getVar(ins.callLabel!!) - checkDt(value, DataType.FLOAT) - evalstack.push(value) - } - Opcode.PUSH_REGAX_WORD -> { - val a=variables.getValue("A").integerValue() - val x=variables.getValue("X").integerValue() - evalstack.push(RuntimeValue(DataType.UWORD, x * 256 + a)) - } - Opcode.PUSH_REGAY_WORD -> { - val a=variables.getValue("A").integerValue() - val y=variables.getValue("Y").integerValue() - evalstack.push(RuntimeValue(DataType.UWORD, y * 256 + a)) - } - Opcode.PUSH_REGXY_WORD -> { - val x=variables.getValue("X").integerValue() - val y=variables.getValue("Y").integerValue() - evalstack.push(RuntimeValue(DataType.UWORD, y * 256 + x)) - } - Opcode.POP_REGAX_WORD -> { - val value=evalstack.pop().integerValue() - val valueA: RuntimeValue - val valueX: RuntimeValue - if(value>=0) { - valueA = RuntimeValue(DataType.UBYTE, value and 255) - valueX = RuntimeValue(DataType.UBYTE, value shr 8) - } else { - val value2c = 65536+value - valueA = RuntimeValue(DataType.UBYTE, value2c and 255) - valueX = RuntimeValue(DataType.UBYTE, value2c shr 8) - } - variables["A"] = valueA - variables["X"] = valueX - setFlags(valueA.bitor(valueX)) - } - Opcode.POP_REGAY_WORD -> { - val value=evalstack.pop().integerValue() - val valueA: RuntimeValue - val valueY: RuntimeValue - if(value>=0) { - valueA = RuntimeValue(DataType.UBYTE, value and 255) - valueY = RuntimeValue(DataType.UBYTE, value shr 8) - } else { - val value2c = 65536+value - valueA = RuntimeValue(DataType.UBYTE, value2c and 255) - valueY = RuntimeValue(DataType.UBYTE, value2c shr 8) - } - variables["A"] = valueA - variables["Y"] = valueY - setFlags(valueA.bitor(valueY)) - } - Opcode.POP_REGXY_WORD -> { - val value=evalstack.pop().integerValue() - val valueX: RuntimeValue - val valueY: RuntimeValue - if(value>=0) { - valueX = RuntimeValue(DataType.UBYTE, value and 255) - valueY = RuntimeValue(DataType.UBYTE, value shr 8) - } else { - val value2c = 65536+value - valueX = RuntimeValue(DataType.UBYTE, value2c and 255) - valueY = RuntimeValue(DataType.UBYTE, value2c shr 8) - } - variables["X"] = valueX - variables["Y"] = valueY - setFlags(valueX.bitor(valueY)) - } - Opcode.POP_VAR_BYTE -> { - val value = evalstack.pop() - checkDt(value, DataType.UBYTE, DataType.BYTE) - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UBYTE, DataType.BYTE) - if(value.type!=variable.type) { - if(ins.callLabel !in Register.values().map { it.name }) { - throw VmExecutionException("datatype mismatch") - } - } - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.POP_VAR_WORD -> { - val value = evalstack.pop() - checkDt(value, DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_S) - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_S) - if(value.type!=variable.type) - throw VmExecutionException("datatype mismatch") - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.POP_VAR_FLOAT -> { - val value = evalstack.pop() - checkDt(value, DataType.FLOAT) - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.FLOAT) - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.SHL_VAR_BYTE -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UBYTE) - val value = variable.shl() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.SHL_VAR_WORD -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UWORD) - val value = variable.shl() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.SHR_VAR_UBYTE -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UBYTE) - val value = variable.shr() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.SHR_VAR_SBYTE -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.BYTE) - val value = variable.shr() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.SHR_VAR_UWORD -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UWORD) - val value = variable.shr() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.SHR_VAR_SWORD -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.WORD) - val value = variable.shr() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.ROL_VAR_BYTE -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UBYTE) - val (newValue, newCarry) = variable.rol(P_carry) - variables[ins.callLabel] =newValue - P_carry = newCarry - setFlags(newValue) - } - Opcode.ROL_VAR_WORD -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UWORD) - val (newValue, newCarry) = variable.rol(P_carry) - variables[ins.callLabel] =newValue - P_carry = newCarry - setFlags(newValue) - } - Opcode.ROR_VAR_BYTE -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UBYTE) - val (newValue, newCarry) = variable.ror(P_carry) - variables[ins.callLabel] =newValue - P_carry = newCarry - setFlags(newValue) - } - Opcode.ROR_VAR_WORD -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UWORD) - val (newValue, newCarry) = variable.ror(P_carry) - variables[ins.callLabel] =newValue - P_carry = newCarry - setFlags(newValue) - } - Opcode.ROL2_VAR_BYTE -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UBYTE) - val value = variable.rol2() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.ROL2_VAR_WORD -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UWORD) - val value = variable.rol2() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.ROR2_VAR_BYTE -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UBYTE) - val value = variable.ror2() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.ROR2_VAR_WORD -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UWORD) - val value = variable.ror2() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.INC_VAR_UB -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UBYTE) - val value = variable.inc() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.INC_VAR_B -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.BYTE) - val value = variable.inc() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.INC_VAR_UW -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UWORD) - val value = variable.inc() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.INC_VAR_W -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.WORD) - val value = variable.inc() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.INC_VAR_F -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.FLOAT) - val value = variable.inc() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.DEC_VAR_UB -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UBYTE) - val value = variable.dec() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.DEC_VAR_B -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.BYTE) - val value = variable.dec() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.DEC_VAR_UW -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.UWORD) - val value = variable.dec() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.DEC_VAR_W -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.WORD) - val value = variable.dec() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.DEC_VAR_F -> { - val variable = getVar(ins.callLabel!!) - checkDt(variable, DataType.FLOAT) - val value = variable.dec() - variables[ins.callLabel] = value - setFlags(value) - } - Opcode.INC_INDEXED_VAR_B,Opcode.INC_INDEXED_VAR_UB,Opcode.INC_INDEXED_VAR_W,Opcode.INC_INDEXED_VAR_UW,Opcode.INC_INDEXED_VAR_FLOAT -> { - val index = evalstack.pop().integerValue() - val variable = getVar(ins.callLabel!!) - val array = heap.get(variable.heapId!!) - TODO("INC_INDEXED_VAR + flags") - } - Opcode.DEC_INDEXED_VAR_B,Opcode.DEC_INDEXED_VAR_UB,Opcode.DEC_INDEXED_VAR_W,Opcode.DEC_INDEXED_VAR_UW,Opcode.DEC_INDEXED_VAR_FLOAT -> { - val index = evalstack.pop().integerValue() - val variable = getVar(ins.callLabel!!) - val array = heap.get(variable.heapId!!) - TODO("DEC_INDEXED_VAR + flags") - } - Opcode.INC_MEMORY -> { - val address = evalstack.pop() - checkDt(address, DataType.UWORD) - TODO("inc_memory $address + flags") - } - Opcode.DEC_MEMORY -> { - val address = evalstack.pop() - checkDt(address, DataType.UWORD) - TODO("dec_memory $address + flags") - } - Opcode.MSB -> { - val v = evalstack.pop() - checkDt(v, DataType.UWORD, DataType.WORD) - val value=v.msb() - evalstack.push(value) - setFlags(value) - } - Opcode.MKWORD -> { - val msb = evalstack.pop() - val lsb = evalstack.pop() - checkDt(lsb, DataType.UBYTE) - checkDt(msb, DataType.UBYTE) - val value = RuntimeValue(DataType.UWORD, (msb.integerValue() shl 8) or lsb.integerValue()) - evalstack.push(value) - setFlags(value) - } - Opcode.AND_BYTE -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val value=second.and(top) - evalstack.push(value) - setFlags(value) - } - Opcode.AND_WORD -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val value=second.and(top) - evalstack.push(value) - setFlags(value) - } - Opcode.OR_BYTE -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val value=second.or(top) - evalstack.push(value) - setFlags(value) - } - Opcode.OR_WORD -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val value=second.or(top) - evalstack.push(value) - setFlags(value) - } - Opcode.XOR_BYTE -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val value=second.xor(top) - evalstack.push(value) - setFlags(value) - } - Opcode.XOR_WORD -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val value=second.xor(top) - evalstack.push(value) - setFlags(value) - } - Opcode.NOT_BYTE -> { - val value = evalstack.pop() - checkDt(value, DataType.UBYTE) - val result=value.not() - evalstack.push(result) - setFlags(result) - } - Opcode.NOT_WORD -> { - val value = evalstack.pop() - checkDt(value, DataType.UWORD) - val result=value.not() - evalstack.push(result) - setFlags(result) - } - Opcode.LESS_UB -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val result = RuntimeValue(DataType.UBYTE, if (second < top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.LESS_B -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.BYTE) - checkDt(second, DataType.BYTE) - val result = RuntimeValue(DataType.UBYTE, if (second < top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.LESS_UW -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val result = RuntimeValue(DataType.UBYTE, if (second < top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.LESS_W -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.WORD) - checkDt(second, DataType.WORD) - val result = RuntimeValue(DataType.UBYTE, if (second < top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.LESS_F -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.FLOAT) - checkDt(second, DataType.FLOAT) - val result = RuntimeValue(DataType.UBYTE, if (second < top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.GREATER_UB -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val result = RuntimeValue(DataType.UBYTE, if (second > top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.GREATER_B -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.BYTE) - checkDt(second, DataType.BYTE) - val result = RuntimeValue(DataType.UBYTE, if (second > top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.GREATER_UW -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val result = RuntimeValue(DataType.UBYTE, if (second > top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.GREATER_W -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.WORD) - checkDt(second, DataType.WORD) - val result = RuntimeValue(DataType.UBYTE, if (second > top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.GREATER_F -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.FLOAT) - checkDt(second, DataType.FLOAT) - val result = RuntimeValue(DataType.UBYTE, if (second > top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.LESSEQ_UB -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val result= RuntimeValue(DataType.UBYTE, if (second <= top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.LESSEQ_B -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.BYTE) - checkDt(second, DataType.BYTE) - val result = RuntimeValue(DataType.UBYTE, if (second <= top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.LESSEQ_UW -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val result= RuntimeValue(DataType.UBYTE, if (second <= top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.LESSEQ_W -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.WORD) - checkDt(second, DataType.WORD) - val result = RuntimeValue(DataType.UBYTE, if (second <= top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.LESSEQ_F -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.FLOAT) - checkDt(second, DataType.FLOAT) - val result = RuntimeValue(DataType.UBYTE, if (second <= top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.GREATEREQ_UB -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UBYTE) - checkDt(second, DataType.UBYTE) - val result= RuntimeValue(DataType.UBYTE, if (second >= top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.GREATEREQ_B -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.BYTE) - checkDt(second, DataType.BYTE) - val result= RuntimeValue(DataType.UBYTE, if (second >= top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.GREATEREQ_UW -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.UWORD) - checkDt(second, DataType.UWORD) - val result = RuntimeValue(DataType.UBYTE, if (second >= top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.GREATEREQ_W -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.WORD) - checkDt(second, DataType.WORD) - val result= RuntimeValue(DataType.UBYTE, if (second >= top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.GREATEREQ_F -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.FLOAT) - checkDt(second, DataType.FLOAT) - val result = RuntimeValue(DataType.UBYTE, if (second >= top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.EQUAL_BYTE -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.BYTE, DataType.UBYTE) - checkDt(second, DataType.BYTE, DataType.UBYTE) - val result = RuntimeValue(DataType.UBYTE, if (second == top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.EQUAL_WORD -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.WORD, DataType.UWORD) - checkDt(second, DataType.WORD, DataType.UWORD) - val result = RuntimeValue(DataType.UBYTE, if (second == top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.EQUAL_F -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.FLOAT) - checkDt(second, DataType.FLOAT) - val result= RuntimeValue(DataType.UBYTE, if (second == top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.NOTEQUAL_BYTE -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.BYTE, DataType.UBYTE) - checkDt(second, DataType.BYTE, DataType.UBYTE) - val result= RuntimeValue(DataType.UBYTE, if (second != top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.NOTEQUAL_WORD -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.WORD, DataType.UWORD) - checkDt(second, DataType.UWORD, DataType.UWORD) - val result= RuntimeValue(DataType.UBYTE, if (second != top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.NOTEQUAL_F -> { - val (top, second) = evalstack.pop2() - checkDt(top, DataType.FLOAT) - checkDt(second, DataType.FLOAT) - val result= RuntimeValue(DataType.UBYTE, if (second != top) 1 else 0) - evalstack.push(result) - setFlags(result) - } - Opcode.READ_INDEXED_VAR_BYTE -> { - // put the byte value of variable[index] onto the stack - val index = evalstack.pop().integerValue() - val result = - if(ins.callLabel in memoryPointers) { - val variable = memoryPointers.getValue(ins.callLabel!!) - val address = variable.first + index - when(variable.second) { - DataType.ARRAY_UB -> RuntimeValue(DataType.UBYTE, mem.getUByte(address)) - DataType.ARRAY_B -> RuntimeValue(DataType.BYTE, mem.getSByte(address)) - else -> throw VmExecutionException("not a proper array/string variable with byte elements") - } - } else { - val variable = getVar(ins.callLabel!!) - if (variable.type == DataType.UWORD) { - // assume the variable is a pointer (address) and get the ubyte value from that memory location - RuntimeValue(DataType.UBYTE, mem.getUByte(variable.integerValue())) - } else { - // get indexed byte element from the arraysize - val array = heap.get(variable.heapId!!) - when (array.type) { - DataType.ARRAY_UB -> RuntimeValue(DataType.UBYTE, array.array!![index].integer!!) - DataType.ARRAY_B -> RuntimeValue(DataType.BYTE, array.array!![index].integer!!) - DataType.STR, DataType.STR_S -> RuntimeValue(DataType.UBYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0]) - else -> throw VmExecutionException("not a proper array/string variable with byte elements") - } - } - } - evalstack.push(result) - setFlags(result) - } - Opcode.READ_INDEXED_VAR_WORD -> { - // put the word value of variable[index] onto the stack - val index = evalstack.pop().integerValue() - val result= - if(ins.callLabel in memoryPointers) { - val variable = memoryPointers.getValue(ins.callLabel!!) - val address = variable.first + index*2 - when(variable.second) { - DataType.ARRAY_UW -> RuntimeValue(DataType.UWORD, mem.getUWord(address)) - DataType.ARRAY_W -> RuntimeValue(DataType.WORD, mem.getSWord(address)) - else -> throw VmExecutionException("not a proper arraysize var with word elements") - } - } else { - // normal variable - val variable = getVar(ins.callLabel!!) - if(variable.type== DataType.UWORD) { - // assume the variable is a pointer (address) and get the word value from that memory location - RuntimeValue(DataType.UWORD, mem.getUWord(variable.integerValue())) - } else { - // get indexed word element from the arraysize - val array = heap.get(variable.heapId!!) - when(array.type){ - DataType.ARRAY_UW -> { - val value = array.array!![index] - when { - value.integer!=null -> RuntimeValue(DataType.UWORD, value.integer) - value.addressOf!=null -> { - val heapId = variables.getValue(value.addressOf.scopedname!!).heapId!! - if(heapId<0) - throw VmExecutionException("expected variable on heap") - evalstack.push(RuntimeValue(DataType.UWORD, heapId)) // push the "address" of the string or array variable (this is taken care of properly in the assembly code generator) - } - else -> throw VmExecutionException("strange array value") - } - } - DataType.ARRAY_W -> RuntimeValue(DataType.WORD, array.array!![index].integer!!) - else -> throw VmExecutionException("not a proper arraysize var with word elements") - } - } - } - evalstack.push(result) - setFlags(result) - } - Opcode.READ_INDEXED_VAR_FLOAT -> { - // put the float value of variable[index] onto the stack - val index = evalstack.pop().integerValue() - val result = - if(ins.callLabel in memoryPointers) { - val variable = memoryPointers.getValue(ins.callLabel!!) - val address = variable.first + index*5 - if(variable.second== DataType.ARRAY_F) - RuntimeValue(DataType.FLOAT, mem.getFloat(address)) - else - throw VmExecutionException("not a proper arraysize var with float elements") - } else { - val variable = getVar(ins.callLabel!!) - if (variable.type == DataType.UWORD) { - // assume the variable is a pointer (address) and get the float value from that memory location - RuntimeValue(DataType.UWORD, mem.getFloat(variable.integerValue())) - } else { - // get indexed float element from the arraysize - val array = heap.get(variable.heapId!!) - if (array.type != DataType.ARRAY_F) - throw VmExecutionException("not a proper arraysize var with float elements") - RuntimeValue(DataType.FLOAT, array.doubleArray!![index]) - } - } - evalstack.push(result) - setFlags(result) - } - Opcode.WRITE_INDEXED_VAR_BYTE -> { - // 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, DataType.BYTE) - val varname = ins.callLabel!! - val memloc = memoryPointers[varname] - if(memloc!=null) { - // variable is the name of a pointer, write the byte value to that memory location - if(value.type== DataType.UBYTE) { - if(memloc.second!= DataType.ARRAY_UB) - throw VmExecutionException("invalid memory pointer type $memloc") - mem.setUByte(memloc.first, value.integerValue().toShort()) - } - else { - if(memloc.second!= DataType.ARRAY_B) - throw VmExecutionException("invalid memory pointer type $memloc") - mem.setSByte(memloc.first, value.integerValue().toShort()) - } - } else { - val variable = getVar(varname) - if (variable.type == DataType.UWORD) { - // assume the variable is a pointer (address) and write the byte value to that memory location - if(value.type== DataType.UBYTE) - mem.setUByte(variable.integerValue(), value.integerValue().toShort()) - else - mem.setSByte(variable.integerValue(), value.integerValue().toShort()) - } else { - // set indexed byte element in the arraysize - val array = heap.get(variable.heapId!!) - when (array.type) { - DataType.ARRAY_UB -> array.array!![index] = IntegerOrAddressOf(value.integerValue(), null) - DataType.ARRAY_B -> array.array!![index] = IntegerOrAddressOf(value.integerValue(), null) - DataType.STR, DataType.STR_S -> { - val chars = array.str!!.toCharArray() - val ps = Petscii.decodePetscii(listOf(value.integerValue().toShort()), true)[0] - if(ps=='\ufffe') // undefined - chars[index] = '\u0000' - else - chars[index] = ps - heap.update(variable.heapId, chars.joinToString("")) - } - else -> throw VmExecutionException("not a proper array/string var with byte elements") - } - } - } - setFlags(value) - } - Opcode.WRITE_INDEXED_VAR_WORD -> { - // 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, DataType.WORD) - val varname = ins.callLabel!! - val memloc = memoryPointers[varname] - if(memloc!=null) { - // variable is the name of a pointer, write the word value to that memory location - if(value.type== DataType.UWORD) { - if(memloc.second!= DataType.ARRAY_UW) - throw VmExecutionException("invalid memory pointer type $memloc") - mem.setUWord(memloc.first+index*2, value.integerValue()) - } - else { - if(memloc.second!= DataType.ARRAY_W) - throw VmExecutionException("invalid memory pointer type $memloc") - mem.setSWord(memloc.first+index*2, value.integerValue()) - } - } else { - val variable = getVar(varname) - if (variable.type == DataType.UWORD) { - // assume the variable is a pointer (address) and write the word value to that memory location - if(value.type== DataType.UWORD) - mem.setUWord(variable.integerValue()+index*2, value.integerValue()) - else - mem.setSWord(variable.integerValue()+index*2, value.integerValue()) - } else { - // set indexed word element in the arraysize - val array = heap.get(variable.heapId!!) - when (array.type) { - DataType.ARRAY_UW -> array.array!![index] = IntegerOrAddressOf(value.integerValue(), null) - DataType.ARRAY_W -> array.array!![index] = IntegerOrAddressOf(value.integerValue(), null) - else -> throw VmExecutionException("not a proper arraysize var with word elements") - } - } - } - setFlags(value) - } - Opcode.WRITE_INDEXED_VAR_FLOAT -> { - // 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 varname = ins.callLabel!! - val memloc = memoryPointers[varname] - if(memloc!=null) { - // variable is the name of a pointer, write the float value to that memory location - if(memloc.second!= DataType.ARRAY_F) - throw VmExecutionException("invalid memory pointer type $memloc") - mem.setFloat(memloc.first+index*5, value.numericValue().toDouble()) - } else { - val variable = getVar(varname) - if (variable.type == DataType.UWORD) { - // assume the variable is a pointer (address) and write the float value to that memory location - mem.setFloat(variable.integerValue()+index*5, value.numericValue().toDouble()) - } else { - // set indexed float element in the arraysize - val array = heap.get(variable.heapId!!) - if (array.type != DataType.ARRAY_F) - throw VmExecutionException("not a proper arraysize var with float elements") - array.doubleArray!![index] = value.numericValue().toDouble() - } - } - setFlags(value) - } - Opcode.RSAVE -> { - evalstack.push(RuntimeValue(DataType.UBYTE, if (P_irqd) 1 else 0)) - evalstack.push(RuntimeValue(DataType.UBYTE, if (P_carry) 1 else 0)) - evalstack.push(variables["X"]) - evalstack.push(variables["Y"]) - evalstack.push(variables["A"]) - } - Opcode.RRESTORE -> { - variables["A"] = evalstack.pop() - variables["Y"] = evalstack.pop() - variables["X"] = evalstack.pop() - P_carry = evalstack.pop().asBoolean - P_irqd = evalstack.pop().asBoolean - } - Opcode.RSAVEX -> registerSaveX = variables.getValue("X") - Opcode.RRESTOREX -> variables["X"] = registerSaveX - Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code $ins") - Opcode.INCLUDE_FILE -> throw VmExecutionException("stackVm doesn't support including a file $ins") - Opcode.PUSH_ADDR_HEAPVAR -> { - val variable = variables.getValue(ins.callLabel!!) - if(variable.heapId!=null) { - val heapId = variable.heapId - if (heapId < 0) - throw VmExecutionException("expected variable on heap") - evalstack.push(RuntimeValue(DataType.UWORD, heapId)) // push the "address" of the string or array variable (this is taken care of properly in the assembly code generator) - } else { - // hack: return hash of the name, so we have at least *a* value... - val addr = ins.callLabel.hashCode() and 65535 - evalstack.push(RuntimeValue(DataType.UWORD, addr)) - } - } - Opcode.CAST_UB_TO_B -> typecast(DataType.UBYTE, DataType.BYTE) - Opcode.CAST_W_TO_B -> typecast(DataType.WORD, DataType.BYTE) - Opcode.CAST_UW_TO_B -> typecast(DataType.UWORD, DataType.BYTE) - Opcode.CAST_F_TO_B -> typecast(DataType.FLOAT, DataType.BYTE) - Opcode.CAST_B_TO_UB-> typecast(DataType.BYTE, DataType.UBYTE) - Opcode.CAST_W_TO_UB -> typecast(DataType.WORD, DataType.UBYTE) - Opcode.CAST_UW_TO_UB -> typecast(DataType.UWORD, DataType.UBYTE) - Opcode.CAST_F_TO_UB -> typecast(DataType.FLOAT, DataType.UBYTE) - Opcode.CAST_UB_TO_UW -> typecast(DataType.UBYTE, DataType.UWORD) - Opcode.CAST_B_TO_UW -> typecast(DataType.BYTE, DataType.UWORD) - Opcode.CAST_W_TO_UW -> typecast(DataType.WORD, DataType.UWORD) - Opcode.CAST_F_TO_UW -> typecast(DataType.FLOAT, DataType.UWORD) - Opcode.CAST_UB_TO_W -> typecast(DataType.UBYTE, DataType.WORD) - Opcode.CAST_B_TO_W -> typecast(DataType.BYTE, DataType.WORD) - Opcode.CAST_UW_TO_W -> typecast(DataType.UWORD, DataType.WORD) - Opcode.CAST_F_TO_W -> typecast(DataType.FLOAT, DataType.WORD) - Opcode.CAST_UB_TO_F -> typecast(DataType.UBYTE, DataType.FLOAT) - Opcode.CAST_B_TO_F -> typecast(DataType.BYTE, DataType.FLOAT) - Opcode.CAST_UW_TO_F -> typecast(DataType.UWORD, DataType.FLOAT) - Opcode.CAST_W_TO_F -> typecast(DataType.WORD, DataType.FLOAT) - Opcode.CARRY_TO_A -> variables["A"] = if(P_carry) RuntimeValue(DataType.UBYTE, 1) else RuntimeValue(DataType.UBYTE, 0) - - //else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}") - } - - if(traceOutput!=null) { - traceOutput?.println(" evalstack (size=${evalstack.size}):") - evalstack.printTop(4, traceOutput!!) - } - - currentInstructionPtr++ - } - - private fun determineBranchInstr(ins: Instruction): Int { - if(ins.branchAddress!=null) { - TODO("call to a system memory routine at ${ins.branchAddress!!.toHex()}") - throw VmExecutionException("stackVm doesn't support branching to a memory address") - } - return if(ins.callLabel==null) - throw VmExecutionException("requires label to branch to") - else { - when(ins.callLabel) { - "c64.CLEARSCR" -> { - canvas?.clearScreen(mem.getUByte(0xd021)) - callstack.pop() - } - "c64.CHROUT" -> { - val sc=variables.getValue("A").integerValue() - canvas?.printPetscii(sc.toShort()) - callstack.pop() - } - "c64.GETIN" -> { - variables["A"] = RuntimeValue(DataType.UBYTE, 0) // TODO keyboard input - callstack.pop() - } - else -> { - labels.getValue(ins.callLabel) - } - } - } - } - - private fun setFlags(value: RuntimeValue?) { - if(value!=null) { - when(value.type) { - DataType.UBYTE -> { - val int = value.integerValue() - P_negative = int>127 - P_zero = int==0 - } - DataType.UWORD -> { - val int = value.integerValue() - P_negative = int>32767 - P_zero = int==0 - } - DataType.BYTE, DataType.WORD -> { - val int = value.integerValue() - P_negative = int<0 - P_zero = int==0 - } - DataType.FLOAT -> { - val flt = value.numericValue().toDouble() - P_negative = flt < 0.0 - P_zero = flt==0.0 - } - else -> { - // no flags for non-numeric type - } - } - } - } - - private fun typecast(from: DataType, to: DataType) { - val value = evalstack.pop() - checkDt(value, from) - val cv = value.cast(to) - evalstack.push(cv) - } - - private fun dispatchSyscall(ins: Instruction) { - val callId = ins.arg!!.integerValue().toShort() - when (val syscall = Syscall.values().first { it.callNr == callId }) { - Syscall.VM_WRITE_MEMCHR -> { - val address = evalstack.pop().integerValue() - print(Petscii.decodePetscii(listOf(mem.getUByte(address)), true)) - } - Syscall.VM_WRITE_MEMSTR -> { - val address = evalstack.pop().integerValue() - print(mem.getString(address)) - } - Syscall.VM_WRITE_NUM -> { - print(evalstack.pop().numericValue()) - } - Syscall.VM_WRITE_CHAR -> { - print(Petscii.decodePetscii(listOf(evalstack.pop().integerValue().toShort()), true)) - } - Syscall.VM_WRITE_STR -> { - val heapId = evalstack.pop().integerValue() - print(heap.get(heapId).str?.substringBefore('\u0000')) - } - Syscall.VM_INPUT_STR -> { - val heapId = evalstack.pop().integerValue() - val value = heap.get(heapId) - val maxlen = value.str!!.length - val input = readLine() ?: "" - heap.update(heapId, input.padEnd(maxlen, '\u0000').substring(0, maxlen)) - } - Syscall.VM_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().toShort()) - } - Syscall.VM_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().toShort()) - } - Syscall.VM_GFX_CLEARSCR -> { - val color = evalstack.pop() - canvas?.clearScreen(color.integerValue().toShort()) - } - Syscall.VM_GFX_TEXT -> { - val textPtr = evalstack.pop().integerValue() - val color = evalstack.pop() - val (cy, cx) = evalstack.pop2() - val text = heap.get(textPtr) - canvas?.writeTextAt(cx.integerValue(), cy.integerValue(), text.str!!, color.integerValue().toShort(), true) - } - Syscall.FUNC_RND -> evalstack.push(RuntimeValue(DataType.UBYTE, rnd.nextInt() and 255)) - Syscall.FUNC_RNDW -> evalstack.push(RuntimeValue(DataType.UWORD, rnd.nextInt() and 65535)) - Syscall.FUNC_RNDF -> evalstack.push(RuntimeValue(DataType.FLOAT, rnd.nextDouble())) - Syscall.FUNC_LEN_STR, Syscall.FUNC_LEN_STRS -> { - val strPtr = evalstack.pop().integerValue() - val text = heap.get(strPtr).str!! - evalstack.push(RuntimeValue(DataType.UBYTE, text.length)) - } - Syscall.FUNC_STRLEN -> { - val strPtr = evalstack.pop().integerValue() - val text = heap.get(strPtr).str!! - val zeroIdx = text.indexOf('\u0000') - val len = if(zeroIdx>=0) zeroIdx else text.length - evalstack.push(RuntimeValue(DataType.UBYTE, len)) - } - Syscall.FUNC_READ_FLAGS -> { - val carry = if(P_carry) 1 else 0 - val zero = if(P_zero) 2 else 0 - val irqd = if(P_irqd) 4 else 0 - val negative = if(P_negative) 128 else 0 - val flags = carry or zero or irqd or negative - evalstack.push(RuntimeValue(DataType.UBYTE, flags)) - } - Syscall.FUNC_SIN -> evalstack.push(RuntimeValue(DataType.FLOAT, sin(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_COS -> evalstack.push(RuntimeValue(DataType.FLOAT, cos(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_SIN8 -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.BYTE, (127.0 * sin(rad)).toShort())) - } - Syscall.FUNC_SIN8U -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort())) - } - Syscall.FUNC_SIN16 -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.WORD, (32767.0 * sin(rad)).toInt())) - } - Syscall.FUNC_SIN16U -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.WORD, (32768.0 + 32767.5 * sin(rad)).toInt())) - } - Syscall.FUNC_COS8 -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.BYTE, (127.0 * cos(rad)).toShort())) - } - Syscall.FUNC_COS8U -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort())) - } - Syscall.FUNC_COS16 -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.WORD, (32767.0 * cos(rad)).toInt())) - } - Syscall.FUNC_COS16U -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.WORD, (32768.0 + 32767.5 * cos(rad)).toInt())) - } - Syscall.FUNC_ROUND -> evalstack.push(RuntimeValue(DataType.WORD, evalstack.pop().numericValue().toDouble().roundToInt())) - Syscall.FUNC_ABS -> { - val value = evalstack.pop() - val absValue = - when (value.type) { - DataType.UBYTE -> RuntimeValue(DataType.UBYTE, value.numericValue()) - DataType.UWORD -> RuntimeValue(DataType.UWORD, value.numericValue()) - DataType.FLOAT -> RuntimeValue(DataType.FLOAT, value.numericValue()) - else -> throw VmExecutionException("cannot get abs of $value") - } - evalstack.push(absValue) - } - Syscall.FUNC_TAN -> evalstack.push(RuntimeValue(DataType.FLOAT, tan(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_ATAN -> evalstack.push(RuntimeValue(DataType.FLOAT, atan(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_LN -> evalstack.push(RuntimeValue(DataType.FLOAT, ln(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_LOG2 -> evalstack.push(RuntimeValue(DataType.FLOAT, log2(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_SQRT -> evalstack.push(RuntimeValue(DataType.FLOAT, sqrt(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_SQRT16 -> evalstack.push(RuntimeValue(DataType.UBYTE, sqrt(evalstack.pop().numericValue().toDouble()).toInt())) - Syscall.FUNC_RAD -> evalstack.push(RuntimeValue(DataType.FLOAT, Math.toRadians(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_DEG -> evalstack.push(RuntimeValue(DataType.FLOAT, Math.toDegrees(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_FLOOR -> { - val value = evalstack.pop() - if (value.type in NumericDatatypes) - evalstack.push(RuntimeValue(DataType.FLOAT, floor(value.numericValue().toDouble()))) - else throw VmExecutionException("cannot get floor of $value") - } - Syscall.FUNC_CEIL -> { - val value = evalstack.pop() - if (value.type in NumericDatatypes) - evalstack.push(RuntimeValue(DataType.FLOAT, ceil(value.numericValue().toDouble()))) - else throw VmExecutionException("cannot get ceil of $value") - } - Syscall.FUNC_MAX_UB -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, value.array.map { it.integer!! }.max() ?: 0)) - } - Syscall.FUNC_MAX_B -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.BYTE, value.array.map { it.integer!! }.max() ?: 0)) - } - Syscall.FUNC_MAX_UW -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - if(value.array.any {it.addressOf!=null}) - throw VmExecutionException("stackvm cannot accept raw memory pointers") - evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.max() ?: 0)) - } - Syscall.FUNC_MAX_W -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.WORD, value.array.map { it.integer!! }.max() ?: 0)) - } - Syscall.FUNC_MAX_F -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.doubleArray!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.FLOAT, value.doubleArray.max() ?: 0.0)) - } - Syscall.FUNC_MIN_UB -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, value.array.map { it.integer!! }.min() ?: 0)) - } - Syscall.FUNC_MIN_B -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.BYTE, value.array.map { it.integer!! }.min() ?: 0)) - } - Syscall.FUNC_MIN_UW -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - if(value.array.any {it.addressOf!=null}) - throw VmExecutionException("stackvm cannot accept raw memory pointers") - evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.min() ?: 0)) - } - Syscall.FUNC_MIN_W -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.WORD, value.array.map { it.integer!! }.min() ?: 0)) - } - Syscall.FUNC_MIN_F -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.doubleArray!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.FLOAT, value.doubleArray.min() ?: 0.0)) - } - Syscall.FUNC_SUM_W, Syscall.FUNC_SUM_B -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.WORD, value.array.map { it.integer!! }.sum())) - } - Syscall.FUNC_SUM_UW -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - if(value.array.any {it.addressOf!=null}) - throw VmExecutionException("stackvm cannot accept raw memory pointers") - evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.sum())) - } - Syscall.FUNC_SUM_UB -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.sum())) - } - Syscall.FUNC_SUM_F -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.doubleArray!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.FLOAT, value.doubleArray.sum())) - } - Syscall.FUNC_ANY_B, Syscall.FUNC_ANY_W -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, if (value.array.any { it.integer != 0 }) 1 else 0)) - } - Syscall.FUNC_ANY_F -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.doubleArray!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, if (value.doubleArray.any { it != 0.0 }) 1 else 0)) - } - Syscall.FUNC_ALL_B, Syscall.FUNC_ALL_W -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, if (value.array.all { it.integer != 0 }) 1 else 0)) - } - Syscall.FUNC_ALL_F -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.doubleArray!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, if (value.doubleArray.all { it != 0.0 }) 1 else 0)) - } - Syscall.FUNC_MEMCOPY -> { - val numbytes = evalstack.pop().integerValue() - val to = evalstack.pop().integerValue() - val from = evalstack.pop().integerValue() - mem.copy(from, to, numbytes) - } - Syscall.FUNC_MEMSET -> { - val value = evalstack.pop() - val address = evalstack.pop().integerValue() - val numbytes = evalstack.pop().integerValue() - val bytevalue = value.integerValue().toShort() - when { - value.type== DataType.UBYTE -> for(addr in address until address+numbytes) - mem.setUByte(addr, bytevalue) - value.type== DataType.BYTE -> for(addr in address until address+numbytes) - mem.setSByte(addr, bytevalue) - else -> throw VmExecutionException("(u)byte value expected") - } - } - Syscall.FUNC_MEMSETW -> { - val value = evalstack.pop() - val address = evalstack.pop().integerValue() - val numwords = evalstack.pop().integerValue() - val wordvalue = value.integerValue() - when { - value.type== DataType.UWORD -> for(addr in address until address+numwords*2 step 2) - mem.setUWord(addr, wordvalue) - value.type== DataType.WORD -> for(addr in address until address+numwords*2 step 2) - mem.setSWord(addr, wordvalue) - else -> throw VmExecutionException("(u)word value expected") - } - } - Syscall.SYSCALLSTUB -> throw VmExecutionException("unimplemented sysasm called: ${ins.callLabel} Create a Syscall enum for this and implement the vm intercept for it.") - Syscall.SYSASM_c64scr_plot -> { - val x = variables.getValue("Y").integerValue() - val y = variables.getValue("A").integerValue() - canvas?.setCursorPos(x, y) - } - Syscall.SYSASM_c64scr_print -> { - val straddr = variables.getValue("A").integerValue() + 256*variables.getValue("Y").integerValue() - val str = heap.get(straddr).str!! - canvas?.printText(str, true) - } - Syscall.SYSASM_c64scr_print_ub -> { - val num = variables.getValue("A").integerValue() - canvas?.printText(num.toString(), true) - } - Syscall.SYSASM_c64scr_print_ub0 -> { - val num = variables.getValue("A").integerValue() - canvas?.printText("%03d".format(num), true) - } - Syscall.SYSASM_c64scr_print_b -> { - val num = variables.getValue("A").integerValue() - if(num<=127) - canvas?.printText(num.toString(), true) - else - canvas?.printText("-${256-num}", true) - } - Syscall.SYSASM_c64scr_print_uw -> { - val lo = variables.getValue("A").integerValue() - val hi = variables.getValue("Y").integerValue() - val number = lo+256*hi - canvas?.printText(number.toString(), true) - } - Syscall.SYSASM_c64scr_print_uw0 -> { - val lo = variables.getValue("A").integerValue() - val hi = variables.getValue("Y").integerValue() - val number = lo+256*hi - canvas?.printText("%05d".format(number), true) - } - Syscall.SYSASM_c64scr_print_uwhex -> { - val prefix = if(this.P_carry) "$" else "" - val lo = variables.getValue("A").integerValue() - val hi = variables.getValue("Y").integerValue() - val number = lo+256*hi - canvas?.printText("$prefix${number.toString(16).padStart(4, '0')}", true) - } - Syscall.SYSASM_c64scr_print_w -> { - val lo = variables.getValue("A").integerValue() - val hi = variables.getValue("Y").integerValue() - val number = lo+256*hi - if(number<=32767) - canvas?.printText(number.toString(), true) - else - canvas?.printText("-${65536-number}", true) - } - Syscall.SYSASM_c64flt_print_f -> { - val number = variables.getValue("c64flt.print_f.value").numericValue() - canvas?.printText(number.toString(), true) - } - Syscall.SYSASM_c64scr_setcc -> { - val x = variables.getValue("c64scr.setcc.column").integerValue() - val y = variables.getValue("c64scr.setcc.row").integerValue() - val char = variables.getValue("c64scr.setcc.char").integerValue() - val color = variables.getValue("c64scr.setcc.color").integerValue() - canvas?.setChar(x, y, char.toShort(), color.toShort()) - } - else -> throw VmExecutionException("unimplemented syscall $syscall") - } - } - - fun irq(timestamp: Long) { - // 60hz IRQ handling - if(P_irqd) - return // interrupt is disabled - - P_irqd=true - swapIrqExecutionContexts(true) - - val jiffies = min((timestamp-rtcOffset)*60/1000, 24*3600*60-1) - // update the C-64 60hz jiffy clock in the ZP addresses: - mem.setUByte_DMA(0x00a0, (jiffies ushr 16).toShort()) - mem.setUByte_DMA(0x00a1, (jiffies ushr 8 and 255).toShort()) - mem.setUByte_DMA(0x00a2, (jiffies and 255).toShort()) - - if(irqStartInstructionPtr>=0) { - try { - // execute the irq routine - this.step(Int.MAX_VALUE) - } catch (vmt: VmTerminationException) { - // irq routine ended - } - } - - swapIrqExecutionContexts(false) - P_irqd=false - } - - private var irqStoredEvalStack = MyStack() - private var irqStoredCallStack = MyStack() - private var irqStoredCarry = false - private var irqStoredTraceOutputFile: String? = null - private var irqStoredMainInstructionPtr = -1 - - private fun swapIrqExecutionContexts(startingIrq: Boolean) { - if(startingIrq) { - irqStoredMainInstructionPtr = currentInstructionPtr - irqStoredCallStack = callstack - irqStoredEvalStack = evalstack - irqStoredCarry = P_carry - irqStoredTraceOutputFile = traceOutputFile - - currentInstructionPtr = if(irqStartInstructionPtr>=0) - irqStartInstructionPtr - else { - if(program.last().opcode!=Opcode.RETURN) - throw VmExecutionException("last instruction in program should be RETURN for irq handler") - program.size-1 - } - callstack = MyStack() - evalstack = MyStack() - P_carry = false - traceOutputFile = null - } else { - if(evalstack.isNotEmpty()) - throw VmExecutionException("irq: eval stack is not empty at exit from irq program") - if(callstack.isNotEmpty()) - throw VmExecutionException("irq: call stack is not empty at exit from irq program") - currentInstructionPtr = irqStoredMainInstructionPtr - callstack = irqStoredCallStack - evalstack = irqStoredEvalStack - P_carry = irqStoredCarry - traceOutputFile = irqStoredTraceOutputFile - } - } -} diff --git a/DeprecatedStackVm/test/StackVMOpcodeTests.kt b/DeprecatedStackVm/test/StackVMOpcodeTests.kt deleted file mode 100644 index d906b827b..000000000 --- a/DeprecatedStackVm/test/StackVMOpcodeTests.kt +++ /dev/null @@ -1,1269 +0,0 @@ -package prog8tests - -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.empty -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance -import prog8.ast.base.ByteDatatypes -import prog8.ast.base.DataType -import prog8.ast.base.IterableDatatypes -import prog8.ast.base.WordDatatypes -import prog8.compiler.HeapValues -import prog8.compiler.intermediate.Instruction -import prog8.compiler.intermediate.Opcode -import prog8.vm.RuntimeValue -import prog8.vm.stackvm.* -import kotlin.test.* - - -/*** - -@todo opcodes still to be unit-tested: - - SHL_MEM_BYTE, - SHL_MEM_WORD, - SHL_VAR_BYTE, - SHL_VAR_WORD, - SHR_MEM_BYTE, - SHR_MEM_WORD, - SHR_VAR_BYTE, - SHR_VAR_WORD, - ROL_MEM_BYTE, - ROL_MEM_WORD, - ROL_VAR_BYTE, - ROL_VAR_WORD, - ROR_MEM, - ROR_MEM_W, - ROR_VAR, - ROR_VAR_W, - ROL2_MEM, - ROL2_MEM_W, - ROL2_VAR, - ROL2_VAR_W, - ROR2_MEM, - ROR2_MEM_W, - ROR2_VAR, - ROR2_VAR_W - - and several others. - -**/ - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class TestStackVmOpcodes { - - private val vm = StackVm(null) - - private fun makeProg(ins: MutableList, - vars: Map?=null, - memoryPointers: Map>?=null, - labels: Map?=null, - mem: Map>?=null) : Program { - val heap = HeapValues() - return Program("test", ins, vars ?: mapOf(), memoryPointers ?: mapOf(), labels ?: mapOf(), mem - ?: mapOf(), heap) - } - - @Test - fun testInitAndNop() { - val ins = mutableListOf(Instruction(Opcode.NOP)) - vm.load(makeProg(ins), null) - assertEquals(3, vm.variables.size) - assertTrue(vm.variables.containsKey("A")) - vm.step(1) - assertThat(vm.callstack, empty()) - assertThat(vm.evalstack, empty()) - assertFailsWith { - vm.step() - } - } - - @Test - fun testBreakpoint() { - val ins = mutableListOf(Instruction(Opcode.BREAKPOINT)) - vm.load(makeProg(ins), null) - assertFailsWith { - vm.step() - } - assertThat(vm.callstack, empty()) - assertThat(vm.evalstack, empty()) - } - - @Test - fun testLine() { - val ins = mutableListOf(Instruction(Opcode.LINE, callLabel = "line 99")) - vm.load(makeProg(ins), null) - assertEquals("", vm.sourceLine) - vm.step(1) - assertEquals("line 99", vm.sourceLine) - } - - @Test - fun testSECandSEIandCLCandCLI() { - val ins = mutableListOf(Instruction(Opcode.SEC), Instruction(Opcode.SEI), Instruction(Opcode.CLC), Instruction(Opcode.CLI)) - vm.load(makeProg(ins), null) - assertFalse(vm.P_carry) - assertFalse(vm.P_irqd) - vm.step(1) - assertTrue(vm.P_carry) - assertFalse(vm.P_irqd) - vm.step(1) - assertTrue(vm.P_carry) - assertTrue(vm.P_irqd) - vm.step(1) - assertFalse(vm.P_carry) - assertTrue(vm.P_irqd) - vm.step(1) - assertFalse(vm.P_carry) - assertFalse(vm.P_irqd) - } - - @Test - fun testPushWrongDt() { - val ins = mutableListOf(Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UWORD, 4299))) - vm.load(makeProg(ins), null) - assertFailsWith { - vm.step(1) - } - } - - @Test - fun testPush() { - val ins = mutableListOf(Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, 42.999))) - vm.load(makeProg(ins), null) - assertThat(vm.evalstack, empty()) - vm.step(1) - assertEquals(1, vm.evalstack.size) - assertEquals(RuntimeValue(DataType.FLOAT, 42.999), vm.evalstack.pop()) - } - - @Test - fun testPushMem() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_MEM_B, RuntimeValue(DataType.UWORD, 0x2000)), - Instruction(Opcode.PUSH_MEM_UB, RuntimeValue(DataType.UWORD, 0x3000)), - Instruction(Opcode.PUSH_MEM_W, RuntimeValue(DataType.UWORD, 0x4000)), - Instruction(Opcode.PUSH_MEM_UW, RuntimeValue(DataType.UWORD, 0x5000)), - Instruction(Opcode.PUSH_MEM_FLOAT, RuntimeValue(DataType.UWORD, 0x6000)) - ) - val mem=mapOf(0x2000 to listOf(RuntimeValue(DataType.UWORD, 0xc2ca)), - 0x3000 to listOf(RuntimeValue(DataType.UWORD, 0xc2ca)), - 0x4000 to listOf(RuntimeValue(DataType.UWORD, 0xc2ca)), - 0x5000 to listOf(RuntimeValue(DataType.UWORD, 0xc2ca)), - 0x6000 to listOf(RuntimeValue(DataType.FLOAT, 42.25))) - vm.load(makeProg(ins, mem=mem), null) - 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(5) - assertEquals(5, vm.evalstack.size) - assertEquals(RuntimeValue(DataType.FLOAT, 42.25), vm.evalstack.pop()) - assertEquals(RuntimeValue(DataType.UWORD, 0xc2ca), vm.evalstack.pop()) - assertEquals(RuntimeValue(DataType.WORD, -15670), vm.evalstack.pop()) - assertEquals(RuntimeValue(DataType.UBYTE, 0xca), vm.evalstack.pop()) - assertEquals(RuntimeValue(DataType.BYTE, -54), vm.evalstack.pop()) - } - - @Test - fun testPushVar() { - val ins = mutableListOf(Instruction(Opcode.PUSH_VAR_FLOAT, callLabel = "varname")) - vm.load(makeProg(ins, mapOf("varname" to RuntimeValue(DataType.FLOAT, 42.999))), null) - assertEquals(4, vm.variables.size) - assertTrue(vm.variables.containsKey("varname")) - assertTrue(vm.variables.containsKey("A")) - assertEquals(RuntimeValue(DataType.FLOAT, 42.999), vm.variables["varname"]) - assertThat(vm.evalstack, empty()) - vm.step(1) - assertEquals(1, vm.evalstack.size) - assertEquals(RuntimeValue(DataType.FLOAT, 42.999), vm.evalstack.pop()) - assertEquals(RuntimeValue(DataType.FLOAT, 42.999), vm.variables["varname"]) - } - - @Test - fun testDiscard() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, 42.999)), - Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, 3.1415)), - Instruction(Opcode.DISCARD_FLOAT)) - vm.load(makeProg(ins), null) - assertThat(vm.evalstack, empty()) - vm.step(2) - assertEquals(2, vm.evalstack.size) - vm.step(1) - assertEquals(1, vm.evalstack.size) - assertEquals(RuntimeValue(DataType.FLOAT, 42.999), vm.evalstack.pop()) - } - - @Test - fun testPopMem() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, 42.25)), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0xc2ca)), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.WORD, -23456)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 177)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.BYTE, -55)), - Instruction(Opcode.POP_MEM_BYTE, RuntimeValue(DataType.UWORD, 0x2000)), - Instruction(Opcode.POP_MEM_BYTE, RuntimeValue(DataType.UWORD, 0x2001)), - Instruction(Opcode.POP_MEM_WORD, RuntimeValue(DataType.UWORD, 0x3000)), - Instruction(Opcode.POP_MEM_WORD, RuntimeValue(DataType.UWORD, 0x3002)), - Instruction(Opcode.POP_MEM_FLOAT, RuntimeValue(DataType.UWORD, 0x4000))) - vm.load(makeProg(ins), null) - 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(11) - assertThat(vm.evalstack, empty()) - 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)) - } - - @Test - fun testPopVar() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, 42.25)), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0x42ea)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 123)), - Instruction(Opcode.POP_VAR_BYTE, callLabel = "var1"), - Instruction(Opcode.POP_VAR_WORD, callLabel = "var2"), - Instruction(Opcode.POP_VAR_FLOAT, callLabel = "var3")) - val vars = mapOf( - "var1" to RuntimeValue(DataType.UBYTE, 0), - "var2" to RuntimeValue(DataType.UWORD, 0), - "var3" to RuntimeValue(DataType.FLOAT, 0) - ) - vm.load(makeProg(ins, vars), null) - assertEquals(6, vm.variables.size) - vm.step(6) - assertEquals(RuntimeValue(DataType.UBYTE, 123), vm.variables["var1"]) - assertEquals(RuntimeValue(DataType.UWORD, 0x42ea), vm.variables["var2"]) - assertEquals(RuntimeValue(DataType.FLOAT, 42.25), vm.variables["var3"]) - - val ins2 = mutableListOf( - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0x42ea)), - Instruction(Opcode.POP_VAR_WORD, callLabel = "var1")) - val vars2 = mapOf( - "var1" to RuntimeValue(DataType.UBYTE, 0) - ) - vm.load(makeProg(ins2, vars2), null) - assertEquals(4, vm.variables.size) - assertFailsWith { - vm.step(2) - } - } - - @Test - fun testAdd() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 140), Opcode.ADD_UB, RuntimeValue(DataType.UBYTE, 222), RuntimeValue(DataType.UBYTE, 106)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 40), Opcode.ADD_UB, RuntimeValue(DataType.UBYTE, 122), RuntimeValue(DataType.UBYTE, 162)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 4000), Opcode.ADD_UW, RuntimeValue(DataType.UWORD, 40), RuntimeValue(DataType.UWORD, 4040)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 24000), Opcode.ADD_UW, RuntimeValue(DataType.UWORD, 55000), RuntimeValue(DataType.UWORD, 13464)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 4000.0), Opcode.ADD_F, RuntimeValue(DataType.FLOAT, 123.22), RuntimeValue(DataType.FLOAT, 4123.22)) - assertFailsWith { - testBinaryOperator(RuntimeValue(DataType.UWORD, 4000 + 40), Opcode.ADD_UW, RuntimeValue(DataType.FLOAT, 42.25), RuntimeValue(DataType.FLOAT, 42.25 + (4000 + 40))) - } - } - - @Test - fun testSub() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 250), Opcode.SUB_UB, RuntimeValue(DataType.UBYTE, 70), RuntimeValue(DataType.UBYTE, 180)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 4000), Opcode.SUB_UW, RuntimeValue(DataType.UWORD, 123), RuntimeValue(DataType.UWORD, 4000 - 123)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 123.44), Opcode.SUB_F, RuntimeValue(DataType.FLOAT, 23.44), RuntimeValue(DataType.FLOAT, 100.0)) - assertFailsWith { - testBinaryOperator(RuntimeValue(DataType.UWORD, 4000 - 40), Opcode.SUB_UW, RuntimeValue(DataType.FLOAT, 42.25), RuntimeValue(DataType.FLOAT, 42.25 - (4000 - 40))) - } - } - - @Test - fun testMul() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 41), Opcode.MUL_UB, RuntimeValue(DataType.UBYTE, 4), RuntimeValue(DataType.UBYTE, 164)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 401), Opcode.MUL_UW, RuntimeValue(DataType.UWORD, 4), RuntimeValue(DataType.UWORD, 401 * 4)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 40.1), Opcode.MUL_F, RuntimeValue(DataType.FLOAT, 2.4), RuntimeValue(DataType.FLOAT, 96.24)) - assertFailsWith { - testBinaryOperator(RuntimeValue(DataType.UWORD, 401 * 4), Opcode.MUL_UW, RuntimeValue(DataType.FLOAT, 42.2533), RuntimeValue(DataType.FLOAT, 42.2533 * (401 * 4))) - } - } - - @Test - fun testDiv() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 250), Opcode.IDIV_UB, RuntimeValue(DataType.UBYTE, 12), RuntimeValue(DataType.UBYTE, 20)) - testBinaryOperator(RuntimeValue(DataType.BYTE, 120), Opcode.IDIV_B, RuntimeValue(DataType.BYTE, -9), RuntimeValue(DataType.BYTE, -13)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 3999), Opcode.IDIV_UW, RuntimeValue(DataType.UWORD, 40), RuntimeValue(DataType.UWORD, 99)) - testBinaryOperator(RuntimeValue(DataType.WORD, 3999), Opcode.IDIV_W, RuntimeValue(DataType.WORD, -40), RuntimeValue(DataType.WORD, -99)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 42.25), Opcode.DIV_F, RuntimeValue(DataType.FLOAT, 99.0), RuntimeValue(DataType.FLOAT, 42.25 / 99.0)) - assertFailsWith { - testBinaryOperator(RuntimeValue(DataType.UWORD, 3333), Opcode.IDIV_UW, RuntimeValue(DataType.FLOAT, 2.22), RuntimeValue(DataType.FLOAT, 3333 / 2.22)) - } - } - - @Test - fun testPow() { - testBinaryOperator(RuntimeValue(DataType.FLOAT, 1.1), Opcode.POW_F, RuntimeValue(DataType.FLOAT, 81.0), RuntimeValue(DataType.FLOAT, 2253.2402360440274)) - } - - @Test - fun testRemainder() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 250), Opcode.REMAINDER_UB, RuntimeValue(DataType.UBYTE, 29), RuntimeValue(DataType.UBYTE, 18)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 500), Opcode.REMAINDER_UW, RuntimeValue(DataType.UWORD, 29), RuntimeValue(DataType.UWORD, 7)) - } - - @Test - fun testBitand() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0b10011111), Opcode.BITAND_BYTE, RuntimeValue(DataType.UBYTE, 0b11111101), RuntimeValue(DataType.UBYTE, 0b10011101)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 0b0011001011110001), Opcode.BITAND_WORD, RuntimeValue(DataType.UWORD, 0b1110000010011101), RuntimeValue(DataType.UWORD, 0b0010000010010001)) - } - - @Test - fun testBitor() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0b00011101), Opcode.BITOR_BYTE, RuntimeValue(DataType.UBYTE, 0b10010001), RuntimeValue(DataType.UBYTE, 0b10011101)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 0b0011001011100000), Opcode.BITOR_WORD, RuntimeValue(DataType.UWORD, 0b1000000010011101), RuntimeValue(DataType.UWORD, 0b1011001011111101)) - } - - @Test - fun testBitxor() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0b00011101), Opcode.BITXOR_BYTE, RuntimeValue(DataType.UBYTE, 0b10010001), RuntimeValue(DataType.UBYTE, 0b10001100)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 0b0011001011100000), Opcode.BITXOR_WORD, RuntimeValue(DataType.UWORD, 0b1000000010001100), RuntimeValue(DataType.UWORD, 0b1011001001101100)) - } - - @Test - fun testAnd() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 200), Opcode.AND_BYTE, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 200), Opcode.AND_BYTE, RuntimeValue(DataType.UBYTE, 0), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0), Opcode.AND_BYTE, RuntimeValue(DataType.UBYTE, 101), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 200), Opcode.AND_WORD, RuntimeValue(DataType.UWORD, 13455), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 200), Opcode.AND_WORD, RuntimeValue(DataType.UWORD, 0), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 0), Opcode.AND_WORD, RuntimeValue(DataType.UWORD, 101), RuntimeValue(DataType.UBYTE, 0)) - } - - @Test - fun testOr() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 200), Opcode.OR_BYTE, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 200), Opcode.OR_BYTE, RuntimeValue(DataType.UBYTE, 0), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0), Opcode.OR_BYTE, RuntimeValue(DataType.UBYTE, 0), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 200), Opcode.OR_WORD, RuntimeValue(DataType.UWORD, 13455), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 200), Opcode.OR_WORD, RuntimeValue(DataType.UWORD, 0), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 0), Opcode.OR_WORD, RuntimeValue(DataType.UWORD, 0), RuntimeValue(DataType.UBYTE, 0)) - } - - @Test - fun testXor() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 200), Opcode.XOR_BYTE, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 200), Opcode.XOR_BYTE, RuntimeValue(DataType.UBYTE, 0), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0), Opcode.XOR_BYTE, RuntimeValue(DataType.UBYTE, 0), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 200), Opcode.XOR_WORD, RuntimeValue(DataType.UWORD, 13455), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 200), Opcode.XOR_WORD, RuntimeValue(DataType.UWORD, 0), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 0), Opcode.XOR_WORD, RuntimeValue(DataType.UWORD, 0), RuntimeValue(DataType.UBYTE, 0)) - } - - @Test - fun testNot() { - testUnaryOperator(RuntimeValue(DataType.UBYTE, 0), Opcode.NOT_BYTE, RuntimeValue(DataType.UBYTE, 1)) - testUnaryOperator(RuntimeValue(DataType.UBYTE, 20), Opcode.NOT_BYTE, RuntimeValue(DataType.UBYTE, 0)) - testUnaryOperator(RuntimeValue(DataType.UWORD, 0), Opcode.NOT_WORD, RuntimeValue(DataType.UBYTE, 1)) - testUnaryOperator(RuntimeValue(DataType.UWORD, 5000), Opcode.NOT_WORD, RuntimeValue(DataType.UBYTE, 0)) - } - - @Test - fun testNeg() { - testUnaryOperator(RuntimeValue(DataType.BYTE, 12), Opcode.NEG_B, RuntimeValue(DataType.BYTE, -12)) - testUnaryOperator(RuntimeValue(DataType.WORD, 1234), Opcode.NEG_W, RuntimeValue(DataType.WORD, -1234)) - testUnaryOperator(RuntimeValue(DataType.FLOAT, 123.456), Opcode.NEG_F, RuntimeValue(DataType.FLOAT, -123.456)) - assertFailsWith { - testUnaryOperator(RuntimeValue(DataType.UBYTE, 12), Opcode.NEG_B, RuntimeValue(DataType.UBYTE, 244)) - } - assertFailsWith { - testUnaryOperator(RuntimeValue(DataType.UWORD, 1234), Opcode.NEG_W, RuntimeValue(DataType.UWORD, 64302)) - } - } - - @Test - fun testInv() { - testUnaryOperator(RuntimeValue(DataType.UBYTE, 123), Opcode.INV_BYTE, RuntimeValue(DataType.UBYTE, 0x84)) - testUnaryOperator(RuntimeValue(DataType.UWORD, 4044), Opcode.INV_WORD, RuntimeValue(DataType.UWORD, 0xf033)) - assertFailsWith { - testUnaryOperator(RuntimeValue(DataType.BYTE, 123), Opcode.INV_BYTE, RuntimeValue(DataType.BYTE, -124)) - } - assertFailsWith { - testUnaryOperator(RuntimeValue(DataType.WORD, 4044), Opcode.INV_WORD, RuntimeValue(DataType.WORD, -4043)) - } - } - - @Test - fun testMsb() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 0x45)), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0xea31)), - Instruction(Opcode.MSB), - Instruction(Opcode.MSB) - ) - vm.load(makeProg(ins), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.UBYTE, 0xea), vm.evalstack.pop()) - assertFailsWith { - vm.step(1) - } - } - - // @todo more conversion tests. - - @Test - fun testB2Ub() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.BYTE, -88)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.BYTE, 127)), - Instruction(Opcode.CAST_B_TO_UB), - Instruction(Opcode.CAST_B_TO_UB) - ) - vm.load(makeProg(ins), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.UBYTE, 127), vm.evalstack.pop()) - vm.step(1) - assertEquals(RuntimeValue(DataType.UBYTE, 168), vm.evalstack.pop()) - } - - @Test - fun testUB2b() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 168)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 127)), - Instruction(Opcode.CAST_UB_TO_B), - Instruction(Opcode.CAST_UB_TO_B) - ) - vm.load(makeProg(ins), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.BYTE, 127), vm.evalstack.pop()) - vm.step(1) - assertEquals(RuntimeValue(DataType.BYTE, -88), vm.evalstack.pop()) - } - - @Test - fun testB2Word() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.WORD, 0x7a31)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.BYTE, 127)), - Instruction(Opcode.CAST_B_TO_W), - Instruction(Opcode.CAST_B_TO_W) - ) - vm.load(makeProg(ins), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.WORD, 127), vm.evalstack.pop()) - assertFailsWith { - vm.step(1) - } - } - - @Test - fun testUB2Uword() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0xea31)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 0x45)), - Instruction(Opcode.CAST_UB_TO_UW), - Instruction(Opcode.CAST_UB_TO_UW) - ) - vm.load(makeProg(ins), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.UWORD, 0x0045), vm.evalstack.pop()) - assertFailsWith { - vm.step(1) - } - } - - @Test - fun testB2Float() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.WORD, 0x7a31)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.BYTE, 127)), - Instruction(Opcode.CAST_B_TO_F), - Instruction(Opcode.CAST_B_TO_F) - ) - vm.load(makeProg(ins), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.FLOAT, 127.0), vm.evalstack.pop()) - assertFailsWith { - vm.step(1) - } - } - - @Test - fun testUB2Float() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0xea31)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 177)), - Instruction(Opcode.CAST_UB_TO_F), - Instruction(Opcode.CAST_UB_TO_F) - ) - vm.load(makeProg(ins), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.FLOAT, 177.0), vm.evalstack.pop()) - assertFailsWith { - vm.step(1) - } - } - - @Test - fun testW2Float() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 177)), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.WORD, -12345)), - Instruction(Opcode.CAST_W_TO_F), - Instruction(Opcode.CAST_W_TO_F) - ) - vm.load(makeProg(ins), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.FLOAT, -12345.0), vm.evalstack.pop()) - assertFailsWith { - vm.step(1) - } - } - - @Test - fun testUW2Float() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 177)), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 52345)), - Instruction(Opcode.CAST_UW_TO_F), - Instruction(Opcode.CAST_UW_TO_F) - ) - vm.load(makeProg(ins), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.FLOAT, 52345.0), vm.evalstack.pop()) - assertFailsWith { - vm.step(1) - } - } - - @Test - fun testIncVar() { - val ins = mutableListOf( - Instruction(Opcode.INC_VAR_UW, callLabel = "var1"), - Instruction(Opcode.INC_VAR_UB, callLabel = "var2"), - Instruction(Opcode.INC_VAR_F, callLabel = "var3"), - Instruction(Opcode.INC_VAR_UW, callLabel = "var1"), - Instruction(Opcode.INC_VAR_UB, callLabel = "var2"), - Instruction(Opcode.INC_VAR_F, callLabel = "var3") - ) - val vars = mapOf("var1" to RuntimeValue(DataType.UWORD, 65534), - "var2" to RuntimeValue(DataType.UBYTE, 254), - "var3" to RuntimeValue(DataType.FLOAT, -1.5) - ) - vm.load(makeProg(ins, vars = vars), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.UWORD, 65535), vm.variables["var1"]) - assertEquals(RuntimeValue(DataType.UBYTE, 255), vm.variables["var2"]) - assertEquals(RuntimeValue(DataType.FLOAT, -0.5), vm.variables["var3"]) - vm.step(3) - assertEquals(RuntimeValue(DataType.UWORD, 0), vm.variables["var1"]) - assertEquals(RuntimeValue(DataType.UBYTE, 0), vm.variables["var2"]) - assertEquals(RuntimeValue(DataType.FLOAT, 0.5), vm.variables["var3"]) - } - - @Test - fun testDecVar() { - val ins = mutableListOf( - Instruction(Opcode.DEC_VAR_UW, callLabel = "var1"), - Instruction(Opcode.DEC_VAR_UB, callLabel = "var2"), - Instruction(Opcode.DEC_VAR_F, callLabel = "var3"), - Instruction(Opcode.DEC_VAR_UW, callLabel = "var1"), - Instruction(Opcode.DEC_VAR_UB, callLabel = "var2"), - Instruction(Opcode.DEC_VAR_F, callLabel = "var3") - ) - val vars = mapOf("var1" to RuntimeValue(DataType.UWORD, 1), - "var2" to RuntimeValue(DataType.UBYTE, 1), - "var3" to RuntimeValue(DataType.FLOAT, 1.5) - ) - vm.load(makeProg(ins, vars = vars), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.UWORD, 0), vm.variables["var1"]) - assertEquals(RuntimeValue(DataType.UBYTE, 0), vm.variables["var2"]) - assertEquals(RuntimeValue(DataType.FLOAT, 0.5), vm.variables["var3"]) - vm.step(3) - assertEquals(RuntimeValue(DataType.UWORD, 65535), vm.variables["var1"]) - assertEquals(RuntimeValue(DataType.UBYTE, 255), vm.variables["var2"]) - assertEquals(RuntimeValue(DataType.FLOAT, -0.5), vm.variables["var3"]) - } - - @Test - fun testSyscall() { - val ins = mutableListOf( - Instruction(Opcode.SYSCALL, RuntimeValue(DataType.UBYTE, Syscall.FUNC_RNDF.callNr)), - Instruction(Opcode.SYSCALL, RuntimeValue(DataType.UBYTE, Syscall.FUNC_RNDW.callNr)), - Instruction(Opcode.SYSCALL, RuntimeValue(DataType.UBYTE, Syscall.FUNC_RND.callNr)), - Instruction(Opcode.SYSCALL, RuntimeValue(DataType.UBYTE, Syscall.FUNC_RND.callNr)), - - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 25544)), - Instruction(Opcode.SYSCALL, RuntimeValue(DataType.UBYTE, Syscall.FUNC_SIN.callNr)) - ) - vm.load(makeProg(ins), null) - vm.step(4) - - val rndb1 = vm.evalstack.pop() - val rndb2 = vm.evalstack.pop() - val rndw = vm.evalstack.pop() - val rndf = vm.evalstack.pop() - assertEquals(DataType.UBYTE, rndb1.type) - assertEquals(DataType.UBYTE, rndb2.type) - assertEquals(DataType.UWORD, rndw.type) - assertEquals(DataType.FLOAT, rndf.type) - assertNotEquals(rndb1.integerValue(), rndb2.integerValue()) // this *sometimes* fails when the two random numbers are the isSameAs by pure chance - assertTrue(rndf.numericValue().toDouble() > 0.0 && rndf.numericValue().toDouble() < 1.0) - - vm.step(2) - assertEquals(RuntimeValue(DataType.FLOAT, 0.28582414234140724), vm.evalstack.pop()) - } - - @Test - fun testLess() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0), Opcode.LESS_UB, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0), Opcode.LESS_UB, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.BYTE, 1), Opcode.LESS_B, RuntimeValue(DataType.BYTE, 1), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.BYTE, -2), Opcode.LESS_B, RuntimeValue(DataType.BYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - - testBinaryOperator(RuntimeValue(DataType.UWORD, 2), Opcode.LESS_UW, RuntimeValue(DataType.UWORD, 20), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 20), Opcode.LESS_UW, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.LESS_UW, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.LESS_UW, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.WORD, 21), Opcode.LESS_W, RuntimeValue(DataType.WORD, 21), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.WORD, -2), Opcode.LESS_W, RuntimeValue(DataType.WORD, 1), RuntimeValue(DataType.UBYTE, 1)) - - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.LESS_F, RuntimeValue(DataType.FLOAT, 21.0), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.LESS_F, RuntimeValue(DataType.FLOAT, 21.001), RuntimeValue(DataType.UBYTE, 1)) - } - - @Test - fun testLessEq() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0), Opcode.LESSEQ_UB, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 1), Opcode.LESSEQ_UB, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.BYTE, 1), Opcode.LESSEQ_B, RuntimeValue(DataType.BYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.BYTE, -2), Opcode.LESSEQ_B, RuntimeValue(DataType.BYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - - testBinaryOperator(RuntimeValue(DataType.UWORD, 2), Opcode.LESSEQ_UW, RuntimeValue(DataType.UWORD, 20), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 20), Opcode.LESSEQ_UW, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.LESSEQ_UW, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.LESSEQ_UW, RuntimeValue(DataType.UWORD, 20), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.WORD, 21), Opcode.LESSEQ_W, RuntimeValue(DataType.WORD, 20), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.WORD, -2), Opcode.LESSEQ_W, RuntimeValue(DataType.WORD, 1), RuntimeValue(DataType.UBYTE, 1)) - - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.LESSEQ_F, RuntimeValue(DataType.FLOAT, 21.0), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.LESSEQ_F, RuntimeValue(DataType.FLOAT, 20.999), RuntimeValue(DataType.UBYTE, 0)) - } - - @Test - fun testGreater() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0), Opcode.GREATER_UB, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 1), Opcode.GREATER_UB, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.BYTE, 1), Opcode.GREATER_B, RuntimeValue(DataType.BYTE, -1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.BYTE, -1), Opcode.GREATER_B, RuntimeValue(DataType.BYTE, 1), RuntimeValue(DataType.UBYTE, 0)) - - testBinaryOperator(RuntimeValue(DataType.UWORD, 2), Opcode.GREATER_UW, RuntimeValue(DataType.UWORD, 20), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 20), Opcode.GREATER_UW, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.GREATER_UW, RuntimeValue(DataType.UWORD, 20), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.GREATER_UW, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.WORD, 21), Opcode.GREATER_W, RuntimeValue(DataType.WORD, -21), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.WORD, -2), Opcode.GREATER_W, RuntimeValue(DataType.WORD, 21), RuntimeValue(DataType.UBYTE, 0)) - - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.GREATER_F, RuntimeValue(DataType.FLOAT, 21.0), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.GREATER_F, RuntimeValue(DataType.FLOAT, 20.999), RuntimeValue(DataType.UBYTE, 1)) - } - - @Test - fun testGreaterEq() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0), Opcode.GREATEREQ_UB, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 1), Opcode.GREATEREQ_UB, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.BYTE, 1), Opcode.GREATEREQ_B, RuntimeValue(DataType.BYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.BYTE, -11), Opcode.GREATEREQ_B, RuntimeValue(DataType.BYTE, 11), RuntimeValue(DataType.UBYTE, 0)) - - testBinaryOperator(RuntimeValue(DataType.UWORD, 2), Opcode.GREATEREQ_UW, RuntimeValue(DataType.UWORD, 20), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 20), Opcode.GREATEREQ_UW, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.GREATEREQ_UW, RuntimeValue(DataType.UWORD, 20), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.GREATEREQ_UW, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.WORD, 21), Opcode.GREATEREQ_W, RuntimeValue(DataType.WORD, 21), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.WORD, -21), Opcode.GREATEREQ_W, RuntimeValue(DataType.WORD, 21), RuntimeValue(DataType.UBYTE, 0)) - - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.GREATEREQ_F, RuntimeValue(DataType.FLOAT, 21.0), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.GREATEREQ_F, RuntimeValue(DataType.FLOAT, 21.001), RuntimeValue(DataType.UBYTE, 0)) - } - - @Test - fun testEqual() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0), Opcode.EQUAL_BYTE, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 1), Opcode.EQUAL_BYTE, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 2), Opcode.EQUAL_WORD, RuntimeValue(DataType.UWORD, 20), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 20), Opcode.EQUAL_WORD, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.EQUAL_WORD, RuntimeValue(DataType.UWORD, 20), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.EQUAL_WORD, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.EQUAL_F, RuntimeValue(DataType.FLOAT, 21.0), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.EQUAL_F, RuntimeValue(DataType.FLOAT, 21.001), RuntimeValue(DataType.UBYTE, 0)) - } - - @Test - fun testNotEqual() { - testBinaryOperator(RuntimeValue(DataType.UBYTE, 0), Opcode.NOTEQUAL_BYTE, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UBYTE, 1), Opcode.NOTEQUAL_BYTE, RuntimeValue(DataType.UBYTE, 1), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 2), Opcode.NOTEQUAL_WORD, RuntimeValue(DataType.UWORD, 20), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 20), Opcode.NOTEQUAL_WORD, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.NOTEQUAL_WORD, RuntimeValue(DataType.UWORD, 20), RuntimeValue(DataType.UBYTE, 1)) - testBinaryOperator(RuntimeValue(DataType.UWORD, 21), Opcode.NOTEQUAL_WORD, RuntimeValue(DataType.UWORD, 21), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.NOTEQUAL_F, RuntimeValue(DataType.FLOAT, 21.0), RuntimeValue(DataType.UBYTE, 0)) - testBinaryOperator(RuntimeValue(DataType.FLOAT, 21.0), Opcode.NOTEQUAL_F, RuntimeValue(DataType.FLOAT, 21.001), RuntimeValue(DataType.UBYTE, 1)) - } - - @Test - fun testBCC() { - val ins = mutableListOf( - Instruction(Opcode.SEC), - Instruction(Opcode.BCC, callLabel = "label"), - Instruction(Opcode.CLC), - Instruction(Opcode.BCC, callLabel = "label"), - Instruction(Opcode.LINE, callLabel = "string1"), - Instruction(Opcode.TERMINATE), - Instruction(Opcode.LINE, callLabel = "string2")) - val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction - vm.load(makeProg(ins, labels=labels), null) - vm.step(2) - assertEquals("", vm.sourceLine) - vm.step(3) - assertEquals("string2", vm.sourceLine) - assertEquals(0, vm.callstack.size) - assertEquals(0, vm.evalstack.size) - } - - @Test - fun testBCS() { - val ins = mutableListOf( - Instruction(Opcode.CLC), - Instruction(Opcode.BCS, callLabel = "label"), - Instruction(Opcode.SEC), - Instruction(Opcode.BCS, callLabel = "label"), - Instruction(Opcode.LINE, callLabel = "string1"), - Instruction(Opcode.TERMINATE), - Instruction(Opcode.LINE, callLabel = "string2")) - val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction - vm.load(makeProg(ins, labels=labels), null) - assertFalse(vm.P_carry) - vm.step(2) - assertEquals("", vm.sourceLine) - vm.step(3) - assertEquals("string2", vm.sourceLine) - assertEquals(0, vm.callstack.size) - assertEquals(0, vm.evalstack.size) - } - - @Test - fun testJZ() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0xaa01)), - Instruction(Opcode.JZ, callLabel = "label"), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0xaa00)), - Instruction(Opcode.JZ, callLabel = "label"), - Instruction(Opcode.LINE, callLabel = "string1"), - Instruction(Opcode.TERMINATE), - Instruction(Opcode.LINE, callLabel = "string2")) - val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction - vm.load(makeProg(ins, labels=labels), null) - vm.step(2) - assertEquals("", vm.sourceLine) - vm.step(3) - assertEquals("string2", vm.sourceLine) - assertEquals(0, vm.callstack.size) - assertEquals(0, vm.evalstack.size) - } - - @Test - fun testJNZ() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0xaa00)), - Instruction(Opcode.JNZ, callLabel = "label"), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0xaa01)), - Instruction(Opcode.JNZ, callLabel = "label"), - Instruction(Opcode.LINE, callLabel = "string1"), - Instruction(Opcode.TERMINATE), - Instruction(Opcode.LINE, callLabel = "string2")) - val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction - vm.load(makeProg(ins, labels=labels), null) - vm.step(2) - assertEquals("", vm.sourceLine) - vm.step(3) - assertEquals("string2", vm.sourceLine) - assertEquals(0, vm.callstack.size) - assertEquals(0, vm.evalstack.size) - } - - @Test - fun testJZW() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0xaa00)), - Instruction(Opcode.JZW, callLabel = "label"), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0x0000)), - Instruction(Opcode.JZW, callLabel = "label"), - Instruction(Opcode.LINE, callLabel = "string1"), - Instruction(Opcode.TERMINATE), - Instruction(Opcode.LINE, callLabel = "string2")) - val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction - vm.load(makeProg(ins, labels=labels), null) - vm.step(2) - assertEquals("", vm.sourceLine) - vm.step(3) - assertEquals("string2", vm.sourceLine) - assertEquals(0, vm.callstack.size) - assertEquals(0, vm.evalstack.size) - } - - @Test - fun testJNZW() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0x0000)), - Instruction(Opcode.JNZW, callLabel = "label"), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0xaa00)), - Instruction(Opcode.JNZW, callLabel = "label"), - Instruction(Opcode.LINE, callLabel = "string1"), - Instruction(Opcode.TERMINATE), - Instruction(Opcode.LINE, callLabel = "string2")) - val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction - vm.load(makeProg(ins, labels=labels), null) - vm.step(2) - assertEquals("", vm.sourceLine) - vm.step(3) - assertEquals("string2", vm.sourceLine) - assertEquals(0, vm.callstack.size) - assertEquals(0, vm.evalstack.size) - } - - @Test - fun testJump() { - val ins = mutableListOf( - Instruction(Opcode.JUMP, callLabel = "label"), - Instruction(Opcode.LINE, callLabel = "string1"), - Instruction(Opcode.TERMINATE), - Instruction(Opcode.LINE, callLabel = "string2")) - val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction - vm.load(makeProg(ins, labels=labels), null) - vm.step(2) - assertEquals("string2", vm.sourceLine) - assertEquals(0, vm.callstack.size) - assertEquals(0, vm.evalstack.size) - } - - @Test - fun testReturn() { - val ins = mutableListOf( - Instruction(Opcode.RETURN), - Instruction(Opcode.TERMINATE), - Instruction(Opcode.LINE, callLabel = "string1") - ) - vm.load(makeProg(ins), null) - assertFailsWith { - vm.step(1) - } - - vm.callstack.add(2) // set the LINE opcode as return instruction - assertEquals("", vm.sourceLine) - vm.step(2) - assertEquals("string1", vm.sourceLine) - assertEquals(0, vm.callstack.size) - assertEquals(0, vm.evalstack.size) - } - - @Test - fun testCall() { - val ins = mutableListOf( - Instruction(Opcode.CALL, callLabel = "label"), - Instruction(Opcode.LINE, callLabel = "returned"), - Instruction(Opcode.TERMINATE), - Instruction(Opcode.LINE, callLabel = "called"), - Instruction(Opcode.RETURN) - ) - val labels = mapOf("label" to 3) // points to the LINE instruction - vm.load(makeProg(ins, labels = labels), null) - vm.step(1) - assertEquals("", vm.sourceLine) - assertEquals(1, vm.callstack.size) - assertSame(1, vm.callstack.peek()) - vm.step(1) - assertEquals("called", vm.sourceLine) - vm.step(1) - assertEquals(0, vm.callstack.size) - assertEquals("called", vm.sourceLine) - vm.step(1) - assertEquals("returned", vm.sourceLine) - } - - @Test - fun testSHIFTEDR() { - // @todo test SHR signed byte + signed word - val ins = mutableListOf( - Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, 9.99)), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 3)), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 61005)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 3)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 249)), - Instruction(Opcode.SHIFTEDR_UBYTE), // 124 - Instruction(Opcode.DISCARD_BYTE), - Instruction(Opcode.SHIFTEDR_UBYTE), // 1 - Instruction(Opcode.SHIFTEDR_UBYTE), // 0 - Instruction(Opcode.SHIFTEDR_UBYTE), // 0 - Instruction(Opcode.DISCARD_BYTE), - Instruction(Opcode.SHIFTEDR_UWORD), // 30502 - Instruction(Opcode.DISCARD_WORD), - Instruction(Opcode.SHIFTEDR_UWORD), // 1 - Instruction(Opcode.SHIFTEDR_UWORD), // 0 - Instruction(Opcode.SHIFTEDR_UWORD), // 0 - Instruction(Opcode.DISCARD_WORD), - Instruction(Opcode.SHIFTEDR_UWORD) // error on float - ) - vm.load(makeProg(ins), null) - vm.step(6) - assertEquals(RuntimeValue(DataType.UBYTE, 124), vm.evalstack.peek()) - vm.step(2) - assertEquals(RuntimeValue(DataType.UBYTE, 1), vm.evalstack.peek()) - vm.step(2) - assertEquals(RuntimeValue(DataType.UBYTE, 0), vm.evalstack.peek()) - vm.step(2) - assertEquals(RuntimeValue(DataType.UWORD, 30502), vm.evalstack.peek()) - vm.step(2) - assertEquals(RuntimeValue(DataType.UWORD, 1), vm.evalstack.peek()) - vm.step(2) - assertEquals(RuntimeValue(DataType.UWORD, 0), vm.evalstack.peek()) - vm.step(1) - assertEquals(RuntimeValue(DataType.FLOAT, 9.99), vm.evalstack.peek()) - assertFailsWith { - vm.step(1) // float shift error - } - } - - @Test - fun testSHIFTEDL() { - val ins = mutableListOf( - Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, 9.99)), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 3)), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 61005)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 3)), - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 249)), - Instruction(Opcode.SHIFTEDL_BYTE), // 242 - Instruction(Opcode.DISCARD_BYTE), - Instruction(Opcode.SHIFTEDL_BYTE), // 6 - Instruction(Opcode.DISCARD_BYTE), - Instruction(Opcode.SHIFTEDL_WORD), // 56474 - Instruction(Opcode.DISCARD_WORD), - Instruction(Opcode.SHIFTEDL_WORD), // 6 - Instruction(Opcode.DISCARD_WORD), - Instruction(Opcode.SHIFTEDL_WORD) // error on float - ) - vm.load(makeProg(ins), null) - vm.step(6) - assertEquals(RuntimeValue(DataType.UBYTE, 242), vm.evalstack.peek()) - vm.step(2) - assertEquals(RuntimeValue(DataType.UBYTE, 6), vm.evalstack.peek()) - vm.step(2) - assertEquals(RuntimeValue(DataType.UWORD, 56474), vm.evalstack.peek()) - vm.step(2) - assertEquals(RuntimeValue(DataType.UWORD, 6), vm.evalstack.peek()) - vm.step(1) - assertEquals(RuntimeValue(DataType.FLOAT, 9.99), vm.evalstack.peek()) - assertFailsWith { - vm.step(1) // float shift error - } - } - - @Test - fun testROR() { - // 9/17-bit rotation right (using carry) - val ins = mutableListOf( - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 0b10010011)), - Instruction(Opcode.ROR_BYTE), // 0b01001001 c=1 - Instruction(Opcode.ROR_BYTE), // 0b10100100 c=1 - Instruction(Opcode.ROR_BYTE), // 0b11010010 c=0 - Instruction(Opcode.ROR_BYTE), // 0b01101001 c=0 - Instruction(Opcode.ROR_BYTE), // 0b00110100 c=1 - Instruction(Opcode.ROR_BYTE), // 0b10011010 c=0 - Instruction(Opcode.ROR_BYTE), // 0b01001101 c=0 - Instruction(Opcode.ROR_BYTE), // 0b00100110 c=1 - Instruction(Opcode.ROR_BYTE) // 0b10010011 c=0 (original value after 9 rors) - ) - vm.load(makeProg(ins), null) - vm.step(2) - assertEquals(RuntimeValue(DataType.UBYTE, 0b01001001), vm.evalstack.peek()) - assertTrue(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UBYTE, 0b10100100), vm.evalstack.peek()) - assertTrue(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UBYTE, 0b11010010), vm.evalstack.peek()) - assertFalse(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UBYTE, 0b01101001), vm.evalstack.peek()) - assertFalse(vm.P_carry) - vm.step(5) - assertEquals(RuntimeValue(DataType.UBYTE, 0b10010011), vm.evalstack.peek()) - assertFalse(vm.P_carry) - - val ins2 = mutableListOf( - Instruction(Opcode.CLC), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0b1001001100001101)), - Instruction(Opcode.ROR_WORD), // 0b0100100110000110 c=1 - Instruction(Opcode.ROR_WORD), // 0b1010010011000011 c=0 - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD), - Instruction(Opcode.ROR_WORD) // 0b1001001100001101 c=0 (original value after 17 rors) - ) - vm.load(makeProg(ins2), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.UWORD, 0b0100100110000110), vm.evalstack.peek()) - assertTrue(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UWORD, 0b1010010011000011), vm.evalstack.peek()) - assertFalse(vm.P_carry) - vm.step(15) - assertEquals(RuntimeValue(DataType.UWORD, 0b1001001100001101), vm.evalstack.peek()) - assertFalse(vm.P_carry) - } - - @Test - fun testROL() { - // 9/17-bit rotation left (using carry) - val ins = mutableListOf( - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 0b10010011)), - Instruction(Opcode.ROL_BYTE), // 0b00100110 c=1 - Instruction(Opcode.ROL_BYTE), // 0b01001101 c=0 - Instruction(Opcode.ROL_BYTE), // 0b10011010 c=0 - Instruction(Opcode.ROL_BYTE), // 0b00110100 c=1 - Instruction(Opcode.ROL_BYTE), // 0b01101001 c=0 - Instruction(Opcode.ROL_BYTE), // 0b11010010 c=0 - Instruction(Opcode.ROL_BYTE), // 0b10100100 c=1 - Instruction(Opcode.ROL_BYTE), // 0b01001001 c=1 - Instruction(Opcode.ROL_BYTE) // 0b10010011 c=0 (original value after 9 rors) - ) - vm.load(makeProg(ins), null) - vm.step(2) - assertEquals(RuntimeValue(DataType.UBYTE, 0b00100110), vm.evalstack.peek()) - assertTrue(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UBYTE, 0b01001101), vm.evalstack.peek()) - assertFalse(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UBYTE, 0b10011010), vm.evalstack.peek()) - assertFalse(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UBYTE, 0b00110100), vm.evalstack.peek()) - assertTrue(vm.P_carry) - vm.step(5) - assertEquals(RuntimeValue(DataType.UBYTE, 0b10010011), vm.evalstack.peek()) - assertFalse(vm.P_carry) - - val ins2 = mutableListOf( - Instruction(Opcode.CLC), - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0b1001001100001101)), - Instruction(Opcode.ROL_WORD), // 0b0010011000011010 c=1 - Instruction(Opcode.ROL_WORD), // 0b0100110000110101 c=0 - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD), - Instruction(Opcode.ROL_WORD) // 0b1001001100001101 c=0 (original value after 17 rors) - ) - vm.load(makeProg(ins2), null) - vm.step(3) - assertEquals(RuntimeValue(DataType.UWORD, 0b0010011000011010), vm.evalstack.peek()) - assertTrue(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UWORD, 0b0100110000110101), vm.evalstack.peek()) - assertFalse(vm.P_carry) - vm.step(15) - assertEquals(RuntimeValue(DataType.UWORD, 0b1001001100001101), vm.evalstack.peek()) - assertFalse(vm.P_carry) - } - - @Test - fun testROR2() { - // 8/16-bit rotation right - val ins = mutableListOf( - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 0b10010011)), - Instruction(Opcode.ROR2_BYTE), // 0b11001001 - Instruction(Opcode.ROR2_BYTE), // 0b11100100 - Instruction(Opcode.ROR2_BYTE), // 0b01110010 - Instruction(Opcode.ROR2_BYTE), - Instruction(Opcode.ROR2_BYTE), - Instruction(Opcode.ROR2_BYTE), - Instruction(Opcode.ROR2_BYTE), - Instruction(Opcode.ROR2_BYTE) // 0b10010011 (original value after 8 rors) - ) - vm.load(makeProg(ins), null) - vm.step(2) - assertEquals(RuntimeValue(DataType.UBYTE, 0b11001001), vm.evalstack.peek()) - assertFalse(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UBYTE, 0b11100100), vm.evalstack.peek()) - vm.step(1) - assertEquals(RuntimeValue(DataType.UBYTE, 0b01110010), vm.evalstack.peek()) - vm.step(5) - assertEquals(RuntimeValue(DataType.UBYTE, 0b10010011), vm.evalstack.peek()) - - val ins2 = mutableListOf( - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0b1001001100001101)), - Instruction(Opcode.ROR2_WORD), // 0b1100100110000110 - Instruction(Opcode.ROR2_WORD), // 0b0110010011000011 - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD), - Instruction(Opcode.ROR2_WORD) // 0b1001001100001101 (original value after 16 rors) - ) - vm.load(makeProg(ins2), null) - vm.step(2) - assertEquals(RuntimeValue(DataType.UWORD, 0b1100100110000110), vm.evalstack.peek()) - assertFalse(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UWORD, 0b0110010011000011), vm.evalstack.peek()) - vm.step(14) - assertEquals(RuntimeValue(DataType.UWORD, 0b1001001100001101), vm.evalstack.peek()) - } - - @Test - fun testROL2() { - // 8/16-bit rotation left - val ins = mutableListOf( - Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, 0b10010011)), - Instruction(Opcode.ROL2_BYTE), // 0b00100111 - Instruction(Opcode.ROL2_BYTE), // 0b01001110 - Instruction(Opcode.ROL2_BYTE), - Instruction(Opcode.ROL2_BYTE), - Instruction(Opcode.ROL2_BYTE), - Instruction(Opcode.ROL2_BYTE), - Instruction(Opcode.ROL2_BYTE), - Instruction(Opcode.ROL2_BYTE) // 0b10010011 (original value after 8 rols) - ) - vm.load(makeProg(ins), null) - vm.step(2) - assertEquals(RuntimeValue(DataType.UBYTE, 0b00100111), vm.evalstack.peek()) - assertFalse(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UBYTE, 0b01001110), vm.evalstack.peek()) - vm.step(6) - assertEquals(RuntimeValue(DataType.UBYTE, 0b10010011), vm.evalstack.peek()) - - val ins2 = mutableListOf( - Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, 0b1001001100001101)), - Instruction(Opcode.ROL2_WORD), // 0b0010011000011011 - Instruction(Opcode.ROL2_WORD), // 0b0100110000110110 - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD), - Instruction(Opcode.ROL2_WORD) // 0b1001001100001101 (original value after 16 rols) - ) - vm.load(makeProg(ins2), null) - vm.step(2) - assertEquals(RuntimeValue(DataType.UWORD, 0b0010011000011011), vm.evalstack.peek()) - assertFalse(vm.P_carry) - vm.step(1) - assertEquals(RuntimeValue(DataType.UWORD, 0b0100110000110110), vm.evalstack.peek()) - assertFalse(vm.P_carry) - vm.step(14) - assertEquals(RuntimeValue(DataType.UWORD, 0b1001001100001101), vm.evalstack.peek()) - } - - private fun pushOpcode(dt: DataType): Opcode { - return when (dt) { - in ByteDatatypes -> Opcode.PUSH_BYTE - in WordDatatypes -> Opcode.PUSH_WORD - in IterableDatatypes -> Opcode.PUSH_WORD - DataType.FLOAT -> Opcode.PUSH_FLOAT - else -> throw IllegalArgumentException("invalid datatype") - } - } - - private fun testBinaryOperator(left: RuntimeValue, operator: Opcode, right: RuntimeValue, result: RuntimeValue) { - val program=makeProg(mutableListOf( - Instruction(pushOpcode(left.type), left), - Instruction(pushOpcode(right.type), right), - Instruction(operator) - )) - vm.load(program, null) - vm.step(3) - assertEquals(1, vm.evalstack.size) - assertEquals(result, vm.evalstack.pop()) - } - - private fun testUnaryOperator(value: RuntimeValue, operator: Opcode, result: RuntimeValue) { - val program=makeProg(mutableListOf( - Instruction(pushOpcode(value.type), value), - Instruction(operator) - )) - vm.load(program, null) - vm.step(2) - assertEquals(1, vm.evalstack.size) - assertEquals(result, vm.evalstack.pop()) - } - -} diff --git a/DeprecatedStackVm/DeprecatedStackVm.iml b/OldCodeGen/OldCodeGen.iml similarity index 100% rename from DeprecatedStackVm/DeprecatedStackVm.iml rename to OldCodeGen/OldCodeGen.iml diff --git a/DeprecatedStackVm/src/compiler/target/c64/codegen/AsmGen.kt b/OldCodeGen/src/oldcodegen/AsmGen.kt similarity index 99% rename from DeprecatedStackVm/src/compiler/target/c64/codegen/AsmGen.kt rename to OldCodeGen/src/oldcodegen/AsmGen.kt index 8dd370824..95eaee845 100644 --- a/DeprecatedStackVm/src/compiler/target/c64/codegen/AsmGen.kt +++ b/OldCodeGen/src/oldcodegen/AsmGen.kt @@ -1,4 +1,6 @@ -package compiler.target.c64.codegen +package oldcodegen + +/** OLD STACK-VM CODE GEN -- NO LONGER USED **/ // note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles // possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles diff --git a/DeprecatedStackVm/src/compiler/target/c64/codegen/AsmPatterns.kt b/OldCodeGen/src/oldcodegen/AsmPatterns.kt similarity index 99% rename from DeprecatedStackVm/src/compiler/target/c64/codegen/AsmPatterns.kt rename to OldCodeGen/src/oldcodegen/AsmPatterns.kt index 565907e31..a35f66af1 100644 --- a/DeprecatedStackVm/src/compiler/target/c64/codegen/AsmPatterns.kt +++ b/OldCodeGen/src/oldcodegen/AsmPatterns.kt @@ -1,4 +1,7 @@ -package compiler.target.c64.codegen +package oldcodegen + +/** OLD STACK-VM CODE GEN -- NO LONGER USED **/ + import prog8.ast.base.printWarning import prog8.compiler.intermediate.Instruction diff --git a/DeprecatedStackVm/src/compiler/target/c64/codegen/SimpleAsm.kt b/OldCodeGen/src/oldcodegen/SimpleAsm.kt similarity index 99% rename from DeprecatedStackVm/src/compiler/target/c64/codegen/SimpleAsm.kt rename to OldCodeGen/src/oldcodegen/SimpleAsm.kt index 03481aed7..c653079cc 100644 --- a/DeprecatedStackVm/src/compiler/target/c64/codegen/SimpleAsm.kt +++ b/OldCodeGen/src/oldcodegen/SimpleAsm.kt @@ -1,4 +1,7 @@ -package compiler.target.c64.codegen +package oldcodegen + +/** OLD STACK-VM CODE GEN -- NO LONGER USED **/ + import prog8.compiler.CompilerException import prog8.compiler.intermediate.Instruction