i386: Improved accuracy of the various emulated models, makes CPU detection programs detect the correct CPU (in MESS).

- eflags mask added
- Initial values for the EAX and EDX registers fixed
- cpuid values improved
- feature flags improved
- initial value for the cr0 register improved
- changed 486 to not support the cpuid instruction by default
- take a trap on invalid instructions instead of throwing a fatalerror
- emulated the undefined flag behavior for the DIV/IDIV instructions on Intel CPUs
This commit is contained in:
Dirk Best 2010-01-15 18:54:23 +00:00
parent 43ce0bbbd1
commit 3997330a94
5 changed files with 80 additions and 48 deletions

View File

@ -94,7 +94,7 @@ static UINT32 get_flags(i386_state *cpustate)
f |= cpustate->IOP1 << 12;
f |= cpustate->IOP2 << 13;
f |= cpustate->NT << 14;
return (cpustate->eflags & 0xFFFF0000) | (f & 0xFFFF);
return (cpustate->eflags & cpustate->eflags_mask) | (f & 0xffff);
}
static void set_flags(i386_state *cpustate, UINT32 f )
@ -111,6 +111,7 @@ static void set_flags(i386_state *cpustate, UINT32 f )
cpustate->IOP1 = (f & 0x1000) ? 1 : 0;
cpustate->IOP2 = (f & 0x2000) ? 1 : 0;
cpustate->NT = (f & 0x4000) ? 1 : 0;
cpustate->eflags = f & cpustate->eflags_mask;
}
static void sib_byte(i386_state *cpustate,UINT8 mod, UINT32* out_ea, UINT8* out_segment)
@ -662,12 +663,17 @@ static CPU_RESET( i386 )
cpustate->a20_mask = ~0;
cpustate->cr[0] = 0;
cpustate->cr[0] = 0x7ffffff0; // reserved bits set to 1
cpustate->eflags = 0;
cpustate->eflags_mask = 0x00030000;
cpustate->eip = 0xfff0;
REG32(EAX) = 0x0308; // Intel 386, stepping D1
REG32(EDX) = 0;
// [11:8] Family
// [ 7:4] Model
// [ 3:0] Stepping ID
// Family 3 (386), Model 0 (DX), Stepping 8 (D1)
REG32(EAX) = 0;
REG32(EDX) = (3 << 8) | (0 << 4) | (8);
build_opcode_table(cpustate, OP_I386);
cpustate->cycle_table_rm = cycle_table_rm[CPU_CYCLES_I386];
@ -1111,12 +1117,17 @@ static CPU_RESET( i486 )
cpustate->a20_mask = ~0;
cpustate->cr[0] = 0;
cpustate->cr[0] = 0x00000010;
cpustate->eflags = 0;
cpustate->eflags_mask = 0x00070000;
cpustate->eip = 0xfff0;
REG32(EAX) = 0x0308; // Intel 386, stepping D1
REG32(EDX) = 0;
// [11:8] Family
// [ 7:4] Model
// [ 3:0] Stepping ID
// Family 4 (486), Model 0/1 (DX), Stepping 3
REG32(EAX) = 0;
REG32(EDX) = (4 << 8) | (0 << 4) | (3);
build_opcode_table(cpustate, OP_I386 | OP_FPU | OP_I486);
cpustate->cycle_table_rm = cycle_table_rm[CPU_CYCLES_I486];
@ -1222,12 +1233,17 @@ static CPU_RESET( pentium )
cpustate->a20_mask = ~0;
cpustate->cr[0] = 0;
cpustate->cr[0] = 0x00000010;
cpustate->eflags = 0;
cpustate->eflags_mask = 0x003b0000;
cpustate->eip = 0xfff0;
REG32(EAX) = 0x0308; // Intel 386, stepping D1
REG32(EDX) = 0;
// [11:8] Family
// [ 7:4] Model
// [ 3:0] Stepping ID
// Family 5 (Pentium), Model 2 (75 - 200MHz), Stepping 5
REG32(EAX) = 0;
REG32(EDX) = (5 << 8) | (2 << 4) | (5);
build_opcode_table(cpustate, OP_I386 | OP_FPU | OP_I486 | OP_PENTIUM);
cpustate->cycle_table_rm = cycle_table_rm[CPU_CYCLES_PENTIUM];
@ -1238,12 +1254,7 @@ static CPU_RESET( pentium )
cpustate->cpuid_id2 = 0x6c65746e; // ntel
cpustate->cpuid_max_input_value_eax = 0x01;
// [11:8] Family
// [ 7:4] Model
// [ 3:0] Stepping ID
// Family 5 (Pentium), Model 2 (75 - 200MHz), Stepping 1
cpustate->cpu_version = (5 << 8) | (2 << 4) | (1);
cpustate->cpu_version = REG32(EDX);
// [ 0:0] FPU on chip
// [ 2:2] I/O breakpoints
@ -1251,7 +1262,7 @@ static CPU_RESET( pentium )
// [ 5:5] Pentium CPU style model specific registers
// [ 7:7] Machine Check Exception
// [ 8:8] CMPXCHG8B instruction
cpustate->feature_flags = 0x00000000;
cpustate->feature_flags = 0x000001bf;
CHANGE_PC(cpustate,cpustate->eip);
}
@ -1353,12 +1364,17 @@ static CPU_RESET( mediagx )
cpustate->a20_mask = ~0;
cpustate->cr[0] = 0;
cpustate->cr[0] = 0x00000010;
cpustate->eflags = 0;
cpustate->eflags_mask = 0x00270000; /* TODO: is this correct? */
cpustate->eip = 0xfff0;
REG32(EAX) = 0x0308; // Intel 386, stepping D1
REG32(EDX) = 0;
// [11:8] Family
// [ 7:4] Model
// [ 3:0] Stepping ID
// Family 4, Model 4 (MediaGX)
REG32(EAX) = 0;
REG32(EDX) = (4 << 8) | (4 << 4) | (1); /* TODO: is this correct? */
build_opcode_table(cpustate, OP_I386 | OP_FPU | OP_I486 | OP_PENTIUM | OP_CYRIX);
cpustate->cycle_table_rm = cycle_table_rm[CPU_CYCLES_MEDIAGX];
@ -1369,19 +1385,9 @@ static CPU_RESET( mediagx )
cpustate->cpuid_id2 = 0x6d616574; // tead
cpustate->cpuid_max_input_value_eax = 0x01;
// [11:8] Family
// [ 7:4] Model
// [ 3:0] Stepping ID
// Family 4, Model 4 (MediaGX)
cpustate->cpu_version = (4 << 8) | (4 << 4) | (1);
cpustate->cpu_version = REG32(EDX);
// [ 0:0] FPU on chip
// [ 2:2] I/O breakpoints
// [ 4:4] Time Stamp Counter
// [ 5:5] Pentium CPU style model specific registers
// [ 7:7] Machine Check Exception
// [ 8:8] CMPXCHG8B instruction
cpustate->feature_flags = 0x00000001;
CHANGE_PC(cpustate,cpustate->eip);

