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
This commit is contained in:
Sour 2020-05-26 00:05:35 -04:00
parent 8b2a6b5fa6
commit faf4d62ef4
39 changed files with 437 additions and 163 deletions

View file

@ -48,6 +48,22 @@ enum class CoprocessorType
Gameboy
};
enum class FirmwareType
{
CX4,
DSP1,
DSP1B,
DSP2,
DSP3,
DSP4,
ST010,
ST011,
ST018,
Satellaview,
Gameboy,
GameboyColor
};
struct RomInfo
{
SnesCartInformation Header;
@ -69,4 +85,4 @@ namespace CartFlags
ExHiRom = 16,
CopierHeader = 32
};
}
}

View file

@ -50,6 +50,7 @@ public:
case SnesMemoryType::GbCartRam:
case SnesMemoryType::GbVideoRam:
case SnesMemoryType::GbHighRam:
case SnesMemoryType::GbBootRom:
return CpuType::Gameboy;
default:

View file

@ -533,6 +533,7 @@ AddressInfo Debugger::GetRelativeAddress(AddressInfo absAddress, CpuType cpuType
case SnesMemoryType::GbWorkRam:
case SnesMemoryType::GbCartRam:
case SnesMemoryType::GbHighRam:
case SnesMemoryType::GbBootRom:
return { _cart->GetGameboy()->GetRelativeAddress(absAddress), SnesMemoryType::GameboyMemory };
case SnesMemoryType::DspProgramRom:

View file

@ -77,6 +77,8 @@ Disassembler::Disassembler(shared_ptr<Console> console, shared_ptr<CodeDataLogge
_gbCartRamSize = _gameboy ? _gameboy->DebugGetMemorySize(SnesMemoryType::GbCartRam) : 0;
_gbHighRam = _gameboy ? _gameboy->DebugGetMemory(SnesMemoryType::GbHighRam) : nullptr;
_gbHighRamSize = _gameboy ? _gameboy->DebugGetMemorySize(SnesMemoryType::GbHighRam) : 0;
_gbBootRom = _gameboy ? _gameboy->DebugGetMemory(SnesMemoryType::GbBootRom) : nullptr;
_gbBootRomSize = _gameboy ? _gameboy->DebugGetMemorySize(SnesMemoryType::GbBootRom) : 0;
_prgCache = vector<DisassemblyInfo>(_prgRomSize);
_sramCache = vector<DisassemblyInfo>(_sramSize);
@ -93,6 +95,7 @@ Disassembler::Disassembler(shared_ptr<Console> console, shared_ptr<CodeDataLogge
_gbWorkRamCache = vector<DisassemblyInfo>(_gbWorkRamSize);
_gbCartRamCache = vector<DisassemblyInfo>(_gbCartRamSize);
_gbHighRamCache = vector<DisassemblyInfo>(_gbHighRamSize);
_gbBootRomCache = vector<DisassemblyInfo>(_gbBootRomSize);
for(int i = 0; i < (int)DebugUtilities::GetLastCpuType(); i++) {
_needDisassemble[i] = true;
@ -113,6 +116,7 @@ Disassembler::Disassembler(shared_ptr<Console> console, shared_ptr<CodeDataLogge
_sources[(int)SnesMemoryType::GbWorkRam] = { _gbWorkRam, &_gbWorkRamCache, _gbWorkRamSize };
_sources[(int)SnesMemoryType::GbCartRam] = { _gbCartRam, &_gbCartRamCache, _gbCartRamSize };
_sources[(int)SnesMemoryType::GbHighRam] = { _gbHighRam, &_gbHighRamCache, _gbHighRamSize };
_sources[(int)SnesMemoryType::GbBootRom] = { _gbBootRom, &_gbBootRomCache, _gbBootRomSize };
if(_necDspProgramRomSize > 0) {
//Build cache for the entire DSP chip (since it only contains instructions)

View file

@ -58,6 +58,7 @@ private:
vector<DisassemblyInfo> _gbWorkRamCache;
vector<DisassemblyInfo> _gbCartRamCache;
vector<DisassemblyInfo> _gbHighRamCache;
vector<DisassemblyInfo> _gbBootRomCache;
SimpleLock _disassemblyLock;
vector<DisassemblyResult> _disassembly;
@ -105,6 +106,8 @@ private:
uint32_t _gbCartRamSize;
uint8_t* _gbHighRam;
uint32_t _gbHighRamSize;
uint8_t* _gbBootRom;
uint32_t _gbBootRomSize;
DisassemblerSource GetSource(SnesMemoryType type);
vector<DisassemblyResult>& GetDisassemblyList(CpuType type);

View file

@ -7,7 +7,7 @@
struct MissingFirmwareMessage
{
const char* Filename;
CoprocessorType FirmwareType;
FirmwareType Firmware;
uint32_t Size;
};
@ -49,8 +49,20 @@ private:
return false;
}
static bool AttemptLoadFirmware(uint8_t** out, string filename, uint32_t size)
{
VirtualFile firmware(FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), filename));
if(firmware.IsValid() && firmware.GetSize() == size) {
*out = new uint8_t[firmware.GetSize()];
firmware.ReadFile(*out, (uint32_t)firmware.GetSize());
return true;
}
return false;
}
public:
static bool LoadDspFirmware(Console *console, CoprocessorType coprocessorType, string combinedFilename, string splitFilenameProgram, string splitFilenameData, vector<uint8_t> &programRom, vector<uint8_t> &dataRom, vector<uint8_t> &embeddedFirware, uint32_t programSize = 0x1800, uint32_t dataSize = 0x800)
static bool LoadDspFirmware(Console *console, FirmwareType type, string combinedFilename, string splitFilenameProgram, string splitFilenameData, vector<uint8_t> &programRom, vector<uint8_t> &dataRom, vector<uint8_t> &embeddedFirware, uint32_t programSize = 0x1800, uint32_t dataSize = 0x800)
{
if(embeddedFirware.size() == programSize + dataSize) {
programRom.insert(programRom.end(), embeddedFirware.begin(), embeddedFirware.begin() + programSize);
@ -62,7 +74,7 @@ public:
MissingFirmwareMessage msg;
msg.Filename = combinedFilename.c_str();
msg.FirmwareType = coprocessorType;
msg.Firmware = type;
msg.Size = programSize + dataSize;
console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg);
@ -83,7 +95,7 @@ public:
MissingFirmwareMessage msg;
msg.Filename = "BsxBios.sfc";
msg.FirmwareType = CoprocessorType::Satellaview;
msg.Firmware = FirmwareType::Satellaview;
msg.Size = 1024*1024;
console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg);
@ -94,4 +106,26 @@ public:
MessageManager::DisplayMessage("Error", "Could not find firmware file for BS-X");
return false;
}
static bool LoadGbBootRom(Console* console, uint8_t** bootRom, FirmwareType type)
{
string filename = type == FirmwareType::Gameboy ? "dmg_boot.bin" : "cgb_boot.bin";
uint32_t size = type == FirmwareType::Gameboy ? 256 : 2304;
if(AttemptLoadFirmware(bootRom, filename, size)) {
return true;
}
MissingFirmwareMessage msg;
msg.Filename = filename.c_str();
msg.Firmware = type;
msg.Size = size;
console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg);
if(AttemptLoadFirmware(bootRom, filename, size)) {
return true;
}
MessageManager::DisplayMessage("Error", "Could not find boot rom: " + filename);
return false;
}
};

View file

