Plan 9 from Bell Labs’s /usr/web/sources/contrib/rsc/8i/exec.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include "8i.h"

static long pop(Cpu*, int);

int
parityw(ulong r)
{
	int n;

	n = 0;
	while(r){
		n ^= r&1;
		r>>=1;
	}
	return n ? 0 : PF;
}

void
trap(Cpu *cpu, int t)
{
	if(t >= 0x100){
		dumpreg(cpu);
		print("%.5P %I\n", cpu, cpu->inst);
		switch(t){
		case TBADOP:
			print("TRAP: bad opcode\n");
			break;
		case THALT:
			print("TRAP: halt\n");
			break;
		case TGP0:
			print("TRAP: general protection fault\n");
			break;
		case TSEGFAULT:
			print("TRAP: segfault addr %.lux\n", cpu->addr);
			break;
		}
		abort();
	}

	if(cpu->trap)
		cpu->trap(cpu, cpu->inst, t);
	else{
		fprint(2, "unhandled trap\n");
		abort();
	}
	longjmp(cpu->jmp, 1);
}

static long
pop(Cpu *cpu, int sz)
{
	long r;
	Wordop *wop;

	switch(sz){
	default:
		trap(cpu, TBADOP);
	case 16:
		wop = &wop16;
		break;
	case 32:
		wop = &wop32;
		break;
	}
	r = wop->fetch(cpu, wop->rreg(cpu, RSS, 0), wop->rreg(cpu, RSP, 0));
	wop->wreg(cpu, RSP, 0, wop->rreg(cpu, RSP, 0)+wop->len);
	return r;
}

void
push(Cpu *cpu, int sz, long r)
{
	Wordop *wop;

	switch(sz){
	default:
		trap(cpu, TBADOP);
	case 16:
		wop = &wop16;
		break;
	case 32:
		wop = &wop32;
		break;
	}

	wop->wreg(cpu, RSP, 0, wop->rreg(cpu, RSP, 0)-wop->len);
	wop->store(cpu, wop->rreg(cpu, RSS, 0), wop->rreg(cpu, RSP, 0), r);
}

static long
blu(Cpu *cpu, int sz, long r)
{
	ulong psw;

	psw = cpu->flags & ~(OF|CF|SF|ZF|PF);
	switch(sz){
	case 8:	
		r &= 0xff;
		if(r == 0)
			psw |= ZF;
		if(r & 0x80)
			psw |= SF;
		break;
	case 16:
		r &= 0xffff;
		if(r == 0)
			psw |= ZF;
		if(r & 0x8000)
			psw |= SF;
		break;
	case 32:
		if(r == 0)
			psw |= ZF;
		if(r & 0x80000000)
			psw |= SF;
		break;
	}
	psw |= parityw(r&0xFF);
	cpu->flags = psw;
	return r;
}

static vlong
alul(Cpu *cpu, vlong a, vlong b, vlong c)
{
	int psw;
	ulong r;
	vlong vr;

	vr = a + b;
	if(c)
		++vr;
	r = vr;
	if(c >= 0){
		psw = cpu->flags & ~(OF|CF|SF|ZF|PF);
		if(vr & 0x100000000LL)
			psw |= CF;
	}else
		psw = cpu->flags & ~(OF|SF|ZF|PF);
	if(r & 0x80000000)
		psw |= SF;
	if(!((a^b) & 0x80000000) && ((a^r) & 0x80000000))
		psw |= OF;
	if(r == 0)
		psw |= ZF;
	psw |= parityw(r&0xFF);
	cpu->flags = psw;
	return r;
}

static int
aluw(Cpu *cpu, int a, int b, int c)
{
	int r, psw;

	r = a + b;
	if(c)
		++r;
	if(c >= 0){
		psw = cpu->flags & ~(OF|CF|SF|ZF|PF);
		if(r & 0x10000)
			psw |= CF;
	}else
		psw = cpu->flags & ~(OF|SF|ZF|PF);
	if(r & 0x8000)
		psw |= SF;
	if(!((a^b) & 0x8000) && ((a^r) & 0x8000))
		psw |= OF;
	r &= 0xffff;
	if(r == 0)
		psw |= ZF;
	psw |= parityw(r&0xFF);
	cpu->flags = psw;
	return r;
}

static int
alub(Cpu *cpu, int a, int b, int c)
{
	int r, psw;

	r = a + b;
	if(c)
		++r;
	if(c >= 0){
		psw = cpu->flags & ~(OF|CF|SF|ZF|PF);
		if(r & 0x100)
			psw |= CF;
	}else
		psw = cpu->flags & ~(OF|SF|ZF|PF);
	if(r & 0x80)
		psw |= SF;
	if(!((a^b) & 0x80) && ((a^r) & 0x80))
		psw |= OF;
	r &= 0xff;
	if(r == 0)
		psw |= ZF;
	psw |= parityw(r&0xFF);
	cpu->flags = psw;
	return r;
}

