Refactoring: start debugger from dispatch loop.

This commit is contained in:
Christian Speckner 2018-05-24 00:13:43 +02:00
parent ae0faaabfc
commit a14cf8d077
11 changed files with 243 additions and 70 deletions

View File

@ -0,0 +1,55 @@
//============================================================================
//
// MM MM 6666 555555 0000 2222
// MMMM MMMM 66 66 55 00 00 22 22
// MM MMM MM 66 55 00 00 22
// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator"
// MM MM 66 66 55 00 00 22
// MM MM 66 66 55 55 00 00 22
// MM MM 6666 5555 0000 222222
//
// 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 "DispatchResult.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DispatchResult::assertStatus(Status status) const
{
if (myStatus != status) throw runtime_error("invalid status for operation");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool DispatchResult::isSuccess() const
{
return myStatus == Status::debugger || myStatus == Status::ok;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DispatchResult::setOk(uInt32 cycles)
{
myStatus = Status::ok;
myCycles = cycles;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DispatchResult::setDebugger(uInt32 cycles, const string& message, int address, bool wasReadTrap)
{
myStatus = Status::debugger;
myCycles = cycles;
myMessage = message;
myAddress = address;
myWasReadTrap = wasReadTrap;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DispatchResult::setFatal(uInt32 cycles)
{
myCycles = cycles;
myStatus = Status::fatal;
}

View File

@ -0,0 +1,67 @@
//============================================================================
//
// MM MM 6666 555555 0000 2222
// MMMM MMMM 66 66 55 00 00 22 22
// MM MMM MM 66 55 00 00 22
// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator"
// MM MM 66 66 55 00 00 22
// MM MM 66 66 55 55 00 00 22
// MM MM 6666 5555 0000 222222
//
// 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.
//============================================================================
#ifndef DISPATCH_RESULT_HXX
#define DISPATCH_RESULT_HXX
#include "bspf.hxx"
class DispatchResult
{
public:
enum class Status { invalid, ok, debugger, fatal };
public:
DispatchResult() : myStatus(Status::invalid) {}
Status getStatus() const { return myStatus; }
uInt32 getCycles() const { return myCycles; }
const string& getMessage() const { assertStatus(Status::debugger); return myMessage; }
uInt16 getAddress() const { assertStatus(Status::debugger); return myAddress; }
bool wasReadTrap() const { assertStatus(Status::debugger); return myWasReadTrap; }
bool isSuccess() const;
void setOk(uInt32 cycles);
void setDebugger(uInt32 cycles, const string& message = "", int address = -1, bool wasReadTrap = -1);
void setFatal(uInt32 cycles);
private:
void assertStatus(Status status) const;
private:
Status myStatus;
uInt32 myCycles;
string myMessage;
int myAddress;
bool myWasReadTrap;
};
#endif // DISPATCH_RESULT_HXX

View File

@ -268,17 +268,8 @@ void FrameBuffer::update()
switch(myOSystem.eventHandler().state())
{
case EventHandlerState::EMULATION:
{
myTIASurface->render();
// Show frame statistics
if(myStatsMsg.enabled)
drawFrameStats();
myLastScanlines = myOSystem.console().tia().scanlinesLastFrame();
myPausedCount = 0;
break; // EventHandlerState::EMULATION
}
// Do nothing; emulation mode is handled separately (see below)
break;
case EventHandlerState::PAUSE:
{
@ -340,6 +331,30 @@ void FrameBuffer::update()
postFrameUpdate();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::updateInEmulationMode()
{
// Determine which mode we are in (from the EventHandler)
// Take care of S_EMULATE mode here, otherwise let the GUI
// figure out what to draw
myTIASurface->render();
// Show frame statistics
if(myStatsMsg.enabled)
drawFrameStats();
myLastScanlines = myOSystem.console().tia().scanlinesLastFrame();
myPausedCount = 0;
// Draw any pending messages
if(myMsg.enabled)
drawMessage();
// Do any post-frame stuff
postFrameUpdate();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::showMessage(const string& message, MessagePosition position,
bool force)

View File

@ -118,6 +118,11 @@ class FrameBuffer
*/
void update();
/**
There is a dedicated update method for emulation mode.
*/
void updateInEmulationMode();
/**
Shows a message onscreen.

View File

@ -47,6 +47,7 @@
#include "System.hxx"
#include "M6502.hxx"
#include "DispatchResult.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
M6502::M6502(const Settings& settings)
@ -208,9 +209,9 @@ inline void M6502::handleHalt()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool M6502::execute(uInt32 number)
void M6502::execute(uInt32 number, DispatchResult& result)
{
const bool status = _execute(number);
_execute(number, result);
#ifdef DEBUGGER_SUPPORT
// Debugger hack: this ensures that stepping a "STA WSYNC" will actually end at the
@ -225,12 +226,20 @@ bool M6502::execute(uInt32 number)
// that audio samples are generated for the whole timeslice.
mySystem->tia().updateEmulation();
mySystem->m6532().updateEmulation();
return status;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline bool M6502::_execute(uInt32 cycles)
bool M6502::execute(uInt32 number)
{
DispatchResult result;
_execute(number, result);
return result.isSuccess();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline void M6502::_execute(uInt32 cycles, DispatchResult& result)
{
// Clear all of the execution status bits except for the fatal error bit
myExecutionStatus &= FatalErrorBit;
@ -241,7 +250,7 @@ inline bool M6502::_execute(uInt32 cycles)
#endif
uInt64 previousCycles = mySystem->cycles();
uInt64 currentCycles = 0;
uInt32 currentCycles = 0;
// Loop until execution is stopped or a fatal error occurs
for(;;)
@ -254,18 +263,23 @@ inline bool M6502::_execute(uInt32 cycles)
bool read = myJustHitReadTrapFlag;
myJustHitReadTrapFlag = myJustHitWriteTrapFlag = false;
if (startDebugger(myHitTrapInfo.message, myHitTrapInfo.address, read)) return true;
result.setDebugger(currentCycles, myHitTrapInfo.message, myHitTrapInfo.address, read);
return;
}
if(myBreakPoints.isInitialized() && myBreakPoints.isSet(PC) && startDebugger("BP: ", PC))
return true;
if(myBreakPoints.isInitialized() && myBreakPoints.isSet(PC)) {
result.setDebugger(currentCycles, "BP: ", PC);
return;
}
int cond = evalCondBreaks();
if(cond > -1)
{
stringstream msg;
msg << "CBP[" << Common::Base::HEX2 << cond << "]: " << myCondBreakNames[cond];
if (startDebugger(msg.str())) return true;
result.setDebugger(currentCycles, msg.str());
return;
}
cond = evalCondSaveStates();
@ -324,21 +338,24 @@ inline bool M6502::_execute(uInt32 cycles)
if(myExecutionStatus & StopExecutionBit)
{
// Yes, so answer that everything finished fine
return true;
result.setOk(currentCycles);
return;
}
// See if a fatal error has occured
if(myExecutionStatus & FatalErrorBit)
{
// Yes, so answer that something when wrong
return false;
result.setFatal(currentCycles + icycles);
return;
}
// See if we've executed the specified number of instructions
if (currentCycles >= cycles * SYSTEM_CYCLES_PER_CPU)
{
// Yes, so answer that everything finished fine
return true;
result.setOk(currentCycles);
return;
}
}
}
@ -612,15 +629,4 @@ void M6502::updateStepStateByInstruction()
myTrapConds.size();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool M6502::startDebugger(const string& message, int address, bool read)
{
handleHalt();
mySystem->tia().updateEmulation();
mySystem->m6532().updateEmulation();
return myDebugger->start(message, address, read);
}
#endif // DEBUGGER_SUPPORT

View File

@ -22,6 +22,7 @@
class Settings;
class System;
class DispatchResult;
#ifdef DEBUGGER_SUPPORT
class Debugger;
@ -113,8 +114,10 @@ class M6502 : public Serializable
@param cycles Indicates the number of cycles to execute. Not that the actual
granularity of the CPU is instructions, so this is only accurate up to
a couple of cycles
@return true iff execution stops normally
@param result A DispatchResult object that will transport the result
*/
void execute(uInt32 cycles, DispatchResult& result);
bool execute(uInt32 cycles);
/**
@ -320,7 +323,7 @@ class M6502 : public Serializable
This is the actual dispatch function that does the grunt work. M6502::execute
wraps it and makes sure that any pending halt is processed before returning.
*/
bool _execute(uInt32 cycles);
void _execute(uInt32 cycles, DispatchResult& result);
#ifdef DEBUGGER_SUPPORT
/**
@ -328,12 +331,6 @@ class M6502 : public Serializable
with the CPU and update the flag accordingly.
*/
void updateStepStateByInstruction();
/**
Make sure that the current hardware state is up to date (TIA & RIOT) and dispatch
debugger.
*/
bool startDebugger(const string& message = "", int address = -1, bool read = true);
#endif // DEBUGGER_SUPPORT
private:

View File

@ -53,6 +53,7 @@
#include "StateManager.hxx"
#include "Version.hxx"
#include "TIA.hxx"
#include "DispatchResult.hxx"
#include "OSystem.hxx"
@ -635,6 +636,30 @@ float OSystem::frameRate() const
return myConsole ? myConsole->getFramerate() : 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double OSystem::dispatchEmulation(uInt32 cyclesPerSecond)
{
if (!myConsole) return 0.;
Int64 totalCycles = 0;
const Int64 minCycles = myConsole->emulationTiming().minCyclesPerTimeslice();
const Int64 maxCycles = myConsole->emulationTiming().maxCyclesPerTimeslice();
DispatchResult dispatchResult;
do {
myConsole->tia().update(dispatchResult, totalCycles > 0 ? minCycles - totalCycles : maxCycles);
totalCycles += dispatchResult.getCycles();
} while (totalCycles < minCycles && dispatchResult.getStatus() == DispatchResult::Status::ok);
if (dispatchResult.getStatus() == DispatchResult::Status::debugger) myDebugger->start();
if (dispatchResult.getStatus() == DispatchResult::Status::ok && myEventHandler->frying())
myConsole->fry();
return static_cast<double>(totalCycles) / static_cast<double>(cyclesPerSecond);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void OSystem::mainLoop()
{
@ -650,40 +675,29 @@ void OSystem::mainLoop()
double timesliceSeconds;
if (myEventHandler->state() == EventHandlerState::EMULATION) {
Int64 totalCycles = 0;
const Int64 minCycles = myConsole ? myConsole->emulationTiming().minCyclesPerTimeslice() : 50000;
const Int64 maxCycles = myConsole ? myConsole->emulationTiming().maxCyclesPerTimeslice() : 0;
const uInt32 cyclesPerSecond = myConsole ? myConsole->emulationTiming().cyclesPerSecond() : 1;
timesliceSeconds = dispatchEmulation(myConsole ? myConsole->emulationTiming().cyclesPerSecond() : 1);
do {
Int64 cycles = myConsole ? myConsole->tia().update(totalCycles > 0 ? minCycles - totalCycles : maxCycles) : 0;
totalCycles += cycles;
} while (myConsole && totalCycles < minCycles && myEventHandler->state() == EventHandlerState::EMULATION);
if (myEventHandler->state() == EventHandlerState::EMULATION && myEventHandler->frying())
myConsole->fry();
timesliceSeconds = static_cast<double>(totalCycles) / static_cast<double>(cyclesPerSecond);
}
else
timesliceSeconds = 1. / 30.;
if (myEventHandler->state() == EventHandlerState::EMULATION) {
if (myConsole && myConsole->tia().newFramePending()) {
myFrameBuffer->update();
myFrameBuffer->updateInEmulationMode();
myConsole->tia().clearNewFramePending();
}
}
else
} else {
timesliceSeconds = 1. / 30.;
myFrameBuffer->update();
}
duration<double> timeslice(timesliceSeconds);
virtualTime += duration_cast<high_resolution_clock::duration>(timeslice);
time_point<high_resolution_clock> now = high_resolution_clock::now();
double maxLag = myConsole
? (
static_cast<double>(myConsole->emulationTiming().cyclesPerFrame()) /
static_cast<double>(myConsole->emulationTiming().cyclesPerSecond())
)
: 0;
if (duration_cast<duration<double>>(now - virtualTime).count() > 0)
if (duration_cast<duration<double>>(now - virtualTime).count() > maxLag)
virtualTime = now;
else if (virtualTime > now) {
if (busyWait && myEventHandler->state() == EventHandlerState::EMULATION) {

View File

@ -563,6 +563,8 @@ class OSystem
void validatePath(string& path, const string& setting,
const string& defaultpath);
double dispatchEmulation(uInt32 cyclesPerSecond);
// Following constructors and assignment operators not supported
OSystem(const OSystem&) = delete;
OSystem(OSystem&&) = delete;

View File

@ -51,6 +51,7 @@ MODULE_OBJS := \
src/emucore/CompuMate.o \
src/emucore/Console.o \
src/emucore/Control.o \
src/emucore/DispatchResult.o \
src/emucore/Driving.o \
src/emucore/EventHandler.o \
src/emucore/EmulationTiming.o \

View File

@ -24,6 +24,7 @@
#include "TIAConstants.hxx"
#include "frame-manager/FrameManager.hxx"
#include "AudioQueue.hxx"
#include "DispatchResult.hxx"
#ifdef DEBUGGER_SUPPORT
#include "CartDebug.hxx"
@ -814,14 +815,21 @@ bool TIA::loadDisplay(Serializer& in)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt64 TIA::update(uInt32 maxCycles)
void TIA::update(DispatchResult& result, uInt32 maxCycles)
{
uInt64 timestampOld = myTimestamp;
mySystem->m6502().execute(maxCycles);
mySystem->m6502().execute(maxCycles, result);
updateEmulation();
return (myTimestamp - timestampOld) / 3;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::update(uInt32 maxCycles)
{
DispatchResult dispatchResult;
update(dispatchResult, maxCycles);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -41,6 +41,7 @@
#include "System.hxx"
class AudioQueue;
class DispatchResult;
/**
This class is a device that emulates the Television Interface Adaptor
@ -199,7 +200,9 @@ class TIA : public Device
desired frame rate to update the TIA. Invoking this method will update
the graphics buffer and generate the corresponding audio samples.
*/
uInt64 update(uInt32 maxCycles = 50000);
void update(DispatchResult& result, uInt32 maxCycles = 50000);
void update(uInt32 maxCycles = 50000);
/**
Did we generate a new frame?