@@ -0,0 +1,17 @@
#include "symbol_file_interface.hpp"

class SymbolMap;

class ViceLabelFile : public SymbolFileInterface {
public:
ViceLabelFile() {}
virtual ~ViceLabelFile() {}

const char *getName() const;
const char *getDescription() const;

int32_t scoreReadString(const nall::lstring &rows) const;
bool read(const nall::lstring &rows, SymbolMap *map) const;

uint32_t getFeatures() const;
};
@@ -0,0 +1,136 @@
#include "wla_symbol_file.hpp"

// ------------------------------------------------------------------------
const char *WlaSymbolFile::getName() const {
return "WLA symbol file";
}

// ------------------------------------------------------------------------
const char *WlaSymbolFile::getDescription() const {
return "WLA-Assembler symbol file format";
}

// ------------------------------------------------------------------------
uint32_t WlaSymbolFile::getFeatures() const {
return 0
| SymbolFileInterface::Readable
| SymbolFileInterface::Symbols
| SymbolFileInterface::Files
| SymbolFileInterface::LineMap
;
}

// ------------------------------------------------------------------------
string WlaSymbolFile::filteredRow(const string &input) const {
string row(input);

row.trim("\r");
optional<unsigned> comment = row.position(";");
if (comment) {
unsigned index = comment();
if (index == 0) {
return "";
}
row = nall::substr(row, 0, index);
}

row.trim(" ");
return row;
}

// ------------------------------------------------------------------------
int32_t WlaSymbolFile::scoreReadString(const lstring &rows) const {
if (rows.size() == 0) {
return -1;
}

bool isInLabelsSection = false;

for (uint32_t i=0; i<rows.size(); i++) {
const string &row = filteredRow(rows[i]);
if (row.length() == 0) {
continue;
}

if (row == "[labels]") {
isInLabelsSection = true;
} else if (row[0] == '[') {
isInLabelsSection = false;
} else if (isInLabelsSection) {
uint32_t address = (nall::hex(nall::substr(row, 0, 2)) << 16) | nall::hex(nall::substr(row, 3, 4));

if (address > 0) {
return 1;
}
}
}

return -1;
}

// ------------------------------------------------------------------------
bool WlaSymbolFile::read(const lstring &rows, SymbolMap *map) const {
enum Section {
SECTION_UNKNOWN,
SECTION_LABELS,
SECTION_COMMENTS,
SECTION_DEBUG,
SECTION_FILES,
SECTION_SOURCEMAP,
};

Section section = SECTION_LABELS;
for (int i=0; i<rows.size(); i++) {
string row = filteredRow(rows[i]);
if (row.length() == 0) {
continue;
}

if (row[0] == '[') {
if (row == "[labels]") { section = SECTION_LABELS; }
else if (row == "[comments]") { section = SECTION_COMMENTS; }
else if (row == "[addr-to-line-mapping]") { section = SECTION_SOURCEMAP; }
else if (row == "[source-files]") { section = SECTION_FILES; }

else { section = SECTION_UNKNOWN; }
continue;
}

switch (section) {
case SECTION_LABELS:
map->addLocation(
(nall::hex(nall::substr(row, 0, 2)) << 16) | nall::hex(nall::substr(row, 3, 4)),
nall::substr(row, 8, row.length() - 8)
);
break;

case SECTION_COMMENTS:
map->addComment(
(nall::hex(nall::substr(row, 0, 2)) << 16) | nall::hex(nall::substr(row, 3, 4)),
nall::substr(row, 8, row.length() - 8)
);
break;

case SECTION_SOURCEMAP:
map->addSourceLine(
(nall::hex(nall::substr(row, 0, 2)) << 16) | nall::hex(nall::substr(row, 3, 4)),
nall::hex(nall::substr(row, 8, 4)),
nall::hex(nall::substr(row, 13, 8))
);
break;

case SECTION_FILES:
map->addSourceFile(
nall::hex(nall::substr(row, 0, 4)),
nall::hex(nall::substr(row, 5, 8)),
nall::substr(row, 14, row.length() - 14)
);
break;

case SECTION_UNKNOWN:
break;
}
}

return true;
}
@@ -0,0 +1,19 @@
#include "symbol_file_interface.hpp"

class SymbolMap;

class WlaSymbolFile : public SymbolFileInterface {
public:
WlaSymbolFile() {}
virtual ~WlaSymbolFile() {}

const char *getName() const;
const char *getDescription() const;

int32_t scoreReadString(const nall::lstring &rows) const;
bool read(const nall::lstring &rows, SymbolMap *map) const;

nall::string filteredRow(const nall::string &input) const;

uint32_t getFeatures() const;
};
@@ -0,0 +1,72 @@
#include "symbol_file_adapters.hpp"

