From fd07ae5225cd0ac1f89e63def0e6318450b4c0ce Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 6 Mar 2023 21:42:08 +0100 Subject: [PATCH] fix various IR file and symboltable issues --- codeCore/src/prog8/code/SymbolTable.kt | 9 +- codeCore/src/prog8/code/SymbolTableMaker.kt | 6 +- .../codegen/intermediate/BuiltinFuncGen.kt | 4 +- .../codegen/intermediate/ExpressionGen.kt | 3 + .../prog8/codegen/intermediate/IRCodeGen.kt | 168 +++++---- codeGenIntermediate/test/TestVmCodeGen.kt | 339 ++++++++++++++++++ compiler/res/version.txt | 2 +- compiler/test/TestLaunchEmu.kt | 2 +- docs/source/todo.rst | 8 + .../src/prog8/intermediate/IRFileReader.kt | 16 +- .../src/prog8/intermediate/IRFileWriter.kt | 167 ++++++--- .../src/prog8/intermediate/IRInstructions.kt | 8 +- .../src/prog8/intermediate/IRSymbolTable.kt | 9 +- intermediate/test/TestIRFileInOut.kt | 12 +- virtualmachine/test/TestVm.kt | 2 +- 15 files changed, 595 insertions(+), 160 deletions(-) diff --git a/codeCore/src/prog8/code/SymbolTable.kt b/codeCore/src/prog8/code/SymbolTable.kt index 489402fa6..dd2672e9f 100644 --- a/codeCore/src/prog8/code/SymbolTable.kt +++ b/codeCore/src/prog8/code/SymbolTable.kt @@ -133,7 +133,7 @@ open class StNode(val name: String, } private val scopedNameList: List by lazy { - if(type== StNodeType.GLOBAL) + if(type==StNodeType.GLOBAL) emptyList() else parent.scopedNameList + name @@ -142,7 +142,7 @@ open class StNode(val name: String, private fun lookup(scopedName: List): StNode? { // a scoped name refers to a name in another namespace, and always stars from the root. var node = this - while(node.type!= StNodeType.GLOBAL) + while(node.type!=StNodeType.GLOBAL) node = node.parent for(name in scopedName) { @@ -201,6 +201,11 @@ class StMemVar(name: String, val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte astNode: PtNode) : StNode(name, StNodeType.MEMVAR, astNode) { + + init{ + if(dt in ArrayDatatypes || dt == DataType.STR) + require(length!=null) { "memory mapped array or string must have known length" } + } } class StMemorySlab( diff --git a/codeCore/src/prog8/code/SymbolTableMaker.kt b/codeCore/src/prog8/code/SymbolTableMaker.kt index baaa125ff..b793b4c1a 100644 --- a/codeCore/src/prog8/code/SymbolTableMaker.kt +++ b/codeCore/src/prog8/code/SymbolTableMaker.kt @@ -26,11 +26,11 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY), PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY), PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY), - PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 256u, Position.DUMMY), - PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 256u, Position.DUMMY) + PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 128u, Position.DUMMY), + PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 128u, Position.DUMMY) ).forEach { it.parent = program - st.add(StMemVar(it.name, it.type, it.address, null, it)) + st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it)) } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index cadf83029..ce18f7b33 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -265,7 +265,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe val addressReg = codeGen.registers.nextFree() result += exprGen.translateExpression(call.args[0], addressReg, -1) result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg) + it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = addressReg) } } } else { @@ -300,7 +300,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe val addressReg = codeGen.registers.nextFree() result += exprGen.translateExpression(call.args[0], addressReg, -1) result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg) + it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = addressReg) } } } else { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 05fe03c67..7eba99e4c 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -16,6 +16,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { require(codeGen.registers.peekNext() > resultRegister || resultRegister >= SyscallRegisterBase) { "no more registers for expression ${expr.position}" } + require((expr.type!=DataType.FLOAT && resultRegister>=0) || (expr.type==DataType.FLOAT && resultFpRegister>=0)) { + "mismatched result register for expression datatype ${expr.type} ${expr.position}" + } return when (expr) { is PtMachineRegister -> { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index fdcff9aa1..652406e3f 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -358,6 +358,12 @@ class IRCodeGen( } private fun labelFirstChunk(chunks: IRCodeChunks, label: String): IRCodeChunks { + if(chunks.isEmpty()) { + return listOf( + IRCodeChunk(label, null) + ) + } + require(chunks.isNotEmpty() && label.isNotBlank()) val first = chunks[0] if(first.label!=null) { @@ -883,16 +889,25 @@ class IRCodeGen( } private fun translate(ifElse: PtIfElse): IRCodeChunks { - if(ifElse.condition.operator !in ComparisonOperators) + val condition = ifElse.condition + if(condition.operator !in ComparisonOperators) throw AssemblyError("if condition should only be a binary comparison expression") - val signed = ifElse.condition.left.type in SignedDatatypes - val irDt = irType(ifElse.condition.left.type) - + val signed = condition.left.type in SignedDatatypes + val irDtLeft = irType(condition.left.type) val goto = ifElse.ifScope.children.firstOrNull() as? PtJump - if(goto!=null && ifElse.elseScope.children.isEmpty()) { - // special case the form: if goto - val result = mutableListOf() + return when { + goto!=null && ifElse.elseScope.children.isEmpty() -> translateIfFollowedByJustGoto(ifElse, goto, irDtLeft, signed) + constValue(condition.right) == 0.0 -> translateIfElseZeroComparison(ifElse, irDtLeft, signed) + else -> translateIfElseNonZeroComparison(ifElse, irDtLeft, signed) + } + } + + private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList { + val result = mutableListOf() + if(irDtLeft==IRDataType.FLOAT) { + TODO("float comparison followed by goto") + } else { val leftRegNum = registers.nextFree() val rightRegNum = registers.nextFree() result += expressionEval.translateExpression(ifElse.condition.left, leftRegNum, -1) @@ -900,7 +915,7 @@ class IRCodeGen( val opcode: Opcode val firstReg: Int val secondReg: Int - when(ifElse.condition.operator) { + when (ifElse.condition.operator) { "==" -> { opcode = Opcode.BEQ firstReg = leftRegNum @@ -913,48 +928,93 @@ class IRCodeGen( } "<" -> { // swapped '>' - opcode = if(signed) Opcode.BGTS else Opcode.BGT + opcode = if (signed) Opcode.BGTS else Opcode.BGT firstReg = rightRegNum secondReg = leftRegNum } ">" -> { - opcode = if(signed) Opcode.BGTS else Opcode.BGT + opcode = if (signed) Opcode.BGTS else Opcode.BGT firstReg = leftRegNum secondReg = rightRegNum } "<=" -> { // swapped '>=' - opcode = if(signed) Opcode.BGES else Opcode.BGE + opcode = if (signed) Opcode.BGES else Opcode.BGE firstReg = rightRegNum secondReg = leftRegNum } ">=" -> { - opcode = if(signed) Opcode.BGES else Opcode.BGE + opcode = if (signed) Opcode.BGES else Opcode.BGE firstReg = leftRegNum secondReg = rightRegNum } else -> throw AssemblyError("invalid comparison operator") } - if(goto.address!=null) - addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, value = goto.address?.toInt()), null) - else if(goto.generatedLabel!=null) - addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, labelSymbol = goto.generatedLabel), null) + if (goto.address != null) + addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, value = goto.address?.toInt()), null) + else if (goto.generatedLabel != null) + addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null) else - addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, labelSymbol = goto.identifier!!.name), null) - return result + addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null) } + return result + } - fun translateNonZeroComparison(): IRCodeChunks { - val result = mutableListOf() + private fun translateIfElseZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks { + if(irDtLeft==IRDataType.FLOAT) { + TODO("float zero compare via fcomp instruction") + } else { + // integer comparisons + fun equalOrNotEqualZero(elseBranch: Opcode): IRCodeChunks { + val result = mutableListOf() + val leftReg = registers.nextFree() + result += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1) + if(ifElse.elseScope.children.isNotEmpty()) { + // if and else parts + val elseLabel = createLabelName() + val afterIfLabel = createLabelName() + addInstr(result, IRInstruction(elseBranch, irDtLeft, reg1=leftReg, labelSymbol = elseLabel), null) + result += translateNode(ifElse.ifScope) + addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null) + result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel) + result += IRCodeChunk(afterIfLabel, null) + } else { + // only if part + val afterIfLabel = createLabelName() + addInstr(result, IRInstruction(elseBranch, irDtLeft, reg1=leftReg, labelSymbol = afterIfLabel), null) + result += translateNode(ifElse.ifScope) + result += IRCodeChunk(afterIfLabel, null) + } + return result + } + return when (ifElse.condition.operator) { + "==" -> equalOrNotEqualZero(Opcode.BNZ) + "!=" -> equalOrNotEqualZero(Opcode.BZ) + "<" -> if (signed) equalOrNotEqualZero(Opcode.BGEZS) else throw AssemblyError("unsigned < 0 shouldn't occur in codegen") + ">" -> if (signed) equalOrNotEqualZero(Opcode.BLEZS) else throw AssemblyError("unsigned > 0 shouldn't occur in codegen") + "<=" -> if (signed) equalOrNotEqualZero(Opcode.BGZS) else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen") + ">=" -> if (signed) equalOrNotEqualZero(Opcode.BLZS) else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen") + else -> throw AssemblyError("weird operator") + } + } + } + + private fun translateIfElseNonZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks { + val result = mutableListOf() + + val elseBranchOpcode: Opcode + val elseBranchFirstReg: Int + val elseBranchSecondReg: Int + + if(irDtLeft==IRDataType.FLOAT) { + TODO("float non-zero compare via fcomp instruction") + } else { + // integer comparisons val leftRegNum = registers.nextFree() val rightRegNum = registers.nextFree() result += expressionEval.translateExpression(ifElse.condition.left, leftRegNum, -1) result += expressionEval.translateExpression(ifElse.condition.right, rightRegNum, -1) - - val elseBranchOpcode: Opcode - val elseBranchFirstReg: Int - val elseBranchSecondReg: Int - when(ifElse.condition.operator) { + when (ifElse.condition.operator) { "==" -> { elseBranchOpcode = Opcode.BNE elseBranchFirstReg = leftRegNum @@ -967,36 +1027,37 @@ class IRCodeGen( } "<" -> { // else part when left >= right - elseBranchOpcode = if(signed) Opcode.BGES else Opcode.BGE + elseBranchOpcode = if (signed) Opcode.BGES else Opcode.BGE elseBranchFirstReg = leftRegNum elseBranchSecondReg = rightRegNum } ">" -> { // else part when left <= right --> right >= left - elseBranchOpcode = if(signed) Opcode.BGES else Opcode.BGE + elseBranchOpcode = if (signed) Opcode.BGES else Opcode.BGE elseBranchFirstReg = rightRegNum elseBranchSecondReg = leftRegNum } "<=" -> { // else part when left > right - elseBranchOpcode = if(signed) Opcode.BGTS else Opcode.BGT + elseBranchOpcode = if (signed) Opcode.BGTS else Opcode.BGT elseBranchFirstReg = leftRegNum elseBranchSecondReg = rightRegNum } ">=" -> { // else part when left < right --> right > left - elseBranchOpcode = if(signed) Opcode.BGTS else Opcode.BGT + elseBranchOpcode = if (signed) Opcode.BGTS else Opcode.BGT elseBranchFirstReg = rightRegNum elseBranchSecondReg = leftRegNum } else -> throw AssemblyError("invalid comparison operator") } - if(ifElse.elseScope.children.isNotEmpty()) { // if and else parts val elseLabel = createLabelName() val afterIfLabel = createLabelName() - addInstr(result, IRInstruction(elseBranchOpcode, irDt, reg1=elseBranchFirstReg, reg2=elseBranchSecondReg, labelSymbol = elseLabel), null) + addInstr(result, IRInstruction(elseBranchOpcode, irDtLeft, + reg1=elseBranchFirstReg, reg2=elseBranchSecondReg, + labelSymbol = elseLabel), null) result += translateNode(ifElse.ifScope) addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null) result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel) @@ -1004,52 +1065,15 @@ class IRCodeGen( } else { // only if part val afterIfLabel = createLabelName() - addInstr(result, IRInstruction(elseBranchOpcode, irDt, reg1=elseBranchFirstReg, reg2=elseBranchSecondReg, labelSymbol = afterIfLabel), null) + addInstr(result, IRInstruction(elseBranchOpcode, irDtLeft, + reg1=elseBranchFirstReg, reg2=elseBranchSecondReg, + labelSymbol = afterIfLabel), null) result += translateNode(ifElse.ifScope) result += IRCodeChunk(afterIfLabel, null) } - return result } - fun translateZeroComparison(): IRCodeChunks { - fun equalOrNotEqualZero(elseBranch: Opcode): IRCodeChunks { - val result = mutableListOf() - val leftReg = registers.nextFree() - result += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1) - if(ifElse.elseScope.children.isNotEmpty()) { - // if and else parts - val elseLabel = createLabelName() - val afterIfLabel = createLabelName() - addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, labelSymbol = elseLabel), null) - result += translateNode(ifElse.ifScope) - addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null) - result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel) - result += IRCodeChunk(afterIfLabel, null) - } else { - // only if part - val afterIfLabel = createLabelName() - addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, labelSymbol = afterIfLabel), null) - result += translateNode(ifElse.ifScope) - result += IRCodeChunk(afterIfLabel, null) - } - return result - } - - return when (ifElse.condition.operator) { - "==" -> equalOrNotEqualZero(Opcode.BNZ) - "!=" -> equalOrNotEqualZero(Opcode.BZ) - "<" -> if(signed) equalOrNotEqualZero(Opcode.BGEZS) else throw AssemblyError("unsigned < 0 shouldn't occur in codegen") - ">" -> if(signed) equalOrNotEqualZero(Opcode.BLEZS) else throw AssemblyError("unsigned > 0 shouldn't occur in codegen") - "<=" -> if(signed) equalOrNotEqualZero(Opcode.BGZS) else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen") - ">=" -> if(signed) equalOrNotEqualZero(Opcode.BLZS) else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen") - else -> throw AssemblyError("weird operator") - } - } - - return if(constValue(ifElse.condition.right)==0.0) - translateZeroComparison() - else - translateNonZeroComparison() + return result } private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks { diff --git a/codeGenIntermediate/test/TestVmCodeGen.kt b/codeGenIntermediate/test/TestVmCodeGen.kt index 9206e8b43..055592b5e 100644 --- a/codeGenIntermediate/test/TestVmCodeGen.kt +++ b/codeGenIntermediate/test/TestVmCodeGen.kt @@ -100,4 +100,343 @@ class TestVmCodeGen: FunSpec({ val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks irChunks.size shouldBeGreaterThan 4 } + + test("float comparison expressions against zero") { +//main { +// sub start() { +// float @shared f1 +// +// if f1==0 +// nop +// if f1!=0 +// nop +// if f1>0 +// nop +// if f1<0 +// nop +// } +//} + val codegen = VmCodeGen() + val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) + val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val sub = PtSub("start", emptyList(), null, Position.DUMMY) + sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) + val if1 = PtIfElse(Position.DUMMY) + val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY) + cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) + cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) + if1.add(cmp1) + if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if1) + val if2 = PtIfElse(Position.DUMMY) + val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY) + cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) + cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) + if2.add(cmp2) + if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if2) + val if3 = PtIfElse(Position.DUMMY) + val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY) + cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) + cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) + if3.add(cmp3) + if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if3) + val if4 = PtIfElse(Position.DUMMY) + val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY) + cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) + cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) + if4.add(cmp4) + if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if4) + block.add(sub) + program.add(block) + + val options = getTestOptions() + val st = SymbolTableMaker(program, options).make() + val errors = ErrorReporterForTests() + val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram + val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks + irChunks.size shouldBeGreaterThan 4 + } + + test("float comparison expressions against nonzero") { +//main { +// sub start() { +// float @shared f1 +// +// if f1==42 +// nop +// if f1!=42 +// nop +// if f1>42 +// nop +// if f1<42 +// nop +// } +//} + val codegen = VmCodeGen() + val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) + val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val sub = PtSub("start", emptyList(), null, Position.DUMMY) + sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) + val if1 = PtIfElse(Position.DUMMY) + val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY) + cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) + cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) + if1.add(cmp1) + if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if1) + val if2 = PtIfElse(Position.DUMMY) + val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY) + cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) + cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) + if2.add(cmp2) + if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if2) + val if3 = PtIfElse(Position.DUMMY) + val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY) + cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) + cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) + if3.add(cmp3) + if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if3) + val if4 = PtIfElse(Position.DUMMY) + val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY) + cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) + cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) + if4.add(cmp4) + if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if4) + block.add(sub) + program.add(block) + + val options = getTestOptions() + val st = SymbolTableMaker(program, options).make() + val errors = ErrorReporterForTests() + val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram + val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks + irChunks.size shouldBeGreaterThan 4 + } + + test("float conditional jump") { +//main { +// sub start() { +// float @shared f1 +// +// if f1==42 +// goto $c000 +// if f1>42 +// goto $c000 +// } +//} + val codegen = VmCodeGen() + val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) + val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val sub = PtSub("start", emptyList(), null, Position.DUMMY) + sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) + val if1 = PtIfElse(Position.DUMMY) + val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY) + cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) + cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) + if1.add(cmp1) + if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) }) + if1.add(PtNodeGroup()) + sub.add(if1) + val if2 = PtIfElse(Position.DUMMY) + val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY) + cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) + cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) + if2.add(cmp2) + if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) }) + if2.add(PtNodeGroup()) + sub.add(if2) + block.add(sub) + program.add(block) + + val options = getTestOptions() + val st = SymbolTableMaker(program, options).make() + val errors = ErrorReporterForTests() + val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram + val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks + irChunks.size shouldBeGreaterThan 4 + } + + test("integer comparison expressions against zero") { +//main { +// sub start() { +// byte @shared sb1 +// +// if sb1==0 +// nop +// if sb1!=0 +// nop +// if sb1>0 +// nop +// if sb1<0 +// nop +// } +//} + val codegen = VmCodeGen() + val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) + val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val sub = PtSub("start", emptyList(), null, Position.DUMMY) + sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) + val if1 = PtIfElse(Position.DUMMY) + val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY) + cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) + cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) + if1.add(cmp1) + if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if1) + val if2 = PtIfElse(Position.DUMMY) + val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY) + cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) + cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) + if2.add(cmp2) + if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if2) + val if3 = PtIfElse(Position.DUMMY) + val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY) + cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) + cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) + if3.add(cmp3) + if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if3) + val if4 = PtIfElse(Position.DUMMY) + val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY) + cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) + cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) + if4.add(cmp4) + if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if4) + block.add(sub) + program.add(block) + + val options = getTestOptions() + val st = SymbolTableMaker(program, options).make() + val errors = ErrorReporterForTests() + val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram + val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks + irChunks.size shouldBeGreaterThan 4 + } + + test("integer comparison expressions against nonzero") { +//main { +// sub start() { +// byte @shared sb1 +// +// if sb1==42 +// nop +// if sb1!=42 +// nop +// if sb1>42 +// nop +// if sb1<42 +// nop +// } +//} + val codegen = VmCodeGen() + val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) + val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val sub = PtSub("start", emptyList(), null, Position.DUMMY) + sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) + val if1 = PtIfElse(Position.DUMMY) + val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY) + cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) + cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) + if1.add(cmp1) + if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if1) + val if2 = PtIfElse(Position.DUMMY) + val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY) + cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) + cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) + if2.add(cmp2) + if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if2) + val if3 = PtIfElse(Position.DUMMY) + val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY) + cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) + cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) + if3.add(cmp3) + if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if3) + val if4 = PtIfElse(Position.DUMMY) + val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY) + cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) + cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) + if4.add(cmp4) + if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) + sub.add(if4) + block.add(sub) + program.add(block) + + val options = getTestOptions() + val st = SymbolTableMaker(program, options).make() + val errors = ErrorReporterForTests() + val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram + val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks + irChunks.size shouldBeGreaterThan 4 + } + + test("integer conditional jump") { +//main { +// sub start() { +// ubyte @shared ub1 +// +// if ub1==42 +// goto $c000 +// if ub1>42 +// goto $c000 +// } +//} + val codegen = VmCodeGen() + val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) + val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) + val sub = PtSub("start", emptyList(), null, Position.DUMMY) + sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) + val if1 = PtIfElse(Position.DUMMY) + val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY) + cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY)) + cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY)) + if1.add(cmp1) + if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) }) + if1.add(PtNodeGroup()) + sub.add(if1) + val if2 = PtIfElse(Position.DUMMY) + val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY) + cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY)) + cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY)) + if2.add(cmp2) + if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) }) + if2.add(PtNodeGroup()) + sub.add(if2) + block.add(sub) + program.add(block) + + val options = getTestOptions() + val st = SymbolTableMaker(program, options).make() + val errors = ErrorReporterForTests() + val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram + val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks + irChunks.size shouldBeGreaterThan 4 + } + }) \ No newline at end of file diff --git a/compiler/res/version.txt b/compiler/res/version.txt index 22ec1e649..d9bbf5ea0 100644 --- a/compiler/res/version.txt +++ b/compiler/res/version.txt @@ -1 +1 @@ -8.10 +8.11-dev diff --git a/compiler/test/TestLaunchEmu.kt b/compiler/test/TestLaunchEmu.kt index c2e072fc4..f73a9a44f 100644 --- a/compiler/test/TestLaunchEmu.kt +++ b/compiler/test/TestLaunchEmu.kt @@ -33,7 +33,7 @@ class TestLaunchEmu: FunSpec({ - + """) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 8e0499d74..fb0c59d30 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,14 @@ TODO For next minor release ^^^^^^^^^^^^^^^^^^^^^^ +- fix expericodegen compiler crashes: + float comparison <= , >= , == , != all crash with mismatched result register for FLOAT (cube3d-float uses this) + maze: thinks draw is unused + tehtriz: thinks sound.init routine is unused + textelite: thinks trader.do_local routine is unused , and debug_seed +- fix IR/VM: animals.p8 example somehow doesn't print the animal name in the first question, and exits after 1 animal instead of looping +- fix Github issue with X register https://github.com/irmen/prog8/issues/94 + ... diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index cced0794a..1a8a7a523 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -103,8 +103,8 @@ class IRFileReader { "output" -> outputType = OutputType.valueOf(value) "launcher" -> launcher = CbmPrgLauncherType.valueOf(value) "zeropage" -> zeropage = ZeropageType.valueOf(value) - "loadAddress" -> loadAddress = value.toUInt() - "evalStackBaseAddress" -> evalStackBaseAddress = if(value=="null") null else parseIRValue(value).toUInt() + "loadAddress" -> loadAddress = parseIRValue(value).toUInt() + "evalStackBaseAddress" -> evalStackBaseAddress = if(value=="") null else parseIRValue(value).toUInt() "zpReserved" -> { val (zpstart, zpend) = value.split(',') zpReserved.add(UIntRange(zpstart.toUInt(), zpend.toUInt())) @@ -262,10 +262,10 @@ class IRFileReader { emptyList() else { val slabs = mutableListOf() - val slabPattern = Regex("SLAB (.+) (.+) (.+)") + val slabPattern = Regex("(.+) (.+) (.+)") text.lineSequence().forEach { line -> - // example: "SLAB slabname 4096 0" - val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid SLAB $line") + // example: "slabname 4096 0" + val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid slab $line") val (name, size, align) = match.destructured val dummyNode = PtVariable(name, DataType.ARRAY_UB, ZeropageWish.NOT_IN_ZEROPAGE, null, null, Position.DUMMY) slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), dummyNode)) @@ -331,7 +331,7 @@ class IRFileReader { val attrs = start.attributes.asSequence().associate { it.name.localPart to it.value } val block = IRBlock( attrs.getValue("NAME"), - if(attrs.getValue("ADDRESS")=="null") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(), + if(attrs.getValue("ADDRESS")=="") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(), IRBlock.BlockAlignment.valueOf(attrs.getValue("ALIGN")), parsePosition(attrs.getValue("POS"))) skipText(reader) @@ -364,7 +364,7 @@ class IRFileReader { skipText(reader) val sub = IRSubroutine(attrs.getValue("NAME"), parseParameters(reader), - if(returntype=="null") null else parseDatatype(returntype, false), + if(returntype=="") null else parseDatatype(returntype, false), parsePosition(attrs.getValue("POS"))) skipText(reader) @@ -433,7 +433,7 @@ class IRFileReader { } return IRAsmSubroutine( attrs.getValue("NAME"), - if(attrs.getValue("ADDRESS")=="null") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(), + if(attrs.getValue("ADDRESS")=="") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(), clobberRegs.toSet(), params, returns, diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index 8b8f8217d..862b59ea8 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -2,6 +2,7 @@ package prog8.intermediate import prog8.code.core.* import java.nio.file.Path +import javax.xml.stream.XMLOutputFactory import kotlin.io.path.bufferedWriter import kotlin.io.path.div @@ -9,23 +10,32 @@ import kotlin.io.path.div class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { private val outfile = outfileOverride ?: (irProgram.options.outputDir / ("${irProgram.name}.p8ir")) private val out = outfile.bufferedWriter(charset=Charsets.UTF_8) + private val xml = XMLOutputFactory.newInstance().createXMLStreamWriter(out) private var numChunks = 0 private var numInstr = 0 fun write(): Path { + println("Writing intermediate representation to $outfile") - out.write("\n") - out.write("\n") + xml.writeStartDocument("utf-8", "1.0") + xml.writeEndDocument() + xml.writeCharacters("\n") + xml.writeStartElement("PROGRAM") + xml.writeAttribute("NAME", irProgram.name) + xml.writeCharacters("\n") writeOptions() writeAsmSymbols() writeVariables() - - out.write("\n\n") + xml.writeStartElement("INITGLOBALS") + xml.writeCharacters("\n") writeCodeChunk(irProgram.globalInits) - out.write("\n") + xml.writeEndElement() + xml.writeCharacters("\n\n") writeBlocks() - out.write("\n") + xml.writeEndElement() + xml.writeCharacters("\n") + xml.close() out.close() val used = irProgram.registersUsed() @@ -35,14 +45,21 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { } private fun writeAsmSymbols() { - out.write("\n") - irProgram.asmSymbols.forEach { (name, value) -> out.write("$name=$value\n" )} - out.write("\n") + xml.writeStartElement("ASMSYMBOLS") + xml.writeCharacters("\n") + irProgram.asmSymbols.forEach { (name, value) -> xml.writeCharacters("$name=$value\n" )} + xml.writeEndElement() + xml.writeCharacters("\n") } private fun writeBlocks() { irProgram.blocks.forEach { block -> - out.write("\n\n") + xml.writeStartElement("BLOCK") + xml.writeAttribute("NAME", block.name) + xml.writeAttribute("ADDRESS", block.address?.toHex() ?: "") + xml.writeAttribute("ALIGN", block.alignment.toString()) + xml.writeAttribute("POS", block.position.toString()) + xml.writeCharacters("\n") block.children.forEach { child -> when(child) { is IRAsmSubroutine -> { @@ -51,25 +68,40 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { if(ret.reg.registerOrPair!=null) "${ret.reg.registerOrPair}:${ret.dt.toString().lowercase()}" else "${ret.reg.statusflag}:${ret.dt.toString().lowercase()}" }.joinToString(",") - out.write("\n") - out.write("\n") + xml.writeStartElement("ASMSUB") + xml.writeAttribute("NAME", child.label) + xml.writeAttribute("ADDRESS", child.address?.toHex() ?: "") + xml.writeAttribute("CLOBBERS", clobbers) + xml.writeAttribute("RETURNS", returns) + xml.writeAttribute("POS", child.position.toString()) + xml.writeCharacters("\n") + xml.writeStartElement("ASMPARAMS") + xml.writeCharacters("\n") child.parameters.forEach { ret -> val reg = if(ret.reg.registerOrPair!=null) ret.reg.registerOrPair.toString() else ret.reg.statusflag.toString() - out.write("${ret.dt.toString().lowercase()} $reg\n") + xml.writeCharacters("${ret.dt.toString().lowercase()} $reg\n") } - out.write("\n") + xml.writeEndElement() + xml.writeCharacters("\n") writeInlineAsm(child.asmChunk) - out.write("\n") + xml.writeEndElement() + xml.writeCharacters("\n\n") } is IRCodeChunk -> writeCodeChunk(child) is IRInlineAsmChunk -> writeInlineAsm(child) is IRInlineBinaryChunk -> writeInlineBytes(child) is IRSubroutine -> { - out.write("\n") - out.write("\n") - child.parameters.forEach { param -> out.write("${getTypeString(param.dt)} ${param.name}\n") } - out.write("\n") + xml.writeStartElement("SUB") + xml.writeAttribute("NAME", child.label) + xml.writeAttribute("RETURNTYPE", child.returnType?.toString()?.lowercase() ?: "") + xml.writeAttribute("POS", child.position.toString()) + xml.writeCharacters("\n") + xml.writeStartElement("PARAMS") + xml.writeCharacters("\n") + child.parameters.forEach { param -> xml.writeCharacters("${getTypeString(param.dt)} ${param.name}\n") } + xml.writeEndElement() + xml.writeCharacters("\n") child.chunks.forEach { chunk -> numChunks++ when (chunk) { @@ -79,71 +111,87 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { else -> throw InternalCompilerException("invalid chunk") } } - out.write("\n") + xml.writeEndElement() + xml.writeCharacters("\n\n") } } } - out.write("\n") + xml.writeEndElement() + xml.writeCharacters("\n\n") } } private fun writeCodeChunk(chunk: IRCodeChunk) { - if(chunk.label!=null) - out.write("\n") - else - out.write("\n") + xml.writeStartElement("CODE") + chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) } + xml.writeCharacters("\n") chunk.instructions.forEach { instr -> numInstr++ - out.write(instr.toString()) - out.write("\n") + xml.writeCharacters(instr.toString()) + xml.writeCharacters("\n") } - out.write("\n") + xml.writeEndElement() + xml.writeCharacters("\n") } private fun writeInlineBytes(chunk: IRInlineBinaryChunk) { - out.write("\n") + xml.writeStartElement("BYTES") + chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) } + xml.writeCharacters("\n") chunk.data.withIndex().forEach {(index, byte) -> - out.write(byte.toString(16).padStart(2,'0')) + xml.writeCharacters(byte.toString(16).padStart(2,'0')) if(index and 63 == 63 && index < chunk.data.size-1) - out.write("\n") + xml.writeCharacters("\n") } - out.write("\n\n") + xml.writeEndElement() + xml.writeCharacters("\n") } private fun writeInlineAsm(chunk: IRInlineAsmChunk) { - out.write("\n") - out.write(chunk.assembly) - out.write("\n\n") + xml.writeStartElement("INLINEASM") + xml.writeAttribute("LABEL", chunk.label ?: "") + xml.writeAttribute("IR", chunk.isIR.toString()) + xml.writeCharacters("\n") + xml.writeCharacters(chunk.assembly) + xml.writeEndElement() + xml.writeCharacters("\n") } private fun writeOptions() { - out.write("\n") - out.write("compTarget=${irProgram.options.compTarget.name}\n") - out.write("output=${irProgram.options.output}\n") - out.write("launcher=${irProgram.options.launcher}\n") - out.write("zeropage=${irProgram.options.zeropage}\n") + xml.writeStartElement("OPTIONS") + xml.writeCharacters("\n") + xml.writeCharacters("compTarget=${irProgram.options.compTarget.name}\n") + xml.writeCharacters("output=${irProgram.options.output}\n") + xml.writeCharacters("launcher=${irProgram.options.launcher}\n") + xml.writeCharacters("zeropage=${irProgram.options.zeropage}\n") for(range in irProgram.options.zpReserved) { - out.write("zpReserved=${range.first},${range.last}\n") + xml.writeCharacters("zpReserved=${range.first},${range.last}\n") } - out.write("loadAddress=${irProgram.options.loadAddress.toHex()}\n") - out.write("optimize=${irProgram.options.optimize}\n") - out.write("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress?.toHex()}\n") - out.write("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n") + xml.writeCharacters("loadAddress=${irProgram.options.loadAddress.toHex()}\n") + xml.writeCharacters("optimize=${irProgram.options.optimize}\n") + xml.writeCharacters("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress?.toHex() ?: ""}\n") + xml.writeCharacters("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n") // other options not yet useful here? - out.write("\n") + xml.writeEndElement() + xml.writeCharacters("\n\n") } private fun writeVariables() { val (variablesNoInit, variablesWithInit) = irProgram.st.allVariables().partition { it.uninitialized } - out.write("\n\n") + xml.writeStartElement("VARIABLESNOINIT") + xml.writeCharacters("\n") for (variable in variablesNoInit) { val typeStr = getTypeString(variable) - out.write("$typeStr ${variable.name} zp=${variable.zpwish}\n") + xml.writeCharacters("$typeStr ${variable.name} zp=${variable.zpwish}\n") } - out.write("\n\n") + xml.writeEndElement() + xml.writeCharacters("\n") + xml.writeStartElement("VARIABLESWITHINIT") + xml.writeCharacters("\n") + for (variable in variablesWithInit) { val typeStr = getTypeString(variable) val value: String = when(variable.dt) { @@ -174,19 +222,24 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { } else -> throw InternalCompilerException("weird dt") } - out.write("$typeStr ${variable.name}=$value zp=${variable.zpwish}\n") + xml.writeCharacters("$typeStr ${variable.name}=$value zp=${variable.zpwish}\n") } - out.write("\n") + xml.writeEndElement() + xml.writeCharacters("\n") - out.write("\n\n") + xml.writeStartElement("MEMORYMAPPEDVARIABLES") + xml.writeCharacters("\n") for (variable in irProgram.st.allMemMappedVariables()) { val typeStr = getTypeString(variable) - out.write("@$typeStr ${variable.name}=${variable.address.toHex()}\n") + xml.writeCharacters("@$typeStr ${variable.name}=${variable.address.toHex()}\n") } - out.write("\n") + xml.writeEndElement() + xml.writeCharacters("\n") - out.write("\n\n") - irProgram.st.allMemorySlabs().forEach{ slab -> out.write("SLAB ${slab.name} ${slab.size} ${slab.align}\n") } - out.write("\n") + xml.writeStartElement("MEMORYSLABS") + xml.writeCharacters("\n") + irProgram.st.allMemorySlabs().forEach{ slab -> xml.writeCharacters("${slab.name} ${slab.size} ${slab.align}\n") } + xml.writeEndElement() + xml.writeCharacters("\n") } } \ No newline at end of file diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index e4215d49a..9edf288c8 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -585,10 +585,10 @@ val instructionFormats = mutableMapOf( Opcode.SLES to InstructionFormat.from("BW,<>r1,r1,r1,r1"), - Opcode.INCM to InstructionFormat.from("BW,r1"), - Opcode.DECM to InstructionFormat.from("BW,r1 | F,<>fr1"), + Opcode.INCM to InstructionFormat.from("BW,r1 | F,<>fr1"), + Opcode.DECM to InstructionFormat.from("BW,r1 | F,<>fr1"), Opcode.NEGM to InstructionFormat.from("BW,r1,fr1, @@ -75,8 +75,8 @@ load.b r1,42 - - + + @@ -85,8 +85,8 @@ return - - + + uword sys.wait.jiffies diff --git a/virtualmachine/test/TestVm.kt b/virtualmachine/test/TestVm.kt index 555805202..0d2f9c9db 100644 --- a/virtualmachine/test/TestVm.kt +++ b/virtualmachine/test/TestVm.kt @@ -129,7 +129,7 @@ class TestVm: FunSpec( { - + """