]> git.sur5r.net Git - cc65/blob - src/ca65/scanner.c
Fix style
[cc65] / src / ca65 / scanner.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 scanner.c                                 */
4 /*                                                                           */
5 /*                  The scanner for the ca65 macroassembler                  */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2013, Ullrich von Bassewitz                                      */
10 /*                Roemerstrasse 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
42 /* common */
43 #include "addrsize.h"
44 #include "attrib.h"
45 #include "chartype.h"
46 #include "check.h"
47 #include "filestat.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 /* Current input token incl. attributes */
73 Token CurTok = STATIC_TOKEN_INITIALIZER;
74
75 /* Struct to handle include files. */
76 typedef struct InputFile InputFile;
77 struct InputFile {
78     FILE*           F;                  /* Input file descriptor */
79     FilePos         Pos;                /* Position in file */
80     token_t         Tok;                /* Last token */
81     int             C;                  /* Last character */
82     StrBuf          Line;               /* The current input line */
83     int             IncSearchPath;      /* True if we've added a search path */
84     int             BinSearchPath;      /* True if we've added a search path */
85     InputFile*      Next;               /* Linked list of input files */
86 };
87
88 /* Struct to handle textual input data */
89 typedef struct InputData InputData;
90 struct InputData {
91     char*           Text;               /* Pointer to the text data */
92     const char*     Pos;                /* Pointer to current position */
93     int             Malloced;           /* Memory was malloced */
94     token_t         Tok;                /* Last token */
95     int             C;                  /* Last character */
96     InputData*      Next;               /* Linked list of input data */
97 };
98
99 /* Input source: Either file or data */
100 typedef struct CharSource CharSource;
101
102 /* Set of input functions */
103 typedef struct CharSourceFunctions CharSourceFunctions;
104 struct CharSourceFunctions {
105     void (*MarkStart) (CharSource*);    /* Mark the start pos of a token */
106     void (*NextChar) (CharSource*);     /* Read next char from input */
107     void (*Done) (CharSource*);         /* Close input source */
108 };
109
110 /* Input source: Either file or data */
111 struct CharSource {
112     CharSource*                 Next;   /* Linked list of char sources */
113     token_t                     Tok;    /* Last token */
114     int                         C;      /* Last character */
115     const CharSourceFunctions*  Func;   /* Pointer to function table */
116     union {
117         InputFile               File;   /* File data */
118         InputData               Data;   /* Textual data */
119     }                           V;
120 };
121
122 /* Current input variables */
123 static CharSource* Source       = 0;    /* Current char source */
124 static unsigned    FCount       = 0;    /* Count of input files */
125 static int         C            = 0;    /* Current input character */
126
127 /* Force end of assembly */
128 int               ForcedEnd     = 0;
129
130 /* List of dot keywords with the corresponding tokens */
131 struct DotKeyword {
132     const char* Key;                    /* MUST be first field */
133     token_t     Tok;
134 } DotKeywords [] = {
135     { ".A16",           TOK_A16                 },
136     { ".A8",            TOK_A8                  },
137     { ".ADDR",          TOK_ADDR                },
138     { ".ADDRSIZE",      TOK_ADDRSIZE            },
139     { ".ALIGN",         TOK_ALIGN               },
140     { ".AND",           TOK_BOOLAND             },
141     { ".ASCIIZ",        TOK_ASCIIZ              },
142     { ".ASSERT",        TOK_ASSERT              },
143     { ".AUTOIMPORT",    TOK_AUTOIMPORT          },
144     { ".BANK",          TOK_BANK                },
145     { ".BANKBYTE",      TOK_BANKBYTE            },
146     { ".BANKBYTES",     TOK_BANKBYTES           },
147     { ".BITAND",        TOK_AND                 },
148     { ".BITNOT",        TOK_NOT                 },
149     { ".BITOR",         TOK_OR                  },
150     { ".BITXOR",        TOK_XOR                 },
151     { ".BLANK",         TOK_BLANK               },
152     { ".BSS",           TOK_BSS                 },
153     { ".BYT",           TOK_BYTE                },
154     { ".BYTE",          TOK_BYTE                },
155     { ".CASE",          TOK_CASE                },
156     { ".CHARMAP",       TOK_CHARMAP             },
157     { ".CODE",          TOK_CODE                },
158     { ".CONCAT",        TOK_CONCAT              },
159     { ".CONDES",        TOK_CONDES              },
160     { ".CONST",         TOK_CONST               },
161     { ".CONSTRUCTOR",   TOK_CONSTRUCTOR         },
162     { ".CPU",           TOK_CPU                 },
163     { ".DATA",          TOK_DATA                },
164     { ".DBG",           TOK_DBG                 },
165     { ".DBYT",          TOK_DBYT                },
166     { ".DEBUGINFO",     TOK_DEBUGINFO           },
167     { ".DEF",           TOK_DEFINED             },
168     { ".DEFINE",        TOK_DEFINE              },
169     { ".DEFINED",       TOK_DEFINED             },
170     { ".DEFINEDMACRO",  TOK_DEFINEDMACRO        },
171     { ".DELMAC",        TOK_DELMAC              },
172     { ".DELMACRO",      TOK_DELMAC              },
173     { ".DESTRUCTOR",    TOK_DESTRUCTOR          },
174     { ".DWORD",         TOK_DWORD               },
175     { ".ELSE",          TOK_ELSE                },
176     { ".ELSEIF",        TOK_ELSEIF              },
177     { ".END",           TOK_END                 },
178     { ".ENDENUM",       TOK_ENDENUM             },
179     { ".ENDIF",         TOK_ENDIF               },
180     { ".ENDMAC",        TOK_ENDMACRO            },
181     { ".ENDMACRO",      TOK_ENDMACRO            },
182     { ".ENDPROC",       TOK_ENDPROC             },
183     { ".ENDREP",        TOK_ENDREP              },
184     { ".ENDREPEAT",     TOK_ENDREP              },
185     { ".ENDSCOPE",      TOK_ENDSCOPE            },
186     { ".ENDSTRUCT",     TOK_ENDSTRUCT           },
187     { ".ENDUNION",      TOK_ENDUNION            },
188     { ".ENUM",          TOK_ENUM                },
189     { ".ERROR",         TOK_ERROR               },
190     { ".EXITMAC",       TOK_EXITMACRO           },
191     { ".EXITMACRO",     TOK_EXITMACRO           },
192     { ".EXPORT",        TOK_EXPORT              },
193     { ".EXPORTZP",      TOK_EXPORTZP            },
194     { ".FARADDR",       TOK_FARADDR             },
195     { ".FATAL",         TOK_FATAL               },
196     { ".FEATURE",       TOK_FEATURE             },
197     { ".FILEOPT",       TOK_FILEOPT             },
198     { ".FOPT",          TOK_FILEOPT             },
199     { ".FORCEIMPORT",   TOK_FORCEIMPORT         },
200     { ".FORCEWORD",     TOK_FORCEWORD           },
201     { ".GLOBAL",        TOK_GLOBAL              },
202     { ".GLOBALZP",      TOK_GLOBALZP            },
203     { ".HIBYTE",        TOK_HIBYTE              },
204     { ".HIBYTES",       TOK_HIBYTES             },
205     { ".HIWORD",        TOK_HIWORD              },
206     { ".I16",           TOK_I16                 },
207     { ".I8",            TOK_I8                  },
208     { ".IDENT",         TOK_MAKEIDENT           },
209     { ".IF",            TOK_IF                  },
210     { ".IFBLANK",       TOK_IFBLANK             },
211     { ".IFCONST",       TOK_IFCONST             },
212     { ".IFDEF",         TOK_IFDEF               },
213     { ".IFNBLANK",      TOK_IFNBLANK            },
214     { ".IFNCONST",      TOK_IFNCONST            },
215     { ".IFNDEF",        TOK_IFNDEF              },
216     { ".IFNREF",        TOK_IFNREF              },
217     { ".IFP02",         TOK_IFP02               },
218     { ".IFP816",        TOK_IFP816              },
219     { ".IFPC02",        TOK_IFPC02              },
220     { ".IFPSC02",       TOK_IFPSC02             },
221     { ".IFREF",         TOK_IFREF               },
222     { ".IMPORT",        TOK_IMPORT              },
223     { ".IMPORTZP",      TOK_IMPORTZP            },
224     { ".INCBIN",        TOK_INCBIN              },
225     { ".INCLUDE",       TOK_INCLUDE             },
226     { ".INTERRUPTOR",   TOK_INTERRUPTOR         },
227     { ".ISMNEM",        TOK_ISMNEMONIC          },
228     { ".ISMNEMONIC",    TOK_ISMNEMONIC          },
229     { ".LEFT",          TOK_LEFT                },
230     { ".LINECONT",      TOK_LINECONT            },
231     { ".LIST",          TOK_LIST                },
232     { ".LISTBYTES",     TOK_LISTBYTES           },
233     { ".LOBYTE",        TOK_LOBYTE              },
234     { ".LOBYTES",       TOK_LOBYTES             },
235     { ".LOCAL",         TOK_LOCAL               },
236     { ".LOCALCHAR",     TOK_LOCALCHAR           },
237     { ".LOWORD",        TOK_LOWORD              },
238     { ".MAC",           TOK_MACRO               },
239     { ".MACPACK",       TOK_MACPACK             },
240     { ".MACRO",         TOK_MACRO               },
241     { ".MATCH",         TOK_MATCH               },
242     { ".MAX",           TOK_MAX                 },
243     { ".MID",           TOK_MID                 },
244     { ".MIN",           TOK_MIN                 },
245     { ".MOD",           TOK_MOD                 },
246     { ".NOT",           TOK_BOOLNOT             },
247     { ".NULL",          TOK_NULL                },
248     { ".OR",            TOK_BOOLOR              },
249     { ".ORG",           TOK_ORG                 },
250     { ".OUT",           TOK_OUT                 },
251     { ".P02",           TOK_P02                 },
252     { ".P816",          TOK_P816                },
253     { ".PAGELEN",       TOK_PAGELENGTH          },
254     { ".PAGELENGTH",    TOK_PAGELENGTH          },
255     { ".PARAMCOUNT",    TOK_PARAMCOUNT          },
256     { ".PC02",          TOK_PC02                },
257     { ".POPCPU",        TOK_POPCPU              },
258     { ".POPSEG",        TOK_POPSEG              },
259     { ".PROC",          TOK_PROC                },
260     { ".PSC02",         TOK_PSC02               },
261     { ".PUSHCPU",       TOK_PUSHCPU             },
262     { ".PUSHSEG",       TOK_PUSHSEG             },
263     { ".REF",           TOK_REFERENCED          },
264     { ".REFERENCED",    TOK_REFERENCED          },
265     { ".RELOC",         TOK_RELOC               },
266     { ".REPEAT",        TOK_REPEAT              },
267     { ".RES",           TOK_RES                 },
268     { ".RIGHT",         TOK_RIGHT               },
269     { ".RODATA",        TOK_RODATA              },
270     { ".SCOPE",         TOK_SCOPE               },
271     { ".SEGMENT",       TOK_SEGMENT             },
272     { ".SET",           TOK_SET                 },
273     { ".SETCPU",        TOK_SETCPU              },
274     { ".SHL",           TOK_SHL                 },
275     { ".SHR",           TOK_SHR                 },
276     { ".SIZEOF",        TOK_SIZEOF              },
277     { ".SMART",         TOK_SMART               },
278     { ".SPRINTF",       TOK_SPRINTF             },
279     { ".STRAT",         TOK_STRAT               },
280     { ".STRING",        TOK_STRING              },
281     { ".STRLEN",        TOK_STRLEN              },
282     { ".STRUCT",        TOK_STRUCT              },
283     { ".TAG",           TOK_TAG                 },
284     { ".TCOUNT",        TOK_TCOUNT              },
285     { ".TIME",          TOK_TIME                },
286     { ".UNDEF",         TOK_UNDEF               },
287     { ".UNDEFINE",      TOK_UNDEF               },
288     { ".UNION",         TOK_UNION               },
289     { ".VERSION",       TOK_VERSION             },
290     { ".WARNING",       TOK_WARNING             },
291     { ".WORD",          TOK_WORD                },
292     { ".XMATCH",        TOK_XMATCH              },
293     { ".XOR",           TOK_BOOLXOR             },
294     { ".ZEROPAGE",      TOK_ZEROPAGE            },
295 };
296
297
298
299 /*****************************************************************************/
300 /*                            CharSource functions                           */
301 /*****************************************************************************/
302
303
304
305 static void UseCharSource (CharSource* S)
306 /* Initialize a new input source and start to use it. */
307 {
308     /* Remember the current input char and token */
309     S->Tok      = CurTok.Tok;
310     S->C        = C;
311
312     /* Use the new input source */
313     S->Next     = Source;
314     Source      = S;
315
316     /* Read the first character from the new file */
317     S->Func->NextChar (S);
318
319     /* Setup the next token so it will be skipped on the next call to
320     ** NextRawTok().
321     */
322     CurTok.Tok = TOK_SEP;
323 }
324
325
326
327 static void DoneCharSource (void)
328 /* Close the top level character source */
329 {
330     CharSource* S;
331
332     /* First, call the type specific function */
333     Source->Func->Done (Source);
334
335     /* Restore the old token */
336     CurTok.Tok = Source->Tok;
337     C   = Source->C;
338
339     /* Remember the last stacked input source */
340     S = Source->Next;
341
342     /* Delete the top level one ... */
343     xfree (Source);
344
345     /* ... and use the one before */
346     Source = S;
347 }
348
349
350
351 /*****************************************************************************/
352 /*                            InputFile functions                            */
353 /*****************************************************************************/
354
355
356
357 static void IFMarkStart (CharSource* S)
358 /* Mark the start of the next token */
359 {
360     CurTok.Pos = S->V.File.Pos;
361 }
362
363
364
365 static void IFNextChar (CharSource* S)
366 /* Read the next character from the input file */
367 {
368     /* Check for end of line, read the next line if needed */
369     while (SB_GetIndex (&S->V.File.Line) >= SB_GetLen (&S->V.File.Line)) {
370
371         unsigned Len;
372
373         /* End of current line reached, read next line */
374         SB_Clear (&S->V.File.Line);
375         while (1) {
376
377             int N = fgetc (S->V.File.F);
378             if (N == EOF) {
379                 /* End of file. Accept files without a newline at the end */
380                 if (SB_NotEmpty (&S->V.File.Line)) {
381                     break;
382                 }
383
384                 /* No more data - add an empty line to the listing. This
385                 ** is a small hack needed to keep the PC output in sync.
386                 */
387                 NewListingLine (&EmptyStrBuf, S->V.File.Pos.Name, FCount);
388                 C = EOF;
389                 return;
390
391             /* Check for end of line */
392             } else if (N == '\n') {
393
394                 /* End of line */
395                 break;
396
397             /* Collect other stuff */
398             } else {
399
400                 /* Append data to line */
401                 SB_AppendChar (&S->V.File.Line, N);
402
403             }
404         }
405
406
407         /* If we come here, we have a new input line. To avoid problems
408         ** with strange line terminators, remove all whitespace from the
409         ** end of the line, the add a single newline.
410         */
411         Len = SB_GetLen (&S->V.File.Line);
412         while (Len > 0 && IsSpace (SB_AtUnchecked (&S->V.File.Line, Len-1))) {
413             --Len;
414         }
415         SB_Drop (&S->V.File.Line, SB_GetLen (&S->V.File.Line) - Len);
416         SB_AppendChar (&S->V.File.Line, '\n');
417
418         /* Terminate the string buffer */
419         SB_Terminate (&S->V.File.Line);
420
421         /* One more line */
422         S->V.File.Pos.Line++;
423
424         /* Remember the new line for the listing */
425         NewListingLine (&S->V.File.Line, S->V.File.Pos.Name, FCount);
426
427     }
428
429     /* Set the column pointer */
430     S->V.File.Pos.Col = SB_GetIndex (&S->V.File.Line);
431
432     /* Return the next character from the buffer */
433     C = SB_Get (&S->V.File.Line);
434 }
435
436
437
438 void IFDone (CharSource* S)
439 /* Close the current input file */
440 {
441     /* We're at the end of an include file. Check if we have any
442     ** open .IFs, or any open token lists in this file. This
443     ** enforcement is artificial, using conditionals that start
444     ** in one file and end in another are uncommon, and don't
445     ** allowing these things will help finding errors.
446     */
447     CheckOpenIfs ();
448
449     /* If we've added search paths for this file, remove them */
450     if (S->V.File.IncSearchPath) {
451         PopSearchPath (IncSearchPath);
452     }
453     if (S->V.File.BinSearchPath) {
454         PopSearchPath (BinSearchPath);
455     }
456
457     /* Free the line buffer */
458     SB_Done (&S->V.File.Line);
459
460     /* Close the input file and decrement the file count. We will ignore
461     ** errors here, since we were just reading from the file.
462     */
463     (void) fclose (S->V.File.F);
464     --FCount;
465 }
466
467
468
469 /* Set of input file handling functions */
470 static const CharSourceFunctions IFFunc = {
471     IFMarkStart,
472     IFNextChar,
473     IFDone
474 };
475
476
477
478 int NewInputFile (const char* Name)
479 /* Open a new input file. Returns true if the file could be successfully opened
480 ** and false otherwise.
481 */
482 {
483     int         RetCode = 0;            /* Return code. Assume an error. */
484     char*       PathName = 0;
485     FILE*       F;
486     struct stat Buf;
487     StrBuf      NameBuf;                /* No need to initialize */
488     StrBuf      Path = AUTO_STRBUF_INITIALIZER;
489     unsigned    FileIdx;
490     CharSource* S;
491
492
493     /* If this is the main file, just try to open it. If it's an include file,
494     ** search for it using the include path list.
495     */
496     if (FCount == 0) {
497         /* Main file */
498         F = fopen (Name, "r");
499         if (F == 0) {
500             Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
501         }
502     } else {
503         /* We are on include level. Search for the file in the include
504         ** directories.
505         */
506         PathName = SearchFile (IncSearchPath, Name);
507         if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
508             /* Not found or cannot open, print an error and bail out */
509             Error ("Cannot open include file `%s': %s", Name, strerror (errno));
510             goto ExitPoint;
511         }
512
513         /* Use the path name from now on */
514         Name = PathName;
515     }
516
517     /* Stat the file and remember the values. There's a race condition here,
518     ** since we cannot use fileno() (non-standard identifier in standard
519     ** header file), and therefore not fstat. When using stat with the
520     ** file name, there's a risk that the file was deleted and recreated
521     ** while it was open. Since mtime and size are only used to check
522     ** if a file has changed in the debugger, we will ignore this problem
523     ** here.
524     */
525     if (FileStat (Name, &Buf) != 0) {
526         Fatal ("Cannot stat input file `%s': %s", Name, strerror (errno));
527     }
528
529     /* Add the file to the input file table and remember the index */
530     FileIdx = AddFile (SB_InitFromString (&NameBuf, Name),
531                        (FCount == 0)? FT_MAIN : FT_INCLUDE,
532                        Buf.st_size, (unsigned long) Buf.st_mtime);
533
534     /* Create a new input source variable and initialize it */
535     S                   = xmalloc (sizeof (*S));
536     S->Func             = &IFFunc;
537     S->V.File.F         = F;
538     S->V.File.Pos.Line  = 0;
539     S->V.File.Pos.Col   = 0;
540     S->V.File.Pos.Name  = FileIdx;
541     SB_Init (&S->V.File.Line);
542
543     /* Push the path for this file onto the include search lists */
544     SB_CopyBuf (&Path, Name, FindName (Name) - Name);
545     SB_Terminate (&Path);
546     S->V.File.IncSearchPath = PushSearchPath (IncSearchPath, SB_GetConstBuf (&Path));
547     S->V.File.BinSearchPath = PushSearchPath (BinSearchPath, SB_GetConstBuf (&Path));
548     SB_Done (&Path);
549
550     /* Count active input files */
551     ++FCount;
552
553     /* Use this input source */
554     UseCharSource (S);
555
556     /* File successfully opened */
557     RetCode = 1;
558
559 ExitPoint:
560     /* Free an allocated name buffer */
561     xfree (PathName);
562
563     /* Return the success code */
564     return RetCode;
565 }
566
567
568
569 /*****************************************************************************/
570 /*                            InputData functions                            */
571 /*****************************************************************************/
572
573
574
575 static void IDMarkStart (CharSource* S attribute ((unused)))
576 /* Mark the start of the next token */
577 {
578     /* Nothing to do here */
579 }
580
581
582
583 static void IDNextChar (CharSource* S)
584 /* Read the next character from the input text */
585 {
586     C = *S->V.Data.Pos++;
587     if (C == '\0') {
588         /* End of input data */
589         --S->V.Data.Pos;
590         C = EOF;
591     }
592 }
593
594
595
596 void IDDone (CharSource* S)
597 /* Close the current input data */
598 {
599     /* Cleanup the current stuff */
600     if (S->V.Data.Malloced) {
601         xfree (S->V.Data.Text);
602     }
603 }
604
605
606
607 /* Set of input data handling functions */
608 static const CharSourceFunctions IDFunc = {
609     IDMarkStart,
610     IDNextChar,
611     IDDone
612 };
613
614
615
616 void NewInputData (char* Text, int Malloced)
617 /* Add a chunk of input data to the input stream */
618 {
619     CharSource* S;
620
621     /* Create a new input source variable and initialize it */
622     S                   = xmalloc (sizeof (*S));
623     S->Func             = &IDFunc;
624     S->V.Data.Text      = Text;
625     S->V.Data.Pos       = Text;
626     S->V.Data.Malloced  = Malloced;
627
628     /* Use this input source */
629     UseCharSource (S);
630 }
631
632
633
634 /*****************************************************************************/
635 /*                    Character classification functions                     */
636 /*****************************************************************************/
637
638
639
640 int IsIdChar (int C)
641 /* Return true if the character is a valid character for an identifier */
642 {
643     return IsAlNum (C)                  ||
644            (C == '_')                   ||
645            (C == '@' && AtInIdents)     ||
646            (C == '$' && DollarInIdents);
647 }
648
649
650
651 int IsIdStart (int C)
652 /* Return true if the character may start an identifier */
653 {
654     return IsAlpha (C) || C == '_';
655 }
656
657
658
659 /*****************************************************************************/
660 /*                                   Code                                    */
661 /*****************************************************************************/
662
663
664
665 static unsigned DigitVal (unsigned char C)
666 /* Convert a digit into it's numerical representation */
667 {
668     if (IsDigit (C)) {
669         return C - '0';
670     } else {
671         return toupper (C) - 'A' + 10;
672     }
673 }
674
675
676
677 static void NextChar (void)
678 /* Read the next character from the input file */
679 {
680     Source->Func->NextChar (Source);
681 }
682
683
684
685 void LocaseSVal (void)
686 /* Make SVal lower case */
687 {
688     SB_ToLower (&CurTok.SVal);
689 }
690
691
692
693 void UpcaseSVal (void)
694 /* Make SVal upper case */
695 {
696     SB_ToUpper (&CurTok.SVal);
697 }
698
699
700
701 static int CmpDotKeyword (const void* K1, const void* K2)
702 /* Compare function for the dot keyword search */
703 {
704     return strcmp (((struct DotKeyword*)K1)->Key, ((struct DotKeyword*)K2)->Key);
705 }
706
707
708
709 static token_t FindDotKeyword (void)
710 /* Find the dot keyword in SVal. Return the corresponding token if found,
711 ** return TOK_NONE if not found.
712 */
713 {
714     struct DotKeyword K;
715     struct DotKeyword* R;
716
717     /* Initialize K */
718     K.Key = SB_GetConstBuf (&CurTok.SVal);
719     K.Tok = 0;
720
721     /* If we aren't in ignore case mode, we have to uppercase the keyword */
722     if (!IgnoreCase) {
723         UpcaseSVal ();
724     }
725
726     /* Search for the keyword */
727     R = bsearch (&K, DotKeywords, sizeof (DotKeywords) / sizeof (DotKeywords [0]),
728                  sizeof (DotKeywords [0]), CmpDotKeyword);
729     if (R != 0) {
730
731         /* By default, disable any somewhat experiemental DotKeyword. */
732
733         switch (R->Tok) {
734
735             case TOK_ADDRSIZE:
736                 /* Disallow .ADDRSIZE function by default */
737                 if (AddrSize == 0) {
738                     return TOK_NONE;
739                 }
740                 break;
741
742             default:
743                 break;
744         }
745
746         return R->Tok;
747
748     } else {
749         return TOK_NONE;
750     }
751 }
752
753
754
755 static void ReadIdent (void)
756 /* Read an identifier from the current input position into Ident. Filling SVal
757 ** starts at the current position with the next character in C. It is assumed
758 ** that any characters already filled in are ok, and the character in C is
759 ** checked.
760 */
761 {
762     /* Read the identifier */
763     do {
764         SB_AppendChar (&CurTok.SVal, C);
765         NextChar ();
766     } while (IsIdChar (C));
767     SB_Terminate (&CurTok.SVal);
768
769     /* If we should ignore case, convert the identifier to upper case */
770     if (IgnoreCase) {
771         UpcaseSVal ();
772     }
773 }
774
775
776
777 static void ReadStringConst (int StringTerm)
778 /* Read a string constant into SVal. */
779 {
780     /* Skip the leading string terminator */
781     NextChar ();
782
783     /* Read the string */
784     while (1) {
785         if (C == StringTerm) {
786             break;
787         }
788         if (C == '\n' || C == EOF) {
789             Error ("Newline in string constant");
790             break;
791         }
792
793         /* Append the char to the string */
794         SB_AppendChar (&CurTok.SVal, C);
795
796         /* Skip the character */
797         NextChar ();
798     }
799
800     /* Skip the trailing terminator */
801     NextChar ();
802
803     /* Terminate the string */
804     SB_Terminate (&CurTok.SVal);
805 }
806
807
808
809 static int Sweet16Reg (const StrBuf* Id)
810 /* Check if the given identifier is a sweet16 register. Return -1 if this is
811 ** not the case, return the register number otherwise.
812 */
813 {
814     unsigned RegNum;
815     char Check;
816
817     if (SB_GetLen (Id) < 2) {
818         return -1;
819     }
820     if (toupper (SB_AtUnchecked (Id, 0)) != 'R') {
821         return -1;
822     }
823     if (!IsDigit (SB_AtUnchecked (Id, 1))) {
824         return -1;
825     }
826
827     if (sscanf (SB_GetConstBuf (Id)+1, "%u%c", &RegNum, &Check) != 1 || RegNum > 15) {
828         /* Invalid register */
829         return -1;
830     }
831
832     /* The register number is valid */
833     return (int) RegNum;
834 }
835
836
837
838 void NextRawTok (void)
839 /* Read the next raw token from the input stream */
840 {
841     Macro* M;
842
843     /* If we've a forced end of assembly, don't read further */
844     if (ForcedEnd) {
845         CurTok.Tok = TOK_EOF;
846         return;
847     }
848
849 Restart:
850     /* Check if we have tokens from another input source */
851     if (InputFromStack ()) {
852         if (CurTok.Tok == TOK_IDENT && (M = FindDefine (&CurTok.SVal)) != 0) {
853             /* This is a define style macro - expand it */
854             MacExpandStart (M);
855             goto Restart;
856         }
857         return;
858     }
859
860 Again:
861     /* Skip whitespace, remember if we had some */
862     if ((CurTok.WS = IsBlank (C)) != 0) {
863         do {
864             NextChar ();
865         } while (IsBlank (C));
866     }
867
868     /* Mark the file position of the next token */
869     Source->Func->MarkStart (Source);
870
871     /* Clear the string attribute */
872     SB_Clear (&CurTok.SVal);
873
874     /* Generate line info for the current token */
875     NewAsmLine ();
876
877     /* Hex number or PC symbol? */
878     if (C == '$') {
879         NextChar ();
880
881         /* Hex digit must follow or DollarIsPC must be enabled */
882         if (!IsXDigit (C)) {
883             if (DollarIsPC) {
884                 CurTok.Tok = TOK_PC;
885                 return;
886             } else {
887                 Error ("Hexadecimal digit expected");
888             }
889         }
890
891         /* Read the number */
892         CurTok.IVal = 0;
893         while (1) {
894             if (UnderlineInNumbers && C == '_') {
895                 while (C == '_') {
896                     NextChar ();
897                 }
898                 if (!IsXDigit (C)) {
899                     Error ("Number may not end with underline");
900                 }
901             }
902             if (IsXDigit (C)) {
903                 if (CurTok.IVal & 0xF0000000) {
904                     Error ("Overflow in hexadecimal number");
905                     CurTok.IVal = 0;
906                 }
907                 CurTok.IVal = (CurTok.IVal << 4) + DigitVal (C);
908                 NextChar ();
909             } else {
910                 break;
911             }
912         }
913
914         /* This is an integer constant */
915         CurTok.Tok = TOK_INTCON;
916         return;
917     }
918
919     /* Binary number? */
920     if (C == '%') {
921         NextChar ();
922
923         /* 0 or 1 must follow */
924         if (!IsBDigit (C)) {
925             Error ("Binary digit expected");
926         }
927
928         /* Read the number */
929         CurTok.IVal = 0;
930         while (1) {
931             if (UnderlineInNumbers && C == '_') {
932                 while (C == '_') {
933                     NextChar ();
934                 }
935                 if (!IsBDigit (C)) {
936                     Error ("Number may not end with underline");
937                 }
938             }
939             if (IsBDigit (C)) {
940                 if (CurTok.IVal & 0x80000000) {
941                     Error ("Overflow in binary number");
942                     CurTok.IVal = 0;
943                 }
944                 CurTok.IVal = (CurTok.IVal << 1) + DigitVal (C);
945                 NextChar ();
946             } else {
947                 break;
948             }
949         }
950
951         /* This is an integer constant */
952         CurTok.Tok = TOK_INTCON;
953         return;
954     }
955
956     /* Number? */
957     if (IsDigit (C)) {
958
959         char Buf[16];
960         unsigned Digits;
961         unsigned Base;
962         unsigned I;
963         long     Max;
964         unsigned DVal;
965
966         /* Ignore leading zeros */
967         while (C == '0') {
968             NextChar ();
969         }
970
971         /* Read the number into Buf counting the digits */
972         Digits = 0;
973         while (1) {
974             if (UnderlineInNumbers && C == '_') {
975                 while (C == '_') {
976                     NextChar ();
977                 }
978                 if (!IsXDigit (C)) {
979                     Error ("Number may not end with underline");
980                 }
981             }
982             if (IsXDigit (C)) {
983                 /* Buf is big enough to allow any decimal and hex number to
984                 ** overflow, so ignore excess digits here, they will be detected
985                 ** when we convert the value.
986                 */
987                 if (Digits < sizeof (Buf)) {
988                     Buf[Digits++] = C;
989                 }
990                 NextChar ();
991             } else {
992                 break;
993             }
994         }
995
996         /* Allow zilog/intel style hex numbers with a 'h' suffix */
997         if (C == 'h' || C == 'H') {
998             NextChar ();
999             Base = 16;
1000             Max  = 0xFFFFFFFFUL / 16;
1001         } else {
1002             Base = 10;
1003             Max  = 0xFFFFFFFFUL / 10;
1004         }
1005
1006         /* Convert the number using the given base */
1007         CurTok.IVal = 0;
1008         for (I = 0; I < Digits; ++I) {
1009             if (CurTok.IVal > Max) {
1010                 Error ("Number out of range");
1011                 CurTok.IVal = 0;
1012                 break;
1013             }
1014             DVal = DigitVal (Buf[I]);
1015             if (DVal >= Base) {
1016                 Error ("Invalid digits in number");
1017                 CurTok.IVal = 0;
1018                 break;
1019             }
1020             CurTok.IVal = (CurTok.IVal * Base) + DVal;
1021         }
1022
1023         /* This is an integer constant */
1024         CurTok.Tok = TOK_INTCON;
1025         return;
1026     }
1027
1028     /* Control command? */
1029     if (C == '.') {
1030
1031         /* Remember and skip the dot */
1032         NextChar ();
1033
1034         /* Check if it's just a dot */
1035         if (!IsIdStart (C)) {
1036
1037             /* Just a dot */
1038             CurTok.Tok = TOK_DOT;
1039
1040         } else {
1041
1042             /* Read the remainder of the identifier */
1043             SB_AppendChar (&CurTok.SVal, '.');
1044             ReadIdent ();
1045
1046             /* Dot keyword, search for it */
1047             CurTok.Tok = FindDotKeyword ();
1048             if (CurTok.Tok == TOK_NONE) {
1049
1050                 /* Not found */
1051                 if (!LeadingDotInIdents) {
1052                     /* Invalid pseudo instruction */
1053                     Error ("`%m%p' is not a recognized control command", &CurTok.SVal);
1054                     goto Again;
1055                 }
1056
1057                 /* An identifier with a dot. Check if it's a define style
1058                 ** macro.
1059                 */
1060                 if ((M = FindDefine (&CurTok.SVal)) != 0) {
1061                     /* This is a define style macro - expand it */
1062                     MacExpandStart (M);
1063                     goto Restart;
1064                 }
1065
1066                 /* Just an identifier with a dot */
1067                 CurTok.Tok = TOK_IDENT;
1068             }
1069
1070         }
1071         return;
1072     }
1073
1074     /* Indirect op for sweet16 cpu. Must check this before checking for local
1075     ** symbols, because these may also use the '@' symbol.
1076     */
1077     if (CPU == CPU_SWEET16 && C == '@') {
1078         NextChar ();
1079         CurTok.Tok = TOK_AT;
1080         return;
1081     }
1082
1083     /* Local symbol? */
1084     if (C == LocalStart) {
1085
1086         /* Read the identifier. */
1087         ReadIdent ();
1088
1089         /* Start character alone is not enough */
1090         if (SB_GetLen (&CurTok.SVal) == 1) {
1091             Error ("Invalid cheap local symbol");
1092             goto Again;
1093         }
1094
1095         /* A local identifier */
1096         CurTok.Tok = TOK_LOCAL_IDENT;
1097         return;
1098     }
1099
1100
1101     /* Identifier or keyword? */
1102     if (IsIdStart (C)) {
1103
1104         /* Read the identifier */
1105         ReadIdent ();
1106
1107         /* Check for special names. Bail out if we have identified the type of
1108         ** the token. Go on if the token is an identifier.
1109         */
1110         if (SB_GetLen (&CurTok.SVal) == 1) {
1111             switch (toupper (SB_AtUnchecked (&CurTok.SVal, 0))) {
1112
1113                 case 'A':
1114                     if (C == ':') {
1115                         NextChar ();
1116                         CurTok.Tok = TOK_OVERRIDE_ABS;
1117                     } else {
1118                         CurTok.Tok = TOK_A;
1119                     }
1120                     return;
1121
1122                 case 'F':
1123                     if (C == ':') {
1124                         NextChar ();
1125                         CurTok.Tok = TOK_OVERRIDE_FAR;
1126                         return;
1127                     }
1128                     break;
1129
1130                 case 'S':
1131                     if (CPU == CPU_65816) {
1132                         CurTok.Tok = TOK_S;
1133                         return;
1134                     }
1135                     break;
1136
1137                 case 'X':
1138                     CurTok.Tok = TOK_X;
1139                     return;
1140
1141                 case 'Y':
1142                     CurTok.Tok = TOK_Y;
1143                     return;
1144
1145                 case 'Z':
1146                     if (C == ':') {
1147                         NextChar ();
1148                         CurTok.Tok = TOK_OVERRIDE_ZP;
1149                         return;
1150                     }
1151                     break;
1152
1153                 default:
1154                     break;
1155             }
1156
1157         } else if (CPU == CPU_SWEET16 &&
1158                   (CurTok.IVal = Sweet16Reg (&CurTok.SVal)) >= 0) {
1159
1160             /* A sweet16 register number in sweet16 mode */
1161             CurTok.Tok = TOK_REG;
1162             return;
1163
1164         }
1165
1166         /* Check for define style macro */
1167         if ((M = FindDefine (&CurTok.SVal)) != 0) {
1168             /* Macro - expand it */
1169             MacExpandStart (M);
1170             goto Restart;
1171         } else {
1172             /* An identifier */
1173             CurTok.Tok = TOK_IDENT;
1174         }
1175         return;
1176     }
1177
1178     /* Ok, let's do the switch */
1179 CharAgain:
1180     switch (C) {
1181
1182         case '+':
1183             NextChar ();
1184             CurTok.Tok = TOK_PLUS;
1185             return;
1186
1187         case '-':
1188             NextChar ();
1189             CurTok.Tok = TOK_MINUS;
1190             return;
1191
1192         case '/':
1193             NextChar ();
1194             if (C != '*') {
1195                 CurTok.Tok = TOK_DIV;
1196             } else if (CComments) {
1197                 /* Remember the position, then skip the '*' */
1198                 Collection LineInfos = STATIC_COLLECTION_INITIALIZER;
1199                 GetFullLineInfo (&LineInfos);
1200                 NextChar ();
1201                 do {
1202                     while (C !=  '*') {
1203                         if (C == EOF) {
1204                             LIError (&LineInfos, "Unterminated comment");
1205                             ReleaseFullLineInfo (&LineInfos);
1206                             DoneCollection (&LineInfos);
1207                             goto CharAgain;
1208                         }
1209                         NextChar ();
1210                     }
1211                     NextChar ();
1212                 } while (C != '/');
1213                 NextChar ();
1214                 ReleaseFullLineInfo (&LineInfos);
1215                 DoneCollection (&LineInfos);
1216                 goto Again;
1217             }
1218             return;
1219
1220         case '*':
1221             NextChar ();
1222             CurTok.Tok = TOK_MUL;
1223             return;
1224
1225         case '^':
1226             NextChar ();
1227             CurTok.Tok = TOK_XOR;
1228             return;
1229
1230         case '&':
1231             NextChar ();
1232             if (C == '&') {
1233                 NextChar ();
1234                 CurTok.Tok = TOK_BOOLAND;
1235             } else {
1236                 CurTok.Tok = TOK_AND;
1237             }
1238             return;
1239
1240         case '|':
1241             NextChar ();
1242             if (C == '|') {
1243                 NextChar ();
1244                 CurTok.Tok = TOK_BOOLOR;
1245             } else {
1246                 CurTok.Tok = TOK_OR;
1247             }
1248             return;
1249
1250         case ':':
1251             NextChar ();
1252             switch (C) {
1253
1254                 case ':':
1255                     NextChar ();
1256                     CurTok.Tok = TOK_NAMESPACE;
1257                     break;
1258
1259                 case '-':
1260                     CurTok.IVal = 0;
1261                     do {
1262                         --CurTok.IVal;
1263                         NextChar ();
1264                     } while (C == '-');
1265                     CurTok.Tok = TOK_ULABEL;
1266                     break;
1267
1268                 case '+':
1269                     CurTok.IVal = 0;
1270                     do {
1271                         ++CurTok.IVal;
1272                         NextChar ();
1273                     } while (C == '+');
1274                     CurTok.Tok = TOK_ULABEL;
1275                     break;
1276
1277                 case '=':
1278                     NextChar ();
1279                     CurTok.Tok = TOK_ASSIGN;
1280                     break;
1281
1282                 default:
1283                     CurTok.Tok = TOK_COLON;
1284                     break;
1285             }
1286             return;
1287
1288         case ',':
1289             NextChar ();
1290             CurTok.Tok = TOK_COMMA;
1291             return;
1292
1293         case ';':
1294             NextChar ();
1295             while (C != '\n' && C != EOF) {
1296                 NextChar ();
1297             }
1298             goto CharAgain;
1299
1300         case '#':
1301             NextChar ();
1302             CurTok.Tok = TOK_HASH;
1303             return;
1304
1305         case '(':
1306             NextChar ();
1307             CurTok.Tok = TOK_LPAREN;
1308             return;
1309
1310         case ')':
1311             NextChar ();
1312             CurTok.Tok = TOK_RPAREN;
1313             return;
1314
1315         case '[':
1316             NextChar ();
1317             CurTok.Tok = TOK_LBRACK;
1318             return;
1319
1320         case ']':
1321             NextChar ();
1322             CurTok.Tok = TOK_RBRACK;
1323             return;
1324
1325         case '{':
1326             NextChar ();
1327             CurTok.Tok = TOK_LCURLY;
1328             return;
1329
1330         case '}':
1331             NextChar ();
1332             CurTok.Tok = TOK_RCURLY;
1333             return;
1334
1335         case '<':
1336             NextChar ();
1337             if (C == '=') {
1338                 NextChar ();
1339                 CurTok.Tok = TOK_LE;
1340             } else if (C == '<') {
1341                 NextChar ();
1342                 CurTok.Tok = TOK_SHL;
1343             } else if (C == '>') {
1344                 NextChar ();
1345                 CurTok.Tok = TOK_NE;
1346             } else {
1347                 CurTok.Tok = TOK_LT;
1348             }
1349             return;
1350
1351         case '=':
1352             NextChar ();
1353             CurTok.Tok = TOK_EQ;
1354             return;
1355
1356         case '!':
1357             NextChar ();
1358             CurTok.Tok = TOK_BOOLNOT;
1359             return;
1360
1361         case '>':
1362             NextChar ();
1363             if (C == '=') {
1364                 NextChar ();
1365                 CurTok.Tok = TOK_GE;
1366             } else if (C == '>') {
1367                 NextChar ();
1368                 CurTok.Tok = TOK_SHR;
1369             } else {
1370                 CurTok.Tok = TOK_GT;
1371             }
1372             return;
1373
1374         case '~':
1375             NextChar ();
1376             CurTok.Tok = TOK_NOT;
1377             return;
1378
1379         case '\'':
1380             /* Hack: If we allow ' as terminating character for strings, read
1381             ** the following stuff as a string, and check for a one character
1382             ** string later.
1383             */
1384             if (LooseStringTerm) {
1385                 ReadStringConst ('\'');
1386                 if (SB_GetLen (&CurTok.SVal) == 1) {
1387                     CurTok.IVal = SB_AtUnchecked (&CurTok.SVal, 0);
1388                     CurTok.Tok = TOK_CHARCON;
1389                 } else {
1390                     CurTok.Tok = TOK_STRCON;
1391                 }
1392             } else {
1393                 /* Always a character constant */
1394                 NextChar ();
1395                 if (C == EOF || IsControl (C)) {
1396                     Error ("Illegal character constant");
1397                     goto CharAgain;
1398                 }
1399                 CurTok.IVal = C;
1400                 CurTok.Tok = TOK_CHARCON;
1401                 NextChar ();
1402                 if (C != '\'') {
1403                     if (!MissingCharTerm) {
1404                         Error ("Illegal character constant");
1405                     }
1406                 } else {
1407                     NextChar ();
1408                 }
1409             }
1410             return;
1411
1412         case '\"':
1413             ReadStringConst ('\"');
1414             CurTok.Tok = TOK_STRCON;
1415             return;
1416
1417         case '\\':
1418             /* Line continuation? */
1419             if (LineCont) {
1420                 NextChar ();
1421                 /* Next char should be a LF, if not, will result in an error later */
1422                 if (C == '\n') {
1423                     /* Ignore the '\n' */
1424                     NextChar ();
1425                     goto Again;
1426                 }
1427                 else {
1428                     /* Make it clear what the problem is: */
1429                     Error ("EOL expected.");
1430                 }
1431             }
1432             break;
1433
1434         case '\n':
1435             NextChar ();
1436             CurTok.Tok = TOK_SEP;
1437             return;
1438
1439         case EOF:
1440             CheckInputStack ();
1441             /* In case of the main file, do not close it, but return EOF. */
1442             if (Source && Source->Next) {
1443                 DoneCharSource ();
1444                 goto Again;
1445             } else {
1446                 CurTok.Tok = TOK_EOF;
1447             }
1448             return;
1449     }
1450
1451     /* If we go here, we could not identify the current character. Skip it
1452     ** and try again.
1453     */
1454     Error ("Invalid input character: 0x%02X", C & 0xFF);
1455     NextChar ();
1456     goto Again;
1457 }
1458
1459
1460
1461 int GetSubKey (const char** Keys, unsigned Count)
1462 /* Search for a subkey in a table of keywords. The current token must be an
1463 ** identifier and all keys must be in upper case. The identifier will be
1464 ** uppercased in the process. The function returns the index of the keyword,
1465 ** or -1 if the keyword was not found.
1466 */
1467 {
1468     unsigned I;
1469
1470     /* Must have an identifier */
1471     PRECONDITION (CurTok.Tok == TOK_IDENT);
1472
1473     /* If we aren't in ignore case mode, we have to uppercase the identifier */
1474     if (!IgnoreCase) {
1475         UpcaseSVal ();
1476     }
1477
1478     /* Do a linear search (a binary search is not worth the effort) */
1479     for (I = 0; I < Count; ++I) {
1480         if (SB_CompareStr (&CurTok.SVal, Keys [I]) == 0) {
1481             /* Found it */
1482             return I;
1483         }
1484     }
1485
1486     /* Not found */
1487     return -1;
1488 }
1489
1490
1491
1492 unsigned char ParseAddrSize (void)
1493 /* Check if the next token is a keyword that denotes an address size specifier.
1494 ** If so, return the corresponding address size constant, otherwise output an
1495 ** error message and return ADDR_SIZE_DEFAULT.
1496 */
1497 {
1498     unsigned char AddrSize;
1499
1500     /* Check for an identifier */
1501     if (CurTok.Tok != TOK_IDENT) {
1502         Error ("Address size specifier expected");
1503         return ADDR_SIZE_DEFAULT;
1504     }
1505
1506     /* Convert the attribute */
1507     AddrSize = AddrSizeFromStr (SB_GetConstBuf (&CurTok.SVal));
1508     if (AddrSize == ADDR_SIZE_INVALID) {
1509         Error ("Address size specifier expected");
1510         AddrSize = ADDR_SIZE_DEFAULT;
1511     }
1512
1513     /* Done */
1514     return AddrSize;
1515 }
1516
1517
1518
1519 void InitScanner (const char* InFile)
1520 /* Initialize the scanner, open the given input file */
1521 {
1522     /* Open the input file */
1523     NewInputFile (InFile);
1524 }
1525
1526
1527
1528 void DoneScanner (void)
1529 /* Release scanner resources */
1530 {
1531     DoneCharSource ();
1532 }