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