From f0553bafb5d8b6ccd9d5b19c4d2d7f1ff5560d1e Mon Sep 17 00:00:00 2001 From: mahlemiut Date: Sun, 22 Jan 2012 22:49:12 +0000 Subject: [PATCH] i386: More page fault work. [Carl] --- src/emu/cpu/i386/i386.c | 2 +- src/emu/cpu/i386/i386priv.h | 87 +++++++++++++++++++++++++++---------- 2 files changed, 66 insertions(+), 23 deletions(-) diff --git a/src/emu/cpu/i386/i386.c b/src/emu/cpu/i386/i386.c index 4ed597ce0fe..8318c92d051 100644 --- a/src/emu/cpu/i386/i386.c +++ b/src/emu/cpu/i386/i386.c @@ -3099,7 +3099,7 @@ static CPU_TRANSLATE( i386 ) if (space == AS_PROGRAM) { if (cpustate->cr[0] & 0x80000000) - result = translate_address(cpustate,0,address,&error); + result = translate_address(cpustate,-1,address,&error); *address &= cpustate->a20_mask; } return result; diff --git a/src/emu/cpu/i386/i386priv.h b/src/emu/cpu/i386/i386priv.h index a578a901093..20968854d16 100644 --- a/src/emu/cpu/i386/i386priv.h +++ b/src/emu/cpu/i386/i386priv.h @@ -381,7 +381,8 @@ INLINE UINT32 i386_translate(i386_state *cpustate, int segment, UINT32 ip) return cpustate->sreg[segment].base + ip; } -INLINE int translate_address(i386_state *cpustate, bool rw, UINT32 *address, UINT32 *error) +// rwn; read = 0, write = 1, none = -1 +INLINE int translate_address(i386_state *cpustate, int rwn, UINT32 *address, UINT32 *error) { UINT32 a = *address; UINT32 pdbr = cpustate->cr[3] & 0xfffff000; @@ -390,38 +391,80 @@ INLINE int translate_address(i386_state *cpustate, bool rw, UINT32 *address, UIN UINT32 offset = a & 0xfff; UINT32 page_entry; UINT32 ret = 1; + *error = 0; - // TODO: 4MB pages + // TODO: cr0 wp bit, 486 and higher UINT32 page_dir = cpustate->program->read_dword(pdbr + directory * 4); - if (!(cpustate->cr[4] & 0x10)) + if(page_dir & 1) { - page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4)); - if(!(page_entry & 1)) - ret = 0; - else - *address = (page_entry & 0xfffff000) | offset; - } - else - { - if (page_dir & 0x80) - { - if(!(page_dir & 1)) - ret = 0; - else - *address = (page_dir & 0xffc00000) | (a & 0x003fffff); - } - else + if (!(cpustate->cr[4] & 0x10)) { page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4)); if(!(page_entry & 1)) ret = 0; + else if(!(page_entry & 2) && cpustate->CPL && (rwn == 1)) + { + *error = 1; + ret = 0; + } else + { + if(!(page_dir & 0x20) && (rwn != -1)) + cpustate->program->write_dword(pdbr + directory * 4, page_dir | 0x20); + if(!(page_entry & 0x40) && (rwn == 1)) + cpustate->program->write_dword((page_dir & 0xfffff000) + (table * 4), page_entry | 0x60); + else if(!(page_entry & 0x20) && (rwn != -1)) + cpustate->program->write_dword((page_dir & 0xfffff000) + (table * 4), page_entry | 0x20); *address = (page_entry & 0xfffff000) | offset; + } + } + else + { + if (page_dir & 0x80) + { + if(!(page_dir & 2) && cpustate->CPL && (rwn == 1)) + { + *error = 1; + ret = 0; + } + else + { + if(!(page_dir & 0x40) && (rwn == 1)) + cpustate->program->write_dword(pdbr + directory * 4, page_dir | 0x60); + else if(!(page_dir & 0x20) && (rwn != -1)) + cpustate->program->write_dword(pdbr + directory * 4, page_dir | 0x20); + *address = (page_dir & 0xffc00000) | (a & 0x003fffff); + } + } + else + { + page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4)); + if(!(page_entry & 1)) + ret = 0; + else if(!(page_entry & 2) && cpustate->CPL && (rwn == 1)) + { + *error = 1; + ret = 0; + } + else + { + if(!(page_dir & 0x20) && (rwn != -1)) + cpustate->program->write_dword(pdbr + directory * 4, page_dir | 0x20); + if(!(page_entry & 0x40) && (rwn == 1)) + cpustate->program->write_dword((page_dir & 0xfffff000) + (table * 4), page_entry | 0x60); + else if(!(page_entry & 0x20) && (rwn != -1)) + cpustate->program->write_dword((page_dir & 0xfffff000) + (table * 4), page_entry | 0x20); + *address = (page_entry & 0xfffff000) | offset; + } + } } } + else + ret = 0; if(!ret) { - *error = ((rw && 1)<<1) | ((cpustate->CPL==3)?1<<2:0); + if(rwn != -1) + *error |= ((rwn && 1)<<1) | ((cpustate->CPL==3)?1<<2:0); return 0; } return 1; @@ -436,7 +479,7 @@ INLINE void CHANGE_PC(i386_state *cpustate, UINT32 pc) if (cpustate->cr[0] & 0x80000000) // page translation enabled { - translate_address(cpustate,0,&address,&error); + translate_address(cpustate,-1,&address,&error); } } @@ -451,7 +494,7 @@ INLINE void NEAR_BRANCH(i386_state *cpustate, INT32 offs) if (cpustate->cr[0] & 0x80000000) // page translation enabled { - translate_address(cpustate,0,&address,&error); + translate_address(cpustate,-1,&address,&error); } }