1/*****************************************************************************/
2/* */
3/* struct.c */
4/* */
5/* .STRUCT/.UNION commands */
6/* */
7/* */
8/* */
9/* (C) 2003-2011, Ullrich von Bassewitz */
10/* Roemerstrasse 52 */
11/* D-70794 Filderstadt */
12/* EMail: uz@cc65.org */
13/* */
14/* */
15/* This software is provided 'as-is', without any expressed or implied */
16/* warranty. In no event will the authors be held liable for any damages */
17/* arising from the use of this software. */
18/* */
19/* Permission is granted to anyone to use this software for any purpose, */
20/* including commercial applications, and to alter it and redistribute it */
21/* freely, subject to the following restrictions: */
22/* */
23/* 1. The origin of this software must not be misrepresented; you must not */
24/* claim that you wrote the original software. If you use this software */
25/* in a product, an acknowledgment in the product documentation would be */
26/* appreciated but is not required. */
27/* 2. Altered source versions must be plainly marked as such, and must not */
28/* be misrepresented as being the original software. */
29/* 3. This notice may not be removed or altered from any source */
30/* distribution. */
31/* */
32/*****************************************************************************/
33
34
35
36/* common */
37#include "addrsize.h"
38#include "scopedefs.h"
39
40/* ca65 */
41#include "condasm.h"
42#include "error.h"
43#include "expr.h"
44#include "macro.h"
45#include "nexttok.h"
46#include "scanner.h"
47#include "sizeof.h"
48#include "symbol.h"
49#include "symtab.h"
50#include "struct.h"
51
52
53
54/*****************************************************************************/
55/* Data */
56/*****************************************************************************/
57
58
59
60enum {
61 STRUCT,
62 UNION
63};
64
65
66
67/*****************************************************************************/
68/* Code */
69/*****************************************************************************/
70
71
72
73static long Member (long AllocSize)
74/* Read one struct member and return its size */
75{
76 long Multiplicator;
77
78 /* A multiplicator may follow */
79 if (CurTok.Tok != TOK_SEP) {
80 Multiplicator = ConstExpression ();
81 if (Multiplicator <= 0) {
82 ErrorSkip ("Range error");
83 Multiplicator = 1;
84 }
85 AllocSize *= Multiplicator;
86 }
87
88 /* Check the size for a reasonable value */
89 if (AllocSize >= 0x10000) {
90 ErrorSkip ("Range error");
91 }
92
93 /* Return the size */
94 return AllocSize;
95}
96
97
98
99static long DoStructInternal (long Offs, unsigned Type)
100/* Handle the .STRUCT command */
101{
102 long Size = 0;
103
104 /* Outside of other structs, we need a name. Inside another struct or
105 ** union, the struct may be anonymous, in which case no new lexical level
106 ** is started.
107 */
108 int Anon = (CurTok.Tok != TOK_IDENT);
109 if (!Anon) {
110 /* Enter a new scope, then skip the name */
111 SymEnterLevel (&CurTok.SVal, SCOPE_STRUCT, ADDR_SIZE_ABS, 0);
112 NextTok ();
113 /* Start at zero offset in the new scope */
114 Offs = 0;
115 }
116
117 /* Test for end of line */
118 ConsumeSep ();
119
120 /* Read until end of struct */
121 while (CurTok.Tok != TOK_ENDSTRUCT &&
122 CurTok.Tok != TOK_ENDUNION &&
123 CurTok.Tok != TOK_EOF) {
124
125 long MemberSize;
126 SymTable* Struct;
127 SymEntry* Sym;
128
129 /* Allow empty and comment lines */
130 if (CurTok.Tok == TOK_SEP) {
131 NextTok ();
132 continue;
133 }
134
135 /* The format is "[identifier] storage-allocator [, multiplicator]" */
136 Sym = 0;
137 if (CurTok.Tok == TOK_IDENT) {
138
139 /* Beware: An identifier may also be a macro, in which case we have
140 ** to start over.
141 */
142 Macro* M = FindMacro (&CurTok.SVal);
143 if (M) {
144 MacExpandStart (M);
145 continue;
146 }
147
148 /* We have an identifier, generate a symbol */
149 Sym = SymFind (CurrentScope, &CurTok.SVal, SYM_ALLOC_NEW);
150
151 /* Assign the symbol the offset of the current member */
152 SymDef (Sym, GenLiteralExpr (Offs), ADDR_SIZE_DEFAULT, SF_NONE);
153
154 /* Skip the member name */
155 NextTok ();
156 }
157
158 /* Read storage allocators */
159 MemberSize = 0; /* In case of errors, use zero */
160 switch (CurTok.Tok) {
161
162 case TOK_BYTE:
163 NextTok ();
164 MemberSize = Member (1);
165 break;
166
167 case TOK_DBYT:
168 case TOK_WORD:
169 case TOK_ADDR:
170 NextTok ();
171 MemberSize = Member (2);
172 break;
173
174 case TOK_FARADDR:
175 NextTok ();
176 MemberSize = Member (3);
177 break;
178
179 case TOK_DWORD:
180 NextTok ();
181 MemberSize = Member (4);
182 break;
183
184 case TOK_RES:
185 NextTok ();
186 if (CurTok.Tok == TOK_SEP) {
187 ErrorSkip ("Size is missing");
188 } else {
189 MemberSize = Member (1);
190 }
191 break;
192
193 case TOK_TAG:
194 NextTok ();
195 Struct = ParseScopedSymTable ();
196 if (Struct == 0) {
197 ErrorSkip ("Unknown struct/union");
198 } else if (GetSymTabType (Struct) != SCOPE_STRUCT) {
199 ErrorSkip ("Not a struct/union");
200 } else {
201 SymEntry* SizeSym = GetSizeOfScope (Struct);
202 if (!SymIsDef (SizeSym) || !SymIsConst (SizeSym, &MemberSize)) {
203 ErrorSkip ("Size of struct/union is unknown");
204 }
205 }
206 MemberSize = Member (MemberSize);
207 break;
208
209 case TOK_STRUCT:
210 NextTok ();
211 MemberSize = DoStructInternal (Offs, STRUCT);
212 break;
213
214 case TOK_UNION:
215 NextTok ();
216 MemberSize = DoStructInternal (Offs, UNION);
217 break;
218
219 default:
220 if (!CheckConditionals ()) {
221 /* Not a conditional directive */
222 ErrorSkip ("Invalid storage allocator in struct/union");
223 }
224 }
225
226 /* Assign the size to the member if it has a name */
227 if (Sym) {
228 DefSizeOfSymbol (Sym, MemberSize);
229 }
230
231 /* Next member */
232 if (Type == STRUCT) {
233 /* Struct */
234 Offs += MemberSize;
235 Size += MemberSize;
236 } else {
237 /* Union */
238 if (MemberSize > Size) {
239 Size = MemberSize;
240 }
241 }
242
243 /* Expect end of line */
244 ConsumeSep ();
245 }
246
247 /* If this is not a anon struct, enter a special symbol named ".size"
248 ** into the symbol table of the struct that holds the size of the
249 ** struct. Since the symbol starts with a dot, it cannot be accessed
250 ** by user code.
251 ** Leave the struct scope level.
252 */
253 if (!Anon) {
254 /* Add a symbol */
255 SymEntry* SizeSym = GetSizeOfScope (CurrentScope);
256 SymDef (SizeSym, GenLiteralExpr (Size), ADDR_SIZE_DEFAULT, SF_NONE);
257
258 /* Close the struct scope */
259 SymLeaveLevel ();
260 }
261
262 /* End of struct/union definition */
263 if (Type == STRUCT) {
264 Consume (TOK_ENDSTRUCT, "'.ENDSTRUCT' expected");
265 } else {
266 Consume (TOK_ENDUNION, "'.ENDUNION' expected");
267 }
268
269 /* Return the size of the struct */
270 return Size;
271}
272
273
274
275long GetStructSize (SymTable* Struct)
276/* Get the size of a struct or union */
277{
278 SymEntry* SizeSym = FindSizeOfScope (Struct);
279 if (SizeSym == 0) {
280 Error ("Size of struct/union is unknown");
281 return 0;
282 } else {
283 return GetSymVal (SizeSym);
284 }
285}
286
287
288
289void DoStruct (void)
290/* Handle the .STRUCT command */
291{
292 DoStructInternal (0, STRUCT);
293}
294
295
296
297void DoUnion (void)
298/* Handle the .UNION command */
299{
300 DoStructInternal (0, UNION);
301}
302