BitGet/BitSet/BitOff

Share your advanced PureBasic knowledge/code with the community.
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

BitGet/BitSet/BitOff

Post by Lunasole »

It should be kind of "classic", a macro for bitwise operations ^^
But maybe someone find following variations usable/useful/interesting, time ago I composed them to make some "standart for bit moving stuff" (also to stop messing with bit constants and keep code clean from ugly bit operators).

NOTE: BitSet and BitOff here are specially designed to be usable in both forms, with asigment and without it: "A = BitSet (A, 1)" and "BitSet (A, 1)". Such an "amazing universality and portability" caused some restrictions (see comments).

Also, it is recommended to add all 3 keywords to custom keywords and set highlight color for them the same as used for PB keywords 8)

Code: Select all

; check if value has specified bit set up
; RETURN: 1 or 0
Macro BitGet (Bitfield, Bit)
	Bool(Bitfield & (1 << Bit)) ; or: ((Bitfield & (1 << Bit)) >> Bit) ; or:  ~~(Bitfield & (1 << Bit)) ; (both won't work nice)
EndMacro

; BitSet macro IS NOT SAFE to use in expressions where it is surrounded by "~,<<,>>,%,!" operators.
; BitOff macro too, but another set of operators: "~,<<,>>,%"
; You can add brackets to macro code to avoid such restrictions, but that will also disallow use w/o assignment.

; set up specified bit
; RETURN: modified bitfield, or result stored inside "Bitfield" if no assignment operator
Macro BitSet (Bitfield, Bit)
	Bitfield | (1 << Bit)
EndMacro
; removes specified bit
; &-fix by Blue (I think he made it in deep meditation)
; RETURN: modified bitfield, or result stored inside "Bitfield" if no assignment operator
Macro BitOff (Bitfield, Bit)
   Bitfield & ~(1 << Bit)
EndMacro
Last edited by Lunasole on Thu Jun 23, 2016 6:33 am, edited 5 times in total.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: BitGet/BitSet/BitOff

Post by Keya »

"Bit" is int representing bit # (starting from 1, up to 32 or more)
Bool(Value & (1 << (Bit - 1)))
if you change it to work from 0 instead of 1 the extra "- 1" instruction is not required :)

Code: Select all

Macro BitGet(bitnum, value=0)
  (value & (1<<bitnum))
EndMacro

Macro BitSet(bitnum, value=0)
  (value | (1<<bitnum))
EndMacro

Macro BitUnset(bitnum, value=0)
  (value ! (1<<bitnum))
EndMacro
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: BitGet/BitSet/BitOff

Post by Lunasole »

Keya wrote:if you change it to work from 0 instead of 1 the extra "- 1" instruction is not required :)
Yes ^^ I just like bits index starting from 1, one subtract doesn't seems too expensive for this (in most cases)
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: BitGet/BitSet/BitOff

Post by Lunasole »

More of it, I just checked the assembly (becoming interested of your post), seems PB compiler is smart enough to replace operations with constants to predefined values.

Thats a listing for "BitSet(A, 3)" for example:

Code: Select all

Disassembled code: 
00401071 mov dword [0x43703c], 0x1f
0040107B mov dword [0x43703c], 0x20
00401085 mov ebx, [0x4449f0]
0040108B or ebx, 0x4
0040108E mov [0x4449f0], ebx
The Bit param of macro is "1 << 2 = $04"
As you can see compiler directly placed 0x4 and does Or using it, so no additional "- 1" is performed if used constant value for Bit paramether (if use variable as parameter it will do - 1 always , but that should be rare case to send bit number as variable)

The full example:

Code: Select all

Macro BitSet (Value, Bit)
	Value | (1 << (Bit - 1))
EndMacro


EnableExplicit


DisableDebugger
Code_Start: ; Place code to be disassembled here
	Define A
	BitSet(A, 3)
Code_End:
EnableDebugger

Debug Hex(1 << 2)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Define Text$ = "Disassembled code: " + Chr(13)  
Define Addr.i
If ExamineAssembly(?Code_Start, ?Code_End)
	While NextInstruction()
	  Text$ + RSet(Hex(InstructionAddress()), SizeOf(Integer)*2, "0")
	  Text$ + " " + InstructionString() + Chr(13)
	Wend
EndIf
Debug Text$
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: BitGet/BitSet/BitOff

Post by Little John »

Lunasole wrote:It should be kind of "classic", a macro for bitwise operations
Lunasole wrote:I just like bits index starting from 1
Fine. ;-)

However, I think your macros are useless for about more than 99,9% of programmers, because they go by the classic rule that bit indexing starts from 0. :D
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: BitGet/BitSet/BitOff

Post by Lunasole »

Little John wrote: However, I think your macros are useless for about more than 99,9% of programmers, because they go by the classic rule that bit indexing starts from 0. :D
Pff, their choice.
Me think that starting from zero is stupid, cause 0 is better to use as NULL, i.e. variable not initialized / value disabled.
If you use -1 for that purpose following code will trigger as "TRUE", as A <> zero, and Purebasic #False = 0 too:

Code: Select all

Define A = -1

If A 
	Debug "true"
EndIf
and you have to write exactly "If Not A = -1" / "If A = #True", which defaces/bloats your code and is just unhandy.

