Change 4 middle bits of a byte in C - c

I'm trying to change the 4 middle bits of a byte to correspond to the High nibble of another byte:
Suppose we start with:
In = 0bABCDEFGH
Out = 0bXXXXXXXX // Some random byte
I want:
Out = 0bXXABCDXX
Leaving whatever other bits were in Out's extremes unchanged.
How can I do this?
Note: The 'X' represents any bit, 0 or 1, just to distinguish what came from the input.
I got to:
(0b00111100 & (IN>>2)) = 0b00ABCD00
, which filters the high nibble and centers it but then what? How can I move it to Out?

simple:
out &= 0b11000011;
out |= (in >> 2 & 0b00111100);
out &= 0b11000011 sets out to 0bxx0000xx preserving 2 most significant bits and 2 least significant bits. in >> 2 shifts input by 2 giving us 0xYYABCDEF, YY could be 00 or 11 depending on what A is. To get rid of YY and EF we do & 0b00111100.
As pointed by #JB 0B is not standard notation, thus you should use something else, most preferably hex 0x notation. See this for more info.
Thus using hex this would be:
out &= 0xC3;
out |= (in >> 2 & 0x3C)
here is conversion table
`0xf` is `0b1111`
`0x3` is `0b0011`
`0xc` is `0b1100`

Assuming in and out are unsigned char, and that CHAR_BIT == 8:
out = (out & 0xC3) | ((in >> 2) & 0x3C);
i.e. 4 operations in total.

There are multiple alternatives. From a high-level perspective, you could
force the four middle bits of Out off, prepare a mask from In as show in your question, and combine Out and mask via bitwise OR (|)
force the four middle bits of Out off, prepare a mask from In as show in your question, and combine Out and mask via bitwise EXCLUSIVE OR (^)
force the four middle bits of Out on, prepare a mask from In similarly to how you do now, but with the outer bits on, and combine Out and mask via bitwise AND (&)
use a series of shifts, masks, and addition or bitwise OR operations to build up the wanted result section by section
Forcing bits off is achieved by bitwise AND with a mask that has 0s at (only) the positions you want to turn off.
Forcing bits on is achieved by bitwise OR with a mask that has 1s at (only) the positions you want to turn on.
You already seem to have a handle on shifting, though you do need to be careful there if you happen to be shifting objects of signed types. Prefer to use unsigned types for bit manipulation wherever possible.

Related

bitwise operation confusion in tutorials