static long
add(Cpu *cpu, int sz, long a, long b)
{
	switch(sz){
	default:	abort();
	case 8:	return alub(cpu, a, b, 0);
	case 16:	return aluw(cpu, a, b, 0);
	case 32:	return alul(cpu, a, b, 0);
	}
}

static long
adc(Cpu *cpu, int sz, long a, long b)
{
	switch(sz){
	default:	abort();
	case 8:	return alub(cpu, a, b, cpu->flags&CF);
	case 16:	return aluw(cpu, a, b, cpu->flags&CF);
	case 32:	return alul(cpu, a, b, cpu->flags&CF);
	}
}

static long
inc(Cpu *cpu, int sz, long a)
{
	switch(sz){
	default:	abort();
	case 8:	return alub(cpu, a, 0, -1);
	case 16:	return aluw(cpu, a, 0, -1);
	case 32:	return alul(cpu, a, 0, -1);
	}
}

static long
dec(Cpu *cpu, int sz, long a)
{
	switch(sz){
	default:	abort();
	case 8:	return alub(cpu, a, 0x1fe, -1);
	case 16:	return aluw(cpu, a, 0x1fffe, -1);
	case 32:	return alul(cpu, a, 0x1ffffffffLL, -1);
	}
}

static long
sub(Cpu *cpu, int sz, long a, long b)
{
	switch(sz){
	default:	abort();
	case 8:	return alub(cpu, a, 0x100+(0xff^b), 1);
	case 16:	return aluw(cpu, a, 0x10000+(0xffff^b), 1);
	case 32:	return alul(cpu, a, (1LL<<32)+~b, 1);
	}
}

static long
sbb(Cpu *cpu, int sz, long a, long b)
{
	switch(sz){
	default:	abort();
	case 8:	return alub(cpu, a, 0x100+(0xff^b), CF^(cpu->flags&CF));
	case 16:	return aluw(cpu, a, 0x10000+(0xffff^b), CF^(cpu->flags&CF));
	case 32:	return alul(cpu, a, (1LL<<32)+~b, CF^(cpu->flags&CF));
	}
}

static void
opbad(Cpu *cpu, Inst *inst)
{
	cpu->pc = inst->spc;
	trap(cpu, TBADOP);
}

static void
opadc(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, 
		adc(cpu, inst->opsize, inst->arg1.val, inst->arg2.val));
}

static void
opadd(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, 
		add(cpu, inst->opsize, inst->arg1.val, inst->arg2.val));
}

static void
opand(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, 
		blu(cpu, inst->opsize, inst->arg1.val & inst->arg2.val));
}

static void
opcall(Cpu *cpu, Inst *inst)
{
	ulong cpc, ccs;

	switch(inst->opsize){
	default:
		trap(cpu, TBADOP);
	case 16:
		break;
	}

	cpc = inst->arg1.off;
	ccs = cpu->reg[RCS];
	switch(inst->arg1.atype){
	default:
		trap(cpu, TBADOP);

	case AAp:		/* far through memory */
	case AEp:
		push(cpu, inst->opsize, wop16.rreg(cpu, RCS, 0));
		push(cpu, inst->opsize, cpu->npc);
		ccs = inst->arg1.seg;
		break;
	case AEv:		/* local through memory */
		push(cpu, inst->opsize, cpu->npc);
		cpc = inst->arg1.val;
		break;
	case AJv:		/* local */
		push(cpu, inst->opsize, cpu->npc);
		break;
	}
	cpu->ncs = ccs;
	cpu->npc = cpc;
}

static void
opcbw(Cpu *cpu, Inst *inst)
{
	if(inst->opsize == 16)
		wop16.wreg(cpu, RAX, 0, (schar)wop8.rreg(cpu, RAL, 0));
	else
		wop32.wreg(cpu, RAX, 0, (short)wop16.rreg(cpu, RAX, 0));
}

static void
opclc(Cpu *cpu, Inst*)
{
	cpu->flags &= ~CF;
}

static void
opcld(Cpu *cpu, Inst*)
{
	cpu->flags &= ~DF;
}

static void
opcli(Cpu *cpu, Inst*)
{
	cpu->flags &= ~IF;
}

static void
opcmc(Cpu *cpu, Inst*)
{
	cpu->flags ^= CF;
}