So indexes starting from 1 is always better (or almost always) and not only when counting bits, if used with arrays/function returns etc it removes many other possible problems and extra trashing code too. Zero has to be reserved... for nothing.

And what about those macro, Keya already fixed them up to 0 ^^
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: BitGet/BitSet/BitOff

Post by Little John »

Lunasole wrote:
Little John wrote: However, I think your macros are useless for about more than 99,9% of programmers, because they go by the classic rule that bit indexing starts from 0. :D
Pff, their choice.
Me think that starting from zero is stupid
I'm glad that we have talked about it. ;-)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: BitGet/BitSet/BitOff

Post by wilbert »

Lunasole wrote:Me think that starting from zero is stupid
Start counting bits with 1 will get you in a lot of problems when you are working with api or asm.
For example if you want to check if your cpu supports SSE2, Intel mentions you have to check for bit 26 of a certain register.
If you start counting at 1, you will check for the wrong bit.

A bit off topic but in some countries people start counting age with 1.
The second you are born you are 1 year old because their logic says the time in the womb should also be counted for.
If you are not aware of this it can give a lot of confusion.

If you decide to start counting bits with 1, please add at least always a comment line to your code to make other people aware of it.
Windows (x64)
Raspberry Pi OS (Arm64)
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: BitGet/BitSet/BitOff

Post by Little John »

wilbert wrote:If you decide to start counting bits with 1, please add at least always a comment line to your code to make other people aware of it.
I absolutely agree. That's why I quoted his regarding text in red color.
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: BitGet/BitSet/BitOff

Post by Lunasole »

wilbert wrote:Start counting bits with 1 will get you in a lot of problems when you are working with api or asm.
For example if you want to check if your cpu supports SSE2, Intel mentions you have to check for bit 26 of a certain register.
If you start counting at 1, you will check for the wrong bit.
No any problems with APIs like Windows or other well-designed APIs. They using just a predefined bitmask constants for almost all cases, not referencing to a bit number.
No problems with asm too, unless using someones else code doing a lot bit operations referencing a bit number (pff, should be hard to find such, as in 95% cases predefined bitmask constants are used too, not an 1<< every time used as here in macro). Single cases like 26 bit are easily resolving, while you remember what your code doing.

I cannot even remember any case when index beginning from 1 lead me to some problems. except that it was curious for some time when I only started. So I have 0 reasons to care about zero-based rudiment, it's a personal choice (sometimes I anyway using it too, if this is really justified, almost no chance for this), the props of 1-based are obvious.
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
idle
Always Here
Always Here
Posts: 5913
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: BitGet/BitSet/BitOff

Post by idle »

It's probably better to use macros with the assembly instructions BTS, BTR, BTC and a procedure for BT

Code: Select all

CompilerIf SizeOf(Integer) = 8
  Macro ecx : rcx : EndMacro 
  Macro eax : rax : EndMacro
CompilerEndIf   

Macro BitSet(bitfield,bit) 
  EnableASM
  bts bitfield , bit 
  DisableASM 
EndMacro 

Macro BitReset(bitfield,bit)
  EnableASM 
  btr bitfield,bit
  DisableASM 
EndMacro   

Macro BitToggle(bitfield,bit)
  EnableASM 
  btc bitfield,bit
  DisableASM 
EndMacro   

Procedure BitGet(bitfield.i,bit.i)
  EnableASM 
  !mov ecx, [p.v_bit]
  !bt [p.v_bitfield],ecx
  DisableASM  
  !jnb lbitget 
  ProcedureReturn 1 
  !lbitget:
  !xor eax,eax 
  ProcedureReturn 
EndProcedure    

Global mybit.l 

BitSet(mybit,16) 
If BitGet(mybit,16)    
  Debug "on: " + Bin(mybit,#PB_Long) 
EndIf   

BitReset(mybit,16) 
If Not BitGet(mybit,16)    
  Debug "off: " + Bin(mybit,#PB_Long)  
EndIf   

BitToggle(mybit,16) 
Debug "toggle on: " + Bin(mybit,#PB_Long)  
BitToggle(mybit,16) 
Debug "toggle off: " + Bin(mybit,#PB_Long) 

Windows 11, Manjaro, Raspberry Pi OS
Image
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: BitGet/BitSet/BitOff

Post by Keya »

idle, very nice!!! :)

also here is Get Bit Count, i'm just linking for reference as i struggled to find it the other day as its thread subject is (probably the more correct) population count :D
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: BitGet/BitSet/BitOff

Post by Little John »

idle, very elegant.
Thanks for sharing!
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: BitGet/BitSet/BitOff

Post by Little John »

I found a small bug:

Code: Select all

!xor rax,rax
does not work with PB x86.
User avatar
idle
Always Here
Always Here
Posts: 5913
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: BitGet/BitSet/BitOff

Post by idle »

Little John wrote:I found a small bug:

Code: Select all

!xor rax,rax
does not work with PB x86.
thanks, I missed that, only tested on x64 and forgot to alias the register

If Lunasole want's an option to do indexing from 1, I can add them with a compiler if.
Windows 11, Manjaro, Raspberry Pi OS
Image
Post Reply