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
64static 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
72static 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
83static 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
129static 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
163static 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
197static 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
218static 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
248static 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
281static 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
306static 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
340static 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
410Done:
411 /* Call the string buf destructors */
412 SB_Done (&S);
413 SB_Done (&T);
414}
415
416
417
418void 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}
461