/*************************************************************************** * 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 #include #include #include #include #include #include #include #include #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.find("//"); if ( pos != -1 ) { file.remove( 0, pos + 1 ); } return file; } GpsimProcessor::GpsimProcessor( TQString symbolFile, TQObject *parent ) : TQObject(parent), 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_codLoadStatus = 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_codLoadStatus = CodSuccess; break; case COD_FILE_NOT_FOUND: m_codLoadStatus = CodFileNotFound; break; case COD_UNRECOGNIZED_PROCESSOR: m_codLoadStatus = CodUnrecognizedProcessor; break; case COD_FILE_NAME_TOO_LONG: m_codLoadStatus = CodFileNameTooLong; break; case COD_LST_NOT_FOUND: m_codLoadStatus = CodLstNotFound; break; case COD_BAD_FILE: m_codLoadStatus = CodBadFile; break; default: m_codLoadStatus = CodUnknown; } #else // GPSIM_0_21_11+ FILE * pFile = fopen( fileName, "r" ); if ( !pFile ) m_codLoadStatus = CodFileUnreadable; else m_codLoadStatus = ( ProgramFileTypeList::GetList().LoadProgramFile( & tempProcessor, fileName, pFile ) ) ? CodSuccess : CodFailure; #endif m_pPicProcessor = dynamic_cast(tempProcessor); if ( codLoadStatus() == 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::displayCodLoadStatus( ) { switch (m_codLoadStatus) { case CodSuccess: break; case CodFileNotFound: KMessageBox::sorry( 0l, i18n("The cod file \"%1\" was not found.").arg(m_symbolFile), i18n("File Not Found") ); break; case CodUnrecognizedProcessor: KMessageBox::sorry( 0l, i18n("The processor for cod file \"%1\" is unrecognized.").arg(m_symbolFile), i18n("Unrecognized Processor") ); break; case CodFileNameTooLong: KMessageBox::sorry( 0l, i18n("The file name \"%1\" is too long.").arg(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.").arg(m_symbolFile), i18n("LST File Not Found") ); break; case CodBadFile: KMessageBox::sorry( 0l, i18n("The cod file \"%1\" is bad.").arg(m_symbolFile), i18n("Bad File") ); break; case CodFileUnreadable: KMessageBox::sorry( 0l, i18n("The cod file \"%1\" could not be read from.").arg(m_symbolFile), i18n("Unreadable File") ); break; case CodFailure: case CodUnknown: KMessageBox::sorry( 0l, i18n("An error occured with the cod file \"%1\".").arg(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(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(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.findRev('.') - 1 ).lower(); if ( extension == "flowcode" || extension == "asm" || extension == "cod" || extension == "basic" || extension == "microbe" || extension == "c" ) return Valid; if ( extension == "hex" && TQFile::exists( TQString(programFile).replace(".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.findRev('.') - 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).replace(".hex",".cod"); } else if ( extension == "basic" || extension == "microbe" ) { compileMicrobe( fileName, receiver, successMember, failMember ); return TQString(fileName).replace( "."+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).replace( ".hex", ".cod" ); } else if ( extension == "asm" ) { ProcessOptions o; o.b_addToProject = false; o.setTargetFile( TQString(fileName).replace(".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).replace(".asm",".cod"); } else if ( extension == "c" ) { ProcessOptions o; o.b_addToProject = false; o.setTargetFile( TQString(fileName).replace(".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).replace(".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).replace(".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 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::iterator end = debugLinesToDelete.end(); for ( TQValueList::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="<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=\""<picProcessor()->files[fileID]->max_line() - 2; if ( (asmFromLine < 0) || (asmToLine < asmFromLine) ) { kdWarning() << k_funcinfo << "Invalid lines: asmFromLine="<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.contains( 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