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:
Stephen Anthony 2018-12-07 21:19:44 -03:30
parent 6a9ae2e3a1
commit abfc01b483
8 changed files with 45 additions and 99 deletions

View File

@ -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());
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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());