JSON-based controller descriptions
[lsnes.git] / src / core / memorymanip.cpp
blob8201935329204e8ed97e9b2a6fd2a89f3e478082
1 #include "core/command.hpp"
2 #include "core/memorymanip.hpp"
3 #include "core/moviedata.hpp"
4 #include "core/misc.hpp"
5 #include "core/rom.hpp"
6 #include "core/rrdata.hpp"
7 #include "interface/romtype.hpp"
8 #include "library/string.hpp"
9 #include "library/int24.hpp"
10 #include "library/minmax.hpp"
11 #include "library/memorysearch.hpp"
13 #include <iostream>
14 #include <limits>
15 #include <sstream>
16 #include <iomanip>
17 #include <cstdint>
18 #include <cstring>
20 memory_space lsnes_memory;
22 namespace
24 uint8_t lsnes_mmio_iospace_handler(uint64_t offset, uint8_t data, bool write)
26 if(offset >= 0 && offset < 8 && !write) {
27 //Frame counter.
28 uint64_t x = get_movie().get_current_frame();
29 return x >> (8 * (offset & 7));
30 } else if(offset >= 8 && offset < 16 && !write) {
31 //Movie length.
32 uint64_t x = get_movie().get_frame_count();
33 return x >> (8 * (offset & 7));
34 } else if(offset >= 16 && offset < 24 && !write) {
35 //Lag counter.
36 uint64_t x = get_movie().get_lag_frames();
37 return x >> (8 * (offset & 7));
38 } else if(offset >= 24 && offset < 32 && !write) {
39 //Rerecord counter.
40 uint64_t x = rrdata.count();
41 return x >> (8 * (offset & 7));
42 } else
43 return 0;
46 class iospace_region : public memory_region
48 public:
49 iospace_region(const std::string& _name, uint64_t _base, uint64_t _size,
50 uint8_t (*_iospace_rw)(uint64_t offset, uint8_t data, bool write))
52 name = _name;
53 base = _base;
54 size = _size;
55 iospace_rw = _iospace_rw;
56 endian = -1;
57 readonly = false;
58 special = true;
59 direct_map = NULL;
61 ~iospace_region() throw() {}
62 void read(uint64_t offset, void* buffer, size_t rsize)
64 uint8_t* _buffer = reinterpret_cast<uint8_t*>(buffer);
65 for(size_t i = 0; i < rsize; i++)
66 _buffer[i] = iospace_rw(offset + i, 0, false);
68 bool write(uint64_t offset, const void* buffer, size_t rsize)
70 const uint8_t* _buffer = reinterpret_cast<const uint8_t*>(buffer);
71 for(size_t i = 0; i < rsize; i++)
72 iospace_rw(offset + i, _buffer[i], true);
73 return offset + rsize <= size;
75 uint8_t (*iospace_rw)(uint64_t offset, uint8_t data, bool write);
79 void refresh_cart_mappings() throw(std::bad_alloc)
81 if(!our_rom.rtype)
82 return;
83 std::list<memory_region*> cur_regions = lsnes_memory.get_regions();
84 std::list<memory_region*> regions;
85 memory_region* tmp = NULL;
86 auto vmalist = our_rom.rtype->vma_list();
87 try {
88 tmp = new iospace_region("LSNESMMIO", 0xFFFFFFFF00000000ULL, 32, lsnes_mmio_iospace_handler);
89 regions.push_back(tmp);
90 tmp = NULL;
91 for(auto i : vmalist) {
92 if(i.iospace_rw)
93 tmp = new iospace_region(i.name, i.base, i.size, i.iospace_rw);
94 else
95 tmp = new memory_region_direct(i.name, i.base, i.endian,
96 reinterpret_cast<uint8_t*>(i.backing_ram), i.size, i.readonly);
97 regions.push_back(tmp);
98 tmp = NULL;
100 lsnes_memory.set_regions(regions);
101 } catch(...) {
102 if(tmp)
103 delete tmp;
104 for(auto i : regions)
105 delete i;
106 throw;
108 for(auto i : cur_regions)
109 delete i;
112 namespace
114 unsigned char hex(char ch)
116 switch(ch) {
117 case '0': return 0;
118 case '1': return 1;
119 case '2': return 2;
120 case '3': return 3;
121 case '4': return 4;
122 case '5': return 5;
123 case '6': return 6;
124 case '7': return 7;
125 case '8': return 8;
126 case '9': return 9;
127 case 'a': case 'A': return 10;
128 case 'b': case 'B': return 11;
129 case 'c': case 'C': return 12;
130 case 'd': case 'D': return 13;
131 case 'e': case 'E': return 14;
132 case 'f': case 'F': return 15;
134 throw std::runtime_error("Bad hex character");
137 class memorymanip_command : public command
139 public:
140 memorymanip_command(const std::string& cmd) throw(std::bad_alloc)
141 : command(lsnes_cmd, cmd)
143 _command = cmd;
145 ~memorymanip_command() throw() {}
146 void invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
148 regex_results t = regex("(([^ \t]+)([ \t]+([^ \t]+)([ \t]+([^ \t].*)?)?)?)?", args);
149 firstword = t[2];
150 secondword = t[4];
151 has_tail = (t[6] != "");
152 address_bad = true;
153 value_bad = true;
154 has_value = (secondword != "");
155 try {
156 if(t = regex("0x(.+)", firstword)) {
157 if(t[1].length() > 16)
158 throw 42;
159 address = 0;
160 for(unsigned i = 0; i < t[1].length(); i++)
161 address = 16 * address + hex(t[1][i]);
162 } else {
163 address = parse_value<uint64_t>(firstword);
165 address_bad = false;
166 } catch(...) {
168 try {
169 if(has_value) {
170 valuef = parse_value<double>(secondword);
171 has_valuef = true;
173 } catch(...) {
175 try {
176 if(t = regex("0x(.+)", secondword)) {
177 if(t[1].length() > 16)
178 throw 42;
179 value = 0;
180 for(unsigned i = 0; i < t[1].length(); i++)
181 value = 16 * value + hex(t[1][i]);
182 } else if(regex("-.*", secondword)) {
183 value = static_cast<uint64_t>(parse_value<int64_t>(secondword));
184 } else {
185 value = parse_value<uint64_t>(secondword);
187 value_bad = false;
188 } catch(...) {
190 invoke2();
192 virtual void invoke2() throw(std::bad_alloc, std::runtime_error) = 0;
193 std::string firstword;
194 std::string secondword;
195 uint64_t address;
196 uint64_t value;
197 double valuef;
198 bool has_tail;
199 bool address_bad;
200 bool value_bad;
201 bool has_value;
202 bool has_valuef;
203 std::string _command;
206 template<typename ret, ret (memory_space::*_rfn)(uint64_t addr)>
207 class read_command : public memorymanip_command
209 public:
210 read_command(const std::string& cmd) throw(std::bad_alloc)
211 : memorymanip_command(cmd)
214 ~read_command() throw() {}
215 void invoke2() throw(std::bad_alloc, std::runtime_error)
217 if(address_bad || has_value || has_tail)
218 throw std::runtime_error("Syntax: " + _command + " <address>");
220 std::ostringstream x;
221 if(sizeof(ret) > 1)
222 x << "0x" << std::hex << address << " -> " << std::dec
223 << (lsnes_memory.*_rfn)(address);
224 else
225 x << "0x" << std::hex << address << " -> " << std::dec
226 << (int)(lsnes_memory.*_rfn)(address);
227 messages << x.str() << std::endl;
230 std::string get_short_help() throw(std::bad_alloc) { return "Read memory"; }
231 std::string get_long_help() throw(std::bad_alloc)
233 return "Syntax: " + _command + " <address>\n"
234 "Reads data from memory.\n";
238 template<typename arg, int64_t low, uint64_t high, bool (memory_space::*_wfn)(uint64_t addr, arg a)>
239 class write_command : public memorymanip_command
241 public:
242 write_command(const std::string& cmd)
243 throw(std::bad_alloc)
244 : memorymanip_command(cmd)
247 ~write_command() throw() {}
248 void invoke2() throw(std::bad_alloc, std::runtime_error)
250 if(address_bad || value_bad || has_tail)
251 throw std::runtime_error("Syntax: " + _command + " <address> <value>");
252 int64_t value2 = static_cast<int64_t>(value);
253 if(value2 < low || (value > high && value2 >= 0))
254 throw std::runtime_error("Value to write out of range");
255 (lsnes_memory.*_wfn)(address, value & high);
257 std::string get_short_help() throw(std::bad_alloc) { return "Write memory"; }
258 std::string get_long_help() throw(std::bad_alloc)
260 return "Syntax: " + _command + " <address> <value>\n"
261 "Writes data to memory.\n";
265 template<typename arg, bool (memory_space::*_wfn)(uint64_t addr, arg a)>
266 class writef_command : public memorymanip_command
268 public:
269 writef_command(const std::string& cmd)
270 throw(std::bad_alloc)
271 : memorymanip_command(cmd)
274 ~writef_command() throw() {}
275 void invoke2() throw(std::bad_alloc, std::runtime_error)
277 if(address_bad || !has_valuef || has_tail)
278 throw std::runtime_error("Syntax: " + _command + " <address> <value>");
279 (lsnes_memory.*_wfn)(address, valuef);
281 std::string get_short_help() throw(std::bad_alloc) { return "Write memory"; }
282 std::string get_long_help() throw(std::bad_alloc)
284 return "Syntax: " + _command + " <address> <value>\n"
285 "Writes data to memory.\n";
289 read_command<uint8_t, &memory_space::read<uint8_t>> ru1("read-byte");
290 read_command<uint16_t, &memory_space::read<uint16_t>> ru2("read-word");
291 read_command<ss_uint24_t, &memory_space::read<ss_uint24_t>> ru3("read-hword");
292 read_command<uint32_t, &memory_space::read<uint32_t>> ru4("read-dword");
293 read_command<uint64_t, &memory_space::read<uint64_t>> ru8("read-qword");
294 read_command<int8_t, &memory_space::read<int8_t>> rs1("read-sbyte");
295 read_command<int16_t, &memory_space::read<int16_t>> rs2("read-sword");
296 read_command<ss_int24_t, &memory_space::read<ss_int24_t>> rs3("read-shword");
297 read_command<int32_t, &memory_space::read<int32_t>> rs4("read-sdword");
298 read_command<float, &memory_space::read<float>> rf4("read-float");
299 read_command<double, &memory_space::read<double>> rf8("read-double");
300 write_command<uint8_t, -128, 0xFF, &memory_space::write<uint8_t>> w1("write-byte");
301 write_command<uint16_t, -32768, 0xFFFF, &memory_space::write<uint16_t>> w2("write-word");
302 write_command<ss_uint24_t, -8388608, 0xFFFFFF, &memory_space::write<ss_uint24_t>> w3("write-hword");
303 write_command<uint32_t, -2147483648LL, 0xFFFFFFFFULL, &memory_space::write<uint32_t>> w4("write-dword");
304 write_command<uint64_t, -9223372036854775808LL, 0xFFFFFFFFFFFFFFFFULL, &memory_space::write<uint64_t>>
305 w8("write-qword");
306 writef_command<float, &memory_space::write<float>> wf4("write-float");
307 writef_command<double, &memory_space::write<double>> wf8("write-double");