Bit Shifting in Assembly
We’re going to talk about bit shifting!!
| |
| |
SHL - Shift logical left
C uses « operator when doing this, the first operand (source and destination) is an r/mX, second operand is either cl (lowest byte of rcx), or a 1 byte immediate. The 2nd operand is the number of places to shift. It multiplies the register by 2 for each place the value is shifted. It’s more efficient than a multiply instruction. Bits shifted off the left hand side are shifted into (set) the carry flag. For purposes of determining if the CF is set at the end, think of it as n independent 1 bit shifts. When you’re going to the left the LSB are filled in with zeros.
shl bl, 2
| |
Confused how it worked? Let’s start with the basics. Let’s say we have the decimal 5.
So in binary it looks like this and each position is a power of 2
| |
So if we did left shift in C:
| |
Visually it looks like this before:
+---------------------------------------------+
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
+---------------------------------------------+
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | = 5 |
+---------------------------------------------+
Now shift left by 1:
+----------------------------------------------+
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
+----------------------------------------------+
| 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | = 10 |
+----------------------------------------------+
As you can see here we indeed literally shifted it left by 1!
SHR - Shift Logical right
C uses » operator, first operand (source and destination) is an r/mX. Second operand is either cl (lowest byte of rcx), or a 1 byte immediate. The 2nd operand is the number of places to shift. It divides the register by 2 for each place the value is shifted. More efficient than a divide instruction. Bits shifted off the right hand side are shifted into (set) the carry flag (CF). For purposes of determining if the CF is set at the end think of it as n independent 1 bit shifts. When you’re shifting to the right the MSB are filled with zeros.
Let’s say we have 00110011b (bl - 0x33) and we shift it right by 2!
Before:
+----------------------------------------------+
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
+----------------------------------------------+
| 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | = 51 |
+----------------------------------------------+
After (shift right by 2):
+----------------------------------------------+
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
+----------------------------------------------+
| 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | = 12 |
+----------------------------------------------+
And the CF is 1! because the last bit that fell off the right edge was a 1.
Unsigned shifting
| |
| |
Where are the multiply and divide? :0
You may see shifts in asm when no one wrote any shifts in C. If a multiply by power of 2 or a divide by power of 2 exist in C, a compiler may just turn it into a shift left or a shift right. This particular assembly was compiled with optimization.
Differentiation between signed and unsigned in shifting
| |
The sign can cause integer overflows btw.
Let’s take a look at an example (this one is signed)
| |
SAR - Shift arithmetic right
Can be used with » operator too, if operands are signed. First operand (source and destination) is an r/mX, second operand is either cl (lowest byte of rcx) or a 1 byte immediate. The 2nd operand is the number of places to shift. It divides the register by 2 for each place the value is shifted. More efficient than divide instruction. Each bit shifted off the right side is placed in CF.
sar bl, 1
+-----------------------------------------------+
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
+-----------------------------------------------+
| 1 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | = 179 |
+-----------------------------------------------+
So if we shift right by 1:
+-----------------------------------------------+
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
+-----------------------------------------------+
| 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | |
+-----------------------------------------------+
( MSB ) The MSB must be filled in with whatever the sign bit is
Why should the MSB be filled in with whatever the sign bit is? Well, to preserve the negativeness or positiveness of the value that you’re shifting.
SAL - Shift Arithmetic left
Behaves exactly the same as SHL. First operand (source and destination) is an r/mX, second operand is either cl (lowest byte of rcx), or a 1 byte immediate. The 2nd operand is the number of places to shift. It multiplies the register by 2 for each place the value is shifted. More efficient than a multiply instruction. Each bit shifted off the left side is placed in CF.
+-----------------------------------------------+
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
+-----------------------------------------------+
| 1 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | |
+-----------------------------------------------+
So if we shifted this left by 1:
+-----------------------------------------------+
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | |
+-----------------------------------------------+
| 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | |
+-----------------------------------------------+