#include "adapters/fma_symbol_file.cpp"
#include "adapters/wla_symbol_file.cpp"
#include "adapters/vice_label_file.cpp"

// ------------------------------------------------------------------------
SymbolFileAdapters::SymbolFileAdapters() {
registerAdapter(new FmaSymbolFile());
registerAdapter(new WlaSymbolFile());
registerAdapter(new ViceLabelFile());
}

// ------------------------------------------------------------------------
void SymbolFileAdapters::registerAdapter(SymbolFileInterface* adapter) {
adapters.append(adapter);
}

// ------------------------------------------------------------------------
SymbolFileInterface *SymbolFileAdapters::findBestAdapter(const nall::lstring &rows) {
SymbolFileInterface *current = NULL;
int32_t currentScore = -1;

for (uint32_t i=0; i<adapters.size(); i++) {
SymbolFileInterface *adapter = adapters[i];
uint32_t features = adapter->getFeatures();

if ((features & SymbolFileInterface::Readable) == 0) {
continue;
}

int32_t score = adapter->scoreReadString(rows);
if (score > currentScore) {
currentScore = score;
current = adapter;
}
}

return current;
}

// ------------------------------------------------------------------------
SymbolFileInterface *SymbolFileAdapters::fetchAdapter(uint32_t requiredFeatures, uint32_t optionalFeatures) {
SymbolFileInterface *current = NULL;
uint32_t currentScore = 0;

for (uint32_t i=0; i<adapters.size(); i++) {
SymbolFileInterface *adapter = adapters[i];
uint32_t features = adapter->getFeatures();
uint32_t score = 0;

if ((features & requiredFeatures) != requiredFeatures) {
continue;
}

features &= optionalFeatures;
for (; features; features >>= 1) {
if (features & 1) {
score++;
}
}

if (score > currentScore || current == NULL) {
current = adapter;
currentScore= score;
}
}

return current;
}

// ------------------------------------------------------------------------
@@ -0,0 +1,14 @@
class SymbolFileInterface;

class SymbolFileAdapters {
public:
SymbolFileAdapters();

void registerAdapter(SymbolFileInterface*);

SymbolFileInterface *findBestAdapter(const nall::lstring &rows);
SymbolFileInterface *fetchAdapter(uint32_t requiredFeatures, uint32_t optionalFeatures=0);

protected:
nall::linear_vector<SymbolFileInterface*> adapters;
};
@@ -0,0 +1,282 @@
#include "symbol_map.moc"
#include "symbol_file_adapters.cpp"

// ------------------------------------------------------------------------
Symbol Symbols::getSymbol() {
for (uint32_t i=0; i<symbols.size(); i++) {
if (symbols[i].isSymbol()) {
return symbols[i];
}
}

return Symbol::createInvalid();
}

// ------------------------------------------------------------------------
Symbol Symbols::getComment() {
for (uint32_t i=0; i<symbols.size(); i++) {
if (symbols[i].isComment()) {
return symbols[i];
}
}

return Symbol::createInvalid();
}

// ------------------------------------------------------------------------
Symbol Symbols::getSourceLine()
{
for (uint32_t i = 0; i < symbols.size(); i++) {
if (symbols[i].isSourceLine()) {
return symbols[i];
}
}

return Symbol::createInvalid();
}


// ------------------------------------------------------------------------
SymbolMap::SymbolMap() {
isValid = false;
adapters = new SymbolFileAdapters();
}

// ------------------------------------------------------------------------
int32_t SymbolMap::getSymbolIndex(uint32_t address) {
revalidate();

int32_t left = 0;
int32_t right = symbols.size() - 1;

while (right >= left) {
uint32_t cur = ((right - left) >> 1) + left;
uint32_t curaddr = symbols[cur].address;

if (address < curaddr) {
right = cur - 1;
} else if (address > curaddr) {
left = cur + 1;
} else {
return cur;
}
}

return -1;
}

// ------------------------------------------------------------------------
void SymbolMap::addLocation(uint32_t address, const string &name) {
addSymbol(address, Symbol::createLocation(address, name));
}

// ------------------------------------------------------------------------
void SymbolMap::addComment(uint32_t address, const string &name) {
addSymbol(address, Symbol::createComment(address, name));
}

// ------------------------------------------------------------------------
void SymbolMap::addSymbol(uint32_t address, const Symbol &name) {
isValid = false;

int32_t right = symbols.size();
for (int32_t i=0; i<right; i++) {
if (symbols[i].address == address) {
symbols[i].symbols.append(Symbol(name));
return;
}
}


Symbols s;
s.address = address;
s.symbols.append(Symbol(name));
symbols.append(s);
}

// ------------------------------------------------------------------------
void SymbolMap::addSourceLine(uint32_t address, uint32_t file, uint32_t line) {
AddressToSourceLine newMapping;
newMapping.address = address;
newMapping.file = file;
newMapping.line = line;
addressToSourceLineMappings.append(newMapping);
}

