/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #include #include #include #include extern char __libemu_ident_string[]; static char *id = __libemu_ident_string; #define EXP_BIAS 16383 #define EXP_MAX 32766 #define SIGN_POS 0 #define SIGN_NEG 1 #define TW_V 0 // valid #define TW_Z 1 // zero #define TW_S 2 // special #define TW_E 3 // empty #define EX_SO 0x02C1 // stack overflow #define EX_SU 0x00C1 // stack underflow #define EX_P 0x00A0 // loss of precision #define EX_U 0x0090 // underflow #define EX_O 0x0088 // overflow #define EX_Z 0x0084 // divide by zero #define EX_D 0x0082 // denormalized operand #define EX_I 0x0081 // invalid operation #define SW_B 0x8000 // backward compatibility (=ES) #define SW_C3 0x4000 // condition bit 3 #define SW_TOP 0x3800 // top of stack #define SW_TOPS 0x0800 // scale for TOS #define SW_C2 0x0400 // condition bit 2 #define SW_C1 0x0200 // condition bit 1 #define SW_C0 0x0100 // condition bit 0 #define SW_ES 0x0080 // exception summary #define SW_SF 0x0040 // stack fault #define SW_PE 0x0020 // loss of precision #define SW_UE 0x0010 // underflow #define SW_OE 0x0008 // overflow #define SW_ZE 0x0004 // divide by zero #define SW_DE 0x0002 // denormalized operand #define SW_IE 0x0001 // invalid operation #define CW_RC 0x0C00 // rounding control #define CW_PC 0x0300 // precision control #define CW_PM 0x0020 // precision mask #define CW_UM 0x0010 // underflow mask #define CW_OM 0x0008 // overflow mask #define CW_ZM 0x0004 // divide by zero mask #define CW_DM 0x0002 // denormalized operand mask #define CW_IM 0x0001 // invalid operation mask #define CW_EXM 0x007f // all masks #define RC_RND 0x0000 #define RC_DOWN 0x0400 #define RC_UP 0x0800 #define RC_CHOP 0x0C00 #define COMP_A_GT_B 1 #define COMP_A_EQ_B 2 #define COMP_A_LT_B 3 #define COMP_NOCOMP 4 #define COMP_NAN 0x40 #define COMP_SNAN 0x80 #define NAN_NONE 0 #define NAN_SNAN 1 #define NAN_QNAN 2 struct reg { char sign; char tag; short exp; unsigned sigl; unsigned sigh; }; typedef void (*FUNC)(); static int modrm; static unsigned eax; static unsigned ebx; static unsigned ecx; static unsigned edx; static unsigned esi; static unsigned edi; static unsigned ebp; static unsigned esp; static unsigned char *eip; static jmp_buf jumpbuf; static int status_word = 0; static int control_word = 0x77e; static reg regs[8] = { { SIGN_POS, TW_E, 0, 0x0, 0x0 }, { SIGN_POS, TW_E, 0, 0x0, 0x0 }, { SIGN_POS, TW_E, 0, 0x0, 0x0 }, { SIGN_POS, TW_E, 0, 0x0, 0x0 }, { SIGN_POS, TW_E, 0, 0x0, 0x0 }, { SIGN_POS, TW_E, 0, 0x0, 0x0 }, { SIGN_POS, TW_E, 0, 0x0, 0x0 }, { SIGN_POS, TW_E, 0, 0x0, 0x0 }, }; static int top = 0; static reg CONST_1 = { SIGN_POS, TW_V, EXP_BIAS, 0x00000000, 0x80000000 }; static reg CONST_L2T = { SIGN_POS, TW_V, EXP_BIAS+1, 0xcd1b8afe, 0xd49a784b }; static reg CONST_L2E = { SIGN_POS, TW_V, EXP_BIAS, 0x5c17f0bc, 0xb8aa3b29 }; static reg CONST_PI = { SIGN_POS, TW_V, EXP_BIAS+1, 0x2168c235, 0xc90fdaa2 }; static reg CONST_PI2 = { SIGN_POS, TW_V, EXP_BIAS, 0x2168c235, 0xc90fdaa2 }; static reg CONST_LG2 = { SIGN_POS, TW_V, EXP_BIAS-2, 0xfbcff799, 0x9a209a84 }; static reg CONST_LN2 = { SIGN_POS, TW_V, EXP_BIAS-1, 0xd1cf79ac, 0xb17217f7 }; static reg CONST_Z = { SIGN_POS, TW_Z, 0, 0x0, 0x0 }; static reg CONST_PINF = { SIGN_POS, TW_S, EXP_MAX+1, 0x00000000, 0x80000000 }; static reg CONST_NINF = { SIGN_NEG, TW_S, EXP_MAX+1, 0x00000000, 0x80000000 }; static reg CONST_NAN = { SIGN_NEG, TW_S, EXP_MAX+1, 0x00000000, 0xC0000000 }; inline reg& st(int which=0) { return regs[(top+which)&7]; } extern "C" _write(int,void*,int); static inline int val_same(reg& a, reg& b) { if (a.sign != b.sign || a.exp != b.exp || a.sigl != b.sigl || a.sigh != b.sigh) return 0; return 1; } static inline int is_zero(reg a) { return (a.exp == 0 && a.sigl == 0 && a.sigh == 0); } #ifndef eprintf static void eprintf(const char *f, ...) { char buf[1000]; vsprintf(buf, f, (&f)+1); _write(1, buf, strlen(buf)); } #endif /* extern "C" void djshld(void *); */ #define djshld(q) ((*(long long unsigned *)(q)) <<= 1) #define djshrd(q) ((*(long long unsigned *)(q)) >>= 1) static int nan_type(reg& r) { if (r.exp != EXP_MAX+1) return NAN_NONE; if (r.sigh & 0x40000000) return NAN_QNAN; return NAN_SNAN; } static int compare(reg& a, reg& b) { int a_inf, b_inf; // 0=no, 1=pos, -1=neg a_inf = 0; if (val_same(a, CONST_PINF)) a_inf = 1; else if (val_same(a, CONST_NINF)) a_inf = -1; b_inf = 0; if (val_same(b, CONST_PINF)) b_inf = 1; else if (val_same(b, CONST_NINF)) b_inf = -1; if (a_inf || b_inf) { if (a_inf == 1) if (b_inf == 1) return COMP_A_EQ_B; else return COMP_A_GT_B; if (b_inf == 1) return COMP_A_LT_B; if (a_inf == -1) if (b_inf == -1) return COMP_A_EQ_B; else return COMP_A_LT_B; if (b_inf == -1) return COMP_A_GT_B; } int a_nan = nan_type(a); int b_nan = nan_type(b); if (a_nan || b_nan) { if ((a_nan == NAN_SNAN) || (b_nan == NAN_SNAN)) return COMP_NOCOMP | COMP_SNAN | COMP_NAN; return COMP_NOCOMP | COMP_NAN; } if (a.sign != b.sign) { if ((a.tag == TW_Z) && (b.tag == TW_Z)) return COMP_A_EQ_B; if (a.sign == SIGN_POS) return COMP_A_GT_B; return COMP_A_LT_B; } while (!(a.sigh & 0x80000000)) { if (!a.exp) break; djshld(&a.sigl); a.exp--; } while (!(b.sigh & 0x80000000)) { if (!b.exp) break; djshld(&b.sigl); b.exp--; } int diff = a.exp - b.exp; if (diff == 0) diff = a.sigh - b.sigh; if (diff == 0) diff = a.sigl - b.sigl; if (a.sign == SIGN_NEG) diff = -diff; if (diff > 0) return COMP_A_GT_B; if (diff < 0) return COMP_A_LT_B; return COMP_A_EQ_B; } static void emu_printall() { static const char *tag_desc[] = { "Valid", "Zero", "Special", "Empty" }; status_word = status_word & ~SW_TOP; status_word += (top&7) * SW_TOPS; eprintf(" SW: 0x%04x top=%d cc=%d%d%d%d ", status_word, top&7, status_word & SW_C3?1:0, status_word & SW_C2?1:0, status_word & SW_C1?1:0, status_word & SW_C0?1:0); eprintf("CW: 0x%04x\r\n", control_word); for (int i=0; i<8; i++) { reg *r = &st(i); switch (r->tag) { case TW_E: continue; eprintf("st(%d) ", i); break; case TW_Z: eprintf("st(%d) %c .0000 0000 0000 0000 ", i, r->sign ? '-' : '+'); break; case TW_S: case TW_V: eprintf("st(%d) %c .%04x %04x %04x %04x e%+-6d ", i, r->sign ? '-' : '+', (long)(r->sigh >> 16), (long)(r->sigh & 0xFFFF), (long)(r->sigl >> 16), (long)(r->sigl & 0xFFFF), r->exp - EXP_BIAS + 1); } eprintf("%s\r\n", tag_desc[r->tag]); } } static struct { int type; const char *name; } ex_names[] = { { EX_SO, "stack overflow" }, { EX_SU, "stack underflow" }, { EX_P, "loss of precision" }, { EX_U, "underflow" }, { EX_O, "overflow" }, { EX_Z, "divide by zero" }, { EX_D, "denormalized operand" }, { EX_I, "invalid operation" }, { 0,0 } }; static void exception(int n) { int i; status_word |= n; if (n == EX_SU) status_word &= ~SW_C1; for (i=0; ex_names[i].type; i++) if (ex_names[i].type == n) break; if (~control_word & n & CW_EXM) { if (ex_names[i].type) eprintf("80387 Exception: %s!\r\n", ex_names[i].name); else eprintf("80387 Exception: 0x%04x!\r\n", n); emu_printall(); longjmp(jumpbuf,1); } } static void emu_bad() { eprintf("Unimplemented 80387 Opcode at eip=0x%08x : %02x", eip-2, eip[-2]); if (eip[-1] > 0277) eprintf(" %02x", eip[-1]); else eprintf(" /%d", (eip[-1]>>3)&7); eprintf(" - e%d%d", eip[-2]&7, (eip[-1]>>3)&7); if (eip[-1] > 0277) eprintf(" s%d", eip[-1]&7); eprintf("\r\n"); exception(EX_I); } static void setcc(int cc) { status_word &= ~(SW_C0|SW_C1|SW_C2|SW_C3); status_word |= cc & (SW_C0|SW_C1|SW_C2|SW_C3); } static int full() { if (st(7).tag != TW_E) { exception(EX_SO); top--; st(0) = CONST_NAN; return 1; } return 0; } static int empty(int i=0) { if (st(i).tag == TW_E) { exception(EX_SU); return 1; } return 0; } static int sregval(int reg1, int mod) { switch (reg1) { case 0: return eax; case 1: return ecx; case 2: return edx; case 3: return ebx; case 4: return (mod==-1) ? 0 : esp; case 5: return mod ? ebp : 0; // data case 6: return esi; case 7: return edi; } return 0; } static int scale[] = { 1, 2, 4, 8 }; static int getsib() { int mod = modrm >> 6; int sib = *eip++; int ss = sib>>6; int index = (sib>>3) & 7; int base = sib & 7; int rv = sregval(base, mod) + sregval(index, -1) * scale[ss]; int rv2; switch (mod) { case 1: rv2 = *(signed char *)eip++; rv += rv2; break; case 0: if (base != 5) break; case 2: ((unsigned char *)&rv2)[0] = *eip++; ((unsigned char *)&rv2)[1] = *eip++; ((unsigned char *)&rv2)[2] = *eip++; ((unsigned char *)&rv2)[3] = *eip++; rv += rv2; break; } return rv; } static int regval(int reg1, int mod) { switch (reg1) { case 0: return eax; case 1: return ecx; case 2: return edx; case 3: return ebx; case 4: return getsib(); case 5: return mod ? ebp : 0; // data case 6: return esi; case 7: return edi; } return 0; } static void *get_modrm() { int mod = modrm>>6; int rm = modrm & 7; int rv; switch (mod) { case 0: if (rm == 5) { ((unsigned char *)&rv)[0] = *eip++; ((unsigned char *)&rv)[1] = *eip++; ((unsigned char *)&rv)[2] = *eip++; ((unsigned char *)&rv)[3] = *eip++; } else rv = regval(rm, mod); break; case 1: if (rm != 4) rv = (*(signed char *)eip++) + regval(rm, mod); else rv = regval(rm, mod); break; case 2: if (rm != 4) { ((unsigned char *)&rv)[0] = *eip++; ((unsigned char *)&rv)[1] = *eip++; ((unsigned char *)&rv)[2] = *eip++; ((unsigned char *)&rv)[3] = *eip++; rv += regval(rm, mod); } else rv = regval(rm, mod); break; case 3: eprintf("Attempt to get address from mod = 3\r\n"); longjmp(jumpbuf,1); } // eprintf("modrm returning 0x%x\r\n", rv); return (void *)rv; } static void r_uadd(reg& a, reg& b, reg& s) // signs ignored { reg t; int dif = a.exp - b.exp; if (!dif) dif = a.sigh - b.sigh; if (!dif) dif = a.sigl - b.sigl; if (dif > 0) { s = a; t = b; } else { s = b; t = a; } if (s.exp - t.exp > 64) return; while (t.exp < s.exp) { t.exp ++; djshrd(&t.sigl); } unsigned short *ss, *ts; unsigned long tmp; ss = (unsigned short *)&s.sigl; ts = (unsigned short *)&t.sigl; tmp = 0; for (int i=4; i>0; i--) { tmp += (unsigned long)*ss + (unsigned long)*ts; *ss = tmp; ss++; ts++; tmp >>= 16; } if (tmp) { djshrd(&s.sigl); s.exp++; s.sigh |= 0x80000000; } if (!(s.sigh | s.sigl)) { s.exp = 0; s.tag = TW_Z; } else { while (!(s.sigh & 0x80000000)) { if (s.exp == 0) return; djshld(&s.sigl); s.exp--; } } } static void r_usub(reg& a, reg& b, reg& d) // a > b { reg t; d = a; t = b; if (d.exp - t.exp > 64) return; while (t.exp < d.exp) { t.exp ++; djshrd(&t.sigl); } unsigned short *ss, *ts; long tmp; ss = (unsigned short *)&d.sigl; ts = (unsigned short *)&t.sigl; tmp = 0; for (int i=4; i>0; i--) { tmp += (long)*ss - (long)*ts; *ss = tmp; ss++; ts++; tmp >>= 16; } if (!(d.sigh | d.sigl)) { d.exp = 0; d.tag = TW_Z; } else { while (!(d.sigh & 0x80000000)) { if (d.exp == 0) return; djshld(&d.sigl); d.exp--; } } } static void r_sub(reg& a, reg& b, reg& d) { if (b.tag == TW_Z) { d = a; return; } if (a.tag == TW_Z) { d = b; d.sign ^= SIGN_POS^SIGN_NEG; return; } if (a.tag == TW_S) { d = a; return; } if (b.tag == TW_S) { d = b; d.sign ^= SIGN_POS^SIGN_NEG; return; } int mdif; mdif = a.exp - b.exp; if (!mdif) mdif = a.sigh - b.sigh; if (!mdif) mdif = a.sigl - b.sigl; switch (a.sign*2 + b.sign) { case 0: // P - P case 3: // N - N if (mdif > 0) { r_usub(a, b, d); d.sign = a.sign; } else { r_usub(b, a, d); d.sign = a.sign ^ SIGN_POS^SIGN_NEG; } break; case 1: // P - N r_uadd(a, b, d); d.sign = SIGN_POS; break; case 2: // N - P r_uadd(a, b, d); d.sign = SIGN_NEG; break; } } static void r_add(reg& a, reg& b, reg& s) { char old_sign; if (a.tag == TW_Z) { s = b; return; } if (b.tag == TW_Z) { s = a; return; } if (a.tag == TW_S) { s = a; return; } if (b.tag == TW_S) { s = b; return; } switch (a.sign*2 + b.sign) { case 0: // P + P case 3: // N + N r_uadd(a, b, s); s.sign = a.sign; break; case 1: // P + N old_sign = b.sign; b.sign ^= SIGN_POS^SIGN_NEG; r_sub(a, b, s); b.sign = old_sign; break; case 2: // N + P old_sign = a.sign; a.sign ^= SIGN_POS^SIGN_NEG; r_sub(b, a, s); a.sign = old_sign; break; } } static void normalize(reg& r) { if (!(r.sigl | r.sigh)) { r.exp = 0; r.tag = TW_Z; return; } if (r.exp > EXP_MAX) { r.tag = TW_S; return; } while (!(r.sigh & 0x80000000)) { if (r.exp == 0) return; djshld(&r.sigl); r.exp--; } } static void r_mul(reg& a, reg& b, reg& s) { if (a.tag == TW_Z) { s = CONST_Z; } else if (b.tag == TW_Z) { s = CONST_Z; } else if (a.tag == TW_S) { s = a; } else if (b.tag == TW_S) { s = b; } else { unsigned short sl[9], carry[10]; unsigned short *as = (unsigned short *)(&a.sigl); unsigned short *bs = (unsigned short *)(&b.sigl); unsigned long l, sum; int ai, bi; for (ai=0; ai<8; ai++) sl[ai] = carry[ai] = 0; for (ai = 0; ai < 4; ai++) for (bi = 0; bi < 4; bi++) { l = as[ai] * bs[bi]; sum = sl[ai+bi] + (l & 0xffff); sl[ai+bi] = sum & 0xffff; sum = sl[ai+bi+1] + (l>>16) + (sum>>16); sl[ai+bi+1] = sum & 0xffff; carry[ai+bi+2] += sum>>16; } for (ai=0; ai<8; ai++) { if (carry[ai]) { sum = sl[ai] + carry[ai]; sl[ai] = sum & 0xffff; carry[ai+1] += sum>>16; } } s.sigl = *(long *)(sl+4); s.sigh = *(long *)(sl+6); s.exp = a.exp + b.exp - EXP_BIAS + 1; s.tag = TW_V; } if (a.sign == b.sign) s.sign = SIGN_POS; else s.sign = SIGN_NEG; normalize(s); } static void r_div(reg& a, reg& b, reg& q) { if (a.tag == TW_S) { if (val_same(a, CONST_PINF)) q = a; else if (val_same(a, CONST_NINF)) q = a; } else if (b.tag == TW_S) { if (val_same(b, CONST_PINF)) q = CONST_Z; else if (val_same(b, CONST_NINF)) q = CONST_Z; } else if (a.tag == TW_Z) { q = a; } else if (b.tag == TW_Z) { exception(EX_Z); } else { q.exp = a.exp - b.exp + EXP_BIAS; if (q.exp > EXP_MAX) q = CONST_PINF; else if (q.exp <= 0) q = CONST_Z; else { unsigned long long al, bl, ql, f; int i; al = *(unsigned long long *)(&a.sigl); bl = *(unsigned long long *)(&b.sigl); ql = 0; f = (unsigned long long)1 << 63; for (i=0; i<64; i++) { if (al >= bl) { al -= bl; ql += f; } bl >>= 1; f >>= 1; } *(unsigned long long *)(&q.sigl) = ql; q.tag = TW_V; } } if (a.sign == b.sign) q.sign = SIGN_POS; else q.sign = SIGN_NEG; normalize(q); } static void r_mov(long double *s, reg& d) { unsigned long *sp = (unsigned long *)s; if (sp[2] & 0x8000) d.sign = SIGN_NEG; else d.sign = SIGN_POS; d.exp = sp[2] & 0x7fff; d.sigh = sp[1]; d.sigl = sp[0]; d.tag = TW_V; normalize(d); } static void r_mov(double *s, reg& d) { unsigned m64 = ((unsigned *)s)[1]; unsigned l64 = ((unsigned *)s)[0]; if (m64 & 0x80000000) d.sign = SIGN_NEG; else d.sign = SIGN_POS; if (!((m64 & 0x7fffffff) | (l64))) { int c = d.sign; d = CONST_Z; d.sign = c; return; } d.exp = (int)((m64>>20)&0x7ff) - 1023 + EXP_BIAS; d.sigh = ((m64 & 0xfffff)<<11) | 0x80000000; d.sigh |= l64 >> 21; d.sigl = l64 << 11; d.tag = TW_V; if ((m64 & 0x7ff00000) == 0x7ff00000) d.exp = EXP_MAX+1; normalize(d); } static void r_mov(float *s, reg& d) { unsigned m32 = *(unsigned *)s; if (m32 & 0x80000000) d.sign = SIGN_NEG; else d.sign = SIGN_POS; if (!(m32 & 0x7fffffff)) { int c = d.sign; d = CONST_Z; d.sign = c; return; } d.exp = (int)((m32>>23)&0xff) - 127 + EXP_BIAS; d.sigh = ((m32 & 0x7fffff)<<8) | 0x80000000; d.sigl = 0; d.tag = TW_V; if ((m32 & 0x7f800000) == 0x7f800000) d.exp = EXP_MAX+1; normalize(d); } static void r_mov(long long *_s, reg& d) { long long s = *_s; if (s == 0) { d = CONST_Z; return; } if (s > 0) d.sign = SIGN_POS; else { s = -s; d.sign = SIGN_NEG; } int e = EXP_BIAS + 63; while (s >= 0) { djshld(&s); e -= 1; } d.sigh = s >> 32; d.sigl = s; d.exp = e; d.tag = TW_V; normalize(d); } static void r_mov(long *_s, reg& d) { long s = *_s; if (s == 0) { d = CONST_Z; return; } if (s > 0) d.sign = SIGN_POS; else { s = -s; d.sign = SIGN_NEG; } int e = EXP_BIAS + 31; while (!(s & 0x80000000)) { s <<= 1; e -= 1; } d.sigh = s; d.sigl = 0; d.exp = e; d.tag = TW_V; normalize(d); } static void r_mov(short *_s, reg& d) { int s = *_s; if (s == 0) { d = CONST_Z; return; } if (s > 0) d.sign = SIGN_POS; else { s = -s; d.sign = SIGN_NEG; } int e = EXP_BIAS + 15; while (!(s & 0x8000)) { s <<= 1; e -= 1; } d.sigh = s << 16; d.sigl = 0; d.exp = e; d.tag = TW_V; normalize(d); } static void r_mov(char *s, reg& d) { int side=1, pos=8; long long l; l = 0; for (int i=0; i<18; i++) { l *= 10; switch (side) { case 0: l += s[pos] & 0x0f; side = 1; pos--; break; case 1: l += s[pos] >> 4; side = 0; break; } } r_mov(&l, d); if (s[9] & 0x80) d.sign = SIGN_NEG; } //============================================================================= static void round_to_int(reg& r) // r gets mangled such that sig is int, sign { int more_than_half = 0; int half_or_more = 0; if (r.tag == TW_Z) { return; } while (r.exp < EXP_BIAS+62) { if (r.sigl & 1) more_than_half = 1; djshrd(&r.sigl); r.exp++; } while (r.exp < EXP_BIAS+63) { if (r.sigl & 1) half_or_more = 1; djshrd(&r.sigl); r.exp++; } if (r.exp > EXP_BIAS+63) { r.sigl = r.sigh = ~0; return; } switch (control_word & CW_RC) { case RC_RND: if (half_or_more) if (more_than_half) // nearest (*(long long *)(&r.sigl)) ++; else if (r.sigl & 1) // odd? (*(long long *)(&r.sigl)) ++; break; case RC_DOWN: if ((half_or_more||more_than_half) && r.sign) (*(long long *)(&r.sigl)) ++; break; case RC_UP: if ((half_or_more||more_than_half) && !r.sign) (*(long long *)(&r.sigl)) ++; break; case RC_CHOP: break; } } static void r_mov(reg& s, long double *d) { ((short *)d)[4] = s.exp + s.sign*0x8000; ((long *)d)[0] = s.sigl; ((long *)d)[1] = s.sigh; } static void r_mov(reg& s, double *d) { unsigned long *l = (unsigned long *)d; if (s.tag == TW_Z) { l[0] = 0; l[1] = 0; } else { l[0] = (s.sigl >> 11) | (s.sigh << 21); l[1] = ((s.sigh >> 11) & 0xfffff) | (((s.exp-EXP_BIAS+1023) & 0x7ff) << 20); } if (s.sign) l[1] |= 0x80000000; } static void r_mov(reg& s, float *d) { long f; if (s.tag == TW_Z) { f = 0; } else { f = (s.sigh >> 8) & 0x007fffff; f |= ((s.exp-EXP_BIAS+127) & 0xff) << 23; } if (s.sign) f |= 0x80000000; *(long *)d = f; } static void r_mov(reg& s, long long *d) { reg t; t = s; round_to_int(t); ((long *)d)[0] = t.sigl; ((long *)d)[1] = t.sigh; if (t.sign) *d = - *d; } static void r_mov(reg& s, long *d) { reg t; t = s; round_to_int(t); if (t.sigh || (t.sigl & 0x80000000)) *d = -1; else *d = s.sign ? -t.sigl : t.sigl; } static void r_mov(reg& s, short *d) { reg t; t = s; round_to_int(t); if (t.sigh || (t.sigl & 0xFFFF8000)) *d = -1; else *d = s.sign ? -t.sigl : t.sigl; } static void r_mov(reg& s, char *d) { reg t; t = s; round_to_int(t); long long ll = *(long long *)(&t.sigl); int side = 0; int r, i; for (i=0; i<10; i++) d[i] = 0; int pos=0; for (i=0; i<18; i++) { r = ll % 10; ll /= 10; if (side) { d[pos] |= r << 4; side = 0; pos++; } else { d[pos] |= r; side = 1; } } if (s.sign == SIGN_NEG) d[9] = 0x80; } static void emu_00() { if (empty()) return; if (modrm > 0277) { // fadd st,st(i) int i = modrm & 7; if (empty(i)) return; reg tmp; r_add(st(), st(i), tmp); st() = tmp; return; } else { // fadd m32real reg t1, t2; r_mov((float *)get_modrm(), t1); r_add(t1, st(), t2); st() = t2; } } static void emu_01() { if (empty()) return; if (modrm > 0277) { // fmul st,st(i) int i = modrm & 7; if (empty(i)) return; reg t; r_mul(st(), st(i), t); st() = t; } else { // fmul m32real reg t, t2; r_mov((float *)get_modrm(), t); r_mul(st(), t, t2); st() = t2; } } static void emu_02() { if (empty()) return; if (modrm > 0277) { // fcom st(i) if (empty(modrm&7)) { setcc(SW_C3|SW_C2|SW_C0); return; } int c = compare(st(), st(modrm&7)); int f; if (c & COMP_NAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } else { // fcom m32real reg t; r_mov((float *)get_modrm(), t); int c = compare(st(), t); int f; if (c & COMP_NAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } } static void emu_03() { if (empty()) return; if (modrm > 0277) { // fcomp st(i) if (empty(modrm&7)) { setcc(SW_C3|SW_C2|SW_C0); return; } int c = compare(st(), st(modrm&7)); st().tag = TW_E; top++; int f; if (c & COMP_NAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } else { // fcom m32real reg t; r_mov((float *)get_modrm(), t); int c = compare(st(), t); st().tag = TW_E; top++; int f; if (c & COMP_NAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } } static void emu_04() { if (empty()) return; if (modrm > 0277) { // fsub st,st(i) int i = modrm & 7; if (empty(i)) return; reg tmp; r_sub(st(), st(i), tmp); st() = tmp; return; } else { // fsub m32real reg t1, t2; r_mov((float *)get_modrm(), t1); r_sub(st(), t1, t2); st() = t2; } } static void emu_05() { if (empty()) return; if (modrm > 0277) { // fsubr st,st(i) int i = modrm & 7; if (empty(i)) return; reg tmp; r_sub(st(i), st(), tmp); st() = tmp; return; } else { // fsubr m32real reg t1, t2; r_mov((float *)get_modrm(), t1); r_sub(t1, st(), t2); st() = t2; } } static void emu_06() { if (empty()) return; if (modrm > 0277) { // fdiv st,st(i) int i = modrm & 7; if (empty(i)) return; reg t; r_div(st(), st(i), t); st() = t; } else { // fdiv m32real reg t, t2; r_mov((float *)get_modrm(), t); r_div(st(), t, t2); st() = t2; } } static void emu_07() { if (empty()) return; if (modrm > 0277) { // fdivr st,st(i) int i = modrm & 7; if (empty(i)) return; reg t; r_div(st(i), st(), t); st() = t; } else { // fdivr m32real reg t, t2; r_mov((float *)get_modrm(), t); r_div(t, st(), t2); st() = t2; } } static void emu_10() { if (full()) return; if (modrm > 0277) { // fld st(i) int i = modrm & 7; if (empty(i)) return; st(7) = st(i); top--; return; } else { // fld m32real top--; r_mov((float *)get_modrm(), st()); } } static void emu_11() { if (empty()) return; if (modrm > 0277) { // fxch st(i) int i = modrm & 7; if (empty(i)) return; reg t; t = st(); st() = st(i); st(i) = t; } else { emu_bad(); } } static void fnop() { } static FUNC emu_12_table[] = { fnop, emu_bad, emu_bad, emu_bad, emu_bad, emu_bad, emu_bad, emu_bad }; static void emu_12() { if (modrm > 0277) { (emu_12_table[modrm&7])(); } else { // fst m32real if (empty()) return; r_mov(st(), (float *)get_modrm()); } } static void emu_13() { if (modrm > 0277) { emu_bad(); } else { // fstp m32real if (empty()) return; r_mov(st(), (float *)get_modrm()); st().tag = TW_E; top++; } } static void fchs() { if (empty()) return; st().sign ^= SIGN_POS^SIGN_NEG; status_word &= ~SW_C1; } static void fabs() { if (empty()) return; st().sign = SIGN_POS; status_word &= ~SW_C1; } static void ftst() { switch (st().tag) { case TW_Z: setcc(SW_C3); break; case TW_V: if (st().sign == SIGN_POS) setcc(0); else setcc(SW_C0); break; case TW_S: if (val_same(st(), CONST_PINF)) { setcc(0); break; } else if (val_same(st(), CONST_NINF)) { setcc(SW_C3); break; } setcc(SW_C0|SW_C2|SW_C3); exception(EX_I); break; case TW_E: setcc(SW_C0|SW_C2|SW_C3); exception(EX_SU); break; } } static void fxam() { int c=0; switch (st().tag) { case TW_E: c = SW_C3|SW_C0; break; case TW_Z: c = SW_C3; break; case TW_V: if (st().sigh & 0x80000000) c = SW_C2; else c = SW_C3|SW_C2; break; case TW_S: if (val_same(st(), CONST_NAN)) c = SW_C0; else if (val_same(st(), CONST_PINF)) c = SW_C2|SW_C0; else if (val_same(st(), CONST_NINF)) c = SW_C2|SW_C0; break; } if (st().sign == SIGN_NEG) c |= SW_C1; setcc(c); } static FUNC emu_14_table[] = { fchs, fabs, emu_bad, emu_bad, ftst, fxam, emu_bad, emu_bad }; static void emu_14() { if (modrm > 0277) { (emu_14_table[modrm&7])(); } else { // emu_bad(); } } static void fld_const(reg &c) { if (full()) return; top--; st() = c; status_word &= ~SW_C1; } static void fld1() { fld_const(CONST_1); } static void fldl2t() { fld_const(CONST_L2T); } static void fldl2e() { fld_const(CONST_L2E); } static void fldpi() { fld_const(CONST_PI); } static void fldlg2() { fld_const(CONST_LG2); } static void fldln2() { fld_const(CONST_LN2); } static void fldz() { fld_const(CONST_Z); } static FUNC emu_15_table[] = { fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, emu_bad }; static void emu_15() { if (modrm > 0277) { (emu_15_table[modrm&7])(); } else { // fldcw control_word = *(short *)get_modrm(); } } static void f2xm1() { if (empty()) return; reg xloga, val, rv, bottom, tmp; long i; r_mul(CONST_LN2, st(), xloga); val = xloga; rv = xloga; for (i=2; i<16; i++) { r_mov(&i, bottom); r_mul(val, xloga, tmp); r_div(tmp, bottom, val); r_add(val, rv, tmp); rv = tmp; } st() = rv; } // logb(x) = loga(x) / loga(b) // log2(x) = loge(x) / loge(2) static void fyl2x() { if (empty()) return; reg z, x, nom, denom, xsquare, term, temp, sum, pow; long exponent; reg CONST_SQRT2 = { SIGN_POS, TW_V, EXP_BIAS, 0xf9de6000, 0xb504f333 }; z = st(); if ((z.tag != TW_V) || (z.sign != SIGN_POS)) { return exception(EX_I); // not valid, zero or negative } exponent = (long)(z.exp - EXP_BIAS); z.exp=EXP_BIAS; if (compare(z, CONST_SQRT2) == COMP_A_GT_B) { (z.exp)--; exponent++; } r_sub(z, CONST_1, nom); r_add(z, CONST_1, denom); r_div(nom, denom, x); pow = x; sum = x; r_mul(x, x, xsquare); for (long i=3; i<25; i+=2) { r_mul(pow, xsquare, temp); pow = temp; r_mov(&i, denom); r_div(pow, denom, term); r_add(term, sum, temp); sum = temp; } r_div(sum, CONST_LN2, temp); temp.exp++; if (exponent) { r_mov(&exponent, term); r_add(term, temp, sum); } else { sum = temp; } r_mul(sum, st(1), temp); st(1) = temp; st().tag = TW_E; top++; } static int fprem_do(reg& quot, reg& div1, int round) // remainder of st() / st(1) { int rv; int old_cw = control_word; control_word &= ~CW_RC; control_word |= round; int expdif = quot.exp - div1.exp; if (expdif < 64) { reg tmp, tmp2; r_div(quot, div1, tmp); long q; r_mov(tmp, &q); r_mov(&q, tmp); r_mul(div1, tmp, tmp2); r_sub(quot, tmp2, tmp); quot = tmp; rv = q & 7; } else { reg tmp, tmp2; setcc(SW_C2); r_div(st(), div1, tmp); int old_exp = tmp.exp; tmp.exp &= 31; long q; r_mov(tmp, &q); r_mov(&q, tmp); tmp.exp = old_exp; r_mul(div1, tmp, tmp2); r_sub(quot, tmp2, tmp); quot = tmp; rv = -1; } control_word = old_cw; return rv;; } static void fsincos() { if (empty()) return; int q = fprem_do(st(), CONST_PI2, RC_CHOP); if (q & 1) { reg tmp; r_sub(CONST_PI2, st(), tmp); st() = tmp; } reg x2, val, rv, tmp, t2; reg valc, rvc, tmpc; val = st(); r_mul(st(), val, x2); rv = val; valc = CONST_1; rvc = valc; for (int i=0; i<11; i++) { val.sign ^= SIGN_POS ^ SIGN_NEG; valc.sign ^= SIGN_POS ^ SIGN_NEG; r_mul(x2, val, tmp); r_mul(x2, valc, tmpc); long c = ((i<<1)+2) * ((i<<1)+3); r_mov(&c, t2); r_div(tmp, t2, val); c = ((i<<1)+1) * ((i<<1)+2); r_mov(&c, t2); r_div(tmpc, t2, valc); r_add(val, rv, tmp); rv = tmp; r_add(valc, rvc, tmpc); rvc = tmpc; } setcc(0); if (q & 2) rv.sign ^= SIGN_POS ^ SIGN_NEG; st() = rv; top--; register int qq = q & 3; if ((qq == 1) || (qq == 2)) rvc.sign ^= SIGN_POS ^ SIGN_NEG; st() = rvc; } static void fptan() { fsincos(); if (empty(1)) return; reg tmp; r_div(st(1), st(), tmp); st(1) = tmp; st() = CONST_1; } static void fpatan() { if (empty(1)) return; if (is_zero(st())) { st(1) = CONST_PI2; st().tag = TW_E; top++; return; } if (is_zero(st(1))) { st(1) = CONST_Z; st().tag = TW_E; top++; return; } reg x2, sum, term, pow, temp; int quadrant = 0; if (st(1).sign == SIGN_NEG) quadrant |= 1; if (st(0).sign == SIGN_NEG) quadrant |= 2; st(1).sign = st().sign = SIGN_POS; if (compare(st(1), st()) == COMP_A_GT_B) { quadrant |= 4; temp = st(1); st(1) = st(); st() = temp; } r_div(st(1), st(), sum); r_mul(sum, sum, x2); pow = sum; x2.sign ^= SIGN_POS^SIGN_NEG; for (long i=3; i<25; i+=2) { r_mul(pow, x2, temp); pow = temp; r_mov(&i, temp); r_div(pow, temp, term); r_add(sum, term, temp); sum = temp; } if (quadrant & 4) { r_sub(CONST_PI2, sum, temp); sum = temp; } if (quadrant & 2) { r_sub(CONST_PI, sum, temp); sum = temp; } if (quadrant & 1) sum.sign ^= SIGN_POS^SIGN_NEG; st(1) = sum; st().tag = TW_E; top++; } static void fxtract() { if (empty()) return; if (full()) return; top--; st() = st(1); st().exp = EXP_BIAS; long e = st(1).exp - EXP_BIAS; r_mov(&e, st(1)); } static void fprem1() { if (empty(1)) return; int q = fprem_do(st(), st(1), RC_RND); if (q == -1) setcc(SW_C2); else { int c = 0; if (q&4) c |= SW_C3; if (q&2) c |= SW_C1; if (q&1) c |= SW_C0; setcc(c); } } static void fdecstp() { top--; } static void fincstp() { top++; } static FUNC emu_16_table[] = { f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp }; static void emu_16() { if (modrm > 0277) { (emu_16_table[modrm&7])(); } else { emu_bad(); } } static void fprem() { if (empty(1)) return; int q = fprem_do(st(), st(1), RC_CHOP); if (q == -1) setcc(SW_C2); else { int c = 0; if (q&4) c |= SW_C3; if (q&2) c |= SW_C1; if (q&1) c |= SW_C0; setcc(c); } } void fyl2x(); static void fyl2xp1() { reg newx; r_add(st(), CONST_1, newx); st() = newx; fyl2x(); } static void fsqrt() { if (empty()) return; if (st().tag == TW_Z) return; if (st().exp == EXP_MAX) return; if (st().sign == SIGN_NEG) return exception(EX_I); unsigned long long val = *(unsigned long long *)(&st().sigl); unsigned long long result = 0; unsigned long long side = 0; unsigned long long left = 0; if (st().exp & 1) { djshrd(&val); st().exp++; } int exp = (st().exp - EXP_BIAS - 1)/2 - 64; while (!(((long *)&result)[1] & 0x80000000)) { left = (left << 2) + (((unsigned *)&val)[1] >> 30); djshld(&val); djshld(&val); if (left >= side*2 + 1) { left -= side*2+1; side = (side+1)*2; djshld(&result); result |= 1; } else { side *= 2; djshld(&result); } exp++; } st().exp = exp + EXP_BIAS; st().sigl = result & 0xffffffff; st().sigh = result >> 32; st().tag = TW_V; } static void frndint() { if (empty()) return; long long tmp; if (st().exp > EXP_BIAS+62) return; r_mov(st(), &tmp); r_mov(&tmp, st()); } static void fscale() { long scale1; if (empty(1)) return; r_mov(st(1), &scale1); st().exp += scale1; } static void fsin() { if (empty()) return; int q = fprem_do(st(), CONST_PI2, RC_CHOP); if (q & 1) { reg tmp; r_sub(CONST_PI2, st(), tmp); st() = tmp; } reg x2, val, rv, tmp, t2; val = st(); r_mul(st(), val, x2); rv = val; for (int i=0; i<11; i++) { long c = ((i<<1)+2) * ((i<<1)+3); val.sign ^= SIGN_POS ^ SIGN_NEG; r_mul(x2, val, tmp); r_mov(&c, t2); r_div(tmp, t2, val); r_add(val, rv, tmp); rv = tmp; } setcc(0); if (q & 2) rv.sign ^= SIGN_POS ^ SIGN_NEG; st() = rv; } static void fcos() { if (empty()) return; int q = fprem_do(st(), CONST_PI2, RC_CHOP); if (q & 1) { reg tmp; r_sub(CONST_PI2, st(), tmp); st() = tmp; } reg x2, val, rv, tmp, t2; val = st(); r_mul(st(), val, x2); val = CONST_1; rv = val; for (int i=0; i<11; i++) { long c = ((i<<1)+1) * ((i<<1)+2); val.sign ^= SIGN_POS ^ SIGN_NEG; r_mul(x2, val, tmp); r_mov(&c, t2); r_div(tmp, t2, val); r_add(val, rv, tmp); rv = tmp; } setcc(0); register int qq = q & 3; if ((qq == 1) || (qq == 2)) rv.sign ^= SIGN_POS ^ SIGN_NEG; st() = rv; } static FUNC emu_17_table[] = { fprem, fyl2xp1, fsqrt, fsincos, frndint, fscale, fsin, fcos }; static void emu_17() { if (modrm > 0277) { (emu_17_table[modrm&7])(); } else { // fstcw m16int *(short *)get_modrm() = control_word; } } static void emu_20() { if (empty()) return; if (modrm > 0277) { emu_bad(); } else { // fiadd m32int reg t1, t2; r_mov((long *)get_modrm(), t1); r_add(st(), t1, t2); st() = t2; } } static void emu_21() { if (empty()) return; if (modrm > 0277) { // emu_bad(); } else { // fimul m32int reg t, t2; r_mov((long *)get_modrm(), t); r_mul(st(), t, t2); st() = t2; } } static void emu_22() { if (empty()) return; if (modrm > 0277) { emu_bad(); } else { // ficom m32int reg t; r_mov((long *)get_modrm(), t); int c = compare(st(), t); int f; if (c & COMP_NAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } } static void emu_23() { if (empty()) return; if (modrm > 0277) { emu_bad(); } else { // ficomp m32int reg t; r_mov((long *)get_modrm(), t); int c = compare(st(), t); st().tag = TW_E; top++; int f; if (c & COMP_NAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } } static void emu_24() { if (empty()) return; if (modrm > 0277) { emu_bad(); } else { // fisub m32int reg t1, t2; r_mov((long *)get_modrm(), t1); r_sub(st(), t1, t2); st() = t2; } } static void emu_25() { if (empty()) return; if (modrm > 0277) { // fucompp if ((modrm&7) != 1) emu_bad(); else { if (empty(modrm&7)) { setcc(SW_C3|SW_C2|SW_C0); return; } int c = compare(st(), st(1)); st().tag = TW_E; top++; st().tag = TW_E; top++; int f; if (c & COMP_SNAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } } else { // fisubr m32int reg t1, t2; r_mov((long *)get_modrm(), t1); r_sub(t1, st(), t2); st() = t2; } } static void emu_26() { if (empty()) return; if (modrm > 0277) { // emu_bad(); } else { // fidiv m32int reg t, t2; r_mov((long *)get_modrm(), t); r_div(st(), t, t2); st() = t2; } } static void emu_27() { if (empty()) return; if (modrm > 0277) { // emu_bad(); } else { // fidivr m32int reg t, t2; r_mov((long *)get_modrm(), t); r_div(t, st(), t2); st() = t2; } } static void emu_30() { if (full()) return; if (modrm > 0277) { emu_bad(); } else { // fild m32int top--; r_mov((long *)get_modrm(), st()); } } static void emu_31() { void emu_bad(); emu_bad(); } static void emu_32() { if (empty()) return; if (modrm > 0277) { // emu_bad(); } else { // fist m32int r_mov(st(), (long *)get_modrm()); } } static void emu_33() { if (empty()) return; if (modrm > 0277) { // emu_bad(); } else { // fistp m32int r_mov(st(), (long *)get_modrm()); st().tag = TW_E; top++; } } static void fclex() { status_word &= ~(SW_B|SW_ES|SW_SF|SW_PE|SW_UE|SW_OE|SW_ZE|SW_DE|SW_IE); } static void finit() { control_word = 0x037e; status_word = 0; top = 0; for (int r=0; r<8; r++) { regs[r].sign = 0; regs[r].tag = TW_E; regs[r].exp = 0; regs[r].sigh = 0; regs[r].sigl = 0; } } static FUNC emu_34_table[] = { emu_bad, emu_bad, fclex, finit, emu_bad, emu_bad, emu_bad, emu_bad }; static void emu_34() { if (modrm > 0277) { (emu_34_table[modrm&7])(); } else { // emu_bad(); } } static void emu_35() { if (full()) return; if (modrm > 0277) { emu_bad(); } else { // fld m80real top--; r_mov((long double *)get_modrm(), st()); } } static void emu_36() { void emu_bad(); emu_bad(); } static void emu_37() { if (modrm > 0277) { emu_bad(); } else { // fstp m80real if (empty()) return; r_mov(st(), (long double *)get_modrm()); st().tag = TW_E; top++; } } static void emu_40() { if (empty()) return; if (modrm > 0277) { // fadd st(i),st int i = modrm & 7; if (empty(i)) return; reg tmp; r_add(st(), st(i), tmp); st(i) = tmp; return; } else { // fadd m64real reg t1, t2; r_mov((double *)get_modrm(), t1); r_add(t1, st(), t2); st() = t2; } } static void emu_41() { if (empty()) return; if (modrm > 0277) { // fmul st(i),st int i = modrm & 7; if (empty(i)) return; reg t; r_mul(st(i), st(), t); st(i) = t; } else { // fmul m64real reg t, t2; r_mov((double *)get_modrm(), t); r_mul(st(), t, t2); st() = t2; } } static void emu_42() { if (empty()) return; if (modrm > 0277) { // emu_bad(); } else { // fcom m64real reg t; r_mov((double *)get_modrm(), t); int c = compare(st(), t); int f; if (c & COMP_NAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } } static void emu_43() { if (empty()) return; if (modrm > 0277) { emu_bad(); } else { // fcomp m64real reg t; r_mov((double *)get_modrm(), t); int c = compare(st(), t); st().tag = TW_E; top++; int f; if (c & COMP_NAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } } static void emu_44() { if (empty()) return; if (modrm > 0277) { // fsub st(i),st int i = modrm & 7; if (empty(i)) return; reg tmp; r_sub(st(), st(i), tmp); st(i) = tmp; return; } else { // fsub m64real reg t1, t2; r_mov((double *)get_modrm(), t1); r_sub(st(), t1, t2); st() = t2; } } static void emu_45() { if (empty()) return; if (modrm > 0277) { // fsubr st(i),st int i = modrm & 7; if (empty(i)) return; reg tmp; r_sub(st(i), st(), tmp); st(i) = tmp; return; } else { // fsubr m64real reg t1, t2; r_mov((double *)get_modrm(), t1); r_sub(t1, st(), t2); st() = t2; } } static void emu_46() { if (empty()) return; if (modrm > 0277) { // fdivr st(i),st int i = modrm&7; if (empty(i)) return; reg t; r_div(st(), st(i), t); st(i) = t; } else { // fdiv m64real reg t, t2; r_mov((double *)get_modrm(), t); r_div(st(), t, t2); st() = t2; } } static void emu_47() { if (empty()) return; if (modrm > 0277) { // fdiv st(i),st reg t; int i = modrm & 7; if (empty(i)) return; r_div(st(i), st(0), t); st(i) = t; } else { // fdivr m64real reg t, t2; r_mov((double *)get_modrm(), t); r_div(t, st(), t2); st() = t2; } } static void emu_50() { if (full()) return; if (modrm > 0277) { // ffree st(i) int i = modrm & 7; st(i).tag = TW_E; } else { // fld m64real top--; r_mov((double *)get_modrm(), st()); } } static void emu_51() { void emu_bad(); emu_bad(); } static void emu_52() { if (modrm > 0277) { st(modrm&7) = st(); } else { // fst m64real if (empty()) return; r_mov(st(), (double *)get_modrm()); } } static void emu_53() { if (modrm > 0277) { // fstp st(i) st(modrm&7) = st(); st().tag = TW_E; top++; } else { // fstp m64real if (empty()) return; r_mov(st(), (double *)get_modrm()); st().tag = TW_E; top++; } } static void emu_54() { if (modrm > 0277) { // fucom st(i) if (empty()) return; if (empty(modrm&7)) { setcc(SW_C3|SW_C2|SW_C0); return; } int c = compare(st(), st(modrm&7)); int f; if (c & COMP_SNAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } else { // frestor void *addr = get_modrm(); int i, tag_word; control_word = *(int *)(addr+0) & 0xffff; status_word = *(int *)(addr+4) & 0xffff; tag_word = *(int *)(addr+8) & 0xffff; top = (status_word / SW_TOPS) & 3; for (i=0; i<8; i++) { r_mov((long double *)(addr + 0x1c + 10), st(i)); st(i).tag = (tag_word >> (((i+top)&7)*2)) & 3; } } } static void emu_55() { if (empty()) return; if (modrm > 0277) { // fucomp st(i) if (empty(modrm&7)) { setcc(SW_C3|SW_C2|SW_C0); return; } int c = compare(st(), st(modrm&7)); st().tag = TW_E; top++; int f; if (c & COMP_SNAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } else { emu_bad(); } } static void emu_56() /* fsave */ { void *addr = get_modrm(); int i, tag_word=0; status_word = status_word & ~SW_TOP; status_word += (top&7) * SW_TOPS; *(int *)(addr+0) = control_word & 0xffff; *(int *)(addr+4) = status_word & 0xffff; for (i=0; i<8; i++) { tag_word |= (st(i).tag << (((i+top)&7)*2)); r_mov(st(i), (long double *)(addr + 0x1c + i*10)); } *(int *)(addr+8) = tag_word & 0xffff; } static void emu_57() { if (modrm > 0277) { // emu_bad(); } else { // fstsw m2byte status_word &= ~SW_TOP; status_word |= (top&7) * SW_TOPS; *(short *)get_modrm() = status_word; } } static void emu_60() { if (empty()) return; if (modrm > 0277) { // faddp st(i),st int i = modrm & 7; if (empty(i)) return; reg tmp; r_add(st(), st(i), tmp); st(i) = tmp; st().tag = TW_E; top++; return; } else { // fiadd m16int reg t1, t2; r_mov((short *)get_modrm(), t1); r_add(st(), t1, t2); st() = t2; } } static void emu_61() { if (empty()) return; if (modrm > 0277) { // fmulp st(i),st int i = modrm & 7; if (empty(i)) return; reg t; r_mul(st(i), st(), t); st(i) = t; st().tag = TW_E; top++; } else { // fimul m16int reg t, t2; r_mov((short *)get_modrm(), t); r_mul(st(), t, t2); st() = t2; } } static void emu_62() { if (empty()) return; if (modrm > 0277) { emu_bad(); } else { // ficom m16int reg t; r_mov((short *)get_modrm(), t); int c = compare(st(), t); int f; if (c & COMP_NAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } } static void emu_63() { if (empty()) return; if (modrm > 0277) { // fcompp if ((modrm&7) != 1) return emu_bad(); if (empty(1)) { setcc(SW_C3|SW_C2|SW_C0); return; } int c = compare(st(), st(1)); int f; st().tag = TW_E; top++; st().tag = TW_E; top++; if (c & COMP_NAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } else { // ficomp m16int reg t; r_mov((short *)get_modrm(), t); int c = compare(st(), t); st().tag = TW_E; top++; int f; if (c & COMP_NAN) { exception(EX_I); f = SW_C3 | SW_C2 | SW_C0; } else switch (c) { case COMP_A_LT_B: f = SW_C0; break; case COMP_A_EQ_B: f = SW_C3; break; case COMP_A_GT_B: f = 0; break; case COMP_NOCOMP: f = SW_C3 | SW_C2 | SW_C0; break; } setcc(f); } } static void emu_64() { if (empty()) return; if (modrm > 0277) { // fsubp st(i),st int i = modrm & 7; if (empty(i)) return; reg tmp; r_sub(st(), st(i), tmp); st(i) = tmp; st().tag = TW_E; top++; return; } else { // fisub m16int reg t1, t2; r_mov((short *)get_modrm(), t1); r_sub(st(), t1, t2); st() = t2; } } static void emu_65() { if (empty()) return; if (modrm > 0277) { // fsubr st(i),st int i = modrm & 7; if (empty(i)) return; reg tmp; r_sub(st(i), st(), tmp); st(i) = tmp; st().tag = TW_E; top++; return; } else { // fisubr m16int reg t1, t2; r_mov((short *)get_modrm(), t1); r_sub(t1, st(), t2); st() = t2; } } static void emu_66() { if (empty()) return; if (modrm > 0277) { // fdivrp st(i),st int i = modrm & 7; if (empty(i)) return; reg t; r_div(st(), st(i), t); st(i) = t; st().tag = TW_E; top++; } else { // fidiv m16int reg t, t2; r_mov((short *)get_modrm(), t); r_div(st(), t, t2); st() = t2; } } static void emu_67() { if (empty()) return; if (modrm > 0277) { // fdivp st(i),st reg t; int i = modrm & 7; if (empty(i)) return; r_div(st(i), st(), t); st(i) = t; st().tag = TW_E; top++; } else { // fidiv m16int reg t, t2; r_mov((short *)get_modrm(), t); r_div(t, st(), t2); st() = t2; } } static void emu_70() { if (full()) return; if (modrm > 0277) { emu_bad(); } else { // fild m16int top--; r_mov((short *)get_modrm(), st()); } } static void emu_71() { void emu_bad(); emu_bad(); } static void emu_72() { if (empty()) return; if (modrm > 0277) { // emu_bad(); } else { // fist m16int r_mov(st(), (short *)get_modrm()); } } static void emu_73() { if (empty()) return; if (modrm > 0277) { // emu_bad(); } else { // fistp m16int r_mov(st(), (short *)get_modrm()); st().tag = TW_E; top++; } } static void fstsw_ax() { status_word &= ~SW_TOP; status_word |= (top&7) * SW_TOPS; eax &= 0xffff0000; eax |= status_word; } static FUNC emu_74_table[] = { fstsw_ax, emu_bad, emu_bad, emu_bad, emu_bad, emu_bad, emu_bad, emu_bad }; static void emu_74() { if (modrm > 0277) { (emu_74_table[modrm&7])(); } else { // fbld m80dec if (full()) return; top--; r_mov((char *)get_modrm(), st()); } } static void emu_75() { if (full()) return; if (modrm > 0277) { emu_bad(); } else { // fild m80int top--; r_mov((long long *)get_modrm(), st()); } } static void emu_76() { if (st().tag == TW_E) exception(EX_SU); if (modrm > 0277) { emu_bad(); } else { // fbstp r_mov(st(), (char *)get_modrm()); st().tag = TW_E; top++; } } static void emu_77() { if (empty()) return; if (modrm > 0277) { // emu_bad(); } else { // fistp m32int r_mov(st(), (long long *)get_modrm()); st().tag = TW_E; top++; } } #ifndef TEST #define TEST 0 #endif extern void emu_install(); extern void emu_printall(); #if TEST double a=10, b=16; float f=3.3; int i=3; static void test() { asm("fldl _b"); asm("fldl _a"); emu_printall(); asm("fdivr %st,%st(1)"); emu_printall(); } #endif #if 0 main() { #if TEST test(); #endif emu_install(); #if TEST test(); #endif } #endif #if 0 char saw[256*8]; /* zero here means invalid. If first entry starts with '*', use st(i) */ /* no assumed %EFs here. Indexed by rm(modrm()) */ char *f0[] = {0, 0, 0, 0, 0, 0, 0, 0}; char *fop_9[] = { "*fxch st,%GF" }; char *fop_10[] = { "fnop", 0, 0, 0, 0, 0, 0, 0 }; char *fop_12[] = { "fchs", "fabs", 0, 0, "ftst", "fxam", 0, 0 }; char *fop_13[] = { "fld1", "fldl2t", "fldl2e", "fldpi", "fldlg2", "fldln2", "fldz", 0 }; char *fop_14[] = { "f2xm1", "fyl2x", "fptan", "fpatan", "fxtract", "fprem1", "fdecstp", "fincstp" }; char *fop_15[] = { "fprem", "fyl2xp1", "fsqrt", "fsincos", "frndint", "fscale", "fsin", "fcos" }; char *fop_21[] = { 0, "fucompp", 0, 0, 0, 0, 0, 0 }; char *fop_28[] = { 0, 0, "fclex", "finit", 0, 0, 0, 0 }; char *fop_32[] = { "*fadd %GF,st" }; char *fop_33[] = { "*fmul %GF,st" }; char *fop_36[] = { "*fsubr %GF,st" }; char *fop_37[] = { "*fsub %GF,st" }; char *fop_38[] = { "*fdivr %GF,st" }; char *fop_39[] = { "*fdiv %GF,st" }; char *fop_40[] = { "*ffree %GF" }; char *fop_42[] = { "*fst %GF" }; char *fop_43[] = { "*fstp %GF" }; char *fop_44[] = { "*fucom %GF" }; char *fop_45[] = { "*fucomp %GF" }; char *fop_48[] = { "*faddp %GF,st" }; char *fop_49[] = { "*fmulp %GF,st" }; char *fop_51[] = { 0, "fcompp", 0, 0, 0, 0, 0, 0 }; char *fop_52[] = { "*fsubrp %GF,st" }; char *fop_53[] = { "*fsubp %GF,st" }; char *fop_54[] = { "*fdivrp %GF,st" }; char *fop_55[] = { "*fdivp %GF,st" }; char *fop_60[] = { "fstsw ax", 0, 0, 0, 0, 0, 0, 0 }; char **fspecial[] = { /* 0=use st(i), 1=undefined 0 in fop_* means undefined */ 0, 0, 0, 0, 0, 0, 0, 0, 0, fop_9, fop_10, 0, fop_12, fop_13, fop_14, fop_15, f0, f0, f0, f0, f0, fop_21, f0, f0, f0, f0, f0, f0, fop_28, f0, f0, f0, fop_32, fop_33, f0, f0, fop_36, fop_37, fop_38, fop_39, fop_40, f0, fop_42, fop_43, fop_44, fop_45, f0, f0, fop_48, fop_49, f0, fop_51, fop_52, fop_53, fop_54, fop_55, f0, f0, f0, f0, fop_60, f0, f0, f0, }; char *floatops[] = { /* assumed " %EF" at end of each. mod != 3 only */ /*00*/ "fadd", "fmul", "fcom", "fcomp", "fsub", "fsubr", "fdiv", "fdivr", /*08*/ "fld", 0, "fst", "fstp", "fldenv", "fldcw", "fstenv", "fstcw", /*16*/ "fiadd", "fimul", "ficomw", "ficompw", "fisub", "fisubr", "fidiv", "fidivr", /*24*/ "fild", 0, "fist", "fistp", "frstor", "fldt", 0, "fstpt", /*32*/ "faddq", "fmulq", "fcomq", "fcompq", "fsubq", "fsubrq", "fdivq", "fdivrq", /*40*/ "fldq", 0, "fstq", "fstpq", 0, 0, "fsave", "fstsww", /*48*/ "fiaddw", "fimulw", "ficomw", "ficompw", "fisubw", "fisubrw", "fidivw", "fidivr", /*56*/ "fildw", 0, "fistw", "fistpw", "fbldt", "fildq", "fbstpt", "fistpq" }; #endif static FUNC esc_table[64] = { emu_00, emu_10, emu_20, emu_30, emu_40, emu_50, emu_60, emu_70, emu_01, emu_11, emu_21, emu_31, emu_41, emu_51, emu_61, emu_71, emu_02, emu_12, emu_22, emu_32, emu_42, emu_52, emu_62, emu_72, emu_03, emu_13, emu_23, emu_33, emu_43, emu_53, emu_63, emu_73, emu_04, emu_14, emu_24, emu_34, emu_44, emu_54, emu_64, emu_74, emu_05, emu_15, emu_25, emu_35, emu_45, emu_55, emu_65, emu_75, emu_06, emu_16, emu_26, emu_36, emu_46, emu_56, emu_66, emu_76, emu_07, emu_17, emu_27, emu_37, emu_47, emu_57, emu_67, emu_77, }; extern "C" int _emu_entry(jmp_buf _exception_state); int _emu_entry(jmp_buf _exception_state) { int jmpval; eip = (char *) _exception_state->__eip; eax = _exception_state->__eax; ebx = _exception_state->__ebx; ecx = _exception_state->__ecx; edx = _exception_state->__edx; esi = _exception_state->__esi; edi = _exception_state->__edi; ebp = _exception_state->__ebp; esp = _exception_state->__esp; if (*eip == 0x66) // operand size - we know what size we need eip++; if (*eip == 0x9b) // fwait { // _exception_state->__eip++; return 0; } jmpval = setjmp(jumpbuf); if(jmpval) return 1; /* Emulator failed for some reason */ #if 0 int see = ((int)(eip[0] & 7) << 8) | eip[1]; if (saw[see] != 42) { eprintf("EMU387: %02x %02x %02x %02x - e%d%d", eip[0], eip[1], eip[2], eip[3], eip[0]&7, (eip[1]>>3)&7); eprintf(" s%d ", eip[1]&7); int esc = ((eip[0]<<3)&070) | ((eip[1]>>3)&007); int modrm = eip[1]; if ((modrm>>6) == 3) if (fspecial[esc]) if (fspecial[esc][0] && (fspecial[esc][0][0] == '*')) eprintf("%s\r\n", fspecial[esc][0]+1); else if (fspecial[esc][modrm&7]) eprintf("%s\r\n", fspecial[esc][modrm&7]); else eprintf("\r\n"); else eprintf("%s st(i)\r\n", floatops[esc]); else eprintf("%s st(i)\r\n", floatops[esc]); saw[see] = 42; } #endif int esc_value = *eip++ & 7; modrm = *eip++; esc_value |= (modrm & 070); (esc_table[esc_value])(); // emu_printall(); _exception_state->__eip = (unsigned long) eip; _exception_state->__eax = eax; return 0; }