Fix race between killing object and drawing object
[lsnes.git] / src / core / memorymanip.cpp
blob09498684267596d08dc94044149c8fc9981e3ce2
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 <iostream>
16 #include <limits>
17 #include <sstream>
18 #include <iomanip>
19 #include <cstdint>
20 #include <cstring>
22 namespace
24 uint8_t lsnes_mmio_iospace_read(movie_logic* mlogic, uint64_t offset)
26 try {
27 if(offset >= 0 && offset < 8) {
28 //Frame counter.
29 uint64_t x = CORE().mlogic->get_movie().get_current_frame();
30 return x >> (8 * (offset & 7));
31 } else if(offset >= 8 && offset < 16) {
32 //Movie length.
33 uint64_t x = CORE().mlogic->get_movie().get_frame_count();
34 return x >> (8 * (offset & 7));
35 } else if(offset >= 16 && offset < 24) {
36 //Lag counter.
37 uint64_t x = CORE().mlogic->get_movie().get_lag_frames();
38 return x >> (8 * (offset & 7));
39 } else if(offset >= 24 && offset < 32) {
40 //Rerecord counter.
41 uint64_t x = CORE().mlogic->get_rrdata().count();
42 return x >> (8 * (offset & 7));
43 } else
44 return 0;
45 } catch(...) {
46 return 0;
50 void lsnes_mmio_iospace_write(movie_logic* mlogic, uint64_t offset, uint8_t data)
52 //Ignore.
55 class iospace_region : public memory_space::region
57 public:
58 iospace_region(const std::string& _name, uint64_t _base, uint64_t _size, bool _special,
59 std::function<uint8_t(uint64_t)> _read, std::function<void(uint64_t, uint8_t)> _write)
61 name = _name;
62 base = _base;
63 size = _size;
64 Xread = _read;
65 Xwrite = _write;
66 endian = -1;
67 readonly = (_write == NULL);
68 special = _special;
69 direct_map = NULL;
71 ~iospace_region() throw() {}
72 void read(uint64_t offset, void* buffer, size_t rsize)
74 uint8_t* _buffer = reinterpret_cast<uint8_t*>(buffer);
75 for(size_t i = 0; i < rsize; i++)
76 _buffer[i] = Xread(offset + i);
78 bool write(uint64_t offset, const void* buffer, size_t rsize)
80 const uint8_t* _buffer = reinterpret_cast<const uint8_t*>(buffer);
81 for(size_t i = 0; i < rsize; i++)
82 Xwrite(offset + i, _buffer[i]);
83 return offset + rsize <= size;
85 std::function<uint8_t(uint64_t)> Xread;
86 std::function<void(uint64_t, uint8_t)> Xwrite;
90 cart_mappings_refresher::cart_mappings_refresher(memory_space& _mspace, movie_logic& _mlogic, loaded_rom& _rom)
91 : mspace(_mspace), mlogic(_mlogic), rom(_rom)
95 void cart_mappings_refresher::operator()() throw(std::bad_alloc)
97 std::list<memory_space::region*> cur_regions = mspace.get_regions();
98 std::list<memory_space::region*> regions;
99 memory_space::region* tmp = NULL;
100 auto vmalist = rom.vma_list();
101 auto _mlogic = &mlogic;
102 try {
103 tmp = new iospace_region("LSNESMMIO", 0xFFFFFFFF00000000ULL, 32, true,
104 [_mlogic](uint64_t addr) -> uint8_t { return lsnes_mmio_iospace_read(_mlogic, addr); },
105 [_mlogic](uint64_t addr, uint8_t value) { lsnes_mmio_iospace_write(_mlogic, addr, value); });
106 regions.push_back(tmp);
107 tmp = NULL;
108 for(auto i : vmalist) {
109 if(!i.backing_ram)
110 tmp = new iospace_region(i.name, i.base, i.size, i.special, i.read, i.write);
111 else
112 tmp = new memory_space::region_direct(i.name, i.base, i.endian,
113 reinterpret_cast<uint8_t*>(i.backing_ram), i.size, i.readonly);
114 regions.push_back(tmp);
115 tmp = NULL;
117 mspace.set_regions(regions);
118 } catch(...) {
119 if(tmp)
120 delete tmp;
121 for(auto i : regions)
122 delete i;
123 throw;
125 for(auto i : cur_regions)
126 delete i;
129 namespace
131 uint64_t parse_address(std::string addr)
133 if(regex_match("[0-9]+|0x[0-9A-Fa-f]+", addr)) {
134 //Absolute in mapspace.
135 return parse_value<uint64_t>(addr);
136 } else if(regex_match("[^+]+\\+[0-9A-Fa-f]+", addr)) {
137 //VMA-relative.
138 regex_results r = regex("([^+]+)\\+([0-9A-Fa-f]+)", addr);
139 std::string vma = r[1];
140 std::string _offset = r[2];
141 uint64_t offset = parse_value<uint64_t>("0x" + _offset);
142 for(auto i : CORE().memory->get_regions())
143 if(i->name == vma) {
144 if(offset >= i->size)
145 throw std::runtime_error("Offset out of range");
146 return i->base + offset;
148 throw std::runtime_error("No such VMA");
149 } else
150 throw std::runtime_error("Unknown syntax");
153 std::string format_address(uint64_t addr)
155 for(auto i : CORE().memory->get_regions())
156 if(i->base <= addr && i->base + i->size > addr) {
157 //Hit.
158 unsigned hcount = 1;
159 uint64_t t = i->size;
160 while(t > 0x10) { hcount++; t >>= 4; }
161 return (stringfmt() << i->name << "+" << std::hex << std::setw(hcount)
162 << std::setfill('0')
163 << (addr - i->base)).str();
165 //Fallback.
166 return hex::to(addr);
169 class memorymanip_command : public command::base
171 public:
172 memorymanip_command(command::group& grp, const std::string& cmd) throw(std::bad_alloc)
173 : command::base(grp, cmd, true)
175 _command = cmd;
177 ~memorymanip_command() throw() {}
178 void invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
180 regex_results t = regex("(([^ \t]+)([ \t]+([^ \t]+)([ \t]+([^ \t].*)?)?)?)?", args);
181 if(!t) {
182 address_bad = true;
183 return;
185 firstword = t[2];
186 secondword = t[4];
187 has_tail = (t[6] != "");
188 address_bad = true;
189 value_bad = true;
190 has_value = (secondword != "");
191 try {
192 address = parse_address(firstword);
193 address_bad = false;
194 } catch(...) {
196 try {
197 if(has_value) {
198 valuef = parse_value<double>(secondword);
199 has_valuef = true;
201 } catch(...) {
203 try {
204 value = parse_value<uint64_t>(secondword);
205 value_bad = false;
206 } catch(...) {
208 invoke2();
210 virtual void invoke2() throw(std::bad_alloc, std::runtime_error) = 0;
211 std::string firstword;
212 std::string secondword;
213 uint64_t address;
214 uint64_t value;
215 double valuef;
216 bool has_tail;
217 bool address_bad;
218 bool value_bad;
219 bool has_value;
220 bool has_valuef;
221 std::string _command;
224 template<typename ret, ret (memory_space::*_rfn)(uint64_t addr), bool hexd>
225 class read_command : public memorymanip_command
227 public:
228 read_command(command::group& grp, const std::string& cmd) throw(std::bad_alloc)
229 : memorymanip_command(grp, cmd)
232 ~read_command() throw() {}
233 void invoke2() throw(std::bad_alloc, std::runtime_error)
235 if(address_bad || has_value || has_tail)
236 throw std::runtime_error("Syntax: " + _command + " <address>");
238 std::ostringstream x;
239 if(hexd)
240 x << format_address(address) << " -> "
241 << hex::to((CORE().memory->*_rfn)(address), true);
242 else if(sizeof(ret) > 1)
243 x << format_address(address) << " -> " << std::dec
244 << (CORE().memory->*_rfn)(address);
245 else
246 x << format_address(address) << " -> " << std::dec
247 << (int)(CORE().memory->*_rfn)(address);
248 messages << x.str() << std::endl;
251 std::string get_short_help() throw(std::bad_alloc) { return "Read memory"; }
252 std::string get_long_help() throw(std::bad_alloc)
254 return "Syntax: " + _command + " <address>\n"
255 "Reads data from memory.\n";
259 template<typename arg, int64_t low, uint64_t high, bool (memory_space::*_wfn)(uint64_t addr, arg a)>
260 class write_command : public memorymanip_command
262 public:
263 write_command(command::group& grp, const std::string& cmd)
264 throw(std::bad_alloc)
265 : memorymanip_command(grp, cmd)
268 ~write_command() throw() {}
269 void invoke2() throw(std::bad_alloc, std::runtime_error)
271 if(address_bad || value_bad || has_tail)
272 throw std::runtime_error("Syntax: " + _command + " <address> <value>");
273 int64_t value2 = static_cast<int64_t>(value);
274 if(value2 < low || (value > high && value2 >= 0))
275 throw std::runtime_error("Value to write out of range");
276 (CORE().memory->*_wfn)(address, value & high);
278 std::string get_short_help() throw(std::bad_alloc) { return "Write memory"; }
279 std::string get_long_help() throw(std::bad_alloc)
281 return "Syntax: " + _command + " <address> <value>\n"
282 "Writes data to memory.\n";
286 template<typename arg, bool (memory_space::*_wfn)(uint64_t addr, arg a)>
287 class writef_command : public memorymanip_command
289 public:
290 writef_command(command::group& grp, const std::string& cmd)
291 throw(std::bad_alloc)
292 : memorymanip_command(grp, cmd)
295 ~writef_command() throw() {}
296 void invoke2() throw(std::bad_alloc, std::runtime_error)
298 if(address_bad || !has_valuef || has_tail)
299 throw std::runtime_error("Syntax: " + _command + " <address> <value>");
300 (CORE().memory->*_wfn)(address, valuef);
302 std::string get_short_help() throw(std::bad_alloc) { return "Write memory"; }
303 std::string get_long_help() throw(std::bad_alloc)
305 return "Syntax: " + _command + " <address> <value>\n"
306 "Writes data to memory.\n";
310 command::byname_factory<read_command<uint8_t, &memory_space::read<uint8_t>, false>> CMD_ru1(lsnes_cmds,
311 "read-byte");
312 command::byname_factory<read_command<uint16_t, &memory_space::read<uint16_t>, false>> CMD_ru2(lsnes_cmds,
313 "read-word");
314 command::byname_factory<read_command<ss_uint24_t, &memory_space::read<ss_uint24_t>, false>> CMD_ru3(
315 lsnes_cmds, "read-hword");
316 command::byname_factory<read_command<uint32_t, &memory_space::read<uint32_t>, false>> CMD_ru4(lsnes_cmds,
317 "read-dword");
318 command::byname_factory<read_command<uint64_t, &memory_space::read<uint64_t>, false>> CMD_ru8(lsnes_cmds,
319 "read-qword");
320 command::byname_factory<read_command<uint8_t, &memory_space::read<uint8_t>, true>> CMD_rh1(lsnes_cmds,
321 "read-byte-hex");
322 command::byname_factory<read_command<uint16_t, &memory_space::read<uint16_t>, true>> CMD_rh2(lsnes_cmds,
323 "read-word-hex");
324 command::byname_factory<read_command<ss_uint24_t, &memory_space::read<ss_uint24_t>, true>>
325 CMD_rh3(lsnes_cmds, "read-hword-hex");
326 command::byname_factory<read_command<uint32_t, &memory_space::read<uint32_t>, true>> CMD_rh4(lsnes_cmds,
327 "read-dword-hex");
328 command::byname_factory<read_command<uint64_t, &memory_space::read<uint64_t>, true>> CMD_rh8(lsnes_cmds,
329 "read-qword-hex");
330 command::byname_factory<read_command<int8_t, &memory_space::read<int8_t>, false>> CMD_rs1(lsnes_cmds,
331 "read-sbyte");
332 command::byname_factory<read_command<int16_t, &memory_space::read<int16_t>, false>> CMD_rs2(lsnes_cmds,
333 "read-sword");
334 command::byname_factory<read_command<ss_int24_t, &memory_space::read<ss_int24_t>, false>> CMD_rs3(lsnes_cmds,
335 "read-shword");
336 command::byname_factory<read_command<int32_t, &memory_space::read<int32_t>, false>> CMD_rs4(lsnes_cmds,
337 "read-sdword");
338 command::byname_factory<read_command<float, &memory_space::read<float>, false>> CMD_rf4(lsnes_cmds,
339 "read-float");
340 command::byname_factory<read_command<double, &memory_space::read<double>, false>> CMD_rf8(lsnes_cmds,
341 "read-double");
342 command::byname_factory<write_command<uint8_t, -128, 0xFF, &memory_space::write<uint8_t>>> CMD_w1(lsnes_cmds,
343 "write-byte");
344 command::byname_factory<write_command<uint16_t, -32768, 0xFFFF, &memory_space::write<uint16_t>>>
345 CMD_w2(lsnes_cmds, "write-word");
346 command::byname_factory<write_command<ss_uint24_t, -8388608, 0xFFFFFF, &memory_space::write<ss_uint24_t>>>
347 CMD_w3(lsnes_cmds, "write-hword");
348 command::byname_factory<write_command<uint32_t, -2147483648LL, 0xFFFFFFFFULL, &memory_space::write<uint32_t>>>
349 CMD_w4(lsnes_cmds, "write-dword");
350 //Just straight writing the constant would cause a warning.
351 command::byname_factory<write_command<uint64_t, -9223372036854775807LL-1, 0xFFFFFFFFFFFFFFFFULL,
352 &memory_space::write<uint64_t>>> CMD_w8(lsnes_cmds, "write-qword");
353 command::byname_factory<writef_command<float, &memory_space::write<float>>> CMD_wf4(lsnes_cmds,
354 "write-float");
355 command::byname_factory<writef_command<double, &memory_space::write<double>>> CMD_wf8(lsnes_cmds,
356 "write-double");