summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/cpu/i386/x87ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/cpu/i386/x87ops.c')
-rw-r--r--trunk/src/emu/cpu/i386/x87ops.c391
1 files changed, 391 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/i386/x87ops.c b/trunk/src/emu/cpu/i386/x87ops.c
new file mode 100644
index 00000000000..0fd441abbaf
--- /dev/null
+++ b/trunk/src/emu/cpu/i386/x87ops.c
@@ -0,0 +1,391 @@
+// Intel x87 FPU opcodes
+
+#define ST(x) (cpustate->fpu_reg[(cpustate->fpu_top + (x)) & 7])
+#define FPU_INFINITY_DOUBLE U64(0x7ff0000000000000)
+#define FPU_INFINITY_SINGLE (0x7f800000)
+#define FPU_SIGN_BIT_DOUBLE U64(0x8000000000000000)
+#define FPU_SIGN_BIT_SINGLE (0x80000000)
+
+// FPU control word flags
+#define FPU_MASK_INVALID_OP 0x0001
+#define FPU_MASK_DENORMAL_OP 0x0002
+#define FPU_MASK_ZERO_DIVIDE 0x0004
+#define FPU_MASK_OVERFLOW 0x0008
+#define FPU_MASK_UNDERFLOW 0x0010
+#define FPU_MASK_PRECISION 0x0020
+
+// FPU status word flags
+#define FPU_BUSY 0x8000
+#define FPU_C3 0x4000
+#define FPU_STACK_TOP_MASK 0x3800
+#define FPU_C2 0x0400
+#define FPU_C1 0x0200
+#define FPU_C0 0x0100
+#define FPU_ERROR_SUMMARY 0x0080
+#define FPU_STACK_FAULT 0x0040
+#define FPU_EXCEPTION_PRECISION 0x0020
+#define FPU_EXCEPTION_UNDERFLOW 0x0010
+#define FPU_EXCEPTION_OVERFLOW 0x0008
+#define FPU_EXCEPTION_ZERO_DIVIDE 0x0004
+#define FPU_EXCEPTION_DENORMAL_OP 0x0002
+#define FPU_EXCEPTION_INVALID_OP 0x0001
+
+INLINE void FPU_PUSH(i386_state *cpustate, X87_REG value)
+{
+ cpustate->fpu_top--;
+ if (cpustate->fpu_top < 0)
+ {
+ cpustate->fpu_top = 7;
+ }
+
+ cpustate->fpu_reg[cpustate->fpu_top] = value;
+}
+
+INLINE X87_REG FPU_POP(i386_state *cpustate)
+{
+ X87_REG value = cpustate->fpu_reg[cpustate->fpu_top];
+
+ cpustate->fpu_tag_word |= 3 << (cpustate->fpu_top * 2); // set FPU register tag to 3 (empty)
+
+ cpustate->fpu_top++;
+ if (cpustate->fpu_top > 7)
+ {
+ cpustate->fpu_top = 0;
+ }
+
+ return value;
+}
+
+static void I386OP(fpu_group_d8)(i386_state *cpustate) // Opcode 0xd8
+{
+ UINT8 modrm = FETCH(cpustate);
+ if (modrm < 0xc0)
+ {
+ UINT32 ea = GetEA(cpustate,modrm);
+
+ switch ((modrm >> 3) & 0x7)
+ {
+ case 6: // FDIV
+ UINT32 src = READ32(cpustate,ea);
+ if(src == 0)
+ fatalerror("FPU: Unimplemented Divide-by-zero exception at %08X.\n", cpustate->pc-2);
+ ST(0).f = ST(0).f / src;
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+ }
+ else
+ {
+ fatalerror("I386: FPU Op D8 %02X at %08X", modrm, cpustate->pc-2);
+ }
+}
+
+static void I386OP(fpu_group_d9)(i386_state *cpustate) // Opcode 0xd9
+{
+ UINT8 modrm = FETCH(cpustate);
+
+ if (modrm < 0xc0)
+ {
+ UINT32 ea = GetEA(cpustate,modrm);
+
+ switch ((modrm >> 3) & 0x7)
+ {
+ case 5: // FLDCW
+ {
+ cpustate->fpu_control_word = READ16(cpustate,ea);
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ case 6: // FSTENV
+ { // TODO: 32-bit operand size
+ WRITE16(cpustate,ea, cpustate->fpu_control_word);
+ WRITE16(cpustate,ea+2, cpustate->fpu_status_word);
+ WRITE16(cpustate,ea+4, cpustate->fpu_tag_word);
+ WRITE16(cpustate,ea+6, cpustate->fpu_inst_ptr & 0xffff);
+ WRITE16(cpustate,ea+8, (cpustate->fpu_opcode & 0x07ff) | ((cpustate->fpu_inst_ptr & 0x0f0000) >> 4));
+ WRITE16(cpustate,ea+10, cpustate->fpu_data_ptr & 0xffff);
+ WRITE16(cpustate,ea+12, ((cpustate->fpu_inst_ptr & 0x0f0000) >> 4));
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ case 7: // FSTCW
+ {
+ WRITE16(cpustate,ea, cpustate->fpu_control_word);
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ default:
+ fatalerror("I386: FPU Op D9 %02X at %08X", modrm, cpustate->pc-2);
+ }
+ }
+ else
+ {
+ switch (modrm & 0x3f)
+ {
+ // FLD
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
+ {
+ X87_REG t = ST(modrm & 7);
+ FPU_PUSH(cpustate,t);
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ case 0x20: // FCHS
+ {
+ ST(0).i ^= FPU_SIGN_BIT_DOUBLE;
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ case 0x28: // FLD1
+ {
+ X87_REG t;
+ t.f = 1.0;
+ FPU_PUSH(cpustate,t);
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ case 0x2e: // FLDZ
+ {
+ X87_REG t;
+ t.f = 0.0;
+ FPU_PUSH(cpustate,t);
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+ default:
+ fatalerror("I386: FPU Op D9 %02X at %08X", modrm, cpustate->pc-2);
+ }
+ }
+}
+
+static void I386OP(fpu_group_da)(i386_state *cpustate) // Opcode 0xda
+{
+ UINT8 modrm = FETCH(cpustate);
+ fatalerror("I386: FPU Op DA %02X at %08X", modrm, cpustate->pc-2);
+}
+
+static void I386OP(fpu_group_db)(i386_state *cpustate) // Opcode 0xdb
+{
+ UINT8 modrm = FETCH(cpustate);
+
+ if (modrm < 0xc0)
+ {
+ fatalerror("I386: FPU Op DB %02X at %08X", modrm, cpustate->pc-2);
+ }
+ else
+ {
+ switch (modrm & 0x3f)
+ {
+ case 0x22: // FCLEX
+ {
+ // clears exception flags and busy bit.
+ cpustate->fpu_status_word &= ~0x80ff;
+
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+ case 0x23: // FINIT
+ {
+ cpustate->fpu_control_word = 0x37f;
+ cpustate->fpu_status_word = 0;
+ cpustate->fpu_tag_word = 0xffff;
+ cpustate->fpu_data_ptr = 0;
+ cpustate->fpu_inst_ptr = 0;
+ cpustate->fpu_opcode = 0;
+
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ case 0x24: // FSETPM (treated as nop on 387+)
+ {
+ CYCLES(cpustate,1);
+ break;
+ }
+
+ default:
+ fatalerror("I386: FPU Op DB %02X at %08X", modrm, cpustate->pc-2);
+ }
+ }
+}
+
+static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
+{
+ UINT8 modrm = FETCH(cpustate);
+
+ if (modrm < 0xc0)
+ {
+ //UINT32 ea = GetEA(cpustate,modrm);
+
+ switch ((modrm >> 3) & 0x7)
+ {
+ default:
+ fatalerror("I386: FPU Op DC %02X at %08X", modrm, cpustate->pc-2);
+ }
+ }
+ else
+ {
+ switch (modrm & 0x3f)
+ {
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
+ {
+ // FDIVR
+ if ((ST(modrm & 7).i & U64(0x7fffffffffffffff)) == 0)
+ {
+ // set result as infinity if zero divide is masked
+ if (cpustate->fpu_control_word & FPU_MASK_ZERO_DIVIDE)
+ {
+ ST(modrm & 7).i |= FPU_INFINITY_DOUBLE;
+ }
+ }
+ else
+ {
+ ST(modrm & 7).f = ST(0).f / ST(modrm & 7).f;
+ }
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ default:
+ fatalerror("I386: FPU Op DC %02X at %08X", modrm, cpustate->pc-2);
+ }
+ }
+}
+
+static void I386OP(fpu_group_dd)(i386_state *cpustate) // Opcode 0xdd
+{
+ UINT8 modrm = FETCH(cpustate);
+
+ if (modrm < 0xc0)
+ {
+ UINT32 ea = GetEA(cpustate,modrm);
+
+ switch ((modrm >> 3) & 0x7)
+ {
+ case 7: // FSTSW
+ {
+ WRITE16(cpustate,ea, (cpustate->fpu_status_word & ~FPU_STACK_TOP_MASK) | (cpustate->fpu_top << 10));
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ default:
+ fatalerror("I386: FPU Op DD %02X at %08X", modrm, cpustate->pc-2);
+ }
+ }
+ else
+ {
+ switch (modrm & 0x3f)
+ {
+ default:
+ fatalerror("I386: FPU Op DD %02X at %08X", modrm, cpustate->pc-2);
+ }
+ }
+}
+
+static void I386OP(fpu_group_de)(i386_state *cpustate) // Opcode 0xde
+{
+ UINT8 modrm = FETCH(cpustate);
+
+ if (modrm < 0xc0)
+ {
+ // UINT32 ea = GetEA(cpustate,modrm);
+
+ switch ((modrm >> 3) & 0x7)
+ {
+ default:
+ fatalerror("I386: FPU Op DE %02X at %08X", modrm, cpustate->pc-2);
+ }
+ }
+ else
+ {
+ switch (modrm & 0x3f)
+ {
+ case 0x19: // FCOMPP
+ {
+ cpustate->fpu_status_word &= ~(FPU_C3 | FPU_C2 | FPU_C0);
+ if (ST(0).f > ST(1).f)
+ {
+ // C3 = 0, C2 = 0, C0 = 0
+ }
+ else if (ST(0).f < ST(1).f)
+ {
+ cpustate->fpu_status_word |= FPU_C0;
+ }
+ else if (ST(0).f == ST(1).f)
+ {
+ cpustate->fpu_status_word |= FPU_C3;
+ }
+ else
+ {
+ // unordered
+ cpustate->fpu_status_word |= (FPU_C3 | FPU_C2 | FPU_C0);
+ }
+ FPU_POP(cpustate);
+ FPU_POP(cpustate);
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ // FDIVP
+ case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
+ {
+ if ((ST(0).i & U64(0x7fffffffffffffff)) == 0)
+ {
+ // set result as infinity if zero divide is masked
+ if (cpustate->fpu_control_word & FPU_MASK_ZERO_DIVIDE)
+ {
+ ST(modrm & 7).i |= FPU_INFINITY_DOUBLE;
+ }
+ }
+ else
+ {
+ ST(modrm & 7).f = ST(modrm & 7).f / ST(0).f;
+ }
+ FPU_POP(cpustate);
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ default:
+ fatalerror("I386: FPU Op DE %02X at %08X", modrm, cpustate->pc-2);
+ }
+ }
+}
+
+static void I386OP(fpu_group_df)(i386_state *cpustate) // Opcode 0xdf
+{
+ UINT8 modrm = FETCH(cpustate);
+
+ if (modrm < 0xc0)
+ {
+ // UINT32 ea = GetEA(cpustate,modrm);
+
+ switch ((modrm >> 3) & 0x7)
+ {
+ default:
+ fatalerror("I386: FPU Op DF %02X at %08X", modrm, cpustate->pc-2);
+ }
+ }
+ else
+ {
+ switch (modrm & 0x3f)
+ {
+ case 0x20: // FSTSW AX
+ {
+ REG16(AX) = (cpustate->fpu_status_word & ~FPU_STACK_TOP_MASK) | (cpustate->fpu_top << 10);
+ CYCLES(cpustate,1); // TODO
+ break;
+ }
+
+ default:
+ fatalerror("I386: FPU Op DF %02X at %08X", modrm, cpustate->pc-2);
+ }
+ }
+}