@ -14,6 +14,7 @@
#include "GameboyHeader.h"
#include "EmuSettings.h"
#include "MessageManager.h"
#include "FirmwareHelper.h"
#include "../Utilities/VirtualFile.h"
#include "../Utilities/Serializer.h"
@ -50,7 +51,16 @@ Gameboy* Gameboy::Create(Console* console, VirtualFile &romFile)
gb->_cartRam = new uint8_t[gb->_cartRamSize];
gb->_hasBattery = header.HasBattery();
gb->_cgbMode = (header.CgbFlag & 0x80) != 0;
shared_ptr<EmuSettings> settings = console->GetSettings();
EmulationConfig cfg = settings->GetEmulationConfig();
switch(cfg.GbModel) {
default:
case GameboyModel::Auto: gb->_cgbMode = (header.CgbFlag & 0x80) != 0; break;
case GameboyModel::Gameboy: gb->_cgbMode = false; break;
case GameboyModel::GameboyColor: gb->_cgbMode = true; break;
}
gb->_workRamSize = gb->_cgbMode ? 0x8000 : 0x2000;
gb->_videoRamSize = gb->_cgbMode ? 0x4000 : 0x2000;
@ -59,6 +69,15 @@ Gameboy* Gameboy::Create(Console* console, VirtualFile &romFile)
gb->_spriteRam = new uint8_t[Gameboy::SpriteRamSize];
gb->_highRam = new uint8_t[Gameboy::HighRamSize];
gb->_useBootRom = cfg.GbUseBootRom;
gb->_bootRomSize = 0;
if(gb->_useBootRom) {
gb->_useBootRom = FirmwareHelper::LoadGbBootRom(console, &gb->_bootRom, gb->_cgbMode ? FirmwareType::GameboyColor : FirmwareType::Gameboy);
if(gb->_useBootRom) {
gb->_bootRomSize = gb->_cgbMode ? 9 * 256 : 256;
}
}
return gb;
}
@ -77,11 +96,13 @@ Gameboy::~Gameboy()
delete[] _highRam;
delete[] _workRam;
delete[] _bootRom;
}
void Gameboy::PowerOn()
{
EmuSettings* settings = _console->GetSettings().get();
shared_ptr<EmuSettings> settings = _console->GetSettings();
settings->InitializeRam(_cartRam, _cartRamSize);
settings->InitializeRam(_workRam, _workRamSize);
settings->InitializeRam(_spriteRam, Gameboy::SpriteRamSize);
@ -148,9 +169,11 @@ uint32_t Gameboy::DebugGetMemorySize(SnesMemoryType type)
switch(type) {
case SnesMemoryType::GbPrgRom: return _prgRomSize;
case SnesMemoryType::GbWorkRam: return _workRamSize;
case SnesMemoryType::GbVideoRam: return _videoRamSize;
case SnesMemoryType::GbCartRam: return _cartRamSize;
case SnesMemoryType::GbHighRam: return Gameboy::HighRamSize;
case SnesMemoryType::GbBootRom: return _bootRomSize;
case SnesMemoryType::GbVideoRam: return _videoRamSize;
case SnesMemoryType::GbSpriteRam: return Gameboy::SpriteRamSize;
default: return 0;
}
}
@ -160,9 +183,11 @@ uint8_t* Gameboy::DebugGetMemory(SnesMemoryType type)
switch(type) {
case SnesMemoryType::GbPrgRom: return _prgRom;
case SnesMemoryType::GbWorkRam: return _workRam;
case SnesMemoryType::GbVideoRam: return _videoRam;
case SnesMemoryType::GbCartRam: return _cartRam;
case SnesMemoryType::GbHighRam: return _highRam;
case SnesMemoryType::GbBootRom: return _bootRom;
case SnesMemoryType::GbVideoRam: return _videoRam;
case SnesMemoryType::GbSpriteRam: return _spriteRam;
default: return nullptr;
}
}
@ -209,6 +234,9 @@ AddressInfo Gameboy::GetAbsoluteAddress(uint16_t addr)
} else if(ptr >= _cartRam && ptr < _cartRam + _cartRamSize) {
addrInfo.Address = (int32_t)(ptr - _cartRam);
addrInfo.Type = SnesMemoryType::GbCartRam;
} else if(ptr >= _bootRom && ptr < _bootRom + _bootRomSize) {
addrInfo.Address = (int32_t)(ptr - _bootRom);
addrInfo.Type = SnesMemoryType::GbBootRom;
}
return addrInfo;
}
@ -234,6 +262,11 @@ bool Gameboy::IsCgb()
return _cgbMode;
}
bool Gameboy::UseBootRom()
{
return _useBootRom;
}
uint64_t Gameboy::GetCycleCount()
{
return _cpu->GetCycleCount();

View file

@ -19,7 +19,7 @@ private:
static constexpr int SpriteRamSize = 0xA0;
static constexpr int HighRamSize = 0x7F;
Console* _console;
Console* _console = nullptr;
unique_ptr<GbMemoryManager> _memoryManager;
unique_ptr<GbCpu> _cpu;
@ -29,23 +29,27 @@ private:
unique_ptr<GbTimer> _timer;
unique_ptr<GbDmaController> _dmaController;
bool _hasBattery;
bool _cgbMode;
bool _hasBattery = false;
bool _cgbMode = false;
bool _useBootRom = false;
uint8_t* _prgRom;
uint32_t _prgRomSize;
uint8_t* _prgRom = nullptr;
uint32_t _prgRomSize = 0;
uint8_t* _cartRam;
uint32_t _cartRamSize;
uint8_t* _cartRam = nullptr;
uint32_t _cartRamSize = 0;
uint8_t* _workRam;
uint32_t _workRamSize;
uint8_t* _workRam = nullptr;
uint32_t _workRamSize = 0;
uint8_t* _videoRam;
uint32_t _videoRamSize;
uint8_t* _videoRam = nullptr;
uint32_t _videoRamSize = 0;
uint8_t* _spriteRam;
uint8_t* _highRam;
uint8_t* _spriteRam = nullptr;
uint8_t* _highRam = nullptr;
uint8_t* _bootRom = nullptr;
uint32_t _bootRomSize = 0;
public:
static Gameboy* Create(Console* console, VirtualFile& romFile);
@ -70,6 +74,7 @@ public:
int32_t GetRelativeAddress(AddressInfo& absAddress);
bool IsCgb();
bool UseBootRom();
uint64_t GetCycleCount();
uint64_t GetApuCycleCount();

View file

@ -11,6 +11,7 @@ GbApu::GbApu(Console* console, Gameboy* gameboy)
_console = console;
_soundMixer = console->GetSoundMixer().get();
_gameboy = gameboy;
_state = {};
_soundBuffer = new int16_t[GbApu::MaxSamples*2];
memset(_soundBuffer, 0, GbApu::MaxSamples*2*sizeof(int16_t));

View file

@ -10,23 +10,24 @@ GbCpu::GbCpu(Console* console, Gameboy* gameboy, GbMemoryManager* memoryManager)
_console = console;
_gameboy = gameboy;
_memoryManager = memoryManager;
_state = {};
#ifdef USEBOOTROM
_state.PC = 0;
_state.SP = 0xFFFF;
#else
_state.PC = 0x100;
_state.SP = 0xFFFE;
_state.A = gameboy->IsCgb() ? 0x11 : 0x01;
_state.B = 0x00;
_state.C = 0x13;
_state.D = 0x00;
_state.E = 0xD8;
_state.H = 0x01;
_state.L = 0x4D;
_state.Flags = 0xB0;
#endif
if(_gameboy->UseBootRom()) {
_state.PC = 0;
_state.SP = 0xFFFF;
} else {
_state.PC = 0x100;
_state.SP = 0xFFFE;
_state.A = _gameboy->IsCgb() ? 0x11 : 0x01;
_state.B = 0x00;
_state.C = 0x13;
_state.D = 0x00;
_state.E = 0xD8;
_state.H = 0x01;
_state.L = 0x4D;
_state.Flags = 0xB0;
}
}
GbCpu::~GbCpu()

