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 sometimes the image was 'double-blended', resulting in a snapshot that
was too dark. 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 * Added debugger pseudo-register '_fcycles', which gives the number of
CPU cycles that have occurred since the frame started. CPU cycles that have occurred since the frame started.
@ -2676,7 +2680,7 @@
* Fixed crash when adding one-shot cheats. * 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 * Fixed bug with setting snapshot naming type from the GUI (it was
always being set to 'romname'). always being set to 'romname').

View File

@ -53,6 +53,13 @@ CXXFLAGS+= -Wall -Wextra -Wno-unused-parameter -Wno-ignored-qualifiers
ifdef HAVE_GCC ifdef HAVE_GCC
CXXFLAGS+= -Wno-multichar -Wunused -fno-rtti -Woverloaded-virtual -Wnon-virtual-dtor -std=c++14 CXXFLAGS+= -Wno-multichar -Wunused -fno-rtti -Woverloaded-virtual -Wnon-virtual-dtor -std=c++14
endif 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 ifdef PROFILE
PROF:= -g -pg -fprofile-arcs -ftest-coverage PROF:= -g -pg -fprofile-arcs -ftest-coverage
@ -63,10 +70,6 @@ else
endif endif
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 # # 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 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__ ) is_xcode=$( $CXX -dM -E -x c /dev/null | grep __apple_build_version__ )
# Need at least version 8
if test -n "$is_xcode"; then 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" cxx_name="XCode $cxx_version"
if test $clang_major -ge 8; then if test $clang_major -ge 8; then
@ -401,35 +403,23 @@ if test "$have_clang" = yes; then
cxx_verc_fail=no cxx_verc_fail=no
else else
cxx_version="$cxx_version, bad" cxx_version="$cxx_version, bad"
cxx_verc_fail=bad cxx_verc_fail=yes
fi fi
else else
cxx_name=`( $cc -v ) 2>&1 | tail -n 1 | cut -d ' ' -f 1` # Need at least version 3.5
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 [ $clang_major -ge 4 ] || [ $clang_major -eq 3 -a $clang_minor -ge 5 ]; then
if test "$?" -gt 0; then cxx_version="$cxx_version, ok"
cxx_version="not found" cxx_verc_fail=no
else
cxx_version="$cxx_version, bad"
cxx_verc_fail=yes
fi 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 fi
CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS"
_make_def_HAVE_GCC3='HAVE_GCC3 = 1' _make_def_HAVE_GCC3='HAVE_GCC3 = 1'
add_line_to_config_mk 'CXX_UPDATE_DEP_FLAG = -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" -MQ "$@" -MP' 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_GCC='HAVE_GCC = 1'
_make_def_HAVE_CLANG='HAVE_CLANG = 1'
echo "$cxx_version" echo "$cxx_version"
elif test "$have_gcc" = yes; then elif test "$have_gcc" = yes; then
@ -505,7 +495,7 @@ fi
if test "$cxx_verc_fail" = yes ; then if test "$cxx_verc_fail" = yes ; then
echo echo
echo "The version of your compiler is not supported at this time" 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 exit 1
fi fi
@ -827,6 +817,7 @@ PROFILE := $_build_profile
$_make_def_HAVE_GCC $_make_def_HAVE_GCC
$_make_def_HAVE_GCC3 $_make_def_HAVE_GCC3
$_make_def_HAVE_CLANG
INCLUDES += $INCLUDES INCLUDES += $INCLUDES
OBJS += $OBJS 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 <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), 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> means 'search addresses for values that have changed by that amount'.</p>
<p>The 'Reset' button resets the entire operation; it clears the highlighted <p>The 'Reset' button resets the entire operation; it clears the highlighted
addresses and allows another search.</p> addresses and allows another search.</p>

View File

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

View File

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

View File

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

View File

