diff --git a/README.md b/README.md index 12884df43..84f0aac61 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,6 @@ Rapid edit-compile-run-debug cycle: - option to automatically run the program in the Vice emulator - breakpoints, that let the Vice emulator drop into the monitor if execution hits them - source code labels automatically loaded in Vice emulator so it can show them in disassembly -- virtual machine that can execute compiled code directy on the host system, - without having to actually convert it to assembly to run on a real 6502 It is mainly targeted at the Commodore-64 machine at this time. Contributions to add support for other 8-bit (or other?!) machines are welcome. diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index f5806658d..961fb28d5 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -9,7 +9,6 @@ import prog8.compiler.target.c64.C64MachineDefinition import prog8.compiler.target.c64.Petscii import prog8.compiler.target.c64.codegen.AsmGen import prog8.parser.ParsingFailedError -import prog8.vm.astvm.AstVm import java.io.IOException import java.nio.file.FileSystems import java.nio.file.Path @@ -40,7 +39,6 @@ private fun compileMain(args: Array) { val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".") val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code") val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations") - val launchSimulator by cli.flagArgument("-sim", "launch the builtin execution simulator after compilation") val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes), greatly increases compilation speed") val compilationTarget by cli.flagValueArgument("-target", "compilertarget", "target output of the compiler, currently only 'c64' (C64 6502 assembly) available", "c64") val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1) @@ -119,18 +117,6 @@ private fun compileMain(args: Array) { exitProcess(1) } - if (launchSimulator) { -// val c64 = razorvine.c64emu.C64Machine("C64 emulator launched from Prog8 compiler") -// c64.cpu.addBreakpoint(0xea31) { cpu, address -> -// println("zz") -// Cpu6502.BreakpointResultAction() -// } -// c64.start() - println("\nLaunching AST-based simulator...") - val vm = AstVm(compilationResult.programAst, compilationTarget) - vm.run() - } - if (startEmulator) { if (compilationResult.programName.isEmpty()) println("\nCan't start emulator because no program was assembled.") diff --git a/compiler/src/prog8/vm/RuntimeValue.kt b/compiler/src/prog8/vm/RuntimeValue.kt deleted file mode 100644 index 3386578d6..000000000 --- a/compiler/src/prog8/vm/RuntimeValue.kt +++ /dev/null @@ -1,658 +0,0 @@ -package prog8.vm - -import prog8.ast.base.ByteDatatypes -import prog8.ast.base.DataType -import prog8.ast.base.WordDatatypes -import prog8.ast.expressions.ArrayLiteralValue -import prog8.ast.expressions.NumericLiteralValue -import prog8.ast.expressions.StringLiteralValue -import prog8.vm.astvm.VmExecutionException -import java.util.* -import kotlin.math.abs -import kotlin.math.pow - - -/** - * Rather than a literal value (NumericLiteralValue) that occurs in the parsed source code, - * this runtime value can be used to *execute* the parsed Ast (or another intermediary form) - * It contains a value of a variable during run time of the program and provides arithmetic operations on the value. - */ - -abstract class RuntimeValueBase(val type: DataType) { - abstract fun numericValue(): Number - abstract fun integerValue(): Int -} - - -class RuntimeValueNumeric(type: DataType, num: Number): RuntimeValueBase(type) { - - val byteval: Short? - val wordval: Int? - val floatval: Double? - val asBoolean: Boolean - - companion object { - fun fromLv(literalValue: NumericLiteralValue): RuntimeValueNumeric { - return RuntimeValueNumeric(literalValue.type, num = literalValue.number) - } - } - - init { - when (type) { - DataType.UBYTE -> { - val inum = num.toInt() - require(inum in 0..255) { "invalid value for ubyte: $inum" } - byteval = inum.toShort() - wordval = null - floatval = null - asBoolean = byteval != 0.toShort() - } - DataType.BYTE -> { - val inum = num.toInt() - require(inum in -128..127) { "invalid value for byte: $inum" } - byteval = inum.toShort() - wordval = null - floatval = null - asBoolean = byteval != 0.toShort() - } - DataType.UWORD -> { - val inum = num.toInt() - require(inum in 0..65535) { "invalid value for uword: $inum" } - wordval = inum - byteval = null - floatval = null - asBoolean = wordval != 0 - } - DataType.WORD -> { - val inum = num.toInt() - require(inum in -32768..32767) { "invalid value for word: $inum" } - wordval = inum - byteval = null - floatval = null - asBoolean = wordval != 0 - } - DataType.FLOAT -> { - floatval = num.toDouble() - byteval = null - wordval = null - asBoolean = floatval != 0.0 - } - else -> throw VmExecutionException("not a numeric value") - } - } - - override fun toString(): String { - return when (type) { - DataType.UBYTE -> "ub:%02x".format(byteval) - DataType.BYTE -> { - if (byteval!! < 0) - "b:-%02x".format(abs(byteval.toInt())) - else - "b:%02x".format(byteval) - } - DataType.UWORD -> "uw:%04x".format(wordval) - DataType.WORD -> { - if (wordval!! < 0) - "w:-%04x".format(abs(wordval)) - else - "w:%04x".format(wordval) - } - DataType.FLOAT -> "f:$floatval" - else -> "???" - } - } - - override fun numericValue(): Number { - return when (type) { - in ByteDatatypes -> byteval!! - in WordDatatypes -> wordval!! - DataType.FLOAT -> floatval!! - else -> throw ArithmeticException("invalid datatype for numeric value: $type") - } - } - - override fun integerValue(): Int { - return when (type) { - in ByteDatatypes -> byteval!!.toInt() - in WordDatatypes -> wordval!! - DataType.FLOAT -> throw ArithmeticException("float to integer loss of precision") - else -> throw ArithmeticException("invalid datatype for integer value: $type") - } - } - - override fun hashCode(): Int = Objects.hash(byteval, wordval, floatval, type) - - override fun equals(other: Any?): Boolean { - if (other == null || other !is RuntimeValueNumeric) - return false - return compareTo(other) == 0 // note: datatype doesn't matter - } - - operator fun compareTo(other: RuntimeValueNumeric): Int = numericValue().toDouble().compareTo(other.numericValue().toDouble()) - - private fun arithResult(leftDt: DataType, result: Number, rightDt: DataType, op: String): RuntimeValueNumeric { - if (leftDt != rightDt) - throw ArithmeticException("left and right datatypes are not the same") - if (result.toDouble() < 0) { - return when (leftDt) { - DataType.UBYTE, DataType.UWORD -> { - // storing a negative number in an unsigned one is done by storing the 2's complement instead - val number = abs(result.toDouble().toInt()) - if (leftDt == DataType.UBYTE) - RuntimeValueNumeric(DataType.UBYTE, (number xor 255) + 1) - else - RuntimeValueNumeric(DataType.UWORD, (number xor 65535) + 1) - } - DataType.BYTE -> { - val v = result.toInt() and 255 - if (v < 128) - RuntimeValueNumeric(DataType.BYTE, v) - else - RuntimeValueNumeric(DataType.BYTE, v - 256) - } - DataType.WORD -> { - val v = result.toInt() and 65535 - if (v < 32768) - RuntimeValueNumeric(DataType.WORD, v) - else - RuntimeValueNumeric(DataType.WORD, v - 65536) - } - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, result) - else -> throw ArithmeticException("$op on non-numeric type") - } - } - - return when (leftDt) { - DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, result.toInt() and 255) - DataType.BYTE -> { - val v = result.toInt() and 255 - if (v < 128) - RuntimeValueNumeric(DataType.BYTE, v) - else - RuntimeValueNumeric(DataType.BYTE, v - 256) - } - DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, result.toInt() and 65535) - DataType.WORD -> { - val v = result.toInt() and 65535 - if (v < 32768) - RuntimeValueNumeric(DataType.WORD, v) - else - RuntimeValueNumeric(DataType.WORD, v - 65536) - } - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, result) - else -> throw ArithmeticException("$op on non-numeric type") - } - } - - fun add(other: RuntimeValueNumeric): RuntimeValueNumeric { - if (other.type == DataType.FLOAT && (type != DataType.FLOAT)) - throw ArithmeticException("floating point loss of precision on type $type") - val v1 = numericValue() - val v2 = other.numericValue() - val result = v1.toDouble() + v2.toDouble() - return arithResult(type, result, other.type, "add") - } - - fun sub(other: RuntimeValueNumeric): RuntimeValueNumeric { - if (other.type == DataType.FLOAT && (type != DataType.FLOAT)) - throw ArithmeticException("floating point loss of precision on type $type") - val v1 = numericValue() - val v2 = other.numericValue() - val result = v1.toDouble() - v2.toDouble() - return arithResult(type, result, other.type, "sub") - } - - fun mul(other: RuntimeValueNumeric): RuntimeValueNumeric { - if (other.type == DataType.FLOAT && (type != DataType.FLOAT)) - throw ArithmeticException("floating point loss of precision on type $type") - val v1 = numericValue() - val v2 = other.numericValue() - val result = v1.toDouble() * v2.toDouble() - return arithResult(type, result, other.type, "mul") - } - - fun div(other: RuntimeValueNumeric): RuntimeValueNumeric { - if (other.type == DataType.FLOAT && (type != DataType.FLOAT)) - throw ArithmeticException("floating point loss of precision on type $type") - val v1 = numericValue() - val v2 = other.numericValue() - if (v2.toDouble() == 0.0) { - when (type) { - DataType.UBYTE -> return RuntimeValueNumeric(DataType.UBYTE, 255) - DataType.BYTE -> return RuntimeValueNumeric(DataType.BYTE, 127) - DataType.UWORD -> return RuntimeValueNumeric(DataType.UWORD, 65535) - DataType.WORD -> return RuntimeValueNumeric(DataType.WORD, 32767) - else -> { - } - } - } - val result = v1.toDouble() / v2.toDouble() - // NOTE: integer division returns integer result! - return when (type) { - DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, result) - DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, result) - DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, result) - DataType.WORD -> RuntimeValueNumeric(DataType.WORD, result) - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, result) - else -> throw ArithmeticException("div on non-numeric type") - } - } - - fun remainder(other: RuntimeValueNumeric): RuntimeValueNumeric { - val v1 = numericValue() - val v2 = other.numericValue() - val result = v1.toDouble() % v2.toDouble() - return arithResult(type, result, other.type, "remainder") - } - - fun pow(other: RuntimeValueNumeric): RuntimeValueNumeric { - val v1 = numericValue() - val v2 = other.numericValue() - val result = v1.toDouble().pow(v2.toDouble()) - return arithResult(type, result, other.type, "pow") - } - - fun shl(): RuntimeValueNumeric { - val v = integerValue() - return when (type) { - DataType.UBYTE -> RuntimeValueNumeric(type, (v shl 1) and 255) - DataType.UWORD -> RuntimeValueNumeric(type, (v shl 1) and 65535) - DataType.BYTE -> { - val value = v shl 1 - if (value < 128) - RuntimeValueNumeric(type, value) - else - RuntimeValueNumeric(type, value - 256) - } - DataType.WORD -> { - val value = v shl 1 - if (value < 32768) - RuntimeValueNumeric(type, value) - else - RuntimeValueNumeric(type, value - 65536) - } - else -> throw ArithmeticException("invalid type for shl: $type") - } - } - - fun shr(): RuntimeValueNumeric { - val v = integerValue() - return when (type) { - DataType.UBYTE -> RuntimeValueNumeric(type, v ushr 1) - DataType.BYTE -> RuntimeValueNumeric(type, v shr 1) - DataType.UWORD -> RuntimeValueNumeric(type, v ushr 1) - DataType.WORD -> RuntimeValueNumeric(type, v shr 1) - else -> throw ArithmeticException("invalid type for shr: $type") - } - } - - fun rol(carry: Boolean): Pair { - // 9 or 17 bit rotate left (with carry)) - return when (type) { - DataType.UBYTE, DataType.BYTE -> { - val v = byteval!!.toInt() - val newCarry = (v and 0x80) != 0 - val newval = (v and 0x7f shl 1) or (if (carry) 1 else 0) - Pair(RuntimeValueNumeric(DataType.UBYTE, newval), newCarry) - } - DataType.UWORD, DataType.WORD -> { - val v = wordval!! - val newCarry = (v and 0x8000) != 0 - val newval = (v and 0x7fff shl 1) or (if (carry) 1 else 0) - Pair(RuntimeValueNumeric(DataType.UWORD, newval), newCarry) - } - else -> throw ArithmeticException("rol can only work on byte/word") - } - } - - fun ror(carry: Boolean): Pair { - // 9 or 17 bit rotate right (with carry) - return when (type) { - DataType.UBYTE, DataType.BYTE -> { - val v = byteval!!.toInt() - val newCarry = v and 1 != 0 - val newval = (v ushr 1) or (if (carry) 0x80 else 0) - Pair(RuntimeValueNumeric(DataType.UBYTE, newval), newCarry) - } - DataType.UWORD, DataType.WORD -> { - val v = wordval!! - val newCarry = v and 1 != 0 - val newval = (v ushr 1) or (if (carry) 0x8000 else 0) - Pair(RuntimeValueNumeric(DataType.UWORD, newval), newCarry) - } - else -> throw ArithmeticException("ror2 can only work on byte/word") - } - } - - fun rol2(): RuntimeValueNumeric { - // 8 or 16 bit rotate left - return when (type) { - DataType.UBYTE, DataType.BYTE -> { - val v = byteval!!.toInt() - val carry = (v and 0x80) ushr 7 - val newval = (v and 0x7f shl 1) or carry - RuntimeValueNumeric(DataType.UBYTE, newval) - } - DataType.UWORD, DataType.WORD -> { - val v = wordval!! - val carry = (v and 0x8000) ushr 15 - val newval = (v and 0x7fff shl 1) or carry - RuntimeValueNumeric(DataType.UWORD, newval) - } - else -> throw ArithmeticException("rol2 can only work on byte/word") - } - } - - fun ror2(): RuntimeValueNumeric { - // 8 or 16 bit rotate right - return when (type) { - DataType.UBYTE, DataType.BYTE -> { - val v = byteval!!.toInt() - val carry = v and 1 shl 7 - val newval = (v ushr 1) or carry - RuntimeValueNumeric(DataType.UBYTE, newval) - } - DataType.UWORD, DataType.WORD -> { - val v = wordval!! - val carry = v and 1 shl 15 - val newval = (v ushr 1) or carry - RuntimeValueNumeric(DataType.UWORD, newval) - } - else -> throw ArithmeticException("ror2 can only work on byte/word") - } - } - - fun neg(): RuntimeValueNumeric { - return when (type) { - DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, -(byteval!!)) - DataType.WORD -> RuntimeValueNumeric(DataType.WORD, -(wordval!!)) - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, -(floatval)!!) - else -> throw ArithmeticException("neg can only work on byte/word/float") - } - } - - fun abs(): RuntimeValueNumeric { - return when (type) { - DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, abs(byteval!!.toInt())) - DataType.WORD -> RuntimeValueNumeric(DataType.WORD, abs(wordval!!)) - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, abs(floatval!!)) - else -> throw ArithmeticException("abs can only work on byte/word/float") - } - } - - fun bitand(other: RuntimeValueNumeric): RuntimeValueNumeric { - val v1 = integerValue() - val v2 = other.integerValue() - val result = v1 and v2 - return RuntimeValueNumeric(type, result) - } - - fun bitor(other: RuntimeValueNumeric): RuntimeValueNumeric { - val v1 = integerValue() - val v2 = other.integerValue() - val result = v1 or v2 - return RuntimeValueNumeric(type, result) - } - - fun bitxor(other: RuntimeValueNumeric): RuntimeValueNumeric { - val v1 = integerValue() - val v2 = other.integerValue() - val result = v1 xor v2 - return RuntimeValueNumeric(type, result) - } - - fun and(other: RuntimeValueNumeric) = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean && other.asBoolean) 1 else 0) - fun or(other: RuntimeValueNumeric) = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean || other.asBoolean) 1 else 0) - fun xor(other: RuntimeValueNumeric) = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean xor other.asBoolean) 1 else 0) - fun not() = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean) 0 else 1) - - fun inv(): RuntimeValueNumeric { - return when (type) { - DataType.UBYTE -> RuntimeValueNumeric(type, byteval!!.toInt().inv() and 255) - DataType.UWORD -> RuntimeValueNumeric(type, wordval!!.inv() and 65535) - DataType.BYTE -> RuntimeValueNumeric(type, byteval!!.toInt().inv()) - DataType.WORD -> RuntimeValueNumeric(type, wordval!!.inv()) - else -> throw ArithmeticException("inv can only work on byte/word") - } - } - - fun inc(): RuntimeValueNumeric { - return when (type) { - DataType.UBYTE -> RuntimeValueNumeric(type, (byteval!! + 1) and 255) - DataType.UWORD -> RuntimeValueNumeric(type, (wordval!! + 1) and 65535) - DataType.BYTE -> { - val newval = byteval!! + 1 - if (newval == 128) - RuntimeValueNumeric(type, -128) - else - RuntimeValueNumeric(type, newval) - } - DataType.WORD -> { - val newval = wordval!! + 1 - if (newval == 32768) - RuntimeValueNumeric(type, -32768) - else - RuntimeValueNumeric(type, newval) - } - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, floatval!! + 1) - else -> throw ArithmeticException("inc can only work on numeric types") - } - } - - fun dec(): RuntimeValueNumeric { - return when (type) { - DataType.UBYTE -> RuntimeValueNumeric(type, (byteval!! - 1) and 255) - DataType.UWORD -> RuntimeValueNumeric(type, (wordval!! - 1) and 65535) - DataType.BYTE -> { - val newval = byteval!! - 1 - if (newval == -129) - RuntimeValueNumeric(type, 127) - else - RuntimeValueNumeric(type, newval) - } - DataType.WORD -> { - val newval = wordval!! - 1 - if (newval == -32769) - RuntimeValueNumeric(type, 32767) - else - RuntimeValueNumeric(type, newval) - } - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, floatval!! - 1) - else -> throw ArithmeticException("dec can only work on numeric types") - } - } - - fun msb(): RuntimeValueNumeric { - return when (type) { - in ByteDatatypes -> RuntimeValueNumeric(DataType.UBYTE, 0) - in WordDatatypes -> RuntimeValueNumeric(DataType.UBYTE, wordval!! ushr 8 and 255) - else -> throw ArithmeticException("msb can only work on (u)byte/(u)word") - } - } - - fun cast(targetType: DataType): RuntimeValueNumeric { - return when (type) { - DataType.UBYTE -> { - when (targetType) { - DataType.UBYTE -> this - DataType.BYTE -> { - val nval = byteval!!.toInt() - if (nval < 128) - RuntimeValueNumeric(DataType.BYTE, nval) - else - RuntimeValueNumeric(DataType.BYTE, nval - 256) - } - DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, numericValue()) - DataType.WORD -> { - val nval = numericValue().toInt() - if (nval < 32768) - RuntimeValueNumeric(DataType.WORD, nval) - else - RuntimeValueNumeric(DataType.WORD, nval - 65536) - } - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue()) - else -> throw ArithmeticException("invalid type cast from $type to $targetType") - } - } - DataType.BYTE -> { - when (targetType) { - DataType.BYTE -> this - DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, integerValue() and 255) - DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, integerValue() and 65535) - DataType.WORD -> RuntimeValueNumeric(DataType.WORD, integerValue()) - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue()) - else -> throw ArithmeticException("invalid type cast from $type to $targetType") - } - } - DataType.UWORD -> { - when (targetType) { - DataType.BYTE -> { - val v = integerValue() - if (v < 128) - RuntimeValueNumeric(DataType.BYTE, v) - else - RuntimeValueNumeric(DataType.BYTE, v - 256) - } - DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, integerValue() and 255) - DataType.UWORD -> this - DataType.WORD -> { - val v = integerValue() - if (v < 32768) - RuntimeValueNumeric(DataType.WORD, v) - else - RuntimeValueNumeric(DataType.WORD, v - 65536) - } - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue()) - else -> throw ArithmeticException("invalid type cast from $type to $targetType") - } - } - DataType.WORD -> { - when (targetType) { - DataType.BYTE -> { - val v = integerValue() and 255 - if (v < 128) - RuntimeValueNumeric(DataType.BYTE, v) - else - RuntimeValueNumeric(DataType.BYTE, v - 256) - } - DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, integerValue() and 65535) - DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, integerValue()) - DataType.WORD -> this - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue()) - else -> throw ArithmeticException("invalid type cast from $type to $targetType") - } - } - DataType.FLOAT -> { - when (targetType) { - DataType.BYTE -> { - val integer = numericValue().toInt() - if (integer in -128..127) - RuntimeValueNumeric(DataType.BYTE, integer) - else - throw ArithmeticException("overflow when casting float to byte: $this") - } - DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, numericValue().toInt()) - DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, numericValue().toInt()) - DataType.WORD -> { - val integer = numericValue().toInt() - if (integer in -32768..32767) - RuntimeValueNumeric(DataType.WORD, integer) - else - throw ArithmeticException("overflow when casting float to word: $this") - } - DataType.FLOAT -> this - else -> throw ArithmeticException("invalid type cast from $type to $targetType") - } - } - else -> throw ArithmeticException("invalid type cast from $type to $targetType") - } - } -} - - -class RuntimeValueString(val str: String, val altEncoding: Boolean, val heapId: Int?): RuntimeValueBase(DataType.STR) { - companion object { - fun fromLv(string: StringLiteralValue): RuntimeValueString { - return RuntimeValueString(string.value, string.altEncoding, string.heapId) - } - } - - override fun toString(): String = if(type==DataType.STR) "str:$str" else "???" - - override fun hashCode(): Int = Objects.hash(type, str) - - override fun equals(other: Any?): Boolean { - if (other == null || other !is RuntimeValueString) - return false - return type == other.type && str == other.str - } - - fun iterator(): Iterator = str.map { it.toShort() }.iterator() - - override fun numericValue(): Number { - throw VmExecutionException("string is not a number") - } - - override fun integerValue(): Int { - throw VmExecutionException("string is not a number") - } -} - - -open class RuntimeValueArray(type: DataType, val array: Array, val heapId: Int?): RuntimeValueBase(type) { - - companion object { - fun fromLv(array: ArrayLiteralValue): RuntimeValueArray { - return if (array.type.istype(DataType.ARRAY_F)) { - val doubleArray = array.value.map { (it as NumericLiteralValue).number }.toTypedArray() - RuntimeValueArray(DataType.ARRAY_F, doubleArray, array.heapId) - } else { - val resultArray = mutableListOf() - for (elt in array.value.withIndex()) { - if (elt.value is NumericLiteralValue) - resultArray.add((elt.value as NumericLiteralValue).number.toInt()) - else { - resultArray.add((elt.hashCode())) // ...poor man's implementation of ADDRESSOF(array), it probably won't work very well - } - } - RuntimeValueArray(array.type.typeOrElse(DataType.STRUCT), resultArray.toTypedArray(), array.heapId) - } - } - } - - override fun toString(): String { - return when (type) { - DataType.ARRAY_UB -> "array_ub:..." - DataType.ARRAY_B -> "array_b:..." - DataType.ARRAY_UW -> "array_uw:..." - DataType.ARRAY_W -> "array_w:..." - DataType.ARRAY_F -> "array_f:..." - else -> "???" - } - } - - override fun hashCode(): Int = Objects.hash(type, array) - - override fun equals(other: Any?): Boolean { - if (other == null || other !is RuntimeValueArray) - return false - return type == other.type && array.contentEquals(other.array) - } - - open fun iterator(): Iterator = array.iterator() - - override fun numericValue(): Number { - throw VmExecutionException("array is not a number") - } - - override fun integerValue(): Int { - throw VmExecutionException("array is not a number") - } -} - - -class RuntimeValueRange(type: DataType, val range: IntProgression): RuntimeValueArray(type, range.toList().toTypedArray(), null) { - override fun iterator(): Iterator { - return range.iterator() - } -} diff --git a/compiler/src/prog8/vm/astvm/AstVm.kt b/compiler/src/prog8/vm/astvm/AstVm.kt deleted file mode 100644 index f82a51533..000000000 --- a/compiler/src/prog8/vm/astvm/AstVm.kt +++ /dev/null @@ -1,1019 +0,0 @@ -package prog8.vm.astvm - -import prog8.ast.INameScope -import prog8.ast.Program -import prog8.ast.base.* -import prog8.ast.expressions.Expression -import prog8.ast.expressions.IdentifierReference -import prog8.ast.expressions.NumericLiteralValue -import prog8.ast.statements.* -import prog8.compiler.target.c64.C64MachineDefinition -import prog8.vm.* -import java.awt.EventQueue -import java.util.* -import kotlin.NoSuchElementException -import kotlin.concurrent.fixedRateTimer -import kotlin.math.* -import kotlin.random.Random - - -class VmExecutionException(msg: String?) : Exception(msg) - -class VmTerminationException(msg: String?) : Exception(msg) - -class VmBreakpointException : Exception("breakpoint") - - -class StatusFlags { - var carry: Boolean = false - var zero: Boolean = true - var negative: Boolean = false - var irqd: Boolean = false - - private fun setFlags(value: NumericLiteralValue?) { - if (value != null) { - when (value.type) { - DataType.UBYTE -> { - val v = value.number.toInt() - negative = v > 127 - zero = v == 0 - } - DataType.BYTE -> { - val v = value.number.toInt() - negative = v < 0 - zero = v == 0 - } - DataType.UWORD -> { - val v = value.number.toInt() - negative = v > 32767 - zero = v == 0 - } - DataType.WORD -> { - val v = value.number.toInt() - negative = v < 0 - zero = v == 0 - } - DataType.FLOAT -> { - val flt = value.number.toDouble() - negative = flt < 0.0 - zero = flt == 0.0 - } - else -> { - // no flags for non-numeric type - } - } - } - } -} - - -class RuntimeVariables { - fun define(scope: INameScope, name: String, initialValue: RuntimeValueBase) { - val where = vars.getValue(scope) - where[name] = initialValue - vars[scope] = where - if(initialValue is RuntimeValueString) - byHeapId[initialValue.heapId!!] = initialValue - else if(initialValue is RuntimeValueArray) - byHeapId[initialValue.heapId!!] = initialValue - } - - fun defineMemory(scope: INameScope, name: String, address: Int) { - val where = memvars.getValue(scope) - where[name] = address - memvars[scope] = where - } - - fun set(scope: INameScope, name: String, value: RuntimeValueBase) { - val where = vars.getValue(scope) - val existing = where[name] - if(existing==null) { - if(memvars.getValue(scope)[name]!=null) - throw NoSuchElementException("this is a memory mapped var, not a normal var: ${scope.name}.$name") - throw NoSuchElementException("no such runtime variable: ${scope.name}.$name") - } - if(existing.type!=value.type) - throw VmExecutionException("new value is of different datatype ${value.type} expected ${existing.type} for $name") - where[name] = value - vars[scope] = where - if(value is RuntimeValueString) - byHeapId[value.heapId!!] = value - else if(value is RuntimeValueArray) - byHeapId[value.heapId!!] = value - } - - fun getByHeapId(heapId: Int): RuntimeValueBase = byHeapId.getValue(heapId) - - fun get(scope: INameScope, name: String): RuntimeValueBase { - val where = vars.getValue(scope) - return where[name] ?: throw NoSuchElementException("no such runtime variable: ${scope.name}.$name") - } - - fun getMemoryAddress(scope: INameScope, name: String): Int { - val where = memvars.getValue(scope) - return where[name] ?: throw NoSuchElementException("no such runtime memory-variable: ${scope.name}.$name") - } - - fun swap(scope1: INameScope, name1: String, scope2: INameScope, name2: String) { - val v1 = get(scope1, name1) - val v2 = get(scope2, name2) - set(scope1, name1, v2) - set(scope2, name2, v1) - } - - private val vars = mutableMapOf>().withDefault { mutableMapOf() } - private val memvars = mutableMapOf>().withDefault { mutableMapOf() } - private val byHeapId = mutableMapOf() -} - - -class AstVm(val program: Program, compilationTarget: String) { - - val mem = Memory(::memread, ::memwrite) - val statusflags = StatusFlags() - - private var dialog = ScreenDialog("AstVM") - var instructionCounter = 0 - val bootTime = System.currentTimeMillis() - var rtcOffset = bootTime - - private val rnd = Random(0) - private val statusFlagsSave = ArrayDeque() - private val registerXsave = ArrayDeque() - private val registerYsave = ArrayDeque() - private val registerAsave = ArrayDeque() - - - init { - require(compilationTarget == "c64") {"using the AstVm only works for the C64 compiler target"} - - // observe the jiffyclock and screen matrix - mem.observe(0xa0, 0xa1, 0xa2) - for(i in 1024..2023) - mem.observe(i) - - dialog.requestFocusInWindow() - - EventQueue.invokeLater { - dialog.pack() - dialog.isVisible = true - dialog.start() - } - - fixedRateTimer("60hz-irq", true, period=1000/60) { - irq(this.scheduledExecutionTime()) - } - } - - 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 timeHi = if(address==0xa0) value else mem.getUByteDirectly(0xa0) - val timeMid = if(address==0xa1) value else mem.getUByteDirectly(0xa1) - val timeLo = if(address==0xa2) value else mem.getUByteDirectly(0xa2) - val jiffies = (timeHi.toInt() shl 16) + (timeMid.toInt() shl 8) + timeLo - rtcOffset = bootTime - (jiffies*1000/60) - } - if(address in 1024..2023) { - // write to the screen matrix - val scraddr = address-1024 - dialog.canvas.setScreenChar(scraddr % 40, scraddr / 40, value, 1) - } - return value - } - - fun run() { - try { - val init = AstVmVariablesCreator(runtimeVariables) - init.visit(program) - - // initialize all global variables - for (m in program.modules) { - for (b in m.statements.filterIsInstance()) { - for (s in b.statements.filterIsInstance()) { - TODO("initialize variable values") -// if (s.name == initvarsSubName) { -// try { -// executeSubroutine(s, emptyList(), null) -// } catch (x: LoopControlReturn) { -// // regular return -// } -// } - } - } - } - - var entrypoint: Subroutine? = program.entrypoint() ?: throw VmTerminationException("no valid entrypoint found") - var startlabel: Label? = null - - while(entrypoint!=null) { - try { - executeSubroutine(entrypoint, emptyList(), startlabel) - entrypoint = null - } catch (rx: LoopControlReturn) { - // regular return - } catch (jx: LoopControlJump) { - if (jx.address != null) - throw VmTerminationException("doesn't support jumping to machine address ${jx.address}") - when { - jx.generatedLabel != null -> { - val label = entrypoint.getLabelOrVariable(jx.generatedLabel) as Label - TODO("astvm handle generatedlabel $label") - } - jx.identifier != null -> { - when (val jumptarget = entrypoint.lookup(jx.identifier.nameInSource, jx.identifier.parent)) { - is Label -> { - startlabel = jumptarget - entrypoint = jumptarget.definingSubroutine() - } - is Subroutine -> entrypoint = jumptarget - else -> throw VmTerminationException("weird jump target $jumptarget") - } - } - else -> throw VmTerminationException("unspecified jump target") - } - } - } - dialog.canvas.printAsciiText("\n") - println("PROGRAM EXITED!") - dialog.title = "PROGRAM EXITED" - } catch (tx: VmTerminationException) { - println("Execution halted: ${tx.message}") - } catch (xx: VmExecutionException) { - println("Execution error: ${xx.message}") - throw xx - } - } - - private fun irq(timeStamp: Long) { - // 60hz IRQ handling - if(statusflags.irqd) - return // interrupt is disabled - - var jiffies = (timeStamp-rtcOffset)*60/1000 - if(jiffies>24*3600*60-1) { - jiffies = 0 - rtcOffset = timeStamp - } - // update the C-64 60hz jiffy clock in the ZP addresses: - mem.setUByteDirectly(0x00a0, (jiffies ushr 16).toShort()) - mem.setUByteDirectly(0x00a1, (jiffies ushr 8 and 255).toShort()) - mem.setUByteDirectly(0x00a2, (jiffies and 255).toShort()) - } - - private val runtimeVariables = RuntimeVariables() - private val evalCtx = EvalContext(program, mem, statusflags, runtimeVariables, ::performBuiltinFunction, ::executeSubroutine) - - class LoopControlBreak : Exception() - class LoopControlContinue : Exception() - class LoopControlReturn(val returnvalue: RuntimeValueNumeric?) : Exception() - class LoopControlJump(val identifier: IdentifierReference?, val address: Int?, val generatedLabel: String?) : Exception() - - - internal fun executeSubroutine(sub: Subroutine, arguments: List, startAtLabel: Label?=null): RuntimeValueNumeric? { - if(sub.isAsmSubroutine) { - return performSyscall(sub, arguments) - } - - if (sub.statements.isEmpty()) - throw VmTerminationException("scope contains no statements: $sub") - if (arguments.size != sub.parameters.size) - throw VmTerminationException("number of args doesn't match number of required parameters") - - for (arg in sub.parameters.zip(arguments)) { - val idref = IdentifierReference(listOf(arg.first.name), sub.position) - performAssignment(AssignTarget(null, idref, null, null, idref.position), - arg.second, sub.statements.first(), evalCtx) - } - - val statements = sub.statements.iterator() - if(startAtLabel!=null) { - do { - val stmt = statements.next() - } while(stmt!==startAtLabel) - } - - try { - while(statements.hasNext()) { - val s = statements.next() - try { - executeStatement(sub, s) - } - catch (b: VmBreakpointException) { - print("BREAKPOINT HIT at ${s.position} - Press enter to continue:") - readLine() - } - } - } catch (r: LoopControlReturn) { - return r.returnvalue - } - throw VmTerminationException("instruction pointer overflow, is a return missing? $sub") - } - - internal fun executeAnonymousScope(scope: INameScope) { - for (s in scope.statements) { - executeStatement(scope, s) - } - } - - - private fun executeStatement(sub: INameScope, stmt: Statement) { - instructionCounter++ - if (instructionCounter % 200 == 0) - Thread.sleep(1) - when (stmt) { - is NopStatement, is Label, is Subroutine -> { - // do nothing, skip this instruction - } - is Directive -> { - if (stmt.directive == "%breakpoint") - throw VmBreakpointException() - else if (stmt.directive == "%asm") - throw VmExecutionException("can't execute assembly code") - } - is VarDecl -> { - // should have been defined already when the program started - } - is FunctionCallStatement -> { - when (val target = stmt.target.targetStatement(program.namespace)) { - is Subroutine -> { - val args = evaluate(stmt.args).map { it as RuntimeValueNumeric } - if (target.isAsmSubroutine) { - performSyscall(target, args) - } else { - executeSubroutine(target, args, null) - // any return value(s) are discarded - } - } - is BuiltinFunctionStatementPlaceholder -> { - if(target.name=="swap") { - // swap cannot be implemented as a function, so inline it here - executeSwap(stmt) - } else { - val args = evaluate(stmt.args) - performBuiltinFunction(target.name, args, statusflags) - } - } - else -> { - throw VmExecutionException("weird call target $target") - } - } - } - is Return -> { - val value = - if(stmt.value==null) - null - else - evaluate(stmt.value!!, evalCtx) as RuntimeValueNumeric - throw LoopControlReturn(value) - } - is Continue -> throw LoopControlContinue() - is Break -> throw LoopControlBreak() - is Assignment -> { - if (stmt.aug_op != null) - throw VmExecutionException("augmented assignment should have been converted into regular one $stmt") - val value = evaluate(stmt.value, evalCtx) - performAssignment(stmt.target, value, stmt, evalCtx) - } - is PostIncrDecr -> { - when { - stmt.target.identifier != null -> { - val ident = stmt.definingScope().lookup(stmt.target.identifier!!.nameInSource, stmt) as VarDecl - val identScope = ident.definingScope() - when(ident.type){ - VarDeclType.VAR -> { - var value = runtimeVariables.get(identScope, ident.name) as RuntimeValueNumeric - value = when (stmt.operator) { - "++" -> value.add(RuntimeValueNumeric(value.type, 1)) - "--" -> value.sub(RuntimeValueNumeric(value.type, 1)) - else -> throw VmExecutionException("strange postincdec operator $stmt") - } - runtimeVariables.set(identScope, ident.name, value) - } - VarDeclType.MEMORY -> { - val addr=ident.value!!.constValue(program)!!.number.toInt() - val newval = when (stmt.operator) { - "++" -> mem.getUByte(addr)+1 and 255 - "--" -> mem.getUByte(addr)-1 and 255 - else -> throw VmExecutionException("strange postincdec operator $stmt") - } - mem.setUByte(addr,newval.toShort()) - } - VarDeclType.CONST -> throw VmExecutionException("can't be const") - } - } - stmt.target.memoryAddress != null -> { - val addr = (evaluate(stmt.target.memoryAddress!!.addressExpression, evalCtx) as RuntimeValueNumeric).integerValue() - val newval = when (stmt.operator) { - "++" -> mem.getUByte(addr)+1 and 255 - "--" -> mem.getUByte(addr)-1 and 255 - else -> throw VmExecutionException("strange postincdec operator $stmt") - } - mem.setUByte(addr,newval.toShort()) - } - stmt.target.arrayindexed != null -> { - val arrayvar = stmt.target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!! - val arrayvalue = runtimeVariables.get(arrayvar.definingScope(), arrayvar.name) as RuntimeValueArray - val index = (evaluate(stmt.target.arrayindexed!!.arrayspec.index, evalCtx) as RuntimeValueNumeric).integerValue() - val elementType = stmt.target.arrayindexed!!.inferType(program) - if(!elementType.isKnown) - throw VmExecutionException("unknown/void elt type") - var value = RuntimeValueNumeric(elementType.typeOrElse(DataType.BYTE), arrayvalue.array[index].toInt()) - value = when (stmt.operator) { - "++" -> value.inc() - "--" -> value.dec() - else -> throw VmExecutionException("strange postincdec operator $stmt") - } - arrayvalue.array[index] = value.numericValue() - } - stmt.target.register != null -> { - var value = runtimeVariables.get(program.namespace, stmt.target.register!!.name) as RuntimeValueNumeric - value = when (stmt.operator) { - "++" -> value.add(RuntimeValueNumeric(value.type, 1)) - "--" -> value.sub(RuntimeValueNumeric(value.type, 1)) - else -> throw VmExecutionException("strange postincdec operator $stmt") - } - runtimeVariables.set(program.namespace, stmt.target.register!!.name, value) - } - else -> throw VmExecutionException("empty postincrdecr? $stmt") - } - } - is Jump -> throw LoopControlJump(stmt.identifier, stmt.address, stmt.generatedLabel) - is InlineAssembly -> { - if (sub is Subroutine) { - val args = sub.parameters.map { runtimeVariables.get(sub, it.name) as RuntimeValueNumeric } - performSyscall(sub, args) - throw LoopControlReturn(null) - } - throw VmExecutionException("can't execute inline assembly in $sub") - } - is AnonymousScope -> executeAnonymousScope(stmt) - is IfStatement -> { - val condition = evaluate(stmt.condition, evalCtx) as RuntimeValueNumeric - if (condition.asBoolean) - executeAnonymousScope(stmt.truepart) - else - executeAnonymousScope(stmt.elsepart) - } - is BranchStatement -> { - when(stmt.condition) { - BranchCondition.CS -> if(statusflags.carry) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart) - BranchCondition.CC -> if(!statusflags.carry) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart) - BranchCondition.EQ, BranchCondition.Z -> if(statusflags.zero) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart) - BranchCondition.NE, BranchCondition.NZ -> if(statusflags.zero) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart) - BranchCondition.MI, BranchCondition.NEG -> if(statusflags.negative) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart) - BranchCondition.PL, BranchCondition.POS -> if(statusflags.negative) executeAnonymousScope(stmt.truepart) else executeAnonymousScope(stmt.elsepart) - BranchCondition.VS, BranchCondition.VC -> TODO("astvm branch on overflow status") - } - } - is ForLoop -> { - val iterable = evaluate(stmt.iterable, evalCtx) - if (iterable.type !in IterableDatatypes && iterable !is RuntimeValueRange) - throw VmExecutionException("can only iterate over an iterable value: $stmt") - val loopvarDt: DataType - val loopvar: IdentifierReference - if (stmt.loopRegister != null) { - loopvarDt = DataType.UBYTE - loopvar = IdentifierReference(listOf(stmt.loopRegister.name), stmt.position) - } else { - val dt = stmt.loopVar!!.inferType(program) - loopvarDt = dt.typeOrElse(DataType.UBYTE) - loopvar = stmt.loopVar!! - } - val iterator = - when (iterable) { - is RuntimeValueRange -> iterable.iterator() - is RuntimeValueArray -> iterable.iterator() - is RuntimeValueString -> iterable.iterator() - else -> throw VmExecutionException("not iterable") - } - for (loopvalue in iterator) { - try { - oneForCycle(stmt, loopvarDt, loopvalue, loopvar) - } catch (b: LoopControlBreak) { - break - } catch (c: LoopControlContinue) { - continue - } - } - } - is WhileLoop -> { - var condition = evaluate(stmt.condition, evalCtx) as RuntimeValueNumeric - while (condition.asBoolean) { - try { - executeAnonymousScope(stmt.body) - condition = evaluate(stmt.condition, evalCtx) as RuntimeValueNumeric - } catch (b: LoopControlBreak) { - break - } catch (c: LoopControlContinue) { - continue - } - } - } - is RepeatLoop -> { - do { - val condition = evaluate(stmt.untilCondition, evalCtx) as RuntimeValueNumeric - try { - executeAnonymousScope(stmt.body) - } catch (b: LoopControlBreak) { - break - } catch (c: LoopControlContinue) { - continue - } - } while (!condition.asBoolean) - } - is WhenStatement -> { - val condition=evaluate(stmt.condition, evalCtx) as RuntimeValueNumeric - for(choice in stmt.choices) { - if(choice.values==null) { - // the 'else' choice - executeAnonymousScope(choice.statements) - break - } else { - val value = choice.values!!.single().constValue(evalCtx.program) ?: throw VmExecutionException("can only use const values in when choices ${choice.position}") - val rtval = RuntimeValueNumeric.fromLv(value) - if(condition==rtval) { - executeAnonymousScope(choice.statements) - break - } - } - } - } - is ForeverLoop -> { - while(true) { - try { - executeAnonymousScope(stmt.body) - } catch (b: LoopControlBreak) { - break - } catch (c: LoopControlContinue) { - continue - } - } - } - else -> { - TODO("astvm implement statement $stmt") - } - } - } - - private fun executeSwap(swap: FunctionCallStatement) { - val v1 = swap.args[0] - val v2 = swap.args[1] - val value1 = evaluate(v1, evalCtx) - val value2 = evaluate(v2, evalCtx) - val target1 = AssignTarget.fromExpr(v1) - val target2 = AssignTarget.fromExpr(v2) - performAssignment(target1, value2, swap, evalCtx) - performAssignment(target2, value1, swap, evalCtx) - } - - fun performAssignment(target: AssignTarget, value: RuntimeValueBase, contextStmt: Statement, evalCtx: EvalContext) { - val targetIdent = target.identifier - val targetArrayIndexed = target.arrayindexed - when { - targetIdent != null -> { - val decl = contextStmt.definingScope().lookup(targetIdent.nameInSource, contextStmt) as? VarDecl - ?: throw VmExecutionException("can't find assignment target $targetIdent") - if (decl.type == VarDeclType.MEMORY) { - val address = runtimeVariables.getMemoryAddress(decl.definingScope(), decl.name) - when (decl.datatype) { - DataType.UBYTE -> mem.setUByte(address, (value as RuntimeValueNumeric).byteval!!) - DataType.BYTE -> mem.setSByte(address, (value as RuntimeValueNumeric).byteval!!) - DataType.UWORD -> mem.setUWord(address, (value as RuntimeValueNumeric).wordval!!) - DataType.WORD -> mem.setSWord(address, (value as RuntimeValueNumeric).wordval!!) - DataType.FLOAT -> mem.setFloat(address, (value as RuntimeValueNumeric).floatval!!) - DataType.STR -> mem.setString(address, (value as RuntimeValueString).str, value.altEncoding) - else -> throw VmExecutionException("weird memaddress type $decl") - } - } else - runtimeVariables.set(decl.definingScope(), decl.name, value) - } - target.memoryAddress != null -> { - val address = (evaluate(target.memoryAddress.addressExpression, evalCtx) as RuntimeValueNumeric).wordval!! - evalCtx.mem.setUByte(address, (value as RuntimeValueNumeric).byteval!!) - } - targetArrayIndexed != null -> { - val vardecl = targetArrayIndexed.identifier.targetVarDecl(program.namespace)!! - if(vardecl.type==VarDeclType.VAR) { - val array = evaluate(targetArrayIndexed.identifier, evalCtx) - val index = evaluate(targetArrayIndexed.arrayspec.index, evalCtx) as RuntimeValueNumeric - when (array.type) { - DataType.ARRAY_UB -> { - if (value.type != DataType.UBYTE) - throw VmExecutionException("new value is of different datatype ${value.type} for $array") - } - DataType.ARRAY_B -> { - if (value.type != DataType.BYTE) - throw VmExecutionException("new value is of different datatype ${value.type} for $array") - } - DataType.ARRAY_UW -> { - if (value.type != DataType.UWORD) - throw VmExecutionException("new value is of different datatype ${value.type} for $array") - } - DataType.ARRAY_W -> { - if (value.type != DataType.WORD) - throw VmExecutionException("new value is of different datatype ${value.type} for $array") - } - DataType.ARRAY_F -> { - if (value.type != DataType.FLOAT) - throw VmExecutionException("new value is of different datatype ${value.type} for $array") - } - DataType.STR -> { - if (value.type !in ByteDatatypes) - throw VmExecutionException("new value is of different datatype ${value.type} for $array") - } - else -> throw VmExecutionException("strange array type ${array.type}") - } - if (array.type in ArrayDatatypes) - (array as RuntimeValueArray).array[index.integerValue()] = value.numericValue() - else if (array.type == DataType.STR) { - val indexInt = index.integerValue() - val newchr = value.numericValue().toChar().toString() - val newstr = (array as RuntimeValueString).str.replaceRange(indexInt, indexInt + 1, newchr) - val ident = contextStmt.definingScope().lookup(targetArrayIndexed.identifier.nameInSource, contextStmt) as? VarDecl - ?: throw VmExecutionException("can't find assignment target ${target.identifier}") - val identScope = ident.definingScope() - runtimeVariables.set(identScope, ident.name, RuntimeValueString(newstr, false, array.heapId)) - } - } - else { - value as RuntimeValueNumeric - val address = (vardecl.value as NumericLiteralValue).number.toInt() - val index = (evaluate(targetArrayIndexed.arrayspec.index, evalCtx) as RuntimeValueNumeric).integerValue() - val elementType = targetArrayIndexed.inferType(program) - if(!elementType.isKnown) - throw VmExecutionException("unknown/void array elt type $targetArrayIndexed") - when(elementType.typeOrElse(DataType.UBYTE)) { - DataType.UBYTE -> mem.setUByte(address+index, value.byteval!!) - DataType.BYTE -> mem.setSByte(address+index, value.byteval!!) - DataType.UWORD -> mem.setUWord(address+index*2, value.wordval!!) - DataType.WORD -> mem.setSWord(address+index*2, value.wordval!!) - DataType.FLOAT -> mem.setFloat(address+index* C64MachineDefinition.FLOAT_MEM_SIZE, value.floatval!!) - else -> throw VmExecutionException("strange array elt type $elementType") - } - } - } - target.register != null -> { - runtimeVariables.set(program.namespace, target.register.name, value) - } - else -> TODO("assign weird target $target") - } - } - - private fun oneForCycle(stmt: ForLoop, loopvarDt: DataType, loopValue: Number, loopVar: IdentifierReference) { - // assign the new loop value to the loopvar, and run the code - performAssignment(AssignTarget(null, loopVar, null, null, loopVar.position), - RuntimeValueNumeric(loopvarDt, loopValue), stmt.body.statements.first(), evalCtx) - executeAnonymousScope(stmt.body) - } - - private fun evaluate(args: List) = args.map { evaluate(it, evalCtx) } - - private fun performSyscall(sub: Subroutine, args: List): RuntimeValueNumeric? { - var result: RuntimeValueNumeric? = null - when (sub.scopedname) { - "c64scr.print" -> { - // if the argument is an UWORD, consider it to be the "address" of the string (=heapId) - if (args[0].wordval != null) { - val string = getAsciiStringFromRuntimeVars(args[0].wordval!!) - dialog.canvas.printAsciiText(string) - } else - throw VmExecutionException("print non-heap string") - } - "c64scr.print_ub" -> { - dialog.canvas.printAsciiText(args[0].byteval!!.toString()) - } - "c64scr.print_ub0" -> { - dialog.canvas.printAsciiText("%03d".format(args[0].byteval!!)) - } - "c64scr.print_b" -> { - dialog.canvas.printAsciiText(args[0].byteval!!.toString()) - } - "c64scr.print_uw" -> { - dialog.canvas.printAsciiText(args[0].wordval!!.toString()) - } - "c64scr.print_uw0" -> { - dialog.canvas.printAsciiText("%05d".format(args[0].wordval!!)) - } - "c64scr.print_w" -> { - dialog.canvas.printAsciiText(args[0].wordval!!.toString()) - } - "c64scr.print_ubhex" -> { - val number = args[0].byteval!! - val prefix = if (args[1].asBoolean) "$" else "" - dialog.canvas.printAsciiText("$prefix${number.toString(16).padStart(2, '0')}") - } - "c64scr.print_uwhex" -> { - val number = args[0].wordval!! - val prefix = if (args[1].asBoolean) "$" else "" - dialog.canvas.printAsciiText("$prefix${number.toString(16).padStart(4, '0')}") - } - "c64scr.print_uwbin" -> { - val number = args[0].wordval!! - val prefix = if (args[1].asBoolean) "%" else "" - dialog.canvas.printAsciiText("$prefix${number.toString(2).padStart(16, '0')}") - } - "c64scr.print_ubbin" -> { - val number = args[0].byteval!! - val prefix = if (args[1].asBoolean) "%" else "" - dialog.canvas.printAsciiText("$prefix${number.toString(2).padStart(8, '0')}") - } - "c64scr.clear_screenchars" -> { - dialog.canvas.clearScreen(6) - } - "c64scr.clear_screen" -> { - dialog.canvas.clearScreen(args[0].integerValue().toShort()) - } - "c64scr.setcc" -> { - dialog.canvas.setScreenChar(args[0].integerValue(), args[1].integerValue(), args[2].integerValue().toShort(), args[3].integerValue().toShort()) - } - "c64scr.plot" -> { - dialog.canvas.setCursorPos(args[0].integerValue(), args[1].integerValue()) - } - "c64scr.input_chars" -> { - val input=mutableListOf() - for(i in 0 until 80) { - while(dialog.keyboardBuffer.isEmpty()) { - Thread.sleep(10) - } - val char=dialog.keyboardBuffer.pop() - if(char=='\n') - break - else { - input.add(char) - dialog.canvas.printAsciiText(char.toString()) - } - } - var inputStr = input.joinToString("") - val inputLength = inputStr.length - val heapId = args[0].wordval!! - val origStrLength = getAsciiStringFromRuntimeVars(heapId).length - while(inputStr.length < origStrLength) { - inputStr += '\u0000' - } - result = RuntimeValueNumeric(DataType.UBYTE, inputLength) - } - "c64flt.print_f" -> { - dialog.canvas.printAsciiText(args[0].floatval.toString()) - } - "c64.CHROUT" -> { - dialog.canvas.printPetsciiChar(args[0].byteval!!) - } - "c64.CLEARSCR" -> { - dialog.canvas.clearScreen(6) - } - "c64.CHRIN" -> { - while(dialog.keyboardBuffer.isEmpty()) { - Thread.sleep(10) - } - val char=dialog.keyboardBuffer.pop() - result = RuntimeValueNumeric(DataType.UBYTE, char.toShort()) - } - "c64.GETIN" -> { - Thread.sleep(1) - result = if(dialog.keyboardBuffer.isEmpty()) - RuntimeValueNumeric(DataType.UBYTE, 0) - else - RuntimeValueNumeric(DataType.UBYTE, dialog.keyboardBuffer.pop().toShort()) - } - "c64utils.str2uword" -> { - val heapId = args[0].wordval!! - val argString = getAsciiStringFromRuntimeVars(heapId) - val numericpart = argString.takeWhile { it.isDigit() } - result = RuntimeValueNumeric(DataType.UWORD, numericpart.toInt() and 65535) - } - else -> TODO("astvm implement syscall ${sub.scopedname} $sub") - } - - return result - } - - private fun getAsciiStringFromRuntimeVars(heapId: Int): String = - (runtimeVariables.getByHeapId(heapId) as RuntimeValueString).str - - private fun getArrayFromRuntimeVars(heapId: Int): IntArray { - val arrayvar = runtimeVariables.getByHeapId(heapId) as RuntimeValueArray - return arrayvar.array.map { it.toInt() }.toIntArray() - } - - private fun performBuiltinFunction(name: String, args: List, statusflags: StatusFlags): RuntimeValueNumeric? { - return when (name) { - "rnd" -> RuntimeValueNumeric(DataType.UBYTE, rnd.nextInt() and 255) - "rndw" -> RuntimeValueNumeric(DataType.UWORD, rnd.nextInt() and 65535) - "rndf" -> RuntimeValueNumeric(DataType.FLOAT, rnd.nextDouble()) - "lsb" -> RuntimeValueNumeric(DataType.UBYTE, args[0].integerValue() and 255) - "msb" -> RuntimeValueNumeric(DataType.UBYTE, (args[0].integerValue() ushr 8) and 255) - "sin" -> RuntimeValueNumeric(DataType.FLOAT, sin(args[0].numericValue().toDouble())) - "sin8" -> { - val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI - RuntimeValueNumeric(DataType.BYTE, (127.0 * sin(rad)).toShort()) - } - "sin8u" -> { - val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI - RuntimeValueNumeric(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort()) - } - "sin16" -> { - val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI - RuntimeValueNumeric(DataType.BYTE, (32767.0 * sin(rad)).toShort()) - } - "sin16u" -> { - val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI - RuntimeValueNumeric(DataType.UBYTE, (32768.0 + 32767.5 * sin(rad)).toShort()) - } - "cos" -> RuntimeValueNumeric(DataType.FLOAT, cos(args[0].numericValue().toDouble())) - "cos8" -> { - val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI - RuntimeValueNumeric(DataType.BYTE, (127.0 * cos(rad)).toShort()) - } - "cos8u" -> { - val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI - RuntimeValueNumeric(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort()) - } - "cos16" -> { - val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI - RuntimeValueNumeric(DataType.BYTE, (32767.0 * cos(rad)).toShort()) - } - "cos16u" -> { - val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI - RuntimeValueNumeric(DataType.UBYTE, (32768.0 + 32767.5 * cos(rad)).toShort()) - } - "tan" -> RuntimeValueNumeric(DataType.FLOAT, tan(args[0].numericValue().toDouble())) - "atan" -> RuntimeValueNumeric(DataType.FLOAT, atan(args[0].numericValue().toDouble())) - "ln" -> RuntimeValueNumeric(DataType.FLOAT, ln(args[0].numericValue().toDouble())) - "log2" -> RuntimeValueNumeric(DataType.FLOAT, log2(args[0].numericValue().toDouble())) - "sqrt" -> RuntimeValueNumeric(DataType.FLOAT, sqrt(args[0].numericValue().toDouble())) - "sqrt16" -> RuntimeValueNumeric(DataType.UBYTE, sqrt((args[0] as RuntimeValueNumeric).wordval!!.toDouble()).toInt()) - "rad" -> RuntimeValueNumeric(DataType.FLOAT, Math.toRadians(args[0].numericValue().toDouble())) - "deg" -> RuntimeValueNumeric(DataType.FLOAT, Math.toDegrees(args[0].numericValue().toDouble())) - "round" -> RuntimeValueNumeric(DataType.FLOAT, round(args[0].numericValue().toDouble())) - "floor" -> RuntimeValueNumeric(DataType.FLOAT, floor(args[0].numericValue().toDouble())) - "ceil" -> RuntimeValueNumeric(DataType.FLOAT, ceil(args[0].numericValue().toDouble())) - "rol" -> { - val (result, newCarry) = (args[0] as RuntimeValueNumeric).rol(statusflags.carry) - statusflags.carry = newCarry - return result - } - "rol2" -> (args[0] as RuntimeValueNumeric).rol2() - "ror" -> { - val (result, newCarry) = (args[0] as RuntimeValueNumeric).ror(statusflags.carry) - statusflags.carry = newCarry - return result - } - "ror2" -> (args[0] as RuntimeValueNumeric).ror2() - "lsl" -> (args[0] as RuntimeValueNumeric).shl() - "lsr" -> (args[0] as RuntimeValueNumeric).shr() - "abs" -> { - when (args[0].type) { - DataType.UBYTE -> (args[0] as RuntimeValueNumeric) - DataType.BYTE -> RuntimeValueNumeric(DataType.UBYTE, abs(args[0].numericValue().toDouble())) - DataType.UWORD -> (args[0] as RuntimeValueNumeric) - DataType.WORD -> RuntimeValueNumeric(DataType.UWORD, abs(args[0].numericValue().toDouble())) - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, abs(args[0].numericValue().toDouble())) - else -> throw VmExecutionException("strange abs type ${args[0]}") - } - } - "max" -> { - val numbers = (args.single() as RuntimeValueArray).array.map { it.toDouble() } - RuntimeValueNumeric(ArrayElementTypes.getValue(args[0].type), numbers.max()!!) - } - "min" -> { - val numbers = (args.single() as RuntimeValueArray).array.map { it.toDouble() } - RuntimeValueNumeric(ArrayElementTypes.getValue(args[0].type), numbers.min()!!) - } - "sum" -> { - val sum = (args.single() as RuntimeValueArray).array.map { it.toDouble() }.sum() - when (args[0].type) { - DataType.ARRAY_UB -> RuntimeValueNumeric(DataType.UWORD, sum) - DataType.ARRAY_B -> RuntimeValueNumeric(DataType.WORD, sum) - DataType.ARRAY_UW -> RuntimeValueNumeric(DataType.UWORD, sum) - DataType.ARRAY_W -> RuntimeValueNumeric(DataType.WORD, sum) - DataType.ARRAY_F -> RuntimeValueNumeric(DataType.FLOAT, sum) - else -> throw VmExecutionException("weird sum type ${args[0]}") - } - } - "any" -> { - val numbers = (args.single() as RuntimeValueArray).array.map { it.toDouble() } - RuntimeValueNumeric(DataType.UBYTE, if (numbers.any { it != 0.0 }) 1 else 0) - } - "all" -> { - val numbers = (args.single() as RuntimeValueArray).array.map { it.toDouble() } - RuntimeValueNumeric(DataType.UBYTE, if (numbers.all { it != 0.0 }) 1 else 0) - } - "swap" -> - throw VmExecutionException("swap() cannot be implemented as a function") - "strlen" -> { - val zeroIndex = (args[0] as RuntimeValueString).str.indexOf(0.toChar()) - if (zeroIndex >= 0) - RuntimeValueNumeric(DataType.UBYTE, zeroIndex) - else - RuntimeValueNumeric(DataType.UBYTE, (args[0] as RuntimeValueString).str.length) - } - "memset" -> { - val heapId = (args[0] as RuntimeValueNumeric).wordval!! - val target = getArrayFromRuntimeVars(heapId) - val amount = args[1].integerValue() - val value = args[2].integerValue() - for (i in 0 until amount) { - target[i] = value - } - null - } - "memsetw" -> { - val heapId = (args[0] as RuntimeValueNumeric).wordval!! - val target = getArrayFromRuntimeVars(heapId) - val amount = args[1].integerValue() - val value = args[2].integerValue() - for (i in 0 until amount step 2) { - target[i * 2] = value and 255 - target[i * 2 + 1] = value ushr 8 - } - null - } - "memcopy" -> { - val sourceHeapId = (args[0] as RuntimeValueNumeric).wordval!! - val destHeapId = (args[1] as RuntimeValueNumeric).wordval!! - val source = getArrayFromRuntimeVars(sourceHeapId) - val dest = getArrayFromRuntimeVars(destHeapId) - val amount = args[2].integerValue() - for(i in 0 until amount) { - dest[i] = source[i] - } - null - } - "mkword" -> { - val result = (args[1].integerValue() shl 8) or args[0].integerValue() - RuntimeValueNumeric(DataType.UWORD, result) - } - "set_carry" -> { - statusflags.carry=true - null - } - "clear_carry" -> { - statusflags.carry=false - null - } - "set_irqd" -> { - statusflags.irqd=true - null - } - "clear_irqd" -> { - statusflags.irqd=false - null - } - "read_flags" -> { - val carry = if(statusflags.carry) 1 else 0 - val zero = if(statusflags.zero) 2 else 0 - val irqd = if(statusflags.irqd) 4 else 0 - val negative = if(statusflags.negative) 128 else 0 - RuntimeValueNumeric(DataType.UBYTE, carry or zero or irqd or negative) - } - "rsave" -> { - statusFlagsSave.push(statusflags) - registerAsave.push(runtimeVariables.get(program.namespace, Register.A.name) as RuntimeValueNumeric) - registerXsave.push(runtimeVariables.get(program.namespace, Register.X.name) as RuntimeValueNumeric) - registerYsave.push(runtimeVariables.get(program.namespace, Register.Y.name) as RuntimeValueNumeric) - null - } - "rrestore" -> { - val flags = statusFlagsSave.pop() - statusflags.carry = flags.carry - statusflags.negative = flags.negative - statusflags.zero = flags.zero - statusflags.irqd = flags.irqd - runtimeVariables.set(program.namespace, Register.A.name, registerAsave.pop()) - runtimeVariables.set(program.namespace, Register.X.name, registerXsave.pop()) - runtimeVariables.set(program.namespace, Register.Y.name, registerYsave.pop()) - null - } - "sort" -> { - val array=args.single() as RuntimeValueArray - array.array.sort() - null - } - "reverse" -> { - val array=args.single() as RuntimeValueArray - array.array.reverse() - null - } - "sgn" -> { - val value = args.single().numericValue().toDouble() - when { - value<0.0 -> RuntimeValueNumeric(DataType.BYTE, -1) - value==0.0 -> RuntimeValueNumeric(DataType.BYTE, 0) - else -> RuntimeValueNumeric(DataType.BYTE, 1) - } - } - "exit" -> { - val rv = args.single().numericValue() - dialog.canvas.printAsciiText("\n") - throw VmTerminationException("program ended with exit($rv)") - } - else -> TODO("astvm implement builtin function $name") - } - } -} diff --git a/compiler/src/prog8/vm/astvm/AstVmVariablesCreator.kt b/compiler/src/prog8/vm/astvm/AstVmVariablesCreator.kt deleted file mode 100644 index 97f9406f6..000000000 --- a/compiler/src/prog8/vm/astvm/AstVmVariablesCreator.kt +++ /dev/null @@ -1,79 +0,0 @@ -package prog8.vm.astvm - -import prog8.ast.Program -import prog8.ast.base.DataType -import prog8.ast.base.Position -import prog8.ast.base.Register -import prog8.ast.base.VarDeclType -import prog8.ast.expressions.ArrayLiteralValue -import prog8.ast.expressions.NumericLiteralValue -import prog8.ast.expressions.StringLiteralValue -import prog8.ast.processing.IAstVisitor -import prog8.ast.statements.StructDecl -import prog8.ast.statements.VarDecl -import prog8.ast.statements.ZeropageWish -import prog8.vm.RuntimeValueArray -import prog8.vm.RuntimeValueNumeric -import prog8.vm.RuntimeValueString - -class AstVmVariablesCreator(private val runtimeVariables: RuntimeVariables) : IAstVisitor { - - override fun visit(program: Program) { - // define the three registers as global variables, and set their initial values - runtimeVariables.define(program.namespace, Register.A.name, RuntimeValueNumeric(DataType.UBYTE, 0)) - runtimeVariables.define(program.namespace, Register.X.name, RuntimeValueNumeric(DataType.UBYTE, 255)) - runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValueNumeric(DataType.UBYTE, 0)) - - val globalpos = Position("<>", 0, 0, 0) - val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.A.name, null, - NumericLiteralValue.optimalInteger(0, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos) - val vdX = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.X.name, null, - NumericLiteralValue.optimalInteger(255, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos) - val vdY = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.Y.name, null, - NumericLiteralValue.optimalInteger(0, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos) - vdA.linkParents(program.namespace) - vdX.linkParents(program.namespace) - vdY.linkParents(program.namespace) - program.namespace.statements.add(vdA) - program.namespace.statements.add(vdX) - program.namespace.statements.add(vdY) - - super.visit(program) - } - - override fun visit(decl: VarDecl) { - // if the decl is part of a struct, just skip it - if(decl.parent !is StructDecl) { - when (decl.type) { - VarDeclType.VAR -> { - if(decl.datatype!=DataType.STRUCT) { - val numericLv = decl.value as? NumericLiteralValue - val value = if(numericLv!=null) { - RuntimeValueNumeric.fromLv(numericLv) - } else { - val strLv = decl.value as? StringLiteralValue - val arrayLv = decl.value as? ArrayLiteralValue - when { - strLv!=null -> { - RuntimeValueString.fromLv(strLv) - } - arrayLv!=null -> { - RuntimeValueArray.fromLv(arrayLv) - } - else -> throw VmExecutionException("weird var type") - } - } - runtimeVariables.define(decl.definingScope(), decl.name, value) - } - } - VarDeclType.MEMORY -> { - runtimeVariables.defineMemory(decl.definingScope(), decl.name, (decl.value as NumericLiteralValue).number.toInt()) - } - VarDeclType.CONST -> { - // consts should have been const-folded away - } - } - } - super.visit(decl) - } -} diff --git a/compiler/src/prog8/vm/astvm/Expressions.kt b/compiler/src/prog8/vm/astvm/Expressions.kt deleted file mode 100644 index 1f7329f37..000000000 --- a/compiler/src/prog8/vm/astvm/Expressions.kt +++ /dev/null @@ -1,177 +0,0 @@ -package prog8.vm.astvm - -import prog8.ast.Program -import prog8.ast.base.ArrayElementTypes -import prog8.ast.base.DataType -import prog8.ast.base.FatalAstException -import prog8.ast.base.VarDeclType -import prog8.ast.expressions.* -import prog8.ast.statements.BuiltinFunctionStatementPlaceholder -import prog8.ast.statements.Label -import prog8.ast.statements.Subroutine -import prog8.ast.statements.VarDecl -import prog8.vm.* - - -typealias BuiltinfunctionCaller = (name: String, args: List, flags: StatusFlags) -> RuntimeValueNumeric? -typealias SubroutineCaller = (sub: Subroutine, args: List, startAtLabel: Label?) -> RuntimeValueNumeric? - - -class EvalContext(val program: Program, val mem: Memory, val statusflags: StatusFlags, - val runtimeVars: RuntimeVariables, - val performBuiltinFunction: BuiltinfunctionCaller, - val executeSubroutine: SubroutineCaller) - -fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValueBase { - val constval = expr.constValue(ctx.program) - if(constval!=null) - return RuntimeValueNumeric.fromLv(constval) - - when(expr) { - is NumericLiteralValue -> return RuntimeValueNumeric.fromLv(expr) - is StringLiteralValue -> return RuntimeValueString.fromLv(expr) - is ArrayLiteralValue -> return RuntimeValueArray.fromLv(expr) - is PrefixExpression -> { - return when(expr.operator) { - "-" -> (evaluate(expr.expression, ctx) as RuntimeValueNumeric).neg() - "~" -> (evaluate(expr.expression, ctx) as RuntimeValueNumeric).inv() - "not" -> (evaluate(expr.expression, ctx) as RuntimeValueNumeric).not() - // unary '+' should have been optimized away - else -> throw VmExecutionException("unsupported prefix operator "+expr.operator) - } - } - is BinaryExpression -> { - val left = evaluate(expr.left, ctx) as RuntimeValueNumeric - val right = evaluate(expr.right, ctx) as RuntimeValueNumeric - return when(expr.operator) { - "<" -> RuntimeValueNumeric(DataType.UBYTE, if (left < right) 1 else 0) - "<=" -> RuntimeValueNumeric(DataType.UBYTE, if (left <= right) 1 else 0) - ">" -> RuntimeValueNumeric(DataType.UBYTE, if (left > right) 1 else 0) - ">=" -> RuntimeValueNumeric(DataType.UBYTE, if (left >= right) 1 else 0) - "==" -> RuntimeValueNumeric(DataType.UBYTE, if (left == right) 1 else 0) - "!=" -> RuntimeValueNumeric(DataType.UBYTE, if (left != right) 1 else 0) - "+" -> left.add(right) - "-" -> left.sub(right) - "*" -> left.mul(right) - "/" -> left.div(right) - "**" -> left.pow(right) - "<<" -> { - var result = left - repeat(right.integerValue()) {result = result.shl()} - result - } - ">>" -> { - var result = left - repeat(right.integerValue()) {result = result.shr()} - result - } - "%" -> left.remainder(right) - "|" -> left.bitor(right) - "&" -> left.bitand(right) - "^" -> left.bitxor(right) - "and" -> left.and(right) - "or" -> left.or(right) - "xor" -> left.xor(right) - else -> throw VmExecutionException("unsupported operator "+expr.operator) - } - } - is ArrayIndexedExpression -> { - val array = evaluate(expr.identifier, ctx) - val index = evaluate(expr.arrayspec.index, ctx) as RuntimeValueNumeric - return when (array) { - is RuntimeValueString -> { - val value = array.str[index.integerValue()] - RuntimeValueNumeric(ArrayElementTypes.getValue(array.type), value.toShort()) - } - is RuntimeValueArray -> { - val value = array.array[index.integerValue()] - RuntimeValueNumeric(ArrayElementTypes.getValue(array.type), value) - } - else -> throw VmExecutionException("weird type") - } - } - is TypecastExpression -> { - return (evaluate(expr.expression, ctx) as RuntimeValueNumeric).cast(expr.type) - } - is AddressOf -> { - // we support: address of heap var -> the heap id - return try { - val heapId = expr.identifier.heapId(ctx.program.namespace) - RuntimeValueNumeric(DataType.UWORD, heapId) - } catch( f: FatalAstException) { - // fallback: use the hash of the name, so we have at least *a* value... - val address = expr.identifier.hashCode() and 65535 - RuntimeValueNumeric(DataType.UWORD, address) - } - } - is DirectMemoryRead -> { - val address = (evaluate(expr.addressExpression, ctx) as RuntimeValueNumeric).wordval!! - return RuntimeValueNumeric(DataType.UBYTE, ctx.mem.getUByte(address)) - } - is RegisterExpr -> return ctx.runtimeVars.get(ctx.program.namespace, expr.register.name) - is IdentifierReference -> { - val scope = expr.definingScope() - val variable = scope.lookup(expr.nameInSource, expr) - if(variable is VarDecl) { - when { - variable.type==VarDeclType.VAR -> return ctx.runtimeVars.get(variable.definingScope(), variable.name) - variable.datatype==DataType.STRUCT -> throw VmExecutionException("cannot process structs by-value. at ${expr.position}") - else -> { - val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name) - return when(variable.datatype) { - DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, ctx.mem.getUByte(address)) - DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, ctx.mem.getSByte(address)) - DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, ctx.mem.getUWord(address)) - DataType.WORD -> RuntimeValueNumeric(DataType.WORD, ctx.mem.getSWord(address)) - DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, ctx.mem.getFloat(address)) - DataType.STR -> RuntimeValueString(ctx.mem.getString(address, false), false, null) - else -> throw VmExecutionException("unexpected datatype $variable") - } - } - } - } else - throw VmExecutionException("weird identifier reference $variable") - } - is FunctionCall -> { - val sub = expr.target.targetStatement(ctx.program.namespace) - val args = expr.args.map { evaluate(it, ctx) as RuntimeValueNumeric } - return when(sub) { - is Subroutine -> { - val result = ctx.executeSubroutine(sub, args, null) - ?: throw VmExecutionException("expected a result from functioncall $expr") - result - } - is BuiltinFunctionStatementPlaceholder -> { - val result = ctx.performBuiltinFunction(sub.name, args, ctx.statusflags) - ?: throw VmExecutionException("expected 1 result from functioncall $expr") - result - } - else -> { - throw VmExecutionException("unimplemented function call target $sub") - } - } - } - is RangeExpr -> { - val cRange = expr.toConstantIntegerRange() - if(cRange!=null) { - val dt = expr.inferType(ctx.program) - if(dt.isKnown) - return RuntimeValueRange(dt.typeOrElse(DataType.UBYTE), cRange) - else - throw VmExecutionException("couldn't determine datatype") - } - val fromVal = (evaluate(expr.from, ctx) as RuntimeValueNumeric).integerValue() - val toVal = (evaluate(expr.to, ctx) as RuntimeValueNumeric).integerValue() - val stepVal = (evaluate(expr.step, ctx) as RuntimeValueNumeric).integerValue() - val range = makeRange(fromVal, toVal, stepVal) - val dt = expr.inferType(ctx.program) - if(dt.isKnown) - return RuntimeValueRange(dt.typeOrElse(DataType.UBYTE), range) - else - throw VmExecutionException("couldn't determine datatype") - } - else -> { - throw VmExecutionException("unimplemented expression node $expr") - } - } -} diff --git a/compiler/src/prog8/vm/astvm/Memory.kt b/compiler/src/prog8/vm/astvm/Memory.kt deleted file mode 100644 index f9f4dc33f..000000000 --- a/compiler/src/prog8/vm/astvm/Memory.kt +++ /dev/null @@ -1,122 +0,0 @@ -package prog8.vm.astvm - -import prog8.compiler.target.CompilationTarget -import prog8.compiler.target.c64.C64MachineDefinition -import kotlin.math.abs - -class Memory(private val readObserver: (address: Int, value: Short) -> Short, - private val writeObserver: (address: Int, value: Short) -> Short) -{ - - private val mem = ShortArray(65536) // shorts because byte is signed and we store values 0..255 - private val observed = BooleanArray(65536) // what addresses are observed - - - fun observe(vararg address: Int) { - address.forEach { observed[it]=true } - } - - fun getUByte(address: Int): Short { - return if(observed[address]) readObserver(address, mem[address]) - else mem[address] - } - - fun getUByteDirectly(address: Int): Short { - return mem[address] - } - - fun getSByte(address: Int): Short { - val ubyte = getUByte(address) - return if(ubyte <= 127) ubyte - else (-((ubyte.toInt() xor 255)+1)).toShort() // 2's complement - } - - fun setUByte(address: Int, value: Short) { - if(value !in 0..255) - throw VmExecutionException("ubyte value out of range $value") - mem[address] = - if(observed[address]) writeObserver(address, value) - else value - } - - fun setUByteDirectly(address: Int, value: Short) { - if(value !in 0..255) - throw VmExecutionException("ubyte value out of range $value") - mem[address] = value - } - - fun setSByte(address: Int, value: Short) { - if(value !in -128..127) throw VmExecutionException("byte value out of range $value") - val ubyte = - if(value>=0) value - else ((abs(value.toInt()) xor 255)+1).toShort() // 2's complement - setUByte(address, ubyte) - } - - fun getUWord(address: Int): Int { - return getUByte(address) + 256*getUByte(address+1) - } - - fun getSWord(address: Int): Int { - val uword = getUWord(address) - if(uword <= 32767) - return uword - return -((uword xor 65535)+1) // 2's complement - } - - fun setUWord(address: Int, value: Int) { - if(value !in 0..65535) - throw VmExecutionException("uword value out of range $value") - setUByte(address, value.and(255).toShort()) - setUByte(address+1, (value / 256).toShort()) - } - - fun setSWord(address: Int, value: Int) { - if(value !in -32768..32767) throw VmExecutionException("word value out of range $value") - if(value>=0) - setUWord(address, value) - else - setUWord(address, (abs(value) xor 65535)+1) // 2's complement - } - - fun setFloat(address: Int, value: Double) { - val mflpt5 = C64MachineDefinition.Mflpt5.fromNumber(value) - setUByte(address, mflpt5.b0) - setUByte(address+1, mflpt5.b1) - setUByte(address+2, mflpt5.b2) - setUByte(address+3, mflpt5.b3) - setUByte(address+4, mflpt5.b4) - } - - fun getFloat(address: Int): Double { - return C64MachineDefinition.Mflpt5(getUByte(address), getUByte(address + 1), getUByte(address + 2), - getUByte(address + 3), getUByte(address + 4)).toDouble() - } - - fun setString(address: Int, str: String, altEncoding: Boolean) { - val encoded = CompilationTarget.encodeString(str, altEncoding) - var addr = address - for (c in encoded) setUByte(addr++, c) - setUByte(addr, 0) - } - - fun getString(strAddress: Int, altEncoding: Boolean): String { - val encoded = mutableListOf() - var addr = strAddress - while(true) { - val byte = getUByte(addr++) - if(byte==0.toShort()) break - encoded.add(byte) - } - return CompilationTarget.decodeString(encoded, altEncoding) - } - - fun clear() { - for(i in 0..65535) setUByte(i, 0) - } - - fun copy(from: Int, to: Int, numbytes: Int) { - for(i in 0 until numbytes) - setUByte(to+i, getUByte(from+i)) - } -} diff --git a/compiler/src/prog8/vm/astvm/ScreenDialog.kt b/compiler/src/prog8/vm/astvm/ScreenDialog.kt deleted file mode 100644 index 30d14256c..000000000 --- a/compiler/src/prog8/vm/astvm/ScreenDialog.kt +++ /dev/null @@ -1,184 +0,0 @@ -package prog8.vm.astvm - -import prog8.compiler.target.c64.C64MachineDefinition -import prog8.compiler.target.c64.Petscii -import java.awt.* -import java.awt.event.KeyEvent -import java.awt.event.KeyListener -import java.awt.image.BufferedImage -import java.util.* -import javax.swing.JFrame -import javax.swing.JPanel -import javax.swing.Timer - - -class BitmapScreenPanel : KeyListener, JPanel() { - - private val image = BufferedImage(SCREENWIDTH, SCREENHEIGHT, BufferedImage.TYPE_INT_ARGB) - private val g2d = image.graphics as Graphics2D - private var cursorX: Int=0 - private var cursorY: Int=0 - val keyboardBuffer = ArrayDeque() - - init { - val size = Dimension(image.width * SCALING, image.height * SCALING) - minimumSize = size - maximumSize = size - preferredSize = size - clearScreen(6) - isFocusable = true - requestFocusInWindow() - addKeyListener(this) - } - - override fun keyTyped(p0: KeyEvent) { - keyboardBuffer.add(p0.keyChar) - } - - override fun keyPressed(p0: KeyEvent) { - } - - override fun keyReleased(p0: KeyEvent?) { - } - - override fun paint(graphics: Graphics?) { - val g2d = graphics as Graphics2D? - g2d!!.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF) - g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE) - g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR) - g2d.drawImage(image, 0, 0, image.width * 3, image.height * 3, null) - } - - fun clearScreen(color: Short) { - g2d.background = C64MachineDefinition.colorPalette[color % C64MachineDefinition.colorPalette.size] - g2d.clearRect(0, 0, SCREENWIDTH, SCREENHEIGHT) - cursorX = 0 - cursorY = 0 - } - fun setPixel(x: Int, y: Int, color: Short) { - image.setRGB(x, y, C64MachineDefinition.colorPalette[color % C64MachineDefinition.colorPalette.size].rgb) - } - fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Short) { - g2d.color = C64MachineDefinition.colorPalette[color % C64MachineDefinition.colorPalette.size] - g2d.drawLine(x1, y1, x2, y2) - } - - fun printAsciiText(text: String) { - val t2 = text.substringBefore(0.toChar()) - val petscii = Petscii.encodePetscii(t2, true) - petscii.forEach { printPetsciiChar(it) } - } - - fun printPetsciiChar(petscii: Short) { - if(petscii in listOf(0x0d.toShort(), 0x8d.toShort())) { - // Return and shift-Return - cursorX=0 - cursorY++ - } else { - val scr = Petscii.petscii2scr(petscii, false) - setScreenChar(cursorX, cursorY, scr, 1) - cursorX++ - if (cursorX >= (SCREENWIDTH / 8)) { - cursorY++ - cursorX = 0 - } - } - while(cursorY>=(SCREENHEIGHT/8)) { - // scroll the screen up because the cursor went past the last line - Thread.sleep(10) - val screen = image.copy() - val graphics = image.graphics as Graphics2D - graphics.drawImage(screen, 0, -8, null) - val color = graphics.color - graphics.color = C64MachineDefinition.colorPalette[6] - graphics.fillRect(0, 24*8, SCREENWIDTH, 25*8) - graphics.color=color - cursorY-- - } - } - - fun setScreenChar(x: Int, y: Int, screencode: Short, color: Short) { - g2d.clearRect(8*x, 8*y, 8, 8) - val colorIdx = (color % C64MachineDefinition.colorPalette.size).toShort() - val coloredImage = C64MachineDefinition.Charset.getColoredChar(screencode, colorIdx) - g2d.drawImage(coloredImage, 8*x, 8*y , null) - } - - fun setCursorPos(x: Int, y: Int) { - cursorX = x - cursorY = y - } - - fun getCursorPos(): Pair { - return Pair(cursorX, cursorY) - } - - companion object { - const val SCREENWIDTH = 320 - const val SCREENHEIGHT = 200 - const val SCALING = 3 - } -} - - -class ScreenDialog(title: String) : JFrame(title) { - val canvas = BitmapScreenPanel() - val keyboardBuffer = canvas.keyboardBuffer - - init { - val borderWidth = 16 - layout = GridBagLayout() - defaultCloseOperation = EXIT_ON_CLOSE - isResizable = false - - // the borders (top, left, right, bottom) - val borderTop = JPanel().apply { - preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth) - background = C64MachineDefinition.colorPalette[14] - } - val borderBottom = JPanel().apply { - preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth) - background = C64MachineDefinition.colorPalette[14] - } - val borderLeft = JPanel().apply { - preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT) - background = C64MachineDefinition.colorPalette[14] - } - val borderRight = JPanel().apply { - preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT) - background = C64MachineDefinition.colorPalette[14] - } - var c = GridBagConstraints() - c.gridx=0; c.gridy=1; c.gridwidth=3 - add(borderTop, c) - c = GridBagConstraints() - c.gridx=0; c.gridy=2 - add(borderLeft, c) - c = GridBagConstraints() - c.gridx=2; c.gridy=2 - add(borderRight, c) - c = GridBagConstraints() - c.gridx=0; c.gridy=3; c.gridwidth=3 - add(borderBottom, c) - // the screen canvas(bitmap) - c = GridBagConstraints() - c.gridx = 1; c.gridy = 2 - add(canvas, c) - - canvas.requestFocusInWindow() - } - - fun start() { - val repaintTimer = Timer(1000 / 60) { repaint() } - repaintTimer.start() - } -} - - -private fun BufferedImage.copy(): BufferedImage { - val bcopy = BufferedImage(this.width, this.height, this.type) - val g = bcopy.graphics - g.drawImage(this, 0, 0, null) - g.dispose() - return bcopy -} diff --git a/compiler/test/RuntimeValueTests.kt b/compiler/test/RuntimeValueTests.kt deleted file mode 100644 index 882ff1fd3..000000000 --- a/compiler/test/RuntimeValueTests.kt +++ /dev/null @@ -1,352 +0,0 @@ -package prog8tests - -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance -import prog8.ast.base.DataType -import prog8.vm.RuntimeValueNumeric -import kotlin.test.* - - -private fun sameValueAndType(v1: RuntimeValueNumeric, v2: RuntimeValueNumeric): Boolean { - return v1.type==v2.type && v1==v2 -} - - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class TestRuntimeValueNumeric { - - @Test - fun testValueRanges() { - assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 0).integerValue()) - assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 255).integerValue()) - assertFailsWith { RuntimeValueNumeric(DataType.UBYTE, -1)} - assertFailsWith { RuntimeValueNumeric(DataType.UBYTE, 256)} - - assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 0).integerValue()) - assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, -128).integerValue()) - assertEquals(127, RuntimeValueNumeric(DataType.BYTE, 127).integerValue()) - assertFailsWith { RuntimeValueNumeric(DataType.BYTE, -129)} - assertFailsWith { RuntimeValueNumeric(DataType.BYTE, 128)} - - assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 0).integerValue()) - assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 65535).integerValue()) - assertFailsWith { RuntimeValueNumeric(DataType.UWORD, -1)} - assertFailsWith { RuntimeValueNumeric(DataType.UWORD, 65536)} - - assertEquals(0, RuntimeValueNumeric(DataType.WORD, 0).integerValue()) - assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, -32768).integerValue()) - assertEquals(32767, RuntimeValueNumeric(DataType.WORD, 32767).integerValue()) - assertFailsWith { RuntimeValueNumeric(DataType.WORD, -32769)} - assertFailsWith { RuntimeValueNumeric(DataType.WORD, 32768)} - } - - @Test - fun testTruthiness() - { - assertFalse(RuntimeValueNumeric(DataType.BYTE, 0).asBoolean) - assertFalse(RuntimeValueNumeric(DataType.UBYTE, 0).asBoolean) - assertFalse(RuntimeValueNumeric(DataType.WORD, 0).asBoolean) - assertFalse(RuntimeValueNumeric(DataType.UWORD, 0).asBoolean) - assertFalse(RuntimeValueNumeric(DataType.FLOAT, 0.0).asBoolean) - - assertTrue(RuntimeValueNumeric(DataType.BYTE, 42).asBoolean) - assertTrue(RuntimeValueNumeric(DataType.UBYTE, 42).asBoolean) - assertTrue(RuntimeValueNumeric(DataType.WORD, 42).asBoolean) - assertTrue(RuntimeValueNumeric(DataType.UWORD, 42).asBoolean) - assertTrue(RuntimeValueNumeric(DataType.FLOAT, 42.0).asBoolean) - assertTrue(RuntimeValueNumeric(DataType.BYTE, -42).asBoolean) - assertTrue(RuntimeValueNumeric(DataType.WORD, -42).asBoolean) - assertTrue(RuntimeValueNumeric(DataType.FLOAT, -42.0).asBoolean) - } - - - @Test - fun testIdentity() { - val v = RuntimeValueNumeric(DataType.UWORD, 12345) - assertEquals(v, v) - assertFalse(v != v) - assertTrue(v<=v) - assertTrue(v>=v) - assertFalse(vv) - - assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 100))) - } - - @Test - fun testEqualsAndNotEquals() { - assertEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 100)) - assertEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 100)) - assertEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 100)) - assertEquals(RuntimeValueNumeric(DataType.UWORD, 254), RuntimeValueNumeric(DataType.UBYTE, 254)) - assertEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12345)) - assertEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12345)) - assertEquals(RuntimeValueNumeric(DataType.FLOAT, 100.0), RuntimeValueNumeric(DataType.UBYTE, 100)) - assertEquals(RuntimeValueNumeric(DataType.FLOAT, 22239.0), RuntimeValueNumeric(DataType.UWORD, 22239)) - assertEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.99)) - - assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 100))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 100))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 100))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 254), RuntimeValueNumeric(DataType.UBYTE, 254))) - assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12345))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12345))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 100.0), RuntimeValueNumeric(DataType.UBYTE, 100))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 22239.0), RuntimeValueNumeric(DataType.UWORD, 22239))) - assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.99))) - - assertNotEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 101)) - assertNotEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 101)) - assertNotEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 101)) - assertNotEquals(RuntimeValueNumeric(DataType.UWORD, 245), RuntimeValueNumeric(DataType.UBYTE, 246)) - assertNotEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12346)) - assertNotEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12346)) - assertNotEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UBYTE, 9)) - assertNotEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UWORD, 9)) - assertNotEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.0)) - - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 101))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 101))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 101))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 245), RuntimeValueNumeric(DataType.UBYTE, 246))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12346))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12346))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UBYTE, 9))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UWORD, 9))) - assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.0))) - } - - @Test - fun testGreaterThan(){ - assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) > RuntimeValueNumeric(DataType.UBYTE, 99)) - assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) > RuntimeValueNumeric(DataType.UWORD, 253)) - assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) > RuntimeValueNumeric(DataType.FLOAT, 99.9)) - - assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) >= RuntimeValueNumeric(DataType.UBYTE, 100)) - assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) >= RuntimeValueNumeric(DataType.UWORD, 254)) - assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) >= RuntimeValueNumeric(DataType.FLOAT, 100.0)) - - assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) > RuntimeValueNumeric(DataType.UBYTE, 100)) - assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) > RuntimeValueNumeric(DataType.UWORD, 254)) - assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) > RuntimeValueNumeric(DataType.FLOAT, 100.0)) - - assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) >= RuntimeValueNumeric(DataType.UBYTE, 101)) - assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) >= RuntimeValueNumeric(DataType.UWORD, 255)) - assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) >= RuntimeValueNumeric(DataType.FLOAT, 100.1)) - } - - @Test - fun testLessThan() { - assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) < RuntimeValueNumeric(DataType.UBYTE, 101)) - assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) < RuntimeValueNumeric(DataType.UWORD, 255)) - assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) < RuntimeValueNumeric(DataType.FLOAT, 100.1)) - - assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) <= RuntimeValueNumeric(DataType.UBYTE, 100)) - assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) <= RuntimeValueNumeric(DataType.UWORD, 254)) - assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) <= RuntimeValueNumeric(DataType.FLOAT, 100.0)) - - assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) < RuntimeValueNumeric(DataType.UBYTE, 100)) - assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) < RuntimeValueNumeric(DataType.UWORD, 254)) - assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) < RuntimeValueNumeric(DataType.FLOAT, 100.0)) - - assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) <= RuntimeValueNumeric(DataType.UBYTE, 99)) - assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) <= RuntimeValueNumeric(DataType.UWORD, 253)) - assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) <= RuntimeValueNumeric(DataType.FLOAT, 99.9)) - } - - @Test - fun testNoDtConversion() { - assertFailsWith { - RuntimeValueNumeric(DataType.UWORD, 100).add(RuntimeValueNumeric(DataType.UBYTE, 120)) - } - assertFailsWith { - RuntimeValueNumeric(DataType.UBYTE, 100).add(RuntimeValueNumeric(DataType.UWORD, 120)) - } - assertFailsWith { - RuntimeValueNumeric(DataType.FLOAT, 100.22).add(RuntimeValueNumeric(DataType.UWORD, 120)) - } - assertFailsWith { - RuntimeValueNumeric(DataType.UWORD, 1002).add(RuntimeValueNumeric(DataType.FLOAT, 120.22)) - } - assertFailsWith { - RuntimeValueNumeric(DataType.FLOAT, 100.22).add(RuntimeValueNumeric(DataType.UBYTE, 120)) - } - assertFailsWith { - RuntimeValueNumeric(DataType.UBYTE, 12).add(RuntimeValueNumeric(DataType.FLOAT, 120.22)) - } - } - - @Test - fun testNoAutoFloatConversion() { - assertFailsWith { - RuntimeValueNumeric(DataType.UBYTE, 233).add(RuntimeValueNumeric(DataType.FLOAT, 1.234)) - } - assertFailsWith { - RuntimeValueNumeric(DataType.UWORD, 233).add(RuntimeValueNumeric(DataType.FLOAT, 1.234)) - } - assertFailsWith { - RuntimeValueNumeric(DataType.UBYTE, 233).mul(RuntimeValueNumeric(DataType.FLOAT, 1.234)) - } - assertFailsWith { - RuntimeValueNumeric(DataType.UWORD, 233).mul(RuntimeValueNumeric(DataType.FLOAT, 1.234)) - } - assertFailsWith { - RuntimeValueNumeric(DataType.UBYTE, 233).div(RuntimeValueNumeric(DataType.FLOAT, 1.234)) - } - assertFailsWith { - RuntimeValueNumeric(DataType.UWORD, 233).div(RuntimeValueNumeric(DataType.FLOAT, 1.234)) - } - val result = RuntimeValueNumeric(DataType.FLOAT, 233.333).add(RuntimeValueNumeric(DataType.FLOAT, 1.234)) - } - - @Test - fun arithmetictestUbyte() { - assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 200).add(RuntimeValueNumeric(DataType.UBYTE, 55)).integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 200).add(RuntimeValueNumeric(DataType.UBYTE, 56)).integerValue()) - assertEquals(1, RuntimeValueNumeric(DataType.UBYTE, 200).add(RuntimeValueNumeric(DataType.UBYTE, 57)).integerValue()) - - assertEquals(1, RuntimeValueNumeric(DataType.UBYTE, 2).sub(RuntimeValueNumeric(DataType.UBYTE, 1)).integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 2).sub(RuntimeValueNumeric(DataType.UBYTE, 2)).integerValue()) - assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 2).sub(RuntimeValueNumeric(DataType.UBYTE, 3)).integerValue()) - - assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 254).inc().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 255).inc().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 1).dec().integerValue()) - assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 0).dec().integerValue()) - - assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 0).inv().integerValue()) - assertEquals(0b00110011, RuntimeValueNumeric(DataType.UBYTE, 0b11001100).inv().integerValue()) -// assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 0).neg().integerValue()) -// assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 0).neg().integerValue()) - assertEquals(1, RuntimeValueNumeric(DataType.UBYTE, 0).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 1).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 111).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 255).not().integerValue()) - - assertEquals(200, RuntimeValueNumeric(DataType.UBYTE, 20).mul(RuntimeValueNumeric(DataType.UBYTE, 10)).integerValue()) - assertEquals(144, RuntimeValueNumeric(DataType.UBYTE, 20).mul(RuntimeValueNumeric(DataType.UBYTE, 20)).integerValue()) - - assertEquals(25, RuntimeValueNumeric(DataType.UBYTE, 5).pow(RuntimeValueNumeric(DataType.UBYTE, 2)).integerValue()) - assertEquals(125, RuntimeValueNumeric(DataType.UBYTE, 5).pow(RuntimeValueNumeric(DataType.UBYTE, 3)).integerValue()) - assertEquals(113, RuntimeValueNumeric(DataType.UBYTE, 5).pow(RuntimeValueNumeric(DataType.UBYTE, 4)).integerValue()) - - assertEquals(100, RuntimeValueNumeric(DataType.UBYTE, 50).shl().integerValue()) - assertEquals(200, RuntimeValueNumeric(DataType.UBYTE, 100).shl().integerValue()) - assertEquals(144, RuntimeValueNumeric(DataType.UBYTE, 200).shl().integerValue()) - } - - @Test - fun arithmetictestUWord() { - assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 60000).add(RuntimeValueNumeric(DataType.UWORD, 5535)).integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 60000).add(RuntimeValueNumeric(DataType.UWORD, 5536)).integerValue()) - assertEquals(1, RuntimeValueNumeric(DataType.UWORD, 60000).add(RuntimeValueNumeric(DataType.UWORD, 5537)).integerValue()) - - assertEquals(1, RuntimeValueNumeric(DataType.UWORD, 2).sub(RuntimeValueNumeric(DataType.UWORD, 1)).integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 2).sub(RuntimeValueNumeric(DataType.UWORD, 2)).integerValue()) - assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 2).sub(RuntimeValueNumeric(DataType.UWORD, 3)).integerValue()) - - assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 65534).inc().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 65535).inc().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 1).dec().integerValue()) - assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 0).dec().integerValue()) - - assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 0).inv().integerValue()) - assertEquals(0b0011001101010101, RuntimeValueNumeric(DataType.UWORD, 0b1100110010101010).inv().integerValue()) -// assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 0).neg().integerValue()) -// assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 0).neg().integerValue()) - assertEquals(1, RuntimeValueNumeric(DataType.UWORD, 0).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 1).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 11111).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 65535).not().integerValue()) - - assertEquals(2000, RuntimeValueNumeric(DataType.UWORD, 200).mul(RuntimeValueNumeric(DataType.UWORD, 10)).integerValue()) - assertEquals(40000, RuntimeValueNumeric(DataType.UWORD, 200).mul(RuntimeValueNumeric(DataType.UWORD, 200)).integerValue()) - assertEquals(14464, RuntimeValueNumeric(DataType.UWORD, 200).mul(RuntimeValueNumeric(DataType.UWORD, 400)).integerValue()) - - assertEquals(15625, RuntimeValueNumeric(DataType.UWORD, 5).pow(RuntimeValueNumeric(DataType.UWORD, 6)).integerValue()) - assertEquals(12589, RuntimeValueNumeric(DataType.UWORD, 5).pow(RuntimeValueNumeric(DataType.UWORD, 7)).integerValue()) - - assertEquals(10000, RuntimeValueNumeric(DataType.UWORD, 5000).shl().integerValue()) - assertEquals(60000, RuntimeValueNumeric(DataType.UWORD, 30000).shl().integerValue()) - assertEquals(14464, RuntimeValueNumeric(DataType.UWORD, 40000).shl().integerValue()) - } - - @Test - fun arithmetictestByte() { - assertEquals(127, RuntimeValueNumeric(DataType.BYTE, 100).add(RuntimeValueNumeric(DataType.BYTE, 27)).integerValue()) - assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, 100).add(RuntimeValueNumeric(DataType.BYTE, 28)).integerValue()) - assertEquals(-127, RuntimeValueNumeric(DataType.BYTE, 100).add(RuntimeValueNumeric(DataType.BYTE, 29)).integerValue()) - - assertEquals(1, RuntimeValueNumeric(DataType.BYTE, 2).sub(RuntimeValueNumeric(DataType.BYTE, 1)).integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 2).sub(RuntimeValueNumeric(DataType.BYTE, 2)).integerValue()) - assertEquals(-1, RuntimeValueNumeric(DataType.BYTE, 2).sub(RuntimeValueNumeric(DataType.BYTE, 3)).integerValue()) - assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, -100).sub(RuntimeValueNumeric(DataType.BYTE, 28)).integerValue()) - assertEquals(127, RuntimeValueNumeric(DataType.BYTE, -100).sub(RuntimeValueNumeric(DataType.BYTE, 29)).integerValue()) - - assertEquals(127, RuntimeValueNumeric(DataType.BYTE, 126).inc().integerValue()) - assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, 127).inc().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 1).dec().integerValue()) - assertEquals(-1, RuntimeValueNumeric(DataType.BYTE, 0).dec().integerValue()) - assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, -127).dec().integerValue()) - assertEquals(127, RuntimeValueNumeric(DataType.BYTE, -128).dec().integerValue()) - - assertEquals(-1, RuntimeValueNumeric(DataType.BYTE, 0).inv().integerValue()) - assertEquals(-103, RuntimeValueNumeric(DataType.BYTE, 0b01100110).inv().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 0).neg().integerValue()) - assertEquals(-2, RuntimeValueNumeric(DataType.BYTE, 2).neg().integerValue()) - assertEquals(1, RuntimeValueNumeric(DataType.BYTE, 0).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 1).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 111).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.BYTE, -33).not().integerValue()) - - assertEquals(100, RuntimeValueNumeric(DataType.BYTE, 10).mul(RuntimeValueNumeric(DataType.BYTE, 10)).integerValue()) - assertEquals(-56, RuntimeValueNumeric(DataType.BYTE, 20).mul(RuntimeValueNumeric(DataType.BYTE, 10)).integerValue()) - - assertEquals(25, RuntimeValueNumeric(DataType.BYTE, 5).pow(RuntimeValueNumeric(DataType.BYTE, 2)).integerValue()) - assertEquals(125, RuntimeValueNumeric(DataType.BYTE, 5).pow(RuntimeValueNumeric(DataType.BYTE, 3)).integerValue()) - assertEquals(113, RuntimeValueNumeric(DataType.BYTE, 5).pow(RuntimeValueNumeric(DataType.BYTE, 4)).integerValue()) - - assertEquals(100, RuntimeValueNumeric(DataType.BYTE, 50).shl().integerValue()) - assertEquals(-56, RuntimeValueNumeric(DataType.BYTE, 100).shl().integerValue()) - assertEquals(-2, RuntimeValueNumeric(DataType.BYTE, -1).shl().integerValue()) - } - - @Test - fun arithmetictestWorrd() { - assertEquals(32767, RuntimeValueNumeric(DataType.WORD, 32700).add(RuntimeValueNumeric(DataType.WORD, 67)).integerValue()) - assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, 32700).add(RuntimeValueNumeric(DataType.WORD, 68)).integerValue()) - assertEquals(-32767, RuntimeValueNumeric(DataType.WORD, 32700).add(RuntimeValueNumeric(DataType.WORD, 69)).integerValue()) - - assertEquals(1, RuntimeValueNumeric(DataType.WORD, 2).sub(RuntimeValueNumeric(DataType.WORD, 1)).integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.WORD, 2).sub(RuntimeValueNumeric(DataType.WORD, 2)).integerValue()) - assertEquals(-1, RuntimeValueNumeric(DataType.WORD, 2).sub(RuntimeValueNumeric(DataType.WORD, 3)).integerValue()) - assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, -32700).sub(RuntimeValueNumeric(DataType.WORD, 68)).integerValue()) - assertEquals(32767, RuntimeValueNumeric(DataType.WORD, -32700).sub(RuntimeValueNumeric(DataType.WORD, 69)).integerValue()) - - assertEquals(32767, RuntimeValueNumeric(DataType.WORD, 32766).inc().integerValue()) - assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, 32767).inc().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.WORD, 1).dec().integerValue()) - assertEquals(-1, RuntimeValueNumeric(DataType.WORD, 0).dec().integerValue()) - assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, -32767).dec().integerValue()) - assertEquals(32767, RuntimeValueNumeric(DataType.WORD, -32768).dec().integerValue()) - - assertEquals(-1, RuntimeValueNumeric(DataType.WORD, 0).inv().integerValue()) - assertEquals(-103, RuntimeValueNumeric(DataType.WORD, 0b01100110).inv().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.WORD, 0).neg().integerValue()) - assertEquals(-2, RuntimeValueNumeric(DataType.WORD, 2).neg().integerValue()) - assertEquals(1, RuntimeValueNumeric(DataType.WORD, 0).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.WORD, 1).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.WORD, 111).not().integerValue()) - assertEquals(0, RuntimeValueNumeric(DataType.WORD, -33).not().integerValue()) - - assertEquals(10000, RuntimeValueNumeric(DataType.WORD, 100).mul(RuntimeValueNumeric(DataType.WORD, 100)).integerValue()) - assertEquals(-25536, RuntimeValueNumeric(DataType.WORD, 200).mul(RuntimeValueNumeric(DataType.WORD, 200)).integerValue()) - - assertEquals(15625, RuntimeValueNumeric(DataType.WORD, 5).pow(RuntimeValueNumeric(DataType.WORD, 6)).integerValue()) - assertEquals(-6487, RuntimeValueNumeric(DataType.WORD, 9).pow(RuntimeValueNumeric(DataType.WORD, 5)).integerValue()) - - assertEquals(18000, RuntimeValueNumeric(DataType.WORD, 9000).shl().integerValue()) - assertEquals(-25536, RuntimeValueNumeric(DataType.WORD, 20000).shl().integerValue()) - assertEquals(-2, RuntimeValueNumeric(DataType.WORD, -1).shl().integerValue()) - } -} diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt index 13ff3d1e4..3c482629c 100644 --- a/compiler/test/UnitTests.kt +++ b/compiler/test/UnitTests.kt @@ -16,7 +16,6 @@ import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5 import prog8.compiler.target.c64.Petscii -import prog8.vm.RuntimeValueNumeric import java.io.CharConversionException import kotlin.test.* @@ -379,24 +378,4 @@ class TestPetscii { assertTrue(abc!=abd) assertFalse(abc!=abc) } - - @Test - fun testStackvmValueComparisons() { - val ten = RuntimeValueNumeric(DataType.FLOAT, 10) - val nine = RuntimeValueNumeric(DataType.UWORD, 9) - assertEquals(ten, ten) - assertNotEquals(ten, nine) - assertFalse(ten != ten) - assertTrue(ten != nine) - - assertTrue(ten > nine) - assertTrue(ten >= nine) - assertTrue(ten >= ten) - assertFalse(ten > ten) - - assertFalse(ten < nine) - assertFalse(ten <= nine) - assertTrue(ten <= ten) - assertFalse(ten < ten) - } } diff --git a/docs/source/building.rst b/docs/source/building.rst index faff28a29..21453e335 100644 --- a/docs/source/building.rst +++ b/docs/source/building.rst @@ -168,18 +168,3 @@ or:: $ ./p8compile.sh -emu examples/rasterbars.p8 - - -Virtual Machine / Simulator ---------------------------- - -You may have noticed the ``-sim`` command line option for the compiler: - --sim - Launches the "AST virtual machine Simulator" that directly executes the parsed program. - No compilation steps will be performed. - Allows for very fast testing and debugging before actually compiling programs - to machine code. - It simulates a bare minimum of features from the target platform, so most stuff - that calls ROM routines or writes into hardware registers won't work. But basic - system routines are emulated. diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 79885c15a..ca2f77ecf 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -152,13 +152,6 @@ Your program must have a single entry point where code execution begins. The compiler expects a ``start`` subroutine in the ``main`` block for this, taking no parameters and having no return value. -.. sidebar:: - 60hz IRQ entry point - - When running the generated code on the StackVm virtual machine, - it will use the ``irq`` subroutine in the ``irq`` block for the - 60hz irq routine. This is optional. - As any subroutine, it has to end with a ``return`` statement (or a ``goto`` call):: main {