SGB: Added SGB2 support

+ Change name for SGB bios file
This commit is contained in:
Sour 2020-06-18 22:22:46 -04:00
parent d204485153
commit 6592805fc0
16 changed files with 125 additions and 42 deletions

View file

@ -271,6 +271,8 @@ CoprocessorType BaseCartridge::GetCoprocessorType()
}
break;
}
} else if(GetGameCode() == "042J") {
return CoprocessorType::SGB;
}
return CoprocessorType::None;
@ -629,7 +631,8 @@ bool BaseCartridge::LoadGameboy(VirtualFile &romFile)
_headerOffset = Gameboy::HeaderOffset;
if(_gameboy->IsSgb()) {
if(!FirmwareHelper::LoadSgbFirmware(_console, &_prgRom, _prgRomSize)) {
EmulationConfig cfg = _console->GetSettings()->GetEmulationConfig();
if(!FirmwareHelper::LoadSgbFirmware(_console, &_prgRom, _prgRomSize, cfg.UseSgb2)) {
return false;
}
LoadRom();

View file

@ -64,7 +64,8 @@ enum class FirmwareType
Gameboy,
GameboyColor,
SgbGameboyCpu,
SGB
SGB1,
SGB2
};
struct RomInfo

View file

@ -107,20 +107,21 @@ public:
return false;
}
static bool LoadSgbFirmware(Console* console, uint8_t** prgRom, uint32_t& prgSize)
static bool LoadSgbFirmware(Console* console, uint8_t** prgRom, uint32_t& prgSize, bool useSgb2)
{
prgSize = 0x40000;
if(AttemptLoadFirmware(prgRom, "SgbBios.sfc", prgSize)) {
string filename = useSgb2 ? "SGB2.sfc" : "SGB1.sfc";
prgSize = useSgb2 ? 0x80000 : 0x40000;
if(AttemptLoadFirmware(prgRom, filename, prgSize)) {
return true;
}
MissingFirmwareMessage msg;
msg.Filename = "SgbBios.sfc";
msg.Firmware = FirmwareType::SGB;
msg.Filename = filename.c_str();
msg.Firmware = useSgb2 ? FirmwareType::SGB2 : FirmwareType::SGB1;
msg.Size = prgSize;
console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg);
if(AttemptLoadFirmware(prgRom, "SgbBios.sfc", prgSize)) {
if(AttemptLoadFirmware(prgRom, filename, prgSize)) {
return true;
}

View file

@ -15,7 +15,6 @@
#include "EmuSettings.h"
#include "MessageManager.h"
#include "FirmwareHelper.h"
#include "SuperGameboy.h"
#include "GbBootRom.h"
#include "../Utilities/VirtualFile.h"
#include "../Utilities/Serializer.h"
@ -100,7 +99,11 @@ Gameboy* Gameboy::Create(Console* console, VirtualFile &romFile)
case GameboyModel::SuperGameboy:
gb->_bootRom = new uint8_t[gb->_bootRomSize];
memcpy(gb->_bootRom, sgbBootRom, gb->_bootRomSize);
if(cfg.UseSgb2) {
memcpy(gb->_bootRom, sgb2BootRom, gb->_bootRomSize);
} else {
memcpy(gb->_bootRom, sgbBootRom, gb->_bootRomSize);
}
break;
}
}
@ -139,6 +142,8 @@ Gameboy::~Gameboy()
void Gameboy::PowerOn(SuperGameboy* superGameboy)
{
_superGameboy = superGameboy;
shared_ptr<EmuSettings> settings = _console->GetSettings();
settings->InitializeRam(_cartRam, _cartRamSize);
settings->InitializeRam(_workRam, _workRamSize);
@ -158,8 +163,6 @@ void Gameboy::PowerOn(SuperGameboy* superGameboy)
_cpu->Init(_console, this, _memoryManager.get());
_ppu->Init(_console, this, _memoryManager.get(), _dmaController.get(), _videoRam, _spriteRam);
_dmaController->Init(_memoryManager.get(), _ppu.get(), _cpu.get());
_superGameboy = superGameboy;
}
void Gameboy::Exec()
@ -167,15 +170,9 @@ void Gameboy::Exec()
_cpu->Exec();
}
void Gameboy::Run(uint64_t masterClock)
void Gameboy::Run(uint64_t runUntilClock)
{
if(!(_superGameboy->GetControl() & 0x80)) {
return;
}
//TODO support SGB2 timings
masterClock = (masterClock - _superGameboy->GetResetClock()) / 5;
while(_memoryManager->GetCycleCount() < masterClock) {
while(_memoryManager->GetCycleCount() < runUntilClock) {
_cpu->Exec();
}
}

View file

@ -66,7 +66,7 @@ public:
void PowerOn(SuperGameboy* superGameboy = nullptr);
void Exec();
void Run(uint64_t masterClock);
void Run(uint64_t runUntilClock);
void LoadBattery();
void SaveBattery();

View file

@ -4,6 +4,7 @@
#include "Gameboy.h"
#include "SoundMixer.h"
#include "EmuSettings.h"
#include "SuperGameboy.h"
#include "../Utilities/Serializer.h"
GbApu::GbApu()
@ -36,8 +37,8 @@ void GbApu::Init(Console* console, Gameboy* gameboy)
blip_clear(_rightChannel);
if(_gameboy->IsSgb()) {
blip_set_rates(_leftChannel, _console->GetMasterClockRate() / 5, GbApu::SampleRate);
blip_set_rates(_rightChannel, _console->GetMasterClockRate() / 5, GbApu::SampleRate);
blip_set_rates(_leftChannel, _gameboy->GetSgb()->GetClockRate(), GbApu::SampleRate);
blip_set_rates(_rightChannel, _gameboy->GetSgb()->GetClockRate(), GbApu::SampleRate);
} else {
blip_set_rates(_leftChannel, GbApu::ApuFrequency, GbApu::SampleRate);
blip_set_rates(_rightChannel, GbApu::ApuFrequency, GbApu::SampleRate);

View file

@ -54,6 +54,32 @@ uint8_t sgbBootRom[256] = {
0x00, 0x00, 0xE0, 0x50
};
uint8_t sgb2BootRom[256] = {
0x31, 0xFE, 0xFF, 0x21, 0x00, 0x80, 0x22, 0xCB, 0x6C, 0x28, 0xFB, 0x3E,
0x80, 0xE0, 0x26, 0xE0, 0x11, 0x3E, 0xF3, 0xE0, 0x12, 0xE0, 0x25, 0x3E,
0x77, 0xE0, 0x24, 0x3E, 0x00, 0xE0, 0x47, 0x11, 0x04, 0x01, 0x21, 0x10,
0x80, 0x1A, 0x47, 0xCD, 0xC9, 0x00, 0xCD, 0xC9, 0x00, 0x13, 0x7B, 0xEE,
0x34, 0x20, 0xF2, 0x11, 0xEA, 0x00, 0x0E, 0x08, 0x1A, 0x13, 0x22, 0x23,
0x0D, 0x20, 0xF9, 0x3E, 0x19, 0xEA, 0x10, 0x99, 0x21, 0x2F, 0x99, 0x0E,
0x0C, 0x3D, 0x28, 0x08, 0x32, 0x0D, 0x20, 0xF9, 0x2E, 0x0F, 0x18, 0xF5,
0x3E, 0x91, 0xE0, 0x40, 0x3E, 0xF1, 0xE0, 0x80, 0x21, 0x04, 0x01, 0xAF,
0x4F, 0xAF, 0xE2, 0x3E, 0x30, 0xE2, 0xF0, 0x80, 0xCD, 0xB7, 0x00, 0xE5,
0x06, 0x0E, 0x16, 0x00, 0xCD, 0xAD, 0x00, 0x82, 0x57, 0x05, 0x20, 0xF8,
0xCD, 0xB7, 0x00, 0xE1, 0x06, 0x0E, 0xCD, 0xAD, 0x00, 0xCD, 0xB7, 0x00,
0x05, 0x20, 0xF7, 0x3E, 0x20, 0xE2, 0x3E, 0x30, 0xE2, 0xF0, 0x80, 0xC6,
0x02, 0xE0, 0x80, 0x3E, 0x58, 0xBD, 0x20, 0xC9, 0x0E, 0x13, 0x3E, 0xC1,
0xE2, 0x0C, 0x3E, 0x07, 0xE2, 0x3E, 0xFC, 0xE0, 0x47, 0x3E, 0xFF, 0x21,
0x60, 0xC0, 0xC3, 0xFE, 0x00, 0x3E, 0x4F, 0xBD, 0x38, 0x02, 0x2A, 0xC9,
0x23, 0xAF, 0xC9, 0x5F, 0x16, 0x08, 0x3E, 0x10, 0xCB, 0x1B, 0x38, 0x01,
0x87, 0xE2, 0x3E, 0x30, 0xE2, 0x15, 0xC8, 0x18, 0xF1, 0x3E, 0x04, 0x0E,
0x00, 0xCB, 0x20, 0xF5, 0xCB, 0x11, 0xF1, 0xCB, 0x11, 0x3D, 0x20, 0xF5,
0x79, 0x22, 0x23, 0x22, 0x23, 0xC9, 0xE5, 0x21, 0x0F, 0xFF, 0xCB, 0x86,
0xCB, 0x46, 0x28, 0xFC, 0xE1, 0xC9, 0x3C, 0x42, 0xB9, 0xA5, 0xB9, 0xA5,
0x42, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xE0, 0x50
};
uint8_t cgbBootRom[2304] = {
0x31, 0xFE, 0xFF, 0xCD, 0x26, 0x06, 0x3E, 0x02, 0x0E, 0x70, 0xE2, 0x26,
0xD0, 0xCD, 0x29, 0x06, 0xE2, 0x26, 0xFE, 0x0E, 0xA0, 0x22, 0x0D, 0x20,

View file

@ -300,6 +300,7 @@ struct EmulationConfig
int64_t BsxCustomDate = -1;
GameboyModel GbModel = GameboyModel::Auto;
bool UseSgb2 = true;
};
struct PreferencesConfig

View file

@ -2,6 +2,7 @@
#include "SuperGameboy.h"
#include "Console.h"
#include "MemoryManager.h"
#include "EmuSettings.h"
#include "BaseCartridge.h"
#include "Gameboy.h"
#include "GbApu.h"
@ -12,20 +13,25 @@
SuperGameboy::SuperGameboy(Console* console) : BaseCoprocessor(SnesMemoryType::Register)
{
_mixBuffer = new int16_t[0x10000];
_console = console;
_memoryManager = console->GetMemoryManager().get();
_cart = _console->GetCartridge().get();
_gameboy = _cart->GetGameboy();
_gameboy->PowerOn(this);
_ppu = _gameboy->GetPpu();
_mixBuffer = new int16_t[0x10000];
_control = 0x01; //Divider = 5, gameboy = not running
UpdateClockRatio();
MemoryMappings* cpuMappings = _memoryManager->GetMemoryMappings();
for(int i = 0; i <= 0x3F; i++) {
cpuMappings->RegisterHandler(i, i, 0x6000, 0x7FFF, this);
cpuMappings->RegisterHandler(i + 0x80, i + 0x80, 0x6000, 0x7FFF, this);
}
_gameboy->PowerOn(this);
}
SuperGameboy::~SuperGameboy()
@ -109,6 +115,8 @@ void SuperGameboy::Write(uint32_t addr, uint8_t value)
}
_control = value;
_inputIndex %= GetPlayerCount();
UpdateClockRatio();
break;
}
@ -119,11 +127,6 @@ void SuperGameboy::Write(uint32_t addr, uint8_t value)
}
}
void SuperGameboy::Run()
{
_gameboy->Run(_memoryManager->GetMasterClock());
}
void SuperGameboy::ProcessInputPortWrite(uint8_t value)
{
if(_inputValue == value) {
@ -242,14 +245,36 @@ void SuperGameboy::MixAudio(uint32_t targetRate, int16_t* soundSamples, uint32_t
}
}
uint8_t SuperGameboy::GetControl()
void SuperGameboy::Run()
{
return _control;
if(!(_control & 0x80)) {
return;
}
_gameboy->Run((uint64_t)((_memoryManager->GetMasterClock() - _resetClock) * _clockRatio));
}
uint64_t SuperGameboy::GetResetClock()
void SuperGameboy::UpdateClockRatio()
{
return _resetClock;
bool isSgb2 = _console->GetSettings()->GetEmulationConfig().UseSgb2;
uint32_t masterRate = isSgb2 ? 20971520 : _console->GetMasterClockRate();
uint8_t divider = 5;
//TODO: This doesn't actually work properly if the speed is changed while the SGB is running (but this most likely never happens?)
switch(_control & 0x03) {
case 0: divider = 4; break;
case 1: divider = 5; break;
case 2: divider = 7; break;
case 3: divider = 9; break;
}
double effectiveRate = (double)masterRate / divider;
_clockRatio = effectiveRate / _console->GetMasterClockRate();
}
uint32_t SuperGameboy::GetClockRate()
{
return (uint32_t)(_console->GetMasterClockRate() * _clockRatio);
}
uint8_t SuperGameboy::GetInputIndex()
@ -281,7 +306,7 @@ void SuperGameboy::Serialize(Serializer& s)
{
s.Stream(
_control, _resetClock, _input[0], _input[1], _input[2], _input[3], _inputIndex, _listeningForPacket, _packetReady,
_inputWriteClock, _inputValue, _packetByte, _packetBit, _lcdRowSelect, _readPosition, _waitForHigh
_inputWriteClock, _inputValue, _packetByte, _packetBit, _lcdRowSelect, _readPosition, _waitForHigh, _clockRatio
);
s.StreamArray(_packetData, 16);

View file

@ -20,6 +20,7 @@ private:
uint8_t _control = 0;
uint64_t _resetClock = 0;
double _clockRatio = 0;
uint8_t _input[4] = {};
uint8_t _inputIndex = 0;
@ -62,8 +63,9 @@ public:
void MixAudio(uint32_t targetRate, int16_t* soundSamples, uint32_t sampleCount);
uint8_t GetControl();
uint64_t GetResetClock();
void UpdateClockRatio();
uint32_t GetClockRate();
uint8_t GetInputIndex();
uint8_t GetInput();

View file

@ -52,6 +52,7 @@ static constexpr const char* MesenOverclock = "mesen-s_overclock";
static constexpr const char* MesenOverclockType = "mesen-s_overclock_type";
static constexpr const char* MesenSuperFxOverclock = "mesen-s_superfx_overclock";
static constexpr const char* MesenGbModel = "mesen-s_gbmodel";
static constexpr const char* MesenGbSgb2 = "mesen-s_sgb2";
extern "C" {
void logMessage(retro_log_level level, const char* message)
@ -115,6 +116,7 @@ extern "C" {
{ MesenNtscFilter, "NTSC filter; Disabled|Composite (Blargg)|S-Video (Blargg)|RGB (Blargg)|Monochrome (Blargg)" },
{ MesenRegion, "Region; Auto|NTSC|PAL" },
{ MesenGbModel, "Game Boy Model; Auto|Game Boy|Game Boy Color|Super Game Boy" },
{ MesenGbSgb2, "Use SGB2; enabled|disabled" },
{ MesenOverscanVertical, "Vertical Overscan; None|8px|16px" },
{ MesenOverscanHorizontal, "Horizontal Overscan; None|8px|16px" },
{ MesenAspectRatio, "Aspect Ratio; Auto|No Stretching|NTSC|PAL|4:3|16:9" },
@ -404,6 +406,11 @@ extern "C" {
}
}
if(readVariable(MesenGbSgb2, var)) {
string value = string(var.value);
emulation.UseSgb2 = (value == "enabled");
}
auto getKeyCode = [=](int port, int retroKey) {
return (port << 8) | (retroKey + 1);
};

View file

@ -30,6 +30,7 @@ namespace Mesen.GUI.Config
public long BsxCustomDate = -1;
public GameboyModel GbModel = GameboyModel.Auto;
[MarshalAs(UnmanagedType.I1)] public bool UseSgb2 = true;
public void ApplyConfig()
{

View file

@ -79,6 +79,7 @@
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.nudGsuClockSpeed = new Mesen.GUI.Controls.MesenNumericUpDown();
this.lblGsuClockSpeed = new System.Windows.Forms.Label();
this.chkUseSgb2 = new System.Windows.Forms.CheckBox();
this.tabMain.SuspendLayout();
this.tpgGeneral.SuspendLayout();
this.tableLayoutPanel4.SuspendLayout();
@ -450,13 +451,14 @@
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.chkUseSgb2, 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 = 2;
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.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel7.Size = new System.Drawing.Size(431, 258);
this.tableLayoutPanel7.TabIndex = 0;
//
@ -867,6 +869,17 @@
this.lblGsuClockSpeed.TabIndex = 0;
this.lblGsuClockSpeed.Text = "Super FX clock speed (%):";
//
// chkUseSgb2
//
this.chkUseSgb2.AutoSize = true;
this.tableLayoutPanel7.SetColumnSpan(this.chkUseSgb2, 2);
this.chkUseSgb2.Location = new System.Drawing.Point(3, 30);
this.chkUseSgb2.Name = "chkUseSgb2";
this.chkUseSgb2.Size = new System.Drawing.Size(237, 17);
this.chkUseSgb2.TabIndex = 2;
this.chkUseSgb2.Text = "Use Super Game Boy 2 timings and behavior";
this.chkUseSgb2.UseVisualStyleBackColor = true;
//
// frmEmulationConfig
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -971,5 +984,6 @@
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel7;
private System.Windows.Forms.Label lblGameboy;
private System.Windows.Forms.ComboBox cboGameboyModel;
private System.Windows.Forms.CheckBox chkUseSgb2;
}
}

View file

@ -39,6 +39,7 @@ namespace Mesen.GUI.Forms.Config
AddBinding(nameof(EmulationConfig.GsuClockSpeed), nudGsuClockSpeed);
AddBinding(nameof(EmulationConfig.GbModel), cboGameboyModel);
AddBinding(nameof(EmulationConfig.UseSgb2), chkUseSgb2);
long customDate = ConfigManager.Config.Emulation.BsxCustomDate;
if(customDate >= 0) {

View file

@ -251,7 +251,8 @@ namespace Mesen.GUI
Gameboy,
GameboyColor,
SgbGameboyCpu,
SGB
SGB1,
SGB2,
}
public struct MissingFirmwareMessage

View file

@ -52,13 +52,15 @@ namespace Mesen.GUI.Utilities
"0E4DDFF32FC9D1EEAAE812A157DD246459B00C9E14F2F61751F661F32361E360", //SGB1
"FD243C4FB27008986316CE3DF29E9CFBCDC0CD52704970555A8BB76EDBEC3988" //SGB2
};
case FirmwareType.SGB: return new List<string>() {
case FirmwareType.SGB1: return new List<string>() {
"BBA9C269273BEDB9B38BD5EB23BFAA6E509B8DECC7CB80BB5513905AF04F4CEB", //Rev 0 (Japan)
"C6C4DAAB5C899B69900C460787DE6089EDABE94B760F96D9F583D30CC0A5BB30", //Rev 1 (Japan+USA)
"A75160F7B89B1F0E20FD2F6441BB86285C7378DB5035EF6885485EAFF6059376", //Rev 2 (World)
};
case FirmwareType.SGB2:
return new List<string>() {
"C172498A23D1176672931BAB33B629C7D28F914A43DCA9E540B8AF1B37CCF2C6", //SGB2 (Japan)
};
}
throw new Exception("Unexpected firmware type");
}