View file

@ -18,25 +18,8 @@
void GbMemoryManager::Init(Console* console, Gameboy* gameboy, GbCart* cart, GbPpu* ppu, GbApu* apu, GbTimer* timer, GbDmaController* dmaController)
{
_state = {};
_state.CgbWorkRamBank = 1;
_prgRom = gameboy->DebugGetMemory(SnesMemoryType::GbPrgRom);
_prgRomSize = gameboy->DebugGetMemorySize(SnesMemoryType::GbPrgRom);
_cartRam = gameboy->DebugGetMemory(SnesMemoryType::GbCartRam);
_cartRamSize = gameboy->DebugGetMemorySize(SnesMemoryType::GbCartRam);
_workRam = gameboy->DebugGetMemory(SnesMemoryType::GbWorkRam);
_workRamSize = gameboy->DebugGetMemorySize(SnesMemoryType::GbWorkRam);
_highRam = gameboy->DebugGetMemory(SnesMemoryType::GbHighRam);
#ifdef USEBOOTROM
VirtualFile bootRom("Firmware\\boot.bin");
bootRom.ReadFile(_bootRom, 256);
_state.DisableBootRom = false;
#else
_state.DisableBootRom = true;
#endif
_apu = apu;
_ppu = ppu;
_gameboy = gameboy;
@ -47,6 +30,10 @@ void GbMemoryManager::Init(Console* console, Gameboy* gameboy, GbCart* cart, GbP
_controlManager = console->GetControlManager().get();
_settings = console->GetSettings().get();
_state = {};
_state.CgbWorkRamBank = 1;
_state.DisableBootRom = !_gameboy->UseBootRom();
MapRegisters(0x8000, 0x9FFF, RegisterAccess::ReadWrite);
MapRegisters(0xFE00, 0xFFFF, RegisterAccess::ReadWrite);
@ -73,7 +60,10 @@ void GbMemoryManager::RefreshMappings()
_cart->RefreshMappings();
if(!_state.DisableBootRom) {
_reads[0] = _bootRom;
Map(0x0000, 0x00FF, GbMemoryType::BootRom, 0, true);
if(_gameboy->IsCgb()) {
Map(0x0200, 0x08FF, GbMemoryType::BootRom, 0x200, true);
}
}
}
@ -220,7 +210,7 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr)
return _dmaController->Read();
} else if(addr >= 0xFF80) {
return _highRam[addr & 0x7F]; //80-FE
} else if(addr >= 0xFF4D) {
} else if(addr >= 0xFF4C) {
if(_gameboy->IsCgb()) {
switch(addr) {
//FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch
@ -238,9 +228,9 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr)
}
}
LogDebug("[Debug] GB - Missing read handler: $" + HexUtilities::ToHex(addr));
return 0xFF; //4D-7F, open bus
return 0xFF; //4C-7F, open bus
} else if(addr >= 0xFF40) {
return _ppu->Read(addr); //40-4C
return _ppu->Read(addr); //40-4B
} else if(addr >= 0xFF10) {
return _apu->Read(addr); //10-3F
} else {
@ -277,8 +267,8 @@ void GbMemoryManager::WriteRegister(uint16_t addr, uint8_t value)
_dmaController->Write(value);
} else if(addr >= 0xFF80) {
_highRam[addr & 0x7F] = value; //80-FE
} else if(addr >= 0xFF4D) {
//4D-7F
} else if(addr >= 0xFF4C) {
//4C-7F
if(addr == 0xFF50) {
if(value & 0x01) {
_state.DisableBootRom = true;
@ -295,6 +285,12 @@ void GbMemoryManager::WriteRegister(uint16_t addr, uint8_t value)
_dmaController->WriteCgb(addr, value);
break;
case 0xFF4C: //CGB - "LCDMODE", set by boot rom to turn off CGB features for the LCD for DMG games
if(!_state.DisableBootRom) {
_ppu->WriteCgbRegister(addr, value);
}
break;
case 0xFF4F: //CGB - VRAM banking
case 0xFF68: case 0xFF69: case 0xFF6A: case 0xFF6B: //CGB - Palette
_ppu->WriteCgbRegister(addr, value);
@ -312,7 +308,7 @@ void GbMemoryManager::WriteRegister(uint16_t addr, uint8_t value)
}
}
} else if(addr >= 0xFF40) {
_ppu->Write(addr, value); //40-4C
_ppu->Write(addr, value); //40-4B
} else if(addr >= 0xFF10) {
_apu->Write(addr, value); //10-3F
} else {

View file

@ -28,13 +28,6 @@ private:
GbTimer* _timer;
GbDmaController* _dmaController;
uint8_t _bootRom[256];
uint8_t* _prgRom = nullptr;
uint32_t _prgRomSize = 0;
uint8_t* _workRam = nullptr;
uint32_t _workRamSize = 0;
uint8_t* _cartRam = nullptr;
uint32_t _cartRamSize = 0;
uint8_t* _highRam = nullptr;
uint8_t* _reads[0x100] = {};

View file

@ -22,10 +22,6 @@ void GbPpu::Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryMana
_memoryManager = memoryManager;
_vram = vram;
_oam = oam;
_state = {};
_state.Mode = PpuMode::HBlank;
_lastFrameTime = 0;
_outputBuffers[0] = new uint16_t[256 * 240];
_outputBuffers[1] = new uint16_t[256 * 240];
@ -39,17 +35,31 @@ void GbPpu::Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryMana
memset(_eventViewerBuffers[1], 0, 456 * 154 * sizeof(uint16_t));
_currentEventViewerBuffer = _eventViewerBuffers[0];
#ifndef USEBOOTROM
Write(0xFF40, 0x91);
Write(0xFF42, 0x00);
Write(0xFF43, 0x00);
Write(0xFF45, 0x00);
Write(0xFF47, 0xFC);
Write(0xFF48, 0xFF);
Write(0xFF49, 0xFF);
Write(0xFF4A, 0);
Write(0xFF4B, 0);
#endif
_state = {};
_state.Mode = PpuMode::HBlank;
_state.CgbEnabled = _gameboy->IsCgb();
_lastFrameTime = 0;
if(!_gameboy->IsCgb()) {
for(int i = 0; i < 4; i++) {
//Init default palette for use with DMG
_state.CgbBgPalettes[i] = bwRgbPalette[i];
_state.CgbObjPalettes[i] = bwRgbPalette[i];
_state.CgbObjPalettes[i+4] = bwRgbPalette[i];
}
}
if(!_gameboy->UseBootRom()) {
Write(0xFF40, 0x91);
Write(0xFF42, 0x00);
Write(0xFF43, 0x00);
Write(0xFF45, 0x00);
Write(0xFF47, 0xFC);
Write(0xFF48, 0xFF);
Write(0xFF49, 0xFF);
Write(0xFF4A, 0);
Write(0xFF4B, 0);
}
}
GbPpu::~GbPpu()
@ -213,7 +223,7 @@ void GbPpu::RunDrawCycle()
}
uint16_t rgbColor;
if(_gameboy->IsCgb()) {
if(_state.CgbEnabled) {
if(isSprite) {
rgbColor = _state.CgbObjPalettes[entry.Color | ((entry.Attributes & 0x07) << 2)];
} else {
@ -221,9 +231,10 @@ void GbPpu::RunDrawCycle()
}
} else {
if(isSprite) {
rgbColor = bwRgbPalette[(((entry.Attributes & 0x10) ? _state.ObjPalette1 : _state.ObjPalette0) >> (entry.Color * 2)) & 0x03];
uint8_t colorIndex = (((entry.Attributes & 0x10) ? _state.ObjPalette1 : _state.ObjPalette0) >> (entry.Color * 2)) & 0x03;
rgbColor = _state.CgbObjPalettes[((entry.Attributes & 0x10) ? 4 : 0) | colorIndex];
} else {
rgbColor = bwRgbPalette[(_state.BgPalette >> (entry.Color * 2)) & 0x03];
rgbColor = _state.CgbBgPalettes[(_state.BgPalette >> (entry.Color * 2)) & 0x03];
}
}
_currentBuffer[outOffset] = rgbColor;
@ -282,7 +293,7 @@ void GbPpu::ClockSpriteFetcher()
uint8_t sprTile = _oam[_fetchSprite + 2];
uint8_t sprAttr = _oam[_fetchSprite + 3];
bool vMirror = (sprAttr & 0x40) != 0;
uint16_t tileBank = _gameboy->IsCgb() ? ((sprAttr & 0x08) ? 0x2000 : 0x0000) : 0;
uint16_t tileBank = _state.CgbEnabled ? ((sprAttr & 0x08) ? 0x2000 : 0x0000) : 0;
uint8_t sprOffsetY = vMirror ? (_state.LargeSprites ? 15 : 7) - (_state.Scanline - sprY) : (_state.Scanline - sprY);
if(_state.LargeSprites) {
@ -308,7 +319,7 @@ void GbPpu::ClockSpriteFetcher()
void GbPpu::FindNextSprite()
{
if(_prevSprite < _spriteCount && _fetchSprite < 0 && (_state.SpritesEnabled || _gameboy->IsCgb())) {
if(_prevSprite < _spriteCount && _fetchSprite < 0 && (_state.SpritesEnabled || _state.CgbEnabled)) {
for(int i = _prevSprite; i < _spriteCount; i++) {
if((int)_spriteX[i] - 8 == _drawnPixels) {
_fetchSprite = _spriteIndexes[i];
@ -349,7 +360,7 @@ void GbPpu::ClockTileFetcher()
uint16_t tileAddr = tilemapAddr + _fetchColumn + row * 32;
uint8_t tileIndex = _vram[tileAddr];
uint8_t attributes = _gameboy->IsCgb() ? _vram[tileAddr | 0x2000] : 0;
uint8_t attributes = _state.CgbEnabled ? _vram[tileAddr | 0x2000] : 0;
bool vMirror = (attributes & 0x40) != 0;
uint16_t tileBank = (attributes & 0x08) ? 0x2000 : 0x0000;
@ -648,12 +659,13 @@ uint8_t GbPpu::ReadCgbRegister(uint16_t addr)
case 0xFF6B: return (_state.CgbObjPalettes[_state.CgbObjPalPosition >> 1] >> ((_state.CgbObjPalPosition & 0x01) ? 8 : 0) & 0xFF);
}
LogDebug("[Debug] GBC - Missing read handler: $" + HexUtilities::ToHex(addr));
return 0;
return 0xFF;
}
void GbPpu::WriteCgbRegister(uint16_t addr, uint8_t value)
{
switch(addr) {
case 0xFF4C: _state.CgbEnabled = (value & 0x0C) == 0; break;
case 0xFF4F: _state.CgbVramBank = value & 0x01; break;
case 0xFF68:
@ -708,7 +720,7 @@ void GbPpu::Serialize(Serializer& s)
_state.WindowEnabled, _state.BgTileSelect, _state.BgTilemapSelect, _state.LargeSprites, _state.SpritesEnabled, _state.BgEnabled,
_state.Status, _state.FrameCount, _lastFrameTime, _state.LyCoincidenceFlag,
_state.CgbBgPalAutoInc, _state.CgbBgPalPosition,
_state.CgbObjPalAutoInc, _state.CgbObjPalPosition, _state.CgbVramBank
_state.CgbObjPalAutoInc, _state.CgbObjPalPosition, _state.CgbVramBank, _state.CgbEnabled
);
s.StreamArray(_state.CgbBgPalettes, 4 * 8);

View file

@ -64,6 +64,8 @@ private:
public:
virtual ~GbPpu();
void Reset(bool useBootRom);
void Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryManager, uint8_t* vram, uint8_t* oam);
GbPpuState GetState();

View file

@ -9,9 +9,12 @@ GbTimer::GbTimer(GbMemoryManager* memoryManager, GbApu* apu)
_apu = apu;
_memoryManager = memoryManager;
_state = {};
_state.TimerDivider = 1024;
//Passes boot_div-dmgABCmgb
//But that test depends on LCD power on timings, so may be wrong.
_divider = 0x06;
_state.Divider = 0x06;
}
GbTimer::~GbTimer()
@ -20,45 +23,45 @@ GbTimer::~GbTimer()
void GbTimer::Exec()
{
_reloaded = false;
if(_needReload) {
_state.Reloaded = false;
if(_state.NeedReload) {
ReloadCounter();
}
SetDivider(_divider + 4);
SetDivider(_state.Divider + 4);
}
void GbTimer::ReloadCounter()
{
_counter = _modulo;
_state.Counter = _state.Modulo;
_memoryManager->RequestIrq(GbIrqSource::Timer);
_needReload = false;
_reloaded = true;
_state.NeedReload = false;
_state.Reloaded = true;
}
void GbTimer::SetDivider(uint16_t newValue)
{
if(_timerEnabled && !(newValue & _timerDivider) && (_divider & _timerDivider)) {
_counter++;
if(_counter == 0) {
_needReload = true;
if(_state.TimerEnabled && !(newValue & _state.TimerDivider) && (_state.Divider & _state.TimerDivider)) {
_state.Counter++;
if(_state.Counter == 0) {
_state.NeedReload = true;
}
}
uint16_t frameSeqBit = _memoryManager->IsHighSpeed() ? 0x2000 : 0x1000;
if(!(newValue & frameSeqBit) && (_divider & frameSeqBit)) {
if(!(newValue & frameSeqBit) && (_state.Divider & frameSeqBit)) {
_apu->ClockFrameSequencer();
}
_divider = newValue;
_state.Divider = newValue;
}
uint8_t GbTimer::Read(uint16_t addr)
{
switch(addr) {
case 0xFF04: return _divider >> 8;
case 0xFF05: return _counter; //FF05 - TIMA - Timer counter (R/W)
case 0xFF06: return _modulo; //FF06 - TMA - Timer Modulo (R/W)
case 0xFF07: return _control | 0xF8; //FF07 - TAC - Timer Control (R/W)
case 0xFF04: return _state.Divider >> 8;
case 0xFF05: return _state.Counter; //FF05 - TIMA - Timer counter (R/W)
case 0xFF06: return _state.Modulo; //FF06 - TMA - Timer Modulo (R/W)
case 0xFF07: return _state.Control | 0xF8; //FF07 - TAC - Timer Control (R/W)
}
return 0;
}
@ -73,29 +76,29 @@ void GbTimer::Write(uint16_t addr, uint8_t value)
case 0xFF05:
//FF05 - TIMA - Timer counter (R/W)
if(_needReload) {
if(_state.NeedReload) {
//Writing to TIMA when a reload is pending will cancel the reload and IRQ request
_needReload = false;
_state.NeedReload = false;
}
if(!_reloaded) {
if(!_state.Reloaded) {
//Writes to TIMA on the cycle TIMA was reloaded with TMA are ignored
_counter = value;
_state.Counter = value;
}
break;
case 0xFF06:
//FF06 - TMA - Timer Modulo (R/W)
_modulo = value;
if(_reloaded) {
_state.Modulo = value;
if(_state.Reloaded) {
//Writing to TMA on the same cycle it was reloaded into TIMA will also update TIMA
_counter = value;
_state.Counter = value;
}
break;
case 0xFF07: {
//FF07 - TAC - Timer Control (R/W)
_control = value;
_state.Control = value;
bool enabled = (value & 0x04) != 0;
uint16_t newDivider = 0;
switch(value & 0x03) {
@ -105,25 +108,25 @@ void GbTimer::Write(uint16_t addr, uint8_t value)
case 3: newDivider = 1 << 7; break;
}
if(_timerEnabled) {
if(_state.TimerEnabled) {
//When changing the value of TAC, the TIMA register can get incremented due to a glitch
bool incrementCounter;
if(enabled) {
incrementCounter = (_divider & _timerDivider) != 0 && (_divider & newDivider) == 0;
incrementCounter = (_state.Divider & _state.TimerDivider) != 0 && (_state.Divider & newDivider) == 0;
} else {
incrementCounter = (_divider & _timerDivider) != 0;
incrementCounter = (_state.Divider & _state.TimerDivider) != 0;
}
if(incrementCounter) {
_counter++;
if(_counter == 0) {
_state.Counter++;
if(_state.Counter == 0) {
ReloadCounter();
}
}
}
_timerEnabled = enabled;
_timerDivider = newDivider;
_state.TimerEnabled = enabled;
_state.TimerDivider = newDivider;
break;
}
}
@ -131,5 +134,5 @@ void GbTimer::Write(uint16_t addr, uint8_t value)
void GbTimer::Serialize(Serializer& s)
{
s.Stream(_divider, _counter, _modulo, _control, _timerEnabled, _timerDivider, _needReload, _reloaded);
s.Stream(_state.Divider, _state.Counter, _state.Modulo, _state.Control, _state.TimerEnabled, _state.TimerDivider, _state.NeedReload, _state.Reloaded);
}

View file

@ -2,6 +2,7 @@
#include "stdafx.h"
#include "../Utilities/ISerializable.h"
#include "../Utilities/Serializer.h"
#include "GbTypes.h"
class GbMemoryManager;
class GbApu;
@ -9,19 +10,9 @@ class GbApu;
class GbTimer : public ISerializable
{
private:
GbMemoryManager* _memoryManager;
GbApu* _apu;
uint16_t _divider = 0;
bool _needReload = false; //Set after TIMA (_counter) overflowed, next cycle will reload TMA into TIMA
bool _reloaded = false; //Set during the cycle on which TIMA is reloaded (affects TMA/TIMA writes)
uint8_t _counter = 0;
uint8_t _modulo = 0;
uint8_t _control = 0;
bool _timerEnabled = false;
uint16_t _timerDivider = 1024;
GbMemoryManager* _memoryManager = nullptr;
GbApu* _apu = nullptr;
GbTimerState _state = {};
void SetDivider(uint16_t value);
void ReloadCounter();
@ -30,6 +21,8 @@ public:
GbTimer(GbMemoryManager* memoryManager, GbApu* apu);
virtual ~GbTimer();
void Reset();
void Exec();
uint8_t Read(uint16_t addr);

View file

@ -179,6 +179,7 @@ struct GbPpuState
uint8_t Status;
uint32_t FrameCount;
bool CgbEnabled;
uint8_t CgbVramBank;
uint8_t CgbBgPalPosition;
@ -204,6 +205,20 @@ struct GbDmaControllerState
bool CgbHdmaMode;
};
struct GbTimerState
{
uint16_t Divider = 0;
bool NeedReload = false; //Set after TIMA (_counter) overflowed, next cycle will reload TMA into TIMA
bool Reloaded = false; //Set during the cycle on which TIMA is reloaded (affects TMA/TIMA writes)
uint8_t Counter = 0;
uint8_t Modulo = 0;
uint8_t Control = 0;
bool TimerEnabled = false;
uint16_t TimerDivider = 1024;
};
struct GbSquareState
{
uint16_t SweepPeriod;
@ -320,6 +335,7 @@ enum class GbMemoryType
PrgRom = (int)SnesMemoryType::GbPrgRom,
WorkRam = (int)SnesMemoryType::GbWorkRam,
CartRam = (int)SnesMemoryType::GbCartRam,
BootRom = (int)SnesMemoryType::GbBootRom,
};
struct GbMemoryManagerState

View file

@ -60,6 +60,7 @@ int64_t LabelManager::GetLabelKey(uint32_t absoluteAddr, SnesMemoryType memType)
case SnesMemoryType::GbWorkRam: return absoluteAddr | ((uint64_t)13 << 32);
case SnesMemoryType::GbCartRam: return absoluteAddr | ((uint64_t)14 << 32);
case SnesMemoryType::GbHighRam: return absoluteAddr | ((uint64_t)15 << 32);
case SnesMemoryType::GbBootRom: return absoluteAddr | ((uint64_t)16 << 32);
default: return -1;
}
}
@ -82,6 +83,7 @@ SnesMemoryType LabelManager::GetKeyMemoryType(uint64_t key)
case ((uint64_t)13 << 32): return SnesMemoryType::GbWorkRam; break;
case ((uint64_t)14 << 32): return SnesMemoryType::GbCartRam; break;
case ((uint64_t)15 << 32): return SnesMemoryType::GbHighRam; break;
case ((uint64_t)16 << 32): return SnesMemoryType::GbBootRom; break;
}
throw std::runtime_error("Invalid label key");

View file

@ -158,6 +158,7 @@ int LuaApi::GetLibrary(lua_State *lua)
lua_pushintvalue(gbCartRam, SnesMemoryType::GbCartRam);
lua_pushintvalue(gbVideoRam, SnesMemoryType::GbVideoRam);
lua_pushintvalue(gbHighRam, SnesMemoryType::GbHighRam);
lua_pushintvalue(gbBootRom, SnesMemoryType::GbBootRom);
lua_settable(lua, -3);
lua_pushliteral(lua, "counterOpType");

View file

@ -69,6 +69,8 @@ void MemoryDumper::SetMemoryState(SnesMemoryType type, uint8_t *buffer, uint32_t
case SnesMemoryType::GbVideoRam:
case SnesMemoryType::GbCartRam:
case SnesMemoryType::GbHighRam:
case SnesMemoryType::GbBootRom:
case SnesMemoryType::GbSpriteRam:
if(_cartridge->GetGameboy()) {
memcpy(_cartridge->GetGameboy()->DebugGetMemory(type), buffer, length);
}
@ -112,6 +114,8 @@ uint32_t MemoryDumper::GetMemorySize(SnesMemoryType type)
case SnesMemoryType::GbVideoRam:
case SnesMemoryType::GbCartRam:
case SnesMemoryType::GbHighRam:
case SnesMemoryType::GbBootRom:
case SnesMemoryType::GbSpriteRam:
return _cartridge->GetGameboy() ? _cartridge->GetGameboy()->DebugGetMemorySize(type) : 0;
}
}
@ -191,6 +195,8 @@ void MemoryDumper::GetMemoryState(SnesMemoryType type, uint8_t *buffer)
case SnesMemoryType::GbVideoRam:
case SnesMemoryType::GbCartRam:
case SnesMemoryType::GbHighRam:
case SnesMemoryType::GbBootRom:
case SnesMemoryType::GbSpriteRam:
if(_cartridge->GetGameboy()) {
memcpy(buffer, _cartridge->GetGameboy()->DebugGetMemory(type), _cartridge->GetGameboy()->DebugGetMemorySize(type));
}
@ -261,6 +267,8 @@ void MemoryDumper::SetMemoryValue(SnesMemoryType memoryType, uint32_t address, u
case SnesMemoryType::GbVideoRam:
case SnesMemoryType::GbCartRam:
case SnesMemoryType::GbHighRam:
case SnesMemoryType::GbBootRom:
case SnesMemoryType::GbSpriteRam:
if(_cartridge->GetGameboy()) {
_cartridge->GetGameboy()->DebugGetMemory(memoryType)[address] = value;
}
@ -309,6 +317,8 @@ uint8_t MemoryDumper::GetMemoryValue(SnesMemoryType memoryType, uint32_t address
case SnesMemoryType::GbVideoRam:
case SnesMemoryType::GbCartRam:
case SnesMemoryType::GbHighRam:
case SnesMemoryType::GbBootRom:
case SnesMemoryType::GbSpriteRam:
return _cartridge->GetGameboy() ? _cartridge->GetGameboy()->DebugGetMemory(memoryType)[address] : 0;
}
}

View file

@ -92,13 +92,13 @@ NecDsp* NecDsp::InitCoprocessor(CoprocessorType type, Console *console, vector<u
vector<uint8_t> programRom;
vector<uint8_t> dataRom;
switch(type) {
case CoprocessorType::DSP1: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "dsp1.rom", "dsp1.program.rom", "dsp1.data.rom", programRom, dataRom, embeddedFirware); break;
case CoprocessorType::DSP1B: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "dsp1b.rom", "dsp1b.program.rom", "dsp1b.data.rom", programRom, dataRom, embeddedFirware); break;
case CoprocessorType::DSP2: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "dsp2.rom", "dsp2.program.rom", "dsp2.data.rom", programRom, dataRom, embeddedFirware); break;
case CoprocessorType::DSP3: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "dsp3.rom", "dsp3.program.rom", "dsp3.data.rom", programRom, dataRom, embeddedFirware); break;
case CoprocessorType::DSP4: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "dsp4.rom", "dsp4.program.rom", "dsp4.data.rom", programRom, dataRom, embeddedFirware); break;
case CoprocessorType::ST010: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "st010.rom", "st010.program.rom", "st010.data.rom", programRom, dataRom, embeddedFirware, 0xC000, 0x1000); break;
case CoprocessorType::ST011: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "st011.rom", "st011.program.rom", "st011.data.rom", programRom, dataRom, embeddedFirware, 0xC000, 0x1000); break;
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;
}

View file

@ -270,6 +270,13 @@ enum class ConsoleRegion
Pal = 2
};
enum class GameboyModel
{
Auto = 0,
Gameboy = 1,
GameboyColor = 2,
};
struct EmulationConfig
{
uint32_t EmulationSpeed = 100;
@ -290,6 +297,9 @@ struct EmulationConfig
RamState RamPowerOnState = RamState::Random;
int64_t BsxCustomDate = -1;
bool GbUseBootRom = false;
GameboyModel GbModel = GameboyModel::Auto;
};
struct PreferencesConfig

View file

@ -28,7 +28,9 @@ enum class SnesMemoryType
GbPrgRom,
GbWorkRam,
GbCartRam,
GbVideoRam,
GbHighRam,
GbBootRom,
GbVideoRam,
GbSpriteRam,
Register
};

