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