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