summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/cpu/i386
diff options
context:
space:
mode:
author mahlemiut <mahlemiut@users.noreply.github.com>2012-01-06 01:55:32 +0000
committer mahlemiut <mahlemiut@users.noreply.github.com>2012-01-06 01:55:32 +0000
commitbdbe7ad11cd6293e34e9be804c4130a2ef380225 (patch)
treee546b2c670275a21bc33c586cda48b805704ba65 /src/emu/cpu/i386
parent2bec585837cc38284419f67c90257e2794bb16bb (diff)
i386: Moved stack changes to be after protection checks in RETF and IRET
Made ARPL protected mode only Added protection checks when MOVing to a segment register.
Diffstat (limited to 'src/emu/cpu/i386')
-rw-r--r--src/emu/cpu/i386/i386.c212
-rw-r--r--src/emu/cpu/i386/i386op16.c2
-rw-r--r--src/emu/cpu/i386/i386ops.c48
3 files changed, 205 insertions, 57 deletions
diff --git a/src/emu/cpu/i386/i386.c b/src/emu/cpu/i386/i386.c
index 65fc2459d5a..3adcbef5ec9 100644
--- a/src/emu/cpu/i386/i386.c
+++ b/src/emu/cpu/i386/i386.c
@@ -379,6 +379,126 @@ static void i386_check_sreg_validity(i386_state* cpustate, int reg)
}
}
+static void i386_protected_mode_sreg_load(i386_state *cpustate, UINT16 selector, UINT8 reg)
+{
+ // Checks done when MOV changes a segment register in protected mode
+ UINT8 CPL,RPL,DPL;
+
+ CPL = cpustate->CPL;
+ RPL = selector & 0x0003;
+
+ if(reg == SS)
+ {
+ I386_SREG stack;
+
+ memset(&stack, 0, sizeof(stack));
+ stack.selector = selector;
+ i386_load_protected_mode_segment(cpustate,&stack);
+ DPL = (stack.flags >> 5) & 0x03;
+
+ if((selector & ~0x0007) == 0)
+ {
+ logerror("SReg Load (%08x): Selector is null.\n",cpustate->pc);
+ FAULT(FAULT_GP,0)
+ }
+ if(selector & 0x0004) // LDT
+ {
+ if((selector & ~0x0007) > cpustate->ldtr.limit)
+ {
+ logerror("SReg Load (%08x): Selector is out of LDT bounds.\n",cpustate->pc);
+ FAULT(FAULT_GP,selector)
+ }
+ }
+ else // GDT
+ {
+ if((selector & ~0x0007) > cpustate->gdtr.limit)
+ {
+ logerror("SReg Load (%08x): Selector is out of GDT bounds.\n",cpustate->pc);
+ FAULT(FAULT_GP,selector)
+ }
+ }
+ if (RPL != CPL)
+ {
+ logerror("SReg Load (%08x): Selector RPL does not equal CPL.\n",cpustate->pc);
+ FAULT(FAULT_GP,selector)
+ }
+ if(((stack.flags & 0x0018) != 0x10) && (stack.flags & 0x0002) != 0)
+ {
+ logerror("SReg Load (%08x): Segment is not a writable data segment.\n",cpustate->pc);
+ FAULT(FAULT_GP,selector)
+ }
+ if(DPL != CPL)
+ {
+ logerror("SReg Load (%08x): Segment DPL does not equal CPL.\n",cpustate->pc);
+ FAULT(FAULT_GP,selector)
+ }
+ if(!(stack.flags & 0x0080))
+ {
+ logerror("SReg Load (%08x): Segment is not present.\n",cpustate->pc);
+ FAULT(FAULT_SS,selector)
+ }
+ }
+ if(reg == DS || reg == ES || reg == FS || reg == GS)
+ {
+ I386_SREG desc;
+
+ if((selector & ~0x0007) == 0)
+ {
+ cpustate->sreg[reg].selector = selector;
+ i386_load_segment_descriptor(cpustate, reg );
+ // TODO: mark segment as invalid
+ return;
+ }
+
+ memset(&desc, 0, sizeof(desc));
+ desc.selector = selector;
+ i386_load_protected_mode_segment(cpustate,&desc);
+ DPL = (desc.flags >> 5) & 0x03;
+
+ if(selector & 0x0004) // LDT
+ {
+ if((selector & ~0x0007) > cpustate->ldtr.limit)
+ {
+ logerror("SReg Load (%08x): Selector is out of LDT bounds.\n",cpustate->pc);
+ FAULT(FAULT_GP,selector)
+ }
+ }
+ else // GDT
+ {
+ if((selector & ~0x0007) > cpustate->gdtr.limit)
+ {
+ logerror("SReg Load (%08x): Selector is out of GDT bounds.\n",cpustate->pc);
+ FAULT(FAULT_GP,selector)
+ }
+ }
+ if((desc.flags & 0x0018) != 0x10)
+ {
+ if(((desc.flags & 0x0002) != 0) && ((desc.flags & 0x0018) != 0x18))
+ {
+ logerror("SReg Load (%08x): Segment is not a data segment or readable code segment.\n",cpustate->pc);
+ FAULT(FAULT_GP,selector)
+ }
+ }
+ if(((desc.flags & 0x0018) == 0x10) || ((!(desc.flags & 0x0004)) && ((desc.flags & 0x0018) == 0x18)))
+ {
+ // if data or non-conforming code segment
+ if((RPL > DPL) || (CPL > DPL))
+ {
+ logerror("SReg Load (%08x): Selector RPL or CPL is not less or equal to segment DPL.\n",cpustate->pc);
+ FAULT(FAULT_GP,selector)
+ }
+ }
+ if(!(desc.flags & 0x0080))
+ {
+ logerror("SReg Load (%08x): Segment is not present.\n",cpustate->pc);
+ FAULT(FAULT_NP,selector)
+ }
+ }
+
+ cpustate->sreg[reg].selector = selector;
+ i386_load_segment_descriptor(cpustate, reg );
+}
+
static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level)
{
/* I386 Interrupts/Traps/Faults:
@@ -728,7 +848,7 @@ static void i386_trap_with_error(i386_state *cpustate,int irq, int irq_gate, int
{
// for these exceptions, an error code is pushed onto the stack by the processor.
// no error code is pushed for software interrupts, either.
- PUSH32(cpustate,error);
+ PUSH16(cpustate,error);
}
}
@@ -1253,7 +1373,7 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
if((selector & ~0x07) == 0)
{
- logerror("CALL: Selector is null.\n");
+ logerror("CALL (%08x): Selector is null.\n",cpustate->pc);
FAULT(FAULT_GP,0) // #GP(0)
}
if(selector & 0x04)
@@ -1588,6 +1708,7 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of
logerror("CALL: EIP is past segment limit.\n");
FAULT(FAULT_GP,0) // #GP(0)
}
+ SetRPL = 1;
}
break;
case 0x05: // task gate
@@ -1682,17 +1803,24 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
I386_SREG desc;
UINT8 CPL, RPL, DPL;
+
if(operand32 == 0)
{
newEIP = POP16(cpustate) & 0xffff;
newCS = POP16(cpustate) & 0xffff;
REG16(SP) += count;
+ newESP = POP16(cpustate) & 0xffff;
+ newSS = POP16(cpustate) & 0xffff;
+ REG16(SP) -= (8+count); // re-adjust stack pointer
}
else
{
newEIP = POP32(cpustate);
newCS = POP32(cpustate) & 0xffff;
REG32(ESP) += count;
+ newESP = POP32(cpustate);
+ newSS = POP32(cpustate) & 0xffff;
+ REG32(ESP) -= (16+count); // re-adjust stack pointer
}
memset(&desc, 0, sizeof(desc));
@@ -1704,7 +1832,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
if(RPL < CPL)
{
- logerror("RETF: Return segment RPL is less than CPL.\n");
+ logerror("RETF (%08x): Return segment RPL is less than CPL.\n",cpustate->pc);
FAULT(FAULT_GP,newCS & ~0x07)
}
@@ -1713,7 +1841,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
/* same privilege level */
if((newCS & ~0x07) == 0)
{
- logerror("RETF: Return segment RPL is less than CPL.\n");
+ logerror("RETF: Return segment is null.\n");
FAULT(FAULT_GP,0)
}
if(newCS & 0x04)
@@ -1767,7 +1895,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
{
if(REG16(SP) > (cpustate->sreg[SS].limit & 0xffff))
{
- logerror("RETF: SP is past stack segment limit.\n");
+ logerror("RETF (%08x): SP is past stack segment limit.\n",cpustate->pc);
FAULT(FAULT_SS,0)
}
}
@@ -1779,25 +1907,25 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
FAULT(FAULT_SS,0)
}
}
+ if(operand32 == 0)
+ REG16(SP) += (4+count);
+ else
+ REG32(ESP) += (8+count);
}
else if(RPL > CPL)
{
/* outer privilege level */
if(operand32 == 0)
{
- newESP = POP16(cpustate) & 0xffff;
- newSS = POP16(cpustate);
- if(newESP+8+count > cpustate->sreg[SS].limit)
+ if(REG16(SP)+8+count > cpustate->sreg[SS].limit)
{
- logerror("RETF: SP is past stack segment limit.\n");
+ logerror("RETF (%08x): SP is past stack segment limit.\n",cpustate->pc);
FAULT(FAULT_SS,0)
}
}
else
{
- newESP = POP32(cpustate);
- newSS = POP32(cpustate);
- if(newESP+16+count > cpustate->sreg[SS].limit)
+ if(REG32(ESP)+16+count > cpustate->sreg[SS].limit)
{
logerror("RETF: ESP is past stack segment limit.\n");
FAULT(FAULT_SS,0)
@@ -1870,7 +1998,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
{
if((newSS & ~0x07) > cpustate->ldtr.limit)
{
- logerror("RETF: SS segment selector is past LDT limit.\n");
+ logerror("RETF (%08x): SS segment selector is past LDT limit.\n",cpustate->pc);
FAULT(FAULT_GP,newSS & ~0x07)
}
}
@@ -1878,7 +2006,7 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
{
if((newSS & ~0x07) > cpustate->gdtr.limit)
{
- logerror("RETF: SS segment selector is past GDT limit.\n");
+ logerror("RETF (%08x): SS segment selector is past GDT limit.\n",cpustate->pc);
FAULT(FAULT_GP,newSS & ~0x07)
}
}
@@ -1913,9 +2041,10 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op
i386_load_segment_descriptor(cpustate, SS );
if(operand32 == 0)
- REG16(SP) += count;
+ REG16(SP) += (8+count);
else
- REG32(ESP) += count;
+ REG32(ESP) += (16+count);
+
/* Check that DS, ES, FS and GS are valid for the new privilege level */
i386_check_sreg_validity(cpustate,DS);
@@ -1945,15 +2074,21 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
CPL = cpustate->CPL;
if(operand32 == 0)
{
- newEIP = POP16(cpustate);
- newCS = POP16(cpustate);
- newflags = POP16(cpustate);
+ newEIP = POP16(cpustate) & 0xffff;
+ newCS = POP16(cpustate) & 0xffff;
+ newflags = POP16(cpustate) & 0xffff;
+ newESP = POP16(cpustate) & 0xffff;
+ newSS = POP16(cpustate) & 0xffff;
+ REG16(SP) -= 10;
}
else
{
newEIP = POP32(cpustate);
newCS = POP32(cpustate) & 0xffff;
newflags = POP32(cpustate);
+ newESP = POP32(cpustate);
+ newSS = POP32(cpustate) & 0xffff;
+ REG32(ESP) -= 20;
}
if(V8086_MODE)
@@ -2001,7 +2136,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
if(newflags & 0x00020000) // if returning to virtual 8086 mode
{
/* Return to v86 mode */
- popmessage("IRET: Unimplemented return to Virtual 8086 mode.");
+ popmessage("IRET (%08x): Unimplemented return to Virtual 8086 mode.",cpustate->pc);
}
else
{
@@ -2024,7 +2159,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
RPL = newCS & 0x03;
if(RPL < CPL)
{
- logerror("IRET: Return CS RPL is less than CPL.\n");
+ logerror("IRET (%08x): Return CS RPL is less than CPL.\n",cpustate->pc);
FAULT(FAULT_GP,newCS & ~0x07)
}
if(RPL == CPL)
@@ -2074,7 +2209,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
RPL = newCS & 0x03;
if((desc.flags & 0x0018) != 0x0018)
{
- logerror("IRET: Return CS segment is not a code segment.\n");
+ logerror("IRET (%08x): Return CS segment is not a code segment.\n",cpustate->pc);
FAULT(FAULT_GP,newCS & ~0x07)
}
if(desc.flags & 0x0004)
@@ -2108,45 +2243,43 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
cpustate->eip = newEIP;
cpustate->sreg[CS].selector = newCS;
set_flags(cpustate,newflags);
+ REG16(SP) += 6;
}
else
{
cpustate->eip = newEIP;
cpustate->sreg[CS].selector = newCS & 0xffff;
set_flags(cpustate,newflags);
+ REG32(ESP) += 12;
}
}
else if(RPL > CPL)
{
I386_SREG stack;
/* return to outer privilege level */
+ memset(&desc, 0, sizeof(desc));
+ desc.selector = newCS;
+ i386_load_protected_mode_segment(cpustate,&desc);
+ DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level
+ RPL = newCS & 0x03;
+ memset(&stack, 0, sizeof(stack));
+ stack.selector = newSS;
+ i386_load_protected_mode_segment(cpustate,&stack);
if(operand32 == 0)
{
- newESP = POP16(cpustate);
- newSS = POP16(cpustate);
- if(REG16(SP) > cpustate->sreg[SS].limit)
+ if(REG16(SP)+10 > cpustate->sreg[SS].limit)
{
- logerror("IRET: ESP is past SS limit.\n");
+ logerror("IRET: SP is past SS limit.\n");
FAULT(FAULT_SS,0) }
}
else
{
- newESP = POP32(cpustate);
- newSS = POP32(cpustate) & 0xffff;
- if(REG32(ESP) > cpustate->sreg[SS].limit)
+ if(REG32(ESP)+20 > cpustate->sreg[SS].limit)
{
logerror("IRET: ESP is past SS limit.\n");
FAULT(FAULT_SS,0)
}
}
- memset(&desc, 0, sizeof(desc));
- desc.selector = newCS;
- i386_load_protected_mode_segment(cpustate,&desc);
- DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level
- RPL = newCS & 0x03;
- memset(&stack, 0, sizeof(stack));
- stack.selector = newSS;
- i386_load_protected_mode_segment(cpustate,&stack);
/* Check CS selector and descriptor */
if((newCS & ~0x07) == 0)
{
@@ -2251,6 +2384,11 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32)
}
if(operand32 == 0)
+ REG16(SP) += 10;
+ else
+ REG32(ESP) += 20;
+
+ if(operand32 == 0)
{
cpustate->eip = newEIP & 0xffff;
cpustate->sreg[CS].selector = newCS;
diff --git a/src/emu/cpu/i386/i386op16.c b/src/emu/cpu/i386/i386op16.c
index 8b265e09761..7dbaa84e543 100644
--- a/src/emu/cpu/i386/i386op16.c
+++ b/src/emu/cpu/i386/i386op16.c
@@ -3288,7 +3288,7 @@ static void I386OP(lar_r16_rm16)(i386_state *cpustate) // Opcode 0x0f 0x02
if(seg.selector == 0)
{
SetZF(0); // not a valid segment
- logerror("i386: LAR: Selector %04x is invalid type.\n",seg.selector);
+ // logerror("i386 (%08x): LAR: Selector %04x is invalid type.\n",cpustate->pc,seg.selector);
}
else
{
diff --git a/src/emu/cpu/i386/i386ops.c b/src/emu/cpu/i386/i386ops.c
index d5c1f8b4fbe..915e509c14b 100644
--- a/src/emu/cpu/i386/i386ops.c
+++ b/src/emu/cpu/i386/i386ops.c
@@ -763,8 +763,13 @@ static void I386OP(mov_sreg_rm16)(i386_state *cpustate) // Opcode 0x8e
}
}
- cpustate->sreg[s].selector = selector;
- i386_load_segment_descriptor(cpustate, s );
+ if(PROTECTED_MODE && !(V8086_MODE)) // no checks in virtual 8086 mode?
+ i386_protected_mode_sreg_load(cpustate,selector,s);
+ else
+ {
+ cpustate->sreg[s].selector = selector;
+ i386_load_segment_descriptor(cpustate, s );
+ }
}
static void I386OP(mov_al_i8)(i386_state *cpustate) // Opcode 0xb0
@@ -905,25 +910,30 @@ static void I386OP(arpl)(i386_state *cpustate) // Opcode 0x63
UINT8 modrm = FETCH(cpustate);
UINT8 flag = 0;
- if( modrm >= 0xc0 ) {
- src = LOAD_REG16(modrm);
- dst = LOAD_RM16(modrm);
- if( (dst&0x3) < (src&0x3) ) {
- dst = (dst&0xfffc) | (src&0x3);
- flag = 1;
- STORE_RM16(modrm, dst);
- }
- } else {
- UINT32 ea = GetEA(cpustate, modrm);
- src = LOAD_REG16(modrm);
- dst = READ16(cpustate, ea);
- if( (dst&0x3) < (src&0x3) ) {
- dst = (dst&0xfffc) | (src&0x3);
- flag = 1;
- WRITE16(cpustate, ea, dst);
+ if(PROTECTED_MODE)
+ {
+ if( modrm >= 0xc0 ) {
+ src = LOAD_REG16(modrm);
+ dst = LOAD_RM16(modrm);
+ if( (dst&0x3) < (src&0x3) ) {
+ dst = (dst&0xfffc) | (src&0x3);
+ flag = 1;
+ STORE_RM16(modrm, dst);
+ }
+ } else {
+ UINT32 ea = GetEA(cpustate, modrm);
+ src = LOAD_REG16(modrm);
+ dst = READ16(cpustate, ea);
+ if( (dst&0x3) < (src&0x3) ) {
+ dst = (dst&0xfffc) | (src&0x3);
+ flag = 1;
+ WRITE16(cpustate, ea, dst);
+ }
}
+ SetZF(flag);
}
- SetZF(flag);
+ else
+ i386_trap(cpustate, 6, 0, 0); // invalid opcode in real mode or v8086 mode
}
static void I386OP(push_i8)(i386_state *cpustate) // Opcode 0x6a