From ed59780a9a141693a972c37ffdaeebcfa6efd426 Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Thu, 26 Sep 2024 07:55:38 +0200 Subject: [PATCH] wip property rework --- src/cadmium.cpp | 102 +++++++++++--------------- src/debugger.cpp | 77 +++++++++++++++---- src/debugger.hpp | 2 +- src/emuhostex.cpp | 4 +- src/emulation/chip8generic.cpp | 28 +++++++ src/emulation/chip8generic.hpp | 3 + src/emulation/chip8genericbase.hpp | 3 - src/emulation/chip8realcorebase.hpp | 3 - src/emulation/chip8strict.cpp | 16 ++++ src/emulation/chip8strict.hpp | 8 +- src/emulation/cosmacvip.cpp | 32 +++++++- src/emulation/cosmacvip.hpp | 3 + src/emulation/hardware/cdp1802.hpp | 3 +- src/emulation/hardware/genericcpu.hpp | 12 ++- src/emulation/hardware/m6800.hpp | 3 +- src/emulation/ichip8.hpp | 3 - src/emulation/iemulationcore.hpp | 2 + 17 files changed, 217 insertions(+), 87 deletions(-) diff --git a/src/cadmium.cpp b/src/cadmium.cpp index af81118..6e64e14 100644 --- a/src/cadmium.cpp +++ b/src/cadmium.cpp @@ -1737,6 +1737,10 @@ class Cadmium : public emu::EmuHostEx using namespace gui; auto oldProps = _properties; bool forceUpdate = false; + static emu::Properties propsMemento; + auto& props = _properties; + if(props != propsMemento) + propsMemento = props; BeginColumns(); SetNextWidth(0.42f); BeginGroupBox("CHIP-8 variant / Core"); @@ -1855,54 +1859,51 @@ class Cadmium : public emu::EmuHostEx } //else if(!_chipEmu->isGenericEmulation()) { #endif - if(true) { - BeginGroupBox("System Configuration"); - auto startY = GetCurrentPos().y; - auto colWidth1 = GetContentAvailable().width / 2 - 1; - auto colWidth2 = GetContentAvailable().width - colWidth1 - 1; - auto colHeight = GetContentAvailable().height; - auto rowCount = 0; - Space(5); - BeginColumns(); - SetSpacing(2); - SetNextWidth(colWidth1); - 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); + BeginGroupBox("System Configuration"); + auto startY = GetCurrentPos().y; + auto colWidth1 = GetContentAvailable().width / 2 - 1; + auto colWidth2 = GetContentAvailable().width - colWidth1 - 1; + auto colHeight = GetContentAvailable().height; + auto rowCount = 0; + Space(5); + BeginColumns(); + SetSpacing(2); + SetNextWidth(colWidth1); + Begin(); + SetSpacing(2); + 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(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 { - editProperty(prop, forceUpdate); - } - ++rowCount; + else { + editProperty(prop, forceUpdate); } + ++rowCount; } - auto* changedProp = props.changedProperty(propsMemento); - if(changedProp) { - // on change... + } + auto* changedProp = props.changedProperty(propsMemento); + if(changedProp) { + // on change... + if(_chipEmu->updateProperties(props, *changedProp)) { + // core asks for a cold start... updateEmulatorOptions(props); - //rcb->updateProperties(*changedProp); } - auto used = GetCurrentPos().y - startY; - Space(quirksHeight - used - 4); - End(); - EndColumns(); - EndGroupBox(); } + auto used = GetCurrentPos().y - startY; + Space(quirksHeight - used - 4); + End(); + EndColumns(); + EndGroupBox(); + Space(14); { StyleManager::Scope guard; @@ -1930,7 +1931,6 @@ class Cadmium : public emu::EmuHostEx prevPalette.assign(_colorPalette.begin(), _colorPalette.end()); } static int sel = 5; - if(DropdownBox("Cadmium;Silicon-8;Pico-8;Octo Classic;LCD;Custom", &sel, true)) { switch(sel) { case 0: @@ -2011,11 +2011,6 @@ class Cadmium : public emu::EmuHostEx EndColumns(); } Space(8); - // TODO: Fix this - /*if(forceUpdate || oldOptions != _options) { - updateEmulatorOptions(_options); - saveConfig(); - }*/ BeginColumns(); Space(100); SetNextWidth(0.21f); @@ -2170,9 +2165,6 @@ class Cadmium : public emu::EmuHostEx SetNextWidth(80); if(!selectedInfo.analyzed) GuiDisable(); if(Button("Load") && selectedInfo.analyzed) { - //if(selectedInfo.variant != _options.behaviorBase && selectedInfo.variant != emu::Chip8EmulatorOptions::eCHIP8) { - // updateEmulatorOptions(emu::Chip8EmulatorOptions::optionsOfPreset(selectedInfo.variant)); - //} auto mainView = _mainView; loadRom(_librarian.fullPath(selectedInfo.filePath).c_str(), LoadOptions::None); if(_mainView == mainView) @@ -2180,14 +2172,8 @@ class Cadmium : public emu::EmuHostEx } SetNextWidth(110); if(Button("Load w/o Config") && selectedInfo.analyzed) { - //if(selectedInfo.variant != _options.behaviorBase && selectedInfo.variant != emu::Chip8EmulatorOptions::eCHIP8) { - // updateEmulatorOptions(emu::Chip8EmulatorOptions::optionsOfPreset(selectedInfo.variant)); - //} - // TODO: Fix this - // auto options = _options; + _chipEmu->reset(); loadRom(_librarian.fullPath(selectedInfo.filePath).c_str(), LoadOptions::DontChangeOptions); - // TODO: Fix this - // updateEmulatorOptions(options); _mainView = _lastView; } GuiEnable(); diff --git a/src/debugger.cpp b/src/debugger.cpp index 9996363..471148e 100644 --- a/src/debugger.cpp +++ b/src/debugger.cpp @@ -26,6 +26,7 @@ #include "icons.h" #include +#include #include #include "debugger.hpp" @@ -56,14 +57,12 @@ void Debugger::captureStates() auto unit = _core->executionUnit(0); _memBackup.resize(_core->memSize()); std::memcpy(_memBackup.data(), _core->memory(), _core->memSize()); - if(chip8Core()) { - _chip8StackBackup.resize(chip8Core()->stackSize()); - // TODO: Fix this - //std::memcpy(_chip8StackBackup.data(), chip8Core()->stackElements(), sizeof(uint16_t) * chip8Core()->stackSize()); - } _cpuStatesBackup.resize(_core->numberOfExecutionUnits()); + _stackBackup.resize(_core->numberOfExecutionUnits()); for(size_t i = 0; i < _core->numberOfExecutionUnits(); ++i) { _core->executionUnit(i)->fetchAllRegisters(_cpuStatesBackup[i]); + auto stack = _core->executionUnit(i)->stack(); + _stackBackup[i].assign(stack.content.begin(), stack.content.end()); } } @@ -72,6 +71,62 @@ emu::IChip8Emulator* Debugger::chip8Core() return dynamic_cast(_core->executionUnit(0)); } +template +T readStackEntry(const uint8_t* address, emu::GenericCpu::Endianness endianness) +{ + switch(endianness) { + case emu::GenericCpu::eNATIVE: + return *reinterpret_cast(address); + case emu::GenericCpu::eBIG: { + T result = 0; + for (int i = sizeof(T) - 1; i >= 0; --i) { + result |= static_cast(address[i]) << (8 * i); + } + return result; + } + case emu::GenericCpu::eLITTLE: { + T result = 0; + for (int i = 0; i < sizeof(T); ++i) { + result |= static_cast(address[i]) << (8 * i); + } + return result; + } + } + return 0; +} + +static std::pair formatStackElement(const emu::GenericCpu::StackContent& stack, size_t index, const uint8_t* backup) +{ + static char buffer[64]; + if(stack.content.empty() || !stack.entrySize || index * stack.entrySize > stack.content.size()) { + buffer[0] = 0; + return {buffer, false}; + } + auto offset = stack.stackDirection == emu::GenericCpu::eUPWARDS ? index * stack.entrySize : stack.content.size() - (index + 1) * stack.entrySize; + const uint8_t* entry = &stack.content[offset]; + backup += offset; + bool changed = false; + switch(stack.entrySize) { + case 1: { + auto val = readStackEntry(entry, stack.endianness); + changed = val != readStackEntry(backup, stack.endianness); + fmt::format_to_n(buffer, 64, "{:02X}\0", val); break; + } + case 2: { + auto val = readStackEntry(entry, stack.endianness); + changed = val != readStackEntry(backup, stack.endianness); + fmt::format_to_n(buffer, 64, "{:04X}\0", val); break; + } + case 4: { + auto val = readStackEntry(entry, stack.endianness); + changed = val != readStackEntry(backup, stack.endianness); + fmt::format_to_n(buffer, 64, "{:06X}\0", val); break; + } + default: buffer[0] = 0; + } + return {buffer, changed}; +} + void Debugger::render(Font& font, std::function drawScreen) { using namespace gui; @@ -152,15 +207,11 @@ void Debugger::render(Font& font, std::function drawScreen) auto area = GetContentAvailable(); pos.x += 0; Space(area.height); - // TODO: Fix this - /* - auto stackSize = _core->focussedExecutionUnit()->stackSize(); - const auto* stack = _core->focussedExecutionUnit()->stackElements(); - bool fourDigitStack = !_core->isGenericEmulation() || _core->memSize() > 4096; - for (int i = 0; i < stackSize; ++i) { - DrawTextEx(font, fourDigitStack ? TextFormat("%X:%04X", i, stack[i]) : TextFormat("%X: %03X", i, stack[i]), {pos.x, pos.y + i * lineSpacing}, 8, 0, stack[i] == _chip8StackBackup[i] ? lightgrayCol : yellowCol); + auto stack = _core->focussedExecutionUnit()->stack(); + for (int i = 0; i < _core->focussedExecutionUnit()->stackSize(); ++i) { + auto element = formatStackElement(stack, i, _stackBackup[0].data()); + DrawTextEx(font, TextFormat("%X:%s", i & 0xF, element.first), {pos.x, pos.y + i * lineSpacing}, 8, 0, element.second ? yellowCol : lightgrayCol); } - */ } } else { diff --git a/src/debugger.hpp b/src/debugger.hpp index cb3e22a..a1759ee 100644 --- a/src/debugger.hpp +++ b/src/debugger.hpp @@ -63,7 +63,7 @@ class Debugger bool _memViewFollow{true}; std::vector _cpuStates; std::vector _cpuStatesBackup; - std::vector _chip8StackBackup; + std::vector> _stackBackup; std::vector _memBackup; }; diff --git a/src/emuhostex.cpp b/src/emuhostex.cpp index f283fe6..2c6bf8c 100644 --- a/src/emuhostex.cpp +++ b/src/emuhostex.cpp @@ -176,9 +176,11 @@ void EmuHostEx::updateEmulatorOptions(const Properties& properties) { // TODO: Fix this if(_previousProperties != properties || !_chipEmu) { + if(&properties != &_properties) { - _previousProperties = _properties = properties; + _properties = properties; } + _previousProperties = properties; _chipEmu = create(_properties, _chipEmu.get()); if(_chipEmu->getScreen()) (void)_chipEmu->getScreen(); diff --git a/src/emulation/chip8generic.cpp b/src/emulation/chip8generic.cpp index cb3fca2..3ce8e5f 100644 --- a/src/emulation/chip8generic.cpp +++ b/src/emulation/chip8generic.cpp @@ -199,6 +199,7 @@ struct Chip8GenericSetupInfo const char* presetName; const char* description; const char* defaultExtensions; + chip8::VariantSet supportedChip8Variants{chip8::Variant::CHIP_8}; Chip8GenericOptions options; }; @@ -208,24 +209,28 @@ Chip8GenericSetupInfo genericPresets[] = { "CHIP-8", "The classic CHIP-8 for the COSMAC VIP by Joseph Weisbecker, 1977", ".ch8", + .supportedChip8Variants = {chip8::Variant::CHIP_8}, {.behaviorBase = Chip8GenericOptions::eCHIP8, .optExtendedVBlank = true} }, { "CHIP-10", "128x64 CHIP-8 from #VIPER-V1-I7 and #IpsoFacto-I10, by Ben H. Hutchinson, Jr., 1979", ".ch10", + .supportedChip8Variants = {chip8::Variant::CHIP_10}, {.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", + .supportedChip8Variants = {chip8::Variant::CHIP_8E}, {.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", + .supportedChip8Variants = {chip8::Variant::CHIP_8X}, {.behaviorBase = Chip8GenericOptions::eCHIP8X, .startAddress = 768, .optExtendedVBlank = true, .instructionsPerFrame = 18, .palette = {"#000080","#000000","#008000","#800000","#181818","#FF0000","#0000FF","#FF00FF","#00FF00","#FFFF00","#00FFFF","#FFFFFF","#000000","#000000","#000000","#000000"} } @@ -234,42 +239,49 @@ Chip8GenericSetupInfo genericPresets[] = { "CHIP-48", "The initial CHIP-8 port to the HP-48SX by Andreas Gustafsson, 1990", ".ch48;.c48", + .supportedChip8Variants = {chip8::Variant::CHIP_48}, {.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 by Erik Bryntse, 1991", ".sc10", + .supportedChip8Variants = {chip8::Variant::SCHIP_1_0}, {.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 by Erik Bryntse, 1991", ".sc8;.sc11", + .supportedChip8Variants = {chip8::Variant::SCHIP_1_1}, {.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, 2017", ".scc", + .supportedChip8Variants = {chip8::Variant::SCHIPC}, {.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, 2014", ".scm", + .supportedChip8Variants = {chip8::Variant::SCHIP_MODERN}, {.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, 2007", ".mc8", + .supportedChip8Variants = {chip8::Variant::MEGA_CHIP}, {.behaviorBase = Chip8GenericOptions::eMEGACHIP, .ramSize = 0x1000000, .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, 2014", "xo8", + .supportedChip8Variants = {chip8::Variant::XO_CHIP}, {.behaviorBase = Chip8GenericOptions::eXOCHIP, .ramSize = 0x10000, .optDontResetVf = true, .optWrapSprites = true, .optInstantDxyn = true, .optLoresDxy0Is16x16 = true, .optModeChangeClear = true, .optAllowHires = true, .optAllowColors = true, .optHas16BitAddr = true, .optXOChipSound = true, .instructionsPerFrame = 1000} } /*{Opts::eCHIP10, R"({"optAllowHires":true,"optOnlyHires":true})"}, @@ -319,9 +331,16 @@ Chip8GenericEmulator::Chip8GenericEmulator(EmulatorHost& host, Properties& props _screen.setMode(SCREEN_WIDTH, SCREEN_HEIGHT); _screenRGBA1.setMode(SCREEN_WIDTH, SCREEN_HEIGHT); _screenRGBA2.setMode(SCREEN_WIDTH, SCREEN_HEIGHT); + if(!props.palette().empty()) { + _screen.setPalette(props.palette()); + } setHandler(); } +GenericCpu::StackContent Chip8GenericEmulator::stack() const +{ + return {2, eNATIVE, eUPWARDS, std::span(reinterpret_cast(_stack.data()), reinterpret_cast(_stack.data() + _stack.size()))}; +} static Chip8GenericBase::Chip8Font getSmallFontId(Chip8GenericOptions::SupportedPreset behavior) { @@ -423,6 +442,15 @@ void Chip8GenericEmulator::reset() _mcPalette[254] = 0xffffffff; } +bool Chip8GenericEmulator::updateProperties(Properties& props, Property& changed) +{ + if(fuzzyAnyOf(changed.getName(), {"TraceLog", "InstructionsPerFrame", "FrameRate"})) { + _options = Chip8GenericOptions::fromProperties(props); + return false; + } + return true; +} + bool Chip8GenericEmulator::loadData(std::span data, std::optional loadAddress) { return Chip8GenericBase::loadData(data, loadAddress ? loadAddress : _options.startAddress); diff --git a/src/emulation/chip8generic.hpp b/src/emulation/chip8generic.hpp index 2b6f14d..5845518 100644 --- a/src/emulation/chip8generic.hpp +++ b/src/emulation/chip8generic.hpp @@ -102,6 +102,7 @@ class Chip8GenericEmulator : public Chip8GenericBase } uint32_t cpuID() const override { return 0xC8; } void reset() override; + bool updateProperties(Properties& props, Property& changed) override; int64_t machineCycles() const override { return 0; } int executeInstruction() override; void executeInstructionNoBreakpoints(); @@ -119,6 +120,8 @@ class Chip8GenericEmulator : public Chip8GenericBase uint8_t getScreenAlpha() const override { return _screenAlpha; } bool isDoublePixel() const override { return _options.behaviorBase == Chip8GenericOptions::eMEGACHIP ? false : (_options.optAllowHires && !_isHires); } bool loadData(std::span data, std::optional loadAddress) override; + unsigned stackSize() const override { return 16; } + StackContent stack() const override; uint8_t getNextMCSample(); diff --git a/src/emulation/chip8genericbase.hpp b/src/emulation/chip8genericbase.hpp index f7b1d95..2b6e6bc 100644 --- a/src/emulation/chip8genericbase.hpp +++ b/src/emulation/chip8genericbase.hpp @@ -54,9 +54,6 @@ class Chip8GenericBase : public IEmulationCore, public IChip8Emulator uint32_t getPC() const override { return _rPC; } uint32_t getI() const override { return _rI; } uint32_t getSP() const override { return _rSP; } - uint8_t stackSize() const override { return 24; } - std::span stack() const override { return {}; } - const uint16_t* stackElements() const override { return nullptr; } uint8_t delayTimer() const override { return _rDT; } uint8_t soundTimer() const override { return _rST; } uint8_t* memory() override { return _memory.data(); } diff --git a/src/emulation/chip8realcorebase.hpp b/src/emulation/chip8realcorebase.hpp index 822b6ab..ceeac77 100644 --- a/src/emulation/chip8realcorebase.hpp +++ b/src/emulation/chip8realcorebase.hpp @@ -75,9 +75,6 @@ class Chip8RealCoreBase : public IEmulationCore, public IChip8Emulator, public C uint32_t getPC() const override { auto pcstr = std::to_string(_state.pc); return _state.pc; } uint32_t getI() const override { return _state.i; } uint32_t getSP() const override { return _state.sp; } - std::span stack() const override { return {}; } - uint8_t stackSize() const override { return 12; } - const uint16_t* stackElements() const override { return _state.s.data(); } uint8_t delayTimer() const override { return _state.dt; } uint8_t soundTimer() const override { return _state.st; } diff --git a/src/emulation/chip8strict.cpp b/src/emulation/chip8strict.cpp index c61f2b2..a022cab 100644 --- a/src/emulation/chip8strict.cpp +++ b/src/emulation/chip8strict.cpp @@ -52,6 +52,7 @@ struct Chip8StrictSetupInfo { const char* presetName; const char* description; const char* defaultExtensions; + chip8::VariantSet supportedChip8Variants{chip8::Variant::NONE}; Chip8StrictOptions options; }; @@ -61,6 +62,7 @@ Chip8StrictSetupInfo strictPresets[] = { "chip-8", "The classic CHIP-8 that came from Joseph Weisbecker, 1977", ".ch8;.c8vip", + .supportedChip8Variants = {chip8::Variant::CHIP_8 | chip8::Variant::CHIP_8_COSMAC_VIP}, { .clockFrequency = 1760640, .ramSize = 4096, .cleanRam = true, .traceLog = false} } }; @@ -83,4 +85,18 @@ struct StrictFactoryInfo final : public CoreRegistry::FactoryInfo("First cycle exact HLE emulation of CHIP-8 on a COSMAC VIP")); +bool Chip8StrictEmulator::updateProperties(Properties& props, Property& changed) +{ + if(fuzzyAnyOf(changed.getName(), {"TraceLog", "InstructionsPerFrame", "FrameRate"})) { + _options = Chip8StrictOptions::fromProperties(props); + return false; + } + return true; +} + +GenericCpu::StackContent Chip8StrictEmulator::stack() const +{ + return {2, eBIG, eDOWNWARDS, std::span(_memory.data() + _options.ramSize - 0x160, 48)}; +} + } diff --git a/src/emulation/chip8strict.hpp b/src/emulation/chip8strict.hpp index f5b5037..28b9004 100644 --- a/src/emulation/chip8strict.hpp +++ b/src/emulation/chip8strict.hpp @@ -110,13 +110,19 @@ class Chip8StrictEmulator : public Chip8GenericBase _wavePhase = 0; } - inline uint16_t readWord(uint32_t addr) const + bool updateProperties(Properties& props, Property& changed) override; + + uint16_t readWord(uint32_t addr) const { return (readByte(addr) << 8) | readByte(addr + 1); } int64_t machineCycles() const override { return _machineCycles; } + unsigned stackSize() const override { return 24; } + + StackContent stack() const override; + int64_t executeFor(int64_t microseconds) override { if(_execMode != ePAUSED) { diff --git a/src/emulation/cosmacvip.cpp b/src/emulation/cosmacvip.cpp index d91c848..f02c307 100644 --- a/src/emulation/cosmacvip.cpp +++ b/src/emulation/cosmacvip.cpp @@ -109,6 +109,7 @@ struct CosmacVipSetupInfo { const char* presetName; const char* description; const char* defaultExtensions; + chip8::VariantSet supportedChip8Variants{chip8::Variant::NONE}; CosmacVIPOptions options; }; @@ -118,60 +119,70 @@ static CosmacVipSetupInfo vipPresets[] = { "NONE", "Raw COSMAC VIP without any CHIP-8 preloaded", ".bin;.hex;.ram;.raw", + .supportedChip8Variants = {chip8::Variant::NONE}, { .cpuType = "CDP1802", .clockFrequency = 1760640, .ramSize = 4096, .cleanRam = false, .traceLog = false, .videoType = VVT_CDP1861, .audioType = VAT_CA555_BUZZER, .keyboard = VIPK_HEX, .romName = "COSMAC-VIP", .interpreter = VC8I_NONE, .startAddress = 0} }, { "CHIP-8", "The classic CHIP-8 that came from Joseph Weisbecker, 1977", ".ch8;.c8vip;.hc8", + .supportedChip8Variants = {chip8::Variant::CHIP_8 | chip8::Variant::CHIP_8_COSMAC_VIP}, { .cpuType = "CDP1802", .clockFrequency = 1760640, .ramSize = 4096, .cleanRam = true, .traceLog = false, .videoType = VVT_CDP1861, .audioType = VAT_CA555_BUZZER, .keyboard = VIPK_HEX, .romName = "COSMAC-VIP", .interpreter = VC8I_CHIP8, .startAddress = 512} }, { "CHIP-10", "128x64 CHIP-8 with hardware modifications, from #VIPER-V1-I7 and #IpsoFacto-I10, by Ben H. Hutchinson, Jr., 1979", ".ch10;.c10", + .supportedChip8Variants = {chip8::Variant::CHIP_10}, { .cpuType = "CDP1802", .clockFrequency = 1760640, .ramSize = 4096, .cleanRam = true, .traceLog = false, .videoType = VVT_CDP1861, .audioType = VAT_CA555_BUZZER, .keyboard = VIPK_HEX, .romName = "COSMAC-VIP", .interpreter = VC8I_CHIP10, .startAddress = 512} }, { "CHIP-8 RB", "CHIP-8 modification with relative branching (BFnn, FBnn), from #VIPER-V2-I1, by Wayne Smith, 1979", ".c8rb", + .supportedChip8Variants = {chip8::Variant::CHIP_8_RB}, { .cpuType = "CDP1802", .clockFrequency = 1760640, .ramSize = 4096, .cleanRam = true, .traceLog = false, .videoType = VVT_CDP1861, .audioType = VAT_CA555_BUZZER, .keyboard = VIPK_HEX, .romName = "COSMAC-VIP", .interpreter = VC8I_CHIP8RB, .startAddress = 512} }, { "CHIP-8 TPD", "CHIP-8 with two page display (64x64), from #VIPER-V1-I3, by Andy Modla and Jef Winsor, 1979", ".c8tpd;.c8h", + .supportedChip8Variants = {chip8::Variant::CHIP_8_TPD | chip8::Variant::CHIP_8_TDP_COSMAC_VIP}, { .cpuType = "CDP1802", .clockFrequency = 1760640, .ramSize = 4096, .cleanRam = true, .traceLog = false, .videoType = VVT_CDP1861, .audioType = VAT_CA555_BUZZER, .keyboard = VIPK_HEX, .romName = "COSMAC-VIP", .interpreter = VC8I_CHIP8TPD, .startAddress = 608} }, { "CHIP-8 FPD", "CHIP-8 with four page display (64x128), from #VIPER-V2-I6, by Tom Swan, 1980", ".c8fpd", + .supportedChip8Variants = {chip8::Variant::HI_RES_CHIP_8}, { .cpuType = "CDP1802", .clockFrequency = 1760640, .ramSize = 4096, .cleanRam = true, .traceLog = false, .videoType = VVT_CDP1861, .audioType = VAT_CA555_BUZZER, .keyboard = VIPK_HEX, .romName = "COSMAC-VIP", .interpreter = VC8I_CHIP8FPD, .startAddress = 580} }, { "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", + .supportedChip8Variants = {chip8::Variant::CHIP_8X}, { .cpuType = "CDP1802", .clockFrequency = 1789773, .ramSize = 4096, .cleanRam = true, .traceLog = false, .videoType = VVT_VP_590, .audioType = VAT_VP_595_SIMPLE_SB, .keyboard = VIPK_HEX, .romName = "COSMAC-VIP", .interpreter = VC8I_CHIP8X, .startAddress = 768} }, { "CHIP-8X TPD", "A modified version of CHIP-8X to use two page display (64x64), from #VIPER-V4-I3, by by Andy Modle and Jef Winsor", ".c8xtpd", + .supportedChip8Variants = {chip8::Variant::CHIP_8X_TPD}, { .cpuType = "CDP1802", .clockFrequency = 1789773, .ramSize = 4096, .cleanRam = true, .traceLog = false, .videoType = VVT_VP_590, .audioType = VAT_VP_595_SIMPLE_SB, .keyboard = VIPK_HEX, .romName = "COSMAC-VIP", .interpreter = VC8I_CHIP8XTPD, .startAddress = 768} }, { "CHIP-8X FPD", "A modified version of CHIP-8X for the four page display mode (64x128), from #VIPER-V4-I3, by Tom Swan, sadly not actually working as described due to an implementation bug", ".c8xfpd", + .supportedChip8Variants = {chip8::Variant::HI_RES_CHIP_8X}, { .cpuType = "CDP1802", .clockFrequency = 1789773, .ramSize = 4096, .cleanRam = true, .traceLog = false, .videoType = VVT_VP_590, .audioType = VAT_VP_595_SIMPLE_SB, .keyboard = VIPK_HEX, .romName = "COSMAC-VIP", .interpreter = VC8I_CHIP8XFPD, .startAddress = 768} }, { "CHIP-8E", "CHIP-8 rewritten and extended by Gilles Detillieux, from #VIPER-V2-8+9", ".c8e", + .supportedChip8Variants = {chip8::Variant::CHIP_8E}, { .cpuType = "CDP1802", .clockFrequency = 1760640, .ramSize = 4096, .cleanRam = true, .traceLog = false, .videoType = VVT_CDP1861, .audioType = VAT_CA555_BUZZER, .keyboard = VIPK_HEX, .romName = "COSMAC-VIP", .interpreter = VC8I_CHIP8E, .startAddress = 512} } }; @@ -633,7 +644,7 @@ CosmacVIP::CosmacVIP(EmulatorHost& host, Properties& properties, IEmulationCore* _state.sp = prev->getSP(); _state.dt = prev->delayTimer(); _state.st = prev->soundTimer(); - std::memcpy(_state.s.data(), prev->stackElements(), stackSize() * sizeof(uint16_t)); + std::memcpy(_state.s.data(), prev->stack().content.data(), stackSize() * sizeof(uint16_t)); forceState(); } #if 0 //ndef PLATFORM_WEB @@ -715,6 +726,15 @@ void CosmacVIP::reset() Logger::log(Logger::eBACKEND_EMU, _impl->_cpu.cycles(), {_frames, frameCycle()}, fmt::format("End of reset: {}/{}", _impl->_cpu.cycles(), frameCycle()).c_str()); } +bool CosmacVIP::updateProperties(Properties& props, Property& changed) +{ + if(fuzzyAnyOf(changed.getName(), {"TraceLog", "InstructionsPerFrame", "FrameRate"})) { + _impl->_options = CosmacVIPOptions::fromProperties(props); + return false; + } + return false; +} + uint16_t CosmacVIP::patchRAM(VIPChip8Interpreter interpreter, uint8_t* ram, size_t size) { auto iter = g_patchSets.find(interpreter); @@ -737,6 +757,16 @@ std::string CosmacVIP::name() const return "Chip-8-RVIP"; } +unsigned CosmacVIP::stackSize() const +{ + return 24; +} + +GenericCpu::StackContent CosmacVIP::stack() const +{ + return {2, eBIG, eDOWNWARDS, std::span(_impl->_ram.data() + _impl->_options.ramSize - 0x160, 48)}; +} + size_t CosmacVIP::numberOfExecutionUnits() const { return _impl->_options.interpreter == VC8I_NONE ? 1 : 2; diff --git a/src/emulation/cosmacvip.hpp b/src/emulation/cosmacvip.hpp index 7e67056..a94c03f 100644 --- a/src/emulation/cosmacvip.hpp +++ b/src/emulation/cosmacvip.hpp @@ -54,6 +54,7 @@ class CosmacVIP : public Chip8RealCoreBase, public Cdp1802Bus ~CosmacVIP() override; void reset() override; + bool updateProperties(Properties& props, Property& changed) override; std::string name() const override; // IEmulationCore @@ -77,6 +78,8 @@ class CosmacVIP : public Chip8RealCoreBase, public Cdp1802Bus uint8_t* memory() override; int memSize() const override; + unsigned stackSize() const override; + StackContent stack() const override; int64_t frames() const override; diff --git a/src/emulation/hardware/cdp1802.hpp b/src/emulation/hardware/cdp1802.hpp index 959b6e7..fdc7eee 100644 --- a/src/emulation/hardware/cdp1802.hpp +++ b/src/emulation/hardware/cdp1802.hpp @@ -722,7 +722,8 @@ class Cdp1802 : public GenericCpu ~Cdp1802() override = default; uint8_t readMemoryByte(uint32_t addr) const override { return _bus.readByteDMA(addr); } - std::span stack() const override { return {}; } + unsigned stackSize() const override { return 0; } + StackContent stack() const override { return {}; } bool inErrorState() const override { return _cpuState == eERROR; } uint32_t cpuID() const override { return 1802; } std::string name() const override { static const std::string name = "CDP1802"; return name; } diff --git a/src/emulation/hardware/genericcpu.hpp b/src/emulation/hardware/genericcpu.hpp index 456f53e..31a601d 100644 --- a/src/emulation/hardware/genericcpu.hpp +++ b/src/emulation/hardware/genericcpu.hpp @@ -44,6 +44,8 @@ class GenericCpu public: enum ExecMode { ePAUSED, eRUNNING, eSTEP, eSTEPOVER, eSTEPOUT }; enum CpuState { eNORMAL, eIDLE, eWAIT = eIDLE, eHALT, eERROR }; + enum StackDirection { eDOWNWARDS, eUPWARDS }; + enum Endianness { eNATIVE, eLITTLE, eBIG }; struct BreakpointInfo { enum Type { eTRANSIENT, eCODED }; std::string label; @@ -54,6 +56,13 @@ class GenericCpu uint32_t value{}; uint32_t size{}; }; + struct StackContent + { + int entrySize{2}; + Endianness endianness{eLITTLE}; + StackDirection stackDirection{eDOWNWARDS}; + std::span content{}; + }; using RegisterPack = std::vector; virtual ~GenericCpu() = default; @@ -82,7 +91,8 @@ class GenericCpu virtual int64_t cycles() const = 0; virtual const ClockedTime& time() const = 0; virtual uint8_t readMemoryByte(uint32_t addr) const = 0; - virtual std::span stack() const = 0; + virtual unsigned stackSize() const = 0; + virtual StackContent stack() const = 0; virtual std::string disassembleInstructionWithBytes(int32_t pc, int* bytes) const = 0; diff --git a/src/emulation/hardware/m6800.hpp b/src/emulation/hardware/m6800.hpp index 784ff05..7adc19b 100644 --- a/src/emulation/hardware/m6800.hpp +++ b/src/emulation/hardware/m6800.hpp @@ -506,7 +506,8 @@ class M6800 { return _bus.readDebugByte(addr); } - std::span stack() const override + unsigned stackSize() const override { return 0; } + StackContent stack() const override { return {}; } diff --git a/src/emulation/ichip8.hpp b/src/emulation/ichip8.hpp index d5d4832..1a1f3d3 100644 --- a/src/emulation/ichip8.hpp +++ b/src/emulation/ichip8.hpp @@ -60,9 +60,6 @@ class IChip8Emulator : public GenericCpu virtual uint8_t getV(uint8_t index) const = 0; virtual uint32_t getI() const = 0; - //virtual uint8_t getSP() const = 0; - virtual uint8_t stackSize() const = 0; - virtual const uint16_t* stackElements() const = 0; virtual uint8_t* memory() = 0; virtual int memSize() const = 0; diff --git a/src/emulation/iemulationcore.hpp b/src/emulation/iemulationcore.hpp index 2928e73..d150b32 100644 --- a/src/emulation/iemulationcore.hpp +++ b/src/emulation/iemulationcore.hpp @@ -47,6 +47,8 @@ class IEmulationCore { virtual void reset() = 0; + virtual bool updateProperties(Properties& props, Property& changed) = 0; + virtual std::string name() const = 0; virtual CoreState coreState() const { return ECS_NORMAL; } virtual const std::string& errorMessage() const { static std::string none; return none; }