mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 01:29:28 +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"))
|
out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
|
||||||
|
|
||||||
outputSourceLine(block)
|
outputSourceLine(block)
|
||||||
zeropagevars2asm(block.statements)
|
zeropagevars2asm(block.statements, block)
|
||||||
memdefs2asm(block.statements)
|
memdefs2asm(block.statements, block)
|
||||||
vardecls2asm(block.statements)
|
vardecls2asm(block.statements, block)
|
||||||
out("\n; subroutines in this block")
|
out("\n; subroutines in this block")
|
||||||
|
|
||||||
// first translate regular statements, and then put the subroutines at the end.
|
// 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)
|
} else assemblyLines.add(fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun zeropagevars2asm(statements: List<Statement>) {
|
private fun zeropagevars2asm(statements: List<Statement>, inBlock: Block?) {
|
||||||
out("; vars allocated on zeropage")
|
out("; vars allocated on zeropage")
|
||||||
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
||||||
|
val blockname = inBlock?.name
|
||||||
for(variable in variables) {
|
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 fullName = variable.makeScopedName(variable.name)
|
||||||
val zpVar = allocatedZeropageVariables[fullName]
|
val zpVar = allocatedZeropageVariables[fullName]
|
||||||
if(zpVar==null) {
|
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")
|
out("\n; memdefs and kernal subroutines")
|
||||||
val memvars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST }
|
val memvars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST }
|
||||||
for(m in memvars) {
|
for(m in memvars) {
|
||||||
if(m.value is NumericLiteralValue)
|
if(blockname!="prog8_lib" || !m.name.startsWith("P8ZP_SCRATCH_")) // the "hooks" to the temp vars are not generated as new variables
|
||||||
out(" ${m.name} = ${(m.value as NumericLiteralValue).number.toHex()}")
|
if(m.value is NumericLiteralValue)
|
||||||
else
|
out(" ${m.name} = ${(m.value as NumericLiteralValue).number.toHex()}")
|
||||||
out(" ${m.name} = ${asmVariableName((m.value as AddressOf).identifier)}")
|
else
|
||||||
|
out(" ${m.name} = ${asmVariableName((m.value as AddressOf).identifier)}")
|
||||||
}
|
}
|
||||||
val asmSubs = statements.filterIsInstance<Subroutine>().filter { it.isAsmSubroutine }
|
val asmSubs = statements.filterIsInstance<Subroutine>().filter { it.isAsmSubroutine }
|
||||||
for(sub in asmSubs) {
|
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")
|
out("\n; non-zeropage variables")
|
||||||
val vars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
val vars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
||||||
|
|
||||||
@ -394,9 +400,13 @@ class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// non-string variables
|
// non-string variables
|
||||||
|
val blockname = inBlock?.name
|
||||||
|
|
||||||
vars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach {
|
vars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach {
|
||||||
if(it.makeScopedName(it.name) !in allocatedZeropageVariables)
|
if(it.makeScopedName(it.name) !in allocatedZeropageVariables) {
|
||||||
vardecl2asm(it)
|
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 {
|
fun asmSymbolName(identifier: IdentifierReference): String {
|
||||||
if(identifier.nameInSource.size==2 && identifier.nameInSource[0]=="prog8_slabs")
|
fun internalName(): String {
|
||||||
return identifier.nameInSource.joinToString(".")
|
if (identifier.nameInSource.size == 2 && identifier.nameInSource[0] == "prog8_slabs")
|
||||||
|
return identifier.nameInSource.joinToString(".")
|
||||||
|
|
||||||
val tgt2 = identifier.targetStatement(program)
|
val tgt2 = identifier.targetStatement(program)
|
||||||
if(tgt2==null && (identifier.nameInSource[0].startsWith("_prog8") || identifier.nameInSource[0].startsWith("prog8")))
|
if (tgt2 == null && (identifier.nameInSource[0].startsWith("_prog8") || identifier.nameInSource[0].startsWith(
|
||||||
return identifier.nameInSource.joinToString(".")
|
"prog8"
|
||||||
|
))
|
||||||
|
)
|
||||||
|
return identifier.nameInSource.joinToString(".")
|
||||||
|
|
||||||
val target = identifier.targetStatement(program)!!
|
val target = identifier.targetStatement(program)!!
|
||||||
val targetScope = target.definingSubroutine
|
val targetScope = target.definingSubroutine
|
||||||
val identScope = identifier.definingSubroutine
|
val identScope = identifier.definingSubroutine
|
||||||
return if(targetScope !== identScope) {
|
return if (targetScope !== identScope) {
|
||||||
val scopedName = getScopedSymbolNameForTarget(identifier.nameInSource.last(), target)
|
val scopedName = getScopedSymbolNameForTarget(identifier.nameInSource.last(), target)
|
||||||
if(target is Label) {
|
if (target is Label) {
|
||||||
// make labels locally scoped in the asm. Is slightly problematic, see github issue #62
|
// make labels locally scoped in the asm. Is slightly problematic, see github issue #62
|
||||||
val last = scopedName.removeLast()
|
val last = scopedName.removeLast()
|
||||||
scopedName.add("_$last")
|
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("."))
|
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) =
|
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) {
|
internal fun saveRegisterLocal(register: CpuRegister, scope: Subroutine) {
|
||||||
if (isTargetCpu(CpuType.CPU65c02)) {
|
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||||
@ -934,8 +953,8 @@ class AsmGen(private val program: Program,
|
|||||||
} else {
|
} else {
|
||||||
// regular subroutine
|
// regular subroutine
|
||||||
out("${sub.name}\t.proc")
|
out("${sub.name}\t.proc")
|
||||||
zeropagevars2asm(sub.statements)
|
zeropagevars2asm(sub.statements, null)
|
||||||
memdefs2asm(sub.statements)
|
memdefs2asm(sub.statements, null)
|
||||||
|
|
||||||
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
|
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
|
||||||
if(sub.name=="start" && sub.definingBlock.name=="main") {
|
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")
|
out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
|
||||||
if(sub.asmGenInfo.usedFloatEvalResultVar2)
|
if(sub.asmGenInfo.usedFloatEvalResultVar2)
|
||||||
out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
|
out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
|
||||||
vardecls2asm(sub.statements)
|
vardecls2asm(sub.statements, null)
|
||||||
out(" .pend\n")
|
out(" .pend\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,7 @@ import io.kotest.core.spec.style.StringSpec
|
|||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.Position
|
|
||||||
import prog8.ast.base.RegisterOrPair
|
|
||||||
import prog8.ast.base.VarDeclType
|
|
||||||
import prog8.ast.expressions.AddressOf
|
import prog8.ast.expressions.AddressOf
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
@ -84,7 +81,7 @@ class AsmGenSymbolsTests: StringSpec({
|
|||||||
return asmgen
|
return asmgen
|
||||||
}
|
}
|
||||||
|
|
||||||
"symbol names from strings" {
|
"symbol and variable names from strings" {
|
||||||
val program = createTestProgram()
|
val program = createTestProgram()
|
||||||
val asmgen = createTestAsmGen(program)
|
val asmgen = createTestAsmGen(program)
|
||||||
asmgen.asmSymbolName("name") shouldBe "name"
|
asmgen.asmSymbolName("name") shouldBe "name"
|
||||||
@ -97,7 +94,7 @@ class AsmGenSymbolsTests: StringSpec({
|
|||||||
asmgen.asmVariableName(listOf("a", "b", "name")) shouldBe "a.b.name"
|
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 program = createTestProgram()
|
||||||
val asmgen = createTestAsmGen(program)
|
val asmgen = createTestAsmGen(program)
|
||||||
val sub = program.entrypoint
|
val sub = program.entrypoint
|
||||||
@ -120,7 +117,7 @@ class AsmGenSymbolsTests: StringSpec({
|
|||||||
asmgen.asmVariableName(scopedVarIdentScoped) shouldBe "main.var_outside"
|
asmgen.asmVariableName(scopedVarIdentScoped) shouldBe "main.var_outside"
|
||||||
}
|
}
|
||||||
|
|
||||||
"symbol names from label identifiers" {
|
"symbol and variable names from label identifiers" {
|
||||||
val program = createTestProgram()
|
val program = createTestProgram()
|
||||||
val asmgen = createTestAsmGen(program)
|
val asmgen = createTestAsmGen(program)
|
||||||
val sub = program.entrypoint
|
val sub = program.entrypoint
|
||||||
@ -149,4 +146,28 @@ class AsmGenSymbolsTests: StringSpec({
|
|||||||
asmgen.asmVariableName(scopedLabelIdentScoped) shouldBe "main.label_outside"
|
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
|
word retval_interm_w2
|
||||||
byte retval_interm_b2
|
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 {
|
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A {
|
||||||
%asm {{
|
%asm {{
|
||||||
|
@ -240,13 +240,17 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
|
|||||||
var rightAssignment: Assignment? = null
|
var rightAssignment: Assignment? = null
|
||||||
var rightOperandReplacement: Expression? = 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 dt = expr.left.inferType(program)
|
||||||
val name = when {
|
val name = when {
|
||||||
dt.istype(DataType.UBYTE) -> listOf("cx16","r9L") // assume (hope) cx16.r9 isn't used for anything else...
|
// TODO 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.UBYTE) -> listOf("cx16","r9L")
|
||||||
dt.istype(DataType.BYTE) -> listOf("prog8_lib","retval_interm_b")
|
dt.istype(DataType.BYTE) -> listOf("cx16","r9sL")
|
||||||
dt.istype(DataType.WORD) -> listOf("prog8_lib","retval_interm_w")
|
dt.istype(DataType.UWORD) -> listOf("cx16","r9")
|
||||||
|
dt.istype(DataType.WORD) -> listOf("cx16","r9s")
|
||||||
else -> throw AssemblyError("invalid dt")
|
else -> throw AssemblyError("invalid dt")
|
||||||
}
|
}
|
||||||
leftOperandReplacement = IdentifierReference(name, expr.position)
|
leftOperandReplacement = IdentifierReference(name, expr.position)
|
||||||
@ -256,7 +260,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o
|
|||||||
expr.position
|
expr.position
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if(!expr.right.isSimple && expr.right !is IFunctionCall) {
|
if(separateRightExpr) {
|
||||||
val dt = expr.right.inferType(program)
|
val dt = expr.right.inferType(program)
|
||||||
val name = when {
|
val name = when {
|
||||||
dt.istype(DataType.UBYTE) -> listOf("prog8_lib","retval_interm_ub")
|
dt.istype(DataType.UBYTE) -> listOf("prog8_lib","retval_interm_ub")
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.compilerinterface
|
package prog8.compilerinterface
|
||||||
|
|
||||||
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
@ -41,7 +42,7 @@ fun AssignTarget.isInRegularRAMof(machine: IMachineDefinition): Boolean {
|
|||||||
}
|
}
|
||||||
ident != null -> {
|
ident != null -> {
|
||||||
val program = definingModule.program
|
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)
|
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue)
|
||||||
machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
|
machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
|
||||||
else
|
else
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
%import textio
|
|
||||||
%import floats
|
|
||||||
%zeropage dontuse
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
byte xx=1
|
byte xx=1
|
||||||
|
|
||||||
if -xx {
|
if xx+2 {
|
||||||
xx++
|
xx++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user