]> git.sur5r.net Git - cc65/blobdiff - src/ca65/pseudo.c
Merge pull request #80 from AntiheroSoftware/ca65-65816
[cc65] / src / ca65 / pseudo.c
index f1ed0a139cb3d380decd145222572ad094f55683..3af1926101fcde9fcf63b037a2f739760f6a130a 100644 (file)
@@ -1,15 +1,15 @@
 /*****************************************************************************/
 /*                                                                           */
-/*                                pseudo.c                                  */
+/*                                 pseudo.c                                  */
 /*                                                                           */
-/*             Pseudo instructions for the ca65 macroassembler              */
+/*              Pseudo instructions for the ca65 macroassembler              */
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 1998-2002 Ullrich von Bassewitz                                       */
-/*               Wacholderweg 14                                             */
-/*               D-70597 Stuttgart                                           */
-/* EMail:        uz@musoftware.de                                            */
+/* (C) 1998-2012, Ullrich von Bassewitz                                      */
+/*                Roemerstrasse 52                                           */
+/*                D-70794 Filderstadt                                        */
+/* EMail:         uz@cc65.org                                                */
 /*                                                                           */
 /*                                                                           */
 /* This software is provided 'as-is', without any expressed or implied       */
 #include <errno.h>
 
 /* common */
+#include "alignment.h"
+#include "assertion.h"
 #include "bitops.h"
 #include "cddefs.h"
-#include "check.h"
+#include "coll.h"
+#include "filestat.h"
+#include "gentype.h"
+#include "intstack.h"
+#include "scopedefs.h"
 #include "symdefs.h"
 #include "tgttrans.h"
+#include "xmalloc.h"
 
 /* ca65 */
+#include "anonname.h"
+#include "asserts.h"
 #include "condasm.h"
 #include "dbginfo.h"
+#include "enum.h"
 #include "error.h"
 #include "expr.h"
 #include "feature.h"
+#include "filetab.h"
 #include "global.h"
+#include "incpath.h"
 #include "instr.h"
 #include "listing.h"
-#include "macpack.h"
 #include "macro.h"
 #include "nexttok.h"
 #include "objcode.h"
 #include "options.h"
+#include "pseudo.h"
 #include "repeat.h"
+#include "segment.h"
+#include "sizeof.h"
+#include "span.h"
+#include "spool.h"
+#include "struct.h"
+#include "symbol.h"
 #include "symtab.h"
-#include "pseudo.h"
 
 
 
 /*****************************************************************************/
-/*                                          Data                                    */
+/*                                   Data                                    */
 /*****************************************************************************/
 
 
 
 /* Keyword we're about to handle */
-static char Keyword [sizeof (SVal)+1] = ".";
+static StrBuf Keyword = STATIC_STRBUF_INITIALIZER;
+
+/* CPU stack */
+static IntStack CPUStack = STATIC_INTSTACK_INITIALIZER;
+
+/* Segment stack */
+#define MAX_PUSHED_SEGMENTS     16
+static Collection SegStack = STATIC_COLLECTION_INITIALIZER;
 
 
 
 /*****************************************************************************/
-/*                              Forwards                                    */
+/*                               Forwards                                    */
 /*****************************************************************************/
 
 
@@ -98,57 +122,126 @@ static void DoInvalid (void);
 
 
 /*****************************************************************************/
-/*                             Helper functions                             */
+/*                              Helper functions                             */
 /*****************************************************************************/
 
 
 
+static unsigned char OptionalAddrSize (void)
+/* If a colon follows, parse an optional address size spec and return it.
+ * Otherwise return ADDR_SIZE_DEFAULT.
+ */
+{
+    unsigned AddrSize = ADDR_SIZE_DEFAULT;
+    if (CurTok.Tok == TOK_COLON) {
+        NextTok ();
+        AddrSize = ParseAddrSize ();
+        if (!ValidAddrSizeForCPU (AddrSize)) {
+            /* Print an error and reset to default */
+            Error ("Invalid address size specification for current CPU");
+            AddrSize = ADDR_SIZE_DEFAULT;
+        }
+        NextTok ();
+    }
+    return AddrSize;
+}
+
+
+
 static void SetBoolOption (unsigned char* Flag)
 /* Read a on/off/+/- option and set flag accordingly */
 {
     static const char* Keys[] = {
-               "OFF",
-       "ON",
+        "OFF",
+        "ON",
     };
 
-    if (Tok == TOK_PLUS) {
-               *Flag = 1;
-       NextTok ();
-    } else if (Tok == TOK_MINUS) {
-       *Flag = 0;
-       NextTok ();
-    } else if (Tok == TOK_IDENT) {
-               /* Map the keyword to a number */
-               switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
-           case 0:     *Flag = 0; NextTok ();          break;
-           case 1:     *Flag = 1; NextTok ();          break;
-           default:    ErrorSkip (ERR_ONOFF_EXPECTED); break;
-       }
-    } else if (Tok == TOK_SEP || Tok == TOK_EOF) {
-       /* Without anything assume switch on */
-       *Flag = 1;
+    if (CurTok.Tok == TOK_PLUS) {
+        *Flag = 1;
+        NextTok ();
+    } else if (CurTok.Tok == TOK_MINUS) {
+        *Flag = 0;
+        NextTok ();
+    } else if (CurTok.Tok == TOK_IDENT) {
+        /* Map the keyword to a number */
+        switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) {
+            case 0:     *Flag = 0; NextTok ();                  break;
+            case 1:     *Flag = 1; NextTok ();                  break;
+            default:    ErrorSkip ("`on' or `off' expected");   break;
+        }
+    } else if (TokIsSep (CurTok.Tok)) {
+        /* Without anything assume switch on */
+        *Flag = 1;
     } else {
-               ErrorSkip (ERR_ONOFF_EXPECTED);
+        ErrorSkip ("`on' or `off' expected");
     }
 }
 
 
 
-static void ExportImport (void (*SymFunc) (const char*, int), int ZP)
+static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags)
+/* Allow to assign the value of an export in an .export statement */
+{
+    /* The name and optional address size spec may be followed by an assignment
+     * or equal token.
+     */
+    if (CurTok.Tok == TOK_ASSIGN || CurTok.Tok == TOK_EQ) {
+
+        /* Assignment means the symbol is a label */
+        if (CurTok.Tok == TOK_ASSIGN) {
+            Flags |= SF_LABEL;
+        }
+
+        /* Skip the assignment token */
+        NextTok ();
+
+        /* Define the symbol with the expression following the '=' */
+        SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags);
+
+    }
+
+    /* Now export the symbol */
+    SymExport (Sym, AddrSize, Flags);
+}
+
+
+
+static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned),
+                          unsigned char DefAddrSize, unsigned Flags)
 /* Export or import symbols */
 {
+    SymEntry* Sym;
+    unsigned char AddrSize;
+
     while (1) {
-       if (Tok != TOK_IDENT) {
-                   ErrorSkip (ERR_IDENT_EXPECTED);
-           break;
-       }
-       SymFunc (SVal, ZP);
-       NextTok ();
-       if (Tok == TOK_COMMA) {
-           NextTok ();
-       } else {
-           break;
-       }
+
+        /* We need an identifier here */
+        if (CurTok.Tok != TOK_IDENT) {
+            ErrorSkip ("Identifier expected");
+            return;
+        }
+
+        /* Find the symbol table entry, allocate a new one if necessary */
+        Sym = SymFind (CurrentScope, &CurTok.SVal, SYM_ALLOC_NEW);
+
+        /* Skip the name */
+        NextTok ();
+
+        /* Get an optional address size */
+        AddrSize = OptionalAddrSize ();
+        if (AddrSize == ADDR_SIZE_DEFAULT) {
+            AddrSize = DefAddrSize;
+        }
+
+        /* Call the actual import/export function */
+        Func (Sym, AddrSize, Flags);
+
+        /* More symbols? */
+        if (CurTok.Tok == TOK_COMMA) {
+            NextTok ();
+        } else {
+            break;
+        }
     }
 }
 
@@ -159,49 +252,80 @@ static long IntArg (long Min, long Max)
  * and return -1 in this case.
  */
 {
-    if (Tok == TOK_IDENT && strcmp (SVal, "unlimited") == 0) {
-       NextTok ();
-       return -1;
+    if (CurTok.Tok == TOK_IDENT && SB_CompareStr (&CurTok.SVal, "unlimited") == 0) {
+        NextTok ();
+        return -1;
     } else {
-       long Val = ConstExpression ();
-       if (Val < Min || Val > Max) {
-           Error (ERR_RANGE);
-           Val = Min;
-       }
-       return Val;
+        long Val = ConstExpression ();
+        if (Val < Min || Val > Max) {
+            Error ("Range error");
+            Val = Min;
+        }
+        return Val;
     }
 }
 
 
 
-static void ConDes (const char* Name, unsigned Type)
+static void ConDes (const StrBuf* Name, unsigned Type)
 /* Parse remaining line for constructor/destructor of the remaining type */
 {
     long Prio;
 
+
+    /* Find the symbol table entry, allocate a new one if necessary */
+    SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW);
+
     /* Optional constructor priority */
-    if (Tok == TOK_COMMA) {
-       /* Priority value follows */
-       NextTok ();
-       Prio = ConstExpression ();
-       if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
-           /* Value out of range */
-           Error (ERR_RANGE);
-           return;
-       }
+    if (CurTok.Tok == TOK_COMMA) {
+        /* Priority value follows */
+        NextTok ();
+        Prio = ConstExpression ();
+        if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) {
+            /* Value out of range */
+            Error ("Range error");
+            return;
+        }
     } else {
-       /* Use the default priority value */
-       Prio = CD_PRIO_DEF;
+        /* Use the default priority value */
+        Prio = CD_PRIO_DEF;
     }
 
     /* Define the symbol */
