mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
22bd4b9277
commit
876b4be1d2
|
@ -13,7 +13,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
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 License = "GPLv3";
|
||||
static const string Website = "https://byuu.org/";
|
||||
|
|
|
@ -87,6 +87,12 @@ struct Interface {
|
|||
//cheat functions
|
||||
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
|
||||
virtual auto cap(const string& name) -> bool { return false; }
|
||||
virtual auto get(const string& name) -> any { return {}; }
|
||||
|
|
|
@ -5,144 +5,145 @@ namespace MegaDrive {
|
|||
Cartridge cartridge;
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto Cartridge::region() const -> string {
|
||||
return game.region;
|
||||
}
|
||||
|
||||
auto Cartridge::hashes() const -> vector<string> {
|
||||
vector<string> hashes;
|
||||
hashes.append(game.hash);
|
||||
if(lockOn.hash) hashes.append(lockOn.hash);
|
||||
hashes.append(information.hash);
|
||||
if(slot) for(auto& hash : slot->hashes()) hashes.append(hash);
|
||||
return hashes;
|
||||
}
|
||||
|
||||
auto Cartridge::manifests() const -> vector<string> {
|
||||
vector<string> manifests;
|
||||
manifests.append(game.manifest);
|
||||
if(lockOn.manifest) manifests.append(lockOn.manifest);
|
||||
manifests.append(information.manifest);
|
||||
if(slot) for(auto& manifest : slot->manifests()) manifests.append(manifest);
|
||||
return manifests;
|
||||
}
|
||||
|
||||
auto Cartridge::titles() const -> vector<string> {
|
||||
vector<string> titles;
|
||||
titles.append(game.title);
|
||||
if(lockOn.title) titles.append(lockOn.title);
|
||||
titles.append(information.title);
|
||||
if(slot) for(auto& title : slot->titles()) titles.append(title);
|
||||
return titles;
|
||||
}
|
||||
|
||||
auto Cartridge::load() -> bool {
|
||||
game = {};
|
||||
lockOn = {};
|
||||
read.reset();
|
||||
write.reset();
|
||||
unload();
|
||||
|
||||
if(!loadGame()) {
|
||||
game = {};
|
||||
return false;
|
||||
if(auto loaded = platform->load(ID::MegaDrive, "Mega Drive", "md", {"Auto", "NTSC-J", "NTSC-U", "PAL"})) {
|
||||
information.pathID = loaded.pathID;
|
||||
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};
|
||||
write = {&Cartridge::writeGame, this};
|
||||
if(!loadROM(patch, information.document["game/board/memory(type=ROM,content=Patch)"])) {
|
||||
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};
|
||||
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;
|
||||
}
|
||||
|
||||
auto Cartridge::loadGame() -> bool {
|
||||
if(auto loaded = platform->load(ID::MegaDrive, "Mega Drive", "md", {"Auto", "NTSC-J", "NTSC-U", "PAL"})) {
|
||||
game.pathID = loaded.pathID;
|
||||
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::save() -> void {
|
||||
saveRAM(ram, information.document["game/board/memory(type=RAM,content=Save)"]);
|
||||
if(slot) slot->save();
|
||||
}
|
||||
|
||||
auto Cartridge::loadLockOn() -> bool {
|
||||
if(auto loaded = platform->load(ID::MegaDrive, "Mega Drive", "md")) {
|
||||
lockOn.pathID = loaded.pathID;
|
||||
} else return false;
|
||||
|
||||
if(auto fp = platform->open(lockOn.pathID, "manifest.bml", File::Read, File::Required)) {
|
||||
lockOn.manifest = fp->reads();
|
||||
} else return false;
|
||||
|
||||
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::unload() -> void {
|
||||
rom.reset();
|
||||
patch.reset();
|
||||
ram.reset();
|
||||
read.reset();
|
||||
write.reset();
|
||||
if(slot) slot->unload();
|
||||
slot.reset();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
auto name = string{memory["content"].text(), ".", memory["type"].text()}.downcase();
|
||||
rom.size = memory["size"].natural() >> 1;
|
||||
rom.mask = bit::round(rom.size) - 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);
|
||||
} else return false;
|
||||
|
||||
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;
|
||||
|
||||
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.data = new uint16[ram.mask + 1]();
|
||||
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)) {
|
||||
if(ram.bits != 0xffff) ram.data[n] = fp->readm(1) * 0x0101;
|
||||
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;
|
||||
}
|
||||
|
||||
auto Cartridge::save() -> void {
|
||||
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 {
|
||||
auto Cartridge::saveRAM(Memory& ram, Markup::Node memory) -> bool {
|
||||
if(!memory) return false;
|
||||
if((bool)memory["volatile"]) return true;
|
||||
|
||||
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)) {
|
||||
if(ram.bits != 0xffff) fp->writem(ram.data[n], 1);
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
if(slot) slot->readIO(address);
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
auto Cartridge::writeIO(uint24 address, uint16 data) -> void {
|
||||
if(address == 0xa130f1) ramEnable = data.bit(0), ramWritable = data.bit(1);
|
||||
if(address == 0xa130f3) bank[1] = data;
|
||||
if(address == 0xa130f5) bank[2] = data;
|
||||
if(address == 0xa130f7) bank[3] = data;
|
||||
if(address == 0xa130f9) bank[4] = data;
|
||||
if(address == 0xa130fb) bank[5] = data;
|
||||
if(address == 0xa130fd) bank[6] = data;
|
||||
if(address == 0xa130ff) bank[7] = data;
|
||||
if(address == 0xa130f3) romBank[1] = data;
|
||||
if(address == 0xa130f5) romBank[2] = data;
|
||||
if(address == 0xa130f7) romBank[3] = data;
|
||||
if(address == 0xa130f9) romBank[4] = data;
|
||||
if(address == 0xa130fb) romBank[5] = data;
|
||||
if(address == 0xa130fd) romBank[6] = data;
|
||||
if(address == 0xa130ff) romBank[7] = data;
|
||||
if(slot) slot->writeIO(address, data);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
auto Cartridge::readGame(uint24 address) -> uint16 {
|
||||
if(address >= 0x200000 && game.ram.size && ramEnable) {
|
||||
return game.ram.data[address >> 1 & game.ram.mask];
|
||||
} else {
|
||||
address = bank[address.bits(19,21)] << 19 | address.bits(0,18);
|
||||
return game.rom.data[address >> 1 & game.rom.mask];
|
||||
}
|
||||
auto Cartridge::readLinear(uint22 address) -> uint16 {
|
||||
if(ramEnable && ram && address >= 0x200000) return ram.read(address);
|
||||
return rom.read(address);
|
||||
}
|
||||
|
||||
auto Cartridge::writeGame(uint24 address, uint16 data) -> void {
|
||||
//emulating RAM write protect bit breaks some commercial software
|
||||
if(address >= 0x200000 && game.ram.size && ramEnable /* && ramWritable */) {
|
||||
if(game.ram.bits == 0x00ff) data = data.byte(0) * 0x0101;
|
||||
if(game.ram.bits == 0xff00) data = data.byte(1) * 0x0101;
|
||||
game.ram.data[address >> 1 & game.ram.mask] = data;
|
||||
auto Cartridge::writeLinear(uint22 address, uint16 data) -> void {
|
||||
//emulating ramWritable will break commercial software:
|
||||
//it does not appear that many (any?) games actually connect $a130f1.d1 to /WE;
|
||||
//hence RAM ends up always being writable, and many games fail to set d1=1
|
||||
if(ramEnable && ram && address >= 0x200000) return ram.write(address, 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 {
|
||||
if(address >= 0x200000 && lockOn.ram.size && ramEnable) {
|
||||
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];
|
||||
Cartridge::Memory::operator bool() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
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 {
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
|
@ -280,4 +283,16 @@ auto Cartridge::Memory::reset() -> void {
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,73 +1,87 @@
|
|||
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 manifests() const -> vector<string>;
|
||||
auto titles() const -> vector<string>;
|
||||
|
||||
struct Memory;
|
||||
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 saveRAM(Memory& ram, uint pathID, Markup::Node memory) -> bool;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
function<uint16 (uint24 address)> read;
|
||||
function<void (uint24 address, uint16 data)> write;
|
||||
auto loadROM(Memory& rom, Markup::Node memory) -> bool;
|
||||
auto loadRAM(Memory& ram, Markup::Node memory) -> bool;
|
||||
auto saveRAM(Memory& ram, Markup::Node memory) -> bool;
|
||||
|
||||
auto readIO(uint24 address) -> uint16;
|
||||
auto writeIO(uint24 address, uint16 data) -> void;
|
||||
|
||||
auto readGame(uint24 address) -> uint16;
|
||||
auto writeGame(uint24 address, uint16 data) -> void;
|
||||
auto readLinear(uint22 address) -> uint16;
|
||||
auto writeLinear(uint22 address, uint16 data) -> void;
|
||||
|
||||
auto readLockOn(uint24 address) -> uint16;
|
||||
auto writeLockOn(uint24 address, uint16 data) -> void;
|
||||
auto readBanked(uint22 address) -> uint16;
|
||||
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
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct Memory {
|
||||
auto reset() -> void;
|
||||
|
||||
uint16* data = nullptr;
|
||||
uint size = 0;
|
||||
uint mask = 0;
|
||||
uint bits = 0;
|
||||
};
|
||||
|
||||
struct Game {
|
||||
struct Information {
|
||||
uint pathID = 0;
|
||||
string region;
|
||||
string hash;
|
||||
string manifest;
|
||||
string title;
|
||||
|
||||
Markup::Node document;
|
||||
Memory rom;
|
||||
Memory patch;
|
||||
Memory ram;
|
||||
} game;
|
||||
};
|
||||
|
||||
struct LockOn {
|
||||
uint pathID = 0;
|
||||
string hash;
|
||||
string manifest;
|
||||
string title;
|
||||
struct Memory {
|
||||
explicit operator bool() const;
|
||||
auto reset() -> void;
|
||||
auto read(uint24 address) -> uint16;
|
||||
auto write(uint24 address, uint16 word) -> void;
|
||||
|
||||
Markup::Node document;
|
||||
Memory rom;
|
||||
Memory ram;
|
||||
uint16* data = nullptr;
|
||||
uint size = 0; //16-bit word size
|
||||
uint mask = 0;
|
||||
uint bits = 0;
|
||||
};
|
||||
|
||||
bool patch = false;
|
||||
} lockOn;
|
||||
Information information;
|
||||
|
||||
Memory rom;
|
||||
Memory patch;
|
||||
Memory ram;
|
||||
|
||||
uint1 ramEnable;
|
||||
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;
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
auto Cartridge::serialize(serializer& s) -> void {
|
||||
if(game.ram.size) s.array(game.ram.data, game.ram.size);
|
||||
if(lockOn.ram.size) s.array(lockOn.ram.data, lockOn.ram.size);
|
||||
if(ram.size) s.array(ram.data, ram.size);
|
||||
s.integer(ramEnable);
|
||||
s.integer(ramWritable);
|
||||
s.array(romBank);
|
||||
if(slot) slot->serialize(s);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
#include <emulator/random.hpp>
|
||||
#include <emulator/cheat.hpp>
|
||||
|
||||
#include <processor/m68k/m68k.hpp>
|
||||
|
@ -15,8 +16,10 @@ namespace MegaDrive {
|
|||
#define platform Emulator::platform
|
||||
namespace File = Emulator::File;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Random = Emulator::Random;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
extern Random random;
|
||||
extern Cheat cheat;
|
||||
|
||||
struct Wait {
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace MegaDrive {
|
|||
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
Random random;
|
||||
Cheat cheat;
|
||||
#include "serialization.cpp"
|
||||
|
||||
|
@ -67,6 +68,8 @@ auto System::power(bool reset) -> void {
|
|||
|
||||
Emulator::audio.reset(interface);
|
||||
|
||||
random.entropy(Random::Entropy::High);
|
||||
|
||||
scheduler.reset();
|
||||
cartridge.power();
|
||||
cpu.power(reset);
|
||||
|
|
|
@ -44,8 +44,8 @@ auto CPU::main() -> void {
|
|||
instruction();
|
||||
}
|
||||
|
||||
auto CPU::load(Markup::Node node) -> bool {
|
||||
version = node["cpu/version"].natural();
|
||||
auto CPU::load() -> bool {
|
||||
version = configuration.system.cpu.version;
|
||||
if(version < 1) version = 1;
|
||||
if(version > 2) version = 2;
|
||||
return true;
|
||||
|
|
|
@ -6,7 +6,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
|
|||
//cpu.cpp
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto load() -> bool;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//dma.cpp
|
||||
|
|
|
@ -234,7 +234,7 @@ auto DSP::write(uint8 addr, uint8 data) -> void {
|
|||
|
||||
/* initialization */
|
||||
|
||||
auto DSP::load(Markup::Node node) -> bool {
|
||||
auto DSP::load() -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ struct DSP : Thread {
|
|||
auto write(uint8 addr, uint8 data) -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto load() -> bool;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -3,6 +3,7 @@
|
|||
namespace SuperFamicom {
|
||||
|
||||
Settings settings;
|
||||
#include "configuration.cpp"
|
||||
|
||||
auto Interface::information() -> Information {
|
||||
Information information;
|
||||
|
@ -40,7 +41,7 @@ auto Interface::color(uint32 color) -> uint64 {
|
|||
uint64 G = L * image::normalize(g, 5, 16);
|
||||
uint64 B = L * image::normalize(b, 5, 16);
|
||||
|
||||
if(settings.colorEmulation) {
|
||||
if(SuperFamicom::configuration.video.colorEmulation) {
|
||||
static const uint8 gammaRamp[32] = {
|
||||
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
||||
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
||||
|
@ -253,60 +254,20 @@ auto Interface::cheats(const vector<string>& list) -> void {
|
|||
cheat.assign(list);
|
||||
}
|
||||
|
||||
auto Interface::cap(const string& name) -> bool {
|
||||
if(name == "Fast PPU") return true;
|
||||
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::configuration() -> string {
|
||||
return SuperFamicom::configuration.read();
|
||||
}
|
||||
|
||||
auto Interface::get(const string& name) -> any {
|
||||
if(name == "Fast PPU") return settings.fastPPU;
|
||||
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::configuration(string name) -> string {
|
||||
return SuperFamicom::configuration.read(name);
|
||||
}
|
||||
|
||||
auto Interface::set(const string& name, const any& value) -> bool {
|
||||
if(name == "Fast PPU" && value.is<bool>()) {
|
||||
settings.fastPPU = value.get<bool>();
|
||||
return true;
|
||||
}
|
||||
if(name == "Fast PPU/No Sprite Limit" && value.is<bool>()) {
|
||||
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;
|
||||
auto Interface::configure(string configuration) -> bool {
|
||||
return SuperFamicom::configuration.write(configuration);
|
||||
}
|
||||
|
||||
auto Interface::configure(string name, string value) -> bool {
|
||||
return SuperFamicom::configuration.write(name, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -64,21 +64,15 @@ struct Interface : Emulator::Interface {
|
|||
|
||||
auto cheats(const vector<string>&) -> void override;
|
||||
|
||||
auto cap(const string& name) -> bool override;
|
||||
auto get(const string& name) -> any override;
|
||||
auto set(const string& name, const any& value) -> bool override;
|
||||
auto configuration() -> string override;
|
||||
auto configuration(string name) -> string override;
|
||||
auto configure(string configuration) -> bool override;
|
||||
auto configure(string name, string value) -> bool override;
|
||||
};
|
||||
|
||||
#include "configuration.hpp"
|
||||
|
||||
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 controllerPort2 = ID::Device::Gamepad;
|
||||
uint expansionPort = ID::Device::None;
|
||||
|
|
|
@ -23,7 +23,7 @@ auto PPU::Line::render() -> void {
|
|||
}
|
||||
|
||||
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 belowColor = hires ? cgram[0] : io.col.fixedColor;
|
||||
for(uint x : range(256 << hiresMode7)) {
|
||||
|
|
|
@ -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.belowEnable, windowBelow);
|
||||
|
||||
if(!settings.fastPPUHiresMode7) {
|
||||
if(!configuration.hacks.ppuFast.hiresMode7) {
|
||||
for(int X : range(256)) {
|
||||
int x = !io.mode7.hflip ? X : 255 - X;
|
||||
int pixelX = originX + a * x >> 8;
|
||||
|
|
|
@ -76,7 +76,7 @@ auto PPU::scanline() -> void {
|
|||
|
||||
if(vcounter() > 0 && vcounter() < vdisp()) {
|
||||
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) {
|
||||
|
@ -95,16 +95,15 @@ auto PPU::refresh() -> void {
|
|||
auto pitch = 512 << !interlace();
|
||||
auto width = 256 << hires();
|
||||
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);
|
||||
}
|
||||
|
||||
auto PPU::load(Markup::Node node) -> bool {
|
||||
auto PPU::load() -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto PPU::power(bool reset) -> void {
|
||||
//settings.fastPPUHiresMode7=false;
|
||||
create(Enter, system.cpuFrequency());
|
||||
PPUcounter::reset();
|
||||
memory::fill<uint32>(output, 512 * 480);
|
||||
|
@ -126,8 +125,8 @@ auto PPU::power(bool reset) -> void {
|
|||
io = {};
|
||||
updateVideoMode();
|
||||
|
||||
ItemLimit = !settings.fastPPUNoSpriteLimit ? 32 : 128;
|
||||
TileLimit = !settings.fastPPUNoSpriteLimit ? 34 : 128;
|
||||
ItemLimit = !configuration.hacks.ppuFast.noSpriteLimit ? 32 : 128;
|
||||
TileLimit = !configuration.hacks.ppuFast.noSpriteLimit ? 34 : 128;
|
||||
|
||||
Line::start = 0;
|
||||
Line::count = 0;
|
||||
|
|
|
@ -23,7 +23,7 @@ struct PPU : Thread, PPUcounter {
|
|||
auto main() -> void;
|
||||
auto scanline() -> void;
|
||||
auto refresh() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto load() -> bool;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
|
|
|
@ -81,15 +81,15 @@ auto PPU::main() -> void {
|
|||
step(lineclocks() - hcounter());
|
||||
}
|
||||
|
||||
auto PPU::load(Markup::Node node) -> bool {
|
||||
auto PPU::load() -> bool {
|
||||
if(system.fastPPU()) {
|
||||
return ppufast.load(node);
|
||||
return ppufast.load();
|
||||
}
|
||||
|
||||
ppu1.version = max(1, min(1, node["ppu1/version"].natural()));
|
||||
ppu2.version = max(1, min(3, node["ppu2/version"].natural()));
|
||||
ppu.vram.mask = node["ppu1/ram/size"].natural() / sizeof(uint16) - 1;
|
||||
if(ppu.vram.mask != 0xffff) ppu.vram.mask = 0x7fff;
|
||||
ppu1.version = max(1, min(1, configuration.system.ppu1.version));
|
||||
ppu2.version = max(1, min(3, configuration.system.ppu2.version));
|
||||
vram.mask = configuration.system.ppu1.vram.size / sizeof(uint16) - 1;
|
||||
if(vram.mask != 0xffff) vram.mask = 0x7fff;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -243,7 +243,7 @@ auto PPU::refresh() -> void {
|
|||
auto pitch = 512;
|
||||
auto width = 512;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ struct PPU : Thread, PPUcounter {
|
|||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto load() -> bool;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//io.cpp
|
||||
|
|
|
@ -18,12 +18,10 @@ auto SMP::main() -> void {
|
|||
instruction();
|
||||
}
|
||||
|
||||
auto SMP::load(Markup::Node node) -> bool {
|
||||
if(auto name = node["smp/rom/name"].text()) {
|
||||
if(auto fp = platform->open(ID::System, name, File::Read, File::Required)) {
|
||||
fp->read(iplrom, 64);
|
||||
return true;
|
||||
}
|
||||
auto SMP::load() -> bool {
|
||||
if(auto fp = platform->open(ID::System, "ipl.rom", File::Read, File::Required)) {
|
||||
fp->read(iplrom, 64);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ struct SMP : Processor::SPC700, Thread {
|
|||
//smp.cpp
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto load() -> bool;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
|
|
|
@ -32,11 +32,9 @@ auto System::unserialize(serializer& s) -> bool {
|
|||
s.boolean(hacks.fastPPU);
|
||||
s.boolean(hacks.fastDSP);
|
||||
|
||||
settings.fastPPU = hacks.fastPPU;
|
||||
settings.fastDSP = hacks.fastDSP;
|
||||
|
||||
power(/* reset = */ false);
|
||||
serializeAll(s);
|
||||
serializeInit(); //hacks.fastPPU setting changes serializeSize
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,19 +23,14 @@ auto System::runToSave() -> void {
|
|||
|
||||
auto System::load(Emulator::Interface* interface) -> bool {
|
||||
information = {};
|
||||
|
||||
if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
|
||||
information.manifest = fp->reads();
|
||||
} else return false;
|
||||
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
auto system = document["system"];
|
||||
hacks.fastPPU = configuration.hacks.ppuFast.enable;
|
||||
hacks.fastDSP = configuration.hacks.dspFast.enable;
|
||||
|
||||
bus.reset();
|
||||
if(!cpu.load(system)) return false;
|
||||
if(!smp.load(system)) return false;
|
||||
if(!ppu.load(system)) return false;
|
||||
if(!dsp.load(system)) return false;
|
||||
if(!cpu.load()) return false;
|
||||
if(!smp.load()) return false;
|
||||
if(!ppu.load()) return false;
|
||||
if(!dsp.load()) return false;
|
||||
if(!cartridge.load()) return false;
|
||||
|
||||
if(cartridge.region() == "NTSC") {
|
||||
|
@ -88,9 +83,6 @@ auto System::unload() -> void {
|
|||
}
|
||||
|
||||
auto System::power(bool reset) -> void {
|
||||
hacks.fastPPU = settings.fastPPU;
|
||||
hacks.fastDSP = settings.fastDSP;
|
||||
|
||||
Emulator::video.reset(interface);
|
||||
Emulator::video.setPalette();
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ private:
|
|||
Emulator::Interface* interface = nullptr;
|
||||
|
||||
struct Information {
|
||||
string manifest;
|
||||
bool loaded = false;
|
||||
Region region = Region::NTSC;
|
||||
double cpuFrequency = Emulator::Constants::Colorburst::NTSC * 6.0;
|
||||
|
|
|
@ -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
|
|
@ -53,7 +53,7 @@ Presentation::Presentation() {
|
|||
});
|
||||
blurEmulation.setText("Blur Emulation").setChecked(settings["View/BlurEmulation"].boolean()).onToggle([&] {
|
||||
settings["View/BlurEmulation"].setValue(blurEmulation.checked());
|
||||
emulator->set("Blur Emulation", blurEmulation.checked());
|
||||
emulator->configure("video/blurEmulation", blurEmulation.checked());
|
||||
}).doToggle();
|
||||
shaderMenu.setIcon(Icon::Emblem::Image).setText("Shader");
|
||||
synchronizeVideo.setText("Synchronize Video").setChecked(settings["Video/Blocking"].boolean()).onToggle([&] {
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
auto Program::load() -> void {
|
||||
unload();
|
||||
|
||||
if(auto configuration = string::read(locate("configuration.bml"))) {
|
||||
emulator->configure(configuration);
|
||||
settingsWindow->advanced.updateConfiguration();
|
||||
}
|
||||
if(!emulator->load()) return;
|
||||
|
||||
gameQueue = {};
|
||||
|
@ -282,6 +287,9 @@ auto Program::unload() -> void {
|
|||
if(settingsWindow->advanced.autoSaveStateOnUnload.checked()) {
|
||||
saveUndoState();
|
||||
}
|
||||
if(auto configuration = emulator->configuration()) {
|
||||
file::write(locate("configuration.bml"), configuration);
|
||||
}
|
||||
emulator->unload();
|
||||
showMessage("Game unloaded");
|
||||
superFamicom = {};
|
||||
|
|
|
@ -11,10 +11,6 @@ auto Program::open(uint id, string name, vfs::file::mode mode, bool required) ->
|
|||
vfs::shared::file result;
|
||||
|
||||
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) {
|
||||
result = vfs::memory::file::open(Resource::System::Boards.data(), Resource::System::Boards.size());
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ auto Program::updateVideoShader() -> void {
|
|||
}
|
||||
|
||||
auto Program::updateVideoPalette() -> void {
|
||||
emulator->set("Color Emulation", false);
|
||||
emulator->configure("video/colorEmulation", false);
|
||||
double luminance = settings["Video/Luminance"].natural() / 100.0;
|
||||
double saturation = settings["Video/Saturation"].natural() / 100.0;
|
||||
double gamma = settings["Video/Gamma"].natural() / 100.0;
|
||||
|
|
|
@ -2,6 +2,5 @@ namespace name=Resource
|
|||
binary name=Icon file=icon.png
|
||||
binary name=Logo file=logo.png
|
||||
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=IPLROM file="../../systems/Super Famicom.sys/ipl.rom"
|
||||
|
|
|
@ -850,19 +850,6 @@ const nall::vector<uint8_t> Logo = { //size: 23467
|
|||
0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
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
|
||||
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,
|
||||
|
|
|
@ -2,7 +2,6 @@ namespace Resource {
|
|||
extern const nall::vector<uint8_t> Icon;
|
||||
extern const nall::vector<uint8_t> Logo;
|
||||
namespace System {
|
||||
extern const nall::vector<uint8_t> Manifest;
|
||||
extern const nall::vector<uint8_t> Boards;
|
||||
extern const nall::vector<uint8_t> IPLROM;
|
||||
}
|
||||
|
|
|
@ -97,7 +97,6 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
});
|
||||
hiresMode7.setText("Hires mode 7").setChecked(settings["Emulator/Hack/FastPPU/HiresMode7"].boolean()).onToggle([&] {
|
||||
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([&] {
|
||||
settings["Emulator/Hack/FastDSP"].setValue(fastDSPOption.checked());
|
||||
|
@ -140,3 +139,10 @@ auto AdvancedSettings::updateInputDriver() -> void {
|
|||
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());
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ Settings::Settings() {
|
|||
set("Audio/Skew", "0");
|
||||
set("Audio/Volume", "100%");
|
||||
set("Audio/Balance", "50%");
|
||||
set("Audio/Reverb", false);
|
||||
|
||||
set("Input/Driver", Input::safestDriver());
|
||||
set("Input/Frequency", 5);
|
||||
|
|
|
@ -157,6 +157,7 @@ struct AdvancedSettings : TabFrameItem {
|
|||
auto updateVideoDriver() -> void;
|
||||
auto updateAudioDriver() -> void;
|
||||
auto updateInputDriver() -> void;
|
||||
auto updateConfiguration() -> void;
|
||||
|
||||
public:
|
||||
VerticalLayout layout{this};
|
||||
|
|
|
@ -19,6 +19,14 @@ auto Program::load(Emulator::Interface& interface) -> void {
|
|||
gamePaths.append(locate({"systems/", information.name, ".sys/"}));
|
||||
|
||||
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();
|
||||
if(!emulator->load()) {
|
||||
emulator = nullptr;
|
||||
|
|
Loading…
Reference in New Issue