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