421 lines
9.1 KiB
C++
421 lines
9.1 KiB
C++
// ld65dbg.cpp
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "types.h"
|
|
#include "ld65dbg.h"
|
|
#include "utils/StringUtils.h"
|
|
|
|
|
|
namespace ld65
|
|
{
|
|
//---------------------------------------------------------------------------------------------------
|
|
segment::segment( int id, const char *name, int startAddr, int size, int ofs, unsigned char type )
|
|
: _name(name ? name : ""), _id(id), _startAddr(startAddr), _size(size), _ofs(ofs), _type(type)
|
|
{
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
scope::scope(int id, const char *name, int size, int parentID)
|
|
: _name(name ? name : ""), _id(id), _parentID(parentID), _size(size), _parent(nullptr)
|
|
{
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
void scope::getFullName(std::string &out)
|
|
{
|
|
if ( _parent )
|
|
{
|
|
_parent->getFullName(out);
|
|
}
|
|
if (!_name.empty())
|
|
{
|
|
out.append(_name);
|
|
out.append("::");
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
sym::sym(int id, const char *name, int size, int value, int type)
|
|
: _name(name ? name : ""), _id(id), _size(size), _value(value), _type(type), _scope(nullptr), _segment(nullptr)
|
|
{
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
database::database(void)
|
|
{
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
database::~database(void)
|
|
{
|
|
for (auto itSym = symMap.begin(); itSym != symMap.end(); itSym++)
|
|
{
|
|
delete itSym->second;
|
|
}
|
|
for (auto itScope = scopeMap.begin(); itScope != scopeMap.end(); itScope++)
|
|
{
|
|
delete itScope->second;
|
|
}
|
|
for (auto itSeg = segmentMap.begin(); itSeg != segmentMap.end(); itSeg++)
|
|
{
|
|
delete itSeg->second;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
database::dbgLine::dbgLine(size_t bufferSize)
|
|
{
|
|
buf = NULL;
|
|
bufSize = 0;
|
|
readPtr = 0;
|
|
|
|
allocBuffer( bufferSize );
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
database::dbgLine::~dbgLine(void)
|
|
{
|
|
if (buf)
|
|
{
|
|
::free(buf); buf = NULL;
|
|
}
|
|
bufSize = 0;
|
|
readPtr = 0;
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
void database::dbgLine::allocBuffer(size_t bufferSize)
|
|
{
|
|
if (buf)
|
|
{
|
|
::free(buf); buf = NULL;
|
|
}
|
|
bufSize = 0;
|
|
readPtr = 0;
|
|
|
|
buf = static_cast<char*>( ::malloc( bufferSize ) );
|
|
|
|
if (buf == NULL)
|
|
{
|
|
bufSize = 0;
|
|
}
|
|
else
|
|
{
|
|
buf[0] = 0;
|
|
bufSize = bufferSize;
|
|
}
|
|
readPtr = 0;
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
const char *database::dbgLine::readFromFile( FILE *fp )
|
|
{
|
|
readPtr = 0;
|
|
|
|
return fgets(buf, bufSize, fp);
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
int database::dbgLine::readToken( char *tk, size_t tkSize )
|
|
{
|
|
int charsRead = 0;
|
|
size_t i,j;
|
|
|
|
i=readPtr; j=0;
|
|
if ( buf[i] != 0 )
|
|
{
|
|
while (isspace(buf[i])) i++;
|
|
|
|
if ( isalpha(buf[i]) || (buf[i] == '_') )
|
|
{
|
|
while ( isalnum(buf[i]) || (buf[i] == '_') )
|
|
{
|
|
if (j < tkSize)
|
|
{
|
|
tk[j] = buf[i]; j++;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
else if (buf[i] != 0)
|
|
{
|
|
if (j < tkSize)
|
|
{
|
|
tk[j] = buf[i]; j++;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
charsRead = j;
|
|
readPtr = i;
|
|
|
|
if (j < tkSize)
|
|
{
|
|
tk[j] = 0;
|
|
}
|
|
else
|
|
{
|
|
tk[tkSize-1] = 0;
|
|
}
|
|
return charsRead;
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
int database::dbgLine::readKeyValuePair( char *keyValueBuffer, size_t keyValueBufferSize )
|
|
{
|
|
int charsRead = 0;
|
|
size_t i,j;
|
|
bool isStringLiteral = false;
|
|
|
|
i=readPtr; j=0;
|
|
if ( buf[i] != 0 )
|
|
{
|
|
while (isspace(buf[i])) i++;
|
|
|
|
if ( isalpha(buf[i]) || (buf[i] == '_') )
|
|
{
|
|
while ( isalnum(buf[i]) || (buf[i] == '_') )
|
|
{
|
|
if (j < keyValueBufferSize)
|
|
{
|
|
keyValueBuffer[j] = buf[i]; j++;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
else if (buf[i] != 0)
|
|
{
|
|
if (j < keyValueBufferSize)
|
|
{
|
|
keyValueBuffer[j] = buf[i]; j++;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
while (isspace(buf[i])) i++;
|
|
}
|
|
|
|
if ( buf[i] == '=' )
|
|
{
|
|
if (j < keyValueBufferSize)
|
|
{
|
|
keyValueBuffer[j] = buf[i]; j++;
|
|
}
|
|
i++;
|
|
|
|
while (isspace(buf[i])) i++;
|
|
|
|
while ( buf[i] != 0 )
|
|
{
|
|
if ( !isStringLiteral && buf[i] == ',' )
|
|
{
|
|
break;
|
|
}
|
|
else if ( buf[i] == '\"' )
|
|
{
|
|
isStringLiteral = !isStringLiteral;
|
|
}
|
|
else
|
|
{
|
|
if (j < keyValueBufferSize)
|
|
{
|
|
if (!isspace(buf[i]))
|
|
{
|
|
keyValueBuffer[j] = buf[i]; j++;
|
|
}
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
if (buf[i] == ',')
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
charsRead = j;
|
|
readPtr = i;
|
|
|
|
if (j < keyValueBufferSize)
|
|
{
|
|
keyValueBuffer[j] = 0;
|
|
}
|
|
else
|
|
{
|
|
keyValueBuffer[keyValueBufferSize-1] = 0;
|
|
}
|
|
return charsRead;
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
int database::dbgLine::splitKeyValuePair( char *keyValueBuffer, char **keyPtr, char **valuePtr )
|
|
{
|
|
size_t i=0;
|
|
|
|
if (keyPtr != nullptr)
|
|
{
|
|
*keyPtr = keyValueBuffer;
|
|
}
|
|
while (keyValueBuffer[i] != 0)
|
|
{
|
|
if (keyValueBuffer[i] == '=')
|
|
{
|
|
keyValueBuffer[i] = 0; i++; break;
|
|
}
|
|
i++;
|
|
}
|
|
if (valuePtr != nullptr)
|
|
{
|
|
*valuePtr = &keyValueBuffer[i];
|
|
}
|
|
return 0;
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
int database::dbgFileLoad( const char *dbgFilePath )
|
|
{
|
|
static constexpr size_t lineSize = 4096;
|
|
FILE *fp;
|
|
dbgLine line( lineSize );
|
|
char lineType[64];
|
|
fceuScopedPtr <char> keyValueBuffer( new char[ lineSize ], FCEU_ALLOC_TYPE_NEW_ARRAY );
|
|
|
|
fp = ::fopen( dbgFilePath, "r");
|
|
|
|
if (fp == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
while ( line.readFromFile(fp) != NULL )
|
|
{
|
|
//printf("%s", line.getLine());
|
|
|
|
if ( line.readToken( lineType, sizeof(lineType) ) )
|
|
{
|
|
int id = -1, size = 0, startAddr = 0, ofs = -1, parentID = -1, scopeID = -1, segmentID = -1;
|
|
int value = 0;
|
|
unsigned char segType = segment::READ;
|
|
char name[256];
|
|
char type[32];
|
|
|
|
name[0] = 0;
|
|
type[0] = 0;
|
|
|
|
while ( line.readKeyValuePair( keyValueBuffer.get(), lineSize) )
|
|
{
|
|
char *key, *val;
|
|
|
|
line.splitKeyValuePair( keyValueBuffer.get(), &key, &val );
|
|
|
|
//printf(" Key '%s' -> Value '%s' \n", key, val );
|
|
|
|
if ( strcmp( key, "id") == 0 )
|
|
{
|
|
id = strtol( val, nullptr, 0 );
|
|
}
|
|
else if ( strcmp( key, "name") == 0 )
|
|
{
|
|
Strlcpy( name, val, sizeof(name));
|
|
}
|
|
else if ( strcmp( key, "size") == 0 )
|
|
{
|
|
size = strtol( val, nullptr, 0 );
|
|
}
|
|
else if ( strcmp( key, "val") == 0 )
|
|
{
|
|
value = strtol( val, nullptr, 0 );
|
|
}
|
|
else if ( strcmp( key, "scope") == 0 )
|
|
{
|
|
scopeID = strtol( val, nullptr, 0 );
|
|
}
|
|
else if ( strcmp( key, "parent") == 0 )
|
|
{
|
|
parentID = strtol( val, nullptr, 0 );
|
|
}
|
|
else if ( strcmp( key, "seg") == 0 )
|
|
{
|
|
segmentID = strtol( val, nullptr, 0 );
|
|
}
|
|
else if ( strcmp( key, "ooffs") == 0 )
|
|
{
|
|
ofs = strtol( val, nullptr, 0 );
|
|
}
|
|
else if ( strcmp( key, "type") == 0 )
|
|
{
|
|
Strlcpy( type, val, sizeof(type));
|
|
}
|
|
}
|
|
|
|
if ( strcmp( lineType, "seg" ) == 0 )
|
|
{
|
|
if ( id >= 0 )
|
|
{
|
|
segment *s = new segment( id, name, startAddr, size, ofs, segType );
|
|
|
|
segmentMap[id] = s;
|
|
}
|
|
}
|
|
else if ( strcmp( lineType, "scope" ) == 0 )
|
|
{
|
|
if ( id >= 0 )
|
|
{
|
|
scope *s = new scope( id, name, size, parentID );
|
|
|
|
scopeMap[id] = s;
|
|
|
|
auto it = scopeMap.find( parentID );
|
|
|
|
if ( it != scopeMap.end() )
|
|
{
|
|
//printf("Found Parent:%i for %i\n", parentID, id );
|
|
s->_parent = it->second;
|
|
}
|
|
}
|
|
}
|
|
else if ( strcmp( lineType, "sym") == 0 )
|
|
{
|
|
if ( id >= 0 )
|
|
{
|
|
int symType = sym::IMPORT;
|
|
|
|
if ( strcmp( type, "lab") == 0)
|
|
{
|
|
symType = sym::LABEL;
|
|
}
|
|
else if ( strcmp( type, "equ") == 0)
|
|
{
|
|
symType = sym::EQU;
|
|
}
|
|
|
|
sym *s = new sym( id, name, size, value, symType );
|
|
|
|
auto it = scopeMap.find( scopeID );
|
|
|
|
if ( it != scopeMap.end() )
|
|
{
|
|
//printf("Found Scope:%i for %s\n", scopeID, name );
|
|
s->_scope = it->second;
|
|
}
|
|
|
|
auto itSeg = segmentMap.find( segmentID );
|
|
|
|
if ( itSeg != segmentMap.end() )
|
|
{
|
|
//printf("Found Segment:%i for %s\n", segmentID, name );
|
|
s->_segment = itSeg->second;
|
|
}
|
|
symMap[id] = s;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
::fclose(fp);
|
|
|
|
return 0;
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
int database::iterateSymbols( void *userData, void (*cb)( void *userData, sym *s ) )
|
|
{
|
|
int numSyms = 0;
|
|
|
|
for (auto it = symMap.begin(); it != symMap.end(); it++)
|
|
{
|
|
cb( userData, it->second );
|
|
numSyms++;
|
|
}
|
|
return numSyms;
|
|
}
|
|
//---------------------------------------------------------------------------------------------------
|
|
}
|