// ------------------------------------------------------------------------
void SymbolMap::addSourceFile(uint32_t fileId, uint32_t checksum, const string &filename) {

string sourceFileData;
if (fileId < sourceFileLines.size() && sourceFileLines[fileId].size() > 0) {
// todo
//debugger->echo(string() << "WARNING: While parsing symbols, file index " << fileId << " appeared for file \"" << sourceFiles[fileId].filename << "\" and \"" << filename << "\". Disassembly listing for either of these may be incorrect or unavailable.<br>");
}
else if (sourceFileData.readfile(filename)) {
unsigned long local_checksum = crc32_calculate((const uint8_t*)(sourceFileData()), sourceFileData.length());
if (checksum != local_checksum) {
// todo
//debugger->echo(string() << "WARNING: \"" << sourceFiles[fileId].filename << "\" has been modified since the ROM's symbols were built. Disassembly listing for this file may be incorrect or unavailable.<br>");
}
else {
sourceFileLines[fileId].split("\n", sourceFileData);
}
}
}

// ------------------------------------------------------------------------
void SymbolMap::finishUpdates() {
// populate sourceline data, now that we have sourceline and sourcefile information
// make sure symbols and addressToSourceLineMappings are sorted by addr
if (addressToSourceLineMappings.size() > 0) {
revalidate();
nall::sort(&addressToSourceLineMappings[0], addressToSourceLineMappings.size());


uint32_t symbolIndex = 0;
uint32_t symbolIndexEnd = symbols.size();
for (int i = 0; i < addressToSourceLineMappings.size(); ++i) {
AddressToSourceLine addrToLine = addressToSourceLineMappings[i];
string sourceLine;
if (addrToLine.file < sourceFileLines.size() && addrToLine.line < sourceFileLines[addrToLine.file].size()) {
sourceLine = sourceFileLines[addrToLine.file][addrToLine.line - 1]; // -1 because line entries are 1-based
}
else {
char hexAddr[9];
snprintf(hexAddr, 9, "%.8x", addrToLine.address);
// todo
//debugger->echo(string() << "WARNING: Address-to-line mapping for address 0x" << hexAddr << " tried to refer to a file/line location that doesn't exist. File: " << addrToLine.file << ", line: " << addrToLine.line << ".<br>");
continue;
}

// advance symbolindex forward until its address is >= the current addrToLine address
while (symbols[symbolIndex].address < addrToLine.address && symbolIndex < symbolIndexEnd) {
++symbolIndex;
}

// create new symbol if we didn't match the address
if (symbols[symbolIndex].address > addrToLine.address) {
Symbols s;
s.address = addrToLine.address;
s.symbols.append(Symbol::createSourceLine(addrToLine.address, sourceLine));
symbols.append(s);
}
else {
symbols[symbolIndex].symbols.append(Symbol::createSourceLine(addrToLine.address, sourceLine));
}
}
}
emit updated();
}

// ------------------------------------------------------------------------
void SymbolMap::revalidate() {
if (isValid) {
return;
}

// Don't know how to do this with pure nall stuff :(
int numSymbols = symbols.size();
Symbols *temp = new Symbols[numSymbols];
for (int i=0; i<numSymbols; i++) {
temp[i] = symbols[i];
}

nall::sort(temp, numSymbols);

symbols.reset();
symbols.reserve(numSymbols);
for (int i=0; i<numSymbols; i++) {
symbols.append(temp[i]);
}

isValid = true;
}

// ------------------------------------------------------------------------
Symbol SymbolMap::getSymbol(uint32_t address) {
int32_t index = getSymbolIndex(address);
if (index == -1) {
return Symbol::createInvalid();
}

return symbols[index].getSymbol();
}

// ------------------------------------------------------------------------
Symbol SymbolMap::getComment(uint32_t address) {
int32_t index = getSymbolIndex(address);
if (index == -1) {
return Symbol::createInvalid();
}

return symbols[index].getComment();
}

// ------------------------------------------------------------------------
Symbol SymbolMap::getSourceLine(uint32_t address) {
int32_t index = getSymbolIndex(address);
if (index == -1) {
return Symbol::createInvalid();
}

return symbols[index].getSourceLine();
}

// ------------------------------------------------------------------------
void SymbolMap::removeSymbol(uint32_t address, Symbol::Type type) {
int32_t index = getSymbolIndex(address);
if (index == -1) {
return;
}

Symbols &s = symbols[index];
for (int32_t i=0; i<s.symbols.size(); i++) {
if (s.symbols[i].type == type) {
s.symbols.remove(i);
i--;
}
}

if (s.symbols.size() == 0) {
symbols.remove(index);
isValid = false;
}
}

