diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index 0f863f1e6..25afbd411 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -412,10 +412,9 @@ internal class ProgramAndVarsGen( asmgen.out("; struct types") - symboltable.allStructInstances.distinctBy { it.structName }.forEach { - val structtype: StStruct = symboltable.lookup(it.structName) as StStruct + symboltable.allStructTypes().distinctBy { it.name }.forEach { structtype -> val structargs = structtype.fields.withIndex().joinToString(",") { field -> "f${field.index}" } - asmgen.out("${it.structName} .struct $structargs\n") + asmgen.out("${structtype.scopedNameString} .struct $structargs\n") structtype.fields.withIndex().forEach { (index, field) -> val dt = field.first val varname = "f${index}" @@ -425,7 +424,7 @@ internal class ProgramAndVarsGen( asmgen.out(" .endstruct\n") } - val (instancesNoInit, instances) = symboltable.allStructInstances.partition { it.initialValues.isEmpty() } + 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") asmgen.out("${StStructInstanceBlockName}_bss .block\n") diff --git a/compiler/test/ast/TestConst.kt b/compiler/test/ast/TestConst.kt index 1f4eac86a..3fc4d00e7 100644 --- a/compiler/test/ast/TestConst.kt +++ b/compiler/test/ast/TestConst.kt @@ -514,5 +514,37 @@ main { compileText(Cx16Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null } + + test("const struct sizes and offsets, also available in assembly code") { + val src = """ +main { + + struct Node { + ubyte type + uword value + bool flag + } + + sub start() { + const ubyte size = sizeof(Node) + const ubyte offset1 = offsetof(Node.type) + const ubyte offset2 = offsetof(Node.value) + const ubyte offset3 = offsetof(Node.flag) + + %asm {{ + lda p8c_size + lda p8c_offset1 + lda p8c_offset2 + lda p8c_offset3 + lda #size(p8t_Node) + lda #p8t_Node.p8v_type + lda #p8t_Node.p8v_value + lda #p8t_Node.p8v_flag + }} + } +}""" + compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null + } + }) diff --git a/docs/source/structpointers.rst b/docs/source/structpointers.rst index 07aedd9fc..7b0b9e4e0 100644 --- a/docs/source/structpointers.rst +++ b/docs/source/structpointers.rst @@ -261,3 +261,40 @@ Address-Of: untyped vs typed ``&`` still returns an untyped (uword) pointer, as it did in older Prog8 versions. This is for backward compatibility reasons so existing programs don't break. The new *double ampersand* operator ``&&`` returns a *typed* pointer to the value. The semantics are slightly different from the old untyped address-of operator, because adding or subtracting a number from a typed pointer uses *pointer arithmetic* that takes the size of the value that it points to into account. + + +Accessing struct definitions in Assembly code +--------------------------------------------- + +Prog8 lets you query the size of a struct type, and the offsets of a field. +You can do the same in assembly code if needed: the struct definition gets written as `.struct` into the assembly file:: + + ; prog8 struct declaration: + struct Node { + ubyte type + uword value + bool flag + } + + ; generates this assembly code: + ; (the symbol prefixes are explained in the 'Technical details' chapter) + p8b_main.p8t_Node .struct f0,f1,f2 + p8v_type .byte \f0 + p8v_value .word \f1 + p8v_flag .byte \f2 + .endstruct + + +64tass then lets you query that information:: + + ; prog8 code: + ubyte size = sizeof(Node) + ubyte offset1 = offsetof(Node.type) + ubyte offset2 = offsetof(Node.value) + ubyte offset3 = offsetof(Node.flag) + + ; assembly equivalents (64tass syntax): + lda #size(p8t_Node) + lda #p8t_Node.p8v_type + lda #p8t_Node.p8v_value + lda #p8t_Node.p8v_flag diff --git a/examples/test.p8 b/examples/test.p8 index f1d4c3740..6662029ab 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,28 +4,28 @@ main { + struct Node { + ubyte type + uword value + bool flag + } + sub start() { - uword @shared string = 20000 - ubyte[200] barray - uword[100] @nosplit warray - ubyte @shared length - uword @shared lengthw + const ubyte size = sizeof(Node) + const ubyte offset1 = offsetof(Node.type) + const ubyte offset2 = offsetof(Node.value) + const ubyte offset3 = offsetof(Node.flag) - cx16.r0L = string[length] - cx16.r1L = string[lengthw] - cx16.r0bL = string[length]!=0 - cx16.r1bL = string[lengthw]!=0 - while string[length]!=0 - break - while string[lengthw]!=0 - break - - - string[length] = 42 - string[lengthw] = 42 - - ;cx16.r1L = barray[length] - ;cx16.r2 = warray[length] + %asm {{ + lda p8c_size + lda p8c_offset1 + lda p8c_offset2 + lda p8c_offset3 + lda #size(p8t_Node) + lda #p8t_Node.p8v_type + lda #p8t_Node.p8v_value + lda #p8t_Node.p8v_flag + }} } } diff --git a/simpleAst/src/prog8/code/SymbolTable.kt b/simpleAst/src/prog8/code/SymbolTable.kt index d494c55dd..e3e291151 100644 --- a/simpleAst/src/prog8/code/SymbolTable.kt +++ b/simpleAst/src/prog8/code/SymbolTable.kt @@ -81,18 +81,32 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL vars } - val allStructInstances: Collection by lazy { + fun allStructInstances(): Collection { val vars = mutableListOf() fun collect(node: StNode) { for(child in node.children) { - if(child.value.type== StNodeType.STRUCTINSTANCE) + if(child.value.type == StNodeType.STRUCTINSTANCE) vars.add(child.value as StStructInstance) else collect(child.value) } } collect(this) - vars + return vars + } + + fun allStructTypes(): Collection { + val vars = mutableListOf() + fun collect(node: StNode) { + for(child in node.children) { + if(child.value.type == StNodeType.STRUCT) + vars.add(child.value as StStruct) + else + collect(child.value) + } + } + collect(this) + return vars } override fun lookup(scopedName: String) = flat[scopedName]