+ /* if they not symbols */
+ if (!IS_SYMOP (op1) || !IS_SYMOP (op2))
+ return FALSE;
+
+ sym1 = OP_SYMBOL (op1);
+ sym2 = OP_SYMBOL (op2);
+
+ /* if both are itemps & one is spilt
+ and the other is not then false */
+ if (IS_ITEMP (op1) && IS_ITEMP (op2) &&
+ sym1->isspilt != sym2->isspilt)
+ return FALSE;
+
+ /* if they are the same */
+ if (sym1 == sym2)
+ return 1;
+
+ if (sym1->rname[0] && sym2->rname[0]
+ && strcmp (sym1->rname, sym2->rname) == 0)
+ return 2;
+
+ /* if left is a tmp & right is not */
+ if (IS_ITEMP (op1) &&
+ !IS_ITEMP (op2) &&
+ sym1->isspilt &&
+ (sym1->usl.spillLoc == sym2))
+ return 3;
+
+ if (IS_ITEMP (op2) &&
+ !IS_ITEMP (op1) &&
+ sym2->isspilt &&
+ sym1->level > 0 &&
+ (sym2->usl.spillLoc == sym1))
+ return 4;
+
+ return FALSE;
+}
+
+/*-----------------------------------------------------------------*/
+/* sameRegs - two asmops have the same registers */
+/*-----------------------------------------------------------------*/
+bool
+sameRegs (asmop * aop1, asmop * aop2)
+{
+ int i;
+
+ if (aop1->type == AOP_SFR ||
+ aop2->type == AOP_SFR)
+ return FALSE;
+
+ if (aop1 == aop2)
+ return TRUE;
+
+ if (aop1->type != AOP_REG ||
+ aop2->type != AOP_REG)
+ return FALSE;
+
+ if (aop1->size != aop2->size)
+ return FALSE;
+
+ for (i = 0; i < aop1->size; i++)
+ if (aop1->aopu.aop_reg[i] !=
+ aop2->aopu.aop_reg[i])
+ return FALSE;
+
+ return TRUE;
+}
+
+/*-----------------------------------------------------------------*/
+/* aopOp - allocates an asmop for an operand : */
+/*-----------------------------------------------------------------*/
+static void
+aopOp (operand * op, iCode * ic, bool result, bool requires_a)
+{
+ asmop *aop;
+ symbol *sym;
+ int i;
+
+ if (!op)
+ return;
+
+ /* if this a literal */
+ if (IS_OP_LITERAL (op))
+ {
+ op->aop = aop = newAsmop (AOP_LIT);
+ aop->aopu.aop_lit = op->operand.valOperand;
+ aop->size = getSize (operandType (op));
+ return;
+ }
+
+ /* if already has a asmop then continue */
+ if (op->aop)
+ {
+ if (op->aop->type == AOP_SFR)
+ {
+ op->aop->bcInUse = isPairInUse( PAIR_BC, ic );
+ op->aop->deInUse = isPairInUse( PAIR_DE, ic );
+ }
+ return;
+ }
+
+ /* if the underlying symbol has a aop */
+ if (IS_SYMOP (op) && OP_SYMBOL (op)->aop)
+ {
+ op->aop = OP_SYMBOL (op)->aop;
+ if (op->aop->type == AOP_SFR)
+ {
+ op->aop->bcInUse = isPairInUse( PAIR_BC, ic );
+ op->aop->deInUse = isPairInUse( PAIR_DE, ic );
+ }
+ return;
+ }
+
+ /* if this is a true symbol */
+ if (IS_TRUE_SYMOP (op))
+ {
+ op->aop = aopForSym (ic, OP_SYMBOL (op), result, requires_a);
+ return;
+ }
+
+ /* this is a temporary : this has
+ only four choices :
+ a) register
+ b) spillocation
+ c) rematerialize
+ d) conditional
+ e) can be a return use only */
+
+ sym = OP_SYMBOL (op);
+
+ /* if the type is a conditional */
+ if (sym->regType == REG_CND)
+ {
+ aop = op->aop = sym->aop = newAsmop (AOP_CRY);
+ aop->size = 0;
+ return;
+ }
+
+ /* if it is spilt then two situations
+ a) is rematerialize
+ b) has a spill location */
+ if (sym->isspilt || sym->nRegs == 0)
+ {
+ /* rematerialize it NOW */
+ if (sym->remat)
+ {
+ sym->aop = op->aop = aop =
+ aopForRemat (sym);
+ aop->size = getSize (sym->type);
+ return;
+ }
+
+ if (sym->ruonly)
+ {
+ int i;
+ aop = op->aop = sym->aop = newAsmop (AOP_STR);
+ aop->size = getSize (sym->type);
+ for (i = 0; i < 4; i++)
+ aop->aopu.aop_str[i] = _fReturn[i];
+ return;
+ }
+
+ if (sym->accuse)
+ {
+ if (sym->accuse == ACCUSE_A)
+ {
+ aop = op->aop = sym->aop = newAsmop (AOP_ACC);
+ aop->size = getSize (sym->type);
+ wassertl(aop->size == 1, "Internal error: Caching in A, but too big to fit in A");
+
+ aop->aopu.aop_str[0] = _pairs[PAIR_AF].h;
+ }
+ else if (sym->accuse == ACCUSE_SCRATCH)
+ {
+ aop = op->aop = sym->aop = newAsmop (AOP_HLREG);
+ aop->size = getSize (sym->type);
+ wassertl(aop->size <= 2, "Internal error: Caching in HL, but too big to fit in HL");
+ aop->aopu.aop_str[0] = _pairs[PAIR_HL].l;
+ aop->aopu.aop_str[1] = _pairs[PAIR_HL].h;
+ }
+ else if (sym->accuse == ACCUSE_IY)
+ {
+ aop = op->aop = sym->aop = newAsmop (AOP_HLREG);
+ aop->size = getSize (sym->type);
+ wassertl(aop->size <= 2, "Internal error: Caching in IY, but too big to fit in IY");
+ aop->aopu.aop_str[0] = _pairs[PAIR_IY].l;
+ aop->aopu.aop_str[1] = _pairs[PAIR_IY].h;
+ }
+ else
+ {
+ wassertl (0, "Marked as being allocated into A or HL but is actually in neither");
+ }
+ return;
+ }
+
+ if (sym->usl.spillLoc)
+ {
+ asmop *oldAsmOp = NULL;
+
+ if (getSize(sym->type) != getSize(sym->usl.spillLoc->type))
+ {
+ /* force a new aop if sizes differ */
+ oldAsmOp = sym->usl.spillLoc->aop;
+ sym->usl.spillLoc->aop = NULL;
+ }
+ sym->aop = op->aop = aop =
+ aopForSym (ic, sym->usl.spillLoc, result, requires_a);
+ if (getSize(sym->type) != getSize(sym->usl.spillLoc->type))
+ {
+ /* Don't reuse the new aop, go with the last one */
+ sym->usl.spillLoc->aop = oldAsmOp;
+ }
+ aop->size = getSize (sym->type);
+ return;
+ }
+
+ /* else must be a dummy iTemp */
+ sym->aop = op->aop = aop = newAsmop (AOP_DUMMY);
+ aop->size = getSize (sym->type);
+ return;
+ }
+
+ /* must be in a register */
+ sym->aop = op->aop = aop = newAsmop (AOP_REG);
+ aop->size = sym->nRegs;
+ for (i = 0; i < sym->nRegs; i++)
+ aop->aopu.aop_reg[i] = sym->regs[i];
+}
+
+/*-----------------------------------------------------------------*/
+/* freeAsmop - free up the asmop given to an operand */
+/*----------------------------------------------------------------*/
+static void
+freeAsmop (operand * op, asmop * aaop, iCode * ic)
+{
+ asmop *aop;
+
+ if (!op)
+ aop = aaop;
+ else
+ aop = op->aop;
+
+ if (!aop)
+ return;
+
+ if (aop->freed)
+ goto dealloc;
+
+ aop->freed = 1;
+
+ if (aop->type == AOP_PAIRPTR && IS_Z80 && aop->aopu.aop_pairId == PAIR_DE)
+ {
+ _pop (aop->aopu.aop_pairId);
+ }
+
+ if (getPairId (aop) == PAIR_HL)
+ {
+ spillPair (PAIR_HL);
+ }
+
+dealloc:
+ /* all other cases just dealloc */
+ if (op)
+ {
+ op->aop = NULL;
+ if (IS_SYMOP (op))
+ {
+ OP_SYMBOL (op)->aop = NULL;
+ /* if the symbol has a spill */
+ if (SPIL_LOC (op))
+ SPIL_LOC (op)->aop = NULL;
+ }
+ }
+
+}
+
+bool
+isLitWord (asmop * aop)
+{
+ /* if (aop->size != 2)
+ return FALSE; */
+ switch (aop->type)
+ {
+ case AOP_IMMD:
+ case AOP_LIT:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+char *
+aopGetLitWordLong (asmop * aop, int offset, bool with_hash)
+{
+ /* depending on type */
+ switch (aop->type)
+ {
+ case AOP_HL:
+ case AOP_IY:
+ case AOP_IMMD:
+ /* PENDING: for re-target */
+ if (with_hash)
+ {
+ tsprintf (buffer, sizeof(buffer),
+ "!hashedstr + %d", aop->aopu.aop_immd, offset);
+ }
+ else if (offset == 0)
+ {
+ tsprintf (buffer, sizeof(buffer),
+ "%s", aop->aopu.aop_immd);
+ }
+ else
+ {
+ tsprintf (buffer, sizeof(buffer),
+ "%s + %d", aop->aopu.aop_immd, offset);
+ }
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+
+ case AOP_LIT:
+ {
+ value *val = aop->aopu.aop_lit;
+ /* if it is a float then it gets tricky */
+ /* otherwise it is fairly simple */
+ if (!IS_FLOAT (val->type))
+ {
+ unsigned long v = (unsigned long) floatFromVal (val);
+
+ if (offset == 2)
+ {
+ v >>= 16;
+ }
+ else if (offset == 0)
+ {
+ // OK
+ }
+ else
+ {
+ wassertl(0, "Encountered an invalid offset while fetching a literal");
+ }
+
+ if (with_hash)
+ tsprintf (buffer, sizeof(buffer), "!immedword", v);
+ else
+ tsprintf (buffer, sizeof(buffer), "!constword", v);
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+ }
+ else
+ {
+ union {
+ float f;
+ unsigned char c[4];
+ }
+ fl;
+ unsigned int i;
+
+ /* it is type float */
+ fl.f = (float) floatFromVal (val);
+
+#ifdef WORDS_BIGENDIAN
+ i = fl.c[3-offset] | (fl.c[3-offset-1]<<8);
+#else
+ i = fl.c[offset] | (fl.c[offset+1]<<8);
+#endif
+ if (with_hash)
+ tsprintf (buffer, sizeof(buffer), "!immedword", i);
+ else
+ tsprintf (buffer, sizeof(buffer), "!constword", i);
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+ }
+ }
+ default:
+ return NULL;
+ }
+}
+
+char *
+aopGetWord (asmop * aop, int offset)
+{
+ return aopGetLitWordLong (aop, offset, TRUE);
+}
+
+bool
+isPtr (const char *s)
+{
+ if (!strcmp (s, "hl"))
+ return TRUE;
+ if (!strcmp (s, "ix"))
+ return TRUE;
+ if (!strcmp (s, "iy"))
+ return TRUE;
+ return FALSE;
+}
+
+static void
+adjustPair (const char *pair, int *pold, int new)
+{
+ wassert (pair);
+
+ while (*pold < new)
+ {
+ emit2 ("inc %s", pair);
+ (*pold)++;
+ }
+ while (*pold > new)
+ {
+ emit2 ("dec %s", pair);
+ (*pold)--;
+ }
+}
+
+static void
+spillCached (void)
+{
+ spillPair (PAIR_HL);
+ spillPair (PAIR_IY);
+}
+
+static bool
+requiresHL (asmop * aop)
+{
+ switch (aop->type)
+ {
+ case AOP_IY:
+ case AOP_HL:
+ case AOP_STK:
+ case AOP_EXSTK:
+ case AOP_HLREG:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static void
+fetchLitPair (PAIR_ID pairId, asmop * left, int offset)
+{
+ const char *l, *base;
+ const char *pair = _pairs[pairId].name;
+ l = aopGetLitWordLong (left, offset, FALSE);
+ base = aopGetLitWordLong (left, 0, FALSE);
+ wassert (l && pair && base);
+
+ if (isPtr (pair))
+ {
+ if (pairId == PAIR_HL || pairId == PAIR_IY)
+ {
+ if (_G.pairs[pairId].last_type == left->type)
+ {
+ if (_G.pairs[pairId].base && !strcmp (_G.pairs[pairId].base, base))
+ {
+ if (pairId == PAIR_HL && abs (_G.pairs[pairId].offset - offset) < 3)
+ {
+ adjustPair (pair, &_G.pairs[pairId].offset, offset);
+ return;
+ }
+ if (pairId == PAIR_IY && (offset >= INT8MIN && offset <= INT8MAX))
+ {
+ return;
+ }
+ }
+ }
+ }
+ _G.pairs[pairId].last_type = left->type;
+ _G.pairs[pairId].base = traceAlloc(&_G.trace.aops, Safe_strdup (base));
+ _G.pairs[pairId].offset = offset;
+ }
+ /* Both a lit on the right and a true symbol on the left */
+ emit2 ("ld %s,!hashedstr", pair, l);
+}
+
+static PAIR_ID
+makeFreePairId (iCode *ic, bool *pisUsed)
+{
+ *pisUsed = FALSE;
+
+ if (ic != NULL)
+ {
+ if (!bitVectBitValue (ic->rMask, B_IDX) && !bitVectBitValue(ic->rMask, C_IDX))
+ {
+ return PAIR_BC;
+ }
+ else if (IS_Z80 && !bitVectBitValue (ic->rMask, D_IDX) && !bitVectBitValue(ic->rMask, E_IDX))
+ {
+ return PAIR_DE;
+ }
+ else
+ {
+ *pisUsed = TRUE;
+ return PAIR_HL;
+ }
+ }
+ else
+ {
+ *pisUsed = TRUE;
+ return PAIR_HL;
+ }
+}
+
+static void
+fetchPairLong (PAIR_ID pairId, asmop * aop, iCode *ic, int offset)
+{
+ /* if this is remateriazable */
+ if (isLitWord (aop)) {
+ fetchLitPair (pairId, aop, offset);
+ }
+ else
+ {
+ if (getPairId (aop) == pairId)
+ {
+ /* Do nothing */
+ }
+ /* we need to get it byte by byte */
+ else if (pairId == PAIR_HL && IS_GB && requiresHL (aop)) {
+ aopGet (aop, offset, FALSE);
+ switch (aop->size - offset) {
+ case 1:
+ emit2 ("ld l,!*hl");
+ emit2 ("ld h,!immedbyte", 0);
+ break;
+ case 2:
+ // PENDING: Requires that you are only fetching two bytes.
+ case 4:
+ emit2 ("!ldahli");
+ emit2 ("ld h,!*hl");
+ emit2 ("ld l,a");
+ break;
+ default:
+ wassertl (0, "Attempted to fetch too much data into HL");
+ break;
+ }
+ }
+ else if (IS_Z80 && aop->type == AOP_IY) {
+ /* Instead of fetching relative to IY, just grab directly
+ from the address IY refers to */
+ char *l = aopGetLitWordLong (aop, offset, FALSE);
+ wassert (l);
+ emit2 ("ld %s,(%s)", _pairs[pairId].name, l);
+
+ if (aop->size < 2) {
+ emit2("ld %s,!zero", _pairs[pairId].h);
+ }
+ }
+ else if (pairId == PAIR_IY)
+ {
+ if (isPair (aop))
+ {
+ emit2 ("push %s", _pairs[getPairId(aop)].name);
+ emit2 ("pop iy");
+ }
+ else
+ {
+ bool isUsed;
+ PAIR_ID id = makeFreePairId (ic, &isUsed);
+ if (isUsed)
+ _push (id);
+ /* Can't load into parts, so load into HL then exchange. */
+ emit2 ("ld %s,%s", _pairs[id].l, aopGet (aop, offset, FALSE));
+ emit2 ("ld %s,%s", _pairs[id].h, aopGet (aop, offset + 1, FALSE));
+ emit2 ("push %s", _pairs[id].name);
+ emit2 ("pop iy");
+ if (isUsed)
+ _pop (id);
+ }
+ }
+ else if (isUnsplitable(aop))
+ {
+ emit2("push %s", _pairs[getPairId(aop)].name);
+ emit2("pop %s", _pairs[pairId].name);
+ }
+ else
+ {
+ emit2 ("ld %s,%s", _pairs[pairId].l, aopGet (aop, offset, FALSE));
+ emit2 ("ld %s,%s", _pairs[pairId].h, aopGet (aop, offset + 1, FALSE));
+ }
+ /* PENDING: check? */
+ if (pairId == PAIR_HL)
+ spillPair (PAIR_HL);
+ }
+}
+
+static void
+fetchPair (PAIR_ID pairId, asmop * aop)
+{
+ fetchPairLong (pairId, aop, NULL, 0);
+}
+
+static void
+fetchHL (asmop * aop)
+{
+ fetchPair (PAIR_HL, aop);
+}
+
+static void
+setupPairFromSP (PAIR_ID id, int offset)
+{
+ wassertl (id == PAIR_HL, "Setup relative to SP only implemented for HL");
+
+ if (_G.preserveCarry)
+ {
+ _push (PAIR_AF);
+ offset += 2;
+ }
+
+ if (offset < INT8MIN || offset > INT8MAX)
+ {
+ emit2 ("ld hl,!immedword", offset);
+ emit2 ("add hl,sp");
+ }
+ else
+ {
+ emit2 ("!ldahlsp", offset);
+ }
+
+ if (_G.preserveCarry)
+ {
+ _pop (PAIR_AF);
+ offset -= 2;
+ }
+}
+
+static void
+setupPair (PAIR_ID pairId, asmop * aop, int offset)
+{
+ switch (aop->type)
+ {
+ case AOP_IY:
+ wassertl (pairId == PAIR_IY || pairId == PAIR_HL, "AOP_IY must be in IY or HL");
+ fetchLitPair (pairId, aop, 0);
+ break;
+
+ case AOP_HL:
+ wassertl (pairId == PAIR_HL, "AOP_HL must be in HL");
+
+ fetchLitPair (pairId, aop, offset);
+ _G.pairs[pairId].offset = offset;
+ break;
+
+ case AOP_EXSTK:
+ wassertl (IS_Z80, "Only the Z80 has an extended stack");
+ wassertl (pairId == PAIR_IY || pairId == PAIR_HL, "The Z80 extended stack must be in IY or HL");
+
+ {
+ int offset = aop->aopu.aop_stk + _G.stack.offset;
+
+ if (_G.pairs[pairId].last_type == aop->type &&
+ _G.pairs[pairId].offset == offset)
+ {
+ /* Already setup */
+ }
+ else
+ {
+ /* PENDING: Do this better. */
+ if (_G.preserveCarry)
+ _push (PAIR_AF);
+ sprintf (buffer, "%d", offset + _G.stack.pushed);
+ emit2 ("ld %s,!hashedstr", _pairs[pairId].name, buffer);
+ emit2 ("add %s,sp", _pairs[pairId].name);
+ _G.pairs[pairId].last_type = aop->type;
+ _G.pairs[pairId].offset = offset;
+ if (_G.preserveCarry)
+ _pop (PAIR_AF);
+ }
+ }
+ break;
+
+ case AOP_STK:
+ {
+ /* Doesnt include _G.stack.pushed */
+ int abso = aop->aopu.aop_stk + offset + _G.stack.offset;
+
+ if (aop->aopu.aop_stk > 0)
+ {
+ abso += _G.stack.param_offset;
+ }
+ assert (pairId == PAIR_HL);
+ /* In some cases we can still inc or dec hl */
+ if (_G.pairs[pairId].last_type == AOP_STK && abs (_G.pairs[pairId].offset - abso) < 3)
+ {
+ adjustPair (_pairs[pairId].name, &_G.pairs[pairId].offset, abso);
+ }
+ else
+ {
+ setupPairFromSP (PAIR_HL, abso + _G.stack.pushed);
+ }
+ _G.pairs[pairId].offset = abso;
+ break;
+ }
+
+ case AOP_PAIRPTR:
+ if (pairId != aop->aopu.aop_pairId)
+ genMovePairPair(aop->aopu.aop_pairId, pairId);
+ adjustPair (_pairs[pairId].name, &_G.pairs[pairId].offset, offset);
+ break;
+
+ default:
+ wassert (0);
+ }
+ _G.pairs[pairId].last_type = aop->type;
+}
+
+static void
+emitLabel (int key)
+{
+ emit2 ("!tlabeldef", key);
+ spillCached ();
+}
+
+/*-----------------------------------------------------------------*/
+/* aopGet - for fetching value of the aop */
+/*-----------------------------------------------------------------*/
+static const char *
+aopGet (asmop * aop, int offset, bool bit16)
+{
+ // char *s = buffer;
+
+ /* offset is greater than size then zero */
+ /* PENDING: this seems a bit screwed in some pointer cases. */
+ if (offset > (aop->size - 1) &&
+ aop->type != AOP_LIT)
+ {
+ tsprintf (buffer, sizeof(buffer), "!zero");
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+ }
+
+ /* depending on type */
+ switch (aop->type)
+ {
+ case AOP_DUMMY:
+ tsprintf (buffer, sizeof(buffer), "!zero");
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+
+ case AOP_IMMD:
+ /* PENDING: re-target */
+ if (bit16)
+ tsprintf (buffer, sizeof(buffer), "!immedwords", aop->aopu.aop_immd);
+ else
+ switch (offset)
+ {
+ case 2:
+ tsprintf (buffer, sizeof(buffer), "!bankimmeds", aop->aopu.aop_immd);
+ break;
+ case 1:
+ tsprintf (buffer, sizeof(buffer), "!msbimmeds", aop->aopu.aop_immd);
+ break;
+ case 0:
+ tsprintf (buffer, sizeof(buffer), "!lsbimmeds", aop->aopu.aop_immd);
+ break;
+ default:
+ wassertl (0, "Fetching from beyond the limits of an immediate value.");
+ }
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+
+ case AOP_DIR:
+ wassert (IS_GB);
+ emit2 ("ld a,(%s+%d)", aop->aopu.aop_dir, offset);
+ SNPRINTF (buffer, sizeof(buffer), "a");
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+
+ case AOP_SFR:
+ if( IS_GB )
+ {
+ // wassert (IS_GB);
+ emit2 ("ldh a,(%s+%d)", aop->aopu.aop_dir, offset);
+ SNPRINTF (buffer, sizeof(buffer), "a");
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+ }
+ else
+ { /*.p.t.20030716 handling for i/o port read access for Z80 */
+ if( aop->paged )
+ { /* banked mode */
+ /* reg A goes to address bits 15-8 during "in a,(x)" instruction */
+ emit2( "ld a,!msbimmeds", aop->aopu.aop_dir);
+ emit2( "in a,(!lsbimmeds)", aop->aopu.aop_dir);
+ }
+ else if( z80_opts.port_mode == 180 )
+ { /* z180 in0/out0 mode */
+ emit2( "in0 a,(%s)", aop->aopu.aop_dir );
+ }
+ else
+ { /* 8 bit mode */
+ emit2( "in a,(%s)", aop->aopu.aop_dir );
+ }
+
+ SNPRINTF (buffer, sizeof(buffer), "a");
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+ }
+
+ case AOP_REG:
+ return aop->aopu.aop_reg[offset]->name;
+
+ case AOP_HL:
+ wassert (IS_GB);
+ setupPair (PAIR_HL, aop, offset);
+ tsprintf (buffer, sizeof(buffer), "!*hl");
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup (buffer));
+
+ case AOP_IY:
+ wassert (IS_Z80);
+ setupPair (PAIR_IY, aop, offset);
+ tsprintf (buffer, sizeof(buffer), "!*iyx", offset);
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+
+ case AOP_EXSTK:
+ wassert (IS_Z80);
+ setupPair (PAIR_IY, aop, offset);
+ tsprintf (buffer, sizeof(buffer), "!*iyx", offset, offset);
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+
+ case AOP_STK:
+ if (IS_GB)
+ {
+ setupPair (PAIR_HL, aop, offset);
+ tsprintf (buffer, sizeof(buffer), "!*hl");
+ }
+ else
+ {
+ if (aop->aopu.aop_stk >= 0)
+ offset += _G.stack.param_offset;
+ tsprintf (buffer, sizeof(buffer),
+ "!*ixx", aop->aopu.aop_stk + offset);
+ }
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+
+ case AOP_CRY:
+ wassertl (0, "Tried to fetch from a bit variable");
+
+ case AOP_ACC:
+ if (!offset)
+ {
+ return "a";
+ }
+ else
+ {
+ tsprintf(buffer, sizeof(buffer), "!zero");
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+ }
+
+ case AOP_HLREG:
+ wassert (offset < 2);
+ return aop->aopu.aop_str[offset];
+
+ case AOP_LIT:
+ return aopLiteral (aop->aopu.aop_lit, offset);
+
+ case AOP_SIMPLELIT:
+ {
+ unsigned long v = aop->aopu.aop_simplelit;
+
+ v >>= (offset * 8);
+ tsprintf (buffer, sizeof(buffer),
+ "!immedbyte", (unsigned int) v & 0xff);
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+ }
+ case AOP_STR:
+ aop->coff = offset;
+ return aop->aopu.aop_str[offset];
+
+ case AOP_PAIRPTR:
+ setupPair (aop->aopu.aop_pairId, aop, offset);
+ if (aop->aopu.aop_pairId==PAIR_IX)
+ SNPRINTF (buffer, sizeof(buffer),
+ "!*ixx", 0);
+ else if (aop->aopu.aop_pairId==PAIR_IY)
+ SNPRINTF (buffer, sizeof(buffer),
+ "!*iyx", 0);
+ else
+ SNPRINTF (buffer, sizeof(buffer),
+ "(%s)", _pairs[aop->aopu.aop_pairId].name);
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
+
+ default:
+ break;
+ }
+ wassertl (0, "aopget got unsupported aop->type");
+ exit (0);
+}
+
+bool
+isRegString (const char *s)
+{
+ if (!strcmp (s, "b") ||
+ !strcmp (s, "c") ||
+ !strcmp (s, "d") ||
+ !strcmp (s, "e") ||
+ !strcmp (s, "a") ||
+ !strcmp (s, "h") ||
+ !strcmp (s, "l"))
+ return TRUE;
+ return FALSE;
+}
+
+bool
+isConstant (const char *s)
+{
+ /* This is a bit of a hack... */
+ return (*s == '#' || *s == '$');
+}
+
+bool
+canAssignToPtr (const char *s)
+{
+ if (isRegString (s))
+ return TRUE;
+ if (isConstant (s))
+ return TRUE;
+ return FALSE;
+}
+
+/*-----------------------------------------------------------------*/
+/* aopPut - puts a string for a aop */
+/*-----------------------------------------------------------------*/
+static void
+aopPut (asmop * aop, const char *s, int offset)
+{
+ char buffer2[256];
+
+ if (aop->size && offset > (aop->size - 1))
+ {
+ werror (E_INTERNAL_ERROR, __FILE__, __LINE__,
+ "aopPut got offset > aop->size");
+ exit (0);
+ }
+
+ // PENDING
+ tsprintf(buffer2, sizeof(buffer2), s);
+ s = buffer2;
+
+ /* will assign value to value */
+ /* depending on where it is ofcourse */
+ switch (aop->type)
+ {
+ case AOP_DUMMY:
+ _moveA (s); /* in case s is volatile */
+ break;
+
+ case AOP_DIR:
+ /* Direct. Hmmm. */
+ wassert (IS_GB);
+ if (strcmp (s, "a"))
+ emit2 ("ld a,%s", s);
+ emit2 ("ld (%s+%d),a", aop->aopu.aop_dir, offset);
+ break;
+
+ case AOP_SFR:
+ if( IS_GB )
+ {
+ // wassert (IS_GB);
+ if (strcmp (s, "a"))
+ emit2 ("ld a,%s", s);
+ emit2 ("ldh (%s+%d),a", aop->aopu.aop_dir, offset);
+ }
+ else
+ { /*.p.t.20030716 handling for i/o port read access for Z80 */
+ if (aop->paged)
+ { /* banked mode */
+ if (aop->bcInUse)
+ emit2( "push bc" );
+
+ if (strlen(s) != 1
+ || (s[0] != 'a' && s[0] != 'd' && s[0] != 'e'
+ && s[0] != 'h' && s[0] != 'l'))
+ {
+ emit2( "ld a,%s", s );
+ s = "a";
+ }
+
+ emit2( "ld bc,#%s", aop->aopu.aop_dir );
+ emit2( "out (c),%s", s );
+
+ if( aop->bcInUse )
+ emit2( "pop bc" );
+ else
+ spillPair (PAIR_BC);
+ }
+ else if( z80_opts.port_mode == 180 )
+ { /* z180 in0/out0 mode */
+ emit2( "ld a,%s", s );
+ emit2( "out0 (%s),a", aop->aopu.aop_dir );
+ }
+ else
+ { /* 8 bit mode */
+ emit2( "ld a,%s", s );
+ emit2( "out (%s),a", aop->aopu.aop_dir );
+ }
+ }
+ break;
+
+ case AOP_REG:
+ if (!strcmp (s, "!*hl"))
+ emit2 ("ld %s,!*hl", aop->aopu.aop_reg[offset]->name);
+ else
+ emit2 ("ld %s,%s",
+ aop->aopu.aop_reg[offset]->name, s);
+ spillPairReg(aop->aopu.aop_reg[offset]->name);
+ break;
+
+ case AOP_IY:
+ wassert (!IS_GB);
+ if (!canAssignToPtr (s))
+ {
+ emit2 ("ld a,%s", s);
+ setupPair (PAIR_IY, aop, offset);
+ emit2 ("ld !*iyx,a", offset);
+ }
+ else
+ {
+ setupPair (PAIR_IY, aop, offset);
+ emit2 ("ld !*iyx,%s", offset, s);
+ }
+ break;
+
+ case AOP_HL:
+ wassert (IS_GB);
+ /* PENDING: for re-target */
+ if (!strcmp (s, "!*hl") || !strcmp (s, "(hl)") || !strcmp (s, "[hl]"))
+ {
+ emit2 ("ld a,!*hl");
+ s = "a";
+ }
+ setupPair (PAIR_HL, aop, offset);
+
+ emit2 ("ld !*hl,%s", s);
+ break;
+
+ case AOP_EXSTK:
+ wassert (!IS_GB);
+ if (!canAssignToPtr (s))
+ {
+ emit2 ("ld a,%s", s);
+ setupPair (PAIR_IY, aop, offset);
+ emit2 ("ld !*iyx,a", offset);
+ }
+ else
+ {
+ setupPair (PAIR_IY, aop, offset);
+ emit2 ("ld !*iyx,%s", offset, s);
+ }
+ break;
+
+ case AOP_STK:
+ if (IS_GB)
+ {
+ /* PENDING: re-target */
+ if (!strcmp (s, "!*hl") || !strcmp (s, "(hl)") || !strcmp (s, "[hl]"))
+ {
+ emit2 ("ld a,!*hl");
+ s = "a";
+ }
+ setupPair (PAIR_HL, aop, offset);
+ if (!canAssignToPtr (s))
+ {
+ emit2 ("ld a,%s", s);
+ emit2 ("ld !*hl,a");
+ }
+ else
+ emit2 ("ld !*hl,%s", s);
+ }
+ else
+ {
+ if (aop->aopu.aop_stk >= 0)
+ offset += _G.stack.param_offset;
+ if (!canAssignToPtr (s))
+ {
+ emit2 ("ld a,%s", s);
+ emit2 ("ld !*ixx,a", aop->aopu.aop_stk + offset);
+ }
+ else
+ {
+ emit2 ("ld !*ixx,%s", aop->aopu.aop_stk + offset, s);
+ }
+ }
+ break;
+
+ case AOP_CRY:
+ /* if bit variable */
+ if (!aop->aopu.aop_dir)
+ {
+ emit2 ("ld a,!zero");
+ emit2 ("rla");
+ }
+ else
+ {
+ /* In bit space but not in C - cant happen */
+ wassertl (0, "Tried to write into a bit variable");
+ }
+ break;
+
+ case AOP_STR:
+ aop->coff = offset;
+ if (strcmp (aop->aopu.aop_str[offset], s))
+ {
+ emit2 ("ld %s,%s", aop->aopu.aop_str[offset], s);
+ }
+ spillPairReg(aop->aopu.aop_str[offset]);
+ break;
+
+ case AOP_ACC:
+ aop->coff = offset;
+ if (!offset && (strcmp (s, "acc") == 0))
+ break;
+ if (offset > 0)
+ {
+ wassertl (0, "Tried to access past the end of A");
+ }
+ else
+ {
+ if (strcmp (aop->aopu.aop_str[offset], s))
+ {
+ emit2 ("ld %s,%s", aop->aopu.aop_str[offset], s);
+ spillPairReg(aop->aopu.aop_str[offset]);
+ }
+ }
+ break;
+
+ case AOP_HLREG:
+ wassert (offset < 2);
+ emit2 ("ld %s,%s", aop->aopu.aop_str[offset], s);
+ spillPairReg(aop->aopu.aop_str[offset]);
+ break;
+
+ case AOP_PAIRPTR:
+ setupPair (aop->aopu.aop_pairId, aop, offset);
+ if (aop->aopu.aop_pairId==PAIR_IX)
+ emit2 ("ld !*ixx,%s", 0, s);
+ else if (aop->aopu.aop_pairId==PAIR_IY)
+ emit2 ("ld !*ixy,%s", 0, s);
+ else
+ emit2 ("ld (%s),%s", _pairs[aop->aopu.aop_pairId].name, s);
+ break;
+
+ default:
+ werror (E_INTERNAL_ERROR, __FILE__, __LINE__,
+ "aopPut got unsupported aop->type");
+ exit (0);
+ }
+}
+
+#define AOP(op) op->aop
+#define AOP_TYPE(op) AOP(op)->type
+#define AOP_SIZE(op) AOP(op)->size
+#define AOP_NEEDSACC(x) (AOP(x) && ((AOP_TYPE(x) == AOP_CRY) || (AOP_TYPE(x) == AOP_SFR)))
+
+static void
+commitPair (asmop * aop, PAIR_ID id)
+{
+ /* PENDING: Verify this. */
+ if (id == PAIR_HL && requiresHL (aop) && IS_GB)
+ {
+ emit2 ("ld a,l");
+ emit2 ("ld d,h");
+ aopPut (aop, "a", 0);
+ aopPut (aop, "d", 1);
+ }
+ else
+ {
+ /* Special cases */
+ if (id == PAIR_HL && aop->type == AOP_IY && aop->size == 2)
+ {
+ char *l = aopGetLitWordLong (aop, 0, FALSE);
+ wassert (l);
+
+ emit2 ("ld (%s),%s", l, _pairs[id].name);
+ }
+ else
+ {
+ aopPut (aop, _pairs[id].l, 0);
+ aopPut (aop, _pairs[id].h, 1);
+ }
+ }
+}
+
+/*-----------------------------------------------------------------*/
+/* getDataSize - get the operand data size */
+/*-----------------------------------------------------------------*/
+int
+getDataSize (operand * op)
+{
+ int size;
+ size = AOP_SIZE (op);
+ if (size == 3)
+ {
+ /* pointer */
+ wassertl (0, "Somehow got a three byte data pointer");
+ }
+ return size;
+}
+
+/*-----------------------------------------------------------------*/
+/* movLeft2Result - move byte from left to result */
+/*-----------------------------------------------------------------*/
+static void
+movLeft2Result (operand * left, int offl,
+ operand * result, int offr, int sign)
+{
+ const char *l;
+
+ if (!sameRegs (AOP (left), AOP (result)) || (offl != offr))
+ {
+ l = aopGet (AOP (left), offl, FALSE);
+
+ if (!sign)
+ {
+ aopPut (AOP (result), l, offr);
+ }
+ else
+ {
+ if (getDataSize (left) == offl + 1)
+ {
+ emit2 ("ld a,%s", l);
+ aopPut (AOP (result), "a", offr);
+ }
+ }
+ }
+}
+
+static void
+movLeft2ResultLong (operand * left, int offl,
+ operand * result, int offr, int sign,
+ int size)
+{
+ if (size == 1)
+ {
+ movLeft2Result (left, offl, result, offr, sign);
+ }
+ else
+ {
+ wassertl (offl == 0 && offr == 0, "Only implemented for zero offset");
+ wassertl (size == 2, "Only implemented for two bytes or one");
+
+ if ( IS_GB && requiresHL ( AOP (left)) && getPairId ( AOP (result)) == PAIR_HL)
+ {
+ emit2 ("ld a,%s", aopGet (AOP (left), LSB, FALSE));
+ emit2 ("ld h,%s", aopGet (AOP (left), MSB16, FALSE));
+ emit2 ("ld l,a");
+ spillPair (PAIR_HL);
+ }
+ else if ( getPairId ( AOP (result)) == PAIR_IY)
+ {
+ PAIR_ID id = getPairId (AOP (left));
+ if (id != PAIR_INVALID)
+ {
+ emit2("push %s", _pairs[id].name);
+ emit2("pop iy");
+ }
+ else
+ {
+ /* PENDING */
+ emitDebug("Error");
+ }
+ }
+ else
+ {
+ movLeft2Result (left, offl, result, offr, sign);
+ movLeft2Result (left, offl+1, result, offr+1, sign);
+ }
+ }
+}
+
+/** Put Acc into a register set
+ */
+void
+outAcc (operand * result)
+{
+ int size, offset;
+ size = getDataSize (result);
+ if (size)
+ {
+ aopPut (AOP (result), "a", 0);
+ size--;
+ offset = 1;
+ /* unsigned or positive */
+ while (size--)
+ {
+ aopPut (AOP (result), "!zero", offset++);
+ }
+ }
+}
+
+/** Take the value in carry and put it into a register
+ */
+void
+outBitCLong (operand * result, bool swap_sense)
+{
+ /* if the result is bit */
+ if (AOP_TYPE (result) == AOP_CRY)
+ {
+ wassertl (0, "Tried to write carry to a bit");
+ }
+ else
+ {
+ emit2 ("ld a,!zero");
+ emit2 ("rla");
+ if (swap_sense)
+ emit2 ("xor a,!immedbyte", 1);
+ outAcc (result);
+ }
+}
+
+void
+outBitC (operand * result)
+{
+ outBitCLong (result, FALSE);
+}
+
+/*-----------------------------------------------------------------*/
+/* toBoolean - emit code for orl a,operator(sizeop) */
+/*-----------------------------------------------------------------*/
+void
+_toBoolean (operand * oper)
+{
+ int size = AOP_SIZE (oper);
+ int offset = 0;
+ if (size > 1)
+ {
+ emit2 ("ld a,%s", aopGet (AOP (oper), offset++, FALSE));
+ size--;
+ while (size--)
+ emit2 ("or a,%s", aopGet (AOP (oper), offset++, FALSE));
+ }
+ else
+ {
+ if (AOP (oper)->type != AOP_ACC)
+ {
+ _clearCarry();
+ emit2 ("or a,%s", aopGet (AOP (oper), 0, FALSE));
+ }
+ }
+}
+
+
+/*-----------------------------------------------------------------*/
+/* genNot - generate code for ! operation */
+/*-----------------------------------------------------------------*/
+static void
+genNot (iCode * ic)
+{
+
+ /* assign asmOps to operand & result */
+ aopOp (IC_LEFT (ic), ic, FALSE, TRUE);
+ aopOp (IC_RESULT (ic), ic, TRUE, FALSE);
+
+ /* if in bit space then a special case */
+ if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY)
+ {
+ wassertl (0, "Tried to negate a bit");
+ }
+
+ _toBoolean (IC_LEFT (ic));
+
+ /* Not of A:
+ If A == 0, !A = 1
+ else A = 0
+ So if A = 0, A-1 = 0xFF and C is set, rotate C into reg. */
+ emit2 ("sub a,!one");
+ outBitC (IC_RESULT (ic));
+
+ /* release the aops */
+ freeAsmop (IC_LEFT (ic), NULL, ic);
+ freeAsmop (IC_RESULT (ic), NULL, ic);
+}
+
+/*-----------------------------------------------------------------*/
+/* genCpl - generate code for complement */
+/*-----------------------------------------------------------------*/
+static void
+genCpl (iCode * ic)
+{
+ int offset = 0;
+ int size;
+
+
+ /* assign asmOps to operand & result */
+ aopOp (IC_LEFT (ic), ic, FALSE, FALSE);
+ aopOp (IC_RESULT (ic), ic, TRUE, FALSE);
+
+ /* if both are in bit space then
+ a special case */
+ if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY &&
+ AOP_TYPE (IC_LEFT (ic)) == AOP_CRY)
+ {
+ wassertl (0, "Left and the result are in bit space");
+ }
+
+ size = AOP_SIZE (IC_RESULT (ic));
+ while (size--)
+ {
+ const char *l = aopGet (AOP (IC_LEFT (ic)), offset, FALSE);
+ _moveA (l);
+ emit2("cpl");
+ aopPut (AOP (IC_RESULT (ic)), "a", offset++);
+ }
+
+ /* release the aops */
+ freeAsmop (IC_LEFT (ic), NULL, ic);
+ freeAsmop (IC_RESULT (ic), NULL, ic);
+}
+
+static void
+_gbz80_emitAddSubLongLong (iCode *ic, asmop *left, asmop *right, bool isAdd)
+{
+ /* Logic:
+ ld de,right.lw
+ setup hl to left
+ de = hl - de
+ push flags
+ store de into result
+ pop flags
+ ld de,right.hw
+ setup hl
+ de = hl -de
+ store de into result
+ */
+ const char *first = isAdd ? "add" : "sub";
+ const char *later = isAdd ? "adc" : "sbc";
+
+ wassertl (IS_GB, "Code is only relevent to the gbz80");
+ wassertl (AOP( IC_RESULT (ic))->size == 4, "Only works for four bytes");
+
+ fetchPair (PAIR_DE, left);
+
+ emit2 ("ld a,e");
+ emit2 ("%s a,%s", first, aopGet( right, LSB, FALSE));
+ emit2 ("ld e,a");
+ emit2 ("ld a,d");
+ emit2 ("%s a,%s", later, aopGet( right, MSB16, FALSE));
+
+ _push (PAIR_AF);
+ aopPut ( AOP (IC_RESULT (ic)), "a", MSB16);
+ aopPut ( AOP (IC_RESULT (ic)), "e", LSB);
+
+ fetchPairLong (PAIR_DE, left, NULL, MSB24);
+ aopGet (right, MSB24, FALSE);
+
+ _pop (PAIR_AF);
+ emit2 ("ld a,e");
+ emit2 ("%s a,%s", later, aopGet( right, MSB24, FALSE));
+ emit2 ("ld e,a");
+ emit2 ("ld a,d");
+ emit2 ("%s a,%s", later, aopGet( right, MSB32, FALSE));
+
+ aopPut ( AOP (IC_RESULT (ic)), "a", MSB32);
+ aopPut ( AOP (IC_RESULT (ic)), "e", MSB24);
+}
+
+static void
+_gbz80_emitAddSubLong (iCode *ic, bool isAdd)
+{
+ _gbz80_emitAddSubLongLong (ic, AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic)), isAdd);
+}
+
+/*-----------------------------------------------------------------*/
+/* genUminusFloat - unary minus for floating points */
+/*-----------------------------------------------------------------*/
+static void
+genUminusFloat (operand * op, operand * result)
+{
+ int size, offset = 0;
+
+ emitDebug("; genUminusFloat");
+
+ /* for this we just need to flip the
+ first bit then copy the rest in place */
+ size = AOP_SIZE (op) - 1;
+
+ _moveA(aopGet (AOP (op), MSB32, FALSE));
+
+ emit2("xor a,!immedbyte", 0x80);
+ aopPut (AOP (result), "a", MSB32);
+
+ while (size--)
+ {
+ aopPut (AOP (result), aopGet (AOP (op), offset, FALSE), offset);
+ offset++;
+ }
+}
+
+/*-----------------------------------------------------------------*/
+/* genUminus - unary minus code generation */
+/*-----------------------------------------------------------------*/
+static void
+genUminus (iCode * ic)
+{
+ int offset, size;
+ sym_link *optype, *rtype;
+
+ /* assign asmops */
+ aopOp (IC_LEFT (ic), ic, FALSE, FALSE);
+ aopOp (IC_RESULT (ic), ic, TRUE, FALSE);
+
+ /* if both in bit space then special
+ case */
+ if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY &&
+ AOP_TYPE (IC_LEFT (ic)) == AOP_CRY)
+ {
+ wassertl (0, "Left and right are in bit space");
+ goto release;
+ }
+
+ optype = operandType (IC_LEFT (ic));
+ rtype = operandType (IC_RESULT (ic));
+
+ /* if float then do float stuff */
+ if (IS_FLOAT (optype))
+ {
+ genUminusFloat (IC_LEFT (ic), IC_RESULT (ic));
+ goto release;
+ }
+
+ /* otherwise subtract from zero */
+ size = AOP_SIZE (IC_LEFT (ic));
+
+ if (AOP_SIZE (IC_RESULT (ic)) == 4 && IS_GB)
+ {
+ /* Create a new asmop with value zero */
+ asmop *azero = newAsmop (AOP_SIMPLELIT);
+ azero->aopu.aop_simplelit = 0;
+ azero->size = size;
+ _gbz80_emitAddSubLongLong (ic, azero, AOP (IC_LEFT (ic)), FALSE);
+ goto release;
+ }
+
+ offset = 0;
+ _clearCarry();
+ while (size--)
+ {
+ const char *l = aopGet (AOP (IC_LEFT (ic)), offset, FALSE);
+ emit2 ("ld a,!zero");
+ emit2 ("sbc a,%s", l);
+ aopPut (AOP (IC_RESULT (ic)), "a", offset++);
+ }
+
+ /* if any remaining bytes in the result */
+ /* we just need to propagate the sign */
+ if ((size = (AOP_SIZE (IC_RESULT (ic)) - AOP_SIZE (IC_LEFT (ic)))))
+ {
+ emit2 ("rlc a");
+ emit2 ("sbc a,a");
+ while (size--)
+ aopPut (AOP (IC_RESULT (ic)), "a", offset++);
+ }
+
+release:
+ /* release the aops */
+ freeAsmop (IC_LEFT (ic), NULL, ic);
+ freeAsmop (IC_RESULT (ic), NULL, ic);
+}
+
+/*-----------------------------------------------------------------*/
+/* assignResultValue - */
+/*-----------------------------------------------------------------*/
+void
+assignResultValue (operand * oper)
+{
+ int size = AOP_SIZE (oper);
+ bool topInA = 0;
+
+ wassertl (size <= 4, "Got a result that is bigger than four bytes");
+ topInA = requiresHL (AOP (oper));
+
+ if (IS_GB && size == 4 && requiresHL (AOP (oper)))
+ {
+ /* We do it the hard way here. */
+ _push (PAIR_HL);
+ aopPut (AOP (oper), _fReturn[0], 0);
+ aopPut (AOP (oper), _fReturn[1], 1);
+ _pop (PAIR_DE);
+ aopPut (AOP (oper), _fReturn[0], 2);
+ aopPut (AOP (oper), _fReturn[1], 3);
+ }
+ else
+ {
+ while (size--)
+ {
+ aopPut (AOP (oper), _fReturn[size], size);
+ }
+ }
+}
+
+/** Simple restore that doesn't take into account what is used in the
+ return.
+*/
+static void
+_restoreRegsAfterCall(void)
+{
+ if (_G.stack.pushedDE)
+ {
+ _pop ( PAIR_DE);
+ _G.stack.pushedDE = FALSE;
+ }
+ if (_G.stack.pushedBC)
+ {
+ _pop ( PAIR_BC);
+ _G.stack.pushedBC = FALSE;
+ }
+ _G.saves.saved = FALSE;
+}
+
+static void
+_saveRegsForCall(iCode *ic, int sendSetSize)
+{
+ /* Rules:
+ o Stack parameters are pushed before this function enters
+ o DE and BC may be used in this function.
+ o HL and DE may be used to return the result.
+ o HL and DE may be used to send variables.
+ o DE and BC may be used to store the result value.
+ o HL may be used in computing the sent value of DE
+ o The iPushes for other parameters occur before any addSets
+
+ Logic: (to be run inside the first iPush or if none, before sending)
+ o Compute if DE and/or BC are in use over the call
+ o Compute if DE is used in the send set
+ o Compute if DE and/or BC are used to hold the result value
+ o If (DE is used, or in the send set) and is not used in the result, push.
+ o If BC is used and is not in the result, push
+ o
+ o If DE is used in the send set, fetch
+ o If HL is used in the send set, fetch
+ o Call
+ o ...
+ */
+ if (_G.saves.saved == FALSE) {
+ bool deInUse, bcInUse;
+ bool deSending;
+ bool bcInRet = FALSE, deInRet = FALSE;
+ bitVect *rInUse;
+
+ rInUse = bitVectCplAnd (bitVectCopy (ic->rMask),
+ z80_rUmaskForOp (IC_RESULT(ic)));
+
+ deInUse = bitVectBitValue (rInUse, D_IDX) || bitVectBitValue(rInUse, E_IDX);
+ bcInUse = bitVectBitValue (rInUse, B_IDX) || bitVectBitValue(rInUse, C_IDX);
+
+ deSending = (sendSetSize > 1);
+
+ emitDebug ("; _saveRegsForCall: sendSetSize: %u deInUse: %u bcInUse: %u deSending: %u", sendSetSize, deInUse, bcInUse, deSending);
+
+ if (bcInUse && bcInRet == FALSE) {
+ _push(PAIR_BC);
+ _G.stack.pushedBC = TRUE;
+ }
+ if (deInUse && deInRet == FALSE) {
+ _push(PAIR_DE);
+ _G.stack.pushedDE = TRUE;
+ }
+
+ _G.saves.saved = TRUE;
+ }
+ else {
+ /* Already saved. */
+ }
+}
+
+/*-----------------------------------------------------------------*/
+/* genIpush - genrate code for pushing this gets a little complex */
+/*-----------------------------------------------------------------*/
+static void
+genIpush (iCode * ic)
+{
+ int size, offset = 0;
+ const char *l;
+
+ /* if this is not a parm push : ie. it is spill push
+ and spill push is always done on the local stack */
+ if (!ic->parmPush)
+ {
+ wassertl(0, "Encountered an unsupported spill push.");
+ return;
+ }
+
+ if (_G.saves.saved == FALSE) {
+ /* Caller saves, and this is the first iPush. */
+ /* Scan ahead until we find the function that we are pushing parameters to.
+ Count the number of addSets on the way to figure out what registers
+ are used in the send set.
+ */
+ int nAddSets = 0;
+ iCode *walk = ic->next;
+
+ while (walk) {
+ if (walk->op == SEND) {
+ nAddSets++;
+ }
+ else if (walk->op == CALL || walk->op == PCALL) {
+ /* Found it. */
+ break;
+ }
+ else {
+ /* Keep looking. */
+ }
+ walk = walk->next;
+ }
+ _saveRegsForCall(walk, nAddSets);
+ }
+ else {
+ /* Already saved by another iPush. */
+ }
+
+ /* then do the push */
+ aopOp (IC_LEFT (ic), ic, FALSE, FALSE);
+
+ size = AOP_SIZE (IC_LEFT (ic));
+
+ if (isPair (AOP (IC_LEFT (ic))) && size == 2)
+ {
+ _G.stack.pushed += 2;
+ emit2 ("push %s", getPairName (AOP (IC_LEFT (ic))));
+ }
+ else
+ {
+ if (size == 2)
+ {
+ fetchHL (AOP (IC_LEFT (ic)));
+ emit2 ("push hl");
+ spillPair (PAIR_HL);
+ _G.stack.pushed += 2;
+ goto release;
+ }
+ if (size == 4)
+ {
+ fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), ic, 2);
+ emit2 ("push hl");
+ spillPair (PAIR_HL);
+ _G.stack.pushed += 2;
+ fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), ic, 0);
+ emit2 ("push hl");
+ spillPair (PAIR_HL);
+ _G.stack.pushed += 2;
+ goto release;
+ }
+ offset = size;
+ while (size--)
+ {
+ if (AOP (IC_LEFT (ic))->type == AOP_IY)
+ {
+ char *l = aopGetLitWordLong (AOP (IC_LEFT (ic)), --offset, FALSE);
+ wassert (l);
+ emit2 ("ld a,(%s)", l);
+ }
+ else
+ {
+ l = aopGet (AOP (IC_LEFT (ic)), --offset, FALSE);
+ emit2 ("ld a,%s", l);
+ }
+ emit2 ("push af");
+ emit2 ("inc sp");
+ _G.stack.pushed++;
+ }
+ }
+release:
+ freeAsmop (IC_LEFT (ic), NULL, ic);
+}
+
+/*-----------------------------------------------------------------*/
+/* genIpop - recover the registers: can happen only for spilling */
+/*-----------------------------------------------------------------*/
+static void
+genIpop (iCode * ic)
+{
+ int size, offset;
+
+
+ /* if the temp was not pushed then */
+ if (OP_SYMBOL (IC_LEFT (ic))->isspilt)
+ return;
+
+ aopOp (IC_LEFT (ic), ic, FALSE, FALSE);
+ size = AOP_SIZE (IC_LEFT (ic));
+ offset = (size - 1);
+ if (isPair (AOP (IC_LEFT (ic))))
+ {
+ emit2 ("pop %s", getPairName (AOP (IC_LEFT (ic))));
+ }
+ else
+ {
+ while (size--)
+ {
+ emit2 ("dec sp");
+ emit2 ("pop hl");
+ spillPair (PAIR_HL);
+ aopPut (AOP (IC_LEFT (ic)), "l", offset--);
+ }
+ }
+
+ freeAsmop (IC_LEFT (ic), NULL, ic);
+}
+
+/* This is quite unfortunate */
+static void
+setArea (int inHome)
+{
+ /*
+ static int lastArea = 0;
+
+ if (_G.in_home != inHome) {
+ if (inHome) {
+ const char *sz = port->mem.code_name;
+ port->mem.code_name = "HOME";
+ emit2("!area", CODE_NAME);
+ port->mem.code_name = sz;
+ }
+ else
+ emit2("!area", CODE_NAME); */
+ _G.in_home = inHome;
+ // }
+}
+
+static bool
+isInHome (void)
+{
+ return _G.in_home;
+}
+
+static int
+_opUsesPair (operand * op, iCode * ic, PAIR_ID pairId)
+{
+ int ret = 0;
+ asmop *aop;
+ symbol *sym = OP_SYMBOL (op);
+
+ if (sym->isspilt || sym->nRegs == 0)
+ return 0;
+
+ aopOp (op, ic, FALSE, FALSE);
+
+ aop = AOP (op);
+ if (aop->type == AOP_REG)
+ {
+ int i;
+ for (i = 0; i < aop->size; i++)
+ {
+ if (pairId == PAIR_DE)
+ {
+ emitDebug ("; name %s", aop->aopu.aop_reg[i]->name);
+ if (!strcmp (aop->aopu.aop_reg[i]->name, "e"))
+ ret++;
+ if (!strcmp (aop->aopu.aop_reg[i]->name, "d"))
+ ret++;
+ }
+ else if (pairId == PAIR_BC)
+ {
+ emitDebug ("; name %s", aop->aopu.aop_reg[i]->name);
+ if (!strcmp (aop->aopu.aop_reg[i]->name, "c"))
+ ret++;
+ if (!strcmp (aop->aopu.aop_reg[i]->name, "b"))
+ ret++;
+ }
+ else
+ {
+ wassert (0);
+ }
+ }
+ }
+
+ freeAsmop (IC_LEFT (ic), NULL, ic);
+ return ret;
+}
+
+/** Emit the code for a call statement
+ */
+static void
+emitCall (iCode * ic, bool ispcall)
+{
+ bool bInRet, cInRet, dInRet, eInRet;
+ sym_link *dtype = operandType (IC_LEFT (ic));
+
+ /* if caller saves & we have not saved then */
+ if (!ic->regsSaved)
+ {
+ /* PENDING */
+ }
+
+ _saveRegsForCall(ic, _G.sendSet ? elementsInSet(_G.sendSet) : 0);
+
+ /* if send set is not empty then assign */
+ if (_G.sendSet)
+ {
+ iCode *sic;
+ int send = 0;
+ int nSend = elementsInSet(_G.sendSet);
+ bool swapped = FALSE;
+
+ int _z80_sendOrder[] = {
+ PAIR_BC, PAIR_DE
+ };
+
+ if (nSend > 1) {
+ /* Check if the parameters are swapped. If so route through hl instead. */
+ wassertl (nSend == 2, "Pedantic check. Code only checks for the two send items case.");
+
+ sic = setFirstItem(_G.sendSet);
+ sic = setNextItem(_G.sendSet);
+
+ if (_opUsesPair (IC_LEFT(sic), sic, _z80_sendOrder[0])) {
+ /* The second send value is loaded from one the one that holds the first
+ send, i.e. it is overwritten. */
+ /* Cache the first in HL, and load the second from HL instead. */
+ emit2 ("ld h,%s", _pairs[_z80_sendOrder[0]].h);
+ emit2 ("ld l,%s", _pairs[_z80_sendOrder[0]].l);
+
+ swapped = TRUE;
+ }
+ }
+
+ for (sic = setFirstItem (_G.sendSet); sic;
+ sic = setNextItem (_G.sendSet))
+ {
+ int size;
+ aopOp (IC_LEFT (sic), sic, FALSE, FALSE);
+
+ size = AOP_SIZE (IC_LEFT (sic));
+ wassertl (size <= 2, "Tried to send a parameter that is bigger than two bytes");
+ wassertl (_z80_sendOrder[send] != PAIR_INVALID, "Tried to send more parameters than we have registers for");
+
+ // PENDING: Mild hack
+ if (swapped == TRUE && send == 1) {
+ if (size > 1) {
+ emit2 ("ld %s,h", _pairs[_z80_sendOrder[send]].h);
+ }
+ else {
+ emit2 ("ld %s,!zero", _pairs[_z80_sendOrder[send]].h);
+ }
+ emit2 ("ld %s,l", _pairs[_z80_sendOrder[send]].l);
+ }
+ else {
+ fetchPair(_z80_sendOrder[send], AOP (IC_LEFT (sic)));
+ }
+
+ send++;
+ freeAsmop (IC_LEFT (sic), NULL, sic);
+ }
+ _G.sendSet = NULL;
+ }
+
+ if (ispcall)
+ {
+ if (IFFUNC_ISBANKEDCALL (dtype) && !SPEC_STAT(getSpec(dtype)))
+ {
+ werror (W_INDIR_BANKED);
+ }
+ aopOp (IC_LEFT (ic), ic, FALSE, FALSE);
+
+ if (isLitWord (AOP (IC_LEFT (ic))))
+ {
+ emit2 ("call %s", aopGetLitWordLong (AOP (IC_LEFT (ic)), 0, FALSE));
+ }
+ else
+ {
+ symbol *rlbl = newiTempLabel (NULL);
+ spillPair (PAIR_HL);
+ emit2 ("ld hl,!immed!tlabel", (rlbl->key + 100));
+ emit2 ("push hl");
+ _G.stack.pushed += 2;
+
+ fetchHL (AOP (IC_LEFT (ic)));
+ emit2 ("jp !*hl");
+ emit2 ("!tlabeldef", (rlbl->key + 100));
+ _G.stack.pushed -= 2;
+ }
+ freeAsmop (IC_LEFT (ic), NULL, ic);
+ }
+ else
+ {
+ char *name = OP_SYMBOL (IC_LEFT (ic))->rname[0] ?
+ OP_SYMBOL (IC_LEFT (ic))->rname :
+ OP_SYMBOL (IC_LEFT (ic))->name;
+ if (IFFUNC_ISBANKEDCALL (dtype) && !SPEC_STAT(getSpec(dtype)))
+ {
+ emit2 ("call banked_call");
+ emit2 ("!dws", name);
+ emit2 ("!dw !bankimmeds", name);
+ }
+ else
+ {
+ /* make the call */
+ emit2 ("call %s", name);
+ }
+ }
+ spillCached ();
+
+ /* Mark the registers as restored. */
+ _G.saves.saved = FALSE;
+
+ /* 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);
+
+ assignResultValue (IC_RESULT (ic));
+
+ freeAsmop (IC_RESULT (ic), NULL, ic);
+ }
+
+ /* adjust the stack for parameters if required */
+ if (ic->parmBytes)
+ {
+ int i = ic->parmBytes;
+
+ _G.stack.pushed -= i;
+ if (IS_GB)
+ {
+ emit2 ("!ldaspsp", i);
+ }
+ else
+ {
+ spillCached ();
+ if (i > 8)
+ {
+ emit2 ("ld iy,!immedword", i);
+ emit2 ("add iy,sp");
+ emit2 ("ld sp,iy");
+ }
+ else
+ {
+ while (i > 1)
+ {
+ emit2 ("pop af");
+ i -= 2;
+ }
+ if (i)
+ {
+ emit2 ("inc sp");
+ }
+ }
+ }
+ }
+
+ spillCached ();
+ if (IC_RESULT (ic))
+ {
+ bitVect *result = z80_rUmaskForOp (IC_RESULT (ic));
+ bInRet = bitVectBitValue(result, B_IDX);
+ cInRet = bitVectBitValue(result, C_IDX);
+ dInRet = bitVectBitValue(result, D_IDX);
+ eInRet = bitVectBitValue(result, E_IDX);
+ }
+ else
+ {
+ bInRet = FALSE;
+ cInRet = FALSE;
+ dInRet = FALSE;
+ eInRet = FALSE;
+ }
+
+ if (_G.stack.pushedDE)
+ {
+ if (dInRet && eInRet)
+ {
+ wassertl (0, "Shouldn't push DE if it's wiped out by the return");
+ }
+ else if (dInRet)
+ {
+ /* Only restore E */
+ emit2 ("ld a,d");
+ _pop (PAIR_DE);
+ emit2 ("ld d,a");
+ }
+ else if (eInRet)
+ {
+ /* Only restore D */
+ _pop (PAIR_AF);
+ emit2 ("ld d,a");
+ }
+ else
+ {
+ _pop (PAIR_DE);
+ }
+ _G.stack.pushedDE = FALSE;
+ }
+
+ if (_G.stack.pushedBC)
+ {
+ if (bInRet && cInRet)
+ {
+ wassertl (0, "Shouldn't push BC if it's wiped out by the return");
+ }
+ else if (bInRet)
+ {
+ /* Only restore C */
+ emit2 ("ld a,b");
+ _pop (PAIR_BC);
+ emit2 ("ld b,a");
+ }
+ else if (cInRet)
+ {
+ /* Only restore B */
+ _pop (PAIR_AF);
+ emit2 ("ld b,a");
+ }
+ else
+ {
+ _pop (PAIR_BC);
+ }
+ _G.stack.pushedBC = FALSE;
+ }
+}
+
+/*-----------------------------------------------------------------*/
+/* genCall - generates a call statement */
+/*-----------------------------------------------------------------*/
+static void
+genCall (iCode * ic)
+{
+ emitCall (ic, FALSE);
+}
+
+/*-----------------------------------------------------------------*/
+/* genPcall - generates a call by pointer statement */
+/*-----------------------------------------------------------------*/
+static void
+genPcall (iCode * ic)
+{
+ emitCall (ic, TRUE);
+}
+
+/*-----------------------------------------------------------------*/
+/* resultRemat - result is rematerializable */
+/*-----------------------------------------------------------------*/
+static int
+resultRemat (iCode * ic)
+{
+ if (SKIP_IC (ic) || ic->op == IFX)
+ return 0;
+
+ if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic)))
+ {
+ symbol *sym = OP_SYMBOL (IC_RESULT (ic));
+ if (sym->remat && !POINTER_SET (ic))
+ return 1;
+ }
+
+ return 0;
+}
+
+extern set *publics;
+
+/*-----------------------------------------------------------------*/
+/* genFunction - generated code for function entry */
+/*-----------------------------------------------------------------*/
+static void
+genFunction (iCode * ic)
+{
+ bool stackParm;
+
+ symbol *sym = OP_SYMBOL (IC_LEFT (ic));
+ sym_link *ftype;
+
+ bool bcInUse = FALSE;
+ bool deInUse = FALSE;
+
+ setArea (IFFUNC_NONBANKED (sym->type));
+
+ /* PENDING: Reset the receive offset as it
+ doesn't seem to get reset anywhere else.
+ */
+ _G.receiveOffset = 0;
+
+ /* Record the last function name for debugging. */
+ _G.lastFunctionName = sym->rname;
+
+ /* Create the function header */
+ emit2 ("!functionheader", sym->name);
+ if (!IS_STATIC(sym->etype))
+ {
+ sprintf (buffer, "%s_start", sym->rname);
+ emit2 ("!labeldef", buffer);
+ }
+ emit2 ("!functionlabeldef", sym->rname);
+
+ ftype = operandType (IC_LEFT (ic));
+
+ if (IFFUNC_ISNAKED(ftype))
+ {
+ emitDebug("; naked function: no prologue.");
+ return;
+ }
+
+ /* if this is an interrupt service routine
+ then save all potentially used registers. */
+ if (IFFUNC_ISISR (sym->type))
+ {
+ /* If critical function then turn interrupts off */
+ /* except when no interrupt number is given then it implies the NMI handler */
+ if (IFFUNC_ISCRITICAL (sym->type) && (FUNC_INTNO(sym->type) != INTNO_UNSPEC))
+ {
+ emit2 ("!di");
+ }
+
+ emit2 ("!pusha");
+ }
+ else
+ {
+ /* This is a non-ISR function.
+ If critical function then turn interrupts off */
+ if (IFFUNC_ISCRITICAL (sym->type))
+ {
+ if (IS_GB)
+ {
+ emit2 ("!di");
+ }
+ else
+ {
+ //get interrupt enable flag IFF2 into P/O
+ emit2 ("ld a,i");
+ emit2 ("!di");
+ //save P/O flag
+ emit2 ("push af");
+ }
+ }
+ }
+
+ if (options.profile)
+ {
+ emit2 ("!profileenter");
+ }
+
+ /* PENDING: callee-save etc */
+
+ _G.stack.param_offset = 0;
+
+ if (z80_opts.calleeSavesBC)
+ {
+ bcInUse = TRUE;
+ }
+
+ /* Detect which registers are used. */
+ if (IFFUNC_CALLEESAVES(sym->type) && sym->regsUsed)
+ {
+ int i;
+ for (i = 0; i < sym->regsUsed->size; i++)
+ {
+ if (bitVectBitValue (sym->regsUsed, i))
+ {
+ switch (i)
+ {
+ case C_IDX:
+ case B_IDX:
+ bcInUse = TRUE;
+ break;
+ case D_IDX:
+ case E_IDX:
+ if (IS_Z80) {
+ deInUse = TRUE;
+ }
+ else {
+ /* Other systems use DE as a temporary. */
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (bcInUse)
+ {
+ emit2 ("push bc");
+ _G.stack.param_offset += 2;
+ }
+
+ _G.calleeSaves.pushedBC = bcInUse;
+
+ if (deInUse)
+ {
+ emit2 ("push de");
+ _G.stack.param_offset += 2;
+ }
+
+ _G.calleeSaves.pushedDE = deInUse;
+
+ /* adjust the stack for the function */
+ _G.stack.last = sym->stack;
+
+ stackParm = FALSE;
+ for (sym = setFirstItem (istack->syms); sym;
+ sym = setNextItem (istack->syms))
+ {
+ if (sym->_isparm && !IS_REGPARM (sym->etype))
+ {
+ stackParm = TRUE;
+ break;
+ }
+ }
+ sym = OP_SYMBOL (IC_LEFT (ic));
+
+ _G.omitFramePtr = options.ommitFramePtr;
+ if (IS_Z80 && !stackParm && !sym->stack)
+ {
+ /* When the conflicts between AOP_EXSTK && AOP_HLREG are fixed, */
+ /* the above !sym->stack condition can be removed. -- EEP */
+ if (sym->stack)
+ emit2 ("!ldaspsp", -sym->stack);
+ _G.omitFramePtr = TRUE;
+ }
+ else if (sym->stack && IS_GB && sym->stack > -INT8MIN)
+ emit2 ("!enterxl", sym->stack);
+ else if (sym->stack)
+ emit2 ("!enterx", sym->stack);
+ else
+ emit2 ("!enter");
+
+ _G.stack.offset = sym->stack;
+}
+
+/*-----------------------------------------------------------------*/
+/* genEndFunction - generates epilogue for functions */
+/*-----------------------------------------------------------------*/
+static void
+genEndFunction (iCode * ic)
+{
+ symbol *sym = OP_SYMBOL (IC_LEFT (ic));
+
+ if (IFFUNC_ISNAKED(sym->type))
+ {
+ emitDebug("; naked function: no epilogue.");
+ return;
+ }
+
+ /* PENDING: calleeSave */
+ if (IS_Z80 && _G.omitFramePtr)
+ {
+ if (_G.stack.offset)
+ emit2 ("!ldaspsp", _G.stack.offset);
+ }
+ else if (_G.stack.offset && IS_GB && _G.stack.offset > INT8MAX)
+ {
+ emit2 ("!leavexl", _G.stack.offset);
+ }
+ else if (_G.stack.offset)
+ {
+ emit2 ("!leavex", _G.stack.offset);
+ }
+ else
+ {
+ emit2 ("!leave");
+ }
+
+ if (_G.calleeSaves.pushedDE)
+ {
+ emit2 ("pop de");
+ _G.calleeSaves.pushedDE = FALSE;
+ }
+
+ if (_G.calleeSaves.pushedBC)
+ {
+ emit2 ("pop bc");
+ _G.calleeSaves.pushedBC = FALSE;
+ }
+
+ if (options.profile)
+ {
+ emit2 ("!profileexit");
+ }
+
+ /* if this is an interrupt service routine
+ then save all potentially used registers. */
+ if (IFFUNC_ISISR (sym->type))
+ {
+ emit2 ("!popa");
+
+ /* If critical function then turn interrupts back on */
+ /* except when no interrupt number is given then it implies the NMI handler */
+ if (IFFUNC_ISCRITICAL (sym->type) && (FUNC_INTNO(sym->type) != INTNO_UNSPEC))
+ {
+ emit2 ("!ei");
+ }
+ }
+ else
+ {
+ /* This is a non-ISR function.
+ If critical function then turn interrupts back on */
+ if (IFFUNC_ISCRITICAL (sym->type))
+ {
+ if (IS_GB)
+ {
+ emit2 ("!ei");
+ }
+ else
+ {
+ symbol *tlbl = newiTempLabel (NULL);
+ //restore P/O flag
+ emit2 ("pop af");
+ //parity odd <==> P/O=0 <==> interrupt enable flag IFF2 was 0 <==>
+ //don't enable interrupts as they were off before
+ emit2 ("jp po,!tlabel", tlbl->key + 100);
+ emit2 ("!ei");
+ emit2 ("!tlabeldef", (tlbl->key + 100));
+ }
+ }
+ }
+
+ if (options.debug && currFunc)
+ {
+ debugFile->writeEndFunction (currFunc, ic, 1);
+ }
+
+ if (IFFUNC_ISISR (sym->type))
+ {
+ /* "critical interrupt" is used to imply NMI handler */
+ if (IS_Z80 && IFFUNC_ISCRITICAL (sym->type) && FUNC_INTNO(sym->type) == INTNO_UNSPEC)
+ emit2 ("retn");
+ else
+ emit2 ("reti");
+ }
+ else
+ {
+ /* Both banked and non-banked just ret */
+ emit2 ("ret");
+ }
+
+ if (!IS_STATIC(sym->etype))
+ {
+ sprintf (buffer, "%s_end", sym->rname);
+ emit2 ("!labeldef", buffer);
+ }
+
+ _G.flushStatics = 1;
+ _G.stack.pushed = 0;
+ _G.stack.offset = 0;
+}
+
+/*-----------------------------------------------------------------*/
+/* genRet - generate code for return statement */
+/*-----------------------------------------------------------------*/
+static void
+genRet (iCode * ic)
+{
+ const char *l;
+ /* Errk. This is a hack until I can figure out how
+ to cause dehl to spill on a call */
+ int size, offset = 0;
+
+ /* if we have no return value then
+ just generate the "ret" */
+ if (!IC_LEFT (ic))
+ goto jumpret;
+
+ /* we have something to return then
+ move the return value into place */
+ aopOp (IC_LEFT (ic), ic, FALSE, FALSE);
+ size = AOP_SIZE (IC_LEFT (ic));
+
+ aopDump("IC_LEFT", AOP(IC_LEFT(ic)));
+
+ #if 0
+ if ((size == 2) && ((l = aopGetWord (AOP (IC_LEFT (ic)), 0))))
+ {
+ if (IS_GB)
+ {
+ emit2 ("ld de,%s", l);
+ }
+ else
+ {
+ emit2 ("ld hl,%s", l);
+ }
+ }
+ #endif
+ if (size==2)
+ {
+ fetchPair(IS_GB ? PAIR_DE : PAIR_HL, AOP (IC_LEFT (ic)));
+ }
+ else
+ {
+ if (IS_GB && size == 4 && requiresHL (AOP (IC_LEFT (ic))))
+ {
+ fetchPair (PAIR_DE, AOP (IC_LEFT (ic)));
+ fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), ic, 2);
+ }
+ else
+ {
+ while (size--)
+ {
+ l = aopGet (AOP (IC_LEFT (ic)), offset,
+ FALSE);
+ if (strcmp (_fReturn[offset], l))
+ emit2 ("ld %s,%s", _fReturn[offset], l);
+ offset++;
+ }
+ }
+ }
+ freeAsmop (IC_LEFT (ic), NULL, ic);
+
+jumpret:
+ /* generate a jump to the return label
+ if the next is not the return statement */
+ if (!(ic->next && ic->next->op == LABEL &&
+ IC_LABEL (ic->next) == returnLabel))
+
+ emit2 ("jp !tlabel", returnLabel->key + 100);
+}
+
+/*-----------------------------------------------------------------*/
+/* genLabel - generates a label */
+/*-----------------------------------------------------------------*/
+static void
+genLabel (iCode * ic)
+{
+ /* special case never generate */
+ if (IC_LABEL (ic) == entryLabel)
+ return;
+
+ emitLabel (IC_LABEL (ic)->key + 100);
+}
+
+/*-----------------------------------------------------------------*/
+/* genGoto - generates a ljmp */
+/*-----------------------------------------------------------------*/
+static void
+genGoto (iCode * ic)
+{
+ emit2 ("jp !tlabel", IC_LABEL (ic)->key + 100);
+}
+
+/*-----------------------------------------------------------------*/
+/* genPlusIncr :- does addition with increment if possible */
+/*-----------------------------------------------------------------*/
+static bool
+genPlusIncr (iCode * ic)
+{
+ unsigned int icount;
+ unsigned int size = getDataSize (IC_RESULT (ic));
+ PAIR_ID resultId = getPairId (AOP (IC_RESULT (ic)));
+
+ /* will try to generate an increment */
+ /* if the right side is not a literal
+ we cannot */
+ if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT)
+ return FALSE;
+
+ emitDebug ("; genPlusIncr");
+
+ icount = (unsigned int) floatFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit);
+
+ /* If result is a pair */
+ if (resultId != PAIR_INVALID)
+ {
+ if (isLitWord (AOP (IC_LEFT (ic))))
+ {
+ fetchLitPair (getPairId (AOP (IC_RESULT (ic))), AOP (IC_LEFT (ic)), icount);
+ return TRUE;
+ }
+ if (isPair (AOP (IC_LEFT (ic))) && resultId == PAIR_HL && icount > 2)
+ {
+ if (getPairId (AOP (IC_LEFT (ic))) == PAIR_HL)
+ {
+ PAIR_ID freep = getFreePairId (ic);
+ if (freep != PAIR_INVALID)
+ {
+ fetchPair (freep, AOP (IC_RIGHT (ic)));
+ emit2 ("add hl,%s", _pairs[freep].name);
+ return TRUE;
+ }
+ }
+ else
+ {
+ fetchPair (resultId, AOP (IC_RIGHT (ic)));
+ emit2 ("add hl,%s", getPairName (AOP (IC_LEFT (ic))));
+ return TRUE;
+ }
+ }
+ if (icount > 5)
+ return FALSE;
+ /* Inc a pair */
+ if (!sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))))
+ {
+ if (icount > 2)
+ return FALSE;
+ movLeft2ResultLong (IC_LEFT (ic), 0, IC_RESULT (ic), 0, 0, 2);
+ }
+ while (icount--)
+ {
+ emit2 ("inc %s", getPairName (AOP (IC_RESULT (ic))));
+ }
+ return TRUE;
+ }
+
+ if (IS_Z80 && isLitWord (AOP (IC_LEFT (ic))) && size == 2)
+ {
+ fetchLitPair (PAIR_HL, AOP (IC_LEFT (ic)), icount);
+ commitPair (AOP (IC_RESULT (ic)), PAIR_HL);
+ return TRUE;
+ }
+
+ /* if the literal value of the right hand side
+ is greater than 4 then it is not worth it */
+ if (icount > 4)
+ return FALSE;
+
+ /* if increment 16 bits in register */
+ if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) &&
+ size > 1 &&
+ icount == 1
+ )
+ {
+ int offset = 0;
+ symbol *tlbl = NULL;
+ tlbl = newiTempLabel (NULL);
+ while (size--)
+ {
+ emit2 ("inc %s", aopGet (AOP (IC_RESULT (ic)), offset++, FALSE));
+ if (size)
+ {
+ emit2 ("!shortjp nz,!tlabel", tlbl->key + 100);
+ }
+ }
+ emitLabel (tlbl->key + 100);
+ return TRUE;
+ }
+
+ /* if the sizes are greater than 1 then we cannot */
+ if (AOP_SIZE (IC_RESULT (ic)) > 1 ||
+ AOP_SIZE (IC_LEFT (ic)) > 1)
+ return FALSE;
+
+ /* If the result is in a register then we can load then increment.
+ */
+ if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG)
+ {
+ aopPut (AOP (IC_RESULT (ic)), aopGet (AOP (IC_LEFT (ic)), LSB, FALSE), LSB);
+ while (icount--)
+ {
+ emit2 ("inc %s", aopGet (AOP (IC_RESULT (ic)), LSB, FALSE));
+ }
+ return TRUE;
+ }
+
+ /* we can if the aops of the left & result match or
+ if they are in registers and the registers are the
+ same */
+ if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))))
+ {
+ while (icount--)
+ {
+ emit2 ("inc %s", aopGet (AOP (IC_LEFT (ic)), 0, FALSE));
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*-----------------------------------------------------------------*/
+/* outBitAcc - output a bit in acc */
+/*-----------------------------------------------------------------*/
+void
+outBitAcc (operand * result)
+{
+ symbol *tlbl = newiTempLabel (NULL);
+ /* if the result is a bit */
+ if (AOP_TYPE (result) == AOP_CRY)
+ {
+ wassertl (0, "Tried to write A into a bit");
+ }
+ else
+ {
+ emit2 ("!shortjp z,!tlabel", tlbl->key + 100);
+ emit2 ("ld a,!one");
+ emitLabel (tlbl->key + 100);
+ outAcc (result);
+ }
+}
+
+bool
+couldDestroyCarry (asmop *aop)
+{
+ if (aop)
+ {
+ if (aop->type == AOP_EXSTK || aop->type == AOP_IY)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static void
+shiftIntoPair (int idx, asmop *aop)
+{
+ PAIR_ID id = PAIR_INVALID;
+
+ wassertl (IS_Z80, "Only implemented for the Z80");
+ // wassertl (aop->type == AOP_EXSTK, "Only implemented for EXSTK");
+
+ switch (idx)
+ {
+ case 0:
+ id = PAIR_HL;
+ break;
+ case 1:
+ id = PAIR_DE;
+ _push (PAIR_DE);
+ break;
+ default:
+ wassertl (0, "Internal error - hit default case");
+ }
+
+ emitDebug ("; Shift into pair idx %u", idx);
+
+ if (id == PAIR_HL)
+ {
+ setupPair (PAIR_HL, aop, 0);
+ }
+ else
+ {
+ setupPair (PAIR_IY, aop, 0);
+ emit2 ("push iy");
+ emit2 ("pop %s", _pairs[id].name);
+ }
+
+ aop->type = AOP_PAIRPTR;
+ aop->aopu.aop_pairId = id;
+ _G.pairs[id].offset = 0;
+ _G.pairs[id].last_type = aop->type;
+}
+
+static void
+setupToPreserveCarry (asmop *result, asmop *left, asmop *right)
+{
+ wassert (left && right);
+
+ if (IS_Z80)
+ {
+ if (couldDestroyCarry (right) && couldDestroyCarry (result))
+ {
+ shiftIntoPair (0, right);
+ /* check result again, in case right == result */
+ if (couldDestroyCarry (result))
+ shiftIntoPair (1, result);
+ }
+ else if (couldDestroyCarry (right))
+ {
+ if (getPairId (result) == PAIR_HL)
+ _G.preserveCarry = TRUE;
+ else
+ shiftIntoPair (0, right);
+ }
+ else if (couldDestroyCarry (result))
+ {
+ shiftIntoPair (0, result);
+ }
+ else
+ {
+ /* Fine */
+ }
+ }
+}
+
+/*-----------------------------------------------------------------*/
+/* genPlus - generates code for addition */
+/*-----------------------------------------------------------------*/
+static void
+genPlus (iCode * ic)
+{
+ int size, offset = 0;
+
+ /* special cases :- */
+
+ aopOp (IC_LEFT (ic), ic, FALSE, FALSE);
+ aopOp (IC_RIGHT (ic), ic, FALSE, FALSE);
+ aopOp (IC_RESULT (ic), ic, TRUE, FALSE);
+
+ /* Swap the left and right operands if:
+
+ if literal, literal on the right or
+ if left requires ACC or right is already
+ in ACC */
+
+ if ((AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) ||
+ (AOP_NEEDSACC (IC_RIGHT (ic))) ||
+ AOP_TYPE (IC_RIGHT (ic)) == AOP_ACC)
+ {
+ operand *t = IC_RIGHT (ic);
+ IC_RIGHT (ic) = IC_LEFT (ic);
+ IC_LEFT (ic) = t;
+ }
+
+ /* if both left & right are in bit
+ space */
+ if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY &&
+ AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY)
+ {
+ /* Cant happen */
+ wassertl (0, "Tried to add two bits");
+ }
+
+ /* if left in bit space & right literal */
+ if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY &&
+ AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT)
+ {
+ /* Can happen I guess */
+ wassertl (0, "Tried to add a bit to a literal");
+ }
+
+ /* if I can do an increment instead
+ of add then GOOD for ME */
+ if (genPlusIncr (ic) == TRUE)
+ goto release;
+
+ emitDebug ("; Can't optimise plus by inc, falling back to the normal way");
+
+ size = getDataSize (IC_RESULT (ic));
+
+ /* Special case when left and right are constant */
+ 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 (AOP_TYPE(IC_LEFT(ic)) == AOP_LIT && AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT &&
+ left && right)
+ {
+ /* It's a pair */
+ /* PENDING: fix */
+ char buffer[100];
+ sprintf (buffer, "#(%s + %s)", left, right);
+ emit2 ("ld %s,%s", getPairName (AOP (IC_RESULT (ic))), buffer);
+ goto release;
+ }
+ }
+
+ if ((isPair (AOP (IC_RIGHT (ic))) || isPair (AOP (IC_LEFT (ic)))) && getPairId (AOP (IC_RESULT (ic))) == PAIR_HL)
+ {
+ /* Fetch into HL then do the add */
+ PAIR_ID left = getPairId (AOP (IC_LEFT (ic)));
+ PAIR_ID right = getPairId (AOP (IC_RIGHT (ic)));
+
+ spillPair (PAIR_HL);
+
+ if (left == PAIR_HL && right != PAIR_INVALID)
+ {
+ emit2 ("add hl,%s", _pairs[right].name);
+ goto release;
+ }
+ else if (right == PAIR_HL && left != PAIR_INVALID)
+ {
+ emit2 ("add hl,%s", _pairs[left].name);
+ goto release;
+ }
+ else if (right != PAIR_INVALID && right != PAIR_HL)
+ {
+ fetchPair (PAIR_HL, AOP (IC_LEFT (ic)));
+ emit2 ("add hl,%s", getPairName (AOP (IC_RIGHT (ic))));
+ goto release;
+ }
+ else if (left != PAIR_INVALID && left != PAIR_HL)
+ {
+ fetchPair (PAIR_HL, AOP (IC_RIGHT (ic)));
+ emit2 ("add hl,%s", getPairName (AOP (IC_LEFT (ic))));
+ goto release;
+ }
+ else
+ {
+ /* Can't do it */
+ }
+ }
+
+ if (isPair (AOP (IC_RIGHT (ic))) && AOP_TYPE (IC_LEFT (ic)) == AOP_IMMD && getPairId (AOP (IC_RIGHT (ic))) != PAIR_HL)
+ {
+ fetchPair (PAIR_HL, AOP (IC_LEFT (ic)));
+ emit2 ("add hl,%s", getPairName (AOP (IC_RIGHT (ic))));
+ spillCached();
+ commitPair ( AOP (IC_RESULT (ic)), PAIR_HL);
+ goto release;
+ }
+
+ /* Special case:
+ ld hl,sp+n trashes C so we cant afford to do it during an
+ add with stack based varibles. Worst case is:
+ ld hl,sp+left
+ ld a,(hl)
+ ld hl,sp+right
+ add (hl)
+ ld hl,sp+result
+ ld (hl),a
+ ld hl,sp+left+1
+ ld a,(hl)
+ ld hl,sp+right+1
+ adc (hl)
+ ld hl,sp+result+1
+ ld (hl),a
+ So you cant afford to load up hl if either left, right, or result
+ is on the stack (*sigh*) The alt is:
+ ld hl,sp+left
+ ld de,(hl)
+ ld hl,sp+right
+ ld hl,(hl)
+ add hl,de
+ ld hl,sp+result
+ ld (hl),hl
+ Combinations in here are:
+ * If left or right are in bc then the loss is small - trap later
+ * If the result is in bc then the loss is also small
+ */
+ if (IS_GB)
+ {
+ if (AOP_TYPE (IC_LEFT (ic)) == AOP_STK ||
+ AOP_TYPE (IC_RIGHT (ic)) == AOP_STK ||
+ AOP_TYPE (IC_RESULT (ic)) == AOP_STK)
+ {
+ if ((AOP_SIZE (IC_LEFT (ic)) == 2 ||
+ AOP_SIZE (IC_RIGHT (ic)) == 2) &&
+ (AOP_SIZE (IC_LEFT (ic)) <= 2 &&
+ AOP_SIZE (IC_RIGHT (ic)) <= 2))
+ {
+ if (getPairId (AOP (IC_RIGHT (ic))) == PAIR_BC)
+ {
+ /* Swap left and right */
+ operand *t = IC_RIGHT (ic);
+ IC_RIGHT (ic) = IC_LEFT (ic);
+ IC_LEFT (ic) = t;
+ }
+ if (getPairId (AOP (IC_LEFT (ic))) == PAIR_BC)
+ {
+ fetchPair (PAIR_HL, AOP (IC_RIGHT (ic)));
+ emit2 ("add hl,bc");
+ }
+ else
+ {
+ fetchPair (PAIR_DE, AOP (IC_LEFT (ic)));
+ fetchPair (PAIR_HL, AOP (IC_RIGHT (ic)));
+ emit2 ("add hl,de");
+ }
+ commitPair (AOP (IC_RESULT (ic)), PAIR_HL);
+ goto release;
+ }
+ }
+ if (size == 4)
+ {
+ /* Be paranoid on the GB with 4 byte variables due to how C
+ can be trashed by lda hl,n(sp).
+ */
+ _gbz80_emitAddSubLong (ic, TRUE);
+ goto release;
+ }
+ }
+
+ setupToPreserveCarry (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic)));
+
+ while (size--)
+ {
+ if (AOP_TYPE (IC_LEFT (ic)) == AOP_ACC)
+ {
+ _moveA (aopGet (AOP (IC_LEFT (ic)), offset, FALSE));
+ if (offset == 0)
+ emit2 ("add a,%s",
+ aopGet (AOP (IC_RIGHT (ic)), offset, FALSE));
+ else
+ emit2 ("adc a,%s",
+ aopGet (AOP (IC_RIGHT (ic)), offset, FALSE));
+ }
+ else
+ {
+ _moveA (aopGet (AOP (IC_LEFT (ic)), offset, FALSE));
+ if (offset == 0)
+ emit2 ("add a,%s",
+ aopGet (AOP (IC_RIGHT (ic)), offset, FALSE));
+ else
+ emit2 ("adc a,%s",
+ aopGet (AOP (IC_RIGHT (ic)), offset, FALSE));
+ }
+ aopPut (AOP (IC_RESULT (ic)), "a", offset++);
+ }
+
+release:
+ _G.preserveCarry = FALSE;
+ freeAsmop (IC_LEFT (ic), NULL, ic);
+ freeAsmop (IC_RIGHT (ic), NULL, ic);
+ freeAsmop (IC_RESULT (ic), NULL, ic);
+
+}
+
+/*-----------------------------------------------------------------*/
+/* genMinusDec :- does subtraction with deccrement if possible */
+/*-----------------------------------------------------------------*/
+static bool
+genMinusDec (iCode * ic)
+{
+ unsigned int icount;
+ unsigned int size = getDataSize (IC_RESULT (ic));
+
+ /* will try to generate an increment */
+ /* if the right side is not a literal we cannot */
+ if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT)
+ return FALSE;
+
+ /* if the literal value of the right hand side
+ is greater than 4 then it is not worth it */
+ if ((icount = (unsigned int) floatFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit)) > 2)
+ return FALSE;
+
+ size = getDataSize (IC_RESULT (ic));
+
+ /* if decrement 16 bits in register */
+ if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) &&
+ (size > 1) && isPair (AOP (IC_RESULT (ic))))
+ {
+ while (icount--)
+ emit2 ("dec %s", getPairName (AOP (IC_RESULT (ic))));
+ return TRUE;
+ }
+
+ /* If result is a pair */
+ if (isPair (AOP (IC_RESULT (ic))))
+ {
+ movLeft2ResultLong (IC_LEFT (ic), 0, IC_RESULT (ic), 0, 0, 2);
+ while (icount--)
+ emit2 ("dec %s", getPairName (AOP (IC_RESULT (ic))));
+ return TRUE;
+ }
+
+ /* if increment 16 bits in register */
+ if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) &&
+ (size == 2)
+ )
+ {
+ fetchPair (_getTempPairId(), AOP (IC_RESULT (ic)));
+
+ while (icount--) {
+ emit2 ("dec %s", _getTempPairName());
+ }
+
+ commitPair (AOP (IC_RESULT (ic)), _getTempPairId());
+
+ return TRUE;
+ }
+
+
+ /* if the sizes are greater than 1 then we cannot */
+ if (AOP_SIZE (IC_RESULT (ic)) > 1 ||
+ AOP_SIZE (IC_LEFT (ic)) > 1)
+ return FALSE;
+
+ /* we can if the aops of the left & result match or if they are in
+ registers and the registers are the same */
+ if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))))
+ {
+ while (icount--)
+ emit2 ("dec %s", aopGet (AOP (IC_RESULT (ic)), 0, FALSE));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*-----------------------------------------------------------------*/
+/* genMinus - generates code for subtraction */
+/*-----------------------------------------------------------------*/
+static void
+genMinus (iCode * ic)
+{
+ int size, offset = 0;
+ unsigned long lit = 0L;
+
+ aopOp (IC_LEFT (ic), ic, FALSE, FALSE);
+ aopOp (IC_RIGHT (ic), ic, FALSE, FALSE);
+ aopOp (IC_RESULT (ic), ic, TRUE, FALSE);
+
+ /* special cases :- */
+ /* if both left & right are in bit space */
+ if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY &&
+ AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY)
+ {
+ wassertl (0, "Tried to subtract two bits");
+ goto release;
+ }
+
+ /* if I can do an decrement instead of subtract then GOOD for ME */
+ if (genMinusDec (ic) == TRUE)
+ goto release;
+
+ size = getDataSize (IC_RESULT (ic));
+
+ if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT)
+ {
+ }
+ else
+ {
+ lit = (unsigned long) floatFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit);
+ lit = -(long) lit;
+ }
+
+ /* Same logic as genPlus */
+ if (IS_GB)
+ {
+ if (AOP_TYPE (IC_LEFT (ic)) == AOP_STK ||
+ AOP_TYPE (IC_RIGHT (ic)) == AOP_STK ||
+ AOP_TYPE (IC_RESULT (ic)) == AOP_STK)
+ {
+ if ((AOP_SIZE (IC_LEFT (ic)) == 2 ||
+ AOP_SIZE (IC_RIGHT (ic)) == 2) &&
+ (AOP_SIZE (IC_LEFT (ic)) <= 2 &&
+ AOP_SIZE (IC_RIGHT (ic)) <= 2))
+ {
+ PAIR_ID left = getPairId (AOP (IC_LEFT (ic)));
+ PAIR_ID right = getPairId (AOP (IC_RIGHT (ic)));
+
+ if (left == PAIR_INVALID && right == PAIR_INVALID)
+ {
+ left = PAIR_DE;
+ right = PAIR_HL;
+ }
+ else if (right == PAIR_INVALID)
+ right = PAIR_DE;
+ else if (left == PAIR_INVALID)
+ left = PAIR_DE;
+
+ fetchPair (left, AOP (IC_LEFT (ic)));
+ /* Order is important. Right may be HL */
+ fetchPair (right, AOP (IC_RIGHT (ic)));
+
+ emit2 ("ld a,%s", _pairs[left].l);
+ emit2 ("sub a,%s", _pairs[right].l);
+ emit2 ("ld e,a");
+ emit2 ("ld a,%s", _pairs[left].h);
+ emit2 ("sbc a,%s", _pairs[right].h);
+
+ if ( AOP_SIZE (IC_RESULT (ic)) > 1)
+ {
+ aopPut (AOP (IC_RESULT (ic)), "a", 1);
+ }
+ aopPut (AOP (IC_RESULT (ic)), "e", 0);
+ goto release;
+ }
+ }
+ if (size == 4)
+ {
+ /* Be paranoid on the GB with 4 byte variables due to how C
+ can be trashed by lda hl,n(sp).
+ */
+ _gbz80_emitAddSubLong (ic, FALSE);
+ goto release;
+ }
+ }
+
+ setupToPreserveCarry (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic)));
+
+ /* if literal, add a,#-lit, else normal subb */
+ while (size--)
+ {
+ _moveA (aopGet (AOP (IC_LEFT (ic)), offset, FALSE));
+ if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT)
+ {
+ if (!offset)
+ emit2 ("sub a,%s",
+ aopGet (AOP (IC_RIGHT (ic)), offset, FALSE));
+ else
+ emit2 ("sbc a,%s",
+ aopGet (AOP (IC_RIGHT (ic)), offset, FALSE));
+ }
+ else
+ {
+ /* first add without previous c */
+ if (!offset)
+ emit2 ("add a,!immedbyte", (unsigned int) (lit & 0x0FFL));
+ else
+ emit2 ("adc a,!immedbyte", (unsigned int) ((lit >> (offset * 8)) & 0x0FFL));
+ }
+ aopPut (AOP (IC_RESULT (ic)), "a", offset++);
+ }
+
+ if (AOP_SIZE (IC_RESULT (ic)) == 3 &&
+ AOP_SIZE (IC_LEFT (ic)) == 3 &&
+ !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic))))
+ {
+ wassertl (0, "Tried to subtract on a long pointer");
+ }
+
+release:
+ _G.preserveCarry = FALSE;
+ freeAsmop (IC_LEFT (ic), NULL, ic);
+ freeAsmop (IC_RIGHT (ic), NULL, ic);
+ freeAsmop (IC_RESULT (ic), NULL, ic);
+}
+
+/*-----------------------------------------------------------------*/
+/* genMult - generates code for multiplication */
+/*-----------------------------------------------------------------*/
+static void
+genMult (iCode * ic)
+{
+ int val;
+ int count, i;
+ /* If true then the final operation should be a subtract */
+ bool active = FALSE;
+ bool byteResult;
+
+ /* Shouldn't occur - all done through function calls */
+ aopOp (IC_LEFT (ic), ic, FALSE, FALSE);
+ aopOp (IC_RIGHT (ic), ic, FALSE, FALSE);
+ aopOp (IC_RESULT (ic), ic, TRUE, FALSE);
+
+ byteResult = (AOP_SIZE (IC_RESULT (ic)) == 1);
+
+ if (AOP_SIZE (IC_LEFT (ic)) > 2 ||
+ AOP_SIZE (IC_RIGHT (ic)) > 2 ||
+ AOP_SIZE (IC_RESULT (ic)) > 2)
+ {
+ wassertl (0, "Multiplication is handled through support function calls");
+ }
+
+ /* Swap left and right such that right is a literal */
+ if ((AOP_TYPE (IC_LEFT (ic)) == AOP_LIT))
+ {
+ operand *t = IC_RIGHT (ic);
+ IC_RIGHT (ic) = IC_LEFT (ic);
+ IC_LEFT (ic) = t;
+ }
+
+ wassertl (AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT, "Right must be a literal");
+
+ val = (int)floatFromVal ( AOP (IC_RIGHT (ic))->aopu.aop_lit);
+ // wassertl (val > 0, "Multiply must be positive");
+ wassertl (val != 1, "Can't multiply by 1");
+
+ if (IS_Z80 && isPairInUseNotInRet (PAIR_DE, ic)) {
+ _push (PAIR_DE);
+ _G.stack.pushedDE = TRUE;
+ }
+
+ if ( AOP_SIZE (IC_LEFT (ic)) == 1 && !SPEC_USIGN (getSpec (operandType ( IC_LEFT (ic)))))
+ {
+ emit2 ("ld e,%s", aopGet (AOP (IC_LEFT (ic)), LSB, FALSE));
+ if (!byteResult)
+ {
+ emit2 ("ld a,e");
+ emit2 ("rlc a");
+ emit2 ("sbc a,a");
+ emit2 ("ld d,a");
+ }
+ }
+ else
+ {
+ fetchPair (PAIR_DE, AOP (IC_LEFT (ic)));
+ }
+
+ i = val;
+
+ /* Fully unroled version of mul.s. Not the most efficient.
+ */
+ for (count = 0; count < 16; count++)
+ {
+ if (count != 0 && active)
+ {
+ emit2 ("add hl,hl");
+ }
+ if (i & 0x8000U)
+ {
+ if (active == FALSE)
+ {
+ emit2 ("ld l,e");
+ if (!byteResult)
+ emit2 ("ld h,d");
+ }
+ else
+ {
+ emit2 ("add hl,de");
+ }
+ active = TRUE;
+ }
+ i <<= 1;
+ }
+
+ spillCached();
+
+ if (IS_Z80 && _G.stack.pushedDE)
+ {
+ _pop (PAIR_DE);
+ _G.stack.pushedDE = FALSE;
+ }
+
+ if (byteResult)
+ aopPut (AOP (IC_RESULT (ic)), _pairs[PAIR_HL].l, 0);
+ else
+ commitPair ( AOP (IC_RESULT (ic)), PAIR_HL);
+
+ freeAsmop (IC_LEFT (ic), NULL, ic);
+ freeAsmop (IC_RIGHT (ic), NULL, ic);
+ freeAsmop (IC_RESULT (ic), NULL, ic);
+}
+
+/*-----------------------------------------------------------------*/
+/* genDiv - generates code for division */
+/*-----------------------------------------------------------------*/
+static void
+genDiv (iCode * ic)
+{
+ /* Shouldn't occur - all done through function calls */
+ wassertl (0, "Division is handled through support function calls");
+}
+
+/*-----------------------------------------------------------------*/
+/* genMod - generates code for division */
+/*-----------------------------------------------------------------*/
+static void
+genMod (iCode * ic)
+{
+ /* Shouldn't occur - all done through function calls */
+ wassert (0);
+}
+
+/*-----------------------------------------------------------------*/
+/* genIfxJump :- will create a jump depending on the ifx */
+/*-----------------------------------------------------------------*/
+static void
+genIfxJump (iCode * ic, char *jval)
+{
+ symbol *jlbl;
+ const char *inst;
+
+ /* if true label then we jump if condition
+ supplied is true */
+ if (IC_TRUE (ic))
+ {
+ jlbl = IC_TRUE (ic);
+ if (!strcmp (jval, "a"))
+ {
+ inst = "nz";
+ }
+ else if (!strcmp (jval, "c"))
+ {
+ inst = "c";
+ }
+ else if (!strcmp (jval, "nc"))
+ {
+ inst = "nc";
+ }
+ else if (!strcmp (jval, "m"))
+ {
+ inst = "m";
+ }
+ else if (!strcmp (jval, "p"))
+ {
+ inst = "p";
+ }
+ else
+ {
+ /* The buffer contains the bit on A that we should test */
+ inst = "nz";
+ }
+ }
+ else
+ {
+ /* false label is present */
+ jlbl = IC_FALSE (ic);
+ if (!strcmp (jval, "a"))
+ {
+ inst = "z";
+ }
+ else if (!strcmp (jval, "c"))
+ {
+ inst = "nc";
+ }
+ else if (!strcmp (jval, "nc"))
+ {
+ inst = "c";
+ }
+ else if (!strcmp (jval, "m"))
+ {
+ inst = "p";
+ }
+ else if (!strcmp (jval, "p"))
+ {
+ inst = "m";
+ }
+ else
+ {
+ /* The buffer contains the bit on A that we should test */
+ inst = "z";
+ }
+ }
+ /* Z80 can do a conditional long jump */
+ if (!strcmp (jval, "a"))
+ {
+ emit2 ("or a,a");
+ }
+ else if (!strcmp (jval, "c"))
+ {
+ }
+ else if (!strcmp (jval, "nc"))
+ {
+ }
+ else if (!strcmp (jval, "m"))
+ {
+ }
+ else if (!strcmp (jval, "p"))
+ {
+ }
+ else
+ {
+ emit2 ("bit %s,a", jval);
+ }
+ emit2 ("jp %s,!tlabel", inst, jlbl->key + 100);
+
+ /* mark the icode as generated */
+ ic->generated = 1;
+}
+
+#if DISABLED
+static const char *
+_getPairIdName (PAIR_ID id)
+{
+ return _pairs[id].name;
+}
+#endif
+
+#if OLD
+ /* if unsigned char cmp with lit, just compare */
+ if ((size == 1) &&
+ (AOP_TYPE (right) == AOP_LIT && AOP_TYPE (left) != AOP_DIR))
+ {
+ emit2 ("ld a,%s", aopGet (AOP (left), offset, FALSE));
+ if (sign)
+ {
+ emit2 ("xor a,!immedbyte", 0x80);
+ emit2 ("cp %s^!constbyte", aopGet (AOP (right), offset, FALSE), 0x80);
+ }
+ else
+ emit2 ("cp %s", aopGet (AOP (right), offset, FALSE));
+ }
+ else if (size == 4 && IS_GB && requiresHL(AOP(right)) && requiresHL(AOP(left)))
+ {
+ // On the Gameboy we can't afford to adjust HL as it may trash the carry.
+ // Pull left into DE and right into HL
+ aopGet (AOP(left), LSB, FALSE);
+ emit2 ("ld d,h");
+ emit2 ("ld e,l");
+ aopGet (AOP(right), LSB, FALSE);
+
+ while (size--)
+ {
+ if (size == 0 && sign)
+ {
+ // Highest byte when signed needs the bits flipped
+ // Save the flags
+ emit2 ("push af");
+ emit2 ("ld a,(de)");
+ emit2 ("xor !immedbyte", 0x80);
+ emit2 ("ld e,a");
+ emit2 ("ld a,(hl)");
+ emit2 ("xor !immedbyte", 0x80);
+ emit2 ("ld d,a");
+ emit2 ("pop af");
+ emit2 ("ld a,e");
+ emit2 ("%s a,d", offset == 0 ? "sub" : "sbc");
+ }
+ else
+ {
+ emit2 ("ld a,(de)");
+ emit2 ("%s a,(hl)", offset == 0 ? "sub" : "sbc");
+ }
+
+ if (size != 0)
+ {
+ emit2 ("inc hl");
+ emit2 ("inc de");
+ }
+ offset++;
+ }
+ spillPair (PAIR_HL);
+ }
+ else if (size == 4 && IS_Z80 && couldDestroyCarry(AOP(right)) && couldDestroyCarry(AOP(left)))
+ {
+ setupPair (PAIR_HL, AOP (left), 0);
+ aopGet (AOP(right), LSB, FALSE);
+
+ while (size--)
+ {
+ if (size == 0 && sign)
+ {
+ // Highest byte when signed needs the bits flipped
+ // Save the flags
+ emit2 ("push af");
+ emit2 ("ld a,(hl)");
+ emit2 ("xor !immedbyte", 0x80);
+ emit2 ("ld l,a");
+ emit2 ("ld a,%d(iy)", offset);
+ emit2 ("xor !immedbyte", 0x80);
+ emit2 ("ld h,a");
+ emit2 ("pop af");
+ emit2 ("ld a,l");
+ emit2 ("%s a,h", offset == 0 ? "sub" : "sbc");
+ }
+ else
+ {
+ emit2 ("ld a,(hl)");
+ emit2 ("%s a,%d(iy)", offset == 0 ? "sub" : "sbc", offset);
+ }
+
+ if (size != 0)
+ {
+ emit2 ("inc hl");
+ }
+ offset++;
+ }
+ spillPair (PAIR_HL);
+ spillPair (PAIR_IY);
+ }
+ else
+ {
+ if (AOP_TYPE (right) == AOP_LIT)
+ {
+ lit = (unsigned long) floatFromVal (AOP (right)->aopu.aop_lit);
+ /* optimize if(x < 0) or if(x >= 0) */
+ if (lit == 0L)
+ {
+ if (!sign)
+ {
+ /* No sign so it's always false */
+ _clearCarry();
+ }
+ else
+ {
+ /* Just load in the top most bit */
+ _moveA (aopGet (AOP (left), AOP_SIZE (left) - 1, FALSE));
+ if (!(AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) && ifx)
+ {
+ genIfxJump (ifx, "7");
+ return;
+ }
+ else
+ emit2 ("rlc a");
+ }
+ goto release;
+ }
+ }
+
+ if (sign)
+ {
+ /* First setup h and l contaning the top most bytes XORed */
+ bool fDidXor = FALSE;
+ if (AOP_TYPE (left) == AOP_LIT)
+ {
+ unsigned long lit = (unsigned long)
+ floatFromVal (AOP (left)->aopu.aop_lit);
+ emit2 ("ld %s,!immedbyte", _fTmp[0],
+ 0x80 ^ (unsigned int) ((lit >> ((size - 1) * 8)) & 0x0FFL));
+ }
+ else
+ {
+ emit2 ("ld a,%s", aopGet (AOP (left), size - 1, FALSE));
+ emit2 ("xor a,!immedbyte", 0x80);
+ emit2 ("ld %s,a", _fTmp[0]);
+ fDidXor = TRUE;
+ }
+ if (AOP_TYPE (right) == AOP_LIT)
+ {
+ unsigned long lit = (unsigned long)
+ floatFromVal (AOP (right)->aopu.aop_lit);
+ emit2 ("ld %s,!immedbyte", _fTmp[1],
+ 0x80 ^ (unsigned int) ((lit >> ((size - 1) * 8)) & 0x0FFL));
+ }
+ else
+ {
+ emit2 ("ld a,%s", aopGet (AOP (right), size - 1, FALSE));
+ emit2 ("xor a,!immedbyte", 0x80);
+ emit2 ("ld %s,a", _fTmp[1]);
+ fDidXor = TRUE;
+ }
+ }
+ while (size--)
+ {
+ /* Do a long subtract */
+ if (!sign || size)
+ {
+ _moveA (aopGet (AOP (left), offset, FALSE));
+ }
+ if (sign && size == 0)
+ {
+ emit2 ("ld a,%s", _fTmp[0]);
+ emit2 ("%s a,%s", offset == 0 ? "sub" : "sbc", _fTmp[1]);
+ }
+ else
+ {
+ /* Subtract through, propagating the carry */
+ emit2 ("%s a,%s", offset == 0 ? "sub" : "sbc", aopGet (AOP (right), offset, FALSE));
+ offset++;
+ }
+ }
+ }
+ }
+#endif
+
+/** Generic compare for > or <
+ */
+static void
+genCmp (operand * left, operand * right,
+ operand * result, iCode * ifx, int sign)
+{
+ int size, offset = 0;
+ unsigned long lit = 0L;
+ bool swap_sense = FALSE;
+
+ /* if left & right are bit variables */
+ if (AOP_TYPE (left) == AOP_CRY &&
+ AOP_TYPE (right) == AOP_CRY)
+ {
+ /* Cant happen on the Z80 */
+ wassertl (0, "Tried to compare two bits");
+ }
+ else
+ {
+ /* Do a long subtract of right from left. */
+ size = max (AOP_SIZE (left), AOP_SIZE (right));
+
+ if (size > 1 && IS_GB && requiresHL(AOP(right)) && requiresHL(AOP(left)))
+ {
+ // On the Gameboy we can't afford to adjust HL as it may trash the carry.
+ // Pull left into DE and right into HL
+ aopGet (AOP(left), LSB, FALSE);
+ emit2 ("ld d,h");
+ emit2 ("ld e,l");
+ aopGet (AOP(right), LSB, FALSE);
+
+ while (size--)
+ {
+ emit2 ("ld a,(de)");
+ emit2 ("%s a,(hl)", offset == 0 ? "sub" : "sbc");
+
+ if (size != 0)
+ {
+ emit2 ("inc hl");
+ emit2 ("inc de");
+ }
+ offset++;
+ }
+ spillPair (PAIR_HL);
+ goto release;
+ }
+
+ if (AOP_TYPE (right) == AOP_LIT)
+ {
+ lit = (unsigned long) floatFromVal (AOP (right)->aopu.aop_lit);
+ /* optimize if(x < 0) or if(x >= 0) */
+ if (lit == 0)
+ {
+ if (!sign)
+ {
+ /* No sign so it's always false */
+ _clearCarry();
+ }
+ else
+ {
+ /* Just load in the top most bit */
+ _moveA (aopGet (AOP (left), AOP_SIZE (left) - 1, FALSE));
+ if (!(AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) && ifx)
+ {
+ genIfxJump (ifx, "7");
+ return;
+ }
+ else
+ {
+ if (!sign)
+ {
+ emit2 ("rlc a");
+ }
+ if (ifx)
+ {
+ genIfxJump (ifx, swap_sense ? "c" : "nc");
+ return;
+ }
+ }
+ }
+ goto release;
+ }
+ }
+
+ while (size--)
+ {
+ _moveA (aopGet (AOP (left), offset, FALSE));
+ /* Subtract through, propagating the carry */
+ emit2 ("%s a,%s", offset == 0 ? "sub" : "sbc", aopGet (AOP (right), offset, FALSE));
+ offset++;
+ }
+ }
+
+release:
+ if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result))
+ {
+ if (sign)
+ {
+ /* Shift the sign bit up into carry */
+ emit2 ("rlca");
+ }
+ outBitCLong (result, swap_sense);
+ }
+ else
+ {
+ /* if the result is used in the next
+ ifx conditional branch then generate
+ code a little differently */
+ if (ifx)
+ {
+ if (sign)
+ {
+ if (IS_GB)
+ {
+ emit2 ("rlca");
+ genIfxJump (ifx, swap_sense ? "nc" : "c");
+ }
+ else
+ {
+ genIfxJump (ifx, swap_sense ? "p" : "m");
+ }
+ }
+ else
+ {
+ genIfxJump (ifx, swap_sense ? "nc" : "c");
+ }
+ }
+ else
+ {
+ if (sign)
+ {
+ /* Shift the sign bit up into carry */
+ emit2 ("rlca");
+ }
+ outBitCLong (result, swap_sense);
+ }
+ /* leave the result in acc */
+ }
+}
+
+/*-----------------------------------------------------------------*/
+/* genCmpGt :- greater than comparison */
+/*-----------------------------------------------------------------*/
+static void
+genCmpGt (iCode * ic, iCode * ifx)
+{
+ operand *left, *right, *result;
+ sym_link *letype, *retype;
+ int sign;
+
+ left = IC_LEFT (ic);
+ right = IC_RIGHT (ic);
+ result = IC_RESULT (ic);
+
+ letype = getSpec (operandType (left));
+ retype = getSpec (operandType (right));
+ sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype));
+ /* assign the amsops */
+ aopOp (left, ic, FALSE, FALSE);
+ aopOp (right, ic, FALSE, FALSE);
+ aopOp (result, ic, TRUE, FALSE);
+
+ genCmp (right, left, result, ifx, sign);
+
+ freeAsmop (left, NULL, ic);
+ freeAsmop (right, NULL, ic);
+ freeAsmop (result, NULL, ic);
+}
+
+/*-----------------------------------------------------------------*/
+/* genCmpLt - less than comparisons */
+/*-----------------------------------------------------------------*/
+static void
+genCmpLt (iCode * ic, iCode * ifx)
+{
+ operand *left, *right, *result;
+ sym_link *letype, *retype;
+ int sign;
+
+ left = IC_LEFT (ic);
+ right = IC_RIGHT (ic);
+ result = IC_RESULT (ic);
+
+ letype = getSpec (operandType (left));
+ retype = getSpec (operandType (right));
+ sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype));
+
+ /* assign the amsops */
+ aopOp (left, ic, FALSE, FALSE);
+ aopOp (right, ic, FALSE, FALSE);
+ aopOp (result, ic, TRUE, FALSE);
+
+ genCmp (left, right, result, ifx, sign);
+
+ freeAsmop (left, NULL, ic);
+ freeAsmop (right, NULL, ic);
+ freeAsmop (result, NULL, ic);
+}
+
+/*-----------------------------------------------------------------*/
+/* gencjneshort - compare and jump if not equal */
+/*-----------------------------------------------------------------*/
+static void
+gencjneshort (operand * left, operand * right, symbol * lbl)
+{
+ int size = max (AOP_SIZE (left), AOP_SIZE (right));
+ int offset = 0;
+ unsigned long lit = 0L;
+
+ /* Swap the left and right if it makes the computation easier */
+ if (AOP_TYPE (left) == AOP_LIT)
+ {
+ operand *t = right;
+ right = left;
+ left = t;
+ }
+
+ if (AOP_TYPE (right) == AOP_LIT)
+ {
+ lit = (unsigned long) floatFromVal (AOP (right)->aopu.aop_lit);
+ }
+
+ /* if the right side is a literal then anything goes */
+ if (AOP_TYPE (right) == AOP_LIT &&
+ AOP_TYPE (left) != AOP_DIR)
+ {
+ if (lit == 0)
+ {
+ emit2 ("ld a,%s", aopGet (AOP (left), offset, FALSE));
+ if (size > 1)
+ {
+ while (--size)
+ {
+ emit2 ("or a,%s", aopGet (AOP (left), ++offset, FALSE));
+ }
+ }
+ else
+ {
+ emit2 ("or a,a");
+ }
+ emit2 ("jp nz,!tlabel", lbl->key + 100);
+ }
+ else
+ {
+ while (size--)
+ {
+ emit2 ("ld a,%s", aopGet (AOP (left), offset, FALSE));
+ if ((AOP_TYPE (right) == AOP_LIT) && lit == 0)
+ emit2 ("or a,a");
+ else
+ emit2 ("cp a,%s", aopGet (AOP (right), offset, FALSE));
+ emit2 ("jp nz,!tlabel", lbl->key + 100);
+ offset++;
+ }
+ }
+ }
+ /* if the right side is in a register or in direct space or
+ if the left is a pointer register & right is not */
+ else if (AOP_TYPE (right) == AOP_REG ||
+ AOP_TYPE (right) == AOP_DIR ||
+ (AOP_TYPE (left) == AOP_DIR && AOP_TYPE (right) == AOP_LIT))
+ {
+ while (size--)
+ {
+ _moveA (aopGet (AOP (left), offset, FALSE));
+ if ((AOP_TYPE (left) == AOP_DIR && AOP_TYPE (right) == AOP_LIT) &&
+ ((unsigned int) ((lit >> (offset * 8)) & 0x0FFL) == 0))
+ /* PENDING */
+ emit2 ("jp nz,!tlabel", lbl->key + 100);
+ else
+ {
+ emit2 ("cp %s", aopGet (AOP (right), offset, FALSE));
+ emit2 ("jp nz,!tlabel", lbl->key + 100);
+ }
+ offset++;
+ }
+ }
+ else
+ {
+ /* right is a pointer reg need both a & b */
+ /* PENDING: is this required? */
+ while (size--)
+ {
+ _moveA (aopGet (AOP (right), offset, FALSE));
+ emit2 ("cp %s", aopGet (AOP (left), offset, FALSE));
+ emit2 ("!shortjp nz,!tlabel", lbl->key + 100);
+ offset++;
+ }
+ }
+}
+
+/*-----------------------------------------------------------------*/
+/* gencjne - compare and jump if not equal */
+/*-----------------------------------------------------------------*/
+static void
+gencjne (operand * left, operand * right, symbol * lbl)
+{
+ symbol *tlbl = newiTempLabel (NULL);
+
+ gencjneshort (left, right, lbl);
+
+ /* PENDING: ?? */
+ emit2 ("ld a,!one");
+ emit2 ("!shortjp !tlabel", tlbl->key + 100);
+ emitLabel (lbl->key + 100);
+ emit2 ("xor a,a");
+ emitLabel (tlbl->key + 100);
+}
+
+/*-----------------------------------------------------------------*/
+/* genCmpEq - generates code for equal to */
+/*-----------------------------------------------------------------*/
+static void
+genCmpEq (iCode * ic, iCode * ifx)
+{
+ operand *left, *right, *result;
+
+ aopOp ((left = IC_LEFT (ic)), ic, FALSE, FALSE);
+ aopOp ((right = IC_RIGHT (ic)), ic, FALSE, FALSE);
+ aopOp ((result = IC_RESULT (ic)), ic, TRUE, FALSE);
+
+ emitDebug ("; genCmpEq: left %u, right %u, result %u", AOP_SIZE(IC_LEFT(ic)), AOP_SIZE(IC_RIGHT(ic)), AOP_SIZE(IC_RESULT(ic)));
+
+ /* Swap operands if it makes the operation easier. ie if:
+ 1. Left is a literal.
+ */
+ if (AOP_TYPE (IC_LEFT (ic)) == AOP_LIT)
+ {
+ operand *t = IC_RIGHT (ic);
+ IC_RIGHT (ic) = IC_LEFT (ic);
+ IC_LEFT (ic) = t;
+ }
+
+ if (ifx && !AOP_SIZE (result))
+ {
+ symbol *tlbl;
+ /* if they are both bit variables */
+ if (AOP_TYPE (left) == AOP_CRY &&
+ ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT)))
+ {
+ wassertl (0, "Tried to compare two bits");
+ }
+ else
+ {
+ tlbl = newiTempLabel (NULL);
+ gencjneshort (left, right, tlbl);
+ if (IC_TRUE (ifx))
+ {
+ emit2 ("jp !tlabel", IC_TRUE (ifx)->key + 100);
+ emitLabel (tlbl->key + 100);
+ }
+ else
+ {
+ /* PENDING: do this better */
+ symbol *lbl = newiTempLabel (NULL);
+ emit2 ("!shortjp !tlabel", lbl->key + 100);
+ emitLabel (tlbl->key + 100);
+ emit2 ("jp !tlabel", IC_FALSE (ifx)->key + 100);
+ emitLabel (lbl->key + 100);
+ }
+ }
+ /* mark the icode as generated */
+ ifx->generated = 1;
+ goto release;
+ }
+
+ /* if they are both bit variables */
+ if (AOP_TYPE (left) == AOP_CRY &&
+ ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT)))
+ {
+ wassertl (0, "Tried to compare a bit to either a literal or another bit");
+ }
+ else
+ {
+ emitDebug(";4");
+
+ gencjne (left, right, newiTempLabel (NULL));
+ if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result))
+ {
+ wassert (0);
+ }
+ if (ifx)
+ {
+ emitDebug(";5");
+ genIfxJump (ifx, "a");
+ goto release;
+ }
+ /* if the result is used in an arithmetic operation
+ then put the result in place */
+ if (AOP_TYPE (result) != AOP_CRY)
+ {
+ emitDebug(";6");
+ outAcc (result);
+ }
+ /* leave the result in acc */
+ }
+
+release:
+ freeAsmop (left, NULL, ic);
+ freeAsmop (right, NULL, ic);
+ freeAsmop (result, NULL, ic);
+}
+
+/*-----------------------------------------------------------------*/
+/* ifxForOp - returns the icode containing the ifx for operand */
+/*-----------------------------------------------------------------*/
+static iCode *
+ifxForOp (operand * op, iCode * ic)
+{
+ /* if true symbol then needs to be assigned */
+ if (IS_TRUE_SYMOP (op))
+ return NULL;
+
+ /* if this has register type condition and
+ the next instruction is ifx with the same operand
+ and live to of the operand is upto the ifx only then */
+ if (ic->next &&
+ ic->next->op == IFX &&
+ IC_COND (ic->next)->key == op->key &&
+ OP_SYMBOL (op)->liveTo <= ic->next->seq)
+ return ic->next;
+
+ return NULL;
+}
+
+/*-----------------------------------------------------------------*/
+/* genAndOp - for && operation */
+/*-----------------------------------------------------------------*/
+static void
+genAndOp (iCode * ic)
+{
+ operand *left, *right, *result;
+ symbol *tlbl;
+
+ /* note here that && operations that are in an if statement are
+ taken away by backPatchLabels only those used in arthmetic
+ operations remain */
+ aopOp ((left = IC_LEFT (ic)), ic, FALSE, TRUE);
+ aopOp ((right = IC_RIGHT (ic)), ic, FALSE, TRUE);
+ aopOp ((result = IC_RESULT (ic)), ic, FALSE, FALSE);
+
+ /* if both are bit variables */
+ if (AOP_TYPE (left) == AOP_CRY &&
+ AOP_TYPE (right) == AOP_CRY)
+ {
+ wassertl (0, "Tried to and two bits");
+ }
+ else
+ {
+ tlbl = newiTempLabel (NULL);
+ _toBoolean (left);
+ emit2 ("!shortjp z,!tlabel", tlbl->key + 100);
+ _toBoolean (right);
+ emitLabel (tlbl->key + 100);
+ outBitAcc (result);
+ }
+
+ freeAsmop (left, NULL, ic);
+ freeAsmop (right, NULL, ic);
+ freeAsmop (result, NULL, ic);
+}
+
+/*-----------------------------------------------------------------*/
+/* genOrOp - for || operation */
+/*-----------------------------------------------------------------*/
+static void
+genOrOp (iCode * ic)
+{
+ operand *left, *right, *result;
+ symbol *tlbl;
+
+ /* note here that || operations that are in an
+ if statement are taken away by backPatchLabels
+ only those used in arthmetic operations remain */
+ aopOp ((left = IC_LEFT (ic)), ic, FALSE, TRUE);
+ aopOp ((right = IC_RIGHT (ic)), ic, FALSE, TRUE);
+ aopOp ((result = IC_RESULT (ic)), ic, FALSE, FALSE);
+
+ /* if both are bit variables */
+ if (AOP_TYPE (left) == AOP_CRY &&
+ AOP_TYPE (right) == AOP_CRY)
+ {
+ wassertl (0, "Tried to OR two bits");
+ }
+ else
+ {
+ tlbl = newiTempLabel (NULL);
+ _toBoolean (left);
+ emit2 ("!shortjp nz,!tlabel", tlbl->key + 100);
+ _toBoolean (right);
+ emitLabel (tlbl->key + 100);
+ outBitAcc (result);
+ }
+
+ freeAsmop (left, NULL, ic);
+ freeAsmop (right, NULL, ic);
+ freeAsmop (result, NULL, ic);
+}
+
+/*-----------------------------------------------------------------*/
+/* isLiteralBit - test if lit == 2^n */
+/*-----------------------------------------------------------------*/
+int
+isLiteralBit (unsigned long lit)
+{
+ unsigned long pw[32] =
+ {1L, 2L, 4L, 8L, 16L, 32L, 64L, 128L,
+ 0x100L, 0x200L, 0x400L, 0x800L,
+ 0x1000L, 0x2000L, 0x4000L, 0x8000L,
+ 0x10000L, 0x20000L, 0x40000L, 0x80000L,
+ 0x100000L, 0x200000L, 0x400000L, 0x800000L,
+ 0x1000000L, 0x2000000L, 0x4000000L, 0x8000000L,
+ 0x10000000L, 0x20000000L, 0x40000000L, 0x80000000L};
+ int idx;
+
+ for (idx = 0; idx < 32; idx++)
+ if (lit == pw[idx])
+ return idx + 1;
+ return 0;
+}
+
+/*-----------------------------------------------------------------*/
+/* jmpTrueOrFalse - */
+/*-----------------------------------------------------------------*/
+static void
+jmpTrueOrFalse (iCode * ic, symbol * tlbl)
+{
+ // ugly but optimized by peephole
+ if (IC_TRUE (ic))
+ {
+ symbol *nlbl = newiTempLabel (NULL);
+ emit2 ("jp !tlabel", nlbl->key + 100);
+ emitLabel (tlbl->key + 100);
+ emit2 ("jp !tlabel", IC_TRUE (ic)->key + 100);
+ emitLabel (nlbl->key + 100);
+ }
+ else
+ {
+ emit2 ("jp !tlabel", IC_FALSE (ic)->key + 100);
+ emitLabel (tlbl->key + 100);
+ }
+ ic->generated = 1;
+}
+
+/*-----------------------------------------------------------------*/
+/* genAnd - code for and */
+/*-----------------------------------------------------------------*/
+static void
+genAnd (iCode * ic, iCode * ifx)
+{
+ operand *left, *right, *result;
+ int size, offset = 0;
+ unsigned long lit = 0L;
+ int bytelit = 0;
+
+ aopOp ((left = IC_LEFT (ic)), ic, FALSE, FALSE);
+ aopOp ((right = IC_RIGHT (ic)), ic, FALSE, FALSE);
+ aopOp ((result = IC_RESULT (ic)), ic, TRUE, FALSE);
+
+ /* if left is a literal & right is not then exchange them */
+ if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) ||
+ (AOP_NEEDSACC (right) && !AOP_NEEDSACC (left)))
+ {
+ operand *tmp = right;
+ right = left;
+ left = tmp;
+ }
+
+ /* if result = right then exchange them */
+ if (sameRegs (AOP (result), AOP (right)))
+ {
+ operand *tmp = right;
+ right = left;
+ left = tmp;
+ }
+
+ /* if right is bit then exchange them */
+ if (AOP_TYPE (right) == AOP_CRY &&
+ AOP_TYPE (left) != AOP_CRY)
+ {
+ operand *tmp = right;
+ right = left;
+ left = tmp;
+ }
+ if (AOP_TYPE (right) == AOP_LIT)
+ lit = (unsigned long) floatFromVal (AOP (right)->aopu.aop_lit);
+
+ size = AOP_SIZE (result);
+
+ if (AOP_TYPE (left) == AOP_CRY)
+ {
+ wassertl (0, "Tried to perform an AND with a bit as an operand");
+ goto release;
+ }
+
+ // if(val & 0xZZ) - size = 0, ifx != FALSE -
+ // bit = val & 0xZZ - size = 1, ifx = FALSE -
+ if ((AOP_TYPE (right) == AOP_LIT) &&
+ (AOP_TYPE (result) == AOP_CRY) &&
+ (AOP_TYPE (left) != AOP_CRY))
+ {
+ symbol *tlbl = newiTempLabel (NULL);
+ int sizel = AOP_SIZE (left);
+ if (size)
+ {
+ /* PENDING: Test case for this. */
+ emit2 ("scf");
+ }
+ while (sizel--)
+ {
+ if ((bytelit = ((lit >> (offset * 8)) & 0x0FFL)) != 0x0L)
+ {
+ _moveA (aopGet (AOP (left), offset, FALSE));
+ if (bytelit != 0x0FFL)
+ {
+ emit2 ("and a,%s", aopGet (AOP (right), offset, FALSE));
+ }
+ else
+ {
+ /* For the flags */
+ emit2 ("or a,a");
+ }
+ emit2 ("!shortjp nz,!tlabel", tlbl->key + 100);
+ }
+ offset++;
+ }
+ // bit = left & literal
+ if (size)
+ {
+ emit2 ("clr c");
+ emit2 ("!tlabeldef", tlbl->key + 100);
+ }
+ // if(left & literal)
+ else
+ {
+ if (ifx)
+ {
+ jmpTrueOrFalse (ifx, tlbl);
+ }
+ goto release;
+ }
+ outBitC (result);
+ goto release;
+ }