Page 1 of 1

BitHelper

Posted: Wed Aug 19, 2015 1:32 am
by Gemorg
Hi guys! It may be useful. :)

GetBit(dw.l, nBitNumber) - gets a n-bit at number (the first bit as 0)
SetBit(*_dw, nBitNumber, nBitValue) - sets a n-bit at number (the first bit as 0)
GetBinDWord(dw.l) - works like a Bin()

The example is in a code.

Code: Select all

; BitHelper.pbi
; by Gemorg

Procedure GetBit(dw.l, nBitNumber)
  ; dw - целое в котором узнаем бит
  ; nBitNumber - номер бита, который узнаем (0..31)
  Protected dwMask = 1 << nBitNumber
  
  dw = dwMask & dw;
  
  If(dw)
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0
  
EndProcedure

Procedure SetBit(*_dw, nBitNumber, nBitValue)
  ; dw - целое, в котором задаем бит
  ; nBitNumber - номер бита, который задаем (0..31)
  Protected *dw.Long = *_dw
  Protected dwMask = 1 << nBitNumber
  
  If(nBitValue = 0)
    ; задаем 0
    dwMask = ~dwMask; 1111011111....
    *dw\l = *dw\l & dwMask; 0 & x = 0
  Else
    ; задаем 1
    *dw\l = *dw\l | dwMask; 1 | x = 1
  EndIf
EndProcedure

Procedure.s GetBinDWord(dw.l)
  Protected res.s = "", i
  For i = 0 To 31
    res + Str(GetBit(dw, i))
  Next
  res = ReverseString(res)
  ProcedureReturn res
EndProcedure

