diff options
| author | Xavier ASUS <xavi92psx@gmail.com> | 2019-10-18 00:31:54 +0200 |
|---|---|---|
| committer | Xavier ASUS <xavi92psx@gmail.com> | 2019-10-18 00:31:54 +0200 |
| commit | 268a53de823a6750d6256ee1fb1e7707b4b45740 (patch) | |
| tree | 42c1799a9a82b2f7d9790ee9fe181d72a7274751 /src/mcs51/gen.c | |
| download | sdcc-gas-268a53de823a6750d6256ee1fb1e7707b4b45740.tar.gz | |
sdcc-3.9.0 fork implementing GNU assembler syntax
This fork aims to provide better support for stm8-binutils
Diffstat (limited to 'src/mcs51/gen.c')
| -rw-r--r-- | src/mcs51/gen.c | 12371 |
1 files changed, 12371 insertions, 0 deletions
diff --git a/src/mcs51/gen.c b/src/mcs51/gen.c new file mode 100644 index 0000000..b3ebf12 --- /dev/null +++ b/src/mcs51/gen.c @@ -0,0 +1,12371 @@ +/*------------------------------------------------------------------------- + gen.c - source file for code generation for 8051 + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Bug Fixes - Wojciech Stryjewski wstryj1@tiger.lsu.edu (1999 v2.1.9a) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------*/ +/* + Notes: + 000123 mlh Moved aopLiteral to SDCCglue.c to help the split + Made everything static +*/ + +#define D(x) do if (options.verboseAsm) {x;} while(0) + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "common.h" +#include "ralloc.h" +#include "rtrack.h" +#include "gen.h" +#include "dbuf_string.h" + +char *aopLiteralGptr (const char *name, value * val); +extern int allocInfo; + +/* this is the down and dirty file with all kinds of + kludgy & hacky stuff. This is what it is all about + CODE GENERATION for a specific MCU . some of the + routines may be reusable, will have to see */ + +static char *zero = "#0x00"; +static char *one = "#0x01"; +static char *spname; + +char *fReturn8051[] = { "dpl", "dph", "b", "a", "r4", "r5", "r6", "r7" }; + +unsigned fReturnSizeMCS51 = 4; /* shared with ralloc.c */ +char **fReturn = fReturn8051; +static char *accUse[] = { "a", "b" }; + +static short rbank = -1; + +#define REG_WITH_INDEX mcs51_regWithIdx + +#define AOP(op) op->aop +#define AOP_TYPE(op) AOP(op)->type +#define AOP_SIZE(op) AOP(op)->size +#define IS_AOP_PREG(x) (AOP(x) && (AOP_TYPE(x) == AOP_R1 || \ + AOP_TYPE(x) == AOP_R0)) + +#define AOP_NEEDSACC(x) (AOP(x) && (AOP_TYPE(x) == AOP_CRY || \ + AOP_TYPE(x) == AOP_DPTR || \ + AOP(x)->paged)) + +#define AOP_INPREG(x) (x && (x->type == AOP_REG && \ + (x->aopu.aop_reg[0] == REG_WITH_INDEX(R0_IDX) || \ + x->aopu.aop_reg[0] == REG_WITH_INDEX(R1_IDX) ))) + +#define IS_AOP_IMMEDIATE(x) (AOP(x) && (AOP_TYPE(x) == AOP_LIT || \ + AOP_TYPE(x) == AOP_IMMD || \ + AOP_TYPE(x) == AOP_STR)) + +#define SP_BP(sp, bp) (options.omitFramePtr ? sp : bp) +#define SYM_BP(sym) (SPEC_OCLS (sym->etype)->paged ? SP_BP("_spx", "_bpx") : SP_BP("sp", "_bp")) + +#define EQ(a, b) (strcmp (a, b) == 0) + +#define R0INB _G.bu.bs.r0InB +#define R1INB _G.bu.bs.r1InB +#define OPINB _G.bu.bs.OpInB +#define BITSINB _G.bu.bs.bitsInB +#define BINUSE _G.bu.BInUse + +static struct +{ + short r0Pushed; + short r1Pushed; + union + { + struct + { + short r0InB: 2; //2 so we can see it overflow + short r1InB: 2; //2 so we can see it overflow + short OpInB: 2; //2 so we can see it overflow + short bitsInB: 2; //2 so we can see it overflow + } bs; + short BInUse; + } bu; + short accInUse; + struct + { + int pushed; + int pushedregs; + int offset; + int param_offset; + int xpushed; + int xpushedregs; + int xoffset; + } stack; + set *sendSet; + symbol *currentFunc; +} +_G; + +static char *rb1regs[] = +{ + "b1_0", "b1_1", "b1_2", "b1_3", "b1_4", "b1_5", "b1_6", "b1_7", + "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7" +}; + +extern struct dbuf_s *codeOutBuf; + +#define RESULTONSTACK(x) \ + (IC_RESULT (x) && IC_RESULT (x)->aop && \ + IC_RESULT (x)->aop->type == AOP_STK ) + +#define MOVA(x) mova (x) /* use function to avoid multiple eval */ +#define MOVB(x) movb (x) + +#define CLRC emitcode ("clr","c") +#define SETC emitcode ("setb","c") + +static unsigned char SLMask[] = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, + 0xE0, 0xC0, 0x80, 0x00 + }; + +static unsigned char SRMask[] = { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, + 0x07, 0x03, 0x01, 0x00 + }; + +#define MAX_REGISTER_BANKS 4 + +#define LSB 0 +#define MSB16 1 +#define MSB24 2 +#define MSB32 3 + +/*-----------------------------------------------------------------*/ +/* mcs51_emitDebuggerSymbol - associate the current code location */ +/* with a debugger symbol */ +/*-----------------------------------------------------------------*/ +void +mcs51_emitDebuggerSymbol (const char *debugSym) +{ + genLine.lineElement.isDebug = 1; + emitcode ("", "%s ==.", debugSym); + genLine.lineElement.isDebug = 0; +} + +/*-----------------------------------------------------------------*/ +/* mova - moves specified value into accumulator */ +/*-----------------------------------------------------------------*/ +static void +mova (const char *x) +{ + /* do some early peephole optimization */ + if (!strncmp (x, "a", 2) || !strncmp (x, "acc", 4)) + return; + + /* if it is a literal mov try to get it cheaper */ + if (*x == '#' && rtrackMoveALit (x)) + return; + + /* another early peephole optimization */ + if (EQ (x, "#0x00")) + { + emitcode ("clr", "a"); + return; + } + + emitcode ("mov", "a,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* movb - moves specified value into register b */ +/*-----------------------------------------------------------------*/ +static void +movb (const char *x) +{ + /* do some early peephole optimization */ + if (!strncmp (x, "b", 2)) + return; + + /* if it is a literal mov try to get it cheaper */ + if (*x == '#') + { + emitcode ("mov", "b,%s", rtrackGetLit (x)); + return; + } + + emitcode ("mov", "b,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* emitpush - push something on internal stack */ +/*-----------------------------------------------------------------*/ +static void +emitpush (const char *arg) +{ + char buf[] = "ar?"; + + _G.stack.pushed++; + if (!arg) + { + emitcode ("inc", "sp"); + return; + } + else if (EQ (arg, "a")) + { + arg = "acc"; + } + else if ((*arg == '@') || (*arg == '#')) + { + MOVA (arg); + arg = "acc"; + } + else if (EQ (arg, "r0") || EQ (arg, "r1") || EQ (arg, "r2") || EQ (arg, "r3") || + EQ (arg, "r4") || EQ (arg, "r5") || EQ (arg, "r6") || EQ (arg, "r7")) + { + buf[2] = arg[1]; + arg = buf; + } + emitcode ("push", arg); +} + +/*-----------------------------------------------------------------*/ +/* emitpop - pop something from internal stack */ +/*-----------------------------------------------------------------*/ +static void +emitpop (const char *arg) +{ + if (!arg) + emitcode ("dec", "sp"); + else + emitcode ("pop", arg); + _G.stack.pushed--; + wassertl (_G.stack.pushed >= 0, "stack underflow"); +} + +/*-----------------------------------------------------------------*/ +/* pushB - saves register B if necessary */ +/*-----------------------------------------------------------------*/ +static bool +pushB (void) +{ + bool pushedB = FALSE; + + if (BINUSE) + { + emitpush ("b"); +// printf("B was in use !\n"); + pushedB = TRUE; + } + else + { + OPINB++; + } + return pushedB; +} + +/*-----------------------------------------------------------------*/ +/* popB - restores value of register B if necessary */ +/*-----------------------------------------------------------------*/ +static void +popB (bool pushedB) +{ + if (pushedB) + { + emitpop ("b"); + } + else + { + OPINB--; + } +} + +/*-----------------------------------------------------------------*/ +/* pushReg - saves register */ +/*-----------------------------------------------------------------*/ +static bool +pushReg (int index, bool bits_pushed) +{ + const reg_info *reg = REG_WITH_INDEX (index); + if (reg->type == REG_BIT) + { + if (!bits_pushed) + emitpush (reg->base); + return TRUE; + } + else + emitpush (reg->dname); + return bits_pushed; +} + +/*-----------------------------------------------------------------*/ +/* popReg - restores register */ +/*-----------------------------------------------------------------*/ +static bool +popReg (int index, bool bits_popped) +{ + const reg_info *reg = REG_WITH_INDEX (index); + if (reg->type == REG_BIT) + { + if (!bits_popped) + emitpop (reg->base); + return TRUE; + } + else + emitpop (reg->dname); + return bits_popped; +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* showR0R1status - helper for debugging getFreePtr failures */ +/*-----------------------------------------------------------------*/ +static void +showR0R1status(iCode * ic) +{ + bool r0iu, r1iu; + bool r0ou, r1ou; + + r0iu = bitVectBitValue (ic->rUsed, R0_IDX); + r1iu = bitVectBitValue (ic->rUsed, R1_IDX); + printf ("ic->rUsed = ["); + if (r0iu) + if (r1iu) + printf("r0,r1"); + else + printf("r0"); + else + if (r1iu) + printf("r1"); + printf("] "); + + r0ou = bitVectBitValue (ic->rMask, R0_IDX); + r1ou = bitVectBitValue (ic->rMask, R1_IDX); + printf ("ic->rMask = ["); + if (r0ou) + if (r1ou) + printf("r0,r1"); + else + printf("r0"); + else + if (r1ou) + printf("r1"); + printf("]\n"); +} +#endif + +/*-----------------------------------------------------------------*/ +/* getFreePtr - returns r0 or r1 whichever is free or can be pushed */ +/*-----------------------------------------------------------------*/ +static reg_info * +getFreePtr (iCode * ic, asmop * aop, bool result) +{ + bool r0iu, r1iu; + bool r0ou, r1ou; + + /* the logic: if r0 & r1 used in the instruction + then we are in trouble otherwise */ + + /* first check if r0 & r1 are used by this + instruction, in which case we are in trouble */ + r0iu = bitVectBitValue (ic->rUsed, R0_IDX); + r1iu = bitVectBitValue (ic->rUsed, R1_IDX); + if (r0iu && r1iu) + { + goto endOfWorld; + } + + r0ou = bitVectBitValue (ic->rMask, R0_IDX); + r1ou = bitVectBitValue (ic->rMask, R1_IDX); + + /* if no usage of r0 then return it */ + if (!r0iu && !r0ou) + { + ic->rUsed = bitVectSetBit (ic->rUsed, R0_IDX); + aop->type = AOP_R0; + + return aop->aopu.aop_ptr = REG_WITH_INDEX (R0_IDX); + } + + /* if no usage of r1 then return it */ + if (!r1iu && !r1ou) + { + ic->rUsed = bitVectSetBit (ic->rUsed, R1_IDX); + aop->type = AOP_R1; + + return aop->aopu.aop_ptr = REG_WITH_INDEX (R1_IDX); + } + + /* now we know they both have usage */ + /* if r0 not used in this instruction */ + if (!r0iu) + { + /* push it if not already pushed */ + if ((ic->op == IPUSH) || (ic->op == PCALL)) + { + MOVB (REG_WITH_INDEX (R0_IDX)->dname); + R0INB++; + } + else if (!_G.r0Pushed) + { + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + _G.r0Pushed++; + } + + ic->rUsed = bitVectSetBit (ic->rUsed, R0_IDX); + aop->type = AOP_R0; + + return aop->aopu.aop_ptr = REG_WITH_INDEX (R0_IDX); + } + + /* if r1 not used then */ + + if (!r1iu) + { + /* push it if not already pushed */ + if ((ic->op == IPUSH) || (ic->op == PCALL)) + { + MOVB (REG_WITH_INDEX (R1_IDX)->dname); + R1INB++; + } + else if (!_G.r1Pushed) + { + emitpush (REG_WITH_INDEX (R1_IDX)->dname); + _G.r1Pushed++; + } + + ic->rUsed = bitVectSetBit (ic->rUsed, R1_IDX); + aop->type = AOP_R1; + return REG_WITH_INDEX (R1_IDX); + } + +endOfWorld: + /* I said end of world, but not quite end of world yet */ + /* if this is a result then we can push it on the stack */ + if (result) + { + aop->type = AOP_STK; + return NULL; + } + /* in the case that result AND left AND right needs a pointer reg + we can safely use the result's */ + if (bitVectBitValue (mcs51_rUmaskForOp (IC_RESULT (ic)), R0_IDX) && + (!OP_SYMBOL (IC_RESULT (ic)) || OP_SYMBOL (IC_RESULT (ic))->regs[getSize (operandType (IC_RESULT (ic))) - 1]->rIdx == R0_IDX)) + { + aop->type = AOP_R0; + return REG_WITH_INDEX (R0_IDX); + } + if (bitVectBitValue (mcs51_rUmaskForOp (IC_RESULT (ic)), R1_IDX) && + (!OP_SYMBOL (IC_RESULT (ic)) || OP_SYMBOL (IC_RESULT (ic))->regs[getSize (operandType (IC_RESULT (ic))) - 1]->rIdx == R1_IDX)) + { + aop->type = AOP_R1; + return REG_WITH_INDEX (R1_IDX); + } + + /* now this is REALLY the end of the world */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "getFreePtr should never reach here"); + exit (EXIT_FAILURE); +} + + +/*-----------------------------------------------------------------*/ +/* getTempRegs - initialize an array of pointers to GPR registers */ +/* that are not in use. Returns 1 if the requested */ +/* number of registers were available, 0 otherwise. */ +/*-----------------------------------------------------------------*/ +int +getTempRegs (reg_info ** tempRegs, int size, iCode * ic) +{ + bitVect *freeRegs; + int i; + int offset; + + if (!ic) + ic = genLine.lineElement.ic; + if (!ic) + return 0; + if (!_G.currentFunc) + return 0; + + freeRegs = newBitVect (8); + bitVectSetBit (freeRegs, R2_IDX); + bitVectSetBit (freeRegs, R3_IDX); + bitVectSetBit (freeRegs, R4_IDX); + bitVectSetBit (freeRegs, R5_IDX); + bitVectSetBit (freeRegs, R6_IDX); + bitVectSetBit (freeRegs, R7_IDX); + + if (IFFUNC_CALLEESAVES (_G.currentFunc->type)) + { + bitVect *newfreeRegs; + newfreeRegs = bitVectIntersect (freeRegs, _G.currentFunc->regsUsed); + freeBitVect (freeRegs); + freeRegs = newfreeRegs; + } + freeRegs = bitVectCplAnd (freeRegs, ic->rMask); + + offset = 0; + for (i = 0; i < freeRegs->size; i++) + { + if (bitVectBitValue (freeRegs, i)) + tempRegs[offset++] = REG_WITH_INDEX (i); + if (offset >= size) + { + freeBitVect (freeRegs); + return 1; + } + } + + freeBitVect (freeRegs); + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = Safe_calloc (1, sizeof (asmop)); + aop->type = type; + aop->allocated = 1; + return aop; +} + +/*-----------------------------------------------------------------*/ +/* pointerCode - returns the code for a pointer type */ +/*-----------------------------------------------------------------*/ +static int +pointerCode (sym_link * etype) +{ + return PTR_TYPE (SPEC_OCLS (etype)); +} + +/*-----------------------------------------------------------------*/ +/* leftRightUseAcc - returns size of accumulator use by operands */ +/*-----------------------------------------------------------------*/ +static int +leftRightUseAcc (iCode * ic) +{ + operand *op; + int size; + int accuseSize = 0; + int accuse = 0; + + if (!ic) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "null iCode pointer"); + return 0; + } + + if (ic->op == IFX) + { + op = IC_COND (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + else if (ic->op == JUMPTABLE) + { + op = IC_JTCOND (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + else + { + op = IC_LEFT (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + op = IC_RIGHT (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + + if (accuseSize) + return accuseSize; + else + return accuse; +} + +/*-----------------------------------------------------------------*/ +/* stackoffset - stack offset for symbol */ +/*-----------------------------------------------------------------*/ +static int +stackoffset (symbol * sym) +{ + int offset = sym->stack; + if (options.omitFramePtr) + { + if (SPEC_OCLS (sym->etype)->paged) + offset -= _G.stack.xoffset + _G.stack.xpushed; + else + offset -= _G.stack.offset + _G.stack.pushed; + } + if (sym->stack < 0) + offset -= _G.stack.param_offset; + return offset; +} + +/*-----------------------------------------------------------------*/ +/* aopPtrForSym - pointer for symbol */ +/*-----------------------------------------------------------------*/ +static void +aopPtrForSym (symbol * sym, bool accuse, int offset, asmop * aop, iCode * ic) +{ + char *base; + struct dbuf_s tmpBuf; + dbuf_init (&tmpBuf, 1024); + if (sym->onStack) + { + dbuf_printf (&tmpBuf, "%s", SYM_BP (sym)); + } + else + { + dbuf_printf (&tmpBuf, "#%s", sym->rname); + } + base = dbuf_detach_c_str (&tmpBuf); + + offset += stackoffset (sym); + + if (abs (offset) >= 248) + werrorfl (ic->filename, ic->lineno, W_LIT_OVERFLOW); + + if ((abs (offset) < 3) || (accuse && (abs (offset) < 4))) + { + emitcode ("mov", "%s,%s", aop->aopu.aop_ptr->name, base); + while (offset < 0) + { + emitcode ("dec", aop->aopu.aop_ptr->name); + offset++; + } + while (offset > 0) + { + emitcode ("inc", aop->aopu.aop_ptr->name); + offset--; + } + } + else + { + if (accuse) + { + emitcode ("xch", "a,%s", aop->aopu.aop_ptr->name); + emitcode ("mov", "a,%s", base); + emitcode ("add", "a,#0x%02x", offset & 0xff); + emitcode ("xch", "a,%s", aop->aopu.aop_ptr->name); + } + else + { + emitcode ("mov", "a,%s", base); + emitcode ("add", "a,#0x%02x", offset & 0xff); + emitcode ("mov", "%s,a", aop->aopu.aop_ptr->name); + } + } + aop->paged = SPEC_OCLS (sym->etype)->paged; + dbuf_free (base); +} + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (iCode * ic, symbol * sym, bool result) +{ + asmop *aop; + memmap *space; + bool accuse = leftRightUseAcc (ic) || _G.accInUse; + + wassertl (ic != NULL, "Got a null iCode"); + wassertl (sym != NULL, "Got a null symbol"); + + space = SPEC_OCLS (sym->etype); + + /* if already has one */ + if (sym->aop) + { + sym->aop->allocated++; + return sym->aop; + } + + /* assign depending on the storage class */ + /* if it is on the stack or indirectly addressable */ + /* space we need to assign either r0 or r1 to it */ + if (sym->onStack || sym->iaccess) + { + sym->aop = aop = newAsmop (0); + aop->aopu.aop_ptr = getFreePtr (ic, aop, result); + aop->size = getSize (sym->type); + + /* now assign the address of the variable to + the pointer register */ + if (aop->type != AOP_STK) + { + aopPtrForSym (sym, accuse, 0, aop, ic); + } + else + { + aop->aopu.aop_sym = sym; + } + return aop; + } + + /* if in bit space */ + if (IN_BITSPACE (space)) + { + sym->aop = aop = newAsmop (AOP_CRY); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; + } + /* if it is in direct space */ + if (IN_DIRSPACE (space)) + { + //printf("aopForSym, using AOP_DIR for %s (%x)\n", sym->name, sym); + //printTypeChainRaw(sym->type, NULL); + //printf("space = %s\n", space ? space->sname : "NULL"); + sym->aop = aop = newAsmop (AOP_DIR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; + } + + /* special case for a function */ + if (IS_FUNC (sym->type)) + { + sym->aop = aop = newAsmop (AOP_IMMD); + aop->aopu.aop_immd.aop_immd1 = Safe_strdup (sym->rname); + aop->size = getSize (sym->type); + return aop; + } + + /* only remaining is far space */ + /* in which case DPTR gets the address */ + sym->aop = aop = newAsmop (AOP_DPTR); + + rtrackLoadDptrWithSym (sym->rname); + + aop->size = getSize (sym->type); + + /* if it is in code space */ + if (IN_CODESPACE (space)) + aop->code = 1; + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerializes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (symbol * sym) +{ + iCode *ic = sym->rematiCode; + asmop *aop = newAsmop (AOP_IMMD); + int ptr_type = 0; + int val = 0; + sym_link *from_type = NULL; + const char *from_name = NULL; + struct dbuf_s dbuf; + + for (;;) + { + if (ic->op == '+') + { + val += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else if (ic->op == '-') + { + val -= (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else if (IS_CAST_ICODE (ic)) + { + from_type = operandType (IC_RIGHT (ic)); + from_name = IS_SYMOP (IC_RIGHT (ic)) ? OP_SYMBOL (IC_RIGHT (ic))->name : NULL; + aop->aopu.aop_immd.from_cast_remat = 1; + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + else + { + break; + } + } + + dbuf_init (&dbuf, 128); + if (val) + { + dbuf_printf (&dbuf, "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic))->rname, val >= 0 ? '+' : '-', abs (val) & 0xffff); + } + else + { + dbuf_append_str (&dbuf, OP_SYMBOL (IC_LEFT (ic))->rname); + } + + aop->aopu.aop_immd.aop_immd1 = dbuf_detach_c_str (&dbuf); + /* set immd2 field if required */ + if (aop->aopu.aop_immd.from_cast_remat) + { + ptr_type = pointerTypeToGPByte (DCL_TYPE (from_type), from_name, sym->name); + dbuf_init (&dbuf, 128); + dbuf_tprintf (&dbuf, "#!constbyte", ptr_type); + aop->aopu.aop_immd.aop_immd2 = dbuf_detach_c_str (&dbuf); + } + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* regsInCommon - two operands have some registers in common */ +/*-----------------------------------------------------------------*/ +static bool +regsInCommon (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + int i; + + /* if they have registers in common */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + if (sym1->nRegs == 0 || sym2->nRegs == 0) + return FALSE; + + for (i = 0; i < sym1->nRegs; i++) + { + int j; + if (!sym1->regs[i]) + continue; + + for (j = 0; j < sym2->nRegs; j++) + { + if (!sym2->regs[j]) + continue; + + if (sym2->regs[j] == sym1->regs[i]) + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandsEqu - equivalent */ +/*-----------------------------------------------------------------*/ +static bool +operandsEqu (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + + /* if they're not symbols */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + /* if both are itemps & one is spilt + and the other is not then false */ + if (IS_ITEMP (op1) && IS_ITEMP (op2) && sym1->isspilt != sym2->isspilt) + return FALSE; + + /* if they are the same */ + if (sym1 == sym2) + return TRUE; + + /* if they have the same rname */ + if (sym1->rname[0] && sym2->rname[0] && EQ (sym1->rname, sym2->rname) && !(IS_PARM (op2) && IS_ITEMP (op1))) + return TRUE; + + /* if left is a tmp & right is not */ + if (IS_ITEMP (op1) && !IS_ITEMP (op2) && sym1->isspilt && (sym1->usl.spillLoc == sym2)) + return TRUE; + + if (IS_ITEMP (op2) && !IS_ITEMP (op1) && sym2->isspilt && sym1->level > 0 && (sym2->usl.spillLoc == sym1)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* sameByte - two asmops have the same address at given offsets */ +/*-----------------------------------------------------------------*/ +static bool +sameByte (asmop * aop1, int off1, asmop * aop2, int off2) +{ + if (aop1 == aop2 && off1 == off2) + return TRUE; + + if (aop1->type != AOP_REG && aop1->type != AOP_CRY) + return FALSE; + + if (aop1->type != aop2->type) + return FALSE; + + if (aop1->aopu.aop_reg[off1] != aop2->aopu.aop_reg[off2]) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* sameRegs - two asmops have the same registers */ +/*-----------------------------------------------------------------*/ +static bool +sameRegs (asmop * aop1, asmop * aop2) +{ + int i; + + if (aop1 == aop2) + return TRUE; + + if (aop1->type != AOP_REG && aop1->type != AOP_CRY) + return FALSE; + + if (aop1->type != aop2->type) + return FALSE; + + if (aop1->size != aop2->size) + return FALSE; + + for (i = 0; i < aop1->size; i++) + if (aop1->aopu.aop_reg[i] != aop2->aopu.aop_reg[i]) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +static void +aopOp (operand * op, iCode * ic, bool result) +{ + asmop *aop; + symbol *sym; + int i; + + if (!op) + return; + + /* if this a literal */ + if (IS_OP_LITERAL (op)) + { + op->aop = aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + return; + } + + /* if already has a asmop then continue */ + if (op->aop) + { + op->aop->allocated++; + return; + } + + /* if the underlying symbol has a aop */ + if (IS_SYMOP (op) && OP_SYMBOL (op)->aop) + { + op->aop = OP_SYMBOL (op)->aop; + op->aop->allocated++; + return; + } + + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) + { + op->aop = aopForSym (ic, OP_SYMBOL (op), result); + return; + } + + /* this is a temporary : this has + only five choices : + a) register + b) spillocation + c) rematerialize + d) conditional + e) can be a return use only */ + + sym = OP_SYMBOL (op); + + /* if the type is a conditional */ + if (sym->regType == REG_CND) + { + sym->aop = op->aop = aop = newAsmop (AOP_CRY); + aop->size = sym->ruonly ? 1 : 0; + return; + } + + /* if it is spilt then two situations + a) is rematerialize + b) has a spill location */ + if (sym->isspilt || sym->nRegs == 0) + { + /* rematerialize it NOW */ + if (sym->remat) + { + sym->aop = op->aop = aop = aopForRemat (sym); + aop->size = operandSize (op); + return; + } + + if (sym->accuse) + { + int i; + sym->aop = op->aop = aop = newAsmop (AOP_ACC); + aop->size = getSize (sym->type); + for (i = 0; i < 2; i++) + aop->aopu.aop_str[i] = accUse[i]; + return; + } + + if (sym->ruonly) + { + unsigned i; + + sym->aop = op->aop = aop = newAsmop (AOP_STR); + aop->size = getSize (sym->type); + for (i = 0; i < fReturnSizeMCS51; i++) + aop->aopu.aop_str[i] = fReturn[i]; + return; + } + + if (sym->isspilt && sym->usl.spillLoc) + { + asmop *oldAsmOp = NULL; + + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* force a new aop if sizes differ */ + oldAsmOp = sym->usl.spillLoc->aop; + sym->usl.spillLoc->aop = NULL; + } + sym->aop = op->aop = aop = aopForSym (ic, sym->usl.spillLoc, result); + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* Don't reuse the new aop, go with the last one */ + sym->usl.spillLoc->aop = oldAsmOp; + } + aop->size = getSize (sym->type); + return; + } + + /* else must be a dummy iTemp */ + sym->aop = op->aop = aop = newAsmop (AOP_DUMMY); + aop->size = getSize (sym->type); + return; + } + + /* if the type is a bit register */ + if (sym->regType == REG_BIT && sym->regs[0]->type == REG_BIT) + { + sym->aop = op->aop = aop = newAsmop (AOP_CRY); + aop->size = sym->nRegs; //1??? + aop->aopu.aop_reg[0] = sym->regs[0]; + aop->aopu.aop_dir = sym->regs[0]->name; + return; + } + + /* must be in a register */ + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = sym->nRegs; + for (i = 0; i < sym->nRegs; i++) + aop->aopu.aop_reg[i] = sym->regs[i]; +} + +/*-----------------------------------------------------------------*/ +/* freeAsmop - free up the asmop given to an operand */ +/*-----------------------------------------------------------------*/ +static void +freeAsmop (operand * op, asmop * aaop, iCode * ic, bool pop) +{ + asmop *aop; + int sz; + symbol *sym; + + if (!op) + aop = aaop; + else + aop = op->aop; + + if (!aop) + return; + + aop->allocated--; + + if (aop->allocated) + goto dealloc; + + /* depending on the asmop type only three cases need work + AOP_R0, AOP_R1 & AOP_STK */ + switch (aop->type) + { + case AOP_R0: + if (R0INB) + { + emitcode ("mov", "r0,b"); + R0INB--; + } + else if (_G.r0Pushed) + { + if (pop) + { + emitpop ("ar0"); + _G.r0Pushed--; + } + } + bitVectUnSetBit (ic->rUsed, R0_IDX); + break; + + case AOP_R1: + if (R1INB) + { + emitcode ("mov", "r1,b"); + R1INB--; + } + else if (_G.r1Pushed) + { + if (pop) + { + emitpop ("ar1"); + _G.r1Pushed--; + } + } + bitVectUnSetBit (ic->rUsed, R1_IDX); + break; + + case AOP_STK: + sz = aop->size; + sym = aop->aopu.aop_sym; + bitVectUnSetBit (ic->rUsed, R0_IDX); + bitVectUnSetBit (ic->rUsed, R1_IDX); + + getFreePtr (ic, aop, FALSE); + + aopPtrForSym (sym, FALSE, aop->size - 1, aop, ic); + + while (sz--) + { + emitpop ("acc"); + if (aop->paged) + emitcode ("movx", "@%s,a", aop->aopu.aop_ptr->name); + else + emitcode ("mov", "@%s,a", aop->aopu.aop_ptr->name); + if (!sz) + break; + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + } + op->aop = aop; + freeAsmop (op, NULL, ic, TRUE); + if (_G.r1Pushed) + { + emitpop ("ar1"); + _G.r1Pushed--; + } + if (_G.r0Pushed) + { + emitpop ("ar0"); + _G.r0Pushed--; + } + break; + } + +dealloc: + /* all other cases just dealloc */ + if (op) + { + op->aop = NULL; + if (IS_SYMOP (op)) + { + OP_SYMBOL (op)->aop = NULL; + /* if the symbol has a spill */ + if (SPIL_LOC (op)) + SPIL_LOC (op)->aop = NULL; + } + } +} + +/*------------------------------------------------------------------*/ +/* freeForBranchAsmop - partial free up of Asmop for a branch; just */ +/* pop r0 or r1 off stack if pushed */ +/*------------------------------------------------------------------*/ +static void +freeForBranchAsmop (operand * op, iCode * ic) +{ + asmop *aop; + + if (!op) + return; + + aop = op->aop; + + if (!aop) + return; + + if (!aop->allocated) + return; + + switch (aop->type) + { + case AOP_R0: + if (R0INB) + { + emitcode ("mov", "r0,b"); + } + else if (_G.r0Pushed) + { + emitcode ("pop", "ar0"); /* without pushed-- */ + } + break; + + case AOP_R1: + if (R1INB) + { + emitcode ("mov", "r1,b"); + } + else if (_G.r1Pushed) + { + emitcode ("pop", "ar1"); /* without pushed-- */ + } + break; + + case AOP_STK: + { + int sz = aop->size; + + emitcode ("mov", "b,r0"); + aopPtrForSym (aop->aopu.aop_sym, FALSE, 0, aop, ic); + + while (sz--) + { + emitcode ("pop", "acc"); /* without pushed-- */ + if (aop->paged) + emitcode ("movx", "@r0,a"); + else + emitcode ("mov", "@r0,a"); + if (!sz) + break; + emitcode ("dec", "r0"); + } + emitcode ("mov", "r0,b"); + } + } +} + +/*------------------------------------------------------------------*/ +/* freeForBranchAsmops - partial free up of 3 Asmops for a branch; */ +/* just pop r0 or r1 off stack if pushed */ +/*------------------------------------------------------------------*/ +static void +freeForBranchAsmops (operand * op1, operand * op2, operand * op3, iCode * ic) +{ + if (op1) + freeForBranchAsmop (op1, ic); + if (op2) + freeForBranchAsmop (op2, ic); + if (op3) + freeForBranchAsmop (op3, ic); +} + +/*-----------------------------------------------------------------*/ +/* opIsGptr: returns non-zero if the passed operand is */ +/* a generic pointer type. */ +/*-----------------------------------------------------------------*/ +static int +opIsGptr (operand * op) +{ + if (op && (AOP_SIZE (op) == GPTRSIZE) && (IS_GENPTR (operandType (op)) || IFFUNC_ISBANKEDCALL (operandType (op)))) + { + return 1; + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* swapOperands - swap two operands */ +/*-----------------------------------------------------------------*/ +static void +swapOperands (operand ** left, operand ** right) +{ + operand *t = *right; + *right = *left; + *left = t; +} + +/*-----------------------------------------------------------------*/ +/* aopGetUsesAcc - indicates ahead of time whether aopGet() will */ +/* clobber the accumulator */ +/*-----------------------------------------------------------------*/ +static bool +aopGetUsesAcc (operand * oper, int offset) +{ + asmop *aop = AOP (oper); + + if (offset > (aop->size - 1)) + return FALSE; + + switch (aop->type) + { + + case AOP_R0: + case AOP_R1: + if (aop->paged) + return TRUE; + return FALSE; + case AOP_DPTR: + return TRUE; + case AOP_IMMD: + return FALSE; + case AOP_DIR: + return FALSE; + case AOP_REG: + wassert (!EQ (aop->aopu.aop_reg[offset]->name, "a")); + return FALSE; + case AOP_CRY: + return TRUE; + case AOP_ACC: + if (offset) + return FALSE; + return TRUE; + case AOP_LIT: + return FALSE; + case AOP_STR: + if (EQ (aop->aopu.aop_str[offset], "a")) + return TRUE; + return FALSE; + case AOP_DUMMY: + return FALSE; + default: + /* Error case --- will have been caught already */ + wassert (0); + return FALSE; + } +} + +/*-------------------------------------------------------------------*/ +/* aopGet - for fetching value of the aop */ +/*-------------------------------------------------------------------*/ +/* + * NOTE: function returns a pointer to a reusable dynamically allocated + * buffer, which should never be freed! + * Subsequent call to aopGet() will rewrite the result of the previous + * call, so the content of the result should be copied to an other + * location, usually using Safe_strdup(), in order to perserve it. + */ +static const char * +aopGet (operand * oper, int offset, bool bit16, bool dname) +{ + asmop *aop = AOP (oper); + static struct dbuf_s dbuf = { 0 }; + + if (dbuf_is_initialized (&dbuf)) + { + /* reuse the dynamically allocated buffer */ + dbuf_set_length (&dbuf, 0); + } + else + { + /* first time: initialize the dynamically allocated buffer */ + dbuf_init (&dbuf, 128); + } + + /* offset is greater than + size then zero */ + if (offset > (aop->size - 1) && aop->type != AOP_LIT) + { + dbuf_append_str (&dbuf, zero); + } + else + { + /* depending on type */ + switch (aop->type) + { + case AOP_DUMMY: + dbuf_append_str (&dbuf, zero); + break; + + case AOP_R0: + case AOP_R1: + /* if we need to increment it */ + while (offset > aop->coff) + { + emitcode ("inc", "%s", aop->aopu.aop_ptr->name); + aop->coff++; + } + + while (offset < aop->coff) + { + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + aop->coff--; + } + + aop->coff = offset; + if (aop->paged) + { + emitcode ("movx", "a,@%s", aop->aopu.aop_ptr->name); + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + } + else + { + dbuf_printf (&dbuf, "@%s", aop->aopu.aop_ptr->name); + } + break; + + case AOP_DPTR: + if (aop->code && aop->coff == 0 && offset >= 1) + { + emitcode ("mov", "a,#0x%02x", offset); + emitcode ("movc", "a,@a+dptr"); + } + else + { + while (offset > aop->coff) + { + emitcode ("inc", "dptr"); + aop->coff++; + } + + while (offset < aop->coff) + { + emitcode ("lcall", "__decdptr"); + aop->coff--; + } + + aop->coff = offset; + if (aop->code) + { + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + } + else + { + emitcode ("movx", "a,@dptr"); + } + } + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + break; + + case AOP_IMMD: + if (aop->aopu.aop_immd.from_cast_remat && opIsGptr (oper) && offset == GPTRSIZE - 1) + { + dbuf_printf (&dbuf, "%s", aop->aopu.aop_immd.aop_immd2); + } + else if (bit16) + { + dbuf_printf (&dbuf, "#%s", aop->aopu.aop_immd.aop_immd1); + } + else if (offset) + { + dbuf_printf (&dbuf, "#(%s >> %d)", aop->aopu.aop_immd.aop_immd1, offset * 8); + } + else + { + dbuf_printf (&dbuf, "#%s", aop->aopu.aop_immd.aop_immd1); + } + break; + + case AOP_DIR: + if ((SPEC_SCLS (getSpec (operandType (oper))) == S_SFR) && (aop->size > 1)) + { + dbuf_printf (&dbuf, "((%s >> %d) & 0xFF)", aop->aopu.aop_dir, offset * 8); + } + else if (offset) + { + dbuf_printf (&dbuf, "(%s + %d)", aop->aopu.aop_dir, offset); + } + else + { + dbuf_printf (&dbuf, "%s", aop->aopu.aop_dir); + } + break; + + case AOP_REG: + dbuf_append_str (&dbuf, dname ? aop->aopu.aop_reg[offset]->dname : aop->aopu.aop_reg[offset]->name); + break; + + case AOP_CRY: + if (!IS_OP_RUONLY (oper)) + emitcode ("mov", "c,%s", aop->aopu.aop_dir); + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + break; + + case AOP_ACC: + dbuf_append_str (&dbuf, (!offset && dname) ? "acc" : aop->aopu.aop_str[offset]); + break; + + case AOP_LIT: + if (opIsGptr (oper) && IS_FUNCPTR (operandType (oper)) && offset == GPTRSIZE - 1) + { + dbuf_append_str (&dbuf, aopLiteralGptr (NULL, aop->aopu.aop_lit)); + } + else + { + int size = 1 + (bit16 ? 1 : 0); + dbuf_append_str (&dbuf, aopLiteralLong (aop->aopu.aop_lit, offset, size)); + } + break; + + case AOP_STR: + aop->coff = offset; + if (EQ (aop->aopu.aop_str[offset], "a") && dname) + dbuf_append_str (&dbuf, "acc"); + else + dbuf_append_str (&dbuf, aop->aopu.aop_str[offset]); + break; + + default: + dbuf_destroy (&dbuf); + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopget got unsupported aop->type"); + exit (EXIT_FAILURE); + } + } + return dbuf_c_str (&dbuf); +} + +/*-----------------------------------------------------------------*/ +/* aopPutUsesAcc - indicates ahead of time whether aopPut() will */ +/* clobber the accumulator */ +/*-----------------------------------------------------------------*/ +static bool +aopPutUsesAcc (operand * oper, const char *s, int offset) +{ + asmop *aop = AOP (oper); + + if (offset > (aop->size - 1)) + return FALSE; + + switch (aop->type) + { + case AOP_DUMMY: + return TRUE; + case AOP_DIR: + return FALSE; + case AOP_REG: + wassert (!EQ (aop->aopu.aop_reg[offset]->name, "a")); + return FALSE; + case AOP_DPTR: + return TRUE; + case AOP_R0: + case AOP_R1: + return ((aop->paged) || (*s == '@')); + case AOP_STK: + return (*s == '@'); + case AOP_CRY: + return (!aop->aopu.aop_dir || !EQ (s, aop->aopu.aop_dir)); + case AOP_STR: + return FALSE; + case AOP_IMMD: + return FALSE; + case AOP_ACC: + return FALSE; + default: + /* Error case --- will have been caught already */ + wassert (0); + return FALSE; + } +} + +/*-----------------------------------------------------------------*/ +/* aopPut - puts a string for a aop and indicates if acc is in use */ +/*-----------------------------------------------------------------*/ +static bool +aopPut (operand * result, const char *s, int offset) +{ + bool bvolatile = isOperandVolatile (result, FALSE); + bool accuse = FALSE; + asmop *aop = AOP (result); + const char *d = NULL; + static struct dbuf_s dbuf = { 0 }; + + if (dbuf_is_initialized (&dbuf)) + { + /* reuse the dynamically allocated buffer */ + dbuf_set_length (&dbuf, 0); + } + else + { + /* first time: initialize the dynamically allocated buffer */ + dbuf_init (&dbuf, 128); + } + + if (aop->size && offset > (aop->size - 1)) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got offset > aop->size"); + exit (EXIT_FAILURE); + } + + /* will assign value to value */ + /* depending on where it is ofcourse */ + switch (aop->type) + { + case AOP_DUMMY: + MOVA (s); /* read s in case it was volatile */ + accuse = TRUE; + break; + + case AOP_DIR: + if ((SPEC_SCLS (getSpec (operandType (result))) == S_SFR) && (aop->size > 1)) + { + dbuf_printf (&dbuf, "((%s >> %d) & 0xFF)", aop->aopu.aop_dir, offset * 8); + } + else if (offset) + { + dbuf_printf (&dbuf, "(%s + %d)", aop->aopu.aop_dir, offset); + } + else + { + dbuf_append_str (&dbuf, aop->aopu.aop_dir); + } + + if (!EQ (dbuf_c_str (&dbuf), s) || bvolatile) + { + emitcode ("mov", "%s,%s", dbuf_c_str (&dbuf), s); + } + if (EQ (dbuf_c_str (&dbuf), "acc")) + { + accuse = TRUE; + } + break; + + case AOP_REG: + if (!EQ (aop->aopu.aop_reg[offset]->name, s) && !EQ (aop->aopu.aop_reg[offset]->dname, s)) + { + if (*s == '@' || + EQ (s, "r0") || EQ (s, "r1") || EQ (s, "r2") || EQ (s, "r3") || + EQ (s, "r4") || EQ (s, "r5") || EQ (s, "r6") || EQ (s, "r7")) + { + emitcode ("mov", "%s,%s", aop->aopu.aop_reg[offset]->dname, s); + } + else + { + emitcode ("mov", "%s,%s", aop->aopu.aop_reg[offset]->name, s); + } + } + break; + + case AOP_DPTR: + if (aop->code) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut writing to code space"); + exit (EXIT_FAILURE); + } + + /* if not in accumulator */ + MOVA (s); + + while (offset > aop->coff) + { + aop->coff++; + emitcode ("inc", "dptr"); + } + + while (offset < aop->coff) + { + aop->coff--; + emitcode ("lcall", "__decdptr"); + } + + aop->coff = offset; + + emitcode ("movx", "@dptr,a"); + break; + + case AOP_R0: + case AOP_R1: + while (offset > aop->coff) + { + aop->coff++; + emitcode ("inc", "%s", aop->aopu.aop_ptr->name); + } + while (offset < aop->coff) + { + aop->coff--; + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + } + aop->coff = offset; + + if (aop->paged) + { + MOVA (s); + emitcode ("movx", "@%s,a", aop->aopu.aop_ptr->name); + } + else if (*s == '@') + { + MOVA (s); + emitcode ("mov", "@%s,a", aop->aopu.aop_ptr->name); + } + else if (EQ (s, "r0") || EQ (s, "r1") || EQ (s, "r2") || EQ (s, "r3") || + EQ (s, "r4") || EQ (s, "r5") || EQ (s, "r6") || EQ (s, "r7")) + { + dbuf_printf (&dbuf, "a%s", s); + emitcode ("mov", "@%s,%s", aop->aopu.aop_ptr->name, dbuf_c_str (&dbuf)); + } + else + { + emitcode ("mov", "@%s,%s", aop->aopu.aop_ptr->name, s); + } + break; + + case AOP_STK: + emitpush (s); + break; + + case AOP_CRY: + // destination is carry for return-use-only + d = (IS_OP_RUONLY (result)) ? "c" : aop->aopu.aop_dir; + + // source is no literal and not in carry + if (!EQ (s, zero) && !EQ (s, one) && !EQ (s, "c")) + { + MOVA (s); + /* set C, if a >= 1 */ + emitcode ("add", "a,#!constbyte", 0xff); + s = "c"; + } + // now source is zero, one or carry + + /* if result no bit variable */ + if (!d) + { + if (EQ (s, "c")) + { + /* inefficient: move carry into A and use jz/jnz */ + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + accuse = TRUE; + } + else + { + MOVA (s); + accuse = TRUE; + } + } + else if (EQ (s, zero)) + emitcode ("clr", "%s", d); + else if (EQ (s, one)) + emitcode ("setb", "%s", d); + else if (!EQ (s, d)) + emitcode ("mov", "%s,c", d); + break; + + case AOP_STR: + aop->coff = offset; + if (!EQ (aop->aopu.aop_str[offset], s) || bvolatile) + emitcode ("mov", "%s,%s", aop->aopu.aop_str[offset], s); + break; + + case AOP_ACC: + accuse = TRUE; + aop->coff = offset; + if (!offset && EQ (s, "acc") && !bvolatile) + break; + + if (!EQ (aop->aopu.aop_str[offset], s) && !bvolatile) + emitcode ("mov", "%s,%s", aop->aopu.aop_str[offset], s); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got unsupported aop->type"); + exit (EXIT_FAILURE); + } + + return accuse; +} + +/*--------------------------------------------------------------------*/ +/* loadDptrFromOperand - load dptr (and optionally B) from operand op */ +/*--------------------------------------------------------------------*/ +static void +loadDptrFromOperand (operand *op, bool loadBToo) +{ + if (AOP_TYPE (op) != AOP_STR) + { + /* if this is rematerializable */ + if (AOP_TYPE (op) == AOP_IMMD) + { + emitcode ("mov", "dptr,%s", aopGet (op, 0, TRUE, FALSE)); + if (loadBToo) + { + if (AOP (op)->aopu.aop_immd.from_cast_remat) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + else + { + wassertl (FALSE, "need pointerCode"); + emitcode (";", "mov b,???"); + /* genPointerGet and genPointerSet originally did different + ** things for this case. Both seem wrong. + ** from genPointerGet: + ** emitcode ("mov", "b,#%d", pointerCode (retype)); + ** from genPointerSet: + ** emitcode ("mov", "b,%s + 1", aopGet (result, 0, TRUE, FALSE)); + */ + } + } + } + else if (AOP_TYPE (op) == AOP_LIT) + { + emitcode ("mov", "dptr,%s", aopGet (op, 0, TRUE, FALSE)); + if (loadBToo) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + } + else if (AOP_TYPE (op) == AOP_DPTR) + { + emitpush (aopGet (op, 0, FALSE, FALSE)); + if (loadBToo) + { + emitpush (aopGet (op, 1, FALSE, FALSE)); + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + emitpop ("dph"); + } + else + { + emitcode ("mov", "dph,%s", aopGet (op, 1, FALSE, FALSE)); + } + emitpop ("dpl"); + } + else + { + /* we need to get it byte by byte */ + emitcode ("mov", "dpl,%s", aopGet (op, 0, FALSE, FALSE)); + emitcode ("mov", "dph,%s", aopGet (op, 1, FALSE, FALSE)); + if (loadBToo) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + } + } +} + +/*-----------------------------------------------------------------*/ +/* reAdjustPreg - points a register back to where it should */ +/*-----------------------------------------------------------------*/ +static void +reAdjustPreg (asmop * aop) +{ + if ((aop->coff == 0) || (aop->size <= 1)) + return; + + switch (aop->type) + { + case AOP_R0: + case AOP_R1: + while (aop->coff--) + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + break; + case AOP_DPTR: + while (aop->coff--) + { + emitcode ("lcall", "__decdptr"); + } + break; + } + aop->coff = 0; +} + +/*-----------------------------------------------------------------*/ +/* getDataSize - get the operand data size */ +/*-----------------------------------------------------------------*/ +static int +getDataSize (operand * op) +{ + int size = AOP_SIZE (op); + + if (size == GPTRSIZE) + { + sym_link *type = operandType (op); + if (IS_GENPTR (type)) + { + /* generic pointer; arithmetic operations + * should ignore the high byte (pointer type). + */ + size--; + } + } + return size; +} + +/*-----------------------------------------------------------------*/ +/* outAcc - output Acc */ +/*-----------------------------------------------------------------*/ +static void +outAcc (operand * result) +{ + int size, offset; + size = getDataSize (result); + if (size) + { + aopPut (result, "a", 0); + size--; + offset = 1; + /* unsigned or positive */ + while (size--) + { + aopPut (result, zero, offset++); + } + } +} + +/*-----------------------------------------------------------------*/ +/* outBitC - output a bit C */ +/*-----------------------------------------------------------------*/ +static void +outBitC (operand * result) +{ + /* if the result is bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + if (!IS_OP_RUONLY (result) && !IS_OP_ACCUSE (result)) + aopPut (result, "c", 0); + } + else if (AOP_TYPE (result) != AOP_DUMMY) + { + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + outAcc (result); + } +} + +/*-----------------------------------------------------------------*/ +/* toBoolean - emit code for orl a,operator(sizeop) */ +/*-----------------------------------------------------------------*/ +static void +toBoolean (operand * oper) +{ + int size = AOP_SIZE (oper) - 1; + int offset = 1; + bool AccUsed; + sym_link *type = operandType (oper); + bool pushedB; + + /* always need B for float */ + AccUsed = IS_FLOAT (type); + + while (!AccUsed && size--) + { + AccUsed |= aopGetUsesAcc (oper, offset++); + } + + if (opIsGptr (oper)) + { + /* assumes that banks never map to address 0x0000 + so it suffices to check dptr part only and ignore b */ + size = AOP_SIZE (oper) - 2; + } + else + { + size = AOP_SIZE (oper) - 1; + } + + offset = 0; + if (size && AccUsed && (AOP (oper)->type != AOP_ACC)) + { + pushedB = pushB (); + MOVB (aopGet (oper, offset++, FALSE, FALSE)); + while (--size) + { + MOVA (aopGet (oper, offset++, FALSE, FALSE)); + emitcode ("orl", "b,a"); + } + MOVA (aopGet (oper, offset++, FALSE, FALSE)); + if (IS_FLOAT (type)) + emitcode ("anl", "a,#0x7F"); //clear sign bit + emitcode ("orl", "a,b"); + popB (pushedB); + } + else + { + MOVA (aopGet (oper, offset++, FALSE, FALSE)); + while (size--) + { + emitcode ("orl", "a,%s", aopGet (oper, offset++, FALSE, FALSE)); + } + } +} + +/*-----------------------------------------------------------------*/ +/* toCarry - make boolean and move into carry */ +/*-----------------------------------------------------------------*/ +static void +toCarry (operand *oper) +{ + /* if the operand is a literal then + we know what the value is */ + if (AOP_TYPE (oper) == AOP_LIT) + { + if ((int) operandLitValue (oper)) + SETC; + else + CLRC; + } + else if (AOP_TYPE (oper) == AOP_CRY) + { + if (!IS_OP_ACCUSE (oper)) + emitcode ("mov", "c,%s", oper->aop->aopu.aop_dir); + } + else if (IS_BOOL (operandType (oper)) || IS_BITFIELD (operandType (oper)) && SPEC_BLEN (getSpec (operandType (oper))) == 1) + { + MOVA (aopGet (oper, 0, FALSE, FALSE)); + emitcode ("rrc", "a"); + } + else + { + /* or the operand into a */ + toBoolean (oper); + /* set C, if a >= 1 */ + emitcode ("add", "a,#0xff"); + } +} + +/*-----------------------------------------------------------------*/ +/* assignBit - assign operand to bit operand */ +/*-----------------------------------------------------------------*/ +static void +assignBit (operand * result, operand * right) +{ + emitcode (";", "assignBit"); + /* if the right side is a literal then + we know what the value is */ + if (AOP_TYPE (right) == AOP_LIT) + { + if ((int) operandLitValue (right)) + aopPut (result, one, 0); + else + aopPut (result, zero, 0); + } + else + { + toCarry (right); + outBitC (result); + } +} + +/*-------------------------------------------------------------------*/ +/* xch_a_aopGet - for exchanging acc with value of the aop */ +/*-------------------------------------------------------------------*/ +static const char * +xch_a_aopGet (operand * oper, int offset, bool bit16, bool dname) +{ + const char *l; + + if (aopGetUsesAcc (oper, offset)) + { + emitcode ("mov", "b,a"); + MOVA (aopGet (oper, offset, bit16, dname)); + emitcode ("xch", "a,b"); + aopPut (oper, "a", offset); + emitcode ("xch", "a,b"); + l = "b"; + } + else + { + l = aopGet (oper, offset, bit16, dname); + emitcode ("xch", "a,%s", l); + } + return l; +} + +/*-----------------------------------------------------------------*/ +/* genNot - generate code for ! operation */ +/*-----------------------------------------------------------------*/ +static void +genNot (iCode * ic) +{ + symbol *tlbl; + + D (emitcode (";", "genNot")); + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if in bit space then a special case */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + /* if left==result then cpl bit */ + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + emitcode ("cpl", "%s", IC_LEFT (ic)->aop->aopu.aop_dir); + } + else + { + toCarry (IC_LEFT (ic)); + emitcode ("cpl", "c"); + outBitC (IC_RESULT (ic)); + } + goto release; + } + + toBoolean (IC_LEFT (ic)); + + /* set C, if a == 0 */ + tlbl = newiTempLabel (NULL); + emitcode ("cjne", "a,#0x01,!tlabel", labelKey2num (tlbl->key)); + emitLabel (tlbl); + outBitC (IC_RESULT (ic)); + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +static void +genCpl (iCode * ic) +{ + int offset = 0; + int size; + symbol *tlbl; + sym_link *letype = getSpec (operandType (IC_LEFT (ic))); + + D (emitcode (";", "genCpl")); + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* special case if in bit space */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + const char *l; + + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY || (SPEC_USIGN (letype) && IS_CHAR (letype))) + { + /* promotion rules are responsible for this strange result: + bit -> int -> ~int -> bit + uchar -> int -> ~int -> bit + */ + emitcode ("setb", "%s", IC_RESULT (ic)->aop->aopu.aop_dir); + goto release; + } + + tlbl = newiTempLabel (NULL); + l = aopGet (IC_LEFT (ic), offset++, FALSE, FALSE); + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_ACC && offset == 0) || + AOP_TYPE (IC_LEFT (ic)) == AOP_REG || IS_AOP_PREG (IC_LEFT (ic))) + { + emitcode ("cjne", "%s,#0xFF,!tlabel", l, labelKey2num (tlbl->key)); + } + else + { + MOVA (l); + emitcode ("cjne", "a,#0xFF,!tlabel", labelKey2num (tlbl->key)); + } + emitLabel (tlbl); + outBitC (IC_RESULT (ic)); + goto release; + } + + size = AOP_SIZE (IC_RESULT (ic)); + while (size--) + { + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE)); + emitcode ("cpl", "a"); + aopPut (IC_RESULT (ic), "a", offset++); + } + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* genUminusFloat - unary minus for floating points */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (operand * op, operand * result) +{ + int size, offset = 0; + + D (emitcode (";", "genUminusFloat")); + + /* for this we just copy and then flip the bit */ + + size = AOP_SIZE (op) - 1; + + while (size--) + { + aopPut (result, aopGet (op, offset, FALSE, FALSE), offset); + offset++; + } + + MOVA (aopGet (op, offset, FALSE, FALSE)); + + emitcode ("cpl", "acc.7"); + aopPut (result, "a", offset); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - unary minus code generation */ +/*-----------------------------------------------------------------*/ +static void +genUminus (iCode * ic) +{ + int offset, size; + sym_link *optype; + + D (emitcode (";", "genUminus")); + + /* assign asmops */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if both in bit space then special + case */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY && AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + + emitcode ("mov", "c,%s", IC_LEFT (ic)->aop->aopu.aop_dir); + emitcode ("cpl", "c"); + emitcode ("mov", "%s,c", IC_RESULT (ic)->aop->aopu.aop_dir); + goto release; + } + + optype = operandType (IC_LEFT (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)); + offset = 0; + while (size--) + { + const char *l = aopGet (IC_LEFT (ic), offset, FALSE, FALSE); + if (EQ (l, "a")) + { + if (offset == 0) + SETC; + emitcode ("cpl", "a"); + emitcode ("addc", "a,#0x00"); + } + else + { + if (offset == 0) + CLRC; + emitcode ("clr", "a"); + emitcode ("subb", "a,%s", l); + } + aopPut (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))))) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (IC_RESULT (ic), "a", offset++); + } + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* inExcludeList - return 1 if the string is in exclude Reg list */ +/*-----------------------------------------------------------------*/ +static int +regsCmp (void *p1, void *p2) +{ + return (STRCASECMP ((char *) p1, (char *) (p2)) == 0); +} + +static bool +inExcludeList (char *s) +{ + const char *p = setFirstItem (options.excludeRegsSet); + + if (p == NULL || STRCASECMP (p, "none") == 0) + return FALSE; + + return isinSetWith (options.excludeRegsSet, s, regsCmp); +} + +/*-----------------------------------------------------------------*/ +/* xstackRegisters - create bitmask for registers on xstack */ +/*-----------------------------------------------------------------*/ +static int +xstackRegisters (bitVect * rsave, bool push, int count, char szRegs[32]) +{ + int i; + int mask = 0; + + szRegs[0] = '\0'; + + for (i = mcs51_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rsave, i)) + { + reg_info *reg = REG_WITH_INDEX (i); + if (reg->type == REG_BIT) + { + mask |= 0x01; + strncat (szRegs, reg->base, 31); + } + else + { + if (i == R0_IDX) + { + mask |= 0x100; + } + else + { + //set bit(n) for Rn + mask |= (0x01 << reg->offset); + } + strncat (szRegs, reg->name, 31); + } + } + } + return mask ^ 0xFF; //invert all bits for jbc +} + +/*-----------------------------------------------------------------*/ +/* saveRegisters - will look for a call and save the registers */ +/*-----------------------------------------------------------------*/ +static void +saveRegisters (iCode * lic) +{ + int i; + iCode *ic; + bitVect *rsave; + + /* look for call */ + for (ic = lic; ic; ic = ic->next) + if (ic->op == CALL || ic->op == PCALL) + break; + + if (!ic) + { + fprintf (stderr, "found parameter push with no function call\n"); + return; + } + + /* if the registers have been saved already or don't need to be then + do nothing */ + if (ic->regsSaved) + return; + if (IS_SYMOP (IC_LEFT (ic))) + { + sym_link *type = OP_SYM_TYPE (IC_LEFT (ic)); + if (IFFUNC_ISNAKED (type) && !IFFUNC_ISBANKEDCALL (type)) + return; + if (IFFUNC_CALLEESAVES (type)) + return; + } + + if (IFFUNC_CALLEESAVES (_G.currentFunc->type)) + { + /* save all registers if the caller is callee_saves and the callee is not */ + rsave = bitVectCopy (mcs51_allBankregs ()); + if (!inExcludeList ("bits")) + { + rsave = bitVectUnion (rsave, mcs51_allBitregs ()); + BitBankUsed = 1; + } + } + else + /* save only the registers in use at this time */ + rsave = bitVectCopy (ic->rMask); + /* but skip the ones for the result */ + rsave = bitVectCplAnd (rsave, mcs51_rUmaskForOp (IC_RESULT (ic))); + + ic->regsSaved = 1; + if (options.useXstack) + { + bitVect *rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), rsave); + int nBits = bitVectnBitsOn (rsavebits); + int count = bitVectnBitsOn (rsave); + + if (nBits != 0) + { + count = count - nBits + 1; + /* remove all but the first bits as they are pushed all at once */ + rsave = bitVectCplAnd (rsave, rsavebits); + rsave = bitVectSetBit (rsave, bitVectFirstBit (rsavebits)); + } + freeBitVect (rsavebits); + + if (count == 1) + { + reg_info *reg = REG_WITH_INDEX (bitVectFirstBit (rsave)); + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + if (reg->type == REG_BIT) + { + emitcode ("mov", "a,%s", reg->base); + } + else + { + emitcode ("mov", "a,%s", reg->name); + } + emitcode ("mov", "r0,%s", spname); + emitcode ("inc", "%s", spname); // allocate before use + emitcode ("movx", "@r0,a"); + _G.stack.xpushed++; + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + else if (count != 0) + { + if ((FUNC_REGBANK (currFunc->type) == 0) && optimize.codeSize) + { + char szRegs[32]; + int mask = xstackRegisters (rsave, TRUE, count, szRegs); + if (BINUSE) + emitpush ("b"); + emitcode ("mov", "a,#0x%02x", count); + emitcode ("mov", "b,#0x%02x", mask & 0xFF); + if (mask & 0x100) + emitcode ("lcall", "___sdcc_xpush_regs_r0\t;(%s)", szRegs); + else + emitcode ("lcall", "___sdcc_xpush_regs\t;(%s)", szRegs); + genLine.lineCurr->isInline = 1; + if (BINUSE) + emitpop ("b"); + _G.stack.xpushed += count; + } + else + { + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + emitcode ("mov", "r0,%s", spname); + if (count == 2) + { + emitcode ("inc", "%s", spname); + emitcode ("inc", "%s", spname); + } + else + { + MOVA ("r0"); + emitcode ("add", "a,#0x%02x", count); + emitcode ("mov", "%s,a", spname); + } + for (i = 0; i < mcs51_nRegs; i++) + { + if (bitVectBitValue (rsave, i)) + { + reg_info *reg = REG_WITH_INDEX (i); + if (i == R0_IDX) + { + emitpop ("acc"); + emitpush ("acc"); + } + else if (reg->type == REG_BIT) + { + emitcode ("mov", "a,%s", reg->base); + } + else + { + emitcode ("mov", "a,%s", reg->name); + } + emitcode ("movx", "@r0,a"); + _G.stack.xpushed++; + if (--count) + { + emitcode ("inc", "r0"); + } + } + } + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + } + } + else + { + bool bits_pushed = FALSE; + for (i = 0; i < mcs51_nRegs; i++) + { + if (bitVectBitValue (rsave, i)) + { + bits_pushed = pushReg (i, bits_pushed); + } + } + } + freeBitVect (rsave); +} + +/*-----------------------------------------------------------------*/ +/* unsaveRegisters - pop the pushed registers */ +/*-----------------------------------------------------------------*/ +static void +unsaveRegisters (iCode * ic) +{ + int i; + bitVect *rsave; + + if (IFFUNC_CALLEESAVES (_G.currentFunc->type)) + { + /* restore all registers if the caller is callee_saves and the callee is not */ + rsave = bitVectCopy (mcs51_allBankregs ()); + if (!inExcludeList ("bits")) + { + rsave = bitVectUnion (rsave, mcs51_allBitregs ()); + BitBankUsed = 1; + } + } + else + /* restore only the registers in use at this time */ + rsave = bitVectCopy (ic->rMask); + /* but skip the ones for the result */ + rsave = bitVectCplAnd (rsave, mcs51_rUmaskForOp (IC_RESULT (ic))); + + if (options.useXstack) + { + bitVect *rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), rsave); + int nBits = bitVectnBitsOn (rsavebits); + int count = bitVectnBitsOn (rsave); + + if (nBits != 0) + { + count = count - nBits + 1; + /* remove all but the first bits as they are popped all at once */ + rsave = bitVectCplAnd (rsave, rsavebits); + rsave = bitVectSetBit (rsave, bitVectFirstBit (rsavebits)); + } + freeBitVect (rsavebits); + + if (count == 1) + { + reg_info *reg = REG_WITH_INDEX (bitVectFirstBit (rsave)); + emitcode ("mov", "r0,%s", spname); + emitcode ("dec", "r0"); + emitcode ("movx", "a,@r0"); + _G.stack.xpushed--; + if (reg->type == REG_BIT) + { + emitcode ("mov", "%s,a", reg->base); + } + else + { + emitcode ("mov", "%s,a", reg->name); + } + emitcode ("dec", "%s", spname); + } + else if (count != 0) + { + if ((FUNC_REGBANK (currFunc->type) == 0) && optimize.codeSize) + { + char szRegs[32]; + int mask = xstackRegisters (rsave, FALSE, count, szRegs); + emitcode ("mov", "b,#0x%02x", mask & 0xFF); + if (mask & 0x100) + emitcode ("lcall", "___sdcc_xpop_regs_r0\t;(%s)", szRegs); + else + emitcode ("lcall", "___sdcc_xpop_regs\t;(%s)", szRegs); + genLine.lineCurr->isInline = 1; + _G.stack.xpushed -= count; + } + else + { + bool resultInR0 = bitVectBitValue (mcs51_rUmaskForOp (IC_RESULT (ic)), R0_IDX); + if (resultInR0) + { + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + } + emitcode ("mov", "r0,%s", spname); + for (i = mcs51_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rsave, i)) + { + reg_info *reg = REG_WITH_INDEX (i); + emitcode ("dec", "r0"); + emitcode ("movx", "a,@r0"); + _G.stack.xpushed--; + if (i == R0_IDX) + { + emitpush ("acc"); + } + else if (reg->type == REG_BIT) + { + emitcode ("mov", "%s,a", reg->base); + } + else + { + emitcode ("mov", "%s,a", reg->name); + } + } + } + emitcode ("mov", "%s,r0", spname); + if (bitVectBitValue (rsave, R0_IDX) || resultInR0) + { + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + } + } + } + else + { + bool bits_popped = FALSE; + for (i = mcs51_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rsave, i)) + { + bits_popped = popReg (i, bits_popped); + } + } + } + freeBitVect (rsave); +} + + +/*-----------------------------------------------------------------*/ +/* pushSide - */ +/*-----------------------------------------------------------------*/ +static void +pushSide (operand * oper, int size, iCode * ic) +{ + int offset = 0; + int nPushed = _G.r0Pushed + _G.r1Pushed; + + aopOp (oper, ic, FALSE); + + if (nPushed != _G.r0Pushed + _G.r1Pushed) + { + while (offset < size) + { + const char *l = aopGet (oper, offset, FALSE, TRUE); + emitcode ("mov", "%s,%s", fReturn[offset++], l); + } + freeAsmop (oper, NULL, ic, TRUE); + offset = 0; + while (offset < size) + { + emitpush (fReturn[offset++]); + } + return; + } + + while (size--) + { + const char *l = aopGet (oper, offset++, FALSE, TRUE); + if (AOP_TYPE (oper) != AOP_REG && AOP_TYPE (oper) != AOP_DIR) + { + MOVA (l); + emitpush ("acc"); + } + else + { + emitpush (l); + } + } + + freeAsmop (oper, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* assignResultValue - also indicates if acc is in use afterwards */ +/*-----------------------------------------------------------------*/ +static bool +assignResultValue (operand * oper, operand * func) +{ + int offset = 0; + int size = AOP_SIZE (oper); + bool accuse = FALSE; + bool pushedA = FALSE; + + if (func && IS_BIT (getSpec (operandType (func)))) + { + outBitC (oper); + return FALSE; + } + if ((size > 3) && aopPutUsesAcc (oper, fReturn[offset], offset)) + { + emitpush ("acc"); + pushedA = TRUE; + } + while (size--) + { + if ((offset == 3) && pushedA) + emitpop ("acc"); + accuse |= aopPut (oper, fReturn[offset], offset); + offset++; + } + return accuse; +} + + +/*-----------------------------------------------------------------*/ +/* genXpush - pushes onto the external stack */ +/*-----------------------------------------------------------------*/ +static void +genXpush (iCode * ic) +{ + asmop *aop = newAsmop (0); + reg_info *r; + int size, offset = 0; + + D (emitcode (";", "genXpush")); + + aopOp (IC_LEFT (ic), ic, FALSE); + r = getFreePtr (ic, aop, FALSE); + + size = AOP_SIZE (IC_LEFT (ic)); + emitcode ("mov", "%s,%s", r->name, spname); + + // allocate space first + if (size <= 2) + { + emitcode ("inc", "%s", spname); + if (size == 2) + emitcode ("inc", "%s", spname); + } + else + { + MOVA (r->name); + emitcode ("add", "a,#0x%02x", size); + emitcode ("mov", "%s,a", spname); + } + + while (offset < size) + { + MOVA (aopGet (IC_LEFT (ic), offset++, FALSE, FALSE)); + emitcode ("movx", "@%s,a", r->name); + emitcode ("inc", "%s", r->name); + } + _G.stack.xpushed += size; + + freeAsmop (NULL, aop, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIpush - generate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (iCode * ic) +{ + int size, offset = 0; + char *prev; + + D (emitcode (";", "genIpush")); + + /* 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) + { + /* and the item is spilt then do nothing */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + /* push it on the stack */ + while (size--) + { + emitpush (aopGet (IC_LEFT (ic), offset++, FALSE, TRUE)); + } + return; + } + + /* this is a parameter push: in this case we call + the routine to find the call and save those + registers that need to be saved */ + saveRegisters (ic); + + /* if use external stack then call the external + stack pushing routine */ + if (options.useXstack) + { + genXpush (ic); + return; + } + + /* then do the push */ + aopOp (IC_LEFT (ic), ic, FALSE); + + // pushSide(IC_LEFT(ic), AOP_SIZE(IC_LEFT(ic))); + size = AOP_SIZE (IC_LEFT (ic)); + + prev = Safe_strdup (""); + while (size--) + { + const char *l = aopGet (IC_LEFT (ic), offset++, FALSE, TRUE); + if (AOP_TYPE (IC_LEFT (ic)) != AOP_REG && AOP_TYPE (IC_LEFT (ic)) != AOP_DIR) + { + if (!EQ (l, prev) || *l == '@') + MOVA (l); + emitpush ("acc"); + } + else + { + emitpush (l); + } + Safe_free (prev); + prev = Safe_strdup (l); + } + Safe_free (prev); + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIpop - recover the registers: can happen only for spilling */ +/*-----------------------------------------------------------------*/ +static void +genIpop (iCode * ic) +{ + int size, offset; + + D (emitcode (";", "genIpop")); + + /* if the temp was not pushed then */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + offset = size - 1; + while (size--) + { + emitpop (aopGet (IC_LEFT (ic), offset--, FALSE, TRUE)); + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* popForBranch - recover the spilt registers for a branch */ +/*-----------------------------------------------------------------*/ +static void +popForBranch (iCode * ic, bool markGenerated) +{ + while (ic && ic->op == IPOP) + { + int pushed = _G.stack.pushed; + genIpop (ic); + if (markGenerated) + ic->generated = 1; /* mark the icode as generated */ + else + _G.stack.pushed = pushed; + ic = ic->next; + } +} + +/*-----------------------------------------------------------------*/ +/* emitDummyCall - emit a dummy call for --no-ret-without-call */ +/*-----------------------------------------------------------------*/ +static void +emitDummyCall(void) +{ + symbol *dummyLabel; + + if (!options.no_ret_without_call) + return; + dummyLabel = newiTempLabel (NULL); + emitcode ("lcall", "!tlabel", labelKey2num (dummyLabel->key)); + emitLabel (dummyLabel); + emitcode ("dec", "sp"); + emitcode ("dec", "sp"); +} + +/*-----------------------------------------------------------------*/ +/* saveRBank - saves an entire register bank on the stack */ +/*-----------------------------------------------------------------*/ +static void +saveRBank (int bank, iCode * ic, bool pushPsw) +{ + int i; + int count = 8 + (pushPsw ? 1 : 0); + asmop *aop = NULL; + reg_info *r = NULL; + + if (options.useXstack) + { + if (!ic) + { + /* Assume r0 is available for use. */ + r = REG_WITH_INDEX (R0_IDX); + } + else + { + aop = newAsmop (0); + r = getFreePtr (ic, aop, FALSE); + } + // allocate space first + emitcode ("mov", "%s,%s", r->name, spname); + MOVA (r->name); + emitcode ("add", "a,#!constbyte", count); + emitcode ("mov", "%s,a", spname); + } + + for (i = 0; i < 8; i++) + { + if (options.useXstack) + { + emitcode ("mov", "a,(%s+%d)", regs8051[i].base, 8 * bank + regs8051[i].offset); + emitcode ("movx", "@%s,a", r->name); + _G.stack.xpushed++; + if (--count) + emitcode ("inc", "%s", r->name); + } + else + { + char buf[16] = ""; + SNPRINTF (buf, 16, "(%s+%d)", regs8051[i].base, 8 * bank + regs8051[i].offset); + emitpush (buf); + } + } + + if (pushPsw) + { + if (options.useXstack) + { + emitcode ("mov", "a,psw"); + emitcode ("movx", "@%s,a", r->name); + _G.stack.xpushed++; + } + else + { + emitpush ("psw"); + } + + emitcode ("mov", "psw,#!constbyte", (bank << 3) & 0x00ff); + } + + if (aop) + { + freeAsmop (NULL, aop, ic, TRUE); + } + + if (ic) + { + ic->bankSaved = 1; + } +} + +/*-----------------------------------------------------------------*/ +/* unsaveRBank - restores the register bank from stack */ +/*-----------------------------------------------------------------*/ +static void +unsaveRBank (int bank, iCode * ic, bool popPsw) +{ + int i; + asmop *aop = NULL; + reg_info *r = NULL; + + if (options.useXstack) + { + if (!ic) + { + /* Assume r0 is available for use. */ + r = REG_WITH_INDEX (R0_IDX); + } + else + { + aop = newAsmop (0); + r = getFreePtr (ic, aop, FALSE); + } + emitcode ("mov", "%s,%s", r->name, spname); + } + + if (popPsw) + { + if (options.useXstack) + { + emitcode ("dec", "%s", r->name); + emitcode ("movx", "a,@%s", r->name); + emitcode ("mov", "psw,a"); + _G.stack.xpushed--; + } + else + { + emitpop ("psw"); + } + } + + for (i = 7; i >= 0; i--) + { + if (options.useXstack) + { + emitcode ("dec", "%s", r->name); + emitcode ("movx", "a,@%s", r->name); + emitcode ("mov", "(%s+%d),a", regs8051[i].base, 8 * bank + regs8051[i].offset); + _G.stack.xpushed--; + } + else + { + char buf[16] = ""; + SNPRINTF (buf, 16, "(%s+%d)", regs8051[i].base, 8 * bank + regs8051[i].offset); + emitpop (buf); + } + } + + if (options.useXstack) + { + emitcode ("mov", "%s,%s", spname, r->name); + } + + if (aop) + { + freeAsmop (NULL, aop, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* genSend - gen code for SEND */ +/*-----------------------------------------------------------------*/ +static void +genSend (set * sendSet) +{ + iCode *sic; + int bit_count = 0; + + /* first we do all bit parameters */ + for (sic = setFirstItem (sendSet); sic; sic = setNextItem (sendSet)) + { + if (sic->argreg > 12) + { + int bit = sic->argreg - 13; + + aopOp (IC_LEFT (sic), sic, FALSE); + + /* if left is a literal then + we know what the value is */ + if (AOP_TYPE (IC_LEFT (sic)) == AOP_LIT) + { + if (((int) operandLitValue (IC_LEFT (sic)))) + emitcode ("setb", "b[%d]", bit); + else + emitcode ("clr", "b[%d]", bit); + } + else + { + /* we need to or */ + toCarry (IC_LEFT (sic)); + emitcode ("mov", "b[%d],c", bit); + } + bit_count++; + BitBankUsed = 1; + + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + } + + if (options.useXstack || bit_count || setFirstItem (sendSet) && operandSize (IC_LEFT ((iCode *)(setFirstItem (sendSet)))) >= 6) + { + if (bit_count) + BITSINB++; + saveRegisters (setFirstItem (sendSet)); + if (bit_count) + BITSINB--; + } + + if (bit_count) + { + emitcode ("mov", "bits,b"); + } + + /* then we do all other parameters */ + for (sic = setFirstItem (sendSet); sic; sic = setNextItem (sendSet)) + { + if (sic->argreg <= 12) + { + int size, offset = 0; + aopOp (IC_LEFT (sic), sic, FALSE); + size = AOP_SIZE (IC_LEFT (sic)); + + if (sic->argreg == 1) + { + if (AOP_TYPE (IC_LEFT (sic)) != AOP_DPTR) + { + bool pushedA = FALSE; + while (size--) + { + const char *l = aopGet (IC_LEFT (sic), offset, FALSE, FALSE); + if (!EQ (l, fReturn[offset])) + if (fReturn[offset][0] == 'r' && (AOP_TYPE (IC_LEFT (sic)) == AOP_REG || AOP_TYPE (IC_LEFT (sic)) == AOP_R0 || AOP_TYPE (IC_LEFT (sic)) == AOP_R1)) + emitcode ("mov", "a%s,%s", fReturn[offset], l); // use register's direct address instead of name + else + emitcode ("mov", "%s,%s", fReturn[offset], l); + else if (EQ (l, "a") && size != 0) + { + emitpush ("acc"); + pushedA = TRUE; + } + offset++; + } + if (pushedA) + emitpop ("acc"); + } + else /* need to load dpl, dph, etc from @dptr */ + { + while (size--) + { + MOVA (aopGet (IC_LEFT (sic), offset, FALSE, FALSE)); + emitpush ("acc"); + offset++; + } + size = AOP_SIZE (IC_LEFT (sic)); + while (size--) + { + offset--; + if (!EQ ("a", fReturn[offset])) + { + emitpop (fReturn[offset]); + } + else + { + emitpop ("acc"); + } + } + } + } + else + { + while (size--) + { + emitcode ("mov", "%s,%s", rb1regs[sic->argreg + offset - 5], aopGet (IC_LEFT (sic), offset, FALSE, FALSE)); + offset++; + } + } + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + } +} + +/*-----------------------------------------------------------------*/ +/* selectRegBank - emit code to select the register bank */ +/*-----------------------------------------------------------------*/ +static void +selectRegBank (short bank, bool keepFlags) +{ + /* if f.e. result is in carry */ + if (keepFlags) + { + emitcode ("anl", "psw,#0xE7"); + if (bank) + emitcode ("orl", "psw,#0x%02x", (bank << 3) & 0xff); + } + else + { + emitcode ("mov", "psw,#0x%02x", (bank << 3) & 0xff); + } +} + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (iCode * ic) +{ + sym_link *dtype; + sym_link *etype; + bool swapBanks = FALSE; + bool accuse = FALSE; + bool accPushed = FALSE; + bool resultInF0 = FALSE; + bool assignResultGenerated = FALSE; + + D (emitcode (";", "genCall")); + + dtype = operandType (IC_LEFT (ic)); + etype = getSpec (dtype); + /* if send set is not empty then assign */ + if (_G.sendSet) + { + if (IFFUNC_ISREENT (dtype)) + { + /* need to reverse the send set */ + genSend (reverseSet (_G.sendSet)); + } + else + { + genSend (_G.sendSet); + } + _G.sendSet = NULL; + } + + /* if we are calling a not _naked function that is not using + the same register bank then we need to save the + destination registers on the stack */ + if (currFunc && dtype && !IFFUNC_ISNAKED (dtype) && + (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype)) && !IFFUNC_ISISR (dtype)) + { + swapBanks = TRUE; + } + + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + if (swapBanks) + { + emitcode ("mov", "psw,#!constbyte", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + /* make the call */ + if (IFFUNC_ISBANKEDCALL (dtype)) + { + if (IFFUNC_CALLEESAVES (dtype)) + { + werror (E_BANKED_WITH_CALLEESAVES); + } + else + { + if (IS_LITERAL (etype)) + { + emitcode ("mov", "r0,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 0, 1)); + emitcode ("mov", "r1,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 1, 1)); + emitcode ("mov", "r2,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 2, 1)); + } + else + { + char *name = (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? + OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name); + emitcode ("mov", "r0,#%s", name); + emitcode ("mov", "r1,#(%s >> 8)", name); + emitcode ("mov", "r2,#(%s >> 16)", name); + } + emitcode ("lcall", "__sdcc_banked_call"); + } + } + else + { + if (IS_LITERAL (etype)) + { + emitcode ("lcall", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + } + else + { + emitcode ("lcall", "%s", (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? + OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); + } + } + + if (swapBanks) + { + selectRegBank (FUNC_REGBANK (currFunc->type), IS_BIT (etype)); + } + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + !IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || + OP_SYMBOL (IC_RESULT (ic))->accuse || + OP_SYMBOL (IC_RESULT (ic))->spildir || IS_BIT (etype))) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + accuse = assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + assignResultGenerated = TRUE; + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) + { + int i; + if (ic->parmBytes > 3) + { + if (accuse) + { + emitpush ("acc"); + accPushed = TRUE; + } + if (IS_BIT (etype) && IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && !assignResultGenerated) + { + emitcode ("mov", "F0,c"); + resultInF0 = TRUE; + } + + emitcode ("mov", "a,%s", spname); + emitcode ("add", "a,#0x%02x", (-ic->parmBytes) & 0xff); + emitcode ("mov", "%s,a", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + + /* unsaveRegisters from xstack needs acc, but */ + /* unsaveRegisters from stack needs this popped */ + if (accPushed && !options.useXstack) + { + emitpop ("acc"); + accPushed = FALSE; + } + } + else + { + for (i = 0; i < ic->parmBytes; i++) + emitcode ("dec", "%s", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + } + } + + /* if we had saved some registers then unsave them */ + if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) + { + if (accuse && !accPushed && options.useXstack) + { + /* xstack needs acc, but doesn't touch normal stack */ + emitpush ("acc"); + accPushed = TRUE; + } + unsaveRegisters (ic); + } + + if (IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && !assignResultGenerated) + { + if (resultInF0) + emitcode ("mov", "c,F0"); + + aopOp (IC_RESULT (ic), ic, FALSE); + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + if (accPushed) + emitpop ("acc"); +} + +/*-----------------------------------------------------------------*/ +/* genPcall - generates a call by pointer statement */ +/*-----------------------------------------------------------------*/ +static void +genPcall (iCode * ic) +{ + sym_link *dtype; + sym_link *etype; + bool swapBanks = FALSE; + bool resultInF0 = FALSE; + + D (emitcode (";", "genPcall")); + + dtype = operandType (IC_LEFT (ic))->next; + etype = getSpec (dtype); + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + /* if we are calling a not _naked function that is not using + the same register bank then we need to save the + destination registers on the stack */ + if (currFunc && dtype && !IFFUNC_ISNAKED (dtype) && + (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype)) && !IFFUNC_ISISR (dtype)) + { + swapBanks = TRUE; + // need caution message to user here + } + + if (IS_LITERAL (etype)) + { + /* if send set is not empty then assign */ + if (_G.sendSet) + { + genSend (reverseSet (_G.sendSet)); + _G.sendSet = NULL; + } + + if (swapBanks) + { + emitcode ("mov", "psw,#0x%02x", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + if (IFFUNC_ISBANKEDCALL (dtype)) + { + if (IFFUNC_CALLEESAVES (dtype)) + { + werror (E_BANKED_WITH_CALLEESAVES); + } + else + { + emitcode ("mov", "r0,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 0, 1)); + emitcode ("mov", "r1,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 1, 1)); + emitcode ("mov", "r2,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 2, 1)); + emitcode ("lcall", "__sdcc_banked_call"); + } + } + else + { + emitcode ("lcall", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + } + } + else + { + if (IFFUNC_ISBANKEDCALL (dtype)) + { + if (IFFUNC_CALLEESAVES (dtype)) + { + werror (E_BANKED_WITH_CALLEESAVES); + } + else + { + aopOp (IC_LEFT (ic), ic, FALSE); + + emitpush (aopGet (IC_LEFT (ic), 0, FALSE, TRUE)); + emitpush (aopGet (IC_LEFT (ic), 1, FALSE, TRUE)); + emitpush (aopGet (IC_LEFT (ic), 2, FALSE, TRUE)); + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + + /* if send set is not empty then assign */ + if (_G.sendSet) + { + genSend (reverseSet (_G.sendSet)); + _G.sendSet = NULL; + } + + if (swapBanks) + { + char buf[8] = ""; + int reg = ((FUNC_REGBANK (dtype)) << 3) & 0xff; + emitcode ("mov", "psw,#0x%02x", reg); + SNPRINTF (buf, 8, "0x%02x", reg + 2); + emitpop (buf); + SNPRINTF (buf, 8, "0x%02x", reg + 1); + emitpop (buf); + SNPRINTF (buf, 8, "0x%02x", reg + 0); + emitpop (buf); + } + else + { + emitpop ("ar2"); + emitpop ("ar1"); + emitpop ("ar0"); + } + /* make the call */ + emitcode ("lcall", "__sdcc_banked_call"); + } + } + else if (_G.sendSet) /* the send set is not empty */ + { + symbol *callLabel = newiTempLabel (NULL); + symbol *returnLabel = newiTempLabel (NULL); + + /* create the return address on the stack */ + emitcode ("lcall", "!tlabel", labelKey2num (callLabel->key)); + emitcode ("ljmp", "!tlabel", labelKey2num (returnLabel->key)); + emitLabel (callLabel); + _G.stack.pushed += 2; + + emitDummyCall(); + /* now push the function address */ + pushSide (IC_LEFT (ic), FARPTRSIZE, ic); + + /* send set is not empty: assign */ + genSend (reverseSet (_G.sendSet)); + _G.sendSet = NULL; + + if (swapBanks) + { + emitcode ("mov", "psw,#0x%02x", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + /* make the call */ + emitcode ("ret", ""); + _G.stack.pushed -= 4; + emitLabel (returnLabel); + } + else /* the send set is empty */ + { + /* now get the called address into dptr */ + aopOp (IC_LEFT (ic), ic, FALSE); + + if (AOP_TYPE (IC_LEFT (ic)) == AOP_DPTR) + { + emitcode ("mov", "r0,%s", aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("mov", "dph,%s", aopGet (IC_LEFT (ic), 1, FALSE, FALSE)); + emitcode ("mov", "dpl,r0"); + } + else + { + emitcode ("mov", "dpl,%s", aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("mov", "dph,%s", aopGet (IC_LEFT (ic), 1, FALSE, FALSE)); + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + + if (swapBanks) + { + emitcode ("mov", "psw,#0x%02x", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + /* make the call */ + emitcode ("lcall", "__sdcc_call_dptr"); + } + } + if (swapBanks) + { + selectRegBank (FUNC_REGBANK (currFunc->type), IS_BIT (etype)); + } + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + !IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->accuse || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) + { + int i; + if (ic->parmBytes > 3) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic))) && IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic)))) + { + emitcode ("mov", "F0,c"); + resultInF0 = TRUE; + } + + emitcode ("mov", "a,%s", spname); + emitcode ("add", "a,#0x%02x", (-ic->parmBytes) & 0xff); + emitcode ("mov", "%s,a", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + } + else + { + for (i = 0; i < ic->parmBytes; i++) + emitcode ("dec", "%s", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + } + } + + /* if we had saved some registers then unsave them */ + if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) + unsaveRegisters (ic); + + if (IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic)))) + { + if (resultInF0) + emitcode ("mov", "c,F0"); + + aopOp (IC_RESULT (ic), ic, FALSE); + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + freeAsmop (IC_RESULT (ic), NULL, 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; +} + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + sym_link *ftype = operandType (IC_LEFT (ic)); + bool switchedPSW = FALSE; + int calleesaves_saved_register = -1; + int stackAdjust = sym->stack; + int accIsFree = sym->recvSize < 4; + char *freereg = NULL; + iCode *ric = (ic->next && ic->next->op == RECEIVE) ? ic->next : NULL; + bool fReentrant = (IFFUNC_ISREENT (sym->type) || options.stackAuto); + + /* create the function header */ + emitcode (";", "-----------------------------------------"); + emitcode (";", " function %s", sym->name); + emitcode (";", "-----------------------------------------"); + + emitcode ("", "%s:", sym->rname); + genLine.lineCurr->isLabel = 1; + _G.currentFunc = sym; + + if (IFFUNC_ISNAKED (ftype)) + { + emitcode (";", "naked function: no prologue."); + return; + } + + /* here we need to generate the equates for the + register bank if required */ + if (FUNC_REGBANK (ftype) != rbank) + { + int i; + + rbank = FUNC_REGBANK (ftype); + for (i = 0; i < mcs51_nRegs; i++) + { + if (regs8051[i].type != REG_BIT) + { + if (EQ (regs8051[i].base, "0")) + emitcode ("", "%s !equ !constbyte", regs8051[i].dname, 8 * rbank + regs8051[i].offset); + else + emitcode ("", "%s !equ %s + !constbyte", regs8051[i].dname, regs8051[i].base, 8 * rbank + regs8051[i].offset); + } + } + } + + _G.stack.param_offset = 0; + _G.stack.offset = sym->stack; + _G.stack.xoffset = sym->xstack; + wassertl (_G.stack.pushed == 0, "stack over/underflow"); + wassertl (_G.stack.xpushed == 0, "xstack over/underflow"); + + /* if this is an interrupt service routine then + save acc, b, dpl, dph */ + if (IFFUNC_ISISR (ftype)) + { + bitVect *rsavebits; + + /* weird but possible, one should better use a different priority */ + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + emitcode ("clr", "ea"); + } + + rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), sym->regsUsed); + if (IFFUNC_HASFCALL (ftype) || !bitVectIsZero (rsavebits)) + { + if (!inExcludeList ("bits")) + { + emitpush ("bits"); + BitBankUsed = 1; + } + } + freeBitVect (rsavebits); + + if (!inExcludeList ("acc")) + emitpush ("acc"); + if (!inExcludeList ("b")) + emitpush ("b"); + if (!inExcludeList ("dpl")) + emitpush ("dpl"); + if (!inExcludeList ("dph")) + emitpush ("dph"); + /* if this isr has no bank i.e. is going to + run with bank 0 , then we need to save more + registers :-) */ + if (!FUNC_REGBANK (ftype)) + { + int i; + + /* if this function does not call any other + function then we can be economical and + save only those registers that are used */ + if (!IFFUNC_HASFCALL (ftype)) + { + /* if any registers used */ + if (!bitVectIsZero (sym->regsUsed)) + { + /* save the registers used */ + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + pushReg (i, TRUE); + } + } + } + else + { + /* this function has a function call. We cannot + determine register usage so we will have to push the + entire bank */ + saveRBank (0, ic, FALSE); + if (options.parms_in_bank1) + { + for (i = 0; i < 8; i++) + { + emitpush (rb1regs[i]); + } + } + } + } + else + { + /* This ISR uses a non-zero bank. + * + * We assume that the bank is available for our + * exclusive use. + * + * However, if this ISR calls a function which uses some + * other bank, we must save that bank entirely. + */ + unsigned long banksToSave = 0; + + if (IFFUNC_HASFCALL (ftype)) + { + iCode *i; + int ix; + + for (i = ic; i; i = i->next) + { + sym_link *dtype = NULL; + + if (i->op == ENDFUNCTION) + { + /* we got to the end OK. */ + break; + } + + if (i->op == CALL) + { + dtype = operandType (IC_LEFT (i)); + } + if (i->op == PCALL) + { + /* This is a mess; we have no idea what + * register bank the called function might + * use. + * + * The only thing I can think of to do is + * throw a warning and hope. + */ +// werror (W_FUNCPTR_IN_USING_ISR); + dtype = operandType (IC_LEFT (i))->next; + } + if (dtype && FUNC_REGBANK (dtype) != FUNC_REGBANK (ftype)) + { + /* Mark this bank for saving. */ + if (FUNC_REGBANK (dtype) >= MAX_REGISTER_BANKS) + { + werror (E_NO_SUCH_BANK, FUNC_REGBANK (dtype)); + } + else + { + banksToSave |= (1 << FUNC_REGBANK (dtype)); + } + + /* And note that we don't need to do it in + * genCall. + */ + i->bankSaved = 1; + } + } + + if (banksToSave && options.useXstack) + { + /* Since we aren't passing it an ic, + * saveRBank will assume r0 is available to abuse. + * + * So switch to our (trashable) bank now, so + * the caller's R0 isn't trashed. + */ + emitpush ("psw"); + emitcode ("mov", "psw,#!constbyte", (FUNC_REGBANK (sym->type) << 3) & 0x00ff); + switchedPSW = TRUE; + } + + for (ix = 0; ix < MAX_REGISTER_BANKS; ix++) + { + if (banksToSave & (1 << ix)) + { + saveRBank (ix, NULL, FALSE); + } + } + } + // TODO: this needs a closer look + SPEC_ISR_SAVED_BANKS (currFunc->etype) = banksToSave; + } + + /* Set the register bank to the desired value if nothing else */ + /* has done so yet. */ + if (!switchedPSW) + { + emitpush ("psw"); + emitcode ("mov", "psw,#!constbyte", (FUNC_REGBANK (ftype) << 3) & 0x00ff); + } + } + else + { + /* This is a non-ISR function. */ + + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("setb", "c"); + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + emitcode ("clr", "c"); + emitLabel (tlbl); + emitpush ("psw"); /* save old ea via c in psw */ + } + + /* The caller has already switched register banks if */ + /* necessary, so just handle the callee-saves option. */ + + /* if callee-save to be used for this function + then save the registers being used in this function */ + if (IFFUNC_CALLEESAVES (ftype)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + bool bits_pushed = FALSE; + /* save the registers used */ + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + { + /* remember one saved register for later usage */ + if (calleesaves_saved_register < 0) + calleesaves_saved_register = i; + bits_pushed = pushReg (i, bits_pushed); + _G.stack.param_offset--; + } + } + } + } + } + + if (fReentrant && !options.omitFramePtr) + { + if (options.useXstack) + { + if (sym->xstack || FUNC_HASSTACKPARM (ftype)) + { + emitcode ("mov", "r0,%s", spname); + emitcode ("inc", "%s", spname); + emitcode ("xch", "a,_bpx"); + emitcode ("movx", "@r0,a"); + emitcode ("inc", "r0"); + emitcode ("mov", "a,r0"); + emitcode ("xch", "a,_bpx"); + } + if (sym->stack) + { + /* save the callers stack, but without pushed++ */ + emitcode ("push", "_bp"); + emitcode ("mov", "_bp,sp"); + } + } + else + { + if (sym->stack || FUNC_HASSTACKPARM (ftype)) + { + /* set up the stack */ + /* save the callers stack, but without pushed++ */ + emitcode ("push", "_bp"); + emitcode ("mov", "_bp,sp"); + } + } + } + + /* For some cases it is worthwhile to perform a RECEIVE iCode */ + /* before setting up the stack frame completely. */ + if (ric && ric->argreg == 1 && IC_RESULT (ric)) + { + symbol *rsym = OP_SYMBOL (IC_RESULT (ric)); + + if (rsym->isitmp) + { + if (rsym && rsym->regType == REG_CND) + rsym = NULL; + if (rsym && (rsym->accuse || rsym->ruonly)) + rsym = NULL; + if (rsym && (rsym->isspilt || rsym->nRegs == 0) && rsym->usl.spillLoc) + rsym = rsym->usl.spillLoc; + } + + /* If the RECEIVE operand immediately spills to the first entry on the */ + /* stack, we can push it directly (since sp = _bp + 1 at this point) */ + /* rather than the usual @r0/r1 machinations. */ + if (!options.useXstack && rsym && rsym->onStack && rsym->stack == 1) + { + int ofs; + + genLine.lineElement.ic = ric; + D (emitcode (";", "genReceive")); + for (ofs = 0; ofs < sym->recvSize; ofs++) + { + emitpush (fReturn[ofs]); + _G.stack.pushed--; /* cancel out pushed++ from emitpush()*/ + } + stackAdjust -= sym->recvSize; + if (stackAdjust < 0) + { + assert (stackAdjust >= 0); + stackAdjust = 0; + } + genLine.lineElement.ic = ic; + ric->generated = 1; + accIsFree = 1; + } + /* If the RECEIVE operand is 4 registers, we can do the moves now */ + /* to free up the accumulator. */ + else if (rsym && rsym->nRegs && sym->recvSize == 4) + { + int ofs; + + genLine.lineElement.ic = ric; + D (emitcode (";", "genReceive")); + for (ofs = 0; ofs < sym->recvSize; ofs++) + { + emitcode ("mov", "%s,%s", rsym->regs[ofs]->name, fReturn[ofs]); + } + genLine.lineElement.ic = ic; + ric->generated = 1; + accIsFree = 1; + } + } + + /* If the accumulator is not free, we will need another register */ + /* to clobber. No need to worry about a possible conflict with */ + /* the above early RECEIVE optimizations since they would have */ + /* freed the accumulator if they were generated. */ + if (IFFUNC_CALLEESAVES (ftype)) + { + /* if it's a callee-saves function we need a saved register */ + if (calleesaves_saved_register >= 0) + { + freereg = REG_WITH_INDEX (calleesaves_saved_register)->dname; + } + } + else + { + /* not callee-saves, we can clobber r0 */ + freereg = "r0"; + } + + /* adjust the stack for the function */ + if (stackAdjust) + { + int i = stackAdjust & 0xff; + if (stackAdjust > 256) + werror (W_STACK_OVERFLOW, sym->name); + + if (i > 3 && accIsFree) + { + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", i & 0xff); + emitcode ("mov", "sp,a"); + } + else if (i > 4) + { + if (freereg) + { + emitcode ("xch", "a,%s", freereg); + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", i & 0xff); + emitcode ("mov", "sp,a"); + emitcode ("xch", "a,%s", freereg); + } + else + { + /* do it the hard way */ + while (i--) + emitcode ("inc", "sp"); + } + } + else + { + while (i--) + emitcode ("inc", "sp"); + } + } + + if (sym->xstack) + { + int i = sym->xstack & 0xff; + if (sym->xstack > 256) + werror (W_STACK_OVERFLOW, sym->name); + + if (i > 3 && accIsFree) + { + emitcode ("mov", "a,_spx"); + emitcode ("add", "a,#!constbyte", i & 0xff); + emitcode ("mov", "_spx,a"); + } + else if (i > 4) + { + if (freereg) + emitcode ("xch", "a,%s", freereg); + else + emitpush ("acc"); + emitcode ("mov", "a,_spx"); + emitcode ("add", "a,#0x%02x", i & 0xff); + emitcode ("mov", "_spx,a"); + if (freereg) + emitcode ("xch", "a,%s", freereg); + else + emitpop ("acc"); + } + else + { + while (i--) + emitcode ("inc", "_spx"); + } + } + + _G.stack.param_offset = options.useXstack ? _G.stack.xpushed : _G.stack.pushed; + _G.stack.pushedregs = _G.stack.pushed; + _G.stack.xpushedregs = _G.stack.xpushed; + _G.stack.pushed = 0; + _G.stack.xpushed = 0; +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + sym_link *ftype = operandType (IC_LEFT (ic)); + bool fReentrant = (IFFUNC_ISREENT (sym->type) || options.stackAuto); + lineNode *lineBodyEnd = genLine.lineCurr; + lineNode *linePrologueStart = NULL; + lineNode *lnp; + bitVect *regsUsed; + bitVect *regsUnneeded; + int idx; + + _G.currentFunc = NULL; + if (IFFUNC_ISNAKED (ftype)) + { + emitcode (";", "naked function: no epilogue."); + if (options.debug && currFunc) + debugFile->writeEndFunction (currFunc, ic, 0); + return; + } + + _G.stack.xpushed = _G.stack.xpushedregs; + _G.stack.pushed = _G.stack.pushedregs; + + if (fReentrant) + { + if (options.omitFramePtr) + { + bool cy_in_r0 = FALSE; + bool acc_in_r0 = FALSE; + + if (sym->stack > 3) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic)))) + { + emitcode ("mov", "r0,psw"); /* save cy in r0 */ + cy_in_r0 = TRUE; + } + if (getSize (OP_SYM_ETYPE (IC_LEFT (ic))) >= 4) + { + emitcode ("xch", "a,r0"); /* save a in r0 */ + acc_in_r0 = TRUE; + } + + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", (-sym->stack) & 0xff); + emitcode ("mov", "sp,a"); + } + else + { + int i = sym->stack; + while (i--) + emitcode ("dec", "sp"); + } + if (sym->xstack > 3) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic)))) + { + if (!cy_in_r0) + emitcode ("mov", "r0,psw"); /* save cy in r0 */ + cy_in_r0 = TRUE; + } + if (getSize (OP_SYM_ETYPE (IC_LEFT (ic))) >= 4) + { + if (!acc_in_r0) + emitcode ("xch", "a,r0"); /* save a in r0 */ + acc_in_r0 = TRUE; + } + + emitcode ("mov", "a,_spx"); + emitcode ("add", "a,#!constbyte", (-sym->xstack) & 0xff); + emitcode ("mov", "_spx,a"); + } + else + { + int i = sym->xstack; + while (i--) + emitcode ("dec", "_spx"); + } + + if (acc_in_r0) + emitcode ("xch", "a,r0"); /* restore a from r0 */ + + if (cy_in_r0) + emitcode ("mov", "psw,r0"); /* restore c from r0 */ + } + else + { + if (options.useXstack) + { + if (sym->stack) + { + if (sym->stack == 1) + emitcode ("dec", "sp"); + else + emitcode ("mov", "sp,_bp"); + emitcode ("pop", "_bp"); /* without pushed-- */ + } + if (sym->xstack || FUNC_HASSTACKPARM (ftype)) + { + emitcode ("xch", "a,_bpx"); + emitcode ("mov", "r0,a"); + emitcode ("dec", "r0"); + emitcode ("movx", "a,@r0"); + emitcode ("xch", "a,_bpx"); + emitcode ("mov", "%s,r0", spname); //read before freeing stack space (interrupts) + } + } + else if (sym->stack || FUNC_HASSTACKPARM (ftype)) + { + if (sym->stack == 1) + emitcode ("dec", "sp"); + else if (sym->stack) + emitcode ("mov", "sp,_bp"); + emitcode ("pop", "_bp"); /* without pushed-- */ + } + } + } + + /* restore the register bank */ + if (IFFUNC_ISISR (ftype)) + { + if (!FUNC_REGBANK (ftype) || !options.useXstack) + { + /* Special case of ISR using non-zero bank with useXstack + * is handled below. + */ + emitpop ("psw"); + } + } + + if (IFFUNC_ISISR (ftype)) + { + bitVect *rsavebits; + + /* now we need to restore the registers */ + /* if this isr has no bank i.e. is going to + run with bank 0 , then we need to save more + registers :-) */ + if (!FUNC_REGBANK (ftype)) + { + int i; + /* if this function does not call any other + function then we can be economical and + save only those registers that are used */ + if (!IFFUNC_HASFCALL (ftype)) + { + /* if any registers used */ + if (!bitVectIsZero (sym->regsUsed)) + { + /* restore the registers used */ + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i)) + popReg (i, TRUE); + } + } + } + else + { + if (options.parms_in_bank1) + { + for (i = 7; i >= 0; i--) + { + emitpop (rb1regs[i]); + } + } + /* this function has a function call. We cannot + determine register usage so we will have to pop the + entire bank */ + unsaveRBank (0, ic, FALSE); + } + } + else + { + /* This ISR uses a non-zero bank. + * + * Restore any register banks saved by genFunction + * in reverse order. + */ + unsigned savedBanks = SPEC_ISR_SAVED_BANKS (currFunc->etype); + int ix; + + for (ix = MAX_REGISTER_BANKS - 1; ix >= 0; ix--) + { + if (savedBanks & (1 << ix)) + { + unsaveRBank (ix, NULL, FALSE); + } + } + + if (options.useXstack) + { + /* Restore bank AFTER calling unsaveRBank, + * since it can trash r0. + */ + emitpop ("psw"); + } + } + + if (!inExcludeList ("dph")) + emitpop ("dph"); + if (!inExcludeList ("dpl")) + emitpop ("dpl"); + if (!inExcludeList ("b")) + emitpop ("b"); + if (!inExcludeList ("acc")) + emitpop ("acc"); + + rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), sym->regsUsed); + if (IFFUNC_HASFCALL (ftype) || !bitVectIsZero (rsavebits)) + { + if (!inExcludeList ("bits")) + emitpop ("bits"); + } + freeBitVect (rsavebits); + + /* weird but possible, one should better use a different priority */ + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + emitcode ("setb", "ea"); + } + + /* if debug then send end of function */ + if (options.debug && currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + emitcode ("reti", ""); + } + else + { + if (IFFUNC_CALLEESAVES (ftype)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + /* save the registers used */ + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i) || (mcs51_ptrRegReq && (i == R0_IDX || i == R1_IDX))) + emitpop (REG_WITH_INDEX (i)->dname); + } + } + else if (mcs51_ptrRegReq) + { + emitpop (REG_WITH_INDEX (R1_IDX)->dname); + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + } + + if (IFFUNC_ISCRITICAL (ftype)) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic)))) + { + emitcode ("rlc", "a"); /* save c in a */ + emitpop ("psw"); /* restore ea via c in psw */ + emitcode ("mov", "ea,c"); + emitcode ("rrc", "a"); /* restore c from a */ + } + else + { + emitpop ("psw"); /* restore ea via c in psw */ + emitcode ("mov", "ea,c"); + } + } + + /* if debug then send end of function */ + if (options.debug && currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + if (IFFUNC_ISBANKEDCALL (ftype)) + { + emitcode ("ljmp", "__sdcc_banked_ret"); + } + else + { + emitcode ("ret", ""); + } + } + + wassertl (_G.stack.pushed == 0, "stack over/underflow"); + wassertl (_G.stack.xpushed == 0, "xstack over/underflow"); + + if (!port->peep.getRegsRead || !port->peep.getRegsWritten || options.nopeep) + return; + + /* If this was an interrupt handler using bank 0 that called another */ + /* function, then all registers must be saved; nothing to optimize. */ + if (IFFUNC_ISISR (ftype) && IFFUNC_HASFCALL (ftype) && !FUNC_REGBANK (ftype)) + return; + + /* There are no push/pops to optimize if not callee-saves or ISR */ + if (!(FUNC_CALLEESAVES (ftype) || FUNC_ISISR (ftype))) + return; + + /* If there were stack parameters, we cannot optimize without also */ + /* fixing all of the stack offsets; this is too dificult to consider. */ + if (FUNC_HASSTACKPARM (ftype)) + return; + + /* Compute the registers actually used */ + regsUsed = newBitVect (mcs51_nRegs); + lnp = lineBodyEnd; + while (lnp) + { + /* Remove change of register bank if no registers used */ + if (lnp->ic && lnp->ic->op == FUNCTION && + !strncmp (lnp->line, "mov", 3) && + bitVectFirstBit (port->peep.getRegsWritten (lnp)) == CND_IDX && + !bitVectBitsInCommon (mcs51_allBankregs (), regsUsed) && + !IFFUNC_HASFCALL (ftype)) + { + emitcode (";", "eliminated unneeded mov psw,# (no regs used in bank)"); + connectLine (lnp->prev, lnp->next); + } + else + { + regsUsed = bitVectUnion (regsUsed, port->peep.getRegsWritten (lnp)); + } + + if (lnp->ic && lnp->ic->op == FUNCTION) + { + if (!lnp->prev || (lnp->prev->ic && lnp->prev->ic->op != FUNCTION)) + break; + } + lnp = lnp->prev; + } + linePrologueStart = lnp; + + /* If this was an interrupt handler that called another function */ + /* function, then assume A, B, DPH, & DPL may be modified by it. */ + if (IFFUNC_ISISR (ftype) && IFFUNC_HASFCALL (ftype)) + { + regsUsed = bitVectSetBit (regsUsed, DPL_IDX); + regsUsed = bitVectSetBit (regsUsed, DPH_IDX); + regsUsed = bitVectSetBit (regsUsed, B_IDX); + regsUsed = bitVectSetBit (regsUsed, A_IDX); + regsUsed = bitVectSetBit (regsUsed, CND_IDX); + } + + /* Remove the unneeded push/pops */ + regsUnneeded = newBitVect (mcs51_nRegs); + for (lnp = genLine.lineCurr; lnp != linePrologueStart; lnp = lnp->prev) + { + if (lnp->ic) + { + if (lnp->ic && (lnp->ic->op == FUNCTION) && !strncmp (lnp->line, "push", 4)) + { + idx = bitVectFirstBit (port->peep.getRegsRead (lnp)); + if (idx >= 0 && !bitVectBitValue (regsUsed, idx)) + { + connectLine (lnp->prev, lnp->next); + regsUnneeded = bitVectSetBit (regsUnneeded, idx); + } + } + if (lnp->ic && (lnp->ic->op == ENDFUNCTION) && !strncmp (lnp->line, "pop", 3)) + { + idx = bitVectFirstBit (port->peep.getRegsWritten (lnp)); + if (idx >= 0 && !bitVectBitValue (regsUsed, idx)) + { + connectLine (lnp->prev, lnp->next); + regsUnneeded = bitVectSetBit (regsUnneeded, idx); + } + } + } + } + + for (idx = 0; idx < regsUnneeded->size; idx++) + if (bitVectBitValue (regsUnneeded, idx)) + emitcode (";", "eliminated unneeded push/pop %s", REG_WITH_INDEX (idx)->dname); + + freeBitVect (regsUnneeded); + freeBitVect (regsUsed); +} + +/*-----------------------------------------------------------------*/ +/* genRet - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genRet (iCode * ic) +{ + int size, offset = 0, pushed = 0; + bool pushedA = FALSE; + + D (emitcode (";", "genRet")); + + /* 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); + size = AOP_SIZE (IC_LEFT (ic)); + + if (IS_BIT (_G.currentFunc->etype)) + { + if (!IS_OP_RUONLY (IC_LEFT (ic))) + toCarry (IC_LEFT (ic)); + } + else + { + while (size--) + { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_DPTR) + { + /* #NOCHANGE */ + emitpush (aopGet (IC_LEFT (ic), offset++, FALSE, TRUE)); + pushed++; + } + else + { + const char *l = aopGet (IC_LEFT (ic), offset, FALSE, FALSE); + if (!EQ (fReturn[offset], l)) + if (fReturn[offset][0] == 'r' && (AOP_TYPE (IC_LEFT (ic)) == AOP_REG || AOP_TYPE (IC_LEFT (ic)) == AOP_R0 || AOP_TYPE (IC_LEFT (ic)) == AOP_R1)) + emitcode ("mov", "a%s,%s", fReturn[offset], l); // use register's direct address instead of name + else + emitcode ("mov", "%s,%s", fReturn[offset], l); + if (size && !strcmp(fReturn[offset], "a") && aopGetUsesAcc (IC_LEFT (ic), offset+1)) + { + emitpush ("acc"); + pushedA = TRUE; + } + offset++; + } + } + if (pushedA) + { + emitpop ("acc"); + } + + while (pushed) + { + pushed--; + if (!EQ (fReturn[pushed], "a")) + emitpop (fReturn[pushed]); + else + emitpop ("acc"); + } + } + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + +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)) + { + emitcode ("ljmp", "!tlabel", labelKey2num (returnLabel->key)); + } +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (iCode * ic) +{ + /* special case never generate */ + if (IC_LABEL (ic) == entryLabel) + return; + + emitLabel (IC_LABEL (ic)); +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a ljmp */ +/*-----------------------------------------------------------------*/ +static void +genGoto (iCode * ic) +{ + emitcode ("ljmp", "!tlabel", labelKey2num (IC_LABEL (ic)->key)); +} + +/*-----------------------------------------------------------------*/ +/* genPlusIncr :- does addition with increment if possible */ +/*-----------------------------------------------------------------*/ +static bool +genPlusIncr (iCode * ic) +{ + unsigned int icount; + unsigned int size = getDataSize (IC_RESULT (ic)), offset; + + /* 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; + + icount = (unsigned int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + + D (emitcode (";", "genPlusIncr")); + + /* if increment >=16 bits in register or direct space */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_REG || + AOP_TYPE (IC_LEFT (ic)) == AOP_DIR || + (IS_AOP_PREG (IC_LEFT (ic)) && !AOP_NEEDSACC (IC_LEFT (ic)))) && + sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && + !isOperandVolatile (IC_RESULT (ic), FALSE) && (size > 1) && (icount == 1)) + { + symbol *tlbl; + const char *l; + + tlbl = newiTempLabel (NULL); + l = aopGet (IC_RESULT (ic), LSB, FALSE, FALSE); + emitcode ("inc", "%s", l); + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,%s,!tlabel", l, zero, labelKey2num (tlbl->key)); + } + else + { + emitcode ("clr", "a"); + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + + l = aopGet (IC_RESULT (ic), MSB16, FALSE, FALSE); + emitcode ("inc", "%s", l); + + for(offset = 2; size > 2; size--, offset++) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,%s,!tlabel", l, zero, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + + l = aopGet (IC_RESULT (ic), offset, FALSE, FALSE); + emitcode ("inc", "%s", l); + } + + emitLabel (tlbl); + return TRUE; + } + + /* if result is dptr */ + if ((AOP_TYPE (IC_RESULT (ic)) == AOP_STR) && + (AOP_SIZE (IC_RESULT (ic)) == 2) && + !strncmp (AOP (IC_RESULT (ic))->aopu.aop_str[0], "dpl", 4) && !strncmp (AOP (IC_RESULT (ic))->aopu.aop_str[1], "dph", 4)) + { + if (aopGetUsesAcc (IC_LEFT (ic), 0)) + return FALSE; + + if (icount > 9) + return FALSE; + + if ((AOP_TYPE (IC_LEFT (ic)) != AOP_DIR) && (icount > 5)) + return FALSE; + + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), 0, FALSE, FALSE), 0); + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), 1, FALSE, FALSE), 1); + while (icount--) + emitcode ("inc", "dptr"); + + 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 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)))) + { + if (icount > 3) + { + MOVA (aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("add", "a,#!constbyte", ((char) icount) & 0xff); + aopPut (IC_RESULT (ic), "a", 0); + } + else + { + while (icount--) + { + emitcode ("inc", "%s", aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + } + } + + return TRUE; + } + + if (icount == 1) + { + MOVA (aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("inc", "a"); + aopPut (IC_RESULT (ic), "a", 0); + return TRUE; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* outBitAcc - output a bit in acc */ +/*-----------------------------------------------------------------*/ +static void +outBitAcc (operand * result) +{ + symbol *tlbl = newiTempLabel (NULL); + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + aopPut (result, "a", 0); + } + else + { + emitcode ("jz", "!tlabel", labelKey2num (tlbl->key)); + emitcode ("mov", "a,%s", one); + emitLabel (tlbl); + outAcc (result); + } +} + +/*-----------------------------------------------------------------*/ +/* genPlusBits - generates code for addition of two bits */ +/*-----------------------------------------------------------------*/ +static void +genPlusBits (iCode * ic) +{ + D (emitcode (";", "genPlusBits")); + + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("jnb", "%s,!tlabel", AOP (IC_RIGHT (ic))->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + outBitC (IC_RESULT (ic)); + } + else + { + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + emitcode ("mov", "c,%s", AOP (IC_RIGHT (ic))->aopu.aop_dir); + emitcode ("addc", "a,%s", zero); + outAcc (IC_RESULT (ic)); + } +} + +static void +adjustArithmeticResult (iCode * ic) +{ + if (opIsGptr (IC_RESULT (ic))) + { + struct dbuf_s dbuf; + + if (opIsGptr (IC_LEFT (ic))) + { + if (!sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)))) + { + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), GPTRSIZE - 1, FALSE, FALSE), GPTRSIZE - 1); + } + return; + } + + if (opIsGptr (IC_RIGHT (ic))) + { + if (!sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) + { + aopPut (IC_RESULT (ic), aopGet (IC_RIGHT (ic), GPTRSIZE - 1, FALSE, FALSE), GPTRSIZE - 1); + } + return; + } + + dbuf_init (&dbuf, 128); + if (IC_LEFT (ic) && AOP_SIZE (IC_LEFT (ic)) < GPTRSIZE && + IC_RIGHT (ic) && AOP_SIZE (IC_RIGHT (ic)) < GPTRSIZE && + !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic))) && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) + { + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_LEFT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + } + else if (IC_LEFT (ic) && AOP_SIZE (IC_LEFT (ic)) < GPTRSIZE && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)))) + { + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_LEFT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + } + else if (IC_RIGHT (ic) && AOP_SIZE (IC_RIGHT (ic)) < GPTRSIZE && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) + { + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_RIGHT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + } + dbuf_destroy (&dbuf); + } +} + +/*-----------------------------------------------------------------*/ +/* genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void +genPlus (iCode * ic) +{ + int size, offset = 0; + int skip_bytes = 0; + char *add = "add"; + bool swappedLR = FALSE; + operand *leftOp, *rightOp; + operand *op; + + D (emitcode (";", "genPlus")); + + /* special cases :- */ + + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* 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_LEFT (ic))) || AOP_TYPE (IC_RIGHT (ic)) == AOP_ACC) + { + swapOperands (&IC_LEFT (ic), &IC_RIGHT (ic)); + swappedLR = TRUE; + } + + /* if both left & right are in bit space */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + genPlusBits (ic); + goto release; + } + + /* if left in bit space & right literal */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT) + { + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + /* if result in bit space */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + if (ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit) != 0L) + emitcode ("cpl", "c"); + outBitC (IC_RESULT (ic)); + } + else + { + size = getDataSize (IC_RESULT (ic)); + while (size--) + { + MOVA (aopGet (IC_RIGHT (ic), offset, FALSE, FALSE)); + emitcode ("addc", "a,%s", zero); + aopPut (IC_RESULT (ic), "a", offset++); + } + } + goto release; + } + + /* if I can do an increment instead + of add then GOOD for ME */ + if (genPlusIncr (ic) == TRUE) + goto release; + + size = getDataSize (IC_RESULT (ic)); + leftOp = IC_LEFT (ic); + rightOp = IC_RIGHT (ic); + op = IC_LEFT (ic); + + /* if this is an add for an array access + at a 256 byte boundary */ + if (2 == size + && AOP_TYPE (op) == AOP_IMMD + && IS_SYMOP (op) + && IS_SPEC (OP_SYM_ETYPE (op)) && SPEC_ABSA (OP_SYM_ETYPE (op)) && (SPEC_ADDR (OP_SYM_ETYPE (op)) & 0xff) == 0) + { + D (emitcode (";", "genPlus aligned array")); + aopPut (IC_RESULT (ic), aopGet (rightOp, 0, FALSE, FALSE), 0); + + if (1 == getDataSize (IC_RIGHT (ic))) + { + aopPut (IC_RESULT (ic), aopGet (leftOp, 1, FALSE, FALSE), 1); + } + else + { + MOVA (aopGet (IC_LEFT (ic), 1, FALSE, FALSE)); + emitcode ("add", "a,%s", aopGet (rightOp, 1, FALSE, FALSE)); + aopPut (IC_RESULT (ic), "a", 1); + } + goto release; + } + + /* if the lower bytes of a literal are zero skip the addition */ + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT) + { + while ((0 == ((unsigned int) ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit) & (0xff << skip_bytes * 8))) && + (skip_bytes + 1 < size)) + { + skip_bytes++; + } + if (skip_bytes) + D (emitcode (";", "genPlus shortcut")); + } + + while (size--) + { + if (offset >= skip_bytes) + { + if (aopGetUsesAcc (leftOp, offset) && aopGetUsesAcc (rightOp, offset)) + { + bool pushedB; + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + pushedB = pushB (); + emitcode ("xch", "a,b"); + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + emitcode (add, "a,b"); + popB (pushedB); + } + else if (aopGetUsesAcc (leftOp, offset)) + { + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + emitcode (add, "a,%s", aopGet (rightOp, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + emitcode (add, "a,%s", aopGet (leftOp, offset, FALSE, FALSE)); + } + aopPut (IC_RESULT (ic), "a", offset); + add = "addc"; /* further adds must propagate carry */ + } + else + { + if (!sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) || isOperandVolatile (IC_RESULT (ic), FALSE)) + { + /* just move */ + aopPut (IC_RESULT (ic), aopGet (leftOp, offset, FALSE, FALSE), offset); + } + } + offset++; + } + + adjustArithmeticResult (ic); + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + if (swappedLR) + swapOperands (&IC_LEFT (ic), &IC_RIGHT (ic)); + freeAsmop (IC_RIGHT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genMinusDec :- does subtraction with decrement if possible */ +/*-----------------------------------------------------------------*/ +static bool +genMinusDec (iCode * ic) +{ + unsigned int icount; + unsigned int size = getDataSize (IC_RESULT (ic)); + + /* will try to generate a decrement */ + /* 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) ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit)) > 4) + return FALSE; + + D (emitcode (";", "genMinusDec")); + + /* if decrement >=16 bits in register or direct space */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_REG || + AOP_TYPE (IC_LEFT (ic)) == AOP_DIR || + (IS_AOP_PREG (IC_LEFT (ic)) && !AOP_NEEDSACC (IC_LEFT (ic)))) && + sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && (size > 1) && (icount == 1)) + { + symbol *tlbl; + const char *l; + + tlbl = newiTempLabel (NULL); + l = aopGet (IC_RESULT (ic), LSB, FALSE, FALSE); + emitcode ("dec", "%s", l); + + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("mov", "a,#!constbyte", 0xff); + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + l = aopGet (IC_RESULT (ic), MSB16, FALSE, FALSE); + emitcode ("dec", "%s", l); + if (size > 2) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + l = aopGet (IC_RESULT (ic), MSB24, FALSE, FALSE); + emitcode ("dec", "%s", l); + } + if (size > 3) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), MSB32, FALSE, FALSE)); + } + emitLabel (tlbl); + 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)))) + { + const char *l; + + if (aopGetUsesAcc (IC_LEFT (ic), 0)) + { + MOVA (aopGet (IC_RESULT (ic), 0, FALSE, FALSE)); + l = "a"; + } + else + { + l = aopGet (IC_RESULT (ic), 0, FALSE, FALSE); + } + + while (icount--) + { + emitcode ("dec", "%s", l); + } + + if (AOP_NEEDSACC (IC_RESULT (ic))) + aopPut (IC_RESULT (ic), "a", 0); + + return TRUE; + } + + if (icount == 1) + { + MOVA (aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("dec", "a"); + aopPut (IC_RESULT (ic), "a", 0); + return TRUE; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* addSign - complete with sign */ +/*-----------------------------------------------------------------*/ +static void +addSign (operand * result, int offset, int sign) +{ + int size = (getDataSize (result) - offset); + if (size > 0) + { + if (sign) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + { + aopPut (result, "a", offset++); + } + } + else + { + while (size--) + { + aopPut (result, zero, offset++); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* genMinusBits - generates code for subtraction of two bits */ +/*-----------------------------------------------------------------*/ +static void +genMinusBits (iCode * ic) +{ + symbol *lbl = newiTempLabel (NULL); + + D (emitcode (";", "genMinusBits")); + + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + emitcode ("jnb", "%s,!tlabel", AOP (IC_RIGHT (ic))->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + outBitC (IC_RESULT (ic)); + } + else + { + emitcode ("mov", "c,%s", AOP (IC_RIGHT (ic))->aopu.aop_dir); + emitcode ("subb", "a,acc"); + emitcode ("jnb", "%s,!tlabel", AOP (IC_LEFT (ic))->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("inc", "a"); + emitLabel (lbl); + aopPut (IC_RESULT (ic), "a", 0); + addSign (IC_RESULT (ic), MSB16, SPEC_USIGN (getSpec (operandType (IC_RESULT (ic))))); + } +} + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void +genMinus (iCode * ic) +{ + int size, offset = 0; + + D (emitcode (";", "genMinus")); + + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* 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) + { + genMinusBits (ic); + goto release; + } + + /* if I can do a decrement instead + of subtract then GOOD for ME */ + if (genMinusDec (ic) == TRUE) + goto release; + + size = getDataSize (IC_RESULT (ic)); + + /* if literal, add a,#-lit, else normal subb */ + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT) + { + unsigned long long lit = 0L; + bool useCarry = FALSE; + + lit = ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + lit = -(long long) lit; + + while (size--) + { + if (useCarry || ((lit >> (offset * 8)) & 0x0ffll)) + { + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE)); + if (!offset && !size && lit == (unsigned long long) - 1) + { + emitcode ("dec", "a"); + } + else if (!useCarry) + { + /* first add without previous c */ + emitcode ("add", "a,#!constbyte", (unsigned int) ((lit >> (offset * 8)) & 0x0ffll)); + useCarry = TRUE; + } + else + { + emitcode ("addc", "a,#!constbyte", (unsigned int) ((lit >> (offset * 8)) & 0x0ffll)); + } + aopPut (IC_RESULT (ic), "a", offset++); + } + else + { + /* no need to add zeroes */ + if (!sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)))) + { + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), offset, FALSE, FALSE), offset); + } + offset++; + } + } + } + else + { + operand *leftOp, *rightOp; + + leftOp = IC_LEFT (ic); + rightOp = IC_RIGHT (ic); + + while (size--) + { + if (aopGetUsesAcc (rightOp, offset)) + { + if (aopGetUsesAcc (leftOp, offset)) + { + bool pushedB; + + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + pushedB = pushB (); + emitcode ("mov", "b,a"); + if (offset == 0) + CLRC; + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + emitcode ("subb", "a,b"); + popB (pushedB); + } + else + { + /* reverse subtraction with 2's complement */ + if (offset == 0) + emitcode ("setb", "c"); + else + emitcode ("cpl", "c"); + wassertl (!aopGetUsesAcc (leftOp, offset), "accumulator clash"); + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + emitcode ("subb", "a,%s", aopGet (leftOp, offset, FALSE, FALSE)); + emitcode ("cpl", "a"); + if (size) /* skip if last byte */ + emitcode ("cpl", "c"); + } + } + else + { + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + if (offset == 0) + CLRC; + emitcode ("subb", "a,%s", aopGet (rightOp, offset, FALSE, FALSE)); + } + + aopPut (IC_RESULT (ic), "a", offset++); + } + } + + adjustArithmeticResult (ic); + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_RIGHT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + + +/*-----------------------------------------------------------------*/ +/* genMultbits :- multiplication of bits */ +/*-----------------------------------------------------------------*/ +static void +genMultbits (operand * left, operand * right, operand * result) +{ + D (emitcode (";", "genMultbits")); + + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + outBitC (result); +} + +/*-----------------------------------------------------------------*/ +/* genMultOneByte : 8*8=8/16 bit multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMultOneByte (operand * left, operand * right, operand * result) +{ + symbol *lbl; + int size = AOP_SIZE (result); + bool runtimeSign, compiletimeSign; + bool lUnsigned, rUnsigned, pushedB; + + D (emitcode (";", "genMultOneByte")); + + if (size < 1 || size > 2) + { + /* this should never happen */ + fprintf (stderr, "size!=1||2 (%d) in %s at line:%d \n", AOP_SIZE (result), __FILE__, lineno); + exit (EXIT_FAILURE); + } + + /* (if two literals: the value is computed before) */ + /* if one literal, literal on the right */ + if (AOP_TYPE (left) == AOP_LIT || AOP_TYPE (right) == AOP_ACC) + { + operand *t = right; + right = left; + left = t; + /* emitcode (";", "swapped left and right"); */ + } + /* if no literal, unsigned on the right: shorter code */ + if (AOP_TYPE (right) != AOP_LIT && SPEC_USIGN (getSpec (operandType (left)))) + { + operand *t = right; + right = left; + left = t; + } + + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + pushedB = pushB (); + + if (size == 1 /* no, this is not a bug; with a 1 byte result there's + no need to take care about the signedness! */ + || (lUnsigned && rUnsigned)) + { + /* just an unsigned 8 * 8 = 8 multiply + or 8u * 8u = 16u */ + /* emitcode (";","unsigned"); */ + /* TODO: check for accumulator clash between left & right aops? */ + + /*if (AOP_TYPE (right) == AOP_ACC) + MOVB (aopGet (left, 0, FALSE, FALSE)); + else*/ if (AOP_TYPE (right) == AOP_LIT) + { + /* moving to accumulator first helps peepholes */ + MOVA (aopGet (left, 0, FALSE, FALSE)); + MOVB (aopGet (right, 0, FALSE, FALSE)); + } + else + { + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + MOVA (aopGet (left, 0, FALSE, FALSE)); + } + + emitcode ("mul", "ab"); + aopPut (result, "a", 0); + if (size == 2) + aopPut (result, "b", 1); + + popB (pushedB); + return; + } + + /* we have to do a signed multiply */ + /* emitcode (";", "signed"); */ + + /* now sign adjust for both left & right */ + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + if (!lUnsigned) + { + if (AOP_TYPE (left) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + if (val < 0) + compiletimeSign = TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + if (!rUnsigned) + { + if (AOP_TYPE (right) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + compiletimeSign ^= TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + /* initialize F0, which stores the runtime sign */ + if (runtimeSign) + { + if (compiletimeSign) + emitcode ("setb", "F0"); /* set sign flag */ + else + emitcode ("clr", "F0"); /* reset sign flag */ + } + + /* save the signs of the operands */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#!constbyte", -val); + else + emitcode ("mov", "b,#!constbyte", (unsigned char) val); + } + else /* ! literal */ + { + if (rUnsigned) /* emitcode (";", "signed"); */ + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + emitcode ("mov", "a,#!constbyte", -val); + else + emitcode ("mov", "a,#!constbyte", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + + if (!lUnsigned) + { + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the multiplication */ + emitcode ("mul", "ab"); + if (runtimeSign || compiletimeSign) + { + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* lsb 2's complement */ + if (size != 2) + emitcode ("inc", "a"); /* inc doesn't set carry flag */ + else + { + emitcode ("add", "a,#0x01"); /* this sets carry flag */ + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); /* msb 2's complement */ + emitcode ("addc", "a,#0x00"); + emitcode ("xch", "a,b"); + } + emitLabel (lbl); + } + aopPut (result, "a", 0); + if (size == 2) + aopPut (result, "b", 1); + + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genMult")); + + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genMultbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ +#if 0 // one of them can be a sloc shared with the result + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) +#else + if (getSize (operandType (left)) == 1 && getSize (operandType (right)) == 1) +#endif + { + genMultOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + fprintf (stderr, "left: %d right: %d\n", getSize (OP_SYMBOL (left)->type), getSize (OP_SYMBOL (right)->type)); + assert (0); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genDivbits :- division of bits */ +/*-----------------------------------------------------------------*/ +static void +genDivbits (operand * left, operand * right, operand * result) +{ + bool pushedB; + + D (emitcode (";", "genDivbits")); + + pushedB = pushB (); + + /* the result must be bit */ + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + + MOVA (aopGet (left, 0, FALSE, FALSE)); + + emitcode ("div", "ab"); + emitcode ("rrc", "a"); + + popB (pushedB); + + aopPut (result, "c", 0); +} + +/*-----------------------------------------------------------------*/ +/* genDivOneByte : 8 bit division */ +/*-----------------------------------------------------------------*/ +static void +genDivOneByte (operand * left, operand * right, operand * result) +{ + bool lUnsigned, rUnsigned, pushedB; + bool runtimeSign, compiletimeSign; + bool accuse = FALSE; + bool pushedA = FALSE; + symbol *lbl; + int size, offset; + + D (emitcode (";", "genDivOneByte")); + + /* Why is it necessary that genDivOneByte() can return an int result? + Have a look at: + + volatile unsigned char uc; + volatile signed char sc1, sc2; + volatile int i; + + uc = 255; + sc1 = -1; + i = uc / sc1; + + Or: + + sc1 = -128; + sc2 = -1; + i = sc1 / sc2; + + In all cases a one byte result would overflow, the following cast to int + would return the wrong result. + + Two possible solution: + a) cast operands to int, if ((unsigned) / (signed)) or + ((signed) / (signed)) + b) return an 16 bit signed int; this is what we're doing here! + */ + + size = AOP_SIZE (result) - 1; + offset = 1; + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + pushedB = pushB (); + + /* signed or unsigned */ + if (lUnsigned && rUnsigned) + { + /* unsigned is easy */ + MOVB (aopGet (right, 0, FALSE, FALSE)); + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("div", "ab"); + aopPut (result, "a", 0); + while (size--) + aopPut (result, zero, offset++); + + popB (pushedB); + return; + } + + /* signed is a little bit more difficult */ + + /* now sign adjust for both left & right */ + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + if (!lUnsigned) + { + if (AOP_TYPE (left) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + if (val < 0) + compiletimeSign = TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + if (!rUnsigned) + { + if (AOP_TYPE (right) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + compiletimeSign ^= TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + /* initialize F0, which stores the runtime sign */ + if (runtimeSign) + { + if (compiletimeSign) + emitcode ("setb", "F0"); /* set sign flag */ + else + emitcode ("clr", "F0"); /* reset sign flag */ + } + + /* save the signs of the operands */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#0x%02x", -val); + else + emitcode ("mov", "b,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + if (rUnsigned) + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + emitcode ("mov", "a,#0x%02x", -val); + else + emitcode ("mov", "a,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + + if (!lUnsigned) + { + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the division */ + emitcode ("div", "ab"); + + if (runtimeSign || compiletimeSign) + { + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* lsb 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + + accuse = aopPut (result, "a", 0); + if (size > 0) + { + /* msb is 0x00 or 0xff depending on the sign */ + if (runtimeSign) + { + if (accuse) + { + emitpush ("acc"); + pushedA = TRUE; + } + emitcode ("mov", "c,F0"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (result, "a", offset++); + } + else /* compiletimeSign */ + { + if (aopPutUsesAcc (result, "#0xff", offset)) + { + emitpush ("acc"); + pushedA = TRUE; + } + while (size--) + aopPut (result, "#0xff", offset++); + } + } + } + else + { + aopPut (result, "a", 0); + while (size--) + aopPut (result, zero, offset++); + } + + if (pushedA) + emitpop ("acc"); + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genDiv - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genDiv (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genDiv")); + + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genDivbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genDivOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genModbits :- modulus of bits */ +/*-----------------------------------------------------------------*/ +static void +genModbits (operand * left, operand * right, operand * result) +{ + bool pushedB; + + D (emitcode (";", "genModbits")); + + pushedB = pushB (); + + /* the result must be bit */ + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + + MOVA (aopGet (left, 0, FALSE, FALSE)); + + emitcode ("div", "ab"); + emitcode ("mov", "a,b"); + emitcode ("rrc", "a"); + + popB (pushedB); + + aopPut (result, "c", 0); +} + +/*-----------------------------------------------------------------*/ +/* genModOneByte : 8 bit modulus */ +/*-----------------------------------------------------------------*/ +static void +genModOneByte (operand * left, operand * right, operand * result) +{ + bool lUnsigned, rUnsigned, pushedB; + bool runtimeSign, compiletimeSign; + symbol *lbl; + int size, offset; + + D (emitcode (";", "genModOneByte")); + + size = AOP_SIZE (result) - 1; + offset = 1; + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + /* if right is a literal, check it for 2^n */ + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned char val = abs ((int) operandLitValue (right)); + symbol *lbl2 = NULL; + + switch (val) + { + case 1: /* sometimes it makes sense (on tricky code and hardware)... */ + case 2: + case 4: + case 8: + case 16: + case 32: + case 64: + case 128: + if (lUnsigned) + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "modulus of unsigned char by 2^n literal shouldn't be processed here"); + /* because iCode should have been changed to genAnd */ + /* see file "SDCCopt.c", function "convertToFcall()" */ + + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("mov", "c,acc.7"); + emitcode ("anl", "a,#0x%02x", val - 1); + lbl = newiTempLabel (NULL); + emitcode ("jz", "!tlabel", labelKey2num (lbl->key)); + emitcode ("jnc", "!tlabel", labelKey2num (lbl->key)); + emitcode ("orl", "a,#0x%02x", 0xff ^ (val - 1)); + if (size) + { + int size2 = size; + int offs2 = offset; + + aopPut (result, "a", 0); + while (size2--) + aopPut (result, "#0xff", offs2++); + lbl2 = newiTempLabel (NULL); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl2->key)); + } + emitLabel (lbl); + aopPut (result, "a", 0); + while (size--) + aopPut (result, zero, offset++); + if (lbl2) + { + emitLabel (lbl2); + } + return; + + default: + break; + } + } + + pushedB = pushB (); + + /* signed or unsigned */ + if (lUnsigned && rUnsigned) + { + /* unsigned is easy */ + MOVB (aopGet (right, 0, FALSE, FALSE)); + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("div", "ab"); + aopPut (result, "b", 0); + while (size--) + aopPut (result, zero, offset++); + + popB (pushedB); + return; + } + + /* signed is a little bit more difficult */ + + /* now sign adjust for both left & right */ + + /* modulus: sign of the right operand has no influence on the result! */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (signed char) operandLitValue (right); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#0x%02x", -val); + else + emitcode ("mov", "b,#0x%02x", (unsigned char) val); + } + else /* not literal */ + { + if (rUnsigned) + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + /* sign adjust left side */ + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + { + compiletimeSign = TRUE; /* set sign flag */ + emitcode ("mov", "a,#0x%02x", -val); + } + else + emitcode ("mov", "a,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + + if (!lUnsigned) + { + runtimeSign = TRUE; + emitcode ("clr", "F0"); /* clear sign flag */ + + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("setb", "F0"); /* set sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the modulus */ + emitcode ("div", "ab"); + + if (runtimeSign || compiletimeSign) + { + emitcode ("mov", "a,b"); + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + + aopPut (result, "a", 0); + if (size > 0) + { + /* msb is 0x00 or 0xff depending on the sign */ + if (runtimeSign) + { + emitcode ("mov", "c,F0"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (result, "a", offset++); + } + else /* compiletimeSign */ + while (size--) + aopPut (result, "#0xff", offset++); + } + } + else + { + aopPut (result, "b", 0); + while (size--) + aopPut (result, zero, offset++); + } + + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genMod - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genMod (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genMod")); + + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genModbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genModOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genIfxJump :- will create a jump depending on the ifx */ +/*-----------------------------------------------------------------*/ +static void +genIfxJump (iCode * ic, const char *jval, operand * left, operand * right, operand * result, iCode * popIc) +{ + symbol *jlbl; + symbol *tlbl = newiTempLabel (NULL); + char *inst; + + /* if there is something to be popped then do it first */ + popForBranch (popIc, TRUE); + + D (emitcode (";", "genIfxJump")); + + /* if true label then we jump if condition + supplied is true */ + if (IC_TRUE (ic)) + { + jlbl = IC_TRUE (ic); + inst = ((EQ (jval, "a") ? "jz" : (EQ (jval, "c") ? "jnc" : "jnb"))); + } + else + { + /* false label is present */ + jlbl = IC_FALSE (ic); + inst = ((EQ (jval, "a") ? "jnz" : (EQ (jval, "c") ? "jc" : "jb"))); + } + if (EQ (inst, "jb") || EQ (inst, "jnb")) + emitcode (inst, "%s,!tlabel", jval, labelKey2num (tlbl->key)); + else + emitcode (inst, "!tlabel", labelKey2num (tlbl->key)); + freeForBranchAsmops (result, right, left, ic); + emitcode ("ljmp", "!tlabel", labelKey2num (jlbl->key)); + emitLabel (tlbl); + + /* mark the icode as generated */ + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genCmp :- greater or less than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmp (operand * left, operand * right, operand * result, iCode * ifx, int sign, iCode * ic) +{ + int size, offset = 0; + unsigned long long lit = 0L; + bool rightInB; + + D (emitcode (";", "genCmp")); + + /* if left & right are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + /* generic pointers require special handling since all NULL pointers must compare equal */ + else if (opIsGptr (left) || opIsGptr (right)) + { + /* push right */ + while (offset < GPTRSIZE) + { + emitpush (aopGet (right, offset++, FALSE, TRUE)); + } + loadDptrFromOperand (left, TRUE); + emitcode ("lcall", "___gptr_cmp"); + for (offset = 0; offset < GPTRSIZE; offset++) + emitpop (NULL); + } + else + { + /* subtract right from left if at the + end the carry flag is set then we know that + left is greater than right */ + size = max (AOP_SIZE (left), AOP_SIZE (right)); + + /* if unsigned char cmp with lit, do cjne left,#right,zz */ + if (size == 1 && !sign && AOP_TYPE (right) == AOP_LIT && AOP_TYPE (left) != AOP_DIR && AOP_TYPE (left) != AOP_STR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + symbol *lbl = newiTempLabel (NULL); + emitcode ("cjne", "%s,%s,!tlabel", l, aopGet (right, offset, FALSE, FALSE), labelKey2num (lbl->key)); + Safe_free (l); + emitLabel (lbl); + } + else + { + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + /* optimize if(x < 0) or if(x >= 0) */ + if (lit == 0ll) + { + if (!sign) + { + CLRC; + } + else + { + MOVA (aopGet (left, AOP_SIZE (left) - 1, FALSE, FALSE)); + if (!(AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) && ifx) + { + genIfxJump (ifx, "acc.7", left, right, result, ic->next); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); + + return; + } + else + { + emitcode ("rlc", "a"); + } + } + goto release; + } + else + { + //nonzero literal + int bytelit = ((lit >> (offset * 8)) & 0x0ffll); + while (size && (bytelit == 0)) + { + offset++; + bytelit = ((lit >> (offset * 8)) & 0x0ffll); + size--; + } + CLRC; + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + if (sign && size == 0) + { + emitcode ("xrl", "a,#0x80"); + emitcode ("subb", "a,#0x%02x", 0x80 ^ (unsigned int) ((lit >> (offset * 8)) & 0x0ffll)); + } + else + { + emitcode ("subb", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + offset++; + } + goto release; + } + } + CLRC; + while (size--) + { + bool pushedB = FALSE; + rightInB = aopGetUsesAcc (right, offset); + if (rightInB) + { + pushedB = pushB (); + emitcode ("mov", "b,%s", aopGet (right, offset, FALSE, FALSE)); + } + MOVA (aopGet (left, offset, FALSE, FALSE)); + if (sign && size == 0) + { + emitcode ("xrl", "a,#0x80"); + if (!rightInB) + { + pushedB = pushB (); + rightInB++; + MOVB (aopGet (right, offset, FALSE, FALSE)); + } + emitcode ("xrl", "b,#0x80"); + emitcode ("subb", "a,b"); + } + else + { + if (rightInB) + emitcode ("subb", "a,b"); + else + emitcode ("subb", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + if (rightInB) + popB (pushedB); + offset++; + } + } + } + +release: + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + outBitC (result); + } + else + { + /* if the result is used in the next + ifx conditional branch then generate + code a little differently */ + if (ifx) + { + genIfxJump (ifx, "c", NULL, NULL, result, ic->next); + } + else + { + outBitC (result); + } + /* 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 = 0; + + D (emitcode (";", "genCmpGt")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !((SPEC_USIGN (letype) && !(IS_CHAR (letype) && IS_LITERAL (letype))) || + (SPEC_USIGN (retype) && !(IS_CHAR (retype) && IS_LITERAL (retype)))); + } + /* assign the asmops */ + aopOp (result, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + + genCmp (right, left, result, ifx, sign, ic); + + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCmpLt - less than comparisons */ +/*-----------------------------------------------------------------*/ +static void +genCmpLt (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign = 0; + + D (emitcode (";", "genCmpLt")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !((SPEC_USIGN (letype) && !(IS_CHAR (letype) && IS_LITERAL (letype))) || + (SPEC_USIGN (retype) && !(IS_CHAR (retype) && IS_LITERAL (retype)))); + } + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + genCmp (left, right, result, ifx, sign, ic); + + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* 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; + + D (emitcode (";", "gencjneshort")); + + /* if the left side is a immediate/literal or + if the right is in a pointer register and left is not */ + if (IS_AOP_IMMEDIATE (left) || + (AOP_TYPE (left) == AOP_DIR && !IS_AOP_IMMEDIATE (right)) || + (IS_AOP_PREG (right) && !IS_AOP_PREG (left))) + { + operand *t = right; + right = left; + left = t; + } + + /* generic pointers require special handling since all NULL pointers must compare equal */ + if (opIsGptr (left) || opIsGptr (right)) + { + /* push right */ + while (offset < size) + { + emitpush (aopGet (right, offset++, FALSE, TRUE)); + } + loadDptrFromOperand (left, TRUE); + emitcode ("lcall", "___gptr_cmp"); + for (offset = 0; offset < GPTRSIZE; offset++) + emitpop (NULL); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + + /* if the right side is a literal then anything goes */ + else if (IS_AOP_IMMEDIATE (right) && + AOP_TYPE (left) != AOP_DIR && !IS_AOP_IMMEDIATE (left)) + { + while (size--) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + const char *r = aopGet (right, offset, FALSE, FALSE); + if (EQ (l, "a") && EQ (r, zero)) + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + else + emitcode ("cjne", "%s,%s,!tlabel", l, r, labelKey2num (lbl->key)); + Safe_free (l); + 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 || + IS_AOP_IMMEDIATE (right) || + (IS_AOP_PREG (left) && !IS_AOP_PREG (right))) + { + if (AOP_TYPE (right) == AOP_LIT) + { + int val[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + bool chk[8] = {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}; + int rsize = AOP_SIZE (right); + int pidx = -1; + int lidx, cidx; + unsigned long long lit = ullFromVal (AOP (right)->aopu.aop_lit); + + switch (rsize) + { + case 8: + val[7] = (lit >> 56) & 0xff; + chk[7] = TRUE; + val[6] = (lit >> 48) & 0xff; + chk[6] = TRUE; + val[5] = (lit >> 40) & 0xff; + chk[5] = TRUE; + val[4] = (lit >> 32) & 0xff; + chk[4] = TRUE; + //fallthrough + case 4: + val[3] = (lit >> 24) & 0xff; + chk[3] = TRUE; + //fallthrough + case 3: + val[2] = (lit >> 16) & 0xff; + chk[2] = TRUE; + //fallthrough + case 2: + val[1] = (lit >> 8) & 0xff; + chk[1] = TRUE; + //fallthrough + default: + val[0] = (lit >> 0) & 0xff; + chk[0] = TRUE; + } + if (optimize.codeSize && (rsize > 1)) + { + if ((!chk[0] || val[0] == 0x00) && + (!chk[1] || val[1] == 0x00) && + (!chk[2] || val[2] == 0x00) && + (!chk[3] || val[3] == 0x00) && + (!chk[4] || val[4] == 0x00) && + (!chk[5] || val[5] == 0x00) && + (!chk[6] || val[6] == 0x00) && + (!chk[7] || val[7] == 0x00)) + { + MOVA(aopGet(left, 0, FALSE, FALSE)); + for (cidx = 1; cidx < size; cidx++) + if (chk[cidx]) + emitcode ("orl", "a,%s", aopGet(left, cidx, FALSE, FALSE)); + emitcode ("jnz", "%05d$", lbl->key + 100); + return; + } + if ((!chk[0] || val[0] == 0xFF) && + (!chk[1] || val[1] == 0xFF) && + (!chk[2] || val[2] == 0xFF) && + (!chk[3] || val[3] == 0xFF) && + (!chk[4] || val[4] == 0xFF) && + (!chk[5] || val[5] == 0xFF) && + (!chk[6] || val[6] == 0xFF) && + (!chk[7] || val[7] == 0xFF)) + { + MOVA(aopGet(left, 0, FALSE, FALSE)); + for (cidx = 1; cidx < size; cidx++) + if (chk[cidx]) + emitcode ("anl", "a,%s", aopGet(left, cidx, FALSE, FALSE)); + emitcode ("cjne", "a,#0xFF,%05d$", lbl->key + 100); + return; + } + } + + for (lidx = 0; lidx < size; lidx++) + { + if (chk[lidx] && !aopGetUsesAcc(left, lidx)) + { + if (pidx >= 0) + { + if (val[pidx] == val[lidx]) + { + chk[lidx] = FALSE; + } + if ((~val[pidx] & 0xff) == val[lidx]) + { + emitcode ("cpl", "a"); + chk[lidx] = FALSE; + } + else if (((val[pidx] + 1) & 0xff) == val[lidx]) + { + emitcode ("inc", "a"); + chk[lidx] = FALSE; + } + else if (((val[pidx] - 1) & 0xff) == val[lidx]) + { + emitcode ("dec", "a"); + chk[lidx] = FALSE; + } + } + pidx = lidx; + if (chk[lidx]) + { + MOVA(aopGet(right, lidx, FALSE, FALSE)); + chk[lidx] = FALSE; + } + emitcode ("cjne", "a,%s,%05d$", aopGet(left, lidx, FALSE, TRUE), lbl->key + 100); + + for (cidx = lidx + 1; cidx < size; cidx++) + { + if (chk[cidx] && val[lidx] == val[cidx] && !IS_AOP_PREG (left)) + { + chk[cidx] = FALSE; + emitcode ("cjne", "a,%s,%05d$", aopGet(left, cidx, FALSE, TRUE), lbl->key + 100); + } + } + } + } + return; + } + + while (size--) + { + const char *r; + MOVA (aopGet (left, offset, FALSE, FALSE)); + r = aopGet (right, offset, FALSE, TRUE); + if (EQ (r, zero)) + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + else + emitcode ("cjne", "a,%s,!tlabel", r, labelKey2num (lbl->key)); + offset++; + } + } + else + { + /* right is a pointer reg need both a & b */ + while (size--) + { + //if B in use: push B; mov B,left; mov A,right; clrc; subb A,B; pop B; jnz + wassertl (!BINUSE, "B was in use"); + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("cjne", "a,b,!tlabel", labelKey2num (lbl->key)); + offset++; + } + } +} + +/*-----------------------------------------------------------------*/ +/* gencjne - compare and jump if not equal */ +/*-----------------------------------------------------------------*/ +static void +gencjne (operand * left, operand * right, symbol * lbl, bool useCarry) +{ + symbol *tlbl = newiTempLabel (NULL); + + D (emitcode (";", "gencjne")); + + gencjneshort (left, right, lbl); + + if (useCarry) + SETC; + else + MOVA (one); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl->key)); + emitLabel (lbl); + if (useCarry) + CLRC; + else + MOVA (zero); + emitLabel (tlbl); +} + +/*-----------------------------------------------------------------*/ +/* genCmpEq - generates code for equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpEq (iCode * ic, iCode * ifx) +{ + bool swappedLR = FALSE; + operand *left, *right, *result; + iCode *popIc = ic->next; + + D (emitcode (";", "genCmpEq")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + /* if literal, literal on the right or + if the right is in a pointer register and left + is not */ + if ((AOP_TYPE (left) == AOP_LIT) || (IS_AOP_PREG (right) && !IS_AOP_PREG (left))) + { + swapOperands (&left, &right); + swappedLR = TRUE; + } + + 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))) + { + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long lit = ulFromVal (AOP (right)->aopu.aop_lit); + if (lit == 0L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + else if (lit == 1L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("clr", "c"); + } + /* AOP_TYPE(right) == AOP_CRY */ + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("jb", "%s,!tlabel", AOP (right)->aopu.aop_dir, labelKey2num ((lbl->key))); + emitcode ("cpl", "c"); + emitLabel (lbl); + } + /* if true label then we jump if condition + supplied is true */ + tlbl = newiTempLabel (NULL); + if (IC_TRUE (ifx)) + { + emitcode ("jnc", "!tlabel", labelKey2num (tlbl->key)); + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + } + else + { + emitcode ("jc", "!tlabel", labelKey2num (tlbl->key)); + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ifx)->key)); + } + emitLabel (tlbl); + } + else + { + tlbl = newiTempLabel (NULL); + gencjneshort (left, right, tlbl); + if (IC_TRUE (ifx)) + { + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + emitLabel (tlbl); + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl->key)); + emitLabel (tlbl); + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ifx)->key)); + emitLabel (lbl); + } + } + /* 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))) + { + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long lit = ulFromVal (AOP (right)->aopu.aop_lit); + if (lit == 0L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + else if (lit == 1L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("clr", "c"); + } + /* AOP_TYPE(right) == AOP_CRY */ + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("jb", "%s,!tlabel", AOP (right)->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + } + /* c = 1 if egal */ + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + outBitC (result); + goto release; + } + if (ifx) + { + genIfxJump (ifx, "c", left, right, result, popIc); + goto release; + } + /* if the result is used in an arithmetic operation + then put the result in place */ + outBitC (result); + } + else + { + bool useCarry = (AOP_TYPE (result) == AOP_CRY); + gencjne (left, right, newiTempLabel (NULL), useCarry); + if (useCarry && AOP_SIZE (result)) + { + aopPut (result, "c", 0); + goto release; + } + if (ifx) + { + genIfxJump (ifx, useCarry ? "c" : "a", left, right, result, popIc); + goto release; + } + /* if the result is used in an arithmetic operation + then put the result in place */ + if (!useCarry) + outAcc (result); + /* leave the result in acc */ + } + +release: + freeAsmop (result, NULL, ic, TRUE); + if (swappedLR) + swapOperands (&left, &right); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* hasInc - operand is incremented before any other use */ +/*-----------------------------------------------------------------*/ +static iCode * +hasInc (operand * op, iCode * ic, int osize) +{ + sym_link *type = operandType (op); + sym_link *retype = getSpec (type); + iCode *lic = ic->next; + int isize; + + /* this could from a cast, e.g.: "(char xdata *) 0x7654;" */ + if (!IS_SYMOP (op)) + return NULL; + + if (IS_BITVAR (retype) || !IS_PTR (type)) + return NULL; + if (IS_AGGREGATE (type->next)) + return NULL; + if (osize != (isize = getSize (type->next))) + return NULL; + + while (lic) + { + /* if operand of the form op = op + <sizeof *op> */ + if (lic->op == '+' && isOperandEqual (IC_LEFT (lic), op) && + isOperandEqual (IC_RESULT (lic), op) && + isOperandLiteral (IC_RIGHT (lic)) && operandLitValue (IC_RIGHT (lic)) == isize) + { + return lic; + } + /* if the operand used or deffed */ + if (bitVectBitValue (OP_USES (op), lic->key) || lic->defKey == op->key) + { + return NULL; + } + /* if GOTO or IFX */ + if (lic->op == IFX || lic->op == GOTO || lic->op == LABEL) + break; + lic = lic->next; + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* genAndOp - for && operation */ +/*-----------------------------------------------------------------*/ +static void +genAndOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + + D (emitcode (";", "genAndOp")); + + /* 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); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + /* if both are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + outBitC (result); + } + else + { + tlbl = newiTempLabel (NULL); + toBoolean (left); + emitcode ("jz", "!tlabel", labelKey2num (tlbl->key)); + toBoolean (right); + emitLabel (tlbl); + outBitAcc (result); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + + +/*-----------------------------------------------------------------*/ +/* genOrOp - for || operation */ +/*-----------------------------------------------------------------*/ +static void +genOrOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + + D (emitcode (";", "genOrOp")); + + /* 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); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + /* if both are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("orl", "c,%s", AOP (right)->aopu.aop_dir); + outBitC (result); + } + else + { + tlbl = newiTempLabel (NULL); + toBoolean (left); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + toBoolean (right); + emitLabel (tlbl); + outBitAcc (result); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long long lit) +{ + unsigned long long w = 1; + int idx; + + for (idx = 0; idx < 64; idx++, w<<=1) + if (lit == w) + return idx + 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* continueIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +continueIfTrue (iCode * ic, iCode * popIc) +{ + if (IC_TRUE (ic)) + { + popForBranch (popIc, TRUE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ic)->key)); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* jmpIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +jumpIfTrue (iCode * ic, iCode * popIc) +{ + if (!IC_TRUE (ic)) + { + popForBranch (popIc, TRUE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ic)->key)); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* jmpTrueOrFalse - */ +/*-----------------------------------------------------------------*/ +static void +jmpTrueOrFalse (iCode * ic, symbol * tlbl, operand * left, operand * right, operand * result, iCode * popIc) +{ + // ugly but optimized by peephole + if (IC_TRUE (ic)) + { + symbol *nlbl = newiTempLabel (NULL); + emitcode ("sjmp", "!tlabel", labelKey2num (nlbl->key)); + emitLabel (tlbl); + popForBranch (popIc, FALSE); + freeForBranchAsmops (result, right, left, ic); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ic)->key)); + emitLabel (nlbl); + } + else + { + popForBranch (popIc, FALSE); + freeForBranchAsmops (result, right, left, ic); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ic)->key)); + emitLabel (tlbl); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long long lit = 0ull; + int bytelit = 0; + + D (emitcode (";", "genAnd")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + 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 = ullFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit & yy) + // result = bit & yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit & literal; + if (lit & 1) + { + if (size && sameRegs (AOP (result), AOP (left))) + // no change + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // bit(result) = 0; + if (size && (AOP_TYPE (result) == AOP_CRY)) + { + emitcode ("clr", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + jumpIfTrue (ifx, ic->next); + goto release; + } + emitcode ("clr", "c"); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit & bit; + if (IS_OP_ACCUSE (left)) + { + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + } + else if (IS_OP_ACCUSE (right)) + { + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else if (AOP_TYPE (right) == AOP_DIR && IS_BOOL (operandType (right)) && AOP_TYPE (left) == AOP_CRY) + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + emitcode ("anl", "c,acc.0"); + } + else + { + // c = bit & val; + MOVA (aopGet (right, 0, FALSE, FALSE)); + // c = lsb + emitcode ("rrc", "a"); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit & ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", left, right, result, ic->next); + 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)) + { + int posbit = isLiteralBit (lit); + /* left & 2^n */ + if (posbit) + { + posbit--; + MOVA (aopGet (left, posbit >> 3, FALSE, FALSE)); + // bit = left & 2^n + if (size) + { + switch (posbit & 0x07) + { + case 0: + emitcode ("rrc", "a"); + break; + case 7: + emitcode ("rlc", "a"); + break; + default: + emitcode ("mov", "c,acc.%d", posbit & 0x07); + break; + } + } + // if(left & 2^n) + else + { + if (ifx) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "acc.%d", posbit & 0x07); + genIfxJump (ifx, dbuf_c_str (&dbuf), left, right, result, ic->next); + dbuf_destroy (&dbuf); + } + else + { + emitcode ("anl", "a,#!constbyte", 1 << (posbit & 0x07)); + } + goto release; + } + } + else + { + symbol *tlbl = newiTempLabel (NULL); + int sizel = AOP_SIZE (left); + if (size) + emitcode ("setb", "c"); + while (sizel--) + { + if ((bytelit = ((lit >> (offset * 8)) & 0x0FFL)) != 0x0L) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + // byte == 2^n ? + if ((posbit = isLiteralBit (bytelit)) != 0) + emitcode ("jb", "acc.%d,!tlabel", (posbit - 1) & 0x07, labelKey2num (tlbl->key)); + else + { + if (bytelit != 0x0FFL) + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, TRUE)); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + } + offset++; + } + // bit = left & literal + if (size) + { + emitcode ("clr", "c"); + emitLabel (tlbl); + } + // if(left & literal) + else + { + if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (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) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0ffull); + if (bytelit == 0x0FF) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + else + continue; + } + else if (bytelit == 0) + { + aopPut (result, zero, offset); + } + else if (IS_AOP_PREG (result)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else if (AOP_TYPE (left) != AOP_DPTR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("anl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("anl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + aopPut (result, "a", offset); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (IS_AOP_PREG (result)) + { + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, TRUE)); + aopPut (result, "a", offset); + } + else + { + emitcode ("anl", "%s,a", aopGet (left, offset, FALSE, TRUE)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left & right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = min (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left & right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0ffull); + if (bytelit == 0x0FF) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + continue; + } + else if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + aopPut (result, zero, offset); + continue; + } + else if (AOP_TYPE (left) == AOP_ACC) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + aopPut (result, l, offset); + Safe_free (l); + continue; + } + } + // faster than result <- left, anl result,right + // and better if result is SFR + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + aopPut (result, "a", offset); + } + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + int bytelit = 0; + + D (emitcode (";", "genOr")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + 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 = ulFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit | yy) + // xx = bit | yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit | literal; + if (lit) + { + // lit != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx, ic->next); + goto release; + } + emitcode ("setb", "c"); + } + else + { + // lit == 0 => result = left + if (size && sameRegs (AOP (result), AOP (left))) + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit | bit; + if (IS_OP_ACCUSE (left)) + { + emitcode ("orl", "c,%s", AOP (right)->aopu.aop_dir); + } + else if (IS_OP_ACCUSE (right)) + { + emitcode ("orl", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("orl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else + { + // c = bit | val; + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("jb", "%s,!tlabel", AOP (left)->aopu.aop_dir, labelKey2num (tlbl->key)); + toBoolean (right); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + goto release; + } + else + { + toCarry (right); + emitcode ("orl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit | ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", left, right, result, ic->next); + 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)) + { + if (lit) + { + // result = 1 + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx, ic->next); + goto release; + } + else + { + // lit = 0, result = boolean(left) + if (size) + SETC; + toBoolean (left); + if (size) + { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + CLRC; + emitLabel (tlbl); + } + else + { + /* FIXME, thats pretty fishy, check for ifx!=0, testcase .. */ + assert (ifx); + genIfxJump (ifx, "a", left, right, result, ic->next); + 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) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + else + continue; + } + else if (bytelit == 0x0FF) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + aopPut (result, "#0xff", offset); + } + else if (IS_AOP_PREG (left)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else if (AOP_TYPE (left) != AOP_DPTR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("orl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("orl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + aopPut (result, "a", offset); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (IS_AOP_PREG (left)) + { + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, TRUE)); + aopPut (result, "a", offset); + } + else + { + emitcode ("orl", "%s,a", aopGet (left, offset, FALSE, TRUE)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left | right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left | right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + continue; + } + else if (bytelit == 0x0FF) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + aopPut (result, "#0xff", offset); + continue; + } + else if (AOP_TYPE (left) == AOP_ACC) + { + // this should be the only use of left so A,B can be overwritten + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + aopPut (result, l, offset); + Safe_free (l); + continue; + } + } + // faster than result <- left, orl result,right + // and better if result is SFR + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + aopPut (result, "a", offset); + } + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for xclusive or */ +/*-----------------------------------------------------------------*/ +static void +genXor (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + int bytelit = 0; + + D (emitcode (";", "genXor")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not || + if left needs acc & right does not */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || (AOP_NEEDSACC (left) && !AOP_NEEDSACC (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + 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 = ulFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit ^ yy) + // xx = bit ^ yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit ^ literal; + if (lit >> 1) + { + // lit>>1 != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx, ic->next); + goto release; + } + emitcode ("setb", "c"); + } + else + { + // lit == (0 or 1) + if (lit == 0) + { + // lit == 0, result = left + if (size && sameRegs (AOP (result), AOP (left))) + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // lit == 1, result = not(left) + if (size && sameRegs (AOP (result), AOP (left))) + { + emitcode ("cpl", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + else + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + } + } + } + else + { + // right != literal + symbol *tlbl = newiTempLabel (NULL); + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit ^ bit; + if (IS_OP_ACCUSE (left)) + { + // left already is in the carry + operand *tmp = right; + right = left; + left = tmp; + } + else + { + toCarry (right); + } + } + else + { + // c = bit ^ val + toCarry (right); + } + emitcode ("jnb", "%s,!tlabel", AOP (left)->aopu.aop_dir, labelKey2num (tlbl->key)); + emitcode ("cpl", "c"); + emitLabel (tlbl); + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit ^ ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", left, right, result, ic->next); + goto release; + } + + /* if left is same as result */ + if (sameRegs (AOP (result), AOP (left))) + { + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + else + continue; + } + else if (IS_AOP_PREG (left)) + { + MOVA (aopGet (left, offset, FALSE, TRUE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else if (AOP_TYPE (left) != AOP_DPTR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("xrl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("xrl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + aopPut (result, "a", offset); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (IS_AOP_PREG (left)) + { + emitcode ("xrl", "a,%s", aopGet (left, offset, FALSE, TRUE)); + aopPut (result, "a", offset); + } + else + { + emitcode ("xrl", "%s,a", aopGet (left, offset, FALSE, TRUE)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left ^ right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_LIT) && (((lit >> (offset * 8)) & 0x0FFL) == 0x00L)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + } + else if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left ^ right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + continue; + } + else if (AOP_TYPE (left) == AOP_ACC) + { + // this should be the only use of left so A,B can be overwritten + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + aopPut (result, l, offset); + Safe_free (l); + continue; + } + } + // faster than result <- left, xrl result,right + // and better if result is SFR + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + aopPut (result, "a", offset); + } + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genRRC - rotate right with carry */ +/*-----------------------------------------------------------------*/ +static void +genRRC (iCode * ic) +{ + operand *left, *result; + int size, offset; + + D (emitcode (";", "genRRC")); + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* move it to the result */ + size = AOP_SIZE (result); + offset = size - 1; + if (size == 1) + { + /* special case for 1 byte */ + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rr", "a"); + goto release; + } + /* no need to clear carry, bit7 will be written later */ + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + if (AOP_SIZE (result) > 1) + aopPut (result, "a", offset--); + } + /* now we need to put the carry into the + highest order byte of the result */ + if (AOP_SIZE (result) > 1) + { + MOVA (aopGet (result, AOP_SIZE (result) - 1, FALSE, FALSE)); + } + emitcode ("mov", "acc.7,c"); +release: + aopPut (result, "a", AOP_SIZE (result) - 1); + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRLC - generate code for rotate left with carry */ +/*-----------------------------------------------------------------*/ +static void +genRLC (iCode * ic) +{ + operand *left, *result; + int size, offset; + + D (emitcode (";", "genRLC")); + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* move it to the result */ + size = AOP_SIZE (result); + offset = 0; + if (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + if (size == 0) + { + /* special case for 1 byte */ + emitcode ("rl", "a"); + goto release; + } + emitcode ("rlc", "a"); /* bit0 will be written later */ + if (AOP_SIZE (result) > 1) + { + aopPut (result, "a", offset++); + } + + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + if (AOP_SIZE (result) > 1) + aopPut (result, "a", offset++); + } + } + /* now we need to put the carry into the + highest order byte of the result */ + if (AOP_SIZE (result) > 1) + { + MOVA (aopGet (result, 0, FALSE, FALSE)); + } + emitcode ("mov", "acc.0,c"); +release: + aopPut (result, "a", 0); + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetAbit - generates code get a single bit */ +/*-----------------------------------------------------------------*/ +static void +genGetAbit (iCode * ic) +{ + operand *left, *right, *result; + int shCount; + + D (emitcode (";", "genGetAbit")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + shCount = (int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + + /* get the needed byte into a */ + MOVA (aopGet (left, shCount / 8, FALSE, FALSE)); + shCount %= 8; + if (AOP_TYPE (result) == AOP_CRY) + { + if ((shCount) == 7) + emitcode ("rlc", "a"); + else if ((shCount) == 0) + emitcode ("rrc", "a"); + else + emitcode ("mov", "c,acc[%d]", shCount); + outBitC (result); + } + else + { + switch (shCount) + { + case 2: + emitcode ("rr", "a"); + //fallthrough + case 1: + emitcode ("rr", "a"); + //fallthrough + case 0: + emitcode ("anl", "a,#0x01"); + break; + case 3: + case 5: + emitcode ("mov", "c,acc[%d]", shCount); + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + break; + case 4: + emitcode ("swap", "a"); + emitcode ("anl", "a,#0x01"); + break; + case 6: + emitcode ("rl", "a"); + //fallthrough + case 7: + emitcode ("rl", "a"); + emitcode ("anl", "a,#0x01"); + break; + } + outAcc (result); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetByte - generates code get a single byte */ +/*-----------------------------------------------------------------*/ +static void +genGetByte (iCode * ic) +{ + operand *left, *right, *result; + int offset; + + D (emitcode (";", "genGetByte")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + offset = (int) ulFromVal (AOP (right)->aopu.aop_lit) / 8; + aopPut (result, aopGet (left, offset, FALSE, FALSE), 0); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetWord - generates code get two bytes */ +/*-----------------------------------------------------------------*/ +static void +genGetWord (iCode * ic) +{ + operand *left, *right, *result; + int offset; + + D (emitcode (";", "genGetWord")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + offset = (int) ulFromVal (AOP (right)->aopu.aop_lit) / 8; + aopPut (result, aopGet (left, offset, FALSE, FALSE), 0); + aopPut (result, aopGet (left, offset + 1, FALSE, FALSE), 1); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genSwap - generates code to swap nibbles or bytes */ +/*-----------------------------------------------------------------*/ +static void +genSwap (iCode * ic) +{ + operand *left, *result; + + D (emitcode (";", "genSwap")); + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + switch (AOP_SIZE (left)) + { + case 1: /* swap nibbles in byte */ + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("swap", "a"); + aopPut (result, "a", 0); + break; + case 2: /* swap bytes in word */ + if (AOP_TYPE (left) == AOP_REG && sameRegs (AOP (left), AOP (result))) + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + aopPut (result, aopGet (left, 1, FALSE, FALSE), 0); + aopPut (result, "a", 1); + } + else if (operandsEqu (left, result)) + { + char *reg = "a"; + bool pushedB = FALSE, leftInB = FALSE; + + MOVA (aopGet (left, 0, FALSE, FALSE)); + if (aopGetUsesAcc (left, 1) || aopGetUsesAcc (result, 0)) + { + pushedB = pushB (); + emitcode ("mov", "b,a"); + reg = "b"; + leftInB = TRUE; + } + aopPut (result, aopGet (left, 1, FALSE, FALSE), 0); + aopPut (result, reg, 1); + + if (leftInB) + popB (pushedB); + } + else + { + aopPut (result, aopGet (left, 1, FALSE, FALSE), 0); + aopPut (result, aopGet (left, 0, FALSE, FALSE), 1); + } + break; + default: + wassertl (FALSE, "unsupported SWAP operand size"); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* AccRol - rotate left accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRol (int shCount) +{ + shCount &= 0x0007; // shCount : 0..7 + + switch (shCount) + { + case 0: + break; + case 1: + emitcode ("rl", "a"); + break; + case 2: + emitcode ("rl", "a"); + emitcode ("rl", "a"); + break; + case 3: + emitcode ("swap", "a"); + emitcode ("rr", "a"); + break; + case 4: + emitcode ("swap", "a"); + break; + case 5: + emitcode ("swap", "a"); + emitcode ("rl", "a"); + break; + case 6: + emitcode ("rr", "a"); + emitcode ("rr", "a"); + break; + case 7: + emitcode ("rr", "a"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccLsh - left shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccLsh (int shCount) +{ + if (shCount != 0) + { + if (shCount == 1) + emitcode ("add", "a,acc"); + else if (shCount == 2) + { + emitcode ("add", "a,acc"); + emitcode ("add", "a,acc"); + } + else + { + /* rotate left accumulator */ + AccRol (shCount); + /* and kill the lower order bits */ + emitcode ("anl", "a,#!constbyte", SLMask[shCount]); + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccRsh - right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRsh (int shCount) +{ + if (shCount != 0) + { + if (shCount == 1) + { + CLRC; + emitcode ("rrc", "a"); + } + else + { + /* rotate right accumulator */ + AccRol (8 - shCount); + /* and kill the higher order bits */ + emitcode ("anl", "a,#!constbyte", SRMask[shCount]); + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccSRsh - signed right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccSRsh (int shCount) +{ + symbol *tlbl; + if (shCount != 0) + { + if (shCount == 1) + { + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + } + else if (shCount == 2) + { + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + } + else + { + tlbl = newiTempLabel (NULL); + /* rotate right accumulator */ + AccRol (8 - shCount); + /* and kill the higher order bits */ + emitcode ("anl", "a,#!constbyte", SRMask[shCount]); + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#!constbyte", (unsigned char) ~SRMask[shCount]); + emitLabel (tlbl); + } + } +} + +/*-----------------------------------------------------------------*/ +/* shiftR1Left2Result - shift right one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR1Left2Result (operand * left, int offl, operand * result, int offr, int shCount, int sign) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift right accumulator */ + if (sign) + AccSRsh (shCount); + else + AccRsh (shCount); + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftL1Left2Result - shift left one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL1Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift left accumulator */ + AccLsh (shCount); + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* movLeft2Result - move byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +movLeft2Result (operand * left, int offl, operand * result, int offr, int sign) +{ + const char *l; + if (!sameRegs (AOP (left), AOP (result)) || (offl != offr)) + { + l = aopGet (left, offl, FALSE, FALSE); + + if (*l == '@' && (IS_AOP_PREG (result))) + { + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offr); + } + else + { + if (!sign) + { + aopPut (result, l, offr); + } + else + { + /* MSB sign in acc.7 ! */ + if (getDataSize (left) == offl + 1) + { + MOVA (l); + aopPut (result, "a", offr); + } + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRrl1 - right rotate c->a:x->c by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXRrl1 (const char *x) +{ + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLrl1 - left rotate c<-a:x<-c by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXLrl1 (const char *x) +{ + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLsh1 - left shift a:x<-0 by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXLsh1 (const char *x) +{ + emitcode ("xch", "a,%s", x); + emitcode ("add", "a,acc"); + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLsh - left shift a:x by known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXLsh (const char *x, int shCount) +{ + unsigned char mask; + + switch (shCount) + { + case 0: + break; + case 1: + AccAXLsh1 (x); + break; + case 2: + AccAXLsh1 (x); + AccAXLsh1 (x); + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD + mask = SLMask[shCount]; + AccRol (shCount); // BBBAAAAA:CCCCCDDD + emitcode ("anl", "a,#!constbyte", mask); // BBB00000:CCCCCDDD + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBB00000 + AccRol (shCount); // DDDCCCCC:BBB00000 + emitcode ("xch", "a,%s", x); // BBB00000:DDDCCCCC + emitcode ("xrl", "a,%s", x); // (BBB^DDD)CCCCC:DDDCCCCC + emitcode ("xch", "a,%s", x); // DDDCCCCC:(BBB^DDD)CCCCC + emitcode ("anl", "a,#!constbyte", mask); // DDD00000:(BBB^DDD)CCCCC + emitcode ("xch", "a,%s", x); // (BBB^DDD)CCCCC:DDD00000 + emitcode ("xrl", "a,%s", x); // BBBCCCCC:DDD00000 + break; + case 6: // AAAAAABB:CCCCCCDD + mask = SRMask[shCount]; + emitcode ("anl", "a,#!constbyte", mask); // 000000BB:CCCCCCDD + emitcode ("mov", "c,acc.0"); // c = B + emitcode ("xch", "a,%s", x); // CCCCCCDD:000000BB + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("mov", "c,acc.0"); //<< get correct bit + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + break; + case 7: // a:x <<= 7 + mask = SRMask[shCount]; + emitcode ("anl", "a,#!constbyte", mask); // 0000000B:CCCCCCCD + emitcode ("mov", "c,acc.0"); // c = B + emitcode ("xch", "a,%s", x); // CCCCCCCD:0000000B + AccAXRrl1 (x); // BCCCCCCC:D0000000 + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRsh - right shift a:x known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXRsh (const char *x, int shCount) +{ + unsigned char mask = SRMask[shCount]; + + switch (shCount) + { + case 0: + break; + case 1: + CLRC; + AccAXRrl1 (x); // 0->a:x + break; + case 2: + CLRC; + AccAXRrl1 (x); // 0->a:x + CLRC; + AccAXRrl1 (x); // 0->a:x + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD = a:x + AccRol (8 - shCount); // BBBAAAAA:DDDCCCCC + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBBAAAAA + AccRol (8 - shCount); // DDDCCCCC:BBBAAAAA + emitcode ("anl", "a,#!constbyte", mask); // 000CCCCC:BBBAAAAA + emitcode ("xrl", "a,%s", x); // BBB(CCCCC^AAAAA):BBBAAAAA + emitcode ("xch", "a,%s", x); // BBBAAAAA:BBB(CCCCC^AAAAA) + emitcode ("anl", "a,#!constbyte", mask); // 000AAAAA:BBB(CCCCC^AAAAA) + emitcode ("xch", "a,%s", x); // BBB(CCCCC^AAAAA):000AAAAA + emitcode ("xrl", "a,%s", x); // BBBCCCCC:000AAAAA + emitcode ("xch", "a,%s", x); // 000AAAAA:BBBCCCCC + break; + case 6: // AABBBBBB:CCDDDDDD + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // ABBBBBBC:CDDDDDDA + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // BBBBBBCC:DDDDDDAA + emitcode ("xch", "a,%s", x); // DDDDDDAA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 000000AA:BBBBBBCC + break; + case 7: // ABBBBBBB:CDDDDDDD + emitcode ("mov", "c,acc.7"); // c = A + AccAXLrl1 (x); // BBBBBBBC:DDDDDDDA + emitcode ("xch", "a,%s", x); // DDDDDDDA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 0000000A:BBBBBBBC + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRshS - right shift signed a:x known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXRshS (const char *x, int shCount) +{ + symbol *tlbl; + unsigned char mask = SRMask[shCount]; + + switch (shCount) + { + case 0: + break; + case 1: + emitcode ("mov", "c,acc.7"); + AccAXRrl1 (x); // s->a:x + break; + case 2: + emitcode ("mov", "c,acc.7"); + AccAXRrl1 (x); // s->a:x + emitcode ("mov", "c,acc.7"); + AccAXRrl1 (x); // s->a:x + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD = a:x + tlbl = newiTempLabel (NULL); + AccRol (8 - shCount); // BBBAAAAA:CCCCCDDD + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBBAAAAA + AccRol (8 - shCount); // DDDCCCCC:BBBAAAAA + emitcode ("anl", "a,#!constbyte", mask); // 000CCCCC:BBBAAAAA + emitcode ("xrl", "a,%s", x); // BBB(CCCCC^AAAAA):BBBAAAAA + emitcode ("xch", "a,%s", x); // BBBAAAAA:BBB(CCCCC^AAAAA) + emitcode ("anl", "a,#!constbyte", mask); // 000AAAAA:BBB(CCCCC^AAAAA) + emitcode ("xch", "a,%s", x); // BBB(CCCCC^AAAAA):000AAAAA + emitcode ("xrl", "a,%s", x); // BBBCCCCC:000AAAAA + emitcode ("xch", "a,%s", x); // 000SAAAA:BBBCCCCC + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 111AAAAA:BBBCCCCC + emitLabel (tlbl); + break; // SSSSAAAA:BBBCCCCC + case 6: // AABBBBBB:CCDDDDDD + tlbl = newiTempLabel (NULL); + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // ABBBBBBC:CDDDDDDA + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // BBBBBBCC:DDDDDDAA + emitcode ("xch", "a,%s", x); // DDDDDDAA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 000000AA:BBBBBBCC + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 111111AA:BBBBBBCC + emitLabel (tlbl); + break; + case 7: // ABBBBBBB:CDDDDDDD + tlbl = newiTempLabel (NULL); + emitcode ("mov", "c,acc.7"); // c = A + AccAXLrl1 (x); // BBBBBBBC:DDDDDDDA + emitcode ("xch", "a,%s", x); // DDDDDDDA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 0000000A:BBBBBBBC + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 1111111A:BBBBBBBC + emitLabel (tlbl); + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* shiftL2Left2Result - shift left two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL2Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + const char *x; + bool pushedB = FALSE; + bool usedB = FALSE; + + if (sameRegs (AOP (result), AOP (left)) && ((offl + MSB16) == offr)) + { + /* don't crash result[offr] */ + MOVA (aopGet (left, offl, FALSE, FALSE)); + x = xch_a_aopGet (left, offl + MSB16, FALSE, FALSE); + usedB = !strncmp (x, "b", 1); + } + else if (aopGetUsesAcc (result, offr)) + { + movLeft2Result (left, offl, result, offr, 0); + pushedB = pushB (); + usedB = TRUE; + emitcode ("mov", "b,%s", aopGet (left, offl + MSB16, FALSE, FALSE)); + MOVA (aopGet (result, offr, FALSE, FALSE)); + emitcode ("xch", "a,b"); + x = "b"; + } + else + { + movLeft2Result (left, offl, result, offr, 0); + MOVA (aopGet (left, offl + MSB16, FALSE, FALSE)); + x = aopGet (result, offr, FALSE, FALSE); + } + /* ax << shCount (x = lsb(result)) */ + AccAXLsh (x, shCount); + if (usedB) + { + emitcode ("xch", "a,b"); + aopPut (result, "a", offr); + aopPut (result, "b", offr + MSB16); + popB (pushedB); + } + else + { + aopPut (result, "a", offr + MSB16); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftR2Left2Result - shift right two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR2Left2Result (operand * left, int offl, operand * result, int offr, int shCount, int sign) +{ + const char *x; + bool pushedB = FALSE; + bool usedB = FALSE; + + if (sameRegs (AOP (result), AOP (left)) && ((offl + MSB16) == offr)) + { + /* don't crash result[offr] */ + MOVA (aopGet (left, offl, FALSE, FALSE)); + x = xch_a_aopGet (left, offl + MSB16, FALSE, FALSE); + usedB = !strncmp (x, "b", 1); + } + else if (aopGetUsesAcc (result, offr)) + { + movLeft2Result (left, offl, result, offr, 0); + pushedB = pushB (); + usedB = TRUE; + emitcode ("mov", "b,%s", aopGet (result, offr, FALSE, FALSE)); + MOVA (aopGet (left, offl + MSB16, FALSE, FALSE)); + x = "b"; + } + else + { + movLeft2Result (left, offl, result, offr, 0); + MOVA (aopGet (left, offl + MSB16, FALSE, FALSE)); + x = aopGet (result, offr, FALSE, FALSE); + } + /* a:x >> shCount (x = lsb(result)) */ + if (sign) + AccAXRshS (x, shCount); + else + AccAXRsh (x, shCount); + if (usedB) + { + emitcode ("xch", "a,b"); + aopPut (result, "a", offr); + emitcode ("xch", "a,b"); + popB (pushedB); + } + if (getDataSize (result) > 1) + aopPut (result, "a", offr + MSB16); +} + +/*------------------------------------------------------------------*/ +/* shiftLLeftOrResult - shift left one byte from left, or to result */ +/*------------------------------------------------------------------*/ +static void +shiftLLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift left accumulator */ + AccLsh (shCount); + /* or with result */ + if (aopGetUsesAcc (result, offr)) + { + emitcode ("xch", "a,b"); + MOVA (aopGet (result, offr, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else + { + emitcode ("orl", "a,%s", aopGet (result, offr, FALSE, FALSE)); + } + /* back to result */ + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftRLeftOrResult - shift right one byte from left,or to result */ +/*-----------------------------------------------------------------*/ +static void +shiftRLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift right accumulator */ + AccRsh (shCount); + /* or with result */ + if (aopGetUsesAcc (result, offr)) + { + emitcode ("xch", "a,b"); + MOVA (aopGet (result, offr, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else + { + emitcode ("orl", "a,%s", aopGet (result, offr, FALSE, FALSE)); + } + /* back to result */ + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftLLong - shift left one long from left to result */ +/* offl = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftLLong (operand * left, operand * result, int offr) +{ + int offl = LSB; + int size = AOP_SIZE (result); + int useXch = (sameRegs (AOP (left), AOP (result)) && size >= MSB16 + offr && offr != LSB); + + if (size > offl + offr) + { + MOVA (aopGet (left, offl, FALSE, FALSE)); + emitcode ("add", "a,acc"); + if (useXch) + xch_a_aopGet (left, offl + offr, FALSE, FALSE); + else + aopPut (result, "a", offl + offr); + } + + for (offl = LSB + 1; offl < LSB + 8; offl++) + { + if (size > offl + offr) + { + if (!useXch) + MOVA (aopGet (left, offl, FALSE, FALSE)); + emitcode ("rlc", "a"); + if (useXch) + xch_a_aopGet (left, offl + offr, FALSE, FALSE); + else + aopPut (result, "a", offl + offr); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genlshFixed - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshFixed (operand *result, operand *left, int shCount) +{ + int size, b; + int full_bytes; + + D (emitcode (";", "genlshFixed")); + + size = AOP_SIZE (result); + + full_bytes = shCount / 8; + shCount -= full_bytes * 8; + if (shCount == 0) + { + for (b = size - 1; b > full_bytes - 1; b--) + movLeft2Result (left, b - full_bytes, result, b, 0); + } + else if ((shCount == 1) && (full_bytes < 2)) + { + shiftLLong (left, result, full_bytes); + } + else if ((shCount == 2) && (full_bytes == 0)) + { + shiftLLong (left, result, full_bytes); + shiftLLong (result, result, full_bytes); + } + else + { + int off; + for (off = size - 2; off - full_bytes >= 0; off -= 2) + { + shiftL2Left2Result (left, off - full_bytes, result, off, shCount); + if (off - full_bytes - 1 >= 0) + shiftRLeftOrResult (left, off - full_bytes - 1, result, off, 8 - shCount); + } + if (off - full_bytes == -1) + { + shiftL1Left2Result (left, LSB, result, full_bytes, shCount); + } + } + for (b = LSB; b < full_bytes; b++) + aopPut (result, zero, b); + return; +} + +/*-----------------------------------------------------------------*/ +/* genlshAny - shift any number of bytes by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshAny (operand *result, operand *left, int shCount) +{ + int size, size2, offset; + + D (emitcode (";", "genlshAny")); + + size = AOP_SIZE (result); + + if (!operandsEqu (result, left)) + for (size2 = size, offset = 0; size2 > 0; size2--, offset++) + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + + while (shCount--) + { + MOVA (aopGet (result, LSB, FALSE, FALSE)); + emitcode ("add", "a,acc"); + aopPut (result, "a", 0); + + for(size2 = size - 1, offset = 1; size2 > 0; size2--, offset++) + { + + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + aopPut (result, "a", offset); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genLeftShiftLiteral - left shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genLeftShiftLiteral (operand * left, operand * right, operand * result, iCode * ic) +{ + unsigned int shCount = (unsigned int) ulFromVal (AOP (right)->aopu.aop_lit); + unsigned int size; + + size = getSize (operandType (result)); + + D (emitcode (";", "genLeftShiftLiteral (%d), size %d", shCount, size)); + + freeAsmop (right, NULL, ic, TRUE); + + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + +#if VIEW_SIZE + emitcode ("; shift left ", "result %d, left %d", size, AOP_SIZE (left)); +#endif + + switch (size) + { + case 1: + case 2: + case 4: + case 8: + genlshFixed (result, left, shCount); + break; + + default: + genlshAny (result, left, shCount); + break; + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genLeftShift - generates code for left shifting */ +/*-----------------------------------------------------------------*/ +static void +genLeftShift (iCode * ic) +{ + operand *left, *right, *result; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genLeftShift")); + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + genLeftShiftLiteral (left, right, result, ic); + return; + } + + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits makes no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genLeftShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE)); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* now move the left to the result if they are not the same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE); + if (*l == '@' && (IS_AOP_PREG (result))) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + } + + tlbl = newiTempLabel (NULL); + size = AOP_SIZE (result); + offset = 0; + tlbl1 = newiTempLabel (NULL); + + /* if it is only one byte then */ + if (size == 1) + { + symbol *tlbl1 = newiTempLabel (NULL); + + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("add", "a,acc"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("add", "a,acc"); + aopPut (result, "a", offset++); + while (--size) + { + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + aopPut (result, "a", offset++); + } + reAdjustPreg (AOP (result)); + + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genrshOne - right shift a one byte quantity by known count */ +/*-----------------------------------------------------------------*/ +static void +genrshOne (operand * result, operand * left, int shCount, int sign) +{ + D (emitcode (";", "genrshOne")); + + shiftR1Left2Result (left, LSB, result, LSB, shCount, sign); +} + +/*-----------------------------------------------------------------*/ +/* genrshTwo - right shift two bytes by known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshTwo (operand * result, operand * left, int shCount, int sign) +{ + D (emitcode (";", "genrshTwo")); + + /* if shCount >= 8 */ + if (shCount >= 8) + { + shCount -= 8; + if (shCount) + shiftR1Left2Result (left, MSB16, result, LSB, shCount, sign); + else + movLeft2Result (left, MSB16, result, LSB, sign); + addSign (result, MSB16, sign); + } + + /* 1 <= shCount <= 7 */ + else + { + shiftR2Left2Result (left, LSB, result, LSB, shCount, sign); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftRLong - shift right one long from left to result */ +/* offl = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftRLong (operand * left, int offl, operand * result, int sign) +{ + bool overlapping = regsInCommon (left, result) || operandsEqu (left, result); + + if (overlapping && offl > 1) + { + // we are in big trouble, but this shouldn't happen + werror (E_INTERNAL_ERROR, __FILE__, __LINE__); + } + + MOVA (aopGet (left, MSB32, FALSE, FALSE)); + + if (offl == MSB16) + { + // shift is > 8 + if (sign) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + if (overlapping && sameByte (AOP (left), MSB32, AOP (result), MSB32)) + { + xch_a_aopGet (left, MSB32, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB32); + MOVA (aopGet (left, MSB32, FALSE, FALSE)); + } + } + else + { + if (aopPutUsesAcc (result, zero, MSB32)) + { + emitcode ("xch", "a,b"); + aopPut (result, zero, MSB32); + emitcode ("xch", "a,b"); + } + else + { + aopPut (result, zero, MSB32); + } + } + } + + if (!sign) + { + emitcode ("clr", "c"); + } + else + { + emitcode ("mov", "c,acc.7"); + } + + emitcode ("rrc", "a"); + + if (overlapping && offl == MSB16 && sameByte (AOP (left), MSB24, AOP (result), MSB32 - offl)) + { + xch_a_aopGet (left, MSB24, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB32 - offl); + MOVA (aopGet (left, MSB24, FALSE, FALSE)); + } + + emitcode ("rrc", "a"); + if (overlapping && offl == MSB16 && sameByte (AOP (left), MSB16, AOP (result), MSB24 - offl)) + { + xch_a_aopGet (left, MSB16, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB24 - offl); + MOVA (aopGet (left, MSB16, FALSE, FALSE)); + } + + emitcode ("rrc", "a"); + if (offl != LSB) + { + aopPut (result, "a", MSB16 - offl); + } + else + { + if (overlapping && sameByte (AOP (left), LSB, AOP (result), MSB16 - offl)) + { + xch_a_aopGet (left, LSB, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB16 - offl); + MOVA (aopGet (left, LSB, FALSE, FALSE)); + } + emitcode ("rrc", "a"); + aopPut (result, "a", LSB); + } +} + +/*-----------------------------------------------------------------*/ +/* genrshFour - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshFour (operand *result, operand *left, int shCount, int sign) +{ + D (emitcode (";", "genrshFour")); + + /* if shifting more that 3 bytes */ + if (shCount >= 24) + { + shCount -= 24; + if (shCount) + shiftR1Left2Result (left, MSB32, result, LSB, shCount, sign); + else + movLeft2Result (left, MSB32, result, LSB, sign); + addSign (result, MSB16, sign); + } + else if (shCount >= 16) + { + shCount -= 16; + if (shCount) + shiftR2Left2Result (left, MSB24, result, LSB, shCount, sign); + else + { + movLeft2Result (left, MSB24, result, LSB, 0); + movLeft2Result (left, MSB32, result, MSB16, sign); + } + addSign (result, MSB24, sign); + } + else if (shCount >= 8) + { + shCount -= 8; + if (shCount == 1) + { + shiftRLong (left, MSB16, result, sign); + } + else if (shCount == 0) + { + movLeft2Result (left, MSB16, result, LSB, 0); + movLeft2Result (left, MSB24, result, MSB16, 0); + movLeft2Result (left, MSB32, result, MSB24, sign); + addSign (result, MSB32, sign); + } + else + { + shiftR2Left2Result (left, MSB16, result, LSB, shCount, 0); + shiftLLeftOrResult (left, MSB32, result, MSB16, 8 - shCount); + /* the last shift is signed */ + shiftR1Left2Result (left, MSB32, result, MSB24, shCount, sign); + addSign (result, MSB32, sign); + } + } + else + { + /* 1 <= shCount <= 7 */ + if (shCount <= 2) + { + shiftRLong (left, LSB, result, sign); + if (shCount == 2) + shiftRLong (result, LSB, result, sign); + } + else + { + shiftR2Left2Result (left, LSB, result, LSB, shCount, 0); + shiftLLeftOrResult (left, MSB24, result, MSB16, 8 - shCount); + shiftR2Left2Result (left, MSB24, result, MSB24, shCount, sign); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genrshAny - shift any number of bytes by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshAny (operand *result, operand *left, int shCount, int sign) +{ + int size, size2, offset; + + D (emitcode (";", "genrshAny")); + + size = AOP_SIZE (result); + + if (!operandsEqu (result, left)) + for (size2 = size, offset = 0; size2 > 0; size2--, offset++) + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + + while (shCount--) + { + MOVA (aopGet (result, size - 1, FALSE, FALSE)); + if (!sign) + emitcode ("clr", "c"); + else + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + aopPut (result, "a", size - 1); + + for(size2 = size - 1, offset = size - 2; size2 > 0; size2--, offset--) + { + + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genRightShiftLiteral - right shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genRightShiftLiteral (operand * left, operand * right, operand * result, iCode * ic, int sign) +{ + int shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); + int size; + + size = getSize (operandType (result)); //getDataSize (left); + + D (emitcode (";", "genRightShiftLiteral (%d), size %d", shCount, size)); + + freeAsmop (right, NULL, ic, TRUE); + + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + +#if VIEW_SIZE + emitcode ("; shift right ", "result %d, left %d", AOP_SIZE (result), AOP_SIZE (left)); +#endif + + /* test the LEFT size !!! */ + + /* I suppose that the left size >= result size */ + wassert ((int)getSize (operandType (left)) >= size); + + if (shCount == 0) + { + size = getDataSize (result); + while (size--) + movLeft2Result (left, size, result, size, 0); + } + else if (shCount >= (size * 8)) + { + if (sign) + { + /* get sign in acc.7 */ + MOVA (aopGet (left, size - 1, FALSE, FALSE)); + } + addSign (result, LSB, sign); + } + else + { + switch (size) + { + case 1: + genrshOne (result, left, shCount, sign); + break; + + case 2: + genrshTwo (result, left, shCount, sign); + break; + + case 4: + genrshFour (result, left, shCount, sign); + break; + + default: + genrshAny (result, left, shCount, sign); + break; + } + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genSignedRightShift - right shift of signed number */ +/*-----------------------------------------------------------------*/ +static void +genSignedRightShift (iCode * ic) +{ + operand *right, *left, *result; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genSignedRightShift")); + + /* we do it the hard way put the shift count in b + and loop thru preserving the sign */ + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + if (AOP_TYPE (right) == AOP_LIT) + { + genRightShiftLiteral (left, right, result, ic, 1); + return; + } + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits make no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genRightShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE)); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* now move the left to the result if they are not the + same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE); + if (*l == '@' && IS_AOP_PREG (result)) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + } + + /* mov the highest order bit to OVR */ + tlbl = newiTempLabel (NULL); + tlbl1 = newiTempLabel (NULL); + + size = AOP_SIZE (result); + offset = size - 1; + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + emitcode ("mov", "ov,c"); + /* if it is only one byte then */ + if (size == 1) + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("mov", "c,ov"); + emitcode ("rrc", "a"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("mov", "c,ov"); + while (size--) + { + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset--); + } + reAdjustPreg (AOP (result)); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRightShift - generate code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genRightShift (iCode * ic) +{ + operand *right, *left, *result; + sym_link *letype; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genRightShift")); + + /* if signed then we do it the hard way preserve the + sign bit moving it inwards */ + letype = getSpec (operandType (IC_LEFT (ic))); + + if (!SPEC_USIGN (letype)) + { + genSignedRightShift (ic); + return; + } + + /* signed & unsigned types are treated the same : i.e. the + signed is NOT propagated inwards : quoting from the + ANSI - standard : "for E1 >> E2, is equivalent to division + by 2**E2 if unsigned or if it has a non-negative value, + otherwise the result is implementation defined ", MY definition + is that the sign does not get propagated */ + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + genRightShiftLiteral (left, right, result, ic, 0); + return; + } + + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits make no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genRightShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE)); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* now move the left to the result if they are not the + same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE); + if (*l == '@' && IS_AOP_PREG (result)) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + } + + tlbl = newiTempLabel (NULL); + tlbl1 = newiTempLabel (NULL); + size = AOP_SIZE (result); + offset = size - 1; + + /* if it is only one byte then */ + if (size == 1) + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + CLRC; + emitcode ("rrc", "a"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + CLRC; + while (size--) + { + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset--); + } + reAdjustPreg (AOP (result)); + + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* emitPtrByteGet - emits code to get a byte into A through a */ +/* pointer register (R0, R1, or DPTR). The */ +/* original value of A can be preserved in B. */ +/*-----------------------------------------------------------------*/ +static void +emitPtrByteGet (const char *rname, int p_type, bool preserveAinB) +{ + switch (p_type) + { + case IPOINTER: + case POINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("mov", "a,@%s", rname); + break; + + case PPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("movx", "a,@%s", rname); + break; + + case FPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("movx", "a,@dptr"); + break; + + case CPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + break; + + case GPOINTER: + if (preserveAinB) + { + emitpush ("b"); + emitpush ("acc"); + } + emitcode ("lcall", "__gptrget"); + if (preserveAinB) + emitpop ("b"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* emitPtrByteSet - emits code to set a byte from src through a */ +/* pointer register (R0, R1, or DPTR). */ +/*-----------------------------------------------------------------*/ +static void +emitPtrByteSet (const char *rname, int p_type, const char *src) +{ + switch (p_type) + { + case IPOINTER: + case POINTER: + if (*src == '@') + { + MOVA (src); + emitcode ("mov", "@%s,a", rname); + } + else + emitcode ("mov", "@%s,%s", rname, src); + break; + + case PPOINTER: + MOVA (src); + emitcode ("movx", "@%s,a", rname); + break; + + case FPOINTER: + MOVA (src); + emitcode ("movx", "@dptr,a"); + break; + + case GPOINTER: + MOVA (src); + emitcode ("lcall", "__gptrput"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* genUnpackBits - generates code for unpacking bits */ +/*-----------------------------------------------------------------*/ +static char * +genUnpackBits (operand * result, const char *rname, int ptype, iCode * ifx) +{ + int offset = 0; /* result byte offset */ + int rsize; /* result size */ + int rlen = 0; /* remaining bitfield length */ + sym_link *etype; /* bitfield type information */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + static char *const accBits[] = { "acc.0", "acc.1", "acc.2", "acc.3", + "acc.4", "acc.5", "acc.6", "acc.7" + }; + + D (emitcode (";", "genUnpackBits")); + + etype = getSpec (operandType (result)); + rsize = getSize (operandType (result)); + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + if (ifx && blen <= 8) + { + emitPtrByteGet (rname, ptype, FALSE); + if (blen == 1) + { + return accBits[bstr];; + } + else + { + if (blen < 8) + emitcode ("anl", "a,#!constbyte", (((unsigned char) - 1) >> (8 - blen)) << bstr); + return "a"; + } + } + wassert (!ifx); + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + emitPtrByteGet (rname, ptype, FALSE); + AccRol (8 - bstr); + emitcode ("anl", "a,#!constbyte", ((unsigned char) - 1) >> (8 - blen)); + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = newiTempLabel (NULL); + + emitcode ("jnb", "acc.%d,!tlabel", blen - 1, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#0x%02x", (unsigned char) (0xff << blen)); + emitLabel (tlbl); + } + aopPut (result, "a", offset++); + goto finish; + } + + /* Bit field did not fit in a byte. Copy all + but the partial byte at the end. */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + emitPtrByteGet (rname, ptype, FALSE); + aopPut (result, "a", offset++); + if (rlen > 8) + emitcode ("inc", "%s", rname); + } + + /* Handle the partial byte at the end */ + if (rlen) + { + emitPtrByteGet (rname, ptype, FALSE); + emitcode ("anl", "a,#!constbyte", ((unsigned char) - 1) >> (8 - rlen)); + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = newiTempLabel (NULL); + + emitcode ("jnb", "acc.%d,!tlabel", rlen - 1, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#0x%02x", (unsigned char) (0xff << rlen)); + emitLabel (tlbl); + } + aopPut (result, "a", offset++); + } + +finish: + if (offset < rsize) + { + char *source; + + if (SPEC_USIGN (etype)) + source = zero; + else + { + /* signed bitfield: sign extension with 0x00 or 0xff */ + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + + source = "a"; + } + rsize -= offset; + while (rsize--) + aopPut (result, source, offset++); + } + return NULL; +} + + +/*-----------------------------------------------------------------*/ +/* genDataPointerGet - generates code when ptr offset is known */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerGet (operand * left, operand * result, iCode * ic) +{ + const char *l; + int size, offset = 0; + + D (emitcode (";", "genDataPointerGet")); + + aopOp (result, ic, TRUE); + + /* get the string representation of the name */ + l = aopGet (left, 0, FALSE, TRUE) + 1; // remove # + size = AOP_SIZE (result); + while (size--) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (AOP_SIZE (result) > 1) + { + dbuf_printf (&dbuf, "(%s + %d)", l, offset); + } + else + { + dbuf_append_str (&dbuf, l); + } + aopPut (result, dbuf_c_str (&dbuf), offset++); + dbuf_destroy (&dbuf); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genNearPointerGet - emitcode for near pointer fetch */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + char *ifxCond = "a"; + sym_link *rtype, *retype; + sym_link *ltype = operandType (left); + + D (emitcode (";", "genNearPointerGet")); + + rtype = operandType (result); + retype = getSpec (rtype); + + aopOp (left, ic, FALSE); + + /* if left is rematerialisable and + result is not bitfield variable type and + the left is pointer to data space i.e + lower 128 bytes of space */ + if (AOP_TYPE (left) == AOP_IMMD && !IS_BITFIELD (retype) && DCL_TYPE (ltype) == POINTER) + { + genDataPointerGet (left, result, ic); + return; + } + + //aopOp (result, ic, FALSE); + aopOp (result, ic, result ? TRUE : FALSE); + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (left))) + { + if (IS_AOP_PREG (left)) + { + // Aha, it is a pointer, just in disguise. + rname = aopGet (left, 0, FALSE, FALSE); + if (EQ (rname, "a")) + { + // It's in pdata or on xstack + rname = AOP (left)->aopu.aop_ptr->name; + emitcode ("mov", "%s,a", rname); + } + else if (*rname != '@') + { + fprintf (stderr, "probable internal error: unexpected rname '%s' @ %s:%d\n", rname, __FILE__, __LINE__); + } + else + { + // Expected case. + emitcode ("mov", "a%s,%s", rname + 1, rname); + rname++; // skip the '@'. + } + } + else + { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (left, 0, FALSE, TRUE)); + rname = preg->name; + } + } + else + rname = aopGet (left, 0, FALSE, FALSE); + + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype)) + ifxCond = genUnpackBits (result, rname, POINTER, ifx); + else + { + /* we can just get the values */ + int size = AOP_SIZE (result); + int offset = 0; + + while (size--) + { + if (ifx || IS_AOP_PREG (result) || AOP_TYPE (result) == AOP_STK) + { + emitcode ("mov", "a,@%s", rname); + if (!ifx) + aopPut (result, "a", offset); + } + else + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "@%s", rname); + aopPut (result, dbuf_c_str (&dbuf), offset); + dbuf_destroy (&dbuf); + } + offset++; + if (size || pi) + emitcode ("inc", "%s", rname); + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + { + /* post increment present */ + aopPut (left, rname, 0); + } + freeAsmop (NULL, aop, ic, RESULTONSTACK (ic) ? FALSE : TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (result) > 1 && !OP_SYMBOL (left)->remat && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) && !pi) + { + int size = AOP_SIZE (result) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + /* done */ + freeAsmop (result, NULL, ic, RESULTONSTACK (ic) ? FALSE : TRUE); + freeAsmop (left, NULL, ic, TRUE); + if (pi) + pi->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genPagedPointerGet - emitcode for paged pointer fetch */ +/*-----------------------------------------------------------------*/ +static void +genPagedPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + char *ifxCond = "a"; + sym_link *rtype, *retype; + + D (emitcode (";", "genPagedPointerGet")); + + rtype = operandType (result); + retype = getSpec (rtype); + + aopOp (left, ic, FALSE); + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (left))) + { + const char *l; + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + l = aopGet (left, 0, FALSE, TRUE); + if (*l == '@') + emitcode ("mov", "a%s,%s", preg->name, l); + else + emitcode ("mov", "%s,%s", preg->name, l); + rname = preg->name; + } + else + { + rname = aopGet (left, 0, FALSE, FALSE); + } + + aopOp (result, ic, TRUE); + + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype)) + { + ifxCond = genUnpackBits (result, rname, PPOINTER, ifx); + } + else + { + /* we can just get the values */ + int size = AOP_SIZE (result); + int offset = 0; + + while (size--) + { + emitcode ("movx", "a,@%s", rname); + if (!ifx) + aopPut (result, "a", offset); + + offset++; + + if (size || pi) + emitcode ("inc", "%s", rname); + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (left, rname, 0); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 1 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (result) > 1 && !OP_SYMBOL (left)->remat && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) && !pi) + { + int size = AOP_SIZE (result) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + + /* done */ + freeAsmop (result, NULL, ic, TRUE); + if (aop) + { + freeAsmop (NULL, aop, ic, TRUE); + } + freeAsmop (left, NULL, ic, TRUE); + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, NULL, NULL, NULL, ic->next); + } + + if (pi) + pi->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genFarPointerGet - get value from far space */ +/*-----------------------------------------------------------------*/ +static void +genFarPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + int size, offset; + char *ifxCond = "a"; + sym_link *retype = getSpec (operandType (result)); + + D (emitcode (";", "genFarPointerGet")); + + aopOp (left, ic, FALSE); + loadDptrFromOperand (left, FALSE); + + /* so dptr now contains the address */ + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype)) + ifxCond = genUnpackBits (result, "dptr", FPOINTER, ifx); + else + { + size = AOP_SIZE (result); + offset = 0; + + while (size--) + { + emitcode ("movx", "a,@dptr"); + if (!ifx) + aopPut (result, "a", offset++); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (left) != AOP_IMMD && AOP_TYPE (left) != AOP_STR) + { + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + pi->generated = 1; + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCodePointerGet - get value from code space */ +/*-----------------------------------------------------------------*/ +static void +genCodePointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + int size, offset; + char *ifxCond = "a"; + sym_link *retype = getSpec (operandType (result)); + + D (emitcode (";", "genCodePointerGet")); + + aopOp (left, ic, FALSE); + loadDptrFromOperand (left, FALSE); + + /* so dptr now contains the address */ + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype)) + ifxCond = genUnpackBits (result, "dptr", CPOINTER, ifx); + else + { + size = AOP_SIZE (result); + offset = 0; + + while (size--) + { + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + if (!ifx) + aopPut (result, "a", offset++); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (left) != AOP_IMMD && AOP_TYPE (left) != AOP_STR) + { + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + pi->generated = 1; + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerGet - get value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + int size, offset; + char *ifxCond = "a"; + sym_link *retype = getSpec (operandType (result)); + + D (emitcode (";", "genGenPointerGet")); + + aopOp (left, ic, FALSE); + loadDptrFromOperand (left, TRUE); + + /* so dptr-b now contains the address */ + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype)) + { + ifxCond = genUnpackBits (result, "dptr", GPOINTER, ifx); + } + else + { + size = AOP_SIZE (result); + offset = 0; + + while (size--) + { + emitcode ("lcall", "__gptrget"); + if (!ifx) + aopPut (result, "a", offset++); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (left) != AOP_IMMD && AOP_TYPE (left) != AOP_STR) + { + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + pi->generated = 1; + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (iCode * ic, iCode * pi, iCode * ifx) +{ + operand *left, *result; + sym_link *type, *etype; + int p_type; + + D (emitcode (";", "genPointerGet")); + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + if (getSize (operandType (result)) > 1) + ifx = NULL; + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (left); + etype = getSpec (type); + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + { + p_type = DCL_TYPE (type); + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + + /* special case when cast remat */ + while (IS_SYMOP (left) && OP_SYMBOL (left)->remat && IS_CAST_ICODE (OP_SYMBOL (left)->rematiCode)) + { + left = IC_RIGHT (OP_SYMBOL (left)->rematiCode); + type = operandType (left); + p_type = DCL_TYPE (type); + } + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + case POINTER: + case IPOINTER: + genNearPointerGet (left, result, ic, pi, ifx); + break; + + case PPOINTER: + genPagedPointerGet (left, result, ic, pi, ifx); + break; + + case FPOINTER: + genFarPointerGet (left, result, ic, pi, ifx); + break; + + case CPOINTER: + genCodePointerGet (left, result, ic, pi, ifx); + break; + + case GPOINTER: + genGenPointerGet (left, result, ic, pi, ifx); + break; + } +} + + +/*-----------------------------------------------------------------*/ +/* genPackBits - generates code for packed bit storage */ +/*-----------------------------------------------------------------*/ +static void +genPackBits (sym_link * etype, operand * right, const char *rname, int p_type) +{ + int offset = 0; /* source byte offset */ + int rlen = 0; /* remaining bitfield length */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + int litval; /* source literal value (if AOP_LIT) */ + unsigned char mask; /* bitmask within current byte */ + + D (emitcode (";", "genPackBits")); + + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + mask = ((unsigned char) (0xFF << (blen + bstr)) | (unsigned char) (0xFF >> (8 - bstr))); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with a bitfield length <8 and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval <<= bstr; + litval &= (~mask) & 0xff; + emitPtrByteGet (rname, p_type, FALSE); + if ((mask | litval) != 0xff) + emitcode ("anl", "a,#!constbyte", mask); + if (litval) + emitcode ("orl", "a,#!constbyte", litval); + } + else + { + if ((blen == 1) && (p_type != GPOINTER)) + { + /* Case with a bitfield length == 1 and no generic pointer + */ + if (AOP_TYPE (right) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + emitcode ("rrc", "a"); + } + emitPtrByteGet (rname, p_type, FALSE); + emitcode ("mov", "acc.%d,c", bstr); + } + else + { + bool pushedB; + /* Case with a bitfield length < 8 and arbitrary source + */ + MOVA (aopGet (right, 0, FALSE, FALSE)); + /* shift and mask source value */ + AccLsh (bstr); + emitcode ("anl", "a,#!constbyte", (~mask) & 0xff); + + pushedB = pushB (); + /* transfer A to B and get next byte */ + emitPtrByteGet (rname, p_type, TRUE); + + emitcode ("anl", "a,#!constbyte", mask); + emitcode ("orl", "a,b"); + if (p_type == GPOINTER) + emitpop ("b"); + + popB (pushedB); + } + } + + emitPtrByteSet (rname, p_type, "a"); + return; + } + + /* Bit length is greater than 7 bits. In this case, copy */ + /* all except the partial byte at the end */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + emitPtrByteSet (rname, p_type, aopGet (right, offset++, FALSE, TRUE)); + if (rlen > 8) + emitcode ("inc", "%s", rname); + } + + /* If there was a partial byte at the end */ + if (rlen) + { + mask = (((unsigned char) - 1 << rlen) & 0xff); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with partial byte and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval >>= (blen - rlen); + litval &= (~mask) & 0xff; + emitPtrByteGet (rname, p_type, FALSE); + if ((mask | litval) != 0xff) + emitcode ("anl", "a,#!constbyte", mask); + if (litval) + emitcode ("orl", "a,#!constbyte", litval); + } + else + { + bool pushedB; + /* Case with partial byte and arbitrary source + */ + MOVA (aopGet (right, offset++, FALSE, FALSE)); + emitcode ("anl", "a,#!constbyte", (~mask) & 0xff); + + pushedB = pushB (); + /* transfer A to B and get next byte */ + emitPtrByteGet (rname, p_type, TRUE); + + emitcode ("anl", "a,#!constbyte", mask); + emitcode ("orl", "a,b"); + if (p_type == GPOINTER) + emitpop ("b"); + + popB (pushedB); + } + emitPtrByteSet (rname, p_type, "a"); + } +} + +/*-----------------------------------------------------------------*/ +/* genLiteralAssign - assignment of literal */ +/*-----------------------------------------------------------------*/ +static void +genLiteralAssign (operand * result, operand * right, int size, bool (*output_fn) (operand * result, const char *s, int offset)) +{ + unsigned long long lit = 0LL; + int offset; + int accumulator_value = -1; /* -1 denotes: not yet set */ + + if (!IS_FLOAT (operandType (right))) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + } + else + { + union + { + float f; + unsigned char c[4]; + } fl; + + fl.f = (float) floatFromVal (AOP (right)->aopu.aop_lit); +#ifdef WORDS_BIGENDIAN + lit = (fl.c[3] << 0) | (fl.c[2] << 8) | (fl.c[1] << 16) | (fl.c[0] << 24); +#else + lit = (fl.c[0] << 0) | (fl.c[1] << 8) | (fl.c[2] << 16) | (fl.c[3] << 24); +#endif + } + + offset = 0; + while (size) + { + /* check whether preloading the accumulator pays off: + + mov direct,#something 3 byte, 2 cycle + mov direct,a 2 byte, 1 cycle + + mov @r0,#something 2 byte, 1 cycle + mov @r0,a 1 byte, 1 cycle + + mov rx,#something 2 byte, 1 cycle + mov rx,a 1 byte, 1 cycle + + clr a 1 byte, 1 cycle + mov a,#something 2 byte, 1 cycle + + (setting bytes in pdata and xdata need the accumulator anyway) + */ + + /* clr a needs an extra byte. If two bytes are zero it starts to pay off + to preload the accumulator */ + int clr_num_bytes_saved = -1 + /* size of clr a */ + (int) ((((lit >> 0) & 0xff) == 0) && (size >= 1)) + + (int) ((((lit >> 8) & 0xff) == 0) && (size >= 2)) + + (int) ((((lit >> 16) & 0xff) == 0) && (size >= 3)) + (int) ((((lit >> 24) & 0xff) == 0) && (size >= 4)); + + /* mov a,#something needs two extra bytes. If three bytes are identical it starts to pay off */ + int mov_num_bytes_saved = -2 + /* size of mov a,#something */ + (int) ((lit & 0xff) == ((lit >> 0) & 0xff) && (size >= 1)) + /* true */ + (int) ((lit & 0xff) == ((lit >> 8) & 0xff) && (size >= 2)) + + (int) ((lit & 0xff) == ((lit >> 16) & 0xff) && (size >= 3)) + + (int) ((lit & 0xff) == ((lit >> 24) & 0xff) && (size >= 4)); + + int num_bytes_to_save_before_using_acc_takes_effect = 1; + + if (optimize.codeSpeed && (AOP_TYPE (result) != AOP_DIR)) + { + /* require an extra byte being saved */ + num_bytes_to_save_before_using_acc_takes_effect++; + } + + /* eventually preload accumulator */ + if ((clr_num_bytes_saved >= num_bytes_to_save_before_using_acc_takes_effect) && + (clr_num_bytes_saved >= mov_num_bytes_saved) && (lit & 0xff) == 0) + { + if (0 != accumulator_value) + { + accumulator_value = 0; +// emitcode ("clr", "a"); + MOVA ("#0x00"); + } + } + else if ((mov_num_bytes_saved >= num_bytes_to_save_before_using_acc_takes_effect) && (mov_num_bytes_saved > clr_num_bytes_saved)) /* preferrably have 0 in acc */ + { + if ((lit & 0xff) != accumulator_value) + { + accumulator_value = lit & 0xff; +// emitcode ("mov", "a,%s", aopGet (right, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + } + } + + /* write byte */ + if ((lit & 0xff) == accumulator_value) + { + /* value in accumulator can be used */ + output_fn (result, "a", offset); + } + else + { + /* otherwise use the normal path that should always work */ + char *r = Safe_strdup (aopGet (right, offset, FALSE, FALSE)); + output_fn (result, r, offset); + Safe_free (r); + } + + /* advance */ + lit >>= 8; + offset++; + size--; + } +} + +/*-----------------------------------------------------------------*/ +/* dataPut - puts a string for a aop and indicates if acc is in use */ +/*-----------------------------------------------------------------*/ +static bool +litPut (operand * result, const char *s, int offset) +{ + emitcode ("mov", "(%s + %d),%s", aopGet (result, 0, FALSE, TRUE) + 1, offset, s); + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* genDataPointerSet - remat pointer to data space */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerSet (operand * right, operand * result, iCode * ic) +{ + int size, offset; + + D (emitcode (";", "genDataPointerSet")); + + aopOp (right, ic, FALSE); + + size = max (AOP_SIZE (right), AOP_SIZE (result)); + if ((size > 1) && IS_OP_LITERAL (right)) + { + genLiteralAssign (result, right, size, litPut); + } + else + { + //remove # + char *l = Safe_strdup (aopGet (result, 0, FALSE, TRUE) + 1); //remove # + for (offset = 0; offset < size; offset++) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (size > 1) + dbuf_printf (&dbuf, "(%s + %d)", l, offset); + else + dbuf_append_str (&dbuf, l); + emitcode ("mov", "%s,%s", dbuf_c_str (&dbuf), aopGet (right, offset, FALSE, FALSE)); + dbuf_destroy (&dbuf); + } + Safe_free (l); + } + + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genNearPointerSet - emitcode for near pointer put */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + sym_link *retype, *letype; + sym_link *ptype = operandType (result); + + D (emitcode (";", "genNearPointerSet")); + + retype = getSpec (operandType (right)); + letype = getSpec (ptype); + + aopOp (result, ic, FALSE); + + /* if the result is rematerializable & + in data space & not a bit variable */ + if (AOP_TYPE (result) == AOP_IMMD && DCL_TYPE (ptype) == POINTER && !IS_BITVAR (retype) && !IS_BITVAR (letype)) + { + genDataPointerSet (right, result, ic); + return; + } + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (result))) + { + if (IS_AOP_PREG (result)) + { + // Aha, it is a pointer, just in disguise. + rname = aopGet (result, 0, FALSE, FALSE); + if (EQ (rname, "a")) + { + // It's in pdata or on xstack + rname = AOP (result)->aopu.aop_ptr->name; + emitcode ("mov", "%s,a", rname); + } + else if (*rname != '@') + { + fprintf (stderr, "probable internal error: unexpected rname @ %s:%d\n", __FILE__, __LINE__); + } + else + { + // Expected case. + emitcode ("mov", "a%s,%s", rname + 1, rname); + rname++; // skip the '@'. + } + } + else + { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (result, 0, FALSE, TRUE)); + rname = preg->name; + } + } + else + { + rname = aopGet (result, 0, FALSE, FALSE); + } + + aopOp (right, ic, FALSE); + + rname = Safe_strdup (rname); + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, rname, POINTER); + else + { + /* we can just get the values */ + int size = AOP_SIZE (right); + int offset = 0; + + while (size--) + { + const char *l = aopGet (right, offset, FALSE, TRUE); + if ((*l == '@') || (EQ (l, "acc"))) + { + MOVA (l); + emitcode ("mov", "@%s,a", rname); + } + else + emitcode ("mov", "@%s,%s", rname, l); + if (size || pi) + emitcode ("inc", "%s", rname); + offset++; + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (result, rname, 0); + freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (right) > 1 && !OP_SYMBOL (result)->remat && (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth)) && !pi) + { + int size = AOP_SIZE (right) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + Safe_free ((void *) rname); + + /* done */ + if (pi) + pi->generated = 1; + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPagedPointerSet - emitcode for Paged pointer put */ +/*-----------------------------------------------------------------*/ +static void +genPagedPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + sym_link *retype, *letype; + + D (emitcode (";", "genPagedPointerSet")); + + retype = getSpec (operandType (right)); + letype = getSpec (operandType (result)); + + aopOp (result, ic, FALSE); + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (result))) + { + if (IS_AOP_PREG (result)) + { + // Aha, it is a pointer, just in disguise. + rname = aopGet (result, 0, FALSE, FALSE); + if (*rname != '@') + { + fprintf (stderr, "probable internal error: unexpected rname @ %s:%d\n", __FILE__, __LINE__); + } + else + { + // Expected case. + emitcode ("mov", "a%s,%s", rname + 1, rname); + rname++; // skip the '@'. + } + } + else + { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (result, 0, FALSE, TRUE)); + rname = preg->name; + } + } + else + { + rname = aopGet (result, 0, FALSE, FALSE); + } + + aopOp (right, ic, FALSE); + + rname = Safe_strdup (rname); + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, rname, PPOINTER); + else + { + /* we can just get the values */ + int size = AOP_SIZE (right); + int offset = 0; + + while (size--) + { + MOVA (aopGet (right, offset, FALSE, TRUE)); + emitcode ("movx", "@%s,a", rname); + if (size || pi) + emitcode ("inc", "%s", rname); + offset++; + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (result, rname, 0); + freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 1 && this could be used again + we have to point it back to where it + belongs */ + if (AOP_SIZE (right) > 1 && !OP_SYMBOL (result)->remat && (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth) && !pi) + { + int size = AOP_SIZE (right) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + Safe_free ((void *) rname); + + /* done */ + if (pi) + pi->generated = 1; + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genFarPointerSet - set value in far space */ +/*-----------------------------------------------------------------*/ +static void +genFarPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + int size, offset; + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (result)); + + D (emitcode (";", "genFarPointerSet")); + + aopOp (result, ic, FALSE); + loadDptrFromOperand (result, FALSE); + + /* so dptr now contains the address */ + aopOp (right, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, "dptr", FPOINTER); + else + { + size = AOP_SIZE (right); + offset = 0; + + while (size--) + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (offset++ > 0) + emitcode ("inc", "dptr"); + emitcode ("movx", "@dptr,a"); + } + if (pi) + emitcode ("inc", "dptr"); + } + if (pi && AOP_TYPE (result) != AOP_STR && AOP_TYPE (result) != AOP_IMMD) + { + aopPut (result, "dpl", 0); + aopPut (result, "dph", 1); + pi->generated = 1; + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerSet - set value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + int size, offset; + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (result)); + + D (emitcode (";", "genGenPointerSet")); + + aopOp (result, ic, FALSE); + loadDptrFromOperand (result, TRUE); + + /* so dptr-b now contains the address */ + aopOp (right, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + { + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, "dptr", GPOINTER); + } + else + { + size = AOP_SIZE (right); + offset = 0; + + while (size--) + { + MOVA (aopGet (right, offset++, FALSE, FALSE)); + emitcode ("lcall", "__gptrput"); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (result) != AOP_STR && AOP_TYPE (result) != AOP_IMMD) + { + aopPut (result, "dpl", 0); + aopPut (result, "dph", 1); + pi->generated = 1; + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode * ic, iCode * pi) +{ + operand *right, *result; + sym_link *type, *etype; + int p_type; + + D (emitcode (";", "genPointerSet")); + + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (result); + etype = getSpec (type); + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + { + p_type = DCL_TYPE (type); + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + + /* special case when cast remat */ + while (IS_SYMOP (result) && OP_SYMBOL (result)->remat && + IS_CAST_ICODE (OP_SYMBOL (result)->rematiCode) && + !IS_BITFIELD (getSpec (operandType (result)))) + { + result = IC_RIGHT (OP_SYMBOL (result)->rematiCode); + type = operandType (result); + p_type = DCL_TYPE (type); + } + + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + case POINTER: + case IPOINTER: + genNearPointerSet (right, result, ic, pi); + break; + + case PPOINTER: + genPagedPointerSet (right, result, ic, pi); + break; + + case FPOINTER: + genFarPointerSet (right, result, ic, pi); + break; + + case GPOINTER: + genGenPointerSet (right, result, ic, pi); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "genPointerSet: illegal pointer type"); + } +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (iCode * ic, iCode * popIc) +{ + operand *cond = IC_COND (ic); + int isbit = 0; + char *dup = NULL; + + D (emitcode (";", "genIfx")); + + aopOp (cond, ic, FALSE); + + /* get the value into acc */ + if (AOP_TYPE (cond) != AOP_CRY) + { + toBoolean (cond); + } + else + { + isbit = 1; + if (AOP (cond)->aopu.aop_dir) + dup = Safe_strdup (AOP (cond)->aopu.aop_dir); + } + + /* the result is now in the accumulator or a directly addressable bit */ + freeAsmop (cond, NULL, ic, TRUE); + + /* if the condition is a bit variable */ + if (isbit && dup) + genIfxJump (ic, dup, NULL, NULL, NULL, popIc); + else if (isbit && IS_OP_ACCUSE (cond)) + genIfxJump (ic, "c", NULL, NULL, NULL, popIc); + else if (isbit && IS_ITEMP (cond) && SPIL_LOC (cond)) + genIfxJump (ic, SPIL_LOC (cond)->rname, NULL, NULL, NULL, popIc); + else if (isbit && !IS_ITEMP (cond)) + genIfxJump (ic, OP_SYMBOL (cond)->rname, NULL, NULL, NULL, popIc); + else + genIfxJump (ic, "a", NULL, NULL, NULL, popIc); + + if (dup) + Safe_free (dup); + + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + int size, offset; + + D (emitcode (";", "genAddrOf")); + + aopOp (IC_RESULT (ic), ic, FALSE); + + /* if the operand is on the stack then we + need to get the stack offset of this + variable */ + if (sym->onStack) + { + int stack_offset = stackoffset (sym); + + /* if it has an offset then we need to compute it */ + if (stack_offset) + { + if ((abs (stack_offset) == 1) && !AOP_NEEDSACC (IC_RESULT (ic)) && !isOperandVolatile (IC_RESULT (ic), FALSE)) + { + aopPut (IC_RESULT (ic), SYM_BP (sym), 0); + if (stack_offset > 0) + emitcode ("inc", "%s", aopGet (IC_RESULT (ic), LSB, FALSE, FALSE)); + else + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), LSB, FALSE, FALSE)); + } + else + { + emitcode ("mov", "a,%s", SYM_BP (sym)); + emitcode ("add", "a,#!constbyte", stack_offset & 0xff); + aopPut (IC_RESULT (ic), "a", 0); + } + } + else + { + /* we can just move _bp */ + aopPut (IC_RESULT (ic), SYM_BP (sym), 0); + } + /* fill the result with zero */ + size = AOP_SIZE (IC_RESULT (ic)) - 1; + + offset = 1; + while (size--) + { + aopPut (IC_RESULT (ic), zero, offset++); + } + goto release; + } + + /* object not on stack then we need the name */ + size = getDataSize (IC_RESULT (ic)); + offset = 0; + + while (size--) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (offset) + { + dbuf_printf (&dbuf, "#(%s >> %d)", sym->rname, offset * 8); + } + else + { + dbuf_printf (&dbuf, "#%s", sym->rname); + } + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), offset++); + dbuf_destroy (&dbuf); + } + if (opIsGptr (IC_RESULT (ic))) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_LEFT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + dbuf_destroy (&dbuf); + } + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genFarFarAssign - assignment when both are in far space */ +/*-----------------------------------------------------------------*/ +static void +genFarFarAssign (operand * result, operand * right, iCode * ic) +{ + int size = AOP_SIZE (right); + int offset = 0; + + D (emitcode (";", "genFarFarAssign")); + + /* first push the right side on to the stack */ + while (size--) + { + MOVA (aopGet (right, offset++, FALSE, FALSE)); + emitpush ("acc"); + } + + freeAsmop (right, NULL, ic, FALSE); + /* now assign DPTR to result */ + aopOp (result, ic, FALSE); + size = AOP_SIZE (result); + while (size--) + { + emitpop ("acc"); + aopPut (result, "a", --offset); + } + freeAsmop (result, NULL, ic, FALSE); +} + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (iCode * ic) +{ + operand *result, *right; + int size, offset; + + D (emitcode (";", "genAssign")); + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + /* if they are the same */ + if (operandsEqu (result, right) && !isOperandVolatile (result, FALSE) && !isOperandVolatile (right, FALSE)) + return; + + aopOp (right, ic, FALSE); + + /* special case both in far space */ + if (AOP_TYPE (right) == AOP_DPTR && IS_TRUE_SYMOP (result) && isOperandInFarSpace (result)) + { + genFarFarAssign (result, right, ic); + return; + } + + aopOp (result, ic, TRUE); + + /* if they are the same registers */ + if (sameRegs (AOP (right), AOP (result)) && !isOperandVolatile (result, FALSE) && !isOperandVolatile (right, FALSE)) + goto release; + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + assignBit (result, right); + goto release; + } + + /* bit variables done */ + /* general case */ + + size = getDataSize (result); + + if ((size > 1) && (AOP_TYPE (result) != AOP_REG) && /* for registers too? (regression test passes) */ + (AOP_TYPE (right) == AOP_LIT) && !aopPutUsesAcc (result, aopGet (right, 0, FALSE, FALSE), 0)) + { + genLiteralAssign (result, right, size, aopPut); + } + else + { + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + } + adjustArithmeticResult (ic); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - generates code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (iCode * ic) +{ + operand *cond = IC_JTCOND (ic); + symbol *jtab, *jtablo, *jtabhi; + unsigned int count; + const char *l; + bool useB = FALSE; + + D (emitcode (";", "genJumpTab")); + + count = elementsInSet (IC_JTLABELS (ic)); + + if ((count <= 7) || + (count <= 16 && optimize.codeSpeed) || + options.acall_ajmp) + { + /* This algorithm needs 9 cycles and 7 + 3*n bytes + if the switch argument is in a register. + (6 + 2*n bytes when options.acall_ajmp is set) */ + /* Peephole may not convert ljmp to sjmp or ret + labelIsReturnOnly & labelInRange must check + currPl->ic->op != JUMPTABLE */ + aopOp (cond, ic, FALSE); + /* get the condition into accumulator */ + l = aopGet (cond, 0, FALSE, FALSE); + MOVA (l); + /* multiply by three */ + if ((AOP_TYPE (cond) == AOP_REG) || (IS_AOP_PREG (cond) && !AOP (cond)->paged && !IS_VOLATILE (operandType (cond)))) + { + emitcode ("add", "a,%s", l); + if (options.acall_ajmp == 0) + emitcode ("add", "a,%s", l); + } + else + { + if (options.acall_ajmp == 0) + { + MOVB ("#0x03"); + emitcode ("mul", "ab"); + } + else + { + emitcode ("add", "a,acc"); + } + } + freeAsmop (cond, NULL, ic, TRUE); + + jtab = newiTempLabel (NULL); + emitcode ("mov", "dptr,#!tlabel", labelKey2num (jtab->key)); + emitcode ("jmp", "@a+dptr"); + emitLabel (jtab); + /* now generate the jump labels */ + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + if (options.acall_ajmp) + emitcode ("ajmp", "!tlabel", labelKey2num (jtab->key)); + else + emitcode ("ljmp", "!tlabel", labelKey2num (jtab->key)); + } + else + { + /* this algorithm needs 14 cycles and 14 + 2*n bytes + if the switch argument is in a register. + For n>7 this algorithm may be more compact */ + jtablo = newiTempLabel (NULL); + jtabhi = newiTempLabel (NULL); + + /* get the condition into accumulator. + Using b as temporary storage, if register push/pop is needed */ + aopOp (cond, ic, FALSE); + l = aopGet (cond, 0, FALSE, FALSE); + if ((AOP_TYPE (cond) == AOP_R0 && _G.r0Pushed) || + (AOP_TYPE (cond) == AOP_R1 && _G.r1Pushed) || + EQ (l, "a") || EQ (l, "acc") || + (count > 125 && EQ (l, "dpl")) || + IS_VOLATILE (operandType (cond))) + { + // (MB) what if B is in use??? + wassertl (!BINUSE, "B was in use"); + MOVB (l); + l = "b"; + useB = TRUE; + } + freeAsmop (cond, NULL, ic, TRUE); + MOVA (l); + if (count <= 125) + { + emitcode ("add", "a,#(!tlabel-3-.)", labelKey2num (jtablo->key)); + emitcode ("movc", "a,@a+pc"); + if (EQ (l, "dpl")) + { + emitcode ("xch", "a,dpl"); + } + else + { + emitcode ("mov", "dpl,a"); + MOVA (l); + } + emitcode ("add", "a,#(!tlabel-3-.)", labelKey2num (jtabhi->key)); + emitcode ("movc", "a,@a+pc"); + emitcode ("mov", "dph,a"); + } + else + { + /* this scales up to n<=255, but needs four more bytes + and changes dptr */ + emitcode ("mov", "dptr,#!tlabel", labelKey2num (jtablo->key)); + emitcode ("movc", "a,@a+dptr"); + if (useB) + { + emitcode ("xch", "a,b"); + } + else + { + emitpush ("acc"); + MOVA (l); + } + emitcode ("mov", "dptr,#!tlabel", labelKey2num (jtabhi->key)); + emitcode ("movc", "a,@a+dptr"); + emitcode ("mov", "dph,a"); + if (useB) + emitcode ("mov", "dpl,b"); + else + emitpop ("dpl"); + } + + emitcode ("clr", "a"); + emitcode ("jmp", "@a+dptr"); + + /* now generate jump table, LSB */ + emitLabel (jtablo); + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + emitcode (".db", "!tlabel", labelKey2num (jtab->key)); + + /* now generate jump table, MSB */ + emitLabel (jtabhi); + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + emitcode (".db", "!tlabel>>8", labelKey2num (jtab->key)); + } +} + +/*-----------------------------------------------------------------*/ +/* genCast - gen code for casting */ +/*-----------------------------------------------------------------*/ +static void +genCast (iCode * ic) +{ + operand *result = IC_RESULT (ic); + sym_link *ctype = operandType (IC_LEFT (ic)); + sym_link *rtype = operandType (IC_RIGHT (ic)); + operand *right = IC_RIGHT (ic); + int size, offset; + bool right_boolean; + + D (emitcode (";", "genCast")); + + /* if they are equivalent then do nothing */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + right_boolean = IS_BOOLEAN (rtype) || IS_BITFIELD (rtype) && SPEC_BLEN (getSpec (rtype)) == 1; + + /* if the result is a bit (and not a bitfield) */ + if (IS_BOOLEAN (OP_SYMBOL (result)->type) && !right_boolean) + { + assignBit (result, right); + goto release; + } + + /* if they are the same size : or less */ + if (AOP_SIZE (result) <= AOP_SIZE (right)) + { + /* if they are in the same place */ + if (sameRegs (AOP (right), AOP (result))) + goto release; + + /* if they are in different places then copy */ + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + goto release; + } + + /* if the either is of type pointer */ + if ((IS_PTR (ctype) || IS_PTR (rtype)) && !IS_INTEGRAL (rtype)) + { + int p_type; + sym_link *type = operandType (right); + sym_link *etype = getSpec (type); + + /* pointer to generic pointer or long */ + if (AOP_SIZE (result) >= GPTRSIZE) + { + if (IS_PTR (type)) + { + p_type = DCL_TYPE (type); + } + else + { + if (SPEC_SCLS (etype) == S_REGISTER) + { + // let's assume it is a generic pointer + p_type = GPOINTER; + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + } + + /* the first two bytes are known */ + size = GPTRSIZE - 1; + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + /* the third byte depending on type */ + { + int gpVal; + + /* If there will be no loss of precision, handle generic */ + /* pointer as special case to avoid generating warning */ + if (p_type == GPOINTER && AOP_SIZE (result) >= GPTRSIZE) + gpVal = -1; + else + gpVal = pointerTypeToGPByte (p_type, NULL, NULL); + + if (gpVal == -1) + { + // pointerTypeToGPByte will have warned, just copy. + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + } + else + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "#0x%02x", gpVal); + aopPut (result, dbuf_c_str (&dbuf), GPTRSIZE - 1); + dbuf_destroy (&dbuf); + } + } + + /* 8051 uses unsigned address spaces, so no sign extension needed. */ + /* Pad the remaining bytes of the result (if any) with 0. */ + size = AOP_SIZE (result) - GPTRSIZE; + offset = GPTRSIZE; + while (size--) + { + aopPut (result, zero, offset++); + } + goto release; + } + + /* just copy the pointers */ + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + goto release; + } + + /* so we now know that the size of destination is greater + than the size of the source */ + /* we move to result for the size of source */ + size = AOP_SIZE (right); + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + + /* now depending on the sign of the source && destination */ + size = AOP_SIZE (result) - AOP_SIZE (right); + /* if unsigned or not an integral type */ + if (!IS_SPEC (rtype) || SPEC_USIGN (rtype) || AOP_TYPE (right) == AOP_CRY) + { + while (size--) + { + aopPut (result, zero, offset++); + } + } + else + { + /* we need to extend the sign :{ */ + MOVA (aopGet (right, AOP_SIZE (right) - 1, FALSE, FALSE)); + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (result, "a", offset++); + } + + /* we are done hurray !!!! */ + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDjnz - generate decrement & jump if not zero instruction */ +/*-----------------------------------------------------------------*/ +static int +genDjnz (iCode * ic, iCode * ifx) +{ + symbol *lbl, *lbl1; + if (!ifx) + return 0; + + /* if the if condition has a false label + then we cannot save */ + if (IC_FALSE (ifx)) + return 0; + + /* if the minus is not of the form a = a - 1 */ + if (!isOperandEqual (IC_RESULT (ic), IC_LEFT (ic)) || !IS_OP_LITERAL (IC_RIGHT (ic))) + return 0; + + if (operandLitValue (IC_RIGHT (ic)) != 1) + return 0; + + /* if the size of this greater than one then no saving */ + if (getSize (operandType (IC_RESULT (ic))) > 1) + return 0; + + /* otherwise we can save BIG */ + + popForBranch (ic->next, TRUE); + + D (emitcode (";", "genDjnz")); + + lbl = newiTempLabel (NULL); + lbl1 = newiTempLabel (NULL); + + aopOp (IC_RESULT (ic), ic, FALSE); + + if (AOP_NEEDSACC (IC_RESULT (ic))) + { + /* If the result is accessed indirectly via + * the accumulator, we must explicitly write + * it back after the decrement. + */ + const char *rByte = aopGet (IC_RESULT (ic), 0, FALSE, FALSE); + + if (!EQ (rByte, "a")) + { + /* Something is hopelessly wrong */ + fprintf (stderr, "*** warning: internal error at %s:%d\n", __FILE__, __LINE__); + /* We can just give up; the generated code will be inefficient, + * but what the hey. + */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + return 0; + } + emitcode ("dec", "%s", rByte); + aopPut (IC_RESULT (ic), rByte, 0); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + else if (IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), 0, FALSE, FALSE)); + MOVA (aopGet (IC_RESULT (ic), 0, FALSE, FALSE)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + ifx->generated = 1; + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + else + { + emitcode ("djnz", "%s,!tlabel", aopGet (IC_RESULT (ic), 0, FALSE, FALSE), labelKey2num (lbl->key)); + } + emitcode ("sjmp", "!tlabel", labelKey2num (lbl1->key)); + emitLabel (lbl); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + emitLabel (lbl1); + + if (!ifx->generated) + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + ifx->generated = 1; + return 1; +} + +/*-----------------------------------------------------------------*/ +/* genReceive - generate code for a receive iCode */ +/*-----------------------------------------------------------------*/ +static void +genReceive (iCode * ic) +{ + int size = getSize (operandType (IC_RESULT (ic))); + int offset = 0; + + D (emitcode (";", "genReceive")); + + if (ic->argreg == 1) + { + /* first parameter */ + if ((isOperandInFarSpace (IC_RESULT (ic)) || + isOperandInPagedSpace (IC_RESULT (ic))) && (OP_SYMBOL (IC_RESULT (ic))->isspilt || IS_TRUE_SYMOP (IC_RESULT (ic)))) + { + reg_info *tempRegs[8]; + int receivingA = 0; + int roffset = 0; + + for (offset = 0; offset < size; offset++) + if (EQ (fReturn[offset], "a")) + receivingA = 1; + + if (!receivingA) + { + if (size == 1 || getTempRegs (tempRegs, size - 1, ic)) + { + for (offset = size - 1; offset > 0; offset--) + emitcode ("mov", "%s,%s", tempRegs[roffset++]->name, fReturn[offset]); + emitcode ("mov", "a,%s", fReturn[0]); + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + aopPut (IC_RESULT (ic), "a", offset); + for (offset = 1; offset < size; offset++) + aopPut (IC_RESULT (ic), tempRegs[--roffset]->name, offset); + goto release; + } + } + else + { + if (getTempRegs (tempRegs, size, ic)) + { + for (offset = 0; offset < size; offset++) + emitcode ("mov", "%s,%s", tempRegs[offset]->name, fReturn[offset]); + aopOp (IC_RESULT (ic), ic, FALSE); + for (offset = 0; offset < size; offset++) + aopPut (IC_RESULT (ic), tempRegs[offset]->name, offset); + goto release; + } + } + + offset = fReturnSizeMCS51 - size; + while (size--) + { + emitpush ((!EQ (fReturn[fReturnSizeMCS51 - offset - 1], "a") ? fReturn[fReturnSizeMCS51 - offset - 1] : "acc")); + offset++; + } + aopOp (IC_RESULT (ic), ic, FALSE); + size = AOP_SIZE (IC_RESULT (ic)); + offset = 0; + while (size--) + { + emitpop ("acc"); + aopPut (IC_RESULT (ic), "a", offset++); + } + } + else + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + assignResultValue (IC_RESULT (ic), NULL); + } + } + else if (ic->argreg > 12) + { + /* bit parameters */ + reg_info *reg = OP_SYMBOL (IC_RESULT (ic))->regs[0]; + + BitBankUsed = 1; + if (!reg || reg->rIdx != ic->argreg - 5) + { + aopOp (IC_RESULT (ic), ic, FALSE); + emitcode ("mov", "c,%s", rb1regs[ic->argreg - 5]); + outBitC (IC_RESULT (ic)); + } + } + else + { + /* other parameters */ + int rb1off; + aopOp (IC_RESULT (ic), ic, FALSE); + rb1off = ic->argreg; + while (size--) + { + aopPut (IC_RESULT (ic), rb1regs[rb1off++ - 5], offset++); + } + } + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (iCode * ic) +{ + operand *op; + int size, offset; + + D (emitcode (";", "genDummyRead")); + + op = IC_RIGHT (ic); + if (op && IS_SYMOP (op)) + { + aopOp (op, ic, FALSE); + + /* if the result is a bit */ + if (AOP_TYPE (op) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (op)->aopu.aop_dir); + else + { + /* bit variables done */ + /* general case */ + size = AOP_SIZE (op); + offset = 0; + while (size--) + { + MOVA (aopGet (op, offset, FALSE, FALSE)); + offset++; + } + } + + freeAsmop (op, NULL, ic, TRUE); + } + + op = IC_LEFT (ic); + if (op && IS_SYMOP (op)) + { + aopOp (op, ic, FALSE); + + /* if the result is a bit */ + if (AOP_TYPE (op) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (op)->aopu.aop_dir); + else + { + /* bit variables done */ + /* general case */ + size = AOP_SIZE (op); + offset = 0; + while (size--) + { + MOVA (aopGet (op, offset, FALSE, FALSE)); + offset++; + } + } + + freeAsmop (op, NULL, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* genCritical - generate code for start of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genCritical (iCode * ic) +{ + symbol *tlbl = newiTempLabel (NULL); + + D (emitcode (";", "genCritical")); + + if (IC_RESULT (ic)) + { + aopOp (IC_RESULT (ic), ic, TRUE); + aopPut (IC_RESULT (ic), one, 0); /* save old ea in an operand */ + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + aopPut (IC_RESULT (ic), zero, 0); + emitLabel (tlbl); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + else + { + emitcode ("setb", "c"); + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + emitcode ("clr", "c"); + emitLabel (tlbl); + emitpush ("psw"); /* save old ea via c in psw on top of stack */ + } +} + +/*-----------------------------------------------------------------*/ +/* genEndCritical - generate code for end of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genEndCritical (iCode * ic) +{ + D (emitcode (";", "genEndCritical")); + + if (IC_RIGHT (ic)) + { + aopOp (IC_RIGHT (ic), ic, FALSE); + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + emitcode ("mov", "c,%s", IC_RIGHT (ic)->aop->aopu.aop_dir); + emitcode ("mov", "ea,c"); + } + else + { + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_DUMMY) + MOVA (aopGet (IC_RIGHT (ic), 0, FALSE, FALSE)); + emitcode ("rrc", "a"); + emitcode ("mov", "ea,c"); + } + freeAsmop (IC_RIGHT (ic), NULL, ic, TRUE); + } + else + { + emitpop ("psw"); /* restore ea via c in psw on top of stack */ + emitcode ("mov", "ea,c"); + } +} + +/*-----------------------------------------------------------------*/ +/* gen51Code - generate code for 8051 based controllers */ +/*-----------------------------------------------------------------*/ +void +gen51Code (iCode * lic) +{ + iCode *ic; + int cln = 0; +#ifdef _DEBUG + int cseq = 0; +#endif + + _G.currentFunc = NULL; + + /* print the allocation information */ + if (allocInfo && currFunc) + printAllocInfo (currFunc, codeOutBuf); + /* if debug information required */ + if (options.debug && currFunc) + { + debugFile->writeFunction (currFunc, lic); + } + /* stack pointer name */ + if (options.useXstack) + spname = "_spx"; + else + spname = "sp"; + + for (ic = lic; ic; ic = ic->next) + { + initGenLineElement (); + + genLine.lineElement.ic = ic; + + if (ic->lineno && cln != ic->lineno) + { + if (options.debug) + { + debugFile->writeCLine (ic); + } + if (!options.noCcodeInAsm) + { + emitcode (";", "%s:%d: %s", ic->filename, ic->lineno, printCLine (ic->filename, ic->lineno)); + } + cln = ic->lineno; + } +#ifdef _DEBUG + if (ic->seqPoint && ic->seqPoint != cseq) + { + emitcode (";", "sequence point %d", ic->seqPoint); + cseq = ic->seqPoint; + } +#endif + if (options.iCodeInAsm) + { + char regsInUse[80]; + int i; + const char *iLine; + +#if 0 + for (i = 0; i < 8; i++) + { + sprintf (®sInUse[i], "%c", ic->riu & (1 << i) ? i + '0' : '-'); /* show riu */ + regsInUse[i] = 0; + } +#else + strcpy (regsInUse, "--------"); + for (i = 0; i < 8; i++) + { + if (bitVectBitValue (ic->rMask, i)) + { + int offset = regs8051[i].offset; + regsInUse[offset] = offset + '0'; /* show rMask */ + } + } +#endif + iLine = printILine (ic); + emitcode (";", "[%s] ic:%d: %s", regsInUse, ic->seq, iLine); + dbuf_free (iLine); + } + /* if the result is marked as + spilt and rematerializable or code for + this has already been generated then + do nothing */ + if (resultRemat (ic) || ic->generated) + continue; + + /* depending on the operation */ + switch (ic->op) + { + case '!': + genNot (ic); + break; + + case '~': + genCpl (ic); + break; + + case UNARYMINUS: + genUminus (ic); + break; + + case IPUSH: + genIpush (ic); + break; + + case IPOP: + { + iCode *ifxIc, *popIc; + bool CommonRegs = FALSE; + + /* IPOP happens only when trying to restore a + spilt live range, if there is an ifx statement + following this pop (or several) then the if statement might + be using some of the registers being popped which + would destroy the contents of the register so + we need to check for this condition and handle it */ + for (ifxIc = ic->next; ifxIc && ifxIc->op == IPOP; ifxIc = ifxIc->next); + for (popIc = ic; popIc && popIc->op == IPOP; popIc = popIc->next) + CommonRegs |= (ifxIc && ifxIc->op == IFX && !ifxIc->generated && regsInCommon (IC_LEFT (popIc), IC_COND (ifxIc))); + if (CommonRegs) + genIfx (ifxIc, ic); + else + genIpop (ic); + } + break; + + case CALL: + genCall (ic); + break; + + case PCALL: + genPcall (ic); + break; + + case FUNCTION: + genFunction (ic); + break; + + case ENDFUNCTION: + genEndFunction (ic); + break; + + case RETURN: + genRet (ic); + break; + + case LABEL: + genLabel (ic); + break; + + case GOTO: + genGoto (ic); + break; + + case '+': + genPlus (ic); + break; + + case '-': + if (!genDjnz (ic, ifxForOp (IC_RESULT (ic), ic))) + genMinus (ic); + break; + + case '*': + genMult (ic); + break; + + case '/': + genDiv (ic); + break; + + case '%': + genMod (ic); + break; + + case '>': + genCmpGt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '<': + genCmpLt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case LE_OP: + case GE_OP: + case NE_OP: + + /* note these two are xlated by algebraic equivalence + in decorateType() in SDCCast.c */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "got '>=' or '<=' shouldn't have come here"); + break; + + case EQ_OP: + genCmpEq (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + genAndOp (ic); + break; + + case OR_OP: + genOrOp (ic); + break; + + case '^': + genXor (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '|': + genOr (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case BITWISEAND: + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + genInline (ic); + break; + + case RRC: + genRRC (ic); + break; + + case RLC: + genRLC (ic); + break; + + case GETHBIT: + assert (0); + break; + + case GETABIT: + genGetAbit (ic); + break; + + case GETBYTE: + genGetByte (ic); + break; + + case GETWORD: + genGetWord (ic); + break; + + case LEFT_OP: + genLeftShift (ic); + break; + + case RIGHT_OP: + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + genPointerGet (ic, hasInc (IC_LEFT (ic), ic, getSize (operandType (IC_RESULT (ic)))), ifxForOp (IC_RESULT (ic), ic)); + break; + + case '=': + if (POINTER_SET (ic)) + genPointerSet (ic, hasInc (IC_RESULT (ic), ic, getSize (operandType (IC_RIGHT (ic))))); + else + genAssign (ic); + break; + + case IFX: + genIfx (ic, NULL); + break; + + case ADDRESS_OF: + genAddrOf (ic); + break; + + case JUMPTABLE: + genJumpTab (ic); + break; + + case CAST: + genCast (ic); + break; + + case RECEIVE: + genReceive (ic); + break; + + case SEND: + addSet (&_G.sendSet, ic); + break; + + case DUMMY_READ_VOLATILE: + genDummyRead (ic); + break; + + case CRITICAL: + genCritical (ic); + break; + + case ENDCRITICAL: + genEndCritical (ic); + break; + + case SWAP: + genSwap (ic); + break; + + default: + ic = ic; + } + } + + genLine.lineElement.ic = NULL; + + /* now we are ready to call the + peep hole optimizer */ + if (!options.nopeep) + peepHole (&genLine.lineHead); + + /* now do the actual printing */ + printLine (genLine.lineHead, codeOutBuf); + + /* destroy the line list */ + destroy_line_list (); +} |
