lsnes rr2-β24
[lsnes.git] / src / core / memorymanip.cpp
blob07721079a99cc250796fe9d07bd340c686fd9813
1 #include "core/command.hpp"
2 #include "core/instance.hpp"
3 #include "core/memorymanip.hpp"
4 #include "core/messages.hpp"
5 #include "core/misc.hpp"
6 #include "core/moviedata.hpp"
7 #include "core/rom.hpp"
8 #include "interface/romtype.hpp"
9 #include "library/hex.hpp"
10 #include "library/int24.hpp"
11 #include "library/memorysearch.hpp"
12 #include "library/minmax.hpp"
13 #include "library/string.hpp"
15 #include <functional>
16 #include <iostream>
17 #include <limits>
18 #include <sstream>
19 #include <iomanip>
20 #include <cstdint>
21 #include <cstring>
23 namespace
25 uint8_t lsnes_mmio_iospace_read(movie_logic* mlogic, uint64_t offset)
27 try {
28 if(offset >= 0 && offset < 8) {
29 //Frame counter.
30 uint64_t x = CORE().mlogic->get_movie().get_current_frame();
31 return x >> (8 * (offset & 7));
32 } else if(offset >= 8 && offset < 16) {
33 //Movie length.
34 uint64_t x = CORE().mlogic->get_movie().get_frame_count();
35 return x >> (8 * (offset & 7));
36 } else if(offset >= 16 && offset < 24) {
37 //Lag counter.
38 uint64_t x = CORE().mlogic->get_movie().get_lag_frames();
39 return x >> (8 * (offset & 7));
40 } else if(offset >= 24 && offset < 32) {
41 //Rerecord counter.
42 uint64_t x = CORE().mlogic->get_rrdata().count();
43 return x >> (8 * (offset & 7));
44 } else
45 return 0;
46 } catch(...) {
47 return 0;
51 void lsnes_mmio_iospace_write(movie_logic* mlogic, uint64_t offset, uint8_t data)
53 //Ignore.
56 class iospace_region : public memory_space::region
58 public:
59 iospace_region(const std::string& _name, uint64_t _base, uint64_t _size, bool _special,
60 std::function<uint8_t(uint64_t)> _read, std::function<void(uint64_t, uint8_t)> _write)
62 name = _name;
63 base = _base;
64 size = _size;
65 Xread = _read;
66 Xwrite = _write;
67 endian = -1;
68 readonly = (_write == NULL);
69 special = _special;
70 direct_map = NULL;
72 ~iospace_region() throw() {}
73 void read(uint64_t offset, void* buffer, size_t rsize)
75 uint8_t* _buffer = reinterpret_cast<uint8_t*>(buffer);
76 for(size_t i = 0; i < rsize; i++)
77 _buffer[i] = Xread(offset + i);
79 bool write(uint64_t offset, const void* buffer, size_t rsize)
81 const uint8_t* _buffer = reinterpret_cast<const uint8_t*>(buffer);
82 for(size_t i = 0; i < rsize; i++)
83 Xwrite(offset + i, _buffer[i]);
84 return offset + rsize <= size;
86 std::function<uint8_t(uint64_t)> Xread;
87 std::function<void(uint64_t, uint8_t)> Xwrite;
91 cart_mappings_refresher::cart_mappings_refresher(memory_space& _mspace, movie_logic& _mlogic, loaded_rom& _rom)
92 : mspace(_mspace), mlogic(_mlogic), rom(_rom)
96 void cart_mappings_refresher::operator()() throw(std::bad_alloc)
98 std::list<memory_space::region*> cur_regions = mspace.get_regions();
99 std::list<memory_space::region*> regions;
100 memory_space::region* tmp = NULL;
101 auto vmalist = rom.vma_list();
102 auto _mlogic = &mlogic;
103 try {
104 tmp = new iospace_region("LSNESMMIO", 0xFFFFFFFF00000000ULL, 32, true,
105 [_mlogic](uint64_t addr) -> uint8_t { return lsnes_mmio_iospace_read(_mlogic, addr); },
106 [_mlogic](uint64_t addr, uint8_t value) { lsnes_mmio_iospace_write(_mlogic, addr, value); });
107 regions.push_back(tmp);
108 tmp = NULL;
109 for(auto i : vmalist) {
110 if(!i.backing_ram)
111 tmp = new iospace_region(i.name, i.base, i.size, i.special, i.read, i.write);
112 else
113 tmp = new memory_space::region_direct(i.name, i.base, i.endian,
114 reinterpret_cast<uint8_t*>(i.backing_ram), i.size, i.readonly);
115 regions.push_back(tmp);
116 tmp = NULL;
118 mspace.set_regions(regions);
119 } catch(...) {
120 if(tmp)
121 delete tmp;
122 for(auto i : regions)
123 delete i;
124 throw;
126 for(auto i : cur_regions)
127 delete i;
130 namespace
132 uint64_t parse_address(std::string addr)
134 if(regex_match("[0-9]+|0x[0-9A-Fa-f]+", addr)) {
135 //Absolute in mapspace.
136 return parse_value<uint64_t>(addr);
137 } else if(regex_match("[^+]+\\+[0-9A-Fa-f]+", addr)) {
138 //VMA-relative.
139 regex_results r = regex("([^+]+)\\+([0-9A-Fa-f]+)", addr);
140 std::string vma = r[1];
141 std::string _offset = r[2];
142 uint64_t offset = parse_value<uint64_t>("0x" + _offset);
143 for(auto i : CORE().memory->get_regions())
144 if(i->name == vma) {
145 if(offset >= i->size)
146 throw std::runtime_error("Offset out of range");
147 return i->base + offset;
149 throw std::runtime_error("No such VMA");
150 } else
151 throw std::runtime_error("Unknown syntax");
154 std::string format_address(uint64_t addr)
156 for(auto i : CORE().memory->get_regions())
157 if(i->base <= addr && i->base + i->size > addr) {
158 //Hit.
159 unsigned hcount = 1;
160 uint64_t t = i->size;
161 while(t > 0x10) { hcount++; t >>= 4; }
162 return (stringfmt() << i->name << "+" << std::hex << std::setw(hcount)
163 << std::setfill('0')
164 << (addr - i->base)).str();
166 //Fallback.
167 return hex::to(addr);
170 class memorymanip_command : public command::base
172 public:
173 memorymanip_command(command::group& grp, const std::string& cmd) throw(std::bad_alloc)
174 : command::base(grp, cmd, true)
176 _command = cmd;
178 ~memorymanip_command() throw() {}
179 void invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
181 regex_results t = regex("(([^ \t]+)([ \t]+([^ \t]+)([ \t]+([^ \t].*)?)?)?)?", args);
182 if(!t) {
183 address_bad = true;
184 return;
186 firstword = t[2];
187 secondword = t[4];
188 has_tail = (t[6] != "");
189 address_bad = true;
190 value_bad = true;
191 has_value = (secondword != "");
192 try {
193 address = parse_address(firstword);
194 address_bad = false;
195 } catch(...) {
197 try {
198 if(has_value) {
199 valuef = parse_value<double>(secondword);
200 has_valuef = true;
202 } catch(...) {
204 try {
205 value = parse_value<uint64_t>(secondword);
206 value_bad = false;
207 } catch(...) {
209 invoke2();
211 virtual void invoke2() throw(std::bad_alloc, std::runtime_error) = 0;
212 std::string firstword;
213 std::string secondword;
214 uint64_t address;
215 uint64_t value;
216 double valuef;
217 bool has_tail;
218 bool address_bad;
219 bool value_bad;
220 bool has_value;
221 bool has_valuef;
222 std::string _command;
225 template<typename ret, ret (memory_space::*_rfn)(uint64_t addr), bool hexd>
226 class read_command : public memorymanip_command
228 public:
229 read_command(command::group& grp, const std::string& cmd) throw(std::bad_alloc)
230 : memorymanip_command(grp, cmd)
233 ~read_command() throw() {}
234 void invoke2() throw(std::bad_alloc, std::runtime_error)
236 if(address_bad || has_value || has_tail)
237 throw std::runtime_error("Syntax: " + _command + " <address>");
239 std::ostringstream x;
240 if(hexd)
241 x << format_address(address) << " -> "
242 << hex::to((CORE().memory->*_rfn)(address), true);
243 else if(sizeof(ret) > 1)
244 x << format_address(address) << " -> " << std::dec
245 << (CORE().memory->*_rfn)(address);
246 else
247 x << format_address(address) << " -> " << std::dec
248 << (int)(CORE().memory->*_rfn)(address);
249 messages << x.str() << std::endl;
252 std::string get_short_help() throw(std::bad_alloc) { return "Read memory"; }
253 std::string get_long_help() throw(std::bad_alloc)
255 return "Syntax: " + _command + " <address>\n"
256 "Reads data from memory.\n";
260 template<typename arg, int64_t low, uint64_t high, bool (memory_space::*_wfn)(uint64_t addr, arg a)>
261 class write_command : public memorymanip_command
263 public:
264 write_command(command::group& grp, const std::string& cmd)
265 throw(std::bad_alloc)
266 : memorymanip_command(grp, cmd)
269 ~write_command() throw() {}
270 void invoke2() throw(std::bad_alloc, std::runtime_error)
272 if(address_bad || value_bad || has_tail)
273 throw std::runtime_error("Syntax: " + _command + " <address> <value>");
274 int64_t value2 = static_cast<int64_t>(value);
275 if(value2 < low || (value > high && value2 >= 0))
276 throw std::runtime_error("Value to write out of range");
277 (CORE().memory->*_wfn)(address, value & high);
279 std::string get_short_help() throw(std::bad_alloc) { return "Write memory"; }
280 std::string get_long_help() throw(std::bad_alloc)
282 return "Syntax: " + _command + " <address> <value>\n"
283 "Writes data to memory.\n";
287 template<typename arg, bool (memory_space::*_wfn)(uint64_t addr, arg a)>
288 class writef_command : public memorymanip_command
290 public:
291 writef_command(command::group& grp, const std::string& cmd)
292 throw(std::bad_alloc)
293 : memorymanip_command(grp, cmd)
296 ~writef_command() throw() {}
297 void invoke2() throw(std::bad_alloc, std::runtime_error)
299 if(address_bad || !has_valuef || has_tail)
300 throw std::runtime_error("Syntax: " + _command + " <address> <value>");
301 (CORE().memory->*_wfn)(address, valuef);
303 std::string get_short_help() throw(std::bad_alloc) { return "Write memory"; }
304 std::string get_long_help() throw(std::bad_alloc)
306 return "Syntax: " + _command + " <address> <value>\n"
307 "Writes data to memory.\n";
311 command::byname_factory<read_command<uint8_t, &memory_space::read<uint8_t>, false>> CMD_ru1(lsnes_cmds,
312 "read-byte");
313 command::byname_factory<read_command<uint16_t, &memory_space::read<uint16_t>, false>> CMD_ru2(lsnes_cmds,
314 "read-word");
315 command::byname_factory<read_command<ss_uint24_t, &memory_space::read<ss_uint24_t>, false>> CMD_ru3(
316 lsnes_cmds, "read-hword");
317 command::byname_factory<read_command<uint32_t, &memory_space::read<uint32_t>, false>> CMD_ru4(lsnes_cmds,
318 "read-dword");
319 command::byname_factory<read_command<uint64_t, &memory_space::read<uint64_t>, false>> CMD_ru8(lsnes_cmds,
320 "read-qword");
321 command::byname_factory<read_command<uint8_t, &memory_space::read<uint8_t>, true>> CMD_rh1(lsnes_cmds,
322 "read-byte-hex");
323 command::byname_factory<read_command<uint16_t, &memory_space::read<uint16_t>, true>> CMD_rh2(lsnes_cmds,
324 "read-word-hex");
325 command::byname_factory<read_command<ss_uint24_t, &memory_space::read<ss_uint24_t>, true>>
326 CMD_rh3(lsnes_cmds, "read-hword-hex");
327 command::byname_factory<read_command<uint32_t, &memory_space::read<uint32_t>, true>> CMD_rh4(lsnes_cmds,
328 "read-dword-hex");
329 command::byname_factory<read_command<uint64_t, &memory_space::read<uint64_t>, true>> CMD_rh8(lsnes_cmds,
330 "read-qword-hex");
331 command::byname_factory<read_command<int8_t, &memory_space::read<int8_t>, false>> CMD_rs1(lsnes_cmds,
332 "read-sbyte");
333 command::byname_factory<read_command<int16_t, &memory_space::read<int16_t>, false>> CMD_rs2(lsnes_cmds,
334 "read-sword");
335 command::byname_factory<read_command<ss_int24_t, &memory_space::read<ss_int24_t>, false>> CMD_rs3(lsnes_cmds,
336 "read-shword");
337 command::byname_factory<read_command<int32_t, &memory_space::read<int32_t>, false>> CMD_rs4(lsnes_cmds,
338 "read-sdword");
339 command::byname_factory<read_command<float, &memory_space::read<float>, false>> CMD_rf4(lsnes_cmds,
340 "read-float");
341 command::byname_factory<read_command<double, &memory_space::read<double>, false>> CMD_rf8(lsnes_cmds,
342 "read-double");
343 command::byname_factory<write_command<uint8_t, -128, 0xFF, &memory_space::write<uint8_t>>> CMD_w1(lsnes_cmds,
344 "write-byte");
345 command::byname_factory<write_command<uint16_t, -32768, 0xFFFF, &memory_space::write<uint16_t>>>
346 CMD_w2(lsnes_cmds, "write-word");
347 command::byname_factory<write_command<ss_uint24_t, -8388608, 0xFFFFFF, &memory_space::write<ss_uint24_t>>>
348 CMD_w3(lsnes_cmds, "write-hword");
349 command::byname_factory<write_command<uint32_t, -2147483648LL, 0xFFFFFFFFULL, &memory_space::write<uint32_t>>>
350 CMD_w4(lsnes_cmds, "write-dword");
351 //Just straight writing the constant would cause a warning.
352 command::byname_factory<write_command<uint64_t, -9223372036854775807LL-1, 0xFFFFFFFFFFFFFFFFULL,
353 &memory_space::write<uint64_t>>> CMD_w8(lsnes_cmds, "write-qword");
354 command::byname_factory<writef_command<float, &memory_space::write<float>>> CMD_wf4(lsnes_cmds,
355 "write-float");
356 command::byname_factory<writef_command<double, &memory_space::write<double>>> CMD_wf8(lsnes_cmds,
357 "write-double");