1 /***************************************************************************
2  *   Copyright (C) 2005 by David Saxton                                    *
3  *   david@bluehaze.org                                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  ***************************************************************************/
10 
11 #include "config.h"
12 #ifndef NO_GPSIM
13 
14 #include "asmparser.h"
15 #include "debugmanager.h"
16 #include "flowcodedocument.h"
17 #include "gpsimprocessor.h"
18 #include "language.h"
19 #include "languagemanager.h"
20 #include "microlibrary.h"
21 #include "processchain.h"
22 #include "simulator.h"
23 
24 #include <cassert>
25 
26 #include <KLocalizedString>
27 #include <KMessageBox>
28 
29 #include <QDebug>
30 #include <QTemporaryFile>
31 #include <QDir>
32 #include <QFile>
33 #include <QTextStream>
34 #include <QTimer>
35 
36 #include "gpsim/cod.h"
37 #include "gpsim/interface.h"
38 #include "gpsim/gpsim_classes.h"
39 #include "gpsim/pic-processor.h"
40 #include "gpsim/registers.h"
41 #include "gpsim/14bit-registers.h"
42 #include "gpsim/symbol.h"
43 #include "gpsim/sim_context.h"
44 
45 bool bDoneGpsimInit = false;
46 bool bUseGUI = true;
47 // extern "C" void initialize_gpsim();
48 // void initialize_gpsim(void);
49 extern void initialize_commands();
50 extern void initialize_readline();
51 extern void gui_main(void);
52 extern void cli_main();
gpsim_version()53 void gpsim_version() {}
quit_gui()54 void quit_gui() {}
55 
56 
57 //BEGIN class GpsimProcessor
58 /**
59 Work around a bug in gpsim: the directory in a filename is recorded twice, e.g.
60 "/home/david/afile.asm" is recorded as "/home/david//home/david/afile.asm". This
61 function will remove the duplicated directory path (by searching for a "//").
62 */
sanitizeGpsimFile(QString file)63 QString sanitizeGpsimFile( QString file )
64 {
65 	int pos = file.indexOf("//");
66 	if ( pos != -1 )
67 	{
68 		file.remove( 0, pos + 1 );
69 	}
70 	return file;
71 }
72 
73 
GpsimProcessor(QString symbolFile,QObject * parent)74 GpsimProcessor::GpsimProcessor( QString symbolFile, QObject *parent )
75 	: QObject(parent),
76 	m_symbolFile(symbolFile)
77 {
78 	if (!bDoneGpsimInit)
79 	{
80 		initialize_gpsim_core();
81 		initialization_is_complete();
82 
83 		bDoneGpsimInit = true;
84 	}
85 
86 	m_bCanExecuteNextCycle = true;
87 	m_bIsRunning = false;
88 	m_pPicProcessor = nullptr;
89 	m_codLoadStatus = CodUnknown;
90 	m_pRegisterMemory = nullptr;
91 	m_debugMode = GpsimDebugger::AsmDebugger;
92 	m_pDebugger[0] = m_pDebugger[1] = nullptr;
93 
94 	Processor * tempProcessor = nullptr;
95 	const char * fileName = symbolFile.toAscii();
96 
97 #ifdef GPSIM_0_21_4
98     qDebug() << "GPSIM_0_21_4 GpsimProcessor " << fileName;
99 	switch ( (cod_errors)load_symbol_file( &tempProcessor, fileName ) )
100 	{
101 		case COD_SUCCESS:
102 			m_codLoadStatus = CodSuccess;
103 			break;
104 		case COD_FILE_NOT_FOUND:
105 			m_codLoadStatus = CodFileNotFound;
106 			break;
107 		case COD_UNRECOGNIZED_PROCESSOR:
108 			m_codLoadStatus = CodUnrecognizedProcessor;
109 			break;
110 		case COD_FILE_NAME_TOO_LONG:
111 			m_codLoadStatus = CodFileNameTooLong;
112 			break;
113 		case COD_LST_NOT_FOUND:
114 			m_codLoadStatus = CodLstNotFound;
115 			break;
116 		case COD_BAD_FILE:
117 			m_codLoadStatus = CodBadFile;
118 			break;
119 		default:
120 			m_codLoadStatus = CodUnknown;
121 	}
122 #else // GPSIM_0_21_11+
123     qDebug() << "GPSIM_0_21_11+ GpsimProcessor " << fileName;
124 	FILE * pFile = fopen( fileName, "r" );
125 	if ( !pFile )
126 		m_codLoadStatus = CodFileUnreadable;
127 	else
128 		m_codLoadStatus = ( ProgramFileTypeList::GetList().LoadProgramFile( & tempProcessor, fileName, pFile ) ) ? CodSuccess : CodFailure;
129 #endif
130     qDebug() << " m_codLoadStatus=" << m_codLoadStatus;
131 
132 	m_pPicProcessor = dynamic_cast<pic_processor*>(tempProcessor);
133 
134 	if ( codLoadStatus() == CodSuccess )
135 	{
136 		m_pRegisterMemory = new RegisterSet( m_pPicProcessor );
137 		m_pDebugger[0] = new GpsimDebugger( GpsimDebugger::AsmDebugger, this );
138 		m_pDebugger[1] = new GpsimDebugger( GpsimDebugger::HLLDebugger, this );
139 		Simulator::self()->attachGpsimProcessor(this);
140 		DebugManager::self()->registerGpsim(this);
141 	}
142 }
143 
144 
~GpsimProcessor()145 GpsimProcessor::~GpsimProcessor()
146 {
147     if (!Simulator::isDestroyedSim()) {
148         Simulator::self()->detachGpsimProcessor(this);
149     }
150 	delete m_pRegisterMemory;
151 
152 	if ( m_pDebugger[0] )
153 		m_pDebugger[0]->deleteLater();
154 	if ( m_pDebugger[1] )
155 		m_pDebugger[1]->deleteLater();
156 }
157 
158 
displayCodLoadStatus()159 void GpsimProcessor::displayCodLoadStatus( )
160 {
161 	switch (m_codLoadStatus)
162 	{
163 		case CodSuccess:
164 			break;
165 		case CodFileNotFound:
166 			KMessageBox::sorry( nullptr, i18n("The cod file \"%1\" was not found.", m_symbolFile), i18n("File Not Found") );
167 			break;
168 		case CodUnrecognizedProcessor:
169 			KMessageBox::sorry( nullptr, i18n("The processor for cod file \"%1\" is unrecognized.", m_symbolFile), i18n("Unrecognized Processor") );
170 			break;
171 		case CodFileNameTooLong:
172 			KMessageBox::sorry( nullptr, i18n("The file name \"%1\" is too long.", m_symbolFile), i18n("Filename Too Long") );
173 			break;
174 		case CodLstNotFound:
175 			KMessageBox::sorry( nullptr, i18n("The lst file associated with the cod file \"%1\" was not found.", m_symbolFile), i18n("LST File Not Found") );
176 			break;
177 		case CodBadFile:
178 			KMessageBox::sorry( nullptr, i18n("The cod file \"%1\" is bad.", m_symbolFile), i18n("Bad File") );
179 			break;
180 		case CodFileUnreadable:
181 			KMessageBox::sorry( nullptr, i18n("The cod file \"%1\" could not be read from.", m_symbolFile), i18n("Unreadable File") );
182 			break;
183 		case CodFailure:
184 		case CodUnknown:
185 			KMessageBox::sorry( nullptr, i18n("An error occurred with the cod file \"%1\".", m_symbolFile), i18n("Error") );
186 			break;
187 	}
188 }
189 
190 
programMemorySize() const191 unsigned GpsimProcessor::programMemorySize() const
192 {
193 	return m_pPicProcessor->program_memory_size();
194 }
195 
196 
sourceFileList()197 QStringList GpsimProcessor::sourceFileList()
198 {
199 	QStringList files;
200 
201 	// Work around nasty bug in gpsim 0.21.4 where nsrc_files value might be used uninitiazed
202 	int max = m_pPicProcessor->files.nsrc_files();
203 #ifdef GPSIM_0_21_4
204 	if ( max > 10 )
205 		max = 10;
206 #endif
207 
208 	for ( int i = 0; i < max; ++i )
209 	{
210 		if ( !m_pPicProcessor->files[i] )
211 			continue;
212 
213 		files << sanitizeGpsimFile( m_pPicProcessor->files[i]->name().c_str() );
214 	}
215 
216 	return files;
217 }
218 
219 
emitLineReached()220 void GpsimProcessor::emitLineReached()
221 {
222 	m_pDebugger[0]->emitLineReached();
223 	m_pDebugger[1]->emitLineReached();
224 }
225 
226 
setRunning(bool run)227 void GpsimProcessor::setRunning( bool run )
228 {
229 	if ( m_bIsRunning == run )
230 		return;
231 
232 	m_bIsRunning = run;
233 	emit runningStatusChanged(run);
234 }
235 
236 
executeNext()237 void GpsimProcessor::executeNext()
238 {
239 	if ( !m_bIsRunning )
240 		return;
241 
242 	if ( !m_bCanExecuteNextCycle )
243 	{
244 		m_bCanExecuteNextCycle = true;
245 		return;
246 	}
247 
248 	unsigned long long beforeExecuteCount = get_cycles().get();
249 
250 	if(get_bp().have_interrupt())
251 	{
252 		m_pPicProcessor->interrupt();
253 	}
254 	else
255 	{
256 		m_pPicProcessor->step_one(false); // Don't know what the false is for; gpsim ignores its value anyway
257 
258 		// Some instructions take more than one cycle to execute, so ignore next cycle if this was the case
259 		if ( (get_cycles().get() - beforeExecuteCount) > 1 )
260 			m_bCanExecuteNextCycle = false;
261 	}
262 
263 	currentDebugger()->checkForBreak();
264 
265 	// Let's also update the values of RegisterInfo every 25 milliseconds
266 	if ( (beforeExecuteCount % 10000) == 0 )
267 		registerMemory()->update();
268 }
269 
270 
reset()271 void GpsimProcessor::reset()
272 {
273 	bool wasRunning = isRunning();
274 	m_pPicProcessor->reset(SIM_RESET);
275 	setRunning(false);
276 	if (!wasRunning)
277 	{
278 		// If we weren't running before, then the next signal won't have been emitted
279 		emitLineReached();
280 	}
281 }
282 
283 
microInfo() const284 MicroInfo * GpsimProcessor::microInfo( ) const
285 {
286 	if ( !m_pPicProcessor ){
287 		qWarning() << Q_FUNC_INFO << " m_pPicProcessor == nullptr" << endl;
288 		return nullptr;
289 	}
290 
291 	return MicroLibrary::self()->microInfoWithID( m_pPicProcessor->name().c_str() );
292 }
293 
294 
operandRegister(unsigned address)295 int GpsimProcessor::operandRegister( unsigned address )
296 {
297 	instruction * ins = m_pPicProcessor->program_memory[ address ];
298 	if ( Register_op * reg = dynamic_cast<Register_op*>(ins) )
299 		return reg->register_address;
300 	return -1;
301 }
302 
303 
operandLiteral(unsigned address)304 int GpsimProcessor::operandLiteral( unsigned address )
305 {
306 	instruction * ins = m_pPicProcessor->program_memory[ address ];
307 	if ( Literal_op * lit = dynamic_cast<Literal_op*>(ins) )
308 		return lit->L;
309 	return -1;
310 }
311 
312 
isValidProgramFile(const QString & programFile)313 GpsimProcessor::ProgramFileValidity GpsimProcessor::isValidProgramFile( const QString & programFile )
314 {
315 	if ( !QFile::exists(programFile) )
316 		return DoesntExist;
317 
318 	QString extension = programFile.right( programFile.length() - programFile.lastIndexOf('.') - 1 ).toLower();
319 
320 	if ( extension == "flowcode" ||
321 			extension == "asm" ||
322 			extension == "cod" ||
323 			extension == "basic" || extension == "microbe" ||
324 	   		extension == "c" )
325 		return Valid;
326 
327 	if ( extension == "hex" && QFile::exists( QString(programFile).replace(".hex",".cod") ) )
328 		return Valid;
329 
330 	return IncorrectType;
331 }
332 
333 
generateSymbolFile(const QString & fileName,QObject * receiver,const char * successMember,const char * failMember)334 QString GpsimProcessor::generateSymbolFile( const QString &fileName, QObject *receiver, const char *successMember, const char * failMember )
335 {
336     qDebug() << Q_FUNC_INFO << "fileName=" << fileName ;
337 	if (isValidProgramFile(fileName) != GpsimProcessor::Valid) {
338         qDebug() << Q_FUNC_INFO << "not valid program file";
339 		return QString::null;
340     }
341 
342 	QString extension = fileName.right( fileName.length() - fileName.lastIndexOf('.') - 1 ).toLower();
343 
344 	if ( extension == "cod" )
345 	{
346 		QTimer::singleShot( 0, receiver, successMember );
347 		return fileName;
348 	}
349 	if ( extension == "hex" )
350 	{
351 		QTimer::singleShot( 0, receiver, successMember );
352 		// We've already checked for the existance of the ".cod" file in GpsimProcessor::isValidProgramFile
353 		return QString(fileName).replace(".hex",".cod");
354 	}
355 
356 	else if ( extension == "basic" || extension == "microbe" )
357 	{
358 		compileMicrobe( fileName, receiver, successMember, failMember );
359 		return QString(fileName).replace( "."+extension, ".cod" );
360 	}
361 	else if ( extension == "flowcode" )
362 	{
363         QTemporaryFile tmpFile(QDir::tempPath() + QLatin1String("/ktechlab_XXXXXX.hex"));
364         if (!tmpFile.open()) {
365             qWarning() << " failed to open " << tmpFile.fileName() << " error " << tmpFile.errorString();
366             return QString::null;
367         }
368 		const QString hexFile = tmpFile.fileName();
369 		ProcessOptions o;
370 		o.b_addToProject = false;
371 		o.setTargetFile( hexFile );
372 		o.setInputFiles( QStringList(fileName) );
373 		o.setMethod( ProcessOptions::Method::Forget );
374 		o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_Program );
375 
376 		ProcessChain * pc = LanguageManager::self()->compile(o);
377 		if (receiver)
378 		{
379 			if (successMember)
380 				connect( pc, SIGNAL(successful()), receiver, successMember );
381 			if (failMember)
382 				connect( pc, SIGNAL(failed()), receiver, failMember );
383 		}
384 
385 		return QString(hexFile).replace( ".hex", ".cod" );
386 	}
387 	else if ( extension == "asm" )
388 	{
389 		ProcessOptions o;
390 		o.b_addToProject = false;
391 		o.setTargetFile( QString(fileName).replace(".asm",".hex"));
392 		o.setInputFiles(QStringList(fileName));
393 		o.setMethod( ProcessOptions::Method::Forget );
394 		o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(fileName), ProcessOptions::ProcessPath::Program ) );
395 
396 		ProcessChain *pc = LanguageManager::self()->compile(o);
397 		if (receiver)
398 		{
399 			if (successMember)
400 				connect( pc, SIGNAL(successful()), receiver, successMember );
401 			if (failMember)
402 				connect( pc, SIGNAL(failed()), receiver, failMember );
403 		}
404 
405 		return QString(fileName).replace(".asm",".cod");
406 	}
407 	else if ( extension == "c" )
408 	{
409 		ProcessOptions o;
410 		o.b_addToProject = false;
411 		o.setTargetFile( QString(fileName).replace(".c",".hex"));
412 		o.setInputFiles(QStringList(fileName));
413 		o.setMethod( ProcessOptions::Method::Forget );
414 		o.setProcessPath( ProcessOptions::ProcessPath::C_Program );
415 
416 		ProcessChain *pc = LanguageManager::self()->compile(o);
417 		if (receiver)
418 		{
419 			if (successMember)
420 				connect( pc, SIGNAL(successful()), receiver, successMember );
421 			if (failMember)
422 				connect( pc, SIGNAL(failed()), receiver, failMember );
423 		}
424 
425 		return QString(fileName).replace(".c",".cod");
426 	}
427 
428 	if ( failMember )
429 		QTimer::singleShot( 0, receiver, failMember );
430 	return QString::null;
431 }
432 
433 
compileMicrobe(const QString & filename,QObject * receiver,const char * successMember,const char * failMember)434 void GpsimProcessor::compileMicrobe( const QString &filename, QObject *receiver, const char * successMember, const char * failMember )
435 {
436 	ProcessOptions o;
437 	o.b_addToProject = false;
438 	o.setTargetFile( QString(filename).replace(".microbe",".hex") );
439 	o.setInputFiles(QStringList(filename));
440 	o.setMethod( ProcessOptions::Method::Forget );
441 	o.setProcessPath( ProcessOptions::ProcessPath::Microbe_Program );
442 	ProcessChain * pc = LanguageManager::self()->compile(o);
443 	if (receiver)
444 	{
445 		if (successMember)
446 			connect( pc, SIGNAL(successful()), receiver, successMember );
447 		if (failMember)
448 			connect( pc, SIGNAL(failed()), receiver, failMember );
449 	}
450 }
451 //END class GpsimProcessor
452 
453 
454 
455 //BEGIN class GpsimDebugger
GpsimDebugger(Type type,GpsimProcessor * gpsim)456 GpsimDebugger::GpsimDebugger( Type type, GpsimProcessor * gpsim )
457 	: QObject()
458 {
459 	m_pGpsim = gpsim;
460 	m_type = type;
461 	m_pBreakFromOldLine = nullptr;
462 	m_addressToLineMap = nullptr;
463 	m_stackLevelLowerBreak = -1;
464 	m_addressSize = 0;
465 
466 	connect( m_pGpsim, SIGNAL(runningStatusChanged(bool )), this, SLOT(gpsimRunningStatusChanged(bool )) );
467 
468 	if ( type == HLLDebugger )
469 	{
470 		const QStringList sourceFileList = m_pGpsim->sourceFileList();
471 		QStringList::const_iterator sflEnd = sourceFileList.end();
472 		for ( QStringList::const_iterator it = sourceFileList.begin(); it != sflEnd; ++it )
473 		{
474 			AsmParser p(*it);
475 			p.parse(this);
476 		}
477 	}
478 
479 	initAddressToLineMap();
480 }
481 
482 
~GpsimDebugger()483 GpsimDebugger::~GpsimDebugger()
484 {
485 	QList<DebugLine*> debugLinesToDelete;
486 
487 	for ( unsigned i = 0; i < m_addressSize; ++i )
488 	{
489 		DebugLine * dl = m_addressToLineMap[i];
490 		if ( !dl || dl->markedAsDeleted() )
491 			continue;
492 
493 		dl->markAsDeleted();
494 		debugLinesToDelete += dl;
495 	}
496 
497 	const QList<DebugLine*>::iterator end = debugLinesToDelete.end();
498 	for ( QList<DebugLine*>::iterator it = debugLinesToDelete.begin(); it != end; ++it )
499 		delete *it;
500 
501 	delete [] m_addressToLineMap;
502 }
503 
504 
gpsimRunningStatusChanged(bool isRunning)505 void GpsimDebugger::gpsimRunningStatusChanged( bool isRunning )
506 {
507 	if (!isRunning)
508 	{
509 		m_stackLevelLowerBreak = -1;
510 		m_pBreakFromOldLine = nullptr;
511 		emitLineReached();
512 	}
513 }
514 
515 
associateLine(const QString & sourceFile,int sourceLine,const QString & assemblyFile,int assemblyLine)516 void GpsimDebugger::associateLine( const QString & sourceFile, int sourceLine, const QString & assemblyFile, int assemblyLine )
517 {
518 	if ( assemblyLine < 0 || sourceLine < 0 )
519 	{
520 		qWarning() << Q_FUNC_INFO << "Invalid lines: assemblyLine="<<assemblyLine<<" sourceLine="<<sourceLine<<endl;
521 		return;
522 	}
523 
524 	SourceLine hllSource = SourceLine( sourceFile, sourceLine );
525 	SourceLine asmSource = SourceLine( assemblyFile, assemblyLine );
526 
527 	if ( m_sourceLineMap.contains(asmSource) )
528 	{
529 		qWarning() << Q_FUNC_INFO << "Already have an association for assembly (\""<<assemblyFile<<"\","<<assemblyLine<<")"<<endl;
530 		return;
531 	}
532 
533 	m_sourceLineMap[asmSource] = hllSource;
534 }
535 
536 
initAddressToLineMap()537 void GpsimDebugger::initAddressToLineMap()
538 {
539 	m_addressSize = m_pGpsim->programMemorySize();
540 
541 	delete [] m_addressToLineMap;
542 	m_addressToLineMap = new DebugLine*[m_addressSize];
543 	memset( m_addressToLineMap, 0, m_addressSize * sizeof(DebugLine*) );
544 
545 	if ( m_type == AsmDebugger )
546 	{
547 		for ( unsigned i = 0; i < m_addressSize; ++i )
548 		{
549 			int line = m_pGpsim->picProcessor()->pma->get_src_line(i) - 1;
550 			int fileID = m_pGpsim->picProcessor()->pma->get_file_id(i);
551 			FileContext * fileContext = m_pGpsim->picProcessor()->files[fileID];
552 
553 			if (fileContext)
554 				m_addressToLineMap[i] = new DebugLine( sanitizeGpsimFile( fileContext->name().c_str() ), line );
555 		}
556 	}
557 	else
558 	{
559 		SourceLineMap::const_iterator slmEnd = m_sourceLineMap.end();
560 		for ( SourceLineMap::const_iterator it = m_sourceLineMap.begin(); it != slmEnd; ++it )
561 		{
562 			SourceLineMap::const_iterator next = it;
563 			++next;
564 
565 			int asmToLine = ((next == slmEnd) || (next.key().fileName() != it.key().fileName())) ? -1 : next.key().line() - 1;
566 
567 			QString asmFile = it.key().fileName();
568 			int asmFromLine = it.key().line();
569 			SourceLine sourceLine = it.value();
570 
571 
572 			std::string stdAsmFile( asmFile.toAscii() );
573 			int fileID = m_pGpsim->picProcessor()->files.Find( stdAsmFile );
574 			if ( fileID == -1 )
575 			{
576 				qWarning() << Q_FUNC_INFO << "Could not find FileContext (asmFile=\""<<asmFile<<"\")"<<endl;
577 				continue;
578 			}
579 
580 			if ( asmToLine == -1 )
581 				asmToLine = m_pGpsim->picProcessor()->files[fileID]->max_line() - 2;
582 
583 			if ( (asmFromLine < 0) || (asmToLine < asmFromLine) )
584 			{
585 				qWarning() << Q_FUNC_INFO << "Invalid lines: asmFromLine="<<asmFromLine<<" asmToLine="<<asmToLine<<endl;
586 				continue;
587 			}
588 
589 			DebugLine * debugLine = new DebugLine( sourceLine.fileName(), sourceLine.line() );
590 			bool used = false;
591 
592 			for ( int i = asmFromLine; i <= asmToLine; ++i )
593 			{
594 #ifdef GPSIM_0_21_4
595 				int address = m_pGpsim->picProcessor()->pma->find_address_from_line( fileID, i+1 );
596 #else // GPSIM_0_21_11
597 				int address = m_pGpsim->picProcessor()->pma->find_address_from_line( m_pGpsim->picProcessor()->files[fileID], i+1 );
598 #endif
599 				if ( address != -1 )
600 				{
601 					used = true;
602 					m_addressToLineMap[address] = debugLine;
603 				}
604 			}
605 
606 			if (!used)
607 				delete debugLine;
608 		}
609 	}
610 }
611 
612 
setBreakpoints(const QString & path,const IntList & lines)613 void GpsimDebugger::setBreakpoints( const QString & path, const IntList & lines )
614 {
615 	for ( unsigned i = 0; i < m_addressSize; i++ )
616 	{
617 		DebugLine * dl = m_addressToLineMap[i];
618 		if ( !dl || dl->fileName() != path )
619 			continue;
620 
621 		dl->setBreakpoint( lines.contains( dl->line() ) );
622 	}
623 }
624 
625 
setBreakpoint(const QString & path,int line,bool isBreakpoint)626 void GpsimDebugger::setBreakpoint( const QString & path, int line, bool isBreakpoint )
627 {
628 	for ( unsigned i = 0; i < m_addressSize; i++ )
629 	{
630 		if ( !m_addressToLineMap[i] )
631 			continue;
632 
633 		if ( (m_addressToLineMap[i]->fileName() == path) &&
634 					( line == m_addressToLineMap[i]->line() ) )
635 			m_addressToLineMap[i]->setBreakpoint(isBreakpoint);
636 	}
637 }
638 
639 
currentDebugLine()640 DebugLine * GpsimDebugger::currentDebugLine()
641 {
642 	return m_addressToLineMap[ m_pGpsim->picProcessor()->pc->get_value() ];
643 }
644 
645 
currentLine()646 SourceLine GpsimDebugger::currentLine()
647 {
648 	DebugLine * dl = currentDebugLine();
649 	return dl ? *dl : SourceLine();
650 }
651 
652 
emitLineReached()653 void GpsimDebugger::emitLineReached()
654 {
655 	SourceLine currentAt = currentLine();
656 
657 	if ( currentAt == m_previousAtLineEmit )
658 		return;
659 
660 	m_previousAtLineEmit = currentAt;
661 	m_pGpsim->registerMemory()->update();
662 	emit lineReached(currentAt);
663 }
664 
665 
checkForBreak()666 void GpsimDebugger::checkForBreak()
667 {
668 	DebugLine * currentLine = m_addressToLineMap[ m_pGpsim->picProcessor()->pc->get_value() ];
669 	int currentStackLevel = int( m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask );
670 
671 	bool ontoNextLine = m_pBreakFromOldLine != currentLine;
672 	bool lineBreakpoint = currentLine ? currentLine->isBreakpoint() : false;
673 	bool stackBreakpoint = m_stackLevelLowerBreak >= currentStackLevel;
674 
675 	if ( ontoNextLine && (lineBreakpoint || stackBreakpoint) )
676 		m_pGpsim->setRunning(false);
677 }
678 
679 
programAddress(const QString & path,int line)680 int GpsimDebugger::programAddress( const QString & path, int line )
681 {
682 	for ( unsigned i = 0; i < m_addressSize; ++i )
683 	{
684 		DebugLine * dl = m_addressToLineMap[i];
685 		if ( !dl || (dl->line() != line) || (dl->fileName() != path) )
686 			continue;
687 
688 		return i;
689 	}
690 
691 	return -1;
692 }
693 
694 
stepInto()695 void GpsimDebugger::stepInto()
696 {
697 	// I'm not aware of the stack being able to increase in size by more than
698 	// one at a time, so "1" should suffice here...but to be on the safe side,
699 	// make it a nice large number
700 	stackStep( 1 << 16 );
701 }
stepOver()702 void GpsimDebugger::stepOver()
703 {
704 	stackStep(0);
705 }
stepOut()706 void GpsimDebugger::stepOut()
707 {
708 	stackStep(-1);
709 }
stackStep(int dl)710 void GpsimDebugger::stackStep( int dl )
711 {
712 	if ( m_pGpsim->isRunning() )
713 		return;
714 
715 	int initialStack = (m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask) + dl;
716 	DebugLine * initialLine = currentDebugLine();
717 
718 	if ( initialStack < 0 )
719 		initialStack = 0;
720 
721 	// Reset any previous stackStep, and step
722 	m_pBreakFromOldLine = nullptr;
723 	m_stackLevelLowerBreak = -1;
724 	m_pGpsim->picProcessor()->step_one(false);
725 
726 	int currentStack = m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask;
727 	DebugLine * currentLine = currentDebugLine();
728 
729 	if ( (initialStack >= currentStack) && (initialLine != currentLine) )
730 		emitLineReached();
731 
732 	else
733 	{
734 		// Looks like we stepped into something or haven't gone onto the next
735 		// instruction, wait until we step back out....
736 		m_stackLevelLowerBreak = initialStack;
737 		m_pBreakFromOldLine = initialLine;
738 		m_pGpsim->setRunning(true);
739 	}
740 }
741 //END class Debugger
742 
743 
744 
745 //BEGIN class RegisterSet
RegisterSet(pic_processor * picProcessor)746 RegisterSet::RegisterSet( pic_processor * picProcessor )
747 {
748 	unsigned numRegisters = picProcessor->rma.get_size();
749 	qDebug() << Q_FUNC_INFO << "numRegisters="<<numRegisters<<endl;
750 	m_registers.resize( numRegisters /*, nullptr - 2018.06.02 - initialized below */ );
751 	for ( unsigned i = 0; i < numRegisters; ++i )
752 	{
753 		RegisterInfo * info = new RegisterInfo( & picProcessor->rma[i] );
754 		m_registers[i] = info;
755 		m_nameToRegisterMap[ info->name() ] = info;
756         qDebug() << Q_FUNC_INFO << " add register info " << info->name() << " at pos " << i << " addr " << info;
757 	}
758 #if defined(HAVE_GPSIM_0_26)
759 	RegisterInfo * info = new RegisterInfo( picProcessor->Wreg ); // is tihs correct for "W" member? TODO
760 #else
761 	RegisterInfo * info = new RegisterInfo( picProcessor->W );
762 #endif
763 	m_registers.append( info );
764 	m_nameToRegisterMap[ info->name() ] = info;
765     qDebug() << Q_FUNC_INFO << " add register info " << info->name() << " at end, addr " << info;
766     qDebug() << Q_FUNC_INFO << " registers.size " << m_registers.size() << " ; numRegisters " << numRegisters;
767 }
768 
769 
~RegisterSet()770 RegisterSet::~RegisterSet()
771 {
772 	for ( unsigned i = 0; i < m_registers.size(); ++i )
773 		delete m_registers[i];
774 }
775 
776 
fromAddress(unsigned address)777 RegisterInfo * RegisterSet::fromAddress( unsigned address )
778 {
779 	return (address < m_registers.size()) ? m_registers[address] : nullptr;
780 }
781 
782 
fromName(const QString & name)783 RegisterInfo * RegisterSet::fromName( const QString & name )
784 {
785 	// First try the name as case sensitive, then as case insensitive.
786 	if ( m_nameToRegisterMap.contains( name ) )
787 		return m_nameToRegisterMap[ name ];
788 
789 	QString nameLower = name.toLower();
790 
791 	RegisterInfoMap::iterator end = m_nameToRegisterMap.end();
792 	for ( RegisterInfoMap::iterator it = m_nameToRegisterMap.begin(); it != end; ++ it )
793 	{
794 		if ( it.key().toLower() == nameLower )
795 			return it.value();
796 	}
797 
798 	return nullptr;
799 }
800 
801 
update()802 void RegisterSet::update()
803 {
804 	for ( unsigned i = 0; i < m_registers.size(); ++i )
805 		m_registers[i]->update();
806 }
807 //END class RegisterSet
808 
809 
810 
811 //BEGIN class RegisterInfo
RegisterInfo(Register * reg)812 RegisterInfo::RegisterInfo( Register * reg )
813 {
814 	assert(reg);
815 	m_pRegister = reg;
816 	m_type = Invalid;
817 	m_prevEmitValue = 0;
818 
819 	switch ( m_pRegister->isa() )
820 	{
821 		case Register::GENERIC_REGISTER:
822 			m_type = Generic;
823 			break;
824 		case Register::FILE_REGISTER:
825 			m_type = File;
826 			break;
827 		case Register::SFR_REGISTER:
828 			m_type = SFR;
829 			break;
830 		case Register::BP_REGISTER:
831 			m_type = Breakpoint;
832 			break;
833 		case Register::INVALID_REGISTER:
834 			m_type = Invalid;
835 			break;
836 	}
837 
838 	m_name = QString::fromLatin1(m_pRegister->baseName().c_str());
839 }
840 
841 
value() const842 unsigned RegisterInfo::value() const
843 {
844 	return m_pRegister->value.data;
845 }
846 
847 
update()848 void RegisterInfo::update()
849 {
850 	unsigned newValue = value();
851 	if ( newValue != m_prevEmitValue )
852 	{
853 		m_prevEmitValue = newValue;
854 		emit valueChanged(newValue);
855 	}
856 }
857 
858 
toString(RegisterType type)859 QString RegisterInfo::toString( RegisterType type )
860 {
861 	switch ( type )
862 	{
863 		case Generic:
864 			return i18n("Generic");
865 
866 		case File:
867 			return i18n("File");
868 
869 		case SFR:
870 			return i18n("SFR");
871 
872 		case Breakpoint:
873 			return i18n("Breakpoint");
874 
875 		case Invalid:
876 			return i18n("Invalid");
877 	}
878 
879 	return i18n("Unknown");
880 }
881 //END class RegisterInfo
882 
883 
884 
885 //BEGIN class DebugLine
DebugLine(const QString & fileName,int line)886 DebugLine::DebugLine( const QString & fileName, int line )
887 	: SourceLine( fileName, line )
888 {
889 	m_bIsBreakpoint = false;
890 	m_bMarkedAsDeleted = false;
891 }
892 
893 
DebugLine()894 DebugLine::DebugLine()
895 	: SourceLine()
896 {
897 	m_bIsBreakpoint = false;
898 	m_bMarkedAsDeleted = false;
899 }
900 //END class DebugLine
901 
902 #endif
903