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