| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #include "Common.h" | ||
| #include "CPUDetect.h" | ||
| #include "StringUtil.h" | ||
|
|
||
| const char procfile[] = "/proc/cpuinfo"; | ||
|
|
||
| char *GetCPUString() | ||
| { | ||
| const char marker[] = "Hardware\t: "; | ||
| char *cpu_string = 0; | ||
| // Count the number of processor lines in /proc/cpuinfo | ||
| char buf[1024]; | ||
| FILE *fp; | ||
|
|
||
| fp = fopen(procfile, "r"); | ||
| if (!fp) | ||
| return 0; | ||
|
|
||
| while (fgets(buf, sizeof(buf), fp)) | ||
| { | ||
| if (strncmp(buf, marker, sizeof(marker) - 1)) | ||
| continue; | ||
| cpu_string = buf + sizeof(marker) - 1; | ||
| cpu_string = strndup(cpu_string, strlen(cpu_string) - 1); // Strip the newline | ||
| break; | ||
| } | ||
| return cpu_string; | ||
| } | ||
| bool CheckCPUFeature(const char *feature) | ||
| { | ||
| const char marker[] = "Features\t: "; | ||
| char buf[1024]; | ||
| FILE *fp; | ||
|
|
||
| fp = fopen(procfile, "r"); | ||
| if (!fp) | ||
| return 0; | ||
|
|
||
| while (fgets(buf, sizeof(buf), fp)) | ||
| { | ||
| if (strncmp(buf, marker, sizeof(marker) - 1)) | ||
| continue; | ||
| char *featurestring = buf + sizeof(marker) - 1; | ||
| char *token = strtok(featurestring, " "); | ||
| while (token != NULL) | ||
| { | ||
| if (strstr(token, feature)) | ||
| return true; | ||
| token = strtok(NULL, " "); | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| int GetCoreCount() | ||
| { | ||
| const char marker[] = "processor\t: "; | ||
| int cores = 0; | ||
| char buf[1024]; | ||
| FILE *fp; | ||
|
|
||
| fp = fopen(procfile, "r"); | ||
| if (!fp) | ||
| return 0; | ||
|
|
||
| while (fgets(buf, sizeof(buf), fp)) | ||
| { | ||
| if (strncmp(buf, marker, sizeof(marker) - 1)) | ||
| continue; | ||
| ++cores; | ||
| } | ||
| return cores; | ||
| } | ||
|
|
||
| CPUInfo cpu_info; | ||
|
|
||
| CPUInfo::CPUInfo() { | ||
| Detect(); | ||
| } | ||
|
|
||
| // Detects the various cpu features | ||
| void CPUInfo::Detect() | ||
| { | ||
| // Set some defaults here | ||
| // When ARMv8 cpus come out, these need to be updated. | ||
| HTT = false; | ||
| OS64bit = false; | ||
| CPU64bit = false; | ||
| Mode64bit = false; | ||
| vendor = VENDOR_ARM; | ||
|
|
||
| // Get the information about the CPU | ||
| strncpy(cpu_string, GetCPUString(), sizeof(cpu_string)); | ||
| num_cores = GetCoreCount(); | ||
| bSwp = CheckCPUFeature("swp"); | ||
| bHalf = CheckCPUFeature("half"); | ||
| bThumb = CheckCPUFeature("thumb"); | ||
| bFastMult = CheckCPUFeature("fastmult"); | ||
| bVFP = CheckCPUFeature("vfp"); | ||
| bEDSP = CheckCPUFeature("edsp"); | ||
| bThumbEE = CheckCPUFeature("thumbee"); | ||
| bNEON = CheckCPUFeature("neon"); | ||
| bVFPv3 = CheckCPUFeature("vfpv3"); | ||
| bTLS = CheckCPUFeature("tls"); | ||
| bVFPv4 = CheckCPUFeature("vfpv4"); | ||
| bIDIVa = CheckCPUFeature("idiva"); | ||
| bIDIVt = CheckCPUFeature("idivt"); | ||
| // These two are ARMv8 specific. | ||
| bFP = CheckCPUFeature("fp"); | ||
| bASIMD = CheckCPUFeature("asimd"); | ||
|
|
||
|
|
||
| #if defined(__ARM_ARCH_7A__) | ||
| bArmV7 = true; | ||
| #else | ||
| bArmV7 = false; | ||
| #endif | ||
| } | ||
|
|
||
| // Turn the cpu info into a string we can show | ||
| std::string CPUInfo::Summarize() | ||
| { | ||
| std::string sum; | ||
| if (num_cores == 1) | ||
| sum = StringFromFormat("%s, %i core", cpu_string, num_cores); | ||
| else | ||
| sum = StringFromFormat("%s, %i cores", cpu_string, num_cores); | ||
|
|
||
| if (bSwp) sum += ", SWP"; | ||
| if (bHalf) sum += ", Half"; | ||
| if (bThumb) sum += ", Thumb"; | ||
| if (bFastMult) sum += ", FastMult"; | ||
| if (bVFP) sum += ", VFP"; | ||
| if (bEDSP) sum += ", EDSP"; | ||
| if (bThumbEE) sum += ", ThumbEE"; | ||
| if (bNEON) sum += ", NEON"; | ||
| if (bVFPv3) sum += ", VFPv3"; | ||
| if (bTLS) sum += ", TLS"; | ||
| if (bVFPv4) sum += ", VFPv4"; | ||
| if (bIDIVa) sum += ", IDIVa"; | ||
| if (bIDIVt) sum += ", IDIVt"; | ||
|
|
||
| return sum; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
| #ifndef FPU_ROUND_MODE_H_ | ||
| #define FPU_ROUND_MODE_H_ | ||
| #include "Common.h" | ||
|
|
||
| namespace FPURoundMode | ||
| { | ||
| enum RoundModes | ||
| { | ||
| ROUND_NEAR = 0, | ||
| ROUND_CHOP, | ||
| ROUND_UP, | ||
| ROUND_DOWN | ||
| }; | ||
| enum PrecisionModes { | ||
| PREC_24 = 0, | ||
| PREC_53, | ||
| PREC_64 | ||
| }; | ||
| void SetRoundMode(u32 mode); | ||
|
|
||
| void SetPrecisionMode(u32 mode); | ||
|
|
||
| void SetSIMDMode(u32 mode); | ||
|
|
||
| /* | ||
| There are two different flavors of float to int conversion: | ||
| _mm_cvtps_epi32() and _mm_cvttps_epi32(). The first rounds | ||
| according to the MXCSR rounding bits. The second one always | ||
| uses round towards zero. | ||
| */ | ||
| void SaveSIMDState(); | ||
| void LoadSIMDState(); | ||
| void LoadDefaultSIMDState(); | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #include "FPURoundMode.h" | ||
|
|
||
| // Generic, do nothing | ||
| namespace FPURoundMode | ||
| { | ||
| void SetRoundMode(u32 mode) | ||
| { | ||
| } | ||
| void SetPrecisionMode(u32 mode) | ||
| { | ||
| } | ||
| void SetSIMDMode(u32 mode) | ||
| { | ||
| } | ||
| void SaveSIMDState() | ||
| { | ||
| } | ||
| void LoadSIMDState() | ||
| { | ||
| } | ||
| void LoadDefaultSIMDState() | ||
| { | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,7 @@ | |
|
|
||
| #include "Common.h" | ||
| #include "x64Emitter.h" | ||
| #include "x64ABI.h" | ||
|
|
||
| using namespace Gen; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #include "Common.h" | ||
| #include "FPURoundMode.h" | ||
|
|
||
| #ifndef _WIN32 | ||
| static const unsigned short FPU_ROUND_NEAR = 0 << 10; | ||
| static const unsigned short FPU_ROUND_DOWN = 1 << 10; | ||
| static const unsigned short FPU_ROUND_UP = 2 << 10; | ||
| static const unsigned short FPU_ROUND_CHOP = 3 << 10; | ||
| static const unsigned short FPU_ROUND_MASK = 3 << 10; | ||
| #include <xmmintrin.h> | ||
| #endif | ||
|
|
||
| const u32 MASKS = 0x1F80; // mask away the interrupts. | ||
| const u32 DAZ = 0x40; | ||
| const u32 FTZ = 0x8000; | ||
|
|
||
| namespace FPURoundMode | ||
| { | ||
| // Get the default SSE states here. | ||
| static u32 saved_sse_state = _mm_getcsr(); | ||
| static const u32 default_sse_state = _mm_getcsr(); | ||
|
|
||
| void SetRoundMode(u32 mode) | ||
| { | ||
| // Set FPU rounding mode to mimic the PowerPC's | ||
| #ifdef _M_IX86 | ||
| // This shouldn't really be needed anymore since we use SSE | ||
| #ifdef _WIN32 | ||
| const int table[4] = | ||
| { | ||
| _RC_NEAR, | ||
| _RC_CHOP, | ||
| _RC_UP, | ||
| _RC_DOWN | ||
| }; | ||
| _set_controlfp(_MCW_RC, table[mode]); | ||
| #else | ||
| const unsigned short table[4] = | ||
| { | ||
| FPU_ROUND_NEAR, | ||
| FPU_ROUND_CHOP, | ||
| FPU_ROUND_UP, | ||
| FPU_ROUND_DOWN | ||
| }; | ||
| unsigned short _mode; | ||
| asm ("fstcw %0" : "=m" (_mode) : ); | ||
| _mode = (_mode & ~FPU_ROUND_MASK) | table[mode]; | ||
| asm ("fldcw %0" : : "m" (_mode)); | ||
| #endif | ||
| #endif | ||
| } | ||
|
|
||
| void SetPrecisionMode(u32 mode) | ||
| { | ||
| #ifdef _M_IX86 | ||
| // sets the floating-point lib to 53-bit | ||
| // PowerPC has a 53bit floating pipeline only | ||
| // eg: sscanf is very sensitive | ||
| #ifdef _WIN32 | ||
| _control87(_PC_53, MCW_PC); | ||
| #else | ||
| const unsigned short table[4] = { | ||
| 0 << 8, // FPU_PREC_24 | ||
| 2 << 8, // FPU_PREC_53 | ||
| 3 << 8, // FPU_PREC_64 | ||
| 3 << 8, // FPU_PREC_MASK | ||
| }; | ||
| unsigned short _mode; | ||
| asm ("fstcw %0" : : "m" (_mode)); | ||
| _mode = (_mode & ~table[4]) | table[mode]; | ||
| asm ("fldcw %0" : : "m" (_mode)); | ||
| #endif | ||
| #else | ||
| //x64 doesn't need this - fpu is done with SSE | ||
| //but still - set any useful sse options here | ||
| #endif | ||
| } | ||
| void SetSIMDMode(u32 mode) | ||
| { | ||
| static const u32 ssetable[4] = | ||
| { | ||
| (0 << 13) | MASKS, | ||
| (3 << 13) | MASKS, | ||
| (2 << 13) | MASKS, | ||
| (1 << 13) | MASKS, | ||
| }; | ||
| u32 csr = ssetable[mode]; | ||
| _mm_setcsr(csr); | ||
| } | ||
|
|
||
| void SaveSIMDState() | ||
| { | ||
| saved_sse_state = _mm_getcsr(); | ||
| } | ||
| void LoadSIMDState() | ||
| { | ||
| _mm_setcsr(saved_sse_state); | ||
| } | ||
| void LoadDefaultSIMDState() | ||
| { | ||
| _mm_setcsr(default_sse_state); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
|
|
||
| #include <stdio.h> | ||
| #include <signal.h> | ||
| #ifdef ANDROID | ||
| #include <asm/sigcontext.h> | ||
| #else | ||
| #include <sys/ucontext.h> // Look in here for the context definition. | ||
| #include <execinfo.h> | ||
| #endif | ||
|
|
||
| #include "Common.h" | ||
| #include "MemTools.h" | ||
| #include "HW/Memmap.h" | ||
| #include "PowerPC/PowerPC.h" | ||
| #include "PowerPC/JitInterface.h" | ||
| #include "PowerPC/JitCommon/JitBase.h" | ||
|
|
||
| namespace EMM | ||
| { | ||
| #ifdef ANDROID | ||
| typedef struct sigcontext mcontext_t; | ||
| typedef struct ucontext { | ||
| uint32_t uc_flags; | ||
| struct ucontext* uc_link; | ||
| stack_t uc_stack; | ||
| mcontext_t uc_mcontext; | ||
| // Other fields are not used by Google Breakpad. Don't define them. | ||
| } ucontext_t; | ||
| #endif | ||
|
|
||
| void sigsegv_handler(int signal, siginfo_t *info, void *raw_context) | ||
| { | ||
| if (signal != SIGSEGV) | ||
| { | ||
| // We are not interested in other signals - handle it as usual. | ||
| return; | ||
| } | ||
| ucontext_t *context = (ucontext_t *)raw_context; | ||
| int sicode = info->si_code; | ||
| if (sicode != SEGV_MAPERR && sicode != SEGV_ACCERR) | ||
| { | ||
| // Huh? Return. | ||
| return; | ||
| } | ||
|
|
||
|
|
||
| // Get all the information we can out of the context. | ||
| mcontext_t *ctx = &context->uc_mcontext; | ||
|
|
||
| void *fault_memory_ptr = (void*)ctx->arm_r10; | ||
| u8 *fault_instruction_ptr = (u8 *)ctx->arm_pc; | ||
|
|
||
| if (!JitInterface::IsInCodeSpace(fault_instruction_ptr)) { | ||
| // Let's not prevent debugging. | ||
| return; | ||
| } | ||
|
|
||
| u64 bad_address = (u64)fault_memory_ptr; | ||
| u64 memspace_bottom = (u64)Memory::base; | ||
| if (bad_address < memspace_bottom) { | ||
| PanicAlertT("Exception handler - access below memory space. %08llx%08llx", | ||
| bad_address >> 32, bad_address); | ||
| } | ||
|
|
||
| u32 em_address = (u32)(bad_address - memspace_bottom); | ||
|
|
||
| int access_type = 0; | ||
|
|
||
| CONTEXT fake_ctx; | ||
| fake_ctx.reg_pc = ctx->arm_pc; | ||
| const u8 *new_rip = jit->BackPatch(fault_instruction_ptr, access_type, em_address, &fake_ctx); | ||
| if (new_rip) { | ||
| ctx->arm_pc = fake_ctx.reg_pc; | ||
| } | ||
| } | ||
|
|
||
| void InstallExceptionHandler() | ||
| { | ||
| struct sigaction sa; | ||
| sa.sa_handler = 0; | ||
| sa.sa_sigaction = &sigsegv_handler; | ||
| sa.sa_flags = SA_SIGINFO; | ||
| sigemptyset(&sa.sa_mask); | ||
| sigaction(SIGSEGV, &sa, NULL); | ||
| } | ||
| } // namespace |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,7 +57,7 @@ | |
| #define DISABLE64 | ||
| #endif | ||
|
|
||
| class JitIL : public Jitx86Base | ||
| { | ||
| private: | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| // ======================== | ||
| // See comments in Jit.cpp. | ||
| // ======================== | ||
|
|
||
| // Mystery: Capcom vs SNK 800aa278 | ||
|
|
||
| // CR flags approach: | ||
| // * Store that "N+Z flag contains CR0" or "S+Z flag contains CR3". | ||
| // * All flag altering instructions flush this | ||
| // * A flush simply does a conditional write to the appropriate CRx. | ||
| // * If flag available, branch code can become absolutely trivial. | ||
|
|
||
| // Settings | ||
| // ---------- | ||
| #ifndef _JITARM_H | ||
| #define _JITARM_H | ||
| #include "../CPUCoreBase.h" | ||
| #include "../PPCAnalyst.h" | ||
| #include "JitArmCache.h" | ||
| #include "JitRegCache.h" | ||
| #include "JitFPRCache.h" | ||
| #include "JitAsm.h" | ||
| #include "../JitCommon/JitBase.h" | ||
|
|
||
| // Use these to control the instruction selection | ||
| // #define INSTRUCTION_START Default(inst); return; | ||
| // #define INSTRUCTION_START PPCTables::CountInstruction(inst); | ||
| #define INSTRUCTION_START | ||
| #define JITDISABLE(type) \ | ||
| if (Core::g_CoreStartupParameter.bJITOff || \ | ||
| Core::g_CoreStartupParameter.bJIT##type##Off) \ | ||
| {Default(inst); return;} | ||
|
|
||
| class JitArm : public JitBase, public ArmGen::ARMXCodeBlock | ||
| { | ||
| private: | ||
| JitArmBlockCache blocks; | ||
|
|
||
| JitArmAsmRoutineManager asm_routines; | ||
|
|
||
| // TODO: Make arm specific versions of these, shouldn't be too hard to | ||
| // make it so we allocate some space at the start(?) of code generation | ||
| // and keep the registers in a cache. Will burn this bridge when we get to | ||
| // it. | ||
| ArmRegCache gpr; | ||
| ArmFPRCache fpr; | ||
|
|
||
| PPCAnalyst::CodeBuffer code_buffer; | ||
| void DoDownCount(); | ||
|
|
||
| void PrintDebug(UGeckoInstruction inst, u32 level); | ||
|
|
||
| void Helper_UpdateCR1(ARMReg value); | ||
| public: | ||
| JitArm() : code_buffer(32000) {} | ||
| ~JitArm() {} | ||
|
|
||
| void Init(); | ||
| void Shutdown(); | ||
|
|
||
| // Jit! | ||
|
|
||
| void Jit(u32 em_address); | ||
| const u8* DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b); | ||
|
|
||
| JitBaseBlockCache *GetBlockCache() { return &blocks; } | ||
|
|
||
| const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx); | ||
|
|
||
| bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); } | ||
|
|
||
| void Trace(); | ||
|
|
||
| void ClearCache(); | ||
|
|
||
| const u8 *GetDispatcher() { | ||
| return asm_routines.dispatcher; | ||
| } | ||
| CommonAsmRoutinesBase *GetAsmRoutines() { | ||
| return &asm_routines; | ||
| } | ||
|
|
||
| const char *GetName() { | ||
| return "JITARM"; | ||
| } | ||
| // Run! | ||
|
|
||
| void Run(); | ||
| void SingleStep(); | ||
|
|
||
| // Utilities for use by opcodes | ||
|
|
||
| void WriteExit(u32 destination, int exit_num); | ||
| void WriteExitDestInR(ARMReg Reg); | ||
| void WriteRfiExitDestInR(ARMReg Reg); | ||
| void WriteExceptionExit(); | ||
| void WriteCallInterpreter(UGeckoInstruction _inst); | ||
| void Cleanup(); | ||
|
|
||
| void GenerateRC(int cr = 0); | ||
| void ComputeRC(int cr = 0); | ||
|
|
||
| // TODO: This shouldn't be here | ||
| void StoreFromReg(ARMReg dest, ARMReg value, int accessSize, s32 offset); | ||
| void LoadToReg(ARMReg dest, ARMReg addr, int accessSize, s32 offset); | ||
|
|
||
| // OPCODES | ||
| void unknown_instruction(UGeckoInstruction _inst); | ||
| void Default(UGeckoInstruction _inst); | ||
| void DoNothing(UGeckoInstruction _inst); | ||
| void HLEFunction(UGeckoInstruction _inst); | ||
|
|
||
| void DynaRunTable4(UGeckoInstruction _inst); | ||
| void DynaRunTable19(UGeckoInstruction _inst); | ||
| void DynaRunTable31(UGeckoInstruction _inst); | ||
| void DynaRunTable59(UGeckoInstruction _inst); | ||
| void DynaRunTable63(UGeckoInstruction _inst); | ||
|
|
||
| // Breakin shit | ||
| void Break(UGeckoInstruction _inst); | ||
| // Branch | ||
| void bx(UGeckoInstruction _inst); | ||
| void bcx(UGeckoInstruction _inst); | ||
| void bclrx(UGeckoInstruction _inst); | ||
| void sc(UGeckoInstruction _inst); | ||
| void rfi(UGeckoInstruction _inst); | ||
| void bcctrx(UGeckoInstruction _inst); | ||
|
|
||
| // Integer | ||
| void addi(UGeckoInstruction _inst); | ||
| void addis(UGeckoInstruction _inst); | ||
| void addx(UGeckoInstruction _inst); | ||
| void cmp (UGeckoInstruction _inst); | ||
| void cmpi(UGeckoInstruction _inst); | ||
| void cmpli(UGeckoInstruction _inst); | ||
| void negx(UGeckoInstruction _inst); | ||
| void mulli(UGeckoInstruction _inst); | ||
| void ori(UGeckoInstruction _inst); | ||
| void oris(UGeckoInstruction _inst); | ||
| void orx(UGeckoInstruction _inst); | ||
| void rlwimix(UGeckoInstruction _inst); | ||
| void rlwinmx(UGeckoInstruction _inst); | ||
| void extshx(UGeckoInstruction inst); | ||
| void extsbx(UGeckoInstruction inst); | ||
|
|
||
| // System Registers | ||
| void mtmsr(UGeckoInstruction _inst); | ||
| void mtspr(UGeckoInstruction _inst); | ||
| void mfspr(UGeckoInstruction _inst); | ||
|
|
||
| // LoadStore | ||
| void icbi(UGeckoInstruction _inst); | ||
| void lbz(UGeckoInstruction _inst); | ||
| void lhz(UGeckoInstruction _inst); | ||
| void lwz(UGeckoInstruction _inst); | ||
| void lwzx(UGeckoInstruction _inst); | ||
| void stw(UGeckoInstruction _inst); | ||
| void stwu(UGeckoInstruction _inst); | ||
|
|
||
| // Floating point | ||
| void fabsx(UGeckoInstruction _inst); | ||
| void faddx(UGeckoInstruction _inst); | ||
| void fmrx(UGeckoInstruction _inst); | ||
|
|
||
| // Floating point loadStore | ||
| void lfs(UGeckoInstruction _inst); | ||
| }; | ||
|
|
||
| #endif // _JIT64_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| // Enable define below to enable oprofile integration. For this to work, | ||
| // it requires at least oprofile version 0.9.4, and changing the build | ||
| // system to link the Dolphin executable against libopagent. Since the | ||
| // dependency is a little inconvenient and this is possibly a slight | ||
| // performance hit, it's not enabled by default, but it's useful for | ||
| // locating performance issues. | ||
|
|
||
| #include "../JitInterface.h" | ||
| #include "JitArmCache.h" | ||
|
|
||
|
|
||
| using namespace ArmGen; | ||
|
|
||
| void JitArmBlockCache::WriteLinkBlock(u8* location, const u8* address) | ||
| { | ||
| ARMXEmitter emit(location); | ||
| emit.B(address); | ||
| } | ||
| void JitArmBlockCache::WriteDestroyBlock(const u8* location, u32 address) | ||
| { | ||
| ARMXEmitter emit((u8 *)location); | ||
| emit.MOVI2R(R10, (u32)&PC); | ||
| emit.MOVI2R(R11, address); | ||
| emit.MOVI2R(R12, (u32)jit->GetAsmRoutines()->dispatcher); | ||
| emit.STR(R10, R11); | ||
| emit.B(R12); | ||
| } | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #ifndef _JITARMCACHE_H | ||
| #define _JITARMCACHE_H | ||
|
|
||
| #include "../JitCommon/JitCache.h" | ||
|
|
||
|
|
||
| typedef void (*CompiledCode)(); | ||
|
|
||
| class JitArmBlockCache : public JitBaseBlockCache | ||
| { | ||
| private: | ||
| void WriteLinkBlock(u8* location, const u8* address); | ||
| void WriteDestroyBlock(const u8* location, u32 address); | ||
| }; | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #include <string> | ||
|
|
||
| #include "Common.h" | ||
|
|
||
| #include "../../HW/Memmap.h" | ||
| #include "Jit.h" | ||
| #include "../JitCommon/JitBackpatch.h" | ||
| #include "StringUtil.h" | ||
|
|
||
| #ifdef _M_X64 | ||
| static void BackPatchError(const std::string &text, u8 *codePtr, u32 emAddress) { | ||
| u64 code_addr = (u64)codePtr; | ||
| disassembler disasm; | ||
| char disbuf[256]; | ||
| memset(disbuf, 0, 256); | ||
| #ifdef _M_IX86 | ||
| disasm.disasm32(0, code_addr, codePtr, disbuf); | ||
| #else | ||
| disasm.disasm64(0, code_addr, codePtr, disbuf); | ||
| #endif | ||
| PanicAlert("%s\n\n" | ||
| "Error encountered accessing emulated address %08x.\n" | ||
| "Culprit instruction: \n%s\nat %#llx", | ||
| text.c_str(), emAddress, disbuf, code_addr); | ||
| return; | ||
| } | ||
| #endif | ||
|
|
||
| // This generates some fairly heavy trampolines, but: | ||
| // 1) It's really necessary. We don't know anything about the context. | ||
| // 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be | ||
| // that many of them in a typical program/game. | ||
| bool DisamLoadStore(const u32 inst, ARMReg &rD, u8 &accessSize, bool &Store) | ||
| { | ||
| u8 op = (inst >> 20) & 0xFF; | ||
| printf("op: 0x%08x\n", op); | ||
| switch (op) | ||
| { | ||
| case 0x58: // STR | ||
| { | ||
| rD = (ARMReg)((inst >> 16) & 0xF); | ||
| Store = true; | ||
| accessSize = 32; | ||
| } | ||
| break; | ||
| case 0x59: // LDR | ||
| { | ||
| rD = (ARMReg)((inst >> 16) & 0xF); | ||
| Store = false; | ||
| accessSize = 32; | ||
| } | ||
| break; | ||
| case 0x05: // LDRH | ||
| { | ||
| rD = (ARMReg)((inst >> 16) & 0xF); | ||
| Store = false; | ||
| accessSize = 16; | ||
| } | ||
| break; | ||
| case 0x45 + 0x18: // LDRB | ||
| { | ||
| rD = (ARMReg)((inst >> 16) & 0xF); | ||
| Store = false; | ||
| accessSize = 8; | ||
| } | ||
| break; | ||
| case 0x44 + 0x18: // STRB | ||
| default: | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| const u8 *JitArm::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void) | ||
| { | ||
| // TODO: This ctx needs to be filled with our information | ||
| CONTEXT *ctx = (CONTEXT *)ctx_void; | ||
|
|
||
| // We need to get the destination register before we start | ||
| u32 Value = *(u32*)codePtr; | ||
| ARMReg rD; | ||
| u8 accessSize; | ||
| bool Store; | ||
|
|
||
| if (!DisamLoadStore(Value, rD, accessSize, Store)) | ||
| { | ||
| printf("Invalid backpatch at location 0x%08x(0x%08x)\n", ctx->reg_pc, Value); | ||
| exit(0); | ||
| } | ||
|
|
||
| if (Store) | ||
| { | ||
| const u32 ARMREGOFFSET = 4 * 7; | ||
| ARMXEmitter emitter(codePtr - ARMREGOFFSET); | ||
| switch (accessSize) | ||
| { | ||
| case 8: // 8bit | ||
| //emitter.MOVI2R(R14, (u32)&Memory::Write_U8, false); // 1-2 | ||
| return 0; | ||
| break; | ||
| case 16: // 16bit | ||
| //emitter.MOVI2R(R14, (u32)&Memory::Write_U16, false); // 1-2 | ||
| return 0; | ||
| break; | ||
| case 32: // 32bit | ||
| emitter.MOVI2R(R14, (u32)&Memory::Write_U32, false); // 1-2 | ||
| break; | ||
| } | ||
| emitter.PUSH(4, R0, R1, R2, R3); // 3 | ||
| emitter.MOV(R0, rD); // Value - 4 | ||
| emitter.MOV(R1, R10); // Addr- 5 | ||
| emitter.BL(R14); // 6 | ||
| emitter.POP(4, R0, R1, R2, R3); // 7 | ||
| emitter.NOP(1); // 8 | ||
| u32 newPC = ctx->reg_pc - (ARMREGOFFSET + 4 * 4); | ||
| ctx->reg_pc = newPC; | ||
| emitter.FlushIcache(); | ||
| return codePtr; | ||
| } | ||
| else | ||
| { | ||
| const u32 ARMREGOFFSET = 4 * 6; | ||
| ARMXEmitter emitter(codePtr - ARMREGOFFSET); | ||
| switch (accessSize) | ||
| { | ||
| case 8: // 8bit | ||
| emitter.MOVI2R(R14, (u32)&Memory::Read_U8, false); // 2 | ||
| break; | ||
| case 16: // 16bit | ||
| emitter.MOVI2R(R14, (u32)&Memory::Read_U16, false); // 2 | ||
| break; | ||
| case 32: // 32bit | ||
| emitter.MOVI2R(R14, (u32)&Memory::Read_U32, false); // 2 | ||
| break; | ||
| } | ||
| emitter.PUSH(4, R0, R1, R2, R3); // 3 | ||
| emitter.MOV(R0, R10); // 4 | ||
| emitter.BL(R14); // 5 | ||
| emitter.MOV(R14, R0); // 6 | ||
| emitter.POP(4, R0, R1, R2, R3); // 7 | ||
| emitter.MOV(rD, R14); // 8 | ||
| ctx->reg_pc -= ARMREGOFFSET + (4 * 4); | ||
| emitter.FlushIcache(); | ||
| return codePtr; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,347 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
| #include "Common.h" | ||
| #include "Thunk.h" | ||
|
|
||
| #include "../../Core.h" | ||
| #include "../PowerPC.h" | ||
| #include "../../CoreTiming.h" | ||
| #include "../PPCTables.h" | ||
| #include "ArmEmitter.h" | ||
|
|
||
| #include "Jit.h" | ||
| #include "JitRegCache.h" | ||
| #include "JitAsm.h" | ||
|
|
||
| // The branches are known good, or at least reasonably good. | ||
| // No need for a disable-mechanism. | ||
|
|
||
| // If defined, clears CR0 at blr and bl-s. If the assumption that | ||
| // flags never carry over between functions holds, then the task for | ||
| // an optimizer becomes much easier. | ||
|
|
||
| // #define ACID_TEST | ||
|
|
||
| // Zelda and many more games seem to pass the Acid Test. | ||
|
|
||
|
|
||
| using namespace ArmGen; | ||
| void JitArm::sc(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Branch) | ||
|
|
||
| gpr.Flush(); | ||
| fpr.Flush(); | ||
|
|
||
| ARMABI_MOVI2M((u32)&PC, js.compilerPC + 4); // Destroys R12 and R14 | ||
| ARMReg rA = gpr.GetReg(); | ||
| LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions)); | ||
| ORR(rA, rA, EXCEPTION_SYSCALL); | ||
| STR(R9, rA, STRUCT_OFF(PowerPC::ppcState, Exceptions)); | ||
| gpr.Unlock(rA); | ||
|
|
||
| WriteExceptionExit(); | ||
| } | ||
|
|
||
| void JitArm::rfi(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Branch) | ||
|
|
||
| gpr.Flush(); | ||
| fpr.Flush(); | ||
|
|
||
| // See Interpreter rfi for details | ||
| const u32 mask = 0x87C0FFFF; | ||
| const u32 clearMSR13 = 0xFFFBFFFF; // Mask used to clear the bit MSR[13] | ||
| // MSR = ((MSR & ~mask) | (SRR1 & mask)) & clearMSR13; | ||
| // R0 = MSR location | ||
| // R1 = MSR contents | ||
| // R2 = Mask | ||
| // R3 = Mask | ||
| ARMReg rA = gpr.GetReg(); | ||
| ARMReg rB = gpr.GetReg(); | ||
| ARMReg rC = gpr.GetReg(); | ||
| ARMReg rD = gpr.GetReg(); | ||
| MOVI2R(rA, (u32)&MSR); | ||
| MOVI2R(rB, (~mask) & clearMSR13); | ||
| MOVI2R(rC, mask & clearMSR13); | ||
|
|
||
| LDR(rD, rA); | ||
|
|
||
| AND(rD, rD, rB); // rD = Masked MSR | ||
| STR(rA, rD); | ||
|
|
||
| MOVI2R(rB, (u32)&SRR1); | ||
| LDR(rB, rB); // rB contains SRR1 here | ||
|
|
||
| AND(rB, rB, rC); // rB contains masked SRR1 here | ||
| ORR(rB, rD, rB); // rB = Masked MSR OR masked SRR1 | ||
|
|
||
| STR(rA, rB); // STR rB in to rA | ||
|
|
||
| MOVI2R(rA, (u32)&SRR0); | ||
| LDR(rA, rA); | ||
|
|
||
| gpr.Unlock(rB, rC, rD); | ||
| WriteRfiExitDestInR(rA); // rA gets unlocked here | ||
| //AND(32, M(&MSR), Imm32((~mask) & clearMSR13)); | ||
| //MOV(32, R(EAX), M(&SRR1)); | ||
| //AND(32, R(EAX), Imm32(mask & clearMSR13)); | ||
| //OR(32, M(&MSR), R(EAX)); | ||
| // NPC = SRR0; | ||
| //MOV(32, R(EAX), M(&SRR0)); | ||
| //WriteRfiExitDestInEAX(); | ||
| } | ||
|
|
||
| void JitArm::bx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Branch) | ||
| // We must always process the following sentence | ||
| // even if the blocks are merged by PPCAnalyst::Flatten(). | ||
| if (inst.LK) | ||
| ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); | ||
|
|
||
| // If this is not the last instruction of a block, | ||
| // we will skip the rest process. | ||
| // Because PPCAnalyst::Flatten() merged the blocks. | ||
| if (!js.isLastInstruction) { | ||
| return; | ||
| } | ||
|
|
||
| gpr.Flush(); | ||
| fpr.Flush(); | ||
|
|
||
| u32 destination; | ||
| if (inst.AA) | ||
| destination = SignExt26(inst.LI << 2); | ||
| else | ||
| destination = js.compilerPC + SignExt26(inst.LI << 2); | ||
| #ifdef ACID_TEST | ||
| // TODO: Not implemented yet. | ||
| //if (inst.LK) | ||
| //AND(32, M(&PowerPC::ppcState.cr), Imm32(~(0xFF000000))); | ||
| #endif | ||
| if (destination == js.compilerPC) | ||
| { | ||
| //PanicAlert("Idle loop detected at %08x", destination); | ||
| // CALL(ProtectFunction(&CoreTiming::Idle, 0)); | ||
| // JMP(Asm::testExceptions, true); | ||
| // make idle loops go faster | ||
| js.downcountAmount += 8; | ||
| } | ||
| WriteExit(destination, 0); | ||
| } | ||
|
|
||
| void JitArm::bcx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Branch) | ||
| // USES_CR | ||
| _assert_msg_(DYNA_REC, js.isLastInstruction, "bcx not last instruction of block"); | ||
|
|
||
| gpr.Flush(); | ||
| fpr.Flush(); | ||
|
|
||
| ARMReg rA = gpr.GetReg(); | ||
| ARMReg rB = gpr.GetReg(); | ||
| FixupBranch pCTRDontBranch; | ||
| if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR | ||
| { | ||
| MOVI2R(rA, (u32)&CTR); | ||
| LDR(rB, rA); | ||
| SUBS(rB, rB, 1); | ||
| STR(rA, rB); | ||
|
|
||
| //SUB(32, M(&CTR), Imm8(1)); | ||
| if (inst.BO & BO_BRANCH_IF_CTR_0) | ||
| pCTRDontBranch = B_CC(CC_NEQ); | ||
| else | ||
| pCTRDontBranch = B_CC(CC_EQ); | ||
| } | ||
|
|
||
| FixupBranch pConditionDontBranch; | ||
| if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit | ||
| { | ||
| LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2)); | ||
| TST(rA, 8 >> (inst.BI & 3)); | ||
|
|
||
| //TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3))); | ||
| if (inst.BO & BO_BRANCH_IF_TRUE) // Conditional branch | ||
| pConditionDontBranch = B_CC(CC_EQ); // Zero | ||
| else | ||
| pConditionDontBranch = B_CC(CC_NEQ); // Not Zero | ||
| } | ||
| gpr.Unlock(rA, rB); | ||
| if (inst.LK) | ||
| ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); // Careful, destroys R14, R12 | ||
|
|
||
| u32 destination; | ||
| if(inst.AA) | ||
| destination = SignExt16(inst.BD << 2); | ||
| else | ||
| destination = js.compilerPC + SignExt16(inst.BD << 2); | ||
| WriteExit(destination, 0); | ||
|
|
||
| if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) | ||
| SetJumpTarget( pConditionDontBranch ); | ||
| if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) | ||
| SetJumpTarget( pCTRDontBranch ); | ||
|
|
||
| WriteExit(js.compilerPC + 4, 1); | ||
| } | ||
| void JitArm::bcctrx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Branch) | ||
|
|
||
| gpr.Flush(); | ||
| fpr.Flush(); | ||
|
|
||
| // bcctrx doesn't decrement and/or test CTR | ||
| _dbg_assert_msg_(POWERPC, inst.BO_2 & BO_DONT_DECREMENT_FLAG, "bcctrx with decrement and test CTR option is invalid!"); | ||
|
|
||
| if (inst.BO_2 & BO_DONT_CHECK_CONDITION) | ||
| { | ||
| // BO_2 == 1z1zz -> b always | ||
|
|
||
| //NPC = CTR & 0xfffffffc; | ||
| if(inst.LK_3) | ||
| ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); | ||
| ARMReg rA = gpr.GetReg(); | ||
| ARMReg rB = gpr.GetReg(); | ||
| MOVI2R(rA, (u32)&CTR); | ||
| MVN(rB, 0x3); // 0xFFFFFFFC | ||
| LDR(rA, rA); | ||
| AND(rA, rA, rB); | ||
| gpr.Unlock(rB); | ||
| WriteExitDestInR(rA); | ||
| } | ||
| else | ||
| { | ||
| // Rare condition seen in (just some versions of?) Nintendo's NES Emulator | ||
|
|
||
| // BO_2 == 001zy -> b if false | ||
| // BO_2 == 011zy -> b if true | ||
| ARMReg rA = gpr.GetReg(); | ||
| ARMReg rB = gpr.GetReg(); | ||
|
|
||
| LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2)); | ||
| TST(rA, 8 >> (inst.BI & 3)); | ||
| CCFlags branch; | ||
| if (inst.BO_2 & BO_BRANCH_IF_TRUE) | ||
| branch = CC_EQ; | ||
| else | ||
| branch = CC_NEQ; | ||
| FixupBranch b = B_CC(branch); | ||
|
|
||
| MOVI2R(rA, (u32)&CTR); | ||
| LDR(rA, rA); | ||
| MVN(rB, 0x3); // 0xFFFFFFFC | ||
| AND(rA, rA, rB); | ||
|
|
||
| if (inst.LK_3){ | ||
| ARMReg rC = gpr.GetReg(false); | ||
| u32 Jumpto = js.compilerPC + 4; | ||
| MOVI2R(rB, (u32)&LR); | ||
| MOVI2R(rC, Jumpto); | ||
| STR(rB, rC); | ||
| //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); | ||
| } | ||
| gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR | ||
| WriteExitDestInR(rA); | ||
|
|
||
| SetJumpTarget(b); | ||
| WriteExit(js.compilerPC + 4, 1); | ||
| } | ||
| } | ||
| void JitArm::bclrx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Branch) | ||
| if (!js.isLastInstruction && | ||
| (inst.BO & (1 << 4)) && (inst.BO & (1 << 2))) { | ||
| if (inst.LK) | ||
| { | ||
| ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); | ||
| } | ||
| return; | ||
| } | ||
| gpr.Flush(); | ||
| fpr.Flush(); | ||
|
|
||
| ARMReg rA = gpr.GetReg(); | ||
| ARMReg rB = gpr.GetReg(); | ||
| FixupBranch pCTRDontBranch; | ||
| if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR | ||
| { | ||
| MOVI2R(rA, (u32)&CTR); | ||
| LDR(rB, rA); | ||
| SUBS(rB, rB, 1); | ||
| STR(rA, rB); | ||
|
|
||
| //SUB(32, M(&CTR), Imm8(1)); | ||
| if (inst.BO & BO_BRANCH_IF_CTR_0) | ||
| pCTRDontBranch = B_CC(CC_NEQ); | ||
| else | ||
| pCTRDontBranch = B_CC(CC_EQ); | ||
| } | ||
|
|
||
| FixupBranch pConditionDontBranch; | ||
| if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit | ||
| { | ||
| LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2)); | ||
| TST(rA, 8 >> (inst.BI & 3)); | ||
| //TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3))); | ||
| if (inst.BO & BO_BRANCH_IF_TRUE) // Conditional branch | ||
| pConditionDontBranch = B_CC(CC_EQ); // Zero | ||
| else | ||
| pConditionDontBranch = B_CC(CC_NEQ); // Not Zero | ||
| } | ||
|
|
||
| // This below line can be used to prove that blr "eats flags" in practice. | ||
| // This observation will let us do a lot of fun observations. | ||
| #ifdef ACID_TEST | ||
| // TODO: Not yet implemented | ||
| // AND(32, M(&PowerPC::ppcState.cr), Imm32(~(0xFF000000))); | ||
| #endif | ||
|
|
||
| //MOV(32, R(EAX), M(&LR)); | ||
| //AND(32, R(EAX), Imm32(0xFFFFFFFC)); | ||
| MOVI2R(rA, (u32)&LR); | ||
| MVN(rB, 0x3); // 0xFFFFFFFC | ||
| LDR(rA, rA); | ||
| AND(rA, rA, rB); | ||
| if (inst.LK){ | ||
| ARMReg rC = gpr.GetReg(false); | ||
| u32 Jumpto = js.compilerPC + 4; | ||
| MOVI2R(rB, (u32)&LR); | ||
| MOVI2R(rC, Jumpto); | ||
| STR(rB, rC); | ||
| //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); | ||
| } | ||
| gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR | ||
| WriteExitDestInR(rA); | ||
|
|
||
| if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) | ||
| SetJumpTarget( pConditionDontBranch ); | ||
| if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) | ||
| SetJumpTarget( pCTRDontBranch ); | ||
| WriteExit(js.compilerPC + 4, 1); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #include "Common.h" | ||
| #include "Thunk.h" | ||
|
|
||
| #include "../../Core.h" | ||
| #include "../PowerPC.h" | ||
| #include "../../ConfigManager.h" | ||
| #include "../../CoreTiming.h" | ||
| #include "../PPCTables.h" | ||
| #include "ArmEmitter.h" | ||
| #include "../../HW/Memmap.h" | ||
|
|
||
|
|
||
| #include "Jit.h" | ||
| #include "JitRegCache.h" | ||
| #include "JitFPRCache.h" | ||
| #include "JitAsm.h" | ||
|
|
||
| void JitArm::Helper_UpdateCR1(ARMReg value) | ||
| { | ||
| // Should just update exception flags, not do any compares. | ||
| PanicAlert("CR1"); | ||
| } | ||
|
|
||
| void JitArm::fabsx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(FloatingPoint) | ||
|
|
||
| ARMReg vD = fpr.R0(inst.FD); | ||
| ARMReg vB = fpr.R0(inst.FB); | ||
|
|
||
| VABS(vD, vB); | ||
|
|
||
| if (inst.Rc) Helper_UpdateCR1(vD); | ||
| } | ||
|
|
||
| void JitArm::faddx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(FloatingPoint) | ||
|
|
||
| ARMReg vD = fpr.R0(inst.FD); | ||
| ARMReg vA = fpr.R0(inst.FA); | ||
| ARMReg vB = fpr.R0(inst.FB); | ||
|
|
||
| VADD(vD, vA, vB); | ||
| if (inst.Rc) Helper_UpdateCR1(vD); | ||
| } | ||
|
|
||
| void JitArm::fmrx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(FloatingPoint) | ||
| Default(inst); return; | ||
|
|
||
| ARMReg vD = fpr.R0(inst.FD); | ||
| ARMReg vB = fpr.R0(inst.FB); | ||
|
|
||
| VMOV(vD, vB); | ||
|
|
||
| if (inst.Rc) Helper_UpdateCR1(vD); | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,297 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program 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, version 2.0. | ||
|
|
||
| // This program 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 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
| #include "Common.h" | ||
| #include "Thunk.h" | ||
|
|
||
| #include "../../Core.h" | ||
| #include "../PowerPC.h" | ||
| #include "../../CoreTiming.h" | ||
| #include "../PPCTables.h" | ||
| #include "ArmEmitter.h" | ||
|
|
||
| #include "Jit.h" | ||
| #include "JitRegCache.h" | ||
| #include "JitAsm.h" | ||
| extern u32 Helper_Mask(u8 mb, u8 me); | ||
| // ADDI and RLWINMX broken for now | ||
|
|
||
| // Assumes that Sign and Zero flags were set by the last operation. Preserves all flags and registers. | ||
| // Jit64 ComputerRC is signed | ||
| // JIT64 GenerateRC is unsigned | ||
| void JitArm::GenerateRC(int cr) { | ||
| ARMReg rB = gpr.GetReg(); | ||
|
|
||
| MOV(rB, 0x4); // Result > 0 | ||
| SetCC(CC_EQ); MOV(rB, 0x2); // Result == 0 | ||
| SetCC(CC_MI); MOV(rB, 0x8); // Result < 0 | ||
| SetCC(); | ||
|
|
||
| STRB(R9, rB, STRUCT_OFF(PowerPC::ppcState, cr_fast) + cr); | ||
| gpr.Unlock(rB); | ||
| } | ||
| void JitArm::ComputeRC(int cr) { | ||
| ARMReg rB = gpr.GetReg(); | ||
|
|
||
| MOV(rB, 0x2); // Result == 0 | ||
| SetCC(CC_LT); MOV(rB, 0x8); // Result < 0 | ||
| SetCC(CC_GT); MOV(rB, 0x4); // Result > 0 | ||
| SetCC(); | ||
|
|
||
| STRB(R9, rB, STRUCT_OFF(PowerPC::ppcState, cr_fast) + cr); | ||
| gpr.Unlock(rB); | ||
| } | ||
|
|
||
| void JitArm::addi(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| ARMReg RD = gpr.R(inst.RD); | ||
|
|
||
| if (inst.RA) | ||
| { | ||
| ARMReg rA = gpr.GetReg(false); | ||
| ARMReg RA = gpr.R(inst.RA); | ||
| MOVI2R(rA, (u32)inst.SIMM_16); | ||
| ADD(RD, RA, rA); | ||
| } | ||
| else | ||
| MOVI2R(RD, inst.SIMM_16); | ||
| } | ||
| void JitArm::addis(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| ARMReg RD = gpr.R(inst.RD); | ||
| if (inst.RA) | ||
| { | ||
| ARMReg rA = gpr.GetReg(false); | ||
| ARMReg RA = gpr.R(inst.RA); | ||
| MOVI2R(rA, inst.SIMM_16 << 16); | ||
| ADD(RD, RA, rA); | ||
| } | ||
| else | ||
| MOVI2R(RD, inst.SIMM_16 << 16); | ||
| } | ||
| void JitArm::addx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| ARMReg RA = gpr.R(inst.RA); | ||
| ARMReg RB = gpr.R(inst.RB); | ||
| ARMReg RD = gpr.R(inst.RD); | ||
| ADDS(RD, RA, RB); | ||
| if (inst.Rc) ComputeRC(); | ||
| } | ||
| // Wrong - 28/10/2012 | ||
| void JitArm::mulli(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| Default(inst); return; | ||
| ARMReg RA = gpr.R(inst.RA); | ||
| ARMReg RD = gpr.R(inst.RD); | ||
| ARMReg rA = gpr.GetReg(); | ||
| MOVI2R(rA, inst.SIMM_16); | ||
| MUL(RD, RA, rA); | ||
| gpr.Unlock(rA); | ||
| } | ||
| void JitArm::ori(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| ARMReg RA = gpr.R(inst.RA); | ||
| ARMReg RS = gpr.R(inst.RS); | ||
| ARMReg rA = gpr.GetReg(); | ||
| MOVI2R(rA, inst.UIMM); | ||
| ORR(RA, RS, rA); | ||
| gpr.Unlock(rA); | ||
| } | ||
| void JitArm::oris(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| ARMReg RA = gpr.R(inst.RA); | ||
| ARMReg RS = gpr.R(inst.RS); | ||
| ARMReg rA = gpr.GetReg(); | ||
| MOVI2R(rA, inst.UIMM << 16); | ||
| ORR(RA, RS, rA); | ||
| gpr.Unlock(rA); | ||
| } | ||
| void JitArm::orx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| ARMReg rA = gpr.R(inst.RA); | ||
| ARMReg rS = gpr.R(inst.RS); | ||
| ARMReg rB = gpr.R(inst.RB); | ||
| ORRS(rA, rS, rB); | ||
| if (inst.Rc) | ||
| ComputeRC(); | ||
| } | ||
|
|
||
| void JitArm::extshx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
| ARMReg RA, RS; | ||
| RA = gpr.R(inst.RA); | ||
| RS = gpr.R(inst.RS); | ||
| SXTH(RA, RS); | ||
| if (inst.Rc){ | ||
| CMP(RA, 0); | ||
| ComputeRC(); | ||
| } | ||
| } | ||
| void JitArm::extsbx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
| ARMReg RA, RS; | ||
| RA = gpr.R(inst.RA); | ||
| RS = gpr.R(inst.RS); | ||
| SXTB(RA, RS); | ||
| if (inst.Rc){ | ||
| CMP(RA, 0); | ||
| ComputeRC(); | ||
| } | ||
| } | ||
| void JitArm::cmp (UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| ARMReg RA = gpr.R(inst.RA); | ||
| ARMReg RB = gpr.R(inst.RB); | ||
| int crf = inst.CRFD; | ||
| CMP(RA, RB); | ||
| ComputeRC(crf); | ||
| } | ||
| void JitArm::cmpi(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| ARMReg RA = gpr.R(inst.RA); | ||
| ARMReg rA = gpr.GetReg(); | ||
| int crf = inst.CRFD; | ||
| MOVI2R(rA, inst.SIMM_16); | ||
| CMP(RA, rA); | ||
| gpr.Unlock(rA); | ||
| ComputeRC(crf); | ||
| } | ||
| void JitArm::cmpli(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| ARMReg RA = gpr.R(inst.RA); | ||
| ARMReg rA = gpr.GetReg(); | ||
| int crf = inst.CRFD; | ||
| MOVI2R(rA, (u32)inst.UIMM); | ||
| CMP(RA, rA); | ||
|
|
||
| // Unsigned GenerateRC() | ||
|
|
||
| MOV(rA, 0x2); // Result == 0 | ||
| SetCC(CC_LO); MOV(rA, 0x8); // Result < 0 | ||
| SetCC(CC_HI); MOV(rA, 0x4); // Result > 0 | ||
| SetCC(); | ||
|
|
||
| STRB(R9, rA, STRUCT_OFF(PowerPC::ppcState, cr_fast) + crf); | ||
| gpr.Unlock(rA); | ||
|
|
||
| } | ||
| // Wrong - 27/10/2012 | ||
| void JitArm::negx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| Default(inst);return; | ||
| ARMReg RA = gpr.R(inst.RA); | ||
| ARMReg RD = gpr.R(inst.RD); | ||
|
|
||
| RSBS(RD, RA, 0); | ||
| if (inst.Rc) | ||
| { | ||
| GenerateRC(); | ||
| } | ||
| if (inst.OE) | ||
| { | ||
| BKPT(0x333); | ||
| //GenerateOverflow(); | ||
| } | ||
| } | ||
| void JitArm::rlwimix(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| u32 mask = Helper_Mask(inst.MB,inst.ME); | ||
| ARMReg RA = gpr.R(inst.RA); | ||
| ARMReg RS = gpr.R(inst.RS); | ||
| ARMReg rA = gpr.GetReg(); | ||
| ARMReg rB = gpr.GetReg(); | ||
| MOVI2R(rA, mask); | ||
|
|
||
| Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it. | ||
| if (inst.Rc) | ||
| { | ||
| BIC (rB, RA, rA); // RA & ~mask | ||
| AND (rA, rA, Shift); | ||
| ORRS(RA, rB, rA); | ||
| GenerateRC(); | ||
| } | ||
| else | ||
| { | ||
| BIC (rB, RA, rA); // RA & ~mask | ||
| AND (rA, rA, Shift); | ||
| ORR(RA, rB, rA); | ||
| } | ||
| gpr.Unlock(rA, rB); | ||
| } | ||
| void JitArm::rlwinmx(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(Integer) | ||
|
|
||
| u32 mask = Helper_Mask(inst.MB,inst.ME); | ||
| ARMReg RA = gpr.R(inst.RA); | ||
| ARMReg RS = gpr.R(inst.RS); | ||
| ARMReg rA = gpr.GetReg(); | ||
| MOVI2R(rA, mask); | ||
|
|
||
| Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it. | ||
| if (inst.Rc) | ||
| { | ||
| ANDS(RA, rA, Shift); | ||
| GenerateRC(); | ||
| } | ||
| else | ||
| AND (RA, rA, Shift); | ||
| gpr.Unlock(rA); | ||
|
|
||
| //m_GPR[inst.RA] = _rotl(m_GPR[inst.RS],inst.SH) & mask; | ||
| } | ||
|
|