static int
should(Cpu *cpu, Inst *inst, int j)
{
	int psw, t;
	enum {SO = 1<<16,	/* pseudo-flag SF != OF */ };
	static int test[] = {
		OF,
		CF,
		ZF,
		CF|ZF,
		SF,
		PF,
		SO,
		SO|ZF,
	};

	switch(j){
	case 0xE3:	/* JCXZ */
		return inst->arg2.val==0;
	case 0xEB:	/* JMP */
	case 0xE9:
	case 0xEA:
	case 0xFF:
		return 1;
	default:
		psw = cpu->flags;
		if(((psw&SF)!=0) ^ ((psw&OF)!=0))
			psw |= SO;
		t = test[(j>>1)&7];
		return ((t&psw) != 0) ^ (j&1);
	}

}

static void
opcmov(Cpu *cpu, Inst *inst)
{
	if(should(cpu, inst, inst->i))
		inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, inst->arg2.val);
}

static void
opcmp(Cpu *cpu, Inst *inst)
{
//print("cmp %x %x\n", inst->arg1.val, inst->arg2.val);
	sub(cpu, inst->opsize, inst->arg1.val, inst->arg2.val);
	if(inst->arg1.p)
		inst->arg1.p(cpu, inst, &inst->arg1);
	if(inst->arg2.p)
		inst->arg2.p(cpu, inst, &inst->arg2);
}

static void
opcwd(Cpu *cpu, Inst *inst)
{
	if(inst->opsize == 16)
		wop16.wreg(cpu, RDX, 0, ((short)wop16.rreg(cpu, RAX, 0)) >> 15);
	else
		wop32.wreg(cpu, RDX, 0, ((long)wop32.rreg(cpu, RAX, 0)) >> 31);
}

static void
opdec(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, 
		dec(cpu, inst->opsize, inst->arg1.val));
}

static void
opdiv(Cpu *cpu, Inst *inst)
{
	uint n, d, q, r;
	uvlong vn, vq;

	switch(inst->opsize){
	default:
		trap(cpu, TBADOP);

	case 8:
		n = wop16.rreg(cpu, RAX, 0);
		d = inst->arg1.val;
		if(d == 0)
			trap(cpu, TDIV0);
		r = n%d;
		q = n/d;
		if(q > 0xFF)
			trap(cpu, TDIV0);
		wop8.wreg(cpu, RAH, 0, r);
		wop8.wreg(cpu, RAL, 0, q);
		break;

	case 16:
		n = (wop16.rreg(cpu, RDX, 0)<<16)|wop16.rreg(cpu, RAX, 0);
		d = inst->arg1.val;
		if(d == 0)
			trap(cpu, TDIV0);
		r = n%d;
		q = n/d;
		if(q > 0xFFFF)
			trap(cpu, TDIV0);
		wop16.wreg(cpu, RDX, 0, r);
		wop16.wreg(cpu, RAX, 0, q);
		break;

	case 32:
		vn = ((uvlong)wop32.rreg(cpu, RDX, 0)<<32)|wop32.rreg(cpu, RAX, 0);
		d = inst->arg1.val;
		if(d == 0)
			trap(cpu, TDIV0);
		r = vn%d;
		vq = vn/d;
		if(vq > 0xFFFFFFFFULL)
			trap(cpu, TDIV0);
		wop32.wreg(cpu, RDX, 0, r);
		wop32.wreg(cpu, RAX, 0, vq);
		break;
	}

	/* flags are undefined now */
}

static void
openter(Cpu *cpu, Inst *inst)
{
	int i, level;
	int n, fp;

	if(inst->opsize != 16 || inst->addrsize != 16)
		trap(cpu, TBADOP);

	n = inst->arg1.val;
	level = inst->arg2.val;
	level &= 31;
	push(cpu, 16, cpu->reg[RBP]);
	fp = cpu->reg[RSP] & 0xFFFF;
	if(level){
		for(i=0; i<level-1; i++){
			cpu->reg[RBP] -= 2;
			push(cpu, 16, cpu->reg[RBP]);
		}
		push(cpu, 16, fp);
	}
	cpu->reg[RBP] = fp;
	cpu->reg[RSP] -= n;
	cpu->reg[RBP] &= 0xFFFF;
	cpu->reg[RSP] &= 0xFFFF;
}

static void
ophlt(Cpu *cpu, Inst*)
{
	trap(cpu, THALT);
}

