File:  [MW Coherent from dump] / coherent / d / bin / as / i8086 / machine.c,v
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:39 2019 UTC (7 years, 2 months ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

head     1.2;
access   ;
symbols  ;
locks    bin:1.2;
comment  @ * @;


1.2
date     91.02.06.20.44.23;  author bin;  state Exp;
branches ;
next	1.1;

1.1
date     91.02.06.20.44.12;  author bin;  state Exp;
branches ;
next	;


desc
@steve 2/6/91
Initial MWC RCS revision.
@


1.2
log
@steve 2/6/91
Extensive changes from cef: descriptive error messages, bug fixes,
who knows what else.
@
text
@/*
 * Instruction formatting
 * and decoding. This is for the Intel
 * iAPX-86 microprocessor.
 * Small addressing model (perhaps with
 * separated CS and SS,DS,ES).
 * No formatting for the large model calls,
 * returns or jumps.
 */
#include "asm.h"

/*
 * Branch map.
 */
int	*bp;
int	bm;
int	bb[NB];

/*
 * Read and format machine instructions.
 * The argument `sp' is a pointer to the symbol
 * table entry of the opcode. The `s_kind' field
 * holds the operation kind; this determines the
 * format and the semantics of the operation.
 */
machine(sp)
struct sym *sp;
{
	register op, m1, m2;
	struct expr e1, e2;
	int disp, flag, ob, rf, si, rn;

	op = sp->s_addr;
	switch (sp->s_kind) {

	case S_EVEN:
		if ((dot->s_addr&01) != 0) {
			if (inbss == 0)
				outab(0);
			else
				++dot->s_addr;
		}
		lmode = ALIST;
		break;

	case S_ODD:
		if ((dot->s_addr&01) == 0) {
			if (inbss == 0)
				outab(0);
			else
				++dot->s_addr;
		}
		lmode = ALIST;
		break;

	case S_INH:
		outab(op);
		if (op==AAM || op==AAD)		/* Need suffix byte */
			outab(APB);
		break;

	case S_INT:
		expr(&e1, 0);
		if (isabsn(&e1, 3)) {
			outab(INT3);
			break;
		}
		outab(INT);
		outrb(&e1, 0);
		break;

	case S_SYS:
		expr(&e1, 0);
		if (e1.e_type == E_ACON) {
			outab(INT);
			e1.e_addr += 128;
			outrb(&e1, 0);
			break;
		}
		aerr("expected constant");
		break;

	case S_OVER:
		if (addr(&e1) == SEGR) {
			out3(SEGPFX, rof(e1));
			break;
		}
		aerr("segment override by non-segment register");
		break;

	case S_RET:
		if (more() == 0) {
			outab(op);
			break;
		}
		expr(&e1, 0);
		outab(op-1);		/* Use ret n form */
		outrw(&e1, 0);
		break;

	case S_PUSH:
		m1 = addr(&e1);
		rn = rof(e1);
		if (m1 == SEGR) {
			if (op == PUSH)
				out3(PUSHSR, rn);
			else {
				if (e1.e_mode == CS)	/* No pop CS */
					aerr("no 'pop CS' instruction");
				out3(POPSR, rn);
			}
			break;
		}
		if (m1 == WR) {
			if (op == PUSH)
				outab(PUSHR | rn);
			else
				outab(POPR  | rn);
			break;
		}
		if ( m1 == IM ) {
			if ( (e1.e_type == E_ACON)
			  && (  ((e1.e_addr & 0xFF80) == 0xFF80)
			     || ((e1.e_addr & 0xFF80) == 0x0000) ) ) {
				outab( PUSHIB );
				outab( e1.e_addr );
			}
			else {
				outab( PUSHIW );
				outrw(&e1, 0);
			}
			break;
		}
		outgen(op, ((op==PUSH) ? 6 : 0), &e1);
		break;

	case S_IN:
		m1 = addr(&e1);
		comma();
		m2 = addr(&e2);
	IN_OUT:
		if (m1 == WR && rof(e1) == AX)
			op |= W;
		else if (m1 == BR && rof(e1) == reg(AL))
			op &= ~W;
		else {
			aerr("(in|out)(b|) must use AX or AL");
			break;
		}
		if (m2 == WR && rof(e2) == DX) {
			outab(op|010);
			break;
		} else if (m2 == DIR) {
			outab(op);
			outrb(&e2, 0);
			break;
		}
		aerr("(in|out)(b|) must use DX or constant");
		break;

	case S_OUT:
		m2 = addr(&e2);
		comma();
		m1 = addr(&e1);
		goto IN_OUT;

	case S_SHL:
	case S_SHLB:
		m1 = addr(&e1);
		comma();
		m2 = addr(&e2);
		ob = SHL;
		if (sp->s_kind == S_SHL)
			ob |= W;
		bytecheck(ob, m1, NONE);
		if (m2 == BR && e2.e_addr == CL)
			ob |= V;
		else if ( (m2 == IM) && (e2.e_type == E_ACON) ) {
			if ( e2.e_addr != 1 )
				ob = (ob & W) ? (SHLI|W) : SHLI;
		}
		else
			aerr("Improper shift amount");
		outgen(ob, op, &e1);
		if ( (ob & ~W) == SHLI )
			outab( e2.e_addr );
		break;

	case S_JMP:
		if (addr(&e1) != DIR)
			aerr("jmp must be direct address");
		if (pass == 0) {
			dot->s_addr += 3;
			if (op != JMP)
				dot->s_addr += 2;
		} else if (pass == 1) {
			if (e1.e_type != E_DIR
			||  e1.e_base.e_lp != dot->s_base.s_lp) {
				/* long */
				dot->s_addr += 3;
				if (op != JMP)
					dot->s_addr += 2;
				flag = 1;
			} else {
				if (e1.e_addr >= dot->s_addr)
					e1.e_addr -= fuzz;
				dot->s_addr += 2;
				disp = e1.e_addr - dot->s_addr;
				flag = 0;
				if (disp<-128 || disp>127) {
					/* long */
					flag = 1;
					++dot->s_addr;
					if (op != JMP)
						dot->s_addr += 2;
				}
			}
			setbit(flag);
		} else if (getbit()) {
			if (op != JMP) {
				outab(op ^ 01);
				outab(03);
			}
			outab(0xE9);
			outrw(&e1, 1);
		} else {
			disp = e1.e_addr - dot->s_addr - 2;
			outab(op);
			outab(disp);
		}
		break;

	case S_REL:
		if (addr(&e1) != DIR
		||  e1.e_type != E_DIR
		||  e1.e_base.e_lp != dot->s_base.s_lp)
			aerr("not a direct address in this segment");
		disp = e1.e_addr - dot->s_addr - 2;
		if (disp<-128 || disp>127)
			aerr("address out of range");
		outab(op);
		outab(disp);
		break;

	case S_IJMP:
		addr(&e1);
		outgen(IJORC, op, &e1);
		break;

	case S_CALL:
		if (addr(&e1) != DIR)
			aerr("not a direct address");
		outab(DCALL);
		outrw(&e1, 1);
		break;

	case S_DOP:
		m1 = addr(&e1);
		comma();
		m2 = addr(&e2);
		bytecheck(op, m1, m2);
		if (m2 == IM) {
			si = 0;
			if (isalax(&e1)) {
				if (op == TESTW)
					op = TESTAW;
				else if (op == TESTB)
					op = TESTAB;
				else
					op |= 04;
				outab(op);
			} else {
				if (op==TESTW || op==TESTB) {
					rf = 0;
					if (op == TESTW)
						op = TESTMW;
					else
						op = TESTMB;
				} else {
					rf = (op&070)>>3;
					op = (op&W)|0200;
					if (ishort(sp, &e2)) {
						++si;
						op |= S;
					}
				}
				outgen(op, rf, &e1);
			}
			if (si || (op&W)==0)
				outrb(&e2, 0);
			else
				outrw(&e2, 0);
			break;
		}
		/* Dest is reg */
		if (m1==BR || m1==WR) {
			if (op!=TESTB && op!=TESTW)
				op |= D;
			outgen(op, rof(e1), &e2);
			break;
		}
		/* Dest is mem */
		outgen(op, rof(e2), &e1);
		break;

	case S_XCHG:
		m1 = addr(&e1);
		comma();
		m2 = addr(&e2);
		bytecheck(op, m1, m2);
		if (m1==WR && m2==WR) {
			if (isalax(&e1)) {
				outab(XCHGR | rof(e2));
				break;
			}
			if (isalax(&e2)) {
				outab(XCHGR | rof(e1));
				break;
			}
		}
		if (m1==BR || m1==WR) {
			outgen(op, rof(e1), &e2);
			break;
		}
		if (m2==BR || m2==WR) {
			outgen(op, rof(e2), &e1);
			break;
		}
		aerr("improper operand pair");
		break;

	case S_SOP:
	case S_SOPB:
		m1 = addr(&e1);
		ob = (op < 2) ? INCDEC : NOTNEG;
		if (sp->s_kind == S_SOP)
			ob |= W;
		bytecheck(ob, m1, NONE);
		if (op<2 && m1==WR) {
			outab(IDREG | (op<<3) | rof(e1));
			break;
		}
		outgen(ob, op, &e1);
		break;

	case S_ESC:
		addr(&e1);
		outgen(ESC, 0, &e1);
		break;

	case S_LEA:
		m1 = addr(&e1);
		comma();
		m2 = addr(&e2);
		if (m1!=WR)
			aerr("must load address into register");
		if(m2!=DIR && m2!=X)
			aerr("must load direct address");
		outgen(op, rof(e1), &e2);
		break;

	case S_MOV:
		m1 = addr(&e1);
		comma();
		m2 = addr(&e2);
		bytecheck(op, m1, m2);
		if (m2 == IM) {
			if (m1 == BR) {
				outab(MVIB | rof(e1));
				outrb(&e2, 0);
				break;
			}
			if (m1 == WR) {
				outab(MVIW | rof(e1));
				outrw(&e2, 0);
				break;
			}
			/* To memory */
			outgen((MVI|(op&W)), 0, &e1);
			if ((op&W) == 0)
				outrb(&e2, 0);
			else
				outrw(&e2, 0);
			break;
		}
		/* Quick load */
		if (isalax(&e1) && m2==DIR) {
			outab(MOVMA|(op&W));
			outrw(&e2, 0);
			break;
		}
		/* Quick store */
		if (m1==DIR && isalax(&e2)) {
			outab(MOVAM|(op&W));
			outrw(&e1, 0);
			break;
		}
		if (m1 == SEGR)
			outgen(MOVSEG|D, rof(e1), &e2);
		else if (m2 == SEGR)
			outgen(MOVSEG,   rof(e2), &e1);
		else if (m1==WR || m1==BR)
			outgen(op|D, rof(e1), &e2);
		else
			outgen(op,   rof(e2), &e1);
		break;

	case S_MUL:
	case S_MULB:
		m1 = addr(&e1);
		ob = MULDIV;
		if (sp->s_kind == S_MUL)
			ob |= W;
		bytecheck(ob, m1, NONE);
		outgen(ob, op, &e1);
		break;
	case S_PROT0:
	case S_PROT1:
		/*
		 * Protection control.
		 */
		m1 = addr(&e1);
		if ( (m1 != WR) && (m1 != DIR) && (m1 != X) )
			aerr();
		outab( 0x0F );
		outgen( (sp->s_kind == S_PROT0) ? 0x00 : 0x01, op, &e1 );
		break;

	case S_PROTR:
		/*
		 * Protection control to register.
		 */
		if ( op == ARPL ) {
			m2 = addr(&e2);
			comma();
			m1 = addr(&e1 );
		}
		else if ( op == CLTS ) {
			outab( 0x0F );
			outab( op );
			break;
		}
		else {
			m1 = addr(&e1);
			comma();
			m2 = addr(&e2);
		}
		if (m1!=WR || (m2!=WR &&m2!=DIR && m2!=X))
			aerr("Improper operand");
		if ( op != ARPL )
			outab( 0x0F );
		outgen(op, rof(e1), &e2);
		break;

	case S_ENTER:
		m1 = addr(&e1);
		comma();
		m2 = addr(&e2);
		if ( (m1 != DIR) || (e1.e_type != E_ACON)
		  || (m2 != DIR) || (e2.e_type != E_ACON) )
			aerr();
		outab( op );
		outrw( &e1, 0);
		outrb( &e2, 0 );
		break;

	/* Floating point operations. */
	/* No operands. */
	case S_FP_F:
		outab((sp->s_flag==S_NW) ? FNOP : FWAIT);
		outab(BYTE1(op));
		outab(BYTE2(op));
		break;
	/* Memory operand. */
	case S_FP_M:
		outab((sp->s_flag==S_NW) ? FNOP : FWAIT);
		m1 = addr(&e1);
		if (m1 != DIR && m1 != X)
			qerr("invalid operand type");
		outgen(BYTE1(op), BYTE2(op), &e1);
		break;
	/* Two optional fp stack operands. */
	/* The opcode in the table is for the format "f<op> st<n>,st". */
	/* Some nasty fudging here;  thanks again, Intel. */
	case S_FP_S:
		outab(FWAIT);
		if (fp_reg2(&e1, &e2)) {
			if (e1.e_mode == ST) {
				/* "f<op> st,st<n>". */
				/* Change BYTE1 from 0xDC to 0xD8. */
				outab(BYTE1(op)^4);
				if (sp->s_flag == S_FIX)
					outab((BYTE2(op)^8)|rof(e2));
				else
					outab(BYTE2(op)|rof(e2));
			}
			else {
				/* "f<op> st<n>,st". */
				outab(BYTE1(op));
				outab(BYTE2(op)|rof(e1));
			}
		}
		else {	/* No args supplied; "f<op>" means "f<op>p st1,st". */
			/* Change BYTE1 from 0xDC to 0xDE. */
			outab(BYTE1(op)|2);
			outab(BYTE2(op)|1);
		}
		break;
	/* Two optional fp stack operands. */
	case S_FP_SP:
		fp_reg2(&e1, &e2);
		if (e2.e_mode != ST)
			qerr("invalid operand type");
		outab(FWAIT);
		outab(BYTE1(op));
		outab((BYTE2(op))|rof(e1));
		break;
	/* One optional fp stack operand (default: ST). */
	case S_FP_S1:
		fp_reg(&e1);
		outab(FWAIT);
		outab(BYTE1(op));
		outab(BYTE2(op)|rof(e1));
		break;

	default:
		err('o', "unknown operator");
	}
}

/*
 * Check if the next non blank
 * character is a comma. Give an error if
 * not (and give up).
 */
comma()
{
	if (getnb() != ',')
		qerr("expected comma");
}

/*
 * Output `general' format instructions.
 * `Op' is the opcode, `r' is the general
 * register (bits 3-5 of the postbyte) and
 * `esp' is an address.
 * The address must not be the flags, a
 * segment register, (dx) or an immediate
 * thing.
 */
outgen(op, r, esp)
register struct expr *esp;
{
	register disp, mode, regm;

	outab(op);
	mode = esp->e_mode & MMASK;
	regm = esp->e_mode & RMASK;
	if (mode==IDX || mode==ICL || mode==IM || mode==SEGR) {
		aerr("invalid operand");
		return;
	}
	if (mode==BR || mode==WR) {
		outab(0300 | (r<<3) | regm);
		return;
	}
	if (mode == DIR) {
		outab(0006 | (r<<3));
		outrw(esp, 0);
		return;
	}
	/* Mode is X */
	if (esp->e_type == E_ACON) {
		disp = esp->e_addr;		/* Displacement */
		if (regm!=6 && disp==0) {
			outab((r<<3) | regm);
			return;
		}
		if (disp>=-128 && disp<=127) {
			outab(0100 | (r<<3) | regm);
			outab(disp);
			return;
		}
	}
	outab(0200 | (r<<3) | regm);
	outrw(esp, 0);
}

/*
 * Output a byte that looks like
 * `bbbbbaaa'; this is a somewhat common
 * format on the iAPX-86.
 */
out3(a, b)
{
	outab(a | (b<<3));
}

/*
 * Some consistancy checks.
 * The `op' is an opcode with a valid `W' bit.
 * `m1' and `m2' are modes.
 */
bytecheck(op, m1, m2)
register op, m1, m2;
{
	register bad;

	bad = 0;
	if (m1 == BR) {
		if ((op&W) != 0)
			++bad;
		if (m2==WR || m2==SEGR)
			++bad;
	}
	if (m1==WR || m1==SEGR) {
		if ((op&W) == 0)
			++bad;
		if (m2 == BR)
			++bad;
	}
	if (m2 == BR) {
		if ((op&W) != 0)
			++bad;
		if (m1==WR || m1==SEGR)
			++bad;
	}
	if (m2==WR || m2==SEGR) {
		if ((op&W) == 0)
			++bad;
		if (m1 == BR)
			++bad;
	}
	if (bad)
		aerr("invalid operand");
}

/*
 * Check to see if an address is
 * one of the two machine accumulator registers.
 * (AX or AL).
 * True return if so.
 */
isalax(esp)
register struct expr *esp;
{
	if (esp->e_mode==AX || esp->e_mode==AL)
		return (1);
	return (0);
}

/*
 * Set up the big bit table
 * used by the branch adjustment code.
 */
minit()
{
	bp = bb;
	bm = 1;
}

/*
 * Store `b' in the next slot of the
 * bit table.
 * If no room, throw it away.
 */
setbit(b)
{
	if (bp >= &bb[NB])
		return;
	if (b)
		*bp |= bm;
	bm <<= 1;
	if (bm == 0) {
		bm = 1;
		++bp;
	}
}

/*
 * Get the next bit from the bit
 * table.
 * If none left, return a `1'.
 * This will get the long form of
 * branches.
 */
getbit()
{
	register f;

	if (bp >= &bb[NB])
		return (1);
	f = *bp & bm;
	bm <<= 1;
	if (bm == 0) {
		bm = 1;
		++bp;
	}
	return (f);
}

/*
 * This routine checks if the expression
 * pointed to by `esp' is an absolute expression
 * and has value `n'. This is used to check out
 * the address fields of shifts and of interrupt
 * request instructions.
 */
isabsn(esp, n)
register struct expr *esp;
{
	if (esp->e_type==E_ACON && esp->e_addr==n)
		return (1);
	return (0);
}

/*
 * This routine checks if an immediate
 * operand fits in 8 bits. It is used to check
 * for immediate length in instructions that
 * can take the w:s immediate.
 */
ishort(sp, esp)
register struct sym  *sp;
register struct expr *esp;
{
	register n;

	if ((sp->s_flag&S_OBL) == 0)
		return (0);
	if (esp->e_type != E_ACON)
		return (0);
	n = esp->e_addr&0177600;
	if (n!=0 && n!=0177600)
		return (0);
	return (1);
}

/*
 * Make up the initial set of
 * location counters.
 * Poke one in every nonsegmented
 * space. Add a special one for
 * the C compiler's strings.
 */
locinit()
{
	struct loc *locdef();

	defloc = locdef(".shri", L_SHRI);
	locdef(".prvi", L_PRVI);
	locdef(".bssi", L_BSSI);
	locdef(".shrd", L_SHRD);
	locdef(".prvd", L_PRVD);
	locdef(".bssd", L_BSSD);
	locdef(".strn", L_PRVD);
	locdef(".symt", L_DEBUG);
	nloc = NLSEG;
}

struct loc *
locdef(cp, t)
char *cp;
{
	register char *cp1, *cp2;
	register struct loc *lp;
	struct sym *sp;
	struct loc *lp1, *lp2;
	int c;
	char id[NCPLN];

	lp = (struct loc *) new(sizeof(struct loc));
	lp->l_seg = t;
	lp->l_lp = NULL;
	lp->l_fuzz = 0;
	lp->l_break = 0;
	lp->l_offset = 0;
	lp1 = NULL;
	lp2 = loc[t];
	while (lp2 != NULL) {
		lp1 = lp2;
		lp2 = lp2->l_lp;
	}
	if (lp1 == NULL)
		loc[t] = lp; else
		lp1->l_lp = lp;
	cp1 = cp;
	cp2 = &id[0];
	while (c = *cp1++)
		if (cp2 < &id[NCPLN])
			*cp2++ = c;
	while (cp2 < &id[NCPLN])
		*cp2++ = 0;
	sp = lookup(id, 1);
	sp->s_kind = S_LOC;
	sp->s_flag = 0;
	sp->s_addr = (address)lp;
	return (lp);
}
@


1.1
log
@Initial revision
@
text
@d80 1
a80 1
		aerr();
d88 1
a88 1
		aerr();
d109 1
a109 1
					aerr();
d121 13
d147 1
a147 1
			aerr();
d158 1
a158 1
		aerr();
d178 6
a183 2
		else if (m2!=IM || !isabsn(&e2, 1))
			aerr();
d185 2
d191 1
a191 1
			aerr();
d237 1
a237 1
			aerr();
d240 1
a240 1
			aerr();
d252 1
a252 1
			aerr();
d329 1
a329 1
		aerr();
d355 4
a358 2
		if (m1!=WR || (m2!=DIR && m2!=X))
			aerr();
d417 11
d429 97
d527 1
a527 1
		err('o');
d539 1
a539 1
		qerr();
d560 1
a560 1
		aerr();
d635 1
a635 1
		aerr();
d797 1
a797 1
	sp->s_addr = lp;
@

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.