diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 000000000..e96534fb2
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt
index 5f4e9944f..8d2286424 100644
--- a/compiler/src/prog8/CompilerMain.kt
+++ b/compiler/src/prog8/CompilerMain.kt
@@ -1,6 +1,7 @@
package prog8
import prog8.ast.*
+import prog8.astvm.AstVm
import prog8.compiler.*
import prog8.compiler.target.c64.AsmGen
import prog8.compiler.target.c64.C64Zeropage
@@ -49,6 +50,7 @@ private fun compileMain(args: Array) {
var writeAssembly = true
var optimize = true
var optimizeInlining = true
+ var launchAstVm = false
for (arg in args) {
if(arg=="-emu")
emulatorToStart = "x64"
@@ -62,6 +64,8 @@ private fun compileMain(args: Array) {
optimize = false
else if(arg=="-nooptinline")
optimizeInlining = false
+ else if(arg=="-avm")
+ launchAstVm = true
else if(!arg.startsWith("-"))
moduleFile = arg
else
@@ -72,12 +76,13 @@ private fun compileMain(args: Array) {
val filepath = Paths.get(moduleFile).normalize()
var programname = "?"
+ lateinit var programAst: Program
try {
val totalTime = measureTimeMillis {
// import main module and everything it needs
println("Parsing...")
- val programAst = Program(moduleName(filepath.fileName), mutableListOf())
+ programAst = Program(moduleName(filepath.fileName), mutableListOf())
importModule(programAst, filepath)
val compilerOptions = determineCompilationOptions(programAst)
@@ -180,6 +185,12 @@ private fun compileMain(args: Array) {
throw x
}
+ if(launchAstVm) {
+ println("\nLaunching AST-based vm...")
+ val vm = AstVm(programAst)
+ vm.run()
+ }
+
if(emulatorToStart.isNotEmpty()) {
println("\nStarting C-64 emulator $emulatorToStart...")
val cmdline = listOf(emulatorToStart, "-silent", "-moncommands", "$programname.vice-mon-list",
@@ -233,6 +244,7 @@ private fun usage() {
System.err.println(" [-writevm] write intermediate vm code to a file as well")
System.err.println(" [-noasm] don't create assembly code")
System.err.println(" [-vm] launch the prog8 virtual machine instead of the compiler")
+ System.err.println(" [-avm] launch the prog8 ast-based virtual machine after compilation")
System.err.println(" [-noopt] don't perform any optimizations")
System.err.println(" [-nooptinline] don't perform subroutine inlining optimizations")
System.err.println(" modulefile main module file to compile")
diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt
index a723a3e08..9e57291ec 100644
--- a/compiler/src/prog8/ast/AST.kt
+++ b/compiler/src/prog8/ast/AST.kt
@@ -1597,7 +1597,7 @@ data class IdentifierReference(val nameInSource: List, override val posi
fun heapId(namespace: INameScope): Int {
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
- return ((node as? VarDecl)?.value as? LiteralValue)?.heapId ?: throw FatalAstException("identifier is not on the heap")
+ return ((node as? VarDecl)?.value as? LiteralValue)?.heapId ?: throw FatalAstException("identifier is not on the heap: $this")
}
}
diff --git a/compiler/src/prog8/astvm/AstVm.kt b/compiler/src/prog8/astvm/AstVm.kt
new file mode 100644
index 000000000..ef4a63942
--- /dev/null
+++ b/compiler/src/prog8/astvm/AstVm.kt
@@ -0,0 +1,319 @@
+package prog8.astvm
+
+import prog8.ast.*
+import java.awt.EventQueue
+
+
+class VmExecutionException(msg: String?) : Exception(msg)
+
+class VmTerminationException(msg: String?) : Exception(msg)
+
+class VmBreakpointException : Exception("breakpoint")
+
+
+class RuntimeVariables {
+ fun define(scope: INameScope, name: String, initialValue: RuntimeValue) {
+ val where = vars.getValue(scope)
+ where[name] = initialValue
+ vars[scope] = where
+ println("DEFINE RUNTIMEVAR: ${scope.name}.$name = $initialValue") // TODO
+ }
+
+ fun set(scope: INameScope, name: String, value: RuntimeValue) {
+ val where = vars.getValue(scope)
+ val existing = where[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
+ println("SET RUNTIMEVAR: ${scope.name}.$name = $value") // TODO
+ }
+
+ fun get(scope: INameScope, name: String): RuntimeValue {
+ val where = vars.getValue(scope)
+ val value = where[name]
+ if(value!=null)
+ return value
+ throw NoSuchElementException("no such runtime variable: ${scope.name}.$name")
+ }
+
+ private val vars = mutableMapOf>().withDefault { mutableMapOf() }
+}
+
+
+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
+ private var dialog = ScreenDialog()
+
+ init {
+ dialog.requestFocusInWindow()
+
+ EventQueue.invokeLater {
+ dialog.pack()
+ dialog.isVisible = true
+ dialog.start()
+ }
+ }
+
+ fun run() {
+ try {
+ val init = VariablesInitializer(runtimeVariables, program.heap)
+ init.process(program)
+ val entrypoint = program.entrypoint() ?: throw VmTerminationException("no valid entrypoint found")
+ executeSubroutine(entrypoint, emptyList())
+ println("PROGRAM EXITED!")
+ dialog.title = "PROGRAM EXITED"
+ } catch(bp: VmBreakpointException) {
+ println("Breakpoint: execution halted. Press enter to resume.")
+ readLine()
+ } catch (tx: VmTerminationException) {
+ println("Execution halted: ${tx.message}")
+ } catch (xx: VmExecutionException) {
+ println("Execution error: ${xx.message}")
+ throw xx
+ }
+ }
+
+ private val runtimeVariables = RuntimeVariables()
+
+ internal fun executeSubroutine(sub: INameScope, arguments: List): List {
+ if (sub.statements.isEmpty())
+ throw VmTerminationException("scope contains no statements: $sub")
+ if(sub is Subroutine) {
+ assert(!sub.isAsmSubroutine)
+ // TODO process arguments if it's a subroutine
+ }
+ for (s in sub.statements) {
+ if(s is Return) {
+ return s.values.map { evaluate(it, program, runtimeVariables, ::executeSubroutine) }
+ }
+ executeStatement(sub, s)
+ }
+ if(sub !is AnonymousScope)
+ throw VmTerminationException("instruction pointer overflow, is a return missing? $sub")
+ return emptyList()
+ }
+
+ private fun executeStatement(sub: INameScope, stmt: IStatement) {
+ 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 -> {
+ val target = stmt.target.targetStatement(program.namespace)
+ when(target) {
+ is Subroutine -> {
+ val args = evaluate(stmt.arglist)
+ if(target.isAsmSubroutine) {
+ performSyscall(target, args)
+ } else {
+ val results = executeSubroutine(target, args)
+ // TODO process result values
+ }
+ }
+ is BuiltinFunctionStatementPlaceholder -> {
+ val args = evaluate(stmt.arglist)
+ performBuiltinFunction(target.name, args)
+ }
+ else -> {
+ TODO("CALL $target")
+ }
+ }
+ }
+ is BuiltinFunctionStatementPlaceholder -> {
+ TODO("$stmt")
+ }
+ is Return -> {
+ throw VmExecutionException("return statement should have been handled by the subroutine loop")
+ }
+ is Continue -> {
+ TODO("$stmt")
+ }
+ is Break -> {
+ TODO("$stmt")
+ }
+ is Assignment -> {
+ if(stmt.aug_op==null) {
+ val target = stmt.singleTarget
+ if(target!=null) {
+ when {
+ target.identifier!=null -> {
+ val ident = stmt.definingScope().lookup(target.identifier.nameInSource, stmt) as VarDecl
+ val value = evaluate(stmt.value, program, runtimeVariables, ::executeSubroutine)
+ val identScope = ident.definingScope()
+ runtimeVariables.set(identScope, ident.name, value)
+ }
+ target.memoryAddress!=null -> {
+ TODO("$stmt")
+ }
+ target.arrayindexed!=null -> {
+ val array = evaluate(target.arrayindexed.identifier, program, runtimeVariables, ::executeSubroutine)
+ val index = evaluate(target.arrayindexed.arrayspec.index, program, runtimeVariables, ::executeSubroutine)
+ val value = evaluate(stmt.value, program, runtimeVariables, ::executeSubroutine)
+ 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")
+ }
+ else -> throw VmExecutionException("strange array type ${array.type}")
+ }
+ array.array!![index.integerValue()] = value.numericValue()
+ }
+ }
+ }
+ else TODO("$stmt")
+ } else TODO("$stmt")
+ }
+ is PostIncrDecr -> {
+ TODO("$stmt")
+ }
+ is Jump -> {
+ TODO("$stmt")
+ }
+ is InlineAssembly -> {
+ throw VmExecutionException("can't execute inline assembly in $sub")
+ }
+ is AnonymousScope -> {
+ throw VmExecutionException("anonymous scopes should have been flattened")
+ }
+ is IfStatement -> {
+ TODO("$stmt")
+ }
+ is BranchStatement -> {
+ TODO("$stmt")
+ }
+ is ForLoop -> {
+ TODO("$stmt")
+ }
+ is WhileLoop -> {
+ var condition = evaluate(stmt.condition, program, runtimeVariables, ::executeSubroutine)
+ while(condition.asBooleanRuntimeValue) {
+ println("STILL IN WHILE LOOP ${stmt.position}")
+ executeSubroutine(stmt.body, emptyList())
+ condition = evaluate(stmt.condition, program, runtimeVariables, ::executeSubroutine)
+ }
+ println(">>>>WHILE LOOP EXITED")
+ }
+ is RepeatLoop -> {
+ do {
+ val condition = evaluate(stmt.untilCondition, program, runtimeVariables, ::executeSubroutine)
+ executeSubroutine(stmt.body, emptyList())
+ } while(!condition.asBooleanRuntimeValue)
+ }
+ else -> {
+ TODO("implement $stmt")
+ }
+ }
+ }
+
+
+ private fun evaluate(args: List): List = args.map { evaluate(it, program, runtimeVariables, ::executeSubroutine) }
+
+ private fun performBuiltinFunction(name: String, args: List) {
+ when(name) {
+ "memset" -> {
+ val target = args[0].array!!
+ val amount = args[1].integerValue()
+ val value = args[2].integerValue()
+ for(i in 0 until amount) {
+ target[i] = value
+ }
+ }
+ else -> TODO("builtin function $name")
+ }
+ }
+
+ private fun performSyscall(sub: Subroutine, args: List) {
+ assert(sub.isAsmSubroutine)
+ 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 str = program.heap.get(args[0].wordval!!).str!!
+ dialog.canvas.printText(str, 1, true)
+ }
+ else
+ dialog.canvas.printText(args[0].str!!, 1, true)
+ }
+ "c64scr.print_ub" -> {
+ dialog.canvas.printText(args[0].byteval!!.toString(), 1, true)
+ }
+ "c64scr.print_uw" -> {
+ dialog.canvas.printText(args[0].wordval!!.toString(), 1, true)
+ }
+ "c64.CHROUT" -> {
+ dialog.canvas.printChar(args[0].byteval!!)
+ }
+ else -> TODO("syscall $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/CallStack.kt b/compiler/src/prog8/astvm/CallStack.kt
new file mode 100644
index 000000000..195e714e1
--- /dev/null
+++ b/compiler/src/prog8/astvm/CallStack.kt
@@ -0,0 +1,18 @@
+package prog8.astvm
+
+import prog8.ast.INameScope
+import java.util.*
+
+class CallStack {
+
+ private val stack = Stack>()
+
+ fun pop(): Pair {
+ return stack.pop()
+ }
+
+ fun push(scope: INameScope, index: Int) {
+ stack.push(Pair(scope, index))
+ }
+
+}
diff --git a/compiler/src/prog8/astvm/Expressions.kt b/compiler/src/prog8/astvm/Expressions.kt
new file mode 100644
index 000000000..58a52591e
--- /dev/null
+++ b/compiler/src/prog8/astvm/Expressions.kt
@@ -0,0 +1,102 @@
+package prog8.astvm
+
+import prog8.ast.*
+
+fun evaluate(expr: IExpression, program: Program, runtimeVars: RuntimeVariables,
+ executeSubroutine: (sub: Subroutine, args: List) -> List): RuntimeValue {
+ val constval = expr.constValue(program)
+ if(constval!=null)
+ return RuntimeValue.from(constval, program.heap)
+
+ when(expr) {
+ is LiteralValue -> {
+ return RuntimeValue.from(expr, program.heap)
+ }
+ is PrefixExpression -> {
+ TODO("$expr")
+ }
+ is BinaryExpression -> {
+ val left = evaluate(expr.left, program, runtimeVars, executeSubroutine)
+ val right = evaluate(expr.right, program, runtimeVars, executeSubroutine)
+ return when(expr.operator) {
+ "<" -> RuntimeValue(DataType.UBYTE, if(left < right) 1 else 0)
+ "<=" -> RuntimeValue(DataType.UBYTE, if(left <= right) 1 else 0)
+ ">" -> RuntimeValue(DataType.UBYTE, if(left > right) 1 else 0)
+ ">=" -> RuntimeValue(DataType.UBYTE, if(left >= right) 1 else 0)
+ "==" -> RuntimeValue(DataType.UBYTE, if(left == right) 1 else 0)
+ "!=" -> RuntimeValue(DataType.UBYTE, if(left != right) 1 else 0)
+ "+" -> {
+ val result = left.add(right)
+ RuntimeValue(result.type, result.numericValue())
+ }
+ "-" -> {
+ val result = left.sub(right)
+ RuntimeValue(result.type, result.numericValue())
+ }
+ else -> TODO("binexpression operator ${expr.operator}")
+ }
+ }
+ is ArrayIndexedExpression -> {
+ val array = evaluate(expr.identifier, program, runtimeVars, executeSubroutine)
+ val index = evaluate(expr.arrayspec.index, program, runtimeVars, executeSubroutine)
+ val value = array.array!![index.integerValue()]
+ return when(array.type) {
+ DataType.ARRAY_UB -> RuntimeValue(DataType.UBYTE, num=value)
+ DataType.ARRAY_B -> RuntimeValue(DataType.BYTE, num=value)
+ DataType.ARRAY_UW -> RuntimeValue(DataType.UWORD, num=value)
+ DataType.ARRAY_W -> RuntimeValue(DataType.WORD, num=value)
+ DataType.ARRAY_F -> RuntimeValue(DataType.FLOAT, num=value)
+ else -> throw VmExecutionException("strange array type ${array.type}")
+ }
+ }
+ is TypecastExpression -> {
+ return evaluate(expr.expression, program, runtimeVars, executeSubroutine).cast(expr.type)
+ }
+ is AddressOf -> {
+ // we support: address of heap var -> the heap id
+ val heapId = expr.identifier.heapId(program.namespace)
+ return RuntimeValue(DataType.UWORD, num=heapId)
+ }
+ is DirectMemoryRead -> {
+ TODO("$expr")
+
+ }
+ is DirectMemoryWrite -> {
+ TODO("$expr")
+
+ }
+ is RegisterExpr -> {
+ TODO("$expr")
+ }
+ is IdentifierReference -> {
+ val scope = expr.definingScope()
+ val variable = scope.lookup(expr.nameInSource, expr)
+ if(variable is VarDecl) {
+ val stmt = scope.lookup(listOf(variable.name), expr)!!
+ return runtimeVars.get(stmt.definingScope(), variable.name)
+ } else
+ TODO("$variable")
+ }
+ is FunctionCall -> {
+ val sub = expr.target.targetStatement(program.namespace)
+ val args = expr.arglist.map { evaluate(it, program, runtimeVars, executeSubroutine) }
+ when(sub) {
+ is Subroutine -> {
+ val results = executeSubroutine(sub, args)
+ if(results.size!=1)
+ throw VmExecutionException("expected 1 result from functioncall $expr")
+ return results[0]
+ }
+ else -> {
+ TODO("call expr function ${expr.target}")
+ }
+ }
+ }
+ is RangeExpr -> {
+ TODO("eval range $expr")
+ }
+ else -> {
+ TODO("implement eval $expr")
+ }
+ }
+}
diff --git a/compiler/src/prog8/astvm/Memory.kt b/compiler/src/prog8/astvm/Memory.kt
new file mode 100644
index 000000000..b6050b315
--- /dev/null
+++ b/compiler/src/prog8/astvm/Memory.kt
@@ -0,0 +1,102 @@
+package prog8.astvm
+
+import prog8.compiler.target.c64.Mflpt5
+import prog8.compiler.target.c64.Petscii
+import kotlin.math.abs
+
+class Memory {
+ private val mem = ShortArray(65536) // shorts because byte is signed and we store values 0..255
+
+ fun getUByte(address: Int): Short {
+ return mem[address]
+ }
+
+ fun getSByte(address: Int): Short {
+ val ubyte = getUByte(address)
+ if(ubyte <= 127)
+ return ubyte
+ return (-((ubyte.toInt() xor 255)+1)).toShort() // 2's complement
+ }
+
+ fun setUByte(address: Int, value: Short) {
+ if(value !in 0..255)
+ throw VmExecutionException("ubyte value out of range")
+ mem[address] = value
+ }
+
+ fun setSByte(address: Int, value: Short) {
+ if(value !in -128..127) throw VmExecutionException("byte value out of range")
+ if(value>=0)
+ mem[address] = value
+ else
+ mem[address] = ((abs(value.toInt()) xor 255)+1).toShort() // 2's complement
+ }
+
+ fun getUWord(address: Int): Int {
+ return mem[address] + 256*mem[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")
+ mem[address] = value.and(255).toShort()
+ mem[address+1] = (value / 256).toShort()
+ }
+
+ fun setSWord(address: Int, value: Int) {
+ if(value !in -32768..32767) throw VmExecutionException("word value out of range")
+ if(value>=0)
+ setUWord(address, value)
+ else
+ setUWord(address, (abs(value) xor 65535)+1) // 2's complement
+ }
+
+ fun setFloat(address: Int, value: Double) {
+ val mflpt5 = Mflpt5.fromNumber(value)
+ mem[address] = mflpt5.b0
+ mem[address+1] = mflpt5.b1
+ mem[address+2] = mflpt5.b2
+ mem[address+3] = mflpt5.b3
+ mem[address+4] = mflpt5.b4
+ }
+
+ fun getFloat(address: Int): Double {
+ return Mflpt5(mem[address], mem[address + 1], mem[address + 2], mem[address + 3], mem[address + 4]).toDouble()
+ }
+
+ fun setString(address: Int, str: String) {
+ // lowercase PETSCII
+ val petscii = Petscii.encodePetscii(str, true)
+ var addr = address
+ for (c in petscii) mem[addr++] = c
+ mem[addr] = 0
+ }
+
+ fun getString(strAddress: Int): String {
+ // lowercase PETSCII
+ val petscii = mutableListOf()
+ var addr = strAddress
+ while(true) {
+ val byte = mem[addr++]
+ if(byte==0.toShort()) break
+ petscii.add(byte)
+ }
+ return Petscii.decodePetscii(petscii, true)
+ }
+
+ fun clear() {
+ for(i in 0..65535) mem[i]=0
+ }
+
+ fun copy(from: Int, to: Int, numbytes: Int) {
+ for(i in 0 until numbytes)
+ mem[to+i] = mem[from+i]
+ }
+}
diff --git a/compiler/src/prog8/astvm/RuntimeValue.kt b/compiler/src/prog8/astvm/RuntimeValue.kt
new file mode 100644
index 000000000..165c9b3d1
--- /dev/null
+++ b/compiler/src/prog8/astvm/RuntimeValue.kt
@@ -0,0 +1,516 @@
+package prog8.astvm
+
+import prog8.ast.*
+import prog8.compiler.HeapValues
+import kotlin.math.abs
+import kotlin.math.pow
+
+
+class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=null, val array: Array?=null, val heapId: Int?=null) {
+
+ val byteval: Short?
+ val wordval: Int?
+ val floatval: Double?
+ val asBooleanRuntimeValue: Boolean
+
+ companion object {
+ fun from(literalValue: LiteralValue, heap: HeapValues): RuntimeValue {
+ return when(literalValue.type) {
+ in NumericDatatypes -> RuntimeValue(literalValue.type, num = literalValue.asNumericValue!!)
+ in StringDatatypes -> from(literalValue.heapId!!, heap)
+ in ArrayDatatypes -> from(literalValue.heapId!!, heap)
+ else -> TODO("type")
+ }
+ }
+
+ fun from(heapId: Int, heap: HeapValues): RuntimeValue {
+ val value = heap.get(heapId)
+ return when {
+ value.type in StringDatatypes ->
+ RuntimeValue(value.type, str = value.str!!, heapId = heapId)
+ value.type in ArrayDatatypes ->
+ if (value.type == DataType.ARRAY_F) {
+ RuntimeValue(value.type, array = value.doubleArray!!.toList().toTypedArray(), heapId = heapId)
+ } else {
+ val array = value.array!!
+ if (array.any { it.addressOf != null })
+ TODO("addressof values")
+ RuntimeValue(value.type, array = array.map { it.integer!! }.toTypedArray(), heapId = heapId)
+ }
+ else -> TODO("weird type on heap")
+ }
+ }
+
+ }
+
+ init {
+ when(type) {
+ DataType.UBYTE -> {
+ byteval = num!!.toShort()
+ if(byteval !in 0..255)
+ throw ArithmeticException("value out of range: $num")
+ wordval = null
+ floatval = null
+ asBooleanRuntimeValue = num != 0
+ }
+ DataType.BYTE -> {
+ byteval = num!!.toShort()
+ if(byteval !in -128..127)
+ throw ArithmeticException("value out of range: $num")
+ wordval = null
+ floatval = null
+ asBooleanRuntimeValue = num != 0
+ }
+ DataType.UWORD -> {
+ wordval = num!!.toInt()
+ if(wordval !in 0..65535)
+ throw ArithmeticException("value out of range: $num")
+ byteval = null
+ floatval = null
+ asBooleanRuntimeValue = wordval != 0
+ }
+ DataType.WORD -> {
+ wordval = num!!.toInt()
+ if(wordval !in -32768..32767)
+ throw ArithmeticException("value out of range: $num")
+ byteval = null
+ floatval = null
+ asBooleanRuntimeValue = wordval != 0
+ }
+ DataType.FLOAT -> {
+ floatval = num!!.toDouble()
+ byteval = null
+ wordval = null
+ asBooleanRuntimeValue = floatval != 0.0
+ }
+ else -> {
+ if(heapId==null)
+ throw ArithmeticException("for non-numeric types, a heapId should be given")
+ byteval = null
+ wordval = null
+ floatval = null
+ asBooleanRuntimeValue = true
+ }
+ }
+ }
+
+ 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 -> "heap:$heapId"
+ }
+ }
+
+ fun numericValue(): Number {
+ return when(type) {
+ in ByteDatatypes -> byteval!!
+ in WordDatatypes -> wordval!!
+ DataType.FLOAT -> floatval!!
+ else -> throw ArithmeticException("invalid datatype for numeric value: $type")
+ }
+ }
+
+ 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 {
+ val bh = byteval?.hashCode() ?: 0x10001234
+ val wh = wordval?.hashCode() ?: 0x01002345
+ val fh = floatval?.hashCode() ?: 0x00103456
+ return bh xor wh xor fh xor heapId.hashCode() xor type.hashCode()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if(other==null || other !is RuntimeValue)
+ return false
+ if(type==other.type)
+ return if (type in IterableDatatypes) heapId==other.heapId else compareTo(other)==0
+ return compareTo(other)==0 // note: datatype doesn't matter
+ }
+
+ operator fun compareTo(other: RuntimeValue): Int {
+ return if (type in NumericDatatypes && other.type in NumericDatatypes)
+ numericValue().toDouble().compareTo(other.numericValue().toDouble())
+ else throw ArithmeticException("comparison can only be done between two numeric values")
+ }
+
+ private fun arithResult(leftDt: DataType, result: Number, rightDt: DataType, op: String): RuntimeValue {
+ 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)
+ RuntimeValue(DataType.UBYTE, (number xor 255) + 1)
+ else
+ RuntimeValue(DataType.UBYTE, (number xor 65535) + 1)
+ }
+ DataType.BYTE -> RuntimeValue(DataType.BYTE, result.toInt())
+ DataType.WORD -> RuntimeValue(DataType.WORD, result.toInt())
+ DataType.FLOAT -> RuntimeValue(DataType.FLOAT, result)
+ else -> throw ArithmeticException("$op on non-numeric type")
+ }
+ }
+
+ return when(leftDt) {
+ DataType.UBYTE -> RuntimeValue(DataType.UBYTE, result.toInt() and 255)
+ DataType.BYTE -> RuntimeValue(DataType.BYTE, result.toInt())
+ DataType.UWORD -> RuntimeValue(DataType.UWORD, result.toInt() and 65535)
+ DataType.WORD -> RuntimeValue(DataType.WORD, result.toInt())
+ DataType.FLOAT -> RuntimeValue(DataType.FLOAT, result)
+ else -> throw ArithmeticException("$op on non-numeric type")
+ }
+ }
+
+ fun add(other: RuntimeValue): RuntimeValue {
+ 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: RuntimeValue): RuntimeValue {
+ 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: RuntimeValue): RuntimeValue {
+ 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: RuntimeValue): RuntimeValue {
+ 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 RuntimeValue(DataType.UBYTE, 255)
+ DataType.BYTE -> return RuntimeValue(DataType.BYTE, 127)
+ DataType.UWORD -> return RuntimeValue(DataType.UWORD, 65535)
+ DataType.WORD -> return RuntimeValue(DataType.WORD, 32767)
+ else -> {}
+ }
+ }
+ val result = v1.toDouble() / v2.toDouble()
+ // NOTE: integer division returns integer result!
+ return when(type) {
+ DataType.UBYTE -> RuntimeValue(DataType.UBYTE, result)
+ DataType.BYTE -> RuntimeValue(DataType.BYTE, result)
+ DataType.UWORD -> RuntimeValue(DataType.UWORD, result)
+ DataType.WORD -> RuntimeValue(DataType.WORD, result)
+ DataType.FLOAT -> RuntimeValue(DataType.FLOAT, result)
+ else -> throw ArithmeticException("div on non-numeric type")
+ }
+ }
+
+ fun remainder(other: RuntimeValue): RuntimeValue? {
+ val v1 = numericValue()
+ val v2 = other.numericValue()
+ val result = v1.toDouble() % v2.toDouble()
+ return arithResult(type, result, other.type, "remainder")
+ }
+
+ fun pow(other: RuntimeValue): RuntimeValue {
+ val v1 = numericValue()
+ val v2 = other.numericValue()
+ val result = v1.toDouble().pow(v2.toDouble())
+ return arithResult(type, result, other.type,"pow")
+ }
+
+ fun shl(): RuntimeValue {
+ val v = integerValue()
+ return when (type) {
+ DataType.UBYTE -> return RuntimeValue(type, (v shl 1) and 255)
+ DataType.BYTE -> {
+ if(v<0)
+ RuntimeValue(type, -((-v shl 1) and 255))
+ else
+ RuntimeValue(type, (v shl 1) and 255)
+ }
+ DataType.UWORD -> return RuntimeValue(type, (v shl 1) and 65535)
+ DataType.WORD -> {
+ if(v<0)
+ RuntimeValue(type, -((-v shl 1) and 65535))
+ else
+ RuntimeValue(type, (v shl 1) and 65535)
+ }
+ else -> throw ArithmeticException("invalid type for shl: $type")
+ }
+ }
+
+ fun shr(): RuntimeValue {
+ val v = integerValue()
+ return when(type){
+ DataType.UBYTE -> RuntimeValue(type, (v ushr 1) and 255)
+ DataType.BYTE -> RuntimeValue(type, v shr 1)
+ DataType.UWORD -> RuntimeValue(type, (v ushr 1) and 65535)
+ DataType.WORD -> RuntimeValue(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 -> {
+ val v = byteval!!.toInt()
+ val newCarry = (v and 0x80) != 0
+ val newval = (v and 0x7f shl 1) or (if(carry) 1 else 0)
+ Pair(RuntimeValue(DataType.UBYTE, newval), newCarry)
+ }
+ DataType.UWORD -> {
+ val v = wordval!!
+ val newCarry = (v and 0x8000) != 0
+ val newval = (v and 0x7fff shl 1) or (if(carry) 1 else 0)
+ Pair(RuntimeValue(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 -> {
+ val v = byteval!!.toInt()
+ val newCarry = v and 1 != 0
+ val newval = (v ushr 1) or (if(carry) 0x80 else 0)
+ Pair(RuntimeValue(DataType.UBYTE, newval), newCarry)
+ }
+ DataType.UWORD -> {
+ val v = wordval!!
+ val newCarry = v and 1 != 0
+ val newval = (v ushr 1) or (if(carry) 0x8000 else 0)
+ Pair(RuntimeValue(DataType.UWORD, newval), newCarry)
+ }
+ else -> throw ArithmeticException("ror2 can only work on byte/word")
+ }
+ }
+
+ fun rol2(): RuntimeValue {
+ // 8 or 16 bit rotate left
+ return when(type) {
+ DataType.UBYTE -> {
+ val v = byteval!!.toInt()
+ val carry = (v and 0x80) ushr 7
+ val newval = (v and 0x7f shl 1) or carry
+ RuntimeValue(DataType.UBYTE, newval)
+ }
+ DataType.UWORD -> {
+ val v = wordval!!
+ val carry = (v and 0x8000) ushr 15
+ val newval = (v and 0x7fff shl 1) or carry
+ RuntimeValue(DataType.UWORD, newval)
+ }
+ else -> throw ArithmeticException("rol2 can only work on byte/word")
+ }
+ }
+
+ fun ror2(): RuntimeValue {
+ // 8 or 16 bit rotate right
+ return when(type) {
+ DataType.UBYTE -> {
+ val v = byteval!!.toInt()
+ val carry = v and 1 shl 7
+ val newval = (v ushr 1) or carry
+ RuntimeValue(DataType.UBYTE, newval)
+ }
+ DataType.UWORD -> {
+ val v = wordval!!
+ val carry = v and 1 shl 15
+ val newval = (v ushr 1) or carry
+ RuntimeValue(DataType.UWORD, newval)
+ }
+ else -> throw ArithmeticException("ror2 can only work on byte/word")
+ }
+ }
+
+ fun neg(): RuntimeValue {
+ return when(type) {
+ DataType.BYTE -> RuntimeValue(DataType.BYTE, -(byteval!!))
+ DataType.WORD -> RuntimeValue(DataType.WORD, -(wordval!!))
+ DataType.FLOAT -> RuntimeValue(DataType.FLOAT, -(floatval)!!)
+ else -> throw ArithmeticException("neg can only work on byte/word/float")
+ }
+ }
+
+ fun abs(): RuntimeValue {
+ return when(type) {
+ DataType.BYTE -> RuntimeValue(DataType.BYTE, abs(byteval!!.toInt()))
+ DataType.WORD -> RuntimeValue(DataType.WORD, abs(wordval!!))
+ DataType.FLOAT -> RuntimeValue(DataType.FLOAT, abs(floatval!!))
+ else -> throw ArithmeticException("abs can only work on byte/word/float")
+ }
+ }
+
+ fun bitand(other: RuntimeValue): RuntimeValue {
+ val v1 = integerValue()
+ val v2 = other.integerValue()
+ val result = v1 and v2
+ return RuntimeValue(type, result)
+ }
+
+ fun bitor(other: RuntimeValue): RuntimeValue {
+ val v1 = integerValue()
+ val v2 = other.integerValue()
+ val result = v1 or v2
+ return RuntimeValue(type, result)
+ }
+
+ fun bitxor(other: RuntimeValue): RuntimeValue {
+ val v1 = integerValue()
+ val v2 = other.integerValue()
+ val result = v1 xor v2
+ return RuntimeValue(type, result)
+ }
+
+ fun and(other: RuntimeValue) = RuntimeValue(DataType.UBYTE, if (this.asBooleanRuntimeValue && other.asBooleanRuntimeValue) 1 else 0)
+ fun or(other: RuntimeValue) = RuntimeValue(DataType.UBYTE, if (this.asBooleanRuntimeValue || other.asBooleanRuntimeValue) 1 else 0)
+ fun xor(other: RuntimeValue) = RuntimeValue(DataType.UBYTE, if (this.asBooleanRuntimeValue xor other.asBooleanRuntimeValue) 1 else 0)
+ fun not() = RuntimeValue(DataType.UBYTE, if (this.asBooleanRuntimeValue) 0 else 1)
+
+ fun inv(): RuntimeValue {
+ return when(type) {
+ DataType.UBYTE -> RuntimeValue(DataType.UBYTE, byteval!!.toInt().inv() and 255)
+ DataType.UWORD -> RuntimeValue(DataType.UWORD, wordval!!.inv() and 65535)
+ else -> throw ArithmeticException("inv can only work on byte/word")
+ }
+ }
+
+ fun inc(): RuntimeValue {
+ return when(type) {
+ DataType.UBYTE -> RuntimeValue(DataType.UBYTE, (byteval!! + 1) and 255)
+ DataType.UWORD -> RuntimeValue(DataType.UWORD, (wordval!! + 1) and 65535)
+ DataType.FLOAT -> RuntimeValue(DataType.FLOAT, floatval!! + 1)
+ else -> throw ArithmeticException("inc can only work on byte/word/float")
+ }
+ }
+
+ fun dec(): RuntimeValue {
+ return when(type) {
+ DataType.UBYTE -> RuntimeValue(DataType.UBYTE, (byteval!! - 1) and 255)
+ DataType.UWORD -> RuntimeValue(DataType.UWORD, (wordval!! - 1) and 65535)
+ DataType.FLOAT -> RuntimeValue(DataType.FLOAT, floatval!! - 1)
+ else -> throw ArithmeticException("dec can only work on byte/word/float")
+ }
+ }
+
+ fun msb(): RuntimeValue {
+ return when(type) {
+ in ByteDatatypes -> RuntimeValue(DataType.UBYTE, 0)
+ in WordDatatypes -> RuntimeValue(DataType.UBYTE, wordval!! ushr 8 and 255)
+ else -> throw ArithmeticException("msb can only work on (u)byte/(u)word")
+ }
+ }
+
+ fun cast(targetType: DataType): RuntimeValue {
+ return when (type) {
+ DataType.UBYTE -> {
+ when (targetType) {
+ DataType.UBYTE -> this
+ DataType.BYTE -> {
+ if(byteval!!<=127)
+ RuntimeValue(DataType.BYTE, byteval!!)
+ else
+ RuntimeValue(DataType.BYTE, -(256-byteval!!))
+ }
+ DataType.UWORD -> RuntimeValue(DataType.UWORD, numericValue())
+ DataType.WORD -> RuntimeValue(DataType.WORD, numericValue())
+ DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
+ else -> throw ArithmeticException("invalid type cast from $type to $targetType")
+ }
+ }
+ DataType.BYTE -> {
+ when (targetType) {
+ DataType.BYTE -> this
+ DataType.UBYTE -> RuntimeValue(DataType.UBYTE, integerValue() and 255)
+ DataType.UWORD -> RuntimeValue(DataType.UWORD, integerValue() and 65535)
+ DataType.WORD -> RuntimeValue(DataType.WORD, integerValue())
+ DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
+ else -> throw ArithmeticException("invalid type cast from $type to $targetType")
+ }
+ }
+ DataType.UWORD -> {
+ when (targetType) {
+ in ByteDatatypes -> RuntimeValue(DataType.UBYTE, integerValue() and 255)
+ DataType.UWORD -> this
+ DataType.WORD -> {
+ if(integerValue()<=32767)
+ RuntimeValue(DataType.WORD, integerValue())
+ else
+ RuntimeValue(DataType.WORD, -(65536-integerValue()))
+ }
+ DataType.FLOAT -> RuntimeValue(DataType.FLOAT, numericValue())
+ else -> throw ArithmeticException("invalid type cast from $type to $targetType")
+ }
+ }
+ DataType.WORD -> {
+ when (targetType) {
+ in ByteDatatypes -> RuntimeValue(DataType.UBYTE, integerValue() and 255)
+ DataType.UWORD -> RuntimeValue(DataType.UWORD, integerValue() and 65535)
+ DataType.WORD -> this
+ DataType.FLOAT -> RuntimeValue(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)
+ RuntimeValue(DataType.BYTE, integer)
+ else
+ throw ArithmeticException("overflow when casting float to byte: $this")
+ }
+ DataType.UBYTE -> RuntimeValue(DataType.UBYTE, numericValue().toInt() and 255)
+ DataType.UWORD -> RuntimeValue(DataType.UWORD, numericValue().toInt() and 65535)
+ DataType.WORD -> {
+ val integer=numericValue().toInt()
+ if(integer in -32768..32767)
+ RuntimeValue(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")
+ }
+ }
+
+}
diff --git a/compiler/src/prog8/astvm/ScreenDialog.kt b/compiler/src/prog8/astvm/ScreenDialog.kt
new file mode 100644
index 000000000..e84140f7a
--- /dev/null
+++ b/compiler/src/prog8/astvm/ScreenDialog.kt
@@ -0,0 +1,209 @@
+package prog8.astvm
+
+import prog8.compiler.target.c64.Charset
+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 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
+
+ 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?) {}
+
+ override fun keyPressed(p0: KeyEvent?) {
+ println("pressed: $p0.k")
+ }
+
+ override fun keyReleased(p0: KeyEvent?) {
+ println("released: $p0")
+ }
+
+ 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: Int) {
+ g2d.background = palette[color and 15]
+ g2d.clearRect(0, 0, BitmapScreenPanel.SCREENWIDTH, BitmapScreenPanel.SCREENHEIGHT)
+ cursorX = 0
+ cursorY = 0
+ }
+ fun setPixel(x: Int, y: Int, color: Int) {
+ image.setRGB(x, y, palette[color and 15].rgb)
+ }
+ fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Int) {
+ g2d.color = palette[color and 15]
+ g2d.drawLine(x1, y1, x2, y2)
+ }
+ fun printText(text: String, color: Int, lowercase: Boolean) {
+ val lines = text.split('\n')
+ for(line in lines.withIndex()) {
+ printTextSingleLine(line.value, color, lowercase)
+ if(line.index=(SCREENWIDTH/8)) {
+ cursorY++
+ cursorX=0
+ }
+ }
+ }
+
+ fun printChar(char: Short) {
+ if(char==13.toShort() || char==141.toShort()) {
+ cursorX=0
+ cursorY++
+ } else {
+ setChar(cursorX, cursorY, char)
+ cursorX++
+ if (cursorX >= (SCREENWIDTH / 8)) {
+ cursorY++
+ cursorX = 0
+ }
+ }
+ }
+
+ fun setChar(x: Int, y: Int, screenCode: Short) {
+ g2d.clearRect(8*x, 8*y, 8, 8)
+ g2d.drawImage(Charset.shiftedChars[screenCode.toInt()], 8*x, 8*y , null)
+ }
+
+ fun setCursorPos(x: Int, y: Int) {
+ cursorX = x
+ cursorY = y
+ }
+
+ fun getCursorPos(): Pair {
+ return Pair(cursorX, cursorY)
+ }
+
+ fun writeText(x: Int, y: Int, text: String, color: Int, lowercase: Boolean) {
+ var xx=x
+ if(color!=1) {
+ TODO("text can only be white for now")
+ }
+ for(clearx in xx until xx+text.length) {
+ g2d.clearRect(8*clearx, 8*y, 8, 8)
+ }
+ for(sc in Petscii.encodeScreencode(text, lowercase)) {
+ setChar(xx++, y, sc)
+ }
+ }
+
+
+ companion object {
+ const val SCREENWIDTH = 320
+ const val SCREENHEIGHT = 200
+ const val SCALING = 3
+ val palette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
+ Color(0x000000), // 0 = black
+ Color(0xFFFFFF), // 1 = white
+ Color(0x813338), // 2 = red
+ Color(0x75cec8), // 3 = cyan
+ Color(0x8e3c97), // 4 = purple
+ Color(0x56ac4d), // 5 = green
+ Color(0x2e2c9b), // 6 = blue
+ Color(0xedf171), // 7 = yellow
+ Color(0x8e5029), // 8 = orange
+ Color(0x553800), // 9 = brown
+ Color(0xc46c71), // 10 = light red
+ Color(0x4a4a4a), // 11 = dark grey
+ Color(0x7b7b7b), // 12 = medium grey
+ Color(0xa9ff9f), // 13 = light green
+ Color(0x706deb), // 14 = light blue
+ Color(0xb2b2b2) // 15 = light grey
+ )
+ }
+}
+
+
+class ScreenDialog : JFrame() {
+ val canvas = BitmapScreenPanel()
+
+ init {
+ val borderWidth = 16
+ title = "AstVm graphics. Text I/O goes to console."
+ layout = GridBagLayout()
+ defaultCloseOperation = JFrame.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 = BitmapScreenPanel.palette[14]
+ }
+ val borderBottom = JPanel().apply {
+ preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
+ background = BitmapScreenPanel.palette[14]
+ }
+ val borderLeft = JPanel().apply {
+ preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
+ background = BitmapScreenPanel.palette[14]
+ }
+ val borderRight = JPanel().apply {
+ preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
+ background = BitmapScreenPanel.palette[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()
+ }
+}
diff --git a/compiler/src/prog8/astvm/VariablesInitializer.kt b/compiler/src/prog8/astvm/VariablesInitializer.kt
new file mode 100644
index 000000000..34b27d317
--- /dev/null
+++ b/compiler/src/prog8/astvm/VariablesInitializer.kt
@@ -0,0 +1,39 @@
+package prog8.astvm
+
+import prog8.ast.*
+import prog8.compiler.HeapValues
+
+class VariablesInitializer(private val runtimeVariables: RuntimeVariables, private val heap: HeapValues) : IAstProcessor {
+
+ override fun process(decl: VarDecl): IStatement {
+ if(decl.type==VarDeclType.VAR) {
+ val value = when (decl.datatype) {
+ in NumericDatatypes -> {
+ if(decl.value !is LiteralValue) {
+ TODO("evaluate vardecl expression $decl")
+ //RuntimeValue(decl.datatype, num = evaluate(decl.value!!, program, runtimeVariables, executeSubroutine).numericValue())
+ } else {
+ RuntimeValue.from(decl.value as LiteralValue, heap)
+ }
+ }
+ in StringDatatypes -> {
+ RuntimeValue.from(decl.value as LiteralValue, heap)
+ }
+ in ArrayDatatypes -> {
+ RuntimeValue.from(decl.value as LiteralValue, heap)
+ }
+ else -> throw VmExecutionException("weird type ${decl.datatype}")
+ }
+ runtimeVariables.define(decl.definingScope(), decl.name, value)
+ }
+ return super.process(decl)
+ }
+
+// override fun process(assignment: Assignment): IStatement {
+// if(assignment is VariableInitializationAssignment) {
+// println("INIT VAR $assignment")
+// }
+// return super.process(assignment)
+// }
+
+}
diff --git a/examples/primes.p8 b/examples/primes.p8
index f89672ed3..62a106495 100644
--- a/examples/primes.p8
+++ b/examples/primes.p8
@@ -37,10 +37,13 @@
; found next one, mark the multiples and return it.
sieve[candidate_prime] = true
- uword multiple = candidate_prime
+ ; uword multiple = candidate_prime ; TODO the parser should have added a type cast from UBYTE to UWORD here
+ uword multiple = candidate_prime as uword ; TODO should not be required
while multiple < len(sieve) {
sieve[lsb(multiple)] = true
- multiple += candidate_prime
+ multiple += candidate_prime as uword ; TODO cast should not be required
+ c64scr.print_uw(multiple) ; TODO
+ c64.CHROUT('\n') ; TODO
}
return candidate_prime
}