38 changes: 27 additions & 11 deletions Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp
Expand Up @@ -47,6 +47,14 @@ void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate)
pThis->Flush();
}

void CEXIMemoryCard::CmdDoneCallback(u64 userdata, int cyclesLate)
{
int card_index = (int)userdata;
CEXIMemoryCard* pThis = (CEXIMemoryCard*)ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARD, card_index);
if (pThis)
pThis->CmdDone();
}

CEXIMemoryCard::CEXIMemoryCard(const int index)
: card_index(index)
, m_bDirty(false)
Expand All @@ -56,7 +64,8 @@ CEXIMemoryCard::CEXIMemoryCard(const int index)
m_strFilename = "Movie.raw";

// we're potentially leaking events here, since there's no UnregisterEvent until emu shutdown, but I guess it's inconsequential
et_this_card = CoreTiming::RegisterEvent((card_index == 0) ? "memcardA" : "memcardB", FlushCallback);
et_this_card = CoreTiming::RegisterEvent((card_index == 0) ? "memcardFlushA" : "memcardFlushB", FlushCallback);
et_cmd_done = CoreTiming::RegisterEvent((card_index == 0) ? "memcardDoneA" : "memcardDoneB", CmdDoneCallback);

interruptSwitch = 0;
m_bInterruptSet = 0;
Expand Down Expand Up @@ -178,6 +187,21 @@ bool CEXIMemoryCard::IsPresent()
return true;
}

void CEXIMemoryCard::CmdDone()
{
status |= MC_STATUS_READY;
status &= ~MC_STATUS_BUSY;

m_bInterruptSet = 1;
m_bDirty = true;
}

void CEXIMemoryCard::CmdDoneLater(u64 cycles)
{
CoreTiming::RemoveEvent(et_cmd_done);
CoreTiming::ScheduleEvent(cycles, et_cmd_done, (u64)card_index);
}

void CEXIMemoryCard::SetCS(int cs)
{
// So that memory card won't be invalidated during flushing
Expand All @@ -201,11 +225,7 @@ void CEXIMemoryCard::SetCS(int cs)

//???

status |= MC_STATUS_READY;
status &= ~MC_STATUS_BUSY;

m_bInterruptSet = 1;
m_bDirty = true;
CmdDoneLater(5000);
}
break;

Expand All @@ -232,11 +252,7 @@ void CEXIMemoryCard::SetCS(int cs)
address = (address & ~0x1FF) | ((address+1) & 0x1FF);
}

status |= MC_STATUS_READY;
status &= ~MC_STATUS_BUSY;

m_bInterruptSet = 1;
m_bDirty = true;
CmdDoneLater(5000);
}

// Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec)
Expand Down
11 changes: 10 additions & 1 deletion Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h
Expand Up @@ -47,9 +47,18 @@ class CEXIMemoryCard : public IEXIDevice
// through the userdata parameter, so that it can then call Flush on the right card.
static void FlushCallback(u64 userdata, int cyclesLate);

// Scheduled when a command that required delayed end signaling is done.
static void CmdDoneCallback(u64 userdata, int cyclesLate);

// Flushes the memory card contents to disk.
void Flush(bool exiting = false);

// Signals that the command that was previously executed is now done.
void CmdDone();

// Variant of CmdDone which schedules an event later in the future to complete the command.
void CmdDoneLater(u64 cycles);

enum
{
cmdNintendoID = 0x00,
Expand All @@ -71,7 +80,7 @@ class CEXIMemoryCard : public IEXIDevice

std::string m_strFilename;
int card_index;
int et_this_card;
int et_this_card, et_cmd_done;
//! memory card state

// STATE_TO_SAVE
Expand Down
13 changes: 9 additions & 4 deletions Source/Core/Core/Src/HW/GPFifo.cpp
Expand Up @@ -98,12 +98,17 @@ void STACKALIGN CheckGatherPipe()
memmove(m_gatherPipe, m_gatherPipe + cnt, m_gatherPipeCount);

// Profile where the FIFO writes are occurring.
if (jit && (jit->js.fifoWriteAddresses.find(PC)) == (jit->js.fifoWriteAddresses.end()))
if (jit && PC != 0 && (jit->js.fifoWriteAddresses.find(PC)) == (jit->js.fifoWriteAddresses.end()))
{
jit->js.fifoWriteAddresses.insert(PC);
// Log only stores, fp stores and ps stores, filtering out other instructions arrived via optimizeGatherPipe
int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type;
if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_PS && GetOpInfo(Memory::ReadUnchecked_U32(PC))->opname=="psq_st"))
{
jit->js.fifoWriteAddresses.insert(PC);

// Invalidate the JIT block so that it gets recompiled with the external exception check included.
jit->GetBlockCache()->InvalidateICache(PC, 4);
// Invalidate the JIT block so that it gets recompiled with the external exception check included.
jit->GetBlockCache()->InvalidateICache(PC, 4);
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/Core/Src/HW/ProcessorInterface.cpp
Expand Up @@ -99,7 +99,7 @@ void Init()
m_ResetCode = 0x80000000; // Cold reset
m_InterruptCause = INT_CAUSE_RST_BUTTON | INT_CAUSE_VI;

toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", &ToggleResetButtonCallback);
toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback);
}

void Read16(u16& _uReturnValue, const u32 _iAddress)
Expand Down
15 changes: 14 additions & 1 deletion Source/Core/Core/Src/HW/SI_DeviceGCController.cpp
Expand Up @@ -69,6 +69,19 @@ int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength)
*(u32*)&_pBuffer[0] = SI_GC_CONTROLLER;
break;

case CMD_DIRECT:
{
INFO_LOG(SERIALINTERFACE, "PAD - Direct (Length: %d)", _iLength);
u32 high, low;
GetData(high, low);
for (int i = 0; i < (_iLength - 1) / 2; i++)
{
_pBuffer[0 + i] = (high >> (i * 8)) & 0xff;
_pBuffer[4 + i] = (low >> (i * 8)) & 0xff;
}
}
break;

