mirror of
https://github.com/irmen/prog8.git
synced 2025-04-07 16:41:46 +00:00
streamlining vars asmgen using new mechanism
This commit is contained in:
parent
98b2855b9c
commit
b043c3a6da
@ -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
|
||||
|
@ -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") }
|
||||
|
@ -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 }
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user