View File

@ -2639,6 +2639,10 @@ static void I386OP(groupF7_16)(i386_state *cpustate) // Opcode 0xf7
} else {
REG16(DX) = (UINT16)remainder;
REG16(AX) = (UINT16)result;
// this flag is actually undefined, enable on non-cyrix
if (cpustate->cpuid_id0 != 0x69727943)
cpustate->CF = 1;
}
} else {
/* TODO: Divide by zero */
@ -2667,6 +2671,10 @@ static void I386OP(groupF7_16)(i386_state *cpustate) // Opcode 0xf7
} else {
REG16(DX) = (UINT16)remainder;
REG16(AX) = (UINT16)result;
// this flag is actually undefined, enable on non-cyrix
if (cpustate->cpuid_id0 != 0x69727943)
cpustate->CF = 1;
}
} else {
/* TODO: Divide by zero */

View File

@ -2023,6 +2023,10 @@ static void I386OP(groupF6_8)(i386_state *cpustate) // Opcode 0xf6
} else {
REG8(AH) = (UINT8)remainder & 0xff;
REG8(AL) = (UINT8)result & 0xff;
// this flag is actually undefined, enable on non-cyrix
if (cpustate->cpuid_id0 != 0x69727943)
cpustate->CF = 1;
}
} else {
/* TODO: Divide by zero */
@ -2051,6 +2055,10 @@ static void I386OP(groupF6_8)(i386_state *cpustate) // Opcode 0xf6
} else {
REG8(AH) = (UINT8)remainder & 0xff;
REG8(AL) = (UINT8)result & 0xff;
// this flag is actually undefined, enable on non-cyrix
if (cpustate->cpuid_id0 != 0x69727943)
cpustate->CF = 1;
}
} else {
/* TODO: Divide by zero */
@ -2361,6 +2369,6 @@ static void I386OP(unimplemented)(i386_state *cpustate)
static void I386OP(invalid)(i386_state *cpustate)
{
//i386_trap(cpustate,6);
fatalerror("i386: Invalid opcode %02X at %08X", cpustate->opcode, cpustate->pc - 1);
logerror("i386: Invalid opcode %02X at %08X\n", cpustate->opcode, cpustate->pc - 1);
i386_trap(cpustate, 6, 0);
}

View File

@ -187,6 +187,7 @@ struct _i386_state
UINT32 pc;
UINT32 prev_eip;
UINT32 eflags;
UINT32 eflags_mask;
UINT8 CF;
UINT8 DF;
UINT8 SF;

View File

@ -2,24 +2,33 @@
static void I486OP(cpuid)(i386_state *cpustate) // Opcode 0x0F A2
{
switch (REG32(EAX))
if (cpustate->cpuid_id0 == 0)
{
case 0:
// this 486 doesn't support the CPUID instruction
logerror("CPUID not supported at %08x!\n", cpustate->eip);
i386_trap(cpustate, 6, 0);
}
else
{
switch (REG32(EAX))
{
REG32(EAX) = cpustate->cpuid_max_input_value_eax;
REG32(EBX) = cpustate->cpuid_id0;
REG32(ECX) = cpustate->cpuid_id2;
REG32(EDX) = cpustate->cpuid_id1;
CYCLES(cpustate,CYCLES_CPUID);
break;
}
case 0:
{
REG32(EAX) = cpustate->cpuid_max_input_value_eax;
REG32(EBX) = cpustate->cpuid_id0;
REG32(ECX) = cpustate->cpuid_id2;
REG32(EDX) = cpustate->cpuid_id1;
CYCLES(cpustate,CYCLES_CPUID);
break;
}
case 1:
{
REG32(EAX) = cpustate->cpu_version;
REG32(EDX) = cpustate->feature_flags;
CYCLES(cpustate,CYCLES_CPUID_EAX1);
break;
case 1:
{
REG32(EAX) = cpustate->cpu_version;
REG32(EDX) = cpustate->feature_flags;
CYCLES(cpustate,CYCLES_CPUID_EAX1);
break;
}
}
}
}