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