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") val counterVar = makeLabel("counter")
when(dt) { when(dt) {
DataType.UBYTE, DataType.UWORD -> { 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( result.fold(
success = { (address, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) }, success = { (address, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally

View File

@ -292,7 +292,7 @@ $loopLabel sty $indexVar
} }
if(length>=16) { if(length>=16) {
// allocate index var on ZP if possible // 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( result.fold(
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") } failure = { asmgen.out("$indexVar .byte 0") }
@ -333,7 +333,7 @@ $loopLabel sty $indexVar
} }
if(length>=16) { if(length>=16) {
// allocate index var on ZP if possible // 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( result.fold(
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") } failure = { asmgen.out("$indexVar .byte 0") }

View File

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

View File

@ -5,7 +5,6 @@ import com.github.michaelbull.result.onSuccess
import prog8.ast.base.ArrayDatatypes import prog8.ast.base.ArrayDatatypes
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.IntegerDatatypes import prog8.ast.base.IntegerDatatypes
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.Subroutine import prog8.ast.statements.Subroutine
import prog8.ast.statements.ZeropageWish import prog8.ast.statements.ZeropageWish
import prog8.compilerinterface.* import prog8.compilerinterface.*
@ -53,7 +52,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
variable.scopedName, variable.scopedName,
variable.dt, variable.dt,
numElements, numElements,
variable.initialvalue,
variable.position, variable.position,
errors errors
) )
@ -74,7 +72,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
variable.scopedName, variable.scopedName,
variable.dt, variable.dt,
numElements, numElements,
variable.initialvalue,
variable.position, variable.position,
errors errors
) )
@ -95,7 +92,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
variable.scopedName, variable.scopedName,
variable.dt, variable.dt,
numElements, numElements,
variable.initialvalue,
variable.position, variable.position,
errors errors
) )
@ -130,7 +126,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
private fun numArrayElements(variable: StStaticVariable) = private fun numArrayElements(variable: StStaticVariable) =
when(variable.dt) { 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!! in ArrayDatatypes -> variable.arraysize!!
else -> null 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. // 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. // 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) { 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}")] = 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, null, null) // cx16.r0s .. cx16.r15s 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, null, null) // cx16.r0L .. cx16.r15L 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, null, null) // cx16.r0H .. cx16.r15H 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, null, null) // cx16.r0sL .. cx16.r15sL 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, null, null) // cx16.r0sH .. cx16.r15sH 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.Program
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.base.VarDeclType import prog8.ast.base.VarDeclType
import prog8.ast.expressions.ArrayLiteral
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.Block import prog8.ast.statements.Block
import prog8.ast.statements.Label import prog8.ast.statements.Label
import prog8.ast.statements.Subroutine import prog8.ast.statements.Subroutine
@ -49,7 +51,14 @@ internal class SymbolTableMaker: IAstVisitor {
override fun visit(decl: VarDecl) { override fun visit(decl: VarDecl) {
val node = val node =
when(decl.type) { 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.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) 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 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) { override fun visit(label: Label) {
val node = StNode(label.name, StNodeType.LABEL, label.position) val node = StNode(label.name, StNodeType.LABEL, label.position)
scopestack.peek().add(node) scopestack.peek().add(node)

View File

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

View File

@ -64,26 +64,26 @@ class TestC64Zeropage: FunSpec({
test("testNames") { test("testNames") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, c64target)) 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.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.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()) } result.onFailure { fail(it.toString()) }
shouldThrow<IllegalArgumentException> { zp.allocate(listOf("varname"), 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, null, errors) result = zp.allocate(listOf("varname2"), DataType.UBYTE, null, null, errors)
result.onFailure { fail(it.toString()) } result.onFailure { fail(it.toString()) }
} }
test("testZpFloatEnable") { test("testZpFloatEnable") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target)) 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" } result.expectError { "should be allocation error due to disabled floats" }
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, c64target)) 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" } 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)) 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") { test("testZpModesWithFloats") {
@ -105,7 +105,7 @@ class TestC64Zeropage: FunSpec({
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, c64target)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, c64target))
println(zp.free) println(zp.free)
zp.availableBytes() shouldBe 0 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" } result.expectError { "expected error due to disabled ZP use" }
} }
@ -118,9 +118,9 @@ class TestC64Zeropage: FunSpec({
zp3.availableBytes() shouldBe 125 zp3.availableBytes() shouldBe 125
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target)) val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, c64target))
zp4.availableBytes() shouldBe 239 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.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 zp4.availableBytes() shouldBe 237
} }
@ -151,19 +151,19 @@ class TestC64Zeropage: FunSpec({
zp.hasByteAvailable() shouldBe true zp.hasByteAvailable() shouldBe true
zp.hasWordAvailable() 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" } result.expectError { "expect allocation error: in regular zp there aren't 5 sequential bytes free" }
for (i in 0 until zp.availableBytes()) { 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 } alloc.getOrElse { throw it }
} }
zp.availableBytes() shouldBe 0 zp.availableBytes() shouldBe 0
zp.hasByteAvailable() shouldBe false zp.hasByteAvailable() shouldBe false
zp.hasWordAvailable() 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.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" } result.expectError { "expected allocation error" }
} }
@ -172,47 +172,47 @@ class TestC64Zeropage: FunSpec({
zp.availableBytes() shouldBe 239 zp.availableBytes() shouldBe 239
zp.hasByteAvailable() shouldBe true zp.hasByteAvailable() shouldBe true
zp.hasWordAvailable() 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 val loc = result.getOrElse { throw it } .first
loc shouldBeGreaterThan 3u loc shouldBeGreaterThan 3u
loc shouldNotBeIn zp.free loc shouldNotBeIn zp.free
val num = zp.availableBytes() / 2 val num = zp.availableBytes() / 2
for(i in 0..num-3) { 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 zp.availableBytes() shouldBe 5
// can't allocate because no more sequential bytes, only fragmented // 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" } result.expectError { "should give allocation error" }
for(i in 0..4) { 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.availableBytes() shouldBe 0
zp.hasByteAvailable() shouldBe false zp.hasByteAvailable() shouldBe false
zp.hasWordAvailable() 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" } result.expectError { "should give allocation error" }
} }
test("testEfficientAllocation") { test("testEfficientAllocation") {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, c64target))
zp.availableBytes() shouldBe 18 zp.availableBytes() shouldBe 18
zp.allocate(emptyList(), DataType.WORD, null, null, null, errors).getOrElse{throw it}.first shouldBe 0x04u zp.allocate(emptyList(), DataType.WORD, 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, 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.UBYTE, 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, 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, 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, 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, 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.UWORD, 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, 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, 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, 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.UBYTE, null, null, errors).getOrElse{throw it}.first shouldBe 0xf9u
zp.availableBytes() shouldBe 0 zp.availableBytes() shouldBe 0
} }
@ -243,9 +243,9 @@ class TestCx16Zeropage: FunSpec({
zp2.availableBytes() shouldBe 175 zp2.availableBytes() shouldBe 175
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target)) val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, cx16target))
zp3.availableBytes() shouldBe 216 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.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 zp3.availableBytes() shouldBe 214
} }