View file

@ -29,6 +29,9 @@ namespace Mesen.GUI.Config
public long BsxCustomDate = -1;
[MarshalAs(UnmanagedType.I1)] public bool GbUseBootRom = false;
public GameboyModel GbModel = GameboyModel.Auto;
public void ApplyConfig()
{
ConfigApi.SetEmulationConfig(this);
@ -48,4 +51,11 @@ namespace Mesen.GUI.Config
AllZeros = 1,
AllOnes = 2,
}
public enum GameboyModel
{
Auto = 0,
Gameboy = 1,
GameboyColor = 2,
}
}

View file

@ -135,6 +135,7 @@ namespace Mesen.GUI.Debugger
case SnesMemoryType.GbWorkRam: type = "WRAM"; break;
case SnesMemoryType.GbCartRam: type = "SRAM"; break;
case SnesMemoryType.GbHighRam: type = "HRAM"; break;
case SnesMemoryType.GbBootRom: type = "BOOT"; break;
case SnesMemoryType.Register: type = "REG"; break;
}

View file

@ -89,6 +89,9 @@ namespace Mesen.GUI.Debugger
if(DebugApi.GetMemorySize(SnesMemoryType.GbCartRam) > 0) {
cboBreakpointType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbCartRam));
}
if(DebugApi.GetMemorySize(SnesMemoryType.GbBootRom) > 0) {
cboBreakpointType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbBootRom));
}
}
this.toolTip.SetToolTip(this.picExpressionWarning, "Condition contains invalid syntax or symbols.");