// ------------------------------------------------------------------------
void SymbolMap::loadFromFile(const string &baseName, const string &ext) {
string fileName = baseName;
fileName.append(ext);

::nall::file f;
if (!f.open((const char*)fileName, ::nall::file::mode::read)) {
return;
}

int size = f.size();
char *buffer = new char[size + 1];
buffer[size] = 0;
f.read((uint8_t*)buffer, f.size());
loadFromString(buffer);

delete[] buffer;

f.close();
}

// ------------------------------------------------------------------------
void SymbolMap::loadFromString(const string &file) {
nall::lstring rows;
rows.split("\n", file);

SymbolFileInterface *adapter = adapters->findBestAdapter(rows);
if (adapter == NULL) {
return;
}

if (adapter->read(rows, this)) {
finishUpdates();
}
}

// ------------------------------------------------------------------------
@@ -0,0 +1,133 @@
#ifndef __SYMBOL_MAP__H__
#define __SYMBOL_MAP__H__

class SymbolFileAdapters;

struct Symbol {
enum Type { INVALID, LOCATION, COMMENT, SOURCE_LINE };

static Symbol createInvalid() {
Symbol s;
s.type = INVALID;
return s;
}

static Symbol createComment(uint32_t address, const string &name) {
Symbol s;
s.type = COMMENT;
s.address = address;
s.name = name;
return s;
}

static Symbol createLocation(uint32_t address, const string &name) {
Symbol s;
s.type = LOCATION;
s.address = address;
s.name = name;
return s;
}

static Symbol createSourceLine(uint32_t address, const string &name) {
Symbol s;
s.type = SOURCE_LINE;
s.address = address;
s.name = name;
return s;
}

inline bool isInvalid() const {
return type == INVALID;
}

inline bool isSymbol() const {
return type == LOCATION;
}

inline bool isComment() const {
return type == COMMENT;
}

inline bool isSourceLine() const {
return type == SOURCE_LINE;
}

bool operator <(const Symbol &other) {
return address < other.address;
}

uint32_t address;
string name;
Type type;
};

struct Symbols {
typedef nall::linear_vector<Symbol> SymbolList;

uint32_t address;
SymbolList symbols;

Symbol getSymbol();
Symbol getComment();
Symbol getSourceLine();

bool operator <(const Symbols &other) {
return address < other.address;
}
};

class SymbolMap : public QObject {
Q_OBJECT

public:
SymbolMap();

typedef nall::linear_vector<Symbols> SymbolsLists;

void addLocation(uint32_t address, const string &name);
void addComment(uint32_t address, const string &name);
void addSymbol(uint32_t address, const Symbol &name);
void addCommand(uint32_t id, const string &content);
void addSourceLine(uint32_t address, uint32_t file, uint32_t line);
void addSourceFile(uint32_t fileId, uint32_t checksum, const string &filename);
void removeSymbol(uint32_t address, Symbol::Type type);
void loadFromString(const string &file);
void loadFromFile(const string &baseName, const string &ext);
void saveToFile(const string &baseName, const string &ext);
void finishUpdates();

void revalidate();

int32_t getSymbolIndex(uint32_t address);
Symbol getSymbol(uint32_t address);
Symbol getComment(uint32_t address);
Symbol getSourceLine(uint32_t address);

bool isValid;
SymbolsLists symbols;
SymbolFileAdapters *adapters;

private:

struct AddressToSourceLine {
uint32_t address;
uint32_t file;
uint32_t line;

bool operator <(const AddressToSourceLine &other) { return address < other.address; }
};
nall::linear_vector<AddressToSourceLine> addressToSourceLineMappings;

struct SourceFileInformation {
string filename;
unsigned long checksum;
};
linear_vector<SourceFileInformation> sourceFiles;
linear_vector<lstring> sourceFileLines;

signals:
void updated();

};

