Lua: Fix type confusion between signed and unsigned
[lsnes.git] / src / core / memorywatch.cpp
blob2de0a404e58fee1ac04cecab80436dc3117ba949
1 #include "core/command.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/framebuffer.hpp"
4 #include "core/instance.hpp"
5 #include "core/memorywatch.hpp"
6 #include "core/messages.hpp"
7 #include "core/project.hpp"
8 #include "core/rom.hpp"
9 #include "core/window.hpp"
10 #include "fonts/wrapper.hpp"
11 #include "library/directory.hpp"
12 #include "library/framebuffer-font2.hpp"
13 #include "library/globalwrap.hpp"
14 #include "library/int24.hpp"
15 #include "library/mathexpr-ntype.hpp"
16 #include "library/memoryspace.hpp"
17 #include "library/memorywatch-fb.hpp"
18 #include "library/memorywatch.hpp"
19 #include "library/memorywatch-list.hpp"
20 #include "library/memorywatch-null.hpp"
21 #include "library/string.hpp"
23 #include <cstdio>
24 #include <cstdlib>
25 #include <list>
26 #include <iomanip>
27 #include <stack>
28 #include <cmath>
29 #include <sstream>
30 #include <map>
32 namespace
34 globalwrap<std::map<std::string, std::pair<framebuffer::font2*, size_t>>> S_fonts_in_use;
36 framebuffer::font2& get_builtin_font2()
38 static framebuffer::font2 f(main_font);
39 return f;
42 framebuffer::font2* get_font(const std::string filename)
44 //Handle NULL font.
45 if(filename == "")
46 return &get_builtin_font2();
47 std::string abs_filename = directory::absolute_path(filename);
48 if(S_fonts_in_use().count(abs_filename)) {
49 S_fonts_in_use()[abs_filename].second++;
50 return S_fonts_in_use()[abs_filename].first;
52 framebuffer::font2* f = new framebuffer::font2(abs_filename);
53 try {
54 S_fonts_in_use()[abs_filename] = std::make_pair(f, 1);
55 } catch(...) {
56 delete f;
57 throw;
59 return f;
61 void put_font(framebuffer::font2* font)
63 //Handle NULL font (always there).
64 if(!font)
65 return;
66 //Find font using this.
67 std::string filename;
68 for(auto& i : S_fonts_in_use())
69 if(i.second.first == font)
70 filename = i.first;
71 if(filename == "")
72 return;
73 S_fonts_in_use()[filename].second--;
74 if(!S_fonts_in_use()[filename].second) {
75 delete S_fonts_in_use()[filename].first;
76 S_fonts_in_use().erase(filename);
80 std::string json_string_default(const JSON::node& node, const std::string& pointer, const std::string& dflt)
82 return (node.type_of(pointer) == JSON::string) ? node[pointer].as_string8() : dflt;
85 uint64_t json_unsigned_default(const JSON::node& node, const std::string& pointer, uint64_t dflt)
87 return (node.type_of(pointer) == JSON::number) ? node[pointer].as_uint() : dflt;
90 int64_t json_signed_default(const JSON::node& node, const std::string& pointer, int64_t dflt)
92 return (node.type_of(pointer) == JSON::number) ? node[pointer].as_int() : dflt;
95 bool json_boolean_default(const JSON::node& node, const std::string& pointer, bool dflt)
97 return (node.type_of(pointer) == JSON::boolean) ? node[pointer].as_bool() : dflt;
100 void dummy_target_fn(const std::string& n, const std::string& v) {}
103 struct regread_oper : public mathexpr::operinfo
105 regread_oper();
106 ~regread_oper();
107 //The first promise is the register name.
108 void evaluate(mathexpr::value target, std::vector<std::function<mathexpr::value()>> promises);
109 //Fields.
110 bool signed_flag;
111 loaded_rom* rom;
114 regread_oper::regread_oper()
115 : operinfo("(readregister)")
117 signed_flag = false;
118 rom = NULL;
120 regread_oper::~regread_oper()
123 void regread_oper::evaluate(mathexpr::value target, std::vector<std::function<mathexpr::value()>> promises)
125 if(promises.size() != 1)
126 throw mathexpr::error(mathexpr::error::ARGCOUNT, "register read operator takes 1 argument");
127 std::string rname;
128 try {
129 mathexpr::value val = promises[0]();
130 void* res = val._value;
131 rname = val.type->tostring(res);
132 } catch(std::exception& e) {
133 throw mathexpr::error(mathexpr::error::ADDR, e.what());
135 const interface_device_reg* regs = rom->get_registers();
136 bool found = false;
137 for(size_t i = 0; regs && regs[i].name; i++) {
138 if(rname != regs[i].name)
139 continue;
140 found = true;
141 if(regs[i].boolean) {
142 bool v = (regs[i].read() != 0);
143 target.type->parse_b(target._value, v);
144 } else if(signed_flag) {
145 int64_t v = regs[i].read();
146 target.type->parse_s(target._value, v);
147 } else {
148 uint64_t v = regs[i].read();
149 target.type->parse_u(target._value, v);
151 return;
153 if(!found) {
154 //N/A value.
155 throw mathexpr::error(mathexpr::error::ADDR, "No such register");
160 memwatch_printer::memwatch_printer()
162 position = PC_MEMORYWATCH;
163 cond_enable = false;
164 onscreen_alt_origin_x = false;
165 onscreen_alt_origin_y = false;
166 onscreen_cliprange_x = false;
167 onscreen_cliprange_y = false;
168 onscreen_fg_color = 0xFFFFFF;
169 onscreen_bg_color = -1;
170 onscreen_halo_color = 0;
173 JSON::node memwatch_printer::serialize()
175 JSON::node ndata(JSON::object);
176 switch(position) {
177 case PC_DISABLED: ndata["position"] = JSON::s("disabled"); break;
178 case PC_MEMORYWATCH: ndata["position"] = JSON::s("memorywatch"); break;
179 case PC_ONSCREEN: ndata["position"] = JSON::s("onscreen"); break;
181 ndata["cond_enable"] = JSON::b(cond_enable);
182 ndata["enabled"] = JSON::s(enabled);
183 ndata["onscreen_xpos"] = JSON::s(onscreen_xpos);
184 ndata["onscreen_ypos"] = JSON::s(onscreen_ypos);
185 ndata["onscreen_alt_origin_x"] = JSON::b(onscreen_alt_origin_x);
186 ndata["onscreen_alt_origin_y"] = JSON::b(onscreen_alt_origin_y);
187 ndata["onscreen_cliprange_x"] = JSON::b(onscreen_cliprange_x);
188 ndata["onscreen_cliprange_y"] = JSON::b(onscreen_cliprange_y);
189 ndata["onscreen_font"] = JSON::s(onscreen_font);
190 ndata["onscreen_fg_color"] = JSON::i(onscreen_fg_color);
191 ndata["onscreen_bg_color"] = JSON::i(onscreen_bg_color);
192 ndata["onscreen_halo_color"] = JSON::i(onscreen_halo_color);
193 return ndata;
196 void memwatch_printer::unserialize(const JSON::node& node)
198 std::string _position = json_string_default(node, "position", "");
199 if(_position == "disabled") position = PC_DISABLED;
200 else if(_position == "memorywatch") position = PC_MEMORYWATCH;
201 else if(_position == "onscreen") position = PC_ONSCREEN;
202 else position = PC_MEMORYWATCH;
203 cond_enable = json_boolean_default(node, "cond_enable", false);
204 enabled = json_string_default(node, "enabled", "");
205 onscreen_xpos = json_string_default(node, "onscreen_xpos", "");
206 onscreen_ypos = json_string_default(node, "onscreen_ypos", "");
207 onscreen_alt_origin_x = json_boolean_default(node, "onscreen_alt_origin_x", false);
208 onscreen_alt_origin_y = json_boolean_default(node, "onscreen_alt_origin_y", false);
209 onscreen_cliprange_x = json_boolean_default(node, "onscreen_cliprange_x", false);
210 onscreen_cliprange_y = json_boolean_default(node, "onscreen_cliprange_y", false);
211 onscreen_font = json_string_default(node, "onscreen_font", "");
212 onscreen_fg_color = json_signed_default(node, "onscreen_fg_color", false);
213 onscreen_bg_color = json_signed_default(node, "onscreen_bg_color", false);
214 onscreen_halo_color = json_signed_default(node, "onscreen_halo_color", false);
217 GC::pointer<memorywatch::item_printer> memwatch_printer::get_printer_obj(
218 std::function<GC::pointer<mathexpr::mathexpr>(const std::string& n)> vars)
220 GC::pointer<memorywatch::item_printer> ptr;
221 memorywatch::output_list* l;
222 memorywatch::output_fb* f;
224 std::string _enabled = (enabled != "") ? enabled : "true";
226 switch(position) {
227 case PC_DISABLED:
228 ptr = GC::pointer<memorywatch::item_printer>(new memorywatch::output_null);
229 break;
230 case PC_MEMORYWATCH:
231 ptr = GC::pointer<memorywatch::item_printer>(new memorywatch::output_list);
232 l = dynamic_cast<memorywatch::output_list*>(ptr.as_pointer());
233 l->cond_enable = cond_enable;
234 try {
235 if(l->cond_enable)
236 l->enabled = mathexpr::mathexpr::parse(*mathexpr::expression_value(), _enabled, vars);
237 else
238 l->enabled = mathexpr::mathexpr::parse(*mathexpr::expression_value(), "true", vars);
239 } catch(std::exception& e) {
240 (stringfmt() << "Error while parsing conditional: " << e.what()).throwex();
242 l->set_output(dummy_target_fn);
243 break;
244 case PC_ONSCREEN:
245 ptr = GC::pointer<memorywatch::item_printer>(new memorywatch::output_fb);
246 f = dynamic_cast<memorywatch::output_fb*>(ptr.as_pointer());
247 f->font = NULL;
248 f->set_dtor_cb([](memorywatch::output_fb& obj) { put_font(obj.font); });
249 f->cond_enable = cond_enable;
250 std::string while_parsing = "(unknown)";
251 try {
252 while_parsing = "conditional";
253 if(f->cond_enable)
254 f->enabled = mathexpr::mathexpr::parse(*mathexpr::expression_value(), _enabled, vars);
255 else
256 f->enabled = mathexpr::mathexpr::parse(*mathexpr::expression_value(), "true", vars);
257 while_parsing = "X position";
258 f->pos_x = mathexpr::mathexpr::parse(*mathexpr::expression_value(), onscreen_xpos, vars);
259 while_parsing = "Y position";
260 f->pos_y = mathexpr::mathexpr::parse(*mathexpr::expression_value(), onscreen_ypos, vars);
261 } catch(std::exception& e) {
262 (stringfmt() << "Error while parsing " << while_parsing << ": " << e.what()).throwex();
264 f->alt_origin_x = onscreen_alt_origin_x;
265 f->alt_origin_y = onscreen_alt_origin_y;
266 f->cliprange_x = onscreen_cliprange_x;
267 f->cliprange_y = onscreen_cliprange_y;
268 f->fg = onscreen_fg_color;
269 f->bg = onscreen_bg_color;
270 f->halo = onscreen_halo_color;
271 try {
272 f->font = get_font(onscreen_font);
273 } catch(std::exception& e) {
274 messages << "Bad font '" << onscreen_font << "': " << e.what() << std::endl;
275 f->font = &get_builtin_font2();
277 break;
279 return ptr;
282 memwatch_item::memwatch_item()
284 bytes = 0;
285 signed_flag = false;
286 float_flag = false;
287 endianess = 0;
288 scale_div = 1;
289 addr_base = 0;
290 addr_size = 0;
293 JSON::node memwatch_item::serialize()
295 JSON::node ndata(JSON::object);
296 ndata["printer"] = printer.serialize();
297 ndata["expr"] = JSON::s(expr);
298 ndata["format"] = JSON::s(format);
299 ndata["bytes"] = JSON::u(bytes);
300 ndata["signed"] = JSON::b(signed_flag);
301 ndata["float"] = JSON::b(float_flag);
302 ndata["endianess"] = JSON::i(endianess);
303 ndata["scale_div"] = JSON::u(scale_div);
304 ndata["addr_base"] = JSON::u(addr_base);
305 ndata["addr_size"] = JSON::u(addr_size);
306 return ndata;
309 void memwatch_item::unserialize(const JSON::node& node)
311 if(node.type_of("printer") == JSON::object)
312 printer.unserialize(node["printer"]);
313 else
314 printer = memwatch_printer();
315 expr = json_string_default(node, "expr", "0");
316 format = json_string_default(node, "format", "");
317 bytes = json_unsigned_default(node, "bytes", 0);
318 signed_flag = json_boolean_default(node, "signed", false);
319 float_flag = json_boolean_default(node, "float", false);
320 endianess = json_signed_default(node, "endianess", false);
321 scale_div = json_unsigned_default(node, "scale_div", 1);
322 addr_base = json_unsigned_default(node, "addr_base", 0);
323 addr_size = json_unsigned_default(node, "addr_size", 0);
326 mathexpr::operinfo* memwatch_item::get_memread_oper(memory_space& memory, loaded_rom& rom)
328 if(addr_base == 0xFFFFFFFFFFFFFFFFULL && addr_size == 0) {
329 //Hack: Registers.
330 regread_oper* o = new regread_oper;
331 o->rom = &rom;
332 o->signed_flag = signed_flag;
333 return o;
335 if(!bytes)
336 return NULL;
337 memorywatch::memread_oper* o = new memorywatch::memread_oper;
338 o->bytes = bytes;
339 o->signed_flag = signed_flag;
340 o->float_flag = float_flag;
341 o->endianess = endianess;
342 o->scale_div = scale_div;
343 o->addr_base = addr_base;
344 o->addr_size = addr_size;
345 o->mspace = &memory;
346 return o;
349 void memwatch_item::compatiblity_unserialize(memory_space& memory, const std::string& item)
351 regex_results r;
352 if(!(r = regex("C0x([0-9A-Fa-f]{1,16})z([bBwWoOdDqQfF])(H([0-9A-Ga-g]))?", item)))
353 throw std::runtime_error("Unknown compatiblity memory watch");
354 std::string _addr = r[1];
355 std::string _type = r[2];
356 std::string _hext = r[4];
357 uint64_t addr = strtoull(_addr.c_str(), NULL, 16);
358 char type = _type[0];
359 char hext = (_hext != "") ? _hext[0] : 0;
360 switch(type) {
361 case 'b': bytes = 1; signed_flag = true; float_flag = false; break;
362 case 'B': bytes = 1; signed_flag = false; float_flag = false; break;
363 case 'w': bytes = 2; signed_flag = true; float_flag = false; break;
364 case 'W': bytes = 2; signed_flag = false; float_flag = false; break;
365 case 'o': bytes = 3; signed_flag = true; float_flag = false; break;
366 case 'O': bytes = 3; signed_flag = false; float_flag = false; break;
367 case 'd': bytes = 4; signed_flag = true; float_flag = false; break;
368 case 'D': bytes = 4; signed_flag = false; float_flag = false; break;
369 case 'q': bytes = 8; signed_flag = true; float_flag = false; break;
370 case 'Q': bytes = 8; signed_flag = false; float_flag = false; break;
371 case 'f': bytes = 4; signed_flag = true; float_flag = true; break;
372 case 'F': bytes = 8; signed_flag = true; float_flag = true; break;
373 default: bytes = 0; break;
375 auto mdata = memory.lookup(addr);
376 if(mdata.first) {
377 addr = mdata.second;
378 addr_base = mdata.first->base;
379 addr_size = mdata.first->size;
380 endianess = mdata.first->endian;
381 } else {
382 addr_base = 0;
383 addr_size = 0;
384 endianess = -1;
386 if(hext) {
387 unsigned width;
388 if(hext >= '0' && hext <= '9')
389 width = hext - '0';
390 else
391 width = (hext & 0x1F) + 9;
392 format = (stringfmt() << "%0" << width << "x").str();
393 } else
394 format = "";
395 expr = (stringfmt() << "0x" << std::hex << addr).str();
396 scale_div = 1;
397 printer.position = memwatch_printer::PC_MEMORYWATCH;
398 printer.cond_enable = false;
399 printer.enabled = "true";
400 printer.onscreen_xpos = "0";
401 printer.onscreen_ypos = "0";
402 printer.onscreen_alt_origin_x = false;
403 printer.onscreen_alt_origin_y = false;
404 printer.onscreen_cliprange_x = false;
405 printer.onscreen_cliprange_y = false;
406 printer.onscreen_font = "";
407 printer.onscreen_fg_color = 0xFFFFFF;
408 printer.onscreen_bg_color = -1;
409 printer.onscreen_halo_color = 0;
412 memwatch_set::memwatch_set(memory_space& _memory, project_state& _project, emu_framebuffer& _fbuf,
413 loaded_rom& _rom)
414 : memory(_memory), project(_project), fbuf(_fbuf), rom(_rom)
418 std::set<std::string> memwatch_set::enumerate()
420 std::set<std::string> r;
421 for(auto& i : items)
422 r.insert(i.first);
423 return r;
426 void memwatch_set::clear(const std::string& name)
428 std::map<std::string, memwatch_item> nitems = items;
429 nitems.erase(name);
430 rebuild(nitems);
431 std::swap(items, nitems);
432 auto pr = project.get();
433 if(pr) {
434 pr->watches.erase(name);
435 pr->flush();
437 fbuf.redraw_framebuffer();
440 void memwatch_set::set(const std::string& name, const std::string& item)
442 memwatch_item _item;
443 if(item != "" && item[0] != '{') {
444 //Compatiblity.
445 try {
446 _item.compatiblity_unserialize(memory, item);
447 } catch(std::exception& e) {
448 messages << "Can't handle old memory watch '" << name << "'" << std::endl;
449 return;
451 } else
452 _item.unserialize(JSON::node(item));
453 set(name, _item);
456 memwatch_item& memwatch_set::get(const std::string& name)
458 if(!items.count(name))
459 throw std::runtime_error("No such memory watch named '" + name + "'");
460 return items.find(name)->second;
463 std::string memwatch_set::get_string(const std::string& name, JSON::printer* printer)
465 auto& x = get(name);
466 auto y = x.serialize();
467 auto z = y.serialize(printer);
468 return z;
471 void memwatch_set::watch(struct framebuffer::queue& rq)
473 //Set framebuffer for all FB watches.
474 watch_set.foreach([&rq](memorywatch::item& i) {
475 memorywatch::output_fb* fb = dynamic_cast<memorywatch::output_fb*>(i.printer.as_pointer());
476 if(fb)
477 fb->set_rqueue(rq);
479 watch_set.refresh();
480 erase_unused_watches();
483 bool memwatch_set::rename(const std::string& oldname, const std::string& newname)
485 std::map<std::string, memwatch_item> nitems = items;
486 if(nitems.count(newname))
487 return false;
488 if(!nitems.count(oldname))
489 return false;
490 nitems.insert(std::make_pair(newname, nitems.find(oldname)->second));
491 nitems.erase(oldname);
492 rebuild(nitems);
493 std::swap(items, nitems);
494 auto pr = project.get();
495 if(pr) {
496 pr->watches.erase(oldname);
497 pr->watches[newname] = get_string(newname);
498 pr->flush();
500 fbuf.redraw_framebuffer();
501 return true;
504 void memwatch_set::set(const std::string& name, memwatch_item& item)
506 std::map<std::string, memwatch_item> nitems = items;
507 nitems.erase(name); //Insert does not insert if already existing.
508 nitems.insert(std::make_pair(name, item));
509 rebuild(nitems);
510 std::swap(items, nitems);
511 auto pr = project.get();
512 if(pr) {
513 pr->watches[name] = get_string(name);
514 pr->flush();
516 fbuf.redraw_framebuffer();
519 std::string memwatch_set::get_value(const std::string& name)
521 return watch_set.get(name).get_value();
524 void memwatch_set::set_multi(std::list<std::pair<std::string, memwatch_item>>& list)
526 std::map<std::string, memwatch_item> nitems = items;
527 for(auto& i : list)
528 nitems.insert(i);
529 rebuild(nitems);
530 std::swap(items, nitems);
531 auto pr = project.get();
532 if(pr) {
533 for(auto& i : list)
534 pr->watches[i.first] = get_string(i.first);
535 pr->flush();
537 fbuf.redraw_framebuffer();
540 void memwatch_set::set_multi(std::list<std::pair<std::string, std::string>>& list)
542 std::list<std::pair<std::string, memwatch_item>> _list;
543 for(auto& i: list) {
544 memwatch_item it;
545 it.unserialize(JSON::node(i.second));
546 _list.push_back(std::make_pair(i.first, it));
548 set_multi(_list);
551 void memwatch_set::clear_multi(const std::set<std::string>& names)
553 std::map<std::string, memwatch_item> nitems = items;
554 for(auto& i : names)
555 nitems.erase(i);
556 rebuild(nitems);
557 std::swap(items, nitems);
558 auto pr = project.get();
559 if(pr) {
560 for(auto& i : names)
561 pr->watches.erase(i);
562 pr->flush();
564 fbuf.redraw_framebuffer();
567 void memwatch_set::rebuild(std::map<std::string, memwatch_item>& nitems)
570 memorywatch::set new_set;
571 std::map<std::string, GC::pointer<mathexpr::mathexpr>> vars;
572 auto vars_fn = [&vars](const std::string& n) -> GC::pointer<mathexpr::mathexpr> {
573 if(!vars.count(n))
574 vars[n] = GC::pointer<mathexpr::mathexpr>(GC::obj_tag(),
575 mathexpr::expression_value());
576 return vars[n];
578 for(auto& i : nitems) {
579 mathexpr::operinfo* memread_oper = i.second.get_memread_oper(memory, rom);
580 try {
581 GC::pointer<mathexpr::mathexpr> rt_expr;
582 GC::pointer<memorywatch::item_printer> rt_printer;
583 std::vector<GC::pointer<mathexpr::mathexpr>> v;
584 try {
585 rt_expr = mathexpr::mathexpr::parse(*mathexpr::expression_value(),
586 i.second.expr, vars_fn);
587 } catch(std::exception& e) {
588 (stringfmt() << "Error while parsing address/expression: "
589 << e.what()).throwex();
591 v.push_back(rt_expr);
592 if(memread_oper) {
593 rt_expr = GC::pointer<mathexpr::mathexpr>(GC::obj_tag(),
594 mathexpr::expression_value(), memread_oper, v, true);
595 memread_oper = NULL;
597 rt_printer = i.second.printer.get_printer_obj(vars_fn);
599 //Set final callback for list objects (since it wasn't known on creation).
600 auto list_obj = dynamic_cast<memorywatch::output_list*>(rt_printer.as_pointer());
601 if(list_obj)
602 list_obj->set_output([this](const std::string& n, const std::string& v) {
603 this->watch_output(n, v);
606 memorywatch::item it(*mathexpr::expression_value());
607 *vars_fn(i.first) = *rt_expr;
608 it.expr = vars_fn(i.first);
609 it.printer = rt_printer;
610 it.format = i.second.format;
611 new_set.create(i.first, it);
612 } catch(...) {
613 delete memread_oper;
614 throw;
617 watch_set.swap(new_set);
619 GC::item::do_gc();
622 void memwatch_set::watch_output(const std::string& name, const std::string& value)
624 used_memorywatches[name] = true;
625 window_vars[name] = utf8::to32(value);
628 void memwatch_set::erase_unused_watches()
630 for(auto& i : used_memorywatches) {
631 if(!i.second)
632 window_vars.erase(i.first);
633 i.second = false;