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