1 /*
2 Copyright (C) 1999 T. Scott Dattalo
3
4 This file is part of gpsim.
5
6 gpsim is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 gpsim is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with gpsim; see the file COPYING. If not, write to
18 the Free Software Foundation, 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21
22 #include <iostream>
23 #include <string>
24
25 #include "command.h"
26 #include "misc.h"
27 #include "cmd_break.h"
28
29 #include "../src/breakpoints.h"
30 #include "../src/expr.h"
31 #include "../src/gpsim_object.h"
32 #include "../src/ui.h"
33
34 cmd_break c_break;
35
36 #define CYCLE 1
37 #define EXECUTION 2
38 #define WRITE 3
39 #define READ 4
40 #define REGCHANGE 5
41 #define STK_OVERFLOW 7
42 #define STK_UNDERFLOW 8
43 #define WDT 9
44
45 static cmd_options cmd_break_options[] = {
46 {"c", CYCLE, OPT_TT_BITFLAG},
47 {"e", EXECUTION, OPT_TT_BITFLAG},
48 {"w", WRITE, OPT_TT_BITFLAG},
49 {"r", READ, OPT_TT_BITFLAG},
50 {"ch", REGCHANGE, OPT_TT_BITFLAG},
51 {"so", STK_OVERFLOW, OPT_TT_BITFLAG},
52 {"su", STK_UNDERFLOW, OPT_TT_BITFLAG},
53 {"wdt", WDT, OPT_TT_BITFLAG},
54 {0, 0, 0}
55 };
56
57
cmd_break()58 cmd_break::cmd_break()
59 : command("break", "br")
60 {
61 brief_doc = "Set a break point";
62 long_doc = "The 'break' command can be used to examine or set breakpoints.\n"
63 "gpsim supports execution style breaks, register access breaks,\n"
64 "complex expression breaks, attribute breaks, and other special breaks.\n"
65 "Program Memory breaks:\n"
66 " break e|r|w ADDRESS [,expr] [,\"message\"]\n"
67 " Halts when the address is executed, read, or written. The ADDRESS can be \n"
68 " a symbol or a number. If the optional expr is specified, then it must\n"
69 " evaluate to true before the simulation will halt. The optional message\n"
70 " allows a description to be associated with the break.\n"
71 "Register Memory breaks:\n"
72 " break r|w|ch REGISTER [,expr] [,\"message\"]\n"
73 " Halts when 'REGISTER' is read, written, or changed\n"
74 " and the optional expression evaluates to true\n"
75 " break r|w|ch boolean_expression\n"
76 " The boolean expression can only be of the form:\n"
77 " a) reg & mask == value\n"
78 " b) reg == value\n"
79 " - Note the 'ch' option is similar to the write option.\n"
80 " The change option evaluates the expression before and after\n"
81 " a register write and halts if the evaluation differs.\n"
82 "Cycle counter breaks:\n"
83 " break c VALUE [,\"message\"]\n"
84 " Halts when the cycle counter reaches 'VALUE'.\n"
85 "Attribute breaks:\n"
86 " break attribute\n"
87 " Arms the breakpoint condition for those attributes that support breaks.\n"
88 " For example, the stopwatch (help stopwatch) attribute can cause a break.\n"
89 "Miscellaneous breaks:\n"
90 " break so # halts on stack overflow.\n"
91 " break su # halts on stack underflow.\n"
92 " break wdt # halts on Watch Dog Timer timeout.\n"
93 "Expressions:\n"
94 " The conditional expressions mentioned above are syntactically similar to C's\n"
95 " expressions.\n"
96 "Examples:\n"
97 "\tbreak # display all of the break points\n"
98 "\tbreak e 0x20 # set an execution break point at address 0x20\n"
99 "\tbreak w reg1 == 0 # break if a zero is written to register reg1\n"
100 "\tbreak w reg2 & 0x30 == 0xf0 # break if '3' is written to the\n"
101 "\t # upper nibble or reg2\n"
102 "\tbreak w reg3, (reg4 > 45) # break if reg4>45 while writing to reg3\n"
103 "\tbreak c 1000000 # break on the one million'th cycle\n";
104 op = cmd_break_options;
105 }
106
107
list(guint64 value)108 void cmd_break::list(guint64 value)
109 {
110 if (value == CMDBREAK_BAD_BREAK_NUMBER) {
111 get_bp().dump();
112
113 } else if (!get_bp().dump1((unsigned int)value)) {
114 printf("break not found at given break point number %u (0x%x)\n", (unsigned int)value, (unsigned int)value);
115 }
116 }
117
118
119 const char *TOO_FEW_ARGS = "missing register or location\n";
120 const char *TOO_MANY_ARGS = "too many arguments\n";
121
122
123 //------------------------------------------------------------------------
MapBreakActions(int co_value)124 static gpsimObject::ObjectBreakTypes MapBreakActions(int co_value)
125 {
126 switch (co_value) {
127 case READ:
128 return gpsimObject::eBreakRead;
129
130 case WRITE:
131 return gpsimObject::eBreakWrite;
132
133 case REGCHANGE:
134 return gpsimObject::eBreakChange;
135
136 case EXECUTION:
137 return gpsimObject::eBreakExecute;
138 }
139
140 return gpsimObject::eBreakAny;
141 }
142
143
144 //------------------------------------------------------------------------
set_break(cmd_options * co,ExprList_t * pEL,bool bLog)145 unsigned int cmd_break::set_break(cmd_options *co, ExprList_t *pEL, bool bLog)
146 {
147 if (!co) {
148 list();
149 return MAX_BREAKPOINTS;
150 }
151
152 if (!pEL || pEL->size() > 3) {
153 // FIXME - fix this error message
154 std::cout << "ERROR: Bad expression for break command\n";
155 return MAX_BREAKPOINTS;
156 }
157
158 ExprList_itor ei = pEL->begin();
159 Expression *pFirst = *ei;
160 ++ei;
161 Expression *pSecond = (ei != pEL->end()) ? *ei++ : nullptr;
162 Expression *pThird = (ei != pEL->end()) ? *ei : nullptr;
163
164 if (verbose) {
165 std::cout << "setting breakpoint:\n";
166
167 if (pFirst) {
168 std::cout << " first expression " << pFirst->toString() << '\n';
169 }
170
171 if (pSecond) {
172 std::cout << " second expression " << pSecond->toString() << '\n';
173 }
174
175 if (pThird) {
176 std::cout << " third expression " << pThird->toString() << '\n';
177 }
178 }
179
180 LiteralString *pString = nullptr;
181 std::string m;
182
183 if (pSecond) {
184 pString = dynamic_cast<LiteralString*>(pSecond);
185
186 if (pString) {
187 String *pS = (String *)pString->evaluate();
188 m = std::string(pS->getVal());
189 delete pSecond;
190 delete pS;
191 pSecond = nullptr;
192 }
193 }
194
195 // If there is a third expression and the second expression is not
196 // a string, then try to cast the third expression into a string.
197 if (pThird && !pString) {
198 pString = dynamic_cast<LiteralString*>(pThird);
199
200 if (pString) {
201 String *pS = (String *)pString->evaluate();
202 m = std::string(pS->getVal());
203 delete pThird;
204 delete pS;
205 pThird = nullptr;
206 }
207 }
208
209 if (!pFirst) {
210 return set_break(co->value, bLog);
211 }
212
213 // See if the expression supports break points. If it does, the break points
214 // will get set and the expressions deleted.
215 int bpn = pFirst ? pFirst->set_break(MapBreakActions(co->value),
216 (bLog ? gpsimObject::eActionLog : gpsimObject::eActionHalt),
217 pSecond) : -1;
218
219 if (bpn == -1 && co->value != CYCLE)
220 GetUserInterface().DisplayMessage("break cannot be set on '%s'\n",
221 pFirst->toString().c_str());
222
223 if (bpn < 0) {
224 // We failed to set a break point from the first expression.
225 // It may be that we have a type of break point that is not supported
226 // by the expression code.
227 if (co->value == CYCLE) {
228 LiteralInteger *pLitInt = dynamic_cast<LiteralInteger*>(pFirst);
229 Integer *pInt = pLitInt ? dynamic_cast<Integer*>(pLitInt->evaluate()) : nullptr;
230 guint64 ui64Val = pInt ? (guint64)pInt->getVal() : 0;
231
232 if (pInt) {
233 bpn = get_bp().set_cycle_break(GetActiveCPU(), ui64Val);
234 }
235
236 delete pInt;
237 }
238 }
239
240 if (bpn >= 0) {
241 if (pString) {
242 get_bp().set_message(bpn, m);
243 }
244
245 if (verbose) {
246 get_bp().dump1(bpn); //RRR
247 }
248
249 if (dynamic_cast<LiteralInteger*>(pFirst)) {
250 delete pFirst;
251 }
252
253 } else {
254 delete pFirst;
255 delete pSecond;
256 }
257
258 delete pEL;
259 return bpn;
260 }
261
262
263 //------------------------------------------------------------------------
264 // set_break(cmd_options *co,
265 // Expression *pExpr1,
266 // Expression *pExpr2)
267 //
268 // Given two expressions, this function will call the set
269
set_break(cmd_options * co,Expression * pExpr1,Expression * pExpr2,bool bLog)270 unsigned int cmd_break::set_break(cmd_options *co,
271 Expression *pExpr1,
272 Expression *pExpr2,
273 bool bLog)
274 {
275 if (!co) {
276 list();
277 return MAX_BREAKPOINTS;
278 }
279
280 unsigned int bit_flag = co->value;
281
282 if (!pExpr1) {
283 return set_break(bit_flag, bLog);
284 }
285
286 // See if the expression supports break points. If it does, the break points
287 // will get set and the expressions deleted.
288 int i = pExpr1 ? pExpr1->set_break(MapBreakActions(co->value),
289 (bLog ? gpsimObject::eActionLog : gpsimObject::eActionHalt), pExpr2) : -1;
290
291 if (i >= 0) {
292 get_bp().dump1(i);
293 return i;
294 }
295
296 unsigned int b = MAX_BREAKPOINTS;
297 delete pExpr1;
298 delete pExpr2;
299 return b;
300 }
301
302
set_break(cmd_options * co,bool bLog)303 unsigned int cmd_break::set_break(cmd_options *co, bool bLog)
304 {
305 if (!co) {
306 list();
307 return MAX_BREAKPOINTS;
308 }
309
310 int bit_flag = co->value;
311 return set_break(bit_flag, bLog);
312 }
313
set_break(int bit_flag,bool)314 unsigned int cmd_break::set_break(int bit_flag, bool /* bLog */)
315 {
316 unsigned int b = MAX_BREAKPOINTS;
317
318 if (!GetActiveCPU()) {
319 return b;
320 }
321
322 switch (bit_flag) {
323 case STK_OVERFLOW:
324 b = get_bp().set_stk_overflow_break(GetActiveCPU());
325
326 if (b < MAX_BREAKPOINTS) {
327 std::cout << "break when stack over flows. " << "bp#: " << b << '\n';
328 }
329
330 break;
331
332 case STK_UNDERFLOW:
333 b = get_bp().set_stk_underflow_break(GetActiveCPU());
334
335 if (b < MAX_BREAKPOINTS) {
336 std::cout << "break when stack under flows. " << "bp#: " << b << '\n';
337 }
338
339 break;
340
341 case WDT:
342 b = get_bp().set_wdt_break(GetActiveCPU());
343
344 if (b < MAX_BREAKPOINTS) {
345 std::cout << "break when wdt times out. " << "bp#: " << b << '\n';
346 }
347
348 break;
349
350 case CYCLE:
351 get_bp().dump(Breakpoints::BREAK_ON_CYCLE);
352 break;
353
354 case EXECUTION:
355 get_bp().dump(Breakpoints::BREAK_ON_EXECUTION);
356 break;
357
358 case WRITE:
359 get_bp().dump(Breakpoints::BREAK_ON_REG_WRITE);
360 break;
361
362 case READ:
363 get_bp().dump(Breakpoints::BREAK_ON_REG_READ);
364 break;
365
366 default:
367 std::cout << TOO_FEW_ARGS;
368 break;
369 }
370
371 return b;
372 }
373
374
375 // attribute breakpoints
set_break(gpsimObject * v)376 unsigned int cmd_break::set_break(gpsimObject *v)
377 {
378 if (v) {
379 v->set_break();
380 }
381
382 return MAX_BREAKPOINTS;
383 }
384