/*
	Copyright (C) 2006 yopyop
	Copyright (C) 2006-2010 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "armcpu.h"

const uint32_t CP15_ACCESS_WRITE = 0;
const uint32_t CP15_ACCESS_READ = 2;
const uint32_t CP15_ACCESS_EXECUTE = 4;
const uint32_t CP15_ACCESS_WRITEUSR = CP15_ACCESS_WRITE;
const uint32_t CP15_ACCESS_WRITESYS = 1;
const uint32_t CP15_ACCESS_READUSR = CP15_ACCESS_READ;
const uint32_t CP15_ACCESS_READSYS = 3;
const uint32_t CP15_ACCESS_EXECUSR = CP15_ACCESS_EXECUTE;
const uint32_t CP15_ACCESS_EXECSYS = 5;

struct armcp15_t
{
public:
	uint32_t IDCode;
	uint32_t cacheType;
	uint32_t TCMSize;
	uint32_t ctrl;
	uint32_t DCConfig;
	uint32_t ICConfig;
	uint32_t writeBuffCtrl;
	uint32_t und;
	uint32_t DaccessPerm;
	uint32_t IaccessPerm;
	uint32_t protectBaseSize0;
	uint32_t protectBaseSize1;
	uint32_t protectBaseSize2;
	uint32_t protectBaseSize3;
	uint32_t protectBaseSize4;
	uint32_t protectBaseSize5;
	uint32_t protectBaseSize6;
	uint32_t protectBaseSize7;
	uint32_t cacheOp;
	uint32_t DcacheLock;
	uint32_t IcacheLock;
	uint32_t ITCMRegion;
	uint32_t DTCMRegion;
	uint32_t processID;
	uint32_t RAM_TAG;
	uint32_t testState;
	uint32_t cacheDbg;
	/* calculated bitmasks for the regions to decide rights uppon */
	/* calculation is done in the MCR instead of on mem access for performance */
	uint32_t regionWriteMask_USR[8];
	uint32_t regionWriteMask_SYS[8];
	uint32_t regionReadMask_USR[8];
	uint32_t regionReadMask_SYS[8];
	uint32_t regionExecuteMask_USR[8];
	uint32_t regionExecuteMask_SYS[8];
	uint32_t regionWriteSet_USR[8];
	uint32_t regionWriteSet_SYS[8];
	uint32_t regionReadSet_USR[8];
	uint32_t regionReadSet_SYS[8];
	uint32_t regionExecuteSet_USR[8];
	uint32_t regionExecuteSet_SYS[8];

	armcpu_t *cpu;

	void setSingleRegionAccess(uint32_t dAccess, uint32_t iAccess, unsigned char num, uint32_t mask, uint32_t set);
	void maskPrecalc();

public:
	armcp15_t() : IDCode(0), cacheType(0), TCMSize(0), ctrl(0), DCConfig(0), ICConfig(0), writeBuffCtrl(0), und(0), DaccessPerm(0), IaccessPerm(0), protectBaseSize0(0), protectBaseSize1(0), protectBaseSize2(0),
		protectBaseSize3(0), protectBaseSize4(0), protectBaseSize5(0), protectBaseSize6(0), protectBaseSize7(0), cacheOp(0), DcacheLock(0), IcacheLock(0), ITCMRegion(0), DTCMRegion(0), processID(0), RAM_TAG(0),
		testState(0), cacheDbg(0), cpu(nullptr)
	{
		memset(&this->regionWriteMask_USR[0], 0, sizeof(this->regionWriteMask_USR));
		memset(&this->regionWriteMask_SYS[0], 0, sizeof(this->regionWriteMask_SYS));
		memset(&this->regionReadMask_USR[0], 0, sizeof(this->regionReadMask_USR));
		memset(&this->regionReadMask_SYS[0], 0, sizeof(this->regionReadMask_SYS));
		memset(&this->regionExecuteMask_USR[0], 0, sizeof(this->regionExecuteMask_USR));
		memset(&this->regionExecuteMask_SYS[0], 0, sizeof(this->regionExecuteMask_SYS));
		memset(&this->regionWriteSet_USR[0], 0, sizeof(this->regionWriteSet_USR));
		memset(&this->regionWriteSet_SYS[0], 0, sizeof(this->regionWriteSet_SYS));
		memset(&this->regionReadSet_USR[0], 0, sizeof(this->regionReadSet_USR));
		memset(&this->regionReadSet_SYS[0], 0, sizeof(this->regionReadSet_SYS));
		memset(&this->regionExecuteSet_USR[0], 0, sizeof(this->regionExecuteSet_USR));
		memset(&this->regionExecuteSet_SYS[0], 0, sizeof(this->regionExecuteSet_SYS));
	}
	bool reset(armcpu_t *c);
	bool dataProcess(uint8_t CRd, uint8_t CRn, uint8_t CRm, uint8_t opcode1, uint8_t opcode2);
	bool load(uint8_t CRd, uint8_t adr);
	bool store(uint8_t CRd, uint8_t adr);
	bool moveCP2ARM(uint32_t *R, uint8_t CRn, uint8_t CRm, uint8_t opcode1, uint8_t opcode2);
	bool moveARM2CP(uint32_t val, uint8_t CRn, uint8_t CRm, uint8_t opcode1, uint8_t opcode2);
	bool isAccessAllowed(uint32_t address,uint32_t access);
};

extern armcp15_t cp15;
void maskPrecalc();