mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 01:29:28 +00:00
removed broken unused symbol clean up for now
streamlined irq routine handling and compilation
This commit is contained in:
parent
4d7279b004
commit
c5d251073f
@ -1,16 +0,0 @@
|
||||
%option enable_floats
|
||||
|
||||
~ main {
|
||||
|
||||
memory byte jiffyclockHi = $a0
|
||||
memory byte jiffyclockMid = $a1
|
||||
memory byte jiffyclockLo = $a2
|
||||
|
||||
|
||||
sub start() -> () {
|
||||
_vm_gfx_pixel(jiffyclockLo,190,jiffyclockHi)
|
||||
_vm_gfx_pixel(jiffyclockLo,191,jiffyclockMid)
|
||||
_vm_gfx_pixel(jiffyclockLo,192,jiffyclockLo)
|
||||
return
|
||||
}
|
||||
}
|
@ -50,3 +50,20 @@
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
; ---- 60hz irq handling routine------
|
||||
|
||||
~ irq {
|
||||
|
||||
memory byte jiffyclockHi = $a0
|
||||
memory byte jiffyclockMid = $a1
|
||||
memory byte jiffyclockLo = $a2
|
||||
|
||||
sub irq() -> () {
|
||||
_vm_gfx_pixel(jiffyclockLo,190,jiffyclockHi)
|
||||
_vm_gfx_pixel(jiffyclockLo,191,jiffyclockMid)
|
||||
_vm_gfx_pixel(jiffyclockLo,192,jiffyclockLo)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ fun main(args: Array<String>) {
|
||||
while(true) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = moduleAst.simplifyExpressions(namespace)
|
||||
val optsDone2 = moduleAst.optimizeStatements(namespace, allScopedSymbolDefinitions)
|
||||
val optsDone2 = moduleAst.optimizeStatements(namespace)
|
||||
if(optsDone1 + optsDone2 == 0)
|
||||
break
|
||||
}
|
||||
|
@ -17,11 +17,9 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
val program = Program.load(args.first())
|
||||
val irqProgram = Program("irq", mutableListOf(), emptyMap(), emptyMap(), emptyMap())
|
||||
//val irqProgram = Program.load("irq_stackvm.txt")
|
||||
val vm = StackVm(traceOutputFile = "stackvm.log")
|
||||
val vm = StackVm(traceOutputFile = null)
|
||||
val dialog = ScreenDialog()
|
||||
vm.load(program, irqProgram, dialog.canvas)
|
||||
vm.load(program, dialog.canvas)
|
||||
EventQueue.invokeLater {
|
||||
dialog.pack()
|
||||
dialog.isVisible = true
|
||||
|
@ -269,8 +269,6 @@ interface INameScope {
|
||||
val position: Position
|
||||
var statements: MutableList<IStatement>
|
||||
|
||||
fun usedNames(): Set<String>
|
||||
|
||||
fun registerUsedName(name: String)
|
||||
|
||||
fun subScopes() = statements.asSequence().filter { it is INameScope }.map { it as INameScope }.associate { it.name to it }
|
||||
@ -364,7 +362,6 @@ object BuiltinFunctionScopePlaceholder : INameScope {
|
||||
override val name = "<<builtin-functions-scope-placeholder>>"
|
||||
override val position = Position("<<placeholder>>", 0, 0, 0)
|
||||
override var statements = mutableListOf<IStatement>()
|
||||
override fun usedNames(): Set<String> = throw NotImplementedError("not implemented on sub-scopes")
|
||||
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
||||
}
|
||||
|
||||
@ -395,10 +392,7 @@ class Module(override val name: String,
|
||||
}
|
||||
|
||||
override fun definingScope(): INameScope = GlobalNamespace("<<<global>>>", statements, position)
|
||||
override fun usedNames(): Set<String> = throw NotImplementedError("not implemented on sub-scopes")
|
||||
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -408,8 +402,6 @@ private class GlobalNamespace(override val name: String,
|
||||
|
||||
private val scopedNamesUsed: MutableSet<String> = mutableSetOf("main", "main.start") // main and main.start are always used
|
||||
|
||||
override fun usedNames(): Set<String> = scopedNamesUsed
|
||||
|
||||
override fun lookup(scopedName: List<String>, statement: Node): IStatement? {
|
||||
if(BuiltinFunctionNames.contains(scopedName.last())) {
|
||||
// builtin functions always exist, return a dummy statement for them
|
||||
@ -459,7 +451,6 @@ class Block(override val name: String,
|
||||
return "Block(name=$name, address=$address, ${statements.size} statements)"
|
||||
}
|
||||
|
||||
override fun usedNames(): Set<String> = throw NotImplementedError("not implemented on sub-scopes")
|
||||
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
||||
}
|
||||
|
||||
@ -1203,32 +1194,32 @@ class FunctionCall(override var target: IdentifierReference,
|
||||
if(constVal!=null)
|
||||
return constVal.resultingDatatype(namespace)
|
||||
val stmt = target.targetStatement(namespace) ?: return null
|
||||
if(stmt is BuiltinFunctionStatementPlaceholder) {
|
||||
if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" ||
|
||||
target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
|
||||
return null // these have no return value
|
||||
}
|
||||
return builtinFunctionReturnType(target.nameInSource[0], this.arglist, namespace)
|
||||
}
|
||||
else if(stmt is Subroutine) {
|
||||
if(stmt.returnvalues.isEmpty()) {
|
||||
return null // no return value
|
||||
}
|
||||
if(stmt.returnvalues.size==1) {
|
||||
if(stmt.returnvalues[0].register!=null) {
|
||||
return when(stmt.returnvalues[0].register!!) {
|
||||
Register.A, Register.X, Register.Y -> DataType.BYTE
|
||||
Register.AX, Register.AY, Register.XY -> DataType.WORD
|
||||
}
|
||||
} else if(stmt.returnvalues[0].statusflag!=null) {
|
||||
val flag = stmt.returnvalues[0].statusflag!!
|
||||
TODO("return value in status flag $flag")
|
||||
when (stmt) {
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" ||
|
||||
target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
|
||||
return null // these have no return value
|
||||
}
|
||||
return builtinFunctionReturnType(target.nameInSource[0], this.arglist, namespace)
|
||||
}
|
||||
TODO("return type for subroutine with multiple return values $stmt")
|
||||
}
|
||||
else if(stmt is Label) {
|
||||
return null
|
||||
is Subroutine -> {
|
||||
if(stmt.returnvalues.isEmpty()) {
|
||||
return null // no return value
|
||||
}
|
||||
if(stmt.returnvalues.size==1) {
|
||||
if(stmt.returnvalues[0].register!=null) {
|
||||
return when(stmt.returnvalues[0].register!!) {
|
||||
Register.A, Register.X, Register.Y -> DataType.BYTE
|
||||
Register.AX, Register.AY, Register.XY -> DataType.WORD
|
||||
}
|
||||
} else if(stmt.returnvalues[0].statusflag!=null) {
|
||||
val flag = stmt.returnvalues[0].statusflag!!
|
||||
TODO("return value in status flag $flag")
|
||||
}
|
||||
}
|
||||
TODO("return type for subroutine with multiple return values $stmt")
|
||||
}
|
||||
is Label -> return null
|
||||
}
|
||||
TODO("datatype of functioncall to $stmt")
|
||||
}
|
||||
@ -1293,7 +1284,6 @@ class Subroutine(override val name: String,
|
||||
return "Subroutine(name=$name, address=$address, parameters=$parameters, returnvalues=$returnvalues, ${statements.size} statements)"
|
||||
}
|
||||
|
||||
override fun usedNames(): Set<String> = throw NotImplementedError("not implemented on sub-scopes")
|
||||
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
||||
}
|
||||
|
||||
@ -1653,23 +1643,26 @@ private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
||||
fun makeLiteral(text: String, radix: Int, forceWord: Boolean): NumericLiteral {
|
||||
val integer: Int
|
||||
var datatype = DataType.BYTE
|
||||
if(radix==10) {
|
||||
integer = text.toInt()
|
||||
datatype = when(integer) {
|
||||
in 0..255 -> DataType.BYTE
|
||||
in 256..65535 -> DataType.WORD
|
||||
else -> DataType.FLOAT
|
||||
when (radix) {
|
||||
10 -> {
|
||||
integer = text.toInt()
|
||||
datatype = when(integer) {
|
||||
in 0..255 -> DataType.BYTE
|
||||
in 256..65535 -> DataType.WORD
|
||||
else -> DataType.FLOAT
|
||||
}
|
||||
}
|
||||
} else if(radix==2) {
|
||||
if(text.length>8)
|
||||
datatype = DataType.WORD
|
||||
integer = text.toInt(2)
|
||||
} else if(radix==16) {
|
||||
if(text.length>2)
|
||||
datatype = DataType.WORD
|
||||
integer = text.toInt(16)
|
||||
} else {
|
||||
throw FatalAstException("invalid radix")
|
||||
2 -> {
|
||||
if(text.length>8)
|
||||
datatype = DataType.WORD
|
||||
integer = text.toInt(2)
|
||||
}
|
||||
16 -> {
|
||||
if(text.length>2)
|
||||
datatype = DataType.WORD
|
||||
integer = text.toInt(16)
|
||||
}
|
||||
else -> throw FatalAstException("invalid radix")
|
||||
}
|
||||
return NumericLiteral(integer, if(forceWord) DataType.WORD else datatype)
|
||||
}
|
||||
|
@ -62,7 +62,16 @@ class AstChecker(private val namespace: INameScope, private val compilerOptions:
|
||||
checkResult.add(SyntaxError("missing program entrypoint ('start' subroutine in 'main' block)", module.position))
|
||||
} else {
|
||||
if(startSub.parameters.isNotEmpty() || startSub.returnvalues.isNotEmpty())
|
||||
checkResult.add(SyntaxError("program entrypoint subroutine can't have parameters and/or return values", module.position))
|
||||
checkResult.add(SyntaxError("program entrypoint subroutine can't have parameters and/or return values", startSub.position))
|
||||
}
|
||||
|
||||
// there can be an optional 'irq' block with a 'irq' subroutine in it,
|
||||
// which will be used as the 60hz irq routine in the vm if it's present.
|
||||
val irqBlock = module.statements.singleOrNull { it is Block && it.name=="irq" } as? Block?
|
||||
val irqSub = irqBlock?.subScopes()?.get("irq") as? Subroutine
|
||||
if(irqSub!=null) {
|
||||
if(irqSub.parameters.isNotEmpty() || irqSub.returnvalues.isNotEmpty())
|
||||
checkResult.add(SyntaxError("irq entrypoint subroutine can't have parameters and/or return values", irqSub.position))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ package prog8.compiler
|
||||
import prog8.ast.*
|
||||
|
||||
|
||||
abstract class Zeropage(protected val options: CompilationOptions) {
|
||||
abstract class Zeropage(private val options: CompilationOptions) {
|
||||
|
||||
private val allocations = mutableMapOf<Int, Pair<String, DataType>>()
|
||||
val free = mutableListOf<Int>() // subclasses must set this to the appropriate free locations.
|
||||
|
@ -2,7 +2,6 @@ package prog8.optimizing
|
||||
|
||||
import prog8.ast.AstException
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.IStatement
|
||||
import prog8.ast.Module
|
||||
import prog8.parser.ParsingFailedError
|
||||
|
||||
@ -36,10 +35,9 @@ fun Module.constantFold(globalNamespace: INameScope) {
|
||||
}
|
||||
|
||||
|
||||
fun Module.optimizeStatements(globalNamespace: INameScope, allScopedSymbolDefinitions: MutableMap<String, IStatement>): Int {
|
||||
fun Module.optimizeStatements(globalNamespace: INameScope): Int {
|
||||
val optimizer = StatementOptimizer(globalNamespace)
|
||||
this.process(optimizer)
|
||||
optimizer.removeUnusedNodes(globalNamespace.usedNames(), allScopedSymbolDefinitions)
|
||||
if(optimizer.optimizationsDone > 0)
|
||||
println("[${this.name}] Debug: ${optimizer.optimizationsDone} statement optimizations performed")
|
||||
this.linkParents() // re-link in final configuration
|
||||
|
@ -6,6 +6,9 @@ import prog8.functions.BuiltinFunctionsWithoutSideEffects
|
||||
|
||||
|
||||
/*
|
||||
todo remove unused blocks
|
||||
todo remove unused variables
|
||||
todo remove unused subroutines
|
||||
todo remove if statements with empty statement blocks
|
||||
todo replace if statements with only else block
|
||||
todo statement optimization: create augmented assignment from assignment that only refers to its lvalue (A=A+10, A=4*A, ...)
|
||||
@ -26,18 +29,7 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso
|
||||
|
||||
private var statementsToRemove = mutableListOf<IStatement>()
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
val target = globalNamespace.lookup(functionCall.target.nameInSource, functionCall)
|
||||
if(target!=null)
|
||||
used(target)
|
||||
return super.process(functionCall)
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCallStatement): IStatement {
|
||||
val target = globalNamespace.lookup(functionCall.target.nameInSource, functionCall)
|
||||
if(target!=null)
|
||||
used(target)
|
||||
|
||||
if(functionCall.target.nameInSource.size==1 && BuiltinFunctionNames.contains(functionCall.target.nameInSource[0])) {
|
||||
val functionName = functionCall.target.nameInSource[0]
|
||||
if (BuiltinFunctionsWithoutSideEffects.contains(functionName)) {
|
||||
@ -49,15 +41,6 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso
|
||||
return super.process(functionCall)
|
||||
}
|
||||
|
||||
override fun process(jump: Jump): IStatement {
|
||||
if(jump.identifier!=null) {
|
||||
val target = globalNamespace.lookup(jump.identifier.nameInSource, jump)
|
||||
if (target != null)
|
||||
used(target)
|
||||
}
|
||||
return super.process(jump)
|
||||
}
|
||||
|
||||
override fun process(ifStatement: IfStatement): IStatement {
|
||||
super.process(ifStatement)
|
||||
val constvalue = ifStatement.condition.constValue(globalNamespace)
|
||||
@ -123,46 +106,4 @@ class StatementOptimizer(private val globalNamespace: INameScope) : IAstProcesso
|
||||
}
|
||||
return repeatLoop
|
||||
}
|
||||
|
||||
private fun used(stmt: IStatement) {
|
||||
val scopedName = when (stmt) {
|
||||
is Label -> stmt.scopedname
|
||||
is Subroutine -> stmt.scopedname
|
||||
else -> throw AstException("invalid call target node type: ${stmt::class}")
|
||||
}
|
||||
globalNamespace.registerUsedName(scopedName)
|
||||
}
|
||||
|
||||
fun removeUnusedNodes(usedNames: Set<String>, allScopedSymbolDefinitions: MutableMap<String, IStatement>) {
|
||||
val symbolsToRemove = mutableListOf<String>()
|
||||
|
||||
for ((name, value) in allScopedSymbolDefinitions) {
|
||||
if(!usedNames.contains(name)) {
|
||||
val parentScope = value.parent as INameScope
|
||||
val localname = name.substringAfterLast(".")
|
||||
// printing every possible node that is removed can result in many dozens of warnings.
|
||||
// we chose to just print the blocks that aren't used.
|
||||
if(value is Block)
|
||||
println("${value.position} Info: block '$localname' is never used")
|
||||
if(value is VarDecl) {
|
||||
val scope = value.definingScope() as? Subroutine
|
||||
if(scope!=null && scope.parameters.any { it.name==localname})
|
||||
println("${value.position} Info: parameter '$localname' is never used")
|
||||
else
|
||||
println("${value.position} Info: variable '$localname' is never used")
|
||||
}
|
||||
parentScope.removeStatement(value)
|
||||
symbolsToRemove.add(name)
|
||||
optimizationsDone++
|
||||
}
|
||||
}
|
||||
|
||||
for(name in symbolsToRemove) {
|
||||
allScopedSymbolDefinitions.remove(name)
|
||||
}
|
||||
|
||||
for(stmt in statementsToRemove) {
|
||||
stmt.definingScope().removeStatement(stmt)
|
||||
}
|
||||
}
|
||||
}
|
@ -849,7 +849,7 @@ class Program (val name: String,
|
||||
}
|
||||
|
||||
|
||||
class StackVm(val traceOutputFile: String?) {
|
||||
class StackVm(private var traceOutputFile: String?) {
|
||||
val mem = Memory()
|
||||
var P_carry: Boolean = false
|
||||
private set
|
||||
@ -857,26 +857,24 @@ class StackVm(val traceOutputFile: String?) {
|
||||
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 irqProgram = listOf<Instruction>()
|
||||
var evalstack = MyStack<Value>()
|
||||
private set
|
||||
var callstack = MyStack<Instruction>()
|
||||
private set
|
||||
private var program = listOf<Instruction>()
|
||||
private var traceOutput = if(traceOutputFile!=null) PrintStream(File(traceOutputFile), "utf-8") else null
|
||||
private var canvas: BitmapScreenPanel? = null
|
||||
private val rnd = Random()
|
||||
private val bootTime = System.currentTimeMillis()
|
||||
private lateinit var currentIns: Instruction
|
||||
private var irqStartInstruction: Instruction? = null // set to first instr of irq routine, if any
|
||||
var sourceLine: String = ""
|
||||
private set
|
||||
|
||||
fun load(program: Program, irqProgram: Program?, canvas: BitmapScreenPanel?) {
|
||||
fun load(program: Program, canvas: BitmapScreenPanel?) {
|
||||
this.program = program.program
|
||||
this.irqProgram = irqProgram?.program ?: mutableListOf(Instruction(Opcode.NOP))
|
||||
this.canvas = canvas
|
||||
variables = program.variables.toMutableMap()
|
||||
irqCurrentVariables = irqProgram?.variables?.toMutableMap() ?: mutableMapOf()
|
||||
if(variables.contains("A") ||
|
||||
variables.contains("X") ||
|
||||
variables.contains("Y") ||
|
||||
@ -884,13 +882,6 @@ class StackVm(val traceOutputFile: String?) {
|
||||
variables.contains("AX") ||
|
||||
variables.contains("AY"))
|
||||
throw VmExecutionException("program contains variable(s) for the reserved registers A,X,...")
|
||||
if(irqCurrentVariables.contains("A") ||
|
||||
irqCurrentVariables.contains("X") ||
|
||||
irqCurrentVariables.contains("Y") ||
|
||||
irqCurrentVariables.contains("XY") ||
|
||||
irqCurrentVariables.contains("AX") ||
|
||||
irqCurrentVariables.contains("AY"))
|
||||
throw VmExecutionException("irqProgram contains variable(s) for the reserved registers A,X,...")
|
||||
// define the 'registers'
|
||||
variables["A"] = Value(DataType.BYTE, 0)
|
||||
variables["X"] = Value(DataType.BYTE, 0)
|
||||
@ -898,24 +889,15 @@ class StackVm(val traceOutputFile: String?) {
|
||||
variables["AX"] = Value(DataType.WORD, 0)
|
||||
variables["AY"] = Value(DataType.WORD, 0)
|
||||
variables["XY"] = Value(DataType.WORD, 0)
|
||||
irqCurrentVariables["A"] = Value(DataType.BYTE, 0)
|
||||
irqCurrentVariables["X"] = Value(DataType.BYTE, 0)
|
||||
irqCurrentVariables["Y"] = Value(DataType.BYTE, 0)
|
||||
irqCurrentVariables["AX"] = Value(DataType.WORD, 0)
|
||||
irqCurrentVariables["AY"] = Value(DataType.WORD, 0)
|
||||
irqCurrentVariables["XY"] = Value(DataType.WORD, 0)
|
||||
|
||||
initMemory(program.memory)
|
||||
evalstack.clear()
|
||||
callstack.clear()
|
||||
irqEvalStack.clear()
|
||||
irqCallStack.clear()
|
||||
P_carry = false
|
||||
P_irqd = false
|
||||
sourceLine = ""
|
||||
irqCurrentLine = ""
|
||||
currentIns = this.program[0]
|
||||
irqCurrentIns = this.irqProgram[0]
|
||||
irqStartInstruction = program.labels["irq.irq"]
|
||||
}
|
||||
|
||||
fun step(instructionCount: Int = 10000) {
|
||||
@ -1546,40 +1528,48 @@ class StackVm(val traceOutputFile: String?) {
|
||||
mem.setByte(0x00a1, (jiffies ushr 8 and 255).toShort())
|
||||
mem.setByte(0x00a2, (jiffies and 255).toShort())
|
||||
|
||||
try {
|
||||
// execute the irq routine
|
||||
this.step(Int.MAX_VALUE)
|
||||
} catch(vmt: VmTerminationException) {
|
||||
// irq routine ended
|
||||
if(irqStartInstruction!=null) {
|
||||
try {
|
||||
// execute the irq routine
|
||||
this.step(Int.MAX_VALUE)
|
||||
} catch (vmt: VmTerminationException) {
|
||||
// irq routine ended
|
||||
}
|
||||
}
|
||||
|
||||
if(evalstack.isNotEmpty())
|
||||
throw VmExecutionException("irq: eval stack is not empty at exit from irq program")
|
||||
if(callstack.isNotEmpty())
|
||||
throw VmExecutionException("irq: call stack is not empty at exit from irq program")
|
||||
swapIrqExecutionContexts(false)
|
||||
P_irqd=false
|
||||
}
|
||||
|
||||
private lateinit var irqCurrentIns: Instruction
|
||||
private var irqCurrentVariables = mutableMapOf<String, Value>()
|
||||
private var irqCurrentLine: String = ""
|
||||
private var irqEvalStack = MyStack<Value>()
|
||||
private var irqCallStack = MyStack<Instruction>()
|
||||
private var irqCarry = false
|
||||
private var irqStoredEvalStack = MyStack<Value>()
|
||||
private var irqStoredCallStack = MyStack<Instruction>()
|
||||
private var irqStoredCarry = false
|
||||
private var irqStoredTraceOutputFile: String? = null
|
||||
private var irqStoredMainInstruction: Instruction = Instruction(Opcode.TERMINATE)
|
||||
|
||||
private fun swapIrqExecutionContexts(startingIrq: Boolean) {
|
||||
irqCarry = P_carry.also { P_carry = irqCarry }
|
||||
irqProgram = program.also { program = irqProgram }
|
||||
irqCurrentIns = currentIns.also { currentIns = irqCurrentIns }
|
||||
irqCurrentVariables = variables.also {variables = irqCurrentVariables }
|
||||
irqCurrentLine = sourceLine.also { sourceLine = irqCurrentLine }
|
||||
irqEvalStack = evalstack.also { evalstack = irqEvalStack }
|
||||
irqCallStack = callstack.also { callstack = irqCallStack }
|
||||
if(startingIrq) {
|
||||
currentIns = program.first()
|
||||
sourceLine = ""
|
||||
irqStoredMainInstruction = currentIns
|
||||
irqStoredCallStack = callstack
|
||||
irqStoredEvalStack = evalstack
|
||||
irqStoredCarry = P_carry
|
||||
irqStoredTraceOutputFile = traceOutputFile
|
||||
|
||||
currentIns = irqStartInstruction ?: Instruction(Opcode.RETURN)
|
||||
callstack = MyStack()
|
||||
evalstack = MyStack()
|
||||
P_carry = false
|
||||
traceOutputFile = null
|
||||
} else {
|
||||
if(evalstack.isNotEmpty())
|
||||
throw VmExecutionException("irq: eval stack is not empty at exit from irq program")
|
||||
if(callstack.isNotEmpty())
|
||||
throw VmExecutionException("irq: call stack is not empty at exit from irq program")
|
||||
currentIns = irqStoredMainInstruction
|
||||
callstack = irqStoredCallStack
|
||||
evalstack = irqStoredEvalStack
|
||||
P_carry = irqStoredCarry
|
||||
traceOutputFile = irqStoredTraceOutputFile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ class TestStackVmOpcodes {
|
||||
@Test
|
||||
fun testInitAndNop() {
|
||||
val ins = mutableListOf(Instruction(Opcode.NOP))
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertEquals(6, vm.variables.size)
|
||||
assertTrue(vm.variables.containsKey("XY"))
|
||||
assertTrue(vm.variables.containsKey("A"))
|
||||
@ -65,7 +65,7 @@ class TestStackVmOpcodes {
|
||||
@Test
|
||||
fun testBreakpoint() {
|
||||
val ins = mutableListOf(Instruction(Opcode.BREAKPOINT))
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertFailsWith<VmBreakpointException> {
|
||||
vm.step()
|
||||
}
|
||||
@ -76,7 +76,7 @@ class TestStackVmOpcodes {
|
||||
@Test
|
||||
fun testLine() {
|
||||
val ins = mutableListOf(Instruction(Opcode.LINE, Value(DataType.STR, null, "line 99")))
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertEquals("", vm.sourceLine)
|
||||
vm.step(1)
|
||||
assertEquals("line 99", vm.sourceLine)
|
||||
@ -85,7 +85,7 @@ class TestStackVmOpcodes {
|
||||
@Test
|
||||
fun testSECandSEIandCLCandCLI() {
|
||||
val ins = mutableListOf(Instruction(Opcode.SEC), Instruction(Opcode.SEI), Instruction(Opcode.CLC), Instruction(Opcode.CLI))
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertFalse(vm.P_carry)
|
||||
assertFalse(vm.P_irqd)
|
||||
vm.step(1)
|
||||
@ -105,7 +105,7 @@ class TestStackVmOpcodes {
|
||||
@Test
|
||||
fun testPush() {
|
||||
val ins = mutableListOf(Instruction(Opcode.PUSH, Value(DataType.FLOAT, 42.999)))
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertThat(vm.evalstack, empty())
|
||||
vm.step(1)
|
||||
assertEquals(1, vm.evalstack.size)
|
||||
@ -122,7 +122,7 @@ class TestStackVmOpcodes {
|
||||
val mem=mapOf(0x2000 to listOf(Value(DataType.WORD, 0x42ea)),
|
||||
0x3000 to listOf(Value(DataType.WORD, 0x42ea)),
|
||||
0x4000 to listOf(Value(DataType.FLOAT, 42.25)))
|
||||
vm.load(makeProg(ins, mem=mem), null, null)
|
||||
vm.load(makeProg(ins, mem=mem), null)
|
||||
assertEquals(0xea, vm.mem.getByte(0x2000))
|
||||
assertEquals(0x42, vm.mem.getByte(0x2001))
|
||||
assertEquals(0xea, vm.mem.getByte(0x3000))
|
||||
@ -141,7 +141,7 @@ class TestStackVmOpcodes {
|
||||
@Test
|
||||
fun testPushVar() {
|
||||
val ins = mutableListOf(Instruction(Opcode.PUSH_VAR, Value(DataType.STR, null, "varname")))
|
||||
vm.load(makeProg(ins, mapOf("varname" to Value(DataType.FLOAT, 42.999))), null, null)
|
||||
vm.load(makeProg(ins, mapOf("varname" to Value(DataType.FLOAT, 42.999))), null)
|
||||
assertEquals(7, vm.variables.size)
|
||||
assertTrue(vm.variables.containsKey("varname"))
|
||||
assertTrue(vm.variables.containsKey("XY"))
|
||||
@ -159,7 +159,7 @@ class TestStackVmOpcodes {
|
||||
val ins = mutableListOf(
|
||||
Instruction(Opcode.PUSH, Value(DataType.FLOAT, 42.999)),
|
||||
Instruction(Opcode.DUP))
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertThat(vm.evalstack, empty())
|
||||
vm.step(2)
|
||||
assertEquals(2, vm.evalstack.size)
|
||||
@ -174,7 +174,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.PUSH, Value(DataType.WORD, 9999)),
|
||||
Instruction(Opcode.SWAP)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertThat(vm.evalstack, empty())
|
||||
vm.step(3)
|
||||
assertEquals(2, vm.evalstack.size)
|
||||
@ -188,7 +188,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.PUSH, Value(DataType.FLOAT, 42.999)),
|
||||
Instruction(Opcode.PUSH, Value(DataType.FLOAT, 3.1415)),
|
||||
Instruction(Opcode.DISCARD))
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertThat(vm.evalstack, empty())
|
||||
vm.step(2)
|
||||
assertEquals(2, vm.evalstack.size)
|
||||
@ -204,7 +204,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.PUSH, Value(DataType.WORD, 222)),
|
||||
Instruction(Opcode.PUSH, Value(DataType.WORD, 333)),
|
||||
Instruction(Opcode.ARRAY, Value(DataType.WORD, 2)))
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertThat(vm.evalstack, empty())
|
||||
vm.step(4)
|
||||
assertEquals(2, vm.evalstack.size)
|
||||
@ -218,7 +218,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.PUSH, Value(DataType.BYTE, 22)),
|
||||
Instruction(Opcode.PUSH, Value(DataType.BYTE, 33)),
|
||||
Instruction(Opcode.ARRAY, Value(DataType.WORD, 2)))
|
||||
vm.load(makeProg(ins2), null, null)
|
||||
vm.load(makeProg(ins2), null)
|
||||
assertThat(vm.evalstack, empty())
|
||||
vm.step(4)
|
||||
assertEquals(2, vm.evalstack.size)
|
||||
@ -232,7 +232,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.PUSH, Value(DataType.WORD, 222)),
|
||||
Instruction(Opcode.PUSH, Value(DataType.FLOAT, 333.33)),
|
||||
Instruction(Opcode.ARRAY, Value(DataType.WORD, 2)))
|
||||
vm.load(makeProg(ins3), null, null)
|
||||
vm.load(makeProg(ins3), null)
|
||||
assertFailsWith<VmExecutionException> {
|
||||
vm.step(4)
|
||||
}
|
||||
@ -247,7 +247,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.POP_MEM, Value(DataType.WORD, 0x2000)),
|
||||
Instruction(Opcode.POP_MEM, Value(DataType.WORD, 0x3000)),
|
||||
Instruction(Opcode.POP_MEM, Value(DataType.WORD, 0x4000)))
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertEquals(0, vm.mem.getWord(0x2000))
|
||||
assertEquals(0, vm.mem.getWord(0x3000))
|
||||
assertEquals(0.0, vm.mem.getFloat(0x4000))
|
||||
@ -274,7 +274,7 @@ class TestStackVmOpcodes {
|
||||
"var2" to Value(DataType.WORD, 0),
|
||||
"var3" to Value(DataType.FLOAT, 0)
|
||||
)
|
||||
vm.load(makeProg(ins, vars), null, null)
|
||||
vm.load(makeProg(ins, vars), null)
|
||||
assertEquals(9, vm.variables.size)
|
||||
vm.step(6)
|
||||
assertEquals(Value(DataType.BYTE, 123), vm.variables["var1"])
|
||||
@ -287,7 +287,7 @@ class TestStackVmOpcodes {
|
||||
val vars2 = mapOf(
|
||||
"var1" to Value(DataType.BYTE, 0)
|
||||
)
|
||||
vm.load(makeProg(ins2, vars2), null, null)
|
||||
vm.load(makeProg(ins2, vars2), null)
|
||||
assertEquals(7, vm.variables.size)
|
||||
assertFailsWith<VmExecutionException> {
|
||||
vm.step(2)
|
||||
@ -597,7 +597,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.NEG),
|
||||
Instruction(Opcode.NEG)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertThat(vm.evalstack, empty())
|
||||
vm.step(2)
|
||||
assertEquals(1, vm.evalstack.size)
|
||||
@ -610,7 +610,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.PUSH, Value(DataType.WORD, 1234)),
|
||||
Instruction(Opcode.NEG)
|
||||
)
|
||||
vm.load(makeProg(ins2), null, null)
|
||||
vm.load(makeProg(ins2), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.WORD, 64302), vm.evalstack.pop())
|
||||
|
||||
@ -618,7 +618,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.PUSH, Value(DataType.BYTE, 12)),
|
||||
Instruction(Opcode.NEG)
|
||||
)
|
||||
vm.load(makeProg(ins3), null, null)
|
||||
vm.load(makeProg(ins3), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.BYTE, 244), vm.evalstack.pop())
|
||||
}
|
||||
@ -632,7 +632,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.INV),
|
||||
Instruction(Opcode.INV)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertThat(vm.evalstack, empty())
|
||||
vm.step(3)
|
||||
assertEquals(2, vm.evalstack.size)
|
||||
@ -645,7 +645,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.PUSH, Value(DataType.FLOAT, 1234.33)),
|
||||
Instruction(Opcode.INV)
|
||||
)
|
||||
vm.load(makeProg(ins2), null, null)
|
||||
vm.load(makeProg(ins2), null)
|
||||
assertFailsWith<VmExecutionException> {
|
||||
vm.step(2)
|
||||
}
|
||||
@ -661,7 +661,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.LSB),
|
||||
Instruction(Opcode.LSB)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(4)
|
||||
assertEquals(Value(DataType.BYTE, 0x31), vm.evalstack.pop())
|
||||
vm.step(1)
|
||||
@ -681,7 +681,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.MSB),
|
||||
Instruction(Opcode.MSB)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(4)
|
||||
assertEquals(Value(DataType.BYTE, 0xea), vm.evalstack.pop())
|
||||
vm.step(1)
|
||||
@ -699,7 +699,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.B2WORD),
|
||||
Instruction(Opcode.B2WORD)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(3)
|
||||
assertEquals(Value(DataType.WORD, 0x0045), vm.evalstack.pop())
|
||||
assertFailsWith<VmExecutionException> {
|
||||
@ -715,7 +715,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.MSB2WORD),
|
||||
Instruction(Opcode.MSB2WORD)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(3)
|
||||
assertEquals(Value(DataType.WORD, 0x4500), vm.evalstack.pop())
|
||||
assertFailsWith<VmExecutionException> {
|
||||
@ -731,7 +731,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.B2FLOAT),
|
||||
Instruction(Opcode.B2FLOAT)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(3)
|
||||
assertEquals(Value(DataType.FLOAT, 123.0), vm.evalstack.pop())
|
||||
assertFailsWith<VmExecutionException> {
|
||||
@ -747,7 +747,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.W2FLOAT),
|
||||
Instruction(Opcode.W2FLOAT)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(3)
|
||||
assertEquals(Value(DataType.FLOAT, 12345.0), vm.evalstack.pop())
|
||||
assertFailsWith<VmExecutionException> {
|
||||
@ -765,7 +765,7 @@ class TestStackVmOpcodes {
|
||||
)
|
||||
val mem=mapOf(0x2000 to listOf(Value(DataType.BYTE, 100), Value(DataType.BYTE, 255)),
|
||||
0x3000 to listOf(Value(DataType.WORD, 0x42ea), Value(DataType.WORD, 0xffff)))
|
||||
vm.load(makeProg(ins, mem=mem), null, null)
|
||||
vm.load(makeProg(ins, mem=mem), null)
|
||||
vm.step(4)
|
||||
assertEquals(101, vm.mem.getByte(0x2000))
|
||||
assertEquals(0, vm.mem.getByte(0x2001))
|
||||
@ -782,7 +782,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.INC_VAR, Value(DataType.STR, null, "var2")))
|
||||
val vars = mapOf("var1" to Value(DataType.WORD, 65534),
|
||||
"var2" to Value(DataType.BYTE, 254))
|
||||
vm.load(makeProg(ins, vars = vars), null, null)
|
||||
vm.load(makeProg(ins, vars = vars), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.WORD, 65535), vm.variables["var1"])
|
||||
assertEquals(Value(DataType.BYTE, 255), vm.variables["var2"])
|
||||
@ -800,7 +800,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.DEC_VAR, Value(DataType.STR, null, "var2")))
|
||||
val vars = mapOf("var1" to Value(DataType.WORD,1),
|
||||
"var2" to Value(DataType.BYTE, 1))
|
||||
vm.load(makeProg(ins, vars = vars), null, null)
|
||||
vm.load(makeProg(ins, vars = vars), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.WORD, 0), vm.variables["var1"])
|
||||
assertEquals(Value(DataType.BYTE, 0), vm.variables["var2"])
|
||||
@ -819,7 +819,7 @@ class TestStackVmOpcodes {
|
||||
)
|
||||
val mem=mapOf(0x2000 to listOf(Value(DataType.BYTE, 100), Value(DataType.BYTE, 0)),
|
||||
0x3000 to listOf(Value(DataType.WORD, 0x42ea), Value(DataType.WORD, 0)))
|
||||
vm.load(makeProg(ins, mem=mem), null, null)
|
||||
vm.load(makeProg(ins, mem=mem), null)
|
||||
vm.step(4)
|
||||
assertEquals(99, vm.mem.getByte(0x2000))
|
||||
assertEquals(255, vm.mem.getByte(0x2001))
|
||||
@ -838,7 +838,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.PUSH, Value(DataType.WORD, 25544)),
|
||||
Instruction(Opcode.SYSCALL, Value(DataType.BYTE, Syscall.FUNC_SIN.callNr))
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(4)
|
||||
|
||||
val rndb1 = vm.evalstack.pop()
|
||||
@ -1047,7 +1047,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, Value(DataType.STR, null, stringvalue = "string2")))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null, null)
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("", vm.sourceLine)
|
||||
vm.step(3)
|
||||
@ -1067,7 +1067,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, Value(DataType.STR, null, stringvalue = "string2")))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null, null)
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
assertFalse(vm.P_carry)
|
||||
vm.step(2)
|
||||
assertEquals("", vm.sourceLine)
|
||||
@ -1088,7 +1088,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, Value(DataType.STR, null, stringvalue = "string2")))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null, null)
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("", vm.sourceLine)
|
||||
vm.step(3)
|
||||
@ -1108,7 +1108,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, Value(DataType.STR, null, stringvalue = "string2")))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null, null)
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("", vm.sourceLine)
|
||||
vm.step(3)
|
||||
@ -1130,7 +1130,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, Value(DataType.STR, null, stringvalue = "string2")))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null, null)
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("", vm.sourceLine)
|
||||
vm.step(2)
|
||||
@ -1152,7 +1152,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, Value(DataType.STR, null, stringvalue = "string2")))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null, null)
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("", vm.sourceLine)
|
||||
vm.step(3)
|
||||
@ -1169,7 +1169,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, Value(DataType.STR, null, stringvalue = "string2")))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null, null)
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("string2", vm.sourceLine)
|
||||
assertEquals(0, vm.callstack.size)
|
||||
@ -1184,7 +1184,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, Value(DataType.STR, null, stringvalue = "string1"))
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
assertFailsWith<VmTerminationException> {
|
||||
vm.step(1)
|
||||
}
|
||||
@ -1208,7 +1208,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.RETURN)
|
||||
)
|
||||
val labels = mapOf("label" to ins[3]) // points to the LINE instruction
|
||||
vm.load(makeProg(ins, labels = labels), null, null)
|
||||
vm.load(makeProg(ins, labels = labels), null)
|
||||
vm.step(1)
|
||||
assertEquals("", vm.sourceLine)
|
||||
assertEquals(1, vm.callstack.size)
|
||||
@ -1244,7 +1244,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.DISCARD),
|
||||
Instruction(Opcode.SHR) // error
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(6)
|
||||
assertEquals(Value(DataType.BYTE, 124), vm.evalstack.peek())
|
||||
vm.step(2)
|
||||
@ -1282,7 +1282,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.DISCARD),
|
||||
Instruction(Opcode.SHL) // error
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(6)
|
||||
assertEquals(Value(DataType.BYTE, 242), vm.evalstack.peek())
|
||||
vm.step(2)
|
||||
@ -1313,7 +1313,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.ROR), // 0b00100110 c=1
|
||||
Instruction(Opcode.ROR) // 0b10010011 c=0 (original value after 9 rors)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.BYTE, 0b01001001), vm.evalstack.peek())
|
||||
assertTrue(vm.P_carry)
|
||||
@ -1351,7 +1351,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.ROR),
|
||||
Instruction(Opcode.ROR) // 0b1001001100001101 c=0 (original value after 17 rors)
|
||||
)
|
||||
vm.load(makeProg(ins2), null, null)
|
||||
vm.load(makeProg(ins2), null)
|
||||
vm.step(3)
|
||||
assertEquals(Value(DataType.WORD, 0b0100100110000110), vm.evalstack.peek())
|
||||
assertTrue(vm.P_carry)
|
||||
@ -1378,7 +1378,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.ROL), // 0b01001001 c=1
|
||||
Instruction(Opcode.ROL) // 0b10010011 c=0 (original value after 9 rors)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.BYTE, 0b00100110), vm.evalstack.peek())
|
||||
assertTrue(vm.P_carry)
|
||||
@ -1416,7 +1416,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.ROL),
|
||||
Instruction(Opcode.ROL) // 0b1001001100001101 c=0 (original value after 17 rors)
|
||||
)
|
||||
vm.load(makeProg(ins2), null, null)
|
||||
vm.load(makeProg(ins2), null)
|
||||
vm.step(3)
|
||||
assertEquals(Value(DataType.WORD, 0b0010011000011010), vm.evalstack.peek())
|
||||
assertTrue(vm.P_carry)
|
||||
@ -1442,7 +1442,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.ROR2),
|
||||
Instruction(Opcode.ROR2) // 0b10010011 (original value after 8 rors)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.BYTE, 0b11001001), vm.evalstack.peek())
|
||||
assertFalse(vm.P_carry)
|
||||
@ -1472,7 +1472,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.ROR2),
|
||||
Instruction(Opcode.ROR2) // 0b1001001100001101 (original value after 16 rors)
|
||||
)
|
||||
vm.load(makeProg(ins2), null, null)
|
||||
vm.load(makeProg(ins2), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.WORD, 0b1100100110000110), vm.evalstack.peek())
|
||||
assertFalse(vm.P_carry)
|
||||
@ -1496,7 +1496,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.ROL2),
|
||||
Instruction(Opcode.ROL2) // 0b10010011 (original value after 8 rols)
|
||||
)
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.BYTE, 0b00100111), vm.evalstack.peek())
|
||||
assertFalse(vm.P_carry)
|
||||
@ -1524,7 +1524,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.ROL2),
|
||||
Instruction(Opcode.ROL2) // 0b1001001100001101 (original value after 16 rols)
|
||||
)
|
||||
vm.load(makeProg(ins2), null, null)
|
||||
vm.load(makeProg(ins2), null)
|
||||
vm.step(2)
|
||||
assertEquals(Value(DataType.WORD, 0b0010011000011011), vm.evalstack.peek())
|
||||
assertFalse(vm.P_carry)
|
||||
@ -1544,7 +1544,7 @@ class TestStackVmOpcodes {
|
||||
ins.add(Instruction(Opcode.PUSH, vars.next()))
|
||||
ins.add(Instruction(operator))
|
||||
}
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
for(expectedValue in expected) {
|
||||
vm.step(3)
|
||||
assertEquals(Value(DataType.BYTE, expectedValue), vm.evalstack.pop())
|
||||
@ -1558,7 +1558,7 @@ class TestStackVmOpcodes {
|
||||
ins.add(Instruction(Opcode.PUSH, value))
|
||||
for (i in 1 until values.size)
|
||||
ins.add(Instruction(operator))
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(values.size)
|
||||
assertEquals(values.size, vm.evalstack.size)
|
||||
for (expectedVal in expected) {
|
||||
@ -1579,7 +1579,7 @@ class TestStackVmOpcodes {
|
||||
ins.add(Instruction(operator))
|
||||
ins.add(Instruction(Opcode.DISCARD))
|
||||
}
|
||||
vm.load(makeProg(ins), null, null)
|
||||
vm.load(makeProg(ins), null)
|
||||
vm.step(values.size)
|
||||
assertEquals(values.size, vm.evalstack.size)
|
||||
for (expectedVal in expected) {
|
||||
|
@ -25,11 +25,19 @@ The compiler will link everything together into one output program at the end.
|
||||
|
||||
The compiler is invoked with the command:
|
||||
|
||||
``$ @todo``
|
||||
``$ ./compile.sh modulefile.p8``
|
||||
|
||||
It produces an assembly source code file which in turn will (automatically) be passed to
|
||||
the `64tass <https://sourceforge.net/projects/tass64/>`_ cross assembler tool
|
||||
that assembles it into the final program.
|
||||
For now, it produces intermediate code for the stack-based "StackVM" virtual machine. You can run this code
|
||||
with the command:
|
||||
|
||||
``$ ./stackvm.sh modulefile_stackvm.txt``
|
||||
|
||||
|
||||
.. todo::
|
||||
... Real assembler output ...
|
||||
It produces an assembly source code file which in turn will (automatically) be passed to
|
||||
the `64tass <https://sourceforge.net/projects/tass64/>`_ cross assembler tool
|
||||
that assembles it into the final program.
|
||||
|
||||
|
||||
Module source code files
|
||||
|
@ -135,6 +135,14 @@ Program Start and Entry Point
|
||||
Your program must have a single entry point where code execution begins.
|
||||
The compiler expects a ``start`` subroutine in the ``main`` block for this,
|
||||
taking no parameters and having no return value.
|
||||
|
||||
.. sidebar::
|
||||
60hz IRQ entry point
|
||||
|
||||
When running the generated code on the StackVm virtual machine,
|
||||
it will use the ``irq`` subroutine in the ``irq`` block for the
|
||||
60hz irq routine. This is optional.
|
||||
|
||||
As any subroutine, it has to end with a ``return`` statement (or a ``goto`` call)::
|
||||
|
||||
~ main {
|
||||
@ -144,12 +152,16 @@ As any subroutine, it has to end with a ``return`` statement (or a ``goto`` call
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
The ``main`` module is always relocated to the start of your programs
|
||||
address space, and the ``start`` subroutine (the entrypoint) will be on the
|
||||
first address. This will also be the address that the BASIC loader program (if generated)
|
||||
calls with the SYS statement.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Variables and values
|
||||
--------------------
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user