Update to 20180726 release.

byuu says:

Once again, I wasn't able to complete a full WIP revision.

This WIP-WIP adds very sophisticated emulation of the Sega Genesis
Lock-On and Game Genie cartridges ... essentially, through recursion and
a linked list, higan supports an infinite nesting of cartridges.

Of course, on real hardware, after you stack more than three or four
cartridges, the power draw gets too high and things start glitching out
more and more as you keep stacking. I've heard that someone chained up
to ten Sonic & Knuckles cartridges before it finally became completely
unplayable.

And so of course, higan emulates this limitation as well ^-^. On the
fourth cartridge and beyond, it will become more and more likely that
address and/or data lines "glitch" out randomly, causing various
glitches. It's a completely silly easter egg that requires no speed
impact whatsoever beyond the impact of the new linked list cartridge
system.

I also designed the successor to Emulator::Interface::cap,get,set. Those
were holdovers from the older, since-removed ruby-style accessors.

In its place is the new Emulator::Interface::configuration,configure
API. There's the usual per-property access, and there's also access to
read and write all configurable options at once. In essence, this
enables introspection into core-specific features.

So far, you can control processor version#s, PPU VRAM size, video
settings, and hacks. As such, the .sys/manifest.bml files are no longer
necessary. Instead, it all goes into .sys/configuration.bml, which is
generated by the emulator if it's missing.

higan is going to take this even further and allow each option under
"Systems" to have its own editable configuration file. So if you wanted,
you could have a 1/1/1 SNES menu option, and a 2/1/3 SNES menu option.
Or a Model 1 Genesis option, and a Model 2 Genesis option. Or the
various Game Boy model revisions. Or an "SNES-Fast" and "SNES-Accurate"
option.

I've not fully settled on the syntax of the new configuration API. I
feel it might be useful to provide type information, but I really quite
passionately hate any<T> container objects. For now it's all
string-based, because strings can hold anything in nall.

I might also change the access rules. Right now it's like:
emulator→configure("video/blurEmulation", true); but it might be nicer
as "Video::Blur Emulation", or "Video.BlurEmulation", or something like
that.
This commit is contained in:
Tim Allen 2018-07-26 20:36:43 +10:00
parent 22bd4b9277
commit 876b4be1d2
38 changed files with 461 additions and 354 deletions

View File

@ -13,7 +13,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "106.52"; static const string Version = "106.53";
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

