1 /*****************************************************************************/
5 /* Conditional assembly support for ca65 */
9 /* (C) 2000-2011, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
48 /*****************************************************************************/
50 /*****************************************************************************/
54 /* Maximum count of nested .ifs */
57 /* Set of bitmapped flags for the if descriptor */
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 */
66 /* The overall .IF condition */
71 /*****************************************************************************/
73 /*****************************************************************************/
77 /* One .IF descriptor */
78 typedef struct IfDesc IfDesc;
80 unsigned Flags; /* Bitmapped flags, see above */
81 Collection LineInfos; /* File position of the .IF */
82 const char* Name; /* Name of the directive */
86 static IfDesc IfStack [MAX_IFS];
87 static unsigned IfCount = 0;
91 static IfDesc* GetCurrentIf (void)
92 /* Return the current .IF descriptor */
97 return &IfStack[IfCount-1];
103 static int GetOverallIfCond (void)
104 /* Get the overall condition based on all conditions on the stack. */
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.
110 return (IfCount == 0) ||
111 ((IfStack[IfCount-1].Flags & (ifCond | ifParentCond)) == (ifCond | ifParentCond));
116 static void CalcOverallIfCond (void)
117 /* Caclulate the overall condition based on all conditions on the stack. */
119 IfCond = GetOverallIfCond ();
124 static void SetIfCond (IfDesc* ID, int C)
125 /* Set the .IF condition */
130 ID->Flags &= ~ifCond;
136 static void ElseClause (IfDesc* ID, const char* Directive)
137 /* Enter an .ELSE clause */
139 /* Check if we have an open .IF - otherwise .ELSE is not allowed */
141 Error ("Unexpected %s", Directive);
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");
152 /* Condition is inverted now */
158 static IfDesc* AllocIf (const char* Directive, int NeedTerm)
159 /* Alloc a new element from the .IF stack */
163 /* Check for stack overflow */
164 if (IfCount >= MAX_IFS) {
165 Fatal ("Too many nested .IFs");
168 /* Get the next element */
169 ID = &IfStack[IfCount];
171 /* Initialize elements */
172 ID->Flags = NeedTerm? ifNeedTerm : ifNone;
173 if (GetOverallIfCond ()) {
174 /* The parents .IF condition is true */
175 ID->Flags |= ifParentCond;
177 ID->LineInfos = EmptyCollection;
178 GetFullLineInfo (&ID->LineInfos);
179 ID->Name = Directive;
181 /* One more slot allocated */
184 /* Return the result */
190 static void FreeIf (void)
191 /* Free all .IF descriptors until we reach one with the NeedTerm bit set */
195 IfDesc* ID = GetCurrentIf();
197 Error (" Unexpected .ENDIF");
200 Done = (ID->Flags & ifNeedTerm) != 0;
201 ReleaseFullLineInfo (&ID->LineInfos);
202 DoneCollection (&ID->LineInfos);
210 /*****************************************************************************/
212 /*****************************************************************************/
216 void DoConditionals (void)
217 /* Catch all for conditional directives */
223 switch (CurTok.Tok) {
229 ElseClause (D, ".ELSE");
231 /* Remember the data for the .ELSE */
233 ReleaseFullLineInfo (&D->LineInfos);
234 GetFullLineInfo (&D->LineInfos);
238 /* Calculate the new overall condition */
239 CalcOverallIfCond ();
248 /* Handle as if there was an .ELSE first */
249 ElseClause (D, ".ELSEIF");
251 /* Calculate the new overall if condition */
252 CalcOverallIfCond ();
254 /* Allocate and prepare a new descriptor */
255 D = AllocIf (".ELSEIF", 0);
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...
263 SetIfCond (D, ConstExpression ());
267 /* Get the new overall condition */
268 CalcOverallIfCond ();
272 /* We're done with this .IF.. - remove the descriptor(s) */
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.
281 /* Get the new overall condition */
282 CalcOverallIfCond ();
286 D = AllocIf (".IF", 1);
289 SetIfCond (D, ConstExpression ());
292 CalcOverallIfCond ();
296 D = AllocIf (".IFBLANK", 1);
299 if (TokIsSep (CurTok.Tok)) {
306 CalcOverallIfCond ();
310 D = AllocIf (".IFCONST", 1);
313 ExprNode* Expr = Expression();
314 SetIfCond (D, IsConstExpr (Expr, 0));
318 CalcOverallIfCond ();
322 D = AllocIf (".IFDEF", 1);
325 SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
326 SetIfCond (D, Sym != 0 && SymIsDef (Sym));
328 CalcOverallIfCond ();
332 D = AllocIf (".IFNBLANK", 1);
335 if (TokIsSep (CurTok.Tok)) {
342 CalcOverallIfCond ();
346 D = AllocIf (".IFNCONST", 1);
349 ExprNode* Expr = Expression();
350 SetIfCond (D, !IsConstExpr (Expr, 0));
354 CalcOverallIfCond ();
358 D = AllocIf (".IFNDEF", 1);
361 SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
362 SetIfCond (D, Sym == 0 || !SymIsDef (Sym));
365 CalcOverallIfCond ();
369 D = AllocIf (".IFNREF", 1);
372 SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
373 SetIfCond (D, Sym == 0 || !SymIsRef (Sym));
376 CalcOverallIfCond ();
380 D = AllocIf (".IFP02", 1);
383 SetIfCond (D, GetCPU() == CPU_6502);
386 CalcOverallIfCond ();
390 D = AllocIf (".IFP816", 1);
393 SetIfCond (D, GetCPU() == CPU_65816);
396 CalcOverallIfCond ();
400 D = AllocIf (".IFPC02", 1);
403 SetIfCond (D, GetCPU() == CPU_65C02);
406 CalcOverallIfCond ();
410 D = AllocIf (".IFPSC02", 1);
413 SetIfCond (D, GetCPU() == CPU_65SC02);
416 CalcOverallIfCond ();
420 D = AllocIf (".IFREF", 1);
423 SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
424 SetIfCond (D, Sym != 0 && SymIsRef (Sym));
427 CalcOverallIfCond ();
436 } while (IfCond == 0 && CurTok.Tok != TOK_EOF);
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.
447 switch (CurTok.Tok) {
474 void CheckOpenIfs (void)
475 /* Called from the scanner before closing an input file. Will check for any
476 * open .ifs in this file.
482 /* Get the current file number and check if the topmost entry on the
483 * .IF stack was inserted with this file number
485 IfDesc* D = GetCurrentIf ();
487 /* There are no open .IFs */
491 LI = CollConstAt (&D->LineInfos, 0);
492 if (GetSourcePos (LI)->Name != CurTok.Pos.Name) {
493 /* The .if is from another file, bail out */
497 /* Start of .if is in the file we're about to leave */
498 LIError (&D->LineInfos, "Conditional assembly branch was never closed");
502 /* Calculate the new overall .IF condition */
503 CalcOverallIfCond ();
508 unsigned GetIfStack (void)
509 /* Get the current .IF stack pointer */
516 void CleanupIfStack (unsigned SP)
517 /* Cleanup the .IF stack, remove anything above the given stack pointer */
519 while (IfCount > SP) {
523 /* Calculate the new overall .IF condition */
524 CalcOverallIfCond ();