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