summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2011-07-06 07:08:37 +0000
committer Miodrag Milanovic <mmicko@gmail.com>2011-07-06 07:08:37 +0000
commit9babf1fcf48ff30f443d995283f2db592a08d4f1 (patch)
tree53139d650cb0e282f9d1d5a02e09898abd4fd810
parentdc74c5f753c323fe7602eaacd70805ed12a0e7ba (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.c43
-rw-r--r--src/emu/cpu/i386/i386op16.c16
-rw-r--r--src/emu/cpu/i386/i386op32.c16
-rw-r--r--src/emu/cpu/i386/i386ops.c32
-rw-r--r--src/emu/cpu/i386/i386ops.h1
-rw-r--r--src/emu/cpu/i386/i386priv.h1
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