-    SymConDes (Name, Type, (unsigned) Prio);
+    SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio);
+}
+
+
+
+static StrBuf* GenArrayType (StrBuf* Type, unsigned SpanSize,
+                             const char* ElementType,
+                             unsigned ElementTypeLen)
+/* Create an array (or single data) of the given type. SpanSize is the size
+ * of the span, ElementType is a string that encodes the element data type.
+ * The function returns Type.
+ */
+{
+    /* Get the size of the element type */
+    unsigned ElementSize = GT_GET_SIZE (ElementType[0]);
+
+    /* Get the number of array elements */
+    unsigned ElementCount = SpanSize / ElementSize;
+
+    /* The span size must be divideable by the element size */
+    CHECK ((SpanSize % ElementSize) == 0);
+
+    /* Encode the array */
+    GT_AddArray (Type, ElementCount);
+    SB_AppendBuf (Type, ElementType, ElementTypeLen);
+
+    /* Return the pointer to the created array type */
+    return Type;
 }
 
 
 
 /*****************************************************************************/
-/*                            Handler functions                             */
+/*                             Handler functions                             */
 /*****************************************************************************/
 
 
@@ -210,10 +334,10 @@ static void DoA16 (void)
 /* Switch the accu to 16 bit mode (assembler only) */
 {
     if (GetCPU() != CPU_65816) {
-       Error (ERR_816_MODE_ONLY);
+        Error ("Command is only valid in 65816 mode");
     } else {
-               /* Immidiate mode has two extension bytes */
-       ExtBytes [AMI_IMM_ACCU] = 2;
+        /* Immidiate mode has two extension bytes */
+        ExtBytes [AM65I_IMM_ACCU] = 2;
     }
 }
 
@@ -223,10 +347,10 @@ static void DoA8 (void)
 /* Switch the accu to 8 bit mode (assembler only) */
 {
     if (GetCPU() != CPU_65816) {
-       Error (ERR_816_MODE_ONLY);
+        Error ("Command is only valid in 65816 mode");
     } else {
-       /* Immidiate mode has one extension byte */
-       ExtBytes [AMI_IMM_ACCU] = 1;
+        /* Immidiate mode has one extension byte */
+        ExtBytes [AM65I_IMM_ACCU] = 1;
     }
 }
 
@@ -235,19 +359,34 @@ static void DoA8 (void)
 static void DoAddr (void)
 /* Define addresses */
 {
+    /* Element type for the generated array */
+    static const char EType[2] = { GT_PTR, GT_VOID };
+
+    /* Record type information */
+    Span* S = OpenSpan ();
+    StrBuf Type = STATIC_STRBUF_INITIALIZER;
+
+    /* Parse arguments */
     while (1) {
-       if (GetCPU() == CPU_65816) {
-                   EmitWord (ForceWordExpr (Expression ()));
-       } else {
-           /* Do a range check */
-           EmitWord (Expression ());
-               }
-       if (Tok != TOK_COMMA) {
-           break;
-       } else {
-           NextTok ();
-       }
+        ExprNode* Expr = Expression ();
+        if (GetCPU () == CPU_65816 || ForceRange) {
+            /* Do a range check */
+            Expr = GenWordExpr (Expr);
+        }
+        EmitWord (Expr);
+        if (CurTok.Tok != TOK_COMMA) {
+            break;
+        } else {
+            NextTok ();
+        }
     }
+
+    /* Close the span, then add type information to it */
+    S = CloseSpan (S);
+    SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
+
+    /* Free the strings */
+    SB_Done (&Type);
 }
 
 
