From abfc01b4836347c091392f95a700a8f4f3d50e5b Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Fri, 7 Dec 2018 21:19:44 -0330 Subject: [PATCH] Rework trapping on 'read from write port' functionality. The new code introduces a generic 'EmulationWarning' exception that can be thrown for any reason. The RWP functionality has been ported to use this, with the main benefit that if we ever have to add new exceptional cases, we no longer need to modify M6502 class; it will pick up on the new exceptions and pass them to the EmulationWorker as necessary. Functionally, the RWP stuff works the same as before, just with a different implementation behind the scenes. --- src/debugger/CartDebug.cxx | 17 +++++-- src/debugger/CartDebug.hxx | 11 +++- src/emucore/M6502.cxx | 51 ++++--------------- src/emucore/M6502.hxx | 5 -- src/emucore/exception/FatalEmulationError.cxx | 36 ------------- src/emucore/exception/FatalEmulationError.hxx | 15 +++--- src/emucore/module.mk | 3 +- src/gui/DeveloperDialog.cxx | 6 ++- 8 files changed, 45 insertions(+), 99 deletions(-) delete mode 100644 src/emucore/exception/FatalEmulationError.cxx diff --git a/src/debugger/CartDebug.cxx b/src/debugger/CartDebug.cxx index 2e7a320b7..c6d575f3d 100644 --- a/src/debugger/CartDebug.cxx +++ b/src/debugger/CartDebug.cxx @@ -32,6 +32,8 @@ #include "CartRamWidget.hxx" #include "RomWidget.hxx" #include "Base.hxx" +#include "exception/EmulationWarning.hxx" + using Common::Base; using std::hex; using std::dec; @@ -47,6 +49,7 @@ CartDebug::CartDebug(Debugger& dbg, Console& console, const OSystem& osystem) myDebugWidget(nullptr), myAddrToLineIsROM(true), myRWPortAddress(0), + myRWPortTriggersBreak(false), myLabelLength(8) // longest pre-defined label { // Add case sensitive compare for user labels @@ -129,6 +132,10 @@ CartDebug::CartDebug(Debugger& dbg, Console& console, const OSystem& osystem) DiStella::settings.rFlag = myOSystem.settings().getBool("dis.relocate"); DiStella::settings.bFlag = true; // Not currently configurable DiStella::settings.bytesWidth = 8+1; // TODO - configure based on window size + + setReadFromWritePortBreak(myOSystem.settings().getBool( + myOSystem.settings().getBool("dev.settings") ? + "dev.rwportbreak" : "plr.rwportbreak")); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -163,9 +170,13 @@ void CartDebug::triggerReadFromWritePort(uInt16 addr) { myRWPortAddress = addr; mySystem.setDirtyPage(addr); -#ifdef DEBUGGER_SUPPORT - mySystem.m6502().setReadFromWritePort(addr); -#endif // DEBUGGER_SUPPORT + + if(myRWPortTriggersBreak) + { + ostringstream msg; + msg << "RWP[@ $" << Common::Base::HEX4 << addr << "]: "; + EmulationWarning::raise(msg.str()); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/debugger/CartDebug.hxx b/src/debugger/CartDebug.hxx index e3e4c998a..3128b056c 100644 --- a/src/debugger/CartDebug.hxx +++ b/src/debugger/CartDebug.hxx @@ -109,14 +109,18 @@ class CartDebug : public DebuggerSystem CartDebugWidget* getDebugWidget() const { return myDebugWidget; } void setDebugWidget(CartDebugWidget* w) { myDebugWidget = w; } - // Indicate that a read from write port has occurred at the specified - // address. + // Called when a read from write port has occurred at the specified address. + // An exception will be thrown, if enabled void triggerReadFromWritePort(uInt16 address); // Return the address at which an invalid read was performed in a // write port area. int readFromWritePort(); + // Determines whether a debugger exception will be thrown when a read from + // the write port happens. + void setReadFromWritePortBreak(bool enable) { myRWPortTriggersBreak = enable; } + // Return the base (= non-mirrored) address of the last CPU read int lastReadBaseAddress(); // Return the base (= non-mirrored) address of the last CPU write @@ -357,6 +361,9 @@ class CartDebug : public DebuggerSystem // occurred uInt16 myRWPortAddress; + // Whether a read from write port should trigger a debugger exception + bool myRWPortTriggersBreak; + // The maximum length of all labels currently defined uInt16 myLabelLength; diff --git a/src/emucore/M6502.cxx b/src/emucore/M6502.cxx index 2a3d8fe9a..e8c394dfa 100644 --- a/src/emucore/M6502.cxx +++ b/src/emucore/M6502.cxx @@ -48,6 +48,7 @@ #include "System.hxx" #include "M6502.hxx" #include "DispatchResult.hxx" +#include "exception/EmulationWarning.hxx" #include "exception/FatalEmulationError.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -73,9 +74,7 @@ M6502::M6502(const Settings& settings) myOnHaltCallback(nullptr), myHaltRequested(false), myGhostReadsTrap(true), - myStepStateByInstruction(false), - myReadFromWritePortBreak(false), - myReadFromWritePortAddr(0) + myStepStateByInstruction(false) { #ifdef DEBUGGER_SUPPORT myDebugger = nullptr; @@ -122,7 +121,6 @@ void M6502::reset() myHaltRequested = false; myGhostReadsTrap = mySettings.getBool("dbg.ghostreadstrap"); - myReadFromWritePortBreak = mySettings.getBool(devSettings ? "dev.rwportbreak" : "plr.rwportbreak"); myLastBreakCycle = ULLONG_MAX; } @@ -285,31 +283,19 @@ inline void M6502::_execute(uInt64 cycles, DispatchResult& result) int cond = evalCondBreaks(); if(cond > -1) { - stringstream msg; + ostringstream msg; msg << "CBP[" << Common::Base::HEX2 << cond << "]: " << myCondBreakNames[cond]; myLastBreakCycle = mySystem->cycles(); result.setDebugger(currentCycles, msg.str()); return; } - - int rwAddr = readFromWritePort(); - if(rwAddr) - { - stringstream msg; - msg << "RWP[@ $" << Common::Base::HEX4 << rwAddr << "]: "; - - myLastBreakCycle = mySystem->cycles(); - - result.setDebugger(currentCycles, msg.str(), PC); - return; - } } int cond = evalCondSaveStates(); if(cond > -1) { - stringstream msg; + ostringstream msg; msg << "conditional savestate [" << Common::Base::HEX2 << cond << "]"; myDebugger->addState(msg.str()); } @@ -336,9 +322,12 @@ inline void M6502::_execute(uInt64 cycles, DispatchResult& result) default: FatalEmulationError::raise("invalid instruction"); } - } catch (FatalEmulationError& e) { + } catch (const FatalEmulationError& e) { myExecutionStatus |= FatalErrorBit; result.setMessage(e.what()); + } catch (const EmulationWarning& e) { + result.setDebugger(currentCycles, e.what(), PC); + return; } currentCycles = (mySystem->cycles() - previousCycles); @@ -363,7 +352,7 @@ inline void M6502::_execute(uInt64 cycles, DispatchResult& result) interruptHandler(); } - // See if a fatal error has occured + // See if a fatal error has occurred if(myExecutionStatus & FatalErrorBit) { // Yes, so answer that something when wrong. The message has already been set when @@ -452,8 +441,6 @@ bool M6502::save(Serializer& out) const out.putBool(myHaltRequested); out.putBool(myStepStateByInstruction); out.putBool(myGhostReadsTrap); - out.putBool(myReadFromWritePortBreak); - out.putInt(myReadFromWritePortAddr); out.putLong(myLastBreakCycle); } catch(...) @@ -502,8 +489,6 @@ bool M6502::load(Serializer& in) myHaltRequested = in.getBool(); myStepStateByInstruction = in.getBool(); myGhostReadsTrap = in.getBool(); - myReadFromWritePortBreak = in.getBool(); - myReadFromWritePortAddr = in.getInt(); myLastBreakCycle = in.getLong(); } catch(...) @@ -652,22 +637,4 @@ void M6502::updateStepStateByInstruction() myStepStateByInstruction = myCondBreaks.size() || myCondSaveStates.size() || myTrapConds.size(); } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int M6502::readFromWritePort() -{ - uInt16 addr = myReadFromWritePortAddr; - myReadFromWritePortAddr = 0; - - // A read from the write port occurs when the read is actually in the write - // port address space AND the last access was actually a read (the latter - // differentiates between reads that are normally part of a write cycle vs. - // ones that are illegal) - if(myReadFromWritePortBreak && lastReadAddress() && - (mySystem->getPageAccessType(addr) & System::PA_WRITE) == System::PA_WRITE) - return addr; - else - return 0; -} - #endif // DEBUGGER_SUPPORT diff --git a/src/emucore/M6502.hxx b/src/emucore/M6502.hxx index 856f7f3c9..5be6c1fcd 100644 --- a/src/emucore/M6502.hxx +++ b/src/emucore/M6502.hxx @@ -242,9 +242,6 @@ class M6502 : public Serializable const StringList& getCondTrapNames() const; void setGhostReadsTrap(bool enable) { myGhostReadsTrap = enable; } - void setReadFromWritePortBreak(bool enable) { myReadFromWritePortBreak = enable; } - void setReadFromWritePort(uInt16 addr) { myReadFromWritePortAddr = addr; }; - int readFromWritePort(); #endif // DEBUGGER_SUPPORT private: @@ -464,8 +461,6 @@ class M6502 : public Serializable // in save states, they cannot be conditionally compiled bool myGhostReadsTrap; // trap on ghost reads bool myStepStateByInstruction; - bool myReadFromWritePortBreak; - uInt16 myReadFromWritePortAddr; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/exception/FatalEmulationError.cxx b/src/emucore/exception/FatalEmulationError.cxx deleted file mode 100644 index 8646444ac..000000000 --- a/src/emucore/exception/FatalEmulationError.cxx +++ /dev/null @@ -1,36 +0,0 @@ -//============================================================================ -// -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa -// SSSS tt ee ee ll ll aa -// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" -// SS SS tt ee ll ll aa aa -// SSSS ttt eeeee llll llll aaaaa -// -// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony -// and the Stella Team -// -// See the file "License.txt" for information on usage and redistribution of -// this file, and for a DISCLAIMER OF ALL WARRANTIES. -//============================================================================ - -#include "FatalEmulationError.hxx" - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FatalEmulationError::FatalEmulationError(const string& message) - : myMessage(message) -{ -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const char* FatalEmulationError::what() const noexcept -{ - return myMessage.c_str(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FatalEmulationError::raise(const string& message) -{ - throw FatalEmulationError(message); -} diff --git a/src/emucore/exception/FatalEmulationError.hxx b/src/emucore/exception/FatalEmulationError.hxx index 25c3af13b..c340787a1 100644 --- a/src/emucore/exception/FatalEmulationError.hxx +++ b/src/emucore/exception/FatalEmulationError.hxx @@ -20,20 +20,19 @@ #include "bspf.hxx" -class FatalEmulationError: public std::exception { - +class FatalEmulationError : public std::exception +{ public: + FatalEmulationError(const string& message) : myMessage(message) { } - FatalEmulationError(const string& message); + const char* what() const noexcept override { return myMessage.c_str(); } - virtual const char* what() const noexcept; - - [[noreturn]] static void raise(const string& message); + [[noreturn]] static void raise(const string& message) { + throw FatalEmulationError(message); + } private: - const string myMessage; - }; #endif // FATAL_EMULATION_ERROR_HXX diff --git a/src/emucore/module.mk b/src/emucore/module.mk index 3ea4ba691..778b747b1 100644 --- a/src/emucore/module.mk +++ b/src/emucore/module.mk @@ -80,8 +80,7 @@ MODULE_OBJS := \ src/emucore/Switches.o \ src/emucore/System.o \ src/emucore/TIASurface.o \ - src/emucore/Thumbulator.o \ - src/emucore/exception/FatalEmulationError.o + src/emucore/Thumbulator.o MODULE_DIRS += \ src/emucore diff --git a/src/gui/DeveloperDialog.cxx b/src/gui/DeveloperDialog.cxx index 13555fb53..b5b83a729 100644 --- a/src/gui/DeveloperDialog.cxx +++ b/src/gui/DeveloperDialog.cxx @@ -34,6 +34,8 @@ #include "DebuggerDialog.hxx" #endif #include "Console.hxx" +#include "Debugger.hxx" +#include "CartDebug.hxx" #include "TIA.hxx" #include "OSystem.hxx" #include "StateManager.hxx" @@ -695,9 +697,11 @@ void DeveloperDialog::saveConfig() saveSettings(SettingsSet::player); saveSettings(SettingsSet::developer); +#ifdef DEBUGGER_SUPPORT // Read from write ports break if(instance().hasConsole()) - instance().console().system().m6502().setReadFromWritePortBreak(myRWPortBreakWidget->getState()); + Debugger::debugger().cartDebug().setReadFromWritePortBreak(myRWPortBreakWidget->getState()); +#endif // activate the current settings instance().frameBuffer().showFrameStats(myFrameStatsWidget->getState());