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 {
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 License = "GPLv3";
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
// 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
#define ECHO_FIR( i ) (m.echo_hist_pos [i])
@ -835,9 +835,10 @@ void SPC_DSP::run( int clocks_remain )
//// 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.echo = (uint8_t*) echo_64k;
mute_voices( 0 );
disable_surround( false );
set_output( 0, 0 );
@ -862,6 +863,7 @@ void SPC_DSP::init( void* ram_64k )
void SPC_DSP::soft_reset_common()
{
require( m.ram ); // init() must have been called already
require( m.echo );
m.noise = 0x4000;
m.echo_hist_pos = m.echo_hist;

View File

@ -15,7 +15,7 @@ public:
// Setup
// 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,
// doesn't generate any.
@ -179,6 +179,7 @@ private:
// non-emulation state
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;
sample_t* out;
sample_t* out_end;

View File

@ -30,6 +30,12 @@ auto DSP::read(uint8 address) -> uint8 {
}
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);
}
@ -42,7 +48,12 @@ auto DSP::power(bool reset) -> void {
stream = Emulator::audio.createStream(2, system.apuFrequency() / 768.0);
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.set_output(samplebuffer, 8192);
} else {

View File

@ -20,6 +20,9 @@ private:
bool fastDSP = false;
SPC_DSP spc_dsp;
int16 samplebuffer[8192];
//unserialized:
uint8 echoram[64 * 1024] = {};
};
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(natural, "Hacks/PPU/RenderCycle", hacks.ppu.renderCycle);
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(boolean, "Hacks/PPU/Mode7/Perspective", hacks.ppu.mode7.perspective);
bind(boolean, "Hacks/PPU/Mode7/Supersample", hacks.ppu.mode7.supersample);
bind(boolean, "Hacks/PPU/Mode7/Mosaic", hacks.ppu.mode7.mosaic);
bind(boolean, "Hacks/DSP/Fast", hacks.dsp.fast);
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/PreferHLE", hacks.coprocessor.preferHLE);
bind(natural, "Hacks/SA1/Overclock", hacks.sa1.overclock);

View File

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

View File

@ -29,7 +29,7 @@ auto PPU::readVRAM() -> uint16 {
template<bool Byte>
auto PPU::writeVRAM(uint8 data) -> void {
if(!io.displayDisable && cpu.vcounter() < vdisp()) return;
if(!io.displayDisable && cpu.vcounter() < vdisp() && !noVRAMBlocking()) return;
Line::flush();
auto address = vramAddress();
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::deinterlace() const -> bool { return configuration.hacks.ppu.deinterlace; }
auto PPU::renderCycle() const -> uint { return configuration.hacks.ppu.renderCycle; }
auto PPU::noVRAMBlocking() const -> bool { return configuration.hacks.ppu.noVRAMBlocking; }
#define ppu ppufast
PPU::PPU() {

View File

@ -20,6 +20,7 @@ struct PPU : PPUcounter {
alwaysinline auto hdMosaic() const -> bool;
alwaysinline auto deinterlace() const -> bool;
alwaysinline auto renderCycle() const -> uint;
alwaysinline auto noVRAMBlocking() const -> bool;
//ppu.cpp
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/Deinterlace", settings.emulator.hack.ppu.deinterlace);
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/Perspective", settings.emulator.hack.ppu.mode7.perspective);
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/DSP/Fast", settings.emulator.hack.dsp.fast);
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/PreferHLE", settings.emulator.hack.coprocessor.preferHLE);
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([&] {
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());
mode7ScaleLabel.setText("Scale:");
mode7Scale.append(ComboButtonItem().setText( "240p").setProperty("multiplier", 1));
@ -104,6 +113,14 @@ auto EmulatorSettings::create() -> void {
settings.emulator.hack.dsp.cubic = cubicInterpolation.checked();
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());
coprocessorDelayedSyncOption.setText("Fast mode").setChecked(settings.emulator.hack.coprocessor.delayedSync).onToggle([&] {
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/Deinterlace", emulator.hack.ppu.deinterlace);
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(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/Mosaic", emulator.hack.ppu.mode7.mosaic);
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/EchoShadow", emulator.hack.dsp.echoShadow);
bind(boolean, "Emulator/Hack/Coprocessor/DelayedSync", emulator.hack.coprocessor.delayedSync);
bind(boolean, "Emulator/Hack/Coprocessor/PreferHLE", emulator.hack.coprocessor.preferHLE);
bind(natural, "Emulator/Hack/SA1/Overclock", emulator.hack.sa1.overclock);

View File

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