mirror of https://github.com/stella-emu/stella.git
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.
This commit is contained in:
parent
6a9ae2e3a1
commit
abfc01b483
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue