DS800C400 fun, improved ROM interface and tinibios
[fw/sdcc] / src / z80 / main.c
1 /*-------------------------------------------------------------------------
2   main.c - Z80 specific definitions.
3
4   Michael Hope <michaelh@juju.net.nz> 2001
5
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20    In other words, you are welcome to use, share and improve this program.
21    You are forbidden to forbid anyone else to use, share and improve
22    what you give them.   Help stamp out software-hoarding!
23 -------------------------------------------------------------------------*/
24
25 #include <sys/stat.h>
26 #include "z80.h"
27 #include "MySystem.h"
28 #include "BuildCmd.h"
29 #include "SDCCutil.h"
30 #include "dbuf.h"
31
32 static char _z80_defaultRules[] =
33 {
34 #include "peeph.rul"
35 #include "peeph-z80.rul"
36 };
37
38 static char _gbz80_defaultRules[] =
39 {
40 #include "peeph.rul"
41 #include "peeph-gbz80.rul"
42 };
43
44 Z80_OPTS z80_opts;
45
46 static OPTION _z80_options[] = 
47   {
48     { 0,   "--callee-saves-bc", &z80_opts.calleeSavesBC, "Force a called function to always save BC" },
49     { 0, NULL }
50   };
51
52 typedef enum
53   {
54     /* Must be first */
55     ASM_TYPE_ASXXXX,
56     ASM_TYPE_RGBDS,
57     ASM_TYPE_ISAS,
58     ASM_TYPE_Z80ASM
59   }
60 ASM_TYPE;
61
62 static struct
63   {
64     ASM_TYPE asmType;
65     /* determine if we can register a parameter */    
66     int regParams;
67   }
68 _G;
69
70 static char *_keywords[] =
71 {
72   "sfr",
73   "nonbanked",
74   "banked",
75   NULL
76 };
77
78 extern PORT gbz80_port;
79 extern PORT z80_port;
80
81 #include "mappings.i"
82
83 static builtins _z80_builtins[] = {
84   /* Disabled for now.
85     { "__builtin_strcpy", "v", 2, {"cg*", "cg*" } },
86     { "__builtin_memcpy", "cg*", 3, {"cg*", "cg*", "ui" } },
87   */
88     { NULL , NULL,0, {NULL}}
89 };    
90
91 static void
92 _z80_init (void)
93 {
94   z80_opts.sub = SUB_Z80;
95   asm_addTree (&_asxxxx_z80);
96 }
97
98 static void
99 _gbz80_init (void)
100 {
101   z80_opts.sub = SUB_GBZ80;
102 }
103
104 static void
105 _reset_regparm ()
106 {
107   _G.regParams = 0;
108 }
109
110 static int
111 _reg_parm (sym_link * l)
112 {
113   if (options.noRegParams) 
114     {
115       return FALSE;
116     }
117   else 
118     {
119       if (_G.regParams == 2)
120         {
121           return FALSE;
122         }
123       else
124         {
125           _G.regParams++;
126           return TRUE;
127         }
128     }
129 }
130
131 static int
132 _process_pragma (const char *sz)
133 {
134   if (startsWith (sz, "bank="))
135     {
136       char buffer[128];
137       strcpy (buffer, sz + 5);
138       chomp (buffer);
139       if (isdigit (buffer[0]))
140         {
141
142         }
143       else if (!strcmp (buffer, "BASE"))
144         {
145           strcpy (buffer, "HOME");
146         }
147       if (isdigit (buffer[0]))
148         {
149           /* Arg was a bank number.  Handle in an ASM independent
150              way. */
151           char num[128];
152           strcpy (num, sz + 5);
153           chomp (num);
154
155           switch (_G.asmType)
156             {
157             case ASM_TYPE_ASXXXX:
158               sprintf (buffer, "CODE_%s", num);
159               break;
160             case ASM_TYPE_RGBDS:
161               sprintf (buffer, "CODE,BANK[%s]", num);
162               break;
163             case ASM_TYPE_ISAS:
164               /* PENDING: what to use for ISAS? */
165               sprintf (buffer, "CODE,BANK(%s)", num);
166               break;
167             default:
168               wassert (0);
169             }
170         }
171       gbz80_port.mem.code_name = Safe_strdup (buffer);
172       code->sname = gbz80_port.mem.code_name;
173       return 0;
174     }
175   return 1;
176 }
177
178 static const char *_gbz80_rgbasmCmd[] =
179 {
180   "rgbasm", "-o\"$1.o\"", "\"$1.asm\"", NULL
181 };
182
183 static const char *_gbz80_rgblinkCmd[] =
184 {
185   "xlink", "-tg", "-n\"$1.sym\"", "-m\"$1.map\"", "-zFF", "\"$1.lnk\"", NULL
186 };
187
188 static void
189 _gbz80_rgblink (void)
190 {
191   FILE *lnkfile;
192
193   /* first we need to create the <filename>.lnk file */
194   sprintf (scratchFileName, "%s.lnk", dstFileName);
195   if (!(lnkfile = fopen (scratchFileName, "w")))
196     {
197       werror (E_FILE_OPEN_ERR, scratchFileName);
198       exit (1);
199     }
200
201   fprintf (lnkfile, "[Objects]\n");
202
203   fprintf (lnkfile, "%s.o\n", dstFileName);
204
205   fputStrSet(lnkfile, relFilesSet);
206
207   fprintf (lnkfile, "\n[Libraries]\n");
208   /* additional libraries if any */
209   fputStrSet(lnkfile, libFilesSet);
210
211   fprintf (lnkfile, "\n[Output]\n" "%s.gb", dstFileName);
212
213   fclose (lnkfile);
214
215   buildCmdLine (buffer,port->linker.cmd, dstFileName, NULL, NULL, NULL);
216   /* call the linker */
217   if (my_system (buffer))
218     {
219       perror ("Cannot exec linker");
220       exit (1);
221     }
222 }
223
224 static bool
225 _parseOptions (int *pargc, char **argv, int *i)
226 {
227   if (argv[*i][0] == '-')
228     {
229       if (argv[*i][1] == 'b' && IS_GB)
230         {
231           int bank = atoi (argv[*i] + 3);
232           char buffer[128];
233           switch (argv[*i][2])
234             {
235             case 'o':
236               /* ROM bank */
237               sprintf (buffer, "CODE_%u", bank);
238               gbz80_port.mem.code_name = Safe_strdup (buffer);
239               return TRUE;
240             case 'a':
241               /* RAM bank */
242               sprintf (buffer, "DATA_%u", bank);
243               gbz80_port.mem.data_name = Safe_strdup (buffer);
244               return TRUE;
245             }
246         }
247       else if (!strncmp (argv[*i], "--asm=", 6))
248         {
249           if (!strcmp (argv[*i], "--asm=rgbds"))
250             {
251               asm_addTree (&_rgbds_gb);
252               gbz80_port.assembler.cmd = _gbz80_rgbasmCmd;
253               gbz80_port.linker.cmd = _gbz80_rgblinkCmd;
254               gbz80_port.linker.do_link = _gbz80_rgblink;
255               _G.asmType = ASM_TYPE_RGBDS;
256               return TRUE;
257             }
258           else if (!strcmp (argv[*i], "--asm=asxxxx"))
259             {
260               _G.asmType = ASM_TYPE_ASXXXX;
261               return TRUE;
262             }
263           else if (!strcmp (argv[*i], "--asm=isas"))
264             {
265               asm_addTree (&_isas_gb);
266               /* Munge the function prefix */
267               gbz80_port.fun_prefix = "";
268               _G.asmType = ASM_TYPE_ISAS;
269               return TRUE;
270             }
271           else if (!strcmp (argv[*i], "--asm=z80asm"))
272             {
273               port->assembler.externGlobal = TRUE;
274               asm_addTree (&_z80asm_z80);
275               _G.asmType = ASM_TYPE_ISAS;
276               return TRUE;
277             }
278         }
279     }
280   return FALSE;
281 }
282
283 static void
284 _setValues(void)
285 {
286   const char *s;
287
288   if (options.nostdlib == FALSE)
289     {
290       const char *s;
291       char path[PATH_MAX];
292       struct dbuf_s dbuf;
293
294       dbuf_init(&dbuf, PATH_MAX);
295
296       for (s = setFirstItem(libDirsSet); s != NULL; s = setNextItem(libDirsSet))
297         {
298           buildCmdLine2(path, sizeof path, "-k\"%s" DIR_SEPARATOR_STRING "{port}\" ", s);
299           dbuf_append(&dbuf, path, strlen(path));
300         }
301       buildCmdLine2(path, sizeof path, "-l\"{port}.lib\"", s);
302       dbuf_append(&dbuf, path, strlen(path));
303
304       setMainValue ("z80libspec", dbuf_c_str(&dbuf));
305       dbuf_destroy(&dbuf);
306
307       for (s = setFirstItem(libDirsSet); s != NULL; s = setNextItem(libDirsSet))
308         {
309           struct stat stat_buf;
310
311           buildCmdLine2(path, sizeof path, "%s" DIR_SEPARATOR_STRING "{port}" DIR_SEPARATOR_STRING "crt0{objext}", s);
312           if (stat(path, &stat_buf) == 0)
313             break;
314         }
315
316       if (s == NULL)
317         setMainValue ("z80crt0", "\"crt0{objext}\"");
318       else
319         {
320           char *buf;
321           size_t len = strlen(path) + 3;
322
323           buf = Safe_alloc(len);
324           SNPRINTF(buf, len, "\"%s\"", path);
325           setMainValue("z80crt0", buf);
326           Safe_free(buf);
327         } 
328     }
329   else
330     {
331       setMainValue ("z80libspec", "");
332       setMainValue ("z80crt0", "");
333     }
334
335   setMainValue ("z80extralibfiles", (s = joinStrSet(libFilesSet)));
336   Safe_free((void *)s);
337   setMainValue ("z80extralibpaths", (s = joinStrSet(libPathsSet)));
338   Safe_free((void *)s);
339
340   if (IS_GB)
341     {
342       setMainValue ("z80outputtypeflag", "-z");
343       setMainValue ("z80outext", ".gb");
344     }
345   else
346     {
347       setMainValue ("z80outputtypeflag", "-i");
348       setMainValue ("z80outext", ".ihx");
349     }
350
351   setMainValue ("stdobjdstfilename" , "{dstfilename}{objext}");
352   setMainValue ("stdlinkdstfilename", "{dstfilename}{z80outext}");
353
354   setMainValue ("z80extraobj", (s = joinStrSet(relFilesSet)));
355   Safe_free((void *)s);
356
357   sprintf (buffer, "-b_CODE=0x%04X -b_DATA=0x%04X", options.code_loc, options.data_loc);
358   setMainValue ("z80bases", buffer);
359 }
360
361 static void
362 _finaliseOptions (void)
363 {
364   port->mem.default_local_map = data;
365   port->mem.default_globl_map = data;
366   if (_G.asmType == ASM_TYPE_ASXXXX && IS_GB)
367     asm_addTree (&_asxxxx_gb);
368
369   _setValues();
370 }
371
372 static void
373 _setDefaultOptions (void)
374 {
375   options.nopeep = 0;
376   options.stackAuto = 1;
377   options.mainreturn = 1;
378   /* first the options part */
379   options.intlong_rent = 1;
380   options.float_rent = 1;
381   options.noRegParams = 1;
382   /* Default code and data locations. */
383   options.code_loc = 0x200;
384
385   if (IS_GB) 
386     {
387       options.data_loc = 0xC000;
388     }
389   else
390     {
391       options.data_loc = 0x8000;
392     }
393
394   optimize.global_cse = 1;
395   optimize.label1 = 1;
396   optimize.label2 = 1;
397   optimize.label3 = 1;
398   optimize.label4 = 1;
399   optimize.loopInvariant = 1;
400   optimize.loopInduction = 1;
401 }
402
403 /* Mangaling format:
404     _fun_policy_params
405     where:
406       policy is the function policy
407       params is the parameter format
408
409    policy format:
410     rsp
411     where:
412       r is 'r' for reentrant, 's' for static functions
413       s is 'c' for callee saves, 'r' for caller saves
414       p is 'p' for profiling on, 'x' for profiling off
415     examples:
416       rr - reentrant, caller saves
417    params format:
418     A combination of register short names and s to signify stack variables.
419     examples:
420       bds - first two args appear in BC and DE, the rest on the stack
421       s - all arguments are on the stack.
422 */
423 static char *
424 _mangleSupportFunctionName(char *original)
425 {
426   char buffer[128];
427
428   sprintf(buffer, "%s_rr%s_%s", original,
429           options.profile ? "f" : "x",
430           options.noRegParams ? "s" : "bds"
431           );
432
433   return Safe_strdup(buffer);
434 }
435
436 static const char *
437 _getRegName (struct regs *reg)
438 {
439   if (reg)
440     {
441       return reg->name;
442     }
443   /*  assert (0); */
444   return "err";
445 }
446
447 static bool
448 _hasNativeMulFor (iCode *ic, sym_link *left, sym_link *right)
449 {
450   sym_link *test = NULL;
451   value *val;
452
453   if ( ic->op != '*')
454     {
455       return FALSE;
456     }
457
458   if ( IS_LITERAL (left))
459     {
460       test = left;
461       val = OP_VALUE (IC_LEFT (ic));
462     }
463   else if ( IS_LITERAL (right))
464     {
465       test = left;
466       val = OP_VALUE (IC_RIGHT (ic));
467     }
468   else
469     {
470       return FALSE;
471     }
472
473   if ( getSize (test) <= 2)
474     {
475       return TRUE;
476     }
477
478   return FALSE;
479 }
480
481 #define LINKCMD \
482     "link-{port} -n -c -- {z80bases} -m -j" \
483     " {z80libspec}" \
484     " {z80extralibfiles} {z80extralibpaths}" \
485     " {z80outputtypeflag} \"{linkdstfilename}\"" \
486     " {z80crt0}" \
487     " \"{dstfilename}{objext}\"" \
488     " {z80extraobj}"
489
490 #define ASMCMD \
491     "as-{port} -plosgff \"{objdstfilename}\" \"{dstfilename}{asmext}\""
492
493 /* Globals */
494 PORT z80_port =
495 {
496   TARGET_ID_Z80,
497   "z80",
498   "Zilog Z80",                  /* Target name */
499   NULL,                         /* Processor name */
500   {
501     FALSE,
502     MODEL_MEDIUM | MODEL_SMALL,
503     MODEL_SMALL
504   },
505   {
506     NULL,
507     ASMCMD,
508     "-plosgff",                 /* Options with debug */
509     "-plosgff",                 /* Options without debug */
510     0,
511     ".asm"
512   },
513   {
514     NULL,
515     LINKCMD,
516     NULL,
517     ".o"
518   },
519   {
520     _z80_defaultRules
521   },
522   {
523         /* Sizes: char, short, int, long, ptr, fptr, gptr, bit, float, max */
524     1, 2, 2, 4, 2, 2, 2, 1, 4, 4
525   },
526   {
527     "XSEG",
528     "STACK",
529     "CODE",
530     "DATA",
531     "ISEG",
532     "XSEG",
533     "BSEG",
534     "RSEG",
535     "GSINIT",
536     "OVERLAY",
537     "GSFINAL",
538     "HOME",
539     NULL, /* xidata */
540     NULL, /* xinit */
541     NULL,
542     NULL,
543     1
544   },
545   { NULL, NULL },
546   {
547     -1, 0, 0, 4, 0, 2
548   },
549     /* Z80 has no native mul/div commands */
550   {
551     0, 2
552   },
553   "_",
554   _z80_init,
555   _parseOptions,
556   _z80_options,
557   _finaliseOptions,
558   _setDefaultOptions,
559   z80_assignRegisters,
560   _getRegName,
561   _keywords,
562   0,                            /* no assembler preamble */
563   NULL,                         /* no genAssemblerEnd */
564   0,                            /* no local IVT generation code */
565   0,                            /* no genXINIT code */
566   _reset_regparm,
567   _reg_parm,
568   _process_pragma,
569   _mangleSupportFunctionName,
570   _hasNativeMulFor,
571   TRUE,
572   0,                            /* leave lt */
573   0,                            /* leave gt */
574   1,                            /* transform <= to ! > */
575   1,                            /* transform >= to ! < */
576   1,                            /* transform != to !(a == b) */
577   0,                            /* leave == */
578   TRUE,                         /* Array initializer support. */        
579   0,                            /* no CSE cost estimation yet */
580   _z80_builtins,                /* no builtin functions */
581   GPOINTER,                     /* treat unqualified pointers as "generic" pointers */
582   1,                            /* reset labelKey to 1 */
583   1,                            /* globals & local static allowed */
584   PORT_MAGIC
585 };
586
587 /* Globals */
588 PORT gbz80_port =
589 {
590   TARGET_ID_GBZ80,
591   "gbz80",
592   "Gameboy Z80-like",           /* Target name */
593   NULL,
594   {
595     FALSE,
596     MODEL_MEDIUM | MODEL_SMALL,
597     MODEL_SMALL
598   },
599   {
600     NULL,
601     ASMCMD,
602     "-plosgff",                 /* Options with debug */
603     "-plosgff",                 /* Options without debug */
604     0,
605     ".asm",
606     NULL                        /* no do_assemble function */
607   },
608   {
609     NULL,
610     LINKCMD,
611     NULL,
612     ".o"
613   },
614   {
615     _gbz80_defaultRules
616   },
617   {
618     /* Sizes: char, short, int, long, ptr, fptr, gptr, bit, float, max */
619     1, 2, 2, 4, 2, 2, 2, 1, 4, 4
620   },
621   {
622     "XSEG",
623     "STACK",
624     "CODE",
625     "DATA",
626     "ISEG",
627     "XSEG",
628     "BSEG",
629     "RSEG",
630     "GSINIT",
631     "OVERLAY",
632     "GSFINAL",
633     "HOME",
634     NULL, /* xidata */
635     NULL, /* xinit */
636     NULL,
637     NULL,
638     1
639   },
640   { NULL, NULL },
641   {
642     -1, 0, 0, 2, 0, 4
643   },
644     /* gbZ80 has no native mul/div commands */
645   {
646     0, 2
647   },
648   "_",
649   _gbz80_init,
650   _parseOptions,
651   _z80_options,
652   _finaliseOptions,
653   _setDefaultOptions,
654   z80_assignRegisters,
655   _getRegName,
656   _keywords,
657   0,                            /* no assembler preamble */
658   NULL,                         /* no genAssemblerEnd */
659   0,                            /* no local IVT generation code */
660   0,                            /* no genXINIT code */
661   _reset_regparm,
662   _reg_parm,
663   _process_pragma,
664   _mangleSupportFunctionName,
665   _hasNativeMulFor,
666   TRUE,
667   0,                            /* leave lt */
668   0,                            /* leave gt */
669   1,                            /* transform <= to ! > */
670   1,                            /* transform >= to ! < */
671   1,                            /* transform != to !(a == b) */
672   0,                            /* leave == */
673   TRUE,                         /* Array initializer support. */
674   0,                            /* no CSE cost estimation yet */
675   NULL,                         /* no builtin functions */
676   GPOINTER,                     /* treat unqualified pointers as "generic" pointers */
677   1,                            /* reset labelKey to 1 */
678   1,                            /* globals & local static allowed */
679   PORT_MAGIC
680 };