diff options
| author | 2011-07-06 07:08:37 +0000 | |
|---|---|---|
| committer | 2011-07-06 07:08:37 +0000 | |
| commit | 9babf1fcf48ff30f443d995283f2db592a08d4f1 (patch) | |
| tree | 53139d650cb0e282f9d1d5a02e09898abd4fd810 | |
| parent | dc74c5f753c323fe7602eaacd70805ed12a0e7ba (diff) | |
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.
| -rw-r--r-- | src/emu/cpu/i386/i386.c | 43 | ||||
| -rw-r--r-- | src/emu/cpu/i386/i386op16.c | 16 | ||||
| -rw-r--r-- | src/emu/cpu/i386/i386op32.c | 16 | ||||
| -rw-r--r-- | src/emu/cpu/i386/i386ops.c | 32 | ||||
| -rw-r--r-- | src/emu/cpu/i386/i386ops.h | 1 | ||||
| -rw-r--r-- | src/emu/cpu/i386/i386priv.h | 1 |
6 files changed, 85 insertions, 24 deletions
diff --git a/src/emu/cpu/i386/i386.c b/src/emu/cpu/i386/i386.c index 8e199cd6c93..806a6ca8003 100644 --- a/src/emu/cpu/i386/i386.c +++ b/src/emu/cpu/i386/i386.c @@ -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); diff --git a/src/emu/cpu/i386/i386op16.c b/src/emu/cpu/i386/i386op16.c index 59edeb5f1f7..531a08b53e4 100644 --- a/src/emu/cpu/i386/i386op16.c +++ b/src/emu/cpu/i386/i386op16.c @@ -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 diff --git a/src/emu/cpu/i386/i386op32.c b/src/emu/cpu/i386/i386op32.c index 6b0d3868c4e..69ef2d4fd31 100644 --- a/src/emu/cpu/i386/i386op32.c +++ b/src/emu/cpu/i386/i386op32.c @@ -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 diff --git a/src/emu/cpu/i386/i386ops.c b/src/emu/cpu/i386/i386ops.c index c6b8778a728..e2b76d63ec1 100644 --- a/src/emu/cpu/i386/i386ops.c +++ b/src/emu/cpu/i386/i386ops.c @@ -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 ); diff --git a/src/emu/cpu/i386/i386ops.h b/src/emu/cpu/i386/i386ops.h index 4edfe15a85f..a79759faa3b 100644 --- a/src/emu/cpu/i386/i386ops.h +++ b/src/emu/cpu/i386/i386ops.h @@ -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), }, diff --git a/src/emu/cpu/i386/i386priv.h b/src/emu/cpu/i386/i386priv.h index 931c0b5eb65..5088dbda180 100644 --- a/src/emu/cpu/i386/i386priv.h +++ b/src/emu/cpu/i386/i386priv.h @@ -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 |