View file

@ -57,7 +57,12 @@ namespace Mesen.GUI.Debugger.Controls
if(DebugApi.GetMemorySize(SnesMemoryType.GbCartRam) > 0) {
this.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbCartRam));
}
if(DebugApi.GetMemorySize(SnesMemoryType.GbBootRom) > 0) {
this.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbBootRom));
}
this.Items.Add("-");
this.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbVideoRam));
this.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbSpriteRam));
}
}

View file

@ -34,6 +34,7 @@ namespace Mesen.GUI.Debugger.Labels
case SnesMemoryType.GbWorkRam: sb.Append("GBWRAM:"); break;
case SnesMemoryType.GbCartRam: sb.Append("GBSRAM:"); break;
case SnesMemoryType.GbHighRam: sb.Append("GBHRAM:"); break;
case SnesMemoryType.GbBootRom: sb.Append("GBBOOT:"); break;
}
sb.Append(Address.ToString("X4"));
@ -74,6 +75,7 @@ namespace Mesen.GUI.Debugger.Labels
case "GBWRAM": type = SnesMemoryType.GsuWorkRam; break;
case "GBSRAM": type = SnesMemoryType.GbCartRam; break;
case "GBHRAM": type = SnesMemoryType.GbHighRam; break;
case "GBBOOT": type = SnesMemoryType.GbBootRom; break;
default: return null;
}

