/* Copyright (C) 2006 yopyop Copyright (C) 2009-2012 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 #include #include #include "types.h" #include "instructions.h" #include "cp15.h" #include "bios.h" #include "NDSSystem.h" #include "MMU_timing.h" #ifdef HAVE_LUA #include "lua-engine.h" #endif #ifdef HAVE_JIT #include "arm_jit.h" #endif template static uint32_t armcpu_prefetch(); static inline uint32_t armcpu_prefetch(armcpu_t *armcpu) { if (!armcpu->proc_ID) return armcpu_prefetch<0>(); else return armcpu_prefetch<1>(); } armcpu_t NDS_ARM7; armcpu_t NDS_ARM9; int armcpu_new(armcpu_t *armcpu, uint32_t id) { armcpu->proc_ID = id; armcpu->stalled = 0; armcpu_init(armcpu, 0); return 0; } // call this whenever CPSR is changed (other than CNVZQ or T flags); interrupts may need to be unleashed void armcpu_t::changeCPSR() { // but all it does is give them a chance to unleash by forcing an immediate reschedule // TODO - we could actually set CPSR through here and look for a change in the I bit // that would be a little optimization as well as a safety measure if we prevented setting CPSR directly NDS_Reschedule(); } void armcpu_init(armcpu_t *armcpu, uint32_t adr) { #if defined(_M_X64) || defined(__x86_64__) memcpy(&armcpu->cond_table[0], &arm_cond_table[0], sizeof(arm_cond_table)); #endif armcpu->LDTBit = !armcpu->proc_ID; // Si ARM9 utiliser le syte v5 pour le load armcpu->intVector = 0xFFFF0000 * !armcpu->proc_ID; armcpu->waitIRQ = false; armcpu->halt_IE_and_IF = false; armcpu->intrWaitARM_state = 0; for (int i = 0; i < 16; ++i) armcpu->R[i] = 0; armcpu->CPSR.val = armcpu->SPSR.val = SYS; armcpu->R13_usr = armcpu->R14_usr = 0; armcpu->R13_svc = armcpu->R14_svc = 0; armcpu->R13_abt = armcpu->R14_abt = 0; armcpu->R13_und = armcpu->R14_und = 0; armcpu->R13_irq = armcpu->R14_irq = 0; armcpu->R8_fiq = armcpu->R9_fiq = armcpu->R10_fiq = armcpu->R11_fiq = armcpu->R12_fiq = armcpu->R13_fiq = armcpu->R14_fiq = 0; armcpu->SPSR_svc.val = armcpu->SPSR_abt.val = armcpu->SPSR_und.val = armcpu->SPSR_irq.val = armcpu->SPSR_fiq.val = 0; armcpu->next_instruction = adr; armcpu_prefetch(armcpu); } uint32_t armcpu_switchMode(armcpu_t *armcpu, uint8_t mode) { uint32_t oldmode = armcpu->CPSR.bits.mode; switch (oldmode) { case USR: case SYS: armcpu->R13_usr = armcpu->R[13]; armcpu->R14_usr = armcpu->R[14]; break; case FIQ: std::swap(armcpu->R[8], armcpu->R8_fiq); std::swap(armcpu->R[9], armcpu->R9_fiq); std::swap(armcpu->R[10], armcpu->R10_fiq); std::swap(armcpu->R[11], armcpu->R11_fiq); std::swap(armcpu->R[12], armcpu->R12_fiq); armcpu->R13_fiq = armcpu->R[13]; armcpu->R14_fiq = armcpu->R[14]; armcpu->SPSR_fiq = armcpu->SPSR; break; case IRQ: armcpu->R13_irq = armcpu->R[13]; armcpu->R14_irq = armcpu->R[14]; armcpu->SPSR_irq = armcpu->SPSR; break; case SVC: armcpu->R13_svc = armcpu->R[13]; armcpu->R14_svc = armcpu->R[14]; armcpu->SPSR_svc = armcpu->SPSR; break; case ABT: armcpu->R13_abt = armcpu->R[13]; armcpu->R14_abt = armcpu->R[14]; armcpu->SPSR_abt = armcpu->SPSR; break; case UND: armcpu->R13_und = armcpu->R[13]; armcpu->R14_und = armcpu->R[14]; armcpu->SPSR_und = armcpu->SPSR; break; default: printf("switchMode: WRONG mode %02X\n",mode); } switch (mode) { case USR: case SYS: armcpu->R[13] = armcpu->R13_usr; armcpu->R[14] = armcpu->R14_usr; //SPSR = CPSR; break; case FIQ: std::swap(armcpu->R[8], armcpu->R8_fiq); std::swap(armcpu->R[9], armcpu->R9_fiq); std::swap(armcpu->R[10], armcpu->R10_fiq); std::swap(armcpu->R[11], armcpu->R11_fiq); std::swap(armcpu->R[12], armcpu->R12_fiq); armcpu->R[13] = armcpu->R13_fiq; armcpu->R[14] = armcpu->R14_fiq; armcpu->SPSR = armcpu->SPSR_fiq; break; case IRQ: armcpu->R[13] = armcpu->R13_irq; armcpu->R[14] = armcpu->R14_irq; armcpu->SPSR = armcpu->SPSR_irq; break; case SVC: armcpu->R[13] = armcpu->R13_svc; armcpu->R[14] = armcpu->R14_svc; armcpu->SPSR = armcpu->SPSR_svc; break; case ABT: armcpu->R[13] = armcpu->R13_abt; armcpu->R[14] = armcpu->R14_abt; armcpu->SPSR = armcpu->SPSR_abt; break; case UND: armcpu->R[13] = armcpu->R13_und; armcpu->R[14] = armcpu->R14_und; armcpu->SPSR = armcpu->SPSR_und; break; default: break; } armcpu->CPSR.bits.mode = mode & 0x1F; armcpu->changeCPSR(); return oldmode; } uint32_t armcpu_Wait4IRQ(armcpu_t *cpu) { cpu->waitIRQ = true; cpu->halt_IE_and_IF = true; return 1; } template static inline uint32_t armcpu_prefetch() { armcpu_t *const armcpu = &ARMPROC; uint32_t curInstruction = armcpu->next_instruction; if (!armcpu->CPSR.bits.T) { curInstruction &= 0xFFFFFFFC; // please don't change this to 0x0FFFFFFC -- the NDS will happily run on 0xF******* addresses all day long // please note that we must setup R[15] before reading the instruction since there is a protection // which prevents PC > 0x3FFF from reading the bios region armcpu->instruct_adr = curInstruction; armcpu->next_instruction = curInstruction + 4; armcpu->R[15] = curInstruction + 8; armcpu->instruction = _MMU_read32(curInstruction); return MMU_codeFetchCycles(curInstruction); } curInstruction &= 0xFFFFFFFE; // please don't change this to 0x0FFFFFFE -- the NDS will happily run on 0xF******* addresses all day long // please note that we must setup R[15] before reading the instruction since there is a protection // which prevents PC > 0x3FFF from reading the bios region armcpu->instruct_adr = curInstruction; armcpu->next_instruction = curInstruction + 2; armcpu->R[15] = curInstruction + 4; armcpu->instruction = _MMU_read16(curInstruction); if (!PROCNUM) { // arm9 fetches 2 instructions at a time in thumb mode if (!(curInstruction == armcpu->instruct_adr + 2 && (curInstruction & 2))) return MMU_codeFetchCycles(curInstruction); else return 0; } return MMU_codeFetchCycles(curInstruction); } // TODO - merge with armcpu_irqException? // http://www.ethernut.de/en/documents/arm-exceptions.html // http://docs.google.com/viewer?a=v&q=cache:V4ht1YkxprMJ:www.cs.nctu.edu.tw/~wjtsai/EmbeddedSystemDesign/Ch3-1.pdf+arm+exception+handling&hl=en&gl=us&pid=bl&srcid=ADGEEShx9VTHbUhWdDOrTVRzLkcCsVfJiijncNDkkgkrlJkLa7D0LCpO8fQ_hhU3DTcgZh9rcZWWQq4TYhhCovJ625h41M0ZUX3WGasyzWQFxYzDCB-VS6bsUmpoJnRxAc-bdkD0qmsu&sig=AHIEtbR9VHvDOCRmZFQDUVwy53iJDjoSPQ void armcpu_exception(armcpu_t *cpu, uint32_t number) { Mode cpumode = USR; switch (number) { case EXCEPTION_RESET: cpumode = SVC; break; case EXCEPTION_UNDEFINED_INSTRUCTION: cpumode = UND; break; case EXCEPTION_SWI: cpumode = SVC; break; case EXCEPTION_PREFETCH_ABORT: cpumode = ABT; break; case EXCEPTION_DATA_ABORT: cpumode = ABT; break; case EXCEPTION_RESERVED_0x14: execute = false; break; case EXCEPTION_IRQ: cpumode = IRQ; break; case EXCEPTION_FAST_IRQ: cpumode = FIQ; } Status_Reg tmp = cpu->CPSR; armcpu_switchMode(cpu, cpumode); // enter new mode cpu->R[14] = cpu->next_instruction; cpu->SPSR = tmp; // save old CPSR as new SPSR cpu->CPSR.bits.T = 0; // handle as ARM32 code cpu->CPSR.bits.I = 1; cpu->changeCPSR(); cpu->R[15] = cpu->intVector + number; cpu->next_instruction = cpu->R[15]; printf("armcpu_exception!\n"); // HOW DOES THIS WORTK WITHOUT A PREFETCH, LIKE IRQ BELOW? // I REALLY WISH WE DIDNT PREFETCH BEFORE EXECUTING } bool armcpu_irqException(armcpu_t *armcpu) { Status_Reg tmp; tmp = armcpu->CPSR; armcpu_switchMode(armcpu, IRQ); armcpu->R[14] = armcpu->instruct_adr + 4; armcpu->SPSR = tmp; armcpu->CPSR.bits.T = 0; armcpu->CPSR.bits.I = 1; armcpu->next_instruction = armcpu->intVector + 0x18; armcpu->waitIRQ = 0; // must retain invariant of having next instruction to be executed prefetched // (yucky) armcpu_prefetch(armcpu); return true; } uint32_t TRAPUNDEF(armcpu_t *cpu) { if (!!cpu->intVector ^ (cpu->proc_ID == ARMCPU_ARM9)) { armcpu_exception(&NDS_ARM9, EXCEPTION_UNDEFINED_INSTRUCTION); return 4; } else { execute = false; return 4; } } template uint32_t armcpu_exec() { // Usually, fetching and executing are processed parallelly. // So this function stores the cycles of each process to // the variables below, and returns appropriate cycle count. uint32_t cFetch = 0; uint32_t cExecute = 0; // this assert is annoying. but sometimes it is handy. //assert(ARMPROC.instruct_adr!=0x00000000); //cFetch = armcpu_prefetch(&ARMPROC); //printf("%d: %08X\n",PROCNUM,ARMPROC.instruct_adr); if (!ARMPROC.CPSR.bits.T) { if ( CONDITION(ARMPROC.instruction) == 0x0E // fast path for unconditional instructions || (TEST_COND(CONDITION(ARMPROC.instruction), CODE(ARMPROC.instruction), ARMPROC.CPSR)) // handles any condition ) { #ifdef HAVE_LUA CallRegisteredLuaMemHook(ARMPROC.instruct_adr, 4, ARMPROC.instruction, LUAMEMHOOK_EXEC); // should report even if condition=false? #endif cExecute = arm_instructions_set[PROCNUM][INSTRUCTION_INDEX(ARMPROC.instruction)](ARMPROC.instruction); } else cExecute = 1; // If condition=false: 1S cycle cFetch = armcpu_prefetch(); return MMU_fetchExecuteCycles(cExecute, cFetch); } #ifdef HAVE_LUA CallRegisteredLuaMemHook(ARMPROC.instruct_adr, 2, ARMPROC.instruction, LUAMEMHOOK_EXEC); #endif cExecute = thumb_instructions_set[PROCNUM][ARMPROC.instruction>>6](ARMPROC.instruction); cFetch = armcpu_prefetch(); return MMU_fetchExecuteCycles(cExecute, cFetch); } // these templates needed to be instantiated manually template uint32_t armcpu_exec<0>(); template uint32_t armcpu_exec<1>(); #ifdef HAVE_JIT void arm_jit_sync() { NDS_ARM7.next_instruction = NDS_ARM7.instruct_adr; NDS_ARM9.next_instruction = NDS_ARM9.instruct_adr; armcpu_prefetch<0>(); armcpu_prefetch<1>(); } template uint32_t armcpu_exec() { if (jit) { ArmOpCompiled f = reinterpret_cast(JIT_COMPILED_FUNC(ARMPROC.instruct_adr, PROCNUM)); return f ? f() : arm_jit_compile(); } return armcpu_exec(); } template uint32_t armcpu_exec<0, false>(); template uint32_t armcpu_exec<0, true>(); template uint32_t armcpu_exec<1, false>(); template uint32_t armcpu_exec<1, true>(); #endif