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:
Tim Allen 2015-08-04 19:00:55 +10:00
parent 1b0b54a690
commit 0271d6a12b
59 changed files with 561 additions and 459 deletions

View File

@ -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/";

View File

@ -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); }

View File

@ -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});

View File

@ -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;

View File

@ -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()));

View File

@ -9,6 +9,8 @@ struct ID {
}; };
enum : unsigned { enum : unsigned {
SystemManifest,
Manifest, Manifest,
ProgramROM, ProgramROM,
ProgramRAM, ProgramRAM,

View File

@ -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();
} }

View File

@ -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;

View File

@ -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});
} }

View File

@ -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()));

View File

@ -11,6 +11,7 @@ struct ID {
}; };
enum : unsigned { enum : unsigned {
SystemManifest,
GameBoyBootROM, GameBoyBootROM,
SuperGameBoyBootROM, SuperGameBoyBootROM,
GameBoyColorBootROM, GameBoyColorBootROM,

View File

@ -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");
} }
} }

View File

@ -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>

View File

@ -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()});
} }
} }

View File

@ -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()));

View File

@ -9,6 +9,7 @@ struct ID {
}; };
enum : unsigned { enum : unsigned {
SystemManifest,
BIOS, BIOS,
Manifest, Manifest,

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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; }

View File

@ -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);

View File

@ -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)))
); );
} }

View File

@ -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();

View File

@ -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, &gtkIter, 0, checked, -1); gtk_list_store_set(parent->gtkListStore, &gtkIter, 0, checked, -1);

View File

@ -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;

View File

@ -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();
} }

View File

@ -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;

View File

@ -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};
} }

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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); }

View File

@ -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++) {

View File

@ -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()});
} }
} }

View File

@ -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")) {

View File

@ -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;
} }

View File

@ -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;

View File

@ -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);
}
} }

View File

@ -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"

View File

@ -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);
}
}
}
}
} }

View File

@ -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;
}; };

View File

@ -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);
} }

View File

@ -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;
}
}
} }
} }

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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());
} }

View File

@ -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();
} }

View File

@ -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;

View File

@ -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();

View File

@ -29,6 +29,10 @@ struct System : property<System> {
System(); System();
struct Information {
string manifest;
} information;
private: private:
void runthreadtosave(); void runthreadtosave();

View File

@ -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};
mmapstream stream{location}; if(file::exists(location)) {
return emulator->load(id, stream); mmapstream stream{location};
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);
} }

View File

@ -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;