View file

@ -92,6 +92,7 @@ namespace Mesen.GUI.Debugger.Labels
case SnesMemoryType.GbWorkRam: return address | ((ulong)13 << 32);
case SnesMemoryType.GbCartRam: return address | ((ulong)14 << 32);
case SnesMemoryType.GbHighRam: return address | ((ulong)15 << 32);
case SnesMemoryType.GbBootRom: return address | ((ulong)16 << 32);
}
throw new Exception("Invalid type");
}

View file

@ -141,6 +141,7 @@ namespace Mesen.GUI.Debugger.Controls
case SnesMemoryType.GbWorkRam: prefix = "WRAM: $"; break;
case SnesMemoryType.GbCartRam: prefix = "SRAM: $"; break;
case SnesMemoryType.GbHighRam: prefix = "HRAM: $"; break;
case SnesMemoryType.GbBootRom: prefix = "BOOT: $"; break;
default: throw new Exception("Unsupported type");
}
int relAddress = label.GetRelativeAddress(_cpuType).Address;

View file

@ -64,6 +64,9 @@ namespace Mesen.GUI.Debugger
if(DebugApi.GetMemorySize(SnesMemoryType.GbCartRam) > 0) {
cboType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbCartRam));
}
if(DebugApi.GetMemorySize(SnesMemoryType.GbBootRom) > 0) {
cboType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbBootRom));
}
}
}

View file

