1
0
mirror of https://github.com/cc65/cc65.git synced 2025-01-11 11:30:13 +00:00

Added a .ORG keyword to ca65 structs/unions.

Allow 24-bit numbers as operands in ca65 structs/unions.
This commit is contained in:
Greg King 2019-09-11 17:55:49 -04:00
parent 3b128ba59f
commit 0896deedef
2 changed files with 111 additions and 71 deletions

View File

@ -1,4 +1,4 @@
<!doctype linuxdoc system> <!-- -*- text-mode -*- --> <!doctype linuxdoc system>
<article> <article>
<title>ca65 Users Guide <title>ca65 Users Guide
@ -1752,18 +1752,18 @@ either a string or an expression.
<sect1><tt>.SIZEOF</tt><label id=".SIZEOF"><p> <sect1><tt>.SIZEOF</tt><label id=".SIZEOF"><p>
<tt/.SIZEOF/ is a pseudo function that returns the size of its argument. The <tt/.SIZEOF()/ is a pseudo function that returns the size of its argument.
argument can be a struct/union, a struct member, a procedure, or a label. In The argument can be a struct/union, a struct member, a scope/procedure, or a
case of a procedure or label, its size is defined by the amount of data label. In the case of a procedure or label, its size is defined by the
placed in the segment where the label is relative to. If a line of code amount of data placed in the segment where the label is relative to. If a
switches segments (for example in a macro) data placed in other segments line of code switches segments (for example, in a macro), data placed in
does not count for the size. other segments does not count for the size.
Please note that a symbol or scope must exist, before it is used together with Please note that a symbol or scope must exist before it can be used together
<tt/.SIZEOF/ (this may get relaxed later, but will always be true for scopes). with <tt/.SIZEOF()/ (that may get relaxed later, but always will be true for
A scope has preference over a symbol with the same name, so if the last part scopes). A scope has preference over a symbol with the same name; so, if the
of a name represents both, a scope and a symbol, the scope is chosen over the last part of a name represents both a scope and a symbol, then the scope is
symbol. chosen over the symbol.
After the following code: After the following code:
@ -2496,7 +2496,7 @@ Here's a list of all control commands and a description, what they do:
<sect1><tt>.ENDPROC</tt><label id=".ENDPROC"><p> <sect1><tt>.ENDPROC</tt><label id=".ENDPROC"><p>
End of local lexical level (see <tt><ref id=".PROC" name=".PROC"></tt>). End of the local lexical level (see <tt><ref id=".PROC" name=".PROC"></tt>).
<sect1><tt>.ENDREP, .ENDREPEAT</tt><label id=".ENDREPEAT"><p> <sect1><tt>.ENDREP, .ENDREPEAT</tt><label id=".ENDREPEAT"><p>
@ -2506,7 +2506,7 @@ Here's a list of all control commands and a description, what they do:
<sect1><tt>.ENDSCOPE</tt><label id=".ENDSCOPE"><p> <sect1><tt>.ENDSCOPE</tt><label id=".ENDSCOPE"><p>
End of local lexical level (see <tt/<ref id=".SCOPE" name=".SCOPE">/). End of the local lexical level (see <tt/<ref id=".SCOPE" name=".SCOPE">/).
<sect1><tt>.ENDSTRUCT</tt><label id=".ENDSTRUCT"><p> <sect1><tt>.ENDSTRUCT</tt><label id=".ENDSTRUCT"><p>
@ -2530,8 +2530,8 @@ Here's a list of all control commands and a description, what they do:
otherwise the enumeration members are placed in the enclosing scope. otherwise the enumeration members are placed in the enclosing scope.
In the enumeration body, symbols are declared. The first symbol has a value In the enumeration body, symbols are declared. The first symbol has a value
of zero, and each following symbol will get the value of the preceding plus of zero, and each following symbol will get the value of the preceding, plus
one. This behaviour may be overridden by an explicit assignment. Two symbols one. That behaviour may be overridden by an explicit assignment. Two symbols
may have the same value. may have the same value.
Example: Example:
@ -2544,9 +2544,9 @@ Here's a list of all control commands and a description, what they do:
.endenum .endenum
</verb></tscreen> </verb></tscreen>
Above example will create a new scope named <tt/errorcodes/ with three The above example will create a new scope named <tt/errorcodes/ with three
symbols in it that get the values 0, 1 and 2 respectively. Another way symbols in it that get the values 0, 1, and 2 respectively. Another way
to write this would have been: to write that would have been:
<tscreen><verb> <tscreen><verb>
.scope errorcodes .scope errorcodes
@ -2575,12 +2575,12 @@ Here's a list of all control commands and a description, what they do:
.endenum .endenum
</verb></tscreen> </verb></tscreen>
In this example, the enumeration does not have a name, which means that the In that example, the enumeration does not have a name, which means that the
members will be visible in the enclosing scope and can be used in this scope members will be visible in the enclosing scope, and can be used in that scope
without explicit scoping. The first member (<tt/EUNKNOWN/) has the value -1. without explicit scoping. The first member (<tt/EUNKNOWN/) has the value -1.
The value for the following members is incremented by one, so <tt/EOK/ would The values for the following members are incremented by one; so, <tt/EOK/
be zero and so on. <tt/EWOULDBLOCK/ is an alias for <tt/EGAIN/, so it has an would be zero, and so on. <tt/EWOULDBLOCK/ is an alias for <tt/EAGAIN/; so,
override for the value using an already defined symbol. it has an override for the value, using an already defined symbol.
<sect1><tt>.ERROR</tt><label id=".ERROR"><p> <sect1><tt>.ERROR</tt><label id=".ERROR"><p>
@ -4672,22 +4672,22 @@ compiler, depending on the target system selected:
</itemize> </itemize>
<sect>Structs and unions<label id="structs"><p> <sect>Structs and unions<label id="structs"><p>
<sect1>Structs and unions Overview<p> <sect1>Structs and unions Overview<p>
Structs and unions are special forms of <ref id="scopes" name="scopes">. They 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 are, to some degree, comparable to their C counterparts. Both have a list of
members. Each member allocates storage and may optionally have a name, which, members. Each member allocates storage, and optionally may have a name whose
in case of a struct, is the offset from the beginning and, in case of a union, value, in the case of a struct, usually is the storage offset from the
is always zero. beginning, and in the case of a union, doesn't change, and usually is zero.
<sect1>Declaration<p> <sect1>Declaration<p>
Here is an example for a very simple struct with two members and a total size Here is an example for a very simple struct with two members and a total size
of 4 bytes: of 4 bytes:
<tscreen><verb> <tscreen><verb>
.struct Point .struct Point
xcoord .word xcoord .word
@ -4695,10 +4695,9 @@ of 4 bytes:
.endstruct .endstruct
</verb></tscreen> </verb></tscreen>
A union shares the total space between all its members, its size is the same A union shares the total space between all its members; its size is the same
as that of the largest member. The offset of all members relative to the union as that of the largest member. The offset of all members relative to the union
is zero. is zero.
<tscreen><verb> <tscreen><verb>
.union Entry .union Entry
index .word index .word
@ -4706,13 +4705,12 @@ is zero.
.endunion .endunion
</verb></tscreen> </verb></tscreen>
A struct or union must not necessarily have a name. If it is anonymous, no 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 local scope is opened; the identifiers used to name the members are placed
into the current scope instead. into the current scope instead.
A struct may contain unnamed members and definitions of local structs. The A struct may contain unnamed members and definitions of local structs/unions.
storage allocators may contain a multiplier, as in the example below: The storage allocators may contain a multiplier, as in the example below:
<tscreen><verb> <tscreen><verb>
.struct Circle .struct Circle
.struct Point .struct Point
@ -4721,13 +4719,51 @@ storage allocators may contain a multiplier, as in the example below:
Radius .word Radius .word
.endstruct .endstruct
</verb></tscreen> </verb></tscreen>
The size of the Circle struct is 6 (three words).
<sect1>The storage allocator keywords<p>
<descrip>
<tag/.BYTE, .RES/
Allocates multiples of 1 byte. <tt/.RES/ requires an operand.
<tag/.DBYTE, .WORD, .ADDR/
Allocates multiples of 2 bytes.
<tag/.FARADDR/
Allocates multiples of 3 bytes.
<tag/.DWORD/
Allocates multiples of 4 bytes.
</descrip>
<sect1>The <tt/.ORG/ keyword<p>
The <tt/.ORG/ keyword changes the offset value that is assigned to subsequent
member names. It's useful when using a struct to define the names of the
registers in an I/O chip. Example:
<tscreen><verb>
; 6551
.struct ACIA ; Asynchronous Communications Interface Adapter
.org $031C
DATA .byte
STATUS .byte
CMD .byte ; Command register
CTRL .byte ; Control register
.endstruct
lda ACIA::DATA ; Get an RS-232 character
</verb></tscreen>
<sect1>The <tt/.TAG/ keyword<p> <sect1>The <tt/.TAG/ keyword<p>
Using the <ref id=".TAG" name=".TAG"> keyword, it is possible to reserve space By using the <ref id=".TAG" name=".TAG"> keyword, it is possible to reserve
for an already defined struct or unions within another struct: space for an already defined struct or union within another struct:
<tscreen><verb> <tscreen><verb>
.struct Point .struct Point
xcoord .word xcoord .word
@ -4740,33 +4776,30 @@ for an already defined struct or unions within another struct:
.endstruct .endstruct
</verb></tscreen> </verb></tscreen>
Space for a struct or union may be allocated using the <ref id=".TAG" Actual space for a struct or union may be allocated by using the <ref id=".TAG"
name=".TAG"> directive. name=".TAG"> directive.
<tscreen><verb> <tscreen><verb>
C: .tag Circle C: .tag Circle
</verb></tscreen> </verb></tscreen>
Currently, members are just offsets from the start of the struct or union. To Currently, members are just offsets from the start of the struct or union. To
access a field of a struct, the member offset has to be added to the address access a field of a struct, the member offset must be added to the address of
of the struct itself: the struct variable itself:
<tscreen><verb> <tscreen><verb>
lda C+Circle::Radius ; Load circle radius into A lda C+Circle::Radius ; Load circle radius into A
</verb></tscreen> </verb></tscreen>
That may change in a future version of the assembler.
This may change in a future version of the assembler.
<sect1>Limitations<p> <sect1>Limitations<p>
Structs and unions are currently implemented as nested symbol tables (in fact, Structs and unions currently are implemented as nested symbol tables (in fact,
they were a by-product of the improved scoping rules). Currently, the they were a by-product of the improved scoping rules). Currently, the
assembler has no idea of types. This means that the <ref id=".TAG" assembler has no idea of types. That means that the <ref id=".TAG"
name=".TAG"> keyword will only allocate space. You won't be able to initialize name=".TAG"> keyword only will allocate space. You won't be able to initialize
variables declared with <ref id=".TAG" name=".TAG">, and adding an embedded variables declared with <ref id=".TAG" name=".TAG">; and, adding an embedded
structure to another structure with <ref id=".TAG" name=".TAG"> will not make structure to another structure with <ref id=".TAG" name=".TAG"> will not make
this structure accessible by using the '::' operator. that added structure accessible by using the '::' operator.

