i386: Big pmode update. [Carl]

This commit is contained in:
mahlemiut 2012-05-03 05:58:32 +00:00
parent 51c9a46f66
commit 605a48921b
2 changed files with 152 additions and 118 deletions

View File

@ -52,10 +52,15 @@ static UINT32 i386_load_protected_mode_segment(i386_state *cpustate, I386_SREG *
entry = seg->selector & ~0x7; entry = seg->selector & ~0x7;
if (limit == 0 || entry + 7 > limit) if (limit == 0 || entry + 7 > limit)
return 0; return 0;
int cpl = cpustate->CPL;
cpustate->CPL = 0;
v1 = READ32(cpustate, base + entry ); v1 = READ32(cpustate, base + entry );
v2 = READ32(cpustate, base + entry + 4 ); v2 = READ32(cpustate, base + entry + 4 );
cpustate->CPL = cpl;
seg->flags = (v2 >> 8) & 0xf0ff; seg->flags = (v2 >> 8) & 0xf0ff;
seg->base = (v2 & 0xff000000) | ((v2 & 0xff) << 16) | ((v1 >> 16) & 0xffff); seg->base = (v2 & 0xff000000) | ((v2 & 0xff) << 16) | ((v1 >> 16) & 0xffff);
seg->limit = (v2 & 0xf0000) | (v1 & 0xffff); seg->limit = (v2 & 0xf0000) | (v1 & 0xffff);
@ -88,9 +93,14 @@ static void i386_load_call_gate(i386_state* cpustate, I386_CALL_GATE *gate)
if (limit == 0 || entry + 7 > limit) if (limit == 0 || entry + 7 > limit)
return; return;
int cpl = cpustate->CPL;
cpustate->CPL = 0;
v1 = READ32(cpustate, base + entry ); v1 = READ32(cpustate, base + entry );
v2 = READ32(cpustate, base + entry + 4 ); v2 = READ32(cpustate, base + entry + 4 );
cpustate->CPL = cpl;
/* Note that for task gates, offset and dword_count are not used */ /* Note that for task gates, offset and dword_count are not used */
gate->selector = (v1 >> 16) & 0xffff; gate->selector = (v1 >> 16) & 0xffff;
gate->offset = (v1 & 0x0000ffff) | (v2 & 0xffff0000); gate->offset = (v1 & 0x0000ffff) | (v2 & 0xffff0000);
@ -129,35 +139,39 @@ static void i386_load_segment_descriptor(i386_state *cpustate, int segment )
/* Retrieves the stack selector located in the current TSS */ /* Retrieves the stack selector located in the current TSS */
static UINT32 i386_get_stack_segment(i386_state* cpustate, UINT8 privilege) static UINT32 i386_get_stack_segment(i386_state* cpustate, UINT8 privilege)
{ {
UINT32 ret;
if(privilege >= 3) if(privilege >= 3)
return 0; return 0;
return READ32(cpustate,(cpustate->task.base+8) + (8*privilege)); int cpl = cpustate->CPL;
} cpustate->CPL = 0;
static UINT16 i286_get_stack_segment(i386_state* cpustate, UINT8 privilege) if(cpustate->task.flags & 8)
{ ret = READ32(cpustate,(cpustate->task.base+8) + (8*privilege));
if(privilege >= 3) else
return 0; ret = READ16(cpustate,(cpustate->task.base+4) + (4*privilege));
return READ16(cpustate,(cpustate->task.base+4) + (4*privilege)); cpustate->CPL = cpl;
return ret;
} }
/* Retrieves the stack pointer located in the current TSS */ /* Retrieves the stack pointer located in the current TSS */
static UINT32 i386_get_stack_ptr(i386_state* cpustate, UINT8 privilege) static UINT32 i386_get_stack_ptr(i386_state* cpustate, UINT8 privilege)
{ {
UINT32 ret;
if(privilege >= 3) if(privilege >= 3)
return 0; return 0;
return READ32(cpustate,(cpustate->task.base+4) + (8*privilege)); int cpl = cpustate->CPL;
} cpustate->CPL = 0;
static UINT16 i286_get_stack_ptr(i386_state* cpustate, UINT8 privilege) if(cpustate->task.flags & 8)
{ ret = READ32(cpustate,(cpustate->task.base+4) + (8*privilege));
if(privilege >= 3) else
return 0; ret = READ16(cpustate,(cpustate->task.base+2) + (4*privilege));
return READ16(cpustate,(cpustate->task.base+2) + (4*privilege)); cpustate->CPL = cpl;
return ret;
} }
static UINT32 get_flags(i386_state *cpustate) static UINT32 get_flags(i386_state *cpustate)
@ -596,8 +610,10 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
UINT8 CPL = cpustate->CPL, DPL = 0; //, RPL = 0; UINT8 CPL = cpustate->CPL, DPL = 0; //, RPL = 0;
/* 32-bit */ /* 32-bit */
cpustate->CPL = 0;
v1 = READ32(cpustate, cpustate->idtr.base + entry ); v1 = READ32(cpustate, cpustate->idtr.base + entry );
v2 = READ32(cpustate, cpustate->idtr.base + entry + 4 ); v2 = READ32(cpustate, cpustate->idtr.base + entry + 4 );
cpustate->CPL = CPL;
offset = (v2 & 0xffff0000) | (v1 & 0xffff); offset = (v2 & 0xffff0000) | (v1 & 0xffff);
segment = (v1 >> 16) & 0xffff; segment = (v1 >> 16) & 0xffff;
type = (v2>>8) & 0x1F; type = (v2>>8) & 0x1F;
@ -661,6 +677,7 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
/* Get privilege level 0 stack pointer from TSS */ /* Get privilege level 0 stack pointer from TSS */
cpustate->sreg[SS].selector = i386_get_stack_segment(cpustate,0); cpustate->sreg[SS].selector = i386_get_stack_segment(cpustate,0);
REG32(ESP) = i386_get_stack_ptr(cpustate,0); REG32(ESP) = i386_get_stack_ptr(cpustate,0);
cpustate->CPL = segment & 0x03;
i386_load_segment_descriptor(cpustate,SS); i386_load_segment_descriptor(cpustate,SS);
PUSH32(cpustate,cpustate->sreg[GS].selector & 0xffff); PUSH32(cpustate,cpustate->sreg[GS].selector & 0xffff);
PUSH32(cpustate,cpustate->sreg[FS].selector & 0xffff); PUSH32(cpustate,cpustate->sreg[FS].selector & 0xffff);
@ -686,7 +703,6 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
cpustate->sreg[CS].selector = segment; cpustate->sreg[CS].selector = segment;
cpustate->eip = offset; cpustate->eip = offset;
// CPL set to CS RPL? // CPL set to CS RPL?
cpustate->CPL = segment & 0x03;
i386_load_segment_descriptor(cpustate,CS); i386_load_segment_descriptor(cpustate,CS);
CHANGE_PC(cpustate,cpustate->eip); CHANGE_PC(cpustate,cpustate->eip);
@ -702,25 +718,25 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
if(segment & 0x04) if(segment & 0x04)
{ {
logerror("IRQ: Task gate: TSS is not in the GDT.\n"); logerror("IRQ: Task gate: TSS is not in the GDT.\n");
FAULT_EXP(FAULT_TS,segment & ~0x07); FAULT_EXP(FAULT_TS,segment & ~0x03);
} }
else else
{ {
if(segment > cpustate->gdtr.limit) if(segment > cpustate->gdtr.limit)
{ {
logerror("IRQ: Task gate: TSS is past GDT limit.\n"); logerror("IRQ: Task gate: TSS is past GDT limit.\n");
FAULT_EXP(FAULT_TS,segment & ~0x07); FAULT_EXP(FAULT_TS,segment & ~0x03);
} }
} }
if((desc.flags & 0x000f) != 0x09 && (desc.flags & 0x000f) != 0x01) if((desc.flags & 0x000f) != 0x09 && (desc.flags & 0x000f) != 0x01)
{ {
logerror("IRQ: Task gate: TSS is not an available TSS.\n"); logerror("IRQ: Task gate: TSS is not an available TSS.\n");
FAULT_EXP(FAULT_TS,segment & ~0x07); FAULT_EXP(FAULT_TS,segment & ~0x03);
} }
if((desc.flags & 0x0080) == 0) if((desc.flags & 0x0080) == 0)
{ {
logerror("IRQ: Task gate: TSS is not present.\n"); logerror("IRQ: Task gate: TSS is not present.\n");
FAULT_EXP(FAULT_NP,segment & ~0x07); FAULT_EXP(FAULT_NP,segment & ~0x03);
} }
if(!(irq == 3 || irq == 4 || irq == 9 || irq_gate == 1)) if(!(irq == 3 || irq == 4 || irq == 9 || irq_gate == 1))
cpustate->eip = cpustate->prev_eip; cpustate->eip = cpustate->prev_eip;
@ -750,7 +766,7 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
if((segment & ~0x07) > cpustate->ldtr.limit) if((segment & ~0x07) > cpustate->ldtr.limit)
{ {
logerror("IRQ: Gate segment is past LDT limit.\n"); logerror("IRQ: Gate segment is past LDT limit.\n");
FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext) FAULT_EXP(FAULT_GP,(segment & 0x03)+cpustate->ext)
} }
} }
else else
@ -758,18 +774,18 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
if((segment & ~0x07) > cpustate->gdtr.limit) if((segment & ~0x07) > cpustate->gdtr.limit)
{ {
logerror("IRQ: Gate segment is past GDT limit.\n"); logerror("IRQ: Gate segment is past GDT limit.\n");
FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext) FAULT_EXP(FAULT_GP,(segment & 0x03)+cpustate->ext)
} }
} }
if((desc.flags & 0x0018) != 0x18) if((desc.flags & 0x0018) != 0x18)
{ {
logerror("IRQ: Gate descriptor is not a code segment.\n"); logerror("IRQ: Gate descriptor is not a code segment.\n");
FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext) FAULT_EXP(FAULT_GP,(segment & 0x03)+cpustate->ext)
} }
if((desc.flags & 0x0080) == 0) if((desc.flags & 0x0080) == 0)
{ {
logerror("IRQ: Gate segment is not present.\n"); logerror("IRQ: Gate segment is not present.\n");
FAULT_EXP(FAULT_NP,(segment & 0x07)+cpustate->ext) FAULT_EXP(FAULT_NP,(segment & 0x03)+cpustate->ext)
} }
if((desc.flags & 0x0004) == 0 && (DPL < CPL)) if((desc.flags & 0x0004) == 0 && (DPL < CPL))
{ {
@ -779,17 +795,14 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
/* Check new stack segment in TSS */ /* Check new stack segment in TSS */
memset(&stack, 0, sizeof(stack)); memset(&stack, 0, sizeof(stack));
if(flags & 0x0008) stack.selector = i386_get_stack_segment(cpustate,DPL);
stack.selector = i386_get_stack_segment(cpustate,DPL);
else
stack.selector = i286_get_stack_segment(cpustate,DPL);
i386_load_protected_mode_segment(cpustate,&stack,NULL); i386_load_protected_mode_segment(cpustate,&stack,NULL);
oldSS = cpustate->sreg[SS].selector; oldSS = cpustate->sreg[SS].selector;
if(flags & 0x0008) if(flags & 0x0008)
oldESP = REG32(ESP); oldESP = REG32(ESP);
else else
oldESP = REG16(SP); oldESP = REG16(SP);
if((stack.selector & ~0x07) == 0) if((stack.selector & ~0x03) == 0)
{ {
logerror("IRQ: New stack selector is null.\n"); logerror("IRQ: New stack selector is null.\n");
FAULT_EXP(FAULT_GP,cpustate->ext) FAULT_EXP(FAULT_GP,cpustate->ext)
@ -799,7 +812,7 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
if((stack.selector & ~0x07) > cpustate->ldtr.base) if((stack.selector & ~0x07) > cpustate->ldtr.base)
{ {
logerror("IRQ: New stack selector is past LDT limit.\n"); logerror("IRQ: New stack selector is past LDT limit.\n");
FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext)
} }
} }
else else
@ -807,32 +820,32 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
if((stack.selector & ~0x07) > cpustate->gdtr.base) if((stack.selector & ~0x07) > cpustate->gdtr.base)
{ {
logerror("IRQ: New stack selector is past GDT limit.\n"); logerror("IRQ: New stack selector is past GDT limit.\n");
FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext)
} }
} }
if((stack.selector & 0x03) != DPL) if((stack.selector & 0x03) != DPL)
{ {
logerror("IRQ: New stack selector RPL is not equal to code segment DPL.\n"); logerror("IRQ: New stack selector RPL is not equal to code segment DPL.\n");
FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext)
} }
if(((stack.flags >> 5) & 0x03) != DPL) if(((stack.flags >> 5) & 0x03) != DPL)
{ {
logerror("IRQ: New stack segment DPL is not equal to code segment DPL.\n"); logerror("IRQ: New stack segment DPL is not equal to code segment DPL.\n");
FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext)
} }
if(((stack.flags & 0x0018) != 0x10) && (stack.flags & 0x0002) != 0) if(((stack.flags & 0x0018) != 0x10) && (stack.flags & 0x0002) != 0)
{ {
logerror("IRQ: New stack segment is not a writable data segment.\n"); logerror("IRQ: New stack segment is not a writable data segment.\n");
FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) // #TS(stack selector + EXT) FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext) // #TS(stack selector + EXT)
} }
if((stack.flags & 0x0080) == 0) if((stack.flags & 0x0080) == 0)
{ {
logerror("IRQ: New stack segment is not present.\n"); logerror("IRQ: New stack segment is not present.\n");
FAULT_EXP(FAULT_SS,(stack.selector & ~0x07)+cpustate->ext) // #TS(stack selector + EXT) FAULT_EXP(FAULT_SS,(stack.selector & ~0x03)+cpustate->ext) // #TS(stack selector + EXT)
} }
newESP = i386_get_stack_ptr(cpustate,DPL);
if(type & 0x08) // 32-bit gate if(type & 0x08) // 32-bit gate
{ {
newESP = i386_get_stack_ptr(cpustate,DPL);
if(newESP < 20) if(newESP < 20)
{ {
logerror("IRQ: New stack has no space for return addresses.\n"); logerror("IRQ: New stack has no space for return addresses.\n");
@ -841,7 +854,7 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
} }
else // 16-bit gate else // 16-bit gate
{ {
newESP = i286_get_stack_ptr(cpustate,DPL); newESP &= 0xffff;
if(newESP < 10) if(newESP < 10)
{ {
logerror("IRQ: New stack has no space for return addresses.\n"); logerror("IRQ: New stack has no space for return addresses.\n");
@ -853,13 +866,17 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
logerror("IRQ: New EIP is past code segment limit.\n"); logerror("IRQ: New EIP is past code segment limit.\n");
FAULT_EXP(FAULT_GP,0) FAULT_EXP(FAULT_GP,0)
} }
/* change CPL before accessing the stack */
cpustate->CPL = DPL;
/* check for page fault at new stack TODO: check if stack frame crosses page boundary */
WRITE_TEST(cpustate, stack.base+newESP-1);
/* Load new stack segment descriptor */ /* Load new stack segment descriptor */
cpustate->sreg[SS].selector = stack.selector; cpustate->sreg[SS].selector = stack.selector;
i386_load_segment_descriptor(cpustate,SS); i386_load_segment_descriptor(cpustate,SS);
if(flags & 0x0008) if(flags & 0x0008)
REG32(ESP) = i386_get_stack_ptr(cpustate,DPL); REG32(ESP) = i386_get_stack_ptr(cpustate,DPL);
else else
REG16(SP) = i286_get_stack_ptr(cpustate,DPL); REG16(SP) = i386_get_stack_ptr(cpustate,DPL);
if(type & 0x08) if(type & 0x08)
{ {
// 32-bit gate // 32-bit gate
@ -872,7 +889,6 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level
PUSH16(cpustate,oldSS); PUSH16(cpustate,oldSS);
PUSH16(cpustate,oldESP); PUSH16(cpustate,oldESP);
} }
cpustate->CPL = DPL;
SetRPL = 1; SetRPL = 1;
} }
else else
@ -951,6 +967,8 @@ static void i386_trap_with_error(i386_state *cpustate,int irq, int irq_gate, int
{ {
UINT32 entry = irq * 8; UINT32 entry = irq * 8;
UINT32 v2,type; UINT32 v2,type;
int cpl = cpustate->CPL;
cpustate->CPL = 0;
v2 = READ32(cpustate, cpustate->idtr.base + entry + 4 ); v2 = READ32(cpustate, cpustate->idtr.base + entry + 4 );
type = (v2>>8) & 0x1F; type = (v2>>8) & 0x1F;
if(type == 5) if(type == 5)
@ -959,6 +977,7 @@ static void i386_trap_with_error(i386_state *cpustate,int irq, int irq_gate, int
v2 = READ32(cpustate, cpustate->gdtr.base + ((v2 >> 16) & 0xfff8) + 4); v2 = READ32(cpustate, cpustate->gdtr.base + ((v2 >> 16) & 0xfff8) + 4);
type = (v2>>8) & 0x1F; type = (v2>>8) & 0x1F;
} }
cpustate->CPL = cpl;
if(type >= 9) if(type >= 9)
PUSH32(cpustate,error); PUSH32(cpustate,error);
else else
@ -1136,6 +1155,7 @@ static void i386_task_switch(i386_state *cpustate, UINT16 selector, UINT8 nested
cpustate->cr[0] |= 0x08; cpustate->cr[0] |= 0x08;
/* Load incoming task state from the new task's TSS */ /* Load incoming task state from the new task's TSS */
cpustate->CPL = 0;
tss = cpustate->task.base; tss = cpustate->task.base;
cpustate->ldtr.segment = READ32(cpustate,tss+0x60) & 0xffff; cpustate->ldtr.segment = READ32(cpustate,tss+0x60) & 0xffff;
seg.selector = cpustate->ldtr.segment; seg.selector = cpustate->ldtr.segment;
@ -1351,19 +1371,17 @@ static void i386_protected_mode_jump(i386_state *cpustate, UINT16 seg, UINT32 of
} }
if(call_gate.selector & 0x04) if(call_gate.selector & 0x04)
{ {
/* check GDT limit */ if((call_gate.selector & ~0x07) > cpustate->ldtr.limit)
if((call_gate.selector & ~0x07) > cpustate->gdtr.limit)
{ {
logerror("JMP: Call Gate: Gate Selector is past GDT segment limit\n"); logerror("JMP: Call Gate: Gate Selector is past LDT segment limit\n");
FAULT(FAULT_GP,call_gate.selector & 0xfffc) FAULT(FAULT_GP,call_gate.selector & 0xfffc)
} }
} }
else else
{ {
/* check LDT limit */ if((call_gate.selector & ~0x07) > cpustate->gdtr.limit)
if((call_gate.selector & ~0x07) > cpustate->ldtr.limit)
{ {
logerror("JMP: Call Gate: Gate Selector is past LDT segment limit\n"); logerror("JMP: Call Gate: Gate Selector is past GDT segment limit\n");
FAULT(FAULT_GP,call_gate.selector & 0xfffc) FAULT(FAULT_GP,call_gate.selector & 0xfffc)
} }
} }
@ -1638,7 +1656,7 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
FAULT(FAULT_NP,desc.selector & ~0x03) // #GP(selector) FAULT(FAULT_NP,desc.selector & ~0x03) // #GP(selector)
} }
desc.selector = gate.selector; desc.selector = gate.selector;
if((gate.selector & ~0x07) == 0) if((gate.selector & ~0x03) == 0)
{ {
logerror("CALL: Call gate: Segment is null.\n"); logerror("CALL: Call gate: Segment is null.\n");
FAULT(FAULT_GP,0) // #GP(0) FAULT(FAULT_GP,0) // #GP(0)
@ -1684,12 +1702,9 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
/* more privilege */ /* more privilege */
/* Check new SS segment for privilege level from TSS */ /* Check new SS segment for privilege level from TSS */
memset(&stack, 0, sizeof(stack)); memset(&stack, 0, sizeof(stack));
if(operand32 != 0) stack.selector = i386_get_stack_segment(cpustate,DPL);
stack.selector = i386_get_stack_segment(cpustate,DPL);
else
stack.selector = i286_get_stack_segment(cpustate,DPL);
i386_load_protected_mode_segment(cpustate,&stack,NULL); i386_load_protected_mode_segment(cpustate,&stack,NULL);
if((stack.selector & ~0x07) == 0) if((stack.selector & ~0x03) == 0)
{ {
logerror("CALL: Call gate: TSS selector is null\n"); logerror("CALL: Call gate: TSS selector is null\n");
FAULT(FAULT_TS,0) // #TS(0) FAULT(FAULT_TS,0) // #TS(0)
@ -1730,9 +1745,9 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
logerror("CALL: Call gate: Stack segment is not present\n"); logerror("CALL: Call gate: Stack segment is not present\n");
FAULT(FAULT_SS,stack.selector) // #SS(SS selector) FAULT(FAULT_SS,stack.selector) // #SS(SS selector)
} }
UINT32 newESP = i386_get_stack_ptr(cpustate,DPL);
if(operand32 != 0) if(operand32 != 0)
{ {
UINT32 newESP = i386_get_stack_ptr(cpustate,DPL);
if(newESP < ((gate.dword_count & 0x1f) + 16)) if(newESP < ((gate.dword_count & 0x1f) + 16))
{ {
logerror("CALL: Call gate: New stack has no room for 32-bit return address and parameters.\n"); logerror("CALL: Call gate: New stack has no room for 32-bit return address and parameters.\n");
@ -1746,7 +1761,7 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
} }
else else
{ {
UINT32 newESP = i286_get_stack_ptr(cpustate,DPL) & 0x0000ffff; newESP &= 0x0000ffff;
if(newESP < ((gate.dword_count & 0x1f) + 8)) if(newESP < ((gate.dword_count & 0x1f) + 8))
{ {
logerror("CALL: Call gate: New stack has no room for 16-bit return address and parameters.\n"); logerror("CALL: Call gate: New stack has no room for 16-bit return address and parameters.\n");
@ -1761,23 +1776,25 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
selector = gate.selector; selector = gate.selector;
offset = gate.offset; offset = gate.offset;
cpustate->CPL = (stack.flags >> 5) & 0x03;
/* check for page fault at new stack TODO: check if stack frame crosses page boundary */
WRITE_TEST(cpustate, stack.base+newESP-1);
/* switch to new stack */ /* switch to new stack */
oldSS = cpustate->sreg[SS].selector; oldSS = cpustate->sreg[SS].selector;
cpustate->sreg[SS].selector = i386_get_stack_segment(cpustate,gate.selector & 0x03);
if(operand32 != 0) if(operand32 != 0)
{ {
oldESP = REG32(ESP); oldESP = REG32(ESP);
cpustate->sreg[SS].selector = i386_get_stack_segment(cpustate,gate.selector & 0x03);
} }
else else
{ {
oldESP = REG16(SP); oldESP = REG16(SP);
cpustate->sreg[SS].selector = i286_get_stack_segment(cpustate,gate.selector & 0x03);
} }
i386_load_segment_descriptor(cpustate, SS ); i386_load_segment_descriptor(cpustate, SS );
if(operand32 != 0) if(operand32 != 0)
REG32(ESP) = i386_get_stack_ptr(cpustate,gate.selector & 0x03); REG32(ESP) = i386_get_stack_ptr(cpustate,gate.selector & 0x03);
else else
REG16(SP) = i286_get_stack_ptr(cpustate,gate.selector & 0x03) & 0x0000ffff; REG16(SP) = i386_get_stack_ptr(cpustate,gate.selector & 0x03) & 0x0000ffff;
if(operand32 != 0) if(operand32 != 0)
{ {
@ -1796,10 +1813,13 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
/* copy parameters from old stack to new stack */ /* copy parameters from old stack to new stack */
for(x=(gate.dword_count & 0x1f)-1;x>=0;x--) for(x=(gate.dword_count & 0x1f)-1;x>=0;x--)
{ {
UINT32 addr = temp.base + oldESP + (x*2); UINT32 addr = oldESP + (operand32?(x*4):(x*2));
PUSH16(cpustate,READ16(cpustate,addr)); addr = temp.base + (temp.d?addr:(addr&0xffff));
if(operand32)
PUSH32(cpustate,READ32(cpustate,addr));
else
PUSH16(cpustate,READ16(cpustate,addr));
} }
cpustate->CPL = (stack.flags >> 5) & 0x03;
SetRPL = 1; SetRPL = 1;
} }
else else
@ -1842,17 +1862,17 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
if(DPL < CPL) if(DPL < CPL)
{ {
logerror("CALL: Task Gate: Gate DPL is less than CPL.\n"); logerror("CALL: Task Gate: Gate DPL is less than CPL.\n");
FAULT(FAULT_TS,selector & ~0x07) // #TS(selector) FAULT(FAULT_TS,selector & ~0x03) // #TS(selector)
} }
if(DPL < RPL) if(DPL < RPL)
{ {
logerror("CALL: Task Gate: Gate DPL is less than RPL.\n"); logerror("CALL: Task Gate: Gate DPL is less than RPL.\n");
FAULT(FAULT_TS,selector & ~0x07) // #TS(selector) FAULT(FAULT_TS,selector & ~0x03) // #TS(selector)
} }
if(gate.ar & 0x0080) if(gate.ar & 0x0080)
{ {
logerror("CALL: Task Gate: Gate is not present.\n"); logerror("CALL: Task Gate: Gate is not present.\n");
FAULT(FAULT_NP,selector & ~0x07) // #NP(selector) FAULT(FAULT_NP,selector & ~0x03) // #NP(selector)
} }
/* Check the TSS that the task gate points to */ /* Check the TSS that the task gate points to */
desc.selector = gate.selector; desc.selector = gate.selector;
@ -1860,25 +1880,25 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
if(gate.selector & 0x04) if(gate.selector & 0x04)
{ {
logerror("CALL: Task Gate: TSS is not global.\n"); logerror("CALL: Task Gate: TSS is not global.\n");
FAULT(FAULT_TS,gate.selector & ~0x07) // #TS(selector) FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector)
} }
else else
{ {
if((gate.selector & ~0x07) > cpustate->gdtr.limit) if((gate.selector & ~0x07) > cpustate->gdtr.limit)
{ {
logerror("CALL: Task Gate: TSS is past GDT limit.\n"); logerror("CALL: Task Gate: TSS is past GDT limit.\n");
FAULT(FAULT_TS,gate.selector & ~0x07) // #TS(selector) FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector)
} }
} }
if(desc.flags & 0x0002) if(desc.flags & 0x0002)
{ {
logerror("CALL: Task Gate: TSS is busy.\n"); logerror("CALL: Task Gate: TSS is busy.\n");
FAULT(FAULT_TS,gate.selector & ~0x07) // #TS(selector) FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector)
} }
if(desc.flags & 0x0080) if(desc.flags & 0x0080)
{ {
logerror("CALL: Task Gate: TSS is not present.\n"); logerror("CALL: Task Gate: TSS is not present.\n");
FAULT(FAULT_NP,gate.selector & ~0x07) // #TS(selector) FAULT(FAULT_NP,gate.selector & ~0x03) // #TS(selector)
} }
if(desc.flags & 0x08) if(desc.flags & 0x08)
i386_task_switch(cpustate,desc.selector,1); // with nesting i386_task_switch(cpustate,desc.selector,1); // with nesting
@ -1981,17 +2001,17 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
} }
if(desc.flags & 0x0004) if(desc.flags & 0x0004)
{ {
if(DPL > CPL) if(DPL > RPL)
{ {
logerror("RETF: Conforming code segment DPL is greater than CPL.\n"); logerror("RETF: Conforming code segment DPL is greater than CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x03) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
else else
{ {
if(DPL != CPL) if(DPL != RPL)
{ {
logerror("RETF: Non-conforming code segment DPL does not equal CPL.\n"); logerror("RETF: Non-conforming code segment DPL does not equal CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x03) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
@ -2061,7 +2081,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
if((newCS & ~0x07) >= cpustate->ldtr.limit) if((newCS & ~0x07) >= cpustate->ldtr.limit)
{ {
logerror("RETF: CS segment selector is past LDT limit.\n"); logerror("RETF: CS segment selector is past LDT limit.\n");
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
else else
@ -2069,20 +2089,20 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
if((newCS & ~0x07) >= cpustate->gdtr.limit) if((newCS & ~0x07) >= cpustate->gdtr.limit)
{ {
logerror("RETF: CS segment selector is past GDT limit.\n"); logerror("RETF: CS segment selector is past GDT limit.\n");
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
if((desc.flags & 0x0018) != 0x0018) if((desc.flags & 0x0018) != 0x0018)
{ {
logerror("RETF: CS segment is not a code segment.\n"); logerror("RETF: CS segment is not a code segment.\n");
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
if(desc.flags & 0x0004) if(desc.flags & 0x0004)
{ {
if(DPL > RPL) if(DPL > RPL)
{ {
logerror("RETF: Conforming CS segment DPL is greater than return selector RPL.\n"); logerror("RETF: Conforming CS segment DPL is greater than return selector RPL.\n");
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
else else
@ -2090,13 +2110,13 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
if(DPL != RPL) if(DPL != RPL)
{ {
logerror("RETF: Non-conforming CS segment DPL is not equal to return selector RPL.\n"); logerror("RETF: Non-conforming CS segment DPL is not equal to return selector RPL.\n");
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
if((desc.flags & 0x0080) == 0) if((desc.flags & 0x0080) == 0)
{ {
logerror("RETF: CS segment is not present.\n"); logerror("RETF: CS segment is not present.\n");
FAULT(FAULT_NP,newCS & ~0x07) FAULT(FAULT_NP,newCS & ~0x03)
} }
if(newEIP > desc.limit) if(newEIP > desc.limit)
{ {
@ -2131,7 +2151,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
if((newSS & ~0x07) > cpustate->ldtr.limit) if((newSS & ~0x07) > cpustate->ldtr.limit)
{ {
logerror("RETF (%08x): SS segment selector is past LDT limit.\n",cpustate->pc); logerror("RETF (%08x): SS segment selector is past LDT limit.\n",cpustate->pc);
FAULT(FAULT_GP,newSS & ~0x07) FAULT(FAULT_GP,newSS & ~0x03)
} }
} }
else else
@ -2139,28 +2159,28 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
if((newSS & ~0x07) > cpustate->gdtr.limit) if((newSS & ~0x07) > cpustate->gdtr.limit)
{ {
logerror("RETF (%08x): SS segment selector is past GDT limit.\n",cpustate->pc); logerror("RETF (%08x): SS segment selector is past GDT limit.\n",cpustate->pc);
FAULT(FAULT_GP,newSS & ~0x07) FAULT(FAULT_GP,newSS & ~0x03)
} }
} }
if((newSS & 0x03) != RPL) if((newSS & 0x03) != RPL)
{ {
logerror("RETF: SS segment RPL is not equal to CS segment RPL.\n"); logerror("RETF: SS segment RPL is not equal to CS segment RPL.\n");
FAULT(FAULT_GP,newSS & ~0x07) FAULT(FAULT_GP,newSS & ~0x03)
} }
if((desc.flags & 0x0018) != 0x0010 || (desc.flags & 0x0002) == 0) if((desc.flags & 0x0018) != 0x0010 || (desc.flags & 0x0002) == 0)
{ {
logerror("RETF: SS segment is not a writable data segment.\n"); logerror("RETF: SS segment is not a writable data segment.\n");
FAULT(FAULT_GP,newSS & ~0x07) FAULT(FAULT_GP,newSS & ~0x03)
} }
if(((desc.flags >> 5) & 0x03) != RPL) if(((desc.flags >> 5) & 0x03) != RPL)
{ {
logerror("RETF: SS DPL is not equal to CS segment RPL.\n"); logerror("RETF: SS DPL is not equal to CS segment RPL.\n");
FAULT(FAULT_GP,newSS & ~0x07) FAULT(FAULT_GP,newSS & ~0x03)
} }
if((desc.flags & 0x0080) == 0) if((desc.flags & 0x0080) == 0)
{ {
logerror("RETF: SS segment is not present.\n"); logerror("RETF: SS segment is not present.\n");
FAULT(FAULT_GP,newSS & ~0x07) FAULT(FAULT_GP,newSS & ~0x03)
} }
cpustate->CPL = newCS & 0x03; cpustate->CPL = newCS & 0x03;
@ -2249,12 +2269,12 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if(task & 0x04) if(task & 0x04)
{ {
logerror("IRET: Task return: Back-linked TSS is not in GDT.\n"); logerror("IRET: Task return: Back-linked TSS is not in GDT.\n");
FAULT(FAULT_TS,task & ~0x07) FAULT(FAULT_TS,task & ~0x03)
} }
if((task & ~0x07) >= cpustate->gdtr.limit) if((task & ~0x07) >= cpustate->gdtr.limit)
{ {
logerror("IRET: Task return: Back-linked TSS is not in GDT.\n"); logerror("IRET: Task return: Back-linked TSS is not in GDT.\n");
FAULT(FAULT_TS,task & ~0x07) FAULT(FAULT_TS,task & ~0x03)
} }
memset(&desc, 0, sizeof(desc)); memset(&desc, 0, sizeof(desc));
desc.selector = task; desc.selector = task;
@ -2262,12 +2282,12 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((desc.flags & 0x001f) != 0x000b) if((desc.flags & 0x001f) != 0x000b)
{ {
logerror("IRET (%08x): Task return: Back-linked TSS is not a busy TSS.\n",cpustate->pc); logerror("IRET (%08x): Task return: Back-linked TSS is not a busy TSS.\n",cpustate->pc);
FAULT(FAULT_TS,task & ~0x07) FAULT(FAULT_TS,task & ~0x03)
} }
if((desc.flags & 0x0080) == 0) if((desc.flags & 0x0080) == 0)
{ {
logerror("IRET: Task return: Back-linked TSS is not present.\n"); logerror("IRET: Task return: Back-linked TSS is not present.\n");
FAULT(FAULT_NP,task & ~0x07) FAULT(FAULT_NP,task & ~0x03)
} }
if(desc.flags & 0x08) if(desc.flags & 0x08)
i386_task_switch(cpustate,desc.selector,0); i386_task_switch(cpustate,desc.selector,0);
@ -2462,7 +2482,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if(RPL < CPL) if(RPL < CPL)
{ {
logerror("IRET (%08x): Return CS RPL is less than CPL.\n",cpustate->pc); logerror("IRET (%08x): Return CS RPL is less than CPL.\n",cpustate->pc);
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
if(RPL == CPL) if(RPL == CPL)
{ {
@ -2495,7 +2515,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newCS & ~0x07) >= cpustate->ldtr.limit) if((newCS & ~0x07) >= cpustate->ldtr.limit)
{ {
logerror("IRET: Return CS selector (%04x) is past LDT limit.\n",newCS); logerror("IRET: Return CS selector (%04x) is past LDT limit.\n",newCS);
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
else else
@ -2503,7 +2523,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newCS & ~0x07) >= cpustate->gdtr.limit) if((newCS & ~0x07) >= cpustate->gdtr.limit)
{ {
logerror("IRET: Return CS selector is past GDT limit.\n"); logerror("IRET: Return CS selector is past GDT limit.\n");
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
memset(&desc, 0, sizeof(desc)); memset(&desc, 0, sizeof(desc));
@ -2518,24 +2538,24 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
} }
if(desc.flags & 0x0004) if(desc.flags & 0x0004)
{ {
if(DPL > CPL) if(DPL > RPL)
{ {
logerror("IRET: Conforming return CS DPL is greater than CPL.\n"); logerror("IRET: Conforming return CS DPL is greater than CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
else else
{ {
if(DPL != CPL) if(DPL != RPL)
{ {
logerror("IRET: Non-conforming return CS DPL is not equal to CPL.\n"); logerror("IRET: Non-conforming return CS DPL is not equal to CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
if((desc.flags & 0x0080) == 0) if((desc.flags & 0x0080) == 0)
{ {
logerror("IRET: Return CS segment is not present.\n"); logerror("IRET: Return CS segment is not present.\n");
FAULT(FAULT_NP,newCS & ~0x07) FAULT(FAULT_NP,newCS & ~0x03)
} }
if(newEIP > desc.limit) if(newEIP > desc.limit)
{ {
@ -2601,7 +2621,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newCS & ~0x07) >= cpustate->ldtr.limit) if((newCS & ~0x07) >= cpustate->ldtr.limit)
{ {
logerror("IRET: Return CS selector is past LDT limit.\n"); logerror("IRET: Return CS selector is past LDT limit.\n");
FAULT(FAULT_GP,newCS & ~0x07); FAULT(FAULT_GP,newCS & ~0x03);
} }
} }
else else
@ -2609,20 +2629,20 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newCS & ~0x07) >= cpustate->gdtr.limit) if((newCS & ~0x07) >= cpustate->gdtr.limit)
{ {
logerror("IRET: Return CS selector is past GDT limit.\n"); logerror("IRET: Return CS selector is past GDT limit.\n");
FAULT(FAULT_GP,newCS & ~0x07); FAULT(FAULT_GP,newCS & ~0x03);
} }
} }
if((desc.flags & 0x0018) != 0x0018) if((desc.flags & 0x0018) != 0x0018)
{ {
logerror("IRET: Return CS segment is not a code segment.\n"); logerror("IRET: Return CS segment is not a code segment.\n");
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
if(desc.flags & 0x0004) if(desc.flags & 0x0004)
{ {
if(DPL <= CPL) if(DPL > RPL)
{ {
logerror("IRET: Conforming return CS DPL is not greater than CPL.\n"); logerror("IRET: Conforming return CS DPL is greater than CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
else else
@ -2630,13 +2650,13 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if(DPL != RPL) if(DPL != RPL)
{ {
logerror("IRET: Non-conforming return CS DPL does not equal CS RPL.\n"); logerror("IRET: Non-conforming return CS DPL does not equal CS RPL.\n");
FAULT(FAULT_GP,newCS & ~0x07) FAULT(FAULT_GP,newCS & ~0x03)
} }
} }
if((desc.flags & 0x0080) == 0) if((desc.flags & 0x0080) == 0)
{ {
logerror("IRET: Return CS segment is not present.\n"); logerror("IRET: Return CS segment is not present.\n");
FAULT(FAULT_NP,newCS & ~0x07) FAULT(FAULT_NP,newCS & ~0x03)
} }
/* Check SS selector and descriptor */ /* Check SS selector and descriptor */
@ -2664,7 +2684,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newSS & ~0x07) >= cpustate->ldtr.limit) if((newSS & ~0x07) >= cpustate->ldtr.limit)
{ {
logerror("IRET: Return SS selector is past LDT limit.\n"); logerror("IRET: Return SS selector is past LDT limit.\n");
FAULT(FAULT_GP,newSS & ~0x07); FAULT(FAULT_GP,newSS & ~0x03);
} }
} }
else else
@ -2672,33 +2692,33 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if((newSS & ~0x07) >= cpustate->gdtr.limit) if((newSS & ~0x07) >= cpustate->gdtr.limit)
{ {
logerror("IRET: Return SS selector is past GDT limit.\n"); logerror("IRET: Return SS selector is past GDT limit.\n");
FAULT(FAULT_GP,newSS & ~0x07); FAULT(FAULT_GP,newSS & ~0x03);
} }
} }
if((newSS & 0x03) != RPL) if((newSS & 0x03) != RPL)
{ {
logerror("IRET: Return SS RPL is not equal to return CS RPL.\n"); logerror("IRET: Return SS RPL is not equal to return CS RPL.\n");
FAULT(FAULT_GP,newSS & ~0x07) FAULT(FAULT_GP,newSS & ~0x03)
} }
if((stack.flags & 0x0018) != 0x0010) if((stack.flags & 0x0018) != 0x0010)
{ {
logerror("IRET: Return SS segment is not a data segment.\n"); logerror("IRET: Return SS segment is not a data segment.\n");
FAULT(FAULT_GP,newSS & ~0x07) FAULT(FAULT_GP,newSS & ~0x03)
} }
if((stack.flags & 0x0002) == 0) if((stack.flags & 0x0002) == 0)
{ {
logerror("IRET: Return SS segment is not writable.\n"); logerror("IRET: Return SS segment is not writable.\n");
FAULT(FAULT_GP,newSS & ~0x07) FAULT(FAULT_GP,newSS & ~0x03)
} }
if(DPL != RPL) if(DPL != RPL)
{ {
logerror("IRET: Return SS DPL does not equal SS RPL.\n"); logerror("IRET: Return SS DPL does not equal SS RPL.\n");
FAULT(FAULT_GP,newSS & ~0x07) FAULT(FAULT_GP,newSS & ~0x03)
} }
if((stack.flags & 0x0080) == 0) if((stack.flags & 0x0080) == 0)
{ {
logerror("IRET: Return SS segment is not present.\n"); logerror("IRET: Return SS segment is not present.\n");
FAULT(FAULT_NP,newSS & ~0x07) FAULT(FAULT_NP,newSS & ~0x03)
} }
if(newEIP > desc.limit) if(newEIP > desc.limit)
{ {
@ -3127,7 +3147,7 @@ static CPU_EXECUTE( i386 )
cpustate->address_prefix = 0; cpustate->address_prefix = 0;
cpustate->ext = 1; cpustate->ext = 1;
cpustate->old_tf = cpustate->TF; int old_tf = cpustate->TF;
cpustate->segment_prefix = 0; cpustate->segment_prefix = 0;
cpustate->prev_eip = cpustate->eip; cpustate->prev_eip = cpustate->eip;
@ -3142,7 +3162,7 @@ static CPU_EXECUTE( i386 )
try try
{ {
I386OP(decode_opcode)(cpustate); I386OP(decode_opcode)(cpustate);
if(cpustate->TF && cpustate->old_tf) if(cpustate->TF && old_tf)
{ {
cpustate->prev_eip = cpustate->eip; cpustate->prev_eip = cpustate->eip;
cpustate->ext = 1; cpustate->ext = 1;

View File

@ -240,7 +240,6 @@ struct _i386_state
UINT8 performed_intersegment_jump; UINT8 performed_intersegment_jump;
UINT8 delayed_interrupt_enable; UINT8 delayed_interrupt_enable;
UINT8 old_tf;
UINT32 cr[5]; // Control registers UINT32 cr[5]; // Control registers
UINT32 dr[8]; // Debug registers UINT32 dr[8]; // Debug registers
@ -419,18 +418,19 @@ INLINE int translate_address(i386_state *cpustate, int rwn, UINT32 *address, UIN
UINT32 offset = a & 0xfff; UINT32 offset = a & 0xfff;
UINT32 page_entry; UINT32 page_entry;
UINT32 ret = 1; UINT32 ret = 1;
bool user = (cpustate->CPL == 3);
*error = 0; *error = 0;
// TODO: cr0 wp bit, 486 and higher // TODO: cr0 wp bit, 486 and higher
UINT32 page_dir = cpustate->program->read_dword(pdbr + directory * 4); UINT32 page_dir = cpustate->program->read_dword(pdbr + directory * 4);
if(page_dir & 1) if((page_dir & 1) && ((page_dir & 4) || !user))
{ {
if (!(cpustate->cr[4] & 0x10)) if (!(cpustate->cr[4] & 0x10))
{ {
page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4)); page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4));
if(!(page_entry & 1)) if(!(page_entry & 1))
ret = 0; ret = 0;
else if(!(page_entry & 2) && cpustate->CPL && (rwn == 1)) else if((!(page_entry & 2) && user && (rwn == 1)) || (!(page_entry & 4) && user))
{ {
*error = 1; *error = 1;
ret = 0; ret = 0;
@ -450,7 +450,7 @@ INLINE int translate_address(i386_state *cpustate, int rwn, UINT32 *address, UIN
{ {
if (page_dir & 0x80) if (page_dir & 0x80)
{ {
if(!(page_dir & 2) && cpustate->CPL && (rwn == 1)) if(!(page_dir & 2) && user && (rwn == 1))
{ {
*error = 1; *error = 1;
ret = 0; ret = 0;
@ -469,7 +469,7 @@ INLINE int translate_address(i386_state *cpustate, int rwn, UINT32 *address, UIN
page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4)); page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4));
if(!(page_entry & 1)) if(!(page_entry & 1))
ret = 0; ret = 0;
else if(!(page_entry & 2) && cpustate->CPL && (rwn == 1)) else if((!(page_entry & 2) && user && (rwn == 1)) || (!(page_entry & 4) && user))
{ {
*error = 1; *error = 1;
ret = 0; ret = 0;
@ -488,11 +488,15 @@ INLINE int translate_address(i386_state *cpustate, int rwn, UINT32 *address, UIN
} }
} }
else else
{
if(page_dir & 1)
*error = 1;
ret = 0; ret = 0;
}
if(!ret) if(!ret)
{ {
if(rwn != -1) if(rwn != -1)
*error |= ((rwn & 1)<<1) | ((cpustate->CPL==3)?1<<2:0); *error |= ((rwn & 1)<<1) | (user<<2);
return 0; return 0;
} }
return 1; return 1;
@ -672,6 +676,16 @@ INLINE UINT64 READ64(i386_state *cpustate,UINT32 ea)
return value; return value;
} }
INLINE void WRITE_TEST(i386_state *cpustate,UINT32 ea)
{
UINT32 address = ea, error;
if (cpustate->cr[0] & 0x80000000) // page translation enabled
{
if(!translate_address(cpustate,1,&address,&error))
PF_THROW(error);
}
}
INLINE void WRITE8(i386_state *cpustate,UINT32 ea, UINT8 value) INLINE void WRITE8(i386_state *cpustate,UINT32 ea, UINT8 value)
{ {
UINT32 address = ea, error; UINT32 address = ea, error;