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