599 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			599 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 | |
|  * See the copyright notice in the ACK home directory, in the file "Copyright".
 | |
|  */
 | |
| #define RCSID5 "$Id$"
 | |
| 
 | |
| /*
 | |
|  * INTEL 80386 special routines
 | |
|  */
 | |
| 
 | |
| ea_1_16(param)
 | |
| {
 | |
| 	reg_1 &= 0377;
 | |
|         if ((reg_1 & 070) || (param & ~070)) {
 | |
|                 serror("bad operand");
 | |
|         }
 | |
|         emit1(reg_1 | param);
 | |
|         switch(reg_1 >> 6) {
 | |
|         case 0:
 | |
|                 if (reg_1 == 6 || (reg_1 & 040)) {
 | |
| #ifdef RELOCATION
 | |
|                         RELOMOVE(relonami, rel_1);
 | |
|                         newrelo(exp_1.typ, RELO2);
 | |
| #endif
 | |
|                         emit2(exp_1.val);
 | |
|                 }
 | |
|                 break;
 | |
|         case 1:
 | |
|                 emit1(exp_1.val);
 | |
|                 break;
 | |
|         case 2:
 | |
| #ifdef RELOCATION
 | |
|                 RELOMOVE(relonami, rel_1);
 | |
|                 newrelo(exp_1.typ, RELO2);
 | |
| #endif
 | |
|                 emit2(exp_1.val);
 | |
|                 break;
 | |
|         }
 | |
| }
 | |
| 
 | |
