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