Page 1 of 3
BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 5:13 am
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
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
Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 5:29 am
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
Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 5:50 am
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)
Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 5:57 am
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$
Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 7:44 am
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.

Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 10:05 am
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.

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 ^^
Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 10:42 am
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.

Pff, their choice.
Me think that starting from zero is stupid
I'm glad that we have talked about it.

Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 10:45 am
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.
Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 10:55 am
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.
Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 6:24 pm
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.
Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 8:43 pm
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)
Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 8:51 pm
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

Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 9:17 pm
by Little John
idle, very elegant.
Thanks for sharing!
Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 10:18 pm
by Little John
I found a small bug:
does not work with PB
x86.
Re: BitGet/BitSet/BitOff
Posted: Tue Feb 09, 2016 10:53 pm
by idle
Little John wrote:I found a small bug:
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.