@ -124,6 +124,7 @@ namespace Mesen.GUI.Debugger.Controls
case SnesMemoryType.GbWorkRam: functionName = "WRAM: $"; break;
case SnesMemoryType.GbCartRam: functionName = "SRAM: $"; break;
case SnesMemoryType.GbHighRam: functionName = "HRAM: $"; break;
case SnesMemoryType.GbBootRom: functionName = "BOOT: $"; break;
default: throw new Exception("Unsupported type");
}

View file

@ -960,10 +960,17 @@
<Value ID="GbPrgRom">GB - PRG ROM</Value>
<Value ID="GbWorkRam">GB - Work RAM</Value>
<Value ID="GbVideoRam">GB - Video RAM</Value>
<Value ID="GbSpriteRam">GB - Sprite RAM</Value>
<Value ID="GbCartRam">GB - Cart RAM</Value>
<Value ID="GbHighRam">GB - High RAM</Value>
<Value ID="GbBootRom">GB - Boot ROM</Value>
<Value ID="Register">Register</Value>
</Enum>
<Enum ID="GameboyModel">
<Value ID="Auto">Auto</Value>
<Value ID="Gameboy">Game Boy</Value>
<Value ID="GameboyColor">Game Boy Color</Value>
</Enum>
<Enum ID="TileFormat">
<Value ID="Bpp2">2 bpp</Value>
<Value ID="Bpp4">4 bpp</Value>

View file

@ -75,6 +75,11 @@
this.radBsxLocalTime = new System.Windows.Forms.RadioButton();
this.radBsxCustomTime = new System.Windows.Forms.RadioButton();
this.dtpBsxCustomTime = new System.Windows.Forms.DateTimePicker();
this.tpgGameboy = new System.Windows.Forms.TabPage();
this.tableLayoutPanel7 = new System.Windows.Forms.TableLayoutPanel();
this.lblGameboy = new System.Windows.Forms.Label();
this.cboGameboyModel = new System.Windows.Forms.ComboBox();
this.chkUseBootRom = new System.Windows.Forms.CheckBox();
this.tabMain.SuspendLayout();
this.tpgGeneral.SuspendLayout();
this.tableLayoutPanel4.SuspendLayout();
@ -93,6 +98,8 @@
this.tpgBsx.SuspendLayout();
this.grpBsxDateTime.SuspendLayout();
this.tableLayoutPanel6.SuspendLayout();
this.tpgGameboy.SuspendLayout();
this.tableLayoutPanel7.SuspendLayout();
this.SuspendLayout();
//
// baseConfigPanel
@ -104,9 +111,10 @@
// tabMain
//
this.tabMain.Controls.Add(this.tpgGeneral);
this.tabMain.Controls.Add(this.tpgGameboy);
this.tabMain.Controls.Add(this.tpgBsx);
this.tabMain.Controls.Add(this.tpgAdvanced);
this.tabMain.Controls.Add(this.tpgOverclocking);
this.tabMain.Controls.Add(this.tpgBsx);
this.tabMain.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabMain.Location = new System.Drawing.Point(0, 0);
this.tabMain.Name = "tabMain";
@ -814,6 +822,64 @@
this.dtpBsxCustomTime.Value = new System.DateTime(2000, 1, 1, 0, 0, 0, 0);
this.dtpBsxCustomTime.Enter += new System.EventHandler(this.dtpBsxCustomTime_Enter);
//
// tpgGameboy
//
this.tpgGameboy.Controls.Add(this.tableLayoutPanel7);
this.tpgGameboy.Location = new System.Drawing.Point(4, 22);
this.tpgGameboy.Name = "tpgGameboy";
this.tpgGameboy.Padding = new System.Windows.Forms.Padding(3);
this.tpgGameboy.Size = new System.Drawing.Size(437, 264);
this.tpgGameboy.TabIndex = 6;
this.tpgGameboy.Text = "Game Boy";
this.tpgGameboy.UseVisualStyleBackColor = true;
//
// tableLayoutPanel7
//
this.tableLayoutPanel7.ColumnCount = 2;
this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel7.Controls.Add(this.lblGameboy, 0, 0);
this.tableLayoutPanel7.Controls.Add(this.cboGameboyModel, 1, 0);
this.tableLayoutPanel7.Controls.Add(this.chkUseBootRom, 0, 1);
this.tableLayoutPanel7.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel7.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel7.Name = "tableLayoutPanel7";
this.tableLayoutPanel7.RowCount = 3;
this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel7.Size = new System.Drawing.Size(431, 258);
this.tableLayoutPanel7.TabIndex = 0;
//
// lblGameboy
//
this.lblGameboy.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblGameboy.AutoSize = true;
this.lblGameboy.Location = new System.Drawing.Point(3, 7);
this.lblGameboy.Name = "lblGameboy";
this.lblGameboy.Size = new System.Drawing.Size(91, 13);
this.lblGameboy.TabIndex = 0;
this.lblGameboy.Text = "Game Boy Model:";
//
// cboGameboyModel
//
this.cboGameboyModel.FormattingEnabled = true;
this.cboGameboyModel.Location = new System.Drawing.Point(100, 3);
this.cboGameboyModel.Name = "cboGameboyModel";
this.cboGameboyModel.Size = new System.Drawing.Size(119, 21);
this.cboGameboyModel.TabIndex = 1;
//
// chkUseBootRom
//
this.chkUseBootRom.AutoSize = true;
this.tableLayoutPanel7.SetColumnSpan(this.chkUseBootRom, 2);
this.chkUseBootRom.Location = new System.Drawing.Point(3, 30);
this.chkUseBootRom.Name = "chkUseBootRom";
this.chkUseBootRom.Size = new System.Drawing.Size(89, 17);
this.chkUseBootRom.TabIndex = 2;
this.chkUseBootRom.Text = "Use boot rom";
this.chkUseBootRom.UseVisualStyleBackColor = true;
//
// frmEmulationConfig
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -857,6 +923,9 @@
this.grpBsxDateTime.ResumeLayout(false);
this.tableLayoutPanel6.ResumeLayout(false);
this.tableLayoutPanel6.PerformLayout();
this.tpgGameboy.ResumeLayout(false);
this.tableLayoutPanel7.ResumeLayout(false);
this.tableLayoutPanel7.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
@ -911,5 +980,10 @@
private System.Windows.Forms.RadioButton radBsxCustomTime;
private System.Windows.Forms.DateTimePicker dtpBsxCustomTime;
private System.Windows.Forms.DateTimePicker dtpBsxCustomDate;
private System.Windows.Forms.TabPage tpgGameboy;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel7;
private System.Windows.Forms.Label lblGameboy;
private System.Windows.Forms.ComboBox cboGameboyModel;
private System.Windows.Forms.CheckBox chkUseBootRom;
}
}

View file

@ -37,6 +37,9 @@ namespace Mesen.GUI.Forms.Config
AddBinding(nameof(EmulationConfig.PpuExtraScanlinesBeforeNmi), nudExtraScanlinesBeforeNmi);
AddBinding(nameof(EmulationConfig.PpuExtraScanlinesAfterNmi), nudExtraScanlinesAfterNmi);
AddBinding(nameof(EmulationConfig.GsuClockSpeed), nudGsuClockSpeed);
AddBinding(nameof(EmulationConfig.GbModel), cboGameboyModel);
AddBinding(nameof(EmulationConfig.GbUseBootRom), chkUseBootRom);
long customDate = ConfigManager.Config.Emulation.BsxCustomDate;
if(customDate >= 0) {

View file

@ -213,8 +213,10 @@ namespace Mesen.GUI
GbPrgRom,
GbWorkRam,
GbCartRam,
GbVideoRam,
GbHighRam,
GbBootRom,
GbVideoRam,
GbSpriteRam,
Register,
}
@ -245,6 +247,7 @@ namespace Mesen.GUI
case SnesMemoryType.GbWorkRam:
case SnesMemoryType.GbCartRam:
case SnesMemoryType.GbHighRam:
case SnesMemoryType.GbBootRom:
return CpuType.Gameboy;
default:
@ -281,6 +284,7 @@ namespace Mesen.GUI
case SnesMemoryType.GbWorkRam:
case SnesMemoryType.GbCartRam:
case SnesMemoryType.GbHighRam:
case SnesMemoryType.GbBootRom:
return true;
}