@@ -255,37 +394,31 @@ static void DoAddr (void)
 static void DoAlign (void)
 /* Align the PC to some boundary */
 {
-    long Val;
-    long Align;
-    unsigned Bit;
+    long FillVal;
+    long Alignment;
 
     /* Read the alignment value */
-    Align = ConstExpression ();
-    if (Align <= 0 || Align > 0x10000) {
-               ErrorSkip (ERR_RANGE);
-       return;
+    Alignment = ConstExpression ();
+    if (Alignment <= 0 || (unsigned long) Alignment > MAX_ALIGNMENT) {
+        ErrorSkip ("Range error");
+        return;
     }
 
     /* Optional value follows */
-    if (Tok == TOK_COMMA) {
-       NextTok ();
-       Val = ConstExpression ();
-       /* We need a byte value here */
-       if (!IsByteRange (Val)) {
-                   ErrorSkip (ERR_RANGE);
-           return;
-       }
+    if (CurTok.Tok == TOK_COMMA) {
+        NextTok ();
+        FillVal = ConstExpression ();
+        /* We need a byte value here */
+        if (!IsByteRange (FillVal)) {
+            ErrorSkip ("Range error");
+            return;
+        }
     } else {
-       Val = -1;
+        FillVal = -1;
     }
 
-    /* Check if the alignment is a power of two */
-    Bit = BitFind (Align);
-    if (Align != (0x01L << Bit)) {
-       Error (ERR_ALIGN);
-    } else {
-       SegAlign (Bit, (int) Val);
-    }
+    /* Generate the alignment */
+    SegAlign (Alignment, (int) FillVal);
 }
 
 
@@ -293,33 +426,116 @@ static void DoAlign (void)
 static void DoASCIIZ (void)
 /* Define text with a zero terminator */
 {
-    unsigned Len;
-
     while (1) {
-       /* Must have a string constant */
-       if (Tok != TOK_STRCON) {
-           ErrorSkip (ERR_STRCON_EXPECTED);
-           return;
-       }
-
-       /* Get the length of the string constant */
-       Len = strlen (SVal);
-
-       /* Translate into target charset and emit */
-       TgtTranslateBuf (SVal, Len);
-               EmitData ((unsigned char*) SVal, Len);
-       NextTok ();
-       if (Tok == TOK_COMMA) {
-           NextTok ();
-       } else {
-           break;
-       }
+        /* Must have a string constant */
+        if (CurTok.Tok != TOK_STRCON) {
+            ErrorSkip ("String constant expected");
+            return;
+        }
+
+        /* Translate into target charset and emit */
+        TgtTranslateStrBuf (&CurTok.SVal);
+        EmitStrBuf (&CurTok.SVal);
+        NextTok ();
+        if (CurTok.Tok == TOK_COMMA) {
+            NextTok ();
+        } else {
+            break;
+        }
     }
     Emit0 (0);
 }
 
 
 
+static void DoAssert (void)
+/* Add an assertion */
+{
+    static const char* ActionTab [] = {
+        "WARN", "WARNING",
+        "ERROR",
+        "LDWARN", "LDWARNING",
+        "LDERROR",
+    };
+
+    AssertAction Action;
+    unsigned     Msg;
+
+    /* First we have the expression that has to evaluated */
+    ExprNode* Expr = Expression ();
+    ConsumeComma ();
+
+    /* Action follows */
+    if (CurTok.Tok != TOK_IDENT) {
+        ErrorSkip ("Identifier expected");
+        return;
+    }
+    switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) {
+
+        case 0:
+        case 1:
+            /* Warning */
+            Action = ASSERT_ACT_WARN;
+            break;
+
+        case 2:
+            /* Error */
+            Action = ASSERT_ACT_ERROR;
+            break;
+
+        case 3:
+        case 4:
+            /* Linker warning */
+            Action = ASSERT_ACT_LDWARN;
+            break;
+
+        case 5:
+            /* Linker error */
+            Action = ASSERT_ACT_LDERROR;
+            break;
+
+        default:
+            Error ("Illegal assert action specifier");
+            /* Use lderror - there won't be an .o file anyway */
+            Action = ASSERT_ACT_LDERROR;
+            break;
+
+    }
+    NextTok ();
+
+    /* We can have an optional message. If no message is present, use
+     * "Assertion failed".
+     */
+    if (CurTok.Tok == TOK_COMMA) {
+
+        /* Skip the comma */
+        NextTok ();
+
+        /* Read the message */
+        if (CurTok.Tok != TOK_STRCON) {
+            ErrorSkip ("String constant expected");
+            return;
+        }
+
+        /* Translate the message into a string id. We can then skip the input
+         * string.
+         */
+        Msg = GetStrBufId (&CurTok.SVal);
+        NextTok ();
+
+    } else {
+
+        /* Use "Assertion failed" */
+        Msg = GetStringId ("Assertion failed");
+
+    }
+
+    /* Remember the assertion */
+    AddAssertion (Expr, (AssertAction) Action, Msg);
+}
+
+
+
 static void DoAutoImport (void)
 /* Mark unresolved symbols as imported */
 {
@@ -327,11 +543,25 @@ static void DoAutoImport (void)
 }
 
 
+static void DoBankBytes (void)
+/* Define bytes, extracting the bank byte from each expression in the list */
+{
+    while (1) {
+        EmitByte (FuncBankByte ());
+        if (CurTok.Tok != TOK_COMMA) {
+            break;
+        } else {
+            NextTok ();
+        }
+    }
+}
+
+
 
 static void DoBss (void)
 /* Switch to the BSS segment */
 {
-    UseBssSeg ();
+    UseSeg (&BssSegDef);
 }
 
 
@@ -339,27 +569,41 @@ static void DoBss (void)
 static void DoByte (void)
 /* Define bytes */
 {
+    /* Element type for the generated array */
+    static const char EType[1] = { GT_BYTE };
+
+    /* Record type information */
+    Span* S = OpenSpan ();
+    StrBuf Type = STATIC_STRBUF_INITIALIZER;
+
+    /* Parse arguments */
     while (1) {
-       if (Tok == TOK_STRCON) {
-           /* A string, translate into target charset and emit */
-           unsigned Len = strlen (SVal);
-           TgtTranslateBuf (SVal, Len);
-           EmitData ((unsigned char*) SVal, Len);
-           NextTok ();
-       } else {
-           EmitByte (Expression ());
-       }
-       if (Tok != TOK_COMMA) {
-           break;
-       } else {
-           NextTok ();
-           /* Do smart handling of dangling comma */
-           if (Tok == TOK_SEP) {
-               Error (ERR_UNEXPECTED_EOL);
-               break;
-           }
-       }
+        if (CurTok.Tok == TOK_STRCON) {
+            /* A string, translate into target charset and emit */
+            TgtTranslateStrBuf (&CurTok.SVal);
+            EmitStrBuf (&CurTok.SVal);
+            NextTok ();
+        } else {
+            EmitByte (BoundedExpr (Expression, 1));
+        }
+        if (CurTok.Tok != TOK_COMMA) {
+            break;
+        } else {
+            NextTok ();
+            /* Do smart handling of dangling comma */
+            if (CurTok.Tok == TOK_SEP) {
+                Error ("Unexpected end of line");
+                break;
+            }
+        }
     }
+
+    /* Close the span, then add type information to it */
+    S = CloseSpan (S);
+    SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
+
+    /* Free the type string */
+    SB_Done (&Type);
 }
 
 
@@ -381,10 +625,10 @@ static void DoCharMap (void)
 
     /* Read the index as numerical value */
     Index = ConstExpression ();
-    if (Index < 1 || Index > 255) {
-       /* Value out of range */
-               ErrorSkip (ERR_RANGE);
-       return;
+    if (Index <= 0 || Index > 255) {
+        /* Value out of range */
+        ErrorSkip ("Range error");
+        return;
     }
 
     /* Comma follows */
@@ -392,10 +636,10 @@ static void DoCharMap (void)
 
     /* Read the character code */
     Code = ConstExpression ();
-    if (Code < 1 || Code > 255) {
-       /* Value out of range */
-               ErrorSkip (ERR_RANGE);
-       return;
+    if (Code < 0 || Code > 255) {
+        /* Value out of range */
+        ErrorSkip ("Range error");
+        return;
     }
 
     /* Set the character translation */
@@ -407,7 +651,7 @@ static void DoCharMap (void)
 static void DoCode (void)
 /* Switch to the code segment */
 {
-    UseCodeSeg ();
+    UseSeg (&CodeSegDef);
 }
 
 
@@ -416,49 +660,53 @@ static void DoConDes (void)
 /* Export a symbol as constructor/destructor */
 {
     static const char* Keys[] = {
-               "CONSTRUCTOR",
-       "DESTRUCTOR",
+        "CONSTRUCTOR",
+        "DESTRUCTOR",
+        "INTERRUPTOR",
     };
-    char Name [sizeof (SVal)];
+    StrBuf Name = STATIC_STRBUF_INITIALIZER;
     long Type;
 
     /* Symbol name follows */
-    if (Tok != TOK_IDENT) {
-       ErrorSkip (ERR_IDENT_EXPECTED);
-       return;
+    if (CurTok.Tok != TOK_IDENT) {
+        ErrorSkip ("Identifier expected");
+        return;
     }
-    strcpy (Name, SVal);
+    SB_Copy (&Name, &CurTok.SVal);
     NextTok ();
 
     /* Type follows. May be encoded as identifier or numerical */
     ConsumeComma ();
-    if (Tok == TOK_IDENT) {
+    if (CurTok.Tok == TOK_IDENT) {
 
-       /* Map the following keyword to a number, then skip it */
-       Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
-       NextTok ();
+        /* Map the following keyword to a number, then skip it */
+        Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
+        NextTok ();
 
-       /* Check if we got a valid keyword */
-       if (Type < 0) {
-           Error (ERR_SYNTAX);
-           SkipUntilSep ();
-           return;
-       }
+        /* Check if we got a valid keyword */
+        if (Type < 0) {
+            ErrorSkip ("Syntax error");
+            goto ExitPoint;
+        }
 
     } else {
 
-       /* Read the type as numerical value */
-               Type = ConstExpression ();
-       if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
-           /* Value out of range */
-           Error (ERR_RANGE);
-           return;
-       }
+        /* Read the type as numerical value */
+        Type = ConstExpression ();
+        if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) {
+            /* Value out of range */
+            ErrorSkip ("Range error");
+            goto ExitPoint;
+        }
 
     }
 
     /* Parse the remainder of the line and export the symbol */
-    ConDes (Name, (unsigned) Type);
+    ConDes (&Name, (unsigned) Type);
+
+ExitPoint:
+    /* Free string memory */
+    SB_Done (&Name);
 }
 
 
@@ -466,18 +714,21 @@ static void DoConDes (void)
 static void DoConstructor (void)
 /* Export a symbol as constructor */
 {
-    char Name [sizeof (SVal)];
+    StrBuf Name = STATIC_STRBUF_INITIALIZER;
 
     /* Symbol name follows */
-    if (Tok != TOK_IDENT) {
-       ErrorSkip (ERR_IDENT_EXPECTED);
-       return;
+    if (CurTok.Tok != TOK_IDENT) {
+        ErrorSkip ("Identifier expected");
+        return;
     }
-    strcpy (Name, SVal);
+    SB_Copy (&Name, &CurTok.SVal);
     NextTok ();
 
     /* Parse the remainder of the line and export the symbol */
-    ConDes (Name, CD_TYPE_CON);
+    ConDes (&Name, CD_TYPE_CON);
+
+    /* Free string memory */
+    SB_Done (&Name);
 }
 
 
@@ -485,7 +736,7 @@ static void DoConstructor (void)
 static void DoData (void)
 /* Switch to the data segment */
 {
-    UseDataSeg ();
+    UseSeg (&DataSegDef);
 }
 
 
@@ -494,17 +745,18 @@ static void DoDbg (void)
 /* Add debug information from high level code */
 {
     static const char* Keys[] = {
-               "FILE",
-       "LINE",
-       "SYM",
+        "FILE",
+        "FUNC",
+        "LINE",
+        "SYM",
     };
     int Key;
 
 
     /* We expect a subkey */
-    if (Tok != TOK_IDENT) {
-       ErrorSkip (ERR_IDENT_EXPECTED);
-       return;
+    if (CurTok.Tok != TOK_IDENT) {
+        ErrorSkip ("Identifier expected");
+        return;
     }
 
     /* Map the following keyword to a number */
@@ -515,10 +767,11 @@ static void DoDbg (void)
 
     /* Check the key and dispatch to a handler */
     switch (Key) {
-       case 0:     DbgInfoFile ();             break;
-       case 1:     DbgInfoLine ();             break;
-       case 2:     DbgInfoSym ();              break;
-       default:    ErrorSkip (ERR_SYNTAX);     break;
+        case 0:     DbgInfoFile ();             break;
+        case 1:     DbgInfoFunc ();             break;
+        case 2:     DbgInfoLine ();             break;
+        case 3:     DbgInfoSym ();              break;
+        default:    ErrorSkip ("Syntax error"); break;
     }
 }
 
@@ -527,14 +780,29 @@ static void DoDbg (void)
 static void DoDByt (void)
 /* Output double bytes */
 {
+    /* Element type for the generated array */
+    static const char EType[1] = { GT_DBYTE };
+
+    /* Record type information */
+    Span* S = OpenSpan ();
+    StrBuf Type = STATIC_STRBUF_INITIALIZER;
+
+    /* Parse arguments */
     while (1) {
-       EmitWord (SwapExpr (Expression ()));
-       if (Tok != TOK_COMMA) {
-           break;
-       } else {
-           NextTok ();
-       }
+        EmitWord (GenSwapExpr (BoundedExpr (Expression, 2)));
+        if (CurTok.Tok != TOK_COMMA) {
+            break;
+        } else {
+            NextTok ();
+        }
     }
+
+    /* Close the span, then add type information to it */
+    S = CloseSpan (S);
+    SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
+
+    /* Free the type string */
+    SB_Done (&Type);
 }
 
 
@@ -555,21 +823,38 @@ static void DoDefine (void)
 
 
 
+static void DoDelMac (void)
+/* Delete a classic macro */
+{
+    /* We expect an identifier */
+    if (CurTok.Tok != TOK_IDENT) {
+        ErrorSkip ("Identifier expected");
+    } else {
+        MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC);
+        NextTok ();
+    }
+}
+
+
+
 static void DoDestructor (void)
 /* Export a symbol as destructor */
 {
-    char Name [sizeof (SVal)];
+    StrBuf Name = STATIC_STRBUF_INITIALIZER;
 
     /* Symbol name follows */
-    if (Tok != TOK_IDENT) {
-       ErrorSkip (ERR_IDENT_EXPECTED);
-       return;
+    if (CurTok.Tok != TOK_IDENT) {
+        ErrorSkip ("Identifier expected");
+        return;
     }
-    strcpy (Name, SVal);
+    SB_Copy (&Name, &CurTok.SVal);
     NextTok ();
 
     /* Parse the remainder of the line and export the symbol */
-    ConDes (Name, CD_TYPE_DES);
+    ConDes (&Name, CD_TYPE_DES);
+
+    /* Free string memory */
+    SB_Done (&Name);
 }
 
 
@@ -578,12 +863,12 @@ static void DoDWord (void)
 /* Define dwords */
 {
     while (1) {
-               EmitDWord (Expression ());
-       if (Tok != TOK_COMMA) {
-           break;
-       } else {
-           NextTok ();
-       }
+        EmitDWord (BoundedExpr (Expression, 4));
+        if (CurTok.Tok != TOK_COMMA) {
+            break;
+        } else {
+            NextTok ();
+        }
     }
 }
 
@@ -593,6 +878,7 @@ static void DoEnd (void)
 /* End of assembly */
 {
     ForcedEnd = 1;
+    NextTok ();
 }
 
 
@@ -600,7 +886,25 @@ static void DoEnd (void)
 static void DoEndProc (void)
 /* Leave a lexical level */
 {
-    SymLeaveLevel ();
+    if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) {
+        /* No local scope */
+        ErrorSkip ("No open .PROC");
+    } else {
+        SymLeaveLevel ();
+    }
+}
+
+
+
+static void DoEndScope (void)
+/* Leave a lexical level */
+{
+    if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label != 0) {
+        /* No local scope */
+        ErrorSkip ("No open .SCOPE");
+    } else {
+        SymLeaveLevel ();
+    }
 }
 
 
@@ -608,11 +912,11 @@ static void DoEndProc (void)
 static void DoError (void)
 /* User error */
 {
-    if (Tok != TOK_STRCON) {
-       ErrorSkip (ERR_STRCON_EXPECTED);
+    if (CurTok.Tok != TOK_STRCON) {
+        ErrorSkip ("String constant expected");
     } else {
-               Error (ERR_USER, SVal);
-       SkipUntilSep ();
+        Error ("User error: %m%p", &CurTok.SVal);
+        SkipUntilSep ();
     }
 }
 
@@ -622,10 +926,10 @@ static void DoExitMacro (void)
 /* Exit a macro expansion */
 {
     if (!InMacExpansion ()) {
-       /* We aren't expanding a macro currently */
-               DoUnexpected ();
+        /* We aren't expanding a macro currently */
+        DoUnexpected ();
     } else {
-       MacAbort ();
+        MacAbort ();
     }
 }
 
@@ -634,7 +938,7 @@ static void DoExitMacro (void)
 static void DoExport (void)
 /* Export a symbol */
 {
-    ExportImport (SymExport, 0);
+    ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE);
 }
 
 
@@ -642,7 +946,7 @@ static void DoExport (void)
 static void DoExportZP (void)
 /* Export a zeropage symbol */
 {
-    ExportImport (SymExport, 1);
+    ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE);
 }
 
 
@@ -650,13 +954,41 @@ static void DoExportZP (void)
 static void DoFarAddr (void)
 /* Define far addresses (24 bit) */
 {
+    /* Element type for the generated array */
+    static const char EType[2] = { GT_FAR_PTR, GT_VOID };
+
+    /* Record type information */
+    Span* S = OpenSpan ();
+    StrBuf Type = STATIC_STRBUF_INITIALIZER;
+
+    /* Parse arguments */
     while (1) {
-               EmitFarAddr (Expression ());
-       if (Tok != TOK_COMMA) {
-           break;
-       } else {
-           NextTok ();
-       }
+        EmitFarAddr (BoundedExpr (Expression, 3));
+        if (CurTok.Tok != TOK_COMMA) {
+            break;
+        } else {
+            NextTok ();
+        }
+    }
+
+    /* Close the span, then add type information to it */
+    S = CloseSpan (S);
+    SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
+
+    /* Free the type string */
+    SB_Done (&Type);
+}
+
+
+
+static void DoFatal (void)
+/* Fatal user error */
+{
+    if (CurTok.Tok != TOK_STRCON) {
+        ErrorSkip ("String constant expected");
+    } else {
+        Fatal ("User error: %m%p", &CurTok.SVal);
+        SkipUntilSep ();
     }
 }
 
@@ -668,31 +1000,31 @@ static void DoFeature (void)
     /* Allow a list of comma separated keywords */
     while (1) {
 
-       /* We expect an identifier */
-       if (Tok != TOK_IDENT) {
-           ErrorSkip (ERR_IDENT_EXPECTED);
-           return;
-       }
-
-       /* Make the string attribute lower case */
-       LocaseSVal ();
-
-       /* Set the feature and check for errors */
-       if (SetFeature (SVal) == FEAT_UNKNOWN) {
-           /* Not found */
-           ErrorSkip (ERR_ILLEGAL_FEATURE);
-           return;
-       } else {
-           /* Skip the keyword */
-           NextTok ();
-       }
-
-       /* Allow more than one keyword */
-       if (Tok == TOK_COMMA) {
-           NextTok ();
-       } else {
-           break;
-       }
+        /* We expect an identifier */
+        if (CurTok.Tok != TOK_IDENT) {
+            ErrorSkip ("Identifier expected");
+            return;
+        }
+
+        /* Make the string attribute lower case */
+        LocaseSVal ();
+
+        /* Set the feature and check for errors */
+        if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) {
+            /* Not found */
+            ErrorSkip ("Invalid feature: `%m%p'", &CurTok.SVal);
+            return;
+        } else {
+            /* Skip the keyword */
+            NextTok ();
+        }
+
+        /* Allow more than one keyword */
+        if (CurTok.Tok == TOK_COMMA) {
+            NextTok ();
+        } else {
+            break;
+        }
     }
 }
 
