* src/port.h,
[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 /* Indicate which extended bit operations this port supports */
482 static bool
483 hasExtBitOp (int op, int size)
484 {
485   if (op == GETHBIT)
486     return TRUE;
487   else
488     return FALSE;
489 }
490
491 /* Indicate the expense of an access to an output storage class */
492 static int
493 oclsExpense (struct memmap *oclass)
494 {
495   if (IN_FARSPACE(oclass))
496     return 1;
497     
498   return 0;
499 }
500
501
502 #define LINKCMD "link-{port} -nf {dstfilename}"
503 /*
504 #define LINKCMD \
505     "link-{port} -n -c -- {z80bases} -m -j" \
506     " {z80libspec}" \
507     " {z80extralibfiles} {z80extralibpaths}" \
508     " {z80outputtypeflag} \"{linkdstfilename}\"" \
509     " {z80crt0}" \
510     " \"{dstfilename}{objext}\"" \
511     " {z80extraobj}"
512 */
513
514 #define ASMCMD \
515     "as-{port} -plosgff \"{objdstfilename}\" \"{dstfilename}{asmext}\""
516
517 /* Globals */
518 PORT z80_port =
519 {
520   TARGET_ID_Z80,
521   "z80",
522   "Zilog Z80",                  /* Target name */
523   NULL,                         /* Processor name */
524   {
525     glue,
526     FALSE,
527     MODEL_MEDIUM | MODEL_SMALL,
528     MODEL_SMALL
529   },
530   {
531     NULL,
532     ASMCMD,
533     "-plosgff",                 /* Options with debug */
534     "-plosgff",                 /* Options without debug */
535     0,
536     ".asm"
537   },
538   {
539     NULL,
540     LINKCMD,
541     NULL,
542     ".o"
543   },
544   {
545     _z80_defaultRules
546   },
547   {
548         /* Sizes: char, short, int, long, ptr, fptr, gptr, bit, float, max */
549     1, 2, 2, 4, 2, 2, 2, 1, 4, 4
550   },
551   {
552     "XSEG",
553     "STACK",
554     "CODE",
555     "DATA",
556     "ISEG",
557     "XSEG",
558     "BSEG",
559     "RSEG",
560     "GSINIT",
561     "OVERLAY",
562     "GSFINAL",
563     "HOME",
564     NULL, /* xidata */
565     NULL, /* xinit */
566     NULL,
567     NULL,
568     1
569   },
570   { NULL, NULL },
571   {
572     -1, 0, 0, 4, 0, 2
573   },
574     /* Z80 has no native mul/div commands */
575   {
576     0, 2
577   },
578   "_",
579   _z80_init,
580   _parseOptions,
581   _z80_options,
582   _finaliseOptions,
583   _setDefaultOptions,
584   z80_assignRegisters,
585   _getRegName,
586   _keywords,
587   0,                            /* no assembler preamble */
588   NULL,                         /* no genAssemblerEnd */
589   0,                            /* no local IVT generation code */
590   0,                            /* no genXINIT code */
591   _reset_regparm,
592   _reg_parm,
593   _process_pragma,
594   _mangleSupportFunctionName,
595   _hasNativeMulFor,
596   hasExtBitOp,                  /* hasExtBitOp */
597   oclsExpense,                  /* oclsExpense */
598   TRUE,
599   TRUE,                         /* little endian */
600   0,                            /* leave lt */
601   0,                            /* leave gt */
602   1,                            /* transform <= to ! > */
603   1,                            /* transform >= to ! < */
604   1,                            /* transform != to !(a == b) */
605   0,                            /* leave == */
606   TRUE,                         /* Array initializer support. */        
607   0,                            /* no CSE cost estimation yet */
608   _z80_builtins,                /* no builtin functions */
609   GPOINTER,                     /* treat unqualified pointers as "generic" pointers */
610   1,                            /* reset labelKey to 1 */
611   1,                            /* globals & local static allowed */
612   PORT_MAGIC
613 };
614
615 /* Globals */
616 PORT gbz80_port =
617 {
618   TARGET_ID_GBZ80,
619   "gbz80",
620   "Gameboy Z80-like",           /* Target name */
621   NULL,
622   {
623     glue,
624     FALSE,
625     MODEL_MEDIUM | MODEL_SMALL,
626     MODEL_SMALL
627   },
628   {
629     NULL,
630     ASMCMD,
631     "-plosgff",                 /* Options with debug */
632     "-plosgff",                 /* Options without debug */
633     0,
634     ".asm",
635     NULL                        /* no do_assemble function */
636   },
637   {
638     NULL,
639     LINKCMD,
640     NULL,
641     ".o"
642   },
643   {
644     _gbz80_defaultRules
645   },
646   {
647     /* Sizes: char, short, int, long, ptr, fptr, gptr, bit, float, max */
648     1, 2, 2, 4, 2, 2, 2, 1, 4, 4
649   },
650   {
651     "XSEG",
652     "STACK",
653     "CODE",
654     "DATA",
655     "ISEG",
656     "XSEG",
657     "BSEG",
658     "RSEG",
659     "GSINIT",
660     "OVERLAY",
661     "GSFINAL",
662     "HOME",
663     NULL, /* xidata */
664     NULL, /* xinit */
665     NULL,
666     NULL,
667     1
668   },
669   { NULL, NULL },
670   {
671     -1, 0, 0, 2, 0, 4
672   },
673     /* gbZ80 has no native mul/div commands */
674   {
675     0, 2
676   },
677   "_",
678   _gbz80_init,
679   _parseOptions,
680   _z80_options,
681   _finaliseOptions,
682   _setDefaultOptions,
683   z80_assignRegisters,
684   _getRegName,
685   _keywords,
686   0,                            /* no assembler preamble */
687   NULL,                         /* no genAssemblerEnd */
688   0,                            /* no local IVT generation code */
689   0,                            /* no genXINIT code */
690   _reset_regparm,
691   _reg_parm,
692   _process_pragma,
693   _mangleSupportFunctionName,
694   _hasNativeMulFor,
695   hasExtBitOp,                  /* hasExtBitOp */
696   oclsExpense,                  /* oclsExpense */
697   TRUE,
698   TRUE,                         /* little endian */
699   0,                            /* leave lt */
700   0,                            /* leave gt */
701   1,                            /* transform <= to ! > */
702   1,                            /* transform >= to ! < */
703   1,                            /* transform != to !(a == b) */
704   0,                            /* leave == */
705   TRUE,                         /* Array initializer support. */
706   0,                            /* no CSE cost estimation yet */
707   NULL,                         /* no builtin functions */
708   GPOINTER,                     /* treat unqualified pointers as "generic" pointers */
709   1,                            /* reset labelKey to 1 */
710   1,                            /* globals & local static allowed */
711   PORT_MAGIC
712 };