screen added to vm, some new opcodes

This commit is contained in:
Irmen de Jong 2018-09-10 22:39:15 +02:00
parent af7103f0f7
commit e71fef709a
6 changed files with 216 additions and 146 deletions

View File

@ -11,19 +11,25 @@ main.var1 str "This is main.var1"
main.var2 byte aa main.var2 byte aa
main.var3 word ea44 main.var3 word ea44
main.var4 float 3.1415927 main.var4 float 3.1415927
main.textcolor byte 0
input.prompt str "Enter a number: " input.prompt str "Enter a number: "
input.result word 0 input.result word 0
%end_variables %end_variables
; instructions and labels ; instructions and labels
%instructions %instructions
nop nop
syscall WRITE_MEMSTR word:1000 syscall WRITE_MEMSTR w:1000
loop: loop:
syscall WRITE_VAR str:"input.prompt" inc_var "main.textcolor"
syscall INPUT_VAR str:"input.result" syscall RANDOM
syscall WRITE_VAR str:"input.result" syscall RANDOM
push byte:8d push_var "main.textcolor"
syscall WRITE_CHAR 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 jump loop
%end_instructions %end_instructions

View File

@ -544,14 +544,6 @@ class VarDecl(val type: VarDeclType,
val scopedname: String by lazy { makeScopedName(name).joinToString(".") } 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 { override fun toString(): String {
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)" return "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)"
} }

View File