#endif
@@ -0,0 +1,202 @@
const char *DEFAULT_SYMBOL_MAP_CPU =
"; default registers used for CPU\n" \
"\n" \
"[labels]; simple labels\n" \
"00:2100 SNES.INIDISP\n" \
"00:2101 SNES.OBSEL\n" \
"00:2102 SNES.OAMADDL\n" \
"00:2103 SNES.OAMADDH\n" \
"00:2104 SNES.OAMDATA\n" \
"00:2105 SNES.BGMODE\n" \
"00:2106 SNES.MOSAIC\n" \
"00:2107 SNES.BG1SC\n" \
"00:2108 SNES.BG2SC\n" \
"00:2109 SNES.BG3SC\n" \
"00:210A SNES.BG3SC\n" \
"00:210B SNES.BG12NBA\n" \
"00:210C SNES.BG34NBA\n" \
"00:210D SNES.BG1HOFS\n" \
"00:210E SNES.BG1VOFS\n" \
"00:210F SNES.BG2HOFS\n" \
"00:2110 SNES.BG2VOFS\n" \
"00:2111 SNES.BG3HOFS\n" \
"00:2112 SNES.BG3VOFS\n" \
"00:2113 SNES.BG4HOFS\n" \
"00:2114 SNES.BG4VOFS\n" \
"00:2115 SNES.VMAIN\n" \
"00:2116 SNES.VMADDL\n" \
"00:2117 SNES.VMADDH\n" \
"00:2118 SNES.VMDATAL\n" \
"00:2119 SNES.VMDATAH\n" \
"00:211A SNES.M7SEL\n" \
"00:211B SNES.M7A\n" \
"00:211C SNES.M7B\n" \
"00:211D SNES.M7C\n" \
"00:211E SNES.M7D\n" \
"00:211F SNES.M7X\n" \
"00:2120 SNES.M7Y\n" \
"00:2121 SNES.CGADD\n" \
"00:2122 SNES.CGDATA\n" \
"00:2123 SNES.W12SEL\n" \
"00:2124 SNES.W34SEL\n" \
"00:2125 SNES.WOBJSEL\n" \
"00:2126 SNES.WH0\n" \
"00:2127 SNES.WH1\n" \
"00:2128 SNES.WH2\n" \
"00:2129 SNES.WH3\n" \
"00:212A SNES.WBGLOG\n" \
"00:212B SNES.WOBJLOG\n" \
"00:212C SNES.TM\n" \
"00:212D SNES.TS\n" \
"00:212E SNES.TMW\n" \
"00:212F SNES.TSW\n" \
"00:2130 SNES.CGWSEL\n" \
"00:2131 SNES.CGADSUB\n" \
"00:2132 SNES.COLDATA\n" \
"00:2133 SNES.SETINI\n" \
"00:2134 SNES.MPYL\n" \
"00:2135 SNES.MPYM\n" \
"00:2136 SNES.MPYH\n" \
"00:2137 SNES.SLHV\n" \
"00:2138 SNES.OAMDATAREAD\n" \
"00:2139 SNES.VMDATALREAD\n" \
"00:213A SNES.VMDATAHREAD\n" \
"00:213B SNES.CGDATAREAD\n" \
"00:213C SNES.OPHCT\n" \
"00:213D SNES.OPVCT\n" \
"00:213E SNES.STAT77\n" \
"00:213F SNES.STAT78\n" \
"00:2140 SNES.APUIO0\n" \
"00:2141 SNES.APUIO1\n" \
"00:2142 SNES.APUIO2\n" \
"00:2143 SNES.APUIO3\n" \
"00:2180 SNES.WMDATA\n" \
"00:2181 SNES.WMADDL\n" \
"00:2182 SNES.WMADDM\n" \
"00:2183 SNES.WMADDH\n" \
"00:4016 SNES.JOYSER0\n" \
"00:4017 SNES.JOYSER1\n" \
"00:4200 SNES.NMITIMEN\n" \
"00:4201 SNES.WRIO\n" \
"00:4202 SNES.WRMPYA\n" \
"00:4203 SNES.WRMPYB\n" \
"00:4204 SNES.WRDIVL\n" \
"00:4205 SNES.WRDIVH\n" \
"00:4206 SNES.WRDIVB\n" \
"00:4207 SNES.HTIMEL\n" \
"00:4208 SNES.HTIMEH\n" \
"00:4209 SNES.VTIMEL\n" \
"00:420A SNES.VTIMEH\n" \
"00:420B SNES.MDMAEN\n" \
"00:420C SNES.HDMAEN\n" \
"00:420D SNES.MEMSEL\n" \
"00:4210 SNES.RDNMI\n" \
"00:4211 SNES.TIMEUP\n" \
"00:4212 SNES.HVBJOY\n" \
"00:4213 SNES.RDIO\n" \
"00:4214 SNES.RDDIVL\n" \
"00:4215 SNES.RDDIVH\n" \
"00:4216 SNES.RDMPYL\n" \
"00:4217 SNES.RDMPYH\n" \
"00:4218 SNES.JOY1L\n" \
"00:4219 SNES.JOY1H\n" \
"00:421A SNES.JOY2L\n" \
"00:421B SNES.JOY2H\n" \
"00:421C SNES.JOY3L\n" \
"00:421D SNES.JOY3H\n" \
"00:421E SNES.JOY4L\n" \
"00:421F SNES.JOY4H\n" \
"00:4300 SNES.DMAP0\n" \
"00:4301 SNES.BBAD0\n" \
"00:4302 SNES.A1T0L\n" \
"00:4303 SNES.A1T0H\n" \
"00:4304 SNES.A1B0\n" \
"00:4305 SNES.DAS0L\n" \
"00:4306 SNES.DAS0H\n" \
"00:4307 SNES.DASB0\n" \
"00:4308 SNES.A2A0L\n" \
"00:4309 SNES.A2A0H\n" \
"00:430A SNES.NTLR0\n" \
"00:4310 SNES.DMAP1\n" \
"00:4311 SNES.BBAD1\n" \
"00:4312 SNES.A1T1L\n" \
"00:4313 SNES.A1T1H\n" \
"00:4314 SNES.A1B1\n" \
"00:4315 SNES.DAS1L\n" \
"00:4316 SNES.DAS1H\n" \
"00:4317 SNES.DASB1\n" \
"00:4318 SNES.A2A1L\n" \
"00:4319 SNES.A2A1H\n" \
"00:431A SNES.NTLR1\n" \
"00:4320 SNES.DMAP2\n" \
"00:4321 SNES.BBAD2\n" \
"00:4322 SNES.A1T2L\n" \
"00:4323 SNES.A1T2H\n" \
"00:4324 SNES.A1B2\n" \
"00:4325 SNES.DAS2L\n" \
"00:4326 SNES.DAS2H\n" \
"00:4327 SNES.DASB2\n" \
"00:4328 SNES.A2A2L\n" \
"00:4329 SNES.A2A2H\n" \
"00:432A SNES.NTLR2\n" \
"00:4330 SNES.DMAP3\n" \
"00:4331 SNES.BBAD3\n" \
"00:4332 SNES.A1T3L\n" \
"00:4333 SNES.A1T3H\n" \
"00:4334 SNES.A1B3\n" \
"00:4335 SNES.DAS3L\n" \
"00:4336 SNES.DAS3H\n" \
"00:4337 SNES.DASB3\n" \
"00:4338 SNES.A2A3L\n" \
"00:4339 SNES.A2A3H\n" \
"00:433A SNES.NTLR3\n" \
"00:4340 SNES.DMAP4\n" \
"00:4341 SNES.BBAD4\n" \
"00:4342 SNES.A1T4L\n" \
"00:4343 SNES.A1T4H\n" \
"00:4344 SNES.A1B4\n" \
"00:4345 SNES.DAS4L\n" \
"00:4346 SNES.DAS4H\n" \
"00:4347 SNES.DASB4\n" \
"00:4348 SNES.A2A4L\n" \
"00:4349 SNES.A2A4H\n" \
"00:434A SNES.NTLR4\n" \
"00:4350 SNES.DMAP5\n" \
"00:4351 SNES.BBAD5\n" \
"00:4352 SNES.A1T5L\n" \
"00:4353 SNES.A1T5H\n" \
"00:4354 SNES.A1B5\n" \
"00:4355 SNES.DAS5L\n" \
"00:4356 SNES.DAS5H\n" \
"00:4357 SNES.DASB5\n" \
"00:4358 SNES.A2A5L\n" \
"00:4359 SNES.A2A5H\n" \
"00:435A SNES.NTLR5\n" \
"00:4360 SNES.DMAP6\n" \
"00:4361 SNES.BBAD6\n" \
"00:4362 SNES.A1T6L\n" \
"00:4363 SNES.A1T6H\n" \
"00:4364 SNES.A1B6\n" \
"00:4365 SNES.DAS6L\n" \
"00:4366 SNES.DAS6H\n" \
"00:4367 SNES.DASB6\n" \
"00:4368 SNES.A2A6L\n" \
"00:4369 SNES.A2A6H\n" \
"00:436A SNES.NTLR6\n" \
"00:4370 SNES.DMAP7\n" \
"00:4371 SNES.BBAD7\n" \
"00:4372 SNES.A1T7L\n" \
"00:4373 SNES.A1T7H\n" \
"00:4374 SNES.A1B7\n" \
"00:4375 SNES.DAS7L\n" \
"00:4376 SNES.DAS7H\n" \
"00:4377 SNES.DASB7\n" \
"00:4378 SNES.A2A7L\n" \
"00:4379 SNES.A2A7H\n" \
"00:437A SNES.NTLR7\n" \
"\n" \
"[definitions]\n" \
"00000000 test\n" \
"00000000 test\n" \
"00000000 test\n" \
;
@@ -0,0 +1,19 @@
const char *DEFAULT_SYMBOL_MAP_SMP =
"; default registers used for SMP\n" \
"\n" \
"[labels]\n" \
"00:00F0 SMP.UNKNOWN\n" \
"00:00F1 SMP.CONTROL\n" \
"00:00F2 SMP.DSP_ADDR\n" \
"00:00F3 SMP.DSP_DATA\n" \
"00:00F4 SMP.PORT0\n" \
"00:00F5 SMP.PORT1\n" \
"00:00F6 SMP.PORT2\n" \
"00:00F7 SMP.PORT3\n" \
"00:00FA SMP.TIMER0\n" \
"00:00FB SMP.TIMER1\n" \
"00:00FC SMP.TIMER2\n" \
"00:00FD SMP.COUNTER0\n" \
"00:00FE SMP.COUNTER1\n" \
"00:00FF SMP.COUNTER2\n" \
;
@@ -0,0 +1,103 @@
#include "symbolsview.moc"

