diff --git a/compiler/src/prog8/astvm/AstVm.kt b/compiler/src/prog8/astvm/AstVm.kt index 0898813bf..a981b56e2 100644 --- a/compiler/src/prog8/astvm/AstVm.kt +++ b/compiler/src/prog8/astvm/AstVm.kt @@ -14,6 +14,49 @@ 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: LiteralValue?) { + if (value != null) { + when (value.type) { + DataType.UBYTE -> { + val v = value.bytevalue!!.toInt() + negative = v > 127 + zero = v == 0 + } + DataType.BYTE -> { + val v = value.bytevalue!!.toInt() + negative = v < 0 + zero = v == 0 + } + DataType.UWORD -> { + val v = value.wordvalue!! + negative = v > 32767 + zero = v == 0 + } + DataType.WORD -> { + val v = value.wordvalue!! + negative = v < 0 + zero = v == 0 + } + DataType.FLOAT -> { + val flt = value.floatvalue!! + negative = flt < 0.0 + zero = flt == 0.0 + } + else -> { + // no flags for non-numeric type + } + } + } + } +} + + class RuntimeVariables { fun define(scope: INameScope, name: String, initialValue: RuntimeValue) { val where = vars.getValue(scope) @@ -53,6 +96,13 @@ class RuntimeVariables { return address } + fun swap(a1: VarDecl, a2: VarDecl) { + val v1 = get(a1.definingScope(), a1.name) + val v2 = get(a2.definingScope(), a2.name) + set(a1.definingScope(), a1.name, v2) + set(a2.definingScope(), a2.name, v1) + } + private val vars = mutableMapOf>().withDefault { mutableMapOf() } private val memvars = mutableMapOf>().withDefault { mutableMapOf() } } @@ -60,14 +110,8 @@ class RuntimeVariables { class AstVm(val program: Program) { val mem = Memory() - var P_carry: Boolean = false - private set - var P_zero: Boolean = true - private set - var P_negative: Boolean = false - private set - var P_irqd: Boolean = false - private set + val statusflags = StatusFlags() + private var dialog = ScreenDialog() var instructionCounter = 0 @@ -135,7 +179,7 @@ class AstVm(val program: Program) { val idref = IdentifierReference(listOf(arg.first.name), sub.position) performAssignment(AssignTarget(null, idref, null, null, idref.position), arg.second, sub.statements.first(), - EvalContext(program, mem, runtimeVariables, functions, ::executeSubroutine)) + EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine)) } try { @@ -161,7 +205,7 @@ class AstVm(val program: Program) { } private fun executeStatement(sub: INameScope, stmt: IStatement) { - val evalCtx = EvalContext(program, mem, runtimeVariables, functions, ::executeSubroutine) + val evalCtx = EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine) instructionCounter++ if (instructionCounter % 100 == 0) Thread.sleep(1) @@ -191,8 +235,15 @@ class AstVm(val program: Program) { } } is BuiltinFunctionStatementPlaceholder -> { - val args = evaluate(stmt.arglist) - functions.performBuiltinFunction(target.name, args) + if(target.name=="swap") { + // swap cannot be implemented as a function, so inline it here + val a1 = (stmt.arglist[0] as IdentifierReference).targetVarDecl(program.namespace)!! + val a2 = (stmt.arglist[1] as IdentifierReference).targetVarDecl(program.namespace)!! + runtimeVariables.swap(a1, a2) + } else { + val args = evaluate(stmt.arglist) + functions.performBuiltinFunction(target.name, args, statusflags) + } } else -> { TODO("weird call $target") @@ -394,12 +445,12 @@ class AstVm(val program: Program) { // assign the new loop value to the loopvar, and run the code performAssignment(AssignTarget(null, loopVar, null, null, loopVar.position), RuntimeValue(loopvarDt, loopValue), stmt.body.statements.first(), - EvalContext(program, mem, runtimeVariables, functions, ::executeSubroutine)) + EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine)) executeAnonymousScope(stmt.body) } private fun evaluate(args: List): List { - val ctx = EvalContext(program, mem, runtimeVariables, functions, ::executeSubroutine) + val ctx = EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine) return args.map { evaluate(it, ctx) } } @@ -420,22 +471,32 @@ class AstVm(val program: Program) { "c64scr.print_b" -> { dialog.canvas.printText(args[0].byteval!!.toString(), 1, true) } - "c64scr.print_ubhex" -> { - val prefix = if (args[0].asBoolean) "$" else "" - val number = args[1].byteval!! - dialog.canvas.printText("$prefix${number.toString(16).padStart(2, '0')}", 1, true) - } "c64scr.print_uw" -> { dialog.canvas.printText(args[0].wordval!!.toString(), 1, true) } "c64scr.print_w" -> { dialog.canvas.printText(args[0].wordval!!.toString(), 1, true) } + "c64scr.print_ubhex" -> { + val prefix = if (args[0].asBoolean) "$" else "" + val number = args[1].byteval!! + dialog.canvas.printText("$prefix${number.toString(16).padStart(2, '0')}", 1, true) + } "c64scr.print_uwhex" -> { val prefix = if (args[0].asBoolean) "$" else "" val number = args[1].wordval!! dialog.canvas.printText("$prefix${number.toString(16).padStart(4, '0')}", 1, true) } + "c64scr.print_uwbin" -> { + val prefix = if (args[0].asBoolean) "%" else "" + val number = args[1].wordval!! + dialog.canvas.printText("$prefix${number.toString(2).padStart(16, '0')}", 1, true) + } + "c64scr.print_ubbin" -> { + val prefix = if (args[0].asBoolean) "%" else "" + val number = args[1].byteval!! + dialog.canvas.printText("$prefix${number.toString(2).padStart(8, '0')}", 1, true) + } "c64.CHROUT" -> { dialog.canvas.printChar(args[0].byteval!!) } @@ -445,40 +506,5 @@ class AstVm(val program: Program) { else -> TODO("syscall ${sub.scopedname} $sub") } } - - private fun setFlags(value: LiteralValue?) { - if (value != null) { - when (value.type) { - DataType.UBYTE -> { - val v = value.bytevalue!!.toInt() - P_negative = v > 127 - P_zero = v == 0 - } - DataType.BYTE -> { - val v = value.bytevalue!!.toInt() - P_negative = v < 0 - P_zero = v == 0 - } - DataType.UWORD -> { - val v = value.wordvalue!! - P_negative = v > 32767 - P_zero = v == 0 - } - DataType.WORD -> { - val v = value.wordvalue!! - P_negative = v < 0 - P_zero = v == 0 - } - DataType.FLOAT -> { - val flt = value.floatvalue!! - P_negative = flt < 0.0 - P_zero = flt == 0.0 - } - else -> { - // no flags for non-numeric type - } - } - } - } } diff --git a/compiler/src/prog8/astvm/BuiltinFunctions.kt b/compiler/src/prog8/astvm/BuiltinFunctions.kt index 62c5e404b..e98a1178d 100644 --- a/compiler/src/prog8/astvm/BuiltinFunctions.kt +++ b/compiler/src/prog8/astvm/BuiltinFunctions.kt @@ -2,23 +2,131 @@ package prog8.astvm import prog8.ast.DataType import prog8.compiler.RuntimeValue +import java.lang.Math.toDegrees +import java.lang.Math.toRadians +import java.util.* +import kotlin.math.* import kotlin.random.Random class BuiltinFunctions { private val rnd = Random(0) + private val statusFlagsSave = Stack() - fun performBuiltinFunction(name: String, args: List): RuntimeValue? { - return when(name) { + + fun performBuiltinFunction(name: String, args: List, statusflags: StatusFlags): RuntimeValue? { + return when (name) { "rnd" -> RuntimeValue(DataType.UBYTE, rnd.nextInt() and 255) "rndw" -> RuntimeValue(DataType.UWORD, rnd.nextInt() and 65535) "rndf" -> RuntimeValue(DataType.FLOAT, rnd.nextDouble()) "lsb" -> RuntimeValue(DataType.UBYTE, args[0].integerValue() and 255) "msb" -> RuntimeValue(DataType.UBYTE, (args[0].integerValue() ushr 8) and 255) + "sin" -> RuntimeValue(DataType.FLOAT, sin(args[0].numericValue().toDouble())) + "sin8" -> { + val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI + RuntimeValue(DataType.BYTE, (127.0 * sin(rad)).toShort()) + } + "sin8u" -> { + val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI + RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort()) + } + "sin16" -> { + val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI + RuntimeValue(DataType.BYTE, (32767.0 * sin(rad)).toShort()) + } + "sin16u" -> { + val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI + RuntimeValue(DataType.UBYTE, (32768.0 + 32767.5 * sin(rad)).toShort()) + } + "cos" -> RuntimeValue(DataType.FLOAT, cos(args[0].numericValue().toDouble())) + "cos8" -> { + val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI + RuntimeValue(DataType.BYTE, (127.0 * cos(rad)).toShort()) + } + "cos8u" -> { + val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI + RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort()) + } + "cos16" -> { + val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI + RuntimeValue(DataType.BYTE, (32767.0 * cos(rad)).toShort()) + } + "cos16u" -> { + val rad = args[0].numericValue().toDouble() / 256.0 * 2.0 * PI + RuntimeValue(DataType.UBYTE, (32768.0 + 32767.5 * cos(rad)).toShort()) + } + "tan" -> RuntimeValue(DataType.FLOAT, tan(args[0].numericValue().toDouble())) + "atan" -> RuntimeValue(DataType.FLOAT, atan(args[0].numericValue().toDouble())) + "ln" -> RuntimeValue(DataType.FLOAT, ln(args[0].numericValue().toDouble())) + "log2" -> RuntimeValue(DataType.FLOAT, log2(args[0].numericValue().toDouble())) + "sqrt" -> RuntimeValue(DataType.FLOAT, sqrt(args[0].numericValue().toDouble())) + "sqrt16" -> RuntimeValue(DataType.UBYTE, sqrt(args[0].wordval!!.toDouble()).toInt()) + "rad" -> RuntimeValue(DataType.FLOAT, toRadians(args[0].numericValue().toDouble())) + "deg" -> RuntimeValue(DataType.FLOAT, toDegrees(args[0].numericValue().toDouble())) + "round" -> RuntimeValue(DataType.FLOAT, round(args[0].numericValue().toDouble())) + "floor" -> RuntimeValue(DataType.FLOAT, floor(args[0].numericValue().toDouble())) + "ceil" -> RuntimeValue(DataType.FLOAT, ceil(args[0].numericValue().toDouble())) + "rol" -> { + val (result, newCarry) = args[0].rol(statusflags.carry) + statusflags.carry = newCarry + return result + } + "rol2" -> args[0].rol2() + "ror" -> { + val (result, newCarry) = args[0].ror(statusflags.carry) + statusflags.carry = newCarry + return result + } + "ror2" -> args[0].ror2() + "lsl" -> args[0].shl() + "lsr" -> args[0].shr() + "abs" -> { + when (args[0].type) { + DataType.UBYTE -> args[0] + DataType.BYTE -> RuntimeValue(DataType.UBYTE, abs(args[0].numericValue().toDouble())) + DataType.UWORD -> args[0] + DataType.WORD -> RuntimeValue(DataType.UWORD, abs(args[0].numericValue().toDouble())) + DataType.FLOAT -> RuntimeValue(DataType.FLOAT, abs(args[0].numericValue().toDouble())) + else -> TODO("strange abs type") + } + } + "max" -> { + val numbers = args.map { it.numericValue().toDouble() } + RuntimeValue(args[0].type, numbers.max()) + } + "min" -> { + val numbers = args.map { it.numericValue().toDouble() } + RuntimeValue(args[0].type, numbers.min()) + } + "avg" -> { + val numbers = args.map { it.numericValue().toDouble() } + RuntimeValue(DataType.FLOAT, numbers.average()) + } + "sum" -> { + val sum = args.map { it.numericValue().toDouble() }.sum() + when (args[0].type) { + DataType.UBYTE -> RuntimeValue(DataType.UWORD, sum) + DataType.BYTE -> RuntimeValue(DataType.WORD, sum) + DataType.UWORD -> RuntimeValue(DataType.UWORD, sum) + DataType.WORD -> RuntimeValue(DataType.WORD, sum) + DataType.FLOAT -> RuntimeValue(DataType.FLOAT, sum) + else -> TODO("weird sum type") + } + } + "any" -> { + val numbers = args.map { it.numericValue().toDouble() } + RuntimeValue(DataType.UBYTE, if (numbers.any { it != 0.0 }) 1 else 0) + } + "all" -> { + val numbers = args.map { it.numericValue().toDouble() } + RuntimeValue(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].str!!.indexOf(0.toChar()) - if(zeroIndex>=0) + if (zeroIndex >= 0) RuntimeValue(DataType.UBYTE, zeroIndex) else RuntimeValue(DataType.UBYTE, args[0].str!!.length) @@ -37,262 +145,60 @@ class BuiltinFunctions { 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 + target[i * 2] = value and 255 + target[i * 2 + 1] = value ushr 8 } null } + "memcopy" -> { + val source = args[0].array!! + val dest = args[1].array!! + val amount = args[2].integerValue() + for(i in 0 until amount) { + dest[i] = source[i] + } + null + } + "mkword" -> { + val result = (args[0].integerValue() shl 8) or args[1].integerValue() + RuntimeValue(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 + RuntimeValue(DataType.UBYTE, carry or zero or irqd or negative) + } + "rsave" -> { + statusFlagsSave.push(statusflags) + null + } + "rrestore" -> { + val flags = statusFlagsSave.pop() + statusflags.carry = flags.carry + statusflags.negative = flags.negative + statusflags.zero = flags.zero + statusflags.irqd = flags.irqd + null + } else -> TODO("builtin function $name") } } } - - -/** - - - Syscall.FUNC_LEN_STR, Syscall.FUNC_LEN_STRS -> { - val strPtr = evalstack.pop().integerValue() - val text = heap.get(strPtr).str!! - evalstack.push(RuntimeValue(DataType.UBYTE, text.length)) - } - Syscall.FUNC_STRLEN -> { - val strPtr = evalstack.pop().integerValue() - val text = heap.get(strPtr).str!! - val zeroIdx = text.indexOf('\u0000') - val len = if(zeroIdx>=0) zeroIdx else text.length - evalstack.push(RuntimeValue(DataType.UBYTE, len)) - } - Syscall.FUNC_READ_FLAGS -> { - val carry = if(P_carry) 1 else 0 - val zero = if(P_zero) 2 else 0 - val irqd = if(P_irqd) 4 else 0 - val negative = if(P_negative) 128 else 0 - val flags = carry or zero or irqd or negative - evalstack.push(RuntimeValue(DataType.UBYTE, flags)) - } - Syscall.FUNC_SIN -> evalstack.push(RuntimeValue(DataType.FLOAT, sin(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_COS -> evalstack.push(RuntimeValue(DataType.FLOAT, cos(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_SIN8 -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.BYTE, (127.0 * sin(rad)).toShort())) - } - Syscall.FUNC_SIN8U -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort())) - } - Syscall.FUNC_SIN16 -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.WORD, (32767.0 * sin(rad)).toInt())) - } - Syscall.FUNC_SIN16U -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.WORD, (32768.0 + 32767.5 * sin(rad)).toInt())) - } - Syscall.FUNC_COS8 -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.BYTE, (127.0 * cos(rad)).toShort())) - } - Syscall.FUNC_COS8U -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort())) - } - Syscall.FUNC_COS16 -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.WORD, (32767.0 * cos(rad)).toInt())) - } - Syscall.FUNC_COS16U -> { - val rad = evalstack.pop().numericValue().toDouble() /256.0 * 2.0 * PI - evalstack.push(RuntimeValue(DataType.WORD, (32768.0 + 32767.5 * cos(rad)).toInt())) - } - Syscall.FUNC_ROUND -> evalstack.push(RuntimeValue(DataType.WORD, evalstack.pop().numericValue().toDouble().roundToInt())) - Syscall.FUNC_ABS -> { - val value = evalstack.pop() - val absValue = - when (value.type) { - DataType.UBYTE -> RuntimeValue(DataType.UBYTE, value.numericValue()) - DataType.UWORD -> RuntimeValue(DataType.UWORD, value.numericValue()) - DataType.FLOAT -> RuntimeValue(DataType.FLOAT, value.numericValue()) - else -> throw VmExecutionException("cannot get abs of $value") - } - evalstack.push(absValue) - } - Syscall.FUNC_TAN -> evalstack.push(RuntimeValue(DataType.FLOAT, tan(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_ATAN -> evalstack.push(RuntimeValue(DataType.FLOAT, atan(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_LN -> evalstack.push(RuntimeValue(DataType.FLOAT, ln(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_LOG2 -> evalstack.push(RuntimeValue(DataType.FLOAT, log2(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_SQRT -> evalstack.push(RuntimeValue(DataType.FLOAT, sqrt(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_SQRT16 -> evalstack.push(RuntimeValue(DataType.UBYTE, sqrt(evalstack.pop().numericValue().toDouble()).toInt())) - Syscall.FUNC_RAD -> evalstack.push(RuntimeValue(DataType.FLOAT, Math.toRadians(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_DEG -> evalstack.push(RuntimeValue(DataType.FLOAT, Math.toDegrees(evalstack.pop().numericValue().toDouble()))) - Syscall.FUNC_FLOOR -> { - val value = evalstack.pop() - if (value.type in NumericDatatypes) - evalstack.push(RuntimeValue(DataType.FLOAT, floor(value.numericValue().toDouble()))) - else throw VmExecutionException("cannot get floor of $value") - } - Syscall.FUNC_CEIL -> { - val value = evalstack.pop() - if (value.type in NumericDatatypes) - evalstack.push(RuntimeValue(DataType.FLOAT, ceil(value.numericValue().toDouble()))) - else throw VmExecutionException("cannot get ceil of $value") - } - Syscall.FUNC_MAX_UB -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, value.array.map { it.integer!! }.max() ?: 0)) - } - Syscall.FUNC_MAX_B -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.BYTE, value.array.map { it.integer!! }.max() ?: 0)) - } - Syscall.FUNC_MAX_UW -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - if(value.array.any {it.addressOf!=null}) - throw VmExecutionException("stackvm cannot process raw memory pointers") - evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.max() ?: 0)) - } - Syscall.FUNC_MAX_W -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.WORD, value.array.map { it.integer!! }.max() ?: 0)) - } - Syscall.FUNC_MAX_F -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.doubleArray!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.FLOAT, value.doubleArray.max() ?: 0.0)) - } - Syscall.FUNC_MIN_UB -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, value.array.map { it.integer!! }.min() ?: 0)) - } - Syscall.FUNC_MIN_B -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.BYTE, value.array.map { it.integer!! }.min() ?: 0)) - } - Syscall.FUNC_MIN_UW -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - if(value.array.any {it.addressOf!=null}) - throw VmExecutionException("stackvm cannot process raw memory pointers") - evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.min() ?: 0)) - } - Syscall.FUNC_MIN_W -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.WORD, value.array.map { it.integer!! }.min() ?: 0)) - } - Syscall.FUNC_MIN_F -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.doubleArray!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.FLOAT, value.doubleArray.min() ?: 0.0)) - } - Syscall.FUNC_SUM_W, Syscall.FUNC_SUM_B -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.WORD, value.array.map { it.integer!! }.sum())) - } - Syscall.FUNC_SUM_UW -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - if(value.array.any {it.addressOf!=null}) - throw VmExecutionException("stackvm cannot process raw memory pointers") - evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.sum())) - } - Syscall.FUNC_SUM_UB -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.sum())) - } - Syscall.FUNC_SUM_F -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.doubleArray!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.FLOAT, value.doubleArray.sum())) - } - Syscall.FUNC_ANY_B, Syscall.FUNC_ANY_W -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, if (value.array.any { it.integer != 0 }) 1 else 0)) - } - Syscall.FUNC_ANY_F -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.doubleArray!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, if (value.doubleArray.any { it != 0.0 }) 1 else 0)) - } - Syscall.FUNC_ALL_B, Syscall.FUNC_ALL_W -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.array!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, if (value.array.all { it.integer != 0 }) 1 else 0)) - } - Syscall.FUNC_ALL_F -> { - val length = evalstack.pop().integerValue() - val heapVarId = evalstack.pop().integerValue() - val value = heap.get(heapVarId) - if(length!=value.doubleArray!!.size) - throw VmExecutionException("iterable length mismatch") - evalstack.push(RuntimeValue(DataType.UBYTE, if (value.doubleArray.all { it != 0.0 }) 1 else 0)) - } - Syscall.FUNC_MEMCOPY -> { - val numbytes = evalstack.pop().integerValue() - val to = evalstack.pop().integerValue() - val from = evalstack.pop().integerValue() - mem.copy(from, to, numbytes) - } - - - **/ diff --git a/compiler/src/prog8/astvm/Expressions.kt b/compiler/src/prog8/astvm/Expressions.kt index 49d259690..2671db1b4 100644 --- a/compiler/src/prog8/astvm/Expressions.kt +++ b/compiler/src/prog8/astvm/Expressions.kt @@ -5,7 +5,7 @@ import prog8.compiler.RuntimeValue import prog8.compiler.RuntimeValueRange import kotlin.math.abs -class EvalContext(val program: Program, val mem: Memory, +class EvalContext(val program: Program, val mem: Memory, val statusflags: StatusFlags, val runtimeVars: RuntimeVariables, val functions: BuiltinFunctions, val executeSubroutine: (sub: Subroutine, args: List) -> List) @@ -19,7 +19,13 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { return RuntimeValue.from(expr, ctx.program.heap) } is PrefixExpression -> { - TODO("prefixexpr $expr") + return when(expr.operator) { + "-" -> evaluate(expr.expression, ctx).neg() + "~" -> evaluate(expr.expression, ctx).inv() + "not" -> evaluate(expr.expression, ctx).not() + // unary '+' should have been optimized away + else -> TODO("prefixexpr ${expr.operator}") + } } is BinaryExpression -> { val left = evaluate(expr.left, ctx) @@ -35,6 +41,7 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { "-" -> left.sub(right) "*" -> left.mul(right) "/" -> left.div(right) + "**" -> left.pow(right) "<<" -> { var result = left repeat(right.integerValue()) {result = result.shl()} @@ -46,9 +53,12 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { result } "%" -> left.remainder(right) - "|" -> left.or(right) - "&" -> left.and(right) - "^" -> left.xor(right) + "|" -> left.bitor(right) + "&" -> left.bitand(right) + "^" -> left.bitxor(right) + "and" -> left.and(right) + "or" -> left.or(right) + "xor" -> left.xor(right) else -> TODO("binexpression operator ${expr.operator}") } } @@ -113,7 +123,8 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { results[0] } is BuiltinFunctionStatementPlaceholder -> { - val result = ctx.functions.performBuiltinFunction(sub.name, args) ?: throw VmExecutionException("expected 1 result from functioncall $expr") + val result = ctx.functions.performBuiltinFunction(sub.name, args, ctx.statusflags) + ?: throw VmExecutionException("expected 1 result from functioncall $expr") result } else -> { diff --git a/compiler/src/prog8/compiler/RuntimeValue.kt b/compiler/src/prog8/compiler/RuntimeValue.kt index d0b976b61..12a300743 100644 --- a/compiler/src/prog8/compiler/RuntimeValue.kt +++ b/compiler/src/prog8/compiler/RuntimeValue.kt @@ -407,27 +407,27 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?= fun inv(): RuntimeValue { return when(type) { - DataType.UBYTE -> RuntimeValue(DataType.UBYTE, byteval!!.toInt().inv()) - DataType.UWORD -> RuntimeValue(DataType.UWORD, wordval!!.inv()) + in ByteDatatypes -> RuntimeValue(type, byteval!!.toInt().inv()) + in WordDatatypes -> RuntimeValue(type, wordval!!.inv()) else -> throw ArithmeticException("inv can only work on byte/word") } } fun inc(): RuntimeValue { return when(type) { - DataType.UBYTE -> RuntimeValue(DataType.UBYTE, byteval!! + 1) - DataType.UWORD -> RuntimeValue(DataType.UWORD, wordval!! + 1) + in ByteDatatypes -> RuntimeValue(type, byteval!! + 1) + in WordDatatypes -> RuntimeValue(type, wordval!! + 1) DataType.FLOAT -> RuntimeValue(DataType.FLOAT, floatval!! + 1) - else -> throw ArithmeticException("inc can only work on byte/word/float") + else -> throw ArithmeticException("inc can only work on numeric types") } } fun dec(): RuntimeValue { return when(type) { - DataType.UBYTE -> RuntimeValue(DataType.UBYTE, byteval!! - 1) - DataType.UWORD -> RuntimeValue(DataType.UWORD, wordval!! - 1) + in ByteDatatypes -> RuntimeValue(type, byteval!! - 1) + in WordDatatypes -> RuntimeValue(type, wordval!! - 1) DataType.FLOAT -> RuntimeValue(DataType.FLOAT, floatval!! - 1) - else -> throw ArithmeticException("dec can only work on byte/word/float") + else -> throw ArithmeticException("dec can only work on numeric types") } } diff --git a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt index 9b3269373..18976d28f 100644 --- a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt +++ b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt @@ -327,6 +327,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap instructionsToReplace[index1] = Instruction(Opcode.NOP) } Opcode.DISCARD_WORD, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte") + Opcode.MKWORD -> {} else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}") } } diff --git a/examples/test.p8 b/examples/test.p8 index 35757d989..2aac98e3b 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,16 +6,10 @@ ~ main { sub start() { - ubyte u1 = 100 - ubyte u2 = 30 - byte bb = -30 - byte bb2 = -30 - float ff = -3.3 - word ww - - bb = (u2 as word) as byte - bb = (((u2 as word) as byte) as word) as byte + ubyte[100] arr1 + ubyte[100] arr2 + memcopy(arr1, arr2, len(arr2)) }