Merge branch 'rr1-maint'
[lsnes.git] / src / core / memorywatch.cpp
blob347277bd1d1d229d5bbc80ecfb186f7d73d33c3a
1 #include "core/command.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/memorymanip.hpp"
4 #include "core/memorywatch.hpp"
5 #include "core/window.hpp"
6 #include <library/string.hpp>
8 #include <cstdio>
9 #include <cstdlib>
10 #include <list>
11 #include <iomanip>
12 #include <stack>
13 #include <cmath>
14 #include <sstream>
15 #include <map>
17 namespace
19 std::map<std::string, std::string> watches;
21 struct numeric_type
23 numeric_type() { t = VT_NAN; hex = 0; }
24 numeric_type(int8_t x) { t = VT_SIGNED; s = x; hex = 0; }
25 numeric_type(uint8_t x) { t = VT_UNSIGNED; u = x; hex = 0; }
26 numeric_type(int16_t x) { t = VT_SIGNED; s = x; hex = 0; }
27 numeric_type(uint16_t x) { t = VT_UNSIGNED; u = x; hex = 0; }
28 numeric_type(int32_t x) { t = VT_SIGNED; s = x; hex = 0; }
29 numeric_type(uint32_t x) { t = VT_UNSIGNED; u = x; hex = 0; }
30 numeric_type(int64_t x) { t = VT_SIGNED; s = x; hex = 0; }
31 numeric_type(uint64_t x) { t = VT_UNSIGNED; u = x; hex = 0; }
32 numeric_type(double x) { t = VT_FLOAT; f = x; }
33 numeric_type(const std::string& _s)
35 char* end;
36 if(_s.length() > 2 && _s[0] == '0' && _s[1] == 'x') {
37 t = VT_UNSIGNED;
38 u = strtoull(_s.c_str() + 2, &end, 16);
39 if(*end)
40 throw std::runtime_error("#syntax (badval)");
41 } else if(_s.length() > 3 && _s[0] == '+' && _s[1] == '0' && _s[2] == 'x') {
42 t = VT_SIGNED;
43 s = (int64_t)strtoull(_s.c_str() + 3, &end, 16);
44 if(*end)
45 throw std::runtime_error("#syntax (badval)");
46 } else if(_s.length() > 3 && _s[0] == '-' && _s[1] == '0' && _s[2] == 'x') {
47 t = VT_SIGNED;
48 s = -(int64_t)strtoull(_s.c_str() + 3, &end, 16);
49 if(*end)
50 throw std::runtime_error("#syntax (badval)");
51 } else if(_s.find_first_of(".") < _s.length()) {
52 t = VT_FLOAT;
53 f = strtod(_s.c_str(), &end);
54 if(*end)
55 throw std::runtime_error("#syntax (badval)");
56 } else if(_s.length() > 1 && _s[0] == '+') {
57 t = VT_SIGNED;
58 s = (int64_t)strtoull(_s.c_str() + 1, &end, 10);
59 if(*end)
60 throw std::runtime_error("#syntax (badval)");
61 } else if(_s.length() > 1 && _s[0] == '-') {
62 t = VT_SIGNED;
63 s = -(int64_t)strtoull(_s.c_str() + 1, &end, 10);
64 if(*end)
65 throw std::runtime_error("#syntax (badval)");
66 } else {
67 t = VT_UNSIGNED;
68 u = strtoull(_s.c_str(), &end, 10);
69 if(*end)
70 throw std::runtime_error("#syntax (badval)");
72 hex = 0;
74 uint64_t as_address() const
76 switch(t) {
77 case VT_SIGNED: return s;
78 case VT_UNSIGNED: return u;
79 case VT_FLOAT: return f;
80 case VT_NAN: throw std::runtime_error("#NAN");
82 return 0;
84 int64_t as_integer() const
86 switch(t) {
87 case VT_SIGNED: return s;
88 case VT_UNSIGNED: return u;
89 case VT_FLOAT: return f;
90 case VT_NAN: throw std::runtime_error("#NAN");
92 return 0;
94 double as_double() const
96 switch(t) {
97 case VT_SIGNED: return s;
98 case VT_UNSIGNED: return u;
99 case VT_FLOAT: return f;
100 case VT_NAN: throw std::runtime_error("#NAN");
102 return 0;
104 std::string str() const
106 uint64_t wmasks[] = {
107 0x0000000000000000ULL, 0x000000000000000FULL, 0x00000000000000FFULL,
108 0x0000000000000FFFULL, 0x000000000000FFFFULL, 0x00000000000FFFFFULL,
109 0x0000000000FFFFFFULL, 0x000000000FFFFFFFULL, 0x00000000FFFFFFFFULL,
110 0x0000000FFFFFFFFFULL, 0x000000FFFFFFFFFFULL, 0x00000FFFFFFFFFFFULL,
111 0x0000FFFFFFFFFFFFULL, 0x000FFFFFFFFFFFFFULL, 0x00FFFFFFFFFFFFFFULL,
112 0x0FFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL
114 std::ostringstream x;
115 if(hex && (t == VT_SIGNED || t == VT_UNSIGNED)) {
116 uint64_t wmask = wmasks[hex];
117 uint64_t w = (t == VT_SIGNED) ? s : u;
118 x << std::hex << std::setw(hex) << std::setfill('0') << (w & wmask);
119 return x.str();
121 switch(t) {
122 case VT_SIGNED: x << s; break;
123 case VT_UNSIGNED: x << u; break;
124 case VT_FLOAT: x << f; break;
125 case VT_NAN: x << "#NAN"; break;
127 return x.str();
129 numeric_type round(int prec) const
131 double b = 0, c = 0;
132 switch(t) {
133 case VT_FLOAT:
134 b = pow(10, prec);
135 c = floor(b * f + 0.5) / b;
136 return numeric_type(c);
137 default:
138 return *this;
141 numeric_type operator+(const numeric_type& b) const
143 if(t == VT_NAN || b.t == VT_NAN)
144 return numeric_type();
145 else if(t == VT_FLOAT || b.t == VT_FLOAT)
146 return numeric_type(as_double() + b.as_double());
147 else if(t == VT_SIGNED || b.t == VT_SIGNED)
148 return numeric_type(as_integer() + b.as_integer());
149 else
150 return numeric_type(as_address() + b.as_address());
152 numeric_type operator-(const numeric_type& b) const
154 if(t == VT_NAN || b.t == VT_NAN)
155 return numeric_type();
156 else if(t == VT_FLOAT || b.t == VT_FLOAT)
157 return numeric_type(as_double() - b.as_double());
158 else if(t == VT_SIGNED || b.t == VT_SIGNED)
159 return numeric_type(as_integer() - b.as_integer());
160 else
161 return numeric_type(as_address() - b.as_address());
163 numeric_type operator*(const numeric_type& b) const
165 if(t == VT_NAN || b.t == VT_NAN)
166 return numeric_type();
167 else if(t == VT_FLOAT || b.t == VT_FLOAT)
168 return numeric_type(as_double() * b.as_double());
169 else if(t == VT_SIGNED || b.t == VT_SIGNED)
170 return numeric_type(as_integer() * b.as_integer());
171 else
172 return numeric_type(as_address() * b.as_address());
174 numeric_type operator/(const numeric_type& b) const
176 if(b.t != VT_NAN && fabs(b.as_double()) < 1e-30)
177 throw std::runtime_error("#DIV-BY-0");
178 if(t == VT_NAN || b.t == VT_NAN)
179 return numeric_type();
180 else
181 return numeric_type(as_double() / b.as_double());
183 numeric_type operator%(const numeric_type& b) const
185 return numeric_type(*this - b * idiv(b));
187 numeric_type idiv(const numeric_type& b) const
189 if(b.t != VT_NAN && fabs(b.as_double()) < 1e-30)
190 throw std::runtime_error("#DIV-BY-0");
191 if(t == VT_NAN || b.t == VT_NAN)
192 return numeric_type();
193 else if(t == VT_FLOAT || b.t == VT_FLOAT)
194 return numeric_type(floor(as_double() / b.as_double()));
195 else if(t == VT_SIGNED || b.t == VT_SIGNED)
196 return numeric_type(as_integer() / b.as_integer());
197 else
198 return numeric_type(as_address() / b.as_address());
200 void sethex(char ch)
202 if(ch >= '0' && ch <= '9')
203 hex = (ch - '0');
204 if(ch >= 'A' && ch <= 'G')
205 hex = (ch - 'A') + 10;
206 if(ch >= 'a' && ch <= 'g')
207 hex = (ch - 'a') + 10;
209 private:
210 enum value_type
212 VT_SIGNED,
213 VT_UNSIGNED,
214 VT_FLOAT,
215 VT_NAN
216 } t;
217 int64_t s;
218 uint64_t u;
219 double f;
220 unsigned hex;
223 numeric_type stack_pop(std::stack<numeric_type>& s, bool norm = false)
225 if(s.size() < 1)
226 throw std::runtime_error("#syntax (underflow)");
227 numeric_type r = s.top();
228 if(!norm)
229 s.pop();
230 return r;
233 template<typename T> void stack_push(std::stack<numeric_type>& s, T val)
235 s.push(numeric_type(val));
239 std::string evaluate_watch(const std::string& expr) throw(std::bad_alloc)
241 std::stack<numeric_type> s;
242 size_t y;
243 std::string _expr = expr;
244 std::string t;
245 numeric_type a;
246 numeric_type b;
247 int d;
248 try {
249 for(size_t i = 0; i < expr.length(); i++) {
250 numeric_type r;
251 switch(expr[i]) {
252 case 'C':
253 y = expr.find_first_of("z", i);
254 if(y > expr.length())
255 return "#syntax (noterm)";
256 t = _expr.substr(i + 1, y - i - 1);
257 stack_push(s, numeric_type(t));
258 i = y;
259 break;
260 case 'R':
261 if(i + 1 == expr.length())
262 throw std::runtime_error("#syntax (noparam)");
263 a = stack_pop(s);
264 d = expr[++i] - '0';
265 stack_push(s, a.round(d));
266 break;
267 case 'H':
268 if(i + 1 == expr.length())
269 throw std::runtime_error("#syntax (noparam)");
270 s.top().sethex(expr[++i]);
271 break;
272 case 'a':
273 stack_push<double>(s, atan(stack_pop(s).as_double()));
274 break;
275 case 'A':
276 a = stack_pop(s);
277 b = stack_pop(s);
278 stack_push<double>(s, atan2(a.as_double(), b.as_double()));
279 break;
280 case 'c':
281 stack_push<double>(s, cos(stack_pop(s).as_double()));
282 break;
283 case 'r':
284 a = stack_pop(s);
285 if(a.as_double() < 0)
286 throw std::runtime_error("#NAN");
287 stack_push<double>(s, sqrt(a.as_double()));
288 break;
289 case 's':
290 stack_push<double>(s, sin(stack_pop(s).as_double()));
291 break;
292 case 't':
293 stack_push<double>(s, tan(stack_pop(s).as_double()));
294 break;
295 case 'u':
296 stack_push(s, stack_pop(s, true));
297 break;
298 case 'p':
299 stack_push(s, 4 * atan(1));
300 break;
301 case '+':
302 a = stack_pop(s);
303 b = stack_pop(s);
304 stack_push(s, a + b);
305 break;
306 case '-':
307 a = stack_pop(s);
308 b = stack_pop(s);
309 stack_push(s, a - b);
310 break;
311 case '*':
312 a = stack_pop(s);
313 b = stack_pop(s);
314 stack_push(s, a * b);
315 break;
316 case 'i':
317 a = stack_pop(s);
318 b = stack_pop(s);
319 stack_push(s, a.idiv(b));
320 break;
321 case '/':
322 a = stack_pop(s);
323 b = stack_pop(s);
324 stack_push(s, a / b);
325 break;
326 case '%':
327 a = stack_pop(s);
328 b = stack_pop(s);
329 stack_push(s, a % b);
330 break;
331 case 'b':
332 stack_push<int8_t>(s, lsnes_memory.read<uint8_t>(stack_pop(s).as_address()));
333 break;
334 case 'B':
335 stack_push<uint8_t>(s, lsnes_memory.read<uint8_t>(stack_pop(s).as_address()));
336 break;
337 case 'w':
338 stack_push<int16_t>(s, lsnes_memory.read<uint16_t>(stack_pop(s).as_address()));
339 break;
340 case 'W':
341 stack_push<uint16_t>(s, lsnes_memory.read<uint16_t>(stack_pop(s).as_address()));
342 break;
343 case 'd':
344 stack_push<int32_t>(s, lsnes_memory.read<uint32_t>(stack_pop(s).as_address()));
345 break;
346 case 'D':
347 stack_push<uint32_t>(s, lsnes_memory.read<uint32_t>(stack_pop(s).as_address()));
348 break;
349 case 'q':
350 stack_push<int64_t>(s, lsnes_memory.read<uint64_t>(stack_pop(s).as_address()));
351 break;
352 case 'Q':
353 stack_push<uint64_t>(s, lsnes_memory.read<uint64_t>(stack_pop(s).as_address()));
354 break;
355 default:
356 throw std::runtime_error("#syntax (illchar)");
359 if(s.empty())
360 return "#ERR";
361 else
362 return s.top().str();
363 } catch(std::exception& e) {
364 return e.what();
368 std::set<std::string> get_watches() throw(std::bad_alloc)
370 std::set<std::string> r;
371 for(auto i : watches)
372 r.insert(i.first);
373 return r;
376 std::string get_watchexpr_for(const std::string& w) throw(std::bad_alloc)
378 if(watches.count(w))
379 return watches[w];
380 else
381 return "";
384 void set_watchexpr_for(const std::string& w, const std::string& expr) throw(std::bad_alloc)
386 auto& status = platform::get_emustatus();
387 if(expr != "") {
388 watches[w] = expr;
389 status.set("M[" + w + "]", evaluate_watch(expr));
390 } else {
391 watches.erase(w);
392 status.erase("M[" + w + "]");
394 information_dispatch::do_status_update();
397 void do_watch_memory()
399 auto& status = platform::get_emustatus();
400 for(auto i : watches)
401 status.set("M[" + i.first + "]", evaluate_watch(i.second));
404 namespace
406 function_ptr_command<const std::string&> add_watch(lsnes_cmd, "add-watch", "Add a memory watch",
407 "Syntax: add-watch <name> <expression>\nAdds a new memory watch\n",
408 [](const std::string& t) throw(std::bad_alloc, std::runtime_error) {
409 auto r = regex("([^ \t]+)[ \t]+(|[^ \t].*)", t, "Name and expression required.");
410 set_watchexpr_for(r[1], r[2]);
413 function_ptr_command<const std::string&> remove_watch(lsnes_cmd, "remove-watch", "Remove a memory watch",
414 "Syntax: remove-watch <name>\nRemoves a memory watch\n",
415 [](const std::string& t) throw(std::bad_alloc, std::runtime_error) {
416 auto r = regex("([^ \t]+)[ \t]*", t, "Name required.");
417 set_watchexpr_for(r[1], "");