i386: More page fault work. [Carl]

This commit is contained in:
mahlemiut 2012-01-22 22:49:12 +00:00
parent d83925d546
commit f0553bafb5
2 changed files with 66 additions and 23 deletions

View File

@ -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;

View File

@ -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(page_dir & 1)
{
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 & 1))
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);
}
}