/* 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 "debug.h" #include "MMU.h" armcp15_t *armcp15_new(armcpu_t * c) { int i; armcp15_t *armcp15 = (armcp15_t*)malloc(sizeof(armcp15_t)); if(!armcp15) return NULL; armcp15->cpu = c; armcp15->IDCode = 0x41059461; armcp15->cacheType = 0x0F0D2112; armcp15->TCMSize = 0x00140180; armcp15->ctrl = 0x00012078; armcp15->DCConfig = 0x0; armcp15->ICConfig = 0x0; armcp15->writeBuffCtrl = 0x0; armcp15->und = 0x0; armcp15->DaccessPerm = 0x22222222; armcp15->IaccessPerm = 0x22222222; armcp15->protectBaseSize0 = 0x0; armcp15->protectBaseSize1 = 0x0; armcp15->protectBaseSize2 = 0x0; armcp15->protectBaseSize3 = 0x0; armcp15->protectBaseSize4 = 0x0; armcp15->protectBaseSize5 = 0x0; armcp15->protectBaseSize6 = 0x0; armcp15->protectBaseSize7 = 0x0; armcp15->cacheOp = 0x0; armcp15->DcacheLock = 0x0; armcp15->IcacheLock = 0x0; armcp15->ITCMRegion = 0x0C; armcp15->DTCMRegion = 0x0080000A; armcp15->processID = 0; MMU.ARM9_RW_MODE = static_cast(BIT7(armcp15->ctrl)); armcp15->cpu->intVector = 0xFFFF0000 * (BIT13(armcp15->ctrl)); armcp15->cpu->LDTBit = !BIT15(armcp15->ctrl); //TBit /* preset calculated regionmasks */ for (i=0;i<8;i++) { armcp15->regionWriteMask_USR[i] = 0 ; armcp15->regionWriteMask_SYS[i] = 0 ; armcp15->regionReadMask_USR[i] = 0 ; armcp15->regionReadMask_SYS[i] = 0 ; armcp15->regionExecuteMask_USR[i] = 0 ; armcp15->regionExecuteMask_SYS[i] = 0 ; armcp15->regionWriteSet_USR[i] = 0 ; armcp15->regionWriteSet_SYS[i] = 0 ; armcp15->regionReadSet_USR[i] = 0 ; armcp15->regionReadSet_SYS[i] = 0 ; armcp15->regionExecuteSet_USR[i] = 0 ; armcp15->regionExecuteSet_SYS[i] = 0 ; } ; return armcp15; } //#define ACCESSTYPE(val,n) (((val) >> (4*n)) & 0x0F) static inline uint32_t ACCESSTYPE(uint32_t val, unsigned char n) { return (val >> (4 * n)) & 0x0F; } //#define SIZEIDENTIFIER(val) ((((val) >> 1) & 0x1F)) static inline uint32_t SIZEIDENTIFIER(uint32_t val) { return (val >> 1) & 0x1F; } //#define SIZEBINARY(val) (1 << (SIZEIDENTIFIER(val)+1)) static inline uint32_t SIZEBINARY(uint32_t val) { return 1 << (SIZEIDENTIFIER(val) + 1); } //#define MASKFROMREG(val) (~((SIZEBINARY(val)-1) | 0x3F)) static inline uint32_t MASKFROMREG(uint32_t val) { return ~((SIZEBINARY(val) - 1) | 0x3F); } //#define SETFROMREG(val) ((val) & MASKFROMREG(val)) static inline uint32_t SETFROMREG(uint32_t val) { return val & MASKFROMREG(val); } /* sets the precalculated regions to mask,set for the affected accesstypes */ static void armcp15_setSingleRegionAccess(armcp15_t *armcp15,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 */ armcp15->regionWriteMask_USR[num] = 0 ; armcp15->regionWriteSet_USR[num] = 0xFFFFFFFF ; armcp15->regionReadMask_USR[num] = 0 ; armcp15->regionReadSet_USR[num] = 0xFFFFFFFF ; armcp15->regionWriteMask_SYS[num] = 0 ; armcp15->regionWriteSet_SYS[num] = 0xFFFFFFFF ; armcp15->regionReadMask_SYS[num] = 0 ; armcp15->regionReadSet_SYS[num] = 0xFFFFFFFF ; break ; case 1: /* no access at USR, all to sys */ armcp15->regionWriteMask_USR[num] = 0 ; armcp15->regionWriteSet_USR[num] = 0xFFFFFFFF ; armcp15->regionReadMask_USR[num] = 0 ; armcp15->regionReadSet_USR[num] = 0xFFFFFFFF ; armcp15->regionWriteMask_SYS[num] = mask ; armcp15->regionWriteSet_SYS[num] = set ; armcp15->regionReadMask_SYS[num] = mask ; armcp15->regionReadSet_SYS[num] = set ; break ; case 2: /* read at USR, all to sys */ armcp15->regionWriteMask_USR[num] = 0 ; armcp15->regionWriteSet_USR[num] = 0xFFFFFFFF ; armcp15->regionReadMask_USR[num] = mask ; armcp15->regionReadSet_USR[num] = set ; armcp15->regionWriteMask_SYS[num] = mask ; armcp15->regionWriteSet_SYS[num] = set ; armcp15->regionReadMask_SYS[num] = mask ; armcp15->regionReadSet_SYS[num] = set ; break ; case 3: /* all to USR, all to sys */ armcp15->regionWriteMask_USR[num] = mask ; armcp15->regionWriteSet_USR[num] = set ; armcp15->regionReadMask_USR[num] = mask ; armcp15->regionReadSet_USR[num] = set ; armcp15->regionWriteMask_SYS[num] = mask ; armcp15->regionWriteSet_SYS[num] = set ; armcp15->regionReadMask_SYS[num] = mask ; armcp15->regionReadSet_SYS[num] = set ; break ; case 5: /* no access at USR, read to sys */ armcp15->regionWriteMask_USR[num] = 0 ; armcp15->regionWriteSet_USR[num] = 0xFFFFFFFF ; armcp15->regionReadMask_USR[num] = 0 ; armcp15->regionReadSet_USR[num] = 0xFFFFFFFF ; armcp15->regionWriteMask_SYS[num] = 0 ; armcp15->regionWriteSet_SYS[num] = 0xFFFFFFFF ; armcp15->regionReadMask_SYS[num] = mask ; armcp15->regionReadSet_SYS[num] = set ; break ; case 6: /* read at USR, read to sys */ armcp15->regionWriteMask_USR[num] = 0 ; armcp15->regionWriteSet_USR[num] = 0xFFFFFFFF ; armcp15->regionReadMask_USR[num] = mask ; armcp15->regionReadSet_USR[num] = set ; armcp15->regionWriteMask_SYS[num] = 0 ; armcp15->regionWriteSet_SYS[num] = 0xFFFFFFFF ; armcp15->regionReadMask_SYS[num] = mask ; armcp15->regionReadSet_SYS[num] = set ; break ; } 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 */ armcp15->regionExecuteMask_USR[num] = 0 ; armcp15->regionExecuteSet_USR[num] = 0xFFFFFFFF ; armcp15->regionExecuteMask_SYS[num] = 0 ; armcp15->regionExecuteSet_SYS[num] = 0xFFFFFFFF ; break ; case 1: armcp15->regionExecuteMask_USR[num] = 0 ; armcp15->regionExecuteSet_USR[num] = 0xFFFFFFFF ; armcp15->regionExecuteMask_SYS[num] = mask ; armcp15->regionExecuteSet_SYS[num] = set ; break ; case 2: case 3: case 6: armcp15->regionExecuteMask_USR[num] = mask ; armcp15->regionExecuteSet_USR[num] = set ; armcp15->regionExecuteMask_SYS[num] = mask ; armcp15->regionExecuteSet_SYS[num] = set ; break ; } } /* precalculate region masks/sets from cp15 register */ static void armcp15_maskPrecalc(armcp15_t *armcp15) { #define precalc(num) { \ uint32_t mask = 0, set = 0xFFFFFFFF ; /* (x & 0) == 0xFF..FF is allways false (disabled) */ \ if (BIT_N(armcp15->protectBaseSize##num,0)) /* if region is enabled */ \ { /* reason for this define: naming includes var */ \ mask = MASKFROMREG(armcp15->protectBaseSize##num) ; \ set = SETFROMREG(armcp15->protectBaseSize##num) ; \ if (SIZEIDENTIFIER(armcp15->protectBaseSize##num)==0x1F) \ { /* for the 4GB region, uint32_t suffers wraparound */ \ mask = 0 ; set = 0 ; /* (x & 0) == 0 is allways true (enabled) */ \ } \ } \ armcp15_setSingleRegionAccess(armcp15,armcp15->DaccessPerm,armcp15->IaccessPerm,num,mask,set) ; \ } precalc(0) ; precalc(1) ; precalc(2) ; precalc(3) ; precalc(4) ; precalc(5) ; precalc(6) ; precalc(7) ; } /*bool armcp15_isAccessAllowed(armcp15_t *armcp15,uint32_t address,uint32_t access) { int i ; if (!(armcp15->ctrl & 1)) return true ;*/ /* protection checking is not enabled */ /*for (i=0;i<8;i++) { switch (access) { case CP15_ACCESS_WRITEUSR: if ((address & armcp15->regionWriteMask_USR[i]) == armcp15->regionWriteSet_USR[i]) return true ; break ; case CP15_ACCESS_WRITESYS: if ((address & armcp15->regionWriteMask_SYS[i]) == armcp15->regionWriteSet_SYS[i]) return true ; break ; case CP15_ACCESS_READUSR: if ((address & armcp15->regionReadMask_USR[i]) == armcp15->regionReadSet_USR[i]) return true ; break ; case CP15_ACCESS_READSYS: if ((address & armcp15->regionReadMask_SYS[i]) == armcp15->regionReadSet_SYS[i]) return true ; break ; case CP15_ACCESS_EXECUSR: if ((address & armcp15->regionExecuteMask_USR[i]) == armcp15->regionExecuteSet_USR[i]) return true ; break ; case CP15_ACCESS_EXECSYS: if ((address & armcp15->regionExecuteMask_SYS[i]) == armcp15->regionExecuteSet_SYS[i]) return true ; break ; } }*/ /* when protections are enabled, but no region allows access, deny access */ /*return false ; }*/ /*bool armcp15_dataProcess(armcp15_t *, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t) { //LOG("Unsupported CP15 operation : DataProcess\n"); return false; }*/ /*bool armcp15_load(armcp15_t *, uint8_t, uint8_t) { //LOG("Unsupported CP15 operation : Load\n"); return false; }*/ /*bool armcp15_store(armcp15_t *, uint8_t, uint8_t) { //LOG("Unsupported CP15 operation : Store\n"); return false; }*/ bool armcp15_moveCP2ARM(armcp15_t *armcp15, uint32_t * R, uint8_t CRn, uint8_t CRm, uint8_t opcode1, uint8_t opcode2) { if(armcp15->cpu->CPSR.bits.mode == USR) return false; switch(CRn) { case 0: if((opcode1 == 0)&&(CRm==0)) { switch(opcode2) { case 1: *R = armcp15->cacheType; return true; case 2: *R = armcp15->TCMSize; return true; default: *R = armcp15->IDCode; return true; } } return false; case 1: if((opcode1==0) && (opcode2==0) && (CRm==0)) { *R = armcp15->ctrl; //LOG("CP15: CPtoARM ctrl %08X\n", armcp15->ctrl); return true; } return false; case 2: if((opcode1==0) && (CRm==0)) { switch(opcode2) { case 0: *R = armcp15->DCConfig; return true; case 1: *R = armcp15->ICConfig; return true; default: return false; } } return false; case 3: if((opcode1==0) && (opcode2==0) && (CRm==0)) { *R = armcp15->writeBuffCtrl; //LOG("CP15: CPtoARM writeBuffer ctrl %08X\n", armcp15->writeBuffCtrl); return true; } return false; case 5: if((opcode1==0) && (CRm==0)) { switch(opcode2) { case 2: *R = armcp15->DaccessPerm; return true; case 3: *R = armcp15->IaccessPerm; return true; default: return false; } } return false; case 6: if((opcode1==0) && (opcode2==0)) { switch(CRm) { case 0: *R = armcp15->protectBaseSize0; return true; case 1: *R = armcp15->protectBaseSize1; return true; case 2: *R = armcp15->protectBaseSize2; return true; case 3: *R = armcp15->protectBaseSize3; return true; case 4: *R = armcp15->protectBaseSize4; return true; case 5: *R = armcp15->protectBaseSize5; return true; case 6: *R = armcp15->protectBaseSize6; return true; case 7: *R = armcp15->protectBaseSize7; return true; default: return false; } } return false; case 9: if((opcode1==0)) { switch(CRm) { case 0: switch(opcode2) { case 0: *R = armcp15->DcacheLock; return true; case 1: *R = armcp15->IcacheLock; return true; default: return false; } case 1: switch(opcode2) { case 0: *R = armcp15->DTCMRegion; return true; case 1: *R = armcp15->ITCMRegion; return true; default: return false; } } } return false; default: //LOG("Unsupported CP15 operation : MRC\n"); return false; } } static uint32_t CP15wait4IRQ(armcpu_t *cpu) { cpu->waitIRQ = true; cpu->halt_IE_and_IF = true; //IME set deliberately omitted: only SWI sets IME to 1 return 1; } bool armcp15_moveARM2CP(armcp15_t *armcp15, uint32_t val, uint8_t CRn, uint8_t CRm, uint8_t opcode1, uint8_t opcode2) { if(armcp15->cpu->CPSR.bits.mode == USR) return false; switch(CRn) { case 1: if((opcode1==0) && (opcode2==0) && (CRm==0)) { //On the NDS bit0,2,7,12..19 are R/W, Bit3..6 are always set, all other bits are always zero. armcp15->ctrl = (val & 0x000FF085) | 0x00000078; MMU.ARM9_RW_MODE = static_cast(BIT7(val)); //zero 31-jan-2010: change from 0x0FFF0000 to 0xFFFF0000 per gbatek armcp15->cpu->intVector = 0xFFFF0000 * (BIT13(val)); armcp15->cpu->LDTBit = !BIT15(val); //TBit //LOG("CP15: ARMtoCP ctrl %08X (val %08X)\n", armcp15->ctrl, val); return true; } return false; case 2: if((opcode1==0) && (CRm==0)) { switch(opcode2) { case 0: armcp15->DCConfig = val; return true; case 1: armcp15->ICConfig = val; return true; default: return false; } } return false; case 3: if((opcode1==0) && (opcode2==0) && (CRm==0)) { armcp15->writeBuffCtrl = val; //LOG("CP15: ARMtoCP writeBuffer ctrl %08X\n", armcp15->writeBuffCtrl); return true; } return false; case 5: if((opcode1==0) && (CRm==0)) { switch(opcode2) { case 2: armcp15->DaccessPerm = val; armcp15_maskPrecalc(armcp15); return true; case 3: armcp15->IaccessPerm = val; armcp15_maskPrecalc(armcp15); return true; default: return false; } } return false; case 6: if((opcode1==0) && (opcode2==0)) { switch(CRm) { case 0: armcp15->protectBaseSize0 = val; armcp15_maskPrecalc(armcp15) ; return true; case 1: armcp15->protectBaseSize1 = val; armcp15_maskPrecalc(armcp15) ; return true; case 2: armcp15->protectBaseSize2 = val; armcp15_maskPrecalc(armcp15) ; return true; case 3: armcp15->protectBaseSize3 = val; armcp15_maskPrecalc(armcp15) ; return true; case 4: armcp15->protectBaseSize4 = val; armcp15_maskPrecalc(armcp15) ; return true; case 5: armcp15->protectBaseSize5 = val; armcp15_maskPrecalc(armcp15) ; return true; case 6: armcp15->protectBaseSize6 = val; armcp15_maskPrecalc(armcp15) ; return true; case 7: armcp15->protectBaseSize7 = val; armcp15_maskPrecalc(armcp15) ; return true; default: return false; } } return false; case 7: if((CRm==0)&&(opcode1==0)&&((opcode2==4))) { CP15wait4IRQ(armcp15->cpu); return true; } return false; case 9: if((opcode1==0)) { switch(CRm) { case 0: switch(opcode2) { case 0: armcp15->DcacheLock = val; return true; case 1: armcp15->IcacheLock = val; return true; default: return false; } case 1: switch(opcode2) { case 0: MMU.DTCMRegion = armcp15->DTCMRegion = val & 0x0FFFF000; return true; case 1: armcp15->ITCMRegion = val; //ITCM base is not writeable! MMU.ITCMRegion = 0; return true; default: return false; } } } return false; default: return false; } }