static void
opidiv(Cpu *cpu, Inst *inst)
{
	int n, d, q, r;
	vlong vn, vq;

	switch(inst->opsize){
	default:
		trap(cpu, TBADOP);

	case 8:
		n = (short)wop16.rreg(cpu, RAX, 0);
		d = (schar)inst->arg1.val;
		if(d == 0)
			trap(cpu, TDIV0);
		r = n%d;
		q = n/d;
		if(q > 0x7F || q < -0x80)
			trap(cpu, TDIV0);
		wop8.wreg(cpu, RAH, 0, r);
		wop8.wreg(cpu, RAL, 0, q);
		break;

	case 16:
		n = (long)((wop16.rreg(cpu, RDX, 0)<<16)|wop16.rreg(cpu, RAX, 0));
		d = (short)inst->arg1.val;
		if(d == 0)
			trap(cpu, TDIV0);
		r = n%d;
		q = n/d;
		if(q > 0x7FFF || q < -0x8000)
			trap(cpu, TDIV0);
		wop16.wreg(cpu, RDX, 0, r);
		wop16.wreg(cpu, RAX, 0, q);
		break;

	case 32:
		vn = ((vlong)wop32.rreg(cpu, RDX, 0)<<32)|wop32.rreg(cpu, RAX, 0);
		d = (long)inst->arg1.val;
		if(d == 0)
			trap(cpu, TDIV0);
		r = vn%d;
		vq = vn/d;
		if(vq > 0x7FFFFFFFLL || vq < -0x80000000LL)
			trap(cpu, TDIV0);
		wop32.wreg(cpu, RDX, 0, r);
		wop32.wreg(cpu, RAX, 0, vq);
		break;
	}

	/* flags are undefined now */
}

static void
opimul(Cpu *cpu, Inst *inst)
{
	int p, f1, f2, overflow;
	vlong vp, vf1, vf2;

	overflow = 0;
	switch(inst->opsize){
	default:
		trap(cpu, TBADOP);

	case 8:
		f1 = (schar)inst->arg2.val;
		f2 = (schar)inst->arg3.val;
		p = f1*f2;
		if(p > 0x7F || p < -0x80)
			overflow = 1;
		inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, p);
		break;

	case 16:
		f1 = (short)inst->arg2.val;
		f2 = (short)inst->arg3.val;
		p = f1*f2;
		if(p > 0x7FFF || p < -0x8000)
			overflow = 1;
		inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, p);
		if(inst->arg1.atype == AAX)
			wop16.wreg(cpu, RDX, 0, p>>16);
		break;

	case 32:
		vf1 = (long)inst->arg2.val;
		vf2 = (long)inst->arg3.val;
		vp = vf1*vf2;
		if(vp > 0x7FFFFFFFLL || vp < -0x80000000LL)
			overflow = 1;
		inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, vp);
		if(inst->arg1.atype == AAX)
			wop32.wreg(cpu, RDX, 0, vp>>32);
		break;
	}

	if(overflow)
		cpu->flags |= (CF|OF);
	else
		cpu->flags &= ~(CF|OF);

	/* SF, ZF, AF, PF are undefined now */
}

static void
opin(Cpu *cpu, Inst *inst)
{
	int port;

	port = inst->arg2.val;
	switch(inst->opsize){
	case 8:
		if(cpu->inb == nil)
			trap(cpu, TGP0);
		inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, cpu->inb(port));
		break;
	case 16:
		if(cpu->inw == nil)
			trap(cpu, TGP0);
		inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, cpu->inw(port));
		break;
	case 32:
		if(cpu->inl == nil)
			trap(cpu, TGP0);
		inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, cpu->inl(port));
		break;
	}
	if(inst->arg1.p)
		inst->arg1.p(cpu, inst, &inst->arg1);
}

static void
opinc(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, 
		inc(cpu, inst->opsize, inst->arg1.val));
}

static void
opint(Cpu *cpu, Inst *inst)
{
	trap(cpu, inst->arg1.val);
}

static void
opiret(Cpu *cpu, Inst *inst)
{
	switch(inst->opsize){
	default:
		trap(cpu, TBADOP);
	case 8:
	case 16:
		cpu->npc &= ~0xFFFF;
		cpu->npc |= pop(cpu, 16) & 0xFFFF;
		cpu->ncs = pop(cpu, 16);
		putflags(cpu, 0, 0, pop(cpu, 16));
	}
}

static void
opjump(Cpu *cpu, Inst *inst)
{
	if(should(cpu, inst, inst->i)){
		cpu->ncs = inst->arg1.seg;
		cpu->npc = inst->arg1.off;
	}
}

static void
oplahf(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, cpu->flags);
}

static void
oplea(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, inst->arg2.off);
}

static void
opleave(Cpu *cpu, Inst *inst)
{
	if(inst->opsize != 16 || inst->addrsize != 16)
		trap(cpu, TBADOP);
	cpu->reg[RSP] = cpu->reg[RBP];
	cpu->reg[RBP] = pop(cpu, 16);
}

static void
oplfp(Cpu *cpu, Inst *inst)
{
	ulong o, s;
	Wordop *wop;

	switch(inst->opsize){
	default:
		trap(cpu, TBADOP);
	case 16:
		wop = &wop16;
		break;
	case 32:
		wop = &wop32;
		break;
	}

	o = wop->fetch(cpu, inst->arg3.seg, inst->arg3.off);
	s = wop->fetch(cpu, inst->arg3.seg, inst->arg3.off+2);
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, s);
	inst->arg2.w(cpu, inst->arg2.seg, inst->arg2.off, o);
}