// ------------------------------------------------------------------------
SymbolsView::SymbolsView(DisasmProcessor *processor) : processor(processor) {
setObjectName("symbols");
setWindowTitle("Symbols");
application.windowList.append(this);

layout = new QVBoxLayout;
layout->setMargin(Style::WindowMargin);
layout->setSpacing(Style::WidgetSpacing);
setLayout(layout);

QHBoxLayout *topLayout = new QHBoxLayout();
layout->addLayout(topLayout);

search = new QLineEdit();
topLayout->addWidget(search);

list = new QTreeWidget;
list->setColumnCount(3);
list->setHeaderLabels(QStringList() << "Address" << "Name" << "Description");
list->setColumnWidth(1, list->fontMetrics().width(" 123456789 "));
list->setAllColumnsShowFocus(true);
list->sortByColumn(0, Qt::AscendingOrder);
list->setRootIsDecorated(false);
list->setSelectionMode(QAbstractItemView::ExtendedSelection);
list->resizeColumnToContents(0);
layout->addWidget(list);

resize(400, 500);
synchronize();

connect(processor->getSymbols(), SIGNAL(updated()), this, SLOT(synchronize()));
connect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(bind(QTreeWidgetItem*, int)));
connect(search, SIGNAL(textChanged(const QString&)), this, SLOT(synchronize()));
}

