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