-#This file was created by <sandeep> Thu Oct 14 02:37:27 1999
-#LyX 1.0 (C) 1995-1999 Matthias Ettrich and the LyX Team
+#LyX 1.1 created this file. For more info see http://www.lyx.org/
\lyxformat 2.15
\textclass linuxdoc
\language english
\layout Standard
Unknown - for the GNU C - preprocessor.
+\layout Section
+
+Appendix A: The Z80 and gbz80 port
+\layout Standard
+
+2.2.0 can target both the Zilog Z80 and the Nintendo Gameboy's Z80-like gbz80.
+ The port is incomplete - long support is incomplete (mul, div and mod are
+ unimplimented), and both float and bitfield support is missing, but apart
+ from that the code generated is correct.
+\layout Standard
+
+As always, the code is the authoritave reference - see z80/ralloc.c and z80/gen.c.
+ The stack frame is similar to that generated by the IAR Z80 compiler.
+ IX is used as the base pointer, HL is used as a temporary register, and
+ BC and DE are available for holding varibles.
+ IY is currently unusued.
+ Return values are stored in HL.
+ One bad side effect of using IX as the base pointer is that a functions
+ stack frame is limited to 127 bytes - this will be fixed in a later version.bc
+\layout Standard
+
+9
\layout Standard
(*s != '\n'))
s++ ;
+ /* First give the port a chance */
+ if (port->process_pragma && !port->process_pragma(cp))
+ return 0;
+
/* now compare and do what needs to be done */
if (strncmp(cp,PRAGMA_SAVE,strlen(PRAGMA_SAVE)) == 0) {
doPragma(P_SAVE,cp+strlen(PRAGMA_SAVE));
extern int maxInterrupts;
void parseWithComma (char **,char *) ;
+/** Creates a temporary file a'la tmpfile which avoids the bugs
+ in cygwin wrt c:\tmp.
+ Scans, in order: TMP, TEMP, TMPDIR, else uses tmpfile().
+*/
+FILE *tempfile(void);
+
#endif
extern char *VersionString;
extern FILE *codeOutFile;
set *tmpfileSet = NULL; /* set of tmp file created by the compiler */
+set *tmpfileNameSet = NULL; /* All are unlinked at close. */
/*-----------------------------------------------------------------*/
/* closeTmpFiles - closes all tmp files created by the compiler */
/* because of BRAIN DEAD MS/DOS & CYGNUS Libraries */
return 0;
}
+/*-----------------------------------------------------------------*/
+/* rmTmpFiles - closes all tmp files created by the compiler */
+/* because of BRAIN DEAD MS/DOS & CYGNUS Libraries */
+/*-----------------------------------------------------------------*/
+DEFSETFUNC(rmTmpFiles)
+{
+ char *name = item;
+
+ if (name) {
+ unlink(name);
+ free(name);
+ }
+ return 0;
+}
+
/*-----------------------------------------------------------------*/
/* copyFile - copies source file to destination file */
/*-----------------------------------------------------------------*/
{
FILE *vFile;
FILE *asmFile;
- FILE *ovrFile = tmpfile();
+ FILE *ovrFile = tempfile();
addSetHead(&tmpfileSet,ovrFile);
/* print the global struct definitions */
if (options.debug)
cdbStructBlock (0,cdbFile);
- vFile = tmpfile();
+ vFile = tempfile();
/* PENDING: this isnt the best place but it will do */
if (port->general.glue_up_main) {
/* create the interrupt vector table */
fclose (asmFile);
applyToSet(tmpfileSet,closeTmpFiles);
+ applyToSet(tmpfileNameSet, rmTmpFiles);
+}
+
+/** Creates a temporary file a'la tmpfile which avoids the bugs
+ in cygwin wrt c:\tmp.
+ Scans, in order: TMP, TEMP, TMPDIR, else uses tmpfile().
+*/
+FILE *tempfile(void)
+{
+ const char *tmpdir = NULL;
+ if (getenv("TMP"))
+ tmpdir = getenv("TMP");
+ else if (getenv("TEMP"))
+ tmpdir = getenv("TEMP");
+ else if (getenv("TMPDIR"))
+ tmpdir = getenv("TMPDIR");
+ if (tmpdir) {
+ char *name = tempnam(tmpdir, "sdcc");
+ if (name) {
+ FILE *fp = fopen(name, "w+b");
+ if (fp)
+ addSetHead(&tmpfileNameSet, name);
+ return fp;
+ }
+ return NULL;
+ }
+ return tmpfile();
}
map->sname = name ;
map->dbName = dbName ;
map->ptrType= ptrType;
- if (!(map->oFile = tmpfile())) {
+ if (!(map->oFile = tempfile())) {
werror(E_TMPFILE_FAILED);
exit (1);
}
_mcs51_genAssemblerPreamble,
_mcs51_genIVT ,
_mcs51_reset_regparm,
- _mcs51_regparm
+ _mcs51_regparm,
+ NULL
};
int call_overhead;
/** Re-enterant space */
int reent_overhead;
-
} stack;
+
struct {
/** One more than the smallest
mul/div operation the processor can do nativley
/* parameter passing in register related functions */
void (*reset_regparms)(); /* reset the register count */
int (*reg_parm)(struct link *); /* will return 1 if can be passed in register */
+
+ /** Process the pragma string 'sz'. Returns 0 if recognised and
+ processed, 1 otherwise. May be NULL.
+ */
+ int (*process_pragma)(const char *sz);
} PORT;
include $(PRJDIR)/Makefile.common
-OBJ = gen.o ralloc.o main.o gbz80.o
+OBJ = gen.o ralloc.o main.o
LIB = port.a
CFLAGS = -ggdb -Wall
%.rul: %.def
$(AWK) -f ../SDCCpeeph.awk $< > $@
-main.o: main.c peeph.rul peeph-z80.rul
-gbz80.o: gbz80.c peeph.rul peeph-gbz80.rul
+main.o: main.c peeph.rul peeph-z80.rul peeph-gbz80.rul
include clean.mk
# !include $(PRJDIR)/Makefile.common
-OBJ = gen.obj ralloc.obj main.obj gbz80.obj
+OBJ = gen.obj ralloc.obj main.obj
LIB = port.lib
!include ..\..\Bcc.inc
all: $(LIB)
-main.obj: main.c peeph.rul
-gbz80.obj: gbz80.c peeph-gbz80.rul
+main.obj: main.c peeph.rul peeph-z80.rul peeph-gbz80.rul
$(LIB): peeph.rul $(OBJ)
del $(LIB)
- tlib /a $(LIB) +gen.obj +ralloc.obj +main.obj +gbz80.obj
-
+ tlib /a $(LIB) +gen.obj +ralloc.obj +main.obj
+
peeph.rul: peeph.def
gawk -f ../SDCCpeeph.awk peeph.def > peeph.rul
peeph-gbz80.rul: peeph-gbz80.def
gawk -f ../SDCCpeeph.awk peeph-gbz80.def > peeph-gbz80.rul
+peeph-z80.rul: peeph-z80.def
+ gawk -f ../SDCCpeeph.awk peeph-z80.def > peeph-z80.rul
+
# include clean.mk
#include "z80.h"
-static char _defaultRules[] =
+static char _z80_defaultRules[] =
{
#include "peeph.rul"
#include "peeph-z80.rul"
};
+static char _gbz80_defaultRules[] =
+{
+#include "peeph.rul"
+#include "peeph-gbz80.rul"
+};
+
Z80_OPTS z80_opts;
-static char *_z80_keywords[] = { NULL };
+static char *_keywords[] = { NULL };
static void _z80_init(void)
{
z80_opts.sub = SUB_Z80;
}
+static void _gbz80_init(void)
+{
+ z80_opts.sub = SUB_GBZ80;
+}
+
static int regParmFlg = 0; /* determine if we can register a parameter */
-static void _z80_reset_regparm()
+static void _reset_regparm()
{
regParmFlg = 0;
}
-static int _z80_reg_parm(link *l)
+static int _reg_parm(link *l)
{
/* for this processor it is simple
can pass only the first parameter in a register */
}
-static bool _z80_parseOptions(int *pargc, char **argv, int *i)
+static int _process_pragma(const char *sz)
+{
+ printf("Got pragma \"%s\"\n", sz);
+ return 1;
+}
+
+static bool _parseOptions(int *pargc, char **argv, int *i)
{
return FALSE;
}
-static void _z80_finaliseOptions(void)
+static void _finaliseOptions(void)
{
port->mem.default_local_map = data;
port->mem.default_globl_map = data;
}
-static void _z80_setDefaultOptions(void)
+static void _setDefaultOptions(void)
{
options.genericPtr = 1; /* default on */
options.nopeep = 0;
optimize.loopInduction = 0;
}
-static const char *_z80_getRegName(struct regs *reg)
+static const char *_getRegName(struct regs *reg)
{
if (reg)
return reg->name;
$l is the list of extra options that should be there somewhere...
MUST be terminated with a NULL.
*/
-static const char *_linkCmd[] = {
+static const char *_z80_linkCmd[] = {
"link-z80", "-nf", "$1", NULL
};
-static const char *_asmCmd[] = {
+static const char *_z80_asmCmd[] = {
"as-z80", "-plosgff", "$1.o", "$1.asm", NULL
};
+/** $1 is always the basename.
+ $2 is always the output file.
+ $3 varies
+ $l is the list of extra options that should be there somewhere...
+ MUST be terminated with a NULL.
+*/
+static const char *_gbz80_linkCmd[] = {
+ "link-gbz80", "-nf", "$1", NULL
+};
+
+static const char *_gbz80_asmCmd[] = {
+ "as-gbz80", "-plosgff", "$1.o", "$1.asm", NULL
+};
+
/* Globals */
PORT z80_port = {
"z80",
FALSE,
},
{
- _asmCmd,
+ _z80_asmCmd,
"-plosgff", /* Options with debug */
"-plosgff", /* Options without debug */
},
{
- _linkCmd
+ _z80_linkCmd
},
{
- _defaultRules
+ _z80_defaultRules
},
{
/* Sizes: char, short, int, long, ptr, fptr, gptr, bit, float, max */
0
},
_z80_init,
- _z80_parseOptions,
- _z80_finaliseOptions,
- _z80_setDefaultOptions,
+ _parseOptions,
+ _finaliseOptions,
+ _setDefaultOptions,
z80_assignRegisters,
- _z80_getRegName,
- _z80_keywords,
+ _getRegName,
+ _keywords,
0, /* no assembler preamble */
0, /* no local IVT generation code */
- _z80_reset_regparm,
- _z80_reg_parm
+ _reset_regparm,
+ _reg_parm
};
+/* Globals */
+PORT gbz80_port = {
+ "gbz80",
+ "Gameboy Z80-like", /* Target name */
+ {
+ FALSE,
+ },
+ {
+ _gbz80_asmCmd,
+ "-plosgff", /* Options with debug */
+ "-plosgff", /* Options without debug */
+ },
+ {
+ _gbz80_linkCmd
+ },
+ {
+ _gbz80_defaultRules
+ },
+ {
+ /* Sizes: char, short, int, long, ptr, fptr, gptr, bit, float, max */
+ 1, 1, 2, 4, 2, 2, 2, 1, 4, 4
+ },
+ {
+ "_XSEG",
+ "_STACK",
+ "_CODE",
+ "_DATA",
+ "_ISEG",
+ "_XSEG",
+ "_BSEG",
+ "_RSEG",
+ "_GSINIT",
+ "_OVERLAY",
+ "_GSFINAL",
+ NULL,
+ NULL,
+ 1
+ },
+ {
+ -1, 0, 0, 4, 0
+ },
+ /* gbZ80 has no native mul/div commands */
+ {
+ 0
+ },
+ _gbz80_init,
+ _parseOptions,
+ _finaliseOptions,
+ _setDefaultOptions,
+ z80_assignRegisters,
+ _getRegName,
+ _keywords,
+ 0, /* no assembler preamble */
+ 0, /* no local IVT generation code */
+ _reset_regparm,
+ _reg_parm,
+ _process_pragma
+};
hardware. It allocates based on usage and how long the varible
lives into registers or temporary memory on the stack.
- On the Z80 hl, ix, iy, and a are reserved for the code generator,
- leaving bc and de for allocation. The extra register pressure
+ On the Z80 hl and ix and a are reserved for the code generator,
+ leaving bc and de for allocation. iy is unusable due to currently
+ as it's only adressable as a pair. The extra register pressure
from reserving hl is made up for by how much easier the sub
operations become. You could swap hl for iy if the undocumented
iyl/iyh instructions are available.
# Simple Makefile for dhrystone and sdcc
-PROC = gbz80
-PORT = gb
+PROC = z80
+PORT = consolez80
CC = /home/michaelh/projects/gbdk-support/lcc/lcc
# -DNOENUM is here to make the results more predictable