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:
parent
4b37fdfa45
commit
86b6dfe4b3
|
@ -158,6 +158,9 @@ static ARAMInfo g_ARAM;
|
|||
static DSPState g_dspState;
|
||||
static AudioDMA g_audioDMA;
|
||||
static ARAM_DMA g_arDMA;
|
||||
static u32 last_mmaddr;
|
||||
static u32 last_aram_dma_count;
|
||||
static bool instant_dma;
|
||||
|
||||
union ARAM_Info
|
||||
{
|
||||
|
@ -195,6 +198,9 @@ void DoState(PointerWrap &p)
|
|||
p.Do(g_AR_MODE);
|
||||
p.Do(g_AR_REFRESH);
|
||||
p.Do(dsp_slice);
|
||||
p.Do(last_mmaddr);
|
||||
p.Do(last_aram_dma_count);
|
||||
p.Do(instant_dma);
|
||||
|
||||
dsp_emulator->DoState(p);
|
||||
}
|
||||
|
@ -213,6 +219,12 @@ static void CompleteARAM(u64 userdata, int cyclesLate)
|
|||
GenerateDSPInterrupt(INT_ARAM);
|
||||
}
|
||||
|
||||
void EnableInstantDMA()
|
||||
{
|
||||
CoreTiming::RemoveEvent(et_CompleteARAM);
|
||||
CompleteARAM(0, 0);
|
||||
instant_dma = true;
|
||||
}
|
||||
|
||||
DSPEmulator *GetDSPEmulator()
|
||||
{
|
||||
|
@ -250,6 +262,11 @@ void Init(bool hle)
|
|||
g_AR_MODE = 1; // ARAM Controller has init'd
|
||||
g_AR_REFRESH = 156; // 156MHz
|
||||
|
||||
instant_dma = false;
|
||||
|
||||
last_aram_dma_count = 0;
|
||||
last_mmaddr = 0;
|
||||
|
||||
et_GenerateDSPInterrupt = CoreTiming::RegisterEvent("DSPint", GenerateDSPInterrupt);
|
||||
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);
|
||||
|
||||
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)
|
||||
|
@ -525,11 +534,20 @@ void UpdateAudioDMA()
|
|||
static void Do_ARAM_DMA()
|
||||
{
|
||||
g_dspState.DSPControl.DMAState = 1;
|
||||
CoreTiming::ScheduleEvent_Threadsafe(0, et_CompleteARAM);
|
||||
|
||||
// Force an early exception check on large transfers (transfers longer than 250+ ticks).
|
||||
// The shorter transfers are checked by dspARAMAddresses. Fixes RE2 audio.
|
||||
CoreTiming::ForceExceptionCheck(250);
|
||||
// ARAM DMA transfer rate has been measured on real hw
|
||||
int ticksToTransfer = (g_arDMA.Cnt.count / 32) * 246;
|
||||
|
||||
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
|
||||
if (g_arDMA.Cnt.dir)
|
||||
|
@ -655,5 +673,14 @@ u8 *GetARAMPtr()
|
|||
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
|
||||
|
||||
|
|
|
@ -76,5 +76,7 @@ u8* GetARAMPtr();
|
|||
|
||||
void UpdateAudioDMA();
|
||||
void UpdateDSPSlice(int cycles);
|
||||
u64 DMAInProgress();
|
||||
void EnableInstantDMA();
|
||||
|
||||
}// end of namespace DSP
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MathUtil.h"
|
||||
|
||||
#include "Core/HW/DSP.h"
|
||||
#include "Core/PowerPC/JitInterface.h"
|
||||
#include "Core/PowerPC/Interpreter/Interpreter.h"
|
||||
#include "Core/PowerPC/Interpreter/Interpreter_FPUtils.h"
|
||||
|
@ -325,24 +326,40 @@ void Interpreter::dcbf(UGeckoInstruction _inst)
|
|||
{
|
||||
NPC = PC + 12;
|
||||
}*/
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
JitInterface::InvalidateICache(address & ~0x1f, 32);
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
JitInterface::InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
|
||||
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
|
||||
// However, we invalidate the jit block cache on dcbi
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
JitInterface::InvalidateICache(address & ~0x1f, 32);
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
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)
|
||||
{
|
||||
// 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
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
JitInterface::InvalidateICache(address & ~0x1f, 32);
|
||||
u32 address = Helper_Get_EA_X(_inst);
|
||||
JitInterface::InvalidateICache(address & ~0x1f, 32);
|
||||
}
|
||||
|
||||
void Interpreter::dcbt(UGeckoInstruction _inst)
|
||||
|
|
|
@ -251,19 +251,14 @@ namespace JitInterface
|
|||
exception_addresses = &jit->js.fifoWriteAddresses;
|
||||
break;
|
||||
}
|
||||
case EXCEPTIONS_ARAM_DMA:
|
||||
{
|
||||
exception_addresses = &jit->js.dspARAMAddresses;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ERROR_LOG(POWERPC, "Unknown exception check type");
|
||||
}
|
||||
|
||||
if (PC != 0 && (exception_addresses->find(PC)) == (exception_addresses->end()))
|
||||
{
|
||||
int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type;
|
||||
if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_STOREPS))
|
||||
int optype = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type;
|
||||
if (optype == OPTYPE_STORE || optype == OPTYPE_STOREFP || (optype == OPTYPE_STOREPS))
|
||||
{
|
||||
exception_addresses->insert(PC);
|
||||
|
||||
|
|
|
@ -13,8 +13,7 @@ namespace JitInterface
|
|||
{
|
||||
enum
|
||||
{
|
||||
EXCEPTIONS_FIFO_WRITE,
|
||||
EXCEPTIONS_ARAM_DMA
|
||||
EXCEPTIONS_FIFO_WRITE
|
||||
};
|
||||
|
||||
void DoState(PointerWrap &p);
|
||||
|
|
Loading…
Reference in New Issue