#include "emu.h" #include "const.h" extern "C" void shld(void *); extern "C" void shrd(void *); extern void normalize(reg& r); 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) { r_mov(a, s); r_mov(b, t); } else { r_mov(b, s); r_mov(a, t); } if (s.exp - t.exp > 64) return; while (t.exp < s.exp) { t.exp ++; shrd(&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) { shrd(&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; shld(&s.sigl); s.exp--; } } } void r_usub(reg& a, reg& b, reg& d) // a > b { reg t; r_mov(a, d); r_mov(b, t); if (d.exp - t.exp > 64) return; while (t.exp < d.exp) { t.exp ++; shrd(&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 /= 65536; } if (!(d.sigh | d.sigl)) { d.exp = 0; d.tag = TW_Z; } else { while (!(d.sigh & 0x80000000)) { if (d.exp == 0) return; shld(&d.sigl); d.exp--; } } } void r_add(reg& a, reg& b, reg& s) { if (a.tag == TW_Z) return r_mov(b, s); if (b.tag == TW_Z) return r_mov(a, s); if (a.tag == TW_S) return r_mov(a, s); if (b.tag == TW_S) return r_mov(b, s); 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 b.sign ^= SIGN_POS^SIGN_NEG; r_sub(a, b, s); break; case 2: // N + P a.sign ^= SIGN_POS^SIGN_NEG; r_sub(b, a, s); break; } } void r_sub(reg& a, reg& b, reg& d) { if (b.tag == TW_Z) return r_mov(a, d); if (a.tag == TW_Z) { r_mov(b, d); d.sign ^= SIGN_POS^SIGN_NEG; return; } if (a.tag == TW_S) return r_mov(a, d); if (b.tag == TW_S) { r_mov(b, d); 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; } } void r_mul(reg& a, reg& b, reg& s) { if (a.tag == TW_Z) { r_mov(CONST_Z, s); } else if (b.tag == TW_Z) { r_mov(CONST_Z, s); } else if (a.tag == TW_S) { r_mov(a, s); } else if (b.tag == TW_S) { r_mov(b, s); } 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); } void r_div(reg& a, reg& b, reg& q) { if (a.tag == TW_S) { if (val_same(a, CONST_PINF)) r_mov(a, q); else if (val_same(a, CONST_NINF)) r_mov(a, q); } else if (b.tag == TW_S) { if (val_same(b, CONST_PINF)) r_mov(CONST_Z, q); else if (val_same(b, CONST_NINF)) r_mov(CONST_Z, q); } else if (a.tag == TW_Z) { r_mov(a, q); } else if (b.tag == TW_Z) { exception(EX_Z); } else { q.exp = a.exp - b.exp + EXP_BIAS; if (q.exp > EXP_MAX) r_mov(CONST_PINF, q); else if (q.exp <= 0) r_mov(CONST_Z, q); 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); }