Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Endian swapping in QB64pe
#1
Both of these routines work if I implement them in C as an external library or use the bswap_32/bswap_64 libc functions.

They also happen to work in other BASIC's like FreeBASIC, BBC BASIC (SDL & Windows) and even the 32bit routine works in GFA BASIC on the Atari ST.

Looking at the CPP code QB64pe uses for _SHL/_SHR, it "should" work!

Is it QB64pe or me?

Code: (Select All)

' 64 Bit endian swap, should print DEADBEEFCAFEF00D
' But instead prints...            FEFFFEEFCAFEF00D
DIM SHARED AS _UNSIGNED _INTEGER64 rax, rdx
rdx = &H0DF0FECAEFBEADDE
rax = _SHR((rdx AND &HFF00000000000000), 56) OR _
      _SHR((rdx AND &H00FF000000000000), 40) OR _
      _SHR((rdx AND &H0000FF0000000000), 24) OR _
      _SHR((rdx AND &H000000FF00000000),  8) OR _
      _SHL((rdx AND &H00000000FF000000),  8) OR _
      _SHL((rdx AND &H0000000000FF0000), 24) OR _
      _SHL((rdx AND &H000000000000FF00), 40) OR _
      _SHL((rdx AND &H00000000000000FF), 56)
PRINT HEX$(rax)

'32 Bit endian swap, should print DEADBEEF
'But instead prints...            FEADBEEF
DIM SHARED AS _UNSIGNED LONG eax, edx
edx = &HEFBEADDE
eax = _SHR((edx AND &HFF000000), 24) OR _
      _SHR((edx AND &H00FF0000),  8) OR _
      _SHL((edx AND &H0000FF00),  8) OR _
      _SHL((edx AND &H000000FF), 24)
PRINT HEX$(eax)
Reply
#2
I tried to figure it out. I tried replacing all the _SHL, _SHR, AND, _READBIT and _SETBIT functions and in the end I got the same thing as with these functions. So I'll summarize. It's a perfect job for headaches. You can even see smoke coming from the ear. Your program even caused snow to melt in my area. I really don't know what the problem is!

Code: (Select All)


' 64 Bit endian swap, should print DEADBEEFCAFEF00D
' But instead prints...            FEFFFEEFCAFEF00D
Dim Shared As _Integer64 rax, rdx
rdx = &H0DF0FECAEFBEADDE
Print rdx
rax = mySHR64((rdx AND &HFF00000000000000), 56) OR _
      mySHR64((rdx AND &H00FF000000000000), 40) OR _
      mySHR64((rdx AND &H0000FF0000000000), 24) OR _
      mySHR64((rdx AND &H000000FF00000000),  8) OR _
      mySHL64((rdx AND &H00000000FF000000),  8) OR _
      mySHL64((rdx AND &H0000000000FF0000), 24) OR _
      mySHL64((rdx AND &H000000000000FF00), 40) OR _
      mySHL64((rdx AND &H00000000000000FF), 56)
Print Hex$(rax)

'32 Bit endian swap, should print DEADBEEF
'But instead prints...            FEADBEEF
Dim Shared As Long eax, edx
edx = &HEFBEADDE
eax = mySHR32((edx AND &HFF000000), 24) OR _
      mySHR32((edx AND &H00FF0000),  8) OR _
      mySHL32((edx AND &H0000FF00),  8) OR _
      mySHL32((edx AND &H000000FF), 24)
Print Hex$(eax)


' 32bit version: Left bit shift
Function MySHL32~& (x As _Unsigned Long, shift As Integer)
    Dim result As _Unsigned Long
    result = 0
    For i = 0 To 31
        ' If the bit at position i is set...
        If MyReadBit64(x, i) Then
            target = i + shift
            ' Set the target bit only if it fits in 32 bits
            If target <= 31 Then result = MySetBit64(result, target)
        End If
    Next i
    MySHL32 = result
End Function

' 32bit version: Right bit shift
Function MySHR32~& (x As _Unsigned Long, shift As Integer)
    Dim result As _Unsigned Long
    result = 0
    For i = 0 To 31
        If MyReadBit64(x, i) Then
            target = i - shift
            If target >= 0 Then result = MySetBit64(result, target)
        End If
    Next i
    MySHR32 = result
End Function

' 64bit version: Left bit shift
Function MySHL64~&& (x As _Unsigned _Integer64, shift As Integer)
    Dim result As _Unsigned _Integer64
    result = 0
    For i = 0 To 63
        If MyReadBit64(x, i) Then
            target = i + shift
            If target <= 63 Then result = MySetBit64(result, target)
        End If
    Next i
    MySHL64 = result