case CMD_ORIGIN:
{
INFO_LOG(SERIALINTERFACE, "PAD - Get Origin");
Expand Down Expand Up @@ -96,7 +109,7 @@ int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength)
default:
{
ERROR_LOG(SERIALINTERFACE, "unknown SI command (0x%x)", command);
PanicAlert("SI: Unknown command");
PanicAlert("SI: Unknown command (0x%x)", command);
}
break;
}
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Core/Src/HW/SI_DeviceGCController.h
Expand Up @@ -31,6 +31,7 @@ class CSIDevice_GCController : public ISIDevice
enum EBufferCommands
{
CMD_RESET = 0x00,
CMD_DIRECT = 0x40,
CMD_ORIGIN = 0x41,
CMD_RECALIBRATE = 0x42,
};
Expand Down
33 changes: 18 additions & 15 deletions Source/Core/Core/Src/HW/SystemTimers.cpp
Expand Up @@ -74,6 +74,7 @@ IPC_HLE_PERIOD: For the Wiimote this is the call schedule:
#include "Thread.h"
#include "Timer.h"
#include "VideoBackendBase.h"
#include "CommandProcessor.h"


namespace SystemTimers
Expand Down Expand Up @@ -109,7 +110,7 @@ enum
int et_Dec;
int et_VI;
int et_SI;
int et_AI;
int et_CP;
int et_AudioDMA;
int et_DSP;
int et_IPC_HLE;
Expand All @@ -118,15 +119,15 @@ int et_PatchEngine; // PatchEngine updates every 1/60th of a second by default
// These are badly educated guesses
// Feel free to experiment. Set these in Init below.
int
// This one should simply be determined by the increasing counter in AI.
AI_PERIOD,

// These shouldn't be period controlled either, most likely.
DSP_PERIOD,

// This is a fixed value, don't change it
AUDIO_DMA_PERIOD,

// Regulates the speed of the Command Processor
CP_PERIOD,

// This is completely arbitrary. If we find that we need lower latency, we can just
// increase this number.
IPC_HLE_PERIOD;
Expand All @@ -143,12 +144,6 @@ u32 ConvertMillisecondsToTicks(u32 _Milliseconds)
return GetTicksPerSecond() / 1000 * _Milliseconds;
}

void AICallback(u64 userdata, int cyclesLate)
{
AudioInterface::Update();
CoreTiming::ScheduleEvent(AI_PERIOD - cyclesLate, et_AI);
}

// DSP/CPU timeslicing.
void DSPCallback(u64 userdata, int cyclesLate)
{
Expand Down Expand Up @@ -187,6 +182,12 @@ void SICallback(u64 userdata, int cyclesLate)
CoreTiming::ScheduleEvent(SerialInterface::GetTicksToNextSIPoll() - cyclesLate, et_SI);
}

void CPCallback(u64 userdata, int cyclesLate)
{
CommandProcessor::Update();
CoreTiming::ScheduleEvent(CP_PERIOD - cyclesLate, et_CP);
}

void DecrementerCallback(u64 userdata, int cyclesLate)
{
PowerPC::ppcState.spr[SPR_DEC] = 0xFFFFFFFF;
Expand Down Expand Up @@ -266,12 +267,12 @@ void Init()
if (DSP::GetDSPEmulator()->IsLLE())
DSP_PERIOD = 12000; // TO BE TWEAKED

// This is the biggest question mark.
AI_PERIOD = GetTicksPerSecond() / 80;

// System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA
AUDIO_DMA_PERIOD = CPU_CORE_CLOCK / (AudioInterface::GetAIDSampleRate() * 4 / 32);

// Emulated gekko <-> flipper bus speed ratio (cpu clock / flipper clock)
CP_PERIOD = GetTicksPerSecond() / 10000;

Common::Timer::IncreaseResolution();
// store and convert localtime at boot to timebase ticks
CoreTiming::SetFakeTBStartValue((u64)(CPU_CORE_CLOCK / TIMER_RATIO) * (u64)CEXIIPL::GetGCTime());
Expand All @@ -281,19 +282,21 @@ void Init()
CoreTiming::SetFakeDecStartTicks(CoreTiming::GetTicks());

et_Dec = CoreTiming::RegisterEvent("DecCallback", DecrementerCallback);
et_AI = CoreTiming::RegisterEvent("AICallback", AICallback);
et_VI = CoreTiming::RegisterEvent("VICallback", VICallback);
et_SI = CoreTiming::RegisterEvent("SICallback", SICallback);
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSyncGPU)
et_CP = CoreTiming::RegisterEvent("CPCallback", CPCallback);
et_DSP = CoreTiming::RegisterEvent("DSPCallback", DSPCallback);
et_AudioDMA = CoreTiming::RegisterEvent("AudioDMACallback", AudioDMACallback);
et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback);
et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback);

CoreTiming::ScheduleEvent(AI_PERIOD, et_AI);
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerLine(), et_VI);
CoreTiming::ScheduleEvent(DSP_PERIOD, et_DSP);
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_SI);
CoreTiming::ScheduleEvent(AUDIO_DMA_PERIOD, et_AudioDMA);
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSyncGPU)
CoreTiming::ScheduleEvent(CP_PERIOD, et_CP);

CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_PatchEngine);

Expand Down
Expand Up @@ -193,6 +193,7 @@ void Interpreter::mtmsr(UGeckoInstruction _inst)
{
// Privileged?
MSR = m_GPR[_inst.RS];
PowerPC::CheckExceptions();
m_EndBlock = true;
}

Expand Down
6 changes: 5 additions & 1 deletion Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp
Expand Up @@ -291,7 +291,9 @@ static void ImHere()
void Jit64::Cleanup()
{
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0)
{
ABI_CallFunction((void *)&GPFifo::CheckGatherPipe);
}

// SPEED HACK: MMCR0/MMCR1 should be checked at run-time, not at compile time.
if (MMCR0.Hex || MMCR1.Hex)
Expand All @@ -301,7 +303,8 @@ void Jit64::Cleanup()
void Jit64::WriteExit(u32 destination, int exit_num)
{
Cleanup();
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));

SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));

//If nobody has taken care of this yet (this can be removed when all branches are done)
JitBlock *b = js.curBlock;
Expand Down Expand Up @@ -561,6 +564,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
{
js.fifoBytesThisBlock -= 32;
MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write
ABI_CallFunction(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0));
}

