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