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