@@ -704,91 +1036,99 @@ static void DoFileOpt (void)
     long OptNum;
 
     /* The option type may be given as a keyword or as a number. */
-    if (Tok == TOK_IDENT) {
+    if (CurTok.Tok == TOK_IDENT) {
 
-       /* Option given as keyword */
-       static const char* Keys [] = {
-           "AUTHOR", "COMMENT", "COMPILER"
-       };
+        /* Option given as keyword */
+        static const char* Keys [] = {
+            "AUTHOR", "COMMENT", "COMPILER"
+        };
 
-       /* Map the option to a number */
-       OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
-       if (OptNum < 0) {
-           /* Not found */
-           ErrorSkip (ERR_OPTION_KEY_EXPECTED);
-           return;
-       }
+        /* Map the option to a number */
+        OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
+        if (OptNum < 0) {
+            /* Not found */
+            ErrorSkip ("File option keyword expected");
+            return;
+        }
 
-       /* Skip the keyword */
-       NextTok ();
+        /* Skip the keyword */
+        NextTok ();
 
-       /* Must be followed by a comma */
-       ConsumeComma ();
+        /* Must be followed by a comma */
+        ConsumeComma ();
 
-       /* We accept only string options for now */
-       if (Tok != TOK_STRCON) {
-           ErrorSkip (ERR_STRCON_EXPECTED);
-           return;
-       }
+        /* We accept only string options for now */
+        if (CurTok.Tok != TOK_STRCON) {
+            ErrorSkip ("String constant expected");
+            return;
+        }
 
-               /* Insert the option */
-       switch (OptNum) {
+        /* Insert the option */
+        switch (OptNum) {
 
-           case 0:
-               /* Author */
-               OptAuthor (SVal);
-               break;
+            case 0:
+                /* Author */
+                OptAuthor (&CurTok.SVal);
+                break;
 
-           case 1:
-               /* Comment */
-               OptComment (SVal);
-               break;
+            case 1:
+                /* Comment */
+                OptComment (&CurTok.SVal);
+                break;
 
-           case 2:
-               /* Compiler */
-               OptCompiler (SVal);
-               break;
+            case 2:
+                /* Compiler */
+                OptCompiler (&CurTok.SVal);
+                break;
 
-           default:
-               Internal ("Invalid OptNum: %l", OptNum);
+            default:
+                Internal ("Invalid OptNum: %ld", OptNum);
 
-       }
+        }
 
-       /* Done */
-       NextTok ();
+        /* Done */
+        NextTok ();
 
     } else {
 
-       /* Option given as number */
-               OptNum = ConstExpression ();
-       if (!IsByteRange (OptNum)) {
-           ErrorSkip (ERR_RANGE);
-           return;
-       }
+        /* Option given as number */
+        OptNum = ConstExpression ();
+        if (!IsByteRange (OptNum)) {
+            ErrorSkip ("Range error");
+            return;
+        }
 
-       /* Must be followed by a comma */
-       ConsumeComma ();
+        /* Must be followed by a comma */
+        ConsumeComma ();
 
-       /* We accept only string options for now */
-       if (Tok != TOK_STRCON) {
-           ErrorSkip (ERR_STRCON_EXPECTED);
-           return;
-       }
+        /* We accept only string options for now */
+        if (CurTok.Tok != TOK_STRCON) {
+            ErrorSkip ("String constant expected");
+            return;
+        }
 
-       /* Insert the option */
-       OptStr ((unsigned char) OptNum, SVal);
+        /* Insert the option */
+        OptStr ((unsigned char) OptNum, &CurTok.SVal);
 
-       /* Done */
-       NextTok ();
+        /* Done */
+        NextTok ();
     }
 }
 
 
 
+static void DoForceImport (void)
+/* Do a forced import on a symbol */
+{
+    ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED);
+}
+
+
+
 static void DoGlobal (void)
 /* Declare a global symbol */
 {
-    ExportImport (SymGlobal, 0);
+    ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE);
 }
 
 
@@ -796,7 +1136,21 @@ static void DoGlobal (void)
 static void DoGlobalZP (void)
 /* Declare a global zeropage symbol */
 {
-    ExportImport (SymGlobal, 1);
+    ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE);
+}
+
+
+static void DoHiBytes (void)
+/* Define bytes, extracting the hi byte from each expression in the list */
+{
+    while (1) {
+        EmitByte (FuncHiByte ());
+        if (CurTok.Tok != TOK_COMMA) {
+            break;
+        } else {
+            NextTok ();
+        }
+    }
 }
 
 
@@ -805,10 +1159,10 @@ static void DoI16 (void)
 /* Switch the index registers to 16 bit mode (assembler only) */
 {
     if (GetCPU() != CPU_65816) {
-       Error (ERR_816_MODE_ONLY);
+        Error ("Command is only valid in 65816 mode");
     } else {
-               /* Immidiate mode has two extension bytes */
-       ExtBytes [AMI_IMM_INDEX] = 2;
+        /* Immidiate mode has two extension bytes */
+        ExtBytes [AM65I_IMM_INDEX] = 2;
     }
 }
 
@@ -818,10 +1172,10 @@ static void DoI8 (void)
 /* Switch the index registers to 16 bit mode (assembler only) */
 {
     if (GetCPU() != CPU_65816) {
-       Error (ERR_816_MODE_ONLY);
+        Error ("Command is only valid in 65816 mode");
     } else {
-       /* Immidiate mode has one extension byte */
-       ExtBytes [AMI_IMM_INDEX] = 1;
+        /* Immidiate mode has one extension byte */
+        ExtBytes [AM65I_IMM_INDEX] = 1;
     }
 }
 
