Added a instant ARAM DMA mode which is enabled automatically when required.

Detects a situation where the game is writing to the dcache at the address being DMA'd. As we do not have dcache emulation, invalid data is being DMA'd causing audio glitches. The following code detects this and enables the DMA to complete instantly before the invalid data is written.
Added accurate ARAM DMA transfer timing.
Removed the addition of DSP exception checking.
This commit is contained in:
skidau 2014-09-27 20:47:29 +10:00
parent 4b37fdfa45
commit 86b6dfe4b3
5 changed files with 67 additions and 27 deletions

View File

@ -158,6 +158,9 @@ static ARAMInfo g_ARAM;
static DSPState g_dspState; static DSPState g_dspState;
static AudioDMA g_audioDMA; static AudioDMA g_audioDMA;
static ARAM_DMA g_arDMA; static ARAM_DMA g_arDMA;
static u32 last_mmaddr;
static u32 last_aram_dma_count;
static bool instant_dma;
union ARAM_Info union ARAM_Info
{ {
@ -195,6 +198,9 @@ void DoState(PointerWrap &p)
p.Do(g_AR_MODE); p.Do(g_AR_MODE);
p.Do(g_AR_REFRESH); p.Do(g_AR_REFRESH);
p.Do(dsp_slice); p.Do(dsp_slice);
p.Do(last_mmaddr);
p.Do(last_aram_dma_count);
p.Do(instant_dma);
dsp_emulator->DoState(p); dsp_emulator->DoState(p);
} }
@ -213,6 +219,12 @@ static void CompleteARAM(u64 userdata, int cyclesLate)
GenerateDSPInterrupt(INT_ARAM); GenerateDSPInterrupt(INT_ARAM);
} }
void EnableInstantDMA()
{
CoreTiming::RemoveEvent(et_CompleteARAM);
CompleteARAM(0, 0);
instant_dma = true;
}
DSPEmulator *GetDSPEmulator() DSPEmulator *GetDSPEmulator()
{ {
@ -250,6 +262,11 @@ void Init(bool hle)
g_AR_MODE = 1; // ARAM Controller has init'd g_AR_MODE = 1; // ARAM Controller has init'd
g_AR_REFRESH = 156; // 156MHz g_AR_REFRESH = 156; // 156MHz
instant_dma = false;
last_aram_dma_count = 0;
last_mmaddr = 0;
et_GenerateDSPInterrupt = CoreTiming::RegisterEvent("DSPint", GenerateDSPInterrupt); et_GenerateDSPInterrupt = CoreTiming::RegisterEvent("DSPint", GenerateDSPInterrupt);
et_CompleteARAM = CoreTiming::RegisterEvent("ARAMint", CompleteARAM); et_CompleteARAM = CoreTiming::RegisterEvent("ARAMint", CompleteARAM);
} }
@ -442,14 +459,6 @@ static void UpdateInterrupts()
bool ints_set = (((g_dspState.DSPControl.Hex >> 1) & g_dspState.DSPControl.Hex & (INT_DSP | INT_ARAM | INT_AID)) != 0); bool ints_set = (((g_dspState.DSPControl.Hex >> 1) & g_dspState.DSPControl.Hex & (INT_DSP | INT_ARAM | INT_AID)) != 0);
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_DSP, ints_set); ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_DSP, ints_set);
if ((g_dspState.DSPControl.Hex >> 1) & g_dspState.DSPControl.Hex & INT_ARAM)
{
if (g_dspState.DSPControl.ARAM & g_dspState.DSPControl.ARAM_mask)
{
JitInterface::CompileExceptionCheck(JitInterface::EXCEPTIONS_ARAM_DMA);
}
}
} }
static void GenerateDSPInterrupt(u64 DSPIntType, int cyclesLate) static void GenerateDSPInterrupt(u64 DSPIntType, int cyclesLate)
@ -525,11 +534,20 @@ void UpdateAudioDMA()
static void Do_ARAM_DMA() static void Do_ARAM_DMA()
{ {
g_dspState.DSPControl.DMAState = 1; g_dspState.DSPControl.DMAState = 1;
CoreTiming::ScheduleEvent_Threadsafe(0, et_CompleteARAM);
// Force an early exception check on large transfers (transfers longer than 250+ ticks). // ARAM DMA transfer rate has been measured on real hw
// The shorter transfers are checked by dspARAMAddresses. Fixes RE2 audio. int ticksToTransfer = (g_arDMA.Cnt.count / 32) * 246;
CoreTiming::ForceExceptionCheck(250);
if (instant_dma)
ticksToTransfer = 0;
CoreTiming::ScheduleEvent_Threadsafe(ticksToTransfer, et_CompleteARAM);
if (instant_dma)
CoreTiming::ForceExceptionCheck(100);
last_mmaddr = g_arDMA.MMAddr;
last_aram_dma_count = g_arDMA.Cnt.count;
// Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks // Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks
if (g_arDMA.Cnt.dir) if (g_arDMA.Cnt.dir)
@ -655,5 +673,14 @@ u8 *GetARAMPtr()
return g_ARAM.ptr; return g_ARAM.ptr;
} }
u64 DMAInProgress()
{
if (g_dspState.DSPControl.DMAState == 1)
{
return ((u64)last_mmaddr << 32 | (last_mmaddr + last_aram_dma_count));
}
return 0;
}
} // end of namespace DSP } // end of namespace DSP

