]> git.sur5r.net Git - cc65/blobdiff - src/ca65/scanner.c
Added builtin .min() and .max() pseudo functions to the assembler.
[cc65] / src / ca65 / scanner.c
index 30ba6db7c914d5850b2b0987d7254bae7396afe2..7edee8dfbc8979cc245dd1f6b9380e3e8d22a9c3 100644 (file)
@@ -6,10 +6,10 @@
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 1998-2000 Ullrich von Bassewitz                                       */
-/*               Wacholderweg 14                                             */
-/*               D-70597 Stuttgart                                           */
-/* EMail:        uz@musoftware.de                                            */
+/* (C) 1998-2010, Ullrich von Bassewitz                                      */
+/*                Roemerstrasse 52                                           */
+/*                D-70794 Filderstadt                                        */
+/* EMail:         uz@cc65.org                                                */
 /*                                                                           */
 /*                                                                           */
 /* This software is provided 'as-is', without any expressed or implied       */
 #include <string.h>
 #include <ctype.h>
 #include <errno.h>
+#include <sys/types.h>         /* EMX needs this */
 #include <sys/stat.h>
 
+/* common */
+#include "addrsize.h"
+#include "attrib.h"
+#include "chartype.h"
+#include "check.h"
+#include "fname.h"
+#include "xmalloc.h"
+
+/* ca65 */
 #include "condasm.h"
 #include "error.h"
-#include "fname.h"
+#include "filetab.h"
 #include "global.h"
 #include "incpath.h"
 #include "instr.h"
 #include "istack.h"
 #include "listing.h"
 #include "macro.h"
-#include "mem.h"
-#include "objfile.h"
 #include "toklist.h"
 #include "scanner.h"
 
 
 
 
-enum Token Tok = TOK_NONE;             /* Current token */
-int WS;                                        /* Flag: Whitespace before token */
+Token Tok = TOK_NONE;                   /* Current token */
+int WS;                                        /* Flag: Whitespace before token */
 long IVal;                             /* Integer token attribute */
-char SVal [MAX_STR_LEN+1];             /* String token attribute */
+StrBuf SVal = STATIC_STRBUF_INITIALIZER;/* String token attribute */
 
 FilePos        CurPos = { 0, 0, 0 };           /* Name and position in current file */
 
 
 
