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