Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / lua / memory2.cpp
blob43c59a752b9c8b290aa76e69aee268fcb655b71a
1 #include "lua/internal.hpp"
2 #include "lua/debug.hpp"
3 #include "core/memorymanip.hpp"
4 #include "core/memorywatch.hpp"
5 #include "core/instance.hpp"
6 #include "core/moviedata.hpp"
7 #include "core/moviefile.hpp"
8 #include "core/rom.hpp"
9 #include "library/sha256.hpp"
10 #include "library/skein.hpp"
11 #include "library/string.hpp"
12 #include "library/serialization.hpp"
13 #include "library/memoryspace.hpp"
14 #include "library/minmax.hpp"
15 #include "library/int24.hpp"
17 namespace
19 int handle_push_vma(lua::state& L, memory_space::region& r)
21 L.newtable();
22 L.pushstring("name");
23 L.pushlstring(r.name.c_str(), r.name.size());
24 L.settable(-3);
25 L.pushstring("address");
26 L.pushnumber(r.base);
27 L.settable(-3);
28 L.pushstring("size");
29 L.pushnumber(r.size);
30 L.settable(-3);
31 L.pushstring("last");
32 L.pushnumber(r.last_address());
33 L.settable(-3);
34 L.pushstring("readonly");
35 L.pushboolean(r.readonly);
36 L.settable(-3);
37 L.pushstring("special");
38 L.pushboolean(r.special);
39 L.settable(-3);
40 L.pushstring("endian");
41 L.pushnumber(r.endian);
42 L.settable(-3);
43 return 1;
46 template<typename T> T bswap(T val)
48 T val2 = val;
49 serialization::swap_endian(val2);
50 return val2;
53 class lua_vma
55 public:
56 lua_vma(lua::state& L, memory_space::region* r);
57 static size_t overcommit(memory_space::region* r) { return 0; }
58 int info(lua::state& L, lua::parameters& P);
59 template<class T, bool _bswap> int rw(lua::state& L, lua::parameters& P);
60 template<bool write, bool sign> int scattergather(lua::state& L, lua::parameters& P);
61 template<class T> int hash(lua::state& L, lua::parameters& P);
62 template<bool cmp> int storecmp(lua::state& L, lua::parameters& P);
63 int readregion(lua::state& L, lua::parameters& P);
64 int writeregion(lua::state& L, lua::parameters& P);
65 int cheat(lua::state& L, lua::parameters& P);
66 template<debug_context::etype type, bool reg> int registerX(lua::state& L, lua::parameters& P);
67 std::string print()
69 return vma;
71 private:
72 std::string vma;
73 uint64_t vmabase;
74 uint64_t vmasize;
75 bool ro;
78 class lua_vma_list
80 public:
81 lua_vma_list(lua::state& L);
82 static size_t overcommit() { return 0; }
83 static int create(lua::state& L, lua::parameters& P);
84 int index(lua::state& L, lua::parameters& P);
85 int newindex(lua::state& L, lua::parameters& P);
86 int call(lua::state& L, lua::parameters& P);
87 std::string print()
89 return "";
93 struct l_sha256_h
95 static sha256 create()
97 return sha256();
99 static void write(sha256& h, void* b, size_t s)
101 h.write(reinterpret_cast<uint8_t*>(b), s);
103 static std::string read(sha256& h)
105 return h.read();
109 struct l_skein_h
111 static skein::hash create()
113 return skein::hash(skein::hash::PIPE_512, 256);
115 static void write(skein::hash& h, void* b, size_t s)
117 h.write(reinterpret_cast<uint8_t*>(b), s);
119 static std::string read(skein::hash& h)
121 uint8_t buf[32];
122 h.read(buf);
123 return hex::b_to(buf, 32);
127 lua::_class<lua_vma> LUA_class_vma(lua_class_memory, "VMA", {}, {
128 {"info", &lua_vma::info},
129 {"read", &lua_vma::scattergather<false, false>},
130 {"sread", &lua_vma::scattergather<false, true>},
131 {"write", &lua_vma::scattergather<true, false>},
132 {"sbyte", &lua_vma::rw<int8_t, false>},
133 {"byte", &lua_vma::rw<uint8_t, false>},
134 {"sword", &lua_vma::rw<int16_t, false>},
135 {"word", &lua_vma::rw<uint16_t, false>},
136 {"shword", &lua_vma::rw<ss_int24_t, false>},
137 {"hword", &lua_vma::rw<ss_uint24_t, false>},
138 {"sdword", &lua_vma::rw<int32_t, false>},
139 {"dword", &lua_vma::rw<uint32_t, false>},
140 {"sqword", &lua_vma::rw<int64_t, false>},
141 {"qword", &lua_vma::rw<uint64_t, false>},
142 {"float", &lua_vma::rw<float, false>},
143 {"double", &lua_vma::rw<double, false>},
144 {"isbyte", &lua_vma::rw<int8_t, true>},
145 {"ibyte", &lua_vma::rw<uint8_t, true>},
146 {"isword", &lua_vma::rw<int16_t, true>},
147 {"iword", &lua_vma::rw<uint16_t, true>},
148 {"ishword", &lua_vma::rw<ss_int24_t, true>},
149 {"ihword", &lua_vma::rw<ss_uint24_t, true>},
150 {"isdword", &lua_vma::rw<int32_t, true>},
151 {"idword", &lua_vma::rw<uint32_t, true>},
152 {"isqword", &lua_vma::rw<int64_t, true>},
153 {"iqword", &lua_vma::rw<uint64_t, true>},
154 {"ifloat", &lua_vma::rw<float, true>},
155 {"idouble", &lua_vma::rw<double, true>},
156 {"cheat", &lua_vma::cheat},
157 {"sha256", &lua_vma::hash<l_sha256_h>},
158 {"skein", &lua_vma::hash<l_skein_h>},
159 {"store", &lua_vma::storecmp<false>},
160 {"storecmp", &lua_vma::storecmp<true>},
161 {"readregion", &lua_vma::readregion},
162 {"writeregion", &lua_vma::writeregion},
163 {"registerread", &lua_vma::registerX<debug_context::DEBUG_READ, true>},
164 {"unregisterread", &lua_vma::registerX<debug_context::DEBUG_READ, false>},
165 {"registerwrite", &lua_vma::registerX<debug_context::DEBUG_WRITE, true>},
166 {"unregisterwrite", &lua_vma::registerX<debug_context::DEBUG_WRITE, false>},
167 {"registerexec", &lua_vma::registerX<debug_context::DEBUG_EXEC, true>},
168 {"unregisterexec", &lua_vma::registerX<debug_context::DEBUG_EXEC, false>},
169 }, &lua_vma::print);
171 lua::_class<lua_vma_list> LUA_class_vmalist(lua_class_memory, "VMALIST", {
172 {"new", lua_vma_list::create},
173 }, {
174 {"__index", &lua_vma_list::index},
175 {"__newindex", &lua_vma_list::newindex},
176 {"__call", &lua_vma_list::call},
179 lua_vma::lua_vma(lua::state& L, memory_space::region* r)
181 vmabase = r->base;
182 vmasize = r->size;
183 vma = r->name;
184 ro = r->readonly;
187 int lua_vma::info(lua::state& L, lua::parameters& P)
189 for(auto i : CORE().memory->get_regions())
190 if(i->name == vma)
191 return handle_push_vma(L, *i);
192 (stringfmt() << P.get_fname() << ": Stale region").throwex();
193 return 0; //NOTREACHED
196 template<class T, bool _bswap> int lua_vma::rw(lua::state& L, lua::parameters& P)
198 auto& core = CORE();
199 uint64_t addr;
200 T val;
202 P(P.skipped(), addr);
204 if(addr > vmasize || addr > vmasize - sizeof(T))
205 throw std::runtime_error("VMA::rw<T>: Address outside VMA bounds");
206 if(P.is_novalue()) {
207 //Read.
208 T val = core.memory->read<T>(addr + vmabase);
209 if(_bswap) val = bswap(val);
210 L.pushnumber(val);
211 return 1;
212 } else if(P.is_number()) {
213 //Write.
214 if(ro)
215 (stringfmt() << P.get_fname() << ": VMA is read-only").throwex();
216 P(val);
217 if(_bswap) val = bswap(val);
218 core.memory->write<T>(addr + vmabase, val);
219 return 0;
220 } else
221 P.expected("number or nil");
222 return 0; //NOTREACHED
225 template<bool write, bool sign> int lua_vma::scattergather(lua::state& L, lua::parameters& P)
227 auto& core = CORE();
228 uint64_t val = 0;
229 unsigned shift = 0;
230 uint64_t addr = 0;
232 P(P.skipped());
233 if(write)
234 P(val);
236 while(!P.is_novalue()) {
237 if(P.is_boolean()) {
238 if(P.arg<bool>())
239 addr++;
240 else
241 addr--;
242 } else
243 addr = P.arg<uint64_t>();
244 if(write)
245 core.memory->write<uint8_t>(addr + vmabase, val >> shift);
246 else
247 val = val + ((uint64_t)core.memory->read<uint8_t>(addr + vmabase) << shift);
248 shift += 8;
250 if(!write) {
251 int64_t sval = val;
252 if(val >= (1ULL << (shift - 1))) sval -= (1ULL << shift);
253 if(sign) L.pushnumber(sval); else L.pushnumber(val);
255 return write ? 0 : 1;
258 template<class T> int lua_vma::hash(lua::state& L, lua::parameters& P)
260 auto& core = CORE();
261 uint64_t addr, size, rows, stride = 0;
263 P(P.skipped(), addr, size, P.optional(rows, 1));
264 if(rows > 1) P(stride);
266 //First verify that all reads are to the region.
267 if(!memoryspace_row_limited(addr, size, rows, stride, vmasize))
268 throw std::runtime_error("Region out of range");
270 auto hstate = T::create();
271 //Try to map the VMA.
272 char* vmabuf = core.memory->get_physical_mapping(vmabase, vmasize);
273 if(vmabuf) {
274 for(uint64_t i = 0; i < rows; i++) {
275 T::write(hstate, vmabuf + addr, size);
276 addr += stride;
278 } else {
279 uint8_t buf[512]; //Must be power of 2.
280 unsigned bf = 0;
281 for(uint64_t i = 0; i < rows; i++) {
282 for(uint64_t j = 0; j < size; j++) {
283 buf[bf] = core.memory->read<uint8_t>(vmabase + addr + j);
284 bf = (bf + 1) & (sizeof(buf) - 1);
285 if(!bf)
286 T::write(hstate, buf, sizeof(buf));
288 addr += stride;
290 if(bf)
291 T::write(hstate, buf, bf);
293 L.pushlstring(T::read(hstate));
294 return 1;
297 int lua_vma::readregion(lua::state& L, lua::parameters& P)
299 auto& core = CORE();
300 uint64_t addr, size;
302 P(P.skipped(), addr, size);
304 if(addr >= vmasize || size > vmasize || addr + size > vmasize)
305 throw std::runtime_error("Read out of range");
307 L.newtable();
308 char* vmabuf = core.memory->get_physical_mapping(vmabase, vmasize);
309 if(vmabuf) {
310 uint64_t ctr = 1;
311 for(size_t i = 0; i < size; i++) {
312 L.pushnumber(ctr++);
313 L.pushnumber(static_cast<unsigned char>(vmabuf[addr + i]));
314 L.settable(-3);
316 } else {
317 uint64_t ctr = 1;
318 for(size_t i = 0; i < size; i++) {
319 L.pushnumber(ctr++);
320 L.pushnumber(core.memory->read<uint8_t>(addr + i));
321 L.settable(-3);
324 return 1;
327 int lua_vma::writeregion(lua::state& L, lua::parameters& P)
329 auto& core = CORE();
330 uint64_t addr;
331 int ltbl;
333 P(P.skipped(), addr, P.table(ltbl));
335 auto g = core.memory->lookup(vmabase);
336 if(!g.first || g.first->readonly)
337 throw std::runtime_error("Memory address is read-only");
338 if(addr >= vmasize)
339 throw std::runtime_error("Write out of range");
341 uint64_t ctr = 1;
342 char* vmabuf = core.memory->get_physical_mapping(vmabase, vmasize);
343 if(vmabuf) {
344 for(size_t i = 0;; i++) {
345 L.pushnumber(ctr++);
346 L.gettable(ltbl);
347 if(L.type(-1) == LUA_TNIL)
348 break;
349 if(addr + i >= vmasize)
350 throw std::runtime_error("Write out of range");
351 vmabuf[addr + i] = L.tointeger(-1);
352 L.pop(1);
354 } else {
355 for(size_t i = 0;; i++) {
356 L.pushnumber(ctr++);
357 L.gettable(ltbl);
358 if(L.type(-1) == LUA_TNIL)
359 break;
360 if(addr + i >= vmasize)
361 throw std::runtime_error("Write out of range");
362 core.memory->write<uint8_t>(vmabase + addr + i, L.tointeger(-1));
363 L.pop(1);
366 return 0;
369 template<bool cmp> int lua_vma::storecmp(lua::state& L, lua::parameters& P)
371 auto& core = CORE();
372 uint64_t addr, daddr, size, rows, stride = 0;
373 bool equals = true;
375 P(P.skipped(), addr, daddr, size, P.optional(rows, 1));
376 if(rows > 1) P(stride);
378 //First verify that all reads are to the region.
379 if(!memoryspace_row_limited(addr, size, rows, stride, vmasize))
380 throw std::runtime_error("Source out of range");
382 //Calculate new size of target.
383 auto& h = core.mlogic->get_mfile().host_memory;
384 size_t rsize = size * rows;
385 if(size && rsize / size != rows)
386 throw std::runtime_error("Copy size out of range");
387 if((size_t)daddr + rsize < rsize)
388 throw std::runtime_error("Target out of range");
389 if(daddr + rsize > h.size()) {
390 h.resize(daddr + rsize);
391 equals = false;
394 //Try to map the VMA.
395 char* vmabuf = core.memory->get_physical_mapping(vmabase, vmasize);
396 if(vmabuf) {
397 for(uint64_t i = 0; i < rows; i++) {
398 bool eq = (cmp && !memcmp(&h[daddr], vmabuf + addr, size));
399 if(!eq)
400 memcpy(&h[daddr], vmabuf + addr, size);
401 equals &= eq;
402 addr += stride;
403 daddr += size;
405 } else {
406 for(uint64_t i = 0; i < rows; i++) {
407 for(uint64_t j = 0; j < size; j++) {
408 uint8_t byte = core.memory->read<uint8_t>(vmabase + addr + j);
409 bool eq = (cmp && ((uint8_t)h[daddr + j] == byte));
410 h[daddr + j] = byte;
411 equals &= eq;
413 addr += stride;
414 daddr += size;
417 if(cmp) L.pushboolean(equals);
418 return cmp ? 1 : 0;
421 template<debug_context::etype type, bool reg> int lua_vma::registerX(lua::state& L, lua::parameters& P)
423 uint64_t addr;
424 int lfn;
425 P(P.skipped(), addr, P.function(lfn));
427 if(reg) {
428 handle_registerX<type>(L, vmabase + addr, lfn);
429 L.pushvalue(lfn);
430 return 1;
431 } else {
432 handle_unregisterX<type>(L, vmabase + addr, lfn);
433 return 0;
437 int lua_vma::cheat(lua::state& L, lua::parameters& P)
439 auto& core = CORE();
440 uint64_t addr, value;
442 P(P.skipped(), addr);
443 if(addr >= vmasize)
444 throw std::runtime_error("Address out of range");
445 if(P.is_novalue()) {
446 core.dbg->clear_cheat(vmabase + addr);
447 } else {
448 P(value);
449 core.dbg->set_cheat(vmabase + addr, value);
451 return 0;
454 lua_vma_list::lua_vma_list(lua::state& L)
458 int lua_vma_list::create(lua::state& L, lua::parameters& P)
460 lua::_class<lua_vma_list>::create(L);
461 return 1;
464 int lua_vma_list::call(lua::state& L, lua::parameters& P)
466 L.newtable();
467 size_t key = 1;
468 for(auto i : CORE().memory->get_regions()) {
469 L.pushnumber(key++);
470 L.pushlstring(i->name);
471 L.rawset(-3);
473 return 1;
476 int lua_vma_list::index(lua::state& L, lua::parameters& P)
478 std::string vma;
480 P(P.skipped(), vma);
482 auto l = CORE().memory->get_regions();
483 size_t j;
484 std::list<memory_space::region*>::iterator i;
485 for(i = l.begin(), j = 0; i != l.end(); i++, j++)
486 if((*i)->name == vma) {
487 lua::_class<lua_vma>::create(L, *i);
488 return 1;
490 (stringfmt() << P.get_fname() << ": No such VMA").throwex();
491 return 0; //NOTREACHED
494 int lua_vma_list::newindex(lua::state& L, lua::parameters& P)
496 throw std::runtime_error("Writing is not allowed");