End Function

' 64bit version: Right bit shift
Function MySHR64~&& (x As _Unsigned _Integer64, shift As Integer)
    Dim result As _Unsigned _Integer64
    result = 0
    For i = 0 To 63
        If MyReadBit64(x, i) Then
            target = i - shift
            If target >= 0 Then result = MySetBit64(result, target)
        End If
    Next i
    MySHR64 = result
End Function
'maybe bug in ReadBit or Setbit? The same wrong result...

' Returns the value of the bit at position "pos" (0 = least significant bit) in a 64-bit number
Function MyReadBit64& (x As _Unsigned _Integer64, poss As Integer)
    Dim divisor As _Unsigned _Integer64
    divisor = 1
    For i = 1 To poss
        divisor = divisor * 2
    Next i
    MyReadBit64 = (x \ divisor) Mod 2
End Function

' Sets the bit at position "pos" in the 64-bit number x and returns the new number.
' If the bit is already set, returns the original number.
Function MySetBit64~&& (x As _Unsigned _Integer64, poss As Integer)
    Dim factor As _Unsigned _Integer64
    If MyReadBit64(x, poss) = 1 Then
        MySetBit64 = x
    Else
        factor = 1
        For i = 1 To poss
            factor = factor * 2
        Next i
        MySetBit64 = x + factor
    End If
End Function

again the same bad result....    Conclusion: I really don't know!


Reply
#3
@Petr, thanks for that.

I can now claim back at least 50% of my sanity. Confused ..... for the time being.
Reply
#4
I figure it's mainly missing the type symbols and screwing with you with that.

Code: (Select All)
' 64 Bit endian swap, should print DEADBEEFCAFEF00D
' But instead prints...            FEFFFEEFCAFEF00D
Dim Shared As _Integer64 rax, rdx
rdx = &H0DF0FECAEFBEADDE&&
Print Hex$(rdx)
Print Hex$(SwapEnd(rdx))
Print Hex$(SwapEnd(SwapEnd(rdx)))
Print
Print "(leading 0 is missing when printed)"

Function SwapEnd~&& (num As _Unsigned _Integer64)
Dim As _Unsigned _Integer64 temp, temp2
temp = _ShL(num And &HFF&&, 56) 'byte #1
temp = temp Or _ShL(num And &HFF00&&, 40) '2
temp = temp Or _ShL(num And &HFF0000&&, 24) '3
temp = temp Or _ShL(num And &HFF000000&&, 8) '4
temp = temp Or _ShR(num And &HFF00000000&&, 8) '5
temp = temp Or _ShR(num And &HFF0000000000&&, 24) '6
temp = temp Or _ShR(num And &HFF000000000000&&, 40) '7
temp = temp Or _ShR(num And &HFF00000000000000&&, 56) '8
SwapEnd = temp
End Function
Reply
#5
For an explanation of the issue you're seeing, (so you can account for it elsewhere in your code), see this:

Code: (Select All)
Print Hex$(&HFFFFFFFF And &HFF)
Print Hex$(&HFFFFFFFF And &HFFFF)
Print Hex$(&HFFFFFFFF And &HFFFFFFFF)
Print
Print &HFF
Print &HFFFF
Print &HFFFFFFFF
Print
Print &HFF~%%
Print &HFFFF~%
Print &HFFFFFFFF~&


Notice how the &H value changes on you? That's because QB64 and basic back in the day, didn't have UNSIGNED values, and also because they wanted to save as much precious memory as possible.

&HH was... an INTEGER, as there was no _BYTE type in QB45. Thus, it represented 255.
&HHHH was... an INTEGER, as integers are 2 bytes and that was the smallest size that could represent those hex digits. As an integer, that made its value -1.
&HHHHHHHH was... an LONG. 4 bytes to hold those 8 hex values. In a signed long, that also made the value -1.

So.... when you AND a value with -1, is that AND with a long or an integer or ????

You run into issues. (As you both have noticed.)



Solution?

Take that ambiguity out of your &H type.

IF &HFF is *supposed* to be a LONG, then mark it as one: &HFF&
IF &HFF is *supposed* to be an _INTEGER64, then mark it as one: &HFF&&

Those suffixes there on the end of that &H value is *essential* for what you're doing to work without issues for you. Else you're mixing integers with longs with int64s and... You get FEFOFLOPOP00PS
Reply
#6
(03-22-2025, 03:25 PM)SMcNeill Wrote: I figure it's mainly missing the type symbols and screwing with you with that.

