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