Ok, this is the last opcode based x87 batch, now things needs to be bug fixed ...

This commit is contained in:
Angelo Salese 2012-01-03 20:57:54 +00:00
parent e146bf3d06
commit f7bdcb79cb

View File

@ -1,10 +1,17 @@
// Intel x87 FPU opcodes
// TODO:
// - double check single precision opcodes;
// - opcode cycles (every single CPU has different counting on it);
// - clean-ups;
#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)
#define FPU_MANTISSA_DOUBLE U64(0x000fffffffffffff)
// FPU control word flags
#define FPU_MASK_INVALID_OP 0x0001
@ -79,6 +86,52 @@ INLINE X87_REG X87_FROUND(i386_state *cpustate, X87_REG t)
return t;
}
INLINE X87_REG READ80(i386_state *cpustate,UINT32 ea)
{
X87_REG t;
UINT16 begin;
INT64 exp;
INT64 mantissa;
UINT8 sign;
t.i = READ64(cpustate,ea);
begin = READ16(cpustate,ea+8);
exp = (begin&0x7fff) - 16383;
exp = (exp > 0) ? (exp&0x3ff) : (-exp&0x3ff);
exp += 1023;
mantissa = (t.i >> 11) & (FPU_MANTISSA_DOUBLE);
sign = (begin&0x8000) >> 15;
if(t.i & 0x400)
mantissa++;
t.i = ((UINT64)sign << 63)|((UINT64)exp << 52)|((UINT64)mantissa);
return t;
}
INLINE void WRITE80(i386_state *cpustate,UINT32 ea, X87_REG t)
{
UINT8 sign = (t.i & FPU_SIGN_BIT_DOUBLE) ? 1 : 0;
INT64 exp = (t.i & FPU_INFINITY_DOUBLE) >> 52;
INT64 mantissa = (t.i & FPU_MANTISSA_DOUBLE) << 11;
INT16 begin;
if(t.f != 0)
{
logerror("Check WRITE80 with t.f != 0\n");
//mantissa |= FPU_SIGN_BIT_DOUBLE;
//exp += (16383 - 1023);
}
begin = (sign<<15)|(INT16)(exp);
t.i = mantissa;
WRITE64(cpustate,ea,t.i);
WRITE16(cpustate,ea+8,begin);
}
static void I386OP(fpu_group_d8)(i386_state *cpustate) // Opcode 0xd8
{
UINT8 modrm = FETCH(cpustate);
@ -676,12 +729,18 @@ static void I386OP(fpu_group_da)(i386_state *cpustate) // Opcode 0xda
break;
case 6: // FIDIV
ST(0).i/=t.i;
if(t.i)
ST(0).i/=t.i;
else
fatalerror("Divide-by-zero on FIDIV 0xda 0x06 opcode");
CYCLES(cpustate,84);
break;
case 7: // FIDIVR
ST(0).i = t.i / ST(0).i;
if(ST(0).i)
ST(0).i = t.i / ST(0).i;
else
fatalerror("Divide-by-zero on FIDIV 0xda 0x07 opcode");
CYCLES(cpustate,84);
break;
@ -751,6 +810,24 @@ static void I386OP(fpu_group_db)(i386_state *cpustate) // Opcode 0xdb
break;
}
case 5: // FLD extended
{
X87_REG t;
t = READ80(cpustate,ea);
FPU_PUSH(cpustate,t);
CYCLES(cpustate,6);
break;
}
case 7: // FSTP extended
{
WRITE80(cpustate,ea,ST(0));
FPU_POP(cpustate);
CYCLES(cpustate,6);
break;
}
default:
fatalerror("I386: FPU Op DB %02X at %08X", modrm, cpustate->pc-2);
}
@ -799,13 +876,14 @@ static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
if (modrm < 0xc0)
{
UINT32 ea = GetEA(cpustate,modrm);
X87_REG t;
t.i = READ64(cpustate,ea);
switch ((modrm >> 3) & 0x7)
{
case 0: /* FADD double */
{
X87_REG t;
t.i = READ64(cpustate,ea);
ST(0).f += t.f;
CYCLES(cpustate,8);
break;
@ -813,8 +891,6 @@ static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
case 1: /* FMUL double */
{
X87_REG t;
t.i = READ64(cpustate,ea);
ST(0).f *= t.f;
CYCLES(cpustate,14);
break;
@ -822,8 +898,6 @@ static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
case 2: /* FCOM double */
{
X87_REG t;
t.i = READ64(cpustate,ea);
cpustate->fpu_status_word &= ~(FPU_C3 | FPU_C2 | FPU_C0);
if(ST(0).f == t.f)
cpustate->fpu_status_word |= FPU_C3;
@ -836,8 +910,6 @@ static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
case 3: /* FCOMP double */
{
X87_REG t;
t.i = READ64(cpustate,ea);
cpustate->fpu_status_word &= ~(FPU_C3 | FPU_C2 | FPU_C0);
if(ST(0).f == t.f)
cpustate->fpu_status_word |= FPU_C3;
@ -851,8 +923,6 @@ static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
case 4: /* FSUB double */
{
X87_REG t;
t.i = READ64(cpustate,ea);
ST(0).f -= t.f;
CYCLES(cpustate,8);
break;
@ -860,8 +930,6 @@ static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
case 5: /* FSUBR double */
{
X87_REG t;
t.i = READ64(cpustate,ea);
ST(0).f = t.f - ST(0).f;
CYCLES(cpustate,8);
break;
@ -869,8 +937,6 @@ static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
case 6: /* FDIV double */
{
X87_REG t;
t.i = READ64(cpustate,ea);
if(t.f)
ST(0).f /= t.f;
else
@ -882,8 +948,6 @@ static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
case 7: /* FDIV double */
{
X87_REG t;
t.i = READ64(cpustate,ea);
if(ST(0).f)
ST(0).f = t.f / ST(0).f;
else
@ -915,6 +979,20 @@ static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
break;
}
case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: // FSUBR
{
ST(modrm & 7).f = ST(0).f - ST(modrm & 7).f;
CYCLES(cpustate,8);
break;
}
case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: // FSUB
{
ST(modrm & 7).f -= ST(0).f;
CYCLES(cpustate,8);
break;
}
case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: // FSUBR
{
ST(modrm & 7).f = ST(0).f - ST(modrm & 7).f;
@ -929,9 +1007,9 @@ static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
break;
}
// FDIVR
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
@ -944,7 +1022,26 @@ static void I386OP(fpu_group_dc)(i386_state *cpustate) // Opcode 0xdc
{
ST(modrm & 7).f = ST(0).f / ST(modrm & 7).f;
}
CYCLES(cpustate,1); // TODO
CYCLES(cpustate,73);
break;
}
// FDIV
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(0).f;
}
CYCLES(cpustate,73);
break;
}
@ -994,6 +1091,89 @@ static void I386OP(fpu_group_dd)(i386_state *cpustate) // Opcode 0xdd
break;
}
case 4: // FRSTOR
{
int i;
if( cpustate->operand_size ) // 32-bit real/protected mode
{
cpustate->fpu_control_word = READ16(cpustate,ea);
cpustate->fpu_status_word = READ16(cpustate,ea+4);
cpustate->fpu_tag_word = READ16(cpustate,ea+8);
ea+=28;
}
else // 16-bit real/protected mode
{
cpustate->fpu_control_word = READ16(cpustate,ea);
cpustate->fpu_status_word = READ16(cpustate,ea+2);
cpustate->fpu_tag_word = READ16(cpustate,ea+4);
ea+=14;
}
cpustate->fpu_top = (cpustate->fpu_status_word>>11) & 7;
for(i=0;i<8;i++)
ST(i) = READ80(cpustate,ea+i*10);
CYCLES(cpustate,(cpustate->cr[0] & 1) ? 34 : 44);
break;
}
case 6: // FSAVE
{
int i;
logerror("x87 FSAVE triggered with mode = %d %02x %02x\n",(cpustate->cr[0] & 1)|(cpustate->operand_size & 1)<<1,cpustate->cr[0],cpustate->operand_size);
switch((cpustate->cr[0] & 1)|(cpustate->operand_size & 1)<<1)
{
case 0: // 16-bit real mode
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));
ea+=14;
break;
case 1: // 16-bit protected mode
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));
ea+=14;
break;
case 2: // 32-bit real mode
WRITE16(cpustate,ea, cpustate->fpu_control_word);
WRITE16(cpustate,ea+4, cpustate->fpu_status_word);
WRITE16(cpustate,ea+8, cpustate->fpu_tag_word);
WRITE16(cpustate,ea+12, cpustate->fpu_inst_ptr & 0xffff);
//WRITE16(cpustate,ea+8, (cpustate->fpu_opcode & 0x07ff) | ((cpustate->fpu_inst_ptr & 0x0f0000) >> 4));
WRITE16(cpustate,ea+20, cpustate->fpu_data_ptr & 0xffff);
//WRITE16(cpustate,ea+12, ((cpustate->fpu_inst_ptr & 0x0f0000) >> 4));
WRITE32(cpustate,ea+24, (cpustate->fpu_data_ptr>>16)<<12);
ea+=28;
break;
case 3: // 32-bit protected mode
WRITE16(cpustate,ea, cpustate->fpu_control_word);
WRITE16(cpustate,ea+4, cpustate->fpu_status_word);
WRITE16(cpustate,ea+8, cpustate->fpu_tag_word);
WRITE32(cpustate,ea+12, cpustate->fpu_inst_ptr);
WRITE32(cpustate,ea+16, cpustate->fpu_opcode);
WRITE32(cpustate,ea+20, cpustate->fpu_data_ptr);
WRITE32(cpustate,ea+24, cpustate->fpu_inst_ptr);
ea+=28;
break;
}
for(i=0;i<8;i++)
ST(i) = READ80(cpustate,ea+i*10);
CYCLES(cpustate,(cpustate->cr[0] & 1) ? 56 : 67);
break;
}
case 7: // FSTSW
{
WRITE16(cpustate,ea, (cpustate->fpu_status_word & ~FPU_STACK_TOP_MASK) | (cpustate->fpu_top << 10));
@ -1009,7 +1189,25 @@ static void I386OP(fpu_group_dd)(i386_state *cpustate) // Opcode 0xdd
{
switch (modrm & 0x3f)
{
case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: // FFREE
{
cpustate->fpu_tag_word |= (3<<((modrm & 7)<< 1));
CYCLES(cpustate,3);
break;
}
case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: // FST
{
UINT16 tmp;
ST(modrm & 7) = ST(0);
tmp = (cpustate->fpu_tag_word>>((cpustate->fpu_top&7)<<1))&3;
cpustate->fpu_tag_word &= ~(3<<((modrm & 7)<< 1));
cpustate->fpu_tag_word |= (tmp<<((modrm & 7)<< 1));
CYCLES(cpustate,3);
break;
}
case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: // FSTP
{
UINT16 tmp;
ST(modrm & 7) = ST(0);
@ -1020,6 +1218,59 @@ static void I386OP(fpu_group_dd)(i386_state *cpustate) // Opcode 0xdd
CYCLES(cpustate,3);
break;
}
case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: // FUCOM
{
cpustate->fpu_status_word &= ~(FPU_C3 | FPU_C2 | FPU_C0);
if (ST(0).f > ST(modrm & 7).f)
{
// C3 = 0, C2 = 0, C0 = 0
}
else if (ST(0).f < ST(modrm & 7).f)
{
cpustate->fpu_status_word |= FPU_C0;
}
else if (ST(0).f == ST(modrm & 7).f)
{
cpustate->fpu_status_word |= FPU_C3;
}
else
{
// unordered
cpustate->fpu_status_word |= (FPU_C3 | FPU_C2 | FPU_C0);
}
//FPU_POP(cpustate);
CYCLES(cpustate,4);
break;
}
case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f: // FUCOMP
{
cpustate->fpu_status_word &= ~(FPU_C3 | FPU_C2 | FPU_C0);
if (ST(0).f > ST(modrm & 7).f)
{
// C3 = 0, C2 = 0, C0 = 0
}
else if (ST(0).f < ST(modrm & 7).f)
{
cpustate->fpu_status_word |= FPU_C0;
}
else if (ST(0).f == ST(modrm & 7).f)
{
cpustate->fpu_status_word |= FPU_C3;
}
else
{
// unordered
cpustate->fpu_status_word |= (FPU_C3 | FPU_C2 | FPU_C0);
}
FPU_POP(cpustate);
CYCLES(cpustate,4);
break;
}
default:
fatalerror("I386: FPU Op DD %02X at %08X", modrm, cpustate->pc-2);
}
@ -1032,10 +1283,109 @@ static void I386OP(fpu_group_de)(i386_state *cpustate) // Opcode 0xde
if (modrm < 0xc0)
{
// UINT32 ea = GetEA(cpustate,modrm);
UINT32 ea = GetEA(cpustate,modrm);
INT32 t;
t = (INT32)READ16(cpustate,ea);
switch ((modrm >> 3) & 0x7)
{
case 0: // FIADD single precision
{
ST(0).f+=(double)t;
CYCLES(cpustate,20);
break;
}
case 1: // FIMUL single precision
{
ST(0).f*=(double)t;
CYCLES(cpustate,22);
break;
}
case 2: // FICOM
{
cpustate->fpu_status_word &= ~(FPU_C3 | FPU_C2 | FPU_C0);
if (ST(0).f > (double)t)
{
// C3 = 0, C2 = 0, C0 = 0
}
else if (ST(0).f < (double)t)
{
cpustate->fpu_status_word |= FPU_C0;
}
else if (ST(0).f == (double)t)
{
cpustate->fpu_status_word |= FPU_C3;
}
else
{
// unordered
cpustate->fpu_status_word |= (FPU_C3 | FPU_C2 | FPU_C0);
}
CYCLES(cpustate,15);
}
case 3: // FICOMP
{
cpustate->fpu_status_word &= ~(FPU_C3 | FPU_C2 | FPU_C0);
if (ST(0).f > (double)t)
{
// C3 = 0, C2 = 0, C0 = 0
}
else if (ST(0).f < (double)t)
{
cpustate->fpu_status_word |= FPU_C0;
}
else if (ST(0).f == (double)t)
{
cpustate->fpu_status_word |= FPU_C3;
}
else
{
// unordered
cpustate->fpu_status_word |= (FPU_C3 | FPU_C2 | FPU_C0);
}
FPU_POP(cpustate);
CYCLES(cpustate,15);
}
case 4: // FISUB single precision
{
ST(0).f-=(double)t;
CYCLES(cpustate,20);
break;
}
case 5: // FISUBR single precision
{
ST(0).f = (double)t - ST(0).f;
CYCLES(cpustate,20);
break;
}
case 6: // FIDIV single precision
{
if((double)t == 0)
fatalerror("Divide by zero on x87 opcode 0xde 0x06");
else
ST(0).f/=(double)t;
CYCLES(cpustate,84);
break;
}
case 7: // FIDIVR single precision
{
if(ST(0).f == 0)
fatalerror("Divide by zero on x87 opcode 0xde 0x07");
else
ST(0).f = (double)t / ST(0).f;
CYCLES(cpustate,84);
break;
}
default:
fatalerror("I386: FPU Op DE %02X at %08X", modrm, cpustate->pc-2);
}
@ -1082,7 +1432,7 @@ static void I386OP(fpu_group_de)(i386_state *cpustate) // Opcode 0xde
}
FPU_POP(cpustate);
FPU_POP(cpustate);
CYCLES(cpustate,1); // TODO
CYCLES(cpustate,5);
break;
}
@ -1189,6 +1539,33 @@ static void I386OP(fpu_group_df)(i386_state *cpustate) // Opcode 0xdf
break;
}
case 6: // FBSTP
{
UINT8 res;
double bcd_data;
int i;
bcd_data = ST(0).f;
for(i=0;i<9;i++)
{
res = (UINT8)floor(fmod(bcd_data,10.0));
bcd_data -= floor(fmod(bcd_data,10.0));
bcd_data /= 10.0;
res = (UINT8)floor(fmod(bcd_data,10.0))<<4;
bcd_data -= floor(fmod(bcd_data,10.0));
bcd_data /= 10.0;
WRITE8(cpustate,ea+i,res);
}
res = (UINT8)floor(fmod(bcd_data,10.0));
if(bcd_data < 0)
res |= 0x80;
WRITE8(cpustate,ea+9,res);
FPU_POP(cpustate);
CYCLES(cpustate,1); // TODO
break;
}
case 7: // FISTP long
{
X87_REG t;