//Quake timedemo demo1 - 8.1FPS //11A00 - D_SCAlloc //11C1C - D_CacheSurface //36174 - SCR_CalcRefdef //SCR_CalcRefdef //Calls R_SetVrect and R_ViewChanged #define fplog 0 #include #include "ibm.h" #include "pic.h" #include "x86.h" #include "x87.h" #undef readmemb #undef writememb #define readmemb(s,a) ((readlookup2[((s)+(a))>>12]==0xFFFFFFFF || (s)==0xFFFFFFFF)?readmemb386l(s,a): *((uint8_t *)readlookup2[((s)+(a))>>12]+ (s) + (a)) ) #define writememb(s,a,v) if (writelookup2[((s)+(a))>>12]==0xFFFFFFFF || (s)==0xFFFFFFFF) writememb386l(s,a,v); else *(uint8_t *)(writelookup2[((s) + (a)) >> 12] + (s) + (a)) = v #define writememw(s,a,v) if (writelookup2[((s)+(a))>>12]==0xFFFFFFFF || (s)==0xFFFFFFFF || (((s)+(a))&0xFFF)>0xFFE) writememwl(s,a,v); else *(uint16_t *)(writelookup2[((s) + (a)) >> 12] + (s) + (a)) = v #define writememl(s,a,v) if (writelookup2[((s)+(a))>>12]==0xFFFFFFFF || (s)==0xFFFFFFFF || (((s)+(a))&0xFFF)>0xFFC) writememll(s,a,v); else *(uint32_t *)(writelookup2[((s) + (a)) >> 12] + (s) + (a)) = v static inline uint8_t geteab() { if (mod==3) return (rm&4)?regs[rm&3].b.h:regs[rm&3].b.l; // cycles-=3; if (eal_r) return *(uint8_t *)eal_r; return readmemb(easeg,eaaddr); } static inline uint16_t geteaw() { if (mod==3) return regs[rm].w; // cycles-=3; if (eal_r) return *(uint16_t *)eal_r; return readmemw(easeg,eaaddr); } static inline uint32_t geteal() { if (mod==3) return regs[rm].l; // cycles-=3; if (eal_r) return *eal_r; return readmeml(easeg,eaaddr); } #define seteab(v) if (mod!=3) { if (eal_w) *(uint8_t *)eal_w=v; else writememb386l(easeg,eaaddr,v); } else if (rm&4) regs[rm&3].b.h=v; else regs[rm].b.l=v #define seteaw(v) if (mod!=3) { if (eal_w) *(uint16_t *)eal_w=v; else writememwl(easeg,eaaddr,v); } else regs[rm].w=v #define seteal(v) if (mod!=3) { if (eal_w) *eal_w=v; else writememll(easeg,eaaddr,v); } else regs[rm].l=v double ST[8]; MMX_REG MM[8]; int ismmx = 0; uint16_t npxs,npxc,tag; int TOP; #define ST(x) ST[((TOP+(x))&7)] #define C0 (1<<8) #define C1 (1<<9) #define C2 (1<<10) #define C3 (1<<14) #define STATUS_ZERODIVIDE 4 #define x87_div(dst, src1, src2) do \ { \ if (((double)src2) == 0.0) \ { \ npxs |= STATUS_ZERODIVIDE; \ if (npxc & STATUS_ZERODIVIDE) \ dst = src1 / (double)src2; \ else \ { \ pclog("FPU : divide by zero\n"); \ picint(1 << 13); \ } \ return; \ } \ else \ dst = src1 / (double)src2; \ } while (0) void x87_set_mmx() { TOP = 0; tag = 0; ismmx = 1; } void x87_emms() { tag = 0xffff; ismmx = 0; } void x87_checkexceptions() { } void x87_reset() { } void x87_dumpregs() { if (ismmx) { pclog("MM0=%016llX\tMM1=%016llX\tMM2=%016llX\tMM3=%016llX\n", MM[0].q, MM[1].q, MM[2].q, MM[3].q); pclog("MM4=%016llX\tMM5=%016llX\tMM6=%016llX\tMM7=%016llX\n", MM[4].q, MM[5].q, MM[6].q, MM[7].q); } else { pclog("ST(0)=%f\tST(1)=%f\tST(2)=%f\tST(3)=%f\t\n",ST[TOP],ST[(TOP+1)&7],ST[(TOP+2)&7],ST[(TOP+3)&7]); pclog("ST(4)=%f\tST(5)=%f\tST(6)=%f\tST(7)=%f\t\n",ST[(TOP+4)&7],ST[(TOP+5)&7],ST[(TOP+6)&7],ST[(TOP+7)&7]); } pclog("Status = %04X Control = %04X Tag = %04X\n",npxs,npxc,tag); } void x87_print() { if (ismmx) { pclog("\tMM0=%016llX\tMM1=%016llX\tMM2=%016llX\tMM3=%016llX\t", MM[0].q, MM[1].q, MM[2].q, MM[3].q); pclog("MM4=%016llX\tMM5=%016llX\tMM6=%016llX\tMM7=%016llX\n", MM[4].q, MM[5].q, MM[6].q, MM[7].q); } else { pclog("\tST(0)=%.20f\tST(1)=%.20f\tST(2)=%f\tST(3)=%f\t",ST[TOP&7],ST[(TOP+1)&7],ST[(TOP+2)&7],ST[(TOP+3)&7]); pclog("ST(4)=%f\tST(5)=%f\tST(6)=%f\tST(7)=%f\t TOP=%i CR=%04X SR=%04X TAG=%04X\n",ST[(TOP+4)&7],ST[(TOP+5)&7],ST[(TOP+6)&7],ST[(TOP+7)&7], TOP, npxc, npxs, tag); } } void x87_push(double i) { TOP=(TOP-1)&7; ST[TOP]=i; tag&=~(3<<((TOP&7)<<1)); if (i==0.0) tag|=(1<<((TOP&7)<<1)); } double x87_pop() { double t=ST[TOP]; tag|=(3<<((TOP&7)<<1)); TOP=(TOP+1)&7; return t; } int64_t x87_fround(double b) { switch ((npxc>>10)&3) { case 0: /*Nearest*/ return (int64_t)(b+0.5); case 1: /*Down*/ return (int64_t)floor(b); case 2: /*Up*/ return (int64_t)ceil(b); case 3: /*Chop*/ return (int64_t)b; } } #define BIAS80 16383 #define BIAS64 1023 double x87_ld80() { struct { int16_t begin; union { double d; uint64_t ll; } eind; } test; test.eind.ll = readmeml(easeg,eaaddr); test.eind.ll |= (uint64_t)readmeml(easeg,eaaddr+4)<<32; test.begin = readmemw(easeg,eaaddr+8); int64_t exp64 = (((test.begin&0x7fff) - BIAS80)); int64_t blah = ((exp64 >0)?exp64:-exp64)&0x3ff; int64_t exp64final = ((exp64 >0)?blah:-blah) +BIAS64; int64_t mant64 = (test.eind.ll >> 11) & (0xfffffffffffff); int64_t sign = (test.begin&0x8000)?1:0; if ((test.begin & 0x7fff) == 0x7fff) exp64final = 0x7ff; if ((test.begin & 0x7fff) == 0) exp64final = 0; if (test.eind.ll & 0x400) mant64++; test.eind.ll = (sign <<63)|(exp64final << 52)| mant64; return test.eind.d; } void x87_st80(double d) { struct { int16_t begin; union { double d; uint64_t ll; } eind; } test; test.eind.d=d; int64_t sign80 = (test.eind.ll&(0x8000000000000000))?1:0; int64_t exp80 = test.eind.ll&(0x7ff0000000000000); int64_t exp80final = (exp80>>52); int64_t mant80 = test.eind.ll&(0x000fffffffffffff); int64_t mant80final = (mant80 << 11); if (exp80final == 0x7ff) /*Infinity / Nan*/ { exp80final = 0x7fff; mant80final |= (0x8000000000000000); } else if (d != 0){ //Zero is a special case // Elvira wants the 8 and tcalc doesn't mant80final |= (0x8000000000000000); //Ca-cyber doesn't like this when result is zero. exp80final += (BIAS80 - BIAS64); } test.begin = (((int16_t)sign80)<<15)| (int16_t)exp80final; test.eind.ll = mant80final; writememl(easeg,eaaddr,test.eind.ll); writememl(easeg,eaaddr+4,test.eind.ll>>32); writememw(easeg,eaaddr+8,test.begin); } void x87_ldmmx(MMX_REG *r) { r->l[0] = readmeml(easeg, eaaddr); r->l[1] = readmeml(easeg, eaaddr + 4); r->w[4] = readmemw(easeg, eaaddr + 8); } void x87_stmmx(MMX_REG r) { writememl(easeg, eaaddr, r.l[0]); writememl(easeg, eaaddr + 4, r.l[1]); writememw(easeg, eaaddr + 8, 0xffff); } void x87_d8() { union { float s; uint32_t i; } ts; if (mod==3) { switch (rmdat32&0xFF) { case 0xC0: case 0xC1: case 0xC2: case 0xC3: /*FADD*/ case 0xC4: case 0xC5: case 0xC6: case 0xC7: if (fplog) pclog("FADD\n"); ST(0)=ST(0)+ST(rmdat32&7); cycles-=8; return; case 0xC8: case 0xC9: case 0xCA: case 0xCB: /*FMUL*/ case 0xCC: case 0xCD: case 0xCE: case 0xCF: if (fplog) pclog("FMUL\n"); ST(0)=ST(0)*ST(rmdat32&7); cycles-=16; return; case 0xD0: case 0xD1: case 0xD2: case 0xD3: /*FCOM*/ case 0xD4: case 0xD5: case 0xD6: case 0xD7: if (fplog) pclog("FCOM\n"); npxs&=~(C0|C2|C3); if (ST(0)==ST(rmdat32&7)) npxs|=C3; else if (ST(0)> ((TOP & 7) << 1)) & 3; tag &= ~(3 << (((TOP + rmdat32) & 7) << 1)); tag |= (temp << (((TOP + rmdat32) & 7) << 1)); x87_pop(); cycles-=3; return; case 0xE0: /*FCHS*/ if (fplog) pclog("FCHS\n"); ST(0)=-ST(0); cycles-=6; return; case 0xE1: /*FABS*/ if (fplog) pclog("FABS %f\n", ST(0)); ST(0)=fabs(ST(0)); cycles-=3; return; case 0xE4: /*FTST*/ if (fplog) pclog("FTST\n"); npxs&=~(C0|C2|C3); if (ST(0)==0.0) npxs|=C3; else if (ST(0)<0.0) npxs|=C0; cycles-=4; return; case 0xE5: /*FXAM*/ if (fplog) pclog("FXAM %i %f\n", ((tag>>((TOP&7)<<1))&3), ST(0)); // pclog("FXAM %f %i\n",ST(0),(tag>>((TOP&7)<<1))&3); npxs&=~(C0|C2|C3); if (((tag>>((TOP&7)<<1))&3)==3) npxs|=(C0|C3); else if (ST(0)==0.0) npxs|=C3; else npxs|=C2; if (ST(0)<0.0) npxs|=C1; cycles-=8; return; case 0xE8: /*FLD1*/ if (fplog) pclog("FLD1\n"); x87_push(1.0); cycles-=4; return; case 0xE9: /*FLDL2T*/ if (fplog) pclog("FLDL2T\n"); x87_push(3.3219280948873623); cycles-=8; return; case 0xEA: /*FLDL2E*/ if (fplog) pclog("FLDL2E\n"); x87_push(1.4426950408889634); cycles-=8; return; case 0xEB: /*FLDPI*/ if (fplog) pclog("FLDPI\n"); x87_push(3.141592653589793); cycles-=8; return; case 0xEC: /*FLDEG2*/ if (fplog) pclog("FLDEG2\n"); x87_push(0.3010299956639812); cycles-=8; return; case 0xED: /*FLDLN2*/ if (fplog) pclog("FLDLN2\n"); x87_push(0.693147180559945); cycles-=8; return; case 0xEE: /*FLDZ*/ if (fplog) pclog("FLDZ\n"); // pclog("FLDZ %04X:%08X\n",CS,pc); x87_push(0.0); tag|=(1<<((TOP&7)<<1)); cycles-=4; return; case 0xF0: /*F2XM1*/ if (fplog) pclog("F2XM1\n"); ST(0)=pow(2.0,ST(0))-1.0; cycles-=200; return; case 0xF1: /*FYL2X*/ if (fplog) pclog("FYL2X\n"); ST(1)=ST(1)*(log(ST(0))/log(2.0)); x87_pop(); cycles-=250; return; case 0xF2: /*FPTAN*/ if (fplog) pclog("FPTAN\n"); // pclog("Tan of %f = ",ST(0)); ST(0)=tan(ST(0)); // pclog("%f\n",ST(0)); x87_push(1.0); npxs&=~C2; cycles-=235; return; case 0xF3: /*FPATAN*/ if (fplog) pclog("FPATAN\n"); /* times++; if (times==50) { dumpregs(); exit(-1); }*/ // pclog("FPATAN %08X\n",pc); ST(1)=atan2(ST(1),ST(0)); x87_pop(); cycles-=250; return; case 0xF6: /*FDECSTP*/ if (fplog) pclog("FDECSTP\n"); TOP=(TOP-1)&7; return; case 0xF7: /*FINCSTP*/ if (fplog) pclog("FINCSTP\n"); TOP=(TOP+1)&7; return; case 0xF8: /*FPREM*/ if (fplog) pclog("FPREM %f %f ", ST(0), ST(1)); temp64=(int64_t)(ST(0)/ST(1)); ST(0)=ST(0)-(ST(1)*(double)temp64); if (fplog) pclog("%f\n", ST(0)); npxs&=~(C0|C1|C2|C3); if (temp64&4) npxs|=C0; if (temp64&2) npxs|=C3; if (temp64&1) npxs|=C1; cycles-=100; return; case 0xFA: /*FSQRT*/ if (fplog) pclog("FSQRT\n"); ST(0)=sqrt(ST(0)); cycles-=83; return; case 0xFB: /*FSINCOS*/ if (fplog) pclog("FSINCOS\n"); td=ST(0); ST(0)=sin(td); x87_push(cos(td)); npxs&=~C2; cycles-=330; return; case 0xFC: /*FRNDINT*/ if (fplog) pclog("FRNDINT %g ", ST(0)); // pclog("FRNDINT %f %i ",ST(0),(npxc>>10)&3); ST(0)=(double)x87_fround(ST(0)); // pclog("%f\n",ST(0)); if (fplog) pclog("%g\n", ST(0)); cycles-=21; return; case 0xFD: /*FSCALE*/ if (fplog) pclog("FSCALE\n"); temp64=(int64_t)ST(1); ST(0)=ST(0)*pow(2.0,(double)temp64); cycles-=30; return; case 0xFE: /*FSIN*/ if (fplog) pclog("FSIN\n"); ST(0)=sin(ST(0)); npxs&=~C2; cycles-=300; return; case 0xFF: /*FCOS*/ if (fplog) pclog("FCOS\n"); ST(0)=cos(ST(0)); npxs&=~C2; cycles-=300; return; } } else { switch (reg) { case 0: /*FLD single-precision*/ if (fplog) pclog("FLDs %08X:%08X\n", easeg, eaaddr); // if ((rmdat32&0xFFFF)==0xD445) { pclog("FLDS\n"); output=3; dumpregs(); exit(-1); } ts.i=geteal(); if (abrt) { pclog("FLD sp abort\n"); return; } if (fplog) pclog(" %f\n", ts.s); x87_push((double)ts.s); cycles-=3; return; case 2: /*FST single-precision*/ if (fplog) pclog("FSTs %08X:%08X\n", easeg, eaaddr); ts.s=(float)ST(0); seteal(ts.i); if (abrt) { pclog("FST sp abort\n"); return; } cycles-=7; return; case 3: /*FSTP single-precision*/ if (fplog) pclog("FSTPs %08X:%08X %f\n", easeg, eaaddr, ST(0)); ts.s=(float)ST(0); seteal(ts.i); if (abrt) { pclog("FSTP sp abort\n"); return; } // if (pc==0x3f50c) pclog("FSTP %f %f %08X\n",ST(0),ts.s,ts.i); x87_pop(); cycles-=7; return; case 4: /*FLDENV*/ if (fplog) pclog("FLDENV %08X:%08X\n", easeg, eaaddr); switch ((cr0&1)|(op32&0x100)) { case 0x000: /*16-bit real mode*/ case 0x001: /*16-bit protected mode*/ npxc=readmemw(easeg,eaaddr); npxs=readmemw(easeg,eaaddr+2); tag=readmemw(easeg,eaaddr+4); TOP=(npxs>>11)&7; break; case 0x100: /*32-bit real mode*/ case 0x101: /*32-bit protected mode*/ npxc=readmemw(easeg,eaaddr); npxs=readmemw(easeg,eaaddr+4); tag=readmemw(easeg,eaaddr+8); TOP=(npxs>>11)&7; break; } cycles-=(cr0&1)?34:44; if (abrt) { pclog("FLDENV\n"); return; } return; case 5: /*FLDCW*/ if (fplog) pclog("FLDCW %08X:%08X\n", easeg, eaaddr); tempw=geteaw(); if (abrt) if (abrt) { pclog("FLDCW abort\n"); return; } npxc=tempw; cycles-=4; return; case 6: /*FSTENV*/ if (fplog) pclog("FSTENV %08X:%08X\n", easeg, eaaddr); switch ((cr0&1)|(op32&0x100)) { case 0x000: /*16-bit real mode*/ writememw(easeg,eaaddr,npxc); writememw(easeg,eaaddr+2,npxs); writememw(easeg,eaaddr+4,tag); writememw(easeg,eaaddr+6,x87_pc_off); writememw(easeg,eaaddr+10,x87_op_off); break; case 0x001: /*16-bit protected mode*/ writememw(easeg,eaaddr,npxc); writememw(easeg,eaaddr+2,npxs); writememw(easeg,eaaddr+4,tag); writememw(easeg,eaaddr+6,x87_pc_off); writememw(easeg,eaaddr+8,x87_pc_seg); writememw(easeg,eaaddr+10,x87_op_off); writememw(easeg,eaaddr+12,x87_op_seg); break; case 0x100: /*32-bit real mode*/ writememw(easeg,eaaddr,npxc); writememw(easeg,eaaddr+4,npxs); writememw(easeg,eaaddr+8,tag); writememw(easeg,eaaddr+12,x87_pc_off); writememw(easeg,eaaddr+20,x87_op_off); writememl(easeg,eaaddr+24,(x87_op_off>>16)<<12); break; case 0x101: /*32-bit protected mode*/ writememw(easeg,eaaddr,npxc); writememw(easeg,eaaddr+4,npxs); writememw(easeg,eaaddr+8,tag); writememl(easeg,eaaddr+12,x87_pc_off); writememl(easeg,eaaddr+16,x87_pc_seg); writememl(easeg,eaaddr+20,x87_op_off); writememl(easeg,eaaddr+24,x87_op_seg); break; } if (abrt) { pclog("FSTENV\n"); return; } cycles-=(cr0&1)?56:67; return; case 7: /*FSTCW*/ if (fplog) pclog("FSTCW %08X:%08X\n", easeg, eaaddr); seteaw(npxc); if (abrt) { pclog("FSTCW\n"); return; } cycles-=3; return; } } // fatal("Bad x87 D9 opcode %i %02X\n",reg,rmdat32&0xFF); x86illegal(); } void x87_da() { int32_t templ; if (mod==3) { switch (rmdat32&0xFF) { case 0xE9: /*FUCOMPP*/ if (fplog) pclog("FUCOMPP\n", easeg, eaaddr); npxs&=~(C0|C2|C3); if (ST(0)==ST(1)) npxs|=C3; else if (ST(0) 2147483647 || temp64 < -2147483647) fatal("FISTl out of range! %i\n", temp64);*/ seteal((int32_t)temp64); cycles-=28; return; case 3: /*FISTP short*/ if (fplog) pclog("FISTPs %08X:%08X %g ", easeg, eaaddr, ST(0)); temp64 = x87_fround(ST(0)); /* if (temp64 > 2147483647 || temp64 < -2147483647) fatal("FISTPl out of range! %i\n", temp64);*/ if (fplog) pclog("%lli %llX\n", temp64, temp64); seteal((int32_t)temp64); if (abrt) return; x87_pop(); cycles-=28; return; case 5: /*FLD extended*/ if (fplog) pclog("FLDe %08X:%08X\n", easeg, eaaddr); t=x87_ld80(); if (abrt) return; if (fplog) pclog(" %f\n", t); x87_push(t); cycles-=6; return; case 7: /*FSTP extended*/ if (fplog) pclog("FSTPe %08X:%08X\n", easeg, eaaddr); x87_st80(ST(0)); if (abrt) return; x87_pop(); cycles-=6; return; } } // fatal("Bad x87 DB opcode %i %02X\n",reg,rmdat32&0xFF); x86illegal(); } void x87_dc() { union { double d; uint64_t i; } t; if (mod==3) { switch (rmdat32&0xFF) { case 0xC0: case 0xC1: case 0xC2: case 0xC3: /*FADD*/ case 0xC4: case 0xC5: case 0xC6: case 0xC7: if (fplog) pclog("FADD %f %f\n", ST(rmdat32 & 7), ST(0)); ST(rmdat32&7)=ST(rmdat32&7)+ST(0); cycles-=8; return; case 0xC8: case 0xC9: case 0xCA: case 0xCB: /*FMUL*/ case 0xCC: case 0xCD: case 0xCE: case 0xCF: if (fplog) pclog("FMUL %f %f\n", ST(rmdat32 & 7), ST(0)); ST(rmdat32&7)=ST(rmdat32&7)*ST(0); cycles-=16; return; case 0xE0: case 0xE1: case 0xE2: case 0xE3: /*FSUBR*/ case 0xE4: case 0xE5: case 0xE6: case 0xE7: if (fplog) pclog("FSUBR %f %f\n", ST(rmdat32 & 7), ST(0)); ST(rmdat32&7)=ST(0)-ST(rmdat32&7); cycles-=8; return; case 0xE8: case 0xE9: case 0xEA: case 0xEB: /*FSUB*/ case 0xEC: case 0xED: case 0xEE: case 0xEF: if (fplog) pclog("FSUB %f %f\n", ST(rmdat32 & 7), ST(0)); ST(rmdat32&7)=ST(rmdat32&7)-ST(0); cycles-=8; return; case 0xF0: case 0xF1: case 0xF2: case 0xF3: /*FDIVR*/ case 0xF4: case 0xF5: case 0xF6: case 0xF7: if (fplog) pclog("FDIVR %f %f\n", ST(rmdat32 & 7), ST(0)); x87_div(ST(rmdat32&7), ST(0), ST(rmdat32&7)); cycles-=73; return; case 0xF8: case 0xF9: case 0xFA: case 0xFB: /*FDIV*/ case 0xFC: case 0xFD: case 0xFE: case 0xFF: if (fplog) pclog("FDIV %f %f\n", ST(rmdat32 & 7), ST(0)); x87_div(ST(rmdat32&7), ST(rmdat32&7), ST(0)); cycles-=73; return; } } else { t.i=readmeml(easeg,eaaddr); t.i|=(uint64_t)readmeml(easeg,eaaddr+4)<<32; if (abrt) return; switch (reg) { case 0: /*FADD double*/ if (fplog) pclog("FADDd %08X:%08X %f %f\n", easeg, eaaddr, ST(0), t.d); ST(0)+=t.d; cycles-=8; return; case 1: /*FMUL double*/ if (fplog) pclog("FMULd %08X:%08X %f %f\n", easeg, eaaddr, ST(0), t.d); ST(0)*=t.d; cycles-=14; return; case 2: /*FCOM double*/ if (fplog) pclog("FCOMd %08X:%08X %f %f\n", easeg, eaaddr, ST(0), t.d); npxs&=~(C0|C2|C3); if (ST(0)==t.d) npxs|=C3; else if (ST(0)> ((TOP & 7) << 1)) & 3; tag &= ~(3 << (((TOP + rmdat32) & 7) << 1)); tag |= (temp << (((TOP + rmdat32) & 7) << 1)); cycles-=3; return; case 3: /*FSTP*/ if (fplog) pclog("FSTP\n"); ST(rmdat32 & 7) = ST(0); temp = (tag >> ((TOP & 7) << 1)) & 3; tag &= ~(3 << (((TOP + rmdat32) & 7) << 1)); tag |= (temp << (((TOP + rmdat32) & 7) << 1)); x87_pop(); cycles-=3; return; case 4: /*FUCOM*/ if (fplog) pclog("FUCOM\n"); npxs&=~(C0|C2|C3); if (ST(0)==ST(rmdat32&7)) npxs|=C3; else if (ST(0)>32)); cycles-=8; return; case 3: /*FSTP double-precision*/ if (fplog) pclog("FSTPd %08X:%08X\n", easeg, eaaddr); t.d=ST(0); writememl(easeg,eaaddr,t.i); writememl(easeg,eaaddr+4,(t.i>>32)); if (abrt) return; x87_pop(); cycles-=8; return; case 4: /*FRSTOR*/ switch ((cr0&1)|(op32&0x100)) { case 0x000: /*16-bit real mode*/ case 0x001: /*16-bit protected mode*/ npxc=readmemw(easeg,eaaddr); npxs=readmemw(easeg,eaaddr+2); tag=readmemw(easeg,eaaddr+4); TOP=(npxs>>11)&7; eaaddr+=14; break; case 0x100: /*32-bit real mode*/ case 0x101: /*32-bit protected mode*/ npxc=readmemw(easeg,eaaddr); npxs=readmemw(easeg,eaaddr+4); tag=readmemw(easeg,eaaddr+8); TOP=(npxs>>11)&7; eaaddr+=28; break; } x87_ldmmx(&MM[0]); ST(0)=x87_ld80(); eaaddr += 10; x87_ldmmx(&MM[1]); ST(1)=x87_ld80(); eaaddr += 10; x87_ldmmx(&MM[2]); ST(2)=x87_ld80(); eaaddr += 10; x87_ldmmx(&MM[3]); ST(3)=x87_ld80(); eaaddr += 10; x87_ldmmx(&MM[4]); ST(4)=x87_ld80(); eaaddr += 10; x87_ldmmx(&MM[5]); ST(5)=x87_ld80(); eaaddr += 10; x87_ldmmx(&MM[6]); ST(6)=x87_ld80(); eaaddr += 10; x87_ldmmx(&MM[7]); ST(7)=x87_ld80(); ismmx = 0; /*Horrible hack, but as PCem doesn't keep the FPU stack in 80-bit precision at all times something like this is needed*/ if (MM[0].w == 0xffff && MM[1].w == 0xffff && MM[2].w == 0xffff && MM[3].w == 0xffff && MM[4].w == 0xffff && MM[5].w == 0xffff && MM[6].w == 0xffff && MM[7].w == 0xffff && !TOP && !tag) ismmx = 1; cycles-=(cr0&1)?34:44; if (fplog) pclog("FRSTOR %08X:%08X %i %i %04X\n", easeg, eaaddr, ismmx, TOP, tag); /* pclog("new TOP %i\n", TOP); pclog("%04X %04X %04X %f %f %f %f %f %f %f %f\n", npxc, npxs, tag, ST(0), ST(1), ST(2), ST(3), ST(4), ST(5), ST(6), ST(7));*/ return; case 6: /*FSAVE*/ if (fplog) pclog("FSAVE %08X:%08X %i\n", easeg, eaaddr, ismmx); npxs = (npxs & ~(7 << 11)) | (TOP << 11); /* pclog("old TOP %i %04X\n", TOP, npxs); pclog("%04X %04X %04X %f %f %f %f %f %f %f %f\n", npxc, npxs, tag, ST(0), ST(1), ST(2), ST(3), ST(4), ST(5), ST(6), ST(7));*/ switch ((cr0&1)|(op32&0x100)) { case 0x000: /*16-bit real mode*/ writememw(easeg,eaaddr,npxc); writememw(easeg,eaaddr+2,npxs); writememw(easeg,eaaddr+4,tag); writememw(easeg,eaaddr+6,x87_pc_off); writememw(easeg,eaaddr+10,x87_op_off); eaaddr+=14; if (ismmx) { x87_stmmx(MM[0]); eaaddr+=10; x87_stmmx(MM[1]); eaaddr+=10; x87_stmmx(MM[2]); eaaddr+=10; x87_stmmx(MM[3]); eaaddr+=10; x87_stmmx(MM[4]); eaaddr+=10; x87_stmmx(MM[5]); eaaddr+=10; x87_stmmx(MM[6]); eaaddr+=10; x87_stmmx(MM[7]); } else { x87_st80(ST(0)); eaaddr+=10; x87_st80(ST(1)); eaaddr+=10; x87_st80(ST(2)); eaaddr+=10; x87_st80(ST(3)); eaaddr+=10; x87_st80(ST(4)); eaaddr+=10; x87_st80(ST(5)); eaaddr+=10; x87_st80(ST(6)); eaaddr+=10; x87_st80(ST(7)); } break; case 0x001: /*16-bit protected mode*/ writememw(easeg,eaaddr,npxc); writememw(easeg,eaaddr+2,npxs); writememw(easeg,eaaddr+4,tag); writememw(easeg,eaaddr+6,x87_pc_off); writememw(easeg,eaaddr+8,x87_pc_seg); writememw(easeg,eaaddr+10,x87_op_off); writememw(easeg,eaaddr+12,x87_op_seg); eaaddr+=14; if (ismmx) { x87_stmmx(MM[0]); eaaddr+=10; x87_stmmx(MM[1]); eaaddr+=10; x87_stmmx(MM[2]); eaaddr+=10; x87_stmmx(MM[3]); eaaddr+=10; x87_stmmx(MM[4]); eaaddr+=10; x87_stmmx(MM[5]); eaaddr+=10; x87_stmmx(MM[6]); eaaddr+=10; x87_stmmx(MM[7]); } else { x87_st80(ST(0)); eaaddr+=10; x87_st80(ST(1)); eaaddr+=10; x87_st80(ST(2)); eaaddr+=10; x87_st80(ST(3)); eaaddr+=10; x87_st80(ST(4)); eaaddr+=10; x87_st80(ST(5)); eaaddr+=10; x87_st80(ST(6)); eaaddr+=10; x87_st80(ST(7)); } break; case 0x100: /*32-bit real mode*/ writememw(easeg,eaaddr,npxc); writememw(easeg,eaaddr+4,npxs); writememw(easeg,eaaddr+8,tag); writememw(easeg,eaaddr+12,x87_pc_off); writememw(easeg,eaaddr+20,x87_op_off); writememl(easeg,eaaddr+24,(x87_op_off>>16)<<12); eaaddr+=28; if (ismmx) { x87_stmmx(MM[0]); eaaddr+=10; x87_stmmx(MM[1]); eaaddr+=10; x87_stmmx(MM[2]); eaaddr+=10; x87_stmmx(MM[3]); eaaddr+=10; x87_stmmx(MM[4]); eaaddr+=10; x87_stmmx(MM[5]); eaaddr+=10; x87_stmmx(MM[6]); eaaddr+=10; x87_stmmx(MM[7]); } else { x87_st80(ST(0)); eaaddr+=10; x87_st80(ST(1)); eaaddr+=10; x87_st80(ST(2)); eaaddr+=10; x87_st80(ST(3)); eaaddr+=10; x87_st80(ST(4)); eaaddr+=10; x87_st80(ST(5)); eaaddr+=10; x87_st80(ST(6)); eaaddr+=10; x87_st80(ST(7)); } break; case 0x101: /*32-bit protected mode*/ writememw(easeg,eaaddr,npxc); writememw(easeg,eaaddr+4,npxs); writememw(easeg,eaaddr+8,tag); writememl(easeg,eaaddr+12,x87_pc_off); writememl(easeg,eaaddr+16,x87_pc_seg); writememl(easeg,eaaddr+20,x87_op_off); writememl(easeg,eaaddr+24,x87_op_seg); eaaddr+=28; if (ismmx) { x87_stmmx(MM[0]); eaaddr+=10; x87_stmmx(MM[1]); eaaddr+=10; x87_stmmx(MM[2]); eaaddr+=10; x87_stmmx(MM[3]); eaaddr+=10; x87_stmmx(MM[4]); eaaddr+=10; x87_stmmx(MM[5]); eaaddr+=10; x87_stmmx(MM[6]); eaaddr+=10; x87_stmmx(MM[7]); } else { x87_st80(ST(0)); eaaddr+=10; x87_st80(ST(1)); eaaddr+=10; x87_st80(ST(2)); eaaddr+=10; x87_st80(ST(3)); eaaddr+=10; x87_st80(ST(4)); eaaddr+=10; x87_st80(ST(5)); eaaddr+=10; x87_st80(ST(6)); eaaddr+=10; x87_st80(ST(7)); } break; } cycles-=(cr0&1)?56:67; return; case 7: /*FSTSW*/ if (fplog) pclog("FSTSW %08X:%08X\n", easeg, eaaddr); seteaw((npxs&0xC7FF)|(TOP<<11)); cycles-=3; return; } } // fatal("Bad x87 DD opcode %i %02X\n",reg,rmdat32&0xFF); x86illegal(); } void x87_de() { int32_t templ; if (mod==3) { switch (rmdat32&0xFF) { case 0xC0: case 0xC1: case 0xC2: case 0xC3: /*FADDP*/ case 0xC4: case 0xC5: case 0xC6: case 0xC7: if (fplog) pclog("FADDP %f %f\n", ST(rmdat32 & 7), ST(0)); ST(rmdat32&7)=ST(rmdat32&7)+ST(0); x87_pop(); cycles-=8; return; case 0xC8: case 0xC9: case 0xCA: case 0xCB: /*FMULP*/ case 0xCC: case 0xCD: case 0xCE: case 0xCF: if (fplog) pclog("FMULP %f %f\n", ST(rmdat32 & 7), ST(0)); ST(rmdat32&7)=ST(rmdat32&7)*ST(0); x87_pop(); cycles-=16; return; case 0xD9: /*FCOMPP*/ if (fplog) pclog("FCOMPP %f %f\n", ST(0), ST(1)); npxs&=~(C0|C2|C3); if (*(uint64_t *)&ST(0) == ((uint64_t)1 << 63) && *(uint64_t *)&ST(1) == 0) npxs |= C0; /*Nasty hack to fix 80387 detection*/ else if (ST(0) == ST(1)) npxs |= C3; else if (ST(0) < ST(1)) npxs |= C0; x87_pop(); x87_pop(); cycles-=5; return; case 0xE0: case 0xE1: case 0xE2: case 0xE3: /*FSUBRP*/ case 0xE4: case 0xE5: case 0xE6: case 0xE7: if (fplog) pclog("FSUBRP %f %f\n", ST(rmdat32 & 7), ST(0)); ST(rmdat32&7)=ST(0)-ST(rmdat32&7); x87_pop(); cycles-=8; return; case 0xE8: case 0xE9: case 0xEA: case 0xEB: /*FSUBP*/ case 0xEC: case 0xED: case 0xEE: case 0xEF: if (fplog) pclog("FSUBP %f %f\n", ST(rmdat32 & 7), ST(0)); ST(rmdat32&7)=ST(rmdat32&7)-ST(0); x87_pop(); cycles-=8; return; case 0xF0: case 0xF1: case 0xF2: case 0xF3: /*FDIVRP*/ case 0xF4: case 0xF5: case 0xF6: case 0xF7: if (fplog) pclog("FDIVRP %f %f\n", ST(rmdat32 & 7), ST(0)); x87_div(ST(rmdat32&7), ST(0), ST(rmdat32&7)); x87_pop(); cycles-=73; return; case 0xF8: case 0xF9: case 0xFA: case 0xFB: /*FDIVP*/ case 0xFC: case 0xFD: case 0xFE: case 0xFF: if (fplog) pclog("FDIVP %f %f %i\n", ST(rmdat32 & 7), ST(0), rmdat32 & 7); x87_div(ST(rmdat32&7), ST(rmdat32&7), ST(0)); x87_pop(); cycles-=73; return; } } else { templ=(int32_t)(int16_t)geteaw(); if (abrt) return; switch (reg) { case 0: /*FIADD*/ if (fplog) pclog("FIADDw %08X:%08X %f %f\n", easeg, eaaddr, ST(0), (double)templ); ST(0)=ST(0)+(double)templ; cycles-=20; return; case 1: /*FIMUL*/ if (fplog) pclog("FIMULw %08X:%08X %f %f\n", easeg, eaaddr, ST(0), (double)templ); ST(0)=ST(0)*(double)templ; cycles-=22; return; case 2: /*FICOM*/ if (fplog) pclog("FICOMw %08X:%08X %f %f\n", easeg, eaaddr, ST(0), (double)templ); npxs&=~(C0|C2|C3); if (ST(0)==(double)templ) npxs|=C3; else if (ST(0)<(double)templ) npxs|=C0; cycles-=15; return; case 3: /*FICOMP*/ if (fplog) pclog("FICOMPw %08X:%08X %f %f\n", easeg, eaaddr, ST(0), (double)templ); npxs&=~(C0|C2|C3); if (ST(0)==(double)templ) npxs|=C3; else if (ST(0)<(double)templ) npxs|=C0; x87_pop(); cycles-=15; return; case 4: /*FISUB*/ if (fplog) pclog("FISUBw %08X:%08X %f %f\n", easeg, eaaddr, ST(0), (double)templ); ST(0)=ST(0)-(double)templ; cycles-=20; return; case 5: /*FISUBR*/ if (fplog) pclog("FISUBRw %08X:%08X %f %f\n", easeg, eaaddr, ST(0), (double)templ); ST(0)=(double)templ-ST(0); cycles-=19; return; case 6: /*FIDIV*/ if (fplog) pclog("FIDIVw %08X:%08X %f %f\n", easeg, eaaddr, ST(0), (double)templ); x87_div(ST(0), ST(0), (double)templ); cycles-=84; return; case 7: /*FIDIVR*/ if (fplog) pclog("FIDIVRw %08X:%08X %f %f\n", easeg, eaaddr, ST(0), (double)templ); x87_div(ST(0), (double)templ, ST(0)); cycles-=84; return; } } // fatal("Bad x87 DE opcode %i %02X\n",reg,rmdat32&0xFF); x86illegal(); } void x87_df() { int c; int64_t temp64; int16_t temp16; double tempd; uint8_t tempc; if (mod==3) { switch (reg) { case 4: switch (rmdat32&0xFF) { case 0xE0: /*FSTSW AX*/ if (fplog) pclog("FSTSW\n"); AX=npxs; cycles-=3; return; } break; } } else { switch (reg) { case 0: /*FILD short*/ if (fplog) pclog("FILDw %08X:%08X\n", easeg, eaaddr); temp16=geteaw(); if (abrt) return; if (fplog) pclog(" %f\n", (double)temp16); x87_push((double)temp16); cycles-=13; return; case 2: /*FIST word*/ if (fplog) pclog("FISTw %08X:%08X\n", easeg, eaaddr); temp64 = x87_fround(ST(0)); /* if (temp64 > 32767 || temp64 < -32768) fatal("FISTw overflow %i\n", temp64);*/ seteaw((int16_t)temp64); cycles-=29; return; case 3: /*FISTP word*/ if (fplog) pclog("FISTPw %08X:%08X\n", easeg, eaaddr); temp64 = x87_fround(ST(0)); /* if (temp64 > 32767 || temp64 < -32768) fatal("FISTw overflow %i\n", temp64);*/ seteaw((int16_t)temp64); if (abrt) return; x87_pop(); cycles-=29; return; case 5: /*FILD long*/ if (fplog) pclog("FILDl %08X:%08X\n", easeg, eaaddr); temp64=geteal(); if (abrt) return; temp64|=(uint64_t)readmeml(easeg,eaaddr+4)<<32; if (fplog) pclog(" %f %08X %08X\n", (double)temp64, readmeml(easeg,eaaddr), readmeml(easeg,eaaddr+4)); x87_push((double)temp64); cycles-=10; return; case 6: /*FBSTP*/ if (fplog) pclog("FBSTP %08X:%08X\n", easeg, eaaddr); tempd=ST(0); if (tempd < 0.0) tempd = -tempd; for (c=0;c<9;c++) { tempc=(uint8_t)floor(fmod(tempd,10.0)); tempd-=floor(fmod(tempd,10.0)); tempd/=10.0; tempc|=((uint8_t)floor(fmod(tempd,10.0)))<<4; tempd-=floor(fmod(tempd,10.0)); tempd/=10.0; writememb(easeg,eaaddr+c,tempc); } tempc=(uint8_t)floor(fmod(tempd,10.0)); if (ST(0)<0.0) tempc|=0x80; writememb(easeg,eaaddr+9,tempc); if (abrt) return; x87_pop(); return; case 7: /*FISTP long*/ if (fplog) pclog("FISTPl %08X:%08X\n", easeg, eaaddr); temp64=x87_fround(ST(0)); seteal(temp64); writememl(easeg,eaaddr+4,temp64>>32); if (abrt) return; x87_pop(); cycles-=29; return; } } // fatal("Bad x87 DF opcode %i %02X\n",reg,rmdat32&0xFF); x86illegal(); }