Code: (Select All)
' 64 Bit endian swap, should print DEADBEEFCAFEF00D
' But instead prints...            FEFFFEEFCAFEF00D
Dim Shared As _Integer64 rax, rdx
rdx = &H0DF0FECAEFBEADDE&&
Print Hex$(rdx)
Print Hex$(SwapEnd(rdx))
Print Hex$(SwapEnd(SwapEnd(rdx)))
Print
Print "(leading 0 is missing when printed)"

Function SwapEnd~&& (num As _Unsigned _Integer64)
    Dim As _Unsigned _Integer64 temp, temp2
    temp = _ShL(num And &HFF&&, 56) 'byte #1
    temp = temp Or _ShL(num And &HFF00&&, 40) '2
    temp = temp Or _ShL(num And &HFF0000&&, 24) '3
    temp = temp Or _ShL(num And &HFF000000&&, 8) '4
    temp = temp Or _ShR(num And &HFF00000000&&, 8) '5
    temp = temp Or _ShR(num And &HFF0000000000&&, 24) '6
    temp = temp Or _ShR(num And &HFF000000000000&&, 40) '7
    temp = temp Or _ShR(num And &HFF00000000000000&&, 56) '8
    SwapEnd = temp
End Function
Thanks @SMcNeil, but I see this as not really acceptable in a language where I have explicitly declared the variable type, and I would have used OPTION _EXPLICIT also to make sure all my variables were correctly type declared to then have to use type suffixes too.

Too many unnecessary temporary variables also mixing unsigned and signed, although my code above uses 2, I only did that for clarity/sanity.  My original code was a function that worked on the reference and passed that back. Is this a quirk/niggle of QB64pe that will stay?
Reply
#7
See my post above yours.  I believe you missed it as I posted the second post and you were probably typing your response.

Basically it's an issue that goes back to QB45 days and us maintaining backwards compatibility for that old code.  You just need to add the proper suffix to your &H values to make certain that they're the return value you expect and not something unexpected.

And I'm not certain what you're talking about with temp variables mixing signed and unsigned?  You can strip those out completely.

Code: (Select All)
Dim As _Integer64 rdx
rdx = &H0DF0FECAEFBEADDE
Print Hex$(rdx)
Print Hex$(SwapEnd(rdx))
Print Hex$(SwapEnd(SwapEnd(rdx)))
Print
Print "(leading 0 is missing when printed)"

Function SwapEnd~&& (num As _Unsigned _Integer64)
SwapEnd = _ShL(num And &HFF&&, 56) OR _
_ShL(num And &HFF00&&, 40) OR _
_ShL(num And &HFF0000&&, 24) OR _
_ShL(num And &HFF000000&&, 8) OR _
_ShR(num And &HFF00000000&&, 8) OR _
_ShR(num And &HFF0000000000&&, 24) OR _
_ShR(num And &HFF000000000000&&, 40) OR _
_ShR(num And &HFF00000000000000&&, 56)
End Function
Reply
#8
(03-22-2025, 03:47 PM)tantalus Wrote: Thanks @SMcNeil, but I see this as not really acceptable in a language where I have explicitly declared the variable type, and I would have used OPTION _EXPLICIT also to make sure all my variables were correctly type declared to then have to use type suffixes too.

Too many unnecessary temporary variables also mixing unsigned and signed, although my code above uses 2, I only did that for clarity/sanity.  My original code was a function that worked on the reference and passed that back. Is this a quirk/niggle of QB64pe that will stay?
The problem is mixing the signed literals with the unsigned variables. `&HFF00` is a signed 16-bit integer, so it undergoes sign extension when AND'd with an unsigned 32-bit variable, turning it into `&HFFFFFF00`. If you specify the original as `&HFF00~%` (unsigned 16-bit integer) then the problem goes away since sign extension won't happen when the value gets promoted to 32-bit.

The other confusing aspect is that the number of digits you specify doesn't determine the type, so `&HFF00` and `&H0000FF00` are both signed 16-bit integers, adding extra zeros doesn't make it into a 32-bit integer. `&H0000FF00` is thus really `&HFFFFFF00` if you treat it as a 32-bit integer.

In other languages hex literals are usually unsigned so this doesn't come up when you use them, otherwise the rules regarding integer promotion and such are basically the same. As much as I'd like to change hex literals to be unsigned by default it can't be done without breaking a significant number of existing QBasic and QB64 programs so it's not happening. You can always add the type suffix to make the literal unsigned though.
Reply
#9
Oh, right! Damn, I didn't remember that before. Now I remember it, when I read it here, in the first draft of $Color:32 I saw these declarations with data types for constants. Well, at least I repeated the bit operations Smile Thanks for the explanation. I hope I finally remember it.


Reply




Users browsing this thread: 1 Guest(s)