mirror of https://github.com/bsnes-emu/bsnes.git
Update to v094r39 release.
byuu says: Changelog: - SNES mid-scanline BGMODE fixes finally merged (can run atx2.zip{mode7.smc}+mtest(2).sfc properly now) - Makefile now discards all built-in rules and variables - switch on bool warning disabled for GCC now as well (was already disabled for Clang) - when loading a game, if any required files are missing, display a warning message box (manifest.bml, program.rom, bios.rom, etc) - when loading a game (or a game slot), if manifest.bml is missing, it will invoke icarus to try and generate it - if that fails (icarus is missing or the folder is bad), you will get a warning telling you that the manifest can't be loaded The warning prompt on missing files work for both games and the .sys folders and their files. For some reason, failing to load the DMG/CGB BIOS is causing a crash before I can display the modal dialog. I have no idea why, and the stack frame backtrace is junk. I also can't seem to abort the failed loading process. If I call Program::unloadMedia(), I get a nasty segfault. Again with a really nasty stack trace. So for now, it'll just end up sitting there emulating an empty ROM (solid black screen.) In time, I'd like to fix that too. Lastly, I need a better method than popen for Windows. popen is kind of ugly and flashes a console window for a brief second even if the application launched is linked with -mwindows. Not sure if there even is one (I need to read the stdout result, so CreateProcess may not work unless I do something nasty like "> %tmp%/temp") I'm also using the regular popen instead of _wpopen, so for this WIP, it won't work if your game folder has non-English letters in the path.
This commit is contained in:
parent
1b0b54a690
commit
0271d6a12b
|
@ -8,7 +8,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "094.38";
|
static const string Version = "094.39";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
|
|
@ -47,8 +47,8 @@ struct Interface {
|
||||||
vector<Port> port;
|
vector<Port> port;
|
||||||
|
|
||||||
struct Bind {
|
struct Bind {
|
||||||
virtual auto loadRequest(unsigned, string, string) -> void {}
|
virtual auto loadRequest(unsigned, string, string, bool) -> void {}
|
||||||
virtual auto loadRequest(unsigned, string) -> void {}
|
virtual auto loadRequest(unsigned, string, bool) -> void {}
|
||||||
virtual auto saveRequest(unsigned, string) -> void {}
|
virtual auto saveRequest(unsigned, string) -> void {}
|
||||||
virtual auto videoColor(unsigned, uint16_t, uint16_t, uint16_t, uint16_t) -> uint32_t { return 0u; }
|
virtual auto videoColor(unsigned, uint16_t, uint16_t, uint16_t, uint16_t) -> uint32_t { return 0u; }
|
||||||
virtual auto videoRefresh(const uint32_t*, const uint32_t*, unsigned, unsigned, unsigned) -> void {}
|
virtual auto videoRefresh(const uint32_t*, const uint32_t*, unsigned, unsigned, unsigned) -> void {}
|
||||||
|
@ -62,8 +62,8 @@ struct Interface {
|
||||||
Bind* bind = nullptr;
|
Bind* bind = nullptr;
|
||||||
|
|
||||||
//callback bindings (provided by user interface)
|
//callback bindings (provided by user interface)
|
||||||
auto loadRequest(unsigned id, string name, string type) -> void { return bind->loadRequest(id, name, type); }
|
auto loadRequest(unsigned id, string name, string type, bool required) -> void { return bind->loadRequest(id, name, type, required); }
|
||||||
auto loadRequest(unsigned id, string path) -> void { return bind->loadRequest(id, path); }
|
auto loadRequest(unsigned id, string path, bool required) -> void { return bind->loadRequest(id, path, required); }
|
||||||
auto saveRequest(unsigned id, string path) -> void { return bind->saveRequest(id, path); }
|
auto saveRequest(unsigned id, string path) -> void { return bind->saveRequest(id, path); }
|
||||||
auto videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue) -> uint32_t { return bind->videoColor(source, alpha, red, green, blue); }
|
auto videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue) -> uint32_t { return bind->videoColor(source, alpha, red, green, blue); }
|
||||||
auto videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height) -> void { return bind->videoRefresh(palette, data, pitch, width, height); }
|
auto videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height) -> void { return bind->videoRefresh(palette, data, pitch, width, height); }
|
||||||
|
|
|
@ -104,10 +104,10 @@ Board::Board(Markup::Node& document) {
|
||||||
if(chrrom.size) chrrom.data = new uint8[chrrom.size]();
|
if(chrrom.size) chrrom.data = new uint8[chrrom.size]();
|
||||||
if(chrram.size) chrram.data = new uint8[chrram.size]();
|
if(chrram.size) chrram.data = new uint8[chrram.size]();
|
||||||
|
|
||||||
if(auto name = prom["name"].text()) interface->loadRequest(ID::ProgramROM, name);
|
if(auto name = prom["name"].text()) interface->loadRequest(ID::ProgramROM, name, true);
|
||||||
if(auto name = pram["name"].text()) interface->loadRequest(ID::ProgramRAM, name);
|
if(auto name = pram["name"].text()) interface->loadRequest(ID::ProgramRAM, name, false);
|
||||||
if(auto name = crom["name"].text()) interface->loadRequest(ID::CharacterROM, name);
|
if(auto name = crom["name"].text()) interface->loadRequest(ID::CharacterROM, name, true);
|
||||||
if(auto name = cram["name"].text()) interface->loadRequest(ID::CharacterRAM, name);
|
if(auto name = cram["name"].text()) interface->loadRequest(ID::CharacterRAM, name, false);
|
||||||
|
|
||||||
if(auto name = pram["name"].text()) Famicom::cartridge.memory.append({ID::ProgramRAM, name});
|
if(auto name = pram["name"].text()) Famicom::cartridge.memory.append({ID::ProgramRAM, name});
|
||||||
if(auto name = cram["name"].text()) Famicom::cartridge.memory.append({ID::CharacterRAM, name});
|
if(auto name = cram["name"].text()) Famicom::cartridge.memory.append({ID::CharacterRAM, name});
|
||||||
|
|
|
@ -19,7 +19,7 @@ void Cartridge::main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::load() {
|
void Cartridge::load() {
|
||||||
interface->loadRequest(ID::Manifest, "manifest.bml");
|
interface->loadRequest(ID::Manifest, "manifest.bml", true);
|
||||||
|
|
||||||
Board::load(information.markup); //this call will set Cartridge::board if successful
|
Board::load(information.markup); //this call will set Cartridge::board if successful
|
||||||
if(board == nullptr) return;
|
if(board == nullptr) return;
|
||||||
|
|
|
@ -26,6 +26,8 @@ string Interface::sha256() {
|
||||||
|
|
||||||
unsigned Interface::group(unsigned id) {
|
unsigned Interface::group(unsigned id) {
|
||||||
switch(id) {
|
switch(id) {
|
||||||
|
case ID::SystemManifest:
|
||||||
|
return 0;
|
||||||
case ID::Manifest:
|
case ID::Manifest:
|
||||||
case ID::ProgramROM:
|
case ID::ProgramROM:
|
||||||
case ID::ProgramRAM:
|
case ID::ProgramRAM:
|
||||||
|
@ -48,7 +50,13 @@ void Interface::save() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::load(unsigned id, const stream& stream) {
|
void Interface::load(unsigned id, const stream& stream) {
|
||||||
if(id == ID::Manifest) cartridge.information.markup = stream.text();
|
if(id == ID::SystemManifest) {
|
||||||
|
system.information.manifest = stream.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(id == ID::Manifest) {
|
||||||
|
cartridge.information.markup = stream.text();
|
||||||
|
}
|
||||||
|
|
||||||
if(id == ID::ProgramROM) {
|
if(id == ID::ProgramROM) {
|
||||||
stream.read(cartridge.board->prgrom.data, min(cartridge.board->prgrom.size, stream.size()));
|
stream.read(cartridge.board->prgrom.data, min(cartridge.board->prgrom.size, stream.size()));
|
||||||
|
|
|
@ -9,6 +9,8 @@ struct ID {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum : unsigned {
|
enum : unsigned {
|
||||||
|
SystemManifest,
|
||||||
|
|
||||||
Manifest,
|
Manifest,
|
||||||
ProgramROM,
|
ProgramROM,
|
||||||
ProgramRAM,
|
ProgramRAM,
|
||||||
|
|
|
@ -42,8 +42,8 @@ void System::runthreadtosave() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::load() {
|
void System::load() {
|
||||||
string manifest = string::read({interface->path(ID::System), "manifest.bml"});
|
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||||
auto document = BML::unserialize(manifest);
|
auto document = BML::unserialize(information.manifest);
|
||||||
|
|
||||||
serialize_init();
|
serialize_init();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,10 @@ struct System {
|
||||||
void serialize_all(serializer&);
|
void serialize_all(serializer&);
|
||||||
void serialize_init();
|
void serialize_init();
|
||||||
unsigned serialize_size;
|
unsigned serialize_size;
|
||||||
|
|
||||||
|
struct Information {
|
||||||
|
string manifest;
|
||||||
|
} information;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern System system;
|
extern System system;
|
||||||
|
|
|
@ -35,7 +35,7 @@ void Cartridge::load(System::Revision revision) {
|
||||||
|
|
||||||
system.revision = revision; //needed for ID::Manifest to return correct group ID
|
system.revision = revision; //needed for ID::Manifest to return correct group ID
|
||||||
if(revision != System::Revision::SuperGameBoy) {
|
if(revision != System::Revision::SuperGameBoy) {
|
||||||
interface->loadRequest(ID::Manifest, "manifest.bml");
|
interface->loadRequest(ID::Manifest, "manifest.bml", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
information.mapper = Mapper::Unknown;
|
information.mapper = Mapper::Unknown;
|
||||||
|
@ -74,8 +74,8 @@ void Cartridge::load(System::Revision revision) {
|
||||||
|
|
||||||
//Super Game Boy core loads memory from Super Famicom core
|
//Super Game Boy core loads memory from Super Famicom core
|
||||||
if(revision != System::Revision::SuperGameBoy) {
|
if(revision != System::Revision::SuperGameBoy) {
|
||||||
if(auto name = rom["name"].text()) interface->loadRequest(ID::ROM, name);
|
if(auto name = rom["name"].text()) interface->loadRequest(ID::ROM, name, true);
|
||||||
if(auto name = ram["name"].text()) interface->loadRequest(ID::RAM, name);
|
if(auto name = ram["name"].text()) interface->loadRequest(ID::RAM, name, false);
|
||||||
if(auto name = ram["name"].text()) memory.append({ID::RAM, name});
|
if(auto name = ram["name"].text()) memory.append({ID::RAM, name});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ string Interface::sha256() {
|
||||||
|
|
||||||
unsigned Interface::group(unsigned id) {
|
unsigned Interface::group(unsigned id) {
|
||||||
switch(id) {
|
switch(id) {
|
||||||
|
case ID::SystemManifest:
|
||||||
case ID::GameBoyBootROM:
|
case ID::GameBoyBootROM:
|
||||||
case ID::SuperGameBoyBootROM:
|
case ID::SuperGameBoyBootROM:
|
||||||
case ID::GameBoyColorBootROM:
|
case ID::GameBoyColorBootROM:
|
||||||
|
@ -68,6 +69,10 @@ void Interface::save() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::load(unsigned id, const stream& stream) {
|
void Interface::load(unsigned id, const stream& stream) {
|
||||||
|
if(id == ID::SystemManifest) {
|
||||||
|
system.information.manifest = stream.text();
|
||||||
|
}
|
||||||
|
|
||||||
if(id == ID::GameBoyBootROM) {
|
if(id == ID::GameBoyBootROM) {
|
||||||
stream.read(system.bootROM.dmg, min( 256u, stream.size()));
|
stream.read(system.bootROM.dmg, min( 256u, stream.size()));
|
||||||
}
|
}
|
||||||
|
@ -80,7 +85,9 @@ void Interface::load(unsigned id, const stream& stream) {
|
||||||
stream.read(system.bootROM.cgb, min(2048u, stream.size()));
|
stream.read(system.bootROM.cgb, min(2048u, stream.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(id == ID::Manifest) cartridge.information.markup = stream.text();
|
if(id == ID::Manifest) {
|
||||||
|
cartridge.information.markup = stream.text();
|
||||||
|
}
|
||||||
|
|
||||||
if(id == ID::ROM) {
|
if(id == ID::ROM) {
|
||||||
stream.read(cartridge.romdata, min(cartridge.romsize, stream.size()));
|
stream.read(cartridge.romdata, min(cartridge.romsize, stream.size()));
|
||||||
|
|
|
@ -11,6 +11,7 @@ struct ID {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum : unsigned {
|
enum : unsigned {
|
||||||
|
SystemManifest,
|
||||||
GameBoyBootROM,
|
GameBoyBootROM,
|
||||||
SuperGameBoyBootROM,
|
SuperGameBoyBootROM,
|
||||||
GameBoyColorBootROM,
|
GameBoyColorBootROM,
|
||||||
|
|
|
@ -49,16 +49,14 @@ void System::load(Revision revision) {
|
||||||
serialize_init();
|
serialize_init();
|
||||||
if(revision == Revision::SuperGameBoy) return; //Super Famicom core loads boot ROM for SGB
|
if(revision == Revision::SuperGameBoy) return; //Super Famicom core loads boot ROM for SGB
|
||||||
|
|
||||||
string manifest = string::read({interface->path(ID::System), "manifest.bml"});
|
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||||
auto document = BML::unserialize(manifest);
|
auto document = BML::unserialize(information.manifest);
|
||||||
|
|
||||||
auto bootROM = document["system/cpu/rom/name"].text();
|
if(auto bootROM = document["system/cpu/rom/name"].text()) {
|
||||||
interface->loadRequest(
|
interface->loadRequest(
|
||||||
revision == Revision::GameBoy ? ID::GameBoyBootROM : ID::GameBoyColorBootROM,
|
revision == Revision::GameBoy ? ID::GameBoyBootROM : ID::GameBoyColorBootROM,
|
||||||
bootROM
|
bootROM, true
|
||||||
);
|
);
|
||||||
if(!file::exists({interface->path(ID::System), bootROM})) {
|
|
||||||
interface->notify("Error: required Game Boy firmware boot.rom not found.\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,10 @@ struct System {
|
||||||
void serialize_init();
|
void serialize_init();
|
||||||
|
|
||||||
System();
|
System();
|
||||||
|
|
||||||
|
struct Information {
|
||||||
|
string manifest;
|
||||||
|
} information;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <gb/interface/interface.hpp>
|
#include <gb/interface/interface.hpp>
|
||||||
|
|
|
@ -12,7 +12,7 @@ string Cartridge::title() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::load() {
|
void Cartridge::load() {
|
||||||
interface->loadRequest(ID::Manifest, "manifest.bml");
|
interface->loadRequest(ID::Manifest, "manifest.bml", true);
|
||||||
|
|
||||||
auto document = BML::unserialize(information.markup);
|
auto document = BML::unserialize(information.markup);
|
||||||
information.title = document["information/title"].text();
|
information.title = document["information/title"].text();
|
||||||
|
@ -20,7 +20,7 @@ void Cartridge::load() {
|
||||||
unsigned rom_size = 0;
|
unsigned rom_size = 0;
|
||||||
if(document["cartridge/rom"]) {
|
if(document["cartridge/rom"]) {
|
||||||
auto info = document["cartridge/rom"];
|
auto info = document["cartridge/rom"];
|
||||||
interface->loadRequest(ID::ROM, info["name"].text());
|
interface->loadRequest(ID::ROM, info["name"].text(), true);
|
||||||
rom_size = info["size"].decimal();
|
rom_size = info["size"].decimal();
|
||||||
for(unsigned addr = rom_size; addr < rom.size; addr++) {
|
for(unsigned addr = rom_size; addr < rom.size; addr++) {
|
||||||
rom.data[addr] = rom.data[Bus::mirror(addr, rom_size)];
|
rom.data[addr] = rom.data[Bus::mirror(addr, rom_size)];
|
||||||
|
@ -40,7 +40,7 @@ void Cartridge::load() {
|
||||||
ram.mask = ram.size - 1;
|
ram.mask = ram.size - 1;
|
||||||
for(unsigned n = 0; n < ram.size; n++) ram.data[n] = 0xff;
|
for(unsigned n = 0; n < ram.size; n++) ram.data[n] = 0xff;
|
||||||
|
|
||||||
interface->loadRequest(ID::RAM, info["name"].text());
|
interface->loadRequest(ID::RAM, info["name"].text(), false);
|
||||||
memory.append({ID::RAM, info["name"].text()});
|
memory.append({ID::RAM, info["name"].text()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ void Cartridge::load() {
|
||||||
eeprom.test = rom_size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
|
eeprom.test = rom_size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
|
||||||
for(unsigned n = 0; n < eeprom.size; n++) eeprom.data[n] = 0xff;
|
for(unsigned n = 0; n < eeprom.size; n++) eeprom.data[n] = 0xff;
|
||||||
|
|
||||||
interface->loadRequest(ID::EEPROM, info["name"].text());
|
interface->loadRequest(ID::EEPROM, info["name"].text(), false);
|
||||||
memory.append({ID::EEPROM, info["name"].text()});
|
memory.append({ID::EEPROM, info["name"].text()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ void Cartridge::load() {
|
||||||
if(!flashrom.id && flashrom.size == 64 * 1024) flashrom.id = 0x1cc2;
|
if(!flashrom.id && flashrom.size == 64 * 1024) flashrom.id = 0x1cc2;
|
||||||
if(!flashrom.id && flashrom.size == 128 * 1024) flashrom.id = 0x09c2;
|
if(!flashrom.id && flashrom.size == 128 * 1024) flashrom.id = 0x09c2;
|
||||||
|
|
||||||
interface->loadRequest(ID::FlashROM, info["name"].text());
|
interface->loadRequest(ID::FlashROM, info["name"].text(), false);
|
||||||
memory.append({ID::FlashROM, info["name"].text()});
|
memory.append({ID::FlashROM, info["name"].text()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ bool Interface::loaded() {
|
||||||
|
|
||||||
unsigned Interface::group(unsigned id) {
|
unsigned Interface::group(unsigned id) {
|
||||||
switch(id) {
|
switch(id) {
|
||||||
|
case ID::SystemManifest:
|
||||||
case ID::BIOS:
|
case ID::BIOS:
|
||||||
return ID::System;
|
return ID::System;
|
||||||
case ID::Manifest:
|
case ID::Manifest:
|
||||||
|
@ -46,11 +47,17 @@ void Interface::save() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::load(unsigned id, const stream& stream) {
|
void Interface::load(unsigned id, const stream& stream) {
|
||||||
|
if(id == ID::SystemManifest) {
|
||||||
|
system.information.manifest = stream.text();
|
||||||
|
}
|
||||||
|
|
||||||
if(id == ID::BIOS) {
|
if(id == ID::BIOS) {
|
||||||
stream.read(bios.data, min(bios.size, stream.size()));
|
stream.read(bios.data, min(bios.size, stream.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(id == ID::Manifest) cartridge.information.markup = stream.text();
|
if(id == ID::Manifest) {
|
||||||
|
cartridge.information.markup = stream.text();
|
||||||
|
}
|
||||||
|
|
||||||
if(id == ID::ROM) {
|
if(id == ID::ROM) {
|
||||||
stream.read(cartridge.rom.data, min(cartridge.rom.size, stream.size()));
|
stream.read(cartridge.rom.data, min(cartridge.rom.size, stream.size()));
|
||||||
|
|
|
@ -9,6 +9,7 @@ struct ID {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum : unsigned {
|
enum : unsigned {
|
||||||
|
SystemManifest,
|
||||||
BIOS,
|
BIOS,
|
||||||
|
|
||||||
Manifest,
|
Manifest,
|
||||||
|
|
|
@ -24,13 +24,11 @@ void System::power() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::load() {
|
void System::load() {
|
||||||
string manifest = string::read({interface->path(ID::System), "manifest.bml"});
|
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||||
auto document = BML::unserialize(manifest);
|
auto document = BML::unserialize(information.manifest);
|
||||||
|
|
||||||
auto bios = document["system/cpu/rom/name"].text();
|
if(auto bios = document["system/cpu/rom/name"].text()) {
|
||||||
interface->loadRequest(ID::BIOS, bios);
|
interface->loadRequest(ID::BIOS, bios, true);
|
||||||
if(!file::exists({interface->path(ID::System), bios})) {
|
|
||||||
interface->notify("Error: required Game Boy Advance firmware bios.rom not found.\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize_init();
|
serialize_init();
|
||||||
|
|
|
@ -31,6 +31,10 @@ struct System {
|
||||||
void serialize(serializer&);
|
void serialize(serializer&);
|
||||||
void serialize_all(serializer&);
|
void serialize_all(serializer&);
|
||||||
void serialize_init();
|
void serialize_init();
|
||||||
|
|
||||||
|
struct Information {
|
||||||
|
string manifest;
|
||||||
|
} information;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern BIOS bios;
|
extern BIOS bios;
|
||||||
|
|
|
@ -1353,12 +1353,14 @@ struct mListViewItem : mObject {
|
||||||
auto backgroundColor() const -> Color;
|
auto backgroundColor() const -> Color;
|
||||||
auto cell(unsigned position) const -> ListViewCell;
|
auto cell(unsigned position) const -> ListViewCell;
|
||||||
auto cells() const -> unsigned;
|
auto cells() const -> unsigned;
|
||||||
|
auto checkable() const -> bool;
|
||||||
auto checked() const -> bool;
|
auto checked() const -> bool;
|
||||||
auto foregroundColor() const -> Color;
|
auto foregroundColor() const -> Color;
|
||||||
auto remove() -> type& override;
|
auto remove() -> type& override;
|
||||||
auto remove(sListViewCell cell) -> type&;
|
auto remove(sListViewCell cell) -> type&;
|
||||||
auto selected() const -> bool;
|
auto selected() const -> bool;
|
||||||
auto setBackgroundColor(Color color = {}) -> type&;
|
auto setBackgroundColor(Color color = {}) -> type&;
|
||||||
|
auto setCheckable(bool checkable = true) -> type&;
|
||||||
auto setChecked(bool checked = true) -> type&;
|
auto setChecked(bool checked = true) -> type&;
|
||||||
auto setFocused() -> type& override;
|
auto setFocused() -> type& override;
|
||||||
auto setForegroundColor(Color color = {}) -> type&;
|
auto setForegroundColor(Color color = {}) -> type&;
|
||||||
|
@ -1369,6 +1371,7 @@ struct mListViewItem : mObject {
|
||||||
struct State {
|
struct State {
|
||||||
Color backgroundColor;
|
Color backgroundColor;
|
||||||
vector<sListViewCell> cells;
|
vector<sListViewCell> cells;
|
||||||
|
bool checkable = true;
|
||||||
bool checked = false;
|
bool checked = false;
|
||||||
Color foregroundColor;
|
Color foregroundColor;
|
||||||
bool selected = false;
|
bool selected = false;
|
||||||
|
|
|
@ -502,11 +502,13 @@ struct ListViewItem : sListViewItem {
|
||||||
auto backgroundColor() const { return self().backgroundColor(); }
|
auto backgroundColor() const { return self().backgroundColor(); }
|
||||||
auto cell(unsigned position) const { return self().cell(position); }
|
auto cell(unsigned position) const { return self().cell(position); }
|
||||||
auto cells() const { return self().cells(); }
|
auto cells() const { return self().cells(); }
|
||||||
|
auto checkable() const { return self().checkable(); }
|
||||||
auto checked() const { return self().checked(); }
|
auto checked() const { return self().checked(); }
|
||||||
auto foregroundColor() const { return self().foregroundColor(); }
|
auto foregroundColor() const { return self().foregroundColor(); }
|
||||||
auto remove(sListViewCell cell) { return self().remove(cell), *this; }
|
auto remove(sListViewCell cell) { return self().remove(cell), *this; }
|
||||||
auto selected() const { return self().selected(); }
|
auto selected() const { return self().selected(); }
|
||||||
auto setBackgroundColor(Color color = {}) { return self().setBackgroundColor(color), *this; }
|
auto setBackgroundColor(Color color = {}) { return self().setBackgroundColor(color), *this; }
|
||||||
|
auto setCheckable(bool checkable = true) { return self().setCheckable(checkable), *this; }
|
||||||
auto setChecked(bool checked = true) { return self().setChecked(checked), *this; }
|
auto setChecked(bool checked = true) { return self().setChecked(checked), *this; }
|
||||||
auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; }
|
auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; }
|
||||||
auto setSelected(bool selected = true) { return self().setSelected(selected), *this; }
|
auto setSelected(bool selected = true) { return self().setSelected(selected), *this; }
|
||||||
|
|
|
@ -26,8 +26,12 @@ auto mListViewItem::cells() const -> unsigned {
|
||||||
return state.cells.size();
|
return state.cells.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto mListViewItem::checkable() const -> bool {
|
||||||
|
return state.checkable;
|
||||||
|
}
|
||||||
|
|
||||||
auto mListViewItem::checked() const -> bool {
|
auto mListViewItem::checked() const -> bool {
|
||||||
return state.checked;
|
return state.checkable && state.checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mListViewItem::foregroundColor() const -> Color {
|
auto mListViewItem::foregroundColor() const -> Color {
|
||||||
|
@ -59,6 +63,12 @@ auto mListViewItem::setBackgroundColor(Color color) -> type& {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto mListViewItem::setCheckable(bool checkable) -> type& {
|
||||||
|
state.checkable = checkable;
|
||||||
|
signal(setCheckable, checkable);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
auto mListViewItem::setChecked(bool checked) -> type& {
|
auto mListViewItem::setChecked(bool checked) -> type& {
|
||||||
state.checked = checked;
|
state.checked = checked;
|
||||||
signal(setChecked, checked);
|
signal(setChecked, checked);
|
||||||
|
|
|
@ -163,7 +163,6 @@ auto BrowserDialogWindow::setPath(string path) -> void {
|
||||||
|
|
||||||
view.reset();
|
view.reset();
|
||||||
view.append(ListViewColumn().setExpandable());
|
view.append(ListViewColumn().setExpandable());
|
||||||
view.append(ListViewColumn().setForegroundColor({192, 128, 128}));
|
|
||||||
|
|
||||||
auto contents = directory::icontents(path);
|
auto contents = directory::icontents(path);
|
||||||
bool folderMode = state.action == "openFolder";
|
bool folderMode = state.action == "openFolder";
|
||||||
|
@ -175,7 +174,6 @@ auto BrowserDialogWindow::setPath(string path) -> void {
|
||||||
|
|
||||||
view.append(ListViewItem()
|
view.append(ListViewItem()
|
||||||
.append(ListViewCell().setText(content).setIcon(Icon::Emblem::Folder))
|
.append(ListViewCell().setText(content).setIcon(Icon::Emblem::Folder))
|
||||||
.append(ListViewCell().setText(octal(file_system_object::mode({path, content}) & 0777, 3L)))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +184,6 @@ auto BrowserDialogWindow::setPath(string path) -> void {
|
||||||
|
|
||||||
view.append(ListViewItem()
|
view.append(ListViewItem()
|
||||||
.append(ListViewCell().setText(content).setIcon(folderMode ? Icon::Action::Open : Icon::Emblem::File))
|
.append(ListViewCell().setText(content).setIcon(folderMode ? Icon::Action::Open : Icon::Emblem::File))
|
||||||
.append(ListViewCell().setText(octal(file_system_object::mode({path, content}) & 0777, 3L)))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ auto pListViewColumn::construct() -> void {
|
||||||
gtkCellToggle = gtk_cell_renderer_toggle_new();
|
gtkCellToggle = gtk_cell_renderer_toggle_new();
|
||||||
gtk_tree_view_column_pack_start(gtkColumn, gtkCellToggle, false);
|
gtk_tree_view_column_pack_start(gtkColumn, gtkCellToggle, false);
|
||||||
gtk_tree_view_column_set_attributes(gtkColumn, gtkCellToggle, "active", 0, nullptr);
|
gtk_tree_view_column_set_attributes(gtkColumn, gtkCellToggle, "active", 0, nullptr);
|
||||||
|
gtk_tree_view_column_set_cell_data_func(gtkColumn, GTK_CELL_RENDERER(gtkCellToggle), (GtkTreeCellDataFunc)ListView_cellRendererToggleDataFunc, (gpointer)_parent(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
gtkCellIcon = gtk_cell_renderer_pixbuf_new();
|
gtkCellIcon = gtk_cell_renderer_pixbuf_new();
|
||||||
|
|
|
@ -17,6 +17,9 @@ auto pListViewItem::remove(sListViewCell cell) -> void {
|
||||||
auto pListViewItem::setBackgroundColor(Color color) -> void {
|
auto pListViewItem::setBackgroundColor(Color color) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pListViewItem::setCheckable(bool checkable) -> void {
|
||||||
|
}
|
||||||
|
|
||||||
auto pListViewItem::setChecked(bool checked) -> void {
|
auto pListViewItem::setChecked(bool checked) -> void {
|
||||||
if(auto parent = _parent()) {
|
if(auto parent = _parent()) {
|
||||||
gtk_list_store_set(parent->gtkListStore, >kIter, 0, checked, -1);
|
gtk_list_store_set(parent->gtkListStore, >kIter, 0, checked, -1);
|
||||||
|
|
|
@ -8,6 +8,7 @@ struct pListViewItem : pObject {
|
||||||
auto append(sListViewCell cell) -> void;
|
auto append(sListViewCell cell) -> void;
|
||||||
auto remove(sListViewCell cell) -> void;
|
auto remove(sListViewCell cell) -> void;
|
||||||
auto setBackgroundColor(Color color) -> void;
|
auto setBackgroundColor(Color color) -> void;
|
||||||
|
auto setCheckable(bool checkable) -> void;
|
||||||
auto setChecked(bool checked) -> void;
|
auto setChecked(bool checked) -> void;
|
||||||
auto setFocused() -> void;
|
auto setFocused() -> void;
|
||||||
auto setForegroundColor(Color color) -> void;
|
auto setForegroundColor(Color color) -> void;
|
||||||
|
|
|
@ -9,6 +9,8 @@ static auto ListView_edit(GtkCellRendererText* renderer, const char* path, const
|
||||||
static auto ListView_headerActivate(GtkTreeViewColumn* column, pListView* p) -> void { return p->_doHeaderActivate(column); }
|
static auto ListView_headerActivate(GtkTreeViewColumn* column, pListView* p) -> void { return p->_doHeaderActivate(column); }
|
||||||
static auto ListView_mouseMoveEvent(GtkWidget*, GdkEvent*, pListView* p) -> signed { return p->_doMouseMove(); }
|
static auto ListView_mouseMoveEvent(GtkWidget*, GdkEvent*, pListView* p) -> signed { return p->_doMouseMove(); }
|
||||||
static auto ListView_popup(GtkTreeView*, pListView* p) -> void { return p->_doContext(); }
|
static auto ListView_popup(GtkTreeView*, pListView* p) -> void { return p->_doContext(); }
|
||||||
|
|
||||||
|
static auto ListView_cellRendererToggleDataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, pListView* p) -> void { return p->_doCellRendererToggleDataFunc(renderer, iter); }
|
||||||
static auto ListView_toggle(GtkCellRendererToggle*, const char* path, pListView* p) -> void { return p->_doToggle(path); }
|
static auto ListView_toggle(GtkCellRendererToggle*, const char* path, pListView* p) -> void { return p->_doToggle(path); }
|
||||||
|
|
||||||
auto pListView::construct() -> void {
|
auto pListView::construct() -> void {
|
||||||
|
@ -276,6 +278,15 @@ auto pListView::_doActivate() -> void {
|
||||||
if(!locked()) self().doActivate();
|
if(!locked()) self().doActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pListView::_doCellRendererToggleDataFunc(GtkCellRenderer* renderer, GtkTreeIter* iter) -> void {
|
||||||
|
auto path = gtk_tree_model_get_string_from_iter(gtkTreeModel, iter);
|
||||||
|
auto row = decimal(path);
|
||||||
|
if(auto item = self().item(row)) {
|
||||||
|
gtk_cell_renderer_set_visible(renderer, state().checkable && item->state.checkable);
|
||||||
|
}
|
||||||
|
g_free(path);
|
||||||
|
}
|
||||||
|
|
||||||
auto pListView::_doChange() -> void {
|
auto pListView::_doChange() -> void {
|
||||||
if(!locked()) _updateSelected();
|
if(!locked()) _updateSelected();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ struct pListView : pWidget {
|
||||||
auto _columnWidth(unsigned column) -> unsigned;
|
auto _columnWidth(unsigned column) -> unsigned;
|
||||||
auto _createModel() -> void;
|
auto _createModel() -> void;
|
||||||
auto _doActivate() -> void;
|
auto _doActivate() -> void;
|
||||||
|
auto _doCellRendererToggleDataFunc(GtkCellRenderer* renderer, GtkTreeIter* iter) -> void;
|
||||||
auto _doChange() -> void;
|
auto _doChange() -> void;
|
||||||
auto _doContext() -> void;
|
auto _doContext() -> void;
|
||||||
auto _doEdit(GtkCellRendererText* renderer, const char* path, const char* text) -> void;
|
auto _doEdit(GtkCellRendererText* renderer, const char* path, const char* text) -> void;
|
||||||
|
|
|
@ -18,7 +18,7 @@ auto pCheckLabel::destruct() -> void {
|
||||||
DestroyWindow(hwnd);
|
DestroyWindow(hwnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pCheckLabel::minimumSize() -> Size {
|
auto pCheckLabel::minimumSize() const -> Size {
|
||||||
auto size = pFont::size(hfont, state().text);
|
auto size = pFont::size(hfont, state().text);
|
||||||
return {size.width() + 20, size.height() + 4};
|
return {size.width() + 20, size.height() + 4};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace hiro {
|
||||||
struct pCheckLabel : pWidget {
|
struct pCheckLabel : pWidget {
|
||||||
Declare(CheckLabel, Widget)
|
Declare(CheckLabel, Widget)
|
||||||
|
|
||||||
auto minimumSize() -> Size;
|
auto minimumSize() const -> Size override;
|
||||||
auto setChecked(bool checked) -> void;
|
auto setChecked(bool checked) -> void;
|
||||||
auto setText(const string& text) -> void;
|
auto setText(const string& text) -> void;
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@ auto pListViewItem::remove(sListViewCell cell) -> void {
|
||||||
auto pListViewItem::setBackgroundColor(Color color) -> void {
|
auto pListViewItem::setBackgroundColor(Color color) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pListViewItem::setCheckable(bool checkable) -> void {
|
||||||
|
}
|
||||||
|
|
||||||
auto pListViewItem::setChecked(bool checked) -> void {
|
auto pListViewItem::setChecked(bool checked) -> void {
|
||||||
if(auto parent = _parent()) {
|
if(auto parent = _parent()) {
|
||||||
parent->lock();
|
parent->lock();
|
||||||
|
|
|
@ -8,6 +8,7 @@ struct pListViewItem : pObject {
|
||||||
auto append(sListViewCell cell) -> void;
|
auto append(sListViewCell cell) -> void;
|
||||||
auto remove(sListViewCell cell) -> void;
|
auto remove(sListViewCell cell) -> void;
|
||||||
auto setBackgroundColor(Color color) -> void;
|
auto setBackgroundColor(Color color) -> void;
|
||||||
|
auto setCheckable(bool checkable) -> void;
|
||||||
auto setChecked(bool checked) -> void;
|
auto setChecked(bool checked) -> void;
|
||||||
auto setFocused() -> void;
|
auto setFocused() -> void;
|
||||||
auto setForegroundColor(Color color) -> void;
|
auto setForegroundColor(Color color) -> void;
|
||||||
|
|
|
@ -275,7 +275,7 @@ auto pListView::onCustomDraw(LPARAM lparam) -> LRESULT {
|
||||||
HBRUSH brush = CreateSolidBrush(selected ? GetSysColor(COLOR_HIGHLIGHT) : CreateRGB(_backgroundColor(row, column)));
|
HBRUSH brush = CreateSolidBrush(selected ? GetSysColor(COLOR_HIGHLIGHT) : CreateRGB(_backgroundColor(row, column)));
|
||||||
FillRect(hdc, &rc, brush);
|
FillRect(hdc, &rc, brush);
|
||||||
DeleteObject(brush);
|
DeleteObject(brush);
|
||||||
if(state().checkable && column == 0) {
|
if(state().checkable && self().item(row).checkable() && column == 0) {
|
||||||
if(auto htheme = OpenThemeData(hwnd, L"BUTTON")) {
|
if(auto htheme = OpenThemeData(hwnd, L"BUTTON")) {
|
||||||
unsigned state = checked ? CBS_CHECKEDNORMAL : CBS_UNCHECKEDNORMAL;
|
unsigned state = checked ? CBS_CHECKEDNORMAL : CBS_UNCHECKEDNORMAL;
|
||||||
SIZE size;
|
SIZE size;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# disable built-in rules and variables
|
||||||
|
MAKEFLAGS := Rr
|
||||||
|
.SUFFIXES:
|
||||||
|
|
||||||
[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
|
[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
|
||||||
[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z
|
[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z
|
||||||
[0-9] = 0 1 2 3 4 5 6 7 8 9
|
[0-9] = 0 1 2 3 4 5 6 7 8 9
|
||||||
|
|
|
@ -37,11 +37,15 @@ namespace nall {
|
||||||
#elif defined(__GNUC__)
|
#elif defined(__GNUC__)
|
||||||
#define COMPILER_GCC
|
#define COMPILER_GCC
|
||||||
auto Intrinsics::compiler() -> Compiler { return Compiler::GCC; }
|
auto Intrinsics::compiler() -> Compiler { return Compiler::GCC; }
|
||||||
|
|
||||||
|
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
||||||
|
#pragma GCC diagnostic ignored "-Wpragmas"
|
||||||
|
#pragma GCC diagnostic ignored "-Wswitch-bool"
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
#define COMPILER_VISUALCPP
|
#define COMPILER_VISUALCPP
|
||||||
auto Intrinsics::compiler() -> Compiler { return Compiler::VisualCPP; }
|
auto Intrinsics::compiler() -> Compiler { return Compiler::VisualCPP; }
|
||||||
|
|
||||||
#pragma warning(disable:4996) //disable libc "deprecation" warnings
|
#pragma warning(disable:4996) //libc "deprecation" warnings
|
||||||
#else
|
#else
|
||||||
#warning "unable to detect compiler"
|
#warning "unable to detect compiler"
|
||||||
#define COMPILER_UNKNOWN
|
#define COMPILER_UNKNOWN
|
||||||
|
|
|
@ -340,6 +340,7 @@ template<typename... P> auto append(lstring& self, const string& value, P&&... p
|
||||||
inline auto append(lstring& self) -> lstring&;
|
inline auto append(lstring& self) -> lstring&;
|
||||||
inline auto find(const lstring& self, const string& source) -> maybe<unsigned>;
|
inline auto find(const lstring& self, const string& source) -> maybe<unsigned>;
|
||||||
inline auto ifind(const lstring& self, const string& source) -> maybe<unsigned>;
|
inline auto ifind(const lstring& self, const string& source) -> maybe<unsigned>;
|
||||||
|
inline auto match(const lstring& self, const string& pattern) -> lstring;
|
||||||
inline auto merge(const lstring& self, const string& separator) -> string;
|
inline auto merge(const lstring& self, const string& separator) -> string;
|
||||||
inline auto strip(lstring& self) -> lstring&;
|
inline auto strip(lstring& self) -> lstring&;
|
||||||
|
|
||||||
|
@ -368,6 +369,7 @@ struct lstring : vector<string> {
|
||||||
template<typename... P> auto append(P&&... p) -> type& { return nall::append(*this, forward<P>(p)...); }
|
template<typename... P> auto append(P&&... p) -> type& { return nall::append(*this, forward<P>(p)...); }
|
||||||
auto find(const string& source) const -> maybe<unsigned> { return nall::find(*this, source); }
|
auto find(const string& source) const -> maybe<unsigned> { return nall::find(*this, source); }
|
||||||
auto ifind(const string& source) const -> maybe<unsigned> { return nall::ifind(*this, source); }
|
auto ifind(const string& source) const -> maybe<unsigned> { return nall::ifind(*this, source); }
|
||||||
|
auto match(const string& pattern) const -> lstring { return nall::match(*this, pattern); }
|
||||||
auto merge(const string& separator) const -> string { return nall::merge(*this, separator); }
|
auto merge(const string& separator) const -> string { return nall::merge(*this, separator); }
|
||||||
auto strip() -> type& { return nall::strip(*this); }
|
auto strip() -> type& { return nall::strip(*this); }
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,14 @@ auto ifind(const lstring& self, const string& source) -> maybe<unsigned> {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto match(const lstring& self, const string& pattern) -> lstring {
|
||||||
|
lstring result;
|
||||||
|
for(unsigned n = 0; n < self.size(); n++) {
|
||||||
|
if(self[n].match(pattern)) result.append(self[n]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
auto merge(const lstring& self, const string& separator) -> string {
|
auto merge(const lstring& self, const string& separator) -> string {
|
||||||
string output;
|
string output;
|
||||||
for(unsigned n = 0; n < self.size(); n++) {
|
for(unsigned n = 0; n < self.size(); n++) {
|
||||||
|
|
|
@ -62,7 +62,7 @@ auto Cartridge::load() -> void {
|
||||||
information.title.sufamiTurboA = "";
|
information.title.sufamiTurboA = "";
|
||||||
information.title.sufamiTurboB = "";
|
information.title.sufamiTurboB = "";
|
||||||
|
|
||||||
interface->loadRequest(ID::Manifest, "manifest.bml");
|
interface->loadRequest(ID::Manifest, "manifest.bml", true);
|
||||||
parseMarkup(information.markup.cartridge);
|
parseMarkup(information.markup.cartridge);
|
||||||
|
|
||||||
//Super Game Boy
|
//Super Game Boy
|
||||||
|
@ -115,7 +115,7 @@ auto Cartridge::load() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::loadSuperGameBoy() -> void {
|
auto Cartridge::loadSuperGameBoy() -> void {
|
||||||
interface->loadRequest(ID::SuperGameBoyManifest, "manifest.bml");
|
interface->loadRequest(ID::SuperGameBoyManifest, "manifest.bml", true);
|
||||||
auto document = BML::unserialize(information.markup.gameBoy);
|
auto document = BML::unserialize(information.markup.gameBoy);
|
||||||
information.title.gameBoy = document["information/title"].text();
|
information.title.gameBoy = document["information/title"].text();
|
||||||
|
|
||||||
|
@ -125,13 +125,13 @@ auto Cartridge::loadSuperGameBoy() -> void {
|
||||||
GameBoy::cartridge.information.markup = information.markup.gameBoy;
|
GameBoy::cartridge.information.markup = information.markup.gameBoy;
|
||||||
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy);
|
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy);
|
||||||
|
|
||||||
if(auto name = rom["name"].text()) interface->loadRequest(ID::SuperGameBoyROM, name);
|
if(auto name = rom["name"].text()) interface->loadRequest(ID::SuperGameBoyROM, name, true);
|
||||||
if(auto name = ram["name"].text()) interface->loadRequest(ID::SuperGameBoyRAM, name);
|
if(auto name = ram["name"].text()) interface->loadRequest(ID::SuperGameBoyRAM, name, false);
|
||||||
if(auto name = ram["name"].text()) memory.append({ID::SuperGameBoyRAM, name});
|
if(auto name = ram["name"].text()) memory.append({ID::SuperGameBoyRAM, name});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::loadSatellaview() -> void {
|
auto Cartridge::loadSatellaview() -> void {
|
||||||
interface->loadRequest(ID::SatellaviewManifest, "manifest.bml");
|
interface->loadRequest(ID::SatellaviewManifest, "manifest.bml", true);
|
||||||
auto document = BML::unserialize(information.markup.satellaview);
|
auto document = BML::unserialize(information.markup.satellaview);
|
||||||
information.title.satellaview = document["information/title"].text();
|
information.title.satellaview = document["information/title"].text();
|
||||||
|
|
||||||
|
@ -140,14 +140,14 @@ auto Cartridge::loadSatellaview() -> void {
|
||||||
if(rom["name"]) {
|
if(rom["name"]) {
|
||||||
unsigned size = rom["size"].decimal();
|
unsigned size = rom["size"].decimal();
|
||||||
satellaviewcartridge.memory.map(allocate<uint8>(size, 0xff), size);
|
satellaviewcartridge.memory.map(allocate<uint8>(size, 0xff), size);
|
||||||
interface->loadRequest(ID::SatellaviewROM, rom["name"].text());
|
interface->loadRequest(ID::SatellaviewROM, rom["name"].text(), true);
|
||||||
|
|
||||||
satellaviewcartridge.readonly = (rom["type"].text() == "MaskROM");
|
satellaviewcartridge.readonly = (rom["type"].text() == "MaskROM");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::loadSufamiTurboA() -> void {
|
auto Cartridge::loadSufamiTurboA() -> void {
|
||||||
interface->loadRequest(ID::SufamiTurboSlotAManifest, "manifest.bml");
|
interface->loadRequest(ID::SufamiTurboSlotAManifest, "manifest.bml", true);
|
||||||
auto document = BML::unserialize(information.markup.sufamiTurboA);
|
auto document = BML::unserialize(information.markup.sufamiTurboA);
|
||||||
information.title.sufamiTurboA = document["information/title"].text();
|
information.title.sufamiTurboA = document["information/title"].text();
|
||||||
|
|
||||||
|
@ -157,23 +157,23 @@ auto Cartridge::loadSufamiTurboA() -> void {
|
||||||
if(rom["name"]) {
|
if(rom["name"]) {
|
||||||
unsigned size = rom["size"].decimal();
|
unsigned size = rom["size"].decimal();
|
||||||
sufamiturboA.rom.map(allocate<uint8>(size, 0xff), size);
|
sufamiturboA.rom.map(allocate<uint8>(size, 0xff), size);
|
||||||
interface->loadRequest(ID::SufamiTurboSlotAROM, rom["name"].text());
|
interface->loadRequest(ID::SufamiTurboSlotAROM, rom["name"].text(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ram["name"]) {
|
if(ram["name"]) {
|
||||||
unsigned size = ram["size"].decimal();
|
unsigned size = ram["size"].decimal();
|
||||||
sufamiturboA.ram.map(allocate<uint8>(size, 0xff), size);
|
sufamiturboA.ram.map(allocate<uint8>(size, 0xff), size);
|
||||||
interface->loadRequest(ID::SufamiTurboSlotARAM, ram["name"].text());
|
interface->loadRequest(ID::SufamiTurboSlotARAM, ram["name"].text(), false);
|
||||||
memory.append({ID::SufamiTurboSlotARAM, ram["name"].text()});
|
memory.append({ID::SufamiTurboSlotARAM, ram["name"].text()});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(document["cartridge/linkable"]) {
|
if(document["cartridge/linkable"]) {
|
||||||
interface->loadRequest(ID::SufamiTurboSlotB, "Sufami Turbo - Slot B", "st");
|
interface->loadRequest(ID::SufamiTurboSlotB, "Sufami Turbo - Slot B", "st", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::loadSufamiTurboB() -> void {
|
auto Cartridge::loadSufamiTurboB() -> void {
|
||||||
interface->loadRequest(ID::SufamiTurboSlotBManifest, "manifest.bml");
|
interface->loadRequest(ID::SufamiTurboSlotBManifest, "manifest.bml", true);
|
||||||
auto document = BML::unserialize(information.markup.sufamiTurboB);
|
auto document = BML::unserialize(information.markup.sufamiTurboB);
|
||||||
information.title.sufamiTurboB = document["information/title"].text();
|
information.title.sufamiTurboB = document["information/title"].text();
|
||||||
|
|
||||||
|
@ -183,13 +183,13 @@ auto Cartridge::loadSufamiTurboB() -> void {
|
||||||
if(rom["name"]) {
|
if(rom["name"]) {
|
||||||
unsigned size = rom["size"].decimal();
|
unsigned size = rom["size"].decimal();
|
||||||
sufamiturboB.rom.map(allocate<uint8>(size, 0xff), size);
|
sufamiturboB.rom.map(allocate<uint8>(size, 0xff), size);
|
||||||
interface->loadRequest(ID::SufamiTurboSlotBROM, rom["name"].text());
|
interface->loadRequest(ID::SufamiTurboSlotBROM, rom["name"].text(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ram["name"]) {
|
if(ram["name"]) {
|
||||||
unsigned size = ram["size"].decimal();
|
unsigned size = ram["size"].decimal();
|
||||||
sufamiturboB.ram.map(allocate<uint8>(size, 0xff), size);
|
sufamiturboB.ram.map(allocate<uint8>(size, 0xff), size);
|
||||||
interface->loadRequest(ID::SufamiTurboSlotBRAM, ram["name"].text());
|
interface->loadRequest(ID::SufamiTurboSlotBRAM, ram["name"].text(), false);
|
||||||
memory.append({ID::SufamiTurboSlotBRAM, ram["name"].text()});
|
memory.append({ID::SufamiTurboSlotBRAM, ram["name"].text()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ auto Cartridge::parseMarkupMemory(MappedRAM& ram, Markup::Node node, unsigned id
|
||||||
unsigned size = node["size"].decimal();
|
unsigned size = node["size"].decimal();
|
||||||
ram.map(allocate<uint8>(size, 0xff), size);
|
ram.map(allocate<uint8>(size, 0xff), size);
|
||||||
if(name) {
|
if(name) {
|
||||||
interface->loadRequest(id, name);
|
interface->loadRequest(id, name, !writable); //treat ROM as required; RAM as optional
|
||||||
if(writable) memory.append({id, name});
|
if(writable) memory.append({id, name});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,10 +83,10 @@ auto Cartridge::parseMarkupICD2(Markup::Node root) -> void {
|
||||||
icd2.revision = max(1, root["revision"].decimal());
|
icd2.revision = max(1, root["revision"].decimal());
|
||||||
|
|
||||||
GameBoy::cartridge.load_empty(GameBoy::System::Revision::SuperGameBoy);
|
GameBoy::cartridge.load_empty(GameBoy::System::Revision::SuperGameBoy);
|
||||||
interface->loadRequest(ID::SuperGameBoy, "Game Boy", "gb");
|
interface->loadRequest(ID::SuperGameBoy, "Game Boy", "gb", false);
|
||||||
|
|
||||||
string bootROMName = root["rom/name"].text();
|
string bootROMName = root["rom/name"].text();
|
||||||
interface->loadRequest(ID::SuperGameBoyBootROM, bootROMName);
|
interface->loadRequest(ID::SuperGameBoyBootROM, bootROMName, true);
|
||||||
|
|
||||||
for(auto node : root.find("map")) {
|
for(auto node : root.find("map")) {
|
||||||
if(node["id"].text() == "io") {
|
if(node["id"].text() == "io") {
|
||||||
|
@ -309,10 +309,10 @@ auto Cartridge::parseMarkupARMDSP(Markup::Node root) -> void {
|
||||||
string dataROMName = rom(1)["name"].text();
|
string dataROMName = rom(1)["name"].text();
|
||||||
string dataRAMName = ram(0)["name"].text();
|
string dataRAMName = ram(0)["name"].text();
|
||||||
|
|
||||||
interface->loadRequest(ID::ArmDSPPROM, programROMName);
|
interface->loadRequest(ID::ArmDSPPROM, programROMName, true);
|
||||||
interface->loadRequest(ID::ArmDSPDROM, dataROMName);
|
interface->loadRequest(ID::ArmDSPDROM, dataROMName, true);
|
||||||
if(dataRAMName.empty() == false) {
|
if(dataRAMName.empty() == false) {
|
||||||
interface->loadRequest(ID::ArmDSPRAM, dataRAMName);
|
interface->loadRequest(ID::ArmDSPRAM, dataRAMName, false);
|
||||||
memory.append({ID::ArmDSPRAM, dataRAMName});
|
memory.append({ID::ArmDSPRAM, dataRAMName});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,9 +344,9 @@ auto Cartridge::parseMarkupHitachiDSP(Markup::Node root, unsigned roms) -> void
|
||||||
string dataROMName = rom(1)["name"].text();
|
string dataROMName = rom(1)["name"].text();
|
||||||
string dataRAMName = ram(1)["name"].text();
|
string dataRAMName = ram(1)["name"].text();
|
||||||
|
|
||||||
interface->loadRequest(ID::HitachiDSPDROM, dataROMName);
|
interface->loadRequest(ID::HitachiDSPDROM, dataROMName, true);
|
||||||
if(dataRAMName.empty() == false) {
|
if(dataRAMName.empty() == false) {
|
||||||
interface->loadRequest(ID::HitachiDSPDRAM, dataRAMName);
|
interface->loadRequest(ID::HitachiDSPDRAM, dataRAMName, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto node : root.find("map")) {
|
for(auto node : root.find("map")) {
|
||||||
|
@ -394,19 +394,19 @@ auto Cartridge::parseMarkupNECDSP(Markup::Node root) -> void {
|
||||||
string dataRAMName = ram(0)["name"].text();
|
string dataRAMName = ram(0)["name"].text();
|
||||||
|
|
||||||
if(necdsp.revision == NECDSP::Revision::uPD7725) {
|
if(necdsp.revision == NECDSP::Revision::uPD7725) {
|
||||||
interface->loadRequest(ID::Nec7725DSPPROM, programROMName);
|
interface->loadRequest(ID::Nec7725DSPPROM, programROMName, true);
|
||||||
interface->loadRequest(ID::Nec7725DSPDROM, dataROMName);
|
interface->loadRequest(ID::Nec7725DSPDROM, dataROMName, true);
|
||||||
if(dataRAMName.empty() == false) {
|
if(dataRAMName.empty() == false) {
|
||||||
interface->loadRequest(ID::Nec7725DSPRAM, dataRAMName);
|
interface->loadRequest(ID::Nec7725DSPRAM, dataRAMName, false);
|
||||||
memory.append({ID::Nec7725DSPRAM, dataRAMName});
|
memory.append({ID::Nec7725DSPRAM, dataRAMName});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(necdsp.revision == NECDSP::Revision::uPD96050) {
|
if(necdsp.revision == NECDSP::Revision::uPD96050) {
|
||||||
interface->loadRequest(ID::Nec96050DSPPROM, programROMName);
|
interface->loadRequest(ID::Nec96050DSPPROM, programROMName, true);
|
||||||
interface->loadRequest(ID::Nec96050DSPDROM, dataROMName);
|
interface->loadRequest(ID::Nec96050DSPDROM, dataROMName, true);
|
||||||
if(dataRAMName.empty() == false) {
|
if(dataRAMName.empty() == false) {
|
||||||
interface->loadRequest(ID::Nec96050DSPRAM, dataRAMName);
|
interface->loadRequest(ID::Nec96050DSPRAM, dataRAMName, false);
|
||||||
memory.append({ID::Nec96050DSPRAM, dataRAMName});
|
memory.append({ID::Nec96050DSPRAM, dataRAMName});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,7 +431,7 @@ auto Cartridge::parseMarkupEpsonRTC(Markup::Node root) -> void {
|
||||||
hasEpsonRTC = true;
|
hasEpsonRTC = true;
|
||||||
|
|
||||||
string name = root["ram/name"].text();
|
string name = root["ram/name"].text();
|
||||||
interface->loadRequest(ID::EpsonRTC, name);
|
interface->loadRequest(ID::EpsonRTC, name, false);
|
||||||
memory.append({ID::EpsonRTC, name});
|
memory.append({ID::EpsonRTC, name});
|
||||||
|
|
||||||
for(auto node : root.find("map")) {
|
for(auto node : root.find("map")) {
|
||||||
|
@ -447,7 +447,7 @@ auto Cartridge::parseMarkupSharpRTC(Markup::Node root) -> void {
|
||||||
hasSharpRTC = true;
|
hasSharpRTC = true;
|
||||||
|
|
||||||
string name = root["ram/name"].text();
|
string name = root["ram/name"].text();
|
||||||
interface->loadRequest(ID::SharpRTC, name);
|
interface->loadRequest(ID::SharpRTC, name, false);
|
||||||
memory.append({ID::SharpRTC, name});
|
memory.append({ID::SharpRTC, name});
|
||||||
|
|
||||||
for(auto node : root.find("map")) {
|
for(auto node : root.find("map")) {
|
||||||
|
|
|
@ -5,19 +5,19 @@ namespace SuperFamicom {
|
||||||
|
|
||||||
Cheat cheat;
|
Cheat cheat;
|
||||||
|
|
||||||
void Cheat::reset() {
|
auto Cheat::reset() -> void {
|
||||||
codes.reset();
|
codes.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cheat::append(unsigned addr, unsigned data) {
|
auto Cheat::append(unsigned addr, unsigned data) -> void {
|
||||||
codes.append({addr, Unused, data});
|
codes.append({addr, Unused, data});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cheat::append(unsigned addr, unsigned comp, unsigned data) {
|
auto Cheat::append(unsigned addr, unsigned comp, unsigned data) -> void {
|
||||||
codes.append({addr, comp, data});
|
codes.append({addr, comp, data});
|
||||||
}
|
}
|
||||||
|
|
||||||
maybe<unsigned> Cheat::find(unsigned addr, unsigned comp) {
|
auto Cheat::find(unsigned addr, unsigned comp) -> maybe<unsigned> {
|
||||||
//WRAM mirroring: $00-3f,80-bf:0000-1fff -> $7e:0000-1fff
|
//WRAM mirroring: $00-3f,80-bf:0000-1fff -> $7e:0000-1fff
|
||||||
if((addr & 0x40e000) == 0x000000) addr = 0x7e0000 | (addr & 0x1fff);
|
if((addr & 0x40e000) == 0x000000) addr = 0x7e0000 | (addr & 0x1fff);
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ maybe<unsigned> Cheat::find(unsigned addr, unsigned comp) {
|
||||||
return code.data;
|
return code.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
struct Cheat {
|
struct Cheat {
|
||||||
|
enum : unsigned { Unused = ~0u };
|
||||||
|
|
||||||
|
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
|
||||||
|
|
||||||
|
auto reset() -> void;
|
||||||
|
auto append(unsigned addr, unsigned data) -> void;
|
||||||
|
auto append(unsigned addr, unsigned comp, unsigned data) -> void;
|
||||||
|
auto find(unsigned addr, unsigned comp) -> maybe<unsigned>;
|
||||||
|
|
||||||
struct Code {
|
struct Code {
|
||||||
unsigned addr;
|
unsigned addr;
|
||||||
unsigned comp;
|
unsigned comp;
|
||||||
unsigned data;
|
unsigned data;
|
||||||
};
|
};
|
||||||
vector<Code> codes;
|
vector<Code> codes;
|
||||||
enum : unsigned { Unused = ~0u };
|
|
||||||
|
|
||||||
alwaysinline bool enable() const { return codes.size() > 0; }
|
|
||||||
void reset();
|
|
||||||
void append(unsigned addr, unsigned data);
|
|
||||||
void append(unsigned addr, unsigned comp, unsigned data);
|
|
||||||
maybe<unsigned> find(unsigned addr, unsigned comp);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Cheat cheat;
|
extern Cheat cheat;
|
||||||
|
|
|
@ -10,21 +10,25 @@ namespace SuperFamicom {
|
||||||
#include "justifier/justifier.cpp"
|
#include "justifier/justifier.cpp"
|
||||||
#include "usart/usart.cpp"
|
#include "usart/usart.cpp"
|
||||||
|
|
||||||
void Controller::Enter() {
|
Controller::Controller(bool port) : port(port) {
|
||||||
|
if(!thread) create(Controller::Enter, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Controller::Enter() -> void {
|
||||||
if(co_active() == input.port1->thread) input.port1->enter();
|
if(co_active() == input.port1->thread) input.port1->enter();
|
||||||
if(co_active() == input.port2->thread) input.port2->enter();
|
if(co_active() == input.port2->thread) input.port2->enter();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::enter() {
|
auto Controller::enter() -> void {
|
||||||
while(true) step(1);
|
while(true) step(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::step(unsigned clocks) {
|
auto Controller::step(unsigned clocks) -> void {
|
||||||
clock += clocks * (uint64)cpu.frequency;
|
clock += clocks * (uint64)cpu.frequency;
|
||||||
synchronize_cpu();
|
synchronize_cpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::synchronize_cpu() {
|
auto Controller::synchronize_cpu() -> void {
|
||||||
if(CPU::Threaded == true) {
|
if(CPU::Threaded == true) {
|
||||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||||
} else {
|
} else {
|
||||||
|
@ -32,22 +36,18 @@ void Controller::synchronize_cpu() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::iobit() {
|
auto Controller::iobit() -> bool {
|
||||||
switch(port) {
|
switch(port) {
|
||||||
case Controller::Port1: return cpu.pio() & 0x40;
|
case Controller::Port1: return cpu.pio() & 0x40;
|
||||||
case Controller::Port2: return cpu.pio() & 0x80;
|
case Controller::Port2: return cpu.pio() & 0x80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::iobit(bool data) {
|
auto Controller::iobit(bool data) -> void {
|
||||||
switch(port) {
|
switch(port) {
|
||||||
case Controller::Port1: bus.write(0x4201, (cpu.pio() & ~0x40) | (data << 6)); break;
|
case Controller::Port1: bus.write(0x4201, (cpu.pio() & ~0x40) | (data << 6)); break;
|
||||||
case Controller::Port2: bus.write(0x4201, (cpu.pio() & ~0x80) | (data << 7)); break;
|
case Controller::Port2: bus.write(0x4201, (cpu.pio() & ~0x80) | (data << 7)); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller::Controller(bool port) : port(port) {
|
|
||||||
if(!thread) create(Controller::Enter, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,21 @@
|
||||||
|
|
||||||
struct Controller : Thread {
|
struct Controller : Thread {
|
||||||
enum : bool { Port1 = 0, Port2 = 1 };
|
enum : bool { Port1 = 0, Port2 = 1 };
|
||||||
const bool port;
|
|
||||||
|
|
||||||
static void Enter();
|
|
||||||
virtual void enter();
|
|
||||||
void step(unsigned clocks);
|
|
||||||
void synchronize_cpu();
|
|
||||||
|
|
||||||
bool iobit();
|
|
||||||
void iobit(bool data);
|
|
||||||
virtual uint2 data() { return 0; }
|
|
||||||
virtual void latch(bool data) {}
|
|
||||||
Controller(bool port);
|
Controller(bool port);
|
||||||
|
|
||||||
|
static auto Enter() -> void;
|
||||||
|
virtual auto enter() -> void;
|
||||||
|
|
||||||
|
auto step(unsigned clocks) -> void;
|
||||||
|
auto synchronize_cpu() -> void;
|
||||||
|
|
||||||
|
auto iobit() -> bool;
|
||||||
|
auto iobit(bool data) -> void;
|
||||||
|
virtual auto data() -> uint2 { return 0; }
|
||||||
|
virtual auto latch(bool data) -> void {}
|
||||||
|
|
||||||
|
const bool port;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "gamepad/gamepad.hpp"
|
#include "gamepad/gamepad.hpp"
|
||||||
|
|
|
@ -4,31 +4,150 @@ namespace SuperFamicom {
|
||||||
|
|
||||||
Interface* interface = nullptr;
|
Interface* interface = nullptr;
|
||||||
|
|
||||||
string Interface::title() {
|
Interface::Interface() {
|
||||||
|
interface = this;
|
||||||
|
system.init();
|
||||||
|
|
||||||
|
information.name = "Super Famicom";
|
||||||
|
information.width = 256;
|
||||||
|
information.height = 240;
|
||||||
|
information.overscan = true;
|
||||||
|
information.aspectRatio = 8.0 / 7.0;
|
||||||
|
information.resettable = true;
|
||||||
|
information.capability.states = true;
|
||||||
|
information.capability.cheats = true;
|
||||||
|
|
||||||
|
media.append({ID::SuperFamicom, "Super Famicom", "sfc", true });
|
||||||
|
media.append({ID::SuperFamicom, "Game Boy", "gb", false});
|
||||||
|
media.append({ID::SuperFamicom, "BS-X Satellaview", "bs", false});
|
||||||
|
media.append({ID::SuperFamicom, "Sufami Turbo", "st", false});
|
||||||
|
|
||||||
|
{ Device device{0, ID::Port1 | ID::Port2, "Controller"};
|
||||||
|
device.input.append({ 0, 0, "B" });
|
||||||
|
device.input.append({ 1, 0, "Y" });
|
||||||
|
device.input.append({ 2, 0, "Select"});
|
||||||
|
device.input.append({ 3, 0, "Start" });
|
||||||
|
device.input.append({ 4, 0, "Up" });
|
||||||
|
device.input.append({ 5, 0, "Down" });
|
||||||
|
device.input.append({ 6, 0, "Left" });
|
||||||
|
device.input.append({ 7, 0, "Right" });
|
||||||
|
device.input.append({ 8, 0, "A" });
|
||||||
|
device.input.append({ 9, 0, "X" });
|
||||||
|
device.input.append({10, 0, "L" });
|
||||||
|
device.input.append({11, 0, "R" });
|
||||||
|
device.order = {4, 5, 6, 7, 0, 8, 1, 9, 10, 11, 2, 3};
|
||||||
|
this->device.append(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ Device device{1, ID::Port1 | ID::Port2, "Multitap"};
|
||||||
|
for(unsigned p = 1, n = 0; p <= 4; p++, n += 12) {
|
||||||
|
device.input.append({n + 0, 0, {"Port ", p, " - ", "B" }});
|
||||||
|
device.input.append({n + 1, 0, {"Port ", p, " - ", "Y" }});
|
||||||
|
device.input.append({n + 2, 0, {"Port ", p, " - ", "Select"}});
|
||||||
|
device.input.append({n + 3, 0, {"Port ", p, " - ", "Start" }});
|
||||||
|
device.input.append({n + 4, 0, {"Port ", p, " - ", "Up" }});
|
||||||
|
device.input.append({n + 5, 0, {"Port ", p, " - ", "Down" }});
|
||||||
|
device.input.append({n + 6, 0, {"Port ", p, " - ", "Left" }});
|
||||||
|
device.input.append({n + 7, 0, {"Port ", p, " - ", "Right" }});
|
||||||
|
device.input.append({n + 8, 0, {"Port ", p, " - ", "A" }});
|
||||||
|
device.input.append({n + 9, 0, {"Port ", p, " - ", "X" }});
|
||||||
|
device.input.append({n + 10, 0, {"Port ", p, " - ", "L" }});
|
||||||
|
device.input.append({n + 11, 0, {"Port ", p, " - ", "R" }});
|
||||||
|
device.order.append(n + 4, n + 5, n + 6, n + 7, n + 0, n + 8);
|
||||||
|
device.order.append(n + 1, n + 9, n + 10, n + 11, n + 2, n + 3);
|
||||||
|
}
|
||||||
|
this->device.append(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ Device device{2, ID::Port1 | ID::Port2, "Mouse"};
|
||||||
|
device.input.append({0, 1, "X-axis"});
|
||||||
|
device.input.append({1, 1, "Y-axis"});
|
||||||
|
device.input.append({2, 0, "Left" });
|
||||||
|
device.input.append({3, 0, "Right" });
|
||||||
|
device.order = {0, 1, 2, 3};
|
||||||
|
this->device.append(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ Device device{3, ID::Port2, "Super Scope"};
|
||||||
|
device.input.append({0, 1, "X-axis" });
|
||||||
|
device.input.append({1, 1, "Y-axis" });
|
||||||
|
device.input.append({2, 0, "Trigger"});
|
||||||
|
device.input.append({3, 0, "Cursor" });
|
||||||
|
device.input.append({4, 0, "Turbo" });
|
||||||
|
device.input.append({5, 0, "Pause" });
|
||||||
|
device.order = {0, 1, 2, 3, 4, 5};
|
||||||
|
this->device.append(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ Device device{4, ID::Port2, "Justifier"};
|
||||||
|
device.input.append({0, 1, "X-axis" });
|
||||||
|
device.input.append({1, 1, "Y-axis" });
|
||||||
|
device.input.append({2, 0, "Trigger"});
|
||||||
|
device.input.append({3, 0, "Start" });
|
||||||
|
device.order = {0, 1, 2, 3};
|
||||||
|
this->device.append(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ Device device{5, ID::Port2, "Justifiers"};
|
||||||
|
device.input.append({0, 1, "Port 1 - X-axis" });
|
||||||
|
device.input.append({1, 1, "Port 1 - Y-axis" });
|
||||||
|
device.input.append({2, 0, "Port 1 - Trigger"});
|
||||||
|
device.input.append({3, 0, "Port 1 - Start" });
|
||||||
|
device.order.append(0, 1, 2, 3);
|
||||||
|
device.input.append({4, 1, "Port 2 - X-axis" });
|
||||||
|
device.input.append({5, 1, "Port 2 - Y-axis" });
|
||||||
|
device.input.append({6, 0, "Port 2 - Trigger"});
|
||||||
|
device.input.append({7, 0, "Port 2 - Start" });
|
||||||
|
device.order.append(4, 5, 6, 7);
|
||||||
|
this->device.append(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ Device device{6, ID::Port1, "Serial USART"};
|
||||||
|
this->device.append(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ Device device{7, ID::Port1 | ID::Port2, "None"};
|
||||||
|
this->device.append(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
port.append({0, "Port 1"});
|
||||||
|
port.append({1, "Port 2"});
|
||||||
|
|
||||||
|
for(auto& device : this->device) {
|
||||||
|
for(auto& port : this->port) {
|
||||||
|
if(device.portmask & (1 << port.id)) {
|
||||||
|
port.device.append(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Interface::title() -> string {
|
||||||
return cartridge.title();
|
return cartridge.title();
|
||||||
}
|
}
|
||||||
|
|
||||||
double Interface::videoFrequency() {
|
auto Interface::videoFrequency() -> double {
|
||||||
switch(system.region()) { default:
|
switch(system.region()) { default:
|
||||||
case System::Region::NTSC: return system.cpu_frequency() / (262.0 * 1364.0 - 4.0);
|
case System::Region::NTSC: return system.cpu_frequency() / (262.0 * 1364.0 - 4.0);
|
||||||
case System::Region::PAL: return system.cpu_frequency() / (312.0 * 1364.0);
|
case System::Region::PAL: return system.cpu_frequency() / (312.0 * 1364.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double Interface::audioFrequency() {
|
auto Interface::audioFrequency() -> double {
|
||||||
return system.apu_frequency() / 768.0;
|
return system.apu_frequency() / 768.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interface::loaded() {
|
auto Interface::loaded() -> bool {
|
||||||
return cartridge.loaded();
|
return cartridge.loaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
string Interface::sha256() {
|
auto Interface::sha256() -> string {
|
||||||
return cartridge.sha256();
|
return cartridge.sha256();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned Interface::group(unsigned id) {
|
auto Interface::group(unsigned id) -> unsigned {
|
||||||
switch(id) {
|
switch(id) {
|
||||||
|
case ID::SystemManifest:
|
||||||
case ID::IPLROM:
|
case ID::IPLROM:
|
||||||
return 0;
|
return 0;
|
||||||
case ID::Manifest:
|
case ID::Manifest:
|
||||||
|
@ -94,7 +213,7 @@ unsigned Interface::group(unsigned id) {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::load(unsigned id) {
|
auto Interface::load(unsigned id) -> void {
|
||||||
if(id == ID::SuperFamicom) cartridge.load();
|
if(id == ID::SuperFamicom) cartridge.load();
|
||||||
if(id == ID::SuperGameBoy) cartridge.loadSuperGameBoy();
|
if(id == ID::SuperGameBoy) cartridge.loadSuperGameBoy();
|
||||||
if(id == ID::Satellaview) cartridge.loadSatellaview();
|
if(id == ID::Satellaview) cartridge.loadSatellaview();
|
||||||
|
@ -102,13 +221,17 @@ void Interface::load(unsigned id) {
|
||||||
if(id == ID::SufamiTurboSlotB) cartridge.loadSufamiTurboB();
|
if(id == ID::SufamiTurboSlotB) cartridge.loadSufamiTurboB();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::save() {
|
auto Interface::save() -> void {
|
||||||
for(auto& memory : cartridge.memory) {
|
for(auto& memory : cartridge.memory) {
|
||||||
saveRequest(memory.id, memory.name);
|
saveRequest(memory.id, memory.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::load(unsigned id, const stream& stream) {
|
auto Interface::load(unsigned id, const stream& stream) -> void {
|
||||||
|
if(id == ID::SystemManifest) {
|
||||||
|
system.information.manifest = stream.text();
|
||||||
|
}
|
||||||
|
|
||||||
if(id == ID::IPLROM) {
|
if(id == ID::IPLROM) {
|
||||||
stream.read(smp.iplrom, min(64u, stream.size()));
|
stream.read(smp.iplrom, min(64u, stream.size()));
|
||||||
}
|
}
|
||||||
|
@ -219,7 +342,7 @@ void Interface::load(unsigned id, const stream& stream) {
|
||||||
if(id == ID::SufamiTurboSlotBRAM) sufamiturboB.ram.read(stream);
|
if(id == ID::SufamiTurboSlotBRAM) sufamiturboB.ram.read(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::save(unsigned id, const stream& stream) {
|
auto Interface::save(unsigned id, const stream& stream) -> void {
|
||||||
if(id == ID::RAM) stream.write(cartridge.ram.data(), cartridge.ram.size());
|
if(id == ID::RAM) stream.write(cartridge.ram.data(), cartridge.ram.size());
|
||||||
if(id == ID::EventRAM) stream.write(event.ram.data(), event.ram.size());
|
if(id == ID::EventRAM) stream.write(event.ram.data(), event.ram.size());
|
||||||
if(id == ID::SA1IRAM) stream.write(sa1.iram.data(), sa1.iram.size());
|
if(id == ID::SA1IRAM) stream.write(sa1.iram.data(), sa1.iram.size());
|
||||||
|
@ -267,48 +390,48 @@ void Interface::save(unsigned id, const stream& stream) {
|
||||||
if(id == ID::SufamiTurboSlotBRAM) stream.write(sufamiturboB.ram.data(), sufamiturboB.ram.size());
|
if(id == ID::SufamiTurboSlotBRAM) stream.write(sufamiturboB.ram.data(), sufamiturboB.ram.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::unload() {
|
auto Interface::unload() -> void {
|
||||||
save();
|
save();
|
||||||
cartridge.unload();
|
cartridge.unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::connect(unsigned port, unsigned device) {
|
auto Interface::connect(unsigned port, unsigned device) -> void {
|
||||||
input.connect(port, (Input::Device)device);
|
input.connect(port, (Input::Device)device);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::power() {
|
auto Interface::power() -> void {
|
||||||
system.power();
|
system.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::reset() {
|
auto Interface::reset() -> void {
|
||||||
system.reset();
|
system.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::run() {
|
auto Interface::run() -> void {
|
||||||
system.run();
|
system.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interface::rtc() {
|
auto Interface::rtc() -> bool {
|
||||||
if(cartridge.hasEpsonRTC()) return true;
|
if(cartridge.hasEpsonRTC()) return true;
|
||||||
if(cartridge.hasSharpRTC()) return true;
|
if(cartridge.hasSharpRTC()) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::rtcsync() {
|
auto Interface::rtcsync() -> void {
|
||||||
if(cartridge.hasEpsonRTC()) epsonrtc.sync();
|
if(cartridge.hasEpsonRTC()) epsonrtc.sync();
|
||||||
if(cartridge.hasSharpRTC()) sharprtc.sync();
|
if(cartridge.hasSharpRTC()) sharprtc.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer Interface::serialize() {
|
auto Interface::serialize() -> serializer {
|
||||||
system.runtosave();
|
system.runtosave();
|
||||||
return system.serialize();
|
return system.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interface::unserialize(serializer& s) {
|
auto Interface::unserialize(serializer& s) -> bool {
|
||||||
return system.unserialize(s);
|
return system.unserialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::cheatSet(const lstring& list) {
|
auto Interface::cheatSet(const lstring& list) -> void {
|
||||||
cheat.reset();
|
cheat.reset();
|
||||||
|
|
||||||
//Super Game Boy
|
//Super Game Boy
|
||||||
|
@ -336,134 +459,8 @@ void Interface::cheatSet(const lstring& list) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::paletteUpdate(PaletteMode mode) {
|
auto Interface::paletteUpdate(PaletteMode mode) -> void {
|
||||||
video.generate_palette(mode);
|
video.generate_palette(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Interface::Interface() {
|
|
||||||
interface = this;
|
|
||||||
system.init();
|
|
||||||
|
|
||||||
information.name = "Super Famicom";
|
|
||||||
information.width = 256;
|
|
||||||
information.height = 240;
|
|
||||||
information.overscan = true;
|
|
||||||
information.aspectRatio = 8.0 / 7.0;
|
|
||||||
information.resettable = true;
|
|
||||||
information.capability.states = true;
|
|
||||||
information.capability.cheats = true;
|
|
||||||
|
|
||||||
media.append({ID::SuperFamicom, "Super Famicom", "sfc", true });
|
|
||||||
media.append({ID::SuperFamicom, "Game Boy", "gb", false});
|
|
||||||
media.append({ID::SuperFamicom, "BS-X Satellaview", "bs", false});
|
|
||||||
media.append({ID::SuperFamicom, "Sufami Turbo", "st", false});
|
|
||||||
|
|
||||||
{
|
|
||||||
Device device{0, ID::Port1 | ID::Port2, "Controller"};
|
|
||||||
device.input.append({ 0, 0, "B" });
|
|
||||||
device.input.append({ 1, 0, "Y" });
|
|
||||||
device.input.append({ 2, 0, "Select"});
|
|
||||||
device.input.append({ 3, 0, "Start" });
|
|
||||||
device.input.append({ 4, 0, "Up" });
|
|
||||||
device.input.append({ 5, 0, "Down" });
|
|
||||||
device.input.append({ 6, 0, "Left" });
|
|
||||||
device.input.append({ 7, 0, "Right" });
|
|
||||||
device.input.append({ 8, 0, "A" });
|
|
||||||
device.input.append({ 9, 0, "X" });
|
|
||||||
device.input.append({10, 0, "L" });
|
|
||||||
device.input.append({11, 0, "R" });
|
|
||||||
device.order = {4, 5, 6, 7, 0, 8, 1, 9, 10, 11, 2, 3};
|
|
||||||
this->device.append(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Device device{1, ID::Port1 | ID::Port2, "Multitap"};
|
|
||||||
for(unsigned p = 1, n = 0; p <= 4; p++, n += 12) {
|
|
||||||
device.input.append({n + 0, 0, {"Port ", p, " - ", "B" }});
|
|
||||||
device.input.append({n + 1, 0, {"Port ", p, " - ", "Y" }});
|
|
||||||
device.input.append({n + 2, 0, {"Port ", p, " - ", "Select"}});
|
|
||||||
device.input.append({n + 3, 0, {"Port ", p, " - ", "Start" }});
|
|
||||||
device.input.append({n + 4, 0, {"Port ", p, " - ", "Up" }});
|
|
||||||
device.input.append({n + 5, 0, {"Port ", p, " - ", "Down" }});
|
|
||||||
device.input.append({n + 6, 0, {"Port ", p, " - ", "Left" }});
|
|
||||||
device.input.append({n + 7, 0, {"Port ", p, " - ", "Right" }});
|
|
||||||
device.input.append({n + 8, 0, {"Port ", p, " - ", "A" }});
|
|
||||||
device.input.append({n + 9, 0, {"Port ", p, " - ", "X" }});
|
|
||||||
device.input.append({n + 10, 0, {"Port ", p, " - ", "L" }});
|
|
||||||
device.input.append({n + 11, 0, {"Port ", p, " - ", "R" }});
|
|
||||||
device.order.append(n + 4, n + 5, n + 6, n + 7, n + 0, n + 8);
|
|
||||||
device.order.append(n + 1, n + 9, n + 10, n + 11, n + 2, n + 3);
|
|
||||||
}
|
|
||||||
this->device.append(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Device device{2, ID::Port1 | ID::Port2, "Mouse"};
|
|
||||||
device.input.append({0, 1, "X-axis"});
|
|
||||||
device.input.append({1, 1, "Y-axis"});
|
|
||||||
device.input.append({2, 0, "Left" });
|
|
||||||
device.input.append({3, 0, "Right" });
|
|
||||||
device.order = {0, 1, 2, 3};
|
|
||||||
this->device.append(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Device device{3, ID::Port2, "Super Scope"};
|
|
||||||
device.input.append({0, 1, "X-axis" });
|
|
||||||
device.input.append({1, 1, "Y-axis" });
|
|
||||||
device.input.append({2, 0, "Trigger"});
|
|
||||||
device.input.append({3, 0, "Cursor" });
|
|
||||||
device.input.append({4, 0, "Turbo" });
|
|
||||||
device.input.append({5, 0, "Pause" });
|
|
||||||
device.order = {0, 1, 2, 3, 4, 5};
|
|
||||||
this->device.append(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Device device{4, ID::Port2, "Justifier"};
|
|
||||||
device.input.append({0, 1, "X-axis" });
|
|
||||||
device.input.append({1, 1, "Y-axis" });
|
|
||||||
device.input.append({2, 0, "Trigger"});
|
|
||||||
device.input.append({3, 0, "Start" });
|
|
||||||
device.order = {0, 1, 2, 3};
|
|
||||||
this->device.append(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Device device{5, ID::Port2, "Justifiers"};
|
|
||||||
device.input.append({0, 1, "Port 1 - X-axis" });
|
|
||||||
device.input.append({1, 1, "Port 1 - Y-axis" });
|
|
||||||
device.input.append({2, 0, "Port 1 - Trigger"});
|
|
||||||
device.input.append({3, 0, "Port 1 - Start" });
|
|
||||||
device.order.append(0, 1, 2, 3);
|
|
||||||
device.input.append({4, 1, "Port 2 - X-axis" });
|
|
||||||
device.input.append({5, 1, "Port 2 - Y-axis" });
|
|
||||||
device.input.append({6, 0, "Port 2 - Trigger"});
|
|
||||||
device.input.append({7, 0, "Port 2 - Start" });
|
|
||||||
device.order.append(4, 5, 6, 7);
|
|
||||||
this->device.append(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Device device{6, ID::Port1, "Serial USART"};
|
|
||||||
this->device.append(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Device device{7, ID::Port1 | ID::Port2, "None"};
|
|
||||||
this->device.append(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
port.append({0, "Port 1"});
|
|
||||||
port.append({1, "Port 2"});
|
|
||||||
|
|
||||||
for(auto& device : this->device) {
|
|
||||||
for(auto& port : this->port) {
|
|
||||||
if(device.portmask & (1 << port.id)) {
|
|
||||||
port.device.append(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ struct ID {
|
||||||
SufamiTurboSlotB,
|
SufamiTurboSlotB,
|
||||||
|
|
||||||
//memory (files)
|
//memory (files)
|
||||||
|
SystemManifest,
|
||||||
IPLROM,
|
IPLROM,
|
||||||
|
|
||||||
Manifest,
|
Manifest,
|
||||||
|
@ -89,36 +90,36 @@ struct ID {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Interface : Emulator::Interface {
|
struct Interface : Emulator::Interface {
|
||||||
string title();
|
|
||||||
double videoFrequency();
|
|
||||||
double audioFrequency();
|
|
||||||
|
|
||||||
bool loaded();
|
|
||||||
string sha256();
|
|
||||||
unsigned group(unsigned id);
|
|
||||||
void load(unsigned id);
|
|
||||||
void save();
|
|
||||||
void load(unsigned id, const stream& stream);
|
|
||||||
void save(unsigned id, const stream& stream);
|
|
||||||
void unload();
|
|
||||||
|
|
||||||
void connect(unsigned port, unsigned device);
|
|
||||||
void power();
|
|
||||||
void reset();
|
|
||||||
void run();
|
|
||||||
|
|
||||||
bool rtc();
|
|
||||||
void rtcsync();
|
|
||||||
|
|
||||||
serializer serialize();
|
|
||||||
bool unserialize(serializer&);
|
|
||||||
|
|
||||||
void cheatSet(const lstring&);
|
|
||||||
|
|
||||||
void paletteUpdate(PaletteMode mode);
|
|
||||||
|
|
||||||
Interface();
|
Interface();
|
||||||
|
|
||||||
|
auto title() -> string;
|
||||||
|
auto videoFrequency() -> double;
|
||||||
|
auto audioFrequency() -> double;
|
||||||
|
|
||||||
|
auto loaded() -> bool;
|
||||||
|
auto sha256() -> string;
|
||||||
|
auto group(unsigned id) -> unsigned;
|
||||||
|
auto load(unsigned id) -> void;
|
||||||
|
auto save() -> void;
|
||||||
|
auto load(unsigned id, const stream& stream) -> void;
|
||||||
|
auto save(unsigned id, const stream& stream) -> void;
|
||||||
|
auto unload() -> void;
|
||||||
|
|
||||||
|
auto connect(unsigned port, unsigned device) -> void;
|
||||||
|
auto power() -> void;
|
||||||
|
auto reset() -> void;
|
||||||
|
auto run() -> void;
|
||||||
|
|
||||||
|
auto rtc() -> bool;
|
||||||
|
auto rtcsync() -> void;
|
||||||
|
|
||||||
|
auto serialize() -> serializer;
|
||||||
|
auto unserialize(serializer&) -> bool;
|
||||||
|
|
||||||
|
auto cheatSet(const lstring&) -> void;
|
||||||
|
|
||||||
|
auto paletteUpdate(PaletteMode mode) -> void;
|
||||||
|
|
||||||
vector<Device> device;
|
vector<Device> device;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
//Memory
|
//Memory
|
||||||
|
|
||||||
unsigned Memory::size() const { return 0; }
|
auto Memory::size() const -> unsigned { return 0; }
|
||||||
|
|
||||||
//StaticRAM
|
//StaticRAM
|
||||||
|
|
||||||
uint8* StaticRAM::data() { return data_; }
|
|
||||||
unsigned StaticRAM::size() const { return size_; }
|
|
||||||
|
|
||||||
uint8 StaticRAM::read(unsigned addr) { return data_[addr]; }
|
|
||||||
void StaticRAM::write(unsigned addr, uint8 n) { data_[addr] = n; }
|
|
||||||
uint8& StaticRAM::operator[](unsigned addr) { return data_[addr]; }
|
|
||||||
const uint8& StaticRAM::operator[](unsigned addr) const { return data_[addr]; }
|
|
||||||
|
|
||||||
StaticRAM::StaticRAM(unsigned n) : size_(n) { data_ = new uint8[size_]; }
|
StaticRAM::StaticRAM(unsigned n) : size_(n) { data_ = new uint8[size_]; }
|
||||||
StaticRAM::~StaticRAM() { delete[] data_; }
|
StaticRAM::~StaticRAM() { delete[] data_; }
|
||||||
|
|
||||||
|
auto StaticRAM::data() -> uint8* { return data_; }
|
||||||
|
auto StaticRAM::size() const -> unsigned { return size_; }
|
||||||
|
|
||||||
|
auto StaticRAM::read(unsigned addr) -> uint8 { return data_[addr]; }
|
||||||
|
auto StaticRAM::write(unsigned addr, uint8 n) -> void { data_[addr] = n; }
|
||||||
|
auto StaticRAM::operator[](unsigned addr) -> uint8& { return data_[addr]; }
|
||||||
|
auto StaticRAM::operator[](unsigned addr) const -> const uint8& { return data_[addr]; }
|
||||||
|
|
||||||
//MappedRAM
|
//MappedRAM
|
||||||
|
|
||||||
void MappedRAM::reset() {
|
auto MappedRAM::reset() -> void {
|
||||||
if(data_) {
|
if(data_) {
|
||||||
delete[] data_;
|
delete[] data_;
|
||||||
data_ = nullptr;
|
data_ = nullptr;
|
||||||
|
@ -26,13 +26,13 @@ void MappedRAM::reset() {
|
||||||
write_protect_ = false;
|
write_protect_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MappedRAM::map(uint8* source, unsigned length) {
|
auto MappedRAM::map(uint8* source, unsigned length) -> void {
|
||||||
reset();
|
reset();
|
||||||
data_ = source;
|
data_ = source;
|
||||||
size_ = data_ ? length : 0;
|
size_ = data_ ? length : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MappedRAM::copy(const stream& memory) {
|
auto MappedRAM::copy(const stream& memory) -> void {
|
||||||
if(data_) delete[] data_;
|
if(data_) delete[] data_;
|
||||||
//round size up to multiple of 256-bytes
|
//round size up to multiple of 256-bytes
|
||||||
size_ = (memory.size() & ~255) + ((bool)(memory.size() & 255) << 8);
|
size_ = (memory.size() & ~255) + ((bool)(memory.size() & 255) << 8);
|
||||||
|
@ -40,22 +40,21 @@ void MappedRAM::copy(const stream& memory) {
|
||||||
memory.read(data_, memory.size());
|
memory.read(data_, memory.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MappedRAM::read(const stream& memory) {
|
auto MappedRAM::read(const stream& memory) -> void {
|
||||||
memory.read(data_, min(memory.size(), size_));
|
memory.read(data_, min(memory.size(), size_));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MappedRAM::write_protect(bool status) { write_protect_ = status; }
|
auto MappedRAM::write_protect(bool status) -> void { write_protect_ = status; }
|
||||||
uint8* MappedRAM::data() { return data_; }
|
auto MappedRAM::data() -> uint8* { return data_; }
|
||||||
unsigned MappedRAM::size() const { return size_; }
|
auto MappedRAM::size() const -> unsigned { return size_; }
|
||||||
|
|
||||||
uint8 MappedRAM::read(unsigned addr) { return data_[addr]; }
|
auto MappedRAM::read(unsigned addr) -> uint8 { return data_[addr]; }
|
||||||
void MappedRAM::write(unsigned addr, uint8 n) { if(!write_protect_) data_[addr] = n; }
|
auto MappedRAM::write(unsigned addr, uint8 n) -> void { if(!write_protect_) data_[addr] = n; }
|
||||||
const uint8& MappedRAM::operator[](unsigned addr) const { return data_[addr]; }
|
auto MappedRAM::operator[](unsigned addr) const -> const uint8& { return data_[addr]; }
|
||||||
MappedRAM::MappedRAM() : data_(nullptr), size_(0), write_protect_(false) {}
|
|
||||||
|
|
||||||
//Bus
|
//Bus
|
||||||
|
|
||||||
unsigned Bus::mirror(unsigned addr, unsigned size) {
|
auto Bus::mirror(unsigned addr, unsigned size) -> unsigned {
|
||||||
if(size == 0) return 0;
|
if(size == 0) return 0;
|
||||||
unsigned base = 0;
|
unsigned base = 0;
|
||||||
unsigned mask = 1 << 23;
|
unsigned mask = 1 << 23;
|
||||||
|
@ -71,7 +70,7 @@ unsigned Bus::mirror(unsigned addr, unsigned size) {
|
||||||
return base + addr;
|
return base + addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned Bus::reduce(unsigned addr, unsigned mask) {
|
auto Bus::reduce(unsigned addr, unsigned mask) -> unsigned {
|
||||||
while(mask) {
|
while(mask) {
|
||||||
unsigned bits = (mask & -mask) - 1;
|
unsigned bits = (mask & -mask) - 1;
|
||||||
addr = ((addr >> 1) & ~bits) | (addr & bits);
|
addr = ((addr >> 1) & ~bits) | (addr & bits);
|
||||||
|
@ -80,7 +79,7 @@ unsigned Bus::reduce(unsigned addr, unsigned mask) {
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 Bus::read(unsigned addr) {
|
auto Bus::read(unsigned addr) -> uint8 {
|
||||||
uint8 data = reader[lookup[addr]](target[addr]);
|
uint8 data = reader[lookup[addr]](target[addr]);
|
||||||
|
|
||||||
if(cheat.enable()) {
|
if(cheat.enable()) {
|
||||||
|
@ -90,6 +89,6 @@ uint8 Bus::read(unsigned addr) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::write(unsigned addr, uint8 data) {
|
auto Bus::write(unsigned addr, uint8 data) -> void {
|
||||||
return writer[lookup[addr]](target[addr], data);
|
return writer[lookup[addr]](target[addr], data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,32 +5,17 @@ namespace SuperFamicom {
|
||||||
|
|
||||||
Bus bus;
|
Bus bus;
|
||||||
|
|
||||||
void Bus::map(
|
Bus::Bus() {
|
||||||
const function<uint8 (unsigned)>& reader,
|
lookup = new uint8 [16 * 1024 * 1024];
|
||||||
const function<void (unsigned, uint8)>& writer,
|
target = new uint32[16 * 1024 * 1024];
|
||||||
unsigned banklo, unsigned bankhi,
|
|
||||||
unsigned addrlo, unsigned addrhi,
|
|
||||||
unsigned size, unsigned base, unsigned mask
|
|
||||||
) {
|
|
||||||
assert(banklo <= bankhi && banklo <= 0xff);
|
|
||||||
assert(addrlo <= addrhi && addrlo <= 0xffff);
|
|
||||||
assert(idcount < 255);
|
|
||||||
|
|
||||||
unsigned id = idcount++;
|
|
||||||
this->reader[id] = reader;
|
|
||||||
this->writer[id] = writer;
|
|
||||||
|
|
||||||
for(unsigned bank = banklo; bank <= bankhi; bank++) {
|
|
||||||
for(unsigned addr = addrlo; addr <= addrhi; addr++) {
|
|
||||||
unsigned offset = reduce(bank << 16 | addr, mask);
|
|
||||||
if(size) offset = base + mirror(offset, size - base);
|
|
||||||
lookup[bank << 16 | addr] = id;
|
|
||||||
target[bank << 16 | addr] = offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::map_reset() {
|
Bus::~Bus() {
|
||||||
|
delete[] lookup;
|
||||||
|
delete[] target;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Bus::reset() -> void {
|
||||||
function<uint8 (unsigned)> reader = [](unsigned) { return cpu.regs.mdr; };
|
function<uint8 (unsigned)> reader = [](unsigned) { return cpu.regs.mdr; };
|
||||||
function<void (unsigned, uint8)> writer = [](unsigned, uint8) {};
|
function<void (unsigned, uint8)> writer = [](unsigned, uint8) {};
|
||||||
|
|
||||||
|
@ -38,7 +23,7 @@ void Bus::map_reset() {
|
||||||
map(reader, writer, 0x00, 0xff, 0x0000, 0xffff);
|
map(reader, writer, 0x00, 0xff, 0x0000, 0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bus::map_xml() {
|
auto Bus::map() -> void {
|
||||||
for(auto& m : cartridge.mapping) {
|
for(auto& m : cartridge.mapping) {
|
||||||
lstring part = m.addr.split(":", 1L);
|
lstring part = m.addr.split(":", 1L);
|
||||||
lstring banks = part(0).split(",");
|
lstring banks = part(0).split(",");
|
||||||
|
@ -57,14 +42,29 @@ void Bus::map_xml() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bus::Bus() {
|
auto Bus::map(
|
||||||
lookup = new uint8 [16 * 1024 * 1024];
|
const function<uint8 (unsigned)>& reader,
|
||||||
target = new uint32[16 * 1024 * 1024];
|
const function<void (unsigned, uint8)>& writer,
|
||||||
}
|
unsigned banklo, unsigned bankhi,
|
||||||
|
unsigned addrlo, unsigned addrhi,
|
||||||
|
unsigned size, unsigned base, unsigned mask
|
||||||
|
) -> void {
|
||||||
|
assert(banklo <= bankhi && banklo <= 0xff);
|
||||||
|
assert(addrlo <= addrhi && addrlo <= 0xffff);
|
||||||
|
assert(idcount < 255);
|
||||||
|
|
||||||
Bus::~Bus() {
|
unsigned id = idcount++;
|
||||||
delete[] lookup;
|
this->reader[id] = reader;
|
||||||
delete[] target;
|
this->writer[id] = writer;
|
||||||
|
|
||||||
|
for(unsigned bank = banklo; bank <= bankhi; bank++) {
|
||||||
|
for(unsigned addr = addrlo; addr <= addrhi; addr++) {
|
||||||
|
unsigned offset = reduce(bank << 16 | addr, mask);
|
||||||
|
if(size) offset = base + mirror(offset, size - base);
|
||||||
|
lookup[bank << 16 | addr] = id;
|
||||||
|
target[bank << 16 | addr] = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +1,72 @@
|
||||||
struct Memory {
|
struct Memory {
|
||||||
virtual inline unsigned size() const;
|
virtual inline auto size() const -> unsigned;
|
||||||
virtual uint8 read(unsigned addr) = 0;
|
virtual auto read(unsigned addr) -> uint8 = 0;
|
||||||
virtual void write(unsigned addr, uint8 data) = 0;
|
virtual auto write(unsigned addr, uint8 data) -> void = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StaticRAM : Memory {
|
struct StaticRAM : Memory {
|
||||||
inline uint8* data();
|
|
||||||
inline unsigned size() const;
|
|
||||||
|
|
||||||
inline uint8 read(unsigned addr);
|
|
||||||
inline void write(unsigned addr, uint8 n);
|
|
||||||
inline uint8& operator[](unsigned addr);
|
|
||||||
inline const uint8& operator[](unsigned addr) const;
|
|
||||||
|
|
||||||
inline StaticRAM(unsigned size);
|
inline StaticRAM(unsigned size);
|
||||||
inline ~StaticRAM();
|
inline ~StaticRAM();
|
||||||
|
|
||||||
|
inline auto data() -> uint8*;
|
||||||
|
inline auto size() const -> unsigned;
|
||||||
|
|
||||||
|
inline auto read(unsigned addr) -> uint8;
|
||||||
|
inline auto write(unsigned addr, uint8 n) -> void;
|
||||||
|
inline auto operator[](unsigned addr) -> uint8&;
|
||||||
|
inline auto operator[](unsigned addr) const -> const uint8&;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8* data_;
|
uint8* data_ = nullptr;
|
||||||
unsigned size_;
|
unsigned size_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MappedRAM : Memory {
|
struct MappedRAM : Memory {
|
||||||
inline void reset();
|
inline auto reset() -> void;
|
||||||
inline void map(uint8*, unsigned);
|
inline auto map(uint8*, unsigned) -> void;
|
||||||
inline void copy(const stream& memory);
|
inline auto copy(const stream& memory) -> void;
|
||||||
inline void read(const stream& memory);
|
inline auto read(const stream& memory) -> void;
|
||||||
|
|
||||||
inline void write_protect(bool status);
|
inline auto write_protect(bool status) -> void;
|
||||||
inline uint8* data();
|
inline auto data() -> uint8*;
|
||||||
inline unsigned size() const;
|
inline auto size() const -> unsigned;
|
||||||
|
|
||||||
inline uint8 read(unsigned addr);
|
inline auto read(unsigned addr) -> uint8;
|
||||||
inline void write(unsigned addr, uint8 n);
|
inline auto write(unsigned addr, uint8 n) -> void;
|
||||||
inline const uint8& operator[](unsigned addr) const;
|
inline auto operator[](unsigned addr) const -> const uint8&;
|
||||||
inline MappedRAM();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8* data_;
|
uint8* data_ = nullptr;
|
||||||
unsigned size_;
|
unsigned size_ = 0;
|
||||||
bool write_protect_;
|
bool write_protect_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Bus {
|
struct Bus {
|
||||||
alwaysinline static unsigned mirror(unsigned addr, unsigned size);
|
alwaysinline static auto mirror(unsigned addr, unsigned size) -> unsigned;
|
||||||
alwaysinline static unsigned reduce(unsigned addr, unsigned mask);
|
alwaysinline static auto reduce(unsigned addr, unsigned mask) -> unsigned;
|
||||||
|
|
||||||
alwaysinline uint8 read(unsigned addr);
|
Bus();
|
||||||
alwaysinline void write(unsigned addr, uint8 data);
|
~Bus();
|
||||||
|
|
||||||
uint8* lookup;
|
alwaysinline auto read(unsigned addr) -> uint8;
|
||||||
uint32* target;
|
alwaysinline auto write(unsigned addr, uint8 data) -> void;
|
||||||
|
|
||||||
unsigned idcount;
|
auto reset() -> void;
|
||||||
function<uint8 (unsigned)> reader[256];
|
auto map() -> void;
|
||||||
function<void (unsigned, uint8)> writer[256];
|
auto map(
|
||||||
|
|
||||||
void map(
|
|
||||||
const function<uint8 (unsigned)>& reader,
|
const function<uint8 (unsigned)>& reader,
|
||||||
const function<void (unsigned, uint8)>& writer,
|
const function<void (unsigned, uint8)>& writer,
|
||||||
unsigned banklo, unsigned bankhi,
|
unsigned banklo, unsigned bankhi,
|
||||||
unsigned addrlo, unsigned addrhi,
|
unsigned addrlo, unsigned addrhi,
|
||||||
unsigned size = 0, unsigned base = 0, unsigned mask = 0
|
unsigned size = 0, unsigned base = 0, unsigned mask = 0
|
||||||
);
|
) -> void;
|
||||||
|
|
||||||
void map_reset();
|
uint8* lookup = nullptr;
|
||||||
void map_xml();
|
uint32* target = nullptr;
|
||||||
|
|
||||||
Bus();
|
unsigned idcount = 0;
|
||||||
~Bus();
|
function<uint8 (unsigned)> reader[256];
|
||||||
|
function<void (unsigned, uint8)> writer[256];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Bus bus;
|
extern Bus bus;
|
||||||
|
|
|
@ -169,14 +169,13 @@ void PPU::Background::run(bool screen) {
|
||||||
if(hires == false) return;
|
if(hires == false) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(regs.mode == Mode::Inactive) return;
|
|
||||||
if(regs.mode == Mode::Mode7) return run_mode7();
|
|
||||||
|
|
||||||
if(tile_counter-- == 0) {
|
if(tile_counter-- == 0) {
|
||||||
tile_counter = 7;
|
tile_counter = 7;
|
||||||
get_tile();
|
get_tile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(regs.mode == Mode::Mode7) return run_mode7();
|
||||||
|
|
||||||
uint8 palette = get_tile_color();
|
uint8 palette = get_tile_color();
|
||||||
if(x == 0) mosaic.hcounter = 1;
|
if(x == 0) mosaic.hcounter = 1;
|
||||||
if(x >= 0 && --mosaic.hcounter == 0) {
|
if(x >= 0 && --mosaic.hcounter == 0) {
|
||||||
|
|
|
@ -2,31 +2,25 @@
|
||||||
|
|
||||||
Scheduler scheduler;
|
Scheduler scheduler;
|
||||||
|
|
||||||
void Scheduler::enter() {
|
auto Scheduler::init() -> void {
|
||||||
host_thread = co_active();
|
|
||||||
co_switch(thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::exit(ExitReason reason) {
|
|
||||||
exit_reason = reason;
|
|
||||||
thread = co_active();
|
|
||||||
co_switch(host_thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::debug() {
|
|
||||||
exit(ExitReason::DebuggerEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::init() {
|
|
||||||
host_thread = co_active();
|
host_thread = co_active();
|
||||||
thread = cpu.thread;
|
thread = cpu.thread;
|
||||||
sync = SynchronizeMode::None;
|
sync = SynchronizeMode::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Scheduler::Scheduler() {
|
auto Scheduler::enter() -> void {
|
||||||
host_thread = nullptr;
|
host_thread = co_active();
|
||||||
thread = nullptr;
|
co_switch(thread);
|
||||||
exit_reason = ExitReason::UnknownEvent;
|
}
|
||||||
|
|
||||||
|
auto Scheduler::exit(ExitReason reason) -> void {
|
||||||
|
exit_reason = reason;
|
||||||
|
thread = co_active();
|
||||||
|
co_switch(host_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Scheduler::debug() -> void {
|
||||||
|
exit(ExitReason::DebuggerEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
struct Scheduler : property<Scheduler> {
|
struct Scheduler {
|
||||||
enum class SynchronizeMode : unsigned { None, CPU, All } sync;
|
enum class SynchronizeMode : unsigned { None, CPU, All } sync;
|
||||||
enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent, DebuggerEvent };
|
enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent, DebuggerEvent };
|
||||||
readonly<ExitReason> exit_reason;
|
|
||||||
|
|
||||||
cothread_t host_thread; //program thread (used to exit emulation)
|
auto init() -> void;
|
||||||
cothread_t thread; //active emulation thread (used to enter emulation)
|
auto enter() -> void;
|
||||||
|
auto exit(ExitReason) -> void;
|
||||||
|
auto debug() -> void;
|
||||||
|
|
||||||
void enter();
|
cothread_t host_thread = nullptr; //program thread (used to exit emulation)
|
||||||
void exit(ExitReason);
|
cothread_t thread = nullptr; //active emulation thread (used to enter emulation)
|
||||||
void debug();
|
ExitReason exit_reason = ExitReason::UnknownEvent;
|
||||||
|
|
||||||
void init();
|
|
||||||
Scheduler();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Scheduler scheduler;
|
extern Scheduler scheduler;
|
||||||
|
|
|
@ -5,23 +5,23 @@ namespace SuperFamicom {
|
||||||
|
|
||||||
SatellaviewCartridge satellaviewcartridge;
|
SatellaviewCartridge satellaviewcartridge;
|
||||||
|
|
||||||
void SatellaviewCartridge::init() {
|
auto SatellaviewCartridge::init() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SatellaviewCartridge::load() {
|
auto SatellaviewCartridge::load() -> void {
|
||||||
if(memory.size() == 0) {
|
if(memory.size() == 0) {
|
||||||
memory.map(allocate<uint8>(1024 * 1024, 0xff), 1024 * 1024);
|
memory.map(allocate<uint8>(1024 * 1024, 0xff), 1024 * 1024);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SatellaviewCartridge::unload() {
|
auto SatellaviewCartridge::unload() -> void {
|
||||||
memory.reset();
|
memory.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SatellaviewCartridge::power() {
|
auto SatellaviewCartridge::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SatellaviewCartridge::reset() {
|
auto SatellaviewCartridge::reset() -> void {
|
||||||
regs.command = 0;
|
regs.command = 0;
|
||||||
regs.write_old = 0x00;
|
regs.write_old = 0x00;
|
||||||
regs.write_new = 0x00;
|
regs.write_new = 0x00;
|
||||||
|
@ -32,11 +32,11 @@ void SatellaviewCartridge::reset() {
|
||||||
memory.write_protect(!regs.write_enable);
|
memory.write_protect(!regs.write_enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned SatellaviewCartridge::size() const {
|
auto SatellaviewCartridge::size() const -> unsigned {
|
||||||
return memory.size();
|
return memory.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 SatellaviewCartridge::read(unsigned addr) {
|
auto SatellaviewCartridge::read(unsigned addr) -> uint8 {
|
||||||
if(readonly) return memory.read(bus.mirror(addr, memory.size()));
|
if(readonly) return memory.read(bus.mirror(addr, memory.size()));
|
||||||
|
|
||||||
if(addr == 0x0002) {
|
if(addr == 0x0002) {
|
||||||
|
@ -65,7 +65,7 @@ uint8 SatellaviewCartridge::read(unsigned addr) {
|
||||||
return memory.read(addr);
|
return memory.read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SatellaviewCartridge::write(unsigned addr, uint8 data) {
|
auto SatellaviewCartridge::write(unsigned addr, uint8 data) -> void {
|
||||||
if(readonly) return;
|
if(readonly) return;
|
||||||
|
|
||||||
if((addr & 0xff0000) == 0) {
|
if((addr & 0xff0000) == 0) {
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
struct SatellaviewCartridge : Memory {
|
struct SatellaviewCartridge : Memory {
|
||||||
|
auto init() -> void;
|
||||||
|
auto load() -> void;
|
||||||
|
auto unload() -> void;
|
||||||
|
auto power() -> void;
|
||||||
|
auto reset() -> void;
|
||||||
|
|
||||||
|
auto size() const -> unsigned;
|
||||||
|
auto read(unsigned addr) -> uint8;
|
||||||
|
auto write(unsigned addr, uint8 data) -> void;
|
||||||
|
|
||||||
MappedRAM memory;
|
MappedRAM memory;
|
||||||
bool readonly;
|
bool readonly;
|
||||||
|
|
||||||
void init();
|
|
||||||
void load();
|
|
||||||
void unload();
|
|
||||||
void power();
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
unsigned size() const;
|
|
||||||
uint8 read(unsigned addr);
|
|
||||||
void write(unsigned addr, uint8 data);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct {
|
struct {
|
||||||
unsigned command;
|
unsigned command;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#ifdef SUFAMITURBO_CPP
|
#ifdef SUFAMITURBO_CPP
|
||||||
|
|
||||||
void SufamiTurboCartridge::serialize(serializer& s) {
|
auto SufamiTurboCartridge::serialize(serializer& s) -> void {
|
||||||
s.array(ram.data(), ram.size());
|
s.array(ram.data(), ram.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,10 @@ namespace SuperFamicom {
|
||||||
SufamiTurboCartridge sufamiturboA;
|
SufamiTurboCartridge sufamiturboA;
|
||||||
SufamiTurboCartridge sufamiturboB;
|
SufamiTurboCartridge sufamiturboB;
|
||||||
|
|
||||||
void SufamiTurboCartridge::load() {
|
auto SufamiTurboCartridge::load() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SufamiTurboCartridge::unload() {
|
auto SufamiTurboCartridge::unload() -> void {
|
||||||
rom.reset();
|
rom.reset();
|
||||||
ram.reset();
|
ram.reset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
struct SufamiTurboCartridge {
|
struct SufamiTurboCartridge {
|
||||||
|
auto load() -> void;
|
||||||
|
auto unload() -> void;
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
MappedRAM rom;
|
MappedRAM rom;
|
||||||
MappedRAM ram;
|
MappedRAM ram;
|
||||||
|
|
||||||
void load();
|
|
||||||
void unload();
|
|
||||||
void serialize(serializer&);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern SufamiTurboCartridge sufamiturboA;
|
extern SufamiTurboCartridge sufamiturboA;
|
||||||
|
|
|
@ -18,7 +18,7 @@ void System::run() {
|
||||||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||||
|
|
||||||
scheduler.enter();
|
scheduler.enter();
|
||||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) {
|
||||||
video.update();
|
video.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,8 @@ void System::runtosave() {
|
||||||
void System::runthreadtosave() {
|
void System::runthreadtosave() {
|
||||||
while(true) {
|
while(true) {
|
||||||
scheduler.enter();
|
scheduler.enter();
|
||||||
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
|
if(scheduler.exit_reason == Scheduler::ExitReason::SynchronizeEvent) break;
|
||||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) {
|
||||||
video.update();
|
video.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,13 +93,12 @@ void System::term() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::load() {
|
void System::load() {
|
||||||
string manifest = string::read({interface->path(ID::System), "manifest.bml"});
|
//string manifest = string::read({interface->path(ID::System), "manifest.bml"});
|
||||||
auto document = BML::unserialize(manifest);
|
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||||
|
auto document = BML::unserialize(information.manifest);
|
||||||
|
|
||||||
auto iplrom = document["system/smp/rom/name"].text();
|
if(auto iplrom = document["system/smp/rom/name"].text()) {
|
||||||
interface->loadRequest(ID::IPLROM, iplrom);
|
interface->loadRequest(ID::IPLROM, iplrom, true);
|
||||||
if(!file::exists({interface->path(ID::System), iplrom})) {
|
|
||||||
interface->notify("Error: required Super Famicom firmware ipl.rom not found.\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
region = configuration.region;
|
region = configuration.region;
|
||||||
|
@ -113,8 +112,8 @@ void System::load() {
|
||||||
|
|
||||||
audio.coprocessor_enable(false);
|
audio.coprocessor_enable(false);
|
||||||
|
|
||||||
bus.map_reset();
|
bus.reset();
|
||||||
bus.map_xml();
|
bus.map();
|
||||||
|
|
||||||
cpu.enable();
|
cpu.enable();
|
||||||
ppu.enable();
|
ppu.enable();
|
||||||
|
|
|
@ -29,6 +29,10 @@ struct System : property<System> {
|
||||||
|
|
||||||
System();
|
System();
|
||||||
|
|
||||||
|
struct Information {
|
||||||
|
string manifest;
|
||||||
|
} information;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void runthreadtosave();
|
void runthreadtosave();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//request from emulation core to load non-volatile media folder
|
//request from emulation core to load non-volatile media folder
|
||||||
auto Program::loadRequest(unsigned id, string name, string type) -> void {
|
auto Program::loadRequest(unsigned id, string name, string type, bool required) -> void {
|
||||||
string location = BrowserDialog()
|
string location = BrowserDialog()
|
||||||
.setTitle({"Load ", name})
|
.setTitle({"Load ", name})
|
||||||
.setPath({config->library.location, name})
|
.setPath({config->library.location, name})
|
||||||
|
@ -13,16 +13,38 @@ auto Program::loadRequest(unsigned id, string name, string type) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
//request from emulation core to load non-volatile media file
|
//request from emulation core to load non-volatile media file
|
||||||
auto Program::loadRequest(unsigned id, string path) -> void {
|
auto Program::loadRequest(unsigned id, string filename, bool required) -> void {
|
||||||
string location = {mediaPaths(emulator->group(id)), path};
|
string pathname = mediaPaths(emulator->group(id));
|
||||||
if(!file::exists(location)) return;
|
string location = {pathname, filename};
|
||||||
|
if(file::exists(location)) {
|
||||||
mmapstream stream{location};
|
mmapstream stream{location};
|
||||||
return emulator->load(id, stream);
|
return emulator->load(id, stream);
|
||||||
|
}
|
||||||
|
if(filename == "manifest.bml") {
|
||||||
|
string manifest;
|
||||||
|
if(auto fp = popen(string{"icarus -m \"", pathname, "\""}, "r")) {
|
||||||
|
while(true) {
|
||||||
|
auto byte = fgetc(fp);
|
||||||
|
if(byte == EOF) break;
|
||||||
|
manifest.append((char)byte);
|
||||||
|
}
|
||||||
|
pclose(fp);
|
||||||
|
}
|
||||||
|
if(manifest) {
|
||||||
|
memorystream stream{manifest.binary(), manifest.size()};
|
||||||
|
return emulator->load(id, stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(required) MessageDialog().setTitle("higan").setText({
|
||||||
|
"Missing required file: ", location.filename(), "\n\n",
|
||||||
|
"From location:\n", location.pathname()
|
||||||
|
}).error();
|
||||||
}
|
}
|
||||||
|
|
||||||
//request from emulation core to save non-volatile media file
|
//request from emulation core to save non-volatile media file
|
||||||
auto Program::saveRequest(unsigned id, string path) -> void {
|
auto Program::saveRequest(unsigned id, string filename) -> void {
|
||||||
string location = {mediaPaths(emulator->group(id)), path};
|
string pathname = mediaPaths(emulator->group(id));
|
||||||
|
string location = {pathname, filename};
|
||||||
filestream stream{location, file::mode::write};
|
filestream stream{location, file::mode::write};
|
||||||
return emulator->save(id, stream);
|
return emulator->save(id, stream);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@ struct Program : Emulator::Interface::Bind {
|
||||||
auto quit() -> void;
|
auto quit() -> void;
|
||||||
|
|
||||||
//interface.cpp
|
//interface.cpp
|
||||||
auto loadRequest(unsigned id, string name, string type) -> void override;
|
auto loadRequest(unsigned id, string name, string type, bool required) -> void override;
|
||||||
auto loadRequest(unsigned id, string path) -> void override;
|
auto loadRequest(unsigned id, string path, bool required) -> void override;
|
||||||
auto saveRequest(unsigned id, string path) -> void override;
|
auto saveRequest(unsigned id, string path) -> void override;
|
||||||
auto videoColor(unsigned source, uint16 alpha, uint16 red, uint16 green, uint16 blue) -> uint32 override;
|
auto videoColor(unsigned source, uint16 alpha, uint16 red, uint16 green, uint16 blue) -> uint32 override;
|
||||||
auto videoRefresh(const uint32* palette, const uint32* data, unsigned pitch, unsigned width, unsigned height) -> void override;
|
auto videoRefresh(const uint32* palette, const uint32* data, unsigned pitch, unsigned width, unsigned height) -> void override;
|
||||||
|
|
Loading…
Reference in New Issue