@ -25,7 +25,7 @@ fun Module.checkValid(globalNamespace: INameScope, compilerOptions: CompilationO
* todo check subroutine return values against the call's result assignments * 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<SyntaxError> = mutableListOf() private val checkResult: MutableList<SyntaxError> = mutableListOf()
fun result(): List<SyntaxError> { fun result(): List<SyntaxError> {
@ -122,7 +122,7 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp
*/ */
override fun process(assignment: Assignment): IStatement { override fun process(assignment: Assignment): IStatement {
if(assignment.target.identifier!=null) { 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) { if(targetSymbol !is VarDecl) {
checkResult.add(SyntaxError("assignment LHS must be register or variable", assignment.position)) checkResult.add(SyntaxError("assignment LHS must be register or variable", assignment.position))
return super.process(assignment) return super.process(assignment)
@ -133,7 +133,7 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp
} }
if(assignment.value is LiteralValue) { 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) checkValueTypeAndRange(targetDatatype, null, assignment.value as LiteralValue, assignment.position)
} }
return super.process(assignment) return super.process(assignment)
@ -191,7 +191,7 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp
* check if condition * check if condition
*/ */
override fun process(ifStatement: IfStatement): IStatement { override fun process(ifStatement: IfStatement): IStatement {
val constvalue = ifStatement.condition.constValue(globalNamespace) val constvalue = ifStatement.condition.constValue(namespace)
if(constvalue!=null) { if(constvalue!=null) {
val msg = if (constvalue.asBooleanValue) "condition is always true" else "condition is always false" val msg = if (constvalue.asBooleanValue) "condition is always true" else "condition is always false"
println("${ifStatement.position} Warning: $msg") println("${ifStatement.position} Warning: $msg")
@ -279,8 +279,8 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp
checkResult.add(SyntaxError(msg, range.position)) checkResult.add(SyntaxError(msg, range.position))
} }
super.process(range) super.process(range)
val from = range.from.constValue(globalNamespace) val from = range.from.constValue(namespace)
val to = range.to.constValue(globalNamespace) val to = range.to.constValue(namespace)
if(from!=null && to != null) { if(from!=null && to != null) {
when { when {
from.intvalue!=null && to.intvalue!=null -> { 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 { override fun process(postIncrDecr: PostIncrDecr): IStatement {
if(postIncrDecr.target.register==null) { if(postIncrDecr.target.register==null) {
val targetName = postIncrDecr.target.identifier!!.nameInSource val targetName = postIncrDecr.target.identifier!!.nameInSource
val target = globalNamespace.lookup(targetName, postIncrDecr) val target = namespace.lookup(targetName, postIncrDecr)
if(target==null) { if(target==null) {
checkResult.add(SyntaxError("undefined symbol: ${targetName.joinToString(".")}", postIncrDecr.position)) checkResult.add(SyntaxError("undefined symbol: ${targetName.joinToString(".")}", postIncrDecr.position))
} else { } else {
@ -335,7 +335,7 @@ class AstChecker(private val globalNamespace: INameScope, private val compilerOp
} }
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: IStatement): IStatement? { 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) if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
return targetStatement return targetStatement
checkResult.add(SyntaxError("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position)) 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 val number = (av as LiteralValue).intvalue
?: return err("array must be all bytes") ?: 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) if (value.arrayvalue.size != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})") 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 val number = (av as LiteralValue).intvalue
?: return err("array must be all words") ?: 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) if (value.arrayvalue.size != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})") return err("initializer array size mismatch (expecting $expectedSize, got ${value.arrayvalue.size})")

View File

@ -88,7 +88,7 @@ class DirectedGraph<VT> {
} }
class AstRecursionChecker(val namespace: INameScope) : IAstProcessor { class AstRecursionChecker(private val namespace: INameScope) : IAstProcessor {
private val callGraph = DirectedGraph<INameScope>() private val callGraph = DirectedGraph<INameScope>()
fun result(): List<AstException> { fun result(): List<AstException> {

View File

@ -3,13 +3,22 @@ package il65.stackvm
import javax.swing.* import javax.swing.*
import java.awt.* import java.awt.*
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
import java.util.*
import javax.swing.Timer 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?) { override fun paint(graphics: Graphics?) {
val g2d = graphics as Graphics2D? val g2d = graphics as Graphics2D?
@ -19,29 +28,53 @@ class MyJPanel : JPanel() {
g2d.drawImage(image, 0, 0, image.width * 3, image.height * 3, null) 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 companion object {
const val BitmapHeight = 200 const val ScreenWidth = 320
const val BorderWidth = 16 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() { class ScreenDialog : JFrame() {
private val canvas = MyJPanel() val canvas = BitmapScreenPanel()
private val buttonQuit = JButton("Quit") private val buttonQuit = JButton("Quit")
private val logicTimer: Timer
private val repaintTimer: Timer
init { init {
val borderWidth = 16
val screenSize = Dimension(canvas.image.width * Visuals.Scaling, canvas.image.height * Visuals.Scaling) title = "StackVm graphics. Text I/O goes to console."
layout = GridBagLayout() layout = GridBagLayout()
canvas.minimumSize = screenSize
canvas.maximumSize = screenSize
canvas.preferredSize = screenSize
defaultCloseOperation = JFrame.EXIT_ON_CLOSE defaultCloseOperation = JFrame.EXIT_ON_CLOSE
isResizable = false isResizable = false
@ -49,85 +82,60 @@ class ScreenDialog : JFrame() {
buttonBar.add(buttonQuit) buttonBar.add(buttonQuit)
var c = GridBagConstraints() var c = GridBagConstraints()
c.fill = GridBagConstraints.BOTH // the button bar
c.gridx = 0 c.gridx = 0
c.gridy = 0 c.gridy = 0
add(canvas, c) c.gridwidth = 3
c = GridBagConstraints()
c.gridx = 0
c.gridy = 1
c.anchor = GridBagConstraints.LINE_END c.anchor = GridBagConstraints.LINE_END
add(buttonBar, c) add(buttonBar, c)
getRootPane().defaultButton = buttonQuit // the borders (top, left, right, bottom)
buttonQuit.addActionListener { e -> onOK() } val borderTop = JPanel().apply {
preferredSize = Dimension(BitmapScreenPanel.Scaling * (BitmapScreenPanel.ScreenWidth+2*borderWidth), BitmapScreenPanel.Scaling * borderWidth)
background = BitmapScreenPanel.palette[14]
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()
} }
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() repaintTimer.start()
} }
private fun onOK() { private fun onOK() {
// add your code here // add your code here
dispose() dispose()
logicTimer.stop()
repaintTimer.stop()
System.exit(0) 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<String>) {
EventQueue.invokeLater {
val dialog = ScreenDialog()
dialog.pack()
dialog.isVisible = true
}
} }

View File

@ -3,10 +3,12 @@ package il65.stackvm
import il65.ast.DataType import il65.ast.DataType
import il65.compiler.Mflpt5 import il65.compiler.Mflpt5
import il65.compiler.Petscii import il65.compiler.Petscii
import java.awt.EventQueue
import java.io.File import java.io.File
import java.io.PrintWriter import java.io.PrintWriter
import java.util.* import java.util.*
import java.util.regex.Pattern import java.util.regex.Pattern
import javax.swing.Timer
import kotlin.math.max import kotlin.math.max
import kotlin.math.pow import kotlin.math.pow
@ -14,11 +16,14 @@ enum class Opcode {
// pushing values on the (evaluation) stack // pushing values on the (evaluation) stack
PUSH, // push constant byte value 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 PUSH_VAR, // push a variable
DUP, // push topmost value once again DUP, // push topmost value once again
// popping values off the (evaluation) stack, possibly storing them in another location // 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_MEM, // pop value into destination memory address
POP_VAR, // pop value into variable POP_VAR, // pop value into variable
@ -78,12 +83,12 @@ enum class Opcode {
JUMP, JUMP,
BCS, BCS,
BCC, BCC,
//BEQ, // @todo not implemented status flag Z BEQ, // branch if value on top of stack is zero
//BNE, // @todo not implemented status flag Z BNE, // branch if value on top of stack is not zero
//BVS, // @todo not implemented status flag V BMI, // branch if value on top of stack < 0
//BVC, // @todo not implemented status flag V BPL, // branch if value on top of stack >= 0
//BMI, // @todo not implemented status flag N // BVS, // status flag V (overflow) not implemented
//BPL, // @todo not implemented status flag N // BVC, // status flag V (overflow) not implemented
// subroutine calling // subroutine calling
CALL, CALL,
@ -92,8 +97,8 @@ enum class Opcode {
// misc // misc
SWAP, SWAP,
SEC, SEC, // set carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
CLC, CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
NOP, NOP,
TERMINATE 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_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 WRITE_VAR(14), // print the number or string from the given variable
INPUT_VAR(15), // user input a string into a 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 { class Memory {
@ -506,17 +516,15 @@ class Program (prog: MutableList<Instruction>,
private fun getArgValue(args: String?): Value? { private fun getArgValue(args: String?): Value? {
if(args==null) if(args==null)
return 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(':') val (type, valueStr) = args.split(':')
return when(type) { return when(type) {
"byte" -> Value(DataType.BYTE, valueStr.toShort(16)) "b" -> Value(DataType.BYTE, valueStr.toShort(16))
"word" -> Value(DataType.WORD, valueStr.toInt(16)) "w" -> Value(DataType.WORD, valueStr.toInt(16))
"float" -> Value(DataType.FLOAT, valueStr.toDouble()) "f" -> 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")
}
else -> throw VmExecutionException("invalid datatype $type") else -> throw VmExecutionException("invalid datatype $type")
} }
} }
@ -616,13 +624,18 @@ class Program (prog: MutableList<Instruction>,
when(instr.opcode) { when(instr.opcode) {
Opcode.TERMINATE -> instr.next = instr // won't ever execute a next instruction 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.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}") val target = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}")
instr.next = target 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 -> { Opcode.CALL -> {
val jumpInstr = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}") val jumpInstr = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}")
instr.next=jumpInstr instr.next = jumpInstr
instr.nextAlt = nextInstr // instruction to return to instr.nextAlt = nextInstr // instruction to return to
} }
else -> instr.next = nextInstr else -> instr.next = nextInstr
@ -633,33 +646,41 @@ class Program (prog: MutableList<Instruction>,
class StackVm(val traceOutputFile: String?) { class StackVm(val traceOutputFile: String?) {
val mem = Memory() private val mem = Memory()
private val evalstack = MyStack<Value>() // evaluation stack private val evalstack = MyStack<Value>() // evaluation stack
private val callstack = MyStack<Instruction>() // subroutine call stack (@todo maybe use evalstack as well for this?) private val callstack = MyStack<Instruction>() // subroutine call stack (@todo maybe use evalstack as well for this?)
private var variables = mutableMapOf<String, Value>() // all variables (set of all vars used by all blocks/subroutines) key = their fully scoped name private var variables = mutableMapOf<String, Value>() // all variables (set of all vars used by all blocks/subroutines) key = their fully scoped name
private var carry: Boolean = false private var carry: Boolean = false
private var program = listOf<Instruction>() private var program = listOf<Instruction>()
private var traceOutput = if(traceOutputFile!=null) File(traceOutputFile).printWriter() else null 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.program = program.program
this.canvas = canvas
this.variables = program.variables.toMutableMap() this.variables = program.variables.toMutableMap()
initMemory(program.memory) initMemory(program.memory)
var ins = this.program[0] currentIns = this.program[0]
}
try { fun step() {
while (true) { // step is invoked every 1/100 sec
ins = dispatch(ins) // 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) if (evalstack.size > 128)
throw VmExecutionException("too many values on evaluation stack") throw VmExecutionException("too many values on evaluation stack")
if(callstack.size > 128) if (callstack.size > 128)
throw VmExecutionException("too many nested/recursive calls") throw VmExecutionException("too many nested/recursive calls")
} }
} catch (x: VmTerminationException) { val time = System.currentTimeMillis()-start
println("\n\nExecution terminated.") if(time > 100) {
} finally { println("WARNING: vm dispatch step took > 100 msec")
traceOutput?.close()
} }
} }
@ -695,6 +716,18 @@ class StackVm(val traceOutputFile: String?) {
when (ins.opcode) { when (ins.opcode) {
Opcode.NOP -> {} Opcode.NOP -> {}
Opcode.PUSH -> evalstack.push(ins.arg) 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.DUP -> evalstack.push(evalstack.peek())
Opcode.DISCARD -> evalstack.pop() Opcode.DISCARD -> evalstack.pop()
Opcode.SWAP -> { Opcode.SWAP -> {
@ -818,6 +851,24 @@ class StackVm(val traceOutputFile: String?) {
} }
variables[varname] = value 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") 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.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.BCS -> return if(carry) ins.next else ins.nextAlt!!
Opcode.BCC -> return if(carry) ins.nextAlt!! else ins.next 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.CALL -> callstack.push(ins.nextAlt)
Opcode.RETURN -> return callstack.pop() Opcode.RETURN -> return callstack.pop()
Opcode.PUSH_VAR -> { Opcode.PUSH_VAR -> {
@ -1005,7 +1060,16 @@ class StackVm(val traceOutputFile: String?) {
fun main(args: Array<String>) { fun main(args: Array<String>) {
val vm = StackVm(traceOutputFile = "vmtrace.txt") val program = Program.load("examples/stackvmtest.txt")
val program = Program.load("il65/examples/stackvmtest.txt") val vm = StackVm(traceOutputFile = null)
vm.run(program) 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()
}
}