From e71fef709aebd8af259e26a06d6c9bcf8eab7bcc Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 10 Sep 2018 22:39:15 +0200 Subject: [PATCH] screen added to vm, some new opcodes --- il65/examples/stackvmtest.txt | 18 ++- il65/src/il65/ast/AST.kt | 8 -- il65/src/il65/ast/AstChecker.kt | 20 +-- il65/src/il65/ast/AstRecursionChecker.kt | 2 +- il65/src/il65/stackvm/ScreenDialog.kt | 172 ++++++++++++----------- il65/src/il65/stackvm/StackVm.kt | 142 ++++++++++++++----- 6 files changed, 216 insertions(+), 146 deletions(-) diff --git a/il65/examples/stackvmtest.txt b/il65/examples/stackvmtest.txt index cfcdce220..1ed2e86ee 100644 --- a/il65/examples/stackvmtest.txt +++ b/il65/examples/stackvmtest.txt @@ -11,19 +11,25 @@ main.var1 str "This is main.var1" main.var2 byte aa main.var3 word ea44 main.var4 float 3.1415927 +main.textcolor byte 0 input.prompt str "Enter a number: " input.result word 0 %end_variables ; instructions and labels %instructions nop - syscall WRITE_MEMSTR word:1000 + syscall WRITE_MEMSTR w:1000 loop: - syscall WRITE_VAR str:"input.prompt" - syscall INPUT_VAR str:"input.result" - syscall WRITE_VAR str:"input.result" - push byte:8d - syscall WRITE_CHAR + inc_var "main.textcolor" + syscall RANDOM + syscall RANDOM + push_var "main.textcolor" + syscall GFX_PIXEL +; syscall WRITE_VAR "input.prompt" +; syscall INPUT_VAR "input.result" +; syscall WRITE_VAR "input.result" +; push b:8d +; syscall WRITE_CHAR jump loop %end_instructions diff --git a/il65/src/il65/ast/AST.kt b/il65/src/il65/ast/AST.kt index 77dde1b68..c447247fa 100644 --- a/il65/src/il65/ast/AST.kt +++ b/il65/src/il65/ast/AST.kt @@ -544,14 +544,6 @@ class VarDecl(val type: VarDeclType, val scopedname: String by lazy { makeScopedName(name).joinToString(".") } - - fun arraySizeX(namespace: INameScope) : Int? { - return arrayspec?.x?.constValue(namespace)?.intvalue - } - fun arraySizeY(namespace: INameScope) : Int? { - return arrayspec?.y?.constValue(namespace)?.intvalue - } - override fun toString(): String { return "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)" } diff --git a/il65/src/il65/ast/AstChecker.kt b/il65/src/il65/ast/AstChecker.kt index 58ef55666..02294e418 100644 --- a/il65/src/il65/ast/AstChecker.kt +++ b/il65/src/il65/ast/AstChecker.kt @@ -25,7 +25,7 @@ fun Module.checkValid(globalNamespace: INameScope, compilerOptions: CompilationO * todo check subroutine return values against the call's result assignments */ -class AstChecker(private val globalNamespace: INameScope, private val compilerOptions: CompilationOptions) : IAstProcessor { +class AstChecker(private val namespace: INameScope, private val compilerOptions: CompilationOptions) : IAstProcessor { private val checkResult: MutableList = mutableListOf() fun result(): List { @@ -122,7 +122,7 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp */ override fun process(assignment: Assignment): IStatement { if(assignment.target.identifier!=null) { - val targetSymbol = globalNamespace.lookup(assignment.target.identifier!!.nameInSource, assignment) + val targetSymbol = namespace.lookup(assignment.target.identifier!!.nameInSource, assignment) if(targetSymbol !is VarDecl) { checkResult.add(SyntaxError("assignment LHS must be register or variable", assignment.position)) return super.process(assignment) @@ -133,7 +133,7 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp } if(assignment.value is LiteralValue) { - val targetDatatype = assignment.target.determineDatatype(globalNamespace, assignment) + val targetDatatype = assignment.target.determineDatatype(namespace, assignment) checkValueTypeAndRange(targetDatatype, null, assignment.value as LiteralValue, assignment.position) } return super.process(assignment) @@ -191,7 +191,7 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp * check if condition */ override fun process(ifStatement: IfStatement): IStatement { - val constvalue = ifStatement.condition.constValue(globalNamespace) + val constvalue = ifStatement.condition.constValue(namespace) if(constvalue!=null) { val msg = if (constvalue.asBooleanValue) "condition is always true" else "condition is always false" println("${ifStatement.position} Warning: $msg") @@ -279,8 +279,8 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp checkResult.add(SyntaxError(msg, range.position)) } super.process(range) - val from = range.from.constValue(globalNamespace) - val to = range.to.constValue(globalNamespace) + val from = range.from.constValue(namespace) + val to = range.to.constValue(namespace) if(from!=null && to != null) { when { from.intvalue!=null && to.intvalue!=null -> { @@ -320,7 +320,7 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp override fun process(postIncrDecr: PostIncrDecr): IStatement { if(postIncrDecr.target.register==null) { val targetName = postIncrDecr.target.identifier!!.nameInSource - val target = globalNamespace.lookup(targetName, postIncrDecr) + val target = namespace.lookup(targetName, postIncrDecr) if(target==null) { checkResult.add(SyntaxError("undefined symbol: ${targetName.joinToString(".")}", postIncrDecr.position)) } else { @@ -335,7 +335,7 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp } private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: IStatement): IStatement? { - val targetStatement = target.targetStatement(globalNamespace) + val targetStatement = target.targetStatement(namespace) if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder) return targetStatement checkResult.add(SyntaxError("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position)) @@ -396,7 +396,7 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp val number = (av as LiteralValue).intvalue ?: return err("array must be all bytes") - val expectedSize = arrayspec?.x?.constValue(globalNamespace)?.intvalue + val expectedSize = arrayspec?.x?.constValue(namespace)?.intvalue if (value.arrayvalue.size != expectedSize) return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})") @@ -417,7 +417,7 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp val number = (av as LiteralValue).intvalue ?: return err("array must be all words") - val expectedSize = arrayspec?.x?.constValue(globalNamespace)?.intvalue + val expectedSize = arrayspec?.x?.constValue(namespace)?.intvalue if (value.arrayvalue.size != expectedSize) return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})") diff --git a/il65/src/il65/ast/AstRecursionChecker.kt b/il65/src/il65/ast/AstRecursionChecker.kt index 715cc7788..f9acbcb2f 100644 --- a/il65/src/il65/ast/AstRecursionChecker.kt +++ b/il65/src/il65/ast/AstRecursionChecker.kt @@ -88,7 +88,7 @@ class DirectedGraph { } -class AstRecursionChecker(val namespace: INameScope) : IAstProcessor { +class AstRecursionChecker(private val namespace: INameScope) : IAstProcessor { private val callGraph = DirectedGraph() fun result(): List { diff --git a/il65/src/il65/stackvm/ScreenDialog.kt b/il65/src/il65/stackvm/ScreenDialog.kt index 0c0e1fb53..29fc9dacf 100644 --- a/il65/src/il65/stackvm/ScreenDialog.kt +++ b/il65/src/il65/stackvm/ScreenDialog.kt @@ -3,13 +3,22 @@ package il65.stackvm import javax.swing.* import java.awt.* import java.awt.image.BufferedImage -import java.util.* import javax.swing.Timer -class MyJPanel : JPanel() { +class BitmapScreenPanel : JPanel() { - var image = BufferedImage(BitmapWidth + BorderWidth * 2, BitmapHeight + BorderWidth * 2, BufferedImage.TYPE_INT_ARGB) + private val image = BufferedImage(ScreenWidth, ScreenHeight, BufferedImage.TYPE_INT_ARGB) + private val g2d = image.graphics as Graphics2D + + + init { + val size = Dimension(image.width * Scaling, image.height * Scaling) + minimumSize = size + maximumSize = size + preferredSize = size + clearScreen(6) + } override fun paint(graphics: Graphics?) { val g2d = graphics as Graphics2D? @@ -19,29 +28,53 @@ class MyJPanel : JPanel() { g2d.drawImage(image, 0, 0, image.width * 3, image.height * 3, null) } - companion object { + fun clearScreen(color: Int) { + g2d.background = palette[color and 15] + g2d.clearRect(0, 0, BitmapScreenPanel.ScreenWidth, BitmapScreenPanel.ScreenHeight) + } + fun setPixel(x: Int, y: Int, color: Int) { + image.setRGB(x, y, palette[color and 15].rgb) + } + fun writeText(x: Int, y: Int, text: String, color: Int) { + g2d.font = Font(Font.MONOSPACED, Font.PLAIN, 10) + g2d.color = palette[color and 15] + g2d.drawString(text, x, y + g2d.font.size - 1) + } - const val BitmapWidth = 320 - const val BitmapHeight = 200 - const val BorderWidth = 16 + companion object { + const val ScreenWidth = 320 + const val ScreenHeight = 256 + 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() { - private val canvas = MyJPanel() + val canvas = BitmapScreenPanel() private val buttonQuit = JButton("Quit") - private val logicTimer: Timer - private val repaintTimer: Timer init { - - val screenSize = Dimension(canvas.image.width * Visuals.Scaling, canvas.image.height * Visuals.Scaling) + val borderWidth = 16 + title = "StackVm graphics. Text I/O goes to console." layout = GridBagLayout() - canvas.minimumSize = screenSize - canvas.maximumSize = screenSize - canvas.preferredSize = screenSize - defaultCloseOperation = JFrame.EXIT_ON_CLOSE isResizable = false @@ -49,85 +82,60 @@ class ScreenDialog : JFrame() { buttonBar.add(buttonQuit) var c = GridBagConstraints() - c.fill = GridBagConstraints.BOTH + // the button bar c.gridx = 0 c.gridy = 0 - add(canvas, c) - c = GridBagConstraints() - c.gridx = 0 - c.gridy = 1 + c.gridwidth = 3 c.anchor = GridBagConstraints.LINE_END add(buttonBar, c) - getRootPane().defaultButton = buttonQuit - buttonQuit.addActionListener { e -> onOK() } - - - val logic = ScreenDialog.Logic() - val visuals = Visuals(logic, canvas.image) - - logicTimer = Timer(1000 / 120) { actionEvent -> logic.update() } - repaintTimer = Timer(1000 / 60) { actionEvent -> - visuals.update() - repaint() + // 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] + } + 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) - logicTimer.start() + getRootPane().defaultButton = buttonQuit + buttonQuit.addActionListener { _ -> onOK() } + + } + + fun start() { + val repaintTimer = Timer(1000 / 60) { _ -> repaint() } repaintTimer.start() } private fun onOK() { // add your code here dispose() - logicTimer.stop() - repaintTimer.stop() System.exit(0) } - - internal class Logic { - var last = System.currentTimeMillis() - - fun update() { - val now = System.currentTimeMillis() - val timeSinceLast = now - last - last = now - } - } - - internal class Visuals(private val logic: Logic, private val image: BufferedImage) { - private val rnd = Random() - companion object { - const val Scaling = 3 - } - - init { - val g2d = image.graphics as Graphics2D - g2d.background = Color(0x5533ff) - g2d.clearRect(0, 0, image.width, image.height) - g2d.background = Color(0x222277) - g2d.clearRect(MyJPanel.BorderWidth, MyJPanel.BorderWidth, MyJPanel.BitmapWidth, MyJPanel.BitmapHeight) - g2d.font = Font(Font.MONOSPACED, Font.PLAIN, 12) - g2d.drawString("HELLO world 12345", 100, 100) - } - - fun update() { - - image.setRGB(rnd.nextInt(MyJPanel.BitmapWidth), rnd.nextInt(MyJPanel.BitmapHeight), rnd.nextInt()) - - // for(int x = 50; x < MyJPanel.BitmapWidth-50; x++) { - // for(int y = 50; y < MyJPanel.BitmapHeight-50; y++) { - // image.setRGB(x+MyJPanel.BorderWidth, y+MyJPanel.BorderWidth, rnd.nextInt()); - // } - // } - } - } -} - - -fun main(args: Array) { - EventQueue.invokeLater { - val dialog = ScreenDialog() - dialog.pack() - dialog.isVisible = true - } } diff --git a/il65/src/il65/stackvm/StackVm.kt b/il65/src/il65/stackvm/StackVm.kt index 4275daa4b..4236eecd4 100644 --- a/il65/src/il65/stackvm/StackVm.kt +++ b/il65/src/il65/stackvm/StackVm.kt @@ -3,10 +3,12 @@ package il65.stackvm import il65.ast.DataType import il65.compiler.Mflpt5 import il65.compiler.Petscii +import java.awt.EventQueue import java.io.File import java.io.PrintWriter import java.util.* import java.util.regex.Pattern +import javax.swing.Timer import kotlin.math.max import kotlin.math.pow @@ -14,11 +16,14 @@ enum class Opcode { // pushing values on the (evaluation) stack PUSH, // push constant byte value + PUSH_MEM, // push byte value from memory to stack + PUSH_MEM_W, // push word value from memory to stack + PUSH_MEM_F, // push float value from memory to stack PUSH_VAR, // push a variable DUP, // push topmost value once again // popping values off the (evaluation) stack, possibly storing them in another location - DISCARD, // discard X bytes from the top of the stack + DISCARD, // discard top value POP_MEM, // pop value into destination memory address POP_VAR, // pop value into variable @@ -78,12 +83,12 @@ enum class Opcode { JUMP, BCS, BCC, - //BEQ, // @todo not implemented status flag Z - //BNE, // @todo not implemented status flag Z - //BVS, // @todo not implemented status flag V - //BVC, // @todo not implemented status flag V - //BMI, // @todo not implemented status flag N - //BPL, // @todo not implemented status flag N + BEQ, // branch if value on top of stack is zero + BNE, // branch if value on top of stack is not zero + BMI, // branch if value on top of stack < 0 + BPL, // branch if value on top of stack >= 0 + // BVS, // status flag V (overflow) not implemented + // BVC, // status flag V (overflow) not implemented // subroutine calling CALL, @@ -92,8 +97,8 @@ enum class Opcode { // misc SWAP, - SEC, - CLC, + SEC, // set carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations + CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations NOP, TERMINATE } @@ -105,6 +110,11 @@ enum class Syscall(val callNr: Short) { WRITE_CHAR(13), // pop from the evaluation stack and print it as a single petscii character WRITE_VAR(14), // print the number or string from the given variable INPUT_VAR(15), // user input a string into a variable + GFX_PIXEL(16), // plot a pixel at (x,y,color) pushed on stack in that order + GFX_CLEARSCR(17), // clear the screen with color pushed on stack + GFX_TEXT(18), // write text on screen at (x,y,text,color) pushed on stack in that order + RANDOM(19), // push a random byte on the stack + RANDOM_W(20) // push a random word on the stack } class Memory { @@ -506,17 +516,15 @@ class Program (prog: MutableList, private fun getArgValue(args: String?): Value? { if(args==null) return null + if(args[0]=='"' && args[args.length-1]=='"') { + // it's a string. + return Value(DataType.STR, null, unescape(args.substring(1, args.length-1))) + } val (type, valueStr) = args.split(':') return when(type) { - "byte" -> Value(DataType.BYTE, valueStr.toShort(16)) - "word" -> Value(DataType.WORD, valueStr.toInt(16)) - "float" -> Value(DataType.FLOAT, valueStr.toDouble()) - "str" -> { - if(valueStr.startsWith('"') && valueStr.endsWith('"')) - Value(DataType.STR, null, unescape(valueStr.substring(1, valueStr.length-1))) - else - throw VmExecutionException("str should be enclosed in quotes") - } + "b" -> Value(DataType.BYTE, valueStr.toShort(16)) + "w" -> Value(DataType.WORD, valueStr.toInt(16)) + "f" -> Value(DataType.FLOAT, valueStr.toDouble()) else -> throw VmExecutionException("invalid datatype $type") } } @@ -616,13 +624,18 @@ class Program (prog: MutableList, when(instr.opcode) { Opcode.TERMINATE -> instr.next = instr // won't ever execute a next instruction Opcode.RETURN -> instr.next = instr // kinda a special one, in actuality the return instruction is dynamic - Opcode.JUMP, Opcode.BCC, Opcode.BCS -> { + Opcode.JUMP -> { val target = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}") instr.next = target } + Opcode.BCC, Opcode.BCS, Opcode.BEQ, Opcode.BNE, Opcode.BMI, Opcode.BPL -> { + val jumpInstr = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}") + instr.next = jumpInstr + instr.nextAlt = nextInstr + } Opcode.CALL -> { val jumpInstr = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}") - instr.next=jumpInstr + instr.next = jumpInstr instr.nextAlt = nextInstr // instruction to return to } else -> instr.next = nextInstr @@ -633,33 +646,41 @@ class Program (prog: MutableList, class StackVm(val traceOutputFile: String?) { - val mem = Memory() + private val mem = Memory() private val evalstack = MyStack() // evaluation stack private val callstack = MyStack() // subroutine call stack (@todo maybe use evalstack as well for this?) private var variables = mutableMapOf() // all variables (set of all vars used by all blocks/subroutines) key = their fully scoped name private var carry: Boolean = false private var program = listOf() private var traceOutput = if(traceOutputFile!=null) File(traceOutputFile).printWriter() else null + private lateinit var currentIns: Instruction + private lateinit var canvas: BitmapScreenPanel + private val rnd = Random() - fun run(program: Program) { + fun load(program: Program, canvas: BitmapScreenPanel) { this.program = program.program + this.canvas = canvas this.variables = program.variables.toMutableMap() initMemory(program.memory) - var ins = this.program[0] + currentIns = this.program[0] + } - try { - while (true) { - ins = dispatch(ins) + fun step() { + // step is invoked every 1/100 sec + // we execute 5000 instructions in one go so we end up doing 500.000 instructions per second + val instructionsPerStep = 5000 + val start = System.currentTimeMillis() + for(i:Int in 0..instructionsPerStep) { + currentIns = dispatch(currentIns) - if(evalstack.size > 128) - throw VmExecutionException("too many values on evaluation stack") - if(callstack.size > 128) - throw VmExecutionException("too many nested/recursive calls") - } - } catch (x: VmTerminationException) { - println("\n\nExecution terminated.") - } finally { - traceOutput?.close() + if (evalstack.size > 128) + throw VmExecutionException("too many values on evaluation stack") + if (callstack.size > 128) + throw VmExecutionException("too many nested/recursive calls") + } + val time = System.currentTimeMillis()-start + if(time > 100) { + println("WARNING: vm dispatch step took > 100 msec") } } @@ -695,6 +716,18 @@ class StackVm(val traceOutputFile: String?) { when (ins.opcode) { Opcode.NOP -> {} Opcode.PUSH -> evalstack.push(ins.arg) + Opcode.PUSH_MEM -> { + val address = ins.arg!!.integerValue() + evalstack.push(Value(DataType.BYTE, mem.getByte(address))) + } + Opcode.PUSH_MEM_W -> { + val address = ins.arg!!.integerValue() + evalstack.push(Value(DataType.WORD, mem.getWord(address))) + } + Opcode.PUSH_MEM_F -> { + val address = ins.arg!!.integerValue() + evalstack.push(Value(DataType.FLOAT, mem.getFloat(address))) + } Opcode.DUP -> evalstack.push(evalstack.peek()) Opcode.DISCARD -> evalstack.pop() Opcode.SWAP -> { @@ -818,6 +851,24 @@ class StackVm(val traceOutputFile: String?) { } variables[varname] = value } + Syscall.GFX_PIXEL -> { + // plot pixel at (x, y, color) from stack + val color = evalstack.pop() + val (y, x) = evalstack.pop2() + canvas.setPixel(x.integerValue(), y.integerValue(), color.integerValue()) + } + Syscall.GFX_CLEARSCR -> { + val color = evalstack.pop() + canvas.clearScreen(color.integerValue()) + } + Syscall.GFX_TEXT -> { + val color = evalstack.pop() + val text = evalstack.pop() + val (y, x) = evalstack.pop2() + canvas.writeText(x.integerValue(), y.integerValue(), text.stringvalue!!, color.integerValue()) + } + Syscall.RANDOM -> evalstack.push(Value(DataType.BYTE, rnd.nextInt() and 255)) + Syscall.RANDOM_W -> evalstack.push(Value(DataType.WORD, rnd.nextInt() and 65535)) else -> throw VmExecutionException("unimplemented syscall $syscall") } } @@ -926,6 +977,10 @@ class StackVm(val traceOutputFile: String?) { Opcode.JUMP -> {} // do nothing; the next instruction is wired up already to the jump target Opcode.BCS -> return if(carry) ins.next else ins.nextAlt!! Opcode.BCC -> return if(carry) ins.nextAlt!! else ins.next + Opcode.BEQ -> return if(evalstack.peek().numericValue().toDouble()==0.0) ins.next else ins.nextAlt!! + Opcode.BNE -> return if(evalstack.peek().numericValue().toDouble()!=0.0) ins.next else ins.nextAlt!! + Opcode.BMI -> return if(evalstack.peek().numericValue().toDouble()<0.0) ins.next else ins.nextAlt!! + Opcode.BPL -> return if(evalstack.peek().numericValue().toDouble()>=0.0) ins.next else ins.nextAlt!! Opcode.CALL -> callstack.push(ins.nextAlt) Opcode.RETURN -> return callstack.pop() Opcode.PUSH_VAR -> { @@ -1005,7 +1060,16 @@ class StackVm(val traceOutputFile: String?) { fun main(args: Array) { - val vm = StackVm(traceOutputFile = "vmtrace.txt") - val program = Program.load("il65/examples/stackvmtest.txt") - vm.run(program) -} \ No newline at end of file + val program = Program.load("examples/stackvmtest.txt") + val vm = StackVm(traceOutputFile = null) + val dialog = ScreenDialog() + vm.load(program, dialog.canvas) + EventQueue.invokeLater { + dialog.pack() + dialog.isVisible = true + dialog.start() + + val programTimer = Timer(10) { _ -> vm.step() } + programTimer.start() + } +}