+ 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++;
+ }
+ }
+ }