1 /*****************************************************************************/
5 /* Inline assembler statements for the cc65 C compiler */
9 /* (C) 2001 Ullrich von Bassewitz */
11 /* D-70597 Stuttgart */
12 /* EMail: uz@musoftware.de */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
54 /*****************************************************************************/
56 /*****************************************************************************/
60 static void AsmRangeError (unsigned Arg)
61 /* Print a diagnostic about a range error in the argument with the given number */
63 Error ("Range error in argument %u", Arg);
68 static void AsmErrorSkip (void)
69 /* Called in case of an error, skips tokens until the closing paren or a
70 * semicolon is reached.
73 static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
74 SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
79 static SymEntry* AsmGetSym (unsigned Arg, unsigned Type)
80 /* Find the symbol with the name currently in NextTok. The symbol must be of
81 * the given type. On errors, NULL is returned.
86 /* We expect an argument separated by a comma */
89 /* Argument must be an identifier */
90 if (CurTok.Tok != TOK_IDENT) {
91 Error ("Identifier expected for argument %u", Arg);
96 /* Get a pointer to the symbol table entry */
97 Sym = FindSym (CurTok.Ident);
99 /* Did we find a symbol with this name? */
101 Error ("Undefined symbol `%s' for argument %u", CurTok.Ident, Arg);
106 /* We found the symbol - skip the name token */
109 /* Check if we have a global symbol */
110 if ((Sym->Flags & Type) != Type) {
111 Error ("Type of argument %u differs from format specifier", Arg);
116 /* Mark the symbol as referenced */
117 Sym->Flags |= SC_REF;
125 static void ParseByteArg (StrBuf* T, unsigned Arg)
126 /* Parse the %b format specifier */
131 /* We expect an argument separated by a comma */
134 /* Evaluate the expression */
135 ConstSubExpr (hie1, &Expr);
137 /* Check the range but allow negative values if the type is signed */
138 if (IsSignUnsigned (Expr.Type)) {
139 if (Expr.ConstVal < 0 || Expr.ConstVal > 0xFF) {
144 if (Expr.ConstVal < -128 || Expr.ConstVal > 127) {
150 /* Convert into a hex number */
151 xsprintf (Buf, sizeof (Buf), "$%02lX", Expr.ConstVal & 0xFF);
153 /* Add the number to the target buffer */
154 SB_AppendStr (T, Buf);
159 static void ParseWordArg (StrBuf* T, unsigned Arg)
160 /* Parse the %w format specifier */
165 /* We expect an argument separated by a comma */
168 /* Evaluate the expression */
169 ConstSubExpr (hie1, &Expr);
171 /* Check the range but allow negative values if the type is signed */
172 if (IsSignUnsigned (Expr.Type)) {
173 if (Expr.ConstVal < 0 || Expr.ConstVal > 0xFFFF) {
178 if (Expr.ConstVal < -32768 || Expr.ConstVal > 32767) {
184 /* Convert into a hex number */
185 xsprintf (Buf, sizeof (Buf), "$%04lX", Expr.ConstVal & 0xFFFF);
187 /* Add the number to the target buffer */
188 SB_AppendStr (T, Buf);
193 static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused)))
194 /* Parse the %l format specifier */
199 /* We expect an argument separated by a comma */
202 /* Evaluate the expression */
203 ConstSubExpr (hie1, &Expr);
205 /* Convert into a hex number */
206 xsprintf (Buf, sizeof (Buf), "$%08lX", Expr.ConstVal & 0xFFFFFFFF);
208 /* Add the number to the target buffer */
209 SB_AppendStr (T, Buf);
214 static void ParseGVarArg (StrBuf* T, unsigned Arg)
215 /* Parse the %v format specifier */
217 /* Parse the symbol name parameter and check the type */
218 SymEntry* Sym = AsmGetSym (Arg, SC_STATIC);
220 /* Some sort of error */
224 /* Check for external linkage */
225 if (Sym->Flags & (SC_EXTERN | SC_STORAGE)) {
226 /* External linkage */
227 /* ### FIXME: Asm name should be generated by codegen */
228 SB_AppendChar (T, '_');
229 SB_AppendStr (T, Sym->Name);
231 /* Static variable */
233 xsprintf (Buf, sizeof (Buf), "L%04X", Sym->V.Label);
234 SB_AppendStr (T, Buf);
240 static void ParseLVarArg (StrBuf* T, unsigned Arg)
241 /* Parse the %o format specifier */
246 /* Parse the symbol name parameter and check the type */
247 SymEntry* Sym = AsmGetSym (Arg, SC_AUTO);
249 /* Some sort of error */
253 /* The symbol may be a parameter to a variadic function. In this case, we
254 * don't have a fixed stack offset, so check it and bail out with an error
255 * if this is the case.
257 if ((Sym->Flags & SC_PARAM) == SC_PARAM && F_IsVariadic (CurrentFunc)) {
258 Error ("Argument %u has no fixed stack offset", Arg);
263 /* Calculate the current offset from SP */
264 Offs = Sym->V.Offs - oursp;
266 /* Output the offset */
267 xsprintf (Buf, sizeof (Buf), (Offs > 0xFF)? "$%04X" : "$%02X", Offs);
268 SB_AppendStr (T, Buf);
273 static void ParseAsm (void)
274 /* Parse the contents of the ASM statement */
279 /* Create a target string buffer */
280 StrBuf T = AUTO_STRBUF_INITIALIZER;
282 /* Create a string buffer from the string literal */
283 StrBuf S = AUTO_STRBUF_INITIALIZER;
284 GetLiteralStrBuf (&S, CurTok.IVal);
286 /* Reset the string pointer, effectivly clearing the string from the
287 * string table. Since we're working with one token lookahead, this
288 * will fail if the next token is also a string token, but that's a
289 * syntax error anyway, because we expect a right paren.
291 ResetLiteralPoolOffs (CurTok.IVal);
293 /* Skip the string token */
296 /* Parse the statement. It may contain several lines and one or more
297 * of the following place holders:
298 * %b - Numerical 8 bit value
299 * %w - Numerical 16 bit value
300 * %l - Numerical 32 bit value
301 * %v - Assembler name of a (global) variable
302 * %o - Stack offset of a (local) variable
306 while ((C = SB_Get (&S)) != '\0') {
308 /* If it is a newline, the current line is ready to go */
311 /* Pass it to the backend and start over */
315 } else if (C == '%') {
317 /* Format specifier */
321 case 'b': ParseByteArg (&T, Arg); break;
322 case 'w': ParseWordArg (&T, Arg); break;
323 case 'l': ParseLongArg (&T, Arg); break;
324 case 'v': ParseGVarArg (&T, Arg); break;
325 case 'o': ParseLVarArg (&T, Arg); break;
326 case '%': SB_AppendChar (&T, '%'); break;
328 Error ("Error in __asm__ format specifier %u", Arg);
335 /* A normal character, just copy it */
336 SB_AppendChar (&T, C);
341 /* If the target buffer is not empty, we have a last line in there */
342 if (!SB_IsEmpty (&T)) {
347 /* Call the string buf destructors */
354 void AsmStatement (void)
355 /* This function parses ASM statements. The syntax of the ASM directive
356 * looks like the one defined for C++ (C has no ASM directive), that is,
357 * a string literal in parenthesis.
363 /* Need left parenthesis */
364 if (!ConsumeLParen ()) {
369 if (CurTok.Tok != TOK_SCONST) {
371 /* Print a diagnostic */
372 Error ("String literal expected");
374 /* Try some smart error recovery: Skip tokens until we reach the
375 * enclosing paren, or a semicolon.
381 /* Parse the ASM statement */
385 /* Closing paren needed */