diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 02e7e4939..6bbc7556c 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -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 diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt index aabe7f7e6..506710332 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt @@ -292,7 +292,7 @@ $loopLabel sty $indexVar } if(length>=16) { // allocate index var on ZP if possible - val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, null, stmt.position, asmgen.errors) + val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, 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") } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index b476d976b..fcf684e31 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -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, + val alloc: Zeropage.ZpAllocation, + val value: Pair + ) + + private class ZpArrayWithInitial( + val name: List, + val alloc: Zeropage.ZpAllocation, + val value: DoubleArray + ) + + private fun getZpStringVarsWithInitvalue(): Collection { + val result = mutableListOf() + 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 { + val result = mutableListOf() + 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>) { 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", "")}\"") + private fun outputStringvar(varname: String, encoding: Encoding, value: String) { + asmgen.out("$varname\t; $encoding:\"${escape(value).replace("\u0000", "")}\"") 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 { - val array = value?.value ?: - Array(orNumberOfZeros!!) { defaultZero(ArrayToElementTypes.getValue(dt), Position.DUMMY) } + private fun makeArrayFillDataUnsigned(dt: DataType, value: DoubleArray?, orNumberOfZeros: Int?): List { + 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 { - val array = value?.value ?: - Array(orNumberOfZeros!!) { defaultZero(ArrayToElementTypes.getValue(dt), Position.DUMMY) } + private fun makeArrayFillDataSigned(dt: DataType, value: DoubleArray?, orNumberOfZeros: Int?): List { + 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" diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt index b87e7e0d2..710f3cd9b 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt @@ -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 } diff --git a/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt b/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt index 949c362ae..012d7cc62 100644 --- a/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt +++ b/codeGenTargets/src/prog8/codegen/target/cx16/CX16Zeropage.kt @@ -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 } } } diff --git a/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt b/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt index 58c6a6b92..3f43b6814 100644 --- a/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt @@ -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) diff --git a/compiler/test/TestSymbolTable.kt b/compiler/test/TestSymbolTable.kt index 5d3ddbd2b..fdafc1ffd 100644 --- a/compiler/test/TestSymbolTable.kt +++ b/compiler/test/TestSymbolTable.kt @@ -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) diff --git a/compiler/test/ZeropageTests.kt b/compiler/test/ZeropageTests.kt index c135d892a..577102de6 100644 --- a/compiler/test/ZeropageTests.kt +++ b/compiler/test/ZeropageTests.kt @@ -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 { zp.allocate(listOf("varname"), DataType.UBYTE,null, null, null, errors) } - result = zp.allocate(listOf("varname2"), DataType.UBYTE, null, null, null, errors) + shouldThrow { zp.allocate(listOf("varname"), DataType.UBYTE,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 } diff --git a/compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt b/compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt index fa8e9fc56..7f0a7a6cc 100644 --- a/compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt +++ b/compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt @@ -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?, + 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() { diff --git a/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt b/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt index 777310f25..4fa268dde 100644 --- a/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt +++ b/compilerInterfaces/src/prog8/compilerinterface/Zeropage.kt @@ -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, datatype: DataType, numElements: Int?, - initValue: Expression?, position: Position?, errors: IErrorReporter): Result, 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, initValue: Expression?): UInt { + private fun makeAllocation(address: UInt, size: Int, datatype: DataType, name: List): 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") } } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index c0e00f1f2..5347e84f9 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -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!! + ...