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