View File

@ -5,7 +5,6 @@
/* .STRUCT/.UNION commands */ /* .STRUCT/.UNION commands */
/* */ /* */
/* */ /* */
/* */
/* (C) 2003-2011, Ullrich von Bassewitz */ /* (C) 2003-2011, Ullrich von Bassewitz */
/* Roemerstrasse 52 */ /* Roemerstrasse 52 */
/* D-70794 Filderstadt */ /* D-70794 Filderstadt */
@ -73,20 +72,20 @@ enum {
static long Member (long AllocSize) static long Member (long AllocSize)
/* Read one struct member and return its size */ /* Read one struct member and return its size */
{ {
long Multiplicator; long Multiplier;
/* A multiplicator may follow */ /* A multiplier may follow */
if (CurTok.Tok != TOK_SEP) { if (CurTok.Tok != TOK_SEP) {
Multiplicator = ConstExpression (); Multiplier = ConstExpression ();
if (Multiplicator <= 0) { if (Multiplier <= 0) {
ErrorSkip ("Range error"); ErrorSkip ("Range error");
Multiplicator = 1; Multiplier = 1;
} }
AllocSize *= Multiplicator; AllocSize *= Multiplier;
} }
/* Check the size for a reasonable value */ /* Check the size for a reasonable value */
if (AllocSize >= 0x10000) { if (AllocSize >= 0x1000000) {
ErrorSkip ("Range error"); ErrorSkip ("Range error");
} }
@ -102,10 +101,11 @@ static long DoStructInternal (long Offs, unsigned Type)
long Size = 0; long Size = 0;
/* Outside of other structs, we need a name. Inside another struct or /* Outside of other structs, we need a name. Inside another struct or
** union, the struct may be anonymous, in which case no new lexical level ** union, the struct may be anonymous; in which case, no new lexical level
** is started. ** is started.
*/ */
int Anon = (CurTok.Tok != TOK_IDENT); int Anon = (CurTok.Tok != TOK_IDENT);
if (!Anon) { if (!Anon) {
/* Enter a new scope, then skip the name */ /* Enter a new scope, then skip the name */
SymEnterLevel (&CurTok.SVal, SCOPE_STRUCT, ADDR_SIZE_ABS, 0); SymEnterLevel (&CurTok.SVal, SCOPE_STRUCT, ADDR_SIZE_ABS, 0);
@ -121,7 +121,6 @@ static long DoStructInternal (long Offs, unsigned Type)
while (CurTok.Tok != TOK_ENDSTRUCT && while (CurTok.Tok != TOK_ENDSTRUCT &&
CurTok.Tok != TOK_ENDUNION && CurTok.Tok != TOK_ENDUNION &&
CurTok.Tok != TOK_EOF) { CurTok.Tok != TOK_EOF) {
long MemberSize; long MemberSize;
SymTable* Struct; SymTable* Struct;
SymEntry* Sym; SymEntry* Sym;
@ -132,14 +131,14 @@ static long DoStructInternal (long Offs, unsigned Type)
continue; continue;
} }
/* The format is "[identifier] storage-allocator [, multiplicator]" */ /* The format is "[identifier ].storage-allocator[ multiplier]" */
Sym = 0; Sym = 0;
if (CurTok.Tok == TOK_IDENT) { if (CurTok.Tok == TOK_IDENT) {
/* Beware: An identifier may be a macro also;
/* Beware: An identifier may also be a macro, in which case we have ** in which case, we must start over.
** to start over.
*/ */
Macro* M = FindMacro (&CurTok.SVal); Macro* M = FindMacro (&CurTok.SVal);
if (M) { if (M) {
MacExpandStart (M); MacExpandStart (M);
continue; continue;
@ -155,10 +154,9 @@ static long DoStructInternal (long Offs, unsigned Type)
NextTok (); NextTok ();
} }
/* Read storage allocators */ /* Read the storage allocator */
MemberSize = 0; /* In case of errors, use zero */ MemberSize = 0; /* In case of errors or .ORG, use zero */
switch (CurTok.Tok) { switch (CurTok.Tok) {
case TOK_BYTE: case TOK_BYTE:
NextTok (); NextTok ();
MemberSize = Member (1); MemberSize = Member (1);
@ -190,6 +188,15 @@ static long DoStructInternal (long Offs, unsigned Type)
} }
break; break;
case TOK_ORG:
NextTok ();
if (CurTok.Tok == TOK_SEP) {
ErrorSkip ("Address is missing");
} else {
Offs = Member (1);
}
break;
case TOK_TAG: case TOK_TAG:
NextTok (); NextTok ();
Struct = ParseScopedSymTable (); Struct = ParseScopedSymTable ();
@ -244,8 +251,8 @@ static long DoStructInternal (long Offs, unsigned Type)
ConsumeSep (); ConsumeSep ();
} }
/* If this is not a anon struct, enter a special symbol named ".size" /* If this is not an anon. struct, enter a special symbol named ".size"
** into the symbol table of the struct that holds the size of the ** into the symbol table, of the struct, that holds the size of the
** struct. Since the symbol starts with a dot, it cannot be accessed ** struct. Since the symbol starts with a dot, it cannot be accessed
** by user code. ** by user code.
** Leave the struct scope level. ** Leave the struct scope level.