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:
hrydgard 2008-10-16 22:06:06 +00:00
parent 3ae2d556ab
commit 21b0d596e4
26 changed files with 558 additions and 634 deletions

View File

@ -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;
}
} }

View File

@ -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();

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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++)

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}
} }

View File

@ -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)

View File

@ -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;

View File

@ -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,
}; };

View File

@ -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];

View File

@ -46,7 +46,6 @@ enum
FL_CHECKEXCEPTIONS = (1<<16), FL_CHECKEXCEPTIONS = (1<<16),
}; };
enum enum
{ {
OPTYPE_INVALID , OPTYPE_INVALID ,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}; };