memoryqb64

Break up 32-bit hex value into 4 bytes [QB64]


I would want to ask, how do you break up a 32-bit hex (for example: CEED6644) into 4 bytes (var1 = CE, var2 = ED, var3 = 66, var4 = 44). In QB64 or QBasic. I would use this to store several data bytes into one array address. Something like this:

DIM Array(&HFFFF&) AS _UNSIGNED LONG
Array(&HAA00&) = &HCEED6644&
addr = &HAA00&
SUB PrintChar
SHARED addr

IF var1 = &HAA& THEN PRINT "A"
IF var1 = &HBB& THEN PRINT "B"
IF var1 = &HCC& THEN PRINT "C"
IF var1 = &HDD& THEN PRINT "D"
IF var1 = &HEE& THEN PRINT "E"
IF var1 = &HFF& THEN PRINT "F"
IF var1 = &H00& THEN PRINT "G"
IF var1 = &H11& THEN PRINT "H"

And so on...


Solution

  • You could use integer division (\) and bitwise AND (AND) to accomplish this.

    DIM x(0 TO 3) AS _UNSIGNED _BYTE
    a& = &HCEED6644&
    x(0) = (a& AND &HFF000000&) \ 2^24
    x(1) = (a& AND &H00FF0000&) \ 2^16
    x(2) = (a& AND &H0000FF00&) \ 2^8
    x(3) = a& AND &HFF&
    PRINT HEX$(x(0)); HEX$(x(1)); HEX$(x(2)); HEX$(x(3))
    

    Note that you could alternatively use a generic RShift~& function instead of raw integer division since what you're really doing is shifting bits:

    x(0) = RShift~&(a& AND &HFF000000&, 18)
    ...
    
    FUNCTION RShift~& (value AS _UNSIGNED LONG, shiftCount AS _UNSIGNED BYTE)
        ' Raise illegal function call if the shift count is greater than the width of the type.
        ' If shiftCount is not _UNSIGNED, then you must also check that it isn't less than 0.
        IF shiftCount > 32 THEN ERROR 5
        RShift~& = value / 2^shiftCount
    END FUNCTION
    

    Building upon that, you might create another function:

    FUNCTION ByteAt~%% (value AS _UNSIGNED LONG, position AS _UNSIGNED BYTE)
        'position must be in the range [0, 3].
        IF (position AND 3) <> position THEN ERROR 5
        ByteAt~%% = RShift~&(value AND LShift~&(&HFF&, 8*position), 8*position)
    END FUNCTION
    

    Note that an LShift~& function was used that shifts bits to the left (multiplication by a power of 2). A potentially better alternative would be to perform the right-shift first and just mask the lower 8 bits, eliminating the need for LShift~&:

    FUNCTION ByteAt~%% (value AS _UNSIGNED LONG, position AS _UNSIGNED BYTE)
        'position must be in the range [0, 3].
        IF (position AND 3) <> position THEN ERROR 5
        ByteAt~%% = RShift~&(value, 8*position) AND 255
    END FUNCTION
    

    Incidentally, another QB-like implementation known as FreeBASIC has an actual SHR operator, used like MOD or AND, to perform a shift operation directly instead of using division, which is potentially faster.

    You could also use QB64's DECLARE LIBRARY facility to create functions in C++ that will perform the shift operations:

    /*
     * Place in a separate "shift.h" file or something.
     */
    unsigned int LShift (unsigned int n, unsigned char count)
    {
        return n << count;
    }
    
    unsigned int RShift (unsigned int n, unsigned char count)
    {
        return n >> count;
    }
    

    Here's the full corresponding QB64 code:

    DECLARE LIBRARY "shift"
        FUNCTION LShift~& (value AS _UNSIGNED LONG, shiftCount AS _UNSIGNED _BYTE)
        FUNCTION RShift~& (value AS _UNSIGNED LONG, shiftCount AS _UNSIGNED _BYTE)
    END DECLARE
    
    x(0) = ByteAt~%%(a&, 0)
    x(1) = ByteAt~%%(a&, 1)
    x(2) = ByteAt~%%(a&, 2)
    x(3) = ByteAt~%%(a&, 3)
    END
    
    FUNCTION ByteAt~%% (value AS _UNSIGNED LONG, position AS _UNSIGNED BYTE)
        'position must be in the range [0, 3].
        IF (position AND 3) <> position THEN ERROR 5
        ByteAt~%% = RShift~&(value, 8*position) AND 255
    END FUNCTION
    

    If QB64 had a documented API, it might be possible to raise a QB64 error from the C++ code when the shift count is too high, rather than relying on the behavior of C++ to essentially ignore shift counts that are too high. Unfortunately, this isn't the case, and it might actually cause more problems than it's worth.