/*
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;
}
}