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