/*-------------------------------------------------------------------------
gen.c - Z80 specific code generator.
+
+ Michael Hope <michaelh@juju.net.nz> 2000
+ Based on the mcs51 generator -
+ Sandeep Dutta . sandeep.dutta@usa.net (1998)
+ and - Jean-Louis VERN.jlvern@writeme.com (1999)
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ In other words, you are welcome to use, share and improve this program.
+ You are forbidden to forbid anyone else to use, share and improve
+ what you give them. Help stamp out software-hoarding!
+
+-------------------------------------------------------------------------*/
+
+/*
Benchmarks on dhry.c 2.1 with 32766 loops and a 10ms clock:
ticks dhry size
Base with asm strcpy / strcmp / memcpy: 23198 141 1A14
4. Optimised strcmp fun 21999 149 2294
5. Optimised strcmp further 21660 151 228C
6. Optimised memcpy by unroling 20885 157 2201
+ 7. After turning loop induction on 19862 165 236D
+ 8. Same as 7 but with more info
+ 9. With asm optimised strings 17030 192 2223
- Michael Hope <michaelh@juju.net.nz> 2000
- Based on the mcs51 generator -
- Sandeep Dutta . sandeep.dutta@usa.net (1998)
- and - Jean-Louis VERN.jlvern@writeme.com (1999)
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- In other words, you are welcome to use, share and improve this program.
- You are forbidden to forbid anyone else to use, share and improve
- what you give them. Help stamp out software-hoarding!
-
--------------------------------------------------------------------------*/
+ 10 and below are with asm strings off.
+
+ Apparent advantage of turning on regparams:
+ 1. Cost of push
+ Decent case is push of a constant
+ - ld hl,#n; push hl: (10+11)*nargs
+ 2. Cost of pull from stack
+ Using asm with ld hl, etc
+ - ld hl,#2; add hl,sp; (ld bc,(hl); hl+=2)*nargs
+ 10+11+(7+6+7+6)*nargs
+ 3. Cost of fixing stack
+ - pop hl*nargs
+ 10*nargs
+
+ So cost is (10+11+7+6+7+10)*nargs+10+11
+ = 51*nargs+21
+ = 123 for mul, div, strcmp, strcpy
+ Saving of (98298+32766+32766+32766)*123 = 24181308
+ At 192 d/s for 682411768t, speed up to 199. Hmm.
+*/
#include <stdio.h>
#include <stdlib.h>
*/
enum {
- DISABLE_DEBUG = 1
+ DISABLE_DEBUG = 0
};
static char *_z80_return[] =
if (genPlusIncr (ic) == TRUE)
goto release;
- emitDebug ("; genPlusIncr failed");
+ emitDebug ("; Can't optimise plus by inc, falling back to the normal way");
size = getDataSize (IC_RESULT (ic));
if (isPair (AOP (IC_RESULT (ic))))
{
char *left, *right;
-
left = aopGetLitWordLong (AOP (IC_LEFT (ic)), 0, FALSE);
right = aopGetLitWordLong (AOP (IC_RIGHT (ic)), 0, FALSE);
- if (left && right)
+
+ if (AOP_TYPE(IC_LEFT(ic)) == AOP_LIT && AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT &&
+ left && right)
{
/* It's a pair */
/* PENDING: fix */
}
else if (size == 4)
{
- emitDebug ("; WARNING: This add is probably broken.\n");
+ wassertl (0, "Hit bad case for add");
}
}
}
freeAsmop (IC_RESULT (ic), NULL, ic);
}
+enum
+ {
+ /** Maximum number of bytes to emit per line. */
+ DBEMIT_MAX_RUN = 8
+ };
+
+/** Context for the byte output chunker. */
+typedef struct
+{
+ unsigned char buffer[DBEMIT_MAX_RUN];
+ int pos;
+} DBEMITCTX;
+
+
+/** Flushes a byte chunker by writing out all in the buffer and
+ reseting.
+*/
+static void
+_dbFlush(DBEMITCTX *self)
+{
+ char line[256];
+
+ if (self->pos > 0)
+ {
+ int i;
+ sprintf(line, ".db 0x%02X", self->buffer[0]);
+
+ for (i = 1; i < self->pos; i++)
+ {
+ sprintf(line + strlen(line), ", 0x%02X", self->buffer[i]);
+ }
+ emit2(line);
+ }
+ self->pos = 0;
+}
+
+/** Write out another byte, buffering until a decent line is
+ generated.
+*/
+static void
+_dbEmit(DBEMITCTX *self, int c)
+{
+ if (self->pos == DBEMIT_MAX_RUN)
+ {
+ _dbFlush(self);
+ }
+ self->buffer[self->pos++] = c;
+}
+
+/** Context for a simple run length encoder. */
+typedef struct
+{
+ unsigned last;
+ unsigned char buffer[128];
+ int pos;
+ /** runLen may be equivalent to pos. */
+ int runLen;
+} RLECTX;
+
+enum
+ {
+ RLE_CHANGE_COST = 4,
+ RLE_MAX_BLOCK = 127
+ };
+
+/** Flush the buffer of a run length encoder by writing out the run or
+ data that it currently contains.
+*/
+static void
+_rleCommit(RLECTX *self)
+{
+ int i;
+ if (self->pos != 0)
+ {
+ DBEMITCTX db;
+ memset(&db, 0, sizeof(db));
+
+ emit2(".db %u", self->pos);
+
+ for (i = 0; i < self->pos; i++)
+ {
+ _dbEmit(&db, self->buffer[i]);
+ }
+ _dbFlush(&db);
+ }
+ /* Reset */
+ self->pos = 0;
+}
+
+/* Encoder design:
+ Can get either a run or a block of random stuff.
+ Only want to change state if a good run comes in or a run ends.
+ Detecting run end is easy.
+ Initial state?
+
+ Say initial state is in run, len zero, last zero. Then if you get a
+ few zeros then something else then a short run will be output.
+ Seems OK. While in run mode, keep counting. While in random mode,
+ keep a count of the run. If run hits margin, output all up to run,
+ restart, enter run mode.
+*/
+
+/** Add another byte into the run length encoder, flushing as
+ required. The run length encoder uses the Amiga IFF style, where
+ a block is prefixed by its run length. A positive length means
+ the next n bytes pass straight through. A negative length means
+ that the next byte is repeated -n times. A zero terminates the
+ chunks.
+*/
+static void
+_rleAppend(RLECTX *self, int c)
+{
+ int i;
+
+ if (c != self->last)
+ {
+ /* The run has stopped. See if it is worthwhile writing it out
+ as a run. Note that the random data comes in as runs of
+ length one.
+ */
+ if (self->runLen > RLE_CHANGE_COST)
+ {
+ /* Yes, worthwhile. */
+ /* Commit whatever was in the buffer. */
+ _rleCommit(self);
+ emit2(".db -%u,0x%02X", self->runLen, self->last);
+ }
+ else
+ {
+ /* Not worthwhile. Append to the end of the random list. */
+ for (i = 0; i < self->runLen; i++)
+ {
+ if (self->pos >= RLE_MAX_BLOCK)
+ {
+ /* Commit. */
+ _rleCommit(self);
+ }
+ self->buffer[self->pos++] = self->last;
+ }
+ }
+ self->runLen = 1;
+ self->last = c;
+ }
+ else
+ {
+ if (self->runLen >= RLE_MAX_BLOCK)
+ {
+ /* Commit whatever was in the buffer. */
+ _rleCommit(self);
+
+ emit2 (".db -%u,0x%02X", self->runLen, self->last);
+ self->runLen = 0;
+ }
+ self->runLen++;
+ }
+}
+
+static void
+_rleFlush(RLECTX *self)
+{
+ _rleAppend(self, -1);
+ _rleCommit(self);
+ self->pos = 0;
+ self->last = 0;
+ self->runLen = 0;
+}
+
+/** genArrayInit - Special code for initialising an array with constant
+ data.
+*/
+static void
+genArrayInit (iCode * ic)
+{
+ literalList *iLoop;
+ int ix;
+ int elementSize = 0, eIndex, i;
+ unsigned val, lastVal;
+ sym_link *type;
+ RLECTX rle;
+
+ memset(&rle, 0, sizeof(rle));
+
+ aopOp (IC_LEFT(ic), ic, FALSE, FALSE);
+
+ if (AOP_TYPE(IC_LEFT(ic)) == AOP_IMMD)
+ {
+ /* Emit the support function call and the destination address. */
+ emit2("call __initrleblock");
+ emit2(".dw %s", aopGetWord (AOP(IC_LEFT(ic)), 0));
+ }
+ else
+ {
+ wassertl (0, "Unexpected operand to genArrayInit.\n");
+ }
+
+ type = operandType(IC_LEFT(ic));
+
+ if (type && type->next)
+ {
+ elementSize = getSize(type->next);
+ }
+ else
+ {
+ wassertl (0, "Can't determine element size in genArrayInit.");
+ }
+
+ iLoop = IC_ARRAYILIST(ic);
+ lastVal = (unsigned)-1;
+
+ /* Feed all the bytes into the run length encoder which will handle
+ the actual output.
+ This works well for mixed char data, and for random int and long
+ data.
+ */
+ while (iLoop)
+ {
+ ix = iLoop->count;
+
+ if (ix != 0)
+ {
+ for (i = 0; i < ix; i++)
+ {
+ for (eIndex = 0; eIndex < elementSize; eIndex++)
+ {
+ val = (((int)iLoop->literalValue) >> (eIndex * 8)) & 0xff;
+ _rleAppend(&rle, val);
+ }
+ }
+ }
+
+ iLoop = iLoop->next;
+ }
+
+ _rleFlush(&rle);
+ /* Mark the end of the run. */
+ emit2(".db 0");
+
+ freeAsmop (IC_LEFT(ic), NULL, ic);
+}
+
/*-----------------------------------------------------------------*/
/* genZ80Code - generate code for Z80 based controllers */
/*-----------------------------------------------------------------*/
if (cln != ic->lineno)
{
- emitDebug ("; %s %d", ic->filename, ic->lineno);
+ emit2 ("; %s %d", ic->filename, ic->lineno);
cln = ic->lineno;
}
/* if the result is marked as
addSet (&_G.sendSet, ic);
break;
+ case ARRAYINIT:
+ genArrayInit(ic);
+ break;
+
default:
ic = ic;
/* piCode(ic,stdout); */