From 1c0d8af2d02175c5f27970f1c31726bd50291b6a Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Fri, 20 Sep 2024 08:02:01 +0200 Subject: [PATCH] wip of property rework --- src/cadmium.cpp | 109 ++++++++--------- src/configuration.cpp | 4 +- src/configuration.hpp | 2 +- src/emuhostex.cpp | 2 +- src/emulation/chip8generic.cpp | 166 ++++++++++++-------------- src/emulation/chip8generic.hpp | 21 +++- src/emulation/chip8opcodedisass.hpp | 2 +- src/emulation/chip8strict.cpp | 8 +- src/emulation/coreregistry.hpp | 23 ++-- src/emulation/cosmacvip.cpp | 8 +- src/emulation/hardware/genericcpu.hpp | 2 +- src/emulation/properties.cpp | 16 +-- src/emulation/properties.hpp | 45 +++++-- src/librarian.hpp | 2 +- test/chip8adapter.cpp | 1 - test/chip8adapter.hpp | 2 +- 16 files changed, 221 insertions(+), 192 deletions(-) diff --git a/src/cadmium.cpp b/src/cadmium.cpp index ef503db..af81118 100644 --- a/src/cadmium.cpp +++ b/src/cadmium.cpp @@ -637,6 +637,7 @@ class Cadmium : public emu::EmuHostEx #ifdef PLATFORM_WEB JsClipboard_AddJsHook(); #else + _cfgPath = (fs::path(dataPath())/"config.json").string(); _volume = _volumeSlider = _cfg.volume; _styleManager.updateStyle(_cfg.guiHue, _cfg.guiSat, false); #endif @@ -884,7 +885,7 @@ class Cadmium : public emu::EmuHostEx return Vector3Distance(labC1, labC2);; } - static inline uint32_t rgb332To888(uint8_t c) + static uint32_t rgb332To888(uint8_t c) { static uint8_t b3[] = {0, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xff}; static uint8_t b2[] = {0, 0x60, 0xA0, 0xff}; @@ -975,9 +976,11 @@ class Cadmium : public emu::EmuHostEx } else { // TraceLog(LOG_INFO, "Updating MC8 screen!"); - const auto* screen = _chipEmu->getScreenRGBA(); - screen->convert(pixel, _screen.width, _chipEmu->getScreenAlpha(), _chipEmu->getWorkRGBA()); - UpdateTexture(_screenTexture, _screen.data); + const auto* screenRgb = _chipEmu->getScreenRGBA(); + if(screenRgb) { + screenRgb->convert(pixel, _screen.width, _chipEmu->getScreenAlpha(), _chipEmu->getWorkRGBA()); + UpdateTexture(_screenTexture, _screen.data); + } } } } @@ -1667,22 +1670,22 @@ class Cadmium : public emu::EmuHostEx static int editProperty(emu::Property& prop, bool forceUpdate, PropertyAlign pa = PA_RIGHT) { - gui::BeginColumns(); - gui::SetSpacing(4); - gui::SetNextWidth(90); auto prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); if(pa == PA_RIGHT) { + gui::BeginColumns(); + gui::SetSpacing(4); + gui::SetNextWidth(90); gui::SetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT); gui::Label(fmt::format("{}", prop.getName()).c_str()); gui::SetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment); } //gui::SetNextWidth(150); - if (prop.isReadonly()) + if (prop.access() != emu::eWritable) GuiDisable(); const auto rc = std::visit(emu::visitor{ [](std::nullptr_t) -> int { gui::Label(""); return 0; }, [pa, &prop](bool& val) -> int { val = gui::CheckBox(pa == PA_RIGHT ? "" : prop.getName().c_str(), val); return val ? 1 : 0; }, - [](emu::Property::Integer& val) -> int { gui::Spinner("", &val.intValue, val.minValue, val.maxValue); return val.intValue; }, + [pa, &prop](emu::Property::Integer& val) -> int { gui::Spinner(pa == PA_RIGHT ? "" : prop.getName().c_str(), &val.intValue, val.minValue, val.maxValue); return val.intValue; }, [](std::string& val) -> int { auto prevTextAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT); gui::SetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); @@ -1696,9 +1699,11 @@ class Cadmium : public emu::EmuHostEx return val.index; }}, prop.getValue()); - if (prop.isReadonly()) + if (prop.access() != emu::eWritable) GuiEnable(); - gui::EndColumns(); + if(pa == PA_RIGHT) { + gui::EndColumns(); + } return rc; } @@ -1718,7 +1723,7 @@ class Cadmium : public emu::EmuHostEx int editPropertySpinner(std::string_view key, bool forceUpdate, int defaultValue = 0) { if(_properties.containsFuzzy(key)) { - return editProperty(_properties.at(key), forceUpdate); + return editProperty(_properties.at(key), forceUpdate, PA_LEFT); } static int dummyInt = defaultValue; GuiDisable(); @@ -1863,32 +1868,34 @@ class Cadmium : public emu::EmuHostEx SetNextWidth(colWidth1); Begin(); SetSpacing(2); - auto* rcb = dynamic_cast(_chipEmu.get()); - if(rcb) { - static emu::Properties propsMemento; - auto& props = _properties; - if(props != propsMemento) - propsMemento = props; - for(size_t i = 0; i < props.numProperties(); ++i) { - auto& prop = props[i]; - if(prop.getName().empty()) { - auto used = GetCurrentPos().y - startY; - Space(quirksHeight - used - 4); - End(); - Begin(); - SetSpacing(2); + static emu::Properties propsMemento; + auto& props = _properties; + if(props != propsMemento) + propsMemento = props; + for(size_t i = 0; i < props.numProperties(); ++i) { + auto& prop = props[i]; + if(prop.getName().empty()) { + auto used = GetCurrentPos().y - startY; + Space(quirksHeight - used - 4); + End(); + Begin(); + SetSpacing(2); + } + else if(prop.access() != emu::eInvisible && !fuzzyAnyOf(prop.getName(), {"TraceLog", "InstructionsPerFrame", "FrameRate"})) { + if(props.numProperties() > 20 && std::holds_alternative(prop.getValue())) { + editProperty(prop, forceUpdate, PA_LEFT); } - else if(!fuzzyAnyOf(prop.getName(), {"TraceLog"})) { + else { editProperty(prop, forceUpdate); - ++rowCount; } + ++rowCount; } - /*auto* changedProp = props.changedProperty(propsMemento); - if(changedProp) { - // on change... - updateEmulatorOptions(_options); - //rcb->updateProperties(*changedProp); - }*/ + } + auto* changedProp = props.changedProperty(propsMemento); + if(changedProp) { + // on change... + updateEmulatorOptions(props); + //rcb->updateProperties(*changedProp); } auto used = GetCurrentPos().y - startY; Space(quirksHeight - used - 4); @@ -1901,18 +1908,21 @@ class Cadmium : public emu::EmuHostEx StyleManager::Scope guard; BeginColumns(); auto pos = GetCurrentPos(); + pos.x = std::ceil(pos.x); + pos.y = std::ceil(pos.y); SetNextWidth(52.0f + 16*18); Label("Colors:"); for (int i = 0; i < 16; ++i) { - DrawRectangle(pos.x + 52 + i * 18 + 2, pos.y + 2 , 12, 12, GetColor(_colorPalette[i])); bool hover = CheckCollisionPointRec(GetMousePosition(), {pos.x + 52 + i * 18, pos.y, 16, 16}); + DrawRectangle(pos.x + 52 + i * 18, pos.y, 16, 16, GetColor(guard.getStyle(hover ? Style::BORDER_COLOR_FOCUSED : Style::BORDER_COLOR_NORMAL))); + DrawRectangle(pos.x + 52 + i * 18 + 1, pos.y + 1 , 14, 14, GetColor(guard.getStyle(Style::BACKGROUND_COLOR))); + DrawRectangle(pos.x + 52 + i * 18 + 2, pos.y + 2 , 12, 12, GetColor(_colorPalette[i])); if(!GuiIsLocked() && IsMouseButtonReleased(0) && hover) { _selectedColor = &_colorPalette[i]; _previousColor = _colorPalette[i]; _colorText = fmt::format("{:06x}", _colorPalette[i]>>8); _colorSelectOpen = true; } - DrawRectangleLines(pos.x + 52 + i * 18, pos.y, 16, 16, GetColor(guard.getStyle(hover ? Style::BORDER_COLOR_FOCUSED : Style::BORDER_COLOR_NORMAL))); } static std::vector prevPalette(_colorPalette.begin(), _colorPalette.end()); if(std::memcmp(prevPalette.data(), _colorPalette.data(), 16*sizeof(uint32_t)) != 0) { @@ -2175,7 +2185,7 @@ class Cadmium : public emu::EmuHostEx //} // TODO: Fix this // auto options = _options; - loadRom(_librarian.fullPath(selectedInfo.filePath).c_str(), LoadOptions::None); + loadRom(_librarian.fullPath(selectedInfo.filePath).c_str(), LoadOptions::DontChangeOptions); // TODO: Fix this // updateEmulatorOptions(options); _mainView = _lastView; @@ -2321,7 +2331,6 @@ class Cadmium : public emu::EmuHostEx void saveConfig() { - return; // TODO: Reactivate config saving #ifndef PLATFORM_WEB if(!_cfgPath.empty()) { auto opt = _properties; @@ -2330,7 +2339,7 @@ class Cadmium : public emu::EmuHostEx pal[i] = fmt::format("#{:06x}", _defaultPalette[i] >> 8); } // opt.advanced["palette"] = pal; - _cfg.emuOptions = _properties; + _cfg.emuProperties = _properties; _cfg.workingDirectory = _currentDirectory; _cfg.databaseDirectory = _databaseDirectory; if(!_cfg.save(_cfgPath)) { @@ -2342,22 +2351,10 @@ class Cadmium : public emu::EmuHostEx void updateBehaviorSelects() { - _subBehaviorSel = static_cast(emu::CoreRegistry::variantIndex(_properties).index); - // TODO: Fix this - /* - auto result = std::find_if(_presetMapping.begin(), _presetMapping.end(), [this](const auto& pair) {return pair.second == _options.behaviorBase; }); - if(result != _presetMapping.end()) { - _behaviorSel = result->first.first; - if(_behaviorSel == 12) - _subBehaviorSel = result->first.second, _subBehaviorSel2 = 0; - else if(_behaviorSel == 13) - _subBehaviorSel2 = result->first.second, _subBehaviorSel = 0; - else - _subBehaviorSel = _subBehaviorSel2 = 0; + if (auto idx = _cores.classIndex(_properties); idx >= 0) { + _behaviorSel = idx; + _subBehaviorSel = static_cast(emu::CoreRegistry::variantIndex(_properties).index); } - else - _behaviorSel = emu::Chip8EmulatorOptions::eXOCHIP; - */ } void whenEmuChanged(emu::IEmulationCore& emu) override @@ -2842,7 +2839,7 @@ int main(int argc, char* argv[]) auto oldCat = cli.category(fmt::format("{} Options (only available if preset uses {} core)", name, info->prefix().empty() ? "default" : toOptionName(info->prefix()))); for(size_t i = 0; i < proto.numProperties(); ++i) { auto& prop = proto[i]; - if(!prop.isReadonly()) { + if(prop.access() == emu::eWritable) { auto dependencyCheck = [&presetName, info](){ return info->hasVariant(presetName); }; std::visit(emu::visitor{ [](std::nullptr_t) -> void { }, @@ -2913,7 +2910,7 @@ int main(int argc, char* argv[]) CadmiumConfiguration config; auto cfgPath = (fs::path(dataPath())/"config.json").string(); if(config.load(cfgPath)) { - coreProperties = config.emuOptions; + coreProperties = config.emuProperties; } try { diff --git a/src/configuration.cpp b/src/configuration.cpp index 0200600..7be8a65 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -35,7 +35,7 @@ void to_json(nlohmann::json& j, const CadmiumConfiguration& cc) { {"guiSaturation", cc.guiSat}, {"workingDirectory", cc.workingDirectory}, {"databaseDirectory", cc.databaseDirectory}, - {"emuOptions", cc.emuOptions}, + {"emuProperties", cc.emuProperties}, {"romConfigs", cc.romConfigs} }; } @@ -47,7 +47,7 @@ void from_json(const nlohmann::json& j, CadmiumConfiguration& cc) { cc.guiHue = j.value("guiHue", 192); cc.guiSat = j.value("guiSaturation", 90); try { - j.at("emuOptions").get_to(cc.emuOptions); + j.at("emuProperties").get_to(cc.emuProperties); } catch(...) {} try { diff --git a/src/configuration.hpp b/src/configuration.hpp index 33dbaa2..c7b63fc 100644 --- a/src/configuration.hpp +++ b/src/configuration.hpp @@ -38,7 +38,7 @@ struct CadmiumConfiguration uint8_t guiSat{80}; std::string workingDirectory; std::string databaseDirectory; - emu::Properties emuOptions; + emu::Properties emuProperties; std::map romConfigs; bool load(const std::string& filepath); bool save(const std::string& filepath); diff --git a/src/emuhostex.cpp b/src/emuhostex.cpp index 5472dea..f283fe6 100644 --- a/src/emuhostex.cpp +++ b/src/emuhostex.cpp @@ -49,7 +49,7 @@ EmuHostEx::EmuHostEx(CadmiumConfiguration& cfg) , _librarian(_cfg) { #ifndef PLATFORM_WEB - _currentDirectory = _cfg.workingDirectory; + _currentDirectory = _cfg.workingDirectory.empty() ? fs::current_path().string() : _cfg.workingDirectory; _databaseDirectory = _cfg.databaseDirectory; _librarian.fetchDir(_currentDirectory); #endif diff --git a/src/emulation/chip8generic.cpp b/src/emulation/chip8generic.cpp index cbf47b7..58e7cac 100644 --- a/src/emulation/chip8generic.cpp +++ b/src/emulation/chip8generic.cpp @@ -71,6 +71,7 @@ static const std::string PROP_Q_PAL_VIDEO = "PAL video format"; Properties Chip8GenericOptions::asProperties() const { auto result = registeredPrototype(); + result[PROP_BEHAVIOR_BASE].setSelectedIndex(behaviorBase); result[PROP_TRACE_LOG].setBool(traceLog); result[PROP_INSTRUCTIONS_PER_FRAME].setInt(instructionsPerFrame); result[PROP_FRAME_RATE].setInt(frameRate); @@ -104,6 +105,7 @@ Properties Chip8GenericOptions::asProperties() const Chip8GenericOptions Chip8GenericOptions::fromProperties(const Properties& props) { Chip8GenericOptions opts{}; + opts.behaviorBase = static_cast(props[PROP_BEHAVIOR_BASE].getSelectedIndex()); opts.traceLog = props[PROP_TRACE_LOG].getBool(); opts.instructionsPerFrame = props[PROP_INSTRUCTIONS_PER_FRAME].getInt(); opts.frameRate = props[PROP_FRAME_RATE].getInt(); @@ -139,35 +141,36 @@ Properties& Chip8GenericOptions::registeredPrototype() using namespace std::string_literals; auto& prototype = Properties::getProperties(PROP_CLASS); if(!prototype) { - prototype.registerProperty({PROP_TRACE_LOG, false, "Enable trace log", false}); - - prototype.registerProperty({PROP_INSTRUCTIONS_PER_FRAME, Property::Integer{11, 0, 1'000'000}, "Number of instructions per frame, default depends on variant", false}); - prototype.registerProperty({PROP_FRAME_RATE, Property::Integer{60, 50, 100}, "Number of frames per second, default 60", false}); - prototype.registerProperty({PROP_RAM, Property::Combo{"2048"s, "4096"s, "8192"s, "12288"s, "16384"s, "32768"s}, "Size of ram in bytes", false}); - prototype.registerProperty({PROP_CLEAN_RAM, false, "Delete ram on startup", false}); - prototype.registerProperty({PROP_START_ADDRESS, Property::Integer{0x200, 0, 0x7f0}, "Number of instructions per frame, default depends on variant", false}); - prototype.registerProperty({{PROP_Q_JUST_SHIFT_VX, "just-Shift-Vx"}, false, false}); - prototype.registerProperty({{PROP_Q_DONT_RESET_VF, "dont-Reset-Vf"}, false, false}); - prototype.registerProperty({{PROP_Q_LOAD_STORE_INC_I_BY_X_PLUS_ONE, "load-Store-Inc-I-By-X-Plus-1"}, true, false}); - prototype.registerProperty({{PROP_Q_LOAD_STORE_INC_I_BY_X, "load-Store-Inc-I-ByX"}, false, false}); - prototype.registerProperty({{PROP_Q_WRAP_SPRITES, "wrap-sprites"}, false, false}); - prototype.registerProperty({{PROP_Q_INSTANT_DXYN, "instand-dxyn"}, false, false}); - prototype.registerProperty({{PROP_Q_LORES_DXY0_IS_8X16, "lores-Dxy0-Is-8x16"}, false, false}); - prototype.registerProperty({{PROP_Q_LORES_DXY0_IS_16X16, "lores-Dxy0-Is-16x16"}, false, false}); - prototype.registerProperty({{PROP_Q_SC11_COLLISION, "schip-11-Collision"}, false, false}); - prototype.registerProperty({{PROP_Q_SC_LORES_DRAWING, "schip-Lores-Drawing"}, false, false}); - prototype.registerProperty({{PROP_Q_HALF_PIXEL_SCROLL, "half-Pixel-Scroll"}, false, false}); - prototype.registerProperty({{PROP_Q_MODE_CHANGE_CLEAR, "mode-Change-Clear"}, false, false}); - prototype.registerProperty({{PROP_Q_JUMP0_BXNN, "jump0-Bxnn"}, false, false}); - prototype.registerProperty({{PROP_Q_ALLOW_HIRES, "allow-Hires"}, false, false}); - prototype.registerProperty({{PROP_Q_ONLY_HIRES, "only-Hires"}, false, false}); - prototype.registerProperty({{PROP_Q_ALLOW_COLORS, "allow-Colors"}, false, false}); - prototype.registerProperty({{PROP_Q_CYCLIC_STACK, "cyclic-Stack"}, false, false}); - prototype.registerProperty({{PROP_Q_HAS_16BIT_ADDR, "has-16Bit-Addr"}, false, false}); - prototype.registerProperty({{PROP_Q_XO_CHIP_SOUND, "xo-Chip-Sound"}, false, false}); - prototype.registerProperty({{PROP_Q_EXTENDED_VBLANK, "extended-Vblank"}, false, false}); - prototype.registerProperty({{PROP_Q_PAL_VIDEO, "pal-Video"}, false, false}); -} + prototype.registerProperty({PROP_BEHAVIOR_BASE, Property::Combo{"CHIP-8"s, "CHIP-10"s, "CHIP-8E"s, "CHIP-8X"s, "CHIP-48"s, "SCHIP-1.0"s, "SCHIP-1.1"s, "SCHIPC"s, "SCHIP-MODERN"s, "MEGACHIP"s, "XO-CHIP"s}, "CHIP-8 variant", eInvisible}); + prototype.registerProperty({PROP_TRACE_LOG, false, "Enable trace log", eWritable}); + prototype.registerProperty({PROP_INSTRUCTIONS_PER_FRAME, Property::Integer{11, 0, 1'000'000}, "Number of instructions per frame, default depends on variant", eWritable}); + prototype.registerProperty({PROP_FRAME_RATE, Property::Integer{60, 50, 100}, "Number of frames per second, default 60", eWritable}); + prototype.registerProperty({PROP_RAM, Property::Combo{"2048"s, "4096"s, "8192"s, "12288"s, "16384"s, "32768"s}, "Size of ram in bytes", eWritable}); + prototype.registerProperty({PROP_START_ADDRESS, Property::Integer{0x200, 0, 0x7f0}, "Number of instructions per frame, default depends on variant", eReadOnly}); + prototype.registerProperty({PROP_CLEAN_RAM, false, "Delete ram on startup", eWritable}); + prototype.registerProperty({{PROP_Q_JUST_SHIFT_VX, "just-Shift-Vx"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_DONT_RESET_VF, "dont-Reset-Vf"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_LOAD_STORE_INC_I_BY_X_PLUS_ONE, "load-Store-Inc-I-By-X-Plus-1"}, true, eWritable}); + prototype.registerProperty({{PROP_Q_LOAD_STORE_INC_I_BY_X, "load-Store-Inc-I-ByX"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_WRAP_SPRITES, "wrap-sprites"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_INSTANT_DXYN, "instant-dxyn"}, false, eWritable}); + prototype.registerProperty({"", nullptr, ""}); + prototype.registerProperty({{PROP_Q_LORES_DXY0_IS_8X16, "lores-Dxy0-Is-8x16"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_LORES_DXY0_IS_16X16, "lores-Dxy0-Is-16x16"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_SC11_COLLISION, "schip-11-Collision"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_SC_LORES_DRAWING, "schip-Lores-Drawing"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_HALF_PIXEL_SCROLL, "half-Pixel-Scroll"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_MODE_CHANGE_CLEAR, "mode-Change-Clear"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_JUMP0_BXNN, "jump0-Bxnn"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_ALLOW_HIRES, "allow-Hires"}, false, eInvisible}); + prototype.registerProperty({{PROP_Q_ONLY_HIRES, "only-Hires"}, false, eInvisible}); + prototype.registerProperty({{PROP_Q_ALLOW_COLORS, "allow-Colors"}, false, eInvisible}); + prototype.registerProperty({{PROP_Q_CYCLIC_STACK, "cyclic-Stack"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_HAS_16BIT_ADDR, "has-16Bit-Addr"}, false, eInvisible}); + prototype.registerProperty({{PROP_Q_XO_CHIP_SOUND, "xo-Chip-Sound"}, false, eInvisible}); + prototype.registerProperty({{PROP_Q_EXTENDED_VBLANK, "extended-Vblank"}, false, eWritable}); + prototype.registerProperty({{PROP_Q_PAL_VIDEO, "pal-Video"}, false, eInvisible}); + } return prototype; } @@ -175,7 +178,6 @@ Chip8Variant Chip8GenericOptions::variant() const { switch(behaviorBase) { case eCHIP8: return Chip8Variant::CHIP_8; - case eCHIP8TE: return Chip8Variant::CHIP_8; case eCHIP10: return Chip8Variant::CHIP_10; case eCHIP8E: return Chip8Variant::CHIP_8E; case eCHIP8X: return Chip8Variant::CHIP_8X; @@ -186,16 +188,6 @@ Chip8Variant Chip8GenericOptions::variant() const case eSCHIP_MODERN: return Chip8Variant::SCHIP_MODERN; case eMEGACHIP: return Chip8Variant::MEGA_CHIP; case eXOCHIP: return Chip8Variant::XO_CHIP; - case eCHIP8VIP: return Chip8Variant::CHIP_8; - case eCHIP8VIP_TPD: return Chip8Variant::CHIP_8_TPD; - case eCHIP8VIP_FPD: return Chip8Variant::HI_RES_CHIP_8; - case eCHIP8EVIP: return Chip8Variant::CHIP_8E; - case eCHIP8XVIP: return Chip8Variant::CHIP_8X; - case eCHIP8XVIP_TPD: return Chip8Variant::CHIP_8X_TPD; - case eCHIP8XVIP_FPD: return Chip8Variant::HI_RES_CHIP_8X; - case eCHIP8DREAM: return Chip8Variant::CHIP_8_D6800; - case eC8D68CHIPOSLO: return Chip8Variant::CHIP_8_D6800; - case eCHICUEYI: return Chip8Variant::XO_CHIP; default: return Chip8Variant::CHIP_8; } } @@ -212,67 +204,67 @@ struct Chip8GenericSetupInfo Chip8GenericSetupInfo genericPresets[] = { { "CHIP-8", - "The classic CHIP-8 that came from Joseph Weisbecker, 1977", + "The classic CHIP-8 for the COSMAC VIP by Joseph Weisbecker, 1977", ".ch8", - {Chip8GenericOptions::eCHIP8} + {.behaviorBase = Chip8GenericOptions::eCHIP8, .optExtendedVBlank = true} }, { "CHIP-10", "128x64 CHIP-8 from #VIPER-V1-I7 and #IpsoFacto-I10, by Ben H. Hutchinson, Jr., 1979", ".ch10", - {.behaviorBase = Chip8GenericOptions::eCHIP10, .optAllowHires = true, .optOnlyHires = true} + {.behaviorBase = Chip8GenericOptions::eCHIP10, .optAllowHires = true, .optOnlyHires = true, .optExtendedVBlank = true} }, { "CHIP-8E", "CHIP-8 rewritten and extended by Gilles Detillieux, from #VIPER-V2-8+9", ".c8e", - {.behaviorBase = Chip8GenericOptions::eCHIP8E} + {.behaviorBase = Chip8GenericOptions::eCHIP8E, .optExtendedVBlank = true} }, { "CHIP-8X", "An official update to CHIP-8 by RCA, requiring the color extension VP-590 and the simple sound board VP-595, 1980", ".c8x", - {.behaviorBase = Chip8GenericOptions::eCHIP8X, .startAddress = 768, .instructionsPerFrame = 18} + {.behaviorBase = Chip8GenericOptions::eCHIP8X, .startAddress = 768, .optExtendedVBlank = true, .instructionsPerFrame = 18} }, { "CHIP-48", - "The initial CHIP-8 port to the HP-48SX", + "The initial CHIP-8 port to the HP-48SX by Andreas Gustafsson, 1990", ".ch48;.c48", {.behaviorBase = Chip8GenericOptions::eCHIP48, .optJustShiftVx = true, .optDontResetVf = true, .optLoadStoreIncIByX = true, .optJump0Bxnn = true, .instructionsPerFrame = 15, .frameRate = 64} }, { "SCHIP-1.0", - "SUPER-CHIP v1.0 expansion of CHIP-48 for the HP-48SX with 128x64 hires mode", + "SUPER-CHIP v1.0 expansion of CHIP-48 for the HP-48SX with 128x64 hires mode by Erik Bryntse, 1991", ".sc10", {.behaviorBase = Chip8GenericOptions::eSCHIP10, .optJustShiftVx = true, .optDontResetVf = true, .optLoadStoreIncIByX = true, .optLoresDxy0Is8x16 = true, .optSCLoresDrawing = true, .optJump0Bxnn = true, .optAllowHires = true, .instructionsPerFrame = 30, .frameRate = 64} }, { "SCHIP-1.1", - "SUPER-CHIP v1.1 expansion of CHIP-48 for the HP-48SX with 128x64 hires mode", - ".sc10", + "SUPER-CHIP v1.1 expansion of CHIP-48 for the HP-48SX with 128x64 hires mode by Erik Bryntse, 1991", + ".sc8;.sc11", {.behaviorBase = Chip8GenericOptions::eSCHIP11, .optJustShiftVx = true, .optDontResetVf = true, .optLoadStoreDontIncI = true, .optLoresDxy0Is8x16 = true, .optSC11Collision = true, .optSCLoresDrawing = true, .optHalfPixelScroll = true, .optJump0Bxnn = true, .optAllowHires = true, .instructionsPerFrame = 30, .frameRate = 64} }, { "SCHIPC", - "SUPER-CHIP compatibility fix for the HP-48SX by Chromatophore", + "SUPER-CHIP compatibility fix for the HP-48SX by Chromatophore, 2017", ".scc", {.behaviorBase = Chip8GenericOptions::eSCHPC, .optDontResetVf = true, .optLoresDxy0Is16x16 = true, .optModeChangeClear = true, .optAllowHires = true, .instructionsPerFrame = 30, .frameRate = 64} }, { "SCHIP-MODERN", - "Modern SUPER-CHIP interpretation as done in Octo by John Earnest", + "Modern SUPER-CHIP interpretation as done in Octo by John Earnest, 2014", ".scm", - {.behaviorBase = Chip8GenericOptions::eSCHIP_MODERN, .optJustShiftVx = true, .optDontResetVf = true, .optLoadStoreDontIncI = true, .optInstantDxyn = true, .optLoresDxy0Is16x16 = true, .optModeChangeClear = true, .optAllowHires = true, .instructionsPerFrame = 30, .frameRate = 64} + {.behaviorBase = Chip8GenericOptions::eSCHIP_MODERN, .optJustShiftVx = true, .optDontResetVf = true, .optLoadStoreDontIncI = true, .optInstantDxyn = true, .optLoresDxy0Is16x16 = true, .optModeChangeClear = true, .optJump0Bxnn = true, .optAllowHires = true, .instructionsPerFrame = 30, .frameRate = 64} }, { "MEGACHIP", - "MegaChip as specified by Martijn Wanting/Revival-Studios", + "MegaChip as specified by Martijn Wanting, Revival-Studios, 2007", ".mc8", {.behaviorBase = Chip8GenericOptions::eMEGACHIP, .optJustShiftVx = true, .optDontResetVf = true, .optLoadStoreDontIncI = true, .optLoresDxy0Is8x16 = true, .optSC11Collision = true, .optModeChangeClear = true, .optJump0Bxnn = true, .optAllowHires = true, .instructionsPerFrame = 3000, .frameRate = 50} }, { "XO-CHIP", - "A modern extension to SUPER-CHIP supporting colors and actual sound first implemented in Octo, by John Earnest", + "A modern extension to SUPER-CHIP supporting colors and actual sound first implemented in Octo by John Earnest, 2014", "xo8", {.behaviorBase = Chip8GenericOptions::eXOCHIP, .optDontResetVf = true, .optWrapSprites = true, .optInstantDxyn = true, .optLoresDxy0Is16x16 = true, .optModeChangeClear = true, .optAllowHires = true, .optAllowColors = true, .optHas16BitAddr = true, .optXOChipSound = true, .instructionsPerFrame = 1000} } @@ -301,7 +293,8 @@ struct C8GenericFactoryInfo final : public CoreRegistry::FactoryInfo4096 ? 0xFFFF : 0xFFF; + SCREEN_WIDTH = _options.behaviorBase == Chip8GenericOptions::eMEGACHIP ? 256 : (_options.optAllowHires ? 128 : 64); + SCREEN_HEIGHT = _options.behaviorBase == Chip8GenericOptions::eMEGACHIP ? 192 : (_options.optAllowHires ? 64 : (_options.optPalVideo ? 48 : 32)); + _memory.resize(_options.ramSize, 0); _rV = _rVData.data(); _screen.setMode(SCREEN_WIDTH, SCREEN_HEIGHT); _screenRGBA1.setMode(SCREEN_WIDTH, SCREEN_HEIGHT); _screenRGBA2.setMode(SCREEN_WIDTH, SCREEN_HEIGHT); setHandler(); - if(!other) { - reset(); - } } @@ -338,11 +329,9 @@ static Chip8GenericBase::Chip8Font getSmallFontId(Chip8GenericOptions::Supported case Chip8GenericOptions::eSCHIP_MODERN: case Chip8GenericOptions::eMEGACHIP: case Chip8GenericOptions::eXOCHIP: - case Chip8GenericOptions::eCHICUEYI: return Chip8GenericBase::C8F5_CHIP48; case Chip8GenericOptions::eCHIP8: case Chip8GenericOptions::eCHIP10: - case Chip8GenericOptions::eCHIP8VIP: default: return Chip8GenericBase::C8F5_COSMAC; } @@ -355,7 +344,6 @@ static Chip8GenericBase::Chip8BigFont getBigFontId(Chip8GenericOptions::Supporte case Chip8GenericOptions::eSCHIP11: case Chip8GenericOptions::eSCHPC: case Chip8GenericOptions::eSCHIP_MODERN: - case Chip8GenericOptions::eCHICUEYI: return Chip8GenericBase::C8F10_SCHIP11; case Chip8GenericOptions::eMEGACHIP: return Chip8GenericBase::C8F10_MEGACHIP; @@ -364,7 +352,6 @@ static Chip8GenericBase::Chip8BigFont getBigFontId(Chip8GenericOptions::Supporte case Chip8GenericOptions::eCHIP8: case Chip8GenericOptions::eCHIP10: case Chip8GenericOptions::eCHIP48: - case Chip8GenericOptions::eCHIP8VIP: default: return Chip8GenericBase::C8F10_NONE; } @@ -416,7 +403,7 @@ void Chip8GenericEmulator::reset() _errorMessage.clear(); _wavePhase = 0; _simpleRandState = _simpleRandSeed; - _isHires = _options.optOnlyHires ? true : false; + _isHires = _options.optOnlyHires; _isInstantDxyn = _options.optInstantDxyn; _isMegaChipMode = false; _planes = 1; @@ -639,29 +626,6 @@ void Chip8GenericEmulator::setHandler() on(0xF0FF, 0xF075, &Chip8GenericEmulator::opFx75); on(0xF0FF, 0xF085, &Chip8GenericEmulator::opFx85); break; - case Chip8GenericOptions::eCHICUEYI: - on(0xFFF0, 0x00C0, &Chip8GenericEmulator::op00Cn_masked); - on(0xFFF0, 0x00D0, &Chip8GenericEmulator::op00Dn_masked); - on(0xFFFF, 0x00FB, &Chip8GenericEmulator::op00FB_masked); - on(0xFFFF, 0x00FC, &Chip8GenericEmulator::op00FC_masked); - on(0xFFFF, 0x00FD, &Chip8GenericEmulator::op00FD); - on(0xFFFF, 0x00FE, &Chip8GenericEmulator::op00FE_withClear); - on(0xFFFF, 0x00FF, &Chip8GenericEmulator::op00FF_withClear); - on(0xF000, 0x3000, &Chip8GenericEmulator::op3xnn_with_F000); - on(0xF000, 0x4000, &Chip8GenericEmulator::op4xnn_with_F000); - on(0xF00F, 0x5000, &Chip8GenericEmulator::op5xy0_with_F000); - on(0xF00F, 0x5002, &Chip8GenericEmulator::op5xy2); - on(0xF00F, 0x5003, &Chip8GenericEmulator::op5xy3); - on(0xF00F, 0x5004, &Chip8GenericEmulator::op5xy4); - on(0xF00F, 0x9000, &Chip8GenericEmulator::op9xy0_with_F000); - on(0xF0FF, 0xE09E, &Chip8GenericEmulator::opEx9E_with_F000); - on(0xF0FF, 0xE0A1, &Chip8GenericEmulator::opExA1_with_F000); - on(0xFFFF, 0xF000, &Chip8GenericEmulator::opF000); - on(0xF0FF, 0xF001, &Chip8GenericEmulator::opFx01); - on(0xFFFF, 0xF002, &Chip8GenericEmulator::opF002); - on(0xF0FF, 0xF030, &Chip8GenericEmulator::opFx30); - on(0xF0FF, 0xF03A, &Chip8GenericEmulator::opFx3A); - break; default: break; } } @@ -738,6 +702,28 @@ void Chip8GenericEmulator::executeFrame() } } +void Chip8GenericEmulator::handleTimer() +{ + if(_execMode != ePAUSED) { + ++_frameCounter; + ++_randomSeed; + _host.vblank(); +#ifdef EMU_AUDIO_DEBUG + std::clog << "handle-timer" << std::endl; +#endif + if (_rDT > 0) + --_rDT; + if (_rST > 0) + --_rST; + if (!_rST) + _wavePhase = 0; + if(_screenNeedsUpdate) { + _host.updateScreen(); + _screenNeedsUpdate = false; + } + } +} + inline void Chip8GenericEmulator::executeInstructionNoBreakpoints() { uint16_t opcode = (_memory[_rPC] << 8) | _memory[_rPC + 1]; diff --git a/src/emulation/chip8generic.hpp b/src/emulation/chip8generic.hpp index 24a46d8..c345abd 100644 --- a/src/emulation/chip8generic.hpp +++ b/src/emulation/chip8generic.hpp @@ -49,7 +49,7 @@ struct Chip8GenericOptions static Chip8GenericOptions fromProperties(const Properties& props); static Properties& registeredPrototype(); Chip8Variant variant() const; - enum SupportedPreset { eCHIP8, eCHIP8TE, eCHIP10, eCHIP8E, eCHIP8X, eCHIP48, eSCHIP10, eSCHIP11, eSCHPC, eSCHIP_MODERN, eMEGACHIP, eXOCHIP, eCHIP8VIP, eCHIP8VIP_TPD, eCHIP8VIP_FPD, eCHIP8EVIP, eCHIP8XVIP, eCHIP8XVIP_TPD, eCHIP8XVIP_FPD, eRAWVIP, eCHIP8DREAM, eC8D68CHIPOSLO, eCHICUEYI, ePORTABLE, eNUM_PRESETS }; + enum SupportedPreset { eCHIP8, eCHIP10, eCHIP8E, eCHIP8X, eCHIP48, eSCHIP10, eSCHIP11, eSCHPC, eSCHIP_MODERN, eMEGACHIP, eXOCHIP, eNUM_PRESETS }; SupportedPreset behaviorBase{eCHIP8}; uint32_t ramSize{4096}; uint16_t startAddress{0x200}; @@ -74,7 +74,7 @@ struct Chip8GenericOptions bool optHas16BitAddr{false}; bool optXOChipSound{false}; bool optChicueyiSound{false}; - bool optExtendedVBlank{true}; + bool optExtendedVBlank{false}; bool optPalVideo{false}; bool traceLog{false}; int instructionsPerFrame{15}; @@ -88,9 +88,9 @@ class Chip8GenericEmulator : public Chip8GenericBase { public: using OpcodeHandler = void (Chip8GenericEmulator::*)(uint16_t); - const uint32_t ADDRESS_MASK; - const int SCREEN_WIDTH; - const int SCREEN_HEIGHT; + uint32_t ADDRESS_MASK; + int SCREEN_WIDTH; + int SCREEN_HEIGHT; Chip8GenericEmulator(EmulatorHost& host, Properties& props, IChip8Emulator* other = nullptr); ~Chip8GenericEmulator() override; @@ -107,6 +107,16 @@ class Chip8GenericEmulator : public Chip8GenericBase void executeInstructions(int numInstructions) override; int64_t executeFor(int64_t microseconds) override; void executeFrame() override; + void handleTimer() override; + bool needsScreenUpdate() override { bool rc = _screenNeedsUpdate; _screenNeedsUpdate = false; return _isMegaChipMode ? false : rc; } + uint16_t getCurrentScreenWidth() const override { return _isMegaChipMode ? 256 : _options.optAllowHires ? 128 : 64; } + uint16_t getCurrentScreenHeight() const override { return _isMegaChipMode ? 192 : _options.optAllowHires ? 64 : 32; } + uint16_t getMaxScreenWidth() const override { return _options.behaviorBase == Chip8GenericOptions::eMEGACHIP ? 256 : 128; } + uint16_t getMaxScreenHeight() const override { return _options.behaviorBase == Chip8GenericOptions::eMEGACHIP ? 192 : 64; } + const VideoType* getScreen() const override { return _isMegaChipMode ? nullptr : &_screen; } + const VideoRGBAType* getScreenRGBA() const override { return _isMegaChipMode ? _screenRGBA : nullptr; } + uint8_t getScreenAlpha() const override { return _screenAlpha; } + bool isDoublePixel() const override { return _options.behaviorBase == Chip8GenericOptions::eMEGACHIP ? false : (_options.optAllowHires && !_isHires); } uint8_t getNextMCSample(); @@ -489,6 +499,7 @@ class Chip8GenericEmulator : public Chip8GenericBase uint32_t _simpleRandState{12345}; int _chip8xBackgroundColor{0}; uint8_t _vp595Frequency{0x80}; + int64_t _waitCycles{0}; #ifdef GEN_OPCODE_STATS std::map _opcodeStats; #endif diff --git a/src/emulation/chip8opcodedisass.hpp b/src/emulation/chip8opcodedisass.hpp index c28ee4f..67c3912 100644 --- a/src/emulation/chip8opcodedisass.hpp +++ b/src/emulation/chip8opcodedisass.hpp @@ -41,7 +41,7 @@ class Chip8OpcodeDisassembler { public: using SymbolResolver = std::function; - Chip8OpcodeDisassembler(Chip8Variant variant, SymbolResolver resolver = {}); + explicit Chip8OpcodeDisassembler(Chip8Variant variant, SymbolResolver resolver = {}); std::tuple disassembleInstruction(const uint8_t* code, const uint8_t* end) const; protected: SymbolResolver _labelOrAddress; diff --git a/src/emulation/chip8strict.cpp b/src/emulation/chip8strict.cpp index 7d7e12e..0a5454e 100644 --- a/src/emulation/chip8strict.cpp +++ b/src/emulation/chip8strict.cpp @@ -38,10 +38,10 @@ Properties& Chip8StrictOptions::registeredPrototype() using namespace std::string_literals; auto& prototype = Properties::getProperties(PROP_CLASS); if(!prototype) { - prototype.registerProperty({PROP_TRACE_LOG, false, "Enable trace log", false}); - prototype.registerProperty({PROP_CLOCK, Property::Integer{1760640, 100000, 500'000'000}, "Clock frequency, default is 1760640", false}); - prototype.registerProperty({PROP_RAM, Property::Combo{"2048"s, "4096"s, "8192"s, "12288"s, "16384"s, "32768"s}, "Size of ram in bytes", false}); - prototype.registerProperty({PROP_CLEAN_RAM, false, "Delete ram on startup", false}); + prototype.registerProperty({PROP_TRACE_LOG, false, "Enable trace log", eWritable}); + prototype.registerProperty({PROP_CLOCK, Property::Integer{1760640, 100000, 500'000'000}, "Clock frequency, default is 1760640", eWritable}); + prototype.registerProperty({PROP_RAM, Property::Combo{"2048"s, "4096"s, "8192"s, "12288"s, "16384"s, "32768"s}, "Size of ram in bytes", eWritable}); + prototype.registerProperty({PROP_CLEAN_RAM, false, "Delete ram on startup", eWritable}); } return prototype; } diff --git a/src/emulation/coreregistry.hpp b/src/emulation/coreregistry.hpp index f85db03..af0dd28 100644 --- a/src/emulation/coreregistry.hpp +++ b/src/emulation/coreregistry.hpp @@ -121,10 +121,13 @@ class CoreRegistry { } std::pair createCore(EmulatorHost& host, Properties& props) const override { - std::string variant = prefix() + "-CUSTOM"; + std::string variant = prefix().empty() ? "CUSTOM" : prefix() + "-CUSTOM"; for(const auto& setupInfo : presets) { if(props == setupInfo.options.asProperties()) { - variant = !std::strcmp(setupInfo.presetName, "NONE") ? prefix() : prefix() + "-" + setupInfo.presetName; + if(prefix().empty()) + variant = setupInfo.presetName; + else + variant = !std::strcmp(setupInfo.presetName, "NONE") ? prefix() : prefix() + "-" + setupInfo.presetName; } } auto options = OptionsType::fromProperties(props); @@ -198,6 +201,15 @@ class CoreRegistry { return {}; } + int classIndex(const Properties& props) const + { + for(int idx = 0; idx < orderedFactories.size(); ++idx) { + if(fuzzyCompare(orderedFactories[idx].first, props.propertyClass())) { + return idx; + } + } + return -1; + } static IFactoryInfo::VariantIndex variantIndex(const Properties& props) { if (auto iter = factoryMap().find(props.propertyClass()); iter != factoryMap().end()) { @@ -230,12 +242,7 @@ class CoreRegistry { const IFactoryInfo& operator[](size_t index) const { - auto iter = begin(); - while(iter != end() && index) { - ++iter; - --index; - } - return iter == end() ? *begin()->second : *iter->second; + return index < orderedFactories.size() ? *orderedFactories[index].second : *orderedFactories.front().second; } CoreRegistry(); diff --git a/src/emulation/cosmacvip.cpp b/src/emulation/cosmacvip.cpp index 9e6297d..1292aff 100644 --- a/src/emulation/cosmacvip.cpp +++ b/src/emulation/cosmacvip.cpp @@ -74,11 +74,11 @@ struct CosmacVIPOptions using namespace std::string_literals; auto& prototype = Properties::getProperties(PROP_CLASS); if(!prototype) { - prototype.registerProperty({PROP_TRACE_LOG, false, "Enable trace log", false}); + prototype.registerProperty({PROP_TRACE_LOG, false, "Enable trace log", eWritable}); prototype.registerProperty({PROP_CPU, "CDP1802"s, "CPU type (currently only cdp1802)"}); - prototype.registerProperty({PROP_CLOCK, Property::Integer{1760640, 100000, 500'000'000}, "Clock frequency, default is 1760640", false}); - prototype.registerProperty({PROP_RAM, Property::Combo{"2048"s, "4096"s, "8192"s, "12288"s, "16384"s, "32768"s}, "Size of ram in bytes", false}); - prototype.registerProperty({PROP_CLEAN_RAM, false, "Delete ram on startup", false}); + prototype.registerProperty({PROP_CLOCK, Property::Integer{1760640, 100000, 500'000'000}, "Clock frequency, default is 1760640", eWritable}); + prototype.registerProperty({PROP_RAM, Property::Combo{"2048"s, "4096"s, "8192"s, "12288"s, "16384"s, "32768"s}, "Size of ram in bytes", eWritable}); + prototype.registerProperty({PROP_CLEAN_RAM, false, "Delete ram on startup", eWritable}); prototype.registerProperty({PROP_VIDEO, Property::Combo{"CDP1861", "CDP1861-C10-HIRES", "VP-590", "CDP1864"}, "Video hardware, default cdp1861"}); prototype.registerProperty({PROP_AUDIO, Property::Combo{"CA555 Buzzer", "VP-595 Simple SB", "VP-551 2x Super SB"}, "Audio hardware, default is ca555-buzzer"}); prototype.registerProperty({PROP_KEYBOARD, Property::Combo{"VIP Hex", "VP-580 2x Hex", "VP-601 VIP ASCII", "VP-611 VIP A+NP"}, "Keyboard type, default is VIP hex"}); diff --git a/src/emulation/hardware/genericcpu.hpp b/src/emulation/hardware/genericcpu.hpp index ed0e5e2..456f53e 100644 --- a/src/emulation/hardware/genericcpu.hpp +++ b/src/emulation/hardware/genericcpu.hpp @@ -143,7 +143,7 @@ class GenericCpu } virtual bool isBreakpointTriggered() { auto result = _breakpointTriggered; _breakpointTriggered = false; return result; } protected: - mutable ExecMode _execMode{eRUNNING}; + mutable ExecMode _execMode{ePAUSED}; CpuState _cpuState{eNORMAL}; uint32_t _stepOverSP{}; std::array _breakMap{}; diff --git a/src/emulation/properties.cpp b/src/emulation/properties.cpp index 3d60a2a..5f53515 100644 --- a/src/emulation/properties.cpp +++ b/src/emulation/properties.cpp @@ -36,41 +36,41 @@ namespace emu { std::map Properties::propertyRegistry{}; -Property::Property(const std::string& name, Value val, std::string description, std::string additionalInfo, bool isReadOnly) +Property::Property(const std::string& name, Value val, std::string description, std::string additionalInfo, PropertyAccess access_) : _name(name) , _jsonKey(Properties::makeJsonKey(name)) , _optionName(toOptionName(name)) , _value(std::move(val)) , _description(std::move(description)) , _additionalInfo(std::move(additionalInfo)) - , _isReadonly(isReadOnly) + , _access(access_) {} -Property::Property(const std::string& name, Value val, std::string description, bool isReadOnly) +Property::Property(const std::string& name, Value val, std::string description, PropertyAccess access_) : _name(name) , _jsonKey(Properties::makeJsonKey(name)) , _optionName(toOptionName(name)) , _value(std::move(val)) , _description(std::move(description)) - , _isReadonly(isReadOnly) + , _access(access_) {} -Property::Property(const NameAndKeyName& nameAndKey, Value val, std::string description, bool isReadOnly) +Property::Property(const NameAndKeyName& nameAndKey, Value val, std::string description, PropertyAccess access_) : _name(nameAndKey.name) , _jsonKey(Properties::makeJsonKey(nameAndKey.keyName)) , _optionName(toOptionName(nameAndKey.keyName)) , _value(std::move(val)) , _description(std::move(description)) - , _isReadonly(isReadOnly) + , _access(access_) {} -Property::Property(const NameAndKeyName& nameAndKey, Value val, bool isReadOnly) +Property::Property(const NameAndKeyName& nameAndKey, Value val, PropertyAccess access_) : _name(nameAndKey.name) , _jsonKey(Properties::makeJsonKey(nameAndKey.keyName)) , _optionName(toOptionName(nameAndKey.keyName)) , _value(std::move(val)) , _description(nameAndKey.name) - , _isReadonly(isReadOnly) + , _access(access_) {} Property::Property(const Property& other) = default; diff --git a/src/emulation/properties.hpp b/src/emulation/properties.hpp index d3841ba..a7b50c4 100644 --- a/src/emulation/properties.hpp +++ b/src/emulation/properties.hpp @@ -49,6 +49,35 @@ template visitor(Ts...) -> visitor; +enum PropertyAccess { eReadOnly, eWritable, eInvisible }; + + +class Palette +{ +public: + struct Color { + uint8_t r, g, b; + }; + Palette(std::initializer_list colors) + : _colors(colors) + {} + Palette(std::initializer_list colors) + { + _colors.reserve(colors.size()); + for(const auto& col : colors) { + if(col.length()>1 && col[0] == '#') { + uint32_t colInt = std::strtoul(col.data() + 1, nullptr, 16); + _colors.emplace_back(colInt>>16, colInt>>8, colInt); + } + else { + _colors.emplace_back(0,0,0); + } + } + } + +private: + std::vector _colors; +}; class Property { @@ -96,10 +125,10 @@ class Property }; using Value = std::variant; - Property(const std::string& name, Value val, std::string description, std::string additionalInfo, bool isReadOnly = true); - Property(const std::string& name, Value val, std::string description, bool isReadOnly = true); - Property(const NameAndKeyName& nameAndKey, Value val, std::string description, bool isReadOnly = true); - Property(const NameAndKeyName& nameAndKey, Value val, bool isReadOnly = true); + Property(const std::string& name, Value val, std::string description, std::string additionalInfo, PropertyAccess access_ = eReadOnly); + Property(const std::string& name, Value val, std::string description, PropertyAccess access_ = eReadOnly); + Property(const NameAndKeyName& nameAndKey, Value val, std::string description, PropertyAccess access_ = eReadOnly); + Property(const NameAndKeyName& nameAndKey, Value val, PropertyAccess access_ = eReadOnly); Property(const Property& other); const std::string& getName() const { return _name; } const std::string& getJsonKey() const { return _jsonKey; } @@ -108,7 +137,7 @@ class Property const std::string& getDescription() const { return _description; } const std::string& getAdditionalInfo() const { return _additionalInfo; } void setAdditionalInfo(std::string info) { _additionalInfo = std::move(info); } - bool isReadonly() const { return _isReadonly; } + PropertyAccess access() const { return _access; } bool isValid() const { return !std::holds_alternative(_value); } explicit operator bool() const { return isValid(); } Value& getValue() { return _value; } @@ -149,7 +178,7 @@ class Property Value _value; std::string _description; std::string _additionalInfo; - bool _isReadonly{true}; + PropertyAccess _access{eReadOnly}; }; class Properties @@ -213,10 +242,10 @@ class Properties } return iter->second; } - bool isReadonly(size_t index) const + PropertyAccess access(size_t index) const { auto iter = _valueMap.find(_valueList[index]); - return iter == _valueMap.end() || iter->second.isReadonly(); + return iter == _valueMap.end() ? eInvisible : iter->second.access(); } Property& operator[](const std::string& key) { diff --git a/src/librarian.hpp b/src/librarian.hpp index 67058ec..759fe39 100644 --- a/src/librarian.hpp +++ b/src/librarian.hpp @@ -68,7 +68,7 @@ class Librarian int pixelAspect{1}; std::vector pixel; }; - Librarian(const CadmiumConfiguration& cfg); + explicit Librarian(const CadmiumConfiguration& cfg); std::string currentDirectory() const { return _currentPath; } std::string fullPath(std::string file) const; bool fetchDir(std::string directory); diff --git a/test/chip8adapter.cpp b/test/chip8adapter.cpp index fed7323..e28f6b6 100644 --- a/test/chip8adapter.cpp +++ b/test/chip8adapter.cpp @@ -90,7 +90,6 @@ std::unique_ptr createChip8Instance(Chip8TestVariant varian } #elif defined(TEST_CHIP8EMULATOR_STRICT) -#include #include std::unique_ptr createChip8Instance(Chip8TestVariant variant) diff --git a/test/chip8adapter.hpp b/test/chip8adapter.hpp index 14c8a34..8d785bd 100644 --- a/test/chip8adapter.hpp +++ b/test/chip8adapter.hpp @@ -26,7 +26,7 @@ #pragma once #include -#include +#include #include #include