mirror of
https://github.com/irmen/prog8.git
synced 2025-12-20 12:19:39 +00:00
always put all struct types as .struct in asm code to make them all accessible for size and offsets
This commit is contained in:
@@ -412,10 +412,9 @@ internal class ProgramAndVarsGen(
|
|||||||
|
|
||||||
|
|
||||||
asmgen.out("; struct types")
|
asmgen.out("; struct types")
|
||||||
symboltable.allStructInstances.distinctBy { it.structName }.forEach {
|
symboltable.allStructTypes().distinctBy { it.name }.forEach { structtype ->
|
||||||
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
|
|
||||||
val structargs = structtype.fields.withIndex().joinToString(",") { field -> "f${field.index}" }
|
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) ->
|
structtype.fields.withIndex().forEach { (index, field) ->
|
||||||
val dt = field.first
|
val dt = field.first
|
||||||
val varname = "f${index}"
|
val varname = "f${index}"
|
||||||
@@ -425,7 +424,7 @@ internal class ProgramAndVarsGen(
|
|||||||
asmgen.out(" .endstruct\n")
|
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("; struct instances without initialization values, as BSS zeroed at startup\n")
|
||||||
asmgen.out(" .section BSS\n")
|
asmgen.out(" .section BSS\n")
|
||||||
asmgen.out("${StStructInstanceBlockName}_bss .block\n")
|
asmgen.out("${StStructInstanceBlockName}_bss .block\n")
|
||||||
|
|||||||
@@ -514,5 +514,37 @@ main {
|
|||||||
compileText(Cx16Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
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
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
``&`` 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
|
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.
|
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
|
||||||
|
|||||||
@@ -4,28 +4,28 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
ubyte type
|
||||||
|
uword value
|
||||||
|
bool flag
|
||||||
|
}
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
uword @shared string = 20000
|
const ubyte size = sizeof(Node)
|
||||||
ubyte[200] barray
|
const ubyte offset1 = offsetof(Node.type)
|
||||||
uword[100] @nosplit warray
|
const ubyte offset2 = offsetof(Node.value)
|
||||||
ubyte @shared length
|
const ubyte offset3 = offsetof(Node.flag)
|
||||||
uword @shared lengthw
|
|
||||||
|
|
||||||
cx16.r0L = string[length]
|
%asm {{
|
||||||
cx16.r1L = string[lengthw]
|
lda p8c_size
|
||||||
cx16.r0bL = string[length]!=0
|
lda p8c_offset1
|
||||||
cx16.r1bL = string[lengthw]!=0
|
lda p8c_offset2
|
||||||
while string[length]!=0
|
lda p8c_offset3
|
||||||
break
|
lda #size(p8t_Node)
|
||||||
while string[lengthw]!=0
|
lda #p8t_Node.p8v_type
|
||||||
break
|
lda #p8t_Node.p8v_value
|
||||||
|
lda #p8t_Node.p8v_flag
|
||||||
|
}}
|
||||||
string[length] = 42
|
|
||||||
string[lengthw] = 42
|
|
||||||
|
|
||||||
;cx16.r1L = barray[length]
|
|
||||||
;cx16.r2 = warray[length]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
|||||||
vars
|
vars
|
||||||
}
|
}
|
||||||
|
|
||||||
val allStructInstances: Collection<StStructInstance> by lazy {
|
fun allStructInstances(): Collection<StStructInstance> {
|
||||||
val vars = mutableListOf<StStructInstance>()
|
val vars = mutableListOf<StStructInstance>()
|
||||||
fun collect(node: StNode) {
|
fun collect(node: StNode) {
|
||||||
for(child in node.children) {
|
for(child in node.children) {
|
||||||
@@ -92,7 +92,21 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
collect(this)
|
collect(this)
|
||||||
vars
|
return vars
|
||||||
|
}
|
||||||
|
|
||||||
|
fun allStructTypes(): Collection<StStruct> {
|
||||||
|
val vars = mutableListOf<StStruct>()
|
||||||
|
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]
|
override fun lookup(scopedName: String) = flat[scopedName]
|
||||||
|
|||||||
Reference in New Issue
Block a user