Mesen-S/Core/NecDsp.cpp
Sour faf4d62ef4 GB: Allow picking GB model (original vs color) and using a boot rom
+Adds boot rom/sprite ram in hex editor
+Allow colorized GB games in GBC mode
2020-05-26 00:05:35 -04:00

589 lines
17 KiB
C++

#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<uint8_t> &programRom, vector<uint8_t> &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<uint8_t> &embeddedFirware)
{
bool firmwareLoaded = false;
vector<uint8_t> programRom;
vector<uint8_t> 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<CpuType::NecDsp>((_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<uint16_t>(_ram, _ramSize);
s.StreamArray<uint16_t>(_stack, _stackSize);
}