View File

@ -76,5 +76,7 @@ u8* GetARAMPtr();
void UpdateAudioDMA(); void UpdateAudioDMA();
void UpdateDSPSlice(int cycles); void UpdateDSPSlice(int cycles);
u64 DMAInProgress();
void EnableInstantDMA();
}// end of namespace DSP }// end of namespace DSP

View File

@ -5,6 +5,7 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/MathUtil.h" #include "Common/MathUtil.h"
#include "Core/HW/DSP.h"
#include "Core/PowerPC/JitInterface.h" #include "Core/PowerPC/JitInterface.h"
#include "Core/PowerPC/Interpreter/Interpreter.h" #include "Core/PowerPC/Interpreter/Interpreter.h"
#include "Core/PowerPC/Interpreter/Interpreter_FPUtils.h" #include "Core/PowerPC/Interpreter/Interpreter_FPUtils.h"
@ -325,24 +326,40 @@ void Interpreter::dcbf(UGeckoInstruction _inst)
{ {
NPC = PC + 12; NPC = PC + 12;
}*/ }*/
u32 address = Helper_Get_EA_X(_inst); u32 address = Helper_Get_EA_X(_inst);
JitInterface::InvalidateICache(address & ~0x1f, 32); JitInterface::InvalidateICache(address & ~0x1f, 32);
} }
void Interpreter::dcbi(UGeckoInstruction _inst) void Interpreter::dcbi(UGeckoInstruction _inst)
{ {
// Removes a block from data cache. Since we don't emulate the data cache, we don't need to do anything to the data cache // Removes a block from data cache. Since we don't emulate the data cache, we don't need to do anything to the data cache
// However, we invalidate the jit block cache on dcbi // However, we invalidate the jit block cache on dcbi
u32 address = Helper_Get_EA_X(_inst); u32 address = Helper_Get_EA_X(_inst);
JitInterface::InvalidateICache(address & ~0x1f, 32); JitInterface::InvalidateICache(address & ~0x1f, 32);
// The following detects a situation where the game is writing to the dcache at the address being DMA'd. As we do not
// have dcache emulation, invalid data is being DMA'd causing audio glitches. The following code detects this and
// enables the DMA to complete instantly before the invalid data is written.
u64 dma_in_progress = DSP::DMAInProgress();
if (dma_in_progress != 0)
{
u32 start_addr = (dma_in_progress >> 32) & Memory::RAM_MASK;
u32 end_addr = (dma_in_progress & Memory::RAM_MASK) & 0xffffffff;
u32 invalidated_addr = (address & Memory::RAM_MASK) & ~0x1f;
if (invalidated_addr >= start_addr && invalidated_addr <= end_addr)
{
DSP::EnableInstantDMA();
}
}
} }
void Interpreter::dcbst(UGeckoInstruction _inst) void Interpreter::dcbst(UGeckoInstruction _inst)
{ {
// Cache line flush. Since we don't emulate the data cache, we don't need to do anything. // Cache line flush. Since we don't emulate the data cache, we don't need to do anything.
// Invalidate the jit block cache on dcbst in case new code has been loaded via the data cache // Invalidate the jit block cache on dcbst in case new code has been loaded via the data cache
u32 address = Helper_Get_EA_X(_inst); u32 address = Helper_Get_EA_X(_inst);
JitInterface::InvalidateICache(address & ~0x1f, 32); JitInterface::InvalidateICache(address & ~0x1f, 32);
} }
void Interpreter::dcbt(UGeckoInstruction _inst) void Interpreter::dcbt(UGeckoInstruction _inst)

View File

@ -251,19 +251,14 @@ namespace JitInterface
exception_addresses = &jit->js.fifoWriteAddresses; exception_addresses = &jit->js.fifoWriteAddresses;
break; break;
} }
case EXCEPTIONS_ARAM_DMA:
{
exception_addresses = &jit->js.dspARAMAddresses;
break;
}
default: default:
ERROR_LOG(POWERPC, "Unknown exception check type"); ERROR_LOG(POWERPC, "Unknown exception check type");
} }
if (PC != 0 && (exception_addresses->find(PC)) == (exception_addresses->end())) if (PC != 0 && (exception_addresses->find(PC)) == (exception_addresses->end()))
{ {
int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type; int optype = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type;
if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_STOREPS)) if (optype == OPTYPE_STORE || optype == OPTYPE_STOREFP || (optype == OPTYPE_STOREPS))
{ {
exception_addresses->insert(PC); exception_addresses->insert(PC);

View File

@ -13,8 +13,7 @@ namespace JitInterface
{ {
enum enum
{ {
EXCEPTIONS_FIFO_WRITE, EXCEPTIONS_FIFO_WRITE
EXCEPTIONS_ARAM_DMA
}; };
void DoState(PointerWrap &p); void DoState(PointerWrap &p);