Add "No VRAM Blocking" and "Echo Shadow RAM" options:
These allow compatibility with very old ROM hacks that
only previously ran in ZSNES and earlier Snes9X releases.
This commit is contained in:
byuu 2019-09-02 13:51:04 +09:00
parent 03aaaba889
commit 08e5e81609
14 changed files with 57 additions and 9 deletions

View File

@ -29,7 +29,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "bsnes"; static const string Name = "bsnes";
static const string Version = "108.14"; static const string Version = "108.15";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "https://byuu.org"; static const string Website = "https://byuu.org";

View File

@ -612,7 +612,7 @@ VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); }
//// Echo //// Echo
// Current echo buffer pointer for left/right channel // Current echo buffer pointer for left/right channel
#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2]) #define ECHO_PTR( ch ) (&m.echo [m.t_echo_ptr + ch * 2])
// Sample in echo history buffer, where 0 is the oldest // Sample in echo history buffer, where 0 is the oldest
#define ECHO_FIR( i ) (m.echo_hist_pos [i]) #define ECHO_FIR( i ) (m.echo_hist_pos [i])
@ -835,9 +835,10 @@ void SPC_DSP::run( int clocks_remain )
//// Setup //// Setup
void SPC_DSP::init( void* ram_64k ) void SPC_DSP::init( void* ram_64k, void* echo_64k )
{ {
m.ram = (uint8_t*) ram_64k; m.ram = (uint8_t*) ram_64k;
m.echo = (uint8_t*) echo_64k;
mute_voices( 0 ); mute_voices( 0 );
disable_surround( false ); disable_surround( false );
set_output( 0, 0 ); set_output( 0, 0 );
@ -861,7 +862,8 @@ void SPC_DSP::init( void* ram_64k )
void SPC_DSP::soft_reset_common() void SPC_DSP::soft_reset_common()
{ {
require( m.ram ); // init() must have been called already require( m.ram ); // init() must have been called already
require( m.echo );
m.noise = 0x4000; m.noise = 0x4000;
m.echo_hist_pos = m.echo_hist; m.echo_hist_pos = m.echo_hist;

View File

@ -15,7 +15,7 @@ public:
// Setup // Setup
// Initializes DSP and has it use the 64K RAM provided // Initializes DSP and has it use the 64K RAM provided
void init( void* ram_64k ); void init( void* ram_64k, void* echo_64k );
// Sets destination for output samples. If out is NULL or out_size is 0, // Sets destination for output samples. If out is NULL or out_size is 0,
// doesn't generate any. // doesn't generate any.
@ -178,7 +178,8 @@ private:
voice_t voices [voice_count]; voice_t voices [voice_count];
// non-emulation state // non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP uint8_t* ram; // 64K shared RAM between DSP and SMP
uint8_t* echo; // should point at the same memory as ram; used for older hack compatibility
int mute_mask; int mute_mask;
sample_t* out; sample_t* out;
sample_t* out_end; sample_t* out_end;

View File

@ -30,6 +30,12 @@ auto DSP::read(uint8 address) -> uint8 {
} }
auto DSP::write(uint8 address, uint8 data) -> void { auto DSP::write(uint8 address, uint8 data) -> void {
if(configuration.hacks.dsp.echoShadow) {
if(address == 0x6c && (data & 0x20)) {
memset(echoram, 0x00, 65536);
}
}
spc_dsp.write(address, data); spc_dsp.write(address, data);
} }
@ -42,7 +48,12 @@ auto DSP::power(bool reset) -> void {
stream = Emulator::audio.createStream(2, system.apuFrequency() / 768.0); stream = Emulator::audio.createStream(2, system.apuFrequency() / 768.0);
if(!reset) { if(!reset) {
spc_dsp.init(apuram); if(!configuration.hacks.dsp.echoShadow) {
spc_dsp.init(apuram, apuram);
} else {
memset(echoram, 0x00, 65536);
spc_dsp.init(apuram, echoram);
}
spc_dsp.reset(); spc_dsp.reset();
spc_dsp.set_output(samplebuffer, 8192); spc_dsp.set_output(samplebuffer, 8192);
} else { } else {

View File

@ -20,6 +20,9 @@ private:
bool fastDSP = false; bool fastDSP = false;
SPC_DSP spc_dsp; SPC_DSP spc_dsp;
int16 samplebuffer[8192]; int16 samplebuffer[8192];
//unserialized:
uint8 echoram[64 * 1024] = {};
}; };
extern DSP dsp; extern DSP dsp;

View File

@ -22,12 +22,14 @@ auto Configuration::process(Markup::Node document, bool load) -> void {
bind(boolean, "Hacks/PPU/Deinterlace", hacks.ppu.deinterlace); bind(boolean, "Hacks/PPU/Deinterlace", hacks.ppu.deinterlace);
bind(natural, "Hacks/PPU/RenderCycle", hacks.ppu.renderCycle); bind(natural, "Hacks/PPU/RenderCycle", hacks.ppu.renderCycle);
bind(boolean, "Hacks/PPU/NoSpriteLimit", hacks.ppu.noSpriteLimit); bind(boolean, "Hacks/PPU/NoSpriteLimit", hacks.ppu.noSpriteLimit);
bind(boolean, "Hacks/PPU/NoVRAMBlocking", hacks.ppu.noVRAMBlocking);
bind(natural, "Hacks/PPU/Mode7/Scale", hacks.ppu.mode7.scale); bind(natural, "Hacks/PPU/Mode7/Scale", hacks.ppu.mode7.scale);
bind(boolean, "Hacks/PPU/Mode7/Perspective", hacks.ppu.mode7.perspective); bind(boolean, "Hacks/PPU/Mode7/Perspective", hacks.ppu.mode7.perspective);
bind(boolean, "Hacks/PPU/Mode7/Supersample", hacks.ppu.mode7.supersample); bind(boolean, "Hacks/PPU/Mode7/Supersample", hacks.ppu.mode7.supersample);
bind(boolean, "Hacks/PPU/Mode7/Mosaic", hacks.ppu.mode7.mosaic); bind(boolean, "Hacks/PPU/Mode7/Mosaic", hacks.ppu.mode7.mosaic);
bind(boolean, "Hacks/DSP/Fast", hacks.dsp.fast); bind(boolean, "Hacks/DSP/Fast", hacks.dsp.fast);
bind(boolean, "Hacks/DSP/Cubic", hacks.dsp.cubic); bind(boolean, "Hacks/DSP/Cubic", hacks.dsp.cubic);
bind(boolean, "Hacks/DSP/EchoShadow", hacks.dsp.echoShadow);
bind(boolean, "Hacks/Coprocessor/DelayedSync", hacks.coprocessor.delayedSync); bind(boolean, "Hacks/Coprocessor/DelayedSync", hacks.coprocessor.delayedSync);
bind(boolean, "Hacks/Coprocessor/PreferHLE", hacks.coprocessor.preferHLE); bind(boolean, "Hacks/Coprocessor/PreferHLE", hacks.coprocessor.preferHLE);
bind(natural, "Hacks/SA1/Overclock", hacks.sa1.overclock); bind(natural, "Hacks/SA1/Overclock", hacks.sa1.overclock);

View File

@ -33,6 +33,7 @@ struct Configuration {
bool fast = true; bool fast = true;
bool deinterlace = true; bool deinterlace = true;
bool noSpriteLimit = false; bool noSpriteLimit = false;
bool noVRAMBlocking = false;
uint renderCycle = 512; uint renderCycle = 512;
struct Mode7 { struct Mode7 {
uint scale = 1; uint scale = 1;
@ -44,6 +45,7 @@ struct Configuration {
struct DSP { struct DSP {
bool fast = true; bool fast = true;
bool cubic = false; bool cubic = false;
bool echoShadow = false;
} dsp; } dsp;
struct Coprocessor { struct Coprocessor {
bool delayedSync = true; bool delayedSync = true;

View File

@ -29,7 +29,7 @@ auto PPU::readVRAM() -> uint16 {
template<bool Byte> template<bool Byte>
auto PPU::writeVRAM(uint8 data) -> void { auto PPU::writeVRAM(uint8 data) -> void {
if(!io.displayDisable && cpu.vcounter() < vdisp()) return; if(!io.displayDisable && cpu.vcounter() < vdisp() && !noVRAMBlocking()) return;
Line::flush(); Line::flush();
auto address = vramAddress(); auto address = vramAddress();
if constexpr(Byte == 0) { if constexpr(Byte == 0) {

View File

@ -30,6 +30,7 @@ auto PPU::hdSupersample() const -> bool { return configuration.hacks.ppu.mode7.s
auto PPU::hdMosaic() const -> bool { return configuration.hacks.ppu.mode7.mosaic; } auto PPU::hdMosaic() const -> bool { return configuration.hacks.ppu.mode7.mosaic; }
auto PPU::deinterlace() const -> bool { return configuration.hacks.ppu.deinterlace; } auto PPU::deinterlace() const -> bool { return configuration.hacks.ppu.deinterlace; }
auto PPU::renderCycle() const -> uint { return configuration.hacks.ppu.renderCycle; } auto PPU::renderCycle() const -> uint { return configuration.hacks.ppu.renderCycle; }
auto PPU::noVRAMBlocking() const -> bool { return configuration.hacks.ppu.noVRAMBlocking; }
#define ppu ppufast #define ppu ppufast
PPU::PPU() { PPU::PPU() {

View File

@ -20,6 +20,7 @@ struct PPU : PPUcounter {
alwaysinline auto hdMosaic() const -> bool; alwaysinline auto hdMosaic() const -> bool;
alwaysinline auto deinterlace() const -> bool; alwaysinline auto deinterlace() const -> bool;
alwaysinline auto renderCycle() const -> uint; alwaysinline auto renderCycle() const -> uint;
alwaysinline auto noVRAMBlocking() const -> bool;
//ppu.cpp //ppu.cpp
PPU(); PPU();

View File

@ -6,12 +6,14 @@ auto Program::load() -> void {
emulator->configure("Hacks/PPU/Fast", settings.emulator.hack.ppu.fast); emulator->configure("Hacks/PPU/Fast", settings.emulator.hack.ppu.fast);
emulator->configure("Hacks/PPU/Deinterlace", settings.emulator.hack.ppu.deinterlace); emulator->configure("Hacks/PPU/Deinterlace", settings.emulator.hack.ppu.deinterlace);
emulator->configure("Hacks/PPU/NoSpriteLimit", settings.emulator.hack.ppu.noSpriteLimit); emulator->configure("Hacks/PPU/NoSpriteLimit", settings.emulator.hack.ppu.noSpriteLimit);
emulator->configure("Hacks/PPU/NoVRAMBlocking", settings.emulator.hack.ppu.noVRAMBlocking);
emulator->configure("Hacks/PPU/Mode7/Scale", settings.emulator.hack.ppu.mode7.scale); emulator->configure("Hacks/PPU/Mode7/Scale", settings.emulator.hack.ppu.mode7.scale);
emulator->configure("Hacks/PPU/Mode7/Perspective", settings.emulator.hack.ppu.mode7.perspective); emulator->configure("Hacks/PPU/Mode7/Perspective", settings.emulator.hack.ppu.mode7.perspective);
emulator->configure("Hacks/PPU/Mode7/Supersample", settings.emulator.hack.ppu.mode7.supersample); emulator->configure("Hacks/PPU/Mode7/Supersample", settings.emulator.hack.ppu.mode7.supersample);
emulator->configure("Hacks/PPU/Mode7/Mosaic", settings.emulator.hack.ppu.mode7.mosaic); emulator->configure("Hacks/PPU/Mode7/Mosaic", settings.emulator.hack.ppu.mode7.mosaic);
emulator->configure("Hacks/DSP/Fast", settings.emulator.hack.dsp.fast); emulator->configure("Hacks/DSP/Fast", settings.emulator.hack.dsp.fast);
emulator->configure("Hacks/DSP/Cubic", settings.emulator.hack.dsp.cubic); emulator->configure("Hacks/DSP/Cubic", settings.emulator.hack.dsp.cubic);
emulator->configure("Hacks/DSP/EchoShadow", settings.emulator.hack.dsp.echoShadow);
emulator->configure("Hacks/Coprocessor/DelayedSync", settings.emulator.hack.coprocessor.delayedSync); emulator->configure("Hacks/Coprocessor/DelayedSync", settings.emulator.hack.coprocessor.delayedSync);
emulator->configure("Hacks/Coprocessor/PreferHLE", settings.emulator.hack.coprocessor.preferHLE); emulator->configure("Hacks/Coprocessor/PreferHLE", settings.emulator.hack.coprocessor.preferHLE);
emulator->configure("Hacks/SuperFX/Overclock", settings.emulator.hack.superfx.overclock); emulator->configure("Hacks/SuperFX/Overclock", settings.emulator.hack.superfx.overclock);

View File

@ -65,6 +65,15 @@ auto EmulatorSettings::create() -> void {
noSpriteLimit.setText("No sprite limit").setChecked(settings.emulator.hack.ppu.noSpriteLimit).onToggle([&] { noSpriteLimit.setText("No sprite limit").setChecked(settings.emulator.hack.ppu.noSpriteLimit).onToggle([&] {
settings.emulator.hack.ppu.noSpriteLimit = noSpriteLimit.checked(); settings.emulator.hack.ppu.noSpriteLimit = noSpriteLimit.checked();
}); });
noVRAMBlocking.setText("No VRAM blocking").setToolTip(
"This option emulates a bug in older releases of ZSNES and Snes9X where VRAM blocking was not emulated.\n"
"A few older ROM hacks relied on this behavior, and will render graphics incorrectly if not enabled.\n"
"Not only is this extremely inaccurate to real hardware, it also hurts the speed of the fast PPU.\n"
"Do not enable this option unless you need to play a game that is incompatible with bsnes otherwise."
).setChecked(settings.emulator.hack.ppu.noVRAMBlocking).onToggle([&] {
settings.emulator.hack.ppu.noVRAMBlocking = noVRAMBlocking.checked();
emulator->configure("Hacks/PPU/NoVRAMBlocking", settings.emulator.hack.ppu.noVRAMBlocking);
});
mode7Label.setText("HD Mode 7 (fast PPU only)").setFont(Font().setBold()); mode7Label.setText("HD Mode 7 (fast PPU only)").setFont(Font().setBold());
mode7ScaleLabel.setText("Scale:"); mode7ScaleLabel.setText("Scale:");
mode7Scale.append(ComboButtonItem().setText( "240p").setProperty("multiplier", 1)); mode7Scale.append(ComboButtonItem().setText( "240p").setProperty("multiplier", 1));
@ -104,6 +113,14 @@ auto EmulatorSettings::create() -> void {
settings.emulator.hack.dsp.cubic = cubicInterpolation.checked(); settings.emulator.hack.dsp.cubic = cubicInterpolation.checked();
emulator->configure("Hacks/DSP/Cubic", settings.emulator.hack.dsp.cubic); emulator->configure("Hacks/DSP/Cubic", settings.emulator.hack.dsp.cubic);
}); });
echoShadow.setText("Echo shadow RAM").setToolTip(
"This option emulates a bug in ZSNES where echo RAM was treated as separate from APU RAM.\n"
"Many older ROM hacks for Super Mario World relied on this behavior, and will crash without enabling this.\n"
"It is, however, extremely inaccurate to real hardware and should not be enabled unless required."
).setChecked(settings.emulator.hack.dsp.echoShadow).onToggle([&] {
settings.emulator.hack.dsp.echoShadow = echoShadow.checked();
//not a run-time setting: do not call emulator->configure() here.
});
coprocessorLabel.setText("Coprocessors").setFont(Font().setBold()); coprocessorLabel.setText("Coprocessors").setFont(Font().setBold());
coprocessorDelayedSyncOption.setText("Fast mode").setChecked(settings.emulator.hack.coprocessor.delayedSync).onToggle([&] { coprocessorDelayedSyncOption.setText("Fast mode").setChecked(settings.emulator.hack.coprocessor.delayedSync).onToggle([&] {
settings.emulator.hack.coprocessor.delayedSync = coprocessorDelayedSyncOption.checked(); settings.emulator.hack.coprocessor.delayedSync = coprocessorDelayedSyncOption.checked();

View File

@ -117,12 +117,14 @@ auto Settings::process(bool load) -> void {
bind(boolean, "Emulator/Hack/PPU/Fast", emulator.hack.ppu.fast); bind(boolean, "Emulator/Hack/PPU/Fast", emulator.hack.ppu.fast);
bind(boolean, "Emulator/Hack/PPU/Deinterlace", emulator.hack.ppu.deinterlace); bind(boolean, "Emulator/Hack/PPU/Deinterlace", emulator.hack.ppu.deinterlace);
bind(boolean, "Emulator/Hack/PPU/NoSpriteLimit", emulator.hack.ppu.noSpriteLimit); bind(boolean, "Emulator/Hack/PPU/NoSpriteLimit", emulator.hack.ppu.noSpriteLimit);
bind(boolean, "Emulator/Hack/PPU/NoVRAMBlocking", emulator.hack.ppu.noVRAMBlocking);
bind(natural, "Emulator/Hack/PPU/Mode7/Scale", emulator.hack.ppu.mode7.scale); bind(natural, "Emulator/Hack/PPU/Mode7/Scale", emulator.hack.ppu.mode7.scale);
bind(boolean, "Emulator/Hack/PPU/Mode7/Perspective", emulator.hack.ppu.mode7.perspective); bind(boolean, "Emulator/Hack/PPU/Mode7/Perspective", emulator.hack.ppu.mode7.perspective);
bind(boolean, "Emulator/Hack/PPU/Mode7/Supersample", emulator.hack.ppu.mode7.supersample); bind(boolean, "Emulator/Hack/PPU/Mode7/Supersample", emulator.hack.ppu.mode7.supersample);
bind(boolean, "Emulator/Hack/PPU/Mode7/Mosaic", emulator.hack.ppu.mode7.mosaic); bind(boolean, "Emulator/Hack/PPU/Mode7/Mosaic", emulator.hack.ppu.mode7.mosaic);
bind(boolean, "Emulator/Hack/DSP/Fast", emulator.hack.dsp.fast); bind(boolean, "Emulator/Hack/DSP/Fast", emulator.hack.dsp.fast);
bind(boolean, "Emulator/Hack/DSP/Cubic", emulator.hack.dsp.cubic); bind(boolean, "Emulator/Hack/DSP/Cubic", emulator.hack.dsp.cubic);
bind(boolean, "Emulator/Hack/DSP/EchoShadow", emulator.hack.dsp.echoShadow);
bind(boolean, "Emulator/Hack/Coprocessor/DelayedSync", emulator.hack.coprocessor.delayedSync); bind(boolean, "Emulator/Hack/Coprocessor/DelayedSync", emulator.hack.coprocessor.delayedSync);
bind(boolean, "Emulator/Hack/Coprocessor/PreferHLE", emulator.hack.coprocessor.preferHLE); bind(boolean, "Emulator/Hack/Coprocessor/PreferHLE", emulator.hack.coprocessor.preferHLE);
bind(natural, "Emulator/Hack/SA1/Overclock", emulator.hack.sa1.overclock); bind(natural, "Emulator/Hack/SA1/Overclock", emulator.hack.sa1.overclock);

View File

@ -103,6 +103,7 @@ struct Settings : Markup::Node {
bool fast = true; bool fast = true;
bool deinterlace = true; bool deinterlace = true;
bool noSpriteLimit = false; bool noSpriteLimit = false;
bool noVRAMBlocking = false;
struct Mode7 { struct Mode7 {
uint scale = 1; uint scale = 1;
bool perspective = true; bool perspective = true;
@ -113,6 +114,7 @@ struct Settings : Markup::Node {
struct DSP { struct DSP {
bool fast = true; bool fast = true;
bool cubic = false; bool cubic = false;
bool echoShadow = false;
} dsp; } dsp;
struct Coprocessor { struct Coprocessor {
bool delayedSync = true; bool delayedSync = true;
@ -343,6 +345,7 @@ public:
CheckLabel fastPPU{&ppuLayout, Size{0, 0}}; CheckLabel fastPPU{&ppuLayout, Size{0, 0}};
CheckLabel deinterlace{&ppuLayout, Size{0, 0}}; CheckLabel deinterlace{&ppuLayout, Size{0, 0}};
CheckLabel noSpriteLimit{&ppuLayout, Size{0, 0}}; CheckLabel noSpriteLimit{&ppuLayout, Size{0, 0}};
CheckLabel noVRAMBlocking{&ppuLayout, Size{0, 0}};
Label mode7Label{this, Size{~0, 0}, 2}; Label mode7Label{this, Size{~0, 0}, 2};
HorizontalLayout mode7Layout{this, Size{~0, 0}}; HorizontalLayout mode7Layout{this, Size{~0, 0}};
Label mode7ScaleLabel{&mode7Layout, Size{0, 0}}; Label mode7ScaleLabel{&mode7Layout, Size{0, 0}};
@ -354,6 +357,7 @@ public:
HorizontalLayout dspLayout{this, Size{~0, 0}}; HorizontalLayout dspLayout{this, Size{~0, 0}};
CheckLabel fastDSP{&dspLayout, Size{0, 0}}; CheckLabel fastDSP{&dspLayout, Size{0, 0}};
CheckLabel cubicInterpolation{&dspLayout, Size{0, 0}}; CheckLabel cubicInterpolation{&dspLayout, Size{0, 0}};
CheckLabel echoShadow{&dspLayout, Size{0, 0}};
Label coprocessorLabel{this, Size{~0, 0}, 2}; Label coprocessorLabel{this, Size{~0, 0}, 2};
HorizontalLayout coprocessorLayout{this, Size{~0, 0}}; HorizontalLayout coprocessorLayout{this, Size{~0, 0}};
CheckLabel coprocessorDelayedSyncOption{&coprocessorLayout, Size{0, 0}}; CheckLabel coprocessorDelayedSyncOption{&coprocessorLayout, Size{0, 0}};