From 44ee92ea776a9d7a0f6ce35018afe01a8406112f Mon Sep 17 00:00:00 2001 From: mahlemiut Date: Mon, 9 Jan 2012 08:10:14 +0000 Subject: [PATCH] i386: Made a start at Virtual 8086 Mode. Not fully working yet, though. Fixed an issue where two address or operand size prefixes would cancel each other out. --- src/emu/cpu/i386/i386.c | 240 +++++++++++++++++++++++++++++++++--- src/emu/cpu/i386/i386dasm.c | 21 +++- src/emu/cpu/i386/i386op16.c | 2 +- src/emu/cpu/i386/i386op32.c | 2 +- src/emu/cpu/i386/i386ops.c | 12 +- src/emu/cpu/i386/i386priv.h | 3 + 6 files changed, 254 insertions(+), 26 deletions(-) diff --git a/src/emu/cpu/i386/i386.c b/src/emu/cpu/i386/i386.c index 3adcbef5ec9..989744c26a7 100644 --- a/src/emu/cpu/i386/i386.c +++ b/src/emu/cpu/i386/i386.c @@ -167,6 +167,7 @@ static UINT32 get_flags(i386_state *cpustate) f |= cpustate->IOP1 << 12; f |= cpustate->IOP2 << 13; f |= cpustate->NT << 14; + f |= cpustate->VM << 17; return (cpustate->eflags & cpustate->eflags_mask) | (f & 0xffff); } @@ -184,6 +185,7 @@ static void set_flags(i386_state *cpustate, UINT32 f ) cpustate->IOP1 = (f & 0x1000) ? 1 : 0; cpustate->IOP2 = (f & 0x2000) ? 1 : 0; cpustate->NT = (f & 0x4000) ? 1 : 0; + cpustate->VM = (f & 0x20000) ? 1 : 0; cpustate->eflags = f & cpustate->eflags_mask; } @@ -575,13 +577,13 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level /* segment privilege checks */ if(entry > cpustate->idtr.limit) { - logerror("IRQ: Vector is past IDT limit.\n"); + logerror("IRQ (%08x): Vector %02xh is past IDT limit.\n",cpustate->pc,entry); FAULT_EXP(FAULT_GP,entry+2) } /* segment must be interrupt gate, trap gate, or task gate */ if(type != 0x05 && type != 0x06 && type != 0x07 && type != 0x0e && type != 0x0f) { - logerror("IRQ: Vector segment is not an interrupt, trap or task gate.\n"); + logerror("IRQ (%08x): Vector segment %04x is not an interrupt, trap or task gate.\n",cpustate->pc,segment); FAULT_EXP(FAULT_GP,entry+2) } @@ -593,6 +595,48 @@ static void i386_trap(i386_state *cpustate,int irq, int irq_gate, int trap_level FAULT_EXP(FAULT_NP,entry+2) } + if(V8086_MODE) + { + 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; + cpustate->TF = 0; + if(irq_gate == 1) + cpustate->IF = 0; + tempSS = cpustate->sreg[SS].selector; + tempESP = REG32(ESP); + /* 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); + PUSH32(cpustate,cpustate->sreg[GS].selector & 0xffff); + PUSH32(cpustate,cpustate->sreg[FS].selector & 0xffff); + PUSH32(cpustate,cpustate->sreg[DS].selector & 0xffff); + PUSH32(cpustate,cpustate->sreg[ES].selector & 0xffff); + cpustate->sreg[GS].selector = 0; + cpustate->sreg[FS].selector = 0; + cpustate->sreg[DS].selector = 0; + cpustate->sreg[ES].selector = 0; + i386_load_segment_descriptor(cpustate,GS); + 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,tempflags); + PUSH32(cpustate,cpustate->sreg[CS].selector & 0xffff); + PUSH32(cpustate,cpustate->eip); + + cpustate->sreg[CS].selector = segment; + cpustate->eip = offset; + + i386_load_segment_descriptor(cpustate,CS); + CHANGE_PC(cpustate,cpustate->eip); + return; + } + if(type == 0x05) { /* Task gate */ @@ -1428,7 +1472,7 @@ static void i386_protected_mode_call(i386_state *cpustate, UINT16 seg, UINT32 of } if((desc.flags & 0x0080) == 0) { - logerror("CALL: Code segment is not present.\n"); + logerror("CALL (%08x): Code segment is not present.\n",cpustate->pc); FAULT(FAULT_NP,selector & ~0x07) // #NP(selector) } if (operand32 != 0) // if 32-bit @@ -2032,6 +2076,11 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op } cpustate->CPL = newCS & 0x03; + if(operand32 == 0) + REG16(SP) += (8+count); + else + REG32(ESP) += (16+count); + /* Load new SS:(E)SP */ if(operand32 == 0) REG16(SP) = newESP & 0xffff; @@ -2040,12 +2089,6 @@ static void i386_protected_mode_retf(i386_state* cpustate, UINT8 count, UINT8 op cpustate->sreg[SS].selector = newSS; i386_load_segment_descriptor(cpustate, SS ); - if(operand32 == 0) - REG16(SP) += (8+count); - else - REG32(ESP) += (16+count); - - /* Check that DS, ES, FS and GS are valid for the new privilege level */ i386_check_sreg_validity(cpustate,DS); i386_check_sreg_validity(cpustate,ES); @@ -2067,7 +2110,7 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) { UINT32 newCS, newEIP; UINT32 newSS, newESP; // when changing privilege - I386_SREG desc; + I386_SREG desc,stack; UINT8 CPL, RPL, DPL; UINT32 newflags; @@ -2093,8 +2136,28 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) if(V8086_MODE) { - logerror("IRET: Is in Virtual 8086 mode.\n"); - FAULT(FAULT_GP,0) + if(!cpustate->IOP1 || !cpustate->IOP2) + { + logerror("IRET: Is in Virtual 8086 mode and IOPL != 3.\n"); + FAULT(FAULT_GP,0) + } + /* Is this correct? The 80386 programmers' reference says IRET should always trigger #GP(0) in V86 mode */ + if(operand32 == 0) + { + 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); + } + 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); + } } else if(NESTED_TASK) { @@ -2135,8 +2198,149 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) { if(newflags & 0x00020000) // if returning to virtual 8086 mode { + UINT8 SSRPL,SSDPL; + 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); + SSRPL = newSS & 0x03; + SSDPL = (stack.flags >> 5) & 0x03; + /* Return to v86 mode */ - popmessage("IRET (%08x): Unimplemented return to Virtual 8086 mode.",cpustate->pc); + popmessage("IRET (%08x): Returning to Virtual 8086 mode.",cpustate->pc); + // Should these be done at this point? The 386 programmers' reference is a bit confusing about this +/* if(RPL != 3) + { + logerror("IRET to V86 (%08x): Return CS RPL is not 3\n",cpustate->pc); + FAULT(FAULT_GP,newCS); + } + if(operand32 == 0) + { + if(REG16(SP)+36 > cpustate->sreg[SS].limit) + { + logerror("IRET to V86 (%08x): Stack does not have enough room left\n",cpustate->pc); + FAULT(FAULT_SS,0); + } + } + else + { + if(REG32(ESP)+36 > cpustate->sreg[SS].limit) + { + logerror("IRET to V86 (%08x): Stack does not have enough space left\n",cpustate->pc); + FAULT(FAULT_SS,0); + } + } + // code segment checks + if((newCS & ~0x07) == 0) + { + logerror("IRET to V86 (%08x): Return CS selector is null\n",cpustate->pc); + FAULT(FAULT_GP,newCS); + } + if(desc.flags & 0x04) + { // LDT + if(newCS > cpustate->ldtr.limit) + { + logerror("IRET to V86 (%08x): Return CS selector is past LDT limit\n",cpustate->pc); + FAULT(FAULT_GP,newCS); + } + } + else + { // GDT + if(newCS > cpustate->gdtr.limit) + { + logerror("IRET to V86 (%08x): Return CS selector is past GDT limit\n",cpustate->pc); + FAULT(FAULT_GP,newCS); + } + } + if((desc.flags & 0x18) != 0x18) + { + logerror("IRET to V86 (%08x): Return CS segment is not a code segment\n",cpustate->pc); + FAULT(FAULT_GP,newCS); + } + if(DPL != 3) + { + logerror("IRET to V86 (%08x): Return CS segment does not have a DPL of 3\n",cpustate->pc); + FAULT(FAULT_GP,newCS); + } + if(!(desc.flags & 0x0080)) + { + logerror("IRET to V86 (%08x): Return CS segment is not present\n",cpustate->pc); + FAULT(FAULT_NP,newCS); + } + // Stack segment checks + if((newSS & ~0x07) == 0) + { + logerror("IRET to V86 (%08x): Return SS segment is null\n",cpustate->pc); + FAULT(FAULT_GP,newSS); + } + if(desc.flags & 0x04) + { // LDT + if(newSS > cpustate->ldtr.limit) + { + logerror("IRET to V86 (%08x): Return SS selector is past LDT limit\n",cpustate->pc); + FAULT(FAULT_GP,newSS); + } + } + else + { // GDT + if(newSS > cpustate->gdtr.limit) + { + logerror("IRET to V86 (%08x): Return SS selector is past GDT limit\n",cpustate->pc); + FAULT(FAULT_GP,newSS); + } + } + if(SSRPL != RPL) + { + logerror("IRET to V86 (%08x): Return SS selector RPL is not equal to CS selector RPL\n",cpustate->pc); + FAULT(FAULT_GP,newSS); + } + if(((stack.flags & 0x0018) != 0x10) && (!(stack.flags & 0x02))) + { + logerror("IRET to V86 (%08x): Return SS segment is not a writable data segment\n",cpustate->pc); + FAULT(FAULT_GP,newSS); + } + if(SSDPL != RPL) + { + logerror("IRET to V86 (%08x): Return SS segment DPL is not equal to CS selector RPL\n",cpustate->pc); + FAULT(FAULT_GP,newSS); + } + if(!(stack.flags & 0x0080)) + { + logerror("IRET to V86 (%08x): Return SS segment is not present\n",cpustate->pc); + FAULT(FAULT_NP,newSS); + } + + if(newEIP > desc.limit) + { + logerror("IRET to V86 (%08x): New EIP is past CS segment limit\n",cpustate->pc); + FAULT(FAULT_GP,0); + } + */ + set_flags(cpustate,newflags); + 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) + { + newESP = POP32(cpustate); + newSS = POP32(cpustate) & 0xffff; + } + cpustate->sreg[ES].selector = POP32(cpustate) & 0xffff; + cpustate->sreg[DS].selector = POP32(cpustate) & 0xffff; + cpustate->sreg[FS].selector = POP32(cpustate) & 0xffff; + cpustate->sreg[GS].selector = POP32(cpustate) & 0xffff; + REG32(ESP) = newESP; // all 32 bits are loaded + cpustate->sreg[SS].selector = newSS; + i386_load_segment_descriptor(cpustate,ES); + i386_load_segment_descriptor(cpustate,DS); + i386_load_segment_descriptor(cpustate,FS); + i386_load_segment_descriptor(cpustate,GS); + i386_load_segment_descriptor(cpustate,SS); + cpustate->CPL = 3; // Virtual 8086 tasks are always run at CPL 3 } else { @@ -2383,10 +2587,10 @@ static void i386_protected_mode_iret(i386_state* cpustate, int operand32) FAULT(FAULT_GP,0) } - if(operand32 == 0) - REG16(SP) += 10; - else - REG32(ESP) += 20; +// if(operand32 == 0) +// REG16(SP) += 10; +// else +// REG32(ESP) += 20; if(operand32 == 0) { @@ -2790,6 +2994,8 @@ static CPU_EXECUTE( i386 ) { cpustate->operand_size = cpustate->sreg[CS].d; cpustate->address_size = cpustate->sreg[CS].d; + cpustate->operand_prefix = 0; + cpustate->address_prefix = 0; cpustate->segment_prefix = 0; cpustate->prev_eip = cpustate->eip; diff --git a/src/emu/cpu/i386/i386dasm.c b/src/emu/cpu/i386/i386dasm.c index 4dc8a42afad..0fdfaf7da17 100644 --- a/src/emu/cpu/i386/i386dasm.c +++ b/src/emu/cpu/i386/i386dasm.c @@ -1092,6 +1092,8 @@ static const char *const i386_sreg[8] = {"es", "cs", "ss", "ds", "fs", "gs", "?? static int address_size; static int operand_size; +static int address_prefix; +static int operand_prefix; static int max_length; static UINT64 pc; static UINT8 modrm; @@ -2021,18 +2023,25 @@ static void decode_opcode(char *s, const I386_OPCODE *op, UINT8 op1) case OP_SIZE: rex = regex = sibex = rmex = 0; - if (operand_size < 2) + if (operand_size < 2 && operand_prefix == 0) + { operand_size ^= 1; + operand_prefix = 1; + } op2 = FETCH(); decode_opcode( s, &i386_opcode_table1[op2], op2 ); return; case ADDR_SIZE: rex = regex = sibex = rmex = 0; - if (curmode != 64) - address_size ^= 1; - else - address_size ^= 3; + if(address_prefix == 0) + { + if (curmode != 64) + address_size ^= 1; + else + address_size ^= 3; + address_prefix = 1; + } op2 = FETCH(); decode_opcode( s, &i386_opcode_table1[op2], op2 ); return; @@ -2163,6 +2172,8 @@ int i386_dasm_one_ex(char *buffer, UINT64 eip, const UINT8 *oprom, int mode) curmode = mode; pre0f = 0; rex = regex = sibex = rmex = 0; + address_prefix = 0; + operand_prefix = 0; op = FETCH(); diff --git a/src/emu/cpu/i386/i386op16.c b/src/emu/cpu/i386/i386op16.c index 4e3def04992..4efccf80079 100644 --- a/src/emu/cpu/i386/i386op16.c +++ b/src/emu/cpu/i386/i386op16.c @@ -3330,7 +3330,7 @@ static void I386OP(lsl_r16_rm16)(i386_state *cpustate) // Opcode 0x0f 0x03 UINT32 limit; I386_SREG seg; - if(PROTECTED_MODE) + if(PROTECTED_MODE && !V8086_MODE) { memset(&seg, 0, sizeof(seg)); if(modrm >= 0xc0) diff --git a/src/emu/cpu/i386/i386op32.c b/src/emu/cpu/i386/i386op32.c index 69ef2d4fd31..31ffd8316f5 100644 --- a/src/emu/cpu/i386/i386op32.c +++ b/src/emu/cpu/i386/i386op32.c @@ -3147,7 +3147,7 @@ static void I386OP(lsl_r32_rm32)(i386_state *cpustate) // Opcode 0x0f 0x03 UINT32 limit; I386_SREG seg; - if(PROTECTED_MODE) + if(PROTECTED_MODE && !V8086_MODE) { memset(&seg, 0, sizeof(seg)); if(modrm >= 0xc0) diff --git a/src/emu/cpu/i386/i386ops.c b/src/emu/cpu/i386/i386ops.c index 915e509c14b..53b8485b43e 100644 --- a/src/emu/cpu/i386/i386ops.c +++ b/src/emu/cpu/i386/i386ops.c @@ -2207,13 +2207,21 @@ static void I386OP(segment_SS)(i386_state *cpustate) // Opcode 0x36 static void I386OP(operand_size)(i386_state *cpustate) // Opcode 0x66 { - cpustate->operand_size ^= 1; + if(cpustate->operand_prefix == 0) + { + cpustate->operand_size ^= 1; + cpustate->operand_prefix = 1; + } I386OP(decode_opcode)(cpustate); } static void I386OP(address_size)(i386_state *cpustate) // Opcode 0x67 { - cpustate->address_size ^= 1; + if(cpustate->address_prefix == 0) + { + cpustate->address_size ^= 1; + cpustate->address_prefix = 1; + } I386OP(decode_opcode)(cpustate); } diff --git a/src/emu/cpu/i386/i386priv.h b/src/emu/cpu/i386/i386priv.h index 628ef1660f7..7daf85b6081 100644 --- a/src/emu/cpu/i386/i386priv.h +++ b/src/emu/cpu/i386/i386priv.h @@ -225,6 +225,7 @@ struct _i386_state UINT8 IOP1; UINT8 IOP2; UINT8 NT; + UINT8 VM; UINT8 CPL; // current privilege level @@ -244,6 +245,8 @@ struct _i386_state int operand_size; int address_size; + int operand_prefix; + int address_prefix; int segment_prefix; int segment_override;