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