/*-------------------------------------------------------------------------
gen.c - Z80 specific code generator.
+
+ Michael Hope <michaelh@juju.net.nz> 2000
+ Based on the mcs51 generator -
+ Sandeep Dutta . sandeep.dutta@usa.net (1998)
+ and - Jean-Louis VERN.jlvern@writeme.com (1999)
+
+ 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!
+
+-------------------------------------------------------------------------*/
+/*
Benchmarks on dhry.c 2.1 with 32766 loops and a 10ms clock:
ticks dhry size
Base with asm strcpy / strcmp / memcpy: 23198 141 1A14
4. Optimised strcmp fun 21999 149 2294
5. Optimised strcmp further 21660 151 228C
6. Optimised memcpy by unroling 20885 157 2201
+ 7. After turning loop induction on 19862 165 236D
+ 8. Same as 7 but with more info
+ 9. With asm optimised strings 17030 192 2223
- Michael Hope <michaelh@juju.net.nz> 2000
- Based on the mcs51 generator -
- Sandeep Dutta . sandeep.dutta@usa.net (1998)
- and - Jean-Louis VERN.jlvern@writeme.com (1999)
-
- 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!
-
--------------------------------------------------------------------------*/
+ 10 and below are with asm strings off.
+
+ Apparent advantage of turning on regparams:
+ 1. Cost of push
+ Decent case is push of a constant
+ - ld hl,#n; push hl: (10+11)*nargs
+ 2. Cost of pull from stack
+ Using asm with ld hl, etc
+ - ld hl,#2; add hl,sp; (ld bc,(hl); hl+=2)*nargs
+ 10+11+(7+6+7+6)*nargs
+ 3. Cost of fixing stack
+ - pop hl*nargs
+ 10*nargs
+
+ So cost is (10+11+7+6+7+10)*nargs+10+11
+ = 51*nargs+21
+ = 123 for mul, div, strcmp, strcpy
+ Saving of (98298+32766+32766+32766)*123 = 24181308
+ At 192 d/s for 682411768t, speed up to 199. Hmm.
+*/
#include <stdio.h>
#include <stdlib.h>
#include "SDCCglue.h"
#include "newalloc.h"
-/* this is the down and dirty file with all kinds of kludgy & hacky
+/* This is the down and dirty file with all kinds of kludgy & hacky
stuff. This is what it is all about CODE GENERATION for a specific MCU.
Some of the routines may be reusable, will have to see */
PENDING: What if the parameter is a long?
Everything is caller saves. i.e. the caller must save any registers
that it wants to preserve over the call.
- The return value is returned in DEHL. DE is normally used as a
+ GB: The return value is returned in DEHL. DE is normally used as a
working register pair. Caller saves allows it to be used for a
return value.
va args functions do not use register parameters. All arguments
area. ix-0 is the top most local variable.
*/
-enum {
- DISABLE_DEBUG = 1
+enum
+{
+ /* Set to enable debugging trace statements in the output assembly code. */
+ DISABLE_DEBUG = 0
};
static char *_z80_return[] =
extern FILE *codeOutFile;
+enum
+ {
+ INT8MIN = -128,
+ INT8MAX = 127
+ };
+
+/** Enum covering all the possible register pairs.
+ */
typedef enum
{
PAIR_INVALID,
// PENDING
#define ACC_NAME _pairs[PAIR_AF].h
-#define RESULTONSTACK(x) \
- (IC_RESULT(x) && IC_RESULT(x)->aop && \
- IC_RESULT(x)->aop->type == AOP_STK )
-
enum
{
LSB,
MSB32
};
+/** Code generator persistent data.
+ */
static struct
{
+ /** Used to optimised setting up of a pair by remebering what it
+ contains and adjusting instead of reloading where possible.
+ */
struct
{
AOP_TYPE last_type;
- const char *lit;
+ const char *base;
int offset;
} pairs[NUM_PAIRS];
struct
lineNode *head;
lineNode *current;
int isInline;
+ allocTrace trace;
} lines;
+ struct
+ {
+ allocTrace aops;
+ } trace;
} _G;
static const char *aopGet (asmop * aop, int offset, bool bit16);
+static PAIR_ID
+_getTempPairId(void)
+{
+ if (IS_GB)
+ {
+ return PAIR_DE;
+ }
+ else
+ {
+ return PAIR_HL;
+ }
+}
+
+static const char *
+_getTempPairName(void)
+{
+ return _pairs[_getTempPairId()].name;
+}
+
static void
_tidyUp (char *buf)
{
}
}
+static lineNode *
+_newLineNode (char *line)
+{
+ lineNode *pl;
+
+ pl = traceAlloc(&_G.lines.trace, Safe_alloc ( sizeof (lineNode)));
+ pl->line = traceAlloc(&_G.lines.trace, Safe_strdup (line));
+
+ return pl;
+}
+
static void
_vemit2 (const char *szFormat, va_list ap)
{
_tidyUp (buffer);
_G.lines.current = (_G.lines.current ?
- connectLine (_G.lines.current, newLineNode (buffer)) :
- (_G.lines.head = newLineNode (buffer)));
+ connectLine (_G.lines.current, _newLineNode (buffer)) :
+ (_G.lines.head = _newLineNode (buffer)));
_G.lines.current->isInline = _G.lines.isInline;
}
if (lbp && *lbp)
{
_G.lines.current = (_G.lines.current ?
- connectLine (_G.lines.current, newLineNode (lb)) :
- (_G.lines.head = newLineNode (lb)));
+ connectLine (_G.lines.current, _newLineNode (lb)) :
+ (_G.lines.head = _newLineNode (lb)));
}
_G.lines.current->isInline = _G.lines.isInline;
va_end (ap);
break;
}
}
- wassert (0);
+ wassertl (0, "Tried to get the pair name of something that isn't a pair");
return NULL;
}
emit2 ("push %s", getPairName (aop));
}
+static void
+_push (PAIR_ID pairId)
+{
+ emit2 ("push %s", _pairs[pairId].name);
+ _G.stack.pushed += 2;
+}
+
+static void
+_pop (PAIR_ID pairId)
+{
+ emit2 ("pop %s", _pairs[pairId].name);
+ _G.stack.pushed -= 2;
+}
/*-----------------------------------------------------------------*/
/* newAsmop - creates a new asmOp */
{
asmop *aop;
- aop = Safe_calloc (1, sizeof (asmop));
+ aop = traceAlloc(&_G.trace.aops, Safe_alloc (sizeof (asmop)));
aop->type = type;
return aop;
}
/* Assign depending on the storage class */
if (sym->onStack || sym->iaccess)
{
- emitDebug ("; AOP_STK for %s", sym->rname);
- sym->aop = aop = newAsmop (AOP_STK);
+ /* The pointer that is used depends on how big the offset is.
+ Normally everything is AOP_STK, but for offsets of < -128 or
+ > 127 on the Z80 an extended stack pointer is used.
+ */
+ if (IS_Z80 && (options.ommitFramePtr || sym->stack < INT8MIN || sym->stack > (int)(INT8MAX-getSize (sym->type))))
+ {
+ emitDebug ("; AOP_EXSTK for %s", sym->rname);
+ sym->aop = aop = newAsmop (AOP_EXSTK);
+ }
+ else
+ {
+ emitDebug ("; AOP_STK for %s", sym->rname);
+ sym->aop = aop = newAsmop (AOP_STK);
+ }
+
aop->size = getSize (sym->type);
aop->aopu.aop_stk = sym->stack;
return aop;
if (IS_FUNC (sym->type))
{
sym->aop = aop = newAsmop (AOP_IMMD);
- aop->aopu.aop_immd = Safe_calloc (1, strlen (sym->rname) + 1);
- strcpy (aop->aopu.aop_immd, sym->rname);
+ aop->aopu.aop_immd = traceAlloc(&_G.trace.aops, Safe_strdup (sym->rname));
aop->size = 2;
return aop;
}
break;
}
- aop->aopu.aop_immd = Safe_calloc (1, strlen (buffer) + 1);
- strcpy (aop->aopu.aop_immd, buffer);
+ aop->aopu.aop_immd = traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
return aop;
}
}
else
{
- wassert (0);
+ wassertl (0, "Marked as being allocated into A or HL but is actually in neither");
}
return;
}
aop->freed = 1;
+ if (aop->type == AOP_PAIRPTR && IS_Z80 && aop->aopu.aop_pairId == PAIR_DE)
+ {
+ _pop (aop->aopu.aop_pairId);
+ }
+
dealloc:
/* all other cases just dealloc */
if (op)
SPIL_LOC (op)->aop = NULL;
}
}
+
}
bool
case AOP_IMMD:
/* PENDING: for re-target */
if (with_hash)
- tsprintf (s, "!hashedstr + %d", aop->aopu.aop_immd, offset);
+ {
+ tsprintf (s, "!hashedstr + %d", aop->aopu.aop_immd, offset);
+ }
+ else if (offset == 0)
+ {
+ tsprintf (s, "%s", aop->aopu.aop_immd);
+ }
else
- tsprintf (s, "%s + %d", aop->aopu.aop_immd, offset);
-
- return gc_strdup(s);
+ {
+ tsprintf (s, "%s + %d", aop->aopu.aop_immd, offset);
+ }
+ return traceAlloc(&_G.trace.aops, Safe_strdup(s));
case AOP_LIT:
{
else
tsprintf (buffer, "!constword", v);
- return gc_strdup(buffer);
+ return traceAlloc(&_G.trace.aops, Safe_strdup(buffer));
}
else
{
spillPair (PAIR_ID pairId)
{
_G.pairs[pairId].last_type = AOP_INVALID;
- _G.pairs[pairId].lit = NULL;
+ _G.pairs[pairId].base = NULL;
}
static void
case AOP_IY:
case AOP_HL:
case AOP_STK:
+ case AOP_EXSTK:
return TRUE;
default:
return FALSE;
}
}
-static char *
-fetchLitSpecial (asmop * aop, bool negate, bool xor)
-{
- unsigned long v;
- value *val = aop->aopu.aop_lit;
-
- wassert (aop->type == AOP_LIT);
- wassert (!IS_FLOAT (val->type));
-
- v = (unsigned long) floatFromVal (val);
-
- if (xor)
- v ^= 0x8000;
- if (negate)
- v = 0-v;
- v &= 0xFFFF;
-
- tsprintf (buffer, "!immedword", v);
- return gc_strdup (buffer);
-}
-
static void
fetchLitPair (PAIR_ID pairId, asmop * left, int offset)
{
- const char *l;
+ const char *l, *base;
const char *pair = _pairs[pairId].name;
l = aopGetLitWordLong (left, offset, FALSE);
- wassert (l && pair);
+ base = aopGetLitWordLong (left, 0, FALSE);
+ wassert (l && pair && base);
if (isPtr (pair))
{
{
if (_G.pairs[pairId].last_type == left->type)
{
- if (_G.pairs[pairId].lit && !strcmp (_G.pairs[pairId].lit, l))
+ if (_G.pairs[pairId].base && !strcmp (_G.pairs[pairId].base, base))
{
if (pairId == PAIR_HL && abs (_G.pairs[pairId].offset - offset) < 3)
{
adjustPair (pair, &_G.pairs[pairId].offset, offset);
return;
}
- if (pairId == PAIR_IY && abs (offset) < 127)
+ if (pairId == PAIR_IY && (offset >= INT8MIN && offset <= INT8MAX))
{
return;
}
}
}
_G.pairs[pairId].last_type = left->type;
- _G.pairs[pairId].lit = gc_strdup (l);
- _G.pairs[pairId].offset = offset;
- }
- if (IS_GB && pairId == PAIR_DE && 0)
- {
- if (_G.pairs[pairId].lit && !strcmp (_G.pairs[pairId].lit, l))
- {
- if (abs (_G.pairs[pairId].offset - offset) < 3)
- {
- adjustPair (pair, &_G.pairs[pairId].offset, offset);
- return;
- }
- }
- _G.pairs[pairId].last_type = left->type;
- _G.pairs[pairId].lit = gc_strdup (l);
+ _G.pairs[pairId].base = traceAlloc(&_G.trace.aops, Safe_strdup (base));
_G.pairs[pairId].offset = offset;
}
/* Both a lit on the right and a true symbol on the left */
/* we need to get it byte by byte */
if (pairId == PAIR_HL && IS_GB && requiresHL (aop)) {
aopGet (aop, offset, FALSE);
- switch (aop->size) {
+ switch (aop->size - offset) {
case 1:
emit2 ("ld l,!*hl");
emit2 ("ld h,!immedbyte", 0);
break;
case 2:
+ // PENDING: Requires that you are only fetching two bytes.
+ case 4:
emit2 ("!ldahli");
emit2 ("ld h,!*hl");
emit2 ("ld l,a");
break;
default:
- emitDebug ("; WARNING: mlh woosed out. This code is invalid.");
+ wassertl (0, "Attempted to fetch too much data into HL");
+ break;
}
}
else if (IS_Z80 && aop->type == AOP_IY) {
}
static void
-setupPair (PAIR_ID pairId, asmop * aop, int offset)
+setupPairFromSP (PAIR_ID id, int offset)
{
- assert (pairId == PAIR_HL || pairId == PAIR_IY);
+ wassertl (id == PAIR_HL, "Setup relative to SP only implemented for HL");
+
+ if (offset < INT8MIN || offset > INT8MAX)
+ {
+ emit2 ("ld hl,!immedword", offset);
+ emit2 ("add hl,sp");
+ }
+ else
+ {
+ emit2 ("!ldahlsp", offset);
+ }
+}
+static void
+setupPair (PAIR_ID pairId, asmop * aop, int offset)
+{
switch (aop->type)
{
case AOP_IY:
+ wassertl (pairId == PAIR_IY || pairId == PAIR_HL, "AOP_IY must be in IY or HL");
fetchLitPair (pairId, aop, 0);
break;
+
case AOP_HL:
+ wassertl (pairId == PAIR_HL, "AOP_HL must be in HL");
+
fetchLitPair (pairId, aop, offset);
_G.pairs[pairId].offset = offset;
break;
+
+ case AOP_EXSTK:
+ wassertl (IS_Z80, "Only the Z80 has an extended stack");
+ wassertl (pairId == PAIR_IY || pairId == PAIR_HL, "The Z80 extended stack must be in IY or HL");
+
+ {
+ int offset = aop->aopu.aop_stk + _G.stack.offset;
+
+ if (_G.pairs[pairId].last_type == aop->type &&
+ _G.pairs[pairId].offset == offset)
+ {
+ /* Already setup */
+ }
+ else
+ {
+ /* PENDING: Do this better. */
+ sprintf (buffer, "%d", offset + _G.stack.pushed);
+ emit2 ("ld %s,!hashedstr", _pairs[pairId].name, buffer);
+ emit2 ("add %s,sp", _pairs[pairId].name);
+ _G.pairs[pairId].last_type = aop->type;
+ _G.pairs[pairId].offset = offset;
+ }
+ }
+ break;
+
case AOP_STK:
{
/* Doesnt include _G.stack.pushed */
}
else
{
- emit2 ("!ldahlsp", abso + _G.stack.pushed);
+ setupPairFromSP (PAIR_HL, abso + _G.stack.pushed);
}
_G.pairs[pairId].offset = abso;
break;
}
+
+ case AOP_PAIRPTR:
+ adjustPair (_pairs[pairId].name, &_G.pairs[pairId].offset, offset);
+ break;
+
default:
wassert (0);
}
aop->type != AOP_LIT)
{
tsprintf (s, "!zero");
- return gc_strdup(s);
+ return traceAlloc(&_G.trace.aops, Safe_strdup(s));
}
/* depending on type */
tsprintf (s, "!lsbimmeds", aop->aopu.aop_immd);
break;
default:
- wassert (0);
+ wassertl (0, "Fetching from beyond the limits of an immediate value.");
}
- return gc_strdup(s);
+ return traceAlloc(&_G.trace.aops, Safe_strdup(s));
case AOP_DIR:
wassert (IS_GB);
emit2 ("ld a,(%s+%d)", aop->aopu.aop_dir, offset);
sprintf (s, "a");
- return gc_strdup(s);
+ return traceAlloc(&_G.trace.aops, Safe_strdup(s));
case AOP_SFR:
wassert (IS_GB);
emit2 ("ldh a,(%s+%d)", aop->aopu.aop_dir, offset);
sprintf (s, "a");
- return gc_strdup(s);
+ return traceAlloc(&_G.trace.aops, Safe_strdup(s));
case AOP_REG:
return aop->aopu.aop_reg[offset]->name;
setupPair (PAIR_HL, aop, offset);
tsprintf (s, "!*hl");
- return gc_strdup (s);
+ return traceAlloc(&_G.trace.aops, Safe_strdup (s));
case AOP_IY:
wassert (IS_Z80);
setupPair (PAIR_IY, aop, offset);
tsprintf (s, "!*iyx", offset);
- return gc_strdup(s);
+ return traceAlloc(&_G.trace.aops, Safe_strdup(s));
+
+ case AOP_EXSTK:
+ wassert (IS_Z80);
+ setupPair (PAIR_IY, aop, offset);
+ tsprintf (s, "!*iyx", offset, offset);
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(s));
case AOP_STK:
if (IS_GB)
tsprintf (s, "!*ixx", aop->aopu.aop_stk + offset);
}
- return gc_strdup(s);
+ return traceAlloc(&_G.trace.aops, Safe_strdup(s));
case AOP_CRY:
- wassert (0);
+ wassertl (0, "Tried to fetch from a bit variable");
case AOP_ACC:
if (!offset)
else
{
tsprintf(s, "!zero");
- return gc_strdup(s);
+ return traceAlloc(&_G.trace.aops, Safe_strdup(s));
}
case AOP_HLREG:
case AOP_LIT:
return aopLiteral (aop->aopu.aop_lit, offset);
+ case AOP_SIMPLELIT:
+ {
+ unsigned long v = aop->aopu.aop_simplelit;
+
+ v >>= (offset * 8);
+ tsprintf (s, "!immedbyte", (unsigned int) v & 0xff);
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(s));
+ }
case AOP_STR:
aop->coff = offset;
return aop->aopu.aop_str[offset];
+ case AOP_PAIRPTR:
+ setupPair (aop->aopu.aop_pairId, aop, offset);
+ sprintf (s, "(%s)", _pairs[aop->aopu.aop_pairId].name);
+
+ return traceAlloc(&_G.trace.aops, Safe_strdup(s));
+
default:
break;
}
case AOP_IY:
wassert (!IS_GB);
- setupPair (PAIR_IY, aop, offset);
if (!canAssignToPtr (s))
{
emit2 ("ld a,%s", s);
+ setupPair (PAIR_IY, aop, offset);
emit2 ("ld !*iyx,a", offset);
}
else
- emit2 ("ld !*iyx,%s", offset, s);
+ {
+ setupPair (PAIR_IY, aop, offset);
+ emit2 ("ld !*iyx,%s", offset, s);
+ }
break;
case AOP_HL:
emit2 ("ld !*hl,%s", s);
break;
+ case AOP_EXSTK:
+ wassert (!IS_GB);
+ if (!canAssignToPtr (s))
+ {
+ emit2 ("ld a,%s", s);
+ setupPair (PAIR_IY, aop, offset);
+ emit2 ("ld !*iyx,a", offset);
+ }
+ else
+ {
+ setupPair (PAIR_IY, aop, offset);
+ emit2 ("ld !*iyx,%s", offset, s);
+ }
+ break;
+
case AOP_STK:
if (IS_GB)
{
else
{
/* In bit space but not in C - cant happen */
- wassert (0);
+ wassertl (0, "Tried to write into a bit variable");
}
break;
break;
if (offset > 0)
{
-
- emitDebug ("; Error aopPut AOP_ACC");
+ wassertl (0, "Tried to access past the end of A");
}
else
{
emit2 ("ld %s,%s", aop->aopu.aop_str[offset], s);
break;
+ case AOP_PAIRPTR:
+ setupPair (aop->aopu.aop_pairId, aop, offset);
+ emit2 ("ld (%s),%s", _pairs[aop->aopu.aop_pairId].name, s);
+ break;
+
default:
werror (E_INTERNAL_ERROR, __FILE__, __LINE__,
"aopPut got unsupported aop->type");
if (size == 3)
{
/* pointer */
- wassert (0);
+ wassertl (0, "Somehow got a three byte data pointer");
}
return size;
}
movLeft2Result (operand * left, int offl,
operand * result, int offr, int sign)
{
- const char *l;
+ const char *l;
if (!sameRegs (AOP (left), AOP (result)) || (offl != offr))
{
l = aopGet (AOP (left), offl, FALSE);
}
else
{
- wassert (0);
+ if (getDataSize (left) == offl + 1)
+ {
+ emit2 ("ld a,%s", l);
+ aopPut (AOP (result), "a", offr);
+ }
}
}
}
/* if the result is bit */
if (AOP_TYPE (result) == AOP_CRY)
{
- emitDebug ("; Note: outBitC form 1");
- aopPut (AOP (result), "blah", 0);
+ wassertl (0, "Tried to write carry to a bit");
}
else
{
/* if in bit space then a special case */
if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Tried to negate a bit");
}
/* if type float then do float */
if (IS_FLOAT (optype))
{
- wassert (0);
+ wassertl (0, "Tried to negate a float");
}
_toBoolean (IC_LEFT (ic));
if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY &&
AOP_TYPE (IC_LEFT (ic)) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Left and the result are in bit space");
}
size = AOP_SIZE (IC_RESULT (ic));
freeAsmop (IC_RESULT (ic), NULL, ic);
}
+static void
+_gbz80_emitAddSubLongLong (iCode *ic, asmop *left, asmop *right, bool isAdd)
+{
+ /* Logic:
+ ld de,right.lw
+ setup hl to left
+ de = hl - de
+ push flags
+ store de into result
+ pop flags
+ ld de,right.hw
+ setup hl
+ de = hl -de
+ store de into result
+ */
+ const char *first = isAdd ? "add" : "sub";
+ const char *later = isAdd ? "adc" : "sbc";
+
+ wassertl (IS_GB, "Code is only relevent to the gbz80");
+ wassertl (AOP( IC_RESULT (ic))->size == 4, "Only works for four bytes");
+
+ fetchPair (PAIR_DE, left);
+
+ emit2 ("ld a,e");
+ emit2 ("%s a,%s", first, aopGet( right, LSB, FALSE));
+ emit2 ("ld e,a");
+ emit2 ("ld a,d");
+ emit2 ("%s a,%s", later, aopGet( right, MSB16, FALSE));
+
+ _push (PAIR_AF);
+ aopPut ( AOP (IC_RESULT (ic)), "a", MSB16);
+ aopPut ( AOP (IC_RESULT (ic)), "e", LSB);
+
+ fetchPairLong (PAIR_DE, left, MSB24);
+ aopGet (right, MSB24, FALSE);
+
+ _pop (PAIR_AF);
+ emit2 ("ld a,e");
+ emit2 ("%s a,%s", later, aopGet( right, MSB24, FALSE));
+ emit2 ("ld e,a");
+ emit2 ("ld a,d");
+ emit2 ("%s a,%s", later, aopGet( right, MSB32, FALSE));
+
+ aopPut ( AOP (IC_RESULT (ic)), "a", MSB32);
+ aopPut ( AOP (IC_RESULT (ic)), "e", MSB24);
+}
+
+static void
+_gbz80_emitAddSubLong (iCode *ic, bool isAdd)
+{
+ _gbz80_emitAddSubLongLong (ic, AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic)), isAdd);
+}
+
/*-----------------------------------------------------------------*/
/* genUminus - unary minus code generation */
/*-----------------------------------------------------------------*/
if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY &&
AOP_TYPE (IC_LEFT (ic)) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Left and right are in bit space");
goto release;
}
/* if float then do float stuff */
if (IS_FLOAT (optype))
{
- wassert (0);
+ wassertl (0, "Tried to do a unary minus on a float");
goto release;
}
/* otherwise subtract from zero */
size = AOP_SIZE (IC_LEFT (ic));
+
+ if (AOP_SIZE (IC_RESULT (ic)) == 4 && IS_GB)
+ {
+ /* Create a new asmop with value zero */
+ asmop *azero = newAsmop (AOP_SIMPLELIT);
+ azero->aopu.aop_simplelit = 0;
+ azero->size = size;
+ _gbz80_emitAddSubLongLong (ic, azero, AOP (IC_LEFT (ic)), FALSE);
+ goto release;
+ }
+
offset = 0;
_clearCarry();
while (size--)
freeAsmop (IC_RESULT (ic), NULL, ic);
}
-static void
-_push (PAIR_ID pairId)
-{
- emit2 ("push %s", _pairs[pairId].name);
- _G.stack.pushed += 2;
-}
-
-static void
-_pop (PAIR_ID pairId)
-{
- emit2 ("pop %s", _pairs[pairId].name);
- _G.stack.pushed -= 2;
-}
-
-
/*-----------------------------------------------------------------*/
/* assignResultValue - */
/*-----------------------------------------------------------------*/
int size = AOP_SIZE (oper);
bool topInA = 0;
- wassert (size <= 4);
+ wassertl (size <= 4, "Got a result that is bigger than four bytes");
topInA = requiresHL (AOP (oper));
-#if 0
- if (!IS_GB)
- wassert (size <= 2);
-#endif
if (IS_GB && size == 4 && requiresHL (AOP (oper)))
{
/* We do it the hard way here. */
_push (PAIR_HL);
aopPut (AOP (oper), _fReturn[0], 0);
aopPut (AOP (oper), _fReturn[1], 1);
- emit2 ("pop de");
- _G.stack.pushed -= 2;
+ _pop (PAIR_DE);
aopPut (AOP (oper), _fReturn[0], 2);
aopPut (AOP (oper), _fReturn[1], 3);
}
bool bcInRet = FALSE, deInRet = FALSE;
bitVect *rInUse;
-#if 1
rInUse = bitVectCplAnd (bitVectCopy (ic->rMask), ic->rUsed);
-#else
- if (IC_RESULT(ic))
- {
- rInUse = bitVectCplAnd (bitVectCopy (ic->rMask), z80_rUmaskForOp (IC_RESULT(ic)));
- }
- else
- {
- /* Has no result, so in use is all of in use */
- rInUse = ic->rMask;
- }
-#endif
deInUse = bitVectBitValue (rInUse, D_IDX) || bitVectBitValue(rInUse, E_IDX);
bcInUse = bitVectBitValue (rInUse, B_IDX) || bitVectBitValue(rInUse, C_IDX);
if (isLitWord (AOP (IC_LEFT (ic))))
{
- emitDebug ("; Special case where the pCall is to a constant");
emit2 ("call %s", aopGetLitWordLong (AOP (IC_LEFT (ic)), 0, FALSE));
}
else
*/
_G.receiveOffset = 0;
-#if 0
- /* PENDING: hack */
- if (!IS_STATIC (sym->etype))
- {
- addSetIfnotP (&publics, sym);
- }
-#endif
-
/* Record the last function name for debugging. */
_G.lastFunctionName = sym->rname;
/* adjust the stack for the function */
_G.stack.last = sym->stack;
- if (sym->stack)
+ if (sym->stack && IS_GB && sym->stack > -INT8MIN)
+ emit2 ("!enterxl", sym->stack);
+ else if (sym->stack)
emit2 ("!enterx", sym->stack);
else
emit2 ("!enter");
if (IS_ISR (sym->etype))
{
- wassert (0);
+ wassertl (0, "Tried to close an interrupt support function");
}
else
{
/* PENDING: calleeSave */
- if (_G.stack.offset)
+ if (_G.stack.offset && IS_GB && _G.stack.offset > INT8MAX)
+ {
+ emit2 ("!leavexl", _G.stack.offset);
+ }
+ else if (_G.stack.offset)
{
emit2 ("!leavex", _G.stack.offset);
}
/* if the result is a bit */
if (AOP_TYPE (result) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Tried to write A into a bit");
}
else
{
}
}
+bool
+couldDestroyCarry (asmop *aop)
+{
+ if (aop)
+ {
+ if (aop->type == AOP_EXSTK || aop->type == AOP_IY)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static void
+shiftIntoPair (int idx, asmop *aop)
+{
+ PAIR_ID id = PAIR_INVALID;
+
+ wassertl (IS_Z80, "Only implemented for the Z80");
+ // wassertl (aop->type == AOP_EXSTK, "Only implemented for EXSTK");
+
+ switch (idx)
+ {
+ case 0:
+ id = PAIR_HL;
+ break;
+ case 1:
+ id = PAIR_DE;
+ _push (PAIR_DE);
+ break;
+ default:
+ wassertl (0, "Internal error - hit default case");
+ }
+
+ emitDebug ("; Shift into pair idx %u", idx);
+
+ if (id == PAIR_HL)
+ {
+ setupPair (PAIR_HL, aop, 0);
+ }
+ else
+ {
+ setupPair (PAIR_IY, aop, 0);
+ emit2 ("push iy");
+ emit2 ("pop %s", _pairs[id].name);
+ }
+
+ aop->type = AOP_PAIRPTR;
+ aop->aopu.aop_pairId = id;
+ _G.pairs[id].offset = 0;
+ _G.pairs[id].last_type = aop->type;
+}
+
+static void
+setupToPreserveCarry (asmop *result, asmop *left, asmop *right)
+{
+ wassert (left && right);
+
+ if (IS_Z80)
+ {
+ if (couldDestroyCarry (right) && couldDestroyCarry (result))
+ {
+ shiftIntoPair (0, right);
+ shiftIntoPair (1, result);
+ }
+ else if (couldDestroyCarry (right))
+ {
+ shiftIntoPair (0, right);
+ }
+ else if (couldDestroyCarry (result))
+ {
+ shiftIntoPair (0, result);
+ }
+ else
+ {
+ /* Fine */
+ }
+ }
+}
+
/*-----------------------------------------------------------------*/
/* genPlus - generates code for addition */
/*-----------------------------------------------------------------*/
AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY)
{
/* Cant happen */
- wassert (0);
+ wassertl (0, "Tried to add two bits");
}
/* if left in bit space & right literal */
AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT)
{
/* Can happen I guess */
- wassert (0);
+ wassertl (0, "Tried to add a bit to a literal");
}
/* if I can do an increment instead
if (genPlusIncr (ic) == TRUE)
goto release;
- emitDebug ("; genPlusIncr failed");
+ emitDebug ("; Can't optimise plus by inc, falling back to the normal way");
size = getDataSize (IC_RESULT (ic));
if (isPair (AOP (IC_RESULT (ic))))
{
char *left, *right;
-
left = aopGetLitWordLong (AOP (IC_LEFT (ic)), 0, FALSE);
right = aopGetLitWordLong (AOP (IC_RIGHT (ic)), 0, FALSE);
- if (left && right)
+
+ if (AOP_TYPE(IC_LEFT(ic)) == AOP_LIT && AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT &&
+ left && right)
{
/* It's a pair */
/* PENDING: fix */
commitPair (AOP (IC_RESULT (ic)), PAIR_HL);
goto release;
}
- else if (size == 4)
- {
- emitDebug ("; WARNING: This add is probably broken.\n");
- }
}
+ if (size == 4)
+ {
+ /* Be paranoid on the GB with 4 byte variables due to how C
+ can be trashed by lda hl,n(sp).
+ */
+ _gbz80_emitAddSubLong (ic, TRUE);
+ goto release;
+ }
}
+ setupToPreserveCarry (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic)));
+
while (size--)
{
if (AOP_TYPE (IC_LEFT (ic)) == AOP_ACC)
/* if increment 16 bits in register */
if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) &&
- (size == 2))
+ (size == 2)
+ )
{
- fetchPair (PAIR_HL, AOP (IC_RESULT (ic)));
+ fetchPair (_getTempPairId(), AOP (IC_RESULT (ic)));
while (icount--) {
- emit2 ("dec hl");
+ emit2 ("dec %s", _getTempPairName());
}
- aopPut (AOP (IC_RESULT (ic)), "l", LSB);
- aopPut (AOP (IC_RESULT (ic)), "h", MSB16);
+
+ commitPair (AOP (IC_RESULT (ic)), _getTempPairId());
return TRUE;
}
if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY &&
AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Tried to subtract two bits");
goto release;
}
aopPut (AOP (IC_RESULT (ic)), "e", 0);
goto release;
}
- else if (size == 4)
- {
- emitDebug ("; WARNING: This sub is probably broken.\n");
- }
}
+ if (size == 4)
+ {
+ /* Be paranoid on the GB with 4 byte variables due to how C
+ can be trashed by lda hl,n(sp).
+ */
+ _gbz80_emitAddSubLong (ic, FALSE);
+ goto release;
+ }
}
+ setupToPreserveCarry (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic)));
+
/* if literal, add a,#-lit, else normal subb */
while (size--)
{
if (AOP_SIZE (IC_RESULT (ic)) == 3 &&
AOP_SIZE (IC_LEFT (ic)) == 3 &&
!sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic))))
- wassert (0);
+ {
+ wassertl (0, "Tried to subtract on a long pointer");
+ }
release:
freeAsmop (IC_LEFT (ic), NULL, ic);
genMult (iCode * ic)
{
/* Shouldn't occur - all done through function calls */
- wassert (0);
+ wassertl (0, "Multiplication is handled through support function calls");
}
/*-----------------------------------------------------------------*/
genDiv (iCode * ic)
{
/* Shouldn't occur - all done through function calls */
- wassert (0);
+ wassertl (0, "Division is handled through support function calls");
}
/*-----------------------------------------------------------------*/
ic->generated = 1;
}
+#if DISABLED
static const char *
_getPairIdName (PAIR_ID id)
{
return _pairs[id].name;
}
+#endif
/** Generic compare for > or <
*/
AOP_TYPE (right) == AOP_CRY)
{
/* Cant happen on the Z80 */
- wassert (0);
+ wassertl (0, "Tried to compare two bits");
}
else
{
else
emit2 ("cp %s", aopGet (AOP (right), offset, FALSE));
}
+ else if (size == 4 && IS_GB && requiresHL(AOP(right)) && requiresHL(AOP(left)))
+ {
+ // On the Gameboy we can't afford to adjust HL as it may trash the carry.
+ // Pull left into DE and right into HL
+ aopGet (AOP(left), LSB, FALSE);
+ emit2 ("ld d,h");
+ emit2 ("ld e,l");
+ aopGet (AOP(right), LSB, FALSE);
+
+ while (size--)
+ {
+ if (size == 0 && sign)
+ {
+ // Highest byte when signed needs the bits flipped
+ // Save the flags
+ emit2 ("push af");
+ emit2 ("ld a,(de)");
+ emit2 ("xor #0x80");
+ emit2 ("ld e,a");
+ emit2 ("ld a,(hl)");
+ emit2 ("xor #0x80");
+ emit2 ("ld d,a");
+ emit2 ("pop af");
+ emit2 ("ld a,e");
+ emit2 ("%s a,d", offset == 0 ? "sub" : "sbc");
+ }
+ else
+ {
+ emit2 ("ld a,(de)");
+ emit2 ("%s a,(hl)", offset == 0 ? "sub" : "sbc");
+ }
+
+ if (size != 0)
+ {
+ emit2 ("inc hl");
+ emit2 ("inc de");
+ }
+ offset++;
+ }
+ spillPair (PAIR_HL);
+ }
+ else if (size == 4 && IS_Z80 && couldDestroyCarry(AOP(right)) && couldDestroyCarry(AOP(left)))
+ {
+ setupPair (PAIR_HL, AOP (left), 0);
+ aopGet (AOP(right), LSB, FALSE);
+
+ while (size--)
+ {
+ if (size == 0 && sign)
+ {
+ // Highest byte when signed needs the bits flipped
+ // Save the flags
+ emit2 ("push af");
+ emit2 ("ld a,(hl)");
+ emit2 ("xor #0x80");
+ emit2 ("ld l,a");
+ emit2 ("ld a,%d(iy)", offset);
+ emit2 ("xor #0x80");
+ emit2 ("ld h,a");
+ emit2 ("pop af");
+ emit2 ("ld a,l");
+ emit2 ("%s a,h", offset == 0 ? "sub" : "sbc");
+ }
+ else
+ {
+ emit2 ("ld a,(hl)");
+ emit2 ("%s a,%d(iy)", offset == 0 ? "sub" : "sbc", offset);
+ }
+
+ if (size != 0)
+ {
+ emit2 ("inc hl");
+ }
+ offset++;
+ }
+ spillPair (PAIR_HL);
+ spillPair (PAIR_IY);
+ }
else
{
- /* Special cases:
- On the GB:
- If the left or the right is a lit:
- Load -lit into HL, add to right via, check sense.
- */
- if (IS_GB && size == 2 && (AOP_TYPE (right) == AOP_LIT || AOP_TYPE (left) == AOP_LIT))
- {
- PAIR_ID id = PAIR_DE;
- asmop *lit = AOP (right);
- asmop *op = AOP (left);
- swap_sense = TRUE;
-
- if (AOP_TYPE (left) == AOP_LIT)
- {
- swap_sense = FALSE;
- lit = AOP (left);
- op = AOP (right);
- }
- if (sign)
- {
- emit2 ("ld e,%s", aopGet (op, 0, 0));
- emit2 ("ld a,%s", aopGet (op, 1, 0));
- emit2 ("xor a,!immedbyte", 0x80);
- emit2 ("ld d,a");
- }
- else
- {
- id = getPairId (op);
- if (id == PAIR_INVALID)
- {
- fetchPair (PAIR_DE, op);
- id = PAIR_DE;
- }
- }
- spillPair (PAIR_HL);
- emit2 ("ld hl,%s", fetchLitSpecial (lit, TRUE, sign));
- emit2 ("add hl,%s", _getPairIdName (id));
- goto release;
- }
if (AOP_TYPE (right) == AOP_LIT)
{
lit = (unsigned long) floatFromVal (AOP (right)->aopu.aop_lit);
goto release;
}
}
+
if (sign)
{
/* First setup h and l contaning the top most bytes XORed */
emit2 ("ld %s,a", _fTmp[1]);
fDidXor = TRUE;
}
- if (!fDidXor)
- _clearCarry();
- }
- else
- {
- _clearCarry();
}
while (size--)
{
if (sign && size == 0)
{
emit2 ("ld a,%s", _fTmp[0]);
- emit2 ("sbc a,%s", _fTmp[1]);
+ emit2 ("%s a,%s", offset == 0 ? "sub" : "sbc", _fTmp[1]);
}
else
{
/* Subtract through, propagating the carry */
- emit2 ("sbc a,%s", aopGet (AOP (right), offset++, FALSE));
+ emit2 ("%s a,%s", offset == 0 ? "sub" : "sbc", aopGet (AOP (right), offset, FALSE));
+ offset++;
}
}
}
emit2 ("ld a,%s", aopGet (AOP (left), offset, FALSE));
if (size > 1)
{
- size--;
- offset++;
- while (size--)
+ while (--size)
{
- emit2 ("or a,%s", aopGet (AOP (left), offset, FALSE));
+ emit2 ("or a,%s", aopGet (AOP (left), ++offset, FALSE));
}
}
else
if (AOP_TYPE (left) == AOP_CRY &&
((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT)))
{
- wassert (0);
+ wassertl (0, "Tried to compare two bits");
}
else
{
if (AOP_TYPE (left) == AOP_CRY &&
((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT)))
{
- wassert (0);
+ wassertl (0, "Tried to compare a bit to either a literal or another bit");
}
else
{
if (AOP_TYPE (left) == AOP_CRY &&
AOP_TYPE (right) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Tried to and two bits");
}
else
{
if (AOP_TYPE (left) == AOP_CRY &&
AOP_TYPE (right) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Tried to OR two bits");
}
else
{
aopOp ((right = IC_RIGHT (ic)), ic, FALSE, FALSE);
aopOp ((result = IC_RESULT (ic)), ic, TRUE, FALSE);
-#ifdef DEBUG_TYPE
- emitDebug ("; Type res[%d] = l[%d]&r[%d]",
- AOP_TYPE (result),
- AOP_TYPE (left), AOP_TYPE (right));
- emitDebug ("; Size res[%d] = l[%d]&r[%d]",
- AOP_SIZE (result),
- AOP_SIZE (left), AOP_SIZE (right));
-#endif
-
/* if left is a literal & right is not then exchange them */
if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) ||
AOP_NEEDSACC (left))
if (AOP_TYPE (left) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Tried to perform an AND with a bit as an operand");
goto release;
}
(AOP_TYPE (result) == AOP_CRY) &&
(AOP_TYPE (left) != AOP_CRY))
{
- int posbit = isLiteralBit (lit);
- /* left & 2^n */
- if (posbit)
- {
- posbit--;
- _moveA (aopGet (AOP (left), posbit >> 3, FALSE));
- // bit = left & 2^n
- if (size)
- {
- wassert (0);
- emit2 ("mov c,acc.%d", posbit & 0x07);
- }
- // if(left & 2^n)
- else
- {
- if (ifx)
- {
- sprintf (buffer, "%d", posbit & 0x07);
- genIfxJump (ifx, buffer);
- }
- else
- {
- wassert (0);
- }
- goto release;
- }
- }
- else
- {
- symbol *tlbl = newiTempLabel (NULL);
- int sizel = AOP_SIZE (left);
- if (size)
- {
- wassert (0);
- emit2 ("setb c");
- }
- while (sizel--)
- {
- if ((bytelit = ((lit >> (offset * 8)) & 0x0FFL)) != 0x0L)
- {
- _moveA (aopGet (AOP (left), offset, FALSE));
- // byte == 2^n ?
- if ((posbit = isLiteralBit (bytelit)) != 0)
- {
- wassert (0);
- emit2 ("jb acc.%d,%05d$", (posbit - 1) & 0x07, tlbl->key + 100);
- }
- else
- {
- if (bytelit != 0x0FFL)
- emit2 ("and a,%s",
- aopGet (AOP (right), offset, FALSE));
- else
- /* For the flags */
- emit2 ("or a,a");
- emit2 ("!shortjp nz,!tlabel", tlbl->key + 100);
- }
- }
+ symbol *tlbl = newiTempLabel (NULL);
+ int sizel = AOP_SIZE (left);
+ if (size)
+ {
+ /* PENDING: Test case for this. */
+ emit2 ("scf");
+ }
+ while (sizel--)
+ {
+ if ((bytelit = ((lit >> (offset * 8)) & 0x0FFL)) != 0x0L)
+ {
+ _moveA (aopGet (AOP (left), offset, FALSE));
+ if (bytelit != 0x0FFL)
+ {
+ emit2 ("and a,%s", aopGet (AOP (right), offset, FALSE));
+ }
+ else
+ {
+ /* For the flags */
+ emit2 ("or a,a");
+ }
+ emit2 ("!shortjp nz,!tlabel", tlbl->key + 100);
+ }
offset++;
- }
- // bit = left & literal
- if (size)
- {
- emit2 ("clr c");
- emit2 ("!tlabeldef", tlbl->key + 100);
- }
- // if(left & literal)
- else
- {
- if (ifx)
- jmpTrueOrFalse (ifx, tlbl);
- goto release;
- }
- }
+ }
+ // bit = left & literal
+ if (size)
+ {
+ emit2 ("clr c");
+ emit2 ("!tlabeldef", tlbl->key + 100);
+ }
+ // if(left & literal)
+ else
+ {
+ if (ifx)
+ {
+ jmpTrueOrFalse (ifx, tlbl);
+ }
+ goto release;
+ }
outBitC (result);
goto release;
}
{
if (AOP_TYPE (left) == AOP_ACC)
{
- wassert (0);
+ wassertl (0, "Tried to perform an AND where the left operand is allocated into A");
}
else
{
// left & result in different registers
if (AOP_TYPE (result) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Tried to AND where the result is in carry");
}
else
{
operand *left, *right, *result;
int size, offset = 0;
unsigned long lit = 0L;
+ int bytelit = 0;
aopOp ((left = IC_LEFT (ic)), ic, FALSE, FALSE);
aopOp ((right = IC_RIGHT (ic)), ic, FALSE, FALSE);
aopOp ((result = IC_RESULT (ic)), ic, TRUE, FALSE);
-#if 1
- emitDebug ("; Type res[%d] = l[%d]&r[%d]",
- AOP_TYPE (result),
- AOP_TYPE (left), AOP_TYPE (right));
- emitDebug ("; Size res[%d] = l[%d]&r[%d]",
- AOP_SIZE (result),
- AOP_SIZE (left), AOP_SIZE (right));
-#endif
-
/* if left is a literal & right is not then exchange them */
if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) ||
AOP_NEEDSACC (left))
if (AOP_TYPE (left) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Tried to OR where left is a bit");
goto release;
}
+ // if(val | 0xZZ) - size = 0, ifx != FALSE -
+ // bit = val | 0xZZ - size = 1, ifx = FALSE -
if ((AOP_TYPE (right) == AOP_LIT) &&
(AOP_TYPE (result) == AOP_CRY) &&
(AOP_TYPE (left) != AOP_CRY))
{
- wassert (0);
+ symbol *tlbl = newiTempLabel (NULL);
+ int sizel = AOP_SIZE (left);
+
+ if (size)
+ {
+ wassertl (0, "Result is assigned to a bit");
+ }
+ /* PENDING: Modeled after the AND code which is inefficent. */
+ while (sizel--)
+ {
+ bytelit = (lit >> (offset * 8)) & 0x0FFL;
+
+ _moveA (aopGet (AOP (left), offset, FALSE));
+ /* OR with any literal is the same as OR with itself. */
+ emit2 ("or a,a");
+ emit2 ("!shortjp nz,!tlabel", tlbl->key + 100);
+
+ offset++;
+ }
+ if (ifx)
+ {
+ jmpTrueOrFalse (ifx, tlbl);
+ }
goto release;
}
// left & result in different registers
if (AOP_TYPE (result) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Result of OR is in a bit");
}
else
for (; (size--); offset++)
if (AOP_TYPE (left) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Tried to XOR a bit");
goto release;
}
+ // if(val & 0xZZ) - size = 0, ifx != FALSE -
+ // bit = val & 0xZZ - size = 1, ifx = FALSE -
if ((AOP_TYPE (right) == AOP_LIT) &&
(AOP_TYPE (result) == AOP_CRY) &&
(AOP_TYPE (left) != AOP_CRY))
{
- wassert (0);
+ symbol *tlbl = newiTempLabel (NULL);
+ int sizel = AOP_SIZE (left);
+
+ if (size)
+ {
+ /* PENDING: Test case for this. */
+ wassertl (0, "Tried to XOR left against a literal with the result going into a bit");
+ }
+ while (sizel--)
+ {
+ _moveA (aopGet (AOP (left), offset, FALSE));
+ emit2 ("xor a,%s", aopGet (AOP (right), offset, FALSE));
+ emit2 ("!shortjp nz,!tlabel", tlbl->key + 100);
+ offset++;
+ }
+ if (ifx)
+ {
+ jmpTrueOrFalse (ifx, tlbl);
+ }
+ else
+ {
+ wassertl (0, "Result of XOR was destined for a bit");
+ }
goto release;
}
// left & result in different registers
if (AOP_TYPE (result) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Result of XOR is in a bit");
}
else
for (; (size--); offset++)
tlbl1 = newiTempLabel (NULL);
/* Left is already in result - so now do the shift */
- if (shCount <= 2)
+ if (shCount <= 4)
{
while (shCount--)
{
emitLabel (tlbl->key + 100);
}
- emit2 ("or a,a");
while (size--)
{
l = aopGet (AOP (result), offset, FALSE);
- emit2 ("rl %s", l);
+ if (offset == 0)
+ {
+ emit2 ("sla %s", l);
+ }
+ else
+ {
+ emit2 ("rl %s", l);
+ }
offset++;
}
case 0:
break;
case 1:
- emit2 ("rl a");
+ emit2 ("sla a");
break;
case 2:
- emit2 ("rl a");
+ emit2 ("sla a");
emit2 ("rl a");
break;
case 3:
- emit2 ("rl a");
+ emit2 ("sla a");
emit2 ("rl a");
emit2 ("rl a");
break;
case 4:
- emit2 ("rl a");
+ emit2 ("sla a");
emit2 ("rl a");
emit2 ("rl a");
emit2 ("rl a");
break;
case 5:
- emit2 ("rr a");
+ emit2 ("srl a");
emit2 ("rr a");
emit2 ("rr a");
break;
case 6:
- emit2 ("rr a");
+ emit2 ("srl a");
emit2 ("rr a");
break;
case 7:
- emit2 ("rr a");
+ emit2 ("srl a");
break;
}
}
{
movLeft2Result (left, LSB, result, MSB16, 0);
aopPut (AOP (result), "!zero", 0);
- shiftL1Left2Result (left, MSB16, result, MSB16, shCount);
+ shiftL1Left2Result (left, LSB, result, MSB16, shCount);
}
else
{
size = getSize (operandType (result));
-#if VIEW_SIZE
- emitDebug ("; shift left result %d, left %d", size,
- AOP_SIZE (left));
-#endif
-
/* I suppose that the left size >= result size */
if (shCount == 0)
{
/* now move the left to the result if they are not the
same */
-#if 1
+
if (!sameRegs (AOP (left), AOP (result)))
{
offset++;
}
}
-#else
- size = AOP_SIZE (result);
- offset = 0;
- while (size--)
- {
- l = aopGet (AOP (left), offset, FALSE);
- aopPut (AOP (result), l, offset);
- offset++;
- }
-#endif
-
tlbl = newiTempLabel (NULL);
size = AOP_SIZE (result);
emitLabel (tlbl->key + 100);
l = aopGet (AOP (result), offset, FALSE);
- emit2 ("or a,a");
-
while (size--)
{
- l = aopGet (AOP (result), offset++, FALSE);
- emit2 ("rl %s", l);
+ l = aopGet (AOP (result), offset, FALSE);
+
+ if (offset == 0)
+ {
+ emit2 ("sla %s", l);
+ }
+ else
+ {
+ emit2 ("rl %s", l);
+ }
+ offset++;
}
emitLabel (tlbl1->key + 100);
emit2 ("dec a");
l = aopGet (AOP (left), 0, FALSE);
- emit2 ("or a,a");
-
if (AOP (result)->type == AOP_REG)
{
aopPut (AOP (result), l, 0);
_moveA (aopGet (AOP (left), offl, FALSE));
if (sign)
{
- wassert (0);
+ while (shCount--)
+ {
+ emit2 ("%s a", sign ? "sra" : "srl");
+ }
}
else
{
{
movLeft2Result (left, MSB16, result, LSB, sign);
}
- aopPut (AOP (result), "!zero", 1);
+ if (sign)
+ {
+ /* Sign extend the result */
+ _moveA(aopGet (AOP (result), 0, FALSE));
+ emit2 ("rlc a");
+ emit2 ("sbc a,a");
+
+ aopPut (AOP (result), ACC_NAME, MSB16);
+ }
+ else
+ {
+ aopPut (AOP (result), "!zero", 1);
+ }
}
/* 1 <= shCount <= 7 */
else
size = getSize (operandType (result));
- emitDebug ("; shift right result %d, left %d", size,
- AOP_SIZE (left));
-
/* I suppose that the left size >= result size */
if (shCount == 0)
{
genrshOne (result, left, shCount, sign);
break;
case 2:
- /* PENDING: sign support */
genrshTwo (result, left, shCount, sign);
break;
case 4:
spillCached ();
if (sym->stack <= 0)
{
- emit2 ("!ldahlsp", sym->stack + _G.stack.pushed + _G.stack.offset);
+ setupPairFromSP (PAIR_HL, sym->stack + _G.stack.pushed + _G.stack.offset);
}
else
{
- emit2 ("!ldahlsp", sym->stack + _G.stack.pushed + _G.stack.offset + _G.stack.param_offset);
+ setupPairFromSP (PAIR_HL, sym->stack + _G.stack.pushed + _G.stack.offset + _G.stack.param_offset);
}
- emit2 ("ld d,h");
- emit2 ("ld e,l");
+ commitPair (AOP (IC_RESULT (ic)), PAIR_HL);
}
else
{
emit2 ("ld de,!hashedstr", sym->rname);
+ commitPair (AOP (IC_RESULT (ic)), PAIR_DE);
}
- aopPut (AOP (IC_RESULT (ic)), "e", 0);
- aopPut (AOP (IC_RESULT (ic)), "d", 1);
}
else
{
{
emit2 ("ld hl,#%s", sym->rname);
}
- aopPut (AOP (IC_RESULT (ic)), "l", 0);
- aopPut (AOP (IC_RESULT (ic)), "h", 1);
+ commitPair (AOP (IC_RESULT (ic)), PAIR_HL);
}
freeAsmop (IC_RESULT (ic), NULL, ic);
}
result = IC_RESULT (ic);
right = IC_RIGHT (ic);
-#if 1
/* Dont bother assigning if they are the same */
if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic)))
{
emitDebug ("; (operands are equal %u)", operandsEqu (IC_RESULT (ic), IC_RIGHT (ic)));
return;
}
-#endif
aopOp (right, ic, FALSE, FALSE);
aopOp (result, ic, TRUE, FALSE);
/* if the result is a bit */
if (AOP_TYPE (result) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Tried to assign to a bit");
}
/* general case */
aopPut (AOP (result), "a", 0);
aopPut (AOP (result), "e", 1);
}
+ else if (size == 4 && requiresHL (AOP (right)) && requiresHL (AOP (result)) && IS_GB)
+ {
+ /* Special case - simple memcpy */
+ aopGet (AOP (right), LSB, FALSE);
+ emit2 ("ld d,h");
+ emit2 ("ld e,l");
+ aopGet (AOP (result), LSB, FALSE);
+
+ while (size--)
+ {
+ emit2 ("ld a,(de)");
+ /* Peephole will optimise this. */
+ emit2 ("ld (hl),a");
+
+ if (size != 0)
+ {
+ emit2 ("inc hl");
+ emit2 ("inc de");
+ }
+ }
+ spillPair (PAIR_HL);
+ }
else
{
while (size--)
/* if the result is a bit */
if (AOP_TYPE (result) == AOP_CRY)
{
- wassert (0);
+ wassertl (0, "Tried to cast to a bit");
}
/* if they are the same size : or less */
goto release;
}
- /* PENDING: should be OK. */
-#if 0
- /* if the result is of type pointer */
- if (IS_PTR (ctype))
- {
- wassert (0);
- }
-#endif
-
- /* so we now know that the size of destination is greater
+ /* So we now know that the size of destination is greater
than the size of the source */
/* we move to result for the size of source */
size = AOP_SIZE (right);
const char *l = aopGet (AOP (right), AOP_SIZE (right) - 1,
FALSE);
_moveA (l);
- emitDebug ("; genCast: sign extend untested.");
emit2 ("rla ");
emit2 ("sbc a,a");
while (size--)
freeAsmop (IC_RESULT (ic), NULL, ic);
}
+enum
+ {
+ /** Maximum number of bytes to emit per line. */
+ DBEMIT_MAX_RUN = 8
+ };
+
+/** Context for the byte output chunker. */
+typedef struct
+{
+ unsigned char buffer[DBEMIT_MAX_RUN];
+ int pos;
+} DBEMITCTX;
+
+
+/** Flushes a byte chunker by writing out all in the buffer and
+ reseting.
+*/
+static void
+_dbFlush(DBEMITCTX *self)
+{
+ char line[256];
+
+ if (self->pos > 0)
+ {
+ int i;
+ sprintf(line, ".db 0x%02X", self->buffer[0]);
+
+ for (i = 1; i < self->pos; i++)
+ {
+ sprintf(line + strlen(line), ", 0x%02X", self->buffer[i]);
+ }
+ emit2(line);
+ }
+ self->pos = 0;
+}
+
+/** Write out another byte, buffering until a decent line is
+ generated.
+*/
+static void
+_dbEmit(DBEMITCTX *self, int c)
+{
+ if (self->pos == DBEMIT_MAX_RUN)
+ {
+ _dbFlush(self);
+ }
+ self->buffer[self->pos++] = c;
+}
+
+/** Context for a simple run length encoder. */
+typedef struct
+{
+ unsigned last;
+ unsigned char buffer[128];
+ int pos;
+ /** runLen may be equivalent to pos. */
+ int runLen;
+} RLECTX;
+
+enum
+ {
+ RLE_CHANGE_COST = 4,
+ RLE_MAX_BLOCK = 127
+ };
+
+/** Flush the buffer of a run length encoder by writing out the run or
+ data that it currently contains.
+*/
+static void
+_rleCommit(RLECTX *self)
+{
+ int i;
+ if (self->pos != 0)
+ {
+ DBEMITCTX db;
+ memset(&db, 0, sizeof(db));
+
+ emit2(".db %u", self->pos);
+
+ for (i = 0; i < self->pos; i++)
+ {
+ _dbEmit(&db, self->buffer[i]);
+ }
+ _dbFlush(&db);
+ }
+ /* Reset */
+ self->pos = 0;
+}
+
+/* Encoder design:
+ Can get either a run or a block of random stuff.
+ Only want to change state if a good run comes in or a run ends.
+ Detecting run end is easy.
+ Initial state?
+
+ Say initial state is in run, len zero, last zero. Then if you get a
+ few zeros then something else then a short run will be output.
+ Seems OK. While in run mode, keep counting. While in random mode,
+ keep a count of the run. If run hits margin, output all up to run,
+ restart, enter run mode.
+*/
+
+/** Add another byte into the run length encoder, flushing as
+ required. The run length encoder uses the Amiga IFF style, where
+ a block is prefixed by its run length. A positive length means
+ the next n bytes pass straight through. A negative length means
+ that the next byte is repeated -n times. A zero terminates the
+ chunks.
+*/
+static void
+_rleAppend(RLECTX *self, int c)
+{
+ int i;
+
+ if (c != self->last)
+ {
+ /* The run has stopped. See if it is worthwhile writing it out
+ as a run. Note that the random data comes in as runs of
+ length one.
+ */
+ if (self->runLen > RLE_CHANGE_COST)
+ {
+ /* Yes, worthwhile. */
+ /* Commit whatever was in the buffer. */
+ _rleCommit(self);
+ emit2(".db -%u,0x%02X", self->runLen, self->last);
+ }
+ else
+ {
+ /* Not worthwhile. Append to the end of the random list. */
+ for (i = 0; i < self->runLen; i++)
+ {
+ if (self->pos >= RLE_MAX_BLOCK)
+ {
+ /* Commit. */
+ _rleCommit(self);
+ }
+ self->buffer[self->pos++] = self->last;
+ }
+ }
+ self->runLen = 1;
+ self->last = c;
+ }
+ else
+ {
+ if (self->runLen >= RLE_MAX_BLOCK)
+ {
+ /* Commit whatever was in the buffer. */
+ _rleCommit(self);
+
+ emit2 (".db -%u,0x%02X", self->runLen, self->last);
+ self->runLen = 0;
+ }
+ self->runLen++;
+ }
+}
+
+static void
+_rleFlush(RLECTX *self)
+{
+ _rleAppend(self, -1);
+ _rleCommit(self);
+ self->pos = 0;
+ self->last = 0;
+ self->runLen = 0;
+}
+
+/** genArrayInit - Special code for initialising an array with constant
+ data.
+*/
+static void
+genArrayInit (iCode * ic)
+{
+ literalList *iLoop;
+ int ix;
+ int elementSize = 0, eIndex, i;
+ unsigned val, lastVal;
+ sym_link *type;
+ RLECTX rle;
+
+ memset(&rle, 0, sizeof(rle));
+
+ aopOp (IC_LEFT(ic), ic, FALSE, FALSE);
+
+ if (AOP_TYPE(IC_LEFT(ic)) == AOP_IMMD)
+ {
+ /* Emit the support function call and the destination address. */
+ emit2("call __initrleblock");
+ emit2(".dw %s", aopGetWord (AOP(IC_LEFT(ic)), 0));
+ }
+ else
+ {
+ wassertl (0, "Unexpected operand to genArrayInit.\n");
+ }
+
+ type = operandType(IC_LEFT(ic));
+
+ if (type && type->next)
+ {
+ elementSize = getSize(type->next);
+ }
+ else
+ {
+ wassertl (0, "Can't determine element size in genArrayInit.");
+ }
+
+ iLoop = IC_ARRAYILIST(ic);
+ lastVal = (unsigned)-1;
+
+ /* Feed all the bytes into the run length encoder which will handle
+ the actual output.
+ This works well for mixed char data, and for random int and long
+ data.
+ */
+ while (iLoop)
+ {
+ ix = iLoop->count;
+
+ if (ix != 0)
+ {
+ for (i = 0; i < ix; i++)
+ {
+ for (eIndex = 0; eIndex < elementSize; eIndex++)
+ {
+ val = (((int)iLoop->literalValue) >> (eIndex * 8)) & 0xff;
+ _rleAppend(&rle, val);
+ }
+ }
+ }
+
+ iLoop = iLoop->next;
+ }
+
+ _rleFlush(&rle);
+ /* Mark the end of the run. */
+ emit2(".db 0");
+
+ freeAsmop (IC_LEFT(ic), NULL, ic);
+}
+
/*-----------------------------------------------------------------*/
/* genZ80Code - generate code for Z80 based controllers */
/*-----------------------------------------------------------------*/
iCode *ic;
int cln = 0;
- /* HACK */
+ /* Hack */
if (IS_GB)
{
_fReturn = _gbz80_return;
addSet (&_G.sendSet, ic);
break;
+ case ARRAYINIT:
+ genArrayInit(ic);
+ break;
+
default:
ic = ic;
- /* piCode(ic,stdout); */
-
}
}
}
codeOutFile = fp;
}
+
+ freeTrace(&_G.lines.trace);
+ freeTrace(&_G.trace.aops);
}
/*
return ret;
}
+static char *
+fetchLitSpecial (asmop * aop, bool negate, bool xor)
+{
+ unsigned long v;
+ value *val = aop->aopu.aop_lit;
+
+ wassert (aop->type == AOP_LIT);
+ wassert (!IS_FLOAT (val->type));
+
+ v = (unsigned long) floatFromVal (val);
+
+ if (xor)
+ v ^= 0x8000;
+ if (negate)
+ v = 0-v;
+ v &= 0xFFFF;
+
+ tsprintf (buffer, "!immedword", v);
+ return traceAlloc(&_G.trace.aops, Safe_strdup (buffer));
+}
+
+
*/