aboutsummaryrefslogtreecommitdiff
path: root/src/ARM.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/ARM.h')
-rw-r--r--src/ARM.h399
1 files changed, 286 insertions, 113 deletions
diff --git a/src/ARM.h b/src/ARM.h
index 92d47bb..9a9b03c 100644
--- a/src/ARM.h
+++ b/src/ARM.h
@@ -19,28 +19,36 @@
#ifndef ARM_H
#define ARM_H
+#include <algorithm>
+
#include "types.h"
#include "NDS.h"
-#include "CP15.h"
-
-// lame
-#define C_S(x) x
-#define C_N(x) x
-#define C_I(x) x
#define ROR(x, n) (((x) >> (n)) | ((x) << (32-(n))))
+enum
+{
+ RWFlags_Nonseq = (1<<5),
+ RWFlags_ForceUser = (1<<21),
+};
+
class ARM
{
public:
ARM(u32 num);
~ARM(); // destroy shit
- void Reset();
+ virtual void Reset();
- void DoSavestate(Savestate* file);
+ virtual void DoSavestate(Savestate* file);
- void JumpTo(u32 addr, bool restorecpsr = false);
+ void SetClockShift(u32 shift)
+ {
+ ClockShift = shift;
+ ClockDiffMask = (1<<shift) - 1;
+ }
+
+ virtual void JumpTo(u32 addr, bool restorecpsr = false) = 0;
void RestoreCPSR();
void Halt(u32 halt)
@@ -49,6 +57,7 @@ public:
Halted = halt;
}
+ // TODO: is this actually used??
void CheckIRQ()
{
if (!(NDS::IME[Num] & 0x1)) return;
@@ -58,7 +67,7 @@ public:
}
}
- s32 Execute();
+ virtual s32 Execute() = 0;
bool CheckCondition(u32 code)
{
@@ -93,154 +102,318 @@ public:
void TriggerIRQ();
+ void SetupCodeMem(u32 addr);
- u16 CodeRead16(u32 addr)
+
+ virtual void DataRead8(u32 addr, u32* val) = 0;
+ virtual void DataRead16(u32 addr, u32* val) = 0;
+ virtual void DataRead32(u32 addr, u32* val) = 0;
+ virtual void DataRead32S(u32 addr, u32* val) = 0;
+ virtual void DataWrite8(u32 addr, u8 val) = 0;
+ virtual void DataWrite16(u32 addr, u16 val) = 0;
+ virtual void DataWrite32(u32 addr, u32 val) = 0;
+ virtual void DataWrite32S(u32 addr, u32 val) = 0;
+
+ virtual void AddCycles_C() = 0;
+ virtual void AddCycles_CI(s32 numI) = 0;
+ virtual void AddCycles_CDI() = 0;
+ virtual void AddCycles_CD() = 0;
+
+
+ u32 Num;
+
+ // shift relative to system clock
+ // 0=33MHz 1=66MHz 2=133MHz
+ u32 ClockShift;
+ u32 ClockDiffMask;
+
+ s32 Cycles;
+ s32 CyclesToRun;
+ u32 Halted;
+
+ u32 CodeRegion;
+ s32 CodeCycles;
+
+ u32 DataRegion;
+ s32 DataCycles;
+
+ u32 R[16]; // heh
+ u32 CPSR;
+ u32 R_FIQ[8]; // holding SPSR too
+ u32 R_SVC[3];
+ u32 R_ABT[3];
+ u32 R_IRQ[3];
+ u32 R_UND[3];
+ u32 CurInstr;
+ u32 NextInstr[2];
+
+ u32 ExceptionBase;
+
+ NDS::MemRegion CodeMem;
+
+ static u32 ConditionTable[16];
+};
+
+class ARMv5 : public ARM
+{
+public:
+ ARMv5();
+
+ void Reset();
+
+ void DoSavestate(Savestate* file);
+
+ void UpdateRegionTimings(u32 addrstart, u32 addrend);
+
+ void JumpTo(u32 addr, bool restorecpsr = false);
+
+ void PrefetchAbort();
+ void DataAbort();
+
+ s32 Execute();
+
+ // all code accesses are forced nonseq 32bit
+ u32 CodeRead32(u32 addr);
+
+ void DataRead8(u32 addr, u32* val);
+ void DataRead16(u32 addr, u32* val);
+ void DataRead32(u32 addr, u32* val);
+ void DataRead32S(u32 addr, u32* val);
+ void DataWrite8(u32 addr, u8 val);
+ void DataWrite16(u32 addr, u16 val);
+ void DataWrite32(u32 addr, u32 val);
+ void DataWrite32S(u32 addr, u32 val);
+
+ void AddCycles_C()
{
- u16 val;
- // TODO eventually: on ARM9, THUMB opcodes are prefetched with 32bit reads
- if (!Num)
- {
- if (!CP15::HandleCodeRead16(addr, &val))
- val = NDS::ARM9Read16(addr);
- }
- else
- val = NDS::ARM7Read16(addr);
+ // code only. always nonseq 32-bit for ARM9.
+ s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
+ Cycles += numC;
+ }
- Cycles += Waitstates[0][(addr>>24)&0xF];
- return val;
+ void AddCycles_CI(s32 numI)
+ {
+ // code+internal
+ s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
+ Cycles += numC + numI;
}
- u32 CodeRead32(u32 addr)
+ void AddCycles_CDI()
{
- u32 val;
- if (!Num)
- {
- if (!CP15::HandleCodeRead32(addr, &val))
- val = NDS::ARM9Read32(addr);
- }
- else
- val = NDS::ARM7Read32(addr);
+ // LDR/LDM cycles. ARM9 seems to skip the internal cycle there.
+ // TODO: ITCM data fetches shouldn't be parallelized, they say
+ s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
+ s32 numD = DataCycles;
+
+ //if (DataRegion != CodeRegion)
+ Cycles += std::max(numC + numD - 6, std::max(numC, numD));
+ //else
+ // Cycles += numC + numD;
+ }
- Cycles += Waitstates[1][(addr>>24)&0xF];
- return val;
+ void AddCycles_CD()
+ {
+ // TODO: ITCM data fetches shouldn't be parallelized, they say
+ s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
+ s32 numD = DataCycles;
+
+ //if (DataRegion != CodeRegion)
+ Cycles += std::max(numC + numD - 6, std::max(numC, numD));
+ //else
+ // Cycles += numC + numD;
}
+ void GetCodeMemRegion(u32 addr, NDS::MemRegion* region);
+
+ void CP15Reset();
+ void CP15DoSavestate(Savestate* file);
+
+ void UpdateDTCMSetting();
+ void UpdateITCMSetting();
+
+ void UpdatePURegions();
+
+ void CP15Write(u32 id, u32 val);
+ u32 CP15Read(u32 id);
+
+ u32 CP15Control;
+
+ u32 DTCMSetting, ITCMSetting;
+
+ u8 ITCM[0x8000];
+ u32 ITCMSize;
+ u8 DTCM[0x4000];
+ u32 DTCMBase, DTCMSize;
+
+ u32 PU_CodeCacheable;
+ u32 PU_DataCacheable;
+ u32 PU_DataCacheWrite;
+
+ u32 PU_CodeRW;
+ u32 PU_DataRW;
+
+ u32 PU_Region[8];
+
+ // 0=dataR 1=dataW 2=codeR 4=datacache 5=datawrite 6=codecache
+ u8 PU_PrivMap[0x100000];
+ u8 PU_UserMap[0x100000];
+
+ // games operate under system mode, generally
+ #define PU_Map PU_PrivMap
- u8 DataRead8(u32 addr, u32 forceuser=0)
+ // code/16N/32N/32S
+ u8 MemTimings[0x100000][4];
+
+ s32 RegionCodeCycles;
+};
+
+class ARMv4 : public ARM
+{
+public:
+ ARMv4();
+
+ void JumpTo(u32 addr, bool restorecpsr = false);
+
+ s32 Execute();
+
+ u16 CodeRead16(u32 addr)
{
- u8 val;
- if (!Num)
- {
- if (!CP15::HandleDataRead8(addr, &val, forceuser))
- val = NDS::ARM9Read8(addr);
- }
- else
- val = NDS::ARM7Read8(addr);
+ return NDS::ARM7Read16(addr);
+ }
- Cycles += Waitstates[2][(addr>>24)&0xF];
- return val;
+ u32 CodeRead32(u32 addr)
+ {
+ return NDS::ARM7Read32(addr);
+ }
+
+ void DataRead8(u32 addr, u32* val)
+ {
+ *val = NDS::ARM7Read8(addr);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][0];
}
- u16 DataRead16(u32 addr, u32 forceuser=0)
+ void DataRead16(u32 addr, u32* val)
{
- u16 val;
addr &= ~1;
- if (!Num)
- {
- if (!CP15::HandleDataRead16(addr, &val, forceuser))
- val = NDS::ARM9Read16(addr);
- }
- else
- val = NDS::ARM7Read16(addr);
- Cycles += Waitstates[2][(addr>>24)&0xF];
- return val;
+ *val = NDS::ARM7Read16(addr);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][0];
}
- u32 DataRead32(u32 addr, u32 forceuser=0)
+ void DataRead32(u32 addr, u32* val)
{
- u32 val;
addr &= ~3;
- if (!Num)
- {
- if (!CP15::HandleDataRead32(addr, &val, forceuser))
- val = NDS::ARM9Read32(addr);
- }
- else
- val = NDS::ARM7Read32(addr);
- Cycles += Waitstates[3][(addr>>24)&0xF];
- return val;
+ *val = NDS::ARM7Read32(addr);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][2];
}
- void DataWrite8(u32 addr, u8 val, u32 forceuser=0)
+ void DataRead32S(u32 addr, u32* val)
{
- if (!Num)
- {
- if (!CP15::HandleDataWrite8(addr, val, forceuser))
- NDS::ARM9Write8(addr, val);
- }
- else
- NDS::ARM7Write8(addr, val);
+ addr &= ~3;
- Cycles += Waitstates[2][(addr>>24)&0xF];
+ *val = NDS::ARM7Read32(addr);
+ DataCycles += NDS::ARM7MemTimings[DataRegion][3];
}
- void DataWrite16(u32 addr, u16 val, u32 forceuser=0)
+ void DataWrite8(u32 addr, u8 val)
+ {
+ NDS::ARM7Write8(addr, val);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][0];
+ }
+
+ void DataWrite16(u32 addr, u16 val)
{
addr &= ~1;
- if (!Num)
- {
- if (!CP15::HandleDataWrite16(addr, val, forceuser))
- NDS::ARM9Write16(addr, val);
- }
- else
- NDS::ARM7Write16(addr, val);
- Cycles += Waitstates[2][(addr>>24)&0xF];
+ NDS::ARM7Write16(addr, val);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][0];
}
- void DataWrite32(u32 addr, u32 val, u32 forceuser=0)
+ void DataWrite32(u32 addr, u32 val)
{
addr &= ~3;
- if (!Num)
- {
- if (!CP15::HandleDataWrite32(addr, val, forceuser))
- NDS::ARM9Write32(addr, val);
- }
- else
- NDS::ARM7Write32(addr, val);
- Cycles += Waitstates[3][(addr>>24)&0xF];
+ NDS::ARM7Write32(addr, val);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][2];
}
+ void DataWrite32S(u32 addr, u32 val)
+ {
+ addr &= ~3;
+
+ NDS::ARM7Write32(addr, val);
+ DataCycles += NDS::ARM7MemTimings[DataRegion][3];
+ }
- u32 Num;
- // waitstates:
- // 0=code16 1=code32 2=data16 3=data32
- // TODO eventually: nonsequential waitstates
- s32 Waitstates[4][16];
+ void AddCycles_C()
+ {
+ // code only. this code fetch is sequential.
+ Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3];
+ }
- s32 Cycles;
- s32 CyclesToRun;
- u32 Halted;
+ void AddCycles_CI(s32 num)
+ {
+ // code+internal. results in a nonseq code fetch.
+ Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num;
+ }
- u32 R[16]; // heh
- u32 CPSR;
- u32 R_FIQ[8]; // holding SPSR too
- u32 R_SVC[3];
- u32 R_ABT[3];
- u32 R_IRQ[3];
- u32 R_UND[3];
- u32 CurInstr;
- u32 NextInstr[2];
+ void AddCycles_CDI()
+ {
+ // LDR/LDM cycles.
+ s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
+ s32 numD = DataCycles;
- u32 ExceptionBase;
+ if (DataRegion == 0x02) // mainRAM
+ {
+ if (CodeRegion == 0x02)
+ Cycles += numC + numD;
+ else
+ {
+ numC++;
+ Cycles += std::max(numC + numD - 3, std::max(numC, numD));
+ }
+ }
+ else if (CodeRegion == 0x02)
+ {
+ numD++;
+ Cycles += std::max(numC + numD - 3, std::max(numC, numD));
+ }
+ else
+ {
+ Cycles += numC + numD + 1;
+ }
+ }
- static u32 ConditionTable[16];
+ void AddCycles_CD()
+ {
+ // TODO: max gain should be 5c when writing to mainRAM
+ s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
+ s32 numD = DataCycles;
- u32 debug;
+ if (DataRegion == 0x02)
+ {
+ if (CodeRegion == 0x02)
+ Cycles += numC + numD;
+ else
+ Cycles += std::max(numC + numD - 3, std::max(numC, numD));
+ }
+ else if (CodeRegion == 0x02)
+ {
+ Cycles += std::max(numC + numD - 3, std::max(numC, numD));
+ }
+ else
+ {
+ Cycles += numC + numD;
+ }
+ }
};
namespace ARMInterpreter