mirror of
https://github.com/irmen/prog8.git
synced 2024-12-23 09:32:43 +00:00
introduce option to use internal scratch variables via prog8_lib definitions (ony for compiler, not for user code!)
This commit is contained in:
parent
ab2d1122a9
commit
53e1729e2f
@ -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<Statement>) {
|
||||
private fun zeropagevars2asm(statements: List<Statement>, inBlock: Block?) {
|
||||
out("; vars allocated on zeropage")
|
||||
val variables = statements.filterIsInstance<VarDecl>().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<Statement>) {
|
||||
private fun memdefs2asm(statements: List<Statement>, inBlock: Block?) {
|
||||
val blockname = inBlock?.name
|
||||
|
||||
out("\n; memdefs and kernal subroutines")
|
||||
val memvars = statements.filterIsInstance<VarDecl>().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<Subroutine>().filter { it.isAsmSubroutine }
|
||||
for(sub in asmSubs) {
|
||||
@ -379,7 +385,7 @@ class AsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
private fun vardecls2asm(statements: List<Statement>) {
|
||||
private fun vardecls2asm(statements: List<Statement>, inBlock: Block?) {
|
||||
out("\n; non-zeropage variables")
|
||||
val vars = statements.filterIsInstance<VarDecl>().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")
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
})
|
||||
|
@ -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 {{
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -1,13 +1,10 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%zeropage dontuse
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
byte xx=1
|
||||
|
||||
if -xx {
|
||||
if xx+2 {
|
||||
xx++
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user