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 | |
60 | enum { |
61 | STRUCT, |
62 | UNION |
63 | }; |
64 | |
65 | |
66 | |
67 | /*****************************************************************************/ |
68 | /* Code */ |
69 | /*****************************************************************************/ |
70 | |
71 | |
72 | |
73 | static 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 | |
99 | static 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 | |
275 | long 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 | |
289 | void DoStruct (void) |
290 | /* Handle the .STRUCT command */ |
291 | { |
292 | DoStructInternal (0, STRUCT); |
293 | } |
294 | |
295 | |
296 | |
297 | void DoUnion (void) |
298 | /* Handle the .UNION command */ |
299 | { |
300 | DoStructInternal (0, UNION); |
301 | } |
302 | |