mirror of
https://github.com/cc65/cc65.git
synced 2024-11-19 21:32:19 +00:00
Merge pull request #2311 from bbbradsmith/ca65-struct-doc-clarify
ca65 struct and union documentation improvement and feature testing.
This commit is contained in:
commit
d0903ba225
@ -4086,7 +4086,9 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".BYTE" name=".BYTE"
|
||||
|
||||
<sect1><tt>.TAG</tt><label id=".TAG"><p>
|
||||
|
||||
Allocate space for a struct or union.
|
||||
Allocate space for a struct or union. This is equivalent to
|
||||
<tt><ref id=".RES" name=".RES"></tt> with the
|
||||
<tt><ref id=".SIZEOF" name=".SIZEOF"></tt> of a struct.
|
||||
|
||||
Example:
|
||||
|
||||
@ -4100,6 +4102,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".BYTE" name=".BYTE"
|
||||
.tag Point ; Allocate 4 bytes
|
||||
</verb></tscreen>
|
||||
|
||||
See: <ref id="structs" name=""Structs and unions"">
|
||||
|
||||
<sect1><tt>.UNDEF, .UNDEFINE</tt><label id=".UNDEFINE"><p>
|
||||
|
||||
@ -4865,10 +4868,15 @@ compiler, depending on the target system selected:
|
||||
|
||||
Structs and unions are special forms of <ref id="scopes" name="scopes">. They
|
||||
are, to some degree, comparable to their C counterparts. Both have a list of
|
||||
members. Each member allocates storage, and optionally may have a name whose
|
||||
value, in the case of a struct, usually is the storage offset from the
|
||||
beginning, and in the case of a union, doesn't change, and usually is zero.
|
||||
members. Each member allocates storage, and optionally may have a name.
|
||||
|
||||
Each named member has a constant value equal to the storage offset from the
|
||||
beginning of the structure. In the case of a union, all members are placed at
|
||||
the same offset, typically 0.
|
||||
|
||||
Each named member also has a storage size which can be accessed with the
|
||||
<tt><ref id=".SIZEOF" name=".SIZEOF"></tt> operator. The struct or union itself
|
||||
also has a <tt/.SIZEOF/ indicating its total storage size.
|
||||
|
||||
<sect1>Declaration<p>
|
||||
|
||||
@ -4895,8 +4903,9 @@ A struct or union may not necessarily have a name. If it is anonymous, no
|
||||
local scope is opened; the identifiers used to name the members are placed
|
||||
into the current scope instead.
|
||||
|
||||
A struct may contain unnamed members and definitions of local structs/unions.
|
||||
The storage allocators may contain a multiplier, as in the example below:
|
||||
Storage allocators may contain a multiplier. A struct may also contain members
|
||||
and definitions of local structs/unions. Example:
|
||||
|
||||
<tscreen><verb>
|
||||
.struct Circle
|
||||
.struct Point
|
||||
@ -4905,7 +4914,8 @@ The storage allocators may contain a multiplier, as in the example below:
|
||||
Radius .word
|
||||
.endstruct
|
||||
</verb></tscreen>
|
||||
The size of the Circle struct is 6 (three words).
|
||||
|
||||
In this example the size of the Circle struct is 6 (three words).
|
||||
|
||||
|
||||
<sect1>The storage allocator keywords<p>
|
||||
@ -4915,7 +4925,7 @@ The size of the Circle struct is 6 (three words).
|
||||
<tag/.BYTE, .RES/
|
||||
Allocates multiples of 1 byte. <tt/.RES/ requires an operand.
|
||||
|
||||
<tag/.DBYTE, .WORD, .ADDR/
|
||||
<tag/.DBYT, .WORD, .ADDR/
|
||||
Allocates multiples of 2 bytes.
|
||||
|
||||
<tag/.FARADDR/
|
||||
@ -4924,6 +4934,15 @@ The size of the Circle struct is 6 (three words).
|
||||
<tag/.DWORD/
|
||||
Allocates multiples of 4 bytes.
|
||||
|
||||
<tag/.TAG/
|
||||
Allocates a previously defined struct.
|
||||
|
||||
<tag/.STRUCT, .UNION/
|
||||
Begins a nested .struct or .union definition, and allocates it.
|
||||
Note that its member offset values will begin at 0, unless this nested
|
||||
structure is anonymous, in which case they will instead become members of
|
||||
the enclosing scope.
|
||||
|
||||
</descrip>
|
||||
|
||||
|
||||
@ -4968,13 +4987,54 @@ name=".TAG"> directive.
|
||||
C: .tag Circle
|
||||
</verb></tscreen>
|
||||
|
||||
Currently, members are just offsets from the start of the struct or union. To
|
||||
Members are just offsets from the start of the struct or union. To
|
||||
access a field of a struct, the member offset must be added to the address of
|
||||
the struct variable itself:
|
||||
<tscreen><verb>
|
||||
lda C+Circle::Radius ; Load circle radius into A
|
||||
lda C + Circle::Radius ; Load circle radius
|
||||
lda C + Circle::Origin + Point::ycoord ; Load circle origin.ycoord
|
||||
</verb></tscreen>
|
||||
That may change in a future version of the assembler.
|
||||
|
||||
Nested structures or unions are treated differently depending on whether they
|
||||
are anonymous. If named, a new structure definition is created within the
|
||||
enclosing scope, with its offsets beginning at 0. If anonymous, the members of
|
||||
the new structure are added to the enclosing scope instead, with offsets
|
||||
continuing through that scope. Example:
|
||||
|
||||
<tscreen><verb>
|
||||
.struct Object
|
||||
id .byte ; Object::id = 0
|
||||
target .struct Point ; Object::target = 1
|
||||
xcoord .word ; Object::Point::xcoord = 0
|
||||
ycoord .word ; Object::Point::ycoord = 2
|
||||
.endstruct
|
||||
cost .struct ; Object::cost = 5
|
||||
price .word ; Object::price = 5
|
||||
tax .word ; Object::tax = 7
|
||||
.endstruct
|
||||
.struct
|
||||
radius .word ; Object::radius = 9
|
||||
.endstruct
|
||||
.endstruct
|
||||
|
||||
O: .tag Object
|
||||
lda O + Object::target + Object::Point::ycoord ; Named struct
|
||||
lda O + Object::tax ; Anonymous
|
||||
lda O + Object::radius ; Anonymous
|
||||
|
||||
; Be careful not to use a named nested structure without also adding the
|
||||
; offset to the nested structure itself.
|
||||
lda O + Object::Point::ycoord ; Incorrect!
|
||||
lda O + Object::target + Object::Point::ycoord ; Correct
|
||||
</verb></tscreen>
|
||||
|
||||
In this example, the first nested structure is named "Point", and its member
|
||||
offsets begin at 0. On the other hand, the two anonymous structures simply
|
||||
continue to add members to the enclosing "Object" structure.
|
||||
|
||||
Note that an anonymous structure does not need a member name, since all of its
|
||||
members become part of the enclosing structure. The "cost" member in the
|
||||
example is redundantly the same offset as its first member "price".
|
||||
|
||||
|
||||
<sect1>Limitations<p>
|
||||
|
222
test/asm/val/struct.s
Normal file
222
test/asm/val/struct.s
Normal file
@ -0,0 +1,222 @@
|
||||
; test .struct and .union features
|
||||
|
||||
.code
|
||||
|
||||
; exit with 0
|
||||
.export _main
|
||||
_main:
|
||||
lda #0
|
||||
tax
|
||||
rts
|
||||
|
||||
; test storage allocator sizes and offsets
|
||||
|
||||
.struct Storage
|
||||
mb1 .byte
|
||||
mb5 .byte 5
|
||||
mr1 .res 1
|
||||
mr5 .res 5
|
||||
mdb1 .dbyt
|
||||
mdb5 .dbyt 5
|
||||
mw1 .word
|
||||
mw5 .word 5
|
||||
ma1 .addr
|
||||
ma5 .addr 5
|
||||
mf1 .faraddr
|
||||
mf5 .faraddr 5
|
||||
mdw1 .dword
|
||||
mdw5 .dword 5
|
||||
.endstruct
|
||||
|
||||
.assert .sizeof(Storage::mb1) = 1, error, ".struct .byte member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::mb5) = 5, error, ".struct .byte 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::mr1) = 1, error, ".struct .res 1 member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::mr5) = 5, error, ".struct .res 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::mdb1) = 2, error, ".struct .dbyt member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::mdb5) = 10, error, ".struct .dbyt 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::mw1) = 2, error, ".struct .word member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::mw5) = 10, error, ".struct .word 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::ma1) = 2, error, ".struct .addr member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::ma5) = 10, error, ".struct .addr 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::mf1) = 3, error, ".struct .faraddr member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::mf5) = 15, error, ".struct .faraddr 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::mdw1) = 4, error, ".struct .dword member has unexpected .sizeof"
|
||||
.assert .sizeof(Storage::mdw5) = 20, error, ".struct .dword 5 member has unexpected .sizeof"
|
||||
|
||||
.assert Storage::mb1 = 0, error, ".struct storage offset is incorrect"
|
||||
.assert Storage::mb5 = Storage::mb1 + .sizeof(Storage::mb1), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::mr1 = Storage::mb5 + .sizeof(Storage::mb5), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::mr5 = Storage::mr1 + .sizeof(Storage::mr1), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::mdb1 = Storage::mr5 + .sizeof(Storage::mr5), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::mdb5 = Storage::mdb1 + .sizeof(Storage::mdb1), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::mw1 = Storage::mdb5 + .sizeof(Storage::mdb5), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::mw5 = Storage::mw1 + .sizeof(Storage::mw1), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::ma1 = Storage::mw5 + .sizeof(Storage::mw5), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::ma5 = Storage::ma1 + .sizeof(Storage::ma1), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::mf1 = Storage::ma5 + .sizeof(Storage::ma5), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::mf5 = Storage::mf1 + .sizeof(Storage::mf1), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::mdw1 = Storage::mf5 + .sizeof(Storage::mf5), error, ".struct storage offset is incorrect"
|
||||
.assert Storage::mdw5 = Storage::mdw1 + .sizeof(Storage::mdw1), error, ".struct storage offset is incorrect"
|
||||
.assert .sizeof(Storage) = Storage::mdw5 + .sizeof(Storage::mdw5), error, ".struct has unexpected .sizeof"
|
||||
|
||||
; test union offset and size
|
||||
|
||||
.union UStorage
|
||||
mb1 .byte
|
||||
mb5 .byte 5
|
||||
mr1 .res 1
|
||||
mr5 .res 5
|
||||
mdb1 .dbyt
|
||||
mdb5 .dbyt 5
|
||||
mw1 .word
|
||||
mw5 .word 5
|
||||
ma1 .addr
|
||||
ma5 .addr 5
|
||||
mf1 .faraddr
|
||||
mf5 .faraddr 5
|
||||
mdw1 .dword
|
||||
mdw5 .dword 5
|
||||
.endunion
|
||||
|
||||
.assert .sizeof(UStorage::mb1) = 1, error, ".union .byte member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::mb5) = 5, error, ".union .byte 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::mr1) = 1, error, ".union .res 1 member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::mr5) = 5, error, ".union .res 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::mdb1) = 2, error, ".union .dbyt member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::mdb5) = 10, error, ".union .dbyt 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::mw1) = 2, error, ".union .word member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::mw5) = 10, error, ".union .word 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::ma1) = 2, error, ".union .addr member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::ma5) = 10, error, ".union .addr 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::mf1) = 3, error, ".union .faraddr member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::mf5) = 15, error, ".union .faraddr 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::mdw1) = 4, error, ".union .dword member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage::mdw5) = 20, error, ".union .dword 5 member has unexpected .sizeof"
|
||||
.assert .sizeof(UStorage) = 20, error, ".union has unexpected .sizeof"
|
||||
|
||||
.assert UStorage::mb1 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::mb5 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::mr1 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::mr5 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::mdb1 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::mdb5 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::mw1 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::mw5 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::ma1 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::ma5 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::mf1 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::mf5 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::mdw1 = 0, error, ".union storage offset is incorrect"
|
||||
.assert UStorage::mdw5 = 0, error, ".union storage offset is incorrect"
|
||||
|
||||
; test tag
|
||||
|
||||
storage: .tag Storage
|
||||
.assert (*-storage)=.sizeof(Storage), error, ".tag reserved size incorrect"
|
||||
|
||||
; test nested structures
|
||||
|
||||
.struct Point
|
||||
xc .word
|
||||
yc .word
|
||||
.endstruct
|
||||
|
||||
.struct Nested
|
||||
pad .res 13
|
||||
tag .tag Point
|
||||
ch .struct Child
|
||||
ca .word ; offset = 0
|
||||
gch .struct Grandchild
|
||||
gca .word ; offset = 0
|
||||
gcb .byte
|
||||
.endstruct
|
||||
cb .byte
|
||||
.endstruct
|
||||
anon .struct
|
||||
aa .dword ; offset = Nested::anon (anonymous .struct)
|
||||
ab .dword
|
||||
.endstruct
|
||||
chu .union Chunion
|
||||
ua .byte ; offset = 0
|
||||
ub .dword
|
||||
.endunion
|
||||
chanon .union
|
||||
uc .byte ; offset = Nested::chanon
|
||||
ud .dword
|
||||
.endunion
|
||||
last .byte
|
||||
.endstruct
|
||||
|
||||
.assert Nested::pad = 0, error, "Nested .struct has unexpected starting offset"
|
||||
.assert Nested::Child::ca = 0, error, "Nested .struct has unexpected starting offset"
|
||||
.assert Nested::Child::Grandchild::gca = 0, error, "Nested .struct has unexpected starting offset"
|
||||
|
||||
.assert .sizeof(Nested::tag) = .sizeof(Point), error, ".tag in .struct has unexpected .sizeof"
|
||||
.assert .sizeof(Nested::Child::Grandchild) = 2 + 1, error, "Nested .struct has unexpected .sizeof"
|
||||
.assert .sizeof(Nested::Child) = 2 + 1 + .sizeof(Nested::Child::Grandchild), error, "Nested .struct has unpexpected .sizeof"
|
||||
.assert .sizeof(Nested::ch) = .sizeof(Nested::Child), error, "Nested .struct has unexpected member .sizeof"
|
||||
.assert .sizeof(Nested::Child::gch) = .sizeof(Nested::Child::Grandchild), error, "Nested .struct has unexpected member .sizeof"
|
||||
.assert .sizeof(Nested::anon) = 8, error, "Nested anonymous member .struct has unexpected .sizeof"
|
||||
.assert .sizeof(Nested::aa) = 4, error, "Nested anonymous .struct member has unexpected .sizeof"
|
||||
.assert .sizeof(Nested::ab) = 4, error, "Nested anonymous .struct member has unexpected .sizeof"
|
||||
.assert .sizeof(Nested::Chunion) = 4, error, "Nested .union has unexpected .sizeof"
|
||||
.assert .sizeof(Nested::chu) = .sizeof(Nested::Chunion), error, "Nested member .union has unexpected .sizeof"
|
||||
.assert .sizeof(Nested::chanon) = 4, error, "Nested anonymous member .union as unexpected .sizeof"
|
||||
|
||||
.assert Nested::tag = Nested::pad + .sizeof(Nested::pad), error, ".tag within .struct has unexpected offset"
|
||||
.assert Nested::ch = Nested::tag + .sizeof(Nested::tag), error, "Nested .struct has unexpected offset"
|
||||
.assert Nested::anon = Nested::ch + .sizeof(Nested::ch), error, "Nested anonymous member .struct has unexpected offset"
|
||||
.assert Nested::aa = Nested::anon, error, "Nested anonymous .struct member has unexpected offset"
|
||||
.assert Nested::ab = Nested::aa + .sizeof(Nested::aa), error, "Nested anonymous .struct member has unexpected offset"
|
||||
.assert Nested::chu = Nested::ab + .sizeof(Nested::ab), error, "Nested member .union has unexpected offset"
|
||||
.assert Nested::chanon = Nested::chu + .sizeof(Nested::Chunion), error, "Nested anonymous member .union has unexpected offset"
|
||||
.assert Nested::uc = Nested::chanon, error, "Nested anonymous .union member has unexpected offset"
|
||||
.assert Nested::ud = Nested::chanon, error, "Nested anonymous .union member has unexpected offset"
|
||||
.assert Nested::last = Nested::ud + .sizeof(Nested::ud), error, ".struct member has unexpected offset after anonymous nested .struct"
|
||||
|
||||
; test .org
|
||||
|
||||
start:
|
||||
|
||||
.struct OrgStruct
|
||||
ma .byte
|
||||
mb .byte
|
||||
.org $1234
|
||||
mc .byte
|
||||
md .byte
|
||||
.struct Nested
|
||||
me .byte
|
||||
.org $5678
|
||||
mf .byte
|
||||
mg .byte
|
||||
.endstruct
|
||||
mh .byte
|
||||
.endstruct
|
||||
|
||||
.assert start <> (OrgStruct::mh+1), error, "Fatal test error: accidental code position conflict, move OrgStruct .org to another arbitrary address."
|
||||
.assert * = start, error, ".org within .struct does not return to previous location at .endstruct"
|
||||
.assert OrgStruct::ma = 0, error, ".struct with .org has unexpected offset"
|
||||
.assert OrgStruct::mb = 1, error, ".struct with .org has unexpected offset"
|
||||
.assert OrgStruct::mc = $1234, error, ".struct with .org has unexpected offset"
|
||||
.assert OrgStruct::md = $1235, error, ".struct with .org has unexpected offset"
|
||||
.assert OrgStruct::Nested::me = 0, error, "Nested .struct with .org has unexpected offset"
|
||||
.assert OrgStruct::Nested::mf = $5678, error, "Nested .struct with .org has unexpected offset"
|
||||
.assert OrgStruct::Nested::mg = $5679, error, "Nested .struct with .org has unexpected offset"
|
||||
.assert OrgStruct::mh = $1239, error, ".struct with .org has unexpected offset"
|
||||
.assert .sizeof(OrgStruct) = 8, error, ".struct with .org has unexpected .sizeof"
|
||||
|
||||
.union OrgUnion
|
||||
ma .byte
|
||||
mb .word
|
||||
.org $1234
|
||||
mc .byte
|
||||
md .word
|
||||
.endunion
|
||||
|
||||
.assert start <> OrgUnion::md, error, "Fatal test error: accidental code position conflict, move OrgUnion .org to another arbitrary address."
|
||||
.assert * = start, error, ".org within .union does not return to previous location at .endunion"
|
||||
.assert OrgUnion::ma = 0, error, ".union with .org has unexpected offset"
|
||||
.assert OrgUnion::mb = 0, error, ".union with .org has unexpected offset"
|
||||
.assert OrgUnion::mc = $1234, error, ".union with .org has unexpected offset"
|
||||
.assert OrgUnion::md = $1234, error, ".union with .org has unexpected offset"
|
||||
.assert .sizeof(OrgUnion) = 2, error, ".union with .org has unexpected .sizeof"
|
Loading…
Reference in New Issue
Block a user