#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;
}
|