6502 struct allocation to asm file, struct name and field prefixing (maybe unneeded...)

This commit is contained in:
Irmen de Jong
2025-08-15 20:23:45 +02:00
parent 729efb04e1
commit 2f60716082
9 changed files with 148 additions and 47 deletions
@@ -35,12 +35,10 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
when(node) {
is PtAsmSub, is PtSub -> node.name = "p8s_${node.name}"
is PtBlock -> node.name = "p8b_${node.name}"
is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // only prefix user defined labels
is PtConstant -> node.name = "p8c_${node.name}"
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> {
node.name = "p8v_${node.name}"
}
is PtStructDecl -> { /* do nothing */ }
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
is PtStructDecl -> node.name = "p8t_${node.name}"
}
}
@@ -66,7 +64,6 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
if(node.type.isSplitWordArray && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) {
lookupName = lookupName.dropLast(4)
}
// TODO how to deal with struct field names? Prefix or not?
val stNode = st.lookup(lookupName) ?: throw AssemblyError("unknown identifier $node")
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
@@ -82,6 +79,17 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
is PtStructDecl -> prefixNamedNode(node) // note: field names are not prefixed here, we take care of that at asm generation time, which was a lot easier
is PtBuiltinFunctionCall -> {
// could be a struct instance creation
if(node.name=="prog8_lib_structalloc") {
val struct = node.type.subType!!
if(struct is StStruct) {
// update link to active symboltable node
node.type.subType = st.lookup(struct.scopedNameString) as StStruct
}
}
}
else -> { }
}
node.children.forEach { prefixSymbols(it) }
@@ -228,6 +236,7 @@ private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
StNodeType.CONSTANT -> 'c'
StNodeType.BUILTINFUNC -> 's'
StNodeType.MEMORYSLAB -> 'v'
StNodeType.STRUCT -> 't'
StNodeType.STRUCTINSTANCE -> 'i'
else -> '?'
}
@@ -1,5 +1,6 @@
package prog8.codegen.cpu6502
import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.*
@@ -397,11 +398,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcStructAlloc(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
if(discardResult)
throw AssemblyError("should not discard result of struct allocation at $fcall")
val struct = fcall.type.subType!!
// ... don't need to pay attention to args here because struct instance is put together elsewhere we just have to get a pointer to it
val slabname = PtIdentifier("????TODO-STRUCTINSTANCENAME????", DataType.UWORD, fcall.position) // TODO STRUCTNAME
val slabname = SymbolTable.labelnameForStructInstance(fcall)
val addressOf = PtAddressOf(fcall.type, true, fcall.position)
addressOf.add(slabname)
addressOf.add(PtIdentifier(slabname, fcall.type, fcall.position))
addressOf.parent = fcall
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, fcall.type, expression = addressOf)
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
@@ -44,6 +44,7 @@ internal class ProgramAndVarsGen(
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
}
structInstances2asm()
memorySlabs()
footer()
}
@@ -207,18 +208,20 @@ internal class ProgramAndVarsGen(
private fun memorySlabs() {
if(symboltable.allMemorySlabs.isNotEmpty()) {
asmgen.out("; memory slabs\n .section slabs_BSS")
asmgen.out("; memory slabs\n .section BSS_SLABS")
asmgen.out("prog8_slabs\t.block")
for (slab in symboltable.allMemorySlabs) {
if (slab.align > 1u)
asmgen.out("\t.align ${slab.align.toHex()}")
asmgen.out("${slab.name}\t.fill ${slab.size}")
}
asmgen.out("\t.bend\n .send slabs_BSS")
asmgen.out("\t.bend\n .send BSS_SLABS")
}
}
private fun footer() {
asmgen.out(" .dsection STRUCTINSTANCES\n")
var relocateBssVars = false
var relocateBssSlabs = false
var relocatedBssStart = 0u
@@ -274,14 +277,14 @@ internal class ProgramAndVarsGen(
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
if(relocateBssVars) {
if(!relocateBssSlabs)
asmgen.out(" .dsection slabs_BSS")
asmgen.out(" .dsection BSS_SLABS")
asmgen.out("prog8_program_end\t; end of program label for progend()")
asmgen.out(" * = ${relocatedBssStart.toHex()}")
asmgen.out(" .dsection BSS_NOCLEAR")
asmgen.out("prog8_bss_section_start")
asmgen.out(" .dsection BSS")
if(relocateBssSlabs)
asmgen.out(" .dsection slabs_BSS")
asmgen.out(" .dsection BSS_SLABS")
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
} else {
@@ -290,12 +293,12 @@ internal class ProgramAndVarsGen(
asmgen.out(" .dsection BSS")
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
if(!relocateBssSlabs)
asmgen.out(" .dsection slabs_BSS")
asmgen.out(" .dsection BSS_SLABS")
asmgen.out("prog8_program_end\t; end of program label for progend()")
if(relocateBssSlabs) {
asmgen.out(" * = ${relocatedBssStart.toHex()}")
asmgen.out(" .dsection slabs_BSS")
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
asmgen.out(" .dsection BSS_SLABS")
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for BSS_SLABS section\"")
}
}
@@ -367,6 +370,88 @@ internal class ProgramAndVarsGen(
nonZpVariables2asm(variables)
}
private fun asmTypeString(dt: DataType): String {
return when {
dt.isBool || dt.isUnsignedByte -> ".byte"
dt.isSignedByte -> ".char"
dt.isUnsignedWord || dt.isPointer -> ".word"
dt.isSignedWord -> ".sint"
dt.isFloat -> ".byte"
else -> {
throw AssemblyError("weird dt")
}
}
}
private fun structInstances2asm() {
fun initValues(instance: StStructInstance): List<String> {
val structtype: StStruct = symboltable.lookup(instance.structName) as StStruct
return structtype.fields.zip(instance.initialValues).map { (field, value) ->
if(field.first.isFloat) {
"["+compTarget.getFloatAsmBytes(value.number!!)+"]"
} else {
when {
value.number!=null -> {
if(field.first.isPointer)
"$"+value.number!!.toInt().toString(16)
else if(field.first.isInteger)
value.number!!.toInt().toString()
else
value.number.toString()
}
value.addressOfSymbol!=null -> value.addressOfSymbol!!
value.boolean!=null -> if(value.boolean==true) "1" else "0"
else -> throw AssemblyError("weird struct initial value $value")
}
}
}
}
asmgen.out("; struct types")
symboltable.allStructInstances.distinctBy { it.structName }.forEach {
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
val structargs = structtype.fields.withIndex().joinToString(",") { "f${it.index}" }
asmgen.out("${it.structName} .struct $structargs\n")
structtype.fields.withIndex().forEach { (index, field) ->
val dt = field.first
val varname = "f${index}"
val type = when {
dt.isBool || dt.isUnsignedByte -> ".byte"
dt.isSignedByte -> ".char"
dt.isUnsignedWord || dt.isPointer -> ".word"
dt.isSignedWord -> ".sint"
dt.isFloat -> ".byte" // TODO check that float bytes are passed as an array parameter
else -> throw AssemblyError("weird dt")
}
asmgen.out("p8v_${field.second} $type \\$varname") // note: struct field symbol prefixing done here because that is a lot simpler than fixing up all expressions in the AST
}
asmgen.out(" .endstruct\n")
}
val (instancesNoInit, instances) = symboltable.allStructInstances.partition { it.initialValues.isEmpty() }
asmgen.out("; struct instances without initialization values, as BSS zeroed at startup\n")
asmgen.out(" .section BSS\n")
instancesNoInit.forEach {
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
val zerovalues = structtype.fields.map { field ->
if(field.first.isFloat) {
val floatbytes = List(compTarget.memorySize(BaseDataType.FLOAT)) { "?" }
"[${floatbytes.joinToString(",")}]"
}
else "?"
}
asmgen.out("${it.name} .dstruct ${it.structName}, ${zerovalues.joinToString(",")}\n")
}
asmgen.out(" .send BSS\n")
asmgen.out("; struct instances with initialization values\n")
asmgen.out(" .section STRUCTINSTANCES\n")
instances.forEach { asmgen.out("${it.name} .dstruct ${it.structName}, ${initValues(it).joinToString(",")}\n") }
asmgen.out(" .send STRUCTINSTANCES\n")
}
internal fun translateAsmSubroutine(sub: PtAsmSub) {
if(sub.inline) {
return // subroutine gets inlined at call site.
+1 -1
View File
@@ -258,7 +258,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
return allIdentifiersAndTargets.filter { decl===it.second }.map{ it.first } + assemblyBlocks
}
private val prefixes = listOf("p8b_", "p8v_", "p8s_", "p8c_", "p8l_", "p8_", "")
private val prefixes = listOf("p8b_", "p8v_", "p8s_", "p8c_", "p8l_", "p8t_", "p8_", "")
private fun nameInAssemblyCode(name: String, knownAsmPrefixes: List<String> = emptyList()): List<InlineAssembly> {
if(knownAsmPrefixes.isNotEmpty())
return allAssemblyNodes.filter {
+12 -10
View File
@@ -100,16 +100,18 @@ Symbol prefixing in generated Assembly code
*All* symbols in the prog8 program will be prefixed in the generated assembly code:
============ ========
Element type prefix
============ ========
Block ``p8b_``
Subroutine ``p8s_``
Variable ``p8v_``
Constant ``p8c_``
Label ``p8l_``
other ``p8_``
============ ========
================ ========
Element type prefix
================ ========
Block ``p8b_``
Subroutine ``p8s_``
Variable ``p8v_``
Constant ``p8c_``
Label ``p8l_``
Struct ``p8t_``
Struct Field ``p8v_``
other ``p8_``
================ ========
This is to avoid naming conflicts with CPU registers, assembly instructions, etc.
So if you're referencing symbols from the prog8 program in inlined assembly code, you have to take
+1 -5
View File
@@ -5,11 +5,7 @@ TODO
STRUCTS and TYPED POINTERS (6502 codegen specific)
--------------------------------------------------
- fix struct allocations/inits.
- prefixSymbols(): what to do with prefixing struct fields? Should they be prefixed with something or no?
But need to solve struct allocation/initialization first.
- fix struct and field name prefixing errors
- 6502 codegen should warn about writing to initialized struct instances when using romable code, like with arrays "can only be used as read-only in ROMable code"
- 6502 asm symbol name prefixing should work for dereferences and structs too.
- update structpointers.rst docs with 6502 specific things?
+22 -13
View File
@@ -1,17 +1,26 @@
%option enable_floats
%zeropage dontuse
main {
sub start() {
void derp("a")
}
sub derp(str arg) -> ubyte {
if arg==0
return 42
if arg==4444
return 42
if arg!=0
return 42
if arg!=4444
return 42
return 1
struct List {
uword s
bool b
float f
^^ubyte p
}
struct Node {
uword value
^^Node next
}
^^List l1 = List()
^^List l2 = List(1234,true,9.876,50000)
^^Node n1 = Node()
^^Node n2 = Node(1234, 50000)
cx16.r0 = l1
cx16.r1 = l2
cx16.r0 = n1
cx16.r1 = n2
;l.s[2] = 42
}
}
-1
View File
@@ -315,7 +315,6 @@ class StMemorySlab(name: String, val size: UInt, val align: UInt, astNode: PtNod
class StStructInstance(name: String, val structName: String, val initialValues: StArray, val size: UInt, astNode: PtNode?) :
StNode(name, StNodeType.STRUCTINSTANCE, astNode)
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returns: List<DataType>, astNode: PtNode) :
StNode(name, StNodeType.SUBROUTINE, astNode)
+2 -1
View File
@@ -133,7 +133,8 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
else -> throw AssemblyError("invalid structalloc argument type $it")
}
}
scope.first().add(StStructInstance(label, struct.scopedNameString, initialValues, struct.size, null))
val scopedName = if(struct.astNode!=null) (struct.astNode as PtNamedNode).scopedName else struct.scopedNameString
scope.first().add(StStructInstance(label, scopedName, initialValues, struct.size, null))
}
}
null