/******************************************************************************/ /* 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 static MDFN_FASTCALL T SoundCPU_BusRead(uint32 A); static MDFN_FASTCALL uint16 SoundCPU_BusReadInstr(uint32 A); template 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; SoundCPU.BusRead16 = SoundCPU_BusRead; SoundCPU.BusWrite8 = SoundCPU_BusWrite; SoundCPU.BusWrite16 = SoundCPU_BusWrite; 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; SoundCPU.DBG_Verbose = SS_DBG_Wrap; #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(SCSP.GetRAMPtr(), A & 0x7FFFF); } void SOUND_PokeRAM(uint32 A, uint8 V) { ne16_wbo_be(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(A, ret); return ret; } void SOUND_Write8(uint32 A, uint8 V) { SCSP.RW(A, V); } void SOUND_Write16(uint32 A, uint16 V) { SCSP.RW(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(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 static MDFN_FASTCALL T SoundCPU_BusRead(uint32 A) { T ret; SoundCPU.timestamp += 4; if (MDFN_UNLIKELY(SoundCPU.timestamp >= next_scsp_time)) RunSCSP(); SCSP.RW(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(A & 0x1FFFFF, ret); SoundCPU.timestamp += 2; return ret; } template static MDFN_FASTCALL void SoundCPU_BusWrite(uint32 A, T V) { if (MDFN_UNLIKELY(SoundCPU.timestamp >= next_scsp_time)) RunSCSP(); SoundCPU.timestamp += 2; SCSP.RW(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(A & 0x1FFFFF, tmp); tmp = cb(&SoundCPU, tmp); SoundCPU.timestamp += 6; SCSP.RW(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); } }