2009-09-08 12:08:10 +00:00
|
|
|
/* PCSX2 - PS2 Emulator for PCs
|
2010-05-03 14:08:02 +00:00
|
|
|
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
2010-04-25 00:31:27 +00:00
|
|
|
*
|
2009-09-08 12:08:10 +00:00
|
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
2009-02-09 21:15:56 +00:00
|
|
|
*
|
2009-09-08 12:08:10 +00:00
|
|
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
2009-02-09 21:15:56 +00:00
|
|
|
*/
|
|
|
|
|
2016-01-29 22:22:37 +00:00
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
#include "PrecompiledHeader.h"
|
|
|
|
#include "Common.h"
|
|
|
|
|
2009-11-14 12:02:56 +00:00
|
|
|
#include "R5900.h"
|
2009-02-09 21:15:56 +00:00
|
|
|
#include "R3000A.h"
|
|
|
|
#include "VUmicro.h"
|
|
|
|
#include "COP0.h"
|
2011-08-12 02:31:49 +00:00
|
|
|
#include "MTVU.h"
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-10-23 20:24:59 +00:00
|
|
|
#include "System/SysThreads.h"
|
2009-03-01 21:49:17 +00:00
|
|
|
#include "R5900Exceptions.h"
|
|
|
|
|
2009-11-14 12:02:56 +00:00
|
|
|
#include "Hardware.h"
|
2010-09-05 00:36:03 +00:00
|
|
|
#include "IPU/IPUdma.h"
|
2009-11-14 12:02:56 +00:00
|
|
|
|
2010-04-22 00:48:06 +00:00
|
|
|
#include "Elfheader.h"
|
2010-04-19 23:46:12 +00:00
|
|
|
#include "CDVD/CDVD.h"
|
|
|
|
#include "Patch.h"
|
2010-06-18 14:27:13 +00:00
|
|
|
#include "GameDatabase.h"
|
2010-04-19 23:46:12 +00:00
|
|
|
|
2014-08-21 11:01:07 +00:00
|
|
|
#include "../DebugTools/Breakpoints.h"
|
|
|
|
#include "R5900OpcodeTables.h"
|
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
using namespace R5900; // for R5900 disasm tools
|
|
|
|
|
|
|
|
s32 EEsCycle; // used to sync the IOP to the EE
|
|
|
|
u32 EEoCycle;
|
|
|
|
|
2009-10-05 02:15:49 +00:00
|
|
|
__aligned16 cpuRegisters cpuRegs;
|
|
|
|
__aligned16 fpuRegisters fpuRegs;
|
|
|
|
__aligned16 tlbs tlb[48];
|
2009-02-09 21:15:56 +00:00
|
|
|
R5900cpu *Cpu = NULL;
|
|
|
|
|
2010-04-19 23:46:12 +00:00
|
|
|
bool g_SkipBiosHack; // set at boot if the skip bios hack is on, reset before the game has started
|
|
|
|
bool g_GameStarted; // set when we reach the game's entry point or earlier if the entry point cannot be determined
|
2016-07-29 07:22:28 +00:00
|
|
|
bool g_GameLoading; // EELOAD has been called to load the game
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-07-11 15:43:28 +00:00
|
|
|
static const uint eeWaitCycles = 3072;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
bool eeEventTestIsActive = false;
|
|
|
|
|
2016-07-29 07:22:28 +00:00
|
|
|
u32 eeloadMain = 0;
|
|
|
|
|
2010-11-15 14:05:02 +00:00
|
|
|
extern SysMainMemory& GetVmMemory();
|
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
void cpuReset()
|
|
|
|
{
|
2011-08-12 02:31:49 +00:00
|
|
|
vu1Thread.WaitVU();
|
2010-11-15 14:05:02 +00:00
|
|
|
if (GetMTGS().IsOpen())
|
2009-11-23 06:54:24 +00:00
|
|
|
GetMTGS().WaitGS(); // GS better be done processing before we reset the EE, just in case.
|
2009-10-09 15:17:53 +00:00
|
|
|
|
2010-11-15 14:05:02 +00:00
|
|
|
GetVmMemory().ResetAll();
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-09-23 12:53:32 +00:00
|
|
|
memzero(cpuRegs);
|
|
|
|
memzero(fpuRegs);
|
|
|
|
memzero(tlb);
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-04-25 00:31:27 +00:00
|
|
|
cpuRegs.pc = 0xbfc00000; //set pc reg to stack
|
2009-12-14 14:36:13 +00:00
|
|
|
cpuRegs.CP0.n.Config = 0x440;
|
|
|
|
cpuRegs.CP0.n.Status.val= 0x70400004; //0x10900000 <-- wrong; // COP0 enabled | BEV = 1 | TS = 1
|
|
|
|
cpuRegs.CP0.n.PRid = 0x00002e20; // PRevID = Revision ID, same as R5900
|
2016-01-29 22:22:37 +00:00
|
|
|
fpuRegs.fprc[0] = 0x00002e30; // fpu Revision..
|
2009-12-14 14:36:13 +00:00
|
|
|
fpuRegs.fprc[31] = 0x01000001; // fpu Status/Control
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
g_nextEventCycle = cpuRegs.cycle + 4;
|
2009-02-09 21:15:56 +00:00
|
|
|
EEsCycle = 0;
|
|
|
|
EEoCycle = cpuRegs.cycle;
|
|
|
|
|
|
|
|
hwReset();
|
|
|
|
rcntInit();
|
|
|
|
psxReset();
|
2016-01-29 22:22:37 +00:00
|
|
|
|
2010-04-27 16:23:08 +00:00
|
|
|
extern void Deci2Reset(); // lazy, no good header for it yet.
|
|
|
|
Deci2Reset();
|
2010-04-19 23:46:12 +00:00
|
|
|
|
|
|
|
g_GameStarted = false;
|
2016-07-29 07:22:28 +00:00
|
|
|
g_GameLoading = false;
|
2010-04-27 13:12:03 +00:00
|
|
|
g_SkipBiosHack = EmuConfig.UseBOOT2Injection;
|
2010-04-19 23:46:12 +00:00
|
|
|
|
|
|
|
ElfCRC = 0;
|
2010-06-22 18:57:48 +00:00
|
|
|
DiscSerial = L"";
|
2010-04-19 23:46:12 +00:00
|
|
|
ElfEntry = -1;
|
2010-08-16 15:57:01 +00:00
|
|
|
|
2011-09-04 01:49:34 +00:00
|
|
|
// Probably not the right place, but it has to be done when the ram is actually initialized
|
|
|
|
if(USBsetRAM != 0)
|
|
|
|
USBsetRAM(iopMem->Main);
|
|
|
|
|
2010-08-16 15:57:01 +00:00
|
|
|
// FIXME: LastELF should be reset on media changes as well as on CPU resets, in
|
|
|
|
// the very unlikely case that a user swaps to another media source that "looks"
|
|
|
|
// the same (identical ELF names) but is actually different (devs actually could
|
|
|
|
// run into this while testing minor binary hacked changes to ISO images, which
|
|
|
|
// is why I found out about this) --air
|
2010-05-28 12:13:51 +00:00
|
|
|
LastELF = L"";
|
2016-07-29 07:22:28 +00:00
|
|
|
|
|
|
|
eeloadMain = 0;
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
2010-12-30 06:21:07 +00:00
|
|
|
void cpuShutdown()
|
|
|
|
{
|
|
|
|
hwShutdown();
|
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__ri void cpuException(u32 code, u32 bd)
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2009-03-20 01:26:38 +00:00
|
|
|
bool errLevel2, checkStatus;
|
2015-07-10 19:08:26 +00:00
|
|
|
u32 offset = 0;
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2009-11-14 12:02:56 +00:00
|
|
|
cpuRegs.branch = 0; // Tells the interpreter that an exception occurred during a branch.
|
2009-02-09 21:15:56 +00:00
|
|
|
cpuRegs.CP0.n.Cause = code & 0xffff;
|
|
|
|
|
2009-03-20 01:26:38 +00:00
|
|
|
if(cpuRegs.CP0.n.Status.b.ERL == 0)
|
|
|
|
{
|
|
|
|
//Error Level 0-1
|
|
|
|
errLevel2 = FALSE;
|
|
|
|
checkStatus = (cpuRegs.CP0.n.Status.b.BEV == 0); // for TLB/general exceptions
|
2010-04-25 00:31:27 +00:00
|
|
|
|
|
|
|
if (((code & 0x7C) >= 0x8) && ((code & 0x7C) <= 0xC))
|
2009-03-20 01:26:38 +00:00
|
|
|
offset = 0x0; //TLB Refill
|
2010-04-25 00:31:27 +00:00
|
|
|
else if ((code & 0x7C) == 0x0)
|
2009-03-20 01:26:38 +00:00
|
|
|
offset = 0x200; //Interrupt
|
2010-04-25 00:31:27 +00:00
|
|
|
else
|
2009-03-20 01:26:38 +00:00
|
|
|
offset = 0x180; // Everything else
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Error Level 2
|
|
|
|
errLevel2 = TRUE;
|
|
|
|
checkStatus = (cpuRegs.CP0.n.Status.b.DEV == 0); // for perf/debug exceptions
|
2010-04-25 00:31:27 +00:00
|
|
|
|
Lots of new code maintenance stuffs:
* Completely new assertion macros: pxAssert, pxAssertMsg, and pxFail, pxAssertDev (both which default to using a message). These replace *all* wxASSERT, DevAssert, and jASSUME varieties of macros. New macros borrow the best of all assertion worlds: MSVCRT, wxASSERT, and AtlAssume. :)
* Rewrote the Console namespace as a structure called IConsoleWriter, and created several varieties of ConsoleWriters for handling different states of log and console availability (should help reduce overhead of console logging nicely).
* More improvements to the PersistentThread model, using safely interlocked "Do*" style callbacks for starting and cleaning up threads.
* Fixed console logs so that they're readable in Win32 notepad again (the log writer adds CRs to naked LFs).
* Added AppInit.cpp -- contains constructor, destructor, OnInit, and command line parsing mess.
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1950 96395faa-99c1-11dd-bbfe-3dabce05a288
2009-10-04 08:27:27 +00:00
|
|
|
Console.Error("*PCSX2* FIX ME: Level 2 cpuException");
|
2010-04-25 00:31:27 +00:00
|
|
|
if ((code & 0x38000) <= 0x8000 )
|
2009-03-20 01:26:38 +00:00
|
|
|
{
|
|
|
|
//Reset / NMI
|
2009-02-09 21:15:56 +00:00
|
|
|
cpuRegs.pc = 0xBFC00000;
|
2009-10-29 13:32:40 +00:00
|
|
|
Console.Warning("Reset request");
|
2010-09-05 15:11:19 +00:00
|
|
|
cpuUpdateOperationMode();
|
2009-02-09 21:15:56 +00:00
|
|
|
return;
|
2010-04-25 00:31:27 +00:00
|
|
|
}
|
|
|
|
else if((code & 0x38000) == 0x10000)
|
2009-03-20 01:26:38 +00:00
|
|
|
offset = 0x80; //Performance Counter
|
2010-04-25 00:31:27 +00:00
|
|
|
else if((code & 0x38000) == 0x18000)
|
2009-03-20 01:26:38 +00:00
|
|
|
offset = 0x100; //Debug
|
2010-04-25 00:31:27 +00:00
|
|
|
else
|
Lots of new code maintenance stuffs:
* Completely new assertion macros: pxAssert, pxAssertMsg, and pxFail, pxAssertDev (both which default to using a message). These replace *all* wxASSERT, DevAssert, and jASSUME varieties of macros. New macros borrow the best of all assertion worlds: MSVCRT, wxASSERT, and AtlAssume. :)
* Rewrote the Console namespace as a structure called IConsoleWriter, and created several varieties of ConsoleWriters for handling different states of log and console availability (should help reduce overhead of console logging nicely).
* More improvements to the PersistentThread model, using safely interlocked "Do*" style callbacks for starting and cleaning up threads.
* Fixed console logs so that they're readable in Win32 notepad again (the log writer adds CRs to naked LFs).
* Added AppInit.cpp -- contains constructor, destructor, OnInit, and command line parsing mess.
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1950 96395faa-99c1-11dd-bbfe-3dabce05a288
2009-10-04 08:27:27 +00:00
|
|
|
Console.Error("Unknown Level 2 Exception!! Cause %x", code);
|
2009-03-20 01:26:38 +00:00
|
|
|
}
|
2010-04-25 00:31:27 +00:00
|
|
|
|
|
|
|
if (cpuRegs.CP0.n.Status.b.EXL == 0)
|
2009-03-20 01:26:38 +00:00
|
|
|
{
|
|
|
|
cpuRegs.CP0.n.Status.b.EXL = 1;
|
2010-04-25 00:31:27 +00:00
|
|
|
if (bd)
|
2009-03-20 01:26:38 +00:00
|
|
|
{
|
2009-10-29 13:32:40 +00:00
|
|
|
Console.Warning("branch delay!!");
|
2009-03-20 01:26:38 +00:00
|
|
|
cpuRegs.CP0.n.EPC = cpuRegs.pc - 4;
|
|
|
|
cpuRegs.CP0.n.Cause |= 0x80000000;
|
2010-04-25 00:31:27 +00:00
|
|
|
}
|
|
|
|
else
|
2009-03-20 01:26:38 +00:00
|
|
|
{
|
|
|
|
cpuRegs.CP0.n.EPC = cpuRegs.pc;
|
|
|
|
cpuRegs.CP0.n.Cause &= ~0x80000000;
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
2010-04-25 00:31:27 +00:00
|
|
|
}
|
|
|
|
else
|
2009-03-20 01:26:38 +00:00
|
|
|
{
|
2010-04-25 00:31:27 +00:00
|
|
|
offset = 0x180; //Override the cause
|
2009-10-29 13:32:40 +00:00
|
|
|
if (errLevel2) Console.Warning("cpuException: Status.EXL = 1 cause %x", code);
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2009-03-20 01:26:38 +00:00
|
|
|
if (checkStatus)
|
|
|
|
cpuRegs.pc = 0x80000000 + offset;
|
2010-04-25 00:31:27 +00:00
|
|
|
else
|
2009-03-20 01:26:38 +00:00
|
|
|
cpuRegs.pc = 0xBFC00200 + offset;
|
2010-04-25 00:31:27 +00:00
|
|
|
|
2010-09-05 15:11:19 +00:00
|
|
|
cpuUpdateOperationMode();
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
2010-04-25 00:31:27 +00:00
|
|
|
void cpuTlbMiss(u32 addr, u32 bd, u32 excode)
|
2009-03-20 01:26:38 +00:00
|
|
|
{
|
2014-12-02 22:10:58 +00:00
|
|
|
// Avoid too much spamming on the interpreter
|
|
|
|
if (Cpu != &intCpu || IsDebugBuild) {
|
|
|
|
Console.Error("cpuTlbMiss pc:%x, cycl:%x, addr: %x, status=%x, code=%x",
|
|
|
|
cpuRegs.pc, cpuRegs.cycle, addr, cpuRegs.CP0.n.Status.val, excode);
|
|
|
|
}
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
cpuRegs.CP0.n.BadVAddr = addr;
|
|
|
|
cpuRegs.CP0.n.Context &= 0xFF80000F;
|
|
|
|
cpuRegs.CP0.n.Context |= (addr >> 9) & 0x007FFFF0;
|
|
|
|
cpuRegs.CP0.n.EntryHi = (addr & 0xFFFFE000) | (cpuRegs.CP0.n.EntryHi & 0x1FFF);
|
|
|
|
|
2014-06-20 11:15:27 +00:00
|
|
|
cpuRegs.pc -= 4;
|
2014-02-22 16:23:31 +00:00
|
|
|
cpuException(excode, bd);
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void cpuTlbMissR(u32 addr, u32 bd) {
|
|
|
|
cpuTlbMiss(addr, bd, EXC_CODE_TLBL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cpuTlbMissW(u32 addr, u32 bd) {
|
|
|
|
cpuTlbMiss(addr, bd, EXC_CODE_TLBS);
|
|
|
|
}
|
|
|
|
|
|
|
|
// sets a branch test to occur some time from an arbitrary starting point.
|
2010-09-05 15:38:14 +00:00
|
|
|
__fi void cpuSetNextEvent( u32 startCycle, s32 delta )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
// typecast the conditional to signed so that things don't blow up
|
|
|
|
// if startCycle is greater than our next branch cycle.
|
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
if( (int)(g_nextEventCycle - startCycle) > delta )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-09-05 15:38:14 +00:00
|
|
|
g_nextEventCycle = startCycle + delta;
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// sets a branch to occur some time from the current cycle
|
2010-09-05 15:38:14 +00:00
|
|
|
__fi void cpuSetNextEventDelta( s32 delta )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-09-05 15:38:14 +00:00
|
|
|
cpuSetNextEvent( cpuRegs.cycle, delta );
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
2010-09-05 15:11:19 +00:00
|
|
|
// tests the cpu cycle against the given start and delta values.
|
2009-02-09 21:15:56 +00:00
|
|
|
// Returns true if the delta time has passed.
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi int cpuTestCycle( u32 startCycle, s32 delta )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
// typecast the conditional to signed so that things don't explode
|
|
|
|
// if the startCycle is ahead of our current cpu cycle.
|
|
|
|
|
|
|
|
return (int)(cpuRegs.cycle - startCycle) >= delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
// tells the EE to run the branch test the next time it gets a chance.
|
2010-09-05 15:38:14 +00:00
|
|
|
__fi void cpuSetEvent()
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-09-05 15:38:14 +00:00
|
|
|
g_nextEventCycle = cpuRegs.cycle;
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void cpuClearInt( uint i )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2014-08-15 04:32:11 +00:00
|
|
|
pxAssume( i < 32 );
|
2009-02-09 21:15:56 +00:00
|
|
|
cpuRegs.interrupt &= ~(1 << i);
|
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
static __fi void TESTINT( u8 n, void (*callback)() )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
if( !(cpuRegs.interrupt & (1 << n)) ) return;
|
|
|
|
|
|
|
|
if( cpuTestCycle( cpuRegs.sCycle[n], cpuRegs.eCycle[n] ) )
|
|
|
|
{
|
|
|
|
cpuClearInt( n );
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
else
|
2010-09-05 15:38:14 +00:00
|
|
|
cpuSetNextEvent( cpuRegs.sCycle[n], cpuRegs.eCycle[n] );
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
2010-09-05 00:36:03 +00:00
|
|
|
// [TODO] move this function to LegacyDmac.cpp, and remove most of the DMAC-related headers from
|
|
|
|
// being included into R5900.cpp.
|
2010-08-09 04:10:38 +00:00
|
|
|
static __fi void _cpuTestInterrupts()
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-09-04 14:11:50 +00:00
|
|
|
if (!dmacRegs.ctrl.DMAE || (psHu8(DMAC_ENABLER+2) & 1))
|
2010-04-15 00:32:58 +00:00
|
|
|
{
|
|
|
|
//Console.Write("DMAC Disabled or suspended");
|
|
|
|
return;
|
|
|
|
}
|
2009-02-09 21:15:56 +00:00
|
|
|
/* These are 'pcsx2 interrupts', they handle asynchronous stuff
|
|
|
|
that depends on the cycle timings */
|
|
|
|
|
2010-07-22 12:20:11 +00:00
|
|
|
TESTINT(DMAC_VIF1, vif1Interrupt);
|
2011-07-24 13:02:50 +00:00
|
|
|
TESTINT(DMAC_GIF, gifInterrupt);
|
2010-07-22 12:20:11 +00:00
|
|
|
TESTINT(DMAC_SIF0, EEsif0Interrupt);
|
|
|
|
TESTINT(DMAC_SIF1, EEsif1Interrupt);
|
2011-07-24 13:02:50 +00:00
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
// Profile-guided Optimization (sorta)
|
|
|
|
// The following ints are rarely called. Encasing them in a conditional
|
|
|
|
// as follows helps speed up most games.
|
|
|
|
|
2012-04-07 01:48:34 +00:00
|
|
|
if( cpuRegs.interrupt & 0x60F19 ) // Bits 0 3 4 8 9 10 11 17 18( 1100000111100011001 )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-07-22 12:20:11 +00:00
|
|
|
TESTINT(DMAC_VIF0, vif0Interrupt);
|
2010-05-07 22:10:36 +00:00
|
|
|
|
2010-07-22 12:20:11 +00:00
|
|
|
TESTINT(DMAC_FROM_IPU, ipu0Interrupt);
|
|
|
|
TESTINT(DMAC_TO_IPU, ipu1Interrupt);
|
2010-05-07 22:10:36 +00:00
|
|
|
|
2010-07-22 12:20:11 +00:00
|
|
|
TESTINT(DMAC_FROM_SPR, SPRFROMinterrupt);
|
|
|
|
TESTINT(DMAC_TO_SPR, SPRTOinterrupt);
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-07-22 12:20:11 +00:00
|
|
|
TESTINT(DMAC_MFIFO_VIF, vifMFIFOInterrupt);
|
|
|
|
TESTINT(DMAC_MFIFO_GIF, gifMFIFOInterrupt);
|
2012-04-07 01:48:34 +00:00
|
|
|
|
|
|
|
TESTINT(VIF_VU0_FINISH, vif0VUFinish);
|
|
|
|
TESTINT(VIF_VU1_FINISH, vif1VUFinish);
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
static __fi void _cpuTestTIMR()
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
cpuRegs.CP0.n.Count += cpuRegs.cycle-s_iLastCOP0Cycle;
|
|
|
|
s_iLastCOP0Cycle = cpuRegs.cycle;
|
|
|
|
|
|
|
|
// fixme: this looks like a hack to make up for the fact that the TIMR
|
2010-09-05 15:38:14 +00:00
|
|
|
// doesn't yet have a proper mechanism for setting itself up on a nextEventCycle.
|
2009-02-09 21:15:56 +00:00
|
|
|
// A proper fix would schedule the TIMR to trigger at a specific cycle anytime
|
|
|
|
// the Count or Compare registers are modified.
|
|
|
|
|
|
|
|
if ( (cpuRegs.CP0.n.Status.val & 0x8000) &&
|
|
|
|
cpuRegs.CP0.n.Count >= cpuRegs.CP0.n.Compare && cpuRegs.CP0.n.Count < cpuRegs.CP0.n.Compare+1000 )
|
|
|
|
{
|
2009-10-29 13:32:40 +00:00
|
|
|
Console.WriteLn( Color_Magenta, "timr intr: %x, %x", cpuRegs.CP0.n.Count, cpuRegs.CP0.n.Compare);
|
2009-02-09 21:15:56 +00:00
|
|
|
cpuException(0x808000, cpuRegs.branch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
static __fi void _cpuTestPERF()
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2009-03-10 19:21:54 +00:00
|
|
|
// Perfs are updated when read by games (COP0's MFC0/MTC0 instructions), so we need
|
|
|
|
// only update them at semi-regular intervals to keep cpuRegs.cycle from wrapping
|
|
|
|
// around twice on us btween updates. Hence this function is called from the cpu's
|
|
|
|
// Counters update.
|
|
|
|
|
2009-03-11 19:08:08 +00:00
|
|
|
COP0_UpdatePCCR();
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Checks the COP0.Status for exception enablings.
|
|
|
|
// Exception handling for certain modes is *not* currently supported, this function filters
|
|
|
|
// them out. Exceptions while the exception handler is active (EIE), or exceptions of any
|
|
|
|
// level other than 0 are ignored here.
|
|
|
|
|
2010-06-22 23:10:40 +00:00
|
|
|
static bool cpuIntsEnabled(int Interrupt)
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-06-29 16:06:12 +00:00
|
|
|
bool IntType = !!(cpuRegs.CP0.n.Status.val & Interrupt); //Choose either INTC or DMAC, depending on what called it
|
2010-06-22 23:10:40 +00:00
|
|
|
|
2010-09-05 15:11:19 +00:00
|
|
|
return IntType && cpuRegs.CP0.n.Status.b.EIE && cpuRegs.CP0.n.Status.b.IE &&
|
|
|
|
!cpuRegs.CP0.n.Status.b.EXL && (cpuRegs.CP0.n.Status.b.ERL == 0);
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
// if cpuRegs.cycle is greater than this cycle, should check cpuEventTest for updates
|
|
|
|
u32 g_nextEventCycle = 0;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
// Shared portion of the branch test, called from both the Interpreter
|
|
|
|
// and the recompiler. (moved here to help alleviate redundant code)
|
2010-09-05 15:38:14 +00:00
|
|
|
__fi void _cpuEventTest_Shared()
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-06-28 18:03:54 +00:00
|
|
|
ScopedBool etest(eeEventTestIsActive);
|
2010-09-05 15:38:14 +00:00
|
|
|
g_nextEventCycle = cpuRegs.cycle + eeWaitCycles;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-09-05 15:11:19 +00:00
|
|
|
// ---- INTC / DMAC (CPU-level Exceptions) -----------------
|
|
|
|
// Done first because exceptions raised during event tests need to be postponed a few
|
|
|
|
// cycles (fixes Grandia II [PAL], which does a spin loop on a vsync and expects to
|
|
|
|
// be able to read the value before the exception handler clears it).
|
|
|
|
|
|
|
|
uint mask = intcInterrupt() | dmacInterrupt();
|
|
|
|
if (cpuIntsEnabled(mask)) cpuException(mask, cpuRegs.branch);
|
|
|
|
|
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
// ---- Counters -------------
|
2009-08-31 03:47:05 +00:00
|
|
|
// Important: the vsync counter must be the first to be checked. It includes emulation
|
|
|
|
// escape/suspend hooks, and it's really a good idea to suspend/resume emulation before
|
2010-09-05 15:11:19 +00:00
|
|
|
// doing any actual meaningful branchtest logic.
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
if( cpuTestCycle( nextsCounter, nextCounter ) )
|
|
|
|
{
|
2009-08-25 15:38:48 +00:00
|
|
|
rcntUpdate();
|
2009-02-09 21:15:56 +00:00
|
|
|
_cpuTestPERF();
|
|
|
|
}
|
|
|
|
|
2009-08-31 03:47:05 +00:00
|
|
|
rcntUpdate_hScanline();
|
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
_cpuTestTIMR();
|
|
|
|
|
|
|
|
// ---- Interrupts -------------
|
2010-09-05 15:11:19 +00:00
|
|
|
// These are basically just DMAC-related events, which also piggy-back the same bits as
|
|
|
|
// the PS2's own DMA channel IRQs and IRQ Masks.
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-09-05 15:11:19 +00:00
|
|
|
_cpuTestInterrupts();
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
// ---- IOP -------------
|
2010-09-05 15:38:14 +00:00
|
|
|
// * It's important to run a iopEventTest before calling ExecuteBlock. This
|
2009-02-09 21:15:56 +00:00
|
|
|
// is because the IOP does not always perform branch tests before returning
|
|
|
|
// (during the prev branch) and also so it can act on the state the EE has
|
|
|
|
// given it before executing any code.
|
|
|
|
//
|
|
|
|
// * The IOP cannot always be run. If we run IOP code every time through the
|
2010-09-05 15:38:14 +00:00
|
|
|
// cpuEventTest, the IOP generally starts to run way ahead of the EE.
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2009-08-31 03:47:05 +00:00
|
|
|
EEsCycle += cpuRegs.cycle - EEoCycle;
|
|
|
|
EEoCycle = cpuRegs.cycle;
|
|
|
|
|
|
|
|
if( EEsCycle > 0 )
|
2010-09-05 15:38:14 +00:00
|
|
|
iopEventAction = true;
|
2009-08-31 03:47:05 +00:00
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
iopEventTest();
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
if( iopEventAction )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
//if( EEsCycle < -450 )
|
Lots of new code maintenance stuffs:
* Completely new assertion macros: pxAssert, pxAssertMsg, and pxFail, pxAssertDev (both which default to using a message). These replace *all* wxASSERT, DevAssert, and jASSUME varieties of macros. New macros borrow the best of all assertion worlds: MSVCRT, wxASSERT, and AtlAssume. :)
* Rewrote the Console namespace as a structure called IConsoleWriter, and created several varieties of ConsoleWriters for handling different states of log and console availability (should help reduce overhead of console logging nicely).
* More improvements to the PersistentThread model, using safely interlocked "Do*" style callbacks for starting and cleaning up threads.
* Fixed console logs so that they're readable in Win32 notepad again (the log writer adds CRs to naked LFs).
* Added AppInit.cpp -- contains constructor, destructor, OnInit, and command line parsing mess.
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1950 96395faa-99c1-11dd-bbfe-3dabce05a288
2009-10-04 08:27:27 +00:00
|
|
|
// Console.WriteLn( " IOP ahead by: %d cycles", -EEsCycle );
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-09-05 15:11:19 +00:00
|
|
|
EEsCycle = psxCpu->ExecuteBlock( EEsCycle );
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
iopEventAction = false;
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ---- VU0 -------------
|
2010-09-05 15:38:14 +00:00
|
|
|
// We're in a EventTest. All dynarec registers are flushed
|
2010-02-24 07:20:33 +00:00
|
|
|
// so there is no need to freeze registers here.
|
|
|
|
CpuVU0->ExecuteBlock();
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
// Note: We don't update the VU1 here because it runs it's micro-programs in
|
|
|
|
// one shot always. That is, when a program is executed the VU1 doesn't even
|
|
|
|
// bother to return until the program is completely finished.
|
|
|
|
|
|
|
|
// ---- Schedule Next Event Test --------------
|
|
|
|
|
|
|
|
if( EEsCycle > 192 )
|
|
|
|
{
|
|
|
|
// EE's running way ahead of the IOP still, so we should branch quickly to give the
|
|
|
|
// IOP extra timeslices in short order.
|
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
cpuSetNextEventDelta( 48 );
|
|
|
|
//Console.Warning( "EE ahead of the IOP -- Rapid Event! %d", EEsCycle );
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The IOP could be running ahead/behind of us, so adjust the iop's next branch by its
|
|
|
|
// relative position to the EE (via EEsCycle)
|
2010-09-05 15:38:14 +00:00
|
|
|
cpuSetNextEventDelta( ((g_iopNextEventCycle-psxRegs.cycle)*8) - EEsCycle );
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
// Apply the hsync counter's nextCycle
|
2010-09-05 15:38:14 +00:00
|
|
|
cpuSetNextEvent( hsyncCounter.sCycle, hsyncCounter.CycleT );
|
2009-02-09 21:15:56 +00:00
|
|
|
|
|
|
|
// Apply vsync and other counter nextCycles
|
2010-09-05 15:38:14 +00:00
|
|
|
cpuSetNextEvent( nextsCounter, nextCounter );
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__ri void cpuTestINTCInts()
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-06-29 16:06:12 +00:00
|
|
|
// Check the COP0's Status register for general interrupt disables, and the 0x400
|
|
|
|
// bit (which is INTC master toggle).
|
2010-06-22 23:10:40 +00:00
|
|
|
if( !cpuIntsEnabled(0x400) ) return;
|
2010-06-29 16:06:12 +00:00
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
if( (psHu32(INTC_STAT) & psHu32(INTC_MASK)) == 0 ) return;
|
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
cpuSetNextEventDelta( 4 );
|
|
|
|
if(eeEventTestIsActive && (iopCycleEE > 0))
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-09-05 15:38:14 +00:00
|
|
|
iopBreak += iopCycleEE; // record the number of cycles the IOP didn't run.
|
|
|
|
iopCycleEE = 0;
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void cpuTestDMACInts()
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-06-29 16:06:12 +00:00
|
|
|
// Check the COP0's Status register for general interrupt disables, and the 0x800
|
|
|
|
// bit (which is the DMAC master toggle).
|
2010-06-22 23:10:40 +00:00
|
|
|
if( !cpuIntsEnabled(0x800) ) return;
|
2009-02-09 21:15:56 +00:00
|
|
|
|
2010-04-25 00:31:27 +00:00
|
|
|
if ( ( (psHu16(0xe012) & psHu16(0xe010)) == 0) &&
|
2009-02-09 21:15:56 +00:00
|
|
|
( (psHu16(0xe010) & 0x8000) == 0) ) return;
|
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
cpuSetNextEventDelta( 4 );
|
|
|
|
if(eeEventTestIsActive && (iopCycleEE > 0))
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-09-05 15:38:14 +00:00
|
|
|
iopBreak += iopCycleEE; // record the number of cycles the IOP didn't run.
|
|
|
|
iopCycleEE = 0;
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void cpuTestTIMRInts() {
|
2009-02-09 21:15:56 +00:00
|
|
|
if ((cpuRegs.CP0.n.Status.val & 0x10007) == 0x10001) {
|
|
|
|
_cpuTestPERF();
|
|
|
|
_cpuTestTIMR();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void cpuTestHwInts() {
|
2009-02-09 21:15:56 +00:00
|
|
|
cpuTestINTCInts();
|
|
|
|
cpuTestDMACInts();
|
|
|
|
cpuTestTIMRInts();
|
|
|
|
}
|
|
|
|
|
2010-08-09 04:10:38 +00:00
|
|
|
__fi void CPU_INT( EE_EventType n, s32 ecycle)
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
2010-06-27 12:56:35 +00:00
|
|
|
// EE events happen 8 cycles in the future instead of whatever was requested.
|
2010-05-07 22:10:36 +00:00
|
|
|
// This can be used on games with PATH3 masking issues for example, or when
|
|
|
|
// some FMV look bad.
|
2010-06-27 12:56:35 +00:00
|
|
|
if(CHECK_EETIMINGHACK) ecycle = 8;
|
2010-01-21 23:25:54 +00:00
|
|
|
|
2009-02-09 21:15:56 +00:00
|
|
|
cpuRegs.interrupt|= 1 << n;
|
|
|
|
cpuRegs.sCycle[n] = cpuRegs.cycle;
|
|
|
|
cpuRegs.eCycle[n] = ecycle;
|
|
|
|
|
|
|
|
// Interrupt is happening soon: make sure both EE and IOP are aware.
|
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
if( ecycle <= 28 && iopCycleEE > 0 )
|
2009-02-09 21:15:56 +00:00
|
|
|
{
|
|
|
|
// If running in the IOP, force it to break immediately into the EE.
|
|
|
|
// the EE's branch test is due to run.
|
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
iopBreak += iopCycleEE; // record the number of cycles the IOP didn't run.
|
|
|
|
iopCycleEE = 0;
|
2009-02-09 21:15:56 +00:00
|
|
|
}
|
|
|
|
|
2010-09-05 15:38:14 +00:00
|
|
|
cpuSetNextEventDelta( cpuRegs.eCycle[n] );
|
2010-04-19 23:46:12 +00:00
|
|
|
}
|
|
|
|
|
2010-06-18 14:27:13 +00:00
|
|
|
// Called from recompilers; __fastcall define is mandatory.
|
2010-04-19 23:46:12 +00:00
|
|
|
void __fastcall eeGameStarting()
|
|
|
|
{
|
2010-06-18 14:27:13 +00:00
|
|
|
if (!g_GameStarted)
|
|
|
|
{
|
2010-07-09 16:51:48 +00:00
|
|
|
//Console.WriteLn( Color_Green, "(R5900) ELF Entry point! [addr=0x%08X]", ElfEntry );
|
2010-04-19 23:46:12 +00:00
|
|
|
g_GameStarted = true;
|
2016-07-29 07:22:28 +00:00
|
|
|
g_GameLoading = false;
|
2010-06-18 14:27:13 +00:00
|
|
|
GetCoreThread().GameStartingInThread();
|
2010-07-09 16:51:48 +00:00
|
|
|
|
2010-06-24 16:04:27 +00:00
|
|
|
// GameStartingInThread may issue a reset of the cpu and/or recompilers. Check for and
|
|
|
|
// handle such things here:
|
|
|
|
Cpu->CheckExecutionState();
|
2010-06-18 14:27:13 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Console.WriteLn( Color_Green, "(R5900) Re-executed ELF Entry point (ignored) [addr=0x%08X]", ElfEntry );
|
2010-04-19 23:46:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-18 14:27:13 +00:00
|
|
|
// Called from recompilers; __fastcall define is mandatory.
|
2016-07-29 07:22:28 +00:00
|
|
|
void __fastcall eeloadHook()
|
2010-04-19 23:46:12 +00:00
|
|
|
{
|
|
|
|
const wxString &elf_override = GetCoreThread().GetElfOverride();
|
|
|
|
|
2010-05-28 10:52:04 +00:00
|
|
|
if (!elf_override.IsEmpty())
|
|
|
|
cdvdReloadElfInfo(L"host:" + elf_override);
|
|
|
|
else
|
|
|
|
cdvdReloadElfInfo();
|
2010-04-19 23:46:12 +00:00
|
|
|
|
2016-07-29 07:22:28 +00:00
|
|
|
wxString discelf;
|
|
|
|
int disctype = GetPS2ElfName(discelf);
|
2010-04-19 23:46:12 +00:00
|
|
|
|
2010-06-25 02:35:27 +00:00
|
|
|
std::string elfname;
|
2016-07-29 07:22:28 +00:00
|
|
|
if (cpuRegs.GPR.n.a0.SD[0] >= 2) // argc >= 2
|
|
|
|
elfname = (char*)PSM(memRead32(cpuRegs.GPR.n.a1.UD[0] + 4)); // argv[1]
|
2010-05-28 10:33:15 +00:00
|
|
|
|
2016-07-29 07:22:28 +00:00
|
|
|
if (g_SkipBiosHack && elfname.empty())
|
2010-05-28 10:33:15 +00:00
|
|
|
{
|
2016-07-29 07:22:28 +00:00
|
|
|
std::string elftoload;
|
|
|
|
if (!elf_override.IsEmpty())
|
|
|
|
{
|
|
|
|
elftoload = "host:";
|
|
|
|
elftoload += elf_override.ToUTF8();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (disctype == 2)
|
|
|
|
elftoload = discelf.ToUTF8();
|
|
|
|
}
|
2010-05-28 10:33:15 +00:00
|
|
|
|
2016-07-29 07:22:28 +00:00
|
|
|
if (!elftoload.empty())
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
// FIXME: you'd think that changing argc and argv would work but no, need to work out why not
|
|
|
|
// This method would support adding command line arguments to homebrew (and is generally less hacky)
|
|
|
|
// It works if you hook on rom0:PS2LOGO loading, but not on the first call with no arguments
|
|
|
|
cpuRegs.GPR.n.a0.SD[0] = 2; // argc = 2
|
|
|
|
// argv[0] = "EELOAD"
|
|
|
|
strcpy((char*)PSM(cpuRegs.GPR.n.a1.UD[0] + 0x40), "EELOAD");
|
|
|
|
memWrite32(cpuRegs.GPR.n.a1.UD[0] + 0, cpuRegs.GPR.n.a1.UD[0] + 0x40);
|
|
|
|
// argv[1] = elftoload
|
|
|
|
strcpy((char*)PSM(cpuRegs.GPR.n.a1.UD[0] + 0x47), elftoload.c_str());
|
|
|
|
memWrite32(cpuRegs.GPR.n.a1.UD[0] + 4, cpuRegs.GPR.n.a1.UD[0] + 0x47);
|
|
|
|
memWrite32(cpuRegs.GPR.n.a1.UD[0] + 8, 0);
|
|
|
|
g_GameLoading = true;
|
|
|
|
return;
|
|
|
|
#else
|
|
|
|
// The strings are all 64-bit aligned. Why? I don't know, but they are
|
|
|
|
for (u32 osdsys_str = EELOAD_START; osdsys_str < EELOAD_START + EELOAD_SIZE; osdsys_str += 8) {
|
|
|
|
if (!strcmp((char*)PSM(osdsys_str), "rom0:OSDSYS")) {
|
|
|
|
for (u32 osdsys_ptr = osdsys_str - 4; osdsys_ptr >= EELOAD_START; osdsys_ptr -= 4) {
|
|
|
|
if (memRead32(osdsys_ptr) == osdsys_str) {
|
|
|
|
strcpy((char*)PSM(cpuRegs.GPR.n.a1.UD[0] + 0x40), elftoload.c_str());
|
|
|
|
memWrite32(osdsys_ptr, cpuRegs.GPR.n.a1.UD[0] + 0x40);
|
|
|
|
g_GameLoading = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2010-04-19 23:46:12 +00:00
|
|
|
}
|
2016-07-29 07:22:28 +00:00
|
|
|
|
|
|
|
if (!g_GameStarted && disctype == 2 && elfname == discelf)
|
|
|
|
g_GameLoading = true;
|
2010-04-19 23:46:12 +00:00
|
|
|
}
|
2014-08-21 11:01:07 +00:00
|
|
|
|
|
|
|
inline bool isBranchOrJump(u32 addr)
|
|
|
|
{
|
|
|
|
u32 op = memRead32(addr);
|
|
|
|
const OPCODE& opcode = GetInstruction(op);
|
|
|
|
|
|
|
|
return (opcode.flags & IS_BRANCH) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The next two functions return 0 if no breakpoint is needed,
|
|
|
|
// 1 if it's needed on the current pc, 2 if it's needed in the delay slot
|
2016-02-26 07:04:28 +00:00
|
|
|
// 3 if needed in both
|
2014-08-21 11:01:07 +00:00
|
|
|
|
|
|
|
int isBreakpointNeeded(u32 addr)
|
|
|
|
{
|
2016-02-26 07:04:28 +00:00
|
|
|
int bpFlags = 0;
|
2014-08-21 11:01:07 +00:00
|
|
|
if (CBreakPoints::IsAddressBreakPoint(addr))
|
2016-02-26 07:04:28 +00:00
|
|
|
bpFlags += 1;
|
2014-08-21 11:01:07 +00:00
|
|
|
|
|
|
|
// there may be a breakpoint in the delay slot
|
|
|
|
if (isBranchOrJump(addr) && CBreakPoints::IsAddressBreakPoint(addr+4))
|
2016-02-26 07:04:28 +00:00
|
|
|
bpFlags += 2;
|
2014-08-21 11:01:07 +00:00
|
|
|
|
2016-02-26 07:04:28 +00:00
|
|
|
return bpFlags;
|
2014-08-21 11:01:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int isMemcheckNeeded(u32 pc)
|
|
|
|
{
|
|
|
|
if (CBreakPoints::GetNumMemchecks() == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
u32 addr = pc;
|
|
|
|
if (isBranchOrJump(addr))
|
|
|
|
addr += 4;
|
|
|
|
|
|
|
|
u32 op = memRead32(addr);
|
|
|
|
const OPCODE& opcode = GetInstruction(op);
|
|
|
|
|
|
|
|
if (opcode.flags & IS_MEMORY)
|
|
|
|
return addr == pc ? 1 : 2;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|