SPU2: merging some codebase callbacks

This commit is contained in:
Gauvain 'GovanifY' Roussel-Tarbouriech 2020-09-24 15:26:34 +02:00 committed by refractionpcsx2
parent 3d3ccbfcd7
commit 9c97092efd
11 changed files with 1081 additions and 0 deletions

View File

@ -222,6 +222,46 @@ set(pcsx2CDVDHeaders
CDVD/zlib_indexed.h
)
# SPU2 sources
set(pcsx2SPU2Sources
SPU2/ADSR.cpp
SPU2/Debug.cpp
SPU2/DplIIdecoder.cpp
SPU2/Dma.cpp
SPU2/Lowpass.cpp
SPU2/Mixer.cpp
SPU2/spu2.cpp
SPU2/ReadInput.cpp
SPU2/RegLog.cpp
SPU2/RegTable.cpp
SPU2/Reverb.cpp
SPU2/SndOut.cpp
SPU2/SndOut_SDL.cpp
SPU2/spu2freeze.cpp
SPU2/spu2replay.cpp
SPU2/spu2sys.cpp
SPU2/Timestretcher.cpp
SPU2/Wavedump_wav.cpp
SPU2/WavFile.cpp
)
# SPU2 headers
set(pcsx2SPU2Headers
SPU2/Config.h
SPU2/Debug.h
SPU2/defs.h
SPU2/Dma.h
SPU2/Global.h
SPU2/Lowpass.h
SPU2/Mixer.h
SPU2/spu2.h
SPU2/regs.h
SPU2/SndOut.h
SPU2/spdif.h
SPU2/spu2replay.h
SPU2/WavFile.h
)
# DebugTools sources
set(pcsx2DebugToolsSources
DebugTools/DebugInterface.cpp
@ -651,6 +691,8 @@ set(Common
${pcsx2Headers}
${pcsx2CDVDSources}
${pcsx2CDVDHeaders}
${pcsx2SPU2Sources}
${pcsx2SPU2Headers}
${pcsx2DebugToolsSources}
${pcsx2GuiSources}
${pcsx2GuiResources}

View File

@ -21,6 +21,7 @@
#include "IPU/IPUdma.h"
#include "Gif_Unit.h"
#include "IopCommon.h"
#include "SPU2/spu2.h"
using namespace R5900;

View File

@ -23,6 +23,7 @@
#include "ps2/eeHwTraceLog.inl"
#include "ps2/pgif.h"
#include "SPU2/spu2.h"
#include "R3000A.h"
using namespace R5900;

View File

@ -20,6 +20,7 @@
#include "PrecompiledHeader.h"
#include "IopCommon.h"
#include "SPU2/spu2.h"
#include <math.h>

View File

@ -15,6 +15,7 @@
#include "PrecompiledHeader.h"
#include "IopCommon.h"
#include "SPU2/spu2.h"
#include "Sif.h"

View File

@ -17,6 +17,7 @@
#include "PrecompiledHeader.h"
#include "IopCommon.h"
#include "ps2/pgif.h" // for PSX kernel TTY in iopMemWrite32
#include "SPU2/spu2.h"
uptr *psxMemWLUT = NULL;
const uptr *psxMemRLUT = NULL;

View File

@ -44,6 +44,7 @@ BIOS
#include "ps2/HwInternal.h"
#include "ps2/BiosTools.h"
#include "SPU2/spu2.h"
#include "Utilities/PageFaultSource.h"

612
pcsx2/SPU2/spu2.cpp Normal file
View File

@ -0,0 +1,612 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X 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.
*
* SPU2-X 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "spu2.h"
#include "Dma.h"
#include "Dialogs.h"
#include "svnrev.h"
#ifdef _MSC_VER
#define snprintf sprintf_s
#endif
// PCSX2 expects ASNI, not unicode, so this MUST always be char...
static char libraryName[256];
int SampleRate = 48000;
static bool IsOpened = false;
static bool IsInitialized = false;
static u32 pClocks = 0;
u32 *cyclePtr = NULL;
u32 lClocks = 0;
//static bool cpu_detected = false;
static bool CheckSSE()
{
return true;
#if 0
if( !cpu_detected )
{
cpudetectInit();
cpu_detected = true;
}
if( !x86caps.hasStreamingSIMDExtensions || !x86caps.hasStreamingSIMD2Extensions )
{
SysMessage( "Your CPU does not support SSE2 instructions.\nThe SPU2-X plugin requires SSE2 to run." );
return false;
}
return true;
#endif
}
void SPU2configure()
{
if (!CheckSSE())
return;
configure();
}
s32 SPU2test()
{
if (!CheckSSE())
return -1;
ReadSettings();
if (SndBuffer::Test() != 0) {
// TODO : Implement a proper dialog that allows the user to test different audio out drivers.
const wchar_t *wtf = mods[OutputModule]->GetIdent();
SysMessage(L"The '%s' driver test failed. Please configure\na different SoundOut module and try again.", wtf);
return -1;
}
return 0;
}
// --------------------------------------------------------------------------------------
// DMA 4/7 Callbacks from Core Emulator
// --------------------------------------------------------------------------------------
u16 *DMABaseAddr;
void (*_irqcallback)();
void (*dma4callback)();
void (*dma7callback)();
u32 CALLBACK SPU2ReadMemAddr(int core)
{
return Cores[core].MADR;
}
void CALLBACK SPU2WriteMemAddr(int core, u32 value)
{
Cores[core].MADR = value;
}
void CALLBACK SPU2setDMABaseAddr(uptr baseaddr)
{
DMABaseAddr = (u16 *)baseaddr;
}
void CALLBACK SPU2setSettingsDir(const char *dir)
{
CfgSetSettingsDir(dir);
}
void CALLBACK SPU2setLogDir(const char *dir)
{
CfgSetLogDir(dir);
}
void SPU2irqCallback(void (*SPU2callback)(), void (*DMA4callback)(), void (*DMA7callback)())
{
_irqcallback = SPU2callback;
dma4callback = DMA4callback;
dma7callback = DMA7callback;
}
void CALLBACK SPU2readDMA4Mem(u16 *pMem, u32 size) // size now in 16bit units
{
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
FileLog("[%10d] SPU2 readDMA4Mem size %x\n", Cycles, size << 1);
Cores[0].DoDMAread(pMem, size);
}
void CALLBACK SPU2writeDMA4Mem(u16 *pMem, u32 size) // size now in 16bit units
{
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
FileLog("[%10d] SPU2 writeDMA4Mem size %x at address %x\n", Cycles, size << 1, Cores[0].TSA);
#ifdef S2R_ENABLE
if (!replay_mode)
s2r_writedma4(Cycles, pMem, size);
#endif
Cores[0].DoDMAwrite(pMem, size);
}
void CALLBACK SPU2interruptDMA4()
{
FileLog("[%10d] SPU2 interruptDMA4\n", Cycles);
Cores[0].Regs.STATX |= 0x80;
//Cores[0].Regs.ATTR &= ~0x30;
}
void CALLBACK SPU2interruptDMA7()
{
FileLog("[%10d] SPU2 interruptDMA7\n", Cycles);
Cores[1].Regs.STATX |= 0x80;
//Cores[1].Regs.ATTR &= ~0x30;
}
void CALLBACK SPU2readDMA7Mem(u16 *pMem, u32 size)
{
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
FileLog("[%10d] SPU2 readDMA7Mem size %x\n", Cycles, size << 1);
Cores[1].DoDMAread(pMem, size);
}
void CALLBACK SPU2writeDMA7Mem(u16 *pMem, u32 size)
{
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
FileLog("[%10d] SPU2 writeDMA7Mem size %x at address %x\n", Cycles, size << 1, Cores[1].TSA);
#ifdef S2R_ENABLE
if (!replay_mode)
s2r_writedma7(Cycles, pMem, size);
#endif
Cores[1].DoDMAwrite(pMem, size);
}
s32 SPU2reset()
{
if (SndBuffer::Test() == 0 && SampleRate != 48000)
{
SampleRate = 48000;
SndBuffer::Cleanup();
try {
SndBuffer::Init();
}
catch (std::exception& ex) {
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
SPU2close();
return -1;
}
}
else
SampleRate = 48000;
memset(spu2regs, 0, 0x010000);
memset(_spu2mem, 0, 0x200000);
memset(_spu2mem + 0x2800, 7, 0x10); // from BIOS reversal. Locks the voices so they don't run free.
Cores[0].Init(0);
Cores[1].Init(1);
return 0;
}
s32 SPU2ps1reset()
{
printf("RESET PS1 \n");
if (SndBuffer::Test() == 0 && SampleRate != 44100)
{
SampleRate = 44100;
SndBuffer::Cleanup();
try {
SndBuffer::Init();
}
catch (std::exception& ex) {
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
SPU2close();
return -1;
}
}
else
SampleRate = 44100;
/* memset(spu2regs, 0, 0x010000);
memset(_spu2mem, 0, 0x200000);
memset(_spu2mem + 0x2800, 7, 0x10); // from BIOS reversal. Locks the voices so they don't run free.
Cores[0].Init(0);
Cores[1].Init(1);*/
return 0;
}
s32 SPU2init()
{
assert(regtable[0x400] == NULL);
if (IsInitialized) {
printf(" * SPU2-X: Already initialized - Ignoring SPU2init signal.");
return 0;
}
IsInitialized = true;
ReadSettings();
#ifdef SPU2_LOG
if (AccessLog()) {
spu2Log = OpenLog(AccessLogFileName);
setvbuf(spu2Log, NULL, _IONBF, 0);
FileLog("SPU2init\n");
}
#endif
srand((unsigned)time(NULL));
spu2regs = (s16 *)malloc(0x010000);
_spu2mem = (s16 *)malloc(0x200000);
// adpcm decoder cache:
// the cache data size is determined by taking the number of adpcm blocks
// (2MB / 16) and multiplying it by the decoded block size (28 samples).
// Thus: pcm_cache_data = 7,340,032 bytes (ouch!)
// Expanded: 16 bytes expands to 56 bytes [3.5:1 ratio]
// Resulting in 2MB * 3.5.
pcm_cache_data = (PcmCacheEntry *)calloc(pcm_BlockCount, sizeof(PcmCacheEntry));
if ((spu2regs == NULL) || (_spu2mem == NULL) || (pcm_cache_data == NULL)) {
SysMessage("SPU2-X: Error allocating Memory\n");
return -1;
}
// Patch up a copy of regtable that directly maps "NULLs" to SPU2 memory.
memcpy(regtable, regtable_original, sizeof(regtable));
for (uint mem = 0; mem < 0x800; mem++) {
u16 *ptr = regtable[mem >> 1];
if (!ptr) {
regtable[mem >> 1] = &(spu2Ru16(mem));
}
}
SPU2reset();
DMALogOpen();
InitADSR();
#ifdef S2R_ENABLE
if (!replay_mode)
s2r_open(Cycles, "replay_dump.s2r");
#endif
return 0;
}
#ifdef _MSC_VER
// Bit ugly to have this here instead of in RealttimeDebugger.cpp, but meh :p
extern bool debugDialogOpen;
extern HWND hDebugDialog;
static INT_PTR CALLBACK DebugProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int wmId;
switch (uMsg) {
case WM_PAINT:
return FALSE;
case WM_INITDIALOG: {
debugDialogOpen = true;
} break;
case WM_COMMAND:
wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId) {
case IDOK:
case IDCANCEL:
debugDialogOpen = false;
EndDialog(hWnd, 0);
break;
default:
return FALSE;
}
break;
default:
return FALSE;
}
return TRUE;
}
#endif
uptr gsWindowHandle = 0;
s32 SPU2open(void *pDsp)
{
if (IsOpened)
return 0;
FileLog("[%10d] SPU2 Open\n", Cycles);
if (pDsp != NULL)
gsWindowHandle = *(uptr *)pDsp;
else
gsWindowHandle = 0;
#ifdef _MSC_VER
#ifdef PCSX2_DEVBUILD // Define may not be needed but not tested yet. Better make sure.
if (IsDevBuild && VisualDebug()) {
if (debugDialogOpen == 0) {
hDebugDialog = CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_DEBUG), 0, DebugProc, 0);
ShowWindow(hDebugDialog, SW_SHOWNORMAL);
debugDialogOpen = 1;
}
} else if (debugDialogOpen) {
DestroyWindow(hDebugDialog);
debugDialogOpen = 0;
}
#endif
#endif
IsOpened = true;
lClocks = (cyclePtr != NULL) ? *cyclePtr : 0;
try {
SndBuffer::Init();
#ifndef __POSIX__
DspLoadLibrary(dspPlugin, dspPluginModule);
#endif
WaveDump::Open();
} catch (std::exception &ex) {
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
SPU2close();
return -1;
}
return 0;
}
void SPU2close()
{
if (!IsOpened)
return;
IsOpened = false;
FileLog("[%10d] SPU2 Close\n", Cycles);
#ifndef __POSIX__
DspCloseLibrary();
#endif
SndBuffer::Cleanup();
}
void SPU2shutdown()
{
if (!IsInitialized)
return;
IsInitialized = false;
ConLog("* SPU2-X: Shutting down.\n");
SPU2close();
#ifdef S2R_ENABLE
if (!replay_mode)
s2r_close();
#endif
DoFullDump();
#ifdef STREAM_DUMP
fclose(il0);
fclose(il1);
#endif
#ifdef EFFECTS_DUMP
fclose(el0);
fclose(el1);
#endif
WaveDump::Close();
DMALogClose();
safe_free(spu2regs);
safe_free(_spu2mem);
safe_free(pcm_cache_data);
#ifdef SPU2_LOG
if (!AccessLog())
return;
FileLog("[%10d] SPU2shutdown\n", Cycles);
if (spu2Log)
fclose(spu2Log);
#endif
}
void SPU2setClockPtr(u32 *ptr)
{
cyclePtr = ptr;
}
#ifdef DEBUG_KEYS
static u32 lastTicks;
static bool lState[6];
#endif
void SPU2async(u32 cycles)
{
DspUpdate();
if (cyclePtr != NULL) {
TimeUpdate(*cyclePtr);
} else {
pClocks += cycles;
TimeUpdate(pClocks);
}
#ifdef DEBUG_KEYS
u32 curTicks = GetTickCount();
if ((curTicks - lastTicks) >= 50) {
int oldI = Interpolation;
bool cState[6];
for (int i = 0; i < 6; i++) {
cState[i] = !!(GetAsyncKeyState(VK_NUMPAD0 + i) & 0x8000);
if ((cState[i] && !lState[i]) && i != 5)
Interpolation = i;
if ((cState[i] && !lState[i]) && i == 5) {
postprocess_filter_enabled = !postprocess_filter_enabled;
printf("Post process filters %s \n", postprocess_filter_enabled ? "enabled" : "disabled");
}
lState[i] = cState[i];
}
if (Interpolation != oldI) {
printf("Interpolation set to %d", Interpolation);
switch (Interpolation) {
case 0:
printf(" - Nearest.\n");
break;
case 1:
printf(" - Linear.\n");
break;
case 2:
printf(" - Cubic.\n");
break;
case 3:
printf(" - Hermite.\n");
break;
case 4:
printf(" - Catmull-Rom.\n");
break;
default:
printf(" (unknown).\n");
break;
}
}
lastTicks = curTicks;
}
#endif
}
u16 SPU2read(u32 rmem)
{
// if(!replay_mode)
// s2r_readreg(Cycles,rmem);
u16 ret = 0xDEAD;
u32 core = 0, mem = rmem & 0xFFFF, omem = mem;
if (mem & 0x400) {
omem ^= 0x400;
core = 1;
}
if (omem == 0x1f9001AC) {
ret = Cores[core].DmaRead();
} else {
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
if (rmem >> 16 == 0x1f80) {
ret = Cores[0].ReadRegPS1(rmem);
} else if (mem >= 0x800) {
ret = spu2Ru16(mem);
ConLog("* SPU2-X: Read from reg>=0x800: %x value %x\n", mem, ret);
} else {
ret = *(regtable[(mem >> 1)]);
//FileLog("[%10d] SPU2 read mem %x (core %d, register %x): %x\n",Cycles, mem, core, (omem & 0x7ff), ret);
SPU2writeLog("read", rmem, ret);
}
}
return ret;
}
void SPU2write(u32 rmem, u16 value)
{
#ifdef S2R_ENABLE
if (!replay_mode)
s2r_writereg(Cycles, rmem, value);
#endif
// Note: Reverb/Effects are very sensitive to having precise update timings.
// If the SPU2 isn't in in sync with the IOP, samples can end up playing at rather
// incorrect pitches and loop lengths.
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
if (rmem >> 16 == 0x1f80)
Cores[0].WriteRegPS1(rmem, value);
else {
SPU2writeLog("write", rmem, value);
SPU2_FastWrite(rmem, value);
}
}
// if start is 1, starts recording spu2 data, else stops
// returns a non zero value if successful
// for now, pData is not used
int SPU2setupRecording(int start, std::wstring* filename)
{
if (start == 0)
RecordStop();
else if (start == 1)
RecordStart(filename);
return 0;
}
s32 SPU2freeze(int mode, freezeData *data)
{
pxAssume(data != NULL);
if (!data) {
printf("SPU2-X savestate null pointer!\n");
return -1;
}
if (mode == FREEZE_SIZE) {
data->size = Savestate::SizeIt();
return 0;
}
pxAssume(mode == FREEZE_LOAD || mode == FREEZE_SAVE);
if (data->data == NULL) {
printf("SPU2-X savestate null pointer!\n");
return -1;
}
Savestate::DataBlock &spud = (Savestate::DataBlock &)*(data->data);
switch (mode) {
case FREEZE_LOAD:
return Savestate::ThawIt(spud);
case FREEZE_SAVE:
return Savestate::FreezeIt(spud);
jNO_DEFAULT;
}
// technically unreachable, but kills a warning:
return 0;
}