CompilerIf #PB_Compiler_IsMainFile
  x = 3
  Debug "Decimal value: " + x
  Debug "Bin value by Bin():"
  Debug RSet(Bin(x, #PB_Long), 32, "0")
  Debug "Bin value by GetBinDWord():"
  Debug GetBinDWord(x)
  Debug #CRLF$
  
  Debug "Set zero bit to 0..."
  SetBit(@x, 0, 0)
  Debug "Decimal value: " + x
  Debug "Bin value by Bin():"
  Debug RSet(Bin(x, #PB_Long), 32, "0")
  Debug "Bin value by GetBinDWord():"
  Debug GetBinDWord(x)
  Debug #CRLF$
  
  Debug "FR overflow test:"
  flag.l = 0
  ! MOV [v_flag], 2147483647 ; Long - 32 bit, max value - 2147483647
  ! ADD [v_flag], 1 ; Overflow
  ! PUSHFD ; push Flag Register
  ! POP EAX
  ! MOV [v_flag], EAX
  Debug GetBinDWord(flag)
  Debug "CF: " + GetBit(flag, 0)
  Debug "PF: " + GetBit(flag, 2)
  Debug "AF: " + GetBit(flag, 4)
  Debug "ZF: " + GetBit(flag, 6)
  Debug "SF: " + GetBit(flag, 7)
  Debug "TF: " + GetBit(flag, 8)
  Debug "IF: " + GetBit(flag, 9)
  Debug "OF: " + GetBit(flag, 11) ; must be 1
  Debug "NT: " + GetBit(flag, 14)
CompilerEndIf
Debug output:
Image

p.s. sorry for my english

Re: BitHelper

Posted: Wed Aug 19, 2015 4:23 am
by Thunder93
Good work for 32bit demonstration. :wink:

Re: BitHelper

Posted: Wed Aug 19, 2015 4:59 am
by Keya
here is mine also only for 32bit. Of course we can just use rax instead of eax on 64bit system, but I would like to extend it to be able to test 64bit values on 32bit system eventually.
I prefer your non-asm way as Id like to be able to use it on Mac which uses a different assembler and might cause issues (hopefully not with this simple code!) :)

Code: Select all

Procedure.l GetBit32(lVal.l, bitnum.l)
 ! xor eax, eax            ;eax = 0
 ! mov ecx, [p.v_bitnum]   ;bit # to test
 ! bt [p.v_lVal], ecx      ;Bit Test sets Carry flag if bit#<bitnum> is set
 ! setc al                 ;sets al=1 if Carry flag is set, for ProcedureReturn
 ProcedureReturn
EndProcedure

        ;31                              0
lVal.l = %00010010000000000000000000000000

For i = 0 To 31
  If GetBit32(lVal, i)
    Debug("Bit " + Str(i) + " is set")
  EndIf
Next i

Re: BitHelper

Posted: Wed Aug 19, 2015 5:29 am
by wilbert
Keya wrote:I prefer your non-asm way as Id like to be able to use it on Mac which uses a different assembler and might cause issues (hopefully not with this simple code!)
While there are differences between the different assemblers, they are not huge. In general it's not a problem to write assembler code that works on both OSX and Windows.
As for your routine, using bt on a memory address is a lot slower compared to using it on a register. Compare the speed of the routine you posted with the code below and see the difference

Code: Select all

Procedure.i GetBit32(lVal.l, bitnum)
  !movzx eax, byte [p.v_bitnum]
  !mov ecx, [p.v_lVal]
  !bt ecx, eax
  !setc al 
  ProcedureReturn
EndProcedure
On a quad is not much more complicated

Code: Select all

Procedure.i GetBit64(qVal.q, bitnum)
  !movzx eax, byte [p.v_bitnum]
  !bt eax, 5
  !jc getbit64_high
  !mov ecx, [p.v_qVal]
  !bt ecx, eax
  !setc al 
  ProcedureReturn
  !getbit64_high:
  !mov ecx, [p.v_qVal + 4]
  !bt ecx, eax
  !setc al 
  ProcedureReturn
EndProcedure
Both procedures tested on OSX and Windows.

Re: BitHelper

Posted: Wed Aug 19, 2015 5:59 am
by Keya
Great work wilbert, thankyou! Your optimised version goes through 100 million iterations on my computer in about 6797ms compared to my 11416ms :)
I accidentally had the Debugger enabled at first ... strangely with that on mine was about 1% faster than yours heehee

Thankyou very much also for x86-friendly 64bit version! btw my concern regarding asm on Mac was just in regards to that current "pointer variable bug" or whatever it is, ive only read briefly about it

Gemorg im sorry for hijacking your thread! :)

Code: Select all

Procedure.i GetBit8(bVal.a, bitnum)
  !movzx eax, byte [p.v_bitnum]
  !mov ecx, [p.v_bVal]
  !bt cl, al
  !setc al
  ProcedureReturn
EndProcedure

Procedure.i GetBit16(wVal.w, bitnum)
  !movzx eax, byte [p.v_bitnum]
  !mov ecx, [p.v_wVal]
  !bt cx, ax
  !setc al
  ProcedureReturn
EndProcedure

Procedure.i GetBit32(lVal.l, bitnum)
  !movzx eax, byte [p.v_bitnum]
  !mov ecx, [p.v_lVal]
  !bt ecx, eax
  !setc al
  ProcedureReturn
EndProcedure

CompilerIf #PB_Compiler_Processor = #PB_Processor_x64  
  Procedure.i GetBit64(qVal.q, bitnum)  ;X64-only
    !movzx rax, byte [p.v_bitnum]
    !mov rcx, [p.v_qVal]
    !bt rcx, rax
    !setc al
    ProcedureReturn
  EndProcedure  
CompilerElse  
  Procedure.i GetBit64(qVal.q, bitnum)  ;x86+x64, wilbert rocks
    !movzx eax, byte [p.v_bitnum]
    !bt eax, 5
    !jc getbit64_high
    !mov ecx, [p.v_qVal]
    !bt ecx, eax
    !setc al
    ProcedureReturn
    !getbit64_high:
    !mov ecx, [p.v_qVal + 4]
    !bt ecx, eax
    !setc al
    ProcedureReturn
  EndProcedure  
CompilerEndIf

Re: BitHelper

Posted: Wed Aug 19, 2015 6:43 am
by Little John
My code for setting a bit:

Code: Select all

Macro BitSet (_n_, _x_=0)
   ; set bit #n in x
   (_x_ | (1<<_n_))
EndMacro

; -- Demo
Debug Bin(BitSet(0))
Debug Bin(BitSet(1, %100))

Re: BitHelper

Posted: Wed Aug 19, 2015 8:26 am
by infratec
Following Little John:

Code: Select all

Macro BitGet(_n_, _x_=0)
  (_x_ & (1<<_n_))
EndMacro
Delivers TRUE or FALSE or better: not 0 or 0

Bernd

Re: BitHelper

Posted: Wed Aug 19, 2015 9:22 am
by Keya

Code: Select all

Macro BitGet(_n_, _x_=0)
  (_x_ & (1<<_n))
EndMacro
lVar.l = 5
lBit.l = BitGet(3, lVar)
=

Code: Select all

MOV DWORD PTR [0X4030EC], 5
MOV EBX, DWORD PTR [0X4030EC]
MOV EDI, DWORD PTR [0X4030E0]
MOV ECX, EDI
MOV EAX, 1
SHL EAX, CL
MOV EDI, EAX
AND EBX, EDI
MOV DWORD PTR [0X4030E8], EBX
Not quite as fast as Wilberts, but really nice and clean PB code heehee :)