mirror of https://github.com/stella-emu/stella.git
Many big improvements to disassembler directives. Directives are no
longer simply added to a list, but intelligently 'merged' (so that the entire range represented by all directives contains no overlap). This makes the disassembly a little faster, since it doesn't have to iterate redundantly. Still TODO in this area is intelligent insertion for the same type (ie, if inserting in between like blocks, the blocks should coalesce, instead of being clipped and then a new range inserted in between). Added 'loadconfig' and 'saveconfig' debugger prompt commands, which will eventually access Distella-like config files. No implementation is present yet. Added 'listconfig' debugger command, which lists all directives currently defined by the user, as well as the directives resulting from a disassembly of a bank (taking into account extra knowledge that Stella has WRT cached entry points). This command can show information for a specified bank, or all banks in the cart. User-defined directives can now be removed; simply issue the same command that caused an insertion (ie, attempting to insert the same type and range will remove it instead). Fixed bug in Distella processing of directives; similar to the standalone Distella, directives should be processed *before* any automatic code determination is done. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2121 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
parent
e0dd8b1451
commit
f8b2621ab5
|
@ -24,6 +24,8 @@
|
|||
#include "CpuDebug.hxx"
|
||||
#include "CartDebug.hxx"
|
||||
|
||||
#define HEX4 setw(4) << setfill('0') << hex
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CartDebug::CartDebug(Debugger& dbg, Console& console, const RamAreaList& areas)
|
||||
: DebuggerSystem(dbg, console),
|
||||
|
@ -339,18 +341,85 @@ string CartDebug::disassemble(uInt16 start, uInt16 lines) const
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartDebug::addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 end)
|
||||
bool CartDebug::addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 end)
|
||||
{
|
||||
BankInfo& info = (myDebugger.cpuDebug().pc() & 0x1000) ?
|
||||
myBankInfo[getBank()] : myBankInfo[myBankInfo.size()-1];
|
||||
|
||||
DirectiveList& list = info.directiveList;
|
||||
DirectiveTag tag;
|
||||
tag.type = type;
|
||||
tag.start = start;
|
||||
tag.end = end;
|
||||
info.directiveList.push_back(tag);
|
||||
|
||||
// FIXME - process the request somehow; don't just automatically add it to the list
|
||||
DirectiveList::iterator i;
|
||||
|
||||
// If the same directive and range is added, consider it a removal instead
|
||||
for(i = list.begin(); i != list.end(); ++i)
|
||||
{
|
||||
if(i->type == tag.type && i->start == tag.start && i->end == tag.end)
|
||||
{
|
||||
list.erase(i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, scan the list and make space for a 'smart' merge
|
||||
// Note that there are 4 possibilities:
|
||||
// 1: a range is completely inside the new range
|
||||
// 2: a range is completely outside the new range
|
||||
// 3: a range overlaps at the beginning of the new range
|
||||
// 4: a range overlaps at the end of the new range
|
||||
for(i = list.begin(); i != list.end(); ++i)
|
||||
{
|
||||
// Case 1: remove range that is completely inside new range
|
||||
if(i->start >= tag.start && i->end <= tag.end)
|
||||
{
|
||||
i = list.erase(i);
|
||||
}
|
||||
// Case 2: split the old range
|
||||
else if(i->start <= tag.start && i->end >= tag.end)
|
||||
{
|
||||
// Create new endpoint
|
||||
DirectiveTag tag2;
|
||||
tag2.type = i->type;
|
||||
tag2.start = tag.end + 1;
|
||||
tag2.end = i->end;
|
||||
|
||||
// Modify startpoint
|
||||
i->end = tag.start - 1;
|
||||
|
||||
// Insert new endpoint
|
||||
i++;
|
||||
i = list.insert(i, tag2);
|
||||
break; // no need to go further; this is the insertion point
|
||||
}
|
||||
// Case 3: truncate end of old range
|
||||
else if(i->end >= tag.start)
|
||||
{
|
||||
i->end = tag.start - 1;
|
||||
}
|
||||
// Case 4: truncate start of old range
|
||||
else if(i->start <= tag.end)
|
||||
{
|
||||
i->start = tag.end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// We now know that the new range can be inserted without overlap
|
||||
for(i = list.begin(); i != list.end(); ++i)
|
||||
{
|
||||
if(tag.end < i->start)
|
||||
{
|
||||
i = list.insert(i, tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Otherwise, add the tag at the end
|
||||
if(i == list.end())
|
||||
list.push_back(tag);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -515,18 +584,61 @@ string CartDebug::loadSymbolFile(const string& f)
|
|||
return "loaded " + file + " OK";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string CartDebug::loadConfigFile(const string& f)
|
||||
{
|
||||
return "NOT YET IMPLEMENTED";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string CartDebug::saveConfigFile(const string& f)
|
||||
{
|
||||
return "NOT YET IMPLEMENTED";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string CartDebug::listConfig(int bank)
|
||||
{
|
||||
uInt32 startbank = 0, endbank = bankCount();
|
||||
if(bank >= 0 && bank < bankCount())
|
||||
{
|
||||
startbank = bank;
|
||||
endbank = startbank + 1;
|
||||
}
|
||||
|
||||
ostringstream buf;
|
||||
buf << "(items marked '*' are user-defined)" << endl;
|
||||
for(uInt32 b = startbank; b < endbank; ++b)
|
||||
{
|
||||
BankInfo& info = myBankInfo[b];
|
||||
buf << "[" << b << "]" << endl;
|
||||
for(DirectiveList::const_iterator i = info.directiveList.begin();
|
||||
i != info.directiveList.end(); ++i)
|
||||
{
|
||||
buf << "(*) "
|
||||
<< (i->type == CartDebug::CODE ? "CODE" :
|
||||
i->type == CartDebug::DATA ? "DATA" :
|
||||
"GFX")
|
||||
<< " " << hex << i->start << " " << hex << i->end << endl;
|
||||
}
|
||||
getBankDirectives(buf, info);
|
||||
}
|
||||
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartDebug::getCompletions(const char* in, StringList& completions) const
|
||||
{
|
||||
// First scan system equates
|
||||
for(uInt16 addr = 0x00; addr <= 0x0F; ++addr)
|
||||
if(BSPF_strncasecmp(ourTIAMnemonicR[addr], in, strlen(in)) == 0)
|
||||
if(BSPF_startsWithIgnoreCase(ourTIAMnemonicR[addr], in))
|
||||
completions.push_back(ourTIAMnemonicR[addr]);
|
||||
for(uInt16 addr = 0x00; addr <= 0x3F; ++addr)
|
||||
if(BSPF_strncasecmp(ourTIAMnemonicW[addr], in, strlen(in)) == 0)
|
||||
if(BSPF_startsWithIgnoreCase(ourTIAMnemonicW[addr], in))
|
||||
completions.push_back(ourTIAMnemonicW[addr]);
|
||||
for(uInt16 addr = 0; addr <= 0x297-0x280; ++addr)
|
||||
if(BSPF_strncasecmp(ourIOMnemonic[addr], in, strlen(in)) == 0)
|
||||
if(BSPF_startsWithIgnoreCase(ourIOMnemonic[addr], in))
|
||||
completions.push_back(ourIOMnemonic[addr]);
|
||||
|
||||
// Now scan user-defined labels
|
||||
|
@ -534,7 +646,7 @@ void CartDebug::getCompletions(const char* in, StringList& completions) const
|
|||
for(iter = myUserAddresses.begin(); iter != myUserAddresses.end(); ++iter)
|
||||
{
|
||||
const char* l = iter->first.c_str();
|
||||
if(BSPF_strncasecmp(l, in, strlen(in)) == 0)
|
||||
if(BSPF_startsWithIgnoreCase(l, in))
|
||||
completions.push_back(l);
|
||||
}
|
||||
}
|
||||
|
@ -581,6 +693,55 @@ CartDebug::AddrType CartDebug::addressType(uInt16 addr) const
|
|||
return type;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartDebug::getBankDirectives(ostringstream& buf, BankInfo& info) const
|
||||
{
|
||||
// Start with the offset for this bank
|
||||
buf << "ORG " << HEX4 << info.offset << endl;
|
||||
|
||||
// Disassemble the bank, then scan it for an up-to-date description
|
||||
DisassemblyList list;
|
||||
uInt16 banksize =
|
||||
!BSPF_equalsIgnoreCase(myConsole.cartridge().name(), "Cartridge2K") ? 4 : 2;
|
||||
DiStella distella(*this, list, info, banksize, true);
|
||||
|
||||
if(list.size() == 0)
|
||||
return;
|
||||
|
||||
DisasmType type = list[0].type;
|
||||
uInt16 start = list[0].address, last = list[1].address;
|
||||
if(start == 0) start = info.offset;
|
||||
for(uInt32 i = 1; i < list.size(); ++i)
|
||||
{
|
||||
const DisassemblyTag& tag = list[i];
|
||||
|
||||
if(tag.type == CartDebug::NONE)
|
||||
continue;
|
||||
else if(tag.type != type) // new range has started
|
||||
{
|
||||
// If switching from a data range, make sure the endpoint is valid
|
||||
// This is necessary because DATA sections don't always generate
|
||||
// consecutive numbers/addresses for the range
|
||||
if(type == CartDebug::DATA)
|
||||
last = tag.address - 1;
|
||||
|
||||
buf << (type == CartDebug::CODE ? "CODE" :
|
||||
type == CartDebug::DATA ? "DATA" :
|
||||
"GFX")
|
||||
<< " " << HEX4 << start << " " << HEX4 << last << endl;
|
||||
type = tag.type;
|
||||
start = last = tag.address;
|
||||
}
|
||||
else
|
||||
last = tag.address;
|
||||
}
|
||||
// Grab the last directive, making sure it accounts for all remaining space
|
||||
buf << (type == CartDebug::CODE ? "CODE" :
|
||||
type == CartDebug::DATA ? "DATA" :
|
||||
"GFX")
|
||||
<< " " << HEX4 << start << " " << HEX4 << (info.offset+info.end) << endl;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string CartDebug::extractLabel(const char *c) const
|
||||
{
|
||||
|
|
|
@ -160,9 +160,9 @@ class CartDebug : public DebuggerSystem
|
|||
@param start The start address (inclusive) to mark with the given type
|
||||
@param end The end address (inclusive) to mark with the given type
|
||||
|
||||
@return The disassembly represented as a string
|
||||
@return True if directive was added, else false if it was removed
|
||||
*/
|
||||
void addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 end);
|
||||
bool addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 end);
|
||||
|
||||
// The following are convenience methods that query the cartridge object
|
||||
// for the desired information.
|
||||
|
@ -207,9 +207,21 @@ class CartDebug : public DebuggerSystem
|
|||
int getAddress(const string& label) const;
|
||||
|
||||
/**
|
||||
Load user equates from the given symbol file (as generated by DASM)
|
||||
Load user equates from the given symbol file (as generated by DASM).
|
||||
*/
|
||||
string loadSymbolFile(const string& file);
|
||||
string loadSymbolFile(const string& file = "");
|
||||
|
||||
/**
|
||||
Load/save Distella config file (Distella directives)
|
||||
*/
|
||||
string loadConfigFile(const string& file = "");
|
||||
string saveConfigFile(const string& file = "");
|
||||
|
||||
/**
|
||||
Show Distella directives (both set by the user and determined by Distella)
|
||||
for the given bank (or all banks, if no bank is specified.
|
||||
*/
|
||||
string listConfig(int bank = -1);
|
||||
|
||||
/**
|
||||
Methods used by the command parser for tab-completion
|
||||
|
@ -250,6 +262,10 @@ class CartDebug : public DebuggerSystem
|
|||
// Return whether the search address was actually in the list
|
||||
bool fillDisassemblyList(BankInfo& bankinfo, bool resolvedata, uInt16 search);
|
||||
|
||||
// Analyze of bank of ROM, generating a list of Distella directives
|
||||
// based on its disassembly
|
||||
void getBankDirectives(ostringstream& buf, BankInfo& info) const;
|
||||
|
||||
// Extract labels and values from the given character stream
|
||||
string extractLabel(const char* c) const;
|
||||
int extractValue(const char* c) const;
|
||||
|
|
|
@ -805,8 +805,9 @@ void DebuggerParser::executeCode()
|
|||
return;
|
||||
}
|
||||
|
||||
debugger->cartDebug().addDirective(CartDebug::CODE, args[0], args[1]);
|
||||
commandResult << "added CODE directive on range $" << hex << args[0]
|
||||
bool result = debugger->cartDebug().addDirective(
|
||||
CartDebug::CODE, args[0], args[1]);
|
||||
commandResult << (result ? "added" : "removed") << " CODE directive on range $" << hex << args[0]
|
||||
<< " $" << hex << args[1];
|
||||
debugger->myRom->invalidate();
|
||||
}
|
||||
|
@ -845,8 +846,9 @@ void DebuggerParser::executeData()
|
|||
return;
|
||||
}
|
||||
|
||||
debugger->cartDebug().addDirective(CartDebug::DATA, args[0], args[1]);
|
||||
commandResult << "added DATA directive on range $" << hex << args[0]
|
||||
bool result = debugger->cartDebug().addDirective(
|
||||
CartDebug::DATA, args[0], args[1]);
|
||||
commandResult << (result ? "added" : "removed") << " DATA directive on range $" << hex << args[0]
|
||||
<< " $" << hex << args[1];
|
||||
debugger->myRom->invalidate();
|
||||
}
|
||||
|
@ -983,8 +985,9 @@ void DebuggerParser::executeGfx()
|
|||
return;
|
||||
}
|
||||
|
||||
debugger->cartDebug().addDirective(CartDebug::GFX, args[0], args[1]);
|
||||
commandResult << "added GFX directive on range $" << hex << args[0]
|
||||
bool result = debugger->cartDebug().addDirective(
|
||||
CartDebug::GFX, args[0], args[1]);
|
||||
commandResult << (result ? "added" : "removed") << " GFX directive on range $" << hex << args[0]
|
||||
<< " $" << hex << args[1];
|
||||
debugger->myRom->invalidate();
|
||||
}
|
||||
|
@ -1048,6 +1051,16 @@ void DebuggerParser::executeListbreaks()
|
|||
commandResult << "no breakpoints set";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "listconfig"
|
||||
void DebuggerParser::executeListconfig()
|
||||
{
|
||||
if(argCount == 1)
|
||||
commandResult << debugger->cartDebug().listConfig(args[0]);
|
||||
else
|
||||
commandResult << debugger->cartDebug().listConfig();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "listfunctions"
|
||||
void DebuggerParser::executeListfunctions()
|
||||
|
@ -1083,6 +1096,18 @@ void DebuggerParser::executeListtraps()
|
|||
commandResult << "no traps set";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "loadconfig"
|
||||
void DebuggerParser::executeLoadconfig()
|
||||
{
|
||||
if(argCount == 1)
|
||||
commandResult << debugger->cartDebug().loadConfigFile(argStrings[0]);
|
||||
else
|
||||
commandResult << debugger->cartDebug().loadConfigFile();
|
||||
|
||||
debugger->myRom->invalidate();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "loadstate"
|
||||
void DebuggerParser::executeLoadstate()
|
||||
|
@ -1291,6 +1316,16 @@ void DebuggerParser::executeSave()
|
|||
commandResult << red("I/O error");
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "saveconfig"
|
||||
void DebuggerParser::executeSaveconfig()
|
||||
{
|
||||
if(argCount == 1)
|
||||
commandResult << debugger->cartDebug().saveConfigFile(argStrings[0]);
|
||||
else
|
||||
commandResult << debugger->cartDebug().saveConfigFile();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "saverom"
|
||||
void DebuggerParser::executeSaverom()
|
||||
|
@ -1682,6 +1717,15 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
|
|||
&DebuggerParser::executeListbreaks
|
||||
},
|
||||
|
||||
{
|
||||
"listconfig",
|
||||
"List Distella config directives [bank xx]",
|
||||
false,
|
||||
false,
|
||||
{ kARG_WORD, kARG_MULTI_BYTE },
|
||||
&DebuggerParser::executeListconfig
|
||||
},
|
||||
|
||||
{
|
||||
"listfunctions",
|
||||
"List user-defined functions",
|
||||
|
@ -1700,6 +1744,15 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
|
|||
&DebuggerParser::executeListtraps
|
||||
},
|
||||
|
||||
{
|
||||
"loadconfig",
|
||||
"Load Distella config file [from file xx]",
|
||||
false,
|
||||
true,
|
||||
{ kARG_FILE, kARG_MULTI_BYTE },
|
||||
&DebuggerParser::executeLoadconfig
|
||||
},
|
||||
|
||||
{
|
||||
"loadstate",
|
||||
"Load emulator state xx (0-9)",
|
||||
|
@ -1835,6 +1888,15 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
|
|||
&DebuggerParser::executeSave
|
||||
},
|
||||
|
||||
{
|
||||
"saveconfig",
|
||||
"Save Distella config file [to file xx]",
|
||||
false,
|
||||
false,
|
||||
{ kARG_FILE, kARG_MULTI_BYTE },
|
||||
&DebuggerParser::executeSaveconfig
|
||||
},
|
||||
|
||||
{
|
||||
"saverom",
|
||||
"Save (possibly patched) ROM to file xx",
|
||||
|
|
|
@ -83,7 +83,7 @@ class DebuggerParser
|
|||
|
||||
private:
|
||||
enum {
|
||||
kNumCommands = 60,
|
||||
kNumCommands = 63,
|
||||
kMAX_ARG_TYPES = 10
|
||||
};
|
||||
|
||||
|
@ -161,8 +161,10 @@ class DebuggerParser
|
|||
void executeGfx();
|
||||
void executeHelp();
|
||||
void executeListbreaks();
|
||||
void executeListconfig();
|
||||
void executeListfunctions();
|
||||
void executeListtraps();
|
||||
void executeLoadconfig();
|
||||
void executeLoadstate();
|
||||
void executeLoadsym();
|
||||
void executeN();
|
||||
|
@ -178,6 +180,7 @@ class DebuggerParser
|
|||
void executeRunToPc();
|
||||
void executeS();
|
||||
void executeSave();
|
||||
void executeSaveconfig();
|
||||
void executeSaverom();
|
||||
void executeSaveses();
|
||||
void executeSavestate();
|
||||
|
|
|
@ -87,6 +87,9 @@ DiStella::DiStella(const CartDebug& dbg, CartDebug::DisassemblyList& list,
|
|||
memset(labels, 0, 0x1000);
|
||||
myAddressQueue.push(start);
|
||||
|
||||
// Process any directives first, as they override automatic code determination
|
||||
processDirectives(info.directiveList);
|
||||
|
||||
if(resolvedata)
|
||||
{
|
||||
while(!myAddressQueue.empty())
|
||||
|
@ -134,9 +137,6 @@ DiStella::DiStella(const CartDebug& dbg, CartDebug::DisassemblyList& list,
|
|||
}
|
||||
}
|
||||
|
||||
// Process any directives later, as they override automatic code determination
|
||||
processDirectives(info.directiveList);
|
||||
|
||||
// Second pass
|
||||
disasm(myOffset, 2);
|
||||
|
||||
|
|
Loading…
Reference in New Issue