adding @alignword/page on individual variables

This commit is contained in:
Irmen de Jong 2024-10-26 00:30:14 +02:00
parent 97b8cb748d
commit 1cd754f05d
33 changed files with 433 additions and 119 deletions

View File

@ -2,6 +2,7 @@ package prog8.code
import prog8.code.ast.PtNode
import prog8.code.ast.PtProgram
import prog8.code.ast.PtVariable
import prog8.code.core.*
@ -182,6 +183,7 @@ class StStaticVariable(name: String,
val initializationArrayValue: StArray?,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
val zpwish: ZeropageWish, // used in the variable allocator
val align: PtVariable.Alignment,
astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) {
var initializationNumericValue: Double? = null
@ -216,6 +218,10 @@ class StStaticVariable(name: String,
require(dt == DataType.STR)
require(length == initializationStringValue.first.length+1)
}
if(align!=PtVariable.Alignment.NONE) {
require(dt == DataType.STR || dt in ArrayDatatypes)
require(zpwish != ZeropageWish.REQUIRE_ZEROPAGE && zpwish != ZeropageWish.PREFER_ZEROPAGE)
}
}
}

View File

@ -3,7 +3,6 @@ package prog8.code
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.target.VMTarget
import kotlin.collections.ArrayDeque
class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) {
@ -98,7 +97,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
// if(node.type in SplitWordArrayTypes) {
// ... split array also add _lsb and _msb to symboltable?
// }
val stVar = StStaticVariable(node.name, node.type, initialString, initialArray, numElements, node.zeropage, node)
val stVar = StStaticVariable(node.name, node.type, initialString, initialArray, numElements, node.zeropage, node.align, node)
if(initialNumeric!=null)
stVar.setOnetimeInitNumeric(initialNumeric)
stVar

View File

@ -126,13 +126,18 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
}
is PtVariable -> {
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
val align = when(node.align) {
PtVariable.Alignment.NONE -> ""
PtVariable.Alignment.WORD -> "@alignword"
PtVariable.Alignment.PAGE -> "@alignpage"
}
val str = if(node.arraySize!=null) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[${node.arraySize}] $split ${node.name}"
"${eltType.name.lowercase()}[${node.arraySize}] $split $align ${node.name}"
}
else if(node.type in ArrayDatatypes) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[] $split ${node.name}"
"${eltType.name.lowercase()}[] $split $align ${node.name}"
}
else
"${node.type.name.lowercase()} ${node.name}"

View File

@ -149,10 +149,24 @@ sealed interface IPtVariable {
}
class PtVariable(name: String, override val type: DataType, val zeropage: ZeropageWish, val value: PtExpression?, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
class PtVariable(
name: String,
override val type: DataType,
val zeropage: ZeropageWish,
val align: Alignment,
val value: PtExpression?,
val arraySize: UInt?,
position: Position
) : PtNamedNode(name, position), IPtVariable {
init {
value?.let {it.parent=this}
}
enum class Alignment {
NONE,
WORD,
PAGE
}
}

View File

@ -9,7 +9,6 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.target.Cx16Target
import prog8.codegen.cpu6502.assignment.*
import kotlin.collections.ArrayDeque
import kotlin.io.path.Path
import kotlin.io.path.writeLines
@ -162,7 +161,7 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
else -> throw AssemblyError("weird array value element $elt")
}
}
val result = PtVariable(name, type, zeropage, newValue, arraySize, position)
val result = PtVariable(name, type, zeropage, align, newValue, arraySize, position)
result.parent = parent
result
}

View File

