mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Merge pull request #22087 from hrydgard/interpreter-stuff
Interpreter: Use a generated switch-tree dispatcher
This commit is contained in:
29 files changed
+1193
-111
No files matched your search
@@ -180,12 +180,26 @@ small examples to copy from). A module is a `const HLEFunction <name>[]` table o
|
||||
the `// add new modules here.` comment near the end of that function) - not inserted alphabetically/logically among
|
||||
the existing `Register_*()` calls. Module registration order affects numeric IDs used in savestates, so inserting a
|
||||
new module earlier in that list would break save-state compatibility for saves made with older builds.
|
||||
- Remember to add any new `.cpp`/`.c` file to **five** places: `Core/CMakeLists.txt`, `Core/Core.vcxproj`,
|
||||
`Core/Core.vcxproj.filters`, `android/jni/Android.mk`, and `libretro/Makefile.common`. New `.h` files only need the
|
||||
first three (`Android.mk`/`Makefile.common` are plain compiled-source lists so headers
|
||||
don't go in them). Only the CMakeLists.txt change can be verified from a Linux/Mac build - the rest can't be
|
||||
build-tested here, so double check them by hand against how an existing neighboring file (e.g. `sceVaudio.cpp`) is
|
||||
listed in each. Note: New files in the unittest project have to be updated in the unittest part in android/jni/Android.mk.
|
||||
- Remember to add any new `.cpp`/`.c` file to **seven** places: `Core/CMakeLists.txt`, `Core/Core.vcxproj`,
|
||||
`Core/Core.vcxproj.filters`, `UWP/CoreUWP/CoreUWP.vcxproj`, `UWP/CoreUWP/CoreUWP.vcxproj.filters`,
|
||||
`android/jni/Android.mk`, and `libretro/Makefile.common`. New `.h` files need the first five (everything except
|
||||
`Android.mk`/`Makefile.common`, which are plain compiled-source lists so headers don't go in them). Double check
|
||||
each by hand against how an existing neighboring file (e.g. `sceVaudio.cpp`) is listed. Forgetting the UWP entries
|
||||
is easy to miss - the CMake and MSBuild (`Core.vcxproj`) builds both succeed silently, and it only surfaces as a
|
||||
UWP-only build failure (this has happened for real: `Core/MIPS/InterpreterDispatch.cpp` landed without its UWP
|
||||
entries, and the omission wasn't caught until someone actually built the UWP project). Note: New files in the
|
||||
unittest project have to be updated in the unittest part in android/jni/Android.mk.
|
||||
|
||||
Both the CMakeLists.txt change (via a Linux/Mac build) and the `Core.vcxproj`/UWP changes (via MSBuild on Windows)
|
||||
can actually be build-tested, not just eyeballed - see "Build and Validation" above for the main Windows solution,
|
||||
and for UWP specifically:
|
||||
```powershell
|
||||
$installPath = & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath
|
||||
$msbuild = "$installPath\MSBuild\Current\Bin\MSBuild.exe"
|
||||
& $msbuild "UWP\PPSSPP_UWP.sln" /t:CoreUWP /p:Configuration=Debug /p:Platform=x64 /m
|
||||
```
|
||||
(only `android/jni/Android.mk` and `libretro/Makefile.common` genuinely can't be build-tested here - see their
|
||||
respective sections above for what verification is possible for those.)
|
||||
|
||||
## WebSocket debugger
|
||||
|
||||
|
||||
+6
-4
@@ -618,10 +618,12 @@ add_library(Core STATIC
|
||||
MIPS/MIPSDis.h
|
||||
MIPS/MIPSDisVFPU.cpp
|
||||
MIPS/MIPSDisVFPU.h
|
||||
MIPS/MIPSInt.cpp
|
||||
MIPS/MIPSInt.h
|
||||
MIPS/MIPSIntVFPU.cpp
|
||||
MIPS/MIPSIntVFPU.h
|
||||
MIPS/Interpreter.cpp
|
||||
MIPS/Interpreter.h
|
||||
MIPS/InterpreterDispatch.cpp
|
||||
MIPS/InterpreterDispatch.h
|
||||
MIPS/InterpreterVFPU.cpp
|
||||
MIPS/InterpreterVFPU.h
|
||||
MIPS/MIPSStackWalk.cpp
|
||||
MIPS/MIPSStackWalk.h
|
||||
MIPS/MIPSTables.cpp
|
||||
|
||||
+8
-3
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "Core/Config.h"
|
||||
@@ -188,6 +189,7 @@ static const CommandLineParam g_autoParams[] = {
|
||||
{POFF(maxScreenshotError), CmdParamType::Double, "max-mse", '\0', "Maximum allowed MSE error for screenshot comparison", CmdLineMode::Headless},
|
||||
{POFF(mountIso), CmdParamType::String, "mount", 'm', "Mount ISO/CSO on umd1:", CmdLineMode::Headless},
|
||||
{POFF(odsLog), CmdParamType::Bool, "odslog", 'o', "Also log through OutputDebugString (Windows)", CmdLineMode::Headless},
|
||||
{POFF(generateInterpreterDispatch), CmdParamType::Bool, "generate-interpreter-dispatch", '\0', "Generate C++ interpreter dispatch code (ExecInstruction) to stdout and exit", CmdLineMode::Headless},
|
||||
{POFF(resolutionScale), CmdParamType::Int, "resolution-scale", '\0', "Set the resolution scale factor"},
|
||||
{POFF(debuggerPort), CmdParamType::Int, "debugger", '\0', "Enable the WebSocket debugger on this port (0 = pick automatically); see docs/WebSocketDebugger.md"},
|
||||
{POFF(bootVSH), CmdParamType::Bool, "vsh", '\0', "Boot the VSH (requires files dumped from a PSP in the flash0 directory)"},
|
||||
@@ -257,12 +259,15 @@ int CommandLineOptions::PrintUsage(const char *progname, const char *situationTe
|
||||
// Skip mode-irrelevant parameters in help.
|
||||
continue;
|
||||
}
|
||||
char key[25]{};
|
||||
char key[64]{};
|
||||
snprintf(key, ARRAY_SIZE(key), " --%s%s%s", param.longName,
|
||||
param.shortName ? ", -" : "",
|
||||
param.shortName ? std::string(1, param.shortName).c_str() : "");
|
||||
// Fill key with spacing.
|
||||
for (size_t j = strlen(key); j < ARRAY_SIZE(key) - 1; ++j) {
|
||||
// Fill key with spacing, keeping at least one space before the doc string
|
||||
// even if the name itself ran past the normal column width.
|
||||
size_t padTo = std::max(strlen(key) + 1, (size_t)24);
|
||||
padTo = std::min(padTo, ARRAY_SIZE(key) - 1);
|
||||
for (size_t j = strlen(key); j < padTo; ++j) {
|
||||
key[j] = ' ';
|
||||
}
|
||||
if (param.type == CmdParamType::Enum) {
|
||||
|
||||
@@ -107,6 +107,8 @@ struct CommandLineOptions {
|
||||
std::optional<double> maxScreenshotError;
|
||||
// Headless: test names to skip. May be specified more than once.
|
||||
std::vector<std::string> ignoredTests;
|
||||
// Headless: generate C++ interpreter dispatch code to stdout and exit.
|
||||
std::optional<bool> generateInterpreterDispatch;
|
||||
|
||||
// SDL only.
|
||||
std::optional<int> xres;
|
||||
|
||||
+6
-4
@@ -844,8 +844,9 @@
|
||||
<ClCompile Include="MIPS\MIPSDebugInterface.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSDis.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSDisVFPU.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSInt.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSIntVFPU.cpp" />
|
||||
<ClCompile Include="MIPS\Interpreter.cpp" />
|
||||
<ClCompile Include="MIPS\InterpreterDispatch.cpp" />
|
||||
<ClCompile Include="MIPS\InterpreterVFPU.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSTables.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSVFPUUtils.cpp" />
|
||||
<ClCompile Include="MIPS\MIPSTracer.cpp" />
|
||||
@@ -1229,8 +1230,9 @@
|
||||
<ClInclude Include="MIPS\MIPSDebugInterface.h" />
|
||||
<ClInclude Include="MIPS\MIPSDis.h" />
|
||||
<ClInclude Include="MIPS\MIPSDisVFPU.h" />
|
||||
<ClInclude Include="MIPS\MIPSInt.h" />
|
||||
<ClInclude Include="MIPS\MIPSIntVFPU.h" />
|
||||
<ClInclude Include="MIPS\Interpreter.h" />
|
||||
<ClInclude Include="MIPS\InterpreterDispatch.h" />
|
||||
<ClInclude Include="MIPS\InterpreterVFPU.h" />
|
||||
<ClInclude Include="MIPS\MIPSTables.h" />
|
||||
<ClInclude Include="MIPS\MIPSVFPUUtils.h" />
|
||||
<ClInclude Include="MIPS\MIPSTracer.h" />
|
||||
|
||||
@@ -111,10 +111,13 @@
|
||||
<ClCompile Include="MIPS\MIPSDisVFPU.cpp">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\MIPSInt.cpp">
|
||||
<ClCompile Include="MIPS\Interpreter.cpp">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\MIPSIntVFPU.cpp">
|
||||
<ClCompile Include="MIPS\InterpreterDispatch.cpp">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\InterpreterVFPU.cpp">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\MIPSTables.cpp">
|
||||
@@ -1416,10 +1419,13 @@
|
||||
<ClInclude Include="MIPS\MIPSDisVFPU.h">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\MIPSInt.h">
|
||||
<ClInclude Include="MIPS\Interpreter.h">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\MIPSIntVFPU.h">
|
||||
<ClInclude Include="MIPS\InterpreterDispatch.h">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\InterpreterVFPU.h">
|
||||
<Filter>MIPS</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\MIPSTables.h">
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "Core/HLE/ErrorCodes.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
#include "Core/MIPS/Interpreter.h"
|
||||
#include "Core/MIPS/JitCommon/JitCommon.h"
|
||||
|
||||
#include "Core/FileSystems/FileSystem.h"
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSAnalyst.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
#include "Core/MIPS/Interpreter.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/HLE/ReplaceTables.h"
|
||||
#include "Core/MIPS/ARM/ArmRegCache.h"
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSAnalyst.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
#include "Core/MIPS/Interpreter.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/HLE/ReplaceTables.h"
|
||||
#include "Core/MIPS/ARM64/Arm64RegCache.h"
|
||||
|
||||
@@ -1216,7 +1216,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) {
|
||||
case IROp::Interpret: // SLOW fallback. Can be made faster. Ideally should be removed but may be useful for debugging.
|
||||
{
|
||||
MIPSOpcode op(inst->constant);
|
||||
MIPSInterpret(op);
|
||||
MIPSInterpret(mips, op);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
#include "Core/MIPS/Interpreter.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/MIPS/IR/IRRegCache.h"
|
||||
#include "Core/MIPS/IR/IRInterpreter.h"
|
||||
|
||||
@@ -149,7 +149,7 @@ void IRNativeBackend::DoMIPSInst(uint32_t value) {
|
||||
if constexpr (enableDebugStats)
|
||||
debugSeenNotCompiled[MIPSGetName(op)]++;
|
||||
|
||||
MIPSInterpret(op);
|
||||
MIPSInterpret(currentMIPS, op);
|
||||
}
|
||||
|
||||
// This is called from IR->JIT implementation to fall back to the IR interpreter for missing ops.
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
#include "Core/MIPS/Interpreter.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
@@ -75,20 +75,20 @@ static inline void SkipLikely() {
|
||||
}
|
||||
}
|
||||
|
||||
int MIPS_SingleStep() {
|
||||
if (!Memory::IsValid4AlignedAddress(mipsr4k.pc)) {
|
||||
Core_ExecException(mipsr4k.pc, mipsr4k.pc, ExecExceptionType::JUMP);
|
||||
int MIPS_SingleStep(MIPSState *mips) {
|
||||
if (!Memory::IsValid4AlignedAddress(mips->pc)) {
|
||||
Core_ExecException(mips->pc, mips-> pc, ExecExceptionType::JUMP);
|
||||
return 0;
|
||||
}
|
||||
MIPSOpcode op = Memory::Read_Opcode_JIT(mipsr4k.pc); // now unchecked
|
||||
if (mipsr4k.inDelaySlot) {
|
||||
MIPSInterpret(op);
|
||||
if (mipsr4k.inDelaySlot) {
|
||||
mipsr4k.pc = mipsr4k.nextPC;
|
||||
mipsr4k.inDelaySlot = false;
|
||||
MIPSOpcode op = Memory::Read_Opcode_JIT(mips->pc); // now unchecked
|
||||
if (mips->inDelaySlot) {
|
||||
MIPSInterpret(mips, op);
|
||||
if (mips->inDelaySlot) {
|
||||
mips->pc = mips->nextPC;
|
||||
mips->inDelaySlot = false;
|
||||
}
|
||||
} else {
|
||||
MIPSInterpret(op);
|
||||
MIPSInterpret(mips, op);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -1122,7 +1122,7 @@ namespace MIPSInt
|
||||
|
||||
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
|
||||
// Interpret the original instruction under the hook.
|
||||
MIPSInterpret(Memory::Read_Instruction(PC, true));
|
||||
MIPSInterpret(currentMIPS, Memory::Read_Instruction(PC, true));
|
||||
} else if (cycles < 0) {
|
||||
// Leave PC unchanged, call the replacement again (assumes args are modified.)
|
||||
currentMIPS->downcount += cycles;
|
||||
@@ -1135,7 +1135,7 @@ namespace MIPSInt
|
||||
ERROR_LOG(Log::CPU, "Bad replacement function index %i", index);
|
||||
}
|
||||
// Interpret the original instruction under it.
|
||||
MIPSInterpret(Memory::Read_Instruction(PC, true));
|
||||
MIPSInterpret(currentMIPS, Memory::Read_Instruction(PC, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
|
||||
int MIPS_SingleStep();
|
||||
int MIPS_SingleStep(MIPSState *mips);
|
||||
|
||||
namespace MIPSInt
|
||||
{
|
||||
@@ -0,0 +1,736 @@
|
||||
// Copyright (c) 2012- PPSSPP 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 or later versions.
|
||||
|
||||
// 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 git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
// AUTO-GENERATED by `PPSSPPHeadless --generate-interpreter-dispatch` from the
|
||||
// tables in MIPSTables.cpp - do not hand-edit, regenerate instead.
|
||||
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/Interpreter.h"
|
||||
#include "Core/MIPS/InterpreterVFPU.h"
|
||||
|
||||
// Returns the cycle count consumed, or -1 if op isn't a recognized instruction -
|
||||
// callers must fall back to MIPSInterpret() themselves in that case.
|
||||
int ExecInstruction(MIPSOpcode op) {
|
||||
switch ((op.encoding >> 26) & 0x3f) {
|
||||
case 0:
|
||||
{
|
||||
switch ((op.encoding >> 0) & 0x3f) {
|
||||
// sll, srl, sra, sllv, srlv, srav
|
||||
case 0:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 6:
|
||||
case 7:
|
||||
MIPSInt::Int_ShiftType(op);
|
||||
return 1;
|
||||
// jr, jalr
|
||||
case 8:
|
||||
case 9:
|
||||
MIPSInt::Int_JumpRegType(op);
|
||||
return 1;
|
||||
// movz, movn, add, addu, sub, subu, and, or, xor, nor, slt, sltu, max, min
|
||||
case 10:
|
||||
case 11:
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
case 36:
|
||||
case 37:
|
||||
case 38:
|
||||
case 39:
|
||||
case 42:
|
||||
case 43:
|
||||
case 44:
|
||||
case 45:
|
||||
MIPSInt::Int_RType3(op);
|
||||
return 1;
|
||||
// syscall
|
||||
case 12:
|
||||
MIPSInt::Int_Syscall(op);
|
||||
return 1;
|
||||
// break
|
||||
case 13:
|
||||
MIPSInt::Int_Break(op);
|
||||
return 1;
|
||||
// sync
|
||||
case 15:
|
||||
MIPSInt::Int_Sync(op);
|
||||
return 1;
|
||||
// mfhi, mthi, mflo, mtlo, mult, multu, div, divu, madd, maddu, msub, msubu
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
case 19:
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
case 27:
|
||||
case 28:
|
||||
case 29:
|
||||
case 46:
|
||||
case 47:
|
||||
MIPSInt::Int_MulDivType(op);
|
||||
return 1;
|
||||
// clz, clo
|
||||
case 22:
|
||||
case 23:
|
||||
MIPSInt::Int_RType2(op);
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch ((op.encoding >> 16) & 0x1f) {
|
||||
// bltz, bgez, bltzl, bgezl, bltzal, bgezal, bltzall, bgezall
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
case 19:
|
||||
MIPSInt::Int_RelBranchRI(op);
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// j, jal
|
||||
case 2:
|
||||
case 3:
|
||||
MIPSInt::Int_JumpType(op);
|
||||
return 1;
|
||||
// beq, bne, blez, bgtz, beql, bnel, blezl, bgtzl
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 20:
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
MIPSInt::Int_RelBranch(op);
|
||||
return 1;
|
||||
// addi, addiu, slti, sltiu, andi, ori, xori, lui
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
MIPSInt::Int_IType(op);
|
||||
return 1;
|
||||
case 16:
|
||||
{
|
||||
switch ((op.encoding >> 21) & 0x1f) {
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
case 19:
|
||||
case 20:
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
case 27:
|
||||
case 28:
|
||||
case 29:
|
||||
case 30:
|
||||
case 31:
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 17:
|
||||
{
|
||||
switch ((op.encoding >> 21) & 0x1f) {
|
||||
// mfc1, cfc1, mtc1, ctc1
|
||||
case 0:
|
||||
case 2:
|
||||
case 4:
|
||||
case 6:
|
||||
MIPSInt::Int_mxc1(op);
|
||||
return 1;
|
||||
case 8:
|
||||
{
|
||||
switch ((op.encoding >> 16) & 0x1f) {
|
||||
// bc1f, bc1t, bc1fl, bc1tl
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
MIPSInt::Int_FPUBranch(op);
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 16:
|
||||
{
|
||||
switch ((op.encoding >> 0) & 0x3f) {
|
||||
// add.s, sub.s, mul.s
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
MIPSInt::Int_FPU3op(op);
|
||||
return 1;
|
||||
// div.s
|
||||
case 3:
|
||||
MIPSInt::Int_FPU3op(op);
|
||||
return 29;
|
||||
// sqrt.s, abs.s, mov.s, neg.s, round.w.s, trunc.w.s, ceil.w.s, floor.w.s, cvt.w.s
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
case 36:
|
||||
MIPSInt::Int_FPU2op(op);
|
||||
return 1;
|
||||
// dis.int
|
||||
case 38:
|
||||
MIPSInt::Int_Interrupt(op);
|
||||
return 1;
|
||||
// c.f.s, c.un.s, c.eq.s, c.ueq.s, c.olt.s, c.ult.s, c.ole.s, c.ule.s, c.sf.s, c.ngle.s, c.seq.s, c.ngl.s, c.lt.s, c.nge.s, c.le.s, c.ngt.s
|
||||
case 48:
|
||||
case 49:
|
||||
case 50:
|
||||
case 51:
|
||||
case 52:
|
||||
case 53:
|
||||
case 54:
|
||||
case 55:
|
||||
case 56:
|
||||
case 57:
|
||||
case 58:
|
||||
case 59:
|
||||
case 60:
|
||||
case 61:
|
||||
case 62:
|
||||
case 63:
|
||||
MIPSInt::Int_FPUComp(op);
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 20:
|
||||
{
|
||||
switch ((op.encoding >> 0) & 0x3f) {
|
||||
// cvt.s.w
|
||||
case 32:
|
||||
MIPSInt::Int_FPU2op(op);
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 18:
|
||||
{
|
||||
switch ((op.encoding >> 21) & 0x1f) {
|
||||
// mfv, mtv
|
||||
case 3:
|
||||
case 7:
|
||||
MIPSInt::Int_Mftv(op);
|
||||
return 2;
|
||||
case 8:
|
||||
{
|
||||
switch ((op.encoding >> 16) & 0x3) {
|
||||
// bvf, bvt, bvfl, bvtl
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
MIPSInt::Int_VBranch(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 24:
|
||||
{
|
||||
switch ((op.encoding >> 23) & 0x7) {
|
||||
// vadd, vsub, vdiv
|
||||
case 0:
|
||||
case 1:
|
||||
case 7:
|
||||
MIPSInt::Int_VecDo3(op);
|
||||
return 2;
|
||||
// vsbn
|
||||
case 2:
|
||||
MIPSInt::Int_Vsbn(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 25:
|
||||
{
|
||||
switch ((op.encoding >> 23) & 0x7) {
|
||||
// vmul
|
||||
case 0:
|
||||
MIPSInt::Int_VecDo3(op);
|
||||
return 2;
|
||||
// vdot
|
||||
case 1:
|
||||
MIPSInt::Int_VDot(op);
|
||||
return 2;
|
||||
// vscl
|
||||
case 2:
|
||||
MIPSInt::Int_VScl(op);
|
||||
return 2;
|
||||
// vhdp
|
||||
case 4:
|
||||
MIPSInt::Int_VHdp(op);
|
||||
return 2;
|
||||
// vcrs
|
||||
case 5:
|
||||
MIPSInt::Int_Vcrs(op);
|
||||
return 2;
|
||||
// vdet
|
||||
case 6:
|
||||
MIPSInt::Int_Vdet(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 26:
|
||||
{
|
||||
switch ((op.encoding >> 24) & 0x3) {
|
||||
// RUNBLOCK
|
||||
case 0:
|
||||
MIPSInt::Int_Emuhack(op);
|
||||
return 2;
|
||||
// RetKrnl, CallRepl
|
||||
case 1:
|
||||
case 2:
|
||||
MIPSInt::Int_Emuhack(op);
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 27:
|
||||
{
|
||||
switch ((op.encoding >> 23) & 0x7) {
|
||||
// vcmp
|
||||
case 0:
|
||||
MIPSInt::Int_Vcmp(op);
|
||||
return 2;
|
||||
// vmin, vmax
|
||||
case 2:
|
||||
case 3:
|
||||
MIPSInt::Int_Vminmax(op);
|
||||
return 2;
|
||||
// vscmp
|
||||
case 5:
|
||||
MIPSInt::Int_Vscmp(op);
|
||||
return 2;
|
||||
// vsge
|
||||
case 6:
|
||||
MIPSInt::Int_Vsge(op);
|
||||
return 2;
|
||||
// vslt
|
||||
case 7:
|
||||
MIPSInt::Int_Vslt(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 28:
|
||||
{
|
||||
switch ((op.encoding >> 0) & 0x3f) {
|
||||
// mfic, mtic
|
||||
case 36:
|
||||
case 38:
|
||||
MIPSInt::Int_Special2(op);
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 31:
|
||||
{
|
||||
switch ((op.encoding >> 0) & 0x3f) {
|
||||
// ext, ins
|
||||
case 0:
|
||||
case 4:
|
||||
MIPSInt::Int_Special3(op);
|
||||
return 1;
|
||||
case 24:
|
||||
case 32:
|
||||
{
|
||||
switch ((op.encoding >> 6) & 0x1f) {
|
||||
// wsbh, wsbw
|
||||
case 2:
|
||||
case 3:
|
||||
MIPSInt::Int_Allegrex2(op);
|
||||
return 1;
|
||||
// seb, bitrev, seh
|
||||
case 16:
|
||||
case 20:
|
||||
case 24:
|
||||
MIPSInt::Int_Allegrex(op);
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// lb, lh, lwl, lw, lbu, lhu, lwr, sb, sh, swl, sw, swr
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
case 36:
|
||||
case 37:
|
||||
case 38:
|
||||
case 40:
|
||||
case 41:
|
||||
case 42:
|
||||
case 43:
|
||||
case 46:
|
||||
MIPSInt::Int_ITypeMem(op);
|
||||
return 1;
|
||||
// cache
|
||||
case 47:
|
||||
MIPSInt::Int_Cache(op);
|
||||
return 1;
|
||||
// ll, sc
|
||||
case 48:
|
||||
case 56:
|
||||
MIPSInt::Int_StoreSync(op);
|
||||
return 1;
|
||||
// lwc1, swc1
|
||||
case 49:
|
||||
case 57:
|
||||
MIPSInt::Int_FPULS(op);
|
||||
return 1;
|
||||
// lv.s, sv.s
|
||||
case 50:
|
||||
case 58:
|
||||
MIPSInt::Int_SV(op);
|
||||
return 2;
|
||||
case 52:
|
||||
{
|
||||
switch ((op.encoding >> 21) & 0x1f) {
|
||||
case 0:
|
||||
{
|
||||
switch ((op.encoding >> 16) & 0x1f) {
|
||||
// vmov, vabs, vneg, vsat0, vsat1, vrcp, vrsq, vsin, vcos, vexp2, vlog2, vsqrt, vasin, vnrcp, vnsin, vrexp2
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 5:
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
case 19:
|
||||
case 20:
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
case 26:
|
||||
case 28:
|
||||
MIPSInt::Int_VV2Op(op);
|
||||
return 2;
|
||||
// vidt
|
||||
case 3:
|
||||
MIPSInt::Int_Vidt(op);
|
||||
return 2;
|
||||
// vzero, vone
|
||||
case 6:
|
||||
case 7:
|
||||
MIPSInt::Int_VVectorInit(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch ((op.encoding >> 16) & 0x1f) {
|
||||
// vrnds
|
||||
case 0:
|
||||
MIPSInt::Int_Vrnds(op);
|
||||
return 2;
|
||||
// vrndi, vrndf1, vrndf2
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
MIPSInt::Int_VrndX(op);
|
||||
return 2;
|
||||
// vf2h
|
||||
case 18:
|
||||
MIPSInt::Int_Vf2h(op);
|
||||
return 2;
|
||||
// vh2f
|
||||
case 19:
|
||||
MIPSInt::Int_Vh2f(op);
|
||||
return 2;
|
||||
// vsbz
|
||||
case 22:
|
||||
MIPSInt::Int_Vsbz(op);
|
||||
return 2;
|
||||
// vlgb
|
||||
case 23:
|
||||
MIPSInt::Int_Vlgb(op);
|
||||
return 2;
|
||||
// vuc2ifs, vc2i, vus2i, vs2i
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
case 27:
|
||||
MIPSInt::Int_Vx2i(op);
|
||||
return 2;
|
||||
// vi2uc, vi2c, vi2us, vi2s
|
||||
case 28:
|
||||
case 29:
|
||||
case 30:
|
||||
case 31:
|
||||
MIPSInt::Int_Vi2x(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
switch ((op.encoding >> 16) & 0x1f) {
|
||||
// vsrt1
|
||||
case 0:
|
||||
MIPSInt::Int_Vsrt1(op);
|
||||
return 2;
|
||||
// vsrt2
|
||||
case 1:
|
||||
MIPSInt::Int_Vsrt2(op);
|
||||
return 2;
|
||||
// vbfy1, vbfy2
|
||||
case 2:
|
||||
case 3:
|
||||
MIPSInt::Int_Vbfy(op);
|
||||
return 2;
|
||||
// vocp
|
||||
case 4:
|
||||
MIPSInt::Int_Vocp(op);
|
||||
return 2;
|
||||
// vsocp
|
||||
case 5:
|
||||
MIPSInt::Int_Vsocp(op);
|
||||
return 2;
|
||||
// vfad
|
||||
case 6:
|
||||
MIPSInt::Int_Vfad(op);
|
||||
return 2;
|
||||
// vavg
|
||||
case 7:
|
||||
MIPSInt::Int_Vavg(op);
|
||||
return 2;
|
||||
// vsrt3
|
||||
case 8:
|
||||
MIPSInt::Int_Vsrt3(op);
|
||||
return 2;
|
||||
// vsrt4
|
||||
case 9:
|
||||
MIPSInt::Int_Vsrt4(op);
|
||||
return 2;
|
||||
// vsgn
|
||||
case 10:
|
||||
MIPSInt::Int_Vsgn(op);
|
||||
return 2;
|
||||
// vmfvc
|
||||
case 16:
|
||||
MIPSInt::Int_Vmfvc(op);
|
||||
return 2;
|
||||
// vmtvc
|
||||
case 17:
|
||||
MIPSInt::Int_Vmtvc(op);
|
||||
return 2;
|
||||
// vt4444, vt5551, vt5650
|
||||
case 25:
|
||||
case 26:
|
||||
case 27:
|
||||
MIPSInt::Int_ColorConv(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// vcst
|
||||
case 3:
|
||||
MIPSInt::Int_Vcst(op);
|
||||
return 2;
|
||||
// vf2in, vf2iz, vf2iu, vf2id
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
case 19:
|
||||
MIPSInt::Int_Vf2i(op);
|
||||
return 2;
|
||||
// vi2f
|
||||
case 20:
|
||||
MIPSInt::Int_Vi2f(op);
|
||||
return 2;
|
||||
// vcmov
|
||||
case 21:
|
||||
MIPSInt::Int_Vcmov(op);
|
||||
return 2;
|
||||
// vwbn, vwbn, vwbn, vwbn, vwbn, vwbn, vwbn, vwbn
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
case 27:
|
||||
case 28:
|
||||
case 29:
|
||||
case 30:
|
||||
case 31:
|
||||
MIPSInt::Int_Vwbn(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// lv, lv.q, sv, sv.q
|
||||
case 53:
|
||||
case 54:
|
||||
case 61:
|
||||
case 62:
|
||||
MIPSInt::Int_SVQ(op);
|
||||
return 2;
|
||||
case 55:
|
||||
{
|
||||
switch ((op.encoding >> 23) & 0x7) {
|
||||
// vpfxs, vpfxs, vpfxt, vpfxt, vpfxd, vpfxd
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
MIPSInt::Int_VPFX(op);
|
||||
return 2;
|
||||
// viim.s, vfim.s
|
||||
case 6:
|
||||
case 7:
|
||||
MIPSInt::Int_Viim(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
case 60:
|
||||
{
|
||||
switch ((op.encoding >> 21) & 0x1f) {
|
||||
// vmmul, vmmul, vmmul, vmmul
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
MIPSInt::Int_Vmmul(op);
|
||||
return 2;
|
||||
// v(h)tfm2, v(h)tfm2, v(h)tfm2, v(h)tfm2, v(h)tfm3, v(h)tfm3, v(h)tfm3, v(h)tfm3, v(h)tfm4, v(h)tfm4, v(h)tfm4, v(h)tfm4
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
MIPSInt::Int_Vtfm(op);
|
||||
return 2;
|
||||
// vmscl, vmscl, vmscl, vmscl
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
case 19:
|
||||
MIPSInt::Int_Vmscl(op);
|
||||
return 2;
|
||||
// vcrsp.t/vqmul.q, vcrsp.t/vqmul.q, vcrsp.t/vqmul.q, vcrsp.t/vqmul.q
|
||||
case 20:
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
MIPSInt::Int_CrossQuat(op);
|
||||
return 2;
|
||||
case 28:
|
||||
{
|
||||
switch ((op.encoding >> 16) & 0xf) {
|
||||
// vmmov
|
||||
case 0:
|
||||
MIPSInt::Int_Vmmov(op);
|
||||
return 2;
|
||||
// vmidt, vmzero, vmone
|
||||
case 3:
|
||||
case 6:
|
||||
case 7:
|
||||
MIPSInt::Int_VMatrixInit(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// vrot
|
||||
case 29:
|
||||
MIPSInt::Int_Vrot(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// vflush
|
||||
case 63:
|
||||
MIPSInt::Int_Vflush(op);
|
||||
return 2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2012- PPSSPP 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 or later versions.
|
||||
|
||||
// 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 git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
|
||||
// Fast switch-tree interpreter dispatcher, generated into InterpreterDispatch.cpp by
|
||||
// GenerateInterpreterDispatch() in MIPSTables.cpp. Executes op on the global currentMIPS
|
||||
// (same convention as the MIPSInt::Int_* handlers it calls into) and returns the number of
|
||||
// cycles it consumed - or -1 if op isn't a recognized instruction (an invalid encoding, or
|
||||
// one of the handful of real instructions with no interpreter implementation, e.g.
|
||||
// tge/tlt/teq/...). Not total by design: the caller must fall back to MIPSInterpret() on a
|
||||
// negative return, since deciding what "unhandled" means is a caller policy, not something
|
||||
// a mechanically generated dispatch tree should embed.
|
||||
int ExecInstruction(MIPSOpcode op);
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
#include "Core/MIPS/Interpreter.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/MIPS/MIPSVFPUUtils.h"
|
||||
|
||||
File renamed without changes.
+4
-4
@@ -26,7 +26,7 @@
|
||||
#include "Common/Serialize/SerializeFuncs.h"
|
||||
#include "Core/ConfigValues.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
#include "Core/MIPS/Interpreter.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/MIPS/MIPSDebugInterface.h"
|
||||
#include "Core/MIPS/MIPSVFPUUtils.h"
|
||||
@@ -323,8 +323,8 @@ void MIPSState::DoState(PointerWrap &p) {
|
||||
}
|
||||
|
||||
void MIPSState::SingleStep() {
|
||||
int cycles = MIPS_SingleStep();
|
||||
currentMIPS->downcount -= cycles;
|
||||
int cycles = MIPS_SingleStep(this);
|
||||
downcount -= cycles;
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ int MIPSState::RunLoopUntil(u64 globalTicks) {
|
||||
break;
|
||||
|
||||
case CPUCore::INTERPRETER:
|
||||
return MIPSInterpret_RunUntil(globalTicks);
|
||||
return MIPSInterpret_RunUntil(this, globalTicks);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
+194
-44
@@ -15,6 +15,8 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
#include "Core/Core.h"
|
||||
@@ -22,8 +24,9 @@
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSDis.h"
|
||||
#include "Core/MIPS/MIPSDisVFPU.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
#include "Core/MIPS/MIPSIntVFPU.h"
|
||||
#include "Core/MIPS/Interpreter.h"
|
||||
#include "Core/MIPS/InterpreterDispatch.h"
|
||||
#include "Core/MIPS/InterpreterVFPU.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
@@ -72,6 +75,7 @@ struct MIPSInstruction {
|
||||
MIPSComp::MIPSCompileFunc compile;
|
||||
MIPSDisFunc disasm;
|
||||
MIPSInterpretFunc interpret;
|
||||
const char *interpretName;
|
||||
//MIPSInstructionInfo information;
|
||||
MIPSInfo flags;
|
||||
};
|
||||
@@ -80,7 +84,7 @@ struct MIPSInstruction {
|
||||
#define INVALID_X_8 INVALID,INVALID,INVALID,INVALID,INVALID,INVALID,INVALID,INVALID
|
||||
|
||||
#define ENCODING(a) {a}
|
||||
#define INSTR(name, comp, dis, inter, flags) {Instruc, name, comp, dis, inter, MIPSInfo(flags)}
|
||||
#define INSTR(name, comp, dis, inter, flags) {Instruc, name, comp, dis, inter, #inter, MIPSInfo(flags)}
|
||||
|
||||
#define JITFUNC(f) (&MIPSFrontendInterface::f)
|
||||
|
||||
@@ -899,6 +903,145 @@ const MIPSInstruction *MIPSGetInstruction(MIPSOpcode op) {
|
||||
return instr;
|
||||
}
|
||||
|
||||
// Emits one level of the dispatch tree as a switch statement, recursing into nested
|
||||
// switches for ENCODING() redirects. Mirrors the traversal MIPSGetInstruction() does at
|
||||
// runtime, but resolves it into source text once instead of walking it on every call.
|
||||
static void EmitDispatchLevel(std::string &out, MipsEncoding encoding, int indent) {
|
||||
const MIPSInstruction *table = mipsTables[encoding];
|
||||
const EncodingBitsInfo &bits = encodingBits[encoding];
|
||||
_dbg_assert_(table != nullptr);
|
||||
|
||||
std::string ind(indent * 4, ' ');
|
||||
std::string ind2((indent + 1) * 4, ' ');
|
||||
|
||||
// Group indices that share identical behavior (same leaf interpret function, or same
|
||||
// redirect target) so they can share one case block instead of duplicating it.
|
||||
u32 size = bits.mask + 1;
|
||||
std::vector<bool> handled(size, false);
|
||||
bool anyCases = false;
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
const MIPSInstruction &probe = table[i];
|
||||
bool isInvalid = probe.altEncoding == Inval || (probe.altEncoding == Instruc && probe.interpret == nullptr);
|
||||
if (!isInvalid) {
|
||||
anyCases = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!anyCases) {
|
||||
// Every slot in this level is invalid (or unimplemented) - no point emitting an
|
||||
// empty switch (MSVC warns C4065 on a switch with only a default label).
|
||||
out += ind + "return -1;\n";
|
||||
return;
|
||||
}
|
||||
|
||||
out += ind + StringFromFormat("switch ((op.encoding >> %d) & 0x%x) {\n", bits.shift, bits.mask);
|
||||
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
if (handled[i])
|
||||
continue;
|
||||
const MIPSInstruction &instr = table[i];
|
||||
if (instr.altEncoding == Inval)
|
||||
continue; // Falls through to default (slow path).
|
||||
if (instr.altEncoding == Instruc && instr.interpret == nullptr)
|
||||
continue; // No interpreter implemented for this one (e.g. tge/tlt/teq/...) - slow path.
|
||||
handled[i] = true;
|
||||
|
||||
std::vector<u32> group{ i };
|
||||
for (u32 j = i + 1; j < size; j++) {
|
||||
if (handled[j])
|
||||
continue;
|
||||
const MIPSInstruction &other = table[j];
|
||||
bool same;
|
||||
if (instr.altEncoding == Instruc && other.altEncoding == Instruc)
|
||||
// Cycle count must match too - it's baked into the generated "return N;",
|
||||
// so two leaves sharing a handler but not a cycle count can't be merged.
|
||||
same = instr.interpret == other.interpret && instr.flags.cycles == other.flags.cycles;
|
||||
else if (instr.altEncoding != Instruc && other.altEncoding != Instruc)
|
||||
same = instr.altEncoding == other.altEncoding;
|
||||
else
|
||||
same = false;
|
||||
if (same) {
|
||||
group.push_back(j);
|
||||
handled[j] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (instr.altEncoding == Instruc) {
|
||||
std::string names;
|
||||
for (u32 idx : group) {
|
||||
if (!names.empty())
|
||||
names += ", ";
|
||||
names += table[idx].name;
|
||||
}
|
||||
out += ind + StringFromFormat("// %s\n", names.c_str());
|
||||
for (u32 idx : group)
|
||||
out += ind + StringFromFormat("case %d:\n", idx);
|
||||
out += ind2 + StringFromFormat("MIPSInt::%s(op);\n", instr.interpretName);
|
||||
out += ind2 + StringFromFormat("return %d;\n", (int)instr.flags.cycles);
|
||||
} else {
|
||||
for (u32 idx : group)
|
||||
out += ind + StringFromFormat("case %d:\n", idx);
|
||||
out += ind + "{\n";
|
||||
EmitDispatchLevel(out, instr.altEncoding, indent + 1);
|
||||
out += ind + "}\n";
|
||||
// No trailing return here - the nested switch above always either returns
|
||||
// a value directly from a case, or falls to its own default's "goto slow_path;".
|
||||
}
|
||||
}
|
||||
|
||||
out += ind + "default:\n";
|
||||
out += ind2 + "return -1;\n";
|
||||
out += ind + "}\n";
|
||||
}
|
||||
|
||||
// Generates a full, compilable Core/MIPS/InterpreterDispatch.cpp: a fast
|
||||
// int ExecInstruction(MIPSOpcode op) dispatcher, built by resolving the tables above into
|
||||
// a nested switch tree at generation time, so each real instruction is reached by a direct
|
||||
// call instead of MIPSGetInstruction()'s per-instruction table walk plus indirect call
|
||||
// through instr->interpret. Leaves call straight into the existing MIPSInt::Int_* handlers
|
||||
// - the tables only record which handler an opcode maps to, not the handler's behavior, so
|
||||
// that's the only thing there is to call - and return that instruction's fixed cycle count
|
||||
// (baked in at generation time, same value MIPSGetInstructionCycleEstimate() would have
|
||||
// returned) so the caller can still track downcount. Operates on the global currentMIPS,
|
||||
// same as the Int_* handlers do.
|
||||
//
|
||||
// The generated function is deliberately not total: most of the 32-bit opcode space doesn't
|
||||
// decode to anything (either a genuinely invalid encoding, or a real-but-uninterpreted
|
||||
// instruction like tge/tlt/teq/...), and any such case returns -1 instead of embedding a
|
||||
// fallback. The generator has no more information about those cases than "not one of the
|
||||
// ~305 known leaves" - what to actually do about that (log it, disassemble it, whatever) is
|
||||
// a policy decision that belongs to the caller, not to a mechanically generated dispatch
|
||||
// tree. Callers must check for a negative return and fall back to MIPSInterpret() themselves.
|
||||
//
|
||||
// Regenerate by running `PPSSPPHeadless --generate-interpreter-dispatch > Core/MIPS/InterpreterDispatch.cpp`
|
||||
// after the tables above change. Do not hand-edit the generated file.
|
||||
std::string GenerateInterpreterDispatch() {
|
||||
std::string out;
|
||||
out += "// Copyright (c) 2012- PPSSPP Project.\n\n";
|
||||
out += "// This program is free software: you can redistribute it and/or modify\n";
|
||||
out += "// it under the terms of the GNU General Public License as published by\n";
|
||||
out += "// the Free Software Foundation, version 2.0 or later versions.\n\n";
|
||||
out += "// This program is distributed in the hope that it will be useful,\n";
|
||||
out += "// but WITHOUT ANY WARRANTY; without even the implied warranty of\n";
|
||||
out += "// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n";
|
||||
out += "// GNU General Public License 2.0 for more details.\n\n";
|
||||
out += "// A copy of the GPL 2.0 should have been included with the program.\n";
|
||||
out += "// If not, see http://www.gnu.org/licenses/\n\n";
|
||||
out += "// Official git repository and contact information can be found at\n";
|
||||
out += "// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.\n\n";
|
||||
out += "// AUTO-GENERATED by `PPSSPPHeadless --generate-interpreter-dispatch` from the\n";
|
||||
out += "// tables in MIPSTables.cpp - do not hand-edit, regenerate instead.\n\n";
|
||||
out += "#include \"Core/MIPS/MIPS.h\"\n";
|
||||
out += "#include \"Core/MIPS/Interpreter.h\"\n";
|
||||
out += "#include \"Core/MIPS/InterpreterVFPU.h\"\n\n";
|
||||
out += "// Returns the cycle count consumed, or -1 if op isn't a recognized instruction -\n";
|
||||
out += "// callers must fall back to MIPSInterpret() themselves in that case.\n";
|
||||
out += "int ExecInstruction(MIPSOpcode op) {\n";
|
||||
EmitDispatchLevel(out, Imme, 1);
|
||||
out += "}\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
void MIPSCompileOp(MIPSOpcode op, MIPSComp::MIPSFrontendInterface *jit) {
|
||||
if (op == 0)
|
||||
return;
|
||||
@@ -939,16 +1082,24 @@ void MIPSDisAsm(MIPSOpcode op, u32 pc, char *out, size_t outSize, bool tabsToSpa
|
||||
}
|
||||
}
|
||||
|
||||
static inline void Interpret(const MIPSInstruction *instr, MIPSOpcode op) {
|
||||
// Shared by both Interpret() below (the MIPSGetInstruction()-based slow path, still used by
|
||||
// RunUntilWithChecks) and RunUntilFast()'s -1 handling (ExecInstruction() is generated from
|
||||
// the exact same tables MIPSGetInstruction() walks, so it has no better information to offer
|
||||
// here - no point re-walking those tables just to rediscover "no interpreter for this op").
|
||||
static void HandleUnknownInstruction(MIPSState *mips, MIPSOpcode op) {
|
||||
ERROR_LOG_REPORT(Log::CPU, "Unknown instruction %08x at %08x", op.encoding, mips->pc);
|
||||
// Try to disassemble it
|
||||
char disasm[256];
|
||||
MIPSDisAsm(op, mips->pc, disasm, sizeof(disasm));
|
||||
_dbg_assert_msg_(0, "%s", disasm);
|
||||
mips->pc += 4;
|
||||
}
|
||||
|
||||
static inline void Interpret(MIPSState *mips, const MIPSInstruction *instr, MIPSOpcode op) {
|
||||
if (instr && instr->interpret) {
|
||||
instr->interpret(op);
|
||||
} else {
|
||||
ERROR_LOG_REPORT(Log::CPU, "Unknown instruction %08x at %08x", op.encoding, currentMIPS->pc);
|
||||
// Try to disassemble it
|
||||
char disasm[256];
|
||||
MIPSDisAsm(op, currentMIPS->pc, disasm, sizeof(disasm));
|
||||
_dbg_assert_msg_(0, "%s", disasm);
|
||||
currentMIPS->pc += 4;
|
||||
HandleUnknownInstruction(mips, op);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,18 +1109,12 @@ inline int GetInstructionCycleEstimate(const MIPSInstruction *instr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void MIPSInterpret(MIPSOpcode op) {
|
||||
void MIPSInterpret(MIPSState *mips, MIPSOpcode op) {
|
||||
const MIPSInstruction *instr = MIPSGetInstruction(op);
|
||||
Interpret(instr, op);
|
||||
Interpret(mips, instr, op);
|
||||
}
|
||||
|
||||
#define _RS ((op>>21) & 0x1F)
|
||||
#define _RT ((op>>16) & 0x1F)
|
||||
#define _RD ((op>>11) & 0x1F)
|
||||
#define R(i) (curMips->r[i])
|
||||
|
||||
static inline void RunUntilFast() {
|
||||
MIPSState *curMips = currentMIPS;
|
||||
static inline void RunUntilFast(MIPSState *curMips) {
|
||||
// NEVER stop in a delay slot!
|
||||
while (curMips->downcount >= 0 && coreState == CORE_RUNNING_CPU) {
|
||||
do {
|
||||
@@ -980,9 +1125,17 @@ static inline void RunUntilFast() {
|
||||
MIPSOpcode op = MIPSOpcode(Memory::ReadUnchecked_U32(curMips->pc));
|
||||
|
||||
bool wasInDelaySlot = curMips->inDelaySlot;
|
||||
const MIPSInstruction *instr = MIPSGetInstruction(op);
|
||||
Interpret(instr, op);
|
||||
curMips->downcount -= GetInstructionCycleEstimate(instr);
|
||||
int cycles = ExecInstruction(op);
|
||||
if (cycles < 0) {
|
||||
// Not a recognized instruction (invalid encoding, or a known instruction
|
||||
// with no interpreter implementation, e.g. tge/tlt/teq/...). No point
|
||||
// calling MIPSGetInstruction() here - see HandleUnknownInstruction()'s
|
||||
// comment. Every such case has cycles == 1 today (same as
|
||||
// GetInstructionCycleEstimate()'s default for a null/flagless instr).
|
||||
HandleUnknownInstruction(curMips, op);
|
||||
cycles = 1;
|
||||
}
|
||||
curMips->downcount -= cycles;
|
||||
|
||||
// The reason we have to check this is the delay slot hack in Int_Syscall.
|
||||
if (curMips->inDelaySlot && wasInDelaySlot) {
|
||||
@@ -993,8 +1146,8 @@ static inline void RunUntilFast() {
|
||||
}
|
||||
}
|
||||
|
||||
static void RunUntilWithChecks(u64 globalTicks) {
|
||||
MIPSState *curMips = currentMIPS;
|
||||
#define _RS(op) ((op>>21) & 0x1F)
|
||||
static void RunUntilWithChecks(MIPSState *curMips, u64 globalTicks) {
|
||||
// NEVER stop in a delay slot!
|
||||
bool hasBPs = g_breakpoints.HasBreakPoints();
|
||||
bool hasMCs = g_breakpoints.HasMemChecks();
|
||||
@@ -1010,7 +1163,7 @@ static void RunUntilWithChecks(u64 globalTicks) {
|
||||
|
||||
// Check for breakpoint
|
||||
if (hasBPs && g_breakpoints.IsAddressBreakPoint(curMips->pc) && g_breakpoints.CheckSkipFirst() != curMips->pc) {
|
||||
auto cond = g_breakpoints.GetBreakPointCondition(currentMIPS->pc);
|
||||
auto cond = g_breakpoints.GetBreakPointCondition(curMips->pc);
|
||||
if (!cond || cond->Evaluate()) {
|
||||
Core_Break(BreakReason::CpuBreakpoint, curMips->pc);
|
||||
if (g_breakpoints.IsTempBreakPoint(curMips->pc))
|
||||
@@ -1021,7 +1174,7 @@ static void RunUntilWithChecks(u64 globalTicks) {
|
||||
if (hasMCs && (instr->flags & (IN_MEM | OUT_MEM)) != 0 && g_breakpoints.CheckSkipFirst() != curMips->pc && instr->interpret != &Int_Syscall) {
|
||||
// This is common for all IN_MEM/OUT_MEM funcs.
|
||||
int offset = (instr->flags & IS_VFPU) != 0 ? SignExtend16ToS32(op & 0xFFFC) : SignExtend16ToS32(op);
|
||||
u32 addr = (R(_RS) + offset) & 0xFFFFFFFC;
|
||||
u32 addr = (curMips->r[_RS(op)] + offset) & 0xFFFFFFFC;
|
||||
int sz = MIPSGetMemoryAccessSize(op);
|
||||
|
||||
if ((instr->flags & IN_MEM) != 0)
|
||||
@@ -1035,7 +1188,7 @@ static void RunUntilWithChecks(u64 globalTicks) {
|
||||
}
|
||||
|
||||
bool wasInDelaySlot = curMips->inDelaySlot;
|
||||
Interpret(instr, op);
|
||||
Interpret(curMips, instr, op);
|
||||
curMips->downcount -= GetInstructionCycleEstimate(instr);
|
||||
|
||||
// The reason we have to check this is the delay slot hack in Int_Syscall.
|
||||
@@ -1049,17 +1202,17 @@ static void RunUntilWithChecks(u64 globalTicks) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#undef _RS
|
||||
|
||||
int MIPSInterpret_RunUntil(u64 globalTicks) {
|
||||
MIPSState *curMips = currentMIPS;
|
||||
int MIPSInterpret_RunUntil(MIPSState *curMips, u64 globalTicks) {
|
||||
while (coreState == CORE_RUNNING_CPU) {
|
||||
CoreTiming::Advance();
|
||||
|
||||
uint64_t ticksLeft = globalTicks - CoreTiming::GetTicks();
|
||||
if (g_breakpoints.HasBreakPoints() || g_breakpoints.HasMemChecks() || ticksLeft <= curMips->downcount)
|
||||
RunUntilWithChecks(globalTicks);
|
||||
RunUntilWithChecks(curMips, globalTicks);
|
||||
else
|
||||
RunUntilFast();
|
||||
RunUntilFast(curMips);
|
||||
|
||||
if (CoreTiming::GetTicks() > globalTicks) {
|
||||
// DEBUG_LOG(Log::CPU, "Hit the max ticks, bailing 1 : %llu, %llu", globalTicks, CoreTiming::GetTicks());
|
||||
@@ -1070,28 +1223,26 @@ int MIPSInterpret_RunUntil(u64 globalTicks) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *MIPSGetName(MIPSOpcode op)
|
||||
{
|
||||
const char *MIPSGetName(MIPSOpcode op) {
|
||||
static const char * const noname = "unk";
|
||||
const MIPSInstruction *instr = MIPSGetInstruction(op);
|
||||
if (!instr)
|
||||
if (!instr) {
|
||||
return noname;
|
||||
else
|
||||
} else {
|
||||
return instr->name;
|
||||
}
|
||||
}
|
||||
|
||||
MIPSInfo MIPSGetInfo(MIPSOpcode op)
|
||||
{
|
||||
// int crunch = CRUNCH_MIPS_OP(op);
|
||||
MIPSInfo MIPSGetInfo(MIPSOpcode op) {
|
||||
const MIPSInstruction *instr = MIPSGetInstruction(op);
|
||||
if (instr)
|
||||
if (instr) {
|
||||
return instr->flags;
|
||||
else
|
||||
} else {
|
||||
return MIPSInfo(BAD_INSTRUCTION);
|
||||
}
|
||||
}
|
||||
|
||||
MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op)
|
||||
{
|
||||
MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op) {
|
||||
const MIPSInstruction *instr = MIPSGetInstruction(op);
|
||||
if (instr->interpret)
|
||||
return instr->interpret;
|
||||
@@ -1100,8 +1251,7 @@ MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op)
|
||||
}
|
||||
|
||||
// TODO: Do something that makes sense here.
|
||||
int MIPSGetInstructionCycleEstimate(MIPSOpcode op)
|
||||
{
|
||||
int MIPSGetInstructionCycleEstimate(MIPSOpcode op) {
|
||||
const MIPSInstruction *instr = MIPSGetInstruction(op);
|
||||
return GetInstructionCycleEstimate(instr);
|
||||
}
|
||||
|
||||
@@ -131,11 +131,16 @@ namespace MIPSComp {
|
||||
void MIPSCompileOp(MIPSOpcode op, MIPSComp::MIPSFrontendInterface *jit);
|
||||
void MIPSDisAsm(MIPSOpcode op, u32 pc, char *out, size_t outSize, bool tabsToSpaces = false);
|
||||
MIPSInfo MIPSGetInfo(MIPSOpcode op);
|
||||
void MIPSInterpret(MIPSOpcode op); //only for those rare ones
|
||||
int MIPSInterpret_RunUntil(u64 globalTicks);
|
||||
void MIPSInterpret(MIPSState *mips, MIPSOpcode op); //only for those rare ones
|
||||
int MIPSInterpret_RunUntil(MIPSState *mips, u64 globalTicks);
|
||||
MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op);
|
||||
|
||||
int MIPSGetInstructionCycleEstimate(MIPSOpcode op);
|
||||
int MIPSGetMemoryAccessSize(MIPSOpcode op);
|
||||
const char *MIPSGetName(MIPSOpcode op);
|
||||
std::string MIPSDisasmAt(u32 compilerPC);
|
||||
|
||||
// Generates the full contents of Core/MIPS/InterpreterDispatch.cpp: a fast switch-tree
|
||||
// int ExecInstruction(MIPSOpcode op) dispatcher, derived from the tables in MIPSTables.cpp.
|
||||
// See GenerateInterpreterDispatch()'s comment (in the .cpp) for details.
|
||||
std::string GenerateInterpreterDispatch();
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
#include "Core/MIPS/Interpreter.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/HLE/ReplaceTables.h"
|
||||
|
||||
@@ -129,7 +129,7 @@ void FakeJit::Compile(u32 em_address) {
|
||||
}
|
||||
|
||||
void FakeJit::RunLoopUntil(u64 globalticks) {
|
||||
MIPSInterpret_RunUntil(globalticks);
|
||||
MIPSInterpret_RunUntil(currentMIPS, globalticks);
|
||||
}
|
||||
|
||||
void FakeJit::DoJit(u32 em_address, JitBlock *b) {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSAnalyst.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
#include "Core/MIPS/Interpreter.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/HLE/ReplaceTables.h"
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Regenerates Core/MIPS/InterpreterDispatch.cpp (the ExecInstruction switch-tree
|
||||
# interpreter dispatcher) from the tables in Core/MIPS/MIPSTables.cpp.
|
||||
#
|
||||
# Run this after changing anything that affects those tables - new instructions,
|
||||
# retimed cycle counts, encoding changes, etc. The generated file is checked into
|
||||
# git like any other source file, not built on the fly, so it needs to be
|
||||
# regenerated and the diff committed whenever the tables change.
|
||||
#
|
||||
# This script only needs an already-built PPSSPPHeadless binary (see
|
||||
# docs/pspautotests.md for how to build one); it doesn't build anything itself.
|
||||
# After it finishes, just build normally - Core/MIPS/InterpreterDispatch.cpp is
|
||||
# already wired into every build system, so no further steps are needed.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# Same search list test.py uses to find a headless build.
|
||||
CANDIDATES = [
|
||||
"Windows/x64/Debug/PPSSPPHeadless.exe",
|
||||
"Windows/x64/Release/PPSSPPHeadless.exe",
|
||||
"Windows/Debug/PPSSPPHeadless.exe",
|
||||
"Windows/Release/PPSSPPHeadless.exe",
|
||||
"build/Debug/PPSSPPHeadless",
|
||||
"build/Release/PPSSPPHeadless",
|
||||
"build/RelWithDebInfo/PPSSPPHeadless",
|
||||
"build/MinSizeRel/PPSSPPHeadless",
|
||||
"build/PPSSPPHeadless",
|
||||
"./PPSSPPHeadless",
|
||||
"./PPSSPPHeadless.exe",
|
||||
]
|
||||
|
||||
OUT_PATH = os.path.join("Core", "MIPS", "InterpreterDispatch.cpp")
|
||||
|
||||
|
||||
def find_headless():
|
||||
for candidate in CANDIDATES:
|
||||
if os.path.isfile(candidate):
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
os.chdir(repo_root)
|
||||
|
||||
headless = find_headless()
|
||||
if headless is None:
|
||||
sys.stderr.write("error: couldn't find a built PPSSPPHeadless binary. Checked:\n")
|
||||
for candidate in CANDIDATES:
|
||||
sys.stderr.write(" %s\n" % candidate)
|
||||
sys.stderr.write("Build PPSSPPHeadless first (see docs/pspautotests.md), then re-run this script.\n")
|
||||
return 1
|
||||
|
||||
print("Using %s" % headless)
|
||||
|
||||
# subprocess/CreateProcess on Windows can fail to resolve a relative,
|
||||
# forward-slash path as the executable itself - use an absolute, normalized path.
|
||||
headless_abs = os.path.normpath(os.path.abspath(headless))
|
||||
|
||||
try:
|
||||
result = subprocess.run([headless_abs, "--generate-interpreter-dispatch"], capture_output=True)
|
||||
except OSError as e:
|
||||
sys.stderr.write("error: couldn't run %s: %s\n" % (headless, e))
|
||||
return 1
|
||||
|
||||
if result.returncode != 0:
|
||||
sys.stderr.buffer.write(result.stderr)
|
||||
sys.stderr.write("error: %s exited with code %d\n" % (headless, result.returncode))
|
||||
return 1
|
||||
|
||||
generated = result.stdout
|
||||
|
||||
# Sanity check before clobbering the tracked file - a stale/broken binary should
|
||||
# fail loudly here rather than silently truncating InterpreterDispatch.cpp.
|
||||
if b"int ExecInstruction(MIPSOpcode op)" not in generated:
|
||||
sys.stderr.write("error: generated output doesn't look right (missing ExecInstruction) - not overwriting %s\n" % OUT_PATH)
|
||||
return 1
|
||||
|
||||
with open(OUT_PATH, "wb") as f:
|
||||
f.write(generated)
|
||||
|
||||
print("Regenerated %s" % OUT_PATH)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -290,8 +290,9 @@
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSDebugInterface.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSDis.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSDisVFPU.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSInt.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSIntVFPU.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\Interpreter.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\InterpreterDispatch.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\InterpreterVFPU.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSStackWalk.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSTables.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSVFPUFallbacks.h" />
|
||||
@@ -586,8 +587,9 @@
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSDebugInterface.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSDis.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSDisVFPU.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSInt.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSIntVFPU.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\Interpreter.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\InterpreterDispatch.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\InterpreterVFPU.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSStackWalk.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSTables.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSVFPUFallbacks.cpp" />
|
||||
|
||||
@@ -225,8 +225,9 @@
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSDebugInterface.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSDis.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSDisVFPU.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSInt.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSIntVFPU.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\Interpreter.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\InterpreterDispatch.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\InterpreterVFPU.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSStackWalk.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSTables.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\MIPSVFPUFallbacks.cpp" />
|
||||
@@ -618,8 +619,9 @@
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSDebugInterface.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSDis.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSDisVFPU.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSInt.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSIntVFPU.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\Interpreter.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\InterpreterDispatch.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\InterpreterVFPU.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSStackWalk.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSTables.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\MIPSVFPUFallbacks.h" />
|
||||
|
||||
@@ -514,8 +514,9 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/Core/MIPS/MIPSDis.cpp \
|
||||
$(SRC)/Core/MIPS/MIPSDisVFPU.cpp \
|
||||
$(SRC)/Core/MIPS/MIPSAsm.cpp \
|
||||
$(SRC)/Core/MIPS/MIPSInt.cpp.arm \
|
||||
$(SRC)/Core/MIPS/MIPSIntVFPU.cpp.arm \
|
||||
$(SRC)/Core/MIPS/Interpreter.cpp.arm \
|
||||
$(SRC)/Core/MIPS/InterpreterDispatch.cpp \
|
||||
$(SRC)/Core/MIPS/InterpreterVFPU.cpp.arm \
|
||||
$(SRC)/Core/MIPS/MIPSStackWalk.cpp \
|
||||
$(SRC)/Core/MIPS/MIPSTables.cpp \
|
||||
$(SRC)/Core/MIPS/MIPSVFPUUtils.cpp.arm \
|
||||
|
||||
+38
-4
@@ -54,6 +54,7 @@
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/EmuThread.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/WebServer.h"
|
||||
#include "Core/HLE/sceUtility.h"
|
||||
@@ -521,6 +522,35 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Has a parameter because we might start using this from the main build.
|
||||
void SetupCRT(bool headless) {
|
||||
#if defined(_MSC_VER)
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
|
||||
if (headless) {
|
||||
// Suppress abort dialogs and similar.
|
||||
// 1. Redirect CRT Assertions/Errors/Warnings to stdout/stderr
|
||||
|
||||
const _HFILE reportTarget = _CRTDBG_FILE_STDERR;
|
||||
|
||||
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
|
||||
_CrtSetReportFile(_CRT_ASSERT, reportTarget);
|
||||
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
|
||||
_CrtSetReportFile(_CRT_ERROR, reportTarget);
|
||||
|
||||
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
|
||||
_CrtSetReportFile(_CRT_WARN, reportTarget);
|
||||
|
||||
// 2. Suppress the abort() message box & crash reporting dialogs
|
||||
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
|
||||
|
||||
// 3. Suppress Windows OS-level "Program has stopped working" modal dialogs
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
PROFILE_INIT();
|
||||
TimeInit();
|
||||
@@ -535,9 +565,7 @@ int main(int argc, const char* argv[]) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_DEBUG) && defined(_MSC_VER)
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
#endif
|
||||
SetupCRT(true);
|
||||
|
||||
CommandLineOptions cmdLineOptions;
|
||||
CommandLineParseResult parseResult = cmdLineOptions.Parse(argc, argv, CmdLineMode::Headless);
|
||||
@@ -550,6 +578,12 @@ int main(int argc, const char* argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmdLineOptions.generateInterpreterDispatch.value_or(false)) {
|
||||
std::string code = GenerateInterpreterDispatch();
|
||||
fwrite(code.data(), 1, code.size(), stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Needed before any sockets can be used (WSAStartup on Windows) - without this, the
|
||||
// WebSocket debugger silently fails to listen. Only done when requested since headless
|
||||
// otherwise has no use for networking.
|
||||
@@ -709,7 +743,7 @@ int main(int argc, const char* argv[]) {
|
||||
return 1;
|
||||
#else
|
||||
// TODO: Will we need a larger window for higher resolutions? Well, not if we use buffered rendering.
|
||||
window = CreateHiddenWindow(480, 272, cmdLineOptions.gpuBackend.value(), &windowDesc);
|
||||
window = CreateHiddenWindow(480, 272, cmdLineOptions.gpuBackend.value_or(GPUBackend::OPENGL), &windowDesc);
|
||||
if (!windowDesc.Valid()) {
|
||||
fprintf(stderr, "Failed to create a window for graphics context");
|
||||
return 1;
|
||||
|
||||
@@ -871,8 +871,9 @@ SOURCES_CXX += \
|
||||
$(COREDIR)/MIPS/MIPSDebugInterface.cpp \
|
||||
$(COREDIR)/MIPS/MIPSDis.cpp \
|
||||
$(COREDIR)/MIPS/MIPSDisVFPU.cpp \
|
||||
$(COREDIR)/MIPS/MIPSInt.cpp \
|
||||
$(COREDIR)/MIPS/MIPSIntVFPU.cpp \
|
||||
$(COREDIR)/MIPS/Interpreter.cpp \
|
||||
$(COREDIR)/MIPS/InterpreterDispatch.cpp \
|
||||
$(COREDIR)/MIPS/InterpreterVFPU.cpp \
|
||||
$(COREDIR)/MIPS/MIPSTables.cpp \
|
||||
$(COREDIR)/MIPS/MIPSStackWalk.cpp \
|
||||
$(COREDIR)/MIPS/MIPSVFPUUtils.cpp \
|
||||
|
||||
Reference in new issue
Block a user