Fix SA1 open bus
[lsnes.git] / src / core / memorywatch.cpp
blobdc04bcce18489f0b9d1f69dcd06b096a15ad53b6
1 #include "core/command.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/framebuffer.hpp"
4 #include "core/memorymanip.hpp"
5 #include "core/memorywatch.hpp"
6 #include "core/project.hpp"
7 #include "core/window.hpp"
8 #include "fonts/wrapper.hpp"
9 #include "library/directory.hpp"
10 #include "library/string.hpp"
11 #include "library/int24.hpp"
12 #include "library/mathexpr-ntype.hpp"
13 #include "library/memorywatch.hpp"
14 #include "library/memorywatch-list.hpp"
15 #include "library/memorywatch-fb.hpp"
16 #include "library/memorywatch-null.hpp"
18 #include <cstdio>
19 #include <cstdlib>
20 #include <list>
21 #include <iomanip>
22 #include <stack>
23 #include <cmath>
24 #include <sstream>
25 #include <map>
27 namespace
29 std::map<std::string, std::pair<framebuffer::font2*, size_t>> fonts_in_use;
30 std::map<std::string, std::u32string> memorywatch_vars;
32 framebuffer::font2& get_builtin_font2()
34 static framebuffer::font2 f(main_font);
35 return f;
38 framebuffer::font2* get_font(const std::string filename)
40 //Handle NULL font.
41 if(filename == "")
42 return &get_builtin_font2();
43 std::string abs_filename = get_absolute_path(filename);
44 if(fonts_in_use.count(abs_filename)) {
45 fonts_in_use[abs_filename].second++;
46 return fonts_in_use[abs_filename].first;
48 framebuffer::font2* f = new framebuffer::font2(abs_filename);
49 try {
50 fonts_in_use[abs_filename] = std::make_pair(f, 1);
51 } catch(...) {
52 delete f;
53 throw;
55 return f;
57 void put_font(framebuffer::font2* font)
59 //Handle NULL font (always there).
60 if(!font)
61 return;
62 //Find font using this.
63 std::string filename;
64 for(auto& i : fonts_in_use)
65 if(i.second.first == font)
66 filename = i.first;
67 if(filename == "")
68 return;
69 fonts_in_use[filename].second--;
70 if(!fonts_in_use[filename].second) {
71 delete fonts_in_use[filename].first;
72 fonts_in_use.erase(filename);
76 std::string json_string_default(const JSON::node& node, const std::string& pointer, const std::string& dflt)
78 return (node.type_of(pointer) == JSON::string) ? node[pointer].as_string8() : dflt;
81 uint64_t json_unsigned_default(const JSON::node& node, const std::string& pointer, uint64_t dflt)
83 return (node.type_of(pointer) == JSON::number) ? node[pointer].as_uint() : dflt;
86 int64_t json_signed_default(const JSON::node& node, const std::string& pointer, int64_t dflt)
88 return (node.type_of(pointer) == JSON::number) ? node[pointer].as_int() : dflt;
91 bool json_boolean_default(const JSON::node& node, const std::string& pointer, bool dflt)
93 return (node.type_of(pointer) == JSON::boolean) ? node[pointer].as_bool() : dflt;
96 void dummy_target_fn(const std::string& n, const std::string& v) {}
99 lsnes_memorywatch_printer::lsnes_memorywatch_printer()
101 position = PC_MEMORYWATCH;
102 cond_enable = false;
103 onscreen_alt_origin_x = false;
104 onscreen_alt_origin_y = false;
105 onscreen_cliprange_x = false;
106 onscreen_cliprange_y = false;
107 onscreen_fg_color = 0xFFFFFF;
108 onscreen_bg_color = -1;
109 onscreen_halo_color = 0;
112 JSON::node lsnes_memorywatch_printer::serialize()
114 JSON::node ndata(JSON::object);
115 switch(position) {
116 case PC_DISABLED: ndata["position"] = JSON::s("disabled"); break;
117 case PC_MEMORYWATCH: ndata["position"] = JSON::s("memorywatch"); break;
118 case PC_ONSCREEN: ndata["position"] = JSON::s("onscreen"); break;
120 ndata["cond_enable"] = JSON::b(cond_enable);
121 ndata["enabled"] = JSON::s(enabled);
122 ndata["onscreen_xpos"] = JSON::s(onscreen_xpos);
123 ndata["onscreen_ypos"] = JSON::s(onscreen_ypos);
124 ndata["onscreen_alt_origin_x"] = JSON::b(onscreen_alt_origin_x);
125 ndata["onscreen_alt_origin_y"] = JSON::b(onscreen_alt_origin_y);
126 ndata["onscreen_cliprange_x"] = JSON::b(onscreen_cliprange_x);
127 ndata["onscreen_cliprange_y"] = JSON::b(onscreen_cliprange_y);
128 ndata["onscreen_font"] = JSON::s(onscreen_font);
129 ndata["onscreen_fg_color"] = JSON::i(onscreen_fg_color);
130 ndata["onscreen_bg_color"] = JSON::i(onscreen_bg_color);
131 ndata["onscreen_halo_color"] = JSON::i(onscreen_halo_color);
132 return ndata;
135 void lsnes_memorywatch_printer::unserialize(const JSON::node& node)
137 std::string _position = json_string_default(node, "position", "");
138 if(_position == "disabled") position = PC_DISABLED;
139 else if(_position == "memorywatch") position = PC_MEMORYWATCH;
140 else if(_position == "onscreen") position = PC_ONSCREEN;
141 else position = PC_MEMORYWATCH;
142 cond_enable = json_boolean_default(node, "cond_enable", false);
143 enabled = json_string_default(node, "enabled", "");
144 onscreen_xpos = json_string_default(node, "onscreen_xpos", "");
145 onscreen_ypos = json_string_default(node, "onscreen_ypos", "");
146 onscreen_alt_origin_x = json_boolean_default(node, "onscreen_alt_origin_x", false);
147 onscreen_alt_origin_y = json_boolean_default(node, "onscreen_alt_origin_y", false);
148 onscreen_cliprange_x = json_boolean_default(node, "onscreen_cliprange_x", false);
149 onscreen_cliprange_y = json_boolean_default(node, "onscreen_cliprange_y", false);
150 onscreen_font = json_string_default(node, "onscreen_font", "");
151 onscreen_fg_color = json_signed_default(node, "onscreen_fg_color", false);
152 onscreen_bg_color = json_signed_default(node, "onscreen_bg_color", false);
153 onscreen_halo_color = json_signed_default(node, "onscreen_halo_color", false);
156 gcroot_pointer<memorywatch_item_printer> lsnes_memorywatch_printer::get_printer_obj(
157 std::function<gcroot_pointer<mathexpr>(const std::string& n)> vars)
159 gcroot_pointer<memorywatch_item_printer> ptr;
160 memorywatch_output_list* l;
161 memorywatch_output_fb* f;
163 std::string _enabled = (enabled != "") ? enabled : "true";
165 switch(position) {
166 case PC_DISABLED:
167 ptr = gcroot_pointer<memorywatch_item_printer>(new memorywatch_output_null);
168 break;
169 case PC_MEMORYWATCH:
170 ptr = gcroot_pointer<memorywatch_item_printer>(new memorywatch_output_list);
171 l = dynamic_cast<memorywatch_output_list*>(ptr.as_pointer());
172 l->cond_enable = cond_enable;
173 try {
174 if(l->cond_enable)
175 l->enabled = mathexpr::parse(*expression_value(), _enabled, vars);
176 else
177 l->enabled = mathexpr::parse(*expression_value(), "true", vars);
178 } catch(std::exception& e) {
179 (stringfmt() << "Error while parsing conditional: " << e.what()).throwex();
181 l->set_output(dummy_target_fn);
182 break;
183 case PC_ONSCREEN:
184 ptr = gcroot_pointer<memorywatch_item_printer>(new memorywatch_output_fb);
185 f = dynamic_cast<memorywatch_output_fb*>(ptr.as_pointer());
186 f->font = NULL;
187 f->set_dtor_cb([](memorywatch_output_fb& obj) { put_font(obj.font); });
188 f->cond_enable = cond_enable;
189 std::string while_parsing = "(unknown)";
190 try {
191 while_parsing = "conditional";
192 if(f->cond_enable)
193 f->enabled = mathexpr::parse(*expression_value(), _enabled, vars);
194 else
195 f->enabled = mathexpr::parse(*expression_value(), "true", vars);
196 while_parsing = "X position";
197 f->pos_x = mathexpr::parse(*expression_value(), onscreen_xpos, vars);
198 while_parsing = "Y position";
199 f->pos_y = mathexpr::parse(*expression_value(), onscreen_ypos, vars);
200 } catch(std::exception& e) {
201 (stringfmt() << "Error while parsing " << while_parsing << ": " << e.what()).throwex();
203 f->alt_origin_x = onscreen_alt_origin_x;
204 f->alt_origin_y = onscreen_alt_origin_y;
205 f->cliprange_x = onscreen_cliprange_x;
206 f->cliprange_y = onscreen_cliprange_y;
207 f->fg = onscreen_fg_color;
208 f->bg = onscreen_bg_color;
209 f->halo = onscreen_halo_color;
210 try {
211 f->font = get_font(onscreen_font);
212 } catch(std::exception& e) {
213 messages << "Bad font '" << onscreen_font << "': " << e.what() << std::endl;
214 f->font = &get_builtin_font2();
216 break;
218 return ptr;
221 lsnes_memorywatch_item::lsnes_memorywatch_item()
223 bytes = 0;
224 signed_flag = false;
225 float_flag = false;
226 endianess = 0;
227 scale_div = 1;
228 addr_base = 0;
229 addr_size = 0;
230 mspace = &lsnes_memory;
233 JSON::node lsnes_memorywatch_item::serialize()
235 JSON::node ndata(JSON::object);
236 ndata["printer"] = printer.serialize();
237 ndata["expr"] = JSON::s(expr);
238 ndata["format"] = JSON::s(format);
239 ndata["bytes"] = JSON::u(bytes);
240 ndata["signed"] = JSON::b(signed_flag);
241 ndata["float"] = JSON::b(float_flag);
242 ndata["endianess"] = JSON::i(endianess);
243 ndata["scale_div"] = JSON::u(scale_div);
244 ndata["addr_base"] = JSON::u(addr_base);
245 ndata["addr_size"] = JSON::u(addr_size);
246 return ndata;
249 void lsnes_memorywatch_item::unserialize(const JSON::node& node)
251 if(node.type_of("printer") == JSON::object)
252 printer.unserialize(node["printer"]);
253 else
254 printer = lsnes_memorywatch_printer();
255 expr = json_string_default(node, "expr", "0");
256 format = json_string_default(node, "format", "");
257 bytes = json_unsigned_default(node, "bytes", 0);
258 signed_flag = json_boolean_default(node, "signed", false);
259 float_flag = json_boolean_default(node, "float", false);
260 endianess = json_signed_default(node, "endianess", false);
261 scale_div = json_unsigned_default(node, "scale_div", 1);
262 addr_base = json_unsigned_default(node, "addr_base", 0);
263 addr_size = json_unsigned_default(node, "addr_size", 0);
266 memorywatch_memread_oper* lsnes_memorywatch_item::get_memread_oper()
268 if(!bytes)
269 return NULL;
270 memorywatch_memread_oper* o = new memorywatch_memread_oper;
271 o->bytes = bytes;
272 o->signed_flag = signed_flag;
273 o->float_flag = float_flag;
274 o->endianess = endianess;
275 o->scale_div = scale_div;
276 o->addr_base = addr_base;
277 o->addr_size = addr_size;
278 o->mspace = mspace;
279 return o;
282 void lsnes_memorywatch_item::compatiblity_unserialize(const std::string& item)
284 regex_results r;
285 if(!(r = regex("C0x([0-9A-Fa-f]{1,16})z([bBwWoOdDqQfF])(H([0-9A-Ga-g]))?", item)))
286 throw std::runtime_error("Unknown compatiblity memory watch");
287 std::string _addr = r[1];
288 std::string _type = r[2];
289 std::string _hext = r[4];
290 uint64_t addr = strtoull(_addr.c_str(), NULL, 16);
291 char type = _type[0];
292 char hext = (_hext != "") ? _hext[0] : 0;
293 switch(type) {
294 case 'b': bytes = 1; signed_flag = true; float_flag = false; break;
295 case 'B': bytes = 1; signed_flag = false; float_flag = false; break;
296 case 'w': bytes = 2; signed_flag = true; float_flag = false; break;
297 case 'W': bytes = 2; signed_flag = false; float_flag = false; break;
298 case 'o': bytes = 3; signed_flag = true; float_flag = false; break;
299 case 'O': bytes = 3; signed_flag = false; float_flag = false; break;
300 case 'd': bytes = 4; signed_flag = true; float_flag = false; break;
301 case 'D': bytes = 4; signed_flag = false; float_flag = false; break;
302 case 'q': bytes = 8; signed_flag = true; float_flag = false; break;
303 case 'Q': bytes = 8; signed_flag = false; float_flag = false; break;
304 case 'f': bytes = 4; signed_flag = true; float_flag = true; break;
305 case 'F': bytes = 8; signed_flag = true; float_flag = true; break;
306 default: bytes = 0; break;
308 auto mdata = lsnes_memory.lookup(addr);
309 if(mdata.first) {
310 addr = mdata.second;
311 addr_base = mdata.first->base;
312 addr_size = mdata.first->size;
313 endianess = mdata.first->endian;
314 } else {
315 addr_base = 0;
316 addr_size = 0;
317 endianess = -1;
319 if(hext) {
320 unsigned width;
321 if(hext >= '0' && hext <= '9')
322 width = hext - '0';
323 else
324 width = (hext & 0x1F) + 9;
325 format = (stringfmt() << "%0" << width << "x").str();
326 } else
327 format = "";
328 expr = (stringfmt() << "0x" << std::hex << addr).str();
329 scale_div = 1;
330 mspace = &lsnes_memory;
331 printer.position = lsnes_memorywatch_printer::PC_MEMORYWATCH;
332 printer.cond_enable = false;
333 printer.enabled = "true";
334 printer.onscreen_xpos = "0";
335 printer.onscreen_ypos = "0";
336 printer.onscreen_alt_origin_x = false;
337 printer.onscreen_alt_origin_y = false;
338 printer.onscreen_cliprange_x = false;
339 printer.onscreen_cliprange_y = false;
340 printer.onscreen_font = "";
341 printer.onscreen_fg_color = 0xFFFFFF;
342 printer.onscreen_bg_color = -1;
343 printer.onscreen_halo_color = 0;
346 std::set<std::string> lsnes_memorywatch_set::enumerate()
348 std::set<std::string> r;
349 for(auto& i : items)
350 r.insert(i.first);
351 return r;
354 void lsnes_memorywatch_set::clear(const std::string& name)
356 std::map<std::string, lsnes_memorywatch_item> nitems = items;
357 nitems.erase(name);
358 rebuild(nitems);
359 std::swap(items, nitems);
360 auto pr = project_get();
361 if(pr) {
362 pr->watches.erase(name);
363 pr->flush();
365 redraw_framebuffer();
368 void lsnes_memorywatch_set::set(const std::string& name, const std::string& item)
370 lsnes_memorywatch_item _item;
371 if(item != "" && item[0] != '{') {
372 //Compatiblity.
373 try {
374 _item.compatiblity_unserialize(item);
375 } catch(std::exception& e) {
376 messages << "Can't handle old memory watch '" << name << "'" << std::endl;
377 return;
379 } else
380 _item.unserialize(JSON::node(item));
381 set(name, _item);
384 lsnes_memorywatch_item& lsnes_memorywatch_set::get(const std::string& name)
386 if(!items.count(name))
387 throw std::runtime_error("No such memory watch named '" + name + "'");
388 return items[name];
391 std::string lsnes_memorywatch_set::get_string(const std::string& name, JSON::printer* printer)
393 auto& x = get(name);
394 auto y = x.serialize();
395 auto z = y.serialize(printer);
396 return z;
399 void lsnes_memorywatch_set::watch(struct framebuffer::queue& rq)
401 //Set framebuffer for all FB watches.
402 watch_set.foreach([&rq](memorywatch_item& i) {
403 memorywatch_output_fb* fb = dynamic_cast<memorywatch_output_fb*>(i.printer.as_pointer());
404 if(fb)
405 fb->set_rqueue(rq);
407 watch_set.refresh();
408 erase_unused_watches();
411 bool lsnes_memorywatch_set::rename(const std::string& oldname, const std::string& newname)
413 std::map<std::string, lsnes_memorywatch_item> nitems = items;
414 if(nitems.count(newname))
415 return false;
416 if(!nitems.count(oldname))
417 return false;
418 nitems[newname] = nitems[oldname];
419 nitems.erase(oldname);
420 rebuild(nitems);
421 std::swap(items, nitems);
422 redraw_framebuffer();
423 return true;
426 void lsnes_memorywatch_set::set(const std::string& name, lsnes_memorywatch_item& item)
428 std::map<std::string, lsnes_memorywatch_item> nitems = items;
429 nitems[name] = item;
430 rebuild(nitems);
431 std::swap(items, nitems);
432 auto pr = project_get();
433 if(pr) {
434 pr->watches[name] = get_string(name);
435 pr->flush();
437 redraw_framebuffer();
440 std::string lsnes_memorywatch_set::get_value(const std::string& name)
442 return watch_set.get(name).get_value();
445 void lsnes_memorywatch_set::set_multi(std::list<std::pair<std::string, lsnes_memorywatch_item>>& list)
447 std::map<std::string, lsnes_memorywatch_item> nitems = items;
448 for(auto& i : list)
449 nitems[i.first] = i.second;
450 rebuild(nitems);
451 std::swap(items, nitems);
452 auto pr = project_get();
453 if(pr) {
454 for(auto& i : list)
455 pr->watches[i.first] = get_string(i.first);
456 pr->flush();
458 redraw_framebuffer();
461 void lsnes_memorywatch_set::set_multi(std::list<std::pair<std::string, std::string>>& list)
463 std::list<std::pair<std::string, lsnes_memorywatch_item>> _list;
464 for(auto& i: list) {
465 lsnes_memorywatch_item it;
466 it.unserialize(JSON::node(i.second));
467 _list.push_back(std::make_pair(i.first, it));
469 set_multi(_list);
472 void lsnes_memorywatch_set::clear_multi(const std::set<std::string>& names)
474 std::map<std::string, lsnes_memorywatch_item> nitems = items;
475 for(auto& i : names)
476 nitems.erase(i);
477 rebuild(nitems);
478 std::swap(items, nitems);
479 auto pr = project_get();
480 if(pr) {
481 for(auto& i : names)
482 pr->watches.erase(i);
483 pr->flush();
485 redraw_framebuffer();
488 void lsnes_memorywatch_set::rebuild(std::map<std::string, lsnes_memorywatch_item>& nitems)
491 memorywatch_set new_set;
492 std::map<std::string, gcroot_pointer<mathexpr>> vars;
493 auto vars_fn = [&vars](const std::string& n) -> gcroot_pointer<mathexpr> {
494 if(!vars.count(n))
495 vars[n] = gcroot_pointer<mathexpr>(gcroot_pointer_object_tag(),
496 expression_value());
497 return vars[n];
499 for(auto& i : nitems) {
500 mathexpr_operinfo* memread_oper = i.second.get_memread_oper();
501 try {
502 gcroot_pointer<mathexpr> rt_expr;
503 gcroot_pointer<memorywatch_item_printer> rt_printer;
504 std::vector<gcroot_pointer<mathexpr>> v;
505 try {
506 rt_expr = mathexpr::parse(*expression_value(), i.second.expr, vars_fn);
507 } catch(std::exception& e) {
508 (stringfmt() << "Error while parsing address/expression: "
509 << e.what()).throwex();
511 v.push_back(rt_expr);
512 if(memread_oper) {
513 rt_expr = gcroot_pointer<mathexpr>(gcroot_pointer_object_tag(),
514 expression_value(), memread_oper, v, true);
515 memread_oper = NULL;
517 rt_printer = i.second.printer.get_printer_obj(vars_fn);
519 //Set final callback for list objects (since it wasn't known on creation).
520 auto list_obj = dynamic_cast<memorywatch_output_list*>(rt_printer.as_pointer());
521 if(list_obj)
522 list_obj->set_output([this](const std::string& n, const std::string& v) {
523 this->memorywatch_output(n, v);
526 memorywatch_item it(*expression_value());
527 *vars_fn(i.first) = *rt_expr;
528 it.expr = vars_fn(i.first);
529 it.printer = rt_printer;
530 it.format = i.second.format;
531 new_set.create(i.first, it);
532 } catch(...) {
533 delete memread_oper;
534 throw;
537 watch_set.swap(new_set);
539 garbage_collectable::do_gc();
542 void lsnes_memorywatch_set::memorywatch_output(const std::string& name, const std::string& value)
544 used_memorywatches[name] = true;
545 window_vars[name] = utf8::to32(value);
548 void lsnes_memorywatch_set::erase_unused_watches()
550 for(auto& i : used_memorywatches) {
551 if(!i.second)
552 window_vars.erase(i.first);
553 i.second = false;
557 lsnes_memorywatch_set lsnes_memorywatch;