More accurate audio interrupts (preparation for homebrew audio support). some minor cleanup in gl plugin.
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@226 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
7962ab4713
commit
dcd5ffeb7a
|
@ -15,6 +15,13 @@
|
|||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// This file is ONLY about disc streaming. It's a bit unfortunately named.
|
||||
// For the rest of the audio stuff, including the "real" AI, see DSP.cpp/h.
|
||||
|
||||
// AI disc streaming is handled completely separately from the rest of the
|
||||
// audio processing. In short, it simply streams audio directly from disc
|
||||
// out through the speakers.
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include "StreamADPCM.H"
|
||||
|
@ -125,6 +132,8 @@ void Read32(u32& _rReturnValue, const u32 _Address)
|
|||
return;
|
||||
|
||||
case AI_INTERRUPT_TIMING:
|
||||
// When sample counter reaches the value of this register, the interrupt AIINT should
|
||||
// fire.
|
||||
LOG(AUDIO_INTERFACE, "AudioInterface(R) 0x%08x", _Address);
|
||||
_rReturnValue = g_AudioRegister.m_InterruptTiming;
|
||||
return;
|
||||
|
@ -196,7 +205,8 @@ void Write32(const u32 _Value, const u32 _Address)
|
|||
break;
|
||||
|
||||
case AI_SAMPLE_COUNTER:
|
||||
_dbg_assert_msg_(AUDIO_INTERFACE,0,"AudioInterface - m_SampleCounter is Read only");
|
||||
// _dbg_assert_msg_(AUDIO_INTERFACE, 0, "AudioInterface - m_SampleCounter is Read only");
|
||||
g_AudioRegister.m_SampleCounter = _Value;
|
||||
break;
|
||||
|
||||
case AI_INTERRUPT_TIMING:
|
||||
|
@ -229,7 +239,7 @@ void GenerateAudioInterrupt()
|
|||
UpdateInterrupts();
|
||||
}
|
||||
|
||||
// Callback for the DSP streaming
|
||||
// Callback for the disc streaming
|
||||
// WARNING - called from audio thread
|
||||
unsigned __int32 Callback_GetStreaming(short* _pDestBuffer, unsigned __int32 _numSamples)
|
||||
{
|
||||
|
@ -289,6 +299,7 @@ void ReadStreamBlock(short* _pPCM)
|
|||
// our whole streaming code is "faked" ... so it shouldn't increase the sample counter
|
||||
// streaming will never work correctly this way, but at least the program will think all is alright.
|
||||
|
||||
// This call must not be done wihout going through CoreTiming's threadsafe option.
|
||||
// IncreaseSampleCount(28);
|
||||
}
|
||||
|
||||
|
@ -298,7 +309,7 @@ void IncreaseSampleCount(const u32 _iAmount)
|
|||
{
|
||||
g_AudioRegister.m_SampleCounter += _iAmount;
|
||||
if (g_AudioRegister.m_Control.AIINTVLD &&
|
||||
(g_AudioRegister.m_SampleCounter > g_AudioRegister.m_InterruptTiming))
|
||||
(g_AudioRegister.m_SampleCounter >= g_AudioRegister.m_InterruptTiming))
|
||||
{
|
||||
GenerateAudioInterrupt();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// See CPP file for comments.
|
||||
|
||||
#ifndef _AUDIOINTERFACE_H
|
||||
#define _AUDIOINTERFACE_H
|
||||
|
||||
|
|
|
@ -15,6 +15,22 @@
|
|||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
// AID / AUDIO_DMA controls pushing audio out to the SRC and then the speakers.
|
||||
// The audio DMA pushes audio through a small FIFO 32 bytes at a time, as needed.
|
||||
// Since the SRC behind the fifo eats stereo 16-bit data at a sample rate of 32khz,
|
||||
// that is, 4 bytes at 32 khz, which is 32 bytes at 4 khz. We should thus schedule an
|
||||
// event that runs at 4khz, that eats audio from the fifo, and all the rest will follow.
|
||||
// Then we will have homebrew audio.
|
||||
|
||||
// The AID interrupt is set when the fifo STARTS a transfer. It latches address and count
|
||||
// into internal registers and starts copying. This means that the interrupt handler can simply
|
||||
// set the registers to where the next buffer is, and start filling it. When the DMA is complete,
|
||||
// it will automatically relatch and fire a new interrupt.
|
||||
|
||||
// Then there's the DSP... what likely happens is that the fifo-latched-interrupt handler
|
||||
// kicks off the DSP, requesting it to fill up the just used buffer through the AXList (or
|
||||
// whatever it might be called in Nintendo games).
|
||||
|
||||
#include "DSP.h"
|
||||
|
||||
#include "../CoreTiming.h"
|
||||
|
@ -80,8 +96,8 @@ union UDSPControl
|
|||
unsigned DSPAssertInt : 1;
|
||||
unsigned DSPHalt : 1;
|
||||
|
||||
unsigned AI : 1;
|
||||
unsigned AI_mask : 1;
|
||||
unsigned AID : 1;
|
||||
unsigned AID_mask : 1;
|
||||
unsigned ARAM : 1;
|
||||
unsigned ARAM_mask : 1;
|
||||
unsigned DSP : 1;
|
||||
|
@ -107,13 +123,13 @@ struct DSPState
|
|||
}
|
||||
};
|
||||
|
||||
// UDSPControl
|
||||
// Blocks are 32 bytes.
|
||||
union UAudioDMAControl
|
||||
{
|
||||
u16 Hex;
|
||||
struct
|
||||
{
|
||||
unsigned NumSamples : 15;
|
||||
unsigned NumBlocks : 15;
|
||||
unsigned Enabled : 1;
|
||||
};
|
||||
|
||||
|
@ -125,8 +141,9 @@ union UAudioDMAControl
|
|||
struct AudioDMA
|
||||
{
|
||||
u32 SourceAddress;
|
||||
u32 ReadAddress;
|
||||
UAudioDMAControl AudioDMAControl;
|
||||
u32 SamplesLeft;
|
||||
int BlocksLeft;
|
||||
};
|
||||
|
||||
// ARDMA
|
||||
|
@ -162,6 +179,11 @@ void WriteARAM(u8 _iValue, u32 _iAddress);
|
|||
bool Update_DSP_ReadRegister();
|
||||
void Update_DSP_WriteRegister();
|
||||
|
||||
int GetDSPSampleRate()
|
||||
{
|
||||
return 32000; // TODO - can also be 48000
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
g_ARAM = (u8 *)AllocateMemoryPages(ARAM_SIZE);
|
||||
|
@ -236,7 +258,8 @@ void Read16(u16& _uReturnValue, const u32 _iAddress)
|
|||
// DMA_REGS 0x5030+
|
||||
// ==================================================================================
|
||||
case AUDIO_DMA_BYTES_LEFT:
|
||||
_uReturnValue = g_audioDMA.SamplesLeft;
|
||||
// Hmm. Would be stupid to ask for bytes left. Assume it wants blocks left.
|
||||
_uReturnValue = g_audioDMA.BlocksLeft;
|
||||
return;
|
||||
|
||||
case AUDIO_DMA_START_LO:
|
||||
|
@ -305,12 +328,12 @@ void Write16(const u16 _Value, const u32 _Address)
|
|||
g_dspState.DSPControl.DSPInit = tmpControl.DSPInit;
|
||||
|
||||
// Interrupt (mask)
|
||||
g_dspState.DSPControl.AI_mask = tmpControl.AI_mask;
|
||||
g_dspState.DSPControl.AID_mask = tmpControl.AID_mask;
|
||||
g_dspState.DSPControl.ARAM_mask = tmpControl.ARAM_mask;
|
||||
g_dspState.DSPControl.DSP_mask = tmpControl.DSP_mask;
|
||||
|
||||
// Interrupt
|
||||
if (tmpControl.AI) g_dspState.DSPControl.AI = 0;
|
||||
if (tmpControl.AID) g_dspState.DSPControl.AID = 0;
|
||||
if (tmpControl.ARAM) g_dspState.DSPControl.ARAM = 0;
|
||||
if (tmpControl.DSP) g_dspState.DSPControl.DSP = 0;
|
||||
|
||||
|
@ -332,6 +355,7 @@ void Write16(const u16 _Value, const u32 _Address)
|
|||
|
||||
// ==================================================================================
|
||||
// AR_REGS 0x501x+
|
||||
// DMA back and forth between ARAM and RAM
|
||||
// ==================================================================================
|
||||
|
||||
case 0x5012:
|
||||
|
@ -369,6 +393,7 @@ void Write16(const u16 _Value, const u32 _Address)
|
|||
|
||||
// ==================================================================================
|
||||
// Audio DMA_REGS 0x5030+
|
||||
// This is the DMA that goes straight out the speaker.
|
||||
// ==================================================================================
|
||||
case AUDIO_DMA_START_HI:
|
||||
g_audioDMA.SourceAddress = (g_audioDMA.SourceAddress & 0xFFFF) | (_Value<<16);
|
||||
|
@ -379,16 +404,20 @@ void Write16(const u16 _Value, const u32 _Address)
|
|||
break;
|
||||
|
||||
case AUDIO_DMA_CONTROL_LEN: // called by AIStartDMA()
|
||||
g_audioDMA.AudioDMAControl.Hex = _Value;
|
||||
g_audioDMA.SamplesLeft = g_audioDMA.AudioDMAControl.NumSamples;
|
||||
|
||||
if (g_audioDMA.AudioDMAControl.Enabled)
|
||||
{
|
||||
PluginDSP::DSP_SendAIBuffer(g_audioDMA.SourceAddress, g_audioDMA.AudioDMAControl.NumSamples);
|
||||
g_audioDMA.SamplesLeft = 0;
|
||||
UAudioDMAControl old_control = g_audioDMA.AudioDMAControl;
|
||||
g_audioDMA.AudioDMAControl.Hex = _Value;
|
||||
|
||||
if (!old_control.Enabled && g_audioDMA.AudioDMAControl.Enabled)
|
||||
{
|
||||
// Enabled bit was flipped to true, let's latch address & length and call the interrupt.
|
||||
g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks;
|
||||
g_audioDMA.ReadAddress = g_audioDMA.SourceAddress;
|
||||
GenerateDSPInterrupt(DSP::INT_AID);
|
||||
LOG(DSP, "AID DMA started - source address %08x, length %i blocks", g_audioDMA.SourceAddress, g_audioDMA.AudioDMAControl.NumBlocks);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
case AUDIO_DMA_BYTES_LEFT:
|
||||
_dbg_assert_(DSPINTERFACE,0);
|
||||
break;
|
||||
|
@ -399,6 +428,25 @@ void Write16(const u16 _Value, const u32 _Address)
|
|||
}
|
||||
}
|
||||
|
||||
// This happens at 4 khz, since 32 bytes at 4khz = 4 bytes at 32 khz (16bit stereo pcm)
|
||||
void UpdateAudioDMA()
|
||||
{
|
||||
if (g_audioDMA.AudioDMAControl.Enabled && g_audioDMA.BlocksLeft) {
|
||||
// TODO : Read audio at g_audioDMA.ReadAddress in RAM and push onto an external audio fifo in the emulator,
|
||||
// to be mixed with the disc streaming output. If that audio queue fills up, we have to delay the emulator.
|
||||
g_audioDMA.ReadAddress += 32;
|
||||
g_audioDMA.BlocksLeft--;
|
||||
if (!g_audioDMA.BlocksLeft) {
|
||||
// No need to turn off the DMA - we can only get here if we had blocks left when we
|
||||
// entered this function, and no longer have any.
|
||||
// Latch new parameters
|
||||
g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks;
|
||||
g_audioDMA.ReadAddress = g_audioDMA.SourceAddress;
|
||||
GenerateDSPInterrupt(DSP::INT_AID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Read32(u32& _uReturnValue, const u32 _iAddress)
|
||||
{
|
||||
LOG(DSPINTERFACE, "DSPInterface(r) 0x%08x", _iAddress);
|
||||
|
@ -450,7 +498,7 @@ void Write32(const u32 _iValue, const u32 _iAddress)
|
|||
//
|
||||
void UpdateInterrupts()
|
||||
{
|
||||
if ((g_dspState.DSPControl.AI & g_dspState.DSPControl.AI_mask) ||
|
||||
if ((g_dspState.DSPControl.AID & g_dspState.DSPControl.AID_mask) ||
|
||||
(g_dspState.DSPControl.ARAM & g_dspState.DSPControl.ARAM_mask) ||
|
||||
(g_dspState.DSPControl.DSP & g_dspState.DSPControl.DSP_mask))
|
||||
{
|
||||
|
@ -468,7 +516,7 @@ void GenerateDSPInterrupt(DSPInterruptType type, bool _bSet)
|
|||
{
|
||||
case INT_DSP: g_dspState.DSPControl.DSP = _bSet ? 1 : 0; break;
|
||||
case INT_ARAM: g_dspState.DSPControl.ARAM = _bSet ? 1 : 0; break;
|
||||
case INT_AI: g_dspState.DSPControl.AI = _bSet ? 1 : 0; break;
|
||||
case INT_AID: g_dspState.DSPControl.AID = _bSet ? 1 : 0; break;
|
||||
}
|
||||
|
||||
UpdateInterrupts();
|
||||
|
|
|
@ -27,7 +27,7 @@ enum DSPInterruptType
|
|||
{
|
||||
INT_DSP = 0,
|
||||
INT_ARAM = 1,
|
||||
INT_AI = 2
|
||||
INT_AID = 2
|
||||
};
|
||||
|
||||
void Init();
|
||||
|
@ -49,8 +49,8 @@ u8 ReadARAM(const u32 _uAddress);
|
|||
// Debugger Helper
|
||||
u8* GetARAMPtr();
|
||||
|
||||
|
||||
void UpdateAudioDMA();
|
||||
int GetDSPSampleRate();
|
||||
|
||||
}// end of namespace DSP
|
||||
|
||||
|
|
|
@ -47,6 +47,10 @@ void Init()
|
|||
ResetGatherPipe();
|
||||
}
|
||||
|
||||
bool IsEmpty() {
|
||||
return m_gatherPipeCount == 0;
|
||||
}
|
||||
|
||||
void ResetGatherPipe()
|
||||
{
|
||||
m_gatherPipeCount = 0;
|
||||
|
|
|
@ -40,6 +40,8 @@ void Init();
|
|||
void ResetGatherPipe();
|
||||
void CheckGatherPipe();
|
||||
|
||||
bool IsEmpty();
|
||||
|
||||
// Write
|
||||
void HWCALL Write8(const u8 _iValue, const u32 _iAddress);
|
||||
void HWCALL Write16(const u16 _iValue, const u32 _iAddress);
|
||||
|
|
|
@ -54,13 +54,22 @@ int
|
|||
// VideoInterface::Update is stupid!
|
||||
VI_PERIOD = GetTicksPerSecond() / (60*120),
|
||||
|
||||
// TODO: The SI interfact actually has a register that determines the polling frequency.
|
||||
// We should obey that instead of arbitrarly checking at 60fps.
|
||||
SI_PERIOD = GetTicksPerSecond() / 60, //once a frame is good for controllers
|
||||
|
||||
// These are the big question marks IMHO :)
|
||||
// This one should simply be determined by the increasing counter in AI.
|
||||
AI_PERIOD = GetTicksPerSecond() / 80,
|
||||
|
||||
// These shouldn't be period controlled either, most likely.
|
||||
DSP_PERIOD = GetTicksPerSecond() / 250,
|
||||
DSPINT_PERIOD = GetTicksPerSecond() / 230,
|
||||
|
||||
// This is completely arbitrary. If we find that we need lower latency, we can just
|
||||
// increase this number.
|
||||
HLE_IPC_PERIOD = GetTicksPerSecond() / 250,
|
||||
|
||||
// This one is also fairly arbitrary. Every N cycles, run the GPU until it starves (in single core mode only).
|
||||
GPU_PERIOD = 10000;
|
||||
|
||||
u32 GetTicksPerSecond()
|
||||
|
@ -75,10 +84,28 @@ u32 ConvertMillisecondsToTicks(u32 _Milliseconds)
|
|||
|
||||
void AICallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
// Update disk streaming. All that code really needs a revamp, including replacing the codec with the one
|
||||
// from in_cube.
|
||||
AudioInterface::Update();
|
||||
CoreTiming::ScheduleEvent(AI_PERIOD-cyclesLate, &AICallback, "AICallback");
|
||||
}
|
||||
|
||||
void DSPCallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
// Poke the DSP, make it do stuff. This should eventually be replaced with dsp->RunCycles(1000) or whatever,
|
||||
// ~1/6th as many cycles as the period PPC-side.
|
||||
PluginDSP::DSP_Update();
|
||||
CoreTiming::ScheduleEvent(DSP_PERIOD-cyclesLate, &DSPCallback, "DSPCallback");
|
||||
}
|
||||
|
||||
void AudioFifoCallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
int period = CPU_CORE_CLOCK / (DSP::GetDSPSampleRate() * 4 / 32);
|
||||
DSP::UpdateAudioDMA(); // Push audio to speakers.
|
||||
|
||||
CoreTiming::ScheduleEvent(period - cyclesLate, &AudioFifoCallback, "AudioFifoCallback");
|
||||
}
|
||||
|
||||
void IPC_HLE_UpdateCallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
WII_IPC_HLE_Interface::Update();
|
||||
|
@ -100,18 +127,6 @@ void SICallback(u64 userdata, int cyclesLate)
|
|||
CoreTiming::ScheduleEvent(SI_PERIOD-cyclesLate, &SICallback, "SICallback");
|
||||
}
|
||||
|
||||
void DSPCallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
PluginDSP::DSP_Update();
|
||||
CoreTiming::ScheduleEvent(DSP_PERIOD-cyclesLate, &DSPCallback, "DSPCallback");
|
||||
}
|
||||
|
||||
void DSPInterruptCallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
DSP::GenerateDSPInterrupt(DSP::INT_AI);
|
||||
CoreTiming::ScheduleEvent(DSPINT_PERIOD-cyclesLate, &DSPInterruptCallback, "DSPInterruptCallback");
|
||||
}
|
||||
|
||||
void DecrementerCallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
//Why is fakeDec too big here?
|
||||
|
@ -122,7 +137,6 @@ void DecrementerCallback(u64 userdata, int cyclesLate)
|
|||
|
||||
void DecrementerSet()
|
||||
{
|
||||
// MessageBox(0, "dec set",0,0);
|
||||
u32 decValue = PowerPC::ppcState.spr[SPR_DEC];
|
||||
fakeDec = decValue*TIMER_RATIO;
|
||||
CoreTiming::RemoveEvent(DecrementerCallback);
|
||||
|
@ -131,10 +145,7 @@ void DecrementerSet()
|
|||
|
||||
void AdvanceCallback(int cyclesExecuted)
|
||||
{
|
||||
//int oldFakeDec = fakeDec;
|
||||
fakeDec -= cyclesExecuted;
|
||||
//if (fakeDec < 0 && oldFakeDec > 0)
|
||||
// fakeDec = TIMER_RATIO;
|
||||
u64 timebase_ticks = CoreTiming::GetTicks() / TIMER_RATIO; //works since we are little endian and TL comes first :)
|
||||
*(u64*)&TL = timebase_ticks;
|
||||
if (fakeDec >= 0)
|
||||
|
@ -185,26 +196,24 @@ void Init()
|
|||
if (Core::GetStartupParameter().bWii)
|
||||
{
|
||||
CPU_CORE_CLOCK = 721000000;
|
||||
VI_PERIOD = GetTicksPerSecond() / (60*120),
|
||||
SI_PERIOD = GetTicksPerSecond() / 60, //once a frame is good for controllers
|
||||
VI_PERIOD = GetTicksPerSecond() / (60*120);
|
||||
SI_PERIOD = GetTicksPerSecond() / 60; // once a frame is good for controllers
|
||||
|
||||
// These are the big question marks IMHO :)
|
||||
AI_PERIOD = GetTicksPerSecond() / 80,
|
||||
DSP_PERIOD = (int)(GetTicksPerSecond() * 0.003f),
|
||||
DSPINT_PERIOD = (int)(GetTicksPerSecond() * 0.003f);
|
||||
AI_PERIOD = GetTicksPerSecond() / 80;
|
||||
DSP_PERIOD = (int)(GetTicksPerSecond() * 0.003f);
|
||||
|
||||
HLE_IPC_PERIOD = (int)(GetTicksPerSecond() * 0.003f);
|
||||
}
|
||||
else
|
||||
{
|
||||
CPU_CORE_CLOCK = 486000000;
|
||||
VI_PERIOD = GetTicksPerSecond() / (60*120),
|
||||
SI_PERIOD = GetTicksPerSecond() / 60, //once a frame is good for controllers
|
||||
VI_PERIOD = GetTicksPerSecond() / (60*120);
|
||||
SI_PERIOD = GetTicksPerSecond() / 60; // once a frame is good for controllers
|
||||
|
||||
// These are the big question marks IMHO :)
|
||||
AI_PERIOD = GetTicksPerSecond() / 80,
|
||||
DSP_PERIOD = (int)(GetTicksPerSecond() * 0.005f),
|
||||
DSPINT_PERIOD = (int)(GetTicksPerSecond() *0.005f);
|
||||
AI_PERIOD = GetTicksPerSecond() / 80;
|
||||
DSP_PERIOD = (int)(GetTicksPerSecond() * 0.005f);
|
||||
}
|
||||
Common::Timer::IncreaseResolution();
|
||||
memset(timeHistory, 0, sizeof(timeHistory));
|
||||
|
@ -216,7 +225,7 @@ void Init()
|
|||
CoreTiming::ScheduleEvent(VI_PERIOD, &VICallback, "VICallback");
|
||||
CoreTiming::ScheduleEvent(DSP_PERIOD, &DSPCallback, "DSPCallback");
|
||||
CoreTiming::ScheduleEvent(SI_PERIOD, &SICallback, "SICallback");
|
||||
CoreTiming::ScheduleEvent(DSPINT_PERIOD, &DSPInterruptCallback, "DSPInterruptCallback");
|
||||
CoreTiming::ScheduleEvent(CPU_CORE_CLOCK / (32000 * 4 / 32), &AudioFifoCallback, "AudioFifoCallback");
|
||||
|
||||
if (!Core::GetStartupParameter().bUseDualCore)
|
||||
CoreTiming::ScheduleEvent(GPU_PERIOD, &RunGPUCallback, "RunGPUCallback");
|
||||
|
|
|
@ -238,7 +238,7 @@ void CInterpreter::mfspr(UGeckoInstruction _inst)
|
|||
|
||||
//TODO - check processor privilege level - many of these require privilege
|
||||
//XER LR CTR are the only ones available in user mode, time base can be read too.
|
||||
//Not that gamecube games usually run user mode, but hey....
|
||||
//Gamecube games always run in superuser mode, but hey....
|
||||
|
||||
switch (iIndex)
|
||||
{
|
||||
|
@ -247,11 +247,8 @@ void CInterpreter::mfspr(UGeckoInstruction _inst)
|
|||
// break;
|
||||
case SPR_WPAR:
|
||||
{
|
||||
//There's supposed to be a bit here that indicates whether there is any data in the wp
|
||||
//(or if it's full, not sure)
|
||||
//MessageBox(NULL, "Read from SPR_WPAR", "????", MB_OK);
|
||||
//Paper Mario reads here, this should be investigated ... TODO(ector)
|
||||
bool wpar_empty = true;
|
||||
// If wpar_empty ever is false, Paper Mario hangs. Strange.
|
||||
bool wpar_empty = true; //GPFifo::IsEmpty();
|
||||
if (!wpar_empty)
|
||||
rSPR(iIndex) |= 1; // BNE = buffer not empty
|
||||
else
|
||||
|
@ -268,9 +265,9 @@ void CInterpreter::mtspr(UGeckoInstruction _inst)
|
|||
u32 oldValue = rSPR(iIndex);
|
||||
rSPR(iIndex) = m_GPR[_inst.RD];
|
||||
|
||||
//TODO(ector) - check processor privilege level - many of these require privilege
|
||||
//XER LR CTR are the only ones available in user mode
|
||||
//Not that gamecube games usually run user mode, but hey....
|
||||
//TODO - check processor privilege level - many of these require privilege
|
||||
//XER LR CTR are the only ones available in user mode, time base can be read too.
|
||||
//Gamecube games always run in superuser mode, but hey....
|
||||
|
||||
//Our DMA emulation is highly inaccurate - instead of properly emulating the queue
|
||||
//and so on, we simply make all DMA:s complete instantaneously.
|
||||
|
@ -306,7 +303,6 @@ void CInterpreter::mtspr(UGeckoInstruction _inst)
|
|||
//TODO(ector): Protect LC memory if LCE is false.
|
||||
//TODO(ector): Honor PSE.
|
||||
|
||||
//
|
||||
//_assert_msg_(GEKKO, WriteGatherPipeEnable, "Write gather pipe not enabled!");
|
||||
//if ((HID2.PSE == 0))
|
||||
// MessageBox(NULL, "PSE in HID2 is set", "Warning", MB_OK);
|
||||
|
|
|
@ -77,7 +77,9 @@ namespace Jit64
|
|||
int d = inst.RD;
|
||||
switch (iIndex)
|
||||
{
|
||||
|
||||
case SPR_WPAR:
|
||||
Default(inst);
|
||||
return;
|
||||
// case SPR_DEC:
|
||||
//MessageBox(NULL, "Read from DEC", "????", MB_OK);
|
||||
//break;
|
||||
|
|
|
@ -1048,6 +1048,10 @@
|
|||
RelativePath=".\Src\Render.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\SConscript"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\XFB.cpp"
|
||||
>
|
||||
|
|
|
@ -15,6 +15,13 @@
|
|||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/filepicker.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/aboutdlg.h>
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
#include "pluginspecs_video.h"
|
||||
|
|
|
@ -19,12 +19,6 @@
|
|||
#ifndef _GLOBALS_H
|
||||
#define _GLOBALS_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/filepicker.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/aboutdlg.h>
|
||||
|
||||
#define LOGGING
|
||||
|
||||
#include "Common.h"
|
||||
|
@ -32,13 +26,6 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
void OpenConsole();
|
||||
void CloseConsole();
|
||||
|
||||
#define GLEW_STATIC
|
||||
|
||||
#include "GLew/glew.h"
|
||||
|
@ -97,16 +84,8 @@ struct RECT
|
|||
#define GL_DEPTH24_STENCIL8_EXT 0x88F0
|
||||
#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1
|
||||
#endif
|
||||
extern float MValue;
|
||||
// several macros
|
||||
// PLEASE DO NOT USE THE FOLLOWING SAFE*
|
||||
// They only encourage sloppy coding, and hide bugs.
|
||||
#define SAFE_RELEASE_CGPROG(x) { if( (x) != NULL ) { cgDestroyProgram(x); x = NULL; } }
|
||||
#define SAFE_RELEASE_PROG(x) { if( (x) != 0 ) { glDeleteProgramsARB(1, &(x)); x = 0; } }
|
||||
#define SAFE_RELEASE_TEX(x) { if( (x) != 0 ) { glDeleteTextures(1, &(x)); x = 0; } }
|
||||
#define SAFE_RELEASE_BUF(x) { if( (x) != 0 ) { glDeleteBuffers(1, &(x)); x = 0; } }
|
||||
|
||||
#define FORIT(it, v) for(it = (v).begin(); it != (v).end(); ++(it))
|
||||
extern float MValue;
|
||||
|
||||
#define ERROR_LOG __Log
|
||||
|
||||
|
|
|
@ -18,11 +18,20 @@
|
|||
|
||||
#include <windows.h>
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/filepicker.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/aboutdlg.h>
|
||||
|
||||
#include "../Globals.h"
|
||||
|
||||
#include "../../Core/Src/Core.h"
|
||||
#include "Win32.h"
|
||||
|
||||
void OpenConsole();
|
||||
void CloseConsole();
|
||||
|
||||
HINSTANCE g_hInstance;
|
||||
|
||||
class wxDLLApp : public wxApp
|
||||
|
|
|
@ -94,14 +94,15 @@ void PixelShaderMngr::Init()
|
|||
GL_REPORT_ERROR();
|
||||
if( err != GL_NO_ERROR ) {
|
||||
ERROR_LOG("Failed to create color matrix fragment program\n");
|
||||
|
||||
SAFE_RELEASE_PROG(s_ColorMatrixProgram);
|
||||
glDeleteProgramsARB(1, &s_ColorMatrixProgram);
|
||||
s_ColorMatrixProgram = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PixelShaderMngr::Shutdown()
|
||||
{
|
||||
SAFE_RELEASE_PROG(s_ColorMatrixProgram);
|
||||
glDeleteProgramsARB(1, &s_ColorMatrixProgram);
|
||||
s_ColorMatrixProgram = 0;
|
||||
PSCache::iterator iter = pshaders.begin();
|
||||
for (; iter != pshaders.end(); iter++)
|
||||
iter->second.Destroy();
|
||||
|
|
|
@ -80,7 +80,8 @@ void TextureMngr::TCacheEntry::SetTextureParameters(TexMode0& newmode)
|
|||
|
||||
void TextureMngr::TCacheEntry::Destroy()
|
||||
{
|
||||
SAFE_RELEASE_TEX(texture);
|
||||
glDeleteTextures(1, &texture);
|
||||
texture = 0;
|
||||
}
|
||||
|
||||
void TextureMngr::Init()
|
||||
|
|
|
@ -1087,7 +1087,7 @@ void VertexManager::Flush()
|
|||
|
||||
int offset = 0;
|
||||
vector< pair<int, int> >::iterator it;
|
||||
FORIT (it, s_vStoredPrimitives) {
|
||||
for (it = s_vStoredPrimitives.begin(); it != s_vStoredPrimitives.end(); ++it) {
|
||||
glDrawArrays(it->first, offset, it->second);
|
||||
offset += it->second;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue