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