X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fasmstmt.c;h=0644e802980e5dde69f9c4f72e1b9e54755de81d;hb=73dfa23c987d8a7f1154801b85c171f9e01dcd58;hp=76f5596d92a4b75971ef84016a60f3446e99eaf9;hpb=56b8ead1128434d05e0998e672e9e6d449f46d70;p=cc65 diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index 76f5596d9..0644e8029 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -35,21 +35,322 @@ #include -/* cc65 */ +/* common */ +#include "xsprintf.h" + +/* cc65 */ #include "codegen.h" +#include "datatype.h" #include "error.h" +#include "expr.h" +#include "function.h" #include "litpool.h" #include "scanner.h" +#include "symtab.h" #include "asmstmt.h" /*****************************************************************************/ -/* Code */ +/* Code */ /*****************************************************************************/ +static void AsmRangeError (unsigned Arg) +/* Print a diagnostic about a range error in the argument with the given number */ +{ + Error ("Range error in argument %u", Arg); +} + + + +static void AsmErrorSkip (void) +/* Called in case of an error, skips tokens until the closing paren or a + * semicolon is reached. + */ +{ + static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI }; + SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0])); +} + + + +static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) +/* Find the symbol with the name currently in NextTok. The symbol must be of + * the given type. On errors, NULL is returned. + */ +{ + SymEntry* Sym; + + /* We expect an argument separated by a comma */ + ConsumeComma (); + + /* Argument must be an identifier */ + if (CurTok.Tok != TOK_IDENT) { + Error ("Identifier expected for argument %u", Arg); + AsmErrorSkip (); + return 0; + } + + /* Get a pointer to the symbol table entry */ + Sym = FindSym (CurTok.Ident); + + /* Did we find a symbol with this name? */ + if (Sym == 0) { + Error ("Undefined symbol `%s' for argument %u", CurTok.Ident, Arg); + AsmErrorSkip (); + return 0; + } + + /* We found the symbol - skip the name token */ + NextToken (); + + /* Check if we have a global symbol */ + if ((Sym->Flags & Type) != Type) { + Error ("Type of argument %u differs from format specifier", Arg); + AsmErrorSkip (); + return 0; + } + + /* Mark the symbol as referenced */ + Sym->Flags |= SC_REF; + + /* Return it */ + return Sym; +} + + + +static void ParseByteArg (StrBuf* T, unsigned Arg) +/* Parse the %b format specifier */ +{ + ExprDesc Expr; + char Buf [16]; + + /* We expect an argument separated by a comma */ + ConsumeComma (); + + /* Evaluate the expression */ + ConstSubExpr (hie1, &Expr); + + /* Check the range but allow negative values if the type is signed */ + if (IsSignUnsigned (Expr.Type)) { + if (Expr.ConstVal < 0 || Expr.ConstVal > 0xFF) { + AsmRangeError (Arg); + Expr.ConstVal = 0; + } + } else { + if (Expr.ConstVal < -128 || Expr.ConstVal > 127) { + AsmRangeError (Arg); + Expr.ConstVal = 0; + } + } + + /* Convert into a hex number */ + xsprintf (Buf, sizeof (Buf), "$%02lX", Expr.ConstVal & 0xFF); + + /* Add the number to the target buffer */ + SB_AppendStr (T, Buf); +} + + + +static void ParseWordArg (StrBuf* T, unsigned Arg) +/* Parse the %w format specifier */ +{ + ExprDesc Expr; + char Buf [16]; + + /* We expect an argument separated by a comma */ + ConsumeComma (); + + /* Evaluate the expression */ + ConstSubExpr (hie1, &Expr); + + /* Check the range but allow negative values if the type is signed */ + if (IsSignUnsigned (Expr.Type)) { + if (Expr.ConstVal < 0 || Expr.ConstVal > 0xFFFF) { + AsmRangeError (Arg); + Expr.ConstVal = 0; + } + } else { + if (Expr.ConstVal < -32768 || Expr.ConstVal > 32767) { + AsmRangeError (Arg); + Expr.ConstVal = 0; + } + } + + /* Convert into a hex number */ + xsprintf (Buf, sizeof (Buf), "$%04lX", Expr.ConstVal & 0xFFFF); + + /* Add the number to the target buffer */ + SB_AppendStr (T, Buf); +} + + + +static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused))) +/* Parse the %l format specifier */ +{ + ExprDesc Expr; + char Buf [16]; + + /* We expect an argument separated by a comma */ + ConsumeComma (); + + /* Evaluate the expression */ + ConstSubExpr (hie1, &Expr); + + /* Convert into a hex number */ + xsprintf (Buf, sizeof (Buf), "$%08lX", Expr.ConstVal & 0xFFFFFFFF); + + /* Add the number to the target buffer */ + SB_AppendStr (T, Buf); +} + + + +static void ParseGVarArg (StrBuf* T, unsigned Arg) +/* Parse the %v format specifier */ +{ + /* Parse the symbol name parameter and check the type */ + SymEntry* Sym = AsmGetSym (Arg, SC_STATIC); + if (Sym == 0) { + /* Some sort of error */ + return; + } + + /* Check for external linkage */ + if (Sym->Flags & (SC_EXTERN | SC_STORAGE)) { + /* External linkage */ + /* ### FIXME: Asm name should be generated by codegen */ + SB_AppendChar (T, '_'); + SB_AppendStr (T, Sym->Name); + } else { + /* Static variable */ + char Buf [16]; + xsprintf (Buf, sizeof (Buf), "L%04X", Sym->V.Label); + SB_AppendStr (T, Buf); + } +} + + + +static void ParseLVarArg (StrBuf* T, unsigned Arg) +/* Parse the %o format specifier */ +{ + unsigned Offs; + char Buf [16]; + + /* Parse the symbol name parameter and check the type */ + SymEntry* Sym = AsmGetSym (Arg, SC_AUTO); + if (Sym == 0) { + /* Some sort of error */ + return; + } + + /* The symbol may be a parameter to a variadic function. In this case, we + * don't have a fixed stack offset, so check it and bail out with an error + * if this is the case. + */ + if ((Sym->Flags & SC_PARAM) == SC_PARAM && F_IsVariadic (CurrentFunc)) { + Error ("Argument %u has no fixed stack offset", Arg); + AsmErrorSkip (); + return; + } + + /* Calculate the current offset from SP */ + Offs = Sym->V.Offs - oursp; + + /* Output the offset */ + xsprintf (Buf, sizeof (Buf), (Offs > 0xFF)? "$%04X" : "$%02X", Offs); + SB_AppendStr (T, Buf); +} + + + +static void ParseAsm (void) +/* Parse the contents of the ASM statement */ +{ + unsigned Arg; + char C; + + /* Create a target string buffer */ + StrBuf T = AUTO_STRBUF_INITIALIZER; + + /* Create a string buffer from the string literal */ + StrBuf S = AUTO_STRBUF_INITIALIZER; + GetLiteralStrBuf (&S, CurTok.IVal); + + /* Reset the string pointer, effectivly clearing the string from the + * string table. Since we're working with one token lookahead, this + * will fail if the next token is also a string token, but that's a + * syntax error anyway, because we expect a right paren. + */ + ResetLiteralPoolOffs (CurTok.IVal); + + /* Skip the string token */ + NextToken (); + + /* Parse the statement. It may contain several lines and one or more + * of the following place holders: + * %b - Numerical 8 bit value + * %w - Numerical 16 bit value + * %l - Numerical 32 bit value + * %v - Assembler name of a (global) variable + * %o - Stack offset of a (local) variable + * %% - The % sign + */ + Arg = 0; + while ((C = SB_Get (&S)) != '\0') { + + /* If it is a newline, the current line is ready to go */ + if (C == '\n') { + + /* Pass it to the backend and start over */ + g_asmcode (&T); + SB_Clear (&T); + + } else if (C == '%') { + + /* Format specifier */ + ++Arg; + C = SB_Get (&S); + switch (C) { + case 'b': ParseByteArg (&T, Arg); break; + case 'w': ParseWordArg (&T, Arg); break; + case 'l': ParseLongArg (&T, Arg); break; + case 'v': ParseGVarArg (&T, Arg); break; + case 'o': ParseLVarArg (&T, Arg); break; + case '%': SB_AppendChar (&T, '%'); break; + default: + Error ("Error in __asm__ format specifier %u", Arg); + AsmErrorSkip (); + goto Done; + } + + } else { + + /* A normal character, just copy it */ + SB_AppendChar (&T, C); + + } + } + + /* If the target buffer is not empty, we have a last line in there */ + if (!SB_IsEmpty (&T)) { + g_asmcode (&T); + } + +Done: + /* Call the string buf destructors */ + DoneStrBuf (&S); + DoneStrBuf (&T); +} + + + void AsmStatement (void) /* This function parses ASM statements. The syntax of the ASM directive * looks like the one defined for C++ (C has no ASM directive), that is, @@ -60,43 +361,27 @@ void AsmStatement (void) NextToken (); /* Need left parenthesis */ - ConsumeLParen (); + if (!ConsumeLParen ()) { + return; + } /* String literal */ if (CurTok.Tok != TOK_SCONST) { + + /* Print a diagnostic */ Error ("String literal expected"); + + /* Try some smart error recovery: Skip tokens until we reach the + * enclosing paren, or a semicolon. + */ + AsmErrorSkip (); + } else { - /* The string literal may consist of more than one line of assembler - * code. Separate the single lines and output the code. - */ - const char* S = GetLiteral (CurTok.IVal); - while (*S) { - - /* Separate the lines */ - const char* E = strchr (S, '\n'); - if (E) { - /* Found a newline */ - g_asmcode (S, E-S); - S = E+1; - } else { - int Len = strlen (S); - g_asmcode (S, Len); - S += Len; - } - } - - /* Reset the string pointer, effectivly clearing the string from the - * string table. Since we're working with one token lookahead, this - * will fail if the next token is also a string token, but that's a - * syntax error anyway, because we expect a right paren. - */ - ResetLiteralPoolOffs (CurTok.IVal); + /* Parse the ASM statement */ + ParseAsm (); } - /* Skip the string token */ - NextToken (); - /* Closing paren needed */ ConsumeRParen (); }