@@ -830,7 +1184,7 @@ static void DoI8 (void)
 static void DoImport (void)
 /* Import a symbol */
 {
-    ExportImport (SymImport, 0);
+    ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE);
 }
 
 
@@ -838,7 +1192,7 @@ static void DoImport (void)
 static void DoImportZP (void)
 /* Import a zero page symbol */
 {
-    ExportImport (SymImport, 1);
+    ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE);
 }
 
 
@@ -846,58 +1200,89 @@ static void DoImportZP (void)
 static void DoIncBin (void)
 /* Include a binary file */
 {
-    char Name [sizeof (SVal)];
+    StrBuf Name = STATIC_STRBUF_INITIALIZER;
+    struct stat StatBuf;
     long Start = 0L;
     long Count = -1L;
     long Size;
     FILE* F;
 
     /* Name must follow */
-    if (Tok != TOK_STRCON) {
-       ErrorSkip (ERR_STRCON_EXPECTED);
-       return;
+    if (CurTok.Tok != TOK_STRCON) {
+        ErrorSkip ("String constant expected");
+        return;
     }
-    strcpy (Name, SVal);
+    SB_Copy (&Name, &CurTok.SVal);
+    SB_Terminate (&Name);
     NextTok ();
 
     /* A starting offset may follow */
-    if (Tok == TOK_COMMA) {
-       NextTok ();
-       Start = ConstExpression ();
+    if (CurTok.Tok == TOK_COMMA) {
+        NextTok ();
+        Start = ConstExpression ();
 
-       /* And a length may follow */
-       if (Tok == TOK_COMMA) {
-           NextTok ();
-           Count = ConstExpression ();
-       }
+        /* And a length may follow */
+        if (CurTok.Tok == TOK_COMMA) {
+            NextTok ();
+            Count = ConstExpression ();
+        }
 
     }
 
     /* Try to open the file */
-    F = fopen (Name, "rb");
+    F = fopen (SB_GetConstBuf (&Name), "rb");
     if (F == 0) {
-               ErrorSkip (ERR_CANNOT_OPEN_INCLUDE, Name, strerror (errno));
-       return;
+
+        /* Search for the file in the binary include directory */
+        char* PathName = SearchFile (BinSearchPath, SB_GetConstBuf (&Name));
+        if (PathName == 0 || (F = fopen (PathName, "rb")) == 0) {
+            /* Not found or cannot open, print an error and bail out */
+            ErrorSkip ("Cannot open include file `%m%p': %s", &Name, strerror (errno));
+            xfree (PathName);
+            goto ExitPoint;
+        }
+
+        /* Remember the new file name */
+        SB_CopyStr (&Name, PathName);
+
+        /* Free the allocated memory */
+        xfree (PathName);
     }
 
     /* Get the size of the file */
     fseek (F, 0, SEEK_END);
     Size = ftell (F);
 
+    /* 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.
+     */
+    SB_Terminate (&Name);
+    if (FileStat (SB_GetConstBuf (&Name), &StatBuf) != 0) {
+        Fatal ("Cannot stat input file `%m%p': %s", &Name, strerror (errno));
+    }
+
+    /* Add the file to the input file table */
+    AddFile (&Name, FT_BINARY, Size, (unsigned long) StatBuf.st_mtime);
+
     /* If a count was not given, calculate it now */
     if (Count < 0) {
-       Count = Size - Start;
-       if (Count < 0) {
-           /* Nothing to read - flag this as a range error */
-           ErrorSkip (ERR_RANGE);
-           goto Done;
-       }
+        Count = Size - Start;
+        if (Count < 0) {
+            /* Nothing to read - flag this as a range error */
+            ErrorSkip ("Range error");
+            goto Done;
+        }
     } else {
-       /* Count was given, check if it is valid */
-       if (Start + Count > Size) {
-           ErrorSkip (ERR_RANGE);
-           goto Done;
-       }
+        /* Count was given, check if it is valid */
+        if (Start + Count > Size) {
+            ErrorSkip ("Range error");
+            goto Done;
+        }
     }
 
     /* Seek to the start position */
@@ -906,29 +1291,34 @@ static void DoIncBin (void)
     /* Read chunks and insert them into the output */
     while (Count > 0) {
 
-       unsigned char Buf [1024];
+        unsigned char Buf [1024];
 
-       /* Calculate the number of bytes to read */
-               size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
+        /* Calculate the number of bytes to read */
+        size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count;
 
-       /* Read chunk */
-       size_t BytesRead = fread (Buf, 1, BytesToRead, F);
-       if (BytesToRead != BytesRead) {
-           /* Some sort of error */
-           ErrorSkip (ERR_CANNOT_READ_INCLUDE, Name, strerror (errno));
-           break;
-       }
+        /* Read chunk */
+        size_t BytesRead = fread (Buf, 1, BytesToRead, F);
+        if (BytesToRead != BytesRead) {
+            /* Some sort of error */
+            ErrorSkip ("Cannot read from include file `%m%p': %s",
+                       &Name, strerror (errno));
+            break;
+        }
 
-       /* Insert it into the output */
-       EmitData (Buf, BytesRead);
+        /* Insert it into the output */
+        EmitData (Buf, BytesRead);
 
-       /* Keep the counters current */
-       Count -= BytesRead;
+        /* Keep the counters current */
+        Count -= BytesRead;
     }
 
 Done:
     /* Close the file, ignore errors since it's r/o */
     (void) fclose (F);
+
+ExitPoint:
+    /* Free string memory */
+    SB_Done (&Name);
 }
 
 
@@ -936,20 +1326,42 @@ Done:
 static void DoInclude (void)
 /* Include another file */
 {
-    char Name [MAX_STR_LEN+1];
-
     /* Name must follow */
-    if (Tok != TOK_STRCON) {
-       ErrorSkip (ERR_STRCON_EXPECTED);
+    if (CurTok.Tok != TOK_STRCON) {
+        ErrorSkip ("String constant expected");
     } else {
-       strcpy (Name, SVal);
-       NextTok ();
-       NewInputFile (Name);
+        SB_Terminate (&CurTok.SVal);
+        if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
+            /* Error opening the file, skip remainder of line */
+            SkipUntilSep ();
+        }
     }
 }
 
 
 
+static void DoInterruptor (void)
+/* Export a symbol as interruptor */
+{
+    StrBuf Name = STATIC_STRBUF_INITIALIZER;
+
+    /* Symbol name follows */
+    if (CurTok.Tok != TOK_IDENT) {
+        ErrorSkip ("Identifier expected");
+        return;
+    }
+    SB_Copy (&Name, &CurTok.SVal);
+    NextTok ();
+
+    /* Parse the remainder of the line and export the symbol */
+    ConDes (&Name, CD_TYPE_INT);
+
+    /* Free string memory */
+    SB_Done (&Name);
+}
+
+
+
 static void DoInvalid (void)
 /* Handle a token that is invalid here, since it should have been handled on
  * a much lower level of the expression hierarchy. Getting this sort of token
@@ -959,7 +1371,7 @@ static void DoInvalid (void)
  * an error in the assembler itself, while DoInvalid is.
  */
 {
-    Internal ("Unexpected token: %s", Keyword);
+    Internal ("Unexpected token: %m%p", &Keyword);
 }
 
 
@@ -981,14 +1393,28 @@ static void DoList (void)
 
     /* Manage the counter */
     if (List) {
-       EnableListing ();
+        EnableListing ();
     } else {
-       DisableListing ();
+        DisableListing ();
     }
 }
 
 
 
+static void DoLoBytes (void)
+/* Define bytes, extracting the lo byte from each expression in the list */
+{
+    while (1) {
+        EmitByte (FuncLoByte ());
+        if (CurTok.Tok != TOK_COMMA) {
+            break;
+        } else {
+            NextTok ();
+        }
+    }
+}
+
+
 static void DoListBytes (void)
 /* Set maximum number of bytes to list for one line */
 {
@@ -1000,15 +1426,15 @@ static void DoListBytes (void)
 static void DoLocalChar (void)
 /* Define the character that starts local labels */
 {
-    if (Tok != TOK_CHARCON) {
-       ErrorSkip (ERR_CHARCON_EXPECTED);
+    if (CurTok.Tok != TOK_CHARCON) {
+        ErrorSkip ("Character constant expected");
     } else {
-       if (IVal != '@' && IVal != '?') {
-           Error (ERR_ILLEGAL_LOCALSTART);
-       } else {
-           LocalStart = (char) IVal;
-               }
-       NextTok ();
+        if (CurTok.IVal != '@' && CurTok.IVal != '?') {
+            Error ("Invalid start character for locals");
+        } else {
+            LocalStart = (char) CurTok.IVal;
+        }
+        NextTok ();
     }
 }
 
@@ -1017,33 +1443,17 @@ static void DoLocalChar (void)
 static void DoMacPack (void)
 /* Insert a macro package */
 {
-    /* Macro package names */
-    static const char* Keys [] = {
-       "GENERIC",
-               "LONGBRANCH",
-    };
-
-    int Package;
-
     /* We expect an identifier */
-    if (Tok != TOK_IDENT) {
-       ErrorSkip (ERR_IDENT_EXPECTED);
-       return;
-    }
-
-    /* Map the keyword to a number */
-    Package = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]));
-    if (Package < 0) {
-       /* Not found */
-       ErrorSkip (ERR_ILLEGAL_MACPACK);
-       return;
+    if (CurTok.Tok != TOK_IDENT) {
+        ErrorSkip ("Identifier expected");
+    } else {
+        SB_AppendStr (&CurTok.SVal, ".mac");
+        SB_Terminate (&CurTok.SVal);
+        if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) {
+            /* Error opening the file, skip remainder of line */
+            SkipUntilSep ();
+        }
     }
-
-    /* Skip the package name */
-    NextTok ();
-
-    /* Insert the package */
-    InsertMacPack (Package);
 }
 
 
