2019-02-16 11:23:01 -05:00
|
|
|
#include "stdafx.h"
|
|
|
|
#include "Spc.h"
|
|
|
|
#include "Console.h"
|
|
|
|
#include "MemoryManager.h"
|
|
|
|
#include "SoundMixer.h"
|
2019-07-12 08:02:07 -04:00
|
|
|
#include "EmuSettings.h"
|
2019-10-19 15:23:17 -04:00
|
|
|
#include "SpcFileData.h"
|
2020-02-10 00:12:00 -05:00
|
|
|
#ifndef DUMMYSPC
|
|
|
|
#include "SPC_DSP.h"
|
|
|
|
#else
|
|
|
|
#undef Spc
|
2019-03-22 21:29:51 -04:00
|
|
|
#include "SPC_DSP.h"
|
2020-02-10 00:12:00 -05:00
|
|
|
#define Spc DummySpc
|
|
|
|
#endif
|
2019-03-12 09:15:57 -04:00
|
|
|
#include "../Utilities/Serializer.h"
|
2019-02-16 11:23:01 -05:00
|
|
|
|
2019-07-12 23:34:19 -04:00
|
|
|
Spc::Spc(Console* console)
|
2019-02-16 11:23:01 -05:00
|
|
|
{
|
|
|
|
_console = console;
|
2019-07-12 23:34:19 -04:00
|
|
|
_memoryManager = console->GetMemoryManager().get();
|
2019-02-21 16:49:19 -05:00
|
|
|
_soundBuffer = new int16_t[Spc::SampleBufferSize];
|
2019-02-16 11:23:01 -05:00
|
|
|
|
2019-04-07 17:57:30 -04:00
|
|
|
_ram = new uint8_t[Spc::SpcRamSize];
|
2019-07-12 08:02:07 -04:00
|
|
|
_console->GetSettings()->InitializeRam(_ram, Spc::SpcRamSize);
|
2019-04-07 17:57:30 -04:00
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
_dsp.reset(new SPC_DSP());
|
2021-03-10 11:13:28 -05:00
|
|
|
#ifndef DUMMYSPC
|
2020-04-28 17:56:57 -04:00
|
|
|
_dsp->init(this, _console->GetSettings().get(), _ram);
|
2021-03-10 11:13:28 -05:00
|
|
|
#endif
|
2019-03-22 21:29:51 -04:00
|
|
|
_dsp->reset();
|
|
|
|
_dsp->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
|
2019-02-16 11:23:01 -05:00
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
_state = {};
|
|
|
|
_state.WriteEnabled = true;
|
|
|
|
_state.TimersEnabled = true;
|
|
|
|
_state.RomEnabled = true;
|
|
|
|
_state.SP = 0xFF;
|
|
|
|
_state.PC = ReadWord(Spc::ResetVector);
|
2019-07-08 22:06:12 -04:00
|
|
|
_state.StopState = CpuStopState::Running;
|
2019-03-29 22:13:22 -04:00
|
|
|
|
2019-07-01 18:34:39 -04:00
|
|
|
_opCode = 0;
|
|
|
|
_opStep = SpcOpStep::ReadOpCode;
|
|
|
|
_opSubStep = 0;
|
|
|
|
_tmp1 = 0;
|
|
|
|
_tmp2 = 0;
|
|
|
|
_tmp3 = 0;
|
|
|
|
_operandA = 0;
|
|
|
|
_operandB = 0;
|
2019-07-19 19:39:38 -04:00
|
|
|
_enabled = true;
|
2019-07-01 18:34:39 -04:00
|
|
|
|
2020-02-29 11:34:23 -05:00
|
|
|
UpdateClockRatio();
|
2019-02-16 11:23:01 -05:00
|
|
|
}
|
|
|
|
|
2019-04-07 17:57:30 -04:00
|
|
|
#ifndef DUMMYSPC
|
2019-02-16 11:23:01 -05:00
|
|
|
Spc::~Spc()
|
|
|
|
{
|
2019-03-22 21:29:51 -04:00
|
|
|
delete[] _soundBuffer;
|
2019-04-07 17:57:30 -04:00
|
|
|
delete[] _ram;
|
2019-02-16 11:23:01 -05:00
|
|
|
}
|
2019-04-07 17:57:30 -04:00
|
|
|
#endif
|
2019-02-16 11:23:01 -05:00
|
|
|
|
2019-03-16 12:20:18 -04:00
|
|
|
void Spc::Reset()
|
|
|
|
{
|
2019-07-08 22:06:12 -04:00
|
|
|
_state.StopState = CpuStopState::Running;
|
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
_state.Timer0.Reset();
|
|
|
|
_state.Timer1.Reset();
|
|
|
|
_state.Timer2.Reset();
|
2019-11-02 21:00:03 -04:00
|
|
|
|
|
|
|
//Resetting appears to reset the values the main CPU can read (not doing this causes a freeze in Kaite Tsukette Asoberu Dezaemon)
|
|
|
|
_state.OutputReg[0] = 0;
|
|
|
|
_state.OutputReg[1] = 0;
|
|
|
|
_state.OutputReg[2] = 0;
|
|
|
|
_state.OutputReg[3] = 0;
|
|
|
|
|
2019-03-22 23:53:20 -04:00
|
|
|
_state.RomEnabled = true;
|
|
|
|
_state.Cycle = 0;
|
2019-03-22 21:29:51 -04:00
|
|
|
_state.PC = ReadWord(Spc::ResetVector);
|
2021-03-10 11:13:28 -05:00
|
|
|
|
2019-07-01 18:34:39 -04:00
|
|
|
_opCode = 0;
|
|
|
|
_opStep = SpcOpStep::ReadOpCode;
|
|
|
|
_opSubStep = 0;
|
|
|
|
_tmp1 = 0;
|
|
|
|
_tmp2 = 0;
|
|
|
|
_tmp3 = 0;
|
|
|
|
_operandA = 0;
|
|
|
|
_operandB = 0;
|
2019-03-22 21:29:51 -04:00
|
|
|
|
|
|
|
_dsp->soft_reset();
|
|
|
|
_dsp->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
|
|
|
|
}
|
|
|
|
|
2019-07-19 19:39:38 -04:00
|
|
|
void Spc::SetSpcState(bool enabled)
|
|
|
|
{
|
|
|
|
//Used by overclocking logic to disable SPC during the extra scanlines added to the PPU
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_enabled != enabled) {
|
|
|
|
if(enabled) {
|
2019-07-19 19:39:38 -04:00
|
|
|
//When re-enabling, adjust the cycle counter to prevent running extra cycles
|
2020-02-29 11:34:23 -05:00
|
|
|
UpdateClockRatio();
|
2021-03-10 11:13:28 -05:00
|
|
|
} else {
|
2019-07-19 19:39:38 -04:00
|
|
|
//Catch up SPC before disabling it
|
|
|
|
Run();
|
|
|
|
}
|
|
|
|
_enabled = enabled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-29 11:34:23 -05:00
|
|
|
void Spc::UpdateClockRatio()
|
|
|
|
{
|
|
|
|
_clockRatio = (double)(Spc::SpcSampleRate * 64) / _console->GetMasterClockRate();
|
|
|
|
|
|
|
|
//If the target cycle is off by more than 10 cycles, reset the counter to match what was expected
|
|
|
|
//This can happen due to overclocking (which disables the SPC for some scanlines) or if the SPC's
|
|
|
|
//internal sample rate is changed between versions (e.g 32000hz -> 32040hz)
|
|
|
|
uint64_t targetCycle = (uint64_t)(_memoryManager->GetMasterClock() * _clockRatio);
|
2021-03-10 11:13:28 -05:00
|
|
|
if(std::abs((int64_t)targetCycle - (int64_t)_state.Cycle) > 10) {
|
2020-02-29 11:34:23 -05:00
|
|
|
_state.Cycle = targetCycle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
void Spc::Idle()
|
|
|
|
{
|
|
|
|
IncCycleCount(-1);
|
2019-03-16 12:20:18 -04:00
|
|
|
}
|
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
void Spc::DummyRead()
|
2019-02-16 11:23:01 -05:00
|
|
|
{
|
2019-04-07 14:38:22 -04:00
|
|
|
Read(_state.PC, MemoryOperationType::DummyRead);
|
2019-02-21 16:49:19 -05:00
|
|
|
}
|
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
void Spc::DummyRead(uint16_t addr)
|
2019-02-21 16:49:19 -05:00
|
|
|
{
|
2019-04-07 14:38:22 -04:00
|
|
|
Read(addr, MemoryOperationType::DummyRead);
|
2019-02-16 11:23:01 -05:00
|
|
|
}
|
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
void Spc::IncCycleCount(int32_t addr)
|
2019-02-16 11:23:01 -05:00
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
static constexpr uint8_t cpuWait[4] = { 2, 4, 10, 20 };
|
|
|
|
static constexpr uint8_t timerMultiplier[4] = { 2, 4, 8, 16 };
|
2019-03-22 21:29:51 -04:00
|
|
|
|
|
|
|
uint8_t speedSelect;
|
2021-03-10 11:13:28 -05:00
|
|
|
if(addr < 0 || ((addr & 0xFFF0) == 0x00F0) || (addr >= 0xFFC0 && _state.RomEnabled)) {
|
2019-03-22 21:29:51 -04:00
|
|
|
//Use internal speed (bits 4-5) for idle cycles, register access or IPL rom access
|
|
|
|
speedSelect = _state.InternalSpeed;
|
2021-03-10 11:13:28 -05:00
|
|
|
} else {
|
2019-03-22 21:29:51 -04:00
|
|
|
speedSelect = _state.ExternalSpeed;
|
|
|
|
}
|
|
|
|
|
|
|
|
_state.Cycle += cpuWait[speedSelect];
|
2019-04-07 17:57:30 -04:00
|
|
|
#ifndef DUMMYSPC
|
2019-03-22 21:29:51 -04:00
|
|
|
_dsp->run();
|
2019-04-07 17:57:30 -04:00
|
|
|
#endif
|
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
uint8_t timerInc = timerMultiplier[speedSelect];
|
|
|
|
_state.Timer0.Run(timerInc);
|
|
|
|
_state.Timer1.Run(timerInc);
|
|
|
|
_state.Timer2.Run(timerInc);
|
|
|
|
}
|
|
|
|
|
2019-04-07 14:38:22 -04:00
|
|
|
uint8_t Spc::DebugRead(uint16_t addr)
|
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
if(addr >= 0xFFC0 && _state.RomEnabled) {
|
2019-04-07 14:38:22 -04:00
|
|
|
return _spcBios[addr & 0x3F];
|
|
|
|
}
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
switch(addr) {
|
|
|
|
case 0xF0: return 0;
|
|
|
|
case 0xF1: return 0;
|
2019-04-07 14:38:22 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
case 0xF2: return _state.DspReg;
|
|
|
|
case 0xF3: return _dsp->read(_state.DspReg & 0x7F);
|
2019-04-07 14:38:22 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
case 0xF4: return _state.CpuRegs[0];
|
|
|
|
case 0xF5: return _state.CpuRegs[1];
|
|
|
|
case 0xF6: return _state.CpuRegs[2];
|
|
|
|
case 0xF7: return _state.CpuRegs[3];
|
2019-04-07 14:38:22 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
case 0xF8: return _state.RamReg[0];
|
|
|
|
case 0xF9: return _state.RamReg[1];
|
2019-04-07 14:38:22 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
case 0xFA: return 0;
|
|
|
|
case 0xFB: return 0;
|
|
|
|
case 0xFC: return 0;
|
2019-04-07 14:38:22 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
case 0xFD: return _state.Timer0.DebugRead();
|
|
|
|
case 0xFE: return _state.Timer1.DebugRead();
|
|
|
|
case 0xFF: return _state.Timer2.DebugRead();
|
2019-04-07 14:38:22 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
default: return _ram[addr];
|
2019-04-07 14:38:22 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Spc::DebugWrite(uint16_t addr, uint8_t value)
|
|
|
|
{
|
|
|
|
_ram[addr] = value;
|
|
|
|
}
|
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
uint8_t Spc::Read(uint16_t addr, MemoryOperationType type)
|
|
|
|
{
|
|
|
|
IncCycleCount(addr);
|
|
|
|
|
2019-04-06 17:38:14 -04:00
|
|
|
uint8_t value;
|
2021-03-10 11:13:28 -05:00
|
|
|
if(addr >= 0xFFC0 && _state.RomEnabled) {
|
2019-04-07 17:57:30 -04:00
|
|
|
value = _spcBios[addr & 0x3F];
|
2021-03-10 11:13:28 -05:00
|
|
|
} else {
|
|
|
|
switch(addr) {
|
|
|
|
case 0xF0: value = 0; break;
|
|
|
|
case 0xF1: value = 0; break;
|
|
|
|
|
|
|
|
case 0xF2: value = _state.DspReg; break;
|
|
|
|
case 0xF3:
|
|
|
|
#ifndef DUMMYSPC
|
2019-04-07 17:57:30 -04:00
|
|
|
value = _dsp->read(_state.DspReg & 0x7F);
|
2021-03-10 11:13:28 -05:00
|
|
|
#else
|
|
|
|
value = 0;
|
|
|
|
#endif
|
|
|
|
break;
|
2019-04-07 17:57:30 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
case 0xF4: value = _state.CpuRegs[0]; break;
|
|
|
|
case 0xF5: value = _state.CpuRegs[1]; break;
|
|
|
|
case 0xF6: value = _state.CpuRegs[2]; break;
|
|
|
|
case 0xF7: value = _state.CpuRegs[3]; break;
|
2019-04-07 17:57:30 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
case 0xF8: value = _state.RamReg[0]; break;
|
|
|
|
case 0xF9: value = _state.RamReg[1]; break;
|
2019-04-07 17:57:30 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
case 0xFA: value = 0; break;
|
|
|
|
case 0xFB: value = 0; break;
|
|
|
|
case 0xFC: value = 0; break;
|
2019-04-07 17:57:30 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
case 0xFD: value = _state.Timer0.GetOutput(); break;
|
|
|
|
case 0xFE: value = _state.Timer1.GetOutput(); break;
|
|
|
|
case 0xFF: value = _state.Timer2.GetOutput(); break;
|
2019-04-07 17:57:30 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
default: value = _ram[addr]; break;
|
2019-04-07 17:57:30 -04:00
|
|
|
}
|
2019-03-22 21:29:51 -04:00
|
|
|
}
|
|
|
|
|
2019-04-07 17:57:30 -04:00
|
|
|
#ifndef DUMMYSPC
|
2019-07-25 22:22:09 -04:00
|
|
|
_console->ProcessMemoryRead<CpuType::Spc>(addr, value, type);
|
2021-03-10 11:13:28 -05:00
|
|
|
#else
|
2019-04-07 17:57:30 -04:00
|
|
|
LogRead(addr, value);
|
|
|
|
#endif
|
|
|
|
|
2019-03-28 17:47:43 -04:00
|
|
|
return value;
|
2019-03-22 21:29:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Spc::Write(uint16_t addr, uint8_t value, MemoryOperationType type)
|
|
|
|
{
|
|
|
|
IncCycleCount(addr);
|
|
|
|
|
2019-04-07 17:57:30 -04:00
|
|
|
#ifdef DUMMYSPC
|
|
|
|
LogWrite(addr, value);
|
|
|
|
#else
|
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
//Writes always affect the underlying RAM
|
|
|
|
if(_state.WriteEnabled) {
|
2019-07-25 22:22:09 -04:00
|
|
|
_console->ProcessMemoryWrite<CpuType::Spc>(addr, value, type);
|
2019-03-22 21:29:51 -04:00
|
|
|
_ram[addr] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(addr) {
|
|
|
|
case 0xF0:
|
|
|
|
if(!CheckFlag(SpcFlags::DirectPage)) {
|
|
|
|
_state.InternalSpeed = (value >> 6) & 0x03;
|
|
|
|
_state.ExternalSpeed = (value >> 4) & 0x03;
|
2019-10-10 23:54:38 -04:00
|
|
|
_state.TimersEnabled = (value & 0x08) != 0;
|
|
|
|
_state.TimersDisabled = (value & 0x01) != 0;
|
2019-03-22 21:29:51 -04:00
|
|
|
_state.WriteEnabled = value & 0x02;
|
|
|
|
|
2019-10-10 23:54:38 -04:00
|
|
|
bool timersEnabled = _state.TimersEnabled && !_state.TimersDisabled;
|
|
|
|
_state.Timer0.SetGlobalEnabled(timersEnabled);
|
|
|
|
_state.Timer1.SetGlobalEnabled(timersEnabled);
|
|
|
|
_state.Timer2.SetGlobalEnabled(timersEnabled);
|
2019-03-22 21:29:51 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF1:
|
|
|
|
if(value & SpcControlFlags::ClearPortsA) {
|
|
|
|
_state.CpuRegs[0] = _state.CpuRegs[1] = 0;
|
|
|
|
}
|
|
|
|
if(value & SpcControlFlags::ClearPortsB) {
|
|
|
|
_state.CpuRegs[2] = _state.CpuRegs[3] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_state.Timer0.SetEnabled((value & SpcControlFlags::Timer0) != 0);
|
|
|
|
_state.Timer1.SetEnabled((value & SpcControlFlags::Timer1) != 0);
|
|
|
|
_state.Timer2.SetEnabled((value & SpcControlFlags::Timer2) != 0);
|
|
|
|
|
|
|
|
_state.RomEnabled = (value & SpcControlFlags::EnableRom) != 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF2: _state.DspReg = value; break;
|
|
|
|
case 0xF3:
|
|
|
|
if(_state.DspReg < 128) {
|
|
|
|
_dsp->write(_state.DspReg, value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF4: _state.OutputReg[0] = value; break;
|
|
|
|
case 0xF5: _state.OutputReg[1] = value; break;
|
|
|
|
case 0xF6: _state.OutputReg[2] = value; break;
|
|
|
|
case 0xF7: _state.OutputReg[3] = value; break;
|
|
|
|
case 0xF8: _state.RamReg[0] = value; break;
|
|
|
|
case 0xF9: _state.RamReg[1] = value; break;
|
|
|
|
|
|
|
|
case 0xFA: _state.Timer0.SetTarget(value); break;
|
|
|
|
case 0xFB: _state.Timer1.SetTarget(value); break;
|
|
|
|
case 0xFC: _state.Timer2.SetTarget(value); break;
|
|
|
|
|
|
|
|
case 0xFD: break;
|
|
|
|
case 0xFE: break;
|
|
|
|
case 0xFF: break;
|
|
|
|
}
|
2019-04-07 17:57:30 -04:00
|
|
|
#endif
|
2019-03-22 21:29:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Spc::CpuReadRegister(uint16_t addr)
|
|
|
|
{
|
|
|
|
Run();
|
|
|
|
return _state.OutputReg[addr & 0x03];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Spc::CpuWriteRegister(uint32_t addr, uint8_t value)
|
|
|
|
{
|
|
|
|
Run();
|
|
|
|
_state.CpuRegs[addr & 0x03] = value;
|
|
|
|
}
|
|
|
|
|
2020-02-10 00:12:00 -05:00
|
|
|
uint8_t Spc::DspReadRam(uint16_t addr)
|
|
|
|
{
|
|
|
|
uint8_t value = _ram[addr];
|
|
|
|
#ifndef DUMMYSPC
|
|
|
|
_console->ProcessMemoryRead<CpuType::Spc>(addr, value, MemoryOperationType::Read);
|
|
|
|
#endif
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Spc::DspWriteRam(uint16_t addr, uint8_t value)
|
|
|
|
{
|
|
|
|
#ifndef DUMMYSPC
|
|
|
|
_console->ProcessMemoryWrite<CpuType::Spc>(addr, value, MemoryOperationType::Write);
|
|
|
|
#endif
|
|
|
|
_ram[addr] = value;
|
|
|
|
}
|
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
void Spc::Run()
|
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
if(!_enabled || _state.StopState != CpuStopState::Running) {
|
2019-07-08 22:06:12 -04:00
|
|
|
//STOP or SLEEP were executed - execution is stopped forever.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-29 22:13:22 -04:00
|
|
|
uint64_t targetCycle = (uint64_t)(_memoryManager->GetMasterClock() * _clockRatio);
|
2021-03-10 11:13:28 -05:00
|
|
|
while(_state.Cycle < targetCycle) {
|
2019-07-25 22:22:09 -04:00
|
|
|
ProcessCycle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Spc::ProcessCycle()
|
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_opStep == SpcOpStep::ReadOpCode) {
|
2019-07-25 22:22:09 -04:00
|
|
|
_opCode = GetOpCode();
|
|
|
|
_opStep = SpcOpStep::Addressing;
|
|
|
|
_opSubStep = 0;
|
2021-03-10 11:13:28 -05:00
|
|
|
} else {
|
2019-07-25 22:22:09 -04:00
|
|
|
Exec();
|
2019-03-22 21:29:51 -04:00
|
|
|
}
|
2019-02-16 11:23:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void Spc::ProcessEndFrame()
|
|
|
|
{
|
2019-03-22 21:29:51 -04:00
|
|
|
Run();
|
2019-02-21 16:49:19 -05:00
|
|
|
|
2020-02-29 11:34:23 -05:00
|
|
|
UpdateClockRatio();
|
2019-03-29 22:13:22 -04:00
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
int sampleCount = _dsp->sample_count();
|
2021-03-10 11:13:28 -05:00
|
|
|
if(sampleCount != 0) {
|
2020-05-18 16:10:53 -04:00
|
|
|
_console->GetSoundMixer()->PlayAudioBuffer(_soundBuffer, sampleCount / 2, Spc::SpcSampleRate);
|
2019-03-22 23:53:20 -04:00
|
|
|
}
|
2019-03-22 21:29:51 -04:00
|
|
|
_dsp->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
|
2019-02-16 11:23:01 -05:00
|
|
|
}
|
2019-03-12 09:15:57 -04:00
|
|
|
|
2019-04-06 17:38:14 -04:00
|
|
|
SpcState Spc::GetState()
|
|
|
|
{
|
|
|
|
return _state;
|
|
|
|
}
|
|
|
|
|
2020-02-10 00:12:00 -05:00
|
|
|
DspState Spc::GetDspState()
|
|
|
|
{
|
|
|
|
DspState state;
|
|
|
|
_dsp->copyRegs(state.Regs);
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
2019-11-01 21:15:11 -04:00
|
|
|
bool Spc::IsMuted()
|
|
|
|
{
|
|
|
|
return _dsp->isMuted();
|
|
|
|
}
|
|
|
|
|
2019-04-06 17:38:14 -04:00
|
|
|
AddressInfo Spc::GetAbsoluteAddress(uint16_t addr)
|
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
if(addr < 0xFFC0 || !_state.RomEnabled) {
|
|
|
|
return AddressInfo { addr, SnesMemoryType::SpcRam };
|
2019-04-06 17:38:14 -04:00
|
|
|
}
|
2021-03-10 11:13:28 -05:00
|
|
|
return AddressInfo { addr & 0x3F, SnesMemoryType::SpcRom };
|
2019-04-27 12:10:31 -04:00
|
|
|
}
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
int Spc::GetRelativeAddress(AddressInfo &absAddress)
|
2019-04-27 12:10:31 -04:00
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
if(absAddress.Type == SnesMemoryType::SpcRom) {
|
|
|
|
if(_state.RomEnabled) {
|
2019-04-27 12:10:31 -04:00
|
|
|
return 0xFFC0 | (absAddress.Address & 0x3F);
|
|
|
|
}
|
2021-03-10 11:13:28 -05:00
|
|
|
} else {
|
|
|
|
if(absAddress.Address < 0xFFC0 || !_state.RomEnabled) {
|
2019-04-27 12:10:31 -04:00
|
|
|
return absAddress.Address;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
2019-04-06 17:38:14 -04:00
|
|
|
}
|
|
|
|
|
2019-03-22 21:38:31 -04:00
|
|
|
uint8_t* Spc::GetSpcRam()
|
|
|
|
{
|
|
|
|
return _ram;
|
|
|
|
}
|
|
|
|
|
2019-04-06 17:38:14 -04:00
|
|
|
uint8_t* Spc::GetSpcRom()
|
|
|
|
{
|
|
|
|
return _spcBios;
|
|
|
|
}
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
void Spc::Serialize(Serializer &s)
|
2019-03-12 09:15:57 -04:00
|
|
|
{
|
2019-03-22 21:29:51 -04:00
|
|
|
s.Stream(_state.A, _state.Cycle, _state.PC, _state.PS, _state.SP, _state.X, _state.Y);
|
|
|
|
s.Stream(_state.CpuRegs[0], _state.CpuRegs[1], _state.CpuRegs[2], _state.CpuRegs[3]);
|
|
|
|
s.Stream(_state.OutputReg[0], _state.OutputReg[1], _state.OutputReg[2], _state.OutputReg[3]);
|
|
|
|
s.Stream(_state.RamReg[0], _state.RamReg[1]);
|
|
|
|
s.Stream(_state.ExternalSpeed, _state.InternalSpeed, _state.WriteEnabled, _state.TimersEnabled);
|
2019-03-29 22:13:22 -04:00
|
|
|
s.Stream(_state.DspReg, _state.RomEnabled, _clockRatio);
|
2019-03-12 09:15:57 -04:00
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
_state.Timer0.Serialize(s);
|
|
|
|
_state.Timer1.Serialize(s);
|
|
|
|
_state.Timer2.Serialize(s);
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
ArrayInfo<uint8_t> ram { _ram, Spc::SpcRamSize };
|
2019-03-29 22:13:22 -04:00
|
|
|
s.Stream(ram);
|
2019-03-22 21:29:51 -04:00
|
|
|
|
|
|
|
uint8_t dspState[SPC_DSP::state_size];
|
|
|
|
memset(dspState, 0, SPC_DSP::state_size);
|
2021-03-10 11:13:28 -05:00
|
|
|
if(s.IsSaving()) {
|
|
|
|
uint8_t *out = dspState;
|
|
|
|
_dsp->copy_state(&out, [](uint8_t** output, void* in, size_t size) {
|
2019-03-12 09:15:57 -04:00
|
|
|
memcpy(*output, in, size);
|
|
|
|
*output += size;
|
|
|
|
});
|
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
s.StreamArray(dspState, SPC_DSP::state_size);
|
2021-03-10 11:13:28 -05:00
|
|
|
} else {
|
2019-03-22 21:29:51 -04:00
|
|
|
s.StreamArray(dspState, SPC_DSP::state_size);
|
2019-03-12 09:15:57 -04:00
|
|
|
|
2020-02-29 11:34:23 -05:00
|
|
|
UpdateClockRatio();
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
uint8_t *in = dspState;
|
|
|
|
_dsp->copy_state(&in, [](uint8_t** input, void* output, size_t size) {
|
2019-03-12 09:15:57 -04:00
|
|
|
memcpy(output, *input, size);
|
|
|
|
*input += size;
|
|
|
|
});
|
|
|
|
|
2019-03-22 21:29:51 -04:00
|
|
|
_dsp->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
|
2019-03-12 09:15:57 -04:00
|
|
|
}
|
2019-07-01 18:34:39 -04:00
|
|
|
|
2019-10-10 23:54:38 -04:00
|
|
|
s.Stream(_operandA, _operandB, _tmp1, _tmp2, _tmp3, _opCode, _opStep, _opSubStep, _enabled, _state.TimersDisabled);
|
2019-03-12 09:15:57 -04:00
|
|
|
}
|
2019-03-22 21:29:51 -04:00
|
|
|
|
|
|
|
uint8_t Spc::GetOpCode()
|
|
|
|
{
|
2019-04-07 12:25:14 -04:00
|
|
|
uint8_t value = Read(_state.PC, MemoryOperationType::ExecOpCode);
|
|
|
|
_state.PC++;
|
|
|
|
return value;
|
2019-03-22 21:29:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Spc::ReadOperandByte()
|
|
|
|
{
|
2019-04-07 12:25:14 -04:00
|
|
|
uint8_t value = Read(_state.PC, MemoryOperationType::ExecOperand);
|
|
|
|
_state.PC++;
|
|
|
|
return value;
|
2019-03-22 21:29:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t Spc::ReadWord(uint16_t addr, MemoryOperationType type)
|
|
|
|
{
|
|
|
|
uint8_t lsb = Read(addr, type);
|
|
|
|
uint8_t msb = Read(addr + 1, type);
|
|
|
|
return (msb << 8) | lsb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Spc::ClearFlags(uint8_t flags)
|
|
|
|
{
|
|
|
|
_state.PS &= ~flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Spc::SetFlags(uint8_t flags)
|
|
|
|
{
|
|
|
|
_state.PS |= flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Spc::CheckFlag(uint8_t flag)
|
|
|
|
{
|
|
|
|
return (_state.PS & flag) == flag;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Spc::SetZeroNegativeFlags(uint8_t value)
|
|
|
|
{
|
|
|
|
ClearFlags(SpcFlags::Zero | SpcFlags::Negative);
|
2021-03-10 11:13:28 -05:00
|
|
|
if(value == 0) {
|
2019-03-22 21:29:51 -04:00
|
|
|
SetFlags(SpcFlags::Zero);
|
2021-03-10 11:13:28 -05:00
|
|
|
} else if(value & 0x80) {
|
2019-03-22 21:29:51 -04:00
|
|
|
SetFlags(SpcFlags::Negative);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Spc::SetZeroNegativeFlags16(uint16_t value)
|
|
|
|
{
|
|
|
|
ClearFlags(SpcFlags::Zero | SpcFlags::Negative);
|
2021-03-10 11:13:28 -05:00
|
|
|
if(value == 0) {
|
2019-03-22 21:29:51 -04:00
|
|
|
SetFlags(SpcFlags::Zero);
|
2021-03-10 11:13:28 -05:00
|
|
|
} else if(value & 0x8000) {
|
2019-03-22 21:29:51 -04:00
|
|
|
SetFlags(SpcFlags::Negative);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Spc::GetByteValue()
|
|
|
|
{
|
2019-07-01 18:34:39 -04:00
|
|
|
return Read(_operandA);
|
2019-03-22 21:29:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Spc::Push(uint8_t value)
|
|
|
|
{
|
|
|
|
Write(0x100 | _state.SP, value);
|
|
|
|
_state.SP--;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Spc::Pop()
|
|
|
|
{
|
|
|
|
_state.SP++;
|
|
|
|
return Read(0x100 | _state.SP);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t Spc::GetDirectAddress(uint8_t offset)
|
|
|
|
{
|
|
|
|
return (CheckFlag(SpcFlags::DirectPage) ? 0x100 : 0) + offset;
|
|
|
|
}
|
2019-10-19 15:23:17 -04:00
|
|
|
|
|
|
|
void Spc::LoadSpcFile(SpcFileData* data)
|
|
|
|
{
|
|
|
|
memcpy(_ram, data->SpcRam, Spc::SpcRamSize);
|
|
|
|
|
|
|
|
_dsp->load(data->DspRegs);
|
|
|
|
|
|
|
|
_state.PC = data->PC;
|
|
|
|
_state.A = data->A;
|
|
|
|
_state.X = data->X;
|
|
|
|
_state.Y = data->Y;
|
|
|
|
_state.PS = data->PS;
|
|
|
|
_state.SP = data->SP;
|
|
|
|
|
|
|
|
Write(0xF1, data->ControlReg);
|
|
|
|
_state.DspReg = data->DspRegSelect;
|
|
|
|
|
|
|
|
_state.CpuRegs[0] = data->CpuRegs[0];
|
|
|
|
_state.CpuRegs[1] = data->CpuRegs[1];
|
|
|
|
_state.CpuRegs[2] = data->CpuRegs[2];
|
|
|
|
_state.CpuRegs[3] = data->CpuRegs[3];
|
2021-03-10 11:13:28 -05:00
|
|
|
|
2019-10-19 15:23:17 -04:00
|
|
|
_state.RamReg[0] = data->RamRegs[0];
|
|
|
|
_state.RamReg[1] = data->RamRegs[1];
|
|
|
|
|
|
|
|
_state.Timer0.SetTarget(data->TimerTarget[0]);
|
|
|
|
_state.Timer1.SetTarget(data->TimerTarget[1]);
|
|
|
|
_state.Timer2.SetTarget(data->TimerTarget[2]);
|
|
|
|
|
|
|
|
_state.Timer0.SetOutput(data->TimerOutput[0]);
|
|
|
|
_state.Timer1.SetOutput(data->TimerOutput[0]);
|
|
|
|
_state.Timer2.SetOutput(data->TimerOutput[0]);
|
2020-10-11 13:57:01 +03:00
|
|
|
}
|
|
|
|
|
2020-10-11 14:26:01 -04:00
|
|
|
void Spc::SetReg(SpcRegister reg, uint16_t value)
|
2020-10-11 13:57:01 +03:00
|
|
|
{
|
|
|
|
switch (reg)
|
|
|
|
{
|
|
|
|
case SpcRegister::SpcRegPC:
|
2021-03-10 11:13:28 -05:00
|
|
|
{
|
|
|
|
_state.PC = value;
|
|
|
|
} break;
|
2020-10-11 13:57:01 +03:00
|
|
|
case SpcRegister::SpcRegA:
|
2021-03-10 11:13:28 -05:00
|
|
|
{
|
|
|
|
_state.A = value & 0xFF;
|
|
|
|
} break;
|
2020-10-11 13:57:01 +03:00
|
|
|
case SpcRegister::SpcRegX:
|
2021-03-10 11:13:28 -05:00
|
|
|
{
|
|
|
|
_state.X = value & 0xFF;
|
|
|
|
} break;
|
2020-10-11 13:57:01 +03:00
|
|
|
case SpcRegister::SpcRegY:
|
2021-03-10 11:13:28 -05:00
|
|
|
{
|
|
|
|
_state.Y = value & 0xFF;
|
|
|
|
} break;
|
2020-10-11 13:57:01 +03:00
|
|
|
case SpcRegister::SpcRegSP:
|
2021-03-10 11:13:28 -05:00
|
|
|
{
|
|
|
|
_state.SP = value & 0xFF;
|
|
|
|
} break;
|
2020-10-11 13:57:01 +03:00
|
|
|
case SpcRegister::SpcRegPS:
|
2021-03-10 11:13:28 -05:00
|
|
|
{
|
|
|
|
_state.PS = value & 0xFF;
|
|
|
|
} break;
|
2020-10-11 13:57:01 +03:00
|
|
|
}
|
|
|
|
}
|