* Added support for doing shifts by helper functions
[fw/sdcc] / src / z80 / main.c
1 #include "z80.h"
2 #include "MySystem.h"
3 #include "BuildCmd.h"
4
5 static char _z80_defaultRules[] =
6 {
7 #include "peeph.rul"
8 #include "peeph-z80.rul"
9 };
10
11 static char _gbz80_defaultRules[] =
12 {
13 #include "peeph.rul"
14 #include "peeph-gbz80.rul"
15 };
16
17 Z80_OPTS z80_opts;
18
19 typedef enum
20   {
21     /* Must be first */
22     ASM_TYPE_ASXXXX,
23     ASM_TYPE_RGBDS,
24     ASM_TYPE_ISAS
25   }
26 ASM_TYPE;
27
28 static struct
29   {
30     ASM_TYPE asmType;
31   }
32 _G;
33
34 static char *_keywords[] =
35 {
36   "sfr",
37   "nonbanked",
38   "banked",
39   NULL
40 };
41
42 extern PORT gbz80_port;
43 extern PORT z80_port;
44
45 #include "mappings.i"
46
47 static void
48 _z80_init (void)
49 {
50   z80_opts.sub = SUB_Z80;
51   asm_addTree (&_asxxxx_z80);
52 }
53
54 static void
55 _gbz80_init (void)
56 {
57   z80_opts.sub = SUB_GBZ80;
58 }
59
60 static int regParmFlg = 0;      /* determine if we can register a parameter */
61
62 static void
63 _reset_regparm ()
64 {
65   regParmFlg = 0;
66 }
67
68 static int
69 _reg_parm (sym_link * l)
70 {
71   if (options.noRegParams) 
72     {
73       return FALSE;
74     }
75   else 
76     {
77       if (regParmFlg == 2)
78         {
79           return FALSE;
80         }
81       else
82         {
83           regParmFlg++;
84           return TRUE;
85         }
86     }
87 }
88
89 static bool
90 _startsWith (const char *sz, const char *key)
91 {
92   return !strncmp (sz, key, strlen (key));
93 }
94
95 static void
96 _chomp (char *sz)
97 {
98   char *nl;
99   while ((nl = strrchr (sz, '\n')))
100     *nl = '\0';
101 }
102
103 static int
104 _process_pragma (const char *sz)
105 {
106   if (_startsWith (sz, "bank="))
107     {
108       char buffer[128];
109       strcpy (buffer, sz + 5);
110       _chomp (buffer);
111       if (isdigit (buffer[0]))
112         {
113
114         }
115       else if (!strcmp (buffer, "BASE"))
116         {
117           strcpy (buffer, "HOME");
118         }
119       if (isdigit (buffer[0]))
120         {
121           /* Arg was a bank number.  Handle in an ASM independent
122              way. */
123           char num[128];
124           strcpy (num, sz + 5);
125           _chomp (num);
126
127           switch (_G.asmType)
128             {
129             case ASM_TYPE_ASXXXX:
130               sprintf (buffer, "CODE_%s", num);
131               break;
132             case ASM_TYPE_RGBDS:
133               sprintf (buffer, "CODE,BANK[%s]", num);
134               break;
135             case ASM_TYPE_ISAS:
136               /* PENDING: what to use for ISAS? */
137               sprintf (buffer, "CODE,BANK(%s)", num);
138               break;
139             default:
140               wassert (0);
141             }
142         }
143       gbz80_port.mem.code_name = gc_strdup (buffer);
144       code->sname = gbz80_port.mem.code_name;
145       return 0;
146     }
147   return 1;
148 }
149
150 static const char *_gbz80_rgbasmCmd[] =
151 {
152   "rgbasm", "-o$1.o", "$1.asm", NULL
153 };
154
155 static const char *_gbz80_rgblinkCmd[] =
156 {
157   "xlink", "-tg", "-n$1.sym", "-m$1.map", "-zFF", "$1.lnk", NULL
158 };
159
160 static void
161 _gbz80_rgblink (void)
162 {
163   FILE *lnkfile;
164   const char *sz;
165
166   int i;
167   sz = srcFileName;
168   if (!sz)
169     sz = "a";
170
171   /* first we need to create the <filename>.lnk file */
172   sprintf (scratchFileName, "%s.lnk", sz);
173   if (!(lnkfile = fopen (scratchFileName, "w")))
174     {
175       werror (E_FILE_OPEN_ERR, scratchFileName);
176       exit (1);
177     }
178
179   fprintf (lnkfile, "[Objects]\n");
180
181   if (srcFileName)
182     fprintf (lnkfile, "%s.o\n", sz);
183
184   for (i = 0; i < nrelFiles; i++)
185     fprintf (lnkfile, "%s\n", relFiles[i]);
186
187   fprintf (lnkfile, "\n[Libraries]\n");
188   /* additional libraries if any */
189   for (i = 0; i < nlibFiles; i++)
190     fprintf (lnkfile, "%s\n", libFiles[i]);
191
192
193   fprintf (lnkfile, "\n[Output]\n" "%s.gb", sz);
194
195   fclose (lnkfile);
196
197   buildCmdLine (buffer,port->linker.cmd, sz, NULL, NULL, NULL);
198   /* call the linker */
199   if (my_system (buffer))
200     {
201       perror ("Cannot exec linker");
202       exit (1);
203     }
204 }
205
206 static bool
207 _parseOptions (int *pargc, char **argv, int *i)
208 {
209   if (argv[*i][0] == '-')
210     {
211       if (argv[*i][1] == 'b' && IS_GB)
212         {
213           int bank = atoi (argv[*i] + 3);
214           char buffer[128];
215           switch (argv[*i][2])
216             {
217             case 'o':
218               /* ROM bank */
219               sprintf (buffer, "CODE_%u", bank);
220               gbz80_port.mem.code_name = gc_strdup (buffer);
221               return TRUE;
222             case 'a':
223               /* RAM bank */
224               sprintf (buffer, "DATA_%u", bank);
225               gbz80_port.mem.data_name = gc_strdup (buffer);
226               return TRUE;
227             }
228         }
229       else if (!strncmp (argv[*i], "--asm=", 6))
230         {
231           if (!strcmp (argv[*i], "--asm=rgbds"))
232             {
233               asm_addTree (&_rgbds_gb);
234               gbz80_port.assembler.cmd = _gbz80_rgbasmCmd;
235               gbz80_port.linker.cmd = _gbz80_rgblinkCmd;
236               gbz80_port.linker.do_link = _gbz80_rgblink;
237               _G.asmType = ASM_TYPE_RGBDS;
238               return TRUE;
239             }
240           else if (!strcmp (argv[*i], "--asm=asxxxx"))
241             {
242               _G.asmType = ASM_TYPE_ASXXXX;
243               return TRUE;
244             }
245           else if (!strcmp (argv[*i], "--asm=isas"))
246             {
247               asm_addTree (&_isas_gb);
248               /* Munge the function prefix */
249               gbz80_port.fun_prefix = "";
250               _G.asmType = ASM_TYPE_ISAS;
251               return TRUE;
252             }
253         }
254     }
255   return FALSE;
256 }
257
258 static void
259 _finaliseOptions (void)
260 {
261   port->mem.default_local_map = data;
262   port->mem.default_globl_map = data;
263   if (_G.asmType == ASM_TYPE_ASXXXX && IS_GB)
264     asm_addTree (&_asxxxx_gb);
265 }
266
267 static void
268 _setDefaultOptions (void)
269 {
270   options.genericPtr = 1;       /* default on */
271   options.nopeep = 0;
272   options.stackAuto = 1;
273   options.mainreturn = 1;
274   /* first the options part */
275   options.intlong_rent = 1;
276   options.noRegParams = 1;
277   /* Default code and data locations. */
278   options.code_loc = 0x200;
279   options.data_loc = 0x8000;
280
281   optimize.global_cse = 1;
282   optimize.label1 = 1;
283   optimize.label2 = 1;
284   optimize.label3 = 1;
285   optimize.label4 = 1;
286   optimize.loopInvariant = 1;
287   optimize.loopInduction = 0;
288 }
289
290 /* Mangaling format:
291     _fun_policy_params
292     where:
293       policy is the function policy
294       params is the parameter format
295
296    policy format:
297     rs
298     where:
299       r is 'r' for reentrant, 's' for static functions
300       s is 'c' for callee saves, 'r' for caller saves
301     examples:
302       rr - reentrant, caller saves
303    params format:
304     A combination of register short names and s to signify stack variables.
305     examples:
306       bds - first two args appear in BC and DE, the rest on the stack
307       s - all arguments are on the stack.
308 */
309 static char *
310 _mangleSupportFunctionName(char *original)
311 {
312   char buffer[128];
313
314   if (TARGET_IS_Z80) 
315     {
316       if (options.noRegParams) 
317         {
318           sprintf(buffer, "%s_rr_s", original);
319         }
320       else 
321         {
322           sprintf(buffer, "%s_rr_bds", original);
323         }
324     }
325   else 
326     {
327       strcpy(buffer, original);
328     }
329
330   return gc_strdup(buffer);
331 }
332
333 static const char *
334 _getRegName (struct regs *reg)
335 {
336   if (reg)
337     return reg->name;
338   assert (0);
339   return "err";
340 }
341
342 /** $1 is always the basename.
343     $2 is always the output file.
344     $3 varies
345     $l is the list of extra options that should be there somewhere...
346     MUST be terminated with a NULL.
347 */
348 static const char *_z80_linkCmd[] =
349 {
350     "link-z80", 
351     "-n",                       // Don't echo output
352     "-c",                       // Command line input
353     "--",                       // Again, command line input...
354     "-b_CODE=0x200",            // Code starts at 0x200
355     "-b_DATA=0x8000",           // RAM starts at 0x8000
356     "-j",                       // Output a symbol file as well
357     "-k" SDCC_LIB_DIR "/z80",   // Library path
358     "-lz80.lib",                // Library to use
359     "-i",                       // Output Intel IHX
360     "$1.ihx",                   // Output to
361     SDCC_LIB_DIR "/z80/crt0.o", // Link in crt0 first
362     "$1.o",                     // Actual code
363     NULL
364 };
365
366 /* sprintf that appends to the string. */
367 static void
368 _saprintf(char *pinto, const char *format, ...)
369 {
370     va_list ap;
371     va_start(ap, format);
372
373     vsprintf(pinto + strlen(pinto), format, ap);
374     va_end(ap);
375 }
376
377 static void
378 _z80_link(void)
379 {
380     int i;
381     // PENDING
382     char buffer[2048];
383
384     sprintf(buffer, 
385             "link-z80 "
386             "-n "                       // Don't echo output
387             "-c "                       // Command line input
388             "-- "                       // Again, command line input...
389             "-b_CODE=0x%04X "           // Code starts at 0x200
390             "-b_DATA=0x%04X "           // RAM starts at 0x8000
391             "-j ",                      // Output a symbol file as well
392             options.code_loc,
393             options.data_loc
394             );
395
396     // Add the standard lib in.
397     if (options.nostdlib == FALSE) {
398         _saprintf(buffer,
399                   "-k" SDCC_LIB_DIR "/z80 "   // Library path
400                   "-lz80.lib "                // Library to use
401                   );
402     }
403
404     // Add in the library paths and libraries
405     for (i = 0; i < nlibFiles; i++) {
406         _saprintf(buffer, "-k%s ", libFiles[i]);
407     }
408     for (i = 0; i < nlibPaths; i++) {
409         _saprintf(buffer, "-l%s ", libPaths[i]);
410     }
411
412     _saprintf(buffer,
413               "-i "                       // Output Intel IHX
414               "%s.ihx ",                  // Output to
415               srcFileName
416               );
417
418     if (options.nostdlib == FALSE) {
419         _saprintf(buffer, 
420                   SDCC_LIB_DIR "/z80/crt0.o " // Link in crt0 first
421                   );
422     }
423
424     _saprintf(buffer,
425               "%s.o ",                    // Actual code
426               srcFileName
427               );
428
429     // Append all the other targets
430     for (i = 0; i < nrelFiles; i++) {
431         _saprintf(buffer, "%s ", relFiles[i]);
432     }
433
434     // Do it.
435     if (my_system (buffer)) {
436         exit(1);
437     }
438 }
439
440 static const char *_z80_asmCmd[] =
441 {
442     "as-z80", "-plosgff", "$1.o", "$1.asm", NULL
443 };
444
445 /** $1 is always the basename.
446     $2 is always the output file.
447     $3 varies
448     $l is the list of extra options that should be there somewhere...
449     MUST be terminated with a NULL.
450 */
451 static const char *_gbz80_linkCmd[] =
452 {
453     // PENDING
454     "link-gbz80", "-nf", "$1", NULL
455 };
456
457 static const char *_gbz80_asmCmd[] =
458 {
459     "as-gbz80", "-plosgff", "$1.o", "$1.asm", NULL
460 };
461
462 /* Globals */
463 PORT z80_port =
464 {
465   TARGET_ID_Z80,
466   "z80",
467   "Zilog Z80",                  /* Target name */
468   {
469     FALSE,
470     MODEL_MEDIUM | MODEL_SMALL,
471     MODEL_SMALL
472   },
473   {
474     _z80_asmCmd,
475     "-plosgff",                 /* Options with debug */
476     "-plosgff",                 /* Options without debug */
477     0,
478     ".asm"
479   },
480   {
481     _z80_linkCmd,
482     _z80_link,
483     ".o"
484   },
485   {
486     _z80_defaultRules
487   },
488   {
489         /* Sizes: char, short, int, long, ptr, fptr, gptr, bit, float, max */
490     1, 2, 2, 4, 2, 2, 2, 1, 4, 4
491   },
492   {
493     "XSEG",
494     "STACK",
495     "CODE",
496     "DATA",
497     "ISEG",
498     "XSEG",
499     "BSEG",
500     "RSEG",
501     "GSINIT",
502     "OVERLAY",
503     "GSFINAL",
504     "HOME",
505     NULL,
506     NULL,
507     1
508   },
509   {
510     -1, 0, 0, 4, 0, 2
511   },
512     /* Z80 has no native mul/div commands */
513   {
514     0, 2
515   },
516   "_",
517   _z80_init,
518   _parseOptions,
519   _finaliseOptions,
520   _setDefaultOptions,
521   z80_assignRegisters,
522   _getRegName,
523   _keywords,
524   0,                            /* no assembler preamble */
525   0,                            /* no local IVT generation code */
526   _reset_regparm,
527   _reg_parm,
528   _process_pragma,
529   _mangleSupportFunctionName,
530   TRUE,
531   0,                            /* leave lt */
532   0,                            /* leave gt */
533   1,                            /* transform <= to ! > */
534   1,                            /* transform >= to ! < */
535   1,                            /* transform != to !(a == b) */
536   0,                            /* leave == */
537   PORT_MAGIC
538 };
539
540 /* Globals */
541 PORT gbz80_port =
542 {
543   TARGET_ID_GBZ80,
544   "gbz80",
545   "Gameboy Z80-like",           /* Target name */
546   {
547     FALSE,
548     MODEL_MEDIUM | MODEL_SMALL,
549     MODEL_SMALL
550   },
551   {
552     _gbz80_asmCmd,
553     "-plosgff",                 /* Options with debug */
554     "-plosgff",                 /* Options without debug */
555     1,
556     ".asm"
557   },
558   {
559     _gbz80_linkCmd,
560     NULL,
561     ".o"
562   },
563   {
564     _gbz80_defaultRules
565   },
566   {
567         /* Sizes: char, short, int, long, ptr, fptr, gptr, bit, float, max */
568     1, 1, 2, 4, 2, 2, 2, 1, 4, 4
569   },
570   {
571     "XSEG",
572     "STACK",
573     "CODE",
574     "DATA",
575     "ISEG",
576     "XSEG",
577     "BSEG",
578     "RSEG",
579     "GSINIT",
580     "OVERLAY",
581     "GSFINAL",
582     "HOME",
583     NULL,
584     NULL,
585     1
586   },
587   {
588     -1, 0, 0, 2, 0, 4
589   },
590     /* gbZ80 has no native mul/div commands */
591   {
592     0, 2
593   },
594   "_",
595   _gbz80_init,
596   _parseOptions,
597   _finaliseOptions,
598   _setDefaultOptions,
599   z80_assignRegisters,
600   _getRegName,
601   _keywords,
602   0,                            /* no assembler preamble */
603   0,                            /* no local IVT generation code */
604   _reset_regparm,
605   _reg_parm,
606   _process_pragma,
607   NULL,
608   TRUE,
609   0,                            /* leave lt */
610   0,                            /* leave gt */
611   1,                            /* transform <= to ! > */
612   1,                            /* transform >= to ! < */
613   1,                            /* transform != to !(a == b) */
614   0,                            /* leave == */
615   PORT_MAGIC
616 };