i386: Various fixes. [Barry Rodewald]

- Load LDT before segment registers when switching tasks,
  - Set IF after the instruction following STI,
  - Reset IF after MOV SS or POP SS for one instruction,
  - Fixed LSL instruction when modrm < 0xc0.
This commit is contained in:
Miodrag Milanovic 2011-07-06 07:08:37 +00:00
parent dc74c5f753
commit 9babf1fcf4
6 changed files with 85 additions and 24 deletions

View File

@ -790,6 +790,12 @@ static void i286_task_switch(i386_state *cpustate, UINT16 selector, UINT8 nested
/* Load incoming task state from the new task's TSS */
tss = cpustate->task.base;
cpustate->ldtr.segment = READ16(cpustate,tss+0x2a) & 0xffff;
seg.selector = cpustate->ldtr.segment;
i386_load_protected_mode_segment(cpustate,&seg);
cpustate->ldtr.limit = seg.limit;
cpustate->ldtr.base = seg.base;
cpustate->ldtr.flags = seg.flags;
cpustate->eip = READ16(cpustate,tss+0x0e);
set_flags(cpustate,READ16(cpustate,tss+0x10));
REG16(AX) = READ16(cpustate,tss+0x12);
@ -808,12 +814,6 @@ static void i286_task_switch(i386_state *cpustate, UINT16 selector, UINT8 nested
i386_load_segment_descriptor(cpustate, SS);
cpustate->sreg[DS].selector = READ16(cpustate,tss+0x28) & 0xffff;
i386_load_segment_descriptor(cpustate, DS);
cpustate->ldtr.segment = READ16(cpustate,tss+0x2a) & 0xffff;
seg.selector = cpustate->ldtr.segment;
i386_load_protected_mode_segment(cpustate,&seg);
cpustate->ldtr.limit = seg.limit;
cpustate->ldtr.base = seg.base;
cpustate->ldtr.flags = seg.flags;
/* Set the busy bit in the new task's descriptor */
if(selector & 0x0004)
@ -900,6 +900,12 @@ static void i386_task_switch(i386_state *cpustate, UINT16 selector, UINT8 nested
/* Load incoming task state from the new task's TSS */
tss = cpustate->task.base;
cpustate->ldtr.segment = READ32(cpustate,tss+0x60) & 0xffff;
seg.selector = cpustate->ldtr.segment;
i386_load_protected_mode_segment(cpustate,&seg);
cpustate->ldtr.limit = seg.limit;
cpustate->ldtr.base = seg.base;
cpustate->ldtr.flags = seg.flags;
cpustate->cr[3] = READ32(cpustate,tss+0x1c); // CR3 (PDBR)
cpustate->eip = READ32(cpustate,tss+0x20);
set_flags(cpustate,READ32(cpustate,tss+0x24));
@ -923,12 +929,6 @@ static void i386_task_switch(i386_state *cpustate, UINT16 selector, UINT8 nested
i386_load_segment_descriptor(cpustate, FS);
cpustate->sreg[GS].selector = READ32(cpustate,tss+0x5c) & 0xffff;
i386_load_segment_descriptor(cpustate, GS);
cpustate->ldtr.segment = READ32(cpustate,tss+0x60) & 0xffff;
seg.selector = cpustate->ldtr.segment;
i386_load_protected_mode_segment(cpustate,&seg);
cpustate->ldtr.limit = seg.limit;
cpustate->ldtr.base = seg.base;
cpustate->ldtr.flags = seg.flags;
/* Set the busy bit in the new task's descriptor */
if(selector & 0x0004)
@ -1055,8 +1055,9 @@ static void i386_protected_mode_jump(i386_state *cpustate, UINT16 seg, UINT32 of
{
switch(desc.flags & 0x000f)
{
case 0x01: // 286 Available TSS
case 0x09: // 386 Available TSS
popmessage("JMP: Available TSS.");
logerror("JMP: Available 386 TSS at %08x\n",cpustate->pc);
memset(&desc, 0, sizeof(desc));
desc.selector = segment;
i386_load_protected_mode_segment(cpustate,&desc);
@ -1084,7 +1085,7 @@ static void i386_protected_mode_jump(i386_state *cpustate, UINT16 seg, UINT32 of
break;
case 0x04: // 286 Call Gate
case 0x0c: // 386 Call Gate
popmessage("JMP: Call gate.");
logerror("JMP: Call gate at %08x\n",cpustate->pc);
SetRPL = 1;
memset(&call_gate, 0, sizeof(call_gate));
call_gate.segment = segment;
@ -1167,7 +1168,7 @@ static void i386_protected_mode_jump(i386_state *cpustate, UINT16 seg, UINT32 of
offset = call_gate.offset;
break;
case 0x05: // Task Gate
popmessage("JMP: Task gate.");
logerror("JMP: Task gate at %08x\n",cpustate->pc);
memset(&call_gate, 0, sizeof(call_gate));
call_gate.segment = segment;
i386_load_call_gate(cpustate,&call_gate);
@ -1205,7 +1206,7 @@ static void i386_protected_mode_jump(i386_state *cpustate, UINT16 seg, UINT32 of
FAULT(FAULT_GP,call_gate.selector & 0xfffc)
}
}
if((call_gate.ar & 0x000f) == 0x0009)
if((call_gate.ar & 0x000f) == 0x0009 || (call_gate.ar & 0x000f) == 0x0001)
{
logerror("JMP: Task Gate TSS: Segment is not an available TSS.\n");
FAULT(FAULT_GP,call_gate.selector & 0xfffc)
@ -1344,8 +1345,9 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
{
switch(desc.flags & 0x000f)
{
case 0x01: // Available 286 TSS
case 0x09: // Available 386 TSS
logerror("CALL: Available TSS (386) at %08x\n",cpustate->pc);
logerror("CALL: Available TSS at %08x\n",cpustate->pc);
if(DPL < CPL)
{
logerror("CALL: TSS: DPL is less than CPL.\n");
@ -2579,7 +2581,7 @@ static CPU_RESET( i386 )
cpustate->a20_mask = ~0;
cpustate->cr[0] = 0x7ffffff0; // reserved bits set to 1
cpustate->cr[0] = 0x7fffffe0; // reserved bits set to 1
cpustate->eflags = 0;
cpustate->eflags_mask = 0x00030000;
cpustate->eip = 0xfff0;
@ -2657,6 +2659,11 @@ static CPU_EXECUTE( i386 )
debugger_instruction_hook(device, cpustate->pc);
i386_check_irq_line(cpustate);
if(cpustate->delayed_interrupt_enable != 0)
{
cpustate->IF = 1;
cpustate->delayed_interrupt_enable = 0;
}
I386OP(decode_opcode)(cpustate);
}
cpustate->tsc += (cycles - cpustate->cycles);

View File

@ -1569,6 +1569,12 @@ static void I386OP(pop_gs16)(i386_state *cpustate) // Opcode 0x0f a9
static void I386OP(pop_ss16)(i386_state *cpustate) // Opcode 0x17
{
if(cpustate->IF != 0) // if external interrupts are enabled
{
cpustate->IF = 0; // reset IF for the next instruction
cpustate->delayed_interrupt_enable = 1;
}
cpustate->sreg[SS].selector = POP16(cpustate);
if( PROTECTED_MODE ) {
i386_load_segment_descriptor(cpustate,SS);
@ -3327,7 +3333,15 @@ static void I386OP(lsl_r16_rm16)(i386_state *cpustate) // Opcode 0x0f 0x03
if(PROTECTED_MODE)
{
memset(&seg, 0, sizeof(seg));
seg.selector = LOAD_RM16(modrm);
if(modrm >= 0xc0)
{
seg.selector = LOAD_RM16(modrm);
}
else
{
UINT32 ea = GetEA(cpustate,modrm);
seg.selector = READ16(cpustate,ea);
}
if(seg.selector == 0)
{
SetZF(0); // not a valid segment

View File

@ -1445,6 +1445,12 @@ static void I386OP(pop_gs32)(i386_state *cpustate) // Opcode 0x0f a9
static void I386OP(pop_ss32)(i386_state *cpustate) // Opcode 0x17
{
if(cpustate->IF != 0) // if external interrupts are enabled
{
cpustate->IF = 0; // reset IF for the next instruction
cpustate->delayed_interrupt_enable = 1;
}
cpustate->sreg[SS].selector = POP32(cpustate);
if( PROTECTED_MODE ) {
i386_load_segment_descriptor(cpustate,SS);
@ -3144,7 +3150,15 @@ static void I386OP(lsl_r32_rm32)(i386_state *cpustate) // Opcode 0x0f 0x03
if(PROTECTED_MODE)
{
memset(&seg, 0, sizeof(seg));
seg.selector = LOAD_RM32(modrm);
if(modrm >= 0xc0)
{
seg.selector = LOAD_RM32(modrm);
}
else
{
UINT32 ea = GetEA(cpustate,modrm);
seg.selector = READ32(cpustate,ea);
}
if(seg.selector == 0)
{
SetZF(0); // not a valid segment

View File

@ -753,6 +753,16 @@ static void I386OP(mov_sreg_rm16)(i386_state *cpustate) // Opcode 0x8e
selector = READ16(cpustate,ea);
CYCLES(cpustate,CYCLES_MOV_MEM_SREG);
}
if(s == SS)
{
if(cpustate->IF != 0) // if external interrupts are enabled
{
cpustate->IF = 0; // reset IF for the next instruction
cpustate->delayed_interrupt_enable = 1;
}
}
cpustate->sreg[s].selector = selector;
i386_load_segment_descriptor(cpustate, s );
}
@ -947,7 +957,10 @@ static void I386OP(ins_generic)(i386_state *cpustate, int size)
break;
}
REG32(EDI) += ((cpustate->DF) ? -1 : 1) * size;
if(cpustate->address_size)
REG32(EDI) += ((cpustate->DF) ? -1 : 1) * size;
else
REG16(DI) += ((cpustate->DF) ? -1 : 1) * size;
CYCLES(cpustate,CYCLES_INS); // TODO: Confirm this value
}
@ -994,7 +1007,10 @@ static void I386OP(outs_generic)(i386_state *cpustate, int size)
break;
}
REG32(ESI) += ((cpustate->DF) ? -1 : 1) * size;
if(cpustate->address_size)
REG32(ESI) += ((cpustate->DF) ? -1 : 1) * size;
else
REG16(SI) += ((cpustate->DF) ? -1 : 1) * size;
CYCLES(cpustate,CYCLES_OUTS); // TODO: Confirm this value
}
@ -1550,7 +1566,7 @@ static void I386OP(std)(i386_state *cpustate) // Opcode 0xfd
static void I386OP(sti)(i386_state *cpustate) // Opcode 0xfb
{
cpustate->IF = 1;
cpustate->delayed_interrupt_enable = 1; // IF is set after the next instruction.
CYCLES(cpustate,CYCLES_STI);
}
@ -2336,7 +2352,9 @@ static void I386OP(aam)(i386_state *cpustate) // Opcode 0xd4
static void I386OP(clts)(i386_state *cpustate) // Opcode 0x0f 0x06
{
// TODO: #GP(0) is executed
// Privileged instruction, CPL must be zero. Can be used in real or v86 mode.
if(PROTECTED_MODE && cpustate->CPL != 0)
FAULT(FAULT_GP,0)
cpustate->cr[0] &= ~0x08; /* clear TS bit */
CYCLES(cpustate,CYCLES_CLTS);
}
@ -2364,6 +2382,12 @@ static void I386OP(mov_tr_r32)(i386_state *cpustate) // Opcode 0x0f 26
CYCLES(cpustate,1); // TODO: correct cycle count
}
static void I386OP(loadall)(i386_state *cpustate) // Opcode 0x0f 0x07 (0x0f 0x05 on 80286), undocumented
{
popmessage("LOADALL instruction hit!");
CYCLES(cpustate,1); // TODO: correct cycle count
}
static void I386OP(unimplemented)(i386_state *cpustate)
{
fatalerror("i386: Unimplemented opcode %02X at %08X", cpustate->opcode, cpustate->pc - 1 );

View File

@ -290,6 +290,7 @@ static const X86_OPCODE x86_opcode_table[] =
{ 0x02, OP_2BYTE|OP_I386, I386OP(lar_r16_rm16), I386OP(lar_r32_rm32), },
{ 0x03, OP_2BYTE|OP_I386, I386OP(lsl_r16_rm16), I386OP(lsl_r32_rm32), },
{ 0x06, OP_2BYTE|OP_I386, I386OP(clts), I386OP(clts), },
{ 0x07, OP_2BYTE|OP_I386, I386OP(loadall), I386OP(loadall), },
{ 0x08, OP_2BYTE|OP_I486, I486OP(invd), I486OP(invd), },
{ 0x09, OP_2BYTE|OP_I486, I486OP(wbinvd), I486OP(wbinvd), },
{ 0x0B, OP_2BYTE|OP_I386, I386OP(unimplemented), I386OP(unimplemented), },

View File

@ -229,6 +229,7 @@ struct _i386_state
UINT8 CPL; // current privilege level
UINT8 performed_intersegment_jump;
UINT8 delayed_interrupt_enable;
UINT32 cr[5]; // Control registers
UINT32 dr[8]; // Debug registers