@@ -1059,7 +1469,7 @@ static void DoMacro (void)
 static void DoNull (void)
 /* Switch to the NULL segment */
 {
-    UseNullSeg ();
+    UseSeg (&NullSegDef);
 }
 
 
@@ -1069,10 +1479,10 @@ static void DoOrg (void)
 {
     long PC = ConstExpression ();
     if (PC < 0 || PC > 0xFFFFFF) {
-       Error (ERR_RANGE);
-       return;
+        Error ("Range error");
+        return;
     }
-    SetAbsPC (PC);
+    EnterAbsoluteMode (PC);
 }
 
 
@@ -1080,15 +1490,17 @@ static void DoOrg (void)
 static void DoOut (void)
 /* Output a string */
 {
-    if (Tok != TOK_STRCON) {
-       ErrorSkip (ERR_STRCON_EXPECTED);
+    if (CurTok.Tok != TOK_STRCON) {
+        ErrorSkip ("String constant expected");
     } else {
-       /* Output the string and be sure to flush the output to keep it in
-        * sync with any error messages if the output is redirected to a file.
-        */
-       printf ("%s\n", SVal);
-       fflush (stdout);
-       NextTok ();
+        /* Output the string and be sure to flush the output to keep it in
+         * sync with any error messages if the output is redirected to a file.
+         */
+        printf ("%.*s\n",
+                (int) SB_GetLen (&CurTok.SVal),
+                SB_GetConstBuf (&CurTok.SVal));
+        fflush (stdout);
+        NextTok ();
     }
 }
 
@@ -1126,15 +1538,121 @@ static void DoPageLength (void)
 
 
 
+static void DoPopCPU (void)
+/* Pop an old CPU setting from the CPU stack */
+{
+    /* Must have a CPU on the stack */
+    if (IS_IsEmpty (&CPUStack)) {
+        ErrorSkip ("CPU stack is empty");
+        return;
+    }
+
+    /* Set the CPU to the value popped from stack */
+    SetCPU (IS_Pop (&CPUStack));
+}
+
+
+
+static void DoPopSeg (void)
+/* Pop an old segment from the segment stack */
+{
+    SegDef* Def;
+
+    /* Must have a segment on the stack */
+    if (CollCount (&SegStack) == 0) {
+        ErrorSkip ("Segment stack is empty");
+        return;
+    }
+
+    /* Pop the last element */
+    Def = CollPop (&SegStack);
+
+    /* Restore this segment */
+    UseSeg (Def);
+
+    /* Delete the segment definition */
+    FreeSegDef (Def);
+}
+
+
+
 static void DoProc (void)
 /* Start a new lexical scope */
 {
-    if (Tok == TOK_IDENT) {
-       /* The new scope has a name */
-       SymDef (SVal, CurrentPC (), IsZPSeg (), 1);
-       NextTok ();
+    StrBuf Name = STATIC_STRBUF_INITIALIZER;
+    unsigned char AddrSize;
+    SymEntry* Sym = 0;
+
+
+    if (CurTok.Tok == TOK_IDENT) {
+
+        /* The new scope has a name. Remember it. */
+        SB_Copy (&Name, &CurTok.SVal);
+
+        /* Search for the symbol, generate a new one if needed */
+        Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW);
+
+        /* Skip the scope name */
+        NextTok ();
+
+        /* Read an optional address size specifier */
+        AddrSize = OptionalAddrSize ();
+
+        /* Mark the symbol as defined */
+        SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL);
+
+    } else {
+
+        /* A .PROC statement without a name */
+        Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE");
+        AnonName (&Name, "PROC");
+        AddrSize = ADDR_SIZE_DEFAULT;
+
+    }
+
+    /* Enter a new scope */
+    SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, Sym);
+
+    /* Free memory for Name */
+    SB_Done (&Name);
+}
+
+
+
+static void DoPSC02 (void)
+/* Switch to 65SC02 CPU */
+{
+    SetCPU (CPU_65SC02);
+}
+
+
+
+static void DoPushCPU (void)
+/* Push the current CPU setting onto the CPU stack */
+{
+    /* Can only push a limited size of segments */
+    if (IS_IsFull (&CPUStack)) {
+        ErrorSkip ("CPU stack overflow");
+        return;
+    }
+
+    /* Get the current segment and push it */
+    IS_Push (&CPUStack, GetCPU ());
+}
+
+
+
+static void DoPushSeg (void)
+/* Push the current segment onto the segment stack */
+{
+    /* Can only push a limited size of segments */
+    if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) {
+        ErrorSkip ("Segment stack overflow");
+        return;
     }
-    SymEnterLevel ();
+
+    /* Get the current segment and push it */
+    CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ()));
 }
 
 
@@ -1142,7 +1660,7 @@ static void DoProc (void)
 static void DoReloc (void)
 /* Enter relocatable mode */
 {
-    RelocMode = 1;
+    EnterRelocMode ();
 }
 
 
@@ -1163,26 +1681,26 @@ static void DoRes (void)
 
     Count = ConstExpression ();
     if (Count > 0xFFFF || Count < 0) {
-       ErrorSkip (ERR_RANGE);
-       return;
-    }
-    if (Tok == TOK_COMMA) {
-       NextTok ();
-       Val = ConstExpression ();
-       /* We need a byte value here */
-       if (!IsByteRange (Val)) {
-                   ErrorSkip (ERR_RANGE);
-           return;
-       }
-
-       /* Emit constant values */
-       while (Count--) {
-           Emit0 ((unsigned char) Val);
-       }
+        ErrorSkip ("Range error");
+        return;
+    }
+    if (CurTok.Tok == TOK_COMMA) {
+        NextTok ();
+        Val = ConstExpression ();
+        /* We need a byte value here */
+        if (!IsByteRange (Val)) {
+            ErrorSkip ("Range error");
+            return;
+        }
+
+        /* Emit constant values */
+        while (Count--) {
+            Emit0 ((unsigned char) Val);
+        }
 
     } else {
-       /* Emit fill fragments */
-       EmitFill (Count);
+        /* Emit fill fragments */
+        EmitFill (Count);
     }
 }
 
@@ -1191,7 +1709,39 @@ static void DoRes (void)
 static void DoROData (void)
 /* Switch to the r/o data segment */
 {
-    UseRODataSeg ();
+    UseSeg (&RODataSegDef);
+}
+
+
+
+static void DoScope (void)
+/* Start a local scope */
+{
+    StrBuf Name = STATIC_STRBUF_INITIALIZER;
+    unsigned char AddrSize;
+
+
+    if (CurTok.Tok == TOK_IDENT) {
+
+        /* The new scope has a name. Remember and skip it. */
+        SB_Copy (&Name, &CurTok.SVal);
+        NextTok ();
+
+    } else {
+
+        /* An unnamed scope */
+        AnonName (&Name, "SCOPE");
+
+    }
+
+    /* Read an optional address size specifier */
+    AddrSize = OptionalAddrSize ();
+
+    /* Enter the new scope */
+    SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, 0);
+
+    /* Free memory for Name */
+    SB_Done (&Name);
 }
 
 
@@ -1199,58 +1749,54 @@ static void DoROData (void)
 static void DoSegment (void)
 /* Switch to another segment */
 {
-    static const char* AttrTab [] = {
-       "ZEROPAGE", "DIRECT",
-       "ABSOLUTE",
-       "FAR", "LONG"
-    };
-    char Name [sizeof (SVal)];
-    int SegType;
+    StrBuf Name = STATIC_STRBUF_INITIALIZER;
+    SegDef Def;
 
-    if (Tok != TOK_STRCON) {
-       ErrorSkip (ERR_STRCON_EXPECTED);
+    if (CurTok.Tok != TOK_STRCON) {
+        ErrorSkip ("String constant expected");
     } else {
 
-       /* Save the name of the segment and skip it */
-       strcpy (Name, SVal);
-       NextTok ();
+        /* Save the name of the segment and skip it */
+        SB_Copy (&Name, &CurTok.SVal);
+        NextTok ();
 
-       /* Check for an optional segment attribute */
-       SegType = SEGTYPE_DEFAULT;
-       if (Tok == TOK_COMMA) {
-           NextTok ();
-           if (Tok != TOK_IDENT) {
-               ErrorSkip (ERR_IDENT_EXPECTED);
-           } else {
-               int Attr = GetSubKey (AttrTab, sizeof (AttrTab) / sizeof (AttrTab [0]));
-               switch (Attr) {
+        /* Use the name for the segment definition */
+        SB_Terminate (&Name);
+        Def.Name = SB_GetBuf (&Name);
 
-                   case 0:
-                   case 1:
-                       /* Zeropage */
-                       SegType = SEGTYPE_ZP;
-                       break;
+        /* Check for an optional address size modifier */
+        Def.AddrSize = OptionalAddrSize ();
 
-                   case 2:
-                       /* Absolute */
-                       SegType = SEGTYPE_ABS;
-                       break;
+        /* Set the segment */
+        UseSeg (&Def);
+    }
+
+    /* Free memory for Name */
+    SB_Done (&Name);
+}
 
-                   case 3:
-                   case 4:
-                       /* Far */
-                       SegType = SEGTYPE_FAR;
-                       break;
 
-                   default:
-                       Error (ERR_ILLEGAL_SEG_ATTR);
-               }
-               NextTok ();
-           }
-       }
 
-       /* Set the segment */
-       UseSeg (Name, SegType);
+static void DoSetCPU (void)
+/* Switch the CPU instruction set */
+{
+    /* We expect an identifier */
+    if (CurTok.Tok != TOK_STRCON) {
+        ErrorSkip ("String constant expected");
+    } else {
+        cpu_t CPU;
+
+        /* Try to find the CPU */
+        SB_Terminate (&CurTok.SVal);
+        CPU = FindCPU (SB_GetConstBuf (&CurTok.SVal));
+
+        /* Switch to the new CPU */
+        SetCPU (CPU);
+
+        /* Skip the identifier. If the CPU switch was successful, the scanner
+         * will treat the input now correctly for the new CPU.
+         */
+        NextTok ();
     }
 }
 
@@ -1264,10 +1810,72 @@ static void DoSmart (void)
 
 
 
-static void DoSunPlus (void)
-/* Switch to the SUNPLUS CPU */
+static void DoTag (void)
+/* Allocate space for a struct */
 {
-    SetCPU (CPU_SUNPLUS);
+    SymEntry* SizeSym;
+    long Size;
+
+    /* Read the struct name */
+    SymTable* Struct = ParseScopedSymTable ();
+
+    /* Check the supposed struct */
+    if (Struct == 0) {
+        ErrorSkip ("Unknown struct");
+        return;
+    }
+    if (GetSymTabType (Struct) != SCOPE_STRUCT) {
+        ErrorSkip ("Not a struct");
+        return;
+    }
+
+    /* Get the symbol that defines the size of the struct */
+    SizeSym = GetSizeOfScope (Struct);
+
+    /* Check if it does exist and if its value is known */
+    if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) {
+        ErrorSkip ("Size of struct/union is unknown");
+        return;
+    }
+
+    /* Optional multiplicator may follow */
+    if (CurTok.Tok == TOK_COMMA) {
+        long Multiplicator;
+        NextTok ();
+        Multiplicator = ConstExpression ();
+        /* Multiplicator must make sense */
+        if (Multiplicator <= 0) {
+            ErrorSkip ("Range error");
+            return;
+        }
+        Size *= Multiplicator;
+    }
+
+    /* Emit fill fragments */
+    EmitFill (Size);
+}
+
+
+
+static void DoUnDef (void)
+/* Undefine a define style macro */
+{
+    /* The function is called with the .UNDEF token in place, because we need
+     * to disable .define macro expansions before reading the next token.
+     * Otherwise the name of the macro would be expanded, so we would never
+     * see it.
+     */
+    DisableDefineStyleMacros ();
+    NextTok ();
+    EnableDefineStyleMacros ();
+
+    /* We expect an identifier */
+    if (CurTok.Tok != TOK_IDENT) {
+        ErrorSkip ("Identifier expected");
+    } else {
+        MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE);
+        NextTok ();
+    }
 }
 
 
