streamlining vars asmgen using new mechanism

This commit is contained in:
Irmen de Jong 2022-02-09 17:35:49 +01:00
parent 98b2855b9c
commit b043c3a6da
8 changed files with 146 additions and 133 deletions

View File

@ -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

View File

@ -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") }

View File

@ -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<VarDecl>()
zeropagevars2asm(vardecls)
memdefs2asmVars(vardecls, block)
memdefs2asmAsmsubs(block.statements.filterIsInstance<Subroutine>())
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<VarDecl>()
zeropagevars2asm(vardecls)
memdefs2asmVars(vardecls, null)
memdefs2asmAsmsubs(sub.statements.filterIsInstance<Subroutine>())
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<VarDecl>) {
// 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<VarDecl>()
.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<VarDecl>, 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<Subroutine>) {
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<VarDecl>) {
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<IVariablesAndConsts.MemoryMappedVariable>,
consts: Set<IVariablesAndConsts.ConstantNumberSymbol>
) {
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<Statement>) {
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<String>, scopename: List<String>): Boolean {
if(varname.size!=scopename.size+1)
return false
val pairs = scopename.zip(varname)
return pairs.all { it.first==it.second }
}

View File

@ -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<Subroutine, SubroutineExtraAsmInfo>()
private val memorySlabsInternal = mutableMapOf<String, Pair<UInt, UInt>>()
@ -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
}

View File

@ -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
}
}
}

View File

@ -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<IllegalArgumentException> { zp.allocate(listOf("varname"), DataType.UBYTE, null, null, null, errors) }
result = zp.allocate(listOf("varname2"), DataType.UBYTE, null, null, null, errors)
shouldThrow<IllegalArgumentException> { 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
}

View File

@ -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<String>,
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<String>, initValue: Expression?): UInt {
private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: List<String>, 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")
}
}

View File

@ -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