From 08e03939c90dd7eec66cdb3a1ef53ef76bed7a37 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Fri, 30 Dec 2011 08:59:28 +0000 Subject: [PATCH] arm7: implemented MMU permission faults [Tim Schuerewegen] arm7: return correct MMU ID Code register values for ARM920T/ARM7500 [Tim Schuerewegen] --- src/emu/cpu/arm7/arm7.c | 227 ++++++++++++++++++++++++++++++------ src/emu/cpu/arm7/arm7core.c | 126 ++++++++++++-------- src/emu/cpu/arm7/arm7core.h | 17 +-- src/emu/cpu/arm7/arm7exec.c | 7 +- 4 files changed, 280 insertions(+), 97 deletions(-) diff --git a/src/emu/cpu/arm7/arm7.c b/src/emu/cpu/arm7/arm7.c index d647f04ddce..d3c547ed3da 100644 --- a/src/emu/cpu/arm7/arm7.c +++ b/src/emu/cpu/arm7/arm7.c @@ -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 - paddr = ( desc_lvl2 & COPRO_TLB_SMALL_PAGE_MASK ) | ( vaddr & ~COPRO_TLB_SMALL_PAGE_MASK ); + { + 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); diff --git a/src/emu/cpu/arm7/arm7core.c b/src/emu/cpu/arm7/arm7core.c index b07de65ce33..f75f554de41 100644 --- a/src/emu/cpu/arm7/arm7core.c +++ b/src/emu/cpu/arm7/arm7core.c @@ -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; @@ -469,28 +490,29 @@ static int loadInc(arm_state *cpustate, UINT32 pat, UINT32 rbv, UINT32 s) { 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; @@ -501,28 +523,30 @@ static int loadDec(arm_state *cpustate, UINT32 pat, UINT32 rbv, UINT32 s) { 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,7 +815,10 @@ 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 - SET_REGISTER(cpustate, (insn >> 12) & 0xf, res); + 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)) { diff --git a/src/emu/cpu/arm7/arm7core.h b/src/emu/cpu/arm7/arm7core.h index 8209c3c89bb..80e31e2b58d 100644 --- a/src/emu/cpu/arm7/arm7core.h +++ b/src/emu/cpu/arm7/arm7core.h @@ -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; diff --git a/src/emu/cpu/arm7/arm7exec.c b/src/emu/cpu/arm7/arm7exec.c index 5cf952c46e4..7a9028663d0 100644 --- a/src/emu/cpu/arm7/arm7exec.c +++ b/src/emu/cpu/arm7/arm7exec.c @@ -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; }