#include #include #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; ireg[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<>(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<>(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; }