69
pcsx2/SPU2/spu2.h Normal file
View File

@ -0,0 +1,69 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X 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.
*
* SPU2-X 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Pcsx2Defs.h"
s32 SPU2init();
s32 SPU2reset();
s32 SPU2ps1reset();
s32 SPU2open(void *pDsp);
void SPU2close();
void SPU2shutdown();
void SPU2write(u32 mem, u16 value);
u16 SPU2read(u32 mem);
// extended funcs
// if start is 1, starts recording spu2 data, else stops
// returns a non zero value if successful
// for now, pData is not used
int SPU2setupRecording(int start, std::wstring* filename);
void SPU2setClockPtr(u32 *ptr);
void SPU2async(u32 cycles);
s32 SPU2freeze(int mode, freezeData *data);
void SPU2configure();
void SPU2about();
s32 SPU2test();
#include "Spu2replay.h"
extern u8 callirq;
extern void (*_irqcallback)();
extern void (*dma4callback)();
extern void (*dma7callback)();
extern s16 *input_data;
extern u32 input_data_ptr;
extern double srate_pv;
extern int recording;
extern u32 lClocks;
extern u32 *cyclePtr;
extern void SPU2writeLog(const char *action, u32 rmem, u16 value);
extern void TimeUpdate(u32 cClocks);
extern void SPU2_FastWrite(u32 rmem, u16 value);
extern void LowPassFilterInit();
//#define PCM24_S1_INTERLEAVE