static void
oplods(Cpu *cpu, Inst *inst)
{	
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, inst->arg2.val);
	inst->arg2.p(cpu, inst, &inst->arg2);
}

static void
oploop(Cpu *cpu, Inst *inst)
{
	int mask, set;

	switch(inst->i){
	default:
		trap(cpu, TBADOP);
	case 0xE0:	/* LOOPNZ */
		mask = ZF;
		set = 0;
		break;
	case 0xE1:	/* LOOPZ */
		mask = ZF;
		set = ZF;
		break;
	case 0xE2:	/* LOOP */
		mask = 0;
		set = ZF;	
		break;
	}

	inst->arg2.val--;
	inst->arg2.w(cpu, inst->arg2.seg, inst->arg2.off, inst->arg2.val);
	if(inst->arg2.val != 0 && (cpu->flags&mask) != set){
		cpu->ncs = inst->arg1.seg;
		cpu->npc = inst->arg1.off;
	}
}

static void
opmov(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, inst->arg2.val);
}

static void
opmovs(Cpu *cpu, Inst *inst)
{
	inst->arg2.w(cpu, inst->arg2.seg, inst->arg2.off, inst->arg1.val);
	inst->arg1.p(cpu, inst, &inst->arg1);
	inst->arg2.p(cpu, inst, &inst->arg2);
}

static void
opmul(Cpu *cpu, Inst *inst)
{
	uint p, f1, f2, overflow;
	uvlong vp, vf1, vf2;

	overflow = 0;
	switch(inst->opsize){
	default:
		trap(cpu, TBADOP);

	case 8:
		f1 = inst->arg2.val;
		f2 = inst->arg3.val;
		p = f1*f2;
		if(p > 0xFF)
			overflow = 1;
		inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, p);
		break;

	case 16:
		f1 = inst->arg2.val;
		f2 = inst->arg3.val;
		p = f1*f2;
		if(p > 0xFFFF)
			overflow = 1;
		inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, p);
		wop16.wreg(cpu, RDX, 0, p>>16);
		break;

	case 32:
		vf1 = inst->arg2.val;
		vf2 = inst->arg3.val;
		vp = vf1*vf2;
		if(vp > 0xFFFFFFFFULL)
			overflow = 1;
		inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, vp);
		wop32.wreg(cpu, RDX, 0, vp>>32);
		break;
	}

	if(overflow)
		cpu->flags |= (CF|OF);
	else
		cpu->flags &= ~(CF|OF);

	/* SF, ZF, AF, PF are undefined now */
}

static void
opneg(Cpu *cpu, Inst *inst)
{
	inst->arg1.val = -inst->arg1.val;
	// set OF, SF, ZF, PF 
	if(inst->arg1.val == 0)
		cpu->flags &= ~CF;
	else
		cpu->flags |= CF;

	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, inst->arg1.val);
}

static void
opnop(Cpu*, Inst*)
{
}

static void
opnot(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, ~inst->arg1.val);
}

static void
opor(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, 
		blu(cpu, inst->opsize, inst->arg1.val | inst->arg2.val));
}

static void
opout(Cpu *cpu, Inst *inst)
{
	int port;

	port = inst->arg1.val;
	switch(inst->opsize){
	case 8:
		if(cpu->outb == nil)
			trap(cpu, TGP0);
		cpu->outb(port, inst->arg2.val);
		break;
	case 16:
		if(cpu->outw == nil)
			trap(cpu, TGP0);
		cpu->outw(port, inst->arg2.val);
		break;
	case 32:
		if(cpu->outl == nil)
			trap(cpu, TGP0);
		cpu->outl(port, inst->arg2.val);
		break;
	}
	if(inst->arg2.p)
		inst->arg2.p(cpu, inst, &inst->arg2);
}

static void
oppop(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, pop(cpu, inst->opsize));
}

static void
oppopa(Cpu *cpu, Inst *inst)
{
	int i, sz;
	Wordop *wop;

	sz = inst->opsize;
	wop = (sz == 16) ? &wop16 : &wop32;
	for(i=7; i>=0; i--){
		if(i==RSP)
			pop(cpu, sz);
		else
			wop->wreg(cpu, i, 0, pop(cpu,sz));
	}
}

static void
oppush(Cpu *cpu, Inst *inst)
{
	push(cpu, inst->opsize, inst->arg1.val);
}

static void
oppusha(Cpu *cpu, Inst *inst)
{
	int i, sz;
	ulong sp;
	Wordop *wop;

	sz = inst->opsize;
	wop = (sz == 16) ? &wop16 : &wop32;
	sp = wop->rreg(cpu, RSP, 0);
	for(i=0; i<8; i++){
		if(i==RSP)
			push(cpu, sz, sp);
		else
			push(cpu, sz, wop->rreg(cpu, i, 0));
	}
}

static void
oprcl(Cpu *cpu, Inst *inst)
{
	ulong x, y, s;
	int n;

	s = inst->opsize;
	x = inst->arg1.val;
	n = inst->arg2.val;
	n &= 31;
	if(s < 32)
		n %= (s+1);
	if(n == 0)
		return;

	y = (x<<n) | ((x>>(s-n))>>1);	/* (x>>31)>>1 != (x>>32) on some machines */
	if(cpu->flags & CF)
		y |= 1<<(n-1);
	blu(cpu, s, y);
	if((x>>(s-n)) & 1)
		cpu->flags |= CF;
	if(((x>>(s-n))^(y>>(s-1))) & 1)
		cpu->flags |= OF;
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, y);
}

static void
oprcr(Cpu *cpu, Inst *inst)
{
	ulong x, y, s;
	int n;

	s = inst->opsize;
	x = inst->arg1.val;
	n = inst->arg2.val;
	n &= 31;
	if(s < 32)
		n %= (s+1);
	if(n == 0)
		return;

	y = (x>>n) | ((x<<(s-n))<<1);	/* (x<<31)<<1 != (x<<32) on some machines */
	if(cpu->flags & CF)
		y |= 1<<(s-n);
	blu(cpu, s, y);
	if((x<<(s-n))&(1<<(s-1)))
		cpu->flags |= CF;
	if((y^(y<<1))&(1<<(s-1)))
		cpu->flags |= OF;
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, x);
}

static void
oprol(Cpu *cpu, Inst *inst)
{
	ulong x, s;
	int n;

	s = inst->opsize;
	x = inst->arg1.val;
	n = inst->arg2.val;
	n &= s-1;
	if(n == 0)
		return;

	x = (x<<n) | (x>>(s-n));
	blu(cpu, s, x);
	if(x & 1)
		cpu->flags |= CF;
	if(((x>>(s-1))^x) & 1)
		cpu->flags |= OF;
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, x);
}

static void
opror(Cpu *cpu, Inst *inst)
{
	ulong x, s;
	int n;

	s = inst->opsize;
	x = inst->arg1.val;
	n = inst->arg2.val;
	n &= s-1;
	if(n == 0)
		return;

	x = (x>>n) | (x<<(s-n));
	blu(cpu, s, x);
	if(x & (1<<(s-1)))
		cpu->flags |= CF;
	if(((x>>(s-1))^x) & 1)
		cpu->flags |= OF;
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, x);
}

static void
opret(Cpu *cpu, Inst *inst)
{
	switch(inst->opsize){
	default:
		trap(cpu, TBADOP);
	case 16:
		cpu->npc &= ~0xFFFF;
		cpu->npc |= pop(cpu, 16) & 0xFFFF;
		wop16.wreg(cpu, RSP, 0, wop16.rreg(cpu, RSP, 0)+inst->arg1.val);
		break;
	}
}

static void
opretf(Cpu *cpu, Inst *inst)
{
	switch(inst->opsize){
	default:
		trap(cpu, TBADOP);
	case 16:
		cpu->npc &= ~0xFFFF;
		cpu->npc |= pop(cpu, 16) & 0xFFFF;
		cpu->ncs = pop(cpu, 16);
		wop16.wreg(cpu, RSP, 0, wop16.rreg(cpu, RSP, 0)+inst->arg1.val);
		break;
	}
}

static void
opsahf(Cpu *cpu, Inst *inst)
{
	ulong f;

	f = cpu->flags;
	f &= ~0xFF;
	f |= (inst->arg1.val) & 0xFF;
	putflags(cpu, 0, 0, f);
}

static void
opsar(Cpu *cpu, Inst *inst)
{
	long x, y, of;
	int n, s;

	s = inst->opsize;
	x = y = inst->arg1.val;
	n = inst->arg2.val;
	n &= s-1;
	if(n == 0)
		return;

	if(s < 32){	/* sign extend */
		x <<= 32-s;
		x >>= 32-s;
	}
	x >>= n;
	of = cpu->flags & OF;
	blu(cpu, s, x);
	if(n != 1)	/* shifts don't clear OF except on n==1 */
		cpu->flags |= of;
	if((y>>(n-1)) & 1)
		cpu->flags |= CF;
	/* OF stays cleared */
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, x);
}

