mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-05-02 03:17:22 -05:00
This is the bare minimum required to run a few games on AArch64. Was able to run starfield and Animal Crossing to the Nintendo logo. QEmu emulation is literally the slowest thing in the world, it maxes out at around 12mhz on my Core i7-4930MX.
196 lines
4.1 KiB
C++
196 lines
4.1 KiB
C++
// Copyright 2013 Dolphin Emulator Project
|
|
// Licensed under GPLv2
|
|
// Refer to the license.txt file included.
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "Common/Common.h"
|
|
#include "Core/PowerPC/PPCTables.h"
|
|
|
|
class PPCSymbolDB;
|
|
struct Symbol;
|
|
|
|
namespace PPCAnalyst
|
|
{
|
|
|
|
struct CodeOp //16B
|
|
{
|
|
UGeckoInstruction inst;
|
|
GekkoOPInfo * opinfo;
|
|
u32 address;
|
|
u32 branchTo; //if 0, not a branch
|
|
int branchToIndex; //index of target block
|
|
s8 regsOut[2];
|
|
s8 regsIn[3];
|
|
s8 fregOut;
|
|
s8 fregsIn[3];
|
|
bool isBranchTarget;
|
|
bool wantsCR0;
|
|
bool wantsCR1;
|
|
bool wantsPS1;
|
|
bool wantsFPRF;
|
|
bool outputCR0;
|
|
bool outputCR1;
|
|
bool outputPS1;
|
|
bool outputFPRF;
|
|
bool canEndBlock;
|
|
bool skip; // followed BL-s for example
|
|
};
|
|
|
|
struct BlockStats
|
|
{
|
|
bool isFirstBlockOfFunction;
|
|
bool isLastBlockOfFunction;
|
|
int numCycles;
|
|
};
|
|
|
|
struct BlockRegStats
|
|
{
|
|
short firstRead[32];
|
|
short firstWrite[32];
|
|
short lastRead[32];
|
|
short lastWrite[32];
|
|
short numReads[32];
|
|
short numWrites[32];
|
|
|
|
bool any;
|
|
bool anyTimer;
|
|
|
|
int GetTotalNumAccesses(int reg) {return numReads[reg] + numWrites[reg];}
|
|
int GetUseRange(int reg)
|
|
{
|
|
return std::max(lastRead[reg], lastWrite[reg]) -
|
|
std::min(firstRead[reg], firstWrite[reg]);
|
|
}
|
|
|
|
bool IsUsed(int reg)
|
|
{
|
|
return (numReads[reg] + numWrites[reg]) > 0;
|
|
}
|
|
|
|
inline void SetInputRegister(int reg, short opindex)
|
|
{
|
|
if (firstRead[reg] == -1)
|
|
firstRead[reg] = (short)(opindex);
|
|
lastRead[reg] = (short)(opindex);
|
|
numReads[reg]++;
|
|
}
|
|
|
|
inline void SetOutputRegister(int reg, short opindex)
|
|
{
|
|
if (firstWrite[reg] == -1)
|
|
firstWrite[reg] = (short)(opindex);
|
|
lastWrite[reg] = (short)(opindex);
|
|
numWrites[reg]++;
|
|
}
|
|
|
|
inline void Clear()
|
|
{
|
|
for (int i = 0; i < 32; ++i)
|
|
{
|
|
firstRead[i] = -1;
|
|
firstWrite[i] = -1;
|
|
numReads[i] = 0;
|
|
numWrites[i] = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class CodeBuffer
|
|
{
|
|
int size_;
|
|
public:
|
|
CodeBuffer(int size);
|
|
~CodeBuffer();
|
|
|
|
int GetSize() const { return size_; }
|
|
|
|
PPCAnalyst::CodeOp *codebuffer;
|
|
|
|
|
|
};
|
|
|
|
struct CodeBlock
|
|
{
|
|
// Beginning PPC address.
|
|
u32 m_address;
|
|
|
|
// Number of instructions
|
|
// Gives us the size of the block.
|
|
u32 m_num_instructions;
|
|
|
|
// Some basic statistics about the block.
|
|
BlockStats *m_stats;
|
|
|
|
// Register statistics about the block.
|
|
BlockRegStats *m_gpa, *m_fpa;
|
|
|
|
// Are we a broken block?
|
|
bool m_broken;
|
|
|
|
// Did we have a memory_exception?
|
|
bool m_memory_exception;
|
|
};
|
|
|
|
class PPCAnalyzer
|
|
{
|
|
private:
|
|
|
|
void ReorderInstructions(u32 instructions, CodeOp *code);
|
|
void SetInstructionStats(CodeBlock *block, CodeOp *code, GekkoOPInfo *opinfo, u32 index);
|
|
|
|
// Options
|
|
u32 m_options;
|
|
public:
|
|
|
|
enum AnalystOption
|
|
{
|
|
// Conditional branch continuing
|
|
// If the JIT core supports conditional branches within the blocks
|
|
// Block will end on unconditional branch or other ENDBLOCK flagged instruction.
|
|
// Requires JIT support to be enabled.
|
|
OPTION_CONDITIONAL_CONTINUE = (1 << 0),
|
|
|
|
// If there is a unconditional branch that jumps to a leaf function then inline it.
|
|
// Might require JIT intervention to support it correctly.
|
|
// Requires JITBLock support for inlined code
|
|
// XXX: NOT COMPLETE
|
|
OPTION_LEAF_INLINE = (1 << 1),
|
|
|
|
// Complex blocks support jumping backwards on to themselves.
|
|
// Happens commonly in loops, pretty complex to support.
|
|
// May require register caches to use register usage metrics.
|
|
// XXX: NOT COMPLETE
|
|
OPTION_COMPLEX_BLOCK = (1 << 2),
|
|
|
|
// Similar to complex blocks.
|
|
// Instead of jumping backwards, this jumps forwards within the block.
|
|
// Requires JIT support to work.
|
|
// XXX: NOT COMPLETE
|
|
OPTION_FORWARD_JUMP = (1 << 3),
|
|
};
|
|
|
|
|
|
PPCAnalyzer() : m_options(0) {}
|
|
|
|
// Option setting/getting
|
|
void SetOption(AnalystOption option) { m_options |= option; }
|
|
void ClearOption(AnalystOption option) { m_options &= ~(option); }
|
|
bool HasOption(AnalystOption option) { return !!(m_options & option); }
|
|
|
|
u32 Analyze(u32 address, CodeBlock *block, CodeBuffer *buffer, u32 blockSize);
|
|
};
|
|
|
|
void LogFunctionCall(u32 addr);
|
|
void FindFunctions(u32 startAddr, u32 endAddr, PPCSymbolDB *func_db);
|
|
bool AnalyzeFunction(u32 startAddr, Symbol &func, int max_size = 0);
|
|
|
|
} // namespace
|