Пример #1
0
void SkipUntilSep (void)
/* Skip tokens until we reach a line separator or end of file */
{
    while (!TokIsSep (CurTok.Tok)) {
        NextTok ();
    }
}
Пример #2
0
void ExpectSep (void)
/* Check if we've reached a line separator, and output an error if not. Do
 * not skip the line separator.
 */
{
    if (!TokIsSep (CurTok.Tok)) {
        ErrorSkip ("Unexpected trailing garbage characters");
    }
}
Пример #3
0
static TokList* CollectTokens (unsigned Start, unsigned Count)
/* Read a list of tokens that is optionally enclosed in curly braces and
 * terminated by a right paren. For all tokens starting at the one with index
 * Start, and ending at (Start+Count-1), place them into a token list, and
 * return this token list.
 */
{

    /* Create the token list */
    TokList* List = NewTokList ();

    /* Determine if the list is enclosed in curly braces. */
    token_t Term = GetTokListTerm (TOK_RPAREN);

    /* Read the token list */
    unsigned Current = 0;
    while (CurTok.Tok != Term) {

        /* Check for end of line or end of input */
        if (TokIsSep (CurTok.Tok)) {
            Error ("Unexpected end of line");
            return List;
        }

        /* Collect tokens in the given range */
        if (Current >= Start && Current < Start+Count) {
            /* Add the current token to the list */
            AddCurTok (List);
        }

        /* Get the next token */
        ++Current;
        NextTok ();
    }

    /* Eat the terminator token */
    NextTok ();

    /* If the list was enclosed in curly braces, we do expect now a right paren */
    if (Term == TOK_RCURLY) {
        ConsumeRParen ();
    }

    /* Return the list of collected tokens */
    return List;
}
Пример #4
0
void GetEA (EffAddr* A)
/* Parse an effective address, return the result in A */
{
    unsigned long Restrictions;
    token_t IndirectEnter;
    token_t IndirectLeave;
    const char* IndirectExpect;

    /* Choose syntax for indirection */
    if (BracketAsIndirect) {
        IndirectEnter = TOK_LBRACK;
        IndirectLeave = TOK_RBRACK;
        IndirectExpect = "']' expected";
    } else {
        IndirectEnter = TOK_LPAREN;
        IndirectLeave = TOK_RPAREN;
        IndirectExpect = "')' expected";
    }

    /* Clear the output struct */
    A->AddrModeSet = 0;
    A->Expr = 0;

    /* Handle an addressing size override */
    switch (CurTok.Tok) {
        case TOK_OVERRIDE_ZP:
            Restrictions = AM65_DIR | AM65_DIR_X | AM65_DIR_Y;
            NextTok ();
            break;

        case TOK_OVERRIDE_ABS:
            Restrictions = AM65_ABS | AM65_ABS_X | AM65_ABS_Y;
            NextTok ();
            break;

        case TOK_OVERRIDE_FAR:
            Restrictions = AM65_ABS_LONG | AM65_ABS_LONG_X;
            NextTok ();
            break;

        default:
            Restrictions = ~0UL;        /* None */
            break;
    }

    /* Parse the effective address */
    if (TokIsSep (CurTok.Tok)) {

        A->AddrModeSet = AM65_IMPLICIT;

    } else if (CurTok.Tok == TOK_HASH) {

        /* #val */
        NextTok ();
        A->Expr  = Expression ();
        A->AddrModeSet = AM65_ALL_IMM;

    } else if (CurTok.Tok == TOK_A) {

        NextTok ();
        A->AddrModeSet = AM65_ACCU;

    } else if (CurTok.Tok == IndirectEnter) {

        /* One of the indirect modes */
        NextTok ();
        A->Expr = Expression ();

        if (CurTok.Tok == TOK_COMMA) {

            /* (expr,X) or (rel,S),y */
            NextTok ();
            if (CurTok.Tok == TOK_X) {
                /* (adr,x) */
                NextTok ();
                A->AddrModeSet = AM65_ABS_X_IND | AM65_DIR_X_IND;
                Consume (IndirectLeave, IndirectExpect);
            } else if (CurTok.Tok == TOK_S) {
                /* (rel,s),y */
                NextTok ();
                A->AddrModeSet = AM65_STACK_REL_IND_Y;
                Consume (IndirectLeave, IndirectExpect);
                ConsumeComma ();
                Consume (TOK_Y, "`Y' expected");
            } else {
                Error ("Syntax error");
            }

        } else {

            /* (adr), (adr),y or (adr),z */
            Consume (IndirectLeave, IndirectExpect);
            if (CurTok.Tok == TOK_COMMA) {
                /* (adr),y */
                NextTok ();
                switch (CurTok.Tok) {
                case TOK_Z:
                    /* only set by scanner.c if in 4510-mode */
                    NextTok ();
                    A->AddrModeSet = AM65_DIR_IND;
                    break;
                default:
                    Consume (TOK_Y, "`Y' expected");
                    A->AddrModeSet = AM65_DIR_IND_Y;
                    break;
                }
            } else {
                /* (adr) */
                A->AddrModeSet = (CPU == CPU_4510) ? AM65_ABS_IND
                                                   : AM65_ABS_IND | AM65_ABS_IND_LONG | AM65_DIR_IND;
            }
        }

    } else if (CurTok.Tok == TOK_LBRACK) {

        /* Never executed if BracketAsIndirect feature is enabled. */
        /* [dir] or [dir],y */
        NextTok ();
        A->Expr = Expression ();
        Consume (TOK_RBRACK, "']' expected");
        if (CurTok.Tok == TOK_COMMA) {
            /* [dir],y */
            NextTok ();
            Consume (TOK_Y, "`Y' expected");
            A->AddrModeSet = AM65_DIR_IND_LONG_Y;
        } else {
            /* [dir] */
            A->AddrModeSet = AM65_DIR_IND_LONG | AM65_ABS_IND_LONG;
        }

    } else {

        /* Remaining stuff:
        **
        ** adr
        ** adr,x
        ** adr,y
        ** adr,s
        */
        A->Expr = Expression ();

        if (CurTok.Tok == TOK_COMMA) {

            NextTok ();
            switch (CurTok.Tok) {

                case TOK_X:
                    A->AddrModeSet = AM65_ABS_LONG_X | AM65_ABS_X | AM65_DIR_X;
                    NextTok ();
                    break;

                case TOK_Y:
                    A->AddrModeSet = AM65_ABS_Y | AM65_DIR_Y;
                    NextTok ();
                    break;

                case TOK_S:
                    A->AddrModeSet = AM65_STACK_REL;
                    NextTok ();
                    break;

                default:
                    Error ("Syntax error");

            }

        } else {

            A->AddrModeSet = AM65_ABS_LONG | AM65_ABS | AM65_DIR;

        }
    }

    /* Apply addressing mode overrides */
    A->AddrModeSet &= Restrictions;
}
Пример #5
0
void DoConditionals (void)
/* Catch all for conditional directives */
{
    IfDesc* D;

    do {

        switch (CurTok.Tok) {

            case TOK_ELSE:
                D = GetCurrentIf ();

                /* Allow an .ELSE */
                ElseClause (D, ".ELSE");

                /* Remember the data for the .ELSE */
                if (D) {
                    ReleaseFullLineInfo (&D->LineInfos);
                    GetFullLineInfo (&D->LineInfos);
                    D->Name = ".ELSE";
                }

                /* Calculate the new overall condition */
                CalcOverallIfCond ();

                /* Skip .ELSE */
                NextTok ();
                ExpectSep ();
                break;

            case TOK_ELSEIF:
                D = GetCurrentIf ();
                /* Handle as if there was an .ELSE first */
                ElseClause (D, ".ELSEIF");

                /* Calculate the new overall if condition */
                CalcOverallIfCond ();

                /* Allocate and prepare a new descriptor */
                D = AllocIf (".ELSEIF", 0);
                NextTok ();

                /* Ignore the new condition if we are inside a false .ELSE
                ** branch. This way we won't get any errors about undefined
                ** symbols or similar...
                */
                if (IfCond) {
                    SetIfCond (D, ConstExpression ());
                    ExpectSep ();
                }

                /* Get the new overall condition */
                CalcOverallIfCond ();
                break;

            case TOK_ENDIF:
                /* We're done with this .IF.. - remove the descriptor(s) */
                FreeIf ();

                /* Be sure not to read the next token until the .IF stack
                ** has been cleanup up, since we may be at end of file.
                */
                NextTok ();
                ExpectSep ();

                /* Get the new overall condition */
                CalcOverallIfCond ();
                break;

            case TOK_IF:
                D = AllocIf (".IF", 1);
                NextTok ();
                if (IfCond) {
                    SetIfCond (D, ConstExpression ());
                    ExpectSep ();
                }
                CalcOverallIfCond ();
                break;

            case TOK_IFBLANK:
                D = AllocIf (".IFBLANK", 1);
                NextTok ();
                if (IfCond) {
                    if (TokIsSep (CurTok.Tok)) {
                        SetIfCond (D, 1);
                    } else {
                        SetIfCond (D, 0);
                        SkipUntilSep ();
                    }
                }
                CalcOverallIfCond ();
                break;

            case TOK_IFCONST:
                D = AllocIf (".IFCONST", 1);
                NextTok ();
                if (IfCond) {
                    ExprNode* Expr = Expression();
                    SetIfCond (D, IsConstExpr (Expr, 0));
                    FreeExpr (Expr);
                    ExpectSep ();
                }
                CalcOverallIfCond ();
                break;

            case TOK_IFDEF:
                D = AllocIf (".IFDEF", 1);
                NextTok ();
                if (IfCond) {
                    SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
                    SetIfCond (D, Sym != 0 && SymIsDef (Sym));
                }
                CalcOverallIfCond ();
                break;

            case TOK_IFNBLANK:
                D = AllocIf (".IFNBLANK", 1);
                NextTok ();
                if (IfCond) {
                    if (TokIsSep (CurTok.Tok)) {
                        SetIfCond (D, 0);
                    } else {
                        SetIfCond (D, 1);
                        SkipUntilSep ();
                    }
                }
                CalcOverallIfCond ();
                break;

            case TOK_IFNCONST:
                D = AllocIf (".IFNCONST", 1);
                NextTok ();
                if (IfCond) {
                    ExprNode* Expr = Expression();
                    SetIfCond (D, !IsConstExpr (Expr, 0));
                    FreeExpr (Expr);
                    ExpectSep ();
                }
                CalcOverallIfCond ();
                break;

            case TOK_IFNDEF:
                D = AllocIf (".IFNDEF", 1);
                NextTok ();
                if (IfCond) {
                    SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
                    SetIfCond (D, Sym == 0 || !SymIsDef (Sym));
                    ExpectSep ();
                }
                CalcOverallIfCond ();
                break;

            case TOK_IFNREF:
                D = AllocIf (".IFNREF", 1);
                NextTok ();
                if (IfCond) {
                    SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
                    SetIfCond (D, Sym == 0 || !SymIsRef (Sym));
                    ExpectSep ();
                }
                CalcOverallIfCond ();
                break;

            case TOK_IFP02:
                D = AllocIf (".IFP02", 1);
                NextTok ();
                if (IfCond) {
                    SetIfCond (D, GetCPU() == CPU_6502);
                }
                ExpectSep ();
                CalcOverallIfCond ();
                break;

            case TOK_IFP4510:
                D = AllocIf (".IFP4510", 1);
                NextTok ();
                if (IfCond) {
                    SetIfCond (D, GetCPU() == CPU_4510);
                }
                ExpectSep ();
                CalcOverallIfCond ();
                break;

            case TOK_IFP816:
                D = AllocIf (".IFP816", 1);
                NextTok ();
                if (IfCond) {
                    SetIfCond (D, GetCPU() == CPU_65816);
                }
                ExpectSep ();
                CalcOverallIfCond ();
                break;

            case TOK_IFPC02:
                D = AllocIf (".IFPC02", 1);
                NextTok ();
                if (IfCond) {
                    SetIfCond (D, GetCPU() == CPU_65C02);
                }
                ExpectSep ();
                CalcOverallIfCond ();
                break;

            case TOK_IFPSC02:
                D = AllocIf (".IFPSC02", 1);
                NextTok ();
                if (IfCond) {
                    SetIfCond (D, GetCPU() == CPU_65SC02);
                }
                ExpectSep ();
                CalcOverallIfCond ();
                break;

            case TOK_IFREF:
                D = AllocIf (".IFREF", 1);
                NextTok ();
                if (IfCond) {
                    SymEntry* Sym = ParseAnySymName (SYM_FIND_EXISTING);
                    SetIfCond (D, Sym != 0 && SymIsRef (Sym));
                    ExpectSep ();
                }
                CalcOverallIfCond ();
                break;

            default:
                /* Skip tokens */
                NextTok ();

        }

    } while (IfCond == 0 && CurTok.Tok != TOK_EOF);
}
Пример #6
0
void GetEA (EffAddr* A)
/* Parse an effective address, return the result in A */
{
    unsigned long Restrictions;

    /* Clear the output struct */
    A->AddrModeSet = 0;
    A->Expr = 0;

    /* Handle an addressing size override */
    switch (CurTok.Tok) {
        case TOK_OVERRIDE_ZP:
            Restrictions = AM65_DIR | AM65_DIR_X | AM65_DIR_Y;
            NextTok ();
            break;

        case TOK_OVERRIDE_ABS:
            Restrictions = AM65_ABS | AM65_ABS_X | AM65_ABS_Y;
            NextTok ();
            break;

        case TOK_OVERRIDE_FAR:
            Restrictions = AM65_ABS_LONG | AM65_ABS_LONG_X;
            NextTok ();
            break;

        default:
            Restrictions = ~0UL;        /* None */
            break;
    }

    /* Parse the effective address */
    if (TokIsSep (CurTok.Tok)) {

	A->AddrModeSet = AM65_IMPLICIT;

    } else if (CurTok.Tok == TOK_HASH) {

	/* #val */
	NextTok ();
	A->Expr  = Expression ();
	A->AddrModeSet = AM65_ALL_IMM;

    } else if (CurTok.Tok == TOK_A) {

	NextTok ();
	A->AddrModeSet = AM65_ACCU;

    } else if (CurTok.Tok == TOK_LBRACK) {

	/* [dir] or [dir],y */
	NextTok ();
	A->Expr = Expression ();
	Consume (TOK_RBRACK, "']' expected");
	if (CurTok.Tok == TOK_COMMA) {
	    /* [dir],y */
	    NextTok ();
	    Consume (TOK_Y, "`Y' expected");
	    A->AddrModeSet = AM65_DIR_IND_LONG_Y;
	} else {
	    /* [dir] */
	    A->AddrModeSet = AM65_DIR_IND_LONG;
	}

    } else if (CurTok.Tok == TOK_LPAREN) {

    	/* One of the indirect modes */
    	NextTok ();
    	A->Expr = Expression ();

    	if (CurTok.Tok == TOK_COMMA) {

    	    /* (expr,X) or (rel,S),y */
    	    NextTok ();
    	    if (CurTok.Tok == TOK_X) {
	   	/* (adr,x) */
    	   	NextTok ();
       	       	A->AddrModeSet = AM65_ABS_X_IND | AM65_DIR_X_IND;
    	       	ConsumeRParen ();
    	    } else if (CurTok.Tok == TOK_S) {
	   	/* (rel,s),y */
    	 	NextTok ();
    	 	A->AddrModeSet = AM65_STACK_REL_IND_Y;
    	 	ConsumeRParen ();
    	 	ConsumeComma ();
    	 	Consume (TOK_Y, "`Y' expected");
    	    } else {
    	 	Error ("Syntax error");
    	    }

       	} else {

	    /* (adr) or (adr),y */
    	    ConsumeRParen ();
    	    if (CurTok.Tok == TOK_COMMA) {
		/* (adr),y */
    	 	NextTok ();
    	 	Consume (TOK_Y, "`Y' expected");
    	 	A->AddrModeSet = AM65_DIR_IND_Y;
    	    } else {
		/* (adr) */
    	 	A->AddrModeSet = AM65_ABS_IND | AM65_DIR_IND;
    	    }
    	}

    } else {

	/* Remaining stuff:
	 *
	 * adr
	 * adr,x
	 * adr,y
	 * adr,s
	 */
       	A->Expr = Expression ();

        if (CurTok.Tok == TOK_COMMA) {

            NextTok ();
            switch (CurTok.Tok) {

                case TOK_X:
                    A->AddrModeSet = AM65_ABS_LONG_X | AM65_ABS_X | AM65_DIR_X;
                    NextTok ();
                    break;

                case TOK_Y:
                    A->AddrModeSet = AM65_ABS_Y | AM65_DIR_Y;
                    NextTok ();
                    break;

                case TOK_S:
                    A->AddrModeSet = AM65_STACK_REL;
                    NextTok ();
                    break;

                default:
                    Error ("Syntax error");

            }

        } else {

            A->AddrModeSet = AM65_ABS_LONG | AM65_ABS | AM65_DIR;

        }
    }

    /* Apply addressing mode overrides */
    A->AddrModeSet &= Restrictions;
}
Пример #7
0
static void StartExpDefine (MacExp* E)
/* Start expanding a DEFINE style macro */
{
    /* A define style macro must be called with as many actual parameters
     * as there are formal ones. Get the parameter count.
     */
    unsigned Count = E->M->ParamCount;

    /* Skip the current token */
    NextTok ();

    /* Read the actual parameters */
    while (Count--) {

        TokNode*   Last;

        /* The macro may optionally be enclosed in curly braces */
        token_t Term = GetTokListTerm (TOK_COMMA);

        /* Check if there is really a parameter */
        if (TokIsSep (CurTok.Tok) || CurTok.Tok == Term) {
            ErrorSkip ("Macro parameter #%u is empty", E->ParamCount+1);
            FreeMacExp (E);
            return;
        }

        /* Read tokens for one parameter */
        Last = 0;
        do {

            TokNode* T;

            /* Get the next token in a node */
            T = NewTokNode ();

            /* Insert it into the list */
            if (Last == 0) {
                E->Params [E->ParamCount] = T;
            } else {
                Last->Next = T;
            }
            Last = T;

            /* And skip it... */
            NextTok ();

        } while (CurTok.Tok != Term && !TokIsSep (CurTok.Tok));

        /* One parameter more */
        ++E->ParamCount;

        /* If the macro argument was enclosed in curly braces, end-of-line
         * is an error. Skip the closing curly brace.
         */
        if (Term == TOK_RCURLY) {
            if (TokIsSep (CurTok.Tok)) {
                Error ("End of line encountered within macro argument");
                break;
            }
            NextTok ();
        }

        /* Check for a comma */
        if (Count > 0) {
            if (CurTok.Tok == TOK_COMMA) {
                NextTok ();
            } else {
                Error ("`,' expected");
            }
        }
    }

    /* Macro expansion will overwrite the current token. This is a problem
     * for define style macros since these are called from the scanner level.
     * To avoid it, remember the current token and re-insert it, once macro
     * expansion is done.
     */
    E->Final = NewTokNode ();

    /* Insert a new token input function */
    PushInput (MacExpand, E, ".DEFINE");
}
Пример #8
0
static void StartExpClassic (MacExp* E)
/* Start expanding a classic macro */
{
    token_t     Term;

    /* Skip the macro name */
    NextTok ();

    /* Read the actual parameters */
    while (!TokIsSep (CurTok.Tok)) {

        TokNode* Last;

        /* Check for maximum parameter count */
        if (E->ParamCount >= E->M->ParamCount) {
            ErrorSkip ("Too many macro parameters");
            break;
        }

        /* The macro may optionally be enclosed in curly braces */
        Term = GetTokListTerm (TOK_COMMA);

        /* Read tokens for one parameter, accept empty params */
        Last = 0;
        while (CurTok.Tok != Term && CurTok.Tok != TOK_SEP) {

            TokNode* T;

            /* Check for end of file */
            if (CurTok.Tok == TOK_EOF) {
                Error ("Unexpected end of file");
                FreeMacExp (E);
                return;
            }

            /* Get the next token in a node */
            T = NewTokNode ();

            /* Insert it into the list */
            if (Last == 0) {
                E->Params [E->ParamCount] = T;
            } else {
                Last->Next = T;
            }
            Last = T;

            /* And skip it... */
            NextTok ();
        }

        /* One parameter more */
        ++E->ParamCount;

        /* If the macro argument was enclosed in curly braces, end-of-line
         * is an error. Skip the closing curly brace.
         */
        if (Term == TOK_RCURLY) {
            if (CurTok.Tok == TOK_SEP) {
                Error ("End of line encountered within macro argument");
                break;
            }
            NextTok ();
        }

        /* Check for a comma */
        if (CurTok.Tok == TOK_COMMA) {
            NextTok ();
        } else {
            break;
        }
    }

    /* We must be at end of line now, otherwise something is wrong */
    ExpectSep ();

    /* Insert a new token input function */
    PushInput (MacExpand, E, ".MACRO");
}
Пример #9
0
void MacDef (unsigned Style)
/* Parse a macro definition */
{
    Macro* M;
    TokNode* N;
    int HaveParams;

    /* We expect a macro name here */
    if (CurTok.Tok != TOK_IDENT) {
        Error ("Identifier expected");
        MacSkipDef (Style);
        return;
    } else if (!UbiquitousIdents && FindInstruction (&CurTok.SVal) >= 0) {
        /* The identifier is a name of a 6502 instruction, which is not
         * allowed if not explicitly enabled.
         */
        Error ("Cannot use an instruction as macro name");
        MacSkipDef (Style);
        return;
    }

    /* Did we already define that macro? */
    if (HT_Find (&MacroTab, &CurTok.SVal) != 0) {
        /* Macro is already defined */
        Error ("A macro named `%m%p' is already defined", &CurTok.SVal);
        /* Skip tokens until we reach the final .endmacro */
        MacSkipDef (Style);
        return;
    }

    /* Define the macro */
    M = NewMacro (&CurTok.SVal, Style);

    /* Switch to raw token mode and skip the macro name */
    EnterRawTokenMode ();
    NextTok ();

    /* If we have a DEFINE style macro, we may have parameters in braces,
     * otherwise we may have parameters without braces.
     */
    if (Style == MAC_STYLE_CLASSIC) {
        HaveParams = 1;
    } else {
        if (CurTok.Tok == TOK_LPAREN) {
            HaveParams = 1;
            NextTok ();
        } else {
            HaveParams = 0;
        }
    }

    /* Parse the parameter list */
    if (HaveParams) {

        while (CurTok.Tok == TOK_IDENT) {

            /* Create a struct holding the identifier */
            IdDesc* I = NewIdDesc (&CurTok.SVal);

            /* Insert the struct into the list, checking for duplicate idents */
            if (M->ParamCount == 0) {
                M->Params = I;
            } else {
                IdDesc* List = M->Params;
                while (1) {
                    if (SB_Compare (&List->Id, &CurTok.SVal) == 0) {
                        Error ("Duplicate symbol `%m%p'", &CurTok.SVal);
                    }
                    if (List->Next == 0) {
                        break;
                    } else {
                        List = List->Next;
                    }
                }
                List->Next = I;
            }
            ++M->ParamCount;

            /* Skip the name */
            NextTok ();

            /* Maybe there are more params... */
            if (CurTok.Tok == TOK_COMMA) {
                NextTok ();
            } else {
                break;
            }
        }
    }

    /* For class macros, we expect a separator token, for define style macros,
     * we expect the closing paren.
     */
    if (Style == MAC_STYLE_CLASSIC) {
        ConsumeSep ();
    } else if (HaveParams) {
        ConsumeRParen ();
    }

    /* Preparse the macro body. We will read the tokens until we reach end of
     * file, or a .endmacro (or end of line for DEFINE style macros) and store
     * them into an token list internal to the macro. For classic macros, there
     * the .LOCAL command is detected and removed at this time.
     */
    while (1) {

        /* Check for end of macro */
        if (Style == MAC_STYLE_CLASSIC) {
            /* In classic macros, only .endmacro is allowed */
            if (CurTok.Tok == TOK_ENDMACRO) {
                /* Done */
                break;
            }
            /* May not have end of file in a macro definition */
            if (CurTok.Tok == TOK_EOF) {
                Error ("`.ENDMACRO' expected");
                goto Done;
            }
        } else {
            /* Accept a newline or end of file for new style macros */
            if (TokIsSep (CurTok.Tok)) {
                break;
            }
        }

        /* Check for a .LOCAL declaration */
        if (CurTok.Tok == TOK_LOCAL && Style == MAC_STYLE_CLASSIC) {

            while (1) {

                IdDesc* I;

                /* Skip .local or comma */
                NextTok ();

                /* Need an identifer */
                if (CurTok.Tok != TOK_IDENT && CurTok.Tok != TOK_LOCAL_IDENT) {
                    Error ("Identifier expected");
                    SkipUntilSep ();
                    break;
                }

                /* Put the identifier into the locals list and skip it */
                I = NewIdDesc (&CurTok.SVal);
                I->Next = M->Locals;
                M->Locals = I;
                ++M->LocalCount;
                NextTok ();

                /* Check for end of list */
                if (CurTok.Tok != TOK_COMMA) {
                    break;
                }

            }

            /* We need end of line after the locals */
            ConsumeSep ();
            continue;
        }

        /* Create a token node for the current token */
        N = NewTokNode ();

        /* If the token is an identifier, check if it is a local parameter */
        if (CurTok.Tok == TOK_IDENT) {
            unsigned Count = 0;
            IdDesc* I = M->Params;
            while (I) {
                if (SB_Compare (&I->Id, &CurTok.SVal) == 0) {
                    /* Local param name, replace it */
                    N->T.Tok  = TOK_MACPARAM;
                    N->T.IVal = Count;
                    break;
                }
                ++Count;
                I = I->Next;
            }
        }

        /* Insert the new token in the list */
        if (M->TokCount == 0) {
            /* First token */
            M->TokRoot = M->TokLast = N;
        } else {
            /* We have already tokens */
            M->TokLast->Next = N;
            M->TokLast = N;
        }
        ++M->TokCount;

        /* Read the next token */
        NextTok ();
    }

    /* Skip the .endmacro for a classic macro */
    if (Style == MAC_STYLE_CLASSIC) {
        NextTok ();
    }

    /* Reset the Incomplete flag now that parsing is done */
    M->Incomplete = 0;

Done:
    /* Switch out of raw token mode */
    LeaveRawTokenMode ();
}