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