]> git.sur5r.net Git - cc65/blob - src/cc65/asmstmt.c
d680f0b694a004a7d33866517f8dad959b9e7c76
[cc65] / src / cc65 / asmstmt.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 asmstmt.c                                 */
4 /*                                                                           */
5 /*            Inline assembler statements for the cc65 C compiler            */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2001-2003 Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 52                                             */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@musoftware.de                                            */
13 /*                                                                           */
14 /*                                                                           */
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.                                    */
18 /*                                                                           */
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:                            */
22 /*                                                                           */
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              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <string.h>
37
38 /* common */
39 #include "xsprintf.h"
40
41 /* cc65 */
42 #include "codegen.h"
43 #include "datatype.h"
44 #include "error.h"
45 #include "expr.h"
46 #include "function.h"
47 #include "litpool.h"
48 #include "scanner.h"
49 #include "symtab.h"
50 #include "asmstmt.h"
51
52
53
54 /*****************************************************************************/
55 /*                                   Code                                    */
56 /*****************************************************************************/
57
58
59
60 static void AsmRangeError (unsigned Arg)
61 /* Print a diagnostic about a range error in the argument with the given number */
62 {
63     Error ("Range error in argument %u", Arg);
64 }
65
66
67
68 static void AsmErrorSkip (void)
69 /* Called in case of an error, skips tokens until the closing paren or a
70  * semicolon is reached.
71  */
72 {
73     static const token_t TokenList[] = { TOK_RPAREN, TOK_SEMI };
74     SkipTokens (TokenList, sizeof(TokenList) / sizeof(TokenList[0]));
75 }
76
77
78
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.
82  */
83 {
84     SymEntry* Sym;
85
86     /* We expect an argument separated by a comma */
87     ConsumeComma ();
88
89     /* Argument must be an identifier */
90     if (CurTok.Tok != TOK_IDENT) {
91         Error ("Identifier expected for argument %u", Arg);
92         AsmErrorSkip ();
93         return 0;
94     }
95
96     /* Get a pointer to the symbol table entry */
97     Sym = FindSym (CurTok.Ident);
98
99     /* Did we find a symbol with this name? */
100     if (Sym == 0) {
101         Error ("Undefined symbol `%s' for argument %u", CurTok.Ident, Arg);
102         AsmErrorSkip ();
103         return 0;
104     }
105
106     /* We found the symbol - skip the name token */
107     NextToken ();
108
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);
112         AsmErrorSkip ();
113         return 0;
114     }
115
116     /* Mark the symbol as referenced */
117     Sym->Flags |= SC_REF;
118
119     /* Return it */
120     return Sym;
121 }
122
123
124
125 static void ParseByteArg (StrBuf* T, unsigned Arg)
126 /* Parse the %b format specifier */
127 {
128     ExprDesc Expr;
129     char     Buf [16];
130
131     /* We expect an argument separated by a comma */
132     ConsumeComma ();
133
134     /* Evaluate the expression */
135     ConstSubExpr (hie1, &Expr);
136
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) {
140             AsmRangeError (Arg);
141             Expr.ConstVal = 0;
142         }
143     } else {
144         if (Expr.ConstVal < -128 || Expr.ConstVal > 127) {
145             AsmRangeError (Arg);
146             Expr.ConstVal = 0;
147         }
148     }
149
150     /* Convert into a hex number */
151     xsprintf (Buf, sizeof (Buf), "$%02lX", Expr.ConstVal & 0xFF);
152
153     /* Add the number to the target buffer */
154     SB_AppendStr (T, Buf);
155 }
156
157
158
159 static void ParseWordArg (StrBuf* T, unsigned Arg)
160 /* Parse the %w format specifier */
161 {
162     ExprDesc Expr;
163     char     Buf [16];
164
165     /* We expect an argument separated by a comma */
166     ConsumeComma ();
167
168     /* Evaluate the expression */
169     ConstSubExpr (hie1, &Expr);
170
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) {
174             AsmRangeError (Arg);
175             Expr.ConstVal = 0;
176         }
177     } else {
178         if (Expr.ConstVal < -32768 || Expr.ConstVal > 32767) {
179             AsmRangeError (Arg);
180             Expr.ConstVal = 0;
181         }
182     }
183
184     /* Convert into a hex number */
185     xsprintf (Buf, sizeof (Buf), "$%04lX", Expr.ConstVal & 0xFFFF);
186
187     /* Add the number to the target buffer */
188     SB_AppendStr (T, Buf);
189 }
190
191
192
193 static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused)))
194 /* Parse the %l format specifier */
195 {
196     ExprDesc Expr;
197     char     Buf [16];
198
199     /* We expect an argument separated by a comma */
200     ConsumeComma ();
201
202     /* Evaluate the expression */
203     ConstSubExpr (hie1, &Expr);
204
205     /* Convert into a hex number */
206     xsprintf (Buf, sizeof (Buf), "$%08lX", Expr.ConstVal & 0xFFFFFFFF);
207
208     /* Add the number to the target buffer */
209     SB_AppendStr (T, Buf);
210 }
211
212
213
214 static void ParseGVarArg (StrBuf* T, unsigned Arg)
215 /* Parse the %v format specifier */
216 {
217     /* Parse the symbol name parameter and check the type */
218     SymEntry* Sym = AsmGetSym (Arg, SC_STATIC);
219     if (Sym == 0) {
220         /* Some sort of error */
221         return;
222     }
223
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);
230     } else if (Sym->Flags & SC_REGISTER) {
231         char Buf[32];
232         xsprintf (Buf, sizeof (Buf), "regbank+%d", Sym->V.R.RegOffs);
233         SB_AppendStr (T, Buf);
234     } else {
235         /* Static variable */
236         char Buf [16];
237         xsprintf (Buf, sizeof (Buf), "L%04X", Sym->V.Label);
238         SB_AppendStr (T, Buf);
239     }
240 }
241
242
243
244 static void ParseLVarArg (StrBuf* T, unsigned Arg)
245 /* Parse the %o format specifier */
246 {
247     unsigned Offs;
248     char Buf [16];
249
250     /* Parse the symbol name parameter and check the type */
251     SymEntry* Sym = AsmGetSym (Arg, SC_AUTO);
252     if (Sym == 0) {
253         /* Some sort of error */
254         return;
255     }
256
257     /* The symbol may be a parameter to a variadic function. In this case, we
258      * don't have a fixed stack offset, so check it and bail out with an error
259      * if this is the case.
260      */
261     if ((Sym->Flags & SC_PARAM) == SC_PARAM && F_IsVariadic (CurrentFunc)) {
262         Error ("Argument %u has no fixed stack offset", Arg);
263         AsmErrorSkip ();
264         return;
265     }
266
267     /* Calculate the current offset from SP */
268     Offs = Sym->V.Offs - oursp;
269
270     /* Output the offset */
271     xsprintf (Buf, sizeof (Buf), (Offs > 0xFF)? "$%04X" : "$%02X", Offs);
272     SB_AppendStr (T, Buf);
273 }
274
275
276
277 static void ParseStrArg (StrBuf* T, unsigned Arg attribute ((unused)))
278 /* Parse the %s format specifier */
279 {
280     ExprDesc Expr;
281     char Buf [64];
282
283     /* We expect an argument separated by a comma */
284     ConsumeComma ();
285
286     /* Check what comes */
287     switch (CurTok.Tok) {
288
289         case TOK_IDENT:
290             /* Identifier */
291             SB_AppendStr (T, CurTok.Ident);
292             NextToken ();
293             break;
294
295         case TOK_SCONST:
296             /* String constant */
297             SB_AppendStr (T, GetLiteral (CurTok.IVal));
298             ResetLiteralPoolOffs (CurTok.IVal);
299             NextToken ();
300             break;
301
302         default:
303             ConstSubExpr (hie1, InitExprDesc (&Expr));
304             xsprintf (Buf, sizeof (Buf), "%ld", Expr.ConstVal);
305             SB_AppendStr (T, Buf);
306             break;
307     }
308 }
309
310
311
312 static void ParseAsm (void)
313 /* Parse the contents of the ASM statement */
314 {
315     unsigned Arg;
316     char     C;
317
318     /* Create a target string buffer */
319     StrBuf T = AUTO_STRBUF_INITIALIZER;
320
321     /* Create a string buffer from the string literal */
322     StrBuf S = AUTO_STRBUF_INITIALIZER;
323     GetLiteralStrBuf (&S, CurTok.IVal);
324
325     /* Reset the string pointer, effectivly clearing the string from the
326      * string table. Since we're working with one token lookahead, this
327      * will fail if the next token is also a string token, but that's a
328      * syntax error anyway, because we expect a right paren.
329      */
330     ResetLiteralPoolOffs (CurTok.IVal);
331
332     /* Skip the string token */
333     NextToken ();
334
335     /* Parse the statement. It may contain several lines and one or more
336      * of the following place holders:
337      *   %b     - Numerical 8 bit value
338      *   %w     - Numerical 16 bit value
339      *   %l     - Numerical 32 bit value
340      *   %v     - Assembler name of a (global) variable
341      *   %o     - Stack offset of a (local) variable
342      *   %s     - Any argument converted to a string (almost)
343      *   %%     - The % sign
344      */
345     Arg = 0;
346     while ((C = SB_Get (&S)) != '\0') {
347
348         /* If it is a newline, the current line is ready to go */
349         if (C == '\n') {
350
351             /* Pass it to the backend and start over */
352             g_asmcode (&T);
353             SB_Clear (&T);
354
355         } else if (C == '%') {
356
357             /* Format specifier */
358             ++Arg;
359             C = SB_Get (&S);
360             switch (C) {
361                 case '%':   SB_AppendChar (&T, '%');        break;
362                 case 'b':   ParseByteArg (&T, Arg);         break;
363                 case 'l':   ParseLongArg (&T, Arg);         break;
364                 case 'o':   ParseLVarArg (&T, Arg);         break;
365                 case 's':   ParseStrArg (&T, Arg);          break;
366                 case 'v':   ParseGVarArg (&T, Arg);         break;
367                 case 'w':   ParseWordArg (&T, Arg);         break;
368                 default:
369                     Error ("Error in __asm__ format specifier %u", Arg);
370                     AsmErrorSkip ();
371                     goto Done;
372             }
373
374         } else {
375
376             /* A normal character, just copy it */
377             SB_AppendChar (&T, C);
378
379         }
380     }
381
382     /* If the target buffer is not empty, we have a last line in there */
383     if (!SB_IsEmpty (&T)) {
384         g_asmcode (&T);
385     }
386
387 Done:
388     /* Call the string buf destructors */
389     DoneStrBuf (&S);
390     DoneStrBuf (&T);
391 }
392
393
394
395 void AsmStatement (void)
396 /* This function parses ASM statements. The syntax of the ASM directive
397  * looks like the one defined for C++ (C has no ASM directive), that is,
398  * a string literal in parenthesis.
399  */
400 {
401     /* Skip the ASM */
402     NextToken ();
403
404     /* Need left parenthesis */
405     if (!ConsumeLParen ()) {
406         return;
407     }
408
409     /* String literal */
410     if (CurTok.Tok != TOK_SCONST) {
411
412         /* Print a diagnostic */
413         Error ("String literal expected");
414
415         /* Try some smart error recovery: Skip tokens until we reach the
416          * enclosing paren, or a semicolon.
417          */
418         AsmErrorSkip ();
419
420     } else {
421
422         /* Parse the ASM statement */
423         ParseAsm ();
424     }
425
426     /* Closing paren needed */
427     ConsumeRParen ();
428 }
429
430
431