/*------------------------------------------------------------------------- gen.c - source file for code generation for the 68HC08 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) Hacked for the 68HC08: Copyright (C) 2003, Erik Petrich 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. -------------------------------------------------------------------------*/ /* Use the D macro for basic (unobtrusive) debugging messages */ #define D(x) do if (options.verboseAsm) { x; } while (0) /* Use the DD macro for detailed debugging messages */ #define DD(x) //#define DD(x) x #include #include #include #include #include "common.h" #include "hc08.h" #include "ralloc.h" #include "gen.h" #include "dbuf_string.h" extern int allocInfo; static int pushReg (reg_info * reg, bool freereg); static void pullReg (reg_info * reg); static void transferAopAop (asmop * srcaop, int srcofs, asmop * dstaop, int dstofs); static void adjustStack (int n); static char *zero = "#0x00"; static char *one = "#0x01"; unsigned fReturnSizeHC08 = 4; /* shared with ralloc.c */ static struct { short hxPushed; short accInUse; short nRegsSaved; int stackOfs; int stackPushes; short regsinuse; set *sendSet; int tsxStackPushes; } _G; static asmop *hc08_aop_pass[8]; static asmop tsxaop; extern int hc08_ptrRegReq; extern int hc08_nRegs; extern struct dbuf_s *codeOutBuf; static bool operandsEqu (operand * op1, operand * op2); static void loadRegFromConst (reg_info * reg, int c); static asmop *newAsmop (short type); static const char *aopAdrStr (asmop * aop, int loffset, bool bit16); static void updateiTempRegisterUse (operand * op); #define RESULTONSTACK(x) \ (IC_RESULT(x) && IC_RESULT(x)->aop && \ IC_RESULT(x)->aop->type == AOP_STK ) #define IS_AOP_HX(x) ((x)->regmask == HC08MASK_HX) #define IS_AOP_XA(x) ((x)->regmask == HC08MASK_XA) #define IS_AOP_AX(x) ((x)->regmask == HC08MASK_AX) #define IS_AOP_A(x) ((x)->regmask == HC08MASK_A) #define IS_AOP_X(x) ((x)->regmask == HC08MASK_X) #define IS_AOP_H(x) ((x)->regmask == HC08MASK_H) #define IS_AOP_WITH_A(x) (((x)->regmask & HC08MASK_A) != 0) #define IS_AOP_WITH_X(x) (((x)->regmask & HC08MASK_X) != 0) #define IS_AOP_WITH_H(x) (((x)->regmask & HC08MASK_H) != 0) #define IS_AOPOFS_A(x,o) (((x)->type == AOP_REG) && ((x)->aopu.aop_reg[o]->mask == HC08MASK_A)) #define IS_AOPOFS_X(x,o) (((x)->type == AOP_REG) && ((x)->aopu.aop_reg[o]->mask == HC08MASK_X)) #define IS_AOPOFS_H(x,o) (((x)->type == AOP_REG) && ((x)->aopu.aop_reg[o]->mask == HC08MASK_H)) #define LSB 0 #define MSB16 1 #define MSB24 2 #define MSB32 3 #define AOP(op) op->aop #define AOP_TYPE(op) AOP(op)->type #define AOP_SIZE(op) AOP(op)->size #define AOP_OP(aop) aop->op static bool regalloc_dry_run; static unsigned char regalloc_dry_run_cost; static void emitBranch (char *branchop, symbol * tlbl) { if (!regalloc_dry_run) emitcode (branchop, "%05d$", labelKey2num (tlbl->key)); regalloc_dry_run_cost += (!strcmp(branchop, "jmp") || !strcmp(branchop, "brclr") || !strcmp(branchop, "brset") ? 3 : 2); } /*-----------------------------------------------------------------*/ /* hc08_emitDebuggerSymbol - associate the current code location */ /* with a debugger symbol */ /*-----------------------------------------------------------------*/ void hc08_emitDebuggerSymbol (const char *debugSym) { genLine.lineElement.isDebug = 1; emitcode ("", "%s ==.", debugSym); genLine.lineElement.isDebug = 0; } /*--------------------------------------------------------------------------*/ /* transferRegReg - Transfer from register(s) sreg to register(s) dreg. If */ /* freesrc is true, sreg is marked free and available for */ /* reuse. sreg and dreg must be of equal size */ /*--------------------------------------------------------------------------*/ static void transferRegReg (reg_info *sreg, reg_info *dreg, bool freesrc) { int srcidx; int dstidx; char error = 0; /* Nothing to do if no destination. */ if (!dreg) return; /* But it's definately an error if there's no source. */ if (!sreg) { werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "NULL sreg in transferRegReg"); return; } DD (emitcode ("", "; transferRegReg(%s,%s)", sreg->name, dreg->name)); srcidx = sreg->rIdx; dstidx = dreg->rIdx; if (srcidx == dstidx) { hc08_useReg (dreg); return; } switch (dstidx) { case A_IDX: switch (srcidx) { case H_IDX: /* H to A */ pushReg (hc08_reg_h, FALSE); pullReg (hc08_reg_a); break; case X_IDX: /* X to A */ emitcode ("txa", ""); regalloc_dry_run_cost++; break; default: error = 1; } break; case H_IDX: switch (srcidx) { case A_IDX: /* A to H */ pushReg (hc08_reg_a, FALSE); pullReg (hc08_reg_h); break; case X_IDX: /* X to H */ pushReg (hc08_reg_x, FALSE); pullReg (hc08_reg_h); break; default: error = 1; } break; case X_IDX: switch (srcidx) { case A_IDX: /* A to X */ emitcode ("tax", ""); regalloc_dry_run_cost++; break; case H_IDX: /* H to X */ pushReg (hc08_reg_h, FALSE); pullReg (hc08_reg_x); break; default: error = 1; } break; case HX_IDX: switch (srcidx) { case XA_IDX: /* XA to HX */ pushReg (hc08_reg_x, FALSE); pullReg (hc08_reg_h); emitcode ("tax", ""); regalloc_dry_run_cost++; break; default: error = 1; } break; case XA_IDX: switch (srcidx) { case HX_IDX: /* HX to XA */ emitcode ("txa", ""); regalloc_dry_run_cost++; pushReg (hc08_reg_h, FALSE); pullReg (hc08_reg_x); break; default: error = 1; } break; default: error = 1; } wassertl (!error, "bad combo in transferRegReg"); if (freesrc) hc08_freeReg (sreg); dreg->aop = sreg->aop; dreg->aopofs = sreg->aopofs; dreg->isFree = FALSE; hc08_dirtyReg (dreg, FALSE); hc08_useReg (dreg); } /*--------------------------------------------------------------------------*/ /* updateCFA - update the debugger information to reflect the current */ /* connonical frame address relative to the stack pointer */ /*--------------------------------------------------------------------------*/ static void updateCFA (void) { /* there is no frame unless there is a function */ if (!currFunc) return; if (options.debug && !regalloc_dry_run) debugFile->writeFrameAddress (NULL, hc08_reg_sp, 1 + _G.stackOfs + _G.stackPushes); } /*--------------------------------------------------------------------------*/ /* pushReg - Push register reg onto the stack. If freereg is true, reg is */ /* marked free and available for reuse. */ /*--------------------------------------------------------------------------*/ static int pushReg (reg_info * reg, bool freereg) { int regidx = reg->rIdx; switch (regidx) { case A_IDX: emitcode ("psha", ""); regalloc_dry_run_cost++; _G.stackPushes++; updateCFA (); break; case X_IDX: emitcode ("pshx", ""); regalloc_dry_run_cost++; _G.stackPushes++; updateCFA (); break; case H_IDX: emitcode ("pshh", ""); regalloc_dry_run_cost++; _G.stackPushes++; updateCFA (); break; case HX_IDX: emitcode ("pshx", ""); regalloc_dry_run_cost++; _G.stackPushes++; updateCFA (); emitcode ("pshh", ""); regalloc_dry_run_cost++; _G.stackPushes++; updateCFA (); break; case XA_IDX: emitcode ("psha", ""); regalloc_dry_run_cost++; updateCFA (); _G.stackPushes++; emitcode ("pshx", ""); regalloc_dry_run_cost++; updateCFA (); _G.stackPushes++; break; default: break; } if (freereg) hc08_freeReg (reg); return -_G.stackOfs - _G.stackPushes; } /*--------------------------------------------------------------------------*/ /* pullReg - Pull register reg off the stack. */ /*--------------------------------------------------------------------------*/ static void pullReg (reg_info * reg) { int regidx = reg->rIdx; switch (regidx) { case A_IDX: emitcode ("pula", ""); regalloc_dry_run_cost++; _G.stackPushes--; updateCFA (); break; case X_IDX: emitcode ("pulx", ""); regalloc_dry_run_cost++; _G.stackPushes--; updateCFA (); break; case H_IDX: emitcode ("pulh", ""); regalloc_dry_run_cost++; _G.stackPushes--; updateCFA (); break; case HX_IDX: emitcode ("pulh", ""); regalloc_dry_run_cost++; _G.stackPushes--; updateCFA (); emitcode ("pulx", ""); regalloc_dry_run_cost++; _G.stackPushes--; updateCFA (); break; case XA_IDX: emitcode ("pulx", ""); regalloc_dry_run_cost++; _G.stackPushes--; updateCFA (); emitcode ("pula", ""); regalloc_dry_run_cost++; _G.stackPushes--; updateCFA (); break; default: break; } hc08_useReg (reg); hc08_dirtyReg (reg, FALSE); } /*--------------------------------------------------------------------------*/ /* pullNull - Discard n bytes off the top of the stack */ /*--------------------------------------------------------------------------*/ static void pullNull (int n) { wassert (n >= 0); adjustStack (n); } /*--------------------------------------------------------------------------*/ /* pushConst - Push a constant byte value onto stack */ /*--------------------------------------------------------------------------*/ static void pushConst (int c) { if (hc08_reg_a->isFree) { loadRegFromConst (hc08_reg_a, c); pushReg (hc08_reg_a, TRUE); } else if (hc08_reg_x->isFree) { loadRegFromConst (hc08_reg_x, c); pushReg (hc08_reg_x, TRUE); } else if (hc08_reg_h->isFree && !c) { loadRegFromConst (hc08_reg_h, c); pushReg (hc08_reg_h, TRUE); } else if (!c) { adjustStack (-1); emitcode ("clr", "1,s"); regalloc_dry_run_cost += 3; } else { pushReg (hc08_reg_a, FALSE); pushReg (hc08_reg_a, FALSE); loadRegFromConst (hc08_reg_a, c); emitcode ("sta", "2,s"); regalloc_dry_run_cost += 3; pullReg (hc08_reg_a); } } /*--------------------------------------------------------------------------*/ /* pushRegIfUsed - Push register reg if marked in use. Returns true if the */ /* push was performed, false otherwise. */ /*--------------------------------------------------------------------------*/ static bool pushRegIfUsed (reg_info *reg) { if (!reg->isFree) { pushReg (reg, TRUE); return TRUE; } else return FALSE; } /*--------------------------------------------------------------------------*/ /* pushRegIfSurv - Push register reg if marked surviving. Returns true if */ /* the push was performed, false otherwise. */ /*--------------------------------------------------------------------------*/ static bool pushRegIfSurv (reg_info *reg) { if (!reg->isDead) { pushReg (reg, TRUE); return TRUE; } else return FALSE; } /*--------------------------------------------------------------------------*/ /* pullOrFreeReg - If needpull is true, register reg is pulled from the */ /* stack. Otherwise register reg is marked as free. */ /*--------------------------------------------------------------------------*/ static void pullOrFreeReg (reg_info * reg, bool needpull) { if (needpull) pullReg (reg); else hc08_freeReg (reg); } /*--------------------------------------------------------------------------*/ /* adjustStack - Adjust the stack pointer by n bytes. */ /*--------------------------------------------------------------------------*/ static void adjustStack (int n) { while (n) { if (n > 127) { emitcode ("ais", "#127"); regalloc_dry_run_cost += 2; n -= 127; _G.stackPushes -= 127; updateCFA (); } else if (n < -128) { emitcode ("ais", "#-128"); regalloc_dry_run_cost += 2; n += 128; _G.stackPushes += 128; updateCFA (); } else { if (n == -1) { emitcode ("pshh", ""); /* 1 byte, 2 cycles */ regalloc_dry_run_cost++; } else if (n == 1 && optimize.codeSize && hc08_reg_h->isFree) { emitcode ("pulh", ""); /* 1 byte, 3 cycles */ regalloc_dry_run_cost++; } else { emitcode ("ais", "#%d", n); /* 2 bytes, 2 cycles */ regalloc_dry_run_cost += 2; } _G.stackPushes -= n; n = 0; updateCFA (); } } } #if DD(1) -1 == 0 /*--------------------------------------------------------------------------*/ /* aopName - Return a string with debugging information about an asmop. */ /*--------------------------------------------------------------------------*/ static char * aopName (asmop * aop) { static char buffer[256]; char *buf = buffer; if (!aop) return "(asmop*)NULL"; switch (aop->type) { case AOP_IMMD: sprintf (buf, "IMMD(%s)", aop->aopu.aop_immd.aop_immd1); return buf; case AOP_LIT: sprintf (buf, "LIT(%s)", aopLiteral (aop->aopu.aop_lit, 0)); return buf; case AOP_DIR: sprintf (buf, "DIR(%s)", aop->aopu.aop_dir); return buf; case AOP_EXT: sprintf (buf, "EXT(%s)", aop->aopu.aop_dir); return buf; case AOP_SOF: sprintf (buf, "SOF(%s)", OP_SYMBOL (aop->op)->name); return buf; case AOP_REG: sprintf (buf, "REG(%s,%s,%s,%s)", aop->aopu.aop_reg[3] ? aop->aopu.aop_reg[3]->name : "-", aop->aopu.aop_reg[2] ? aop->aopu.aop_reg[2]->name : "-", aop->aopu.aop_reg[1] ? aop->aopu.aop_reg[1]->name : "-", aop->aopu.aop_reg[0] ? aop->aopu.aop_reg[0]->name : "-"); return buf; case AOP_STK: return "STK"; default: sprintf (buf, "?%d", aop->type); return buf; } return "?"; } #endif /*--------------------------------------------------------------------------*/ /* loadRegFromAop - Load register reg from logical offset loffset of aop. */ /* For multi-byte registers, loffset is of the lsb reg. */ /*--------------------------------------------------------------------------*/ static void loadRegFromAop (reg_info * reg, asmop * aop, int loffset) { int regidx = reg->rIdx; if (aop->stacked && aop->stk_aop[loffset]) { loadRegFromAop (reg, aop->stk_aop[loffset], 0); return; } DD (emitcode ("", "; loadRegFromAop (%s, %s, %d)", reg->name, aopName (aop), loffset)); #if 0 /* If operand is volatile, we cannot optimize. */ if (!aop->op || isOperandVolatile (aop->op, FALSE)) goto forceload; /* If this register already has this offset of the operand then we need only mark it as in use. */ if (reg->aop && reg->aop->op && aop->op && operandsEqu (reg->aop->op, aop->op) && (reg->aopofs == loffset)) { hc08_useReg (reg); DD (emitcode ("", "; already had correct value for %s", reg->name)); return; } /* TODO: check to see if we can transfer from another register */ if (hc08_reg_h->aop && hc08_reg_h->aop->op && aop->op && operandsEqu (hc08_reg_h->aop->op, aop->op) && (hc08_reg_h->aopofs == loffset)) { DD (emitcode ("", "; found correct value for %s in h", reg->name)); transferRegReg (hc08_reg_h, reg, FALSE); hc08_useReg (reg); return; } if (hc08_reg_x->aop && hc08_reg_x->aop->op && aop->op && operandsEqu (hc08_reg_x->aop->op, aop->op) && (hc08_reg_x->aopofs == loffset)) { DD (emitcode ("", "; found correct value for %s in x", reg->name)); transferRegReg (hc08_reg_x, reg, FALSE); hc08_useReg (reg); return; } if (hc08_reg_a->aop && hc08_reg_a->aop->op && aop->op && operandsEqu (hc08_reg_a->aop->op, aop->op) && (hc08_reg_a->aopofs == loffset)) { DD (emitcode ("", "; found correct value for %s in a", reg->name)); transferRegReg (hc08_reg_a, reg, FALSE); hc08_useReg (reg); return; } forceload: #endif switch (regidx) { case A_IDX: if (aop->type == AOP_REG) { if (loffset < aop->size) transferRegReg (aop->aopu.aop_reg[loffset], reg, FALSE); else loadRegFromConst (reg, 0); /* TODO: handle sign extension */ } else { if (aop->type == AOP_LIT) { loadRegFromConst (reg, byteOfVal (aop->aopu.aop_lit, loffset)); } else { const char *l = aopAdrStr (aop, loffset, FALSE); emitcode ("lda", "%s", l); regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD || aop->type == AOP_LIT) ? 2 : 3); hc08_dirtyReg (reg, FALSE); } } break; case X_IDX: if (aop->type == AOP_REG) { if (loffset < aop->size) transferRegReg (aop->aopu.aop_reg[loffset], reg, FALSE); else loadRegFromConst (reg, 0); /* TODO: handle sign extension */ } else { if (aop->type == AOP_LIT) { loadRegFromConst (reg, byteOfVal (aop->aopu.aop_lit, loffset)); } else { const char *l = aopAdrStr (aop, loffset, FALSE); emitcode ("ldx", "%s", l); regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD || aop->type == AOP_LIT) ? 2 : 3); hc08_dirtyReg (reg, FALSE); } } break; case H_IDX: if (aop->type == AOP_LIT) { loadRegFromConst (reg, byteOfVal (aop->aopu.aop_lit, loffset)); break; } if (aop->type == AOP_SOF && !(_G.stackOfs + _G.stackPushes + aop->aopu.aop_stk + aop->size - loffset - 1)) { pullReg (hc08_reg_h); pushReg (hc08_reg_h, FALSE); break; } if (aop->type == AOP_REG && loffset < aop->size) transferRegReg (aop->aopu.aop_reg[loffset], hc08_reg_h, TRUE); else if (!(aop->op && isOperandVolatile (aop->op, FALSE)) && (loffset - 1 >= 0 || aop->type == AOP_LIT) && (aop->type == AOP_LIT || aop->type == AOP_IMMD || IS_S08 && aop->type == AOP_EXT)) /* TODO: Allow negative loffset - 1 */ { bool pushedx = FALSE; if (!hc08_reg_x->isFree) { pushReg (hc08_reg_x, TRUE); pushedx = TRUE; } loadRegFromAop (hc08_reg_hx, aop, loffset - 1); pullOrFreeReg (hc08_reg_x, pushedx); } else if (hc08_reg_a->isFree) { loadRegFromAop (hc08_reg_a, aop, loffset); transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); } else if (hc08_reg_x->isFree) { loadRegFromAop (hc08_reg_x, aop, loffset); transferRegReg (hc08_reg_x, hc08_reg_h, TRUE); } else { pushReg (hc08_reg_a, TRUE); loadRegFromAop (hc08_reg_a, aop, loffset); transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); pullReg (hc08_reg_a); } break; case HX_IDX: if (aop->type == AOP_LIT) { loadRegFromConst (reg, byteOfVal (aop->aopu.aop_lit, loffset) | (byteOfVal (aop->aopu.aop_lit, loffset+1) << 8)); break; } if (aop->type == AOP_SOF) { int offset = (_G.stackOfs + _G.stackPushes + aop->aopu.aop_stk + aop->size - loffset - 1); if (IS_S08 && offset >= 0 && offset <= 0xff) { emitcode ("ldhx", "%s", aopAdrStr (aop, loffset, TRUE)); regalloc_dry_run_cost += 2; hc08_dirtyReg (reg, FALSE); break; } else if (offset == 1) { pullReg (hc08_reg_h); pullReg (hc08_reg_x); pushReg (hc08_reg_x, FALSE); pushReg (hc08_reg_h, FALSE); break; } } if (IS_AOP_HX (aop)) break; else if (IS_AOP_XA (aop)) transferRegReg (hc08_reg_xa, hc08_reg_hx, FALSE); else if (aop->type == AOP_DIR || IS_S08 && aop->type == AOP_EXT) { if (aop->size >= (loffset + 2)) { emitcode ("ldhx", "%s", aopAdrStr (aop, loffset, TRUE)); regalloc_dry_run_cost += (aop->type == AOP_DIR ? 2 : 3); hc08_dirtyReg (reg, FALSE); } else { loadRegFromConst (hc08_reg_h, 0); loadRegFromAop (hc08_reg_x, aop, loffset); } } else if ((aop->type == AOP_LIT) || (aop->type == AOP_IMMD)) { emitcode ("ldhx", "%s", aopAdrStr (aop, loffset, TRUE)); regalloc_dry_run_cost += 3; hc08_dirtyReg (reg, FALSE); } else { loadRegFromAop (hc08_reg_h, aop, loffset + 1); loadRegFromAop (hc08_reg_x, aop, loffset); } break; case XA_IDX: if (IS_AOP_XA (aop)) break; else if (IS_AOP_HX (aop)) transferRegReg (hc08_reg_hx, hc08_reg_xa, FALSE); else if (IS_AOP_AX (aop)) { pushReg (hc08_reg_a, FALSE); transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); pullReg (hc08_reg_x); } else { loadRegFromAop (hc08_reg_a, aop, loffset); loadRegFromAop (hc08_reg_x, aop, loffset + 1); } break; } hc08_useReg (reg); } /*--------------------------------------------------------------------------*/ /* loadRegHXAfromAop - Load registers A, H, and X from aops. */ /* This takes care of the tricky cases where the */ /* sources may be registers that overlap with the */ /* destination registers. An aop may be NULL if the */ /* corresponding register does not need to be loaded. */ /*--------------------------------------------------------------------------*/ static void loadRegHXAfromAop(asmop * aopH, int ofsH, asmop * aopX, int ofsX, asmop * aopA, int ofsA) { /* There are three cases where pairs of registers need to be swapped */ if (aopA && aopX && IS_AOPOFS_X (aopA, ofsA) && IS_AOPOFS_A (aopX, ofsX)) { /* Swap A and X, load H */ pushReg (hc08_reg_a, TRUE); if (aopH) loadRegFromAop (hc08_reg_h, aopH, ofsH); transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); pullReg (hc08_reg_x); return; } if (aopA && aopH && IS_AOPOFS_H (aopA, ofsA) && IS_AOPOFS_A (aopH, ofsH)) { /* Swap A and H, load X */ pushReg (hc08_reg_a, TRUE); if (aopX) loadRegFromAop (hc08_reg_x, aopX, ofsX); transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); pullReg (hc08_reg_h); return; } if (aopX && aopH && IS_AOPOFS_H (aopX, ofsX) && IS_AOPOFS_X (aopH, ofsH)) { /* Swap X and H, load A */ pushReg (hc08_reg_x, TRUE); if (aopA) loadRegFromAop (hc08_reg_a, aopA, ofsA); transferRegReg (hc08_reg_h, hc08_reg_x, FALSE); pullReg (hc08_reg_h); return; } /* There are two cases where the registers need to rotate */ if (aopA && aopH && aopX && IS_AOPOFS_A (aopH, ofsH) && IS_AOPOFS_H (aopX, ofsX) && IS_AOPOFS_X (aopA, ofsA)) { /* Rotate A->H->X->A */ pushReg (hc08_reg_a, FALSE); transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); transferRegReg (hc08_reg_h, hc08_reg_x, FALSE); pullReg (hc08_reg_h); return; } if (aopA && aopH && aopX && IS_AOPOFS_A (aopX, ofsX) && IS_AOPOFS_X (aopH, ofsH) && IS_AOPOFS_H (aopA, ofsA)) { /* Rotate A->X->H->A */ pushReg (hc08_reg_a, FALSE); transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); transferRegReg (hc08_reg_x, hc08_reg_h, FALSE); pullReg (hc08_reg_x); return; } /* At this point there can be at most 1 overlapping register source. */ if (aopX && (aopX->type == AOP_REG) && !IS_AOP_WITH_X (aopX)) { loadRegFromAop (hc08_reg_x, aopX, ofsX); if (aopA) hc08_freeReg (hc08_reg_a); /* in case it's needed to load H */ if (aopH) loadRegFromAop (hc08_reg_h, aopH, ofsH); if (aopA) loadRegFromAop (hc08_reg_a, aopA, ofsA); return; } if (aopH && (aopH->type == AOP_REG) && !IS_AOP_WITH_H (aopH)) { if (aopA) hc08_freeReg (hc08_reg_a); /* in case it's needed to load H */ if (aopX) hc08_freeReg (hc08_reg_x); /* in case it's needed to load H */ loadRegFromAop (hc08_reg_h, aopH, ofsH); if (aopX) loadRegFromAop (hc08_reg_x, aopX, ofsX); if (aopA) loadRegFromAop (hc08_reg_a, aopA, ofsA); return; } /* Either A needs to be loaded first or the order no longer matters */ if (aopA) loadRegFromAop (hc08_reg_a, aopA, ofsA); if (aopX) hc08_freeReg (hc08_reg_x); /* in case it's needed to load H */ if (aopH && aopX && (aopH == aopX) && (ofsH == (ofsX+1))) loadRegFromAop (hc08_reg_hx, aopX, ofsX); else { if (aopH) loadRegFromAop (hc08_reg_h, aopH, ofsH); if (aopX) loadRegFromAop (hc08_reg_x, aopX, ofsX); } } /*--------------------------------------------------------------------------*/ /* forceStackedAop - Reserve space on the stack for asmop aop; when */ /* freeAsmop is called with aop, the stacked data will */ /* be copied to the original aop location. */ /*--------------------------------------------------------------------------*/ static asmop * forceStackedAop (asmop * aop, bool copyOrig) { reg_info *reg; int loffset; asmop *newaop = newAsmop (aop->type); memcpy (newaop, aop, sizeof (*newaop)); DD (emitcode ("", "; forcedStackAop %s", aopName (aop))); if (copyOrig && hc08_reg_a->isFree) reg = hc08_reg_a; else if (copyOrig && hc08_reg_x->isFree) reg = hc08_reg_x; else reg = NULL; for (loffset = 0; loffset < newaop->size; loffset++) { asmop *aopsof = newAsmop (AOP_SOF); aopsof->size = 1; if (copyOrig && reg) { loadRegFromAop (reg, aop, loffset); aopsof->aopu.aop_stk = pushReg (reg, FALSE); } else { aopsof->aopu.aop_stk = pushReg (hc08_reg_a, FALSE); } aopsof->op = aop->op; newaop->stk_aop[loffset] = aopsof; } newaop->stacked = 1; if (!reg && copyOrig) { for (loffset = 0; loffset < newaop->size; loffset++) { transferAopAop (aop, loffset, newaop, loffset); } } return newaop; } /*--------------------------------------------------------------------------*/ /* storeRegToAop - Store register reg to logical offset loffset of aop. */ /* For multi-byte registers, loffset is of the lsb reg. */ /*--------------------------------------------------------------------------*/ static void storeRegToAop (reg_info *reg, asmop * aop, int loffset) { int regidx = reg->rIdx; DD (emitcode ("", "; storeRegToAop (%s, %s, %d), stacked=%d", reg->name, aopName (aop), loffset, aop->stacked)); if ((reg->rIdx == HX_IDX) && aop->stacked && (aop->stk_aop[loffset] || aop->stk_aop[loffset + 1])) { storeRegToAop (hc08_reg_h, aop, loffset + 1); storeRegToAop (hc08_reg_x, aop, loffset); return; } if ((reg->rIdx == XA_IDX) && aop->stacked && (aop->stk_aop[loffset] || aop->stk_aop[loffset + 1])) { storeRegToAop (hc08_reg_x, aop, loffset + 1); storeRegToAop (hc08_reg_a, aop, loffset); return; } if (aop->stacked && aop->stk_aop[loffset]) { storeRegToAop (reg, aop->stk_aop[loffset], 0); return; } if (aop->type == AOP_DUMMY) return; if (aop->type == AOP_CRY) /* This can only happen if IFX was optimized */ return; /* away, so just toss the result */ switch (regidx) { case A_IDX: if ((aop->type == AOP_REG) && (loffset < aop->size)) transferRegReg (reg, aop->aopu.aop_reg[loffset], FALSE); else { emitcode ("sta", "%s", aopAdrStr (aop, loffset, FALSE)); regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD) ? 2 :3); } break; case X_IDX: if ((aop->type == AOP_REG) && (loffset < aop->size)) transferRegReg (reg, aop->aopu.aop_reg[loffset], FALSE); else { emitcode ("stx", "%s", aopAdrStr (aop, loffset, FALSE)); regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD) ? 2 :3); } break; case H_IDX: if ((aop->type == AOP_REG) && (loffset < aop->size)) transferRegReg (reg, aop->aopu.aop_reg[loffset], FALSE); else if (hc08_reg_a->isFree) { transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); storeRegToAop (hc08_reg_a, aop, loffset); hc08_freeReg (hc08_reg_a); } else if (hc08_reg_x->isFree) { transferRegReg (hc08_reg_h, hc08_reg_x, FALSE); storeRegToAop (hc08_reg_x, aop, loffset); hc08_freeReg (hc08_reg_x); } else { pushReg (hc08_reg_a, TRUE); transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); storeRegToAop (hc08_reg_a, aop, loffset); pullReg (hc08_reg_a); } break; case HX_IDX: if (aop->type == AOP_SOF) { int offset = (_G.stackOfs + _G.stackPushes + aop->aopu.aop_stk + aop->size - loffset - 1); if (IS_S08 && offset >= 0 && offset <= 0xff) { emitcode ("sthx", "%s", aopAdrStr (aop, loffset, TRUE)); regalloc_dry_run_cost += 2; break; } } if (aop->type == AOP_DIR || IS_S08 && aop->type == AOP_EXT) { emitcode ("sthx", "%s", aopAdrStr (aop, loffset, TRUE)); regalloc_dry_run_cost += (aop->type == AOP_DIR ? 2 : 3);; } else if (IS_AOP_XA (aop)) transferRegReg (reg, hc08_reg_xa, FALSE); else if (IS_AOP_HX (aop)) break; else if (hc08_reg_a->isFree) { bool needpula; needpula = pushRegIfUsed (hc08_reg_a); transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); storeRegToAop (hc08_reg_a, aop, loffset + 1); storeRegToAop (hc08_reg_x, aop, loffset); pullOrFreeReg (hc08_reg_a, needpula); } else { bool needpulx; storeRegToAop (hc08_reg_x, aop, loffset); needpulx = pushRegIfUsed (hc08_reg_x); transferRegReg (hc08_reg_h, hc08_reg_x, FALSE); storeRegToAop (hc08_reg_x, aop, loffset + 1); pullOrFreeReg (hc08_reg_x, needpulx); } break; case XA_IDX: if (IS_AOP_HX (aop)) transferRegReg (reg, hc08_reg_hx, FALSE); else if (IS_AOP_XA (aop)) break; else if (IS_AOP_AX (aop)) { pushReg (hc08_reg_a, FALSE); transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); pullReg (hc08_reg_x); } else { storeRegToAop (hc08_reg_a, aop, loffset); storeRegToAop (hc08_reg_x, aop, loffset + 1); } break; default: wassert (0); } /* Disable the register tracking for now */ #if 0 //if (!reg->aop || (reg->aop && (reg->aop != aop))) { //if (reg->aop!=aop) for (otheridx = 0; otheridx < hc08_nRegs; otheridx++) { otherreg = hc08_regWithIdx (otheridx); if (otherreg && otherreg->aop && otherreg->aop->op && aop->op && operandsEqu (otherreg->aop->op, aop->op) && (otherreg->aopofs == loffset)) { DD (emitcode ("", "; marking %s stale", otherreg->name)); otherreg->aop = NULL; } } if ((!hc08_reg_x->aop || !hc08_reg_h->aop) && hc08_reg_hx->aop) { hc08_reg_hx->aop = NULL; DD (emitcode ("", "; marking hx stale")); } if ((!hc08_reg_x->aop || !hc08_reg_a->aop) && hc08_reg_xa->aop) { hc08_reg_xa->aop = NULL; DD (emitcode ("", "; marking xa stale")); } reg->aop = aop; reg->aopofs = loffset; } #endif } /*--------------------------------------------------------------------------*/ /* loadRegFromConst - Load register reg from constant c. */ /*--------------------------------------------------------------------------*/ static void loadRegFromConst (reg_info * reg, int c) { switch (reg->rIdx) { case A_IDX: c &= 0xff; if (reg->isLitConst) { if (reg->litConst == c) break; if (((reg->litConst + 1) & 0xff) == c) { emitcode ("inca", ""); regalloc_dry_run_cost++; break; } if (((reg->litConst - 1) & 0xff) == c) { emitcode ("deca", ""); regalloc_dry_run_cost++; break; } } if (hc08_reg_x->isLitConst && hc08_reg_x->litConst == c) transferRegReg (hc08_reg_x, reg, FALSE); else if (!c) { emitcode ("clra", ""); regalloc_dry_run_cost++; } else { emitcode ("lda", "!immedbyte", c); regalloc_dry_run_cost += 2; } break; case X_IDX: c &= 0xff; if (reg->isLitConst) { if (reg->litConst == c) break; if (((reg->litConst + 1) & 0xff) == c) { emitcode ("incx", ""); regalloc_dry_run_cost++; break; } if (((reg->litConst - 1) & 0xff) == c) { emitcode ("decx", ""); regalloc_dry_run_cost++; break; } } if (hc08_reg_a->isLitConst && hc08_reg_a->litConst == c) transferRegReg (hc08_reg_a, reg, FALSE); else if (!c) { emitcode ("clrx", ""); regalloc_dry_run_cost++; } else { emitcode ("ldx", "!immedbyte", c); regalloc_dry_run_cost += 2; } break; case H_IDX: c &= 0xff; if (reg->isLitConst && reg->litConst == c) break; if (!c) { emitcode ("clrh", ""); regalloc_dry_run_cost++; } else if (hc08_reg_a->isLitConst && hc08_reg_a->litConst == c) transferRegReg (hc08_reg_a, reg, FALSE); else if (hc08_reg_x->isLitConst && hc08_reg_x->litConst == c) transferRegReg (hc08_reg_x, reg, FALSE); else if (hc08_reg_a->isFree) { loadRegFromConst (hc08_reg_a, c); transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); } else if (hc08_reg_x->isFree) { loadRegFromConst (hc08_reg_hx, c << 8); } else { pushReg (hc08_reg_x, TRUE); loadRegFromConst (hc08_reg_hx, c << 8); pullReg (hc08_reg_x); } break; case HX_IDX: c &= 0xffff; if (reg->isLitConst) { int delta = (c - reg->litConst) & 0xffff; if (delta & 0x8000) delta = -0x8000 + (delta & 0x7fff); if (reg->litConst == c) break; if ((reg->litConst & 0xff) == c) { loadRegFromConst (hc08_reg_h, 0); break; } if ((reg->litConst & 0xff00) == (c & 0xff00)) { loadRegFromConst (hc08_reg_x, c & 0xff); break; } if ((delta <= 127) && (delta >= -128)) { emitcode ("aix","#%d", delta); regalloc_dry_run_cost += 2; break; } } if (!c) { loadRegFromConst (hc08_reg_h, 0); loadRegFromConst (hc08_reg_x, 0); break; } emitcode ("ldhx", "!immedword", c); regalloc_dry_run_cost += 3; break; case XA_IDX: c &= 0xffff; if (reg->isLitConst && reg->litConst == c) break; loadRegFromConst (hc08_reg_a, c); loadRegFromConst (hc08_reg_x, c >> 8); break; default: werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "Bad rIdx in loadRegFromConst"); return; } hc08_dirtyReg (reg, FALSE); reg->isLitConst = 1; reg->litConst = c; if (reg->rIdx == HX_IDX) { hc08_reg_x->isLitConst = 1; hc08_reg_x->litConst = c & 0xff; hc08_reg_h->isLitConst = 1; hc08_reg_h->litConst = (c >> 8) & 0xff; } hc08_useReg (reg); } /*--------------------------------------------------------------------------*/ /* loadRegFromImm - Load register reg from immediate value c. */ /*--------------------------------------------------------------------------*/ static void loadRegFromImm (reg_info * reg, char * c) { if (*c == '#') c++; switch (reg->rIdx) { case A_IDX: emitcode ("lda", "#%s", c); regalloc_dry_run_cost += 2; break; case X_IDX: emitcode ("ldx", "#%s", c); regalloc_dry_run_cost += 2; break; case H_IDX: if (hc08_reg_a->isFree) { loadRegFromImm (hc08_reg_a, c); transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); } else if (hc08_reg_x->isFree) { loadRegFromImm (hc08_reg_x, c); transferRegReg (hc08_reg_x, hc08_reg_h, TRUE); } else { pushReg (hc08_reg_a, TRUE); loadRegFromImm (hc08_reg_a, c); transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); pullReg (hc08_reg_a); } break; case HX_IDX: emitcode ("ldhx", "#%s", c); regalloc_dry_run_cost += 3; break; case XA_IDX: emitcode ("lda", "#%s", c); emitcode ("ldx", "#%s >> 8", c); regalloc_dry_run_cost += 4; break; default: werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "Bad rIdx in loadRegFromConst"); return; } hc08_dirtyReg (reg, FALSE); hc08_useReg (reg); } /*--------------------------------------------------------------------------*/ /* storeConstToAop- Store constant c to logical offset loffset of asmop aop.*/ /*--------------------------------------------------------------------------*/ static void storeConstToAop (int c, asmop * aop, int loffset) { if (aop->stacked && aop->stk_aop[loffset]) { storeConstToAop (c, aop->stk_aop[loffset], 0); return; } /* If the value needed is already in A or X, just store it */ if (hc08_reg_a->isLitConst && hc08_reg_a->litConst == c) { storeRegToAop (hc08_reg_a, aop, loffset); return; } if (hc08_reg_x->isLitConst && hc08_reg_x->litConst == c) { storeRegToAop (hc08_reg_x, aop, loffset); return; } switch (aop->type) { case AOP_DIR: /* clr operates with read-modify-write cycles, so don't use if the */ /* destination is volatile to avoid the read side-effect. */ if (!c && !(aop->op && isOperandVolatile (aop->op, FALSE)) && optimize.codeSize) { /* clr dst : 2 bytes, 5 cycles */ emitcode ("clr", "%s", aopAdrStr (aop, loffset, FALSE)); regalloc_dry_run_cost += 2; } else { /* mov #0,dst : 3 bytes, 4 cycles */ emitcode ("mov", "!immedbyte,%s", c, aopAdrStr (aop, loffset, FALSE)); regalloc_dry_run_cost += 3; } break; case AOP_REG: if (loffset > (aop->size - 1)) break; loadRegFromConst (aop->aopu.aop_reg[loffset], c); break; case AOP_DUMMY: break; default: if (hc08_reg_a->isFree) { loadRegFromConst (hc08_reg_a, c); storeRegToAop (hc08_reg_a, aop, loffset); hc08_freeReg (hc08_reg_a); } else if (hc08_reg_x->isFree) { loadRegFromConst (hc08_reg_x, c); storeRegToAop (hc08_reg_x, aop, loffset); hc08_freeReg (hc08_reg_x); } else { pushReg (hc08_reg_a, TRUE); loadRegFromConst (hc08_reg_a, c); storeRegToAop (hc08_reg_a, aop, loffset); pullReg (hc08_reg_a); } } } /*--------------------------------------------------------------------------*/ /* storeImmToAop- Store immediate value c to logical offset loffset of asmop aop.*/ /*--------------------------------------------------------------------------*/ static void storeImmToAop (char *c, asmop * aop, int loffset) { if (aop->stacked && aop->stk_aop[loffset]) { storeImmToAop (c, aop->stk_aop[loffset], 0); return; } switch (aop->type) { case AOP_DIR: /* clr operates with read-modify-write cycles, so don't use if the */ /* destination is volatile to avoid the read side-effect. */ if (!strcmp (c, zero) && !(aop->op && isOperandVolatile (aop->op, FALSE)) && optimize.codeSize) { /* clr dst : 2 bytes, 5 cycles */ emitcode ("clr", "%s", aopAdrStr (aop, loffset, FALSE)); regalloc_dry_run_cost += 2; } else { /* mov #0,dst : 3 bytes, 4 cycles */ emitcode ("mov", "%s,%s", c, aopAdrStr (aop, loffset, FALSE)); regalloc_dry_run_cost += 3; } break; case AOP_REG: if (loffset > (aop->size - 1)) break; loadRegFromImm (aop->aopu.aop_reg[loffset], c); break; case AOP_DUMMY: break; default: if (hc08_reg_a->isFree) { loadRegFromImm (hc08_reg_a, c); storeRegToAop (hc08_reg_a, aop, loffset); hc08_freeReg (hc08_reg_a); } else if (hc08_reg_x->isFree) { loadRegFromImm (hc08_reg_x, c); storeRegToAop (hc08_reg_x, aop, loffset); hc08_freeReg (hc08_reg_x); } else { pushReg (hc08_reg_a, TRUE); loadRegFromImm (hc08_reg_a, c); storeRegToAop (hc08_reg_a, aop, loffset); pullReg (hc08_reg_a); } } } /*--------------------------------------------------------------------------*/ /* storeRegSignToUpperAop - If isSigned is true, the sign bit of register */ /* reg is extended to fill logical offsets loffset */ /* and above of asmop aop. Otherwise, logical */ /* offsets loffset and above of asmop aop are */ /* zeroed. reg must be an 8-bit register. */ /*--------------------------------------------------------------------------*/ static void storeRegSignToUpperAop (reg_info * reg, asmop * aop, int loffset, bool isSigned) { // int regidx = reg->rIdx; int size = aop->size; if (size <= loffset) return; if (!isSigned) { /* Unsigned case */ while (loffset < size) storeConstToAop (0, aop, loffset++); } else { /* Signed case */ transferRegReg (reg, hc08_reg_a, FALSE); emitcode ("rola", ""); emitcode ("clra", ""); emitcode ("sbc", "#0"); regalloc_dry_run_cost += 4; hc08_useReg (hc08_reg_a); while (loffset < size) storeRegToAop (hc08_reg_a, aop, loffset++); hc08_freeReg (hc08_reg_a); } } /*--------------------------------------------------------------------------*/ /* storeRegToFullAop - Store register reg to asmop aop with appropriate */ /* padding and/or truncation as needed. If isSigned is */ /* true, sign extension will take place in the padding. */ /*--------------------------------------------------------------------------*/ static void storeRegToFullAop (reg_info *reg, asmop *aop, bool isSigned) { int regidx = reg->rIdx; int size = aop->size; switch (regidx) { case A_IDX: case X_IDX: case H_IDX: storeRegToAop (reg, aop, 0); if (size > 1 && isSigned && aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == A_IDX) pushReg (hc08_reg_a, TRUE); storeRegSignToUpperAop (reg, aop, 1, isSigned); if (size > 1 && isSigned && aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == A_IDX) pullReg (hc08_reg_a); break; case HX_IDX: if (size == 1) { storeRegToAop (hc08_reg_x, aop, 0); } else { storeRegToAop (reg, aop, 0); storeRegSignToUpperAop (hc08_reg_h, aop, 2, isSigned); } break; case XA_IDX: if (size == 1) { storeRegToAop (hc08_reg_a, aop, 0); } else { storeRegToAop (reg, aop, 0); storeRegSignToUpperAop (hc08_reg_x, aop, 2, isSigned); } break; default: wassert (0); } } /*--------------------------------------------------------------------------*/ /* transferAopAop - Transfer the value at logical offset srcofs of asmop */ /* srcaop to logical offset dstofs of asmop dstaop. */ /*--------------------------------------------------------------------------*/ static void transferAopAop (asmop *srcaop, int srcofs, asmop *dstaop, int dstofs) { bool needpula = FALSE; reg_info *reg = NULL; bool keepreg = FALSE; bool afree; wassert (srcaop && dstaop); /* ignore transfers at the same byte, unless its volatile */ if (srcaop->op && !isOperandVolatile (srcaop->op, FALSE) && dstaop->op && !isOperandVolatile (dstaop->op, FALSE) && operandsEqu (srcaop->op, dstaop->op) && srcofs == dstofs && dstaop->type == srcaop->type) return; if (srcaop->stacked && srcaop->stk_aop[srcofs]) { transferAopAop (srcaop->stk_aop[srcofs], 0, dstaop, dstofs); return; } if (dstaop->stacked && dstaop->stk_aop[srcofs]) { transferAopAop (srcaop, srcofs, dstaop->stk_aop[dstofs], 0); return; } // DD(emitcode ("", "; transferAopAop (%s, %d, %s, %d)", // aopName (srcaop), srcofs, aopName (dstaop), dstofs)); // DD(emitcode ("", "; srcaop->type = %d", srcaop->type)); // DD(emitcode ("", "; dstaop->type = %d", dstaop->type)); if (dstofs >= dstaop->size) return; if (srcaop->type == AOP_LIT) { storeConstToAop (byteOfVal (srcaop->aopu.aop_lit, srcofs), dstaop, dstofs); return; } if ((dstaop->type == AOP_DIR) && (srcaop->type == AOP_DIR)) { const char *src = aopAdrStr (srcaop, srcofs, FALSE); /* mov src,dst : 3 bytes, 5 cycles */ emitcode ("mov", "%s,%s", src, aopAdrStr (dstaop, dstofs, FALSE)); regalloc_dry_run_cost += 3; return; } if (dstaop->type == AOP_REG) { reg = dstaop->aopu.aop_reg[dstofs]; keepreg = TRUE; } else if ((srcaop->type == AOP_REG) && (srcaop->aopu.aop_reg[srcofs])) { reg = srcaop->aopu.aop_reg[srcofs]; keepreg = TRUE; } afree = hc08_reg_a->isFree; if (!reg) { if (hc08_reg_a->isFree) reg = hc08_reg_a; else if (hc08_reg_x->isFree) reg = hc08_reg_x; else { pushReg (hc08_reg_a, TRUE); needpula = TRUE; reg = hc08_reg_a; } } loadRegFromAop (reg, srcaop, srcofs); storeRegToAop (reg, dstaop, dstofs); if (!keepreg) pullOrFreeReg (hc08_reg_a, needpula); hc08_reg_a->isFree = afree; } /*--------------------------------------------------------------------------*/ /* accopWithMisc - Emit accumulator modifying instruction accop with the */ /* parameter param. */ /*--------------------------------------------------------------------------*/ static void accopWithMisc (char *accop, char *param) { emitcode (accop, "%s", param); regalloc_dry_run_cost += ((!param[0] || !strcmp(param, ",x")) ? 1 : ((param[0]=='#' || param[0]=='*') ? 2 : 3)); if (strcmp (accop, "bit") && strcmp (accop, "cmp") && strcmp (accop, "cpx")) hc08_dirtyReg (hc08_reg_a, FALSE); } /*--------------------------------------------------------------------------*/ /* accopWithAop - Emit accumulator modifying instruction accop with the */ /* byte at logical offset loffset of asmop aop. */ /* Supports: adc, add, and, bit, cmp, eor, ora, sbc, sub */ /*--------------------------------------------------------------------------*/ static void accopWithAop (char *accop, asmop *aop, int loffset) { if (aop->stacked && aop->stk_aop[loffset]) { accopWithAop (accop, aop->stk_aop[loffset], 0); return; } if (aop->type == AOP_DUMMY) return; if (loffset >= aop->size) { emitcode (accop, "#0"); regalloc_dry_run_cost += 2; } else if (aop->type == AOP_REG) { pushReg (aop->aopu.aop_reg[loffset], FALSE); emitcode (accop, "1,s"); regalloc_dry_run_cost += 3; pullNull (1); } else { emitcode (accop, "%s", aopAdrStr (aop, loffset, FALSE)); if (aop->type == AOP_DIR || aop->type == AOP_LIT) regalloc_dry_run_cost +=2; else regalloc_dry_run_cost += 3; } if (strcmp (accop, "bit") && strcmp (accop, "cmp") && strcmp (accop, "cpx")) hc08_dirtyReg (hc08_reg_a, FALSE); } /*--------------------------------------------------------------------------*/ /* rmwWithReg - Emit read/modify/write instruction rmwop with register reg. */ /* byte at logical offset loffset of asmop aop. Register reg */ /* must be 8-bit. */ /* Supports: com, dec, inc, lsl, lsr, neg, rol, ror */ /*--------------------------------------------------------------------------*/ static void rmwWithReg (char *rmwop, reg_info * reg) { char rmwbuf[10]; char *rmwaop = rmwbuf; if (reg->rIdx == A_IDX) { sprintf (rmwaop, "%sa", rmwop); emitcode (rmwaop, ""); regalloc_dry_run_cost++; hc08_dirtyReg (hc08_reg_a, FALSE); } else if (reg->rIdx == X_IDX) { sprintf (rmwaop, "%sx", rmwop); emitcode (rmwaop, ""); regalloc_dry_run_cost++; hc08_dirtyReg (hc08_reg_x, FALSE); } else { pushReg (reg, FALSE); emitcode (rmwop, "1,s"); regalloc_dry_run_cost += 3; pullReg (reg); hc08_dirtyReg (reg, FALSE); } } /*--------------------------------------------------------------------------*/ /* rmwWithAop - Emit read/modify/write instruction rmwop with the byte at */ /* logical offset loffset of asmop aop. */ /* Supports: com, dec, inc, lsl, lsr, neg, rol, ror, tst */ /*--------------------------------------------------------------------------*/ static void rmwWithAop (char *rmwop, asmop * aop, int loffset) { bool needpull = FALSE; reg_info * reg; if (aop->stacked && aop->stk_aop[loffset]) { rmwWithAop (rmwop, aop->stk_aop[loffset], 0); return; } /* If we need a register: */ /* use A if it's free, */ /* otherwise use X if it's free */ /* otherwise use A (and preserve original value via the stack) */ if (!hc08_reg_a->isFree && hc08_reg_x->isFree) reg = hc08_reg_x; else reg = hc08_reg_a; switch (aop->type) { case AOP_REG: rmwWithReg (rmwop, aop->aopu.aop_reg[loffset]); break; case AOP_EXT: needpull = pushRegIfUsed (reg); loadRegFromAop (reg, aop, loffset); rmwWithReg (rmwop, reg); if (strcmp ("tst", rmwop)) storeRegToAop (reg, aop, loffset); pullOrFreeReg (reg, needpull); break; case AOP_DUMMY: break; case AOP_SOF: { int offset = aop->size - 1 - loffset; offset += _G.stackOfs + _G.stackPushes + aop->aopu.aop_stk + 1; if ((offset > 0xff) || (offset < 0)) { /* Unfortunately, the rmw class of instructions only support a */ /* single byte stack pointer offset and we need two. */ needpull = pushRegIfUsed (reg); loadRegFromAop (reg, aop, loffset); rmwWithReg (rmwop, reg); if (strcmp ("tst", rmwop)) storeRegToAop (reg, aop, loffset); pullOrFreeReg (reg, needpull); break; } /* If the offset is small enough, fall through to default case */ } default: emitcode (rmwop, "%s", aopAdrStr (aop, loffset, FALSE)); regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD) ? 2 : 3); } } /*--------------------------------------------------------------------------*/ /* loadRegIndexed - Load a register using indexed addressing mode. */ /* NOTE: offset is physical (not logical) */ /*--------------------------------------------------------------------------*/ static void loadRegIndexed (reg_info * reg, int offset, char * rematOfs) { bool needpula = FALSE; /* The rematerialized offset may have a "#" prefix; skip over it */ if (rematOfs && rematOfs[0] == '#') rematOfs++; if (rematOfs && !rematOfs[0]) rematOfs = NULL; /* force offset to signed 16-bit range */ offset &= 0xffff; if (offset & 0x8000) offset = 0x10000 - offset; switch (reg->rIdx) { case A_IDX: if (rematOfs) { if (!offset) emitcode ("lda", "(%s),x", rematOfs); else emitcode ("lda", "(%s+%d),x", rematOfs, offset); regalloc_dry_run_cost += 3; } else if (offset) { emitcode ("lda", "%d,x", offset); if (offset > 1 && offset <= 0xff) regalloc_dry_run_cost += 2; else regalloc_dry_run_cost += 3; } else { emitcode ("lda", ",x"); regalloc_dry_run_cost++; } hc08_dirtyReg (reg, FALSE); break; case X_IDX: if (rematOfs) { if (!offset) emitcode ("ldx", "(%s),x", rematOfs); else emitcode ("ldx", "(%s+%d),x", rematOfs, offset); regalloc_dry_run_cost += 3; } else if (offset) { emitcode ("ldx", "%d,x", offset); if (offset > 1 && offset <= 0xff) regalloc_dry_run_cost += 2; else regalloc_dry_run_cost += 3; } else { emitcode ("ldx", ",x"); regalloc_dry_run_cost++; } hc08_dirtyReg (reg, FALSE); break; case H_IDX: needpula = pushRegIfUsed (hc08_reg_a); loadRegIndexed (hc08_reg_a, offset, rematOfs); transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); pullOrFreeReg (hc08_reg_a, needpula); break; case HX_IDX: if (!IS_S08) { needpula = pushRegIfUsed (hc08_reg_a); loadRegIndexed (hc08_reg_a, offset, rematOfs); pushReg (hc08_reg_a, TRUE); loadRegIndexed (hc08_reg_x, offset+1, rematOfs); pullReg (hc08_reg_h); pullOrFreeReg (hc08_reg_a, needpula); break; } else if (rematOfs) { if (!offset) emitcode ("ldhx", "(%s),x", rematOfs); else emitcode ("ldhx", "(%s+%d),x", rematOfs, offset); regalloc_dry_run_cost += 4; } else if (offset) { emitcode ("ldhx", "%d,x", offset); if (offset > 1 && offset <= 0xff) regalloc_dry_run_cost += 3; else regalloc_dry_run_cost += 4; } else { emitcode ("ldhx", ",x"); regalloc_dry_run_cost += 2; } hc08_dirtyReg (reg, FALSE); break; case XA_IDX: loadRegIndexed (hc08_reg_a, offset+1, rematOfs); loadRegIndexed (hc08_reg_x, offset, rematOfs); break; default: wassert (0); } } /*--------------------------------------------------------------------------*/ /* storeRegIndexed - Store a register using indexed addressing mode. */ /* NOTE: offset is physical (not logical) */ /*--------------------------------------------------------------------------*/ static void storeRegIndexed (reg_info * reg, int offset, char * rematOfs) { bool needpula = FALSE; /* The rematerialized offset may have a "#" prefix; skip over it */ if (rematOfs && rematOfs[0] == '#') rematOfs++; if (rematOfs && !rematOfs[0]) rematOfs = NULL; /* force offset to signed 16-bit range */ offset &= 0xffff; if (offset & 0x8000) offset = offset - 0x10000; switch (reg->rIdx) { case A_IDX: if (rematOfs) { if (!offset) emitcode ("sta", "(%s),x", rematOfs); else emitcode ("sta", "(%s+%d),x", rematOfs, offset); regalloc_dry_run_cost += 3; } else if (offset) { emitcode ("sta", "%d,x", offset); if (offset > 1 && offset <= 0xff) regalloc_dry_run_cost += 2; else regalloc_dry_run_cost += 3; } else { emitcode ("sta", ",x"); regalloc_dry_run_cost++; } break; case X_IDX: if (rematOfs) { if (!offset) emitcode ("stx", "(%s),x", rematOfs); else emitcode ("stx", "(%s+%d),x", rematOfs, offset); regalloc_dry_run_cost += 3; } else if (offset) { emitcode ("stx", "%d,x", offset); if (offset > 1 && offset <= 0xff) regalloc_dry_run_cost += 2; else regalloc_dry_run_cost += 3; } else { emitcode ("stx", ",x"); regalloc_dry_run_cost++; } break; case H_IDX: needpula = pushRegIfUsed (hc08_reg_a); transferRegReg (hc08_reg_h, hc08_reg_a, TRUE); storeRegIndexed (hc08_reg_a, offset, rematOfs); pullOrFreeReg (hc08_reg_a, needpula); break; case HX_IDX: storeRegIndexed (hc08_reg_h, offset, rematOfs); storeRegIndexed (hc08_reg_x, offset+1, rematOfs); break; case XA_IDX: /* This case probably won't happen, but it's easy to implement */ storeRegIndexed (hc08_reg_x, offset, rematOfs); storeRegIndexed (hc08_reg_a, offset+1, rematOfs); break; default: wassert (0); } } /*-----------------------------------------------------------------*/ /* newAsmop - creates a new asmOp */ /*-----------------------------------------------------------------*/ static asmop * newAsmop (short type) { asmop *aop; aop = Safe_calloc (1, sizeof (asmop)); aop->type = type; aop->op = NULL; return aop; } /*-----------------------------------------------------------------*/ /* operandConflictsWithHX - true if operand in h and/or x register */ /*-----------------------------------------------------------------*/ static bool operandConflictsWithHX (operand *op) { symbol *sym; int i; if (IS_ITEMP (op)) { sym = OP_SYMBOL (op); if (!sym->isspilt) { for(i = 0; i < sym->nRegs; i++) if (sym->regs[i] == hc08_reg_h || sym->regs[i] == hc08_reg_x) return TRUE; } } return FALSE; } /*-----------------------------------------------------------------*/ /* operandOnStack - returns True if operand is on the stack */ /*-----------------------------------------------------------------*/ static bool operandOnStack(operand *op) { symbol *sym; if (!op || !IS_SYMOP (op)) return FALSE; sym = OP_SYMBOL (op); if (!sym->isspilt && sym->onStack) return TRUE; if (sym->isspilt) { sym = sym->usl.spillLoc; if (sym && sym->onStack) return TRUE; } return FALSE; } /*-----------------------------------------------------------------*/ /* tsxUseful - returns True if tsx could help at least two */ /* anticipated stack references */ /*-----------------------------------------------------------------*/ static bool tsxUseful(iCode *ic) { int uses = 0; if (ic->op == CALL) { if (IC_RESULT (ic) && operandSize (IC_RESULT (ic)) < 2 && operandOnStack (IC_RESULT (ic))) { uses++; ic = ic->next; } } while (ic && uses < 2) { if (ic->op == IFX) { if (operandOnStack (IC_COND (ic))) uses += operandSize(IC_COND (ic)); break; } else if (ic->op == JUMPTABLE) { if (operandOnStack (IC_JTCOND (ic))) uses++; break; } else if (ic->op == ADDRESS_OF) { if (operandOnStack (IC_RIGHT (ic))) break; } else if (ic->op == LABEL || ic->op == GOTO || ic->op == CALL || ic->op == PCALL) break; else if (POINTER_SET (ic) || POINTER_GET (ic)) break; else { if (operandConflictsWithHX (IC_RESULT (ic))) break; if (operandOnStack (IC_LEFT (ic))) uses += operandSize (IC_LEFT (ic)); if (operandOnStack (IC_RIGHT (ic))) uses += operandSize (IC_RIGHT (ic)); if (operandOnStack (IC_RESULT (ic))) uses += operandSize (IC_RESULT (ic)); } ic = ic->next; } return uses>=2; } /*-----------------------------------------------------------------*/ /* aopForSym - for a true symbol */ /*-----------------------------------------------------------------*/ static asmop * aopForSym (iCode * ic, symbol * sym, bool result) { asmop *aop; memmap *space; wassertl (ic != NULL, "Got a null iCode"); wassertl (sym != NULL, "Got a null symbol"); // printf("in aopForSym for symbol %s\n", sym->name); space = SPEC_OCLS (sym->etype); /* if already has one */ if (sym->aop) { return sym->aop; } /* special case for a function */ if (IS_FUNC (sym->type)) { sym->aop = aop = newAsmop (AOP_IMMD); aop->aopu.aop_immd.aop_immd1 = Safe_calloc (1, strlen (sym->rname) + 1); strcpy (aop->aopu.aop_immd.aop_immd1, sym->rname); aop->size = FARPTRSIZE; return aop; } /* if it is on the stack */ if (sym->onStack) { sym->aop = aop = newAsmop (AOP_SOF); aop->aopu.aop_dir = sym->rname; aop->size = getSize (sym->type); aop->aopu.aop_stk = sym->stack; if (!regalloc_dry_run && hc08_reg_hx->isFree && hc08_reg_hx->aop != &tsxaop) { if (!hc08_reg_h->isDead || !hc08_reg_x->isDead) return aop; if (ic->op == IFX && operandConflictsWithHX (IC_COND (ic))) return aop; else if (ic->op == JUMPTABLE && operandConflictsWithHX (IC_JTCOND (ic))) return aop; else { /* If this is a pointer gen/set, then hx is definitely in use */ if (POINTER_SET (ic) || POINTER_GET (ic)) return aop; if (ic->op == ADDRESS_OF) return aop; if (operandConflictsWithHX (IC_LEFT (ic))) return aop; if (operandConflictsWithHX (IC_RIGHT (ic))) return aop; } /* It's safe to use tsx here. tsx costs 1 byte and 2 cycles but */ /* can save us 1 byte and 1 cycle for each time we can use x */ /* instead of sp. For a single use, we break even on bytes, but */ /* lose a cycle. Make sure there are at least two uses. */ if (!tsxUseful (ic)) return aop; emitcode ("tsx", ""); hc08_dirtyReg (hc08_reg_hx, FALSE); hc08_reg_hx->aop = &tsxaop; _G.tsxStackPushes = _G.stackPushes; } return aop; } /* if it is in direct space */ if (IN_DIRSPACE (space)) { sym->aop = aop = newAsmop (AOP_DIR); aop->aopu.aop_dir = sym->rname; aop->size = getSize (sym->type); return aop; } /* default to far space */ sym->aop = aop = newAsmop (AOP_EXT); aop->aopu.aop_dir = sym->rname; aop->size = getSize (sym->type); return aop; } /*-----------------------------------------------------------------*/ /* aopForRemat - rematerializes an object */ /*-----------------------------------------------------------------*/ static asmop * aopForRemat (symbol * sym) { iCode *ic = sym->rematiCode; asmop *aop = NULL; int val = 0; if (!ic) { fprintf (stderr, "Symbol %s to be rematerialized, but has no rematiCode.\n", sym->name); wassert (0); } for (;;) { if (ic->op == '+') val += (int) operandLitValue (IC_RIGHT (ic)); else if (ic->op == '-') val -= (int) operandLitValue (IC_RIGHT (ic)); else if (IS_CAST_ICODE (ic)) { ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; continue; } else break; ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; } if (ic->op == ADDRESS_OF) { if (val) { SNPRINTF (buffer, sizeof (buffer), "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic))->rname, val >= 0 ? '+' : '-', abs (val) & 0xffff); } else { strncpyz (buffer, OP_SYMBOL (IC_LEFT (ic))->rname, sizeof (buffer)); } aop = newAsmop (AOP_IMMD); aop->aopu.aop_immd.aop_immd1 = Safe_strdup (buffer); /* set immd2 field if required */ } else if (ic->op == '=') { val += (int) operandLitValue (IC_RIGHT (ic)); val &= 0xffff; SNPRINTF (buffer, sizeof (buffer), "0x%04x", val); aop = newAsmop (AOP_LIT); aop->aopu.aop_lit = constVal (buffer); } else { werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "unexpected rematerialization"); } 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 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] && strcmp (sym1->rname, sym2->rname) == 0) 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; } /*-----------------------------------------------------------------*/ /* sameRegs - two asmops have the same registers */ /*-----------------------------------------------------------------*/ static bool sameRegs (asmop * aop1, asmop * aop2) { int i; if (aop1 == aop2) return TRUE; // if (aop1->size != aop2->size) // return FALSE; if (aop1->type == aop2->type) { switch (aop1->type) { case AOP_REG: for (i = 0; i < aop1->size; i++) if (aop1->aopu.aop_reg[i] != aop2->aopu.aop_reg[i]) return FALSE; return TRUE; case AOP_SOF: return (aop1->aopu.aop_stk == aop2->aopu.aop_stk); case AOP_DIR: if (regalloc_dry_run) return FALSE; case AOP_EXT: return (!strcmp (aop1->aopu.aop_dir, aop2->aopu.aop_dir)); } } return FALSE; } /*-----------------------------------------------------------------*/ /* aopOp - allocates an asmop for an operand : */ /*-----------------------------------------------------------------*/ static void aopOp (operand *op, iCode * ic, bool result) { asmop *aop = NULL; symbol *sym; int i; if (!op) return; // Is this a pointer set result? // if ((op == IC_RESULT (ic)) && POINTER_SET (ic)) { } // printf("checking literal\n"); /* 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)); aop->op = op; return; } // printf("checking pre-existing\n"); /* if already has a asmop then continue */ if (op->aop) { op->aop->op = op; return; } // printf("checking underlying sym\n"); /* if the underlying symbol has a aop */ if (IS_SYMOP (op) && OP_SYMBOL (op)->aop) { op->aop = aop = Safe_calloc (1, sizeof (*aop)); memcpy (aop, OP_SYMBOL (op)->aop, sizeof (*aop)); //op->aop = aop = OP_SYMBOL (op)->aop; aop->size = getSize (operandType (op)); //printf ("reusing underlying symbol %s\n",OP_SYMBOL (op)->name); //printf (" with size = %d\n", aop->size); aop->op = op; return; } // printf("checking true sym\n"); /* if this is a true symbol */ if (IS_TRUE_SYMOP (op)) { op->aop = aop = aopForSym (ic, OP_SYMBOL (op), result); aop->op = op; //printf ("new symbol %s\n", OP_SYMBOL (op)->name); //printf (" with size = %d\n", aop->size); 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 */ if (!IS_SYMOP (op)) piCode (ic, NULL); sym = OP_SYMBOL (op); // printf("checking conditional\n"); /* if the type is a conditional */ if (sym->regType == REG_CND) { sym->aop = op->aop = aop = newAsmop (AOP_CRY); aop->size = 0; aop->op = op; return; } // printf("checking spilt\n"); /* if it is spilt then two situations a) is rematerialize b) has a spill location */ if (sym->isspilt || sym->nRegs == 0) { // printf("checking remat\n"); /* rematerialize it NOW */ if (sym->remat) { sym->aop = op->aop = aop = aopForRemat (sym); aop->size = getSize (sym->type); aop->op = op; return; } wassertl (!sym->ruonly, "sym->ruonly not supported"); if (regalloc_dry_run) // Todo: Handle dummy iTemp correctly { if (options.stackAuto || (currFunc && IFFUNC_ISREENT (currFunc->type))) { sym->aop = op->aop = aop = newAsmop (AOP_SOF); aop->aopu.aop_stk = 8; /* bogus stack offset, high enough to prevent optimization */ } else sym->aop = op->aop = aop = newAsmop (AOP_DIR); aop->size = getSize (sym->type); aop->op = op; return; } /* else spill location */ if (sym->isspilt && sym->usl.spillLoc || regalloc_dry_run) { asmop *oldAsmOp = NULL; if (sym->usl.spillLoc->aop && sym->usl.spillLoc->aop->size != getSize (sym->type)) { /* force a new aop if sizes differ */ oldAsmOp = sym->usl.spillLoc->aop; sym->usl.spillLoc->aop = NULL; //printf ("forcing new aop\n"); } sym->aop = op->aop = aop = aopForSym (ic, sym->usl.spillLoc, result); if (sym->usl.spillLoc->aop->size != getSize (sym->type)) { /* Don't reuse the new aop, go with the last one */ sym->usl.spillLoc->aop = oldAsmOp; } aop->size = getSize (sym->type); aop->op = op; //printf ("spill symbol %s\n", OP_SYMBOL (op)->name); //printf (" with size = %d\n", aop->size); return; } /* else must be a dummy iTemp */ sym->aop = op->aop = aop = newAsmop (AOP_DUMMY); aop->size = getSize (sym->type); aop->op = op; return; } // printf("assuming register\n"); /* must be in a register */ wassert (sym->nRegs); sym->aop = op->aop = aop = newAsmop (AOP_REG); aop->size = sym->nRegs; for (i = 0; i < sym->nRegs; i++) { wassert (sym->regs[i] < regshc08 + 3); aop->aopu.aop_reg[i] = sym->regs[i]; aop->regmask |= sym->regs[i]->mask; } if ((sym->nRegs > 1) && (sym->regs[0]->mask > sym->regs[1]->mask)) aop->regmask |= HC08MASK_REV; aop->op = op; } /*-----------------------------------------------------------------*/ /* freeAsmop - free up the asmop given to an operand */ /*----------------------------------------------------------------*/ static void freeAsmop (operand * op, asmop * aaop, iCode * ic, bool pop) { asmop *aop; if (!op) aop = aaop; else aop = op->aop; if (!aop) return; if (aop->freed) goto dealloc; aop->freed = 1; if (aop->stacked) { int stackAdjust; int loffset; DD (emitcode ("", "; freeAsmop restoring stacked %s", aopName (aop))); aop->stacked = 0; stackAdjust = 0; for (loffset = 0; loffset < aop->size; loffset++) if (aop->stk_aop[loffset]) { transferAopAop (aop->stk_aop[loffset], 0, aop, loffset); stackAdjust++; } pullNull (stackAdjust); } 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; } } } /*-----------------------------------------------------------------*/ /* aopDerefAop - treating the aop parameter as a pointer, return */ /* an asmop for the object it references */ /*-----------------------------------------------------------------*/ asmop * aopDerefAop (asmop * aop, int offset) { int adr; asmop *newaop = NULL; sym_link *type, *etype; int p_type; struct dbuf_s dbuf; DD (emitcode ("", "; aopDerefAop(%s)", aopName (aop))); if (aop->op) { type = operandType (aop->op); etype = getSpec (type); /* if op 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)); } } else p_type = UPOINTER; switch (aop->type) { case AOP_IMMD: if (p_type == POINTER) newaop = newAsmop (AOP_DIR); else newaop = newAsmop (AOP_EXT); if (!offset) newaop->aopu.aop_dir = aop->aopu.aop_immd.aop_immd1; else { dbuf_init (&dbuf, 64); dbuf_printf (&dbuf, "(%s+%d)", aop->aopu.aop_immd.aop_immd1, offset); newaop->aopu.aop_dir = dbuf_detach_c_str (&dbuf); } break; case AOP_LIT: adr = (int) ulFromVal (aop->aopu.aop_lit); if (p_type == POINTER) adr &= 0xff; adr = (adr + offset) & 0xffff; dbuf_init (&dbuf, 64); if (adr < 0x100) { newaop = newAsmop (AOP_DIR); dbuf_printf (&dbuf, "0x%02x", adr); } else { newaop = newAsmop (AOP_EXT); dbuf_printf (&dbuf, "0x%04x", adr); } newaop->aopu.aop_dir = dbuf_detach_c_str (&dbuf); break; default: werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "unsupported asmop"); return NULL; } return newaop; } /*-----------------------------------------------------------------*/ /* aopOpExtToIdx - attempt to convert AOP_EXT to AOP_IDX */ /*-----------------------------------------------------------------*/ static void aopOpExtToIdx(asmop * result, asmop *left, asmop *right) { int accesses=0; int resultAccesses=0; int leftAccesses=0; int rightAccesses=0; asmop * winner; int winnerAccesses; if (!hc08_reg_x->isFree || !hc08_reg_h->isFree) return; /* Need to replace at least two extended mode accesses with indexed */ /* to break even with the extra cost of loading HX. Do a quick check */ /* to see if anything is using extended mode at all. */ if (result && result->type == AOP_EXT) accesses += result->size; if (left && left->type == AOP_EXT) accesses += left->size; if (right && right->type == AOP_EXT) accesses += right->size; if (accesses<2) return; /* If an operand is already using or going to H or X then we cannot */ /* use indexed addressing mode at the same time. */ if (result && (IS_AOP_WITH_H (result) || IS_AOP_WITH_X (result))) return; if (left && (IS_AOP_WITH_H (left) || IS_AOP_WITH_X (left))) return; if (right && (IS_AOP_WITH_H (right) || IS_AOP_WITH_X (right))) return; /* Decide which is the best asmop to make indexed. */ if (result && result->type == AOP_EXT) { resultAccesses = result->size; if (result->op && left && left->op && result->op->key == left->op->key) resultAccesses += result->size; if (result->op && right && right->op && result->op->key == right->op->key) resultAccesses += result->size; } if (left && left->type == AOP_EXT) { leftAccesses = left->size; if (left->op && right && right->op && left->op->key == right->op->key) leftAccesses += left->size; } if (right && right->type == AOP_EXT) { rightAccesses = right->size; } winner = result; winnerAccesses = resultAccesses; if (leftAccesses > winnerAccesses) { winnerAccesses = leftAccesses; winner = left; } if (rightAccesses > winnerAccesses) { winnerAccesses = rightAccesses; winner = right; } /* Make sure there were enough accesses of a single variable to be worthwhile. */ if (winnerAccesses < 2) return; if (winner->op && result && result->op && winner->op->key == result->op->key) result->type = AOP_IDX; if (winner->op && left && left->op && winner->op->key == left->op->key) left->type = AOP_IDX; if (winner->op && right && right->op && winner->op->key == right->op->key) right->type = AOP_IDX; loadRegFromImm (hc08_reg_hx, winner->aopu.aop_dir); } /*-----------------------------------------------------------------*/ /* aopAdrStr - for referencing the address of the aop */ /*-----------------------------------------------------------------*/ /* loffset seems to have a weird meaning here. It seems to be nonzero in some places where one would expect an offset to be zero */ static const char * aopAdrStr (asmop * aop, int loffset, bool bit16) { char *s = buffer; char *rs; int offset = aop->size - 1 - loffset - (bit16 ? 1 : 0); int xofs; /* offset is greater than size then zero */ if (loffset > (aop->size - 1) && aop->type != AOP_LIT) return zero; /* depending on type */ switch (aop->type) { case AOP_DUMMY: return zero; case AOP_IMMD: if (loffset) { if (loffset > 1) sprintf (s, "#(%s >> %d)", aop->aopu.aop_immd.aop_immd1, loffset * 8); else sprintf (s, "#>%s", aop->aopu.aop_immd.aop_immd1); } else sprintf (s, "#%s", aop->aopu.aop_immd.aop_immd1); rs = Safe_calloc (1, strlen (s) + 1); strcpy (rs, s); return rs; case AOP_DIR: if (regalloc_dry_run) return "*dry"; if (offset) sprintf (s, "*(%s + %d)", aop->aopu.aop_dir, offset); else sprintf (s, "*%s", aop->aopu.aop_dir); rs = Safe_calloc (1, strlen (s) + 1); strcpy (rs, s); return rs; case AOP_EXT: if (regalloc_dry_run) return "dry"; if (offset) sprintf (s, "(%s + %d)", aop->aopu.aop_dir, offset); else sprintf (s, "%s", aop->aopu.aop_dir); rs = Safe_calloc (1, strlen (s) + 1); strcpy (rs, s); return rs; case AOP_REG: return aop->aopu.aop_reg[loffset]->name; case AOP_LIT: if (bit16) return aopLiteralLong (aop->aopu.aop_lit, loffset, 2); else return aopLiteral (aop->aopu.aop_lit, loffset); case AOP_SOF: if (!regalloc_dry_run && hc08_reg_hx->aop == &tsxaop) { xofs = _G.stackOfs + _G.tsxStackPushes + aop->aopu.aop_stk + offset; if (xofs) sprintf (s, "%d,x", xofs); else sprintf (s, ",x"); } else sprintf (s, "%d,s", _G.stackOfs + _G.stackPushes + aop->aopu.aop_stk + offset + 1); rs = Safe_calloc (1, strlen (s) + 1); strcpy (rs, s); return rs; case AOP_IDX: xofs = offset; /* For now, assume hx points to the base address of operand */ if (xofs) { if (regalloc_dry_run) /* Don't worry about the exact offset during the dry run */ return "1,x"; sprintf (s, "%d,x", xofs); rs = Safe_calloc (1, strlen (s) + 1); strcpy (rs, s); return rs; } else return ",x"; } werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopAdrStr got unsupported aop->type"); exit (1); } /*-----------------------------------------------------------------*/ /* getDataSize - get the operand data size */ /*-----------------------------------------------------------------*/ static int getDataSize (operand *op) { int size; size = AOP_SIZE (op); return size; } /*-----------------------------------------------------------------*/ /* asmopToBool - Emit code to convert an asmop to a boolean. */ /* Result left in A (0=FALSE, 1=TRUE) if ResultInA, */ /* otherwise result left in Z flag (1=FALSE, 0=TRUE) */ /*-----------------------------------------------------------------*/ static void asmopToBool (asmop *aop, bool resultInA) { bool isFloat; symbol *tlbl, *tlbl1; int size = aop->size; bool needpula = FALSE; bool flagsonly = TRUE; int offset = size - 1; sym_link *type; wassert (aop); type = operandType (AOP_OP (aop)); isFloat = IS_FLOAT (type); if (resultInA) hc08_freeReg (hc08_reg_a); if (IS_BOOL (type)) { if (resultInA) loadRegFromAop (hc08_reg_a, aop, 0); else rmwWithAop ("tst", aop, 0); return; } if (resultInA && size == 1) { loadRegFromAop (hc08_reg_a, aop, 0); rmwWithReg ("neg", hc08_reg_a); loadRegFromConst (hc08_reg_a, 0); rmwWithReg ("rol", hc08_reg_a); return; } switch (aop->type) { case AOP_REG: if (IS_AOP_A (aop)) { emitcode ("tsta", ""); regalloc_dry_run_cost++; flagsonly = FALSE; } else if (IS_AOP_X (aop)) { emitcode ("tstx", ""); regalloc_dry_run_cost++; } else if (IS_AOP_H (aop)) { if (hc08_reg_a->isFree) { transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); emitcode ("tsta", ""); regalloc_dry_run_cost++; flagsonly = FALSE; hc08_freeReg (hc08_reg_a); } else if (hc08_reg_x->isFree) { transferRegReg (hc08_reg_h, hc08_reg_x, FALSE); emitcode ("tstx", ""); regalloc_dry_run_cost++; hc08_freeReg (hc08_reg_x); } else { emitcode ("pshh", ""); emitcode ("tst", "1,s"); regalloc_dry_run_cost += 4; if (IS_S08 && optimize.codeSpeed) { emitcode ("ais", "#1"); // 2 Bytes, 2 cycles. regalloc_dry_run_cost += 2; } else { emitcode ("pulh", ""); // 1 Byte, 2 (hc08) / 3 (s08) cycles. regalloc_dry_run_cost += 1; } } } else if (IS_AOP_HX (aop)) { emitcode ("cphx", zero); regalloc_dry_run_cost += 3; } else if (IS_AOP_XA (aop) || IS_AOP_AX (aop)) { symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("tsta", ""); if (!regalloc_dry_run) emitcode ("bne", "%05d$", labelKey2num (tlbl->key)); emitcode ("tstx", ""); regalloc_dry_run_cost += 4; if (!regalloc_dry_run) emitLabel (tlbl); } else { werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "Bad rIdx in asmopToBool"); return; } break; case AOP_EXT: if (!resultInA && (size == 1) && !IS_AOP_A (aop) && !hc08_reg_a->isFree && hc08_reg_x->isFree) { loadRegFromAop (hc08_reg_x, aop, 0); break; } if (resultInA) needpula = FALSE; else needpula = pushRegIfUsed (hc08_reg_a); loadRegFromAop (hc08_reg_a, aop, offset--); if (isFloat) { emitcode ("and", "#0x7F"); //clear sign bit regalloc_dry_run_cost += 2; } while (--size) accopWithAop ("ora", aop, offset--); if (needpula) pullReg (hc08_reg_a); else { hc08_freeReg (hc08_reg_a); flagsonly = FALSE; } break; case AOP_LIT: /* Higher levels should optimize this case away but let's be safe */ if (ulFromVal (aop->aopu.aop_lit)) loadRegFromConst (hc08_reg_a, 1); else loadRegFromConst (hc08_reg_a, 0); hc08_freeReg (hc08_reg_a); break; default: if (size == 1) { if (resultInA) { loadRegFromAop (hc08_reg_a, aop, 0); hc08_freeReg (hc08_reg_a); flagsonly = FALSE; } else { emitcode ("tst", "%s", aopAdrStr (aop, 0, FALSE)); regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD) ? 2 : 3); } break; } else if (size == 2) { if (hc08_reg_a->isFree) { loadRegFromAop (hc08_reg_a, aop, 0); accopWithAop ("ora", aop, 1); hc08_freeReg (hc08_reg_a); flagsonly = FALSE; } else { tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("tst", "%s", aopAdrStr (aop, 0, FALSE)); if (!regalloc_dry_run) emitcode ("bne", "%05d$", labelKey2num (tlbl->key)); emitcode ("tst", "%s", aopAdrStr (aop, 1, FALSE)); regalloc_dry_run_cost += 4; if (!regalloc_dry_run) emitLabel (tlbl); break; } } else { needpula = pushRegIfUsed (hc08_reg_a); loadRegFromAop (hc08_reg_a, aop, offset--); if (isFloat) { emitcode ("and", "#0x7F"); regalloc_dry_run_cost += 2; } while (--size) accopWithAop ("ora", aop, offset--); if (needpula) pullReg (hc08_reg_a); else { hc08_freeReg (hc08_reg_a); flagsonly = FALSE; } } } if (resultInA) { tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); if (flagsonly) { tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitBranch ("bne", tlbl1); loadRegFromConst (hc08_reg_a, 0); emitBranch ("bra", tlbl); if (!regalloc_dry_run) emitLabel (tlbl1); hc08_dirtyReg (hc08_reg_a, FALSE); loadRegFromConst (hc08_reg_a, 1); } else { emitBranch ("beq", tlbl); loadRegFromConst (hc08_reg_a, 1); } if (!regalloc_dry_run) emitLabel (tlbl); hc08_dirtyReg (hc08_reg_a, FALSE); hc08_useReg (hc08_reg_a); } } /*-----------------------------------------------------------------*/ /* genCopy - Copy the value from one operand to another */ /* The caller is responsible for aopOp and freeAsmop */ /*-----------------------------------------------------------------*/ static void genCopy (operand * result, operand * source) { int size = AOP_SIZE (result); int srcsize = AOP_SIZE (source); int offset = 0; /* if they are the same and not volatile */ if (operandsEqu (result, source) && !isOperandVolatile (result, FALSE) && !isOperandVolatile (source, FALSE)) return; /* The source and destinations may be different size due to optimizations. */ /* This is not a cast, so there is no need to worry about sign extension. */ /* When this happens, it is usually just 1 byte source to 2 byte dest, so */ /* nothing significant to optimize. */ if (srcsize < size) { size -= srcsize; while (srcsize) { transferAopAop (AOP (source), offset, AOP (result), offset); offset++; srcsize--; } while (size) { storeConstToAop (0, AOP (result), offset); offset++; size--; } return; } /* if they are the same registers */ if (sameRegs (AOP (source), AOP (result))) return; if (IS_AOP_HX (AOP (result)) && srcsize == 2) { loadRegFromAop (hc08_reg_hx, AOP (source), 0); return; } if (IS_AOP_HX (AOP (source)) && size == 2) { storeRegToAop (hc08_reg_hx, AOP (result), 0); return; } /* If the result and right are 2 bytes and both in registers, we have to be careful */ /* to make sure the registers are not overwritten prematurely. */ if (AOP_SIZE (result) == 2 && AOP (result)->type == AOP_REG && AOP (source)->type == AOP_REG) { if (AOP (result)->aopu.aop_reg[0] == AOP (source)->aopu.aop_reg[1] && AOP (result)->aopu.aop_reg[1] == AOP (source)->aopu.aop_reg[0]) { pushReg (AOP (source)->aopu.aop_reg[1], TRUE); transferAopAop (AOP (source), 0, AOP (result), 0); pullReg (AOP (result)->aopu.aop_reg[1]); } else if (AOP (result)->aopu.aop_reg[0] == AOP (source)->aopu.aop_reg[1]) { transferAopAop (AOP (source), 1, AOP (result), 1); transferAopAop (AOP (source), 0, AOP (result), 0); } else { transferAopAop (AOP (source), 0, AOP (result), 0); transferAopAop (AOP (source), 1, AOP (result), 1); } return; } if (IS_HC08 && (size > 2)) aopOpExtToIdx (AOP (result), NULL, AOP (source)); /* general case */ /* Copy in msb to lsb order, since some multi-byte hardware registers */ /* expect this order. */ offset = size - 1; while (size) { if (size >= 2 && hc08_reg_h->isDead && hc08_reg_x->isDead && (AOP_TYPE (source) == AOP_IMMD || AOP_TYPE (source) == AOP_LIT ||IS_S08 && AOP_TYPE (source) == AOP_EXT) && (AOP_TYPE (result) == AOP_DIR || IS_S08 && AOP_TYPE (result) == AOP_EXT)) { loadRegFromAop (hc08_reg_hx, AOP (source), offset - 1); storeRegToAop (hc08_reg_hx, AOP (result), offset - 1); offset -= 2; size -= 2; } else { if (AOP_TYPE (source) == AOP_IDX && AOP_TYPE (result) == AOP_DIR) { emitcode ("mov", ",x+,%s", aopAdrStr (AOP (result), offset, FALSE)); regalloc_dry_run_cost += 2; } else if (AOP_TYPE (source) == AOP_DIR && AOP_TYPE (result) == AOP_IDX) { emitcode ("mov", "%s,x+", aopAdrStr (AOP (source), offset, FALSE)); regalloc_dry_run_cost += 2; } else transferAopAop (AOP (source), offset, AOP (result), offset); offset--; size--; } } } /*-----------------------------------------------------------------*/ /* genNot - generate code for ! operation */ /*-----------------------------------------------------------------*/ static void genNot (iCode * ic) { bool needpulla; D (emitcode ("; genNot", "")); /* assign asmOps to operand & result */ aopOp (IC_LEFT (ic), ic, FALSE); aopOp (IC_RESULT (ic), ic, TRUE); needpulla = pushRegIfSurv (hc08_reg_a); asmopToBool (AOP (IC_LEFT (ic)), TRUE); emitcode ("eor", one); regalloc_dry_run_cost += 2; storeRegToFullAop (hc08_reg_a, AOP (IC_RESULT (ic)), FALSE); pullOrFreeReg (hc08_reg_a, needpulla); freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genCpl - generate code for complement */ /*-----------------------------------------------------------------*/ static void genCpl (iCode * ic) { int offset = 0; int size; reg_info *reg; bool needpullreg; D (emitcode ("; genCpl", "")); /* assign asmOps to operand & result */ aopOp (IC_LEFT (ic), ic, FALSE); aopOp (IC_RESULT (ic), ic, TRUE); size = AOP_SIZE (IC_RESULT (ic)); if(AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP (IC_RESULT (ic))->aopu.aop_reg[0] == AOP (IC_LEFT (ic))->aopu.aop_reg[0] && (size < 2 || AOP (IC_RESULT (ic))->aopu.aop_reg[1] == AOP (IC_LEFT (ic))->aopu.aop_reg[1])) { while (size--) rmwWithReg ("com", AOP (IC_RESULT (ic))->aopu.aop_reg[offset++]); goto release; } if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP_TYPE (IC_LEFT (ic)) == AOP_REG) { while (size--) { if ((reg = AOP (IC_RESULT (ic))->aopu.aop_reg[offset]) != hc08_reg_h) { transferAopAop (AOP (IC_LEFT (ic)), offset, AOP (IC_RESULT (ic)), offset); rmwWithReg ("com", reg); } else { if ((reg = AOP (IC_LEFT (ic))->aopu.aop_reg[offset]) == hc08_reg_h) reg = hc08_reg_a->isDead ? hc08_reg_a : hc08_reg_x; needpullreg = pushRegIfSurv (reg); loadRegFromAop (reg, AOP (IC_LEFT (ic)), offset); rmwWithReg ("com", reg); storeRegToAop (reg, AOP (IC_RESULT (ic)), offset); if (needpullreg) pullReg (reg); } offset++; } goto release; } reg = (hc08_reg_a->isDead && !(AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP (IC_RESULT (ic))->aopu.aop_reg[0] == hc08_reg_a) ? hc08_reg_a : hc08_reg_x); needpullreg = pushRegIfSurv (reg); while (size--) { bool needpullreg2 = (!size && AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP (IC_RESULT (ic))->aopu.aop_reg[0] == reg || size && AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP (IC_RESULT (ic))->aopu.aop_reg[1] == reg); if (needpullreg2) pushReg (reg, TRUE); loadRegFromAop (reg, AOP (IC_LEFT (ic)), offset); rmwWithReg ("com", reg); hc08_useReg (reg); storeRegToAop (reg, AOP (IC_RESULT (ic)), offset); hc08_freeReg (reg); if (needpullreg2) pullReg (reg); offset++; } pullOrFreeReg (reg, needpullreg); /* release the aops */ release: freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genUminusFloat - unary minus for floating points */ /*-----------------------------------------------------------------*/ static void genUminusFloat (operand * op, operand * result) { int size, offset = 0; bool needpula; D (emitcode ("; genUminusFloat", "")); /* for this we just copy and then flip the bit */ size = AOP_SIZE (op) - 1; while (size--) { transferAopAop (AOP (op), offset, AOP (result), offset); offset++; } needpula = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (op), offset); emitcode ("eor", "#0x80"); regalloc_dry_run_cost += 2; hc08_useReg (hc08_reg_a); storeRegToAop (hc08_reg_a, AOP (result), offset); pullOrFreeReg (hc08_reg_a, needpula); } /*-----------------------------------------------------------------*/ /* genUminus - unary minus code generation */ /*-----------------------------------------------------------------*/ static void genUminus (iCode * ic) { int offset, size; sym_link *optype; char *sub; bool needpula; asmop *result; D (emitcode ("; genUminus", "")); /* assign asmops */ aopOp (IC_LEFT (ic), ic, FALSE); aopOp (IC_RESULT (ic), ic, TRUE); 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; if (size == 1) { needpula = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (ic)), 0); rmwWithReg ("neg", hc08_reg_a); hc08_freeReg (hc08_reg_a); storeRegToFullAop (hc08_reg_a, AOP (IC_RESULT (ic)), SPEC_USIGN (operandType (IC_LEFT (ic)))); pullOrFreeReg (hc08_reg_a, needpula); goto release; } /* If either left or result are in registers, handle this carefully to */ /* avoid prematurely overwriting register values. The 1 byte case was */ /* handled above and there aren't enough registers to handle 4 byte values */ /* so this case only needs to deal with 2 byte values. */ if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || AOP_TYPE (IC_LEFT (ic)) == AOP_REG) { reg_info *result0 = NULL; reg_info *left0 = NULL; reg_info *left1 = NULL; if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG) { left0 = AOP (IC_LEFT (ic))->aopu.aop_reg[0]; left1 = AOP (IC_LEFT (ic))->aopu.aop_reg[1]; } if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG) { result0 = AOP (IC_RESULT (ic))->aopu.aop_reg[0]; } needpula = pushRegIfSurv (hc08_reg_a); if (left1 == hc08_reg_a) pushReg (left1, TRUE); if (left0 == hc08_reg_a) rmwWithReg ("neg", hc08_reg_a); else { loadRegFromConst (hc08_reg_a, 0); accopWithAop ("sub", AOP (IC_LEFT (ic)), 0); } if (result0 == hc08_reg_a || (result0 && result0 == left1)) pushReg (hc08_reg_a, TRUE); else storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), 0); loadRegFromConst (hc08_reg_a, 0); if (left1 == hc08_reg_a) { emitcode ("sbc","%d,s", (result0 == hc08_reg_a || (result0 && result0 == left1)) ? 2 : 1); regalloc_dry_run_cost += 3; hc08_dirtyReg (hc08_reg_a, FALSE); } else { accopWithAop ("sbc", AOP (IC_LEFT (ic)), 1); } storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), 1); if (result0 == hc08_reg_a || (result0 && result0 == left1)) pullReg (result0); if (left1 == hc08_reg_a) pullNull (1); pullOrFreeReg (hc08_reg_a, needpula); goto release; } result = AOP (IC_RESULT (ic)); needpula = pushRegIfSurv (hc08_reg_a); sub = "sub"; while (size--) { loadRegFromConst (hc08_reg_a, 0); accopWithAop (sub, AOP (IC_LEFT (ic)), offset); storeRegToAop (hc08_reg_a, result, offset++); sub = "sbc"; } storeRegSignToUpperAop (hc08_reg_a, result, offset, SPEC_USIGN (operandType (IC_LEFT (ic)))); pullOrFreeReg (hc08_reg_a, needpula); release: /* release the aops */ freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); freeAsmop (IC_LEFT (ic), NULL, ic, FALSE); } /*-----------------------------------------------------------------*/ /* saveRegisters - will look for a call and save the registers */ /*-----------------------------------------------------------------*/ static void saveRegisters (iCode *lic) { int i; iCode *ic; /* 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)) && (IFFUNC_CALLEESAVES (OP_SYMBOL (IC_LEFT (ic))->type) || IFFUNC_ISNAKED (OP_SYM_TYPE (IC_LEFT (ic))))) return; if (!regalloc_dry_run) ic->regsSaved = 1; for (i = A_IDX; i <= H_IDX; i++) { if (bitVectBitValue (ic->rSurv, i)) pushReg (hc08_regWithIdx (i), FALSE); } } /*-----------------------------------------------------------------*/ /* unsaveRegisters - pop the pushed registers */ /*-----------------------------------------------------------------*/ static void unsaveRegisters (iCode *ic) { int i; for (i = H_IDX; i >= A_IDX; i--) { if (bitVectBitValue (ic->rSurv, i)) pullReg (hc08_regWithIdx (i)); } } /*-----------------------------------------------------------------*/ /* pushSide - */ /*-----------------------------------------------------------------*/ static void pushSide (operand *oper, int size, iCode *ic) { int offset = 0; bool xIsFree = hc08_reg_x->isFree; hc08_useReg (hc08_reg_x); aopOp (oper, ic, FALSE); if (AOP_TYPE (oper) == AOP_REG) { /* The operand is in registers; we can push them directly */ while (size--) { pushReg (AOP (oper)->aopu.aop_reg[offset++], TRUE); } } else if (hc08_reg_a->isFree) { /* A is free, so piecewise load operand into a and push A */ while (size--) { loadRegFromAop (hc08_reg_a, AOP (oper), offset++); pushReg (hc08_reg_a, TRUE); } } else { /* A is not free. Adjust stack, preserve A, copy operand */ /* into position on stack (using A), and restore original A */ adjustStack (-size); pushReg (hc08_reg_a, TRUE); while (size--) { loadRegFromAop (hc08_reg_a, AOP (oper), offset++); emitcode ("sta", "%d,s", 2+size); regalloc_dry_run_cost += 3; } pullReg (hc08_reg_a); } freeAsmop (oper, NULL, ic, TRUE); if (xIsFree) hc08_freeReg (hc08_reg_x); } /*-----------------------------------------------------------------*/ /* assignResultValue - */ /*-----------------------------------------------------------------*/ static void assignResultValue (operand * oper) { int size = AOP_SIZE (oper); int offset = 0; bool delayed_x = FALSE; while (size--) { if (!offset && AOP_TYPE (oper) == AOP_REG && AOP_SIZE (oper) > 1 && AOP (oper)->aopu.aop_reg[0]->rIdx == X_IDX) { pushReg (hc08_reg_a, TRUE); delayed_x = TRUE; } else transferAopAop (hc08_aop_pass[offset], 0, AOP (oper), offset); if (hc08_aop_pass[offset]->type == AOP_REG) hc08_freeReg (hc08_aop_pass[offset]->aopu.aop_reg[0]); offset++; } if (delayed_x) pullReg (hc08_reg_x); } /*-----------------------------------------------------------------*/ /* genIpush - generate code for pushing this gets a little complex */ /*-----------------------------------------------------------------*/ static void genIpush (iCode * ic) { int size, offset = 0; 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--) { loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (ic)), offset++); pushReg (hc08_reg_a, 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 */ if (!regalloc_dry_run) /* Cost for saving registers is counted at CALL or PCALL */ saveRegisters (ic); /* 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)); // l = aopGet (AOP (IC_LEFT (ic)), 0, FALSE, TRUE); if (AOP_TYPE (IC_LEFT (ic)) == AOP_IMMD || AOP_TYPE (IC_LEFT (ic)) == AOP_LIT ||IS_AOP_HX (AOP (IC_LEFT (ic)))) { if ((size == 2) && hc08_reg_hx->isDead || IS_AOP_HX (AOP (IC_LEFT (ic)))) { loadRegFromAop (hc08_reg_hx, AOP (IC_LEFT (ic)), 0); pushReg (hc08_reg_hx, TRUE); goto release; } } if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG) { while (size--) pushReg (AOP (IC_LEFT (ic))->aopu.aop_reg[offset++], TRUE); goto release; } while (size--) { // printf("loading %d\n", offset); loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (ic)), offset++); // printf("pushing \n"); pushReg (hc08_reg_a, TRUE); } release: 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--) { pullReg (hc08_reg_a); storeRegToAop (hc08_reg_a, AOP (IC_LEFT (ic)), offset--); } freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genSend - gen code for SEND */ /*-----------------------------------------------------------------*/ static void genSend (set *sendSet) { iCode *send1; iCode *send2; D (emitcode (";", "genSend")); /* case 1: single parameter in A * case 2: single parameter in XA * case 3: first parameter in A, second parameter in X */ send1 = setFirstItem (sendSet); send2 = setNextItem (sendSet); if (!send2) { int size; /* case 1 or 2, this is fairly easy */ aopOp (IC_LEFT (send1), send1, FALSE); size = AOP_SIZE (IC_LEFT (send1)); wassert (size <= 2); if (size == 1) { loadRegFromAop (send1->argreg == 2 ? hc08_reg_x : hc08_reg_a, AOP (IC_LEFT (send1)), 0); } else if (AOP (IC_LEFT (send1))->type == AOP_REG) loadRegFromAop (hc08_reg_xa, AOP (IC_LEFT (send1)), 0); else if (isOperandVolatile (IC_LEFT (send1), FALSE)) { /* use msb to lsb order for volatile operands */ loadRegFromAop (hc08_reg_x, AOP (IC_LEFT (send1)), 1); loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (send1)), 0); } else { /* otherwise perfer to load x last (lsb to msb order) */ loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (send1)), 0); loadRegFromAop (hc08_reg_x, AOP (IC_LEFT (send1)), 1); } freeAsmop (IC_LEFT (send1), NULL, send1, TRUE); } else { /* case 3 */ /* make sure send1 is the first argument and swap with send2 if not */ if (send1->argreg > send2->argreg) { iCode * sic = send1; send1 = send2; send2 = sic; } aopOp (IC_LEFT (send1), send1, FALSE); aopOp (IC_LEFT (send2), send2, FALSE); if (IS_AOP_X (AOP (IC_LEFT (send1))) && IS_AOP_A (AOP (IC_LEFT (send2)))) { /* If the parameters' register assignment is exactly backwards */ /* from what is needed, then swap the registers. */ pushReg (hc08_reg_a, FALSE); transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); pullReg (hc08_reg_x); } else if (IS_AOP_A (AOP (IC_LEFT (send2)))) { loadRegFromAop (hc08_reg_x, AOP (IC_LEFT (send2)), 0); loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (send1)), 0); } else { loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (send1)), 0); loadRegFromAop (hc08_reg_x, AOP (IC_LEFT (send2)), 0); } freeAsmop (IC_LEFT (send2), NULL, send2, TRUE); freeAsmop (IC_LEFT (send1), NULL, send1, TRUE); } } /*-----------------------------------------------------------------*/ /* genCall - generates a call statement */ /*-----------------------------------------------------------------*/ static void genCall (iCode * ic) { sym_link *dtype; sym_link *etype; // bool restoreBank = FALSE; // bool swapBanks = FALSE; D (emitcode (";", "genCall")); /* if caller saves & we have not saved then */ if (!ic->regsSaved) saveRegisters (ic); dtype = operandType (IC_LEFT (ic)); etype = getSpec (dtype); /* if send set is not empty then assign */ if (_G.sendSet && !regalloc_dry_run) { if (IFFUNC_ISREENT (dtype)) /* need to reverse the send set */ { genSend (reverseSet (_G.sendSet)); } else { genSend (_G.sendSet); } _G.sendSet = NULL; } /* make the call */ if (IS_LITERAL (etype)) { emitcode ("jsr", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); regalloc_dry_run_cost += 3; } else { bool jump = (!ic->parmBytes && IFFUNC_ISNORETURN (OP_SYMBOL (IC_LEFT (ic))->type)); emitcode (jump ? "jmp" : "jsr", "%s", (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); regalloc_dry_run_cost += 3; } hc08_dirtyReg (hc08_reg_a, FALSE); hc08_dirtyReg (hc08_reg_hx, FALSE); /* if we need assign a result value */ if ((IS_ITEMP (IC_RESULT (ic)) && (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) { hc08_useReg (hc08_reg_a); if (operandSize (IC_RESULT (ic)) > 1) hc08_useReg (hc08_reg_x); _G.accInUse++; aopOp (IC_RESULT (ic), ic, FALSE); _G.accInUse--; assignResultValue (IC_RESULT (ic)); freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); } /* adjust the stack for parameters if required */ if (ic->parmBytes) { pullNull (ic->parmBytes); } /* if we had saved some registers then unsave them */ if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) unsaveRegisters (ic); } /*-----------------------------------------------------------------*/ /* genPcall - generates a call by pointer statement */ /*-----------------------------------------------------------------*/ static void genPcall (iCode * ic) { sym_link *dtype; sym_link *etype; symbol *rlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); iCode * sendic; 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); /* Go through the send set and mark any registers used by iTemps as */ /* in use so we don't clobber them while setting up the return address */ for (sendic = setFirstItem (_G.sendSet); sendic; sendic = setNextItem (_G.sendSet)) { updateiTempRegisterUse (IC_LEFT (sendic)); } if (!IS_LITERAL (etype)) { /* push the return address on to the stack */ emitBranch ("bsr", tlbl); emitBranch ("bra", rlbl); if (!regalloc_dry_run) emitLabel (tlbl); _G.stackPushes += 2; /* account for the bsr return address now on stack */ updateCFA (); /* now push the function address */ pushSide (IC_LEFT (ic), FARPTRSIZE, ic); } /* if send set is not empty then assign */ if (_G.sendSet && !regalloc_dry_run) { genSend (reverseSet (_G.sendSet)); _G.sendSet = NULL; } /* make the call */ if (!IS_LITERAL (etype)) { emitcode ("rts", ""); regalloc_dry_run_cost++; if (!regalloc_dry_run) emitLabel (rlbl); _G.stackPushes -= 4; /* account for rts here & in called function */ updateCFA (); } else { emitcode ("jsr", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); regalloc_dry_run_cost += 3; } hc08_dirtyReg (hc08_reg_a, FALSE); hc08_dirtyReg (hc08_reg_hx, FALSE); /* if we need assign a result value */ if ((IS_ITEMP (IC_RESULT (ic)) && (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) { hc08_useReg (hc08_reg_a); if (operandSize (IC_RESULT (ic)) > 1) hc08_useReg (hc08_reg_x); _G.accInUse++; aopOp (IC_RESULT (ic), ic, FALSE); _G.accInUse--; assignResultValue (IC_RESULT (ic)); freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); } /* adjust the stack for parameters if required */ if (ic->parmBytes) { pullNull (ic->parmBytes); } /* if we had saved some registers then unsave them */ if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) unsaveRegisters (ic); } /*-----------------------------------------------------------------*/ /* 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; } /*-----------------------------------------------------------------*/ /* 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); } /*-----------------------------------------------------------------*/ /* genFunction - generated code for function entry */ /*-----------------------------------------------------------------*/ static void genFunction (iCode * ic) { symbol *sym = OP_SYMBOL (IC_LEFT (ic)); sym_link *ftype; iCode *ric = (ic->next && ic->next->op == RECEIVE) ? ic->next : NULL; int stackAdjust = sym->stack; int accIsFree = sym->recvSize == 0; _G.nRegsSaved = 0; _G.stackPushes = 0; /* create the function header */ emitcode (";", "-----------------------------------------"); emitcode (";", " function %s", sym->name); emitcode (";", "-----------------------------------------"); emitcode (";", hc08_assignment_optimal ? "Register assignment is optimal." : "Register assignment might be sub-optimal."); emitcode (";", "Stack space usage: %d bytes.", sym->stack); emitcode ("", "%s:", sym->rname); genLine.lineCurr->isLabel = 1; ftype = operandType (IC_LEFT (ic)); _G.stackOfs = 0; _G.stackPushes = 0; if (options.debug && !regalloc_dry_run) debugFile->writeFrameAddress (NULL, hc08_reg_sp, 0); if (IFFUNC_ISNAKED (ftype)) { emitcode (";", "naked function: no prologue."); return; } /* if this is an interrupt service routine then save h */ if (IFFUNC_ISISR (sym->type)) { if (!inExcludeList ("h")) pushReg (hc08_reg_h, FALSE); } /* For some cases it is worthwhile to perform a RECEIVE iCode */ /* before setting up the stack frame completely. */ while (ric && ric->next && ric->next->op == RECEIVE) ric = ric->next; while (ric && IC_RESULT (ric)) { symbol *rsym = OP_SYMBOL (IC_RESULT (ric)); int rsymSize = rsym ? getSize (rsym->type) : 0; 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 rather than use an sp relative store. */ if (rsym && rsym->onStack && rsym->stack == -_G.stackPushes - rsymSize) { int ofs; genLine.lineElement.ic = ric; D (emitcode ("; genReceive", "")); for (ofs = 0; ofs < rsymSize; ofs++) { reg_info *reg = hc08_aop_pass[ofs + (ric->argreg - 1)]->aopu.aop_reg[0]; pushReg (reg, TRUE); if (reg->rIdx == A_IDX) accIsFree = 1; stackAdjust--; } genLine.lineElement.ic = ic; ric->generated = 1; } ric = (ric->prev && ric->prev->op == RECEIVE) ? ric->prev : NULL; } /* adjust the stack for the function */ if (stackAdjust) { adjustStack (-stackAdjust); } _G.stackOfs = sym->stack; _G.stackPushes = 0; /* if critical function then turn interrupts off */ if (IFFUNC_ISCRITICAL (ftype)) { if (!accIsFree) { /* Function was passed parameters, so make sure A is preserved */ pushReg (hc08_reg_a, FALSE); pushReg (hc08_reg_a, FALSE); emitcode ("tpa", ""); emitcode ("sta", "2,s"); emitcode ("sei", ""); regalloc_dry_run_cost += 5; pullReg (hc08_reg_a); } else { /* No passed parameters, so A can be freely modified */ emitcode ("tpa", ""); regalloc_dry_run_cost++; pushReg (hc08_reg_a, TRUE); emitcode ("sei", ""); regalloc_dry_run_cost++; } } } /*-----------------------------------------------------------------*/ /* genEndFunction - generates epilogue for functions */ /*-----------------------------------------------------------------*/ static void genEndFunction (iCode * ic) { symbol *sym = OP_SYMBOL (IC_LEFT (ic)); if (IFFUNC_ISNAKED (sym->type)) { emitcode (";", "naked function: no epilogue."); if (options.debug && currFunc && !regalloc_dry_run) debugFile->writeEndFunction (currFunc, ic, 0); return; } if (IFFUNC_ISCRITICAL (sym->type)) { if (!IS_VOID (sym->type->next)) { /* Function has return value, so make sure A is preserved */ pushReg (hc08_reg_a, FALSE); emitcode ("lda", "2,s"); emitcode ("tap", ""); regalloc_dry_run_cost += 4; pullReg (hc08_reg_a); pullNull (1); } else { /* Function returns void, so A can be freely modified */ pullReg (hc08_reg_a); emitcode ("tap", ""); regalloc_dry_run_cost++; } } if (IFFUNC_ISREENT (sym->type) || options.stackAuto) { } if (sym->stack) { _G.stackPushes += sym->stack; adjustStack (sym->stack); } if ((IFFUNC_ISREENT (sym->type) || options.stackAuto)) { } if (IFFUNC_ISISR (sym->type)) { if (!inExcludeList ("h")) pullReg (hc08_reg_h); /* if debug then send end of function */ if (options.debug && currFunc && !regalloc_dry_run) { debugFile->writeEndFunction (currFunc, ic, 1); } emitcode ("rti", ""); regalloc_dry_run_cost++; } else { if (IFFUNC_CALLEESAVES (sym->type)) { 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) || (hc08_ptrRegReq && (i == HX_IDX || i == HX_IDX))) emitcode ("pop", "%s", hc08_regWithIdx (i)->name); /* Todo: Cost. Can't find this instruction in manual! */ } } } /* if debug then send end of function */ if (options.debug && currFunc && !regalloc_dry_run) { debugFile->writeEndFunction (currFunc, ic, 1); } emitcode ("rts", ""); regalloc_dry_run_cost++; } } /*-----------------------------------------------------------------*/ /* genRet - generate code for return statement */ /*-----------------------------------------------------------------*/ static void genRet (iCode * ic) { int size, offset = 0; // int pushed = 0; bool delayed_x = 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 (AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) { /* If returning a literal, we can load the bytes of the return value */ /* in any order. By loading A and X first, any other bytes that match */ /* can use the shorter sta and stx instructions. */ offset = 0; while (size--) { transferAopAop (AOP (IC_LEFT (ic)), offset, hc08_aop_pass[offset], 0); offset++; } } else { /* Take care when swapping a and x */ if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && size > 1 && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == X_IDX) { delayed_x = TRUE; pushReg (hc08_reg_x, TRUE); } offset = size - 1; while (size--) { if (!(delayed_x && !offset)) transferAopAop (AOP (IC_LEFT (ic)), offset, hc08_aop_pass[offset], 0); offset--; } if (delayed_x) pullReg (hc08_reg_a); } 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 ("jmp", "%05d$", labelKey2num (returnLabel->key)); regalloc_dry_run_cost += 3; } } /*-----------------------------------------------------------------*/ /* genLabel - generates a label */ /*-----------------------------------------------------------------*/ static void genLabel (iCode * ic) { int i; reg_info *reg; /* For the high level labels we cannot depend on any */ /* register's contents. Amnesia time. */ for (i = A_IDX; i <= XA_IDX; i++) { reg = hc08_regWithIdx (i); if (reg) { reg->aop = NULL; reg->isLitConst = 0; } } /* special case never generate */ if (IC_LABEL (ic) == entryLabel) return; if (options.debug && !regalloc_dry_run) debugFile->writeLabel (IC_LABEL (ic), ic); emitLabel (IC_LABEL (ic)); } /*-----------------------------------------------------------------*/ /* genGoto - generates a jmp */ /*-----------------------------------------------------------------*/ static void genGoto (iCode * ic) { emitcode ("jmp", "%05d$", labelKey2num (IC_LABEL (ic)->key)); regalloc_dry_run_cost += 3; } /*-----------------------------------------------------------------*/ /* genPlusIncr :- does addition with increment if possible */ /*-----------------------------------------------------------------*/ static bool genPlusIncr (iCode * ic) { int icount; operand *left; operand *result; bool needpulx; bool needpulh; bool needpula; unsigned int size = getDataSize (IC_RESULT (ic)); unsigned int offset; symbol *tlbl = NULL; left = IC_LEFT (ic); result = IC_RESULT (ic); /* will try to generate an increment */ /* if the right side is not a literal we cannot */ if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) return FALSE; icount = (unsigned int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); DD (emitcode ("", "; IS_AOP_HX = %d", IS_AOP_HX (AOP (left)))); if ((IS_AOP_HX (AOP (left)) || IS_AOP_HX (AOP (result)) || ((AOP_TYPE (left) == AOP_DIR || IS_S08 && AOP_TYPE (left) == AOP_EXT) && (AOP_TYPE (result) == AOP_DIR || IS_S08 && AOP_TYPE (result) == AOP_EXT))) && (icount >= -128) && (icount <= 127) && (size == 2)) { needpulx = pushRegIfSurv (hc08_reg_x); needpulh = pushRegIfSurv (hc08_reg_h); loadRegFromAop (hc08_reg_hx, AOP (left), 0); emitcode ("aix", "#%d", icount); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_hx, FALSE); storeRegToAop (hc08_reg_hx, AOP (result), 0); pullOrFreeReg (hc08_reg_h, needpulh); pullOrFreeReg (hc08_reg_x, needpulx); return TRUE; } if (size == 1 && (IS_AOP_X (AOP (result)) && (!IS_AOP_A (AOP (left)) || hc08_reg_h->isDead) || IS_AOP_X (AOP (left)) && !IS_AOP_A (AOP (result)) && hc08_reg_x->isDead && hc08_reg_h->isDead)) { icount = (int)(signed char) icount; /* truncate to a signed single byte value */ needpulh = pushRegIfSurv (hc08_reg_h); loadRegFromAop (hc08_reg_x, AOP (left), 0); emitcode ("aix", "#%d", icount); regalloc_dry_run_cost += 2; storeRegToAop (hc08_reg_x, AOP (result), 0); pullOrFreeReg (hc08_reg_h, needpulh); return TRUE; } DD (emitcode ("", "; icount = %d, sameRegs=%d", icount, sameRegs (AOP (left), AOP (result)))); if ((icount > 255) || (icount < 0)) return FALSE; if (!sameRegs (AOP (left), AOP (result))) return FALSE; D (emitcode ("; genPlusIncr", "")); aopOpExtToIdx (AOP (result), AOP (left), NULL); if (size > 1) tlbl = regalloc_dry_run ? 0 : newiTempLabel (NULL); if (icount == 1) { needpula = FALSE; rmwWithAop ("inc", AOP (result), 0); if (1 < size) emitBranch ("bne", tlbl); } else { if (!IS_AOP_A (AOP (result)) && !IS_AOP_XA (AOP (result))) needpula = pushRegIfUsed (hc08_reg_a); else needpula = FALSE; loadRegFromAop (hc08_reg_a, AOP (result), 0); accopWithAop ("add", AOP (IC_RIGHT (ic)), 0); hc08_useReg (hc08_reg_a); storeRegToAop (hc08_reg_a, AOP (result), 0); hc08_freeReg (hc08_reg_a); if (1 < size) emitBranch ("bcc", tlbl); } for (offset = 1; offset < size; offset++) { rmwWithAop ("inc", AOP (result), offset); if ((offset + 1) < size) emitBranch ("bne", tlbl); } if (size > 1 && !regalloc_dry_run) emitLabel (tlbl); pullOrFreeReg (hc08_reg_a, needpula); return TRUE; } /*-----------------------------------------------------------------*/ /* genPlus - generates code for addition */ /*-----------------------------------------------------------------*/ static void genPlus (iCode * ic) { int size, offset = 0; char *add; asmop *leftOp, *rightOp; bool needpulla; bool earlystore = FALSE; bool delayedstore = FALSE; bool mayskip = TRUE; bool skip = FALSE; /* special cases :- */ D (emitcode ("; genPlus", "")); aopOp (IC_LEFT (ic), ic, FALSE); aopOp (IC_RIGHT (ic), ic, FALSE); aopOp (IC_RESULT (ic), ic, TRUE); /* we want registers on the left and literals on the right */ if ((AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) || (AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && !IS_AOP_WITH_A (AOP (IC_LEFT (ic))))) { operand *t = IC_RIGHT (ic); IC_RIGHT (ic) = IC_LEFT (ic); IC_LEFT (ic) = t; } /* if I can do an increment instead of add then GOOD for ME */ if (genPlusIncr (ic) == TRUE) goto release; DD (emitcode ("", "; left size = %d", getDataSize (IC_LEFT (ic)))); DD (emitcode ("", "; right size = %d", getDataSize (IC_RIGHT (ic)))); DD (emitcode ("", "; result size = %d", getDataSize (IC_RESULT (ic)))); aopOpExtToIdx (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic))); size = getDataSize (IC_RESULT (ic)); leftOp = AOP (IC_LEFT (ic)); rightOp = AOP (IC_RIGHT (ic)); add = "add"; offset = 0; needpulla = pushRegIfSurv (hc08_reg_a); if(size > 1 && IS_AOP_AX (AOP (IC_LEFT (ic)))) { earlystore = TRUE; pushReg (hc08_reg_a, TRUE); } while (size--) { if (earlystore && offset == 1) pullReg (hc08_reg_a); else loadRegFromAop (hc08_reg_a, leftOp, offset); if (!mayskip || AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT || (byteOfVal (AOP (IC_RIGHT (ic))->aopu.aop_lit, offset) != 0x00) ) { accopWithAop (add, rightOp, offset); mayskip = FALSE; skip = FALSE; } else skip = TRUE; if (size && AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP (IC_RESULT (ic))->aopu.aop_reg[offset]->rIdx == A_IDX) { pushReg (hc08_reg_a, TRUE); delayedstore = TRUE; } else storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), offset); offset++; hc08_freeReg (hc08_reg_a); if (!skip) add = "adc"; /* further adds must propagate carry */ } if (delayedstore) pullReg (hc08_reg_a); pullOrFreeReg (hc08_reg_a, needpulla); wassert (!earlystore || !delayedstore); release: freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); freeAsmop (IC_RIGHT (ic), NULL, ic, TRUE); freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genMinusDec :- does subtraction with decrement if possible */ /*-----------------------------------------------------------------*/ static bool genMinusDec (iCode * ic) { int icount; operand *left; operand *result; bool needpulx; bool needpulh; unsigned int size = getDataSize (IC_RESULT (ic)); // int offset; // symbol *tlbl; left = IC_LEFT (ic); result = IC_RESULT (ic); /* will try to generate an increment */ /* if the right side is not a literal we cannot */ if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) return FALSE; icount = (unsigned int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); if ((IS_AOP_HX (AOP (left)) || IS_AOP_HX (AOP (result)) || ((AOP_TYPE (left) == AOP_DIR || IS_S08 && AOP_TYPE (left) == AOP_EXT) && (AOP_TYPE (result) == AOP_DIR || IS_S08 && AOP_TYPE (result) == AOP_EXT))) && (icount >= -127) && (icount <= 128) && (size == 2)) { needpulx = pushRegIfSurv (hc08_reg_x); needpulh = pushRegIfSurv (hc08_reg_h); loadRegFromAop (hc08_reg_hx, AOP (left), 0); emitcode ("aix", "#%d", -(int) icount); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_hx, FALSE); storeRegToAop (hc08_reg_hx, AOP (result), 0); pullOrFreeReg (hc08_reg_h, needpulh); pullOrFreeReg (hc08_reg_x, needpulx); return TRUE; } if ((icount > 1) || (icount < 0)) return FALSE; if (!sameRegs (AOP (left), AOP (result))) return FALSE; if (size != 1) return FALSE; D (emitcode ("; genMinusDec", "")); aopOpExtToIdx (AOP (result), AOP (left), NULL); rmwWithAop ("dec", AOP (result), 0); return TRUE; } /*-----------------------------------------------------------------*/ /* addSign - complete with sign */ /*-----------------------------------------------------------------*/ static void addSign (operand * result, int offset, int sign) { int size = (getDataSize (result) - offset); if (size > 0) { if (sign) { emitcode ("rola", ""); emitcode ("clra", ""); emitcode ("sbc", zero); hc08_dirtyReg (hc08_reg_a, FALSE); regalloc_dry_run_cost += 4; while (size--) storeRegToAop (hc08_reg_a, AOP (result), offset++); } else while (size--) storeConstToAop (0, AOP (result), offset++); } } /*-----------------------------------------------------------------*/ /* genMinus - generates code for subtraction */ /*-----------------------------------------------------------------*/ static void genMinus (iCode * ic) { char *sub; int size, offset = 0; bool needpulla; bool earlystore = FALSE; bool delayedstore = FALSE; asmop *leftOp, *rightOp; D (emitcode ("; genMinus", "")); aopOp (IC_LEFT (ic), ic, FALSE); aopOp (IC_RIGHT (ic), ic, FALSE); aopOp (IC_RESULT (ic), ic, TRUE); /* special cases :- */ /* if I can do an decrement instead of subtract then GOOD for ME */ if (genMinusDec (ic) == TRUE) goto release; aopOpExtToIdx (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic))); size = getDataSize (IC_RESULT (ic)); leftOp = AOP (IC_LEFT (ic)); rightOp = AOP (IC_RIGHT (ic)); sub = "sub"; offset = 0; needpulla = pushRegIfSurv (hc08_reg_a); if (IS_AOP_A (rightOp)) { loadRegFromAop (hc08_reg_a, rightOp, offset); accopWithAop (sub, leftOp, offset); accopWithMisc ("nega", ""); storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), offset++); pullOrFreeReg (hc08_reg_a, needpulla); goto release; } if (size > 1 && (IS_AOP_AX (AOP (IC_LEFT (ic))) || IS_AOP_AX (AOP (IC_RIGHT (ic))))) { earlystore = TRUE; pushReg (hc08_reg_a, TRUE); } while (size--) { if (earlystore && (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[offset]->rIdx == A_IDX || AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[offset]->rIdx == A_IDX)) pullReg (hc08_reg_a); if (AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[offset]->rIdx == A_IDX) { pushReg (hc08_reg_a, TRUE); loadRegFromAop (hc08_reg_a, leftOp, offset); emitcode (sub, "1, s"); hc08_dirtyReg (hc08_reg_a, FALSE); regalloc_dry_run_cost += 3; pullNull (1); } else { loadRegFromAop (hc08_reg_a, leftOp, offset); accopWithAop (sub, rightOp, offset); } if (size && AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP (IC_RESULT (ic))->aopu.aop_reg[offset]->rIdx == A_IDX) { pushReg (hc08_reg_a, TRUE); delayedstore = TRUE; } else storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), offset); offset++; sub = "sbc"; } if(delayedstore) pullReg (hc08_reg_a); pullOrFreeReg (hc08_reg_a, needpulla); wassert (!earlystore || !delayedstore); release: freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (IC_RIGHT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genMultOneByte : 8*8=8/16 bit multiplication */ /*-----------------------------------------------------------------*/ static void genMultOneByte (operand * left, operand * right, operand * result) { /* sym_link *opetype = operandType (result); */ symbol *tlbl1, *tlbl2, *tlbl3, *tlbl4; int size = AOP_SIZE (result); bool negLiteral = FALSE; bool lUnsigned, rUnsigned; bool needpulla, needpullx; 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 (1); } /* (if two literals: the value is computed before) */ /* if one literal, literal on the right */ if (AOP_TYPE (left) == AOP_LIT) { operand *t = right; right = left; left = t; } /* if an operand is in A, make sure it is on the left */ if (IS_AOP_A (AOP (right))) { operand *t = right; right = left; left = t; } needpulla = pushRegIfSurv (hc08_reg_a); needpullx = pushRegIfSurv (hc08_reg_x); lUnsigned = SPEC_USIGN (getSpec (operandType (left))); rUnsigned = SPEC_USIGN (getSpec (operandType (right))); /* lUnsigned rUnsigned negLiteral negate case */ /* false false false odd 3 */ /* false false true even 3 */ /* false true false odd 3 */ /* false true true impossible */ /* true false false odd 3 */ /* true false true always 2 */ /* true true false never 1 */ /* true true true impossible */ /* case 1 */ if (size == 1 || (lUnsigned && rUnsigned)) { // just an unsigned 8*8=8/16 multiply //DD(emitcode (";","unsigned")); loadRegFromAop (hc08_reg_a, AOP (left), 0); loadRegFromAop (hc08_reg_x, AOP (right), 0); emitcode ("mul", ""); regalloc_dry_run_cost++; hc08_dirtyReg (hc08_reg_xa, FALSE); storeRegToFullAop (hc08_reg_xa, AOP (result), TRUE); hc08_freeReg (hc08_reg_xa); pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_a, needpulla); return; } // we have to do a signed multiply /* case 2 */ /* left unsigned, right signed literal -- literal determines sign handling */ if (AOP_TYPE (right) == AOP_LIT && lUnsigned && !rUnsigned) { signed char val = (signed char) ulFromVal (AOP (right)->aopu.aop_lit); loadRegFromAop (hc08_reg_a, AOP (left), 0); emitcode ("ldx", "#0x%02x", val < 0 ? -val : val); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_x, FALSE); emitcode ("mul", ""); regalloc_dry_run_cost++; hc08_dirtyReg (hc08_reg_xa, FALSE); if (val < 0) { rmwWithReg ("neg", hc08_reg_a); tlbl4 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitBranch ("bcc", tlbl4); rmwWithReg ("inc", hc08_reg_x); if (!regalloc_dry_run) emitLabel (tlbl4); rmwWithReg ("neg", hc08_reg_x); } storeRegToFullAop (hc08_reg_xa, AOP (result), TRUE); hc08_freeReg (hc08_reg_xa); pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_a, needpulla); return; } /* case 3 */ adjustStack (-1); emitcode ("clr", "1,s"); regalloc_dry_run_cost += 3; loadRegFromAop (hc08_reg_a, AOP (left), 0); if (!lUnsigned) { tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("tsta", ""); regalloc_dry_run_cost++; emitBranch ("bpl", tlbl1); emitcode ("inc", "1,s"); regalloc_dry_run_cost += 3; rmwWithReg ("neg", hc08_reg_a); regalloc_dry_run_cost++; if (!regalloc_dry_run) emitLabel (tlbl1); } if (AOP_TYPE (right) == AOP_LIT && !rUnsigned) { signed char val = (signed char) ulFromVal (AOP (right)->aopu.aop_lit); /* AND literal negative */ if (val < 0) { emitcode ("ldx", "#0x%02x", -val); regalloc_dry_run_cost += 2; negLiteral = TRUE; } else { emitcode ("ldx", "#0x%02x", val); regalloc_dry_run_cost += 2; } hc08_dirtyReg (hc08_reg_x, FALSE); hc08_useReg (hc08_reg_x); } else { loadRegFromAop (hc08_reg_x, AOP (right), 0); if (!rUnsigned) { tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("tstx", ""); regalloc_dry_run_cost++; emitBranch ("bpl", tlbl2); emitcode ("inc", "1,s"); regalloc_dry_run_cost += 3; rmwWithReg ("neg", hc08_reg_x); regalloc_dry_run_cost++; if (!regalloc_dry_run) emitLabel (tlbl2); } } emitcode ("mul", ""); regalloc_dry_run_cost++; hc08_dirtyReg (hc08_reg_xa, FALSE); tlbl3 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("dec", "1,s"); regalloc_dry_run_cost += 3; if (!lUnsigned && !rUnsigned && negLiteral) emitBranch ("beq", tlbl3); else emitBranch ("bne", tlbl3); rmwWithReg ("neg", hc08_reg_a); tlbl4 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitBranch ("bcc", tlbl4); rmwWithReg ("inc", hc08_reg_x); if (!regalloc_dry_run) emitLabel (tlbl4); rmwWithReg ("neg", hc08_reg_x); if (!regalloc_dry_run) emitLabel (tlbl3); adjustStack (1); storeRegToFullAop (hc08_reg_xa, AOP (result), TRUE); pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_a, needpulla); } /*-----------------------------------------------------------------*/ /* 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 amsops */ aopOp (left, ic, FALSE); aopOp (right, ic, FALSE); aopOp (result, ic, TRUE); /* special cases first */ /* if both are of size == 1 */ // if (getSize(operandType(left)) == 1 && // getSize(operandType(right)) == 1) if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) { 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)); fprintf (stderr, "left: %d right: %d\n", AOP_SIZE (left), AOP_SIZE (right)); assert (0); release: freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genDivOneByte : 8 bit division */ /*-----------------------------------------------------------------*/ static void genDivOneByte (operand * left, operand * right, operand * result) { symbol *tlbl1, *tlbl2, *tlbl3; int size; bool lUnsigned, rUnsigned; bool runtimeSign, compiletimeSign; bool needpulla, needpullh; bool needpullx = FALSE; bool preload_a = FALSE; lUnsigned = SPEC_USIGN (getSpec (operandType (left))); rUnsigned = SPEC_USIGN (getSpec (operandType (right))); D (emitcode ("; genDivOneByte", "")); needpulla = pushRegIfSurv (hc08_reg_a); needpullh = pushRegIfSurv (hc08_reg_h); needpullx = pushRegIfSurv (hc08_reg_x); /* If both left and right are in registers and backwards from what we need, */ /* the swap the operands and the registers. */ if (IS_AOP_A (AOP (right)) && IS_AOP_X (AOP (left))) { operand * t; t = left; left = right; right = t; pushReg (hc08_reg_a, FALSE); transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); pullReg (hc08_reg_x); } size = AOP_SIZE (result); /* signed or unsigned */ if (lUnsigned && rUnsigned) { /* unsigned is easy */ if (IS_AOP_A (AOP (right))) { loadRegFromAop (hc08_reg_x, AOP (right), 0); loadRegFromAop (hc08_reg_a, AOP (left), 0); } else { loadRegFromAop (hc08_reg_a, AOP (left), 0); loadRegFromAop (hc08_reg_x, AOP (right), 0); } loadRegFromConst (hc08_reg_h, 0); emitcode ("div", ""); regalloc_dry_run_cost++; hc08_dirtyReg (hc08_reg_a, FALSE); hc08_dirtyReg (hc08_reg_h, FALSE); storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_h, needpullh); pullOrFreeReg (hc08_reg_a, needpulla); 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 the runtime sign */ if (runtimeSign) { if (compiletimeSign) pushConst (1); /* set sign flag */ else pushConst (0); /* reset sign flag */ } if (IS_AOP_X (AOP (left))) { loadRegFromAop (hc08_reg_a, AOP (left), 0); preload_a = TRUE; } /* 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 ("ldx", "#0x%02x", -val); regalloc_dry_run_cost += 2; } else { emitcode ("ldx", "#0x%02x", (unsigned char) val); regalloc_dry_run_cost += 2; } hc08_dirtyReg (hc08_reg_x, FALSE); hc08_useReg (hc08_reg_x); } else /* ! literal */ { loadRegFromAop (hc08_reg_x, AOP (right), 0); if (!rUnsigned) { tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("tstx", ""); regalloc_dry_run_cost++; emitBranch ("bpl", tlbl1); emitcode ("inc", "1,s"); regalloc_dry_run_cost += 3; rmwWithReg ("neg", hc08_reg_x); if (!regalloc_dry_run) emitLabel (tlbl1); } } if (AOP_TYPE (left) == AOP_LIT) { signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); if (!lUnsigned && val < 0) { emitcode ("lda", "#0x%02x", -val); regalloc_dry_run_cost += 2; } else { emitcode ("lda", "#0x%02x", (unsigned char) val); regalloc_dry_run_cost += 2; } hc08_dirtyReg (hc08_reg_a, FALSE); hc08_useReg (hc08_reg_a); } else /* ! literal */ { if (!preload_a) loadRegFromAop (hc08_reg_a, AOP (left), 0); if (!lUnsigned) { tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("tsta", ""); regalloc_dry_run_cost++; emitBranch ("bpl", tlbl2); emitcode ("inc", "1,s"); regalloc_dry_run_cost += 3; rmwWithReg ("neg", hc08_reg_a); if (!regalloc_dry_run) emitLabel (tlbl2); } } loadRegFromConst (hc08_reg_h, 0); emitcode ("div", ""); regalloc_dry_run_cost++; hc08_dirtyReg (hc08_reg_x, FALSE); hc08_dirtyReg (hc08_reg_a, FALSE); hc08_dirtyReg (hc08_reg_h, FALSE); if (runtimeSign || compiletimeSign) { if (runtimeSign) { tlbl3 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); pullReg (hc08_reg_x); rmwWithReg ("lsr", hc08_reg_x); if (size > 1) loadRegFromConst (hc08_reg_x, 0); emitBranch ("bcc", tlbl3); rmwWithReg ("neg", hc08_reg_a); if (size > 1) rmwWithReg ("dec", hc08_reg_x); if (!regalloc_dry_run) emitLabel (tlbl3); /* signed result now in A or XA */ if (size == 1) storeRegToAop (hc08_reg_a, AOP (result), 0); else storeRegToAop (hc08_reg_xa, AOP (result), 0); } else /* must be compiletimeSign */ { hc08_freeReg (hc08_reg_x); /* in case we need a free reg for the 0xff */ rmwWithReg ("neg", hc08_reg_a); storeRegToAop (hc08_reg_a, AOP (result), 0); if (size > 1) storeConstToAop (0xff, AOP (result), 1); } } else { storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); } pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_h, needpullh); pullOrFreeReg (hc08_reg_a, needpulla); } /*-----------------------------------------------------------------*/ /* 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 amsops */ aopOp (left, ic, FALSE); aopOp (right, ic, FALSE); aopOp (result, ic, TRUE); /* special cases first */ /* if both are of size == 1 */ if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1 && AOP_SIZE (result) <= 2) { genDivOneByte (left, right, result); goto release; } /* should have been converted to function call */ assert (0); release: freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genModOneByte : 8 bit modulus */ /*-----------------------------------------------------------------*/ static void genModOneByte (operand * left, operand * right, operand * result) { symbol *tlbl1, *tlbl2, *tlbl3; int size; bool lUnsigned, rUnsigned; bool runtimeSign, compiletimeSign; bool needpulla, needpullh; bool needpullx = FALSE; bool preload_a = FALSE; lUnsigned = SPEC_USIGN (getSpec (operandType (left))); rUnsigned = SPEC_USIGN (getSpec (operandType (right))); D (emitcode ("; genModOneByte", "")); size = AOP_SIZE (result); needpulla = pushRegIfSurv (hc08_reg_a); needpullh = pushRegIfSurv (hc08_reg_h); needpullx = pushRegIfSurv (hc08_reg_x); /* If both left and right are in registers and backwards from what we need, */ /* the swap the operands and the registers. */ if (IS_AOP_A (AOP (right)) && IS_AOP_X (AOP (left))) { operand * t; t = left; left = right; right = t; pushReg (hc08_reg_a, FALSE); transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); pullReg (hc08_reg_x); } if (lUnsigned && rUnsigned) { /* unsigned is easy */ if (IS_AOP_A (AOP (right))) { loadRegFromAop (hc08_reg_x, AOP (right), 0); loadRegFromAop (hc08_reg_a, AOP (left), 0); } else { loadRegFromAop (hc08_reg_a, AOP (left), 0); loadRegFromAop (hc08_reg_x, AOP (right), 0); } loadRegFromConst (hc08_reg_h, 0); emitcode ("div", ""); regalloc_dry_run_cost++; hc08_freeReg (hc08_reg_x); hc08_dirtyReg (hc08_reg_a, TRUE); hc08_dirtyReg (hc08_reg_h, FALSE); storeRegToFullAop (hc08_reg_h, AOP (result), FALSE); pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_h, needpullh); pullOrFreeReg (hc08_reg_a, needpulla); return; } /* signed is a little bit more difficult */ if (IS_AOP_X (AOP (left))) { loadRegFromAop (hc08_reg_a, AOP (left), 0); preload_a = TRUE; } if (AOP_TYPE (right) == AOP_LIT) { signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); if (!rUnsigned && val < 0) { emitcode ("ldx", "#0x%02x", -val); regalloc_dry_run_cost += 2; } else { emitcode ("ldx", "#0x%02x", (unsigned char) val); regalloc_dry_run_cost += 2; } hc08_dirtyReg (hc08_reg_x, FALSE); hc08_useReg (hc08_reg_x); } else /* ! literal */ { loadRegFromAop (hc08_reg_x, AOP (right), 0); if (!rUnsigned) { tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("tstx", ""); regalloc_dry_run_cost++; emitBranch ("bpl", tlbl1); rmwWithReg ("neg", hc08_reg_x); if (!regalloc_dry_run) emitLabel (tlbl1); } } /* 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 ("lda", "#0x%02x", -val); regalloc_dry_run_cost += 2; } else { emitcode ("lda", "#0x%02x", (unsigned char) val); regalloc_dry_run_cost += 2; } hc08_dirtyReg (hc08_reg_a, FALSE); hc08_useReg (hc08_reg_a); } else /* ! literal */ { if (lUnsigned) { if (!preload_a) loadRegFromAop (hc08_reg_a, AOP (left), 0); } else { runtimeSign = TRUE; pushConst (0); if (!preload_a) loadRegFromAop (hc08_reg_a, AOP (left), 0); tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("tsta", ""); regalloc_dry_run_cost++; emitBranch ("bpl", tlbl2); emitcode ("inc", "1,s"); regalloc_dry_run_cost += 3; rmwWithReg ("neg", hc08_reg_a); if (!regalloc_dry_run) emitLabel (tlbl2); } } loadRegFromConst (hc08_reg_h, 0); emitcode ("div", ""); regalloc_dry_run_cost++; hc08_freeReg (hc08_reg_x); hc08_dirtyReg (hc08_reg_a, TRUE); hc08_dirtyReg (hc08_reg_h, FALSE); if (runtimeSign || compiletimeSign) { transferRegReg (hc08_reg_h, hc08_reg_a, TRUE); if (runtimeSign) { tlbl3 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); pullReg (hc08_reg_x); rmwWithReg ("lsr", hc08_reg_x); if (size > 1) loadRegFromConst (hc08_reg_x, 0); emitBranch ("bcc", tlbl3); rmwWithReg ("neg", hc08_reg_a); if (size > 1) rmwWithReg ("dec", hc08_reg_x); if (!regalloc_dry_run) emitLabel (tlbl3); /* signed result now in A or XA */ if (size == 1) storeRegToAop (hc08_reg_a, AOP (result), 0); else storeRegToAop (hc08_reg_xa, AOP (result), 0); } else /* must be compiletimeSign */ { hc08_freeReg (hc08_reg_x); /* in case we need a free reg for the 0xff */ rmwWithReg ("neg", hc08_reg_a); storeRegToAop (hc08_reg_a, AOP (result), 0); if (size > 1) storeConstToAop (0xff, AOP (result), 1); } } else { storeRegToFullAop (hc08_reg_h, AOP (result), FALSE); } pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_h, needpullh); pullOrFreeReg (hc08_reg_a, needpulla); } /*-----------------------------------------------------------------*/ /* 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 amsops */ aopOp (left, ic, FALSE); aopOp (right, ic, FALSE); aopOp (result, ic, TRUE); /* special cases first */ /* if both are of size == 1 */ if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1 && AOP_SIZE (result) <= 2) { genModOneByte (left, right, result); goto release; } /* should have been converted to function call */ assert (0); release: freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genIfxJump :- will create a jump depending on the ifx */ /*-----------------------------------------------------------------*/ static void genIfxJump (iCode * ic, char *jval) { symbol *jlbl; symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); char *inst; D (emitcode ("; genIfxJump", "")); /* if true label then we jump if condition supplied is true */ if (IC_TRUE (ic)) { jlbl = IC_TRUE (ic); if (!strcmp (jval, "a")) inst = "beq"; else if (!strcmp (jval, "c")) inst = "bcc"; else if (!strcmp (jval, "n")) inst = "bpl"; else inst = "bge"; } else { /* false label is present */ jlbl = IC_FALSE (ic); if (!strcmp (jval, "a")) inst = "bne"; else if (!strcmp (jval, "c")) inst = "bcs"; else if (!strcmp (jval, "n")) inst = "bmi"; else inst = "blt"; } emitBranch (inst, tlbl); emitBranch ("jmp", jlbl); if (!regalloc_dry_run) emitLabel (tlbl); /* mark the icode as generated */ ic->generated = 1; } /*-----------------------------------------------------------------*/ /* exchangedCmp : returns the opcode need if the two operands are */ /* exchanged in a comparison */ /*-----------------------------------------------------------------*/ static int exchangedCmp (int opcode) { switch (opcode) { case '<': return '>'; case '>': return '<'; case LE_OP: return GE_OP; case GE_OP: return LE_OP; case NE_OP: return NE_OP; case EQ_OP: return EQ_OP; default: werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison"); } return EQ_OP; /* shouldn't happen, but need to return something */ } /*------------------------------------------------------------------*/ /* negatedCmp : returns the equivalent opcode for when a comparison */ /* if not true */ /*------------------------------------------------------------------*/ static int negatedCmp (int opcode) { switch (opcode) { case '<': return GE_OP; case '>': return LE_OP; case LE_OP: return '>'; case GE_OP: return '<'; case NE_OP: return EQ_OP; case EQ_OP: return NE_OP; default: werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison"); } return EQ_OP; /* shouldn't happen, but need to return something */ } /*------------------------------------------------------------------*/ /* nameCmp : helper function for human readable debug output */ /*------------------------------------------------------------------*/ static char * nameCmp (int opcode) { switch (opcode) { case '<': return "<"; case '>': return ">"; case LE_OP: return "<="; case GE_OP: return ">="; case NE_OP: return "!="; case EQ_OP: return "=="; default: return "invalid"; } } /*------------------------------------------------------------------*/ /* branchInstCmp : returns the conditional branch instruction that */ /* will branch if the comparison is true */ /*------------------------------------------------------------------*/ static char * branchInstCmp (int opcode, int sign) { switch (opcode) { case '<': if (sign) return "blt"; else return "bcs"; /* same as blo */ case '>': if (sign) return "bgt"; else return "bhi"; case LE_OP: if (sign) return "ble"; else return "bls"; case GE_OP: if (sign) return "bge"; else return "bcc"; /* same as bhs */ case NE_OP: return "bne"; case EQ_OP: return "beq"; default: werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison"); } return "brn"; } /*------------------------------------------------------------------*/ /* genCmp :- greater or less than (and maybe with equal) comparison */ /*------------------------------------------------------------------*/ static void genCmp (iCode * ic, iCode * ifx) { operand *left, *right, *result; sym_link *letype, *retype; int sign, opcode; int size, offset = 0; unsigned long long lit = 0ull; char *sub; symbol *jlbl = NULL; bool needpulla = FALSE; opcode = ic->op; D (emitcode ("; genCmp", "(%s)", nameCmp (opcode))); result = IC_RESULT (ic); left = IC_LEFT (ic); right = IC_RIGHT (ic); sign = 0; if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) { letype = getSpec (operandType (left)); retype = getSpec (operandType (right)); sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype)); } /* assign the amsops */ aopOp (left, ic, FALSE); aopOp (right, ic, FALSE); aopOp (result, ic, TRUE); /* need register operand on left, prefer literal operand on right */ if ((AOP_TYPE (right) == AOP_REG) || AOP_TYPE (left) == AOP_LIT) { operand *temp = left; left = right; right = temp; opcode = exchangedCmp (opcode); } if (ifx) { if (IC_TRUE (ifx)) { jlbl = IC_TRUE (ifx); opcode = negatedCmp (opcode); } else { /* false label is present */ jlbl = IC_FALSE (ifx); } } size = max (AOP_SIZE (left), AOP_SIZE (right)); if (size == 1 && IS_AOP_X (AOP (left))) { accopWithAop ("cpx", AOP (right), offset); } else if (size == 1 && IS_AOP_A (AOP (left))) { accopWithAop ("cmp", AOP (right), offset); } else if ((size == 2) && ((AOP_TYPE (left) == AOP_DIR || IS_AOP_HX (AOP (left))) && (AOP_SIZE (left) == 2)) && ((AOP_TYPE (right) == AOP_LIT) || ((AOP_TYPE (right) == AOP_DIR || IS_S08 && AOP_TYPE (right) == AOP_EXT) && (AOP_SIZE (right) == 2))) && (hc08_reg_h->isDead && hc08_reg_x->isDead || IS_AOP_HX (AOP (left)))) { loadRegFromAop (hc08_reg_hx, AOP (left), 0); emitcode ("cphx", "%s", aopAdrStr (AOP (right), 0, TRUE)); regalloc_dry_run_cost += (AOP_TYPE (right) == AOP_DIR ? 2 : 3); hc08_freeReg (hc08_reg_hx); } else { offset = 0; if (size == 1) sub = "cmp"; else { sub = "sub"; /* These conditions depend on the Z flag bit, but Z is */ /* only valid for the last byte of the comparison, not */ /* the whole value. So exchange the operands to get a */ /* comparison that doesn't depend on Z. (This is safe */ /* to do here since ralloc won't assign multi-byte */ /* operands to registers for comparisons) */ if ((opcode == '>') || (opcode == LE_OP)) { operand *temp = left; left = right; right = temp; opcode = exchangedCmp (opcode); } if ((AOP_TYPE (right) == AOP_LIT) && !isOperandVolatile (left, FALSE)) { lit = ullFromVal (AOP (right)->aopu.aop_lit); while ((size > 1) && (((lit >> (8 * offset)) & 0xff) == 0)) { offset++; size--; } } } needpulla = pushRegIfSurv (hc08_reg_a); while (size--) { if (AOP_TYPE (right) == AOP_REG && AOP(right)->aopu.aop_reg[offset]->rIdx == A_IDX) { pushReg (hc08_reg_a, TRUE); loadRegFromAop (hc08_reg_a, AOP (left), offset); emitcode (sub, "1, s"); regalloc_dry_run_cost += 3; pullReg (hc08_reg_a); } else { loadRegFromAop (hc08_reg_a, AOP (left), offset); accopWithAop (sub, AOP (right), offset); } hc08_freeReg (hc08_reg_a); offset++; sub = "sbc"; } } freeAsmop (right, NULL, ic, FALSE); freeAsmop (left, NULL, ic, FALSE); if (ifx) { symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); char *inst; pullOrFreeReg (hc08_reg_a, needpulla); freeAsmop (result, NULL, ic, TRUE); inst = branchInstCmp (opcode, sign); emitBranch (inst, tlbl); emitBranch ("jmp", jlbl); if (!regalloc_dry_run) emitLabel (tlbl); /* mark the icode as generated */ ifx->generated = 1; } else { symbol *tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); symbol *tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); if (!needpulla) needpulla = pushRegIfSurv (hc08_reg_a); emitBranch (branchInstCmp (opcode, sign), tlbl1); loadRegFromConst (hc08_reg_a, 0); emitBranch ("bra", tlbl2); if (!regalloc_dry_run) emitLabel (tlbl1); hc08_dirtyReg (hc08_reg_a, FALSE); loadRegFromConst (hc08_reg_a, 1); if (!regalloc_dry_run) emitLabel (tlbl2); hc08_dirtyReg (hc08_reg_a, FALSE); storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); pullOrFreeReg (hc08_reg_a, needpulla); freeAsmop (result, NULL, ic, TRUE); } } /*-----------------------------------------------------------------*/ /* genCmpEQorNE - equal or not equal comparison */ /*-----------------------------------------------------------------*/ static void genCmpEQorNE (iCode * ic, iCode * ifx) { operand *left, *right, *result; int opcode; int size, offset = 0; symbol *jlbl = NULL; symbol *tlbl_NE = NULL; symbol *tlbl_EQ = NULL; bool needpulla = FALSE; opcode = ic->op; D (emitcode ("; genCmpEQorNE", "(%s)", nameCmp (opcode))); result = IC_RESULT (ic); left = IC_LEFT (ic); right = IC_RIGHT (ic); /* assign the amsops */ aopOp (left, ic, FALSE); aopOp (right, ic, FALSE); aopOp (result, ic, TRUE); /* need register operand on left, prefer literal operand on right */ if ((AOP_TYPE (right) == AOP_REG) || AOP_TYPE (left) == AOP_LIT) { operand *temp = left; left = right; right = temp; opcode = exchangedCmp (opcode); } if (ifx) { if (IC_TRUE (ifx)) { jlbl = IC_TRUE (ifx); opcode = negatedCmp (opcode); } else { /* false label is present */ jlbl = IC_FALSE (ifx); } } size = max (AOP_SIZE (left), AOP_SIZE (right)); if ((size == 2) && ((AOP_TYPE (left) == AOP_DIR || IS_AOP_HX (AOP (left))) && (AOP_SIZE (left) == 2)) && ((AOP_TYPE (right) == AOP_LIT) || ((AOP_TYPE (right) == AOP_DIR || IS_S08 && AOP_TYPE (right) == AOP_EXT) && (AOP_SIZE (right) == 2))) && hc08_reg_h->isDead && hc08_reg_x->isDead) { loadRegFromAop (hc08_reg_hx, AOP (left), 0); emitcode ("cphx", "%s", aopAdrStr (AOP (right), 0, TRUE)); regalloc_dry_run_cost += (AOP_TYPE (right) == AOP_DIR ? 2 : 3); hc08_freeReg (hc08_reg_hx); } else { offset = 0; while (size--) { if (AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx == X_IDX) accopWithAop ("cpx", AOP (right), offset); else { if (!(AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx == A_IDX)) { needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (left), offset); } accopWithAop ("cmp", AOP (right), offset); if (!(AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx == A_IDX)) pullOrFreeReg (hc08_reg_a, needpulla); needpulla = FALSE; } if (size) { if (!needpulla && !ifx) needpulla = pushRegIfSurv (hc08_reg_a); if (!tlbl_NE && !regalloc_dry_run) tlbl_NE = newiTempLabel (NULL); emitBranch ("bne", tlbl_NE); pullOrFreeReg (hc08_reg_a, needpulla); needpulla = FALSE; } offset++; } } freeAsmop (right, NULL, ic, FALSE); freeAsmop (left, NULL, ic, FALSE); if (ifx) { freeAsmop (result, NULL, ic, TRUE); if (opcode == EQ_OP) { if (!tlbl_EQ && !regalloc_dry_run) tlbl_EQ = newiTempLabel (NULL); emitBranch ("beq", tlbl_EQ); if (tlbl_NE) emitLabel (tlbl_NE); emitBranch ("jmp", jlbl); if (!regalloc_dry_run) emitLabel (tlbl_EQ); } else { if (!tlbl_NE && !regalloc_dry_run) tlbl_NE = newiTempLabel (NULL); emitBranch ("bne", tlbl_NE); emitBranch ("jmp", jlbl); if (!regalloc_dry_run) emitLabel (tlbl_NE); } /* mark the icode as generated */ ifx->generated = 1; } else { symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); if (!needpulla) needpulla = pushRegIfSurv (hc08_reg_a); if (opcode == EQ_OP) { if (!tlbl_EQ && !regalloc_dry_run) tlbl_EQ = newiTempLabel (NULL); emitBranch ("beq", tlbl_EQ); if (tlbl_NE) emitLabel (tlbl_NE); hc08_dirtyReg (hc08_reg_a, FALSE); loadRegFromConst (hc08_reg_a, 0); emitBranch ("bra", tlbl); if (!regalloc_dry_run) emitLabel (tlbl_EQ); hc08_dirtyReg (hc08_reg_a, FALSE); loadRegFromConst (hc08_reg_a, 1); } else { if (!tlbl_NE && !regalloc_dry_run) tlbl_NE = newiTempLabel (NULL); emitBranch ("bne", tlbl_NE); loadRegFromConst (hc08_reg_a, 0); emitBranch ("bra", tlbl); if (!regalloc_dry_run) emitLabel (tlbl_NE); hc08_dirtyReg (hc08_reg_a, FALSE); loadRegFromConst (hc08_reg_a, 1); } if (!regalloc_dry_run) emitLabel (tlbl); hc08_dirtyReg (hc08_reg_a, FALSE); storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); pullOrFreeReg (hc08_reg_a, needpulla); freeAsmop (result, NULL, ic, TRUE); } } /*-----------------------------------------------------------------*/ /* hasInchc08 - operand is incremented before any other use */ /*-----------------------------------------------------------------*/ iCode * hasInchc08 (operand *op, const 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 + */ 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, *tlbl0; bool needpulla; 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); tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); tlbl0 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); needpulla = pushRegIfSurv (hc08_reg_a); asmopToBool (AOP (left), FALSE); emitBranch ("beq", tlbl0); asmopToBool (AOP (right), FALSE); emitBranch ("beq", tlbl0); loadRegFromConst (hc08_reg_a, 1); emitBranch ("bra", tlbl); if (!regalloc_dry_run) emitLabel (tlbl0); hc08_dirtyReg (hc08_reg_a, FALSE); loadRegFromConst (hc08_reg_a, 0); if (!regalloc_dry_run) emitLabel (tlbl); hc08_dirtyReg (hc08_reg_a, FALSE); hc08_useReg (hc08_reg_a); hc08_freeReg (hc08_reg_a); storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); pullOrFreeReg(hc08_reg_a, needpulla); freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genOrOp - for || operation */ /*-----------------------------------------------------------------*/ static void genOrOp (iCode * ic) { operand *left, *right, *result; symbol *tlbl, *tlbl0; bool needpulla; 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); tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); tlbl0 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); needpulla = pushRegIfSurv (hc08_reg_a); asmopToBool (AOP (left), FALSE); emitBranch ("bne", tlbl0); asmopToBool (AOP (right), FALSE); emitBranch ("bne", tlbl0); loadRegFromConst (hc08_reg_a, 0); emitBranch ("bra", tlbl); if (!regalloc_dry_run) emitLabel (tlbl0); hc08_dirtyReg (hc08_reg_a, FALSE); loadRegFromConst (hc08_reg_a, 1); if (!regalloc_dry_run) emitLabel (tlbl); hc08_dirtyReg (hc08_reg_a, FALSE); hc08_useReg (hc08_reg_a); hc08_freeReg (hc08_reg_a); storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); pullOrFreeReg(hc08_reg_a, needpulla); freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* isLiteralBit - test if lit == 2^n */ /*-----------------------------------------------------------------*/ static int isLiteralBit (unsigned long lit) { unsigned long pw[32] = { 1L, 2L, 4L, 8L, 16L, 32L, 64L, 128L, 0x100L, 0x200L, 0x400L, 0x800L, 0x1000L, 0x2000L, 0x4000L, 0x8000L, 0x10000L, 0x20000L, 0x40000L, 0x80000L, 0x100000L, 0x200000L, 0x400000L, 0x800000L, 0x1000000L, 0x2000000L, 0x4000000L, 0x8000000L, 0x10000000L, 0x20000000L, 0x40000000L, 0x80000000L }; int idx; for (idx = 0; idx < 32; idx++) if (lit == pw[idx]) return idx + 1; return 0; } /*-----------------------------------------------------------------*/ /* genAnd - code for and */ /*-----------------------------------------------------------------*/ static void genAnd (iCode * ic, iCode * ifx) { operand *left, *right, *result; int size, offset = 0; unsigned long long lit = 0ll; unsigned long long litinv; int bitpos = -1; unsigned char bytemask; bool needpulla = FALSE; bool earlystore = FALSE; 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 DD (emitcode ("", "; Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right))); DD (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) { operand *tmp = right; right = left; left = tmp; } /* if right is accumulator & left is not then exchange them */ if (AOP_TYPE (right) == AOP_REG && ! IS_AOP_WITH_A (AOP (left))) { operand *tmp = right; right = left; left = tmp; } size = (AOP_SIZE (left) >= AOP_SIZE (right)) ? AOP_SIZE (left) : AOP_SIZE (right); if (AOP_TYPE (right) == AOP_LIT) { lit = ullFromVal (AOP (right)->aopu.aop_lit); if (size == 1) lit &= 0xff; else if (size == 2) lit &= 0xffff; else if (size == 4) lit &= 0xffffffff; else if (size == 8) lit &= 0xffffffffffffffff; bitpos = isLiteralBit (lit) - 1; } if (ifx && AOP_TYPE (result) == AOP_CRY && AOP_TYPE (right) == AOP_LIT && AOP_TYPE (left) == AOP_DIR && bitpos >= 0) { symbol *tlbl = NULL; if (!regalloc_dry_run) tlbl = newiTempLabel (NULL); if (IC_TRUE (ifx)) { if (!regalloc_dry_run) emitcode ("brclr", "#%d,%s,%05d$", bitpos & 7, aopAdrStr (AOP (left), bitpos >> 3, FALSE), labelKey2num ((tlbl->key))); regalloc_dry_run_cost += 3; emitBranch ("jmp", IC_TRUE (ifx)); if (!regalloc_dry_run) emitLabel (tlbl); if (IC_FALSE (ifx)) emitBranch ("jmp", IC_FALSE (ifx)); } else { if (!regalloc_dry_run) emitcode ("brset", "#%d,%s,%05d$", bitpos & 7, aopAdrStr (AOP (left), bitpos >> 3, FALSE), labelKey2num ((tlbl->key))); regalloc_dry_run_cost += 3; emitBranch ("jmp", IC_FALSE (ifx)); if (!regalloc_dry_run) emitLabel (tlbl); } ifx->generated = TRUE; goto release; } if (AOP_TYPE (result) == AOP_CRY && size > 1 && (isOperandVolatile (left, FALSE) || isOperandVolatile (right, FALSE))) { needpulla = pushRegIfSurv (hc08_reg_a); /* this generates ugly code, but meets volatility requirements */ loadRegFromConst (hc08_reg_a, 0); pushReg (hc08_reg_a, TRUE); offset = 0; while (size--) { loadRegFromAop (hc08_reg_a, AOP (left), offset); accopWithAop ("and", AOP (right), offset); emitcode ("ora", "1,s"); emitcode ("sta", "1,s"); regalloc_dry_run_cost += 6; offset++; } pullReg (hc08_reg_a); emitcode ("tsta", ""); regalloc_dry_run_cost++; pullOrFreeReg (hc08_reg_a, needpulla); genIfxJump (ifx, "a"); goto release; } if (AOP_TYPE (result) == AOP_CRY && AOP_TYPE (right) == AOP_LIT) { if (bitpos >= 0 && (bitpos & 7) == 7) { rmwWithAop ("tst", AOP (left), bitpos >> 3); genIfxJump (ifx, "n"); goto release; } } if (AOP_TYPE (result) == AOP_CRY && size == 1 && (IS_AOP_A (AOP (left)) || IS_AOP_A (AOP (right)))) { if (IS_AOP_A (AOP (left))) accopWithAop ("bit", AOP (right), 0); else accopWithAop ("bit", AOP (left), 0); genIfxJump (ifx, "a"); goto release; } if (AOP_TYPE (result) == AOP_CRY) { symbol *tlbl = NULL; needpulla = pushRegIfSurv (hc08_reg_a); offset = 0; while (size--) { bytemask = (lit >> (offset * 8)) & 0xff; if (AOP_TYPE (right) == AOP_LIT && bytemask == 0) { /* do nothing */ } else if (AOP_TYPE (right) == AOP_LIT && bytemask == 0xff) { rmwWithAop ("tst", AOP (left), offset); if (size) { if (!tlbl && !regalloc_dry_run) tlbl = newiTempLabel (NULL); emitBranch ("bne", tlbl); } } else { loadRegFromAop (hc08_reg_a, AOP (left), offset); accopWithAop ("bit", AOP (right), offset); hc08_freeReg (hc08_reg_a); if (size) { if (!tlbl && !regalloc_dry_run) tlbl = newiTempLabel (NULL); emitBranch ("bne", tlbl); } } offset++; } if (tlbl) emitLabel (tlbl); pullOrFreeReg (hc08_reg_a, needpulla); if (ifx) genIfxJump (ifx, "a"); goto release; } size = AOP_SIZE (result); if (AOP_TYPE (right) == AOP_LIT) { litinv = (~lit) & (((unsigned int) 0xffffffff) >> (8 * (4 - size))); if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && (AOP_TYPE (left) == AOP_DIR) && isLiteralBit (litinv)) { bitpos = isLiteralBit (litinv) - 1; emitcode ("bclr", "#%d,%s", bitpos & 7, aopAdrStr (AOP (left), bitpos >> 3, FALSE)); regalloc_dry_run_cost += 2; goto release; } } needpulla = pushRegIfSurv (hc08_reg_a); offset = 0; if (size >= 2 && IS_AOP_AX (AOP (left))) { pushReg (hc08_reg_a, TRUE); earlystore = TRUE; } while (size--) { bytemask = (lit >> (offset * 8)) & 0xff; if (earlystore && offset == 1) pullReg (hc08_reg_a); if (AOP_TYPE (right) == AOP_LIT && bytemask == 0) { if (isOperandVolatile (left, FALSE)) { loadRegFromAop (hc08_reg_a, AOP (left), offset); hc08_freeReg (hc08_reg_a); } storeConstToAop (0, AOP (result), offset); } else if (AOP_TYPE (right) == AOP_LIT && bytemask == 0xff) { transferAopAop (AOP (left), offset, AOP (result), offset); } else { loadRegFromAop (hc08_reg_a, AOP (left), offset); accopWithAop ("and", AOP (right), offset); storeRegToAop (hc08_reg_a, AOP (result), offset); hc08_freeReg (hc08_reg_a); } if (AOP_TYPE (result) == AOP_REG && size && AOP (result)->aopu.aop_reg[offset]->rIdx == A_IDX) { pushReg (hc08_reg_a, TRUE); needpulla = TRUE; } offset++; } pullOrFreeReg (hc08_reg_a, needpulla); release: freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genOr - code for or */ /*-----------------------------------------------------------------*/ static void genOr (iCode * ic, iCode * ifx) { operand *left, *right, *result; int size, offset = 0; unsigned long long lit = 0ull; unsigned char bytemask; bool needpulla = FALSE; bool earlystore = FALSE; 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 DD (emitcode ("", "; Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right))); DD (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) { operand *tmp = right; right = left; left = tmp; } /* if left is accumulator & right is not then exchange them */ if (AOP_TYPE (right) == AOP_REG && !IS_AOP_WITH_A (AOP (left))) { operand *tmp = right; right = left; left = tmp; } if (AOP_TYPE (right) == AOP_LIT) lit = ullFromVal (AOP (right)->aopu.aop_lit); size = (AOP_SIZE (left) >= AOP_SIZE (right)) ? AOP_SIZE (left) : AOP_SIZE (right); if (AOP_TYPE (result) == AOP_CRY && size > 1 && (isOperandVolatile (left, FALSE) || isOperandVolatile (right, FALSE))) { needpulla = pushRegIfSurv (hc08_reg_a); /* this generates ugly code, but meets volatility requirements */ loadRegFromConst (hc08_reg_a, 0); pushReg (hc08_reg_a, TRUE); offset = 0; while (size--) { loadRegFromAop (hc08_reg_a, AOP (left), offset); accopWithAop ("ora", AOP (right), offset); emitcode ("ora", "1,s"); emitcode ("sta", "1,s"); regalloc_dry_run_cost += 6; offset++; } pullReg (hc08_reg_a); emitcode ("tsta", ""); regalloc_dry_run_cost++; pullOrFreeReg (hc08_reg_a, needpulla); genIfxJump (ifx, "a"); goto release; } if (AOP_TYPE (result) == AOP_CRY) { symbol *tlbl = NULL; needpulla = pushRegIfSurv (hc08_reg_a); offset = 0; while (size--) { bytemask = (lit >> (offset * 8)) & 0xff; if (AOP_TYPE (right) == AOP_LIT && bytemask == 0x00) { rmwWithAop ("tst", AOP (left), offset); if (size) { if (!tlbl && !regalloc_dry_run) tlbl = newiTempLabel (NULL); emitBranch ("bne", tlbl); } } else { loadRegFromAop (hc08_reg_a, AOP (left), offset); accopWithAop ("ora", AOP (right), offset); hc08_freeReg (hc08_reg_a); if (size) { if (!tlbl && !regalloc_dry_run) tlbl = newiTempLabel (NULL); emitBranch ("bne", tlbl); } } offset++; } if (tlbl) emitLabel (tlbl); pullOrFreeReg (hc08_reg_a, needpulla); if (ifx) genIfxJump (ifx, "a"); goto release; } if (AOP_TYPE (right) == AOP_LIT) lit = ullFromVal (AOP (right)->aopu.aop_lit); size = AOP_SIZE (result); if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && (AOP_TYPE (right) == AOP_LIT) && isLiteralBit (lit) && (AOP_TYPE (left) == AOP_DIR)) { int bitpos = isLiteralBit (lit) - 1; emitcode ("bset", "#%d,%s", bitpos & 7, aopAdrStr (AOP (left), bitpos >> 3, FALSE)); regalloc_dry_run_cost += 2; goto release; } needpulla = pushRegIfSurv (hc08_reg_a); offset = 0; if (size >= 2 && IS_AOP_AX (AOP (left))) { pushReg (hc08_reg_a, TRUE); earlystore = TRUE; } while (size--) { bytemask = (lit >> (offset * 8)) & 0xff; if (earlystore && offset == 1) pullReg (hc08_reg_a); if (AOP_TYPE (right) == AOP_LIT && bytemask == 0xff) { if (isOperandVolatile (left, FALSE)) { loadRegFromAop (hc08_reg_a, AOP (left), offset); hc08_freeReg (hc08_reg_a); } transferAopAop (AOP (right), offset, AOP (result), offset); } else if (AOP_TYPE (right) == AOP_LIT && bytemask == 0) { transferAopAop (AOP (left), offset, AOP (result), offset); } else { loadRegFromAop (hc08_reg_a, AOP (left), offset); accopWithAop ("ora", AOP (right), offset); storeRegToAop (hc08_reg_a, AOP (result), offset); hc08_freeReg (hc08_reg_a); } if (AOP_TYPE (result) == AOP_REG && size && AOP (result)->aopu.aop_reg[offset]->rIdx == A_IDX) { pushReg (hc08_reg_a, TRUE); needpulla = TRUE; } offset++; } pullOrFreeReg (hc08_reg_a, needpulla); release: freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genXor - code for Exclusive or */ /*-----------------------------------------------------------------*/ static void genXor (iCode * ic, iCode * ifx) { operand *left, *right, *result; int size, offset = 0; bool needpulla = FALSE; bool earlystore = FALSE; 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 DD (emitcode ("", "; Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right))); DD (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) { operand *tmp = right; right = left; left = tmp; } /* if left is accumulator & right is not then exchange them */ if (AOP_TYPE (right) == AOP_REG && !IS_AOP_WITH_A (AOP (left))) { operand *tmp = right; right = left; left = tmp; } needpulla = pushRegIfSurv (hc08_reg_a); if (AOP_TYPE (result) == AOP_CRY) { symbol *tlbl; tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); size = (AOP_SIZE (left) >= AOP_SIZE (right)) ? AOP_SIZE (left) : AOP_SIZE (right); offset = 0; while (size--) { loadRegFromAop (hc08_reg_a, AOP (left), offset); if (AOP_TYPE (right) == AOP_LIT && ((ullFromVal (AOP (right)->aopu.aop_lit) >> (offset * 8)) & 0xff) == 0) { emitcode ("tsta", ""); regalloc_dry_run_cost++; } else accopWithAop ("eor", AOP (right), offset); hc08_freeReg (hc08_reg_a); if (size) emitBranch ("bne", tlbl); else { /* * I think this is all broken here, (see simulation mismatch in bug1875933.c) * multiple calls to emitLabel() ?! * and we can't genIfxJump, if there is none */ if (!regalloc_dry_run) emitLabel (tlbl); pullOrFreeReg (hc08_reg_a, needpulla); if (ifx) genIfxJump (ifx, "a"); } offset++; } goto release; } size = AOP_SIZE (result); offset = 0; if (size >= 2 && IS_AOP_AX (AOP (left))) { pushReg (hc08_reg_a, TRUE); earlystore = TRUE; } while (size--) { if (earlystore && offset == 1) pullReg (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (left), offset); accopWithAop ("eor", AOP (right), offset); storeRegToAop (hc08_reg_a, AOP (result), offset); if (AOP_TYPE (result) == AOP_REG && size && AOP (result)->aopu.aop_reg[offset]->rIdx == A_IDX) { pushReg (hc08_reg_a, TRUE); needpulla = TRUE; } hc08_freeReg (hc08_reg_a); offset++; } pullOrFreeReg (hc08_reg_a, needpulla); release: freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); freeAsmop (result, NULL, ic, TRUE); } static const char * expand_symbols (iCode * ic, const char *inlin) { const char *begin = NULL, *p = inlin; bool inIdent = FALSE; struct dbuf_s dbuf; dbuf_init (&dbuf, 128); while (*p) { if (inIdent) { if ('_' == *p || isalnum (*p)) /* in the middle of identifier */ ++p; else { /* end of identifier */ symbol *sym, *tempsym; char *symname = Safe_strndup (p + 1, p - begin - 1); inIdent = 0; tempsym = newSymbol (symname, ic->level); tempsym->block = ic->block; sym = (symbol *) findSymWithLevel (SymbolTab, tempsym); if (!sym) { dbuf_append (&dbuf, begin, p - begin); } else { asmop *aop = aopForSym (ic, sym, FALSE); const char *l = aopAdrStr (aop, aop->size - 1, TRUE); if ('#' == *l) l++; sym->isref = 1; if (sym->level && !sym->allocreq && !sym->ismyparm) { werror (E_ID_UNDEF, sym->name); werror (W_CONTINUE, " Add 'volatile' to the variable declaration so that it\n" " can be referenced within inline assembly"); } dbuf_append_str (&dbuf, l); } Safe_free (symname); begin = p++; } } else if ('_' == *p) { /* begin of identifier */ inIdent = TRUE; if (begin) dbuf_append (&dbuf, begin, p - begin); begin = p++; } else { if (!begin) begin = p; p++; } } if (begin) dbuf_append (&dbuf, begin, p - begin); return dbuf_detach_c_str (&dbuf); } /*-----------------------------------------------------------------*/ /* genInline - write the inline code out */ /*-----------------------------------------------------------------*/ static void hc08_genInline (iCode * ic) { char *buf, *bp, *begin; const char *expanded; bool inComment = FALSE; D (emitcode (";", "genInline")); genLine.lineElement.isInline += (!options.asmpeep); buf = bp = begin = Safe_strdup (IC_INLINE (ic)); /* emit each line as a code */ while (*bp) { switch (*bp) { case ';': inComment = TRUE; ++bp; break; case '\x87': case '\n': inComment = FALSE; *bp++ = '\0'; expanded = expand_symbols (ic, begin); emitcode (expanded, NULL); dbuf_free (expanded); begin = bp; break; default: /* Add \n for labels, not dirs such as c:\mydir */ if (!inComment && (*bp == ':') && (isspace ((unsigned char) bp[1]))) { ++bp; *bp = '\0'; ++bp; emitcode (begin, NULL); begin = bp; } else ++bp; break; } } if (begin != bp) { expanded = expand_symbols (ic, begin); emitcode (expanded, NULL); dbuf_free (expanded); } Safe_free (buf); /* consumed; we can free it here */ dbuf_free (IC_INLINE (ic)); genLine.lineElement.isInline -= (!options.asmpeep); } /*-----------------------------------------------------------------*/ /* genRRC - rotate right with carry */ /*-----------------------------------------------------------------*/ static void genRRC (iCode * ic) { operand *left, *result; int size, offset = 0; bool needpula = FALSE; bool resultInA = FALSE; char *shift; D (emitcode ("; genRRC", "")); /* rotate right with carry */ left = IC_LEFT (ic); result = IC_RESULT (ic); aopOp (left, ic, FALSE); aopOp (result, ic, FALSE); if ((AOP_TYPE (result) == AOP_REG) && (AOP (result)->aopu.aop_reg[0]->rIdx == A_IDX)) resultInA = TRUE; size = AOP_SIZE (result); offset = size - 1; shift = "lsr"; if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) { while (size--) { rmwWithAop (shift, AOP (result), offset--); shift = "ror"; } } else { while (size--) { loadRegFromAop (hc08_reg_a, AOP (left), offset); rmwWithReg (shift, hc08_reg_a); storeRegToAop (hc08_reg_a, AOP (result), offset--); hc08_freeReg (hc08_reg_a); shift = "ror"; } } if ((!hc08_reg_a->isFree) || resultInA) { pushReg (hc08_reg_a, TRUE); needpula = TRUE; } /* now we need to put the carry into the highest order byte of the result */ offset = AOP_SIZE (result) - 1; emitcode ("clra", ""); emitcode ("rora", ""); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_a, FALSE); if (resultInA) { emitcode ("ora", "1,s"); pullNull (1); regalloc_dry_run_cost += 3; hc08_dirtyReg (hc08_reg_a, FALSE); needpula = FALSE; } else accopWithAop ("ora", AOP (result), offset); storeRegToAop (hc08_reg_a, AOP (result), offset); pullOrFreeReg (hc08_reg_a, needpula); freeAsmop (left, NULL, ic, TRUE); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genRLC - generate code for rotate left with carry */ /*-----------------------------------------------------------------*/ static void genRLC (iCode * ic) { operand *left, *result; int size, offset = 0; char *shift; bool resultInA = FALSE; bool needpula = FALSE; D (emitcode ("; genRLC", "")); /* rotate right with carry */ left = IC_LEFT (ic); result = IC_RESULT (ic); aopOp (left, ic, FALSE); aopOp (result, ic, FALSE); if ((AOP_TYPE (result) == AOP_REG) && (AOP (result)->aopu.aop_reg[0]->rIdx == A_IDX)) resultInA = TRUE; size = AOP_SIZE (result); offset = 0; shift = "lsl"; if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) { while (size--) { rmwWithAop (shift, AOP (result), offset++); shift = "rol"; } } else { while (size--) { loadRegFromAop (hc08_reg_a, AOP (left), offset); rmwWithReg (shift, hc08_reg_a); storeRegToAop (hc08_reg_a, AOP (result), offset++); hc08_freeReg (hc08_reg_a); shift = "rol"; } } if ((!hc08_reg_a->isFree) || resultInA) { pushReg (hc08_reg_a, TRUE); needpula = TRUE; } /* now we need to put the carry into the lowest order byte of the result */ offset = 0; emitcode ("clra", ""); emitcode ("rola", ""); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_a, FALSE); if (resultInA) { emitcode ("ora", "1,s"); pullNull (1); regalloc_dry_run_cost += 3; hc08_dirtyReg (hc08_reg_a, FALSE); needpula = FALSE; } else accopWithAop ("ora", AOP (result), offset); storeRegToAop (hc08_reg_a, AOP (result), offset); pullOrFreeReg (hc08_reg_a, needpula); freeAsmop (left, NULL, ic, TRUE); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genGetAbit - generates code get a single bit */ /*-----------------------------------------------------------------*/ static void genGetAbit (iCode * ic) { operand *left, *right, *result; int shCount; bool needpulla; 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); needpulla = pushRegIfSurv (hc08_reg_a); /* get the needed byte into a */ loadRegFromAop (hc08_reg_a, AOP (left), shCount / 8); shCount %= 8; if (AOP_TYPE (result) == AOP_CRY) { emitcode ("and", "#0x%02x", 1 << shCount); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_a, FALSE); } else { switch (shCount) { case 3: emitcode ("lsra", ""); regalloc_dry_run_cost++; //fallthrough case 2: emitcode ("lsra", ""); regalloc_dry_run_cost++; //fallthrough case 1: emitcode ("lsra", ""); regalloc_dry_run_cost++; //fallthrough case 0: emitcode ("and", "#0x01"); regalloc_dry_run_cost += 2; break; case 4: emitcode ("nsa", ""); emitcode ("and", "#0x01"); regalloc_dry_run_cost += 3; break; case 5: emitcode ("rola", ""); regalloc_dry_run_cost++; //fallthrough case 6: emitcode ("rola", ""); regalloc_dry_run_cost++; //fallthrough case 7: emitcode ("rola", ""); emitcode ("clra", ""); emitcode ("rola", ""); regalloc_dry_run_cost += 3; break; } hc08_dirtyReg (hc08_reg_a, FALSE); storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); } pullOrFreeReg (hc08_reg_a, needpulla); 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; transferAopAop (AOP (left), offset, AOP (result), 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; transferAopAop (AOP (left), offset + 1, AOP (result), 1); transferAopAop (AOP (left), offset, AOP (result), 0); 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; bool needpulla; 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 */ needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (left), 0); emitcode ("nsa", ""); regalloc_dry_run_cost++; hc08_dirtyReg (hc08_reg_a, FALSE); storeRegToAop (hc08_reg_a, AOP (result), 0); pullOrFreeReg (hc08_reg_a, needpulla); break; case 2: /* swap bytes in a word */ if (IS_AOP_XA (AOP (left)) && IS_AOP_AX (AOP (result)) || IS_AOP_AX (AOP (left)) && IS_AOP_XA (AOP (result))) break; if (AOP_TYPE (result) == AOP_REG && AOP_TYPE (left) == AOP_REG) { if (AOP (result)->aopu.aop_reg[1] != AOP (left)->aopu.aop_reg[0]) pushReg (AOP (left)->aopu.aop_reg[0], TRUE); storeRegToAop (AOP (left)->aopu.aop_reg[1], AOP (result), 0); if (AOP (result)->aopu.aop_reg[1] != AOP (left)->aopu.aop_reg[0]) pullReg (AOP (result)->aopu.aop_reg[1]); } else if (operandsEqu (left, result) || sameRegs (AOP (left), AOP (result))) { needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (left), 0); hc08_useReg (hc08_reg_a); transferAopAop (AOP (left), 1, AOP (result), 0); storeRegToAop (hc08_reg_a, AOP (result), 1); pullOrFreeReg (hc08_reg_a, needpulla); } else { transferAopAop (AOP (left), 0, AOP (result), 1); transferAopAop (AOP (left), 1, AOP (result), 0); } break; default: wassertl (FALSE, "unsupported SWAP operand size"); } freeAsmop (left, NULL, ic, TRUE); freeAsmop (result, NULL, ic, TRUE); } #if 0 /*-----------------------------------------------------------------*/ /* 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 ("rola", ""); /* 1 cycle */ break; case 2: emitcode ("rola", ""); /* 1 cycle */ emitcode ("rola", ""); /* 1 cycle */ break; case 3: emitcode ("nsa", ""); emitcode ("rora", ""); break; case 4: emitcode ("nsa", ""); /* 3 cycles */ break; case 5: emitcode ("nsa", ""); /* 3 cycles */ emitcode ("rola", ""); /* 1 cycle */ break; case 6: emitcode ("nsa", ""); /* 3 cycles */ emitcode ("rola", ""); /* 1 cycle */ emitcode ("rola", ""); /* 1 cycle */ break; case 7: emitcode ("nsa", ""); /* 3 cycles */ emitcode ("rola", ""); /* 1 cycle */ emitcode ("rola", ""); /* 1 cycle */ emitcode ("rola", ""); /* 1 cycle */ break; } } #endif /*-----------------------------------------------------------------*/ /* AccLsh - left shift accumulator by known count */ /*-----------------------------------------------------------------*/ static void AccLsh (int shCount) { int i; shCount &= 0x0007; // shCount : 0..7 /* Shift counts of 4 and 5 are currently optimized for code size. */ /* Falling through to the unrolled loop would be optimal for code speed. */ /* For shift counts of 6 and 7, the unrolled loop is never optimal. */ switch (shCount) { case 4: if (optimize.codeSpeed) break; accopWithMisc ("nsa", ""); accopWithMisc ("and", "#0xf0"); /* total: 5 cycles, 3 bytes */ return; case 5: if (optimize.codeSpeed) break; accopWithMisc ("nsa", ""); accopWithMisc ("and", "#0xf0"); accopWithMisc ("lsla", ""); /* total: 6 cycles, 4 bytes */ return; case 6: accopWithMisc ("rora", ""); accopWithMisc ("rora", ""); accopWithMisc ("rora", ""); accopWithMisc ("and", "#0xc0"); /* total: 5 cycles, 5 bytes */ return; case 7: accopWithMisc ("rora", ""); accopWithMisc ("clra", ""); accopWithMisc ("rora", ""); /* total: 3 cycles, 3 bytes */ return; } /* lsla is only 1 cycle and byte, so an unrolled loop is often */ /* the fastest (shCount<6) and shortest (shCount<4). */ for (i = 0; i < shCount; i++) accopWithMisc ("lsla", ""); } /*-----------------------------------------------------------------*/ /* AccSRsh - signed right shift accumulator by known count */ /*-----------------------------------------------------------------*/ static void AccSRsh (int shCount) { int i; shCount &= 0x0007; // shCount : 0..7 if (shCount == 7) { accopWithMisc ("rola", ""); accopWithMisc ("clra", ""); accopWithMisc ("sbc", zero); /* total: 4 cycles, 4 bytes */ return; } for (i = 0; i < shCount; i++) accopWithMisc ("asra", ""); } /*-----------------------------------------------------------------*/ /* AccRsh - right shift accumulator by known count */ /*-----------------------------------------------------------------*/ static void AccRsh (int shCount, bool sign) { int i; if (sign) { AccSRsh (shCount); return; } shCount &= 0x0007; // shCount : 0..7 /* Shift counts of 4 and 5 are currently optimized for code size. */ /* Falling through to the unrolled loop would be optimal for code speed. */ /* For shift counts of 6 and 7, the unrolled loop is never optimal. */ switch (shCount) { case 4: if (optimize.codeSpeed) break; accopWithMisc ("nsa", ""); accopWithMisc ("and", "#0x0f"); /* total: 5 cycles, 3 bytes */ return; case 5: if (optimize.codeSpeed) break; accopWithMisc ("nsa", ""); accopWithMisc ("and", "#0x0f"); accopWithMisc ("lsra", ""); /* total: 6 cycles, 4 bytes */ return; case 6: accopWithMisc ("rola", ""); accopWithMisc ("rola", ""); accopWithMisc ("rola", ""); accopWithMisc ("and", "#0x03"); /* total: 5 cycles, 5 bytes */ return; case 7: accopWithMisc ("rola", ""); accopWithMisc ("clra", ""); accopWithMisc ("rola", ""); /* total: 3 cycles, 3 bytes */ return; } /* lsra is only 1 cycle and byte, so an unrolled loop is often */ /* the fastest (shCount<6) and shortest (shCount<4). */ for (i = 0; i < shCount; i++) accopWithMisc ("lsra", ""); } /*-----------------------------------------------------------------*/ /* XAccLsh - left shift register pair XA by known count */ /*-----------------------------------------------------------------*/ static void XAccLsh (int shCount) { int i; shCount &= 0x000f; // shCount : 0..15 if (shCount >= 8) { AccLsh (shCount - 8); transferRegReg (hc08_reg_a, hc08_reg_x, FALSE); loadRegFromConst (hc08_reg_a, 0); return; } /* if we can beat 2n cycles or bytes for some special case, do it here */ switch (shCount) { case 7: /* bytes cycles reg x reg a carry ** abcd efgh ijkl mnop ? ** lsrx 1 1 0abc defg ijkl mnop h ** rora 1 1 0abc defg hijk lmno p ** tax 1 1 hijk lmno hijk lmno p ** clra 1 1 hijk lmno 0000 0000 p ** rora 1 1 hijk lmno p000 0000 0 ** total: 5 cycles, 5 bytes (beats 14 cycles, 14 bytes) */ rmwWithReg ("lsr", hc08_reg_x); rmwWithReg ("ror", hc08_reg_a); transferRegReg (hc08_reg_a, hc08_reg_x, FALSE); loadRegFromConst (hc08_reg_a, 0); rmwWithReg ("ror", hc08_reg_a); return; default: ; } /* lsla/rolx is only 2 cycles and bytes, so an unrolled loop is often */ /* the fastest and shortest. */ for (i = 0; i < shCount; i++) { rmwWithReg ("lsl", hc08_reg_a); rmwWithReg ("rol", hc08_reg_x); } } /*-----------------------------------------------------------------*/ /* XAccSRsh - signed right shift register pair XA by known count */ /*-----------------------------------------------------------------*/ static void XAccSRsh (int shCount) { int i; shCount &= 0x000f; // shCount : 0..7 /* if we can beat 2n cycles or bytes for some special case, do it here */ switch (shCount) { case 15: /* bytes cycles reg x reg a carry ** abcd efgh ijkl mnop ? ** lslx 1 1 bcde fgh0 ijkl mnop a ** clra 1 1 bcde fgh0 0000 0000 a ** rola 1 1 bcde fgh0 0000 000a 0 ** nega 1 1 bcde fgh0 aaaa aaaa a ** tax 1 1 aaaa aaaa aaaa aaaa a ** total: 5 cycles, 5 bytes */ rmwWithReg ("lsl", hc08_reg_x); loadRegFromConst (hc08_reg_a, 0); rmwWithReg ("rol", hc08_reg_a); rmwWithReg ("neg", hc08_reg_a); transferRegReg (hc08_reg_a, hc08_reg_x, FALSE); return; case 14: case 13: case 12: case 11: case 10: case 9: case 8: /* bytes cycles reg x reg a carry ** abcd efgh ijkl mnop ? ** txa 1 1 abcd efgh abcd efgh ? ** (AccSRsh) <8 <8 abcd efgh LSBresult ? ** lsla 1 1 abcd efgh ???? ???? a ** clrx 1 1 0000 0000 ???? ???? a ** rolx 1 1 0000 000a ???? ???? 0 ** negx 1 1 aaaa aaaa ???? ???? a ** rora 1 1 aaaa aaaa LSBresult 0 ** total: n-2 cycles, n-2 bytes (beats 2n cycles, 2n bytes (for n>=8)) */ transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); AccSRsh (shCount - 8); rmwWithReg ("lsl", hc08_reg_a); loadRegFromConst (hc08_reg_x, 0); rmwWithReg ("rol", hc08_reg_x); rmwWithReg ("neg", hc08_reg_x); rmwWithReg ("ror", hc08_reg_a); return; default: ; } /* asrx/rora is only 2 cycles and bytes, so an unrolled loop is often */ /* the fastest and shortest. */ for (i = 0; i < shCount; i++) { rmwWithReg ("asr", hc08_reg_x); rmwWithReg ("ror", hc08_reg_a); } } /*-----------------------------------------------------------------*/ /* XAccRsh - right shift register pair XA by known count */ /*-----------------------------------------------------------------*/ static void XAccRsh (int shCount, bool sign) { int i; if (sign) { XAccSRsh (shCount); return; } shCount &= 0x000f; // shCount : 0..f /* if we can beat 2n cycles or bytes for some special case, do it here */ switch (shCount) { case 15: /* bytes cycles reg x reg a carry ** abcd efgh ijkl mnop ? ** clra 1 1 abcd efgh 0000 0000 a ** lslx 1 1 bcde fgh0 0000 0000 a ** rola 1 1 bcde fgh0 0000 000a 0 ** clrx 1 1 0000 0000 0000 000a 0 ** total: 4 cycles, 4 bytes */ loadRegFromConst (hc08_reg_x, 0); rmwWithReg ("lsl", hc08_reg_x); rmwWithReg ("rol", hc08_reg_a); loadRegFromConst (hc08_reg_a, 0); return; case 14: /* bytes cycles reg x reg a carry ** abcd efgh ijkl mnop ? ** clra 1 1 abcd efgh 0000 0000 a ** lslx 1 1 bcde fgh0 0000 0000 a ** rola 1 1 bcde fgh0 0000 000a 0 ** lslx 1 1 cdef gh00 0000 000a b ** rola 1 1 cdef gh00 0000 00ab 0 ** clrx 1 1 0000 0000 0000 00ab 0 ** total: 6 cycles, 6 bytes */ loadRegFromConst (hc08_reg_x, 0); rmwWithReg ("lsl", hc08_reg_x); rmwWithReg ("rol", hc08_reg_a); rmwWithReg ("lsl", hc08_reg_x); rmwWithReg ("rol", hc08_reg_a); loadRegFromConst (hc08_reg_a, 0); return; case 13: case 12: case 11: case 10: case 9: case 8: transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); AccRsh (shCount - 8, FALSE); loadRegFromConst (hc08_reg_x, 0); return; case 7: /* bytes cycles reg x reg a carry ** abcd efgh ijkl mnop ? ** lsla 1 1 abcd efgh jklm nop0 i ** txa 1 1 abcd efgh abcd efgh i ** rola 1 1 abcd efgh bcde fghi a ** clrx 1 1 0000 0000 bcde fghi a ** rolx 1 1 0000 000a bcde fghi 0 ** total: 5 cycles, 5 bytes (beats 14 cycles, 14 bytes) */ rmwWithReg ("lsl", hc08_reg_a); transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); rmwWithReg ("rol", hc08_reg_a); loadRegFromConst (hc08_reg_x, 0); rmwWithReg ("rol", hc08_reg_x); return; case 6: /* bytes cycles reg x reg a carry ** abcd efgh ijkl mnop ? ** lsla 1 1 abcd efgh jklm nop0 i ** rolx 1 1 bcde fghi jklm nop0 a ** rola 1 1 bcde fghi klmn op0a j ** rolx 1 1 cdef ghij klmn op0a b ** rola 1 1 cdef ghij lmno p0ab k ** and #3 2 2 cdef ghij 0000 00ab k ** psha 1 2 cdef ghij 0000 00ab k ** txa 1 1 cdef ghij cdef ghij k ** pula 1 2 0000 00ab cdef ghij k ** total: 12 cycles, 10 bytes (beats 12 bytes) */ default: ; } /* lsrx/rora is only 2 cycles and bytes, so an unrolled loop is often */ /* the fastest and shortest. */ for (i = 0; i < shCount; i++) { rmwWithReg ("lsr", hc08_reg_x); rmwWithReg ("ror", hc08_reg_a); } } /*-----------------------------------------------------------------*/ /* shiftL1Left2Result - shift left one byte from left to result */ /*-----------------------------------------------------------------*/ static void shiftL1Left2Result (operand * left, int offl, operand * result, int offr, int shCount) { bool needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (left), offl); /* shift left accumulator */ AccLsh (shCount); storeRegToAop (hc08_reg_a, AOP (result), offr); pullOrFreeReg (hc08_reg_a, needpulla); } /*-----------------------------------------------------------------*/ /* movLeft2Result - move byte from left to result */ /*-----------------------------------------------------------------*/ static void movLeft2Result (operand * left, int offl, operand * result, int offr, int sign) { if (!sameRegs (AOP (left), AOP (result)) || (offl != offr)) { transferAopAop (AOP (left), offl, AOP (result), offr); } } /*-----------------------------------------------------------------*/ /* shiftL2Left2Result - shift left two bytes from left to result */ /*-----------------------------------------------------------------*/ static void shiftL2Left2Result (operand * left, int offl, operand * result, int offr, int shCount) { int i; bool needpula = FALSE; bool needpulx = FALSE; if (!IS_AOP_XA (AOP (left)) && !IS_AOP_A (AOP (left))) needpula = pushRegIfUsed (hc08_reg_a); else needpula = FALSE; if (!IS_AOP_XA (AOP (left))) needpulx = pushRegIfUsed (hc08_reg_x); else needpulx = FALSE; loadRegFromAop (hc08_reg_xa, AOP (left), offl); switch (shCount) { case 7: rmwWithReg ("lsr", hc08_reg_x); rmwWithReg ("ror", hc08_reg_a); transferRegReg (hc08_reg_a, hc08_reg_x, FALSE); rmwWithReg ("clr", hc08_reg_a); rmwWithReg ("ror", hc08_reg_a); break; default: for (i = 0; i < shCount; i++) { rmwWithReg ("lsl", hc08_reg_a); rmwWithReg ("rol", hc08_reg_x); } } storeRegToAop (hc08_reg_xa, AOP (result), offr); pullOrFreeReg (hc08_reg_x, needpulx); pullOrFreeReg (hc08_reg_a, needpula); } /*-----------------------------------------------------------------*/ /* shiftRLeftOrResult - shift right one byte from left,or to result */ /*-----------------------------------------------------------------*/ static void shiftRLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) { loadRegFromAop (hc08_reg_a, AOP (left), offl); /* shift left accumulator */ AccRsh (shCount, FALSE); /* or with result */ accopWithAop ("ora", AOP (result), offr); /* back to result */ storeRegToAop (hc08_reg_a, AOP (result), offr); hc08_freeReg (hc08_reg_a); } /*-----------------------------------------------------------------*/ /* genlshOne - left shift a one byte quantity by known count */ /*-----------------------------------------------------------------*/ static void genlshOne (operand * result, operand * left, int shCount) { D (emitcode ("; genlshOne", "")); shiftL1Left2Result (left, LSB, result, LSB, shCount); } /*-----------------------------------------------------------------*/ /* genlshTwo - left shift two bytes by known amount != 0 */ /*-----------------------------------------------------------------*/ static void genlshTwo (operand * result, operand * left, int shCount) { int size; bool needpulla, needpullx; D (emitcode ("; genlshTwo", "")); size = getDataSize (result); /* if shCount >= 8 */ if (shCount >= 8) { shCount -= 8; needpulla = pushRegIfSurv (hc08_reg_a); if (size > 1) { loadRegFromAop (hc08_reg_a, AOP (left), 0); AccLsh (shCount); storeRegToAop (hc08_reg_a, AOP (result), 1); } storeConstToAop (0, AOP (result), LSB); pullOrFreeReg (hc08_reg_a, needpulla); } /* 1 <= shCount <= 7 */ else { needpulla = pushRegIfSurv (hc08_reg_a); needpullx = pushRegIfSurv (hc08_reg_x); loadRegFromAop (hc08_reg_xa, AOP (left), 0); XAccLsh (shCount); storeRegToFullAop (hc08_reg_xa, AOP (result), 0); pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_a, needpulla); } } /*-----------------------------------------------------------------*/ /* shiftLLong - shift left one long from left to result */ /* offr = LSB or MSB16 */ /*-----------------------------------------------------------------*/ static void shiftLLong (operand * left, operand * result, int offr) { // char *l; // int size = AOP_SIZE (result); bool needpula = FALSE; bool needpulx = FALSE; needpula = pushRegIfUsed (hc08_reg_a); needpulx = pushRegIfUsed (hc08_reg_x); loadRegFromAop (hc08_reg_xa, AOP (left), LSB); rmwWithReg ("lsl", hc08_reg_a); rmwWithReg ("rol", hc08_reg_x); if (offr == LSB) { storeRegToAop (hc08_reg_xa, AOP (result), offr); loadRegFromAop (hc08_reg_xa, AOP (left), MSB24); rmwWithReg ("rol", hc08_reg_a); rmwWithReg ("rol", hc08_reg_x); storeRegToAop (hc08_reg_xa, AOP (result), offr + 2); } else if (offr == MSB16) { storeRegToAop (hc08_reg_a, AOP (result), offr); loadRegFromAop (hc08_reg_a, AOP (left), MSB24); storeRegToAop (hc08_reg_x, AOP (result), offr + 1); rmwWithReg ("rol", hc08_reg_a); storeRegToAop (hc08_reg_a, AOP (result), offr + 2); storeConstToAop (0, AOP (result), 0); } pullOrFreeReg (hc08_reg_x, needpulx); pullOrFreeReg (hc08_reg_a, needpula); } /*-----------------------------------------------------------------*/ /* genlshFour - shift four byte by a known amount != 0 */ /*-----------------------------------------------------------------*/ static void genlshFour (operand * result, operand * left, int shCount) { int size; D (emitcode ("; genlshFour", "")); size = AOP_SIZE (result); /* TODO: deal with the &result == &left case */ /* if shifting more that 3 bytes */ if (shCount >= 24) { shCount -= 24; if (shCount) /* lowest order of left goes to the highest order of the destination */ shiftL1Left2Result (left, LSB, result, MSB32, shCount); else movLeft2Result (left, LSB, result, MSB32, 0); storeConstToAop (0, AOP (result), LSB); storeConstToAop (0, AOP (result), MSB16); storeConstToAop (0, AOP (result), MSB24); return; } /* more than two bytes */ else if (shCount >= 16) { /* lower order two bytes goes to higher order two bytes */ shCount -= 16; /* if some more remaining */ if (shCount) shiftL2Left2Result (left, LSB, result, MSB24, shCount); else { movLeft2Result (left, MSB16, result, MSB32, 0); movLeft2Result (left, LSB, result, MSB24, 0); } storeConstToAop (0, AOP (result), LSB); storeConstToAop (0, AOP (result), MSB16); return; } /* if more than 1 byte */ else if (shCount >= 8) { /* lower order three bytes goes to higher order three bytes */ shCount -= 8; if (size == 2) { if (shCount) shiftL1Left2Result (left, LSB, result, MSB16, shCount); else movLeft2Result (left, LSB, result, MSB16, 0); } else { /* size = 4 */ if (shCount == 0) { movLeft2Result (left, MSB24, result, MSB32, 0); movLeft2Result (left, MSB16, result, MSB24, 0); movLeft2Result (left, LSB, result, MSB16, 0); storeConstToAop (0, AOP (result), LSB); } else if (shCount == 1) shiftLLong (left, result, MSB16); else { shiftL2Left2Result (left, MSB16, result, MSB24, shCount); shiftL1Left2Result (left, LSB, result, MSB16, shCount); shiftRLeftOrResult (left, LSB, result, MSB24, 8 - shCount); storeConstToAop (0, AOP (result), LSB); } } } /* 1 <= shCount <= 7 */ else if (shCount <= 2) { shiftLLong (left, result, LSB); if (shCount == 2) shiftLLong (result, result, LSB); } /* 3 <= shCount <= 7, optimize */ else { shiftL2Left2Result (left, MSB24, result, MSB24, shCount); shiftRLeftOrResult (left, MSB16, result, MSB24, 8 - shCount); shiftL2Left2Result (left, LSB, result, LSB, shCount); } } /*-----------------------------------------------------------------*/ /* genLeftShiftLiteral - left shifting by known count */ /*-----------------------------------------------------------------*/ static void genLeftShiftLiteral (operand * left, operand * right, operand * result, iCode * ic) { int shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); int size; D (emitcode ("; genLeftShiftLiteral", "")); freeAsmop (right, NULL, ic, TRUE); aopOp (left, ic, FALSE); aopOp (result, ic, FALSE); // size = getSize (operandType (result)); size = AOP_SIZE (result); #if VIEW_SIZE DD (emitcode ("; shift left ", "result %d, left %d", size, AOP_SIZE (left))); #endif if (shCount == 0) { genCopy (result, left); } else if (shCount >= (size * 8)) { while (size--) storeConstToAop (0, AOP (result), size); } else { switch (size) { case 1: genlshOne (result, left, shCount); break; case 2: genlshTwo (result, left, shCount); break; case 4: genlshFour (result, left, shCount); break; default: werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "*** ack! mystery literal shift!\n"); break; } } freeAsmop (left, NULL, ic, TRUE); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genLeftShift - generates code for left shifting */ /*-----------------------------------------------------------------*/ static void genLeftShift (iCode * ic) { operand *left, *right, *result; int size, offset; symbol *tlbl, *tlbl1; char *shift; asmop *aopResult; bool needpullcountreg; reg_info *countreg = NULL; 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 X : 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 ) */ aopOp (result, ic, FALSE); aopOp (left, ic, FALSE); aopResult = AOP (result); if (sameRegs (AOP (right), AOP (result)) || regsInCommon (right, result) || IS_AOP_XA (AOP (result)) || isOperandVolatile (result, FALSE)) aopResult = forceStackedAop (AOP (result), sameRegs (AOP (left), AOP (result))); /* now move the left to the result if they are not the same */ if (IS_AOP_HX (AOP (result))) loadRegFromAop (hc08_reg_hx, AOP (left), 0); else if (IS_AOP_AX (AOP (result)) && IS_AOP_XA (AOP (left)) || IS_AOP_XA (AOP (result)) && IS_AOP_AX (AOP (left))) { pushReg (hc08_reg_x, TRUE); emitcode("tax", ""); regalloc_dry_run_cost++; pullReg (hc08_reg_a); } else if (!sameRegs (AOP (left), aopResult)) { size = AOP_SIZE (result); offset = 0; while (size--) { transferAopAop (AOP (left), offset, aopResult, offset); offset++; } } freeAsmop (left, NULL, ic, TRUE); AOP (result) = aopResult; tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); size = AOP_SIZE (result); offset = 0; tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); if (hc08_reg_x->isDead && !IS_AOP_X (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result)) && !IS_AOP_HX (AOP (result))) countreg = hc08_reg_x; else if (hc08_reg_a->isDead && !IS_AOP_A (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result))) countreg = hc08_reg_a; else if (!IS_AOP_X (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result)) && !IS_AOP_HX (AOP (result))) countreg = hc08_reg_x; else if(!IS_AOP_A (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result))) countreg = hc08_reg_a; needpullcountreg = (countreg && pushRegIfSurv (countreg)); if(countreg) { countreg->isFree = FALSE; loadRegFromAop (countreg, AOP (right), 0); } else { pushReg (hc08_reg_a, FALSE); pushReg (hc08_reg_a, TRUE); loadRegFromAop (hc08_reg_a, AOP (right), 0); emitcode ("sta", "2, s"); regalloc_dry_run_cost += 3; pullReg (hc08_reg_a); } emitcode (countreg == hc08_reg_a ? "tsta" : (countreg ? "tstx" : "tst 1, s"), ""); regalloc_dry_run_cost += (countreg ? 1 : 3); emitBranch ("beq", tlbl1); if (!regalloc_dry_run) emitLabel (tlbl); shift = "lsl"; for (offset = 0; offset < size; offset++) { rmwWithAop (shift, AOP (result), offset); shift = "rol"; } if (!regalloc_dry_run) emitcode (countreg == hc08_reg_a ? "dbnza" : (countreg ? "dbnzx" : "dbnz 1, s"), "%05d$", labelKey2num (tlbl->key)); regalloc_dry_run_cost += (countreg ? 2 : 4); if (!regalloc_dry_run) emitLabel (tlbl1); if (!countreg) pullNull (1); else pullOrFreeReg (countreg, needpullcountreg); freeAsmop (result, NULL, ic, TRUE); freeAsmop (right, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genrshOne - right shift a one byte quantity by known count */ /*-----------------------------------------------------------------*/ static void genrshOne (operand * result, operand * left, int shCount, int sign) { bool needpulla; D (emitcode ("; genrshOne", "")); needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (left), 0); AccRsh (shCount, sign); storeRegToFullAop (hc08_reg_a, AOP (result), sign); pullOrFreeReg (hc08_reg_a, needpulla); } /*-----------------------------------------------------------------*/ /* genrshTwo - right shift two bytes by known amount != 0 */ /*-----------------------------------------------------------------*/ static void genrshTwo (operand * result, operand * left, int shCount, int sign) { bool needpulla, needpullx; D (emitcode ("; genrshTwo", "")); /* if shCount >= 8 */ if (shCount >= 8) { if (shCount != 8 || sign) { needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (left), 1); AccRsh (shCount - 8, sign); storeRegToFullAop (hc08_reg_a, AOP (result), sign); pullOrFreeReg (hc08_reg_a, needpulla); } else { transferAopAop (AOP (left), 1, AOP (result), 0); storeConstToAop (0, AOP (result), 1); } } /* 1 <= shCount <= 7 */ else { needpulla = pushRegIfSurv (hc08_reg_a); needpullx = pushRegIfSurv (hc08_reg_x); loadRegFromAop (hc08_reg_xa, AOP (left), 0); XAccRsh (shCount, sign); storeRegToAop (hc08_reg_xa, AOP (result), 0); pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_a, needpulla); } } /*-----------------------------------------------------------------*/ /* 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 needpula = pushRegIfSurv (hc08_reg_a); bool needpulx = pushRegIfSurv (hc08_reg_x); if (offl == LSB) { loadRegFromAop (hc08_reg_xa, AOP (left), MSB24); if (sign) rmwWithReg ("asr", hc08_reg_x); else rmwWithReg ("lsr", hc08_reg_x); rmwWithReg ("ror", hc08_reg_a); storeRegToAop (hc08_reg_xa, AOP (result), MSB24); loadRegFromAop (hc08_reg_xa, AOP (left), LSB); } else if (offl == MSB16) { loadRegFromAop (hc08_reg_a, AOP (left), MSB32); if (sign) rmwWithReg ("asr", hc08_reg_a); else rmwWithReg ("lsr", hc08_reg_a); loadRegFromAop (hc08_reg_x, AOP (left), MSB24); storeRegToAop (hc08_reg_a, AOP (result), MSB24); loadRegFromAop (hc08_reg_a, AOP (left), MSB16); } rmwWithReg ("ror", hc08_reg_x); rmwWithReg ("ror", hc08_reg_a); storeRegToAop (hc08_reg_xa, AOP (result), LSB); if (offl == MSB16) { if (sign) { loadRegFromAop (hc08_reg_a, AOP (left), MSB24); storeRegSignToUpperAop (hc08_reg_a, AOP (result), MSB32, sign); } else { storeConstToAop (0, AOP (result), MSB32); } } pullOrFreeReg (hc08_reg_x, needpulx); pullOrFreeReg (hc08_reg_a, needpula); } /*-----------------------------------------------------------------*/ /* genrshFour - shift four byte by a known amount != 0 */ /*-----------------------------------------------------------------*/ static void genrshFour (operand * result, operand * left, int shCount, int sign) { bool needpulla = FALSE; bool needpullx = FALSE; /* TODO: handle cases where left == result */ D (emitcode ("; genrshFour", "")); /* if shifting more that 3 bytes */ if (shCount >= 24) { needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (left), 3); AccRsh (shCount - 24, sign); storeRegToFullAop (hc08_reg_a, AOP (result), sign); } else if (shCount >= 16) { needpulla = pushRegIfSurv (hc08_reg_a); needpullx = pushRegIfSurv (hc08_reg_x); loadRegFromAop (hc08_reg_xa, AOP (left), 2); XAccRsh (shCount - 16, sign); storeRegToFullAop (hc08_reg_xa, AOP (result), sign); } else if (shCount >= 8) { if (shCount == 1) { shiftRLong (left, MSB16, result, sign); return; } else if (shCount == 8) { needpulla = pushRegIfSurv (hc08_reg_a); transferAopAop (AOP (left), 1, AOP (result), 0); transferAopAop (AOP (left), 2, AOP (result), 1); loadRegFromAop (hc08_reg_a, AOP (left), 3); storeRegToAop (hc08_reg_a, AOP (result), 2); storeRegSignToUpperAop (hc08_reg_a, AOP (result), 3, sign); } else if (shCount == 9) { shiftRLong (left, MSB16, result, sign); return; } else { needpulla = pushRegIfSurv (hc08_reg_a); needpullx = pushRegIfSurv (hc08_reg_x); loadRegFromAop (hc08_reg_xa, AOP (left), 1); XAccRsh (shCount - 8, FALSE); storeRegToAop (hc08_reg_xa, AOP (result), 0); loadRegFromAop (hc08_reg_x, AOP (left), 3); loadRegFromConst (hc08_reg_a, 0); XAccRsh (shCount - 8, sign); accopWithAop ("ora", AOP (result), 1); storeRegToAop (hc08_reg_xa, AOP (result), 1); storeRegSignToUpperAop (hc08_reg_x, AOP (result), 3, sign); } } else { /* 1 <= shCount <= 7 */ if (shCount == 1) { shiftRLong (left, LSB, result, sign); return; } else { needpulla = pushRegIfSurv (hc08_reg_a); needpullx = pushRegIfSurv (hc08_reg_x); loadRegFromAop (hc08_reg_xa, AOP (left), 0); XAccRsh (shCount, FALSE); storeRegToAop (hc08_reg_xa, AOP (result), 0); loadRegFromAop (hc08_reg_a, AOP (left), 2); AccLsh (8 - shCount); accopWithAop ("ora", AOP (result), 1); storeRegToAop (hc08_reg_a, AOP (result), 1); loadRegFromAop (hc08_reg_xa, AOP (left), 2); XAccRsh (shCount, sign); storeRegToAop (hc08_reg_xa, AOP (result), 2); } } pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_a, needpulla); } /*-----------------------------------------------------------------*/ /* 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; D (emitcode ("; genRightShiftLiteral", "")); freeAsmop (right, NULL, ic, TRUE); aopOp (left, ic, FALSE); aopOp (result, ic, FALSE); #if VIEW_SIZE DD (emitcode ("; shift right ", "result %d, left %d", AOP_SIZE (result), AOP_SIZE (left))); #endif size = getDataSize (left); /* test the LEFT size !!! */ /* I suppose that the left size >= result size */ if (shCount == 0) { genCopy (result, left); } else if (shCount >= (size * 8)) { bool needpulla = pushRegIfSurv (hc08_reg_a); if (sign) { /* get sign in acc.7 */ loadRegFromAop (hc08_reg_a, AOP (left), size - 1); } addSign (result, LSB, sign); pullOrFreeReg (hc08_reg_a, needpulla); } 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: wassertl (0, "Invalid operand size in right shift."); break; } } freeAsmop (left, NULL, ic, TRUE); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genRightShift - generate code for right shifting */ /*-----------------------------------------------------------------*/ static void genRightShift (iCode * ic) { operand *right, *left, *result; sym_link *retype; int size, offset; symbol *tlbl, *tlbl1; char *shift; bool sign; asmop *aopResult; bool needpullcountreg; reg_info *countreg = NULL; D (emitcode ("; genRightShift", "")); /* if signed then we do it the hard way preserve the sign bit moving it inwards */ retype = getSpec (operandType (IC_RESULT (ic))); sign = !SPEC_USIGN (retype); /* 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, sign); return; } /* shift count is unknown then we have to form a loop get the loop count in X : 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 ) */ aopOp (result, ic, FALSE); aopOp (left, ic, FALSE); aopResult = AOP (result); if (sameRegs (AOP (right), AOP (result)) || regsInCommon (right, result) || IS_AOP_XA (AOP (result)) || isOperandVolatile (result, FALSE)) aopResult = forceStackedAop (AOP (result), sameRegs (AOP (left), AOP (result))); /* now move the left to the result if they are not the same */ if (IS_AOP_HX (AOP (result))) loadRegFromAop (hc08_reg_hx, AOP (left), 0); else if (IS_AOP_AX (AOP (result)) && IS_AOP_XA (AOP (left)) || IS_AOP_XA (AOP (result)) && IS_AOP_AX (AOP (left))) { pushReg (hc08_reg_x, TRUE); emitcode("tax", ""); regalloc_dry_run_cost++; pullReg (hc08_reg_a); } else if (!sameRegs (AOP (left), aopResult)) { size = AOP_SIZE (result); offset = 0; while (size--) { transferAopAop (AOP (left), offset, aopResult, offset); offset++; } } freeAsmop (left, NULL, ic, TRUE); AOP (result) = aopResult; tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); size = AOP_SIZE (result); offset = 0; tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); if (hc08_reg_x->isDead && !IS_AOP_X (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result)) && !IS_AOP_HX (AOP (result))) countreg = hc08_reg_x; else if (hc08_reg_a->isDead && !IS_AOP_A (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result))) countreg = hc08_reg_a; else if (!IS_AOP_X (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result)) && !IS_AOP_HX (AOP (result))) countreg = hc08_reg_x; else if(!IS_AOP_A (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result))) countreg = hc08_reg_a; needpullcountreg = (countreg && pushRegIfSurv (countreg)); if(countreg) { countreg->isFree = FALSE; loadRegFromAop (countreg, AOP (right), 0); } else { pushReg (hc08_reg_a, FALSE); pushReg (hc08_reg_a, TRUE); loadRegFromAop (hc08_reg_a, AOP (right), 0); emitcode ("sta", "2, s"); regalloc_dry_run_cost += 3; pullReg (hc08_reg_a); } emitcode (countreg == hc08_reg_a ? "tsta" : (countreg ? "tstx" : "tst 1, s"), ""); regalloc_dry_run_cost += (countreg ? 1 : 3); emitBranch ("beq", tlbl1); if (!regalloc_dry_run) emitLabel (tlbl); shift = sign ? "asr" : "lsr"; for (offset = size - 1; offset >= 0; offset--) { rmwWithAop (shift, AOP (result), offset); shift = "ror"; } if (!regalloc_dry_run) emitcode (countreg == hc08_reg_a ? "dbnza" : (countreg ? "dbnzx" : "dbnz 1, s"), "%05d$", labelKey2num (tlbl->key)); regalloc_dry_run_cost += (countreg ? 2 : 4); if (!regalloc_dry_run) emitLabel (tlbl1); if (!countreg) pullNull (1); else pullOrFreeReg (countreg, needpullcountreg); freeAsmop (result, NULL, ic, TRUE); freeAsmop (right, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* decodePointerOffset - decode a pointer offset operand into a */ /* literal offset and a rematerializable offset */ /*-----------------------------------------------------------------*/ static void decodePointerOffset (operand * opOffset, int * litOffset, char ** rematOffset) { *litOffset = 0; *rematOffset = NULL; if (!opOffset) return; if (IS_OP_LITERAL (opOffset)) { *litOffset = (int)operandLitValue (opOffset); } else if (IS_ITEMP (opOffset) && OP_SYMBOL (opOffset)->remat) { asmop * aop = aopForRemat (OP_SYMBOL (opOffset)); if (aop->type == AOP_LIT) *litOffset = (int) floatFromVal (aop->aopu.aop_lit); else if (aop->type == AOP_IMMD) *rematOffset = aop->aopu.aop_immd.aop_immd1; } else wassertl (0, "Pointer get/set with non-constant offset"); } /*-----------------------------------------------------------------*/ /* genUnpackBits - generates code for unpacking bits */ /*-----------------------------------------------------------------*/ static void genUnpackBits (operand * result, operand * left, operand * right, 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 */ bool needpulla = FALSE; bool needpullh = FALSE; bool needpullx = FALSE; int litOffset = 0; char * rematOffset = NULL; D (emitcode ("; genUnpackBits", "")); decodePointerOffset (right, &litOffset, &rematOffset); etype = getSpec (operandType (result)); rsize = getSize (operandType (result)); blen = SPEC_BLEN (etype); bstr = SPEC_BSTR (etype); needpulla = pushRegIfSurv (hc08_reg_a); if (!IS_AOP_HX (AOP (left))) { needpullx = pushRegIfSurv (hc08_reg_x); needpullh = pushRegIfSurv (hc08_reg_h); } /* if the operand is already in hx then we do nothing else we move the value to hx */ loadRegFromAop (hc08_reg_hx, AOP (left), 0); /* so hx now contains the address */ if (ifx && blen <= 8) { loadRegIndexed (hc08_reg_a, litOffset, rematOffset); if (blen < 8) { emitcode ("and", "#0x%02x", (((unsigned char) - 1) >> (8 - blen)) << bstr); regalloc_dry_run_cost += 2; } pullOrFreeReg (hc08_reg_h, needpullh); pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_a, needpulla); genIfxJump (ifx, "a"); return; } wassert (!ifx); /* If the bitfield length is less than a byte */ if (blen < 8) { loadRegIndexed (hc08_reg_a, litOffset, rematOffset); AccRsh (bstr, FALSE); emitcode ("and", "#0x%02x", ((unsigned char) - 1) >> (8 - blen)); regalloc_dry_run_cost += 2; if (!SPEC_USIGN (etype)) { /* signed bitfield */ symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("bit", "#0x%02x", 1 << (blen - 1)); if (!regalloc_dry_run) emitcode ("beq", "%05d$", labelKey2num (tlbl->key)); emitcode ("ora", "#0x%02x", (unsigned char) (0xff << blen)); regalloc_dry_run_cost += 6; if (!regalloc_dry_run) emitLabel (tlbl); } storeRegToAop (hc08_reg_a, AOP (result), 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) { loadRegIndexed (hc08_reg_a, litOffset, rematOffset); if (rlen > 8 && AOP_TYPE (result) == AOP_REG) pushReg (hc08_reg_a, TRUE); else storeRegToAop (hc08_reg_a, AOP (result), offset); offset++; if (rlen > 8) { litOffset++; } } /* Handle the partial byte at the end */ if (rlen) { loadRegIndexed (hc08_reg_a, litOffset, rematOffset); emitcode ("and", "#0x%02x", ((unsigned char) - 1) >> (8 - rlen)); regalloc_dry_run_cost += 3; if (!SPEC_USIGN (etype)) { /* signed bitfield */ symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("bit", "#0x%02x", 1 << (rlen - 1)); if (!regalloc_dry_run) emitcode ("beq", "%05d$", labelKey2num (tlbl->key)); emitcode ("ora", "#0x%02x", (unsigned char) (0xff << rlen)); regalloc_dry_run_cost += 6; if (!regalloc_dry_run) emitLabel (tlbl); } storeRegToAop (hc08_reg_a, AOP (result), offset++); } if (blen > 8 && AOP_TYPE (result) == AOP_REG) { pullReg (AOP (result)->aopu.aop_reg[0]); } finish: if (offset < rsize) { rsize -= offset; if (SPEC_USIGN (etype)) { while (rsize--) storeConstToAop (0, AOP (result), offset++); } else { /* signed bitfield: sign extension with 0x00 or 0xff */ emitcode ("rola", ""); emitcode ("clra", ""); emitcode ("sbc", zero); regalloc_dry_run_cost += 4; while (rsize--) storeRegToAop (hc08_reg_a, AOP (result), offset++); } } pullOrFreeReg (hc08_reg_h, needpullh); pullOrFreeReg (hc08_reg_x, needpullx); pullOrFreeReg (hc08_reg_a, needpulla); } /*-----------------------------------------------------------------*/ /* genUnpackBitsImmed - generates code for unpacking bits */ /*-----------------------------------------------------------------*/ static void genUnpackBitsImmed (operand * left, operand *right, operand * result, iCode * ic, iCode * ifx) { int size; int offset = 0; /* result byte offset */ int litOffset = 0; char * rematOffset = NULL; 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 */ asmop *derefaop; bool delayed_a = FALSE; bool assigned_a = FALSE; bool needpulla = FALSE; D (emitcode ("; genUnpackBitsImmed", "")); decodePointerOffset (right, &litOffset, &rematOffset); wassert (rematOffset==NULL); aopOp (result, ic, TRUE); size = AOP_SIZE (result); derefaop = aopDerefAop (AOP (left), litOffset); freeAsmop (left, NULL, ic, TRUE); derefaop->size = size; etype = getSpec (operandType (result)); rsize = getSize (operandType (result)); blen = SPEC_BLEN (etype); bstr = SPEC_BSTR (etype); needpulla = pushRegIfSurv (hc08_reg_a); /* if the bitfield is a single bit in the direct page */ if (blen == 1 && derefaop->type == AOP_DIR) { if (!ifx && bstr) { symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); loadRegFromConst (hc08_reg_a, 0); if (!regalloc_dry_run) emitcode ("brclr", "#%d,%s,%05d$", bstr, aopAdrStr (derefaop, 0, FALSE), labelKey2num ((tlbl->key))); regalloc_dry_run_cost += 3; if (SPEC_USIGN (etype)) rmwWithReg ("inc", hc08_reg_a); else rmwWithReg ("dec", hc08_reg_a); if (!regalloc_dry_run) emitLabel (tlbl); storeRegToAop (hc08_reg_a, AOP (result), offset); if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX) assigned_a = TRUE; hc08_freeReg (hc08_reg_a); offset++; goto finish; } else if (ifx) { symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); symbol *jlbl; char *inst; if (IC_TRUE (ifx)) { jlbl = IC_TRUE (ifx); inst = "brclr"; } else { jlbl = IC_FALSE (ifx); inst = "brset"; } if (!regalloc_dry_run) emitcode (inst, "#%d,%s,%05d$", bstr, aopAdrStr (derefaop, 0, FALSE), labelKey2num ((tlbl->key))); regalloc_dry_run_cost += 3; emitBranch ("jmp", jlbl); if (!regalloc_dry_run) emitLabel (tlbl); ifx->generated = 1; offset++; goto finish; } } /* If the bitfield length is less than a byte */ if (blen < 8) { loadRegFromAop (hc08_reg_a, derefaop, 0); if (!ifx) { AccRsh (bstr, FALSE); emitcode ("and", "#0x%02x", ((unsigned char) - 1) >> (8 - blen)); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_a, FALSE); if (!SPEC_USIGN (etype)) { /* signed bitfield */ symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("bit", "#0x%02x", 1 << (blen - 1)); if (!regalloc_dry_run) emitcode ("beq", "%05d$", labelKey2num (tlbl->key)); emitcode ("ora", "#0x%02x", (unsigned char) (0xff << blen)); regalloc_dry_run_cost += 6; if (!regalloc_dry_run) emitLabel (tlbl); } storeRegToAop (hc08_reg_a, AOP (result), offset); if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX) assigned_a = TRUE; } else { emitcode ("and", "#0x%02x", (((unsigned char) - 1) >> (8 - blen)) << bstr); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_a, FALSE); } 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) { if (assigned_a && !delayed_a) { pushReg (hc08_reg_a, TRUE); delayed_a = TRUE; } loadRegFromAop (hc08_reg_a, derefaop, size - offset - 1); if (!ifx) { storeRegToAop (hc08_reg_a, AOP (result), offset); if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX) assigned_a = TRUE; } else { emitcode ("tsta", ""); regalloc_dry_run_cost++; } offset++; } /* Handle the partial byte at the end */ if (rlen) { if (assigned_a && !delayed_a) { pushReg (hc08_reg_a, TRUE); delayed_a = TRUE; } loadRegFromAop (hc08_reg_a, derefaop, size - offset - 1); emitcode ("and", "#0x%02x", ((unsigned char) - 1) >> (8 - rlen)); regalloc_dry_run_cost += 2; if (!SPEC_USIGN (etype)) { /* signed bitfield */ symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); emitcode ("bit", "#0x%02x", 1 << (rlen - 1)); if (!regalloc_dry_run) emitcode ("beq", "%05d$", labelKey2num (tlbl->key)); emitcode ("ora", "#0x%02x", (unsigned char) (0xff << rlen)); regalloc_dry_run_cost += 6; if (!regalloc_dry_run) emitLabel (tlbl); } storeRegToAop (hc08_reg_a, AOP (result), offset); if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX) assigned_a = TRUE; offset++; } finish: if (offset < rsize) { rsize -= offset; if (SPEC_USIGN (etype)) { while (rsize--) storeConstToAop (0, AOP (result), offset++); } else { if (assigned_a && !delayed_a) { pushReg (hc08_reg_a, TRUE); delayed_a = TRUE; } /* signed bitfield: sign extension with 0x00 or 0xff */ emitcode ("rola", ""); emitcode ("clra", ""); emitcode ("sbc", zero); regalloc_dry_run_cost += 4; while (rsize--) storeRegToAop (hc08_reg_a, AOP (result), offset++); } } freeAsmop (NULL, derefaop, ic, TRUE); freeAsmop (result, NULL, ic, TRUE); if (ifx && !ifx->generated) { genIfxJump (ifx, "a"); } if (delayed_a) pullReg (hc08_reg_a); pullOrFreeReg (hc08_reg_a, needpulla); } /*-----------------------------------------------------------------*/ /* genDataPointerGet - generates code when ptr offset is known */ /*-----------------------------------------------------------------*/ static void genDataPointerGet (operand * left, operand * right, operand * result, iCode * ic, iCode * ifx) { int size; int litOffset = 0; char * rematOffset = NULL; asmop *derefaop; bool needpulla = FALSE; D (emitcode ("; genDataPointerGet", "")); decodePointerOffset (right, &litOffset, &rematOffset); wassert (rematOffset==NULL); aopOp (result, ic, TRUE); size = AOP_SIZE (result); derefaop = aopDerefAop (AOP (left), litOffset); freeAsmop (left, NULL, ic, TRUE); derefaop->size = size; if (ifx) needpulla = pushRegIfSurv (hc08_reg_a); if (IS_AOP_HX (AOP (result))) loadRegFromAop (hc08_reg_hx, derefaop, 0); else while (size--) { if (!ifx) transferAopAop (derefaop, size, AOP (result), size); else loadRegFromAop (hc08_reg_a, derefaop, size); } freeAsmop (NULL, derefaop, ic, TRUE); freeAsmop (result, NULL, ic, TRUE); pullOrFreeReg (hc08_reg_a, needpulla); if (ifx && !ifx->generated) { genIfxJump (ifx, "a"); } } /*-----------------------------------------------------------------*/ /* genPointerGet - generate code for pointer get */ /*-----------------------------------------------------------------*/ static void genPointerGet (iCode * ic, iCode * pi, iCode * ifx) { operand *left = IC_LEFT (ic); operand *right = IC_RIGHT (ic); operand *result = IC_RESULT (ic); int size, offset, xoffset; int litOffset = 0; char * rematOffset = NULL; sym_link *retype = getSpec (operandType (result)); bool needpulla = FALSE; bool needpullh = FALSE; bool needpullx = FALSE; bool vol = FALSE; D (emitcode ("; genPointerGet", "")); if ((size = getSize (operandType (result))) > 1) ifx = NULL; aopOp (left, ic, FALSE); /* if left is rematerialisable */ if (AOP_TYPE (left) == AOP_IMMD || AOP_TYPE (left) == AOP_LIT) { /* if result is not bit variable type */ if (!IS_BITVAR (retype)) { genDataPointerGet (left, right, result, ic, ifx); return; } else { genUnpackBitsImmed (left, right, result, ic, ifx); return; } } aopOp (result, ic, FALSE); /* if bit then unpack */ if (IS_BITVAR (retype)) { /* hasInchc08() will be false for bitfields, so no need */ /* to consider post-increment in this case. */ genUnpackBits (result, left, right, ifx); goto release; } /* This was marking the result registers dead, but I think */ /* it should be marking the pointer (left op) registers dead. */ /* EEP - 5 Jan 2013 */ if (AOP_TYPE (left) == AOP_REG && pi) { int i; for (i = 0; i < AOP_SIZE (left); i++) AOP (left)->aopu.aop_reg[i]->isDead = TRUE; } if (!IS_AOP_HX (AOP (left))) { needpullx = pushRegIfSurv (hc08_reg_x); needpullh = pushRegIfSurv (hc08_reg_h); } /* if the operand is already in hx then we do nothing else we move the value to hx */ loadRegFromAop (hc08_reg_hx, AOP (left), 0); /* so hx now contains the address */ decodePointerOffset (right, &litOffset, &rematOffset); if (AOP_TYPE (result) == AOP_REG) { if (pi) aopOp (IC_RESULT (pi), pi, FALSE); if (AOP_SIZE (result) == 1) { if (!pi) { if (IS_AOP_H (AOP (result))) hc08_freeReg (hc08_reg_x); loadRegIndexed (AOP (result)->aopu.aop_reg[0], litOffset, rematOffset); } else { needpulla = pushRegIfSurv (hc08_reg_a); loadRegIndexed (hc08_reg_a, litOffset, rematOffset); hc08_useReg (hc08_reg_a); emitcode ("aix", "#%d", size); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_hx, FALSE); hc08_freeReg (hc08_reg_x); storeRegToAop (hc08_reg_hx, AOP (IC_RESULT (pi)), 0); pi->generated = 1; storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), 0); } } else { if (pi || (IS_AOP_XA (AOP (IC_RESULT (ic))) && vol)) { loadRegIndexed (hc08_reg_a, litOffset, rematOffset); pushReg (hc08_reg_a, FALSE); loadRegIndexed (hc08_reg_a, litOffset+1, rematOffset); hc08_useReg (hc08_reg_a); if (pi) { emitcode ("aix", "#%d", size); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_hx, FALSE); hc08_freeReg (hc08_reg_x); storeRegToAop (hc08_reg_hx, AOP (IC_RESULT (pi)), 0); pi->generated = 1; } storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), 0); pullReg (AOP (IC_RESULT (ic))->aopu.aop_reg[1]); } else { if (IS_AOP_HX (AOP (IC_RESULT (ic)))) { hc08_freeReg (hc08_reg_x); loadRegIndexed (hc08_reg_hx, litOffset, rematOffset); } else if (IS_AOP_XA (AOP (IC_RESULT (ic)))) { loadRegIndexed (hc08_reg_a, litOffset+1, rematOffset); loadRegIndexed (hc08_reg_x, litOffset, rematOffset); } else if (IS_AOP_AX (AOP (IC_RESULT (ic)))) { loadRegIndexed (hc08_reg_a, litOffset, rematOffset); loadRegIndexed (hc08_reg_x, litOffset+1, rematOffset); } } } if (pi) { freeAsmop (IC_RESULT (pi), NULL, pi, TRUE); } } else if (!pi && !ifx && size == 2 && IS_S08 && hc08_reg_x->isDead && hc08_reg_h->isDead && AOP_TYPE (result) == AOP_EXT) /* Todo: Use this for bigger sizes, too */ { loadRegIndexed (hc08_reg_hx, litOffset, rematOffset); storeRegToAop (hc08_reg_hx, AOP (result), 0); } else if (!pi && !ifx && size == 1 && hc08_reg_x->isDead && !hc08_reg_a->isDead) { loadRegIndexed (hc08_reg_x, litOffset, rematOffset); storeRegToAop (hc08_reg_x, AOP (result), 0); } else { offset = size - 1; needpulla = pushRegIfSurv (hc08_reg_a); while (size--) { xoffset = litOffset + (AOP_SIZE (result) - offset - 1); loadRegIndexed (hc08_reg_a, xoffset, rematOffset); if (!ifx) storeRegToAop (hc08_reg_a, AOP (result), offset); offset--; } } release: size = AOP_SIZE (result); freeAsmop (left, NULL, ic, TRUE); freeAsmop (result, NULL, ic, TRUE); if (pi && !pi->generated) { emitcode ("aix", "#%d", size); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_hx, FALSE); aopOp (IC_RESULT (pi), pi, FALSE); storeRegToAop (hc08_reg_hx, AOP (IC_RESULT (pi)), 0); if (ifx && AOP_TYPE (IC_RESULT (pi)) != AOP_REG) { /* Ensure Z/NZ flag is correct since storeRegToAop() */ /* disrupts the flag bits when storing to memory. */ emitcode ("tsta", ""); regalloc_dry_run_cost++; } freeAsmop (IC_RESULT (pi), NULL, pi, TRUE); pi->generated = 1; } pullOrFreeReg (hc08_reg_a, needpulla); pullOrFreeReg (hc08_reg_h, needpullh); pullOrFreeReg (hc08_reg_x, needpullx); if (ifx && !ifx->generated) { genIfxJump (ifx, "a"); } } /*-----------------------------------------------------------------*/ /* genPackBits - generates code for packed bit storage */ /*-----------------------------------------------------------------*/ static void genPackBits (operand * result, operand * left, sym_link * etype, operand * right) { 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 */ int litOffset = 0; char *rematOffset = NULL; bool needpulla; D (emitcode ("; genPackBits", "")); decodePointerOffset (left, &litOffset, &rematOffset); blen = SPEC_BLEN (etype); bstr = SPEC_BSTR (etype); needpulla = pushRegIfSurv (hc08_reg_a); if (AOP_TYPE (right) == AOP_REG) { /* Not optimal, but works for any register sources. */ /* Just push the source values onto the stack and */ /* pull them off any needed. Better optimzed would */ /* be to do some of the shifting/masking now and */ /* push the intermediate result. */ if (blen > 8) pushReg (AOP (right)->aopu.aop_reg[1], TRUE); pushReg (AOP (right)->aopu.aop_reg[0], TRUE); } loadRegFromAop (hc08_reg_hx, AOP (result), 0); /* 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; loadRegIndexed (hc08_reg_a, litOffset, rematOffset); if ((mask | litval) != 0xff) { emitcode ("and", "#0x%02x", mask); regalloc_dry_run_cost += 2; } if (litval) { emitcode ("ora", "#0x%02x", litval); regalloc_dry_run_cost += 2; } hc08_dirtyReg (hc08_reg_a, FALSE); storeRegIndexed (hc08_reg_a, litOffset, rematOffset); pullOrFreeReg (hc08_reg_a, needpulla); return; } /* Case with a bitfield length < 8 and arbitrary source */ if (AOP_TYPE (right) == AOP_REG) pullReg (hc08_reg_a); else loadRegFromAop (hc08_reg_a, AOP (right), 0); /* shift and mask source value */ AccLsh (bstr); emitcode ("and", "#0x%02x", (~mask) & 0xff); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_a, FALSE); pushReg (hc08_reg_a, TRUE); loadRegIndexed (hc08_reg_a, litOffset, rematOffset); emitcode ("and", "#0x%02x", mask); emitcode ("ora", "1,s"); regalloc_dry_run_cost += 5; storeRegIndexed (hc08_reg_a, litOffset, rematOffset); pullReg (hc08_reg_a); pullOrFreeReg (hc08_reg_a, needpulla); 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) { if (!litOffset && !rematOffset && AOP (right)->type == AOP_DIR) { emitcode ("mov", "%s,x+", aopAdrStr (AOP (right), offset, FALSE)); regalloc_dry_run_cost += 2; litOffset--; } else { if (AOP_TYPE (right) == AOP_REG) pullReg (hc08_reg_a); else loadRegFromAop (hc08_reg_a, AOP (right), offset); storeRegIndexed (hc08_reg_a, litOffset+offset, rematOffset); } offset++; } /* 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; loadRegIndexed (hc08_reg_a, litOffset+offset, rematOffset); if ((mask | litval) != 0xff) { emitcode ("and", "#0x%02x", mask); regalloc_dry_run_cost += 2; } if (litval) { emitcode ("ora", "#0x%02x", litval); regalloc_dry_run_cost += 2; } hc08_dirtyReg (hc08_reg_a, FALSE); storeRegIndexed (hc08_reg_a, litOffset+offset, rematOffset); pullOrFreeReg (hc08_reg_a, needpulla); return; } /* Case with partial byte and arbitrary source */ if (AOP_TYPE (right) == AOP_REG) pullReg (hc08_reg_a); else loadRegFromAop (hc08_reg_a, AOP (right), offset); emitcode ("and", "#0x%02x", (~mask) & 0xff); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_a, FALSE); pushReg (hc08_reg_a, TRUE); loadRegIndexed(hc08_reg_a, litOffset+offset, rematOffset); emitcode ("and", "#0x%02x", mask); emitcode ("ora", "1,s"); regalloc_dry_run_cost += 5; storeRegIndexed (hc08_reg_a, litOffset+offset, rematOffset); pullReg (hc08_reg_a); } pullOrFreeReg (hc08_reg_a, needpulla); } /*-----------------------------------------------------------------*/ /* genPackBitsImmed - generates code for packed bit storage */ /*-----------------------------------------------------------------*/ static void genPackBitsImmed (operand * result, operand * left, sym_link * etype, operand * right, iCode * ic) { asmop *derefaop; int size; 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 */ bool needpulla; int litOffset = 0; char *rematOffset = NULL; D (emitcode ("; genPackBitsImmed", "")); blen = SPEC_BLEN (etype); bstr = SPEC_BSTR (etype); aopOp (right, ic, FALSE); size = AOP_SIZE (right); decodePointerOffset (left, &litOffset, &rematOffset); wassert (!rematOffset); derefaop = aopDerefAop (AOP (result), litOffset); freeAsmop (result, NULL, ic, TRUE); derefaop->size = size; /* if the bitfield is a single bit in the direct page */ if (blen == 1 && derefaop->type == AOP_DIR) { if (AOP_TYPE (right) == AOP_LIT) { litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); emitcode ((litval & 1) ? "bset" : "bclr", "#%d,%s", bstr, aopAdrStr (derefaop, 0, FALSE)); regalloc_dry_run_cost += 2; } else { symbol *tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); symbol *tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (right), 0); emitcode ("lsra", ""); regalloc_dry_run_cost++; emitBranch ("bcs", tlbl1); emitcode ("bclr", "#%d,%s", bstr, aopAdrStr (derefaop, 0, FALSE)); regalloc_dry_run_cost += 2; emitBranch ("bra", tlbl2); if (!regalloc_dry_run) emitLabel (tlbl1); emitcode ("bset", "#%d,%s", bstr, aopAdrStr (derefaop, 0, FALSE)); regalloc_dry_run_cost += 2; if (!regalloc_dry_run) emitLabel (tlbl2); pullOrFreeReg (hc08_reg_a, needpulla); } goto release; } /* 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; needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, derefaop, 0); if ((mask | litval) != 0xff) { emitcode ("and", "#0x%02x", mask); regalloc_dry_run_cost += 2; } if (litval) { emitcode ("ora", "#0x%02x", litval); regalloc_dry_run_cost += 2; } hc08_dirtyReg (hc08_reg_a, FALSE); storeRegToAop (hc08_reg_a, derefaop, 0); pullOrFreeReg (hc08_reg_a, needpulla); goto release; } /* Case with a bitfield length < 8 and arbitrary source */ needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (right), 0); /* shift and mask source value */ AccLsh (bstr); emitcode ("and", "#0x%02x", (~mask) & 0xff); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_a, FALSE); pushReg (hc08_reg_a, TRUE); loadRegFromAop (hc08_reg_a, derefaop, 0); emitcode ("and", "#0x%02x", mask); emitcode ("ora", "1,s"); regalloc_dry_run_cost += 5; storeRegToAop (hc08_reg_a, derefaop, 0); pullReg (hc08_reg_a); pullOrFreeReg (hc08_reg_a, needpulla); goto release; } /* 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) { transferAopAop (AOP (right), offset, derefaop, size - offset - 1); offset++; } /* 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; needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, derefaop, size - offset - 1); if ((mask | litval) != 0xff) { emitcode ("and", "#0x%02x", mask); regalloc_dry_run_cost += 2; } if (litval) { emitcode ("ora", "#0x%02x", litval); regalloc_dry_run_cost += 2; } hc08_dirtyReg (hc08_reg_a, FALSE); storeRegToAop (hc08_reg_a, derefaop, size - offset - 1); hc08_dirtyReg (hc08_reg_a, FALSE); pullOrFreeReg (hc08_reg_a, needpulla); goto release; } /* Case with partial byte and arbitrary source */ needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (right), offset); emitcode ("and", "#0x%02x", (~mask) & 0xff); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_a, FALSE); pushReg (hc08_reg_a, TRUE); loadRegFromAop (hc08_reg_a, derefaop, size - offset - 1); emitcode ("and", "#0x%02x", mask); emitcode ("ora", "1,s"); regalloc_dry_run_cost += 5; storeRegToAop (hc08_reg_a, derefaop, size - offset - 1); pullReg (hc08_reg_a); pullOrFreeReg (hc08_reg_a, needpulla); } hc08_freeReg (hc08_reg_a); release: freeAsmop (right, NULL, ic, TRUE); freeAsmop (NULL, derefaop, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genDataPointerSet - remat pointer to data space */ /*-----------------------------------------------------------------*/ static void genDataPointerSet (operand * left, operand * right, operand * result, iCode * ic) { int size; asmop *derefaop; int litOffset = 0; char *rematOffset = NULL; D (emitcode ("; genDataPointerSet", "")); aopOp (right, ic, FALSE); size = AOP_SIZE (right); decodePointerOffset (left, &litOffset, &rematOffset); wassert (!rematOffset); derefaop = aopDerefAop (AOP (result), litOffset); freeAsmop (result, NULL, ic, TRUE); derefaop->size = size; if (IS_AOP_HX (AOP (right))) { storeRegToAop (hc08_reg_hx, derefaop, 0); } else { while (size--) { transferAopAop (AOP (right), size, derefaop, size); } } freeAsmop (right, NULL, ic, TRUE); freeAsmop (NULL, derefaop, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genPointerSet - stores the value into a pointer location */ /*-----------------------------------------------------------------*/ static void genPointerSet (iCode * ic, iCode * pi) { operand *left = IC_LEFT (ic); operand *right = IC_RIGHT (ic); operand *result = IC_RESULT (ic); int size, offset; sym_link *retype = getSpec (operandType (right)); sym_link *letype = getSpec (operandType (result)); bool needpulla = FALSE; bool needpullx = FALSE; bool needpullh = FALSE; bool vol = FALSE; int litOffset = 0; char *rematOffset = NULL; D (emitcode ("; genPointerSet", "")); aopOp (result, ic, FALSE); /* if the result is rematerializable */ if (AOP_TYPE (result) == AOP_IMMD || AOP_TYPE (result) == AOP_LIT) { if (!IS_BITVAR (retype) && !IS_BITVAR (letype)) { genDataPointerSet (left, right, result, ic); return; } else { genPackBitsImmed (result, left, (IS_BITVAR (retype) ? retype : letype), right, ic); return; } } if (AOP_TYPE (result) == AOP_REG && pi) { int i; for (i = 0; i < AOP_SIZE (result); i++) AOP (result)->aopu.aop_reg[i]->isDead = TRUE; } needpullx = pushRegIfSurv (hc08_reg_x); needpullh = pushRegIfSurv (hc08_reg_h); aopOp (right, ic, FALSE); size = AOP_SIZE (right); /* if bit then pack */ if (IS_BITVAR (retype) || IS_BITVAR (letype)) { genPackBits (result, left, (IS_BITVAR (retype) ? retype : letype), right); } else if (AOP_TYPE (right) == AOP_REG) { decodePointerOffset (left, &litOffset, &rematOffset); if (size == 1) { if (!IS_AOP_A (AOP (right))) needpulla = pushRegIfSurv (hc08_reg_a); loadRegHXAfromAop(AOP (result), 1, AOP (result), 0, AOP (right), 0 ); storeRegIndexed (hc08_reg_a, litOffset, rematOffset); } else if (IS_AOP_XA (AOP (right)) || IS_AOP_HX (AOP (right))) { if (vol) { if (AOP (right)->aopu.aop_reg[0]->rIdx != A_IDX) needpulla = pushRegIfSurv (hc08_reg_a); pushReg (AOP (right)->aopu.aop_reg[0], TRUE); pushReg (AOP (right)->aopu.aop_reg[1], TRUE); loadRegFromAop (hc08_reg_hx, AOP (result), 0); pullReg (hc08_reg_a); storeRegIndexed (hc08_reg_a, litOffset, rematOffset); pullReg (hc08_reg_a); storeRegIndexed (hc08_reg_a, litOffset+1, rematOffset); } else { needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_a, AOP (right), 0); pushReg (AOP (right)->aopu.aop_reg[1], TRUE); loadRegFromAop (hc08_reg_hx, AOP (result), 0); storeRegIndexed (hc08_reg_a, litOffset+1, rematOffset); pullReg (hc08_reg_a); storeRegIndexed (hc08_reg_a, litOffset, rematOffset); hc08_freeReg (hc08_reg_a); } } else if (IS_AOP_AX (AOP (right))) { needpulla = pushRegIfSurv (hc08_reg_a); pushReg (hc08_reg_x, TRUE); loadRegFromAop (hc08_reg_hx, AOP (result), 0); storeRegIndexed (hc08_reg_a, litOffset, rematOffset); pullReg (hc08_reg_a); /* original X value */ storeRegIndexed (hc08_reg_a, litOffset+1, rematOffset); hc08_freeReg (hc08_reg_a); } else { wassertl (0, "bad source regs in genPointerSet()"); } } else { decodePointerOffset (left, &litOffset, &rematOffset); needpulla = pushRegIfSurv (hc08_reg_a); loadRegFromAop (hc08_reg_hx, AOP (result), 0); offset = size; while (offset--) { loadRegFromAop (hc08_reg_a, AOP (right), offset); storeRegIndexed (hc08_reg_a, litOffset + size - offset - 1, rematOffset); hc08_freeReg (hc08_reg_a); } } freeAsmop (result, NULL, ic, TRUE); freeAsmop (right, NULL, ic, TRUE); if (pi) { aopOp (IC_RESULT (pi), pi, FALSE); emitcode ("aix", "#%d", size); regalloc_dry_run_cost += 2; hc08_dirtyReg (hc08_reg_hx, FALSE); storeRegToAop (hc08_reg_hx, AOP (IC_RESULT (pi)), 0); freeAsmop (IC_RESULT (pi), NULL, pi, TRUE); pi->generated = 1; } pullOrFreeReg (hc08_reg_a, needpulla); pullOrFreeReg (hc08_reg_h, needpullh); pullOrFreeReg (hc08_reg_x, needpullx); } /*-----------------------------------------------------------------*/ /* genIfx - generate code for Ifx statement */ /*-----------------------------------------------------------------*/ static void genIfx (iCode * ic, iCode * popIc) { operand *cond = IC_COND (ic); D (emitcode ("; genIfx", "")); aopOp (cond, ic, FALSE); /* If the condition is a literal, we can just do an unconditional */ /* branch or no branch */ if (AOP_TYPE (cond) == AOP_LIT) { unsigned long long lit = ullFromVal (AOP (cond)->aopu.aop_lit); freeAsmop (cond, NULL, ic, TRUE); /* if there was something to be popped then do it */ if (popIc) genIpop (popIc); if (lit) { if (IC_TRUE (ic)) emitBranch ("jmp", IC_TRUE (ic)); } else { if (IC_FALSE (ic)) emitBranch ("jmp", IC_FALSE (ic)); } ic->generated = 1; return; } /* evaluate the operand */ if (AOP_TYPE (cond) != AOP_CRY) asmopToBool (AOP (cond), FALSE); /* the result is now in the z flag bit */ freeAsmop (cond, NULL, ic, TRUE); /* if there was something to be popped then do it */ if (popIc) genIpop (popIc); genIfxJump (ic, "a"); ic->generated = 1; } /*-----------------------------------------------------------------*/ /* genAddrOf - generates code for address of */ /*-----------------------------------------------------------------*/ static void genAddrOf (iCode * ic) { symbol *sym = OP_SYMBOL (IC_LEFT (ic)); int size, offset; bool needpullx, needpullh; 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) { needpullx = pushRegIfSurv (hc08_reg_x); needpullh = pushRegIfSurv (hc08_reg_h); /* if it has an offset then we need to compute it */ offset = _G.stackOfs + _G.stackPushes + sym->stack; hc08_useReg (hc08_reg_hx); emitcode ("tsx", ""); hc08_dirtyReg (hc08_reg_hx, FALSE); regalloc_dry_run_cost++; while (offset > 127) { emitcode ("aix", "#127"); regalloc_dry_run_cost += 2; offset -= 127; } while (offset < -128) { emitcode ("aix", "#-128"); regalloc_dry_run_cost += 2; offset += 128; } if (offset) { emitcode ("aix", "#%d", offset); regalloc_dry_run_cost += 2; } storeRegToFullAop (hc08_reg_hx, AOP (IC_RESULT (ic)), FALSE); pullOrFreeReg (hc08_reg_h, needpullh); pullOrFreeReg (hc08_reg_x, needpullx); goto release; } /* object not on stack then we need the name */ size = AOP_SIZE (IC_RESULT (ic)); offset = 0; while (size--) { char s[SDCC_NAME_MAX + 10]; switch (offset) { case 0: sprintf (s, "#%s", sym->rname); break; case 1: sprintf (s, "#>%s", sym->rname); break; default: sprintf (s, "#(%s >> %d)", sym->rname, offset * 8); } storeImmToAop (s, AOP (IC_RESULT (ic)), offset++); } release: freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genAssignLit - Try to generate code for literal assignment. */ /* result and right should already be asmOped */ /*-----------------------------------------------------------------*/ static bool genAssignLit (operand * result, operand * right) { char assigned[8]; unsigned char value[sizeof(assigned)]; char dup[sizeof(assigned)]; int size; int offset,offset2; int dups,multiples; bool needpula = FALSE; bool canUseHX = TRUE; int remaining; /* Make sure this is a literal assignment */ if (AOP_TYPE (right) != AOP_LIT) return FALSE; /* The general case already handles register assignment well */ if (AOP_TYPE (result) == AOP_REG) return FALSE; /* Some hardware registers require MSB to LSB assignment order */ /* so don't optimize the assignment order if volatile */ if (isOperandVolatile (result, FALSE)) return FALSE; /* Make sure the assignment is not larger than we can handle */ size = AOP_SIZE (result); if (size > sizeof(assigned)) return FALSE; for (offset=0; offsetaopu.aop_lit, offset); } if ((AOP_TYPE (result) != AOP_DIR ) && IS_HC08) canUseHX = FALSE; if (canUseHX) { /* Assign words that are already in HX */ for (offset=size-2; offset>=0; offset -= 2) { if (assigned[offset] || assigned[offset+1]) continue; if (hc08_reg_hx->isLitConst && hc08_reg_hx->litConst == ((value[offset+1] << 8) + value[offset])) { storeRegToAop (hc08_reg_hx, AOP (result), offset); assigned[offset] = 1; assigned[offset+1] = 1; } } } if (!(hc08_reg_h->isDead && hc08_reg_x->isDead)) canUseHX = FALSE; if (canUseHX && (size>=2)) { /* Assign whatever reamains to be assigned */ for (offset=size-2; offset>=0; offset -= 2) { if (assigned[offset] && assigned[offset+1]) continue; loadRegFromConst (hc08_reg_hx, (value[offset+1] << 8) + value[offset]); storeRegToAop (hc08_reg_hx, AOP (result), offset); assigned[offset] = 1; assigned[offset+1] = 1; } return TRUE; } remaining = size; for (offset=0; offset 2) aopOpExtToIdx (AOP (result), NULL, NULL); /* Assign bytes that are already in A and/or X */ for (offset=size-1; offset>=0; offset--) { if (assigned[offset]) continue; if ((hc08_reg_a->isLitConst && hc08_reg_a->litConst == value[offset]) || (hc08_reg_x->isLitConst && hc08_reg_x->litConst == value[offset])) { storeConstToAop (value[offset], AOP (result), offset); assigned[offset] = 1; } } /* Consider bytes that appear multiple times */ multiples = 0; for (offset=size-1; offset>=0; offset--) { if (assigned[offset] || dup[offset]) continue; dups = 0; for (offset2=offset-1; offset2>=0; offset2--) if (value[offset2] == value[offset]) { dup[offset] = 1; dups++; } if (dups) multiples += (dups+1); } /* Assign bytes that appear multiple times if the register cost */ /* isn't too high. */ if (multiples > 2 || (multiples && !hc08_reg_a->isDead)) { needpula = pushRegIfSurv (hc08_reg_a); for (offset=size-1; offset>=0; offset--) { if (assigned[offset]) continue; if (dup[offset]) { loadRegFromConst (hc08_reg_a, value[offset]); for (offset2=offset; offset2>=0; offset2--) if (!assigned[offset2] && value[offset2] == value[offset]) { storeRegToAop (hc08_reg_a, AOP (result), offset2); assigned[offset2] = 1; } hc08_freeReg (hc08_reg_a); } } } /* Assign whatever remains to be assigned */ for (offset=size-1; offset>=0; offset--) { if (assigned[offset]) continue; storeConstToAop (value[offset], AOP (result), offset); } if (needpula) pullReg (hc08_reg_a); return TRUE; } /*-----------------------------------------------------------------*/ /* genAssign - generate code for assignment */ /*-----------------------------------------------------------------*/ static void genAssign (iCode * ic) { operand *result, *right; D (emitcode ("; genAssign", "")); result = IC_RESULT (ic); right = IC_RIGHT (ic); aopOp (right, ic, FALSE); aopOp (result, ic, TRUE); if (!genAssignLit (result, right)) genCopy (result, right); freeAsmop (right, NULL, ic, TRUE); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genJumpTab - generates code for jump table */ /*-----------------------------------------------------------------*/ static void genJumpTab (iCode * ic) { symbol *jtab; symbol *jtablo = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); symbol *jtabhi = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); D (emitcode ("; genJumpTab", "")); aopOp (IC_JTCOND (ic), ic, FALSE); if (hc08_reg_x->isFree && hc08_reg_h->isFree) { /* get the condition into x */ loadRegFromAop (hc08_reg_x, AOP (IC_JTCOND (ic)), 0); freeAsmop (IC_JTCOND (ic), NULL, ic, TRUE); loadRegFromConst (hc08_reg_h, 0); if (!regalloc_dry_run) { emitcode ("lda", "%05d$,x", labelKey2num (jtabhi->key)); emitcode ("ldx", "%05d$,x", labelKey2num (jtablo->key)); } regalloc_dry_run_cost += 6; transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); emitcode ("jmp", ",x"); regalloc_dry_run_cost++; hc08_dirtyReg (hc08_reg_a, TRUE); hc08_dirtyReg (hc08_reg_hx, TRUE); } else { adjustStack (-2); pushReg (hc08_reg_hx, TRUE); /* get the condition into x */ loadRegFromAop (hc08_reg_x, AOP (IC_JTCOND (ic)), 0); freeAsmop (IC_JTCOND (ic), NULL, ic, TRUE); loadRegFromConst (hc08_reg_h, 0); if (!regalloc_dry_run) emitcode ("lda", "%05d$,x", labelKey2num (jtabhi->key)); emitcode ("sta", "3,s"); if (!regalloc_dry_run) emitcode ("lda", "%05d$,x", labelKey2num (jtablo->key)); emitcode ("sta", "4,s"); regalloc_dry_run_cost += 12; pullReg (hc08_reg_hx); emitcode ("rts", ""); regalloc_dry_run_cost++; _G.stackPushes -= 2; updateCFA (); } /* now generate the jump labels */ if (!regalloc_dry_run) emitLabel (jtablo); for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) { emitcode (".db", "%05d$", labelKey2num (jtab->key)); regalloc_dry_run_cost++; } if (!regalloc_dry_run) emitLabel (jtabhi); for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) { emitcode (".db", ">%05d$", labelKey2num (jtab->key)); regalloc_dry_run_cost++; } } /*-----------------------------------------------------------------*/ /* genCast - gen code for casting */ /*-----------------------------------------------------------------*/ static void genCast (iCode * ic) { operand *result = IC_RESULT (ic); sym_link *rtype = operandType (IC_RIGHT (ic)); operand *right = IC_RIGHT (ic); int size, offset; bool signExtend; bool save_a; 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, FALSE); if (IS_BOOL (operandType (result))) { bool needpulla = pushRegIfSurv (hc08_reg_a); asmopToBool (AOP (right), TRUE); storeRegToAop (hc08_reg_a, AOP (result), 0); pullOrFreeReg (hc08_reg_a, needpulla); goto release; } /* If the result is 1 byte, then just copy the one byte; there is */ /* nothing special required. */ if (AOP_SIZE (result) == 1) { transferAopAop (AOP (right), 0, AOP (result), 0); goto release; } signExtend = AOP_SIZE (result) > AOP_SIZE (right) && !IS_BOOL (rtype) && IS_SPEC (rtype) && !SPEC_USIGN (rtype); /* If the result is 2 bytes and in registers, we have to be careful */ /* to make sure the registers are not overwritten prematurely. */ if (AOP_SIZE (result) == 2 && AOP (result)->type == AOP_REG) { if (IS_AOP_HX (AOP (result)) && (AOP_SIZE (right) == 2)) { loadRegFromAop (hc08_reg_hx, AOP (right), 0); goto release; } if (AOP_SIZE (right) == 1) { transferAopAop (AOP (right), 0, AOP (result), 0); if (!signExtend) storeConstToAop (0, AOP (result), 1); else { save_a = (AOP (result)->aopu.aop_reg[0] == hc08_reg_a || !hc08_reg_a->isDead); /* we need to extend the sign :{ */ if (save_a) pushReg(hc08_reg_a, FALSE); if (AOP (result)->aopu.aop_reg[0] != hc08_reg_a) loadRegFromAop (hc08_reg_a, AOP (right), 0); accopWithMisc ("rola", ""); accopWithMisc ("clra", ""); accopWithMisc ("sbc", zero); storeRegToAop (hc08_reg_a, AOP (result), 1); if (save_a) pullReg(hc08_reg_a); } goto release; } if (AOP (right)->type == AOP_REG) { wassert (AOP_SIZE (right) == 2); /* Source and destination are the same size; no need for sign */ /* extension or zero padding. Just copy in the order that */ /* won't prematurely overwrite the source. */ if (AOP (result)->aopu.aop_reg[0] == AOP (right)->aopu.aop_reg[1]) { transferAopAop (AOP (right), 1, AOP (result), 1); transferAopAop (AOP (right), 0, AOP (result), 0); } else { transferAopAop (AOP (right), 0, AOP (result), 0); transferAopAop (AOP (right), 1, AOP (result), 1); } goto release; } else { /* Source is at least 2 bytes and not in registers; no need */ /* for sign extension or zero padding. Just copy. */ transferAopAop (AOP (right), 0, AOP (result), 0); transferAopAop (AOP (right), 1, AOP (result), 1); goto release; } } wassert (AOP (result)->type != AOP_REG); save_a = !hc08_reg_a->isDead && signExtend; if (save_a) pushReg(hc08_reg_a, TRUE); offset = 0; size = AOP_SIZE (right); if (AOP_SIZE (result) < size) size = AOP_SIZE (result); while (size) { if (size == 1 && signExtend) { loadRegFromAop (hc08_reg_a, AOP (right), offset); storeRegToAop (hc08_reg_a, AOP (result), offset); offset++; size--; } else if ((size > 2 || size >= 2 && !signExtend) && hc08_reg_h->isDead && hc08_reg_x->isDead && (AOP_TYPE (right) == AOP_IMMD || IS_S08 && AOP_TYPE (right) == AOP_EXT) && (AOP_TYPE (result) == AOP_DIR || IS_S08 && AOP_TYPE (result) == AOP_EXT)) { loadRegFromAop (hc08_reg_hx, AOP (right), offset); storeRegToAop (hc08_reg_hx, AOP (result), offset); offset += 2; size -= 2; } else { transferAopAop (AOP (right), offset, AOP (result), offset); offset++; size--; } } size = AOP_SIZE (result) - offset; if (size && !signExtend) { while (size--) storeConstToAop (0, AOP (result), offset++); } else if (size) { accopWithMisc ("rola", ""); accopWithMisc ("clra", ""); accopWithMisc ("sbc", zero); while (size--) storeRegToAop (hc08_reg_a, AOP (result), offset++); } if (save_a) pullReg(hc08_reg_a); /* we are done hurray !!!! */ release: freeAsmop (right, NULL, ic, TRUE); freeAsmop (result, NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* genDjnz - generate decrement & jump if not zero instrucion */ /*-----------------------------------------------------------------*/ static int genDjnz (iCode * ic, iCode * ifx) { if (!ifx) return 0; D (emitcode ("; genDjnz", "")); /* 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, unless it's already in HX */ aopOp (IC_RESULT (ic), ic, FALSE); if (AOP_SIZE (IC_RESULT (ic)) > 1 && !IS_AOP_HX (AOP (IC_RESULT (ic)))) { freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); return 0; } /* Trying to use dbnz directly requires some convoluted branch/jumps */ /* to handle the cases where the target is far away. The peepholer */ /* is left to clean it up for the simple cases. However, this leaves */ /* a needlessly high dry run cost. So we do not use dbnz and instead */ /* generate simpler (and less constly) code that the peepholer can */ /* easily transform to dbnz if the target is close enough. Thus the */ /* register allocator gets a better idea of the true cost. */ if (IS_AOP_HX (AOP (IC_RESULT (ic)))) { emitcode ("aix", "#-1"); hc08_dirtyReg (hc08_reg_hx, FALSE); emitcode ("cphx", "#0"); regalloc_dry_run_cost += 5; } else rmwWithAop ("dec", AOP (IC_RESULT (ic)), 0); genIfxJump (ifx, "a"); freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); return 1; } /*-----------------------------------------------------------------*/ /* genReceive - generate code for a receive iCode */ /*-----------------------------------------------------------------*/ static void genReceive (iCode * ic) { int size; int offset; bool delayed_x = FALSE; D (emitcode (";", "genReceive")); aopOp (IC_RESULT (ic), ic, FALSE); size = AOP_SIZE (IC_RESULT (ic)); offset = 0; if (ic->argreg && IS_AOP_HX (AOP (IC_RESULT (ic))) && (offset + (ic->argreg - 1)) == 0) { pushReg (hc08_reg_x, TRUE); emitcode ("tax", ""); regalloc_dry_run_cost++; pullReg (hc08_reg_h); } else if (ic->argreg) { while (size--) { if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG && !(offset + (ic->argreg - 1)) && AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx == X_IDX && size) { pushReg (hc08_reg_a, TRUE); delayed_x = TRUE; } else transferAopAop (hc08_aop_pass[offset + (ic->argreg - 1)], 0, AOP (IC_RESULT (ic)), offset); if (hc08_aop_pass[offset]->type == AOP_REG) hc08_freeReg (hc08_aop_pass[offset]->aopu.aop_reg[0]); offset++; } } if (delayed_x) pullReg (hc08_reg_x); 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; bool needpulla; D (emitcode ("; genDummyRead", "")); op = IC_RIGHT (ic); needpulla = pushRegIfSurv (hc08_reg_a); if (op && IS_SYMOP (op)) { aopOp (op, ic, FALSE); size = AOP_SIZE (op); offset = size - 1; while (size--) { loadRegFromAop (hc08_reg_a, AOP (op), offset); hc08_freeReg (hc08_reg_a); offset--; } freeAsmop (op, NULL, ic, TRUE); } op = IC_LEFT (ic); if (op && IS_SYMOP (op)) { aopOp (op, ic, FALSE); size = AOP_SIZE (op); offset = size - 1; while (size--) { loadRegFromAop (hc08_reg_a, AOP (op), offset); hc08_freeReg (hc08_reg_a); offset--; } freeAsmop (op, NULL, ic, TRUE); } pullOrFreeReg (hc08_reg_a, needpulla); } /*-----------------------------------------------------------------*/ /* genCritical - generate code for start of a critical sequence */ /*-----------------------------------------------------------------*/ static void genCritical (iCode * ic) { D (emitcode ("; genCritical", "")); if (IC_RESULT (ic)) aopOp (IC_RESULT (ic), ic, TRUE); emitcode ("tpa", ""); regalloc_dry_run_cost++; hc08_dirtyReg (hc08_reg_a, FALSE); emitcode ("sei", ""); regalloc_dry_run_cost++; if (IC_RESULT (ic)) storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), 0); else pushReg (hc08_reg_a, FALSE); hc08_freeReg (hc08_reg_a); if (IC_RESULT (ic)) freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); } /*-----------------------------------------------------------------*/ /* 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); loadRegFromAop (hc08_reg_a, AOP (IC_RIGHT (ic)), 0); emitcode ("tap", ""); regalloc_dry_run_cost++; hc08_freeReg (hc08_reg_a); freeAsmop (IC_RIGHT (ic), NULL, ic, TRUE); } else { pullReg (hc08_reg_a); emitcode ("tap", ""); regalloc_dry_run_cost++; } } static void updateiTempRegisterUse (operand * op) { symbol *sym; if (IS_ITEMP (op)) { sym = OP_SYMBOL (op); if (!sym->isspilt) { /* If only used by IFX, there might not be any register assigned */ int i; for(i = 0; i < sym->nRegs; i++) if (sym->regs[i]) hc08_useReg (sym->regs[i]); } } } /*---------------------------------------------------------------------------------------*/ /* genhc08iode - generate code for HC08 based controllers for a single iCode instruction */ /*---------------------------------------------------------------------------------------*/ static void genhc08iCode (iCode *ic) { /* if the result is marked as spilt and rematerializable or code for this has already been generated then do nothing */ if (resultRemat (ic) || ic->generated) return; { int i; reg_info *reg; initGenLineElement (); genLine.lineElement.ic = ic; for (i = A_IDX; i <= XA_IDX; i++) { reg = hc08_regWithIdx (i); //if (reg->aop) // emitcode ("", "; %s = %s offset %d", reg->name, aopName (reg->aop), reg->aopofs); reg->isFree = TRUE; if (regalloc_dry_run) reg->isLitConst = 0; } if (ic->op == IFX) updateiTempRegisterUse (IC_COND (ic)); else if (ic->op == JUMPTABLE) updateiTempRegisterUse (IC_JTCOND (ic)); else if (ic->op == RECEIVE) { hc08_useReg (hc08_reg_a); hc08_useReg (hc08_reg_x); // TODO: x really is free if function only receives 1 byte } else { if (POINTER_SET (ic)) updateiTempRegisterUse (IC_RESULT (ic)); updateiTempRegisterUse (IC_LEFT (ic)); updateiTempRegisterUse (IC_RIGHT (ic)); } for (i = A_IDX; i <= H_IDX; i++) { if (bitVectBitValue (ic->rSurv, i)) { hc08_regWithIdx (i)->isDead = FALSE; hc08_regWithIdx (i)->isFree = FALSE; } else hc08_regWithIdx (i)->isDead = TRUE; } } /* 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: /* IPOP happens only when trying to restore a spilt live range, if there is an ifx statement following this pop then the if statement might be using some of the registers being popped which would destory the contents of the register so we need to check for this condition and handle it */ if (ic->next && ic->next->op == IFX && regsInCommon (IC_LEFT (ic), IC_COND (ic->next))) genIfx (ic->next, 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 '>': case '<': case LE_OP: case GE_OP: genCmp (ic, ifxForOp (IC_RESULT (ic), ic)); break; case NE_OP: case EQ_OP: genCmpEQorNE (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: hc08_genInline (ic); break; case RRC: genRRC (ic); break; case RLC: genRLC (ic); break; case GETHBIT: wassertl (0, "Unimplemented iCode"); 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, hasInchc08 (IC_LEFT (ic), ic, getSize (operandType (IC_RESULT (ic)))), ifxForOp (IC_RESULT (ic), ic)); break; case '=': if (POINTER_SET (ic)) genPointerSet (ic, hasInchc08 (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: if (!regalloc_dry_run) addSet (&_G.sendSet, ic); else { set * sendSet = NULL; addSet (&sendSet, ic); genSend (sendSet); deleteSet (&sendSet); } break; case DUMMY_READ_VOLATILE: genDummyRead (ic); break; case CRITICAL: genCritical (ic); break; case ENDCRITICAL: genEndCritical (ic); break; case SWAP: genSwap (ic); break; default: wassertl (0, "Unknown iCode"); } } static void init_aop_pass(void) { if (hc08_aop_pass[0]) return; hc08_aop_pass[0] = newAsmop (AOP_REG); hc08_aop_pass[0]->size = 1; hc08_aop_pass[0]->aopu.aop_reg[0] = hc08_reg_a; hc08_aop_pass[1] = newAsmop (AOP_REG); hc08_aop_pass[1]->size = 1; hc08_aop_pass[1]->aopu.aop_reg[0] = hc08_reg_x; hc08_aop_pass[2] = newAsmop (AOP_DIR); hc08_aop_pass[2]->size = 1; hc08_aop_pass[2]->aopu.aop_dir = "___SDCC_hc08_ret2"; hc08_aop_pass[3] = newAsmop (AOP_DIR); hc08_aop_pass[3]->size = 1; hc08_aop_pass[3]->aopu.aop_dir = "___SDCC_hc08_ret3"; hc08_aop_pass[4] = newAsmop (AOP_DIR); hc08_aop_pass[4]->size = 1; hc08_aop_pass[4]->aopu.aop_dir = "___SDCC_hc08_ret4"; hc08_aop_pass[5] = newAsmop (AOP_DIR); hc08_aop_pass[5]->size = 1; hc08_aop_pass[5]->aopu.aop_dir = "___SDCC_hc08_ret5"; hc08_aop_pass[6] = newAsmop (AOP_DIR); hc08_aop_pass[6]->size = 1; hc08_aop_pass[6]->aopu.aop_dir = "___SDCC_hc08_ret6"; hc08_aop_pass[7] = newAsmop (AOP_DIR); hc08_aop_pass[7]->size = 1; hc08_aop_pass[7]->aopu.aop_dir = "___SDCC_hc08_ret7"; } unsigned char dryhc08iCode (iCode *ic) { regalloc_dry_run = TRUE; regalloc_dry_run_cost = 0; init_aop_pass(); genhc08iCode (ic); destroy_line_list (); /*freeTrace (&_G.trace.aops);*/ return (regalloc_dry_run_cost); } /*-----------------------------------------------------------------*/ /* genhc08Code - generate code for HC08 based controllers */ /*-----------------------------------------------------------------*/ void genhc08Code (iCode *lic) { iCode *ic; int cln = 0; int clevel = 0; int cblock = 0; regalloc_dry_run = FALSE; hc08_dirtyReg (hc08_reg_a, FALSE); hc08_dirtyReg (hc08_reg_h, FALSE); hc08_dirtyReg (hc08_reg_x, FALSE); /* print the allocation information */ if (allocInfo && currFunc) printAllocInfo (currFunc, codeOutBuf); /* if debug information required */ if (options.debug && currFunc && !regalloc_dry_run) { debugFile->writeFunction (currFunc, lic); } if (options.debug && !regalloc_dry_run) debugFile->writeFrameAddress (NULL, NULL, 0); /* have no idea where frame is now */ init_aop_pass(); for (ic = lic; ic; ic = ic->next) ic->generated = FALSE; for (ic = lic; ic; ic = ic->next) { initGenLineElement (); genLine.lineElement.ic = ic; if (ic->level != clevel || ic->block != cblock) { if (options.debug) { debugFile->writeScope (ic); } clevel = ic->level; cblock = ic->block; } 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; } if (options.iCodeInAsm) { char regsSurv[4]; const char *iLine; regsSurv[0] = (bitVectBitValue (ic->rSurv, A_IDX)) ? 'a' : '-'; regsSurv[1] = (bitVectBitValue (ic->rSurv, H_IDX)) ? 'h' : '-'; regsSurv[2] = (bitVectBitValue (ic->rSurv, X_IDX)) ? 'x' : '-'; regsSurv[3] = 0; iLine = printILine (ic); emitcode ("", "; [%s] ic:%d: %s", regsSurv, ic->seq, printILine (ic)); dbuf_free (iLine); } regalloc_dry_run_cost = 0; genhc08iCode(ic); /*if (options.verboseAsm) emitcode (";", "iCode %d (key %d) total cost: %d\n", ic->seq, ic->key, (int) regalloc_dry_run_cost);*/ if (!hc08_reg_a->isFree) DD (emitcode ("", "; forgot to free a")); if (!hc08_reg_x->isFree) DD (emitcode ("", "; forgot to free x")); if (!hc08_reg_h->isFree) DD (emitcode ("", "; forgot to free h")); if (!hc08_reg_hx->isFree) DD (emitcode ("", "; forgot to free hx")); if (!hc08_reg_xa->isFree) DD (emitcode ("", "; forgot to free xa")); } if (options.debug) debugFile->writeFrameAddress (NULL, NULL, 0); /* have no idea where frame is now */ /* 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 (); }