-/* Struct to handle include files. Note: The length of the input line may
- * not exceed 255+1, since the column is stored in the file position struct
- * as a character. Increasing this value means changing the FilePos struct,
- * and the read and write routines in the assembler and linker.
- */
-typedef struct InputFile_ InputFile;
-struct InputFile_ {
+/* Struct to handle include files. */
+typedef struct InputFile InputFile;
+struct InputFile {
     FILE*                  F;                  /* Input file descriptor */
     FilePos        Pos;                /* Position in file */
-    enum Token     Tok;                /* Last token */
+    Token           Tok;               /* Last token */
     int                    C;                  /* Last character */
     char                   Line[256];          /* The current input line */
     InputFile*     Next;               /* Linked list of input files */
 };
 
 /* Struct to handle textual input data */
-typedef struct InputData_ InputData;
-struct InputData_ {
-    const char*            Data;               /* Pointer to the data */
+typedef struct InputData InputData;
+struct InputData {
+    char*                  Text;               /* Pointer to the text data */
     const char*     Pos;               /* Pointer to current position */
     int                    Malloced;           /* Memory was malloced */
-    enum Token     Tok;                /* Last token */
+    Token           Tok;               /* Last token */
     int                    C;                  /* Last character */
     InputData*     Next;               /* Linked list of input data */
 };
 
-/* List of input files */
-static struct {
-    unsigned long  MTime;              /* Time of last modification */
-    unsigned long  Size;               /* Size of file */
-    const char*           Name;                /* Name of file */
-} Files [MAX_INPUT_FILES];
-static unsigned    FileCount = 0;
+/* Input source: Either file or data */
+typedef struct CharSource CharSource;
+
+/* Set of input functions */
+typedef struct CharSourceFunctions CharSourceFunctions;
+struct CharSourceFunctions {
+    void (*MarkStart) (CharSource*);    /* Mark the start pos of a token */
+    void (*NextChar) (CharSource*);     /* Read next char from input */
+    void (*Done) (CharSource*);         /* Close input source */
+};
+
+/* Input source: Either file or data */
+struct CharSource {
+    CharSource*                 Next;   /* Linked list of char sources */
+    Token                       Tok;   /* Last token */
+    int                                C;      /* Last character */
+    const CharSourceFunctions*  Func;   /* Pointer to function table */
+    union {
+        InputFile               File;   /* File data */
+        InputData               Data;   /* Textual data */
+    }                           V;
+};
 
 /* Current input variables */
-static InputFile* IFile                = 0;    /* Current input file */
-static InputData* IData                = 0;    /* Current input memory data */
-static unsigned          ICount        = 0;    /* Count of input files */
-static int       C             = 0;    /* Current input character */
+static CharSource* Source       = 0;    /* Current char source */
+static unsigned            FCount      = 0;    /* Count of input files */
+static int         C           = 0;    /* Current input character */
 
 /* Force end of assembly */
-int              ForcedEnd = 0;
+int              ForcedEnd     = 0;
 
 /* List of dot keywords with the corresponding tokens */
 struct DotKeyword {
     const char*        Key;                    /* MUST be first field */
-    enum Token  Tok;
+    Token       Tok;
 } DotKeywords [] = {
-    { "A16",           TOK_A16         },
-    { "A8",            TOK_A8          },
-    { "ADDR",          TOK_ADDR        },
-    { "ALIGN",         TOK_ALIGN       },
-    { "AND",           TOK_BAND        },
-    { "ASCIIZ",                TOK_ASCIIZ      },
-    { "AUTOIMPORT",    TOK_AUTOIMPORT  },
-    { "BITAND",                TOK_AND         },
-    { "BITNOT",                TOK_NOT         },
-    { "BITOR",         TOK_OR          },
-    { "BITXOR",                TOK_XOR         },
-    { "BLANK",         TOK_BLANK       },
-    { "BSS",           TOK_BSS         },
-    { "BYTE",          TOK_BYTE        },
-    { "CASE",                  TOK_CASE        },
-    { "CODE",          TOK_CODE        },
-    { "CONST",                 TOK_CONST       },
-    { "CPU",           TOK_CPU         },
-    { "DATA",                  TOK_DATA        },
-    { "DBYT",                  TOK_DBYT        },
-    { "DEBUGINFO",     TOK_DEBUGINFO   },
-    { "DEF",                   TOK_DEFINED     },
-    { "DEFINE",                TOK_DEFINE      },
-    { "DEFINED",       TOK_DEFINED     },
-    { "DWORD",                 TOK_DWORD       },
-    { "ELSE",                  TOK_ELSE        },
-    { "ELSEIF",                TOK_ELSEIF      },
-    { "END",                   TOK_END         },
-    { "ENDIF",                 TOK_ENDIF       },
-    { "ENDMAC",                TOK_ENDMACRO    },
-    { "ENDMACRO",      TOK_ENDMACRO    },
-    { "ENDPROC",       TOK_ENDPROC     },
-    { "ENDREP",                TOK_ENDREP      },
-    { "ENDREPEAT",     TOK_ENDREP      },
-    { "ERROR",                 TOK_ERROR       },
-    { "EXITMAC",       TOK_EXITMACRO   },
-    { "EXITMACRO",     TOK_EXITMACRO   },
-    { "EXPORT",                TOK_EXPORT      },
-    { "EXPORTZP",      TOK_EXPORTZP    },
-    { "FARADDR",       TOK_FARADDR     },
-    { "FEATURE",       TOK_FEATURE     },
-    { "FILEOPT",       TOK_FILEOPT     },
-    { "FOPT",                  TOK_FILEOPT     },
-    { "GLOBAL",                TOK_GLOBAL      },
-    { "GLOBALZP",      TOK_GLOBALZP    },
-    { "I16",                   TOK_I16         },
-    { "I8",                    TOK_I8          },
-    { "IF",                    TOK_IF          },
-    { "IFBLANK",       TOK_IFBLANK     },
-    { "IFCONST",       TOK_IFCONST     },
-    { "IFDEF",                 TOK_IFDEF       },
-    { "IFNBLANK",      TOK_IFNBLANK    },
-    { "IFNCONST",      TOK_IFNCONST    },
-    { "IFNDEF",                TOK_IFNDEF      },
-    { "IFNREF",                TOK_IFNREF      },
-    { "IFP02",         TOK_IFP02       },
-    { "IFP816",                TOK_IFP816      },
-    { "IFPC02",                TOK_IFPC02      },
-    { "IFREF",         TOK_IFREF       },
-    { "IMPORT",                TOK_IMPORT      },
-    { "IMPORTZP",      TOK_IMPORTZP    },
-    { "INCBIN",                TOK_INCBIN      },
-    { "INCLUDE",       TOK_INCLUDE     },
-    { "LINECONT",      TOK_LINECONT    },
-    { "LIST",          TOK_LIST        },
-    { "LISTBYTES",     TOK_LISTBYTES   },
-    { "LOCAL",         TOK_LOCAL       },
-    { "LOCALCHAR",     TOK_LOCALCHAR   },
-    { "MAC",           TOK_MACRO       },
-    { "MACPACK",       TOK_MACPACK     },
-    { "MACRO",         TOK_MACRO       },
-    { "MATCH",         TOK_MATCH       },
-    { "MID",                   TOK_MID         },
-    { "MOD",           TOK_MOD         },
-    { "NOT",           TOK_BNOT        },
-    { "NULL",          TOK_NULL        },
-    { "OR",            TOK_BOR         },
-    { "ORG",           TOK_ORG         },
-    { "OUT",           TOK_OUT         },
-    { "P02",           TOK_P02         },
-    { "P816",          TOK_P816        },
-    { "PAGELEN",       TOK_PAGELENGTH  },
-    { "PAGELENGTH",    TOK_PAGELENGTH  },
-    { "PARAMCOUNT",    TOK_PARAMCOUNT  },
-    { "PC02",          TOK_PC02        },
-    { "PROC",          TOK_PROC        },
-    { "REF",           TOK_REFERENCED  },
-    { "REFERENCED",    TOK_REFERENCED  },
-    { "RELOC",         TOK_RELOC       },
-    { "REPEAT",                TOK_REPEAT      },
-    { "RES",           TOK_RES         },
-    { "RODATA",                TOK_RODATA      },
-    { "SEGMENT",       TOK_SEGMENT     },
-    { "SHL",           TOK_SHL         },
-    { "SHR",           TOK_SHR         },
-    { "SMART",         TOK_SMART       },
-    { "STRING",                TOK_STRING      },
-    { "SUNPLUS",       TOK_SUNPLUS     },
-    { "WORD",          TOK_WORD        },
-    { "XMATCH",                TOK_XMATCH      },
-    { "XOR",           TOK_BXOR        },
-    { "ZEROPAGE",      TOK_ZEROPAGE    },
+    { ".A16",                  TOK_A16         },
+    { ".A8",                   TOK_A8          },
+    { ".ADDR",                 TOK_ADDR        },
+    { ".ALIGN",                TOK_ALIGN       },
+    { ".AND",          TOK_BOOLAND     },
+    { ".ASCIIZ",               TOK_ASCIIZ      },
+    { ".ASSERT",        TOK_ASSERT      },
+    { ".AUTOIMPORT",   TOK_AUTOIMPORT  },
+    { ".BANKBYTE",      TOK_BANKBYTE    },
+    { ".BANKBYTES",     TOK_BANKBYTES   },
+    { ".BITAND",       TOK_AND         },
+    { ".BITNOT",       TOK_NOT         },
+    { ".BITOR",                TOK_OR          },
+    { ".BITXOR",       TOK_XOR         },
+    { ".BLANK",                TOK_BLANK       },
+    { ".BSS",                  TOK_BSS         },
+    { ".BYT",          TOK_BYTE        },
+    { ".BYTE",                 TOK_BYTE        },
+    { ".CASE",         TOK_CASE        },
+    { ".CHARMAP",      TOK_CHARMAP     },
+    { ".CODE",                 TOK_CODE        },
+    { ".CONCAT",               TOK_CONCAT      },
+    { ".CONDES",       TOK_CONDES      },
+    { ".CONST",        TOK_CONST       },
+    { ".CONSTRUCTOR",  TOK_CONSTRUCTOR },
+    { ".CPU",          TOK_CPU         },
+    { ".DATA",         TOK_DATA        },
+    { ".DBG",          TOK_DBG         },
+    { ".DBYT",         TOK_DBYT        },
+    { ".DEBUGINFO",    TOK_DEBUGINFO   },
+    { ".DEF",          TOK_DEFINED     },
+    { ".DEFINE",       TOK_DEFINE      },
+    { ".DEFINED",      TOK_DEFINED     },
+    { ".DESTRUCTOR",   TOK_DESTRUCTOR  },
+    { ".DWORD",        TOK_DWORD       },
+    { ".ELSE",         TOK_ELSE        },
+    { ".ELSEIF",       TOK_ELSEIF      },
+    { ".END",          TOK_END         },
+    { ".ENDENUM",       TOK_ENDENUM     },
+    { ".ENDIF",        TOK_ENDIF       },
+    { ".ENDMAC",       TOK_ENDMACRO    },
+    { ".ENDMACRO",     TOK_ENDMACRO    },
+    { ".ENDPROC",      TOK_ENDPROC     },
+    { ".ENDREP",       TOK_ENDREP      },
+    { ".ENDREPEAT",    TOK_ENDREP      },
+    { ".ENDSCOPE",      TOK_ENDSCOPE    },
+    { ".ENDSTRUCT",    TOK_ENDSTRUCT   },
+    { ".ENDUNION",             TOK_ENDUNION    },
+    { ".ENUM",          TOK_ENUM        },
+    { ".ERROR",        TOK_ERROR       },
+    { ".EXITMAC",      TOK_EXITMACRO   },
+    { ".EXITMACRO",    TOK_EXITMACRO   },
+    { ".EXPORT",       TOK_EXPORT      },
+    { ".EXPORTZP",     TOK_EXPORTZP    },
+    { ".FARADDR",      TOK_FARADDR     },
+    { ".FEATURE",      TOK_FEATURE     },
+    { ".FILEOPT",      TOK_FILEOPT     },
+    { ".FOPT",         TOK_FILEOPT     },
+    { ".FORCEIMPORT",   TOK_FORCEIMPORT },
+    { ".FORCEWORD",    TOK_FORCEWORD   },
+    { ".GLOBAL",       TOK_GLOBAL      },
+    { ".GLOBALZP",     TOK_GLOBALZP    },
+    { ".HIBYTE",        TOK_HIBYTE      },
+    { ".HIBYTES",       TOK_HIBYTES     },
+    { ".HIWORD",        TOK_HIWORD      },
+    { ".I16",          TOK_I16         },
+    { ".I8",           TOK_I8          },
+    { ".IDENT",         TOK_MAKEIDENT   },
+    { ".IF",           TOK_IF          },
+    { ".IFBLANK",      TOK_IFBLANK     },
+    { ".IFCONST",      TOK_IFCONST     },
+    { ".IFDEF",        TOK_IFDEF       },
+    { ".IFNBLANK",     TOK_IFNBLANK    },
+    { ".IFNCONST",     TOK_IFNCONST    },
+    { ".IFNDEF",       TOK_IFNDEF      },
+    { ".IFNREF",       TOK_IFNREF      },
+    { ".IFP02",                TOK_IFP02       },
+    { ".IFP816",       TOK_IFP816      },
+    { ".IFPC02",       TOK_IFPC02      },
+    { ".IFPSC02",              TOK_IFPSC02     },
+    { ".IFREF",                TOK_IFREF       },
+    { ".IMPORT",       TOK_IMPORT      },
+    { ".IMPORTZP",     TOK_IMPORTZP    },
+    { ".INCBIN",       TOK_INCBIN      },
+    { ".INCLUDE",      TOK_INCLUDE     },
+    { ".INTERRUPTOR",   TOK_INTERRUPTOR },
+    { ".LEFT",         TOK_LEFT        },
+    { ".LINECONT",     TOK_LINECONT    },
+    { ".LIST",         TOK_LIST        },
+    { ".LISTBYTES",    TOK_LISTBYTES   },
+    { ".LOBYTE",        TOK_LOBYTE      },
+    { ".LOBYTES",       TOK_LOBYTES     },
+    { ".LOCAL",                TOK_LOCAL       },
+    { ".LOCALCHAR",    TOK_LOCALCHAR   },
+    { ".LOWORD",        TOK_LOWORD      },
+    { ".MAC",                  TOK_MACRO       },
+    { ".MACPACK",      TOK_MACPACK     },
+    { ".MACRO",                TOK_MACRO       },
+    { ".MATCH",                TOK_MATCH       },
+    { ".MAX",           TOK_MAX         },
+    { ".MID",          TOK_MID         },
+    { ".MIN",           TOK_MIN         },
+    { ".MOD",          TOK_MOD         },
+    { ".NOT",          TOK_BOOLNOT     },
+    { ".NULL",         TOK_NULL        },
+    { ".OR",           TOK_BOOLOR      },
+    { ".ORG",                  TOK_ORG         },
+    { ".OUT",                  TOK_OUT         },
+    { ".P02",                  TOK_P02         },
+    { ".P816",                 TOK_P816        },
+    { ".PAGELEN",      TOK_PAGELENGTH  },
+    { ".PAGELENGTH",   TOK_PAGELENGTH  },
+    { ".PARAMCOUNT",   TOK_PARAMCOUNT  },
+    { ".PC02",                 TOK_PC02        },
+    { ".POPSEG",       TOK_POPSEG      },
+    { ".PROC",                 TOK_PROC        },
+    { ".PSC02",                TOK_PSC02       },
+    { ".PUSHSEG",              TOK_PUSHSEG     },
+    { ".REF",          TOK_REFERENCED  },
+    { ".REFERENCED",   TOK_REFERENCED  },
+    { ".RELOC",                TOK_RELOC       },
+    { ".REPEAT",       TOK_REPEAT      },
+    { ".RES",                  TOK_RES         },
+    { ".RIGHT",                TOK_RIGHT       },
+    { ".RODATA",       TOK_RODATA      },
+    { ".SCOPE",         TOK_SCOPE       },
+    { ".SEGMENT",      TOK_SEGMENT     },
+    { ".SET",           TOK_SET         },
+    { ".SETCPU",       TOK_SETCPU      },
+    { ".SHL",          TOK_SHL         },
+    { ".SHR",          TOK_SHR         },
+    { ".SIZEOF",        TOK_SIZEOF      },
+    { ".SMART",                TOK_SMART       },
+    { ".SPRINTF",       TOK_SPRINTF     },
+    { ".STRAT",                TOK_STRAT       },
+    { ".STRING",       TOK_STRING      },
+    { ".STRLEN",       TOK_STRLEN      },
+    { ".STRUCT",        TOK_STRUCT      },
+    { ".SUNPLUS",      TOK_SUNPLUS     },
+    { ".TAG",           TOK_TAG         },
+    { ".TCOUNT",       TOK_TCOUNT      },
+    { ".TIME",                 TOK_TIME        },
+    { ".UNION",         TOK_UNION       },
+    { ".VERSION",       TOK_VERSION     },
+    { ".WARNING",      TOK_WARNING     },
+    { ".WORD",                 TOK_WORD        },
+    { ".XMATCH",       TOK_XMATCH      },
+    { ".XOR",                  TOK_BOOLXOR     },
+    { ".ZEROPAGE",     TOK_ZEROPAGE    },
 };
 
 
 
 /*****************************************************************************/
-/*                                Forwards                                  */
+/*                            CharSource functions                           */
 /*****************************************************************************/
 
 
 
-static void NextChar (void);
-/* Read the next character from the input file */
+static void UseCharSource (CharSource* S)
+/* Initialize a new input source and start to use it. */
+{
+    /* Remember the current input char and token */
+    S->Tok      = Tok;
+    S->C        = C;
 
+    /* Use the new input source */
+    S->Next    = Source;
+    Source      = S;
 
+    /* Read the first character from the new file */
+    S->Func->NextChar (S);
 
-/*****************************************************************************/
-/*                   Character classification functions                     */
-/*****************************************************************************/
+    /* Setup the next token so it will be skipped on the next call to
+     * NextRawTok().
+     */
+    Tok = TOK_SEP;
+}
 
 
 
-static int IsBlank (int C)
-/* Return true if the character is a blank or tab */
+static void DoneCharSource (void)
+/* Close the top level character source */
 {
-    return (C == ' ' || C == '\t');
-}
-
+    CharSource* S;
 
+    /* First, call the type specific function */
+    Source->Func->Done (Source);
 
-static int IsDigit (int C)
-/* Return true if the character is a digit */
-{
-    return isdigit (C);
-}
+    /* Restore the old token */
+    Tok = Source->Tok;
+    C   = Source->C;
 
+    /* Remember the last stacked input source */
+    S = Source->Next;
 
+    /* Delete the top level one ... */
+    xfree (Source);
 
-static int IsXDigit (int C)
-/* Return true if the character is a hexadecimal digit */
-{
-    return isxdigit (C);
+    /* ... and use the one before */
+    Source = S;
 }
 
 
 
-static int IsDDigit (int C)
-/* Return true if the character is a dual digit */
-{
-    return (C == '0' || C == '1');
-}
+/*****************************************************************************/
+/*                            InputFile functions                            */
+/*****************************************************************************/
 
 
 
-static int IsIdChar (int C)
-/* Return true if the character is a valid character for an identifier */
+static void IFMarkStart (CharSource* S)
+/* Mark the start of the next token */
 {
-    return isalnum (C)                         ||
-          (C == '_')                   ||
-          (C == '@' && AtInIdents)     ||
-          (C == '$' && DollarInIdents);
+    CurPos = S->V.File.Pos;
 }
 
 
 
-static int IsIdStart (int C)
-/* Return true if the character may start an identifier */
+static void IFNextChar (CharSource* S)
+/* Read the next character from the input file */
 {
-    return isalpha (C) || C == '_';
-}
-
+    /* Check for end of line, read the next line if needed */
+    while (S->V.File.Line [S->V.File.Pos.Col] == '\0') {
+
+        unsigned Len, Removed;
+
+        /* End of current line reached, read next line */
+        if (fgets (S->V.File.Line, sizeof (S->V.File.Line), S->V.File.F) == 0) {
+            /* End of file. Add an empty line to the listing. This is a
+             * small hack needed to keep the PC output in sync.
+             */
+            NewListingLine ("", S->V.File.Pos.Name, FCount);
+            C = EOF;
+            return;
+        }
+
+        /* For better handling of files with unusual line endings (DOS
+         * files that are accidently translated on Unix for example),
+         * first remove all whitespace at the end, then add a single
+         * newline.
+         */
+        Len = strlen (S->V.File.Line);
+        Removed = 0;
+        while (Len > 0 && IsSpace (S->V.File.Line[Len-1])) {
+            ++Removed;
+            --Len;
+        }
+        if (Removed) {
+            S->V.File.Line[Len+0] = '\n';
+            S->V.File.Line[Len+1] = '\0';
+        }
+
+        /* One more line */
+        S->V.File.Pos.Line++;
+        S->V.File.Pos.Col = 0;
+
+        /* Remember the new line for the listing */
+        NewListingLine (S->V.File.Line, S->V.File.Pos.Name, FCount);
 
+    }
 
-/*****************************************************************************/
-/*                                          Code                                    */
-/*****************************************************************************/
+    /* Return the next character from the file */
+    C = S->V.File.Line [S->V.File.Pos.Col++];
+}
 
 
 
-const char* GetFileName (unsigned char Name)
-/* Get the name of a file where the name index is known */
+void IFDone (CharSource* S)
+/* Close the current input file */
 {
-    PRECONDITION (Name <= FileCount);
-    if (Name == 0) {
-       /* Name was defined outside any file scope, use the name of the first
-        * file instead. Errors are then reported with a file position of
-        * line zero in the first file.
-        */
-       if (FileCount == 0) {
-           /* No files defined until now */
-                   return "(outside file scope)";
-       } else {
-           return Files [0].Name;
-       }
-    } else {
-        return Files [Name-1].Name;
-    }
+    /* We're at the end of an include file. Check if we have any
+     * open .IFs, or any open token lists in this file. This
+     * enforcement is artificial, using conditionals that start
+     * in one file and end in another are uncommon, and don't
+     * allowing these things will help finding errors.
+     */
+    CheckOpenIfs ();
+
+    /* Close the input file and decrement the file count. We will ignore
+     * errors here, since we were just reading from the file.
+     */
+    (void) fclose (S->V.File.F);
+    --FCount;
 }
 
 
 
-void NewInputFile (const char* Name)
-/* Open a new input file */
-{
-    InputFile* I;
-    FILE* F;
+/* Set of input file handling functions */
+static const CharSourceFunctions IFFunc = {
+    IFMarkStart,
+    IFNextChar,
+    IFDone
+};
 
-    /* Check for nested include overflow */
-    if (FileCount >= MAX_INPUT_FILES) {
-       Fatal (FAT_MAX_INPUT_FILES);
-    }
+
+
+int NewInputFile (const char* Name)
+/* Open a new input file. Returns true if the file could be successfully opened
+ * and false otherwise.
+ */
+{
+    int RetCode = 0;            /* Return code. Assume an error. */
+    char* PathName = 0;
 
     /* First try to open the file */
-    F = fopen (Name, "r");
+    FILE* F = fopen (Name, "r");
     if (F == 0) {
 
-       char* PathName;
-
        /* Error (fatal error if this is the main file) */
-       if (ICount == 0) {
-           Fatal (FAT_CANNOT_OPEN_INPUT, Name, strerror (errno));
+               if (FCount == 0) {
+           Fatal ("Cannot open input file `%s': %s", Name, strerror (errno));
                }
 
                /* We are on include level. Search for the file in the include
         * directories.
         */
-       PathName = FindInclude (Name);
+       PathName = FindInclude (Name, INC_STD);
                if (PathName == 0 || (F = fopen (PathName, "r")) == 0) {
            /* Not found or cannot open, print an error and bail out */
-           Error (ERR_CANNOT_OPEN_INCLUDE, Name, strerror (errno));
+           Error ("Cannot open include file `%s': %s", Name, strerror (errno));
+            goto ExitPoint;
        }
 
-       /* Free the allocated memory */
-       Xfree (PathName);
-
+               /* Use the path name from now on */
+        Name = PathName;
     }
 
     /* check again if we do now have an open file */
     if (F != 0) {
 
-       /* Stat the file and remember the values */
+        StrBuf          NameBuf;
+       unsigned        FileIdx;
+        CharSource*     S;
+
+       /* Stat the file and remember the values. There a race condition here,
+         * since we cannot use fileno() (non standard identifier in standard
+         * header file), and therefore not fstat. When using stat with the
+         * file name, there's a risk that the file was deleted and recreated
+         * while it was open. Since mtime and size are only used to check
+         * if a file has changed in the debugger, we will ignore this problem
+         * here.
+         */
        struct stat Buf;
-       if (fstat (fileno (F), &Buf) != 0) {
-           Fatal (FAT_CANNOT_STAT_INPUT, Name, strerror (errno));
+       if (stat (Name, &Buf) != 0) {
+           Fatal ("Cannot stat input file `%s': %s", Name, strerror (errno));
        }
-       Files [FileCount].MTime = Buf.st_mtime;
-       Files [FileCount].Size  = Buf.st_size;
-       Files [FileCount].Name  = StrDup (Name);
-       ++FileCount;
-
-       /* Create a new state variable and initialize it */
-       I           = Xmalloc (sizeof (*I));
-       I->F        = F;
-       I->Pos.Line = 0;
-       I->Pos.Col  = 0;
-       I->Pos.Name = FileCount;
-       I->Tok      = Tok;
-       I->C        = C;
-       I->Line[0]  = '\0';
-
-       /* Use the new file */
-       I->Next     = IFile;
-       IFile       = I;
-       ++ICount;
-
-       /* Prime the pump */
-       NextChar ();
+
+       /* Add the file to the input file table and remember the index */
+       FileIdx = AddFile (SB_InitFromString (&NameBuf, Name), Buf.st_size, Buf.st_mtime);
+
+               /* Create a new input source variable and initialize it */
+       S                   = xmalloc (sizeof (*S));
+        S->Func             = &IFFunc;
+       S->V.File.F         = F;
+       S->V.File.Pos.Line  = 0;
+       S->V.File.Pos.Col   = 0;
+       S->V.File.Pos.Name  = FileIdx;
+               S->V.File.Line[0]   = '\0';
+
+        /* Count active input files */
+               ++FCount;
+
+        /* Use this input source */
+        UseCharSource (S);
     }
+
+    /* File successfully opened */
+    RetCode = 1;
+
+ExitPoint:
+    /* Free an allocated name buffer */
+    xfree (PathName);
+
+    /* Return the success code */
+    return RetCode;
 }
 
 
 
-void DoneInputFile (void)
-/* Close the current input file */
+/*****************************************************************************/
+/*                            InputData functions                            */
+/*****************************************************************************/
+
+
+
+static void IDMarkStart (CharSource* S attribute ((unused)))
+/* Mark the start of the next token */
 {
-    InputFile* I;
+    /* Nothing to do here */
+}
 
-    /* Restore the old token */
-    Tok = IFile->Tok;
-    C   = IFile->C;
 
-    /* Save a pointer to the current struct, then set it back */
-    I     = IFile;
-    IFile = I->Next;
 
+static void IDNextChar (CharSource* S)
+/* Read the next character from the input text */
+{
+    C = *S->V.Data.Pos++;
+    if (C == '\0') {
+        /* End of input data */
+        --S->V.Data.Pos;
+        C = EOF;
+    }
+}
+
+
+
+void IDDone (CharSource* S)
+/* Close the current input data */
+{
     /* Cleanup the current stuff */
-    fclose (I->F);
-    Xfree (I);
-    --ICount;
+    if (S->V.Data.Malloced) {
+               xfree (S->V.Data.Text);
+    }
 }
 
 
 
-void NewInputData (const char* Data, int Malloced)
+/* Set of input data handling functions */
+static const CharSourceFunctions IDFunc = {
+    IDMarkStart,
+    IDNextChar,
+    IDDone
+};
+
+
+
+void NewInputData (char* Text, int Malloced)
 /* Add a chunk of input data to the input stream */
 {
-    InputData* I;
-
-    /* Create a new state variable and initialize it */
-    I                  = Xmalloc (sizeof (*I));
-    I->Data    = Data;
-    I->Pos     = Data;
-    I->Malloced = Malloced;
-    I->Tok             = Tok;
-    I->C        = C;
+    CharSource* S;
 
-    /* Use the new data */
-    I->Next    = IData;
-    IData      = I;
+    /* Create a new input source variable and initialize it */
+    S                   = xmalloc (sizeof (*S));
+    S->Func             = &IDFunc;
+    S->V.Data.Text      = Text;
+    S->V.Data.Pos       = Text;
+    S->V.Data.Malloced  = Malloced;
 
-    /* Prime the pump */
-    NextChar ();
+    /* Use this input source */
+    UseCharSource (S);
 }
 
 
 
-static void DoneInputData (void)
-/* End the current input data stream */
+/*****************************************************************************/
+/*                   Character classification functions                     */
+/*****************************************************************************/
+
+
+
+int IsIdChar (int C)
+/* Return true if the character is a valid character for an identifier */
 {
-    InputData* I;
+    return IsAlNum (C)                         ||
+          (C == '_')                   ||
+          (C == '@' && AtInIdents)     ||
+          (C == '$' && DollarInIdents);
+}
 
-    /* Restore the old token */
-    Tok = IData->Tok;
-    C   = IData->C;
 
-    /* Save a pointer to the current struct, then set it back */
-    I     = IData;
-    IData = I->Next;
 
-    /* Cleanup the current stuff */
-    if (I->Malloced) {
-       Xfree (I->Data);
-    }
-    Xfree (I);
+int IsIdStart (int C)
+/* Return true if the character may start an identifier */
+{
+    return IsAlpha (C) || C == '_';
 }
 
 
 
+/*****************************************************************************/
+/*                                          Code                                    */
+/*****************************************************************************/
+
+
+
 static unsigned DigitVal (unsigned char C)
 /* Convert a digit into it's numerical representation */
 {
@@ -473,43 +628,15 @@ static unsigned DigitVal (unsigned char C)
 static void NextChar (void)
 /* Read the next character from the input file */
 {
-    /* If we have an input data structure, read from there */
-    if (IData) {
-
-               C = *IData->Pos++;
-               if (C == '\0') {
-                   /* End of input data, will set to last file char */
-                   DoneInputData ();
-               }
-
-    } else {
-
-       /* Check for end of line, read the next line if needed */
-               while (IFile->Line [IFile->Pos.Col] == '\0') {
-
-           /* End of current line reached, read next line */
-           if (fgets (IFile->Line, sizeof (IFile->Line), IFile->F) == 0) {
-               /* End of file. Add an empty line to the listing. This is a
-                * small hack needed to keep the PC output in sync.
-                */
-               NewListingLine ("", IFile->Pos.Name, ICount);
-               C = EOF;
-               return;
-           }
-
-           /* One more line */
-           IFile->Pos.Line++;
-           IFile->Pos.Col = 0;
-
-           /* Remember the new line for the listing */
-           NewListingLine (IFile->Line, IFile->Pos.Name, ICount);
+    Source->Func->NextChar (Source);
+}
 
-       }
 
-       /* Return the next character from the file */
-       C = IFile->Line [IFile->Pos.Col++];
 
-    }
+void LocaseSVal (void)
+/* Make SVal lower case */
+{
+    SB_ToLower (&SVal);
 }
 
 
@@ -517,11 +644,7 @@ static void NextChar (void)
 void UpcaseSVal (void)
 /* Make SVal upper case */
 {
-    unsigned I = 0;
-    while (SVal [I]) {
-       SVal [I] = toupper (SVal [I]);
-       ++I;
-    }
+    SB_ToUpper (&SVal);
 }
 
 
@@ -539,9 +662,13 @@ static unsigned char FindDotKeyword (void)
  * return TOK_NONE if not found.
  */
 {
-    static const struct DotKeyword K = { SVal, 0 };
+    struct DotKeyword K;
     struct DotKeyword* R;
 
+    /* Initialize K */
+    K.Key = SB_GetConstBuf (&SVal);
+    K.Tok = 0;
+
     /* If we aren't in ignore case mode, we have to uppercase the keyword */
     if (!IgnoreCase) {
        UpcaseSVal ();
@@ -560,19 +687,18 @@ static unsigned char FindDotKeyword (void)
 
 
 static void ReadIdent (void)
-/* Read an identifier from the current input position into Ident. It is
- * assumed that the first character has already been checked.
+/* Read an identifier from the current input position into Ident. Filling SVal
+ * starts at the current position with the next character in C. It is assumed
+ * that any characters already filled in are ok, and the character in C is
+ * checked.
  */
 {
     /* Read the identifier */
-    unsigned I = 0;
     do {
-       if (I < MAX_STR_LEN) {
-           SVal [I++] = C;
-       }
-       NextChar ();
+        SB_AppendChar (&SVal, C);
+       NextChar ();
     } while (IsIdChar (C));
-    SVal [I] = '\0';
+    SB_Terminate (&SVal);
 
     /* If we should ignore case, convert the identifier to upper case */
     if (IgnoreCase) {
@@ -582,36 +708,26 @@ static void ReadIdent (void)
 
 
 
-static unsigned ReadStringConst (int StringTerm)
-/* Read a string constant into SVal. Check for maximum string length and all
- * other stuff.        The length of the string is returned.
- */
+static void ReadStringConst (int StringTerm)
+/* Read a string constant into SVal. */
 {
-    unsigned I;
-
     /* Skip the leading string terminator */
     NextChar ();
 
     /* Read the string */
-    I = 0;
     while (1) {
        if (C == StringTerm) {
            break;
        }
        if (C == '\n' || C == EOF) {
-           Error (ERR_NEWLINE_IN_STRING);
+           Error ("Newline in string constant");
            break;
        }
 
-       /* Check for string length, print an error message once */
-       if (I == MAX_STR_LEN) {
-           Error (ERR_STRING_TOO_LONG);
-       } else if (I < MAX_STR_LEN) {
-           SVal [I] = C;
-       }
-       ++I;
+               /* Append the char to the string */
+        SB_AppendChar (&SVal, C);
 
-       /* Skip the character */
+       /* Skip the character */
        NextChar ();
     }
 
@@ -619,13 +735,36 @@ static unsigned ReadStringConst (int StringTerm)
     NextChar ();
 
     /* Terminate the string */
-    if (I >= MAX_STR_LEN) {
-       I = MAX_STR_LEN;
+    SB_Terminate (&SVal);
+}
+
+
+
+static int Sweet16Reg (const StrBuf* Id)
+/* Check if the given identifier is a sweet16 register. Return -1 if this is
+ * not the case, return the register number otherwise.
+ */
+{
+    unsigned RegNum;
+    char Check;
+
+    if (SB_GetLen (Id) < 2) {
+        return -1;
+    }
+    if (toupper (SB_AtUnchecked (Id, 0)) != 'R') {
+        return -1;
+    }
+    if (!IsDigit (SB_AtUnchecked (Id, 1))) {
+        return -1;
+    }
+
+    if (sscanf (SB_GetConstBuf (Id)+1, "%u%c", &RegNum, &Check) != 1 || RegNum > 15) {
+        /* Invalid register */
+        return -1;
     }
-    SVal [I] = '\0';
 
-    /* Return the length of the string */
-    return I;
+    /* The register number is valid */
+    return (int) RegNum;
 }
 
 
@@ -653,13 +792,11 @@ Again:
         } while (IsBlank (C));
     }
 
-    /* If we're reading from the file, update the location from where the
-     * next token will be read. If we're reading from input data, keep the
-     * current position.
-     */
-    if (IData == 0) {
-        CurPos = IFile->Pos;
-    }
+    /* Mark the file position of the next token */
+    Source->Func->MarkStart (Source);
+
+    /* Clear the string attribute */
+    SB_Clear (&SVal);
 
     /* Hex number or PC symbol? */
     if (C == '$') {
@@ -671,15 +808,15 @@ Again:
                Tok = TOK_PC;
                return;
            } else {
-               Error (ERR_HEX_DIGIT_EXPECTED);
+               Error ("Hexadecimal digit expected");
            }
        }
 
-       /* Read the number */
+       /* Read the number */
        IVal = 0;
        while (IsXDigit (C)) {
            if (IVal & 0xF0000000) {
-               Error (ERR_NUM_OVERFLOW);
+               Error ("Overflow in hexadecimal number");
                IVal = 0;
            }
            IVal = (IVal << 4) + DigitVal (C);
@@ -691,20 +828,20 @@ Again:
        return;
     }
 
-    /* Dual number? */
+    /* Binary number? */
     if (C == '%') {
        NextChar ();
 
        /* 0 or 1 must follow */
-       if (!IsDDigit (C)) {
-           Error (ERR_01_EXPECTED);
+       if (!IsBDigit (C)) {
+           Error ("Binary digit expected");
        }
 
-       /* Read the number */
+       /* Read the number */
        IVal = 0;
-       while (IsDDigit (C)) {
+       while (IsBDigit (C)) {
            if (IVal & 0x80000000) {
-               Error (ERR_NUM_OVERFLOW);
+               Error ("Overflow in binary number");
                IVal = 0;
            }
            IVal = (IVal << 1) + DigitVal (C);
@@ -716,63 +853,137 @@ Again:
        return;
     }
 
-    /* Decimal number? */
+    /* Number? */
     if (IsDigit (C)) {
 
-       /* Read the number */
+        char Buf[16];
+        unsigned Digits;
+        unsigned Base;
+        unsigned I;
+        long     Max;
+        unsigned DVal;
+
+        /* Ignore leading zeros */
+        while (C == '0') {
+            NextChar ();
+        }
+
+        /* Read the number into Buf counting the digits */
+        Digits = 0;
+        while (IsXDigit (C)) {
+
+            /* Buf is big enough to allow any decimal and hex number to
+             * overflow, so ignore excess digits here, they will be detected
+             * when we convert the value.
+             */
+            if (Digits < sizeof (Buf)) {
+                Buf[Digits++] = C;
+            }
+
+            NextChar ();
+        }
+
+        /* Allow zilog/intel style hex numbers with a 'h' suffix */
+        if (C == 'h' || C == 'H') {
+            NextChar ();
+            Base = 16;
+            Max  = 0xFFFFFFFFUL / 16;
+        } else {
+            Base = 10;
+            Max  = 0xFFFFFFFFUL / 10;
+        }
+
+        /* Convert the number using the given base */
        IVal = 0;
-       while (IsDigit (C)) {
-           if (IVal > (0xFFFFFFFF / 10)) {
-                       Error (ERR_NUM_OVERFLOW);
-               IVal = 0;
+        for (I = 0; I < Digits; ++I) {
+                   if (IVal > Max) {
+                       Error ("Number out of range");
+               IVal = 0;
+                break;
            }
-           IVal = (IVal * 10) + DigitVal (C);
-           NextChar ();
-       }
+            DVal = DigitVal (Buf[I]);
+            if (DVal > Base) {
+                Error ("Invalid digits in number");
+                IVal = 0;
+                break;
+            }
+           IVal = (IVal * Base) + DVal;
+        }
 
        /* This is an integer constant */
                Tok = TOK_INTCON;
-       return;
+       return;
     }
 
     /* Control command? */
     if (C == '.') {
 
+       /* Remember and skip the dot */
        NextChar ();
 
-       if (!IsIdStart (C)) {
-           Error (ERR_PSEUDO_EXPECTED);
-           /* Try to read an identifier */
-           goto Again;
-       }
+       /* Check if it's just a dot */
+               if (!IsIdStart (C)) {
+
+           /* Just a dot */
+           Tok = TOK_DOT;
+
+       } else {
 
-       /* Read the identifier */
-       ReadIdent ();
+           /* Read the remainder of the identifier */
+            SB_AppendChar (&SVal, '.');
+           ReadIdent ();
+
+           /* Dot keyword, search for it */
+           Tok = FindDotKeyword ();
+           if (Tok == TOK_NONE) {
+
+               /* Not found */
+               if (!LeadingDotInIdents) {
+                   /* Invalid pseudo instruction */
+                   Error ("`%m%p' is not a recognized control command", &SVal);
+                   goto Again;
+               }
+
+               /* An identifier with a dot. Check if it's a define style
+                * macro.
+                */
+                       if (IsDefine (&SVal)) {
+                   /* This is a define style macro - expand it */
+                   MacExpandStart ();
+                   goto Restart;
+               }
+
+               /* Just an identifier with a dot */
+               Tok = TOK_IDENT;
+           }
 
-       /* Search the keyword */
-       Tok = FindDotKeyword ();
-       if (Tok == TOK_NONE) {
-           /* Not found */
-           Error (ERR_PSEUDO_EXPECTED);
-           goto Again;
        }
        return;
     }
 
+    /* Indirect op for sweet16 cpu. Must check this before checking for local
+     * symbols, because these may also use the '@' symbol.
+     */
+    if (CPU == CPU_SWEET16 && C == '@') {
+        NextChar ();
+        Tok = TOK_AT;
+        return;
+    }
+
     /* Local symbol? */
     if (C == LocalStart) {
 
-       /* Read the identifier */
+       /* Read the identifier. */
        ReadIdent ();
 
        /* Start character alone is not enough */
-        if (SVal [1] == '\0') {
-           Error (ERR_IDENT_EXPECTED);
+        if (SB_GetLen (&SVal) == 1) {
+           Error ("Invalid cheap local symbol");
                    goto Again;
        }
 
-               /* An identifier */
-       Tok = TOK_IDENT;
+               /* A local identifier */
+       Tok = TOK_LOCAL_IDENT;
        return;
     }
 
@@ -783,39 +994,67 @@ Again:
        /* Read the identifier */
        ReadIdent ();
 
-               /* Check for special names */
-        if (SVal [1] == '\0') {
-           switch (toupper (SVal [0])) {
-
-               case 'A':
-                   Tok = TOK_A;
+               /* Check for special names. Bail out if we have identified the type of
+        * the token. Go on if the token is an identifier.
+        */
+        if (SB_GetLen (&SVal) == 1) {
+           switch (toupper (SB_AtUnchecked (&SVal, 0))) {
+
+               case 'A':
+                    if (C == ':') {
+                        NextChar ();
+                        Tok = TOK_OVERRIDE_ABS;
+                    } else {
+                       Tok = TOK_A;
+                    }
                    return;
 
+                case 'F':
+                    if (C == ':') {
+                        NextChar ();
+                        Tok = TOK_OVERRIDE_FAR;
+                       return;
+                    }
+                   break;
+
+               case 'S':
+                    if (CPU == CPU_65816) {
+                        Tok = TOK_S;
+                        return;
+                    }
+                    break;
+
                case 'X':
-                   Tok = TOK_X;
-                   return;
+                   Tok = TOK_X;
+                   return;
 
                case 'Y':
-                   Tok = TOK_Y;
-                   return;
+                   Tok = TOK_Y;
+                   return;
+
+                case 'Z':
+                    if (C == ':') {
+                        NextChar ();
+                        Tok = TOK_OVERRIDE_ZP;
+                       return;
+                    }
+                    break;
+
+               default:
+                   break;
+           }
 
-               case 'S':
-                   Tok = TOK_S;
-                   return;
+       } else if (CPU == CPU_SWEET16 && (IVal = Sweet16Reg (&SVal)) >= 0) {
 
-               default:
-                   Tok = TOK_IDENT;
-                   return;
-           }
-       }
+            /* A sweet16 register number in sweet16 mode */
+            Tok = TOK_REG;
+            return;
+
+        }
 
-       /* Search for an opcode */
-       IVal = FindInstruction (SVal);
-       if (IVal >= 0) {
-           /* This is a mnemonic */
-                   Tok = TOK_MNEMO;
-               } else if (IsDefine (SVal)) {
-           /* This is a define style macro - expand it */
+       /* Check for define style macro */
+               if (IsDefine (&SVal)) {
+           /* Macro - expand it */
            MacExpandStart ();
            goto Restart;
        } else {
@@ -836,12 +1075,30 @@ CharAgain:
 
        case '-':
            NextChar ();
-           Tok = TOK_MINUS;
+           Tok = TOK_MINUS;
            return;
 
        case '/':
            NextChar ();
-           Tok = TOK_DIV;
+            if (C != '*') {
+                Tok = TOK_DIV;
+            } else if (CComments) {
+                /* Remember the position, then skip the '*' */
+                FilePos Pos = CurPos;
+                NextChar ();
+                do {
+                    while (C !=  '*') {
+                        if (C == EOF) {
+                            PError (&Pos, "Unterminated comment");
+                            goto CharAgain;
+                        }
+                        NextChar ();
+                    }
+                    NextChar ();
+                } while (C != '/');
+                NextChar ();
+                goto Again;
+            }
            return;
 
        case '*':
@@ -851,14 +1108,14 @@ CharAgain:
 
        case '^':
            NextChar ();
-           Tok = TOK_XOR;
+           Tok = TOK_XOR;
            return;
 
        case '&':
            NextChar ();
            if (C == '&') {
                NextChar ();
-               Tok = TOK_BAND;
+               Tok = TOK_BOOLAND;
            } else {
                Tok = TOK_AND;
            }
@@ -868,7 +1125,7 @@ CharAgain:
            NextChar ();
            if (C == '|') {
                NextChar ();
-               Tok = TOK_BOR;
+               Tok = TOK_BOOLOR;
            } else {
                Tok = TOK_OR;
            }
@@ -876,7 +1133,7 @@ CharAgain:
 
        case ':':
            NextChar ();
-           switch (C) {
+           switch (C) {
 
                case ':':
                    NextChar ();
@@ -886,8 +1143,8 @@ CharAgain:
                case '-':
                    IVal = 0;
                    do {
-                       --IVal;
-                       NextChar ();
+                       --IVal;
+                       NextChar ();
                    } while (C == '-');
                    Tok = TOK_ULABEL;
                    break;
@@ -898,9 +1155,14 @@ CharAgain:
                        ++IVal;
                        NextChar ();
                    } while (C == '+');
-                   Tok = TOK_ULABEL;
+                   Tok = TOK_ULABEL;
                    break;
 
+                case '=':
+                    NextChar ();
+                    Tok = TOK_ASSIGN;
+                    break;
+
                default:
                    Tok = TOK_COLON;
                    break;
@@ -921,7 +1183,7 @@ CharAgain:
 
        case '#':
            NextChar ();
-           Tok = TOK_HASH;
+           Tok = TOK_HASH;
            return;
 
        case '(':
@@ -944,6 +1206,16 @@ CharAgain:
            Tok = TOK_RBRACK;
            return;
 
+       case '{':
+           NextChar ();
+           Tok = TOK_LCURLY;
+           return;
+
+       case '}':
+           NextChar ();
+           Tok = TOK_RCURLY;
+           return;
+
        case '<':
            NextChar ();
            if (C == '=') {
@@ -961,17 +1233,17 @@ CharAgain:
            return;
 
        case '=':
-           NextChar ();
+           NextChar ();
                    Tok = TOK_EQ;
            return;
 
        case '!':
            NextChar ();
-           Tok = TOK_BNOT;
+           Tok = TOK_BOOLNOT;
            return;
 
        case '>':
-           NextChar ();
+           NextChar ();
            if (C == '=') {
                NextChar ();
                Tok = TOK_GE;
@@ -986,7 +1258,7 @@ CharAgain:
         case '~':
            NextChar ();
            Tok = TOK_NOT;
-           return;
+           return;
 
        case '\'':
            /* Hack: If we allow ' as terminating character for strings, read
@@ -994,8 +1266,9 @@ CharAgain:
             * string later.
             */
            if (LooseStringTerm) {
-               if (ReadStringConst ('\'') == 1) {
-                   IVal = SVal[0];
+               ReadStringConst ('\'');
+                if (SB_GetLen (&SVal) == 1) {
+                   IVal = SB_AtUnchecked (&SVal, 0);
                    Tok = TOK_CHARCON;
                } else {
                    Tok = TOK_STRCON;
@@ -1003,15 +1276,17 @@ CharAgain:
            } else {
                /* Always a character constant */
                NextChar ();
-               if (C == '\n' || C == EOF) {
-                   Error (ERR_ILLEGAL_CHARCON);
+               if (C == EOF || IsControl (C)) {
+                   Error ("Illegal character constant");
                    goto CharAgain;
                }
                IVal = C;
                Tok = TOK_CHARCON;
                NextChar ();
                if (C != '\'') {
-                   Error (ERR_ILLEGAL_CHARCON);
+                    if (!MissingCharTerm) {
+                        Error ("Illegal character constant");
+                    }
                } else {
                    NextChar ();
                }
@@ -1019,72 +1294,50 @@ CharAgain:
            return;
 
        case '\"':
-           ReadStringConst ('\"');
+           ReadStringConst ('\"');
            Tok = TOK_STRCON;
            return;
 
        case '\\':
            /* Line continuation? */
            if (LineCont) {
-               NextChar ();
-               if (C == '\n') {
-                   /* Handle as white space */
-                   NextChar ();
-                   C = ' ';
-                   goto Again;
-               }
-           }
-           break;
+               NextChar ();
+               if (C == '\n') {
+                   /* Handle as white space */
+                   NextChar ();
+                   C = ' ';
+                   goto Again;
+               }
+           }
+           break;
 
         case '\n':
-           NextChar ();
-           Tok = TOK_SEP;
-           return;
+           NextChar ();
+           Tok = TOK_SEP;
+           return;
 
         case EOF:
-           /* Check if we have any open .IFs in this file */
-           CheckOpenIfs ();
-           /* Check if we have any open token lists in this file */
-           CheckInputStack ();
-
-           /* If this was an include file, then close it and handle like a
-            * separator. Do not close the main file, but return EOF.
-            */
-           if (ICount > 1) {
-               DoneInputFile ();
-           } else {
-               Tok = TOK_EOF;
-           }
-           return;
-
+            CheckInputStack ();
+            /* In case of the main file, do not close it, but return EOF. */
+            if (Source && Source->Next) {
+                DoneCharSource ();
+                goto Again;
+            } else {
+               Tok = TOK_EOF;
+            }
+            return;
     }
 
     /* If we go here, we could not identify the current character. Skip it
      * and try again.
      */
-    Error (ERR_INVALID_CHAR, C & 0xFF);
+    Error ("Invalid input character: 0x%02X", C & 0xFF);
     NextChar ();
     goto Again;
 }
 
 
 
-int TokHasSVal (enum Token Tok)
-/* Return true if the given token has an attached SVal */
-{
-    return (Tok == TOK_IDENT || Tok == TOK_STRCON);
-}
-
-
-
-int TokHasIVal (enum Token Tok)
-/* Return true if the given token has an attached IVal */
-{
-    return (Tok == TOK_INTCON || Tok == TOK_CHARCON || Tok == TOK_MNEMO);
-}
-
-
-
 int GetSubKey (const char** Keys, unsigned Count)
 /* Search for a subkey in a table of keywords. The current token must be an
  * identifier and all keys must be in upper case. The identifier will be
@@ -1099,15 +1352,15 @@ int GetSubKey (const char** Keys, unsigned Count)
 
     /* If we aren't in ignore case mode, we have to uppercase the identifier */
     if (!IgnoreCase) {
-       UpcaseSVal ();
+       UpcaseSVal ();
     }
 
     /* Do a linear search (a binary search is not worth the effort) */
     for (I = 0; I < Count; ++I) {
-       if (strcmp (SVal, Keys [I]) == 0) {
-           /* Found it */
-           return I;
-       }
+               if (SB_CompareStr (&SVal, Keys [I]) == 0) {
+           /* Found it */
+           return I;
+       }
     }
 
     /* Not found */
@@ -1116,26 +1369,29 @@ int GetSubKey (const char** Keys, unsigned Count)
 
 
 
-void WriteFiles (void)
-/* Write the list of input files to the object file */
+unsigned char ParseAddrSize (void)
+/* Check if the next token is a keyword that denotes an address size specifier.
+ * If so, return the corresponding address size constant, otherwise output an
+ * error message and return ADDR_SIZE_DEFAULT.
+ */
 {
-    unsigned I;
-
-    /* Tell the obj file module that we're about to start the file list */
-    ObjStartFiles ();
+    unsigned char AddrSize;
 
-    /* Write the file count */
-    ObjWrite8 (FileCount);
+    /* Check for an identifier */
+    if (Tok != TOK_IDENT) {
+        Error ("Address size specifier expected");
+        return ADDR_SIZE_DEFAULT;
+    }
 
-    /* Write the file data */
-    for (I = 0; I < FileCount; ++I) {
-       ObjWrite32 (Files [I].MTime);
-       ObjWrite32 (Files [I].Size);
-       ObjWriteStr (Files [I].Name);
+    /* Convert the attribute */
+    AddrSize = AddrSizeFromStr (SB_GetConstBuf (&SVal));
+    if (AddrSize == ADDR_SIZE_INVALID) {
+        Error ("Address size specifier expected");
+        AddrSize = ADDR_SIZE_DEFAULT;
     }
 
-    /* Done writing files */
-    ObjEndFiles ();
+    /* Done */
+    return AddrSize;
 }
 
 
@@ -1152,7 +1408,7 @@ void InitScanner (const char* InFile)
 void DoneScanner (void)
 /* Release scanner resources */
 {
-    DoneInputFile ();
+    DoneCharSource ();
 }