X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fz80%2Fgen.c;h=f065a37e76e2f74d4267f0b05ab92da52f16aa6c;hb=6806abbb5bdd22f693b8e58e5c853fe85a3c2e08;hp=d5009fd0f39f347ed547dd3dd4f10a91b11f793d;hpb=a8bd79f0b8619a3d1773c58c623888d2e2fe7c22;p=fw%2Fsdcc diff --git a/src/z80/gen.c b/src/z80/gen.c index d5009fd0..f065a37e 100644 --- a/src/z80/gen.c +++ b/src/z80/gen.c @@ -1,6 +1,33 @@ /*------------------------------------------------------------------------- gen.c - Z80 specific code generator. + + Michael Hope 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 @@ -28,32 +55,30 @@ 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 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 #include @@ -93,7 +118,7 @@ */ enum { - DISABLE_DEBUG = 1 + DISABLE_DEBUG = 0 }; static char *_z80_return[] = @@ -192,6 +217,42 @@ static struct 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; +} + +#if 0 +static const char * +_getTempPairPart(int idx) +{ + wassertl (idx == LSB || idx == MSB16, "Invalid pair offset"); + + if (idx == LSB) + { + return _pairs[_getTempPairId()].l; + } + else + { + return _pairs[_getTempPairId()].h; + } +} +#endif + static void _tidyUp (char *buf) { @@ -957,27 +1018,6 @@ requiresHL (asmop * aop) } } -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) { @@ -1039,18 +1079,21 @@ fetchPairLong (PAIR_ID pairId, asmop * aop, int offset) /* 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) { @@ -1743,8 +1786,7 @@ assignResultValue (operand * oper) _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); } @@ -2723,7 +2765,7 @@ genPlus (iCode * ic) 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)); @@ -2731,10 +2773,11 @@ genPlus (iCode * 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 */ @@ -2816,7 +2859,7 @@ genPlus (iCode * ic) } else if (size == 4) { - emitDebug ("; WARNING: This add is probably broken.\n"); + // Fall through } } } @@ -2895,15 +2938,16 @@ genMinusDec (iCode * ic) /* 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; } @@ -3004,6 +3048,9 @@ genMinus (iCode * ic) } else if (size == 4) { + /* Anything could be on the stack, and we can't afford + to setup the base pointer as that may nuke the carry. + */ emitDebug ("; WARNING: This sub is probably broken.\n"); } } @@ -3149,11 +3196,13 @@ genIfxJump (iCode * ic, char *jval) ic->generated = 1; } +#if DISABLED static const char * _getPairIdName (PAIR_ID id) { return _pairs[id].name; } +#endif /** Generic compare for > or < */ @@ -3194,6 +3243,9 @@ genCmp (operand * left, operand * right, } else { +#if 0 + // PENDING: Doesn't work around zero + /* Special cases: On the GB: If the left or the right is a lit: @@ -3233,6 +3285,7 @@ genCmp (operand * left, operand * right, emit2 ("add hl,%s", _getPairIdName (id)); goto release; } +#endif if (AOP_TYPE (right) == AOP_LIT) { lit = (unsigned long) floatFromVal (AOP (right)->aopu.aop_lit); @@ -5566,6 +5619,246 @@ genReceive (iCode * ic) 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 */ /*-----------------------------------------------------------------*/ @@ -5837,6 +6130,10 @@ genZ80Code (iCode * lic) addSet (&_G.sendSet, ic); break; + case ARRAYINIT: + genArrayInit(ic); + break; + default: ic = ic; /* piCode(ic,stdout); */ @@ -5886,4 +6183,26 @@ _isPairUsed (iCode * ic, PAIR_ID pairId) 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 gc_strdup (buffer); +} + + */