static void
opshl(Cpu *cpu, Inst *inst)
{
	ulong x, y;
	int n, s, of;

	s = inst->opsize;
	x = y = inst->arg1.val;
	n = inst->arg2.val;
	n &= s-1;
	if(n == 0)
		return;

	x <<= n;
	of = cpu->flags & OF;
	blu(cpu, s, x);
	if((y<<(n-1)) & (1<<(s-1)))
		cpu->flags |= CF;
	if(n != 1)
		cpu->flags |= of;
	else if((x^(x<<1)) & (1<<(s-1)))
		cpu->flags |= OF;
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, x);
}

static void
opshr(Cpu *cpu, Inst *inst)
{
	ulong x, y;
	int n, s, of;

	s = inst->opsize;
	x = y = inst->arg1.val;
	n = inst->arg2.val;
	n &= s-1;
	if(n == 0)
		return;

	x >>= n;
	of = cpu->flags & OF;
	blu(cpu, s, x);
	if((y>>(n-1)) & 1)
		cpu->flags |= CF;
	if(n != 1)
		cpu->flags |= of;
	else if(y & (1<<(s-1)))
		cpu->flags |= OF;
	/* OF stays cleared */
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, x);
}

static void
opshld(Cpu *cpu, Inst *inst)
{
	ulong ox, x, y;
	int n, s;

	s = inst->opsize;
	ox = x = inst->arg1.val;
	y = inst->arg2.val;
	n = inst->arg3.val;
	n &= s-1;
	if(n == 0)
		return;

	x <<= n;
	x |= (y>>(s-n));
	blu(cpu, s, x);
	if((ox<<(n-1)) & (1<<(s-n)))
		cpu->flags |= CF;
	if((x^ox) & (1<<(s-n)))
		cpu->flags |= OF;
	
}

static void
opshrd(Cpu *cpu, Inst *inst)
{
	ulong ox, x, y;
	int n, s;

	s = inst->opsize;
	ox = x = inst->arg1.val;
	y = inst->arg2.val;
	n = inst->arg3.val;
	n &= s-1;
	if(n == 0)
		return;

	x >>= n;
	x |= (y<<(s-n));
	blu(cpu, s, x);
	if((ox>>(n-1)) & 1)
		cpu->flags |= CF;
	if(n == 1 && ((x^ox) & (1<<(s-n))))
		cpu->flags |= OF;
	
}

static void
opsbb(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, 
		sbb(cpu, inst->opsize, inst->arg1.val, inst->arg2.val));
}

static void
opscas(Cpu *cpu, Inst *inst)
{
	sub(cpu, inst->opsize, inst->arg1.val, inst->arg2.val);
	inst->arg2.p(cpu, inst, &inst->arg2);
}

static void
opset(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, should(cpu, inst, inst->i));
}

static void
opstc(Cpu *cpu, Inst*)
{
	cpu->flags |= CF;
}

static void
opstd(Cpu *cpu, Inst*)
{
	cpu->flags |= DF;
}

static void
opsti(Cpu *cpu, Inst*)
{
	cpu->flags |= IF;
}

static void
opstos(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, inst->arg2.val);
	inst->arg1.p(cpu, inst, &inst->arg1);
}

static void
opsub(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, 
		sub(cpu, inst->opsize, inst->arg1.val, inst->arg2.val));
}

static void
optest(Cpu *cpu, Inst *inst)
{
	blu(cpu, inst->opsize, inst->arg1.val & inst->arg2.val);
}

static void
opxchg(Cpu *cpu, Inst *inst)
{
	long x;

	x = inst->arg1.val;
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, inst->arg2.val);
	inst->arg2.w(cpu, inst->arg2.seg, inst->arg2.off, x);
}

static void
opxlat(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off,
		wop8.fetch(cpu, wop16.rreg(cpu, RDS, 0), (schar)inst->arg1.val + inst->arg2.val));
}

static void
opxor(Cpu *cpu, Inst *inst)
{
	inst->arg1.w(cpu, inst->arg1.seg, inst->arg1.off, 
		blu(cpu, inst->opsize, inst->arg1.val ^ inst->arg2.val));
}

