arm7: implemented MMU permission faults [Tim Schuerewegen]

arm7: return correct MMU ID Code register values for ARM920T/ARM7500 [Tim Schuerewegen]
This commit is contained in:
Miodrag Milanovic 2011-12-30 08:59:28 +00:00
parent 6fe73755b8
commit 08e03939c9
4 changed files with 280 additions and 97 deletions

View File

@ -135,6 +135,13 @@ enum
TLB_FINE, TLB_FINE,
}; };
enum
{
FAULT_NONE = 0,
FAULT_DOMAIN,
FAULT_PERMISSION,
};
INLINE UINT32 arm7_tlb_get_first_level_descriptor( arm_state *cpustate, UINT32 vaddr ) INLINE UINT32 arm7_tlb_get_first_level_descriptor( arm_state *cpustate, UINT32 vaddr )
{ {
UINT32 entry_paddr = ( COPRO_TLB_BASE & COPRO_TLB_BASE_MASK ) | ( ( vaddr & COPRO_TLB_VADDR_FLTI_MASK ) >> COPRO_TLB_VADDR_FLTI_MASK_SHIFT ); UINT32 entry_paddr = ( COPRO_TLB_BASE & COPRO_TLB_BASE_MASK ) | ( ( vaddr & COPRO_TLB_VADDR_FLTI_MASK ) >> COPRO_TLB_VADDR_FLTI_MASK_SHIFT );
@ -162,11 +169,96 @@ INLINE UINT32 arm7_tlb_get_second_level_descriptor( arm_state *cpustate, UINT32
return cpustate->program->read_dword( desc_lvl2 ); return cpustate->program->read_dword( desc_lvl2 );
} }
INLINE UINT32 arm7_tlb_translate(arm_state *cpustate, UINT32 vaddr, int mode) INLINE int detect_fault( arm_state *cpustate, int permission, int ap, int flags)
{
switch (permission)
{
case 0 : // "No access - Any access generates a domain fault"
{
return FAULT_DOMAIN;
}
break;
case 1 : // "Client - Accesses are checked against the access permission bits in the section or page descriptor"
{
switch (ap)
{
case 0 :
{
int s = (COPRO_CTRL & COPRO_CTRL_SYSTEM) ? 1 : 0;
int r = (COPRO_CTRL & COPRO_CTRL_ROM) ? 1 : 0;
if (s == 0)
{
if (r == 0) // "Any access generates a permission fault"
{
return FAULT_PERMISSION;
}
else // "Any write generates a permission fault"
{
if (flags & ARM7_TLB_WRITE)
{
return FAULT_PERMISSION;
}
}
}
else
{
if (r == 0) // "Only Supervisor read permitted"
{
if ((GET_MODE == eARM7_MODE_USER) || (flags & ARM7_TLB_WRITE))
{
return FAULT_PERMISSION;
}
}
else // "Reserved" -> assume same behaviour as S=0/R=0 case
{
return FAULT_PERMISSION;
}
}
}
break;
case 1 : // "Access allowed only in Supervisor mode"
{
if (GET_MODE == eARM7_MODE_USER)
{
return FAULT_PERMISSION;
}
}
break;
case 2 : // "Writes in User mode cause permission fault"
{
if ((GET_MODE == eARM7_MODE_USER) && (flags & ARM7_TLB_WRITE))
{
return FAULT_PERMISSION;
}
}
break;
case 3 : // "All access types permitted in both modes"
{
return FAULT_NONE;
}
break;
}
}
break;
case 2 : // "Reserved - Reserved. Currently behaves like the no access mode"
{
return FAULT_DOMAIN;
}
break;
case 3 : // "Manager - Accesses are not checked against the access permission bits so a permission fault cannot be generated"
{
return FAULT_NONE;
}
break;
}
return FAULT_NONE;
}
INLINE int arm7_tlb_translate(arm_state *cpustate, UINT32 *addr, int flags)
{ {
UINT32 desc_lvl1; UINT32 desc_lvl1;
UINT32 desc_lvl2 = 0; UINT32 desc_lvl2 = 0;
UINT32 paddr; UINT32 paddr, vaddr = *addr;
UINT8 domain, permission; UINT8 domain, permission;
if (vaddr < 32 * 1024 * 1024) if (vaddr < 32 * 1024 * 1024)
@ -187,7 +279,8 @@ INLINE UINT32 arm7_tlb_translate(arm_state *cpustate, UINT32 vaddr, int mode)
if ((R15 == (cpustate->mmu_enable_addr + 4)) || (R15 == (cpustate->mmu_enable_addr + 8))) if ((R15 == (cpustate->mmu_enable_addr + 4)) || (R15 == (cpustate->mmu_enable_addr + 8)))
{ {
LOG( ( "ARM7: fetch flat, PC = %08x, vaddr = %08x\n", R15, vaddr ) ); LOG( ( "ARM7: fetch flat, PC = %08x, vaddr = %08x\n", R15, vaddr ) );
return vaddr; *addr = vaddr;
return TRUE;
} }
else else
{ {
@ -202,58 +295,64 @@ INLINE UINT32 arm7_tlb_translate(arm_state *cpustate, UINT32 vaddr, int mode)
{ {
case COPRO_TLB_UNMAPPED: case COPRO_TLB_UNMAPPED:
// Unmapped, generate a translation fault // Unmapped, generate a translation fault
if (mode == ARM7_TLB_ABORT_D) if (flags & ARM7_TLB_ABORT_D)
{ {
LOG( ( "ARM7: Not Yet Implemented: Translation fault on unmapped virtual address, PC = %08x, vaddr = %08x\n", R15, vaddr ) ); LOG( ( "ARM7: Translation fault on unmapped virtual address, PC = %08x, vaddr = %08x\n", R15, vaddr ) );
COPRO_FAULT_STATUS = (5 << 0); COPRO_FAULT_STATUS_D = (5 << 0); // 5 = section translation fault
COPRO_FAULT_ADDRESS = vaddr; COPRO_FAULT_ADDRESS = vaddr;
cpustate->pendingAbtD = 1; cpustate->pendingAbtD = 1;
} }
else if (mode == ARM7_TLB_ABORT_P) else if (flags & ARM7_TLB_ABORT_P)
{ {
LOG( ( "ARM7: Not Yet Implemented: Translation fault on unmapped virtual address, PC = %08x, vaddr = %08x\n", R15, vaddr ) ); LOG( ( "ARM7: Translation fault on unmapped virtual address, PC = %08x, vaddr = %08x\n", R15, vaddr ) );
cpustate->pendingAbtP = 1; cpustate->pendingAbtP = 1;
} }
return FALSE;
break; break;
case COPRO_TLB_COARSE_TABLE: case COPRO_TLB_COARSE_TABLE:
// Entry is the physical address of a coarse second-level table // Entry is the physical address of a coarse second-level table
if (permission == 1) if ((permission == 1) || (permission == 3))
{ {
desc_lvl2 = arm7_tlb_get_second_level_descriptor( cpustate, TLB_COARSE, desc_lvl1, vaddr ); desc_lvl2 = arm7_tlb_get_second_level_descriptor( cpustate, TLB_COARSE, desc_lvl1, vaddr );
} }
else else
{ {
LOG( ( "domain %d permission = %d\n", domain, permission ) ); fatalerror("ARM7: Not Yet Implemented: Coarse Table, Section Domain fault on virtual address, vaddr = %08x, domain = %08x, PC = %08x", vaddr, domain, R15);
LOG( ( "ARM7: Coarse Table, Section Domain fault on virtual address, vaddr = %08x, domain = %08x, PC = %08x\n", vaddr, domain, R15 ) );
} }
break; break;
case COPRO_TLB_SECTION_TABLE: case COPRO_TLB_SECTION_TABLE:
{
// Entry is a section // Entry is a section
if ((permission == 1) || (permission == 3)) UINT8 ap = (desc_lvl1 >> 10) & 3;
int fault = detect_fault( cpustate, permission, ap, flags);
if (fault == FAULT_NONE)
{ {
paddr = ( desc_lvl1 & COPRO_TLB_SECTION_PAGE_MASK ) | ( vaddr & ~COPRO_TLB_SECTION_PAGE_MASK ); paddr = ( desc_lvl1 & COPRO_TLB_SECTION_PAGE_MASK ) | ( vaddr & ~COPRO_TLB_SECTION_PAGE_MASK );
} }
else else
{ {
if (mode == ARM7_TLB_ABORT_D) if (flags & ARM7_TLB_ABORT_D)
{ {
LOG( ( "domain %d permission = %d\n", domain, permission ) ); LOG( ( "ARM7: Section Table, Section %s fault on virtual address, vaddr = %08x, PC = %08x\n", (fault == FAULT_DOMAIN) ? "domain" : "permission", vaddr, R15 ) );
LOG( ( "ARM7: Section Table, Section Domain fault on virtual address, vaddr = %08x, domain = %08x, PC = %08x\n", vaddr, domain, R15 ) ); COPRO_FAULT_STATUS_D = ((fault == FAULT_DOMAIN) ? (9 << 0) : (13 << 0)) | (domain << 4); // 9 = section domain fault, 13 = section permission fault
COPRO_FAULT_STATUS = (9 << 0);
COPRO_FAULT_ADDRESS = vaddr; COPRO_FAULT_ADDRESS = vaddr;
cpustate->pendingAbtD = 1; cpustate->pendingAbtD = 1;
LOG( ( "vaddr %08X desc_lvl1 %08X domain %d permission %d ap %d s %d r %d mode %d read %d write %d\n",
vaddr, desc_lvl1, domain, permission, ap, (COPRO_CTRL & COPRO_CTRL_SYSTEM) ? 1 : 0, (COPRO_CTRL & COPRO_CTRL_ROM) ? 1 : 0,
GET_MODE, flags & ARM7_TLB_READ ? 1 : 0, flags & ARM7_TLB_WRITE ? 1 : 0) );
} }
else if (mode == ARM7_TLB_ABORT_P) else if (flags & ARM7_TLB_ABORT_P)
{ {
LOG( ( "domain %d permission = %d\n", domain, permission ) ); LOG( ( "ARM7: Section Table, Section %s fault on virtual address, vaddr = %08x, PC = %08x\n", (fault == FAULT_DOMAIN) ? "domain" : "permission", vaddr, R15 ) );
LOG( ( "ARM7: Section Table, Section Domain fault on virtual address, vaddr = %08x, domain = %08x, PC = %08x\n", vaddr, domain, R15 ) );
cpustate->pendingAbtP = 1; cpustate->pendingAbtP = 1;
}
return FALSE;
} }
} }
break; break;
case COPRO_TLB_FINE_TABLE: case COPRO_TLB_FINE_TABLE:
// Entry is the physical address of a fine second-level table // Entry is the physical address of a fine second-level table
LOG( ( "ARM7: Not Yet Implemented: fine second-level TLB lookup, PC = %08x, vaddr = %08x\n", R15, vaddr ) ); fatalerror("ARM7: Not Yet Implemented: fine second-level TLB lookup, PC = %08x, vaddr = %08x", R15, vaddr);
break; break;
default: default:
// Entry is the physical address of a three-legged termite-eaten table // Entry is the physical address of a three-legged termite-eaten table
@ -266,18 +365,19 @@ INLINE UINT32 arm7_tlb_translate(arm_state *cpustate, UINT32 vaddr, int mode)
{ {
case COPRO_TLB_UNMAPPED: case COPRO_TLB_UNMAPPED:
// Unmapped, generate a translation fault // Unmapped, generate a translation fault
if (mode == ARM7_TLB_ABORT_D) if (flags & ARM7_TLB_ABORT_D)
{ {
LOG( ( "ARM7: Not Yet Implemented: Translation fault on unmapped virtual address, vaddr = %08x, PC %08X\n", vaddr, R15 ) ); LOG( ( "ARM7: Translation fault on unmapped virtual address, vaddr = %08x, PC %08X\n", vaddr, R15 ) );
COPRO_FAULT_STATUS = (7 << 0); COPRO_FAULT_STATUS_D = (7 << 0) | (domain << 4); // 7 = page translation fault
COPRO_FAULT_ADDRESS = vaddr; COPRO_FAULT_ADDRESS = vaddr;
cpustate->pendingAbtD = 1; cpustate->pendingAbtD = 1;
} }
else if (mode == ARM7_TLB_ABORT_P) else if (flags & ARM7_TLB_ABORT_P)
{ {
LOG( ( "ARM7: Not Yet Implemented: Translation fault on unmapped virtual address, vaddr = %08x, PC %08X\n", vaddr, R15 ) ); LOG( ( "ARM7: Translation fault on unmapped virtual address, vaddr = %08x, PC %08X\n", vaddr, R15 ) );
cpustate->pendingAbtP = 1; cpustate->pendingAbtP = 1;
} }
return FALSE;
break; break;
case COPRO_TLB_LARGE_PAGE: case COPRO_TLB_LARGE_PAGE:
// Large page descriptor // Large page descriptor
@ -285,7 +385,34 @@ INLINE UINT32 arm7_tlb_translate(arm_state *cpustate, UINT32 vaddr, int mode)
break; break;
case COPRO_TLB_SMALL_PAGE: case COPRO_TLB_SMALL_PAGE:
// Small page descriptor // Small page descriptor
{
UINT8 ap = ((((desc_lvl2 >> 4) & 0xFF) >> (((vaddr >> 10) & 3) << 1)) & 3);
int fault = detect_fault( cpustate, permission, ap, flags);
if (fault == FAULT_NONE)
{
paddr = ( desc_lvl2 & COPRO_TLB_SMALL_PAGE_MASK ) | ( vaddr & ~COPRO_TLB_SMALL_PAGE_MASK ); paddr = ( desc_lvl2 & COPRO_TLB_SMALL_PAGE_MASK ) | ( vaddr & ~COPRO_TLB_SMALL_PAGE_MASK );
}
else
{
if (flags & ARM7_TLB_ABORT_D)
{
// hapyfish expects a data abort when something tries to write to a read-only memory location from user mode
LOG( ( "ARM7: Page Table, Section %s fault on virtual address, vaddr = %08x, PC = %08x\n", (fault == FAULT_DOMAIN) ? "domain" : "permission", vaddr, R15 ) );
COPRO_FAULT_STATUS_D = ((fault == FAULT_DOMAIN) ? (11 << 0) : (15 << 0)) | (domain << 4); // 11 = page domain fault, 15 = page permission fault
COPRO_FAULT_ADDRESS = vaddr;
cpustate->pendingAbtD = 1;
LOG( ( "vaddr %08X desc_lvl2 %08X domain %d permission %d ap %d s %d r %d mode %d read %d write %d\n",
vaddr, desc_lvl2, domain, permission, ap, (COPRO_CTRL & COPRO_CTRL_SYSTEM) ? 1 : 0, (COPRO_CTRL & COPRO_CTRL_ROM) ? 1 : 0,
GET_MODE, flags & ARM7_TLB_READ ? 1 : 0, flags & ARM7_TLB_WRITE ? 1 : 0) );
}
else if (flags & ARM7_TLB_ABORT_P)
{
LOG( ( "ARM7: Page Table, Section %s fault on virtual address, vaddr = %08x, PC = %08x\n", (fault == FAULT_DOMAIN) ? "domain" : "permission", vaddr, R15 ) );
cpustate->pendingAbtP = 1;
}
return FALSE;
}
}
break; break;
case COPRO_TLB_TINY_PAGE: case COPRO_TLB_TINY_PAGE:
// Tiny page descriptor // Tiny page descriptor
@ -297,8 +424,8 @@ INLINE UINT32 arm7_tlb_translate(arm_state *cpustate, UINT32 vaddr, int mode)
break; break;
} }
} }
*addr = paddr;
return paddr; return TRUE;
} }
static CPU_TRANSLATE( arm7 ) static CPU_TRANSLATE( arm7 )
@ -308,7 +435,7 @@ static CPU_TRANSLATE( arm7 )
/* only applies to the program address space and only does something if the MMU's enabled */ /* only applies to the program address space and only does something if the MMU's enabled */
if( space == AS_PROGRAM && ( COPRO_CTRL & COPRO_CTRL_MMU_EN ) ) if( space == AS_PROGRAM && ( COPRO_CTRL & COPRO_CTRL_MMU_EN ) )
{ {
*address = arm7_tlb_translate(cpustate, *address, ARM7_TLB_NO_ABORT); return arm7_tlb_translate(cpustate, address, 0);
} }
return TRUE; return TRUE;
} }
@ -783,6 +910,8 @@ CPU_GET_INFO( sa1110 )
static WRITE32_DEVICE_HANDLER( arm7_do_callback ) static WRITE32_DEVICE_HANDLER( arm7_do_callback )
{ {
arm_state *cpustate = get_safe_token(device);
cpustate->pendingUnd = 1;
} }
static READ32_DEVICE_HANDLER( arm7_rt_r_callback ) static READ32_DEVICE_HANDLER( arm7_rt_r_callback )
@ -791,7 +920,7 @@ static READ32_DEVICE_HANDLER( arm7_rt_r_callback )
UINT32 opcode = offset; UINT32 opcode = offset;
UINT8 cReg = ( opcode & INSN_COPRO_CREG ) >> INSN_COPRO_CREG_SHIFT; UINT8 cReg = ( opcode & INSN_COPRO_CREG ) >> INSN_COPRO_CREG_SHIFT;
UINT8 op2 = ( opcode & INSN_COPRO_OP2 ) >> INSN_COPRO_OP2_SHIFT; UINT8 op2 = ( opcode & INSN_COPRO_OP2 ) >> INSN_COPRO_OP2_SHIFT;
// UINT8 op3 = opcode & INSN_COPRO_OP3; UINT8 op3 = opcode & INSN_COPRO_OP3;
UINT8 cpnum = (opcode & INSN_COPRO_CPNUM) >> INSN_COPRO_CPNUM_SHIFT; UINT8 cpnum = (opcode & INSN_COPRO_CPNUM) >> INSN_COPRO_CPNUM_SHIFT;
UINT32 data = 0; UINT32 data = 0;
@ -862,9 +991,18 @@ static READ32_DEVICE_HANDLER( arm7_rt_r_callback )
} }
else else
{ {
data = 0x41 | (1 << 23) | (7 << 12); if (device->type() == ARM920T)
//data = (0x41 << 24) | (1 << 20) | (2 << 16) | (0x920 << 4) | (0 << 0); // ARM920T (S3C24xx) {
//data = (0x41 << 24) | (0 << 20) | (1 << 16) | (0x710 << 4) | (0 << 0); // ARM7500 data = (0x41 << 24) | (1 << 20) | (2 << 16) | (0x920 << 4) | (0 << 0); // ARM920T (S3C24xx)
}
else if (device->type() == ARM7500)
{
data = (0x41 << 24) | (0 << 20) | (1 << 16) | (0x710 << 4) | (0 << 0); // ARM7500
}
else
{
data = 0x41 | (1 << 23) | (7 << 12); // <-- where did this come from?
}
} }
break; break;
@ -923,7 +1061,11 @@ static READ32_DEVICE_HANDLER( arm7_rt_r_callback )
break; break;
case 5: // Fault Status case 5: // Fault Status
LOG( ( "arm7_rt_r_callback, Fault Status\n" ) ); LOG( ( "arm7_rt_r_callback, Fault Status\n" ) );
data = COPRO_FAULT_STATUS; switch (op3)
{
case 0: data = COPRO_FAULT_STATUS_D; break;
case 1: data = COPRO_FAULT_STATUS_P; break;
}
break; break;
case 6: // Fault Address case 6: // Fault Address
LOG( ( "arm7_rt_r_callback, Fault Address\n" ) ); LOG( ( "arm7_rt_r_callback, Fault Address\n" ) );
@ -966,7 +1108,9 @@ static WRITE32_DEVICE_HANDLER( arm7_rt_w_callback )
} }
else else
{ {
fatalerror("ARM7: Unhandled coprocessor %d\n", cpnum); LOG( ("ARM7: Unhandled coprocessor %d\n", cpnum) );
cpustate->pendingUnd = 1;
return;
} }
} }
@ -999,7 +1143,10 @@ static WRITE32_DEVICE_HANDLER( arm7_rt_w_callback )
} }
if (((data & COPRO_CTRL_MMU_EN) == 0) && ((COPRO_CTRL & COPRO_CTRL_MMU_EN) != 0)) if (((data & COPRO_CTRL_MMU_EN) == 0) && ((COPRO_CTRL & COPRO_CTRL_MMU_EN) != 0))
{ {
R15 = arm7_tlb_translate( cpustate, R15, ARM7_TLB_NO_ABORT); if (!arm7_tlb_translate( cpustate, &R15, 0))
{
fatalerror("ARM7_MMU_ENABLE_HACK translate failed");
}
} }
#endif #endif
COPRO_CTRL = data & COPRO_CTRL_MASK; COPRO_CTRL = data & COPRO_CTRL_MASK;
@ -1014,7 +1161,11 @@ static WRITE32_DEVICE_HANDLER( arm7_rt_w_callback )
break; break;
case 5: // Fault Status case 5: // Fault Status
LOG( ( "arm7_rt_w_callback Fault Status = %08x (%d) (%d)\n", data, op2, op3 ) ); LOG( ( "arm7_rt_w_callback Fault Status = %08x (%d) (%d)\n", data, op2, op3 ) );
COPRO_FAULT_STATUS = data; switch (op3)
{
case 0: COPRO_FAULT_STATUS_D = data; break;
case 1: COPRO_FAULT_STATUS_P = data; break;
}
break; break;
case 6: // Fault Address case 6: // Fault Address
LOG( ( "arm7_rt_w_callback Fault Address = %08x (%d) (%d)\n", data, op2, op3 ) ); LOG( ( "arm7_rt_w_callback Fault Address = %08x (%d) (%d)\n", data, op2, op3 ) );
@ -1046,10 +1197,12 @@ static WRITE32_DEVICE_HANDLER( arm7_rt_w_callback )
void arm7_dt_r_callback(arm_state *cpustate, UINT32 insn, UINT32 *prn, UINT32 (*read32)(arm_state *cpustate, UINT32 addr)) void arm7_dt_r_callback(arm_state *cpustate, UINT32 insn, UINT32 *prn, UINT32 (*read32)(arm_state *cpustate, UINT32 addr))
{ {
cpustate->pendingUnd = 1;
} }
void arm7_dt_w_callback(arm_state *cpustate, UINT32 insn, UINT32 *prn, void (*write32)(arm_state *cpustate, UINT32 addr, UINT32 data)) void arm7_dt_w_callback(arm_state *cpustate, UINT32 insn, UINT32 *prn, void (*write32)(arm_state *cpustate, UINT32 addr, UINT32 data))
{ {
cpustate->pendingUnd = 1;
} }
DEFINE_LEGACY_CPU_DEVICE(ARM7, arm7); DEFINE_LEGACY_CPU_DEVICE(ARM7, arm7);

