|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 2005 by David Saxton *
|
|
|
|
* david@bluehaze.org *
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#ifndef NO_GPSIM
|
|
|
|
|
|
|
|
#include "asmparser.h"
|
|
|
|
#include "debugmanager.h"
|
|
|
|
#include "flowcodedocument.h"
|
|
|
|
#include "gpsimprocessor.h"
|
|
|
|
#include "language.h"
|
|
|
|
#include "languagemanager.h"
|
|
|
|
#include "microlibrary.h"
|
|
|
|
#include "processchain.h"
|
|
|
|
#include "simulator.h"
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kmessagebox.h>
|
|
|
|
#include <ktempfile.h>
|
|
|
|
#include <kstandarddirs.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqtextstream.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
|
|
|
|
#include "gpsim/cod.h"
|
|
|
|
#include "gpsim/interface.h"
|
|
|
|
#include "gpsim/gpsim_classes.h"
|
|
|
|
#include "gpsim/pic-processor.h"
|
|
|
|
#include "gpsim/registers.h"
|
|
|
|
#include "gpsim/14bit-registers.h"
|
|
|
|
#include "gpsim/symbol.h"
|
|
|
|
#include "gpsim/sim_context.h"
|
|
|
|
|
|
|
|
bool bDoneGpsimInit = false;
|
|
|
|
bool bUseGUI = true;
|
|
|
|
// extern "C" void initialize_gpsim();
|
|
|
|
// void initialize_gpsim(void);
|
|
|
|
extern void initialize_commands();
|
|
|
|
void initialize_ConsoleUI();
|
|
|
|
extern void initialize_readline();
|
|
|
|
extern void gui_main(void);
|
|
|
|
extern void cli_main();
|
|
|
|
void gpsim_version() {};
|
|
|
|
void quit_gui() {};
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN class GpsimProcessor
|
|
|
|
/**
|
|
|
|
Work around a bug in gpsim: the directory in a filename is recorded twice, e.g.
|
|
|
|
"/home/david/afile.asm" is recorded as "/home/david//home/david/afile.asm". This
|
|
|
|
function will remove the duplicated directory path (by searching for a "//").
|
|
|
|
*/
|
|
|
|
TQString sanitizeGpsimFile( TQString file )
|
|
|
|
{
|
|
|
|
int pos = file.tqfind("//");
|
|
|
|
if ( pos != -1 )
|
|
|
|
{
|
|
|
|
file.remove( 0, pos + 1 );
|
|
|
|
}
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GpsimProcessor::GpsimProcessor( TQString symbolFile, TQObject *tqparent )
|
|
|
|
: TQObject(tqparent),
|
|
|
|
m_symbolFile(symbolFile)
|
|
|
|
{
|
|
|
|
if (!bDoneGpsimInit)
|
|
|
|
{
|
|
|
|
#ifndef GPSIM_0_21_4
|
|
|
|
initialize_ConsoleUI();
|
|
|
|
#endif
|
|
|
|
initialize_gpsim_core();
|
|
|
|
initialization_is_complete();
|
|
|
|
|
|
|
|
bDoneGpsimInit = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_bCanExecuteNextCycle = true;
|
|
|
|
m_bIsRunning = false;
|
|
|
|
m_pPicProcessor = 0l;
|
|
|
|
m_codLoadtqStatus = CodUnknown;
|
|
|
|
m_pRegisterMemory = 0l;
|
|
|
|
m_debugMode = GpsimDebugger::AsmDebugger;
|
|
|
|
m_pDebugger[0] = m_pDebugger[1] = 0l;
|
|
|
|
|
|
|
|
Processor * tempProcessor = 0l;
|
|
|
|
const char * fileName = symbolFile.ascii();
|
|
|
|
|
|
|
|
#ifdef GPSIM_0_21_4
|
|
|
|
switch ( (cod_errors)load_symbol_file( &tempProcessor, fileName ) )
|
|
|
|
{
|
|
|
|
case COD_SUCCESS:
|
|
|
|
m_codLoadtqStatus = CodSuccess;
|
|
|
|
break;
|
|
|
|
case COD_FILE_NOT_FOUND:
|
|
|
|
m_codLoadtqStatus = CodFileNotFound;
|
|
|
|
break;
|
|
|
|
case COD_UNRECOGNIZED_PROCESSOR:
|
|
|
|
m_codLoadtqStatus = CodUnrecognizedProcessor;
|
|
|
|
break;
|
|
|
|
case COD_FILE_NAME_TOO_LONG:
|
|
|
|
m_codLoadtqStatus = CodFileNameTooLong;
|
|
|
|
break;
|
|
|
|
case COD_LST_NOT_FOUND:
|
|
|
|
m_codLoadtqStatus = CodLstNotFound;
|
|
|
|
break;
|
|
|
|
case COD_BAD_FILE:
|
|
|
|
m_codLoadtqStatus = CodBadFile;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
m_codLoadtqStatus = CodUnknown;
|
|
|
|
}
|
|
|
|
#else // GPSIM_0_21_11+
|
|
|
|
FILE * pFile = fopen( fileName, "r" );
|
|
|
|
if ( !pFile )
|
|
|
|
m_codLoadtqStatus = CodFileUnreadable;
|
|
|
|
else
|
|
|
|
m_codLoadtqStatus = ( ProgramFileTypeList::GetList().LoadProgramFile( & tempProcessor, fileName, pFile ) ) ? CodSuccess : CodFailure;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
m_pPicProcessor = dynamic_cast<pic_processor*>(tempProcessor);
|
|
|
|
|
|
|
|
if ( codLoadtqStatus() == CodSuccess )
|
|
|
|
{
|
|
|
|
m_pRegisterMemory = new RegisterSet( m_pPicProcessor );
|
|
|
|
m_pDebugger[0] = new GpsimDebugger( GpsimDebugger::AsmDebugger, this );
|
|
|
|
m_pDebugger[1] = new GpsimDebugger( GpsimDebugger::HLLDebugger, this );
|
|
|
|
Simulator::self()->attachGpsimProcessor(this);
|
|
|
|
DebugManager::self()->registerGpsim(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GpsimProcessor::~GpsimProcessor()
|
|
|
|
{
|
|
|
|
Simulator::self()->detachGpsimProcessor(this);
|
|
|
|
delete m_pRegisterMemory;
|
|
|
|
|
|
|
|
if ( m_pDebugger[0] )
|
|
|
|
m_pDebugger[0]->deleteLater();
|
|
|
|
if ( m_pDebugger[1] )
|
|
|
|
m_pDebugger[1]->deleteLater();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimProcessor::displayCodLoadtqStatus( )
|
|
|
|
{
|
|
|
|
switch (m_codLoadtqStatus)
|
|
|
|
{
|
|
|
|
case CodSuccess:
|
|
|
|
break;
|
|
|
|
case CodFileNotFound:
|
|
|
|
KMessageBox::sorry( 0l, i18n("The cod file \"%1\" was not found.").tqarg(m_symbolFile), i18n("File Not Found") );
|
|
|
|
break;
|
|
|
|
case CodUnrecognizedProcessor:
|
|
|
|
KMessageBox::sorry( 0l, i18n("The processor for cod file \"%1\" is unrecognized.").tqarg(m_symbolFile), i18n("Unrecognized Processor") );
|
|
|
|
break;
|
|
|
|
case CodFileNameTooLong:
|
|
|
|
KMessageBox::sorry( 0l, i18n("The file name \"%1\" is too long.").tqarg(m_symbolFile), i18n("Filename Too Long") );
|
|
|
|
break;
|
|
|
|
case CodLstNotFound:
|
|
|
|
KMessageBox::sorry( 0l, i18n("The lst file associated with the cod file \"%1\" was not found.").tqarg(m_symbolFile), i18n("LST File Not Found") );
|
|
|
|
break;
|
|
|
|
case CodBadFile:
|
|
|
|
KMessageBox::sorry( 0l, i18n("The cod file \"%1\" is bad.").tqarg(m_symbolFile), i18n("Bad File") );
|
|
|
|
break;
|
|
|
|
case CodFileUnreadable:
|
|
|
|
KMessageBox::sorry( 0l, i18n("The cod file \"%1\" could not be read from.").tqarg(m_symbolFile), i18n("Unreadable File") );
|
|
|
|
break;
|
|
|
|
case CodFailure:
|
|
|
|
case CodUnknown:
|
|
|
|
KMessageBox::sorry( 0l, i18n("An error occured with the cod file \"%1\".").tqarg(m_symbolFile), i18n("Error") );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned GpsimProcessor::programMemorySize() const
|
|
|
|
{
|
|
|
|
return m_pPicProcessor->program_memory_size();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQStringList GpsimProcessor::sourceFileList()
|
|
|
|
{
|
|
|
|
TQStringList files;
|
|
|
|
|
|
|
|
// Work around nasty bug in gpsim 0.21.4 where nsrc_files value might be used uninitiazed
|
|
|
|
int max = m_pPicProcessor->files.nsrc_files();
|
|
|
|
#ifdef GPSIM_0_21_4
|
|
|
|
if ( max > 10 )
|
|
|
|
max = 10;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for ( int i = 0; i < max; ++i )
|
|
|
|
{
|
|
|
|
if ( !m_pPicProcessor->files[i] )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
files << sanitizeGpsimFile( m_pPicProcessor->files[i]->name().c_str() );
|
|
|
|
}
|
|
|
|
|
|
|
|
return files;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimProcessor::emitLineReached()
|
|
|
|
{
|
|
|
|
m_pDebugger[0]->emitLineReached();
|
|
|
|
m_pDebugger[1]->emitLineReached();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimProcessor::setRunning( bool run )
|
|
|
|
{
|
|
|
|
if ( m_bIsRunning == run )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_bIsRunning = run;
|
|
|
|
emit runningStatusChanged(run);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimProcessor::executeNext()
|
|
|
|
{
|
|
|
|
if ( !m_bIsRunning )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( !m_bCanExecuteNextCycle )
|
|
|
|
{
|
|
|
|
m_bCanExecuteNextCycle = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long long beforeExecuteCount = get_cycles().get();
|
|
|
|
|
|
|
|
m_pPicProcessor->step_one(false); // Don't know what the false is for; gpsim ignores its value anyway
|
|
|
|
|
|
|
|
// Some instructions take more than one cycle to execute, so ignore next cycle if this was the case
|
|
|
|
if ( (get_cycles().get() - beforeExecuteCount) > 1 )
|
|
|
|
m_bCanExecuteNextCycle = false;
|
|
|
|
|
|
|
|
currentDebugger()->checkForBreak();
|
|
|
|
|
|
|
|
// Let's also update the values of RegisterInfo every 50 milliseconds
|
|
|
|
if ( (beforeExecuteCount % 20000) == 0 )
|
|
|
|
registerMemory()->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimProcessor::reset()
|
|
|
|
{
|
|
|
|
bool wasRunning = isRunning();
|
|
|
|
m_pPicProcessor->reset(SIM_RESET);
|
|
|
|
setRunning(false);
|
|
|
|
if (!wasRunning)
|
|
|
|
{
|
|
|
|
// If we weren't running before, then the next signal won't have been emitted
|
|
|
|
emitLineReached();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MicroInfo * GpsimProcessor::microInfo( ) const
|
|
|
|
{
|
|
|
|
if ( !m_pPicProcessor )
|
|
|
|
return 0l;
|
|
|
|
|
|
|
|
return MicroLibrary::self()->microInfoWithID( m_pPicProcessor->name().c_str() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int GpsimProcessor::operandRegister( unsigned address )
|
|
|
|
{
|
|
|
|
instruction * ins = m_pPicProcessor->program_memory[ address ];
|
|
|
|
if ( Register_op * reg = dynamic_cast<Register_op*>(ins) )
|
|
|
|
return reg->register_address;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int GpsimProcessor::operandLiteral( unsigned address )
|
|
|
|
{
|
|
|
|
instruction * ins = m_pPicProcessor->program_memory[ address ];
|
|
|
|
if ( Literal_op * lit = dynamic_cast<Literal_op*>(ins) )
|
|
|
|
return lit->L;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GpsimProcessor::ProgramFileValidity GpsimProcessor::isValidProgramFile( const TQString & programFile )
|
|
|
|
{
|
|
|
|
if ( !KStandardDirs::exists(programFile) )
|
|
|
|
return DoesntExist;
|
|
|
|
|
|
|
|
TQString extension = programFile.right( programFile.length() - programFile.tqfindRev('.') - 1 ).lower();
|
|
|
|
|
|
|
|
if ( extension == "flowcode" ||
|
|
|
|
extension == "asm" ||
|
|
|
|
extension == "cod" ||
|
|
|
|
extension == "basic" || extension == "microbe" ||
|
|
|
|
extension == "c" )
|
|
|
|
return Valid;
|
|
|
|
|
|
|
|
if ( extension == "hex" && TQFile::exists( TQString(programFile).tqreplace(".hex",".cod") ) )
|
|
|
|
return Valid;
|
|
|
|
|
|
|
|
return IncorrectType;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString GpsimProcessor::generateSymbolFile( const TQString &fileName, TQObject *receiver, const char *successMember, const char * failMember )
|
|
|
|
{
|
|
|
|
if ( !isValidProgramFile(fileName) )
|
|
|
|
return TQString();
|
|
|
|
|
|
|
|
TQString extension = fileName.right( fileName.length() - fileName.tqfindRev('.') - 1 ).lower();
|
|
|
|
|
|
|
|
if ( extension == "cod" )
|
|
|
|
{
|
|
|
|
TQTimer::singleShot( 0, receiver, successMember );
|
|
|
|
return fileName;
|
|
|
|
}
|
|
|
|
if ( extension == "hex" )
|
|
|
|
{
|
|
|
|
TQTimer::singleShot( 0, receiver, successMember );
|
|
|
|
// We've already checked for the existance of the ".cod" file in GpsimProcessor::isValidProgramFile
|
|
|
|
return TQString(fileName).tqreplace(".hex",".cod");
|
|
|
|
}
|
|
|
|
|
|
|
|
else if ( extension == "basic" || extension == "microbe" )
|
|
|
|
{
|
|
|
|
compileMicrobe( fileName, receiver, successMember, failMember );
|
|
|
|
return TQString(fileName).tqreplace( "."+extension, ".cod" );
|
|
|
|
}
|
|
|
|
else if ( extension == "flowcode" )
|
|
|
|
{
|
|
|
|
const TQString hexFile = KTempFile( TQString(), ".hex" ).name();
|
|
|
|
|
|
|
|
ProcessOptions o;
|
|
|
|
o.b_addToProject = false;
|
|
|
|
o.setTargetFile( hexFile );
|
|
|
|
o.setInputFiles( fileName );
|
|
|
|
o.setMethod( ProcessOptions::Method::Forget );
|
|
|
|
o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_Program );
|
|
|
|
|
|
|
|
ProcessChain * pc = LanguageManager::self()->compile(o);
|
|
|
|
if (receiver)
|
|
|
|
{
|
|
|
|
if (successMember)
|
|
|
|
connect( pc, TQT_SIGNAL(successful()), receiver, successMember );
|
|
|
|
if (failMember)
|
|
|
|
connect( pc, TQT_SIGNAL(failed()), receiver, failMember );
|
|
|
|
}
|
|
|
|
|
|
|
|
return TQString(hexFile).tqreplace( ".hex", ".cod" );
|
|
|
|
}
|
|
|
|
else if ( extension == "asm" )
|
|
|
|
{
|
|
|
|
ProcessOptions o;
|
|
|
|
o.b_addToProject = false;
|
|
|
|
o.setTargetFile( TQString(fileName).tqreplace(".asm",".hex"));
|
|
|
|
o.setInputFiles(fileName);
|
|
|
|
o.setMethod( ProcessOptions::Method::Forget );
|
|
|
|
o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(fileName), ProcessOptions::ProcessPath::Program ) );
|
|
|
|
|
|
|
|
ProcessChain *pc = LanguageManager::self()->compile(o);
|
|
|
|
if (receiver)
|
|
|
|
{
|
|
|
|
if (successMember)
|
|
|
|
connect( pc, TQT_SIGNAL(successful()), receiver, successMember );
|
|
|
|
if (failMember)
|
|
|
|
connect( pc, TQT_SIGNAL(failed()), receiver, failMember );
|
|
|
|
}
|
|
|
|
|
|
|
|
return TQString(fileName).tqreplace(".asm",".cod");
|
|
|
|
}
|
|
|
|
else if ( extension == "c" )
|
|
|
|
{
|
|
|
|
ProcessOptions o;
|
|
|
|
o.b_addToProject = false;
|
|
|
|
o.setTargetFile( TQString(fileName).tqreplace(".c",".hex"));
|
|
|
|
o.setInputFiles(fileName);
|
|
|
|
o.setMethod( ProcessOptions::Method::Forget );
|
|
|
|
o.setProcessPath( ProcessOptions::ProcessPath::C_Program );
|
|
|
|
|
|
|
|
ProcessChain *pc = LanguageManager::self()->compile(o);
|
|
|
|
if (receiver)
|
|
|
|
{
|
|
|
|
if (successMember)
|
|
|
|
connect( pc, TQT_SIGNAL(successful()), receiver, successMember );
|
|
|
|
if (failMember)
|
|
|
|
connect( pc, TQT_SIGNAL(failed()), receiver, failMember );
|
|
|
|
}
|
|
|
|
|
|
|
|
return TQString(fileName).tqreplace(".c",".cod");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( failMember )
|
|
|
|
TQTimer::singleShot( 0, receiver, failMember );
|
|
|
|
return TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimProcessor::compileMicrobe( const TQString &filename, TQObject *receiver, const char * successMember, const char * failMember )
|
|
|
|
{
|
|
|
|
ProcessOptions o;
|
|
|
|
o.b_addToProject = false;
|
|
|
|
o.setTargetFile( TQString(filename).tqreplace(".microbe",".hex") );
|
|
|
|
o.setInputFiles(filename);
|
|
|
|
o.setMethod( ProcessOptions::Method::Forget );
|
|
|
|
o.setProcessPath( ProcessOptions::ProcessPath::Microbe_Program );
|
|
|
|
ProcessChain * pc = LanguageManager::self()->compile(o);
|
|
|
|
if (receiver)
|
|
|
|
{
|
|
|
|
if (successMember)
|
|
|
|
connect( pc, TQT_SIGNAL(successful()), receiver, successMember );
|
|
|
|
if (failMember)
|
|
|
|
connect( pc, TQT_SIGNAL(failed()), receiver, failMember );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//END class GpsimProcessor
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN class GpsimDebugger
|
|
|
|
GpsimDebugger::GpsimDebugger( Type type, GpsimProcessor * gpsim )
|
|
|
|
: TQObject()
|
|
|
|
{
|
|
|
|
m_pGpsim = gpsim;
|
|
|
|
m_type = type;
|
|
|
|
m_pBreakFromOldLine = 0l;
|
|
|
|
m_addressToLineMap = 0l;
|
|
|
|
m_stackLevelLowerBreak = -1;
|
|
|
|
m_addressSize = 0;
|
|
|
|
|
|
|
|
connect( m_pGpsim, TQT_SIGNAL(runningStatusChanged(bool )), this, TQT_SLOT(gpsimRunningStatusChanged(bool )) );
|
|
|
|
|
|
|
|
if ( type == HLLDebugger )
|
|
|
|
{
|
|
|
|
const TQStringList sourceFileList = m_pGpsim->sourceFileList();
|
|
|
|
TQStringList::const_iterator sflEnd = sourceFileList.end();
|
|
|
|
for ( TQStringList::const_iterator it = sourceFileList.begin(); it != sflEnd; ++it )
|
|
|
|
{
|
|
|
|
AsmParser p(*it);
|
|
|
|
p.parse(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
initAddressToLineMap();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GpsimDebugger::~GpsimDebugger()
|
|
|
|
{
|
|
|
|
TQValueList<DebugLine*> debugLinesToDelete;
|
|
|
|
|
|
|
|
for ( unsigned i = 0; i < m_addressSize; ++i )
|
|
|
|
{
|
|
|
|
DebugLine * dl = m_addressToLineMap[i];
|
|
|
|
if ( !dl || dl->markedAsDeleted() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dl->markAsDeleted();
|
|
|
|
debugLinesToDelete += dl;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQValueList<DebugLine*>::iterator end = debugLinesToDelete.end();
|
|
|
|
for ( TQValueList<DebugLine*>::iterator it = debugLinesToDelete.begin(); it != end; ++it )
|
|
|
|
delete *it;
|
|
|
|
|
|
|
|
delete [] m_addressToLineMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimDebugger::gpsimRunningStatusChanged( bool isRunning )
|
|
|
|
{
|
|
|
|
if (!isRunning)
|
|
|
|
{
|
|
|
|
m_stackLevelLowerBreak = -1;
|
|
|
|
m_pBreakFromOldLine = 0l;
|
|
|
|
emitLineReached();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimDebugger::associateLine( const TQString & sourceFile, int sourceLine, const TQString & assemblyFile, int assemblyLine )
|
|
|
|
{
|
|
|
|
if ( assemblyLine < 0 || sourceLine < 0 )
|
|
|
|
{
|
|
|
|
kdWarning() << k_funcinfo << "Invalid lines: assemblyLine="<<assemblyLine<<" sourceLine="<<sourceLine<<endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceLine hllSource = SourceLine( sourceFile, sourceLine );
|
|
|
|
SourceLine asmSource = SourceLine( assemblyFile, assemblyLine );
|
|
|
|
|
|
|
|
if ( m_sourceLineMap.tqcontains(asmSource) )
|
|
|
|
{
|
|
|
|
kdWarning() << k_funcinfo << "Already have an association for assembly (\""<<assemblyFile<<"\","<<assemblyLine<<")"<<endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_sourceLineMap[asmSource] = hllSource;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimDebugger::initAddressToLineMap()
|
|
|
|
{
|
|
|
|
m_addressSize = m_pGpsim->programMemorySize();
|
|
|
|
|
|
|
|
delete [] m_addressToLineMap;
|
|
|
|
m_addressToLineMap = new DebugLine*[m_addressSize];
|
|
|
|
memset( m_addressToLineMap, 0, m_addressSize * sizeof(DebugLine*) );
|
|
|
|
|
|
|
|
if ( m_type == AsmDebugger )
|
|
|
|
{
|
|
|
|
for ( unsigned i = 0; i < m_addressSize; ++i )
|
|
|
|
{
|
|
|
|
int line = m_pGpsim->picProcessor()->pma->get_src_line(i) - 1;
|
|
|
|
int fileID = m_pGpsim->picProcessor()->pma->get_file_id(i);
|
|
|
|
FileContext * fileContext = m_pGpsim->picProcessor()->files[fileID];
|
|
|
|
|
|
|
|
if (fileContext)
|
|
|
|
m_addressToLineMap[i] = new DebugLine( sanitizeGpsimFile( fileContext->name().c_str() ), line );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SourceLineMap::const_iterator slmEnd = m_sourceLineMap.end();
|
|
|
|
for ( SourceLineMap::const_iterator it = m_sourceLineMap.begin(); it != slmEnd; ++it )
|
|
|
|
{
|
|
|
|
SourceLineMap::const_iterator next = it;
|
|
|
|
++next;
|
|
|
|
|
|
|
|
int asmToLine = ((next == slmEnd) || (next.key().fileName() != it.key().fileName())) ? -1 : next.key().line() - 1;
|
|
|
|
|
|
|
|
TQString asmFile = it.key().fileName();
|
|
|
|
int asmFromLine = it.key().line();
|
|
|
|
SourceLine sourceLine = it.data();
|
|
|
|
|
|
|
|
|
|
|
|
std::string stdAsmFile( asmFile.ascii() );
|
|
|
|
int fileID = m_pGpsim->picProcessor()->files.Find( stdAsmFile );
|
|
|
|
if ( fileID == -1 )
|
|
|
|
{
|
|
|
|
kdWarning() << k_funcinfo << "Could not find FileContext (asmFile=\""<<asmFile<<"\")"<<endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( asmToLine == -1 )
|
|
|
|
asmToLine = m_pGpsim->picProcessor()->files[fileID]->max_line() - 2;
|
|
|
|
|
|
|
|
if ( (asmFromLine < 0) || (asmToLine < asmFromLine) )
|
|
|
|
{
|
|
|
|
kdWarning() << k_funcinfo << "Invalid lines: asmFromLine="<<asmFromLine<<" asmToLine="<<asmToLine<<endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
DebugLine * debugLine = new DebugLine( sourceLine.fileName(), sourceLine.line() );
|
|
|
|
bool used = false;
|
|
|
|
|
|
|
|
for ( int i = asmFromLine; i <= asmToLine; ++i )
|
|
|
|
{
|
|
|
|
#ifdef GPSIM_0_21_4
|
|
|
|
int address = m_pGpsim->picProcessor()->pma->find_address_from_line( fileID, i+1 );
|
|
|
|
#else // GPSIM_0_21_11
|
|
|
|
int address = m_pGpsim->picProcessor()->pma->find_address_from_line( m_pGpsim->picProcessor()->files[fileID], i+1 );
|
|
|
|
#endif
|
|
|
|
if ( address != -1 )
|
|
|
|
{
|
|
|
|
used = true;
|
|
|
|
m_addressToLineMap[address] = debugLine;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!used)
|
|
|
|
delete debugLine;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimDebugger::setBreakpoints( const TQString & path, const IntList & lines )
|
|
|
|
{
|
|
|
|
for ( unsigned i = 0; i < m_addressSize; i++ )
|
|
|
|
{
|
|
|
|
DebugLine * dl = m_addressToLineMap[i];
|
|
|
|
if ( !dl || dl->fileName() != path )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dl->setBreakpoint( lines.tqcontains( dl->line() ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimDebugger::setBreakpoint( const TQString & path, int line, bool isBreakpoint )
|
|
|
|
{
|
|
|
|
for ( unsigned i = 0; i < m_addressSize; i++ )
|
|
|
|
{
|
|
|
|
if ( !m_addressToLineMap[i] )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ( (m_addressToLineMap[i]->fileName() == path) &&
|
|
|
|
( line == m_addressToLineMap[i]->line() ) )
|
|
|
|
m_addressToLineMap[i]->setBreakpoint(isBreakpoint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DebugLine * GpsimDebugger::currentDebugLine()
|
|
|
|
{
|
|
|
|
return m_addressToLineMap[ m_pGpsim->picProcessor()->pc->get_value() ];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SourceLine GpsimDebugger::currentLine()
|
|
|
|
{
|
|
|
|
DebugLine * dl = currentDebugLine();
|
|
|
|
return dl ? *dl : SourceLine();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimDebugger::emitLineReached()
|
|
|
|
{
|
|
|
|
SourceLine currentAt = currentLine();
|
|
|
|
|
|
|
|
if ( currentAt == m_previousAtLineEmit )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_previousAtLineEmit = currentAt;
|
|
|
|
m_pGpsim->registerMemory()->update();
|
|
|
|
emit lineReached(currentAt);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimDebugger::checkForBreak()
|
|
|
|
{
|
|
|
|
DebugLine * currentLine = m_addressToLineMap[ m_pGpsim->picProcessor()->pc->get_value() ];
|
|
|
|
int currentStackLevel = int( m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask );
|
|
|
|
|
|
|
|
bool ontoNextLine = m_pBreakFromOldLine != currentLine;
|
|
|
|
bool lineBreakpoint = currentLine ? currentLine->isBreakpoint() : false;
|
|
|
|
bool stackBreakpoint = m_stackLevelLowerBreak >= currentStackLevel;
|
|
|
|
|
|
|
|
if ( ontoNextLine && (lineBreakpoint || stackBreakpoint) )
|
|
|
|
m_pGpsim->setRunning(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int GpsimDebugger::programAddress( const TQString & path, int line )
|
|
|
|
{
|
|
|
|
for ( unsigned i = 0; i < m_addressSize; ++i )
|
|
|
|
{
|
|
|
|
DebugLine * dl = m_addressToLineMap[i];
|
|
|
|
if ( !dl || (dl->line() != line) || (dl->fileName() != path) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GpsimDebugger::stepInto()
|
|
|
|
{
|
|
|
|
// I'm not aware of the stack being able to increase in size by more than
|
|
|
|
// one at a time, so "1" should suffice here...but to be on the safe side,
|
|
|
|
// make it a nice large number
|
|
|
|
stackStep( 1 << 16 );
|
|
|
|
}
|
|
|
|
void GpsimDebugger::stepOver()
|
|
|
|
{
|
|
|
|
stackStep(0);
|
|
|
|
}
|
|
|
|
void GpsimDebugger::stepOut()
|
|
|
|
{
|
|
|
|
stackStep(-1);
|
|
|
|
}
|
|
|
|
void GpsimDebugger::stackStep( int dl )
|
|
|
|
{
|
|
|
|
if ( m_pGpsim->isRunning() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
int initialStack = (m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask) + dl;
|
|
|
|
DebugLine * initialLine = currentDebugLine();
|
|
|
|
|
|
|
|
if ( initialStack < 0 )
|
|
|
|
initialStack = 0;
|
|
|
|
|
|
|
|
// Reset any previous stackStep, and step
|
|
|
|
m_pBreakFromOldLine = 0l;
|
|
|
|
m_stackLevelLowerBreak = -1;
|
|
|
|
m_pGpsim->picProcessor()->step_one(false);
|
|
|
|
|
|
|
|
int currentStack = m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask;
|
|
|
|
DebugLine * currentLine = currentDebugLine();
|
|
|
|
|
|
|
|
if ( (initialStack >= currentStack) && (initialLine != currentLine) )
|
|
|
|
emitLineReached();
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Looks like we stepped into something or haven't gone onto the next
|
|
|
|
// instruction, wait until we step back out....
|
|
|
|
m_stackLevelLowerBreak = initialStack;
|
|
|
|
m_pBreakFromOldLine = initialLine;
|
|
|
|
m_pGpsim->setRunning(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//END class Debugger
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN class RegisterSet
|
|
|
|
RegisterSet::RegisterSet( pic_processor * picProcessor )
|
|
|
|
{
|
|
|
|
unsigned numRegisters = picProcessor->rma.get_size();
|
|
|
|
m_registers.resize( numRegisters, 0l );
|
|
|
|
for ( unsigned i = 0; i < numRegisters; ++i )
|
|
|
|
{
|
|
|
|
RegisterInfo * info = new RegisterInfo( & picProcessor->rma[i] );
|
|
|
|
m_registers[i] = info;
|
|
|
|
m_nameToRegisterMap[ info->name() ] = info;
|
|
|
|
}
|
|
|
|
|
|
|
|
RegisterInfo * info = new RegisterInfo( picProcessor->W );
|
|
|
|
m_registers.append( info );
|
|
|
|
m_nameToRegisterMap[ info->name() ] = info;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RegisterSet::~RegisterSet()
|
|
|
|
{
|
|
|
|
for ( unsigned i = 0; i < m_registers.size(); ++i )
|
|
|
|
delete m_registers[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RegisterInfo * RegisterSet::fromAddress( unsigned address )
|
|
|
|
{
|
|
|
|
return (address < m_registers.size()) ? m_registers[address] : 0l;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RegisterInfo * RegisterSet::fromName( const TQString & name )
|
|
|
|
{
|
|
|
|
// First try the name as case sensitive, then as case insensitive.
|
|
|
|
if ( m_nameToRegisterMap.tqcontains( name ) )
|
|
|
|
return m_nameToRegisterMap[ name ];
|
|
|
|
|
|
|
|
TQString nameLower = name.lower();
|
|
|
|
|
|
|
|
RegisterInfoMap::iterator end = m_nameToRegisterMap.end();
|
|
|
|
for ( RegisterInfoMap::iterator it = m_nameToRegisterMap.begin(); it != end; ++ it )
|
|
|
|
{
|
|
|
|
if ( it.key().lower() == nameLower )
|
|
|
|
return it.data();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0l;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegisterSet::update()
|
|
|
|
{
|
|
|
|
for ( unsigned i = 0; i < m_registers.size(); ++i )
|
|
|
|
m_registers[i]->update();
|
|
|
|
}
|
|
|
|
//END class RegisterSet
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN class RegisterInfo
|
|
|
|
RegisterInfo::RegisterInfo( Register * reg )
|
|
|
|
{
|
|
|
|
assert(reg);
|
|
|
|
m_pRegister = reg;
|
|
|
|
m_type = Invalid;
|
|
|
|
m_prevEmitValue = 0;
|
|
|
|
|
|
|
|
switch ( m_pRegister->isa() )
|
|
|
|
{
|
|
|
|
case Register::GENERIC_REGISTER:
|
|
|
|
m_type = Generic;
|
|
|
|
break;
|
|
|
|
case Register::FILE_REGISTER:
|
|
|
|
m_type = File;
|
|
|
|
break;
|
|
|
|
case Register::SFR_REGISTER:
|
|
|
|
m_type = SFR;
|
|
|
|
break;
|
|
|
|
case Register::BP_REGISTER:
|
|
|
|
m_type = Breakpoint;
|
|
|
|
break;
|
|
|
|
case Register::INVALID_REGISTER:
|
|
|
|
m_type = Invalid;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_name = m_pRegister->baseName();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned RegisterInfo::value() const
|
|
|
|
{
|
|
|
|
return m_pRegister->value.data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegisterInfo::update()
|
|
|
|
{
|
|
|
|
unsigned newValue = value();
|
|
|
|
if ( newValue != m_prevEmitValue )
|
|
|
|
{
|
|
|
|
m_prevEmitValue = newValue;
|
|
|
|
emit valueChanged(newValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString RegisterInfo::toString( RegisterType type )
|
|
|
|
{
|
|
|
|
switch ( type )
|
|
|
|
{
|
|
|
|
case Generic:
|
|
|
|
return i18n("Generic");
|
|
|
|
|
|
|
|
case File:
|
|
|
|
return i18n("File");
|
|
|
|
|
|
|
|
case SFR:
|
|
|
|
return i18n("SFR");
|
|
|
|
|
|
|
|
case Breakpoint:
|
|
|
|
return i18n("Breakpoint");
|
|
|
|
|
|
|
|
case Invalid:
|
|
|
|
return i18n("Invalid");
|
|
|
|
}
|
|
|
|
|
|
|
|
return i18n("Unknown");
|
|
|
|
}
|
|
|
|
//END class RegisterInfo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//BEGIN class DebugLine
|
|
|
|
DebugLine::DebugLine( const TQString & fileName, int line )
|
|
|
|
: SourceLine( fileName, line )
|
|
|
|
{
|
|
|
|
m_bIsBreakpoint = false;
|
|
|
|
m_bMarkedAsDeleted = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DebugLine::DebugLine()
|
|
|
|
: SourceLine()
|
|
|
|
{
|
|
|
|
m_bIsBreakpoint = false;
|
|
|
|
m_bMarkedAsDeleted = false;
|
|
|
|
}
|
|
|
|
//END class DebugLine
|
|
|
|
|
|
|
|
|
|
|
|
#include "gpsimprocessor.moc"
|
|
|
|
|
|
|
|
#endif
|