Expand Down
2 changes: 2 additions & 0 deletions Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp
Expand Up @@ -302,6 +302,7 @@ void Jit64::stX(UGeckoInstruction inst)
addr += offset;
if ((addr & 0xFFFFF000) == 0xCC008000 && jo.optimizeGatherPipe)
{
MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write
gpr.FlushLockX(ABI_PARAM1);
MOV(32, R(ABI_PARAM1), gpr.R(s));
if (update)
Expand Down Expand Up @@ -329,6 +330,7 @@ void Jit64::stX(UGeckoInstruction inst)
}
else
{
MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write
switch (accessSize)
{
case 32: ABI_CallFunctionAC(thunks.ProtectFunction(true ? ((void *)&Memory::Write_U32) : ((void *)&Memory::Write_U32_Swap), 2), gpr.R(s), addr); break;
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp
Expand Up @@ -42,6 +42,7 @@ const u8 GC_ALIGNED16(pbswapShuffle2x4[16]) = {3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10,
#if 0
static void WriteDual32(u64 value, u32 address)
{
MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write
Memory::Write_U32((u32)(value >> 32), address);
Memory::Write_U32((u32)value, address + 4);
}
Expand Down
23 changes: 23 additions & 0 deletions Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp
Expand Up @@ -28,6 +28,7 @@

#include "Jit.h"
#include "JitRegCache.h"
#include "HW/ProcessorInterface.h"

void Jit64::mtspr(UGeckoInstruction inst)
{
Expand Down Expand Up @@ -122,7 +123,29 @@ void Jit64::mtmsr(UGeckoInstruction inst)
gpr.UnlockAll();
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);

// If some exceptions are pending and EE are now enabled, force checking
// external exceptions when going out of mtmsr in order to execute delayed
// interrupts as soon as possible.
TEST(32, M(&MSR), Imm32(0x8000));
FixupBranch eeDisabled = J_CC(CC_Z);

TEST(32, M((void*)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_EXTERNAL_INT | EXCEPTION_PERFORMANCE_MONITOR | EXCEPTION_DECREMENTER));
FixupBranch noExceptionsPending = J_CC(CC_Z);

// Check if a CP interrupt is waiting and keep the GPU emulation in sync (issue 4336)
TEST(32, M((void *)&ProcessorInterface::m_InterruptCause), Imm32(ProcessorInterface::INT_CAUSE_CP));
FixupBranch cpInt = J_CC(CC_NZ);

MOV(32, M(&PC), Imm32(js.compilerPC + 4));
WriteExternalExceptionExit();

SetJumpTarget(cpInt);
SetJumpTarget(noExceptionsPending);
SetJumpTarget(eeDisabled);

WriteExit(js.compilerPC + 4, 0);

js.firstFPInstructionFound = false;
}

Expand Down
4 changes: 2 additions & 2 deletions Source/Core/Core/Src/PowerPC/Jit64IL/IR.h
Expand Up @@ -276,8 +276,8 @@ class IRBuilder {
InstLoc EmitLoadMSR() {
return FoldZeroOp(LoadMSR, 0);
}
InstLoc EmitStoreMSR(InstLoc val) {
return FoldUOp(StoreMSR, val);
InstLoc EmitStoreMSR(InstLoc val, InstLoc pc) {
return FoldBiOp(StoreMSR, val, pc);
}
InstLoc EmitStoreFPRF(InstLoc value) {
return FoldUOp(StoreFPRF, value);
Expand Down
18 changes: 18 additions & 0 deletions Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp
Expand Up @@ -994,8 +994,26 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak
break;
}
case StoreMSR: {
unsigned InstLoc = ibuild->GetImmValue(getOp2(I));
regStoreInstToConstLoc(RI, 32, getOp1(I), &MSR);
regNormalRegClear(RI, I);

// If some exceptions are pending and EE are now enabled, force checking
// external exceptions when going out of mtmsr in order to execute delayed
// interrupts as soon as possible.
Jit->MOV(32, R(EAX), M(&MSR));
Jit->TEST(32, R(EAX), Imm32(0x8000));
FixupBranch eeDisabled = Jit->J_CC(CC_Z);

Jit->MOV(32, R(EAX), M((void*)&PowerPC::ppcState.Exceptions));
Jit->TEST(32, R(EAX), R(EAX));
FixupBranch noExceptionsPending = Jit->J_CC(CC_Z);

Jit->MOV(32, M(&PC), Imm32(InstLoc + 4));
Jit->WriteExceptionExit(); // TODO: Implement WriteExternalExceptionExit for JitIL

Jit->SetJumpTarget(eeDisabled);
Jit->SetJumpTarget(noExceptionsPending);
break;
}
case StoreGQR: {
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp
Expand Up @@ -389,7 +389,9 @@ static void ImHere()
void JitIL::Cleanup()
{
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0)
{
ABI_CallFunction((void *)&GPFifo::CheckGatherPipe);
}

// SPEED HACK: MMCR0/MMCR1 should be checked at run-time, not at compile time.
if (MMCR0.Hex || MMCR1.Hex)
Expand Down
Expand Up @@ -106,7 +106,7 @@ void JitIL::mfspr(UGeckoInstruction inst)
// --------------
void JitIL::mtmsr(UGeckoInstruction inst)
{
ibuild.EmitStoreMSR(ibuild.EmitLoadGReg(inst.RS));
ibuild.EmitStoreMSR(ibuild.EmitLoadGReg(inst.RS), ibuild.EmitIntConst(js.compilerPC));
ibuild.EmitBranchUncond(ibuild.EmitIntConst(js.compilerPC + 4));
}
// ==============
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp
Expand Up @@ -32,6 +32,7 @@
#include "../../HW/GPFifo.h"
#include "../../Core.h"
#include "JitAsmCommon.h"
#include "JitBase.h"

using namespace Gen;

Expand Down
15 changes: 14 additions & 1 deletion Source/Core/DolphinWX/Src/ISOProperties.cpp
Expand Up @@ -316,6 +316,8 @@ void CISOProperties::CreateGUIControls(bool IsWad)
DCBZOFF->SetToolTip(_("Bypass the clearing of the data cache by the DCBZ instruction. Usually leave this option disabled."));
VBeam = new wxCheckBox(m_GameConfig, ID_VBEAM, _("Accurate VBeam emulation"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator);
VBeam->SetToolTip(_("If the FPS is erratic, this option may help. (ON = Compatible, OFF = Fast)"));
SyncGPU = new wxCheckBox(m_GameConfig, ID_SYNCGPU, _("Sychronise GPU thread"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator);
SyncGPU->SetToolTip(_("Synchonises the GPU and CPU threads to help prevent random freezes in Dual Core mode. (ON = Compatible, OFF = Fast)"));
FastDiscSpeed = new wxCheckBox(m_GameConfig, ID_DISCSPEED, _("Speed up Disc Transfer Rate"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator);
FastDiscSpeed->SetToolTip(_("Enable fast disc access. Needed for a few games. (ON = Fast, OFF = Compatible)"));
BlockMerging = new wxCheckBox(m_GameConfig, ID_MERGEBLOCKS, _("Enable Block Merging"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator);
Expand Down Expand Up @@ -357,9 +359,10 @@ void CISOProperties::CreateGUIControls(bool IsWad)
sbCoreOverrides->Add(CPUThread, 0, wxLEFT, 5);
sbCoreOverrides->Add(SkipIdle, 0, wxLEFT, 5);
sbCoreOverrides->Add(MMU, 0, wxLEFT, 5);
sbCoreOverrides->Add(DCBZOFF, 0, wxLEFT, 5);
sbCoreOverrides->Add(TLBHack, 0, wxLEFT, 5);
sbCoreOverrides->Add(DCBZOFF, 0, wxLEFT, 5);
sbCoreOverrides->Add(VBeam, 0, wxLEFT, 5);
sbCoreOverrides->Add(SyncGPU, 0, wxLEFT, 5);
sbCoreOverrides->Add(FastDiscSpeed, 0, wxLEFT, 5);
sbCoreOverrides->Add(BlockMerging, 0, wxLEFT, 5);
sbCoreOverrides->Add(DSPHLE, 0, wxLEFT, 5);
Expand Down Expand Up @@ -936,6 +939,11 @@ void CISOProperties::LoadGameConfig()
else
VBeam->Set3StateValue(wxCHK_UNDETERMINED);

if (GameIni.Get("Core", "SyncGPU", &bTemp))
SyncGPU->Set3StateValue((wxCheckBoxState)bTemp);
else
SyncGPU->Set3StateValue(wxCHK_UNDETERMINED);

if (GameIni.Get("Core", "FastDiscSpeed", &bTemp))
FastDiscSpeed->Set3StateValue((wxCheckBoxState)bTemp);
else
Expand Down Expand Up @@ -1025,6 +1033,11 @@ bool CISOProperties::SaveGameConfig()
else
GameIni.Set("Core", "VBeam", VBeam->Get3StateValue());

if (SyncGPU->Get3StateValue() == wxCHK_UNDETERMINED)
GameIni.DeleteKey("Core", "SyncGPU");
else
GameIni.Set("Core", "SyncGPU", SyncGPU->Get3StateValue());

if (FastDiscSpeed->Get3StateValue() == wxCHK_UNDETERMINED)
GameIni.DeleteKey("Core", "FastDiscSpeed");
else
Expand Down
3 changes: 2 additions & 1 deletion Source/Core/DolphinWX/Src/ISOProperties.h
Expand Up @@ -70,7 +70,7 @@ class CISOProperties : public wxDialog

// Core
wxCheckBox *CPUThread, *SkipIdle, *MMU, *DCBZOFF, *TLBHack;
wxCheckBox *VBeam, *FastDiscSpeed, *BlockMerging, *DSPHLE;
wxCheckBox *VBeam, *SyncGPU, *FastDiscSpeed, *BlockMerging, *DSPHLE;
// Wii
wxCheckBox *EnableWideScreen;
// Video
Expand Down Expand Up @@ -130,6 +130,7 @@ class CISOProperties : public wxDialog
ID_DCBZOFF,
ID_TLBHACK,
ID_VBEAM,
ID_SYNCGPU,
ID_DISCSPEED,
ID_MERGEBLOCKS,
ID_AUDIO_DSP_HLE,
Expand Down
101 changes: 56 additions & 45 deletions Source/Core/VideoCommon/Src/CommandProcessor.cpp
Expand Up @@ -32,6 +32,8 @@
#include "HW/GPFifo.h"
#include "HW/Memmap.h"
#include "DLCache.h"
#include "HW/SystemTimers.h"
#include "Core.h"

namespace CommandProcessor
{
Expand All @@ -57,12 +59,15 @@ static bool bProcessFifoAllDistance = false;

volatile bool isPossibleWaitingSetDrawDone = false;
volatile bool isHiWatermarkActive = false;
volatile bool isLoWatermarkActive = false;
volatile bool interruptSet= false;
volatile bool interruptWaiting= false;
volatile bool interruptTokenWaiting = false;
volatile bool interruptFinishWaiting = false;
volatile bool waitingForPEInterruptDisable = false;

volatile u32 VITicks = CommandProcessor::m_cpClockOrigin;

bool IsOnThread()
{
return SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread;
Expand All @@ -88,6 +93,7 @@ void DoState(PointerWrap &p)
p.Do(bProcessFifoToLoWatermark);
p.Do(bProcessFifoAllDistance);
p.Do(isHiWatermarkActive);
p.Do(isLoWatermarkActive);
p.Do(isPossibleWaitingSetDrawDone);
p.Do(interruptSet);
p.Do(interruptWaiting);
Expand Down Expand Up @@ -119,7 +125,7 @@ void Init()
m_tokenReg = 0;

memset(&fifo,0,sizeof(fifo));
fifo.CPCmdIdle = 1 ;
fifo.CPCmdIdle = 1;
fifo.CPReadIdle = 1;
fifo.bFF_Breakpoint = 0;
fifo.bFF_HiWatermark = 0;
Expand All @@ -136,6 +142,7 @@ void Init()
bProcessFifoAllDistance = false;
isPossibleWaitingSetDrawDone = false;
isHiWatermarkActive = false;
isLoWatermarkActive = false;

et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper);
}
Expand Down Expand Up @@ -294,7 +301,6 @@ void Read16(u16& _rReturnValue, const u32 _Address)

void Write16(const u16 _Value, const u32 _Address)
{

INFO_LOG(COMMANDPROCESSOR, "(write16): 0x%04x @ 0x%08x",_Value,_Address);

switch (_Address & 0xFFF)
Expand Down Expand Up @@ -405,7 +411,8 @@ void Write16(const u16 _Value, const u32 _Address)
{
GPFifo::ResetGatherPipe();
ResetVideoBuffer();
}else
}
else
{
ResetVideoBuffer();
}
Expand Down Expand Up @@ -461,7 +468,7 @@ void STACKALIGN GatherPipeBursted()
}

if (IsOnThread())
SetOverflowStatusFromGatherPipe();
SetCpStatus();

// update the fifo-pointer
if (fifo.CPWritePointer >= fifo.CPEnd)
Expand Down Expand Up @@ -511,68 +518,68 @@ void AbortFrame()

}

void SetOverflowStatusFromGatherPipe()
{
fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark);
isHiWatermarkActive = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt && m_CPCtrlReg.GPReadEnable;

if (isHiWatermarkActive)
{
interruptSet = true;
INFO_LOG(COMMANDPROCESSOR,"Interrupt set");
ProcessorInterface::SetInterrupt(INT_CAUSE_CP, true);
}
}

void SetCpStatus()
{
// overflow & underflow check
fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark);
fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark);

// breakpoint
if (fifo.bFF_BPEnable)
{
if (fifo.CPBreakpoint == fifo.CPReadPointer)
{
if (!fifo.bFF_Breakpoint)
if (Core::IsGPUThread())
{
if (fifo.bFF_BPEnable)
{
if (fifo.CPBreakpoint == fifo.CPReadPointer)
{
INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i", fifo.CPReadPointer);
fifo.bFF_Breakpoint = true;
IncrementCheckContextId();
if (!fifo.bFF_Breakpoint)
{
INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i", fifo.CPReadPointer);
fifo.bFF_Breakpoint = true;
IncrementCheckContextId();
}
}
}
else
{
if (fifo.bFF_Breakpoint)
INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer);
fifo.bFF_Breakpoint = false;
}
}
else
{
if (fifo.bFF_Breakpoint)
INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer);
fifo.bFF_Breakpoint = false;
fifo.bFF_Breakpoint = false;
}
}
else
{
if (fifo.bFF_Breakpoint)
INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer);
fifo.bFF_Breakpoint = false;
}
}

bool bpInt = fifo.bFF_Breakpoint && fifo.bFF_BPInt;
bool ovfInt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt;
bool undfInt = fifo.bFF_LoWatermark && fifo.bFF_LoWatermarkInt;

bool interrupt = (bpInt || ovfInt || undfInt) && m_CPCtrlReg.GPReadEnable;

isHiWatermarkActive = ovfInt && m_CPCtrlReg.GPReadEnable;
isHiWatermarkActive = ovfInt && m_CPCtrlReg.GPReadEnable;
isLoWatermarkActive = undfInt && m_CPCtrlReg.GPReadEnable;

if (interrupt != interruptSet && !interruptWaiting)
{
u64 userdata = interrupt?1:0;
if (IsOnThread())
{
if(!interrupt || bpInt || undfInt)
if(!interrupt || bpInt || undfInt || ovfInt)
{
interruptWaiting = true;
CommandProcessor::UpdateInterruptsFromVideoBackend(userdata);
if (Core::IsGPUThread())
{
interruptWaiting = true;
CommandProcessor::UpdateInterruptsFromVideoBackend(userdata);
}
else if (Core::IsCPUThread())
{
interruptSet = interrupt;
INFO_LOG(COMMANDPROCESSOR,"Interrupt set");
ProcessorInterface::SetInterrupt(INT_CAUSE_CP, interrupt);
}
}
}
else
Expand All @@ -596,7 +603,7 @@ void ProcessFifoAllDistance()
if (IsOnThread())
{
while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable &&
fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt())
fifo.CPReadWriteDistance && !AtBreakpoint())
Common::YieldCPU();
}
bProcessFifoAllDistance = false;
Expand All @@ -617,15 +624,11 @@ void SetCpStatusRegister()
{
// Here always there is one fifo attached to the GPU
m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint;
m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ;
m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance;
m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || AtBreakpoint() || (fifo.CPReadPointer == fifo.CPWritePointer);
m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance || AtBreakpoint() || !fifo.bFF_GPReadEnable;
m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark;
m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark;

// HACK to compensate for slow response to PE interrupts in Time Splitters: Future Perfect
if (IsOnThread())
PixelEngine::ResumeWaitingForPEInterrupt();

INFO_LOG(COMMANDPROCESSOR,"\t Read from STATUS_REGISTER : %04x", m_CPStatusReg.Hex);
DEBUG_LOG(COMMANDPROCESSOR, "(r) status: iBP %s | fReadIdle %s | fCmdIdle %s | iOvF %s | iUndF %s"
, m_CPStatusReg.Breakpoint ? "ON" : "OFF"
Expand Down Expand Up @@ -693,4 +696,12 @@ void SetCpClearRegister()
// }
}

void Update()
{
while (VITicks > m_cpClockOrigin && fifo.isGpuReadingData && IsOnThread())
Common::YieldCPU();

if (fifo.isGpuReadingData)
Common::AtomicAdd(VITicks, SystemTimers::GetTicksPerSecond() / 10000);
}
} // end of namespace CommandProcessor
9 changes: 8 additions & 1 deletion Source/Core/VideoCommon/Src/CommandProcessor.h
Expand Up @@ -31,6 +31,7 @@ namespace CommandProcessor
extern SCPFifoStruct fifo; //This one is shared between gfx thread and emulator thread.
extern volatile bool isPossibleWaitingSetDrawDone; //This one is used for sync gfx thread and emulator thread.
extern volatile bool isHiWatermarkActive;
extern volatile bool isLoWatermarkActive;
extern volatile bool interruptSet;
extern volatile bool interruptWaiting;
extern volatile bool interruptTokenWaiting;
Expand Down Expand Up @@ -140,6 +141,9 @@ union UCPClearReg
UCPClearReg(u16 _hex) {Hex = _hex; }
};

