/* Copyright (C) 2006 yopyop Copyright (C) 2006-2011 DeSmuME team This file is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the this software. If not, see . */ #include #include "cp15.h" #include "MMU.h" armcp15_t cp15; bool armcp15_t::reset(armcpu_t *c) { //fprintf(stderr, "CP15 Reset\n"); this->cpu = c; this->IDCode = 0x41059461; this->cacheType = 0x0F0D2112; this->TCMSize = 0x00140180; this->ctrl = 0x00012078; this->DCConfig = 0x0; this->ICConfig = 0x0; this->writeBuffCtrl = 0x0; this->und = 0x0; this->DaccessPerm = 0x22222222; this->IaccessPerm = 0x22222222; this->protectBaseSize0 = 0x0; this->protectBaseSize1 = 0x0; this->protectBaseSize2 = 0x0; this->protectBaseSize3 = 0x0; this->protectBaseSize4 = 0x0; this->protectBaseSize5 = 0x0; this->protectBaseSize6 = 0x0; this->protectBaseSize7 = 0x0; this->cacheOp = 0x0; this->DcacheLock = 0x0; this->IcacheLock = 0x0; this->ITCMRegion = 0x0C; this->DTCMRegion = 0x0080000A; this->processID = 0; MMU.ARM9_RW_MODE = BIT7(this->ctrl); this->cpu->intVector = 0xFFFF0000 * BIT13(this->ctrl); this->cpu->LDTBit = !BIT15(this->ctrl); // TBit /* preset calculated regionmasks */ for (uint8_t i = 0; i < 8; ++i) { this->regionWriteMask_USR[i] = 0; this->regionWriteMask_SYS[i] = 0; this->regionReadMask_USR[i] = 0; this->regionReadMask_SYS[i] = 0; this->regionExecuteMask_USR[i] = 0; this->regionExecuteMask_SYS[i] = 0; this->regionWriteSet_USR[i] = 0; this->regionWriteSet_SYS[i] = 0; this->regionReadSet_USR[i] = 0; this->regionReadSet_SYS[i] = 0; this->regionExecuteSet_USR[i] = 0; this->regionExecuteSet_SYS[i] = 0; } return true; } static inline uint32_t ACCESSTYPE(uint32_t val, unsigned char n) { return (val >> (4 * n)) & 0x0F; } static inline uint32_t SIZEIDENTIFIER(uint32_t val) { return (val >> 1) & 0x1F; } static inline uint32_t SIZEBINARY(uint32_t val) { return 1 << (SIZEIDENTIFIER(val) + 1); } static inline uint32_t MASKFROMREG(uint32_t val) { return ~((SIZEBINARY(val) - 1) | 0x3F); } static inline uint32_t SETFROMREG(uint32_t val) { return val & MASKFROMREG(val); } /* sets the precalculated regions to mask,set for the affected accesstypes */ void armcp15_t::setSingleRegionAccess(uint32_t dAccess, uint32_t iAccess, unsigned char num, uint32_t mask, uint32_t set) { switch (ACCESSTYPE(dAccess, num)) { case 4: /* UNP */ case 7: /* UNP */ case 8: /* UNP */ case 9: /* UNP */ case 10: /* UNP */ case 11: /* UNP */ case 12: /* UNP */ case 13: /* UNP */ case 14: /* UNP */ case 15: /* UNP */ case 0: /* no access at all */ this->regionWriteMask_USR[num] = 0; this->regionWriteSet_USR[num] = 0xFFFFFFFF; this->regionReadMask_USR[num] = 0; this->regionReadSet_USR[num] = 0xFFFFFFFF; this->regionWriteMask_SYS[num] = 0; this->regionWriteSet_SYS[num] = 0xFFFFFFFF; this->regionReadMask_SYS[num] = 0; this->regionReadSet_SYS[num] = 0xFFFFFFFF; break; case 1: /* no access at USR, all to sys */ this->regionWriteMask_USR[num] = 0; this->regionWriteSet_USR[num] = 0xFFFFFFFF; this->regionReadMask_USR[num] = 0; this->regionReadSet_USR[num] = 0xFFFFFFFF; this->regionWriteMask_SYS[num] = mask; this->regionWriteSet_SYS[num] = set; this->regionReadMask_SYS[num] = mask; this->regionReadSet_SYS[num] = set; break; case 2: /* read at USR, all to sys */ this->regionWriteMask_USR[num] = 0; this->regionWriteSet_USR[num] = 0xFFFFFFFF; this->regionReadMask_USR[num] = mask; this->regionReadSet_USR[num] = set; this->regionWriteMask_SYS[num] = mask; this->regionWriteSet_SYS[num] = set; this->regionReadMask_SYS[num] = mask; this->regionReadSet_SYS[num] = set; break; case 3: /* all to USR, all to sys */ this->regionWriteMask_USR[num] = mask; this->regionWriteSet_USR[num] = set; this->regionReadMask_USR[num] = mask; this->regionReadSet_USR[num] = set; this->regionWriteMask_SYS[num] = mask; this->regionWriteSet_SYS[num] = set; this->regionReadMask_SYS[num] = mask; this->regionReadSet_SYS[num] = set; break; case 5: /* no access at USR, read to sys */ this->regionWriteMask_USR[num] = 0; this->regionWriteSet_USR[num] = 0xFFFFFFFF; this->regionReadMask_USR[num] = 0; this->regionReadSet_USR[num] = 0xFFFFFFFF; this->regionWriteMask_SYS[num] = 0; this->regionWriteSet_SYS[num] = 0xFFFFFFFF; this->regionReadMask_SYS[num] = mask; this->regionReadSet_SYS[num] = set; break; case 6: /* read at USR, read to sys */ this->regionWriteMask_USR[num] = 0; this->regionWriteSet_USR[num] = 0xFFFFFFFF; this->regionReadMask_USR[num] = mask; this->regionReadSet_USR[num] = set; this->regionWriteMask_SYS[num] = 0; this->regionWriteSet_SYS[num] = 0xFFFFFFFF; this->regionReadMask_SYS[num] = mask; this->regionReadSet_SYS[num] = set; } switch (ACCESSTYPE(iAccess, num)) { case 4: /* UNP */ case 7: /* UNP */ case 8: /* UNP */ case 9: /* UNP */ case 10: /* UNP */ case 11: /* UNP */ case 12: /* UNP */ case 13: /* UNP */ case 14: /* UNP */ case 15: /* UNP */ case 0: /* no access at all */ this->regionExecuteMask_USR[num] = 0; this->regionExecuteSet_USR[num] = 0xFFFFFFFF; this->regionExecuteMask_SYS[num] = 0; this->regionExecuteSet_SYS[num] = 0xFFFFFFFF; break; case 1: this->regionExecuteMask_USR[num] = 0; this->regionExecuteSet_USR[num] = 0xFFFFFFFF; this->regionExecuteMask_SYS[num] = mask; this->regionExecuteSet_SYS[num] = set; break; case 2: case 3: case 6: this->regionExecuteMask_USR[num] = mask; this->regionExecuteSet_USR[num] = set; this->regionExecuteMask_SYS[num] = mask; this->regionExecuteSet_SYS[num] = set; } } /* precalculate region masks/sets from cp15 register */ void armcp15_t::maskPrecalc() { #define precalc(num) \ { \ uint32_t mask = 0, set = 0xFFFFFFFF; /* (x & 0) == 0xFF..FF is allways false (disabled) */ \ if (BIT_N(this->protectBaseSize##num, 0)) /* if region is enabled */ \ { \ /* reason for this define: naming includes var */ \ mask = MASKFROMREG(this->protectBaseSize##num); \ set = SETFROMREG(this->protectBaseSize##num); \ if (SIZEIDENTIFIER(this->protectBaseSize##num) == 0x1F) \ { \ /* for the 4GB region, u32 suffers wraparound */ \ mask = 0; \ set = 0; /* (x & 0) == 0 is allways true (enabled) */ \ } \ } \ this->setSingleRegionAccess(this->DaccessPerm, this->IaccessPerm, num, mask, set); \ } precalc(0); precalc(1); precalc(2); precalc(3); precalc(4); precalc(5); precalc(6); precalc(7); #undef precalc } bool armcp15_t::moveCP2ARM(uint32_t *R, uint8_t CRn, uint8_t CRm, uint8_t opcode1, uint8_t opcode2) { if (!this->cpu) { fprintf(stderr, "ERROR: cp15 don\'t allocated\n"); return false; } if (this->cpu->CPSR.bits.mode == USR) return false; switch (CRn) { case 0: if (!opcode1 && !CRm) { switch (opcode2) { case 1: *R = this->cacheType; return true; case 2: *R = this->TCMSize; return true; default: *R = this->IDCode; return true; } } return false; case 1: if (!opcode1 && !opcode2 && !CRm) { *R = this->ctrl; return true; } return false; case 2: if (!opcode1 && !CRm) { switch (opcode2) { case 0: *R = this->DCConfig; return true; case 1: *R = this->ICConfig; return true; default: return false; } } return false; case 3: if (!opcode1 && ~static_cast(opcode2) && !CRm) { *R = this->writeBuffCtrl; return true; } return false; case 5: if (!opcode1 && !CRm) { switch (opcode2) { case 2: *R = this->DaccessPerm; return true; case 3: *R = this->IaccessPerm; return true; default: return false; } } return false; case 6: if (!opcode1 && !opcode2) { switch (CRm) { case 0: *R = this->protectBaseSize0; return true; case 1: *R = this->protectBaseSize1; return true; case 2: *R = this->protectBaseSize2; return true; case 3: *R = this->protectBaseSize3; return true; case 4: *R = this->protectBaseSize4; return true; case 5: *R = this->protectBaseSize5; return true; case 6: *R = this->protectBaseSize6; return true; case 7: *R = this->protectBaseSize7; return true; default: return false; } } return false; case 9: if (!opcode1) { switch (CRm) { case 0: switch (opcode2) { case 0: *R = this->DcacheLock; return true; case 1: *R = this->IcacheLock; return true; default: return false; } case 1: switch (opcode2) { case 0: *R = this->DTCMRegion; return true; case 1: *R = this->ITCMRegion; return true; default: return false; } } } return false; default: return false; } } bool armcp15_t::moveARM2CP(uint32_t val, uint8_t CRn, uint8_t CRm, uint8_t opcode1, uint8_t opcode2) { if (!this->cpu) { fprintf(stderr, "ERROR: cp15 don\'t allocated\n"); return false; } if (this->cpu->CPSR.bits.mode == USR) return false; switch (CRn) { case 1: if (!opcode1 && !opcode2 && !CRm) { // On the NDS bit0,2,7,12..19 are R/W, Bit3..6 are always set, all other bits are always zero. this->ctrl = (val & 0x000FF085) | 0x00000078; MMU.ARM9_RW_MODE = BIT7(val); // zero 31-jan-2010: change from 0x0FFF0000 to 0xFFFF0000 per gbatek this->cpu->intVector = 0xFFFF0000 * BIT13(val); this->cpu->LDTBit = !BIT15(val); // TBit return true; } return false; case 2: if (!opcode1 && !CRm) { switch (opcode2) { case 0: this->DCConfig = val; return true; case 1: this->ICConfig = val; return true; default: return false; } } return false; case 3: if (!opcode1 && !opcode2 && !CRm) { this->writeBuffCtrl = val; return true; } return false; case 5: if (!opcode1 && !CRm) { switch (opcode2) { case 2: this->DaccessPerm = val; this->maskPrecalc(); return true; case 3: this->IaccessPerm = val; this->maskPrecalc(); return true; default: return false; } } return false; case 6: if (!opcode1 && !opcode2) { switch (CRm) { case 0: this->protectBaseSize0 = val; this->maskPrecalc(); return true; case 1: this->protectBaseSize1 = val; this->maskPrecalc(); return true; case 2: this->protectBaseSize2 = val; this->maskPrecalc(); return true; case 3: this->protectBaseSize3 = val; this->maskPrecalc(); return true; case 4: this->protectBaseSize4 = val; this->maskPrecalc(); return true; case 5: this->protectBaseSize5 = val; this->maskPrecalc(); return true; case 6: this->protectBaseSize6 = val; this->maskPrecalc(); return true; case 7: this->protectBaseSize7 = val; this->maskPrecalc(); return true; default: return false; } } return false; case 7: if (!CRm && !opcode1 && opcode2 == 4) { this->cpu->waitIRQ = true; this->cpu->halt_IE_and_IF = true; // IME set deliberately omitted: only SWI sets IME to 1 return true; } return false; case 9: if (!opcode1) { switch (CRm) { case 0: switch (opcode2) { case 0: this->DcacheLock = val; return true; case 1: this->IcacheLock = val; return true; default: return false; } case 1: switch (opcode2) { case 0: MMU.DTCMRegion = this->DTCMRegion = val & 0x0FFFF000; return true; case 1: this->ITCMRegion = val; // ITCM base is not writeable! MMU.ITCMRegion = 0; return true; default: return false; } } } return false; default: return false; } } /* precalculate region masks/sets from cp15 register ----- JIT */ void maskPrecalc() { #define precalc(num) \ { \ uint32_t mask = 0, set = 0xFFFFFFFF; /* (x & 0) == 0xFF..FF is allways false (disabled) */ \ if (BIT_N(cp15.protectBaseSize##num, 0)) /* if region is enabled */ \ { \ /* reason for this define: naming includes var */ \ mask = MASKFROMREG(cp15.protectBaseSize##num); \ set = SETFROMREG(cp15.protectBaseSize##num); \ if (SIZEIDENTIFIER(cp15.protectBaseSize##num) == 0x1F) \ { \ /* for the 4GB region, u32 suffers wraparound */ \ mask = 0; \ set = 0; /* (x & 0) == 0 is allways true (enabled) */ \ } \ } \ cp15.setSingleRegionAccess(cp15.DaccessPerm, cp15.IaccessPerm, num, mask, set); \ } precalc(0); precalc(1); precalc(2); precalc(3); precalc(4); precalc(5); precalc(6); precalc(7); #undef precalc }