1 /******************************************************************************/
2 /* Mednafen - Multi-system Emulator */
3 /******************************************************************************/
4 /* endian.h:
5 ** Copyright (C) 2006-2017 Mednafen Team
6 **
7 ** This program is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU General Public License
9 ** as published by the Free Software Foundation; either version 2
10 ** of the License, or (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software Foundation, Inc.,
19 ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 #ifndef __MDFN_ENDIAN_H
23 #define __MDFN_ENDIAN_H
24
25 void Endian_A16_Swap(void *src, uint32 nelements);
26 void Endian_A32_Swap(void *src, uint32 nelements);
27 void Endian_A64_Swap(void *src, uint32 nelements);
28
29 void Endian_A16_NE_LE(void *src, uint32 nelements);
30 void Endian_A32_NE_LE(void *src, uint32 nelements);
31 void Endian_A64_NE_LE(void *src, uint32 nelements);
32
33 void Endian_A16_NE_BE(void *src, uint32 nelements);
34 void Endian_A32_NE_BE(void *src, uint32 nelements);
35 void Endian_A64_NE_BE(void *src, uint32 nelements);
36
37 void Endian_V_NE_LE(void* p, size_t len);
38 void Endian_V_NE_BE(void* p, size_t len);
39
40 // This is a hack to remove the dependency on C++11. This is *not*
41 // correct for structs and anything but base types, but it shoud be
42 // enough for our case.
43 #define alignof(T) sizeof(T)
44
45 //
46 //
47 //
48
BitsExtract(const uint8 * ptr,const size_t bit_offset,const size_t bit_count)49 static INLINE uint32 BitsExtract(const uint8* ptr, const size_t bit_offset, const size_t bit_count)
50 {
51 uint32 ret = 0;
52
53 for(size_t x = 0; x < bit_count; x++)
54 {
55 size_t co = bit_offset + x;
56 bool b = (ptr[co >> 3] >> (co & 7)) & 1;
57
58 ret |= (uint64)b << x;
59 }
60
61 return ret;
62 }
63
BitsIntract(uint8 * ptr,const size_t bit_offset,const size_t bit_count,uint32 value)64 static INLINE void BitsIntract(uint8* ptr, const size_t bit_offset, const size_t bit_count, uint32 value)
65 {
66 for(size_t x = 0; x < bit_count; x++)
67 {
68 size_t co = bit_offset + x;
69 bool b = (value >> x) & 1;
70 uint8 tmp = ptr[co >> 3];
71
72 tmp &= ~(1 << (co & 7));
73 tmp |= b << (co & 7);
74
75 ptr[co >> 3] = tmp;
76 }
77 }
78
79 /*
80 Regarding safety of calling MDFN_*sb<true> on dynamically-allocated memory with new uint8[], see C++ standard 3.7.3.1(i.e. it should be
81 safe provided the offsets into the memory are aligned/multiples of the MDFN_*sb access type). malloc()'d and calloc()'d
82 memory should be safe as well.
83
84 Statically-allocated arrays/memory should be unioned with a big POD type or C++11 "alignas"'d. (May need to audit code to ensure
85 this is being done).
86 */
87
MDFN_bswap16(uint16 v)88 static INLINE uint16 MDFN_bswap16(uint16 v)
89 {
90 #if defined(_MSC_VER)
91 return _byteswap_ushort(v);
92 #else
93 return (v << 8) | (v >> 8);
94 #endif
95 }
96
MDFN_bswap32(uint32 v)97 static INLINE uint32 MDFN_bswap32(uint32 v)
98 {
99 #if defined(_MSC_VER)
100 return _byteswap_ulong(v);
101 #else
102 return (v << 24) | ((v & 0xFF00) << 8) | ((v >> 8) & 0xFF00) | (v >> 24);
103 #endif
104 }
105
MDFN_bswap64(uint64 v)106 static INLINE uint64 MDFN_bswap64(uint64 v)
107 {
108 #if defined(_MSC_VER)
109 return _byteswap_uint64(v);
110 #else
111 return (v << 56) | (v >> 56) | ((v & 0xFF00) << 40) | ((v >> 40) & 0xFF00) | ((uint64)MDFN_bswap32(v >> 16) << 16);
112 #endif
113 }
114
115 //
116 // X endian.
117 //
118 template<int isbigendian, typename T, bool aligned>
MDFN_deXsb(const void * ptr)119 static INLINE T MDFN_deXsb(const void* ptr)
120 {
121 T tmp;
122
123 memcpy(&tmp, MDFN_ASSUME_ALIGNED(ptr, (aligned ? alignof(T) : 1)), sizeof(T));
124
125 if(isbigendian != -1 && isbigendian != (int)MDFN_IS_BIGENDIAN)
126 {
127 #ifdef HAS_CXX11
128 static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Gummy penguins.");
129 #endif
130
131 if(sizeof(T) == 8)
132 return MDFN_bswap64(tmp);
133 else if(sizeof(T) == 4)
134 return MDFN_bswap32(tmp);
135 else if(sizeof(T) == 2)
136 return MDFN_bswap16(tmp);
137 }
138
139 return tmp;
140 }
141
142 //
143 // Native endian.
144 //
145 template<typename T, bool aligned>
MDFN_densb(const void * ptr)146 static INLINE T MDFN_densb(const void* ptr)
147 {
148 return MDFN_deXsb<-1, T, aligned>(ptr);
149 }
150
151 //
152 // Little endian.
153 //
154 template<typename T, bool aligned>
MDFN_delsb(const void * ptr)155 static INLINE T MDFN_delsb(const void* ptr)
156 {
157 return MDFN_deXsb<0, T, aligned>(ptr);
158 }
159
160 template<bool aligned>
MDFN_de16lsb(const void * ptr)161 static INLINE uint16 MDFN_de16lsb(const void* ptr)
162 {
163 return MDFN_delsb<uint16, aligned>(ptr);
164 }
165
MDFN_de24lsb(const void * ptr)166 static INLINE uint32 MDFN_de24lsb(const void* ptr)
167 {
168 const uint8* ptr_u8 = (const uint8*)ptr;
169
170 return (ptr_u8[0] << 0) | (ptr_u8[1] << 8) | (ptr_u8[2] << 16);
171 }
172
173 template<bool aligned>
MDFN_de32lsb(const void * ptr)174 static INLINE uint32 MDFN_de32lsb(const void* ptr)
175 {
176 return MDFN_delsb<uint32, aligned>(ptr);
177 }
178
179 template<bool aligned>
MDFN_de64lsb(const void * ptr)180 static INLINE uint64 MDFN_de64lsb(const void* ptr)
181 {
182 return MDFN_delsb<uint64, aligned>(ptr);
183 }
184
185 //
186 // Big endian.
187 //
188 template<typename T, bool aligned>
MDFN_demsb(const void * ptr)189 static INLINE T MDFN_demsb(const void* ptr)
190 {
191 return MDFN_deXsb<1, T, aligned>(ptr);
192 }
193
194 template<bool aligned>
MDFN_de16msb(const void * ptr)195 static INLINE uint16 MDFN_de16msb(const void* ptr)
196 {
197 return MDFN_demsb<uint16, aligned>(ptr);
198 }
199
MDFN_de24msb(const void * ptr)200 static INLINE uint32 MDFN_de24msb(const void* ptr)
201 {
202 const uint8* ptr_u8 = (const uint8*)ptr;
203
204 return (ptr_u8[0] << 16) | (ptr_u8[1] << 8) | (ptr_u8[2] << 0);
205 }
206
207 template<bool aligned>
MDFN_de32msb(const void * ptr)208 static INLINE uint32 MDFN_de32msb(const void* ptr)
209 {
210 return MDFN_demsb<uint32, aligned>(ptr);
211 }
212
213 template<bool aligned>
MDFN_de64msb(const void * ptr)214 static INLINE uint64 MDFN_de64msb(const void* ptr)
215 {
216 return MDFN_demsb<uint64, aligned>(ptr);
217 }
218
219 //
220 //
221 //
222 //
223 //
224 //
225 //
226 //
227
228 //
229 // X endian.
230 //
231 template<int isbigendian, typename T, bool aligned>
MDFN_enXsb(void * ptr,T value)232 static INLINE void MDFN_enXsb(void* ptr, T value)
233 {
234 T tmp = value;
235
236 if(isbigendian != -1 && isbigendian != (int)MDFN_IS_BIGENDIAN)
237 {
238 #ifdef HAS_CXX11
239 static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Gummy penguins.");
240 #endif
241
242 if(sizeof(T) == 8)
243 tmp = MDFN_bswap64(value);
244 else if(sizeof(T) == 4)
245 tmp = MDFN_bswap32(value);
246 else if(sizeof(T) == 2)
247 tmp = MDFN_bswap16(value);
248 }
249
250 memcpy(MDFN_ASSUME_ALIGNED(ptr, (aligned ? alignof(T) : 1)), &tmp, sizeof(T));
251 }
252
253 //
254 // Native endian.
255 //
256 template<typename T, bool aligned>
MDFN_ennsb(void * ptr,T value)257 static INLINE void MDFN_ennsb(void* ptr, T value)
258 {
259 MDFN_enXsb<-1, T, aligned>(ptr, value);
260 }
261
262 //
263 // Little endian.
264 //
265 template<typename T, bool aligned>
MDFN_enlsb(void * ptr,T value)266 static INLINE void MDFN_enlsb(void* ptr, T value)
267 {
268 MDFN_enXsb<0, T, aligned>(ptr, value);
269 }
270
271 template<bool aligned>
MDFN_en16lsb(void * ptr,uint16 value)272 static INLINE void MDFN_en16lsb(void* ptr, uint16 value)
273 {
274 MDFN_enlsb<uint16, aligned>(ptr, value);
275 }
276
MDFN_en24lsb(void * ptr,uint32 value)277 static INLINE void MDFN_en24lsb(void* ptr, uint32 value)
278 {
279 uint8* ptr_u8 = (uint8*)ptr;
280
281 ptr_u8[0] = value >> 0;
282 ptr_u8[1] = value >> 8;
283 ptr_u8[2] = value >> 16;
284 }
285
286 template<bool aligned>
MDFN_en32lsb(void * ptr,uint32 value)287 static INLINE void MDFN_en32lsb(void* ptr, uint32 value)
288 {
289 MDFN_enlsb<uint32, aligned>(ptr, value);
290 }
291
292 template<bool aligned>
MDFN_en64lsb(void * ptr,uint64 value)293 static INLINE void MDFN_en64lsb(void* ptr, uint64 value)
294 {
295 MDFN_enlsb<uint64, aligned>(ptr, value);
296 }
297
298
299 //
300 // Big endian.
301 //
302 template<typename T, bool aligned>
MDFN_enmsb(void * ptr,T value)303 static INLINE void MDFN_enmsb(void* ptr, T value)
304 {
305 MDFN_enXsb<1, T, aligned>(ptr, value);
306 }
307
308 template<bool aligned>
MDFN_en16msb(void * ptr,uint16 value)309 static INLINE void MDFN_en16msb(void* ptr, uint16 value)
310 {
311 MDFN_enmsb<uint16, aligned>(ptr, value);
312 }
313
MDFN_en24msb(void * ptr,uint32 value)314 static INLINE void MDFN_en24msb(void* ptr, uint32 value)
315 {
316 uint8* ptr_u8 = (uint8*)ptr;
317
318 ptr_u8[0] = value >> 16;
319 ptr_u8[1] = value >> 8;
320 ptr_u8[2] = value >> 0;
321 }
322
323 template<bool aligned>
MDFN_en32msb(void * ptr,uint32 value)324 static INLINE void MDFN_en32msb(void* ptr, uint32 value)
325 {
326 MDFN_enmsb<uint32, aligned>(ptr, value);
327 }
328
329 template<bool aligned>
MDFN_en64msb(void * ptr,uint64 value)330 static INLINE void MDFN_en64msb(void* ptr, uint64 value)
331 {
332 MDFN_enmsb<uint64, aligned>(ptr, value);
333 }
334
335
336 //
337 //
338 //
339 //
340 //
341 //
342
343 template<typename T, typename X>
neX_ptr_be(uintptr_t const base,const size_t byte_offset)344 static INLINE uintptr_t neX_ptr_be(uintptr_t const base, const size_t byte_offset)
345 {
346 #ifdef MSB_FIRST
347 return base + (byte_offset &~ (sizeof(T) - 1));
348 #else
349 return base + (((byte_offset &~ (sizeof(T) - 1)) ^ (sizeof(X) - std::min<size_t>(sizeof(X), sizeof(T)))));
350 #endif
351 }
352
353 template<typename T, typename X>
neX_ptr_le(uintptr_t const base,const size_t byte_offset)354 static INLINE uintptr_t neX_ptr_le(uintptr_t const base, const size_t byte_offset)
355 {
356 #ifdef LSB_FIRST
357 return base + (byte_offset &~ (sizeof(T) - 1));
358 #else
359 return base + (((byte_offset &~ (sizeof(T) - 1)) ^ (sizeof(X) - std::min<size_t>(sizeof(X), sizeof(T)))));
360 #endif
361 }
362
363 template<typename T, typename BT>
ne16_wbo_be(BT base,const size_t byte_offset,const T value)364 static INLINE void ne16_wbo_be(BT base, const size_t byte_offset, const T value)
365 {
366 #ifdef HAS_CXX11
367 static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size");
368 static_assert(std::is_same<BT, uintptr_t>::value || std::is_convertible<BT, uint16*>::value, "Wrong base type");
369 #endif
370
371 uintptr_t const ptr = neX_ptr_be<T, uint16>((uintptr_t)base, byte_offset);
372
373 if(sizeof(T) == 4)
374 {
375 uint16* const ptr16 = (uint16*)ptr;
376
377 ptr16[0] = value >> 16;
378 ptr16[1] = value;
379 }
380 else
381 *(T*)ptr = value;
382 }
383
384 template<typename T, typename BT>
ne16_rbo_be(BT base,const size_t byte_offset)385 static INLINE T ne16_rbo_be(BT base, const size_t byte_offset)
386 {
387 #ifdef HAS_CXX11
388 static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size");
389 static_assert(std::is_same<BT, uintptr_t>::value || std::is_convertible<BT, const uint16*>::value, "Wrong base type");
390 #endif
391
392 uintptr_t const ptr = neX_ptr_be<T, uint16>((uintptr_t)base, byte_offset);
393
394 if(sizeof(T) == 4)
395 {
396 uint16* const ptr16 = (uint16*)ptr;
397 T tmp;
398
399 tmp = ptr16[0] << 16;
400 tmp |= ptr16[1];
401
402 return tmp;
403 }
404 else
405 return *(T*)ptr;
406 }
407
408 template<typename T, bool IsWrite, typename BT>
ne16_rwbo_be(BT base,const size_t byte_offset,T * value)409 static INLINE void ne16_rwbo_be(BT base, const size_t byte_offset, T* value)
410 {
411 if(IsWrite)
412 ne16_wbo_be<T>(base, byte_offset, *value);
413 else
414 *value = ne16_rbo_be<T>(base, byte_offset);
415 }
416
417 //
418 //
419 //
420
421 template<typename T, typename BT>
ne16_wbo_le(BT base,const size_t byte_offset,const T value)422 static INLINE void ne16_wbo_le(BT base, const size_t byte_offset, const T value)
423 {
424 #ifdef HAS_CXX11
425 static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size");
426 static_assert(std::is_same<BT, uintptr_t>::value || std::is_convertible<BT, uint16*>::value, "Wrong base type");
427 #endif
428
429 uintptr_t const ptr = neX_ptr_le<T, uint16>((uintptr_t)base, byte_offset);
430
431 if(sizeof(T) == 4)
432 {
433 uint16* const ptr16 = (uint16*)ptr;
434
435 ptr16[0] = value;
436 ptr16[1] = value >> 16;
437 }
438 else
439 *(T*)ptr = value;
440 }
441
442 template<typename T, typename BT>
ne16_rbo_le(BT base,const size_t byte_offset)443 static INLINE T ne16_rbo_le(BT base, const size_t byte_offset)
444 {
445 #ifdef HAS_CXX11
446 static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4, "Unsupported type size");
447 static_assert(std::is_same<BT, uintptr_t>::value || std::is_convertible<BT, const uint16*>::value, "Wrong base type");
448 #endif
449
450 uintptr_t const ptr = neX_ptr_le<T, uint16>((uintptr_t)base, byte_offset);
451
452 if(sizeof(T) == 4)
453 {
454 uint16* const ptr16 = (uint16*)ptr;
455 T tmp;
456
457 tmp = ptr16[0];
458 tmp |= ptr16[1] << 16;
459
460 return tmp;
461 }
462 else
463 return *(T*)ptr;
464 }
465
466
467 template<typename T, bool IsWrite, typename BT>
ne16_rwbo_le(BT base,const size_t byte_offset,T * value)468 static INLINE void ne16_rwbo_le(BT base, const size_t byte_offset, T* value)
469 {
470 if(IsWrite)
471 ne16_wbo_le<T>(base, byte_offset, *value);
472 else
473 *value = ne16_rbo_le<T>(base, byte_offset);
474 }
475
476 //
477 //
478 //
479 template<typename T, typename BT>
ne64_wbo_be(BT base,const size_t byte_offset,const T value)480 static INLINE void ne64_wbo_be(BT base, const size_t byte_offset, const T value)
481 {
482 #ifdef HAS_CXX11
483 static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Unsupported type size");
484 static_assert(std::is_same<BT, uintptr_t>::value || std::is_convertible<BT, uint64*>::value, "Wrong base type");
485 #endif
486
487 uintptr_t const ptr = neX_ptr_be<T, uint64>((uintptr_t)base, byte_offset);
488
489 memcpy(MDFN_ASSUME_ALIGNED((void*)ptr, alignof(T)), &value, sizeof(T));
490 }
491
492 template<typename T, typename BT>
ne64_rbo_be(BT base,const size_t byte_offset)493 static INLINE T ne64_rbo_be(BT base, const size_t byte_offset)
494 {
495 #ifdef HAS_CXX11
496 static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Unsupported type size");
497 static_assert(std::is_same<BT, uintptr_t>::value || std::is_convertible<BT, const uint64*>::value, "Wrong base type");
498 #endif
499
500 uintptr_t const ptr = neX_ptr_be<T, uint64>((uintptr_t)base, byte_offset);
501 T ret;
502
503 memcpy(&ret, MDFN_ASSUME_ALIGNED((void*)ptr, alignof(T)), sizeof(T));
504
505 return ret;
506 }
507
508 template<typename T, bool IsWrite, typename BT>
ne64_rwbo_be(BT base,const size_t byte_offset,T * value)509 static INLINE void ne64_rwbo_be(BT base, const size_t byte_offset, T* value)
510 {
511 if(IsWrite)
512 ne64_wbo_be<T>(base, byte_offset, *value);
513 else
514 *value = ne64_rbo_be<T>(base, byte_offset);
515 }
516 //
517 //
518 //
519 template<typename T, typename BT>
ne64_wbo_le(BT base,const size_t byte_offset,const T value)520 static INLINE void ne64_wbo_le(BT base, const size_t byte_offset, const T value)
521 {
522 #ifdef HAS_CXX11
523 static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Unsupported type size");
524 static_assert(std::is_same<BT, uintptr_t>::value || std::is_convertible<BT, uint64*>::value, "Wrong base type");
525 #endif
526
527 uintptr_t const ptr = neX_ptr_le<T, uint64>((uintptr_t)base, byte_offset);
528
529 memcpy(MDFN_ASSUME_ALIGNED((void*)ptr, alignof(T)), &value, sizeof(T));
530 }
531
532 template<typename T, typename BT>
ne64_rbo_le(BT base,const size_t byte_offset)533 static INLINE T ne64_rbo_le(BT base, const size_t byte_offset)
534 {
535 #ifdef HAS_CXX11
536 static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Unsupported type size");
537 static_assert(std::is_same<BT, uintptr_t>::value || std::is_convertible<BT, const uint64*>::value, "Wrong base type");
538 #endif
539
540 uintptr_t const ptr = neX_ptr_le<T, uint64>((uintptr_t)base, byte_offset);
541 T ret;
542
543 memcpy(&ret, MDFN_ASSUME_ALIGNED((void*)ptr, alignof(T)), sizeof(T));
544
545 return ret;
546 }
547
548 template<typename T, bool IsWrite, typename BT>
ne64_rwbo_le(BT base,const size_t byte_offset,T * value)549 static INLINE void ne64_rwbo_le(BT base, const size_t byte_offset, T* value)
550 {
551 if(IsWrite)
552 ne64_wbo_le<T>(base, byte_offset, *value);
553 else
554 *value = ne64_rbo_le<T>(base, byte_offset);
555 }
556
557
558 #endif
559