// Can be any number, low enough to not be below the number of clocks executed by the GPU per CP_PERIOD
const static u32 m_cpClockOrigin = 200000;

// Init
void Init();
void Shutdown();
Expand All @@ -161,11 +165,14 @@ bool AllowIdleSkipping();
void SetCpClearRegister();
void SetCpControlRegister();
void SetCpStatusRegister();
void SetOverflowStatusFromGatherPipe();
void ProcessFifoToLoWatermark();
void ProcessFifoAllDistance();
void ProcessFifoEvents();
void AbortFrame();

void Update();
extern volatile u32 VITicks;

} // namespace CommandProcessor

#endif // _COMMANDPROCESSOR_H
Expand Down
71 changes: 42 additions & 29 deletions Source/Core/VideoCommon/Src/Fifo.cpp
Expand Up @@ -26,6 +26,7 @@
#include "Fifo.h"
#include "HW/Memmap.h"
#include "Core.h"
#include "CoreTiming.h"

volatile bool g_bSkipCurrentFrame = false;
extern u8* g_pVideoData;
Expand Down Expand Up @@ -72,6 +73,7 @@ void Fifo_Init()
videoBuffer = (u8*)AllocateMemoryPages(FIFO_SIZE);
size = 0;
GpuRunningState = false;
Common::AtomicStore(CommandProcessor::VITicks, CommandProcessor::m_cpClockOrigin);
}

