diff --git a/Source/Core/Core/Src/ActionReplay.cpp b/Source/Core/Core/Src/ActionReplay.cpp index aca8c7f904..f6fff3f47e 100644 --- a/Source/Core/Core/Src/ActionReplay.cpp +++ b/Source/Core/Core/Src/ActionReplay.cpp @@ -15,36 +15,60 @@ // Official SVN repository and contact information can be found at // 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 #include + #include "StringUtil.h" #include "IniFile.h" #include "HW/Memmap.h" #include "ActionReplay.h" -u32 cmd_addr; -u8 cmd; -u32 addr; -u32 data; -u8 subtype; -u8 w; -u8 type; -std::vector::const_iterator iter; -std::vector arCodes; -ARCode code; +namespace { +// 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::const_iterator iter; + +static std::vector 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) { std::vector lines; ARCode currentCode; arCodes.clear(); - if (!ini.GetLines("ActionReplay", lines)) return; + if (!ini.GetLines("ActionReplay", lines)) + return; // no codes found. for (std::vector::const_iterator it = lines.begin(); it != lines.end(); ++it) { std::string line = *it; - std::vector pieces; SplitString(line, " ", pieces); if (pieces.size() == 2 && pieces[0].size() == 8 && pieces[1].size() == 8) @@ -96,6 +120,12 @@ void LoadActionReplayCodes(IniFile &ini) arCodes.push_back(currentCode); } +void ActionReplayRunAllActive() +{ + for (std::vector::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. // 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; // SubType selector - switch(subtype) + switch (subtype) { - case 0x0: // Ram write (and fill) - { - DoARSubtype_RamWriteAndFill(); continue; - } - case 0x1: // Write to pointer - { - DoARSubtype_WriteToPointer(); continue; - } - case 0x2: // Add code - { - DoARSubtype_AddCode(); continue; - } - case 0x3: // Master Code & Write to CCXXXXXX - { - DoARSubtype_MasterCodeAndWriteToCCXXXXXX(); continue;// TODO: This is not implemented yet - } - default: // non-specific z codes (hacks) - { - DoARSubtype_Other(); continue; - } + case 0x0: // Ram write (and fill) + DoARSubtype_RamWriteAndFill(); + continue; + case 0x1: // Write to pointer + DoARSubtype_WriteToPointer(); + continue; + case 0x2: // Add code + DoARSubtype_AddCode(); + continue; + case 0x3: // Master Code & Write to CCXXXXXX + DoARSubtype_MasterCodeAndWriteToCCXXXXXX(); + continue; // TODO: This is not implemented yet + default: // non-specific z codes (hacks) + DoARSubtype_Other(); + continue; } } } 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); switch ((addr >> 25) & 0x03) { - case 0x00: // Byte write - { - u8 repeat = data >> 8; - for (int i = 0; i <= repeat; i++) { - Memory::Write_U8(data & 0xFF, new_addr + i); - } - break; + case 0x00: // Byte write + { + u8 repeat = data >> 8; + for (int i = 0; i <= repeat; i++) { + Memory::Write_U8(data & 0xFF, new_addr + i); } + break; + } - case 0x01: // Short write - { - u16 repeat = data >> 16; - for (int i = 0; i <= repeat; i++) { - Memory::Write_U16(data & 0xFFFF, new_addr + i * 2); - } - break; + case 0x01: // Short write + { + u16 repeat = data >> 16; + for (int i = 0; i <= repeat; i++) { + Memory::Write_U16(data & 0xFFFF, new_addr + i * 2); } + break; + } - - case 0x02: // Dword write - { - Memory::Write_U32(data, new_addr); - break; - } - default: break; // TODO(Omega): maybe add a PanicAlert here? + case 0x02: // Dword write + Memory::Write_U32(data, new_addr); + break; + default: + break; // TODO(Omega): maybe add a PanicAlert here? } } } + + void DoARSubtype_WriteToPointer() { - if(w < 0x8) + if (w < 0x8) { u32 new_addr = ( addr | 0x80000000); switch ((addr >> 25) & 0x03) { - case 0x00: // Byte write to pointer [40] - { - u32 ptr = Memory::Read_U32(new_addr); - u8 thebyte = data & 0xFF; - u32 offset = data >> 8; - Memory::Write_U8(thebyte, ptr + offset); - break; - } + case 0x00: // Byte write to pointer [40] + { + u32 ptr = Memory::Read_U32(new_addr); + u8 thebyte = data & 0xFF; + u32 offset = data >> 8; + Memory::Write_U8(thebyte, ptr + offset); + break; + } - case 0x01: // Short write to pointer [42] - { - u32 ptr = Memory::Read_U32(new_addr); - u16 theshort = data & 0xFFFF; - u32 offset = (data >> 16) << 1; - Memory::Write_U16(theshort, ptr + offset); - break; - } + case 0x01: // Short write to pointer [42] + { + u32 ptr = Memory::Read_U32(new_addr); + u16 theshort = data & 0xFFFF; + u32 offset = (data >> 16) << 1; + Memory::Write_U16(theshort, ptr + offset); + break; + } - case 0x02: // Dword write to pointer [44] - { - Memory::Write_U32(data, Memory::Read_U32(new_addr)); break; - } + case 0x02: // Dword write to pointer [44] + Memory::Write_U32(data, Memory::Read_U32(new_addr)); + 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() { - if(w < 0x8) + if (w < 0x8) { u32 new_addr = ( (addr & 0x01FFFFFF) | 0x81FFFFFF); - switch((addr >> 25) & 0x03) + switch ((addr >> 25) & 0x03) { case 0x0: // Byte add - { - Memory::Write_U8(Memory::Read_U8(new_addr) + (data & 0xFF), new_addr); break; - } + Memory::Write_U8(Memory::Read_U8(new_addr) + (data & 0xFF), new_addr); + break; case 0x1: // Short add - { - Memory::Write_U16(Memory::Read_U16(new_addr) + (data & 0xFFFF), new_addr); break; - } + Memory::Write_U16(Memory::Read_U16(new_addr) + (data & 0xFFFF), new_addr); + break; case 0x2: // DWord add - { - Memory::Write_U32(Memory::Read_U32(new_addr) + data, new_addr); break; - } + Memory::Write_U32(Memory::Read_U32(new_addr) + data, new_addr); + break; case 0x3: // Float add (not working?) { union { u32 u; float f;} fu, d; @@ -267,7 +290,8 @@ void DoARSubtype_AddCode() Memory::Write_U32(fu.u, new_addr); break; } - default: break; + default: + break; } } } @@ -276,10 +300,10 @@ void DoARSubtype_MasterCodeAndWriteToCCXXXXXX() { // code not yet implemented - TODO - //if(w < 0x8) + //if (w < 0x8) //{ // u32 new_addr = (addr | 0x80000000); - // switch((new_addr >> 25) & 0x03) + // switch ((new_addr >> 25) & 0x03) // { // case 0x2: // { @@ -291,76 +315,68 @@ void DoARSubtype_MasterCodeAndWriteToCCXXXXXX() void DoARSubtype_Other() { - switch (cmd & 0xFE) - { - case 0x90: if (Memory::Read_U32(addr) == data) return; // IF 32 bit equal, exit - case 0x08: // IF 8 bit equal, execute next opcode - case 0x48: // (double) - { - if (Memory::Read_U16(addr) != (data & 0xFFFF)) { - if (++iter == code.ops.end()) return; - if (cmd == 0x48) if (++iter == code.ops.end()) return; - } - break; - } - case 0x0A: // IF 16 bit equal, execute next opcode - case 0x4A: // (double) - { - if (Memory::Read_U16(addr) != (data & 0xFFFF)) { - if (++iter == code.ops.end()) return; - if (cmd == 0x4A) if (++iter == code.ops.end()) return; - } - break; - } - case 0x0C: // IF 32 bit equal, execute next opcode - case 0x4C: // (double) - { - if (Memory::Read_U32(addr) != data) { - if (++iter == code.ops.end()) return; - if (cmd == 0x4C) if (++iter == code.ops.end()) return; - } - break; - } - case 0x10: // IF NOT 8 bit equal, execute next opcode - case 0x50: // (double) - { - if (Memory::Read_U8(addr) == (data & 0xFF)) { - if (++iter == code.ops.end()) return; - if (cmd == 0x50) if (++iter == code.ops.end()) return; - } - break; - } - case 0x12: // IF NOT 16 bit equal, execute next opcode - case 0x52: // (double) - { - if (Memory::Read_U16(addr) == (data & 0xFFFF)) { - if (++iter == code.ops.end()) return; - if (cmd == 0x52) if (++iter == code.ops.end()) return; - } - break; - } - case 0x14: // IF NOT 32 bit equal, execute next opcode - case 0x54: // (double) - { - if (Memory::Read_U32(addr) == data) { - if (++iter == code.ops.end()) return; - if (cmd == 0x54) if (++iter == code.ops.end()) return; - } - break; - } - case 0xC4: // "Master Code" - configure the AR - { - u8 number = data & 0xFF; - if (number == 0) - { - // Normal master code - execute once. - } 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; - } + switch (cmd & 0xFE) + { + case 0x90: + // Eh, this must be wrong. Should it really fallthrough? + if (Memory::Read_U32(addr) == data) return; // IF 32 bit equal, exit + case 0x08: // IF 8 bit equal, execute next opcode + case 0x48: // (double) + if (Memory::Read_U16(addr) != (data & 0xFFFF)) { + if (++iter == code.ops.end()) return; + if (cmd == 0x48) if (++iter == code.ops.end()) return; + } + break; + case 0x0A: // IF 16 bit equal, execute next opcode + case 0x4A: // (double) + if (Memory::Read_U16(addr) != (data & 0xFFFF)) { + if (++iter == code.ops.end()) return; + if (cmd == 0x4A) if (++iter == code.ops.end()) return; + } + break; + case 0x0C: // IF 32 bit equal, execute next opcode + case 0x4C: // (double) + if (Memory::Read_U32(addr) != data) { + if (++iter == code.ops.end()) return; + if (cmd == 0x4C) if (++iter == code.ops.end()) return; + } + break; + case 0x10: // IF NOT 8 bit equal, execute next opcode + case 0x50: // (double) + if (Memory::Read_U8(addr) == (data & 0xFF)) { + if (++iter == code.ops.end()) return; + if (cmd == 0x50) if (++iter == code.ops.end()) return; + } + break; + case 0x12: // IF NOT 16 bit equal, execute next opcode + case 0x52: // (double) + if (Memory::Read_U16(addr) == (data & 0xFFFF)) { + if (++iter == code.ops.end()) return; + if (cmd == 0x52) if (++iter == code.ops.end()) return; + } + break; + case 0x14: // IF NOT 32 bit equal, execute next opcode + case 0x54: // (double) + if (Memory::Read_U32(addr) == data) { + if (++iter == code.ops.end()) return; + if (cmd == 0x54) if (++iter == code.ops.end()) return; + } + break; + case 0xC4: // "Master Code" - configure the AR + { + u8 number = data & 0xFF; + if (number == 0) + { + // Normal master code - execute once. + } 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; + } } \ No newline at end of file diff --git a/Source/Core/Core/Src/ActionReplay.h b/Source/Core/Core/Src/ActionReplay.h index f83a8c6f8b..0747ef7072 100644 --- a/Source/Core/Core/Src/ActionReplay.h +++ b/Source/Core/Core/Src/ActionReplay.h @@ -26,13 +26,7 @@ struct ARCode { bool active; }; -extern std::vector arCodes; - +void ActionReplayRunAllActive(); void RunActionReplayCode(const ARCode &arcode, bool nowIsBootup); void LoadActionReplayCodes(IniFile &ini); -void DoARSubtype_RamWriteAndFill(); -void DoARSubtype_WriteToPointer(); -void DoARSubtype_AddCode(); -void DoARSubtype_MasterCodeAndWriteToCCXXXXXX(); -void DoARSubtype_Other(); diff --git a/Source/Core/Core/Src/Console.cpp b/Source/Core/Core/Src/Console.cpp index b2ef092e8d..c9dd42a5e4 100644 --- a/Source/Core/Core/Src/Console.cpp +++ b/Source/Core/Core/Src/Console.cpp @@ -14,6 +14,7 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ + #include #include @@ -22,32 +23,16 @@ #include "HW/Memmap.h" #include "PowerPC/PPCAnalyst.h" #include "PowerPC/PPCTables.h" -#include "Console.h" #include "CoreTiming.h" #include "Core.h" #include "PowerPC/Jit64/JitCache.h" #include "PowerPC/SymbolDB.h" #include "PowerPCDisasm.h" +#include "Console.h" #define CASE(x) else if (memcmp(cmd, x, 4*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) { CASE1("jits") diff --git a/Source/Core/Core/Src/Console.h b/Source/Core/Core/Src/Console.h index 7353f50aab..96e1063b4e 100644 --- a/Source/Core/Core/Src/Console.h +++ b/Source/Core/Core/Src/Console.h @@ -14,11 +14,14 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ + + +// Simple debugging console currently residing in the Logging window. Not used much. + #ifndef _CONSOLE_H #define _CONSOLE_H void Console_Submit(const char *cmd); -// void StartConsoleThread(); #endif diff --git a/Source/Core/Core/Src/Core.h b/Source/Core/Core/Src/Core.h index d4ab0da840..8c3606804c 100644 --- a/Source/Core/Core/Src/Core.h +++ b/Source/Core/Core/Src/Core.h @@ -14,6 +14,14 @@ // Official SVN repository and contact information can be found at // 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 #define _CORE_H @@ -36,10 +44,7 @@ namespace Core bool Init(const SCoreStartupParameter _CoreParameter); void Stop(); - // Get state bool SetState(EState _State); - - // Get state EState GetState(); // Save/Load state @@ -64,8 +69,7 @@ namespace Core int SyncTrace(); void SetBlockStart(u32 addr); void StopTrace(); - -} // end of namespace Core +} // namespace #endif diff --git a/Source/Core/Core/Src/CoreParameter.h b/Source/Core/Core/Src/CoreParameter.h index ea6f22dbc5..f304e10b7c 100644 --- a/Source/Core/Core/Src/CoreParameter.h +++ b/Source/Core/Core/Src/CoreParameter.h @@ -14,6 +14,7 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ + #ifndef _COREPARAMETER_H #define _COREPARAMETER_H diff --git a/Source/Core/Core/Src/CoreTiming.cpp b/Source/Core/Core/Src/CoreTiming.cpp index 7c79751197..e1436f118e 100644 --- a/Source/Core/Core/Src/CoreTiming.cpp +++ b/Source/Core/Core/Src/CoreTiming.cpp @@ -293,16 +293,13 @@ void Advance() externalEventSection.Enter(); while (tsFirst) { - //MessageBox(0,"yay",0,0); Event *next = tsFirst->next; AddEventToQueue(tsFirst); tsFirst = next; } externalEventSection.Leave(); - // we are out of run, downcount = -3 int cyclesExecuted = slicelength - downcount; - // sliceLength = downac globalTimer += cyclesExecuted; @@ -312,7 +309,6 @@ void Advance() { // LOG(GEKKO, "[Scheduler] %s (%lld, %lld) ", // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time); - event_types[first->type].callback(first->userdata, (int)(globalTimer - first->time)); Event *next = first->next; delete first; @@ -326,7 +322,6 @@ void Advance() if (!first) { LOG(GEKKO, "WARNING - no events in queue. Setting downcount to 10000"); - // PanicAlert? downcount += 10000; } else @@ -379,4 +374,4 @@ std::string GetScheduledEventsSummary() return text; } -}; // end of namespace +} // namespace diff --git a/Source/Core/Core/Src/CoreTiming.h b/Source/Core/Core/Src/CoreTiming.h index 09f4ea4f9f..ed98ef4a85 100644 --- a/Source/Core/Core/Src/CoreTiming.h +++ b/Source/Core/Core/Src/CoreTiming.h @@ -24,6 +24,13 @@ // 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. +// 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 @@ -33,7 +40,6 @@ namespace CoreTiming { - void Init(); void Shutdown(); @@ -43,10 +49,6 @@ u64 GetTicks(); u64 GetIdleTicks(); 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. int RegisterEvent(const char *name, TimedCallback callback); diff --git a/Source/Core/Core/Src/HW/DSP.cpp b/Source/Core/Core/Src/HW/DSP.cpp index a70f642558..344c04ba00 100644 --- a/Source/Core/Core/Src/HW/DSP.cpp +++ b/Source/Core/Core/Src/HW/DSP.cpp @@ -17,10 +17,9 @@ // 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. -// Since 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 -// event that runs at 4khz, that eats audio from the fifo, and all the rest will follow. -// Then we will have homebrew audio. +// 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 thereforce schedule an +// event that runs at 4khz, that eats audio from the fifo. Thus, we have homebrew audio. // 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 diff --git a/Source/Core/Core/Src/HW/Memmap.cpp b/Source/Core/Core/Src/HW/Memmap.cpp index f76dc760d5..a2155f0f2e 100644 --- a/Source/Core/Core/Src/HW/Memmap.cpp +++ b/Source/Core/Core/Src/HW/Memmap.cpp @@ -156,6 +156,16 @@ void InitHWMemFuncs() hwRead16 [i] = HW_Default_Read; hwRead32 [i] = HW_Default_Read; hwRead64 [i] = HW_Default_Read; + + // To prevent Dolphin from crashing when running Wii executables in Gc mode. + hwWriteWii8 [i] = HW_Default_Write; + hwWriteWii16[i] = HW_Default_Write; + hwWriteWii32[i] = HW_Default_Write; + hwWriteWii64[i] = HW_Default_Write; + hwReadWii8 [i] = HW_Default_Read; + hwReadWii16 [i] = HW_Default_Read; + hwReadWii32 [i] = HW_Default_Read; + hwReadWii64 [i] = HW_Default_Read; } for (int i = 0; i < BLOCKSIZE; i++) diff --git a/Source/Core/Core/Src/LogManager.cpp b/Source/Core/Core/Src/LogManager.cpp index 0ccad1e621..1daf0f8a57 100644 --- a/Source/Core/Core/Src/LogManager.cpp +++ b/Source/Core/Core/Src/LogManager.cpp @@ -14,6 +14,7 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ + #include #include "Common.h" diff --git a/Source/Core/Core/Src/LogManager.h b/Source/Core/Core/Src/LogManager.h index 1fb10be8cd..f36adfe96a 100644 --- a/Source/Core/Core/Src/LogManager.h +++ b/Source/Core/Core/Src/LogManager.h @@ -14,6 +14,10 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ + + +// Dolphin Logging framework. Needs a good ol' spring cleaning methinks. + #ifndef _LOGMANAGER_H #define _LOGMANAGER_H diff --git a/Source/Core/Core/Src/MemTools.cpp b/Source/Core/Core/Src/MemTools.cpp index 154db1c499..e5c04982bc 100644 --- a/Source/Core/Core/Src/MemTools.cpp +++ b/Source/Core/Core/Src/MemTools.cpp @@ -15,7 +15,9 @@ // Official SVN repository and contact information can be found at // 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 diff --git a/Source/Core/Core/Src/MemTools.h b/Source/Core/Core/Src/MemTools.h index c3d847bbcf..93053f56fd 100644 --- a/Source/Core/Core/Src/MemTools.h +++ b/Source/Core/Core/Src/MemTools.h @@ -20,52 +20,11 @@ #include "Common.h" -typedef u32 EAddr; namespace EMM { - enum WR - { - 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 - + typedef u32 EAddr; 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 diff --git a/Source/Core/Core/Src/PatchEngine.cpp b/Source/Core/Core/Src/PatchEngine.cpp index df0182f8c1..cd8e94e4e0 100644 --- a/Source/Core/Core/Src/PatchEngine.cpp +++ b/Source/Core/Core/Src/PatchEngine.cpp @@ -16,15 +16,15 @@ // http://code.google.com/p/dolphin-emu/ // 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] 0x802904b4 in US released // [Tue Aug 21 2007] [18:30:53] 0x80294d54 in EUR Demo version // [Tue Aug 21 2007] [18:31:10] 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] // 0x80020394=dword,0x4e800020 -// #define BLR_OP 0x4e800020 #include #include @@ -36,9 +36,14 @@ using namespace Common; +namespace +{ + std::vector onLoad; std::vector onFrame; +} // namespace + void LoadPatchSection(const char *section, std::vector &patches, IniFile &ini) { std::vector keys; @@ -113,8 +118,5 @@ void PatchEngine_ApplyFramePatches() void PatchEngine_ApplyARPatches() { - for (std::vector::const_iterator iter = arCodes.begin(); iter != arCodes.end(); ++iter) { - if (iter->active) - RunActionReplayCode(*iter, false); - } + ActionReplayRunAllActive(); } \ No newline at end of file diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp index a047f8c73e..793c5d9ed8 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp @@ -39,130 +39,77 @@ using namespace Gen; using namespace PowerPC; 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 -//RBX - point to base of memory map -//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 - ? +// Unfeatures: +// * Does not recompile all instructions. Often falls back to inserting a CALL to the corresponding JIT function. -//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: -//Make sure that all generated code and all emulator state sits under the 2GB boundary so that -//RIP addressing can be used easily. Windows will always allocate static code under the 2GB boundary. -//Also make sure to use VirtualAlloc and specify EXECUTE permission. +// Register allocation +// RAX - Generic quicktemp register +// RBX - point to base of memory map +// 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 -//a statically allocated base pointer in a register, and use displacement addressing off of that. -//A candidate for this is a non vol like R15, since we would not like to have to do a RIP load -//to restore it all the time. -//No wait a minute, why not just keep the unprotected mappings below 2GB? +// IMPORTANT: +// Make sure that all generated code and all emulator state sits under the 2GB boundary so that +// RIP addressing can be used easily. Windows will always allocate static code under the 2GB boundary. +// Also make sure to use VirtualAlloc and specify EXECUTE permission. -//Another question to be addressed is if it is smart to have a static pointer reg to the base -//of the PowerPC reg area. -//Pro: Smaller accesses for GPR (8-bit displacement good enough only for gprs) -//Con: A taken nonvol register (may not be so bad) +// Open questions +// * Should there be any statically allocated registers? r3, r4, r5, r8, r0 come to mind.. maybe sp +// * Does it make sense to finish off the remaining non-jitted instructions? Seems we are hitting diminishing returns. +// * 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 - -//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. +// Other considerations //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 //optimization manuals, though. -//IDEA: FPU exception emulation support by having all fpu blocks writeNTA to a spot in memory that -//is protected if FPU exceptions are enabled. The exception handler would then have to run the -//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. +// We support block linking. Reserve space at the exits of every block for a full 5-byte jmp. Save 16-bit offsets +// from the starts of each block, marking the exits so that they can be nicely patched at any time. -//Block linking is needed. Reserve space at the end of every block for a full 5-byte jmp. Save 16-bit offsets -//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. -//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 -//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 -//Alternatively, icbi instruction SHOULD mark where we can't compile - -//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 +// 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: 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 -// Plan: 1. Byteswap Dolphin DONE! -// 2. Fix timing WORKING -// 3. Lay groundwork for x64 JIT WORKING -// 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 +// Idea - 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 -//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 - (single precision can be used in write gather) - (this is valid on Wii too when using the VM emulator) - + (single precision can be used in write gather pipe, specialized fast check added) * AMD only - use movaps instead of movapd when loading ps from memory? * 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: stfd -- guaranteed in memory cmpl @@ -186,9 +133,6 @@ cntlzwx bcctrx WriteBigEData - -detect immediates in stb stw sth - TODO lha srawx @@ -196,29 +140,18 @@ addic_rc addex subfcx subfex -000000000A42BD7F mov ecx,0FCBF41BAh -000000000A42BD85 call CInterpreter::fmaddx (5BA3A0h) -000000000A42BD8A mov ecx,0FC8D0132h -000000000A42BD90 call CInterpreter::fmulx (5BA540h) -000000000A42BD95 mov ecx,0FC85202Ah -000000000A42BD9B call CInterpreter::faddx (5BA220h) -000000000A42BDA0 mov ecx,0FC81113Ah -000000000A42BDA6 call CInterpreter::fmaddx (5BA3A0h) -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) + +fmaddx +fmulx +faddx +fnegx +frspx +frsqrtex +ps_sum0 */ -// 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 { extern u32 m_BlockStart; @@ -232,7 +165,7 @@ namespace Jit64 void Init() { - jo.optimizeStack = true; + jo.optimizeStack = false; jo.enableBlocklink = true; // Speed boost, but not 100% safe #ifdef _M_X64 jo.enableFastMem = Core::GetStartupParameter().bUseFastMem; @@ -243,6 +176,7 @@ namespace Jit64 jo.fpAccurateFlags = true; jo.optimizeGatherPipe = true; jo.interpretFPU = false; + jo.fastInterrupts = false; } void WriteCallInterpreter(UGeckoInstruction _inst) @@ -280,6 +214,7 @@ namespace Jit64 static const bool ImHereDebug = false; static const bool ImHereLog = false; static std::map been_here; + void ImHere() { 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 b.checkedEntry = start; b.runCount = 0; + + // Downcount flag check. The last block decremented downcounter, and the flag should still be available. FixupBranch skip = J_CC(CC_NBE); MOV(32, M(&PC), Imm32(js.blockStart)); - JMP(Asm::doTiming, true); + JMP(Asm::doTiming, true); // downcount hit zero - go doTiming. SetJumpTarget(skip); const u8 *normalEntry = GetCodePtr(); @@ -396,6 +333,17 @@ namespace Jit64 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) { ADD(32, M(&b.runCount), Imm8(1)); #ifdef _WIN32 @@ -439,13 +387,12 @@ namespace Jit64 } // const GekkoOpInfo *info = GetOpInfo(); - // if (js.isLastInstruction) + if (jo.interpretFPU && PPCTables::UsesFPU(ops[i].inst)) Default(ops[i].inst); else PPCTables::CompileInstruction(ops[i].inst); - // else - // Default(ops[i].inst); + gpr.SanityCheck(); fpr.SanityCheck(); if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32) diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit.h b/Source/Core/Core/Src/PowerPC/Jit64/Jit.h index e2cb716055..6dbae8b73e 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit.h +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit.h @@ -15,9 +15,9 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -// Low hanging fruit: -// all used in zelda -// negx +// ======================== +// See comments in Jit.cpp. +// ======================== #ifndef _JIT_H #define _JIT_H @@ -26,14 +26,10 @@ #include "JitCache.h" #include "x64Emitter.h" - -// ======================================================================================= // 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. -// -------------- #define JIT_OFF_OPTIONS - namespace Jit64 { struct JitStats @@ -80,6 +76,7 @@ namespace Jit64 bool enableFastMem; bool optimizeGatherPipe; bool interpretFPU; + bool fastInterrupts; }; extern JitState js; diff --git a/Source/Core/Core/Src/PowerPC/PPCAnalyst.cpp b/Source/Core/Core/Src/PowerPC/PPCAnalyst.cpp index b9a5705b97..131b82707f 100644 --- a/Source/Core/Core/Src/PowerPC/PPCAnalyst.cpp +++ b/Source/Core/Core/Src/PowerPC/PPCAnalyst.cpp @@ -30,17 +30,20 @@ // and what functions calls it. That is, we will have an incomplete call graph, // but only missing indirect branches. -// The results of this analysis are currently not really used for anything, other than -// finding function boundaries so that we can find, fingerprint and detect library functions. +// The results of this analysis is displayed in the code browsing sections at the bottom left +// 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 { using namespace std; -// VERY ugly. TODO: remove. PPCAnalyst::CodeOp *codebuffer; -enum { +enum +{ CODEBUFFER_SIZE = 32000, }; diff --git a/Source/Core/Core/Src/PowerPC/PPCTables.cpp b/Source/Core/Core/Src/PowerPC/PPCTables.cpp index 55e4856345..dae64e3e4c 100644 --- a/Source/Core/Core/Src/PowerPC/PPCTables.cpp +++ b/Source/Core/Core/Src/PowerPC/PPCTables.cpp @@ -41,8 +41,6 @@ struct GekkoOPTemplate 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_infoTable4[1024]; static GekkoOPInfo *m_infoTable19[1024]; diff --git a/Source/Core/Core/Src/PowerPC/PPCTables.h b/Source/Core/Core/Src/PowerPC/PPCTables.h index 8aecbc2115..15d7b922dc 100644 --- a/Source/Core/Core/Src/PowerPC/PPCTables.h +++ b/Source/Core/Core/Src/PowerPC/PPCTables.h @@ -46,7 +46,6 @@ enum FL_CHECKEXCEPTIONS = (1<<16), }; - enum { OPTYPE_INVALID , diff --git a/Source/Core/Core/Src/PowerPC/PowerPC.cpp b/Source/Core/Core/Src/PowerPC/PowerPC.cpp index b9f6dd643c..62a7eae02b 100644 --- a/Source/Core/Core/Src/PowerPC/PowerPC.cpp +++ b/Source/Core/Core/Src/PowerPC/PowerPC.cpp @@ -37,269 +37,271 @@ 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++) - { - ppcState.gpr[i] = 0; - riPS0(i) = 0; - riPS1(i) = 0; - } + case MODE_INTERPRETER: // Switching from JIT to interpreter + Jit64::ClearCache(); // Remove all those nasty JIT patches. + break; - 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; + 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 Init() +void SingleStep() +{ + switch (mode) { - 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; + case MODE_INTERPRETER: + Interpreter::SingleStep(); + break; + case MODE_JIT: + Jit64::Core::SingleStep(); + break; } +} - void Shutdown() +void RunLoop() +{ + state = CPU_RUNNING; + switch (mode) { - // Shutdown both execution engines. Doesn't matter which one is active. - Jit64::Core::Shutdown(); - Interpreter::Shutdown(); + case MODE_INTERPRETER: + Interpreter::Run(); + break; + case MODE_JIT: + Jit64::Core::Run(); + break; } + Host_UpdateDisasmDialog(); +} - void SetMode(CoreMode new_mode) - { - if (new_mode == mode) - return; // We don't need to do anything. - - 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() +void Start() +{ + state = Core::g_CoreStartupParameter.bEnableDebugging ? CPU_RUNNINGDEBUG : CPU_RUNNING; + if (Core::bReadTrace || Core::bWriteTrace) { state = CPU_RUNNING; - switch (mode) - { - case MODE_INTERPRETER: - Interpreter::Run(); - break; - case MODE_JIT: - Jit64::Core::Run(); - break; - } - Host_UpdateDisasmDialog(); } + Host_UpdateDisasmDialog(); +} - void Start() - { - state = Core::g_CoreStartupParameter.bEnableDebugging ? CPU_RUNNINGDEBUG : CPU_RUNNING; - if (Core::bReadTrace || Core::bWriteTrace) - { - state = CPU_RUNNING; - } - 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; - void Pause() - { - state = CPU_STEPPING; - Host_UpdateDisasmDialog(); + LOG(GEKKO, "EXCEPTION_SYSCALL (PC=%08x)",PC); + ppcState.Exceptions &= ~EXCEPTION_SYSCALL; + SRR1 |= 0x02; //recoverable } - - void Stop() + else if (ppcState.Exceptions & EXCEPTION_DSI) { - state = CPU_POWERDOWN; - Host_UpdateDisasmDialog(); + SRR0 = PC; // re-execute the instruction + SRR1 = MSR & 0x0780FF77; + NPC = 0x80000300; + + LOG(GEKKO, "EXCEPTION_DSI"); + ppcState.Exceptions &= ~EXCEPTION_DSI; + //SRR1 |= 0x02; //make recoverable ? } - - void CheckExceptions() + else if (ppcState.Exceptions & EXCEPTION_ISI) { - // This check is unnecessary in JIT mode. However, it probably doesn't really hurt. - if (!ppcState.Exceptions) - return; + SRR0 = PC; + SRR1 = (MSR & 0x0780FF77) | 0x40000000; + NPC = 0x80000400; - // TODO(ector): - // gcemu uses the mask 0x87C0FFFF instead of 0x0780FF77 - // Investigate! + 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; - 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) + LOG(GEKKO, "EXCEPTION_ALIGNMENT"); + ppcState.Exceptions &= ~EXCEPTION_ALIGNMENT; + //SRR1 |= 0x02; //make recoverable ? + } + + // EXTERNAL INTTERUPT + else if (MSR & 0x0008000) + { + if (ppcState.Exceptions & EXCEPTION_EXTERNAL_INT) { - SRR0 = PC; // re-execute the instruction - SRR1 = MSR & 0x0780FF77; - NPC = 0x80000300; + // Pokemon gets this "too early", it hasn't a handler yet + ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT; // clear exception - 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; + NPC = 0x80000500; + SRR1 = (MSR & 0x0780FF77); + + LOG(GEKKO, "EXCEPTION_EXTERNAL_INT"); - LOG(GEKKO, "EXCEPTION_ALIGNMENT"); - 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 !?! } - - // EXTERNAL INTTERUPT - else if (MSR & 0x0008000) + else if (ppcState.Exceptions & EXCEPTION_DECREMENTER) { - if (ppcState.Exceptions & EXCEPTION_EXTERNAL_INT) - { - // Pokemon gets this "too early", it hasn't a handler yet - ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT; // clear exception + SRR0 = NPC; + SRR1 = MSR & 0x0000FF77; + NPC = 0x80000900; - SRR0 = NPC; - NPC = 0x80000500; - SRR1 = (MSR & 0x0780FF77); - - LOG(GEKKO, "EXCEPTION_EXTERNAL_INT"); + ppcState.Exceptions &= ~EXCEPTION_DECREMENTER; - 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); - } + LOG(GEKKO, "EXCEPTION_DECREMENTER"); + SRR1 |= 0x02; //make recoverable } - MSR &= ~0x0008000; // clear EE-bit so interrupts aren't possible anymore - } - - void OnIdle(u32 _uThreadAddr) - { - u32 nextThread = Memory::Read_U32(_uThreadAddr); - //do idle skipping - if (nextThread == 0) + else { - 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 - void OnIdleDC(void) + MSR &= ~0x0008000; // clear EE-bit so interrupts aren't possible anymore +} + +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(); } } +//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 diff --git a/Source/Core/Core/Src/PowerPC/PowerPC.h b/Source/Core/Core/Src/PowerPC/PowerPC.h index b97b79ee0c..ab3dbf2605 100644 --- a/Source/Core/Core/Src/PowerPC/PowerPC.h +++ b/Source/Core/Core/Src/PowerPC/PowerPC.h @@ -31,6 +31,7 @@ namespace PowerPC MODE_JIT, }; + // This contains the entire state of the emulated PowerPC "Gekko" CPU. struct GC_ALIGNED64(PowerPCState) { u32 mojs[128]; // Try to isolate the regs from other variables in the cache. @@ -88,7 +89,7 @@ namespace PowerPC void OnIdleDC(void); } -// Special registers +// Easy register access macros. #define HID2 ((UReg_HID2&)PowerPC::ppcState.spr[SPR_HID2]) #define DMAU (*(UReg_DMAU*)&PowerPC::ppcState.spr[SPR_DMAU]) #define DMAL (*(UReg_DMAL*)&PowerPC::ppcState.spr[SPR_DMAL]) @@ -115,15 +116,16 @@ namespace PowerPC #define TL PowerPC::ppcState.spr[SPR_TL] #define TU PowerPC::ppcState.spr[SPR_TU] - #define rPS0(i) (*(double*)(&PowerPC::ppcState.ps[i][0])) #define rPS1(i) (*(double*)(&PowerPC::ppcState.ps[i][1])) #define riPS0(i) (*(u64*)(&PowerPC::ppcState.ps[i][0])) #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) { 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; } - - #endif diff --git a/Source/Core/Core/Src/State.h b/Source/Core/Core/Src/State.h index 2dc5798c95..90d1c17ef0 100644 --- a/Source/Core/Core/Src/State.h +++ b/Source/Core/Core/Src/State.h @@ -15,6 +15,8 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ +// Emulator state saving support. + #ifndef _STATE_H #define _STATE_H diff --git a/Source/Core/Core/Src/VolumeHandler.cpp b/Source/Core/Core/Src/VolumeHandler.cpp index 908048c397..74016568e3 100644 --- a/Source/Core/Core/Src/VolumeHandler.cpp +++ b/Source/Core/Core/Src/VolumeHandler.cpp @@ -31,6 +31,7 @@ void SetVolumeName(const std::string& _rFullPath) { if (g_pVolume) { + // This code looks scary. Can the try/catch stuff be removed? try { delete g_pVolume; @@ -91,4 +92,4 @@ bool IsWii() return false; } -} +} // namespace diff --git a/Source/Core/Core/Src/VolumeHandler.h b/Source/Core/Core/Src/VolumeHandler.h index ce64e59ba5..22d398a139 100644 --- a/Source/Core/Core/Src/VolumeHandler.h +++ b/Source/Core/Core/Src/VolumeHandler.h @@ -15,6 +15,9 @@ // Official SVN repository and contact information can be found at // 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 #define _VOLUMEHANDLER_H @@ -22,25 +25,20 @@ #include "CommonTypes.h" #include "Volume.h" - namespace VolumeHandler { void SetVolumeName(const std::string& _rFullPath); - void SetVolumeDirectory(const std::string& _rFullPath, bool _bIsWii); u32 Read32(u64 _Offset); - bool ReadToPtr(u8* ptr, u64 _dwOffset, u64 _dwLength); bool IsValid(); - bool IsWii(); DiscIO::IVolume *GetVolume(); -} +} // namespace #endif - diff --git a/Source/Plugins/Plugin_VideoOGL/Src/XFB.cpp b/Source/Plugins/Plugin_VideoOGL/Src/XFB.cpp index a8ffceda52..cdb5fb8932 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/XFB.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/XFB.cpp @@ -28,7 +28,7 @@ enum { XFB_WIDTH = 640, - XFB_HEIGHT = 480, //480, + XFB_HEIGHT = 480, // 528 is max height. XFB_BUF_HEIGHT = 538, //480, // TODO: figure out what to do with PAL };