BizHawk/waterbox/ss/sound.cpp

359 lines
8.0 KiB
C++

/******************************************************************************/
/* Mednafen Sega Saturn Emulation Module */
/******************************************************************************/
/* sound.cpp - Sound Emulation
** Copyright (C) 2015-2016 Mednafen Team
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program 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 General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation, Inc.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// TODO: Bus between SCU and SCSP looks to be 8-bit, maybe implement that, but
// first test to see how the bus access cycle(s) work with respect to reading from
// registers whose values may change between the individual byte reads.
// (May not be worth emulating if it could possibly trigger problems in games)
#include "ss.h"
#include "sound.h"
#include "scu.h"
#include "cdb.h"
#include "m68k/m68k.h"
namespace MDFN_IEN_SS
{
#include "scsp.h"
static SS_SCSP SCSP;
static M68K SoundCPU(true);
static int64 run_until_time; // 32.32
static int32 next_scsp_time;
static uint32 clock_ratio;
static sscpu_timestamp_t lastts;
static int16 IBuffer[1024][2];
static uint32 IBufferCount;
static int last_rate;
static uint32 last_quality;
static INLINE void SCSP_SoundIntChanged(unsigned level)
{
SoundCPU.SetIPL(level);
}
static INLINE void SCSP_MainIntChanged(bool state)
{
#ifndef MDFN_SSFPLAY_COMPILE
SCU_SetInt(SCU_INT_SCSP, state);
#endif
}
#include "scsp.inc"
//
//
template <typename T>
static MDFN_FASTCALL T SoundCPU_BusRead(uint32 A);
static MDFN_FASTCALL uint16 SoundCPU_BusReadInstr(uint32 A);
template <typename T>
static MDFN_FASTCALL void SoundCPU_BusWrite(uint32 A, T V);
static MDFN_FASTCALL void SoundCPU_BusRMW(uint32 A, uint8(MDFN_FASTCALL *cb)(M68K *, uint8));
static MDFN_FASTCALL unsigned SoundCPU_BusIntAck(uint8 level);
static MDFN_FASTCALL void SoundCPU_BusRESET(bool state);
//
//
void SOUND_Init(void)
{
memset(IBuffer, 0, sizeof(IBuffer));
IBufferCount = 0;
last_rate = -1;
last_quality = ~0U;
run_until_time = 0;
next_scsp_time = 0;
lastts = 0;
SoundCPU.BusRead8 = SoundCPU_BusRead<uint8>;
SoundCPU.BusRead16 = SoundCPU_BusRead<uint16>;
SoundCPU.BusWrite8 = SoundCPU_BusWrite<uint8>;
SoundCPU.BusWrite16 = SoundCPU_BusWrite<uint16>;
SoundCPU.BusReadInstr = SoundCPU_BusReadInstr;
SoundCPU.BusRMW = SoundCPU_BusRMW;
SoundCPU.BusIntAck = SoundCPU_BusIntAck;
SoundCPU.BusRESET = SoundCPU_BusRESET;
#ifndef MDFN_SSFPLAY_COMPILE
SoundCPU.DBG_Warning = SS_DBG_Wrap<SS_DBG_WARNING | SS_DBG_M68K>;
SoundCPU.DBG_Verbose = SS_DBG_Wrap<SS_DBG_M68K>;
#endif
SS_SetPhysMemMap(0x05A00000, 0x05A7FFFF, SCSP.GetRAMPtr(), 0x80000, true);
// TODO: MEM4B: SS_SetPhysMemMap(0x05A00000, 0x05AFFFFF, SCSP.GetRAMPtr(), 0x40000, true);
AddMemoryDomain("Sound Ram", SCSP.GetRAMPtr(), 0x100000, MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE2);
}
uint8 SOUND_PeekRAM(uint32 A)
{
return ne16_rbo_be<uint8>(SCSP.GetRAMPtr(), A & 0x7FFFF);
}
void SOUND_PokeRAM(uint32 A, uint8 V)
{
ne16_wbo_be<uint8>(SCSP.GetRAMPtr(), A & 0x7FFFF, V);
}
void SOUND_ResetTS(void)
{
next_scsp_time -= SoundCPU.timestamp;
run_until_time -= (int64)SoundCPU.timestamp << 32;
SoundCPU.timestamp = 0;
lastts = 0;
}
void SOUND_Reset(bool powering_up)
{
SCSP.Reset(powering_up);
SoundCPU.Reset(powering_up);
}
void SOUND_Reset68K(void)
{
SoundCPU.Reset(false);
}
void SOUND_Set68KActive(bool active)
{
SoundCPU.SetExtHalted(!active);
}
uint16 SOUND_Read16(uint32 A)
{
uint16 ret;
SCSP.RW<uint16, false>(A, ret);
return ret;
}
void SOUND_Write8(uint32 A, uint8 V)
{
SCSP.RW<uint8, true>(A, V);
}
void SOUND_Write16(uint32 A, uint16 V)
{
SCSP.RW<uint16, true>(A, V);
}
static NO_INLINE void RunSCSP(void)
{
CDB_GetCDDA(SCSP.GetEXTSPtr());
//
//
int16 *const bp = IBuffer[IBufferCount];
SCSP.RunSample(bp);
//bp[0] = rand();
//bp[1] = rand();
bp[0] = (bp[0] * 27 + 16) >> 5;
bp[1] = (bp[1] * 27 + 16) >> 5;
IBufferCount = (IBufferCount + 1) & 1023;
next_scsp_time += 256;
}
// Ratio between SH-2 clock and 68K clock (sound clock / 2)
void SOUND_SetClockRatio(uint32 ratio)
{
clock_ratio = ratio;
}
sscpu_timestamp_t SOUND_Update(sscpu_timestamp_t timestamp)
{
run_until_time += ((uint64)(timestamp - lastts) * clock_ratio);
lastts = timestamp;
//
//
if (MDFN_LIKELY(SoundCPU.timestamp < (run_until_time >> 32)))
{
do
{
int32 next_time = std::min<int32>(next_scsp_time, run_until_time >> 32);
SoundCPU.Run(next_time);
if (SoundCPU.timestamp >= next_scsp_time)
RunSCSP();
} while (MDFN_LIKELY(SoundCPU.timestamp < (run_until_time >> 32)));
}
else
{
while (next_scsp_time < (run_until_time >> 32))
RunSCSP();
}
return timestamp + 128; // FIXME
}
void SOUND_StartFrame(double rate, uint32 quality)
{
if ((int)rate != last_rate || quality != last_quality)
{
int err = 0;
last_rate = (int)rate;
last_quality = quality;
}
}
int32 SOUND_FlushOutput(int16 *SoundBuf, const int32 SoundBufMaxSize, const bool reverse)
{
if (SoundBuf && reverse)
{
for (unsigned lr = 0; lr < 2; lr++)
{
int16 *p0 = &IBuffer[0][lr];
int16 *p1 = &IBuffer[IBufferCount - 1][lr];
unsigned count = IBufferCount >> 1;
while (MDFN_LIKELY(count--))
{
std::swap(*p0, *p1);
p0 += 2;
p1 -= 2;
}
}
}
if (last_rate == 44100)
{
int32 ret = IBufferCount;
memcpy(SoundBuf, IBuffer, IBufferCount * 2 * sizeof(int16));
IBufferCount = 0;
return (ret);
}
else
{
IBufferCount = 0;
return 0;
}
}
//
//
// TODO: test masks.
//
template <typename T>
static MDFN_FASTCALL T SoundCPU_BusRead(uint32 A)
{
T ret;
SoundCPU.timestamp += 4;
if (MDFN_UNLIKELY(SoundCPU.timestamp >= next_scsp_time))
RunSCSP();
SCSP.RW<T, false>(A & 0x1FFFFF, ret);
SoundCPU.timestamp += 2;
return ret;
}
static MDFN_FASTCALL uint16 SoundCPU_BusReadInstr(uint32 A)
{
uint16 ret;
SoundCPU.timestamp += 4;
//if(MDFN_UNLIKELY(SoundCPU.timestamp >= next_scsp_time))
// RunSCSP();
SCSP.RW<uint16, false>(A & 0x1FFFFF, ret);
SoundCPU.timestamp += 2;
return ret;
}
template <typename T>
static MDFN_FASTCALL void SoundCPU_BusWrite(uint32 A, T V)
{
if (MDFN_UNLIKELY(SoundCPU.timestamp >= next_scsp_time))
RunSCSP();
SoundCPU.timestamp += 2;
SCSP.RW<T, true>(A & 0x1FFFFF, V);
SoundCPU.timestamp += 2;
}
static MDFN_FASTCALL void SoundCPU_BusRMW(uint32 A, uint8(MDFN_FASTCALL *cb)(M68K *, uint8))
{
uint8 tmp;
SoundCPU.timestamp += 4;
if (MDFN_UNLIKELY(SoundCPU.timestamp >= next_scsp_time))
RunSCSP();
SCSP.RW<uint8, false>(A & 0x1FFFFF, tmp);
tmp = cb(&SoundCPU, tmp);
SoundCPU.timestamp += 6;
SCSP.RW<uint8, true>(A & 0x1FFFFF, tmp);
SoundCPU.timestamp += 2;
}
static MDFN_FASTCALL unsigned SoundCPU_BusIntAck(uint8 level)
{
return M68K::BUS_INT_ACK_AUTO;
}
static MDFN_FASTCALL void SoundCPU_BusRESET(bool state)
{
//SS_DBG(SS_DBG_WARNING, "[M68K] RESET: %d @ time %d\n", state, SoundCPU.timestamp);
if (state)
{
SoundCPU.Reset(false);
}
}
uint32 SOUND_GetSCSPRegister(const unsigned id, char *const special, const uint32 special_len)
{
return SCSP.GetRegister(id, special, special_len);
}
void SOUND_SetSCSPRegister(const unsigned id, const uint32 value)
{
SCSP.SetRegister(id, value);
}
}