unravel more dependency of SymbolTable on the ASt nodes (Expression), and fix initializing zp-allocated array

This commit is contained in:
Irmen de Jong 2022-03-09 01:42:05 +01:00
parent a58e5a3399
commit 5a54066f81
11 changed files with 183 additions and 137 deletions

View File

@ -751,7 +751,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, 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, 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, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }

View File

@ -2,8 +2,10 @@ package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.antlr.escape
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.base.ArrayDatatypes
import prog8.ast.base.ByteDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.RegisterOrPair
import prog8.ast.statements.*
import prog8.ast.toHex
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
@ -377,12 +379,12 @@ internal class ProgramAndVarsGen(
}
// string and array variables in zeropage that have initializer value, should be initialized
val stringVarsWithInitInZp = allocator.zeropageVars.filter { it.value.dt==DataType.STR && it.value.initialStringValue!=null }
val arrayVarsWithInitInZp = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes && it.value.initialArrayValue!=null }
val stringVarsWithInitInZp = getZpStringVarsWithInitvalue()
val arrayVarsWithInitInZp = getZpArrayVarsWithInitvalue()
if(stringVarsWithInitInZp.isNotEmpty() || arrayVarsWithInitInZp.isNotEmpty()) {
asmgen.out("; zp str and array initializations")
stringVarsWithInitInZp.forEach {
val name = asmgen.asmVariableName(it.key)
val name = asmgen.asmVariableName(it.name)
asmgen.out("""
lda #<${name}
ldy #>${name}
@ -393,8 +395,8 @@ internal class ProgramAndVarsGen(
jsr prog8_lib.strcpy""")
}
arrayVarsWithInitInZp.forEach {
val size = it.value.size
val name = asmgen.asmVariableName(it.key)
val size = it.alloc.size
val name = asmgen.asmVariableName(it.name)
asmgen.out("""
lda #<${name}_init_value
ldy #>${name}_init_value
@ -412,14 +414,13 @@ internal class ProgramAndVarsGen(
}
stringVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.key)+"_init_value"
val stringvalue = it.value.initialStringValue!!
outputStringvar(varname, it.value.dt, stringvalue.encoding, stringvalue.value)
val varname = asmgen.asmVariableName(it.name)+"_init_value"
outputStringvar(varname, it.value.second, it.value.first)
}
arrayVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.key)+"_init_value"
arrayVariable2asm(varname, it.value.dt, it.value.initialArrayValue!!, null)
val varname = asmgen.asmVariableName(it.name)+"_init_value"
arrayVariable2asm(varname, it.alloc.dt, it.value, null)
}
asmgen.out("""+ tsx
@ -429,6 +430,40 @@ internal class ProgramAndVarsGen(
clc""")
}
private class ZpStringWithInitial(
val name: List<String>,
val alloc: Zeropage.ZpAllocation,
val value: Pair<String, Encoding>
)
private class ZpArrayWithInitial(
val name: List<String>,
val alloc: Zeropage.ZpAllocation,
val value: DoubleArray
)
private fun getZpStringVarsWithInitvalue(): Collection<ZpStringWithInitial> {
val result = mutableListOf<ZpStringWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
for (variable in vars) {
val svar = symboltable.lookup(variable.key) as StStaticVariable // TODO faster in flat lookup table
if(svar.initialStringValue!=null)
result.add(ZpStringWithInitial(variable.key, variable.value, svar.initialStringValue!!))
}
return result
}
private fun getZpArrayVarsWithInitvalue(): Collection<ZpArrayWithInitial> {
val result = mutableListOf<ZpArrayWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
for (variable in vars) {
val svar = symboltable.lookup(variable.key) as StStaticVariable // TODO faster in flat lookup table
if(svar.initialArrayValue!=null)
result.add(ZpArrayWithInitial(variable.key, variable.value, svar.initialArrayValue!!))
}
return result
}
private fun zeropagevars2asm(varNames: Set<List<String>>) {
val zpVariables = allocator.zeropageVars.filter { it.key in varNames }
for ((scopedName, zpvar) in zpVariables) {
@ -443,8 +478,7 @@ internal class ProgramAndVarsGen(
asmgen.out("; non-zeropage variables")
val (stringvars, othervars) = variables.partition { it.dt==DataType.STR }
stringvars.forEach {
val stringvalue = it.initialvalue as StringLiteral
outputStringvar(it.name, it.dt, stringvalue.encoding, stringvalue.value)
outputStringvar(it.name, it.initialStringValue!!.second, it.initialStringValue!!.first)
}
othervars.sortedBy { it.type }.forEach {
staticVariable2asm(it)
@ -453,45 +487,38 @@ internal class ProgramAndVarsGen(
private fun staticVariable2asm(variable: StStaticVariable) {
val name = variable.name
val value = variable.initialvalue
val staticValue: Number =
if(value!=null) {
if(value is NumericLiteral) {
if(value.type== DataType.FLOAT)
value.number
else
value.number.toInt()
} else {
if(variable.dt in NumericDatatypes)
throw AssemblyError("can only deal with constant numeric values for global vars")
else 0
}
val initialValue: Number =
if(variable.initialNumericValue!=null) {
if(variable.dt== DataType.FLOAT)
variable.initialNumericValue!!
else
variable.initialNumericValue!!.toInt()
} else 0
when (variable.dt) {
DataType.UBYTE -> asmgen.out("$name\t.byte ${staticValue.toHex()}")
DataType.BYTE -> asmgen.out("$name\t.char $staticValue")
DataType.UWORD -> asmgen.out("$name\t.word ${staticValue.toHex()}")
DataType.WORD -> asmgen.out("$name\t.sint $staticValue")
DataType.UBYTE -> asmgen.out("$name\t.byte ${initialValue.toHex()}")
DataType.BYTE -> asmgen.out("$name\t.char $initialValue")
DataType.UWORD -> asmgen.out("$name\t.word ${initialValue.toHex()}")
DataType.WORD -> asmgen.out("$name\t.sint $initialValue")
DataType.FLOAT -> {
if(staticValue==0) {
if(initialValue==0) {
asmgen.out("$name\t.byte 0,0,0,0,0 ; float")
} else {
val floatFill = compTarget.machine.getFloat(staticValue).makeFloatFillAsm()
asmgen.out("$name\t.byte $floatFill ; float $staticValue")
val floatFill = compTarget.machine.getFloat(initialValue).makeFloatFillAsm()
asmgen.out("$name\t.byte $floatFill ; float $initialValue")
}
}
DataType.STR -> {
throw AssemblyError("all string vars should have been interned into prog")
}
in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, value as? ArrayLiteral, variable.arraysize)
in ArrayDatatypes -> arrayVariable2asm(name, variable.dt, variable.initialArrayValue, variable.arraysize)
else -> {
throw AssemblyError("weird dt")
}
}
}
private fun arrayVariable2asm(varname: String, dt: DataType, value: ArrayLiteral?, orNumberOfZeros: Int?) {
private fun arrayVariable2asm(varname: String, dt: DataType, value: DoubleArray?, orNumberOfZeros: Int?) {
when(dt) {
DataType.ARRAY_UB -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
@ -534,11 +561,9 @@ internal class ProgramAndVarsGen(
}
}
DataType.ARRAY_F -> {
val array = value?.value ?:
Array(orNumberOfZeros!!) { defaultZero(ArrayToElementTypes.getValue(dt), Position.DUMMY) }
val array = value ?: DoubleArray(orNumberOfZeros!!)
val floatFills = array.map {
val number = (it as NumericLiteral).number
compTarget.machine.getFloat(number).makeFloatFillAsm()
compTarget.machine.getFloat(it).makeFloatFillAsm()
}
asmgen.out(varname)
for (f in array.zip(floatFills))
@ -569,56 +594,56 @@ internal class ProgramAndVarsGen(
}
}
private fun outputStringvar(varname: String, dt: DataType, encoding: Encoding, value: String) {
asmgen.out("$varname\t; $dt $encoding:\"${escape(value).replace("\u0000", "<NULL>")}\"")
private fun outputStringvar(varname: String, encoding: Encoding, value: String) {
asmgen.out("$varname\t; $encoding:\"${escape(value).replace("\u0000", "<NULL>")}\"")
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
for (chunk in outputBytes.chunked(16))
asmgen.out(" .byte " + chunk.joinToString())
}
private fun makeArrayFillDataUnsigned(dt: DataType, value: ArrayLiteral?, orNumberOfZeros: Int?): List<String> {
val array = value?.value ?:
Array(orNumberOfZeros!!) { defaultZero(ArrayToElementTypes.getValue(dt), Position.DUMMY) }
private fun makeArrayFillDataUnsigned(dt: DataType, value: DoubleArray?, orNumberOfZeros: Int?): List<String> {
val array = value ?: DoubleArray(orNumberOfZeros!!)
return when (dt) {
DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
val number = (it as NumericLiteral).number.toInt()
val number = it.toInt()
"$"+number.toString(16).padStart(2, '0')
}
DataType.ARRAY_UW -> array.map {
when (it) {
is NumericLiteral -> {
"$" + it.number.toInt().toString(16).padStart(4, '0')
}
is AddressOf -> {
asmgen.asmSymbolName(it.identifier)
}
is IdentifierReference -> {
asmgen.asmSymbolName(it)
}
else -> throw AssemblyError("weird array elt dt")
}
"$" + it.toInt().toString(16).padStart(4, '0')
// TODO: initial array literals with address-of, or just variable references, are no longer possible at this time
// when (it) {
// is NumericLiteral -> {
// "$" + it.number.toInt().toString(16).padStart(4, '0')
// }
// is AddressOf -> {
// asmgen.asmSymbolName(it.identifier)
// }
// is IdentifierReference -> {
// asmgen.asmSymbolName(it)
// }
// else -> throw AssemblyError("weird array elt dt")
// }
}
else -> throw AssemblyError("invalid dt")
}
}
private fun makeArrayFillDataSigned(dt: DataType, value: ArrayLiteral?, orNumberOfZeros: Int?): List<String> {
val array = value?.value ?:
Array(orNumberOfZeros!!) { defaultZero(ArrayToElementTypes.getValue(dt), Position.DUMMY) }
private fun makeArrayFillDataSigned(dt: DataType, value: DoubleArray?, orNumberOfZeros: Int?): List<String> {
val array = value ?: DoubleArray(orNumberOfZeros!!)
return when (dt) {
DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
val number = (it as NumericLiteral).number.toInt()
val number = it.toInt()
"$"+number.toString(16).padStart(2, '0')
}
DataType.ARRAY_B ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
val number = (it as NumericLiteral).number.toInt()
val number = it.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(2, '0')
if(number>=0)
"$$hexnum"
@ -626,11 +651,11 @@ internal class ProgramAndVarsGen(
"-$$hexnum"
}
DataType.ARRAY_UW -> array.map {
val number = (it as NumericLiteral).number.toInt()
val number = it.toInt()
"$" + number.toString(16).padStart(4, '0')
}
DataType.ARRAY_W -> array.map {
val number = (it as NumericLiteral).number.toInt()
val number = it.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
if(number>=0)
"$$hexnum"

View File

@ -5,7 +5,6 @@ import com.github.michaelbull.result.onSuccess
import prog8.ast.base.ArrayDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.IntegerDatatypes
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.Subroutine
import prog8.ast.statements.ZeropageWish
import prog8.compilerinterface.*
@ -53,7 +52,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
variable.scopedName,
variable.dt,
numElements,
variable.initialvalue,
variable.position,
errors
)
@ -74,7 +72,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
variable.scopedName,
variable.dt,
numElements,
variable.initialvalue,
variable.position,
errors
)
@ -95,7 +92,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
variable.scopedName,
variable.dt,
numElements,
variable.initialvalue,
variable.position,
errors
)
@ -130,7 +126,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
private fun numArrayElements(variable: StStaticVariable) =
when(variable.dt) {
DataType.STR -> (variable.initialvalue as StringLiteral).value.length
DataType.STR -> variable.initialStringValue!!.first.length+1 // 1 extra because of 0 termination char
in ArrayDatatypes -> variable.arraysize!!
else -> null
}

View File

@ -43,12 +43,12 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
// 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.
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) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
}
}
}

View File

@ -3,7 +3,9 @@ package prog8.compiler.astprocessing
import prog8.ast.Program
import prog8.ast.base.Position
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.ArrayLiteral
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.Block
import prog8.ast.statements.Label
import prog8.ast.statements.Subroutine
@ -49,7 +51,14 @@ internal class SymbolTableMaker: IAstVisitor {
override fun visit(decl: VarDecl) {
val node =
when(decl.type) {
VarDeclType.VAR -> StStaticVariable(decl.name, decl.datatype, decl.value, decl.arraysize?.constIndex(), decl.zeropage, decl.position)
VarDeclType.VAR -> {
val initialNumeric = (decl.value as? NumericLiteral)?.number
val initialStringLit = decl.value as? StringLiteral
val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding)
val initialArrayLit = decl.value as? ArrayLiteral
val initialArray = makeInitialArray(initialArrayLit)
StStaticVariable(decl.name, decl.datatype, initialNumeric, initialString, initialArray, decl.arraysize?.constIndex(), decl.zeropage, decl.position)
}
VarDeclType.CONST -> StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, decl.position)
VarDeclType.MEMORY -> StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), decl.position)
}
@ -57,6 +66,17 @@ internal class SymbolTableMaker: IAstVisitor {
st.origAstLinks[decl] = node
}
private fun makeInitialArray(arrayLit: ArrayLiteral?): DoubleArray? {
if(arrayLit==null)
return null
return arrayLit.value.map {
if(it !is NumericLiteral)
TODO("ability to have addressof or variables inside array literal currently not possible @ ${arrayLit.position}")
else
it.number
}.toDoubleArray()
}
override fun visit(label: Label) {
val node = StNode(label.name, StNodeType.LABEL, label.position)
scopestack.peek().add(node)

View File

@ -71,10 +71,10 @@ private fun makeSt(): SymbolTable {
block1.add(sub12)
block1.add(StConstant("c1", DataType.UWORD, 12345.0, Position.DUMMY))
block1.add(StConstant("blockc", DataType.UWORD, 999.0, Position.DUMMY))
sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY))
val block2 = StNode("block2", StNodeType.BLOCK, Position.DUMMY)
val sub21 = StNode("sub1", StNodeType.SUBROUTINE, Position.DUMMY)

View File

@ -64,26 +64,26 @@ class TestC64Zeropage: FunSpec({
test("testNames") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.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, null, null, errors)
result.onFailure { fail(it.toString()) }
result = zp.allocate(emptyList(), DataType.UBYTE, null, null, null, errors)
result = zp.allocate(emptyList(), DataType.UBYTE, 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, 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,null, null, errors) }
result = zp.allocate(listOf("varname2"), DataType.UBYTE, null, null, errors)
result.onFailure { fail(it.toString()) }
}
test("testZpFloatEnable") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.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, null, null, errors)
result.expectError { "should be allocation error due to disabled floats" }
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, c64target))
result = zp2.allocate(emptyList(), DataType.FLOAT, null, null, null, errors)
result = zp2.allocate(emptyList(), DataType.FLOAT, null, null, errors)
result.expectError { "should be allocation error due to disabled ZP use" }
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, c64target))
zp3.allocate(emptyList(), DataType.FLOAT, null, null, null, errors)
zp3.allocate(emptyList(), DataType.FLOAT, null, null, errors)
}
test("testZpModesWithFloats") {
@ -105,7 +105,7 @@ class TestC64Zeropage: FunSpec({
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.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, null, null, errors)
result.expectError { "expected error due to disabled ZP use" }
}
@ -118,9 +118,9 @@ class TestC64Zeropage: FunSpec({
zp3.availableBytes() shouldBe 125
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.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, null, null, errors)
zp4.availableBytes() shouldBe 238
zp4.allocate(listOf("test2"), DataType.UBYTE, null, null, null, errors)
zp4.allocate(listOf("test2"), DataType.UBYTE, null, null, errors)
zp4.availableBytes() shouldBe 237
}
@ -151,19 +151,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, 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, 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, null, null, errors)
result.expectError { "expected allocation error" }
result = zp.allocate(emptyList(), DataType.UWORD, null, null, null, errors)
result = zp.allocate(emptyList(), DataType.UWORD, null, null, errors)
result.expectError { "expected allocation error" }
}
@ -172,47 +172,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, 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, 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, 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, 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, null, null, errors)
result.expectError { "should give allocation error" }
}
test("testEfficientAllocation") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.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, null, null, errors).getOrElse{throw it}.first shouldBe 0x04u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x06u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x0au
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0x9bu
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0x9eu
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xa5u
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xb0u
zp.allocate(emptyList(), DataType.UWORD, null, null, errors).getOrElse{throw it}.first shouldBe 0xbeu
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x0eu
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x92u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0x96u
zp.allocate(emptyList(), DataType.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0xf9u
zp.availableBytes() shouldBe 0
}
@ -243,9 +243,9 @@ class TestCx16Zeropage: FunSpec({
zp2.availableBytes() shouldBe 175
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.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, null, null, errors)
zp3.availableBytes() shouldBe 215
zp3.allocate(listOf("test2"), DataType.UBYTE, null, null, null, errors)
zp3.allocate(listOf("test2"), DataType.UBYTE, null, null, errors)
zp3.availableBytes() shouldBe 214
}

View File

@ -3,7 +3,6 @@ package prog8.compilerinterface
import prog8.ast.Node
import prog8.ast.base.DataType
import prog8.ast.base.Position
import prog8.ast.expressions.Expression
import prog8.ast.statements.ZeropageWish
import prog8.ast.toHex
@ -123,15 +122,26 @@ open class StNode(val name: String,
class StStaticVariable(name: String,
val dt: DataType,
val initialvalue: Expression?,
val initialNumericValue: Double?,
val initialStringValue: Pair<String, Encoding>?,
val initialArrayValue: DoubleArray?,
val arraysize: Int?,
val zpw: ZeropageWish,
position: Position) : StNode(name, StNodeType.STATICVAR, position) {
init {
if(arraysize!=null && initialArrayValue!=null)
require(arraysize == initialArrayValue.size)
if(arraysize!=null || initialArrayValue!=null)
require(initialStringValue==null && initialNumericValue==null)
}
override fun printProperties() {
print("$name dt=$dt initialval=$initialvalue arraysize=$arraysize zpw=$zpw")
print("$name dt=$dt zpw=$zpw")
}
}
class StConstant(name: String, val dt: DataType, val value: Double, position: Position) :
StNode(name, StNodeType.CONSTANT, position) {
override fun printProperties() {

View File

@ -4,9 +4,6 @@ import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import prog8.ast.base.*
import prog8.ast.expressions.ArrayLiteral
import prog8.ast.expressions.Expression
import prog8.ast.expressions.StringLiteral
class ZeropageAllocationError(message: String) : Exception(message)
@ -19,11 +16,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
data class ZpAllocation(val address: UInt,
val dt: DataType,
val size: Int,
val initialStringValue: StringLiteral?,
val initialArrayValue: ArrayLiteral?)
data class ZpAllocation(val address: UInt, val dt: DataType, val size: Int)
// the variables allocated into Zeropage.
// name (scoped) ==> pair of address to (Datatype + bytesize)
@ -52,7 +45,6 @@ abstract class Zeropage(protected val options: CompilationOptions) {
fun allocate(name: List<String>,
datatype: DataType,
numElements: Int?,
initValue: Expression?,
position: Position?,
errors: IErrorReporter): Result<Pair<UInt, Int>, ZeropageAllocationError> {
@ -90,13 +82,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), 1))
}
return Ok(Pair(makeAllocation(free[0], 1, datatype, name, initValue), 1))
return Ok(Pair(makeAllocation(free[0], 1, datatype, name), 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), size))
}
}
}
@ -106,14 +98,14 @@ 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>): UInt {
require(size>=0)
free.removeAll(address until address+size.toUInt())
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 because they are set in separate initializer assignments
DataType.STR -> ZpAllocation(address, datatype, size, initValue as? StringLiteral, null)
in ArrayDatatypes -> ZpAllocation(address, datatype, size, null, initValue as? ArrayLiteral)
in NumericDatatypes -> ZpAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
DataType.STR -> ZpAllocation(address, datatype, size)
in ArrayDatatypes -> ZpAllocation(address, datatype, size)
else -> throw AssemblyError("invalid dt")
}
}

View File

@ -7,6 +7,9 @@ For next release
no vardecls in it anymore (these are part of the symboltable) and baked types, so no inferType too.
no name lookup in the Ast, always do this in the symbol table.
- codegen makeInitialArray()/makeArrayFillDataUnsigned():
initial array literals with address-of, or just variable references, are no longer possible at this time!!
...