Sign in to follow this  
Followers 0
GregN

Looking up a bit(s) in a DWORD hex value - Need Advice

6 posts in this topic

Hi everyone,

I'm trying to make a simple app that can look up a bit within a 32bin HEX value.

Can someone provide some advice or examples?

Example1;

Hex value: 3F8A9556

I want to look up bit 31

(it should return 0)

Example2

Hex value: 1D12AAA

I want to look up bits 27-30

(it should return 0000 or just 0)

Thanks guys!

Greg

Share this post


Link to post
Share on other sites



This is how I'd do it: basically, you need to do two things.

1-) Shift your value to the right enough times as to position your first wanted bit in index 0

2-) Create and bitAND a mask that unsets the bits you do not want.

It is a bit tricky because AutoIT doesn't have an unsigned-right-shift function. But you can get around that with a bit of thinking.

PS: I was bored and ended up doing it for ya. Here it is, with nice comments if anybody wants them.

I tested it a bit and it seems to work alright, but please check it yourself as well.

Func _BitRange($v, $start, $end=-1)
    ; We only work for integers, sorry
    If Not IsInt($v) Then Return SetError(1, 0, 0)

    ; Check for a valid range
    If $start < 0 Or $start > 31 Then
        Return SetError(2, 0, 0)
    EndIf

    ; Default to retrieving a single bit
    If $end < 0 Then
        $end = $start
    ; Another range check
    ElseIf $end < $start Then
        Return SetError(3, 0, 0)
    EndIf

    Local $mask, $range=$end-$start

    ; First, we check if the most significant (sign) bit is of any importance.
    If $end = 31 Then
        ; If it is, we first handle the special case of retrieving all the bits.
        If $start = 0 Then Return $v

        ; If fewer than 32 bits matter, we can use 1 of the meaningless ones as we wish.
        ; What allows this is the fact that signed shifts, as in AutoIT,
        ; set vacant bits to the current value of the sign bit.

        ; In this case, we can't ignore the most significant bit.
        ; Because of that, the mask works right-to-left:
        ; The sign bit is set, and propagates that value to (end-start) bits at its right
        $mask = BitShift(0x80000000, $range)

        ; We apply the mask first, then use a rotation to emulate our shift.
        ; This works because by applying the mask, we assured all non-wanted bits are 0s
        Return BitRotate(BitAND($v, $mask), -31+$range, "D")
    EndIf

    ; Here, we need not care for bit 31 at all.
    ; Therefore, this mask works left-to-right: the sign bit *cleans* the bits to its right.
    $mask = BitShift(0x7FFFFFFF, 30-$range)

    ; Here, we *first* shift the value to the right as much as needed to
    ; put our first wanted bit at index 0
    ; Then, afterwards, we apply our mask, and we're done.
    Return BitAND(BitShift($v, $start), $mask)
EndFunc

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

BinaryMid() ?

bah, reinventing the wheel is much funnier. Edited by danielkza

Share this post


Link to post
Share on other sites

Thanks a lot AdmiralAlkex and danielkza! I appreciate your guys help! I'm kind of a newb here, and have been trying to figure this out.

I'm going to try both options right away.

Greg

Share this post


Link to post
Share on other sites

I found danielkza and my approach return the same result, although the parameters differ being zero-based and 1-based respectively.

BinaryMid() works with bytes as opposed to bits - 32bit(4bytes)

The example with BinaryMid() uses the last byte, the last 8 bits.

; 0x3F8A9556 = 00111111 10001010 10010101 01010110
ConsoleWrite("danielkza " & _BitRange(0x3F8A9556, 23, 31) & @CRLF) ; 0-based
; 0x1D12AAA = 00000001 11010001 00101010 10101010
ConsoleWrite("danielkza " & _BitRange(0x1D12AAA, 27, 30) & @CRLF) ; 0-based


; 0x3F8A9556 = 00111111 10001010 10010101 01010110
ConsoleWrite("Malkey    " & _BitValueFromHexMid(0x3F8A9556, 24, 9) & @CRLF) ; 1-based
; 0x1D12AAA = 00000001 11010001 00101010 10101010
ConsoleWrite("Malkey    " & _BitValueFromHexMid(0x1D12AAA, 27, 4) & @CRLF) ; 1-based

; 0x3F8A9556 = 00111111 10001010 10010101 01010110
; 0x0000003F = 00111111 = 63
ConsoleWrite("Fourth Byte " & Dec(Hex(BinaryMid(0x3F8A9556, 4, 1)))) ; 1-based
ConsoleWrite(" = danielkza " & _BitRange(0x3F8A9556, 24, 31)) ; 0-based
ConsoleWrite(" = Malkey     " & _BitValueFromHexMid(0x3F8A9556, 25, 8) & @CRLF) ; 1-based


;For a 32bit number, the right most bit is bit number 0,
; the left most bit is bit number 31.
Func _BitRange($v, $start, $end = -1)
    ; We only work for integers, sorry
    If Not IsInt($v) Then Return SetError(1, 0, 0)

    ; Check for a valid range
    If $start < 0 Or $start > 31 Then
        Return SetError(2, 0, 0)
    EndIf

    ; Default to retrieving a single bit
    If $end < 0 Then
        $end = $start
        ; Another range check
    ElseIf $end < $start Then
        Return SetError(3, 0, 0)
    EndIf

    Local $mask, $range = $end - $start

    ; First, we check if the most significant (sign) bit is of any importance.
    If $end = 31 Then
        ; If it is, we first handle the special case of retrieving all the bits.
        If $start = 0 Then Return $v

        ; If fewer than 32 bits matter, we can use 1 of the meaningless ones as we wish.
        ; What allows this is the fact that signed shifts, as in AutoIT,
        ; set vacant bits to the current value of the sign bit.

        ; In this case, we can't ignore the most significant bit.
        ; Because of that, the mask works right-to-left:
        ; The sign bit is set, and propagates that value to (end-start) bits at its right
        $mask = BitShift(0x80000000, $range)

        ; We apply the mask first, then use a rotation to emulate our shift.
        ; This works because by applying the mask, we assured all non-wanted bits are 0s
        Return BitRotate(BitAND($v, $mask), -31 + $range, "D")
    EndIf

    ; Here, we need not care for bit 31 at all.
    ; Therefore, this mask works left-to-right: the sign bit *cleans* the bits to its right.
    $mask = BitShift(0x7FFFFFFF, 30 - $range)

    ; Here, we *first* shift the value to the right as much as needed to
    ; put our first wanted bit at index 0
    ; Then, afterwards, we apply our mask, and we're done.
    Return BitAND(BitShift($v, $start), $mask)
EndFunc ;==>_BitRange

;For a 32bit number, the right most bit is bit number 1,
; the left most bit is bit number 32.
Func _BitValueFromHexMid($hex, $iStart, $iCount)
    Local $b = "", $Output = 0
    ;Hex to Binary
    For $i = 1 To 32
        $b = BitAND($hex, 1) & $b
        $hex = BitShift($hex, 1)
    Next
    $Bin = StringMid(StringRegExpReplace($b, "(.{8})", "\1"), 33 - $iCount - $iStart + 1, $iCount)
    ;Binary to Dec
    Local $num = StringSplit($Bin, "")
    Local $Output = $num[$num[0]]
    For $n = 1 To $num[0] - 1
        If $num[$num[0] - $n] = "1" Then $Output += (2 ^ ($n))
    Next
    Return $Output
EndFunc ;==>_BitValueFromHexMid

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0