+ 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, unsigned 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 !immed-%u,!immedbyte", 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 !immed-%u,!immedbyte", 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);
+
+ _saveRegsForCall(ic, 0);
+
+ fetchPair (PAIR_HL, AOP (IC_LEFT (ic)));
+ emit2 ("call __initrleblock");
+
+ type = operandType(IC_LEFT(ic));
+
+ if (type && type->next)
+ {
+ if (IS_SPEC(type->next) || IS_PTR(type->next))
+ {
+ elementSize = getSize(type->next);
+ }
+ else if (IS_ARRAY(type->next) && type->next->next)
+ {
+ elementSize = getSize(type->next->next);
+ }
+ else
+ {
+ printTypeChainRaw (type, NULL);
+ wassertl (0, "Can't determine element size in genArrayInit.");
+ }
+ }
+ else
+ {
+ wassertl (0, "Can't determine element size in genArrayInit.");
+ }
+
+ wassertl ((elementSize > 0) && (elementSize <= 4), "Illegal 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;
+
+ 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");
+
+ _restoreRegsAfterCall();
+
+ spillCached ();
+
+ freeAsmop (IC_LEFT(ic), NULL, ic);
+}
+
+static void
+_swap (PAIR_ID one, PAIR_ID two)
+{
+ if ((one == PAIR_DE && two == PAIR_HL) || (one == PAIR_HL && two == PAIR_DE))
+ {
+ emit2 ("ex de,hl");
+ }
+ else
+ {
+ emit2 ("ld a,%s", _pairs[one].l);
+ emit2 ("ld %s,%s", _pairs[one].l, _pairs[two].l);
+ emit2 ("ld %s,a", _pairs[two].l);
+ emit2 ("ld a,%s", _pairs[one].h);
+ emit2 ("ld %s,%s", _pairs[one].h, _pairs[two].h);
+ emit2 ("ld %s,a", _pairs[two].h);
+ }
+}
+
+/* The problem is that we may have all three pairs used and they may
+ be needed in a different order.
+
+ Note: Have ex de,hl
+
+ Combinations:
+ hl = hl => unity, fine
+ bc = bc
+ de = de
+
+ hl = hl hl = hl, swap de <=> bc
+ bc = de
+ de = bc
+
+ hl = bc Worst case
+ bc = de
+ de = hl
+
+ hl = bc de = de, swap bc <=> hl
+ bc = hl
+ de = de
+
+ hl = de Worst case
+ bc = hl
+ de = bc
+
+ hl = de bc = bc, swap hl <=> de
+ bc = bc
+ de = hl
+
+ Break it down into:
+ * Any pair = pair are done last
+ * Any pair = iTemp are done last
+ * Any swaps can be done any time
+
+ A worst case:
+ push p1
+ p1 = p2
+ p2 = p3
+ pop p3
+
+ So how do we detect the cases?
+ How about a 3x3 matrix?
+ source
+ dest x x x x
+ x x x x
+ x x x x (Fourth for iTemp/other)
+
+ First determin which mode to use by counting the number of unity and
+ iTemp assigns.
+ Three - any order
+ Two - Assign the pair first, then the rest
+ One - Swap the two, then the rest
+ Zero - Worst case.
+*/
+static void
+setupForBuiltin3 (iCode *ic, int nparams, operand **pparams)
+{
+ PAIR_ID ids[NUM_PAIRS][NUM_PAIRS];
+ PAIR_ID dest[3] = {
+ PAIR_BC, PAIR_HL, PAIR_DE
+ };
+ int i, j, nunity = 0;
+ memset (ids, PAIR_INVALID, sizeof (ids));
+
+ /* Sanity checks */
+ wassert (nparams == 3);
+
+ /* First save everything that needs to be saved. */
+ _saveRegsForCall (ic, 0);
+
+ /* Loading HL first means that DE is always fine. */
+ for (i = 0; i < nparams; i++)
+ {
+ aopOp (pparams[i], ic, FALSE, FALSE);
+ ids[dest[i]][getPairId (AOP (pparams[i]))] = TRUE;
+ }
+
+ /* Count the number of unity or iTemp assigns. */
+ for (i = 0; i < 3; i++)
+ {
+ if (ids[dest[i]][dest[i]] == TRUE || ids[dest[i]][PAIR_INVALID] == TRUE)
+ {
+ nunity++;
+ }
+ }
+
+ if (nunity == 3)
+ {
+ /* Any order, fall through. */
+ }
+ else if (nunity == 2)
+ {
+ /* One is assigned. Pull it out and assign. */
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < NUM_PAIRS; j++)
+ {
+ if (ids[dest[i]][j] == TRUE)
+ {
+ /* Found it. See if it's the right one. */
+ if (j == PAIR_INVALID || j == dest[i])
+ {
+ /* Keep looking. */
+ }
+ else
+ {
+ fetchPair(dest[i], AOP (pparams[i]));
+ goto done;
+ }
+ }
+ }
+ }
+ }
+ else if (nunity == 1)
+ {
+ /* Find the pairs to swap. */
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < NUM_PAIRS; j++)
+ {
+ if (ids[dest[i]][j] == TRUE)
+ {
+ if (j == PAIR_INVALID || j == dest[i])
+ {
+ /* Keep looking. */
+ }
+ else
+ {
+ _swap (j, dest[i]);
+ goto done;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ int next = getPairId (AOP (pparams[0]));
+ emit2 ("push %s", _pairs[next].name);
+
+ if (next == dest[1])
+ {
+ fetchPair (dest[1], AOP (pparams[1]));
+ fetchPair (dest[2], AOP (pparams[2]));
+ }
+ else
+ {
+ fetchPair (dest[2], AOP (pparams[2]));
+ fetchPair (dest[1], AOP (pparams[1]));
+ }
+ emit2 ("pop %s", _pairs[dest[0]].name);
+ }
+ done:
+ /* Finally pull out all of the iTemps */
+ for (i = 0; i < 3; i++)
+ {
+ if (ids[dest[i]][PAIR_INVALID] == 1)
+ {
+ fetchPair (dest[i], AOP (pparams[i]));
+ }
+ }
+}
+
+static void
+genBuiltInStrcpy (iCode *ic, int nParams, operand **pparams)
+{
+ operand *from, *to;
+ symbol *label;
+ bool deInUse;
+
+ wassertl (nParams == 2, "Built-in strcpy must have two parameters");
+ to = pparams[0];
+ from = pparams[1];
+
+ deInUse = bitVectBitValue (ic->rMask, D_IDX) || bitVectBitValue(ic->rMask, E_IDX);
+
+ setupForBuiltin3 (ic, nParams, pparams);
+
+ label = newiTempLabel(NULL);
+
+ emitLabel (label->key);
+ emit2 ("ld a,(hl)");
+ emit2 ("ldi");
+ emit2 ("or a");
+ emit2 ("!shortjp nz,!tlabel ; 1", label->key);
+
+ freeAsmop (from, NULL, ic->next);
+ freeAsmop (to, NULL, ic);
+}
+
+static void
+genBuiltInMemcpy (iCode *ic, int nParams, operand **pparams)
+{
+ operand *from, *to, *count;
+ bool deInUse;
+
+ wassertl (nParams == 3, "Built-in memcpy must have two parameters");
+ to = pparams[2];
+ from = pparams[1];
+ count = pparams[0];
+
+ deInUse = bitVectBitValue (ic->rMask, D_IDX) || bitVectBitValue(ic->rMask, E_IDX);
+
+ setupForBuiltin3 (ic, nParams, pparams);
+
+ emit2 ("ldir");
+
+ freeAsmop (count, NULL, ic->next->next);
+ freeAsmop (from, NULL, ic);
+
+ _restoreRegsAfterCall();
+
+ /* if we need assign a result value */
+ if ((IS_ITEMP (IC_RESULT (ic)) &&
+ (OP_SYMBOL (IC_RESULT (ic))->nRegs ||
+ OP_SYMBOL (IC_RESULT (ic))->spildir)) ||
+ IS_TRUE_SYMOP (IC_RESULT (ic)))
+ {
+ aopOp (IC_RESULT (ic), ic, FALSE, FALSE);
+ movLeft2ResultLong (to, 0, IC_RESULT (ic), 0, 0, 2);
+ freeAsmop (IC_RESULT (ic), NULL, ic);
+ }
+
+ freeAsmop (to, NULL, ic->next);
+}
+
+/*-----------------------------------------------------------------*/
+/* genBuiltIn - calls the appropriate function to generating code */
+/* for a built in function */
+/*-----------------------------------------------------------------*/
+static void genBuiltIn (iCode *ic)
+{
+ operand *bi_parms[MAX_BUILTIN_ARGS];
+ int nbi_parms;
+ iCode *bi_iCode;
+ symbol *bif;
+
+ /* get all the arguments for a built in function */
+ bi_iCode = getBuiltinParms(ic,&nbi_parms,bi_parms);
+
+ /* which function is it */
+ bif = OP_SYMBOL(IC_LEFT(bi_iCode));
+
+ if (strcmp(bif->name,"__builtin_strcpy")==0)
+ {
+ genBuiltInStrcpy(bi_iCode, nbi_parms, bi_parms);
+ }
+ else if (strcmp(bif->name,"__builtin_memcpy")==0)
+ {
+ genBuiltInMemcpy(bi_iCode, nbi_parms, bi_parms);
+ }
+ else
+ {
+ wassertl (0, "Unknown builtin function encountered");
+ }
+}
+
+/*-----------------------------------------------------------------*/
+/* genZ80Code - generate code for Z80 based controllers */
+/*-----------------------------------------------------------------*/
+void
+genZ80Code (iCode * lic)
+{
+ iCode *ic;
+ int cln = 0;
+
+ /* Hack */
+ if (IS_GB)
+ {
+ _fReturn = _gbz80_return;
+ _fTmp = _gbz80_return;
+ }
+ else
+ {
+ _fReturn = _z80_return;
+ _fTmp = _z80_return;
+ }
+
+ _G.lines.head = _G.lines.current = NULL;
+
+ /* if debug information required */
+ if (options.debug && currFunc)
+ {
+ debugFile->writeFunction (currFunc, lic);
+ }
+
+ for (ic = lic; ic; ic = ic->next)
+ {
+ _G.current_iCode = ic;
+
+ if (ic->lineno && cln != ic->lineno)
+ {
+ if (options.debug)
+ {
+ debugFile->writeCLine (ic);
+ }
+ if (!options.noCcodeInAsm) {
+ emit2 (";%s:%d: %s", ic->filename, ic->lineno,
+ printCLine(ic->filename, ic->lineno));
+ }
+ cln = ic->lineno;
+ }
+ if (options.iCodeInAsm) {
+ emit2 (";ic:%d: %s", ic->key, printILine(ic));
+ }
+ /* if the result is marked as
+ spilt and rematerializable or code for
+ this has already been generated then
+ do nothing */
+ if (resultRemat (ic) || ic->generated)
+ continue;
+
+ /* depending on the operation */
+ switch (ic->op)
+ {
+ case '!':
+ emitDebug ("; genNot");
+ genNot (ic);
+ break;
+
+ case '~':
+ emitDebug ("; genCpl");
+ genCpl (ic);
+ break;
+
+ case UNARYMINUS:
+ emitDebug ("; genUminus");
+ genUminus (ic);
+ break;
+