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