| ea_1(param) {
 | |
| 	if (! address_long) {
 | |
| 		ea_1_16(param);
 | |
| 		return;
 | |
| 	}
 | |
| 	if (is_expr(reg_1)) {
 | |
| 		serror("bad operand");
 | |
| 		return;
 | |
| 	}
 | |
| 	if (is_reg(reg_1)) {
 | |
| 		emit1(0300 | param | (reg_1&07));
 | |
| 		return;
 | |
| 	}
 | |
| 	if (rm_1 == 04) {
 | |
| 		/* sib field use here */
 | |
| 		emit1(mod_1 << 6 | param | 04);
 | |
| 		emit1(sib_1 | reg_1);
 | |
| 	}
 | |
| 	else emit1(mod_1<<6 | param | (reg_1&07));
 | |
| 	if ((mod_1 == 0 && reg_1 == 5) || mod_1 == 2) {
 | |
| 		/* ??? should this be protected by a call to "small" ??? */
 | |
| #ifdef RELOCATION
 | |
| 		RELOMOVE(relonami, rel_1);
 | |
| 		newrelo(exp_1.typ, RELO4);
 | |
| #endif
 | |
| 		emit4((long)(exp_1.val));
 | |
| 	}
 | |
| 	else if (mod_1 == 1) {
 | |
| 		emit1((int)(exp_1.val));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| ea_2(param) {
 | |
| 
 | |
| 	op_1 = op_2;
 | |
| 	RELOMOVE(rel_1, rel_2);
 | |
| 	ea_1(param);
 | |
| }
 | |
| 
 | |
| int
 | |
| checkscale(val)
 | |
| 	valu_t val;
 | |
| {
 | |
| 	int v = val;
 | |
| 
 | |
| 	if (! address_long) {
 | |
| 		serror("scaling not allowed in 16-bit mode");
 | |
| 		return 0;
 | |
| 	}
 | |
| 	if (v != val) v = 0;
 | |
| 	switch(v) {
 | |
| 	case 1:
 | |
| 		return 0;
 | |
| 	case 2:
 | |
| 		return 1 << 6;
 | |
| 	case 4:
 | |
| 		return 2 << 6;
 | |
| 	case 8:
 | |
| 		return 3 << 6;
 | |
| 	default:
 | |
| 		serror("bad scale");
 | |
| 		return 0;
 | |
| 	}
 | |
| 	/*NOTREACHED*/
 | |
| }
 | |
| 
 | |
| reverse() {
 | |
| 	struct operand op;
 | |
| #ifdef RELOCATION
 | |
| 	int r = rel_1;
 | |
| 
 | |
| 	rel_1 = rel_2; rel_2 = r;
 | |
| #endif
 | |
| 	op = op_1; op_1 = op_2; op_2 = op;
 | |
| }
 | |
| 
 | |
| badsyntax() {
 | |
| 
 | |
| 	serror("bad operands");
 | |
| }
 | |
| 
 | |
| regsize(sz)
 | |
| 	int sz;
 | |
| {
 | |
| 	register int bit;
 | |
| 
 | |
| 	bit = (sz&1) ? 0 : IS_R8;
 | |
| 	if ((is_reg(reg_1) && (reg_1 & IS_R8) != bit) ||
 | |
| 	    (is_reg(reg_2) && (reg_2 & IS_R8) != bit)) 
 | |
| 		serror("register error");
 | |
| 	if (! address_long) {
 | |
| 		reg_1 &= ~010;
 | |
| 		reg_2 &= ~010;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| indexed() {
 | |
| 	if (address_long) {
 | |
| 		mod_2 = 0;
 | |
| 		if (sib_2 == -1)
 | |
| 			serror("register error");
 | |
| 		if (rm_2 == 0 && reg_2 == 4) {
 | |
| 			/* base register sp, no index register; use
 | |
| 		   	   indexed mode without index register
 | |
| 			*/
 | |
| 			rm_2 = 04;
 | |
| 			sib_2 = 044;
 | |
| 		}
 | |
| 		if (reg_2 == 015) {
 | |
| 			reg_2 = 05;
 | |
| 			return;
 | |
| 		}
 | |
| 		if (small(exp_2.typ == S_ABS && fitb(exp_2.val), 3)) {
 | |
| 			if (small(exp_2.val == 0 && reg_2 != 5, 1)) {
 | |
| 			}
 | |
| 			else mod_2 = 01;
 | |
| 		}
 | |
| 		else	if (small(0, 1)) {
 | |
| 		}
 | |
| 		else mod_2 = 02;
 | |
| 	}
 | |
| 	else {
 | |
|         	if (reg_2 & ~7)
 | |
|                 	serror("register error");
 | |
|         	if (small(exp_2.typ == S_ABS && fitb(exp_2.val), 1)) {
 | |
| 			if (small(exp_2.val == 0 && reg_2 != 6, 1)) {
 | |
| 			}
 | |
| 			else reg_2 |= 0100;
 | |
| 		}
 | |
| 		else if (small(0, 1)) {
 | |
| 		}
 | |
| 		else reg_2 |= 0200;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| ebranch(opc,exp)
 | |
| 	register int opc;
 | |
| 	expr_t exp;
 | |
| {
 | |
| 	/*	Conditional branching; Full displacements are available
 | |
| 		on the 80386, so the welknown trick with the reverse branch
 | |
| 		over a jump is not needed here.
 | |
| 		The only complication here is with the address size, which
 | |
| 		can be set with a prefix. In this case, the user gets what
 | |
| 		he asked for.
 | |
| 	*/
 | |
| 	register int sm;
 | |
| 	register long dist;
 | |
| 	int saving = address_long ? 4 : 2;
 | |
| 
 | |
| 	if (opc == 0353) saving--;
 | |
| 	dist = exp.val - (DOTVAL + 2);
 | |
| 	if (pass == PASS_2 && dist > 0 && !(exp.typ & S_DOT))
 | |
| 		dist -= DOTGAIN;
 | |
| 	sm = dist > 0 ? fitb(dist-saving) : fitb(dist);
 | |
| 	if ((exp.typ & ~S_DOT) != DOTTYP)
 | |
| 		sm = 0;
 | |
| 	if ((sm = small(sm,saving)) == 0) {
 | |
| 		if (opc == 0353) {
 | |
| 			emit1(0xe9);
 | |
| 		}
 | |
| 		else {
 | |
| 			emit1(0xF);
 | |
| 			emit1(opc | 0x80);
 | |
| 		}
 | |
| 		dist -= saving;
 | |
| 		exp.val = dist;
 | |
| 		adsize_exp(exp, RELPC);
 | |
| 	}
 | |
| 	else {
 | |
| 		if (opc == 0353) {
 | |
| 			emit1(opc);
 | |
| 		}
 | |
| 		else {
 | |
| 			emit1(opc | 0x70);
 | |
| 		}
 | |
| 		emit1((int)dist);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| branch(opc,exp)
 | |
| 	register int opc;
 | |
| 	expr_t exp;
 | |
| {
 | |
| 	/*	LOOP, JCXZ, etc. branch instructions.
 | |
| 		Here, the offset just must fit in a byte.
 | |
| 	*/
 | |
| 	register long dist;
 | |
| 
 | |
| 	dist = exp.val - (DOTVAL + 2);
 | |
| 	if (pass == PASS_2 && dist > 0 && !(exp.typ & S_DOT))
 | |
| 		dist -= DOTGAIN;
 | |
| 	fit((exp.typ & ~S_DOT) == DOTTYP && fitb(dist));
 | |
| 	emit1(opc);
 | |
| 	emit1((int)dist);
 | |
| }
 | |
| 
 | |
| pushop(opc)
 | |
| 	register int opc;
 | |
| {
 | |
| 
 | |
| 	regsize(1);
 | |
| 	if (is_segreg(reg_1)) {
 | |
| 		/* segment register */
 | |
| 		if ((reg_1 & 07) <= 3)
 | |
| 			emit1(6 | opc | (reg_1&7)<<3);
 | |
| 		else {
 | |
| 			emit1(0xF);
 | |
| 			emit1(0200 | opc | ((reg_1&7)<<3));
 | |
| 		}
 | |
| 	} else if (is_reg(reg_1)) {
 | |
| 		/* normal register */
 | |
| 		emit1(0120 | opc<<3 | (reg_1&7));
 | |
| 	} else if (opc == 0) {
 | |
| 		if (is_expr(reg_1)) {
 | |
| 			if (small(exp_1.typ == S_ABS && fitb(exp_1.val),
 | |
| 				  operand_long ? 3 : 1)) {
 | |
| 				emit1(0152);
 | |
| 				emit1((int)(exp_1.val));
 | |
| 			}
 | |
| 			else {
 | |
| 				emit1(0150);
 | |
| 				RELOMOVE(relonami, rel_1);
 | |
| 				opsize_exp(exp_1, 1);
 | |
| 			}
 | |
| 		}
 | |
| 		else {
 | |
| 			emit1(0377); ea_1(6<<3);
 | |
| 		}
 | |
| 	} else {
 | |
| 		emit1(0217); ea_1(0<<3);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| opsize_exp(exp, nobyte)
 | |
| 	expr_t exp;
 | |
| {
 | |
| 	if (! nobyte) {
 | |
| #ifdef RELOCATION
 | |
| 		newrelo(exp.typ, RELO1);
 | |
| #endif
 | |
| 		emit1((int)(exp.val));
 | |
| 	}
 | |
| 	else if (operand_long) {
 | |
| #ifdef RELOCATION
 | |
| 		newrelo(exp.typ, RELO4);
 | |
| #endif
 | |
| 		emit4((long)(exp.val));
 | |
| 	}
 | |
| 	else {
 | |
| #ifdef RELOCATION
 | |
| 		newrelo(exp.typ, RELO2);
 | |
| #endif
 | |
| 		emit2((int)(exp.val));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| adsize_exp(exp, relpc)
 | |
| 	expr_t exp;
 | |
| {
 | |
| 	if (address_long) {
 | |
| #ifdef RELOCATION
 | |
| 		newrelo(exp.typ, RELO4 | relpc);
 | |
| #endif
 | |
| 		emit4((long)(exp.val));
 | |
| 	}
 | |
| 	else {
 | |
| #ifdef RELOCATION
 | |
| 		newrelo(exp.typ, RELO2 | relpc);
 | |
| #endif
 | |
| 		emit2((int)(exp.val));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| addop(opc) 
 | |
| 	register int opc;
 | |
| {
 | |
| 
 | |
| 	regsize(opc);
 | |
| 	if (is_reg(reg_2)) {
 | |
| 		/*	Add register to register or memory */
 | |
| 		emit1(opc); ea_1((reg_2&7)<<3);
 | |
| 	} else if (is_acc(reg_1) && is_expr(reg_2)) {
 | |
| 		/*	Add immediate to accumulator */
 | |
| 		emit1(opc | 4);
 | |
| 		RELOMOVE(relonami, rel_2);
 | |
| 		opsize_exp(exp_2, (opc&1));
 | |
| 	} else if (is_expr(reg_2)) {
 | |
| 		/*	Add immediate to register or memory */
 | |
| 		if ((opc&1) == 0) {
 | |
| 			emit1(0200);
 | |
| 		} else if (! small(exp_2.typ == S_ABS && fitb(exp_2.val),
 | |
| 			   	   operand_long ? 3 : 1)) {
 | |
| 			emit1(0201);
 | |
| 		} else {
 | |
| 			emit1(0203); opc &= ~1;
 | |
| 		}
 | |
| 		ea_1(opc & 070);
 | |
| 		RELOMOVE(relonami, rel_2);
 | |
| 		opsize_exp(exp_2, (opc&1));
 | |
| 	} else if (is_reg(reg_1)) {
 | |
| 		/*	Add register or memory to register */
 | |
| 		emit1(opc | 2);
 | |
| 		ea_2((reg_1&7)<<3);
 | |
| 	} else
 | |
| 		badsyntax();
 | |
| }
 | |
| 
 | |
| rolop(opc)
 | |
| 	register int opc;
 | |
| {
 | |
| 	register int oreg;
 | |
| 
 | |
| 	oreg = reg_2;
 | |
| 	reg_2 = reg_1;
 | |
| 	regsize(opc);
 | |
| 	if (oreg == (IS_R8 | 1 | (address_long ? 0 : 0300))) {
 | |
| 		/* cl register */
 | |
| 		emit1(0322 | (opc&1)); ea_1(opc&070);
 | |
| 	} else if (is_expr(oreg)) {
 | |
| 	    if (small(exp_2.typ == S_ABS && exp_2.val == 1, 1)) {
 | |
| 		/* shift by 1 */
 | |
| 		emit1(0320 | (opc&1)); ea_1(opc&070);
 | |
| 	    } else {
 | |
| 		/* shift by byte count */
 | |
| 		emit1(0300 | (opc & 1)); 
 | |
| 		ea_1(opc & 070);
 | |
| #ifdef RELOCATION
 | |
| 		RELOMOVE(relonami, rel_2);
 | |
| 		newrelo(exp_2.typ, RELO1);
 | |
| #endif
 | |
| 		emit1((int)(exp_2.val));
 | |
| 	    }
 | |
| 	}
 | |
| 	else
 | |
| 		badsyntax();
 | |
| }
 | |
| 
 | |
| incop(opc)
 | |
| 	register int opc;
 | |
| {
 | |
| 
 | |
| 	regsize(opc);
 | |
| 	if ((opc&1) && is_reg(reg_1)) {
 | |
| 		/* word register */
 | |
| 		emit1(0100 | (opc&010) | (reg_1&7));
 | |
| 	} else {
 | |
| 		emit1(0376 | (opc&1));
 | |
| 		ea_1(opc & 010);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| callop(opc) 
 | |
| 	register int opc;
 | |
| {
 | |
| 
 | |
| 	regsize(1);
 | |
| 	if (is_expr(reg_1)) {
 | |
| 		if (opc == (040+(0351<<8))) {
 | |
| 			RELOMOVE(relonami, rel_1);
 | |
| 			ebranch(0353,exp_1);
 | |
| 		} else {
 | |
| 			exp_1.val -= (DOTVAL+3 + (address_long ? 2 : 0));
 | |
| 			emit1(opc>>8);
 | |
| 			RELOMOVE(relonami, rel_1);
 | |
| 			adsize_exp(exp_1, RELPC);
 | |
| 		}
 | |
| 	} else {
 | |
| 		emit1(0377); ea_1(opc&070);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| xchg(opc)
 | |
| 	register int opc;
 | |
| {
 | |
| 
 | |
| 	regsize(opc);
 | |
| 	if (! is_reg(reg_1) || is_acc(reg_2)) {
 | |
| 		reverse();
 | |
| 	}
 | |
| 	if (opc == 1 && is_acc(reg_1) && is_reg(reg_2)) {
 | |
| 		emit1(0220 | (reg_2&7));
 | |
| 	} else if (is_reg(reg_1)) {
 | |
| 		emit1(0206 | opc); ea_2((reg_1&7)<<3);
 | |
| 	} else
 | |
| 		badsyntax();
 | |
| }
 | |
| 
 | |
| test(opc)
 | |
| 	register int opc;
 | |
| {
 | |
| 
 | |
| 	regsize(opc);
 | |
| 	if (is_reg(reg_2) || is_expr(reg_1))
 | |
| 		reverse();
 | |
| 	if (is_expr(reg_2)) {
 | |
| 		if (is_acc(reg_1)) {
 | |
| 			emit1(0250 | opc);
 | |
| 			RELOMOVE(relonami, rel_2);
 | |
| 			opsize_exp(exp_2, (opc&1));
 | |
| 		}
 | |
| 		else {
 | |
| 			emit1(0366 | opc);
 | |
| 			ea_1(0<<3);
 | |
| 			RELOMOVE(relonami, rel_2);
 | |
| 			opsize_exp(exp_2, (opc&1));
 | |
| 		}
 | |
| 	} else if (is_reg(reg_1)) {
 | |
| 		emit1(0204 | opc); ea_2((reg_1&7)<<3);
 | |
| 	} else
 | |
| 		badsyntax();
 | |
| }
 | |
| 
 | |
| mov(opc)
 | |
| 	register int opc;
 | |
| {
 | |
| 
 | |
| 	regsize(opc);
 | |
| 	if (is_segreg(reg_1)) {
 | |
| 		/* to segment register */
 | |
| 		emit1(0216); ea_2((reg_1&07)<<3);
 | |
| 	} else if (is_segreg(reg_2)) {
 | |
| 		/* from segment register */
 | |
| 		emit1(0214); ea_1((reg_2&07)<<3);
 | |
| 	} else if (is_expr(reg_2)) {
 | |
| 		/* from immediate */
 | |
| 		if (is_reg(reg_1)) {
 | |
| 			/* to register */
 | |
| 			emit1(0260 | opc<<3 | (reg_1&7)); 
 | |
| 		} else {
 | |
| 			/* to memory */
 | |
| 			emit1(0306 | opc); ea_1(0<<3); 
 | |
| 		}
 | |
| 		RELOMOVE(relonami, rel_2);
 | |
| 		opsize_exp(exp_2, (opc&1));
 | |
| 	} else if (rm_1 == 05 && is_acc(reg_2)) {
 | |
| 		/* from accumulator to memory  (displacement) */
 | |
| 		emit1(0242 | opc);
 | |
| 		RELOMOVE(relonami, rel_1);
 | |
| 		adsize_exp(exp_1, 0);
 | |
| 	} else if (rm_2 == 05 && is_acc(reg_1)) {
 | |
| 		/* from memory (displacement) to accumulator */
 | |
| 		emit1(0240 | opc);
 | |
| 		RELOMOVE(relonami, rel_2);
 | |
| 		adsize_exp(exp_2, 0);
 | |
| 	} else if (is_reg(reg_2)) {
 | |
| 		/* from register to memory or register */
 | |
| 		emit1(0210 | opc); ea_1((reg_2&07)<<3);
 | |
| 	} else if (is_reg(reg_1)) {
 | |
| 		/* from memory or register to register */
 | |
| 		emit1(0212 | opc); ea_2((reg_1&07)<<3);
 | |
| 	} else
 | |
| 		badsyntax();
 | |
| }
 | |
| 
 | |
| extshft(opc, reg)
 | |
| 	int opc;
 | |
| {
 | |
| 	int oreg2 = reg_2;
 | |
| 
 | |
| 	reg_2 = reg_1;
 | |
| 	regsize(1);
 | |
| 
 | |
| 	emit1(0xF);
 | |
| 	if (oreg2 == (IS_R8 | 1 | (address_long ? 0 : 0300))) {
 | |
| 		/* cl register */
 | |
| 		emit1(opc|1);
 | |
| 		ea_1(reg << 3);
 | |
| 	}
 | |
| 	else if (is_expr(oreg2)) {
 | |
| 		emit1(opc);
 | |
| 		ea_1(reg << 3);
 | |
| #ifdef RELOCATION
 | |
| 		RELOMOVE(relonami, rel_2);
 | |
| 		newrelo(exp_2.typ, RELO1);
 | |
| #endif
 | |
| 		emit1((int)(exp_2.val));
 | |
| 	}
 | |
| 	else	badsyntax();
 | |
| }
 | |
| 
 | |
| bittestop(opc)
 | |
| 	int opc;
 | |
| {
 | |
| 	regsize(1);
 | |
| 	emit1(0xF);
 | |
| 	if (is_expr(reg_2)) {
 | |
| 		emit1(0272);
 | |
| 		ea_1(opc << 3);
 | |
| #ifdef RELOCATION
 | |
| 		RELOMOVE(relonami, rel_2);
 | |
| 		newrelo(exp_2.typ, RELO1);
 | |
| #endif
 | |
| 		emit1((int)(exp_2.val));
 | |
| 	}
 | |
| 	else if (is_reg(reg_2)) {
 | |
| 		emit1(0203 | (opc<<3));
 | |
| 		ea_1((reg_2&7)<<3);
 | |
| 	}
 | |
| 	else	badsyntax();
 | |
| }
 | |
| 
 | |
| imul(reg)
 | |
| 	int reg;
 | |
| {
 | |
| 	/*	This instruction is more elaborate on the 80386. Its most
 | |
| 		general form is:
 | |
| 			imul reg, reg_or_mem, immediate.
 | |
| 		This is the form processed here.
 | |
| 	*/
 | |
| 	regsize(1);
 | |
| 	if (is_expr(reg_1)) {
 | |
| 		/* To also handle
 | |
| 			imul reg, immediate, reg_or_mem
 | |
| 		*/
 | |
| 		reverse();
 | |
| 	}
 | |
| 	if (is_expr(reg_2)) {
 | |
| 		/* The immediate form; two cases: */
 | |
| 		if (small(exp_2.typ == S_ABS && fitb(exp_2.val),
 | |
| 			  operand_long ? 3 : 1)) {
 | |
| 			/* case 1: 1 byte encoding of immediate */
 | |
| 			emit1(0153);
 | |
| 			ea_1((reg & 07) << 3);
 | |
| 			emit1((int)(exp_2.val));
 | |
| 		}
 | |
| 		else {
 | |
| 			/* case 2: WORD or DWORD encoding of immediate */
 | |
| 			emit1(0151);
 | |
| 			ea_1((reg & 07) << 3);
 | |
| 			RELOMOVE(relonami, rel_2);
 | |
| 			opsize_exp(exp_2, 1);
 | |
| 		}
 | |
| 	}
 | |
| 	else if (is_reg(reg_1) && ((reg_1&7) == (reg & 07))) {
 | |
| 		/* the "reg" field and the "reg_or_mem" field are the same,
 | |
| 		   and the 3rd operand is not an immediate ...
 | |
| 		*/
 | |
| 		if (reg == 0) {
 | |
| 			/* how lucky we are, the target is the ax register */
 | |
| 			/* However, we cannot make an optimization for f.i.
 | |
| 				imul eax, blablabla
 | |
| 			   because the latter does not affect edx, whereas
 | |
| 				imul blablabla
 | |
| 			   does! Therefore, "reg" is or-ed with 0x10 in the
 | |
| 			   former case, so that the test above fails.
 | |
| 			*/
 | |
| 			emit1(0367);
 | |
| 			ea_2(050);
 | |
| 		}
 | |
| 		else {
 | |
| 			/* another register ... */
 | |
| 			emit1(0xF);
 | |
| 			emit1(0257);
 | |
| 			ea_2((reg & 07) << 3);
 | |
| 		}
 | |
| 	}
 | |
| 	else badsyntax();
 | |
| }
 |