+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;
+ }
+
+ /* if left is same as result */
+ if (sameRegs (AOP (result), AOP (left)))
+ {
+ for (; size--; offset++)
+ {
+ if (AOP_TYPE (right) == AOP_LIT)
+ {
+ if ((bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL)) == 0x0FF)
+ continue;
+ else
+ {
+ if (bytelit == 0)
+ aopPut (AOP (result), "!zero", offset);
+ else
+ {
+ _moveA (aopGet (AOP (left), offset, FALSE));
+ emit2 ("and a,%s",
+ aopGet (AOP (right), offset, FALSE));
+ aopPut (AOP (left), "a", offset);
+ }
+ }
+
+ }
+ else
+ {
+ if (AOP_TYPE (left) == AOP_ACC)
+ {
+ wassertl (0, "Tried to perform an AND where the left operand is allocated into A");
+ }
+ else
+ {
+ _moveA (aopGet (AOP (left), offset, FALSE));
+ emit2 ("and a,%s",
+ aopGet (AOP (right), offset, FALSE));
+ aopPut (AOP (left), "a", offset);
+ }
+ }
+ }
+ }
+ else
+ {
+ // left & result in different registers
+ if (AOP_TYPE (result) == AOP_CRY)
+ {
+ wassertl (0, "Tried to AND where the result is in carry");
+ }
+ else
+ {
+ for (; (size--); offset++)
+ {
+ // normal case
+ // result = left & right
+ if (AOP_TYPE (right) == AOP_LIT)
+ {
+ if ((bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL)) == 0x0FF)
+ {
+ aopPut (AOP (result),
+ aopGet (AOP (left), offset, FALSE),
+ offset);
+ continue;
+ }
+ else if (bytelit == 0)
+ {
+ aopPut (AOP (result), "!zero", offset);
+ continue;
+ }
+ }
+ // faster than result <- left, anl result,right
+ // and better if result is SFR
+ if (AOP_TYPE (left) == AOP_ACC)
+ emit2 ("and a,%s", aopGet (AOP (right), offset, FALSE));
+ else
+ {
+ _moveA (aopGet (AOP (left), offset, FALSE));
+ emit2 ("and a,%s",
+ aopGet (AOP (right), offset, FALSE));
+ }
+ aopPut (AOP (result), "a", offset);
+ }
+ }
+
+ }
+
+release:
+ freeAsmop (left, NULL, ic);
+ freeAsmop (right, NULL, ic);
+ freeAsmop (result, NULL, ic);
+}
+
+/*-----------------------------------------------------------------*/
+/* genOr - code for or */
+/*-----------------------------------------------------------------*/
+static void
+genOr (iCode * ic, iCode * ifx)