1 #include "core/command.hpp"
2 #include "lua/internal.hpp"
3 #include "core/debug.hpp"
4 #include "core/memorymanip.hpp"
5 #include "core/memorywatch.hpp"
6 #include "core/moviedata.hpp"
7 #include "core/moviefile.hpp"
8 #include "core/rom.hpp"
9 #include "library/sha256.hpp"
10 #include "library/string.hpp"
11 #include "library/minmax.hpp"
12 #include "library/hex.hpp"
13 #include "library/int24.hpp"
17 uint64_t get_vmabase(const std::string
& vma
)
19 for(auto i
: lsnes_memory
.get_regions())
22 throw std::runtime_error("No such VMA");
25 uint64_t get_read_address(lua::parameters
& P
)
29 vmabase
= get_vmabase(P
.arg
<std::string
>());
30 auto addr
= P
.arg
<uint64_t>();
31 return addr
+ vmabase
;
34 template<typename T
, T (memory_space::*rfun
)(uint64_t addr
),
35 bool (memory_space::*wfun
)(uint64_t addr
, T value
)>
36 void do_rw(lua::state
& L
, uint64_t addr
, bool wrflag
)
39 T value
= L
.get_numeric_argument
<T
>(3, "aperture(write)");
40 (lsnes_memory
.*wfun
)(addr
, value
);
42 L
.pushnumber(static_cast<T
>((lsnes_memory
.*rfun
)(addr
)));
45 template<typename T
, T (memory_space::*rfun
)(uint64_t addr
)>
46 int lua_read_memory(lua::state
& L
, lua::parameters
& P
)
48 auto addr
= get_read_address(P
);
49 L
.pushnumber(static_cast<T
>((lsnes_memory
.*rfun
)(addr
)));
53 template<typename T
, bool (memory_space::*wfun
)(uint64_t addr
, T value
)>
54 int lua_write_memory(lua::state
& L
, lua::parameters
& P
)
56 auto addr
= get_read_address(P
);
58 (lsnes_memory
.*wfun
)(addr
, value
);
66 lua_mmap_struct(lua::state
& L
);
72 int index(lua::state
& L
, lua::parameters
& P
)
74 const char* c
= L
.tostring(2);
80 if(!mappings
.count(c2
)) {
84 auto& x
= mappings
[c2
];
85 x
.rw(L
, x
.addr
, false);
88 int newindex(lua::state
& L
, lua::parameters
& P
)
90 const char* c
= L
.tostring(2);
94 if(!mappings
.count(c2
))
96 auto& x
= mappings
[c2
];
97 x
.rw(L
, x
.addr
, true);
100 static int create(lua::state
& L
, lua::parameters
& P
)
102 lua::_class
<lua_mmap_struct
>::create(L
);
105 int map(lua::state
& L
, lua::parameters
& P
);
108 size_t s
= mappings
.size();
109 return (stringfmt() << s
<< " " << ((s
!= 1) ? "mappings" : "mapping")).str();
115 mapping(uint64_t _addr
, void (*_rw
)(lua::state
& L
, uint64_t addr
, bool wrflag
))
116 : addr(_addr
), rw(_rw
)
120 void (*rw
)(lua::state
& L
, uint64_t addr
, bool wrflag
);
122 std::map
<std::string
, mapping
> mappings
;
127 template<typename T
, T (memory_space::*rfun
)(uint64_t addr
)>
128 int aperture_read_fun(lua_State
* _L
)
130 lua::state
& mL
= *reinterpret_cast<lua::state
*>(lua_touserdata(_L
, lua_upvalueindex(3)));
131 lua::state
L(mL
, _L
);
133 uint64_t base
= L
.tonumber(lua_upvalueindex(1));
134 uint64_t size
= 0xFFFFFFFFFFFFFFFFULL
;
135 if(L
.type(lua_upvalueindex(2)) == LUA_TNUMBER
)
136 size
= L
.tonumber(lua_upvalueindex(2));
137 uint64_t addr
= L
.get_numeric_argument
<uint64_t>(2, "aperture(read)");
138 if(addr
> size
|| addr
+ base
< addr
) {
143 L
.pushnumber(static_cast<T
>((lsnes_memory
.*rfun
)(addr
)));
147 template<typename T
, bool (memory_space::*wfun
)(uint64_t addr
, T value
)>
148 int aperture_write_fun(lua_State
* _L
)
150 lua::state
& mL
= *reinterpret_cast<lua::state
*>(lua_touserdata(_L
, lua_upvalueindex(3)));
151 lua::state
L(mL
, _L
);
153 uint64_t base
= L
.tonumber(lua_upvalueindex(1));
154 uint64_t size
= 0xFFFFFFFFFFFFFFFFULL
;
155 if(L
.type(lua_upvalueindex(2)) == LUA_TNUMBER
)
156 size
= L
.tonumber(lua_upvalueindex(2));
157 uint64_t addr
= L
.get_numeric_argument
<uint64_t>(2, "aperture(write)");
158 if(addr
> size
|| addr
+ base
< addr
)
161 T value
= L
.get_numeric_argument
<T
>(3, "aperture(write)");
162 (lsnes_memory
.*wfun
)(addr
, value
);
166 template<typename T
, T (memory_space::*rfun
)(uint64_t addr
), bool (memory_space::*wfun
)(uint64_t addr
,
168 void aperture_make_fun(lua::state
& L
, uint64_t base
, uint64_t size
)
172 L
.pushstring( "__index");
178 L
.pushlightuserdata(&L
);
179 L
.pushcclosure(aperture_read_fun
<T
, rfun
>, 3);
181 L
.pushstring("__newindex");
187 L
.pushlightuserdata(&L
);
188 L
.pushcclosure(aperture_write_fun
<T
, wfun
>, 3);
193 struct lua_debug_callback
200 static int dtor(lua_State
* L
)
202 lua_debug_callback
* D
= (lua_debug_callback
*)lua_touserdata(L
, 1);
205 int _dtor(lua_State
* L
);
207 std::map
<uint64_t, std::list
<lua_debug_callback
*>> cbs
;
209 int lua_debug_callback::_dtor(lua_State
* L
)
213 lua_pushlightuserdata(L
, &type
);
215 lua_rawset(L
, LUA_REGISTRYINDEX
);
216 debug_remove_callback(addr
, type
, h
);
217 for(auto j
= cbs
[addr
].begin(); j
!= cbs
[addr
].end(); j
++)
222 if(cbs
[addr
].empty())
227 void do_lua_error(lua::state
& L
, int ret
)
232 messages
<< "Error in Lua memory callback: " << L
.get_string(-1, "errhnd") << std::endl
;
236 messages
<< "Error in Lua memory callback (Out of memory)" << std::endl
;
239 messages
<< "Error in Lua memory callback (Double fault)" << std::endl
;
242 messages
<< "Error in Lua memory callback (\?\?\?)" << std::endl
;
247 template<debug_type type
, bool reg
>
248 void handle_registerX(lua::state
& L
, uint64_t addr
, int lfn
)
250 auto& cbl
= cbs
[addr
];
252 //Put the context in userdata so it can be gc'd when Lua context is terminated.
253 lua_debug_callback
* D
= (lua_debug_callback
*)L
.newuserdata(sizeof(lua_debug_callback
));
255 L
.pushstring("__gc");
256 L
.pushcclosure(&lua_debug_callback::dtor
, 0);
259 L
.pushlightuserdata(&D
->addr
);
261 L
.rawset(LUA_REGISTRYINDEX
);
262 L
.pop(1); //Pop the copy of object.
269 D
->lua_fn
= L
.topointer(lfn
);
270 lua::state
* LL
= &L
.get_master();
272 if(type
!= DEBUG_TRACE
)
273 D
->h
= debug_add_callback(addr
, type
, [LL
, D2
](uint64_t addr
, uint64_t value
) {
274 LL
->pushlightuserdata(D2
);
275 LL
->rawget(LUA_REGISTRYINDEX
);
276 LL
->pushnumber(addr
);
277 LL
->pushnumber(value
);
278 do_lua_error(*LL
, LL
->pcall(2, 0, 0));
280 LL
->pushlightuserdata(&D
->addr
);
282 LL
->rawset(LUA_REGISTRYINDEX
);
283 D
->_dtor(LL
->handle());
286 D
->h
= debug_add_trace_callback(addr
, [LL
, D2
](uint64_t proc
, const char* str
) {
287 LL
->pushlightuserdata(D2
);
288 LL
->rawget(LUA_REGISTRYINDEX
);
289 LL
->pushnumber(proc
);
291 do_lua_error(*LL
, LL
->pcall(2, 0, 0));
293 LL
->pushlightuserdata(&D
->addr
);
295 LL
->rawset(LUA_REGISTRYINDEX
);
296 D
->_dtor(LL
->handle());
298 L
.pushlightuserdata(D2
);
300 L
.rawset(LUA_REGISTRYINDEX
);
303 template<debug_type type
, bool reg
>
304 void handle_unregisterX(lua::state
& L
, uint64_t addr
, int lfn
)
308 auto& cbl
= cbs
[addr
];
309 for(auto i
= cbl
.begin(); i
!= cbl
.end(); i
++) {
310 if((*i
)->type
!= type
) continue;
311 if(L
.topointer(lfn
) != (*i
)->lua_fn
) continue;
312 L
.pushlightuserdata(&(*i
)->type
);
314 L
.rawset(LUA_REGISTRYINDEX
);
315 (*i
)->_dtor(L
.handle());
316 //Lua will GC the object.
321 template<debug_type type
, bool reg
>
322 int lua_registerX(lua::state
& L
, lua::parameters
& P
)
326 if(P
.is_nil() && type
!= DEBUG_TRACE
) {
327 addr
= 0xFFFFFFFFFFFFFFFFULL
;
329 } else if(type
!= DEBUG_TRACE
)
330 addr
= get_read_address(P
);
336 handle_registerX
<type
, reg
>(L
, addr
, lfn
);
340 handle_unregisterX
<type
, reg
>(L
, addr
, lfn
);
345 command::fnptr
<> callbacks_show_lua(lsnes_cmd
, "show-lua-callbacks", "", "",
346 []() throw(std::bad_alloc
, std::runtime_error
) {
348 for(auto& j
: i
.second
)
349 messages
<< "addr=" << j
->addr
<< " type=" << j
->type
<< " handle="
350 << j
->h
.handle
<< " dead=" << j
->dead
<< " lua_fn="
351 << j
->lua_fn
<< std::endl
;
354 template<typename T
, T (memory_space::*rfun
)(uint64_t addr
), bool (memory_space::*wfun
)(uint64_t addr
,
356 int lua_mmap_memory(lua::state
& L
, lua::parameters
& P
)
359 aperture_make_fun
<T
, rfun
, wfun
>(L
.get_master(), 0, 0xFFFFFFFFFFFFFFFFULL
);
362 auto addr
= get_read_address(P
);
363 auto size
= P
.arg
<uint64_t>();
365 throw std::runtime_error("Aperture with zero size is not valid");
366 aperture_make_fun
<T
, rfun
, wfun
>(L
.get_master(), addr
, size
- 1);
370 int handle_push_vma(lua::state
& L
, memory_region
& r
)
373 L
.pushstring("region_name");
374 L
.pushlstring(r
.name
.c_str(), r
.name
.size());
376 L
.pushstring("baseaddr");
377 L
.pushnumber(r
.base
);
379 L
.pushstring("size");
380 L
.pushnumber(r
.size
);
382 L
.pushstring("lastaddr");
383 L
.pushnumber(r
.last_address());
385 L
.pushstring("readonly");
386 L
.pushboolean(r
.readonly
);
388 L
.pushstring("iospace");
389 L
.pushboolean(r
.special
);
391 L
.pushstring("native_endian");
392 L
.pushboolean(r
.endian
== 0);
394 L
.pushstring("endian");
395 L
.pushnumber(r
.endian
);
400 template<bool write
, bool sign
> int memory_scattergather(lua::state
& L
, lua::parameters
& P
)
405 uint64_t vmabase
= 0;
407 val
= P
.arg
<uint64_t>();
414 } else if(P
.is_string()) {
415 vmabase
= get_vmabase(P
.arg
<std::string
>());
418 addr
= P
.arg
<uint64_t>();
420 lsnes_memory
.write
<uint8_t>(addr
+ vmabase
, val
>> shift
);
422 val
= val
+ ((uint64_t)lsnes_memory
.read
<uint8_t>(addr
+ vmabase
) << shift
);
427 if(val
>= (1ULL << (shift
- 1))) sval
-= (1ULL << shift
);
428 if(sign
) L
.pushnumber(sval
); else L
.pushnumber(val
);
430 return write
? 0 : 1;
433 const char* hexes
= "0123456789ABCDEF";
435 #define BLOCKSIZE 256
437 int vma_count(lua::state
& L
, lua::parameters
& P
)
439 L
.pushnumber(lsnes_memory
.get_regions().size());
443 int cheat(lua::state
& L
, lua::parameters
& P
)
445 uint64_t addr
, value
;
447 addr
= get_read_address(P
);
450 debug_clear_cheat(addr
);
453 debug_set_cheat(addr
, value
);
458 int setxmask(lua::state
& L
, lua::parameters
& P
)
460 auto value
= P
.arg
<uint64_t>();
461 debug_setxmask(value
);
465 int read_vma(lua::state
& L
, lua::parameters
& P
)
471 std::list
<memory_region
*> regions
= lsnes_memory
.get_regions();
473 for(auto i
= regions
.begin(); i
!= regions
.end(); i
++, j
++)
475 return handle_push_vma(L
, **i
);
480 int find_vma(lua::state
& L
, lua::parameters
& P
)
486 auto r
= lsnes_memory
.lookup(addr
);
488 return handle_push_vma(L
, *r
.first
);
493 int hash_state(lua::state
& L
, lua::parameters
& P
)
496 auto x
= our_rom
.save_core_state();
497 size_t offset
= x
.size() - 32;
498 L
.pushlstring(hex::b_to((uint8_t*)&x
[offset
], 32));
502 int hash_region(lua::state
& L
, lua::parameters
& P
)
507 addr
= get_read_address(P
);
510 char buffer
[BLOCKSIZE
];
512 while(size
> BLOCKSIZE
) {
513 for(size_t i
= 0; i
< BLOCKSIZE
; i
++)
514 buffer
[i
] = lsnes_memory
.read
<uint8_t>(addr
+ i
);
515 h
.write(buffer
, BLOCKSIZE
);
519 for(size_t i
= 0; i
< size
; i
++)
520 buffer
[i
] = lsnes_memory
.read
<uint8_t>(addr
+ i
);
521 h
.write(buffer
, size
);
523 L
.pushlstring(hash
.c_str(), 64);
527 int readregion(lua::state
& L
, lua::parameters
& P
)
531 addr
= get_read_address(P
);
535 char buffer
[BLOCKSIZE
];
538 size_t rsize
= min(size
, static_cast<uint64_t>(BLOCKSIZE
));
539 lsnes_memory
.read_range(addr
, buffer
, rsize
);
540 for(size_t i
= 0; i
< rsize
; i
++) {
542 L
.pushnumber(static_cast<unsigned char>(buffer
[i
]));
551 int writeregion(lua::state
& L
, lua::parameters
& P
)
556 addr
= get_read_address(P
);
557 P(size
, P
.table(ltbl
));
559 char buffer
[BLOCKSIZE
];
562 size_t rsize
= min(size
, static_cast<uint64_t>(BLOCKSIZE
));
563 for(size_t i
= 0; i
< rsize
; i
++) {
566 buffer
[i
] = L
.tointeger(-1);
569 lsnes_memory
.write_range(addr
, buffer
, rsize
);
576 lua::functions
memoryfuncs(lua_func_misc
, "memory", {
577 {"vma_count", vma_count
},
579 {"setxmask", setxmask
},
580 {"read_vma", read_vma
},
581 {"find_vma", find_vma
},
582 {"hash_state", hash_state
},
583 {"hash_region", hash_region
},
584 {"readregion", readregion
},
585 {"writeregion", readregion
},
586 {"read_sg", memory_scattergather
<false, false>},
587 {"sread_sg", memory_scattergather
<false, true>},
588 {"write_sg", memory_scattergather
<true, false>},
589 {"readbyte", lua_read_memory
<uint8_t, &memory_space::read
<uint8_t>>},
590 {"readsbyte", lua_read_memory
<int8_t, &memory_space::read
<int8_t>>},
591 {"readword", lua_read_memory
<uint16_t, &memory_space::read
<uint16_t>>},
592 {"readsword", lua_read_memory
<int16_t, &memory_space::read
<int16_t>>},
593 {"readhword", lua_read_memory
<ss_uint24_t
, &memory_space::read
<ss_uint24_t
>>},
594 {"readshword", lua_read_memory
<ss_int24_t
, &memory_space::read
<ss_int24_t
>>},
595 {"readdword", lua_read_memory
<uint32_t, &memory_space::read
<uint32_t>>},
596 {"readsdword", lua_read_memory
<int32_t, &memory_space::read
<int32_t>>},
597 {"readqword", lua_read_memory
<uint64_t, &memory_space::read
<uint64_t>>},
598 {"readsqword", lua_read_memory
<int64_t, &memory_space::read
<int64_t>>},
599 {"readfloat", lua_read_memory
<float, &memory_space::read
<float>>},
600 {"readdouble", lua_read_memory
<double, &memory_space::read
<double>>},
601 {"writebyte", lua_write_memory
<uint8_t, &memory_space::write
<uint8_t>>},
602 {"writeword", lua_write_memory
<uint16_t, &memory_space::write
<uint16_t>>},
603 {"writehword", lua_write_memory
<ss_uint24_t
, &memory_space::write
<ss_uint24_t
>>},
604 {"writedword", lua_write_memory
<uint32_t, &memory_space::write
<uint32_t>>},
605 {"writeqword", lua_write_memory
<uint64_t, &memory_space::write
<uint64_t>>},
606 {"writefloat", lua_write_memory
<float, &memory_space::write
<float>>},
607 {"writedouble", lua_write_memory
<double, &memory_space::write
<double>>},
608 {"mapbyte", lua_mmap_memory
<uint8_t, &memory_space::read
<uint8_t>, &memory_space::write
<uint8_t>>},
609 {"mapsbyte", lua_mmap_memory
<int8_t, &memory_space::read
<int8_t>, &memory_space::write
<int8_t>>},
610 {"mapword", lua_mmap_memory
<uint16_t, &memory_space::read
<uint16_t>, &memory_space::write
<uint16_t>>},
611 {"mapsword", lua_mmap_memory
<int16_t, &memory_space::read
<int16_t>, &memory_space::write
<int16_t>>},
612 {"maphword", lua_mmap_memory
<ss_uint24_t
, &memory_space::read
<ss_uint24_t
>,
613 &memory_space::write
<ss_uint24_t
>>},
614 {"mapshword", lua_mmap_memory
<ss_int24_t
, &memory_space::read
<ss_int24_t
>,
615 &memory_space::write
<ss_int24_t
>>},
616 {"mapdword", lua_mmap_memory
<uint32_t, &memory_space::read
<uint32_t>,
617 &memory_space::write
<uint32_t>>},
618 {"mapsdword", lua_mmap_memory
<int32_t, &memory_space::read
<int32_t>, &memory_space::write
<int32_t>>},
619 {"mapqword", lua_mmap_memory
<uint64_t, &memory_space::read
<uint64_t>,
620 &memory_space::write
<uint64_t>>},
621 {"mapsqword", lua_mmap_memory
<int64_t, &memory_space::read
<int64_t>, &memory_space::write
<int64_t>>},
622 {"mapfloat", lua_mmap_memory
<float, &memory_space::read
<float>, &memory_space::write
<float>>},
623 {"mapdouble", lua_mmap_memory
<double, &memory_space::read
<double>, &memory_space::write
<double>>},
624 {"registerread", lua_registerX
<DEBUG_READ
, true>},
625 {"unregisterread", lua_registerX
<DEBUG_READ
, false>},
626 {"registerwrite", lua_registerX
<DEBUG_WRITE
, true>},
627 {"unregisterwrite", lua_registerX
<DEBUG_WRITE
, false>},
628 {"registerexec", lua_registerX
<DEBUG_EXEC
, true>},
629 {"unregisterexec", lua_registerX
<DEBUG_EXEC
, false>},
630 {"registertrace", lua_registerX
<DEBUG_TRACE
, true>},
631 {"unregistertrace", lua_registerX
<DEBUG_TRACE
, false>},
634 lua::_class
<lua_mmap_struct
> class_mmap_struct(lua_class_memory
, "MMAP_STRUCT", {
635 {"new", &lua_mmap_struct::create
},
637 {"__index", &lua_mmap_struct::index
},
638 {"__newindex", &lua_mmap_struct::newindex
},
639 {"__call", &lua_mmap_struct::map
},
640 }, &lua_mmap_struct::print
);
643 int lua_mmap_struct::map(lua::state
& L
, lua::parameters
& P
)
645 std::string name
, type
;
646 uint64_t vmabase
= 0, addr
;
648 P(P
.skipped(), name
);
650 vmabase
= get_vmabase(P
.arg
<std::string
>());
655 mappings
[name
] = mapping(addr
, do_rw
<uint8_t, &memory_space::read
<uint8_t>,
656 &memory_space::write
<uint8_t>>);
657 else if(type
== "sbyte")
658 mappings
[name
] = mapping(addr
, do_rw
<int8_t, &memory_space::read
<int8_t>,
659 &memory_space::write
<int8_t>>);
660 else if(type
== "word")
661 mappings
[name
] = mapping(addr
, do_rw
<uint16_t, &memory_space::read
<uint16_t>,
662 &memory_space::write
<uint16_t>>);
663 else if(type
== "sword")
664 mappings
[name
] = mapping(addr
, do_rw
<int16_t, &memory_space::read
<int16_t>,
665 &memory_space::write
<int16_t>>);
666 else if(type
== "hword")
667 mappings
[name
] = mapping(addr
, do_rw
<ss_uint24_t
, &memory_space::read
<ss_uint24_t
>,
668 &memory_space::write
<ss_uint24_t
>>);
669 else if(type
== "shword")
670 mappings
[name
] = mapping(addr
, do_rw
<ss_int24_t
, &memory_space::read
<ss_int24_t
>,
671 &memory_space::write
<ss_int24_t
>>);
672 else if(type
== "dword")
673 mappings
[name
] = mapping(addr
, do_rw
<uint32_t, &memory_space::read
<uint32_t>,
674 &memory_space::write
<uint32_t>>);
675 else if(type
== "sdword")
676 mappings
[name
] = mapping(addr
, do_rw
<int32_t, &memory_space::read
<int32_t>,
677 &memory_space::write
<int32_t>>);
678 else if(type
== "qword")
679 mappings
[name
] = mapping(addr
, do_rw
<uint64_t, &memory_space::read
<uint64_t>,
680 &memory_space::write
<uint64_t>>);
681 else if(type
== "sqword")
682 mappings
[name
] = mapping(addr
, do_rw
<int64_t, &memory_space::read
<int64_t>,
683 &memory_space::write
<int64_t>>);
684 else if(type
== "float")
685 mappings
[name
] = mapping(addr
, do_rw
<float, &memory_space::read
<float>,
686 &memory_space::write
<float>>);
687 else if(type
== "double")
688 mappings
[name
] = mapping(addr
, do_rw
<double, &memory_space::read
<double>,
689 &memory_space::write
<double>>);
691 (stringfmt() << P
.get_fname() << ": Bad type").throwex();
695 lua_mmap_struct::lua_mmap_struct(lua::state
& L
)