diff options
| author | Xavier ASUS <xavi92psx@gmail.com> | 2019-10-18 00:31:54 +0200 |
|---|---|---|
| committer | Xavier ASUS <xavi92psx@gmail.com> | 2019-10-18 00:31:54 +0200 |
| commit | 268a53de823a6750d6256ee1fb1e7707b4b45740 (patch) | |
| tree | 42c1799a9a82b2f7d9790ee9fe181d72a7274751 /src/hc08 | |
| download | sdcc-gas-268a53de823a6750d6256ee1fb1e7707b4b45740.tar.gz | |
sdcc-3.9.0 fork implementing GNU assembler syntax
This fork aims to provide better support for stm8-binutils
Diffstat (limited to 'src/hc08')
| -rw-r--r-- | src/hc08/Makefile | 7 | ||||
| -rw-r--r-- | src/hc08/Makefile.in | 7 | ||||
| -rw-r--r-- | src/hc08/gen.c | 10849 | ||||
| -rw-r--r-- | src/hc08/gen.h | 95 | ||||
| -rw-r--r-- | src/hc08/hc08.h | 18 | ||||
| -rw-r--r-- | src/hc08/hc08.vcxproj | 147 | ||||
| -rw-r--r-- | src/hc08/hc08.vcxproj.filters | 49 | ||||
| -rw-r--r-- | src/hc08/main.c | 1058 | ||||
| -rw-r--r-- | src/hc08/main.h | 8 | ||||
| -rw-r--r-- | src/hc08/peeph.def | 278 | ||||
| -rw-r--r-- | src/hc08/ralloc.c | 2423 | ||||
| -rw-r--r-- | src/hc08/ralloc.h | 89 | ||||
| -rw-r--r-- | src/hc08/ralloc2.cc | 674 |
13 files changed, 15702 insertions, 0 deletions
diff --git a/src/hc08/Makefile b/src/hc08/Makefile new file mode 100644 index 0000000..cb704c7 --- /dev/null +++ b/src/hc08/Makefile @@ -0,0 +1,7 @@ + +srcdir = . +top_builddir = ../.. +top_srcdir = ../.. + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/hc08/Makefile.in b/src/hc08/Makefile.in new file mode 100644 index 0000000..dfb8a52 --- /dev/null +++ b/src/hc08/Makefile.in @@ -0,0 +1,7 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/hc08/gen.c b/src/hc08/gen.c new file mode 100644 index 0000000..4ab37b3 --- /dev/null +++ b/src/hc08/gen.c @@ -0,0 +1,10849 @@ +/*------------------------------------------------------------------------- + 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#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 + <sizeof *op> */ + if (lic->op == '+' && isOperandEqual (IC_LEFT (lic), op) && + isOperandEqual (IC_RESULT (lic), op) && + isOperandLiteral (IC_RIGHT (lic)) && operandLitValue (IC_RIGHT (lic)) == isize) + { + return lic; + } + /* if the operand used or deffed */ + if (bitVectBitValue (OP_USES (op), lic->key) || lic->defKey == op->key) + { + return NULL; + } + /* if GOTO or IFX */ + if (lic->op == IFX || lic->op == GOTO || lic->op == LABEL) + break; + lic = lic->next; + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* genAndOp - for && operation */ +/*-----------------------------------------------------------------*/ +static void +genAndOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl, *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; offset<size; offset++) + { + assigned[offset] = 0; + dup[offset] = 0; + value[offset] = byteOfVal (AOP (right)->aopu.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<size; offset++) + remaining -= assigned[offset]; + if (!remaining) + return TRUE; + + if (remaining > 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 (); +} + diff --git a/src/hc08/gen.h b/src/hc08/gen.h new file mode 100644 index 0000000..f946f23 --- /dev/null +++ b/src/hc08/gen.h @@ -0,0 +1,95 @@ +/*------------------------------------------------------------------------- + gen.h - header file for code generation for hc(s)08 + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + 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. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCGENHC08_H +#define SDCCGENHC08_H + +enum + { + AOP_LIT = 1, + AOP_REG, AOP_DIR, + AOP_STK, AOP_IMMD, AOP_STR, + AOP_CRY, + AOP_EXT, AOP_SOF, AOP_DUMMY, AOP_IDX + }; + +enum + { + ACCUSE_XA = 1, + ACCUSE_HX + }; + +/* type asmop : a homogenised type for + all the different spaces an operand can be + in */ +typedef struct asmop + { + + short type; + /* can have values + AOP_LIT - operand is a literal value + AOP_REG - is in registers + AOP_DIR - operand using direct addressing mode + AOP_STK - should be pushed on stack this + can happen only for the result + AOP_IMMD - immediate value for eg. remateriazable + AOP_CRY - carry contains the value of this + AOP_STR - array of strings + AOP_SOF - operand at an offset on the stack + AOP_EXT - operand using extended addressing mode + AOP_IDX - operand using indexed addressing mode + */ + short regmask; /* register mask if AOP_REG */ + short coff; /* current offset */ + short size; /* total size */ + operand *op; /* originating operand */ + unsigned code:1; /* is in Code space */ + unsigned freed:1; /* already freed */ + unsigned stacked:1; /* partial results stored on stack */ + struct asmop *stk_aop[4]; /* asmops for the results on the stack */ + union + { + value *aop_lit; /* if literal */ + reg_info *aop_reg[4]; /* array of registers */ + char *aop_dir; /* if direct */ + struct { + char *aop_immd1; /* if immediate others are implied */ + char *aop_immd2; /* cast remat will generate this */ + } aop_immd; + int aop_stk; /* stack offset when AOP_STK */ + } + aopu; + } +asmop; + +void genhc08Code (iCode *); +void hc08_emitDebuggerSymbol (const char *); + +extern unsigned fReturnSizeHC08; + +iCode *hasInchc08 (operand *op, const iCode *ic, int osize); +extern bool hc08_assignment_optimal; + +#endif + diff --git a/src/hc08/hc08.h b/src/hc08/hc08.h new file mode 100644 index 0000000..aa0f440 --- /dev/null +++ b/src/hc08/hc08.h @@ -0,0 +1,18 @@ +typedef enum + { + SUB_HC08, + SUB_S08 + } +HC08_SUB_PORT; + +typedef struct + { + HC08_SUB_PORT sub; + } +HC08_OPTS; + +extern HC08_OPTS hc08_opts; + +#define IS_HC08 (hc08_opts.sub == SUB_HC08) +#define IS_S08 (hc08_opts.sub == SUB_S08) + diff --git a/src/hc08/hc08.vcxproj b/src/hc08/hc08.vcxproj new file mode 100644 index 0000000..1825393 --- /dev/null +++ b/src/hc08/hc08.vcxproj @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{6FA87476-0FAB-4518-9845-12EFEBBCE03F}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\..\SDCC.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\..\SDCC.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">port</TargetName>
+ <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">port</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <PrecompiledHeaderOutputFile>.\Debug/hc08.pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\Debug/</AssemblerListingLocation>
+ <ObjectFileName>.\Debug/</ObjectFileName>
+ <ProgramDataBaseFileName>.\Debug/</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level2</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(Configuration)\$(TargetFileName)</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug/hc08.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <AdditionalIncludeDirectories>..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\Release/hc08.pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\Release/</AssemblerListingLocation>
+ <ObjectFileName>.\Release/</ObjectFileName>
+ <ProgramDataBaseFileName>.\Release/</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Lib>
+ <OutputFile>$(Configuration)\$(TargetFileName)</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Lib>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release/hc08.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="gen.c">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="main.c">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="ralloc.c">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="ralloc2.cc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="gen.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="ralloc.h" />
+ <ClInclude Include="..\..\sdcc_vc.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="peeph.def">
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul</Command>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">peeph.rul;%(Outputs)</Outputs>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">peeph.rul;%(Outputs)</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Generating Peephole Rule: peeph.rul</Message>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Generating Peephole Rule: peeph.rul</Message>
+ </CustomBuild>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file diff --git a/src/hc08/hc08.vcxproj.filters b/src/hc08/hc08.vcxproj.filters new file mode 100644 index 0000000..ec3612f --- /dev/null +++ b/src/hc08/hc08.vcxproj.filters @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{36ed1d0f-18b0-4751-99cf-8f3e3c6c6af9}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{63c1a458-3e87-4ca3-bd80-b0fe3ce009fc}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Custom Build">
+ <UniqueIdentifier>{efbf5b89-aca8-4e12-ae6a-f2173da55e89}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="gen.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ralloc.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ralloc2.cc">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="gen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ralloc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\sdcc_vc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="peeph.def">
+ <Filter>Custom Build</Filter>
+ </CustomBuild>
+ </ItemGroup>
+</Project>
\ No newline at end of file diff --git a/src/hc08/main.c b/src/hc08/main.c new file mode 100644 index 0000000..85656f5 --- /dev/null +++ b/src/hc08/main.c @@ -0,0 +1,1058 @@ +/*------------------------------------------------------------------------- + main.h - hc08 specific general function + + 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. +-------------------------------------------------------------------------*/ +/* + Note that mlh prepended _hc08_ on the static functions. Makes + it easier to set a breakpoint using the debugger. +*/ +#include "common.h" +#include "hc08.h" +#include "main.h" +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +extern char * iComments2; +extern DEBUGFILE dwarf2DebugFile; +extern int dwarf2FinalizeFile(FILE *); + +static char _hc08_defaultRules[] = +{ +#include "peeph.rul" +}; + +static char _s08_defaultRules[] = +{ +#include "peeph.rul" +}; + +HC08_OPTS hc08_opts; + +/* list of key words used by msc51 */ +static char *_hc08_keywords[] = +{ + "at", + //"bit", + "code", + "critical", + "data", + "far", + //"idata", + "interrupt", + "near", + //"pdata", + "reentrant", + //"sfr", + //"sbit", + //"using", + "xdata", + "_data", + "_code", + "_generic", + "_near", + "_xdata", + //"_pdata", + //"_idata", + "_naked", + "_overlay", + NULL +}; + + +void hc08_assignRegisters (ebbIndex *); + +static int regParmFlg = 0; /* determine if we can register a parameter */ + +static void +_hc08_init (void) +{ + hc08_opts.sub = SUB_HC08; + asm_addTree (&asm_asxxxx_mapping); +} + +static void +_s08_init (void) +{ + hc08_opts.sub = SUB_S08; + asm_addTree (&asm_asxxxx_mapping); +} + +static void +_hc08_reset_regparm (struct sym_link *funcType) +{ + regParmFlg = 0; +} + +static int +_hc08_regparm (sym_link * l, bool reentrant) +{ + int size = getSize(l); + + /* If they fit completely, the first two bytes of parameters can go */ + /* into A and X, otherwise, they go on the stack. Examples: */ + /* foo(char p1) A <- p1 */ + /* foo(char p1, char p2) A <- p1, X <- p2 */ + /* foo(char p1, char p2, char p3) A <- p1, X <- p2, stack <- p3 */ + /* foo(int p1) XA <- p1 */ + /* foo(long p1) stack <- p1 */ + /* foo(char p1, int p2) A <- p1, stack <- p2 */ + /* foo(int p1, char p2) XA <- p1, stack <- p2 */ + + if (regParmFlg>=2) + return 0; + + if ((regParmFlg+size)>2) + { + regParmFlg = 2; + return 0; + } + + regParmFlg += size; + return 1+regParmFlg-size; +} + +static bool +_hc08_parseOptions (int *pargc, char **argv, int *i) +{ + if (!strcmp (argv[*i], "--out-fmt-elf")) + { + options.out_fmt = 'E'; + debugFile = &dwarf2DebugFile; + return TRUE; + } + + if (!strcmp (argv[*i], "--oldralloc")) + { + options.oldralloc = TRUE; + return TRUE; + } + + return FALSE; +} + +#define OPTION_SMALL_MODEL "--model-small" +#define OPTION_LARGE_MODEL "--model-large" + +static OPTION _hc08_options[] = + { + {0, OPTION_SMALL_MODEL, NULL, "8-bit address space for data"}, + {0, OPTION_LARGE_MODEL, NULL, "16-bit address space for data (default)"}, + {0, "--out-fmt-elf", NULL, "Output executable in ELF format" }, + {0, "--oldralloc", NULL, "Use old register allocator"}, + {0, NULL } + }; + +static void +_hc08_finaliseOptions (void) +{ + if (options.noXinitOpt) + port->genXINIT = 0; + + if (options.model == MODEL_LARGE) { + port->mem.default_local_map = xdata; + port->mem.default_globl_map = xdata; + } + else + { + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + } + + istack->ptrType = FPOINTER; +} + +static void +_hc08_setDefaultOptions (void) +{ + options.code_loc = 0x8000; + options.data_loc = 0x80; + options.xdata_loc = 0; /* 0 means immediately following data */ + options.stack_loc = 0x7fff; + options.out_fmt = 's'; /* use motorola S19 output */ + + options.omitFramePtr = 1; /* no frame pointer (we use SP */ + /* offsets instead) */ +} + +static const char * +_hc08_getRegName (const struct reg_info *reg) +{ + if (reg) + return reg->name; + return "err"; +} + +static void +_hc08_genAssemblerPreamble (FILE * of) +{ + int i; + int needOrg = 1; + symbol *mainExists=newSymbol("main", 0); + mainExists->block=0; + + fprintf (of, "\t.area %s\n",HOME_NAME); + fprintf (of, "\t.area GSINIT0 (CODE)\n"); + fprintf (of, "\t.area %s\n",port->mem.static_name); + fprintf (of, "\t.area %s\n",port->mem.post_static_name); + fprintf (of, "\t.area %s\n",CODE_NAME); + fprintf (of, "\t.area %s\n",port->mem.xinit_name); + fprintf (of, "\t.area %s\n",port->mem.const_name); + fprintf (of, "\t.area %s\n",port->mem.data_name); + fprintf (of, "\t.area %s\n",port->mem.overlay_name); + fprintf (of, "\t.area %s\n",port->mem.xdata_name); + fprintf (of, "\t.area %s\n",port->mem.xidata_name); + + if ((mainExists=findSymWithLevel(SymbolTab, mainExists))) + { + // generate interrupt vector table + fprintf (of, "\t.area\tCODEIVT (ABS)\n"); + + for (i=maxInterrupts;i>0;i--) + { + if (interrupts[i]) + { + if (needOrg) + { + fprintf (of, "\t.org\t0x%04x\n", (0xfffe - (i * 2))); + needOrg = 0; + } + fprintf (of, "\t.dw\t%s\n", interrupts[i]->rname); + } + else + needOrg = 1; + } + if (needOrg) + fprintf (of, "\t.org\t0xfffe\n"); + fprintf (of, "\t.dw\t%s", "__sdcc_gs_init_startup\n\n"); + + fprintf (of, "\t.area GSINIT0\n"); + fprintf (of, "__sdcc_gs_init_startup:\n"); + if (options.stack_loc) + { + fprintf (of, "\tldhx\t#0x%04x\n", options.stack_loc+1); + fprintf (of, "\ttxs\n"); + } + else + fprintf (of, "\trsp\n"); + fprintf (of, "\tjsr\t__sdcc_external_startup\n"); + fprintf (of, "\tbeq\t__sdcc_init_data\n"); + fprintf (of, "\tjmp\t__sdcc_program_startup\n"); + fprintf (of, "__sdcc_init_data:\n"); + + fprintf (of, "; _hc08_genXINIT() start\n"); + fprintf (of, " ldhx #0\n"); + fprintf (of, "00001$:\n"); + fprintf (of, " cphx #l_XINIT\n"); + fprintf (of, " beq 00002$\n"); + fprintf (of, " lda s_XINIT,x\n"); + fprintf (of, " sta s_XISEG,x\n"); + fprintf (of, " aix #1\n"); + fprintf (of, " bra 00001$\n"); + fprintf (of, "00002$:\n"); + fprintf (of, "; _hc08_genXINIT() end\n"); + + fprintf (of, "\t.area GSFINAL\n"); + fprintf (of, "\tjmp\t__sdcc_program_startup\n\n"); + + fprintf (of, "\t.area CSEG\n"); + fprintf (of, "__sdcc_program_startup:\n"); + fprintf (of, "\tjsr\t_main\n"); + fprintf (of, "\tbra\t.\n"); + + } +} + +static void +_hc08_genAssemblerEnd (FILE * of) +{ + if (options.out_fmt == 'E' && options.debug) + { + dwarf2FinalizeFile (of); + } +} + +static void +_hc08_genExtraAreas (FILE * asmFile, bool mainExists) +{ + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; extended address mode data\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&xdata->oBuf, asmFile); +} + +/* Generate interrupt vector table. */ +static int +_hc08_genIVT (struct dbuf_s * oBuf, symbol ** interrupts, int maxInterrupts) +{ + int i; + + dbuf_printf (oBuf, "\t.area\tCODEIVT (ABS)\n"); + dbuf_printf (oBuf, "\t.org\t0x%04x\n", + (0xfffe - (maxInterrupts * 2))); + + for (i=maxInterrupts;i>0;i--) + { + if (interrupts[i]) + dbuf_printf (oBuf, "\t.dw\t%s\n", interrupts[i]->rname); + else + dbuf_printf (oBuf, "\t.dw\t0xffff\n"); + } + dbuf_printf (oBuf, "\t.dw\t%s", "__sdcc_gs_init_startup\n"); + + return TRUE; +} + +/* Generate code to copy XINIT to XISEG */ +static void _hc08_genXINIT (FILE * of) { + fprintf (of, "; _hc08_genXINIT() start\n"); + fprintf (of, "; _hc08_genXINIT() end\n"); +} + + +/* Do CSE estimation */ +static bool cseCostEstimation (iCode *ic, iCode *pdic) +{ + operand *result = IC_RESULT(ic); + sym_link *result_type = operandType(result); + + /* if it is a pointer then return ok for now */ + if (IC_RESULT(ic) && IS_PTR(result_type)) return 1; + + if (ic->op == ADDRESS_OF) + return 0; + + /* if bitwise | add & subtract then no since hc08 is pretty good at it + so we will cse only if they are local (i.e. both ic & pdic belong to + the same basic block */ + if (IS_BITWISE_OP(ic) || ic->op == '+' || ic->op == '-') { + /* then if they are the same Basic block then ok */ + if (ic->eBBlockNum == pdic->eBBlockNum) return 1; + else return 0; + } + + /* for others it is cheaper to do the cse */ + return 1; +} + +/* Indicate which extended bit operations this port supports */ +static bool +hasExtBitOp (int op, int size) +{ + if (op == RRC + || op == RLC + || (op == SWAP && size <= 2) + || op == GETABIT + || op == GETBYTE + || op == GETWORD + ) + return TRUE; + else + return FALSE; +} + +/* Indicate the expense of an access to an output storage class */ +static int +oclsExpense (struct memmap *oclass) +{ + /* The hc08's addressing modes allow access to all storage classes */ + /* inexpensively (<=0) */ + + if (IN_DIRSPACE (oclass)) /* direct addressing mode is fastest */ + return -2; + if (IN_FARSPACE (oclass)) /* extended addressing mode is almost at fast */ + return -1; + if (oclass == istack) /* stack is the slowest, but still faster than */ + return 0; /* trying to copy to a temp location elsewhere */ + + return 0; /* anything we missed */ +} + +/*----------------------------------------------------------------------*/ +/* hc08_dwarfRegNum - return the DWARF register number for a register. */ +/* These are defined for the HC08 in "Motorola 8- and 16-bit Embedded */ +/* Application Binary Interface (M8/16EABI)" */ +/*----------------------------------------------------------------------*/ +static int +hc08_dwarfRegNum (const struct reg_info *reg) +{ + switch (reg->rIdx) + { + case A_IDX: return 0; + case H_IDX: return 1; + case X_IDX: return 2; + case CND_IDX: return 17; + case SP_IDX: return 15; + } + return -1; +} + +static bool +_hasNativeMulFor (iCode *ic, sym_link *left, sym_link *right) +{ + return getSize (left) == 1 && getSize (right) == 1; +} + +typedef struct asmLineNode + { + int size; + } +asmLineNode; + +static asmLineNode * +newAsmLineNode (void) +{ + asmLineNode *aln; + + aln = Safe_alloc ( sizeof (asmLineNode)); + aln->size = 0; + + return aln; +} + +typedef struct hc08opcodedata + { + char name[6]; + char adrmode; + /* info for registers used and/or modified by an instruction will be added here */ + } +hc08opcodedata; + +#define HC08OP_STD 1 +#define HC08OP_RMW 2 +#define HC08OP_INH 3 +#define HC08OP_IM1 4 +#define HC08OP_BR 5 +#define HC08OP_BTB 6 +#define HC08OP_BSC 7 +#define HC08OP_MOV 8 +#define HC08OP_CBEQ 9 +#define HC08OP_CPHX 10 +#define HC08OP_LDHX 11 +#define HC08OP_STHX 12 +#define HC08OP_DBNZ 13 + +/* These must be kept sorted by opcode name */ +static hc08opcodedata hc08opcodeDataTable[] = + { + {".db", HC08OP_INH}, /* used by the code generator only in the jump table */ + {"adc", HC08OP_STD}, + {"add", HC08OP_STD}, + {"ais", HC08OP_IM1}, + {"aix", HC08OP_IM1}, + {"and", HC08OP_STD}, + {"asl", HC08OP_RMW}, + {"asla", HC08OP_INH}, + {"aslx", HC08OP_INH}, + {"asr", HC08OP_RMW}, + {"asra", HC08OP_INH}, + {"asrx", HC08OP_INH}, + {"bcc", HC08OP_BR,}, + {"bclr", HC08OP_BSC}, + {"bcs", HC08OP_BR}, + {"beq", HC08OP_BR}, + {"bge", HC08OP_BR}, + {"bgnd", HC08OP_INH}, + {"bgt", HC08OP_BR}, + {"bhcc", HC08OP_BR}, + {"bhcs", HC08OP_BR}, + {"bhi", HC08OP_BR}, + {"bhs", HC08OP_BR}, + {"bih", HC08OP_BR}, + {"bil", HC08OP_BR}, + {"bit", HC08OP_STD}, + {"ble", HC08OP_BR}, + {"blo", HC08OP_BR}, + {"bls", HC08OP_BR}, + {"blt", HC08OP_BR}, + {"bmc", HC08OP_BR}, + {"bmi", HC08OP_BR}, + {"bms", HC08OP_BR}, + {"bne", HC08OP_BR}, + {"bpl", HC08OP_BR}, + {"bra", HC08OP_BR}, + {"brclr", HC08OP_BTB}, + {"brn", HC08OP_BR}, + {"brset", HC08OP_BTB}, + {"bset", HC08OP_BSC}, + {"bsr", HC08OP_BR}, + {"cbeq", HC08OP_CBEQ}, + {"cbeqa", HC08OP_CBEQ}, + {"cbeqx", HC08OP_CBEQ}, + {"clc", HC08OP_INH}, + {"cli", HC08OP_INH}, + {"clr", HC08OP_RMW}, + {"clra", HC08OP_INH}, + {"clrh", HC08OP_INH}, + {"clrx", HC08OP_INH}, + {"cmp", HC08OP_STD}, + {"com", HC08OP_RMW}, + {"coma", HC08OP_INH}, + {"comx", HC08OP_INH}, + {"cphx", HC08OP_CPHX}, + {"cpx", HC08OP_STD}, + {"daa", HC08OP_INH}, + {"dbnz", HC08OP_DBNZ}, + {"dbnza", HC08OP_BR}, + {"dbnzx", HC08OP_BR}, + {"dec", HC08OP_RMW}, + {"deca", HC08OP_INH}, + {"decx", HC08OP_INH}, + {"div", HC08OP_INH}, + {"eor", HC08OP_STD}, + {"inc", HC08OP_RMW}, + {"inca", HC08OP_INH}, + {"incx", HC08OP_INH}, + {"jmp", HC08OP_STD}, + {"jsr", HC08OP_STD}, + {"lda", HC08OP_STD}, + {"ldhx", HC08OP_LDHX}, + {"ldx", HC08OP_STD}, + {"lsl", HC08OP_RMW}, + {"lsla", HC08OP_INH}, + {"lslx", HC08OP_INH}, + {"lsr", HC08OP_RMW}, + {"lsra", HC08OP_INH}, + {"lsrx", HC08OP_INH}, + {"mov", HC08OP_MOV}, + {"mul", HC08OP_INH}, + {"neg", HC08OP_RMW}, + {"nega", HC08OP_INH}, + {"negx", HC08OP_INH}, + {"nop", HC08OP_INH}, + {"nsa", HC08OP_INH}, + {"ora", HC08OP_STD}, + {"psha", HC08OP_INH}, + {"pshh", HC08OP_INH}, + {"pshx", HC08OP_INH}, + {"pula", HC08OP_INH}, + {"pulh", HC08OP_INH}, + {"pulx", HC08OP_INH}, + {"rol", HC08OP_RMW}, + {"rola", HC08OP_INH}, + {"rolx", HC08OP_INH}, + {"ror", HC08OP_RMW}, + {"rora", HC08OP_INH}, + {"rorx", HC08OP_INH}, + {"rsp", HC08OP_INH}, + {"rti", HC08OP_INH}, + {"rts", HC08OP_INH}, + {"sbc", HC08OP_STD}, + {"sec", HC08OP_INH}, + {"sei", HC08OP_INH}, + {"sta", HC08OP_STD}, + {"sthx", HC08OP_STHX}, + {"stop", HC08OP_INH}, + {"stx", HC08OP_STD}, + {"sub", HC08OP_STD}, + {"swi", HC08OP_INH}, + {"tap", HC08OP_INH}, + {"tax", HC08OP_INH}, + {"tpa", HC08OP_INH}, + {"tst", HC08OP_RMW}, + {"tsta", HC08OP_INH}, + {"tstx", HC08OP_INH}, + {"tsx", HC08OP_INH}, + {"txa", HC08OP_INH}, + {"txs", HC08OP_INH}, + {"wait", HC08OP_INH} + }; + +static int +hc08_opcodeCompare (const void *key, const void *member) +{ + return strcmp((const char *)key, ((hc08opcodedata *)member)->name); +} + +/*--------------------------------------------------------------------*/ +/* Given an instruction and its first two operands, compute the */ +/* instruction size. There are a few cases where it's too complicated */ +/* to distinguish between an 8-bit offset and 16-bit offset; in these */ +/* cases we conservatively assume the 16-bit offset size. */ +/*--------------------------------------------------------------------*/ +static int +hc08_instructionSize(const char *inst, const char *op1, const char *op2) +{ + hc08opcodedata *opcode; + int size; + long offset; + char * endnum = NULL; + + opcode = bsearch (inst, hc08opcodeDataTable, + sizeof(hc08opcodeDataTable)/sizeof(hc08opcodedata), + sizeof(hc08opcodedata), hc08_opcodeCompare); + + if (!opcode) + return 999; + switch (opcode->adrmode) + { + case HC08OP_INH: /* Inherent addressing mode */ + return 1; + + case HC08OP_BSC: /* Bit set/clear direct addressing mode */ + case HC08OP_BR: /* Branch (1 byte signed offset) */ + case HC08OP_IM1: /* 1 byte immediate addressing mode */ + return 2; + + case HC08OP_BTB: /* Bit test direct addressing mode and branch */ + return 3; + + case HC08OP_RMW: /* read/modify/write instructions */ + if (!op2[0]) /* if not ,x or ,sp must be direct addressing mode */ + return 2; + if (!op1[0]) /* if ,x with no offset */ + return 1; + if (op2[0] == 'x') /* if ,x with offset */ + return 2; + return 3; /* Otherwise, must be ,sp with offset */ + + case HC08OP_STD: /* standard instruction */ + if (!op2[0]) + { + if (op1[0] == '#') /* Immediate addressing mode */ + return 2; + if (op1[0] == '*') /* Direct addressing mode */ + return 2; + return 3; /* Otherwise, must be extended addressing mode */ + } + else + { + if (!op1[0]) /* if ,x with no offset */ + return 1; + size = 2; + if (op2[0] == 's') + size++; + offset = strtol (op1, &endnum, 0) & 0xffff; + if (endnum && *endnum) + size++; + else if (offset > 0xff) + size++; + return size; + } + case HC08OP_MOV: + if (op2[0] == 'x') + return 2; + return 3; + case HC08OP_CBEQ: + if (op2[0] == 'x' && !op1[0]) + return 2; /* cbeq ,x+,rel */ + if (op2[0] == 's') + return 4; /* cbeq oprx8,sp,rel */ + return 3; + case HC08OP_CPHX: + if (op1[0] == '*') + return 2; + return 3; + case HC08OP_DBNZ: + if (!op2[0]) + return 2; + if (!op1[0] && op2[0] == 'x') + return 2; + if (op2[0] == 's') + return 4; + return 3; + case HC08OP_LDHX: + case HC08OP_STHX: + if (op1[0] == '*') + return 2; + if (!op1[0] && op2[0] == 'x') + return 2; + if (op2[0] == 's' || op1[0] == '#' || !op2[0]) + return 3; + size = 3; + offset = strtol (op1, &endnum, 0) & 0xffff; + if (endnum && *endnum) + size++; + else if (offset > 0xff) + size++; + return size; + default: + return 4; + } +} + + +static asmLineNode * +hc08_asmLineNodeFromLineNode (lineNode *ln) +{ + asmLineNode *aln = newAsmLineNode(); + char *op, op1[256], op2[256]; + int opsize; + const char *p; + char inst[8]; + + p = ln->line; + + while (*p && isspace(*p)) p++; + for (op = inst, opsize=1; *p; p++) + { + if (isspace(*p) || *p == ';' || *p == ':' || *p == '=') + break; + else + if (opsize < sizeof(inst)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + if (*p == ';' || *p == ':' || *p == '=') + return aln; + + while (*p && isspace(*p)) p++; + if (*p == '=') + return aln; + + for (op = op1, opsize=1; *p && *p != ','; p++) + { + if (!isspace(*p) && opsize < sizeof(op1)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + if (*p == ',') p++; + for (op = op2, opsize=1; *p && *p != ','; p++) + { + if (!isspace(*p) && opsize < sizeof(op2)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + aln->size = hc08_instructionSize(inst, op1, op2); + + return aln; +} + +static int +hc08_getInstructionSize (lineNode *line) +{ + if (!line->aln) + line->aln = (asmLineNodeBase *) hc08_asmLineNodeFromLineNode (line); + + return line->aln->size; +} + +/** $1 is always the basename. + $2 is always the output file. + $3 varies + $l is the list of extra options that should be there somewhere... + MUST be terminated with a NULL. +*/ +static const char *_linkCmd[] = +{ + "sdld6808", "-nf", "$1", NULL +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *_asmCmd[] = +{ + "sdas6808", "$l", "$3", "$2", "$1.asm", NULL +}; + +static const char * const _libs_hc08[] = { "hc08", NULL, }; +static const char * const _libs_s08[] = { "s08", NULL, }; + +/* Globals */ +PORT hc08_port = +{ + TARGET_ID_HC08, + "hc08", + "HC08", /* Target name */ + NULL, /* Processor name */ + { + glue, + FALSE, /* Emit glue around main */ + MODEL_SMALL | MODEL_LARGE, + MODEL_LARGE, + NULL, /* model == target */ + }, + { + _asmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm", + NULL /* no do_assemble function */ + }, + { /* Linker */ + _linkCmd, + NULL, + NULL, + ".rel", + 1, + NULL, /* crt */ + _libs_hc08, /* libs */ + }, + { /* Peephole optimizer */ + _hc08_defaultRules, + hc08_getInstructionSize, + }, + { + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + 1, 2, 2, 4, 8, 2, 2, 2, 2, 0, 1, 4 + }, + /* tags for generic pointers */ + { 0x00, 0x00, 0x00, 0x00 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CSEG (CODE)", + "DSEG (PAG)", + NULL, /* "ISEG" */ + NULL, /* "PSEG" */ + "XSEG", + NULL, /* "BSEG" */ + "RSEG (ABS)", + "GSINIT (CODE)", + "OSEG (PAG, OVR)", + "GSFINAL (CODE)", + "HOME (CODE)", + "XISEG", // initialized xdata + "XINIT (CODE)", // a code copy of xiseg + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS)", // xabs_name - absolute xdata + "IABS (ABS)", // iabs_name - absolute data + NULL, // name of segment for initialized variables + NULL, // name of segment for copies of initialized variables in code space + NULL, + NULL, + 1, + 1 // No fancy alignments supported. + }, + { _hc08_genExtraAreas, + NULL }, + { + -1, /* direction (-1 = stack grows down) */ + 0, /* bank_overhead (switch between register banks) */ + 4, /* isr_overhead */ + 2, /* call_overhead */ + 0, /* reent_overhead */ + 0, /* banked_overhead (switch between code banks) */ + 1 /* sp is offset by 1 from last item pushed */ + }, + { + 5, FALSE + }, + { + hc08_emitDebuggerSymbol, + { + hc08_dwarfRegNum, + NULL, + NULL, + 4, /* addressSize */ + 14, /* regNumRet */ + 15, /* regNumSP */ + -1, /* regNumBP */ + 1, /* offsetSP */ + }, + }, + { + 256, /* maxCount */ + 2, /* sizeofElement */ + {8,16,32}, /* sizeofMatchJump[] */ + {8,16,32}, /* sizeofRangeCompare[] */ + 5, /* sizeofSubtract */ + 10, /* sizeofDispatch */ + }, + "_", + _hc08_init, + _hc08_parseOptions, + _hc08_options, + NULL, + _hc08_finaliseOptions, + _hc08_setDefaultOptions, + hc08_assignRegisters, + _hc08_getRegName, + 0, + NULL, + _hc08_keywords, + _hc08_genAssemblerPreamble, + _hc08_genAssemblerEnd, /* no genAssemblerEnd */ + _hc08_genIVT, + _hc08_genXINIT, + NULL, /* genInitStartup */ + _hc08_reset_regparm, + _hc08_regparm, + NULL, /* process_pragma */ + NULL, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + TRUE, /* use_dw_for_init */ + FALSE, /* little_endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + cseCostEstimation, + NULL, /* no builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 3, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +PORT s08_port = +{ + TARGET_ID_S08, + "s08", + "S08", /* Target name */ + NULL, /* Processor name */ + { + glue, + FALSE, /* Emit glue around main */ + MODEL_SMALL | MODEL_LARGE, + MODEL_LARGE, + NULL, /* model == target */ + }, + { + _asmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm", + NULL /* no do_assemble function */ + }, + { /* Linker */ + _linkCmd, + NULL, + NULL, + ".rel", + 1, + NULL, /* crt */ + _libs_s08, /* libs */ + }, + { /* Peephole optimizer */ + _s08_defaultRules, + hc08_getInstructionSize, + }, + { + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + 1, 2, 2, 4, 8, 2, 2, 2, 2, 0, 1, 4 + }, + /* tags for generic pointers */ + { 0x00, 0x00, 0x00, 0x00 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CSEG (CODE)", + "DSEG (PAG)", + NULL, /* "ISEG" */ + NULL, /* "PSEG" */ + "XSEG", + NULL, /* "BSEG" */ + "RSEG (ABS)", + "GSINIT (CODE)", + "OSEG (PAG, OVR)", + "GSFINAL (CODE)", + "HOME (CODE)", + "XISEG", // initialized xdata + "XINIT (CODE)", // a code copy of xiseg + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS)", // xabs_name - absolute xdata + "IABS (ABS)", // iabs_name - absolute data + NULL, // name of segment for initialized variables + NULL, // name of segment for copies of initialized variables in code space + NULL, + NULL, + 1, + 1 // No fancy alignments supported. + }, + { _hc08_genExtraAreas, + NULL }, + { + -1, /* direction (-1 = stack grows down) */ + 0, /* bank_overhead (switch between register banks) */ + 4, /* isr_overhead */ + 2, /* call_overhead */ + 0, /* reent_overhead */ + 0, /* banked_overhead (switch between code banks) */ + 1 /* sp is offset by 1 from last item pushed */ + }, + { + 5, FALSE + }, + { + hc08_emitDebuggerSymbol, + { + hc08_dwarfRegNum, + NULL, + NULL, + 4, /* addressSize */ + 14, /* regNumRet */ + 15, /* regNumSP */ + -1, /* regNumBP */ + 1, /* offsetSP */ + }, + }, + { + 256, /* maxCount */ + 2, /* sizeofElement */ + {8,16,32}, /* sizeofMatchJump[] */ + {8,16,32}, /* sizeofRangeCompare[] */ + 5, /* sizeofSubtract */ + 10, /* sizeofDispatch */ + }, + "_", + _s08_init, + _hc08_parseOptions, + _hc08_options, + NULL, + _hc08_finaliseOptions, + _hc08_setDefaultOptions, + hc08_assignRegisters, + _hc08_getRegName, + 0, + NULL, + _hc08_keywords, + _hc08_genAssemblerPreamble, + _hc08_genAssemblerEnd, /* no genAssemblerEnd */ + _hc08_genIVT, + _hc08_genXINIT, + NULL, /* genInitStartup */ + _hc08_reset_regparm, + _hc08_regparm, + NULL, /* process_pragma */ + NULL, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + TRUE, /* use_dw_for_init */ + FALSE, /* little_endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + cseCostEstimation, + NULL, /* no builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 3, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + diff --git a/src/hc08/main.h b/src/hc08/main.h new file mode 100644 index 0000000..6555225 --- /dev/null +++ b/src/hc08/main.h @@ -0,0 +1,8 @@ +#ifndef MAIN_INCLUDE +#define MAIN_INCLUDE + +bool x_parseOptions (char **argv, int *pargc); +void x_setDefaultOptions (void); +void x_finaliseOptions (void); + +#endif diff --git a/src/hc08/peeph.def b/src/hc08/peeph.def new file mode 100644 index 0000000..745c744 --- /dev/null +++ b/src/hc08/peeph.def @@ -0,0 +1,278 @@ +replace { + pula + psha + lda %1 +} by { + ; Peephole 1a - removed redundant pula/psha + lda %1 +} + +replace { + pula + psha + lda %1,s +} by { + ; Peephole 1b - removed redundant pula/psha + lda %1,s +} + +replace { + pula + psha + clra +} by { + ; Peephole 1c - removed redundant pula/psha + clra +} + + +replace { + bcs %1 + jmp %5 +%1: +} by { + ; Peephole 2a - eliminated jmp + bcc %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bcc %1 + jmp %5 +%1: +} by { + ; Peephole 2b - eliminated jmp + bcs %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + beq %1 + jmp %5 +%1: +} by { + ; Peephole 2c - eliminated jmp + bne %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bne %1 + jmp %5 +%1: +} by { + ; Peephole 2d - eliminated jmp + beq %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + brclr %3,%2,%1 + jmp %5 +%1: +} by { + ; Peephole 2e - eliminated jmp + brset %3,%2,%5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + brset %3,%2,%1 + jmp %5 +%1: +} by { + ; Peephole 2f - eliminated jmp + brclr %3,%2,%5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bls %1 + jmp %5 +%1: +} by { + ; Peephole 2g - eliminated jmp + bhi %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bhi %1 + jmp %5 +%1: +} by { + ; Peephole 2h - eliminated jmp + bls %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + ble %1 + jmp %5 +%1: +} by { + ; Peephole 2i - eliminated jmp + bgt %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bgt %1 + jmp %5 +%1: +} by { + ; Peephole 2j - eliminated jmp + ble %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bge %1 + jmp %5 +%1: +} by { + ; Peephole 2k - eliminated jmp + blt %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + blt %1 + jmp %5 +%1: +} by { + ; Peephole 2l - eliminated jmp + bge %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bmi %1 + jmp %5 +%1: +} by { + ; Peephole 2m - eliminated jmp + bpl %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bpl %1 + jmp %5 +%1: +} by { + ; Peephole 2n - eliminated jmp + bmi %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace restart { + jmp %5 +} by { + ; Peephole 3 - shortened jmp to bra + bra %5 +} if labelInRange(%5) + +replace { + lda %1 + tsta +} by { + ; Peephole 4a - eliminated redundant tsta + lda %1 +} + +replace { + ldx %1 + tstx +} by { + ; Peephole 4b - eliminated redundant tstx + ldx %1 +} + +replace { + jmp %5 +} by { + ; Peephole 6a - replaced jmp to rts with rts + rts +} if optimizeReturn(), labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +replace { + bra %5 +} by { + ; Peephole 6b - replaced jmp to rts with rts + rts +} if optimizeReturn(), labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +replace { + bne %1 +%1: +} by { + ; Peephole 7a - eliminated branch +%1: +} if labelRefCountChange(%1 -1) + +replace { + beq %1 +%1: +} by { + ; Peephole 7b - eliminated branch +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + bra %1 +%1: +} by { + ; Peephole 7c - eliminated branch +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + bra %1 +%2: +%1: +} by { + ; Peephole 7d - eliminated branch +%2: +%1: +} if labelRefCountChange(%1 -1) + +replace { + cmp #%2 + beq %1 +} by { + ; Peephole 10a - combined cmp/beq + cbeqa #%2,%1 +} + +replace { + cmp *%2 + beq %1 +} by { + ; Peephole 10b - combined cmp/beq + cbeq *%2,%1 +} + +replace { + dec %2,s + bne %1 +} by { + ; Peephole 10c - combined dec/bne + dbnz %2,s,%1 +} + +// should be one of the last peepholes +replace restart { +%1: +} by { + ; Peephole 500 removed redundant label %1 +} if labelRefCount(%1 0) + +replace restart { + jsr %1 + rts +} by { + ; Peephole 501 did tail call optimization. + jmp %1 +} if optimizeReturn() + diff --git a/src/hc08/ralloc.c b/src/hc08/ralloc.c new file mode 100644 index 0000000..d6f9573 --- /dev/null +++ b/src/hc08/ralloc.c @@ -0,0 +1,2423 @@ +/*------------------------------------------------------------------------ + + SDCCralloc.c - source file for register allocation. 68HC08 specific + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + 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. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +/*-----------------------------------------------------------------*/ +/* At this point we start getting processor specific although */ +/* some routines are non-processor specific & can be reused when */ +/* targetting other processors. The decision for this will have */ +/* to be made on a routine by routine basis */ +/* routines used to pack registers are most definitely not reusable */ +/* since the pack the registers depending strictly on the MCU */ +/*-----------------------------------------------------------------*/ + +extern void genhc08Code (iCode *); + +#define D(x) + +// Build the old allocator. It can be used by command-line options +#define OLDRALLOC 1 + +/* Global data */ +static struct + { + bitVect *spiltSet; + set *stackSpil; + bitVect *regAssigned; + bitVect *totRegAssigned; /* final set of LRs that got into registers */ + short blockSpil; + int slocNum; + bitVect *funcrUsed; /* registers used in a function */ + int stackExtend; + int dataExtend; + } +_G; + +/* Shared with gen.c */ +int hc08_ptrRegReq; /* one byte pointer register required */ + +/* 6808 registers */ +reg_info regshc08[] = +{ + + {REG_GPR, A_IDX, "a", HC08MASK_A, NULL, 0, 1}, + {REG_GPR, X_IDX, "x", HC08MASK_X, NULL, 0, 1}, + {REG_GPR, H_IDX, "h", HC08MASK_H, NULL, 0, 1}, + {REG_PTR, HX_IDX, "hx", HC08MASK_HX, NULL, 0, 1}, + {REG_GPR, XA_IDX, "xa", HC08MASK_XA, NULL, 0, 1}, + + {REG_CND, CND_IDX, "C", 0, NULL, 0, 1}, + {0, SP_IDX, "sp", 0, NULL, 0, 1}, +}; +int hc08_nRegs = 7; + +reg_info *hc08_reg_a; +reg_info *hc08_reg_x; +reg_info *hc08_reg_h; +reg_info *hc08_reg_hx; +reg_info *hc08_reg_xa; +reg_info *hc08_reg_sp; + +static void spillThis (symbol *); +static void freeAllRegs (); + +/*-----------------------------------------------------------------*/ +/* hc08_regWithIdx - returns pointer to register with index number */ +/*-----------------------------------------------------------------*/ +reg_info * +hc08_regWithIdx (int idx) +{ + int i; + + for (i = 0; i < hc08_nRegs; i++) + if (regshc08[i].rIdx == idx) + return ®shc08[i]; + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "regWithIdx not found"); + exit (1); +} + +/*-----------------------------------------------------------------*/ +/* hc08_freeReg - frees a register */ +/*-----------------------------------------------------------------*/ +void +hc08_freeReg (reg_info * reg) +{ + if (!reg) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "hc08_freeReg - Freeing NULL register"); + exit (1); + } + + reg->isFree = 1; + + switch (reg->rIdx) + { + case A_IDX: + if (hc08_reg_x->isFree) + hc08_reg_xa->isFree = 1; + break; + case X_IDX: + if (hc08_reg_a->isFree) + hc08_reg_xa->isFree = 1; + if (hc08_reg_h->isFree) + hc08_reg_hx->isFree = 1; + break; + case H_IDX: + if (hc08_reg_x->isFree) + hc08_reg_hx->isFree = 1; + break; + case HX_IDX: + hc08_reg_h->isFree = 1; + hc08_reg_x->isFree = 1; + if (hc08_reg_a->isFree) + hc08_reg_xa->isFree = 1; + break; + case XA_IDX: + hc08_reg_x->isFree = 1; + hc08_reg_a->isFree = 1; + if (hc08_reg_h->isFree) + hc08_reg_hx->isFree = 1; + break; + default: + break; + } +} + + +/*-----------------------------------------------------------------*/ +/* hc08_useReg - marks a register as used */ +/*-----------------------------------------------------------------*/ +void +hc08_useReg (reg_info * reg) +{ + reg->isFree = 0; + + switch (reg->rIdx) + { + case A_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isFree = 0; + break; + case X_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isFree = 0; + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isFree = 0; + break; + case H_IDX: + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isFree = 0; + break; + case HX_IDX: + hc08_reg_h->aop = NULL; + hc08_reg_h->isFree = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isFree = 0; + break; + case XA_IDX: + hc08_reg_x->aop = NULL; + hc08_reg_x->isFree = 0; + hc08_reg_a->aop = NULL; + hc08_reg_a->isFree = 0; + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* hc08_dirtyReg - marks a register as dirty */ +/*-----------------------------------------------------------------*/ +void +hc08_dirtyReg (reg_info * reg, bool freereg) +{ + reg->aop = NULL; + + switch (reg->rIdx) + { + case A_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isLitConst = 0; + hc08_reg_a->aop = NULL; + hc08_reg_a->isLitConst = 0; + break; + case X_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isLitConst = 0; + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isLitConst = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isLitConst = 0; + break; + case H_IDX: + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isLitConst = 0; + hc08_reg_h->aop = NULL; + hc08_reg_h->isLitConst = 0; + break; + case HX_IDX: + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isLitConst = 0; + hc08_reg_h->aop = NULL; + hc08_reg_h->isLitConst = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isLitConst = 0; + break; + case XA_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isLitConst = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isLitConst = 0; + hc08_reg_a->aop = NULL; + hc08_reg_a->isLitConst = 0; + break; + default: + break; + } + if (freereg) + hc08_freeReg(reg); +} + +/*-----------------------------------------------------------------*/ +/* noOverLap - will iterate through the list looking for over lap */ +/*-----------------------------------------------------------------*/ +static int +noOverLap (set * itmpStack, symbol * fsym) +{ + symbol *sym; + + for (sym = setFirstItem (itmpStack); sym; + sym = setNextItem (itmpStack)) + { + if (bitVectBitValue(sym->clashes,fsym->key)) return 0; + } + return 1; +} + +/*-----------------------------------------------------------------*/ +/* isFree - will return 1 if the a free spil location is found */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (isFree) +{ + symbol *sym = item; + V_ARG (symbol **, sloc); + V_ARG (symbol *, fsym); + + /* if already found */ + if (*sloc) + return 0; + + /* if it is free && and the itmp assigned to + this does not have any overlapping live ranges + with the one currently being assigned and + the size can be accomodated */ + if (sym->isFree && + noOverLap (sym->usl.itmpStack, fsym) && + getSize (sym->type) >= getSize (fsym->type)) + { + *sloc = sym; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location on the stack to spil */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol * sym) +{ + symbol *sloc = NULL; + struct dbuf_s dbuf; + int useXstack, model; + + /* first go try and find a free one that is already + existing on the stack */ + if (applyToSet (_G.stackSpil, isFree, &sloc, sym)) + { + /* found a free one : just update & return */ + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + sloc->isFree = 0; + addSetHead (&sloc->usl.itmpStack, sym); + return sym; + } + + /* could not then have to create one , this is the hard part + we need to allocate this on the stack : this is really a + hack!! but cannot think of anything better at this time */ + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "sloc%d", _G.slocNum++); + sloc = newiTemp (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + SPEC_SCLS (sloc->etype) = S_DATA; + SPEC_EXTR (sloc->etype) = 0; + SPEC_STAT (sloc->etype) = 0; + SPEC_VOLATILE(sloc->etype) = 0; + SPEC_ABSA(sloc->etype) = 0; + + /* we don't allow it to be allocated + onto the external stack since : so we + temporarily turn it off ; we also + turn off memory model to prevent + the spil from going to the external storage + */ + + useXstack = options.useXstack; + model = options.model; +/* noOverlay = options.noOverlay; */ +/* options.noOverlay = 1; */ + options.model = options.useXstack = 0; + + allocLocal (sloc); + + options.useXstack = useXstack; + options.model = model; +/* options.noOverlay = noOverlay; */ + sloc->isref = 1; /* to prevent compiler warning */ + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) + { + currFunc->stack += getSize (sloc->type); + _G.stackExtend += getSize (sloc->type); + } + else + _G.dataExtend += getSize (sloc->type); + + /* add it to the _G.stackSpil set */ + addSetHead (&_G.stackSpil, sloc); + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + + /* add it to the set of itempStack set + of the spill location */ + addSetHead (&sloc->usl.itmpStack, sym); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +static void +spillThis (symbol * sym) +{ + int i; + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ + if (!(sym->remat || sym->usl.spillLoc)) + createStackSpil (sym); + + /* mark it as spilt & put it in the spilt set */ + sym->isspilt = sym->spillA = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, sym->key); + + bitVectUnSetBit (_G.regAssigned, sym->key); + bitVectUnSetBit (_G.totRegAssigned, sym->key); + + for (i = 0; i < sym->nRegs; i++) + { + if (sym->regs[i]) + { + hc08_freeReg (sym->regs[i]); + sym->regs[i] = NULL; + } + } + + if (sym->usl.spillLoc && !sym->remat) + sym->usl.spillLoc->allocreq++; + return; +} + +/*-----------------------------------------------------------------*/ +/* updateRegUsage - update the registers in use at the start of */ +/* this icode */ +/*-----------------------------------------------------------------*/ +static void +updateRegUsage (iCode * ic) +{ + int reg; + + // update the registers in use at the start of this icode + for (reg=0; reg<hc08_nRegs; reg++) + { + if (regshc08[reg].isFree) + { + ic->riu &= ~(regshc08[reg].mask); + } + else + { + ic->riu |= (regshc08[reg].mask); + } + } +} + +/*-----------------------------------------------------------------*/ +/* deassignLRs - check the live to and if they have registers & are */ +/* not spilt then free up the registers */ +/*-----------------------------------------------------------------*/ +static void +deassignLRs (iCode * ic, eBBlock * ebp) +{ + symbol *sym; + int k; + + for (sym = hTabFirstItem (liveRanges, &k); sym; + sym = hTabNextItem (liveRanges, &k)) + { + /* if it does not end here */ + if (sym->liveTo > ic->seq) + continue; + + /* if it was spilt on stack then we can + mark the stack spil location as free */ + if (sym->isspilt) + { + if (sym->stackSpil) + { + sym->usl.spillLoc->isFree = 1; + sym->stackSpil = 0; + } + continue; + } + } +} + + +/*-----------------------------------------------------------------*/ +/* reassignLR - reassign this to registers */ +/*-----------------------------------------------------------------*/ +static void +reassignLR (operand * op) +{ + symbol *sym = OP_SYMBOL (op); + int i; + + /* not spilt any more */ + sym->isspilt = sym->spillA = sym->blockSpil = sym->remainSpil = 0; + bitVectUnSetBit (_G.spiltSet, sym->key); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + + _G.blockSpil--; + + for (i = 0; i < sym->nRegs; i++) + sym->regs[i]->isFree = 0; +} + +/*------------------------------------------------------------------*/ +/* verifyRegsAssigned - make sure an iTemp is properly initialized; */ +/* it should either have registers or have beed spilled. Otherwise, */ +/* there was an uninitialized variable, so just spill this to get */ +/* the operand in a valid state. */ +/*------------------------------------------------------------------*/ +static void +verifyRegsAssigned (operand *op, iCode * ic) +{ + symbol * sym; + + if (!op) return; + if (!IS_ITEMP (op)) return; + + sym = OP_SYMBOL (op); + if (sym->isspilt) return; + if (!sym->nRegs) return; + if (sym->regs[0]) return; + + /* Don't warn for new allocator, since this is not used by default (until Thoruop is implemented for spillocation compaction). */ + /*if (z80_opts.oldralloc) + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, sym->prereqv ? sym->prereqv->name : sym->name);*/ + + spillThis (sym); +} + + +/*-----------------------------------------------------------------*/ +/* serialRegAssign - serially allocate registers to the variables */ +/*-----------------------------------------------------------------*/ +static void +serialRegAssign (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + updateRegUsage(ic); + + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + reassignLR (IC_LEFT (ic)); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && + IS_TRUE_SYMOP (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + } + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + (IC_RESULT (ic) && POINTER_SET (ic))) + { + continue; + } + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + + /* Make sure any spill location is definitely allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && + !sym->usl.spillLoc->allocreq) + { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || + bitVectBitValue (_G.regAssigned, sym->key) || + sym->liveTo <= ic->seq) + { + continue; + } + + spillThis (sym); + } + } + } + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + { + continue; + } + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +/*-----------------------------------------------------------------*/ +/* rUmaskForOp :- returns register mask for an operand */ +/*-----------------------------------------------------------------*/ +bitVect * +hc08_rUmaskForOp (operand * op) +{ + bitVect *rumask; + symbol *sym; + int j; + + /* only temporaries are assigned registers */ + if (!IS_ITEMP (op)) + return NULL; + + sym = OP_SYMBOL (op); + + /* if spilt or no registers assigned to it + then nothing */ + if (sym->isspilt || !sym->nRegs) + return NULL; + + rumask = newBitVect (hc08_nRegs); + + for (j = 0; j < sym->nRegs; j++) + { + rumask = bitVectSetBit (rumask, sym->regs[j]->rIdx); + } + + return rumask; +} + +/*-----------------------------------------------------------------*/ +/* regsUsedIniCode :- returns bit vector of registers used in iCode */ +/*-----------------------------------------------------------------*/ +static bitVect * +regsUsedIniCode (iCode * ic) +{ + bitVect *rmask = newBitVect (hc08_nRegs); + + /* do the special cases first */ + if (ic->op == IFX) + { + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_COND (ic))); + goto ret; + } + + /* for the jumptable */ + if (ic->op == JUMPTABLE) + { + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_JTCOND (ic))); + goto ret; + } + + /* of all other cases */ + if (IC_LEFT (ic)) + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_LEFT (ic))); + + if (IC_RIGHT (ic)) + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_RIGHT (ic))); + + if (IC_RESULT (ic)) + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_RESULT (ic))); + +ret: + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* createRegMask - for each instruction will determine the regsUsed */ +/*-----------------------------------------------------------------*/ +static void +createRegMask (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + int j; + + if (SKIP_IC2 (ic) || !ic->rlive) + continue; + + /* first mark the registers used in this + instruction */ + ic->rSurv = newBitVect(port->num_regs); + ic->rUsed = regsUsedIniCode (ic); + _G.funcrUsed = bitVectUnion (_G.funcrUsed, ic->rUsed); + + /* now create the register mask for those + registers that are in use : this is a + super set of ic->rUsed */ + ic->rMask = newBitVect (hc08_nRegs + 1); + + /* for all live Ranges alive at this point */ + for (j = 1; j < ic->rlive->size; j++) + { + symbol *sym; + int k; + + /* if not alive then continue */ + if (!bitVectBitValue (ic->rlive, j)) + continue; + + /* find the live range we are interested in */ + if (!(sym = hTabItemWithKey (liveRanges, j))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "createRegMask cannot find live range"); + fprintf(stderr, "\tmissing live range: key=%d\n", j); + exit (0); + } + + /* if no register assigned to it */ + if (!sym->nRegs || sym->isspilt) + continue; + + /* for all the registers allocated to it */ + for (k = 0; k < sym->nRegs; k++) + { + if (!sym->regs[k]) + continue; + ic->rMask = bitVectSetBit (ic->rMask, sym->regs[k]->rIdx); + if (sym->liveTo != ic->key) + ic->rSurv = bitVectSetBit (ic->rSurv, sym->regs[k]->rIdx); + } + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum (eBBlock *ebbs) +{ + symbol *sym; + int k; + + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; + sym = hTabNextItem (liveRanges, &k)) + { + /* if used zero times then no registers needed */ + if ((sym->liveTo - sym->liveFrom) == 0) + continue; + + /* if the live range is a temporary */ + if (sym->isitmp) + { + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->ruonly || sym->accuse) + { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = aggrToPtr (sym->type, FALSE); + continue; + } + + /* if not then we require registers */ + sym->nRegs = ((IS_AGGREGATE (sym->type) || sym->isptr) ? + getSize (sym->type = aggrToPtr (sym->type, FALSE)) : + getSize (sym->type)); + + if (sym->nRegs > 8) + { + fprintf (stderr, "allocated more than 8 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + if (sym->nRegs == 1 && IS_PTR (sym->type) && sym->uptr) + sym->regType = REG_PTR; + else + sym->regType = REG_GPR; + } + else + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + sym->nRegs = 0; + } +} + +/*-----------------------------------------------------------------*/ +/* freeAllRegs - mark all registers as free */ +/*-----------------------------------------------------------------*/ +static void +freeAllRegs () +{ + int i; + + for (i = 0; i < hc08_nRegs; i++) + { + regshc08[i].isFree = 1; + regshc08[i].aop = NULL; + } +} + +/*-----------------------------------------------------------------*/ +/* deallocStackSpil - this will set the stack pointer back */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (deallocStackSpil) +{ + symbol *sym = item; + + deallocLocal (sym); + return 0; +} + + +#if 0 +static void +packRegsForLiteral (iCode * ic) +{ + int k; + iCode *uic; + + if (ic->op != '=') + return; + if (POINTER_SET (ic)) + return; + if (!IS_LITERAL (getSpec (operandType (IC_RIGHT (ic))))) + return; + if (bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) > 1) + return; + + for (k=0; k< OP_USES (IC_RESULT (ic))->size; k++) + if (bitVectBitValue (OP_USES (IC_RESULT (ic)), k)) + { + uic = hTabItemWithKey (iCodehTab, k); + if (!uic) continue; + + if (uic->op != IFX && uic->op != JUMPTABLE) + { + if (IC_LEFT (uic) && IC_LEFT (uic)->key == IC_RESULT (ic)->key) + ReplaceOpWithCheaperOp(&IC_LEFT(uic), IC_RIGHT(ic)); + if (IC_RIGHT (uic) && IC_RIGHT (uic)->key == IC_RESULT (ic)->key) + ReplaceOpWithCheaperOp(&IC_RIGHT(uic), IC_RIGHT(ic)); + if (IC_RESULT (uic) && IC_RESULT (uic)->key == IC_RESULT (ic)->key) + ReplaceOpWithCheaperOp(&IC_RESULT(uic), IC_RIGHT(ic)); + } + } + +} +#endif + + +/*-----------------------------------------------------------------*/ +/* packRegsForAssign - register reduction for assignment */ +/*-----------------------------------------------------------------*/ +static int +packRegsForAssign (iCode * ic, eBBlock * ebp) +{ + iCode *dic, *sic; + + if (!IS_ITEMP (IC_RIGHT (ic)) || + OP_SYMBOL (IC_RIGHT (ic))->isind || + OP_LIVETO (IC_RIGHT (ic)) > ic->seq) + { + return 0; + } + + /* if the true symbol is defined in far space or on stack + then we should not since this will increase register pressure */ +#if 0 + if (isOperandInFarSpace(IC_RESULT(ic)) && !farSpacePackable(ic)) + { + return 0; + } +#endif + + /* find the definition of iTempNN scanning backwards if we find + a use of the true symbol before we find the definition then + we cannot */ + for (dic = ic->prev; dic; dic = dic->prev) + { + int crossedCall = 0; + + /* We can pack across a function call only if it's a local */ + /* variable or our parameter. Never pack global variables */ + /* or parameters to a function we call. */ + if ((dic->op == CALL || dic->op == PCALL)) + { + if (!OP_SYMBOL (IC_RESULT (ic))->ismyparm + && !OP_SYMBOL (IC_RESULT (ic))->islocal) + { + crossedCall = 1; + } + } + + /* Don't move an assignment out of a critical block */ + if (dic->op == CRITICAL) + { + dic = NULL; + break; + } + + if (SKIP_IC2 (dic)) + continue; + + if (dic->op == IFX) + { + if (IS_SYMOP (IC_COND (dic)) && + (IC_COND (dic)->key == IC_RESULT (ic)->key || + IC_COND (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + } + else + { + if (IS_TRUE_SYMOP (IC_RESULT (dic)) && + IS_OP_VOLATILE (IC_RESULT (dic))) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + if (POINTER_SET (dic)) + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RIGHT (dic)) && + (IC_RIGHT (dic)->key == IC_RESULT (ic)->key || + IC_RIGHT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_LEFT (dic)) && + (IC_LEFT (dic)->key == IC_RESULT (ic)->key || + IC_LEFT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (POINTER_SET (dic) && + IC_RESULT (dic)->key == IC_RESULT (ic)->key) + { + dic = NULL; + break; + } + + if (crossedCall) + { + dic = NULL; + break; + } + } + } + + if (!dic) + return 0; /* did not find */ + + /* if assignment then check that right is not a bit */ + if (ASSIGNMENT (ic) && !POINTER_SET (ic)) + { + sym_link *etype = operandType (IC_RESULT (dic)); + if (IS_BITFIELD (etype)) + { + /* if result is a bit too then it's ok */ + etype = operandType (IC_RESULT (ic)); + if (!IS_BITFIELD (etype)) + { + return 0; + } + } + } + + /* found the definition */ + + /* delete from liverange table also + delete from all the points inbetween and the new + one */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_RESULT (ic)->key); + if (IS_ITEMP (IC_RESULT (dic))) + bitVectSetBit (sic->rlive, IC_RESULT (dic)->key); + } + + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + ReplaceOpWithCheaperOp (&IC_RESULT (dic), IC_RESULT (ic)); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + { + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + } + // TODO: and the otherway around? + + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + return 1; +} + +/*------------------------------------------------------------------*/ +/* findAssignToSym : scanning backwards looks for first assig found */ +/*------------------------------------------------------------------*/ +static iCode * +findAssignToSym (operand * op, iCode * ic) +{ + iCode *dic; + + /* This routine is used to find sequences like + iTempAA = FOO; + ...; (intervening ops don't use iTempAA or modify FOO) + blah = blah + iTempAA; + + and eliminate the use of iTempAA, freeing up its register for + other uses. + */ + for (dic = ic->prev; dic; dic = dic->prev) + { + if (dic->op == '=' && + !POINTER_SET (dic) && + IC_RESULT (dic)->key == op->key + && IS_TRUE_SYMOP(IC_RIGHT(dic)) + ) + break; /* found where this temp was defined */ + + /* if we find an usage then we cannot delete it */ + if (IC_LEFT (dic) && IC_LEFT (dic)->key == op->key) + return NULL; + + if (IC_RIGHT (dic) && IC_RIGHT (dic)->key == op->key) + return NULL; + + if (POINTER_SET (dic) && IC_RESULT (dic)->key == op->key) + return NULL; + } + + if (!dic) + return NULL; /* didn't find any assignment to op */ + /* we are interested only if defined in far space */ + /* or in stack space in case of + & - */ + + /* if assigned to a non-symbol then don't repack regs */ + if (!IS_SYMOP (IC_RIGHT (dic))) + return NULL; + + /* if the symbol's address has been taken, there might be a */ + /* non-obvious assignment to it, and so we should not */ + if (OP_SYMBOL (IC_RIGHT (dic))->addrtaken) + return NULL; + + /* if the symbol is volatile then we should not */ + if (isOperandVolatile (IC_RIGHT (dic), TRUE)) + return NULL; + /* XXX TODO --- should we be passing FALSE to isOperandVolatile()? + What does it mean for an iTemp to be volatile, anyway? Passing + TRUE is more cautious but may prevent possible optimizations */ + + /* if the symbol is in far space then we should not */ + /* if (isOperandInFarSpace (IC_RIGHT (dic))) + return NULL; */ + + + /* now make sure that the right side of dic + is not defined between ic & dic */ + if (dic) + { + iCode *sic = dic->next; + + for (; sic != ic; sic = sic->next) + if (IC_RESULT (sic) && + IC_RESULT (sic)->key == IC_RIGHT (dic)->key) + return NULL; + } + + return dic; +} + +/*-----------------------------------------------------------------*/ +/* reassignAliasedSym - used by packRegsForSupport to replace */ +/* redundant iTemp with equivalent symbol */ +/*-----------------------------------------------------------------*/ +static void +reassignAliasedSym (eBBlock *ebp, iCode *assignment, iCode *use, operand *op) +{ + iCode *ic; + unsigned oldSymKey, newSymKey; + + oldSymKey = op->key; + newSymKey = IC_RIGHT(assignment)->key; + + /* only track live ranges of compiler-generated temporaries */ + if (!IS_ITEMP(IC_RIGHT(assignment))) + newSymKey = 0; + + /* update the live-value bitmaps */ + for (ic = assignment; ic != use; ic = ic->next) { + bitVectUnSetBit (ic->rlive, oldSymKey); + if (newSymKey != 0) + ic->rlive = bitVectSetBit (ic->rlive, newSymKey); + } + + /* update the sym of the used operand */ + OP_SYMBOL(op) = OP_SYMBOL(IC_RIGHT(assignment)); + op->key = OP_SYMBOL(op)->key; + + /* update the sym's liverange */ + if ( OP_LIVETO(op) < ic->seq ) + setToRange(op, ic->seq, FALSE); + + /* remove the assignment iCode now that its result is unused */ + remiCodeFromeBBlock (ebp, assignment); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(assignment))->defs, assignment->key); + hTabDeleteItem (&iCodehTab, assignment->key, assignment, DELETE_ITEM, NULL); +} + + +/*-----------------------------------------------------------------*/ +/* packRegsForSupport :- reduce some registers for support calls */ +/*-----------------------------------------------------------------*/ +static int +packRegsForSupport (iCode * ic, eBBlock * ebp) +{ + iCode *dic; + int changes = 0; + + /* for the left & right operand :- look to see if the + left was assigned a true symbol in far space in that + case replace them */ + + if (IS_ITEMP (IC_LEFT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->liveTo <= ic->seq) + { + dic = findAssignToSym (IC_LEFT (ic), ic); + + if (dic) + { + /* found it we need to remove it from the block */ + reassignAliasedSym (ebp, dic, ic, IC_LEFT(ic)); + changes++; + } + } + + /* do the same for the right operand */ + if (IS_ITEMP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_RIGHT (ic), ic); + + if (dic) + { + /* found it we need to remove it from the block */ + reassignAliasedSym (ebp, dic, ic, IC_RIGHT(ic)); + changes++; + } + } + + return changes; +} + + +#if 0 +/*-----------------------------------------------------------------*/ +/* packRegsForOneuse : - will reduce some registers for single Use */ +/*-----------------------------------------------------------------*/ +static iCode * +packRegsForOneuse (iCode * ic, operand * op, eBBlock * ebp) +{ + bitVect *uses; + iCode *dic, *sic; + + /* if returning a literal then do nothing */ + if (!IS_SYMOP (op)) + return NULL; + + /* only up to 2 bytes */ + if (getSize (operandType (op)) > (fReturnSizeHC08 - 2)) + return NULL; + + return NULL; + + if (ic->op != SEND && //RETURN + ic->op != SEND && + !POINTER_SET (ic) && + !POINTER_GET (ic)) + return NULL; + + if (ic->op == SEND && ic->argreg != 1) + return NULL; + + /* this routine will mark the symbol as used in one + instruction use only && if the definition is local + (ie. within the basic block) && has only one definition && + that definition is either a return value from a + function or does not contain any variables in + far space */ + uses = bitVectCopy (OP_USES (op)); + bitVectUnSetBit (uses, ic->key); /* take away this iCode */ + if (!bitVectIsZero (uses)) /* has other uses */ + return NULL; + + /* if it has only one definition */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return NULL; /* has more than one definition */ + + /* get that definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))))) + return NULL; + + /* if that only usage is a cast */ + if (dic->op == CAST) + { + /* to a bigger type */ + if (getSize(OP_SYM_TYPE(IC_RESULT(dic))) > getSize(OP_SYM_TYPE(IC_RIGHT(dic)))) + { + /* then we can not, since we cannot predict the usage of b & acc */ + return NULL; + } + } + + /* found the definition now check if it is local */ + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return NULL; /* non-local */ + + /* now check if it is the return from a function call */ + if (dic->op == CALL || dic->op == PCALL) + { + if (ic->op != SEND && ic->op != RETURN && + !POINTER_SET(ic) && !POINTER_GET(ic)) + { + OP_SYMBOL (op)->ruonly = 1; + return dic; + } + dic = dic->next; + } + + +// /* otherwise check that the definition does +// not contain any symbols in far space */ +// if (isOperandInFarSpace (IC_LEFT (dic)) || +// isOperandInFarSpace (IC_RIGHT (dic)) || +// IS_OP_RUONLY (IC_LEFT (ic)) || +// IS_OP_RUONLY (IC_RIGHT (ic))) +// { +// return NULL; +// } + + /* if pointer set then make sure the pointer + is one byte */ +#if 0 + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; +#endif + + sic = dic; + + /* make sure the intervening instructions + don't have anything in far space */ + for (dic = dic->next; dic && dic != ic && sic != ic; dic = dic->next) + { + /* if there is an intervening function call then no */ + if (dic->op == CALL || dic->op == PCALL) + return NULL; + /* if pointer set then make sure the pointer + is one byte */ +#if 0 + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; +#endif + /* if address of & the result is remat then okay */ + if (dic->op == ADDRESS_OF && + OP_SYMBOL (IC_RESULT (dic))->remat) + continue; + + /* if operand has size of three or more & this + operation is a '*','/' or '%' then 'b' may + cause a problem */ +#if 0 + if ((dic->op == '%' || dic->op == '/' || dic->op == '*') && + getSize (operandType (op)) >= 3) + return NULL; +#endif + + /* if left or right or result is in far space */ +// if (isOperandInFarSpace (IC_LEFT (dic)) || +// isOperandInFarSpace (IC_RIGHT (dic)) || +// isOperandInFarSpace (IC_RESULT (dic)) || +// IS_OP_RUONLY (IC_LEFT (dic)) || +// IS_OP_RUONLY (IC_RIGHT (dic)) || +// IS_OP_RUONLY (IC_RESULT (dic))) +// { +// return NULL; +// } +// /* if left or right or result is on stack */ +// if (isOperandOnStack(IC_LEFT(dic)) || +// isOperandOnStack(IC_RIGHT(dic)) || +// isOperandOnStack(IC_RESULT(dic))) { +// return NULL; +// } + } + + OP_SYMBOL (op)->ruonly = 1; + return sic; +} +#endif + +/*-----------------------------------------------------------------*/ +/* isBitwiseOptimizable - requirements of JEAN LOUIS VERN */ +/*-----------------------------------------------------------------*/ +static bool +isBitwiseOptimizable (iCode * ic) +{ + sym_link *ltype = getSpec (operandType (IC_LEFT (ic))); + sym_link *rtype = getSpec (operandType (IC_RIGHT (ic))); + + /* bitwise operations are considered optimizable + under the following conditions (Jean-Louis VERN) + + x & lit + bit & bit + bit & x + bit ^ bit + bit ^ x + x ^ lit + x | lit + bit | bit + bit | x + */ + if (IS_LITERAL(rtype) || + (IS_BITVAR (ltype) && IN_BITSPACE (SPEC_OCLS (ltype)))) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* isCommutativeOp - tests whether this op cares what order its */ +/* operands are in */ +/*-----------------------------------------------------------------*/ +bool isCommutativeOp2(unsigned int op) +{ + if (op == '+' || op == '*' || op == EQ_OP || + op == '^' || op == '|' || op == BITWISEAND) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandUsesAcc2 - determines whether the code generated for this */ +/* operand will have to use the accumulator */ +/*-----------------------------------------------------------------*/ +bool operandUsesAcc2(operand *op) +{ + if (!op) + return FALSE; + + if (IS_SYMOP(op)) { + symbol *sym = OP_SYMBOL(op); + memmap *symspace; + + if (sym->accuse) + return TRUE; /* duh! */ + + if (IS_ITEMP(op)) + { + if (SPIL_LOC(op)) { + sym = SPIL_LOC(op); /* if spilled, look at spill location */ + } else { + return FALSE; /* more checks? */ + } + } + + symspace = SPEC_OCLS(sym->etype); + + if (IN_BITSPACE(symspace)) + return TRUE; /* fetching bit vars uses the accumulator */ + + if (IN_FARSPACE(symspace) || IN_CODESPACE(symspace)) + return TRUE; /* fetched via accumulator and dptr */ + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* canDefAccResult - return 1 if the iCode can generate a result */ +/* in A or XA */ +/*-----------------------------------------------------------------*/ +static int +canDefAccResult (iCode * ic) +{ + int size; + + if (ic->op == IFX || ic->op == JUMPTABLE) /* these iCodes have no result */ + return 0; + + if (POINTER_SET (ic)) + return 0; + + if (!IC_RESULT (ic)) + return 0; + + if (!IS_ITEMP (IC_RESULT (ic))) + return 0; + + /* I don't think an iTemp can be an aggregate, but just in case */ + if (IS_AGGREGATE(operandType(IC_RESULT(ic)))) + return 0; + + size = getSize (operandType (IC_RESULT (ic))); + + if (size == 1) + { + /* All 1 byte operations should safely generate an accumulator result */ + return 1; + } + else if (size == 2) + { + switch (ic->op) + { + case LEFT_OP: + case RIGHT_OP: + return isOperandLiteral (IC_RIGHT (ic)) + && SPEC_USIGN (operandType (IC_RESULT (ic))); + case CALL: + case PCALL: + case '*': + case RECEIVE: + case '=': /* assignment, since POINTER_SET is already ruled out */ + return 1; + + default: + return 0; + } + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* canUseAccOperand - return 1 if the iCode can use the operand */ +/* when passed in A or XA */ +/*-----------------------------------------------------------------*/ +static int +canUseAccOperand (iCode * ic, operand * op) +{ + int size; + + if (ic->op == IFX) + { + if (isOperandEqual (op, IC_COND (ic))) + return 1; + else + return 0; + } + + if (ic->op == JUMPTABLE) + { + if (isOperandEqual (op, IC_JTCOND (ic))) + return 1; + else + return 0; + } + + if (POINTER_SET (ic) && isOperandEqual (op, IC_RESULT (ic))) + return 1; + + if (!isOperandEqual (op, IC_LEFT (ic)) && !isOperandEqual (op, IC_RIGHT (ic))) + return 0; + + /* Generation of SEND is deferred until CALL; not safe */ + /* if there are intermediate iCodes */ + if (ic->op == SEND && ic->next && ic->next->op != CALL) + return 0; + + size = getSize (operandType (op)); + if (size == 1) + { + /* All 1 byte operations should safely use an accumulator operand */ + return 1; + } + else if (size == 2) + { + switch (ic->op) + { + case LEFT_OP: + case RIGHT_OP: + return isOperandLiteral (IC_RIGHT (ic)); + case SEND: + return 1; + default: + return 0; + } + } + + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* packRegsForAccUse - pack registers for acc use */ +/*-----------------------------------------------------------------*/ +static int +packRegsForAccUse (iCode * ic) +{ + iCode * uic; + operand * op; + + if (!canDefAccResult (ic)) + return 0; + + op = IC_RESULT (ic); + + /* has only one definition */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return 0; + + /* has only one use */ + if (bitVectnBitsOn (OP_USES (op)) > 1) + return 0; + + uic = ic->next; + if (!uic) + return 0; + + if (!canUseAccOperand (uic, op)) + return 0; + + #if 0 + if ((POINTER_GET(uic)) + || (ic->op == ADDRESS_OF && uic->op == '+' && IS_OP_LITERAL (IC_RIGHT (uic)))) + { + OP_SYMBOL (IC_RESULT (ic))->accuse = ACCUSE_HX; + return; + } + #endif + + OP_SYMBOL (IC_RESULT (ic))->accuse = ACCUSE_XA; + return 1; +} + +/*-----------------------------------------------------------------*/ +/* packForPush - heuristics to reduce iCode for pushing */ +/*-----------------------------------------------------------------*/ +static void +packForPush (iCode * ic, eBBlock ** ebpp, int blockno) +{ + iCode *dic, *lic; + bitVect *dbv; + struct eBBlock * ebp=ebpp[blockno]; + int disallowHiddenAssignment = 0; + + if ((ic->op != IPUSH && ic->op != SEND) || !IS_ITEMP (IC_LEFT (ic))) + return; + + /* must have only definition & one usage */ + if (bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) != 1 || + bitVectnBitsOn (OP_USES (IC_LEFT (ic))) != 1) + return; + + /* find the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_DEFS (IC_LEFT (ic)))))) + return; + + if (dic->op != '=' || POINTER_SET (dic)) + return; + + if (dic->seq < ebp->fSeq) // Evelyn did this + { + int i; + for (i=0; i<blockno; i++) + { + if (dic->seq >= ebpp[i]->fSeq && dic->seq <= ebpp[i]->lSeq) + { + ebp=ebpp[i]; + break; + } + } + wassert (i!=blockno); // no way to recover from here + } + + if (IS_SYMOP(IC_RIGHT(dic))) + { + if (IC_RIGHT (dic)->isvolatile) + return; + + if (OP_SYMBOL (IC_RIGHT (dic))->addrtaken || isOperandGlobal (IC_RIGHT (dic))) + disallowHiddenAssignment = 1; + + /* make sure the right side does not have any definitions + inbetween */ + dbv = OP_DEFS(IC_RIGHT(dic)); + for (lic = ic; lic && lic != dic ; lic = lic->prev) + { + if (bitVectBitValue(dbv,lic->key)) + return ; + if (disallowHiddenAssignment && (lic->op == CALL || lic->op == PCALL || POINTER_SET (lic))) + return; + } + /* make sure they have the same type */ + if (IS_SPEC(operandType(IC_LEFT(ic)))) + { + sym_link *itype=operandType(IC_LEFT(ic)); + sym_link *ditype=operandType(IC_RIGHT(dic)); + + if (SPEC_USIGN(itype)!=SPEC_USIGN(ditype) || + SPEC_LONG(itype)!=SPEC_LONG(ditype)) + return; + } + /* extend the live range of replaced operand if needed */ + if (OP_SYMBOL(IC_RIGHT(dic))->liveTo < ic->seq) + { + OP_SYMBOL(IC_RIGHT(dic))->liveTo = ic->seq; + } + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + } + if (IS_ITEMP (IC_RIGHT (dic))) + OP_USES (IC_RIGHT (dic)) = bitVectSetBit (OP_USES (IC_RIGHT (dic)), ic->key); + + /* we now we know that it has one & only one def & use + and the that the definition is an assignment */ + ReplaceOpWithCheaperOp(&IC_LEFT (ic), IC_RIGHT (dic)); + remiCodeFromeBBlock (ebp, dic); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); +} + +/*------------------------------------------------------------------*/ +/* moveSendToCall - move SEND to immediately precede its CALL/PCALL */ +/*------------------------------------------------------------------*/ +static iCode * +moveSendToCall (iCode *sic, eBBlock *ebp) +{ + iCode * prev = sic->prev; + iCode * sic2 = NULL; + iCode * cic; + + /* Go find the CALL/PCALL */ + cic = sic; + while (cic && cic->op != CALL && cic->op != PCALL) + cic = cic->next; + if (!cic) + return sic; + + /* Is there a second SEND? If so, we'll need to move it too. */ + if (sic->next->op == SEND) + sic2 = sic->next; + + /* relocate the SEND(s) */ + remiCodeFromeBBlock (ebp, sic); + addiCodeToeBBlock (ebp, sic, cic); + if (sic2) + { + remiCodeFromeBBlock (ebp, sic2); + addiCodeToeBBlock (ebp, sic2, cic); + } + + /* Return the iCode to continue processing at. */ + if (prev) + return prev->next; + else + return ebp->sch; +} + + +/*---------------------------------------------------------------------*/ +/* packPointerOp - see if we can move an offset from addition iCode */ +/* to the pointer iCode to used indexed addr mode */ +/* The z80-related ports do a similar thing in SDCCopt.c, offsetFold() */ +/*---------------------------------------------------------------------*/ +static void +packPointerOp (iCode * ic, eBBlock ** ebpp) +{ + operand * pointer; + operand * offsetOp; + operand * nonOffsetOp; + iCode * dic; + iCode * uic; + int key; + + if (POINTER_SET (ic)) + { + pointer = IC_RESULT (ic); + offsetOp = IC_LEFT (ic); + } + else if (POINTER_GET (ic)) + { + pointer = IC_LEFT (ic); + offsetOp = IC_RIGHT (ic); + } + else + return; + + if (!IS_ITEMP (pointer)) + return; + + /* If the pointer is rematerializable, it's already fully optimized */ + if (OP_SYMBOL (pointer)->remat) + return; + + if (offsetOp && IS_OP_LITERAL (offsetOp) && operandLitValue (offsetOp) != 0) + return; + if (offsetOp && IS_SYMOP (offsetOp)) + return; + + /* There must be only one definition */ + if (bitVectnBitsOn (OP_DEFS (pointer)) != 1) + return; + /* find the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (pointer))))) + return; + + if (dic->op == '+' && (IS_OP_LITERAL (IC_RIGHT (dic)) || + (IS_ITEMP (IC_RIGHT (dic)) && OP_SYMBOL (IC_RIGHT (dic))->remat))) + { + nonOffsetOp = IC_LEFT (dic); + offsetOp = IC_RIGHT (dic); + } + else if (dic->op == '+' && IS_ITEMP (IC_LEFT (dic)) && OP_SYMBOL (IC_LEFT (dic))->remat) + { + nonOffsetOp = IC_RIGHT (dic); + offsetOp = IC_LEFT (dic); + } + else + return; + + + /* Now check all of the uses to make sure they are all get/set pointer */ + /* and don't already have a non-zero offset operand */ + for (key=0; key<OP_USES (pointer)->size; key++) + { + if (bitVectBitValue (OP_USES (pointer), key)) + { + uic = hTabItemWithKey (iCodehTab, key); + if (POINTER_GET (uic)) + { + if (IC_RIGHT (uic) && IS_OP_LITERAL (IC_RIGHT (uic)) && operandLitValue (IC_RIGHT (uic)) != 0) + return; + if (IC_RIGHT (uic) && IS_SYMOP (IC_RIGHT (uic))) + return; + } + else if (POINTER_SET (uic)) + { + if (IC_LEFT (uic) && IS_OP_LITERAL (IC_LEFT (uic)) && operandLitValue (IC_LEFT (uic)) != 0) + return; + if (IC_LEFT (uic) && IS_SYMOP (IC_LEFT (uic))) + return; + } + else + return; + } + } + + /* Everything checks out. Move the literal or rematerializable offset */ + /* to the pointer get/set iCodes */ + for (key=0; key<OP_USES (pointer)->size; key++) + { + if (bitVectBitValue (OP_USES (pointer), key)) + { + uic = hTabItemWithKey (iCodehTab, key); + if (POINTER_GET (uic)) + { + IC_RIGHT (uic) = offsetOp; + if (IS_SYMOP (offsetOp)) + OP_USES (offsetOp) = bitVectSetBit (OP_USES (offsetOp), key); + } + else if (POINTER_SET (uic)) + { + IC_LEFT (uic) = offsetOp; + if (IS_SYMOP (offsetOp)) + OP_USES (offsetOp) = bitVectSetBit (OP_USES (offsetOp), key); + } + else + return; + } + } + + /* Put the remaining operand on the right and convert to assignment */ + if (IS_SYMOP (offsetOp)) + bitVectUnSetBit (OP_USES (offsetOp), dic->key); + IC_RIGHT (dic) = nonOffsetOp; + IC_LEFT (dic) = NULL; + SET_ISADDR (IC_RESULT (dic), 0); + dic->op = '='; +} + +/*-----------------------------------------------------------------*/ +/* packRegisters - does some transformations to reduce register */ +/* pressure */ +/*-----------------------------------------------------------------*/ +static void +packRegisters (eBBlock ** ebpp, int blockno) +{ + iCode *ic; + int change = 0; + eBBlock *ebp = ebpp[blockno]; + + do + { + change = 0; + + /* look for assignments of the form */ + /* iTempNN = TrueSym (someoperation) SomeOperand */ + /* .... */ + /* TrueSym := iTempNN:1 */ + for (ic = ebp->sch; ic; ic = ic->next) + { + /* find assignment of the form TrueSym := iTempNN:1 */ + if (ic->op == '=' && !POINTER_SET (ic)) + change += packRegsForAssign (ic, ebp); + } + } + while (change); + + for (ic = ebp->sch; ic; ic = ic->next) + { + //packRegsForLiteral (ic); + + /* move SEND to immediately precede its CALL/PCALL */ + if (ic->op == SEND && ic->next && + ic->next->op != CALL && ic->next->op != PCALL) + { + ic = moveSendToCall (ic, ebp); + } + + /* if this is an itemp & result of an address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && + IS_TRUE_SYMOP (IC_LEFT (ic)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_LEFT (ic))->onStack) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* if straight assignment then carry remat flag if + this is the only definition */ + if (ic->op == '=' && + !POINTER_SET (ic) && + IS_SYMOP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->remat && + bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) <= 1 && + !OP_SYMBOL (IC_RESULT (ic))->_isparm && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken && + !isOperandGlobal (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->remat = OP_SYMBOL (IC_RIGHT (ic))->remat; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + + /* if cast to a generic pointer & the pointer being + cast is remat, then we can remat this cast as well */ + if (ic->op == CAST && + IS_SYMOP(IC_RIGHT(ic)) && + OP_SYMBOL(IC_RIGHT(ic))->remat && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_RESULT (ic))->_isparm && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken && + !isOperandGlobal (IC_RESULT (ic))) + { + sym_link *to_type = operandType(IC_LEFT(ic)); + sym_link *from_type = operandType(IC_RIGHT(ic)); + if (IS_PTR(to_type) && IS_PTR(from_type)) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + } + + /* if this is a +/- operation with a rematerizable + then mark this as rematerializable as well */ + if ((ic->op == '+' || ic->op == '-') && + (IS_SYMOP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + IS_OP_LITERAL (IC_RIGHT (ic))) && + OP_SYMBOL (IC_LEFT (ic))->remat && + (!IS_SYMOP (IC_RIGHT (ic)) || !IS_CAST_ICODE(OP_SYMBOL (IC_RIGHT (ic))->rematiCode)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + /* mark the pointer usages */ + if (POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic))) + OP_SYMBOL (IC_RESULT (ic))->uptr = 1; + + if (POINTER_GET (ic) && IS_SYMOP (IC_LEFT (ic))) + OP_SYMBOL (IC_LEFT (ic))->uptr = 1; + + /* reduce for support function calls */ + if (ic->supportRtn || (ic->op != IFX && ic->op != JUMPTABLE)) + packRegsForSupport (ic, ebp); + + /* if the condition of an if instruction + is defined in the previous instruction and + this is the only usage then + mark the itemp as a conditional */ + if ((IS_CONDITIONAL (ic) || + (IS_BITWISE_OP(ic) && isBitwiseOptimizable (ic))) && + ic->next && ic->next->op == IFX && + bitVectnBitsOn (OP_USES(IC_RESULT(ic)))==1 && + isOperandEqual (IC_RESULT (ic), IC_COND (ic->next)) && + OP_SYMBOL (IC_RESULT (ic))->liveTo <= ic->next->seq) + { + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + continue; + } + + /* pack for PUSH + iTempNN := (some variable in farspace) V1 + push iTempNN ; + ------------- + push V1 + */ + if (ic->op == IPUSH || ic->op == SEND) + { + packForPush (ic, ebpp, blockno); + } + + if (POINTER_SET (ic) || POINTER_GET (ic)) + packPointerOp (ic, ebpp); + + if (options.oldralloc) + packRegsForAccUse (ic); + } +} + +static void +RegFix (eBBlock ** ebbs, int count) +{ + int i; + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + deassignLRs (ic, ebbs[i]); + + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +static void +replaceAccuseOperand (operand * op) +{ + symbol * sym; + + if (!IS_ITEMP (op)) + return; + + sym = OP_SYMBOL (op); + if (sym->remat || !sym->accuse) + return; + + sym->nRegs = getSize (operandType (op)); + wassert ((sym->accuse == ACCUSE_XA) || (sym->accuse == ACCUSE_HX)); + wassert (sym->nRegs <= 2); + + if (sym->accuse == ACCUSE_XA) + { + sym->regs[0] = hc08_reg_a; + if (sym->nRegs > 1) + sym->regs[1] = hc08_reg_x; + } + else /* must be sym->accuse == ACCUSE_HX */ + { + sym->regs[0] = hc08_reg_x; + if (sym->nRegs > 1) + sym->regs[1] = hc08_reg_h; + } + sym->accuse = 0; +} + + +/* Replace all uses of the sym->accuse flag with initialized */ +/* sym->regs[] and sym->nRegs so the current code generator */ +/* can work with the old register allocator. */ +static void +replaceAccuse (eBBlock ** ebbs, int count) +{ + int i; + + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + replaceAccuseOperand (IC_COND (ic)); + continue; + } + + if (ic->op == JUMPTABLE) + { + replaceAccuseOperand (IC_JTCOND (ic)); + continue; + } + + replaceAccuseOperand (IC_RESULT (ic)); + replaceAccuseOperand (IC_LEFT (ic)); + replaceAccuseOperand (IC_RIGHT (ic)); + } + } +} + +#ifdef OLDRALLOC +/*-----------------------------------------------------------------*/ +/* Old, obsolete register allocator */ +/*-----------------------------------------------------------------*/ +void +hc08_oldralloc (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + setToNull ((void *) &_G.funcrUsed); + setToNull ((void *) &_G.regAssigned); + setToNull ((void *) &_G.totRegAssigned); + hc08_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + hc08_nRegs = 7; + hc08_reg_a = hc08_regWithIdx(A_IDX); + hc08_reg_x = hc08_regWithIdx(X_IDX); + hc08_reg_h = hc08_regWithIdx(H_IDX); + hc08_reg_hx = hc08_regWithIdx(HX_IDX); + hc08_reg_xa = hc08_regWithIdx(XA_IDX); + hc08_reg_sp = hc08_regWithIdx(SP_IDX); + hc08_nRegs = 5; + + /* change assignments this will remove some + live ranges reducing some register pressure */ + + for (i = 0; i < count; i++) + packRegisters (ebbs, i); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (*ebbs); + + /* and serially allocate registers */ + serialRegAssign (ebbs, count); + + freeAllRegs (); + //setToNull ((void *) &_G.regAssigned); + //setToNull ((void *) &_G.totRegAssigned); +#if 0 + fillGaps(); +#endif + + /* if stack was extended then tell the user */ + if (_G.stackExtend) + { +/* werror(W_TOOMANY_SPILS,"stack", */ +/* _G.stackExtend,currFunc->name,""); */ + _G.stackExtend = 0; + } + + if (_G.dataExtend) + { +/* werror(W_TOOMANY_SPILS,"data space", */ +/* _G.dataExtend,currFunc->name,""); */ + _G.dataExtend = 0; + } + + /* after that create the register mask + for each of the instruction */ + createRegMask (ebbs, count); + + /* Convert the old sym->accuse flag into normal register assignments */ + replaceAccuse (ebbs, count); + + /* redo that offsets for stacked automatic variables */ + if (currFunc) + { + redoStackOffsets (); + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + /* do the overlaysegment stuff SDCCmem.c */ + doOverlays (ebbs, count); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + genhc08Code (ic); + + /* free up any _G.stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + freeAllRegs (); + + return; +} +#endif + +/** Serially allocate registers to the variables. + This was the main register allocation function. It is called after + packing. + In the new register allocator it only serves to mark variables for the new register allocator. + */ +static void +serialRegMark (eBBlock ** ebbs, int count) +{ + int i; + short int max_alloc_bytes = SHRT_MAX; // Byte limit. Set this to a low value to pass only few variables to the register allocator. This can be useful for debugging. + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + updateRegUsage(ic); + + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + reassignLR (IC_LEFT (ic)); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && + IS_TRUE_SYMOP (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + } + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + (IC_RESULT (ic) && POINTER_SET (ic))) + { + continue; + } + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + + /* Make sure any spill location is definitely allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && + !sym->usl.spillLoc->allocreq) + { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || + bitVectBitValue (_G.regAssigned, sym->key) || + sym->liveTo <= ic->seq) + { + continue; + } + + /* if some liverange has been spilt at the block level + and this one live beyond this block then spil this + to be safe */ + if (_G.blockSpil && sym->liveTo > ebbs[i]->lSeq) + { + spillThis (sym); + continue; + } + + if (sym->remat) + { + spillThis (sym); + continue; + } + + if (max_alloc_bytes >= sym->nRegs) + { + sym->for_newralloc = 1; + max_alloc_bytes -= sym->nRegs; + } + else if (!sym->for_newralloc) + { + spillThis (sym); + printf ("Spilt %s due to byte limit.\n", sym->name); + } + } + } + } +} +/*-----------------------------------------------------------------*/ +/* New register allocator */ +/*-----------------------------------------------------------------*/ +void +hc08_ralloc (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + setToNull ((void *) &_G.funcrUsed); + setToNull ((void *) &_G.regAssigned); + setToNull ((void *) &_G.totRegAssigned); + hc08_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + hc08_nRegs = 7; + hc08_reg_a = hc08_regWithIdx(A_IDX); + hc08_reg_x = hc08_regWithIdx(X_IDX); + hc08_reg_h = hc08_regWithIdx(H_IDX); + hc08_reg_hx = hc08_regWithIdx(HX_IDX); + hc08_reg_xa = hc08_regWithIdx(XA_IDX); + hc08_reg_sp = hc08_regWithIdx(SP_IDX); + hc08_nRegs = 5; + + /* change assignments this will remove some + live ranges reducing some register pressure */ + + for (i = 0; i < count; i++) + packRegisters (ebbs, i); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (*ebbs); + + /* and serially allocate registers */ + serialRegMark (ebbs, count); + + /* The new register allocator invokes its magic */ + ic = hc08_ralloc2_cc (ebbi); + + RegFix (ebbs, count); + + /* if stack was extended then tell the user */ + if (_G.stackExtend) + { +/* werror(W_TOOMANY_SPILS,"stack", */ +/* _G.stackExtend,currFunc->name,""); */ + _G.stackExtend = 0; + } + + if (_G.dataExtend) + { +/* werror(W_TOOMANY_SPILS,"data space", */ +/* _G.dataExtend,currFunc->name,""); */ + _G.dataExtend = 0; + } + + /* redo that offsets for stacked automatic variables */ + if (currFunc) + { + redoStackOffsets (); + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + /* do the overlaysegment stuff SDCCmem.c */ + doOverlays (ebbs, count); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + genhc08Code (ic); + + /* free up any _G.stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + freeAllRegs (); + + return; +} + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +hc08_assignRegisters (ebbIndex * ebbi) +{ +#ifdef OLDRALLOC + if (options.oldralloc) + hc08_oldralloc (ebbi); + else +#endif + hc08_ralloc (ebbi); +} + diff --git a/src/hc08/ralloc.h b/src/hc08/ralloc.h new file mode 100644 index 0000000..2dfba64 --- /dev/null +++ b/src/hc08/ralloc.h @@ -0,0 +1,89 @@ +/*------------------------------------------------------------------------- + + SDCCralloc.h - header file register allocation + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + 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. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#include "SDCCicode.h" +#include "SDCCBBlock.h" +#ifndef SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +enum + { + A_IDX, + X_IDX, + H_IDX, + HX_IDX, + XA_IDX, + CND_IDX, + SP_IDX + }; + + +#define REG_PTR 0x01 +#define REG_GPR 0x02 +#define REG_CND 0x04 + +/* Must preserve the relation HC08MASK_H > HC08MASK_X > MC08MASK_A */ +/* so that HC08MASK_REV can be automatically applied when reversing */ +/* the usual register pair ordering. */ +#define HC08MASK_A 0x01 +#define HC08MASK_X 0x02 +#define HC08MASK_H 0x04 +#define HC08MASK_REV 0x08 +#define HC08MASK_XA (HC08MASK_X | HC08MASK_A) +#define HC08MASK_HX (HC08MASK_H | HC08MASK_X) +#define HC08MASK_AX (HC08MASK_REV | HC08MASK_X | HC08MASK_A) + +/* definition for the registers */ +typedef struct reg_info + { + short type; /* can have value + REG_GPR, REG_PTR or REG_CND */ + short rIdx; /* index into register table */ + char *name; /* name */ + short mask; /* bitmask for pair allocation */ + struct asmop *aop; /* last operand */ + int aopofs; /* last operand offset */ + unsigned isFree:1; /* is currently unassigned */ + unsigned isDead:1; /* does not need to survive current instruction */ + unsigned isLitConst:1; /* has an literal constant loaded */ + int litConst; /* last literal constant */ + } +reg_info; +extern reg_info regshc08[]; +extern reg_info *hc08_reg_a; +extern reg_info *hc08_reg_x; +extern reg_info *hc08_reg_h; +extern reg_info *hc08_reg_hx; +extern reg_info *hc08_reg_xa; +extern reg_info *hc08_reg_sp; + +reg_info *hc08_regWithIdx (int); +void hc08_useReg (reg_info * reg); +void hc08_freeReg (reg_info * reg); +void hc08_dirtyReg (reg_info * reg, bool freereg); +bitVect *hc08_rUmaskForOp (operand * op); + +iCode *hc08_ralloc2_cc(ebbIndex *ebbi); + +#endif diff --git a/src/hc08/ralloc2.cc b/src/hc08/ralloc2.cc new file mode 100644 index 0000000..9c76417 --- /dev/null +++ b/src/hc08/ralloc2.cc @@ -0,0 +1,674 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2010 - 2011 +// +// (c) 2012 Goethe-Universität Frankfurt +// +// 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. + +//#define DEBUG_RALLOC_DEC // Uncomment to get debug messages while doing register allocation on the tree decomposition. +//#define DEBUG_RALLOC_DEC_ASS // Uncomment to get debug messages about assignments while doing register allocation on the tree decomposition (much more verbose than the one above). + +#define TD_SALLOC +#define CH_SALLOC + +#include "SDCCralloc.hpp" + +extern "C" +{ + #include "ralloc.h" + #include "gen.h" + unsigned char dryhc08iCode (iCode *ic); + bool hc08_assignment_optimal; +} + +#define REG_A 0 +#define REG_X 1 +#define REG_H 2 + +template <class I_t> +static void add_operand_conflicts_in_node(const cfg_node &n, I_t &I) +{ + const iCode *ic = n.ic; + + const operand *result = IC_RESULT(ic); + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + + if(!result || !IS_SYMOP(result)) + return; + + // Todo: Identify more operations that code generation can always handle and exclude them (as done for the z80-like ports). + if (ic->op == '=') + return; + + operand_map_t::const_iterator oir, oir_end, oirs; + boost::tie(oir, oir_end) = n.operands.equal_range(OP_SYMBOL_CONST(result)->key); + if(oir == oir_end) + return; + + operand_map_t::const_iterator oio, oio_end; + + if(left && IS_SYMOP(left)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(left)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } + + if(right && IS_SYMOP(right)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(right)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } +} + +// Return true, iff the operand is placed (partially) in r. +template <class G_t> +static bool operand_in_reg(const operand *o, reg_t r, const i_assignment_t &ia, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + if(oi->second == ia.registers[r][1] || oi->second == ia.registers[r][0]) + return(true); + + return(false); +} + +// Check that the operand is either fully in registers or fully in memory. +template <class G_t, class I_t> +static bool operand_sane(const operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return(true); + + operand_map_t::const_iterator oi, oi2, oi_end; + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + + if(oi == oi_end) + return(true); + + // Go to the second byte. If the operand is only a single byte, it cannot be + // an unsupported register combination or split between register and memory. + oi2 = oi; + oi2++; + if (oi2 == oi_end) + return(true); + + // Register combinations code generation cannot handle yet (AH, XH, HA). + if(std::binary_search(a.local.begin(), a.local.end(), oi->second) && std::binary_search(a.local.begin(), a.local.end(), oi2->second)) + { + const reg_t l = a.global[oi->second]; + const reg_t h = a.global[oi2->second]; + if(l == REG_A && h == REG_H || l == REG_H) + return(false); + } + + // In registers. + if(std::binary_search(a.local.begin(), a.local.end(), oi->second)) + { + while(++oi != oi_end) + if(!std::binary_search(a.local.begin(), a.local.end(), oi->second)) + return(false); + } + else + { + while(++oi != oi_end) + if(std::binary_search(a.local.begin(), a.local.end(), oi->second)) + return(false); + } + + return(true); +} + +template <class G_t, class I_t> +static bool inst_sane(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + return(operand_sane(IC_RESULT(ic), a, i, G, I) && operand_sane(IC_LEFT(ic), a, i, G, I) && operand_sane(IC_RIGHT(ic), a, i, G, I)); +} + +template <class G_t, class I_t> +static bool operand_is_ax(const operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + operand_map_t::const_iterator oi, oi2, oi_end; + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + + if(oi == oi_end) + return(false); + + oi2 = oi; + oi2++; + if (oi2 == oi_end) + return(false); + + // Register combinations code generation cannot handle yet (AX, AH, XH, HA). + if(std::binary_search(a.local.begin(), a.local.end(), oi->second) && std::binary_search(a.local.begin(), a.local.end(), oi2->second)) + { + const reg_t l = a.global[oi->second]; + const reg_t h = a.global[oi2->second]; + if(l == REG_X && h == REG_A) + return(true); + } + + return(false); +} + +template <class G_t, class I_t> +static bool XAinst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + // Instructions that can handle anything. + if(ic->op == '!' || + ic->op == '~' || + ic->op == UNARYMINUS || + ic->op == CALL || + ic->op == PCALL || + ic->op == FUNCTION || + ic->op == ENDFUNCTION || + ic->op == RETURN || + ic->op == LABEL || + ic->op == GOTO || + ic->op == IFX || + ic->op == '+' || + ic->op == '-' || + ic->op == '*' || + ic->op == '/' || + ic->op == '%' || + ic->op == '<' || ic->op == '>' || ic->op == LE_OP || ic->op == GE_OP || + ic->op == NE_OP || ic->op == EQ_OP || + ic->op == AND_OP || + ic->op == OR_OP || + ic->op == '^' || + ic->op == '|' || + ic->op == BITWISEAND || + ic->op == GETABIT || + ic->op == GETBYTE || + ic->op == GETWORD || + ic->op == LEFT_OP || + ic->op == RIGHT_OP || + ic->op == '=' || /* both regular assignment and POINTER_SET safe */ + ic->op == GET_VALUE_AT_ADDRESS || + ic->op == ADDRESS_OF || + ic->op == CAST || + ic->op == DUMMY_READ_VOLATILE || + ic->op == SWAP) + return(true); + + if(ic->op == IFX && ic->generated) + return(true); + + const i_assignment_t &ia = a.i_assignment; + + bool unused_A = (ia.registers[REG_A][1] < 0); + bool unused_H = (ia.registers[REG_H][1] < 0); + bool unused_X = (ia.registers[REG_X][1] < 0); + + if(unused_X && unused_A && unused_H) + return(true); + +#if 0 + std::cout << "XAinst_ok: at (" << i << ", " << ic->key << ")\nX = (" << ia.registers[REG_X][0] << ", " << ia.registers[REG_X][1] << "), A = (" << ia.registers[REG_A][0] << ", " << ia.registers[REG_A][1] << ")inst " << i << ", " << ic->key << "\n"; +#endif + + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + bool result_in_A = operand_in_reg(result, REG_A, ia, i, G) && !(ic->op == '=' && POINTER_SET(ic)); + bool result_in_H = operand_in_reg(result, REG_H, ia, i, G) && !(ic->op == '=' && POINTER_SET(ic)); + bool result_in_X = operand_in_reg(result, REG_X, ia, i, G) && !(ic->op == '=' && POINTER_SET(ic)); + bool left_in_A = operand_in_reg(result, REG_A, ia, i, G); + bool left_in_X = operand_in_reg(result, REG_X, ia, i, G); + + const cfg_dying_t &dying = G[i].dying; + + bool dying_A = result_in_A || dying.find(ia.registers[REG_A][1]) != dying.end() || dying.find(ia.registers[REG_A][0]) != dying.end(); + bool dying_H = result_in_H || dying.find(ia.registers[REG_H][1]) != dying.end() || dying.find(ia.registers[REG_H][0]) != dying.end(); + bool dying_X = result_in_X || dying.find(ia.registers[REG_X][1]) != dying.end() || dying.find(ia.registers[REG_X][0]) != dying.end(); + + bool result_only_XA = (result_in_X || unused_X || dying_X) && (result_in_A || unused_A || dying_A); + + if(ic->op == JUMPTABLE && (unused_A || dying_A)) + return(true); + + if(ic->op == IPUSH && (unused_A || dying_A || left_in_A || operand_in_reg(left, REG_H, ia, i, G) || left_in_X)) + return(true); + + if(ic->op == RECEIVE && (!ic->next || !(ic->next->op == RECEIVE) || !result_in_X || getSize(operandType(result)) >= 2)) + return(true); + + if(ic->op == SEND && ic->next && ic->next->op == SEND && ic->next->next && ic->next->next->op == SEND) + return(true); + + if(ic->op == SEND && ic->next && ic->next->op == SEND && (unused_X || dying_X)) + return(true); + + if(ic->op == SEND && (unused_X || dying_X) && (unused_A || dying_A)) + return(true); + + if(ic->op == SEND && ic->next && (ic->next->op == CALL || ic->next->op == PCALL)) // Might mess up A and X, but these would have been saved before if surviving, and will not be needed again before the call. + return(true); + + if((ic->op == CRITICAL || ic->op == ENDCRITICAL) && (unused_A || dying_A)) + return(true); + + return(false); +} + +template <class G_t, class I_t> +static bool AXinst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + const i_assignment_t &ia = a.i_assignment; + + if(ic->op == '!' || + ic->op == '~' || + ic->op == IPUSH || + ic->op == CALL || + ic->op == FUNCTION || + ic->op == ENDFUNCTION || + ic->op == RETURN || + ic->op == LABEL || + ic->op == GOTO || + ic->op == '+' || + ic->op == '-' || + ic->op == NE_OP || ic->op == EQ_OP || + ic->op == '^' || + ic->op == '|' || + ic->op == BITWISEAND || + ic->op == GETABIT || + ic->op == GETBYTE || + ic->op == GETWORD || + /*ic->op == LEFT_OP || + ic->op == RIGHT_OP ||*/ + ic->op == GET_VALUE_AT_ADDRESS || + ic->op == '=' || + ic->op == ADDRESS_OF || + ic->op == RECEIVE || + ic->op == SEND || + ic->op == DUMMY_READ_VOLATILE || + ic->op == CRITICAL || + ic->op == ENDCRITICAL || + ic->op == SWAP) + return(true); + + bool unused_A = (ia.registers[REG_A][1] < 0); + bool unused_X = (ia.registers[REG_X][1] < 0); + + if (unused_A || unused_X) + return(true); + + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + bool result_in_A = operand_in_reg(result, REG_A, ia, i, G) && !(ic->op == '=' && POINTER_SET(ic)); + bool result_in_X = operand_in_reg(result, REG_X, ia, i, G) && !(ic->op == '=' && POINTER_SET(ic)); + bool left_in_A = operand_in_reg(result, REG_A, ia, i, G); + bool left_in_X = operand_in_reg(result, REG_X, ia, i, G); + bool right_in_A = operand_in_reg(result, REG_A, ia, i, G); + bool right_in_X = operand_in_reg(result, REG_X, ia, i, G); + + bool result_is_ax = operand_is_ax (result, a, i, G, I); + bool left_is_ax = operand_is_ax (left, a, i, G, I); + bool right_is_ax = operand_is_ax (right, a, i, G, I); + + if (!result_is_ax && !left_is_ax && !right_is_ax) + return(true); + + return(false); +} + +template <class G_t, class I_t> +static void set_surviving_regs(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + + bitVectClear(ic->rMask); + bitVectClear(ic->rSurv); + + cfg_alive_t::const_iterator v, v_end; + for (v = G[i].alive.begin(), v_end = G[i].alive.end(); v != v_end; ++v) + { + if(a.global[*v] < 0) + continue; + ic->rMask = bitVectSetBit(ic->rMask, a.global[*v]); + if(G[i].dying.find(*v) == G[i].dying.end()) + if(!((IC_RESULT(ic) && !POINTER_SET(ic)) && IS_SYMOP(IC_RESULT(ic)) && OP_SYMBOL_CONST(IC_RESULT(ic))->key == I[*v].v)) + ic->rSurv = bitVectSetBit(ic->rSurv, a.global[*v]); + } +} + +template <class G_t, class I_t> +static void assign_operand_for_cost(operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return; + symbol *sym = OP_SYMBOL(o); + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + { + var_t v = oi->second; + if(a.global[v] >= 0) + { + sym->regs[I[v].byte] = regshc08 + a.global[v]; + sym->isspilt = false; + sym->nRegs = I[v].size; + sym->accuse = 0; + } + else + { + for(int i = 0; i < I[v].size; i++) + sym->regs[i] = 0; + sym->accuse = 0; + sym->nRegs = I[v].size; + sym->isspilt = true; + } + } +} + +template <class G_t, class I_t> +static void assign_operands_for_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + if(ic->op == IFX) + assign_operand_for_cost(IC_COND(ic), a, i, G, I); + else if(ic->op == JUMPTABLE) + assign_operand_for_cost(IC_JTCOND(ic), a, i, G, I); + else + { + assign_operand_for_cost(IC_LEFT(ic), a, i, G, I); + assign_operand_for_cost(IC_RIGHT(ic), a, i, G, I); + assign_operand_for_cost(IC_RESULT(ic), a, i, G, I); + } + + if(ic->op == SEND && ic->builtinSEND) + { + assign_operands_for_cost(a, *(adjacent_vertices(i, G).first), G, I); + } +} + +// Cost function. +template <class G_t, class I_t> +static float instruction_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + float c; + + wassert (TARGET_IS_HC08 || TARGET_IS_S08); + + if(!inst_sane(a, i, G, I)) + return(std::numeric_limits<float>::infinity()); + + if(!XAinst_ok(a, i, G, I)) + return(std::numeric_limits<float>::infinity()); + + if(!AXinst_ok(a, i, G, I)) + return(std::numeric_limits<float>::infinity()); + +#if 0 + std::cout << "Calculating at cost at ic " << ic->key << " for: "; + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + std::cout << "(" << i << ", " << int(a.global[i]) << ") "; + } + std::cout << "\n"; + std::cout.flush(); +#endif + + if(ic->generated) + return(0.0f); + + switch(ic->op) + { + // Register assignment doesn't matter for these: + case FUNCTION: + case ENDFUNCTION: + case LABEL: + case GOTO: + case INLINEASM: + return(0.0f); + case '!': + case '~': + case UNARYMINUS: + case '+': + case '-': + case '^': + case '|': + case BITWISEAND: + case IPUSH: + //case IPOP: + case CALL: + case PCALL: + case RETURN: + case '*': + case '/': + case '%': + case '>': + case '<': + case LE_OP: + case GE_OP: + case EQ_OP: + case NE_OP: + case AND_OP: + case OR_OP: + case GETABIT: + case GETBYTE: + case GETWORD: + case LEFT_OP: + case RIGHT_OP: + case GET_VALUE_AT_ADDRESS: + case '=': + case IFX: + case ADDRESS_OF: + case JUMPTABLE: + case CAST: + case RECEIVE: + case SEND: + case DUMMY_READ_VOLATILE: + case CRITICAL: + case ENDCRITICAL: + case SWAP: + assign_operands_for_cost(a, i, G, I); + set_surviving_regs(a, i, G, I); + c = dryhc08iCode(ic); + return(c); + default: + return(0.0f); + } +} + +// For early removal of assignments that cannot be extended to valid assignments. This is just a dummy for now, it probably isn't really needed for hc08 due to the low number of registers. +template <class G_t, class I_t> +static bool assignment_hopeless(const assignment &a, unsigned short int i, const G_t &G, const I_t &I, const var_t lastvar) +{ + return(false); +} + +// Increase chance of finding good compatible assignments at join nodes. This is just a dummy for now, it probably isn't really needed for hc08 due to the low number of registers. +template <class T_t> +static void get_best_local_assignment_biased(assignment &a, typename boost::graph_traits<T_t>::vertex_descriptor t, const T_t &T) +{ + a = *T[t].assignments.begin(); + + std::set<var_t>::const_iterator vi, vi_end; + varset_t newlocal; + std::set_union(T[t].alive.begin(), T[t].alive.end(), a.local.begin(), a.local.end(), std::inserter(newlocal, newlocal.end())); + a.local = newlocal; +} + +// This is just a dummy for now, it probably isn't really needed for hc08 due to the low number of registers. +template <class G_t, class I_t> +static float rough_cost_estimate(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + return(0.0f); +} + +// Code for another ic is generated when generating this one. Mark the other as generated. +static void extra_ic_generated(iCode *ic) +{ + if(ic->op == '>' || ic->op == '<' || ic->op == LE_OP || ic->op == GE_OP || ic->op == EQ_OP || ic->op == NE_OP || ic->op == '^' || ic->op == '|' || ic->op == BITWISEAND) + { + iCode *ifx; + if (ifx = ifxForOp (IC_RESULT (ic), ic)) + { + OP_SYMBOL (IC_RESULT (ic))->for_newralloc = false; + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + ifx->generated = true; + } + } + if(ic->op == '-' && IS_VALOP (IC_RIGHT (ic)) && operandLitValue (IC_RIGHT (ic)) == 1 && getSize(operandType(IC_RESULT (ic))) == 1 && !isOperandInFarSpace (IC_RESULT (ic)) && isOperandEqual (IC_RESULT (ic), IC_LEFT (ic))) + { + iCode *ifx; + if (ifx = ifxForOp (IC_RESULT (ic), ic)) + { + OP_SYMBOL (IC_RESULT (ic))->for_newralloc = false; + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + ifx->generated = true; + } + } + if(ic->op == GET_VALUE_AT_ADDRESS) + { + iCode *inc; + if (inc = hasInchc08 (IC_LEFT (ic), ic, getSize (operandType (IC_RIGHT (ic))))) + inc->generated = true; + } +} + +template <class T_t, class G_t, class I_t> +static bool tree_dec_ralloc(T_t &T, G_t &G, const I_t &I) +{ + bool assignment_optimal; + + con2_t I2(boost::num_vertices(I)); + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + I2[i].v = I[i].v; + I2[i].byte = I[i].byte; + I2[i].size = I[i].size; + I2[i].name = I[i].name; + } + typename boost::graph_traits<I_t>::edge_iterator e, e_end; + for(boost::tie(e, e_end) = boost::edges(I); e != e_end; ++e) + add_edge(boost::source(*e, I), boost::target(*e, I), I2); + + assignment ac; + assignment_optimal = true; + tree_dec_ralloc_nodes(T, find_root(T), G, I2, ac, &assignment_optimal); + + const assignment &winner = *(T[find_root(T)].assignments.begin()); + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Winner: "; + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + std::cout << "(" << i << ", " << int(winner.global[i]) << ") "; + } + std::cout << "\n"; + std::cout << "Cost: " << winner.s << "\n"; + std::cout.flush(); +#endif + + // Todo: Make this an assertion + if(winner.global.size() != boost::num_vertices(I)) + { + std::cerr << "ERROR: No Assignments at root\n"; + exit(-1); + } + + for(unsigned int v = 0; v < boost::num_vertices(I); v++) + { + symbol *sym = (symbol *)(hTabItemWithKey(liveRanges, I[v].v)); + if(winner.global[v] >= 0) + { + sym->regs[I[v].byte] = regshc08 + winner.global[v]; + sym->isspilt = false; + sym->nRegs = I[v].size; + sym->accuse = 0; + } + else + { + for(int i = 0; i < I[v].size; i++) + sym->regs[i] = 0; + sym->accuse = 0; + sym->nRegs = I[v].size; + wassert (sym->nRegs); + //spillThis(sym); Leave it to regFix, which can do some spillocation compaction. Todo: Use Thorup instead. + sym->isspilt = false; + } + } + + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + set_surviving_regs(winner, i, G, I); + + return(!assignment_optimal); +} + +iCode *hc08_ralloc2_cc(ebbIndex *ebbi) +{ + iCode *ic; + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Processing " << currFunc->name << " from " << dstFileName << "\n"; std::cout.flush(); +#endif + + cfg_t control_flow_graph; + + con_t conflict_graph; + + ic = create_cfg(control_flow_graph, conflict_graph, ebbi); + + if(options.dump_graphs) + dump_cfg(control_flow_graph); + + if(options.dump_graphs) + dump_con(conflict_graph); + + tree_dec_t tree_decomposition; + + get_nice_tree_decomposition(tree_decomposition, control_flow_graph); + + alive_tree_dec(tree_decomposition, control_flow_graph); + + good_re_root(tree_decomposition); + nicify(tree_decomposition); + alive_tree_dec(tree_decomposition, control_flow_graph); + + if(options.dump_graphs) + dump_tree_decomposition(tree_decomposition); + + guessCounts (ic, ebbi); + + hc08_assignment_optimal = !tree_dec_ralloc(tree_decomposition, control_flow_graph, conflict_graph); + + return(ic); +} + |
