Allow specifying ROM type in file load dialog
[lsnes.git] / src / library / memoryspace.cpp
blobad8f0ef60dbc9800a5535ae19e28071ed8f59994
1 #include "memoryspace.hpp"
2 #include "minmax.hpp"
3 #include <algorithm>
5 namespace
7 template<typename T, bool linear> inline T internal_read(memory_space& m, uint64_t addr)
9 const int system_endian = memory_space::get_system_endian();
10 std::pair<memory_region*, uint64_t> g;
11 if(linear)
12 g = m.lookup_linear(addr);
13 else
14 g = m.lookup(addr);
15 if(!g.first || g.second + sizeof(T) > g.first->size)
16 return 0;
17 if((!g.first->endian || g.first->endian == system_endian) && memory_space::can_read_unaligned()) {
18 //Native endian.
19 T buf;
20 if(g.first->direct_map)
21 return *reinterpret_cast<T*>(g.first->direct_map + g.second);
22 else
23 g.first->read(g.second, &buf, sizeof(T));
24 return buf;
25 } else if(g.first->endian < 0 || (!g.first->endian && system_endian < 0)) {
26 //The opposite endian (little).
27 unsigned char buf[sizeof(T)];
28 if(g.first->direct_map)
29 memcpy(buf, g.first->direct_map + g.second, sizeof(T));
30 else
31 g.first->read(g.second, buf, sizeof(T));
32 T v = 0;
33 for(unsigned i = 0; i < sizeof(T); i++)
34 v |= (static_cast<T>(buf[i]) << (8 * i));
35 return v;
36 } else {
37 //The opposite endian (big).
38 unsigned char buf[sizeof(T)];
39 if(g.first->direct_map)
40 memcpy(buf, g.first->direct_map + g.second, sizeof(T));
41 else
42 g.first->read(g.second, buf, sizeof(T));
43 T v = 0;
44 for(unsigned i = 0; i < sizeof(T); i++)
45 v |= (static_cast<T>(buf[i]) << (8 * (sizeof(T) - i - 1)));
46 return v;
50 template<typename T, bool linear> inline bool internal_write(memory_space& m, uint64_t addr, T value)
52 const int system_endian = memory_space::get_system_endian();
53 std::pair<memory_region*, uint64_t> g;
54 if(linear)
55 g = m.lookup_linear(addr);
56 else
57 g = m.lookup(addr);
58 if(!g.first || g.first->readonly || g.second + sizeof(T) > g.first->size)
59 return false;
60 if((!g.first->endian || g.first->endian == system_endian) && memory_space::can_read_unaligned()) {
61 //Native endian.
62 if(g.first->direct_map) {
63 *reinterpret_cast<T*>(g.first->direct_map + g.second) = value;
64 return true;
65 } else
66 g.first->write(g.second, &value, sizeof(T));
67 } else if(g.first->endian < 0 || (!g.first->endian && system_endian < 0)) {
68 //The opposite endian (little).
69 unsigned char buf[sizeof(T)];
70 for(unsigned i = 0; i < sizeof(T); i++)
71 buf[i] = value >> (8 * i);
72 if(g.first->direct_map)
73 memcpy(g.first->direct_map + g.second, buf, sizeof(T));
74 else
75 return g.first->write(g.second, buf, sizeof(T));
76 } else {
77 //The opposite endian (big).
78 unsigned char buf[sizeof(T)];
79 for(unsigned i = 0; i < sizeof(T); i++)
80 buf[i] = value >> (8 * (sizeof(T) - i - 1));
81 if(g.first->direct_map)
82 memcpy(g.first->direct_map + g.second, buf, sizeof(T));
83 else
84 return g.first->write(g.second, buf, sizeof(T));
86 return true;
89 void read_range_r(memory_region& r, uint64_t offset, void* buffer, size_t bsize)
91 if(r.direct_map) {
92 if(offset >= r.size) {
93 memset(buffer, 0, bsize);
94 return;
96 uint64_t maxcopy = min(static_cast<uint64_t>(bsize), r.size - offset);
97 memcpy(buffer, r.direct_map + offset, maxcopy);
98 if(maxcopy < bsize)
99 memset(reinterpret_cast<char*>(buffer) + maxcopy, 0, bsize - maxcopy);
100 } else
101 r.read(offset, buffer, bsize);
104 bool write_range_r(memory_region& r, uint64_t offset, const void* buffer, size_t bsize)
106 if(r.readonly)
107 return false;
108 if(r.direct_map) {
109 if(offset >= r.size)
110 return false;
111 uint64_t maxcopy = min(static_cast<uint64_t>(bsize), r.size - offset);
112 memcpy(r.direct_map + offset, buffer, maxcopy);
113 return true;
114 } else
115 return r.write(offset, buffer, bsize);
119 memory_region::~memory_region() throw()
123 void memory_region::read(uint64_t offset, void* buffer, size_t tsize)
125 if(!direct_map || offset >= size) {
126 memset(buffer, 0, tsize);
127 return;
129 uint64_t maxcopy = min(static_cast<uint64_t>(tsize), size - offset);
130 memcpy(buffer, direct_map + offset, maxcopy);
131 if(maxcopy < tsize)
132 memset(reinterpret_cast<char*>(buffer) + maxcopy, 0, tsize - maxcopy);
135 bool memory_region::write(uint64_t offset, const void* buffer, size_t tsize)
137 if(!direct_map || readonly || offset >= size)
138 return false;
139 uint64_t maxcopy = min(static_cast<uint64_t>(tsize), size - offset);
140 memcpy(direct_map + offset, buffer, maxcopy);
141 return true;
144 std::pair<memory_region*, uint64_t> memory_space::lookup(uint64_t address)
146 umutex_class m(mutex);
147 size_t lb = 0;
148 size_t ub = u_regions.size();
149 while(lb < ub) {
150 size_t mb = (lb + ub) / 2;
151 if(u_regions[mb]->base > address) {
152 ub = mb;
153 continue;
155 if(u_regions[mb]->last_address() < address) {
156 lb = mb + 1;
157 continue;
159 return std::make_pair(u_regions[mb], address - u_regions[mb]->base);
161 return std::make_pair(reinterpret_cast<memory_region*>(NULL), 0);
164 std::pair<memory_region*, uint64_t> memory_space::lookup_linear(uint64_t linear)
166 umutex_class m(mutex);
167 if(linear >= linear_size)
168 return std::make_pair(reinterpret_cast<memory_region*>(NULL), 0);
169 size_t lb = 0;
170 size_t ub = linear_bases.size() - 1;
171 while(lb < ub) {
172 size_t mb = (lb + ub) / 2;
173 if(linear_bases[mb] > linear) {
174 ub = mb;
175 continue;
177 if(linear_bases[mb + 1] <= linear) {
178 lb = mb + 1;
179 continue;
181 return std::make_pair(u_lregions[mb], linear - linear_bases[mb]);
183 return std::make_pair(reinterpret_cast<memory_region*>(NULL), 0);
186 #define MSR memory_space::read
187 #define MSW memory_space::write
188 #define MSRL memory_space::read_linear
189 #define MSWL memory_space::write_linear
191 template<> int8_t MSR (uint64_t address) { return internal_read<int8_t, false>(*this, address); }
192 template<> uint8_t MSR (uint64_t address) { return internal_read<uint8_t, false>(*this, address); }
193 template<> int16_t MSR (uint64_t address) { return internal_read<int16_t, false>(*this, address); }
194 template<> uint16_t MSR (uint64_t address) { return internal_read<uint16_t, false>(*this, address); }
195 template<> int32_t MSR (uint64_t address) { return internal_read<int32_t, false>(*this, address); }
196 template<> uint32_t MSR (uint64_t address) { return internal_read<uint32_t, false>(*this, address); }
197 template<> int64_t MSR (uint64_t address) { return internal_read<int64_t, false>(*this, address); }
198 template<> uint64_t MSR (uint64_t address) { return internal_read<uint64_t, false>(*this, address); }
199 template<> bool MSW (uint64_t a, int8_t v) { return internal_write<int8_t, false>(*this, a, v); }
200 template<> bool MSW (uint64_t a, uint8_t v) { return internal_write<uint8_t, false>(*this, a, v); }
201 template<> bool MSW (uint64_t a, int16_t v) { return internal_write<int16_t, false>(*this, a, v); }
202 template<> bool MSW (uint64_t a, uint16_t v) { return internal_write<uint16_t, false>(*this, a, v); }
203 template<> bool MSW (uint64_t a, int32_t v) { return internal_write<int32_t, false>(*this, a, v); }
204 template<> bool MSW (uint64_t a, uint32_t v) { return internal_write<uint32_t, false>(*this, a, v); }
205 template<> bool MSW (uint64_t a, int64_t v) { return internal_write<int64_t, false>(*this, a, v); }
206 template<> bool MSW (uint64_t a, uint64_t v) { return internal_write<uint64_t, false>(*this, a, v); }
207 template<> int8_t MSRL (uint64_t address) { return internal_read<int8_t, true>(*this, address); }
208 template<> uint8_t MSRL (uint64_t address) { return internal_read<uint8_t, true>(*this, address); }
209 template<> int16_t MSRL (uint64_t address) { return internal_read<int16_t, true>(*this, address); }
210 template<> uint16_t MSRL (uint64_t address) { return internal_read<uint16_t, true>(*this, address); }
211 template<> int32_t MSRL (uint64_t address) { return internal_read<int32_t, true>(*this, address); }
212 template<> uint32_t MSRL (uint64_t address) { return internal_read<uint32_t, true>(*this, address); }
213 template<> int64_t MSRL (uint64_t address) { return internal_read<int64_t, true>(*this, address); }
214 template<> uint64_t MSRL (uint64_t address) { return internal_read<uint64_t, true>(*this, address); }
215 template<> bool MSWL (uint64_t a, int8_t v) { return internal_write<int8_t, true>(*this, a, v); }
216 template<> bool MSWL (uint64_t a, uint8_t v) { return internal_write<uint8_t, true>(*this, a, v); }
217 template<> bool MSWL (uint64_t a, int16_t v) { return internal_write<int16_t, true>(*this, a, v); }
218 template<> bool MSWL (uint64_t a, uint16_t v) { return internal_write<uint16_t, true>(*this, a, v); }
219 template<> bool MSWL (uint64_t a, int32_t v) { return internal_write<int32_t, true>(*this, a, v); }
220 template<> bool MSWL (uint64_t a, uint32_t v) { return internal_write<uint32_t, true>(*this, a, v); }
221 template<> bool MSWL (uint64_t a, int64_t v) { return internal_write<int64_t, true>(*this, a, v); }
222 template<> bool MSWL (uint64_t a, uint64_t v) { return internal_write<uint64_t, true>(*this, a, v); }
224 void memory_space::read_range(uint64_t address, void* buffer, size_t bsize)
226 auto g = lookup(address);
227 if(!g.first) {
228 memset(buffer, 0, bsize);
229 return;
231 read_range_r(*g.first, g.second, buffer, bsize);
234 bool memory_space::write_range(uint64_t address, const void* buffer, size_t bsize)
236 auto g = lookup(address);
237 if(!g.first)
238 return false;
239 return write_range_r(*g.first, g.second, buffer, bsize);
242 void memory_space::read_range_linear(uint64_t address, void* buffer, size_t bsize)
244 auto g = lookup_linear(address);
245 if(!g.first) {
246 memset(buffer, 0, bsize);
247 return;
249 read_range_r(*g.first, g.second, buffer, bsize);
252 bool memory_space::write_range_linear(uint64_t address, const void* buffer, size_t bsize)
254 auto g = lookup_linear(address);
255 if(!g.first)
256 return false;
257 return write_range_r(*g.first, g.second, buffer, bsize);
260 memory_region* memory_space::lookup_n(size_t n)
262 umutex_class m(mutex);
263 if(n >= u_regions.size())
264 return NULL;
265 return u_regions[n];
269 std::list<memory_region*> memory_space::get_regions()
271 umutex_class m(mutex);
272 std::list<memory_region*> r;
273 for(auto i : u_regions)
274 r.push_back(i);
275 return r;
278 void memory_space::set_regions(const std::list<memory_region*>& regions)
280 umutex_class m(mutex);
281 std::vector<memory_region*> n_regions;
282 std::vector<memory_region*> n_lregions;
283 std::vector<uint64_t> n_linear_bases;
284 //Calculate array sizes.
285 n_regions.resize(regions.size());
286 size_t linear_c = 0;
287 for(auto i : regions)
288 if(!i->readonly && !i->special)
289 linear_c++;
290 n_lregions.resize(linear_c);
291 n_linear_bases.resize(linear_c + 1);
293 //Fill the main array (it must be sorted!).
294 size_t i = 0;
295 for(auto j : regions)
296 n_regions[i++] = j;
297 std::sort(n_regions.begin(), n_regions.end(),
298 [](memory_region* a, memory_region* b) -> bool { return a->base < b->base; });
300 //Fill linear address arrays from the main array.
301 i = 0;
302 uint64_t base = 0;
303 for(auto j : n_regions) {
304 if(j->readonly || j->special)
305 continue;
306 n_lregions[i] = j;
307 n_linear_bases[i] = base;
308 base = base + j->size;
309 i++;
311 n_linear_bases[i] = base;
313 std::swap(u_regions, n_regions);
314 std::swap(u_lregions, n_lregions);
315 std::swap(linear_bases, n_linear_bases);
316 linear_size = base;
319 int memory_space::_get_system_endian()
321 if(sysendian)
322 return sysendian;
323 uint16_t magic = 258;
324 return (*reinterpret_cast<uint8_t*>(&magic) == 1) ? 1 : -1;
327 int memory_space::sysendian = 0;
329 memory_region_direct::memory_region_direct(const std::string& _name, uint64_t _base, int _endian,
330 unsigned char* _memory, size_t _size, bool _readonly)
332 name = _name;
333 base = _base;
334 endian = _endian;
335 direct_map = _memory;
336 size = _size;
337 readonly = _readonly;
338 special = false;
341 memory_region_direct::~memory_region_direct() throw() {}