void Fifo_Shutdown()
Expand Down Expand Up @@ -123,7 +125,7 @@ void ReadDataFromFifo(u8* _uData, u32 len)
size -= pos;
if (size + len > FIFO_SIZE)
{
PanicAlert("FIFO out of bounds (sz = %i, at %08x)", size, pos);
PanicAlert("FIFO out of bounds (sz = %i, len = %i at %08x)", size, len, pos);
}
memmove(&videoBuffer[0], &videoBuffer[pos], size);
g_pVideoData = videoBuffer;
Expand All @@ -147,6 +149,7 @@ void RunGpuLoop()
std::lock_guard<std::mutex> lk(m_csHWVidOccupied);
GpuRunningState = true;
SCPFifoStruct &fifo = CommandProcessor::fifo;
u32 cyclesExecuted = 0;

while (GpuRunningState)
{
Expand All @@ -155,31 +158,41 @@ void RunGpuLoop()
VideoFifo_CheckAsyncRequest();

CommandProcessor::SetCpStatus();

Common::AtomicStore(CommandProcessor::VITicks, CommandProcessor::m_cpClockOrigin);

// check if we are able to run this buffer
while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt())
while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint())
{
if (!GpuRunningState) break;

fifo.isGpuReadingData = true;
CommandProcessor::isPossibleWaitingSetDrawDone = fifo.bFF_GPLinkEnable ? true : false;

u32 readPtr = fifo.CPReadPointer;
u8 *uData = Memory::GetPointer(readPtr);

if (readPtr == fifo.CPEnd) readPtr = fifo.CPBase;
if (Common::AtomicLoad(CommandProcessor::VITicks) > CommandProcessor::m_cpClockOrigin || !Core::g_CoreStartupParameter.bSyncGPU)
{
u32 readPtr = fifo.CPReadPointer;
u8 *uData = Memory::GetPointer(readPtr);

if (readPtr == fifo.CPEnd) readPtr = fifo.CPBase;
else readPtr += 32;

_assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0 ,
"Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce inestabilty in the game. Please report it.", fifo.CPReadWriteDistance - 32);

ReadDataFromFifo(uData, 32);

OpcodeDecoder_Run(g_bSkipCurrentFrame);

Common::AtomicStore(fifo.CPReadPointer, readPtr);
Common::AtomicAdd(fifo.CPReadWriteDistance, -32);
if((GetVideoBufferEndPtr() - g_pVideoData) == 0)
Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer);
_assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0 ,
"Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce instabilty in the game. Please report it.", fifo.CPReadWriteDistance - 32);

