Endian Swap

  Some processors (Intel for one) put least significant byte first (little) in muli byte numeric types. Others put the most significant byte (big) first. In Latin based languages the left "end" is first and right "end" is last.

  The FASTPROCs and text code will run in either PB For Windows 10 or PBCC 6. To use in older versions change ENDIAN32 to a FUNCTION and the others to SUBs. Test code a little moe effort due to TXT.WINDOW. Same to put in DLL (any language can use) or SLL. Knowledge of assembly not needed to use "as-is". An alternative is to replace FASTPROC and END FASTPROC with PUSHes and POPs of the register(s) used, and put "in-line". To have multiple calls in one procedure make into subroutines (label and RETURN).

4 Byte Numeric Type Byte Swap

LONG or DWORD

'file: EndianSwap32Fast.inc

fastproc Endian32 (byval DwLg as long) as long
  ! mov ebx, DwLg
  ! bswap ebx
  ! mov DwLg, ebx
end fastproc = DwLg
Test Endian32
#compile exe
#dim all
#if %def(%pb_cc32)'ignore if PB for Windows
  #console off    'no console if PBCC
#endif
'
#include "EndianSwap32Fast.inc"

function pbmain () as long
  local Lg as long
  local hTxtWin, Dwd, pByte as dword
  txt.window ("Test EndianSwap32", 20, 20, 20, 76) to hTxtWin
  Lg = -2
  pByte = varptr(Lg)
  '-----------------------------------------------------------------------------
  txt.print hex$(Lg, 8) + " Windows numeric types are litte endian, but " + _
                          "HEX$() shows the Most"
  txt.print space$(9)   + "Significant byte on left for us (i.e. big endian)."
  txt.print
  txt.color = &h0000C000
  txt.print "Endian swap a LONG."
  txt.color = 0
  txt.print hex$(peek(byte, pByte), 2);
  txt.print hex$(peek(byte, pByte + 1), 2);
  txt.print hex$(peek(byte, pByte + 2), 2);
  txt.print hex$(peek(byte, pByte + 3), 2) + " LONG -2 in little endian " + _
  "         byte-by-byte"
  Lg = Endian32(Lg)
  txt.print hex$(peek(byte, pByte), 2);
  txt.print hex$(peek(byte, pByte + 1), 2);
  txt.print hex$(peek(byte, pByte + 2), 2);
  txt.print hex$(peek(byte, pByte + 3), 2) + " LONG -2 in big endian " + _
            "byte-by-byte"
  txt.print
  Dwd = 4026531841
  pByte = varptr(Dwd)
  txt.color = &h0000C000
  txt.print "Endian swap a DWORD. FASTPROC parameters are strickly LONGs, " + _
            "but to BSWAP "
  txt.print "but to BSWAP they are just 4 bytes."
  txt.color = 0
  txt.print hex$(peek(byte, pByte), 2);
  txt.print hex$(peek(byte, pByte + 1), 2);
  txt.print hex$(peek(byte, pByte + 2), 2);
  txt.print hex$(peek(byte, pByte + 3), 2) + " DWORD 4026531841 in little " + _
            "endian byte-by-byte"
  Dwd = Endian32(Dwd)
  txt.print hex$(peek(byte, pByte), 2);
  txt.print hex$(peek(byte, pByte + 1), 2);
  txt.print hex$(peek(byte, pByte + 2), 2);
  txt.print hex$(peek(byte, pByte + 3), 2) + " DWORD 4026531841 in big " + _
            "endian byte-by-byte"
  txt.print
  txt.color = &h0000C000
  txt.print "Endian swapping is symmetrical. One procedure will work for " + _
            "swaps in either"
  txt.print "direction (big to little or little to big)."
  txt.print
  txt.color = &h000000D0
  txt.print "Any key to close."
  txt.waitkey$
end function


8 Byte Numeric Type Byte Swap

QUAD
'File: EndianSwap64Fast.inc

fastproc Endian64 (byval pQd as long)
  ! mov edi, pQd
  ! mov eax, [edi]
  ! mov ebx, [edi + 4]
  ! xchg al, [edi + 7]
  ! xchg ah, [edi + 6]
  ! shr eax, 16
  ! xchg al, [edi + 5]
  ! xchg ah, [edi + 4]
  ! xchg bl, [edi + 3]
  ! xchg bh, [edi + 2]
  ! shr ebx, 16
  ! xchg bl, [edi + 1]
  ! xchg bh, [edi]
