diff options
Diffstat (limited to 'bcc/hardop.c')
-rw-r--r-- | bcc/hardop.c | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/bcc/hardop.c b/bcc/hardop.c new file mode 100644 index 0000000..098d22b --- /dev/null +++ b/bcc/hardop.c @@ -0,0 +1,510 @@ +/* hardop.c - hardware operations for bcc */ + +/* Copyright (C) 1992 Bruce Evans */ + +#include "const.h" +#include "types.h" +#include "byteord.h" +#include "condcode.h" +#include "gencode.h" +#include "reg.h" +#include "sc.h" +#include "scan.h" +#include "sizes.h" +#include "type.h" + +FORWARD void sub1 P((struct symstruct *source, struct symstruct *target)); + +PUBLIC void add(source, target) +struct symstruct *source; +struct symstruct *target; +{ + scalar_t sscalar; + + if (source->indcount == 0 && source->storage != CONSTANT && + (target->indcount != 0 || target->storage & ALLDATREGS)) + swapsym(target, source); + if ((sscalar = source->type->scalar) & DLONG) + { + longop(ADDOP, source, target); + return; + } + if (sscalar & RSCALAR) + { + floatop(ADDOP, source, target); + return; + } + if (source->storage == CONSTANT) + { + extend(target); + if (target->indcount != 0 || target->storage & reguse) + loadany(target); + target->offset.offi += (offset_t) source->offset.offv; + } + else if (source->indcount == 0) + { + /* target is also direct */ + sscalar |= target->type->scalar; /* remember if unsigned/long */ + target->type = pctype; /* fake to use indexadr() */ + indexadr(source, target); + } + else + { + /* source is indirect and not DREG */ + extend(target); + load(target, DREG); + outadd(); + movereg(source, DREG); + if (source->type->scalar & CHAR) + adc0(); + } + target->type = iscalartotype(target->type->scalar | sscalar); +} + +PUBLIC void incdec(op, source) +op_pt op; +struct symstruct *source; +{ + offset_t bump; + bool_t postflag; + store_t regmark; + struct symstruct *target; + struct symstruct targ; +#ifdef MC6809 + store_pt targreg; +#endif + + *(target = &targ) = *source; + bump = 1; + if (targ.type->constructor & POINTER) + bump = targ.type->nexttype->typesize; + if (op == PREDECOP || op == POSTDECOP) + bump = -bump; + postflag = FALSE; + if (op == POSTDECOP || op == POSTINCOP) + postflag = TRUE; + reguse |= targ.storage; + if (targ.type->scalar & DLONG) /* cannot be direct */ + { + if (postflag) + { + regmark = reguse; + if (((reguse |= OPREG) & allindregs) == allindregs) + { + saveopreg(); + load(source, OPREG); + pushreg(source->storage); + restoreopreg(); + } + else + { + loadany(source); + reguse = regmark | source->storage; + saveopreg(); + } + } + else + saveopreg(); + pointat(target); + switch (op) + { + case PREDECOP: + case POSTDECOP: + call("ldec"); + break; + case PREINCOP: + case POSTINCOP: + call("linc"); + break; + } + outlongendian(); + restoreopreg(); + if (postflag && source->storage == OPREG) + poplist(OPREG); + return; + } + if (targ.type->scalar & RSCALAR) + { + saveopreg(); + pointat(target); + switch (op) + { + case PREDECOP: + call("Fpredec"); + break; + case PREINCOP: + call("Fpreinc"); + break; + case POSTDECOP: + call("Fpostdec"); + break; + case POSTINCOP: + call("Fpostinc"); + break; + } + outntypechar(targ.type); + restoreopreg(); + if (postflag) + { + justpushed(source); + source->type = targ.type; + } + return; + } + loadany(source); +#ifdef MC6809 + if (postflag && targ.flags != REGVAR && + !(source->storage & ALLDATREGS) && + ((reguse |= source->storage) & allindregs) != allindregs) + { + targreg = getindexreg(); + outlea(); + outregname(targreg); + outtab(); + outshex(bump); + outncregname(source->storage); + } + else + { + addconst(bump, targreg = source->storage); + if (postflag) + source->offset.offi = -bump; + } + storereg(targreg, target); + target->storage = targreg; + target->offset.offi = 0; +#else + addconst(bump, source->storage); + if (postflag) + source->offset.offi = -bump; + storereg(source->storage, target); + target->storage = source->storage; + target->offset.offi = 0; +#endif +} + +PUBLIC void neg(target) +struct symstruct *target; +{ + scalar_t scalar; + struct symstruct *source; + + if ((scalar = target->type->scalar) & DLONG) + long1op(NEGOP, target); + else if (scalar & RSCALAR) + float1op(NEGOP, target); + else + { + if (scalar & SHORT) + extend(target); + if (!(target->storage & ALLDATREGS)) + { + /* load 0, subtract is shorter than load, negate */ + /* if no hardware integer negate; about the same if there is */ + sub1(target, source = constsym((value_t) 0)); + *target = *source; + } + else + { + load(target, DREG); + negreg(target->storage); + target->storage = DREG; + } + target->type = iscalartotype(scalar); + } +} + +PUBLIC void not(target) +struct symstruct *target; +{ + if (target->type->scalar & DLONG) + long1op(NOTOP, target); + else + { + extend(target); + load(target, DREG); + comDreg(); + } +} + +/* 1-byte ops like AND acting on integers (not both constant) */ + +PUBLIC void op1(op, source, target) +op_pt op; +struct symstruct *source; +struct symstruct *target; +{ + char *opstr; +#ifdef OP1 +# if MAXINDIRECT > 1 + indn_t indcount; +# endif +#endif + bool_t resultchar; + scalar_t resultscalar; + scalar_t sscalar; + scalar_t tscalar; + + if ((sscalar = source->type->scalar) & DLONG) + { + longop(op, source, target); + return; + } + /* Emergency fix. The types of constants should be reduced here and in + * other low-level routines anyway, and not in exptree.c and table.c, + * and for the 80386 and maybe the 8086 they would be better not + * reduced. + */ + if (source->storage == CONSTANT && ischarconst(source->offset.offv)) + { + if (sscalar & UNSIGNED) + source->type = uctype; + else + source->type = ctype; + sscalar = source->type->scalar; + } + tscalar = target->type->scalar; + if (target->storage == CONSTANT && ischarconst(target->offset.offv)) + { + if (sscalar & UNSIGNED) + target->type = uctype; + else + target->type = ctype; + tscalar = target->type->scalar; + } + resultscalar = sscalar | tscalar; + if (op != ANDOP) + resultchar = sscalar & tscalar & CHAR; + else if ((resultchar = (sscalar | tscalar) & CHAR) != 0 && + source->storage == CONSTANT) + { + source->offset.offv &= CHMASKTO; + if (sscalar & UNSIGNED) + source->type = uctype; + else + source->type = ctype; + sscalar = source->type->scalar; + } + if (target->indcount != 0 && + ((tscalar & CHAR && !(sscalar & CHAR) && + op != ANDOP) || (source->indcount == 0 && source->storage != CONSTANT))) + { + swapsym(target, source); + sscalar = tscalar; + tscalar = target->type->scalar; + } + if (source->indcount == 0 && source->storage != CONSTANT) + { + loadpres(source, target); + push(source); + } +#if MAXINDIRECT > 1 + else if (source->indcount >= MAXINDIRECT && !(sscalar & CHAR)) + { + address(source); + if (!(target->storage & ALLDATREGS)) + preserve(target); + load(source, getindexreg()); + indirec(source); + } +#endif + if (!(tscalar & CHAR) && + op == ANDOP && sscalar & CHAR && target->indcount == 1) + cast(ctype, target); + if (!(target->storage & ALLDATREGS) || target->indcount != 0) + pres2(target, source); + load(target, DREG); + opstr = opstring(op); + if (source->storage == CONSTANT && op == ANDOP) + andconst((offset_t) source->offset.offv); +#ifdef OP1 + else if (tscalar & CHAR && !(sscalar & CHAR) && op != ANDOP) + outload(); + else + outop2str(opstr); +#else /* OP1 */ + else + { + if (tscalar & CHAR && !(sscalar & CHAR) && op != ANDOP) + extend(target); + outop2str(opstr); + } +#endif /* OP1 */ + if (source->storage == CONSTANT) + { + if (op != ANDOP) + { +#ifdef OP1 + if (!(sscalar & CHAR)) + { + outhiaccum(); + outncimmadr((offset_t) ((uoffset_t) source->offset.offv + >> (INT16BITSTO - CHBITSTO))); + outop2str(opstr); + } + outregname(BREG); + outncimmadr((offset_t) source->offset.offv & CHMASKTO); +#else /* OP1 */ + if (!(sscalar & CHAR)) + { + outregname(DREG); + bumplc(); + } + else + outregname(BREG); +# ifdef I8088 + if (i386_32 && !(sscalar & CHAR)) + bumplc2(); +# endif + outncimmadr((offset_t) source->offset.offv); + +#endif /* OP1 */ + } + } + else if (sscalar & CHAR) + { + outregname(BREG); + outopsep(); + outadr(source); + } + else + { +#ifdef MC6809 + source->type = ctype; /* fool outadr to avoid ,S++ */ +#endif +#ifdef OP1 + if (!(tscalar & CHAR) || op != ANDOP) + { + outhiaccum(); +# if MAXINDIRECT > 1 + indcount = source->indcount; +# endif + outopsep(); +# if BIG_ENDIAN == 0 + ++source->offset.offi; +# endif + outadr(source); +# if BIG_ENDIAN == 0 + --source->offset.offi; +# endif +# if MAXINDIRECT > 1 + source->indcount = indcount; +# else + source->indcount = 1; +# endif + outop2str(opstr); + } + outregname(BREG); + outopsep(); +# if BIG_ENDIAN + ++source->offset.offi; +# endif + outadr(source); + } +#else /* OP1 */ + if (!(tscalar & CHAR) || op != ANDOP) + outregname(DREG); + else + outregname(BREG); + outopsep(); + outadr(source); + } +#endif /* OP1 */ + if (resultchar) + { + target->storage = BREG; + if (resultscalar & UNSIGNED) + target->type = uctype; + else + target->type = ctype; + } + else + { + target->storage = DREG; + target->type = iscalartotype(resultscalar); + } +} + +PUBLIC void ptrsub(source, target) +struct symstruct *source; +struct symstruct *target; +{ + label_t exitlab; + uoffset_t factor; + label_t usignlab; + + if (source->indcount == 0 && source->storage != CONSTANT) + { + loadpres(source, target); + push(source); + } + if (target->indcount == 0) + { + pres2(target, source); + load(target, DREG); + } + factor = target->type->nexttype->typesize; + source->type = target->type = itype; + sub1(source, target); + if (factor != 1) + { + pushlist(CCREG); + sbranch(HS, usignlab = getlabel()); /* HS == no carry */ + negDreg(); + outnlabel(usignlab); + target->type = uitype; + softop(DIVOP, constsym((value_t) factor), target); + target->type = itype; + poplist(CCREG); + sbranch(HS, exitlab = getlabel()); + negDreg(); + outnlabel(exitlab); + } +} + +PUBLIC void sub(source, target) +struct symstruct *source; +struct symstruct *target; +{ + scalar_t sscalar; + + if ((sscalar = source->type->scalar) & DLONG) + { + longop(SUBOP, source, target); + return; + } + if (sscalar & RSCALAR) + { + floatop(SUBOP, source, target); + return; + } + if (source->storage == CONSTANT) + { + extend(target); + if (target->indcount != 0 || target->storage & reguse) + loadany(target); + target->offset.offi -= (offset_t) source->offset.offv; + } + else + sub1(source, target); + target->type = iscalartotype(target->type->scalar | sscalar); +} + +PRIVATE void sub1(source, target) +struct symstruct *source; +struct symstruct *target; +{ + if (source->storage == CONSTANT) + source->type = itype; + else if (source->indcount == 0) + { + loadpres(source, target); + push(source); + } + extend(target); + load(target, DREG); + outsub(); + movereg(source, DREG); + if (source->type->scalar & CHAR) + sbc0(); +} |