Kill lots of old outdated comments. Some new comments added. Misc style fixes. No effect on emulation.
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@894 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
3ae2d556ab
commit
21b0d596e4
|
@ -15,36 +15,60 @@
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
|
|
||||||
|
// Simple partial Action Replay code system implementation.
|
||||||
|
|
||||||
|
// Will never be able to support some AR codes - specifically those that patch the running
|
||||||
|
// Action Replay engine itself - yes they do exist!!!
|
||||||
|
|
||||||
|
// Action Replay actually is a small virtual machine with a limited number of commands.
|
||||||
|
// It probably is Turing complete - but what does that matter when AR codes can write
|
||||||
|
// actual PowerPC code.
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "StringUtil.h"
|
#include "StringUtil.h"
|
||||||
#include "IniFile.h"
|
#include "IniFile.h"
|
||||||
#include "HW/Memmap.h"
|
#include "HW/Memmap.h"
|
||||||
#include "ActionReplay.h"
|
#include "ActionReplay.h"
|
||||||
|
|
||||||
u32 cmd_addr;
|
namespace {
|
||||||
u8 cmd;
|
|
||||||
u32 addr;
|
|
||||||
u32 data;
|
|
||||||
u8 subtype;
|
|
||||||
u8 w;
|
|
||||||
u8 type;
|
|
||||||
std::vector<AREntry>::const_iterator iter;
|
|
||||||
std::vector<ARCode> arCodes;
|
|
||||||
ARCode code;
|
|
||||||
|
|
||||||
|
// These should be turned into locals in RunActionReplayCode, and passed as parameters to the others.
|
||||||
|
static u32 cmd_addr;
|
||||||
|
static u8 cmd;
|
||||||
|
static u32 addr;
|
||||||
|
static u32 data;
|
||||||
|
static u8 subtype;
|
||||||
|
static u8 w;
|
||||||
|
static u8 type;
|
||||||
|
static std::vector<AREntry>::const_iterator iter;
|
||||||
|
|
||||||
|
static std::vector<ARCode> arCodes;
|
||||||
|
static ARCode code;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void DoARSubtype_RamWriteAndFill();
|
||||||
|
void DoARSubtype_WriteToPointer();
|
||||||
|
void DoARSubtype_AddCode();
|
||||||
|
void DoARSubtype_MasterCodeAndWriteToCCXXXXXX();
|
||||||
|
void DoARSubtype_Other();
|
||||||
|
|
||||||
|
// Parses the Action Replay section of a game ini file.
|
||||||
void LoadActionReplayCodes(IniFile &ini)
|
void LoadActionReplayCodes(IniFile &ini)
|
||||||
{
|
{
|
||||||
std::vector<std::string> lines;
|
std::vector<std::string> lines;
|
||||||
ARCode currentCode;
|
ARCode currentCode;
|
||||||
arCodes.clear();
|
arCodes.clear();
|
||||||
|
|
||||||
if (!ini.GetLines("ActionReplay", lines)) return;
|
if (!ini.GetLines("ActionReplay", lines))
|
||||||
|
return; // no codes found.
|
||||||
|
|
||||||
for (std::vector<std::string>::const_iterator it = lines.begin(); it != lines.end(); ++it)
|
for (std::vector<std::string>::const_iterator it = lines.begin(); it != lines.end(); ++it)
|
||||||
{
|
{
|
||||||
std::string line = *it;
|
std::string line = *it;
|
||||||
|
|
||||||
std::vector<std::string> pieces;
|
std::vector<std::string> pieces;
|
||||||
SplitString(line, " ", pieces);
|
SplitString(line, " ", pieces);
|
||||||
if (pieces.size() == 2 && pieces[0].size() == 8 && pieces[1].size() == 8)
|
if (pieces.size() == 2 && pieces[0].size() == 8 && pieces[1].size() == 8)
|
||||||
|
@ -96,6 +120,12 @@ void LoadActionReplayCodes(IniFile &ini)
|
||||||
arCodes.push_back(currentCode);
|
arCodes.push_back(currentCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActionReplayRunAllActive()
|
||||||
|
{
|
||||||
|
for (std::vector<ARCode>::const_iterator iter = arCodes.begin(); iter != arCodes.end(); ++iter)
|
||||||
|
if (iter->active)
|
||||||
|
RunActionReplayCode(*iter, false);
|
||||||
|
}
|
||||||
|
|
||||||
// The mechanism is slightly different than what the real AR uses, so there may be compatibility problems.
|
// The mechanism is slightly different than what the real AR uses, so there may be compatibility problems.
|
||||||
// For example, some authors have created codes that add features to AR. Hacks for popular ones can be added here,
|
// For example, some authors have created codes that add features to AR. Hacks for popular ones can be added here,
|
||||||
|
@ -143,121 +173,114 @@ void RunActionReplayCode(const ARCode &arcode, bool nowIsBootup) {
|
||||||
if (iter == code.ops.begin() && cmd == 1) continue;
|
if (iter == code.ops.begin() && cmd == 1) continue;
|
||||||
|
|
||||||
// SubType selector
|
// SubType selector
|
||||||
switch(subtype)
|
switch (subtype)
|
||||||
{
|
{
|
||||||
case 0x0: // Ram write (and fill)
|
case 0x0: // Ram write (and fill)
|
||||||
{
|
DoARSubtype_RamWriteAndFill();
|
||||||
DoARSubtype_RamWriteAndFill(); continue;
|
continue;
|
||||||
}
|
case 0x1: // Write to pointer
|
||||||
case 0x1: // Write to pointer
|
DoARSubtype_WriteToPointer();
|
||||||
{
|
continue;
|
||||||
DoARSubtype_WriteToPointer(); continue;
|
case 0x2: // Add code
|
||||||
}
|
DoARSubtype_AddCode();
|
||||||
case 0x2: // Add code
|
continue;
|
||||||
{
|
case 0x3: // Master Code & Write to CCXXXXXX
|
||||||
DoARSubtype_AddCode(); continue;
|
DoARSubtype_MasterCodeAndWriteToCCXXXXXX();
|
||||||
}
|
continue; // TODO: This is not implemented yet
|
||||||
case 0x3: // Master Code & Write to CCXXXXXX
|
default: // non-specific z codes (hacks)
|
||||||
{
|
DoARSubtype_Other();
|
||||||
DoARSubtype_MasterCodeAndWriteToCCXXXXXX(); continue;// TODO: This is not implemented yet
|
continue;
|
||||||
}
|
|
||||||
default: // non-specific z codes (hacks)
|
|
||||||
{
|
|
||||||
DoARSubtype_Other(); continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoARSubtype_RamWriteAndFill()
|
void DoARSubtype_RamWriteAndFill()
|
||||||
{
|
{
|
||||||
if(w < 0x8) // Check the value W in 0xZWXXXXXXX
|
if (w < 0x8) // Check the value W in 0xZWXXXXXXX
|
||||||
{
|
{
|
||||||
u32 new_addr = ( (addr & 0x01FFFFFF) | 0x80000000);
|
u32 new_addr = ( (addr & 0x01FFFFFF) | 0x80000000);
|
||||||
switch ((addr >> 25) & 0x03)
|
switch ((addr >> 25) & 0x03)
|
||||||
{
|
{
|
||||||
case 0x00: // Byte write
|
case 0x00: // Byte write
|
||||||
{
|
{
|
||||||
u8 repeat = data >> 8;
|
u8 repeat = data >> 8;
|
||||||
for (int i = 0; i <= repeat; i++) {
|
for (int i = 0; i <= repeat; i++) {
|
||||||
Memory::Write_U8(data & 0xFF, new_addr + i);
|
Memory::Write_U8(data & 0xFF, new_addr + i);
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x01: // Short write
|
case 0x01: // Short write
|
||||||
{
|
{
|
||||||
u16 repeat = data >> 16;
|
u16 repeat = data >> 16;
|
||||||
for (int i = 0; i <= repeat; i++) {
|
for (int i = 0; i <= repeat; i++) {
|
||||||
Memory::Write_U16(data & 0xFFFF, new_addr + i * 2);
|
Memory::Write_U16(data & 0xFFFF, new_addr + i * 2);
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x02: // Dword write
|
||||||
case 0x02: // Dword write
|
Memory::Write_U32(data, new_addr);
|
||||||
{
|
break;
|
||||||
Memory::Write_U32(data, new_addr);
|
default:
|
||||||
break;
|
break; // TODO(Omega): maybe add a PanicAlert here?
|
||||||
}
|
|
||||||
default: break; // TODO(Omega): maybe add a PanicAlert here?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DoARSubtype_WriteToPointer()
|
void DoARSubtype_WriteToPointer()
|
||||||
{
|
{
|
||||||
if(w < 0x8)
|
if (w < 0x8)
|
||||||
{
|
{
|
||||||
u32 new_addr = ( addr | 0x80000000);
|
u32 new_addr = ( addr | 0x80000000);
|
||||||
switch ((addr >> 25) & 0x03)
|
switch ((addr >> 25) & 0x03)
|
||||||
{
|
{
|
||||||
case 0x00: // Byte write to pointer [40]
|
case 0x00: // Byte write to pointer [40]
|
||||||
{
|
{
|
||||||
u32 ptr = Memory::Read_U32(new_addr);
|
u32 ptr = Memory::Read_U32(new_addr);
|
||||||
u8 thebyte = data & 0xFF;
|
u8 thebyte = data & 0xFF;
|
||||||
u32 offset = data >> 8;
|
u32 offset = data >> 8;
|
||||||
Memory::Write_U8(thebyte, ptr + offset);
|
Memory::Write_U8(thebyte, ptr + offset);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x01: // Short write to pointer [42]
|
case 0x01: // Short write to pointer [42]
|
||||||
{
|
{
|
||||||
u32 ptr = Memory::Read_U32(new_addr);
|
u32 ptr = Memory::Read_U32(new_addr);
|
||||||
u16 theshort = data & 0xFFFF;
|
u16 theshort = data & 0xFFFF;
|
||||||
u32 offset = (data >> 16) << 1;
|
u32 offset = (data >> 16) << 1;
|
||||||
Memory::Write_U16(theshort, ptr + offset);
|
Memory::Write_U16(theshort, ptr + offset);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x02: // Dword write to pointer [44]
|
case 0x02: // Dword write to pointer [44]
|
||||||
{
|
Memory::Write_U32(data, Memory::Read_U32(new_addr));
|
||||||
Memory::Write_U32(data, Memory::Read_U32(new_addr)); break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
default: PanicAlert("AR Method Error (Write To Pointer): w = %08x, addr = %08x",w,addr);
|
default:
|
||||||
|
PanicAlert("AR Method Error (Write To Pointer): w = %08x, addr = %08x", w, addr);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoARSubtype_AddCode()
|
void DoARSubtype_AddCode()
|
||||||
{
|
{
|
||||||
if(w < 0x8)
|
if (w < 0x8)
|
||||||
{
|
{
|
||||||
u32 new_addr = ( (addr & 0x01FFFFFF) | 0x81FFFFFF);
|
u32 new_addr = ( (addr & 0x01FFFFFF) | 0x81FFFFFF);
|
||||||
switch((addr >> 25) & 0x03)
|
switch ((addr >> 25) & 0x03)
|
||||||
{
|
{
|
||||||
case 0x0: // Byte add
|
case 0x0: // Byte add
|
||||||
{
|
Memory::Write_U8(Memory::Read_U8(new_addr) + (data & 0xFF), new_addr);
|
||||||
Memory::Write_U8(Memory::Read_U8(new_addr) + (data & 0xFF), new_addr); break;
|
break;
|
||||||
}
|
|
||||||
case 0x1: // Short add
|
case 0x1: // Short add
|
||||||
{
|
Memory::Write_U16(Memory::Read_U16(new_addr) + (data & 0xFFFF), new_addr);
|
||||||
Memory::Write_U16(Memory::Read_U16(new_addr) + (data & 0xFFFF), new_addr); break;
|
break;
|
||||||
}
|
|
||||||
case 0x2: // DWord add
|
case 0x2: // DWord add
|
||||||
{
|
Memory::Write_U32(Memory::Read_U32(new_addr) + data, new_addr);
|
||||||
Memory::Write_U32(Memory::Read_U32(new_addr) + data, new_addr); break;
|
break;
|
||||||
}
|
|
||||||
case 0x3: // Float add (not working?)
|
case 0x3: // Float add (not working?)
|
||||||
{
|
{
|
||||||
union { u32 u; float f;} fu, d;
|
union { u32 u; float f;} fu, d;
|
||||||
|
@ -267,7 +290,8 @@ void DoARSubtype_AddCode()
|
||||||
Memory::Write_U32(fu.u, new_addr);
|
Memory::Write_U32(fu.u, new_addr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: break;
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,10 +300,10 @@ void DoARSubtype_MasterCodeAndWriteToCCXXXXXX()
|
||||||
{
|
{
|
||||||
// code not yet implemented - TODO
|
// code not yet implemented - TODO
|
||||||
|
|
||||||
//if(w < 0x8)
|
//if (w < 0x8)
|
||||||
//{
|
//{
|
||||||
// u32 new_addr = (addr | 0x80000000);
|
// u32 new_addr = (addr | 0x80000000);
|
||||||
// switch((new_addr >> 25) & 0x03)
|
// switch ((new_addr >> 25) & 0x03)
|
||||||
// {
|
// {
|
||||||
// case 0x2:
|
// case 0x2:
|
||||||
// {
|
// {
|
||||||
|
@ -291,76 +315,68 @@ void DoARSubtype_MasterCodeAndWriteToCCXXXXXX()
|
||||||
|
|
||||||
void DoARSubtype_Other()
|
void DoARSubtype_Other()
|
||||||
{
|
{
|
||||||
switch (cmd & 0xFE)
|
switch (cmd & 0xFE)
|
||||||
{
|
{
|
||||||
case 0x90: if (Memory::Read_U32(addr) == data) return; // IF 32 bit equal, exit
|
case 0x90:
|
||||||
case 0x08: // IF 8 bit equal, execute next opcode
|
// Eh, this must be wrong. Should it really fallthrough?
|
||||||
case 0x48: // (double)
|
if (Memory::Read_U32(addr) == data) return; // IF 32 bit equal, exit
|
||||||
{
|
case 0x08: // IF 8 bit equal, execute next opcode
|
||||||
if (Memory::Read_U16(addr) != (data & 0xFFFF)) {
|
case 0x48: // (double)
|
||||||
if (++iter == code.ops.end()) return;
|
if (Memory::Read_U16(addr) != (data & 0xFFFF)) {
|
||||||
if (cmd == 0x48) if (++iter == code.ops.end()) return;
|
if (++iter == code.ops.end()) return;
|
||||||
}
|
if (cmd == 0x48) if (++iter == code.ops.end()) return;
|
||||||
break;
|
}
|
||||||
}
|
break;
|
||||||
case 0x0A: // IF 16 bit equal, execute next opcode
|
case 0x0A: // IF 16 bit equal, execute next opcode
|
||||||
case 0x4A: // (double)
|
case 0x4A: // (double)
|
||||||
{
|
if (Memory::Read_U16(addr) != (data & 0xFFFF)) {
|
||||||
if (Memory::Read_U16(addr) != (data & 0xFFFF)) {
|
if (++iter == code.ops.end()) return;
|
||||||
if (++iter == code.ops.end()) return;
|
if (cmd == 0x4A) if (++iter == code.ops.end()) return;
|
||||||
if (cmd == 0x4A) if (++iter == code.ops.end()) return;
|
}
|
||||||
}
|
break;
|
||||||
break;
|
case 0x0C: // IF 32 bit equal, execute next opcode
|
||||||
}
|
case 0x4C: // (double)
|
||||||
case 0x0C: // IF 32 bit equal, execute next opcode
|
if (Memory::Read_U32(addr) != data) {
|
||||||
case 0x4C: // (double)
|
if (++iter == code.ops.end()) return;
|
||||||
{
|
if (cmd == 0x4C) if (++iter == code.ops.end()) return;
|
||||||
if (Memory::Read_U32(addr) != data) {
|
}
|
||||||
if (++iter == code.ops.end()) return;
|
break;
|
||||||
if (cmd == 0x4C) if (++iter == code.ops.end()) return;
|
case 0x10: // IF NOT 8 bit equal, execute next opcode
|
||||||
}
|
case 0x50: // (double)
|
||||||
break;
|
if (Memory::Read_U8(addr) == (data & 0xFF)) {
|
||||||
}
|
if (++iter == code.ops.end()) return;
|
||||||
case 0x10: // IF NOT 8 bit equal, execute next opcode
|
if (cmd == 0x50) if (++iter == code.ops.end()) return;
|
||||||
case 0x50: // (double)
|
}
|
||||||
{
|
break;
|
||||||
if (Memory::Read_U8(addr) == (data & 0xFF)) {
|
case 0x12: // IF NOT 16 bit equal, execute next opcode
|
||||||
if (++iter == code.ops.end()) return;
|
case 0x52: // (double)
|
||||||
if (cmd == 0x50) if (++iter == code.ops.end()) return;
|
if (Memory::Read_U16(addr) == (data & 0xFFFF)) {
|
||||||
}
|
if (++iter == code.ops.end()) return;
|
||||||
break;
|
if (cmd == 0x52) if (++iter == code.ops.end()) return;
|
||||||
}
|
}
|
||||||
case 0x12: // IF NOT 16 bit equal, execute next opcode
|
break;
|
||||||
case 0x52: // (double)
|
case 0x14: // IF NOT 32 bit equal, execute next opcode
|
||||||
{
|
case 0x54: // (double)
|
||||||
if (Memory::Read_U16(addr) == (data & 0xFFFF)) {
|
if (Memory::Read_U32(addr) == data) {
|
||||||
if (++iter == code.ops.end()) return;
|
if (++iter == code.ops.end()) return;
|
||||||
if (cmd == 0x52) if (++iter == code.ops.end()) return;
|
if (cmd == 0x54) if (++iter == code.ops.end()) return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
case 0xC4: // "Master Code" - configure the AR
|
||||||
case 0x14: // IF NOT 32 bit equal, execute next opcode
|
{
|
||||||
case 0x54: // (double)
|
u8 number = data & 0xFF;
|
||||||
{
|
if (number == 0)
|
||||||
if (Memory::Read_U32(addr) == data) {
|
{
|
||||||
if (++iter == code.ops.end()) return;
|
// Normal master code - execute once.
|
||||||
if (cmd == 0x54) if (++iter == code.ops.end()) return;
|
} else {
|
||||||
}
|
// PanicAlert("Not supporting multiple master codes.");
|
||||||
break;
|
}
|
||||||
}
|
// u8 numOpsPerFrame = (data >> 8) & 0xFF;
|
||||||
case 0xC4: // "Master Code" - configure the AR
|
// Blah, we generally ignore master codes.
|
||||||
{
|
break;
|
||||||
u8 number = data & 0xFF;
|
}
|
||||||
if (number == 0)
|
default:
|
||||||
{
|
PanicAlert("Unknown Action Replay command %02x (%08x %08x)", cmd, iter->cmd_addr, iter->value);
|
||||||
// Normal master code - execute once.
|
break;
|
||||||
} else {
|
}
|
||||||
// PanicAlert("Not supporting multiple master codes.");
|
|
||||||
}
|
|
||||||
// u8 numOpsPerFrame = (data >> 8) & 0xFF;
|
|
||||||
// Blah, we generally ignore master codes.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: PanicAlert("Unknown Action Replay command %02x (%08x %08x)", cmd, iter->cmd_addr, iter->value); break;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -26,13 +26,7 @@ struct ARCode {
|
||||||
bool active;
|
bool active;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::vector<ARCode> arCodes;
|
void ActionReplayRunAllActive();
|
||||||
|
|
||||||
void RunActionReplayCode(const ARCode &arcode, bool nowIsBootup);
|
void RunActionReplayCode(const ARCode &arcode, bool nowIsBootup);
|
||||||
void LoadActionReplayCodes(IniFile &ini);
|
void LoadActionReplayCodes(IniFile &ini);
|
||||||
void DoARSubtype_RamWriteAndFill();
|
|
||||||
void DoARSubtype_WriteToPointer();
|
|
||||||
void DoARSubtype_AddCode();
|
|
||||||
void DoARSubtype_MasterCodeAndWriteToCCXXXXXX();
|
|
||||||
void DoARSubtype_Other();
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
@ -22,32 +23,16 @@
|
||||||
#include "HW/Memmap.h"
|
#include "HW/Memmap.h"
|
||||||
#include "PowerPC/PPCAnalyst.h"
|
#include "PowerPC/PPCAnalyst.h"
|
||||||
#include "PowerPC/PPCTables.h"
|
#include "PowerPC/PPCTables.h"
|
||||||
#include "Console.h"
|
|
||||||
#include "CoreTiming.h"
|
#include "CoreTiming.h"
|
||||||
#include "Core.h"
|
#include "Core.h"
|
||||||
#include "PowerPC/Jit64/JitCache.h"
|
#include "PowerPC/Jit64/JitCache.h"
|
||||||
#include "PowerPC/SymbolDB.h"
|
#include "PowerPC/SymbolDB.h"
|
||||||
#include "PowerPCDisasm.h"
|
#include "PowerPCDisasm.h"
|
||||||
|
#include "Console.h"
|
||||||
|
|
||||||
#define CASE(x) else if (memcmp(cmd, x, 4*sizeof(TCHAR))==0)
|
#define CASE(x) else if (memcmp(cmd, x, 4*sizeof(TCHAR))==0)
|
||||||
#define CASE1(x) if (memcmp(cmd, x, 2*sizeof(TCHAR))==0)
|
#define CASE1(x) if (memcmp(cmd, x, 2*sizeof(TCHAR))==0)
|
||||||
|
|
||||||
/*
|
|
||||||
static Common::Thread *cons_thread;
|
|
||||||
|
|
||||||
THREAD_RETURN ConsoleThreadFunc(void *) {
|
|
||||||
printf("Welcome to the console thread!\n\n");
|
|
||||||
while (true) {
|
|
||||||
std::string command;
|
|
||||||
getline(std::cin, command);
|
|
||||||
Console_Submit(command.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StartConsoleThread() {
|
|
||||||
cons_thread = new Common::Thread(ConsoleThreadFunc, 0);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
void Console_Submit(const char *cmd)
|
void Console_Submit(const char *cmd)
|
||||||
{
|
{
|
||||||
CASE1("jits")
|
CASE1("jits")
|
||||||
|
|
|
@ -14,11 +14,14 @@
|
||||||
|
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
|
|
||||||
|
// Simple debugging console currently residing in the Logging window. Not used much.
|
||||||
|
|
||||||
#ifndef _CONSOLE_H
|
#ifndef _CONSOLE_H
|
||||||
#define _CONSOLE_H
|
#define _CONSOLE_H
|
||||||
|
|
||||||
void Console_Submit(const char *cmd);
|
void Console_Submit(const char *cmd);
|
||||||
// void StartConsoleThread();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,14 @@
|
||||||
|
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
|
|
||||||
|
// Core
|
||||||
|
|
||||||
|
// The external interface to the emulator core. Plus some extras.
|
||||||
|
// This is another part of the emu that needs cleaning - Core.cpp really has
|
||||||
|
// too much random junk inside.
|
||||||
|
|
||||||
#ifndef _CORE_H
|
#ifndef _CORE_H
|
||||||
#define _CORE_H
|
#define _CORE_H
|
||||||
|
|
||||||
|
@ -36,10 +44,7 @@ namespace Core
|
||||||
bool Init(const SCoreStartupParameter _CoreParameter);
|
bool Init(const SCoreStartupParameter _CoreParameter);
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
// Get state
|
|
||||||
bool SetState(EState _State);
|
bool SetState(EState _State);
|
||||||
|
|
||||||
// Get state
|
|
||||||
EState GetState();
|
EState GetState();
|
||||||
|
|
||||||
// Save/Load state
|
// Save/Load state
|
||||||
|
@ -64,8 +69,7 @@ namespace Core
|
||||||
int SyncTrace();
|
int SyncTrace();
|
||||||
void SetBlockStart(u32 addr);
|
void SetBlockStart(u32 addr);
|
||||||
void StopTrace();
|
void StopTrace();
|
||||||
|
} // namespace
|
||||||
} // end of namespace Core
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
#ifndef _COREPARAMETER_H
|
#ifndef _COREPARAMETER_H
|
||||||
#define _COREPARAMETER_H
|
#define _COREPARAMETER_H
|
||||||
|
|
||||||
|
|
|
@ -293,16 +293,13 @@ void Advance()
|
||||||
externalEventSection.Enter();
|
externalEventSection.Enter();
|
||||||
while (tsFirst)
|
while (tsFirst)
|
||||||
{
|
{
|
||||||
//MessageBox(0,"yay",0,0);
|
|
||||||
Event *next = tsFirst->next;
|
Event *next = tsFirst->next;
|
||||||
AddEventToQueue(tsFirst);
|
AddEventToQueue(tsFirst);
|
||||||
tsFirst = next;
|
tsFirst = next;
|
||||||
}
|
}
|
||||||
externalEventSection.Leave();
|
externalEventSection.Leave();
|
||||||
|
|
||||||
// we are out of run, downcount = -3
|
|
||||||
int cyclesExecuted = slicelength - downcount;
|
int cyclesExecuted = slicelength - downcount;
|
||||||
// sliceLength = downac
|
|
||||||
|
|
||||||
globalTimer += cyclesExecuted;
|
globalTimer += cyclesExecuted;
|
||||||
|
|
||||||
|
@ -312,7 +309,6 @@ void Advance()
|
||||||
{
|
{
|
||||||
// LOG(GEKKO, "[Scheduler] %s (%lld, %lld) ",
|
// LOG(GEKKO, "[Scheduler] %s (%lld, %lld) ",
|
||||||
// first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
|
// first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
|
||||||
|
|
||||||
event_types[first->type].callback(first->userdata, (int)(globalTimer - first->time));
|
event_types[first->type].callback(first->userdata, (int)(globalTimer - first->time));
|
||||||
Event *next = first->next;
|
Event *next = first->next;
|
||||||
delete first;
|
delete first;
|
||||||
|
@ -326,7 +322,6 @@ void Advance()
|
||||||
if (!first)
|
if (!first)
|
||||||
{
|
{
|
||||||
LOG(GEKKO, "WARNING - no events in queue. Setting downcount to 10000");
|
LOG(GEKKO, "WARNING - no events in queue. Setting downcount to 10000");
|
||||||
// PanicAlert?
|
|
||||||
downcount += 10000;
|
downcount += 10000;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -379,4 +374,4 @@ std::string GetScheduledEventsSummary()
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
}; // end of namespace
|
} // namespace
|
||||||
|
|
|
@ -24,6 +24,13 @@
|
||||||
// To schedule an event, you first have to register its type. This is where you pass in the
|
// To schedule an event, you first have to register its type. This is where you pass in the
|
||||||
// callback. You then schedule events using the type id you get back.
|
// callback. You then schedule events using the type id you get back.
|
||||||
|
|
||||||
|
// See HW/SystemTimers.cpp for the main part of Dolphin's usage of this scheduler.
|
||||||
|
|
||||||
|
// The int cyclesLate that the callbacks get is how many cycles late it was.
|
||||||
|
// So to schedule a new event on a regular basis:
|
||||||
|
// inside callback:
|
||||||
|
// ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -33,7 +40,6 @@
|
||||||
namespace CoreTiming
|
namespace CoreTiming
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
|
@ -43,10 +49,6 @@ u64 GetTicks();
|
||||||
u64 GetIdleTicks();
|
u64 GetIdleTicks();
|
||||||
|
|
||||||
void DoState(PointerWrap &p);
|
void DoState(PointerWrap &p);
|
||||||
// The int that the callbacks get is how many cycles late it was.
|
|
||||||
// So to schedule a new event on a regular basis:
|
|
||||||
// inside callback:
|
|
||||||
// ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
|
|
||||||
|
|
||||||
// Returns the event_type identifier.
|
// Returns the event_type identifier.
|
||||||
int RegisterEvent(const char *name, TimedCallback callback);
|
int RegisterEvent(const char *name, TimedCallback callback);
|
||||||
|
|
|
@ -17,10 +17,9 @@
|
||||||
|
|
||||||
// AID / AUDIO_DMA controls pushing audio out to the SRC and then the speakers.
|
// AID / AUDIO_DMA controls pushing audio out to the SRC and then the speakers.
|
||||||
// The audio DMA pushes audio through a small FIFO 32 bytes at a time, as needed.
|
// The audio DMA pushes audio through a small FIFO 32 bytes at a time, as needed.
|
||||||
// Since the SRC behind the fifo eats stereo 16-bit data at a sample rate of 32khz,
|
// The SRC behind the fifo eats stereo 16-bit data at a sample rate of 32khz,
|
||||||
// that is, 4 bytes at 32 khz, which is 32 bytes at 4 khz. We should thus schedule an
|
// that is, 4 bytes at 32 khz, which is 32 bytes at 4 khz. We thereforce schedule an
|
||||||
// event that runs at 4khz, that eats audio from the fifo, and all the rest will follow.
|
// event that runs at 4khz, that eats audio from the fifo. Thus, we have homebrew audio.
|
||||||
// Then we will have homebrew audio.
|
|
||||||
|
|
||||||
// The AID interrupt is set when the fifo STARTS a transfer. It latches address and count
|
// The AID interrupt is set when the fifo STARTS a transfer. It latches address and count
|
||||||
// into internal registers and starts copying. This means that the interrupt handler can simply
|
// into internal registers and starts copying. This means that the interrupt handler can simply
|
||||||
|
|
|
@ -156,6 +156,16 @@ void InitHWMemFuncs()
|
||||||
hwRead16 [i] = HW_Default_Read<u16&>;
|
hwRead16 [i] = HW_Default_Read<u16&>;
|
||||||
hwRead32 [i] = HW_Default_Read<u32&>;
|
hwRead32 [i] = HW_Default_Read<u32&>;
|
||||||
hwRead64 [i] = HW_Default_Read<u64&>;
|
hwRead64 [i] = HW_Default_Read<u64&>;
|
||||||
|
|
||||||
|
// To prevent Dolphin from crashing when running Wii executables in Gc mode.
|
||||||
|
hwWriteWii8 [i] = HW_Default_Write<u8>;
|
||||||
|
hwWriteWii16[i] = HW_Default_Write<u16>;
|
||||||
|
hwWriteWii32[i] = HW_Default_Write<u32>;
|
||||||
|
hwWriteWii64[i] = HW_Default_Write<u64>;
|
||||||
|
hwReadWii8 [i] = HW_Default_Read<u8&>;
|
||||||
|
hwReadWii16 [i] = HW_Default_Read<u16&>;
|
||||||
|
hwReadWii32 [i] = HW_Default_Read<u32&>;
|
||||||
|
hwReadWii64 [i] = HW_Default_Read<u64&>;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < BLOCKSIZE; i++)
|
for (int i = 0; i < BLOCKSIZE; i++)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
|
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
|
|
||||||
|
// Dolphin Logging framework. Needs a good ol' spring cleaning methinks.
|
||||||
|
|
||||||
#ifndef _LOGMANAGER_H
|
#ifndef _LOGMANAGER_H
|
||||||
#define _LOGMANAGER_H
|
#define _LOGMANAGER_H
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
// TODO: create a OS-neutral version of this file and put it in Common.
|
|
||||||
|
// TODO: create a working OS-neutral version of this file and put it in Common.
|
||||||
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
|
|
@ -20,52 +20,11 @@
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
||||||
typedef u32 EAddr;
|
|
||||||
|
|
||||||
namespace EMM
|
namespace EMM
|
||||||
{
|
{
|
||||||
enum WR
|
typedef u32 EAddr;
|
||||||
{
|
|
||||||
Read = 1,
|
|
||||||
Write = 2,
|
|
||||||
Execute = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
enum WatchType
|
|
||||||
{
|
|
||||||
Oneshot,
|
|
||||||
Continuous
|
|
||||||
};
|
|
||||||
|
|
||||||
enum AccessSize
|
|
||||||
{
|
|
||||||
Access8,
|
|
||||||
Access16,
|
|
||||||
Access32,
|
|
||||||
Access64,
|
|
||||||
Access128
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef int WatchID;
|
|
||||||
typedef void (*WatchCallback)(EAddr addr, AccessSize size, WR action, WatchID id);
|
|
||||||
|
|
||||||
//Useful to emulate low-used I/O, and caching of memory resources that can change any time
|
|
||||||
WatchID AddWatchRegion(EAddr startAddr, EAddr endAddr, WR watchFor, WatchType type, WatchCallback callback, u64 userData);
|
|
||||||
void RemoveWatchRegion(WatchID id);
|
|
||||||
void ClearWatches();
|
|
||||||
|
|
||||||
//Call this on your main emulator thread, with your mainloop in codeToRun
|
|
||||||
|
|
||||||
void InstallExceptionHandler();
|
void InstallExceptionHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 ReadHandler8(EAddr address);
|
|
||||||
u16 ReadHandler16(EAddr address);
|
|
||||||
u32 ReadHandler32(EAddr address);
|
|
||||||
u64 ReadHandler64(EAddr address);
|
|
||||||
void WriteHandler8(EAddr address, u8 value);
|
|
||||||
void WriteHandler16(EAddr address, u16 value);
|
|
||||||
void WriteHandler32(EAddr address, u32 value);
|
|
||||||
void WriteHandler64(EAddr address, u64 value);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -16,15 +16,15 @@
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
// PatchEngine
|
// PatchEngine
|
||||||
|
// Supports simple memory patches, and has a partial Action Replay implementation
|
||||||
|
// in ActionReplay.cpp/h.
|
||||||
|
|
||||||
|
// Zelda item hang fixes:
|
||||||
// [Tue Aug 21 2007] [18:30:40] <Knuckles-> 0x802904b4 in US released
|
// [Tue Aug 21 2007] [18:30:40] <Knuckles-> 0x802904b4 in US released
|
||||||
// [Tue Aug 21 2007] [18:30:53] <Knuckles-> 0x80294d54 in EUR Demo version
|
// [Tue Aug 21 2007] [18:30:53] <Knuckles-> 0x80294d54 in EUR Demo version
|
||||||
// [Tue Aug 21 2007] [18:31:10] <Knuckles-> we just patch a blr on it (0x4E800020)
|
// [Tue Aug 21 2007] [18:31:10] <Knuckles-> we just patch a blr on it (0x4E800020)
|
||||||
// A little present to our dear hacker friends
|
|
||||||
// (A partial Action Replay engine)
|
|
||||||
// And a temporary "solution" to Zelda item glitch...
|
|
||||||
// [OnLoad]
|
// [OnLoad]
|
||||||
// 0x80020394=dword,0x4e800020
|
// 0x80020394=dword,0x4e800020
|
||||||
// #define BLR_OP 0x4e800020
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -36,9 +36,14 @@
|
||||||
|
|
||||||
using namespace Common;
|
using namespace Common;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
std::vector<Patch> onLoad;
|
std::vector<Patch> onLoad;
|
||||||
std::vector<Patch> onFrame;
|
std::vector<Patch> onFrame;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void LoadPatchSection(const char *section, std::vector<Patch> &patches, IniFile &ini)
|
void LoadPatchSection(const char *section, std::vector<Patch> &patches, IniFile &ini)
|
||||||
{
|
{
|
||||||
std::vector<std::string> keys;
|
std::vector<std::string> keys;
|
||||||
|
@ -113,8 +118,5 @@ void PatchEngine_ApplyFramePatches()
|
||||||
|
|
||||||
void PatchEngine_ApplyARPatches()
|
void PatchEngine_ApplyARPatches()
|
||||||
{
|
{
|
||||||
for (std::vector<ARCode>::const_iterator iter = arCodes.begin(); iter != arCodes.end(); ++iter) {
|
ActionReplayRunAllActive();
|
||||||
if (iter->active)
|
|
||||||
RunActionReplayCode(*iter, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -39,129 +39,76 @@ using namespace Gen;
|
||||||
using namespace PowerPC;
|
using namespace PowerPC;
|
||||||
|
|
||||||
extern int blocksExecuted;
|
extern int blocksExecuted;
|
||||||
//X64 Win64 calling convention:
|
|
||||||
// Parameters in RCX RDX R8 R9
|
|
||||||
// Volatile RAX R10 R11
|
|
||||||
// Non volatile (must be saved)
|
|
||||||
// RBX RSI RDI R12 R13 R14 R15
|
|
||||||
|
|
||||||
//Register allocation:
|
// Dolphin's PowerPC->x86 JIT dynamic recompiler
|
||||||
|
// All code by ector (hrydgard)
|
||||||
|
// Features:
|
||||||
|
// * x86 & x64 support, lots of shared code.
|
||||||
|
// * Basic block linking
|
||||||
|
// * Fast dispatcher
|
||||||
|
|
||||||
//RAX - Generic quicktemp register
|
// Unfeatures:
|
||||||
//RBX - point to base of memory map
|
// * Does not recompile all instructions. Often falls back to inserting a CALL to the corresponding JIT function.
|
||||||
//RSI RDI R12 R13 R14 R15 - free for allocation
|
|
||||||
//RCX RDX R8 R9 R10 R11 - allocate in emergencies. These need to be flushed before functions are called.
|
|
||||||
//RSP - stack pointer, do not generally use, very dangerous
|
|
||||||
//RBP - ?
|
|
||||||
|
|
||||||
//RCX RDX R8 R9 are function parameters. We will only call 1 and 2 param functions from compiled code anyway.
|
|
||||||
|
|
||||||
//Calling out to the interpreter needs only to flush the volatile regs!
|
// Various notes below
|
||||||
|
|
||||||
//IMPORTANT:
|
// Register allocation
|
||||||
//Make sure that all generated code and all emulator state sits under the 2GB boundary so that
|
// RAX - Generic quicktemp register
|
||||||
//RIP addressing can be used easily. Windows will always allocate static code under the 2GB boundary.
|
// RBX - point to base of memory map
|
||||||
//Also make sure to use VirtualAlloc and specify EXECUTE permission.
|
// RSI RDI R12 R13 R14 R15 - free for allocation
|
||||||
|
// RCX RDX R8 R9 R10 R11 - allocate in emergencies. These need to be flushed before functions are called.
|
||||||
|
// RSP - stack pointer, do not generally use, very dangerous
|
||||||
|
// RBP - ?
|
||||||
|
|
||||||
//Since RIP stores/loads will not be possible to the high memory area, we will have to use
|
// IMPORTANT:
|
||||||
//a statically allocated base pointer in a register, and use displacement addressing off of that.
|
// Make sure that all generated code and all emulator state sits under the 2GB boundary so that
|
||||||
//A candidate for this is a non vol like R15, since we would not like to have to do a RIP load
|
// RIP addressing can be used easily. Windows will always allocate static code under the 2GB boundary.
|
||||||
//to restore it all the time.
|
// Also make sure to use VirtualAlloc and specify EXECUTE permission.
|
||||||
//No wait a minute, why not just keep the unprotected mappings below 2GB?
|
|
||||||
|
|
||||||
//Another question to be addressed is if it is smart to have a static pointer reg to the base
|
// Open questions
|
||||||
//of the PowerPC reg area.
|
// * Should there be any statically allocated registers? r3, r4, r5, r8, r0 come to mind.. maybe sp
|
||||||
//Pro: Smaller accesses for GPR (8-bit displacement good enough only for gprs)
|
// * Does it make sense to finish off the remaining non-jitted instructions? Seems we are hitting diminishing returns.
|
||||||
//Con: A taken nonvol register (may not be so bad)
|
// * Why is the FPU exception handling not working 100%? Several games still get corrupted floating point state.
|
||||||
|
// This can even be seen in one homebrew Wii demo - RayTracer.elf
|
||||||
|
|
||||||
//Should there be any statically allocated registers? r3, r4, r5, r8, r0 come to mind.. maybe sp
|
// Other considerations
|
||||||
|
|
||||||
//When calling external functions, only volatile regs need to be saved.
|
|
||||||
//This means that they should be allocated last. RAX should probably never
|
|
||||||
//be allocated, it should just be a temporary to do non-destructive trinary ops.
|
|
||||||
|
|
||||||
//However, for the above to work and be a win, we need to store away the non volatiles before
|
|
||||||
//entering "JIT space". However, once we're there, it will be a win.
|
|
||||||
//Also, JIT space will need to be surrounded with stack adjusting, since functions will be called.
|
|
||||||
|
|
||||||
//Many instructions have shorter forms for EAX. However, I believe their performance boost
|
//Many instructions have shorter forms for EAX. However, I believe their performance boost
|
||||||
//will be as small to be negligble, so I haven't dirtied up the code with that. AMD recommends it in their
|
//will be as small to be negligble, so I haven't dirtied up the code with that. AMD recommends it in their
|
||||||
//optimization manuals, though.
|
//optimization manuals, though.
|
||||||
|
|
||||||
//IDEA: FPU exception emulation support by having all fpu blocks writeNTA to a spot in memory that
|
// We support block linking. Reserve space at the exits of every block for a full 5-byte jmp. Save 16-bit offsets
|
||||||
//is protected if FPU exceptions are enabled. The exception handler would then have to run the
|
// from the starts of each block, marking the exits so that they can be nicely patched at any time.
|
||||||
//interpreter until rfi, at which point control can be returned. Of course all regs need to be
|
|
||||||
//flushed before this happens. This method is branch free but does a memory write or read in the fast case.
|
|
||||||
// Probably not worthwhile, a test/jz in every fpu block should be enough.
|
|
||||||
|
|
||||||
//Block linking is needed. Reserve space at the end of every block for a full 5-byte jmp. Save 16-bit offsets
|
// * Blocks do NOT use call/ret, they only jmp to each other and to the dispatcher when necessary.
|
||||||
//from the starts of each block, marking the exits so that they can be nicely patched at any time.
|
|
||||||
|
|
||||||
//Blocks do NOT use call/ret, they only jmp to each other and to the dispatcher when necessary.
|
// All blocks that can be precompiled will be precompiled. Code will be memory protected - any write will mark
|
||||||
|
// the region as non-compilable, and all links to the page will be torn out and replaced with dispatcher jmps.
|
||||||
|
|
||||||
//All blocks that can be precompiled will be precompiled. Code will be memory protected - any write will mark
|
// Alternatively, icbi instruction SHOULD mark where we can't compile
|
||||||
//the region as non-compilable, and all links to the page will be torn out and replaced with dispatcher jmps.
|
|
||||||
|
|
||||||
//Alternatively, icbi instruction SHOULD mark where we can't compile
|
// Seldom-happening events will be handled by adding a decrement of a counter to all blr instructions (which are
|
||||||
|
// expensive anyway since we need to return to dispatcher, except when they can be predicted).
|
||||||
//IDEA: All major memory altering events (not singular accesses) should call Gfx::Snoop to let it know that memory chagned.
|
|
||||||
|
|
||||||
//Seldom-happening events will be handled by adding a decrement of a counter to all blr instructions (which are
|
|
||||||
//expensive anyway since we need to return to dispatcher, except when they can be predicted).
|
|
||||||
|
|
||||||
//TODO:
|
|
||||||
|
|
||||||
// TODO: SERIOUS synchronization problem with the video plugin setting tokens and breakpoints!!!
|
|
||||||
// Somewhat fixed by disabling idle skipping when certain interrupts are enabled
|
|
||||||
// This is no permantent reliable fix
|
|
||||||
|
|
||||||
|
// TODO: SERIOUS synchronization problem with the video plugin setting tokens and breakpoints in dual core mode!!!
|
||||||
|
// Somewhat fixed by disabling idle skipping when certain interrupts are enabled
|
||||||
|
// This is no permantent reliable fix
|
||||||
// TODO: Zeldas go whacko when you hang the gfx thread
|
// TODO: Zeldas go whacko when you hang the gfx thread
|
||||||
|
|
||||||
// Plan: 1. Byteswap Dolphin DONE!
|
// Idea - Accurate exception handling
|
||||||
// 2. Fix timing WORKING
|
// Compute register state at a certain instruction by running the JIT in "dry mode", and stopping at the right place.
|
||||||
// 3. Lay groundwork for x64 JIT WORKING
|
// Not likely to be done :P
|
||||||
// 4. Get OneTri up to 60fps, and check compatibility from time to time (yea right) ????
|
|
||||||
// 5. Add block linking to JIT << NOT SO IMPORTANT
|
|
||||||
// 6. Optimize GFX plugin to hell << IMPORTANT
|
|
||||||
// 7. Watch Zelda do 20 fps.
|
|
||||||
// 8. Watch Zelda TP do 30 fps. DONE :D
|
|
||||||
|
|
||||||
//Optimizations -
|
|
||||||
|
// Optimization Ideas -
|
||||||
/*
|
/*
|
||||||
* Assume SP is in main RAM (in Wii mode too?)
|
* Assume SP is in main RAM (in Wii mode too?) - partly done
|
||||||
* Assume all floating point loads and double precision loads+stores are to/from main ram
|
* Assume all floating point loads and double precision loads+stores are to/from main ram
|
||||||
(single precision can be used in write gather)
|
(single precision can be used in write gather pipe, specialized fast check added)
|
||||||
(this is valid on Wii too when using the VM emulator)
|
|
||||||
|
|
||||||
* AMD only - use movaps instead of movapd when loading ps from memory?
|
* AMD only - use movaps instead of movapd when loading ps from memory?
|
||||||
* HLE functions like floorf, sin, memcpy, etc - they can be much faster
|
* HLE functions like floorf, sin, memcpy, etc - they can be much faster
|
||||||
* Optimal sequence to store floats
|
|
||||||
* TODO: find optimal sequence to store doubles as floats
|
|
||||||
|
|
||||||
cvtpd2ps xmm0, xmm0
|
|
||||||
movss xmm0, f
|
|
||||||
movss tempspace, xmm0
|
|
||||||
mov eax, tempspace
|
|
||||||
bswap eax
|
|
||||||
mov [edi], eax
|
|
||||||
|
|
||||||
I think pshufb does it faster.
|
|
||||||
|
|
||||||
BLOCK EXIT DESIGN
|
|
||||||
|
|
||||||
TEST whatever
|
|
||||||
JZ skip
|
|
||||||
MOV NPC, exit1
|
|
||||||
JMP dispatcher
|
|
||||||
skip:
|
|
||||||
MOV NPC, exit2
|
|
||||||
JMP dispatcher
|
|
||||||
|
|
||||||
This can be patched into (when both exits are known):
|
|
||||||
JZ exit2
|
|
||||||
JMP exit1
|
|
||||||
|
|
||||||
The problem is, we still need to fit the downcount somewhere...
|
|
||||||
|
|
||||||
Low hanging fruit:
|
Low hanging fruit:
|
||||||
stfd -- guaranteed in memory
|
stfd -- guaranteed in memory
|
||||||
|
@ -186,9 +133,6 @@ cntlzwx
|
||||||
bcctrx
|
bcctrx
|
||||||
WriteBigEData
|
WriteBigEData
|
||||||
|
|
||||||
|
|
||||||
detect immediates in stb stw sth
|
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
lha
|
lha
|
||||||
srawx
|
srawx
|
||||||
|
@ -196,29 +140,18 @@ addic_rc
|
||||||
addex
|
addex
|
||||||
subfcx
|
subfcx
|
||||||
subfex
|
subfex
|
||||||
000000000A42BD7F mov ecx,0FCBF41BAh
|
|
||||||
000000000A42BD85 call CInterpreter::fmaddx (5BA3A0h)
|
fmaddx
|
||||||
000000000A42BD8A mov ecx,0FC8D0132h
|
fmulx
|
||||||
000000000A42BD90 call CInterpreter::fmulx (5BA540h)
|
faddx
|
||||||
000000000A42BD95 mov ecx,0FC85202Ah
|
fnegx
|
||||||
000000000A42BD9B call CInterpreter::faddx (5BA220h)
|
frspx
|
||||||
000000000A42BDA0 mov ecx,0FC81113Ah
|
frsqrtex
|
||||||
000000000A42BDA6 call CInterpreter::fmaddx (5BA3A0h)
|
ps_sum0
|
||||||
000000000A42C11A call CInterpreter::fnegx (5BA0B0h)
|
|
||||||
000000000A42C604 call CInterpreter::frspx (5BA170h)
|
|
||||||
000000000A428FDC call CInterpreter::ps_sum0 (5C9730h)
|
|
||||||
000000000A428FE1 mov ecx,0FCA02034h
|
|
||||||
000000000A428FE7 call CInterpreter::frsqrtex (5BA7C0h)
|
|
||||||
000000000A429062 call CInterpreter::ps_muls0 (5C9820h)
|
|
||||||
000000000A4290AF call CInterpreter::psq_st (5C9DF0h)
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Accurate exception handling
|
|
||||||
// Compute register state at a certain instruction by running the JIT in "dry mode", and stopping at the right place.
|
|
||||||
// Not likely to be done :P
|
|
||||||
|
|
||||||
// Evil
|
|
||||||
namespace CPUCompare
|
namespace CPUCompare
|
||||||
{
|
{
|
||||||
extern u32 m_BlockStart;
|
extern u32 m_BlockStart;
|
||||||
|
@ -232,7 +165,7 @@ namespace Jit64
|
||||||
|
|
||||||
void Init()
|
void Init()
|
||||||
{
|
{
|
||||||
jo.optimizeStack = true;
|
jo.optimizeStack = false;
|
||||||
jo.enableBlocklink = true; // Speed boost, but not 100% safe
|
jo.enableBlocklink = true; // Speed boost, but not 100% safe
|
||||||
#ifdef _M_X64
|
#ifdef _M_X64
|
||||||
jo.enableFastMem = Core::GetStartupParameter().bUseFastMem;
|
jo.enableFastMem = Core::GetStartupParameter().bUseFastMem;
|
||||||
|
@ -243,6 +176,7 @@ namespace Jit64
|
||||||
jo.fpAccurateFlags = true;
|
jo.fpAccurateFlags = true;
|
||||||
jo.optimizeGatherPipe = true;
|
jo.optimizeGatherPipe = true;
|
||||||
jo.interpretFPU = false;
|
jo.interpretFPU = false;
|
||||||
|
jo.fastInterrupts = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteCallInterpreter(UGeckoInstruction _inst)
|
void WriteCallInterpreter(UGeckoInstruction _inst)
|
||||||
|
@ -280,6 +214,7 @@ namespace Jit64
|
||||||
static const bool ImHereDebug = false;
|
static const bool ImHereDebug = false;
|
||||||
static const bool ImHereLog = false;
|
static const bool ImHereLog = false;
|
||||||
static std::map<u32, int> been_here;
|
static std::map<u32, int> been_here;
|
||||||
|
|
||||||
void ImHere()
|
void ImHere()
|
||||||
{
|
{
|
||||||
static FILE *f = 0;
|
static FILE *f = 0;
|
||||||
|
@ -378,9 +313,11 @@ namespace Jit64
|
||||||
const u8 *start = AlignCode4(); //TODO: Test if this or AlignCode16 make a difference from GetCodePtr
|
const u8 *start = AlignCode4(); //TODO: Test if this or AlignCode16 make a difference from GetCodePtr
|
||||||
b.checkedEntry = start;
|
b.checkedEntry = start;
|
||||||
b.runCount = 0;
|
b.runCount = 0;
|
||||||
|
|
||||||
|
// Downcount flag check. The last block decremented downcounter, and the flag should still be available.
|
||||||
FixupBranch skip = J_CC(CC_NBE);
|
FixupBranch skip = J_CC(CC_NBE);
|
||||||
MOV(32, M(&PC), Imm32(js.blockStart));
|
MOV(32, M(&PC), Imm32(js.blockStart));
|
||||||
JMP(Asm::doTiming, true);
|
JMP(Asm::doTiming, true); // downcount hit zero - go doTiming.
|
||||||
SetJumpTarget(skip);
|
SetJumpTarget(skip);
|
||||||
|
|
||||||
const u8 *normalEntry = GetCodePtr();
|
const u8 *normalEntry = GetCodePtr();
|
||||||
|
@ -396,6 +333,17 @@ namespace Jit64
|
||||||
SetJumpTarget(b1);
|
SetJumpTarget(b1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (false && jo.fastInterrupts)
|
||||||
|
{
|
||||||
|
// This does NOT yet work.
|
||||||
|
TEST(32, M(&PowerPC::ppcState.Exceptions), Imm32(0xFFFFFFFF));
|
||||||
|
FixupBranch b1 = J_CC(CC_Z);
|
||||||
|
MOV(32, M(&PC), Imm32(js.blockStart));
|
||||||
|
JMP(Asm::testExceptions, true);
|
||||||
|
SetJumpTarget(b1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conditionally add profiling code.
|
||||||
if (Profiler::g_ProfileBlocks) {
|
if (Profiler::g_ProfileBlocks) {
|
||||||
ADD(32, M(&b.runCount), Imm8(1));
|
ADD(32, M(&b.runCount), Imm8(1));
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -439,13 +387,12 @@ namespace Jit64
|
||||||
}
|
}
|
||||||
|
|
||||||
// const GekkoOpInfo *info = GetOpInfo();
|
// const GekkoOpInfo *info = GetOpInfo();
|
||||||
// if (js.isLastInstruction)
|
|
||||||
if (jo.interpretFPU && PPCTables::UsesFPU(ops[i].inst))
|
if (jo.interpretFPU && PPCTables::UsesFPU(ops[i].inst))
|
||||||
Default(ops[i].inst);
|
Default(ops[i].inst);
|
||||||
else
|
else
|
||||||
PPCTables::CompileInstruction(ops[i].inst);
|
PPCTables::CompileInstruction(ops[i].inst);
|
||||||
// else
|
|
||||||
// Default(ops[i].inst);
|
|
||||||
gpr.SanityCheck();
|
gpr.SanityCheck();
|
||||||
fpr.SanityCheck();
|
fpr.SanityCheck();
|
||||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
|
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
// Low hanging fruit:
|
// ========================
|
||||||
// all used in zelda
|
// See comments in Jit.cpp.
|
||||||
// negx
|
// ========================
|
||||||
|
|
||||||
#ifndef _JIT_H
|
#ifndef _JIT_H
|
||||||
#define _JIT_H
|
#define _JIT_H
|
||||||
|
@ -26,14 +26,10 @@
|
||||||
#include "JitCache.h"
|
#include "JitCache.h"
|
||||||
#include "x64Emitter.h"
|
#include "x64Emitter.h"
|
||||||
|
|
||||||
|
|
||||||
// =======================================================================================
|
|
||||||
// Enable or disable JIT off options. All the if() checks in the JIT functions may result in a
|
// Enable or disable JIT off options. All the if() checks in the JIT functions may result in a
|
||||||
// speed drop. However it should barely be noticable as the code is recompiled rarely.
|
// speed drop. However it should barely be noticable as the code is recompiled rarely.
|
||||||
// --------------
|
|
||||||
#define JIT_OFF_OPTIONS
|
#define JIT_OFF_OPTIONS
|
||||||
|
|
||||||
|
|
||||||
namespace Jit64
|
namespace Jit64
|
||||||
{
|
{
|
||||||
struct JitStats
|
struct JitStats
|
||||||
|
@ -80,6 +76,7 @@ namespace Jit64
|
||||||
bool enableFastMem;
|
bool enableFastMem;
|
||||||
bool optimizeGatherPipe;
|
bool optimizeGatherPipe;
|
||||||
bool interpretFPU;
|
bool interpretFPU;
|
||||||
|
bool fastInterrupts;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern JitState js;
|
extern JitState js;
|
||||||
|
|
|
@ -30,17 +30,20 @@
|
||||||
// and what functions calls it. That is, we will have an incomplete call graph,
|
// and what functions calls it. That is, we will have an incomplete call graph,
|
||||||
// but only missing indirect branches.
|
// but only missing indirect branches.
|
||||||
|
|
||||||
// The results of this analysis are currently not really used for anything, other than
|
// The results of this analysis is displayed in the code browsing sections at the bottom left
|
||||||
// finding function boundaries so that we can find, fingerprint and detect library functions.
|
// of the disassembly window (debugger).
|
||||||
|
|
||||||
|
// It is also useful for finding function boundaries so that we can find, fingerprint and detect library functions.
|
||||||
|
// We don't do this much currently. Only for the special case Super Monkey Ball.
|
||||||
|
|
||||||
namespace PPCAnalyst {
|
namespace PPCAnalyst {
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
// VERY ugly. TODO: remove.
|
|
||||||
PPCAnalyst::CodeOp *codebuffer;
|
PPCAnalyst::CodeOp *codebuffer;
|
||||||
|
|
||||||
enum {
|
enum
|
||||||
|
{
|
||||||
CODEBUFFER_SIZE = 32000,
|
CODEBUFFER_SIZE = 32000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,6 @@ struct GekkoOPTemplate
|
||||||
int runCount;
|
int runCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The eventual goal is to be able to constify as much as possible in this file.
|
|
||||||
// Currently, the main obstacle is runCount above.
|
|
||||||
static GekkoOPInfo *m_infoTable[64];
|
static GekkoOPInfo *m_infoTable[64];
|
||||||
static GekkoOPInfo *m_infoTable4[1024];
|
static GekkoOPInfo *m_infoTable4[1024];
|
||||||
static GekkoOPInfo *m_infoTable19[1024];
|
static GekkoOPInfo *m_infoTable19[1024];
|
||||||
|
|
|
@ -46,7 +46,6 @@ enum
|
||||||
FL_CHECKEXCEPTIONS = (1<<16),
|
FL_CHECKEXCEPTIONS = (1<<16),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
OPTYPE_INVALID ,
|
OPTYPE_INVALID ,
|
||||||
|
|
|
@ -37,269 +37,271 @@
|
||||||
|
|
||||||
namespace PowerPC
|
namespace PowerPC
|
||||||
{
|
{
|
||||||
// STATE_TO_SAVE
|
|
||||||
PowerPCState GC_ALIGNED16(ppcState);
|
|
||||||
volatile CPUState state = CPU_STEPPING;
|
|
||||||
|
|
||||||
static CoreMode mode;
|
// STATE_TO_SAVE
|
||||||
|
PowerPCState GC_ALIGNED16(ppcState);
|
||||||
|
volatile CPUState state = CPU_STEPPING;
|
||||||
|
|
||||||
void DoState(PointerWrap &p)
|
static CoreMode mode;
|
||||||
|
|
||||||
|
void DoState(PointerWrap &p)
|
||||||
|
{
|
||||||
|
p.Do(ppcState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetRegisters()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
{
|
{
|
||||||
p.Do(ppcState);
|
ppcState.gpr[i] = 0;
|
||||||
|
riPS0(i) = 0;
|
||||||
|
riPS1(i) = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetRegisters()
|
memset(ppcState.spr, 0, sizeof(ppcState.spr));
|
||||||
|
|
||||||
|
ppcState.cr = 0;
|
||||||
|
ppcState.fpscr = 0;
|
||||||
|
ppcState.pc = 0;
|
||||||
|
ppcState.npc = 0;
|
||||||
|
ppcState.Exceptions = 0;
|
||||||
|
|
||||||
|
TL = 0;
|
||||||
|
TU = 0;
|
||||||
|
|
||||||
|
ppcState.msr = 0;
|
||||||
|
rDEC = 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init()
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
FPU_PREC_24 = 0 << 8,
|
||||||
|
FPU_PREC_53 = 2 << 8,
|
||||||
|
FPU_PREC_64 = 3 << 8,
|
||||||
|
FPU_PREC_MASK = 3 << 8,
|
||||||
|
};
|
||||||
|
#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
|
||||||
|
unsigned short _mode;
|
||||||
|
asm ("fstcw %0" : : "m" (_mode));
|
||||||
|
_mode = (_mode & ~FPU_PREC_MASK) | FPU_PREC_53;
|
||||||
|
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
|
||||||
|
|
||||||
|
ResetRegisters();
|
||||||
|
PPCTables::InitTables();
|
||||||
|
|
||||||
|
// Initialize both execution engines ...
|
||||||
|
Interpreter::Init();
|
||||||
|
Jit64::Core::Init();
|
||||||
|
// ... but start as interpreter by default.
|
||||||
|
mode = MODE_INTERPRETER;
|
||||||
|
state = CPU_STEPPING;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shutdown()
|
||||||
|
{
|
||||||
|
// Shutdown both execution engines. Doesn't matter which one is active.
|
||||||
|
Jit64::Core::Shutdown();
|
||||||
|
Interpreter::Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetMode(CoreMode new_mode)
|
||||||
|
{
|
||||||
|
if (new_mode == mode)
|
||||||
|
return; // We don't need to do anything.
|
||||||
|
|
||||||
|
mode = new_mode;
|
||||||
|
switch (mode)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 32; i++)
|
case MODE_INTERPRETER: // Switching from JIT to interpreter
|
||||||
{
|
Jit64::ClearCache(); // Remove all those nasty JIT patches.
|
||||||
ppcState.gpr[i] = 0;
|
break;
|
||||||
riPS0(i) = 0;
|
|
||||||
riPS1(i) = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(ppcState.spr, 0, sizeof(ppcState.spr));
|
case MODE_JIT: // Switching from interpreter to JIT.
|
||||||
|
// Don't really need to do much. It'll work, the cache will refill itself.
|
||||||
ppcState.cr = 0;
|
break;
|
||||||
ppcState.fpscr = 0;
|
|
||||||
ppcState.pc = 0;
|
|
||||||
ppcState.npc = 0;
|
|
||||||
ppcState.Exceptions = 0;
|
|
||||||
|
|
||||||
TL = 0;
|
|
||||||
TU = 0;
|
|
||||||
|
|
||||||
ppcState.msr = 0;
|
|
||||||
rDEC = 0xFFFFFFFF;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Init()
|
void SingleStep()
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
{
|
{
|
||||||
enum {
|
case MODE_INTERPRETER:
|
||||||
FPU_PREC_24 = 0 << 8,
|
Interpreter::SingleStep();
|
||||||
FPU_PREC_53 = 2 << 8,
|
break;
|
||||||
FPU_PREC_64 = 3 << 8,
|
case MODE_JIT:
|
||||||
FPU_PREC_MASK = 3 << 8,
|
Jit64::Core::SingleStep();
|
||||||
};
|
break;
|
||||||
#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
|
|
||||||
unsigned short _mode;
|
|
||||||
asm ("fstcw %0" : : "m" (_mode));
|
|
||||||
_mode = (_mode & ~FPU_PREC_MASK) | FPU_PREC_53;
|
|
||||||
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
|
|
||||||
|
|
||||||
ResetRegisters();
|
|
||||||
PPCTables::InitTables();
|
|
||||||
|
|
||||||
// Initialize both execution engines ...
|
|
||||||
Interpreter::Init();
|
|
||||||
Jit64::Core::Init();
|
|
||||||
// ... but start as interpreter by default.
|
|
||||||
mode = MODE_INTERPRETER;
|
|
||||||
state = CPU_STEPPING;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Shutdown()
|
void RunLoop()
|
||||||
|
{
|
||||||
|
state = CPU_RUNNING;
|
||||||
|
switch (mode)
|
||||||
{
|
{
|
||||||
// Shutdown both execution engines. Doesn't matter which one is active.
|
case MODE_INTERPRETER:
|
||||||
Jit64::Core::Shutdown();
|
Interpreter::Run();
|
||||||
Interpreter::Shutdown();
|
break;
|
||||||
|
case MODE_JIT:
|
||||||
|
Jit64::Core::Run();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
Host_UpdateDisasmDialog();
|
||||||
|
}
|
||||||
|
|
||||||
void SetMode(CoreMode new_mode)
|
void Start()
|
||||||
{
|
{
|
||||||
if (new_mode == mode)
|
state = Core::g_CoreStartupParameter.bEnableDebugging ? CPU_RUNNINGDEBUG : CPU_RUNNING;
|
||||||
return; // We don't need to do anything.
|
if (Core::bReadTrace || Core::bWriteTrace)
|
||||||
|
|
||||||
mode = new_mode;
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case MODE_INTERPRETER: // Switching from JIT to interpreter
|
|
||||||
Jit64::ClearCache(); // Remove all those nasty JIT patches.
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MODE_JIT: // Switching from interpreter to JIT.
|
|
||||||
// Don't really need to do much. It'll work, the cache will refill itself.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SingleStep()
|
|
||||||
{
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case MODE_INTERPRETER:
|
|
||||||
Interpreter::SingleStep();
|
|
||||||
break;
|
|
||||||
case MODE_JIT:
|
|
||||||
Jit64::Core::SingleStep();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RunLoop()
|
|
||||||
{
|
{
|
||||||
state = CPU_RUNNING;
|
state = CPU_RUNNING;
|
||||||
switch (mode)
|
}
|
||||||
{
|
Host_UpdateDisasmDialog();
|
||||||
case MODE_INTERPRETER:
|
}
|
||||||
Interpreter::Run();
|
|
||||||
break;
|
void Pause()
|
||||||
case MODE_JIT:
|
{
|
||||||
Jit64::Core::Run();
|
state = CPU_STEPPING;
|
||||||
break;
|
Host_UpdateDisasmDialog();
|
||||||
}
|
}
|
||||||
Host_UpdateDisasmDialog();
|
|
||||||
|
void Stop()
|
||||||
|
{
|
||||||
|
state = CPU_POWERDOWN;
|
||||||
|
Host_UpdateDisasmDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckExceptions()
|
||||||
|
{
|
||||||
|
// This check is unnecessary in JIT mode. However, it probably doesn't really hurt.
|
||||||
|
if (!ppcState.Exceptions)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO(ector):
|
||||||
|
// gcemu uses the mask 0x87C0FFFF instead of 0x0780FF77
|
||||||
|
// Investigate!
|
||||||
|
|
||||||
|
if (ppcState.Exceptions & EXCEPTION_FPU_UNAVAILABLE)
|
||||||
|
{
|
||||||
|
//This happens a lot - Gamecube OS uses deferred FPU context switching
|
||||||
|
SRR0 = PC; // re-execute the instruction
|
||||||
|
SRR1 = MSR & 0x0780FF77;
|
||||||
|
NPC = 0x80000800;
|
||||||
|
|
||||||
|
LOG(GEKKO, "EXCEPTION_FPU_UNAVAILABLE");
|
||||||
|
ppcState.Exceptions &= ~EXCEPTION_FPU_UNAVAILABLE;
|
||||||
|
SRR1 |= 0x02; //recoverable
|
||||||
|
}
|
||||||
|
else if (ppcState.Exceptions & EXCEPTION_SYSCALL)
|
||||||
|
{
|
||||||
|
SRR0 = NPC; // execute next instruction when we come back from handler
|
||||||
|
SRR1 = MSR & 0x0780FF77;
|
||||||
|
NPC = 0x80000C00;
|
||||||
|
|
||||||
|
LOG(GEKKO, "EXCEPTION_SYSCALL (PC=%08x)",PC);
|
||||||
|
ppcState.Exceptions &= ~EXCEPTION_SYSCALL;
|
||||||
|
SRR1 |= 0x02; //recoverable
|
||||||
|
}
|
||||||
|
else if (ppcState.Exceptions & EXCEPTION_DSI)
|
||||||
|
{
|
||||||
|
SRR0 = PC; // re-execute the instruction
|
||||||
|
SRR1 = MSR & 0x0780FF77;
|
||||||
|
NPC = 0x80000300;
|
||||||
|
|
||||||
|
LOG(GEKKO, "EXCEPTION_DSI");
|
||||||
|
ppcState.Exceptions &= ~EXCEPTION_DSI;
|
||||||
|
//SRR1 |= 0x02; //make recoverable ?
|
||||||
|
}
|
||||||
|
else if (ppcState.Exceptions & EXCEPTION_ISI)
|
||||||
|
{
|
||||||
|
SRR0 = PC;
|
||||||
|
SRR1 = (MSR & 0x0780FF77) | 0x40000000;
|
||||||
|
NPC = 0x80000400;
|
||||||
|
|
||||||
|
LOG(GEKKO, "EXCEPTION_ISI");
|
||||||
|
ppcState.Exceptions &= ~EXCEPTION_ISI;
|
||||||
|
//SRR1 |= 0x02; //make recoverable ?
|
||||||
|
}
|
||||||
|
else if (ppcState.Exceptions & EXCEPTION_ALIGNMENT)
|
||||||
|
{
|
||||||
|
//This never happens ATM
|
||||||
|
SRR0 = NPC;
|
||||||
|
SRR1 = MSR & 0x0780FF77;
|
||||||
|
NPC = 0x80000600;
|
||||||
|
|
||||||
|
LOG(GEKKO, "EXCEPTION_ALIGNMENT");
|
||||||
|
ppcState.Exceptions &= ~EXCEPTION_ALIGNMENT;
|
||||||
|
//SRR1 |= 0x02; //make recoverable ?
|
||||||
}
|
}
|
||||||
|
|
||||||
void Start()
|
// EXTERNAL INTTERUPT
|
||||||
|
else if (MSR & 0x0008000)
|
||||||
{
|
{
|
||||||
state = Core::g_CoreStartupParameter.bEnableDebugging ? CPU_RUNNINGDEBUG : CPU_RUNNING;
|
if (ppcState.Exceptions & EXCEPTION_EXTERNAL_INT)
|
||||||
if (Core::bReadTrace || Core::bWriteTrace)
|
|
||||||
{
|
{
|
||||||
state = CPU_RUNNING;
|
// Pokemon gets this "too early", it hasn't a handler yet
|
||||||
}
|
ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT; // clear exception
|
||||||
Host_UpdateDisasmDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Pause()
|
|
||||||
{
|
|
||||||
state = CPU_STEPPING;
|
|
||||||
Host_UpdateDisasmDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stop()
|
|
||||||
{
|
|
||||||
state = CPU_POWERDOWN;
|
|
||||||
Host_UpdateDisasmDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheckExceptions()
|
|
||||||
{
|
|
||||||
// This check is unnecessary in JIT mode. However, it probably doesn't really hurt.
|
|
||||||
if (!ppcState.Exceptions)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO(ector):
|
|
||||||
// gcemu uses the mask 0x87C0FFFF instead of 0x0780FF77
|
|
||||||
// Investigate!
|
|
||||||
|
|
||||||
if (ppcState.Exceptions & EXCEPTION_FPU_UNAVAILABLE)
|
|
||||||
{
|
|
||||||
//This happens a lot - Gamecube OS uses deferred FPU context switching
|
|
||||||
SRR0 = PC; // re-execute the instruction
|
|
||||||
SRR1 = MSR & 0x0780FF77;
|
|
||||||
NPC = 0x80000800;
|
|
||||||
|
|
||||||
LOG(GEKKO, "EXCEPTION_FPU_UNAVAILABLE");
|
|
||||||
ppcState.Exceptions &= ~EXCEPTION_FPU_UNAVAILABLE;
|
|
||||||
SRR1 |= 0x02; //recoverable
|
|
||||||
}
|
|
||||||
else if (ppcState.Exceptions & EXCEPTION_SYSCALL)
|
|
||||||
{
|
|
||||||
SRR0 = NPC; // execute next instruction when we come back from handler
|
|
||||||
SRR1 = MSR & 0x0780FF77;
|
|
||||||
NPC = 0x80000C00;
|
|
||||||
|
|
||||||
LOG(GEKKO, "EXCEPTION_SYSCALL (PC=%08x)",PC);
|
|
||||||
ppcState.Exceptions &= ~EXCEPTION_SYSCALL;
|
|
||||||
SRR1 |= 0x02; //recoverable
|
|
||||||
}
|
|
||||||
else if (ppcState.Exceptions & EXCEPTION_DSI)
|
|
||||||
{
|
|
||||||
SRR0 = PC; // re-execute the instruction
|
|
||||||
SRR1 = MSR & 0x0780FF77;
|
|
||||||
NPC = 0x80000300;
|
|
||||||
|
|
||||||
LOG(GEKKO, "EXCEPTION_DSI");
|
|
||||||
ppcState.Exceptions &= ~EXCEPTION_DSI;
|
|
||||||
//SRR1 |= 0x02; //make recoverable ?
|
|
||||||
}
|
|
||||||
else if (ppcState.Exceptions & EXCEPTION_ISI)
|
|
||||||
{
|
|
||||||
SRR0 = PC;
|
|
||||||
SRR1 = (MSR & 0x0780FF77) | 0x40000000;
|
|
||||||
NPC = 0x80000400;
|
|
||||||
|
|
||||||
LOG(GEKKO, "EXCEPTION_ISI");
|
|
||||||
ppcState.Exceptions &= ~EXCEPTION_ISI;
|
|
||||||
//SRR1 |= 0x02; //make recoverable ?
|
|
||||||
}
|
|
||||||
else if (ppcState.Exceptions & EXCEPTION_ALIGNMENT)
|
|
||||||
{
|
|
||||||
//This never happens ATM
|
|
||||||
SRR0 = NPC;
|
SRR0 = NPC;
|
||||||
SRR1 = MSR & 0x0780FF77;
|
NPC = 0x80000500;
|
||||||
NPC = 0x80000600;
|
SRR1 = (MSR & 0x0780FF77);
|
||||||
|
|
||||||
LOG(GEKKO, "EXCEPTION_ALIGNMENT");
|
LOG(GEKKO, "EXCEPTION_EXTERNAL_INT");
|
||||||
ppcState.Exceptions &= ~EXCEPTION_ALIGNMENT;
|
|
||||||
//SRR1 |= 0x02; //make recoverable ?
|
SRR1 |= 0x02; //set it to recoverable
|
||||||
|
_dbg_assert_msg_(GEKKO, (SRR1 & 0x02) != 0, "GEKKO", "EXTERNAL_INT unrecoverable???"); // unrecoverable exception !?!
|
||||||
}
|
}
|
||||||
|
else if (ppcState.Exceptions & EXCEPTION_DECREMENTER)
|
||||||
// EXTERNAL INTTERUPT
|
|
||||||
else if (MSR & 0x0008000)
|
|
||||||
{
|
{
|
||||||
if (ppcState.Exceptions & EXCEPTION_EXTERNAL_INT)
|
SRR0 = NPC;
|
||||||
{
|
SRR1 = MSR & 0x0000FF77;
|
||||||
// Pokemon gets this "too early", it hasn't a handler yet
|
NPC = 0x80000900;
|
||||||
ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT; // clear exception
|
|
||||||
|
|
||||||
SRR0 = NPC;
|
ppcState.Exceptions &= ~EXCEPTION_DECREMENTER;
|
||||||
NPC = 0x80000500;
|
|
||||||
SRR1 = (MSR & 0x0780FF77);
|
|
||||||
|
|
||||||
LOG(GEKKO, "EXCEPTION_EXTERNAL_INT");
|
LOG(GEKKO, "EXCEPTION_DECREMENTER");
|
||||||
|
SRR1 |= 0x02; //make recoverable
|
||||||
SRR1 |= 0x02; //set it to recoverable
|
|
||||||
_dbg_assert_msg_(GEKKO, (SRR1 & 0x02) != 0, "GEKKO", "EXTERNAL_INT unrecoverable???"); // unrecoverable exception !?!
|
|
||||||
}
|
|
||||||
else if (ppcState.Exceptions & EXCEPTION_DECREMENTER)
|
|
||||||
{
|
|
||||||
SRR0 = NPC;
|
|
||||||
SRR1 = MSR & 0x0000FF77;
|
|
||||||
NPC = 0x80000900;
|
|
||||||
|
|
||||||
ppcState.Exceptions &= ~EXCEPTION_DECREMENTER;
|
|
||||||
|
|
||||||
LOG(GEKKO, "EXCEPTION_DECREMENTER");
|
|
||||||
SRR1 |= 0x02; //make recoverable
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_dbg_assert_msg_(GEKKO, 0, "Unknown EXT interrupt: Exceptions == %08x", ppcState.Exceptions);
|
|
||||||
LOG(GEKKO, "Unknown EXTERNAL INTERRUPT exception: Exceptions == %08x", ppcState.Exceptions);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MSR &= ~0x0008000; // clear EE-bit so interrupts aren't possible anymore
|
else
|
||||||
}
|
|
||||||
|
|
||||||
void OnIdle(u32 _uThreadAddr)
|
|
||||||
{
|
|
||||||
u32 nextThread = Memory::Read_U32(_uThreadAddr);
|
|
||||||
//do idle skipping
|
|
||||||
if (nextThread == 0)
|
|
||||||
{
|
{
|
||||||
CoreTiming::Idle();
|
_dbg_assert_msg_(GEKKO, 0, "Unknown EXT interrupt: Exceptions == %08x", ppcState.Exceptions);
|
||||||
|
LOG(GEKKO, "Unknown EXTERNAL INTERRUPT exception: Exceptions == %08x", ppcState.Exceptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//DualCore OnIdle
|
MSR &= ~0x0008000; // clear EE-bit so interrupts aren't possible anymore
|
||||||
void OnIdleDC(void)
|
}
|
||||||
|
|
||||||
|
void OnIdle(u32 _uThreadAddr)
|
||||||
|
{
|
||||||
|
u32 nextThread = Memory::Read_U32(_uThreadAddr);
|
||||||
|
//do idle skipping
|
||||||
|
if (nextThread == 0)
|
||||||
{
|
{
|
||||||
#if defined(THREAD_VIDEO_WAKEUP_ONIDLE) && defined(_WIN32)
|
|
||||||
if (g_hEventOnIdle==NULL) PanicAlert("Idle() -> EventOnIdle NULL");
|
|
||||||
if (! SetEvent(g_hEventOnIdle) ) { PanicAlert("Idle() -> SetEvent EventOnIdle failed");}
|
|
||||||
#endif
|
|
||||||
CoreTiming::Idle();
|
CoreTiming::Idle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//DualCore OnIdle
|
||||||
|
void OnIdleDC(void)
|
||||||
|
{
|
||||||
|
#if defined(THREAD_VIDEO_WAKEUP_ONIDLE) && defined(_WIN32)
|
||||||
|
if (g_hEventOnIdle==NULL) PanicAlert("Idle() -> EventOnIdle NULL");
|
||||||
|
if (! SetEvent(g_hEventOnIdle) ) { PanicAlert("Idle() -> SetEvent EventOnIdle failed");}
|
||||||
|
#endif
|
||||||
|
CoreTiming::Idle();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace PowerPC
|
||||||
MODE_JIT,
|
MODE_JIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This contains the entire state of the emulated PowerPC "Gekko" CPU.
|
||||||
struct GC_ALIGNED64(PowerPCState)
|
struct GC_ALIGNED64(PowerPCState)
|
||||||
{
|
{
|
||||||
u32 mojs[128]; // Try to isolate the regs from other variables in the cache.
|
u32 mojs[128]; // Try to isolate the regs from other variables in the cache.
|
||||||
|
@ -88,7 +89,7 @@ namespace PowerPC
|
||||||
void OnIdleDC(void);
|
void OnIdleDC(void);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special registers
|
// Easy register access macros.
|
||||||
#define HID2 ((UReg_HID2&)PowerPC::ppcState.spr[SPR_HID2])
|
#define HID2 ((UReg_HID2&)PowerPC::ppcState.spr[SPR_HID2])
|
||||||
#define DMAU (*(UReg_DMAU*)&PowerPC::ppcState.spr[SPR_DMAU])
|
#define DMAU (*(UReg_DMAU*)&PowerPC::ppcState.spr[SPR_DMAU])
|
||||||
#define DMAL (*(UReg_DMAL*)&PowerPC::ppcState.spr[SPR_DMAL])
|
#define DMAL (*(UReg_DMAL*)&PowerPC::ppcState.spr[SPR_DMAL])
|
||||||
|
@ -115,15 +116,16 @@ namespace PowerPC
|
||||||
#define TL PowerPC::ppcState.spr[SPR_TL]
|
#define TL PowerPC::ppcState.spr[SPR_TL]
|
||||||
#define TU PowerPC::ppcState.spr[SPR_TU]
|
#define TU PowerPC::ppcState.spr[SPR_TU]
|
||||||
|
|
||||||
|
|
||||||
#define rPS0(i) (*(double*)(&PowerPC::ppcState.ps[i][0]))
|
#define rPS0(i) (*(double*)(&PowerPC::ppcState.ps[i][0]))
|
||||||
#define rPS1(i) (*(double*)(&PowerPC::ppcState.ps[i][1]))
|
#define rPS1(i) (*(double*)(&PowerPC::ppcState.ps[i][1]))
|
||||||
|
|
||||||
#define riPS0(i) (*(u64*)(&PowerPC::ppcState.ps[i][0]))
|
#define riPS0(i) (*(u64*)(&PowerPC::ppcState.ps[i][0]))
|
||||||
#define riPS1(i) (*(u64*)(&PowerPC::ppcState.ps[i][1]))
|
#define riPS1(i) (*(u64*)(&PowerPC::ppcState.ps[i][1]))
|
||||||
|
|
||||||
// #define DMAU PowerPC::ppcState.Helper[SPR_DMAU ]
|
|
||||||
// #define DMAL PowerPC::ppcState.Helper[SPR_DMAL ]
|
// Wrappers to make it easier to in the future completely replace the storage of CR and Carry bits
|
||||||
|
// to something more x86-friendly. These are not used 100% consistently yet - and if we do this, we
|
||||||
|
// need the corresponding stuff on the JIT side too.
|
||||||
|
|
||||||
inline void SetCRField(int cr_field, int value) {
|
inline void SetCRField(int cr_field, int value) {
|
||||||
PowerPC::ppcState.cr = (PowerPC::ppcState.cr & (~(0xF0000000 >> (cr_field * 4)))) | (value << ((7 - cr_field) * 4));
|
PowerPC::ppcState.cr = (PowerPC::ppcState.cr & (~(0xF0000000 >> (cr_field * 4)))) | (value << ((7 - cr_field) * 4));
|
||||||
|
@ -153,6 +155,4 @@ inline int GetCarry() {
|
||||||
return XER.CA;
|
return XER.CA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
|
// Emulator state saving support.
|
||||||
|
|
||||||
#ifndef _STATE_H
|
#ifndef _STATE_H
|
||||||
#define _STATE_H
|
#define _STATE_H
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ void SetVolumeName(const std::string& _rFullPath)
|
||||||
{
|
{
|
||||||
if (g_pVolume)
|
if (g_pVolume)
|
||||||
{
|
{
|
||||||
|
// This code looks scary. Can the try/catch stuff be removed?
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
delete g_pVolume;
|
delete g_pVolume;
|
||||||
|
@ -91,4 +92,4 @@ bool IsWii()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
|
// Disc volume handler. It's here because Wii discs can consist of multiple volumes.
|
||||||
|
// GC discs are seen as one big volume.
|
||||||
|
|
||||||
#ifndef _VOLUMEHANDLER_H
|
#ifndef _VOLUMEHANDLER_H
|
||||||
#define _VOLUMEHANDLER_H
|
#define _VOLUMEHANDLER_H
|
||||||
|
|
||||||
|
@ -22,25 +25,20 @@
|
||||||
#include "CommonTypes.h"
|
#include "CommonTypes.h"
|
||||||
#include "Volume.h"
|
#include "Volume.h"
|
||||||
|
|
||||||
|
|
||||||
namespace VolumeHandler
|
namespace VolumeHandler
|
||||||
{
|
{
|
||||||
|
|
||||||
void SetVolumeName(const std::string& _rFullPath);
|
void SetVolumeName(const std::string& _rFullPath);
|
||||||
|
|
||||||
void SetVolumeDirectory(const std::string& _rFullPath, bool _bIsWii);
|
void SetVolumeDirectory(const std::string& _rFullPath, bool _bIsWii);
|
||||||
|
|
||||||
u32 Read32(u64 _Offset);
|
u32 Read32(u64 _Offset);
|
||||||
|
|
||||||
bool ReadToPtr(u8* ptr, u64 _dwOffset, u64 _dwLength);
|
bool ReadToPtr(u8* ptr, u64 _dwOffset, u64 _dwLength);
|
||||||
|
|
||||||
bool IsValid();
|
bool IsValid();
|
||||||
|
|
||||||
bool IsWii();
|
bool IsWii();
|
||||||
|
|
||||||
DiscIO::IVolume *GetVolume();
|
DiscIO::IVolume *GetVolume();
|
||||||
|
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
XFB_WIDTH = 640,
|
XFB_WIDTH = 640,
|
||||||
XFB_HEIGHT = 480, //480,
|
XFB_HEIGHT = 480, // 528 is max height.
|
||||||
XFB_BUF_HEIGHT = 538, //480,
|
XFB_BUF_HEIGHT = 538, //480,
|
||||||
// TODO: figure out what to do with PAL
|
// TODO: figure out what to do with PAL
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue