* link/z80/lkmain.c,
[fw/sdcc] / src / asm.c
1 /** @file asm.c
2     Provides output functions that modify the output string
3     based on the input tokens and the assembler token mapping
4     specification loaded.
5
6     Note that the functions below only handle digit format modifiers.
7     eg %02X is ok, but %lu and %.4u will fail.
8 */
9 #include "common.h"
10 #include "asm.h"
11
12 /* A 'token' is like !blah or %24f and is under the programmers
13    control. */
14 #define MAX_TOKEN_LEN           64
15
16 static hTab *_h;
17
18 char *
19 FileBaseName (char *fileFullName)
20 {
21   char *p = fileFullName;
22
23   if (!fileFullName) {
24     return "unknown";
25   }
26
27   while (*fileFullName)
28     {
29       if ((*fileFullName == '/') || (*fileFullName == '\\') || (*fileFullName == ':'))
30         {
31           p = fileFullName;
32           p++;
33         }
34       fileFullName++;
35     }
36   return p;
37 }
38
39 static const char *
40 _findMapping (const char *szKey)
41 {
42   return shash_find (_h, szKey);
43 }
44
45 // Append a string onto another, and update the pointer to the end of
46 // the new string.
47 static char *
48 _appendAt (char *at, char *onto, const char *sz, size_t *max)
49 {
50   wassert (at && onto && sz);
51   strncpyz (at, sz, *max);
52   *max -= strlen (at);
53   return at + strlen (at);
54 }
55
56 void
57 tvsprintf (char *buffer, size_t len, const char *format, va_list ap)
58 {
59   // Under Linux PPC va_list is a structure instead of a primitive type,
60   // and doesnt like being passed around.  This version turns everything
61   // into one function.
62
63   // Supports:
64   //  !tokens
65   //  %[CIFN] - special formats with no argument (ie list isnt touched)
66   //  All of the system formats
67
68   // This is acheived by expanding the tokens and zero arg formats into
69   // one big format string, which is passed to the native printf.
70   static int    count;
71   char          noTokens[INITIAL_INLINEASM];
72   char          newFormat[INITIAL_INLINEASM];
73   char          *pInto = noTokens;
74   size_t        pIntoLen = sizeof(noTokens);
75   char          *p;
76   char          token[MAX_TOKEN_LEN];
77   const char    *sz = format;
78
79   // NULL terminate it to let strlen work.
80   *pInto = '\0';
81
82   /* First pass: expand all of the macros */
83   while (pIntoLen && *sz)
84     {
85       if (*sz == '!')
86         {
87           /* Start of a token.  Search until the first
88              [non alpha, *] and call it a token. */
89           const char *t;
90           p = token;
91           sz++;
92           while (isalpha ((unsigned char)*sz) || *sz == '*')
93             {
94               *p++ = *sz++;
95             }
96           *p = '\0';
97           /* Now find the token in the token list */
98           if ((t = _findMapping (token)))
99             {
100               pInto = _appendAt (pInto, noTokens, t, &pIntoLen);
101             }
102           else
103             {
104               fprintf (stderr, "Cant find token \"%s\"\n", token);
105               wassert (0);
106             }
107         }
108       else
109         {
110           *pInto++ = *sz++;
111           pIntoLen--;
112         }
113     }
114
115   if (!pIntoLen)
116   {
117       fprintf(stderr,
118                 "Internal error: tvsprintf overflowed on pass one.\n");
119       // Might as well go on...
120   }
121
122   *pInto = '\0';
123
124   /* Second pass: Expand any macros that we own */
125   sz = noTokens;
126   pInto = newFormat;
127   pIntoLen = sizeof(newFormat);
128
129   while (pIntoLen && *sz)
130     {
131       if (*sz == '%')
132         {
133           // See if its one that we handle.
134           sz++;
135           switch (*sz)
136             {
137             case 'C':
138               // Code segment name.
139               pInto = _appendAt (pInto, newFormat, CODE_NAME, &pIntoLen);
140               sz++;
141               break;
142             case 'F':
143               // Source file name.
144               pInto = _appendAt (pInto, newFormat, fullSrcFileName, &pIntoLen);
145               sz++;
146               break;
147             case 'N':
148               // Current function name.
149               pInto = _appendAt (pInto, newFormat, currFunc->rname, &pIntoLen);
150               sz++;
151               break;
152             case 'I':
153               {
154                 // Unique ID.
155                 char id[20];
156                 SNPRINTF (id, sizeof(id), "%u", ++count);
157                 pInto = _appendAt (pInto, newFormat, id, &pIntoLen);
158                 sz++;
159                 break;
160               }
161             default:
162               // Not one of ours.  Copy until the end.
163               *pInto++ = '%';
164               pIntoLen--;
165               while (pIntoLen && !isalpha ((unsigned char)*sz))
166                 {
167                   *pInto++ = *sz++;
168                   pIntoLen--;
169                 }
170                 if (pIntoLen)
171                 {
172                     *pInto++ = *sz++;
173                     pIntoLen--;
174                 }
175             }
176         }
177       else
178         {
179           *pInto++ = *sz++;
180           pIntoLen--;
181         }
182     }
183
184   if (!pIntoLen)
185   {
186       fprintf(stderr,
187                 "Internal error: tvsprintf overflowed on pass two.\n");
188       // Might as well go on...
189   }
190
191   *pInto = '\0';
192
193   // Now do the actual printing
194 #if defined(HAVE_VSNPRINTF)
195     {
196         int wrlen;
197         wrlen = vsnprintf (buffer, len, newFormat, ap);
198
199         if (wrlen < 0 || (size_t)wrlen >= len)
200         {
201             fprintf(stderr, "Internal error: tvsprintf truncated.\n");
202         }
203     }
204
205 #else
206     vsprintf (buffer, newFormat, ap);
207     if (strlen(buffer) >= len)
208     {
209         fprintf(stderr, "Internal error: tvsprintf overflowed.\n");
210     }
211 #endif
212 }
213
214 void
215 tfprintf (FILE * fp, const char *szFormat,...)
216 {
217   va_list ap;
218   char buffer[INITIAL_INLINEASM];
219
220   va_start (ap, szFormat);
221   tvsprintf (buffer, INITIAL_INLINEASM, szFormat, ap);
222   va_end(ap);
223   fputs (buffer, fp);
224 }
225
226 void
227 tsprintf (char *buffer, size_t len, const char *szFormat,...)
228 {
229   va_list ap;
230   va_start (ap, szFormat);
231   tvsprintf (buffer, len, szFormat, ap);
232   va_end(ap);
233 }
234
235 void
236 asm_addTree (const ASM_MAPPINGS * pMappings)
237 {
238   const ASM_MAPPING *pMap;
239
240   /* Traverse down first */
241   if (pMappings->pParent)
242     asm_addTree (pMappings->pParent);
243   pMap = pMappings->pMappings;
244   while (pMap->szKey && pMap->szValue) {
245       shash_add (&_h, pMap->szKey, pMap->szValue);
246       pMap++;
247   }
248 }
249
250 /*-----------------------------------------------------------------*/
251 /* printILine - return the readable i-code for this ic             */
252 /*                                                                 */
253 /* iCodePrint wants a file stream so we need a temporary file to   */
254 /* fool it                                                         */
255 /*-----------------------------------------------------------------*/
256 char *
257 printILine (iCode *ic)
258 {
259   static char verbalICode[1024];
260   FILE *tmpFile;
261   iCodeTable *icTab=getTableEntry(ic->op);
262   int ret;
263
264   if (INLINEASM == ic->op)
265     return "inline";
266
267   tmpFile = tempfile();
268   assert(NULL != tmpFile);
269   if (NULL == tmpFile)
270     return "";  /* return empty line if temporary file creation failed */
271
272   addSetHead(&tmpfileSet, tmpFile);
273
274   /* stuff the temporary file with the readable icode */
275   icTab->iCodePrint(tmpFile, ic, icTab->printName);
276
277   /* now swallow it */
278   rewind(tmpFile);
279   fgets(verbalICode, sizeof(verbalICode), tmpFile);
280
281   /* clean up the mess, we'll return here for all icodes!!
282    * Actually the temporary file is only closed. It will be
283    * removed by rm_tmpfiles().
284    */
285   ret = fclose(tmpFile);
286   assert(!ret);
287   deleteSetItem(&tmpfileSet, tmpFile);
288
289   /* kill the trailing NL */
290   if ('\n' == verbalICode[strlen(verbalICode) - 1])
291     verbalICode[strlen(verbalICode) - 1] = '\0';
292
293   /* and throw it up */
294   return verbalICode;
295 }
296
297 /*-----------------------------------------------------------------*/
298 /* printCLine - return the c-code for this lineno                  */
299 /*-----------------------------------------------------------------*/
300 /* int rewinds=0; */
301 char *
302 printCLine (char *srcFile, int lineno)
303 {
304   static FILE *inFile=NULL;
305   static char inLineString[1024];
306   static int inLineNo=0;
307   static char lastSrcFile[PATH_MAX];
308   char *ilsP=inLineString;
309
310   if (inFile) {
311     if (strcmp (lastSrcFile, srcFile) != 0) {
312       fclose (inFile);
313       inFile = NULL;
314       inLineNo = 0;
315     }
316   }
317   if (!inFile) {
318     inFile=fopen(srcFile, "r");
319     if (!inFile) {
320       perror ("printCLine");
321       exit (1);
322     }
323     strncpyz (lastSrcFile, srcFile, PATH_MAX);
324   }
325   if (lineno<inLineNo) {
326     fseek (inFile, 0, SEEK_SET);
327     inLineNo=0;
328     /* rewinds++; */
329   }
330   while (fgets (inLineString, 1024, inFile)) {
331     inLineNo++;
332     if (inLineNo==lineno) {
333       // remove the trailing NL
334       inLineString[strlen(inLineString)-1]='\0';
335       break;
336     }
337   }
338   while (isspace ((unsigned char)*ilsP))
339     ilsP++;
340
341   return ilsP;
342 }
343
344 static const ASM_MAPPING _asxxxx_mapping[] =
345 {
346   {"labeldef", "%s::"},
347   {"slabeldef", "%s:"},
348   {"tlabeldef", "%05d$:"},
349   {"tlabel", "%05d$"},
350   {"immed", "#"},
351   {"zero", "#0x00"},
352   {"one", "#0x01"},
353   {"area", ".area %s"},
354   {"areacode", ".area %s"},
355   {"areadata", ".area %s"},
356   {"areahome", ".area %s"},
357   {"ascii", ".ascii \"%s\""},
358   {"ds", ".ds %d"},
359   {"db", ".db"},
360   {"dbs", ".db %s"},
361   {"dw", ".dw"},
362   {"dws", ".dw %s"},
363   {"constbyte", "0x%02X"},
364   {"constword", "0x%04X"},
365   {"immedword", "#0x%04X"},
366   {"immedbyte", "#0x%02X"},
367   {"hashedstr", "#%s"},
368   {"lsbimmeds", "#<%s"},
369   {"msbimmeds", "#>%s"},
370   {"module", ".module %s"},
371   {"global", ".globl %s"},
372   {"fileprelude", ""},
373   {"functionheader",
374    "; ---------------------------------\n"
375    "; Function %s\n"
376    "; ---------------------------------"
377   },
378   {"functionlabeldef", "%s:"},
379   {"bankimmeds", "0     ; PENDING: bank support"},
380   {"los","(%s & 0xFF)"},
381   {"his","(%s >> 8)"},
382   {"hihis","(%s >> 16)"},
383   {"hihihis","(%s >> 24)"},
384   {"lod","(%d & 0xFF)"},
385   {"hid","(%d >> 8)"},
386   {"hihid","(%d >> 16)"},
387   {"hihihid","(%d >> 24)"},
388   {"lol","(%05d$ & 0xFF)"},
389   {"hil","(%05d$ >> 8)"},
390   {"hihil","(%05d$ >> 16)"},
391   {"hihihil","(%05d$ >> 24)"},
392   {"equ","="},
393   {"org", ".org %04X"},
394   {NULL, NULL}
395 };
396
397 static const ASM_MAPPING _gas_mapping[] =
398 {
399   {"labeldef", "%s::"},
400   {"slabeldef", "%s:"},
401   {"tlabeldef", "%05d$:"},
402   {"tlabel", "%05d$"},
403   {"immed", "#"},
404   {"zero", "#0x00"},
405   {"one", "#0x01"},
406   {"area", ".section %s"},
407   {"areacode", ".section %s"},
408   {"areadata", ".section %s"},
409   {"areahome", ".section %s"},
410   {"ascii", ".ascii \"%s\""},
411   {"ds", ".ds %d"},
412   {"db", ".db"},
413   {"dbs", ".db %s"},
414   {"dw", ".dw"},
415   {"dws", ".dw %s"},
416   {"constbyte", "0x%02X"},
417   {"constword", "0x%04X"},
418   {"immedword", "#0x%04X"},
419   {"immedbyte", "#0x%02X"},
420   {"hashedstr", "#%s"},
421   {"lsbimmeds", "#<%s"},
422   {"msbimmeds", "#>%s"},
423   {"module", ".file \"%s.c\""},
424   {"global", ".globl %s"},
425   {"extern", ".globl %s"},
426   {"fileprelude", ""},
427   {"functionheader",
428    "; ---------------------------------\n"
429    "; Function %s\n"
430    "; ---------------------------------"
431   },
432   {"functionlabeldef", "%s:"},
433   {"bankimmeds", "0     ; PENDING: bank support"},
434   {NULL, NULL}
435 };
436
437 static const ASM_MAPPING _a390_mapping[] =
438 {
439   {"labeldef", "%s:"},
440   {"slabeldef", "%s:"},
441   {"tlabeldef", "L%05d:"},
442   {"tlabel", "L%05d"},
443   {"immed", "#"},
444   {"zero", "#0"},
445   {"one", "#1"},
446   {"area", "; SECTION NOT SUPPORTED"},
447   {"areacode", "; SECTION NOT SUPPORTED"},
448   {"areadata", "; SECTION NOT SUPPORTED"},
449   {"areahome", "; SECTION NOT SUPPORTED"},
450   {"ascii", "db \"%s\""},
451   {"ds", "; STORAGE NOT SUPPORTED"},
452   {"db", "db"},
453   {"dbs", "db \"%s\""},
454   {"dw", "dw"},
455   {"dws", "dw %s"},
456   {"constbyte", "0%02xh"},
457   {"constword", "0%04xh"},
458   {"immedword", "#0%04Xh"},
459   {"immedbyte", "#0%02Xh"},
460   {"hashedstr", "#%s"},
461   {"lsbimmeds", "#<%s"},
462   {"msbimmeds", "#>%s"},
463   {"module", "; .file \"%s.c\""},
464   {"global", "; .globl %s"},
465   {"fileprelude", ""},
466   {"functionheader",
467    "; ---------------------------------\n"
468    "; Function %s\n"
469    "; ---------------------------------"
470   },
471   {"functionlabeldef", "%s:"},
472   {"bankimmeds", "0     ; PENDING: bank support"},
473   {"los","(%s & 0FFh)"},
474   {"his","((%s / 256) & 0FFh)"},
475   {"hihis","((%s / 65536) & 0FFh)"},
476   {"hihihis","((%s / 16777216) & 0FFh)"},
477   {"lod","(%d & 0FFh)"},
478   {"hid","((%d / 256) & 0FFh)"},
479   {"hihid","((%d / 65536) & 0FFh)"},
480   {"hihihid","((%d / 16777216) & 0FFh)"},
481   {"lol","(L%05d & 0FFh)"},
482   {"hil","((L%05d / 256) & 0FFh)"},
483   {"hihil","((L%05d / 65536) & 0FFh)"},
484   {"hihihil","((L%09d / 16777216) & 0FFh)"},
485   {"equ"," equ"},
486   {NULL, NULL}
487 };
488
489 static const ASM_MAPPING _xa_asm_mapping[] =
490 {
491   {"labeldef", "%s:"},
492   {"slabeldef", "%s:"},
493   {"tlabeldef", "L%05d:"},
494   {"tlabel", "L%05d"},
495   {"immed", "#"},
496   {"zero", "#0"},
497   {"one", "#1"},
498   {"area", ".area %s"},
499   {"areacode", ".area %s"},
500   {"areadata", ".area %s"},
501   {"areahome", ".area %s"},
502   {"ascii", ".db \"%s\""},
503   {"ds", ".ds %d"},
504   {"db", ".db"},
505   {"dbs", ".db \"%s\""},
506   {"dw", ".dw"},
507   {"dws", ".dw %s"},
508   {"constbyte", "0x%02x"},
509   {"constword", "0x%04x"},
510   {"immedword", "0x%04x"},
511   {"immedbyte", "0x%02x"},
512   {"hashedstr", "#%s"},
513   {"lsbimmeds", "#<%s"},
514   {"msbimmeds", "#>%s"},
515   {"module", "; .module %s"},
516   {"global", ".globl %s"},
517   {"fileprelude", ""},
518   {"functionheader",
519    "; ---------------------------------\n"
520    "; Function %s\n"
521    "; ---------------------------------"
522   },
523   {"functionlabeldef", "%s:"},
524   {"bankimmeds", "0     ; PENDING: bank support"},
525   {"los","(%s & 0FFh)"},
526   {"his","((%s / 256) & 0FFh)"},
527   {"hihis","((%s / 65536) & 0FFh)"},
528   {"hihihis","((%s / 16777216) & 0FFh)"},
529   {"lod","(%d & 0FFh)"},
530   {"hid","((%d / 256) & 0FFh)"},
531   {"hihid","((%d / 65536) & 0FFh)"},
532   {"hihihid","((%d / 16777216) & 0FFh)"},
533   {"lol","(L%05d & 0FFh)"},
534   {"hil","((L%05d / 256) & 0FFh)"},
535   {"hihil","((L%05d / 65536) & 0FFh)"},
536   {"hihihil","((L%09d / 16777216) & 0FFh)"},
537   {"equ"," equ"},
538   {NULL, NULL}
539 };
540
541 const ASM_MAPPINGS asm_asxxxx_mapping =
542 {
543   NULL,
544   _asxxxx_mapping
545 };
546
547 const ASM_MAPPINGS asm_gas_mapping =
548 {
549   NULL,
550   _gas_mapping
551 };
552
553 const ASM_MAPPINGS asm_a390_mapping =
554 {
555   NULL,
556   _a390_mapping
557 };
558
559 const ASM_MAPPINGS asm_xa_asm_mapping =
560 {
561   NULL,
562   _xa_asm_mapping
563 };