]> git.sur5r.net Git - cc65/blob - src/ca65/scanner.c
3e3c4777c87f95925372925db77224f5818f7f16
[cc65] / src / ca65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*                  The scanner for the ca65 macroassembler                  */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2010, 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 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <sys/types.h>          /* EMX needs this */
42 #include <sys/stat.h>
43
44 /* common */
45 #include "addrsize.h"
46 #include "attrib.h"
47 #include "chartype.h"
48 #include "check.h"
49 #include "fname.h"
50 #include "xmalloc.h"
51
52 /* ca65 */
53 #include "condasm.h"
54 #include "error.h"
55 #include "filetab.h"
56 #include "global.h"
57 #include "incpath.h"
58 #include "instr.h"
59 #include "istack.h"
60 #include "listing.h"
61 #include "macro.h"
62 #include "toklist.h"
63 #include "scanner.h"
64
65
66
67 /*****************************************************************************/
68 /*                                   Data                                    */
69 /*****************************************************************************/
70
71
72
73 Token Tok = TOK_NONE;                   /* Current token */
74 int WS;                                 /* Flag: Whitespace before token */
75 long IVal;                              /* Integer token attribute */
76 StrBuf SVal = STATIC_STRBUF_INITIALIZER;/* String token attribute */
77
78 FilePos CurPos = { 0, 0, 0 };           /* Name and position in current file */
79
80
81
82 /* Struct to handle include files. */
83 typedef struct InputFile InputFile;
84 struct InputFile {
85     FILE*           F;                  /* Input file descriptor */
86     FilePos         Pos;                /* Position in file */
87     Token           Tok;                /* Last token */
88     int             C;                  /* Last character */
89     char            Line[256];          /* The current input line */
90     InputFile*      Next;               /* Linked list of input files */
91 };
92
93 /* Struct to handle textual input data */
94 typedef struct InputData InputData;
95 struct InputData {
96     char*           Text;               /* Pointer to the text data */
97     const char*     Pos;                /* Pointer to current position */
98     int             Malloced;           /* Memory was malloced */
99     Token           Tok;                /* Last token */
100     int             C;                  /* Last character */
101     InputData*      Next;               /* Linked list of input data */
102 };
103
104 /* Input source: Either file or data */
105 typedef struct CharSource CharSource;
106
107 /* Set of input functions */
108 typedef struct CharSourceFunctions CharSourceFunctions;
109 struct CharSourceFunctions {
110     void (*MarkStart) (CharSource*);    /* Mark the start pos of a token */
111     void (*NextChar) (CharSource*);     /* Read next char from input */
112     void (*Done) (CharSource*);         /* Close input source */
113 };
114
115 /* Input source: Either file or data */
116 struct CharSource {
117     CharSource*                 Next;   /* Linked list of char sources */
118     Token                       Tok;    /* Last token */
119     int                         C;      /* Last character */
120     const CharSourceFunctions*  Func;   /* Pointer to function table */
121     union {
122         InputFile               File;   /* File data */
123         InputData               Data;   /* Textual data */
124     }                           V;
125 };
126
127 /* Current input variables */
128 static CharSource* Source       = 0;    /* Current char source */
129 static unsigned     FCount      = 0;    /* Count of input files */
130 static int          C           = 0;    /* Current input character */
131
132 /* Force end of assembly */
133 int               ForcedEnd     = 0;
134
135 /* List of dot keywords with the corresponding tokens */
136 struct DotKeyword {
137     const char* Key;                    /* MUST be first field */
138     Token       Tok;
139 } DotKeywords [] = {
140     { ".A16",           TOK_A16         },
141     { ".A8",            TOK_A8          },
142     { ".ADDR",          TOK_ADDR        },
143     { ".ALIGN",         TOK_ALIGN       },
144     { ".AND",           TOK_BOOLAND     },
145     { ".ASCIIZ",        TOK_ASCIIZ      },
146     { ".ASSERT",        TOK_ASSERT      },
147     { ".AUTOIMPORT",    TOK_AUTOIMPORT  },
148     { ".BANKBYTE",      TOK_BANKBYTE    },
149     { ".BANKBYTES",     TOK_BANKBYTES   },
150     { ".BITAND",        TOK_AND         },
151     { ".BITNOT",        TOK_NOT         },
152     { ".BITOR",         TOK_OR          },
153     { ".BITXOR",        TOK_XOR         },
154     { ".BLANK",         TOK_BLANK       },
155     { ".BSS",           TOK_BSS         },
156     { ".BYT",           TOK_BYTE        },
157     { ".BYTE",          TOK_BYTE        },
158     { ".CASE",          TOK_CASE        },
159     { ".CHARMAP",       TOK_CHARMAP     },
160     { ".CODE",          TOK_CODE        },
161     { ".CONCAT",        TOK_CONCAT      },
162     { ".CONDES",        TOK_CONDES      },
163     { ".CONST",         TOK_CONST       },
164     { ".CONSTRUCTOR",   TOK_CONSTRUCTOR },
165     { ".CPU",           TOK_CPU         },
166     { ".DATA",          TOK_DATA        },
167     { ".DBG",           TOK_DBG         },
168     { ".DBYT",          TOK_DBYT        },
169     { ".DEBUGINFO",     TOK_DEBUGINFO   },
170     { ".DEF",           TOK_DEFINED     },
171     { ".DEFINE",        TOK_DEFINE      },
172     { ".DEFINED",       TOK_DEFINED     },
173     { ".DESTRUCTOR",    TOK_DESTRUCTOR  },
174     { ".DWORD",         TOK_DWORD       },
175     { ".ELSE",          TOK_ELSE        },
176     { ".ELSEIF",        TOK_ELSEIF      },
177     { ".END",           TOK_END         },
178     { ".ENDENUM",       TOK_ENDENUM     },
179     { ".ENDIF",         TOK_ENDIF       },
180     { ".ENDMAC",        TOK_ENDMACRO    },
181     { ".ENDMACRO",      TOK_ENDMACRO    },
182     { ".ENDPROC",       TOK_ENDPROC     },
183     { ".ENDREP",        TOK_ENDREP      },
184     { ".ENDREPEAT",     TOK_ENDREP      },
185     { ".ENDSCOPE",      TOK_ENDSCOPE    },
186     { ".ENDSTRUCT",     TOK_ENDSTRUCT   },
187     { ".ENDUNION",      TOK_ENDUNION    },
188     { ".ENUM",          TOK_ENUM        },
189     { ".ERROR",         TOK_ERROR       },
190     { ".EXITMAC",       TOK_EXITMACRO   },
191     { ".EXITMACRO",     TOK_EXITMACRO   },
192     { ".EXPORT",        TOK_EXPORT      },
193     { ".EXPORTZP",      TOK_EXPORTZP    },
194     { ".FARADDR",       TOK_FARADDR     },
195     { ".FEATURE",       TOK_FEATURE     },
196     { ".FILEOPT",       TOK_FILEOPT     },
197     { ".FOPT",          TOK_FILEOPT     },
198     { ".FORCEIMPORT",   TOK_FORCEIMPORT },
199     { ".FORCEWORD",     TOK_FORCEWORD   },
200     { ".GLOBAL",        TOK_GLOBAL      },
201     { ".GLOBALZP",      TOK_GLOBALZP    },
202     { ".HIBYTE",        TOK_HIBYTE      },
203     { ".HIBYTES",       TOK_HIBYTES     },
204     { ".HIWORD",        TOK_HIWORD      },
205     { ".I16",           TOK_I16         },
206     { ".I8",            TOK_I8          },
207     { ".IDENT",         TOK_MAKEIDENT   },
208     { ".IF",            TOK_IF          },
209     { ".IFBLANK",       TOK_IFBLANK     },
210     { ".IFCONST",       TOK_IFCONST     },
211     { ".IFDEF",         TOK_IFDEF       },
212     { ".IFNBLANK",      TOK_IFNBLANK    },
213     { ".IFNCONST",      TOK_IFNCONST    },
214     { ".IFNDEF",        TOK_IFNDEF      },
215     { ".IFNREF",        TOK_IFNREF      },
216     { ".IFP02",         TOK_IFP02       },
217     { ".IFP816",        TOK_IFP816      },
218     { ".IFPC02",        TOK_IFPC02      },
219     { ".IFPSC02",       TOK_IFPSC02     },
220     { ".IFREF",         TOK_IFREF       },
221     { ".IMPORT",        TOK_IMPORT      },
222     { ".IMPORTZP",      TOK_IMPORTZP    },
223     { ".INCBIN",        TOK_INCBIN      },
224     { ".INCLUDE",       TOK_INCLUDE     },
225     { ".INTERRUPTOR",   TOK_INTERRUPTOR },
226     { ".LEFT",          TOK_LEFT        },
227     { ".LINECONT",      TOK_LINECONT    },
228     { ".LIST",          TOK_LIST        },
229     { ".LISTBYTES",     TOK_LISTBYTES   },
230     { ".LOBYTE",        TOK_LOBYTE      },
231     { ".LOBYTES",       TOK_LOBYTES     },
232     { ".LOCAL",         TOK_LOCAL       },
233     { ".LOCALCHAR",     TOK_LOCALCHAR   },
234     { ".LOWORD",        TOK_LOWORD      },
235     { ".MAC",           TOK_MACRO       },
236     { ".MACPACK",       TOK_MACPACK     },
237     { ".MACRO",         TOK_MACRO       },
238     { ".MATCH",         TOK_MATCH       },
239     { ".MID",           TOK_MID         },
240     { ".MOD",           TOK_MOD         },
241     { ".NOT",           TOK_BOOLNOT     },
242     { ".NULL",          TOK_NULL        },
243     { ".OR",            TOK_BOOLOR      },
244     { ".ORG",           TOK_ORG         },
245     { ".OUT",           TOK_OUT         },
246     { ".P02",           TOK_P02         },
247     { ".P816",          TOK_P816        },
248     { ".PAGELEN",       TOK_PAGELENGTH  },
249     { ".PAGELENGTH",    TOK_PAGELENGTH  },
250     { ".PARAMCOUNT",    TOK_PARAMCOUNT  },
251     { ".PC02",          TOK_PC02        },
252     { ".POPSEG",        TOK_POPSEG      },
253     { ".PROC",          TOK_PROC        },
254     { ".PSC02",         TOK_PSC02       },
255     { ".PUSHSEG",       TOK_PUSHSEG     },
256     { ".REF",           TOK_REFERENCED  },
257     { ".REFERENCED",    TOK_REFERENCED  },
258     { ".RELOC",         TOK_RELOC       },
259     { ".REPEAT",        TOK_REPEAT      },
260     { ".RES",           TOK_RES         },
261     { ".RIGHT",         TOK_RIGHT       },
262     { ".RODATA",        TOK_RODATA      },
263     { ".SCOPE",         TOK_SCOPE       },
264     { ".SEGMENT",       TOK_SEGMENT     },
265     { ".SET",           TOK_SET         },
266     { ".SETCPU",        TOK_SETCPU      },
267     { ".SHL",           TOK_SHL         },
268     { ".SHR",           TOK_SHR         },
269     { ".SIZEOF",        TOK_SIZEOF      },
270     { ".SMART",         TOK_SMART       },
271     { ".SPRINTF",       TOK_SPRINTF     },
272     { ".STRAT",         TOK_STRAT       },
273     { ".STRING",        TOK_STRING      },
274     { ".STRLEN",        TOK_STRLEN      },
275     { ".STRUCT",        TOK_STRUCT      },
276     { ".SUNPLUS",       TOK_SUNPLUS     },
277     { ".TAG",           TOK_TAG         },
278     { ".TCOUNT",        TOK_TCOUNT      },
279     { ".TIME",          TOK_TIME        },
280     { ".UNION",         TOK_UNION       },
281     { ".VERSION",       TOK_VERSION     },
282     { ".WARNING",       TOK_WARNING     },
283     { ".WORD",          TOK_WORD        },
284     { ".XMATCH",        TOK_XMATCH      },
285     { ".XOR",           TOK_BOOLXOR     },
286     { ".ZEROPAGE",      TOK_ZEROPAGE    },
287 };
288
289
290
291 /*****************************************************************************/
292 /*                            CharSource functions                           */
293 /*****************************************************************************/
294
295
296
297 static void UseCharSource (CharSource* S)
298 /* Initialize a new input source and start to use it. */
299 {
300     /* Remember the current input char and token */
301     S->Tok      = Tok;
302     S->C        = C;
303
304     /* Use the new input source */
305     S->Next     = Source;
306     Source      = S;
307
308     /* Read the first character from the new file */
309     S->Func->NextChar (S);
310
311     /* Setup the next token so it will be skipped on the next call to
312      * NextRawTok().
313      */
314     Tok = TOK_SEP;
315 }
316
317
318
319 static void DoneCharSource (void)
320 /* Close the top level character source */
321 {
322     CharSource* S;
323
324     /* First, call the type specific function */
325     Source->Func->Done (Source);
326
327     /* Restore the old token */
328     Tok = Source->Tok;
329     C   = Source->C;
330
331     /* Remember the last stacked input source */
332     S = Source->Next;
333
334     /* Delete the top level one ... */
335     xfree (Source);
336
337     /* ... and use the one before */
338     Source = S;
339 }
340
341
342
343 /*****************************************************************************/
344 /*                            InputFile functions                            */
345 /*****************************************************************************/
346
347
348
349 static void IFMarkStart (CharSource* S)
350 /* Mark the start of the next token */
351 {
352     CurPos = S->V.File.Pos;
353 }
354
355
356
357 static void IFNextChar (CharSource* S)
358 /* Read the next character from the input file */
359 {
360     /* Check for end of line, read the next line if needed */
361     while (S->V.File.Line [S->V.File.Pos.Col] == '\0') {
362
363         unsigned Len, Removed;
364
365         /* End of current line reached, read next line */
366         if (fgets (S->V.File.Line, sizeof (S->V.File.Line), S->V.File.F) == 0) {
367             /* End of file. Add an empty line to the listing. This is a
368              * small hack needed to keep the PC output in sync.
369              */
370             NewListingLine ("", S->V.File.Pos.Name, FCount);
371             C = EOF;
372             return;
373         }
374
375         /* For better handling of files with unusual line endings (DOS
376          * files that are accidently translated on Unix for example),
377          * first remove all whitespace at the end, then add a single
378          * newline.
379          */
380         Len = strlen (S->V.File.Line);
381         Removed = 0;
382         while (Len > 0 && IsSpace (S->V.File.Line[Len-1])) {
383             ++Removed;
384             --Len;
385         }
386         if (Removed) {
387             S->V.File.Line[Len+0] = '\n';
388             S->V.File.Line[Len+1] = '\0';
389         }
390
391         /* One more line */
392         S->V.File.Pos.Line++;
393         S->V.File.Pos.Col = 0;
394
395         /* Remember the new line for the listing */
396         NewListingLine (S->V.File.Line, S->V.File.Pos.Name, FCount);
397
398     }
399
400     /* Return the next character from the file */
401     C = S->V.File.Line [S->V.File.Pos.Col++];
402 }
403
404
405
406 void IFDone (CharSource* S)
407 /* Close the current input file */
408 {
409     /* We're at the end of an include file. Check if we have any
410      * open .IFs, or any open token lists in this file. This
411      * enforcement is artificial, using conditionals that start
412      * in one file and end in another are uncommon, and don't
413      * allowing these things will help finding errors.
414      */
415     CheckOpenIfs ();
416
417     /* Close the input file and decrement the file count. We will ignore
418      * errors here, since we were just reading from the file.
419      */
420     (void) fclose (S->V.File.F);
421     --FCount;
422 }
423
424
425
426 /* Set of input file handling functions */
427 static const CharSourceFunctions IFFunc = {
428     IFMarkStart,
429     IFNextChar,
430     IFDone
431 };
432
433
434
435 int NewInputFile (const char* Name)
436 /* Open a new input file. Returns true if the file could be successfully opened
437  * and false otherwise.
438  */
439 {
440     int RetCode = 0;            /* Return code. Assume an error. */
441     char* PathName = 0;
442
443     /* First try to open the file */
444     FILE* F = fopen (Name, "r");
445     if (F == 0) {
446
447         /* Error (fatal error if this is the main file) */
448         if (FCount == 0) {
449             Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
450         }
451
452         /* We are on include level. Search for the file in the include
453          * directories.
454          */
455         PathName = FindInclude (Name, INC_STD);
456         if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
457             /* Not found or cannot open, print an error and bail out */
458             Error ("Cannot open include file `%s': %s", Name, strerror (errno));
459             goto ExitPoint;
460         }
461
462         /* Use the path name from now on */
463         Name = PathName;
464     }
465
466     /* check again if we do now have an open file */
467     if (F != 0) {
468
469         StrBuf          NameBuf;
470         unsigned        FileIdx;
471         CharSource*     S;
472
473         /* Stat the file and remember the values. There a race condition here,
474          * since we cannot use fileno() (non standard identifier in standard
475          * header file), and therefore not fstat. When using stat with the
476          * file name, there's a risk that the file was deleted and recreated
477          * while it was open. Since mtime and size are only used to check
478          * if a file has changed in the debugger, we will ignore this problem
479          * here.
480          */
481         struct stat Buf;
482         if (stat (Name, &Buf) != 0) {
483             Fatal ("Cannot stat input file `%s': %s", Name, strerror (errno));
484         }
485
486         /* Add the file to the input file table and remember the index */
487         FileIdx = AddFile (SB_InitFromString (&NameBuf, Name), Buf.st_size, Buf.st_mtime);
488
489         /* Create a new input source variable and initialize it */
490         S                   = xmalloc (sizeof (*S));
491         S->Func             = &IFFunc;
492         S->V.File.F         = F;
493         S->V.File.Pos.Line  = 0;
494         S->V.File.Pos.Col   = 0;
495         S->V.File.Pos.Name  = FileIdx;
496         S->V.File.Line[0]   = '\0';
497
498         /* Count active input files */
499         ++FCount;
500
501         /* Use this input source */
502         UseCharSource (S);
503     }
504
505     /* File successfully opened */
506     RetCode = 1;
507
508 ExitPoint:
509     /* Free an allocated name buffer */
510     xfree (PathName);
511
512     /* Return the success code */
513     return RetCode;
514 }
515
516
517
518 /*****************************************************************************/
519 /*                            InputData functions                            */
520 /*****************************************************************************/
521
522
523
524 static void IDMarkStart (CharSource* S attribute ((unused)))
525 /* Mark the start of the next token */
526 {
527     /* Nothing to do here */
528 }
529
530
531
532 static void IDNextChar (CharSource* S)
533 /* Read the next character from the input text */
534 {
535     C = *S->V.Data.Pos++;
536     if (C == '\0') {
537         /* End of input data */
538         --S->V.Data.Pos;
539         C = EOF;
540     }
541 }
542
543
544
545 void IDDone (CharSource* S)
546 /* Close the current input data */
547 {
548     /* Cleanup the current stuff */
549     if (S->V.Data.Malloced) {
550         xfree (S->V.Data.Text);
551     }
552 }
553
554
555
556 /* Set of input data handling functions */
557 static const CharSourceFunctions IDFunc = {
558     IDMarkStart,
559     IDNextChar,
560     IDDone
561 };
562
563
564
565 void NewInputData (char* Text, int Malloced)
566 /* Add a chunk of input data to the input stream */
567 {
568     CharSource* S;
569
570     /* Create a new input source variable and initialize it */
571     S                   = xmalloc (sizeof (*S));
572     S->Func             = &IDFunc;
573     S->V.Data.Text      = Text;
574     S->V.Data.Pos       = Text;
575     S->V.Data.Malloced  = Malloced;
576
577     /* Use this input source */
578     UseCharSource (S);
579 }
580
581
582
583 /*****************************************************************************/
584 /*                    Character classification functions                     */
585 /*****************************************************************************/
586
587
588
589 int IsIdChar (int C)
590 /* Return true if the character is a valid character for an identifier */
591 {
592     return IsAlNum (C)                  ||
593            (C == '_')                   ||
594            (C == '@' && AtInIdents)     ||
595            (C == '$' && DollarInIdents);
596 }
597
598
599
600 int IsIdStart (int C)
601 /* Return true if the character may start an identifier */
602 {
603     return IsAlpha (C) || C == '_';
604 }
605
606
607
608 /*****************************************************************************/
609 /*                                   Code                                    */
610 /*****************************************************************************/
611
612
613
614 static unsigned DigitVal (unsigned char C)
615 /* Convert a digit into it's numerical representation */
616 {
617     if (IsDigit (C)) {
618         return C - '0';
619     } else {
620         return toupper (C) - 'A' + 10;
621     }
622 }
623
624
625
626 static void NextChar (void)
627 /* Read the next character from the input file */
628 {
629     Source->Func->NextChar (Source);
630 }
631
632
633
634 void LocaseSVal (void)
635 /* Make SVal lower case */
636 {
637     SB_ToLower (&SVal);
638 }
639
640
641
642 void UpcaseSVal (void)
643 /* Make SVal upper case */
644 {
645     SB_ToUpper (&SVal);
646 }
647
648
649
650 static int CmpDotKeyword (const void* K1, const void* K2)
651 /* Compare function for the dot keyword search */
652 {
653     return strcmp (((struct DotKeyword*)K1)->Key, ((struct DotKeyword*)K2)->Key);
654 }
655
656
657
658 static unsigned char FindDotKeyword (void)
659 /* Find the dot keyword in SVal. Return the corresponding token if found,
660  * return TOK_NONE if not found.
661  */
662 {
663     struct DotKeyword K;
664     struct DotKeyword* R;
665
666     /* Initialize K */
667     K.Key = SB_GetConstBuf (&SVal);
668     K.Tok = 0;
669
670     /* If we aren't in ignore case mode, we have to uppercase the keyword */
671     if (!IgnoreCase) {
672         UpcaseSVal ();
673     }
674
675     /* Search for the keyword */
676     R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]),
677                  sizeof (DotKeywords [0]), CmpDotKeyword);
678     if (R != 0) {
679         return R->Tok;
680     } else {
681         return TOK_NONE;
682     }
683 }
684
685
686
687 static void ReadIdent (void)
688 /* Read an identifier from the current input position into Ident. Filling SVal
689  * starts at the current position with the next character in C. It is assumed
690  * that any characters already filled in are ok, and the character in C is
691  * checked.
692  */
693 {
694     /* Read the identifier */
695     do {
696         SB_AppendChar (&SVal, C);
697         NextChar ();
698     } while (IsIdChar (C));
699     SB_Terminate (&SVal);
700
701     /* If we should ignore case, convert the identifier to upper case */
702     if (IgnoreCase) {
703         UpcaseSVal ();
704     }
705 }
706
707
708
709 static void ReadStringConst (int StringTerm)
710 /* Read a string constant into SVal. */
711 {
712     /* Skip the leading string terminator */
713     NextChar ();
714
715     /* Read the string */
716     while (1) {
717         if (C == StringTerm) {
718             break;
719         }
720         if (C == '\n' || C == EOF) {
721             Error ("Newline in string constant");
722             break;
723         }
724
725         /* Append the char to the string */
726         SB_AppendChar (&SVal, C);
727
728         /* Skip the character */
729         NextChar ();
730     }
731
732     /* Skip the trailing terminator */
733     NextChar ();
734
735     /* Terminate the string */
736     SB_Terminate (&SVal);
737 }
738
739
740
741 static int Sweet16Reg (const StrBuf* Id)
742 /* Check if the given identifier is a sweet16 register. Return -1 if this is
743  * not the case, return the register number otherwise.
744  */
745 {
746     unsigned RegNum;
747     char Check;
748
749     if (SB_GetLen (Id) < 2) {
750         return -1;
751     }
752     if (toupper (SB_AtUnchecked (Id, 0)) != 'R') {
753         return -1;
754     }
755     if (!IsDigit (SB_AtUnchecked (Id, 1))) {
756         return -1;
757     }
758
759     if (sscanf (SB_GetConstBuf (Id)+1, "%u%c", &RegNum, &Check) != 1 || RegNum > 15) {
760         /* Invalid register */
761         return -1;
762     }
763
764     /* The register number is valid */
765     return (int) RegNum;
766 }
767
768
769
770 void NextRawTok (void)
771 /* Read the next raw token from the input stream */
772 {
773     /* If we've a forced end of assembly, don't read further */
774     if (ForcedEnd) {
775         Tok = TOK_EOF;
776         return;
777     }
778
779 Restart:
780     /* Check if we have tokens from another input source */
781     if (InputFromStack ()) {
782         return;
783     }
784
785 Again:
786     /* Skip whitespace, remember if we had some */
787     if ((WS = IsBlank (C)) != 0) {
788         do {
789             NextChar ();
790         } while (IsBlank (C));
791     }
792
793     /* Mark the file position of the next token */
794     Source->Func->MarkStart (Source);
795
796     /* Clear the string attribute */
797     SB_Clear (&SVal);
798
799     /* Hex number or PC symbol? */
800     if (C == '$') {
801         NextChar ();
802
803         /* Hex digit must follow or DollarIsPC must be enabled */
804         if (!IsXDigit (C)) {
805             if (DollarIsPC) {
806                 Tok = TOK_PC;
807                 return;
808             } else {
809                 Error ("Hexadecimal digit expected");
810             }
811         }
812
813         /* Read the number */
814         IVal = 0;
815         while (IsXDigit (C)) {
816             if (IVal & 0xF0000000) {
817                 Error ("Overflow in hexadecimal number");
818                 IVal = 0;
819             }
820             IVal = (IVal << 4) + DigitVal (C);
821             NextChar ();
822         }
823
824         /* This is an integer constant */
825         Tok = TOK_INTCON;
826         return;
827     }
828
829     /* Binary number? */
830     if (C == '%') {
831         NextChar ();
832
833         /* 0 or 1 must follow */
834         if (!IsBDigit (C)) {
835             Error ("Binary digit expected");
836         }
837
838         /* Read the number */
839         IVal = 0;
840         while (IsBDigit (C)) {
841             if (IVal & 0x80000000) {
842                 Error ("Overflow in binary number");
843                 IVal = 0;
844             }
845             IVal = (IVal << 1) + DigitVal (C);
846             NextChar ();
847         }
848
849         /* This is an integer constant */
850         Tok = TOK_INTCON;
851         return;
852     }
853
854     /* Number? */
855     if (IsDigit (C)) {
856
857         char Buf[16];
858         unsigned Digits;
859         unsigned Base;
860         unsigned I;
861         long     Max;
862         unsigned DVal;
863
864         /* Ignore leading zeros */
865         while (C == '0') {
866             NextChar ();
867         }
868
869         /* Read the number into Buf counting the digits */
870         Digits = 0;
871         while (IsXDigit (C)) {
872
873             /* Buf is big enough to allow any decimal and hex number to
874              * overflow, so ignore excess digits here, they will be detected
875              * when we convert the value.
876              */
877             if (Digits < sizeof (Buf)) {
878                 Buf[Digits++] = C;
879             }
880
881             NextChar ();
882         }
883
884         /* Allow zilog/intel style hex numbers with a 'h' suffix */
885         if (C == 'h' || C == 'H') {
886             NextChar ();
887             Base = 16;
888             Max  = 0xFFFFFFFFUL / 16;
889         } else {
890             Base = 10;
891             Max  = 0xFFFFFFFFUL / 10;
892         }
893
894         /* Convert the number using the given base */
895         IVal = 0;
896         for (I = 0; I < Digits; ++I) {
897             if (IVal > Max) {
898                 Error ("Number out of range");
899                 IVal = 0;
900                 break;
901             }
902             DVal = DigitVal (Buf[I]);
903             if (DVal > Base) {
904                 Error ("Invalid digits in number");
905                 IVal = 0;
906                 break;
907             }
908             IVal = (IVal * Base) + DVal;
909         }
910
911         /* This is an integer constant */
912         Tok = TOK_INTCON;
913         return;
914     }
915
916     /* Control command? */
917     if (C == '.') {
918
919         /* Remember and skip the dot */
920         NextChar ();
921
922         /* Check if it's just a dot */
923         if (!IsIdStart (C)) {
924
925             /* Just a dot */
926             Tok = TOK_DOT;
927
928         } else {
929
930             /* Read the remainder of the identifier */
931             SB_AppendChar (&SVal, '.');
932             ReadIdent ();
933
934             /* Dot keyword, search for it */
935             Tok = FindDotKeyword ();
936             if (Tok == TOK_NONE) {
937
938                 /* Not found */
939                 if (!LeadingDotInIdents) {
940                     /* Invalid pseudo instruction */
941                     Error ("`%m%p' is not a recognized control command", &SVal);
942                     goto Again;
943                 }
944
945                 /* An identifier with a dot. Check if it's a define style
946                  * macro.
947                  */
948                 if (IsDefine (&SVal)) {
949                     /* This is a define style macro - expand it */
950                     MacExpandStart ();
951                     goto Restart;
952                 }
953
954                 /* Just an identifier with a dot */
955                 Tok = TOK_IDENT;
956             }
957
958         }
959         return;
960     }
961
962     /* Indirect op for sweet16 cpu. Must check this before checking for local
963      * symbols, because these may also use the '@' symbol.
964      */
965     if (CPU == CPU_SWEET16 && C == '@') {
966         NextChar ();
967         Tok = TOK_AT;
968         return;
969     }
970
971     /* Local symbol? */
972     if (C == LocalStart) {
973
974         /* Read the identifier. */
975         ReadIdent ();
976
977         /* Start character alone is not enough */
978         if (SB_GetLen (&SVal) == 1) {
979             Error ("Invalid cheap local symbol");
980             goto Again;
981         }
982
983         /* A local identifier */
984         Tok = TOK_LOCAL_IDENT;
985         return;
986     }
987
988
989     /* Identifier or keyword? */
990     if (IsIdStart (C)) {
991
992         /* Read the identifier */
993         ReadIdent ();
994
995         /* Check for special names. Bail out if we have identified the type of
996          * the token. Go on if the token is an identifier.
997          */
998         if (SB_GetLen (&SVal) == 1) {
999             switch (toupper (SB_AtUnchecked (&SVal, 0))) {
1000
1001                 case 'A':
1002                     if (C == ':') {
1003                         NextChar ();
1004                         Tok = TOK_OVERRIDE_ABS;
1005                     } else {
1006                         Tok = TOK_A;
1007                     }
1008                     return;
1009
1010                 case 'F':
1011                     if (C == ':') {
1012                         NextChar ();
1013                         Tok = TOK_OVERRIDE_FAR;
1014                         return;
1015                     }
1016                     break;
1017
1018                 case 'S':
1019                     if (CPU == CPU_65816) {
1020                         Tok = TOK_S;
1021                         return;
1022                     }
1023                     break;
1024
1025                 case 'X':
1026                     Tok = TOK_X;
1027                     return;
1028
1029                 case 'Y':
1030                     Tok = TOK_Y;
1031                     return;
1032
1033                 case 'Z':
1034                     if (C == ':') {
1035                         NextChar ();
1036                         Tok = TOK_OVERRIDE_ZP;
1037                         return;
1038                     }
1039                     break;
1040
1041                 default:
1042                     break;
1043             }
1044
1045         } else if (CPU == CPU_SWEET16 && (IVal = Sweet16Reg (&SVal)) >= 0) {
1046
1047             /* A sweet16 register number in sweet16 mode */
1048             Tok = TOK_REG;
1049             return;
1050
1051         }
1052
1053         /* Check for define style macro */
1054         if (IsDefine (&SVal)) {
1055             /* Macro - expand it */
1056             MacExpandStart ();
1057             goto Restart;
1058         } else {
1059             /* An identifier */
1060             Tok = TOK_IDENT;
1061         }
1062         return;
1063     }
1064
1065     /* Ok, let's do the switch */
1066 CharAgain:
1067     switch (C) {
1068
1069         case '+':
1070             NextChar ();
1071             Tok = TOK_PLUS;
1072             return;
1073
1074         case '-':
1075             NextChar ();
1076             Tok = TOK_MINUS;
1077             return;
1078
1079         case '/':
1080             NextChar ();
1081             if (C != '*') {
1082                 Tok = TOK_DIV;
1083             } else if (CComments) {
1084                 /* Remember the position, then skip the '*' */
1085                 FilePos Pos = CurPos;
1086                 NextChar ();
1087                 do {
1088                     while (C !=  '*') {
1089                         if (C == EOF) {
1090                             PError (&Pos, "Unterminated comment");
1091                             goto CharAgain;
1092                         }
1093                         NextChar ();
1094                     }
1095                     NextChar ();
1096                 } while (C != '/');
1097                 NextChar ();
1098                 goto Again;
1099             }
1100             return;
1101
1102         case '*':
1103             NextChar ();
1104             Tok = TOK_MUL;
1105             return;
1106
1107         case '^':
1108             NextChar ();
1109             Tok = TOK_XOR;
1110             return;
1111
1112         case '&':
1113             NextChar ();
1114             if (C == '&') {
1115                 NextChar ();
1116                 Tok = TOK_BOOLAND;
1117             } else {
1118                 Tok = TOK_AND;
1119             }
1120             return;
1121
1122         case '|':
1123             NextChar ();
1124             if (C == '|') {
1125                 NextChar ();
1126                 Tok = TOK_BOOLOR;
1127             } else {
1128                 Tok = TOK_OR;
1129             }
1130             return;
1131
1132         case ':':
1133             NextChar ();
1134             switch (C) {
1135
1136                 case ':':
1137                     NextChar ();
1138                     Tok = TOK_NAMESPACE;
1139                     break;
1140
1141                 case '-':
1142                     IVal = 0;
1143                     do {
1144                         --IVal;
1145                         NextChar ();
1146                     } while (C == '-');
1147                     Tok = TOK_ULABEL;
1148                     break;
1149
1150                 case '+':
1151                     IVal = 0;
1152                     do {
1153                         ++IVal;
1154                         NextChar ();
1155                     } while (C == '+');
1156                     Tok = TOK_ULABEL;
1157                     break;
1158
1159                 case '=':
1160                     NextChar ();
1161                     Tok = TOK_ASSIGN;
1162                     break;
1163
1164                 default:
1165                     Tok = TOK_COLON;
1166                     break;
1167             }
1168             return;
1169
1170         case ',':
1171             NextChar ();
1172             Tok = TOK_COMMA;
1173             return;
1174
1175         case ';':
1176             NextChar ();
1177             while (C != '\n' && C != EOF) {
1178                 NextChar ();
1179             }
1180             goto CharAgain;
1181
1182         case '#':
1183             NextChar ();
1184             Tok = TOK_HASH;
1185             return;
1186
1187         case '(':
1188             NextChar ();
1189             Tok = TOK_LPAREN;
1190             return;
1191
1192         case ')':
1193             NextChar ();
1194             Tok = TOK_RPAREN;
1195             return;
1196
1197         case '[':
1198             NextChar ();
1199             Tok = TOK_LBRACK;
1200             return;
1201
1202         case ']':
1203             NextChar ();
1204             Tok = TOK_RBRACK;
1205             return;
1206
1207         case '{':
1208             NextChar ();
1209             Tok = TOK_LCURLY;
1210             return;
1211
1212         case '}':
1213             NextChar ();
1214             Tok = TOK_RCURLY;
1215             return;
1216
1217         case '<':
1218             NextChar ();
1219             if (C == '=') {
1220                 NextChar ();
1221                 Tok = TOK_LE;
1222             } else if (C == '<') {
1223                 NextChar ();
1224                 Tok = TOK_SHL;
1225             } else if (C == '>') {
1226                 NextChar ();
1227                 Tok = TOK_NE;
1228             } else {
1229                 Tok = TOK_LT;
1230             }
1231             return;
1232
1233         case '=':
1234             NextChar ();
1235             Tok = TOK_EQ;
1236             return;
1237
1238         case '!':
1239             NextChar ();
1240             Tok = TOK_BOOLNOT;
1241             return;
1242
1243         case '>':
1244             NextChar ();
1245             if (C == '=') {
1246                 NextChar ();
1247                 Tok = TOK_GE;
1248             } else if (C == '>') {
1249                 NextChar ();
1250                 Tok = TOK_SHR;
1251             } else {
1252                 Tok = TOK_GT;
1253             }
1254             return;
1255
1256         case '~':
1257             NextChar ();
1258             Tok = TOK_NOT;
1259             return;
1260
1261         case '\'':
1262             /* Hack: If we allow ' as terminating character for strings, read
1263              * the following stuff as a string, and check for a one character
1264              * string later.
1265              */
1266             if (LooseStringTerm) {
1267                 ReadStringConst ('\'');
1268                 if (SB_GetLen (&SVal) == 1) {
1269                     IVal = SB_AtUnchecked (&SVal, 0);
1270                     Tok = TOK_CHARCON;
1271                 } else {
1272                     Tok = TOK_STRCON;
1273                 }
1274             } else {
1275                 /* Always a character constant */
1276                 NextChar ();
1277                 if (C == EOF || IsControl (C)) {
1278                     Error ("Illegal character constant");
1279                     goto CharAgain;
1280                 }
1281                 IVal = C;
1282                 Tok = TOK_CHARCON;
1283                 NextChar ();
1284                 if (C != '\'') {
1285                     if (!MissingCharTerm) {
1286                         Error ("Illegal character constant");
1287                     }
1288                 } else {
1289                     NextChar ();
1290                 }
1291             }
1292             return;
1293
1294         case '\"':
1295             ReadStringConst ('\"');
1296             Tok = TOK_STRCON;
1297             return;
1298
1299         case '\\':
1300             /* Line continuation? */
1301             if (LineCont) {
1302                 NextChar ();
1303                 if (C == '\n') {
1304                     /* Handle as white space */
1305                     NextChar ();
1306                     C = ' ';
1307                     goto Again;
1308                 }
1309             }
1310             break;
1311
1312         case '\n':
1313             NextChar ();
1314             Tok = TOK_SEP;
1315             return;
1316
1317         case EOF:
1318             CheckInputStack ();
1319             /* In case of the main file, do not close it, but return EOF. */
1320             if (Source && Source->Next) {
1321                 DoneCharSource ();
1322                 goto Again;
1323             } else {
1324                 Tok = TOK_EOF;
1325             }
1326             return;
1327     }
1328
1329     /* If we go here, we could not identify the current character. Skip it
1330      * and try again.
1331      */
1332     Error ("Invalid input character: 0x%02X", C & 0xFF);
1333     NextChar ();
1334     goto Again;
1335 }
1336
1337
1338
1339 int GetSubKey (const char** Keys, unsigned Count)
1340 /* Search for a subkey in a table of keywords. The current token must be an
1341  * identifier and all keys must be in upper case. The identifier will be
1342  * uppercased in the process. The function returns the index of the keyword,
1343  * or -1 if the keyword was not found.
1344  */
1345 {
1346     unsigned I;
1347
1348     /* Must have an identifier */
1349     PRECONDITION (Tok == TOK_IDENT);
1350
1351     /* If we aren't in ignore case mode, we have to uppercase the identifier */
1352     if (!IgnoreCase) {
1353         UpcaseSVal ();
1354     }
1355
1356     /* Do a linear search (a binary search is not worth the effort) */
1357     for (I = 0; I < Count; ++I) {
1358         if (SB_CompareStr (&SVal, Keys [I]) == 0) {
1359             /* Found it */
1360             return I;
1361         }
1362     }
1363
1364     /* Not found */
1365     return -1;
1366 }
1367
1368
1369
1370 unsigned char ParseAddrSize (void)
1371 /* Check if the next token is a keyword that denotes an address size specifier.
1372  * If so, return the corresponding address size constant, otherwise output an
1373  * error message and return ADDR_SIZE_DEFAULT.
1374  */
1375 {
1376     unsigned char AddrSize;
1377
1378     /* Check for an identifier */
1379     if (Tok != TOK_IDENT) {
1380         Error ("Address size specifier expected");
1381         return ADDR_SIZE_DEFAULT;
1382     }
1383
1384     /* Convert the attribute */
1385     AddrSize = AddrSizeFromStr (SB_GetConstBuf (&SVal));
1386     if (AddrSize == ADDR_SIZE_INVALID) {
1387         Error ("Address size specifier expected");
1388         AddrSize = ADDR_SIZE_DEFAULT;
1389     }
1390
1391     /* Done */
1392     return AddrSize;
1393 }
1394
1395
1396
1397 void InitScanner (const char* InFile)
1398 /* Initialize the scanner, open the given input file */
1399 {
1400     /* Open the input file */
1401     NewInputFile (InFile);
1402 }
1403
1404
1405
1406 void DoneScanner (void)
1407 /* Release scanner resources */
1408 {
1409     DoneCharSource ();
1410 }
1411
1412
1413