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"
35 globalwrap
<std::map
<std::string
, std::pair
<framebuffer::font2
*, size_t>>> S_fonts_in_use
;
37 framebuffer::font2
& get_builtin_font2()
39 static framebuffer::font2
f(main_font
);
43 framebuffer::font2
* get_font(const std::string filename
)
47 return &get_builtin_font2();
48 std::string abs_filename
= directory::absolute_path(filename
);
49 if(S_fonts_in_use().count(abs_filename
)) {
50 S_fonts_in_use()[abs_filename
].second
++;
51 return S_fonts_in_use()[abs_filename
].first
;
53 framebuffer::font2
* f
= new framebuffer::font2(abs_filename
);
55 S_fonts_in_use()[abs_filename
] = std::make_pair(f
, 1);
62 void put_font(framebuffer::font2
* font
)
64 //Handle NULL font (always there).
67 //Find font using this.
69 for(auto& i
: S_fonts_in_use())
70 if(i
.second
.first
== font
)
74 S_fonts_in_use()[filename
].second
--;
75 if(!S_fonts_in_use()[filename
].second
) {
76 delete S_fonts_in_use()[filename
].first
;
77 S_fonts_in_use().erase(filename
);
81 std::string
json_string_default(const JSON::node
& node
, const std::string
& pointer
, const std::string
& dflt
)
83 return (node
.type_of(pointer
) == JSON::string
) ? node
[pointer
].as_string8() : dflt
;
86 uint64_t json_unsigned_default(const JSON::node
& node
, const std::string
& pointer
, uint64_t dflt
)
88 return (node
.type_of(pointer
) == JSON::number
) ? node
[pointer
].as_uint() : dflt
;
91 int64_t json_signed_default(const JSON::node
& node
, const std::string
& pointer
, int64_t dflt
)
93 return (node
.type_of(pointer
) == JSON::number
) ? node
[pointer
].as_int() : dflt
;
96 bool json_boolean_default(const JSON::node
& node
, const std::string
& pointer
, bool dflt
)
98 return (node
.type_of(pointer
) == JSON::boolean
) ? node
[pointer
].as_bool() : dflt
;
101 void dummy_target_fn(const std::string
& n
, const std::string
& v
) {}
104 struct regread_oper
: public mathexpr::operinfo
108 //The first promise is the register name.
109 void evaluate(mathexpr::value target
, std::vector
<std::function
<mathexpr::value()>> promises
);
115 regread_oper::regread_oper()
116 : operinfo("(readregister)")
121 regread_oper::~regread_oper()
124 void regread_oper::evaluate(mathexpr::value target
, std::vector
<std::function
<mathexpr::value()>> promises
)
126 if(promises
.size() != 1)
127 throw mathexpr::error(mathexpr::error::ARGCOUNT
, "register read operator takes 1 argument");
130 mathexpr::value val
= promises
[0]();
131 void* res
= val
._value
;
132 rname
= val
.type
->tostring(res
);
133 } catch(std::exception
& e
) {
134 throw mathexpr::error(mathexpr::error::ADDR
, e
.what());
136 const interface_device_reg
* regs
= rom
->get_registers();
138 for(size_t i
= 0; regs
&& regs
[i
].name
; i
++) {
139 if(rname
!= regs
[i
].name
)
142 if(regs
[i
].boolean
) {
143 bool v
= (regs
[i
].read() != 0);
144 target
.type
->parse_b(target
._value
, v
);
145 } else if(signed_flag
) {
146 int64_t v
= regs
[i
].read();
147 target
.type
->parse_s(target
._value
, v
);
149 uint64_t v
= regs
[i
].read();
150 target
.type
->parse_u(target
._value
, v
);
156 throw mathexpr::error(mathexpr::error::ADDR
, "No such register");
161 memwatch_printer::memwatch_printer()
163 position
= PC_MEMORYWATCH
;
165 onscreen_alt_origin_x
= false;
166 onscreen_alt_origin_y
= false;
167 onscreen_cliprange_x
= false;
168 onscreen_cliprange_y
= false;
169 onscreen_fg_color
= 0xFFFFFF;
170 onscreen_bg_color
= -1;
171 onscreen_halo_color
= 0;
174 JSON::node
memwatch_printer::serialize()
176 JSON::node
ndata(JSON::object
);
178 case PC_DISABLED
: ndata
["position"] = JSON::s("disabled"); break;
179 case PC_MEMORYWATCH
: ndata
["position"] = JSON::s("memorywatch"); break;
180 case PC_ONSCREEN
: ndata
["position"] = JSON::s("onscreen"); break;
182 ndata
["cond_enable"] = JSON::b(cond_enable
);
183 ndata
["enabled"] = JSON::s(enabled
);
184 ndata
["onscreen_xpos"] = JSON::s(onscreen_xpos
);
185 ndata
["onscreen_ypos"] = JSON::s(onscreen_ypos
);
186 ndata
["onscreen_alt_origin_x"] = JSON::b(onscreen_alt_origin_x
);
187 ndata
["onscreen_alt_origin_y"] = JSON::b(onscreen_alt_origin_y
);
188 ndata
["onscreen_cliprange_x"] = JSON::b(onscreen_cliprange_x
);
189 ndata
["onscreen_cliprange_y"] = JSON::b(onscreen_cliprange_y
);
190 ndata
["onscreen_font"] = JSON::s(onscreen_font
);
191 ndata
["onscreen_fg_color"] = JSON::i(onscreen_fg_color
);
192 ndata
["onscreen_bg_color"] = JSON::i(onscreen_bg_color
);
193 ndata
["onscreen_halo_color"] = JSON::i(onscreen_halo_color
);
197 void memwatch_printer::unserialize(const JSON::node
& node
)
199 std::string _position
= json_string_default(node
, "position", "");
200 if(_position
== "disabled") position
= PC_DISABLED
;
201 else if(_position
== "memorywatch") position
= PC_MEMORYWATCH
;
202 else if(_position
== "onscreen") position
= PC_ONSCREEN
;
203 else position
= PC_MEMORYWATCH
;
204 cond_enable
= json_boolean_default(node
, "cond_enable", false);
205 enabled
= json_string_default(node
, "enabled", "");
206 onscreen_xpos
= json_string_default(node
, "onscreen_xpos", "");
207 onscreen_ypos
= json_string_default(node
, "onscreen_ypos", "");
208 onscreen_alt_origin_x
= json_boolean_default(node
, "onscreen_alt_origin_x", false);
209 onscreen_alt_origin_y
= json_boolean_default(node
, "onscreen_alt_origin_y", false);
210 onscreen_cliprange_x
= json_boolean_default(node
, "onscreen_cliprange_x", false);
211 onscreen_cliprange_y
= json_boolean_default(node
, "onscreen_cliprange_y", false);
212 onscreen_font
= json_string_default(node
, "onscreen_font", "");
213 onscreen_fg_color
= json_signed_default(node
, "onscreen_fg_color", false);
214 onscreen_bg_color
= json_signed_default(node
, "onscreen_bg_color", false);
215 onscreen_halo_color
= json_signed_default(node
, "onscreen_halo_color", false);
218 GC::pointer
<memorywatch::item_printer
> memwatch_printer::get_printer_obj(
219 std::function
<GC::pointer
<mathexpr::mathexpr
>(const std::string
& n
)> vars
)
221 GC::pointer
<memorywatch::item_printer
> ptr
;
222 memorywatch::output_list
* l
;
223 memorywatch::output_fb
* f
;
225 std::string _enabled
= (enabled
!= "") ? enabled
: "true";
229 ptr
= GC::pointer
<memorywatch::item_printer
>(new memorywatch::output_null
);
232 ptr
= GC::pointer
<memorywatch::item_printer
>(new memorywatch::output_list
);
233 l
= dynamic_cast<memorywatch::output_list
*>(ptr
.as_pointer());
234 l
->cond_enable
= cond_enable
;
237 l
->enabled
= mathexpr::mathexpr::parse(*mathexpr::expression_value(), _enabled
, vars
);
239 l
->enabled
= mathexpr::mathexpr::parse(*mathexpr::expression_value(), "true", vars
);
240 } catch(std::exception
& e
) {
241 (stringfmt() << "Error while parsing conditional: " << e
.what()).throwex();
243 l
->set_output(dummy_target_fn
);
246 ptr
= GC::pointer
<memorywatch::item_printer
>(new memorywatch::output_fb
);
247 f
= dynamic_cast<memorywatch::output_fb
*>(ptr
.as_pointer());
249 f
->set_dtor_cb([](memorywatch::output_fb
& obj
) { put_font(obj
.font
); });
250 f
->cond_enable
= cond_enable
;
251 std::string while_parsing
= "(unknown)";
253 while_parsing
= "conditional";
255 f
->enabled
= mathexpr::mathexpr::parse(*mathexpr::expression_value(), _enabled
, vars
);
257 f
->enabled
= mathexpr::mathexpr::parse(*mathexpr::expression_value(), "true", vars
);
258 while_parsing
= "X position";
259 f
->pos_x
= mathexpr::mathexpr::parse(*mathexpr::expression_value(), onscreen_xpos
, vars
);
260 while_parsing
= "Y position";
261 f
->pos_y
= mathexpr::mathexpr::parse(*mathexpr::expression_value(), onscreen_ypos
, vars
);
262 } catch(std::exception
& e
) {
263 (stringfmt() << "Error while parsing " << while_parsing
<< ": " << e
.what()).throwex();
265 f
->alt_origin_x
= onscreen_alt_origin_x
;
266 f
->alt_origin_y
= onscreen_alt_origin_y
;
267 f
->cliprange_x
= onscreen_cliprange_x
;
268 f
->cliprange_y
= onscreen_cliprange_y
;
269 f
->fg
= onscreen_fg_color
;
270 f
->bg
= onscreen_bg_color
;
271 f
->halo
= onscreen_halo_color
;
273 f
->font
= get_font(onscreen_font
);
274 } catch(std::exception
& e
) {
275 messages
<< "Bad font '" << onscreen_font
<< "': " << e
.what() << std::endl
;
276 f
->font
= &get_builtin_font2();
283 memwatch_item::memwatch_item()
294 JSON::node
memwatch_item::serialize()
296 JSON::node
ndata(JSON::object
);
297 ndata
["printer"] = printer
.serialize();
298 ndata
["expr"] = JSON::s(expr
);
299 ndata
["format"] = JSON::s(format
);
300 ndata
["bytes"] = JSON::u(bytes
);
301 ndata
["signed"] = JSON::b(signed_flag
);
302 ndata
["float"] = JSON::b(float_flag
);
303 ndata
["endianess"] = JSON::i(endianess
);
304 ndata
["scale_div"] = JSON::u(scale_div
);
305 ndata
["addr_base"] = JSON::u(addr_base
);
306 ndata
["addr_size"] = JSON::u(addr_size
);
310 void memwatch_item::unserialize(const JSON::node
& node
)
312 if(node
.type_of("printer") == JSON::object
)
313 printer
.unserialize(node
["printer"]);
315 printer
= memwatch_printer();
316 expr
= json_string_default(node
, "expr", "0");
317 format
= json_string_default(node
, "format", "");
318 bytes
= json_unsigned_default(node
, "bytes", 0);
319 signed_flag
= json_boolean_default(node
, "signed", false);
320 float_flag
= json_boolean_default(node
, "float", false);
321 endianess
= json_signed_default(node
, "endianess", false);
322 scale_div
= json_unsigned_default(node
, "scale_div", 1);
323 addr_base
= json_unsigned_default(node
, "addr_base", 0);
324 addr_size
= json_unsigned_default(node
, "addr_size", 0);
327 mathexpr::operinfo
* memwatch_item::get_memread_oper(memory_space
& memory
, loaded_rom
& rom
)
329 if(addr_base
== 0xFFFFFFFFFFFFFFFFULL
&& addr_size
== 0) {
331 regread_oper
* o
= new regread_oper
;
333 o
->signed_flag
= signed_flag
;
338 memorywatch::memread_oper
* o
= new memorywatch::memread_oper
;
340 o
->signed_flag
= signed_flag
;
341 o
->float_flag
= float_flag
;
342 o
->endianess
= endianess
;
343 o
->scale_div
= scale_div
;
344 o
->addr_base
= addr_base
;
345 o
->addr_size
= addr_size
;
350 void memwatch_item::compatiblity_unserialize(memory_space
& memory
, const std::string
& item
)
353 if(!(r
= regex("C0x([0-9A-Fa-f]{1,16})z([bBwWoOdDqQfF])(H([0-9A-Ga-g]))?", item
)))
354 throw std::runtime_error("Unknown compatiblity memory watch");
355 std::string _addr
= r
[1];
356 std::string _type
= r
[2];
357 std::string _hext
= r
[4];
358 uint64_t addr
= strtoull(_addr
.c_str(), NULL
, 16);
359 char type
= _type
[0];
360 char hext
= (_hext
!= "") ? _hext
[0] : 0;
362 case 'b': bytes
= 1; signed_flag
= true; float_flag
= false; break;
363 case 'B': bytes
= 1; signed_flag
= false; float_flag
= false; break;
364 case 'w': bytes
= 2; signed_flag
= true; float_flag
= false; break;
365 case 'W': bytes
= 2; signed_flag
= false; float_flag
= false; break;
366 case 'o': bytes
= 3; signed_flag
= true; float_flag
= false; break;
367 case 'O': bytes
= 3; signed_flag
= false; float_flag
= false; break;
368 case 'd': bytes
= 4; signed_flag
= true; float_flag
= false; break;
369 case 'D': bytes
= 4; signed_flag
= false; float_flag
= false; break;
370 case 'q': bytes
= 8; signed_flag
= true; float_flag
= false; break;
371 case 'Q': bytes
= 8; signed_flag
= false; float_flag
= false; break;
372 case 'f': bytes
= 4; signed_flag
= true; float_flag
= true; break;
373 case 'F': bytes
= 8; signed_flag
= true; float_flag
= true; break;
374 default: bytes
= 0; break;
376 auto mdata
= memory
.lookup(addr
);
379 addr_base
= mdata
.first
->base
;
380 addr_size
= mdata
.first
->size
;
381 endianess
= mdata
.first
->endian
;
389 if(hext
>= '0' && hext
<= '9')
392 width
= (hext
& 0x1F) + 9;
393 format
= (stringfmt() << "%0" << width
<< "x").str();
396 expr
= (stringfmt() << "0x" << std::hex
<< addr
).str();
398 printer
.position
= memwatch_printer::PC_MEMORYWATCH
;
399 printer
.cond_enable
= false;
400 printer
.enabled
= "true";
401 printer
.onscreen_xpos
= "0";
402 printer
.onscreen_ypos
= "0";
403 printer
.onscreen_alt_origin_x
= false;
404 printer
.onscreen_alt_origin_y
= false;
405 printer
.onscreen_cliprange_x
= false;
406 printer
.onscreen_cliprange_y
= false;
407 printer
.onscreen_font
= "";
408 printer
.onscreen_fg_color
= 0xFFFFFF;
409 printer
.onscreen_bg_color
= -1;
410 printer
.onscreen_halo_color
= 0;
413 memwatch_set::memwatch_set(memory_space
& _memory
, project_state
& _project
, emu_framebuffer
& _fbuf
,
415 : memory(_memory
), project(_project
), fbuf(_fbuf
), rom(_rom
)
419 std::set
<std::string
> memwatch_set::enumerate()
421 std::set
<std::string
> r
;
427 void memwatch_set::clear(const std::string
& name
)
429 std::map
<std::string
, memwatch_item
> nitems
= items
;
432 std::swap(items
, nitems
);
433 auto pr
= project
.get();
435 pr
->watches
.erase(name
);
438 fbuf
.redraw_framebuffer();
441 void memwatch_set::set(const std::string
& name
, const std::string
& item
)
444 if(item
!= "" && item
[0] != '{') {
447 _item
.compatiblity_unserialize(memory
, item
);
448 } catch(std::exception
& e
) {
449 messages
<< "Can't handle old memory watch '" << name
<< "'" << std::endl
;
453 _item
.unserialize(JSON::node(item
));
457 memwatch_item
& memwatch_set::get(const std::string
& name
)
459 if(!items
.count(name
))
460 throw std::runtime_error("No such memory watch named '" + name
+ "'");
461 return items
.find(name
)->second
;
464 std::string
memwatch_set::get_string(const std::string
& name
, JSON::printer
* printer
)
467 auto y
= x
.serialize();
468 auto z
= y
.serialize(printer
);
472 void memwatch_set::watch(struct framebuffer::queue
& rq
)
474 //Set framebuffer for all FB watches.
475 watch_set
.foreach([&rq
](memorywatch::item
& i
) {
476 memorywatch::output_fb
* fb
= dynamic_cast<memorywatch::output_fb
*>(i
.printer
.as_pointer());
481 erase_unused_watches();
484 bool memwatch_set::rename(const std::string
& oldname
, const std::string
& newname
)
486 std::map
<std::string
, memwatch_item
> nitems
= items
;
487 if(nitems
.count(newname
))
489 if(!nitems
.count(oldname
))
491 nitems
.insert(std::make_pair(newname
, nitems
.find(oldname
)->second
));
492 nitems
.erase(oldname
);
494 std::swap(items
, nitems
);
495 auto pr
= project
.get();
497 pr
->watches
.erase(oldname
);
498 pr
->watches
[newname
] = get_string(newname
);
501 fbuf
.redraw_framebuffer();
505 void memwatch_set::set(const std::string
& name
, memwatch_item
& item
)
507 std::map
<std::string
, memwatch_item
> nitems
= items
;
508 nitems
.erase(name
); //Insert does not insert if already existing.
509 nitems
.insert(std::make_pair(name
, item
));
511 std::swap(items
, nitems
);
512 auto pr
= project
.get();
514 pr
->watches
[name
] = get_string(name
);
517 fbuf
.redraw_framebuffer();
520 std::string
memwatch_set::get_value(const std::string
& name
)
522 return watch_set
.get(name
).get_value();
525 void memwatch_set::set_multi(std::list
<std::pair
<std::string
, memwatch_item
>>& list
)
527 std::map
<std::string
, memwatch_item
> nitems
= items
;
531 std::swap(items
, nitems
);
532 auto pr
= project
.get();
535 pr
->watches
[i
.first
] = get_string(i
.first
);
538 fbuf
.redraw_framebuffer();
541 void memwatch_set::set_multi(std::list
<std::pair
<std::string
, std::string
>>& list
)
543 std::list
<std::pair
<std::string
, memwatch_item
>> _list
;
546 it
.unserialize(JSON::node(i
.second
));
547 _list
.push_back(std::make_pair(i
.first
, it
));
552 void memwatch_set::clear_multi(const std::set
<std::string
>& names
)
554 std::map
<std::string
, memwatch_item
> nitems
= items
;
558 std::swap(items
, nitems
);
559 auto pr
= project
.get();
562 pr
->watches
.erase(i
);
565 fbuf
.redraw_framebuffer();
568 void memwatch_set::rebuild(std::map
<std::string
, memwatch_item
>& nitems
)
571 memorywatch::set new_set
;
572 std::map
<std::string
, GC::pointer
<mathexpr::mathexpr
>> vars
;
573 auto vars_fn
= [&vars
](const std::string
& n
) -> GC::pointer
<mathexpr::mathexpr
> {
575 vars
[n
] = GC::pointer
<mathexpr::mathexpr
>(GC::obj_tag(),
576 mathexpr::expression_value());
579 for(auto& i
: nitems
) {
580 mathexpr::operinfo
* memread_oper
= i
.second
.get_memread_oper(memory
, rom
);
582 GC::pointer
<mathexpr::mathexpr
> rt_expr
;
583 GC::pointer
<memorywatch::item_printer
> rt_printer
;
584 std::vector
<GC::pointer
<mathexpr::mathexpr
>> v
;
586 rt_expr
= mathexpr::mathexpr::parse(*mathexpr::expression_value(),
587 i
.second
.expr
, vars_fn
);
588 } catch(std::exception
& e
) {
589 (stringfmt() << "Error while parsing address/expression: "
590 << e
.what()).throwex();
592 v
.push_back(rt_expr
);
594 rt_expr
= GC::pointer
<mathexpr::mathexpr
>(GC::obj_tag(),
595 mathexpr::expression_value(), memread_oper
, v
, true);
598 rt_printer
= i
.second
.printer
.get_printer_obj(vars_fn
);
600 //Set final callback for list objects (since it wasn't known on creation).
601 auto list_obj
= dynamic_cast<memorywatch::output_list
*>(rt_printer
.as_pointer());
603 list_obj
->set_output([this](const std::string
& n
, const std::string
& v
) {
604 this->watch_output(n
, v
);
607 memorywatch::item
it(*mathexpr::expression_value());
608 *vars_fn(i
.first
) = *rt_expr
;
609 it
.expr
= vars_fn(i
.first
);
610 it
.printer
= rt_printer
;
611 it
.format
= i
.second
.format
;
612 new_set
.create(i
.first
, it
);
618 watch_set
.swap(new_set
);
623 void memwatch_set::watch_output(const std::string
& name
, const std::string
& value
)
625 used_memorywatches
[name
] = true;
626 window_vars
[name
] = utf8::to32(value
);
629 void memwatch_set::erase_unused_watches()
631 for(auto& i
: used_memorywatches
) {
633 window_vars
.erase(i
.first
);