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>
19 std::map
<std::string
, std::string
> watches
;
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
)
36 if(_s
.length() > 2 && _s
[0] == '0' && _s
[1] == 'x') {
38 u
= strtoull(_s
.c_str() + 2, &end
, 16);
40 throw std::runtime_error("#syntax (badval)");
41 } else if(_s
.length() > 3 && _s
[0] == '+' && _s
[1] == '0' && _s
[2] == 'x') {
43 s
= (int64_t)strtoull(_s
.c_str() + 3, &end
, 16);
45 throw std::runtime_error("#syntax (badval)");
46 } else if(_s
.length() > 3 && _s
[0] == '-' && _s
[1] == '0' && _s
[2] == 'x') {
48 s
= -(int64_t)strtoull(_s
.c_str() + 3, &end
, 16);
50 throw std::runtime_error("#syntax (badval)");
51 } else if(_s
.find_first_of(".") < _s
.length()) {
53 f
= strtod(_s
.c_str(), &end
);
55 throw std::runtime_error("#syntax (badval)");
56 } else if(_s
.length() > 1 && _s
[0] == '+') {
58 s
= (int64_t)strtoull(_s
.c_str() + 1, &end
, 10);
60 throw std::runtime_error("#syntax (badval)");
61 } else if(_s
.length() > 1 && _s
[0] == '-') {
63 s
= -(int64_t)strtoull(_s
.c_str() + 1, &end
, 10);
65 throw std::runtime_error("#syntax (badval)");
68 u
= strtoull(_s
.c_str(), &end
, 10);
70 throw std::runtime_error("#syntax (badval)");
74 uint64_t as_address() const
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");
84 int64_t as_integer() const
87 case VT_SIGNED
: return s
;
88 case VT_UNSIGNED
: return u
;
89 case VT_FLOAT
: return f
;
90 case VT_NAN
: throw std::runtime_error("#NAN");
94 double as_double() const
97 case VT_SIGNED
: return s
;
98 case VT_UNSIGNED
: return u
;
99 case VT_FLOAT
: return f
;
100 case VT_NAN
: throw std::runtime_error("#NAN");
104 std::string
str() const
106 uint64_t wmasks
[] = {
107 0x0000000000000000ULL
, 0x000000000000000FULL
, 0x00000000000000FFULL
,
108 0x0000000000000FFFULL
, 0x000000000000FFFFULL
, 0x00000000000FFFFFULL
,
109 0x0000000000FFFFFFULL
, 0x000000000FFFFFFFULL
, 0x00000000FFFFFFFFULL
,
110 0x0000000FFFFFFFFFULL
, 0x000000FFFFFFFFFFULL
, 0x00000FFFFFFFFFFFULL
,
111 0x0000FFFFFFFFFFFFULL
, 0x000FFFFFFFFFFFFFULL
, 0x00FFFFFFFFFFFFFFULL
,
112 0x0FFFFFFFFFFFFFFFULL
, 0xFFFFFFFFFFFFFFFFULL
114 std::ostringstream x
;
115 if(hex
&& (t
== VT_SIGNED
|| t
== VT_UNSIGNED
)) {
116 uint64_t wmask
= wmasks
[hex
];
117 uint64_t w
= (t
== VT_SIGNED
) ? s
: u
;
118 x
<< std::hex
<< std::setw(hex
) << std::setfill('0') << (w
& wmask
);
122 case VT_SIGNED
: x
<< s
; break;
123 case VT_UNSIGNED
: x
<< u
; break;
124 case VT_FLOAT
: x
<< f
; break;
125 case VT_NAN
: x
<< "#NAN"; break;
129 numeric_type
round(int prec
) const
135 c
= floor(b
* f
+ 0.5) / b
;
136 return numeric_type(c
);
141 numeric_type
operator+(const numeric_type
& b
) const
143 if(t
== VT_NAN
|| b
.t
== VT_NAN
)
144 return numeric_type();
145 else if(t
== VT_FLOAT
|| b
.t
== VT_FLOAT
)
146 return numeric_type(as_double() + b
.as_double());
147 else if(t
== VT_SIGNED
|| b
.t
== VT_SIGNED
)
148 return numeric_type(as_integer() + b
.as_integer());
150 return numeric_type(as_address() + b
.as_address());
152 numeric_type
operator-(const numeric_type
& b
) const
154 if(t
== VT_NAN
|| b
.t
== VT_NAN
)
155 return numeric_type();
156 else if(t
== VT_FLOAT
|| b
.t
== VT_FLOAT
)
157 return numeric_type(as_double() - b
.as_double());
158 else if(t
== VT_SIGNED
|| b
.t
== VT_SIGNED
)
159 return numeric_type(as_integer() - b
.as_integer());
161 return numeric_type(as_address() - b
.as_address());
163 numeric_type
operator*(const numeric_type
& b
) const
165 if(t
== VT_NAN
|| b
.t
== VT_NAN
)
166 return numeric_type();
167 else if(t
== VT_FLOAT
|| b
.t
== VT_FLOAT
)
168 return numeric_type(as_double() * b
.as_double());
169 else if(t
== VT_SIGNED
|| b
.t
== VT_SIGNED
)
170 return numeric_type(as_integer() * b
.as_integer());
172 return numeric_type(as_address() * b
.as_address());
174 numeric_type
operator/(const numeric_type
& b
) const
176 if(b
.t
!= VT_NAN
&& fabs(b
.as_double()) < 1e-30)
177 throw std::runtime_error("#DIV-BY-0");
178 if(t
== VT_NAN
|| b
.t
== VT_NAN
)
179 return numeric_type();
181 return numeric_type(as_double() / b
.as_double());
183 numeric_type
operator%(const numeric_type
& b
) const
185 return numeric_type(*this - b
* idiv(b
));
187 numeric_type
idiv(const numeric_type
& b
) const
189 if(b
.t
!= VT_NAN
&& fabs(b
.as_double()) < 1e-30)
190 throw std::runtime_error("#DIV-BY-0");
191 if(t
== VT_NAN
|| b
.t
== VT_NAN
)
192 return numeric_type();
193 else if(t
== VT_FLOAT
|| b
.t
== VT_FLOAT
)
194 return numeric_type(floor(as_double() / b
.as_double()));
195 else if(t
== VT_SIGNED
|| b
.t
== VT_SIGNED
)
196 return numeric_type(as_integer() / b
.as_integer());
198 return numeric_type(as_address() / b
.as_address());
202 if(ch
>= '0' && ch
<= '9')
204 if(ch
>= 'A' && ch
<= 'G')
205 hex
= (ch
- 'A') + 10;
206 if(ch
>= 'a' && ch
<= 'g')
207 hex
= (ch
- 'a') + 10;
223 numeric_type
stack_pop(std::stack
<numeric_type
>& s
, bool norm
= false)
226 throw std::runtime_error("#syntax (underflow)");
227 numeric_type r
= s
.top();
233 template<typename T
> void stack_push(std::stack
<numeric_type
>& s
, T val
)
235 s
.push(numeric_type(val
));
239 std::string
evaluate_watch(const std::string
& expr
) throw(std::bad_alloc
)
241 std::stack
<numeric_type
> s
;
243 std::string _expr
= expr
;
249 for(size_t i
= 0; i
< expr
.length(); i
++) {
253 y
= expr
.find_first_of("z", i
);
254 if(y
> expr
.length())
255 return "#syntax (noterm)";
256 t
= _expr
.substr(i
+ 1, y
- i
- 1);
257 stack_push(s
, numeric_type(t
));
261 if(i
+ 1 == expr
.length())
262 throw std::runtime_error("#syntax (noparam)");
265 stack_push(s
, a
.round(d
));
268 if(i
+ 1 == expr
.length())
269 throw std::runtime_error("#syntax (noparam)");
270 s
.top().sethex(expr
[++i
]);
273 stack_push
<double>(s
, atan(stack_pop(s
).as_double()));
278 stack_push
<double>(s
, atan2(a
.as_double(), b
.as_double()));
281 stack_push
<double>(s
, cos(stack_pop(s
).as_double()));
285 if(a
.as_double() < 0)
286 throw std::runtime_error("#NAN");
287 stack_push
<double>(s
, sqrt(a
.as_double()));
290 stack_push
<double>(s
, sin(stack_pop(s
).as_double()));
293 stack_push
<double>(s
, tan(stack_pop(s
).as_double()));
296 stack_push(s
, stack_pop(s
, true));
299 stack_push(s
, 4 * atan(1));
304 stack_push(s
, a
+ b
);
309 stack_push(s
, a
- b
);
314 stack_push(s
, a
* b
);
319 stack_push(s
, a
.idiv(b
));
324 stack_push(s
, a
/ b
);
329 stack_push(s
, a
% b
);
332 stack_push
<int8_t>(s
, lsnes_memory
.read
<uint8_t>(stack_pop(s
).as_address()));
335 stack_push
<uint8_t>(s
, lsnes_memory
.read
<uint8_t>(stack_pop(s
).as_address()));
338 stack_push
<int16_t>(s
, lsnes_memory
.read
<uint16_t>(stack_pop(s
).as_address()));
341 stack_push
<uint16_t>(s
, lsnes_memory
.read
<uint16_t>(stack_pop(s
).as_address()));
344 stack_push
<int32_t>(s
, lsnes_memory
.read
<uint32_t>(stack_pop(s
).as_address()));
347 stack_push
<uint32_t>(s
, lsnes_memory
.read
<uint32_t>(stack_pop(s
).as_address()));
350 stack_push
<int64_t>(s
, lsnes_memory
.read
<uint64_t>(stack_pop(s
).as_address()));
353 stack_push
<uint64_t>(s
, lsnes_memory
.read
<uint64_t>(stack_pop(s
).as_address()));
356 throw std::runtime_error("#syntax (illchar)");
362 return s
.top().str();
363 } catch(std::exception
& e
) {
368 std::set
<std::string
> get_watches() throw(std::bad_alloc
)
370 std::set
<std::string
> r
;
371 for(auto i
: watches
)
376 std::string
get_watchexpr_for(const std::string
& w
) throw(std::bad_alloc
)
384 void set_watchexpr_for(const std::string
& w
, const std::string
& expr
) throw(std::bad_alloc
)
386 auto& status
= platform::get_emustatus();
389 status
.set("M[" + w
+ "]", evaluate_watch(expr
));
392 status
.erase("M[" + w
+ "]");
394 information_dispatch::do_status_update();
397 void do_watch_memory()
399 auto& status
= platform::get_emustatus();
400 for(auto i
: watches
)
401 status
.set("M[" + i
.first
+ "]", evaluate_watch(i
.second
));
406 function_ptr_command
<const std::string
&> add_watch(lsnes_cmd
, "add-watch", "Add a memory watch",
407 "Syntax: add-watch <name> <expression>\nAdds a new memory watch\n",
408 [](const std::string
& t
) throw(std::bad_alloc
, std::runtime_error
) {
409 auto r
= regex("([^ \t]+)[ \t]+(|[^ \t].*)", t
, "Name and expression required.");
410 set_watchexpr_for(r
[1], r
[2]);
413 function_ptr_command
<const std::string
&> remove_watch(lsnes_cmd
, "remove-watch", "Remove a memory watch",
414 "Syntax: remove-watch <name>\nRemoves a memory watch\n",
415 [](const std::string
& t
) throw(std::bad_alloc
, std::runtime_error
) {
416 auto r
= regex("([^ \t]+)[ \t]*", t
, "Name required.");
417 set_watchexpr_for(r
[1], "");