@@ -1275,7 +1883,7 @@ static void DoSunPlus (void)
 static void DoUnexpected (void)
 /* Got an unexpected keyword */
 {
-    Error (ERR_UNEXPECTED, Keyword);
+    Error ("Unexpected `%m%p'", &Keyword);
     SkipUntilSep ();
 }
 
@@ -1284,11 +1892,11 @@ static void DoUnexpected (void)
 static void DoWarning (void)
 /* User warning */
 {
-    if (Tok != TOK_STRCON) {
-       ErrorSkip (ERR_STRCON_EXPECTED);
+    if (CurTok.Tok != TOK_STRCON) {
+        ErrorSkip ("String constant expected");
     } else {
-               Warning (WARN_USER, SVal);
-       SkipUntilSep ();
+        Warning (0, "User warning: %m%p", &CurTok.SVal);
+        SkipUntilSep ();
     }
 }
 
@@ -1297,14 +1905,29 @@ static void DoWarning (void)
 static void DoWord (void)
 /* Define words */
 {
+    /* Element type for the generated array */
+    static const char EType[1] = { GT_WORD };
+
+    /* Record type information */
+    Span* S = OpenSpan ();
+    StrBuf Type = STATIC_STRBUF_INITIALIZER;
+
+    /* Parse arguments */
     while (1) {
-               EmitWord (Expression ());
-       if (Tok != TOK_COMMA) {
-           break;
-       } else {
-           NextTok ();
-       }
+        EmitWord (BoundedExpr (Expression, 2));
+        if (CurTok.Tok != TOK_COMMA) {
+            break;
+        } else {
+            NextTok ();
+        }
     }
+
+    /* Close the span, then add type information to it */
+    S = CloseSpan (S);
+    SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType)));
+
+    /* Free the type string */
+    SB_Done (&Type);
 }
 
 
@@ -1312,159 +1935,188 @@ static void DoWord (void)
 static void DoZeropage (void)
 /* Switch to the zeropage segment */
 {
-    UseZeropageSeg ();
+    UseSeg (&ZeropageSegDef);
 }
 
 
 
 /*****************************************************************************/
-/*                               Table data                                 */
+/*                                Table data                                 */
 /*****************************************************************************/
 
 
 
 /* Control commands flags */
 enum {
-    ccNone     = 0x0000,               /* No special flags */
-    ccKeepToken        = 0x0001                /* Do not skip the current token */
+    ccNone      = 0x0000,               /* No special flags */
+    ccKeepToken = 0x0001                /* Do not skip the current token */
 };
 
 /* Control command table */
