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