mirror of
https://github.com/irmen/prog8.git
synced 2024-10-18 16:24:26 +00:00
unit tests for most of the StackVM opcodes. Fixed some opcode behaviors.
This commit is contained in:
parent
455f60fb84
commit
2f48406aad
@ -430,7 +430,7 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
|||||||
val to = range.to.constValue(namespace)
|
val to = range.to.constValue(namespace)
|
||||||
var step = 1
|
var step = 1
|
||||||
if(range.step!=null) {
|
if(range.step!=null) {
|
||||||
val stepLv = range.step?.constValue(namespace) ?: LiteralValue(DataType.BYTE, 1, position = range.position)
|
val stepLv = range.step.constValue(namespace) ?: LiteralValue(DataType.BYTE, 1, position = range.position)
|
||||||
if (stepLv.asIntegerValue == null || stepLv.asIntegerValue == 0) {
|
if (stepLv.asIntegerValue == null || stepLv.asIntegerValue == 0) {
|
||||||
err("range step must be an integer != 0")
|
err("range step must be an integer != 0")
|
||||||
return range
|
return range
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package prog8.compiler
|
package prog8.compiler
|
||||||
|
|
||||||
import jdk.nashorn.internal.ir.JumpStatement
|
|
||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
import prog8.stackvm.*
|
import prog8.stackvm.*
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
|
@ -195,7 +195,7 @@ class ConstantFolding(private val namespace: INameScope) : IAstProcessor {
|
|||||||
override fun process(range: RangeExpr): IExpression {
|
override fun process(range: RangeExpr): IExpression {
|
||||||
range.from = range.from.process(this)
|
range.from = range.from.process(this)
|
||||||
range.to = range.to.process(this)
|
range.to = range.to.process(this)
|
||||||
range.step = range.step?.process(this)
|
range.step = range.step.process(this)
|
||||||
return super.process(range)
|
return super.process(range)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,12 +10,12 @@ import javax.swing.Timer
|
|||||||
|
|
||||||
class BitmapScreenPanel : JPanel() {
|
class BitmapScreenPanel : JPanel() {
|
||||||
|
|
||||||
private val image = BufferedImage(ScreenWidth, ScreenHeight, BufferedImage.TYPE_INT_ARGB)
|
private val image = BufferedImage(SCREENWIDTH, SCREENHEIGHT, BufferedImage.TYPE_INT_ARGB)
|
||||||
private val g2d = image.graphics as Graphics2D
|
private val g2d = image.graphics as Graphics2D
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val size = Dimension(image.width * Scaling, image.height * Scaling)
|
val size = Dimension(image.width * SCALING, image.height * SCALING)
|
||||||
minimumSize = size
|
minimumSize = size
|
||||||
maximumSize = size
|
maximumSize = size
|
||||||
preferredSize = size
|
preferredSize = size
|
||||||
@ -32,7 +32,7 @@ class BitmapScreenPanel : JPanel() {
|
|||||||
|
|
||||||
fun clearScreen(color: Int) {
|
fun clearScreen(color: Int) {
|
||||||
g2d.background = palette[color and 15]
|
g2d.background = palette[color and 15]
|
||||||
g2d.clearRect(0, 0, BitmapScreenPanel.ScreenWidth, BitmapScreenPanel.ScreenHeight)
|
g2d.clearRect(0, 0, BitmapScreenPanel.SCREENWIDTH, BitmapScreenPanel.SCREENHEIGHT)
|
||||||
}
|
}
|
||||||
fun setPixel(x: Int, y: Int, color: Int) {
|
fun setPixel(x: Int, y: Int, color: Int) {
|
||||||
image.setRGB(x, y, palette[color and 15].rgb)
|
image.setRGB(x, y, palette[color and 15].rgb)
|
||||||
@ -44,9 +44,9 @@ class BitmapScreenPanel : JPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val ScreenWidth = 320
|
const val SCREENWIDTH = 320
|
||||||
const val ScreenHeight = 256
|
const val SCREENHEIGHT = 256
|
||||||
const val Scaling = 3
|
const val SCALING = 3
|
||||||
val palette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
val palette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
||||||
Color(0x000000), // 0 = black
|
Color(0x000000), // 0 = black
|
||||||
Color(0xFFFFFF), // 1 = white
|
Color(0xFFFFFF), // 1 = white
|
||||||
@ -93,19 +93,19 @@ class ScreenDialog : JFrame() {
|
|||||||
|
|
||||||
// the borders (top, left, right, bottom)
|
// the borders (top, left, right, bottom)
|
||||||
val borderTop = JPanel().apply {
|
val borderTop = JPanel().apply {
|
||||||
preferredSize = Dimension(BitmapScreenPanel.Scaling * (BitmapScreenPanel.ScreenWidth+2*borderWidth), BitmapScreenPanel.Scaling * borderWidth)
|
preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||||
background = BitmapScreenPanel.palette[14]
|
background = BitmapScreenPanel.palette[14]
|
||||||
}
|
}
|
||||||
val borderBottom = JPanel().apply {
|
val borderBottom = JPanel().apply {
|
||||||
preferredSize =Dimension(BitmapScreenPanel.Scaling * (BitmapScreenPanel.ScreenWidth+2*borderWidth), BitmapScreenPanel.Scaling * borderWidth)
|
preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||||
background = BitmapScreenPanel.palette[14]
|
background = BitmapScreenPanel.palette[14]
|
||||||
}
|
}
|
||||||
val borderLeft = JPanel().apply {
|
val borderLeft = JPanel().apply {
|
||||||
preferredSize =Dimension(BitmapScreenPanel.Scaling * borderWidth, BitmapScreenPanel.Scaling * BitmapScreenPanel.ScreenHeight)
|
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
||||||
background = BitmapScreenPanel.palette[14]
|
background = BitmapScreenPanel.palette[14]
|
||||||
}
|
}
|
||||||
val borderRight = JPanel().apply {
|
val borderRight = JPanel().apply {
|
||||||
preferredSize =Dimension(BitmapScreenPanel.Scaling * borderWidth, BitmapScreenPanel.Scaling * BitmapScreenPanel.ScreenHeight)
|
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
||||||
background = BitmapScreenPanel.palette[14]
|
background = BitmapScreenPanel.palette[14]
|
||||||
}
|
}
|
||||||
c = GridBagConstraints()
|
c = GridBagConstraints()
|
||||||
|
@ -117,6 +117,8 @@ enum class Opcode {
|
|||||||
SWAP,
|
SWAP,
|
||||||
SEC, // set carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
|
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
|
CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
|
||||||
|
SEI, // set irq-disable status flag
|
||||||
|
CLI, // clear irq-disable status flag
|
||||||
NOP,
|
NOP,
|
||||||
BREAKPOINT, // breakpoint
|
BREAKPOINT, // breakpoint
|
||||||
TERMINATE, // end the program
|
TERMINATE, // end the program
|
||||||
@ -134,8 +136,6 @@ enum class Syscall(val callNr: Short) {
|
|||||||
GFX_CLEARSCR(17), // clear the screen with color pushed on stack
|
GFX_CLEARSCR(17), // clear the screen with color pushed on stack
|
||||||
GFX_TEXT(18), // write text on screen at (x,y,color,text) pushed on stack in that order
|
GFX_TEXT(18), // write text on screen at (x,y,color,text) pushed on stack in that order
|
||||||
|
|
||||||
FUNC_P_CARRY(64),
|
|
||||||
FUNC_P_IRQD(65),
|
|
||||||
FUNC_SIN(66),
|
FUNC_SIN(66),
|
||||||
FUNC_COS(67),
|
FUNC_COS(67),
|
||||||
FUNC_ABS(68),
|
FUNC_ABS(68),
|
||||||
@ -162,10 +162,9 @@ enum class Syscall(val callNr: Short) {
|
|||||||
FUNC_RND(89), // push a random byte on the stack
|
FUNC_RND(89), // push a random byte on the stack
|
||||||
FUNC_RNDW(90), // push a random word on the stack
|
FUNC_RNDW(90), // push a random word on the stack
|
||||||
FUNC_RNDF(91), // push a random float on the stack (between 0.0 and 1.0)
|
FUNC_RNDF(91), // push a random float on the stack (between 0.0 and 1.0)
|
||||||
FUNC_FLT(92)
|
|
||||||
|
|
||||||
// note: not all builtin functions of the Prog8 language are present as functions:
|
// note: not all builtin functions of the Prog8 language are present as functions:
|
||||||
// some of them are already opcodes (such as MSB and ROL)!
|
// some of them are already opcodes (such as MSB and ROL and FLT)!
|
||||||
}
|
}
|
||||||
|
|
||||||
class Memory {
|
class Memory {
|
||||||
@ -181,13 +180,13 @@ class Memory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getWord(address: Int): Int {
|
fun getWord(address: Int): Int {
|
||||||
return 256*mem[address] + mem[address+1]
|
return mem[address] + 256*mem[address+1]
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setWord(address: Int, value: Int) {
|
fun setWord(address: Int, value: Int) {
|
||||||
if(value<0 || value>65535) throw VmExecutionException("word value not 0..65535")
|
if(value<0 || value>65535) throw VmExecutionException("word value not 0..65535")
|
||||||
mem[address] = (value / 256).toShort()
|
mem[address] = value.and(255).toShort()
|
||||||
mem[address+1] = value.and(255).toShort()
|
mem[address+1] = (value / 256).toShort()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setFloat(address: Int, value: Double) {
|
fun setFloat(address: Int, value: Double) {
|
||||||
@ -222,6 +221,10 @@ class Memory {
|
|||||||
}
|
}
|
||||||
return Petscii.decodePetscii(petscii, true)
|
return Petscii.decodePetscii(petscii, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
for(i in 0..65535) mem[i]=0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -234,18 +237,22 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
|||||||
init {
|
init {
|
||||||
when(type) {
|
when(type) {
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
byteval = (numericvalue!!.toInt() and 255).toShort()
|
byteval = numericvalue!!.toShort()
|
||||||
|
if(byteval!! <0 || byteval!! > 255)
|
||||||
|
throw VmExecutionException("byte value overflow: $byteval")
|
||||||
asBooleanValue = byteval != (0.toShort())
|
asBooleanValue = byteval != (0.toShort())
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
wordval = numericvalue!!.toInt() and 65535
|
wordval = numericvalue!!.toInt()
|
||||||
|
if(wordval!! <0 || wordval!! > 65535)
|
||||||
|
throw VmExecutionException("word value overflow: $wordval")
|
||||||
asBooleanValue = wordval != 0
|
asBooleanValue = wordval != 0
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
floatval = numericvalue!!.toDouble()
|
floatval = numericvalue!!.toDouble()
|
||||||
asBooleanValue = floatval != 0.0
|
asBooleanValue = floatval != 0.0
|
||||||
}
|
}
|
||||||
DataType.ARRAY -> {
|
DataType.ARRAY, DataType.ARRAY_W -> {
|
||||||
asBooleanValue = arrayvalue!!.isNotEmpty()
|
asBooleanValue = arrayvalue!!.isNotEmpty()
|
||||||
}
|
}
|
||||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||||
@ -262,9 +269,9 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
|||||||
DataType.WORD -> "w:%04x".format(wordval)
|
DataType.WORD -> "w:%04x".format(wordval)
|
||||||
DataType.FLOAT -> "f:$floatval"
|
DataType.FLOAT -> "f:$floatval"
|
||||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> "\"$stringvalue\""
|
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> "\"$stringvalue\""
|
||||||
DataType.ARRAY -> TODO("array")
|
DataType.ARRAY -> TODO("tostring array")
|
||||||
DataType.ARRAY_W -> TODO("word array")
|
DataType.ARRAY_W -> TODO("tostring word array")
|
||||||
DataType.MATRIX -> TODO("matrix")
|
DataType.MATRIX -> TODO("tostring matrix")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,13 +293,23 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
val bh = byteval?.hashCode() ?: 0x10001234
|
||||||
|
val wh = wordval?.hashCode() ?: 0x01002345
|
||||||
|
val fh = floatval?.hashCode() ?: 0x00103456
|
||||||
|
val ah = arrayvalue?.hashCode() ?: 0x11119876
|
||||||
|
return bh xor wh xor fh xor ah xor type.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if(other==null || other !is Value)
|
if(other==null || other !is Value)
|
||||||
return false
|
return false
|
||||||
return compareTo(other)==0
|
return compareTo(other)==0 // note: datatype doesn't matter
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun compareTo(other: Value): Int {
|
operator fun compareTo(other: Value): Int {
|
||||||
|
if(stringvalue!=null && other.stringvalue!=null)
|
||||||
|
return stringvalue.compareTo(other.stringvalue)
|
||||||
return numericValue().toDouble().compareTo(other.numericValue().toDouble())
|
return numericValue().toDouble().compareTo(other.numericValue().toDouble())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,21 +463,21 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
|||||||
fun bitand(other: Value): Value {
|
fun bitand(other: Value): Value {
|
||||||
val v1 = integerValue()
|
val v1 = integerValue()
|
||||||
val v2 = other.integerValue()
|
val v2 = other.integerValue()
|
||||||
val result = v1.and(v2)
|
val result = v1 and v2
|
||||||
return Value(type, result)
|
return Value(type, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bitor(other: Value): Value {
|
fun bitor(other: Value): Value {
|
||||||
val v1 = integerValue()
|
val v1 = integerValue()
|
||||||
val v2 = other.integerValue()
|
val v2 = other.integerValue()
|
||||||
val result = v1.or(v2)
|
val result = v1 or v2
|
||||||
return Value(type, result)
|
return Value(type, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bitxor(other: Value): Value {
|
fun bitxor(other: Value): Value {
|
||||||
val v1 = integerValue()
|
val v1 = integerValue()
|
||||||
val v2 = other.integerValue()
|
val v2 = other.integerValue()
|
||||||
val result = v1.xor(v2)
|
val result = v1 xor v2
|
||||||
return Value(type, result)
|
return Value(type, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,16 +487,16 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
|||||||
|
|
||||||
fun inv(): Value {
|
fun inv(): Value {
|
||||||
return when(type) {
|
return when(type) {
|
||||||
DataType.BYTE -> Value(DataType.BYTE, byteval!!.toInt().inv())
|
DataType.BYTE -> Value(DataType.BYTE, byteval!!.toInt().inv() and 255)
|
||||||
DataType.WORD -> Value(DataType.WORD, wordval!!.inv())
|
DataType.WORD -> Value(DataType.WORD, wordval!!.inv() and 65535)
|
||||||
else -> throw VmExecutionException("not can only work on byte/word")
|
else -> throw VmExecutionException("inv can only work on byte/word")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun inc(): Value {
|
fun inc(): Value {
|
||||||
return when(type) {
|
return when(type) {
|
||||||
DataType.BYTE -> Value(DataType.BYTE, (byteval!! + 1).and(255))
|
DataType.BYTE -> Value(DataType.BYTE, (byteval!! + 1) and 255)
|
||||||
DataType.WORD -> Value(DataType.WORD, (wordval!! + 1).and(65535))
|
DataType.WORD -> Value(DataType.WORD, (wordval!! + 1) and 65535)
|
||||||
DataType.FLOAT -> Value(DataType.FLOAT, floatval!! + 1)
|
DataType.FLOAT -> Value(DataType.FLOAT, floatval!! + 1)
|
||||||
else -> throw VmExecutionException("inc can only work on byte/word/float")
|
else -> throw VmExecutionException("inc can only work on byte/word/float")
|
||||||
}
|
}
|
||||||
@ -487,8 +504,8 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
|||||||
|
|
||||||
fun dec(): Value {
|
fun dec(): Value {
|
||||||
return when(type) {
|
return when(type) {
|
||||||
DataType.BYTE -> Value(DataType.BYTE, (byteval!! - 1).and(255))
|
DataType.BYTE -> Value(DataType.BYTE, (byteval!! - 1) and 255)
|
||||||
DataType.WORD -> Value(DataType.WORD, (wordval!! - 1).and(65535))
|
DataType.WORD -> Value(DataType.WORD, (wordval!! - 1) and 65535)
|
||||||
DataType.FLOAT -> Value(DataType.FLOAT, floatval!! - 1)
|
DataType.FLOAT -> Value(DataType.FLOAT, floatval!! - 1)
|
||||||
else -> throw VmExecutionException("dec can only work on byte/word/float")
|
else -> throw VmExecutionException("dec can only work on byte/word/float")
|
||||||
}
|
}
|
||||||
@ -496,7 +513,7 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
|||||||
|
|
||||||
fun lsb(): Value {
|
fun lsb(): Value {
|
||||||
return when(type) {
|
return when(type) {
|
||||||
DataType.BYTE -> Value(DataType.BYTE, byteval!!.toInt() and 255)
|
DataType.BYTE -> Value(DataType.BYTE, byteval!!)
|
||||||
DataType.WORD -> Value(DataType.WORD, wordval!! and 255)
|
DataType.WORD -> Value(DataType.WORD, wordval!! and 255)
|
||||||
else -> throw VmExecutionException("not can only work on byte/word")
|
else -> throw VmExecutionException("not can only work on byte/word")
|
||||||
}
|
}
|
||||||
@ -504,7 +521,7 @@ class Value(val type: DataType, numericvalue: Number?, val stringvalue: String?=
|
|||||||
|
|
||||||
fun msb(): Value {
|
fun msb(): Value {
|
||||||
return when(type) {
|
return when(type) {
|
||||||
DataType.BYTE -> Value(DataType.BYTE, byteval!!.toInt() ushr 8 and 255)
|
DataType.BYTE -> Value(DataType.BYTE, 0)
|
||||||
DataType.WORD -> Value(DataType.WORD, wordval!! ushr 8 and 255)
|
DataType.WORD -> Value(DataType.WORD, wordval!! ushr 8 and 255)
|
||||||
else -> throw VmExecutionException("not can only work on byte/word")
|
else -> throw VmExecutionException("not can only work on byte/word")
|
||||||
}
|
}
|
||||||
@ -543,13 +560,13 @@ class LabelInstr(val name: String) : Instruction(opcode = Opcode.NOP) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class VmExecutionException(msg: String?) : Exception(msg)
|
class VmExecutionException(msg: String?) : Exception(msg)
|
||||||
|
|
||||||
private class VmTerminationException(msg: String?) : Exception(msg)
|
class VmTerminationException(msg: String?) : Exception(msg)
|
||||||
|
|
||||||
private class VmBreakpointException : Exception("breakpoint")
|
class VmBreakpointException : Exception("breakpoint")
|
||||||
|
|
||||||
private class MyStack<T> : Stack<T>() {
|
class MyStack<T> : Stack<T>() {
|
||||||
fun peek(amount: Int) : List<T> {
|
fun peek(amount: Int) : List<T> {
|
||||||
return this.toList().subList(max(0, size-amount), size)
|
return this.toList().subList(max(0, size-amount), size)
|
||||||
}
|
}
|
||||||
@ -822,48 +839,56 @@ class Program (val name: String,
|
|||||||
|
|
||||||
|
|
||||||
class StackVm(val traceOutputFile: String?) {
|
class StackVm(val traceOutputFile: String?) {
|
||||||
private val mem = Memory()
|
val mem = Memory()
|
||||||
private val evalstack = MyStack<Value>() // evaluation stack
|
val evalstack = MyStack<Value>() // evaluation stack
|
||||||
private val callstack = MyStack<Instruction>() // subroutine call stack
|
val callstack = MyStack<Instruction>() // subroutine call stack
|
||||||
private var variables = mutableMapOf<String, Value>() // all variables (set of all vars used by all blocks/subroutines) key = their fully scoped name
|
var sourceLine = "" // meta info about current line in source file
|
||||||
private var P_carry: Boolean = false
|
private set
|
||||||
private var P_irqd: Boolean = false
|
var P_carry: Boolean = false
|
||||||
|
private set
|
||||||
|
var P_irqd: Boolean = false
|
||||||
|
private set
|
||||||
|
var variables = mutableMapOf<String, Value>() // all variables (set of all vars used by all blocks/subroutines) key = their fully scoped name
|
||||||
|
private set
|
||||||
private var program = listOf<Instruction>()
|
private var program = listOf<Instruction>()
|
||||||
private var traceOutput = if(traceOutputFile!=null) PrintStream(File(traceOutputFile), "utf-8") else null
|
private var traceOutput = if(traceOutputFile!=null) PrintStream(File(traceOutputFile), "utf-8") else null
|
||||||
private lateinit var currentIns: Instruction
|
private lateinit var currentIns: Instruction
|
||||||
private lateinit var canvas: BitmapScreenPanel
|
private var canvas: BitmapScreenPanel? = null
|
||||||
private val rnd = Random()
|
private val rnd = Random()
|
||||||
private var sourceLine = ""
|
|
||||||
|
|
||||||
fun load(program: Program, canvas: BitmapScreenPanel) {
|
fun load(program: Program, canvas: BitmapScreenPanel?) {
|
||||||
this.program = program.program
|
this.program = program.program
|
||||||
this.canvas = canvas
|
this.canvas = canvas
|
||||||
this.variables = program.variables.toMutableMap()
|
variables = program.variables.toMutableMap()
|
||||||
if(this.variables.contains("A") ||
|
if(this.variables.contains("A") ||
|
||||||
this.variables.contains("X") ||
|
variables.contains("X") ||
|
||||||
this.variables.contains("Y") ||
|
variables.contains("Y") ||
|
||||||
this.variables.contains("XY") ||
|
variables.contains("XY") ||
|
||||||
this.variables.contains("AX") ||
|
variables.contains("AX") ||
|
||||||
this.variables.contains("AY"))
|
variables.contains("AY"))
|
||||||
throw VmExecutionException("program contains variable(s) for the reserved registers A,X,...")
|
throw VmExecutionException("program contains variable(s) for the reserved registers A,X,...")
|
||||||
// define the 'registers'
|
// define the 'registers'
|
||||||
this.variables["A"] = Value(DataType.BYTE, 0)
|
variables["A"] = Value(DataType.BYTE, 0)
|
||||||
this.variables["X"] = Value(DataType.BYTE, 0)
|
variables["X"] = Value(DataType.BYTE, 0)
|
||||||
this.variables["Y"] = Value(DataType.BYTE, 0)
|
variables["Y"] = Value(DataType.BYTE, 0)
|
||||||
this.variables["AX"] = Value(DataType.WORD, 0)
|
variables["AX"] = Value(DataType.WORD, 0)
|
||||||
this.variables["AY"] = Value(DataType.WORD, 0)
|
variables["AY"] = Value(DataType.WORD, 0)
|
||||||
this.variables["XY"] = Value(DataType.WORD, 0)
|
variables["XY"] = Value(DataType.WORD, 0)
|
||||||
|
|
||||||
initMemory(program.memory)
|
initMemory(program.memory)
|
||||||
|
evalstack.clear()
|
||||||
|
callstack.clear()
|
||||||
|
P_carry = false
|
||||||
|
P_irqd = false
|
||||||
|
sourceLine = ""
|
||||||
currentIns = this.program[0]
|
currentIns = this.program[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
fun step() {
|
fun step(instructionCount: Int = 10000) {
|
||||||
// step is invoked every 1/100 sec
|
// step is invoked every 1/100 sec
|
||||||
// we execute 10k instructions in one go so we end up doing 1 million vm instructions per second
|
// we execute 10k instructions in one go so we end up doing 1 million vm instructions per second
|
||||||
val instructionsPerStep = 10000
|
|
||||||
val start = System.currentTimeMillis()
|
val start = System.currentTimeMillis()
|
||||||
for(i:Int in 0..instructionsPerStep) {
|
for(i:Int in 1..instructionCount) {
|
||||||
try {
|
try {
|
||||||
currentIns = dispatch(currentIns)
|
currentIns = dispatch(currentIns)
|
||||||
|
|
||||||
@ -890,6 +915,7 @@ class StackVm(val traceOutputFile: String?) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initMemory(memory: Map<Int, List<Value>>) {
|
private fun initMemory(memory: Map<Int, List<Value>>) {
|
||||||
|
mem.clear()
|
||||||
for (meminit in memory) {
|
for (meminit in memory) {
|
||||||
var address = meminit.key
|
var address = meminit.key
|
||||||
for (value in meminit.value) {
|
for (value in meminit.value) {
|
||||||
@ -936,14 +962,19 @@ class StackVm(val traceOutputFile: String?) {
|
|||||||
Opcode.DUP -> evalstack.push(evalstack.peek())
|
Opcode.DUP -> evalstack.push(evalstack.peek())
|
||||||
Opcode.ARRAY -> {
|
Opcode.ARRAY -> {
|
||||||
val amount = ins.arg!!.integerValue()
|
val amount = ins.arg!!.integerValue()
|
||||||
|
if(amount<=0)
|
||||||
|
throw VmExecutionException("array size must be > 0")
|
||||||
val array = mutableListOf<Int>()
|
val array = mutableListOf<Int>()
|
||||||
|
var arrayDt = DataType.ARRAY
|
||||||
for (i in 1..amount) {
|
for (i in 1..amount) {
|
||||||
val value = evalstack.pop()
|
val value = evalstack.pop()
|
||||||
if(value.type!=DataType.BYTE && value.type!=DataType.WORD)
|
if(value.type!=DataType.BYTE && value.type!=DataType.WORD)
|
||||||
throw VmExecutionException("array requires values to be all byte/word")
|
throw VmExecutionException("array requires values to be all byte/word")
|
||||||
array.add(value.integerValue())
|
if(value.type==DataType.WORD)
|
||||||
|
arrayDt = DataType.ARRAY_W
|
||||||
|
array.add(0, value.integerValue())
|
||||||
}
|
}
|
||||||
evalstack.push(Value(DataType.ARRAY, null, arrayvalue = array.toIntArray()))
|
evalstack.push(Value(arrayDt, null, arrayvalue = array.toIntArray()))
|
||||||
}
|
}
|
||||||
Opcode.DISCARD -> evalstack.pop()
|
Opcode.DISCARD -> evalstack.pop()
|
||||||
Opcode.SWAP -> {
|
Opcode.SWAP -> {
|
||||||
@ -1080,17 +1111,17 @@ class StackVm(val traceOutputFile: String?) {
|
|||||||
// plot pixel at (x, y, color) from stack
|
// plot pixel at (x, y, color) from stack
|
||||||
val color = evalstack.pop()
|
val color = evalstack.pop()
|
||||||
val (y, x) = evalstack.pop2()
|
val (y, x) = evalstack.pop2()
|
||||||
canvas.setPixel(x.integerValue(), y.integerValue(), color.integerValue())
|
canvas?.setPixel(x.integerValue(), y.integerValue(), color.integerValue())
|
||||||
}
|
}
|
||||||
Syscall.GFX_CLEARSCR -> {
|
Syscall.GFX_CLEARSCR -> {
|
||||||
val color = evalstack.pop()
|
val color = evalstack.pop()
|
||||||
canvas.clearScreen(color.integerValue())
|
canvas?.clearScreen(color.integerValue())
|
||||||
}
|
}
|
||||||
Syscall.GFX_TEXT -> {
|
Syscall.GFX_TEXT -> {
|
||||||
val text = evalstack.pop()
|
val text = evalstack.pop()
|
||||||
val color = evalstack.pop()
|
val color = evalstack.pop()
|
||||||
val (y, x) = evalstack.pop2()
|
val (y, x) = evalstack.pop2()
|
||||||
canvas.writeText(x.integerValue(), y.integerValue(), text.stringvalue!!, color.integerValue())
|
canvas?.writeText(x.integerValue(), y.integerValue(), text.stringvalue!!, color.integerValue())
|
||||||
}
|
}
|
||||||
Syscall.FUNC_RND -> evalstack.push(Value(DataType.BYTE, rnd.nextInt() and 255))
|
Syscall.FUNC_RND -> evalstack.push(Value(DataType.BYTE, rnd.nextInt() and 255))
|
||||||
Syscall.FUNC_RNDW -> evalstack.push(Value(DataType.WORD, rnd.nextInt() and 65535))
|
Syscall.FUNC_RNDW -> evalstack.push(Value(DataType.WORD, rnd.nextInt() and 65535))
|
||||||
@ -1108,8 +1139,6 @@ class StackVm(val traceOutputFile: String?) {
|
|||||||
Syscall.FUNC_SIN -> evalstack.push(Value(DataType.FLOAT, sin(evalstack.pop().numericValue().toDouble())))
|
Syscall.FUNC_SIN -> evalstack.push(Value(DataType.FLOAT, sin(evalstack.pop().numericValue().toDouble())))
|
||||||
Syscall.FUNC_COS -> evalstack.push(Value(DataType.FLOAT, cos(evalstack.pop().numericValue().toDouble())))
|
Syscall.FUNC_COS -> evalstack.push(Value(DataType.FLOAT, cos(evalstack.pop().numericValue().toDouble())))
|
||||||
Syscall.FUNC_ROUND -> evalstack.push(Value(DataType.WORD, evalstack.pop().numericValue().toDouble().roundToInt()))
|
Syscall.FUNC_ROUND -> evalstack.push(Value(DataType.WORD, evalstack.pop().numericValue().toDouble().roundToInt()))
|
||||||
Syscall.FUNC_P_CARRY -> P_carry = evalstack.pop().asBooleanValue
|
|
||||||
Syscall.FUNC_P_IRQD -> P_irqd = evalstack.pop().asBooleanValue
|
|
||||||
Syscall.FUNC_ABS -> {
|
Syscall.FUNC_ABS -> {
|
||||||
val value = evalstack.pop()
|
val value = evalstack.pop()
|
||||||
val absValue=
|
val absValue=
|
||||||
@ -1131,7 +1160,6 @@ class StackVm(val traceOutputFile: String?) {
|
|||||||
Syscall.FUNC_SQRT -> evalstack.push(Value(DataType.FLOAT, sqrt(evalstack.pop().numericValue().toDouble())))
|
Syscall.FUNC_SQRT -> evalstack.push(Value(DataType.FLOAT, sqrt(evalstack.pop().numericValue().toDouble())))
|
||||||
Syscall.FUNC_RAD -> evalstack.push(Value(DataType.FLOAT, Math.toRadians(evalstack.pop().numericValue().toDouble())))
|
Syscall.FUNC_RAD -> evalstack.push(Value(DataType.FLOAT, Math.toRadians(evalstack.pop().numericValue().toDouble())))
|
||||||
Syscall.FUNC_DEG -> evalstack.push(Value(DataType.FLOAT, Math.toDegrees(evalstack.pop().numericValue().toDouble())))
|
Syscall.FUNC_DEG -> evalstack.push(Value(DataType.FLOAT, Math.toDegrees(evalstack.pop().numericValue().toDouble())))
|
||||||
Syscall.FUNC_FLT -> evalstack.push(Value(DataType.FLOAT, evalstack.pop().numericValue().toDouble()))
|
|
||||||
Syscall.FUNC_FLOOR -> {
|
Syscall.FUNC_FLOOR -> {
|
||||||
val value = evalstack.pop()
|
val value = evalstack.pop()
|
||||||
val result =
|
val result =
|
||||||
@ -1196,6 +1224,8 @@ class StackVm(val traceOutputFile: String?) {
|
|||||||
|
|
||||||
Opcode.SEC -> P_carry = true
|
Opcode.SEC -> P_carry = true
|
||||||
Opcode.CLC -> P_carry = false
|
Opcode.CLC -> P_carry = false
|
||||||
|
Opcode.SEI -> P_irqd = true
|
||||||
|
Opcode.CLI -> P_irqd = false
|
||||||
Opcode.TERMINATE -> throw VmTerminationException("terminate instruction")
|
Opcode.TERMINATE -> throw VmTerminationException("terminate instruction")
|
||||||
Opcode.BREAKPOINT -> throw VmBreakpointException()
|
Opcode.BREAKPOINT -> throw VmBreakpointException()
|
||||||
|
|
||||||
|
1245
compiler/test/StackVMOpcodes.kt
Normal file
1245
compiler/test/StackVMOpcodes.kt
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user