ReadDataFromFifo(uData, 32);

cyclesExecuted = OpcodeDecoder_Run(g_bSkipCurrentFrame);

if (Common::AtomicLoad(CommandProcessor::VITicks) > cyclesExecuted && Core::g_CoreStartupParameter.bSyncGPU)
Common::AtomicAdd(CommandProcessor::VITicks, -(s32)cyclesExecuted);

Common::AtomicStore(fifo.CPReadPointer, readPtr);
Common::AtomicAdd(fifo.CPReadWriteDistance, -32);
if((GetVideoBufferEndPtr() - g_pVideoData) == 0)
Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer);
}

CommandProcessor::SetCpStatus();

// This call is pretty important in DualCore mode and must be called in the FIFO Loop.
Expand All @@ -188,7 +201,7 @@ void RunGpuLoop()
VideoFifo_CheckAsyncRequest();
CommandProcessor::isPossibleWaitingSetDrawDone = false;
}

fifo.isGpuReadingData = false;


Expand Down Expand Up @@ -217,23 +230,23 @@ bool AtBreakpoint()

void RunGpu()
{
SCPFifoStruct &fifo = CommandProcessor::fifo;
while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() )
{
u8 *uData = Memory::GetPointer(fifo.CPReadPointer);
SCPFifoStruct &fifo = CommandProcessor::fifo;
while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() )
{
u8 *uData = Memory::GetPointer(fifo.CPReadPointer);

FPURoundMode::SaveSIMDState();
FPURoundMode::LoadDefaultSIMDState();
ReadDataFromFifo(uData, 32);
OpcodeDecoder_Run(g_bSkipCurrentFrame);
u32 count = OpcodeDecoder_Run(g_bSkipCurrentFrame);
FPURoundMode::LoadSIMDState();

//DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base");
//DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base");

if (fifo.CPReadPointer == fifo.CPEnd) fifo.CPReadPointer = fifo.CPBase;
else fifo.CPReadPointer += 32;
if (fifo.CPReadPointer == fifo.CPEnd) fifo.CPReadPointer = fifo.CPBase;
else fifo.CPReadPointer += 32;

fifo.CPReadWriteDistance -= 32;
}
CommandProcessor::SetCpStatus();
fifo.CPReadWriteDistance -= 32;
}
CommandProcessor::SetCpStatus();
}
2 changes: 1 addition & 1 deletion Source/Core/VideoCommon/Src/Fifo.h
Expand Up @@ -23,7 +23,7 @@