// ------------------------------------------------------------------------
void SymbolsView::bind(QTreeWidgetItem *item, int value) {
if (value != 0) {
return;
}

uint32_t address = item->data(0, Qt::UserRole).toUInt();
bool enable = item->checkState(0);

int32_t breakpoint = breakpointEditor->indexOfBreakpointExec(address, processor->getBreakpointBusName());
if (!enable && breakpoint >= 0) {
breakpointEditor->removeBreakpoint(breakpoint);
} else if (enable) {
breakpointEditor->addBreakpoint(nall::hex(address), "x", processor->getBreakpointBusName());
}
}

// ------------------------------------------------------------------------
void SymbolsView::synchronize() {
QString filter = search->text();
SymbolMap *symbols = processor->getSymbols();

list->clear();
list->setSortingEnabled(false);

uint32_t count = symbols->symbols.size();
for (uint32_t i=0; i<count; i++) {
const Symbol &sym = symbols->symbols[i].getSymbol();
if (sym.isInvalid()) {
continue;
}

if (filter.length()) {
QStringList list = filter.split(" ");
QString search = QString((const char*)sym.name);
bool found = true;

for (QStringList::iterator it = list.begin(); it != list.end() && found; it++) {
if (!search.contains(*it, Qt::CaseInsensitive)) {
found = false;
}
}

if (!found) {
continue;
}
}

int32_t breakpoint = breakpointEditor->indexOfBreakpointExec(sym.address, processor->getBreakpointBusName());

auto item = new QTreeWidgetItem(list);
item->setData(0, Qt::UserRole, QVariant(sym.address));
item->setCheckState(0, breakpoint >= 0 ? Qt::Checked : Qt::Unchecked);
item->setText(0, hex<6, '0'>(sym.address));
item->setText(1, sym.name);
item->setText(2, "");
}

list->resizeColumnToContents(0);
list->resizeColumnToContents(1);
list->setSortingEnabled(true);

}

// ------------------------------------------------------------------------
@@ -0,0 +1,20 @@
class Symbols;

class SymbolsView : public Window {
Q_OBJECT

public:
SymbolsView(class DisasmProcessor *processor);

QVBoxLayout *layout;
QTreeWidget *list;

QLineEdit *search;

public slots:
void synchronize();
void bind(QTreeWidgetItem*, int);

private:
class DisasmProcessor *processor;
};
@@ -16,6 +16,7 @@ DisasmWidget::DisasmWidget() {
view->setFont(QFont(Style::Monospace));
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->setMinimumHeight((25 + 1) * view->fontMetrics().height());
view->setWordWrapMode(QTextOption::NoWrap);
layout->addWidget(view);
}

@@ -79,32 +80,87 @@ void Disassembler::refresh(Source source, unsigned addr) {
}
}
}

