BitHelper

Share your advanced PureBasic knowledge/code with the community.
Gemorg
New User
New User
Posts: 7
Joined: Tue Feb 17, 2015 9:33 pm

BitHelper

Post 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
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

Re: BitHelper

Post by Thunder93 »

Good work for 32bit demonstration. :wink:
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: BitHelper

Post 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
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: BitHelper

Post 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.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: BitHelper

Post 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
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: BitHelper

Post 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))
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: BitHelper

Post 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
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: BitHelper

Post 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 :)
Post Reply