Merge branch 'trapif_attempt_1'

This commit is contained in:
thrust26 2017-10-08 18:31:26 +02:00
commit 20babc832a
11 changed files with 563 additions and 148 deletions

View File

@ -30,6 +30,7 @@ class CartDebugWidget;
#include "Cart.hxx"
#include "DebuggerSystem.hxx"
#include "System.hxx"
#include "M6502.hxx"
// Function type for CartDebug instance methods
class CartDebug;
@ -127,6 +128,11 @@ class CartDebug : public DebuggerSystem
// write port area.
int readFromWritePort();
// Return the base (= non-mirrored) address of the last CPU read
int lastReadBaseAddress() { return mySystem.m6502().lastReadBaseAddress(); }
// Return the base (= non-mirrored) address of the last CPU write
int lastWriteBaseAddress() { return mySystem.m6502().lastWriteBaseAddress(); }
// The following two methods are meant to be used together
// First, a call is made to disassemble(), which updates the disassembly
// list; it will figure out when an actual complete disassembly is

View File

@ -85,7 +85,6 @@ static const char* const builtin_functions[][3] = {
{ "_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 }
};
@ -104,7 +103,10 @@ static const char* const pseudo_registers[][2] = {
{ "_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)" },
{ "_vsync", "Whether vertical sync is enabled (1 or 0)" },
// CPU address access functions:
/*{ "__lastread", "last CPU read address" },
{ "__lastwrite", "last CPU write address" },*/
// empty string marks end of list, do not remove
{ 0, 0 }
@ -166,7 +168,7 @@ FBInitStatus Debugger::initializeVideo()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::start(const string& message, int address)
bool Debugger::start(const string& message, int address, bool read)
{
if(myOSystem.eventHandler().enterDebugMode())
{
@ -175,8 +177,7 @@ bool Debugger::start(const string& message, int address)
ostringstream buf;
buf << message;
if(address > -1)
buf << Common::Base::HEX4 << address;
buf << cartDebug().getLabel(address, read);
myDialog->message().setText(buf.str());
return true;
}
@ -367,24 +368,43 @@ bool Debugger::breakPoint(uInt16 bp)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleReadTrap(uInt16 t)
void Debugger::addReadTrap(uInt16 t)
{
readTraps().initialize();
readTraps().toggle(t);
readTraps().add(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleWriteTrap(uInt16 t)
void Debugger::addWriteTrap(uInt16 t)
{
writeTraps().initialize();
writeTraps().toggle(t);
writeTraps().add(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::toggleTrap(uInt16 t)
void Debugger::addTrap(uInt16 t)
{
toggleReadTrap(t);
toggleWriteTrap(t);
addReadTrap(t);
addWriteTrap(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::removeReadTrap(uInt16 t)
{
readTraps().initialize();
readTraps().remove(t);
}
void Debugger::removeWriteTrap(uInt16 t)
{
writeTraps().initialize();
writeTraps().remove(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::removeTrap(uInt16 t)
{
removeReadTrap(t);
removeWriteTrap(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -398,6 +418,48 @@ bool Debugger::writeTrap(uInt16 t)
{
return writeTraps().isInitialized() && writeTraps().isSet(t);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Debugger::getBaseAddress(uInt32 addr, bool read)
{
if((addr & 0x1080) == 0x0000) // (addr & 0b 0001 0000 1000 0000) == 0b 0000 0000 0000 0000
if(read)
// ADDR_TIA read (%xxx0 xxxx 0xxx ????)
return addr & 0x000f; // 0b 0000 0000 0000 1111
else
// ADDR_TIA write (%xxx0 xxxx 0x?? ????)
return addr & 0x003f; // 0b 0000 0000 0011 1111
// ADDR_ZPRAM (%xxx0 xx0x 1??? ????)
if((addr & 0x1280) == 0x0080) // (addr & 0b 0001 0010 1000 0000) == 0b 0000 0000 1000 0000
return addr & 0x00ff; // 0b 0000 0000 1111 1111
// ADDR_ROM
if(addr & 0x1000)
return addr & 0x1fff; // 0b 0001 1111 1111 1111
// ADDR_IO read/write I/O registers (%xxx0 xx1x 1xxx x0??)
if((addr & 0x1284) == 0x0280) // (addr & 0b 0001 0010 1000 0100) == 0b 0000 0010 1000 0000
return addr & 0x0283; // 0b 0000 0010 1000 0011
// ADDR_IO write timers (%xxx0 xx1x 1xx1 ?1??)
if(!read && (addr & 0x1294) == 0x0294) // (addr & 0b 0001 0010 1001 0100) == 0b 0000 0010 1001 0100
return addr & 0x029f; // 0b 0000 0010 1001 1111
// ADDR_IO read timers (%xxx0 xx1x 1xxx ?1x0)
if(read && (addr & 0x1285) == 0x0284) // (addr & 0b 0001 0010 1000 0101) == 0b 0000 0010 1000 0100
return addr & 0x028c; // 0b 0000 0010 1000 1100
// ADDR_IO read timer/PA7 interrupt (%xxx0 xx1x 1xxx x1x1)
if(read && (addr & 0x1285) == 0x0285) // (addr & 0b 0001 0010 1000 0101) == 0b 0000 0010 1000 0101
return addr & 0x0285; // 0b 0000 0010 1000 0101
// ADDR_IO write PA7 edge control (%xxx0 xx1x 1xx0 x1??)
if(!read && (addr & 0x1294) == 0x0284) // (addr & 0b 0001 0010 1001 0100) == 0b 0000 0010 1000 0100
return addr & 0x0287; // 0b 0000 0010 1000 0111
return 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::nextScanline(int lines)

View File

@ -88,7 +88,7 @@ class Debugger : public DialogContainer
@param message Message to display when entering debugger
@param address An address associated with the message
*/
bool start(const string& message = "", int address = -1);
bool start(const string& message = "", int address = -1, bool read = true);
bool startWithFatalError(const string& message = "");
/**
@ -145,8 +145,8 @@ class Debugger : public DialogContainer
TiaOutputWidget& tiaOutput() const { return myDialog->tiaOutput(); }
PackedBitArray& breakPoints() const { return mySystem.m6502().breakPoints(); }
PackedBitArray& readTraps() const { return mySystem.m6502().readTraps(); }
PackedBitArray& writeTraps() const { return mySystem.m6502().writeTraps(); }
TrapArray& readTraps() const { return mySystem.m6502().readTraps(); }
TrapArray& writeTraps() const { return mySystem.m6502().writeTraps(); }
/**
Run the debugger command and return the result.
@ -214,6 +214,7 @@ class Debugger : public DialogContainer
{ mySystem.setAccessFlags(addr, flags); }
void setBreakPoint(uInt16 bp, bool set);
uInt32 getBaseAddress(uInt32 addr, bool read);
bool patchROM(uInt16 addr, uInt8 value);
@ -256,9 +257,12 @@ class Debugger : public DialogContainer
void toggleBreakPoint(uInt16 bp);
bool breakPoint(uInt16 bp);
void toggleReadTrap(uInt16 t);
void toggleWriteTrap(uInt16 t);
void toggleTrap(uInt16 t);
void addReadTrap(uInt16 t);
void addWriteTrap(uInt16 t);
void addTrap(uInt16 t);
void removeReadTrap(uInt16 t);
void removeWriteTrap(uInt16 t);
void removeTrap(uInt16 t);
bool readTrap(uInt16 t);
bool writeTrap(uInt16 t);
void clearAllTraps();

View File

@ -543,31 +543,32 @@ string DebuggerParser::eval()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string DebuggerParser::trapStatus(uInt32 addr, bool& enabled)
string DebuggerParser::trapStatus(const Trap& trap)
{
string result;
result += Base::toString(addr);
result += ": ";
bool r = debugger.readTrap(addr);
bool w = debugger.writeTrap(addr);
enabled = r || w;
if(r && w)
result += "read|write";
else if(r)
result += "read";
else if(w)
result += "write";
else
result += "none";
const string& l = debugger.cartDebug().getLabel(addr, !w);
if(l != "") {
result += " (";
result += l;
result += ")";
stringstream result;
string lblb = debugger.cartDebug().getLabel(trap.begin, !trap.write);
string lble = debugger.cartDebug().getLabel(trap.end, !trap.write);
if(lblb != "") {
result << " (";
result << lblb;
}
return result;
if(trap.begin != trap.end)
{
if(lble != "")
{
if (lblb != "")
result << " ";
else
result << " (";
result << lble;
}
}
if (lblb != "" || lble != "")
result << ")";
return result.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -589,9 +590,10 @@ bool DebuggerParser::saveScriptFile(string file)
if(debugger.breakPoint(i))
out << "break #" << i << endl;
// TODO: new trapif
for(uInt32 i = 0; i < 0x10000; ++i)
{
bool r = debugger.readTrap(i);
bool r = debugger.readTrap(i);
bool w = debugger.writeTrap(i);
if(r && w)
@ -744,8 +746,9 @@ void DebuggerParser::executeClearconfig()
// "cleartraps"
void DebuggerParser::executeCleartraps()
{
myTraps.clear();
debugger.clearAllTraps();
debugger.cpuDebug().m6502().clearCondTraps();
myTraps.clear();
commandResult << "all traps cleared";
}
@ -861,6 +864,24 @@ void DebuggerParser::executeDelfunction()
commandResult << "function " << argStrings[0] << " built-in or not found";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "deltrap"
void DebuggerParser::executeDeltrap()
{
int index = args[0];
if(debugger.cpuDebug().m6502().delCondTrap(index))
{
for(uInt32 addr = myTraps[index]->begin; addr <= myTraps[index]->end; ++addr)
executeTrapRW(addr, myTraps[index]->read, myTraps[index]->write, false);
// @sa666666: please check this:
Vec::removeAt(myTraps, index);
commandResult << "removed trap " << Base::toString(index);
}
else
commandResult << "no such trap";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "delwatch"
void DebuggerParser::executeDelwatch()
@ -1089,7 +1110,7 @@ void DebuggerParser::executeListbreaks()
commandResult << "\nbreakifs:\n";
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;
}
}
@ -1127,11 +1148,37 @@ void DebuggerParser::executeListfunctions()
// "listtraps"
void DebuggerParser::executeListtraps()
{
if(myTraps.size() > 0)
StringList names = debugger.cpuDebug().m6502().getCondTrapNames();
if(myTraps.size() != names.size())
{
bool enabled = true;
for(const auto& trap: myTraps)
commandResult << trapStatus(trap, enabled) << " + mirrors" << endl;
commandResult << "Internal error! Different trap sizes.";
return;
}
if (names.size() > 0)
{
for(uInt32 i = 0; i < names.size(); i++)
{
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";
commandResult << " " << names[i];
commandResult << " " << Base::toString(myTraps[i]->begin);
if (myTraps[i]->begin != myTraps[i]->end)
commandResult << " " << Base::toString(myTraps[i]->end);
commandResult << trapStatus(*myTraps[i]);
commandResult << " + mirrors";
if(i != (names.size() - 1)) commandResult << endl;
}
}
else
commandResult << "no traps set";
@ -1482,71 +1529,153 @@ void DebuggerParser::executeTrace()
// "trap"
void DebuggerParser::executeTrap()
{
if(argCount > 2)
{
commandResult << red("Command takes one or two arguments") << endl;
return;
}
executeTraps(true, true, "trap");
}
uInt32 beg = args[0];
uInt32 end = argCount == 2 ? args[1] : beg;
if(beg > 0xFFFF || end > 0xFFFF)
{
commandResult << red("One or more addresses are invalid") << endl;
return;
}
for(uInt32 addr = beg; addr <= end; ++addr)
executeTrapRW(addr, true, true);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "trapif"
void DebuggerParser::executeTrapif()
{
executeTraps(true, true, "trapif", true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "trapread"
void DebuggerParser::executeTrapread()
{
if(argCount > 2)
{
commandResult << red("Command takes one or two arguments") << endl;
return;
}
executeTraps(true, false, "trapread");
}
uInt32 beg = args[0];
uInt32 end = argCount == 2 ? args[1] : beg;
if(beg > 0xFFFF || end > 0xFFFF)
{
commandResult << red("One or more addresses are invalid") << endl;
return;
}
for(uInt32 addr = beg; addr <= end; ++addr)
executeTrapRW(addr, true, false);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "trapreadif"
void DebuggerParser::executeTrapreadif()
{
executeTraps(true, false, "trapreadif", true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// "trapwrite"
void DebuggerParser::executeTrapwrite()
{
if(argCount > 2)
{
commandResult << red("Command takes one or two arguments") << endl;
return;
}
uInt32 beg = args[0];
uInt32 end = argCount == 2 ? args[1] : beg;
if(beg > 0xFFFF || end > 0xFFFF)
{
commandResult << red("One or more addresses are invalid") << endl;
return;
}
for(uInt32 addr = beg; addr <= end; ++addr)
executeTrapRW(addr, false, true);
executeTraps(false, true, "trapwrite");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// wrapper function for trap/trapread/trapwrite commands
void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
// "trapwriteif"
void DebuggerParser::executeTrapwriteif()
{
executeTraps(false, true, "trapwriteif", true);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Wrapper function for trap(if)s
void DebuggerParser::executeTraps(bool read, bool write, string command, bool hasCond)
{
if(hasCond)
{
if(argCount < 1 || argCount > 3)
{
commandResult << red("Command takes one to three arguments");
return;
}
}
else
{
if(argCount > 2)
{
commandResult << red("Command takes one or two arguments");
return;
}
}
int ofs = hasCond ? 1 : 0;
uInt32 begin = args[ofs];
uInt32 end = argCount == ofs + 2 ? args[ofs + 1] : begin;
// 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);
if(begin > 0xFFFF || end > 0xFFFF || begin > end)
{
commandResult << red("One or more addresses are invalid");
return;
}
// parenthesize provided and address range condition(s) (begin)
stringstream parserBuf, displayBuf;
if(hasCond)
parserBuf << "(" << argStrings[0] << ")&&(";
// add address range condition(s) to provided condition
if(read)
{
if(beginRead != endRead)
parserBuf << "__lastread>=" << Base::toString(beginRead) << "&&__lastread<=" << Base::toString(endRead);
else
parserBuf << "__lastread==" << Base::toString(beginRead);
}
if(read && write)
parserBuf << "||";
if(write)
{
if(beginWrite != endWrite)
parserBuf << "__lastwrite>=" << Base::toString(beginWrite) << "&&__lastwrite<=" << Base::toString(endWrite);
else
parserBuf << "__lastwrite==" << Base::toString(beginWrite);
}
// parenthesize provided condition (end)
if(hasCond)
parserBuf << ")";
const string parserCondition = parserBuf.str();
int res = YaccParser::parse(parserCondition.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 == parserCondition)
{
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, parserCondition });
}
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))
{
@ -1556,10 +1685,11 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{
if((i & 0x1080) == 0x0000)
{
if(read && (i & 0x000F) == addr)
debugger.toggleReadTrap(i);
if(write && (i & 0x003F) == addr)
debugger.toggleWriteTrap(i);
// @sa666666: This seems wrong. E.g. trapread 40 4f will never trigger
if(read && (i & 0x000F) == (addr & 0x000F))
add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
if(write && (i & 0x003F) == (addr & 0x003F))
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
}
}
break;
@ -1568,10 +1698,12 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{
for(uInt32 i = 0; i <= 0xFFFF; ++i)
{
if((i & 0x1080) == 0x0080 && (i & 0x0200) != 0x0000 && (i & 0x02FF) == addr)
if((i & 0x1280) == 0x0280 && (i & 0x029F) == (addr & 0x029F))
{
if(read) debugger.toggleReadTrap(i);
if(write) debugger.toggleWriteTrap(i);
if(read)
add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
if(write)
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
}
}
break;
@ -1580,10 +1712,12 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{
for(uInt32 i = 0; i <= 0xFFFF; ++i)
{
if((i & 0x1080) == 0x0080 && (i & 0x0200) == 0x0000 && (i & 0x00FF) == addr)
if((i & 0x1280) == 0x0080 && (i & 0x00FF) == (addr & 0x00FF))
{
if(read) debugger.toggleReadTrap(i);
if(write) debugger.toggleWriteTrap(i);
if(read)
add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
if(write)
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
}
}
break;
@ -1596,21 +1730,16 @@ void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write)
{
if((i % 0x2000 >= 0x1000) && (i & 0x0FFF) == (addr & 0x0FFF))
{
if(read) debugger.toggleReadTrap(i);
if(write) debugger.toggleWriteTrap(i);
if(read)
add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
if(write)
add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
}
}
}
break;
}
}
bool trapEnabled = false;
const string& result = trapStatus(addr, trapEnabled);
if(trapEnabled) myTraps.insert(addr);
else myTraps.erase(addr);
commandResult << result << " + mirrors" << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1891,6 +2020,16 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeDelfunction)
},
{
"deltrap",
"Delete trap <xx>",
"Example: deltrap 0",
true,
false,
{ kARG_WORD, kARG_END_ARGS },
std::mem_fn(&DebuggerParser::executeDeltrap)
},
{
"delwatch",
"Delete watch <xx>",
@ -2333,6 +2472,17 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeTrap)
},
{
"trapif",
"On <condition> trap R/W access to address(es) xx [yy]",
"Set a conditional R/W trap on the given address(es) and all mirrors\nCondition can include multiple items.\n"
"Example: trapif _scan>100 GRP0, trapif _bank==1 f000 f100",
true,
false,
{ kARG_WORD, kARG_MULTI_BYTE },
std::mem_fn(&DebuggerParser::executeTrapif)
},
{
"trapread",
"Trap read access to address(es) xx [yy]",
@ -2344,6 +2494,17 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeTrapread)
},
{
"trapreadif",
"On <condition> trap read access to address(es) xx [yy]",
"Set a conditional read trap on the given address(es) and all mirrors\nCondition can include multiple items.\n"
"Example: trapreadif _scan>100 GRP0, trapreadif _bank==1 f000 f100",
true,
false,
{ kARG_WORD, kARG_MULTI_BYTE },
std::mem_fn(&DebuggerParser::executeTrapreadif)
},
{
"trapwrite",
"Trap write access to address(es) xx [yy]",
@ -2355,6 +2516,17 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
std::mem_fn(&DebuggerParser::executeTrapwrite)
},
{
"trapwriteif",
"On <condition> trap write access to address(es) xx [yy]",
"Set a conditional write trap on the given address(es) and all mirrors\nCondition can include multiple items.\n"
"Example: trapwriteif _scan>100 GRP0, trapwriteif _bank==1 f000 f100",
true,
false,
{ kARG_WORD, kARG_MULTI_BYTE },
std::mem_fn(&DebuggerParser::executeTrapwriteif)
},
{
"type",
"Show disassembly type for address xx [yy]",

View File

@ -68,7 +68,7 @@ class DebuggerParser
bool saveScriptFile(string file);
private:
enum { kNumCommands = 72 };
enum { kNumCommands = 76 };
// Constants for argument processing
enum {
@ -100,7 +100,15 @@ class DebuggerParser
parameters parms[10];
std::function<void (DebuggerParser*)> executor;
};
struct Trap
{
bool read;
bool write;
uInt32 begin;
uInt32 end;
string condition;
};
// Reference to our debugger object
Debugger& debugger;
@ -117,9 +125,12 @@ class DebuggerParser
StringList myWatches;
// Keep track of traps (read and/or write)
std::set<uInt32> myTraps;
string trapStatus(uInt32 addr, bool& enabled);
vector<unique_ptr<Trap>> myTraps;
/*std::set<uInt32> myTraps;
std::vector<uInt32> myTrapIfs;*/
string trapStatus(const Trap& trap);
// List of available command methods
void executeA();
@ -139,8 +150,9 @@ class DebuggerParser
void executeData();
void executeDebugColors();
void executeDefine();
void executeDelbreakif();
void executeDelbreakif();
void executeDelfunction();
void executeDeltrap();
void executeDelwatch();
void executeDisasm();
void executeDump();
@ -184,9 +196,13 @@ class DebuggerParser
void executeTia();
void executeTrace();
void executeTrap();
void executeTrapif();
void executeTrapread();
void executeTrapwrite();
void executeTrapRW(uInt32 addr, bool read, bool write); // not exposed by debugger
void executeTrapreadif();
void executeTrapwrite();
void executeTrapwriteif();
void executeTraps(bool read, bool write, string command, bool cond = false);
void executeTrapRW(uInt32 addr, bool read, bool write, bool add = true); // not exposed by debugger
void executeType();
void executeUHex();
void executeUndef();

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

@ -55,6 +55,8 @@ M6502::M6502(const Settings& settings)
myLastAddress(0),
myLastPeekAddress(0),
myLastPokeAddress(0),
myLastPeekBaseAddress(0),
myLastPokeBaseAddress(0),
myLastSrcAddressS(-1),
myLastSrcAddressA(-1),
myLastSrcAddressX(-1),
@ -65,7 +67,7 @@ M6502::M6502(const Settings& settings)
{
#ifdef DEBUGGER_SUPPORT
myDebugger = nullptr;
myJustHitTrapFlag = false;
myJustHitReadTrapFlag = myJustHitWriteTrapFlag = false;
#endif
}
@ -98,7 +100,7 @@ void M6502::reset()
// Load PC from the reset vector
PC = uInt16(mySystem->peek(0xfffc)) | (uInt16(mySystem->peek(0xfffd)) << 8);
myLastAddress = myLastPeekAddress = myLastPokeAddress = 0;
myLastAddress = myLastPeekAddress = myLastPokeAddress = myLastPeekBaseAddress = myLastPokeBaseAddress;
myLastSrcAddressS = myLastSrcAddressA =
myLastSrcAddressX = myLastSrcAddressY = -1;
myDataAddressForPoke = 0;
@ -120,18 +122,23 @@ inline uInt8 M6502::peek(uInt16 address, uInt8 flags)
}
////////////////////////////////////////////////
mySystem->incrementCycles(SYSTEM_CYCLES_PER_CPU);
uInt8 result = mySystem->peek(address, flags);
myLastPeekAddress = address;
#ifdef DEBUGGER_SUPPORT
if(myReadTraps.isInitialized() && myReadTraps.isSet(address))
{
myJustHitTrapFlag = true;
myHitTrapInfo.message = "RTrap: ";
myHitTrapInfo.address = address;
myLastPeekBaseAddress = myDebugger->getBaseAddress(myLastPeekAddress, true); // mirror handling
int cond = evalCondTraps();
if(cond > -1)
{
myJustHitReadTrapFlag = true;
myHitTrapInfo.message = "RTrap(" + myTrapCondNames[cond] + "): ";
myHitTrapInfo.address = address;
}
}
#endif // DEBUGGER_SUPPORT
uInt8 result = mySystem->peek(address, flags);
myLastPeekAddress = address;
return result;
}
@ -146,19 +153,23 @@ inline void M6502::poke(uInt16 address, uInt8 value, uInt8 flags)
myLastAddress = address;
}
////////////////////////////////////////////////
mySystem->incrementCycles(SYSTEM_CYCLES_PER_CPU);
mySystem->incrementCycles(SYSTEM_CYCLES_PER_CPU);
mySystem->poke(address, value, flags);
myLastPokeAddress = address;
#ifdef DEBUGGER_SUPPORT
if(myWriteTraps.isInitialized() && myWriteTraps.isSet(address))
{
myJustHitTrapFlag = true;
myHitTrapInfo.message = "WTrap: ";
myHitTrapInfo.address = address;
myLastPokeBaseAddress = myDebugger->getBaseAddress(myLastPokeAddress, false); // mirror handling
int cond = evalCondTraps();
if(cond > -1)
{
myJustHitWriteTrapFlag = true;
myHitTrapInfo.message = "WTrap(" + myTrapCondNames[cond] + "): ";
myHitTrapInfo.address = address;
}
}
#endif // DEBUGGER_SUPPORT
mySystem->poke(address, value, flags);
myLastPokeAddress = address;
#endif // DEBUGGER_SUPPORT
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -189,11 +200,11 @@ bool M6502::execute(uInt32 number)
for(; !myExecutionStatus && (number != 0); --number)
{
#ifdef DEBUGGER_SUPPORT
if(myJustHitTrapFlag)
if(myJustHitReadTrapFlag || myJustHitWriteTrapFlag)
{
if(myDebugger && myDebugger->start(myHitTrapInfo.message, myHitTrapInfo.address))
{
myJustHitTrapFlag = false;
myJustHitReadTrapFlag = myJustHitWriteTrapFlag = false;
if(myDebugger && myDebugger->start(myHitTrapInfo.message, myHitTrapInfo.address, myJustHitReadTrapFlag))
{
return true;
}
}
@ -416,13 +427,15 @@ uInt32 M6502::addCondBreak(Expression* e, const string& name)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::delCondBreak(uInt32 brk)
bool M6502::delCondBreak(uInt32 brk)
{
if(brk < myBreakConds.size())
{
Vec::removeAt(myBreakConds, brk);
Vec::removeAt(myBreakCondNames, brk);
return true;
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -437,4 +450,37 @@ const StringList& M6502::getCondBreakNames() const
{
return myBreakCondNames;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 M6502::addCondTrap(Expression* e, const string& name)
{
myTrapConds.emplace_back(e);
myTrapCondNames.push_back(name);
return uInt32(myTrapConds.size() - 1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool M6502::delCondTrap(uInt32 brk)
{
if(brk < myTrapConds.size())
{
Vec::removeAt(myTrapConds, brk);
Vec::removeAt(myTrapCondNames, brk);
return true;
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::clearCondTraps()
{
myTrapConds.clear();
myTrapCondNames.clear();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const StringList& M6502::getCondTrapNames() const
{
return myTrapCondNames;
}
#endif // DEBUGGER_SUPPORT

View File

@ -24,6 +24,7 @@
#include "Expression.hxx"
#include "PackedBitArray.hxx"
#include "TrapArray.hxx"
#endif
class Settings;
@ -148,7 +149,21 @@ class M6502 : public Serializable
return myLastPokeAddress ?
(myLastPokeAddress != myLastPeekAddress ? myLastPeekAddress : 0) :
myLastPeekAddress;
}
}
/**
Return the last address that was part of a read/peek.
@return The address of the last read
*/
uInt16 lastReadBaseAddress() const { return myLastPeekBaseAddress; }
/**
Return the last address that was part of a write/poke.
@return The address of the last write
*/
uInt16 lastWriteBaseAddress() const { return myLastPokeBaseAddress; }
/**
Return the source of the address that was used for a write/poke.
@ -207,13 +222,24 @@ class M6502 : public Serializable
void attach(Debugger& debugger);
PackedBitArray& breakPoints() { return myBreakPoints; }
PackedBitArray& readTraps() { return myReadTraps; }
PackedBitArray& writeTraps() { return myWriteTraps; }
//PackedBitArray& readTraps() { return myReadTraps; }
//PackedBitArray& writeTraps() { return myWriteTraps; }
//PackedBitArray& readTrapIfs() { return myReadTrapIfs; }
//PackedBitArray& writeTrapIfs() { return myWriteTrapIfs; }
TrapArray& readTraps() { return myReadTraps; }
TrapArray& writeTraps() { return myWriteTraps; }
// methods for 'breakif' handling
uInt32 addCondBreak(Expression* e, const string& name);
void delCondBreak(uInt32 brk);
bool delCondBreak(uInt32 brk);
void clearCondBreaks();
const StringList& getCondBreakNames() const;
// methods for 'trapif' handling
uInt32 addCondTrap(Expression* e, const string& name);
bool delCondTrap(uInt32 brk);
void clearCondTraps();
const StringList& getCondTrapNames() const;
#endif // DEBUGGER_SUPPORT
private:
@ -329,6 +355,9 @@ class M6502 : public Serializable
/// Indicates the last address which was accessed specifically
/// by a peek or poke command
uInt16 myLastPeekAddress, myLastPokeAddress;
/// Indicates the last base (= non-mirrored) address which was
/// accessed specifically by a peek or poke command
uInt16 myLastPeekBaseAddress, myLastPokeBaseAddress;
/// Indicates the last address used to access data by a peek command
/// for the CPU registers (S/A/X/Y)
@ -359,14 +388,25 @@ class M6502 : public Serializable
return -1; // no break hit
}
Int32 evalCondTraps()
{
for(uInt32 i = 0; i < myTrapConds.size(); i++)
if(myTrapConds[i]->evaluate())
return i;
return -1; // no trapif hit
}
/// Pointer to the debugger for this processor or the null pointer
Debugger* myDebugger;
// Addresses for which the specified action should occur
PackedBitArray myBreakPoints, myReadTraps, myWriteTraps;
PackedBitArray myBreakPoints;// , myReadTraps, myWriteTraps, myReadTrapIfs, myWriteTrapIfs;
TrapArray myReadTraps, myWriteTraps;
// Did we just now hit a trap?
bool myJustHitTrapFlag;
bool myJustHitReadTrapFlag;
bool myJustHitWriteTrapFlag;
struct HitTrapInfo {
string message;
int address;
@ -375,6 +415,8 @@ class M6502 : public Serializable
vector<unique_ptr<Expression>> myBreakConds;
StringList myBreakCondNames;
vector<unique_ptr<Expression>> myTrapConds;
StringList myTrapCondNames;
#endif // DEBUGGER_SUPPORT
private:

View File

@ -575,6 +575,7 @@
<ClInclude Include="..\debugger\gui\RomListSettings.hxx" />
<ClInclude Include="..\debugger\gui\SaveKeyWidget.hxx" />
<ClInclude Include="..\debugger\gui\TrakBallWidget.hxx" />
<ClInclude Include="..\debugger\TrapArray.hxx" />
<ClInclude Include="..\emucore\AmigaMouse.hxx" />
<ClInclude Include="..\emucore\AtariMouse.hxx" />
<ClInclude Include="..\emucore\BSType.hxx" />

View File

@ -1751,6 +1751,9 @@
<ClInclude Include="..\debugger\gui\TrakBallWidget.hxx">
<Filter>Header Files\debugger</Filter>
</ClInclude>
<ClInclude Include="..\debugger\TrapArray.hxx">
<Filter>Header Files\debugger</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="stella.ico">

View File

@ -206,6 +206,10 @@ CartMethod getCartSpecial(char* ch)
return &CartDebug::getBank;
else if(BSPF::equalsIgnoreCase(ch, "_rwport"))
return &CartDebug::readFromWritePort;
else if(BSPF::equalsIgnoreCase(ch, "__lastread"))
return &CartDebug::lastReadBaseAddress;
else if(BSPF::equalsIgnoreCase(ch, "__lastwrite"))
return &CartDebug::lastWriteBaseAddress;
else
return 0;
}