end fastproc
Test Endian64
#compile exe
#dim all
#if %def(%pb_cc32)'ignore if PB for Windows
  #console off    'no console if PBCC
#endif
'
#include "EndianSwap64Fast.inc"

function pbmain () as long
  local Qd as quad
  local hTxtWin, pQd as dword
  txt.window ("Test Endian64", 40, 40, 16, 64) to hTxtWin
  Qd = &h7000000320000001
  pQd = varptr(Qd)
  '-----------------------------------------------------------------------------
  txt.print hex$(Qd, 16) + " HEX$() of whole quad 8070450545669701633."
  txt.print
  txt.color = &h0000C000
  txt.print "byte-by-byte one byte HEX$ gives order of binary."
  txt.print "Endian swap a QUAD."
  txt.color = 0
  txt.print hex$(peek(byte, pQd), 2);
  txt.print hex$(peek(byte, pQd + 1), 2);
  txt.print hex$(peek(byte, pQd + 2), 2);
  txt.print hex$(peek(byte, pQd + 3), 2);
  txt.print hex$(peek(byte, pQd + 4), 2);
  txt.print hex$(peek(byte, pQd + 5), 2);
  txt.print hex$(peek(byte, pQd + 6), 2);
  txt.print hex$(peek(byte, pQd + 7), 2) + " QUAD in little endian byte-by-byte."
  Endian64(pQd)
  txt.print hex$(peek(byte, pQd), 2);
  txt.print hex$(peek(byte, pQd + 1), 2);
  txt.print hex$(peek(byte, pQd + 2), 2);
  txt.print hex$(peek(byte, pQd + 3), 2);
  txt.print hex$(peek(byte, pQd + 4), 2);
  txt.print hex$(peek(byte, pQd + 5), 2);
  txt.print hex$(peek(byte, pQd + 6), 2);
  txt.print hex$(peek(byte, pQd + 7), 2) + " QUAD in big endian byte-by-byte."
  txt.print
  txt.print "The only input parameter is a pointer."
  txt.print
  txt.print "To leave original QUAD variable unchanged, set a"
  txt.print "another variable and pass its pointer to Endian64."
  txt.print
  txt.color = &h000000D0
  txt.print "Any key to close."
  txt.waitkey$
end function

2 Byte Numeric Type Byte Swap

WORD or INTEGER
'File EndianSwap16Fast.inc

fastproc Endian16 (byval pWd as long)
  ! mov eax, pWd
  ! mov  bx, [eax]
  ! xchg bl, bh
  ! mov [eax], bx
end fastproc
Test Endian16
#compile exe
#dim all
#if %def(%pb_cc32)'ignore if PB for Windows
  #console off    'no console if PBCC
#endif
'
#include "File EndianSwap16Fast.inc"

function pbmain () as long
  local WdInt as word
  local hTxtWin, pWdInt as dword
  txt.window ("Test Endian16", 40, 40, 16, 64) to hTxtWin
  WdInt = &hF001
  pWDInt = varptr(WdInt)
  '-----------------------------------------------------------------------------
  txt.print hex$(WdInt, 4) + " HEX$() of whole word 61441."
  txt.print
  txt.color = &h0000C000
  txt.print "byte-by-byte one byte HEX$ gives order of binary."
  txt.print "Endian swap a WORD."
  txt.color = 0
  txt.print hex$(peek(byte, pWdInt), 2);
  txt.print hex$(peek(byte, pWdInt + 1), 2) + " WORD in little endian byte-by-byte."
  Endian16(pWdInt)
  txt.print hex$(peek(byte, pWdInt), 2);
  txt.print hex$(peek(byte, pWdInt + 1), 2) + " WORD in big endian byte-by-byte."
  txt.print
  txt.print "No test of INTEGER. Signed/unsigned tested in Endian32 demo."
  txt.print "The only input parameter is a pointer."
  txt.print
  txt.print "To leave original WORD/INTEGER variable unchanged, set a"
  txt.print "another variable and pass its pointer to Endian16."
  txt.print
  txt.color = &h000000D0
  txt.print "Any key to close."
  txt.waitkey$
end function

Other Size Binary With Bytes In String

For numerics with length other than 16, 32 or 64 bits. One example use would be an NMEA sentence that extends Unix time to 36 bits to avoid 2038 problem. It also has fraction of second in two 3 byte strings.
File: EndianSwapX.inc