string output;
for(unsigned i = 0; i < 25; i++) {
if(i < 12) output << "<font color='#0000a0'>";
else if(i == 12) output << "<font color='#00a000'>";
else output << "<font color='#a00000'>";

if(line[i] == -1) {
output << "...";
} else {
char t[256];
if(source == CPU) { SNES::cpu.disassemble_opcode(t, line[i]); t[20] = 0; }
if(source == SMP) { SNES::smp.disassemble_opcode(t, line[i]); t[23] = 0; }
if(source == SA1) { SNES::sa1.disassemble_opcode(t, line[i]); t[20] = 0; }
if(source == SFX) { SNES::superfx.disassemble_opcode(t, line[i]); t[25] = 0; }
string text = rtrim(t);
text.replace(" ", "&nbsp;");
output << text;

string htmlOutput;
htmlOutput << "<table>";
// CodeLocation predictedCodeLoc;
// predictedCodeLoc.file = ~0;
// predictedCodeLoc.line = ~0;
string opCodeHtml;
for (unsigned i = 0; i < 25; i++) {

htmlOutput << "<tr><td width=\"250\">";

opCodeHtml = "";
if (i < 12)
opCodeHtml << "<font color='#0000a0'>";
else if (i == 12)
opCodeHtml << "<font color='#00a000'>";
else
opCodeHtml = "<font color='#a00000'>";

if (line[i] == -1)
{
opCodeHtml << "...";
}
else
{
char opCodeText[128];
if (source == CPU) { SNES::cpu.disassemble_opcode(opCodeText, line[i]); opCodeText[20] = 0; }
if (source == SMP) { SNES::smp.disassemble_opcode(opCodeText, line[i]); opCodeText[23] = 0; }
if (source == SA1) { SNES::sa1.disassemble_opcode(opCodeText, line[i]); opCodeText[20] = 0; }
if (source == SFX) { SNES::superfx.disassemble_opcode(opCodeText, line[i]); opCodeText[25] = 0; }
rtrim(opCodeText);
opCodeHtml << "<pre>" << opCodeText << "</pre>";
}
opCodeHtml << "</font>";

// if (auto optionalLoc = symbols->getAddressToLine(line[i]))
// {
// CodeLocation codeLoc = optionalLoc();

// // re-set the previous line location in case we changed files, or jumped around the same file
// if (predictedCodeLoc.file != codeLoc.file || (predictedCodeLoc.line < codeLoc.line - 5 || predictedCodeLoc.line > codeLoc.line))
// {
// predictedCodeLoc.file = codeLoc.file;
// predictedCodeLoc.line = codeLoc.line;

// auto file = symbols->getSourceFilename(codeLoc.file);
// char lineString[128];
// snprintf(lineString, 128, "</td><td>---------- %s", file ? (const char*)file() : "???");
// htmlOutput << lineString << "</td></tr><tr><td>";
// }

// // add source lines consecutively until we're caught up in case we skipped over a few
// while (predictedCodeLoc.line != codeLoc.line)
// {
// char opCodeSuffix[128];
// auto sourceLine = symbols->getSourceLine(predictedCodeLoc);
// snprintf(opCodeSuffix, 128, "%04d: %s", predictedCodeLoc.line, sourceLine ? (const char*)sourceLine() : "???");
// htmlOutput << "</td><td><pre>" << opCodeSuffix << "</pre></td></tr><tr><td>";
// predictedCodeLoc.line++;
// }

// // add the actual opcode line and its sourceline suffix
// htmlOutput << opCodeHtml << "</td><td>";

// char opCodeSuffix[128];
// auto sourceLine = symbols->getSourceLine(predictedCodeLoc);
// snprintf(opCodeSuffix, 128, "%04d: %s", predictedCodeLoc.line, sourceLine ? (const char*)sourceLine() : "???");
// htmlOutput << "<pre>" << opCodeSuffix << "</pre>";
// predictedCodeLoc.line++;
// }
// else
{
htmlOutput << opCodeHtml;
}
htmlOutput << "</td></tr>";

output << "</font>";
if(i != 24) output << "<br>";
}
htmlOutput << "</table>";

if(source == CPU) cpuDisassembler->view->setHtml(output);
if(source == SMP) smpDisassembler->view->setHtml(output);
if(source == SA1) sa1Disassembler->view->setHtml(output);
if(source == SFX) sfxDisassembler->view->setHtml(output);
if(source == CPU) cpuDisassembler->view->setHtml(htmlOutput);
if(source == SMP) smpDisassembler->view->setHtml(htmlOutput);
if(source == SA1) sa1Disassembler->view->setHtml(htmlOutput);
if(source == SFX) sfxDisassembler->view->setHtml(htmlOutput);
}
@@ -41,6 +41,7 @@ using namespace ruby;
#include "cartridge/cartridge.hpp"

#if defined(DEBUGGER)
#include "debugger/disassembler/symbols/symbol_map.moc.hpp"
#include "debugger/debugger.moc.hpp"
#include "debugger/tracer.moc.hpp"
#include "debugger/registeredit.moc.hpp"