Handle conflicting sysregions
[lsnes.git] / src / core / memorymanip.cpp
blob303b5333580e0321d4969d6bcadc963a639cabe9
2 #include "core/command.hpp"
3 #include "core/memorymanip.hpp"
4 #include "core/moviedata.hpp"
5 #include "core/misc.hpp"
6 #include "core/rom.hpp"
7 #include "core/rrdata.hpp"
8 #include "interface/romtype.hpp"
9 #include "library/string.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 || !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.native_endian ? 0 : -1,
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 memory_search* isrch;
116 std::string tokenize1(const std::string& command, const std::string& syntax);
117 std::pair<std::string, std::string> tokenize2(const std::string& command, const std::string& syntax);
118 std::pair<std::string, std::string> tokenize12(const std::string& command, const std::string& syntax);
120 unsigned char hex(char ch)
122 switch(ch) {
123 case '0': return 0;
124 case '1': return 1;
125 case '2': return 2;
126 case '3': return 3;
127 case '4': return 4;
128 case '5': return 5;
129 case '6': return 6;
130 case '7': return 7;
131 case '8': return 8;
132 case '9': return 9;
133 case 'a': case 'A': return 10;
134 case 'b': case 'B': return 11;
135 case 'c': case 'C': return 12;
136 case 'd': case 'D': return 13;
137 case 'e': case 'E': return 14;
138 case 'f': case 'F': return 15;
140 throw std::runtime_error("Bad hex character");
143 class memorymanip_command : public command
145 public:
146 memorymanip_command(const std::string& cmd) throw(std::bad_alloc)
147 : command(lsnes_cmd, cmd)
149 _command = cmd;
151 ~memorymanip_command() throw() {}
152 void invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
154 regex_results t = regex("(([^ \t]+)([ \t]+([^ \t]+)([ \t]+([^ \t].*)?)?)?)?", args);
155 firstword = t[2];
156 secondword = t[4];
157 has_tail = (t[6] != "");
158 address_bad = true;
159 value_bad = true;
160 has_value = (secondword != "");
161 try {
162 if(t = regex("0x(.+)", firstword)) {
163 if(t[1].length() > 16)
164 throw 42;
165 address = 0;
166 for(unsigned i = 0; i < t[1].length(); i++)
167 address = 16 * address + hex(t[1][i]);
168 } else {
169 address = parse_value<uint64_t>(firstword);
171 address_bad = false;
172 } catch(...) {
174 try {
175 if(t = regex("0x(.+)", secondword)) {
176 if(t[1].length() > 16)
177 throw 42;
178 value = 0;
179 for(unsigned i = 0; i < t[1].length(); i++)
180 value = 16 * value + hex(t[1][i]);
181 } else if(regex("-.*", secondword)) {
182 value = static_cast<uint64_t>(parse_value<int64_t>(secondword));
183 } else {
184 value = parse_value<uint64_t>(secondword);
186 value_bad = false;
187 } catch(...) {
189 invoke2();
191 virtual void invoke2() throw(std::bad_alloc, std::runtime_error) = 0;
192 std::string firstword;
193 std::string secondword;
194 uint64_t address;
195 uint64_t value;
196 bool has_tail;
197 bool address_bad;
198 bool value_bad;
199 bool has_value;
200 std::string _command;
203 template<typename ret, ret (memory_space::*_rfn)(uint64_t addr)>
204 class read_command : public memorymanip_command
206 public:
207 read_command(const std::string& cmd) throw(std::bad_alloc)
208 : memorymanip_command(cmd)
211 ~read_command() throw() {}
212 void invoke2() throw(std::bad_alloc, std::runtime_error)
214 if(address_bad || has_value || has_tail)
215 throw std::runtime_error("Syntax: " + _command + " <address>");
217 std::ostringstream x;
218 x << "0x" << std::hex << address << " -> " << std::dec
219 << (lsnes_memory.*_rfn)(address);
220 messages << x.str() << std::endl;
223 std::string get_short_help() throw(std::bad_alloc) { return "Read memory"; }
224 std::string get_long_help() throw(std::bad_alloc)
226 return "Syntax: " + _command + " <address>\n"
227 "Reads data from memory.\n";
231 template<typename arg, int64_t low, uint64_t high, bool (memory_space::*_wfn)(uint64_t addr, arg a)>
232 class write_command : public memorymanip_command
234 public:
235 write_command(const std::string& cmd)
236 throw(std::bad_alloc)
237 : memorymanip_command(cmd)
240 ~write_command() throw() {}
241 void invoke2() throw(std::bad_alloc, std::runtime_error)
243 if(address_bad || value_bad || has_tail)
244 throw std::runtime_error("Syntax: " + _command + " <address> <value>");
245 int64_t value2 = static_cast<int64_t>(value);
246 if(value2 < low || (value > high && value2 >= 0))
247 throw std::runtime_error("Value to write out of range");
248 (lsnes_memory.*_wfn)(address, value & high);
250 std::string get_short_help() throw(std::bad_alloc) { return "Write memory"; }
251 std::string get_long_help() throw(std::bad_alloc)
253 return "Syntax: " + _command + " <address> <value>\n"
254 "Writes data to memory.\n";
258 read_command<uint8_t, &memory_space::read<uint8_t>> ru1("read-byte");
259 read_command<uint16_t, &memory_space::read<uint16_t>> ru2("read-word");
260 read_command<uint32_t, &memory_space::read<uint32_t>> ru4("read-dword");
261 read_command<uint64_t, &memory_space::read<uint64_t>> ru8("read-qword");
262 read_command<int8_t, &memory_space::read<int8_t>> rs1("read-sbyte");
263 read_command<int16_t, &memory_space::read<int16_t>> rs2("read-sword");
264 read_command<int32_t, &memory_space::read<int32_t>> rs4("read-sdword");
265 read_command<int64_t, &memory_space::read<int64_t>> rs8("read-sqword");
266 write_command<uint8_t, -128, 0xFF, &memory_space::write<uint8_t>> w1("write-byte");
267 write_command<uint16_t, -32768, 0xFFFF, &memory_space::write<uint16_t>> w2("write-word");
268 write_command<uint32_t, -2147483648LL, 0xFFFFFFFFULL, &memory_space::write<uint32_t>> w4("write-dword");
269 write_command<uint64_t, -9223372036854775808LL, 0xFFFFFFFFFFFFFFFFULL, &memory_space::write<uint64_t>>
270 w8("write-qword");