View File

@ -3,7 +3,6 @@ package prog8.compilerinterface
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.expressions.Expression
import prog8.ast.statements.ZeropageWish import prog8.ast.statements.ZeropageWish
import prog8.ast.toHex import prog8.ast.toHex
@ -123,15 +122,26 @@ open class StNode(val name: String,
class StStaticVariable(name: String, class StStaticVariable(name: String,
val dt: DataType, val dt: DataType,
val initialvalue: Expression?, val initialNumericValue: Double?,
val initialStringValue: Pair<String, Encoding>?,
val initialArrayValue: DoubleArray?,
val arraysize: Int?, val arraysize: Int?,
val zpw: ZeropageWish, val zpw: ZeropageWish,
position: Position) : StNode(name, StNodeType.STATICVAR, position) { 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() { 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) : class StConstant(name: String, val dt: DataType, val value: Double, position: Position) :
StNode(name, StNodeType.CONSTANT, position) { StNode(name, StNodeType.CONSTANT, position) {
override fun printProperties() { 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.Ok
import com.github.michaelbull.result.Result import com.github.michaelbull.result.Result
import prog8.ast.base.* 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) 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_W1 : UInt // temp storage 1 for a word $fb+$fc
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
data class ZpAllocation(val address: UInt, data class ZpAllocation(val address: UInt, val dt: DataType, val size: Int)
val dt: DataType,
val size: Int,
val initialStringValue: StringLiteral?,
val initialArrayValue: ArrayLiteral?)
// the variables allocated into Zeropage. // the variables allocated into Zeropage.
// name (scoped) ==> pair of address to (Datatype + bytesize) // name (scoped) ==> pair of address to (Datatype + bytesize)
@ -52,7 +45,6 @@ abstract class Zeropage(protected val options: CompilationOptions) {
fun allocate(name: List<String>, fun allocate(name: List<String>,
datatype: DataType, datatype: DataType,
numElements: Int?, numElements: Int?,
initValue: Expression?,
position: Position?, position: Position?,
errors: IErrorReporter): Result<Pair<UInt, Int>, ZeropageAllocationError> { errors: IErrorReporter): Result<Pair<UInt, Int>, ZeropageAllocationError> {
@ -90,13 +82,13 @@ abstract class Zeropage(protected val options: CompilationOptions) {
if(size==1) { if(size==1) {
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) { for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
if(oneSeparateByteFree(candidate)) 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) { for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
if (sequentialFree(candidate, size)) 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 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) require(size>=0)
free.removeAll(address until address+size.toUInt()) free.removeAll(address until address+size.toUInt())
if(name.isNotEmpty()) { if(name.isNotEmpty()) {
allocatedVariables[name] = when(datatype) { 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 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, initValue as? StringLiteral, null) DataType.STR -> ZpAllocation(address, datatype, size)
in ArrayDatatypes -> ZpAllocation(address, datatype, size, null, initValue as? ArrayLiteral) in ArrayDatatypes -> ZpAllocation(address, datatype, size)
else -> throw AssemblyError("invalid dt") 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 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. 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!!
... ...