diff --git a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt index 79c2ab2bc..17590cbdb 100644 --- a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt +++ b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt @@ -214,9 +214,9 @@ class AsmGen(private val program: Program, out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n")) outputSourceLine(block) - zeropagevars2asm(block.statements) - memdefs2asm(block.statements) - vardecls2asm(block.statements) + zeropagevars2asm(block.statements, block) + memdefs2asm(block.statements, block) + vardecls2asm(block.statements, block) out("\n; subroutines in this block") // first translate regular statements, and then put the subroutines at the end. @@ -258,10 +258,13 @@ class AsmGen(private val program: Program, } else assemblyLines.add(fragment) } - private fun zeropagevars2asm(statements: List) { + private fun zeropagevars2asm(statements: List, inBlock: Block?) { out("; vars allocated on zeropage") val variables = statements.filterIsInstance().filter { it.type==VarDeclType.VAR } + val blockname = inBlock?.name for(variable in variables) { + if(blockname=="prog8_lib" && variable.name.startsWith("P8ZP_SCRATCH_")) + continue // the "hooks" to the temp vars are not generated as new variables val fullName = variable.makeScopedName(variable.name) val zpVar = allocatedZeropageVariables[fullName] if(zpVar==null) { @@ -359,14 +362,17 @@ class AsmGen(private val program: Program, } } - private fun memdefs2asm(statements: List) { + private fun memdefs2asm(statements: List, inBlock: Block?) { + val blockname = inBlock?.name + out("\n; memdefs and kernal subroutines") val memvars = statements.filterIsInstance().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST } for(m in memvars) { - if(m.value is NumericLiteralValue) - out(" ${m.name} = ${(m.value as NumericLiteralValue).number.toHex()}") - else - out(" ${m.name} = ${asmVariableName((m.value as AddressOf).identifier)}") + if(blockname!="prog8_lib" || !m.name.startsWith("P8ZP_SCRATCH_")) // the "hooks" to the temp vars are not generated as new variables + if(m.value is NumericLiteralValue) + out(" ${m.name} = ${(m.value as NumericLiteralValue).number.toHex()}") + else + out(" ${m.name} = ${asmVariableName((m.value as AddressOf).identifier)}") } val asmSubs = statements.filterIsInstance().filter { it.isAsmSubroutine } for(sub in asmSubs) { @@ -379,7 +385,7 @@ class AsmGen(private val program: Program, } } - private fun vardecls2asm(statements: List) { + private fun vardecls2asm(statements: List, inBlock: Block?) { out("\n; non-zeropage variables") val vars = statements.filterIsInstance().filter { it.type==VarDeclType.VAR } @@ -394,9 +400,13 @@ class AsmGen(private val program: Program, } // non-string variables + val blockname = inBlock?.name + vars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach { - if(it.makeScopedName(it.name) !in allocatedZeropageVariables) - vardecl2asm(it) + if(it.makeScopedName(it.name) !in allocatedZeropageVariables) { + if(blockname!="prog8_lib" || !it.name.startsWith("P8ZP_SCRATCH_")) // the "hooks" to the temp vars are not generated as new variables + vardecl2asm(it) + } } } @@ -496,34 +506,40 @@ class AsmGen(private val program: Program, } fun asmSymbolName(identifier: IdentifierReference): String { - if(identifier.nameInSource.size==2 && identifier.nameInSource[0]=="prog8_slabs") - return identifier.nameInSource.joinToString(".") + fun internalName(): String { + if (identifier.nameInSource.size == 2 && identifier.nameInSource[0] == "prog8_slabs") + return identifier.nameInSource.joinToString(".") - val tgt2 = identifier.targetStatement(program) - if(tgt2==null && (identifier.nameInSource[0].startsWith("_prog8") || identifier.nameInSource[0].startsWith("prog8"))) - return identifier.nameInSource.joinToString(".") + val tgt2 = identifier.targetStatement(program) + if (tgt2 == null && (identifier.nameInSource[0].startsWith("_prog8") || identifier.nameInSource[0].startsWith( + "prog8" + )) + ) + return identifier.nameInSource.joinToString(".") - val target = identifier.targetStatement(program)!! - val targetScope = target.definingSubroutine - val identScope = identifier.definingSubroutine - return if(targetScope !== identScope) { - val scopedName = getScopedSymbolNameForTarget(identifier.nameInSource.last(), target) - if(target is Label) { - // make labels locally scoped in the asm. Is slightly problematic, see github issue #62 - val last = scopedName.removeLast() - scopedName.add("_$last") - } - fixNameSymbols(scopedName.joinToString(".")) - } else { - if(target is Label) { - // make labels locally scoped in the asm. Is slightly problematic, see github issue #62 - val scopedName = identifier.nameInSource.toMutableList() - val last = scopedName.removeLast() - scopedName.add("_$last") + val target = identifier.targetStatement(program)!! + val targetScope = target.definingSubroutine + val identScope = identifier.definingSubroutine + return if (targetScope !== identScope) { + val scopedName = getScopedSymbolNameForTarget(identifier.nameInSource.last(), target) + if (target is Label) { + // make labels locally scoped in the asm. Is slightly problematic, see github issue #62 + val last = scopedName.removeLast() + scopedName.add("_$last") + } fixNameSymbols(scopedName.joinToString(".")) + } else { + if (target is Label) { + // make labels locally scoped in the asm. Is slightly problematic, see github issue #62 + val scopedName = identifier.nameInSource.toMutableList() + val last = scopedName.removeLast() + scopedName.add("_$last") + fixNameSymbols(scopedName.joinToString(".")) + } else fixNameSymbols(identifier.nameInSource.joinToString(".")) } - else fixNameSymbols(identifier.nameInSource.joinToString(".")) } + + return fixNameSymbols(internalName()) } fun asmVariableName(identifier: IdentifierReference) = @@ -600,7 +616,10 @@ class AsmGen(private val program: Program, } } - private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names + private fun fixNameSymbols(name: String): String { + val name2 = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names + return name2.replace("prog8_lib.P8ZP_SCRATCH_", "P8ZP_SCRATCH_") // take care of the 'hooks' to the temp vars + } internal fun saveRegisterLocal(register: CpuRegister, scope: Subroutine) { if (isTargetCpu(CpuType.CPU65c02)) { @@ -934,8 +953,8 @@ class AsmGen(private val program: Program, } else { // regular subroutine out("${sub.name}\t.proc") - zeropagevars2asm(sub.statements) - memdefs2asm(sub.statements) + zeropagevars2asm(sub.statements, null) + memdefs2asm(sub.statements, null) // the main.start subroutine is the program's entrypoint and should perform some initialization logic if(sub.name=="start" && sub.definingBlock.name=="main") { @@ -985,7 +1004,7 @@ class AsmGen(private val program: Program, out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0") if(sub.asmGenInfo.usedFloatEvalResultVar2) out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0") - vardecls2asm(sub.statements) + vardecls2asm(sub.statements, null) out(" .pend\n") } } diff --git a/codeGeneration/test/AsmGenSymbolsTests.kt b/codeGeneration/test/AsmGenSymbolsTests.kt index ea794fcc8..75b20dd7c 100644 --- a/codeGeneration/test/AsmGenSymbolsTests.kt +++ b/codeGeneration/test/AsmGenSymbolsTests.kt @@ -5,10 +5,7 @@ import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import prog8.ast.Module import prog8.ast.Program -import prog8.ast.base.DataType -import prog8.ast.base.Position -import prog8.ast.base.RegisterOrPair -import prog8.ast.base.VarDeclType +import prog8.ast.base.* import prog8.ast.expressions.AddressOf import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.NumericLiteralValue @@ -84,7 +81,7 @@ class AsmGenSymbolsTests: StringSpec({ return asmgen } - "symbol names from strings" { + "symbol and variable names from strings" { val program = createTestProgram() val asmgen = createTestAsmGen(program) asmgen.asmSymbolName("name") shouldBe "name" @@ -97,7 +94,7 @@ class AsmGenSymbolsTests: StringSpec({ asmgen.asmVariableName(listOf("a", "b", "name")) shouldBe "a.b.name" } - "symbol names from variable identifiers" { + "symbol and variable names from variable identifiers" { val program = createTestProgram() val asmgen = createTestAsmGen(program) val sub = program.entrypoint @@ -120,7 +117,7 @@ class AsmGenSymbolsTests: StringSpec({ asmgen.asmVariableName(scopedVarIdentScoped) shouldBe "main.var_outside" } - "symbol names from label identifiers" { + "symbol and variable names from label identifiers" { val program = createTestProgram() val asmgen = createTestAsmGen(program) val sub = program.entrypoint @@ -149,4 +146,28 @@ class AsmGenSymbolsTests: StringSpec({ asmgen.asmVariableName(scopedLabelIdentScoped) shouldBe "main.label_outside" } } + + "asm names for hooks to zp temp vars" { + /* +main { + + sub start() { + prog8_lib.P8ZP_SCRATCH_REG = 1 + prog8_lib.P8ZP_SCRATCH_B1 = 1 + prog8_lib.P8ZP_SCRATCH_W1 = 1 + prog8_lib.P8ZP_SCRATCH_W2 = 1 + */ + val program = createTestProgram() + val asmgen = createTestAsmGen(program) + asmgen.asmSymbolName("prog8_lib.P8ZP_SCRATCH_REG") shouldBe "P8ZP_SCRATCH_REG" + asmgen.asmSymbolName("prog8_lib.P8ZP_SCRATCH_W2") shouldBe "P8ZP_SCRATCH_W2" + asmgen.asmSymbolName(listOf("prog8_lib","P8ZP_SCRATCH_REG")) shouldBe "P8ZP_SCRATCH_REG" + asmgen.asmSymbolName(listOf("prog8_lib","P8ZP_SCRATCH_W2")) shouldBe "P8ZP_SCRATCH_W2" + val id1 = IdentifierReference(listOf("prog8_lib","P8ZP_SCRATCH_REG"), Position.DUMMY) + id1.linkParents(program.toplevelModule) + val id2 = IdentifierReference(listOf("prog8_lib","P8ZP_SCRATCH_W2"), Position.DUMMY) + id2.linkParents(program.toplevelModule) + asmgen.asmSymbolName(id1) shouldBe "P8ZP_SCRATCH_REG" + asmgen.asmSymbolName(id2) shouldBe "P8ZP_SCRATCH_W2" + } }) diff --git a/compiler/res/prog8lib/prog8_lib.p8 b/compiler/res/prog8lib/prog8_lib.p8 index 918d041f5..4e50d20b4 100644 --- a/compiler/res/prog8lib/prog8_lib.p8 +++ b/compiler/res/prog8lib/prog8_lib.p8 @@ -15,6 +15,15 @@ prog8_lib { word retval_interm_w2 byte retval_interm_b2 + ; prog8 "hooks" to be able to access the temporary scratch variables + ; YOU SHOULD NOT USE THESE IN USER CODE - THESE ARE MEANT FOR INTERNAL COMPILER USE + ; NOTE: the assembly code generator will match these names and not generate + ; new variables/memdefs for them, rather, they'll point to the scratch variables directly. + &ubyte P8ZP_SCRATCH_REG = $ff + &byte P8ZP_SCRATCH_B1 = $ff + &uword P8ZP_SCRATCH_W1 = $ff + &word P8ZP_SCRATCH_W2 = $ff + asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A { %asm {{ diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index e35ea8da1..f618432c0 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -240,13 +240,17 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o var rightAssignment: Assignment? = null var rightOperandReplacement: Expression? = null - if(!expr.left.isSimple && expr.left !is IFunctionCall) { + val separateLeftExpr = !expr.left.isSimple && expr.left !is IFunctionCall + val separateRightExpr = !expr.right.isSimple && expr.right !is IFunctionCall + + if(separateLeftExpr) { val dt = expr.left.inferType(program) val name = when { - dt.istype(DataType.UBYTE) -> listOf("cx16","r9L") // assume (hope) cx16.r9 isn't used for anything else... - dt.istype(DataType.UWORD) -> listOf("cx16","r9") // assume (hope) cx16.r9 isn't used for anything else... - dt.istype(DataType.BYTE) -> listOf("prog8_lib","retval_interm_b") - dt.istype(DataType.WORD) -> listOf("prog8_lib","retval_interm_w") + // TODO assume (hope) cx16.r9 isn't used for anything else... + dt.istype(DataType.UBYTE) -> listOf("cx16","r9L") + dt.istype(DataType.BYTE) -> listOf("cx16","r9sL") + dt.istype(DataType.UWORD) -> listOf("cx16","r9") + dt.istype(DataType.WORD) -> listOf("cx16","r9s") else -> throw AssemblyError("invalid dt") } leftOperandReplacement = IdentifierReference(name, expr.position) @@ -256,7 +260,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o expr.position ) } - if(!expr.right.isSimple && expr.right !is IFunctionCall) { + if(separateRightExpr) { val dt = expr.right.inferType(program) val name = when { dt.istype(DataType.UBYTE) -> listOf("prog8_lib","retval_interm_ub") diff --git a/compilerInterfaces/src/prog8/compilerinterface/AstExtensions.kt b/compilerInterfaces/src/prog8/compilerinterface/AstExtensions.kt index 89f29daa7..2dc4bf163 100644 --- a/compilerInterfaces/src/prog8/compilerinterface/AstExtensions.kt +++ b/compilerInterfaces/src/prog8/compilerinterface/AstExtensions.kt @@ -1,5 +1,6 @@ package prog8.compilerinterface +import prog8.ast.base.FatalAstException import prog8.ast.base.VarDeclType import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.NumericLiteralValue @@ -41,7 +42,7 @@ fun AssignTarget.isInRegularRAMof(machine: IMachineDefinition): Boolean { } ident != null -> { val program = definingModule.program - val decl = ident.targetVarDecl(program)!! + val decl = ident.targetVarDecl(program) ?: throw FatalAstException("invalid identifier ${ident.nameInSource}") return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue) machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt()) else diff --git a/examples/test.p8 b/examples/test.p8 index 197513e0f..e378e474c 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,13 +1,10 @@ -%import textio -%import floats -%zeropage dontuse main { sub start() { byte xx=1 - if -xx { + if xx+2 { xx++ }