@ -87,6 +87,12 @@ struct Interface {
//cheat functions //cheat functions
virtual auto cheats(const vector<string>& = {}) -> void {} virtual auto cheats(const vector<string>& = {}) -> void {}
//configuration
virtual auto configuration() -> string { return {}; }
virtual auto configuration(string name) -> string { return {}; }
virtual auto configure(string configuration = "") -> bool { return false; }
virtual auto configure(string name, string value) -> bool { return false; }
//settings //settings
virtual auto cap(const string& name) -> bool { return false; } virtual auto cap(const string& name) -> bool { return false; }
virtual auto get(const string& name) -> any { return {}; } virtual auto get(const string& name) -> any { return {}; }

View File

@ -5,144 +5,145 @@ namespace MegaDrive {
Cartridge cartridge; Cartridge cartridge;
#include "serialization.cpp" #include "serialization.cpp"
auto Cartridge::region() const -> string {
return game.region;
}
auto Cartridge::hashes() const -> vector<string> { auto Cartridge::hashes() const -> vector<string> {
vector<string> hashes; vector<string> hashes;
hashes.append(game.hash); hashes.append(information.hash);
if(lockOn.hash) hashes.append(lockOn.hash); if(slot) for(auto& hash : slot->hashes()) hashes.append(hash);
return hashes; return hashes;
} }
auto Cartridge::manifests() const -> vector<string> { auto Cartridge::manifests() const -> vector<string> {
vector<string> manifests; vector<string> manifests;
manifests.append(game.manifest); manifests.append(information.manifest);
if(lockOn.manifest) manifests.append(lockOn.manifest); if(slot) for(auto& manifest : slot->manifests()) manifests.append(manifest);
return manifests; return manifests;
} }
auto Cartridge::titles() const -> vector<string> { auto Cartridge::titles() const -> vector<string> {
vector<string> titles; vector<string> titles;
titles.append(game.title); titles.append(information.title);
if(lockOn.title) titles.append(lockOn.title); if(slot) for(auto& title : slot->titles()) titles.append(title);
return titles; return titles;
} }
auto Cartridge::load() -> bool { auto Cartridge::load() -> bool {
game = {}; unload();
lockOn = {};
read.reset();
write.reset();
if(!loadGame()) { if(auto loaded = platform->load(ID::MegaDrive, "Mega Drive", "md", {"Auto", "NTSC-J", "NTSC-U", "PAL"})) {
game = {}; information.pathID = loaded.pathID;
return false; information.region = loaded.option;
} else return false;
if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads();
} else return false;
information.document = BML::unserialize(information.manifest);
information.hash = information.document["game/sha256"].text();
information.title = information.document["game/label"].text();
if(!loadROM(rom, information.document["game/board/memory(type=ROM,content=Program)"])) {
return unload(), false;
} }
read = {&Cartridge::readGame, this}; if(!loadROM(patch, information.document["game/board/memory(type=ROM,content=Patch)"])) {
write = {&Cartridge::writeGame, this}; patch.reset();
}
if(game.patch.size) { if(!loadRAM(ram, information.document["game/board/memory(type=RAM,content=Save)"])) {
ram.reset();
}
if(information.region == "Auto") {
if(auto region = information.document["game/region"].text()) {
information.region = region.upcase();
} else {
information.region = "NTSC-J";
}
}
read = {&Cartridge::readLinear, this};
write = {&Cartridge::writeLinear, this};
if(rom.size > 0x200000) {
read = {&Cartridge::readBanked, this};
write = {&Cartridge::writeBanked, this};
}
if(patch) {
slot = new Cartridge{depth + 1};
if(!slot->load()) slot.reset();
read = {&Cartridge::readLockOn, this}; read = {&Cartridge::readLockOn, this};
write = {&Cartridge::writeLockOn, this}; write = {&Cartridge::writeLockOn, this};
if(!loadLockOn()) lockOn = {}; }
if(rom.data[0x120>>1]==0x4761 || rom.data[0x120>>1]==0x6147) {
slot = new Cartridge{depth + 1};
if(!slot->load()) slot.reset();
read = {&Cartridge::readGameGenie, this};
write = {&Cartridge::writeGameGenie, this};
}
//easter egg: power draw increases with each successively stacked cartridge
//simulate increasing address/data line errors as stacking increases
if(depth >= 3) {
auto reader = read;
auto writer = write;
auto scramble = [=](uint32 value) -> uint32 {
uint chance = max(1, (1 << 19) >> depth) - 1;
if((random() & chance) == 0) value ^= 1 << (random() & 31);
return value;
};
read = [=](uint22 address) -> uint16 {
return scramble(reader(scramble(address)));
};
write = [=](uint22 address, uint16 data) -> void {
writer(scramble(address), scramble(data));
};
} }
return true; return true;
} }
auto Cartridge::loadGame() -> bool { auto Cartridge::save() -> void {
if(auto loaded = platform->load(ID::MegaDrive, "Mega Drive", "md", {"Auto", "NTSC-J", "NTSC-U", "PAL"})) { saveRAM(ram, information.document["game/board/memory(type=RAM,content=Save)"]);
game.pathID = loaded.pathID; if(slot) slot->save();
game.region = loaded.option;
} else return false;
if(auto fp = platform->open(game.pathID, "manifest.bml", File::Read, File::Required)) {
game.manifest = fp->reads();
} else return false;
game.document = BML::unserialize(game.manifest);
game.hash = game.document["game/sha256"].text();
game.title = game.document["game/label"].text();
if(!loadROM(game.rom, game.pathID, game.document["game/board/memory(type=ROM,content=Program)"])) {
game.rom.reset();
return false;
}
if(!loadROM(game.patch, game.pathID, game.document["game/board/memory(type=ROM,content=Patch)"])) {
game.patch.reset();
}
if(!loadRAM(game.ram, game.pathID, game.document["game/board/memory(type=RAM,content=Save)"])) {
game.ram.reset();
}
if(game.region == "Auto") {
if(auto region = game.document["game/region"].text()) {
game.region = region.upcase();
} else {
game.region = "NTSC-J";
}
}
return true;
} }
auto Cartridge::loadLockOn() -> bool { auto Cartridge::unload() -> void {
if(auto loaded = platform->load(ID::MegaDrive, "Mega Drive", "md")) { rom.reset();
lockOn.pathID = loaded.pathID; patch.reset();
} else return false; ram.reset();
read.reset();
if(auto fp = platform->open(lockOn.pathID, "manifest.bml", File::Read, File::Required)) { write.reset();
lockOn.manifest = fp->reads(); if(slot) slot->unload();
} else return false; slot.reset();
lockOn.document = BML::unserialize(lockOn.manifest);
lockOn.hash = lockOn.document["game/sha256"].text();
lockOn.title = lockOn.document["game/label"].text();
if(!loadROM(lockOn.rom, lockOn.pathID, lockOn.document["game/board/memory(type=ROM,content=Program)"])) {
lockOn.rom.reset();
return false;
}
if(!loadRAM(lockOn.ram, lockOn.pathID, lockOn.document["game/board/memory(type=RAM,content=Save)"])) {
lockOn.ram.reset();
}
if(lockOn.rom.size >= 0x200) {
string name;
name.resize(48);
for(uint n : range(24)) {
name.get()[n * 2 + 0] = lockOn.rom.data[0x120 / 2 + n].byte(1);
name.get()[n * 2 + 1] = lockOn.rom.data[0x120 / 2 + n].byte(0);
}
name.strip();
while(name.find(" ")) name.replace(" ", " ");
lockOn.patch = name == "SONIC THE HEDGEHOG 2";
}
return true;
} }
auto Cartridge::loadROM(Memory& rom, uint pathID, Markup::Node memory) -> bool { auto Cartridge::power() -> void {
ramEnable = 1;
ramWritable = 1;
for(uint n : range(8)) romBank[n] = n;
gameGenie = {};
}
//
auto Cartridge::loadROM(Memory& rom, Markup::Node memory) -> bool {
if(!memory) return false; if(!memory) return false;
auto name = string{memory["content"].text(), ".", memory["type"].text()}.downcase(); auto name = string{memory["content"].text(), ".", memory["type"].text()}.downcase();
rom.size = memory["size"].natural() >> 1; rom.size = memory["size"].natural() >> 1;
rom.mask = bit::round(rom.size) - 1; rom.mask = bit::round(rom.size) - 1;
rom.data = new uint16[rom.mask + 1](); rom.data = new uint16[rom.mask + 1]();
if(auto fp = platform->open(pathID, name, File::Read, File::Required)) { if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) {
for(uint n : range(rom.size)) rom.data[n] = fp->readm(2); for(uint n : range(rom.size)) rom.data[n] = fp->readm(2);
} else return false; } else return false;
return true; return true;
} }
auto Cartridge::loadRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool { auto Cartridge::loadRAM(Memory& ram, Markup::Node memory) -> bool {
if(!memory) return false; if(!memory) return false;
auto name = string{memory["content"].text(), ".", memory["type"].text()}.downcase(); auto name = string{memory["content"].text(), ".", memory["type"].text()}.downcase();
@ -155,7 +156,7 @@ auto Cartridge::loadRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool {
ram.mask = bit::round(ram.size) - 1; ram.mask = bit::round(ram.size) - 1;
ram.data = new uint16[ram.mask + 1](); ram.data = new uint16[ram.mask + 1]();
if(!(bool)memory["volatile"]) { if(!(bool)memory["volatile"]) {
if(auto fp = platform->open(pathID, name, File::Read)) { if(auto fp = platform->open(pathID(), name, File::Read)) {
for(uint n : range(ram.size)) { for(uint n : range(ram.size)) {
if(ram.bits != 0xffff) ram.data[n] = fp->readm(1) * 0x0101; if(ram.bits != 0xffff) ram.data[n] = fp->readm(1) * 0x0101;
if(ram.bits == 0xffff) ram.data[n] = fp->readm(2); if(ram.bits == 0xffff) ram.data[n] = fp->readm(2);
@ -166,17 +167,12 @@ auto Cartridge::loadRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool {
return true; return true;
} }
auto Cartridge::save() -> void { auto Cartridge::saveRAM(Memory& ram, Markup::Node memory) -> bool {
saveRAM(game.ram, game.pathID, game.document["game/board/memory(type=RAM,content=Save)"]);
saveRAM(lockOn.ram, lockOn.pathID, lockOn.document["game/board/memory(type=RAM,content=Save)"]);
}
auto Cartridge::saveRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool {
if(!memory) return false; if(!memory) return false;
if((bool)memory["volatile"]) return true; if((bool)memory["volatile"]) return true;
auto name = string{memory["content"].text(), ".", memory["type"].text()}.downcase(); auto name = string{memory["content"].text(), ".", memory["type"].text()}.downcase();
if(auto fp = platform->open(pathID, name, File::Write)) { if(auto fp = platform->open(pathID(), name, File::Write)) {
for(uint n : range(ram.size)) { for(uint n : range(ram.size)) {
if(ram.bits != 0xffff) fp->writem(ram.data[n], 1); if(ram.bits != 0xffff) fp->writem(ram.data[n], 1);
if(ram.bits == 0xffff) fp->writem(ram.data[n], 2); if(ram.bits == 0xffff) fp->writem(ram.data[n], 2);
@ -186,92 +182,99 @@ auto Cartridge::saveRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool {
return true; return true;
} }
auto Cartridge::unload() -> void {
game.rom.reset();
game.patch.reset();
game.ram.reset();
game = {};
lockOn.rom.reset();
lockOn.ram.reset();
lockOn = {};
}
auto Cartridge::power() -> void {
ramEnable = 1;
ramWritable = 1;
for(auto n : range(8)) bank[n] = n;
}
// //
auto Cartridge::readIO(uint24 address) -> uint16 { auto Cartridge::readIO(uint24 address) -> uint16 {
if(slot) slot->readIO(address);
return 0x0000; return 0x0000;
} }
auto Cartridge::writeIO(uint24 address, uint16 data) -> void { auto Cartridge::writeIO(uint24 address, uint16 data) -> void {
if(address == 0xa130f1) ramEnable = data.bit(0), ramWritable = data.bit(1); if(address == 0xa130f1) ramEnable = data.bit(0), ramWritable = data.bit(1);
if(address == 0xa130f3) bank[1] = data; if(address == 0xa130f3) romBank[1] = data;
if(address == 0xa130f5) bank[2] = data; if(address == 0xa130f5) romBank[2] = data;
if(address == 0xa130f7) bank[3] = data; if(address == 0xa130f7) romBank[3] = data;
if(address == 0xa130f9) bank[4] = data; if(address == 0xa130f9) romBank[4] = data;
if(address == 0xa130fb) bank[5] = data; if(address == 0xa130fb) romBank[5] = data;
if(address == 0xa130fd) bank[6] = data; if(address == 0xa130fd) romBank[6] = data;
if(address == 0xa130ff) bank[7] = data; if(address == 0xa130ff) romBank[7] = data;
if(slot) slot->writeIO(address, data);
} }
// //
auto Cartridge::readGame(uint24 address) -> uint16 { auto Cartridge::readLinear(uint22 address) -> uint16 {
if(address >= 0x200000 && game.ram.size && ramEnable) { if(ramEnable && ram && address >= 0x200000) return ram.read(address);
return game.ram.data[address >> 1 & game.ram.mask]; return rom.read(address);
} else {
address = bank[address.bits(19,21)] << 19 | address.bits(0,18);
return game.rom.data[address >> 1 & game.rom.mask];
}
} }
auto Cartridge::writeGame(uint24 address, uint16 data) -> void { auto Cartridge::writeLinear(uint22 address, uint16 data) -> void {
//emulating RAM write protect bit breaks some commercial software //emulating ramWritable will break commercial software:
if(address >= 0x200000 && game.ram.size && ramEnable /* && ramWritable */) { //it does not appear that many (any?) games actually connect $a130f1.d1 to /WE;
if(game.ram.bits == 0x00ff) data = data.byte(0) * 0x0101; //hence RAM ends up always being writable, and many games fail to set d1=1
if(game.ram.bits == 0xff00) data = data.byte(1) * 0x0101; if(ramEnable && ram && address >= 0x200000) return ram.write(address, data);
game.ram.data[address >> 1 & game.ram.mask] = data; }
//
auto Cartridge::readBanked(uint22 address) -> uint16 {
address = romBank[address.bits(19,21)] << 19 | address.bits(0,18);
return rom.read(address);
}
auto Cartridge::writeBanked(uint22 address, uint16 data) -> void {
}
//
auto Cartridge::readLockOn(uint22 address) -> uint16 {
if(address < 0x200000) return rom.read(address);
if(ramEnable && address >= 0x300000) return patch.read(address);
if(slot) return slot->read(address);
return 0x0000;
}
auto Cartridge::writeLockOn(uint22 address, uint16 data) -> void {
if(slot) return slot->write(address, data);
}
//
auto Cartridge::readGameGenie(uint22 address) -> uint16 {
if(gameGenie.enable) {
for(auto& code : gameGenie.codes) {
if(code.enable && code.address == address) return code.data;
}
if(slot) return slot->read(address);
}
return rom.read(address);
}
auto Cartridge::writeGameGenie(uint22 address, uint16 data) -> void {
if(gameGenie.enable) {
if(slot) return slot->write(address, data);
}
if(address == 0x02 && data == 0x0001) {
gameGenie.enable = true;
}
if(address >= 0x04 && address <= 0x20 && !address.bit(0)) {
address = address - 0x04 >> 1;
auto& code = gameGenie.codes[address / 3];
if(address % 3 == 0) code.address.bits(16,23) = data.byte(0);
if(address % 3 == 1) code.address.bits( 0,15) = data;
if(address % 3 == 2) code.data = data, code.enable = true;
} }
} }
// //
auto Cartridge::readLockOn(uint24 address) -> uint16 { Cartridge::Memory::operator bool() const {
if(address >= 0x200000 && lockOn.ram.size && ramEnable) { return size;
return lockOn.ram.data[address >> 1 & lockOn.ram.mask];
}
if(address >= 0x300000 && lockOn.patch) {
return game.patch.data[address >> 1 & game.patch.mask];
}
if(address >= 0x200000 && lockOn.rom.data) {
return lockOn.rom.data[address >> 1 & lockOn.rom.mask];
}
if(address >= 0x200000) {
return 0x00;
}
return game.rom.data[address >> 1 & game.rom.mask];
} }
auto Cartridge::writeLockOn(uint24 address, uint16 data) -> void {
if(address >= 0x200000 && lockOn.ram.size && ramEnable) {
if(lockOn.ram.bits == 0x00ff) data = data.byte(0) * 0x0101;
if(lockOn.ram.bits == 0xff00) data = data.byte(1) * 0x0101;
lockOn.ram.data[address >> 1 & lockOn.ram.mask] = data;
}
}
//
auto Cartridge::Memory::reset() -> void { auto Cartridge::Memory::reset() -> void {
delete[] data; delete[] data;
data = nullptr; data = nullptr;
@ -280,4 +283,16 @@ auto Cartridge::Memory::reset() -> void {
bits = 0; bits = 0;
} }
auto Cartridge::Memory::read(uint24 address) -> uint16 {
if(!size) return 0x0000;
return data[address >> 1 & mask];
}
auto Cartridge::Memory::write(uint24 address, uint16 word) -> void {
if(!size) return;
if(bits == 0x00ff) word.byte(1) = word.byte(0);
if(bits == 0xff00) word.byte(0) = word.byte(1);
data[address >> 1 & mask] = word;
}
} }

View File

@ -1,73 +1,87 @@
struct Cartridge { struct Cartridge {
auto region() const -> string; auto pathID() const -> uint { return information.pathID; }
auto region() const -> string { return information.region; }
Cartridge() = default;
Cartridge(uint depth) : depth(depth) {}
auto hashes() const -> vector<string>; auto hashes() const -> vector<string>;
auto manifests() const -> vector<string>; auto manifests() const -> vector<string>;
auto titles() const -> vector<string>; auto titles() const -> vector<string>;
struct Memory; struct Memory;
auto load() -> bool; auto load() -> bool;
auto loadGame() -> bool;
auto loadLockOn() -> bool;
auto loadROM(Memory& rom, uint pathID, Markup::Node memory) -> bool;
auto loadRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool;
auto save() -> void; auto save() -> void;
auto saveRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool;
auto unload() -> void; auto unload() -> void;
auto power() -> void; auto power() -> void;
function<uint16 (uint24 address)> read; auto loadROM(Memory& rom, Markup::Node memory) -> bool;
function<void (uint24 address, uint16 data)> write; auto loadRAM(Memory& ram, Markup::Node memory) -> bool;
auto saveRAM(Memory& ram, Markup::Node memory) -> bool;
auto readIO(uint24 address) -> uint16; auto readIO(uint24 address) -> uint16;
auto writeIO(uint24 address, uint16 data) -> void; auto writeIO(uint24 address, uint16 data) -> void;
auto readGame(uint24 address) -> uint16; auto readLinear(uint22 address) -> uint16;
auto writeGame(uint24 address, uint16 data) -> void; auto writeLinear(uint22 address, uint16 data) -> void;
auto readLockOn(uint24 address) -> uint16; auto readBanked(uint22 address) -> uint16;
auto writeLockOn(uint24 address, uint16 data) -> void; auto writeBanked(uint22 address, uint16 data) -> void;
auto readLockOn(uint22 address) -> uint16;
auto writeLockOn(uint22 address, uint16 data) -> void;
auto readGameGenie(uint22 address) -> uint16;
auto writeGameGenie(uint22 address, uint16 data) -> void;
//serialization.cpp //serialization.cpp
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
struct Memory { struct Information {
auto reset() -> void;
uint16* data = nullptr;
uint size = 0;
uint mask = 0;
uint bits = 0;
};
struct Game {
uint pathID = 0; uint pathID = 0;
string region; string region;
string hash; string hash;
string manifest; string manifest;
string title; string title;
Markup::Node document; Markup::Node document;
Memory rom; };
Memory patch;
Memory ram;
} game;
struct LockOn { struct Memory {
uint pathID = 0; explicit operator bool() const;
string hash; auto reset() -> void;
string manifest; auto read(uint24 address) -> uint16;
string title; auto write(uint24 address, uint16 word) -> void;
Markup::Node document; uint16* data = nullptr;
Memory rom; uint size = 0; //16-bit word size
Memory ram; uint mask = 0;
uint bits = 0;
};
bool patch = false; Information information;
} lockOn;
Memory rom;
Memory patch;
Memory ram;
uint1 ramEnable; uint1 ramEnable;
uint1 ramWritable; uint1 ramWritable;
uint6 bank[8]; uint6 romBank[8];
struct GameGenie {
boolean enable;
struct Code {
boolean enable;
uint24 address;
uint16 data;
} codes[5];
} gameGenie;
function<uint16 (uint22 address)> read;
function<void (uint22 address, uint16 data)> write;
unique_pointer<Cartridge> slot;
const uint depth = 0;
}; };
extern Cartridge cartridge; extern Cartridge cartridge;

View File

@ -1,4 +1,7 @@
auto Cartridge::serialize(serializer& s) -> void { auto Cartridge::serialize(serializer& s) -> void {
if(game.ram.size) s.array(game.ram.data, game.ram.size); if(ram.size) s.array(ram.data, ram.size);
if(lockOn.ram.size) s.array(lockOn.ram.data, lockOn.ram.size); s.integer(ramEnable);
s.integer(ramWritable);
s.array(romBank);
if(slot) slot->serialize(s);
} }

View File

@ -6,6 +6,7 @@
#include <emulator/emulator.hpp> #include <emulator/emulator.hpp>
#include <emulator/thread.hpp> #include <emulator/thread.hpp>
#include <emulator/scheduler.hpp> #include <emulator/scheduler.hpp>
#include <emulator/random.hpp>
#include <emulator/cheat.hpp> #include <emulator/cheat.hpp>
#include <processor/m68k/m68k.hpp> #include <processor/m68k/m68k.hpp>
@ -15,8 +16,10 @@ namespace MegaDrive {
#define platform Emulator::platform #define platform Emulator::platform
namespace File = Emulator::File; namespace File = Emulator::File;
using Scheduler = Emulator::Scheduler; using Scheduler = Emulator::Scheduler;
using Random = Emulator::Random;
using Cheat = Emulator::Cheat; using Cheat = Emulator::Cheat;
extern Scheduler scheduler; extern Scheduler scheduler;
extern Random random;
extern Cheat cheat; extern Cheat cheat;
struct Wait { struct Wait {

View File

@ -4,6 +4,7 @@ namespace MegaDrive {
System system; System system;
Scheduler scheduler; Scheduler scheduler;
Random random;
Cheat cheat; Cheat cheat;
#include "serialization.cpp" #include "serialization.cpp"
@ -67,6 +68,8 @@ auto System::power(bool reset) -> void {
Emulator::audio.reset(interface); Emulator::audio.reset(interface);
random.entropy(Random::Entropy::High);
scheduler.reset(); scheduler.reset();
cartridge.power(); cartridge.power();
cpu.power(reset); cpu.power(reset);

View File

@ -44,8 +44,8 @@ auto CPU::main() -> void {
instruction(); instruction();
} }
auto CPU::load(Markup::Node node) -> bool { auto CPU::load() -> bool {
version = node["cpu/version"].natural(); version = configuration.system.cpu.version;
if(version < 1) version = 1; if(version < 1) version = 1;
if(version > 2) version = 2; if(version > 2) version = 2;
return true; return true;

View File

@ -6,7 +6,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
//cpu.cpp //cpu.cpp
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto load(Markup::Node) -> bool; auto load() -> bool;
auto power(bool reset) -> void; auto power(bool reset) -> void;
//dma.cpp //dma.cpp

View File

@ -234,7 +234,7 @@ auto DSP::write(uint8 addr, uint8 data) -> void {
/* initialization */ /* initialization */
auto DSP::load(Markup::Node node) -> bool { auto DSP::load() -> bool {
return true; return true;
} }

View File

@ -13,7 +13,7 @@ struct DSP : Thread {
auto write(uint8 addr, uint8 data) -> void; auto write(uint8 addr, uint8 data) -> void;
auto main() -> void; auto main() -> void;
auto load(Markup::Node) -> bool; auto load() -> bool;
auto power(bool reset) -> void; auto power(bool reset) -> void;
//serialization.cpp //serialization.cpp

View File

@ -0,0 +1,92 @@
Configuration configuration;
auto Configuration::read() -> string {
return {
"system\n"
" cpu version=", system.cpu.version, "\n"
" ppu1 version=", system.ppu1.version, "\n"
" vram size=0x", hex(system.ppu1.vram.size), "\n"
" ppu2 version=", system.ppu2.version, "\n"
"\n"
"video\n"
" blurEmulation: ", video.blurEmulation, "\n"
" colorEmulation: ", video.colorEmulation, "\n"
"\n"
"hacks\n"
" ppuFast\n"
" enable: ", hacks.ppuFast.enable, "\n"
" noSpriteLimit: ", hacks.ppuFast.noSpriteLimit, "\n"
" hiresMode7: ", hacks.ppuFast.hiresMode7, "\n"
" dspFast\n"
" enable: ", hacks.dspFast.enable, "\n"
};
}
auto Configuration::read(string name) -> string {
#define bind(id) { \
string key = {string{#id}.transform(".", "/")}; \
if(name == key) return name; \
}
bind(system.cpu.version);
bind(system.ppu1.version);
bind(system.ppu1.vram.size);
bind(system.ppu2.version);
bind(video.blurEmulation);
bind(video.colorEmulation);
bind(hacks.ppuFast.enable);
bind(hacks.ppuFast.noSpriteLimit);
bind(hacks.ppuFast.hiresMode7);
bind(hacks.dspFast.enable);
#undef bind
return {};
}
auto Configuration::write(string configuration) -> bool {
*this = {};
auto document = BML::unserialize(configuration);
if(!document) return false;
#define bind(type, id) { \
string key = {string{#id}.transform(".", "/")}; \
if(auto node = document[key]) id = node.type(); \
}
bind(natural, system.cpu.version);
bind(natural, system.ppu1.version);
bind(natural, system.ppu1.vram.size);
bind(natural, system.ppu2.version);
bind(boolean, video.blurEmulation);
bind(boolean, video.colorEmulation);
bind(boolean, hacks.ppuFast.enable);
bind(boolean, hacks.ppuFast.noSpriteLimit);
bind(boolean, hacks.ppuFast.hiresMode7);
bind(boolean, hacks.dspFast.enable);
#undef bind
return true;
}
auto Configuration::write(string name, string value) -> bool {
#define bind(type, id) { \
string key = {string{#id}.transform(".", "/")}; \
if(name == key) return id = Markup::Node().setValue(value).type(), true; \
}
bind(boolean, video.blurEmulation);
bind(boolean, video.colorEmulation);
bind(boolean, hacks.ppuFast.enable);
bind(boolean, hacks.ppuFast.noSpriteLimit);
bind(boolean, hacks.ppuFast.hiresMode7);
bind(boolean, hacks.dspFast.enable);
if(SuperFamicom::system.loaded()) return false;
bind(natural, system.cpu.version);
bind(natural, system.ppu1.version);
bind(natural, system.ppu1.vram.size);
bind(natural, system.ppu2.version);
#undef bind
return false;
}

View File

@ -0,0 +1,39 @@
struct Configuration {
auto read() -> string;
auto read(string) -> string;
auto write(string) -> bool;
auto write(string, string) -> bool;
struct System {
struct CPU {
uint version = 2;
} cpu;
struct PPU1 {
uint version = 1;
struct VRAM {
uint size = 0x10000;
} vram;
} ppu1;
struct PPU2 {
uint version = 3;
} ppu2;
} system;
struct Video {
bool blurEmulation = true;
bool colorEmulation = true;
} video;
struct Hacks {
struct PPUFast {
bool enable = false;
bool noSpriteLimit = false;
bool hiresMode7 = false;
} ppuFast;
struct DSPFast {
bool enable = false;
} dspFast;
} hacks;
};
extern Configuration configuration;

View File

@ -3,6 +3,7 @@
namespace SuperFamicom { namespace SuperFamicom {
Settings settings; Settings settings;
#include "configuration.cpp"
auto Interface::information() -> Information { auto Interface::information() -> Information {
Information information; Information information;
@ -40,7 +41,7 @@ auto Interface::color(uint32 color) -> uint64 {
uint64 G = L * image::normalize(g, 5, 16); uint64 G = L * image::normalize(g, 5, 16);
uint64 B = L * image::normalize(b, 5, 16); uint64 B = L * image::normalize(b, 5, 16);
if(settings.colorEmulation) { if(SuperFamicom::configuration.video.colorEmulation) {
static const uint8 gammaRamp[32] = { static const uint8 gammaRamp[32] = {
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c, 0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78, 0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
@ -253,60 +254,20 @@ auto Interface::cheats(const vector<string>& list) -> void {
cheat.assign(list); cheat.assign(list);
} }
auto Interface::cap(const string& name) -> bool { auto Interface::configuration() -> string {
if(name == "Fast PPU") return true; return SuperFamicom::configuration.read();
if(name == "Fast PPU/No Sprite Limit") return true;
if(name == "Fast PPU/Hires Mode 7") return true;
if(name == "Fast DSP") return true;
if(name == "Mode") return true;
if(name == "Blur Emulation") return true;
if(name == "Color Emulation") return true;
if(name == "Scanline Emulation") return true;
return false;
} }
auto Interface::get(const string& name) -> any { auto Interface::configuration(string name) -> string {
if(name == "Fast PPU") return settings.fastPPU; return SuperFamicom::configuration.read(name);
if(name == "Fast PPU/No Sprite Limit") return settings.fastPPUNoSpriteLimit;
if(name == "Fast PPU/Hires Mode 7") return settings.fastPPUHiresMode7;
if(name == "Fast DSP") return settings.fastDSP;
if(name == "Blur Emulation") return settings.blurEmulation;
if(name == "Color Emulation") return settings.colorEmulation;
if(name == "Scanline Emulation") return settings.scanlineEmulation;
return {};
} }
auto Interface::set(const string& name, const any& value) -> bool { auto Interface::configure(string configuration) -> bool {
if(name == "Fast PPU" && value.is<bool>()) { return SuperFamicom::configuration.write(configuration);
settings.fastPPU = value.get<bool>(); }
return true;
} auto Interface::configure(string name, string value) -> bool {
if(name == "Fast PPU/No Sprite Limit" && value.is<bool>()) { return SuperFamicom::configuration.write(name, value);
settings.fastPPUNoSpriteLimit = value.get<bool>();
return true;
}
if(name == "Fast PPU/Hires Mode 7" && value.is<bool>()) {
settings.fastPPUHiresMode7 = value.get<bool>();
return true;
}
if(name == "Fast DSP" && value.is<bool>()) {
settings.fastDSP = value.get<bool>();
return true;
}
if(name == "Blur Emulation" && value.is<bool>()) {
settings.blurEmulation = value.get<bool>();
return true;
}
if(name == "Color Emulation" && value.is<bool>()) {
settings.colorEmulation = value.get<bool>();
Emulator::video.setPalette();
return true;
}
if(name == "Scanline Emulation" && value.is<bool>()) {
settings.scanlineEmulation = value.get<bool>();
return true;
}
return false;
} }
} }

View File

@ -64,21 +64,15 @@ struct Interface : Emulator::Interface {
auto cheats(const vector<string>&) -> void override; auto cheats(const vector<string>&) -> void override;
auto cap(const string& name) -> bool override; auto configuration() -> string override;
auto get(const string& name) -> any override; auto configuration(string name) -> string override;
auto set(const string& name, const any& value) -> bool override; auto configure(string configuration) -> bool override;
auto configure(string name, string value) -> bool override;
}; };
#include "configuration.hpp"
struct Settings { struct Settings {
bool fastPPU = false;
bool fastPPUNoSpriteLimit = false;
bool fastPPUHiresMode7 = false;
bool fastDSP = false;
bool blurEmulation = true;
bool colorEmulation = true;
bool scanlineEmulation = true;
uint controllerPort1 = ID::Device::Gamepad; uint controllerPort1 = ID::Device::Gamepad;
uint controllerPort2 = ID::Device::Gamepad; uint controllerPort2 = ID::Device::Gamepad;
uint expansionPort = ID::Device::None; uint expansionPort = ID::Device::None;

View File

@ -23,7 +23,7 @@ auto PPU::Line::render() -> void {
} }
bool hires = io.pseudoHires || io.bgMode == 5 || io.bgMode == 6; bool hires = io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
bool hiresMode7 = io.bgMode == 7 && settings.fastPPUHiresMode7; bool hiresMode7 = io.bgMode == 7 && configuration.hacks.ppuFast.hiresMode7;
auto aboveColor = cgram[0]; auto aboveColor = cgram[0];
auto belowColor = hires ? cgram[0] : io.col.fixedColor; auto belowColor = hires ? cgram[0] : io.col.fixedColor;
for(uint x : range(256 << hiresMode7)) { for(uint x : range(256 << hiresMode7)) {

View File

@ -25,7 +25,7 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void {
renderWindow(self.window, self.window.aboveEnable, windowAbove); renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow); renderWindow(self.window, self.window.belowEnable, windowBelow);
if(!settings.fastPPUHiresMode7) { if(!configuration.hacks.ppuFast.hiresMode7) {
for(int X : range(256)) { for(int X : range(256)) {
int x = !io.mode7.hflip ? X : 255 - X; int x = !io.mode7.hflip ? X : 255 - X;
int pixelX = originX + a * x >> 8; int pixelX = originX + a * x >> 8;

View File

@ -76,7 +76,7 @@ auto PPU::scanline() -> void {
if(vcounter() > 0 && vcounter() < vdisp()) { if(vcounter() > 0 && vcounter() < vdisp()) {
latch.hires |= io.pseudoHires || io.bgMode == 5 || io.bgMode == 6; latch.hires |= io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
latch.hires |= io.bgMode == 7 && settings.fastPPUHiresMode7; latch.hires |= io.bgMode == 7 && configuration.hacks.ppuFast.hiresMode7;
} }
if(vcounter() == vdisp() && !io.displayDisable) { if(vcounter() == vdisp() && !io.displayDisable) {
@ -95,16 +95,15 @@ auto PPU::refresh() -> void {
auto pitch = 512 << !interlace(); auto pitch = 512 << !interlace();
auto width = 256 << hires(); auto width = 256 << hires();
auto height = 240 << interlace(); auto height = 240 << interlace();
Emulator::video.setEffect(Emulator::Video::Effect::ColorBleed, settings.blurEmulation && hires()); Emulator::video.setEffect(Emulator::Video::Effect::ColorBleed, configuration.video.blurEmulation && hires());
Emulator::video.refresh(output, pitch * sizeof(uint32), width, height); Emulator::video.refresh(output, pitch * sizeof(uint32), width, height);
} }
auto PPU::load(Markup::Node node) -> bool { auto PPU::load() -> bool {
return true; return true;
} }
auto PPU::power(bool reset) -> void { auto PPU::power(bool reset) -> void {
//settings.fastPPUHiresMode7=false;
create(Enter, system.cpuFrequency()); create(Enter, system.cpuFrequency());
PPUcounter::reset(); PPUcounter::reset();
memory::fill<uint32>(output, 512 * 480); memory::fill<uint32>(output, 512 * 480);
@ -126,8 +125,8 @@ auto PPU::power(bool reset) -> void {
io = {}; io = {};
updateVideoMode(); updateVideoMode();
ItemLimit = !settings.fastPPUNoSpriteLimit ? 32 : 128; ItemLimit = !configuration.hacks.ppuFast.noSpriteLimit ? 32 : 128;
TileLimit = !settings.fastPPUNoSpriteLimit ? 34 : 128; TileLimit = !configuration.hacks.ppuFast.noSpriteLimit ? 34 : 128;
Line::start = 0; Line::start = 0;
Line::count = 0; Line::count = 0;

View File

@ -23,7 +23,7 @@ struct PPU : Thread, PPUcounter {
auto main() -> void; auto main() -> void;
auto scanline() -> void; auto scanline() -> void;
auto refresh() -> void; auto refresh() -> void;
auto load(Markup::Node) -> bool; auto load() -> bool;
auto power(bool reset) -> void; auto power(bool reset) -> void;
//serialization.cpp //serialization.cpp

View File

@ -81,15 +81,15 @@ auto PPU::main() -> void {
step(lineclocks() - hcounter()); step(lineclocks() - hcounter());
} }
auto PPU::load(Markup::Node node) -> bool { auto PPU::load() -> bool {
if(system.fastPPU()) { if(system.fastPPU()) {
return ppufast.load(node); return ppufast.load();
} }
ppu1.version = max(1, min(1, node["ppu1/version"].natural())); ppu1.version = max(1, min(1, configuration.system.ppu1.version));
ppu2.version = max(1, min(3, node["ppu2/version"].natural())); ppu2.version = max(1, min(3, configuration.system.ppu2.version));
ppu.vram.mask = node["ppu1/ram/size"].natural() / sizeof(uint16) - 1; vram.mask = configuration.system.ppu1.vram.size / sizeof(uint16) - 1;
if(ppu.vram.mask != 0xffff) ppu.vram.mask = 0x7fff; if(vram.mask != 0xffff) vram.mask = 0x7fff;
return true; return true;
} }
@ -243,7 +243,7 @@ auto PPU::refresh() -> void {
auto pitch = 512; auto pitch = 512;
auto width = 512; auto width = 512;
auto height = 480; auto height = 480;
Emulator::video.setEffect(Emulator::Video::Effect::ColorBleed, settings.blurEmulation); Emulator::video.setEffect(Emulator::Video::Effect::ColorBleed, configuration.video.blurEmulation);
Emulator::video.refresh(output, pitch * sizeof(uint32), width, height); Emulator::video.refresh(output, pitch * sizeof(uint32), width, height);
} }

View File

@ -9,7 +9,7 @@ struct PPU : Thread, PPUcounter {
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto load(Markup::Node) -> bool; auto load() -> bool;
auto power(bool reset) -> void; auto power(bool reset) -> void;
//io.cpp //io.cpp

View File

@ -18,12 +18,10 @@ auto SMP::main() -> void {
instruction(); instruction();
} }
auto SMP::load(Markup::Node node) -> bool { auto SMP::load() -> bool {
if(auto name = node["smp/rom/name"].text()) { if(auto fp = platform->open(ID::System, "ipl.rom", File::Read, File::Required)) {
if(auto fp = platform->open(ID::System, name, File::Read, File::Required)) { fp->read(iplrom, 64);
fp->read(iplrom, 64); return true;
return true;
}
} }
return false; return false;
} }

View File

@ -10,7 +10,7 @@ struct SMP : Processor::SPC700, Thread {
//smp.cpp //smp.cpp
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto load(Markup::Node) -> bool; auto load() -> bool;
auto power(bool reset) -> void; auto power(bool reset) -> void;
//serialization.cpp //serialization.cpp

View File

@ -32,11 +32,9 @@ auto System::unserialize(serializer& s) -> bool {
s.boolean(hacks.fastPPU); s.boolean(hacks.fastPPU);
s.boolean(hacks.fastDSP); s.boolean(hacks.fastDSP);
settings.fastPPU = hacks.fastPPU;
settings.fastDSP = hacks.fastDSP;
power(/* reset = */ false); power(/* reset = */ false);
serializeAll(s); serializeAll(s);
serializeInit(); //hacks.fastPPU setting changes serializeSize
return true; return true;
} }

View File

@ -23,19 +23,14 @@ auto System::runToSave() -> void {
auto System::load(Emulator::Interface* interface) -> bool { auto System::load(Emulator::Interface* interface) -> bool {
information = {}; information = {};
hacks.fastPPU = configuration.hacks.ppuFast.enable;
if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) { hacks.fastDSP = configuration.hacks.dspFast.enable;
information.manifest = fp->reads();
} else return false;
auto document = BML::unserialize(information.manifest);
auto system = document["system"];
bus.reset(); bus.reset();
if(!cpu.load(system)) return false; if(!cpu.load()) return false;
if(!smp.load(system)) return false; if(!smp.load()) return false;
if(!ppu.load(system)) return false; if(!ppu.load()) return false;
if(!dsp.load(system)) return false; if(!dsp.load()) return false;
if(!cartridge.load()) return false; if(!cartridge.load()) return false;
if(cartridge.region() == "NTSC") { if(cartridge.region() == "NTSC") {
@ -88,9 +83,6 @@ auto System::unload() -> void {
} }
auto System::power(bool reset) -> void { auto System::power(bool reset) -> void {
hacks.fastPPU = settings.fastPPU;
hacks.fastDSP = settings.fastDSP;
Emulator::video.reset(interface); Emulator::video.reset(interface);
Emulator::video.setPalette(); Emulator::video.setPalette();

View File

@ -25,7 +25,6 @@ private:
Emulator::Interface* interface = nullptr; Emulator::Interface* interface = nullptr;
struct Information { struct Information {
string manifest;
bool loaded = false; bool loaded = false;
Region region = Region::NTSC; Region region = Region::NTSC;
double cpuFrequency = Emulator::Constants::Colorburst::NTSC * 6.0; double cpuFrequency = Emulator::Constants::Colorburst::NTSC * 6.0;

View File

@ -1,12 +0,0 @@
system name:Super Famicom
cpu version=2
ram name=work.ram size=0x20000 volatile
smp
rom name=ipl.rom size=64
ppu1 version=1
ram name=video.ram size=0x10000 volatile
ram name=object.ram size=544 volatile
ppu2 version=3
ram name=palette.ram size=512 volatile
dsp
ram name=apu.ram size=0x10000 volatile

View File

@ -53,7 +53,7 @@ Presentation::Presentation() {
}); });
blurEmulation.setText("Blur Emulation").setChecked(settings["View/BlurEmulation"].boolean()).onToggle([&] { blurEmulation.setText("Blur Emulation").setChecked(settings["View/BlurEmulation"].boolean()).onToggle([&] {
settings["View/BlurEmulation"].setValue(blurEmulation.checked()); settings["View/BlurEmulation"].setValue(blurEmulation.checked());
emulator->set("Blur Emulation", blurEmulation.checked()); emulator->configure("video/blurEmulation", blurEmulation.checked());
}).doToggle(); }).doToggle();
shaderMenu.setIcon(Icon::Emblem::Image).setText("Shader"); shaderMenu.setIcon(Icon::Emblem::Image).setText("Shader");
synchronizeVideo.setText("Synchronize Video").setChecked(settings["Video/Blocking"].boolean()).onToggle([&] { synchronizeVideo.setText("Synchronize Video").setChecked(settings["Video/Blocking"].boolean()).onToggle([&] {

View File

@ -1,5 +1,10 @@
auto Program::load() -> void { auto Program::load() -> void {
unload(); unload();
if(auto configuration = string::read(locate("configuration.bml"))) {
emulator->configure(configuration);
settingsWindow->advanced.updateConfiguration();
}
if(!emulator->load()) return; if(!emulator->load()) return;
gameQueue = {}; gameQueue = {};
@ -282,6 +287,9 @@ auto Program::unload() -> void {
if(settingsWindow->advanced.autoSaveStateOnUnload.checked()) { if(settingsWindow->advanced.autoSaveStateOnUnload.checked()) {
saveUndoState(); saveUndoState();
} }
if(auto configuration = emulator->configuration()) {
file::write(locate("configuration.bml"), configuration);
}
emulator->unload(); emulator->unload();
showMessage("Game unloaded"); showMessage("Game unloaded");
superFamicom = {}; superFamicom = {};

View File

@ -11,10 +11,6 @@ auto Program::open(uint id, string name, vfs::file::mode mode, bool required) ->
vfs::shared::file result; vfs::shared::file result;
if(id == 0) { //System if(id == 0) { //System
if(name == "manifest.bml" && mode == vfs::file::mode::read) {
result = vfs::memory::file::open(Resource::System::Manifest.data(), Resource::System::Manifest.size());
}
if(name == "boards.bml" && mode == vfs::file::mode::read) { if(name == "boards.bml" && mode == vfs::file::mode::read) {
result = vfs::memory::file::open(Resource::System::Boards.data(), Resource::System::Boards.size()); result = vfs::memory::file::open(Resource::System::Boards.data(), Resource::System::Boards.size());
} }

View File

@ -45,7 +45,7 @@ auto Program::updateVideoShader() -> void {
} }
auto Program::updateVideoPalette() -> void { auto Program::updateVideoPalette() -> void {
emulator->set("Color Emulation", false); emulator->configure("video/colorEmulation", false);
double luminance = settings["Video/Luminance"].natural() / 100.0; double luminance = settings["Video/Luminance"].natural() / 100.0;
double saturation = settings["Video/Saturation"].natural() / 100.0; double saturation = settings["Video/Saturation"].natural() / 100.0;
double gamma = settings["Video/Gamma"].natural() / 100.0; double gamma = settings["Video/Gamma"].natural() / 100.0;

View File

@ -2,6 +2,5 @@ namespace name=Resource
binary name=Icon file=icon.png binary name=Icon file=icon.png
binary name=Logo file=logo.png binary name=Logo file=logo.png
namespace name=System namespace name=System
binary name=Manifest file="../../systems/Super Famicom.sys/manifest.bml"
binary name=Boards file="../../systems/Super Famicom.sys/boards.bml" binary name=Boards file="../../systems/Super Famicom.sys/boards.bml"
binary name=IPLROM file="../../systems/Super Famicom.sys/ipl.rom" binary name=IPLROM file="../../systems/Super Famicom.sys/ipl.rom"

View File

@ -850,19 +850,6 @@ const nall::vector<uint8_t> Logo = { //size: 23467
0,0,0,73,69,78,68,174,66,96,130, 0,0,0,73,69,78,68,174,66,96,130,
}; };
namespace System { namespace System {
const nall::vector<uint8_t> Manifest = { //size: 334
115,121,115,116,101,109,32,110,97,109,101,58,83,117,112,101,114,32,70,97,109,105,99,111,109,10,32,32,99,112,117,32,
118,101,114,115,105,111,110,61,50,10,32,32,32,32,114,97,109,32,110,97,109,101,61,119,111,114,107,46,114,97,109,32,
115,105,122,101,61,48,120,50,48,48,48,48,32,118,111,108,97,116,105,108,101,10,32,32,115,109,112,10,32,32,32,32,
114,111,109,32,110,97,109,101,61,105,112,108,46,114,111,109,32,115,105,122,101,61,54,52,10,32,32,112,112,117,49,32,
118,101,114,115,105,111,110,61,49,10,32,32,32,32,114,97,109,32,110,97,109,101,61,118,105,100,101,111,46,114,97,109,
32,115,105,122,101,61,48,120,49,48,48,48,48,32,118,111,108,97,116,105,108,101,10,32,32,32,32,114,97,109,32,110,
97,109,101,61,111,98,106,101,99,116,46,114,97,109,32,115,105,122,101,61,53,52,52,32,118,111,108,97,116,105,108,101,
10,32,32,112,112,117,50,32,118,101,114,115,105,111,110,61,51,10,32,32,32,32,114,97,109,32,110,97,109,101,61,112,
97,108,101,116,116,101,46,114,97,109,32,115,105,122,101,61,53,49,50,32,118,111,108,97,116,105,108,101,10,32,32,100,
115,112,10,32,32,32,32,114,97,109,32,110,97,109,101,61,97,112,117,46,114,97,109,32,115,105,122,101,61,48,120,49,
48,48,48,48,32,118,111,108,97,116,105,108,101,10,
};
const nall::vector<uint8_t> Boards = { //size: 30182 const nall::vector<uint8_t> Boards = { //size: 30182
100,97,116,97,98,97,115,101,10,32,32,114,101,118,105,115,105,111,110,58,32,50,48,49,56,45,48,55,45,50,53,10, 100,97,116,97,98,97,115,101,10,32,32,114,101,118,105,115,105,111,110,58,32,50,48,49,56,45,48,55,45,50,53,10,
10,47,47,66,111,97,114,100,115,32,40,80,114,111,100,117,99,116,105,111,110,41,10,10,100,97,116,97,98,97,115,101, 10,47,47,66,111,97,114,100,115,32,40,80,114,111,100,117,99,116,105,111,110,41,10,10,100,97,116,97,98,97,115,101,

View File

@ -2,7 +2,6 @@ namespace Resource {
extern const nall::vector<uint8_t> Icon; extern const nall::vector<uint8_t> Icon;
extern const nall::vector<uint8_t> Logo; extern const nall::vector<uint8_t> Logo;
namespace System { namespace System {
extern const nall::vector<uint8_t> Manifest;
extern const nall::vector<uint8_t> Boards; extern const nall::vector<uint8_t> Boards;
extern const nall::vector<uint8_t> IPLROM; extern const nall::vector<uint8_t> IPLROM;
} }

View File

@ -97,7 +97,6 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
}); });
hiresMode7.setText("Hires mode 7").setChecked(settings["Emulator/Hack/FastPPU/HiresMode7"].boolean()).onToggle([&] { hiresMode7.setText("Hires mode 7").setChecked(settings["Emulator/Hack/FastPPU/HiresMode7"].boolean()).onToggle([&] {
settings["Emulator/Hack/FastPPU/HiresMode7"].setValue(hiresMode7.checked()); settings["Emulator/Hack/FastPPU/HiresMode7"].setValue(hiresMode7.checked());
emulator->set("Fast PPU/Hires Mode 7", hiresMode7.checked());
}); });
fastDSPOption.setText("Fast DSP").setChecked(settings["Emulator/Hack/FastDSP"].boolean()).onToggle([&] { fastDSPOption.setText("Fast DSP").setChecked(settings["Emulator/Hack/FastDSP"].boolean()).onToggle([&] {
settings["Emulator/Hack/FastDSP"].setValue(fastDSPOption.checked()); settings["Emulator/Hack/FastDSP"].setValue(fastDSPOption.checked());
@ -140,3 +139,10 @@ auto AdvancedSettings::updateInputDriver() -> void {
if(input && input->driver() == driver) item.setSelected(); if(input && input->driver() == driver) item.setSelected();
} }
} }
auto AdvancedSettings::updateConfiguration() -> void {
emulator->configure("hacks/ppuFast/enable", fastPPUOption.checked());
emulator->configure("hacks/ppuFast/noSpriteLimit", noSpriteLimit.checked());
emulator->configure("hacks/ppuFast/hiresMode7", hiresMode7.checked());
emulator->configure("hacks/dspFast/enable", fastDSPOption.checked());
}

View File

@ -34,7 +34,6 @@ Settings::Settings() {
set("Audio/Skew", "0"); set("Audio/Skew", "0");
set("Audio/Volume", "100%"); set("Audio/Volume", "100%");
set("Audio/Balance", "50%"); set("Audio/Balance", "50%");
set("Audio/Reverb", false);
set("Input/Driver", Input::safestDriver()); set("Input/Driver", Input::safestDriver());
set("Input/Frequency", 5); set("Input/Frequency", 5);

View File

@ -157,6 +157,7 @@ struct AdvancedSettings : TabFrameItem {
auto updateVideoDriver() -> void; auto updateVideoDriver() -> void;
auto updateAudioDriver() -> void; auto updateAudioDriver() -> void;
auto updateInputDriver() -> void; auto updateInputDriver() -> void;
auto updateConfiguration() -> void;
public: public:
VerticalLayout layout{this}; VerticalLayout layout{this};

View File

@ -19,6 +19,14 @@ auto Program::load(Emulator::Interface& interface) -> void {
gamePaths.append(locate({"systems/", information.name, ".sys/"})); gamePaths.append(locate({"systems/", information.name, ".sys/"}));
inputManager->bind(emulator = &interface); inputManager->bind(emulator = &interface);
if(auto configuration = string::read({gamePaths[0], "configuration.bml"})) {
emulator->configure(configuration);
}
if(auto configuration = emulator->configuration()) {
file::write({gamePaths[0], "configuration.bml"}, configuration);
}
presentation->updateEmulatorMenu(); presentation->updateEmulatorMenu();
if(!emulator->load()) { if(!emulator->load()) {
emulator = nullptr; emulator = nullptr;