removed broken unused symbol clean up for now

streamlined irq routine handling and compilation
This commit is contained in:
Irmen de Jong 2018-09-28 21:28:16 +02:00
parent 4d7279b004
commit c5d251073f
14 changed files with 193 additions and 243 deletions

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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)
}

View File

@ -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))
}
}

View File

@ -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.

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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
}
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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
--------------------