]> git.sur5r.net Git - cc65/blob - src/ca65/condasm.c
c15b81e98fe2ed8e754066a43d624c5dc4594567
[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     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 /* Caclulate 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, 0);
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             --IfCount;
202         }
203     } while (!Done);
204 }
205
206
207
208 /*****************************************************************************/
209 /*                                   Code                                    */
210 /*****************************************************************************/
211
212
213
214 void DoConditionals (void)
215 /* Catch all for conditional directives */
216 {
217     IfDesc* D;
218
219     do {
220
221         switch (CurTok.Tok) {
222
223             case TOK_ELSE:
224                 D = GetCurrentIf ();
225
226                 /* Allow an .ELSE */
227                 ElseClause (D, ".ELSE");
228
229                 /* Remember the data for the .ELSE */
230                 GetFullLineInfo (&D->LineInfos, 0);
231                 D->Name = ".ELSE";
232
233                 /* Calculate the new overall condition */
234                 CalcOverallIfCond ();
235
236                 /* Skip .ELSE */
237                 NextTok ();
238                 ExpectSep ();
239                 break;
240
241             case TOK_ELSEIF:
242                 D = GetCurrentIf ();
243                 /* Handle as if there was an .ELSE first */
244                 ElseClause (D, ".ELSEIF");
245
246                 /* Calculate the new overall if condition */
247                 CalcOverallIfCond ();
248
249                 /* Allocate and prepare a new descriptor */
250                 D = AllocIf (".ELSEIF", 0);
251                 NextTok ();
252
253                 /* Ignore the new condition if we are inside a false .ELSE
254                  * branch. This way we won't get any errors about undefined
255                  * symbols or similar...
256                  */
257                 if (IfCond) {
258                     SetIfCond (D, ConstExpression ());
259                     ExpectSep ();
260                 }
261
262                 /* Get the new overall condition */
263                 CalcOverallIfCond ();
264                 break;
265
266             case TOK_ENDIF:
267                 /* We're done with this .IF.. - remove the descriptor(s) */
268                 FreeIf ();
269
270                 /* Be sure not to read the next token until the .IF stack
271                  * has been cleanup up, since we may be at end of file.
272                  */
273                 NextTok ();
274                 ExpectSep ();
275
276                 /* Get the new overall condition */
277                 CalcOverallIfCond ();
278                 break;
279
280             case TOK_IF:
281                 D = AllocIf (".IF", 1);
282                 NextTok ();
283                 if (IfCond) {
284                     SetIfCond (D, ConstExpression ());
285                     ExpectSep ();
286                 }
287                 CalcOverallIfCond ();
288                 break;
289
290             case TOK_IFBLANK:
291                 D = AllocIf (".IFBLANK", 1);
292                 NextTok ();
293                 if (IfCond) {
294                     if (TokIsSep (CurTok.Tok)) {
295                         SetIfCond (D, 1);
296                     } else {
297                         SetIfCond (D, 0);
298                         SkipUntilSep ();
299                     }
300                 }
301                 CalcOverallIfCond ();
302                 break;
303
304             case TOK_IFCONST:
305                 D = AllocIf (".IFCONST", 1);
306                 NextTok ();
307                 if (IfCond) {
308                     ExprNode* Expr = Expression();
309                     SetIfCond (D, IsConstExpr (Expr, 0));
310                     FreeExpr (Expr);
311                     ExpectSep ();
312                 }
313                 CalcOverallIfCond ();
314                 break;
315
316             case TOK_IFDEF:
317                 D = AllocIf (".IFDEF", 1);
318                 NextTok ();
319                 if (IfCond) {
320                     SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
321                     SetIfCond (D, Sym != 0 && SymIsDef (Sym));
322                 }
323                 CalcOverallIfCond ();
324                 break;
325
326             case TOK_IFNBLANK:
327                 D = AllocIf (".IFNBLANK", 1);
328                 NextTok ();
329                 if (IfCond) {
330                     if (TokIsSep (CurTok.Tok)) {
331                         SetIfCond (D, 0);
332                     } else {
333                         SetIfCond (D, 1);
334                         SkipUntilSep ();
335                     }
336                 }
337                 CalcOverallIfCond ();
338                 break;
339
340             case TOK_IFNCONST:
341                 D = AllocIf (".IFNCONST", 1);
342                 NextTok ();
343                 if (IfCond) {
344                     ExprNode* Expr = Expression();
345                     SetIfCond (D, !IsConstExpr (Expr, 0));
346                     FreeExpr (Expr);
347                     ExpectSep ();
348                 }
349                 CalcOverallIfCond ();
350                 break;
351
352             case TOK_IFNDEF:
353                 D = AllocIf (".IFNDEF", 1);
354                 NextTok ();
355                 if (IfCond) {
356                     SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
357                     SetIfCond (D, Sym == 0 || !SymIsDef (Sym));
358                     ExpectSep ();
359                 }
360                 CalcOverallIfCond ();
361                 break;
362
363             case TOK_IFNREF:
364                 D = AllocIf (".IFNREF", 1);
365                 NextTok ();
366                 if (IfCond) {
367                     SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
368                     SetIfCond (D, Sym == 0 || !SymIsRef (Sym));
369                     ExpectSep ();
370                 }
371                 CalcOverallIfCond ();
372                 break;
373
374             case TOK_IFP02:
375                 D = AllocIf (".IFP02", 1);
376                 NextTok ();
377                 if (IfCond) {
378                     SetIfCond (D, GetCPU() == CPU_6502);
379                 }
380                 ExpectSep ();
381                 CalcOverallIfCond ();
382                 break;
383
384             case TOK_IFP816:
385                 D = AllocIf (".IFP816", 1);
386                 NextTok ();
387                 if (IfCond) {
388                     SetIfCond (D, GetCPU() == CPU_65816);
389                 }
390                 ExpectSep ();
391                 CalcOverallIfCond ();
392                 break;
393
394             case TOK_IFPC02:
395                 D = AllocIf (".IFPC02", 1);
396                 NextTok ();
397                 if (IfCond) {
398                     SetIfCond (D, GetCPU() == CPU_65C02);
399                 }
400                 ExpectSep ();
401                 CalcOverallIfCond ();
402                 break;
403
404             case TOK_IFPSC02:
405                 D = AllocIf (".IFPSC02", 1);
406                 NextTok ();
407                 if (IfCond) {
408                     SetIfCond (D, GetCPU() == CPU_65SC02);
409                 }
410                 ExpectSep ();
411                 CalcOverallIfCond ();
412                 break;
413
414             case TOK_IFREF:
415                 D = AllocIf (".IFREF", 1);
416                 NextTok ();
417                 if (IfCond) {
418                     SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
419                     SetIfCond (D, Sym != 0 && SymIsRef (Sym));
420                     ExpectSep ();
421                 }
422                 CalcOverallIfCond ();
423                 break;
424
425             default:
426                 /* Skip tokens */
427                 NextTok ();
428
429         }
430
431     } while (IfCond == 0 && CurTok.Tok != TOK_EOF);
432 }
433
434
435
436 int CheckConditionals (void)
437 /* Check if the current token is one that starts a conditional directive, and
438  * call DoConditionals if so. Return true if a conditional directive was found,
439  * return false otherwise.
440  */
441 {
442     switch (CurTok.Tok) {
443         case TOK_ELSE:
444         case TOK_ELSEIF:
445         case TOK_ENDIF:
446         case TOK_IF:
447         case TOK_IFBLANK:
448         case TOK_IFCONST:
449         case TOK_IFDEF:
450         case TOK_IFNBLANK:
451         case TOK_IFNCONST:
452         case TOK_IFNDEF:
453         case TOK_IFNREF:
454         case TOK_IFP02:
455         case TOK_IFP816:
456         case TOK_IFPC02:
457         case TOK_IFPSC02:
458         case TOK_IFREF:
459             DoConditionals ();
460             return 1;
461
462         default:
463             return 0;
464     }
465 }
466
467
468
469 void CheckOpenIfs (void)
470 /* Called from the scanner before closing an input file. Will check for any
471  * open .ifs in this file.
472  */
473 {
474     const LineInfo* LI;
475
476     while (1) {
477         /* Get the current file number and check if the topmost entry on the
478          * .IF stack was inserted with this file number
479          */
480         IfDesc* D = GetCurrentIf ();
481         if (D == 0) {
482             /* There are no open .IFs */
483             break;
484         }
485
486         LI = CollConstAt (&D->LineInfos, 0);
487         if (LI->Pos.Name != CurTok.Pos.Name) {
488             /* The .if is from another file, bail out */
489             break;
490         }
491
492         /* Start of .if is in the file we're about to leave */
493         LIError (&D->LineInfos, "Conditional assembly branch was never closed");
494         FreeIf ();
495     }
496
497     /* Calculate the new overall .IF condition */
498     CalcOverallIfCond ();
499 }
500
501
502
503 unsigned GetIfStack (void)
504 /* Get the current .IF stack pointer */
505 {
506     return IfCount;
507 }
508
509
510
511 void CleanupIfStack (unsigned SP)
512 /* Cleanup the .IF stack, remove anything above the given stack pointer */
513 {
514     while (IfCount > SP) {
515         FreeIf ();
516     }
517
518     /* Calculate the new overall .IF condition */
519     CalcOverallIfCond ();
520 }
521
522
523
524