class PointerWrap;

#define FIFO_SIZE (1024*1024)
#define FIFO_SIZE (2*1024*1024)

extern volatile bool g_bSkipCurrentFrame;

Expand Down
85 changes: 66 additions & 19 deletions Source/Core/VideoCommon/Src/OpcodeDecoding.cpp
Expand Up @@ -136,40 +136,78 @@ void ExecuteDisplayList(u32 address, u32 size)
InterpretDisplayList(address, size);
}

bool FifoCommandRunnable()
u32 FifoCommandRunnable(u32 &command_size)
{
u32 cycleTime = 0;
u32 buffer_size = (u32)(GetVideoBufferEndPtr() - g_pVideoData);
if (buffer_size == 0)
return false; // can't peek
return 0; // can't peek

u8 cmd_byte = DataPeek8(0);
u32 command_size = 0;

switch (cmd_byte)
{
case GX_NOP: // Hm, this means that we scan over nop streams pretty slowly...
command_size = 1;
cycleTime = 6;
break;
case GX_CMD_INVL_VC: // Invalidate Vertex Cache - no parameters
command_size = 1;
cycleTime = 6;
break;
case GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics registers after that
command_size = 1;
cycleTime = 6;
break;

case GX_LOAD_BP_REG:
command_size = 5;
cycleTime = 12;
break;

case GX_LOAD_CP_REG:
command_size = 6;
cycleTime = 12;
break;

case GX_LOAD_INDX_A:
case GX_LOAD_INDX_B:
case GX_LOAD_INDX_C:
case GX_LOAD_INDX_D:
command_size = 5;
cycleTime = 6; // TODO
break;

case GX_CMD_CALL_DL:
command_size = 9;
case GX_CMD_CALL_DL:
{
// FIXME: Calculate the cycle time of the display list.
//u32 address = DataPeek32(1);
//u32 size = DataPeek32(5);
//u8* old_pVideoData = g_pVideoData;
//u8* startAddress = Memory::GetPointer(address);

//// Avoid the crash if Memory::GetPointer failed ..
//if (startAddress != 0)
//{
// g_pVideoData = startAddress;
// u8 *end = g_pVideoData + size;
// u32 step = 0;
// while (g_pVideoData < end)
// {
// cycleTime += FifoCommandRunnable(step);
// g_pVideoData += step;
// }
//}
//else
//{
// cycleTime = 45;
//}

//// reset to the old pointer
//g_pVideoData = old_pVideoData;
command_size = 9;
cycleTime = 45; // This is unverified
}
break;

case GX_LOAD_XF_REG:
Expand All @@ -180,11 +218,12 @@ bool FifoCommandRunnable()
command_size = 1 + 4;
u32 Cmd2 = DataPeek32(1);
int transfer_size = ((Cmd2 >> 16) & 15) + 1;
command_size += transfer_size * 4;
command_size += transfer_size * 4;
cycleTime = 18 + 6 * transfer_size;
}
else
{
return false;
return 0;
}
}
break;
Expand All @@ -198,10 +237,11 @@ bool FifoCommandRunnable()
command_size = 1 + 2;
u16 numVertices = DataPeek16(1);
command_size += numVertices * VertexLoaderManager::GetVertexSize(cmd_byte & GX_VAT_MASK);
cycleTime = 1600; // This depends on the number of pixels rendered
}
else
{
return false;
return 0;
}
}
else
Expand Down Expand Up @@ -248,11 +288,19 @@ bool FifoCommandRunnable()
}

if (command_size > buffer_size)
return false;
return 0;

// INFO_LOG("OP detected: cmd_byte 0x%x size %i buffer %i",cmd_byte, command_size, buffer_size);
if (cycleTime == 0)
cycleTime = 6;

return true;
return cycleTime;
}

u32 FifoCommandRunnable()
{
u32 command_size = 0;
return FifoCommandRunnable(command_size);
}

static void Decode()
Expand Down Expand Up @@ -461,16 +509,15 @@ void OpcodeDecoder_Shutdown()
}
}

void OpcodeDecoder_Run(bool skipped_frame)
u32 OpcodeDecoder_Run(bool skipped_frame)
{
if (!skipped_frame)
{
while (FifoCommandRunnable())
Decode();
}
else
u32 totalCycles = 0;
u32 cycles = FifoCommandRunnable();
while (cycles > 0)
{
while (FifoCommandRunnable())
DecodeSemiNop();
skipped_frame ? DecodeSemiNop() : Decode();
totalCycles += cycles;
cycles = FifoCommandRunnable();
}
return totalCycles;
}
2 changes: 1 addition & 1 deletion Source/Core/VideoCommon/Src/OpcodeDecoding.h
Expand Up @@ -50,6 +50,6 @@ extern bool g_bRecordFifoData;

void OpcodeDecoder_Init();
void OpcodeDecoder_Shutdown();
void OpcodeDecoder_Run(bool skipped_frame);
u32 OpcodeDecoder_Run(bool skipped_frame);
void ExecuteDisplayList(u32 address, u32 size);
#endif // _OPCODE_DECODING_H
87 changes: 32 additions & 55 deletions Source/Core/VideoCommon/Src/PixelEngine.cpp
Expand Up @@ -33,7 +33,6 @@
#include "HW/ProcessorInterface.h"
#include "DLCache.h"
#include "State.h"
#include "PerfQueryBase.h"

