Merge branch 'master' into refactoring/frame_manager

This commit is contained in:
Christian Speckner 2017-10-15 20:28:27 +02:00
commit be877f8503
193 changed files with 3805 additions and 1748 deletions

View File

@ -46,6 +46,10 @@
sometimes the image was 'double-blended', resulting in a snapshot that
was too dark.
* Fixed crash when selecting 'CompuMate' as a controller type for a non-
CompuMate ROM; this controller type can no longer be manually
selected, and will be used automatically used for CompuMate ROMs.
* Added debugger pseudo-register '_fcycles', which gives the number of
CPU cycles that have occurred since the frame started.
@ -2676,7 +2680,7 @@
* Fixed crash when adding one-shot cheats.
* Fixed bug in RAM comparitive searches in the debugger.
* Fixed bug in RAM comparative searches in the debugger.
* Fixed bug with setting snapshot naming type from the GUI (it was
always being set to 'romname').

View File

@ -53,6 +53,13 @@ CXXFLAGS+= -Wall -Wextra -Wno-unused-parameter -Wno-ignored-qualifiers
ifdef HAVE_GCC
CXXFLAGS+= -Wno-multichar -Wunused -fno-rtti -Woverloaded-virtual -Wnon-virtual-dtor -std=c++14
endif
ifdef HAVE_CLANG
CXXFLAGS+= -Weverything -Wno-c++17-extensions -Wno-c++98-compat -Wno-c++98-compat-pedantic \
-Wno-double-promotion -Wno-switch-enum -Wno-conversion \
-Wno-inconsistent-missing-destructor-override \
-Wno-exit-time-destructors -Wno-global-constructors -Wno-weak-vtables \
-Wno-four-char-constants -Wno-padded
endif
ifdef PROFILE
PROF:= -g -pg -fprofile-arcs -ftest-coverage
@ -63,10 +70,6 @@ else
endif
endif
# Even more warnings...
#CXXFLAGS+= -pedantic -Wpointer-arith -Wcast-qual -Wconversion
#CXXFLAGS+= -Wshadow -Wimplicit -Wundef -Wnon-virtual-dtor
#CXXFLAGS+= -Wno-reorder -Wwrite-strings -fcheck-new -Wctor-dtor-privacy
#######################################################################
# Misc stuff - you should never have to edit this #

45
configure vendored
View File

@ -386,14 +386,16 @@ cc_check_define __GNUC__ && have_gcc=yes
if test "$have_clang" = yes; then
clang_minor=$( $CXX -dM -E -x c /dev/null | grep __clang_minor__ | sed 's/.*\([0-9][0-9]*\).*/\1/' )
clang_patch=$( $CXX -dM -E -x c /dev/null | grep __clang_patchlevel__ | sed 's/.*\([0-9][0-9]*\).*/\1/' )
clang_major=$( $CXX -dM -E -x c /dev/null | grep __clang_major__ | sed 's/.*\([0-9][0-9]*\).*/\1/' )
cxx_version="$clang_major.$clang_minor.$clang_patch"
is_xcode=$( $CXX -dM -E -x c /dev/null | grep __apple_build_version__ )
# Need at least version 8
if test -n "$is_xcode"; then
clang_minor=$( $CXX -dM -E -x c /dev/null | grep __clang_minor__ | sed 's/.*\([0-9][0-9]*\).*/\1/' )
clang_patch=$( $CXX -dM -E -x c /dev/null | grep __clang_patchlevel__ | sed 's/.*\([0-9][0-9]*\).*/\1/' )
clang_major=$( $CXX -dM -E -x c /dev/null | grep __clang_major__ | sed 's/.*\([0-9][0-9]*\).*/\1/' )
cxx_version="$clang_major.$clang_minor.$clang_patch"
cxx_name="XCode $cxx_version"
if test $clang_major -ge 8; then
@ -401,35 +403,23 @@ if test "$have_clang" = yes; then
cxx_verc_fail=no
else
cxx_version="$cxx_version, bad"
cxx_verc_fail=bad
cxx_verc_fail=yes
fi
else
cxx_name=`( $cc -v ) 2>&1 | tail -n 1 | cut -d ' ' -f 1`
cxx_version=$( $CXX -dM -E -x c /dev/null | grep __clang_version__ | sed 's/^.*[^0-9]\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*$/\1/g' 2>&1)
if test "$?" -gt 0; then
cxx_version="not found"
# Need at least version 3.5
if [ $clang_major -ge 4 ] || [ $clang_major -eq 3 -a $clang_minor -ge 5 ]; then
cxx_version="$cxx_version, ok"
cxx_verc_fail=no
else
cxx_version="$cxx_version, bad"
cxx_verc_fail=yes
fi
case $cxx_version in
[3].[4-9]|[3].[4-9].[0-9]|[3].[4-9].[0-9][-.]*|[4-9].[0-9].[0-9])
_cxx_major=`echo $cxx_version | cut -d '.' -f 1`
_cxx_minor=`echo $cxx_version | cut -d '.' -f 2`
cxx_version="$cxx_version, ok"
cxx_verc_fail=no
;;
'not found')
cxx_verc_fail=yes
;;
*)
cxx_version="$cxx_version, bad"
cxx_verc_fail=yes
;;
esac
fi
CXXFLAGS="$CXXFLAGS"
_make_def_HAVE_GCC3='HAVE_GCC3 = 1'
add_line_to_config_mk 'CXX_UPDATE_DEP_FLAG = -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" -MQ "$@" -MP'
_make_def_HAVE_GCC='HAVE_GCC = 1'
_make_def_HAVE_CLANG='HAVE_CLANG = 1'
echo "$cxx_version"
elif test "$have_gcc" = yes; then
@ -505,7 +495,7 @@ fi
if test "$cxx_verc_fail" = yes ; then
echo
echo "The version of your compiler is not supported at this time"
echo "Please ensure you are using GCC 4.7 / Clang 3.4 or above"
echo "Please ensure you are using GCC 5.0 / Clang 3.8 or above"
exit 1
fi
@ -827,6 +817,7 @@ PROFILE := $_build_profile
$_make_def_HAVE_GCC
$_make_def_HAVE_GCC3
$_make_def_HAVE_CLANG
INCLUDES += $INCLUDES
OBJS += $OBJS

View File

@ -957,7 +957,7 @@ and the input is empty, all RAM locations are highlighted.</p>
<p>The 'Compare' button is used to compare the given value using all
addresses currently highlighted. This may be an absolute number (such as 2),
or a comparitive number (such as -1). Using a '+' or '-' operator
or a comparative number (such as -1). Using a '+' or '-' operator
means 'search addresses for values that have changed by that amount'.</p>
<p>The 'Reset' button resets the entire operation; it clears the highlighted
addresses and allows another search.</p>

View File

@ -57,13 +57,13 @@ CheatCodeDialog::CheatCodeDialog(OSystem& osystem, DialogContainer& parent,
xpos += myCheatList->getWidth() + 5; ypos = 15;
b = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
"Add", kAddCheatCmd);
"Add" + ELLIPSIS, kAddCheatCmd);
wid.push_back(b);
ypos += lineHeight + 10;
myEditButton =
new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
"Edit", kEditCheatCmd);
"Edit" + ELLIPSIS, kEditCheatCmd);
wid.push_back(myEditButton);
ypos += lineHeight + 10;
@ -74,7 +74,7 @@ CheatCodeDialog::CheatCodeDialog(OSystem& osystem, DialogContainer& parent,
ypos += lineHeight + 10;
b = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
"One shot", kAddOneShotCmd);
"One shot" + ELLIPSIS, kAddOneShotCmd);
wid.push_back(b);
// Inputbox which will pop up when adding/editing a cheat
@ -196,12 +196,12 @@ void CheatCodeDialog::handleCommand(CommandSender* sender, int cmd,
{
switch(cmd)
{
case kOKCmd:
case GuiObject::kOKCmd:
saveConfig();
close();
break;
case kCloseCmd:
case GuiObject::kCloseCmd:
close();
break;

View File

@ -71,6 +71,11 @@ class Base
os.flags(myHexflags);
return os << std::setw(2) << std::setfill('0');
}
static inline std::ostream& HEX3(std::ostream& os)
{
os.flags(myHexflags);
return os << std::setw(3) << std::setfill('0');
}
static inline std::ostream& HEX4(std::ostream& os) {
os.flags(myHexflags);
return os << std::setw(4) << std::setfill('0');
@ -93,6 +98,7 @@ class Base
// Format specifiers to use for sprintf (eventually we may convert
// to C++ streams
static ostringstream buf;
static const char* const myLowerFmt[4];
static const char* const myUpperFmt[4];
static const char* const* myFmt;

View File

@ -55,7 +55,7 @@ class FilesystemNodeFactory
return new FilesystemNodeZIP(path);
break;
}
return 0;
return nullptr;
}
private:

View File

@ -42,7 +42,6 @@ class FilesystemNodeZIP : public AbstractFSNode
* Creates a FilesystemNodeZIP for a given path.
*
* @param path String with the path the new node should point to.
* @param node Raw pointer to use for the internal FSNode
*/
FilesystemNodeZIP(const string& path);

View File

@ -0,0 +1,163 @@
//============================================================================
//
// 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-2017 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 LINKED_OBJECT_POOL_HXX
#define LINKED_OBJECT_POOL_HXX
#include <list>
#include "bspf.hxx"
/**
* A fixed-size object-pool based doubly-linked list that makes use of
* multiple STL lists, to reduce frequent (de)allocations.
*
* This structure can be used as either a stack or queue, but also allows
* for removal at any location in the list.
*
* There are two internal lists; one stores active nodes, and the other
* stores pool nodes that have been 'deleted' from the active list (note
* that no actual deletion takes place; nodes are simply moved from one list
* to another). Similarly, when a new node is added to the active list, it
* is simply moved from the pool list to the active list.
*
* NOTE: For efficiency reasons, none of the methods check for overflow or
* underflow; that is the responsibility of the caller.
*
* In the case of methods which wrap the C++ 'splice()' method, the
* semantics of splice are followed wrt invalid/out-of-range/etc
* iterators. See the applicable documentation for such behaviour.
*
* @author Stephen Anthony
*/
namespace Common {
template <class T, uInt32 CAPACITY = 100>
class LinkedObjectPool
{
public:
using iter = typename std::list<T>::iterator;
using const_iter = typename std::list<T>::const_iterator;
/*
* Create a pool of size CAPACITY; the active list starts out empty.
*/
LinkedObjectPool<T, CAPACITY>() {
for(uInt32 i = 0; i < CAPACITY; ++i)
myPool.emplace_back(T());
}
/**
* Return a reference to the element at the first node in the active list.
*/
T& first() { return myList.front(); }
/**
* Return a reference to the element at the last node in the active list.
*/
T& last() { return myList.back(); }
/**
* Add a new node at beginning of the active list, and return a reference
* to that nodes' data. The reference may then be modified; ie, you're
* able to change the data located at that node.
*/
T& addFirst() {
myList.splice(myList.begin(), myPool, myPool.begin());
return myList.front();
}
/**
* Add a new node at the end of the active list, and return a reference
* to that nodes' data. The reference may then be modified; ie, you're
* able to change the data located at that node.
*/
T& addLast() {
myList.splice(myList.end(), myPool, myPool.begin());
return myList.back();
}
/**
* Remove the first node of the active list.
*/
void removeFirst() {
myPool.splice(myPool.end(), myList, myList.begin());
}
/**
* Remove the last node of the active list.
*/
void removeLast() {
myPool.splice(myPool.end(), myList, std::prev(myList.end(), 1));
}
/**
* Convenience method to remove a single element from the active list at
* position of the iterator +- the offset.
*/
void remove(const_iter i, Int32 offset = 0) {
myPool.splice(myPool.end(), myList,
offset >= 0 ? std::next(i, offset) : std::prev(i, -offset));
}
/**
* Convenience method to remove a single element from the active list by
* index, offset from the beginning of the list. (ie, '0' means first
* element, '1' is second, and so on).
*/
void remove(uInt32 index) {
myPool.splice(myPool.end(), myList, std::next(myList.begin(), index));
}
/**
* Convenience method to remove a range of elements from 'index' to the
* end of the active list.
*/
void removeLast(uInt32 index) {
myPool.splice(myPool.end(), myList, std::next(myList.begin(), index), myList.end());
}
/**
* Erase entire contents of active list.
*/
void clear() {
myPool.splice(myPool.end(), myList, myList.begin(), myList.end());
}
/** Access the list with iterators, just as you would a normal C++ STL list */
iter begin() { return myList.begin(); }
iter end() { return myList.end(); }
const_iter begin() const { return myList.cbegin(); }
const_iter end() const { return myList.cend(); }
uInt32 size() const { return myList.size(); }
bool empty() const { return myList.size() == 0; }
bool full() const { return myList.size() >= CAPACITY; }
private:
std::list<T> myList, myPool;
private:
// Following constructors and assignment operators not supported
LinkedObjectPool(const LinkedObjectPool&) = delete;
LinkedObjectPool(LinkedObjectPool&&) = delete;
LinkedObjectPool& operator=(const LinkedObjectPool&) = delete;
LinkedObjectPool& operator=(LinkedObjectPool&&) = delete;
};
} // Namespace Common
#endif

View File

@ -108,7 +108,7 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface)
// Cleanup
done:
if(png_ptr)
png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : 0, 0);
png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : nullptr, nullptr);
if(err_message)
throw runtime_error(err_message);

View File

