mirror of
https://github.com/irmen/prog8.git
synced 2026-04-21 02:16:41 +00:00
6502 struct allocation to asm file, struct name and field prefixing (maybe unneeded...)
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user