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:
parent
3b128ba59f
commit
0896deedef
137
doc/ca65.sgml
137
doc/ca65.sgml
@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user