View File

@ -105,7 +105,7 @@ INLINE void arm7_cpu_write16(arm_state *cpustate, UINT32 addr, UINT16 data);
INLINE void arm7_cpu_write8(arm_state *cpustate, UINT32 addr, UINT8 data); INLINE void arm7_cpu_write8(arm_state *cpustate, UINT32 addr, UINT8 data);
INLINE UINT32 arm7_cpu_read32(arm_state *cpustate, UINT32 addr); INLINE UINT32 arm7_cpu_read32(arm_state *cpustate, UINT32 addr);
INLINE UINT16 arm7_cpu_read16(arm_state *cpustate, UINT32 addr); INLINE UINT16 arm7_cpu_read16(arm_state *cpustate, UINT32 addr);
INLINE UINT8 arm7_cpu_read8(arm_state *cpustate, offs_t addr); INLINE UINT8 arm7_cpu_read8(arm_state *cpustate, UINT32 addr);
/* Static Vars */ /* Static Vars */
// Note: for multi-cpu implementation, this approach won't work w/o modification // Note: for multi-cpu implementation, this approach won't work w/o modification
@ -131,7 +131,10 @@ INLINE void arm7_cpu_write32(arm_state *cpustate, UINT32 addr, UINT32 data)
{ {
if( COPRO_CTRL & COPRO_CTRL_MMU_EN ) if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{ {
addr = arm7_tlb_translate( cpustate, addr, ARM7_TLB_ABORT_D ); if (!arm7_tlb_translate( cpustate, &addr, ARM7_TLB_ABORT_D | ARM7_TLB_WRITE ))
{
return;
}
} }
addr &= ~3; addr &= ~3;
@ -146,7 +149,10 @@ INLINE void arm7_cpu_write16(arm_state *cpustate, UINT32 addr, UINT16 data)
{ {
if( COPRO_CTRL & COPRO_CTRL_MMU_EN ) if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{ {
addr = arm7_tlb_translate( cpustate, addr, ARM7_TLB_ABORT_D ); if (!arm7_tlb_translate( cpustate, &addr, ARM7_TLB_ABORT_D | ARM7_TLB_WRITE ))
{
return;
}
} }
addr &= ~1; addr &= ~1;
@ -160,7 +166,10 @@ INLINE void arm7_cpu_write8(arm_state *cpustate, UINT32 addr, UINT8 data)
{ {
if( COPRO_CTRL & COPRO_CTRL_MMU_EN ) if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{ {
addr = arm7_tlb_translate( cpustate, addr, ARM7_TLB_ABORT_D ); if (!arm7_tlb_translate( cpustate, &addr, ARM7_TLB_ABORT_D | ARM7_TLB_WRITE ))
{
return;
}
} }
if ( cpustate->endian == ENDIANNESS_BIG ) if ( cpustate->endian == ENDIANNESS_BIG )
@ -169,13 +178,16 @@ INLINE void arm7_cpu_write8(arm_state *cpustate, UINT32 addr, UINT8 data)
cpustate->program->write_byte(addr, data); cpustate->program->write_byte(addr, data);
} }
INLINE UINT32 arm7_cpu_read32(arm_state *cpustate, offs_t addr) INLINE UINT32 arm7_cpu_read32(arm_state *cpustate, UINT32 addr)
{ {
UINT32 result; UINT32 result;
if( COPRO_CTRL & COPRO_CTRL_MMU_EN ) if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{ {
addr = arm7_tlb_translate( cpustate, addr, ARM7_TLB_ABORT_D ); if (!arm7_tlb_translate( cpustate, &addr, ARM7_TLB_ABORT_D | ARM7_TLB_READ ))
{
return 0;
}
} }
if (addr & 3) if (addr & 3)
@ -197,13 +209,16 @@ INLINE UINT32 arm7_cpu_read32(arm_state *cpustate, offs_t addr)
return result; return result;
} }
INLINE UINT16 arm7_cpu_read16(arm_state *cpustate, offs_t addr) INLINE UINT16 arm7_cpu_read16(arm_state *cpustate, UINT32 addr)
{ {
UINT16 result; UINT16 result;
if( COPRO_CTRL & COPRO_CTRL_MMU_EN ) if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{ {
addr = arm7_tlb_translate( cpustate, addr, ARM7_TLB_ABORT_D ); if (!arm7_tlb_translate( cpustate, &addr, ARM7_TLB_ABORT_D | ARM7_TLB_READ ))
{
return 0;
}
} }
if ( cpustate->endian == ENDIANNESS_BIG ) if ( cpustate->endian == ENDIANNESS_BIG )
@ -219,11 +234,14 @@ INLINE UINT16 arm7_cpu_read16(arm_state *cpustate, offs_t addr)
return result; return result;
} }
INLINE UINT8 arm7_cpu_read8(arm_state *cpustate, offs_t addr) INLINE UINT8 arm7_cpu_read8(arm_state *cpustate, UINT32 addr)
{ {
if( COPRO_CTRL & COPRO_CTRL_MMU_EN ) if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{ {
addr = arm7_tlb_translate( cpustate, addr, ARM7_TLB_ABORT_D ); if (!arm7_tlb_translate( cpustate, &addr, ARM7_TLB_ABORT_D | ARM7_TLB_READ ))
{
return 0;
}
} }
// Handle through normal 8 bit handler (for 32 bit cpu) // Handle through normal 8 bit handler (for 32 bit cpu)
@ -310,6 +328,9 @@ static const char *GetModeText(int cpsr)
#define GetRegister(cpustate, rIndex) ARM7REG(sRegisterTable[GET_MODE][rIndex]) #define GetRegister(cpustate, rIndex) ARM7REG(sRegisterTable[GET_MODE][rIndex])
#define SetRegister(cpustate, rIndex, value) ARM7REG(sRegisterTable[GET_MODE][rIndex]) = value #define SetRegister(cpustate, rIndex, value) ARM7REG(sRegisterTable[GET_MODE][rIndex]) = value
#define GetModeRegister(cpustate, mode, rIndex) ARM7REG(sRegisterTable[mode][rIndex])
#define SetModeRegister(cpustate, mode, rIndex, value) ARM7REG(sRegisterTable[mode][rIndex]) = value
// I could prob. convert to macro, but Switchmode shouldn't occur that often in emulated code.. // I could prob. convert to macro, but Switchmode shouldn't occur that often in emulated code..
INLINE void SwitchMode(arm_state *cpustate, int cpsr_mode_val) INLINE void SwitchMode(arm_state *cpustate, int cpsr_mode_val)
{ {
@ -458,7 +479,7 @@ static UINT32 decodeShift(arm_state *cpustate, UINT32 insn, UINT32 *pCarry)
} /* decodeShift */ } /* decodeShift */
static int loadInc(arm_state *cpustate, UINT32 pat, UINT32 rbv, UINT32 s) static int loadInc(arm_state *cpustate, UINT32 pat, UINT32 rbv, UINT32 s, int mode)
{ {
int i, result; int i, result;
UINT32 data; UINT32 data;
@ -468,29 +489,30 @@ static int loadInc(arm_state *cpustate, UINT32 pat, UINT32 rbv, UINT32 s)
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
{ {
if ((pat >> i) & 1) if ((pat >> i) & 1)
{
if (cpustate->pendingAbtD == 0) // "Overwriting of registers stops when the abort happens."
{ {
data = READ32(rbv += 4); data = READ32(rbv += 4);
if (cpustate->pendingAbtD != 0) break;
if (i == 15) { if (i == 15) {
if (s) /* Pull full contents from stack */ if (s) /* Pull full contents from stack */
SET_REGISTER(cpustate, 15, data); SET_MODE_REGISTER(cpustate, mode, 15, data);
else /* Pull only address, preserve mode & status flags */ else /* Pull only address, preserve mode & status flags */
if (MODE32) if (MODE32)
SET_REGISTER(cpustate, 15, data); SET_MODE_REGISTER(cpustate, mode, 15, data);
else else
{ {
SET_REGISTER(cpustate, 15, (GET_REGISTER(cpustate, 15) & ~0x03FFFFFC) | (data & 0x03FFFFFC)); SET_MODE_REGISTER(cpustate, mode, 15, (GET_MODE_REGISTER(cpustate, mode, 15) & ~0x03FFFFFC) | (data & 0x03FFFFFC));
} }
} else } else
SET_REGISTER(cpustate, i, data); SET_MODE_REGISTER(cpustate, mode, i, data);
}
result++; result++;
} }
} }
return result; return result;
} }
static int loadDec(arm_state *cpustate, UINT32 pat, UINT32 rbv, UINT32 s) static int loadDec(arm_state *cpustate, UINT32 pat, UINT32 rbv, UINT32 s, int mode)
{ {
int i, result; int i, result;
UINT32 data; UINT32 data;
@ -500,29 +522,31 @@ static int loadDec(arm_state *cpustate, UINT32 pat, UINT32 rbv, UINT32 s)
for (i = 15; i >= 0; i--) for (i = 15; i >= 0; i--)
{ {
if ((pat >> i) & 1) if ((pat >> i) & 1)
{
if (cpustate->pendingAbtD == 0) // "Overwriting of registers stops when the abort happens."
{ {
data = READ32(rbv -= 4); data = READ32(rbv -= 4);
if (cpustate->pendingAbtD != 0) break;
if (i == 15) { if (i == 15) {
if (s) /* Pull full contents from stack */ if (s) /* Pull full contents from stack */
SET_REGISTER(cpustate, 15, data); SET_MODE_REGISTER(cpustate, mode, 15, data);
else /* Pull only address, preserve mode & status flags */ else /* Pull only address, preserve mode & status flags */
if (MODE32) if (MODE32)
SET_REGISTER(cpustate, 15, data); SET_MODE_REGISTER(cpustate, mode, 15, data);
else else
{ {
SET_REGISTER(cpustate, 15, (GET_REGISTER(cpustate, 15) & ~0x03FFFFFC) | (data & 0x03FFFFFC)); SET_MODE_REGISTER(cpustate, mode, 15, (GET_MODE_REGISTER(cpustate, mode, 15) & ~0x03FFFFFC) | (data & 0x03FFFFFC));
} }
} }
else else
SET_REGISTER(cpustate, i, data); SET_MODE_REGISTER(cpustate, mode, i, data);
}
result++; result++;
} }
} }
return result; return result;
} }
static int storeInc(arm_state *cpustate, UINT32 pat, UINT32 rbv) static int storeInc(arm_state *cpustate, UINT32 pat, UINT32 rbv, int mode)
{ {
int i, result; int i, result;
@ -535,14 +559,14 @@ static int storeInc(arm_state *cpustate, UINT32 pat, UINT32 rbv)
if (i == 15) /* R15 is plus 12 from address of STM */ if (i == 15) /* R15 is plus 12 from address of STM */
LOG(("%08x: StoreInc on R15\n", R15)); LOG(("%08x: StoreInc on R15\n", R15));
#endif #endif
WRITE32(rbv += 4, GET_REGISTER(cpustate, i)); WRITE32(rbv += 4, GET_MODE_REGISTER(cpustate, mode, i));
result++; result++;
} }
} }
return result; return result;
} /* storeInc */ } /* storeInc */
static int storeDec(arm_state *cpustate, UINT32 pat, UINT32 rbv) static int storeDec(arm_state *cpustate, UINT32 pat, UINT32 rbv, int mode)
{ {
int i, result; int i, result;
@ -555,7 +579,7 @@ static int storeDec(arm_state *cpustate, UINT32 pat, UINT32 rbv)
if (i == 15) /* R15 is plus 12 from address of STM */ if (i == 15) /* R15 is plus 12 from address of STM */
LOG(("%08x: StoreDec on R15\n", R15)); LOG(("%08x: StoreDec on R15\n", R15));
#endif #endif
WRITE32(rbv -= 4, GET_REGISTER(cpustate, i)); WRITE32(rbv -= 4, GET_MODE_REGISTER(cpustate, mode, i));
result++; result++;
} }
} }
@ -791,8 +815,11 @@ static void HandleCoProcRT(arm_state *cpustate, UINT32 insn)
if (arm7_coproc_rt_r_callback) if (arm7_coproc_rt_r_callback)
{ {
UINT32 res = arm7_coproc_rt_r_callback(cpustate->device, insn, 0); // RT Read handler must parse opcode & return appropriate result UINT32 res = arm7_coproc_rt_r_callback(cpustate->device, insn, 0); // RT Read handler must parse opcode & return appropriate result
if (cpustate->pendingUnd == 0)
{
SET_REGISTER(cpustate, (insn >> 12) & 0xf, res); SET_REGISTER(cpustate, (insn >> 12) & 0xf, res);
} }
}
else else
LOG(("%08x: Co-Processor Register Transfer executed, but no RT Read callback defined!\n", R15)); LOG(("%08x: Co-Processor Register Transfer executed, but no RT Read callback defined!\n", R15));
} }
@ -864,6 +891,8 @@ static void HandleCoProcDT(arm_state *cpustate, UINT32 insn)
LOG(("%08x: Co-Processer Data Transfer executed, but no WRITE callback defined!\n", R15)); LOG(("%08x: Co-Processer Data Transfer executed, but no WRITE callback defined!\n", R15));
} }
if (cpustate->pendingUnd != 0) return;
// If writeback not used - ensure the original value of RN is restored in case co-proc callback changed value // If writeback not used - ensure the original value of RN is restored in case co-proc callback changed value
if ((insn & 0x200000) == 0) if ((insn & 0x200000) == 0)
SET_REGISTER(cpustate, rn, ornv); SET_REGISTER(cpustate, rn, ornv);
@ -1826,16 +1855,17 @@ static void HandleMemBlock(arm_state *cpustate, UINT32 insn)
// S Flag Set, but R15 not in list = User Bank Transfer // S Flag Set, but R15 not in list = User Bank Transfer
if (insn & INSN_BDT_S && (insn & 0x8000) == 0) if (insn & INSN_BDT_S && (insn & 0x8000) == 0)
{ {
// !! actually switching to user mode triggers a section permission fault in Happy Fish 302-in-1 (BP C0030DF4, press F5 ~16 times) !!
// set to user mode - then do the transfer, and set back // set to user mode - then do the transfer, and set back
int curmode = GET_MODE; //int curmode = GET_MODE;
SwitchMode(cpustate, eARM7_MODE_USER); //SwitchMode(cpustate, eARM7_MODE_USER);
LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15)); LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15));
result = loadInc(cpustate, insn & 0xffff, rbp, insn & INSN_BDT_S); result = loadInc(cpustate, insn & 0xffff, rbp, insn & INSN_BDT_S, eARM7_MODE_USER);
// todo - not sure if Writeback occurs on User registers also.. // todo - not sure if Writeback occurs on User registers also..
SwitchMode(cpustate, curmode); //SwitchMode(cpustate, curmode);
} }
else else
result = loadInc(cpustate, insn & 0xffff, rbp, insn & INSN_BDT_S); result = loadInc(cpustate, insn & 0xffff, rbp, insn & INSN_BDT_S, GET_MODE);
if ((insn & INSN_BDT_W) && (cpustate->pendingAbtD == 0)) if ((insn & INSN_BDT_W) && (cpustate->pendingAbtD == 0))
{ {
@ -1887,15 +1917,15 @@ static void HandleMemBlock(arm_state *cpustate, UINT32 insn)
if (insn & INSN_BDT_S && ((insn & 0x8000) == 0)) if (insn & INSN_BDT_S && ((insn & 0x8000) == 0))
{ {
// set to user mode - then do the transfer, and set back // set to user mode - then do the transfer, and set back
int curmode = GET_MODE; //int curmode = GET_MODE;
SwitchMode(cpustate, eARM7_MODE_USER); //SwitchMode(cpustate, eARM7_MODE_USER);
LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15)); LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15));
result = loadDec(cpustate, insn & 0xffff, rbp, insn & INSN_BDT_S); result = loadDec(cpustate, insn & 0xffff, rbp, insn & INSN_BDT_S, eARM7_MODE_USER);
// todo - not sure if Writeback occurs on User registers also.. // todo - not sure if Writeback occurs on User registers also..
SwitchMode(cpustate, curmode); //SwitchMode(cpustate, curmode);
} }
else else
result = loadDec(cpustate, insn & 0xffff, rbp, insn & INSN_BDT_S); result = loadDec(cpustate, insn & 0xffff, rbp, insn & INSN_BDT_S, GET_MODE);
if ((insn & INSN_BDT_W) && (cpustate->pendingAbtD == 0)) if ((insn & INSN_BDT_W) && (cpustate->pendingAbtD == 0))
{ {
@ -1960,15 +1990,15 @@ static void HandleMemBlock(arm_state *cpustate, UINT32 insn)
// todo: needs to be tested.. // todo: needs to be tested..
// set to user mode - then do the transfer, and set back // set to user mode - then do the transfer, and set back
int curmode = GET_MODE; //int curmode = GET_MODE;
SwitchMode(cpustate, eARM7_MODE_USER); //SwitchMode(cpustate, eARM7_MODE_USER);
LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15)); LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15));
result = storeInc(cpustate, insn & 0xffff, rbp); result = storeInc(cpustate, insn & 0xffff, rbp, eARM7_MODE_USER);
// todo - not sure if Writeback occurs on User registers also.. // todo - not sure if Writeback occurs on User registers also..
SwitchMode(cpustate, curmode); //SwitchMode(cpustate, curmode);
} }
else else
result = storeInc(cpustate, insn & 0xffff, rbp); result = storeInc(cpustate, insn & 0xffff, rbp, GET_MODE);
if ((insn & INSN_BDT_W) && (cpustate->pendingAbtD == 0)) if ((insn & INSN_BDT_W) && (cpustate->pendingAbtD == 0))
{ {
@ -1987,15 +2017,15 @@ static void HandleMemBlock(arm_state *cpustate, UINT32 insn)
if (insn & INSN_BDT_S) if (insn & INSN_BDT_S)
{ {
// set to user mode - then do the transfer, and set back // set to user mode - then do the transfer, and set back
int curmode = GET_MODE; //int curmode = GET_MODE;
SwitchMode(cpustate, eARM7_MODE_USER); //SwitchMode(cpustate, eARM7_MODE_USER);
LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15)); LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15));
result = storeDec(cpustate, insn & 0xffff, rbp); result = storeDec(cpustate, insn & 0xffff, rbp, eARM7_MODE_USER);
// todo - not sure if Writeback occurs on User registers also.. // todo - not sure if Writeback occurs on User registers also..
SwitchMode(cpustate, curmode); //SwitchMode(cpustate, curmode);
} }
else else
result = storeDec(cpustate, insn & 0xffff, rbp); result = storeDec(cpustate, insn & 0xffff, rbp, GET_MODE);
if ((insn & INSN_BDT_W) && (cpustate->pendingAbtD == 0)) if ((insn & INSN_BDT_W) && (cpustate->pendingAbtD == 0))
{ {

View File

@ -139,7 +139,8 @@ enum
#define COPRO_DOMAIN_ACCESS_CONTROL cpustate->domainAccessControl #define COPRO_DOMAIN_ACCESS_CONTROL cpustate->domainAccessControl
#define COPRO_FAULT_STATUS cpustate->faultStatus #define COPRO_FAULT_STATUS_D cpustate->faultStatus[0]
#define COPRO_FAULT_STATUS_P cpustate->faultStatus[1]
#define COPRO_FAULT_ADDRESS cpustate->faultAddress #define COPRO_FAULT_ADDRESS cpustate->faultAddress
@ -149,7 +150,7 @@ enum
#define ARM7COPRO_REGS \ #define ARM7COPRO_REGS \
UINT32 control; \ UINT32 control; \
UINT32 tlbBase; \ UINT32 tlbBase; \
UINT32 faultStatus; \ UINT32 faultStatus[2]; \
UINT32 faultAddress; \ UINT32 faultAddress; \
UINT32 fcsePID; \ UINT32 fcsePID; \
UINT32 domainAccessControl; UINT32 domainAccessControl;
@ -503,16 +504,16 @@ enum
#define MODE26 (!(GET_CPSR & 0x10)) #define MODE26 (!(GET_CPSR & 0x10))
#define GET_PC (MODE32 ? R15 : R15 & 0x03FFFFFC) #define GET_PC (MODE32 ? R15 : R15 & 0x03FFFFFC)
enum #define ARM7_TLB_ABORT_D (1 << 0)
{ #define ARM7_TLB_ABORT_P (1 << 1)
ARM7_TLB_NO_ABORT, #define ARM7_TLB_READ (1 << 2)
ARM7_TLB_ABORT_D, #define ARM7_TLB_WRITE (1 << 3)
ARM7_TLB_ABORT_P
};
/* At one point I thought these needed to be cpu implementation specific, but they don't.. */ /* At one point I thought these needed to be cpu implementation specific, but they don't.. */
#define GET_REGISTER(state, reg) GetRegister(state, reg) #define GET_REGISTER(state, reg) GetRegister(state, reg)
#define SET_REGISTER(state, reg, val) SetRegister(state, reg, val) #define SET_REGISTER(state, reg, val) SetRegister(state, reg, val)
#define GET_MODE_REGISTER(state, mode, reg) GetModeRegister(state, mode, reg)
#define SET_MODE_REGISTER(state, mode, reg, val) SetModeRegister(state, mode, reg, val)
#define ARM7_CHECKIRQ arm7_check_irq_state(cpustate) #define ARM7_CHECKIRQ arm7_check_irq_state(cpustate)
extern write32_device_func arm7_coproc_do_callback; extern write32_device_func arm7_coproc_do_callback;

View File

@ -63,12 +63,12 @@
if ( COPRO_CTRL & COPRO_CTRL_MMU_EN ) if ( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{ {
raddr = arm7_tlb_translate(cpustate, raddr, ARM7_TLB_ABORT_P); if (!arm7_tlb_translate(cpustate, &raddr, ARM7_TLB_ABORT_P | ARM7_TLB_READ))
if (cpustate->pendingAbtP != 0)
{ {
goto skip_exec; goto skip_exec;
} }
} }
insn = cpustate->direct->read_decrypted_word(raddr); insn = cpustate->direct->read_decrypted_word(raddr);
ARM7_ICOUNT -= (3 - thumbCycles[insn >> 8]); ARM7_ICOUNT -= (3 - thumbCycles[insn >> 8]);
switch ((insn & THUMB_INSN_TYPE) >> THUMB_INSN_TYPE_SHIFT) switch ((insn & THUMB_INSN_TYPE) >> THUMB_INSN_TYPE_SHIFT)
@ -1196,8 +1196,7 @@
if ( COPRO_CTRL & COPRO_CTRL_MMU_EN ) if ( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{ {
raddr = arm7_tlb_translate(cpustate, raddr, ARM7_TLB_ABORT_P); if (!arm7_tlb_translate(cpustate, &raddr, ARM7_TLB_ABORT_P | ARM7_TLB_READ))
if (cpustate->pendingAbtP != 0)
{ {
goto skip_exec; goto skip_exec;
} }