From 9f1c1efe74ff63e897b1208a6852e021e334b779 Mon Sep 17 00:00:00 2001 From: mahlemiut Date: Wed, 11 Jan 2012 08:50:03 +0000 Subject: [PATCH] i386: Fixed high bits in eflags register from being changed by POPF, and VM and IF flags from changing depending on privilege level. Fixed exception error codes in protected mode. Further work on virtual 8086 mode. EMM386 will now load, but will still die a few seconds later. --- src/emu/cpu/i386/i386.c | 92 ++++++++++++++++++++++++------------- src/emu/cpu/i386/i386op16.c | 16 ++++++- src/emu/cpu/i386/i386op32.c | 14 +++++- src/emu/cpu/i386/i386ops.c | 6 +++ src/emu/cpu/i386/i386priv.h | 6 ++- 5 files changed, 97 insertions(+), 37 deletions(-) diff --git a/src/emu/cpu/i386/i386.c b/src/emu/cpu/i386/i386.c index 7203dd6ca36..282c76b6614 100644 --- a/src/emu/cpu/i386/i386.c +++ b/src/emu/cpu/i386/i386.c @@ -27,8 +27,8 @@ static void i386_trap_with_error(i386_state* cpustate, int irq, int irq_gate, in static void i286_task_switch(i386_state* cpustate, UINT16 selector, UINT8 nested); static void i386_task_switch(i386_state* cpustate, UINT16 selector, UINT8 nested); -#define FAULT(fault,error) {i386_trap_with_error(cpustate,fault,0,0,error); return;} -#define FAULT_EXP(fault,error) {i386_trap_with_error(cpustate,fault,0,trap_level+1,error); return;} +#define FAULT(fault,error) {cpustate->ext = 1; i386_trap_with_error(cpustate,fault,0,0,error); return;} +#define FAULT_EXP(fault,error) {cpustate->ext = 1; i386_trap_with_error(cpustate,fault,0,trap_level+1,error); return;} /*************************************************************************/ @@ -168,7 +168,7 @@ static UINT32 get_flags(i386_state *cpustate) f |= cpustate->IOP2 << 13; f |= cpustate->NT << 14; f |= cpustate->VM << 17; - return (cpustate->eflags & cpustate->eflags_mask) | (f & 0xffff); + return (cpustate->eflags & ~cpustate->eflags_mask) | (f & cpustate->eflags_mask); } static void set_flags(i386_state *cpustate, UINT32 f ) @@ -550,7 +550,7 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level int type; UINT16 flags; I386_SREG desc; - UINT8 CPL = 0, DPL = 0; //, RPL = 0; + UINT8 CPL = cpustate->CPL, DPL = 0; //, RPL = 0; I386_CALL_GATE gate; /* 32-bit */ @@ -573,7 +573,6 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level return; } - // TODO: support for EXT bit /* segment privilege checks */ if(entry > cpustate->idtr.limit) { @@ -587,7 +586,14 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level FAULT_EXP(FAULT_GP,entry+2) } - /* TODO: if software IRQ, then gate DPL must be less than CPL, else #GP(vector*8+2+EXT) */ + if(cpustate->ext == 0) // if software interrupt (caused by INT/INTO/INT3) + { + if(((flags >> 5) & 0x03) < CPL) + { + logerror("IRQ (%08x): Software IRQ - gate DPL is less than CPL.\n",cpustate->pc); + FAULT_EXP(FAULT_GP,entry+2) + } + } if((flags & 0x0080) == 0) { @@ -600,6 +606,7 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level UINT32 tempflags; UINT32 tempESP,tempSS; /* Interrupt for a Virtual 8086 task */ + logerror("IRQ (%08x): Interrupt during V8086 task\n",cpustate->pc); tempflags = get_flags(cpustate); cpustate->VM = 0; @@ -611,6 +618,7 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level /* Get privilege level 0 stack pointer from TSS */ cpustate->sreg[SS].selector = i386_get_stack_segment(cpustate,0); REG32(ESP) = i386_get_stack_ptr(cpustate,0); + i386_load_segment_descriptor(cpustate,SS); PUSH32(cpustate,cpustate->sreg[GS].selector & 0xffff); PUSH32(cpustate,cpustate->sreg[FS].selector & 0xffff); PUSH32(cpustate,cpustate->sreg[DS].selector & 0xffff); @@ -623,14 +631,19 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level i386_load_segment_descriptor(cpustate,FS); i386_load_segment_descriptor(cpustate,DS); i386_load_segment_descriptor(cpustate,ES); - PUSH32(cpustate,tempESP); PUSH32(cpustate,tempSS & 0xffff); + PUSH32(cpustate,tempESP); PUSH32(cpustate,tempflags); PUSH32(cpustate,cpustate->sreg[CS].selector & 0xffff); - PUSH32(cpustate,cpustate->eip); + if(irq == 3 || irq == 4 || irq == 9 || irq_gate == 1) + PUSH32(cpustate, cpustate->eip ); + else + PUSH32(cpustate, cpustate->prev_eip ); cpustate->sreg[CS].selector = segment; cpustate->eip = offset; + // CPL set to CS RPL? + cpustate->CPL = segment & 0x03; i386_load_segment_descriptor(cpustate,CS); CHANGE_PC(cpustate,cpustate->eip); @@ -688,14 +701,14 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level if((segment & ~0x07) == 0) { logerror("IRQ: Gate segment is null.\n"); - FAULT_EXP(FAULT_GP,0) + FAULT_EXP(FAULT_GP,cpustate->ext) } if(segment & 0x04) { if((segment & ~0x07) > cpustate->ldtr.limit) { logerror("IRQ: Gate segment is past LDT limit.\n"); - FAULT_EXP(FAULT_GP,segment) + FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext) } } else @@ -703,18 +716,18 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level if((segment & ~0x07) > cpustate->gdtr.limit) { logerror("IRQ: Gate segment is past GDT limit.\n"); - FAULT_EXP(FAULT_GP,segment) + FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext) } } if((desc.flags & 0x0018) != 0x18) { logerror("IRQ: Gate descriptor is not a code segment.\n"); - FAULT_EXP(FAULT_GP,segment) + FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext) } if((desc.flags & 0x0080) == 0) { logerror("IRQ: Gate segment is not present.\n"); - FAULT_EXP(FAULT_NP,segment) + FAULT_EXP(FAULT_NP,(segment & 0x07)+cpustate->ext) } if((desc.flags & 0x0004) == 0 && (DPL < CPL)) { @@ -737,14 +750,14 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level if((stack.selector & ~0x07) == 0) { logerror("IRQ: New stack selector is null.\n"); - FAULT_EXP(FAULT_GP,0) // #GP(EXT) + FAULT_EXP(FAULT_GP,cpustate->ext) } if(stack.selector & 0x04) { if((stack.selector & ~0x07) > cpustate->ldtr.base) { logerror("IRQ: New stack selector is past LDT limit.\n"); - FAULT_EXP(FAULT_TS,stack.selector & ~0x07) + FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) } } else @@ -752,28 +765,28 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level if((stack.selector & ~0x07) > cpustate->gdtr.base) { logerror("IRQ: New stack selector is past GDT limit.\n"); - FAULT_EXP(FAULT_TS,stack.selector & ~0x07) // #TS(stack selector + EXT) + FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) } } if((stack.selector & 0x03) != DPL) { logerror("IRQ: New stack selector RPL is not equal to code segment DPL.\n"); - FAULT_EXP(FAULT_TS,stack.selector & ~0x07) // #TS(stack selector + EXT) + FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) } if(((stack.flags >> 5) & 0x03) != DPL) { logerror("IRQ: New stack segment DPL is not equal to code segment DPL.\n"); - FAULT_EXP(FAULT_TS,stack.selector & ~0x07) // #TS(stack selector + EXT) + FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) } if(((stack.flags & 0x0018) != 0x10) && (stack.flags & 0x0002) != 0) { logerror("IRQ: New stack segment is not a writable data segment.\n"); - FAULT_EXP(FAULT_TS,stack.selector & ~0x07) // #TS(stack selector + EXT) + FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) // #TS(stack selector + EXT) } if((stack.flags & 0x0080) == 0) { logerror("IRQ: New stack segment is not present.\n"); - FAULT_EXP(FAULT_SS,stack.selector & ~0x07) // #TS(stack selector + EXT) + FAULT_EXP(FAULT_SS,(stack.selector & ~0x07)+cpustate->ext) // #TS(stack selector + EXT) } if(type & 0x08) // 32-bit gate { @@ -892,7 +905,19 @@ 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. - PUSH16(cpustate,error); + if(PROTECTED_MODE) + { + UINT32 entry = irq * 8; + UINT32 v2,type; + v2 = READ32(cpustate, cpustate->idtr.base + entry + 4 ); + type = (v2>>8) & 0x1F; + if(type >= 9) + PUSH32(cpustate,error); + else + PUSH16(cpustate,error); + } + else + PUSH16(cpustate,error); } } @@ -2138,25 +2163,26 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) { if(!cpustate->IOP1 || !cpustate->IOP2) { - logerror("IRET: Is in Virtual 8086 mode and IOPL != 3.\n"); + logerror("IRET (%08x): Is in Virtual 8086 mode and IOPL != 3.\n",cpustate->pc); FAULT(FAULT_GP,0) } /* Is this correct? The 80386 programmers' reference says IRET should always trigger #GP(0) in V86 mode */ if(operand32 == 0) { + UINT32 oldflags = get_flags(cpustate); cpustate->eip = newEIP & 0xffff; cpustate->sreg[CS].selector = newCS & 0xffff; newflags |= 0x3000; // IOPL cannot be changed in V86 mode - newflags |= 0x4000; // NT flag - set_flags(cpustate,newflags & 0xffff); + set_flags(cpustate,newflags | oldflags); + REG16(SP) += 6; } else { cpustate->eip = newEIP; cpustate->sreg[CS].selector = newCS & 0xffff; newflags |= 0x3000; // IOPL cannot be changed in V86 mode - newflags |= 0x4000; // NT flag - set_flags(cpustate,newflags & 0xffff); + set_flags(cpustate,newflags); + REG32(ESP) += 12; } } else if(NESTED_TASK) @@ -2180,7 +2206,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) i386_load_protected_mode_segment(cpustate,&desc); if((desc.flags & 0x001f) != 0x000b) { - logerror("IRET: Task return: Back-linked TSS is not a busy TSS.\n"); + logerror("IRET (%08x): Task return: Back-linked TSS is not a busy TSS.\n",cpustate->pc); FAULT(FAULT_TS,task & ~0x07) } if((desc.flags & 0x0080) == 0) @@ -2324,7 +2350,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) cpustate->eip = POP32(cpustate) & 0xffff; // high 16 bits are ignored cpustate->sreg[CS].selector = POP32(cpustate) & 0xffff; POP32(cpustate); // already set flags - if(RPL > CPL) +// if(RPL > CPL) { newESP = POP32(cpustate); newSS = POP32(cpustate) & 0xffff; @@ -2925,7 +2951,7 @@ static CPU_RESET( i386 ) cpustate->cr[0] = 0x7fffffe0; // reserved bits set to 1 cpustate->eflags = 0; - cpustate->eflags_mask = 0x00030000; + cpustate->eflags_mask = 0x00037fd7; cpustate->eip = 0xfff0; // [11:8] Family @@ -2997,6 +3023,8 @@ static CPU_EXECUTE( i386 ) cpustate->operand_prefix = 0; cpustate->address_prefix = 0; + cpustate->ext = 1; + cpustate->segment_prefix = 0; cpustate->prev_eip = cpustate->eip; @@ -3392,7 +3420,7 @@ static CPU_RESET( i486 ) cpustate->cr[0] = 0x00000010; cpustate->eflags = 0; - cpustate->eflags_mask = 0x00070000; + cpustate->eflags_mask = 0x00077fd7; cpustate->eip = 0xfff0; // [11:8] Family @@ -3509,7 +3537,7 @@ static CPU_RESET( pentium ) cpustate->cr[0] = 0x00000010; cpustate->eflags = 0; - cpustate->eflags_mask = 0x003b0000; + cpustate->eflags_mask = 0x003b7fd7; cpustate->eip = 0xfff0; // [11:8] Family @@ -3641,7 +3669,7 @@ static CPU_RESET( mediagx ) cpustate->cr[0] = 0x00000010; cpustate->eflags = 0; - cpustate->eflags_mask = 0x00270000; /* TODO: is this correct? */ + cpustate->eflags_mask = 0x00277fd7; /* TODO: is this correct? */ cpustate->eip = 0xfff0; // [11:8] Family diff --git a/src/emu/cpu/i386/i386op16.c b/src/emu/cpu/i386/i386op16.c index 4efccf80079..6785f8465ea 100644 --- a/src/emu/cpu/i386/i386op16.c +++ b/src/emu/cpu/i386/i386op16.c @@ -1613,8 +1613,20 @@ static void I386OP(popa)(i386_state *cpustate) // Opcode 0x61 static void I386OP(popf)(i386_state *cpustate) // Opcode 0x9d { - UINT16 value = POP16(cpustate); - set_flags(cpustate,value); + UINT32 value = POP16(cpustate); + UINT32 current = get_flags(cpustate); + UINT8 IOPL = (current >> 12) & 0x03; + UINT32 mask = 0x7fd5; + + // IOPL can only change if CPL is 0 + if(cpustate->CPL != 0) + mask &= ~0x00003000; + + // IF can only change if CPL is at least as privileged as IOPL + if(cpustate->CPL > IOPL) + mask &= ~0x00000200; + +set_flags(cpustate,(current & ~mask) | (value & mask)); // mask out reserved bits CYCLES(cpustate,CYCLES_POPF); } diff --git a/src/emu/cpu/i386/i386op32.c b/src/emu/cpu/i386/i386op32.c index 31ffd8316f5..463e3a4e60b 100644 --- a/src/emu/cpu/i386/i386op32.c +++ b/src/emu/cpu/i386/i386op32.c @@ -1490,7 +1490,19 @@ static void I386OP(popad)(i386_state *cpustate) // Opcode 0x61 static void I386OP(popfd)(i386_state *cpustate) // Opcode 0x9d { UINT32 value = POP32(cpustate); - set_flags(cpustate,value); + UINT32 current = get_flags(cpustate); + UINT8 IOPL = (current >> 12) & 0x03; + UINT32 mask = 0x00007fd5; // VM and RF are not affected by POPF or POPFD, same for higher (486+) bits? + + // IOPL can only change if CPL is 0 + if(cpustate->CPL != 0) + mask &= ~0x00003000; + + // IF can only change if CPL is at least as privileged as IOPL + if(cpustate->CPL > IOPL) + mask &= ~0x00000200; + + set_flags(cpustate,(current & ~mask) | (value & mask)); // mask out reserved bits CYCLES(cpustate,CYCLES_POPF); } diff --git a/src/emu/cpu/i386/i386ops.c b/src/emu/cpu/i386/i386ops.c index 53b8485b43e..d1b37043d4f 100644 --- a/src/emu/cpu/i386/i386ops.c +++ b/src/emu/cpu/i386/i386ops.c @@ -2233,20 +2233,26 @@ static void I386OP(nop)(i386_state *cpustate) // Opcode 0x90 static void I386OP(int3)(i386_state *cpustate) // Opcode 0xcc { CYCLES(cpustate,CYCLES_INT3); + cpustate->ext = 0; // not an external interrupt i386_trap(cpustate,3, 1, 0); + cpustate->ext = 1; } static void I386OP(int)(i386_state *cpustate) // Opcode 0xcd { int interrupt = FETCH(cpustate); CYCLES(cpustate,CYCLES_INT); + cpustate->ext = 0; // not an external interrupt i386_trap(cpustate,interrupt, 1, 0); + cpustate->ext = 1; } static void I386OP(into)(i386_state *cpustate) // Opcode 0xce { if( cpustate->OF ) { + cpustate->ext = 0; i386_trap(cpustate,4, 1, 0); + cpustate->ext = 1; CYCLES(cpustate,CYCLES_INTO_OF1); } else diff --git a/src/emu/cpu/i386/i386priv.h b/src/emu/cpu/i386/i386priv.h index 7daf85b6081..b571c2bd36c 100644 --- a/src/emu/cpu/i386/i386priv.h +++ b/src/emu/cpu/i386/i386priv.h @@ -241,6 +241,8 @@ struct _i386_state I386_SEG_DESC task; // Task register I386_SEG_DESC ldtr; // Local Descriptor Table Register + UINT8 ext; // external interrupt + int halted; int operand_size; @@ -302,8 +304,8 @@ extern int i386_parity_table[256]; #define PROTECTED_MODE (cpustate->cr[0] & 0x1) #define STACK_32BIT (cpustate->sreg[SS].d) -#define V8086_MODE (cpustate->eflags & 0x00020000) -#define NESTED_TASK (cpustate->eflags & 0x00004000) +#define V8086_MODE (cpustate->VM) +#define NESTED_TASK (cpustate->NT) #define SetOF_Add32(r,s,d) (cpustate->OF = (((r) ^ (s)) & ((r) ^ (d)) & 0x80000000) ? 1: 0) #define SetOF_Add16(r,s,d) (cpustate->OF = (((r) ^ (s)) & ((r) ^ (d)) & 0x8000) ? 1 : 0)