namespace PixelEngine
{
Expand Down Expand Up @@ -113,14 +112,14 @@ static UPEAlphaReadReg m_AlphaRead;
static UPECtrlReg m_Control;
//static u16 m_Token; // token value most recently encountered

static bool g_bSignalTokenInterrupt;
static bool g_bSignalFinishInterrupt;
volatile u32 g_bSignalTokenInterrupt;
volatile u32 g_bSignalFinishInterrupt;

static int et_SetTokenOnMainThread;
static int et_SetFinishOnMainThread;

volatile bool interruptSetToken = false;
volatile bool interruptSetFinish = false;
volatile u32 interruptSetToken = 0;
volatile u32 interruptSetFinish = 0;

u16 bbox[4];
bool bbox_active;
Expand Down Expand Up @@ -164,10 +163,10 @@ void Init()
m_AlphaModeConf.Hex = 0;
m_AlphaRead.Hex = 0;

g_bSignalTokenInterrupt = false;
g_bSignalFinishInterrupt = false;
interruptSetToken = false;
interruptSetFinish = false;
g_bSignalTokenInterrupt = 0;
g_bSignalFinishInterrupt = 0;
interruptSetToken = 0;
interruptSetFinish = 0;

et_SetTokenOnMainThread = CoreTiming::RegisterEvent("SetToken", SetToken_OnMainThread);
et_SetFinishOnMainThread = CoreTiming::RegisterEvent("SetFinish", SetFinish_OnMainThread);
Expand Down Expand Up @@ -214,7 +213,7 @@ void Read16(u16& _uReturnValue, const u32 _iAddress)
break;

case PE_TOKEN_REG:
_uReturnValue = CommandProcessor::fifo.PEToken;
_uReturnValue = Common::AtomicLoad(*(volatile u32*)&CommandProcessor::fifo.PEToken);
INFO_LOG(PIXELENGINE, "(r16) TOKEN_REG : %04x", _uReturnValue);
break;

Expand Down Expand Up @@ -351,8 +350,8 @@ void Write16(const u16 _iValue, const u32 _iAddress)
{
UPECtrlReg tmpCtrl(_iValue);

if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = false;
if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = false;
if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = 0;
if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = 0;

m_Control.PETokenEnable = tmpCtrl.PETokenEnable;
m_Control.PEFinishEnable = tmpCtrl.PEFinishEnable;
Expand Down Expand Up @@ -398,14 +397,14 @@ void UpdateInterrupts()

void UpdateTokenInterrupt(bool active)
{
ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active);
interruptSetToken = active;
ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active);
Common::AtomicStore(interruptSetToken, active ? 1 : 0);
}

void UpdateFinishInterrupt(bool active)
{
ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active);
interruptSetFinish = active;
ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active);
Common::AtomicStore(interruptSetFinish, active ? 1 : 0);
}

// TODO(mb2): Refactor SetTokenINT_OnMainThread(u64 userdata, int cyclesLate).
Expand All @@ -415,20 +414,23 @@ void UpdateFinishInterrupt(bool active)
// Called only if BPMEM_PE_TOKEN_INT_ID is ack by GP
void SetToken_OnMainThread(u64 userdata, int cyclesLate)
{
//if (userdata >> 16)
//{
g_bSignalTokenInterrupt = true;
//_dbg_assert_msg_(PIXELENGINE, (CommandProcessor::fifo.PEToken == (userdata&0xFFFF)), "WTF? BPMEM_PE_TOKEN_INT_ID's token != BPMEM_PE_TOKEN_ID's token" );
INFO_LOG(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: %04x)", CommandProcessor::fifo.PEToken);
// XXX: No 16-bit atomic store available, so cheat and use 32-bit.
// That's what we've always done. We're counting on fifo.PEToken to be
// 4-byte padded.
Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, userdata & 0xffff);
INFO_LOG(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: %04x)", CommandProcessor::fifo.PEToken);
if (userdata >> 16)
{
Common::AtomicStore(*(volatile u32*)&g_bSignalTokenInterrupt, 1);
UpdateInterrupts();
CommandProcessor::interruptTokenWaiting = false;
IncrementCheckContextId();
//}
}
CommandProcessor::interruptTokenWaiting = false;
IncrementCheckContextId();
}

void SetFinish_OnMainThread(u64 userdata, int cyclesLate)
{
g_bSignalFinishInterrupt = 1;
Common::AtomicStore(*(volatile u32*)&g_bSignalFinishInterrupt, 1);
UpdateInterrupts();
CommandProcessor::interruptFinishWaiting = false;
CommandProcessor::isPossibleWaitingSetDrawDone = false;
Expand All @@ -438,23 +440,13 @@ void SetFinish_OnMainThread(u64 userdata, int cyclesLate)
// THIS IS EXECUTED FROM VIDEO THREAD
void SetToken(const u16 _token, const int _bSetTokenAcknowledge)
{
// TODO?: set-token-value and set-token-INT could be merged since set-token-INT own the token value.
if (_bSetTokenAcknowledge) // set token INT
{

Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, _token);
CommandProcessor::interruptTokenWaiting = true;
CoreTiming::ScheduleEvent_Threadsafe(0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16));
}
else // set token value
{
// we do it directly from videoThread because of
// Super Monkey Ball
// XXX: No 16-bit atomic store available, so cheat and use 32-bit.
// That's what we've always done. We're counting on fifo.PEToken to be
// 4-byte padded.
Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, _token);
Common::AtomicStore(*(volatile u32*)&g_bSignalTokenInterrupt, 1);
}

CommandProcessor::interruptTokenWaiting = true;
CoreTiming::ScheduleEvent_Threadsafe(0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16));
IncrementCheckContextId();
}

Expand All @@ -477,7 +469,6 @@ void ResetSetFinish()
{
UpdateFinishInterrupt(false);
g_bSignalFinishInterrupt = false;

}
else
{
Expand All @@ -491,26 +482,12 @@ void ResetSetToken()
if (g_bSignalTokenInterrupt)
{
UpdateTokenInterrupt(false);
g_bSignalTokenInterrupt = false;

g_bSignalTokenInterrupt = 0;
}
else
{
CoreTiming::RemoveEvent(et_SetTokenOnMainThread);
}
CommandProcessor::interruptTokenWaiting = false;
}

bool WaitingForPEInterrupt()
{
return !CommandProcessor::waitingForPEInterruptDisable && (CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || interruptSetFinish || interruptSetToken);
}

void ResumeWaitingForPEInterrupt()
{
interruptSetFinish = false;
interruptSetToken = false;
CommandProcessor::interruptFinishWaiting = false;
CommandProcessor::interruptTokenWaiting = false;
}
} // end of namespace PixelEngine
2 changes: 0 additions & 2 deletions Source/Core/VideoCommon/Src/PixelEngine.h
Expand Up @@ -81,8 +81,6 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge);
void SetFinish(void);
void ResetSetFinish(void);
void ResetSetToken(void);
bool WaitingForPEInterrupt();
void ResumeWaitingForPEInterrupt();

// Bounding box functionality. Paper Mario (both) are a couple of the few games that use it.
extern u16 bbox[4];
Expand Down