Instructions of this group let us move bits within the destination operand, which is something we only partially have in high-level languages. We can shift, but we cannot rotate, neither can we implicitly specify arithmetic shifts (the selection of arithmetic or a logical shift is usually performed by high-level language implementation based on the type of data the operations are carried on).
Using shift instructions, in addition to their primary role of moving bits left or right a certain number of positions, is a simpler way to perform integer multiplication and division of the destination operand by powers of two. Additionally, two special shift instructions exist that let us move certain amount of bits from one location to another--to be more precise, from a register to another register or to a memory location.
Rotation instructions allow us, as the name suggests, to rotate bits from one end of the destination operand to another. It is important to mention that bits may be rotated through the CF (carry flag), meaning that the bit that is shifted out is stored in the CF while the value of the CF is shifted into the operand on the other side. Let's consider the following example, one of the simplest integrity control algorithms, CRC8:
poly = 0x31 ; The polynomial used for CRC8 calculation
xor dl, dl ; Initialise CRC state register with 0
mov al, 0x16 ; Prepare the sequence of 8 bits (may definitely
; be more than 8 bits)
mov ecx, 8 ; Set amount of iterations
crc_loop:
shl al, 1
rcl bl, 1
shl dl, 1
rcl bh, 1
xor bl, bh
test bl, 1
jz .noxor
xor dl, poly
.noxor:
loop crc_loop
The body of the loop in the preceding snippet was intentionally left without comments as we would like to take a closer look at what is happening there.
The first instruction of the loop shl al, 1 shifts out the most significant bit of the value we are calculating CRC8 for and stores it into the CF. The next instruction rcl bl, 1 stores the value of the CF (the bit we shifted out of our bit stream) into the BL register. The following two instructions do the same for the DL register, storing the most significant bit into BH. The side effect of the rcl instruction is that the most significant bits of the first BL and then the BH register are moved to the CF. Although it is of no importance in this specific case, we should not forget about this when rotating through the CF. At the end, this means after 8 iterations, the preceding code provides us with the CRC8 value for 0x16 (which is 0xE5) in the DL register.
The two shift and rotate instructions mentioned in the example have their right-sided counterparts:
- SHR: This shifts bits to the right, while saving the last bit shifted out in the CF
- RCR: This rotates bits to the right through the carry flag
There are a few additional instructions in this group that we cannot skip:
- SAR: This shifts bits to the right while "dragging" the sign bit instead of simply filling the "vacant" bits with zeroes.
- SAL: This is an arithmetic shift to the left. It is not truly an instruction, rather a mnemonic used for a programmer's convenience. The assembler generates the same encoding as for SHL.
- ROR: This rotates bits to the right. Each bit being shifted out to the right and shifted in to the left is also stored in the CF.
Finally, as it was mentioned earlier, the two special shift instructions are as follows:
- SHLD: This shifts a certain number of left-side (most significant) bits from a register into another register or into a memory location
- SHRD: This shifts a certain number of right-side (least significant) bits from a register into another register or into a memory location
Another new instruction in the previous example is TEST, but it will be explained in the next section.