Lua: memory2: More functions corresponding to memory.*
[lsnes.git] / src / lua / memory2.cpp
blob9aecd67777bf8feb02b573e1fd856723d7d00966
1 #include "lua/internal.hpp"
2 #include "lua/debug.hpp"
3 #include "core/memorymanip.hpp"
4 #include "core/memorywatch.hpp"
5 #include "core/moviedata.hpp"
6 #include "core/moviefile.hpp"
7 #include "core/rom.hpp"
8 #include "library/sha256.hpp"
9 #include "library/skein.hpp"
10 #include "library/string.hpp"
11 #include "library/serialization.hpp"
12 #include "library/minmax.hpp"
13 #include "library/int24.hpp"
15 namespace
17 int handle_push_vma(lua::state& L, memory_region& r)
19 L.newtable();
20 L.pushstring("name");
21 L.pushlstring(r.name.c_str(), r.name.size());
22 L.settable(-3);
23 L.pushstring("address");
24 L.pushnumber(r.base);
25 L.settable(-3);
26 L.pushstring("size");
27 L.pushnumber(r.size);
28 L.settable(-3);
29 L.pushstring("last");
30 L.pushnumber(r.last_address());
31 L.settable(-3);
32 L.pushstring("readonly");
33 L.pushboolean(r.readonly);
34 L.settable(-3);
35 L.pushstring("special");
36 L.pushboolean(r.special);
37 L.settable(-3);
38 L.pushstring("endian");
39 L.pushnumber(r.endian);
40 L.settable(-3);
41 return 1;
44 template<typename T> T bswap(T val)
46 T val2 = val;
47 serialization::swap_endian(val2);
48 return val2;
51 class lua_vma
53 public:
54 lua_vma(lua::state& L, memory_region* r);
55 int info(lua::state& L, lua::parameters& P);
56 template<class T, bool _bswap> int rw(lua::state& L, lua::parameters& P);
57 template<bool write, bool sign> int scattergather(lua::state& L, lua::parameters& P);
58 template<class T> int hash(lua::state& L, lua::parameters& P);
59 template<bool cmp> int storecmp(lua::state& L, lua::parameters& P);
60 int readregion(lua::state& L, lua::parameters& P);
61 int writeregion(lua::state& L, lua::parameters& P);
62 int cheat(lua::state& L, lua::parameters& P);
63 template<debug_type type, bool reg> int registerX(lua::state& L, lua::parameters& P);
64 std::string print()
66 return vma;
68 private:
69 std::string vma;
70 uint64_t vmabase;
71 uint64_t vmasize;
72 bool ro;
75 class lua_vma_list
77 public:
78 lua_vma_list(lua::state& L);
79 static int create(lua::state& L, lua::parameters& P);
80 int index(lua::state& L, lua::parameters& P);
81 int newindex(lua::state& L, lua::parameters& P);
82 int call(lua::state& L, lua::parameters& P);
83 std::string print()
85 return "";
89 struct l_sha256_h
91 static sha256 create()
93 return sha256();
95 static void write(sha256& h, void* b, size_t s)
97 h.write(reinterpret_cast<uint8_t*>(b), s);
99 static std::string read(sha256& h)
101 return h.read();
105 struct l_skein_h
107 static skein::hash create()
109 return skein::hash(skein::hash::PIPE_512, 256);
111 static void write(skein::hash& h, void* b, size_t s)
113 h.write(reinterpret_cast<uint8_t*>(b), s);
115 static std::string read(skein::hash& h)
117 uint8_t buf[32];
118 h.read(buf);
119 return hex::b_to(buf, 32);
123 lua::_class<lua_vma> class_vma(lua_class_memory, "VMA", {}, {
124 {"info", &lua_vma::info},
125 {"read", &lua_vma::scattergather<false, false>},
126 {"sread", &lua_vma::scattergather<false, true>},
127 {"write", &lua_vma::scattergather<true, false>},
128 {"sbyte", &lua_vma::rw<int8_t, false>},
129 {"byte", &lua_vma::rw<uint8_t, false>},
130 {"sword", &lua_vma::rw<int16_t, false>},
131 {"word", &lua_vma::rw<uint16_t, false>},
132 {"shword", &lua_vma::rw<ss_int24_t, false>},
133 {"hword", &lua_vma::rw<ss_uint24_t, false>},
134 {"sdword", &lua_vma::rw<int32_t, false>},
135 {"dword", &lua_vma::rw<uint32_t, false>},
136 {"sqword", &lua_vma::rw<int64_t, false>},
137 {"qword", &lua_vma::rw<uint64_t, false>},
138 {"float", &lua_vma::rw<float, false>},
139 {"double", &lua_vma::rw<double, false>},
140 {"isbyte", &lua_vma::rw<int8_t, true>},
141 {"ibyte", &lua_vma::rw<uint8_t, true>},
142 {"isword", &lua_vma::rw<int16_t, true>},
143 {"iword", &lua_vma::rw<uint16_t, true>},
144 {"ishword", &lua_vma::rw<ss_int24_t, true>},
145 {"ihword", &lua_vma::rw<ss_uint24_t, true>},
146 {"isdword", &lua_vma::rw<int32_t, true>},
147 {"idword", &lua_vma::rw<uint32_t, true>},
148 {"isqword", &lua_vma::rw<int64_t, true>},
149 {"iqword", &lua_vma::rw<uint64_t, true>},
150 {"ifloat", &lua_vma::rw<float, true>},
151 {"idouble", &lua_vma::rw<double, true>},
152 {"cheat", &lua_vma::cheat},
153 {"sha256", &lua_vma::hash<l_sha256_h>},
154 {"skein", &lua_vma::hash<l_skein_h>},
155 {"store", &lua_vma::storecmp<false>},
156 {"storecmp", &lua_vma::storecmp<true>},
157 {"readregion", &lua_vma::readregion},
158 {"writeregion", &lua_vma::writeregion},
159 {"registerread", &lua_vma::registerX<DEBUG_READ, true>},
160 {"unregisterread", &lua_vma::registerX<DEBUG_READ, false>},
161 {"registerwrite", &lua_vma::registerX<DEBUG_WRITE, true>},
162 {"unregisterwrite", &lua_vma::registerX<DEBUG_WRITE, false>},
163 {"registerexec", &lua_vma::registerX<DEBUG_EXEC, true>},
164 {"unregisterexec", &lua_vma::registerX<DEBUG_EXEC, false>},
165 }, &lua_vma::print);
167 lua::_class<lua_vma_list> class_vmalist(lua_class_memory, "VMALIST", {
168 {"new", lua_vma_list::create},
169 }, {
170 {"__index", &lua_vma_list::index},
171 {"__newindex", &lua_vma_list::newindex},
172 {"__call", &lua_vma_list::call},
175 lua_vma::lua_vma(lua::state& L, memory_region* r)
177 vmabase = r->base;
178 vmasize = r->size;
179 vma = r->name;
180 ro = r->readonly;
183 int lua_vma::info(lua::state& L, lua::parameters& P)
185 for(auto i : lsnes_memory.get_regions())
186 if(i->name == vma)
187 return handle_push_vma(L, *i);
188 (stringfmt() << P.get_fname() << ": Stale region").throwex();
191 template<class T, bool _bswap> int lua_vma::rw(lua::state& L, lua::parameters& P)
193 uint64_t addr;
194 T val;
196 P(P.skipped(), addr);
198 if(addr > vmasize || addr > vmasize - sizeof(T))
199 throw std::runtime_error("VMA::rw<T>: Address outside VMA bounds");
200 if(P.is_novalue()) {
201 //Read.
202 T val = lsnes_memory.read<T>(addr + vmabase);
203 if(_bswap) val = bswap(val);
204 L.pushnumber(val);
205 return 1;
206 } else if(P.is_number()) {
207 //Write.
208 if(ro)
209 (stringfmt() << P.get_fname() << ": VMA is read-only").throwex();
210 P(val);
211 if(_bswap) val = bswap(val);
212 lsnes_memory.write<T>(addr + vmabase, val);
213 return 0;
214 } else
215 P.expected("number or nil");
218 template<bool write, bool sign> int lua_vma::scattergather(lua::state& L, lua::parameters& P)
220 uint64_t val = 0;
221 unsigned shift = 0;
222 uint64_t addr = 0;
224 P(P.skipped());
225 if(write)
226 P(val);
228 while(!P.is_novalue()) {
229 if(P.is_boolean()) {
230 if(P.arg<bool>())
231 addr++;
232 else
233 addr--;
234 } else
235 addr = P.arg<uint64_t>();
236 if(write)
237 lsnes_memory.write<uint8_t>(addr + vmabase, val >> shift);
238 else
239 val = val + ((uint64_t)lsnes_memory.read<uint8_t>(addr + vmabase) << shift);
240 shift += 8;
242 if(!write) {
243 int64_t sval = val;
244 if(val >= (1ULL << (shift - 1))) sval -= (1ULL << shift);
245 if(sign) L.pushnumber(sval); else L.pushnumber(val);
247 return write ? 0 : 1;
250 template<class T> int lua_vma::hash(lua::state& L, lua::parameters& P)
252 uint64_t addr, size, rows, stride = 0;
253 bool equals = true;
255 P(P.skipped(), addr, size, P.optional(rows, 1));
256 if(rows > 1) P(stride);
258 //First verify that all reads are to the region.
259 uint64_t tmp = addr;
260 if(size > vmasize && rows)
261 throw std::runtime_error("Region out of range");
262 for(uint64_t i = 0; i < rows; i++) {
263 if(tmp >= vmasize || tmp + size > vmasize)
264 throw std::runtime_error("Region out of range");
265 tmp += stride;
268 auto hstate = T::create();
269 //Try to map the VMA.
270 char* vmabuf = lsnes_memory.get_physical_mapping(vmabase, vmasize);
271 if(vmabuf) {
272 for(uint64_t i = 0; i < rows; i++) {
273 T::write(hstate, vmabuf + addr, size);
274 addr += stride;
276 } else {
277 uint8_t buf[512]; //Must be power of 2.
278 unsigned bf = 0;
279 for(uint64_t i = 0; i < rows; i++) {
280 for(uint64_t j = 0; j < size; j++) {
281 buf[bf] = lsnes_memory.read<uint8_t>(vmabase + addr + j);
282 bf = (bf + 1) & (sizeof(buf) - 1);
283 if(!bf)
284 T::write(hstate, buf, sizeof(buf));
286 addr += stride;
288 if(bf)
289 T::write(hstate, buf, bf);
291 L.pushlstring(T::read(hstate));
292 return 1;
295 int lua_vma::readregion(lua::state& L, lua::parameters& P)
297 uint64_t addr, size;
299 P(P.skipped(), addr, size);
301 if(addr >= vmasize || size > vmasize || addr + size > vmasize)
302 throw std::runtime_error("Read out of range");
304 L.newtable();
305 char* vmabuf = lsnes_memory.get_physical_mapping(vmabase, vmasize);
306 if(vmabuf) {
307 uint64_t ctr = 1;
308 for(size_t i = 0; i < size; i++) {
309 L.pushnumber(ctr++);
310 L.pushnumber(static_cast<unsigned char>(vmabuf[addr + i]));
311 L.settable(-3);
313 } else {
314 uint64_t ctr = 1;
315 for(size_t i = 0; i < size; i++) {
316 L.pushnumber(ctr++);
317 L.pushnumber(lsnes_memory.read<uint8_t>(addr + i));
318 L.settable(-3);
321 return 1;
324 int lua_vma::writeregion(lua::state& L, lua::parameters& P)
326 uint64_t addr;
327 int ltbl;
329 P(P.skipped(), addr, P.table(ltbl));
331 auto g = lsnes_memory.lookup(vmabase);
332 if(!g.first || g.first->readonly)
333 throw std::runtime_error("Memory address is read-only");
334 if(addr >= vmasize)
335 throw std::runtime_error("Write out of range");
337 uint64_t ctr = 1;
338 char* vmabuf = lsnes_memory.get_physical_mapping(vmabase, vmasize);
339 if(vmabuf) {
340 for(size_t i = 0;; i++) {
341 L.pushnumber(ctr++);
342 L.gettable(ltbl);
343 if(L.type(-1) == LUA_TNIL)
344 break;
345 if(addr + i >= vmasize)
346 throw std::runtime_error("Write out of range");
347 vmabuf[addr + i] = L.tointeger(-1);
348 L.pop(1);
350 } else {
351 for(size_t i = 0;; i++) {
352 L.pushnumber(ctr++);
353 L.gettable(ltbl);
354 if(L.type(-1) == LUA_TNIL)
355 break;
356 if(addr + i >= vmasize)
357 throw std::runtime_error("Write out of range");
358 lsnes_memory.write<uint8_t>(vmabase + addr + i, L.tointeger(-1));
359 L.pop(1);
364 template<bool cmp> int lua_vma::storecmp(lua::state& L, lua::parameters& P)
366 uint64_t addr, daddr, size, rows, stride = 0;
367 bool equals = true;
369 P(P.skipped(), addr, daddr, size, P.optional(rows, 1));
370 if(rows > 1) P(stride);
372 //First verify that all reads are to the region.
373 uint64_t tmp = addr;
374 if(size > vmasize && rows)
375 throw std::runtime_error("Source out of range");
376 for(uint64_t i = 0; i < rows; i++) {
377 if(tmp >= vmasize || tmp + size > vmasize)
378 throw std::runtime_error("Source out of range");
379 tmp += stride;
381 //Calculate new size of target.
382 auto& h = movb.get_mfile().host_memory;
383 size_t rsize = size * rows;
384 if(size && rsize / size != rows)
385 throw std::runtime_error("Copy size out of range");
386 if((size_t)daddr + rsize < rsize)
387 throw std::runtime_error("Target out of range");
388 if(daddr + rsize > h.size()) {
389 h.resize(daddr + rsize);
390 equals = false;
393 //Try to map the VMA.
394 char* vmabuf = lsnes_memory.get_physical_mapping(vmabase, vmasize);
395 if(vmabuf) {
396 for(uint64_t i = 0; i < rows; i++) {
397 bool eq = (cmp && !memcmp(&h[daddr], vmabuf + addr, size));
398 if(!eq)
399 memcpy(&h[daddr], vmabuf + addr, size);
400 equals &= eq;
401 addr += stride;
402 daddr += size;
404 } else {
405 for(uint64_t i = 0; i < rows; i++) {
406 for(uint64_t j = 0; j < size; j++) {
407 uint8_t byte = lsnes_memory.read<uint8_t>(vmabase + addr + j);
408 bool eq = (cmp && ((uint8_t)h[daddr + j] == byte));
409 h[daddr + j] = byte;
410 equals &= eq;
412 addr += stride;
413 daddr += size;
416 if(cmp) L.pushboolean(equals);
417 return cmp ? 1 : 0;
420 template<debug_type type, bool reg> int lua_vma::registerX(lua::state& L, lua::parameters& P)
422 uint64_t addr;
423 int lfn;
424 P(P.skipped(), addr, P.function(lfn));
426 if(reg) {
427 handle_registerX<type>(L, vmabase + addr, lfn);
428 L.pushvalue(lfn);
429 return 1;
430 } else {
431 handle_unregisterX<type>(L, vmabase + addr, lfn);
432 return 0;
436 int lua_vma::cheat(lua::state& L, lua::parameters& P)
438 uint64_t addr, value;
440 P(P.skipped(), addr);
441 if(addr >= vmasize)
442 throw std::runtime_error("Address out of range");
443 if(P.is_novalue()) {
444 debug_clear_cheat(vmabase + addr);
445 } else {
446 P(value);
447 debug_set_cheat(vmabase + addr, value);
449 return 0;
452 lua_vma_list::lua_vma_list(lua::state& L)
456 int lua_vma_list::create(lua::state& L, lua::parameters& P)
458 lua::_class<lua_vma_list>::create(L);
459 return 1;
462 int lua_vma_list::call(lua::state& L, lua::parameters& P)
464 L.newtable();
465 size_t key = 1;
466 for(auto i : lsnes_memory.get_regions()) {
467 L.pushnumber(key++);
468 L.pushlstring(i->name);
469 L.rawset(-3);
471 return 1;
474 int lua_vma_list::index(lua::state& L, lua::parameters& P)
476 std::string vma;
478 P(P.skipped(), vma);
480 auto l = lsnes_memory.get_regions();
481 size_t j;
482 std::list<memory_region*>::iterator i;
483 for(i = l.begin(), j = 0; i != l.end(); i++, j++)
484 if((*i)->name == vma) {
485 lua::_class<lua_vma>::create(L, *i);
486 return 1;
488 (stringfmt() << P.get_fname() << ": No such VMA").throwex();
491 int lua_vma_list::newindex(lua::state& L, lua::parameters& P)
493 throw std::runtime_error("Writing is not allowed");