corrected assertion in function printILine(): _pipe() was not executed on MSVC, becau...
[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   icTab->iCodePrint(pipeStream, ic, icTab->printName);
284   // it really needs an extra push
285   fflush(pipeStream);
286   // now swallow it
287   pipeStream=fdopen(filedes[0],"r");
288   fgets(verbalICode, sizeof(verbalICode), pipeStream);
289   // clean up the mess, we'll return here for all icodes!!
290   assert(!close (filedes[0]));
291   assert(!close (filedes[1]));
292   // kill the trailing NL
293   verbalICode[strlen(verbalICode)-1]='\0';
294   // and throw it up
295   return verbalICode;
296 }
297
298 /*-----------------------------------------------------------------*/
299 /* printCLine - return the c-code for this lineno                  */
300 /*-----------------------------------------------------------------*/
301 static FILE *inFile=NULL;
302 static char inLineString[1024];
303 static int inLineNo=0;
304 static char lastSrcFile[PATH_MAX];
305 int rewinds=0;
306
307 char *printCLine (char *srcFile, int lineno) {
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 ((int)*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   {NULL, NULL}
394 };
395
396 static const ASM_MAPPING _gas_mapping[] =
397 {
398   {"labeldef", "%s::"},
399   {"slabeldef", "%s:"},
400   {"tlabeldef", "%05d$:"},
401   {"tlabel", "%05d$"},
402   {"immed", "#"},
403   {"zero", "#0x00"},
404   {"one", "#0x01"},
405   {"area", ".section %s"},
406   {"areacode", ".section %s"},
407   {"areadata", ".section %s"},
408   {"areahome", ".section %s"},
409   {"ascii", ".ascii \"%s\""},
410   {"ds", ".ds %d"},
411   {"db", ".db"},
412   {"dbs", ".db %s"},
413   {"dw", ".dw"},
414   {"dws", ".dw %s"},
415   {"constbyte", "0x%02X"},
416   {"constword", "0x%04X"},
417   {"immedword", "#0x%04X"},
418   {"immedbyte", "#0x%02X"},
419   {"hashedstr", "#%s"},
420   {"lsbimmeds", "#<%s"},
421   {"msbimmeds", "#>%s"},
422   {"module", ".file \"%s.c\""},
423   {"global", ".globl %s"},
424   {"extern", ".globl %s"},
425   {"fileprelude", ""},
426   {"functionheader",
427    "; ---------------------------------\n"
428    "; Function %s\n"
429    "; ---------------------------------"
430   },
431   {"functionlabeldef", "%s:"},
432   {"bankimmeds", "0     ; PENDING: bank support"},  
433   {NULL, NULL}
434 };
435
436 static const ASM_MAPPING _a390_mapping[] =
437 {
438   {"labeldef", "%s:"},
439   {"slabeldef", "%s:"},
440   {"tlabeldef", "L%05d:"},
441   {"tlabel", "L%05d"},
442   {"immed", "#"},
443   {"zero", "#0"},
444   {"one", "#1"},
445   {"area", "; SECTION NOT SUPPORTED"},
446   {"areacode", "; SECTION NOT SUPPORTED"},
447   {"areadata", "; SECTION NOT SUPPORTED"},
448   {"areahome", "; SECTION NOT SUPPORTED"},
449   {"ascii", "db \"%s\""},
450   {"ds", "; STORAGE NOT SUPPORTED"},
451   {"db", "db"},
452   {"dbs", "db \"%s\""},
453   {"dw", "dw"},
454   {"dws", "dw %s"},
455   {"constbyte", "0%02xh"},
456   {"constword", "0%04xh"},
457   {"immedword", "#0%04Xh"},
458   {"immedbyte", "#0%02Xh"},
459   {"hashedstr", "#%s"},
460   {"lsbimmeds", "#<%s"},
461   {"msbimmeds", "#>%s"},
462   {"module", "; .file \"%s.c\""},
463   {"global", "; .globl %s"},
464   {"fileprelude", ""},
465   {"functionheader",
466    "; ---------------------------------\n"
467    "; Function %s\n"
468    "; ---------------------------------"
469   },
470   {"functionlabeldef", "%s:"},
471   {"bankimmeds", "0     ; PENDING: bank support"},  
472   {"los","(%s & 0FFh)"},
473   {"his","((%s / 256) & 0FFh)"},
474   {"hihis","((%s / 65536) & 0FFh)"},
475   {"hihihis","((%s / 16777216) & 0FFh)"},
476   {"lod","(%d & 0FFh)"},
477   {"hid","((%d / 256) & 0FFh)"},
478   {"hihid","((%d / 65536) & 0FFh)"},
479   {"hihihid","((%d / 16777216) & 0FFh)"},
480   {"lol","(L%05d & 0FFh)"},
481   {"hil","((L%05d / 256) & 0FFh)"},
482   {"hihil","((L%05d / 65536) & 0FFh)"},
483   {"hihihil","((L%09d / 16777216) & 0FFh)"},
484   {"equ"," equ"},
485   {NULL, NULL}
486 };
487
488 static const ASM_MAPPING _xa_asm_mapping[] =
489 {
490   {"labeldef", "%s:"},
491   {"slabeldef", "%s:"},
492   {"tlabeldef", "L%05d:"},
493   {"tlabel", "L%05d"},
494   {"immed", "#"},
495   {"zero", "#0"},
496   {"one", "#1"},
497   {"area", ".area %s"},
498   {"areacode", ".area %s"},
499   {"areadata", ".area %s"},
500   {"areahome", ".area %s"},
501   {"ascii", ".db \"%s\""},
502   {"ds", ".ds %d"},
503   {"db", ".db"},
504   {"dbs", ".db \"%s\""},
505   {"dw", ".dw"},
506   {"dws", ".dw %s"},
507   {"constbyte", "0x%02x"},
508   {"constword", "0x%04x"},
509   {"immedword", "0x%04x"},
510   {"immedbyte", "0x%02x"},
511   {"hashedstr", "#%s"},
512   {"lsbimmeds", "#<%s"},
513   {"msbimmeds", "#>%s"},
514   {"module", "; .module %s"},
515   {"global", ".globl %s"},
516   {"fileprelude", ""},
517   {"functionheader",
518    "; ---------------------------------\n"
519    "; Function %s\n"
520    "; ---------------------------------"
521   },
522   {"functionlabeldef", "%s:"},
523   {"bankimmeds", "0     ; PENDING: bank support"},  
524   {"los","(%s & 0FFh)"},
525   {"his","((%s / 256) & 0FFh)"},
526   {"hihis","((%s / 65536) & 0FFh)"},
527   {"hihihis","((%s / 16777216) & 0FFh)"},
528   {"lod","(%d & 0FFh)"},
529   {"hid","((%d / 256) & 0FFh)"},
530   {"hihid","((%d / 65536) & 0FFh)"},
531   {"hihihid","((%d / 16777216) & 0FFh)"},
532   {"lol","(L%05d & 0FFh)"},
533   {"hil","((L%05d / 256) & 0FFh)"},
534   {"hihil","((L%05d / 65536) & 0FFh)"},
535   {"hihihil","((L%09d / 16777216) & 0FFh)"},
536   {"equ"," equ"},
537   {NULL, NULL}
538 };
539
540 const ASM_MAPPINGS asm_asxxxx_mapping =
541 {
542   NULL,
543   _asxxxx_mapping
544 };
545
546 const ASM_MAPPINGS asm_gas_mapping =
547 {
548   NULL,
549   _gas_mapping
550 };
551
552 const ASM_MAPPINGS asm_a390_mapping =
553 {
554   NULL,
555   _a390_mapping
556 };
557
558 const ASM_MAPPINGS asm_xa_asm_mapping =
559 {
560   NULL,
561   _xa_asm_mapping
562 };