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"
21 std::map
<std::string
, std::string
> watches
;
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
)
38 if(_s
.length() > 2 && _s
[0] == '0' && _s
[1] == 'x') {
40 u
= strtoull(_s
.c_str() + 2, &end
, 16);
42 throw std::runtime_error("#syntax (badval)");
43 } else if(_s
.length() > 3 && _s
[0] == '+' && _s
[1] == '0' && _s
[2] == 'x') {
45 s
= (int64_t)strtoull(_s
.c_str() + 3, &end
, 16);
47 throw std::runtime_error("#syntax (badval)");
48 } else if(_s
.length() > 3 && _s
[0] == '-' && _s
[1] == '0' && _s
[2] == 'x') {
50 s
= -(int64_t)strtoull(_s
.c_str() + 3, &end
, 16);
52 throw std::runtime_error("#syntax (badval)");
53 } else if(_s
.find_first_of(".") < _s
.length()) {
55 f
= strtod(_s
.c_str(), &end
);
57 throw std::runtime_error("#syntax (badval)");
58 } else if(_s
.length() > 1 && _s
[0] == '+') {
60 s
= (int64_t)strtoull(_s
.c_str() + 1, &end
, 10);
62 throw std::runtime_error("#syntax (badval)");
63 } else if(_s
.length() > 1 && _s
[0] == '-') {
65 s
= -(int64_t)strtoull(_s
.c_str() + 1, &end
, 10);
67 throw std::runtime_error("#syntax (badval)");
70 u
= strtoull(_s
.c_str(), &end
, 10);
72 throw std::runtime_error("#syntax (badval)");
76 uint64_t as_address() const
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");
86 int64_t as_integer() const
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");
96 double as_double() const
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");
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
);
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;
131 numeric_type
round(int prec
) const
137 c
= floor(b
* f
+ 0.5) / b
;
138 return numeric_type(c
);
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());
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());
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());
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();
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());
200 return numeric_type(as_address() / b
.as_address());
204 if(ch
>= '0' && ch
<= '9')
206 if(ch
>= 'A' && ch
<= 'G')
207 hex
= (ch
- 'A') + 10;
208 if(ch
>= 'a' && ch
<= 'g')
209 hex
= (ch
- 'a') + 10;
225 numeric_type
stack_pop(std::stack
<numeric_type
>& s
, bool norm
= false)
228 throw std::runtime_error("#syntax (underflow)");
229 numeric_type r
= s
.top();
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
;
245 std::string _expr
= expr
;
251 for(size_t i
= 0; i
< expr
.length(); i
++) {
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
));
263 if(i
+ 1 == expr
.length())
264 throw std::runtime_error("#syntax (noparam)");
267 stack_push(s
, a
.round(d
));
270 if(i
+ 1 == expr
.length())
271 throw std::runtime_error("#syntax (noparam)");
272 s
.top().sethex(expr
[++i
]);
275 stack_push
<double>(s
, atan(stack_pop(s
).as_double()));
280 stack_push
<double>(s
, atan2(a
.as_double(), b
.as_double()));
283 stack_push
<double>(s
, cos(stack_pop(s
).as_double()));
287 if(a
.as_double() < 0)
288 throw std::runtime_error("#NAN");
289 stack_push
<double>(s
, sqrt(a
.as_double()));
292 stack_push
<double>(s
, sin(stack_pop(s
).as_double()));
295 stack_push
<double>(s
, tan(stack_pop(s
).as_double()));
298 stack_push(s
, stack_pop(s
, true));
301 stack_push(s
, 4 * atan(1));
306 stack_push(s
, a
+ b
);
311 stack_push(s
, a
- b
);
316 stack_push(s
, a
* b
);
321 stack_push(s
, a
.idiv(b
));
326 stack_push(s
, a
/ b
);
331 stack_push(s
, a
% b
);
334 stack_push
<int8_t>(s
, lsnes_memory
.read
<uint8_t>(stack_pop(s
).as_address()));
337 stack_push
<uint8_t>(s
, lsnes_memory
.read
<uint8_t>(stack_pop(s
).as_address()));
340 stack_push
<int16_t>(s
, lsnes_memory
.read
<uint16_t>(stack_pop(s
).as_address()));
343 stack_push
<uint16_t>(s
, lsnes_memory
.read
<uint16_t>(stack_pop(s
).as_address()));
346 stack_push
<ss_int24_t
>(s
, lsnes_memory
.read
<ss_uint24_t
>(stack_pop(s
).as_address()));
349 stack_push
<ss_uint24_t
>(s
, lsnes_memory
.read
<ss_uint24_t
>(stack_pop(s
).as_address()));
352 stack_push
<int32_t>(s
, lsnes_memory
.read
<uint32_t>(stack_pop(s
).as_address()));
355 stack_push
<uint32_t>(s
, lsnes_memory
.read
<uint32_t>(stack_pop(s
).as_address()));
358 stack_push
<int64_t>(s
, lsnes_memory
.read
<uint64_t>(stack_pop(s
).as_address()));
361 stack_push
<uint64_t>(s
, lsnes_memory
.read
<uint64_t>(stack_pop(s
).as_address()));
364 stack_push
<float>(s
, lsnes_memory
.read
<float>(stack_pop(s
).as_address()));
367 stack_push
<double>(s
, lsnes_memory
.read
<double>(stack_pop(s
).as_address()));
370 throw std::runtime_error("#syntax (illchar)");
376 return s
.top().str();
377 } catch(std::exception
& e
) {
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
;
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
;
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();
416 p
->watches
[w
] = expr
;
419 status
.set("M[" + w
+ "]", evaluate_watch(expr
));
425 status
.erase("M[" + w
+ "]");
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
;
438 status
.set("M[" + i
.first
+ "]", evaluate_watch(i
.second
));
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], "");