1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* condasm.c */ |
4 | /* */ |
5 | /* Conditional assembly support for ca65 */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2000-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 | /* ca65 */ |
37 | #include "error.h" |
38 | #include "expr.h" |
39 | #include "instr.h" |
40 | #include "lineinfo.h" |
41 | #include "nexttok.h" |
42 | #include "symbol.h" |
43 | #include "symtab.h" |
44 | #include "condasm.h" |
45 | |
46 | |
47 | |
48 | /*****************************************************************************/ |
49 | /* Data */ |
50 | /*****************************************************************************/ |
51 | |
52 | |
53 | |
54 | /* Maximum count of nested .ifs */ |
55 | #define MAX_IFS 256 |
56 | |
57 | /* Set of bitmapped flags for the if descriptor */ |
58 | enum { |
59 | ifNone = 0x0000, /* No flag */ |
60 | ifCond = 0x0001, /* IF condition was true */ |
61 | ifParentCond= 0x0002, /* IF condition of parent */ |
62 | ifElse = 0x0004, /* We had a .ELSE branch */ |
63 | ifNeedTerm = 0x0008, /* Need .ENDIF termination */ |
64 | }; |
65 | |
66 | /* The overall .IF condition */ |
67 | int IfCond = 1; |
68 | |
69 | |
70 | |
71 | /*****************************************************************************/ |
72 | /* struct IfDesc */ |
73 | /*****************************************************************************/ |
74 | |
75 | |
76 | |
77 | /* One .IF descriptor */ |
78 | typedef struct IfDesc IfDesc; |
79 | struct IfDesc { |
80 | unsigned Flags; /* Bitmapped flags, see above */ |
81 | Collection LineInfos; /* File position of the .IF */ |
82 | const char* Name; /* Name of the directive */ |
83 | }; |
84 | |
85 | /* The .IF stack */ |
86 | static IfDesc IfStack [MAX_IFS]; |
87 | static unsigned IfCount = 0; |
88 | |
89 | |
90 | |
91 | static IfDesc* GetCurrentIf (void) |
92 | /* Return the current .IF descriptor */ |
93 | { |
94 | if (IfCount == 0) { |
95 | return 0; |
96 | } else { |
97 | return &IfStack[IfCount-1]; |
98 | } |
99 | } |
100 | |
101 | |
102 | |
103 | static int GetOverallIfCond (void) |
104 | /* Get the overall condition based on all conditions on the stack. */ |
105 | { |
106 | /* Since the last entry contains the overall condition of the parent, we |
107 | ** must check it in combination of the current condition. If there is no |
108 | ** last entry, the overall condition is true. |
109 | */ |
110 | return (IfCount == 0) || |
111 | ((IfStack[IfCount-1].Flags & (ifCond | ifParentCond)) == (ifCond | ifParentCond)); |
112 | } |
113 | |
114 | |
115 | |
116 | static void CalcOverallIfCond (void) |
117 | /* Calculate the overall condition, based on all conditions on the stack. */ |
118 | { |
119 | IfCond = GetOverallIfCond (); |
120 | } |
121 | |
122 | |
123 | |
124 | static void SetIfCond (IfDesc* ID, int C) |
125 | /* Set the .IF condition */ |
126 | { |
127 | if (C) { |
128 | ID->Flags |= ifCond; |
129 | } else { |
130 | ID->Flags &= ~ifCond; |
131 | } |
132 | } |
133 | |
134 | |
135 | |
136 | static void ElseClause (IfDesc* ID, const char* Directive) |
137 | /* Enter an .ELSE clause */ |
138 | { |
139 | /* Check if we have an open .IF - otherwise .ELSE is not allowed */ |
140 | if (ID == 0) { |
141 | Error ("Unexpected %s" , Directive); |
142 | return; |
143 | } |
144 | |
145 | /* Check for a duplicate else, then remember that we had one */ |
146 | if (ID->Flags & ifElse) { |
147 | /* We already had a .ELSE ! */ |
148 | Error ("Duplicate .ELSE" ); |
149 | } |
150 | ID->Flags |= ifElse; |
151 | |
152 | /* Condition is inverted now */ |
153 | ID->Flags ^= ifCond; |
154 | } |
155 | |
156 | |
157 | |
158 | static IfDesc* AllocIf (const char* Directive, int NeedTerm) |
159 | /* Alloc a new element from the .IF stack */ |
160 | { |
161 | IfDesc* ID; |
162 | |
163 | /* Check for stack overflow */ |
164 | if (IfCount >= MAX_IFS) { |
165 | Fatal ("Too many nested .IFs" ); |
166 | } |
167 | |
168 | /* Get the next element */ |
169 | ID = &IfStack[IfCount]; |
170 | |
171 | /* Initialize elements */ |
172 | ID->Flags = NeedTerm? ifNeedTerm : ifNone; |
173 | if (GetOverallIfCond ()) { |
174 | /* The parents .IF condition is true */ |
175 | ID->Flags |= ifParentCond; |
176 | } |
177 | ID->LineInfos = EmptyCollection; |
178 | GetFullLineInfo (&ID->LineInfos); |
179 | ID->Name = Directive; |
180 | |
181 | /* One more slot allocated */ |
182 | ++IfCount; |
183 | |
184 | /* Return the result */ |
185 | return ID; |
186 | } |
187 | |
188 | |
189 | |
190 | static void FreeIf (void) |
191 | /* Free all .IF descriptors until we reach one with the NeedTerm bit set */ |
192 | { |
193 | int Done; |
194 | do { |
195 | IfDesc* ID = GetCurrentIf(); |
196 | if (ID == 0) { |
197 | Error (" Unexpected .ENDIF" ); |
198 | Done = 1; |
199 | } else { |
200 | Done = (ID->Flags & ifNeedTerm) != 0; |
201 | ReleaseFullLineInfo (&ID->LineInfos); |
202 | DoneCollection (&ID->LineInfos); |
203 | --IfCount; |
204 | } |
205 | } while (!Done); |
206 | } |
207 | |
208 | |
209 | |
210 | /*****************************************************************************/ |
211 | /* Code */ |
212 | /*****************************************************************************/ |
213 | |
214 | |
215 | |
216 | void DoConditionals (void) |
217 | /* Catch all for conditional directives */ |
218 | { |
219 | IfDesc* D; |
220 | |
221 | do { |
222 | |
223 | switch (CurTok.Tok) { |
224 | |
225 | case TOK_ELSE: |
226 | D = GetCurrentIf (); |
227 | |
228 | /* Allow an .ELSE */ |
229 | ElseClause (D, ".ELSE" ); |
230 | |
231 | /* Remember the data for the .ELSE */ |
232 | if (D) { |
233 | ReleaseFullLineInfo (&D->LineInfos); |
234 | GetFullLineInfo (&D->LineInfos); |
235 | D->Name = ".ELSE" ; |
236 | } |
237 | |
238 | /* Calculate the new overall condition */ |
239 | CalcOverallIfCond (); |
240 | |
241 | /* Skip .ELSE */ |
242 | NextTok (); |
243 | ExpectSep (); |
244 | break; |
245 | |
246 | case TOK_ELSEIF: |
247 | D = GetCurrentIf (); |
248 | /* Handle as if there was an .ELSE first */ |
249 | ElseClause (D, ".ELSEIF" ); |
250 | |
251 | /* Calculate the new overall if condition */ |
252 | CalcOverallIfCond (); |
253 | |
254 | /* Allocate and prepare a new descriptor */ |
255 | D = AllocIf (".ELSEIF" , 0); |
256 | NextTok (); |
257 | |
258 | /* Ignore the new condition if we are inside a false .ELSE |
259 | ** branch. This way we won't get any errors about undefined |
260 | ** symbols or similar... |
261 | */ |
262 | if (IfCond) { |
263 | SetIfCond (D, ConstExpression ()); |
264 | ExpectSep (); |
265 | } |
266 | |
267 | /* Get the new overall condition */ |
268 | CalcOverallIfCond (); |
269 | break; |
270 | |
271 | case TOK_ENDIF: |
272 | /* We're done with this .IF.. - remove the descriptor(s) */ |
273 | FreeIf (); |
274 | |
275 | /* Be sure not to read the next token until the .IF stack |
276 | ** has been cleanup up, since we may be at end of file. |
277 | */ |
278 | NextTok (); |
279 | ExpectSep (); |
280 | |
281 | /* Get the new overall condition */ |
282 | CalcOverallIfCond (); |
283 | break; |
284 | |
285 | case TOK_IF: |
286 | D = AllocIf (".IF" , 1); |
287 | NextTok (); |
288 | if (IfCond) { |
289 | SetIfCond (D, ConstExpression ()); |
290 | ExpectSep (); |
291 | } |
292 | CalcOverallIfCond (); |
293 | break; |
294 | |
295 | case TOK_IFBLANK: |
296 | D = AllocIf (".IFBLANK" , 1); |
297 | NextTok (); |
298 | if (IfCond) { |
299 | if (TokIsSep (CurTok.Tok)) { |
300 | SetIfCond (D, 1); |
301 | } else { |
302 | SetIfCond (D, 0); |
303 | SkipUntilSep (); |
304 | } |
305 | } |
306 | CalcOverallIfCond (); |
307 | break; |
308 | |
309 | case TOK_IFCONST: |
310 | D = AllocIf (".IFCONST" , 1); |
311 | NextTok (); |
312 | if (IfCond) { |
313 | ExprNode* Expr = Expression(); |
314 | SetIfCond (D, IsConstExpr (Expr, 0)); |
315 | FreeExpr (Expr); |
316 | ExpectSep (); |
317 | } |
318 | CalcOverallIfCond (); |
319 | break; |
320 | |
321 | case TOK_IFDEF: |
322 | D = AllocIf (".IFDEF" , 1); |
323 | NextTok (); |
324 | if (IfCond) { |
325 | SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING); |
326 | SetIfCond (D, Sym != 0 && SymIsDef (Sym)); |
327 | } |
328 | CalcOverallIfCond (); |
329 | break; |
330 | |
331 | case TOK_IFNBLANK: |
332 | D = AllocIf (".IFNBLANK" , 1); |
333 | NextTok (); |
334 | if (IfCond) { |
335 | if (TokIsSep (CurTok.Tok)) { |
336 | SetIfCond (D, 0); |
337 | } else { |
338 | SetIfCond (D, 1); |
339 | SkipUntilSep (); |
340 | } |
341 | } |
342 | CalcOverallIfCond (); |
343 | break; |
344 | |
345 | case TOK_IFNCONST: |
346 | D = AllocIf (".IFNCONST" , 1); |
347 | NextTok (); |
348 | if (IfCond) { |
349 | ExprNode* Expr = Expression(); |
350 | SetIfCond (D, !IsConstExpr (Expr, 0)); |
351 | FreeExpr (Expr); |
352 | ExpectSep (); |
353 | } |
354 | CalcOverallIfCond (); |
355 | break; |
356 | |
357 | case TOK_IFNDEF: |
358 | D = AllocIf (".IFNDEF" , 1); |
359 | NextTok (); |
360 | if (IfCond) { |
361 | SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING); |
362 | SetIfCond (D, Sym == 0 || !SymIsDef (Sym)); |
363 | ExpectSep (); |
364 | } |
365 | CalcOverallIfCond (); |
366 | break; |
367 | |
368 | case TOK_IFNREF: |
369 | D = AllocIf (".IFNREF" , 1); |
370 | NextTok (); |
371 | if (IfCond) { |
372 | SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING); |
373 | SetIfCond (D, Sym == 0 || !SymIsRef (Sym)); |
374 | ExpectSep (); |
375 | } |
376 | CalcOverallIfCond (); |
377 | break; |
378 | |
379 | case TOK_IFP02: |
380 | D = AllocIf (".IFP02" , 1); |
381 | NextTok (); |
382 | if (IfCond) { |
383 | SetIfCond (D, GetCPU() == CPU_6502); |
384 | } |
385 | ExpectSep (); |
386 | CalcOverallIfCond (); |
387 | break; |
388 | |
389 | case TOK_IFP4510: |
390 | D = AllocIf (".IFP4510" , 1); |
391 | NextTok (); |
392 | if (IfCond) { |
393 | SetIfCond (D, GetCPU() == CPU_4510); |
394 | } |
395 | ExpectSep (); |
396 | CalcOverallIfCond (); |
397 | break; |
398 | |
399 | case TOK_IFP816: |
400 | D = AllocIf (".IFP816" , 1); |
401 | NextTok (); |
402 | if (IfCond) { |
403 | SetIfCond (D, GetCPU() == CPU_65816); |
404 | } |
405 | ExpectSep (); |
406 | CalcOverallIfCond (); |
407 | break; |
408 | |
409 | case TOK_IFPC02: |
410 | D = AllocIf (".IFPC02" , 1); |
411 | NextTok (); |
412 | if (IfCond) { |
413 | SetIfCond (D, GetCPU() == CPU_65C02); |
414 | } |
415 | ExpectSep (); |
416 | CalcOverallIfCond (); |
417 | break; |
418 | |
419 | case TOK_IFPSC02: |
420 | D = AllocIf (".IFPSC02" , 1); |
421 | NextTok (); |
422 | if (IfCond) { |
423 | SetIfCond (D, GetCPU() == CPU_65SC02); |
424 | } |
425 | ExpectSep (); |
426 | CalcOverallIfCond (); |
427 | break; |
428 | |
429 | case TOK_IFREF: |
430 | D = AllocIf (".IFREF" , 1); |
431 | NextTok (); |
432 | if (IfCond) { |
433 | SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING); |
434 | SetIfCond (D, Sym != 0 && SymIsRef (Sym)); |
435 | ExpectSep (); |
436 | } |
437 | CalcOverallIfCond (); |
438 | break; |
439 | |
440 | default: |
441 | /* Skip tokens */ |
442 | NextTok (); |
443 | |
444 | } |
445 | |
446 | } while (IfCond == 0 && CurTok.Tok != TOK_EOF); |
447 | } |
448 | |
449 | |
450 | |
451 | int CheckConditionals (void) |
452 | /* Check if the current token is one that starts a conditional directive, and |
453 | ** call DoConditionals if so. Return true if a conditional directive was found, |
454 | ** return false otherwise. |
455 | */ |
456 | { |
457 | switch (CurTok.Tok) { |
458 | case TOK_ELSE: |
459 | case TOK_ELSEIF: |
460 | case TOK_ENDIF: |
461 | case TOK_IF: |
462 | case TOK_IFBLANK: |
463 | case TOK_IFCONST: |
464 | case TOK_IFDEF: |
465 | case TOK_IFNBLANK: |
466 | case TOK_IFNCONST: |
467 | case TOK_IFNDEF: |
468 | case TOK_IFNREF: |
469 | case TOK_IFP02: |
470 | case TOK_IFP4510: |
471 | case TOK_IFP816: |
472 | case TOK_IFPC02: |
473 | case TOK_IFPSC02: |
474 | case TOK_IFREF: |
475 | DoConditionals (); |
476 | return 1; |
477 | |
478 | default: |
479 | return 0; |
480 | } |
481 | } |
482 | |
483 | |
484 | |
485 | void CheckOpenIfs (void) |
486 | /* Called from the scanner before closing an input file. Will check for any |
487 | ** open .ifs in this file. |
488 | */ |
489 | { |
490 | const LineInfo* LI; |
491 | |
492 | while (1) { |
493 | /* Get the current file number and check if the topmost entry on the |
494 | ** .IF stack was inserted with this file number |
495 | */ |
496 | IfDesc* D = GetCurrentIf (); |
497 | if (D == 0) { |
498 | /* There are no open .IFs */ |
499 | break; |
500 | } |
501 | |
502 | LI = CollConstAt (&D->LineInfos, 0); |
503 | if (GetSourcePos (LI)->Name != CurTok.Pos.Name) { |
504 | /* The .if is from another file, bail out */ |
505 | break; |
506 | } |
507 | |
508 | /* Start of .if is in the file we're about to leave */ |
509 | LIError (&D->LineInfos, "Conditional assembly branch was never closed" ); |
510 | FreeIf (); |
511 | } |
512 | |
513 | /* Calculate the new overall .IF condition */ |
514 | CalcOverallIfCond (); |
515 | } |
516 | |
517 | |
518 | |
519 | unsigned GetIfStack (void) |
520 | /* Get the current .IF stack pointer */ |
521 | { |
522 | return IfCount; |
523 | } |
524 | |
525 | |
526 | |
527 | void CleanupIfStack (unsigned SP) |
528 | /* Cleanup the .IF stack, remove anything above the given stack pointer */ |
529 | { |
530 | while (IfCount > SP) { |
531 | FreeIf (); |
532 | } |
533 | |
534 | /* Calculate the new overall .IF condition */ |
535 | CalcOverallIfCond (); |
536 | } |
537 | |