mirror of
https://github.com/irmen/prog8.git
synced 2024-09-08 10:55:08 +00:00
screen added to vm, some new opcodes
This commit is contained in:
parent
af7103f0f7
commit
e71fef709a
@ -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
|
||||||
|
|
||||||
|
@ -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)"
|
||||||
}
|
}
|
||||||
|
@ -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})")
|
||||||
|
|
||||||
|
@ -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> {
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user