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,
};
enum
{
FAULT_NONE = 0,
FAULT_DOMAIN,
FAULT_PERMISSION,
};
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 );
@ -162,11 +169,96 @@ INLINE UINT32 arm7_tlb_get_second_level_descriptor( arm_state *cpustate, UINT32
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_lvl2 = 0;
UINT32 paddr;
UINT32 paddr, vaddr = *addr;
UINT8 domain, permission;
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)))
{
LOG( ( "ARM7: fetch flat, PC = %08x, vaddr = %08x\n", R15, vaddr ) );
return vaddr;
*addr = vaddr;
return TRUE;
}
else
{
@ -202,58 +295,64 @@ INLINE UINT32 arm7_tlb_translate(arm_state *cpustate, UINT32 vaddr, int mode)
{
case COPRO_TLB_UNMAPPED:
// 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 ) );
COPRO_FAULT_STATUS = (5 << 0);
LOG( ( "ARM7: Translation fault on unmapped virtual address, PC = %08x, vaddr = %08x\n", R15, vaddr ) );
COPRO_FAULT_STATUS_D = (5 << 0); // 5 = section translation fault
COPRO_FAULT_ADDRESS = vaddr;
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;
}
return FALSE;
break;
case COPRO_TLB_COARSE_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 );
}
else
{
LOG( ( "domain %d permission = %d\n", domain, permission ) );
LOG( ( "ARM7: Coarse Table, Section Domain fault on virtual address, vaddr = %08x, domain = %08x, PC = %08x\n", vaddr, domain, R15 ) );
fatalerror("ARM7: Not Yet Implemented: Coarse Table, Section Domain fault on virtual address, vaddr = %08x, domain = %08x, PC = %08x", vaddr, domain, R15);
}
break;
case COPRO_TLB_SECTION_TABLE:
{
// 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 );
}
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 Domain fault on virtual address, vaddr = %08x, domain = %08x, PC = %08x\n", vaddr, domain, R15 ) );
COPRO_FAULT_STATUS = (9 << 0);
LOG( ( "ARM7: Section 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) ? (9 << 0) : (13 << 0)) | (domain << 4); // 9 = section domain fault, 13 = section permission fault
COPRO_FAULT_ADDRESS = vaddr;
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 Domain fault on virtual address, vaddr = %08x, domain = %08x, PC = %08x\n", vaddr, domain, R15 ) );
LOG( ( "ARM7: Section 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;
case COPRO_TLB_FINE_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;
default:
// 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:
// 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 ) );
COPRO_FAULT_STATUS = (7 << 0);
LOG( ( "ARM7: Translation fault on unmapped virtual address, vaddr = %08x, PC %08X\n", vaddr, R15 ) );
COPRO_FAULT_STATUS_D = (7 << 0) | (domain << 4); // 7 = page translation fault
COPRO_FAULT_ADDRESS = vaddr;
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;
}
return FALSE;
break;
case COPRO_TLB_LARGE_PAGE:
// Large page descriptor
@ -285,7 +385,34 @@ INLINE UINT32 arm7_tlb_translate(arm_state *cpustate, UINT32 vaddr, int mode)
break;
case COPRO_TLB_SMALL_PAGE:
// 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 );
}
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;
case COPRO_TLB_TINY_PAGE:
// Tiny page descriptor
@ -297,8 +424,8 @@ INLINE UINT32 arm7_tlb_translate(arm_state *cpustate, UINT32 vaddr, int mode)
break;
}
}
return paddr;
*addr = paddr;
return TRUE;
}
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 */
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;
}
@ -783,6 +910,8 @@ CPU_GET_INFO( sa1110 )
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 )
@ -791,7 +920,7 @@ static READ32_DEVICE_HANDLER( arm7_rt_r_callback )
UINT32 opcode = offset;
UINT8 cReg = ( opcode & INSN_COPRO_CREG ) >> INSN_COPRO_CREG_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;
UINT32 data = 0;
@ -862,9 +991,18 @@ static READ32_DEVICE_HANDLER( arm7_rt_r_callback )
}
else
{
data = 0x41 | (1 << 23) | (7 << 12);
//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
if (device->type() == ARM920T)
{
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;
@ -923,7 +1061,11 @@ static READ32_DEVICE_HANDLER( arm7_rt_r_callback )
break;
case 5: // Fault Status
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;
case 6: // Fault Address
LOG( ( "arm7_rt_r_callback, Fault Address\n" ) );
@ -966,7 +1108,9 @@ static WRITE32_DEVICE_HANDLER( arm7_rt_w_callback )
}
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))
{
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
COPRO_CTRL = data & COPRO_CTRL_MASK;
@ -1014,7 +1161,11 @@ static WRITE32_DEVICE_HANDLER( arm7_rt_w_callback )
break;
case 5: // Fault Status
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;
case 6: // Fault Address
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))
{
cpustate->pendingUnd = 1;
}
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);

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 UINT32 arm7_cpu_read32(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 */
// 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 )
{
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;
@ -146,7 +149,10 @@ INLINE void arm7_cpu_write16(arm_state *cpustate, UINT32 addr, UINT16 data)
{
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;
@ -160,7 +166,10 @@ INLINE void arm7_cpu_write8(arm_state *cpustate, UINT32 addr, UINT8 data)
{
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 )
@ -169,13 +178,16 @@ INLINE void arm7_cpu_write8(arm_state *cpustate, UINT32 addr, UINT8 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;
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)
@ -197,13 +209,16 @@ INLINE UINT32 arm7_cpu_read32(arm_state *cpustate, offs_t addr)
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;
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 )
@ -219,11 +234,14 @@ INLINE UINT16 arm7_cpu_read16(arm_state *cpustate, offs_t addr)
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 )
{
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)
@ -310,6 +328,9 @@ static const char *GetModeText(int cpsr)
#define GetRegister(cpustate, rIndex) ARM7REG(sRegisterTable[GET_MODE][rIndex])
#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..
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 */
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;
UINT32 data;
@ -468,29 +489,30 @@ static int loadInc(arm_state *cpustate, UINT32 pat, UINT32 rbv, UINT32 s)
for (i = 0; i < 16; i++)
{
if ((pat >> i) & 1)
{
if (cpustate->pendingAbtD == 0) // "Overwriting of registers stops when the abort happens."
{
data = READ32(rbv += 4);
if (cpustate->pendingAbtD != 0) break;
if (i == 15) {
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 */
if (MODE32)
SET_REGISTER(cpustate, 15, data);
SET_MODE_REGISTER(cpustate, mode, 15, data);
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
SET_REGISTER(cpustate, i, data);
SET_MODE_REGISTER(cpustate, mode, i, data);
}
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;
UINT32 data;
@ -500,29 +522,31 @@ static int loadDec(arm_state *cpustate, UINT32 pat, UINT32 rbv, UINT32 s)
for (i = 15; i >= 0; i--)
{
if ((pat >> i) & 1)
{
if (cpustate->pendingAbtD == 0) // "Overwriting of registers stops when the abort happens."
{
data = READ32(rbv -= 4);
if (cpustate->pendingAbtD != 0) break;
if (i == 15) {
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 */
if (MODE32)
SET_REGISTER(cpustate, 15, data);
SET_MODE_REGISTER(cpustate, mode, 15, data);
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
SET_REGISTER(cpustate, i, data);
SET_MODE_REGISTER(cpustate, mode, i, data);
}
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;
@ -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 */
LOG(("%08x: StoreInc on R15\n", R15));
#endif
WRITE32(rbv += 4, GET_REGISTER(cpustate, i));
WRITE32(rbv += 4, GET_MODE_REGISTER(cpustate, mode, i));
result++;
}
}
return result;
} /* 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;
@ -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 */
LOG(("%08x: StoreDec on R15\n", R15));
#endif
WRITE32(rbv -= 4, GET_REGISTER(cpustate, i));
WRITE32(rbv -= 4, GET_MODE_REGISTER(cpustate, mode, i));
result++;
}
}
@ -791,8 +815,11 @@ static void HandleCoProcRT(arm_state *cpustate, UINT32 insn)
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
if (cpustate->pendingUnd == 0)
{
SET_REGISTER(cpustate, (insn >> 12) & 0xf, res);
}
}
else
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));
}
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 ((insn & 0x200000) == 0)
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
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
int curmode = GET_MODE;
SwitchMode(cpustate, eARM7_MODE_USER);
//int curmode = GET_MODE;
//SwitchMode(cpustate, eARM7_MODE_USER);
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..
SwitchMode(cpustate, curmode);
//SwitchMode(cpustate, curmode);
}
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))
{
@ -1887,15 +1917,15 @@ static void HandleMemBlock(arm_state *cpustate, UINT32 insn)
if (insn & INSN_BDT_S && ((insn & 0x8000) == 0))
{
// set to user mode - then do the transfer, and set back
int curmode = GET_MODE;
SwitchMode(cpustate, eARM7_MODE_USER);
//int curmode = GET_MODE;
//SwitchMode(cpustate, eARM7_MODE_USER);
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..
SwitchMode(cpustate, curmode);
//SwitchMode(cpustate, curmode);
}
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))
{
@ -1960,15 +1990,15 @@ static void HandleMemBlock(arm_state *cpustate, UINT32 insn)
// todo: needs to be tested..
// set to user mode - then do the transfer, and set back
int curmode = GET_MODE;
SwitchMode(cpustate, eARM7_MODE_USER);
//int curmode = GET_MODE;
//SwitchMode(cpustate, eARM7_MODE_USER);
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..
SwitchMode(cpustate, curmode);
//SwitchMode(cpustate, curmode);
}
else
result = storeInc(cpustate, insn & 0xffff, rbp);
result = storeInc(cpustate, insn & 0xffff, rbp, GET_MODE);
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)
{
// set to user mode - then do the transfer, and set back
int curmode = GET_MODE;
SwitchMode(cpustate, eARM7_MODE_USER);
//int curmode = GET_MODE;
//SwitchMode(cpustate, eARM7_MODE_USER);
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..
SwitchMode(cpustate, curmode);
//SwitchMode(cpustate, curmode);
}
else
result = storeDec(cpustate, insn & 0xffff, rbp);
result = storeDec(cpustate, insn & 0xffff, rbp, GET_MODE);
if ((insn & INSN_BDT_W) && (cpustate->pendingAbtD == 0))
{

View File

@ -139,7 +139,8 @@ enum
#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
@ -149,7 +150,7 @@ enum
#define ARM7COPRO_REGS \
UINT32 control; \
UINT32 tlbBase; \
UINT32 faultStatus; \
UINT32 faultStatus[2]; \
UINT32 faultAddress; \
UINT32 fcsePID; \
UINT32 domainAccessControl;
@ -503,16 +504,16 @@ enum
#define MODE26 (!(GET_CPSR & 0x10))
#define GET_PC (MODE32 ? R15 : R15 & 0x03FFFFFC)
enum
{
ARM7_TLB_NO_ABORT,
ARM7_TLB_ABORT_D,
ARM7_TLB_ABORT_P
};
#define ARM7_TLB_ABORT_D (1 << 0)
#define ARM7_TLB_ABORT_P (1 << 1)
#define ARM7_TLB_READ (1 << 2)
#define ARM7_TLB_WRITE (1 << 3)
/* 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 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)
extern write32_device_func arm7_coproc_do_callback;

View File

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