1 /*****************************************************************************/
5 /* Inline assembler statements for the cc65 C compiler */
9 /* (C) 2001-2008 Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
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 /*****************************************************************************/
58 /*****************************************************************************/
60 /*****************************************************************************/
64 static void AsmRangeError (unsigned Arg)
65 /* Print a diagnostic about a range error in the argument with the given number */
67 Error ("Range error in argument %u", Arg);
72 static void AsmErrorSkip (void)
73 /* Called in case of an error, skips tokens until the closing paren or a
74 ** semicolon is reached.
77 static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
78 SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
83 static SymEntry* AsmGetSym (unsigned Arg, unsigned Type)
84 /* Find the symbol with the name currently in NextTok. The symbol must be of
85 ** the given type. On errors, NULL is returned.
90 /* We expect an argument separated by a comma */
93 /* Argument must be an identifier */
94 if (CurTok.Tok != TOK_IDENT) {
95 Error ("Identifier expected for argument %u", Arg);
100 /* Get a pointer to the symbol table entry */
101 Sym = FindSym (CurTok.Ident);
103 /* Did we find a symbol with this name? */
105 Error ("Undefined symbol `%s' for argument %u", CurTok.Ident, Arg);
110 /* We found the symbol - skip the name token */
113 /* Check if we have a global symbol */
114 if ((Sym->Flags & Type) != Type) {
115 Error ("Type of argument %u differs from format specifier", Arg);
120 /* Mark the symbol as referenced */
121 Sym->Flags |= SC_REF;
129 static void ParseByteArg (StrBuf* T, unsigned Arg)
130 /* Parse the %b format specifier */
135 /* We expect an argument separated by a comma */
138 /* Evaluate the expression */
139 ConstAbsIntExpr (hie1, &Expr);
141 /* Check the range but allow negative values if the type is signed */
142 if (IsSignUnsigned (Expr.Type)) {
143 if (Expr.IVal < 0 || Expr.IVal > 0xFF) {
148 if (Expr.IVal < -128 || Expr.IVal > 127) {
154 /* Convert into a hex number */
155 xsprintf (Buf, sizeof (Buf), "$%02lX", Expr.IVal & 0xFF);
157 /* Add the number to the target buffer */
158 SB_AppendStr (T, Buf);
163 static void ParseWordArg (StrBuf* T, unsigned Arg)
164 /* Parse the %w format specifier */
169 /* We expect an argument separated by a comma */
172 /* Evaluate the expression */
173 ConstAbsIntExpr (hie1, &Expr);
175 /* Check the range but allow negative values if the type is signed */
176 if (IsSignUnsigned (Expr.Type)) {
177 if (Expr.IVal < 0 || Expr.IVal > 0xFFFF) {
182 if (Expr.IVal < -32768 || Expr.IVal > 32767) {
188 /* Convert into a hex number */
189 xsprintf (Buf, sizeof (Buf), "$%04lX", Expr.IVal & 0xFFFF);
191 /* Add the number to the target buffer */
192 SB_AppendStr (T, Buf);
197 static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused)))
198 /* Parse the %l format specifier */
203 /* We expect an argument separated by a comma */
206 /* Evaluate the expression */
207 ConstAbsIntExpr (hie1, &Expr);
209 /* Convert into a hex number */
210 xsprintf (Buf, sizeof (Buf), "$%08lX", Expr.IVal & 0xFFFFFFFF);
212 /* Add the number to the target buffer */
213 SB_AppendStr (T, Buf);
218 static void ParseGVarArg (StrBuf* T, unsigned Arg)
219 /* Parse the %v format specifier */
221 /* Parse the symbol name parameter and check the type */
222 SymEntry* Sym = AsmGetSym (Arg, SC_STATIC);
224 /* Some sort of error */
228 /* Check for external linkage */
229 if (Sym->Flags & (SC_EXTERN | SC_STORAGE | SC_FUNC)) {
230 /* External linkage or a function */
231 /* ### FIXME: Asm name should be generated by codegen */
232 SB_AppendChar (T, '_');
233 SB_AppendStr (T, Sym->Name);
234 } else if (Sym->Flags & SC_REGISTER) {
236 xsprintf (Buf, sizeof (Buf), "regbank+%d", Sym->V.R.RegOffs);
237 SB_AppendStr (T, Buf);
239 /* Static variable */
241 xsprintf (Buf, sizeof (Buf), "L%04X", Sym->V.L.Label);
242 SB_AppendStr (T, Buf);
248 static void ParseLVarArg (StrBuf* T, unsigned Arg)
249 /* Parse the %o format specifier */
254 /* Parse the symbol name parameter and check the type */
255 SymEntry* Sym = AsmGetSym (Arg, SC_AUTO);
257 /* Some sort of error */
261 /* The symbol may be a parameter to a variadic function. In this case, we
262 ** don't have a fixed stack offset, so check it and bail out with an error
263 ** if this is the case.
265 if ((Sym->Flags & SC_PARAM) == SC_PARAM && F_IsVariadic (CurrentFunc)) {
266 Error ("Argument %u has no fixed stack offset", Arg);
271 /* Calculate the current offset from SP */
272 Offs = Sym->V.Offs - StackPtr;
274 /* Output the offset */
275 xsprintf (Buf, sizeof (Buf), (Offs > 0xFF)? "$%04X" : "$%02X", Offs);
276 SB_AppendStr (T, Buf);
281 static void ParseLabelArg (StrBuf* T, unsigned Arg attribute ((unused)))
282 /* Parse the %g format specifier */
284 /* We expect an identifier separated by a comma */
286 if (CurTok.Tok != TOK_IDENT) {
288 Error ("Label name expected");
292 /* Add a new label symbol if we don't have one until now */
293 SymEntry* Entry = AddLabelSym (CurTok.Ident, SC_REF);
295 /* Append the label name to the buffer */
296 SB_AppendStr (T, LocalLabelName (Entry->V.L.Label));
298 /* Eat the label name */
306 static void ParseStrArg (StrBuf* T, unsigned Arg attribute ((unused)))
307 /* Parse the %s format specifier */
312 /* We expect an argument separated by a comma */
315 /* Check what comes */
316 switch (CurTok.Tok) {
320 SB_AppendStr (T, CurTok.Ident);
325 /* String constant */
326 SB_Append (T, GetLiteralStrBuf (CurTok.SVal));
331 ConstAbsIntExpr (hie1, &Expr);
332 xsprintf (Buf, sizeof (Buf), "%ld", Expr.IVal);
333 SB_AppendStr (T, Buf);
340 static void ParseAsm (void)
341 /* Parse the contents of the ASM statement */
346 /* Create a target string buffer */
347 StrBuf T = AUTO_STRBUF_INITIALIZER;
349 /* Create a string buffer from the string literal */
350 StrBuf S = AUTO_STRBUF_INITIALIZER;
351 SB_Append (&S, GetLiteralStrBuf (CurTok.SVal));
353 /* Skip the string token */
356 /* Parse the statement. It may contain several lines and one or more
357 ** of the following place holders:
358 ** %b - Numerical 8 bit value
359 ** %w - Numerical 16 bit value
360 ** %l - Numerical 32 bit value
361 ** %v - Assembler name of a (global) variable
362 ** %o - Stack offset of a (local) variable
363 ** %g - Assembler name of a C label
364 ** %s - Any argument converted to a string (almost)
368 while ((C = SB_Get (&S)) != '\0') {
370 /* If it is a newline, the current line is ready to go */
373 /* Pass it to the backend and start over */
377 } else if (C == '%') {
379 /* Format specifier */
383 case '%': SB_AppendChar (&T, '%'); break;
384 case 'b': ParseByteArg (&T, Arg); break;
385 case 'g': ParseLabelArg (&T, Arg); break;
386 case 'l': ParseLongArg (&T, Arg); break;
387 case 'o': ParseLVarArg (&T, Arg); break;
388 case 's': ParseStrArg (&T, Arg); break;
389 case 'v': ParseGVarArg (&T, Arg); break;
390 case 'w': ParseWordArg (&T, Arg); break;
392 Error ("Error in __asm__ format specifier %u", Arg);
399 /* A normal character, just copy it */
400 SB_AppendChar (&T, C);
405 /* If the target buffer is not empty, we have a last line in there */
406 if (!SB_IsEmpty (&T)) {
411 /* Call the string buf destructors */
418 void AsmStatement (void)
419 /* This function parses ASM statements. The syntax of the ASM directive
420 ** looks like the one defined for C++ (C has no ASM directive), that is,
421 ** a string literal in parenthesis.
427 /* An optional volatile qualifier disables optimization for
428 ** the entire function [same as #pragma optimize(push, off)].
430 if (CurTok.Tok == TOK_VOLATILE) {
431 /* Don't optimize the Current code Segment */
432 CS->Code->Optimize = 0;
436 /* Need left parenthesis */
437 if (!ConsumeLParen ()) {
442 if (CurTok.Tok != TOK_SCONST) {
444 /* Print a diagnostic */
445 Error ("String literal expected");
447 /* Try some smart error recovery: Skip tokens until we reach the
448 ** enclosing paren, or a semicolon.
454 /* Parse the ASM statement */
458 /* Closing paren needed */