diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index 2c0ee370..5c122adc 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -8,7 +8,7 @@ using namespace nall; namespace Emulator { 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 License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/emulator/interface.hpp b/emulator/interface.hpp index 349b5a8c..b0655290 100644 --- a/emulator/interface.hpp +++ b/emulator/interface.hpp @@ -47,8 +47,8 @@ struct Interface { vector port; struct Bind { - virtual auto loadRequest(unsigned, string, string) -> void {} - virtual auto loadRequest(unsigned, string) -> void {} + virtual auto loadRequest(unsigned, string, string, bool) -> void {} + virtual auto loadRequest(unsigned, string, bool) -> 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 videoRefresh(const uint32_t*, const uint32_t*, unsigned, unsigned, unsigned) -> void {} @@ -62,8 +62,8 @@ struct Interface { Bind* bind = nullptr; //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 path) -> void { return bind->loadRequest(id, path); } + auto loadRequest(unsigned id, string name, string type, bool required) -> void { return bind->loadRequest(id, name, type, required); } + 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 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); } diff --git a/fc/cartridge/board/board.cpp b/fc/cartridge/board/board.cpp index b33ba360..508f37d2 100644 --- a/fc/cartridge/board/board.cpp +++ b/fc/cartridge/board/board.cpp @@ -104,10 +104,10 @@ Board::Board(Markup::Node& document) { if(chrrom.size) chrrom.data = new uint8[chrrom.size](); if(chrram.size) chrram.data = new uint8[chrram.size](); - if(auto name = prom["name"].text()) interface->loadRequest(ID::ProgramROM, name); - if(auto name = pram["name"].text()) interface->loadRequest(ID::ProgramRAM, name); - if(auto name = crom["name"].text()) interface->loadRequest(ID::CharacterROM, name); - if(auto name = cram["name"].text()) interface->loadRequest(ID::CharacterRAM, name); + if(auto name = prom["name"].text()) interface->loadRequest(ID::ProgramROM, name, true); + if(auto name = pram["name"].text()) interface->loadRequest(ID::ProgramRAM, name, false); + if(auto name = crom["name"].text()) interface->loadRequest(ID::CharacterROM, name, true); + 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 = cram["name"].text()) Famicom::cartridge.memory.append({ID::CharacterRAM, name}); diff --git a/fc/cartridge/cartridge.cpp b/fc/cartridge/cartridge.cpp index 844c3b0d..ee4b500c 100644 --- a/fc/cartridge/cartridge.cpp +++ b/fc/cartridge/cartridge.cpp @@ -19,7 +19,7 @@ void Cartridge::main() { } 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 if(board == nullptr) return; diff --git a/fc/interface/interface.cpp b/fc/interface/interface.cpp index 1d38d2b0..e495a48d 100644 --- a/fc/interface/interface.cpp +++ b/fc/interface/interface.cpp @@ -26,6 +26,8 @@ string Interface::sha256() { unsigned Interface::group(unsigned id) { switch(id) { + case ID::SystemManifest: + return 0; case ID::Manifest: case ID::ProgramROM: case ID::ProgramRAM: @@ -48,7 +50,13 @@ void Interface::save() { } 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) { stream.read(cartridge.board->prgrom.data, min(cartridge.board->prgrom.size, stream.size())); diff --git a/fc/interface/interface.hpp b/fc/interface/interface.hpp index 63e9194a..9e5c1a78 100644 --- a/fc/interface/interface.hpp +++ b/fc/interface/interface.hpp @@ -9,6 +9,8 @@ struct ID { }; enum : unsigned { + SystemManifest, + Manifest, ProgramROM, ProgramRAM, diff --git a/fc/system/system.cpp b/fc/system/system.cpp index 39955f4d..2de228c9 100644 --- a/fc/system/system.cpp +++ b/fc/system/system.cpp @@ -42,8 +42,8 @@ void System::runthreadtosave() { } void System::load() { - 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); serialize_init(); } diff --git a/fc/system/system.hpp b/fc/system/system.hpp index 88b73c57..bda553a9 100644 --- a/fc/system/system.hpp +++ b/fc/system/system.hpp @@ -17,6 +17,10 @@ struct System { void serialize_all(serializer&); void serialize_init(); unsigned serialize_size; + + struct Information { + string manifest; + } information; }; extern System system; diff --git a/gb/cartridge/cartridge.cpp b/gb/cartridge/cartridge.cpp index 2d5a1a82..c54bb164 100644 --- a/gb/cartridge/cartridge.cpp +++ b/gb/cartridge/cartridge.cpp @@ -35,7 +35,7 @@ void Cartridge::load(System::Revision revision) { system.revision = revision; //needed for ID::Manifest to return correct group ID if(revision != System::Revision::SuperGameBoy) { - interface->loadRequest(ID::Manifest, "manifest.bml"); + interface->loadRequest(ID::Manifest, "manifest.bml", true); } information.mapper = Mapper::Unknown; @@ -74,8 +74,8 @@ void Cartridge::load(System::Revision revision) { //Super Game Boy core loads memory from Super Famicom core if(revision != System::Revision::SuperGameBoy) { - if(auto name = rom["name"].text()) interface->loadRequest(ID::ROM, name); - if(auto name = ram["name"].text()) interface->loadRequest(ID::RAM, name); + if(auto name = rom["name"].text()) interface->loadRequest(ID::ROM, name, true); + if(auto name = ram["name"].text()) interface->loadRequest(ID::RAM, name, false); if(auto name = ram["name"].text()) memory.append({ID::RAM, name}); } diff --git a/gb/interface/interface.cpp b/gb/interface/interface.cpp index fdddfc6e..0f8f3775 100644 --- a/gb/interface/interface.cpp +++ b/gb/interface/interface.cpp @@ -38,6 +38,7 @@ string Interface::sha256() { unsigned Interface::group(unsigned id) { switch(id) { + case ID::SystemManifest: case ID::GameBoyBootROM: case ID::SuperGameBoyBootROM: case ID::GameBoyColorBootROM: @@ -68,6 +69,10 @@ void Interface::save() { } void Interface::load(unsigned id, const stream& stream) { + if(id == ID::SystemManifest) { + system.information.manifest = stream.text(); + } + if(id == ID::GameBoyBootROM) { 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())); } - if(id == ID::Manifest) cartridge.information.markup = stream.text(); + if(id == ID::Manifest) { + cartridge.information.markup = stream.text(); + } if(id == ID::ROM) { stream.read(cartridge.romdata, min(cartridge.romsize, stream.size())); diff --git a/gb/interface/interface.hpp b/gb/interface/interface.hpp index 822eb1f0..54159ec0 100644 --- a/gb/interface/interface.hpp +++ b/gb/interface/interface.hpp @@ -11,6 +11,7 @@ struct ID { }; enum : unsigned { + SystemManifest, GameBoyBootROM, SuperGameBoyBootROM, GameBoyColorBootROM, diff --git a/gb/system/system.cpp b/gb/system/system.cpp index f8f72344..9813e141 100644 --- a/gb/system/system.cpp +++ b/gb/system/system.cpp @@ -49,16 +49,14 @@ void System::load(Revision revision) { serialize_init(); if(revision == Revision::SuperGameBoy) return; //Super Famicom core loads boot ROM for SGB - 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 bootROM = document["system/cpu/rom/name"].text(); - interface->loadRequest( - revision == Revision::GameBoy ? ID::GameBoyBootROM : ID::GameBoyColorBootROM, - bootROM - ); - if(!file::exists({interface->path(ID::System), bootROM})) { - interface->notify("Error: required Game Boy firmware boot.rom not found.\n"); + if(auto bootROM = document["system/cpu/rom/name"].text()) { + interface->loadRequest( + revision == Revision::GameBoy ? ID::GameBoyBootROM : ID::GameBoyColorBootROM, + bootROM, true + ); } } diff --git a/gb/system/system.hpp b/gb/system/system.hpp index 736880c5..bd3a4a51 100644 --- a/gb/system/system.hpp +++ b/gb/system/system.hpp @@ -42,6 +42,10 @@ struct System { void serialize_init(); System(); + + struct Information { + string manifest; + } information; }; #include diff --git a/gba/cartridge/cartridge.cpp b/gba/cartridge/cartridge.cpp index 815ef8c9..d8c87a9c 100644 --- a/gba/cartridge/cartridge.cpp +++ b/gba/cartridge/cartridge.cpp @@ -12,7 +12,7 @@ string Cartridge::title() { } void Cartridge::load() { - interface->loadRequest(ID::Manifest, "manifest.bml"); + interface->loadRequest(ID::Manifest, "manifest.bml", true); auto document = BML::unserialize(information.markup); information.title = document["information/title"].text(); @@ -20,7 +20,7 @@ void Cartridge::load() { unsigned rom_size = 0; if(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(); for(unsigned addr = rom_size; addr < rom.size; addr++) { rom.data[addr] = rom.data[Bus::mirror(addr, rom_size)]; @@ -40,7 +40,7 @@ void Cartridge::load() { ram.mask = ram.size - 1; 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()}); } @@ -53,7 +53,7 @@ void Cartridge::load() { eeprom.test = rom_size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000; 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()}); } @@ -68,7 +68,7 @@ void Cartridge::load() { if(!flashrom.id && flashrom.size == 64 * 1024) flashrom.id = 0x1cc2; 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()}); } } diff --git a/gba/interface/interface.cpp b/gba/interface/interface.cpp index e5618a5c..22a96c5b 100644 --- a/gba/interface/interface.cpp +++ b/gba/interface/interface.cpp @@ -22,6 +22,7 @@ bool Interface::loaded() { unsigned Interface::group(unsigned id) { switch(id) { + case ID::SystemManifest: case ID::BIOS: return ID::System; case ID::Manifest: @@ -46,11 +47,17 @@ void Interface::save() { } void Interface::load(unsigned id, const stream& stream) { + if(id == ID::SystemManifest) { + system.information.manifest = stream.text(); + } + if(id == ID::BIOS) { 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) { stream.read(cartridge.rom.data, min(cartridge.rom.size, stream.size())); diff --git a/gba/interface/interface.hpp b/gba/interface/interface.hpp index 8553e19a..f4d9bfca 100644 --- a/gba/interface/interface.hpp +++ b/gba/interface/interface.hpp @@ -9,6 +9,7 @@ struct ID { }; enum : unsigned { + SystemManifest, BIOS, Manifest, diff --git a/gba/system/system.cpp b/gba/system/system.cpp index ee7e2028..ebcb2cba 100644 --- a/gba/system/system.cpp +++ b/gba/system/system.cpp @@ -24,13 +24,11 @@ void System::power() { } void System::load() { - 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 bios = document["system/cpu/rom/name"].text(); - interface->loadRequest(ID::BIOS, bios); - if(!file::exists({interface->path(ID::System), bios})) { - interface->notify("Error: required Game Boy Advance firmware bios.rom not found.\n"); + if(auto bios = document["system/cpu/rom/name"].text()) { + interface->loadRequest(ID::BIOS, bios, true); } serialize_init(); diff --git a/gba/system/system.hpp b/gba/system/system.hpp index 6e82244d..8ff55476 100644 --- a/gba/system/system.hpp +++ b/gba/system/system.hpp @@ -31,6 +31,10 @@ struct System { void serialize(serializer&); void serialize_all(serializer&); void serialize_init(); + + struct Information { + string manifest; + } information; }; extern BIOS bios; diff --git a/hiro/core/core.hpp b/hiro/core/core.hpp index 3e6afa68..5980a531 100644 --- a/hiro/core/core.hpp +++ b/hiro/core/core.hpp @@ -1353,12 +1353,14 @@ struct mListViewItem : mObject { auto backgroundColor() const -> Color; auto cell(unsigned position) const -> ListViewCell; auto cells() const -> unsigned; + auto checkable() const -> bool; auto checked() const -> bool; auto foregroundColor() const -> Color; auto remove() -> type& override; auto remove(sListViewCell cell) -> type&; auto selected() const -> bool; auto setBackgroundColor(Color color = {}) -> type&; + auto setCheckable(bool checkable = true) -> type&; auto setChecked(bool checked = true) -> type&; auto setFocused() -> type& override; auto setForegroundColor(Color color = {}) -> type&; @@ -1369,6 +1371,7 @@ struct mListViewItem : mObject { struct State { Color backgroundColor; vector cells; + bool checkable = true; bool checked = false; Color foregroundColor; bool selected = false; diff --git a/hiro/core/shared.hpp b/hiro/core/shared.hpp index 86dc79f6..00c3a671 100644 --- a/hiro/core/shared.hpp +++ b/hiro/core/shared.hpp @@ -502,11 +502,13 @@ struct ListViewItem : sListViewItem { auto backgroundColor() const { return self().backgroundColor(); } auto cell(unsigned position) const { return self().cell(position); } auto cells() const { return self().cells(); } + auto checkable() const { return self().checkable(); } auto checked() const { return self().checked(); } auto foregroundColor() const { return self().foregroundColor(); } auto remove(sListViewCell cell) { return self().remove(cell), *this; } auto selected() const { return self().selected(); } 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 setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; } auto setSelected(bool selected = true) { return self().setSelected(selected), *this; } diff --git a/hiro/core/widget/list-view-item.cpp b/hiro/core/widget/list-view-item.cpp index d3629fe0..87209bb9 100644 --- a/hiro/core/widget/list-view-item.cpp +++ b/hiro/core/widget/list-view-item.cpp @@ -26,8 +26,12 @@ auto mListViewItem::cells() const -> unsigned { return state.cells.size(); } +auto mListViewItem::checkable() const -> bool { + return state.checkable; +} + auto mListViewItem::checked() const -> bool { - return state.checked; + return state.checkable && state.checked; } auto mListViewItem::foregroundColor() const -> Color { @@ -59,6 +63,12 @@ auto mListViewItem::setBackgroundColor(Color color) -> type& { return *this; } +auto mListViewItem::setCheckable(bool checkable) -> type& { + state.checkable = checkable; + signal(setCheckable, checkable); + return *this; +} + auto mListViewItem::setChecked(bool checked) -> type& { state.checked = checked; signal(setChecked, checked); diff --git a/hiro/extension/browser-dialog.cpp b/hiro/extension/browser-dialog.cpp index 903ed8d6..c71baa37 100644 --- a/hiro/extension/browser-dialog.cpp +++ b/hiro/extension/browser-dialog.cpp @@ -163,7 +163,6 @@ auto BrowserDialogWindow::setPath(string path) -> void { view.reset(); view.append(ListViewColumn().setExpandable()); - view.append(ListViewColumn().setForegroundColor({192, 128, 128})); auto contents = directory::icontents(path); bool folderMode = state.action == "openFolder"; @@ -175,7 +174,6 @@ auto BrowserDialogWindow::setPath(string path) -> void { view.append(ListViewItem() .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() .append(ListViewCell().setText(content).setIcon(folderMode ? Icon::Action::Open : Icon::Emblem::File)) - .append(ListViewCell().setText(octal(file_system_object::mode({path, content}) & 0777, 3L))) ); } diff --git a/hiro/gtk/widget/list-view-column.cpp b/hiro/gtk/widget/list-view-column.cpp index 0a6834a7..a37b76d4 100644 --- a/hiro/gtk/widget/list-view-column.cpp +++ b/hiro/gtk/widget/list-view-column.cpp @@ -22,6 +22,7 @@ auto pListViewColumn::construct() -> void { gtkCellToggle = gtk_cell_renderer_toggle_new(); 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_cell_data_func(gtkColumn, GTK_CELL_RENDERER(gtkCellToggle), (GtkTreeCellDataFunc)ListView_cellRendererToggleDataFunc, (gpointer)_parent(), nullptr); } gtkCellIcon = gtk_cell_renderer_pixbuf_new(); diff --git a/hiro/gtk/widget/list-view-item.cpp b/hiro/gtk/widget/list-view-item.cpp index ca597256..2efaa9e7 100644 --- a/hiro/gtk/widget/list-view-item.cpp +++ b/hiro/gtk/widget/list-view-item.cpp @@ -17,6 +17,9 @@ auto pListViewItem::remove(sListViewCell cell) -> void { auto pListViewItem::setBackgroundColor(Color color) -> void { } +auto pListViewItem::setCheckable(bool checkable) -> void { +} + auto pListViewItem::setChecked(bool checked) -> void { if(auto parent = _parent()) { gtk_list_store_set(parent->gtkListStore, >kIter, 0, checked, -1); diff --git a/hiro/gtk/widget/list-view-item.hpp b/hiro/gtk/widget/list-view-item.hpp index 0695ba21..51542b18 100644 --- a/hiro/gtk/widget/list-view-item.hpp +++ b/hiro/gtk/widget/list-view-item.hpp @@ -8,6 +8,7 @@ struct pListViewItem : pObject { auto append(sListViewCell cell) -> void; auto remove(sListViewCell cell) -> void; auto setBackgroundColor(Color color) -> void; + auto setCheckable(bool checkable) -> void; auto setChecked(bool checked) -> void; auto setFocused() -> void; auto setForegroundColor(Color color) -> void; diff --git a/hiro/gtk/widget/list-view.cpp b/hiro/gtk/widget/list-view.cpp index 5308645b..d6e2df9e 100644 --- a/hiro/gtk/widget/list-view.cpp +++ b/hiro/gtk/widget/list-view.cpp @@ -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_mouseMoveEvent(GtkWidget*, GdkEvent*, pListView* p) -> signed { return p->_doMouseMove(); } 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); } auto pListView::construct() -> void { @@ -276,6 +278,15 @@ auto pListView::_doActivate() -> void { 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 { if(!locked()) _updateSelected(); } diff --git a/hiro/gtk/widget/list-view.hpp b/hiro/gtk/widget/list-view.hpp index 852e4298..125c2db0 100644 --- a/hiro/gtk/widget/list-view.hpp +++ b/hiro/gtk/widget/list-view.hpp @@ -31,6 +31,7 @@ struct pListView : pWidget { auto _columnWidth(unsigned column) -> unsigned; auto _createModel() -> void; auto _doActivate() -> void; + auto _doCellRendererToggleDataFunc(GtkCellRenderer* renderer, GtkTreeIter* iter) -> void; auto _doChange() -> void; auto _doContext() -> void; auto _doEdit(GtkCellRendererText* renderer, const char* path, const char* text) -> void; diff --git a/hiro/windows/widget/check-label.cpp b/hiro/windows/widget/check-label.cpp index 60fd9b36..d263c7bb 100644 --- a/hiro/windows/widget/check-label.cpp +++ b/hiro/windows/widget/check-label.cpp @@ -18,7 +18,7 @@ auto pCheckLabel::destruct() -> void { DestroyWindow(hwnd); } -auto pCheckLabel::minimumSize() -> Size { +auto pCheckLabel::minimumSize() const -> Size { auto size = pFont::size(hfont, state().text); return {size.width() + 20, size.height() + 4}; } diff --git a/hiro/windows/widget/check-label.hpp b/hiro/windows/widget/check-label.hpp index f9bad68b..9b6f3a96 100644 --- a/hiro/windows/widget/check-label.hpp +++ b/hiro/windows/widget/check-label.hpp @@ -5,7 +5,7 @@ namespace hiro { struct pCheckLabel : pWidget { Declare(CheckLabel, Widget) - auto minimumSize() -> Size; + auto minimumSize() const -> Size override; auto setChecked(bool checked) -> void; auto setText(const string& text) -> void; diff --git a/hiro/windows/widget/list-view-item.cpp b/hiro/windows/widget/list-view-item.cpp index c4d6371b..34e19b32 100644 --- a/hiro/windows/widget/list-view-item.cpp +++ b/hiro/windows/widget/list-view-item.cpp @@ -17,6 +17,9 @@ auto pListViewItem::remove(sListViewCell cell) -> void { auto pListViewItem::setBackgroundColor(Color color) -> void { } +auto pListViewItem::setCheckable(bool checkable) -> void { +} + auto pListViewItem::setChecked(bool checked) -> void { if(auto parent = _parent()) { parent->lock(); diff --git a/hiro/windows/widget/list-view-item.hpp b/hiro/windows/widget/list-view-item.hpp index 0cbde662..159c9d61 100644 --- a/hiro/windows/widget/list-view-item.hpp +++ b/hiro/windows/widget/list-view-item.hpp @@ -8,6 +8,7 @@ struct pListViewItem : pObject { auto append(sListViewCell cell) -> void; auto remove(sListViewCell cell) -> void; auto setBackgroundColor(Color color) -> void; + auto setCheckable(bool checkable) -> void; auto setChecked(bool checked) -> void; auto setFocused() -> void; auto setForegroundColor(Color color) -> void; diff --git a/hiro/windows/widget/list-view.cpp b/hiro/windows/widget/list-view.cpp index 85bd425e..66c89ab5 100644 --- a/hiro/windows/widget/list-view.cpp +++ b/hiro/windows/widget/list-view.cpp @@ -275,7 +275,7 @@ auto pListView::onCustomDraw(LPARAM lparam) -> LRESULT { HBRUSH brush = CreateSolidBrush(selected ? GetSysColor(COLOR_HIGHLIGHT) : CreateRGB(_backgroundColor(row, column))); FillRect(hdc, &rc, brush); DeleteObject(brush); - if(state().checkable && column == 0) { + if(state().checkable && self().item(row).checkable() && column == 0) { if(auto htheme = OpenThemeData(hwnd, L"BUTTON")) { unsigned state = checked ? CBS_CHECKEDNORMAL : CBS_UNCHECKEDNORMAL; SIZE size; diff --git a/nall/GNUmakefile b/nall/GNUmakefile index f2b6ee05..1d836fcf 100644 --- a/nall/GNUmakefile +++ b/nall/GNUmakefile @@ -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 [0-9] = 0 1 2 3 4 5 6 7 8 9 diff --git a/nall/intrinsics.hpp b/nall/intrinsics.hpp index 28622648..d0d6cb36 100644 --- a/nall/intrinsics.hpp +++ b/nall/intrinsics.hpp @@ -37,11 +37,15 @@ namespace nall { #elif defined(__GNUC__) #define 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) #define 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 #warning "unable to detect compiler" #define COMPILER_UNKNOWN diff --git a/nall/string/base.hpp b/nall/string/base.hpp index 1f4ec0cf..51b5bb66 100644 --- a/nall/string/base.hpp +++ b/nall/string/base.hpp @@ -340,6 +340,7 @@ template auto append(lstring& self, const string& value, P&&... p inline auto append(lstring& self) -> lstring&; inline auto find(const lstring& self, const string& source) -> maybe; inline auto ifind(const lstring& self, const string& source) -> maybe; +inline auto match(const lstring& self, const string& pattern) -> lstring; inline auto merge(const lstring& self, const string& separator) -> string; inline auto strip(lstring& self) -> lstring&; @@ -368,6 +369,7 @@ struct lstring : vector { template auto append(P&&... p) -> type& { return nall::append(*this, forward

(p)...); } auto find(const string& source) const -> maybe { return nall::find(*this, source); } auto ifind(const string& source) const -> maybe { 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 strip() -> type& { return nall::strip(*this); } diff --git a/nall/string/list.hpp b/nall/string/list.hpp index 6b9f4f78..1ada1c90 100644 --- a/nall/string/list.hpp +++ b/nall/string/list.hpp @@ -46,6 +46,14 @@ auto ifind(const lstring& self, const string& source) -> maybe { 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 { string output; for(unsigned n = 0; n < self.size(); n++) { diff --git a/sfc/cartridge/cartridge.cpp b/sfc/cartridge/cartridge.cpp index 2acbc99a..2647d9de 100644 --- a/sfc/cartridge/cartridge.cpp +++ b/sfc/cartridge/cartridge.cpp @@ -62,7 +62,7 @@ auto Cartridge::load() -> void { information.title.sufamiTurboA = ""; information.title.sufamiTurboB = ""; - interface->loadRequest(ID::Manifest, "manifest.bml"); + interface->loadRequest(ID::Manifest, "manifest.bml", true); parseMarkup(information.markup.cartridge); //Super Game Boy @@ -115,7 +115,7 @@ auto Cartridge::load() -> 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); information.title.gameBoy = document["information/title"].text(); @@ -125,13 +125,13 @@ auto Cartridge::loadSuperGameBoy() -> void { GameBoy::cartridge.information.markup = information.markup.gameBoy; GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy); - if(auto name = rom["name"].text()) interface->loadRequest(ID::SuperGameBoyROM, name); - if(auto name = ram["name"].text()) interface->loadRequest(ID::SuperGameBoyRAM, name); + if(auto name = rom["name"].text()) interface->loadRequest(ID::SuperGameBoyROM, name, true); + if(auto name = ram["name"].text()) interface->loadRequest(ID::SuperGameBoyRAM, name, false); if(auto name = ram["name"].text()) memory.append({ID::SuperGameBoyRAM, name}); } auto Cartridge::loadSatellaview() -> void { - interface->loadRequest(ID::SatellaviewManifest, "manifest.bml"); + interface->loadRequest(ID::SatellaviewManifest, "manifest.bml", true); auto document = BML::unserialize(information.markup.satellaview); information.title.satellaview = document["information/title"].text(); @@ -140,14 +140,14 @@ auto Cartridge::loadSatellaview() -> void { if(rom["name"]) { unsigned size = rom["size"].decimal(); satellaviewcartridge.memory.map(allocate(size, 0xff), size); - interface->loadRequest(ID::SatellaviewROM, rom["name"].text()); + interface->loadRequest(ID::SatellaviewROM, rom["name"].text(), true); satellaviewcartridge.readonly = (rom["type"].text() == "MaskROM"); } } auto Cartridge::loadSufamiTurboA() -> void { - interface->loadRequest(ID::SufamiTurboSlotAManifest, "manifest.bml"); + interface->loadRequest(ID::SufamiTurboSlotAManifest, "manifest.bml", true); auto document = BML::unserialize(information.markup.sufamiTurboA); information.title.sufamiTurboA = document["information/title"].text(); @@ -157,23 +157,23 @@ auto Cartridge::loadSufamiTurboA() -> void { if(rom["name"]) { unsigned size = rom["size"].decimal(); sufamiturboA.rom.map(allocate(size, 0xff), size); - interface->loadRequest(ID::SufamiTurboSlotAROM, rom["name"].text()); + interface->loadRequest(ID::SufamiTurboSlotAROM, rom["name"].text(), true); } if(ram["name"]) { unsigned size = ram["size"].decimal(); sufamiturboA.ram.map(allocate(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()}); } 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 { - interface->loadRequest(ID::SufamiTurboSlotBManifest, "manifest.bml"); + interface->loadRequest(ID::SufamiTurboSlotBManifest, "manifest.bml", true); auto document = BML::unserialize(information.markup.sufamiTurboB); information.title.sufamiTurboB = document["information/title"].text(); @@ -183,13 +183,13 @@ auto Cartridge::loadSufamiTurboB() -> void { if(rom["name"]) { unsigned size = rom["size"].decimal(); sufamiturboB.rom.map(allocate(size, 0xff), size); - interface->loadRequest(ID::SufamiTurboSlotBROM, rom["name"].text()); + interface->loadRequest(ID::SufamiTurboSlotBROM, rom["name"].text(), true); } if(ram["name"]) { unsigned size = ram["size"].decimal(); sufamiturboB.ram.map(allocate(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()}); } } diff --git a/sfc/cartridge/markup.cpp b/sfc/cartridge/markup.cpp index f76da21d..7e8f9ca8 100644 --- a/sfc/cartridge/markup.cpp +++ b/sfc/cartridge/markup.cpp @@ -51,7 +51,7 @@ auto Cartridge::parseMarkupMemory(MappedRAM& ram, Markup::Node node, unsigned id unsigned size = node["size"].decimal(); ram.map(allocate(size, 0xff), size); if(name) { - interface->loadRequest(id, name); + interface->loadRequest(id, name, !writable); //treat ROM as required; RAM as optional if(writable) memory.append({id, name}); } } @@ -83,10 +83,10 @@ auto Cartridge::parseMarkupICD2(Markup::Node root) -> void { icd2.revision = max(1, root["revision"].decimal()); 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(); - interface->loadRequest(ID::SuperGameBoyBootROM, bootROMName); + interface->loadRequest(ID::SuperGameBoyBootROM, bootROMName, true); for(auto node : root.find("map")) { if(node["id"].text() == "io") { @@ -309,10 +309,10 @@ auto Cartridge::parseMarkupARMDSP(Markup::Node root) -> void { string dataROMName = rom(1)["name"].text(); string dataRAMName = ram(0)["name"].text(); - interface->loadRequest(ID::ArmDSPPROM, programROMName); - interface->loadRequest(ID::ArmDSPDROM, dataROMName); + interface->loadRequest(ID::ArmDSPPROM, programROMName, true); + interface->loadRequest(ID::ArmDSPDROM, dataROMName, true); if(dataRAMName.empty() == false) { - interface->loadRequest(ID::ArmDSPRAM, dataRAMName); + interface->loadRequest(ID::ArmDSPRAM, dataRAMName, false); 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 dataRAMName = ram(1)["name"].text(); - interface->loadRequest(ID::HitachiDSPDROM, dataROMName); + interface->loadRequest(ID::HitachiDSPDROM, dataROMName, true); if(dataRAMName.empty() == false) { - interface->loadRequest(ID::HitachiDSPDRAM, dataRAMName); + interface->loadRequest(ID::HitachiDSPDRAM, dataRAMName, false); } for(auto node : root.find("map")) { @@ -394,19 +394,19 @@ auto Cartridge::parseMarkupNECDSP(Markup::Node root) -> void { string dataRAMName = ram(0)["name"].text(); if(necdsp.revision == NECDSP::Revision::uPD7725) { - interface->loadRequest(ID::Nec7725DSPPROM, programROMName); - interface->loadRequest(ID::Nec7725DSPDROM, dataROMName); + interface->loadRequest(ID::Nec7725DSPPROM, programROMName, true); + interface->loadRequest(ID::Nec7725DSPDROM, dataROMName, true); if(dataRAMName.empty() == false) { - interface->loadRequest(ID::Nec7725DSPRAM, dataRAMName); + interface->loadRequest(ID::Nec7725DSPRAM, dataRAMName, false); memory.append({ID::Nec7725DSPRAM, dataRAMName}); } } if(necdsp.revision == NECDSP::Revision::uPD96050) { - interface->loadRequest(ID::Nec96050DSPPROM, programROMName); - interface->loadRequest(ID::Nec96050DSPDROM, dataROMName); + interface->loadRequest(ID::Nec96050DSPPROM, programROMName, true); + interface->loadRequest(ID::Nec96050DSPDROM, dataROMName, true); if(dataRAMName.empty() == false) { - interface->loadRequest(ID::Nec96050DSPRAM, dataRAMName); + interface->loadRequest(ID::Nec96050DSPRAM, dataRAMName, false); memory.append({ID::Nec96050DSPRAM, dataRAMName}); } } @@ -431,7 +431,7 @@ auto Cartridge::parseMarkupEpsonRTC(Markup::Node root) -> void { hasEpsonRTC = true; string name = root["ram/name"].text(); - interface->loadRequest(ID::EpsonRTC, name); + interface->loadRequest(ID::EpsonRTC, name, false); memory.append({ID::EpsonRTC, name}); for(auto node : root.find("map")) { @@ -447,7 +447,7 @@ auto Cartridge::parseMarkupSharpRTC(Markup::Node root) -> void { hasSharpRTC = true; string name = root["ram/name"].text(); - interface->loadRequest(ID::SharpRTC, name); + interface->loadRequest(ID::SharpRTC, name, false); memory.append({ID::SharpRTC, name}); for(auto node : root.find("map")) { diff --git a/sfc/cheat/cheat.cpp b/sfc/cheat/cheat.cpp index 8eee902e..14a2ef64 100644 --- a/sfc/cheat/cheat.cpp +++ b/sfc/cheat/cheat.cpp @@ -5,19 +5,19 @@ namespace SuperFamicom { Cheat cheat; -void Cheat::reset() { +auto Cheat::reset() -> void { codes.reset(); } -void Cheat::append(unsigned addr, unsigned data) { +auto Cheat::append(unsigned addr, unsigned data) -> void { 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}); } -maybe Cheat::find(unsigned addr, unsigned comp) { +auto Cheat::find(unsigned addr, unsigned comp) -> maybe { //WRAM mirroring: $00-3f,80-bf:0000-1fff -> $7e:0000-1fff if((addr & 0x40e000) == 0x000000) addr = 0x7e0000 | (addr & 0x1fff); @@ -26,6 +26,7 @@ maybe Cheat::find(unsigned addr, unsigned comp) { return code.data; } } + return nothing; } diff --git a/sfc/cheat/cheat.hpp b/sfc/cheat/cheat.hpp index 43f2d05c..d97fafe8 100644 --- a/sfc/cheat/cheat.hpp +++ b/sfc/cheat/cheat.hpp @@ -1,17 +1,19 @@ 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; + struct Code { unsigned addr; unsigned comp; unsigned data; }; vector 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 find(unsigned addr, unsigned comp); }; extern Cheat cheat; diff --git a/sfc/controller/controller.cpp b/sfc/controller/controller.cpp index 92f1d77e..69932835 100644 --- a/sfc/controller/controller.cpp +++ b/sfc/controller/controller.cpp @@ -10,21 +10,25 @@ namespace SuperFamicom { #include "justifier/justifier.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.port2->thread) input.port2->enter(); } -void Controller::enter() { +auto Controller::enter() -> void { while(true) step(1); } -void Controller::step(unsigned clocks) { +auto Controller::step(unsigned clocks) -> void { clock += clocks * (uint64)cpu.frequency; synchronize_cpu(); } -void Controller::synchronize_cpu() { +auto Controller::synchronize_cpu() -> void { if(CPU::Threaded == true) { if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); } else { @@ -32,22 +36,18 @@ void Controller::synchronize_cpu() { } } -bool Controller::iobit() { +auto Controller::iobit() -> bool { switch(port) { case Controller::Port1: return cpu.pio() & 0x40; case Controller::Port2: return cpu.pio() & 0x80; } } -void Controller::iobit(bool data) { +auto Controller::iobit(bool data) -> void { switch(port) { case Controller::Port1: bus.write(0x4201, (cpu.pio() & ~0x40) | (data << 6)); 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); -} - } diff --git a/sfc/controller/controller.hpp b/sfc/controller/controller.hpp index cdca5bad..2f9c4368 100644 --- a/sfc/controller/controller.hpp +++ b/sfc/controller/controller.hpp @@ -13,18 +13,21 @@ struct Controller : Thread { 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); + + 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" diff --git a/sfc/interface/interface.cpp b/sfc/interface/interface.cpp index 205a06cc..94f9b985 100644 --- a/sfc/interface/interface.cpp +++ b/sfc/interface/interface.cpp @@ -4,31 +4,150 @@ namespace SuperFamicom { 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(); } -double Interface::videoFrequency() { +auto Interface::videoFrequency() -> double { switch(system.region()) { default: 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); } } -double Interface::audioFrequency() { +auto Interface::audioFrequency() -> double { return system.apu_frequency() / 768.0; } -bool Interface::loaded() { +auto Interface::loaded() -> bool { return cartridge.loaded(); } -string Interface::sha256() { +auto Interface::sha256() -> string { return cartridge.sha256(); } -unsigned Interface::group(unsigned id) { +auto Interface::group(unsigned id) -> unsigned { switch(id) { + case ID::SystemManifest: case ID::IPLROM: return 0; case ID::Manifest: @@ -94,7 +213,7 @@ unsigned Interface::group(unsigned id) { throw; } -void Interface::load(unsigned id) { +auto Interface::load(unsigned id) -> void { if(id == ID::SuperFamicom) cartridge.load(); if(id == ID::SuperGameBoy) cartridge.loadSuperGameBoy(); if(id == ID::Satellaview) cartridge.loadSatellaview(); @@ -102,13 +221,17 @@ void Interface::load(unsigned id) { if(id == ID::SufamiTurboSlotB) cartridge.loadSufamiTurboB(); } -void Interface::save() { +auto Interface::save() -> void { for(auto& memory : cartridge.memory) { 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) { 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); } -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::EventRAM) stream.write(event.ram.data(), event.ram.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()); } -void Interface::unload() { +auto Interface::unload() -> void { save(); cartridge.unload(); } -void Interface::connect(unsigned port, unsigned device) { +auto Interface::connect(unsigned port, unsigned device) -> void { input.connect(port, (Input::Device)device); } -void Interface::power() { +auto Interface::power() -> void { system.power(); } -void Interface::reset() { +auto Interface::reset() -> void { system.reset(); } -void Interface::run() { +auto Interface::run() -> void { system.run(); } -bool Interface::rtc() { +auto Interface::rtc() -> bool { if(cartridge.hasEpsonRTC()) return true; if(cartridge.hasSharpRTC()) return true; return false; } -void Interface::rtcsync() { +auto Interface::rtcsync() -> void { if(cartridge.hasEpsonRTC()) epsonrtc.sync(); if(cartridge.hasSharpRTC()) sharprtc.sync(); } -serializer Interface::serialize() { +auto Interface::serialize() -> serializer { system.runtosave(); return system.serialize(); } -bool Interface::unserialize(serializer& s) { +auto Interface::unserialize(serializer& s) -> bool { return system.unserialize(s); } -void Interface::cheatSet(const lstring& list) { +auto Interface::cheatSet(const lstring& list) -> void { cheat.reset(); //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); } -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); - } - } - } -} - } diff --git a/sfc/interface/interface.hpp b/sfc/interface/interface.hpp index b98d6092..e54c58bf 100644 --- a/sfc/interface/interface.hpp +++ b/sfc/interface/interface.hpp @@ -13,6 +13,7 @@ struct ID { SufamiTurboSlotB, //memory (files) + SystemManifest, IPLROM, Manifest, @@ -89,36 +90,36 @@ struct ID { }; 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(); + 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; }; diff --git a/sfc/memory/memory-inline.hpp b/sfc/memory/memory-inline.hpp index 067a0758..392904dd 100644 --- a/sfc/memory/memory-inline.hpp +++ b/sfc/memory/memory-inline.hpp @@ -1,23 +1,23 @@ //Memory -unsigned Memory::size() const { return 0; } +auto Memory::size() const -> unsigned { return 0; } //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() { 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 -void MappedRAM::reset() { +auto MappedRAM::reset() -> void { if(data_) { delete[] data_; data_ = nullptr; @@ -26,13 +26,13 @@ void MappedRAM::reset() { write_protect_ = false; } -void MappedRAM::map(uint8* source, unsigned length) { +auto MappedRAM::map(uint8* source, unsigned length) -> void { reset(); data_ = source; size_ = data_ ? length : 0; } -void MappedRAM::copy(const stream& memory) { +auto MappedRAM::copy(const stream& memory) -> void { if(data_) delete[] data_; //round size up to multiple of 256-bytes size_ = (memory.size() & ~255) + ((bool)(memory.size() & 255) << 8); @@ -40,22 +40,21 @@ void MappedRAM::copy(const stream& memory) { 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_)); } -void MappedRAM::write_protect(bool status) { write_protect_ = status; } -uint8* MappedRAM::data() { return data_; } -unsigned MappedRAM::size() const { return size_; } +auto MappedRAM::write_protect(bool status) -> void { write_protect_ = status; } +auto MappedRAM::data() -> uint8* { return data_; } +auto MappedRAM::size() const -> unsigned { return size_; } -uint8 MappedRAM::read(unsigned addr) { return data_[addr]; } -void MappedRAM::write(unsigned addr, uint8 n) { if(!write_protect_) data_[addr] = n; } -const uint8& MappedRAM::operator[](unsigned addr) const { return data_[addr]; } -MappedRAM::MappedRAM() : data_(nullptr), size_(0), write_protect_(false) {} +auto MappedRAM::read(unsigned addr) -> uint8 { return data_[addr]; } +auto MappedRAM::write(unsigned addr, uint8 n) -> void { if(!write_protect_) data_[addr] = n; } +auto MappedRAM::operator[](unsigned addr) const -> const uint8& { return data_[addr]; } //Bus -unsigned Bus::mirror(unsigned addr, unsigned size) { +auto Bus::mirror(unsigned addr, unsigned size) -> unsigned { if(size == 0) return 0; unsigned base = 0; unsigned mask = 1 << 23; @@ -71,7 +70,7 @@ unsigned Bus::mirror(unsigned addr, unsigned size) { return base + addr; } -unsigned Bus::reduce(unsigned addr, unsigned mask) { +auto Bus::reduce(unsigned addr, unsigned mask) -> unsigned { while(mask) { unsigned bits = (mask & -mask) - 1; addr = ((addr >> 1) & ~bits) | (addr & bits); @@ -80,7 +79,7 @@ unsigned Bus::reduce(unsigned addr, unsigned mask) { return addr; } -uint8 Bus::read(unsigned addr) { +auto Bus::read(unsigned addr) -> uint8 { uint8 data = reader[lookup[addr]](target[addr]); if(cheat.enable()) { @@ -90,6 +89,6 @@ uint8 Bus::read(unsigned addr) { return data; } -void Bus::write(unsigned addr, uint8 data) { +auto Bus::write(unsigned addr, uint8 data) -> void { return writer[lookup[addr]](target[addr], data); } diff --git a/sfc/memory/memory.cpp b/sfc/memory/memory.cpp index c59f786c..19692ca4 100644 --- a/sfc/memory/memory.cpp +++ b/sfc/memory/memory.cpp @@ -5,32 +5,17 @@ namespace SuperFamicom { Bus bus; -void Bus::map( - const function& reader, - const function& writer, - 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; - } - } +Bus::Bus() { + lookup = new uint8 [16 * 1024 * 1024]; + target = new uint32[16 * 1024 * 1024]; } -void Bus::map_reset() { +Bus::~Bus() { + delete[] lookup; + delete[] target; +} + +auto Bus::reset() -> void { function reader = [](unsigned) { return cpu.regs.mdr; }; function writer = [](unsigned, uint8) {}; @@ -38,7 +23,7 @@ void Bus::map_reset() { map(reader, writer, 0x00, 0xff, 0x0000, 0xffff); } -void Bus::map_xml() { +auto Bus::map() -> void { for(auto& m : cartridge.mapping) { lstring part = m.addr.split(":", 1L); lstring banks = part(0).split(","); @@ -57,14 +42,29 @@ void Bus::map_xml() { } } -Bus::Bus() { - lookup = new uint8 [16 * 1024 * 1024]; - target = new uint32[16 * 1024 * 1024]; -} +auto Bus::map( + const function& reader, + const function& 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() { - delete[] lookup; - delete[] target; + 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; + } + } } } diff --git a/sfc/memory/memory.hpp b/sfc/memory/memory.hpp index 245156e7..1016d7eb 100644 --- a/sfc/memory/memory.hpp +++ b/sfc/memory/memory.hpp @@ -1,74 +1,72 @@ struct Memory { - virtual inline unsigned size() const; - virtual uint8 read(unsigned addr) = 0; - virtual void write(unsigned addr, uint8 data) = 0; + virtual inline auto size() const -> unsigned; + virtual auto read(unsigned addr) -> uint8 = 0; + virtual auto write(unsigned addr, uint8 data) -> void = 0; }; 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(); + 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: - uint8* data_; - unsigned size_; + uint8* data_ = nullptr; + unsigned size_ = 0; }; struct MappedRAM : Memory { - inline void reset(); - inline void map(uint8*, unsigned); - inline void copy(const stream& memory); - inline void read(const stream& memory); + inline auto reset() -> void; + inline auto map(uint8*, unsigned) -> void; + inline auto copy(const stream& memory) -> void; + inline auto read(const stream& memory) -> void; - inline void write_protect(bool status); - inline uint8* data(); - inline unsigned size() const; + inline auto write_protect(bool status) -> void; + inline auto data() -> uint8*; + inline auto size() const -> unsigned; - inline uint8 read(unsigned addr); - inline void write(unsigned addr, uint8 n); - inline const uint8& operator[](unsigned addr) const; - inline MappedRAM(); + inline auto read(unsigned addr) -> uint8; + inline auto write(unsigned addr, uint8 n) -> void; + inline auto operator[](unsigned addr) const -> const uint8&; private: - uint8* data_; - unsigned size_; - bool write_protect_; + uint8* data_ = nullptr; + unsigned size_ = 0; + bool write_protect_ = false; }; struct Bus { - alwaysinline static unsigned mirror(unsigned addr, unsigned size); - alwaysinline static unsigned reduce(unsigned addr, unsigned mask); + alwaysinline static auto mirror(unsigned addr, unsigned size) -> unsigned; + alwaysinline static auto reduce(unsigned addr, unsigned mask) -> unsigned; - alwaysinline uint8 read(unsigned addr); - alwaysinline void write(unsigned addr, uint8 data); + Bus(); + ~Bus(); - uint8* lookup; - uint32* target; + alwaysinline auto read(unsigned addr) -> uint8; + alwaysinline auto write(unsigned addr, uint8 data) -> void; - unsigned idcount; - function reader[256]; - function writer[256]; - - void map( + auto reset() -> void; + auto map() -> void; + auto map( const function& reader, const function& writer, unsigned banklo, unsigned bankhi, unsigned addrlo, unsigned addrhi, unsigned size = 0, unsigned base = 0, unsigned mask = 0 - ); + ) -> void; - void map_reset(); - void map_xml(); + uint8* lookup = nullptr; + uint32* target = nullptr; - Bus(); - ~Bus(); + unsigned idcount = 0; + function reader[256]; + function writer[256]; }; extern Bus bus; diff --git a/sfc/ppu/background/background.cpp b/sfc/ppu/background/background.cpp index dc99bf46..6a706f1e 100644 --- a/sfc/ppu/background/background.cpp +++ b/sfc/ppu/background/background.cpp @@ -169,14 +169,13 @@ void PPU::Background::run(bool screen) { if(hires == false) return; } - if(regs.mode == Mode::Inactive) return; - if(regs.mode == Mode::Mode7) return run_mode7(); - if(tile_counter-- == 0) { tile_counter = 7; get_tile(); } + if(regs.mode == Mode::Mode7) return run_mode7(); + uint8 palette = get_tile_color(); if(x == 0) mosaic.hcounter = 1; if(x >= 0 && --mosaic.hcounter == 0) { diff --git a/sfc/scheduler/scheduler.cpp b/sfc/scheduler/scheduler.cpp index 7e259205..2b332d94 100644 --- a/sfc/scheduler/scheduler.cpp +++ b/sfc/scheduler/scheduler.cpp @@ -2,31 +2,25 @@ Scheduler scheduler; -void Scheduler::enter() { - 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() { +auto Scheduler::init() -> void { host_thread = co_active(); thread = cpu.thread; sync = SynchronizeMode::None; } -Scheduler::Scheduler() { - host_thread = nullptr; - thread = nullptr; - exit_reason = ExitReason::UnknownEvent; +auto Scheduler::enter() -> void { + host_thread = co_active(); + co_switch(thread); +} + +auto Scheduler::exit(ExitReason reason) -> void { + exit_reason = reason; + thread = co_active(); + co_switch(host_thread); +} + +auto Scheduler::debug() -> void { + exit(ExitReason::DebuggerEvent); } #endif diff --git a/sfc/scheduler/scheduler.hpp b/sfc/scheduler/scheduler.hpp index 764e179a..acd850d6 100644 --- a/sfc/scheduler/scheduler.hpp +++ b/sfc/scheduler/scheduler.hpp @@ -1,17 +1,15 @@ -struct Scheduler : property { +struct Scheduler { enum class SynchronizeMode : unsigned { None, CPU, All } sync; enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent, DebuggerEvent }; - readonly exit_reason; - cothread_t host_thread; //program thread (used to exit emulation) - cothread_t thread; //active emulation thread (used to enter emulation) + auto init() -> void; + auto enter() -> void; + auto exit(ExitReason) -> void; + auto debug() -> void; - void enter(); - void exit(ExitReason); - void debug(); - - void init(); - Scheduler(); + cothread_t host_thread = nullptr; //program thread (used to exit emulation) + cothread_t thread = nullptr; //active emulation thread (used to enter emulation) + ExitReason exit_reason = ExitReason::UnknownEvent; }; extern Scheduler scheduler; diff --git a/sfc/slot/satellaview/satellaview.cpp b/sfc/slot/satellaview/satellaview.cpp index 5d2a6136..f74d54b5 100644 --- a/sfc/slot/satellaview/satellaview.cpp +++ b/sfc/slot/satellaview/satellaview.cpp @@ -5,23 +5,23 @@ namespace SuperFamicom { SatellaviewCartridge satellaviewcartridge; -void SatellaviewCartridge::init() { +auto SatellaviewCartridge::init() -> void { } -void SatellaviewCartridge::load() { +auto SatellaviewCartridge::load() -> void { if(memory.size() == 0) { memory.map(allocate(1024 * 1024, 0xff), 1024 * 1024); } } -void SatellaviewCartridge::unload() { +auto SatellaviewCartridge::unload() -> void { memory.reset(); } -void SatellaviewCartridge::power() { +auto SatellaviewCartridge::power() -> void { } -void SatellaviewCartridge::reset() { +auto SatellaviewCartridge::reset() -> void { regs.command = 0; regs.write_old = 0x00; regs.write_new = 0x00; @@ -32,11 +32,11 @@ void SatellaviewCartridge::reset() { memory.write_protect(!regs.write_enable); } -unsigned SatellaviewCartridge::size() const { +auto SatellaviewCartridge::size() const -> unsigned { 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(addr == 0x0002) { @@ -65,7 +65,7 @@ uint8 SatellaviewCartridge::read(unsigned addr) { return memory.read(addr); } -void SatellaviewCartridge::write(unsigned addr, uint8 data) { +auto SatellaviewCartridge::write(unsigned addr, uint8 data) -> void { if(readonly) return; if((addr & 0xff0000) == 0) { diff --git a/sfc/slot/satellaview/satellaview.hpp b/sfc/slot/satellaview/satellaview.hpp index e1b8ffa0..3030ac10 100644 --- a/sfc/slot/satellaview/satellaview.hpp +++ b/sfc/slot/satellaview/satellaview.hpp @@ -1,17 +1,17 @@ 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; 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: struct { unsigned command; diff --git a/sfc/slot/sufamiturbo/serialization.cpp b/sfc/slot/sufamiturbo/serialization.cpp index 3586c918..3eb08baf 100644 --- a/sfc/slot/sufamiturbo/serialization.cpp +++ b/sfc/slot/sufamiturbo/serialization.cpp @@ -1,6 +1,6 @@ #ifdef SUFAMITURBO_CPP -void SufamiTurboCartridge::serialize(serializer& s) { +auto SufamiTurboCartridge::serialize(serializer& s) -> void { s.array(ram.data(), ram.size()); } diff --git a/sfc/slot/sufamiturbo/sufamiturbo.cpp b/sfc/slot/sufamiturbo/sufamiturbo.cpp index cfee0940..491769a5 100644 --- a/sfc/slot/sufamiturbo/sufamiturbo.cpp +++ b/sfc/slot/sufamiturbo/sufamiturbo.cpp @@ -7,10 +7,10 @@ namespace SuperFamicom { SufamiTurboCartridge sufamiturboA; SufamiTurboCartridge sufamiturboB; -void SufamiTurboCartridge::load() { +auto SufamiTurboCartridge::load() -> void { } -void SufamiTurboCartridge::unload() { +auto SufamiTurboCartridge::unload() -> void { rom.reset(); ram.reset(); } diff --git a/sfc/slot/sufamiturbo/sufamiturbo.hpp b/sfc/slot/sufamiturbo/sufamiturbo.hpp index 061dbe2f..cb9f4450 100644 --- a/sfc/slot/sufamiturbo/sufamiturbo.hpp +++ b/sfc/slot/sufamiturbo/sufamiturbo.hpp @@ -1,10 +1,10 @@ struct SufamiTurboCartridge { + auto load() -> void; + auto unload() -> void; + auto serialize(serializer&) -> void; + MappedRAM rom; MappedRAM ram; - - void load(); - void unload(); - void serialize(serializer&); }; extern SufamiTurboCartridge sufamiturboA; diff --git a/sfc/system/system.cpp b/sfc/system/system.cpp index 3502e656..9bff60a8 100644 --- a/sfc/system/system.cpp +++ b/sfc/system/system.cpp @@ -18,7 +18,7 @@ void System::run() { scheduler.sync = Scheduler::SynchronizeMode::None; scheduler.enter(); - if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) { + if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) { video.update(); } } @@ -54,8 +54,8 @@ void System::runtosave() { void System::runthreadtosave() { while(true) { scheduler.enter(); - if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break; - if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) { + if(scheduler.exit_reason == Scheduler::ExitReason::SynchronizeEvent) break; + if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) { video.update(); } } @@ -93,13 +93,12 @@ void System::term() { } void System::load() { - string manifest = string::read({interface->path(ID::System), "manifest.bml"}); - auto document = BML::unserialize(manifest); +//string manifest = string::read({interface->path(ID::System), "manifest.bml"}); + interface->loadRequest(ID::SystemManifest, "manifest.bml", true); + auto document = BML::unserialize(information.manifest); - auto iplrom = document["system/smp/rom/name"].text(); - interface->loadRequest(ID::IPLROM, iplrom); - if(!file::exists({interface->path(ID::System), iplrom})) { - interface->notify("Error: required Super Famicom firmware ipl.rom not found.\n"); + if(auto iplrom = document["system/smp/rom/name"].text()) { + interface->loadRequest(ID::IPLROM, iplrom, true); } region = configuration.region; @@ -113,8 +112,8 @@ void System::load() { audio.coprocessor_enable(false); - bus.map_reset(); - bus.map_xml(); + bus.reset(); + bus.map(); cpu.enable(); ppu.enable(); diff --git a/sfc/system/system.hpp b/sfc/system/system.hpp index ae2238b7..d492a73e 100644 --- a/sfc/system/system.hpp +++ b/sfc/system/system.hpp @@ -29,6 +29,10 @@ struct System : property { System(); + struct Information { + string manifest; + } information; + private: void runthreadtosave(); diff --git a/target-tomoko/program/interface.cpp b/target-tomoko/program/interface.cpp index e151a947..590268fe 100644 --- a/target-tomoko/program/interface.cpp +++ b/target-tomoko/program/interface.cpp @@ -1,5 +1,5 @@ //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() .setTitle({"Load ", 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 -auto Program::loadRequest(unsigned id, string path) -> void { - string location = {mediaPaths(emulator->group(id)), path}; - if(!file::exists(location)) return; - mmapstream stream{location}; - return emulator->load(id, stream); +auto Program::loadRequest(unsigned id, string filename, bool required) -> void { + string pathname = mediaPaths(emulator->group(id)); + string location = {pathname, filename}; + if(file::exists(location)) { + 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 -auto Program::saveRequest(unsigned id, string path) -> void { - string location = {mediaPaths(emulator->group(id)), path}; +auto Program::saveRequest(unsigned id, string filename) -> void { + string pathname = mediaPaths(emulator->group(id)); + string location = {pathname, filename}; filestream stream{location, file::mode::write}; return emulator->save(id, stream); } diff --git a/target-tomoko/program/program.hpp b/target-tomoko/program/program.hpp index 65091dd0..d9c4bec7 100644 --- a/target-tomoko/program/program.hpp +++ b/target-tomoko/program/program.hpp @@ -5,8 +5,8 @@ struct Program : Emulator::Interface::Bind { auto quit() -> void; //interface.cpp - auto loadRequest(unsigned id, string name, string type) -> void override; - auto loadRequest(unsigned id, string path) -> void override; + auto loadRequest(unsigned id, string name, string type, bool required) -> void override; + auto loadRequest(unsigned id, string path, bool required) -> 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 videoRefresh(const uint32* palette, const uint32* data, unsigned pitch, unsigned width, unsigned height) -> void override;