359 lines
8.0 KiB
C++
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);
|
|
}
|
|
}
|