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