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