mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 16:29:21 +00:00
first go at ast-based virtual machine (rather than the stackvm that uses intermediate code)
This commit is contained in:
parent
99121004bf
commit
e53c860f1a
124
.idea/uiDesigner.xml
generated
Normal file
124
.idea/uiDesigner.xml
generated
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Palette2">
|
||||||
|
<group name="Swing">
|
||||||
|
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Button" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="RadioButton" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="CheckBox" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Label" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||||
|
<preferred-size width="-1" height="20" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
</group>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -1,6 +1,7 @@
|
|||||||
package prog8
|
package prog8
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
|
import prog8.astvm.AstVm
|
||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
import prog8.compiler.target.c64.AsmGen
|
import prog8.compiler.target.c64.AsmGen
|
||||||
import prog8.compiler.target.c64.C64Zeropage
|
import prog8.compiler.target.c64.C64Zeropage
|
||||||
@ -49,6 +50,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
var writeAssembly = true
|
var writeAssembly = true
|
||||||
var optimize = true
|
var optimize = true
|
||||||
var optimizeInlining = true
|
var optimizeInlining = true
|
||||||
|
var launchAstVm = false
|
||||||
for (arg in args) {
|
for (arg in args) {
|
||||||
if(arg=="-emu")
|
if(arg=="-emu")
|
||||||
emulatorToStart = "x64"
|
emulatorToStart = "x64"
|
||||||
@ -62,6 +64,8 @@ private fun compileMain(args: Array<String>) {
|
|||||||
optimize = false
|
optimize = false
|
||||||
else if(arg=="-nooptinline")
|
else if(arg=="-nooptinline")
|
||||||
optimizeInlining = false
|
optimizeInlining = false
|
||||||
|
else if(arg=="-avm")
|
||||||
|
launchAstVm = true
|
||||||
else if(!arg.startsWith("-"))
|
else if(!arg.startsWith("-"))
|
||||||
moduleFile = arg
|
moduleFile = arg
|
||||||
else
|
else
|
||||||
@ -72,12 +76,13 @@ private fun compileMain(args: Array<String>) {
|
|||||||
|
|
||||||
val filepath = Paths.get(moduleFile).normalize()
|
val filepath = Paths.get(moduleFile).normalize()
|
||||||
var programname = "?"
|
var programname = "?"
|
||||||
|
lateinit var programAst: Program
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val totalTime = measureTimeMillis {
|
val totalTime = measureTimeMillis {
|
||||||
// import main module and everything it needs
|
// import main module and everything it needs
|
||||||
println("Parsing...")
|
println("Parsing...")
|
||||||
val programAst = Program(moduleName(filepath.fileName), mutableListOf())
|
programAst = Program(moduleName(filepath.fileName), mutableListOf())
|
||||||
importModule(programAst, filepath)
|
importModule(programAst, filepath)
|
||||||
|
|
||||||
val compilerOptions = determineCompilationOptions(programAst)
|
val compilerOptions = determineCompilationOptions(programAst)
|
||||||
@ -180,6 +185,12 @@ private fun compileMain(args: Array<String>) {
|
|||||||
throw x
|
throw x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(launchAstVm) {
|
||||||
|
println("\nLaunching AST-based vm...")
|
||||||
|
val vm = AstVm(programAst)
|
||||||
|
vm.run()
|
||||||
|
}
|
||||||
|
|
||||||
if(emulatorToStart.isNotEmpty()) {
|
if(emulatorToStart.isNotEmpty()) {
|
||||||
println("\nStarting C-64 emulator $emulatorToStart...")
|
println("\nStarting C-64 emulator $emulatorToStart...")
|
||||||
val cmdline = listOf(emulatorToStart, "-silent", "-moncommands", "$programname.vice-mon-list",
|
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(" [-writevm] write intermediate vm code to a file as well")
|
||||||
System.err.println(" [-noasm] don't create assembly code")
|
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(" [-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(" [-noopt] don't perform any optimizations")
|
||||||
System.err.println(" [-nooptinline] don't perform subroutine inlining optimizations")
|
System.err.println(" [-nooptinline] don't perform subroutine inlining optimizations")
|
||||||
System.err.println(" modulefile main module file to compile")
|
System.err.println(" modulefile main module file to compile")
|
||||||
|
@ -1597,7 +1597,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
|
|
||||||
fun heapId(namespace: INameScope): Int {
|
fun heapId(namespace: INameScope): Int {
|
||||||
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
319
compiler/src/prog8/astvm/AstVm.kt
Normal file
319
compiler/src/prog8/astvm/AstVm.kt
Normal file
@ -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<INameScope, MutableMap<String, RuntimeValue>>().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<RuntimeValue>): List<RuntimeValue> {
|
||||||
|
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<IExpression>): List<RuntimeValue> = args.map { evaluate(it, program, runtimeVariables, ::executeSubroutine) }
|
||||||
|
|
||||||
|
private fun performBuiltinFunction(name: String, args: List<RuntimeValue>) {
|
||||||
|
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<RuntimeValue>) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
compiler/src/prog8/astvm/CallStack.kt
Normal file
18
compiler/src/prog8/astvm/CallStack.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package prog8.astvm
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class CallStack {
|
||||||
|
|
||||||
|
private val stack = Stack<Pair<INameScope, Int>>()
|
||||||
|
|
||||||
|
fun pop(): Pair<INameScope, Int> {
|
||||||
|
return stack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun push(scope: INameScope, index: Int) {
|
||||||
|
stack.push(Pair(scope, index))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
102
compiler/src/prog8/astvm/Expressions.kt
Normal file
102
compiler/src/prog8/astvm/Expressions.kt
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package prog8.astvm
|
||||||
|
|
||||||
|
import prog8.ast.*
|
||||||
|
|
||||||
|
fun evaluate(expr: IExpression, program: Program, runtimeVars: RuntimeVariables,
|
||||||
|
executeSubroutine: (sub: Subroutine, args: List<RuntimeValue>) -> List<RuntimeValue>): 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
compiler/src/prog8/astvm/Memory.kt
Normal file
102
compiler/src/prog8/astvm/Memory.kt
Normal file
@ -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<Short>()
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
}
|
516
compiler/src/prog8/astvm/RuntimeValue.kt
Normal file
516
compiler/src/prog8/astvm/RuntimeValue.kt
Normal file
@ -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<Number>?=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<RuntimeValue, Boolean> {
|
||||||
|
// 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<RuntimeValue, Boolean> {
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
209
compiler/src/prog8/astvm/ScreenDialog.kt
Normal file
209
compiler/src/prog8/astvm/ScreenDialog.kt
Normal file
@ -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<lines.size-1) {
|
||||||
|
cursorX=0
|
||||||
|
cursorY++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun printTextSingleLine(text: String, color: Int, lowercase: Boolean) {
|
||||||
|
if(color!=1) {
|
||||||
|
TODO("text can only be white for now")
|
||||||
|
}
|
||||||
|
for(clearx in cursorX until cursorX+text.length) {
|
||||||
|
g2d.clearRect(8*clearx, 8*y, 8, 8)
|
||||||
|
}
|
||||||
|
for(sc in Petscii.encodeScreencode(text, lowercase)) {
|
||||||
|
setChar(cursorX, cursorY, sc)
|
||||||
|
cursorX++
|
||||||
|
if(cursorX>=(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<Int, Int> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
39
compiler/src/prog8/astvm/VariablesInitializer.kt
Normal file
39
compiler/src/prog8/astvm/VariablesInitializer.kt
Normal file
@ -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)
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
@ -37,10 +37,13 @@
|
|||||||
|
|
||||||
; found next one, mark the multiples and return it.
|
; found next one, mark the multiples and return it.
|
||||||
sieve[candidate_prime] = true
|
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) {
|
while multiple < len(sieve) {
|
||||||
sieve[lsb(multiple)] = true
|
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
|
return candidate_prime
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user