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 #ifndef GPSIMPROCESSOR_H
15 #define GPSIMPROCESSOR_H
16 
17 #include "sourceline.h"
18 
19 #include <QMap>
20 // #include <q3valuevector.h>
21 #include <QObject>
22 #include <QList>
23 #include <QVector>
24 
25 class DebugLine;
26 class GpsimProcessor;
27 class MicroInfo;
28 class pic_processor; // from gpsim
29 class Register;
30 class RegisterMemoryAccess;
31 
32 typedef QMap<SourceLine, SourceLine> SourceLineMap;
33 typedef QList<int> IntList;
34 
35 
36 class DebugLine : public SourceLine
37 {
38 	public:
39 		DebugLine();
40 		/// @param fileName a path to a file in the local filesystem
41 		DebugLine( const QString & fileName, int line );
42 		/**
43 		 * Whether or not to break when we reach this line.
44 		 */
isBreakpoint()45 		bool isBreakpoint() const { return m_bIsBreakpoint; }
46 		/**
47 		 * Set whether or not to break when we reach this line.
48 		 */
setBreakpoint(bool breakpoint)49 		void setBreakpoint( bool breakpoint ) { m_bIsBreakpoint = breakpoint; }
50 		/**
51 		 * Used for efficiency purposes by GpsimProcessor. Sets a flag.
52 		 */
markAsDeleted()53 		void markAsDeleted() { m_bMarkedAsDeleted = true; }
54 		/**
55 		 * Used for efficiency purposes by GpsimProcessor.
56 		 */
markedAsDeleted()57 		bool markedAsDeleted() const { return m_bMarkedAsDeleted; }
58 
59 	protected:
60 		bool m_bIsBreakpoint;
61 		bool m_bMarkedAsDeleted;
62 
63 	private:
64 		DebugLine( const DebugLine & dl );
65 		DebugLine & operator = ( const DebugLine & dl );
66 };
67 
68 
69 /**
70 @short Stores info from gpsim register, used to hide gpsim interface
71 @author David Saxton
72 */
73 class RegisterInfo : public QObject
74 {
75 	Q_OBJECT
76 	public:
77 		RegisterInfo( Register * reg );
78 
79 		enum RegisterType
80 		{
81 			Invalid,
82 			Generic,
83 			File,
84 			SFR,
85 			Breakpoint
86 		};
87 
type()88 		RegisterType type() const { return m_type; }
name()89 		QString name() const { return m_name; }
90 		unsigned value() const;
91 		static QString toString( RegisterType type );
92 
93 		/**
94 		 * Checks to see if the value has changed; if so, emit new value.
95 		 */
96 		void update();
97 
98 	signals:
99 		void valueChanged( unsigned newValue );
100 
101 	protected:
102 		QString m_name;
103 		RegisterType m_type;
104 		Register * m_pRegister;
105 		unsigned m_prevEmitValue;
106 };
107 
108 
109 /**
110 @short Stores information about a set of registers, used to hide gpsim interface.
111 @author David Saxton
112 */
113 class RegisterSet
114 {
115 	public:
116 		RegisterSet( pic_processor * picProcessor );
117 		~RegisterSet();
118 
119 		/**
120 		 * Calls update for each RegisterInfo in this set.
121 		 */
122 		void update();
123 		/**
124 		 * Returns the number of registers.
125 		 */
size()126 		unsigned size() const { return m_registers.size(); }
127 
128 		RegisterInfo * fromAddress( unsigned address );
129 		RegisterInfo * fromName( const QString & name );
130 
131 	protected:
132 		typedef QMap< QString, RegisterInfo * > RegisterInfoMap;
133 		RegisterInfoMap m_nameToRegisterMap;
134 		QVector< RegisterInfo * > m_registers;
135 };
136 
137 
138 /**
139 @author David Saxton
140 */
141 class GpsimDebugger : public QObject
142 {
143 	friend class GpsimProcessor;
144 	Q_OBJECT
145 
146 	public:
147 		enum Type
148 		{
149 			AsmDebugger = 0,
150 			HLLDebugger = 1
151 		};
152 
153 		GpsimDebugger( Type type, GpsimProcessor * gpsim );
154 		~GpsimDebugger() override;
155 
gpsim()156 		GpsimProcessor * gpsim() const { return m_pGpsim; }
157 
158 		/**
159 		 * When an assembly file was generated by a high level language compiler
160 		 * like SDCC, it will insert markers like ";#CSRC" that show which line
161 		 * of source-code generated the given set of assembly instructions. This
162 		 * matches up the assembly file lines with the associated source file
163 		 * lines.
164 		 * @param sourceFile the path to an assembly file in the local filesystem
165 		 * @param assemblyFile the path to a source file in the local filesystem
166 		 */
167 		void associateLine( const QString & sourceFile, int sourceLine, const QString & assemblyFile, int assemblyLine );
168 		/**
169 		 * Check to see if we've hit a breakpoint or similar; if so, this
170 		 * function will stop the execution of the PIC program.
171 		 */
172 		void checkForBreak();
173 		/**
174 		 * Sets the breakpoints used for the given file to exactly those that
175 		 * are contained in this list. Breakpoints for other files are not
176 		 * affected.
177 		 * @param path the location of the file (which gpsim must recognise).
178 		 */
179 		void setBreakpoints( const QString & path, const IntList & lines );
180 		/**
181 		 * Sets / removes the breakpoint at the given line
182 		 */
183 		void setBreakpoint( const QString & path, int line, bool isBreakpoint );
184 		/**
185 		 * Returns the current source line that gpsim is at. By default, this
186 		 * will be the corresponding assembly line. That can be overwritten
187 		 * using mapAddressBlockToLine.
188 		 */
189 		SourceLine currentLine();
190 		/**
191 		 * Returns a pointer to the debug info for the current line.
192 		 */
193 		DebugLine * currentDebugLine();
194 		/**
195 		 * @return the program address for the given line (or -1 if no such
196 		 * line).
197 		 */
198 		int programAddress( const QString & path, int line );
199 		/**
200 		 * Step into the next program line.
201 		 */
202 		void stepInto();
203 		/**
204 		 * Step over the next program instruction. If we are currently running,
205 		 * this function will do nothing. Otherwise, it will record the current
206 		 * stack level, step, and if the new stack level is <= the initial level
207 		 * then return - otherwise, this processor will set a breakpoint for
208 		 * stack levels <= initial, and go to running mode.
209 		 */
210 		void stepOver();
211 		/**
212 		 * Similar to stepOver, except we break when the stack level becomes <
213 		 * the initial stack level (instead of <= initial).
214 		 */
215 		void stepOut();
216 
217 	signals:
218 		/**
219 		 * Emitted when a line is reached. By default, this is the line of the
220 		 * input assembly file; however, the line associated with an address in
221 		 * the PIC memory can be changed with mapAddressBlockToLine.
222 		 */
223 		void lineReached( const SourceLine & sourceLine );
224 
225 	protected slots:
226 		void gpsimRunningStatusChanged( bool isRunning );
227 
228 	protected:
229 		void initAddressToLineMap();
230 		void stackStep( int dl );
231 		void emitLineReached();
232 
233 		int m_stackLevelLowerBreak; // Set by step-over, for when the stack level decreases to the one given
234 		SourceLine m_previousAtLineEmit; // Used for working out whether we should emit a new line reached signal
235 		DebugLine ** m_addressToLineMap;
236 		DebugLine * m_pBreakFromOldLine;
237 		GpsimProcessor * m_pGpsim;
238 		Type m_type;
239 		unsigned m_addressSize;
240 		SourceLineMap m_sourceLineMap; // assembly <--> High level language
241 };
242 
243 
244 /**
245 @author David Saxton
246 */
247 class GpsimProcessor : public QObject
248 {
249 	friend class GpsimDebugger;
250 	Q_OBJECT
251 
252 	public:
253 		/**
254 		 * Create a new gpsim processor. After calling this constructor, you
255 		 * should always call codLoadStatus() to ensure that the cod file was
256 		 * loaded successfully.
257 		 */
258 		GpsimProcessor( QString symbolFile, QObject *parent = nullptr );
259 		~GpsimProcessor() override;
260 
setDebugMode(GpsimDebugger::Type mode)261 		void setDebugMode( GpsimDebugger::Type mode ) { m_debugMode = mode; }
currentDebugger()262 		GpsimDebugger * currentDebugger() const { return m_pDebugger[m_debugMode]; }
263 
264 		enum CodLoadStatus
265 		{
266 			CodSuccess,
267 			CodFileNotFound,
268 			CodUnrecognizedProcessor,
269 			CodFileNameTooLong,
270 			CodLstNotFound,
271 			CodBadFile,
272 			CodFileUnreadable,
273 			CodFailure,
274 			CodUnknown // Should never be this, but just in case load_symbol_file returns something funny
275 		};
276 
277 		enum InstructionType
278 		{
279 			LiteralOp,
280 			BitOp,
281 			RegisterOp,
282 			UnknownOp
283 		};
284 
285 		/**
286 		 * @return status of opening the COD file
287 		 * @see displayCodLoadStatus
288 		 */
codLoadStatus()289 		CodLoadStatus codLoadStatus() const { return m_codLoadStatus; }
290 		/**
291 		 * Popups a messagebox to the user according to the CodLoadStatus. Will
292 		 * only popup a messagebox if the CodLoadStatus wasn't CodSuccess.
293 		 */
294 		void displayCodLoadStatus();
295 		/**
296 		 * Returns a list of source files for the currently running program.
297 		 * Each entry is a path in the local filesystem.
298 		 */
299 		QStringList sourceFileList();
300 		/**
301 		 * Set whether or not to run gpsim. (i.e. whether or not the step
302 		 * function should do anything when called with force=false).
303 		 */
304 		void setRunning( bool run );
305 		/**
306 		 * Returns true if running (currently simulating), else gpsim is paused.
307 		 */
isRunning()308 		bool isRunning() const { return m_bIsRunning; }
309 		/**
310 		 * Execute the next program instruction. If we are not in a running
311 		 * mode, then this function will do nothing.
312 		 */
313 		void executeNext();
314 		/**
315 		 * Reset all parts of the simulation. Gpsim will not run until
316 		 * setRunning(true) is called. Breakpoints are not affected.
317 		 */
318 		void reset();
319 		/**
320 		 * Returns the microinfo describing this processor.
321 		 */
322 		MicroInfo * microInfo() const;
323 
picProcessor()324 		pic_processor * picProcessor() const { return m_pPicProcessor; }
325 		unsigned programMemorySize() const;
registerMemory()326 		RegisterSet * registerMemory() const { return m_pRegisterMemory; }
327 		/**
328 		 * @return the instruction type at the given address.
329 		 */
330 		InstructionType instructionType( unsigned address );
331 		/**
332 		 * @return the address of the operand's register at address if the
333 		 * instruction at address is a register operation, and -1 otherwise.
334 		 */
335 		int operandRegister( unsigned address );
336 		/**
337 		 * @return the literal if the instruction at address is a literal
338 		 * operation, and -1 otherwise.
339 		 */
340 		int operandLiteral( unsigned address );
341 
342 		//BEGIN Convenience functions for PIC files
343 		enum ProgramFileValidity { DoesntExist, IncorrectType, Valid };
344 		/**
345 		 * @return information on the validity of the given program file (either
346 		 * DoesntExist, IncorrectType, or Valid).
347 		 * @see static QString generateSymbolFile
348 		 */
349 		static ProgramFileValidity isValidProgramFile( const QString & programFile );
350 		/**
351 		 * Converts the file at programFile to a Symbol file for emulation,
352 		 * and returns that symbol file's path
353 		 * @param fileName The full url to the file
354 		 * @param receiver The slot to connect the assembled signal to
355 		 * @see static bool isValidProgramFile( const QString &programFile )
356 		 */
357 		static QString generateSymbolFile( const QString &fileName, QObject *receiver, const char *successMember, const char * failMember = nullptr );
358 		/**
359 		 *Compile microbe to output to the given filename
360 		 */
361 		static void compileMicrobe( const QString &filename, QObject *receiver, const char * successMember, const char * failMember = nullptr );
362 		//END convenience functions for PIC files
363 
364 	signals:
365 		/**
366 		 * Emitted when the running status of gpsim changes.
367 		 */
368 		void runningStatusChanged( bool isRunning );
369 
370 	protected:
371 		/**
372 		 * Calls emitLineReached for each debugger.
373 		 */
374 		void emitLineReached();
375 
376 		pic_processor * m_pPicProcessor;
377 		CodLoadStatus m_codLoadStatus;
378 		const QString m_symbolFile;
379 		RegisterSet * m_pRegisterMemory;
380 		GpsimDebugger::Type m_debugMode;
381 		GpsimDebugger * m_pDebugger[2]; // Asm, HLL
382 
383 		/**
384 		 * We are called effectively for each cycle of the cycle of the
385 		 * processor. This value is used as some instructions (e.g. goto) take
386 		 * two cycles to execute, and so we must ignore one cycle to ensure
387 		 * realtime simulation.
388 		 */
389 		bool m_bCanExecuteNextCycle;
390 
391 	private:
392 		bool m_bIsRunning;
393 };
394 
395 #endif
396 
397 #endif // !NO_GPSIM
398