static char *ID_swagen="@(#) swagen.c: 1.3 5/4/84"; #include <stdio.h> #include "systems.h" #include "symbols.h" #include "instab.h" #include "parse.h" #include "gendefs.h" #include "expand.h" #include "expand2.h" #define MULOP 0 #define DIVOP 1 #define MODOP 2 #define SIGNED 0 #define UNSIGNED 1 extern upsymins *lookup(); extern short workaround, /* no software workaround flag */ opt; /* no optimize flag */ extern unsigned short line; /* current line number */ /* * chip fix routines: * The following three routines (mtoregcheck, intaft1check, and intaft2check) * are chip fix routines for the Interrupt After TSTW bug. * * If an interupt is responded to after a certain sequence of instructions, * (described below) the last instruction in the sequence may not set flags * at all. * * Instruction Sequences which can cause problems: * First Instruction: * Type 1) Instruction which does a store to a register and whose * last source operand comes from memory. This does not * include any discontinuities, SWAPs, RESTORE, or instructions * without destinations (ie. BIT, TST, CMP). * Type 2) Instruction which does a store to a register and is a * multiple cycle ALU operation (ie. multiply, divide, modulo, * insert field, move negate). * Second Instruction: * Any instruction which is executed in 3 cycles. These are: * TSTW %reg MOVW %reg,%reg MCOMW %reg %reg * INCW %reg DECW %reg CLRW %reg * The fix for thisproblem is to insert a NOP before the second instruction * in the sequence. * */ /* This routine will return a 1 so that an indicator may be set if * the first instruction in the dangerous sequence is assembled. */ mtoregcheck(src, dest) register addrmode *src, *dest; { #ifdef CHIPFIX if ((dest->admode == REGMD) && (src->admode == REGDFMD || src->admode == DSPMD || src->admode == DSPDFMD || src->admode == EXADMD || src->admode == ABSMD || src->admode == EXADDFMD || src->admode == ABSDFMD) && workaround ) return(1); else return(0); #else return(0); #endif } /* mtoregcheck */ /* This routine will generate a NOP if the indicator is set, and * the operand is a register operand. */ intaft1check(opnd, mtoreg) register addrmode *opnd; short mtoreg; { #ifdef CHIPFIX instr *newins; if (mtoreg != 1) return; if ( (opnd->admode == REGMD) && workaround ) { newins = (*lookup("NOP",N_INSTALL,MNEMON)).itp; generate(newins->nbits, NOACTION, newins->opcode, NULLSYM); } #endif } /* intaft1check */ /* This routine will generate a NOP if the indicator is set, and * both of the operands are register operands. */ intaft2check(opnd1, opnd2, mtoreg) register addrmode *opnd1, *opnd2; short mtoreg; { #ifdef CHIPFIX instr *newins; if (mtoreg != 1) return; if ((opnd1->admode == REGMD) && (opnd2->admode == REGMD && workaround)) { newins = (*lookup("NOP",N_INSTALL,MNEMON)).itp; generate(newins->nbits, NOACTION, newins->opcode, NULLSYM); } #endif } /* intaft2check */ /* * chip fix routine: Due to pipelining on the cpu chip * it is possible for the chip to have the "next" instruction * almost completely executed while it is waiting for the memory * write of the "current" instr. to be completed, even executed * to the point where the PC is incremented past the "next" instr. * If an interrupt occurs in a situation like this then the PC * will be restored to one past the "next" instr, thereby, in effect, * skipping one instr. The fix for this is to place a NOP after * every instr. that does a memory write, except for: * CALL, call, SAVE, save, PUSHW, pushw, PUSHAW, pushaw * These don't need the NOP padding. */ pcintcheck(insptr, dest) register instr *insptr; register addrmode *dest; { #ifdef CHIPFIX instr *newins; if ( (dest->admode == DSPMD || dest->admode == DSPDFMD || dest->admode == EXADMD || dest->admode == ABSMD || dest->admode == EXADDFMD || dest->admode == ABSDFMD || dest->admode == REGDFMD) && (workaround) ) { newins = (*lookup("NOP",N_INSTALL,MNEMON)).itp; generate(newins->nbits,NOACTION,newins->opcode,NULLSYM); } #endif } /* pcintcheck */ #if 0 /* The following routine is ifdef'ed out because it is not * needed in DBO Mask 2 chips. */ /* * chip fix routine: if one of the following instructions: * MUL[BHW]3, DIV[BHW]3, MOD[BHW]3 * occurs and, during execution, the result from the first two * operands is computed before the third (destination) operand * has been latched and decoded then the CPU will hang. * This problem is fixed with the following mapping: * * MULX3, DIVX3, MODX3 is mapped into * * pushYX arg1 * if arg2 is %sp * subw3 &4, %sp, marg3 * else * movX marg2, marg3 * "offending instr"2 {Z}spoff(%sp), marg3 * SUBW2 &4, %sp * TSTX {Z}arg3 * * where: X = B, H, or W * Y = z (zero extend) for bytes * b (bit extend) for halfs * Z = user specified expand byte * marg[2|3] indicates arg[2|3] has been * modified if it references the stack * pointer. (The first instruction in * the mapping is a push which changes * the stack pointer.) * spoff is -4L if X = W * is -2L if X = H * is -1L if X = B */ trimulcheck(insptr, arg1, arg2, arg3) register instr *insptr; register addrmode *arg1, *arg2, *arg3; { #ifdef CHIPFIX instr *newins, *movins, *tstins; short usrexp1, usrexp2, usrexp3, adjarg3, otype; long spoffset; /* Save user specified expand bytes (if there are any) * so that we can push arg1 on the stack using our own * expand byte */ usrexp1 = arg1->newtype; usrexp2 = arg2->newtype; usrexp3 = arg3->newtype; /* check if USER specified an expand byte in an IS25 instruction */ if (insptr->tag & IS25) { if (( usrexp1 != NOTYPE) || (usrexp2 != NOTYPE) || (usrexp3 != NOTYPE)) yyerror("Expand byte invalid in IS25 instruction"); } arg1->newtype = NOTYPE; /* PUSHYX arg1 * * Generate push instruction. Also look up the correct forms * of the MOV and TST instructions. */ otype = optype(insptr->tag,1); switch (otype) { case 0: newins = (*lookup("pushzb",N_INSTALL,MNEMON)).itp; /* BYTE */ pushopgen(newins, arg1); movins = (*lookup("MOVB",N_INSTALL,MNEMON)).itp; tstins = (*lookup("TSTB",N_INSTALL,MNEMON)).itp; spoffset = -1L; break; case 1: newins = (*lookup("pushbh",N_INSTALL,MNEMON)).itp; /* HALF */ pushopgen(newins, arg1); movins = (*lookup("MOVH",N_INSTALL,MNEMON)).itp; tstins = (*lookup("TSTH",N_INSTALL,MNEMON)).itp; spoffset = -2L; break; case 2: newins = (*lookup("pushw",N_INSTALL,MNEMON)).itp; /* WORD */ generate (newins->nbits, NOACTION, newins->opcode, NULLSYM); addrgen (newins, arg1, NOTYPE, 1); movins = (*lookup("MOVW",N_INSTALL,MNEMON)).itp; tstins = (*lookup("TSTW",N_INSTALL,MNEMON)).itp; spoffset = -4L; break; } /* adjust arg3 if it references the stack pointer */ adjarg3 = 0; if (((arg3->admode == DSPMD) || (arg3->admode == DSPDFMD) || (arg3->admode == REGDFMD)) && (arg3->adreg == SPREG)) { adjarg3 = 1; arg3->adexpr.expval -= 4L; } if ((arg2->admode == REGMD) && (arg2->adreg == SPREG)) { /* SUBW3 &4,%sp,marg3 */ movins = (*lookup("SUBW3",N_INSTALL,MNEMON)).itp; generate(movins->nbits, NOACTION, movins->opcode, NULLSYM); generate(8, NOACTION, 0x4L, NULLSYM); generate(8, NOACTION, (long)((CREGMD<<4)|SPREG), NULLSYM); addrgen(movins, arg3, NOTYPE, 3); } else { /* adjust arg2 if it references the stack pointer */ if (((arg2->admode == DSPMD) || (arg2->admode == DSPDFMD) || (arg3->admode == REGDFMD)) && (arg2->adreg == SPREG)) arg2->adexpr.expval -= 4L; /* MOVX marg2,marg3 */ generate(movins->nbits, NOACTION, movins->opcode, NULLSYM); if (insptr->name[0] == 'u') generate(8, NOACTION, (long)((CEXPAND<<4)|UWORD), NULLSYM); else if (usrexp2 == NOTYPE) arg2->newtype = usrexp1; addrgen(movins, arg2, NOTYPE, 1); addrgen(movins, arg3, NOTYPE, 2); } pcintcheck(movins, arg3); /* <instr>X2 {expand}spoff(%sp),marg3 */ switch ((int)insptr->opcode) { case 0xe4: newins = (*lookup("MODW2",N_INSTALL,MNEMON)).itp; break; case 0xe6: newins = (*lookup("MODH2",N_INSTALL,MNEMON)).itp; break; case 0xe7: newins = (*lookup("MODB2",N_INSTALL,MNEMON)).itp; break; case 0xe8: newins = (*lookup("MULW2",N_INSTALL,MNEMON)).itp; break; case 0xea: newins = (*lookup("MULH2",N_INSTALL,MNEMON)).itp; break; case 0xeb: newins = (*lookup("MULB2",N_INSTALL,MNEMON)).itp; break; case 0xec: newins = (*lookup("DIVW2",N_INSTALL,MNEMON)).itp; break; case 0xee: newins = (*lookup("DIVH2",N_INSTALL,MNEMON)).itp; break; case 0xef: newins = (*lookup("DIVB2",N_INSTALL,MNEMON)).itp; break; } generate(newins->nbits, NOACTION, newins->opcode, NULLSYM); if (insptr->name[0] == 'u') generate(8,NOACTION,(long)((CEXPAND<<4)|UWORD),NULLSYM); else if (usrexp1 != NOTYPE) generate(8,NOACTION,(long)((CEXPAND<<4)|usrexp1),NULLSYM); generate(8, NOACTION, (long)((CBDSPMD<<4)|SPREG), NULLSYM); generate(8, NOACTION, spoffset, NULLSYM); arg3->newtype = usrexp2; addrgen(newins, arg3, NOTYPE, 2); pcintcheck(newins, arg3); /* SUBW2 &4,%sp */ newins = (*lookup("SUBW2",N_INSTALL,MNEMON)).itp; generate(newins->nbits, NOACTION, newins->opcode, NULLSYM); generate(8, NOACTION, 0x4L, NULLSYM); generate(8, NOACTION, (long)((CREGMD<<4)|SPREG), NULLSYM); /* TSTX arg3 * This instruction is generated in order to restore the flag settings * of the mul, div, or mod instruction. * * If arg3 was adjusted because of a stack reference it must now * be readjusted. */ if (adjarg3 != 0) arg3->adexpr.expval += 4L; generate(tstins->nbits, NOACTION, tstins->opcode, NULLSYM); if (insptr->name[0] == 'u') arg3->newtype = UWORD; else if (usrexp3 != NOTYPE) arg3->newtype = usrexp3; else if (usrexp2 != NOTYPE) arg3->newtype = usrexp2; else arg3->newtype = usrexp1; addrgen(tstins, arg3, NOTYPE, 1); #endif } /* trimulcheck */ #endif /* * chip fix routine: if the psw register is used as the source of * an instruction, the flag values may not be set yet from the * previous instruction due to a timing problem. If the instruction * is preceded by a NOP then the bits have time to settle and all * is well. * This chip fix routine will only fix IS25 instructions, BELLMAC-32B * instructions are left untouched. */ pswcheck( insptr, addr) register instr *insptr; register addrmode *addr; { instr *newins; if ( (addr->admode == REGMD) && (addr->adreg == PSWREG) ) { newins = (*lookup("NOP",N_INSTALL,MNEMON)).itp; generate(newins->nbits,NOACTION,newins->opcode,NULLSYM); return(0); /* indicate that NOP was generated */ } else { return(1); /* NOP not needed */ } } #if M32RSTFIX /* * The following routine is a workaround for the * RESTORE chip bug. * * The RESTORE instruction will be mapped into the * following sequence of instructions. * * RESTORE %rX ===> MOVAW -Y(%fp), %sp * POPW %r8 * POPW %rZ * . * . * POPW %rX * POPW %fp * * where: * Y is 4 * (3 - X) (ie. 12-4X ) * Z is Y - 1 * * and if %rW is %fp then the sequence is the following: * RESTORE %fp ===> MOVAW -24(%fp),%sp * POPW %fp */ restorefix(num) register addrmode *num; { register int numregs, rnum; instr *newins; static addrmode dispfp = {NOTYPE, DSPMD,FPREG,{ABS,NULLSYM,1L}}; if (num->admode == REGMD) numregs = 9 - num->adreg; if (num->admode == IMMD) numregs = num->adexpr.expval; newins = (*lookup("MOVAW",N_INSTALL,MNEMON)).itp; generate(newins->nbits,NOACTION,newins->opcode,NULLSYM); dispfp.adexpr.expval = -24 + (4 * numregs); addrgen(newins,&dispfp,NOTYPE,1); generate(8,NOACTION,(long)(CREGMD<<4|SPREG),NULLSYM); /* generate MOVAW (12-4X)(%fp),%sp */ newins = (*lookup("POPW",N_INSTALL,MNEMON)).itp; for (rnum=8; numregs > 0; numregs--,rnum--) { /* generate POPW with the register being 9-numreg */ generate(newins->nbits,NOACTION,newins->opcode,NULLSYM); generate(8,NOACTION,(long)(CREGMD<<4|rnum),NULLSYM); } generate(newins->nbits,NOACTION,newins->opcode,NULLSYM); generate(8,NOACTION,(long)(CREGMD<<4|FPREG),NULLSYM); } #endif /* M32RSTFIX */ flags(flag) char flag; { char errmsg[28]; switch(flag) { default: sprintf(errmsg,"Illegal flag (%c) - ignored",flag); werror(errmsg); break; } /* switch */ } /* flags */ #if MLDVFIX /* * Number: er 1 * * Name: Flag Setting for Multiply/Divide * * Description: There are two situations where the CPU sets the N & V * flags but not the Z flag, even though the result is zero. * The source operands must be of opposite signs, with * an unsigned destination. For multiply one source must be 0. * For divide the absolute value of the numerator must be * non-zero and greater than the absolute value of the denominator. * * Resolution: generate a warning diagnostic * */ mulchk3(insptr,addr1,addr2,addr3) instr * insptr; addrmode * addr1; addrmode * addr2; addrmode * addr3; { if (compare(insptr->name, "MULB3") == EQUAL || compare(insptr->name, "MULH3") == EQUAL || compare(insptr->name, "MULW3") == EQUAL || compare(insptr->name, "DIVB3") == EQUAL || compare(insptr->name, "DIVH3") == EQUAL || compare(insptr->name, "DIVW3") == EQUAL || compare(insptr->name, "MULB2") == EQUAL || compare(insptr->name, "MULH2") == EQUAL || compare(insptr->name, "MULW2") == EQUAL || compare(insptr->name, "DIVB2") == EQUAL || compare(insptr->name, "DIVH2") == EQUAL || compare(insptr->name, "DIVW2") == EQUAL) { int type1, type2, type3; switch (addr1->newtype) { case SBYTE: case SHALF: case SWORD: type1 = SIGNED; break; case UBYTE: case UHALF: case UWORD: type1 = UNSIGNED; break; case NOTYPE: switch(optype(insptr->tag,1)) { case 0: /* byte */ type1 = UNSIGNED; break; case 1: /* half */ type1 = SIGNED; break; case 2: /* word */ type1 = SIGNED; break; } } switch (addr2->newtype) { case SBYTE: case SHALF: case SWORD: type2 = SIGNED; break; case UBYTE: case UHALF: case UWORD: type2 = UNSIGNED; break; case NOTYPE: type2 = type1; } switch (addr3->newtype) { case SBYTE: case SHALF: case SWORD: type3 = SIGNED; break; case UBYTE: case UHALF: case UWORD: type3 = UNSIGNED; break; case NOTYPE: type3 = type2; } if ( (addr1->admode == IMMD) && (addr1->adexpr.expval < 0 ) ) type1 = SIGNED; if ( (addr2->admode == IMMD) && (addr2->adexpr.expval < 0 ) ) type2 = SIGNED; if ( ( ( type1 == SIGNED ) || ( type2 == SIGNED ) ) && ( type3 == UNSIGNED ) ) { werror("Mixed signs with unsignd destination can cause erroneous flags"); } } } /* mulchk3 */ #endif #if ER21FIX er21(insptr,addr1,addr2,addr3) instr * insptr; addrmode * addr1; addrmode * addr2; addrmode * addr3; { if (compare(insptr->name, "MODB3") == EQUAL || compare(insptr->name, "MODH3") == EQUAL || compare(insptr->name, "MODW3") == EQUAL ) { int type1, type2, type3; switch (addr1->newtype) { case SBYTE: case SHALF: case SWORD: type1 = SIGNED; break; case UBYTE: case UHALF: case UWORD: type1 = UNSIGNED; break; case NOTYPE: switch(optype(insptr->tag,1)) { case 0: /* byte */ type1 = UNSIGNED; break; case 1: /* half */ type1 = SIGNED; break; case 2: /* word */ type1 = SIGNED; break; } } switch (addr2->newtype) { case SBYTE: case SHALF: case SWORD: type2 = SIGNED; break; case UBYTE: case UHALF: case UWORD: type2 = UNSIGNED; break; case NOTYPE: type2 = type1; } switch (addr3->newtype) { case SBYTE: case SHALF: case SWORD: type3 = SIGNED; break; case UBYTE: case UHALF: case UWORD: type3 = UNSIGNED; break; case NOTYPE: type3 = type2; } if ( (addr1->admode == IMMD) && (addr1->adexpr.expval < 0 ) ) type1 = SIGNED; if ( (addr2->admode == IMMD) && (addr2->adexpr.expval < 0 ) ) type2 = SIGNED; if ( ( type2 == SIGNED ) && ( type3 == UNSIGNED ) ) { werror ("Mixed signs with unsignd destination can cause erroneous flags"); } } } /* er21 */ #endif #if ER16FIX decrstk(i) int i; { /* decrstk */ instr * tmpins; tmpins = (*lookup("SUBW2",N_INSTALL,MNEMON)).itp; generate(8, NOACTION, tmpins->opcode, NULLSYM); generate(8, NOACTION, i, NULLSYM); generate(8, NOACTION, (long)((CREGMD<<4)|SPREG), NULLSYM); } /* decrstk */ incrstk(i) int i; { /* incrstk */ instr * tmpins; tmpins = (*lookup("ADDW2",N_INSTALL,MNEMON)).itp; generate(8, NOACTION, tmpins->opcode, NULLSYM); generate(8, NOACTION, i, NULLSYM); generate(8, NOACTION, (long)((CREGMD<<4)|SPREG), NULLSYM); } /* incrstk */ copyadd(addr1,addr2) addrmode * addr1; addrmode * addr2; { /* copyadd */ addr2->newtype = addr1->newtype; addr2->admode = addr1->admode; addr2->adreg = addr1->adreg; addr2->adexpr.exptype = addr1->adexpr.exptype; addr2->adexpr.symptr = addr1->adexpr.symptr; addr2->adexpr.expval = addr1->adexpr.expval; addr2->adexpr.fdexpval2 = addr1->adexpr.fdexpval2; addr2->expspec = addr1->expspec; } /* copyadd */ gen2(op,addr1,addr2) char * op; addrmode * addr1; addrmode * addr2; { /* gen2 */ instr * tmpins; tmpins = (*lookup(op,N_INSTALL,MNEMON)).itp; generate (tmpins->nbits, NOACTION, tmpins->opcode, NULLSYM); addrgen(tmpins,addr1,NOTYPE,1); addrgen(tmpins,addr2,NOTYPE,2); } /* gen2 */ er16fix(insptr, arg1, arg2, arg3) instr * insptr; addrmode * arg1; addrmode * arg2; addrmode * arg3; { /* er16fix */ addrmode c_tmp1; addrmode * c_arg1 = &c_tmp1; addrmode c_tmp2; addrmode * c_arg2 = &c_tmp2; addrmode c_tmp3; addrmode * c_arg3 = &c_tmp3; instr * tmpins; if ( ! ( (compare(insptr->name, "MULH3") == EQUAL ) || (compare(insptr->name, "MULB3") == EQUAL ) || (compare(insptr->name, "MULW3") == EQUAL ) || (compare(insptr->name, "DIVH3") == EQUAL ) || (compare(insptr->name, "DIVB3") == EQUAL ) || (compare(insptr->name, "DIVW3") == EQUAL ) || (compare(insptr->name, "MODH3") == EQUAL ) || (compare(insptr->name, "MODB3") == EQUAL ) || (compare(insptr->name, "MODW3") == EQUAL ) || (compare(insptr->name, "mulw3") == EQUAL ) || (compare(insptr->name, "umulw3") == EQUAL ) || (compare(insptr->name, "divw3") == EQUAL ) || (compare(insptr->name, "udivw3") == EQUAL ) || (compare(insptr->name, "modw3") == EQUAL ) || (compare(insptr->name, "umodw3") == EQUAL ) ) ) return(0); if( ! ( (er16chk(arg1,arg3)) || (er16chk(arg2,arg3)) ) ) return(0); if( ! workaround ) return(0); if ( compare(insptr->name, "mulw3") == EQUAL) insptr = (*lookup("MULW3",N_INSTALL,MNEMON)).itp; if (compare(insptr->name, "umulw3") == EQUAL ) { insptr = (*lookup("MULW3",N_INSTALL,MNEMON)).itp; arg1->newtype = UWORD; } if ( compare(insptr->name, "divw3") == EQUAL) insptr = (*lookup("DIVW3",N_INSTALL,MNEMON)).itp; if (compare(insptr->name, "udivw3") == EQUAL ) { insptr = (*lookup("DIVW3",N_INSTALL,MNEMON)).itp; arg1->newtype = UWORD; } if ( compare(insptr->name, "modw3") == EQUAL) insptr = (*lookup("MODW3",N_INSTALL,MNEMON)).itp; if (compare(insptr->name, "umodw3") == EQUAL ) { insptr = (*lookup("MODW3",N_INSTALL,MNEMON)).itp; arg1->newtype = UWORD; } incrstk(12); if (arg1->newtype == NOTYPE) switch(optype(insptr->tag,1)) { case 0: /* byte */ arg1->newtype = UBYTE; break; case 1: /* half */ arg1->newtype = SHALF; break; case 2: /* word */ arg1->newtype = SWORD; break; } if (arg2->newtype == NOTYPE) arg2->newtype = arg1->newtype; if (arg3->newtype == NOTYPE) arg3->newtype = arg2->newtype; copyadd(arg1,c_arg1); copyadd(arg2,c_arg2); copyadd(arg3,c_arg3); if ( ( (c_arg1->admode == DSPMD ) || (c_arg1->admode == DSPDFMD) || (c_arg1->admode == REGDFMD) ) && ( c_arg1->adreg == SPREG ) ) c_arg1->adexpr.expval -= 12; if ( (c_arg1->admode == REGMD) && (c_arg1->adreg == SPREG) ) { /* SUBW3 &12,%sp,c_arg1 */ c_arg1->admode = DSPMD; c_arg1->adexpr.exptype = ABS; c_arg1->adexpr.symptr = NULLSYM; c_arg1->adexpr.expval = -12; tmpins = (*lookup("SUBW3",N_INSTALL,MNEMON)).itp; generate(tmpins->nbits, NOACTION, tmpins->opcode, NULLSYM); generate(8, NOACTION, 0x12L, NULLSYM); generate(8, NOACTION, (long)((CREGMD<<4)|SPREG), NULLSYM); addrgen(tmpins, c_arg1, NOTYPE, 3); } if ( ( (c_arg2->admode == DSPMD ) || (c_arg2->admode == DSPDFMD) || (c_arg2->admode == REGDFMD) ) && ( c_arg2->adreg == SPREG ) ) c_arg2->adexpr.expval -= 12; if ( (c_arg2->admode == REGMD) && (c_arg2->adreg == SPREG) ) { /* SUBW3 &12,%sp,c_arg2 */ c_arg2->admode = DSPMD; c_arg2->adexpr.exptype = ABS; c_arg2->adexpr.symptr = NULLSYM; c_arg2->adexpr.expval = -8; tmpins = (*lookup("SUBW3",N_INSTALL,MNEMON)).itp; generate(tmpins->nbits, NOACTION, tmpins->opcode, NULLSYM); generate(8, NOACTION, 0x12L, NULLSYM); generate(8, NOACTION, (long)((CREGMD<<4)|SPREG), NULLSYM); addrgen(tmpins, c_arg2, NOTYPE, 3); } c_arg3->admode = DSPMD; c_arg3->adreg = SPREG; c_arg3->adexpr.exptype = ABS; c_arg3->adexpr.symptr = NULLSYM; c_arg3->adexpr.expval = -4; generate(8,NOACTION,insptr->opcode,NULLSYM); addrgen(insptr,c_arg1,NOTYPE,1); addrgen(insptr,c_arg2,NOTYPE,2); addrgen(insptr,c_arg3,NOTYPE,3); if ( ( (arg3->admode == DSPMD ) || (arg3->admode == DSPDFMD) || (arg3->admode == REGDFMD) ) && ( arg3->adreg == SPREG ) ) arg3->adexpr.expval -= 12; gen2("MOVW",c_arg3,arg3); decrstk(12); c_arg3->adexpr.expval = 0; generate(8,NOACTION,insptr->opcode,NULLSYM); addrgen(insptr,arg1,NOTYPE,1); addrgen(insptr,arg2,NOTYPE,2); addrgen(insptr,c_arg3,NOTYPE,3); return(1); } /* er16fix */ er16chk(addr1,addr2) addrmode *addr1; addrmode *addr2; { /* er16chk */ switch(addr2->admode) { /* switch(addr2->admode) */ case REGDFMD: /* register deferred mode */ switch(addr1->admode) { case DSPMD: /* displacement mode */ if ( ( addr1->adreg == addr2->adreg ) && ( abs(addr1->adexpr.expval - 0) >= 4 ) ) return(NO); break; case REGMD: /* register mode */ return(NO); } break; case DSPMD: /* displacement mode */ switch(addr1->admode) { case DSPMD: /* displacement mode */ if ( ( addr1->adreg == addr2->adreg ) && ( abs(addr1->adexpr.expval - addr2->adexpr.expval) >=4 ) ) return(NO); break; case REGMD: /* register mode */ return(NO); } break; case DSPDFMD: /* displacement deferred mode */ switch(addr1->admode) { case REGMD: /* register mode */ return(NO); } break; case EXADMD: /* external address mode */ switch(addr1->admode) { case REGMD: /* register mode */ return(NO); } break; case ABSMD: /* absolute address mode */ switch(addr1->admode) { case ABSMD: /* absolute address mode */ if ( abs(addr1->adexpr.expval - addr2->adexpr.expval) >= 4 ) return(NO); break; case REGMD: /* register mode */ return(NO); } break; case EXADDFMD: /* external address deferred mode (PC relative deferred) */ switch(addr1->admode) { case REGMD: /* register mode */ return(NO); } break; case ABSDFMD: /* absolute address deferred mode */ switch(addr1->admode) { case REGMD: /* register mode */ return(NO); } break; case IMMD: /* immediate mode */ return(NO); case REGMD: /* register mode */ return(NO); } /* switch(addr2->admode) */ return(YES); } /* er16chk */ #endif