View file

@ -736,6 +736,7 @@ namespace Mesen.GUI
public byte Status;
public UInt32 FrameCount;
[MarshalAs(UnmanagedType.I1)] public bool CgbEnabled;
public byte CgbVramBank;
public byte CgbBgPalPosition;

View file

@ -208,10 +208,26 @@ namespace Mesen.GUI
Gameboy
}
public enum FirmwareType
{
CX4,
DSP1,
DSP1B,
DSP2,
DSP3,
DSP4,
ST010,
ST011,
ST018,
Satellaview,
Gameboy,
GameboyColor
}
public struct MissingFirmwareMessage
{
public IntPtr Filename;
public CoprocessorType FirmwareType;
public FirmwareType Firmware;
public UInt32 Size;
}
}

View file

@ -28,38 +28,41 @@ namespace Mesen.GUI.Utilities
}
}
private static List<string> GetExpectedHashes(CoprocessorType type)
private static List<string> GetExpectedHashes(FirmwareType type)
{
switch(type) {
case CoprocessorType.CX4: return new List<string>() { "AE8D4D1961B93421FF00B3CAA1D0F0CE7783E749772A3369C36B3DBF0D37EF18" };
case CoprocessorType.DSP1: return new List<string>() { "91E87D11E1C30D172556BED2211CCE2EFA94BA595F58C5D264809EF4D363A97B" };
case CoprocessorType.DSP1B: return new List<string>() { "D789CB3C36B05C0B23B6C6F23BE7AA37C6E78B6EE9CEAC8D2D2AA9D8C4D35FA9" };
case CoprocessorType.DSP2: return new List<string>() { "03EF4EF26C9F701346708CB5D07847B5203CF1B0818BF2930ACD34510FFDD717" };
case CoprocessorType.DSP3: return new List<string>() { "0971B08F396C32E61989D1067DDDF8E4B14649D548B2188F7C541B03D7C69E4E" };
case CoprocessorType.DSP4: return new List<string>() { "752D03B2D74441E430B7F713001FA241F8BBCFC1A0D890ED4143F174DBE031DA" };
case CoprocessorType.ST010: return new List<string>() { "FA9BCED838FEDEA11C6F6ACE33D1878024BDD0D02CC9485899D0BDD4015EC24C" };
case CoprocessorType.ST011: return new List<string>() { "8B2B3F3F3E6E29F4D21D8BC736B400BC988B7D2214EBEE15643F01C1FEE2F364" };
case CoprocessorType.ST018: return new List<string>() { "6DF209AB5D2524D1839C038BE400AE5EB20DAFC14A3771A3239CD9E8ACD53806" };
case CoprocessorType.Satellaview: return new List<string>() {
case FirmwareType.CX4: return new List<string>() { "AE8D4D1961B93421FF00B3CAA1D0F0CE7783E749772A3369C36B3DBF0D37EF18" };
case FirmwareType.DSP1: return new List<string>() { "91E87D11E1C30D172556BED2211CCE2EFA94BA595F58C5D264809EF4D363A97B" };
case FirmwareType.DSP1B: return new List<string>() { "D789CB3C36B05C0B23B6C6F23BE7AA37C6E78B6EE9CEAC8D2D2AA9D8C4D35FA9" };
case FirmwareType.DSP2: return new List<string>() { "03EF4EF26C9F701346708CB5D07847B5203CF1B0818BF2930ACD34510FFDD717" };
case FirmwareType.DSP3: return new List<string>() { "0971B08F396C32E61989D1067DDDF8E4B14649D548B2188F7C541B03D7C69E4E" };
case FirmwareType.DSP4: return new List<string>() { "752D03B2D74441E430B7F713001FA241F8BBCFC1A0D890ED4143F174DBE031DA" };
case FirmwareType.ST010: return new List<string>() { "FA9BCED838FEDEA11C6F6ACE33D1878024BDD0D02CC9485899D0BDD4015EC24C" };
case FirmwareType.ST011: return new List<string>() { "8B2B3F3F3E6E29F4D21D8BC736B400BC988B7D2214EBEE15643F01C1FEE2F364" };
case FirmwareType.ST018: return new List<string>() { "6DF209AB5D2524D1839C038BE400AE5EB20DAFC14A3771A3239CD9E8ACD53806" };
case FirmwareType.Satellaview: return new List<string>() {
"27CFDB99F7E4252BF3740D420147B63C4C88616883BC5E7FE43F2F30BF8C8CBB", //Japan, no DRM
"A49827B45FF9AC9CF5B4658190E1428E59251BC82D8A63D8E9E0F71E439F008F", //English, no DRM
"3CE321496EDC5D77038DE2034EB3FB354D7724AFD0BC7FD0319F3EB5D57B984D", //Japan, original
"77D94D64D745014BF8B51280A4204056CDEB9D41EA30EAE80DBC006675BEBEF8", //English, DRM
};
case FirmwareType.Gameboy: return new List<string>() { "CF053ECCB4CCAFFF9E67339D4E78E98DCE7D1ED59BE819D2A1BA2232C6FCE1C7", "26E71CF01E301E5DC40E987CD2ECBF6D0276245890AC829DB2A25323DA86818E" };
case FirmwareType.GameboyColor: return new List<string>() { "B4F2E416A35EEF52CBA161B159C7C8523A92594FACB924B3EDE0D722867C50C7", "3A307A41689BEE99A9A32EA021BF45136906C86B2E4F06C806738398E4F92E45" };
}
throw new Exception("Unexpected coprocessor type");
throw new Exception("Unexpected firmware type");
}
public static void RequestFirmwareFile(MissingFirmwareMessage msg)
{
string filename = Utf8Marshaler.GetStringFromIntPtr(msg.Filename);
if(MesenMsgBox.Show("FirmwareNotFound", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, msg.FirmwareType.ToString(), filename, msg.Size.ToString()) == DialogResult.OK) {
if(MesenMsgBox.Show("FirmwareNotFound", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, msg.Firmware.ToString(), filename, msg.Size.ToString()) == DialogResult.OK) {
using(OpenFileDialog ofd = new OpenFileDialog()) {
ofd.SetFilter(ResourceHelper.GetMessage("FilterAll"));
if(ofd.ShowDialog(frmMain.Instance) == DialogResult.OK) {
List<string> expectedHashes = GetExpectedHashes(msg.FirmwareType);
List<string> expectedHashes = GetExpectedHashes(msg.Firmware);
if(!expectedHashes.Contains(GetFileHash(ofd.FileName))) {
if(MesenMsgBox.Show("FirmwareMismatch", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning, msg.FirmwareType.ToString(), GetFileHash(ofd.FileName), expectedHashes[0]) != DialogResult.OK) {
if(MesenMsgBox.Show("FirmwareMismatch", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning, msg.Firmware.ToString(), GetFileHash(ofd.FileName), expectedHashes[0]) != DialogResult.OK) {
//Files don't match and user cancelled the action
return;
}