@ -46,9 +46,9 @@ class PNGLibrary
@param filename The filename to load the PNG image
@param surface The FBSurface into which to place the PNG data
@return On success, the FBSurface containing image data, otherwise a
runtime_error is thrown containing a more detailed
error message.
@post On success, the FBSurface containing image data, otherwise a
runtime_error is thrown containing a more detailed
error message.
*/
void loadImage(const string& filename, FBSurface& surface);
@ -60,9 +60,9 @@ class PNGLibrary
@param filename The filename to save the PNG image
@param comments The text comments to add to the PNG image
@return On success, the PNG file has been saved to 'filename',
otherwise a runtime_error is thrown containing a
more detailed error message.
@post On success, the PNG file has been saved to 'filename',
otherwise a runtime_error is thrown containing a
more detailed error message.
*/
void saveImage(const string& filename,
const VariantList& comments = EmptyVarList);
@ -75,9 +75,9 @@ class PNGLibrary
@param rect The area of the surface to use
@param comments The text comments to add to the PNG image
@return On success, the PNG file has been saved to 'filename',
otherwise a runtime_error is thrown containing a
more detailed error message.
@post On success, the PNG file has been saved to 'filename',
otherwise a runtime_error is thrown containing a
more detailed error message.
*/
void saveImage(const string& filename, const FBSurface& surface,
const GUI::Rect& rect = GUI::EmptyRect,
@ -140,8 +140,8 @@ class PNGLibrary
static void png_read_data(png_structp ctx, png_bytep area, png_size_t size);
static void png_write_data(png_structp ctx, png_bytep area, png_size_t size);
static void png_io_flush(png_structp ctx);
static void png_user_warn(png_structp ctx, png_const_charp str);
static void png_user_error(png_structp ctx, png_const_charp str);
[[noreturn]] static void png_user_warn(png_structp ctx, png_const_charp str);
[[noreturn]] static void png_user_error(png_structp ctx, png_const_charp str);
private:
// Following constructors and assignment operators not supported

View File

@ -15,6 +15,8 @@
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#include <cmath>
#include "OSystem.hxx"
#include "Serializer.hxx"
#include "StateManager.hxx"
@ -25,23 +27,30 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RewindManager::RewindManager(OSystem& system, StateManager& statemgr)
: myOSystem(system),
myStateManager(statemgr)
myStateManager(statemgr),
myIsNTSC(true) // TODO
// TODO: current is not valid
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::addState(const string& message)
{
RewindPtr state = make_unique<RewindState>(); // TODO: get this from object pool
Serializer& s = state->data;
// TODO: remove following (preceding???) (all invalid) states
// myStateList.removeToFront(); from current() + 1 (or - 1???)
if(myStateList.full())
compressStates();
RewindState& state = myStateList.addFirst();
// TODO: addFirst() must set current() to the just added element
Serializer& s = state.data;
s.reset(); // rewind Serializer internal buffers
if(myStateManager.saveState(s) && myOSystem.console().tia().saveDisplay(s))
{
state->message = "Rewind " + message;
// Add to the list TODO: should check against current size
myStateList.emplace_front(std::move(state));
state.message = message;
state.cycle = myOSystem.console().tia().cycles();
return true;
}
return false;
@ -50,21 +59,104 @@ bool RewindManager::addState(const string& message)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::rewindState()
{
if(myStateList.size() > 0)
if(!myStateList.empty())
{
RewindPtr state = std::move(myStateList.front());
myStateList.pop_front(); // TODO: add 'state' to object pool
Serializer& s = state->data;
// TODO: get state previous to the current state instead of first()
// RewindState& state = myStateList.current();
// myStateList.prev(); // moves current to the previous (older) element
RewindState& state = myStateList.first(); // TOOD: remove
Serializer& s = state.data;
string message = getMessage(state);
s.reset(); // rewind Serializer internal buffers
myStateManager.loadState(s);
myOSystem.console().tia().loadDisplay(s);
// Show message indicating the rewind state
myOSystem.frameBuffer().showMessage(state->message);
myOSystem.frameBuffer().showMessage(message);
// TODO: Do NOT remove state (TODO later somewhere else: stop emulation)
myStateList.removeFirst(); // TODO: delete this
return true;
}
else
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::unwindState()
{
if(!atFirst()) // or last???
{
// TODO: get state next to the current state
/*RewindState& state = myStateList.???()
Serializer& s = state.data;
string message = getMessage(state);
s.reset(); // rewind Serializer internal buffers
myStateManager.loadState(s);
myOSystem.console().tia().loadDisplay(s);
// Show message indicating the rewind state
myOSystem.frameBuffer().showMessage(message);*/
return true;
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RewindManager::compressStates()
{
myStateList.removeLast(); // remove the oldest state file
// TODO: add smart state removal
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RewindManager::getMessage(RewindState& state)
{
const Int64 NTSC_FREQ = 1193182;
const Int64 PAL_FREQ = 1182298;
Int64 diff = myOSystem.console().tia().cycles() - state.cycle,
freq = myIsNTSC ? NTSC_FREQ : PAL_FREQ,
diffUnit;
stringstream message;
string unit;
message << (diff >= 0 ? "Rewind" : "Unwind");
diff = abs(diff);
if(diff < 76 * 2)
{
unit = "cycle";
diffUnit = diff;
}
else if(diff < 76 * 262 * 2)
{
unit = "scanline";
diffUnit = diff / 76;
}
else if(diff < NTSC_FREQ * 2)
{
unit = "frame";
diffUnit = diff / (76 * 262);
}
else if(diff < NTSC_FREQ * 60 * 2)
{
unit = "second";
diffUnit = diff / NTSC_FREQ;
}
else
{
unit = "minute";
diffUnit = diff / (NTSC_FREQ * 60);
}
message << " " << diffUnit << " " << unit;
if(diffUnit != 1)
message << "s";
// add optional message (TODO: when smart removal works, we have to do something smart with this part too)
if(!state.message.empty())
message << " (" << state.message << ")";
return message.str();
}

View File

@ -21,17 +21,12 @@
class OSystem;
class StateManager;
#include <list>
#include "LinkedObjectPool.hxx"
#include "bspf.hxx"
/**
This class is used to save (and later 'rewind') system save states.
TODO: This will eventually be converted to use object pools
Currently, it uses a C++ doubly-linked list as a stack, with
add/remove happening at the front of the list
Also, the additions are currently unbounded
@author Stephen Anthony
*/
class RewindManager
@ -54,12 +49,19 @@ class RewindManager
*/
bool rewindState();
bool empty() const { return myStateList.size() == 0; }
/**
Unwind one level of the state list, and display the message associated
with that state.
*/
bool unwindState();
bool atLast() const { return myStateList.empty(); }
bool atFirst() const { return false; } // TODO
void clear() { myStateList.clear(); }
private:
// Maximum number of states to save
static constexpr uInt32 MAX_SIZE = 100; // FIXME: use this
static constexpr uInt32 MAX_SIZE = 100;
OSystem& myOSystem;
StateManager& myStateManager;
@ -67,10 +69,21 @@ class RewindManager
struct RewindState {
Serializer data;
string message;
uInt64 cycle;
// We do nothing on object instantiation or copy
RewindState() { }
RewindState(const RewindState&) { }
};
using RewindPtr = unique_ptr<RewindState>;
std::list<RewindPtr> myStateList;
Common::LinkedObjectPool<RewindState, MAX_SIZE> myStateList;
bool myIsNTSC;
void compressStates();
string getMessage(RewindState& state);
private:
// Following constructors and assignment operators not supported

View File

@ -18,13 +18,27 @@
#ifndef SDL_LIB_HXX
#define SDL_LIB_HXX
#include <SDL.h>
/*
* We can't control the quality of code from outside projects, so for now
* just disable warnings for it.
*/
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#include <SDL.h>
#pragma clang diagnostic pop
#else
#include <SDL.h>
#endif
/*
Seems to be needed for ppc64le, doesn't hurt other archs
Note that this is a problem in SDL2, which includes <altivec.h>
https://bugzilla.redhat.com/show_bug.cgi?id=1419452
*/
* Seems to be needed for ppc64le, doesn't hurt other archs
* Note that this is a problem in SDL2, which includes <altivec.h>
* https://bugzilla.redhat.com/show_bug.cgi?id=1419452
*/
#undef vector
#undef pixel
#undef bool

View File

@ -29,7 +29,7 @@
#include "StateManager.hxx"
#define STATE_HEADER "05000302state"
#define MOVIE_HEADER "03030000movie"
// #define MOVIE_HEADER "03030000movie"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StateManager::StateManager(OSystem& osystem)
@ -134,6 +134,22 @@ void StateManager::toggleRewindMode()
myOSystem.frameBuffer().showMessage("Continuous rewind disabled");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool StateManager::rewindState()
{
RewindManager& r = myOSystem.state().rewindManager();
// TODO: add parameter to indicate rewinding from within emulation
return r.rewindState();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool StateManager::unwindState()
{
RewindManager& r = myOSystem.state().rewindManager();
// TODO: add parameter to indicate unwinding from within emulation
return r.unwindState();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StateManager::update()
{

View File

@ -64,6 +64,16 @@ class StateManager
*/
void toggleRewindMode();
/**
Rewinds one state; this uses the RewindManager for its functionality.
*/
bool rewindState();
/**
Unwinds one state; this uses the RewindManager for its functionality.
*/
bool unwindState();
/**
Updates the state of the system based on the currently active mode.
*/

View File

@ -50,7 +50,7 @@ class StringParser
@param str The string to split
@param maxlen The maximum length of string to generate
*/
StringParser(const string& str, uInt16 maxlen)
StringParser(const string& str, uInt32 maxlen)
{
istringstream buf(str);
string line;

View File

@ -168,7 +168,6 @@ bool ZipHandler::stream_read(fstream* stream, void* buffer, uInt64 offset,
{
return false;
}
return false;
}

View File

@ -22,7 +22,7 @@
This file defines various basic data types and preprocessor variables
that need to be defined for different operating systems.
@author Bradford W. Mott
@author Bradford W. Mott and Stephen Anthony
*/
#include <cstdint>
@ -77,6 +77,7 @@ using std::memcpy;
using IntArray = std::vector<Int32>;
using BoolArray = std::vector<bool>;
using ByteArray = std::vector<uInt8>;
using ShortArray = std::vector<uInt16>;
using StringList = std::vector<std::string>;
using BytePtr = std::unique_ptr<uInt8[]>;
@ -87,12 +88,14 @@ namespace BSPF
// Defines to help with path handling
#if defined(BSPF_UNIX) || defined(BSPF_MAC_OSX)
static const string PATH_SEPARATOR = "/";
#define ATTRIBUTE_FMT_PRINTF __attribute__((__format__ (__printf__, 2, 0)))
#elif defined(BSPF_WINDOWS)
static const string PATH_SEPARATOR = "\\";
#pragma warning (disable : 4146) // unary minus operator applied to unsigned type
#pragma warning(2:4264) // no override available for virtual member function from base 'class'; function is hidden
#pragma warning(2:4265) // class has virtual functions, but destructor is not virtual
#pragma warning(2:4266) // no override available for virtual member function from base 'type'; function is hidden
#define ATTRIBUTE_FMT_PRINTF
#else
#error Update src/common/bspf.hxx for path separator
#endif
@ -164,13 +167,13 @@ namespace BSPF
// Find location (if any) of the second string within the first,
// starting from 'startpos' in the first string
inline size_t findIgnoreCase(const string& s1, const string& s2, int startpos = 0)
inline size_t findIgnoreCase(const string& s1, const string& s2, size_t startpos = 0)
{
auto pos = std::search(s1.cbegin()+startpos, s1.cend(),
s2.cbegin(), s2.cend(), [](char ch1, char ch2) {
return toupper(uInt8(ch1)) == toupper(uInt8(ch2));
});
return pos == s1.cend() ? string::npos : size_t(pos - (s1.cbegin()+startpos));
return pos == s1.cend() ? string::npos : pos - (s1.cbegin()+startpos);
}
// Test whether the first string ends with the second one (case insensitive)
@ -189,6 +192,26 @@ namespace BSPF
{
return findIgnoreCase(s1, s2) != string::npos;
}
// Test whether the first string matches the second one (case insensitive)
// - the first character must match
// - the following characters must appear in the order of the first string
inline bool matches(const string& s1, const string& s2)
{
if(BSPF::startsWithIgnoreCase(s1, s2.substr(0, 1)))
{
size_t pos = 1;
for(uInt32 j = 1; j < s2.size(); j++)
{
size_t found = BSPF::findIgnoreCase(s1, s2.substr(j, 1), pos);
if(found == string::npos)
return false;
pos += found + 1;
}
return true;
}
return false;
}
} // namespace BSPF
#endif

View File

@ -38,18 +38,6 @@
#include "CheatManager.hxx"
#endif
// Pointer to the main parent osystem object or the null pointer
unique_ptr<OSystem> theOSystem;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Does general Cleanup in case any operation failed (or at end of program)
int Cleanup()
{
theOSystem->logMessage("Cleanup from main", 2);
theOSystem->saveConfig();
return 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#if defined(BSPF_MAC_OSX)
@ -61,10 +49,17 @@ int main(int argc, char* argv[])
std::ios_base::sync_with_stdio(false);
// Create the parent OSystem object
theOSystem = MediaFactory::createOSystem();
unique_ptr<OSystem> theOSystem = MediaFactory::createOSystem();
theOSystem->loadConfig();
theOSystem->logMessage("Loading config options ...", 2);
auto Cleanup = [&theOSystem]() {
theOSystem->logMessage("Cleanup from main", 2);
theOSystem->saveConfig();
return 0;
};
// Take care of commandline arguments
theOSystem->logMessage("Loading commandline arguments ...", 2);
string romfile = theOSystem->settings().loadCommandLine(argc, argv);

View File

@ -83,4 +83,4 @@ inline uInt8 smartmod<256>(uInt8 x)
return x & 0xFF;
}
#endif // SMARTMOD_HXX
#endif // SMARTMOD_HXX

View File

@ -56,12 +56,12 @@ void AtariNTSC::initializePalette(const uInt8* palette)
uInt32* kernel = myColorTable[entry];
genKernel(myImpl, y, i, q, kernel);
for ( uInt32 i = 0; i < rgb_kernel_size / 2; i++ )
for ( uInt32 c = 0; c < rgb_kernel_size / 2; ++c )
{
uInt32 error = rgb -
kernel [i ] - kernel [(i+10)%14+14] -
kernel [i + 7] - kernel [i + 3 +14];
kernel [i + 3 + 14] += error;
kernel [c ] - kernel [(c+10)%14+14] -
kernel [c + 7] - kernel [c + 3 +14];
kernel [c + 3 + 14] += error;
}
}
}

View File

@ -123,7 +123,7 @@ class AtariNTSC
Used to calculate an averaged color for the 'phosphor' effect.
@param c RGB Color 1 (current frame)
@param p RGB Color 2 (previous frame)
@param cp RGB Color 2 (previous frame)
@return Averaged value of the two RGB colors
*/

View File

@ -58,7 +58,7 @@ CartDebug::CartDebug(Debugger& dbg, Console& console, const OSystem& osystem)
mySystemAddresses = LabelToAddr(sysCmp);
// Add Zero-page RAM addresses
for(uInt32 i = 0x80; i <= 0xFF; ++i)
for(uInt16 i = 0x80; i <= 0xFF; ++i)
{
myState.rport.push_back(i);
myState.wport.push_back(i);
@ -75,16 +75,17 @@ CartDebug::CartDebug(Debugger& dbg, Console& console, const OSystem& osystem)
BankInfo info;
info.size = std::min(banksize, 4096u);
for(int i = 0; i < myConsole.cartridge().bankCount(); ++i)
for(uInt32 i = 0; i < myConsole.cartridge().bankCount(); ++i)
myBankInfo.push_back(info);
info.size = 128; // ZP RAM
myBankInfo.push_back(info);
// We know the address for the startup bank right now
myBankInfo[myConsole.cartridge().startBank()].addressList.push_front(myDebugger.dpeek(0xfffc));
myBankInfo[myConsole.cartridge().startBank()].addressList.push_front(
myDebugger.dpeek(0xfffc));
addLabel("Start", myDebugger.dpeek(0xfffc, DATA));
// Add system equates
for(uInt16 addr = 0x00; addr <= 0x0F; ++addr)
{
@ -132,7 +133,7 @@ const DebuggerState& CartDebug::getState()
{
myState.ram.clear();
for(uInt32 i = 0; i < myState.rport.size(); ++i)
myState.ram.push_back(peek(myState.rport[i]));
myState.ram.push_back(myDebugger.peek(myState.rport[i]));
if(myDebugWidget)
myState.bank = myDebugWidget->bankState();
@ -145,7 +146,7 @@ void CartDebug::saveOldState()
{
myOldState.ram.clear();
for(uInt32 i = 0; i < myOldState.rport.size(); ++i)
myOldState.ram.push_back(peek(myOldState.rport[i]));
myOldState.ram.push_back(myDebugger.peek(myOldState.rport[i]));
if(myDebugWidget)
{
@ -274,7 +275,7 @@ bool CartDebug::disassemble(bool force)
}
// Otherwise, add the item at the end
if (i == addresses.end())
addresses.push_back(PC);
addresses.push_back(PC);
}
// Always attempt to resolve code sections unless it's been
@ -723,7 +724,7 @@ string CartDebug::loadListFile()
if(addr_s.length() == 0)
continue;
const char* p = addr_s[0] == 'U' ? addr_s.c_str() + 1 : addr_s.c_str();
addr = int(strtoul(p, NULL, 16));
addr = int(strtoul(p, nullptr, 16));
// For now, completely ignore ROM addresses
if(!(addr & 0x1000))
@ -786,7 +787,8 @@ string CartDebug::loadSymbolFile()
if (iter == myUserLabels.end() || !BSPF::equalsIgnoreCase(label, iter->second))
{
// Check for period, and strip leading number
if(string::size_type pos = label.find_first_of(".", 0) != string::npos)
string::size_type pos = label.find_first_of(".", 0);
if(pos != string::npos)
addLabel(label.substr(pos), value);
else
addLabel(label, value);
@ -1005,7 +1007,7 @@ string CartDebug::saveDisassembly()
disasm.list.clear();
DiStella distella(*this, disasm.list, info, settings,
myDisLabels, myDisDirectives, myReserved);
if (myReserved.breakFound)
addLabel("Break", myDebugger.dpeek(0xfffe));
@ -1055,7 +1057,7 @@ string CartDebug::saveDisassembly()
break;
}
case CartDebug::DATA:
{
{
buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 8 * 4 - 1) << "; $" << Base::HEX4 << tag.address << " (D)";
break;
}
@ -1063,7 +1065,7 @@ string CartDebug::saveDisassembly()
default:
{
break;
}
}
} // switch
buf << "\n";
}
@ -1100,7 +1102,7 @@ string CartDebug::saveDisassembly()
out << "\n;-----------------------------------------------------------\n"
<< "; TIA and IO constants accessed\n"
<< ";-----------------------------------------------------------\n\n";
// TIA read access
for(uInt16 addr = 0x00; addr <= 0x0F; ++addr)
if(myReserved.TIARead[addr] && ourTIAMnemonicR[addr])
@ -1129,9 +1131,9 @@ string CartDebug::saveDisassembly()
}
addrUsed = false;
for(uInt16 addr = 0x80; addr <= 0xFF; ++addr)
addrUsed = addrUsed || myReserved.ZPRAM[addr-0x80]
|| (mySystem.getAccessFlags(addr) & (DATA | WRITE))
for(uInt16 addr = 0x80; addr <= 0xFF; ++addr)
addrUsed = addrUsed || myReserved.ZPRAM[addr-0x80]
|| (mySystem.getAccessFlags(addr) & (DATA | WRITE))
|| (mySystem.getAccessFlags(addr|0x100) & (DATA | WRITE));
if(addrUsed)
{
@ -1139,13 +1141,13 @@ string CartDebug::saveDisassembly()
out << "\n\n;-----------------------------------------------------------\n"
<< "; RIOT RAM (zero-page) labels\n"
<< ";-----------------------------------------------------------\n\n";
for (uInt16 addr = 0x80; addr <= 0xFF; ++addr) {
bool ramUsed = (mySystem.getAccessFlags(addr) & (DATA | WRITE));
bool codeUsed = (mySystem.getAccessFlags(addr) & CODE);
bool stackUsed = (mySystem.getAccessFlags(addr|0x100) & (DATA | WRITE));
if (myReserved.ZPRAM[addr - 0x80] &&
if (myReserved.ZPRAM[addr - 0x80] &&
myUserLabels.find(addr) == myUserLabels.end()) {
if (addLine)
out << "\n";
@ -1153,7 +1155,7 @@ string CartDebug::saveDisassembly()
<< Base::HEX2 << right << (addr)
<< (stackUsed|codeUsed ? "; (" : "")
<< (codeUsed ? "c" : "")
<< (stackUsed ? "s" : "")
<< (stackUsed ? "s" : "")
<< (stackUsed | codeUsed ? ")" : "")
<< "\n";
addLine = false;
@ -1161,11 +1163,11 @@ string CartDebug::saveDisassembly()
if (addLine)
out << "\n";
out << ALIGN(18) << ";" << "$"
<< Base::HEX2 << right << (addr)
<< " ("
<< (ramUsed ? "i" : "")
<< (codeUsed ? "c" : "")
<< (stackUsed ? "s" : "")
<< Base::HEX2 << right << (addr)
<< " ("
<< (ramUsed ? "i" : "")
<< (codeUsed ? "c" : "")
<< (stackUsed ? "s" : "")
<< ")\n";
addLine = false;
} else
@ -1278,23 +1280,23 @@ void CartDebug::getCompletions(const char* in, StringList& completions) const
{
// First scan system equates
for(uInt16 addr = 0x00; addr <= 0x0F; ++addr)
if(ourTIAMnemonicR[addr] && BSPF::startsWithIgnoreCase(ourTIAMnemonicR[addr], in))
if(ourTIAMnemonicR[addr] && BSPF::matches(ourTIAMnemonicR[addr], in))
completions.push_back(ourTIAMnemonicR[addr]);
for(uInt16 addr = 0x00; addr <= 0x3F; ++addr)
if(ourTIAMnemonicW[addr] && BSPF::startsWithIgnoreCase(ourTIAMnemonicW[addr], in))
if(ourTIAMnemonicW[addr] && BSPF::matches(ourTIAMnemonicW[addr], in))
completions.push_back(ourTIAMnemonicW[addr]);
for(uInt16 addr = 0; addr <= 0x297-0x280; ++addr)
if(ourIOMnemonic[addr] && BSPF::startsWithIgnoreCase(ourIOMnemonic[addr], in))
if(ourIOMnemonic[addr] && BSPF::matches(ourIOMnemonic[addr], in))
completions.push_back(ourIOMnemonic[addr]);
for(uInt16 addr = 0; addr <= 0x7F; ++addr)
if(ourZPMnemonic[addr] && BSPF::startsWithIgnoreCase(ourZPMnemonic[addr], in))
if(ourZPMnemonic[addr] && BSPF::matches(ourZPMnemonic[addr], in))
completions.push_back(ourZPMnemonic[addr]);
// Now scan user-defined labels
for(const auto& iter: myUserAddresses)
{
const char* l = iter.first.c_str();
if(BSPF::startsWithIgnoreCase(l, in))
if(BSPF::matches(l, in))
completions.push_back(l);
}
}
@ -1332,7 +1334,7 @@ void CartDebug::getBankDirectives(ostream& buf, BankInfo& info) const
buf << "ORG " << Base::HEX4 << info.offset << endl;
// Now consider each byte
uInt32 prev = info.offset, addr = prev + 1;
uInt16 prev = info.offset, addr = prev + 1;
DisasmType prevType = disasmTypeAbsolute(mySystem.getAccessFlags(prev));
for( ; addr < info.offset + info.size; ++addr)
{
@ -1455,14 +1457,16 @@ const char* const CartDebug::ourTIAMnemonicW[64] = {
"AUDF1", "AUDV0", "AUDV1", "GRP0", "GRP1", "ENAM0", "ENAM1", "ENABL",
"HMP0", "HMP1", "HMM0", "HMM1", "HMBL", "VDELP0", "VDELP1", "VDELBL",
"RESMP0", "RESMP1", "HMOVE", "HMCLR", "CXCLR", "$2d", "$2e", "$2f",
"$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37",
"$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37",
"$38", "$39", "$3a", "$3b", "$3c", "$3d", "$3e", "$3f"
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const char* const CartDebug::ourIOMnemonic[24] = {
"SWCHA", "SWACNT", "SWCHB", "SWBCNT", "INTIM", "TIMINT", 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, "TIM1T", "TIM8T", "TIM64T", "T1024T"
"SWCHA", "SWACNT", "SWCHB", "SWBCNT", "INTIM", "TIMINT",
"$286", "$287", "$288", "$289", "$28a", "$28b", "$28c",
"$28d", "$28e", "$28f", "$290", "$291", "$292", "$293",
"TIM1T", "TIM8T", "TIM64T", "T1024T"
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -30,6 +30,7 @@ class CartDebugWidget;
#include "Cart.hxx"
#include "DebuggerSystem.hxx"
#include "System.hxx"
#include "M6502.hxx"
// Function type for CartDebug instance methods
class CartDebug;
@ -38,10 +39,10 @@ using CartMethod = int (CartDebug::*)();
class CartState : public DebuggerState
{
public:
ByteArray ram; // The actual data values
IntArray rport; // Address for reading from RAM
IntArray wport; // Address for writing to RAM
string bank; // Current banking layout
ByteArray ram; // The actual data values
ShortArray rport; // Address for reading from RAM
ShortArray wport; // Address for writing to RAM
string bank; // Current banking layout
};
class CartDebug : public DebuggerSystem
@ -112,13 +113,6 @@ class CartDebug : public DebuggerSystem
CartDebugWidget* getDebugWidget() const { return myDebugWidget; }
void setDebugWidget(CartDebugWidget* w) { myDebugWidget = w; }
// The following assume that the given addresses are using the
// correct read/write port ranges; no checking will be done to
// confirm this.
uInt8 peek(uInt16 addr) { return mySystem.peek(addr); }
uInt16 dpeek(uInt16 addr) { return mySystem.peek(addr) | (mySystem.peek(addr+1) << 8); }
void poke(uInt16 addr, uInt8 value) { mySystem.poke(addr, value); }
// Indicate that a read from write port has occurred at the specified
// address.
void triggerReadFromWritePort(uInt16 address);
@ -127,6 +121,11 @@ class CartDebug : public DebuggerSystem
// write port area.
int readFromWritePort();
// Return the base (= non-mirrored) address of the last CPU read
int lastReadBaseAddress() { return mySystem.m6502().lastReadBaseAddress(); }
// Return the base (= non-mirrored) address of the last CPU write
int lastWriteBaseAddress() { return mySystem.m6502().lastWriteBaseAddress(); }
// The following two methods are meant to be used together
// First, a call is made to disassemble(), which updates the disassembly
// list; it will figure out when an actual complete disassembly is
@ -289,7 +288,7 @@ class CartDebug : public DebuggerSystem
uInt16 start; // start of address space
uInt16 end; // end of address space
uInt16 offset; // ORG value
uInt16 size; // size of a bank (in bytes)
uInt32 size; // size of a bank (in bytes)
AddressList addressList; // addresses which PC has hit
DirectiveList directiveList; // overrides for automatic code determination

View File

@ -29,7 +29,6 @@
#include "Settings.hxx"
#include "DebuggerDialog.hxx"
#include "DebuggerParser.hxx"
#include "StateManager.hxx"
#include "Console.hxx"
#include "System.hxx"
@ -58,58 +57,6 @@
Debugger* Debugger::myStaticDebugger = nullptr;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const char* const builtin_functions[][3] = {
// { "name", "definition", "help text" }
// left joystick:
{ "_joy0left", "!(*SWCHA & $40)", "Left joystick moved left" },
{ "_joy0right", "!(*SWCHA & $80)", "Left joystick moved right" },
{ "_joy0up", "!(*SWCHA & $10)", "Left joystick moved up" },
{ "_joy0down", "!(*SWCHA & $20)", "Left joystick moved down" },
{ "_joy0button", "!(*INPT4 & $80)", "Left joystick button pressed" },
// right joystick:
{ "_joy1left", "!(*SWCHA & $04)", "Right joystick moved left" },
{ "_joy1right", "!(*SWCHA & $08)", "Right joystick moved right" },
{ "_joy1up", "!(*SWCHA & $01)", "Right joystick moved up" },
{ "_joy1down", "!(*SWCHA & $02)", "Right joystick moved down" },
{ "_joy1button", "!(*INPT5 & $80)", "Right joystick button pressed" },
// console switches:
{ "_select", "!(*SWCHB & $02)", "Game Select pressed" },
{ "_reset", "!(*SWCHB & $01)", "Game Reset pressed" },
{ "_color", "*SWCHB & $08", "Color/BW set to Color" },
{ "_bw", "!(*SWCHB & $08)", "Color/BW set to BW" },
{ "_diff0b", "!(*SWCHB & $40)", "Left diff. set to B (easy)" },
{ "_diff0a", "*SWCHB & $40", "Left diff. set to A (hard)" },
{ "_diff1b", "!(*SWCHB & $80)", "Right diff. set to B (easy)" },
{ "_diff1a", "*SWCHB & $80", "Right diff. set to A (hard)" },
// empty string marks end of list, do not remove
{ 0, 0, 0 }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Names are defined here, but processed in YaccParser
static const char* const pseudo_registers[][2] = {
// { "name", "help text" }
{ "_bank", "Currently selected bank" },
{ "_cclocks", "Color clocks on current scanline" },
{ "_fcount", "Number of frames since emulation started" },
{ "_fcycles", "Number of cycles since frame started" },
{ "_cyclesLo", "Lower 32 bits of number of cycles since emulation started"},
{ "_cyclesHi", "Higher 32 bits of number of cycles since emulation started"},
{ "_rwport", "Address at which a read from a write port occurred" },
{ "_scan", "Current scanline count" },
{ "_vblank", "Whether vertical blank is enabled (1 or 0)" },
{ "_vsync", "Whether vertical sync is enabled (1 or 0)" },
// empty string marks end of list, do not remove
{ 0, 0 }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Debugger::Debugger(OSystem& osystem, Console& console)
: DialogContainer(osystem),
@ -166,7 +113,7 @@ FBInitStatus Debugger::initializeVideo()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::start(const string& message, int address)
bool Debugger::start(const string& message, int address, bool read)
{
if(myOSystem.eventHandler().enterDebugMode())
{
@ -175,8 +122,7 @@ bool Debugger::start(const string& message, int address)
ostringstream buf;
buf << message;
if(address > -1)
buf << Common::Base::HEX4 << address;
buf << cartDebug().getLabel(address, read, 4);
myDialog->message().setText(buf.str());
return true;
}
@ -206,26 +152,26 @@ void Debugger::quit(bool exitrom)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Debugger::autoExec()
string Debugger::autoExec(StringList* history)
{
ostringstream buf;
// autoexec.stella is always run
FilesystemNode autoexec(myOSystem.baseDir() + "autoexec.stella");
// autoexec.script is always run
FilesystemNode autoexec(myOSystem.baseDir() + "autoexec.script");
buf << "autoExec():" << endl
<< myParser->exec(autoexec) << endl;
<< myParser->exec(autoexec, history) << endl;
// Also, "romname.stella" if present
FilesystemNode romname(myOSystem.romFile().getPathWithExt(".stella"));
buf << myParser->exec(romname) << endl;
// Also, "romname.script" if present
FilesystemNode romname(myOSystem.romFile().getPathWithExt(".script"));
buf << myParser->exec(romname, history) << endl;
// Init builtins
for(int i = 0; builtin_functions[i][0] != 0; i++)
for(uInt32 i = 0; i < NUM_BUILTIN_FUNCS; ++i)
{
// TODO - check this for memory leaks
int res = YaccParser::parse(builtin_functions[i][1]);
int res = YaccParser::parse(ourBuiltinFunctions[i].defn);
if(res == 0)
addFunction(builtin_functions[i][0], builtin_functions[i][1],
addFunction(ourBuiltinFunctions[i].name, ourBuiltinFunctions[i].defn,
YaccParser::getResult(), true);
else
cerr << "ERROR in builtin function!" << endl;
@ -367,24 +313,43 @@ bool Debugger::breakPoint(uInt16 bp)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleReadTrap(uInt16 t)
void Debugger::addReadTrap(uInt16 t)
{
readTraps().initialize();
readTraps().toggle(t);
readTraps().add(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleWriteTrap(uInt16 t)
void Debugger::addWriteTrap(uInt16 t)
{
writeTraps().initialize();
writeTraps().toggle(t);
writeTraps().add(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleTrap(uInt16 t)
void Debugger::addTrap(uInt16 t)
{
toggleReadTrap(t);
toggleWriteTrap(t);
addReadTrap(t);
addWriteTrap(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::removeReadTrap(uInt16 t)
{
readTraps().initialize();
readTraps().remove(t);
}
void Debugger::removeWriteTrap(uInt16 t)
{
writeTraps().initialize();
writeTraps().remove(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::removeTrap(uInt16 t)
{
removeReadTrap(t);
removeWriteTrap(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -399,6 +364,50 @@ bool Debugger::writeTrap(uInt16 t)
return writeTraps().isInitialized() && writeTraps().isSet(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Debugger::getBaseAddress(uInt32 addr, bool read)
{
if((addr & 0x1080) == 0x0000) // (addr & 0b 0001 0000 1000 0000) == 0b 0000 0000 0000 0000
{
if(read)
// ADDR_TIA read (%xxx0 xxxx 0xxx ????)
return addr & 0x000f; // 0b 0000 0000 0000 1111
else
// ADDR_TIA write (%xxx0 xxxx 0x?? ????)
return addr & 0x003f; // 0b 0000 0000 0011 1111
}
// ADDR_ZPRAM (%xxx0 xx0x 1??? ????)
if((addr & 0x1280) == 0x0080) // (addr & 0b 0001 0010 1000 0000) == 0b 0000 0000 1000 0000
return addr & 0x00ff; // 0b 0000 0000 1111 1111
// ADDR_ROM
if(addr & 0x1000)
return addr & 0x1fff; // 0b 0001 1111 1111 1111
// ADDR_IO read/write I/O registers (%xxx0 xx1x 1xxx x0??)
if((addr & 0x1284) == 0x0280) // (addr & 0b 0001 0010 1000 0100) == 0b 0000 0010 1000 0000
return addr & 0x0283; // 0b 0000 0010 1000 0011
// ADDR_IO write timers (%xxx0 xx1x 1xx1 ?1??)
if(!read && (addr & 0x1294) == 0x0294) // (addr & 0b 0001 0010 1001 0100) == 0b 0000 0010 1001 0100
return addr & 0x029f; // 0b 0000 0010 1001 1111
// ADDR_IO read timers (%xxx0 xx1x 1xxx ?1x0)
if(read && (addr & 0x1285) == 0x0284) // (addr & 0b 0001 0010 1000 0101) == 0b 0000 0010 1000 0100
return addr & 0x028c; // 0b 0000 0010 1000 1100
// ADDR_IO read timer/PA7 interrupt (%xxx0 xx1x 1xxx x1x1)
if(read && (addr & 0x1285) == 0x0285) // (addr & 0b 0001 0010 1000 0101) == 0b 0000 0010 1000 0101
return addr & 0x0285; // 0b 0000 0010 1000 0101
// ADDR_IO write PA7 edge control (%xxx0 xx1x 1xx0 x1??)
if(!read && (addr & 0x1294) == 0x0284) // (addr & 0b 0001 0010 1001 0100) == 0b 0000 0010 1000 0100
return addr & 0x0287; // 0b 0000 0010 1000 0111
return 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::nextScanline(int lines)
{
@ -442,21 +451,39 @@ void Debugger::nextFrame(int frames)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::rewindState()
void Debugger::updateRewindbuttons(const RewindManager& r)
{
myDialog->rewindButton().setEnabled(!r.atLast());
myDialog->unwindButton().setEnabled(!r.atFirst());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::windState(bool unwind)
{
RewindManager& r = myOSystem.state().rewindManager();
mySystem.clearDirtyPages();
unlockBankswitchState();
bool result = r.rewindState();
bool result = unwind ? r.unwindState() : r.rewindState();
lockBankswitchState();
myDialog->rewindButton().setEnabled(!r.empty());
updateRewindbuttons(r);
return result;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::rewindState()
{
return windState(false);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::unwindState()
{
return windState(true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::clearAllBreakPoints()
{
@ -495,7 +522,7 @@ void Debugger::saveOldState(string rewindMsg)
{
RewindManager& r = myOSystem.state().rewindManager();
r.addState(rewindMsg);
myDialog->rewindButton().setEnabled(!r.empty());
updateRewindbuttons(r);
}
}
@ -505,11 +532,8 @@ void Debugger::setStartState()
// Lock the bus each time the debugger is entered, so we don't disturb anything
lockBankswitchState();
// If rewinding is not enabled, always start the debugger with a clean list
RewindManager& r = myOSystem.state().rewindManager();
if(myOSystem.state().mode() == StateManager::Mode::Off)
r.clear();
myDialog->rewindButton().setEnabled(!r.empty());
updateRewindbuttons(r);
// Save initial state, but don't add it to the rewind list
saveOldState();
@ -524,6 +548,9 @@ void Debugger::setQuitState()
// Bus must be unlocked for normal operation when leaving debugger mode
unlockBankswitchState();
// Save state when leaving the debugger
saveOldState("exit debugger");
// execute one instruction on quit. If we're
// sitting at a breakpoint/trap, this will get us past it.
// Somehow this feels like a hack to me, but I don't know why
@ -541,6 +568,15 @@ bool Debugger::addFunction(const string& name, const string& definition,
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::isBuiltinFunction(const string& name)
{
for(uInt32 i = 0; i < NUM_BUILTIN_FUNCS; ++i)
if(name == ourBuiltinFunctions[i].name)
return true;
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::delFunction(const string& name)
{
@ -549,8 +585,7 @@ bool Debugger::delFunction(const string& name)
return false;
// We never want to delete built-in functions
for(int i = 0; builtin_functions[i][0] != 0; ++i)
if(name == builtin_functions[i][0])
if(isBuiltinFunction(name))
return false;
myFunctions.erase(name);
@ -590,39 +625,39 @@ string Debugger::builtinHelp() const
uInt16 len, c_maxlen = 0, i_maxlen = 0;
// Get column widths for aligned output (functions)
for(int i = 0; builtin_functions[i][0] != 0; ++i)
for(uInt32 i = 0; i < NUM_BUILTIN_FUNCS; ++i)
{
len = strlen(builtin_functions[i][0]);
len = ourBuiltinFunctions[i].name.size();
if(len > c_maxlen) c_maxlen = len;
len = strlen(builtin_functions[i][1]);
len = ourBuiltinFunctions[i].defn.size();
if(len > i_maxlen) i_maxlen = len;
}
buf << std::setfill(' ') << endl << "Built-in functions:" << endl;
for(int i = 0; builtin_functions[i][0] != 0; ++i)
for(uInt32 i = 0; i < NUM_BUILTIN_FUNCS; ++i)
{
buf << std::setw(c_maxlen) << std::left << builtin_functions[i][0]
buf << std::setw(c_maxlen) << std::left << ourBuiltinFunctions[i].name
<< std::setw(2) << std::right << "{"
<< std::setw(i_maxlen) << std::left << builtin_functions[i][1]
<< std::setw(i_maxlen) << std::left << ourBuiltinFunctions[i].defn
<< std::setw(4) << "}"
<< builtin_functions[i][2]
<< ourBuiltinFunctions[i].help
<< endl;
}
// Get column widths for aligned output (pseudo-registers)
c_maxlen = 0;
for(int i = 0; pseudo_registers[i][0] != 0; ++i)
for(uInt32 i = 0; i < NUM_PSEUDO_REGS; ++i)
{
len = strlen(pseudo_registers[i][0]);
len = ourPseudoRegisters[i].name.size();
if(len > c_maxlen) c_maxlen = len;
}
buf << endl << "Pseudo-registers:" << endl;
for(int i = 0; pseudo_registers[i][0] != 0; ++i)
for(uInt32 i = 0; i < NUM_PSEUDO_REGS; ++i)
{
buf << std::setw(c_maxlen) << std::left << pseudo_registers[i][0]
buf << std::setw(c_maxlen) << std::left << ourPseudoRegisters[i].name
<< std::setw(2) << " "
<< std::setw(i_maxlen) << std::left << pseudo_registers[i][1]
<< std::setw(i_maxlen) << std::left << ourPseudoRegisters[i].help
<< endl;
}
@ -632,16 +667,20 @@ string Debugger::builtinHelp() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::getCompletions(const char* in, StringList& list) const
{
for(const auto& iter: myFunctions)
// skip if filter equals "_" only
if(!BSPF::equalsIgnoreCase(in, "_"))
{
const char* l = iter.first.c_str();
if(BSPF::startsWithIgnoreCase(l, in))
list.push_back(l);
}
for(const auto& iter : myFunctions)
{
const char* l = iter.first.c_str();
if(BSPF::matches(l, in))
list.push_back(l);
}
for(int i = 0; pseudo_registers[i][0] != 0; ++i)
if(BSPF::startsWithIgnoreCase(pseudo_registers[i][0], in))
list.push_back(pseudo_registers[i][0]);
for(uInt32 i = 0; i < NUM_PSEUDO_REGS; ++i)
if(BSPF::matches(ourPseudoRegisters[i].name, in))
list.push_back(ourPseudoRegisters[i].name);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -657,3 +696,49 @@ void Debugger::unlockBankswitchState()
mySystem.unlockDataBus();
myConsole.cartridge().unlockBank();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Debugger::BuiltinFunction Debugger::ourBuiltinFunctions[NUM_BUILTIN_FUNCS] = {
// left joystick:
{ "_joy0left", "!(*SWCHA & $40)", "Left joystick moved left" },
{ "_joy0right", "!(*SWCHA & $80)", "Left joystick moved right" },
{ "_joy0up", "!(*SWCHA & $10)", "Left joystick moved up" },
{ "_joy0down", "!(*SWCHA & $20)", "Left joystick moved down" },
{ "_joy0button", "!(*INPT4 & $80)", "Left joystick button pressed" },
// right joystick:
{ "_joy1left", "!(*SWCHA & $04)", "Right joystick moved left" },
{ "_joy1right", "!(*SWCHA & $08)", "Right joystick moved right" },
{ "_joy1up", "!(*SWCHA & $01)", "Right joystick moved up" },
{ "_joy1down", "!(*SWCHA & $02)", "Right joystick moved down" },
{ "_joy1button", "!(*INPT5 & $80)", "Right joystick button pressed" },
// console switches:
{ "_select", "!(*SWCHB & $02)", "Game Select pressed" },
{ "_reset", "!(*SWCHB & $01)", "Game Reset pressed" },
{ "_color", "*SWCHB & $08", "Color/BW set to Color" },
{ "_bw", "!(*SWCHB & $08)", "Color/BW set to BW" },
{ "_diff0b", "!(*SWCHB & $40)", "Left diff. set to B (easy)" },
{ "_diff0a", "*SWCHB & $40", "Left diff. set to A (hard)" },
{ "_diff1b", "!(*SWCHB & $80)", "Right diff. set to B (easy)" },
{ "_diff1a", "*SWCHB & $80", "Right diff. set to A (hard)" }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Names are defined here, but processed in YaccParser
Debugger::PseudoRegister Debugger::ourPseudoRegisters[NUM_PSEUDO_REGS] = {
{ "_bank", "Currently selected bank" },
{ "_cclocks", "Color clocks on current scanline" },
{ "_cycleshi", "Higher 32 bits of number of cycles since emulation started" },
{ "_cycleslo", "Lower 32 bits of number of cycles since emulation started" },
{ "_fcount", "Number of frames since emulation started" },
{ "_fcycles", "Number of cycles since frame started" },
{ "_rwport", "Address at which a read from a write port occurred" },
{ "_scan", "Current scanline count" },
{ "_scycles", "Number of cycles in current scanline" },
{ "_vblank", "Whether vertical blank is enabled (1 or 0)" },
{ "_vsync", "Whether vertical sync is enabled (1 or 0)" }
// CPU address access functions:
/*{ "__lastread", "last CPU read address" },
{ "__lastwrite", "last CPU write address" },*/
};

View File

@ -36,6 +36,7 @@ class ButtonWidget;
#include "DialogContainer.hxx"
#include "DebuggerDialog.hxx"
#include "DebuggerParser.hxx"
#include "StateManager.hxx"
#include "M6502.hxx"
#include "System.hxx"
#include "Stack.hxx"
@ -88,7 +89,7 @@ class Debugger : public DialogContainer
@param message Message to display when entering debugger
@param address An address associated with the message
*/
bool start(const string& message = "", int address = -1);
bool start(const string& message = "", int address = -1, bool read = true);
bool startWithFatalError(const string& message = "");
/**
@ -99,6 +100,7 @@ class Debugger : public DialogContainer
bool addFunction(const string& name, const string& def,
Expression* exp, bool builtin = false);
bool isBuiltinFunction(const string& name);
bool delFunction(const string& name);
const Expression& getFunction(const string& name) const;
@ -145,15 +147,15 @@ class Debugger : public DialogContainer
TiaOutputWidget& tiaOutput() const { return myDialog->tiaOutput(); }
PackedBitArray& breakPoints() const { return mySystem.m6502().breakPoints(); }
PackedBitArray& readTraps() const { return mySystem.m6502().readTraps(); }
PackedBitArray& writeTraps() const { return mySystem.m6502().writeTraps(); }
TrapArray& readTraps() const { return mySystem.m6502().readTraps(); }
TrapArray& writeTraps() const { return mySystem.m6502().writeTraps(); }
/**
Run the debugger command and return the result.
*/
const string run(const string& command);
string autoExec();
string autoExec(StringList* history);
string showWatches();
@ -168,9 +170,9 @@ class Debugger : public DialogContainer
static uInt8 set_bit(uInt8 input, uInt8 bit, bool on)
{
if(on)
return input | (1 << bit);
return uInt8(input | (1 << bit));
else
return input & ~(1 << bit);
return uInt8(input & ~(1 << bit));
}
static void set_bits(uInt8 reg, BoolArray& bits)
{
@ -192,7 +194,7 @@ class Debugger : public DialogContainer
return result;
}
/* Invert given input if it differs from its previous value */
/** Invert given input if it differs from its previous value */
const string invIfChanged(int reg, int oldReg);
/**
@ -205,15 +207,32 @@ class Debugger : public DialogContainer
*/
static Debugger& debugger() { return *myStaticDebugger; }
/* These are now exposed so Expressions can use them. */
int peek(int addr, uInt8 flags = 0) { return mySystem.peek(addr, flags); }
int dpeek(int addr, uInt8 flags = 0) { return mySystem.peek(addr, flags) | (mySystem.peek(addr+1, flags) << 8); }
/** Convenience methods to access peek/poke from System */
uInt8 peek(uInt16 addr, uInt8 flags = 0) {
return mySystem.peek(addr, flags);
}
uInt16 dpeek(uInt16 addr, uInt8 flags = 0) {
return uInt16(mySystem.peek(addr, flags) | (mySystem.peek(addr+1, flags) << 8));
}
void poke(uInt16 addr, uInt8 value, uInt8 flags = 0) {
mySystem.poke(addr, value, flags);
}
/** These are now exposed so Expressions can use them. */
int peekAsInt(int addr, uInt8 flags = 0) {
return mySystem.peek(uInt16(addr), flags);
}
int dpeekAsInt(int addr, uInt8 flags = 0) {
return mySystem.peek(uInt16(addr), flags) |
(mySystem.peek(uInt16(addr+1), flags) << 8);
}
int getAccessFlags(uInt16 addr) const
{ return mySystem.getAccessFlags(addr); }
void setAccessFlags(uInt16 addr, uInt8 flags)
{ mySystem.setAccessFlags(addr, flags); }
void setBreakPoint(uInt16 bp, bool set);
uInt32 getBaseAddress(uInt32 addr, bool read);
bool patchROM(uInt16 addr, uInt8 value);
@ -252,13 +271,17 @@ class Debugger : public DialogContainer
void nextScanline(int lines);
void nextFrame(int frames);
bool rewindState();
bool unwindState();
void toggleBreakPoint(uInt16 bp);
bool breakPoint(uInt16 bp);
void toggleReadTrap(uInt16 t);
void toggleWriteTrap(uInt16 t);
void toggleTrap(uInt16 t);
void addReadTrap(uInt16 t);
void addWriteTrap(uInt16 t);
void addTrap(uInt16 t);
void removeReadTrap(uInt16 t);
void removeWriteTrap(uInt16 t);
void removeTrap(uInt16 t);
bool readTrap(uInt16 t);
bool writeTrap(uInt16 t);
void clearAllTraps();
@ -292,7 +315,24 @@ class Debugger : public DialogContainer
uInt32 myWidth;
uInt32 myHeight;
// Various builtin functions and operations
struct BuiltinFunction {
string name, defn, help;
};
struct PseudoRegister {
string name, help;
};
static const uInt32 NUM_BUILTIN_FUNCS = 18;
static const uInt32 NUM_PSEUDO_REGS = 11;
static BuiltinFunction ourBuiltinFunctions[NUM_BUILTIN_FUNCS];
static PseudoRegister ourPseudoRegisters[NUM_PSEUDO_REGS];
private:
// rewind/unwind one state
bool windState(bool unwind);
// update the rewind/unwind button state
void updateRewindbuttons(const RewindManager& r);
// Following constructors and assignment operators not supported
Debugger() = delete;
Debugger(const Debugger&) = delete;

View File

@ -337,7 +337,7 @@ class WordDerefExpression : public Expression
public:
WordDerefExpression(Expression* left) : Expression(left) { }
Int32 evaluate() const override
{ return Debugger::debugger().dpeek(myLHS->evaluate()); }
{ return Debugger::debugger().dpeekAsInt(myLHS->evaluate()); }
};
#endif

View File

@ -110,7 +110,10 @@ string DebuggerParser::run(const string& command)
if(BSPF::equalsIgnoreCase(verb, commands[i].cmdString))
{
if(validateArgs(i))
{
myCommand = i;
commands[i].executor(this);
}
if(commands[i].refreshRequired)
debugger.myBaseDialog->loadConfig();
@ -119,17 +122,17 @@ string DebuggerParser::run(const string& command)
}
}
return "No such command (try \"help\")";
return red("No such command (try \"help\")");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string DebuggerParser::exec(const FilesystemNode& file)
string DebuggerParser::exec(const FilesystemNode& file, StringList* history)
{
if(file.exists())
{
ifstream in(file.getPath());
if(!in.is_open())
return red("autoexec file \'" + file.getShortPath() + "\' not found");
return red("script file \'" + file.getShortPath() + "\' not found");
ostringstream buf;
int count = 0;
@ -140,15 +143,27 @@ string DebuggerParser::exec(const FilesystemNode& file)
break;
run(command);
if (history != nullptr)
history->push_back(command);
count++;
}
buf << "Executed " << count << " commands from \""
buf << "\nExecuted " << count << " commands from \""
<< file.getShortPath() << "\"";
return buf.str();
}
else
return red("autoexec file \'" + file.getShortPath() + "\' not found");
return red("script file \'" + file.getShortPath() + "\' not found");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerParser::outputCommandError(const string& errorMsg, int command)
{
string example = commands[command].extendedDesc.substr(commands[command].extendedDesc.find("Example:"));
commandResult << red(errorMsg);
if(!example.empty())
commandResult << endl << example;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -159,7 +174,7 @@ void DebuggerParser::getCompletions(const char* in, StringList& completions) con
// cerr << "Attempting to complete \"" << in << "\"" << endl;
for(int i = 0; i < kNumCommands; ++i)
{
if(BSPF::startsWithIgnoreCase(commands[i].cmdString.c_str(), in))
if(BSPF::matches(commands[i].cmdString, in))
completions.push_back(commands[i].cmdString);
}
}
@ -396,7 +411,8 @@ bool DebuggerParser::validateArgs(int cmd)
{
if(required)
{
commandResult.str(red("missing required argument(s)"));
commandResult.str();
outputCommandError("missing required argument(s)", cmd);
return false; // needed args. didn't get 'em.
}
else
@ -427,6 +443,14 @@ bool DebuggerParser::validateArgs(int cmd)
switch(*p)
{
case kARG_DWORD:
if(curArgInt > 0xffffffff)
{
commandResult.str(red("invalid word argument (must be 0-$ffffffff)"));
return false;
}
break;
case kARG_WORD:
if(curArgInt > 0xffff)
{
@ -484,12 +508,14 @@ cerr << "curCount = " << curCount << endl
if(curCount < argRequiredCount)
{
commandResult.str(red("missing required argument(s)"));
commandResult.str();
outputCommandError("missing required argument(s)", cmd);
return false;
}
else if(argCount > curCount)
{
commandResult.str(red("too many arguments"));
commandResult.str();
outputCommandError("too many arguments", cmd);
return false;
}
@ -502,28 +528,29 @@ string DebuggerParser::eval()
ostringstream buf;
for(uInt32 i = 0; i < argCount; ++i)
{
string rlabel = debugger.cartDebug().getLabel(args[i], true);
string wlabel = debugger.cartDebug().getLabel(args[i], false);
bool validR = rlabel != "" && rlabel[0] != '$',
validW = wlabel != "" && wlabel[0] != '$';
if(validR && validW)
if(args[i] < 0x10000)
{
if(rlabel == wlabel)
buf << rlabel << "(R/W): ";
else
buf << rlabel << "(R) / " << wlabel << "(W): ";
string rlabel = debugger.cartDebug().getLabel(args[i], true);
string wlabel = debugger.cartDebug().getLabel(args[i], false);
bool validR = rlabel != "" && rlabel[0] != '$',
validW = wlabel != "" && wlabel[0] != '$';
if(validR && validW)
{
if(rlabel == wlabel)
buf << rlabel << "(R/W): ";
else
buf << rlabel << "(R) / " << wlabel << "(W): ";
}
else if(validR)
buf << rlabel << "(R): ";
else if(validW)
buf << wlabel << "(W): ";
}
else if(validR)
buf << rlabel << "(R): ";
else if(validW)
buf << wlabel << "(W): ";
if(args[i] < 0x100)
buf << "$" << Base::toString(args[i], Base::F_16_2)
<< " %" << Base::toString(args[i], Base::F_2_8);
else
buf << "$" << Base::toString(args[i], Base::F_16_4)
<< " %" << Base::toString(args[i], Base::F_2_16);
buf << "$" << Base::toString(args[i], Base::F_16);
if(args[i] < 0x10000)
buf << " %" << Base::toString(args[i], Base::F_2);
buf << " #" << int(args[i]);
if(i != argCount - 1)
@ -534,70 +561,117 @@ string DebuggerParser::eval()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string DebuggerParser::trapStatus(uInt32 addr, bool& enabled)
void DebuggerParser::listTraps(bool listCond)
{
string result;
result += Base::toString(addr);
result += ": ";
bool r = debugger.readTrap(addr);
bool w = debugger.writeTrap(addr);
enabled = r || w;
if(r && w)
result += "read|write";
else if(r)
result += "read";
else if(w)
result += "write";
else
result += "none";
StringList names = debugger.cpuDebug().m6502().getCondTrapNames();
const string& l = debugger.cartDebug().getLabel(addr, !w);
if(l != "") {
result += " (";
result += l;
result += ")";
commandResult << (listCond ? "trapifs:" : "traps:") << endl;
for(uInt32 i = 0; i < names.size(); i++)
{
bool hasCond = names[i] != "";
if(hasCond == listCond)
{
commandResult << Base::toString(i) << ": ";
if(myTraps[i]->read && myTraps[i]->write)
commandResult << "read|write";
else if(myTraps[i]->read)
commandResult << "read ";
else if(myTraps[i]->write)
commandResult << " write";
else
commandResult << "none";
if(hasCond)
commandResult << " " << names[i];
commandResult << " " << debugger.cartDebug().getLabel(myTraps[i]->begin, true, 4);
if(myTraps[i]->begin != myTraps[i]->end)
commandResult << " " << debugger.cartDebug().getLabel(myTraps[i]->end, true, 4);
commandResult << trapStatus(*myTraps[i]);
commandResult << " + mirrors";
if(i != (names.size() - 1)) commandResult << endl;
}
}
return result;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool DebuggerParser::saveScriptFile(string file)
string DebuggerParser::trapStatus(const Trap& trap)
{
if( file.find_last_of('.') == string::npos )
file += ".stella";
stringstream result;
string lblb = debugger.cartDebug().getLabel(trap.begin, !trap.write);
string lble = debugger.cartDebug().getLabel(trap.end, !trap.write);
ofstream out(file);
if(lblb != "") {
result << " (";
result << lblb;
}
if(trap.begin != trap.end)
{
if(lble != "")
{
if (lblb != "")
result << " ";
else
result << " (";
result << lble;
}
}
if (lblb != "" || lble != "")
result << ")";
return result.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string DebuggerParser::saveScriptFile(string file)
{
// Append 'script' extension when necessary
if(file.find_last_of('.') == string::npos)
file += ".script";
FilesystemNode node(debugger.myOSystem.defaultSaveDir() + file);
ofstream out(node.getPath());
if(!out.is_open())
return "Unable to save script to " + node.getShortPath();
FunctionDefMap funcs = debugger.getFunctionDefMap();
for(const auto& f: funcs)
out << "function " << f.first << " { " << f.second << " }" << endl;
if (!debugger.isBuiltinFunction(f.first))
out << "function " << f.first << " {" << f.second << "}" << endl;
for(const auto& w: myWatches)
out << "watch " << w << endl;
for(uInt32 i = 0; i < 0x10000; ++i)
if(debugger.breakPoint(i))
out << "break #" << i << endl;
for(uInt32 i = 0; i < 0x10000; ++i)
{
bool r = debugger.readTrap(i);
bool w = debugger.writeTrap(i);
if(r && w)
out << "trap #" << i << endl;
else if(r)
out << "trapread #" << i << endl;
else if(w)
out << "trapwrite #" << i << endl;
}
out << "break " << Base::toString(i) << endl;
StringList conds = debugger.cpuDebug().m6502().getCondBreakNames();
for(const auto& cond: conds)
for(const auto& cond : conds)
out << "breakif {" << cond << "}" << endl;
return out.good();
StringList names = debugger.cpuDebug().m6502().getCondTrapNames();
for(uInt32 i = 0; i < myTraps.size(); ++i)
{
bool read = myTraps[i]->read;
bool write = myTraps[i]->write;
bool hasCond = names[i] != "";
if(read && write)
out << "trap";
else if(read)
out << "trapread";
else if(write)
out << "trapwrite";
if(hasCond)
out << "if {" << names[i] << "}";
out << " " << Base::toString(myTraps[i]->begin);
if(myTraps[i]->begin != myTraps[i]->end)
out << " " << Base::toString(myTraps[i]->end);
out << endl;
}
return "saved " + node.getShortPath() + " OK";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -695,7 +769,7 @@ void DebuggerParser::executeCheat()
#ifdef CHEATCODE_SUPPORT
if(argCount == 0)
{
commandResult << red("Missing cheat code");
outputCommandError("missing cheat code", myCommand);
return;
}
@ -705,7 +779,7 @@ void DebuggerParser::executeCheat()
if(debugger.myOSystem.cheat().add("DBG", cheat))
commandResult << "Cheat code " << cheat << " enabled" << endl;
else
commandResult << red("Invalid cheat code ") << cheat << endl;
commandResult << red("invalid cheat code ") << cheat << endl;
}
#else
commandResult << red("Cheat support not enabled\n");
@ -735,8 +809,9 @@ void DebuggerParser::executeClearconfig()
// "cleartraps"
void DebuggerParser::executeCleartraps()
{
myTraps.clear();
debugger.clearAllTraps();
debugger.cpuDebug().m6502().clearCondTraps();
myTraps.clear();
commandResult << "all traps cleared";
}
@ -762,12 +837,12 @@ void DebuggerParser::executeCode()
{
if(argCount != 2)
{
commandResult << red("Specify start and end of range only");
outputCommandError("specify start and end of range only", myCommand);
return;
}
else if(args[1] < args[0])
{
commandResult << red("Start address must be <= end address");
commandResult << red("start address must be <= end address");
return;
}
@ -803,12 +878,12 @@ void DebuggerParser::executeData()
{
if(argCount != 2)
{
commandResult << red("Specify start and end of range only");
outputCommandError("specify start and end of range only", myCommand);
return;
}
else if(args[1] < args[0])
{
commandResult << red("Start address must be <= end address");
commandResult << red("start address must be <= end address");
return;
}
@ -839,7 +914,10 @@ void DebuggerParser::executeDefine()
// "delbreakif"
void DebuggerParser::executeDelbreakif()
{
debugger.cpuDebug().m6502().delCondBreak(args[0]);
if (debugger.cpuDebug().m6502().delCondBreak(args[0]))
commandResult << "removed breakif " << Base::toString(args[0]);
else
commandResult << red("no such breakif");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -852,6 +930,24 @@ void DebuggerParser::executeDelfunction()
commandResult << "function " << argStrings[0] << " built-in or not found";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "deltrap"
void DebuggerParser::executeDeltrap()
{
int index = args[0];
if(debugger.cpuDebug().m6502().delCondTrap(index))
{
for(uInt32 addr = myTraps[index]->begin; addr <= myTraps[index]->end; ++addr)
executeTrapRW(addr, myTraps[index]->read, myTraps[index]->write, false);
// @sa666666: please check this:
Vec::removeAt(myTraps, index);
commandResult << "removed trap " << Base::toString(index);
}
else
commandResult << red("no such trap");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "delwatch"
void DebuggerParser::executeDelwatch()
@ -863,7 +959,7 @@ void DebuggerParser::executeDelwatch()
commandResult << "removed watch";
}
else
commandResult << "no such watch";
commandResult << red("no such watch");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -880,7 +976,7 @@ void DebuggerParser::executeDisasm()
start = args[0];
lines = args[1];
} else {
commandResult << "wrong number of arguments";
outputCommandError("wrong number of arguments", myCommand);
return;
}
@ -910,7 +1006,7 @@ void DebuggerParser::executeDump()
// Error checking
if(argCount > 1 && args[1] < args[0])
{
commandResult << red("Start address must be <= end address");
commandResult << red("start address must be <= end address");
return;
}
@ -920,7 +1016,7 @@ void DebuggerParser::executeDump()
dump(args[0], args[1]);
else
{
commandResult << "wrong number of arguments";
outputCommandError("wrong number of arguments", myCommand);
return;
}
}
@ -929,8 +1025,13 @@ void DebuggerParser::executeDump()
// "exec"
void DebuggerParser::executeExec()
{
FilesystemNode file(argStrings[0]);
commandResult << exec(file);
// Append 'script' extension when necessary
string file = argStrings[0];
if(file.find_last_of('.') == string::npos)
file += ".script";
FilesystemNode node(debugger.myOSystem.defaultSaveDir() + file);
commandResult << exec(node);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -976,12 +1077,12 @@ void DebuggerParser::executeGfx()
{
if(argCount != 2)
{
commandResult << red("Specify start and end of range only");
outputCommandError("specify start and end of range only", myCommand);
return;
}
else if(args[1] < args[0])
{
commandResult << red("Start address must be <= end address");
commandResult << red("start address must be <= end address");
return;
}
@ -1061,26 +1162,21 @@ void DebuggerParser::executeListbreaks()
if(debugger.breakPoints().isSet(i))
{
buf << debugger.cartDebug().getLabel(i, true, 4) << " ";
if(! (++count % 8) ) buf << "\n";
if(! (++count % 8) ) buf << endl;
}
}
/*
if(count)
return ret;
else
return "no breakpoints set";
*/
if(count)
commandResult << "breaks:\n" << buf.str();
commandResult << "breaks:" << endl << buf.str();
StringList conds = debugger.cpuDebug().m6502().getCondBreakNames();
if(conds.size() > 0)
{
commandResult << "\nbreakifs:\n";
if(count)
commandResult << endl;
commandResult << "breakifs:" << endl;
for(uInt32 i = 0; i < conds.size(); i++)
{
commandResult << i << ": " << conds[i];
commandResult << Base::toString(i) << ": " << conds[i];
if(i != (conds.size() - 1)) commandResult << endl;
}
}
@ -1118,11 +1214,27 @@ void DebuggerParser::executeListfunctions()
// "listtraps"
void DebuggerParser::executeListtraps()
{
if(myTraps.size() > 0)
StringList names = debugger.cpuDebug().m6502().getCondTrapNames();
if(myTraps.size() != names.size())
{
bool enabled = true;
for(const auto& trap: myTraps)
commandResult << trapStatus(trap, enabled) << " + mirrors" << endl;
commandResult << "Internal error! Different trap sizes.";
return;
}
if (names.size() > 0)
{
bool trapFound = false, trapifFound = false;
for(uInt32 i = 0; i < names.size(); i++)
if(names[i] == "")
trapFound = true;
else
trapifFound = true;
if(trapFound)
listTraps(false);
if(trapifFound)
listTraps(true);
}
else
commandResult << "no traps set";
@ -1175,12 +1287,12 @@ void DebuggerParser::executePGfx()
{
if(argCount != 2)
{
commandResult << red("Specify start and end of range only");
outputCommandError("specify start and end of range only", myCommand);
return;
}
else if(args[1] < args[0])
{
commandResult << red("Start address must be <= end address");
commandResult << red("start address must be <= end address");
return;
}
@ -1267,12 +1379,12 @@ void DebuggerParser::executeRow()
{
if(argCount != 2)
{
commandResult << red("Specify start and end of range only");
outputCommandError("specify start and end of range only", myCommand);
return;
}
else if(args[1] < args[0])
{
commandResult << red("Start address must be <= end address");
commandResult << red("start address must be <= end address");
return;
}
@ -1373,10 +1485,7 @@ void DebuggerParser::executeS()
// "save"
void DebuggerParser::executeSave()
{
if(saveScriptFile(argStrings[0]))
commandResult << "saved script to file " << argStrings[0];
else
commandResult << red("I/O error");
commandResult << saveScriptFile(argStrings[0]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1473,71 +1582,153 @@ void DebuggerParser::executeTrace()
// "trap"
void DebuggerParser::executeTrap()
{
if(argCount > 2)
{
commandResult << red("Command takes one or two arguments") << endl;
return;
}
executeTraps(true, true, "trap");
}
uInt32 beg = args[0];
uInt32 end = argCount == 2 ? args[1] : beg;
if(beg > 0xFFFF || end > 0xFFFF)
{
commandResult << red("One or more addresses are invalid") << endl;
return;
}
for(uInt32 addr = beg; addr <= end; ++addr)
executeTrapRW(addr, true, true);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "trapif"
void DebuggerParser::executeTrapif()
{
executeTraps(true, true, "trapif", true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "trapread"
void DebuggerParser::executeTrapread()
{
if(argCount > 2)
{
commandResult << red("Command takes one or two arguments") << endl;
return;
}
executeTraps(true, false, "trapread");
}
uInt32 beg = args[0];
uInt32 end = argCount == 2 ? args[1] : beg;
if(beg > 0xFFFF || end > 0xFFFF)
{
commandResult << red("One or more addresses are invalid") << endl;
return;
}
for(uInt32 addr = beg; addr <= end; ++addr)
executeTrapRW(addr, true, false);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "trapreadif"
void DebuggerParser::executeTrapreadif()
{
executeTraps(true, false, "trapreadif", true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "trapwrite"
void DebuggerParser::executeTrapwrite()
{
if(argCount > 2)
{
commandResult << red("Command takes one or two arguments") << endl;
return;
}
uInt32 beg = args[0];
uInt32 end = argCount == 2 ? args[1] : beg;
if(beg > 0xFFFF || end > 0xFFFF)
{
commandResult << red("One or more addresses are invalid") << endl;
return;
}
for(uInt32 addr = beg; addr <= end; ++addr)
executeTrapRW(addr, false, true);
executeTraps(false, true, "trapwrite");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// wrapper function for trap/trapread/trapwrite commands
void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
// "trapwriteif"
void DebuggerParser::executeTrapwriteif()
{
executeTraps(false, true, "trapwriteif", true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Wrapper function for trap(if)s
void DebuggerParser::executeTraps(bool read, bool write, const string& command,
bool hasCond)
{
uInt32 ofs = hasCond ? 1 : 0;
uInt32 begin = args[ofs];
uInt32 end = argCount == 2 + ofs ? args[1 + ofs] : begin;
if(argCount < 1 + ofs)
{
outputCommandError("missing required argument(s)", myCommand);
return;
}
if(argCount > 2 + ofs)
{
outputCommandError("too many arguments", myCommand);
return;
}
if(begin > 0xFFFF || end > 0xFFFF)
{
commandResult << red("invalid word argument(s) (must be 0-$ffff)");
return;
}
if(begin > end)
{
commandResult << red("start address must be <= end address");
return;
}
// base addresses of mirrors
uInt32 beginRead = debugger.getBaseAddress(begin, true);
uInt32 endRead = debugger.getBaseAddress(end, true);
uInt32 beginWrite = debugger.getBaseAddress(begin, false);
uInt32 endWrite = debugger.getBaseAddress(end, false);
stringstream conditionBuf;
// parenthesize provided and address range condition(s) (begin)
if(hasCond)
conditionBuf << "(" << argStrings[0] << ")&&(";
// add address range condition(s) to provided condition
if(read)
{
if(beginRead != endRead)
conditionBuf << "__lastread>=" << Base::toString(beginRead) << "&&__lastread<=" << Base::toString(endRead);
else
conditionBuf << "__lastread==" << Base::toString(beginRead);
}
if(read && write)
conditionBuf << "||";
if(write)
{
if(beginWrite != endWrite)
conditionBuf << "__lastwrite>=" << Base::toString(beginWrite) << "&&__lastwrite<=" << Base::toString(endWrite);
else
conditionBuf << "__lastwrite==" << Base::toString(beginWrite);
}
// parenthesize provided condition (end)
if(hasCond)
conditionBuf << ")";
const string condition = conditionBuf.str();
int res = YaccParser::parse(condition.c_str());
if(res == 0)
{
// duplicates will remove each other
bool add = true;
for(uInt32 i = 0; i < myTraps.size(); i++)
{
if(myTraps[i]->begin == begin && myTraps[i]->end == end &&
myTraps[i]->read == read && myTraps[i]->write == write &&
myTraps[i]->condition == condition)
{
if(debugger.cpuDebug().m6502().delCondTrap(i))
{
add = false;
// @sa666666: please check this:
Vec::removeAt(myTraps, i);
commandResult << "Removed trap " << Base::toString(i);
break;
}
commandResult << "Internal error! Duplicate trap removal failed!";
return;
}
}
if(add)
{
uInt32 ret = debugger.cpuDebug().m6502().addCondTrap(
YaccParser::getResult(), hasCond ? argStrings[0] : "");
commandResult << "Added trap " << Base::toString(ret);
// @sa666666: please check this:
myTraps.emplace_back(new Trap{ read, write, begin, end, condition });
}
for(uInt32 addr = begin; addr <= end; ++addr)
executeTrapRW(addr, read, write, add);
}
else
{
commandResult << red("invalid expression");
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// wrapper function for trap(if)/trapread(if)/trapwrite(if) commands
void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write, bool add)
{
switch(debugger.cartDebug().addressType(addr))
{
@ -1547,10 +1738,11 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{
if((i & 0x1080) == 0x0000)
{
if(read && (i & 0x000F) == addr)
debugger.toggleReadTrap(i);
if(write && (i & 0x003F) == addr)
debugger.toggleWriteTrap(i);
// @sa666666: This seems wrong. E.g. trapread 40 4f will never trigger
if(read && (i & 0x000F) == (addr & 0x000F))
add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
if(write && (i & 0x003F) == (addr & 0x003F))
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
}
}
break;
@ -1559,10 +1751,12 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{
for(uInt32 i = 0; i <= 0xFFFF; ++i)
{
if((i & 0x1080) == 0x0080 && (i & 0x0200) != 0x0000 && (i & 0x02FF) == addr)
if((i & 0x1280) == 0x0280 && (i & 0x029F) == (addr & 0x029F))
{
if(read) debugger.toggleReadTrap(i);
if(write) debugger.toggleWriteTrap(i);
if(read)
add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
if(write)
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
}
}
break;
@ -1571,10 +1765,12 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{
for(uInt32 i = 0; i <= 0xFFFF; ++i)
{
if((i & 0x1080) == 0x0080 && (i & 0x0200) == 0x0000 && (i & 0x00FF) == addr)
if((i & 0x1280) == 0x0080 && (i & 0x00FF) == (addr & 0x00FF))
{
if(read) debugger.toggleReadTrap(i);
if(write) debugger.toggleWriteTrap(i);
if(read)
add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
if(write)
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
}
}
break;
@ -1587,21 +1783,16 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{
if((i % 0x2000 >= 0x1000) && (i & 0x0FFF) == (addr & 0x0FFF))
{
if(read) debugger.toggleReadTrap(i);
if(write) debugger.toggleWriteTrap(i);
if(read)
add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
if(write)
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
}
}
}
break;
}
}
bool trapEnabled = false;
const string& result = trapStatus(addr, trapEnabled);
if(trapEnabled) myTraps.insert(addr);
else myTraps.erase(addr);
commandResult << result << " + mirrors" << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1646,6 +1837,19 @@ void DebuggerParser::executeUndef()
commandResult << red("no such label");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "unwind"
void DebuggerParser::executeUnwind()
{
if(debugger.unwindState())
{
debugger.rom().invalidate();
commandResult << "unwind by one level";
}
else
commandResult << "no states left to rewind";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "v"
void DebuggerParser::executeV()
@ -1688,7 +1892,6 @@ void DebuggerParser::executeZ()
debugger.cpuDebug().setZ(args[0]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// List of all commands available to the parser
DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
@ -1883,6 +2086,16 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeDelfunction)
},
{
"deltrap",
"Delete trap <xx>",
"Example: deltrap 0",
true,
false,
{ kARG_WORD, kARG_END_ARGS },
std::mem_fn(&DebuggerParser::executeDeltrap)
},
{
"delwatch",
"Delete watch <xx>",
@ -1907,7 +2120,7 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
{
"dump",
"Dump data at address <xx> [to yy]",
"Examples:\n"
"Example:\n"
" dump f000 - dumps 128 bytes @ f000\n"
" dump f000 f0ff - dumps all bytes from f000 to f0ff",
true,
@ -1949,7 +2162,7 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
{
"function",
"Define function name xx for expression yy",
"Example: define FUNC1 { ... }",
"Example: function FUNC1 { ... }",
true,
false,
{ kARG_LABEL, kARG_WORD, kARG_END_ARGS },
@ -2094,7 +2307,7 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
"Example: print pc, print f000",
true,
false,
{ kARG_WORD, kARG_END_ARGS },
{ kARG_DWORD, kARG_END_ARGS },
std::mem_fn(&DebuggerParser::executePrint)
},
@ -2325,6 +2538,17 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeTrap)
},
{
"trapif",
"On <condition> trap R/W access to address(es) xx [yy]",
"Set a conditional R/W trap on the given address(es) and all mirrors\nCondition can include multiple items.\n"
"Example: trapif _scan>#100 GRP0, trapif _bank==1 f000 f100",
true,
false,
{ kARG_WORD, kARG_MULTI_BYTE },
std::mem_fn(&DebuggerParser::executeTrapif)
},
{
"trapread",
"Trap read access to address(es) xx [yy]",
@ -2336,6 +2560,17 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeTrapread)
},
{
"trapreadif",
"On <condition> trap read access to address(es) xx [yy]",
"Set a conditional read trap on the given address(es) and all mirrors\nCondition can include multiple items.\n"
"Example: trapreadif _scan>#100 GRP0, trapreadif _bank==1 f000 f100",
true,
false,
{ kARG_WORD, kARG_MULTI_BYTE },
std::mem_fn(&DebuggerParser::executeTrapreadif)
},
{
"trapwrite",
"Trap write access to address(es) xx [yy]",
@ -2347,6 +2582,17 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeTrapwrite)
},
{
"trapwriteif",
"On <condition> trap write access to address(es) xx [yy]",
"Set a conditional write trap on the given address(es) and all mirrors\nCondition can include multiple items.\n"
"Example: trapwriteif _scan>#100 GRP0, trapwriteif _bank==1 f000 f100",
true,
false,
{ kARG_WORD, kARG_MULTI_BYTE },
std::mem_fn(&DebuggerParser::executeTrapwriteif)
},
{
"type",
"Show disassembly type for address xx [yy]",
@ -2378,6 +2624,16 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeUndef)
},
{
"unwind",
"Unwind state to last step/trace/scanline/frame",
"Unwind currently only works in the debugger",
false,
true,
{ kARG_END_ARGS },
std::mem_fn(&DebuggerParser::executeUnwind)
},
{
"v",
"Overflow Flag: set (0 or 1), or toggle (no arg)",

View File

@ -39,7 +39,7 @@ class DebuggerParser
string run(const string& command);
/** Execute parser commands given in 'file' */
string exec(const FilesystemNode& file);
string exec(const FilesystemNode& file, StringList* history = nullptr);
/** Given a substring, determine matching substrings from the list
of available commands. Used in the debugger prompt for tab-completion */
@ -65,10 +65,10 @@ class DebuggerParser
bool getArgs(const string& command, string& verb);
bool validateArgs(int cmd);
string eval();
bool saveScriptFile(string file);
string saveScriptFile(string file);
private:
enum { kNumCommands = 72 };
enum { kNumCommands = 77 };
// Constants for argument processing
enum {
@ -80,6 +80,7 @@ class DebuggerParser
enum parameters {
kARG_WORD, // single 16-bit value
kARG_DWORD, // single 32-bit value
kARG_MULTI_WORD, // multiple 16-bit values (must occur last)
kARG_BYTE, // single 8-bit value
kARG_MULTI_BYTE, // multiple 8-bit values (must occur last)
@ -99,6 +100,14 @@ class DebuggerParser
parameters parms[10];
std::function<void (DebuggerParser*)> executor;
};
struct Trap
{
bool read;
bool write;
uInt32 begin;
uInt32 end;
string condition;
};
// Reference to our debugger object
Debugger& debugger;
@ -109,6 +118,8 @@ class DebuggerParser
// The results of the currently running command
ostringstream commandResult;
// currently execute command id
int myCommand;
// Arguments in 'int' and 'string' format for the currently running command
IntArray args;
StringList argStrings;
@ -117,8 +128,12 @@ class DebuggerParser
StringList myWatches;
// Keep track of traps (read and/or write)
std::set<uInt32> myTraps;
string trapStatus(uInt32 addr, bool& enabled);
vector<unique_ptr<Trap>> myTraps;
void listTraps(bool listCond);
string trapStatus(const Trap& trap);
// output the error with the example provided for the command
void outputCommandError(const string& errorMsg, int command);
// List of available command methods
void executeA();
@ -140,6 +155,7 @@ class DebuggerParser
void executeDefine();
void executeDelbreakif();
void executeDelfunction();
void executeDeltrap();
void executeDelwatch();
void executeDisasm();
void executeDump();
@ -183,12 +199,17 @@ class DebuggerParser
void executeTia();
void executeTrace();
void executeTrap();
void executeTrapif();
void executeTrapread();
void executeTrapreadif();
void executeTrapwrite();
void executeTrapRW(uInt32 addr, bool read, bool write); // not exposed by debugger
void executeTrapwriteif();
void executeTraps(bool read, bool write, const string& command, bool cond = false);
void executeTrapRW(uInt32 addr, bool read, bool write, bool add = true); // not exposed by debugger
void executeType();
void executeUHex();
void executeUndef();
void executeUnwind();
void executeV();
void executeWatch();
void executeX();

View File

@ -120,6 +120,12 @@ void DiStella::disasm(uInt32 distart, int pass)
myPC = distart - myOffset;
while (myPC <= myAppData.end) {
// since -1 is used in m6502.m4 for clearing the last peek
// and this results into an access at e.g. 0xffff,
// we have to fix the consequences here (ugly!).
if(myPC == myAppData.end)
goto FIX_LAST;
if (checkBits(myPC, CartDebug::GFX | CartDebug::PGFX,
CartDebug::CODE)) {
if (pass == 2)
@ -137,6 +143,7 @@ void DiStella::disasm(uInt32 distart, int pass)
myPC++;
} else if (checkBits(myPC, CartDebug::ROW,
CartDebug::CODE | CartDebug::DATA | CartDebug::GFX | CartDebug::PGFX)) {
FIX_LAST:
if (pass == 2)
mark(myPC + myOffset, CartDebug::VALID_ENTRY);
@ -713,6 +720,7 @@ void DiStella::disasmFromAddress(uInt32 distart)
myPCEnd = myAppData.end + myOffset;
return;
}
break; // TODO - is this the intent?
default:
break;

View File

@ -349,8 +349,8 @@ bool TIADebug::collision(CollisionBit id) const
case Cx_BLPF: return myTIA.collCXBLPF() & 0x80;
case Cx_P0P1: return myTIA.collCXPPMM() & 0x80;
case Cx_M0M1: return myTIA.collCXPPMM() & 0x40;
default: return false; // make compiler happy
}
return false; // make compiler happy
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -703,13 +703,13 @@ int TIADebug::frameCycles() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int TIADebug::cyclesLo() const
{
return (int)myTIA.cycles();
return int(myTIA.cycles());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int TIADebug::cyclesHi() const
{
return (int)(myTIA.cycles() >> 32);
return int(myTIA.cycles() >> 32);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -730,6 +730,12 @@ int TIADebug::clocksThisLine() const
return myTIA.clocksThisLine();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int TIADebug::cyclesThisLine() const
{
return myTIA.clocksThisLine()/3;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::vsync() const
{

View File

@ -163,6 +163,7 @@ class TIADebug : public DebuggerSystem
int cyclesLo() const;
int cyclesHi() const;
int clocksThisLine() const;
int cyclesThisLine() const;
bool vsync() const;
bool vblank() const;
int vsyncAsInt() const { return int(vsync()); } // so we can use _vsync pseudo-register

View File

@ -0,0 +1,59 @@
//============================================================================
//
// 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-2017 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 TRAP_ARRAY_HXX
#define TRAP_ARRAY_HXX
#include "bspf.hxx"
class TrapArray
{
public:
TrapArray() : myInitialized(false) {}
bool isSet(const uInt16 address) const { return myCount[address]; }
bool isClear(const uInt16 address) const { return myCount[address] == 0; }
void add(const uInt16 address) { myCount[address]++; }
void remove(const uInt16 address) { myCount[address]--; }
//void toggle(uInt16 address) { myCount[address] ? remove(address) : add(address); } // TODO condition
void initialize() {
if(!myInitialized)
memset(myCount, 0, sizeof(myCount));
myInitialized = true;
}
void clearAll() { myInitialized = false; memset(myCount, 0, sizeof(myCount)); }
bool isInitialized() const { return myInitialized; }
private:
// The actual counts
uInt8 myCount[0x10000];
// Indicates whether we should treat this array as initialized
bool myInitialized;
private:
// Following constructors and assignment operators not supported
TrapArray(const TrapArray&) = delete;
TrapArray(TrapArray&&) = delete;
TrapArray& operator=(const TrapArray&) = delete;
TrapArray& operator=(TrapArray&&) = delete;
};
#endif

View File

@ -0,0 +1,30 @@
//============================================================================
//
// 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-2017 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 "AmigaMouseWidget.hxx"
AmigaMouseWidget::AmigaMouseWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: PointingDeviceWidget(boss, font, x, y, controller)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 AmigaMouseWidget::getGrayCodeTable(const int index, const int direction)
{
return myGrayCodeTable[index];
}

View File

@ -0,0 +1,45 @@
//============================================================================
//
// 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-2017 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 AMIGAMOUSE_WIDGET_HXX
#define AMIGAMOUSE_WIDGET_HXX
#include "Control.hxx"
#include "PointingDeviceWidget.hxx"
class AmigaMouseWidget : public PointingDeviceWidget
{
public:
AmigaMouseWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
Controller& controller);
virtual ~AmigaMouseWidget() = default;
private:
uInt8 myGrayCodeTable[4] = { 0b00, 0b10, 0b11, 0b01 };
uInt8 getGrayCodeTable(const int index, const int direction) override;
// Following constructors and assignment operators not supported
AmigaMouseWidget() = delete;
AmigaMouseWidget(const AmigaMouseWidget&) = delete;
AmigaMouseWidget(AmigaMouseWidget&&) = delete;
AmigaMouseWidget& operator=(const AmigaMouseWidget&) = delete;
AmigaMouseWidget& operator=(AmigaMouseWidget&&) = delete;
};
#endif

View File

@ -0,0 +1,30 @@
//============================================================================
//
// 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-2017 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 "AtariMouseWidget.hxx"
AtariMouseWidget::AtariMouseWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: PointingDeviceWidget(boss, font, x, y, controller)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 AtariMouseWidget::getGrayCodeTable(const int index, const int direction)
{
return myGrayCodeTable[index];
}

View File

@ -0,0 +1,45 @@
//============================================================================
//
// 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-2017 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 ATARIMOUSE_WIDGET_HXX
#define ATARIMOUSE_WIDGET_HXX
#include "Control.hxx"
#include "PointingDeviceWidget.hxx"
class AtariMouseWidget : public PointingDeviceWidget
{
public:
AtariMouseWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
Controller& controller);
virtual ~AtariMouseWidget() = default;
private:
uInt8 myGrayCodeTable[4] = { 0b00, 0b01, 0b11, 0b10 };
uInt8 getGrayCodeTable(const int index, const int direction) override;
// Following constructors and assignment operators not supported
AtariMouseWidget() = delete;
AtariMouseWidget(const AtariMouseWidget&) = delete;
AtariMouseWidget(AtariMouseWidget&&) = delete;
AtariMouseWidget& operator=(const AtariMouseWidget&) = delete;
AtariMouseWidget& operator=(AtariMouseWidget&&) = delete;
};
#endif

View File

@ -16,56 +16,28 @@
//============================================================================
#include "AtariVox.hxx"
#include "MT24LC256.hxx"
#include "AtariVoxWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AtariVoxWidget::AtariVoxWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
: FlashWidget(boss, font, x, y, controller)
{
bool leftport = myController.jack() == Controller::Left;
const string& label = leftport ? "Left (AtariVox)" : "Right (AtariVox)";
const int fontHeight = font.getFontHeight(),
lineHeight = font.getLineHeight(),
bwidth = font.getStringWidth("Erase EEPROM") + 20,
bheight = lineHeight + 4;
int xpos = x, ypos = y, lwidth = font.getStringWidth("Right (AtariVox)");
StaticTextWidget* t;
t = new StaticTextWidget(boss, font, xpos, ypos+2, lwidth,
fontHeight, label, kTextAlignLeft);
ypos += t->getHeight() + 20;
myEEPROMErase =
new ButtonWidget(boss, font, xpos+10, ypos, bwidth, bheight,
"Erase EEPROM", kEEPROMErase);
myEEPROMErase->setTarget(this);
ypos += lineHeight + 20;
const GUI::Font& ifont = instance().frameBuffer().infoFont();
lwidth = ifont.getMaxCharWidth() * 20;
new StaticTextWidget(boss, ifont, xpos, ypos, lwidth,
fontHeight, "(*) This will erase", kTextAlignLeft);
ypos += lineHeight + 2;
new StaticTextWidget(boss, ifont, xpos, ypos, lwidth,
fontHeight, "all EEPROM data, not", kTextAlignLeft);
ypos += lineHeight + 2;
new StaticTextWidget(boss, ifont, xpos, ypos, lwidth,
fontHeight, "just the range used", kTextAlignLeft);
ypos += lineHeight + 2;
new StaticTextWidget(boss, ifont, xpos, ypos, lwidth,
fontHeight, "for this ROM", kTextAlignLeft);
init(boss, font, x, y);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AtariVoxWidget::handleCommand(CommandSender*, int cmd, int, int)
void AtariVoxWidget::eraseCurrent()
{
if(cmd == kEEPROMErase)
{
AtariVox& avox = static_cast<AtariVox&>(myController);
avox.myEEPROM->erase();
}
AtariVox& avox = static_cast<AtariVox&>(myController);
avox.eraseCurrent();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool AtariVoxWidget::isPageUsed(uInt32 page)
{
AtariVox& avox = static_cast<AtariVox&>(myController);
return avox.isPageUsed(page);
}

View File

@ -18,12 +18,10 @@
#ifndef ATARIVOX_WIDGET_HXX
#define ATARIVOX_WIDGET_HXX
class ButtonWidget;
#include "Control.hxx"
#include "ControllerWidget.hxx"
#include "FlashWidget.hxx"
class AtariVoxWidget : public ControllerWidget
class AtariVoxWidget : public FlashWidget
{
public:
AtariVoxWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
@ -31,12 +29,8 @@ class AtariVoxWidget : public ControllerWidget
virtual ~AtariVoxWidget() = default;
private:
ButtonWidget* myEEPROMErase;
enum { kEEPROMErase = 'eeER' };
private:
void loadConfig() override { }
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
void eraseCurrent() override;
bool isPageUsed(uInt32 page) override;
// Following constructors and assignment operators not supported
AtariVoxWidget() = delete;

View File

@ -24,8 +24,7 @@ BoosterWidget::BoosterWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
{
bool leftport = myController.jack() == Controller::Left;
const string& label = leftport ? "Left (Booster)" : "Right (Booster)";
const string& label = isLeftPort() ? "Left (Booster)" : "Right (Booster)";
const int fontHeight = font.getFontHeight();
int xpos = x, ypos = y, lwidth = font.getStringWidth("Right (Booster)");
@ -34,39 +33,46 @@ BoosterWidget::BoosterWidget(GuiObject* boss, const GUI::Font& font,
t = new StaticTextWidget(boss, font, xpos, ypos+2, lwidth,
fontHeight, label, kTextAlignLeft);
xpos += t->getWidth()/2 - 5; ypos += t->getHeight() + 10;
myPins[kJUp] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJUp] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJUp]->setID(kJUp);
myPins[kJUp]->setTarget(this);
ypos += myPins[kJUp]->getHeight() * 2 + 10;
myPins[kJDown] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJDown] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJDown]->setID(kJDown);
myPins[kJDown]->setTarget(this);
xpos -= myPins[kJUp]->getWidth() + 5;
ypos -= myPins[kJUp]->getHeight() + 5;
myPins[kJLeft] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJLeft] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJLeft]->setID(kJLeft);
myPins[kJLeft]->setTarget(this);
xpos += (myPins[kJUp]->getWidth() + 5) * 2;
myPins[kJRight] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJRight] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJRight]->setID(kJRight);
myPins[kJRight]->setTarget(this);
xpos -= (myPins[kJUp]->getWidth() + 5) * 2;
ypos = 20 + (myPins[kJUp]->getHeight() + 10) * 3;
myPins[kJFire] = new CheckboxWidget(boss, font, xpos, ypos, "Fire", kCheckActionCmd);
myPins[kJFire] = new CheckboxWidget(boss, font, xpos, ypos, "Fire",
CheckboxWidget::kCheckActionCmd);
myPins[kJFire]->setID(kJFire);
myPins[kJFire]->setTarget(this);
ypos += myPins[kJFire]->getHeight() + 5;
myPins[kJBooster] = new CheckboxWidget(boss, font, xpos, ypos, "Booster", kCheckActionCmd);
myPins[kJBooster] = new CheckboxWidget(boss, font, xpos, ypos, "Booster",
CheckboxWidget::kCheckActionCmd);
myPins[kJBooster]->setID(kJBooster);
myPins[kJBooster]->setTarget(this);
ypos += myPins[kJBooster]->getHeight() + 5;
myPins[kJTrigger] = new CheckboxWidget(boss, font, xpos, ypos, "Trigger", kCheckActionCmd);
myPins[kJTrigger] = new CheckboxWidget(boss, font, xpos, ypos, "Trigger",
CheckboxWidget::kCheckActionCmd);
myPins[kJTrigger]->setID(kJTrigger);
myPins[kJTrigger]->setTarget(this);
}
@ -90,7 +96,7 @@ void BoosterWidget::loadConfig()
void BoosterWidget::handleCommand(
CommandSender* sender, int cmd, int data, int id)
{
if(cmd == kCheckActionCmd)
if(cmd == CheckboxWidget::kCheckActionCmd)
{
switch(id)
{

View File

@ -36,7 +36,7 @@ Cartridge0840Widget::Cartridge0840Widget(
for(uInt32 i = 0, offset = 0xFFC, spot = 0x800; i < 2;
++i, offset += 0x1000, spot += 0x40)
{
uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset];
uInt16 start = uInt16((cart.myImage[offset+1] << 8) | cart.myImage[offset]);
start -= start % 0x1000;
info << "Bank " << i << " @ $" << Common::Base::HEX4 << start << " - "
<< "$" << (start + 0xFFF) << " (hotspot = $" << spot << ")\n";

View File

@ -324,7 +324,7 @@ void CartridgeBUSWidget::loadConfig()
for(int i = 0; i < 3; ++i)
{
alist.push_back(0); vlist.push_back(myCart.myMusicCounters[i]);
changed.push_back(myCart.myMusicCounters[i] != (uInt32)myOldState.mcounters[i]);
changed.push_back(myCart.myMusicCounters[i] != uInt32(myOldState.mcounters[i]));
}
myMusicCounters->setList(alist, vlist, changed);
@ -332,7 +332,7 @@ void CartridgeBUSWidget::loadConfig()
for(int i = 0; i < 3; ++i)
{
alist.push_back(0); vlist.push_back(myCart.myMusicFrequencies[i]);
changed.push_back(myCart.myMusicFrequencies[i] != (uInt32)myOldState.mfreqs[i]);
changed.push_back(myCart.myMusicFrequencies[i] != uInt32(myOldState.mfreqs[i]));
}
myMusicFrequencies->setList(alist, vlist, changed);

View File

@ -24,6 +24,7 @@ class ButtonWidget;
#include "Widget.hxx"
#include "Command.hxx"
class ControllerWidget : public Widget, public CommandSender
{
public:
@ -44,6 +45,19 @@ class ControllerWidget : public Widget, public CommandSender
protected:
Controller& myController;
protected:
bool isLeftPort()
{
bool swappedPorts = instance().console().properties().get(Console_SwapPorts) == "YES";
return (myController.jack() == Controller::Left) ^ swappedPorts;
}
string getHeader()
{
return (isLeftPort() ? "Left (" : "Right (") + myController.name() + ")";
}
private:
virtual void handleCommand(CommandSender* sender, int cmd, int data, int id) override { }

View File

@ -540,9 +540,9 @@ void DataGridWidget::handleCommand(CommandSender* sender, int cmd,
{
switch(cmd)
{
case kSetPositionCmd:
case GuiObject::kSetPositionCmd:
// Chain access; pass to parent
sendCommand(kSetPositionCmd, data, _id);
sendCommand(GuiObject::kSetPositionCmd, data, _id);
break;
case kDGZeroCmd:

View File

@ -92,7 +92,10 @@ void DebuggerDialog::handleKeyDown(StellaKey key, StellaMod mod)
switch(key)
{
case KBDK_R:
doRewind();
if(!instance().eventHandler().kbdShift(mod))
doRewind();
else
doUnwind();
break;
case KBDK_S:
doStep();
@ -141,6 +144,10 @@ void DebuggerDialog::handleCommand(CommandSender* sender, int cmd,
doRewind();
break;
case kDDUnwindCmd:
doUnwind();
break;
case kDDExitCmd:
doExitDebugger();
break;
@ -189,6 +196,12 @@ void DebuggerDialog::doRewind()
instance().debugger().parser().run("rewind");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doUnwind()
{
instance().debugger().parser().run("unwind");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DebuggerDialog::doExitDebugger()
{
@ -375,7 +388,7 @@ void DebuggerDialog::addRomArea()
bwidth, bheight, "Exit", kDDExitCmd);
bwidth = myLFont->getStringWidth("< ") + 4;
bheight = bheight * 5 + 4*4;
bheight = bheight * 3 + 4 * 2;
buttonX -= (bwidth + 5);
buttonY = r.top + 5;
myRewindButton =
@ -383,6 +396,14 @@ void DebuggerDialog::addRomArea()
bwidth, bheight, "<", kDDRewindCmd);
myRewindButton->clearFlags(WIDGET_ENABLED);
buttonY += bheight + 4;
bheight = (myLFont->getLineHeight() + 2) * 2 + 4 * 1;
myUnwindButton =
new ButtonWidget(this, *myLFont, buttonX, buttonY,
bwidth, bheight, ">", kDDUnwindCmd);
myUnwindButton->clearFlags(WIDGET_ENABLED);
int xpos = buttonX - 8*myLFont->getMaxCharWidth() - 20, ypos = 20;
DataGridOpsWidget* ops = new DataGridOpsWidget(this, *myLFont, xpos, ypos);

View File

@ -62,6 +62,7 @@ class DebuggerDialog : public Dialog
CartRamWidget& cartRam() const { return *myCartRam; }
EditTextWidget& message() const { return *myMessageBox; }
ButtonWidget& rewindButton() const { return *myRewindButton; }
ButtonWidget& unwindButton() const { return *myUnwindButton; }
void showFatalMessage(const string& msg);
@ -75,6 +76,7 @@ class DebuggerDialog : public Dialog
void doScanlineAdvance();
void doAdvance();
void doRewind();
void doUnwind();
void doExitDebugger();
void doExitRom();
@ -96,6 +98,7 @@ class DebuggerDialog : public Dialog
kDDAdvCmd = 'DDav',
kDDSAdvCmd = 'DDsv',
kDDRewindCmd = 'DDrw',
kDDUnwindCmd = 'DDuw',
kDDExitCmd = 'DDex',
kDDExitFatalCmd = 'DDer'
};
@ -113,6 +116,7 @@ class DebuggerDialog : public Dialog
CartRamWidget* myCartRam;
EditTextWidget* myMessageBox;
ButtonWidget* myRewindButton;
ButtonWidget* myUnwindButton;
unique_ptr<GUI::MessageBox> myFatalError;
unique_ptr<GUI::Font> myLFont; // used for labels

View File

@ -22,13 +22,12 @@
DrivingWidget::DrivingWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller),
myGreyIndex(0)
myGrayIndex(0)
{
bool leftport = myController.jack() == Controller::Left;
const string& label = leftport ? "Left (Driving)" : "Right (Driving)";
const string& label = getHeader();
const int fontHeight = font.getFontHeight(),
bwidth = font.getStringWidth("Grey code +") + 10,
bwidth = font.getStringWidth("Gray code +") + 10,
bheight = font.getLineHeight() + 4;
int xpos = x, ypos = y, lwidth = font.getStringWidth("Right (Driving)");
StaticTextWidget* t;
@ -37,22 +36,22 @@ DrivingWidget::DrivingWidget(GuiObject* boss, const GUI::Font& font,
fontHeight, label, kTextAlignLeft);
ypos += t->getHeight() + 20;
myGreyUp = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Grey code +", kGreyUpCmd);
myGreyUp->setTarget(this);
myGrayUp = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Gray code +", kGrayUpCmd);
myGrayUp->setTarget(this);
ypos += myGreyUp->getHeight() + 5;
myGreyDown = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Grey code -", kGreyDownCmd);
myGreyDown->setTarget(this);
ypos += myGrayUp->getHeight() + 5;
myGrayDown = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Gray code -", kGrayDownCmd);
myGrayDown->setTarget(this);
xpos += myGreyDown->getWidth() + 10; ypos -= 10;
myGreyValue = new DataGridWidget(boss, font, xpos, ypos,
xpos += myGrayDown->getWidth() + 10; ypos -= 10;
myGrayValue = new DataGridWidget(boss, font, xpos, ypos,
1, 1, 2, 8, Common::Base::F_16);
myGreyValue->setTarget(this);
myGreyValue->setEditable(false);
myGrayValue->setTarget(this);
myGrayValue->setEditable(false);
xpos = x + 30; ypos += myGreyDown->getHeight() + 20;
xpos = x + 30; ypos += myGrayDown->getHeight() + 20;
myFire = new CheckboxWidget(boss, font, xpos, ypos, "Fire", kFireCmd);
myFire->setTarget(this);
}
@ -60,16 +59,16 @@ DrivingWidget::DrivingWidget(GuiObject* boss, const GUI::Font& font,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DrivingWidget::loadConfig()
{
uInt8 grey = 0;
if(myController.read(Controller::One)) grey += 1;
if(myController.read(Controller::Two)) grey += 2;
uInt8 gray = 0;
if(myController.read(Controller::One)) gray += 1;
if(myController.read(Controller::Two)) gray += 2;
for(myGreyIndex = 0; myGreyIndex < 4; ++myGreyIndex)
if(ourGreyTable[myGreyIndex] == grey)
for(myGrayIndex = 0; myGrayIndex < 4; ++myGrayIndex)
if(ourGrayTable[myGrayIndex] == gray)
break;
myFire->setState(!myController.read(Controller::Six));
myGreyValue->setList(0, grey);
setValue();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -78,17 +77,17 @@ void DrivingWidget::handleCommand(
{
switch(cmd)
{
case kGreyUpCmd:
myGreyIndex = (myGreyIndex + 1) % 4;
myController.set(Controller::One, (ourGreyTable[myGreyIndex] & 0x1) != 0);
myController.set(Controller::Two, (ourGreyTable[myGreyIndex] & 0x2) != 0);
myGreyValue->setList(0, ourGreyTable[myGreyIndex]);
case kGrayUpCmd:
myGrayIndex = (myGrayIndex + 1) % 4;
myController.set(Controller::One, (ourGrayTable[myGrayIndex] & 0x1) != 0);
myController.set(Controller::Two, (ourGrayTable[myGrayIndex] & 0x2) != 0);
setValue();
break;
case kGreyDownCmd:
myGreyIndex = myGreyIndex == 0 ? 3 : myGreyIndex - 1;
myController.set(Controller::One, (ourGreyTable[myGreyIndex] & 0x1) != 0);
myController.set(Controller::Two, (ourGreyTable[myGreyIndex] & 0x2) != 0);
myGreyValue->setList(0, ourGreyTable[myGreyIndex]);
case kGrayDownCmd:
myGrayIndex = myGrayIndex == 0 ? 3 : myGrayIndex - 1;
myController.set(Controller::One, (ourGrayTable[myGrayIndex] & 0x1) != 0);
myController.set(Controller::Two, (ourGrayTable[myGrayIndex] & 0x2) != 0);
setValue();
break;
case kFireCmd:
myController.set(Controller::Six, !myFire->getState());
@ -97,4 +96,12 @@ void DrivingWidget::handleCommand(
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 DrivingWidget::ourGreyTable[4] = { 0x03, 0x01, 0x00, 0x02 };
void DrivingWidget::setValue()
{
int grayCode = ourGrayTable[myGrayIndex];
// FIXME * 8 = a nasty hack, because the DataGridWidget does not support 2 digit binary output
myGrayValue->setList(0, (grayCode & 0b01) + (grayCode & 0b10) * 8);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 DrivingWidget::ourGrayTable[4] = { 0x03, 0x01, 0x00, 0x02 };

View File

@ -35,22 +35,24 @@ class DrivingWidget : public ControllerWidget
private:
enum {
kGreyUpCmd = 'DWup',
kGreyDownCmd = 'DWdn',
kGrayUpCmd = 'DWup',
kGrayDownCmd = 'DWdn',
kFireCmd = 'DWfr'
};
ButtonWidget *myGreyUp, *myGreyDown;
DataGridWidget* myGreyValue;
ButtonWidget *myGrayUp, *myGrayDown;
DataGridWidget* myGrayValue;
CheckboxWidget* myFire;
int myGreyIndex;
int myGrayIndex;
static uInt8 ourGreyTable[4];
static uInt8 ourGrayTable[4];
private:
void loadConfig() override;
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
void setValue();
// Following constructors and assignment operators not supported
DrivingWidget() = delete;
DrivingWidget(const DrivingWidget&) = delete;

View File

@ -0,0 +1,105 @@
//============================================================================
//
// 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-2017 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 "FlashWidget.hxx"
#include "MT24LC256.hxx"
#include "Base.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FlashWidget::FlashWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FlashWidget::init(GuiObject* boss, const GUI::Font& font, int x, int y)
{
const GUI::Font& ifont = instance().frameBuffer().infoFont();
const int lineHeight = font.getLineHeight();
int xpos = x, ypos = y;
new StaticTextWidget(boss, font, xpos, ypos + 2, getHeader());
ypos += lineHeight + 6;
new StaticTextWidget(boss, ifont, xpos, ypos, "Pages/Ranges used:");
ypos += lineHeight + 2;
xpos += 8;
for(uInt32 page = 0; page < MAX_PAGES; ++page)
{
myPage[page] = new StaticTextWidget(boss, ifont, xpos, ypos,
page ? " " : "none ");
ypos += lineHeight;
}
xpos -= 8; ypos += 2;
myEEPROMEraseCurrent = new ButtonWidget(boss, font, xpos, ypos,
"Erase used pages", kEEPROMEraseCurrent);
myEEPROMEraseCurrent->setTarget(this);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FlashWidget::handleCommand(CommandSender*, int cmd, int, int)
{
if(cmd == kEEPROMEraseCurrent) {
eraseCurrent();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// display the pages used by the current ROM and update erase button status
void FlashWidget::loadConfig()
{
int useCount = 0, startPage = -1;
for(uInt32 page = 0; page < MT24LC256::PAGE_NUM; ++page)
{
if(isPageUsed(page))
{
if (startPage == -1)
startPage = page;
}
else
{
if(startPage != -1)
{
int from = startPage * MT24LC256::PAGE_SIZE;
int to = page * MT24LC256::PAGE_SIZE - 1;
ostringstream label;
label.str("");
label << Common::Base::HEX3 << startPage;
if(int(page) - 1 != startPage)
label << "-" << Common::Base::HEX3 << page - 1;
else
label << " ";
label << ": " << Common::Base::HEX4 << from << "-" << Common::Base::HEX4 << to;
myPage[useCount]->setLabel(label.str());
startPage = -1;
if(++useCount == MAX_PAGES)
break;
}
}
}
myEEPROMEraseCurrent->setEnabled(useCount != 0);
}

View File

@ -0,0 +1,65 @@
//============================================================================
//
// 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-2017 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 FLASH_WIDGET_HXX
#define FLASH_WIDGET_HXX
class ButtonWidget;
#include "Control.hxx"
#include "ControllerWidget.hxx"
class FlashWidget : public ControllerWidget
{
public:
FlashWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
Controller& controller);
virtual ~FlashWidget() = default;
protected:
void init(GuiObject* boss, const GUI::Font& font, int x, int y);
private:
ButtonWidget* myEEPROMEraseCurrent;
enum { kEEPROMEraseCurrent = 'eeEC' };
static constexpr uInt32 MAX_PAGES = 5;
StaticTextWidget* myPage[MAX_PAGES];
private:
void loadConfig() override;
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
/**
Erase the EEPROM pages used by the current ROM
*/
virtual void eraseCurrent() = 0;
/**
Check if a page is used by the current ROM
*/
virtual bool isPageUsed(uInt32 page) = 0;
// Following constructors and assignment operators not supported
FlashWidget() = delete;
FlashWidget(const FlashWidget&) = delete;
FlashWidget(FlashWidget&&) = delete;
FlashWidget& operator=(const FlashWidget&) = delete;
FlashWidget& operator=(FlashWidget&&) = delete;
};
#endif

View File

@ -24,8 +24,7 @@ GenesisWidget::GenesisWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
{
bool leftport = myController.jack() == Controller::Left;
const string& label = leftport ? "Left (Genesis)" : "Right (Genesis)";
const string& label = getHeader();
const int fontHeight = font.getFontHeight();
int xpos = x, ypos = y, lwidth = font.getStringWidth("Right (Genesis)");
@ -34,34 +33,40 @@ GenesisWidget::GenesisWidget(GuiObject* boss, const GUI::Font& font,
t = new StaticTextWidget(boss, font, xpos, ypos+2, lwidth,
fontHeight, label, kTextAlignLeft);
xpos += t->getWidth()/2 - 5; ypos += t->getHeight() + 20;
myPins[kJUp] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJUp] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJUp]->setID(kJUp);
myPins[kJUp]->setTarget(this);
ypos += myPins[kJUp]->getHeight() * 2 + 10;
myPins[kJDown] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJDown] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJDown]->setID(kJDown);
myPins[kJDown]->setTarget(this);
xpos -= myPins[kJUp]->getWidth() + 5;
ypos -= myPins[kJUp]->getHeight() + 5;
myPins[kJLeft] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJLeft] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJLeft]->setID(kJLeft);
myPins[kJLeft]->setTarget(this);
xpos += (myPins[kJUp]->getWidth() + 5) * 2;
myPins[kJRight] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJRight] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJRight]->setID(kJRight);
myPins[kJRight]->setTarget(this);
xpos -= (myPins[kJUp]->getWidth() + 5) * 2;
ypos = 30 + (myPins[kJUp]->getHeight() + 10) * 3;
myPins[kJBbtn] = new CheckboxWidget(boss, font, xpos, ypos, "B button", kCheckActionCmd);
myPins[kJBbtn] = new CheckboxWidget(boss, font, xpos, ypos, "B button",
CheckboxWidget::kCheckActionCmd);
myPins[kJBbtn]->setID(kJBbtn);
myPins[kJBbtn]->setTarget(this);
ypos += myPins[kJBbtn]->getHeight() + 5;
myPins[kJCbtn] = new CheckboxWidget(boss, font, xpos, ypos, "C button", kCheckActionCmd);
myPins[kJCbtn] = new CheckboxWidget(boss, font, xpos, ypos, "C button",
CheckboxWidget::kCheckActionCmd);
myPins[kJCbtn]->setID(kJCbtn);
myPins[kJCbtn]->setTarget(this);
}
@ -83,7 +88,7 @@ void GenesisWidget::loadConfig()
void GenesisWidget::handleCommand(
CommandSender* sender, int cmd, int data, int id)
{
if(cmd == kCheckActionCmd)
if(cmd == CheckboxWidget::kCheckActionCmd)
{
switch(id)
{

View File

@ -24,9 +24,7 @@ JoystickWidget::JoystickWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
{
bool leftport = myController.jack() == Controller::Left;
const string& label = leftport ? "Left (Joystick)" : "Right (Joystick)";
const string& label = getHeader();
const int fontHeight = font.getFontHeight();
int xpos = x, ypos = y, lwidth = font.getStringWidth("Right (Joystick)");
StaticTextWidget* t;
@ -34,29 +32,34 @@ JoystickWidget::JoystickWidget(GuiObject* boss, const GUI::Font& font,
t = new StaticTextWidget(boss, font, xpos, ypos+2, lwidth,
fontHeight, label, kTextAlignLeft);
xpos += t->getWidth()/2 - 5; ypos += t->getHeight() + 20;
myPins[kJUp] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJUp] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJUp]->setID(kJUp);
myPins[kJUp]->setTarget(this);
ypos += myPins[kJUp]->getHeight() * 2 + 10;
myPins[kJDown] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJDown] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJDown]->setID(kJDown);
myPins[kJDown]->setTarget(this);
xpos -= myPins[kJUp]->getWidth() + 5;
ypos -= myPins[kJUp]->getHeight() + 5;
myPins[kJLeft] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJLeft] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJLeft]->setID(kJLeft);
myPins[kJLeft]->setTarget(this);
xpos += (myPins[kJUp]->getWidth() + 5) * 2;
myPins[kJRight] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myPins[kJRight] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myPins[kJRight]->setID(kJRight);
myPins[kJRight]->setTarget(this);
xpos -= (myPins[kJUp]->getWidth() + 5) * 2;
ypos = 30 + (myPins[kJUp]->getHeight() + 10) * 3;
myPins[kJFire] = new CheckboxWidget(boss, font, xpos, ypos, "Fire", kCheckActionCmd);
myPins[kJFire] = new CheckboxWidget(boss, font, xpos, ypos, "Fire",
CheckboxWidget::kCheckActionCmd);
myPins[kJFire]->setID(kJFire);
myPins[kJFire]->setTarget(this);
}
@ -75,7 +78,7 @@ void JoystickWidget::loadConfig()
void JoystickWidget::handleCommand(
CommandSender* sender, int cmd, int data, int id)
{
if(cmd == kCheckActionCmd)
if(cmd == CheckboxWidget::kCheckActionCmd)
myController.set(ourPinNo[id], !myPins[id]->getState());
}

View File

@ -24,7 +24,7 @@ KeyboardWidget::KeyboardWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
{
bool leftport = myController.jack() == Controller::Left;
bool leftport = isLeftPort();
const string& label = leftport ? "Left (Keyboard)" : "Right (Keyboard)";
const int fontHeight = font.getFontHeight();
@ -38,7 +38,8 @@ KeyboardWidget::KeyboardWidget(GuiObject* boss, const GUI::Font& font,
for(int i = 0; i < 12; ++i)
{
myBox[i] = new CheckboxWidget(boss, font, xpos, ypos, "", kCheckActionCmd);
myBox[i] = new CheckboxWidget(boss, font, xpos, ypos, "",
CheckboxWidget::kCheckActionCmd);
myBox[i]->setID(i);
myBox[i]->setTarget(this);
xpos += myBox[i]->getWidth() + 5;
@ -63,7 +64,7 @@ void KeyboardWidget::loadConfig()
void KeyboardWidget::handleCommand(
CommandSender* sender, int cmd, int data, int id)
{
if(cmd == kCheckActionCmd)
if(cmd == CheckboxWidget::kCheckActionCmd)
instance().eventHandler().handleEvent(myEvent[id], myBox[id]->getState());
}

View File

@ -29,10 +29,8 @@ class NullControlWidget : public ControllerWidget
Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
{
bool leftport = controller.jack() == Controller::Left;
ostringstream buf;
buf << (leftport ? "Left (" : "Right (")
<< controller.name() << "):";
buf << getHeader();
const int fontHeight = font.getFontHeight(),
lineHeight = font.getLineHeight(),
lwidth = std::max(font.getStringWidth(buf.str()),

View File

@ -25,8 +25,8 @@ PaddleWidget::PaddleWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
{
bool leftport = myController.jack() == Controller::Left;
const string& label = leftport ? "Left (Paddles)" : "Right (Paddles)";
bool leftport = isLeftPort();
const string& label = getHeader();
const int fontWidth = font.getMaxCharWidth(),
fontHeight = font.getFontHeight(),

View File

@ -0,0 +1,141 @@
//============================================================================
//
// 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-2017 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 "PointingDevice.hxx"
#include "DataGridWidget.hxx"
#include "PointingDeviceWidget.hxx"
PointingDeviceWidget::PointingDeviceWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
{
int ypos = y;
int xLeft = x + 10;
int xMid = xLeft + 30;
int xRight = xLeft + 60;
int xValue = xLeft + 87;
StaticTextWidget* t;
t = new StaticTextWidget(boss, font, x, y + 2, getHeader());
ypos += t->getHeight() + 8;
// add gray code and up widgets
myGrayValueV = new DataGridWidget(boss, font, xMid, ypos,
1, 1, 2, 8, Common::Base::F_16);
myGrayValueV->setTarget(this);
myGrayValueV->setEditable(false);
ypos += myGrayValueV->getHeight() + 2;
myGrayUp = new ButtonWidget(boss, font, xMid, ypos, 17, "+", kTBUp);
myGrayUp->setTarget(this);
ypos += myGrayUp->getHeight() + 5;
// add horizontal direction and gray code widgets
myGrayLeft = new ButtonWidget(boss, font, xLeft, ypos, 17, "-", kTBLeft);
myGrayLeft->setTarget(this);
myGrayRight = new ButtonWidget(boss, font, xRight, ypos, 17, "+", kTBRight);
myGrayRight->setTarget(this);
myGrayValueH = new DataGridWidget(boss, font, xValue, ypos + 2,
1, 1, 2, 8, Common::Base::F_16);
myGrayValueH->setTarget(this);
myGrayValueH->setEditable(false);
ypos += myGrayLeft->getHeight() + 5;
// add down widget
myGrayDown = new ButtonWidget(boss, font, xMid, ypos, 17, "-", kTBDown);
myGrayDown->setTarget(this);
ypos += myGrayDown->getHeight() + 8;
myFire = new CheckboxWidget(boss, font, xLeft, ypos, "Fire", kTBFire);
myFire->setTarget(this);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PointingDeviceWidget::loadConfig()
{
setGrayCodeH();
setGrayCodeV();
myFire->setState(!myController.read(Controller::Six));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PointingDeviceWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
{
// since the PointingDevice uses its own, internal state (not reading the controller),
// we have to communicate directly with it
PointingDevice& pDev = static_cast<PointingDevice&>(myController);
switch(cmd)
{
case kTBLeft:
pDev.myCountH++;
pDev.myTrackBallLeft = false;
setGrayCodeH();
break;
case kTBRight:
pDev.myCountH--;
pDev.myTrackBallLeft = true;
setGrayCodeH();
break;
case kTBUp:
pDev.myCountV++;
pDev.myTrackBallDown = true;
setGrayCodeV();
break;
case kTBDown:
pDev.myCountV--;
pDev.myTrackBallDown = false;
setGrayCodeV();
break;
case kTBFire:
myController.set(Controller::Six, !myFire->getState());
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PointingDeviceWidget::setGrayCodeH()
{
PointingDevice& pDev = static_cast<PointingDevice&>(myController);
pDev.myCountH &= 0b11;
setValue(myGrayValueH, pDev.myCountH, pDev.myTrackBallLeft);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PointingDeviceWidget::setGrayCodeV()
{
PointingDevice& pDev = static_cast<PointingDevice&>(myController);
pDev.myCountV &= 0b11;
setValue(myGrayValueV, pDev.myCountV, !pDev.myTrackBallDown);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PointingDeviceWidget::setValue(DataGridWidget* grayValue, const int index, const int direction)
{
uInt8 grayCode = getGrayCodeTable(index, direction);
// FIXME * 8 = a nasty hack, because the DataGridWidget does not support 2 digit binary output
grayValue->setList(0, (grayCode & 0b01) + (grayCode & 0b10) * 8);
}

View File

@ -0,0 +1,66 @@
//============================================================================
//
// 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-2017 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 POINTINGDEVICE_WIDGET_HXX
#define POINTINGDEVICE_WIDGET_HXX
class DataGridWidget;
#include "Control.hxx"
#include "ControllerWidget.hxx"
class PointingDeviceWidget : public ControllerWidget
{
public:
PointingDeviceWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
Controller& controller);
virtual ~PointingDeviceWidget() = default;
private:
enum {
kTBLeft = 'TWlf',
kTBRight = 'TWrt',
kTBUp = 'TWup',
kTBDown = 'TWdn',
kTBFire = 'TWfr'
};
ButtonWidget *myGrayLeft, *myGrayRight;
DataGridWidget* myGrayValueH;
ButtonWidget *myGrayUp, *myGrayDown;
DataGridWidget* myGrayValueV;
CheckboxWidget* myFire;
private:
virtual uInt8 getGrayCodeTable(const int index, const int direction) = 0;
void loadConfig() override;
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
void setGrayCodeH();
void setGrayCodeV();
void setValue(DataGridWidget* greyValue, const int index, const int direction);
// Following constructors and assignment operators not supported
PointingDeviceWidget() = delete;
PointingDeviceWidget(const PointingDeviceWidget&) = delete;
PointingDeviceWidget(PointingDeviceWidget&&) = delete;
PointingDeviceWidget& operator=(const PointingDeviceWidget&) = delete;
PointingDeviceWidget& operator=(PointingDeviceWidget&&) = delete;
};
#endif

View File

@ -212,13 +212,15 @@ bool PromptWidget::handleKeyDown(StellaKey key, StellaMod mod)
for (int i = 0; i < len; i++)
{
str[i] = buffer(_promptStartPos + i) & 0x7f;
if(strchr("{*@<> ", str[i]) != NULL )
// whitespace characters
if(strchr("{*@<> =[]()+-/&|!^~%", str[i]))
{
lastDelimPos = i;
delimiter = str[i];
}
}
str[len] = '\0';
int strLen = len - lastDelimPos - 1;
StringList list;
string completionList;
@ -226,7 +228,7 @@ bool PromptWidget::handleKeyDown(StellaKey key, StellaMod mod)
if(lastDelimPos < 0)
{
// no delimiters, do command completion:
// no delimiters, do only command completion:
const DebuggerParser& parser = instance().debugger().parser();
parser.getCompletions(str, list);
@ -237,7 +239,7 @@ bool PromptWidget::handleKeyDown(StellaKey key, StellaMod mod)
completionList = list[0];
for(uInt32 i = 1; i < list.size(); ++i)
completionList += " " + list[i];
prefix = getCompletionPrefix(list, str);
prefix = getCompletionPrefix(list);
}
else
{
@ -248,11 +250,15 @@ bool PromptWidget::handleKeyDown(StellaKey key, StellaMod mod)
}
else
{
// we got a delimiter, so this must be a label or a function
const Debugger& dbg = instance().debugger();
// do not show ALL labels without any filter as it makes no sense
if(strLen > 0)
{
// we got a delimiter, so this must be a label or a function
const Debugger& dbg = instance().debugger();
dbg.cartDebug().getCompletions(str + lastDelimPos + 1, list);
dbg.getCompletions(str + lastDelimPos + 1, list);
dbg.cartDebug().getCompletions(str + lastDelimPos + 1, list);
dbg.getCompletions(str + lastDelimPos + 1, list);
}
}
if(list.size() < 1)
@ -262,9 +268,11 @@ bool PromptWidget::handleKeyDown(StellaKey key, StellaMod mod)
completionList = list[0];
for(uInt32 i = 1; i < list.size(); ++i)
completionList += " " + list[i];
prefix = getCompletionPrefix(list, str + lastDelimPos + 1);
prefix = getCompletionPrefix(list);
}
// TODO: tab through list
if(list.size() == 1)
{
// add to buffer as though user typed it (plus a space)
@ -475,7 +483,7 @@ void PromptWidget::handleCommand(CommandSender* sender, int cmd,
{
switch (cmd)
{
case kSetPositionCmd:
case GuiObject::kSetPositionCmd:
int newPos = int(data) + _linesPerPage - 1 + _firstLineInBuffer;
if (newPos != _scrollLine)
{
@ -501,7 +509,14 @@ void PromptWidget::loadConfig()
print(PROMPT);
// Take care of one-time debugger stuff
print(instance().debugger().autoExec());
// fill the history from the saved breaks, traps and watches commands
StringList history;
print(instance().debugger().autoExec(&history));
for(uInt32 i = 0; i < history.size(); i++)
{
addToHistory(history[i].c_str());
}
history.clear();
print(instance().debugger().cartDebug().loadConfigFile() + "\n");
print(instance().debugger().cartDebug().loadListFile() + "\n");
print(instance().debugger().cartDebug().loadSymbolFile() + "\n");
@ -697,8 +712,9 @@ void PromptWidget::historyScroll(int direction)
// Advance to the next line in the history
int line = _historyLine + direction;
if ((direction < 0 && line < 0) || (direction > 0 && line > _historySize))
return;
if(line < 0)
line += _historySize + 1;
line %= (_historySize + 1);
// If they press arrow-up with anything in the buffer, search backwards
// in the history.
@ -903,28 +919,26 @@ bool PromptWidget::saveBuffer(const FilesystemNode& file)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string PromptWidget::getCompletionPrefix(const StringList& completions, string prefix)
string PromptWidget::getCompletionPrefix(const StringList& completions)
{
// Search for prefix in every string, progressively growing it
// Once a mismatch is found or length is past one of the strings, we're done
// We *could* use the longest common string algorithm, but for the lengths
// of the strings we're dealing with, it's probably not worth it
for(;;)
// Find the number of characters matching for each of the completions provided
for(uInt32 len = 1;; len++)
{
for(const auto& s: completions)
for(uInt32 i = 0; i < completions.size(); i++)
{
if(s.length() < prefix.length())
return prefix; // current prefix is the best we're going to get
else if(!BSPF::startsWithIgnoreCase(s, prefix))
string s1 = completions[i];
if(s1.length() < len)
{
prefix.erase(prefix.length()-1);
return prefix;
return s1.substr(0, len - 1);
}
string find = s1.substr(0, len);
for(uInt32 j = i + 1; j < completions.size(); j++)
{
if(!BSPF::startsWithIgnoreCase(completions[j], find))
return s1.substr(0, len - 1);
}
}
if(completions[0].length() > prefix.length())
prefix = completions[0].substr(0, prefix.length() + 1);
else
return prefix;
}
}

View File

@ -35,8 +35,8 @@ class PromptWidget : public Widget, public CommandSender
virtual ~PromptWidget() = default;
public:
int printf(const char* format, ...);
int vprintf(const char* format, va_list argptr);
ATTRIBUTE_FMT_PRINTF int printf(const char* format, ...);
ATTRIBUTE_FMT_PRINTF int vprintf(const char* format, va_list argptr);
void print(const string& str);
void printPrompt();
bool saveBuffer(const FilesystemNode& file);
@ -85,7 +85,7 @@ class PromptWidget : public Widget, public CommandSender
private:
// Get the longest prefix (initially 's') that is in every string in the list
string getCompletionPrefix(const StringList& completions, string s);
string getCompletionPrefix(const StringList& completions);
private:
enum {

View File

@ -257,7 +257,7 @@ void RamWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
break;
}
case kSetPositionCmd:
case GuiObject::kSetPositionCmd:
myCurrentRamBank = data;
showSearchResults();
fillGrid(false);
@ -383,11 +383,11 @@ string RamWidget::doSearch(const string& str)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RamWidget::doCompare(const string& str)
{
bool comparitiveSearch = false;
bool comparativeSearch = false;
int searchVal = 0, offset = 0;
if(str.length() == 0)
return "Enter an absolute or comparitive value";
return "Enter an absolute or comparative value";
// Do some pre-processing on the string
string::size_type pos = str.find_first_of("+-", 0);
@ -397,11 +397,11 @@ string RamWidget::doCompare(const string& str)
return "Input must be [+|-]NUM";
}
// A comparitive search searches memory for locations that have changed by
// A comparative search searches memory for locations that have changed by
// the specified amount, vs. for exact values
if(str[0] == '+' || str[0] == '-')
{
comparitiveSearch = true;
comparativeSearch = true;
bool negative = false;
if(str[0] == '-')
negative = true;
@ -425,7 +425,7 @@ string RamWidget::doCompare(const string& str)
for(uInt32 i = 0; i < mySearchAddr.size(); ++i)
{
if(comparitiveSearch)
if(comparativeSearch)
{
searchVal = mySearchValue[i] + offset;
if(searchVal < 0 || searchVal > 255)

View File

@ -32,14 +32,14 @@ RiotRamWidget::RiotRamWidget(GuiObject* boss, const GUI::Font& lfont,
uInt8 RiotRamWidget::getValue(int addr) const
{
const CartState& state = static_cast<const CartState&>(myDbg.getState());
return myDbg.peek(state.rport[addr]);
return instance().debugger().peek(state.rport[addr]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RiotRamWidget::setValue(int addr, uInt8 value)
{
const CartState& state = static_cast<const CartState&>(myDbg.getState());
myDbg.poke(state.wport[addr], value);
instance().debugger().poke(state.wport[addr], value);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -35,6 +35,9 @@
#include "KeyboardWidget.hxx"
#include "AtariVoxWidget.hxx"
#include "SaveKeyWidget.hxx"
#include "AmigaMouseWidget.hxx"
#include "AtariMouseWidget.hxx"
#include "TrakBallWidget.hxx"
#include "RiotWidget.hxx"
@ -82,7 +85,7 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont,
// SWCHA bits in 'peek' mode
xpos = 10; ypos += lineHeight + 5;
CREATE_IO_REGS("SWCHA(R)", mySWCHAReadBits, 0, false);
CREATE_IO_REGS("SWCHA(R)", mySWCHAReadBits, kSWCHARBitsID, true);
// SWCHB bits in 'poke' mode
xpos = 10; ypos += 2 * lineHeight;
@ -199,13 +202,13 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont,
// Select and Reset
xpos += myP0Diff->getWidth() + 20; ypos = col2_ypos + lineHeight;
mySelect = new CheckboxWidget(boss, lfont, xpos, ypos, "Select",
kCheckActionCmd);
CheckboxWidget::kCheckActionCmd);
mySelect->setID(kSelectID);
mySelect->setTarget(this);
addFocusWidget(mySelect);
ypos += mySelect->getHeight() + 5;
myReset = new CheckboxWidget(boss, lfont, xpos, ypos, "Reset",
kCheckActionCmd);
CheckboxWidget::kCheckActionCmd);
myReset->setID(kResetID);
myReset->setTarget(this);
addFocusWidget(myReset);
@ -219,7 +222,7 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont,
// Randomize RAM
xpos += 30; ypos += lineHeight + 4;
myRandomizeRAM = new CheckboxWidget(boss, lfont, xpos, ypos+1,
"Randomize zero-page and extended RAM", kCheckActionCmd);
"Randomize zero-page and extended RAM", CheckboxWidget::kCheckActionCmd);
myRandomizeRAM->setID(kRandRAMID);
myRandomizeRAM->setTarget(this);
addFocusWidget(myRandomizeRAM);
@ -234,7 +237,7 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont,
for(int i = 0; i < 5; ++i)
{
myRandomizeCPU[i] = new CheckboxWidget(boss, lfont, xpos, ypos+1,
cpuregs[i], kCheckActionCmd);
cpuregs[i], CheckboxWidget::kCheckActionCmd);
myRandomizeCPU[i]->setID(kRandCPUID);
myRandomizeCPU[i]->setTarget(this);
addFocusWidget(myRandomizeCPU[i]);
@ -395,10 +398,24 @@ void RiotWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
value = Debugger::get_bits(mySWBCNTBits->getState());
riot.swbcnt(value & 0xff);
break;
case kSWCHARBitsID:
{
// TODO: Check if there is a nicer way to do this
value = Debugger::get_bits(mySWCHAReadBits->getState());
riot.controller(Controller::Left).set(Controller::One, value & 0b00010000);
riot.controller(Controller::Left).set(Controller::Two, value & 0b00100000);
riot.controller(Controller::Left).set(Controller::Three, value & 0b01000000);
riot.controller(Controller::Left).set(Controller::Four, value & 0b10000000);
riot.controller(Controller::Right).set(Controller::One, value & 0b00000001);
riot.controller(Controller::Right).set(Controller::Two, value & 0b00000010);
riot.controller(Controller::Right).set(Controller::Three, value & 0b00000100);
riot.controller(Controller::Right).set(Controller::Four, value & 0b00001000);
break;
}
}
break;
case kCheckActionCmd:
case CheckboxWidget::kCheckActionCmd:
switch(id)
{
case kSelectID:
@ -436,16 +453,14 @@ ControllerWidget* RiotWidget::addControlWidget(GuiObject* boss, const GUI::Font&
{
switch(controller.type())
{
case Controller::AmigaMouse: // TODO - implement this
return new NullControlWidget(boss, font, x, y, controller);
case Controller::AtariMouse: // TODO - implement this
return new NullControlWidget(boss, font, x, y, controller);
case Controller::AmigaMouse:
return new AmigaMouseWidget(boss, font, x, y, controller);
case Controller::AtariMouse:
return new AtariMouseWidget(boss, font, x, y, controller);
case Controller::AtariVox:
return new AtariVoxWidget(boss, font, x, y, controller);
case Controller::BoosterGrip:
return new BoosterWidget(boss, font, x, y, controller);
case Controller::CompuMate: // TODO - implement this
return new NullControlWidget(boss, font, x, y, controller);
case Controller::Driving:
return new DrivingWidget(boss, font, x, y, controller);
case Controller::Genesis:
@ -454,18 +469,16 @@ ControllerWidget* RiotWidget::addControlWidget(GuiObject* boss, const GUI::Font&
return new JoystickWidget(boss, font, x, y, controller);
case Controller::Keyboard:
return new KeyboardWidget(boss, font, x, y, controller);
case Controller::KidVid: // TODO - implement this
return new NullControlWidget(boss, font, x, y, controller);
case Controller::MindLink: // TODO - implement this
return new NullControlWidget(boss, font, x, y, controller);
// case Controller::KidVid: // TODO - implement this
// case Controller::MindLink: // TODO - implement this
case Controller::Paddles:
return new PaddleWidget(boss, font, x, y, controller);
case Controller::SaveKey:
return new SaveKeyWidget(boss, font, x, y, controller);
case Controller::TrakBall: // TODO - implement this
return new NullControlWidget(boss, font, x, y, controller);
case Controller::TrakBall:
return new TrakBallWidget(boss, font, x, y, controller);
default:
return nullptr; // make compiler happy
return new NullControlWidget(boss, font, x, y, controller);
}
}

View File

@ -75,7 +75,7 @@ class RiotWidget : public Widget, public CommandSender
kTim1TID, kTim8TID, kTim64TID, kTim1024TID, kTimWriteID,
kSWCHABitsID, kSWACNTBitsID, kSWCHBBitsID, kSWBCNTBitsID,
kP0DiffChanged, kP1DiffChanged, kTVTypeChanged, kSelectID, kResetID,
kRandCPUID, kRandRAMID
kRandCPUID, kRandRAMID, kSWCHARBitsID
};
private:

View File

@ -80,7 +80,8 @@ RomListWidget::RomListWidget(GuiObject* boss, const GUI::Font& lfont,
CheckboxWidget* t = nullptr;
for(int i = 0; i < _rows; ++i)
{
t = new CheckboxWidget(boss, lfont, _x + 2, ypos, "", kCheckActionCmd);
t = new CheckboxWidget(boss, lfont, _x + 2, ypos, "",
CheckboxWidget::kCheckActionCmd);
t->setTarget(this);
t->setID(i);
t->setFill(CheckboxWidget::Circle);
@ -114,7 +115,6 @@ RomListWidget::RomListWidget(GuiObject* boss, const GUI::Font& lfont,
default: // TODO - properly handle all other cases
return false;
}
return false;
};
setTextFilter(f);
}
@ -414,14 +414,14 @@ void RomListWidget::handleCommand(CommandSender* sender, int cmd, int data, int
{
switch (cmd)
{
case kCheckActionCmd:
case CheckboxWidget::kCheckActionCmd:
// We let the parent class handle this
// Pass it as a kRLBreakpointChangedCmd command, since that's the intent
sendCommand(RomListWidget::kBPointChangedCmd, _currentPos+id,
myCheckList[id]->getState());
break;
case kSetPositionCmd:
case GuiObject::kSetPositionCmd:
if (_currentPos != data)
{
_currentPos = data;

View File

@ -16,56 +16,28 @@
//============================================================================
#include "SaveKey.hxx"
#include "MT24LC256.hxx"
#include "SaveKeyWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SaveKeyWidget::SaveKeyWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
int x, int y, Controller& controller)
: FlashWidget(boss, font, x, y, controller)
{
bool leftport = myController.jack() == Controller::Left;
const string& label = leftport ? "Left (SaveKey)" : "Right (SaveKey)";
const int fontHeight = font.getFontHeight(),
lineHeight = font.getLineHeight(),
bwidth = font.getStringWidth("Erase EEPROM") + 20,
bheight = lineHeight + 4;
int xpos = x, ypos = y, lwidth = font.getStringWidth("Right (SaveKey)");
StaticTextWidget* t;
t = new StaticTextWidget(boss, font, xpos, ypos+2, lwidth,
fontHeight, label, kTextAlignLeft);
ypos += t->getHeight() + 20;
myEEPROMErase =
new ButtonWidget(boss, font, xpos+10, ypos, bwidth, bheight,
"Erase EEPROM", kEEPROMErase);
myEEPROMErase->setTarget(this);
ypos += lineHeight + 20;
const GUI::Font& ifont = instance().frameBuffer().infoFont();
lwidth = ifont.getMaxCharWidth() * 20;
new StaticTextWidget(boss, ifont, xpos, ypos, lwidth,
fontHeight, "(*) This will erase", kTextAlignLeft);
ypos += lineHeight + 2;
new StaticTextWidget(boss, ifont, xpos, ypos, lwidth,
fontHeight, "all EEPROM data, not", kTextAlignLeft);
ypos += lineHeight + 2;
new StaticTextWidget(boss, ifont, xpos, ypos, lwidth,
fontHeight, "just the range used", kTextAlignLeft);
ypos += lineHeight + 2;
new StaticTextWidget(boss, ifont, xpos, ypos, lwidth,
fontHeight, "for this ROM", kTextAlignLeft);
init(boss, font, x, y);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SaveKeyWidget::handleCommand(CommandSender*, int cmd, int, int)
void SaveKeyWidget::eraseCurrent()
{
if(cmd == kEEPROMErase)
{
SaveKey& skey = static_cast<SaveKey&>(myController);
skey.myEEPROM->erase();
}
SaveKey& skey = static_cast<SaveKey&>(myController);
skey.eraseCurrent();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SaveKeyWidget::isPageUsed(uInt32 page)
{
SaveKey& skey = static_cast<SaveKey&>(myController);
return skey.isPageUsed(page);
}

View File

@ -18,12 +18,10 @@
#ifndef SAVEKEY_WIDGET_HXX
#define SAVEKEY_WIDGET_HXX
class ButtonWidget;
#include "Control.hxx"
#include "ControllerWidget.hxx"
#include "FlashWidget.hxx"
class SaveKeyWidget : public ControllerWidget
class SaveKeyWidget : public FlashWidget
{
public:
SaveKeyWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
@ -31,12 +29,8 @@ class SaveKeyWidget : public ControllerWidget
virtual ~SaveKeyWidget() = default;
private:
ButtonWidget* myEEPROMErase;
enum { kEEPROMErase = 'eeER' };
private:
void loadConfig() override { }
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
void eraseCurrent() override;
bool isPageUsed(uInt32 page) override;
// Following constructors and assignment operators not supported
SaveKeyWidget() = delete;

View File

@ -202,7 +202,7 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont,
// P0 reflect
xpos += myHMP0->getWidth() + 15;
myRefP0 = new CheckboxWidget(boss, lfont, xpos, ypos+1,
"Reflect", kCheckActionCmd);
"Reflect", CheckboxWidget::kCheckActionCmd);
myRefP0->setTarget(this);
myRefP0->setID(kRefP0ID);
addFocusWidget(myRefP0);
@ -226,7 +226,7 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont,
// P0 delay
xpos += myGRP0Old->getWidth() + 12;
myDelP0 = new CheckboxWidget(boss, lfont, xpos, ypos+1,
"VDel", kCheckActionCmd);
"VDel", CheckboxWidget::kCheckActionCmd);
myDelP0->setTarget(this);
myDelP0->setID(kDelP0ID);
addFocusWidget(myDelP0);
@ -287,7 +287,7 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont,
// P1 reflect
xpos += myHMP1->getWidth() + 15;
myRefP1 = new CheckboxWidget(boss, lfont, xpos, ypos+1,
"Reflect", kCheckActionCmd);
"Reflect", CheckboxWidget::kCheckActionCmd);
myRefP1->setTarget(this);
myRefP1->setID(kRefP1ID);
addFocusWidget(myRefP1);
@ -310,7 +310,7 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont,
// P1 delay
xpos += myGRP1Old->getWidth() + 12;
myDelP1 = new CheckboxWidget(boss, lfont, xpos, ypos+1,
"VDel", kCheckActionCmd);
"VDel", CheckboxWidget::kCheckActionCmd);
myDelP1->setTarget(this);
myDelP1->setID(kDelP1ID);
addFocusWidget(myDelP1);
@ -382,7 +382,7 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont,
// M0 reset to player 0
xpos += myNusizM0->getWidth() + 15;
myResMP0 = new CheckboxWidget(boss, lfont, xpos, ypos+1,
"Reset to P0", kCheckActionCmd);
"Reset to P0", CheckboxWidget::kCheckActionCmd);
myResMP0->setTarget(this);
myResMP0->setID(kResMP0ID);
addFocusWidget(myResMP0);
@ -445,7 +445,7 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont,
// M1 reset to player 0
xpos += myNusizM1->getWidth() + 15;
myResMP1 = new CheckboxWidget(boss, lfont, xpos, ypos+1,
"Reset to P1", kCheckActionCmd);
"Reset to P1", CheckboxWidget::kCheckActionCmd);
myResMP1->setTarget(this);
myResMP1->setID(kResMP1ID);
addFocusWidget(myResMP1);
@ -523,7 +523,7 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont,
// Ball delay
xpos += myEnaBLOld->getWidth() + 12;
myDelBL = new CheckboxWidget(boss, lfont, xpos, ypos+1,
"VDel", kCheckActionCmd);
"VDel", CheckboxWidget::kCheckActionCmd);
myDelBL->setTarget(this);
myDelBL->setID(kDelBLID);
addFocusWidget(myDelBL);
@ -561,7 +561,7 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont,
addFocusWidget(myPF[2]);
// PFx bit labels
auto start = [&](int w) { return (w - sfWidth) / 2; };
auto start = [&](int sw) { return (sw - sfWidth) / 2; };
int colw = myPF[0]->getWidth() / 4;
xpos = 10 + 2*fontWidth + 5 + start(colw);
int _ypos = ypos - sfHeight;
@ -590,21 +590,21 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont,
// PF reflect, score, priority
xpos = 10 + 4*fontWidth; ypos += lineHeight + 6;
myRefPF = new CheckboxWidget(boss, lfont, xpos, ypos+1,
"Reflect", kCheckActionCmd);
"Reflect", CheckboxWidget::kCheckActionCmd);
myRefPF->setTarget(this);
myRefPF->setID(kRefPFID);
addFocusWidget(myRefPF);
xpos += myRefPF->getWidth() + 15;
myScorePF = new CheckboxWidget(boss, lfont, xpos, ypos+1,
"Score", kCheckActionCmd);
"Score", CheckboxWidget::kCheckActionCmd);
myScorePF->setTarget(this);
myScorePF->setID(kScorePFID);
addFocusWidget(myScorePF);
xpos += myScorePF->getWidth() + 15;
myPriorityPF = new CheckboxWidget(boss, lfont, xpos, ypos+1,
"Priority", kCheckActionCmd);
"Priority", CheckboxWidget::kCheckActionCmd);
myPriorityPF->setTarget(this);
myPriorityPF->setID(kPriorityPFID);
addFocusWidget(myPriorityPF);
@ -833,7 +833,7 @@ void TiaWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
}
break;
case kCheckActionCmd:
case CheckboxWidget::kCheckActionCmd:
switch(id)
{
case kRefP0ID:

View File

@ -202,7 +202,7 @@ void ToggleWidget::handleCommand(CommandSender* sender, int cmd,
{
switch (cmd)
{
case kSetPositionCmd:
case GuiObject::kSetPositionCmd:
if (_selectedItem != data)
{
_selectedItem = data;

View File

@ -0,0 +1,30 @@
//============================================================================
//
// 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-2017 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 "TrakBallWidget.hxx"
TrakBallWidget::TrakBallWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: PointingDeviceWidget(boss, font, x, y, controller)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TrakBallWidget::getGrayCodeTable(const int index, const int direction)
{
return myGrayCodeTable[(index & 0b1) + direction * 2];
}

View File

@ -0,0 +1,45 @@
//============================================================================
//
// 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-2017 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 TRAKBALL_WIDGET_HXX
#define TRAKBALL_WIDGET_HXX
#include "Control.hxx"
#include "PointingDeviceWidget.hxx"
class TrakBallWidget : public PointingDeviceWidget
{
public:
TrakBallWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
Controller& controller);
virtual ~TrakBallWidget() = default;
private:
uInt8 myGrayCodeTable[4] = { 0b00, 0b10, 0b01, 0b11 };
uInt8 getGrayCodeTable(const int index, const int direction) override;
// Following constructors and assignment operators not supported
TrakBallWidget() = delete;
TrakBallWidget(const TrakBallWidget&) = delete;
TrakBallWidget(TrakBallWidget&&) = delete;
TrakBallWidget& operator=(const TrakBallWidget&) = delete;
TrakBallWidget& operator=(TrakBallWidget&&) = delete;
};
#endif

View File

@ -1,76 +1,81 @@
MODULE := src/debugger/gui
MODULE_OBJS := \
src/debugger/gui/AmigaMouseWidget.o \
src/debugger/gui/AtariMouseWidget.o \
src/debugger/gui/AtariVoxWidget.o \
src/debugger/gui/AudioWidget.o \
src/debugger/gui/CpuWidget.o \
src/debugger/gui/PromptWidget.o \
src/debugger/gui/RamWidget.o \
src/debugger/gui/RiotWidget.o \
src/debugger/gui/RiotRamWidget.o \
src/debugger/gui/RomWidget.o \
src/debugger/gui/RomListWidget.o \
src/debugger/gui/RomListSettings.o \
src/debugger/gui/TiaWidget.o \
src/debugger/gui/TiaInfoWidget.o \
src/debugger/gui/TiaOutputWidget.o \
src/debugger/gui/TiaZoomWidget.o \
src/debugger/gui/DataGridOpsWidget.o \
src/debugger/gui/DataGridWidget.o \
src/debugger/gui/DebuggerDialog.o \
src/debugger/gui/ToggleBitWidget.o \
src/debugger/gui/TogglePixelWidget.o \
src/debugger/gui/ToggleWidget.o \
src/debugger/gui/CartRamWidget.o \
src/debugger/gui/BoosterWidget.o \
src/debugger/gui/Cart0840Widget.o \
src/debugger/gui/Cart2KWidget.o \
src/debugger/gui/Cart3EWidget.o \
src/debugger/gui/Cart3EPlusWidget.o \
src/debugger/gui/Cart3EWidget.o \
src/debugger/gui/Cart3FWidget.o \
src/debugger/gui/Cart4A50Widget.o \
src/debugger/gui/Cart4KWidget.o \
src/debugger/gui/Cart4KSCWidget.o \
src/debugger/gui/Cart4KWidget.o \
src/debugger/gui/CartARWidget.o \
src/debugger/gui/CartBFSCWidget.o \
src/debugger/gui/CartBFWidget.o \
src/debugger/gui/CartBUSWidget.o \
src/debugger/gui/CartCDFWidget.o \
src/debugger/gui/CartCMWidget.o \
src/debugger/gui/CartCTYWidget.o \
src/debugger/gui/CartCVWidget.o \
src/debugger/gui/CartCVPlusWidget.o \
src/debugger/gui/CartCVWidget.o \
src/debugger/gui/CartDASHWidget.o \
src/debugger/gui/CartDPCWidget.o \
src/debugger/gui/CartDFSCWidget.o \
src/debugger/gui/CartDFWidget.o \
src/debugger/gui/CartDPCPlusWidget.o \
src/debugger/gui/CartDPCWidget.o \
src/debugger/gui/CartE0Widget.o \
src/debugger/gui/CartE7Widget.o \
src/debugger/gui/CartEFWidget.o \
src/debugger/gui/CartEFSCWidget.o \
src/debugger/gui/CartBFWidget.o \
src/debugger/gui/CartBFSCWidget.o \
src/debugger/gui/CartDFWidget.o \
src/debugger/gui/CartDFSCWidget.o \
src/debugger/gui/CartEFWidget.o \
src/debugger/gui/CartF0Widget.o \
src/debugger/gui/CartF4Widget.o \
src/debugger/gui/CartF6Widget.o \
src/debugger/gui/CartF8Widget.o \
src/debugger/gui/CartF4SCWidget.o \
src/debugger/gui/CartF4Widget.o \
src/debugger/gui/CartF6SCWidget.o \
src/debugger/gui/CartF6Widget.o \
src/debugger/gui/CartF8SCWidget.o \
src/debugger/gui/CartFAWidget.o \
src/debugger/gui/CartF8Widget.o \
src/debugger/gui/CartFA2Widget.o \
src/debugger/gui/CartFAWidget.o \
src/debugger/gui/CartFEWidget.o \
src/debugger/gui/CartMDMWidget.o \
src/debugger/gui/CartRamWidget.o \
src/debugger/gui/CartSBWidget.o \
src/debugger/gui/CartUAWidget.o \
src/debugger/gui/CartWDWidget.o \
src/debugger/gui/CartX07Widget.o \
src/debugger/gui/JoystickWidget.o \
src/debugger/gui/PaddleWidget.o \
src/debugger/gui/BoosterWidget.o \
src/debugger/gui/CpuWidget.o \
src/debugger/gui/DataGridOpsWidget.o \
src/debugger/gui/DataGridWidget.o \
src/debugger/gui/DebuggerDialog.o \
src/debugger/gui/DelayQueueWidget.o \
src/debugger/gui/DrivingWidget.o \
src/debugger/gui/KeyboardWidget.o \
src/debugger/gui/FlashWidget.o \
src/debugger/gui/GenesisWidget.o \
src/debugger/gui/AtariVoxWidget.o \
src/debugger/gui/JoystickWidget.o \
src/debugger/gui/KeyboardWidget.o \
src/debugger/gui/PaddleWidget.o \
src/debugger/gui/PointingDeviceWidget.o \
src/debugger/gui/PromptWidget.o \
src/debugger/gui/RamWidget.o \
src/debugger/gui/RiotRamWidget.o \
src/debugger/gui/RiotWidget.o \
src/debugger/gui/RomListSettings.o \
src/debugger/gui/RomListWidget.o \
src/debugger/gui/RomWidget.o \
src/debugger/gui/SaveKeyWidget.o \
src/debugger/gui/DelayQueueWidget.o
src/debugger/gui/TiaInfoWidget.o \
src/debugger/gui/TiaOutputWidget.o \
src/debugger/gui/TiaWidget.o \
src/debugger/gui/TiaZoomWidget.o \
src/debugger/gui/ToggleBitWidget.o \
src/debugger/gui/TogglePixelWidget.o \
src/debugger/gui/ToggleWidget.o \
src/debugger/gui/TrakBallWidget.o
MODULE_DIRS += \
src/debugger/gui

View File

@ -38,8 +38,8 @@ class AmigaMouse : public PointingDevice
protected:
uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8, uInt8) override
{
static constexpr uInt32 ourTableH[4] = { 0x00, 0x80, 0xa0, 0x20 };
static constexpr uInt32 ourTableV[4] = { 0x00, 0x10, 0x50, 0x40 };
static constexpr uInt32 ourTableH[4] = { 0b0000, 0b1000, 0b1010, 0b0010 };
static constexpr uInt32 ourTableV[4] = { 0b0000, 0b0100, 0b0101, 0b0001 };
return ourTableH[countH] | ourTableV[countV];
}

View File

@ -38,8 +38,8 @@ class AtariMouse : public PointingDevice
protected:
uInt8 ioPortA(uInt8 countH, uInt8 countV, uInt8, uInt8) override
{
static constexpr uInt32 ourTableH[4] = { 0x00, 0x10, 0x30, 0x20 };
static constexpr uInt32 ourTableV[4] = { 0x00, 0x80, 0xc0, 0x40 };
static constexpr uInt32 ourTableH[4] = { 0b0000, 0b0001, 0b0011, 0b0010 };
static constexpr uInt32 ourTableV[4] = { 0b0000, 0b0100, 0b1100, 0b1000 };
return ourTableH[countH] | ourTableV[countV];
}

View File

@ -15,7 +15,6 @@
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#include "MT24LC256.hxx"
#include "SerialPort.hxx"
#include "System.hxx"
#include "AtariVox.hxx"
@ -24,7 +23,7 @@
AtariVox::AtariVox(Jack jack, const Event& event, const System& system,
const SerialPort& port, const string& portname,
const string& eepromfile)
: Controller(jack, event, system, Controller::AtariVox),
: SaveKey(jack, event, system, eepromfile, Controller::AtariVox),
mySerialPort(const_cast<SerialPort&>(port)),
myShiftCount(0),
myShiftRegister(0),
@ -35,9 +34,6 @@ AtariVox::AtariVox(Jack jack, const Event& event, const System& system,
else
myAboutString = " (invalid serial port \'" + portname + "\')";
myEEPROM = make_unique<MT24LC256>(eepromfile, system);
myDigitalPinState[One] = myDigitalPinState[Two] =
myDigitalPinState[Three] = myDigitalPinState[Four] = true;
}
@ -54,13 +50,8 @@ bool AtariVox::read(DigitalPin pin)
// For now, we just assume the device is always ready
return myDigitalPinState[Two] = true;
// Pin 3: EEPROM SDA
// input data from the 24LC256 EEPROM using the I2C protocol
case Three:
return myDigitalPinState[Three] = myEEPROM->readSDA();
default:
return Controller::read(pin);
return SaveKey::read(pin);
}
}
@ -77,22 +68,8 @@ void AtariVox::write(DigitalPin pin, bool value)
clockDataIn(value);
break;
// Pin 3: EEPROM SDA
// output data to the 24LC256 EEPROM using the I2C protocol
case Three:
myDigitalPinState[Three] = value;
myEEPROM->writeSDA(value);
break;
// Pin 4: EEPROM SCL
// output clock data to the 24LC256 EEPROM using the I2C protocol
case Four:
myDigitalPinState[Four] = value;
myEEPROM->writeSCL(value);
break;
default:
break;
SaveKey::write(pin, value);
}
}
@ -141,18 +118,5 @@ void AtariVox::clockDataIn(bool value)
void AtariVox::reset()
{
myLastDataWriteCycle = 0;
myEEPROM->systemReset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AtariVox::close()
{
// Force the EEPROM object to cleanup
myEEPROM.reset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string AtariVox::about() const
{
return Controller::about() + myAboutString;
SaveKey::reset();
}

View File

@ -21,6 +21,7 @@
class SerialPort;
#include "Control.hxx"
#include "SaveKey.hxx"
#include "MT24LC256.hxx"
/**
@ -32,10 +33,8 @@ class SerialPort;
@author B. Watson
*/
class AtariVox : public Controller
class AtariVox : public SaveKey
{
friend class AtariVoxWidget;
public:
/**
Create a new AtariVox controller plugged into the specified jack
@ -86,18 +85,10 @@ class AtariVox : public Controller
*/
void reset() override;
/**
Notification method invoked by the system indicating that the
console is about to be destroyed. It may be necessary to override
this method for controllers that need cleanup before exiting.
*/
void close() override;
string about() const override;
string about(bool swappedPorts) const override { return Controller::about(swappedPorts) + myAboutString; }
private:
void clockDataIn(bool value);
void shiftIn(bool value);
private:
// Instance of an real serial port on the system
@ -105,9 +96,6 @@ class AtariVox : public Controller
// bytes directly to it
SerialPort& mySerialPort;
// The EEPROM used in the AtariVox
unique_ptr<MT24LC256> myEEPROM;
// How many bits have been shifted into the shift register?
uInt8 myShiftCount;

View File

@ -178,7 +178,7 @@ bool Cartridge3E::bank(uInt16 bank)
mySystem->setPageAccess(addr, access);
}
access.directPeekBase = 0;
access.directPeekBase = nullptr;
access.type = System::PA_WRITE;
// Map write-port RAM image into the system

View File

@ -152,12 +152,17 @@ class Cartridge4A50 : public Cartridge
private:
/**
Query/change the given address type to use the given disassembly flags
Query the given address type for the associated disassembly flags.
@param address The address to modify
@param flags A bitfield of DisasmType directives for the given address
@param address The address to query
*/
uInt8 getAccessFlags(uInt16 address) const override;
/**
Change the given address to use the given disassembly flags.
@param address The address to modify
@param flags A bitfield of DisasmType directives for the given address
*/
void setAccessFlags(uInt16 address, uInt8 flags) override;
/**

View File

@ -53,7 +53,7 @@ void Cartridge4KSC::install(System& system)
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE)
{

View File

@ -156,12 +156,17 @@ class CartridgeAR : public Cartridge
private:
/**
Query/change the given address type to use the given disassembly flags
Query the given address type for the associated disassembly flags.
@param address The address to modify
@param flags A bitfield of DisasmType directives for the given address
@param address The address to query
*/
uInt8 getAccessFlags(uInt16 address) const override;
/**
Change the given address to use the given disassembly flags.
@param address The address to modify
@param flags A bitfield of DisasmType directives for the given address
*/
void setAccessFlags(uInt16 address, uInt8 flags) override;
// Handle a change to the bank configuration

View File

@ -58,7 +58,7 @@ void CartridgeBFSC::install(System& system)
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE)
{

View File

@ -65,7 +65,8 @@ CartridgeBUS::CartridgeBUS(const BytePtr& image, uInt32 size,
#ifdef THUMB_SUPPORT
// Create Thumbulator ARM emulator
myThumbEmulator = make_unique<Thumbulator>((uInt16*)myImage, (uInt16*)myBUSRAM,
myThumbEmulator = make_unique<Thumbulator>(
reinterpret_cast<uInt16*>(myImage), reinterpret_cast<uInt16*>(myBUSRAM),
settings.getBool("thumb.trapfatal"), Thumbulator::ConfigureFor::BUS, this);
#endif
setInitialState();
@ -528,7 +529,6 @@ uInt32 CartridgeBUS::thumbCallback(uInt8 function, uInt32 value1, uInt32 value2)
// _GetWavePtr - return the counter
case 2:
return myMusicCounters[value1];
break;
// _SetWaveSize - set size of waveform buffer
case 3:

View File

@ -69,9 +69,9 @@ CartridgeCDF::CartridgeCDF(const BytePtr& image, uInt32 size,
#ifdef THUMB_SUPPORT
// Create Thumbulator ARM emulator
myThumbEmulator = make_unique<Thumbulator>(
(uInt16*)myImage, (uInt16*)myCDFRAM, settings.getBool("thumb.trapfatal"),
myVersion ? Thumbulator::ConfigureFor::CDF1 : Thumbulator::ConfigureFor::CDF,
this);
reinterpret_cast<uInt16*>(myImage), reinterpret_cast<uInt16*>(myCDFRAM),
settings.getBool("thumb.trapfatal"), myVersion ?
Thumbulator::ConfigureFor::CDF1 : Thumbulator::ConfigureFor::CDF, this);
#endif
setInitialState();
}
@ -465,7 +465,6 @@ uInt32 CartridgeCDF::thumbCallback(uInt8 function, uInt32 value1, uInt32 value2)
// _GetWavePtr - return the counter
case 2:
return myMusicCounters[value1];
break;
// _SetWaveSize - set size of waveform buffer
case 3:

View File

@ -143,7 +143,7 @@ bool CartridgeCM::bank(uInt16 bank)
if((mySWCHA & 0x30) == 0x20)
access.directPokeBase = &myRAM[addr & 0x7FF];
else
access.directPokeBase = 0;
access.directPokeBase = nullptr;
mySystem->setPageAccess(addr, access);
}

View File

@ -74,8 +74,8 @@ void CartridgeCV::install(System& system)
}
// Set the page accessing method for the RAM writing pages
access.directPeekBase = 0;
access.codeAccessBase = 0;
access.directPeekBase = nullptr;
access.codeAccessBase = nullptr;
access.type = System::PA_WRITE;
for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE)
{
@ -84,7 +84,7 @@ void CartridgeCV::install(System& system)
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE)
{

View File

@ -58,8 +58,8 @@ void CartridgeCVPlus::install(System& system)
mySystem->setPageAccess(addr, access);
// Set the page accessing method for the RAM writing pages
access.directPeekBase = 0;
access.codeAccessBase = 0;
access.directPeekBase = nullptr;
access.codeAccessBase = nullptr;
access.type = System::PA_WRITE;
for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE)
{
@ -69,7 +69,7 @@ void CartridgeCVPlus::install(System& system)
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE)
{

View File

@ -58,7 +58,7 @@ void CartridgeDFSC::install(System& system)
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE)
{

View File

@ -57,7 +57,7 @@ void CartridgeE0::install(System& system)
myCurrentSlice[3] = 7;
// Set the page accessing methods for the hot spots in the last segment
access.directPeekBase = 0;
access.directPeekBase = nullptr;
access.codeAccessBase = &myCodeAccessBase[8128];
access.type = System::PA_READ;
for(uInt16 addr = (0x1FE0 & ~System::PAGE_MASK); addr < 0x2000;

View File

@ -162,7 +162,7 @@ void CartridgeE7::bankRAM(uInt16 bank)
}
// Set the page accessing method for the 256 bytes of RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1900; addr < 0x1A00; addr += System::PAGE_SIZE)
{
@ -208,7 +208,7 @@ bool CartridgeE7::bank(uInt16 slice)
}
// Set the page accessing method for the 1K slice of RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE)
{

View File

@ -58,7 +58,7 @@ void CartridgeEFSC::install(System& system)
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE)
{

View File

@ -58,7 +58,7 @@ void CartridgeF4SC::install(System& system)
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE)
{

View File

@ -58,7 +58,7 @@ void CartridgeF6SC::install(System& system)
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE)
{

View File

@ -58,7 +58,7 @@ void CartridgeF8SC::install(System& system)
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE)
{

View File

@ -58,7 +58,7 @@ void CartridgeFA::install(System& system)
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1100; addr < 0x1200; addr += System::PAGE_SIZE)
{

View File

@ -70,7 +70,7 @@ void CartridgeFA2::install(System& system)
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.directPokeBase = nullptr;
access.type = System::PA_READ;
for(uInt16 addr = 0x1100; addr < 0x1200; addr += System::PAGE_SIZE)
{

View File

@ -132,8 +132,6 @@ uInt8 CartridgeWD::peek(uInt16 address)
else
return mySegment3[address & 0x03FF];
}
return 0; // Make the compiler happy; we'll never reach this
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -61,7 +61,6 @@ void CompuMate::update()
Controller& lp = myConsole.leftController();
Controller& rp = myConsole.rightController();
lp.myAnalogPinValue[Controller::Nine] = Controller::maximumResistance;
lp.myAnalogPinValue[Controller::Five] = Controller::minimumResistance;
lp.myDigitalPinState[Controller::Six] = true;

View File

@ -109,8 +109,6 @@ class CompuMate
Called after *all* digital pins have been written on Port A.
Only update on the left controller; the right controller will
happen at the same cycle and is redundant.
@param value The entire contents of the SWCHA register
*/
void controlWrite(uInt8) override {
if(myJack == Controller::Left) myHandler.update();

View File

@ -189,8 +189,9 @@ Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
// Finally, add remaining info about the console
myConsoleInfo.CartName = myProperties.get(Cartridge_Name);
myConsoleInfo.CartMD5 = myProperties.get(Cartridge_MD5);
myConsoleInfo.Control0 = myLeftControl->about();
myConsoleInfo.Control1 = myRightControl->about();
bool swappedPorts = properties().get(Console_SwapPorts) == "YES";
myConsoleInfo.Control0 = myLeftControl->about(swappedPorts);
myConsoleInfo.Control1 = myRightControl->about(swappedPorts);
myConsoleInfo.BankSwitch = myCart->about();
myCart->setRomName(myConsoleInfo.CartName);
@ -724,13 +725,9 @@ void Console::setTIAProperties()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::setControllers(const string& rommd5)
{
// Setup the controllers based on properties
const string& left = myProperties.get(Controller_Left);
const string& right = myProperties.get(Controller_Right);
// Check for CompuMate controllers; they are special in that a handler
// creates them for us, and also that they must be used in both ports
if(left == "COMPUMATE" || right == "COMPUMATE")
// Check for CompuMate scheme; it is special in that a handler creates both
// controllers for us, and associates them with the bankswitching class
if(myCart->detectedType() == "CM")
{
myCMHandler = make_shared<CompuMate>(*this, myEvent, *mySystem);
@ -743,153 +740,114 @@ void Console::setControllers(const string& rommd5)
myLeftControl = std::move(myCMHandler->leftController());
myRightControl = std::move(myCMHandler->rightController());
return;
}
unique_ptr<Controller> leftC = std::move(myLeftControl),
rightC = std::move(myRightControl);
// Also check if we should swap the paddles plugged into a jack
bool swapPaddles = myProperties.get(Controller_SwapPaddles) == "YES";
// Construct left controller
if(left == "JOYSTICK")
{
// Already created in c'tor
// We save some time by not looking at all the other types
if(!leftC)
leftC = make_unique<Joystick>(Controller::Left, myEvent, *mySystem);
}
else if(left == "BOOSTERGRIP")
{
leftC = make_unique<BoosterGrip>(Controller::Left, myEvent, *mySystem);
}
else if(left == "DRIVING")
{
leftC = make_unique<Driving>(Controller::Left, myEvent, *mySystem);
}
else if((left == "KEYBOARD") || (left == "KEYPAD"))
{
leftC = make_unique<Keyboard>(Controller::Left, myEvent, *mySystem);
}
else if(BSPF::startsWithIgnoreCase(left, "PADDLES"))
{
bool swapAxis = false, swapDir = false;
if(left == "PADDLES_IAXIS")
swapAxis = true;
else if(left == "PADDLES_IDIR")
swapDir = true;
else if(left == "PADDLES_IAXDR")
swapAxis = swapDir = true;
leftC = make_unique<Paddles>(Controller::Left, myEvent, *mySystem,
swapPaddles, swapAxis, swapDir);
}
else if(left == "AMIGAMOUSE")
{
leftC = make_unique<AmigaMouse>(Controller::Left, myEvent, *mySystem);
}
else if(left == "ATARIMOUSE")
{
leftC = make_unique<AtariMouse>(Controller::Left, myEvent, *mySystem);
}
else if(left == "TRAKBALL")
{
leftC = make_unique<TrakBall>(Controller::Left, myEvent, *mySystem);
}
else if(left == "GENESIS")
{
leftC = make_unique<Genesis>(Controller::Left, myEvent, *mySystem);
}
else if(left == "MINDLINK")
{
leftC = make_unique<MindLink>(Controller::Left, myEvent, *mySystem);
}
// Construct right controller
if(right == "JOYSTICK")
{
// Already created in c'tor
// We save some time by not looking at all the other types
if(!rightC)
rightC = make_unique<Joystick>(Controller::Right, myEvent, *mySystem);
}
else if(right == "BOOSTERGRIP")
{
rightC = make_unique<BoosterGrip>(Controller::Right, myEvent, *mySystem);
}
else if(right == "DRIVING")
{
rightC = make_unique<Driving>(Controller::Right, myEvent, *mySystem);
}
else if((right == "KEYBOARD") || (right == "KEYPAD"))
{
rightC = make_unique<Keyboard>(Controller::Right, myEvent, *mySystem);
}
else if(BSPF::startsWithIgnoreCase(right, "PADDLES"))
{
bool swapAxis = false, swapDir = false;
if(right == "PADDLES_IAXIS")
swapAxis = true;
else if(right == "PADDLES_IDIR")
swapDir = true;
else if(right == "PADDLES_IAXDR")
swapAxis = swapDir = true;
rightC = make_unique<Paddles>(Controller::Right, myEvent, *mySystem,
swapPaddles, swapAxis, swapDir);
}
else if(left == "AMIGAMOUSE")
{
rightC = make_unique<AmigaMouse>(Controller::Left, myEvent, *mySystem);
}
else if(left == "ATARIMOUSE")
{
rightC = make_unique<AtariMouse>(Controller::Left, myEvent, *mySystem);
}
else if(left == "TRAKBALL")
{
rightC = make_unique<TrakBall>(Controller::Left, myEvent, *mySystem);
}
else if(right == "ATARIVOX")
{
const string& nvramfile = myOSystem.nvramDir() + "atarivox_eeprom.dat";
rightC = make_unique<AtariVox>(Controller::Right, myEvent,
*mySystem, myOSystem.serialPort(),
myOSystem.settings().getString("avoxport"), nvramfile);
}
else if(right == "SAVEKEY")
{
const string& nvramfile = myOSystem.nvramDir() + "savekey_eeprom.dat";
rightC = make_unique<SaveKey>(Controller::Right, myEvent, *mySystem,
nvramfile);
}
else if(right == "GENESIS")
{
rightC = make_unique<Genesis>(Controller::Right, myEvent, *mySystem);
}
else if(right == "KIDVID")
{
rightC = make_unique<KidVid>(Controller::Right, myEvent, *mySystem, rommd5);
}
else if(right == "MINDLINK")
{
rightC = make_unique<MindLink>(Controller::Right, myEvent, *mySystem);
}
// Swap the ports if necessary
if(myProperties.get(Console_SwapPorts) == "NO")
{
myLeftControl = std::move(leftC);
myRightControl = std::move(rightC);
}
else
{
myLeftControl = std::move(rightC);
myRightControl = std::move(leftC);
// Setup the controllers based on properties
const string& left = myProperties.get(Controller_Left);
const string& right = myProperties.get(Controller_Right);
unique_ptr<Controller> leftC = getControllerPort(rommd5, left, Controller::Left),
rightC = getControllerPort(rommd5, right, Controller::Right);
// Swap the ports if necessary
if(myProperties.get(Console_SwapPorts) == "NO")
{
myLeftControl = std::move(leftC);
myRightControl = std::move(rightC);
}
else
{
myLeftControl = std::move(rightC);
myRightControl = std::move(leftC);
}
}
myTIA->bindToControllers();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
unique_ptr<Controller> Console::getControllerPort(const string& rommd5,
const string& controllerName, Controller::Jack port)
{
unique_ptr<Controller> controller = std::move(myLeftControl);
if(controllerName == "JOYSTICK")
{
// Already created in c'tor
// We save some time by not looking at all the other types
if(!controller)
controller = make_unique<Joystick>(port, myEvent, *mySystem);
}
else if(controllerName == "BOOSTERGRIP")
{
controller = make_unique<BoosterGrip>(port, myEvent, *mySystem);
}
else if(controllerName == "DRIVING")
{
controller = make_unique<Driving>(port, myEvent, *mySystem);
}
else if((controllerName == "KEYBOARD") || (controllerName == "KEYPAD"))
{
controller = make_unique<Keyboard>(port, myEvent, *mySystem);
}
else if(BSPF::startsWithIgnoreCase(controllerName, "PADDLES"))
{
// Also check if we should swap the paddles plugged into a jack
bool swapPaddles = myProperties.get(Controller_SwapPaddles) == "YES";
bool swapAxis = false, swapDir = false;
if(controllerName == "PADDLES_IAXIS")
swapAxis = true;
else if(controllerName == "PADDLES_IDIR")
swapDir = true;
else if(controllerName == "PADDLES_IAXDR")
swapAxis = swapDir = true;
controller = make_unique<Paddles>(port, myEvent, *mySystem,
swapPaddles, swapAxis, swapDir);
}
else if(controllerName == "AMIGAMOUSE")
{
controller = make_unique<AmigaMouse>(port, myEvent, *mySystem);
}
else if(controllerName == "ATARIMOUSE")
{
controller = make_unique<AtariMouse>(port, myEvent, *mySystem);
}
else if(controllerName == "TRAKBALL")
{
controller = make_unique<TrakBall>(port, myEvent, *mySystem);
}
else if(controllerName == "ATARIVOX")
{
const string& nvramfile = myOSystem.nvramDir() + "atarivox_eeprom.dat";
controller = make_unique<AtariVox>(port, myEvent,
*mySystem, myOSystem.serialPort(),
myOSystem.settings().getString("avoxport"), nvramfile);
}
else if(controllerName == "SAVEKEY")
{
const string& nvramfile = myOSystem.nvramDir() + "savekey_eeprom.dat";
controller = make_unique<SaveKey>(port, myEvent, *mySystem,
nvramfile);
}
else if(controllerName == "GENESIS")
{
controller = make_unique<Genesis>(port, myEvent, *mySystem);
}
else if(controllerName == "KIDVID")
{
controller = make_unique<KidVid>(port, myEvent, *mySystem, rommd5);
}
else if(controllerName == "MINDLINK")
{
controller = make_unique<MindLink>(port, myEvent, *mySystem);
}
else // What else can we do?
controller = make_unique<Joystick>(port, myEvent, *mySystem);
return controller;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::loadUserPalette()
{
@ -953,7 +911,7 @@ void Console::generateColorLossPalette()
uInt32* palette[9] = {
&ourNTSCPalette[0], &ourPALPalette[0], &ourSECAMPalette[0],
&ourNTSCPaletteZ26[0], &ourPALPaletteZ26[0], &ourSECAMPaletteZ26[0],
0, 0, 0
nullptr, nullptr, nullptr
};
if(myUserPaletteDefined)
{
@ -964,7 +922,7 @@ void Console::generateColorLossPalette()
for(int i = 0; i < 9; ++i)
{
if(palette[i] == 0)
if(palette[i] == nullptr)
continue;
// Fill the odd numbered palette entries with gray values (calculated

View File

@ -326,6 +326,12 @@ class Console : public Serializable
*/
void setControllers(const string& rommd5);
/**
Selects the left or right controller depending on ROM properties
*/
unique_ptr<Controller> getControllerPort(const string& rommd5,
const string& controllerName, Controller::Jack port);
/**
Loads a user-defined palette file (from OSystem::paletteFile), filling the
appropriate user-defined palette arrays.

Some files were not shown because too many files have changed in this diff Show More