Allow immediate saving at point of save
[lsnes.git] / src / core / memorywatch.cpp
blob44785a11c50a9e5e09b42e41427997ed61c8ab79
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");
83 int64_t as_integer() const
85 switch(t) {
86 case VT_SIGNED: return s;
87 case VT_UNSIGNED: return u;
88 case VT_FLOAT: return f;
89 case VT_NAN: throw std::runtime_error("#NAN");
92 double as_double() const
94 switch(t) {
95 case VT_SIGNED: return s;
96 case VT_UNSIGNED: return u;
97 case VT_FLOAT: return f;
98 case VT_NAN: throw std::runtime_error("#NAN");
101 std::string str() const
103 uint64_t wmasks[] = {
104 0x0000000000000000ULL, 0x000000000000000FULL, 0x00000000000000FFULL,
105 0x0000000000000FFFULL, 0x000000000000FFFFULL, 0x00000000000FFFFFULL,
106 0x0000000000FFFFFFULL, 0x000000000FFFFFFFULL, 0x00000000FFFFFFFFULL,
107 0x0000000FFFFFFFFFULL, 0x000000FFFFFFFFFFULL, 0x00000FFFFFFFFFFFULL,
108 0x0000FFFFFFFFFFFFULL, 0x000FFFFFFFFFFFFFULL, 0x00FFFFFFFFFFFFFFULL,
109 0x0FFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL
111 std::ostringstream x;
112 if(hex && (t == VT_SIGNED || t == VT_UNSIGNED)) {
113 uint64_t wmask = wmasks[hex];
114 uint64_t w = (t == VT_SIGNED) ? s : u;
115 x << std::hex << std::setw(hex) << std::setfill('0') << (w & wmask);
116 return x.str();
118 switch(t) {
119 case VT_SIGNED: x << s; break;
120 case VT_UNSIGNED: x << u; break;
121 case VT_FLOAT: x << f; break;
122 case VT_NAN: x << "#NAN"; break;
124 return x.str();
126 numeric_type round(int prec) const
128 double b = 0, c = 0;
129 switch(t) {
130 case VT_FLOAT:
131 b = pow(10, prec);
132 c = floor(b * f + 0.5) / b;
133 return numeric_type(c);
134 default:
135 return *this;
138 numeric_type operator+(const numeric_type& b) const
140 if(t == VT_NAN || b.t == VT_NAN)
141 return numeric_type();
142 else if(t == VT_FLOAT || b.t == VT_FLOAT)
143 return numeric_type(as_double() + b.as_double());
144 else if(t == VT_SIGNED || b.t == VT_SIGNED)
145 return numeric_type(as_integer() + b.as_integer());
146 else
147 return numeric_type(as_address() + b.as_address());
149 numeric_type operator-(const numeric_type& b) const
151 if(t == VT_NAN || b.t == VT_NAN)
152 return numeric_type();
153 else if(t == VT_FLOAT || b.t == VT_FLOAT)
154 return numeric_type(as_double() - b.as_double());
155 else if(t == VT_SIGNED || b.t == VT_SIGNED)
156 return numeric_type(as_integer() - b.as_integer());
157 else
158 return numeric_type(as_address() - b.as_address());
160 numeric_type operator*(const numeric_type& b) const
162 if(t == VT_NAN || b.t == VT_NAN)
163 return numeric_type();
164 else if(t == VT_FLOAT || b.t == VT_FLOAT)
165 return numeric_type(as_double() * b.as_double());
166 else if(t == VT_SIGNED || b.t == VT_SIGNED)
167 return numeric_type(as_integer() * b.as_integer());
168 else
169 return numeric_type(as_address() * b.as_address());
171 numeric_type operator/(const numeric_type& b) const
173 if(b.t != VT_NAN && fabs(b.as_double()) < 1e-30)
174 throw std::runtime_error("#DIV-BY-0");
175 if(t == VT_NAN || b.t == VT_NAN)
176 return numeric_type();
177 else
178 return numeric_type(as_double() / b.as_double());
180 numeric_type operator%(const numeric_type& b) const
182 return numeric_type(*this - b * idiv(b));
184 numeric_type idiv(const numeric_type& b) const
186 if(b.t != VT_NAN && fabs(b.as_double()) < 1e-30)
187 throw std::runtime_error("#DIV-BY-0");
188 if(t == VT_NAN || b.t == VT_NAN)
189 return numeric_type();
190 else if(t == VT_FLOAT || b.t == VT_FLOAT)
191 return numeric_type(floor(as_double() / b.as_double()));
192 else if(t == VT_SIGNED || b.t == VT_SIGNED)
193 return numeric_type(as_integer() / b.as_integer());
194 else
195 return numeric_type(as_address() / b.as_address());
197 void sethex(char ch)
199 if(ch >= '0' && ch <= '9')
200 hex = (ch - '0');
201 if(ch >= 'A' && ch <= 'G')
202 hex = (ch - 'A') + 10;
203 if(ch >= 'a' && ch <= 'g')
204 hex = (ch - 'a') + 10;
206 private:
207 enum value_type
209 VT_SIGNED,
210 VT_UNSIGNED,
211 VT_FLOAT,
212 VT_NAN
213 } t;
214 int64_t s;
215 uint64_t u;
216 double f;
217 unsigned hex;
220 numeric_type stack_pop(std::stack<numeric_type>& s, bool norm = false)
222 if(s.size() < 1)
223 throw std::runtime_error("#syntax (underflow)");
224 numeric_type r = s.top();
225 if(!norm)
226 s.pop();
227 return r;
230 template<typename T> void stack_push(std::stack<numeric_type>& s, T val)
232 s.push(numeric_type(val));
236 std::string evaluate_watch(const std::string& expr) throw(std::bad_alloc)
238 std::stack<numeric_type> s;
239 size_t y;
240 std::string _expr = expr;
241 std::string t;
242 numeric_type a;
243 numeric_type b;
244 int d;
245 try {
246 for(size_t i = 0; i < expr.length(); i++) {
247 numeric_type r;
248 switch(expr[i]) {
249 case 'C':
250 y = expr.find_first_of("z", i);
251 if(y > expr.length())
252 return "#syntax (noterm)";
253 t = _expr.substr(i + 1, y - i - 1);
254 stack_push(s, numeric_type(t));
255 i = y;
256 break;
257 case 'R':
258 if(i + 1 == expr.length())
259 throw std::runtime_error("#syntax (noparam)");
260 a = stack_pop(s);
261 d = expr[++i] - '0';
262 stack_push(s, a.round(d));
263 break;
264 case 'H':
265 if(i + 1 == expr.length())
266 throw std::runtime_error("#syntax (noparam)");
267 s.top().sethex(expr[++i]);
268 break;
269 case 'a':
270 stack_push<double>(s, atan(stack_pop(s).as_double()));
271 break;
272 case 'A':
273 a = stack_pop(s);
274 b = stack_pop(s);
275 stack_push<double>(s, atan2(a.as_double(), b.as_double()));
276 break;
277 case 'c':
278 stack_push<double>(s, cos(stack_pop(s).as_double()));
279 break;
280 case 'r':
281 a = stack_pop(s);
282 if(a.as_double() < 0)
283 throw std::runtime_error("#NAN");
284 stack_push<double>(s, sqrt(a.as_double()));
285 break;
286 case 's':
287 stack_push<double>(s, sin(stack_pop(s).as_double()));
288 break;
289 case 't':
290 stack_push<double>(s, tan(stack_pop(s).as_double()));
291 break;
292 case 'u':
293 stack_push(s, stack_pop(s, true));
294 break;
295 case 'p':
296 stack_push(s, 4 * atan(1));
297 break;
298 case '+':
299 a = stack_pop(s);
300 b = stack_pop(s);
301 stack_push(s, a + b);
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 'i':
314 a = stack_pop(s);
315 b = stack_pop(s);
316 stack_push(s, a.idiv(b));
317 break;
318 case '/':
319 a = stack_pop(s);
320 b = stack_pop(s);
321 stack_push(s, a / 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 'b':
329 stack_push<int8_t>(s, memory_read_byte(stack_pop(s).as_address()));
330 break;
331 case 'B':
332 stack_push<uint8_t>(s, memory_read_byte(stack_pop(s).as_address()));
333 break;
334 case 'w':
335 stack_push<int16_t>(s, memory_read_word(stack_pop(s).as_address()));
336 break;
337 case 'W':
338 stack_push<uint16_t>(s, memory_read_word(stack_pop(s).as_address()));
339 break;
340 case 'd':
341 stack_push<int32_t>(s, memory_read_dword(stack_pop(s).as_address()));
342 break;
343 case 'D':
344 stack_push<uint32_t>(s, memory_read_dword(stack_pop(s).as_address()));
345 break;
346 case 'q':
347 stack_push<int64_t>(s, memory_read_qword(stack_pop(s).as_address()));
348 break;
349 case 'Q':
350 stack_push<uint64_t>(s, memory_read_qword(stack_pop(s).as_address()));
351 break;
352 default:
353 throw std::runtime_error("#syntax (illchar)");
356 if(s.empty())
357 return "#ERR";
358 else
359 return s.top().str();
360 } catch(std::exception& e) {
361 return e.what();
365 std::set<std::string> get_watches() throw(std::bad_alloc)
367 std::set<std::string> r;
368 for(auto i : watches)
369 r.insert(i.first);
370 return r;
373 std::string get_watchexpr_for(const std::string& w) throw(std::bad_alloc)
375 if(watches.count(w))
376 return watches[w];
377 else
378 return "";
381 void set_watchexpr_for(const std::string& w, const std::string& expr) throw(std::bad_alloc)
383 auto& status = platform::get_emustatus();
384 if(expr != "") {
385 watches[w] = expr;
386 status.set("M[" + w + "]", evaluate_watch(expr));
387 } else {
388 watches.erase(w);
389 status.erase("M[" + w + "]");
391 information_dispatch::do_status_update();
394 void do_watch_memory()
396 auto& status = platform::get_emustatus();
397 for(auto i : watches)
398 status.set("M[" + i.first + "]", evaluate_watch(i.second));
401 namespace
403 function_ptr_command<const std::string&> add_watch("add-watch", "Add a memory watch",
404 "Syntax: add-watch <name> <expression>\nAdds a new memory watch\n",
405 [](const std::string& t) throw(std::bad_alloc, std::runtime_error) {
406 auto r = regex("([^ \t]+)[ \t]+(|[^ \t].*)", t, "Name and expression required.");
407 set_watchexpr_for(r[1], r[2]);
410 function_ptr_command<const std::string&> remove_watch("remove-watch", "Remove a memory watch",
411 "Syntax: remove-watch <name>\nRemoves a memory watch\n",
412 [](const std::string& t) throw(std::bad_alloc, std::runtime_error) {
413 auto r = regex("([^ \t]+)[ \t]*", t, "Name required.");
414 set_watchexpr_for(r[1], "");