-struct CtrlDesc_ {
-    unsigned           Flags;                  /* Flags for this directive */
-    void               (*Handler) (void);      /* Command handler */
+typedef struct CtrlDesc CtrlDesc;
+struct CtrlDesc {
+    unsigned    Flags;                  /* Flags for this directive */
+    void        (*Handler) (void);      /* Command handler */
 };
-typedef struct CtrlDesc_ CtrlDesc;
 
-#define PSEUDO_COUNT   (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
+#define PSEUDO_COUNT    (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0]))
 static CtrlDesc CtrlCmdTab [] = {
-    { ccNone,          DoA16           },
-    { ccNone,          DoA8            },
-    { ccNone,          DoAddr          },      /* .ADDR */
-    { ccNone,          DoAlign         },
-    { ccNone,          DoASCIIZ        },
-    { ccNone,          DoAutoImport    },
-    { ccNone,          DoUnexpected    },      /* .BLANK */
-    { ccNone,          DoBss           },
-    { ccNone,          DoByte          },
-    { ccNone,          DoCase          },
-    { ccNone,                  DoCharMap       },
-    { ccNone,          DoCode          },
-    { ccNone,          DoUnexpected,   },      /* .CONCAT */
-    { ccNone,          DoConDes        },
-    { ccNone,          DoUnexpected    },      /* .CONST */
-    { ccNone,          DoConstructor   },
-    { ccNone,          DoUnexpected    },      /* .CPU */
-    { ccNone,          DoData          },
-    { ccNone,          DoDbg,          },
-    { ccNone,          DoDByt          },
-    { ccNone,          DoDebugInfo     },
-    { ccNone,          DoDefine        },
-    { ccNone,          DoUnexpected    },      /* .DEFINED */
-    { ccNone,          DoDestructor    },
-    { ccNone,          DoDWord         },
-    { ccKeepToken,     DoConditionals  },      /* .ELSE */
-    { ccKeepToken,     DoConditionals  },      /* .ELSEIF */
-    { ccNone,          DoEnd           },
-    { ccKeepToken,     DoConditionals  },      /* .ENDIF */
-    { ccNone,          DoUnexpected    },      /* .ENDMACRO */
-    { ccNone,          DoEndProc       },
-    { ccNone,          DoUnexpected    },      /* .ENDREPEAT */
-    { ccNone,          DoError         },
-    { ccNone,          DoExitMacro     },
-    { ccNone,          DoExport        },
-    { ccNone,          DoExportZP      },
-    { ccNone,          DoFarAddr       },
-    { ccNone,          DoFeature       },
-    { ccNone,          DoFileOpt       },
-    { ccNone,          DoUnexpected    },      /* .FORCEWORD */
-    { ccNone,          DoGlobal        },
-    { ccNone,          DoGlobalZP      },
-    { ccNone,          DoI16           },
-    { ccNone,          DoI8            },
-    { ccKeepToken,     DoConditionals  },      /* .IF */
-    { ccKeepToken,     DoConditionals  },      /* .IFBLANK */
-    { ccKeepToken,     DoConditionals  },      /* .IFCONST */
-    { ccKeepToken,     DoConditionals  },      /* .IFDEF */
-    { ccKeepToken,             DoConditionals  },      /* .IFNBLANK */
-    { ccKeepToken,     DoConditionals  },      /* .IFNCONST */
-    { ccKeepToken,     DoConditionals  },      /* .IFNDEF */
-    { ccKeepToken,     DoConditionals  },      /* .IFNREF */
-    { ccKeepToken,     DoConditionals  },      /* .IFP02 */
-    { ccKeepToken,     DoConditionals  },      /* .IFP816 */
-    { ccKeepToken,     DoConditionals  },      /* .IFPC02 */
-    { ccKeepToken,     DoConditionals  },      /* .IFREF */
-    { ccNone,          DoImport        },
-    { ccNone,          DoImportZP      },
-    { ccNone,          DoIncBin        },
-    { ccNone,          DoInclude       },
-    { ccNone,          DoInvalid       },      /* .LEFT */
-    { ccNone,          DoLineCont      },
-    { ccNone,          DoList          },
-    { ccNone,                  DoListBytes     },
-    { ccNone,          DoUnexpected    },      /* .LOCAL */
-    { ccNone,          DoLocalChar     },
-    { ccNone,          DoMacPack       },
-    { ccNone,          DoMacro         },
-    { ccNone,                  DoUnexpected    },      /* .MATCH */
-    { ccNone,                  DoInvalid       },      /* .MID */
-    { ccNone,          DoNull          },
-    { ccNone,          DoOrg           },
-    { ccNone,          DoOut           },
-    { ccNone,          DoP02           },
-    { ccNone,          DoP816          },
-    { ccNone,                  DoPageLength    },
-    { ccNone,          DoUnexpected    },      /* .PARAMCOUNT */
-    { ccNone,          DoPC02          },
-    { ccNone,          DoProc          },
-    { ccNone,          DoUnexpected    },      /* .REFERENCED */
-    { ccNone,          DoReloc         },
-    { ccNone,          DoRepeat        },
-    { ccNone,          DoRes           },
-    { ccNone,          DoInvalid       },      /* .RIGHT */
-    { ccNone,          DoROData        },
-    { ccNone,          DoSegment       },
-    { ccNone,          DoSmart         },
-    { ccNone,          DoUnexpected    },      /* .STRAT */
-    { ccNone,                  DoUnexpected    },      /* .STRING */
-    { ccNone,          DoUnexpected    },      /* .STRLEN */
-    { ccNone,          DoSunPlus       },
-    { ccNone,          DoUnexpected    },      /* .TCOUNT */
-    { ccNone,                  DoUnexpected    },      /* .TIME */
-    { ccNone,          DoWarning       },
-    { ccNone,          DoWord          },
-    { ccNone,                  DoUnexpected    },      /* .XMATCH */
-    { ccNone,          DoZeropage      },
+    { ccNone,           DoA16           },
+    { ccNone,           DoA8            },
+    { ccNone,           DoAddr          },      /* .ADDR */
+    { ccNone,           DoAlign         },
+    { ccNone,           DoASCIIZ        },
+    { ccNone,           DoAssert        },
+    { ccNone,           DoAutoImport    },
+    { ccNone,           DoUnexpected    },      /* .BANK */
+    { ccNone,           DoUnexpected    },      /* .BANKBYTE */
+    { ccNone,           DoBankBytes     },
+    { ccNone,           DoUnexpected    },      /* .BLANK */
+    { ccNone,           DoBss           },
+    { ccNone,           DoByte          },
+    { ccNone,           DoCase          },
+    { ccNone,           DoCharMap       },
+    { ccNone,           DoCode          },
+    { ccNone,           DoUnexpected,   },      /* .CONCAT */
+    { ccNone,           DoConDes        },
+    { ccNone,           DoUnexpected    },      /* .CONST */
+    { ccNone,           DoConstructor   },
+    { ccNone,           DoUnexpected    },      /* .CPU */
+    { ccNone,           DoData          },
+    { ccNone,           DoDbg,          },
+    { ccNone,           DoDByt          },
+    { ccNone,           DoDebugInfo     },
+    { ccNone,           DoDefine        },
+    { ccNone,           DoUnexpected    },      /* .DEFINED */
+    { ccNone,           DoDelMac        },
+    { ccNone,           DoDestructor    },
+    { ccNone,           DoDWord         },
+    { ccKeepToken,      DoConditionals  },      /* .ELSE */
+    { ccKeepToken,      DoConditionals  },      /* .ELSEIF */
+    { ccKeepToken,      DoEnd           },
+    { ccNone,           DoUnexpected    },      /* .ENDENUM */
+    { ccKeepToken,      DoConditionals  },      /* .ENDIF */
+    { ccNone,           DoUnexpected    },      /* .ENDMACRO */
+    { ccNone,           DoEndProc       },
+    { ccNone,           DoUnexpected    },      /* .ENDREPEAT */
+    { ccNone,           DoEndScope      },
+    { ccNone,           DoUnexpected    },      /* .ENDSTRUCT */
+    { ccNone,           DoUnexpected    },      /* .ENDUNION */
+    { ccNone,           DoEnum          },
+    { ccNone,           DoError         },
+    { ccNone,           DoExitMacro     },
+    { ccNone,           DoExport        },
+    { ccNone,           DoExportZP      },
+    { ccNone,           DoFarAddr       },
+    { ccNone,           DoFatal         },
+    { ccNone,           DoFeature       },
+    { ccNone,           DoFileOpt       },
+    { ccNone,           DoForceImport   },
+    { ccNone,           DoUnexpected    },      /* .FORCEWORD */
+    { ccNone,           DoGlobal        },
+    { ccNone,           DoGlobalZP      },
+    { ccNone,           DoUnexpected    },      /* .HIBYTE */
+    { ccNone,           DoHiBytes       },
+    { ccNone,           DoUnexpected    },      /* .HIWORD */
+    { ccNone,           DoI16           },
+    { ccNone,           DoI8            },
+    { ccNone,           DoUnexpected    },      /* .IDENT */
+    { ccKeepToken,      DoConditionals  },      /* .IF */
+    { ccKeepToken,      DoConditionals  },      /* .IFBLANK */
+    { ccKeepToken,      DoConditionals  },      /* .IFCONST */
+    { ccKeepToken,      DoConditionals  },      /* .IFDEF */
+    { ccKeepToken,      DoConditionals  },      /* .IFNBLANK */
+    { ccKeepToken,      DoConditionals  },      /* .IFNCONST */
+    { ccKeepToken,      DoConditionals  },      /* .IFNDEF */
+    { ccKeepToken,      DoConditionals  },      /* .IFNREF */
+    { ccKeepToken,      DoConditionals  },      /* .IFP02 */
+    { ccKeepToken,      DoConditionals  },      /* .IFP816 */
+    { ccKeepToken,      DoConditionals  },      /* .IFPC02 */
+    { ccKeepToken,      DoConditionals  },      /* .IFPSC02 */
+    { ccKeepToken,      DoConditionals  },      /* .IFREF */
+    { ccNone,           DoImport        },
+    { ccNone,           DoImportZP      },
+    { ccNone,           DoIncBin        },
+    { ccNone,           DoInclude       },
+    { ccNone,           DoInterruptor   },
+    { ccNone,           DoInvalid       },      /* .LEFT */
+    { ccNone,           DoLineCont      },
+    { ccNone,           DoList          },
+    { ccNone,           DoListBytes     },
+    { ccNone,           DoUnexpected    },      /* .LOBYTE */
+    { ccNone,           DoLoBytes       },
+    { ccNone,           DoUnexpected    },      /* .LOCAL */
+    { ccNone,           DoLocalChar     },
+    { ccNone,           DoUnexpected    },      /* .LOWORD */
+    { ccNone,           DoMacPack       },
+    { ccNone,           DoMacro         },
+    { ccNone,           DoUnexpected    },      /* .MATCH */
+    { ccNone,           DoUnexpected    },      /* .MAX */
+    { ccNone,           DoInvalid       },      /* .MID */
+    { ccNone,           DoUnexpected    },      /* .MIN */
+    { ccNone,           DoNull          },
+    { ccNone,           DoOrg           },
+    { ccNone,           DoOut           },
+    { ccNone,           DoP02           },
+    { ccNone,           DoP816          },
+    { ccNone,           DoPageLength    },
+    { ccNone,           DoUnexpected    },      /* .PARAMCOUNT */
+    { ccNone,           DoPC02          },
+    { ccNone,           DoPopCPU        },
+    { ccNone,           DoPopSeg        },
+    { ccNone,           DoProc          },
+    { ccNone,           DoPSC02         },
+    { ccNone,           DoPushCPU       },
+    { ccNone,           DoPushSeg       },
+    { ccNone,           DoUnexpected    },      /* .REFERENCED */
+    { ccNone,           DoReloc         },
+    { ccNone,           DoRepeat        },
+    { ccNone,           DoRes           },
+    { ccNone,           DoInvalid       },      /* .RIGHT */
+    { ccNone,           DoROData        },
+    { ccNone,           DoScope         },
+    { ccNone,           DoSegment       },
+    { ccNone,           DoUnexpected    },      /* .SET */
+    { ccNone,           DoSetCPU        },
+    { ccNone,           DoUnexpected    },      /* .SIZEOF */
+    { ccNone,           DoSmart         },
+    { ccNone,           DoUnexpected    },      /* .SPRINTF */
+    { ccNone,           DoUnexpected    },      /* .STRAT */
+    { ccNone,           DoUnexpected    },      /* .STRING */
+    { ccNone,           DoUnexpected    },      /* .STRLEN */
+    { ccNone,           DoStruct        },
+    { ccNone,           DoTag           },
+    { ccNone,           DoUnexpected    },      /* .TCOUNT */
+    { ccNone,           DoUnexpected    },      /* .TIME */
+    { ccKeepToken,      DoUnDef         },
+    { ccNone,           DoUnion         },
+    { ccNone,           DoUnexpected    },      /* .VERSION */
+    { ccNone,           DoWarning       },
+    { ccNone,           DoWord          },
+    { ccNone,           DoUnexpected    },      /* .XMATCH */
+    { ccNone,           DoZeropage      },
 };
 
 
 
 /*****************************************************************************/
-/*                                          Code                                    */
+/*                                   Code                                    */
 /*****************************************************************************/
 
 
 
-int TokIsPseudo (unsigned Tok)
-/* Return true if the given token is a pseudo instruction token */
-{
-    return (Tok >= TOK_FIRSTPSEUDO && Tok <= TOK_LASTPSEUDO);
-}
-
-
-
 void HandlePseudo (void)
 /* Handle a pseudo instruction */
 {
     CtrlDesc* D;
 
     /* Calculate the index into the table */
-    unsigned Index = Tok - TOK_FIRSTPSEUDO;
+    unsigned Index = CurTok.Tok - TOK_FIRSTPSEUDO;
 
     /* Safety check */
     if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) {
-       Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
-                 PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
+        Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n",
+                  (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1);
     }
     CHECK (Index < PSEUDO_COUNT);
 
@@ -1473,8 +2125,8 @@ void HandlePseudo (void)
 
     /* Remember the instruction, then skip it if needed */
     if ((D->Flags & ccKeepToken) == 0) {
-       strcpy (Keyword+1, SVal);
-       NextTok ();
+        SB_Copy (&Keyword, &CurTok.SVal);
+        NextTok ();
     }
 
     /* Call the handler */
@@ -1483,3 +2135,16 @@ void HandlePseudo (void)
 
 
 
+void CheckPseudo (void)
+/* Check if the stacks are empty at end of assembly */
+{
+    if (CollCount (&SegStack) != 0) {
+        Warning (1, "Segment stack is not empty");
+    }
+    if (!IS_IsEmpty (&CPUStack)) {
+        Warning (1, "CPU stack is not empty");
+    }
+}
+
+
+