#include "stdafx.h" #include "NecDsp.h" #include "MemoryManager.h" #include "MemoryMappings.h" #include "Console.h" #include "NotificationManager.h" #include "BaseCartridge.h" #include "CartTypes.h" #include "MessageManager.h" #include "EmuSettings.h" #include "RamHandler.h" #include "BatteryManager.h" #include "FirmwareHelper.h" #include "../Utilities/FolderUtilities.h" NecDsp::NecDsp(CoprocessorType type, Console* console, vector &programRom, vector &dataRom) : BaseCoprocessor(SnesMemoryType::Register) { _console = console; _type = type; _memoryManager = console->GetMemoryManager().get(); _memoryType = SnesMemoryType::Register; MemoryMappings *mm = _memoryManager->GetMemoryMappings(); if(type == CoprocessorType::ST010 || type == CoprocessorType::ST011) { if(type == CoprocessorType::ST010) { _frequency = 11000000; } else { _frequency = 22000000; } _registerMask = 0x0001; _ramSize = 0x800; _stackSize = 8; mm->RegisterHandler(0x60, 0x60, 0x0000, 0x0FFF, this); mm->RegisterHandler(0xE0, 0xE0, 0x0000, 0x0FFF, this); mm->RegisterHandler(0x68, 0x6F, 0x0000, 0x0FFF, this); mm->RegisterHandler(0xE8, 0xEF, 0x0000, 0x0FFF, this); } else { _ramSize = 0x100; _stackSize = 4; _frequency = 7600000; if(console->GetCartridge()->GetCartFlags() & CartFlags::LoRom) { _registerMask = 0x4000; mm->RegisterHandler(0x30, 0x3F, 0x8000, 0xFFFF, this); mm->RegisterHandler(0xB0, 0xBF, 0x8000, 0xFFFF, this); //For Super Bases Loaded 2 mm->RegisterHandler(0x60, 0x6F, 0x0000, 0x7FFF, this); mm->RegisterHandler(0xE0, 0xEF, 0x0000, 0x7FFF, this); } else if(console->GetCartridge()->GetCartFlags() & CartFlags::HiRom) { _registerMask = 0x1000; mm->RegisterHandler(0x00, 0x1F, 0x6000, 0x7FFF, this); mm->RegisterHandler(0x80, 0x9F, 0x6000, 0x7FFF, this); } } _progSize = (uint32_t)programRom.size(); _progRom = new uint8_t[_progSize]; _prgCache = new uint32_t[_progSize / 3]; _progMask = (_progSize / 3)- 1; _dataSize = (uint32_t)dataRom.size() / 2; _dataRom = new uint16_t[_dataSize]; _dataMask = _dataSize - 1; _ram = new uint16_t[_ramSize]; _ramMask = _ramSize - 1; _stackMask = _stackSize - 1; console->GetSettings()->InitializeRam(_ram, _ramSize * sizeof(uint16_t)); console->GetSettings()->InitializeRam(_stack, _stackSize * sizeof(uint16_t)); memcpy(_progRom, programRom.data(), _progSize); BuildProgramCache(); for(uint32_t i = 0; i < _dataSize; i++) { _dataRom[i] = dataRom[i * 2] | (dataRom[i * 2 + 1] << 8); } } NecDsp::~NecDsp() { delete[] _progRom; delete[] _prgCache; delete[] _dataRom; delete[] _ram; } NecDsp* NecDsp::InitCoprocessor(CoprocessorType type, Console *console, vector &embeddedFirware) { bool firmwareLoaded = false; vector programRom; vector dataRom; switch(type) { case CoprocessorType::DSP1: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::DSP1, "dsp1.rom", "dsp1.program.rom", "dsp1.data.rom", programRom, dataRom, embeddedFirware); break; case CoprocessorType::DSP1B: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::DSP1B, "dsp1b.rom", "dsp1b.program.rom", "dsp1b.data.rom", programRom, dataRom, embeddedFirware); break; case CoprocessorType::DSP2: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::DSP2, "dsp2.rom", "dsp2.program.rom", "dsp2.data.rom", programRom, dataRom, embeddedFirware); break; case CoprocessorType::DSP3: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::DSP3, "dsp3.rom", "dsp3.program.rom", "dsp3.data.rom", programRom, dataRom, embeddedFirware); break; case CoprocessorType::DSP4: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::DSP4, "dsp4.rom", "dsp4.program.rom", "dsp4.data.rom", programRom, dataRom, embeddedFirware); break; case CoprocessorType::ST010: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::ST010, "st010.rom", "st010.program.rom", "st010.data.rom", programRom, dataRom, embeddedFirware, 0xC000, 0x1000); break; case CoprocessorType::ST011: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::ST011, "st011.rom", "st011.program.rom", "st011.data.rom", programRom, dataRom, embeddedFirware, 0xC000, 0x1000); break; default: break; } if(!firmwareLoaded) { return nullptr; } return new NecDsp(type, console, programRom, dataRom); } void NecDsp::Reset() { _cycleCount = 0; _state = {}; } void NecDsp::LoadBattery() { if(_type == CoprocessorType::ST010 || _type == CoprocessorType::ST011) { _console->GetBatteryManager()->LoadBattery(".srm", (uint8_t*)_ram, _ramSize * sizeof(uint16_t)); } } void NecDsp::SaveBattery() { if(_type == CoprocessorType::ST010 || _type == CoprocessorType::ST011) { _console->GetBatteryManager()->SaveBattery(".srm", (uint8_t*)_ram, _ramSize * sizeof(uint16_t)); } } void NecDsp::BuildProgramCache() { //For the sake of performance, keep a precalculated array of 24-bit opcodes for each entry in the ROM for(uint32_t i = 0; i < _progSize / 3; i++) { _prgCache[i] = _progRom[i * 3] | (_progRom[i * 3 + 1] << 8) | (_progRom[i * 3 + 2] << 16); } } void NecDsp::ReadOpCode() { _opCode = _prgCache[_state.PC & _progMask]; _console->ProcessMemoryRead((_state.PC & _progMask) * 3, _opCode, MemoryOperationType::ExecOpCode); } void NecDsp::Run() { uint64_t targetCycle = (uint64_t)(_memoryManager->GetMasterClock() * (_frequency / _console->GetMasterClockRate())); if(_inRqmLoop && !_console->IsDebugging()) { _cycleCount = targetCycle; return; } while(_cycleCount < targetCycle) { ReadOpCode(); _state.PC++; switch(_opCode & 0xC00000) { case 0x000000: ExecOp(); break; case 0x400000: ExecAndReturn(); break; case 0x800000: Jump(); break; case 0xC00000: Load(_opCode & 0x0F, (uint16_t)(_opCode >> 6)); break; } //Store the multiplication's result int32_t multResult = (int16_t)_state.K * (int16_t)_state.L; _state.M = multResult >> 15; _state.N = multResult << 1; _cycleCount++; } } uint8_t NecDsp::Read(uint32_t addr) { Run(); if((_type == CoprocessorType::ST010 || _type == CoprocessorType::ST011) && (addr & 0x0F0000) >= 0x080000) { //RAM (Banks $68-$6F) uint16_t value = _ram[(addr >> 1) & _ramMask]; return (addr & 0x01) ? (uint8_t)(value >> 8) : (uint8_t)value; } else if(addr & _registerMask) { //SR return (_state.SR >> 8); } else { //DR _inRqmLoop = false; if(_state.SR & NecDspStatusFlags::DataRegControl) { //8 bits _state.SR &= ~NecDspStatusFlags::RequestForMaster; return (uint8_t)_state.DR; } else { //16 bits if(_state.SR & NecDspStatusFlags::DataRegStatus) { _state.SR &= ~NecDspStatusFlags::RequestForMaster; _state.SR &= ~NecDspStatusFlags::DataRegStatus; return _state.DR >> 8; } else { _state.SR |= NecDspStatusFlags::DataRegStatus; return (uint8_t)_state.DR; } } } } void NecDsp::Write(uint32_t addr, uint8_t value) { Run(); if((_type == CoprocessorType::ST010 || _type == CoprocessorType::ST011) && (addr & 0x0F0000) >= 0x080000) { //RAM (Banks $68-$6F) uint16_t ramAddr = (addr >> 1) & _ramMask; if(addr & 0x01) { _ram[ramAddr] = (_ram[ramAddr] & 0xFF) | (value << 8); } else { _ram[ramAddr] = (_ram[ramAddr] & 0xFF00) | value; } } else if(!(addr & _registerMask)) { //DR _inRqmLoop = false; if(_state.SR & NecDspStatusFlags::DataRegControl) { //8 bits _state.SR &= ~NecDspStatusFlags::RequestForMaster; _state.DR = (_state.DR & 0xFF00) | value; } else { //16 bits if(_state.SR & NecDspStatusFlags::DataRegStatus) { _state.SR &= ~NecDspStatusFlags::RequestForMaster; _state.SR &= ~NecDspStatusFlags::DataRegStatus; _state.DR = (_state.DR & 0xFF) | (value << 8); } else { _state.SR |= NecDspStatusFlags::DataRegStatus; _state.DR = (_state.DR & 0xFF00) | value; } } } } uint8_t NecDsp::Peek(uint32_t addr) { //Avoid side effects for now return 0; } void NecDsp::PeekBlock(uint32_t addr, uint8_t *output) { memset(output, 0, 0x1000); } AddressInfo NecDsp::GetAbsoluteAddress(uint32_t address) { return { -1, SnesMemoryType::Register }; } void NecDsp::RunApuOp(uint8_t aluOperation, uint16_t source) { uint16_t result = 0; //Select the accumulator/flags for the operation uint8_t accSelect = (_opCode >> 15) & 0x01; NecDspAccFlags flags = accSelect ? _state.FlagsB : _state.FlagsA; uint16_t acc = accSelect ? _state.B : _state.A; uint8_t otherCarry = accSelect ? _state.FlagsA.Carry : _state.FlagsB.Carry; //Select the 2nd operand for the operation uint8_t pSelect = (_opCode >> 20) & 0x03; uint16_t p; switch(pSelect) { case 0: p = _ram[_state.DP & _ramMask]; break; case 1: p = source; break; case 2: p = _state.M; break; case 3: p = _state.N; break; } //Perform the ALU operation, and set flags switch(aluOperation) { case 0x00: break; case 0x01: result = acc | p; break; case 0x02: result = acc & p; break; case 0x03: result = acc ^ p; break; case 0x04: result = acc - p; break; case 0x05: result = acc + p; break; case 0x06: result = acc - p - otherCarry; break; case 0x07: result = acc + p + otherCarry; break; case 0x08: result = acc - 1; p = 1; break; case 0x09: result = acc + 1; p = 1; break; case 0x0A: result = ~acc; break; case 0x0B: result = (acc >> 1) | (acc & 0x8000); break; case 0x0C: result = (acc << 1) | (uint8_t)otherCarry; break; case 0x0D: result = (acc << 2) | 0x03; break; case 0x0E: result = (acc << 4) | 0x0F; break; case 0x0F: result = (acc << 8) | (acc >> 8); break; } flags.Zero = result == 0; flags.Sign0 = (result & 0x8000) >> 15; if(!flags.Overflow1) { flags.Sign1 = flags.Sign0; } switch(aluOperation) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x0A: case 0x0D: case 0x0E: case 0x0F: flags.Carry = false; flags.Overflow0 = false; flags.Overflow1 = false; break; case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: { uint16_t overflow = (acc ^ result) & (p ^ ((aluOperation & 0x01) ? result : acc)); flags.Overflow0 = (bool)((overflow & 0x8000) >> 15); if(flags.Overflow0 && flags.Overflow1) { flags.Overflow1 = flags.Sign0 == flags.Sign1; } else { flags.Overflow1 |= flags.Overflow0; } flags.Carry = (bool)(((acc ^ p ^ result ^ overflow) & 0x8000) >> 15); break; } case 0x0B: flags.Carry = (bool)(acc & 0x01); flags.Overflow0 = false; flags.Overflow1 = false; break; case 0x0C: flags.Carry = (bool)((acc >> 15) & 0x01); flags.Overflow0 = false; flags.Overflow1 = false; break; } //Update selected accumulator/flags with the operation's results if(accSelect) { _state.B = result; _state.FlagsB = flags; } else { _state.A = result; _state.FlagsA = flags; } } void NecDsp::UpdateDataPointer() { uint16_t dp = _state.DP; switch((_opCode >> 13) & 0x03) { case 0: break; //NOP case 1: dp = (dp & 0xF0) | ((dp + 1) & 0x0F); break; //Increment lower nibble, with no carry case 2: dp = (dp & 0xF0) | ((dp - 1) & 0x0F); break; //Decrement lower nibble, with no carry case 3: dp &= 0xF0; break; //Clear lower nibble } uint8_t dpHighModify = (_opCode >> 9) & 0x0F; _state.DP = dp ^ (dpHighModify << 4); } void NecDsp::ExecOp() { uint8_t aluOperation = (_opCode >> 16) & 0x0F; uint16_t source = GetSourceValue((_opCode >> 4) & 0x0F); //First, process the ALU operation, if needed if(aluOperation) { RunApuOp(aluOperation, source); } //Then transfer data from source to destination uint8_t dest = _opCode & 0x0F; Load(dest, source); if(dest != 0x04) { //Destination was not the data pointer (DP), update it UpdateDataPointer(); } uint8_t rpDecrement = (_opCode >> 8) & 0x01; if(rpDecrement && dest != 0x05) { //Destination was not the rom pointer (RP), decrement it _state.RP--; } } void NecDsp::ExecAndReturn() { ExecOp(); _state.SP = (_state.SP - 1) & _stackMask; _state.PC = _stack[_state.SP]; } void NecDsp::Jump() { uint8_t bank = _opCode & 0x03; uint16_t address = (_opCode >> 2) & 0x7FF; uint16_t target = (_state.PC & 0x2000) | (bank << 11) | address; uint32_t jmpCond = 0; uint16_t jmpType = (_opCode >> 13) & 0x1FF; switch(jmpType) { case 0x00: _state.PC = _state.SerialOut; break; case 0x80: jmpCond = !_state.FlagsA.Carry; break; case 0x82: jmpCond = _state.FlagsA.Carry; break; case 0x84: jmpCond = !_state.FlagsB.Carry; break; case 0x86: jmpCond = _state.FlagsB.Carry; break; case 0x88: jmpCond = !_state.FlagsA.Zero; break; case 0x8A: jmpCond = _state.FlagsA.Zero; break; case 0x8C: jmpCond = !_state.FlagsB.Zero; break; case 0x8E: jmpCond = _state.FlagsB.Zero; break; case 0x90: jmpCond = !_state.FlagsA.Overflow0; break; case 0x92: jmpCond = _state.FlagsA.Overflow0; break; case 0x94: jmpCond = !_state.FlagsB.Overflow0; break; case 0x96: jmpCond = _state.FlagsB.Overflow0; break; case 0x98: jmpCond = !_state.FlagsA.Overflow1; break; case 0x9A: jmpCond = _state.FlagsA.Overflow1; break; case 0x9C: jmpCond = !_state.FlagsB.Overflow1; break; case 0x9E: jmpCond = _state.FlagsB.Overflow1; break; case 0xA0: jmpCond = !_state.FlagsA.Sign0; break; case 0xA2: jmpCond = _state.FlagsA.Sign0; break; case 0xA4: jmpCond = !_state.FlagsB.Sign0; break; case 0xA6: jmpCond = _state.FlagsB.Sign0; break; case 0xA8: jmpCond = !_state.FlagsA.Sign1; break; case 0xAA: jmpCond = _state.FlagsA.Sign1; break; case 0xAC: jmpCond = !_state.FlagsB.Sign1; break; case 0xAE: jmpCond = _state.FlagsB.Sign1; break; case 0xB0: jmpCond = !(_state.DP & 0x0F); break; case 0xB1: jmpCond = _state.DP & 0x0F; break; case 0xB2: jmpCond = (_state.DP & 0x0F) == 0x0F; break; case 0xB3: jmpCond = (_state.DP & 0x0F) != 0x0F; break; case 0xB4: jmpCond = !(_state.SR & NecDspStatusFlags::SerialInControl); break; case 0xB6: jmpCond = _state.SR & NecDspStatusFlags::SerialInControl; break; case 0xB8: jmpCond = !(_state.SR & NecDspStatusFlags::SerialOutControl); break; case 0xBA: jmpCond = _state.SR & NecDspStatusFlags::SerialOutControl; break; case 0xBC: jmpCond = !(_state.SR & NecDspStatusFlags::RequestForMaster); break; case 0xBE: jmpCond = _state.SR & NecDspStatusFlags::RequestForMaster; break; case 0x100: _state.PC = target & ~0x2000; break; case 0x101: _state.PC = target | 0x2000; break; case 0x140: _stack[_state.SP] = _state.PC; _state.SP = (_state.SP + 1) & _stackMask; _state.PC = target & ~0x2000; break; case 0x141: _stack[_state.SP] = _state.PC; _state.SP = (_state.SP + 1) & _stackMask; _state.PC = target | 0x2000; break; } if(jmpCond) { if((_state.PC - 1 == target) && (jmpType == 0xBC || jmpType == 0xBE)) { //CPU is in a wait loop for RQM, skip emulation until the CPU reads/writes from the IO registers _inRqmLoop = true; } _state.PC = target; } } void NecDsp::Load(uint8_t dest, uint16_t value) { switch(dest) { case 0x00: break; case 0x01: _state.A = value; break; case 0x02: _state.B = value; break; case 0x03: _state.TR = value; break; case 0x04: _state.DP = value; break; case 0x05: _state.RP = value; break; case 0x06: _state.DR = value; _state.SR |= NecDspStatusFlags::RequestForMaster; break; case 0x07: _state.SR = (_state.SR & 0x907C) | (value & ~0x907C); break; case 0x08: _state.SerialOut = value; break; case 0x09: _state.SerialOut = value; break; case 0x0A: _state.K = value; break; case 0x0B: _state.K = value; _state.L = _dataRom[_state.RP & _dataMask]; break; case 0x0C: _state.L = value; _state.K = _ram[(_state.DP | 0x40) & _ramMask]; break; case 0x0D: _state.L = value; break; case 0x0E: _state.TRB = value; break; case 0x0F: _ram[_state.DP & _ramMask] = value; break; default: throw std::runtime_error("DSP-1: invalid destination"); } } uint16_t NecDsp::GetSourceValue(uint8_t source) { switch(source) { case 0x00: return _state.TRB; case 0x01: return _state.A; case 0x02: return _state.B; case 0x03: return _state.TR; case 0x04: return _state.DP; case 0x05: return _state.RP; case 0x06: return _dataRom[_state.RP & _dataMask]; case 0x07: return 0x8000 - _state.FlagsA.Sign1; case 0x08: _state.SR |= NecDspStatusFlags::RequestForMaster; return _state.DR; case 0x09: return _state.DR; case 0x0A: return _state.SR; case 0x0B: return _state.SerialIn; case 0x0C: return _state.SerialIn; case 0x0D: return _state.K; case 0x0E: return _state.L; case 0x0F: return _ram[_state.DP & _ramMask]; } throw std::runtime_error("DSP-1: invalid source"); } uint8_t* NecDsp::DebugGetProgramRom() { return _progRom; } uint8_t* NecDsp::DebugGetDataRom() { return (uint8_t*)_dataRom; } uint8_t* NecDsp::DebugGetDataRam() { return (uint8_t*)_ram; } uint32_t NecDsp::DebugGetProgramRomSize() { return _progSize; } uint32_t NecDsp::DebugGetDataRomSize() { return _dataSize * sizeof(uint16_t); } uint32_t NecDsp::DebugGetDataRamSize() { return _ramSize * sizeof(uint16_t); } NecDspState NecDsp::GetState() { return _state; } void NecDsp::Serialize(Serializer &s) { s.Stream( _state.A, _state.B, _state.DP, _state.DR, _state.K, _state.L, _state.M, _state.N, _state.PC, _state.RP, _state.SerialIn, _state.SerialOut, _state.SP, _state.SR, _state.TR, _state.TRB, _state.FlagsA.Carry, _state.FlagsA.Overflow0, _state.FlagsA.Overflow1, _state.FlagsA.Sign0, _state.FlagsA.Sign1, _state.FlagsA.Zero, _state.FlagsB.Carry, _state.FlagsB.Overflow0, _state.FlagsB.Overflow1, _state.FlagsB.Sign0, _state.FlagsB.Sign1, _state.FlagsB.Zero ); s.Stream(_opCode, _cycleCount, _inRqmLoop); s.StreamArray(_ram, _ramSize); s.StreamArray(_stack, _stackSize); }