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