fastproc EndianX (byval pX as long)
  ! mov edi, pX        'loop control ptr, dec from center each character
  ! mov ebx, edi       'when edi = ebx done
  ! mov edx, [edi - 4] 'string length
  ! mov ecx, edx
  ! and ecx, &h01???   '1 if length is odd
  ! shr edx, 1         '\ 2
  'either odd or even
  ! add edi, edx       'below center
  ! dec edi            'to zero base
  ! mov esi, edi       'copy, to be adjusted to above center
  'odd/even fork
  ! cmp ecx, 0
  ! jnz X_IsOdd
  'X_IsEven
  ! inc esi            'set to above center
  ! jmp LoopTop
  X_IsOdd:
  ! add esi, 2         'center not swapped, set to first above center
  'Do the endian swap.
  LoopTop:
  ! mov  al, [edi]     'byte of lower half to AL
  ! xchg al, [esi]     'swap AL with byte of upper half
  ! mov [edi], al      'AL back to byte of lower half
  ! cmp edi, ebx       'has first byte been swapped with last byte
  !  jz LoopDone
  ! inc esi            'adjust for next pass around loop
  ! dec edi
  ! jmp LoopTop
  LoopDone:
end fastproc
Test EndianX
#compile exe
#dim all
#if %def(%pb_cc32)'ignore if PB for Windows
  #console off    'no console if PBCC
#endif
'
#include "EndianSwapXFast.inc"

function pbmain () as long
  local X as string
  local hTxtWin, pX,  SzX as dword
  txt.window ("Test EndianX", 40, 40, 14, 62) to hTxtWin
  X = chr$(&h70,&h00,&h00,&h32,&h00,&h00,&h01)
  pX = strptr(X)
  '-----------------------------------------------------------------------------
  txt.print "To leave original string unchanged, copy to another"
  txt.print "string variable and pass its pointer to EndianX."
  txt.print
  txt.color = &h0000C000
  txt.print "Odd number of bytes."
  txt.color = 0
  txt.print hex$(peek(byte, pX), 2);
  txt.print hex$(peek(byte, pX + 1), 2);
  txt.print hex$(peek(byte, pX + 2), 2);
  txt.print hex$(peek(byte, pX + 3), 2);
  txt.print hex$(peek(byte, pX + 4), 2);
  txt.print hex$(peek(byte, pX + 5), 2);
  txt.print hex$(peek(byte, pX + 6), 2) + " 7 byte in big endian byte-by-byte."
  EndianX(pX)
  txt.print hex$(peek(byte, pX), 2);
  txt.print hex$(peek(byte, pX + 1), 2);
  txt.print hex$(peek(byte, pX + 2), 2);
  txt.print hex$(peek(byte, pX + 3), 2);
  txt.print hex$(peek(byte, pX + 4), 2);
  txt.print hex$(peek(byte, pX + 5), 2);
  txt.print hex$(peek(byte, pX + 6), 2)+ " 7 byte in little endian byte-by-byte."
  txt.print
  X = chr$(&h60,&h00,&h03,&h20,&h00,&h01)
  pX = strptr(X)
  txt.color = &h0000C000
  txt.print "Even number of bytes."
  txt.color = 0
  txt.print hex$(peek(byte, pX), 2);
  txt.print hex$(peek(byte, pX + 1), 2);
  txt.print hex$(peek(byte, pX + 2), 2);
  txt.print hex$(peek(byte, pX + 3), 2);
  txt.print hex$(peek(byte, pX + 4), 2);
  txt.print hex$(peek(byte, pX + 5), 2) + " 6 byte in big endian byte-by-byte."
  EndianX(pX)
  txt.print hex$(peek(byte, pX), 2);
  txt.print hex$(peek(byte, pX + 1), 2);
  txt.print hex$(peek(byte, pX + 2), 2);
  txt.print hex$(peek(byte, pX + 3), 2);
  txt.print hex$(peek(byte, pX + 4), 2);
  txt.print hex$(peek(byte, pX + 5), 2)+ " 6 byte in little endian byte-by-byte."
  txt.print
  txt.color = &h000000D0
  txt.print "Any key to close."

  txt.waitkey$
end function

Created on 12 April 2025.
Source and compiled code partial copyleft (ↄ), the limitation is you may not claim creation and attempt to copyright it.
Page text copyright © 2025, Dale Yarker.

To Domain Home.

home
To Dale's Notebook index.
notebook
To Programs index.
programs