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