static void (*execfn[NUMOP])(Cpu*, Inst*) = {
	[OADC]=		opadc,
	[OADD]=		opadd,
	[OAND]=		opand,
//	[OBOUND]=	opbound,
	[OCALL]=		opcall,
	[OCBW]=		opcbw,
	[OCLC]=		opclc,
	[OCLD]=		opcld,
	[OCLI]=		opcli,
	[OCMC]=		opcmc,
	[OCMOV]=	opcmov,
	[OCMP]=		opcmp,
	[OCMPS]=		opcmp,
	[OCWD]=		opcwd,
	[ODEC]=		opdec,
	[ODIV]=		opdiv,
	[OENTER]=	openter,
	[OHLT]=		ophlt,
	[OIDIV]=		opidiv,
	[OIMUL]=		opimul,
	[OIN]=		opin,
	[OINC]=		opinc,
	[OINS]=		opin,
	[OINT]=		opint,
	[OIRET]=		opiret,
	[OJUMP]=		opjump,
	[OLAHF]=		oplahf,
	[OLEA]=		oplea,
	[OLEAVE]=	opleave,
	[OLFP]=		oplfp,
	[OLODS]=		oplods,
	[OLOOP]=		oploop,
	[OLOOPNZ]=	oploop,
	[OLOOPZ]=	oploop,
	[OMOV]=		opmov,
	[OMOVS]=	opmovs,
	[OMUL]=		opmul,
	[ONEG]=		opneg,
	[ONOP]=		opnop,
	[ONOT]=		opnot,
	[OOR]=		opor,
	[OOUT]=		opout,
	[OOUTS]=		opout,
	[OPOP]=		oppop,
	[OPOPA]=		oppopa,
	[OPOPF]=		oppop,
	[OPUSH]=		oppush,
	[OPUSHA]=	oppusha,
	[OPUSHF]=	oppush,
	[ORCL]=		oprcl,
	[ORCR]=		oprcr,
	[ORET]=		opret,
	[ORETF]=		opretf,
	[OROL]=		oprol,
	[OROR]=		opror,
	[OSAHF]=		opsahf,
	[OSAR]=		opsar,
	[OSBB]=		opsbb,
	[OSCAS]=		opscas,
	[OSET]=		opset,
	[OSHL]=		opshl,
	[OSHLD]=		opshld,
	[OSHR]=		opshr,
	[OSHRD]=		opshrd,
	[OSTC]=		opstc,
	[OSTD]=		opstd,
	[OSTI]=		opsti,
	[OSTOS]=		opstos,
	[OSUB]=		opsub,
	[OTEST]=		optest,
	[OXCHG]=		opxchg,
	[OXLAT]=		opxlat,
	[OXOR]=		opxor,
};

void
run(Cpu *cpu)
{
	Inst i;

	fmtinstall('P', pcfmt);
	fmtinstall('I', instfmt);
	if(setjmp(cpu->exitjmp))
		return;
	cpu->npc = cpu->pc;
	cpu->ncs = cpu->reg[RCS];

	cpu->addrsize = 16;
	cpu->opsize = 16;

	cpu->inst = &i;
	while(setjmp(cpu->jmp))
		;
	for(;;){
		cpu->pc = cpu->npc;
		cpu->reg[RCS] = cpu->ncs;
		nextinst(cpu, &i);
		cpu->npc = i.epc;
		if(cpu->trace){
			dumpreg(cpu);
			if(print("%.*P %I\n", (int)(i.epc-i.spc), cpu, &i) <= 0)
				abort();
		}
		if(execfn[i.op] == nil)
			trap(cpu, TBADOP);
		else{
			switch(i.repeat){
			case OREPE:
				if(i.op != OCMPS && i.op != OSCAS){	/* simple REP */
					while((cpu->reg[RCX]&0xFFFF) != 0){
						execfn[i.op](cpu, &i);
						cpu->reg[RCX]--;
					}
				}else{		/* real REPE */
					cpu->flags |= ZF;
					while((cpu->reg[RCX]&0xFFFF) != 0 && (cpu->flags & ZF)){
						execfn[i.op](cpu, &i);
						cpu->reg[RCX]--;
					}
				}
				break;
			case OREPNE:
				cpu->flags &= ~ZF;
				while((cpu->reg[RCX]&0xFFFF) != 0 && !(cpu->flags & ZF)){
					execfn[i.op](cpu, &i);
					cpu->reg[RCX]--;
				}
				break;
			case 0:
				execfn[i.op](cpu, &i);
				break;
			}
		}
		cpu->instcount++;
	}
}

int
pcfmt(Fmt *fmt)
{
	Cpu *cpu;
	char buf[512], *p, *op;
	static char blanks[] = "              ";
	ulong pc;
	int n;

	cpu = va_arg(fmt->args, Cpu*);
	p = buf;
	if(!cpu->nflag)
		p += sprint(p, "%d\t", cpu->instcount);
	p += sprint(p, "%4.4lX:%4.4luX", cpu->reg[RCS], cpu->pc);
	op = p;
	pc = cpu->pc;
	if(fmt->flags&FmtPrec){
		n = fmt->prec;
		p += sprint(p, " ");
		while(--n >= 0)
			p += sprint(p, "%2.2luX", wop8.fetch(cpu, cpu->reg[RCS], pc++));
		fmt->flags &= ~FmtPrec;
	}
	n = (p-op);
	if(n < 14)
		sprint(p, "%s", &blanks[n]);
	fmtstrcpy(fmt, buf);
	return 0;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].