diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index b4e616f05..1aff1c7b9 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -739,7 +739,8 @@ $repeatLabel lda $counterVar } private fun createRepeatCounterVar(dt: DataType, preferZeropage: Boolean, stmt: RepeatLoop): String { - val asmInfo = allocator.subroutineExtra(stmt.definingSubroutine!!) + val scope = stmt.definingSubroutine!! + val asmInfo = allocator.subroutineExtra(scope) var parent = stmt.parent while(parent !is ParentSentinel) { if(parent is RepeatLoop) @@ -760,7 +761,7 @@ $repeatLabel lda $counterVar val counterVar = makeLabel("counter") when(dt) { DataType.UBYTE, DataType.UWORD -> { - val result = zeropage.allocate(listOf(counterVar), dt, null, null, stmt.position, errors) + val result = zeropage.allocate(listOf(counterVar), dt, scope,null, null, stmt.position, errors) result.fold( success = { (address, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) }, failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt index aabe7f7e6..e40f4a8f6 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt @@ -292,7 +292,7 @@ $loopLabel sty $indexVar } if(length>=16) { // allocate index var on ZP if possible - val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, null, stmt.position, asmgen.errors) + val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, stmt.definingScope, null, null, stmt.position, asmgen.errors) result.fold( success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, failure = { asmgen.out("$indexVar .byte 0") } @@ -333,7 +333,7 @@ $loopLabel sty $indexVar } if(length>=16) { // allocate index var on ZP if possible - val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, null, stmt.position, asmgen.errors) + val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, stmt.definingScope, null, null, stmt.position, asmgen.errors) result.fold( success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, failure = { asmgen.out("$indexVar .byte 0") } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt index e498d1bf5..2227e4ddf 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramGen.kt @@ -1,12 +1,10 @@ package prog8.codegen.cpu6502 -import prog8.ast.IFunctionCall -import prog8.ast.Program +import prog8.ast.* import prog8.ast.antlr.escape import prog8.ast.base.* import prog8.ast.expressions.* import prog8.ast.statements.* -import prog8.ast.toHex import prog8.codegen.cpu6502.assignment.AsmAssignTarget import prog8.codegen.cpu6502.assignment.TargetStorageKind import prog8.compilerinterface.* @@ -33,12 +31,12 @@ internal class ProgramGen( val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value } require(allInitializers.all { it.origin==AssignmentOrigin.VARINIT }) {"all block-level assignments must be a variable initializer"} + allocator.allocateZeropageVariables(options) header() val allBlocks = program.allBlocks if(allBlocks.first().name != "main") throw AssemblyError("first block should be 'main'") - allocator.allocateZeropageVariables(options) if(errors.noErrors()) { program.allBlocks.forEach { block2asm(it) } memorySlabs() @@ -162,16 +160,10 @@ internal class ProgramGen( asmgen.outputSourceLine(block) - val vardecls = block.statements.filterIsInstance() - zeropagevars2asm(vardecls) - memdefs2asmVars(vardecls, block) - memdefs2asmAsmsubs(block.statements.filterIsInstance()) - vardecls2asm(vardecls) - - vardecls.forEach { - if(it.type== VarDeclType.VAR && it.datatype in NumericDatatypes) - it.value=null // make sure every var has no init value any longer (could be set due to 'noreinit' option) because initialization is done via explicit assignment - } + zeropagevars2asm(block) + memdefsAndConsts2asm(block) + asmsubs2asm(block.statements) + nonZpVariables2asm(block) asmgen.out("\n; subroutines in this block") @@ -224,10 +216,9 @@ internal class ProgramGen( } else { // regular subroutine asmgen.out("${sub.name}\t.proc") - val vardecls = sub.statements.filterIsInstance() - zeropagevars2asm(vardecls) - memdefs2asmVars(vardecls, null) - memdefs2asmAsmsubs(sub.statements.filterIsInstance()) + zeropagevars2asm(sub) + memdefsAndConsts2asm(sub) + asmsubs2asm(sub.statements) // the main.start subroutine is the program's entrypoint and should perform some initialization logic if(sub.name=="start" && sub.definingBlock.name=="main") @@ -278,7 +269,7 @@ internal class ProgramGen( asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0") if(asmGenInfo.usedFloatEvalResultVar2) asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0") - vardecls2asm(vardecls) + nonZpVariables2asm(sub) asmgen.out(" .pend\n") } } @@ -331,14 +322,13 @@ internal class ProgramGen( stringVarsWithInitInZp.forEach { val varname = asmgen.asmVariableName(it.key)+"_init_value" - val sv = it.value.initialStringValue!! - outputStringvar(varname, it.value.dt, sv.encoding, sv.value) + val stringvalue = it.value.initialStringValue!! + outputStringvar(varname, it.value.dt, stringvalue.encoding, stringvalue.value) } arrayVarsWithInitInZp.forEach { val varname = asmgen.asmVariableName(it.key)+"_init_value" - val av = it.value.initialArrayValue!! - arrayVardecl2asm(varname, it.value.dt, av, null) + arrayVardecl2asm(varname, it.value.dt, it.value.initialArrayValue!!, null) } asmgen.out("""+ tsx @@ -348,25 +338,32 @@ internal class ProgramGen( clc""") } - private fun zeropagevars2asm(vardecls: List) { - // TODO get list of variables directly from allocator or zeropage - val zp = zeropage - asmgen.out("; vars allocated on zeropage") - vardecls - .filter { it.type==VarDeclType.VAR } - .forEach { variable -> - val scopedName = variable.scopedName - val zpAlloc = zp.variables[scopedName] - if (zpAlloc != null) { - val lenspec = when(zpAlloc.dt) { - DataType.FLOAT, - DataType.STR, - in ArrayDatatypes -> " ${zpAlloc.size} bytes" - else -> "" - } - asmgen.out("${variable.name} = ${zpAlloc.address}\t; zp ${variable.datatype} $lenspec") - } + private fun zeropagevars2asm(scope: INameScope) { + val zpVariables = zeropage.variables.filter { it.value.originalScope==scope } + for ((scopedName, zpvar) in zpVariables) { + if (scopedName.size == 2 && scopedName[0] == "cx16" && scopedName[1][0] == 'r' && scopedName[1][1].isDigit()) + continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped + asmgen.out("${scopedName.last()} \t= ${zpvar.address} \t; zp ${zpvar.dt}") + } + } + + private fun nonZpVariables2asm(scope: INameScope) { + asmgen.out("\n; non-zeropage variables") + + val vars = scope.statements + .filterIsInstance() + .filter { + it.type==VarDeclType.VAR && it.scopedName !in zeropage.variables } + + vars.filter { it.datatype == DataType.STR && shouldActuallyOutputStringVar(it) } + .forEach { outputStringvar(it) } + + vars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach { + require(it.zeropage!= ZeropageWish.REQUIRE_ZEROPAGE) + if(!asmgen.isZpVar(it.scopedName)) + vardecl2asm(it) + } } private fun vardecl2asm(decl: VarDecl, nameOverride: String?=null) { @@ -466,48 +463,40 @@ internal class ProgramGen( } } - private fun memdefs2asmVars(vardecls: List, inBlock: Block?) { - val blockname = inBlock?.name - asmgen.out("\n; memdefs and kernal subroutines") - vardecls - .filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST } - .forEach { mv -> - if(mv.value is NumericLiteralValue) - asmgen.out(" ${mv.name} = ${(mv.value as NumericLiteralValue).number.toHex()}") - else - asmgen.out(" ${mv.name} = ${asmgen.asmVariableName((mv.value as AddressOf).identifier)}") - } + private fun memdefsAndConsts2asm(block: Block) { + val mvs = variables.blockMemvars[block] ?: emptySet() + val consts = variables.blockConsts[block] ?: emptySet() + memdefsAndConsts2asm(mvs, consts) } - private fun memdefs2asmAsmsubs(subroutines: List) { - subroutines - .filter { it.isAsmSubroutine } - .forEach { sub-> - val addr = sub.asmAddress - if(addr!=null) { - if(sub.statements.isNotEmpty()) - throw AssemblyError("kernal subroutine cannot have statements") - asmgen.out(" ${sub.name} = ${addr.toHex()}") - } - } + private fun memdefsAndConsts2asm(sub: Subroutine) { + val mvs = variables.subroutineMemvars[sub] ?: emptySet() + val consts = variables.subroutineConsts[sub] ?: emptySet() + memdefsAndConsts2asm(mvs, consts) } - private fun vardecls2asm(vardecls: List) { - asmgen.out("\n; non-zeropage variables") - val vars = vardecls.filter { - it.type==VarDeclType.VAR - && it.zeropage!= ZeropageWish.REQUIRE_ZEROPAGE - && it.scopedName !in zeropage.variables - } - - vars.filter { it.datatype == DataType.STR && shouldActuallyOutputStringVar(it) } - .forEach { outputStringvar(it) } - - vars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach { - require(it.zeropage!= ZeropageWish.REQUIRE_ZEROPAGE) - if(!asmgen.isZpVar(it.scopedName)) - vardecl2asm(it) + private fun memdefsAndConsts2asm( + memvars: Set, + consts: Set + ) { + memvars.forEach { + asmgen.out(" ${it.name} = ${it.address.toHex()}") } + consts.forEach { + if(it.type==DataType.FLOAT) + asmgen.out(" ${it.name} = ${it.value}") + else + asmgen.out(" ${it.name} = ${it.value.toHex()}") + } + } + + private fun asmsubs2asm(statements: List) { + statements + .filter { it is Subroutine && it.isAsmSubroutine && it.asmAddress!=null } + .forEach { asmsub -> + asmsub as Subroutine + asmgen.out(" ${asmsub.name} = ${asmsub.asmAddress!!.toHex()}") + } } private fun shouldActuallyOutputStringVar(strvar: VarDecl): Boolean { @@ -599,4 +588,11 @@ internal class ProgramGen( } } +} + +private fun sameScope(varname: List, scopename: List): Boolean { + if(varname.size!=scopename.size+1) + return false + val pairs = scopename.zip(varname) + return pairs.all { it.first==it.second } } \ No newline at end of file diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt index 829ccff44..aab7eddf0 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt @@ -14,7 +14,7 @@ import prog8.compilerinterface.IVariablesAndConsts import prog8.compilerinterface.ZeropageType -internal class VariableAllocator(val vars: IVariablesAndConsts, val errors: IErrorReporter) { +internal class VariableAllocator(private val vars: IVariablesAndConsts, private val errors: IErrorReporter) { private val subroutineExtras = mutableMapOf() private val memorySlabsInternal = mutableMapOf>() @@ -26,6 +26,10 @@ internal class VariableAllocator(val vars: IVariablesAndConsts, val errors: IErr memorySlabsInternal[name] = Pair(size, align) } + /** + * Allocate variables into the Zeropage. + * The result should be retrieved from the current machine's zeropage object! + */ fun allocateZeropageVariables(options: CompilationOptions) { if(options.zeropage== ZeropageType.DONTUSE) return @@ -42,14 +46,14 @@ internal class VariableAllocator(val vars: IVariablesAndConsts, val errors: IErr varsRequiringZp.forEach { (vardecl, scopedname) -> val numElements = numArrayElements(vardecl) - val result = zeropage.allocate(scopedname, vardecl.datatype, numElements, vardecl.value, vardecl.position, errors) + val result = zeropage.allocate(scopedname, vardecl.datatype, vardecl.definingScope, numElements, vardecl.value, vardecl.position, errors) result.onFailure { errors.err(it.message!!, vardecl.position) } } if(errors.noErrors()) { varsPreferringZp.forEach { (vardecl, scopedname) -> val numElements = numArrayElements(vardecl) - zeropage.allocate(scopedname, vardecl.datatype, numElements, vardecl.value, vardecl.position, errors) + zeropage.allocate(scopedname, vardecl.datatype, vardecl.definingScope, numElements, vardecl.value, vardecl.position, errors) // no need to check for allocation error, if there is one, just allocate in normal system ram. } @@ -59,7 +63,7 @@ internal class VariableAllocator(val vars: IVariablesAndConsts, val errors: IErr for ((vardecl, scopedname) in varsDontCare) { if(vardecl.datatype in IntegerDatatypes) { val numElements = numArrayElements(vardecl) - zeropage.allocate(scopedname, vardecl.datatype, numElements, vardecl.value, vardecl.position, errors) + zeropage.allocate(scopedname, vardecl.datatype, vardecl.definingScope, numElements, vardecl.value, vardecl.position, errors) if(zeropage.free.isEmpty()) break } diff --git a/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt b/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt index e457cd5e5..388a2d189 100644 --- a/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt +++ b/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt @@ -1,5 +1,6 @@ package prog8.codegen.target.cx16 +import prog8.ast.GlobalNamespace import prog8.ast.base.DataType import prog8.compilerinterface.CompilationOptions import prog8.compilerinterface.InternalCompilerException @@ -40,13 +41,16 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) { removeReservedFromFreePool() + // note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses. + // however, to be able for the compiler to "see" them as zero page variables, we have to register them here as well. + val dummyscope = GlobalNamespace(emptyList()) for(reg in 0..15) { - allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2, null, null) // cx16.r0 .. cx16.r15 - allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2, null, null) // cx16.r0s .. cx16.r15s - allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1, null, null) // cx16.r0L .. cx16.r15L - allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1, null, null) // cx16.r0H .. cx16.r15H - allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((2+reg*2).toUInt(), DataType.BYTE, 1, null, null) // cx16.r0sL .. cx16.r15sL - allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1, null, null) // cx16.r0sH .. cx16.r15sH + allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2, dummyscope, null, null) // cx16.r0 .. cx16.r15 + allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2, dummyscope, null, null) // cx16.r0s .. cx16.r15s + allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1, dummyscope, null, null) // cx16.r0L .. cx16.r15L + allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1, dummyscope, null, null) // cx16.r0H .. cx16.r15H + allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((2+reg*2).toUInt(), DataType.BYTE, 1, dummyscope, null, null) // cx16.r0sL .. cx16.r15sL + allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1, dummyscope, null, null) // cx16.r0sH .. cx16.r15sH } } } diff --git a/compiler/test/ZeropageTests.kt b/compiler/test/ZeropageTests.kt index 3805a96c7..3adfb78dd 100644 --- a/compiler/test/ZeropageTests.kt +++ b/compiler/test/ZeropageTests.kt @@ -12,6 +12,7 @@ import io.kotest.matchers.collections.shouldNotBeIn import io.kotest.matchers.comparables.shouldBeGreaterThan import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe +import prog8.ast.GlobalNamespace import prog8.ast.base.DataType import prog8.codegen.target.C64Target import prog8.codegen.target.Cx16Target @@ -60,30 +61,31 @@ class TestC64Zeropage: FunSpec({ val errors = ErrorReporterForTests() val c64target = C64Target() + val zpdummyscope = GlobalNamespace(emptyList()) test("testNames") { val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, c64target)) - var result = zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors) + var result = zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors) result.onFailure { fail(it.toString()) } - result = zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors) + result = zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors) result.onFailure { fail(it.toString()) } - result = zp.allocate(listOf("varname"), DataType.UBYTE, null, null, null, errors) + result = zp.allocate(listOf("varname"), DataType.UBYTE, zpdummyscope, null, null, null, errors) result.onFailure { fail(it.toString()) } - shouldThrow { zp.allocate(listOf("varname"), DataType.UBYTE, null, null, null, errors) } - result = zp.allocate(listOf("varname2"), DataType.UBYTE, null, null, null, errors) + shouldThrow { zp.allocate(listOf("varname"), DataType.UBYTE, zpdummyscope,null, null, null, errors) } + result = zp.allocate(listOf("varname2"), DataType.UBYTE, zpdummyscope, null, null, null, errors) result.onFailure { fail(it.toString()) } } test("testZpFloatEnable") { val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target)) - var result = zp.allocate(emptyList(), DataType.FLOAT, null, null, null, errors) + var result = zp.allocate(emptyList(), DataType.FLOAT, zpdummyscope, null, null, null, errors) result.expectError { "should be allocation error due to disabled floats" } val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, c64target)) - result = zp2.allocate(emptyList(), DataType.FLOAT, null, null, null, errors) + result = zp2.allocate(emptyList(), DataType.FLOAT, zpdummyscope, null, null, null, errors) result.expectError { "should be allocation error due to disabled ZP use" } val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, c64target)) - zp3.allocate(emptyList(), DataType.FLOAT, null, null, null, errors) + zp3.allocate(emptyList(), DataType.FLOAT, zpdummyscope, null, null, null, errors) } test("testZpModesWithFloats") { @@ -105,7 +107,7 @@ class TestC64Zeropage: FunSpec({ val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, c64target)) println(zp.free) zp.availableBytes() shouldBe 0 - val result = zp.allocate(emptyList(), DataType.BYTE, null, null, null, errors) + val result = zp.allocate(emptyList(), DataType.BYTE, zpdummyscope, null, null, null, errors) result.expectError { "expected error due to disabled ZP use" } } @@ -118,9 +120,9 @@ class TestC64Zeropage: FunSpec({ zp3.availableBytes() shouldBe 125 val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target)) zp4.availableBytes() shouldBe 239 - zp4.allocate(listOf("test"), DataType.UBYTE, null, null, null, errors) + zp4.allocate(listOf("test"), DataType.UBYTE, zpdummyscope, null, null, null, errors) zp4.availableBytes() shouldBe 238 - zp4.allocate(listOf("test2"), DataType.UBYTE, null, null, null, errors) + zp4.allocate(listOf("test2"), DataType.UBYTE, zpdummyscope, null, null, null, errors) zp4.availableBytes() shouldBe 237 } @@ -151,19 +153,19 @@ class TestC64Zeropage: FunSpec({ zp.hasByteAvailable() shouldBe true zp.hasWordAvailable() shouldBe true - var result = zp.allocate(emptyList(), DataType.FLOAT, null, null, null, errors) + var result = zp.allocate(emptyList(), DataType.FLOAT, zpdummyscope, null, null, null, errors) result.expectError { "expect allocation error: in regular zp there aren't 5 sequential bytes free" } for (i in 0 until zp.availableBytes()) { - val alloc = zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors) + val alloc = zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors) alloc.getOrElse { throw it } } zp.availableBytes() shouldBe 0 zp.hasByteAvailable() shouldBe false zp.hasWordAvailable() shouldBe false - result = zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors) + result = zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors) result.expectError { "expected allocation error" } - result = zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors) + result = zp.allocate(emptyList(), DataType.UWORD, zpdummyscope, null, null, null, errors) result.expectError { "expected allocation error" } } @@ -172,47 +174,47 @@ class TestC64Zeropage: FunSpec({ zp.availableBytes() shouldBe 239 zp.hasByteAvailable() shouldBe true zp.hasWordAvailable() shouldBe true - var result = zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors) + var result = zp.allocate(emptyList(), DataType.UWORD, zpdummyscope, null, null, null, errors) val loc = result.getOrElse { throw it } .first loc shouldBeGreaterThan 3u loc shouldNotBeIn zp.free val num = zp.availableBytes() / 2 for(i in 0..num-3) { - zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors) + zp.allocate(emptyList(), DataType.UWORD, zpdummyscope, null, null, null, errors) } zp.availableBytes() shouldBe 5 // can't allocate because no more sequential bytes, only fragmented - result = zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors) + result = zp.allocate(emptyList(), DataType.UWORD, zpdummyscope, null, null, null, errors) result.expectError { "should give allocation error" } for(i in 0..4) { - zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors) + zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors) } zp.availableBytes() shouldBe 0 zp.hasByteAvailable() shouldBe false zp.hasWordAvailable() shouldBe false - result = zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors) + result = zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors) result.expectError { "should give allocation error" } } test("testEfficientAllocation") { val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target)) zp.availableBytes() shouldBe 18 - zp.allocate(emptyList(), DataType.WORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x04u - zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x06u - zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x0au - zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x9bu - zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x9eu - zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xa5u - zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xb0u - zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xbeu - zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x0eu - zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x92u - zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x96u - zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xf9u + zp.allocate(emptyList(), DataType.WORD, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x04u + zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x06u + zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x0au + zp.allocate(emptyList(), DataType.UWORD, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x9bu + zp.allocate(emptyList(), DataType.UWORD, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x9eu + zp.allocate(emptyList(), DataType.UWORD, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xa5u + zp.allocate(emptyList(), DataType.UWORD, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xb0u + zp.allocate(emptyList(), DataType.UWORD, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xbeu + zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x0eu + zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x92u + zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x96u + zp.allocate(emptyList(), DataType.UBYTE, zpdummyscope, null, null, null, errors).getOrElse{throw it}.first shouldBe 0xf9u zp.availableBytes() shouldBe 0 } @@ -228,6 +230,7 @@ class TestC64Zeropage: FunSpec({ class TestCx16Zeropage: FunSpec({ val errors = ErrorReporterForTests() val cx16target = Cx16Target() + val zpdummyscope = GlobalNamespace(emptyList()) test("testReservedLocations") { val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, cx16target)) @@ -243,9 +246,9 @@ class TestCx16Zeropage: FunSpec({ zp2.availableBytes() shouldBe 175 val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target)) zp3.availableBytes() shouldBe 216 - zp3.allocate(listOf("test"), DataType.UBYTE, null, null, null, errors) + zp3.allocate(listOf("test"), DataType.UBYTE, zpdummyscope, null, null, null, errors) zp3.availableBytes() shouldBe 215 - zp3.allocate(listOf("test2"), DataType.UBYTE, null, null, null, errors) + zp3.allocate(listOf("test2"), DataType.UBYTE, zpdummyscope, null, null, null, errors) zp3.availableBytes() shouldBe 214 } diff --git a/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt b/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt index 96b19b1dc..457ffe04e 100644 --- a/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt +++ b/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt @@ -3,6 +3,7 @@ package prog8.compilerinterface import com.github.michaelbull.result.Err import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Result +import prog8.ast.INameScope import prog8.ast.base.* import prog8.ast.expressions.ArrayLiteralValue import prog8.ast.expressions.Expression @@ -22,6 +23,7 @@ abstract class Zeropage(protected val options: CompilationOptions) { data class ZpAllocation(val address: UInt, val dt: DataType, val size: Int, + val originalScope: INameScope, val initialStringValue: StringLiteralValue?, val initialArrayValue: ArrayLiteralValue?) @@ -53,6 +55,7 @@ abstract class Zeropage(protected val options: CompilationOptions) { fun allocate(name: List, datatype: DataType, + originalScope: INameScope, numElements: Int?, initValue: Expression?, position: Position?, @@ -92,13 +95,13 @@ abstract class Zeropage(protected val options: CompilationOptions) { if(size==1) { for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) { if(oneSeparateByteFree(candidate)) - return Ok(Pair(makeAllocation(candidate, 1, datatype, name, initValue), 1)) + return Ok(Pair(makeAllocation(candidate, 1, datatype, name, initValue, originalScope), 1)) } - return Ok(Pair(makeAllocation(free[0], 1, datatype, name, initValue), 1)) + return Ok(Pair(makeAllocation(free[0], 1, datatype, name, initValue, originalScope), 1)) } for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) { if (sequentialFree(candidate, size)) - return Ok(Pair(makeAllocation(candidate, size, datatype, name, initValue), size)) + return Ok(Pair(makeAllocation(candidate, size, datatype, name, initValue, originalScope), size)) } } } @@ -108,15 +111,15 @@ abstract class Zeropage(protected val options: CompilationOptions) { private fun reserve(range: UIntRange) = free.removeAll(range) - private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: List, initValue: Expression?): UInt { + private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: List, initValue: Expression?, originalScope: INameScope): UInt { require(size>=0) free.removeAll(address until address+size.toUInt()) allocations[address] = name to datatype if(name.isNotEmpty()) { allocatedVariables[name] = when(datatype) { - in NumericDatatypes -> ZpAllocation(address, datatype, size, null, null) // numerical variables in zeropage never have an initial value here TODO why not? - DataType.STR -> ZpAllocation(address, datatype, size, initValue as? StringLiteralValue, null) - in ArrayDatatypes -> ZpAllocation(address, datatype, size, null, initValue as? ArrayLiteralValue) + in NumericDatatypes -> ZpAllocation(address, datatype, size, originalScope, null, null) // numerical variables in zeropage never have an initial value here TODO why not? + DataType.STR -> ZpAllocation(address, datatype, size, originalScope, initValue as? StringLiteralValue, null) + in ArrayDatatypes -> ZpAllocation(address, datatype, size, originalScope, null, initValue as? ArrayLiteralValue) else -> throw AssemblyError("invalid dt") } } diff --git a/examples/test.p8 b/examples/test.p8 index dd554bcbe..99fb5e06f 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,8 +1,10 @@ +%import floats %import textio %zeropage basicsafe main { ubyte @zp mainglobal1=10 + float @shared fl1 = floats.TWOPI uword [2] nullwords ubyte [2] nullbytes