@ -517,12 +517,12 @@ internal class ProgramAndVarsGen(
stringVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.name)+"_init_value"
outputStringvar(varname, it.value.second, it.value.first)
outputStringvar(varname, PtVariable.Alignment.NONE, it.value.second, it.value.first)
}
arrayVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.name)+"_init_value"
arrayVariable2asm(varname, it.alloc.dt, it.value, null)
arrayVariable2asm(varname, it.alloc.dt, PtVariable.Alignment.NONE, it.value, null)
}
asmgen.out("+")
@ -599,6 +599,7 @@ internal class ProgramAndVarsGen(
stringvars.forEach {
outputStringvar(
it.name,
it.align,
it.initializationStringValue!!.second,
it.initializationStringValue!!.first
)
@ -617,11 +618,13 @@ internal class ProgramAndVarsGen(
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
in SplitWordArrayTypes -> {
alignVar(variable.align)
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
}
in ArrayDatatypes -> {
alignVar(variable.align)
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
asmgen.out("${variable.name}\t.fill $numbytes")
}
@ -631,6 +634,14 @@ internal class ProgramAndVarsGen(
}
}
private fun alignVar(align: PtVariable.Alignment) {
when(align) {
PtVariable.Alignment.NONE -> {}
PtVariable.Alignment.WORD -> asmgen.out("\t.align 2")
PtVariable.Alignment.PAGE -> asmgen.out("\t.align $100")
}
}
private fun staticVariable2asm(variable: StStaticVariable) {
val initialValue: Number =
if(variable.initializationNumericValue!=null) {
@ -656,14 +667,15 @@ internal class ProgramAndVarsGen(
DataType.STR -> {
throw AssemblyError("all string vars should have been interned into prog")
}
in ArrayDatatypes -> arrayVariable2asm(variable.name, variable.dt, variable.initializationArrayValue, variable.length)
in ArrayDatatypes -> arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length)
else -> {
throw AssemblyError("weird dt")
}
}
}
private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) {
private fun arrayVariable2asm(varname: String, dt: DataType, align: PtVariable.Alignment, value: StArray?, orNumberOfZeros: Int?) {
alignVar(align)
when(dt) {
DataType.ARRAY_UB, DataType.ARRAY_BOOL -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
@ -759,7 +771,8 @@ internal class ProgramAndVarsGen(
}
}
private fun outputStringvar(varname: String, encoding: Encoding, value: String) {
private fun outputStringvar(varname: String, align: PtVariable.Alignment, encoding: Encoding, value: String) {
alignVar(align)
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }

View File

@ -6,6 +6,7 @@ import prog8.code.StNode
import prog8.code.StNodeType
import prog8.code.StStaticVariable
import prog8.code.SymbolTable
import prog8.code.ast.PtVariable
import prog8.code.core.*
@ -49,8 +50,8 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
val varsDontCare = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align==PtVariable.Alignment.NONE }
require(varsDontCareWithAlignment.size + varsDontCareWithoutAlignment.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
var numVariablesAllocatedInZP = 0
var numberOfNonIntegerVariables = 0
@ -89,7 +90,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
// try to allocate the "don't care" interger variables into the zeropage until it is full.
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
if(errors.noErrors()) {
val sortedList = varsDontCare.sortedByDescending { it.scopedName }
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedName }
for (variable in sortedList) {
if(variable.dt in IntegerDatatypesWithBoolean) {
if(zeropage.free.isEmpty()) {

View File

@ -47,10 +47,42 @@ class TestCodegen: FunSpec({
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
sub.add(PtVariable("particleDX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable(
"pi",
DataType.UBYTE,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
PtNumber(DataType.UBYTE, 0.0, Position.DUMMY),
null,
Position.DUMMY
))
sub.add(PtVariable(
"particleX",
DataType.ARRAY_UB,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
3u,
Position.DUMMY
))
sub.add(PtVariable(
"particleDX",
DataType.ARRAY_UB,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
3u,
Position.DUMMY
))
sub.add(PtVariable(
"xx",
DataType.WORD,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
PtNumber(DataType.WORD, 1.0, Position.DUMMY),
null,
Position.DUMMY
))
val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(false, Position.DUMMY).also {

View File

@ -44,10 +44,42 @@ class TestVmCodeGen: FunSpec({
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
sub.add(PtVariable("particleDX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable(
"pi",
DataType.UBYTE,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
PtNumber(DataType.UBYTE, 0.0, Position.DUMMY),
null,
Position.DUMMY
))
sub.add(PtVariable(
"particleX",
DataType.ARRAY_UB,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
3u,
Position.DUMMY
))
sub.add(PtVariable(
"particleDX",
DataType.ARRAY_UB,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
3u,
Position.DUMMY
))
sub.add(PtVariable(
"xx",
DataType.WORD,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
PtNumber(DataType.WORD, 1.0, Position.DUMMY),
null,
Position.DUMMY
))
val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(false, Position.DUMMY).also {
@ -123,7 +155,15 @@ class TestVmCodeGen: FunSpec({
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"f1",
DataType.FLOAT,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
@ -186,7 +226,15 @@ class TestVmCodeGen: FunSpec({
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"f1",
DataType.FLOAT,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
@ -245,7 +293,15 @@ class TestVmCodeGen: FunSpec({
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"f1",
DataType.FLOAT,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
@ -292,7 +348,15 @@ class TestVmCodeGen: FunSpec({
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"sb1",
DataType.BYTE,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
@ -355,7 +419,15 @@ class TestVmCodeGen: FunSpec({
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"sb1",
DataType.BYTE,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
@ -414,7 +486,15 @@ class TestVmCodeGen: FunSpec({
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
sub.add(PtVariable(
"ub1",
DataType.UBYTE,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))

View File

@ -76,7 +76,7 @@ class VarConstantValueTypeAdjuster(
if (declValue != null) {
// variable is never written to, so it can be replaced with a constant, IF the value is a constant
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.position)
decl.value = null
return listOf(
IAstModification.ReplaceNode(decl, const, parent)
@ -96,7 +96,7 @@ class VarConstantValueTypeAdjuster(
}
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.position)
return listOf(
IAstModification.ReplaceNode(decl, const, parent),
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
@ -271,7 +271,6 @@ class VarConstantValueTypeAdjuster(
// This is needed because further constant optimizations depend on those.
internal class ConstantIdentifierReplacer(
private val program: Program,
private val options: CompilationOptions,
private val errors: IErrorReporter
) : AstWalker() {
@ -399,7 +398,7 @@ internal class ConstantIdentifierReplacer(
if(targetDatatype.isArray) {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOr(DataType.UNDEFINED),
ZeropageWish.DONTCARE, null, "dummy", emptyList(),
assignment.value, false, false, Position.DUMMY)
assignment.value, false, false, VarAlignment.NONE, Position.DUMMY)
val replaceValue = createConstArrayInitializerValue(decl)
if(replaceValue!=null) {
return listOf(IAstModification.ReplaceNode(assignment.value, replaceValue, assignment))

View File

@ -12,7 +12,7 @@ fun Program.constantFold(errors: IErrorReporter, options: CompilationOptions) {
if(errors.noErrors()) {
valuetypefixer.applyModifications()
val replacer = ConstantIdentifierReplacer(this, options, errors)
val replacer = ConstantIdentifierReplacer(this, errors)
replacer.visit(this)
if (errors.noErrors()) {
replacer.applyModifications()

View File

@ -890,6 +890,19 @@ internal class AstChecker(private val program: Program,
errors.err("split can only be used on word arrays", decl.position)
}
}
if(decl.alignment!=VarAlignment.NONE) {
if(!decl.isArray && decl.datatype!=DataType.STR)
err("only string and array variables can have an alignment option")
else if(decl.type==VarDeclType.MEMORY)
err("only normal variables can have an alignment option")
else if (decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage == ZeropageWish.PREFER_ZEROPAGE) {
err("zeropage variables can't have alignment")
} else if(decl.alignment==VarAlignment.PAGE) {
errors.info("page-aligned variables might waste a lot of memory (check Gaps in assembler output)", decl.position)
}
}
super.visit(decl)
}

View File

@ -197,7 +197,7 @@ class AstPreprocessor(val program: Program,
private fun makeSplitArray(decl: VarDecl): Iterable<IAstModification> {
val newDecl = VarDecl(
decl.type, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, emptyList(),
decl.value, decl.sharedWithAsm, true, decl.position
decl.value, decl.sharedWithAsm, true, decl.alignment, decl.position
)
return listOf(IAstModification.ReplaceNode(decl, newDecl, decl.parent))
}

View File

@ -11,9 +11,9 @@ import prog8.ast.statements.*
import prog8.code.ast.*
import prog8.code.core.*
import prog8.compiler.builtinFunctionReturnType
import java.io.File
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
import java.io.File
/**
@ -520,6 +520,11 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
when(srcVar.type) {
VarDeclType.VAR -> {
val value = if(srcVar.value!=null) transformExpression(srcVar.value!!) else null
val align = when(srcVar.alignment) {
VarAlignment.NONE -> PtVariable.Alignment.NONE
VarAlignment.WORD -> PtVariable.Alignment.WORD
VarAlignment.PAGE -> PtVariable.Alignment.PAGE
}
if(srcVar.isArray) {
if(value==null) {
val blockOptions = srcVar.definingBlock.options()
@ -529,11 +534,27 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
repeat(srcVar.arraysize!!.constIndex()!!) {
zeros.children.add(PtNumber(ArrayToElementTypes.getValue(srcVar.datatype), 0.0, srcVar.position))
}
return PtVariable(srcVar.name, srcVar.datatype, srcVar.zeropage, zeros, srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position)
return PtVariable(
srcVar.name,
srcVar.datatype,
srcVar.zeropage,
align,
zeros,
srcVar.arraysize?.constIndex()?.toUInt(),
srcVar.position
)
}
}
}
return PtVariable(srcVar.name, srcVar.datatype, srcVar.zeropage, value, srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position)
return PtVariable(
srcVar.name,
srcVar.datatype,
srcVar.zeropage,
align,
value,
srcVar.arraysize?.constIndex()?.toUInt(),
srcVar.position
)
}
VarDeclType.CONST -> return PtConstant(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number, srcVar.position)
VarDeclType.MEMORY -> return PtMemMapped(srcVar.name, srcVar.datatype, (srcVar.value as NumericLiteral).number.toUInt(), srcVar.arraysize?.constIndex()?.toUInt(), srcVar.position)

View File

@ -41,7 +41,15 @@ private fun setDeferMasks(program: PtProgram, errors: IErrorReporter): Map<PtSub
}
// define the bitmask variable and set it to zero
val deferVariable = PtVariable(maskVarName, DataType.UBYTE, ZeropageWish.NOT_IN_ZEROPAGE, null, null, sub.position)
val deferVariable = PtVariable(
maskVarName,
DataType.UBYTE,
ZeropageWish.NOT_IN_ZEROPAGE,
PtVariable.Alignment.NONE,
null,
null,
sub.position
)
val assignZero = PtAssignment(sub.position)
assignZero.add(PtAssignTarget(false, sub.position).also {
it.add(PtIdentifier(sub.scopedName+"."+maskVarName, DataType.UBYTE, sub.position))

View File

@ -6,6 +6,7 @@ import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.ast.statements.Assignment
import prog8.ast.statements.VarAlignment
import prog8.ast.statements.VarDecl
import prog8.ast.statements.WhenChoice
import prog8.ast.walk.AstWalker
@ -94,6 +95,11 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
// and not in CodeDesugarer, that one is too late (identifiers can't be found otherwise)
if(decl.datatype !in NumericDatatypesWithBoolean)
errors.err("can only multi declare numeric and boolean variables", decl.position)
if(decl.alignment != VarAlignment.NONE) {
errors.err("only single variable declarations can have alignment", decl.position)
return noModifications
}
if(errors.noErrors()) {
// desugar into individual vardecl per name.
return decl.desugarMultiDecl().map {

View File

@ -220,6 +220,7 @@ internal class StatementReorderer(
null,
it.sharedWithAsm,
it.splitArray,
it.alignment,
it.position
)
IAstModification.ReplaceNode(it, newvar, subroutine)

View File

@ -12,7 +12,10 @@ import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.PrefixExpression
import prog8.ast.statements.*
import prog8.code.core.*
import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.code.core.SourceCode
import prog8.code.core.ZeropageWish
import prog8.code.target.*
import prog8tests.helpers.DummyFunctions
import prog8tests.helpers.DummyMemsizer
@ -111,7 +114,7 @@ class TestMemory: FunSpec({
}
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget {
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, VarAlignment.NONE, Position.DUMMY)
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -149,7 +152,7 @@ class TestMemory: FunSpec({
}
test("regular variable not in mapped IO ram on C64") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, VarAlignment.NONE, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -161,7 +164,7 @@ class TestMemory: FunSpec({
test("memory mapped variable not in mapped IO ram on C64") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, VarAlignment.NONE, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -173,7 +176,7 @@ class TestMemory: FunSpec({
test("memory mapped variable in mapped IO ram on C64") {
val address = 0xd020u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, VarAlignment.NONE, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@ -184,7 +187,7 @@ class TestMemory: FunSpec({
}
test("array not in mapped IO ram") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), null, false, false, VarAlignment.NONE, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -197,7 +200,7 @@ class TestMemory: FunSpec({
test("memory mapped array not in mapped IO ram") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, VarAlignment.NONE, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@ -210,7 +213,7 @@ class TestMemory: FunSpec({
test("memory mapped array in mapped IO ram") {
val address = 0xd800u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, VarAlignment.NONE, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)

View File

@ -85,14 +85,14 @@ class TestSymbolTable: FunSpec({
test("static vars") {
val node = PtIdentifier("dummy", DataType.UBYTE, Position.DUMMY)
val stVar1 = StStaticVariable("initialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, node)
val stVar1 = StStaticVariable("initialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, PtVariable.Alignment.NONE, node)
stVar1.setOnetimeInitNumeric(99.0)
val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, node)
val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, PtVariable.Alignment.NONE, node)
val arrayInitNonzero = listOf(StArrayElement(1.1, null, null), StArrayElement(2.2, null, null), StArrayElement(3.3, null, null))
val arrayInitAllzero = listOf(StArrayElement(0.0, null, null), StArrayElement(0.0, null, null), StArrayElement(0.0, null, null))
val stVar3 = StStaticVariable("initialized", DataType.ARRAY_UW, null, arrayInitNonzero, 3, ZeropageWish.DONTCARE, node)
val stVar4 = StStaticVariable("initialized", DataType.ARRAY_UW, null, arrayInitAllzero, 3, ZeropageWish.DONTCARE, node)
val stVar5 = StStaticVariable("uninitialized", DataType.ARRAY_UW, null, null, 3, ZeropageWish.DONTCARE, node)
val stVar3 = StStaticVariable("initialized", DataType.ARRAY_UW, null, arrayInitNonzero, 3, ZeropageWish.DONTCARE, PtVariable.Alignment.NONE, node)
val stVar4 = StStaticVariable("initialized", DataType.ARRAY_UW, null, arrayInitAllzero, 3, ZeropageWish.DONTCARE, PtVariable.Alignment.NONE, node)
val stVar5 = StStaticVariable("uninitialized", DataType.ARRAY_UW, null, null, 3, ZeropageWish.DONTCARE, PtVariable.Alignment.NONE, node)
stVar1.uninitialized shouldBe false
stVar1.length shouldBe null
@ -119,12 +119,60 @@ private fun makeSt(): SymbolTable {
astBlock1.add(astConstant2)
val astSub1 = PtSub("sub1", emptyList(), null, Position.DUMMY)
val astSub2 = PtSub("sub2", emptyList(), null, Position.DUMMY)
val astSub1v1 = PtVariable("v1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)
val astSub1v2 = PtVariable("v2", DataType.BYTE, ZeropageWish.DONTCARE,null, null, Position.DUMMY)
val astSub1v3 = PtVariable("v3", DataType.FLOAT, ZeropageWish.DONTCARE,null, null, Position.DUMMY)
val astSub1v4 = PtVariable("slab1", DataType.UWORD, ZeropageWish.DONTCARE,null, null, Position.DUMMY)
val astSub2v1 = PtVariable("v1", DataType.BYTE, ZeropageWish.DONTCARE,null, null, Position.DUMMY)
val astSub2v2 = PtVariable("v2", DataType.BYTE, ZeropageWish.DONTCARE,null, null, Position.DUMMY)
val astSub1v1 = PtVariable(
"v1",
DataType.BYTE,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
)
val astSub1v2 = PtVariable(
"v2",
DataType.BYTE,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
)
val astSub1v3 = PtVariable(
"v3",
DataType.FLOAT,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
)
val astSub1v4 = PtVariable(
"slab1",
DataType.UWORD,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
)
val astSub2v1 = PtVariable(
"v1",
DataType.BYTE,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
)
val astSub2v2 = PtVariable(
"v2",
DataType.BYTE,
ZeropageWish.DONTCARE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
)
astSub1.add(astSub1v1)
astSub1.add(astSub1v2)
astSub1.add(astSub1v3)
@ -156,12 +204,12 @@ private fun makeSt(): SymbolTable {
block1.add(sub12)
block1.add(StConstant("c1", DataType.UWORD, 12345.0, astConstant1))
block1.add(StConstant("blockc", DataType.UWORD, 999.0, astConstant2))
sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub1v1))
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub1v2))
sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, PtVariable.Alignment.NONE, astSub1v1))
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, PtVariable.Alignment.NONE, astSub1v2))
sub11.add(StMemVar("v3", DataType.FLOAT, 12345u, null, astSub1v3))
sub11.add(StMemorySlab("slab1", 200u, 64u, astSub1v4))
sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub2v1))
sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, astSub2v2))
sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, PtVariable.Alignment.NONE, astSub2v1))
sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, PtVariable.Alignment.NONE, astSub2v2))
val block2 = StNode("block2", StNodeType.BLOCK, astBlock2)
val sub21 = StNode("sub1", StNodeType.SUBROUTINE, astSub21)
val sub22 = StNode("sub2", StNodeType.SUBROUTINE, astSub22)

View File

@ -46,8 +46,8 @@ class TestAsmGenSymbols: StringSpec({
}
*/
val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", emptyList(), NumericLiteral.optimalInteger(1234, Position.DUMMY), false, false, Position.DUMMY)
val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", emptyList(), null, false, false, Position.DUMMY)
val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "localvar", emptyList(), NumericLiteral.optimalInteger(1234, Position.DUMMY), false, false, VarAlignment.NONE, Position.DUMMY)
val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "tgt", emptyList(), null, false, false, VarAlignment.NONE, Position.DUMMY)
val labelInSub = Label("locallabel", Position.DUMMY)
val tgt = AssignTarget(IdentifierReference(listOf("tgt"), Position.DUMMY), null, null, null, false, Position.DUMMY)
@ -63,7 +63,7 @@ class TestAsmGenSymbols: StringSpec({
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
val subroutine = Subroutine("start", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, statements, Position.DUMMY)
val labelInBlock = Label("label_outside", Position.DUMMY)
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", emptyList(),null, false, false, Position.DUMMY)
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", emptyList(),null, false, false, VarAlignment.NONE, Position.DUMMY)
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))

View File

@ -157,6 +157,11 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
output(" @zp")
if(decl.sharedWithAsm)
output(" @shared")
when(decl.alignment) {
VarAlignment.NONE -> {}
VarAlignment.WORD -> output(" @alignword")
VarAlignment.PAGE -> output(" @alignpage")
}
if(decl.names.size>1)
output(decl.names.joinToString(prefix=" "))
else

View File

@ -87,7 +87,7 @@ class Program(val name: String,
val varName = "string_${internedStringsBlock.statements.size}"
val decl = VarDecl(
VarDeclType.VAR, VarDeclOrigin.STRINGLITERAL, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, emptyList(), string,
sharedWithAsm = false, splitArray = false, position = string.position
sharedWithAsm = false, splitArray = false, alignment = VarAlignment.NONE, position = string.position
)
internedStringsBlock.statements.add(decl)
decl.linkParents(internedStringsBlock)

View File

@ -314,6 +314,8 @@ private fun SubroutineContext.toAst() : Subroutine {
private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
vardecl().map {
val options = it.decloptions()
if(options.ALIGNPAGE().isNotEmpty() || options.ALIGNWORD().isNotEmpty())
throw SyntaxError("cannot use alignments on parameters", it.toPosition())
val zp = getZpOption(options)
var datatype = it.datatype()?.toAst() ?: DataType.UNDEFINED
if(it.ARRAYSIG()!=null || it.arrayindex()!=null)
@ -718,6 +720,10 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
val name = if(identifiers.size==1) identifiername.text else "<multiple>"
val isArray = ARRAYSIG() != null || arrayindex() != null
val split = options.SPLIT().isNotEmpty()
val alignword = options.ALIGNWORD().isNotEmpty()
val alignpage = options.ALIGNPAGE().isNotEmpty()
if(alignpage && alignword)
throw SyntaxError("choose a single alignment option", toPosition())
val origDt = datatype()?.toAst() ?: DataType.UNDEFINED
val dt = if(isArray) {
val arrayDt = ElementToArrayTypes.getValue(origDt)
@ -743,6 +749,7 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
value,
options.SHARED().isNotEmpty(),
split,
if(alignword) VarAlignment.WORD else if(alignpage) VarAlignment.PAGE else VarAlignment.NONE,
toPosition()
)
}

View File

@ -216,6 +216,11 @@ enum class VarDeclType {
MEMORY
}
enum class VarAlignment {
NONE,
WORD,
PAGE
}
class VarDecl(val type: VarDeclType,
val origin: VarDeclOrigin,
@ -227,6 +232,7 @@ class VarDecl(val type: VarDeclType,
var value: Expression?,
val sharedWithAsm: Boolean,
val splitArray: Boolean,
val alignment: VarAlignment,
override val position: Position) : Statement(), INamedStatement {
override lateinit var parent: Node
var allowInitializeWithZero = true
@ -239,6 +245,7 @@ class VarDecl(val type: VarDeclType,
return VarDecl(VarDeclType.VAR, VarDeclOrigin.SUBROUTINEPARAM, dt, param.zp, null, param.name, emptyList(), null,
sharedWithAsm = false,
splitArray = false,
alignment = VarAlignment.NONE,
position = param.position
)
}
@ -248,7 +255,7 @@ class VarDecl(val type: VarDeclType,
val arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
val arraysize = ArrayIndex.forArray(array)
return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, emptyList(), array,
sharedWithAsm = false, splitArray = splitArray, position = array.position)
sharedWithAsm = false, splitArray = splitArray, alignment = VarAlignment.NONE, position = array.position)
}
}
@ -285,7 +292,7 @@ class VarDecl(val type: VarDeclType,
if(names.size>1)
throw FatalAstException("should not copy a vardecl that still has multiple names")
val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), name, names, value?.copy(),
sharedWithAsm, splitArray, position)
sharedWithAsm, splitArray, alignment, position)
copy.allowInitializeWithZero = this.allowInitializeWithZero
return copy
}
@ -295,23 +302,24 @@ class VarDecl(val type: VarDeclType,
this.arraysize?.referencesIdentifier(nameInSource)==true
fun desugarMultiDecl(): List<VarDecl> {
require(alignment==VarAlignment.NONE)
if(value==null || value?.isSimple==true) {
// just copy the initialization value to a separata vardecl for each component
// just copy the initialization value to a separate vardecl for each component
return names.map {
val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), it, emptyList(), value?.copy(),
sharedWithAsm, splitArray, position)
sharedWithAsm, splitArray, alignment, position)
copy.allowInitializeWithZero = this.allowInitializeWithZero
copy
}
} else {
// evaluate the value once in the vardecl for the first component, and set the other components to the first
val first = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), names[0], emptyList(), value?.copy(),
sharedWithAsm, splitArray, position)
sharedWithAsm, splitArray, alignment, position)
first.allowInitializeWithZero = this.allowInitializeWithZero
val firstVar = firstVarAsValue(first)
return listOf(first) + names.drop(1 ).map {
val copy = VarDecl(type, origin, datatype, zeropage, arraysize?.copy(), it, emptyList(), firstVar.copy(),
sharedWithAsm, splitArray, position)
sharedWithAsm, splitArray, alignment, position)
copy.allowInitializeWithZero = this.allowInitializeWithZero
copy
}

View File

@ -1,6 +1,13 @@
TODO
====
- are uninitialized (bss) variables correctly @aligned now? (%option align docs say they're not, but maybe the new @align tag fixes this too)
- aligned vars codegen: sort to do all word alignments first then the page alignments
- what to use to align a label ? (%align $100 ?) to support aligned asmincludes for example.
- remove %option align_xxx ? (block level alignment, as we now have individual variable alignments)
Improve register load order in subroutine call args assignments:
in certain situations, the "wrong" order of evaluation of function call arguments is done which results
in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)
@ -29,8 +36,6 @@ Future Things and Ideas
- OR.... make all this more generic and use some %segment option to create real segments for 64tass?
- (need separate step in codegen and IR to write the "golden" variables)
- do we need (array)variable alignment tag instead of block alignment tag? You want to align the data, not the code in the block?
- ir: related to the one above: block alignment doesn't translate well to variables in the block (the actual stuff that needs to be aligned in memory) but: need variable alignment tag instead of block alignment tag, really
- ir: fix call() return value handling
- ir: proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
- ir: idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)

View File

@ -1,23 +1,20 @@
%import textio
%option no_sysinit
%zeropage basicsafe
main {
sub start() {
word @shared ww = 11111
txt.print_ub(if ww==11111 111 else 222)
txt.spc()
txt.print_ub(if ww!=11111 111 else 222)
txt.nl()
if ww==11111
txt.print("one\n")
else
txt.print("two\n")
if ww!=11111
txt.print("one\n")
else
txt.print("two\n")
str @alignword @shared name = "wordaligned"
str @alignpage @shared @nozp name2 = "pagealigned"
ubyte[20] @alignword @shared array1
ubyte[20] @alignword @shared array2
ubyte[20] @alignpage @shared array3
ubyte[20] @alignpage @shared array4
ubyte[] @alignword @shared array5 = [1,2,3,4]
ubyte[] @alignword @shared array6 = [1,2,3,4]
ubyte[] @alignpage @shared array7 = [1,2,3,4]
ubyte[] @alignpage @shared array8 = [1,2,3,4]
uword[20] @alignword @split @shared array9
uword[] @alignword @split @shared array10 = [1111,2222,3333,4444]
}
}

View File

@ -157,19 +157,20 @@ class IRFileReader {
return if(text.isBlank())
emptyList()
else {
val varPattern = Regex("(.+?)(\\[.+?\\])? (.+) (zp=(.+))?")
val varPattern = Regex("(.+?)(\\[.+?\\])? (.+) zp=(.+) align=(.+)")
val variables = mutableListOf<StStaticVariable>()
text.lineSequence().forEach { line ->
// example: uword main.start.qq2 zp=DONTCARE
val match = varPattern.matchEntire(line) ?: throw IRParseException("invalid VARIABLESNOINIT $line")
val (type, arrayspec, name, _, zpwish) = match.destructured
val (type, arrayspec, name, zpwish, alignment) = match.destructured
if('.' !in name)
throw IRParseException("unscoped varname: $name")
val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null
val dt = parseDatatype(type, arraysize!=null)
val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish)
val dummyNode = PtVariable(name, dt, zp, null, null, Position.DUMMY)
val newVar = StStaticVariable(name, dt, null, null, arraysize, zp, dummyNode)
val align = if(alignment.isBlank()) PtVariable.Alignment.NONE else PtVariable.Alignment.valueOf(alignment)
val dummyNode = PtVariable(name, dt, zp, align, null, null, Position.DUMMY)
val newVar = StStaticVariable(name, dt, null, null, arraysize, zp, align, dummyNode)
variables.add(newVar)
}
return variables
@ -186,19 +187,20 @@ class IRFileReader {
return if(text.isBlank())
emptyList()
else {
val varPattern = Regex("(.+?)(\\[.+?\\])? (.+)=(.*?) (zp=(.+))?")
val varPattern = Regex("(.+?)(\\[.+?\\])? (.+)=(.*?) zp=(.+) align=(.+)")
val variables = mutableListOf<StStaticVariable>()
text.lineSequence().forEach { line ->
// examples:
// uword main.start.qq2=0 zp=REQUIRE_ZP
// ubyte[6] main.start.namestring=105,114,109,101,110,0
val match = varPattern.matchEntire(line) ?: throw IRParseException("invalid VARIABLE $line")
val (type, arrayspec, name, value, _, zpwish) = match.destructured
val (type, arrayspec, name, value, zpwish, alignment) = match.destructured
if('.' !in name)
throw IRParseException("unscoped varname: $name")
val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null
val dt: DataType = parseDatatype(type, arraysize!=null)
val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish)
val align = if(alignment.isBlank()) PtVariable.Alignment.NONE else PtVariable.Alignment.valueOf(alignment)
var initNumeric: Double? = null
var initArray: StArray? = null
when(dt) {
@ -220,11 +222,11 @@ class IRFileReader {
DataType.STR -> throw IRParseException("STR should have been converted to byte array")
else -> throw IRParseException("weird dt")
}
val dummyNode = PtVariable(name, dt, zp, null, null, Position.DUMMY)
val dummyNode = PtVariable(name, dt, zp, align, null, null, Position.DUMMY)
if(arraysize!=null && initArray!=null && initArray.all { it.number==0.0 }) {
initArray=null // arrays with just zeros can be left uninitialized
}
val stVar = StStaticVariable(name, dt, null, initArray, arraysize, zp, dummyNode)
val stVar = StStaticVariable(name, dt, null, initArray, arraysize, zp, align, dummyNode)
if(initNumeric!=null)
stVar.setOnetimeInitNumeric(initNumeric)
variables.add(stVar)
@ -253,7 +255,15 @@ class IRFileReader {
val (type, arrayspec, name, address) = match.destructured
val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null
val dt: DataType = parseDatatype(type, arraysize!=null)
val dummyNode = PtVariable(name, dt, ZeropageWish.NOT_IN_ZEROPAGE, null, null, Position.DUMMY)
val dummyNode = PtVariable(
name,
dt,
ZeropageWish.NOT_IN_ZEROPAGE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
)
memvars.add(StMemVar(name, dt, parseIRValue(address).toUInt(), arraysize, dummyNode))
}
memvars
@ -276,7 +286,15 @@ class IRFileReader {
// example: "slabname 4096 0"
val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid slab $line")
val (name, size, align) = match.destructured
val dummyNode = PtVariable(name, DataType.ARRAY_UB, ZeropageWish.NOT_IN_ZEROPAGE, null, null, Position.DUMMY)
val dummyNode = PtVariable(
name,
DataType.ARRAY_UB,
ZeropageWish.NOT_IN_ZEROPAGE,
PtVariable.Alignment.NONE,
null,
null,
Position.DUMMY
)
slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), dummyNode))
}
slabs

View File

@ -210,10 +210,10 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
for (variable in variablesNoInit) {
if(variable.dt in SplitWordArrayTypes) {
// split into 2 ubyte arrays lsb+msb
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_lsb zp=${variable.zpwish}\n")
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb zp=${variable.zpwish}\n")
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_lsb zp=${variable.zpwish} align=${variable.align}\n")
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb zp=${variable.zpwish} align=${variable.align}\n")
} else {
xml.writeCharacters("${variable.typeString} ${variable.name} zp=${variable.zpwish}\n")
xml.writeCharacters("${variable.typeString} ${variable.name} zp=${variable.zpwish} align=${variable.align}\n")
}
}
@ -243,8 +243,8 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
"@>${it.addressOfSymbol}"
}
}
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_lsb=$lsbValue zp=${variable.zpwish}\n")
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb=$msbValue zp=${variable.zpwish}\n")
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_lsb=$lsbValue zp=${variable.zpwish} align=${variable.align}\n")
xml.writeCharacters("ubyte[${variable.length}] ${variable.name}_msb=$msbValue zp=${variable.zpwish} align=${variable.align}\n")
} else {
val value: String = when(variable.dt) {
DataType.BOOL -> variable.onetimeInitializationNumericValue?.toInt()?.toString() ?: ""
@ -279,7 +279,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
}
else -> throw InternalCompilerException("weird dt")
}
xml.writeCharacters("${variable.typeString} ${variable.name}=$value zp=${variable.zpwish}\n")
xml.writeCharacters("${variable.typeString} ${variable.name}=$value zp=${variable.zpwish} align=${variable.align}\n")
}
}
xml.writeEndElement()

View File

@ -1,10 +1,8 @@
package prog8.intermediate
import prog8.code.*
import prog8.code.core.DataType
import prog8.code.core.Encoding
import prog8.code.core.ZeropageWish
import prog8.code.core.internedStringsModuleName
import prog8.code.ast.PtVariable
import prog8.code.core.*
// In the Intermediate Representation, all nesting has been removed.
@ -86,7 +84,8 @@ class IRSymbolTable {
variable.initializationStringValue,
fixupAddressOfInArray(variable.initializationArrayValue),
variable.length,
variable.zpwish
variable.zpwish,
IRStStaticVariable.mapAlign(variable.align)
)
}
table[scopedName] = varToadd
@ -196,7 +195,8 @@ class IRStStaticVariable(name: String,
val onetimeInitializationStringValue: IRStString?,
val onetimeInitializationArrayValue: IRStArray?,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
val zpwish: ZeropageWish // used in the variable allocator
val zpwish: ZeropageWish, // used in the variable allocator
val align: Alignment,
) : IRStNode(name, IRStNodeType.STATICVAR) {
companion object {
fun from(variable: StStaticVariable): IRStStaticVariable {
@ -206,7 +206,29 @@ class IRStStaticVariable(name: String,
variable.initializationStringValue,
variable.initializationArrayValue?.map { IRStArrayElement.from(it) },
variable.length,
variable.zpwish)
variable.zpwish,
mapAlign(variable.align))
}
fun mapAlign(ptAlign: PtVariable.Alignment): Alignment {
return when(ptAlign) {
PtVariable.Alignment.NONE -> Alignment.NONE
PtVariable.Alignment.WORD -> Alignment.WORD
PtVariable.Alignment.PAGE -> Alignment.PAGE
}
}
}
enum class Alignment {
NONE,
WORD,
PAGE
}
init {
if(align!=Alignment.NONE) {
require(dt == DataType.STR || dt in ArrayDatatypes)
require(zpwish != ZeropageWish.REQUIRE_ZEROPAGE && zpwish != ZeropageWish.PREFER_ZEROPAGE)
}
}

View File

@ -51,11 +51,11 @@ loadAddress=$0000
</ASMSYMBOLS>
<VARIABLESNOINIT>
uword sys.bssvar zp=DONTCARE
uword sys.bssvar zp=DONTCARE align=NONE
</VARIABLESNOINIT>
<VARIABLESWITHINIT>
uword sys.wait.jiffies=10 zp=DONTCARE
ubyte[3] sys.emptystring=0,0,0 zp=DONTCARE
uword sys.wait.jiffies=10 zp=DONTCARE align=NONE
ubyte[3] sys.emptystring=0,0,0 zp=DONTCARE align=NONE
</VARIABLESWITHINIT>
<MEMORYMAPPEDVARIABLES>

View File

@ -63,6 +63,10 @@ SHARED : '@shared' ;
SPLIT: '@split' ;
ALIGNWORD: '@alignword' ;
ALIGNPAGE: '@alignpage' ;
ARRAYSIG : '[' [ \t]* ']' ;
NOT_IN: 'not' [ \t]+ 'in' [ \t] ;
@ -143,7 +147,7 @@ directivearg : stringliteral | identifier | integerliteral ;
vardecl: datatype (arrayindex | ARRAYSIG)? decloptions identifier (',' identifier)* ;
decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | ZEROPAGENOT | SPLIT)* ;
decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | ZEROPAGENOT | SPLIT | ALIGNWORD | ALIGNPAGE)* ;
varinitializer : vardecl '=' expression ;

View File

@ -12,8 +12,8 @@
<option name="HAS_STRING_ESCAPES" value="true" />
</options>
<keywords keywords="&amp;;-&gt;;@;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%option;%output;%zeropage;%zpallowed;%zpreserved;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
<keywords3 keywords="@nozp;@requirezp;@shared;@split;@zp;bool;byte;const;float;str;ubyte;uword;void;word" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%option;%output;%zeropage;%zpallowed;%zpreserved;@alignpage;@alignword;@nozp;@requirezp;@shared;@split;@zp;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
<keywords3 keywords="bool;byte;const;float;str;ubyte;uword;void;word" />
<keywords4 keywords="abs;call;callfar;callfar2;clamp;cmp;defer;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt" />
</highlighting>
<extensionMap>

View File

@ -43,7 +43,7 @@ syn region prog8ArrayType matchgroup=prog8Type
\ start="\<\%(u\?byte\|u\?word\|float\|str\|bool\)\[" end="\]"
\ transparent
syn keyword prog8StorageClass const
syn match prog8StorageClass "\(^\|\s\)\(@zp\|@shared\|@split\|@nozp\|@requirezp\)\>"
syn match prog8StorageClass "\(^\|\s\)\(@zp\|@shared\|@split\|@nozp\|@requirezp\|@alignword\|@alignpage\)\>"
syn region prog8Block start="{" end="}" transparent
syn region prog8Expression start="(" end=")" transparent