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:
stephena 2010-08-31 16:37:27 +00:00
parent e0dd8b1451
commit f8b2621ab5
5 changed files with 263 additions and 21 deletions

View File

@ -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
{

View File

@ -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;

View File

@ -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",

View File

@ -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();

View File

@ -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);