321
pcsx2/SPU2/spu2replay.cpp Normal file
View File

@ -0,0 +1,321 @@
//GiGaHeRz's SPU2 Driver
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
//
//This library 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 Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library 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
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
//
#include "Global.h"
#include "PS2E-spu2.h"
#ifdef _MSC_VER
#include "Windows.h"
#endif
FILE *s2rfile;
void s2r_write16(s16 data)
{
fwrite(&data, 2, 1, s2rfile);
}
void s2r_write32(u32 data)
{
fwrite(&data, 4, 1, s2rfile);
}
static void EMITC(u32 i, u32 a)
{
s2r_write32(((i & 0x7u) << 29u) | (a & 0x1FFFFFFFu));
}
int s2r_open(u32 ticks, char *filename)
{
s2rfile = fopen(filename, "wb");
if (s2rfile)
s2r_write32(ticks);
return s2rfile ? 0 : -1;
}
void s2r_readreg(u32 ticks, u32 addr)
{
if (!s2rfile)
return;
s2r_write32(ticks);
EMITC(0, addr);
}
void s2r_writereg(u32 ticks, u32 addr, s16 value)
{
if (!s2rfile)
return;
s2r_write32(ticks);
EMITC(1, addr);
s2r_write16(value);
}
void s2r_writedma4(u32 ticks, u16 *data, u32 len)
{
u32 i;
if (!s2rfile)
return;
s2r_write32(ticks);
EMITC(2, len);
for (i = 0; i < len; i++, data++)
s2r_write16(*data);
}
void s2r_writedma7(u32 ticks, u16 *data, u32 len)
{
u32 i;
if (!s2rfile)
return;
s2r_write32(ticks);
EMITC(3, len);
for (i = 0; i < len; i++, data++)
s2r_write16(*data);
}
void s2r_close()
{
if (!s2rfile)
return;
fclose(s2rfile);
}
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// replay code
bool replay_mode = false;
u16 dmabuffer[0xFFFFF];
const u32 IOP_CLK = 768 * 48000;
const u32 IOPCiclesPerMS = 768 * 48;
u32 CurrentIOPCycle = 0;
u64 HighResFreq;
u64 HighResPrev;
double HighResScale;
bool Running = false;
#ifdef _MSC_VER
int conprintf(const char *fmt, ...)
{
#ifdef _WIN32
char s[1024];
va_list list;
va_start(list, fmt);
vsprintf(s, fmt, list);
va_end(list);
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (handle == INVALID_HANDLE_VALUE)
return 0;
DWORD written = 0;
WriteConsoleA(handle, s, strlen(s), &written, 0);
FlushFileBuffers(handle);
return written;
#else
va_list list;
va_start(list, fmt);
int ret = vsprintf(stderr, fmt, list);
va_end(list);
return ret;
#endif
}
void dummy1()
{
}
void dummy4()
{
SPU2interruptDMA4();
}
void dummy7()
{
SPU2interruptDMA7();
}
u64 HighResFrequency()
{
u64 freq;
#ifdef _WIN32
QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
#else
// TODO
#endif
return freq;
}
u64 HighResCounter()
{
u64 time;
#ifdef _WIN32
QueryPerformanceCounter((LARGE_INTEGER *)&time);
#else
// TODO
#endif
return time;
}
void InitWaitSync() // not extremely accurate but enough.
{
HighResFreq = HighResFrequency();
HighResPrev = HighResCounter();
HighResScale = (double)HighResFreq / (double)IOP_CLK;
}
u32 WaitSync(u32 TargetCycle)
{
u32 WaitCycles = (TargetCycle - CurrentIOPCycle);
u32 WaitTime = WaitCycles / IOPCiclesPerMS;
if (WaitTime > 10)
WaitTime = 10;
if (WaitTime == 0)
WaitTime = 1;
SleepEx(WaitTime, TRUE);
// Refresh current time after sleeping
u64 Current = HighResCounter();
u32 delta = (u32)floor((Current - HighResPrev) / HighResScale + 0.5); // We lose some precision here, cycles might drift away over long periods of time ;P
// Calculate time delta
CurrentIOPCycle += delta;
HighResPrev += (u64)floor(delta * HighResScale + 0.5); // Trying to compensate drifting mentioned above, not necessarily useful.
return delta;
}
#ifdef _WIN32
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType)
{
Running = false;
return TRUE;
}
#endif
#include "Windows/Dialogs.h"
EXPORT_C_(void)
s2r_replay(HWND hwnd, HINSTANCE hinst, LPSTR filename, int nCmdShow)
{
int events = 0;
Running = true;
#ifdef _WIN32
AllocConsole();
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
conprintf("Playing %s file on %x...", filename, hwnd);
#endif
// load file
FILE *file = fopen(filename, "rb");
if (!file) {
conprintf("Could not open the replay file.");
return;
}
// if successful, init the plugin
#define TryRead(dest, size, count, file) \
if (fread(dest, size, count, file) < count) { \
conprintf("Error reading from file."); \
goto Finish; /* Need to exit the while() loop and maybe also the switch */ \
}
TryRead(&CurrentIOPCycle, 4, 1, file);
replay_mode = true;
InitWaitSync(); // Initialize the WaitSync stuff
SPU2init();
SPU2irqCallback(dummy1, dummy4, dummy7);
SPU2setClockPtr(&CurrentIOPCycle);
SPU2open(&hwnd);
CurrentIOPCycle = 0;
SPU2async(0);
while (!feof(file) && Running) {
u32 ccycle = 0;
u32 evid = 0;
u32 sval = 0;
u32 tval = 0;
TryRead(&ccycle, 4, 1, file);
TryRead(&sval, 4, 1, file);
evid = sval >> 29;
sval &= 0x1FFFFFFF;
u32 TargetCycle = ccycle * 768;
while (TargetCycle > CurrentIOPCycle) {
u32 delta = WaitSync(TargetCycle);
SPU2async(delta);
}
switch (evid) {
case 0:
SPU2read(sval);
break;
case 1:
TryRead(&tval, 2, 1, file);
SPU2write(sval, tval);
break;
case 2:
TryRead(dmabuffer, sval, 2, file);
SPU2writeDMA4Mem(dmabuffer, sval);
break;
case 3:
TryRead(dmabuffer, sval, 2, file);
SPU2writeDMA7Mem(dmabuffer, sval);
break;
default:
// not implemented
return;
break;
}
events++;
}
Finish:
//shutdown
SPU2close();
SPU2shutdown();
fclose(file);
conprintf("Finished playing %s file (%d cycles, %d events).", filename, CurrentIOPCycle, events);
#ifdef _WIN32
FreeConsole();
#endif
replay_mode = false;
}
#endif

31
pcsx2/SPU2/spu2replay.h Normal file
View File

@ -0,0 +1,31 @@
//GiGaHeRz's SPU2 Driver
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
//
//This library 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 Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library 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
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
//
#pragma once
//#define S2R_ENABLE
// s2r dumping
int s2r_open(u32 ticks, char *filename);
void s2r_readreg(u32 ticks, u32 addr);
void s2r_writereg(u32 ticks, u32 addr, s16 value);
void s2r_writedma4(u32 ticks, u16 *data, u32 len);
void s2r_writedma7(u32 ticks, u16 *data, u32 len);
void s2r_close();
extern bool replay_mode;