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