]> git.sur5r.net Git - cc65/blob - src/ca65/condasm.c
Removed (pretty inconsistently used) tab chars from source code base.
[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);
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_IFP816:
390                 D = AllocIf (".IFP816", 1);
391                 NextTok ();
392                 if (IfCond) {
393                     SetIfCond (D, GetCPU() == CPU_65816);
394                 }
395                 ExpectSep ();
396                 CalcOverallIfCond ();
397                 break;
398
399             case TOK_IFPC02:
400                 D = AllocIf (".IFPC02", 1);
401                 NextTok ();
402                 if (IfCond) {
403                     SetIfCond (D, GetCPU() == CPU_65C02);
404                 }
405                 ExpectSep ();
406                 CalcOverallIfCond ();
407                 break;
408
409             case TOK_IFPSC02:
410                 D = AllocIf (".IFPSC02", 1);
411                 NextTok ();
412                 if (IfCond) {
413                     SetIfCond (D, GetCPU() == CPU_65SC02);
414                 }
415                 ExpectSep ();
416                 CalcOverallIfCond ();
417                 break;
418
419             case TOK_IFREF:
420                 D = AllocIf (".IFREF", 1);
421                 NextTok ();
422                 if (IfCond) {
423                     SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
424                     SetIfCond (D, Sym != 0 && SymIsRef (Sym));
425                     ExpectSep ();
426                 }
427                 CalcOverallIfCond ();
428                 break;
429
430             default:
431                 /* Skip tokens */
432                 NextTok ();
433
434         }
435
436     } while (IfCond == 0 && CurTok.Tok != TOK_EOF);
437 }
438
439
440
441 int CheckConditionals (void)
442 /* Check if the current token is one that starts a conditional directive, and
443  * call DoConditionals if so. Return true if a conditional directive was found,
444  * return false otherwise.
445  */
446 {
447     switch (CurTok.Tok) {
448         case TOK_ELSE:
449         case TOK_ELSEIF:
450         case TOK_ENDIF:
451         case TOK_IF:
452         case TOK_IFBLANK:
453         case TOK_IFCONST:
454         case TOK_IFDEF:
455         case TOK_IFNBLANK:
456         case TOK_IFNCONST:
457         case TOK_IFNDEF:
458         case TOK_IFNREF:
459         case TOK_IFP02:
460         case TOK_IFP816:
461         case TOK_IFPC02:
462         case TOK_IFPSC02:
463         case TOK_IFREF:
464             DoConditionals ();
465             return 1;
466
467         default:
468             return 0;
469     }
470 }
471
472
473
474 void CheckOpenIfs (void)
475 /* Called from the scanner before closing an input file. Will check for any
476  * open .ifs in this file.
477  */
478 {
479     const LineInfo* LI;
480
481     while (1) {
482         /* Get the current file number and check if the topmost entry on the
483          * .IF stack was inserted with this file number
484          */
485         IfDesc* D = GetCurrentIf ();
486         if (D == 0) {
487             /* There are no open .IFs */
488             break;
489         }
490
491         LI = CollConstAt (&D->LineInfos, 0);
492         if (GetSourcePos (LI)->Name != CurTok.Pos.Name) {
493             /* The .if is from another file, bail out */
494             break;
495         }
496
497         /* Start of .if is in the file we're about to leave */
498         LIError (&D->LineInfos, "Conditional assembly branch was never closed");
499         FreeIf ();
500     }
501
502     /* Calculate the new overall .IF condition */
503     CalcOverallIfCond ();
504 }
505
506
507
508 unsigned GetIfStack (void)
509 /* Get the current .IF stack pointer */
510 {
511     return IfCount;
512 }
513
514
515
516 void CleanupIfStack (unsigned SP)
517 /* Cleanup the .IF stack, remove anything above the given stack pointer */
518 {
519     while (IfCount > SP) {
520         FreeIf ();
521     }
522
523     /* Calculate the new overall .IF condition */
524     CalcOverallIfCond ();
525 }
526
527
528
529