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