Bitmasking
Code examples and bitwise operators presented here use Python conventions. Other programming languages may use different symbols for some or all bitwise operations.
Bitmasking
Bitmasking is a powerful technique for manipulating binary data. It lets us select, modify, or test specific bits.
Understanding bitwise operations
Before we can work with bitmasks, we need to understand how to manipulate data at the bit level. This is done using bitwise operations and bit shift operations, which act directly on individual bits of some value.
Bitwise operations are the building blocks for masking, shifting, and setting flags in hardware and low-level software. They mirror the logic of real electronic circuits that use AND, OR, XOR, and NOT gates to process binary signals.
What bitwise operations do
A bitwise operation compares or modifies individual bits in a binary value. Each bit or pair of bits is processed separately, without affecting the others. This is very different from normal arithmetic, which treats the entire number as one value.
For example, consider these two binary numbers: 0b1100 and 0b1010. A bitwise operation applies a logical rule to each pair of bits. The result is another binary number of the same length.
| operation | description | example | result |
|---|---|---|---|
AND (&) |
Keeps only the bits that are 1 in both operands | 0b1100 & 0b1010 |
0b1000 |
OR (│) |
Sets bits to 1 if they are 1 in either operand | 0b1100 │ 0b1010 |
0b1110 |
XOR (^) |
Sets bits to 1 if they are different in each operand | 0b1100 ^ 0b1010 |
0b0110 |
NOT (~) |
Flips (inverts) every bit (1 becomes 0, 0 becomes 1) | ~0b1100 |
0b0011 |
These are the same logical operations that are physically implemented by transistor-based gates inside a CPU.
Bitwise AND
The bitwise AND operation, when used for bitmasking, is like a bit-level filter. It only keeps the bits that are set to 1 in both operands. If either bit is 0, the result will be 0.
Python uses & for bitwise AND.
>>> a = 0b1100
>>> b = 0b1010
>>> bin(a & b)
'0b1000'In other words, AND can be used to “mask out” bits we do not want. When we use it with a specific mask value, the 1 bits in the mask act like windows that let data through, and the 0 bits block.
Think of bitwise AND as a selective pass filter. It lets through bits wherever the mask has a 1 and blocks everything else.
Here’s another example: Say we wanted to isolate the upper nibble of a byte. First, let’s write a little function so we can display results in binary with a specific width guaranteed (Python will remove leading zeros and we want to keep them).
>>> def binw(n, width):
... # remove leading 0b, pad with zeros, add 0b back and return
... return f"0b{bin(n)[2:].zfill(width)}"
...
>>> binw(0b00000001, 8)
'0b00000001'So that checks out. Now, on to our example.
>>> a = 0b01011101
>>> b = 0b11110000 # mask will let bits of the upper nibble through
>>> binw(a & b, 8)
'0b01010000'Now let’s filter out all but the lower nibble.
>>> a = 0b01011101
>>> b = 0b00001111 # mask will let bits of the lower nibble through
>>> binw(a & b, 8)
'0b00001101'Bitwise OR
The bitwise OR operation “combines” bits. If either of the bits in a given position is 1, the result will also be 1. This is useful for setting specific bits to 1 while leaving others unchanged.
Python uses | for bitwise OR.
>>> a = 0b1100
>>> b = 0b1010
>>> binw(a | b, 4)
'0b1110'If you have a register and you want to turn on one or more bits without changing the others, you can apply bitwise OR with a mask that has 1s in the desired positions. For example: If a bit controls an LED or a CPU flag, bitwise OR with a mask will “turn on” that flag without disturbing other bits.
>>> a = 0b10001100
>>> b = 0b00100000 # to set this one bit in `a`, leaving other bits unchanged
>>> binw(a | b, 0)
'0b10101100'Notice that only one bit in a was changed.
Bitwise XOR
Bitwise XOR (exclusive OR) compares each bit in two operands, and sets bits 1 only when the two bits in the operands are different.
Python uses ^ for bitwise XOR.
>>> a = 0b1100
>>> b = 0b1010
>>> binw(a ^ b, 4)
'0b0110'Bitwise XOR is often used to “toggle” bits or detect differences between two patterns. If you XOR a bit with 1, it flips. If you XOR it with 0, it stays the same.
>>> 1 ^ 1
0
>>> 0 ^ 1
1
>>> 1 ^ 0
1
>>> 0 ^ 0
0This property makes XOR useful in checksum generation, encryption, and control logic.
Bitwise NOT
Bitwise NOT operation (also called bitwise complement) flips all the bits in an operand. Every 1 becomes a 0, and every 0 becomes a 1.
Python uses ~ for bitwise NOT.
>>> a = 0b1100
>>> binw(~a, 4)
0b0011In two’s complement for signed integers, the NOT operation is closely related to negation. However, when masking, we mostly use it to invert a mask. This allows us to clear bits rather than set them.
If a mask has 1s in the bits we want to keep, then ~mask will have 1s in the bits we want to remove. This pattern is used for clearing selected bits.
Shifting bits
In addition to bitwise logical operations, we can shift bits left or right within an operand. These operations are called “bit shifts.”
Python uses << for left shift and >> for right shift.
| operation | symbol | description | example | result |
|---|---|---|---|---|
| left shift | << |
moves bits to the left by n positions | 0001 << 3 |
1000 |
| right shift | >> |
moves bits to the right by n positions | 1000 >> 3 |
0001 |
(Note: left shifts multiply an integer by powers of two, while right shifts divide by powers of two.)
We can use bitshift to build bitmasks dynamically. For example, 1 << 7 creates a mask with only bit 7 set.
>>> binw(1 << 0, 8)
'0b00000001'
>>> binw(1 << 1, 8)
'0b00000010'
>>> binw(1 << 7, 8)
'0b10000000'Shifting is one of the most common operations used in masking. It lets you place a single 1 bit exactly where you need it without having to count manually.
Bitwise Operations in ARM Assembly
Bitwise logic operations are directly supported in the ARM instruction set. These are often used to manipulate bits inside registers.
| ARM instruction | meaning | use |
|---|---|---|
ANDS Rd, Rn, Rm |
bitwise AND, sets condition flags | mask selected bits |
ORR Rd, Rn, Rm |
bitwise OR | combine or set bits |
EOR Rd, Rn, Rm |
bitwise XOR | toggle bits |
BIC Rd, Rn, Rm |
bit Clear (Rn & ~Rm) |
clear specific bits |
For example:
MOV R1, #0xFF00
AND R2, R0, R1 ; Clear the lower 8 bits of R0
This instruction uses AND to keep only the upper byte of the register, which is a simple form of bitmasking.
© 2025 Clayton Cafiero.
No generative AI was used in writing this material. This was written the old-fashioned way.