how is:
GIMSK |= (1 << PCIE);
PCMSK |= (1 << PCINT4);
equal to (I can use the above or the below in my setup of my program, both work and activate pin 4), the GIMSK and the PCMSK are for some reason equal to each other, I am trying to learn why.
GIMSK = 0b00100000;
PCMSK = 0b00010000;
first:
https://thewanderingengineer.com/2014/08/11/pin-change-interrupts-on-attiny85/
second:
https://embeddedthoughts.com/2016/06/06/attiny85-introduction-to-pin-change-and-timer-interrupts/
data:
http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf
The sheet says PCIE is 0b00100000 in the bit mask, so somehow |= (1 << PCIE) equals that? I don't get it if PCIE is supposed to be that, doing a shift would change that value..
how and why would you use that instead of binary? I would guess it changes it but obviously, somehow it doesn't. I've asked this on several different places nobody has an answer so I came here. Hopefully someone can explain.
I'm new to C, I just learned bitwise operations today to try to figure out what is going on here, my code does work with either or but I want to know why! Thanks.
It is equal because all other bits of those registers were 0 before the OR operation
1u << x shifts one by x positions left. As a result you have the number with all bits except x cleared
Short Version
Break apart the compound expression from x |= y to x = x | y
Load integer '1' which is just binary `[0000 0001] and shift it to the spot we want.
Variable is defined in a header, or elsewhere which says where that bit is for compatibility. New card? Just get a new definition file- this is done automatically- usually). PCIE and PCINT4 are defined somewhere, look for it if needed, but this is supposed to handle those details for you.
Yet- we know is PCINT4 is 4 and PCIE is 5 respectively because of the second set- where we see a shifted '1'- 5 spaces for PCIE and 4 spaces for PCINT4. That's why they seem equivalent. Because they are literally equivalent- once you evaluate the expression you know that 0b means that what follows is binary (which might have been obvious). But they are NOT equivalent. Not exactly- see below--
4-So if you "OR" a register, it overrides whatever is there if TRUE and forces it to be what True and leaves everything else alone. We shift that 1 to show the bit we want, then we choose the operant that will have the effect we want. Look at the table for others.**
But we get GIMSK = GIMSK OR 0b00100000 and PCMSK = PCMSK OR 0b00010000;
Which is similar, but not exactly the same thing.
The devil is in the details, see below for
Detailed Explanation.
AKA someone better read this, took me forever.
GIMSK |= (1 << PCIE);
PCMSK |= (1 << PCINT4);
equal to (I can use the above or the below in my setup of my program, both work and activate pin 4), the
GIMSK and the PCMSK are for some reason equal to each other, I am
trying to learn why.
GIMSK = 0b00100000;
PCMSK = 0b00010000;
Let's take it lexicographically by the token to start, so we are all talking about the same thing.
GIMSK |= (1 << PCIE);
Starting at the first bit. Pick it apart-
GIMSK- variable or 'id'
|= - operator and assignment combo bitwise OR and =
1 - the integer 1
<< - shift operation
PCIE - Variable and ID
Of course, this makes it harder to explain. What is |=? I'm certain that led to confusion on this question for some. The better-known one is +=. So if I have a variable x, and I always add to itself, really any time you are counting, etc. the variable is on both sides of the equation. Like this:
x = x + 1 ;this is so common though, that in C, it was shortened to +=
x += 1 ; now its written like this. It takes some time
so programmers are lazy, and if something 'cool' pops up in one language, it usually spreads to the others, so most languages allow this now. It does make a difference if you write += or =+, at least in Java. Won't hit that though.
y = y * 2; it works for other types of operands
y *= 2; Now we take y, multiply by 2 and assign back to y.
Now, let's look at 'C' style bitwise operators- most languages have adopted similar notation though there are exceptions.
Usually in C you use two symbols to compare '&&" or '||' or '=='
Well, that's because the single operator compares bits. This got one symbol because it's much more natural on a computer and much more common. Not to us anymore, we are abstracted away from it by layers of software.
So we have: ** Good Source for more info
Bitwise AND (&)
Bitwise OR (|)
Bitwise XOR (^)
Bitwise NOT (~)
And we can also make this compond (Click for more info)
Basically, they compare some variable on the right with the left and assign it back to the right. Like this x = x* y => x *= y.
Likewise we have x &= y, x |= y and, x ^= y
So for the above- let's unwrap it first- write it out longhand to make it easier to understand-
GIMSK |= (1 << PCIE)
GIMSK = GIMSK | (1 << PCIE) #OK! much easier to understand if your new.
#NOW we can lexigraphically analyze this
VarA {assignment} VarA OR ( 1 {Operator} VarB )
#Ignore the assignment side, for now, practice order of Operations
#Start with Parenthetical Exp.
1 {Operator} VarB
#It turns out this is defined.
#OP Didnt know but computer does. = 5 in this case.
#so 1, shift left 5. To bitwise shift, need bits
1 => 0b00000001 << 5 = 0b00100000
# shift left is really multiplied by 2 in base 10, divide by 2 in shift right. Beware Right Shift, esp in float.
So now we have: GIMSK = GIMSK OR 0b00100000
GIMSK |= (1 << PCIE); PCMSK |= (1 << PCINT4);
GIMSK = 0b00100000; PCMSK = 0b00010000;
Which is just what you already said. More or less. The 2nd operations are not equivalent though as I mentioned above in the short answer. Thats covered at the end.
This is Assembly format, GIMSK is an 8-bit register. We created a bitmask, by moving a 1 to the register we want to effect, and putting a 0 in the bits we want to leave alone. The |= means we will compare the two the save it back to the same register. That's it. OR 1 will always turn it on. Which is what we want.
Think about what we want to do to start. We want to set a boolean value to HIGH or TRUE, or 1, however, you put it. We say "Lets set the register bit that we specify if your value (0/1) OR my value (1) is 1." Well, we know our value is 1 because that's what we put. So when you bit-wise OR (is that a verb?), you are writing a value on the basis of one of 2 values being a 1. It either writes a one or leaves a one, unless you send 0 and it sends 0 it stays off.
It says "I think this should be on. If any other process thinks this should be on, leave it even if I don't need it so (it sends a 0)" It's worth thinking through on paper, and thinking through each of the operands. Make a colored table, that's how I got to understand them. Not of the operands will flip the values whatever it is. AND checks the value for you, it leaves the register the same- Operating on each reg will have this effect. I used to have a cheat sheet, I would have loved to include, but I lost it- but it summarized for my dumb brain the behavior of each operand.
REMEMBER- we can not operate on a single bit. This is a critical bit you need to understand. You cant change just one bit. If I have 0010 0010 and I want to say, hey computer, change byte 6. You cant! You have to load the whole word or byte into a register or at least half (16bits in MIPS, 8 in ATMEL 16bit controllers**), and operate on the whole thing. You can't operate directly from memory (Ram, SSD, L2 Cache- way too far away). There's no such thing as popping a single bit into a register to change it, though there are tricks to make new bytes (8bit) in the shape you want. Want just the 6th bit, well {0100 000} -with AND, will get it for you. Then you can shift right, or divide by 2^6, etc. We will get back to this. First- the actions of the Comparators if you care to learn more:
*this chip is 8 bit. Doubt they have half read. Bit
Logical Operators v. Registers
Register(b) Me OR NOR XOR AND NAND N XNOR
1 0 1 0 1 0 1 0 0
0 0 0 1 0 0 1 1 1
1 1 1 0 0 1 0 0 1
0 1 1 0 1 0 1 1 0
So the above looks at the single bit we want to effect. The left 2 columns are all possible scenarios (just 4), where we show the bit of the register in that byte. I keep saying byte. A register is, as I mentioned 16 or 32 bits usually, so I really mean Word. I have just organized this example around a hypothetical 8 bit machine. edit- this is an 8 bit chip, 32 registers. one of which is this one
Now! What do we want to do? We Want to change 1 byte, which represents a boolean value, but we don't want to mess with the rest! If you OR across all 8 bytes- do this on paper- it leaves the values in there already alone. Perfect! That's what we want.
Whatever they are set to, they stay, if it's 1, OR leaves it a 1, if it's 0, it stays.
Ah, I should mention why.
Its because you start with 0000 0001 (1) so everything is 0, except the 1st bit. Why did we start with 0000 0001? because you told it to.
See here syntax Arduino Doc Bitwise Ops
So, without reviewing Binary, 1 in binary is 0000 0001 It should be noted, that in a computer, it can't tell that 11001100 isn't 11,001,100 (eleven million), or if its 204 (binary) or even 285,217,024 (if it was HEX), or 2,359,872 (in Octal).
The compiler and computer 'know' we always think base 10, but the computer never does, just base 2 or compressed for easy human reading, into octal (2^2,) or hex (2^4) eg, each 'character' is 2 bits or 4 bits. 0x0A is 0b1010. And right there is where I am getting at. We indicate the values are not base 10, with a prefix. 0b***** is binary. 0x**** is hex. And I can never remember Octal- no one uses it anyway.
So!
see here if needed: Another practical book I wrote on Bitwise Ops, that covers basic Binary a little.
Then you shift that bit by the PIN NUMBER I don't know the right term, and this is certainly not it, but you say the register you want to effect is the 5th register. Ok.
#take a 1,
0000 0001 = $temp
#shift it 5 spots "<<" , where 5 is the PCIE 'bit' value spot number.
1<<5 = 32
#binary equals 32.
You could replace either GIMSK value with 32 and it would be fine, again equivalent, or 0x020
# 0010 0000
# Then OR this with whats in the register now:
1010 1010 (made up number, a mix of ones and 0s)
0010 0000 (Our Value)
OR=>
1010 1010 Result.
Note how we left the other bits alone, and only changed what we wanted! Effective bit mask!
Now, why does it say PCIE, and whatever the other one is. Its because somewhere, when you compile, there is a file that assigns values to those variables. This allows the code to be compatible across several different chip designs. The ATMEGA and the ATTINY do not have the same interrupt pin. Though it likely goes to the same internal register.
#Take it bit by bit, no Pun intended
GIMSK |= (1 << PCIE);
PCMSK |= (1 << PCINT4);
GIMSK = 0b00100000;
PCMSK = 0b00010000;
Again, starting at the first bit from above.
GIMSK- some variable
|= - bitwise OR
1 - the integer 1
`<`< - shift operation
PCIE - another var
So all you are doing is taking a base 10 integer- 1, which we know equals 0b0000 0001, then we are pushing that 1 (now in binary to the spot indicated by PCIE or PCINT4. So the latter 2 are just simply variables that hold the bit number, so if it changes, the code doesn't break.
From the latter 2 lines, we infer that PCIE is 5 and PCINT4 is 4. GIMSK is now equal to 32 and the other 16. Shifting << and >> has the effect of multiplying or diving by 2. Although, shifting down is risky for reasons I won't get into, but if you need to multiply a number by 2, for a computer, it's much faster to shift left by 1 bit than it is to go through the multiplayer.
We talked about the OR already. It sets a 1, if there's not one, otherwise it leaves the other bits alone because they are 0.
Equivalent or Not??
GIMSK |= (1 << PCIE);
GIMSK = 0b00100000;
GIMSK = GIMSK OR 0b00100000
PCMSK |= (1 << PCINT4);
PCMSK = 0b00010000;
PCMSK = PCMSK OR 0b00010000
So evaluating the 1st expression in each set gets the 3rd equation. But notice they look a little different. They are not equivalent statements, though, as you say they may work. It depends on what those other bits are.
PCMSK = 0b00010000; #This sets the PCMSK register to be exactly
=> PCMSK = `0|0|0|1|0|0|0|0
#While
PCMSK = PCMSK OR 0b00010000; # yields PCMSK = `?|?|?|1|?|?|?|?`
#Obviously,
GIMSK = 0b00001000; # This sets the GIMSK register to be exactly
=> GIMSK = `0|0|0|0|1|0|0|0`
While`GIMSK = GIMSK OR 0b00001000; # yields
GIMSK = ` ?|?|?|?|1|?|?|? `
while the OR statement leaves the other bits alone, if they were set by something else, and just changes the 5 (or 4th bit) as the case my be. The OR statement is probably the better statement. If you found the latter statement suggested in a reputable place though, it's probably fine.
Conclusion
So that's it. It's much easier than you thought probably now that the different bits make sense. I wrote this though with the hope it'll give some lasting insight rather than just a quick answer. Although in truth- it was complicated. There are a LOT of computer science concepts buried in those 2 statements, that if you're not in the know, might as well be hieroglyphics.
All this makes much more sense if you dive into how a computer works.
Check out Chapter 2 and 3 of Computer Organization and Design (5th ed) Patterson and Hennesy. It's the standard. If this is for fun, you can skim it. But the computer has Registered, of a defined width- 8, 16, 32. and 64 or even 128 (rarely e.g. x86 AVX- Intel x86). but usually 32 bit. These are the bits of data in hand, what the processor actually touches. The processor can only operate on registers. So everything, under the hood, will end up back there.
Now using interrupts correctly is a whole other topic. I again recommend the same book- Ch 5 and Appendix A7
Note- My assembly class was in MIPS. I've never specifically studied this microcontroller. If I get some of the architecture wrong, forgive me.

How do you compare only certain bits in data type?

I'm trying to learn a bit about emulation and I'm trying to think of how I can decode opcodes. Each opcode is a short data type, 16 bits. I'd like to be able to compare only specific sets of 4 bits. For example: there are multiple opcodes that start with 00, such as 0x00E0.
I'd like to be able to compare each of these values in either bit or hexidecimal form. I was thinking maybe something along the lines of bit shifting to bump of everything else off so that the bits I don't care about would zero out. That may cause issues for the center bits and will require additional steps. What kind of solutions do you guys use for a problem like this?
Use a bit mask, which has the bits set that you care about. Then use the & operator to zero out everything that you don't care about. For instance, say we want to compare the lowest four bits in a and b:
uint16 mask = 0x000f;
if ((a & mask) == (b & mask)) {
// lowest 4 bits are equal
}
This is simple bit manipulation. You can mask the relevant bits with
int x = opcode & 0x00f0;
and compare the resulting value
if (x == 0x00e0) {
/* do something */
}
you can easily create the mask of "nbits" and and shift "pos" number of bits and do comparision
uint32_t mask = ~((~0) << nbits);
if( (num(mask << pos)) == 0x00e0 ) {
/* Do something */
}

What does hibyte = Value >> 8 meaning?

I am using C for developing my program and I found out from an example code
unHiByte = unVal >> 8;
What does this mean? If unVal = 250. What could be the value for unHiByte?
>> in programming is a bitwise operation. The operation >> means shift right operation.
So unVal >> 8 means shift right unVal by 8 bits. Shifting the bits to the right can be interpreted as dividing the value by 2.
Hence, unHiByte = unval >> 8 means unHiByte = unVal/(2^8) (divide unVal by 2 eight times)
Without going into the shift operator itself (since that is answered already), here the assumption is that unVal is a two byte variable with a high byte (the upper 8 bits) and a low byte (the lower 8 bits). The intent is to obtain the value produced by ONLY the upper 8 bits and discarding the lower bits.
The shift operator though should easily be learned via any book / tutorial and perhaps was the reason some one down voted the question.
The >> is a bitwise right shift.
It operates on bits. With unHiByte = unVal >> 8; When unVal=250.
Its binary form is 11111010
Right shift means to shift the bits to the right. So when you shift 1111 1010, 8 digits to right you get 0000 0000.
Note: You can easily determine the right shift operation result by dividing the number to the left of >> by 2^(number to right of >>)
So, 250/28= 0
For example: if you have a hex 0x2A63 and you want to take 2A or you want to take 63 out of it, then you will do this.
For example, if we convert 2A63 to binary which is: 0010101001100011. (that is 16 bits, first 8 bits are 2A and the second 8 bits are 63)
The issue is that binary always starts from right. So we have to push the first 8 bits (2A) to the right side to be able to get it.
uint16_t hex = 0x2A63;
uint8_t part2A = (uint8_t)(hex >> 8) // Pushed the first
// eight bits (2A) to right and (63) is gone out of the way. Now we have 0000000000101010
// Now Line 2 returns for us 0x2A which the last 8 bits (2A).
// To get 63 we will do simply:
uint8_t part63 = (uint8_t)hex; // As by default the 63 is on the right most side in the binary.
It is that simple.

reading 2 bits off a register

I'm looking at a datasheet specification of a NIC and it says:
bits 2:3 of register contain the NIC speed, 4 contains link state, etc. How can I isolate these bits using bitwise?
For example, I've seen the code to isolate the link state which is something like:
(link_reg & (1 << 4))>>4
But I don't quite get why the right shift. I must say, I'm still not fairly comfortable with the bitwise ops, even though I understand how to convert to binary and what each operation does, but it doesn't ring as practical.
It depends on what you want to do with that bit. The link state, call it L is in a variable/register somewhere
43210
xxxxLxxxx
To isolate that bit you want to and it with a 1, a bitwise operation:
xxLxxxx
& 0010000
=========
00L0000
1<<4 = 1 with 4 zeros or 0b10000, the number you want to and with.
status&(1<<4)
This will give a result of either zero or 0b10000. You can do a boolean comparison to determine if it is false (zero) or true (not zero)
if(status&(1<<4))
{
//bit was on/one
}
else
{
//bit was off/zero
}
If you want to have the result be a 1 or zero, you need to shift the result to the ones column
(0b00L0000 >> 4) = 0b0000L
If the result of the and was zero then shifting still gives zero, if the result was 0b10000 then the shift right of 4 gives a 0b00001
so
(status&(1<<4))>>4 gives either a 1 or 0;
(xxxxLxxxx & (00001<<4))>>4 =
(xxxxLxxxx & (10000))>>4 =
(0000L0000) >> 4 =
0000L
Another way to do this using fewer operations is
(status>>4)&1;
xxxxLxxxx >> 4 = xxxxxxL
xxxxxxL & 00001 = 00000L
Easiest to look at some binary numbers.
Here's a possible register value, with the bit index underneath:
00111010
76543210
So, bit 4 is 1. How do we get just that bit? We construct a mask containing only that bit (which we can do by shifting a 1 into the right place, i.e. 1<<4), and use &:
00111010
& 00010000
----------
00010000
But we want a 0 or a 1. So, one way is to shift the result down: 00010000 >> 4 == 1. Another alternative is !!val, which turns 0 into 0 and nonzero into 1 (note that this only works for single bits, not a two-bit value like the link speed).
Now, if you want bits 3:2, you can use a mask with both of those bits set. You can write 3 << 2 to get 00001100 (since 3 has two bits set). Then we & with it:
00111010
& 00001100
----------
00001000
and shift down by 2 to get 10, the desired two bits. So, the statement to get the two-bit link speed would be (link_reg & (3<<2))>>2.
If you want to treat bits 2 and 3 (starting the count at 0) as a number, you can do this:
unsigned int n = (link_get & 0xF) >> 2;
The bitwise and with 15 (which is 0b1111 in binary) sets all but the bottom four bits to zero, and the following right-shift by 2 gets you the number in bits 2 and 3.
you can use this to determine if the bit at position pos is set in val:
#define CHECK_BIT(val, pos) ((val) & (1U<<(pos)))
if (CHECK_BIT(reg, 4)) {
/* bit 4 is set */
}
the bitwise and operator (&) sets each bit in the result to 1 if both operands have the corresponding bit set to 1. otherwise, the result bit is 0.
The problem is that isolating bits is not enough: you need to shift them to get the correct size order of the value.
In your example you have bit 2 and 3 for the size (I'm assuming that least significant is bit 0), it means that it is a value in range [0,3]. Now you can mask these bits with reg & (0x03<<2) or, converted, (reg & 0x12) but this is not enough:
reg 0110 1010 &
0x12 0000 1100
---------------
0x08 0000 1000
As you can see the result is 1000b which is 8, which is over the range. To solve this you need to shift back the result so that the least significant bit of the value you are interested in corresponds to the least significant bit of the containing byte:
0000 1000 >> 2 = 10b = 3
which now is correct.

Explanation of an algorithm to set, clear and test a single bit

Hey, in the Programming Pearls book, there is a source code for setting, clearing and testing a bit of the given index in an array of ints that is actually a set representation.
The code is the following:
#include<stdio.h>
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1+ N/BITSPERWORD];
void set(int i)
{
a[i>>SHIFT] |= (1<<(i & MASK));
}
void clr(int i)
{
a[i>>SHIFT] &= ~(1<<(i & MASK));
}
int test(int i)
{
a[i>>SHIFT] & (1<<(i & MASK));
}
Could somebody explain me the reason of the SHIFT and the MASK defines? And what are their purposes in the code?
I've already read the previous related question.
VonC posted a good answer about bitmasks in general. Here's some information that's more specific to the code you posted.
Given an integer representing a bit, we work out which member of the array holds that bit. That is: Bits 0 to 31 live in a[0], bits 32 to 63 live in a[1], etc. All that i>>SHIFT does is i / 32. This works out which member of a the bit lives in. With an optimising compiler, these are probably equivalent.
Obviously, now we've found out which member of a that bitflag lives in, we need to ensure that we set the correct bit in that integer. This is what 1 << i does. However, we need to ensure that we don't try to access the 33rd bit in a 32-bit integer, so the shift operation is constrained by using 1 << (i & 0x1F). The magic here is that 0x1F is 31, so we'll never left-shift the bit represented by i more than 31 places (otherwise it should have gone in the next member of a).
From Here (General answer to get this thread started)
A bit mask is a value (which may be stored in a variable) that enables you to isolate a specific set of bits within an integer type.
Normally the masked will have the bits you are interested in set to 1 and all the other bits set to 0. The mask then allows you to isolate the value of the bits, clear all the bits or set all the bits or set a new value to the bits.
Masks (particularly multi-bit ones) often have an associated shift value which is the amount the bits need shifting left so that the least significant masked bit is shifted to the least significant bit in the type.
For example using a 16 bit short data type suppose you wanted to be able to mask bits 3, 4 and 5 (LSB is number 0). You mask and shift would look something like
#define MASK 0x0038
#define SHIFT 3
Masks are often assigned in hexadecimal because it is easier to work with bits in the data type in that base as opposed to decimal. Historically octal has also been used for bit masks.
If I have a variable, var, that contains data that the mask is relevant to then I can isolate the bits like this
var & MASK
I can isolate all the other bits like this
var & ~MASK
I can clear the bits like this
var &= ~MASK;
I can clear all the other bits like this
var &= MASK;
I can set all the bits like this
var |= MASK;
I can set all the other bits like this
var |= ~MASK;
I can extract the decimal value of the bits like this
(var & MASK) >> SHIFT
I can assign a new value to the bits like this
var &= ~MASK;
var |= (newValue << SHIFT) & MASK;
When You want to set a bit inside the array, You have to
seek to the right array index and
set the appropriate bit inside this array item.
There are BITSPERWORD (=32) bits in one array item, which means that the index i has to be split into two parts:
rightmost 5 bits serve as an index in the array item and
the rest of the bits (leftmost 28) serve as an index into the array.
You get:
the leftmost 28 bits by discarding the rightmost five, which is exactly what i>>SHIFT does, and
the rightmost five bits by masking out anything but the rightmost five bits, which is what i & MASK does.
I guess You understand the rest.
Bitwise operation and the leading paragraphs of Mask are a concise explanation, and contain some pointers for further study.
Think of an 8-bit byte as a set of elements from an 8-member universe. A member is IN the set when the corresponding bit is set. Setting a bit more then once doesn't modify set membership (a bit can have only 2 states). The bitwise operators in C provide access to bits by masking and shifting.
The code is trying to store N bits by an array, where each element of the array contains BITSPERWORD (32) bits.
Thus if you're trying to access bit i, you need to calculate the index of the array element stores it (i/32), which is what i>>SHIFT does.
And then you need to access that bit in the array element we just got.
(i & MASK) gives the bit position at the array element (word).
(1<<(i & MASK)) makes the bit at that position to be set.
Now you can set/clear/test that bit in a[i>>SHIFT] by (1<<i & MASK)).
You may also think i is a 32 bits number, that bits 6~31 is the index of the array element stores it, bits 0~5 represents the bit position in the word.

Resources