@ -42,7 +42,6 @@ class FilesystemNodeZIP : public AbstractFSNode
* Creates a FilesystemNodeZIP for a given path. * Creates a FilesystemNodeZIP for a given path.
* *
* @param path String with the path the new node should point to. * @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); 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 // Cleanup
done: done:
if(png_ptr) 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) if(err_message)
throw runtime_error(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 filename The filename to load the PNG image
@param surface The FBSurface into which to place the PNG data @param surface The FBSurface into which to place the PNG data
@return On success, the FBSurface containing image data, otherwise a @post On success, the FBSurface containing image data, otherwise a
runtime_error is thrown containing a more detailed runtime_error is thrown containing a more detailed
error message. error message.
*/ */
void loadImage(const string& filename, FBSurface& surface); void loadImage(const string& filename, FBSurface& surface);
@ -60,9 +60,9 @@ class PNGLibrary
@param filename The filename to save the PNG image @param filename The filename to save the PNG image
@param comments The text comments to add to 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', @post On success, the PNG file has been saved to 'filename',
otherwise a runtime_error is thrown containing a otherwise a runtime_error is thrown containing a
more detailed error message. more detailed error message.
*/ */
void saveImage(const string& filename, void saveImage(const string& filename,
const VariantList& comments = EmptyVarList); const VariantList& comments = EmptyVarList);
@ -75,9 +75,9 @@ class PNGLibrary
@param rect The area of the surface to use @param rect The area of the surface to use
@param comments The text comments to add to 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', @post On success, the PNG file has been saved to 'filename',
otherwise a runtime_error is thrown containing a otherwise a runtime_error is thrown containing a
more detailed error message. more detailed error message.
*/ */
void saveImage(const string& filename, const FBSurface& surface, void saveImage(const string& filename, const FBSurface& surface,
const GUI::Rect& rect = GUI::EmptyRect, 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_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_write_data(png_structp ctx, png_bytep area, png_size_t size);
static void png_io_flush(png_structp ctx); static void png_io_flush(png_structp ctx);
static void png_user_warn(png_structp ctx, png_const_charp str); [[noreturn]] 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_error(png_structp ctx, png_const_charp str);
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported

View File

@ -15,6 +15,8 @@
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================ //============================================================================
#include <cmath>
#include "OSystem.hxx" #include "OSystem.hxx"
#include "Serializer.hxx" #include "Serializer.hxx"
#include "StateManager.hxx" #include "StateManager.hxx"
@ -25,23 +27,30 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RewindManager::RewindManager(OSystem& system, StateManager& statemgr) RewindManager::RewindManager(OSystem& system, StateManager& statemgr)
: myOSystem(system), : myOSystem(system),
myStateManager(statemgr) myStateManager(statemgr),
myIsNTSC(true) // TODO
// TODO: current is not valid
{ {
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::addState(const string& message) bool RewindManager::addState(const string& message)
{ {
RewindPtr state = make_unique<RewindState>(); // TODO: get this from object pool // TODO: remove following (preceding???) (all invalid) states
Serializer& s = state->data; // 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 s.reset(); // rewind Serializer internal buffers
if(myStateManager.saveState(s) && myOSystem.console().tia().saveDisplay(s)) if(myStateManager.saveState(s) && myOSystem.console().tia().saveDisplay(s))
{ {
state->message = "Rewind " + message; state.message = message;
state.cycle = myOSystem.console().tia().cycles();
// Add to the list TODO: should check against current size
myStateList.emplace_front(std::move(state));
return true; return true;
} }
return false; return false;
@ -50,21 +59,104 @@ bool RewindManager::addState(const string& message)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::rewindState() bool RewindManager::rewindState()
{ {
if(myStateList.size() > 0) if(!myStateList.empty())
{ {
RewindPtr state = std::move(myStateList.front()); // TODO: get state previous to the current state instead of first()
myStateList.pop_front(); // TODO: add 'state' to object pool // RewindState& state = myStateList.current();
Serializer& s = state->data; // 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 s.reset(); // rewind Serializer internal buffers
myStateManager.loadState(s); myStateManager.loadState(s);
myOSystem.console().tia().loadDisplay(s); myOSystem.console().tia().loadDisplay(s);
// Show message indicating the rewind state // 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; return true;
} }
else else
return false; 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 OSystem;
class StateManager; class StateManager;
#include <list> #include "LinkedObjectPool.hxx"
#include "bspf.hxx" #include "bspf.hxx"
/** /**
This class is used to save (and later 'rewind') system save states. 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 @author Stephen Anthony
*/ */
class RewindManager class RewindManager
@ -54,12 +49,19 @@ class RewindManager
*/ */
bool rewindState(); 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(); } void clear() { myStateList.clear(); }
private: private:
// Maximum number of states to save // Maximum number of states to save
static constexpr uInt32 MAX_SIZE = 100; // FIXME: use this static constexpr uInt32 MAX_SIZE = 100;
OSystem& myOSystem; OSystem& myOSystem;
StateManager& myStateManager; StateManager& myStateManager;
@ -67,10 +69,21 @@ class RewindManager
struct RewindState { struct RewindState {
Serializer data; Serializer data;
string message; string message;
uInt64 cycle;
// We do nothing on object instantiation or copy
RewindState() { }
RewindState(const RewindState&) { }
}; };
using RewindPtr = unique_ptr<RewindState>; Common::LinkedObjectPool<RewindState, MAX_SIZE> myStateList;
std::list<RewindPtr> myStateList;
bool myIsNTSC;
void compressStates();
string getMessage(RewindState& state);
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported

View File

@ -18,13 +18,27 @@
#ifndef SDL_LIB_HXX #ifndef SDL_LIB_HXX
#define 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 * Seems to be needed for ppc64le, doesn't hurt other archs
Note that this is a problem in SDL2, which includes <altivec.h> * Note that this is a problem in SDL2, which includes <altivec.h>
https://bugzilla.redhat.com/show_bug.cgi?id=1419452 * https://bugzilla.redhat.com/show_bug.cgi?id=1419452
*/ */
#undef vector #undef vector
#undef pixel #undef pixel
#undef bool #undef bool

View File

@ -29,7 +29,7 @@
#include "StateManager.hxx" #include "StateManager.hxx"
#define STATE_HEADER "05000302state" #define STATE_HEADER "05000302state"
#define MOVIE_HEADER "03030000movie" // #define MOVIE_HEADER "03030000movie"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StateManager::StateManager(OSystem& osystem) StateManager::StateManager(OSystem& osystem)
@ -134,6 +134,22 @@ void StateManager::toggleRewindMode()
myOSystem.frameBuffer().showMessage("Continuous rewind disabled"); 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() void StateManager::update()
{ {

View File

@ -64,6 +64,16 @@ class StateManager
*/ */
void toggleRewindMode(); 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. 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 str The string to split
@param maxlen The maximum length of string to generate @param maxlen The maximum length of string to generate
*/ */
StringParser(const string& str, uInt16 maxlen) StringParser(const string& str, uInt32 maxlen)
{ {
istringstream buf(str); istringstream buf(str);
string line; string line;

View File

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

View File

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

View File

@ -38,18 +38,6 @@
#include "CheatManager.hxx" #include "CheatManager.hxx"
#endif #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) #if defined(BSPF_MAC_OSX)
@ -61,10 +49,17 @@ int main(int argc, char* argv[])
std::ios_base::sync_with_stdio(false); std::ios_base::sync_with_stdio(false);
// Create the parent OSystem object // Create the parent OSystem object
theOSystem = MediaFactory::createOSystem(); unique_ptr<OSystem> theOSystem = MediaFactory::createOSystem();
theOSystem->loadConfig(); theOSystem->loadConfig();
theOSystem->logMessage("Loading config options ...", 2); theOSystem->logMessage("Loading config options ...", 2);
auto Cleanup = [&theOSystem]() {
theOSystem->logMessage("Cleanup from main", 2);
theOSystem->saveConfig();
return 0;
};
// Take care of commandline arguments // Take care of commandline arguments
theOSystem->logMessage("Loading commandline arguments ...", 2); theOSystem->logMessage("Loading commandline arguments ...", 2);
string romfile = theOSystem->settings().loadCommandLine(argc, argv); string romfile = theOSystem->settings().loadCommandLine(argc, argv);

View File

@ -83,4 +83,4 @@ inline uInt8 smartmod<256>(uInt8 x)
return x & 0xFF; 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]; uInt32* kernel = myColorTable[entry];
genKernel(myImpl, y, i, q, kernel); 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 - uInt32 error = rgb -
kernel [i ] - kernel [(i+10)%14+14] - kernel [c ] - kernel [(c+10)%14+14] -
kernel [i + 7] - kernel [i + 3 +14]; kernel [c + 7] - kernel [c + 3 +14];
kernel [i + 3 + 14] += error; kernel [c + 3 + 14] += error;
} }
} }
} }

View File

@ -123,7 +123,7 @@ class AtariNTSC
Used to calculate an averaged color for the 'phosphor' effect. Used to calculate an averaged color for the 'phosphor' effect.
@param c RGB Color 1 (current frame) @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 @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); mySystemAddresses = LabelToAddr(sysCmp);
// Add Zero-page RAM addresses // 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.rport.push_back(i);
myState.wport.push_back(i); myState.wport.push_back(i);
@ -75,16 +75,17 @@ CartDebug::CartDebug(Debugger& dbg, Console& console, const OSystem& osystem)
BankInfo info; BankInfo info;
info.size = std::min(banksize, 4096u); 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); myBankInfo.push_back(info);
info.size = 128; // ZP RAM info.size = 128; // ZP RAM
myBankInfo.push_back(info); myBankInfo.push_back(info);
// We know the address for the startup bank right now // 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)); addLabel("Start", myDebugger.dpeek(0xfffc, DATA));
// Add system equates // Add system equates
for(uInt16 addr = 0x00; addr <= 0x0F; ++addr) for(uInt16 addr = 0x00; addr <= 0x0F; ++addr)
{ {
@ -132,7 +133,7 @@ const DebuggerState& CartDebug::getState()
{ {
myState.ram.clear(); myState.ram.clear();
for(uInt32 i = 0; i < myState.rport.size(); ++i) 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) if(myDebugWidget)
myState.bank = myDebugWidget->bankState(); myState.bank = myDebugWidget->bankState();
@ -145,7 +146,7 @@ void CartDebug::saveOldState()
{ {
myOldState.ram.clear(); myOldState.ram.clear();
for(uInt32 i = 0; i < myOldState.rport.size(); ++i) 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) if(myDebugWidget)
{ {
@ -274,7 +275,7 @@ bool CartDebug::disassemble(bool force)
} }
// Otherwise, add the item at the end // Otherwise, add the item at the end
if (i == addresses.end()) if (i == addresses.end())
addresses.push_back(PC); addresses.push_back(PC);
} }
// Always attempt to resolve code sections unless it's been // Always attempt to resolve code sections unless it's been
@ -723,7 +724,7 @@ string CartDebug::loadListFile()
if(addr_s.length() == 0) if(addr_s.length() == 0)
continue; continue;
const char* p = addr_s[0] == 'U' ? addr_s.c_str() + 1 : addr_s.c_str(); 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 // For now, completely ignore ROM addresses
if(!(addr & 0x1000)) if(!(addr & 0x1000))
@ -786,7 +787,8 @@ string CartDebug::loadSymbolFile()
if (iter == myUserLabels.end() || !BSPF::equalsIgnoreCase(label, iter->second)) if (iter == myUserLabels.end() || !BSPF::equalsIgnoreCase(label, iter->second))
{ {
// Check for period, and strip leading number // 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); addLabel(label.substr(pos), value);
else else
addLabel(label, value); addLabel(label, value);
@ -1005,7 +1007,7 @@ string CartDebug::saveDisassembly()
disasm.list.clear(); disasm.list.clear();
DiStella distella(*this, disasm.list, info, settings, DiStella distella(*this, disasm.list, info, settings,
myDisLabels, myDisDirectives, myReserved); myDisLabels, myDisDirectives, myReserved);
if (myReserved.breakFound) if (myReserved.breakFound)
addLabel("Break", myDebugger.dpeek(0xfffe)); addLabel("Break", myDebugger.dpeek(0xfffe));
@ -1055,7 +1057,7 @@ string CartDebug::saveDisassembly()
break; break;
} }
case CartDebug::DATA: case CartDebug::DATA:
{ {
buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 8 * 4 - 1) << "; $" << Base::HEX4 << tag.address << " (D)"; buf << ".byte " << ALIGN(32) << tag.disasm.substr(6, 8 * 4 - 1) << "; $" << Base::HEX4 << tag.address << " (D)";
break; break;
} }
@ -1063,7 +1065,7 @@ string CartDebug::saveDisassembly()
default: default:
{ {
break; break;
} }
} // switch } // switch
buf << "\n"; buf << "\n";
} }
@ -1100,7 +1102,7 @@ string CartDebug::saveDisassembly()
out << "\n;-----------------------------------------------------------\n" out << "\n;-----------------------------------------------------------\n"
<< "; TIA and IO constants accessed\n" << "; TIA and IO constants accessed\n"
<< ";-----------------------------------------------------------\n\n"; << ";-----------------------------------------------------------\n\n";
// TIA read access // TIA read access
for(uInt16 addr = 0x00; addr <= 0x0F; ++addr) for(uInt16 addr = 0x00; addr <= 0x0F; ++addr)
if(myReserved.TIARead[addr] && ourTIAMnemonicR[addr]) if(myReserved.TIARead[addr] && ourTIAMnemonicR[addr])
@ -1129,9 +1131,9 @@ string CartDebug::saveDisassembly()
} }
addrUsed = false; addrUsed = false;
for(uInt16 addr = 0x80; addr <= 0xFF; ++addr) for(uInt16 addr = 0x80; addr <= 0xFF; ++addr)
addrUsed = addrUsed || myReserved.ZPRAM[addr-0x80] addrUsed = addrUsed || myReserved.ZPRAM[addr-0x80]
|| (mySystem.getAccessFlags(addr) & (DATA | WRITE)) || (mySystem.getAccessFlags(addr) & (DATA | WRITE))
|| (mySystem.getAccessFlags(addr|0x100) & (DATA | WRITE)); || (mySystem.getAccessFlags(addr|0x100) & (DATA | WRITE));
if(addrUsed) if(addrUsed)
{ {
@ -1139,13 +1141,13 @@ string CartDebug::saveDisassembly()
out << "\n\n;-----------------------------------------------------------\n" out << "\n\n;-----------------------------------------------------------\n"
<< "; RIOT RAM (zero-page) labels\n" << "; RIOT RAM (zero-page) labels\n"
<< ";-----------------------------------------------------------\n\n"; << ";-----------------------------------------------------------\n\n";
for (uInt16 addr = 0x80; addr <= 0xFF; ++addr) { for (uInt16 addr = 0x80; addr <= 0xFF; ++addr) {
bool ramUsed = (mySystem.getAccessFlags(addr) & (DATA | WRITE)); bool ramUsed = (mySystem.getAccessFlags(addr) & (DATA | WRITE));
bool codeUsed = (mySystem.getAccessFlags(addr) & CODE); bool codeUsed = (mySystem.getAccessFlags(addr) & CODE);
bool stackUsed = (mySystem.getAccessFlags(addr|0x100) & (DATA | WRITE)); bool stackUsed = (mySystem.getAccessFlags(addr|0x100) & (DATA | WRITE));
if (myReserved.ZPRAM[addr - 0x80] && if (myReserved.ZPRAM[addr - 0x80] &&
myUserLabels.find(addr) == myUserLabels.end()) { myUserLabels.find(addr) == myUserLabels.end()) {
if (addLine) if (addLine)
out << "\n"; out << "\n";
@ -1153,7 +1155,7 @@ string CartDebug::saveDisassembly()
<< Base::HEX2 << right << (addr) << Base::HEX2 << right << (addr)
<< (stackUsed|codeUsed ? "; (" : "") << (stackUsed|codeUsed ? "; (" : "")
<< (codeUsed ? "c" : "") << (codeUsed ? "c" : "")
<< (stackUsed ? "s" : "") << (stackUsed ? "s" : "")
<< (stackUsed | codeUsed ? ")" : "") << (stackUsed | codeUsed ? ")" : "")
<< "\n"; << "\n";
addLine = false; addLine = false;
@ -1161,11 +1163,11 @@ string CartDebug::saveDisassembly()
if (addLine) if (addLine)
out << "\n"; out << "\n";
out << ALIGN(18) << ";" << "$" out << ALIGN(18) << ";" << "$"
<< Base::HEX2 << right << (addr) << Base::HEX2 << right << (addr)
<< " (" << " ("
<< (ramUsed ? "i" : "") << (ramUsed ? "i" : "")
<< (codeUsed ? "c" : "") << (codeUsed ? "c" : "")
<< (stackUsed ? "s" : "") << (stackUsed ? "s" : "")
<< ")\n"; << ")\n";
addLine = false; addLine = false;
} else } else
@ -1278,23 +1280,23 @@ void CartDebug::getCompletions(const char* in, StringList& completions) const
{ {
// First scan system equates // First scan system equates
for(uInt16 addr = 0x00; addr <= 0x0F; ++addr) 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]); completions.push_back(ourTIAMnemonicR[addr]);
for(uInt16 addr = 0x00; addr <= 0x3F; ++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]); completions.push_back(ourTIAMnemonicW[addr]);
for(uInt16 addr = 0; addr <= 0x297-0x280; ++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]); completions.push_back(ourIOMnemonic[addr]);
for(uInt16 addr = 0; addr <= 0x7F; ++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]); completions.push_back(ourZPMnemonic[addr]);
// Now scan user-defined labels // Now scan user-defined labels
for(const auto& iter: myUserAddresses) for(const auto& iter: myUserAddresses)
{ {
const char* l = iter.first.c_str(); const char* l = iter.first.c_str();
if(BSPF::startsWithIgnoreCase(l, in)) if(BSPF::matches(l, in))
completions.push_back(l); completions.push_back(l);
} }
} }
@ -1332,7 +1334,7 @@ void CartDebug::getBankDirectives(ostream& buf, BankInfo& info) const
buf << "ORG " << Base::HEX4 << info.offset << endl; buf << "ORG " << Base::HEX4 << info.offset << endl;
// Now consider each byte // Now consider each byte
uInt32 prev = info.offset, addr = prev + 1; uInt16 prev = info.offset, addr = prev + 1;
DisasmType prevType = disasmTypeAbsolute(mySystem.getAccessFlags(prev)); DisasmType prevType = disasmTypeAbsolute(mySystem.getAccessFlags(prev));
for( ; addr < info.offset + info.size; ++addr) 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", "AUDF1", "AUDV0", "AUDV1", "GRP0", "GRP1", "ENAM0", "ENAM1", "ENABL",
"HMP0", "HMP1", "HMM0", "HMM1", "HMBL", "VDELP0", "VDELP1", "VDELBL", "HMP0", "HMP1", "HMM0", "HMM1", "HMBL", "VDELP0", "VDELP1", "VDELBL",
"RESMP0", "RESMP1", "HMOVE", "HMCLR", "CXCLR", "$2d", "$2e", "$2f", "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" "$38", "$39", "$3a", "$3b", "$3c", "$3d", "$3e", "$3f"
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const char* const CartDebug::ourIOMnemonic[24] = { const char* const CartDebug::ourIOMnemonic[24] = {
"SWCHA", "SWACNT", "SWCHB", "SWBCNT", "INTIM", "TIMINT", 0, 0, 0, 0, 0, "SWCHA", "SWACNT", "SWCHB", "SWBCNT", "INTIM", "TIMINT",
0, 0, 0, 0, 0, 0, 0, 0, 0, "TIM1T", "TIM8T", "TIM64T", "T1024T" "$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 "Cart.hxx"
#include "DebuggerSystem.hxx" #include "DebuggerSystem.hxx"
#include "System.hxx" #include "System.hxx"
#include "M6502.hxx"
// Function type for CartDebug instance methods // Function type for CartDebug instance methods
class CartDebug; class CartDebug;
@ -38,10 +39,10 @@ using CartMethod = int (CartDebug::*)();
class CartState : public DebuggerState class CartState : public DebuggerState
{ {
public: public:
ByteArray ram; // The actual data values ByteArray ram; // The actual data values
IntArray rport; // Address for reading from RAM ShortArray rport; // Address for reading from RAM
IntArray wport; // Address for writing to RAM ShortArray wport; // Address for writing to RAM
string bank; // Current banking layout string bank; // Current banking layout
}; };
class CartDebug : public DebuggerSystem class CartDebug : public DebuggerSystem
@ -112,13 +113,6 @@ class CartDebug : public DebuggerSystem
CartDebugWidget* getDebugWidget() const { return myDebugWidget; } CartDebugWidget* getDebugWidget() const { return myDebugWidget; }
void setDebugWidget(CartDebugWidget* w) { myDebugWidget = w; } 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 // Indicate that a read from write port has occurred at the specified
// address. // address.
void triggerReadFromWritePort(uInt16 address); void triggerReadFromWritePort(uInt16 address);
@ -127,6 +121,11 @@ class CartDebug : public DebuggerSystem
// write port area. // write port area.
int readFromWritePort(); 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 // The following two methods are meant to be used together
// First, a call is made to disassemble(), which updates the disassembly // First, a call is made to disassemble(), which updates the disassembly
// list; it will figure out when an actual complete disassembly is // 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 start; // start of address space
uInt16 end; // end of address space uInt16 end; // end of address space
uInt16 offset; // ORG value 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 AddressList addressList; // addresses which PC has hit
DirectiveList directiveList; // overrides for automatic code determination DirectiveList directiveList; // overrides for automatic code determination

View File

@ -29,7 +29,6 @@
#include "Settings.hxx" #include "Settings.hxx"
#include "DebuggerDialog.hxx" #include "DebuggerDialog.hxx"
#include "DebuggerParser.hxx" #include "DebuggerParser.hxx"
#include "StateManager.hxx"
#include "Console.hxx" #include "Console.hxx"
#include "System.hxx" #include "System.hxx"
@ -58,58 +57,6 @@
Debugger* Debugger::myStaticDebugger = nullptr; 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) Debugger::Debugger(OSystem& osystem, Console& console)
: DialogContainer(osystem), : 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()) if(myOSystem.eventHandler().enterDebugMode())
{ {
@ -175,8 +122,7 @@ bool Debugger::start(const string& message, int address)
ostringstream buf; ostringstream buf;
buf << message; buf << message;
if(address > -1) if(address > -1)
buf << Common::Base::HEX4 << address; buf << cartDebug().getLabel(address, read, 4);
myDialog->message().setText(buf.str()); myDialog->message().setText(buf.str());
return true; return true;
} }
@ -206,26 +152,26 @@ void Debugger::quit(bool exitrom)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Debugger::autoExec() string Debugger::autoExec(StringList* history)
{ {
ostringstream buf; ostringstream buf;
// autoexec.stella is always run // autoexec.script is always run
FilesystemNode autoexec(myOSystem.baseDir() + "autoexec.stella"); FilesystemNode autoexec(myOSystem.baseDir() + "autoexec.script");
buf << "autoExec():" << endl buf << "autoExec():" << endl
<< myParser->exec(autoexec) << endl; << myParser->exec(autoexec, history) << endl;
// Also, "romname.stella" if present // Also, "romname.script" if present
FilesystemNode romname(myOSystem.romFile().getPathWithExt(".stella")); FilesystemNode romname(myOSystem.romFile().getPathWithExt(".script"));
buf << myParser->exec(romname) << endl; buf << myParser->exec(romname, history) << endl;
// Init builtins // 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 // TODO - check this for memory leaks
int res = YaccParser::parse(builtin_functions[i][1]); int res = YaccParser::parse(ourBuiltinFunctions[i].defn);
if(res == 0) if(res == 0)
addFunction(builtin_functions[i][0], builtin_functions[i][1], addFunction(ourBuiltinFunctions[i].name, ourBuiltinFunctions[i].defn,
YaccParser::getResult(), true); YaccParser::getResult(), true);
else else
cerr << "ERROR in builtin function!" << endl; 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().initialize();
readTraps().toggle(t); readTraps().add(t);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Debugger::addWriteTrap(uInt16 t)
void Debugger::toggleWriteTrap(uInt16 t)
{ {
writeTraps().initialize(); writeTraps().initialize();
writeTraps().toggle(t); writeTraps().add(t);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleTrap(uInt16 t) void Debugger::addTrap(uInt16 t)
{ {
toggleReadTrap(t); addReadTrap(t);
toggleWriteTrap(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); 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) 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(); RewindManager& r = myOSystem.state().rewindManager();
mySystem.clearDirtyPages(); mySystem.clearDirtyPages();
unlockBankswitchState(); unlockBankswitchState();
bool result = r.rewindState(); bool result = unwind ? r.unwindState() : r.rewindState();
lockBankswitchState(); lockBankswitchState();
myDialog->rewindButton().setEnabled(!r.empty()); updateRewindbuttons(r);
return result; return result;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::rewindState()
{
return windState(false);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::unwindState()
{
return windState(true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::clearAllBreakPoints() void Debugger::clearAllBreakPoints()
{ {
@ -495,7 +522,7 @@ void Debugger::saveOldState(string rewindMsg)
{ {
RewindManager& r = myOSystem.state().rewindManager(); RewindManager& r = myOSystem.state().rewindManager();
r.addState(rewindMsg); 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 // Lock the bus each time the debugger is entered, so we don't disturb anything
lockBankswitchState(); lockBankswitchState();
// If rewinding is not enabled, always start the debugger with a clean list
RewindManager& r = myOSystem.state().rewindManager(); RewindManager& r = myOSystem.state().rewindManager();
if(myOSystem.state().mode() == StateManager::Mode::Off) updateRewindbuttons(r);
r.clear();
myDialog->rewindButton().setEnabled(!r.empty());
// Save initial state, but don't add it to the rewind list // Save initial state, but don't add it to the rewind list
saveOldState(); saveOldState();
@ -524,6 +548,9 @@ void Debugger::setQuitState()
// Bus must be unlocked for normal operation when leaving debugger mode // Bus must be unlocked for normal operation when leaving debugger mode
unlockBankswitchState(); unlockBankswitchState();
// Save state when leaving the debugger
saveOldState("exit debugger");
// execute one instruction on quit. If we're // execute one instruction on quit. If we're
// sitting at a breakpoint/trap, this will get us past it. // 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 // 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; 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) bool Debugger::delFunction(const string& name)
{ {
@ -549,8 +585,7 @@ bool Debugger::delFunction(const string& name)
return false; return false;
// We never want to delete built-in functions // We never want to delete built-in functions
for(int i = 0; builtin_functions[i][0] != 0; ++i) if(isBuiltinFunction(name))
if(name == builtin_functions[i][0])
return false; return false;
myFunctions.erase(name); myFunctions.erase(name);
@ -590,39 +625,39 @@ string Debugger::builtinHelp() const
uInt16 len, c_maxlen = 0, i_maxlen = 0; uInt16 len, c_maxlen = 0, i_maxlen = 0;
// Get column widths for aligned output (functions) // 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; 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; if(len > i_maxlen) i_maxlen = len;
} }
buf << std::setfill(' ') << endl << "Built-in functions:" << endl; 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(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) << "}" << std::setw(4) << "}"
<< builtin_functions[i][2] << ourBuiltinFunctions[i].help
<< endl; << endl;
} }
// Get column widths for aligned output (pseudo-registers) // Get column widths for aligned output (pseudo-registers)
c_maxlen = 0; 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; if(len > c_maxlen) c_maxlen = len;
} }
buf << endl << "Pseudo-registers:" << endl; 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(2) << " "
<< std::setw(i_maxlen) << std::left << pseudo_registers[i][1] << std::setw(i_maxlen) << std::left << ourPseudoRegisters[i].help
<< endl; << endl;
} }
@ -632,16 +667,20 @@ string Debugger::builtinHelp() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::getCompletions(const char* in, StringList& list) 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(); for(const auto& iter : myFunctions)
if(BSPF::startsWithIgnoreCase(l, in)) {
list.push_back(l); 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) for(uInt32 i = 0; i < NUM_PSEUDO_REGS; ++i)
if(BSPF::startsWithIgnoreCase(pseudo_registers[i][0], in)) if(BSPF::matches(ourPseudoRegisters[i].name, in))
list.push_back(pseudo_registers[i][0]); list.push_back(ourPseudoRegisters[i].name);
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -657,3 +696,49 @@ void Debugger::unlockBankswitchState()
mySystem.unlockDataBus(); mySystem.unlockDataBus();
myConsole.cartridge().unlockBank(); 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 "DialogContainer.hxx"
#include "DebuggerDialog.hxx" #include "DebuggerDialog.hxx"
#include "DebuggerParser.hxx" #include "DebuggerParser.hxx"
#include "StateManager.hxx"
#include "M6502.hxx" #include "M6502.hxx"
#include "System.hxx" #include "System.hxx"
#include "Stack.hxx" #include "Stack.hxx"
@ -88,7 +89,7 @@ class Debugger : public DialogContainer
@param message Message to display when entering debugger @param message Message to display when entering debugger
@param address An address associated with the message @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 = ""); bool startWithFatalError(const string& message = "");
/** /**
@ -99,6 +100,7 @@ class Debugger : public DialogContainer
bool addFunction(const string& name, const string& def, bool addFunction(const string& name, const string& def,
Expression* exp, bool builtin = false); Expression* exp, bool builtin = false);
bool isBuiltinFunction(const string& name);
bool delFunction(const string& name); bool delFunction(const string& name);
const Expression& getFunction(const string& name) const; const Expression& getFunction(const string& name) const;
@ -145,15 +147,15 @@ class Debugger : public DialogContainer
TiaOutputWidget& tiaOutput() const { return myDialog->tiaOutput(); } TiaOutputWidget& tiaOutput() const { return myDialog->tiaOutput(); }
PackedBitArray& breakPoints() const { return mySystem.m6502().breakPoints(); } PackedBitArray& breakPoints() const { return mySystem.m6502().breakPoints(); }
PackedBitArray& readTraps() const { return mySystem.m6502().readTraps(); } TrapArray& readTraps() const { return mySystem.m6502().readTraps(); }
PackedBitArray& writeTraps() const { return mySystem.m6502().writeTraps(); } TrapArray& writeTraps() const { return mySystem.m6502().writeTraps(); }
/** /**
Run the debugger command and return the result. Run the debugger command and return the result.
*/ */
const string run(const string& command); const string run(const string& command);
string autoExec(); string autoExec(StringList* history);
string showWatches(); string showWatches();
@ -168,9 +170,9 @@ class Debugger : public DialogContainer
static uInt8 set_bit(uInt8 input, uInt8 bit, bool on) static uInt8 set_bit(uInt8 input, uInt8 bit, bool on)
{ {
if(on) if(on)
return input | (1 << bit); return uInt8(input | (1 << bit));
else else
return input & ~(1 << bit); return uInt8(input & ~(1 << bit));
} }
static void set_bits(uInt8 reg, BoolArray& bits) static void set_bits(uInt8 reg, BoolArray& bits)
{ {
@ -192,7 +194,7 @@ class Debugger : public DialogContainer
return result; 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); const string invIfChanged(int reg, int oldReg);
/** /**
@ -205,15 +207,32 @@ class Debugger : public DialogContainer
*/ */
static Debugger& debugger() { return *myStaticDebugger; } static Debugger& debugger() { return *myStaticDebugger; }
/* These are now exposed so Expressions can use them. */ /** Convenience methods to access peek/poke from System */
int peek(int addr, uInt8 flags = 0) { return mySystem.peek(addr, flags); } uInt8 peek(uInt16 addr, uInt8 flags = 0) {
int dpeek(int addr, uInt8 flags = 0) { return mySystem.peek(addr, flags) | (mySystem.peek(addr+1, flags) << 8); } 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 int getAccessFlags(uInt16 addr) const
{ return mySystem.getAccessFlags(addr); } { return mySystem.getAccessFlags(addr); }
void setAccessFlags(uInt16 addr, uInt8 flags) void setAccessFlags(uInt16 addr, uInt8 flags)
{ mySystem.setAccessFlags(addr, flags); } { mySystem.setAccessFlags(addr, flags); }
void setBreakPoint(uInt16 bp, bool set); void setBreakPoint(uInt16 bp, bool set);
uInt32 getBaseAddress(uInt32 addr, bool read);
bool patchROM(uInt16 addr, uInt8 value); bool patchROM(uInt16 addr, uInt8 value);
@ -252,13 +271,17 @@ class Debugger : public DialogContainer
void nextScanline(int lines); void nextScanline(int lines);
void nextFrame(int frames); void nextFrame(int frames);
bool rewindState(); bool rewindState();
bool unwindState();
void toggleBreakPoint(uInt16 bp); void toggleBreakPoint(uInt16 bp);
bool breakPoint(uInt16 bp); bool breakPoint(uInt16 bp);
void toggleReadTrap(uInt16 t); void addReadTrap(uInt16 t);
void toggleWriteTrap(uInt16 t); void addWriteTrap(uInt16 t);
void toggleTrap(uInt16 t); void addTrap(uInt16 t);
void removeReadTrap(uInt16 t);
void removeWriteTrap(uInt16 t);
void removeTrap(uInt16 t);
bool readTrap(uInt16 t); bool readTrap(uInt16 t);
bool writeTrap(uInt16 t); bool writeTrap(uInt16 t);
void clearAllTraps(); void clearAllTraps();
@ -292,7 +315,24 @@ class Debugger : public DialogContainer
uInt32 myWidth; uInt32 myWidth;
uInt32 myHeight; 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: 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 // Following constructors and assignment operators not supported
Debugger() = delete; Debugger() = delete;
Debugger(const Debugger&) = delete; Debugger(const Debugger&) = delete;

View File

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

View File

@ -110,7 +110,10 @@ string DebuggerParser::run(const string& command)
if(BSPF::equalsIgnoreCase(verb, commands[i].cmdString)) if(BSPF::equalsIgnoreCase(verb, commands[i].cmdString))
{ {
if(validateArgs(i)) if(validateArgs(i))
{
myCommand = i;
commands[i].executor(this); commands[i].executor(this);
}
if(commands[i].refreshRequired) if(commands[i].refreshRequired)
debugger.myBaseDialog->loadConfig(); 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()) if(file.exists())
{ {
ifstream in(file.getPath()); ifstream in(file.getPath());
if(!in.is_open()) if(!in.is_open())
return red("autoexec file \'" + file.getShortPath() + "\' not found"); return red("script file \'" + file.getShortPath() + "\' not found");
ostringstream buf; ostringstream buf;
int count = 0; int count = 0;
@ -140,15 +143,27 @@ string DebuggerParser::exec(const FilesystemNode& file)
break; break;
run(command); run(command);
if (history != nullptr)
history->push_back(command);
count++; count++;
} }
buf << "Executed " << count << " commands from \"" buf << "\nExecuted " << count << " commands from \""
<< file.getShortPath() << "\""; << file.getShortPath() << "\"";
return buf.str(); return buf.str();
} }
else 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; // cerr << "Attempting to complete \"" << in << "\"" << endl;
for(int i = 0; i < kNumCommands; ++i) 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); completions.push_back(commands[i].cmdString);
} }
} }
@ -396,7 +411,8 @@ bool DebuggerParser::validateArgs(int cmd)
{ {
if(required) 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. return false; // needed args. didn't get 'em.
} }
else else
@ -427,6 +443,14 @@ bool DebuggerParser::validateArgs(int cmd)
switch(*p) switch(*p)
{ {
case kARG_DWORD:
if(curArgInt > 0xffffffff)
{
commandResult.str(red("invalid word argument (must be 0-$ffffffff)"));
return false;
}
break;
case kARG_WORD: case kARG_WORD:
if(curArgInt > 0xffff) if(curArgInt > 0xffff)
{ {
@ -484,12 +508,14 @@ cerr << "curCount = " << curCount << endl
if(curCount < argRequiredCount) if(curCount < argRequiredCount)
{ {
commandResult.str(red("missing required argument(s)")); commandResult.str();
outputCommandError("missing required argument(s)", cmd);
return false; return false;
} }
else if(argCount > curCount) else if(argCount > curCount)
{ {
commandResult.str(red("too many arguments")); commandResult.str();
outputCommandError("too many arguments", cmd);
return false; return false;
} }
@ -502,28 +528,29 @@ string DebuggerParser::eval()
ostringstream buf; ostringstream buf;
for(uInt32 i = 0; i < argCount; ++i) for(uInt32 i = 0; i < argCount; ++i)
{ {
string rlabel = debugger.cartDebug().getLabel(args[i], true); if(args[i] < 0x10000)
string wlabel = debugger.cartDebug().getLabel(args[i], false);
bool validR = rlabel != "" && rlabel[0] != '$',
validW = wlabel != "" && wlabel[0] != '$';
if(validR && validW)
{ {
if(rlabel == wlabel) string rlabel = debugger.cartDebug().getLabel(args[i], true);
buf << rlabel << "(R/W): "; string wlabel = debugger.cartDebug().getLabel(args[i], false);
else bool validR = rlabel != "" && rlabel[0] != '$',
buf << rlabel << "(R) / " << wlabel << "(W): "; 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);
buf << "$" << Base::toString(args[i], Base::F_16_2)
<< " %" << Base::toString(args[i], Base::F_2_8); if(args[i] < 0x10000)
else buf << " %" << Base::toString(args[i], Base::F_2);
buf << "$" << Base::toString(args[i], Base::F_16_4)
<< " %" << Base::toString(args[i], Base::F_2_16);
buf << " #" << int(args[i]); buf << " #" << int(args[i]);
if(i != argCount - 1) 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; StringList names = debugger.cpuDebug().m6502().getCondTrapNames();
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";
const string& l = debugger.cartDebug().getLabel(addr, !w); commandResult << (listCond ? "trapifs:" : "traps:") << endl;
if(l != "") { for(uInt32 i = 0; i < names.size(); i++)
result += " ("; {
result += l; bool hasCond = names[i] != "";
result += ")"; 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 ) stringstream result;
file += ".stella"; 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(); FunctionDefMap funcs = debugger.getFunctionDefMap();
for(const auto& f: funcs) 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) for(const auto& w: myWatches)
out << "watch " << w << endl; out << "watch " << w << endl;
for(uInt32 i = 0; i < 0x10000; ++i) for(uInt32 i = 0; i < 0x10000; ++i)
if(debugger.breakPoint(i)) if(debugger.breakPoint(i))
out << "break #" << i << endl; out << "break " << Base::toString(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;
}
StringList conds = debugger.cpuDebug().m6502().getCondBreakNames(); StringList conds = debugger.cpuDebug().m6502().getCondBreakNames();
for(const auto& cond: conds) for(const auto& cond : conds)
out << "breakif {" << cond << "}" << endl; 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 #ifdef CHEATCODE_SUPPORT
if(argCount == 0) if(argCount == 0)
{ {
commandResult << red("Missing cheat code"); outputCommandError("missing cheat code", myCommand);
return; return;
} }
@ -705,7 +779,7 @@ void DebuggerParser::executeCheat()
if(debugger.myOSystem.cheat().add("DBG", cheat)) if(debugger.myOSystem.cheat().add("DBG", cheat))
commandResult << "Cheat code " << cheat << " enabled" << endl; commandResult << "Cheat code " << cheat << " enabled" << endl;
else else
commandResult << red("Invalid cheat code ") << cheat << endl; commandResult << red("invalid cheat code ") << cheat << endl;
} }
#else #else
commandResult << red("Cheat support not enabled\n"); commandResult << red("Cheat support not enabled\n");
@ -735,8 +809,9 @@ void DebuggerParser::executeClearconfig()
// "cleartraps" // "cleartraps"
void DebuggerParser::executeCleartraps() void DebuggerParser::executeCleartraps()
{ {
myTraps.clear();
debugger.clearAllTraps(); debugger.clearAllTraps();
debugger.cpuDebug().m6502().clearCondTraps();
myTraps.clear();
commandResult << "all traps cleared"; commandResult << "all traps cleared";
} }
@ -762,12 +837,12 @@ void DebuggerParser::executeCode()
{ {
if(argCount != 2) if(argCount != 2)
{ {
commandResult << red("Specify start and end of range only"); outputCommandError("specify start and end of range only", myCommand);
return; return;
} }
else if(args[1] < args[0]) else if(args[1] < args[0])
{ {
commandResult << red("Start address must be <= end address"); commandResult << red("start address must be <= end address");
return; return;
} }
@ -803,12 +878,12 @@ void DebuggerParser::executeData()
{ {
if(argCount != 2) if(argCount != 2)
{ {
commandResult << red("Specify start and end of range only"); outputCommandError("specify start and end of range only", myCommand);
return; return;
} }
else if(args[1] < args[0]) else if(args[1] < args[0])
{ {
commandResult << red("Start address must be <= end address"); commandResult << red("start address must be <= end address");
return; return;
} }
@ -839,7 +914,10 @@ void DebuggerParser::executeDefine()
// "delbreakif" // "delbreakif"
void DebuggerParser::executeDelbreakif() 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"; 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" // "delwatch"
void DebuggerParser::executeDelwatch() void DebuggerParser::executeDelwatch()
@ -863,7 +959,7 @@ void DebuggerParser::executeDelwatch()
commandResult << "removed watch"; commandResult << "removed watch";
} }
else else
commandResult << "no such watch"; commandResult << red("no such watch");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -880,7 +976,7 @@ void DebuggerParser::executeDisasm()
start = args[0]; start = args[0];
lines = args[1]; lines = args[1];
} else { } else {
commandResult << "wrong number of arguments"; outputCommandError("wrong number of arguments", myCommand);
return; return;
} }
@ -910,7 +1006,7 @@ void DebuggerParser::executeDump()
// Error checking // Error checking
if(argCount > 1 && args[1] < args[0]) if(argCount > 1 && args[1] < args[0])
{ {
commandResult << red("Start address must be <= end address"); commandResult << red("start address must be <= end address");
return; return;
} }
@ -920,7 +1016,7 @@ void DebuggerParser::executeDump()
dump(args[0], args[1]); dump(args[0], args[1]);
else else
{ {
commandResult << "wrong number of arguments"; outputCommandError("wrong number of arguments", myCommand);
return; return;
} }
} }
@ -929,8 +1025,13 @@ void DebuggerParser::executeDump()
// "exec" // "exec"
void DebuggerParser::executeExec() void DebuggerParser::executeExec()
{ {
FilesystemNode file(argStrings[0]); // Append 'script' extension when necessary
commandResult << exec(file); 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) if(argCount != 2)
{ {
commandResult << red("Specify start and end of range only"); outputCommandError("specify start and end of range only", myCommand);
return; return;
} }
else if(args[1] < args[0]) else if(args[1] < args[0])
{ {
commandResult << red("Start address must be <= end address"); commandResult << red("start address must be <= end address");
return; return;
} }
@ -1061,26 +1162,21 @@ void DebuggerParser::executeListbreaks()
if(debugger.breakPoints().isSet(i)) if(debugger.breakPoints().isSet(i))
{ {
buf << debugger.cartDebug().getLabel(i, true, 4) << " "; buf << debugger.cartDebug().getLabel(i, true, 4) << " ";
if(! (++count % 8) ) buf << "\n"; if(! (++count % 8) ) buf << endl;
} }
} }
/*
if(count) if(count)
return ret; commandResult << "breaks:" << endl << buf.str();
else
return "no breakpoints set";
*/
if(count)
commandResult << "breaks:\n" << buf.str();
StringList conds = debugger.cpuDebug().m6502().getCondBreakNames(); StringList conds = debugger.cpuDebug().m6502().getCondBreakNames();
if(conds.size() > 0) if(conds.size() > 0)
{ {
commandResult << "\nbreakifs:\n"; if(count)
commandResult << endl;
commandResult << "breakifs:" << endl;
for(uInt32 i = 0; i < conds.size(); i++) 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; if(i != (conds.size() - 1)) commandResult << endl;
} }
} }
@ -1118,11 +1214,27 @@ void DebuggerParser::executeListfunctions()
// "listtraps" // "listtraps"
void DebuggerParser::executeListtraps() void DebuggerParser::executeListtraps()
{ {
if(myTraps.size() > 0) StringList names = debugger.cpuDebug().m6502().getCondTrapNames();
if(myTraps.size() != names.size())
{ {
bool enabled = true; commandResult << "Internal error! Different trap sizes.";
for(const auto& trap: myTraps) return;
commandResult << trapStatus(trap, enabled) << " + mirrors" << endl; }
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 else
commandResult << "no traps set"; commandResult << "no traps set";
@ -1175,12 +1287,12 @@ void DebuggerParser::executePGfx()
{ {
if(argCount != 2) if(argCount != 2)
{ {
commandResult << red("Specify start and end of range only"); outputCommandError("specify start and end of range only", myCommand);
return; return;
} }
else if(args[1] < args[0]) else if(args[1] < args[0])
{ {
commandResult << red("Start address must be <= end address"); commandResult << red("start address must be <= end address");
return; return;
} }
@ -1267,12 +1379,12 @@ void DebuggerParser::executeRow()
{ {
if(argCount != 2) if(argCount != 2)
{ {
commandResult << red("Specify start and end of range only"); outputCommandError("specify start and end of range only", myCommand);
return; return;
} }
else if(args[1] < args[0]) else if(args[1] < args[0])
{ {
commandResult << red("Start address must be <= end address"); commandResult << red("start address must be <= end address");
return; return;
} }
@ -1373,10 +1485,7 @@ void DebuggerParser::executeS()
// "save" // "save"
void DebuggerParser::executeSave() void DebuggerParser::executeSave()
{ {
if(saveScriptFile(argStrings[0])) commandResult << saveScriptFile(argStrings[0]);
commandResult << "saved script to file " << argStrings[0];
else
commandResult << red("I/O error");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1473,71 +1582,153 @@ void DebuggerParser::executeTrace()
// "trap" // "trap"
void DebuggerParser::executeTrap() void DebuggerParser::executeTrap()
{ {
if(argCount > 2) executeTraps(true, true, "trap");
{ }
commandResult << red("Command takes one or two arguments") << endl;
return;
}
uInt32 beg = args[0]; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 end = argCount == 2 ? args[1] : beg; // "trapif"
if(beg > 0xFFFF || end > 0xFFFF) void DebuggerParser::executeTrapif()
{ {
commandResult << red("One or more addresses are invalid") << endl; executeTraps(true, true, "trapif", true);
return;
}
for(uInt32 addr = beg; addr <= end; ++addr)
executeTrapRW(addr, true, true);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "trapread" // "trapread"
void DebuggerParser::executeTrapread() void DebuggerParser::executeTrapread()
{ {
if(argCount > 2) executeTraps(true, false, "trapread");
{ }
commandResult << red("Command takes one or two arguments") << endl;
return;
}
uInt32 beg = args[0]; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 end = argCount == 2 ? args[1] : beg; // "trapreadif"
if(beg > 0xFFFF || end > 0xFFFF) void DebuggerParser::executeTrapreadif()
{ {
commandResult << red("One or more addresses are invalid") << endl; executeTraps(true, false, "trapreadif", true);
return;
}
for(uInt32 addr = beg; addr <= end; ++addr)
executeTrapRW(addr, true, false);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "trapwrite" // "trapwrite"
void DebuggerParser::executeTrapwrite() void DebuggerParser::executeTrapwrite()
{ {
if(argCount > 2) executeTraps(false, true, "trapwrite");
{
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);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// wrapper function for trap/trapread/trapwrite commands // "trapwriteif"
void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write) 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)) switch(debugger.cartDebug().addressType(addr))
{ {
@ -1547,10 +1738,11 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{ {
if((i & 0x1080) == 0x0000) if((i & 0x1080) == 0x0000)
{ {
if(read && (i & 0x000F) == addr) // @sa666666: This seems wrong. E.g. trapread 40 4f will never trigger
debugger.toggleReadTrap(i); if(read && (i & 0x000F) == (addr & 0x000F))
if(write && (i & 0x003F) == addr) add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
debugger.toggleWriteTrap(i); if(write && (i & 0x003F) == (addr & 0x003F))
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
} }
} }
break; break;
@ -1559,10 +1751,12 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{ {
for(uInt32 i = 0; i <= 0xFFFF; ++i) 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(read)
if(write) debugger.toggleWriteTrap(i); add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
if(write)
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
} }
} }
break; break;
@ -1571,10 +1765,12 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{ {
for(uInt32 i = 0; i <= 0xFFFF; ++i) 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(read)
if(write) debugger.toggleWriteTrap(i); add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
if(write)
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
} }
} }
break; break;
@ -1587,21 +1783,16 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{ {
if((i % 0x2000 >= 0x1000) && (i & 0x0FFF) == (addr & 0x0FFF)) if((i % 0x2000 >= 0x1000) && (i & 0x0FFF) == (addr & 0x0FFF))
{ {
if(read) debugger.toggleReadTrap(i); if(read)
if(write) debugger.toggleWriteTrap(i); add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
if(write)
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
} }
} }
} }
break; 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"); 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" // "v"
void DebuggerParser::executeV() void DebuggerParser::executeV()
@ -1688,7 +1892,6 @@ void DebuggerParser::executeZ()
debugger.cpuDebug().setZ(args[0]); debugger.cpuDebug().setZ(args[0]);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// List of all commands available to the parser // List of all commands available to the parser
DebuggerParser::Command DebuggerParser::commands[kNumCommands] = { DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
@ -1883,6 +2086,16 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeDelfunction) 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", "delwatch",
"Delete watch <xx>", "Delete watch <xx>",
@ -1907,7 +2120,7 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
{ {
"dump", "dump",
"Dump data at address <xx> [to yy]", "Dump data at address <xx> [to yy]",
"Examples:\n" "Example:\n"
" dump f000 - dumps 128 bytes @ f000\n" " dump f000 - dumps 128 bytes @ f000\n"
" dump f000 f0ff - dumps all bytes from f000 to f0ff", " dump f000 f0ff - dumps all bytes from f000 to f0ff",
true, true,
@ -1949,7 +2162,7 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
{ {
"function", "function",
"Define function name xx for expression yy", "Define function name xx for expression yy",
"Example: define FUNC1 { ... }", "Example: function FUNC1 { ... }",
true, true,
false, false,
{ kARG_LABEL, kARG_WORD, kARG_END_ARGS }, { kARG_LABEL, kARG_WORD, kARG_END_ARGS },
@ -2094,7 +2307,7 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
"Example: print pc, print f000", "Example: print pc, print f000",
true, true,
false, false,
{ kARG_WORD, kARG_END_ARGS }, { kARG_DWORD, kARG_END_ARGS },
std::mem_fn(&DebuggerParser::executePrint) std::mem_fn(&DebuggerParser::executePrint)
}, },
@ -2325,6 +2538,17 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeTrap) 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", "trapread",
"Trap read access to address(es) xx [yy]", "Trap read access to address(es) xx [yy]",
@ -2336,6 +2560,17 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeTrapread) 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", "trapwrite",
"Trap write access to address(es) xx [yy]", "Trap write access to address(es) xx [yy]",
@ -2347,6 +2582,17 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeTrapwrite) 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", "type",
"Show disassembly type for address xx [yy]", "Show disassembly type for address xx [yy]",
@ -2378,6 +2624,16 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeUndef) 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", "v",
"Overflow Flag: set (0 or 1), or toggle (no arg)", "Overflow Flag: set (0 or 1), or toggle (no arg)",

View File

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

View File

@ -120,6 +120,12 @@ void DiStella::disasm(uInt32 distart, int pass)
myPC = distart - myOffset; myPC = distart - myOffset;
while (myPC <= myAppData.end) { 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, if (checkBits(myPC, CartDebug::GFX | CartDebug::PGFX,
CartDebug::CODE)) { CartDebug::CODE)) {
if (pass == 2) if (pass == 2)
@ -137,6 +143,7 @@ void DiStella::disasm(uInt32 distart, int pass)
myPC++; myPC++;
} else if (checkBits(myPC, CartDebug::ROW, } else if (checkBits(myPC, CartDebug::ROW,
CartDebug::CODE | CartDebug::DATA | CartDebug::GFX | CartDebug::PGFX)) { CartDebug::CODE | CartDebug::DATA | CartDebug::GFX | CartDebug::PGFX)) {
FIX_LAST:
if (pass == 2) if (pass == 2)
mark(myPC + myOffset, CartDebug::VALID_ENTRY); mark(myPC + myOffset, CartDebug::VALID_ENTRY);
@ -713,6 +720,7 @@ void DiStella::disasmFromAddress(uInt32 distart)
myPCEnd = myAppData.end + myOffset; myPCEnd = myAppData.end + myOffset;
return; return;
} }
break; // TODO - is this the intent?
default: default:
break; break;

View File

@ -349,8 +349,8 @@ bool TIADebug::collision(CollisionBit id) const
case Cx_BLPF: return myTIA.collCXBLPF() & 0x80; case Cx_BLPF: return myTIA.collCXBLPF() & 0x80;
case Cx_P0P1: return myTIA.collCXPPMM() & 0x80; case Cx_P0P1: return myTIA.collCXPPMM() & 0x80;
case Cx_M0M1: return myTIA.collCXPPMM() & 0x40; 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 int TIADebug::cyclesLo() const
{ {
return (int)myTIA.cycles(); return int(myTIA.cycles());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int TIADebug::cyclesHi() const 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(); return myTIA.clocksThisLine();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int TIADebug::cyclesThisLine() const
{
return myTIA.clocksThisLine()/3;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIADebug::vsync() const bool TIADebug::vsync() const
{ {

View File

@ -163,6 +163,7 @@ class TIADebug : public DebuggerSystem
int cyclesLo() const; int cyclesLo() const;
int cyclesHi() const; int cyclesHi() const;
int clocksThisLine() const; int clocksThisLine() const;
int cyclesThisLine() const;
bool vsync() const; bool vsync() const;
bool vblank() const; bool vblank() const;
int vsyncAsInt() const { return int(vsync()); } // so we can use _vsync pseudo-register 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 "AtariVox.hxx"
#include "MT24LC256.hxx"
#include "AtariVoxWidget.hxx" #include "AtariVoxWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AtariVoxWidget::AtariVoxWidget(GuiObject* boss, const GUI::Font& font, AtariVoxWidget::AtariVoxWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller) int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller) : FlashWidget(boss, font, x, y, controller)
{ {
bool leftport = myController.jack() == Controller::Left; init(boss, font, x, y);
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);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AtariVoxWidget::handleCommand(CommandSender*, int cmd, int, int) void AtariVoxWidget::eraseCurrent()
{ {
if(cmd == kEEPROMErase) AtariVox& avox = static_cast<AtariVox&>(myController);
{
AtariVox& avox = static_cast<AtariVox&>(myController); avox.eraseCurrent();
avox.myEEPROM->erase(); }
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 #ifndef ATARIVOX_WIDGET_HXX
#define ATARIVOX_WIDGET_HXX #define ATARIVOX_WIDGET_HXX
class ButtonWidget;
#include "Control.hxx" #include "Control.hxx"
#include "ControllerWidget.hxx" #include "FlashWidget.hxx"
class AtariVoxWidget : public ControllerWidget class AtariVoxWidget : public FlashWidget
{ {
public: public:
AtariVoxWidget(GuiObject* boss, const GUI::Font& font, int x, int y, AtariVoxWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
@ -31,12 +29,8 @@ class AtariVoxWidget : public ControllerWidget
virtual ~AtariVoxWidget() = default; virtual ~AtariVoxWidget() = default;
private: private:
ButtonWidget* myEEPROMErase; void eraseCurrent() override;
enum { kEEPROMErase = 'eeER' }; bool isPageUsed(uInt32 page) override;
private:
void loadConfig() override { }
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
AtariVoxWidget() = delete; AtariVoxWidget() = delete;

View File

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

View File

@ -36,7 +36,7 @@ Cartridge0840Widget::Cartridge0840Widget(
for(uInt32 i = 0, offset = 0xFFC, spot = 0x800; i < 2; for(uInt32 i = 0, offset = 0xFFC, spot = 0x800; i < 2;
++i, offset += 0x1000, spot += 0x40) ++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; start -= start % 0x1000;
info << "Bank " << i << " @ $" << Common::Base::HEX4 << start << " - " info << "Bank " << i << " @ $" << Common::Base::HEX4 << start << " - "
<< "$" << (start + 0xFFF) << " (hotspot = $" << spot << ")\n"; << "$" << (start + 0xFFF) << " (hotspot = $" << spot << ")\n";

View File

@ -324,7 +324,7 @@ void CartridgeBUSWidget::loadConfig()
for(int i = 0; i < 3; ++i) for(int i = 0; i < 3; ++i)
{ {
alist.push_back(0); vlist.push_back(myCart.myMusicCounters[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); myMusicCounters->setList(alist, vlist, changed);
@ -332,7 +332,7 @@ void CartridgeBUSWidget::loadConfig()
for(int i = 0; i < 3; ++i) for(int i = 0; i < 3; ++i)
{ {
alist.push_back(0); vlist.push_back(myCart.myMusicFrequencies[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); myMusicFrequencies->setList(alist, vlist, changed);

View File

@ -24,6 +24,7 @@ class ButtonWidget;
#include "Widget.hxx" #include "Widget.hxx"
#include "Command.hxx" #include "Command.hxx"
class ControllerWidget : public Widget, public CommandSender class ControllerWidget : public Widget, public CommandSender
{ {
public: public:
@ -44,6 +45,19 @@ class ControllerWidget : public Widget, public CommandSender
protected: protected:
Controller& myController; 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: private:
virtual void handleCommand(CommandSender* sender, int cmd, int data, int id) override { } 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) switch(cmd)
{ {
case kSetPositionCmd: case GuiObject::kSetPositionCmd:
// Chain access; pass to parent // Chain access; pass to parent
sendCommand(kSetPositionCmd, data, _id); sendCommand(GuiObject::kSetPositionCmd, data, _id);
break; break;
case kDGZeroCmd: case kDGZeroCmd:

View File

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

View File

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

View File

@ -22,13 +22,12 @@
DrivingWidget::DrivingWidget(GuiObject* boss, const GUI::Font& font, DrivingWidget::DrivingWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller) int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller), : ControllerWidget(boss, font, x, y, controller),
myGreyIndex(0) myGrayIndex(0)
{ {
bool leftport = myController.jack() == Controller::Left; const string& label = getHeader();
const string& label = leftport ? "Left (Driving)" : "Right (Driving)";
const int fontHeight = font.getFontHeight(), const int fontHeight = font.getFontHeight(),
bwidth = font.getStringWidth("Grey code +") + 10, bwidth = font.getStringWidth("Gray code +") + 10,
bheight = font.getLineHeight() + 4; bheight = font.getLineHeight() + 4;
int xpos = x, ypos = y, lwidth = font.getStringWidth("Right (Driving)"); int xpos = x, ypos = y, lwidth = font.getStringWidth("Right (Driving)");
StaticTextWidget* t; StaticTextWidget* t;
@ -37,22 +36,22 @@ DrivingWidget::DrivingWidget(GuiObject* boss, const GUI::Font& font,
fontHeight, label, kTextAlignLeft); fontHeight, label, kTextAlignLeft);
ypos += t->getHeight() + 20; ypos += t->getHeight() + 20;
myGreyUp = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight, myGrayUp = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Grey code +", kGreyUpCmd); "Gray code +", kGrayUpCmd);
myGreyUp->setTarget(this); myGrayUp->setTarget(this);
ypos += myGreyUp->getHeight() + 5; ypos += myGrayUp->getHeight() + 5;
myGreyDown = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight, myGrayDown = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
"Grey code -", kGreyDownCmd); "Gray code -", kGrayDownCmd);
myGreyDown->setTarget(this); myGrayDown->setTarget(this);
xpos += myGreyDown->getWidth() + 10; ypos -= 10; xpos += myGrayDown->getWidth() + 10; ypos -= 10;
myGreyValue = new DataGridWidget(boss, font, xpos, ypos, myGrayValue = new DataGridWidget(boss, font, xpos, ypos,
1, 1, 2, 8, Common::Base::F_16); 1, 1, 2, 8, Common::Base::F_16);
myGreyValue->setTarget(this); myGrayValue->setTarget(this);
myGreyValue->setEditable(false); 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 = new CheckboxWidget(boss, font, xpos, ypos, "Fire", kFireCmd);
myFire->setTarget(this); myFire->setTarget(this);
} }
@ -60,16 +59,16 @@ DrivingWidget::DrivingWidget(GuiObject* boss, const GUI::Font& font,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DrivingWidget::loadConfig() void DrivingWidget::loadConfig()
{ {
uInt8 grey = 0; uInt8 gray = 0;
if(myController.read(Controller::One)) grey += 1; if(myController.read(Controller::One)) gray += 1;
if(myController.read(Controller::Two)) grey += 2; if(myController.read(Controller::Two)) gray += 2;
for(myGreyIndex = 0; myGreyIndex < 4; ++myGreyIndex) for(myGrayIndex = 0; myGrayIndex < 4; ++myGrayIndex)
if(ourGreyTable[myGreyIndex] == grey) if(ourGrayTable[myGrayIndex] == gray)
break; break;
myFire->setState(!myController.read(Controller::Six)); myFire->setState(!myController.read(Controller::Six));
myGreyValue->setList(0, grey); setValue();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -78,17 +77,17 @@ void DrivingWidget::handleCommand(
{ {
switch(cmd) switch(cmd)
{ {
case kGreyUpCmd: case kGrayUpCmd:
myGreyIndex = (myGreyIndex + 1) % 4; myGrayIndex = (myGrayIndex + 1) % 4;
myController.set(Controller::One, (ourGreyTable[myGreyIndex] & 0x1) != 0); myController.set(Controller::One, (ourGrayTable[myGrayIndex] & 0x1) != 0);
myController.set(Controller::Two, (ourGreyTable[myGreyIndex] & 0x2) != 0); myController.set(Controller::Two, (ourGrayTable[myGrayIndex] & 0x2) != 0);
myGreyValue->setList(0, ourGreyTable[myGreyIndex]); setValue();
break; break;
case kGreyDownCmd: case kGrayDownCmd:
myGreyIndex = myGreyIndex == 0 ? 3 : myGreyIndex - 1; myGrayIndex = myGrayIndex == 0 ? 3 : myGrayIndex - 1;
myController.set(Controller::One, (ourGreyTable[myGreyIndex] & 0x1) != 0); myController.set(Controller::One, (ourGrayTable[myGrayIndex] & 0x1) != 0);
myController.set(Controller::Two, (ourGreyTable[myGreyIndex] & 0x2) != 0); myController.set(Controller::Two, (ourGrayTable[myGrayIndex] & 0x2) != 0);
myGreyValue->setList(0, ourGreyTable[myGreyIndex]); setValue();
break; break;
case kFireCmd: case kFireCmd:
myController.set(Controller::Six, !myFire->getState()); 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: private:
enum { enum {
kGreyUpCmd = 'DWup', kGrayUpCmd = 'DWup',
kGreyDownCmd = 'DWdn', kGrayDownCmd = 'DWdn',
kFireCmd = 'DWfr' kFireCmd = 'DWfr'
}; };
ButtonWidget *myGreyUp, *myGreyDown; ButtonWidget *myGrayUp, *myGrayDown;
DataGridWidget* myGreyValue; DataGridWidget* myGrayValue;
CheckboxWidget* myFire; CheckboxWidget* myFire;
int myGreyIndex; int myGrayIndex;
static uInt8 ourGreyTable[4]; static uInt8 ourGrayTable[4];
private: private:
void loadConfig() override; void loadConfig() override;
void handleCommand(CommandSender* sender, int cmd, int data, int id) override; void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
void setValue();
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
DrivingWidget() = delete; DrivingWidget() = delete;
DrivingWidget(const 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) int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller) : ControllerWidget(boss, font, x, y, controller)
{ {
bool leftport = myController.jack() == Controller::Left; const string& label = getHeader();
const string& label = leftport ? "Left (Genesis)" : "Right (Genesis)";
const int fontHeight = font.getFontHeight(); const int fontHeight = font.getFontHeight();
int xpos = x, ypos = y, lwidth = font.getStringWidth("Right (Genesis)"); 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, t = new StaticTextWidget(boss, font, xpos, ypos+2, lwidth,
fontHeight, label, kTextAlignLeft); fontHeight, label, kTextAlignLeft);
xpos += t->getWidth()/2 - 5; ypos += t->getHeight() + 20; 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]->setID(kJUp);
myPins[kJUp]->setTarget(this); myPins[kJUp]->setTarget(this);
ypos += myPins[kJUp]->getHeight() * 2 + 10; 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]->setID(kJDown);
myPins[kJDown]->setTarget(this); myPins[kJDown]->setTarget(this);
xpos -= myPins[kJUp]->getWidth() + 5; xpos -= myPins[kJUp]->getWidth() + 5;
ypos -= myPins[kJUp]->getHeight() + 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]->setID(kJLeft);
myPins[kJLeft]->setTarget(this); myPins[kJLeft]->setTarget(this);
xpos += (myPins[kJUp]->getWidth() + 5) * 2; 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]->setID(kJRight);
myPins[kJRight]->setTarget(this); myPins[kJRight]->setTarget(this);
xpos -= (myPins[kJUp]->getWidth() + 5) * 2; xpos -= (myPins[kJUp]->getWidth() + 5) * 2;
ypos = 30 + (myPins[kJUp]->getHeight() + 10) * 3; 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]->setID(kJBbtn);
myPins[kJBbtn]->setTarget(this); myPins[kJBbtn]->setTarget(this);
ypos += myPins[kJBbtn]->getHeight() + 5; 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]->setID(kJCbtn);
myPins[kJCbtn]->setTarget(this); myPins[kJCbtn]->setTarget(this);
} }
@ -83,7 +88,7 @@ void GenesisWidget::loadConfig()
void GenesisWidget::handleCommand( void GenesisWidget::handleCommand(
CommandSender* sender, int cmd, int data, int id) CommandSender* sender, int cmd, int data, int id)
{ {
if(cmd == kCheckActionCmd) if(cmd == CheckboxWidget::kCheckActionCmd)
{ {
switch(id) switch(id)
{ {

View File

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

View File

@ -29,10 +29,8 @@ class NullControlWidget : public ControllerWidget
Controller& controller) Controller& controller)
: ControllerWidget(boss, font, x, y, controller) : ControllerWidget(boss, font, x, y, controller)
{ {
bool leftport = controller.jack() == Controller::Left;
ostringstream buf; ostringstream buf;
buf << (leftport ? "Left (" : "Right (") buf << getHeader();
<< controller.name() << "):";
const int fontHeight = font.getFontHeight(), const int fontHeight = font.getFontHeight(),
lineHeight = font.getLineHeight(), lineHeight = font.getLineHeight(),
lwidth = std::max(font.getStringWidth(buf.str()), 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) int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller) : ControllerWidget(boss, font, x, y, controller)
{ {
bool leftport = myController.jack() == Controller::Left; bool leftport = isLeftPort();
const string& label = leftport ? "Left (Paddles)" : "Right (Paddles)"; const string& label = getHeader();
const int fontWidth = font.getMaxCharWidth(), const int fontWidth = font.getMaxCharWidth(),
fontHeight = font.getFontHeight(), 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++) for (int i = 0; i < len; i++)
{ {
str[i] = buffer(_promptStartPos + i) & 0x7f; str[i] = buffer(_promptStartPos + i) & 0x7f;
if(strchr("{*@<> ", str[i]) != NULL ) // whitespace characters
if(strchr("{*@<> =[]()+-/&|!^~%", str[i]))
{ {
lastDelimPos = i; lastDelimPos = i;
delimiter = str[i]; delimiter = str[i];
} }
} }
str[len] = '\0'; str[len] = '\0';
int strLen = len - lastDelimPos - 1;
StringList list; StringList list;
string completionList; string completionList;
@ -226,7 +228,7 @@ bool PromptWidget::handleKeyDown(StellaKey key, StellaMod mod)
if(lastDelimPos < 0) if(lastDelimPos < 0)
{ {
// no delimiters, do command completion: // no delimiters, do only command completion:
const DebuggerParser& parser = instance().debugger().parser(); const DebuggerParser& parser = instance().debugger().parser();
parser.getCompletions(str, list); parser.getCompletions(str, list);
@ -237,7 +239,7 @@ bool PromptWidget::handleKeyDown(StellaKey key, StellaMod mod)
completionList = list[0]; completionList = list[0];
for(uInt32 i = 1; i < list.size(); ++i) for(uInt32 i = 1; i < list.size(); ++i)
completionList += " " + list[i]; completionList += " " + list[i];
prefix = getCompletionPrefix(list, str); prefix = getCompletionPrefix(list);
} }
else else
{ {
@ -248,11 +250,15 @@ bool PromptWidget::handleKeyDown(StellaKey key, StellaMod mod)
} }
else else
{ {
// we got a delimiter, so this must be a label or a function // do not show ALL labels without any filter as it makes no sense
const Debugger& dbg = instance().debugger(); 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.cartDebug().getCompletions(str + lastDelimPos + 1, list);
dbg.getCompletions(str + lastDelimPos + 1, list); dbg.getCompletions(str + lastDelimPos + 1, list);
}
} }
if(list.size() < 1) if(list.size() < 1)
@ -262,9 +268,11 @@ bool PromptWidget::handleKeyDown(StellaKey key, StellaMod mod)
completionList = list[0]; completionList = list[0];
for(uInt32 i = 1; i < list.size(); ++i) for(uInt32 i = 1; i < list.size(); ++i)
completionList += " " + list[i]; completionList += " " + list[i];
prefix = getCompletionPrefix(list, str + lastDelimPos + 1); prefix = getCompletionPrefix(list);
} }
// TODO: tab through list
if(list.size() == 1) if(list.size() == 1)
{ {
// add to buffer as though user typed it (plus a space) // add to buffer as though user typed it (plus a space)
@ -475,7 +483,7 @@ void PromptWidget::handleCommand(CommandSender* sender, int cmd,
{ {
switch (cmd) switch (cmd)
{ {
case kSetPositionCmd: case GuiObject::kSetPositionCmd:
int newPos = int(data) + _linesPerPage - 1 + _firstLineInBuffer; int newPos = int(data) + _linesPerPage - 1 + _firstLineInBuffer;
if (newPos != _scrollLine) if (newPos != _scrollLine)
{ {
@ -501,7 +509,14 @@ void PromptWidget::loadConfig()
print(PROMPT); print(PROMPT);
// Take care of one-time debugger stuff // 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().loadConfigFile() + "\n");
print(instance().debugger().cartDebug().loadListFile() + "\n"); print(instance().debugger().cartDebug().loadListFile() + "\n");
print(instance().debugger().cartDebug().loadSymbolFile() + "\n"); print(instance().debugger().cartDebug().loadSymbolFile() + "\n");
@ -697,8 +712,9 @@ void PromptWidget::historyScroll(int direction)
// Advance to the next line in the history // Advance to the next line in the history
int line = _historyLine + direction; int line = _historyLine + direction;
if ((direction < 0 && line < 0) || (direction > 0 && line > _historySize)) if(line < 0)
return; line += _historySize + 1;
line %= (_historySize + 1);
// If they press arrow-up with anything in the buffer, search backwards // If they press arrow-up with anything in the buffer, search backwards
// in the history. // 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 // Find the number of characters matching for each of the completions provided
// Once a mismatch is found or length is past one of the strings, we're done for(uInt32 len = 1;; len++)
// 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(;;)
{ {
for(const auto& s: completions) for(uInt32 i = 0; i < completions.size(); i++)
{ {
if(s.length() < prefix.length()) string s1 = completions[i];
return prefix; // current prefix is the best we're going to get if(s1.length() < len)
else if(!BSPF::startsWithIgnoreCase(s, prefix))
{ {
prefix.erase(prefix.length()-1); return s1.substr(0, len - 1);
return prefix; }
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; virtual ~PromptWidget() = default;
public: public:
int printf(const char* format, ...); ATTRIBUTE_FMT_PRINTF int printf(const char* format, ...);
int vprintf(const char* format, va_list argptr); ATTRIBUTE_FMT_PRINTF int vprintf(const char* format, va_list argptr);
void print(const string& str); void print(const string& str);
void printPrompt(); void printPrompt();
bool saveBuffer(const FilesystemNode& file); bool saveBuffer(const FilesystemNode& file);
@ -85,7 +85,7 @@ class PromptWidget : public Widget, public CommandSender
private: private:
// Get the longest prefix (initially 's') that is in every string in the list // 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: private:
enum { enum {

View File

@ -257,7 +257,7 @@ void RamWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
break; break;
} }
case kSetPositionCmd: case GuiObject::kSetPositionCmd:
myCurrentRamBank = data; myCurrentRamBank = data;
showSearchResults(); showSearchResults();
fillGrid(false); fillGrid(false);
@ -383,11 +383,11 @@ string RamWidget::doSearch(const string& str)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string RamWidget::doCompare(const string& str) string RamWidget::doCompare(const string& str)
{ {
bool comparitiveSearch = false; bool comparativeSearch = false;
int searchVal = 0, offset = 0; int searchVal = 0, offset = 0;
if(str.length() == 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 // Do some pre-processing on the string
string::size_type pos = str.find_first_of("+-", 0); string::size_type pos = str.find_first_of("+-", 0);
@ -397,11 +397,11 @@ string RamWidget::doCompare(const string& str)
return "Input must be [+|-]NUM"; 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 // the specified amount, vs. for exact values
if(str[0] == '+' || str[0] == '-') if(str[0] == '+' || str[0] == '-')
{ {
comparitiveSearch = true; comparativeSearch = true;
bool negative = false; bool negative = false;
if(str[0] == '-') if(str[0] == '-')
negative = true; negative = true;
@ -425,7 +425,7 @@ string RamWidget::doCompare(const string& str)
for(uInt32 i = 0; i < mySearchAddr.size(); ++i) for(uInt32 i = 0; i < mySearchAddr.size(); ++i)
{ {
if(comparitiveSearch) if(comparativeSearch)
{ {
searchVal = mySearchValue[i] + offset; searchVal = mySearchValue[i] + offset;
if(searchVal < 0 || searchVal > 255) 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 uInt8 RiotRamWidget::getValue(int addr) const
{ {
const CartState& state = static_cast<const CartState&>(myDbg.getState()); 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) void RiotRamWidget::setValue(int addr, uInt8 value)
{ {
const CartState& state = static_cast<const CartState&>(myDbg.getState()); 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 "KeyboardWidget.hxx"
#include "AtariVoxWidget.hxx" #include "AtariVoxWidget.hxx"
#include "SaveKeyWidget.hxx" #include "SaveKeyWidget.hxx"
#include "AmigaMouseWidget.hxx"
#include "AtariMouseWidget.hxx"
#include "TrakBallWidget.hxx"
#include "RiotWidget.hxx" #include "RiotWidget.hxx"
@ -82,7 +85,7 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont,
// SWCHA bits in 'peek' mode // SWCHA bits in 'peek' mode
xpos = 10; ypos += lineHeight + 5; 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 // SWCHB bits in 'poke' mode
xpos = 10; ypos += 2 * lineHeight; xpos = 10; ypos += 2 * lineHeight;
@ -199,13 +202,13 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont,
// Select and Reset // Select and Reset
xpos += myP0Diff->getWidth() + 20; ypos = col2_ypos + lineHeight; xpos += myP0Diff->getWidth() + 20; ypos = col2_ypos + lineHeight;
mySelect = new CheckboxWidget(boss, lfont, xpos, ypos, "Select", mySelect = new CheckboxWidget(boss, lfont, xpos, ypos, "Select",
kCheckActionCmd); CheckboxWidget::kCheckActionCmd);
mySelect->setID(kSelectID); mySelect->setID(kSelectID);
mySelect->setTarget(this); mySelect->setTarget(this);
addFocusWidget(mySelect); addFocusWidget(mySelect);
ypos += mySelect->getHeight() + 5; ypos += mySelect->getHeight() + 5;
myReset = new CheckboxWidget(boss, lfont, xpos, ypos, "Reset", myReset = new CheckboxWidget(boss, lfont, xpos, ypos, "Reset",
kCheckActionCmd); CheckboxWidget::kCheckActionCmd);
myReset->setID(kResetID); myReset->setID(kResetID);
myReset->setTarget(this); myReset->setTarget(this);
addFocusWidget(myReset); addFocusWidget(myReset);
@ -219,7 +222,7 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont,
// Randomize RAM // Randomize RAM
xpos += 30; ypos += lineHeight + 4; xpos += 30; ypos += lineHeight + 4;
myRandomizeRAM = new CheckboxWidget(boss, lfont, xpos, ypos+1, 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->setID(kRandRAMID);
myRandomizeRAM->setTarget(this); myRandomizeRAM->setTarget(this);
addFocusWidget(myRandomizeRAM); addFocusWidget(myRandomizeRAM);
@ -234,7 +237,7 @@ RiotWidget::RiotWidget(GuiObject* boss, const GUI::Font& lfont,
for(int i = 0; i < 5; ++i) for(int i = 0; i < 5; ++i)
{ {
myRandomizeCPU[i] = new CheckboxWidget(boss, lfont, xpos, ypos+1, myRandomizeCPU[i] = new CheckboxWidget(boss, lfont, xpos, ypos+1,
cpuregs[i], kCheckActionCmd); cpuregs[i], CheckboxWidget::kCheckActionCmd);
myRandomizeCPU[i]->setID(kRandCPUID); myRandomizeCPU[i]->setID(kRandCPUID);
myRandomizeCPU[i]->setTarget(this); myRandomizeCPU[i]->setTarget(this);
addFocusWidget(myRandomizeCPU[i]); addFocusWidget(myRandomizeCPU[i]);
@ -395,10 +398,24 @@ void RiotWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
value = Debugger::get_bits(mySWBCNTBits->getState()); value = Debugger::get_bits(mySWBCNTBits->getState());
riot.swbcnt(value & 0xff); riot.swbcnt(value & 0xff);
break; 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; break;
case kCheckActionCmd: case CheckboxWidget::kCheckActionCmd:
switch(id) switch(id)
{ {
case kSelectID: case kSelectID:
@ -436,16 +453,14 @@ ControllerWidget* RiotWidget::addControlWidget(GuiObject* boss, const GUI::Font&
{ {
switch(controller.type()) switch(controller.type())
{ {
case Controller::AmigaMouse: // TODO - implement this case Controller::AmigaMouse:
return new NullControlWidget(boss, font, x, y, controller); return new AmigaMouseWidget(boss, font, x, y, controller);
case Controller::AtariMouse: // TODO - implement this case Controller::AtariMouse:
return new NullControlWidget(boss, font, x, y, controller); return new AtariMouseWidget(boss, font, x, y, controller);
case Controller::AtariVox: case Controller::AtariVox:
return new AtariVoxWidget(boss, font, x, y, controller); return new AtariVoxWidget(boss, font, x, y, controller);
case Controller::BoosterGrip: case Controller::BoosterGrip:
return new BoosterWidget(boss, font, x, y, controller); 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: case Controller::Driving:
return new DrivingWidget(boss, font, x, y, controller); return new DrivingWidget(boss, font, x, y, controller);
case Controller::Genesis: case Controller::Genesis:
@ -454,18 +469,16 @@ ControllerWidget* RiotWidget::addControlWidget(GuiObject* boss, const GUI::Font&
return new JoystickWidget(boss, font, x, y, controller); return new JoystickWidget(boss, font, x, y, controller);
case Controller::Keyboard: case Controller::Keyboard:
return new KeyboardWidget(boss, font, x, y, controller); return new KeyboardWidget(boss, font, x, y, controller);
case Controller::KidVid: // TODO - implement this // case Controller::KidVid: // TODO - implement this
return new NullControlWidget(boss, font, x, y, controller); // case Controller::MindLink: // TODO - implement this
case Controller::MindLink: // TODO - implement this
return new NullControlWidget(boss, font, x, y, controller);
case Controller::Paddles: case Controller::Paddles:
return new PaddleWidget(boss, font, x, y, controller); return new PaddleWidget(boss, font, x, y, controller);
case Controller::SaveKey: case Controller::SaveKey:
return new SaveKeyWidget(boss, font, x, y, controller); return new SaveKeyWidget(boss, font, x, y, controller);
case Controller::TrakBall: // TODO - implement this case Controller::TrakBall:
return new NullControlWidget(boss, font, x, y, controller); return new TrakBallWidget(boss, font, x, y, controller);
default: 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, kTim1TID, kTim8TID, kTim64TID, kTim1024TID, kTimWriteID,
kSWCHABitsID, kSWACNTBitsID, kSWCHBBitsID, kSWBCNTBitsID, kSWCHABitsID, kSWACNTBitsID, kSWCHBBitsID, kSWBCNTBitsID,
kP0DiffChanged, kP1DiffChanged, kTVTypeChanged, kSelectID, kResetID, kP0DiffChanged, kP1DiffChanged, kTVTypeChanged, kSelectID, kResetID,
kRandCPUID, kRandRAMID kRandCPUID, kRandRAMID, kSWCHARBitsID
}; };
private: private:

View File

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

View File

@ -16,56 +16,28 @@
//============================================================================ //============================================================================
#include "SaveKey.hxx" #include "SaveKey.hxx"
#include "MT24LC256.hxx"
#include "SaveKeyWidget.hxx" #include "SaveKeyWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SaveKeyWidget::SaveKeyWidget(GuiObject* boss, const GUI::Font& font, SaveKeyWidget::SaveKeyWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller) int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller) : FlashWidget(boss, font, x, y, controller)
{ {
bool leftport = myController.jack() == Controller::Left; init(boss, font, x, y);
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);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SaveKeyWidget::handleCommand(CommandSender*, int cmd, int, int) void SaveKeyWidget::eraseCurrent()
{ {
if(cmd == kEEPROMErase) SaveKey& skey = static_cast<SaveKey&>(myController);
{
SaveKey& skey = static_cast<SaveKey&>(myController); skey.eraseCurrent();
skey.myEEPROM->erase(); }
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 #ifndef SAVEKEY_WIDGET_HXX
#define SAVEKEY_WIDGET_HXX #define SAVEKEY_WIDGET_HXX
class ButtonWidget;
#include "Control.hxx" #include "Control.hxx"
#include "ControllerWidget.hxx" #include "FlashWidget.hxx"
class SaveKeyWidget : public ControllerWidget class SaveKeyWidget : public FlashWidget
{ {
public: public:
SaveKeyWidget(GuiObject* boss, const GUI::Font& font, int x, int y, SaveKeyWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
@ -31,12 +29,8 @@ class SaveKeyWidget : public ControllerWidget
virtual ~SaveKeyWidget() = default; virtual ~SaveKeyWidget() = default;
private: private:
ButtonWidget* myEEPROMErase; void eraseCurrent() override;
enum { kEEPROMErase = 'eeER' }; bool isPageUsed(uInt32 page) override;
private:
void loadConfig() override { }
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
SaveKeyWidget() = delete; SaveKeyWidget() = delete;

View File

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

View File

@ -202,7 +202,7 @@ void ToggleWidget::handleCommand(CommandSender* sender, int cmd,
{ {
switch (cmd) switch (cmd)
{ {
case kSetPositionCmd: case GuiObject::kSetPositionCmd:
if (_selectedItem != data) if (_selectedItem != data)
{ {
_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 := src/debugger/gui
MODULE_OBJS := \ 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/AudioWidget.o \
src/debugger/gui/CpuWidget.o \ src/debugger/gui/BoosterWidget.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/Cart0840Widget.o \ src/debugger/gui/Cart0840Widget.o \
src/debugger/gui/Cart2KWidget.o \ src/debugger/gui/Cart2KWidget.o \
src/debugger/gui/Cart3EWidget.o \
src/debugger/gui/Cart3EPlusWidget.o \ src/debugger/gui/Cart3EPlusWidget.o \
src/debugger/gui/Cart3EWidget.o \
src/debugger/gui/Cart3FWidget.o \ src/debugger/gui/Cart3FWidget.o \
src/debugger/gui/Cart4A50Widget.o \ src/debugger/gui/Cart4A50Widget.o \
src/debugger/gui/Cart4KWidget.o \
src/debugger/gui/Cart4KSCWidget.o \ src/debugger/gui/Cart4KSCWidget.o \
src/debugger/gui/Cart4KWidget.o \
src/debugger/gui/CartARWidget.o \ src/debugger/gui/CartARWidget.o \
src/debugger/gui/CartBFSCWidget.o \
src/debugger/gui/CartBFWidget.o \
src/debugger/gui/CartBUSWidget.o \ src/debugger/gui/CartBUSWidget.o \
src/debugger/gui/CartCDFWidget.o \ src/debugger/gui/CartCDFWidget.o \
src/debugger/gui/CartCMWidget.o \ src/debugger/gui/CartCMWidget.o \
src/debugger/gui/CartCTYWidget.o \ src/debugger/gui/CartCTYWidget.o \
src/debugger/gui/CartCVWidget.o \
src/debugger/gui/CartCVPlusWidget.o \ src/debugger/gui/CartCVPlusWidget.o \
src/debugger/gui/CartCVWidget.o \
src/debugger/gui/CartDASHWidget.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/CartDPCPlusWidget.o \
src/debugger/gui/CartDPCWidget.o \
src/debugger/gui/CartE0Widget.o \ src/debugger/gui/CartE0Widget.o \
src/debugger/gui/CartE7Widget.o \ src/debugger/gui/CartE7Widget.o \
src/debugger/gui/CartEFWidget.o \
src/debugger/gui/CartEFSCWidget.o \ src/debugger/gui/CartEFSCWidget.o \
src/debugger/gui/CartBFWidget.o \ src/debugger/gui/CartEFWidget.o \
src/debugger/gui/CartBFSCWidget.o \
src/debugger/gui/CartDFWidget.o \
src/debugger/gui/CartDFSCWidget.o \
src/debugger/gui/CartF0Widget.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/CartF4SCWidget.o \
src/debugger/gui/CartF4Widget.o \
src/debugger/gui/CartF6SCWidget.o \ src/debugger/gui/CartF6SCWidget.o \
src/debugger/gui/CartF6Widget.o \
src/debugger/gui/CartF8SCWidget.o \ src/debugger/gui/CartF8SCWidget.o \
src/debugger/gui/CartFAWidget.o \ src/debugger/gui/CartF8Widget.o \
src/debugger/gui/CartFA2Widget.o \ src/debugger/gui/CartFA2Widget.o \
src/debugger/gui/CartFAWidget.o \
src/debugger/gui/CartFEWidget.o \ src/debugger/gui/CartFEWidget.o \
src/debugger/gui/CartMDMWidget.o \ src/debugger/gui/CartMDMWidget.o \
src/debugger/gui/CartRamWidget.o \
src/debugger/gui/CartSBWidget.o \ src/debugger/gui/CartSBWidget.o \
src/debugger/gui/CartUAWidget.o \ src/debugger/gui/CartUAWidget.o \
src/debugger/gui/CartWDWidget.o \ src/debugger/gui/CartWDWidget.o \
src/debugger/gui/CartX07Widget.o \ src/debugger/gui/CartX07Widget.o \
src/debugger/gui/JoystickWidget.o \ src/debugger/gui/CpuWidget.o \
src/debugger/gui/PaddleWidget.o \ src/debugger/gui/DataGridOpsWidget.o \
src/debugger/gui/BoosterWidget.o \ src/debugger/gui/DataGridWidget.o \
src/debugger/gui/DebuggerDialog.o \
src/debugger/gui/DelayQueueWidget.o \
src/debugger/gui/DrivingWidget.o \ src/debugger/gui/DrivingWidget.o \
src/debugger/gui/KeyboardWidget.o \ src/debugger/gui/FlashWidget.o \
src/debugger/gui/GenesisWidget.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/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 += \ MODULE_DIRS += \
src/debugger/gui src/debugger/gui

View File

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

View File

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

View File

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

View File

@ -21,6 +21,7 @@
class SerialPort; class SerialPort;
#include "Control.hxx" #include "Control.hxx"
#include "SaveKey.hxx"
#include "MT24LC256.hxx" #include "MT24LC256.hxx"
/** /**
@ -32,10 +33,8 @@ class SerialPort;
@author B. Watson @author B. Watson
*/ */
class AtariVox : public Controller class AtariVox : public SaveKey
{ {
friend class AtariVoxWidget;
public: public:
/** /**
Create a new AtariVox controller plugged into the specified jack Create a new AtariVox controller plugged into the specified jack
@ -86,18 +85,10 @@ class AtariVox : public Controller
*/ */
void reset() override; void reset() override;
/** string about(bool swappedPorts) const override { return Controller::about(swappedPorts) + myAboutString; }
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;
private: private:
void clockDataIn(bool value); void clockDataIn(bool value);
void shiftIn(bool value);
private: private:
// Instance of an real serial port on the system // Instance of an real serial port on the system
@ -105,9 +96,6 @@ class AtariVox : public Controller
// bytes directly to it // bytes directly to it
SerialPort& mySerialPort; SerialPort& mySerialPort;
// The EEPROM used in the AtariVox
unique_ptr<MT24LC256> myEEPROM;
// How many bits have been shifted into the shift register? // How many bits have been shifted into the shift register?
uInt8 myShiftCount; uInt8 myShiftCount;

View File

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

View File

@ -152,12 +152,17 @@ class Cartridge4A50 : public Cartridge
private: 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 address The address to query
@param flags A bitfield of DisasmType directives for the given address
*/ */
uInt8 getAccessFlags(uInt16 address) const override; 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; 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 // Set the page accessing method for the RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE)
{ {

View File

@ -156,12 +156,17 @@ class CartridgeAR : public Cartridge
private: 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 address The address to query
@param flags A bitfield of DisasmType directives for the given address
*/ */
uInt8 getAccessFlags(uInt16 address) const override; 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; void setAccessFlags(uInt16 address, uInt8 flags) override;
// Handle a change to the bank configuration // 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 // Set the page accessing method for the RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) 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 #ifdef THUMB_SUPPORT
// Create Thumbulator ARM emulator // 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); settings.getBool("thumb.trapfatal"), Thumbulator::ConfigureFor::BUS, this);
#endif #endif
setInitialState(); setInitialState();
@ -528,7 +529,6 @@ uInt32 CartridgeBUS::thumbCallback(uInt8 function, uInt32 value1, uInt32 value2)
// _GetWavePtr - return the counter // _GetWavePtr - return the counter
case 2: case 2:
return myMusicCounters[value1]; return myMusicCounters[value1];
break;
// _SetWaveSize - set size of waveform buffer // _SetWaveSize - set size of waveform buffer
case 3: case 3:

View File

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

View File

@ -143,7 +143,7 @@ bool CartridgeCM::bank(uInt16 bank)
if((mySWCHA & 0x30) == 0x20) if((mySWCHA & 0x30) == 0x20)
access.directPokeBase = &myRAM[addr & 0x7FF]; access.directPokeBase = &myRAM[addr & 0x7FF];
else else
access.directPokeBase = 0; access.directPokeBase = nullptr;
mySystem->setPageAccess(addr, access); 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 // Set the page accessing method for the RAM writing pages
access.directPeekBase = 0; access.directPeekBase = nullptr;
access.codeAccessBase = 0; access.codeAccessBase = nullptr;
access.type = System::PA_WRITE; access.type = System::PA_WRITE;
for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE) 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 // Set the page accessing method for the RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE) 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); mySystem->setPageAccess(addr, access);
// Set the page accessing method for the RAM writing pages // Set the page accessing method for the RAM writing pages
access.directPeekBase = 0; access.directPeekBase = nullptr;
access.codeAccessBase = 0; access.codeAccessBase = nullptr;
access.type = System::PA_WRITE; access.type = System::PA_WRITE;
for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE) 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 // Set the page accessing method for the RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE) 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 // Set the page accessing method for the RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE)
{ {

View File

@ -57,7 +57,7 @@ void CartridgeE0::install(System& system)
myCurrentSlice[3] = 7; myCurrentSlice[3] = 7;
// Set the page accessing methods for the hot spots in the last segment // Set the page accessing methods for the hot spots in the last segment
access.directPeekBase = 0; access.directPeekBase = nullptr;
access.codeAccessBase = &myCodeAccessBase[8128]; access.codeAccessBase = &myCodeAccessBase[8128];
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = (0x1FE0 & ~System::PAGE_MASK); addr < 0x2000; 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 // Set the page accessing method for the 256 bytes of RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1900; addr < 0x1A00; addr += System::PAGE_SIZE) 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 // Set the page accessing method for the 1K slice of RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE) 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 // Set the page accessing method for the RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) 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 // Set the page accessing method for the RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) 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 // Set the page accessing method for the RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) 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 // Set the page accessing method for the RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1080; addr < 0x1100; addr += System::PAGE_SIZE) 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 // Set the page accessing method for the RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1100; addr < 0x1200; addr += System::PAGE_SIZE) 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 // Set the page accessing method for the RAM reading pages
access.directPokeBase = 0; access.directPokeBase = nullptr;
access.type = System::PA_READ; access.type = System::PA_READ;
for(uInt16 addr = 0x1100; addr < 0x1200; addr += System::PAGE_SIZE) for(uInt16 addr = 0x1100; addr < 0x1200; addr += System::PAGE_SIZE)
{ {

View File

@ -132,8 +132,6 @@ uInt8 CartridgeWD::peek(uInt16 address)
else else
return mySegment3[address & 0x03FF]; 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& lp = myConsole.leftController();
Controller& rp = myConsole.rightController(); Controller& rp = myConsole.rightController();
lp.myAnalogPinValue[Controller::Nine] = Controller::maximumResistance; lp.myAnalogPinValue[Controller::Nine] = Controller::maximumResistance;
lp.myAnalogPinValue[Controller::Five] = Controller::minimumResistance; lp.myAnalogPinValue[Controller::Five] = Controller::minimumResistance;
lp.myDigitalPinState[Controller::Six] = true; lp.myDigitalPinState[Controller::Six] = true;

View File

@ -109,8 +109,6 @@ class CompuMate
Called after *all* digital pins have been written on Port A. Called after *all* digital pins have been written on Port A.
Only update on the left controller; the right controller will Only update on the left controller; the right controller will
happen at the same cycle and is redundant. happen at the same cycle and is redundant.
@param value The entire contents of the SWCHA register
*/ */
void controlWrite(uInt8) override { void controlWrite(uInt8) override {
if(myJack == Controller::Left) myHandler.update(); 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 // Finally, add remaining info about the console
myConsoleInfo.CartName = myProperties.get(Cartridge_Name); myConsoleInfo.CartName = myProperties.get(Cartridge_Name);
myConsoleInfo.CartMD5 = myProperties.get(Cartridge_MD5); myConsoleInfo.CartMD5 = myProperties.get(Cartridge_MD5);
myConsoleInfo.Control0 = myLeftControl->about(); bool swappedPorts = properties().get(Console_SwapPorts) == "YES";
myConsoleInfo.Control1 = myRightControl->about(); myConsoleInfo.Control0 = myLeftControl->about(swappedPorts);
myConsoleInfo.Control1 = myRightControl->about(swappedPorts);
myConsoleInfo.BankSwitch = myCart->about(); myConsoleInfo.BankSwitch = myCart->about();
myCart->setRomName(myConsoleInfo.CartName); myCart->setRomName(myConsoleInfo.CartName);
@ -724,13 +725,9 @@ void Console::setTIAProperties()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::setControllers(const string& rommd5) void Console::setControllers(const string& rommd5)
{ {
// Setup the controllers based on properties // Check for CompuMate scheme; it is special in that a handler creates both
const string& left = myProperties.get(Controller_Left); // controllers for us, and associates them with the bankswitching class
const string& right = myProperties.get(Controller_Right); if(myCart->detectedType() == "CM")
// 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")
{ {
myCMHandler = make_shared<CompuMate>(*this, myEvent, *mySystem); myCMHandler = make_shared<CompuMate>(*this, myEvent, *mySystem);
@ -743,153 +740,114 @@ void Console::setControllers(const string& rommd5)
myLeftControl = std::move(myCMHandler->leftController()); myLeftControl = std::move(myCMHandler->leftController());
myRightControl = std::move(myCMHandler->rightController()); 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 else
{ {
myLeftControl = std::move(rightC); // Setup the controllers based on properties
myRightControl = std::move(leftC); 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(); 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() void Console::loadUserPalette()
{ {
@ -953,7 +911,7 @@ void Console::generateColorLossPalette()
uInt32* palette[9] = { uInt32* palette[9] = {
&ourNTSCPalette[0], &ourPALPalette[0], &ourSECAMPalette[0], &ourNTSCPalette[0], &ourPALPalette[0], &ourSECAMPalette[0],
&ourNTSCPaletteZ26[0], &ourPALPaletteZ26[0], &ourSECAMPaletteZ26[0], &ourNTSCPaletteZ26[0], &ourPALPaletteZ26[0], &ourSECAMPaletteZ26[0],
0, 0, 0 nullptr, nullptr, nullptr
}; };
if(myUserPaletteDefined) if(myUserPaletteDefined)
{ {
@ -964,7 +922,7 @@ void Console::generateColorLossPalette()
for(int i = 0; i < 9; ++i) for(int i = 0; i < 9; ++i)
{ {
if(palette[i] == 0) if(palette[i] == nullptr)
continue; continue;
// Fill the odd numbered palette entries with gray values (calculated // 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); 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 Loads a user-defined palette file (from OSystem::paletteFile), filling the
appropriate user-defined palette arrays. appropriate user-defined palette arrays.

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