There are several different kinds of shift operations in Assembly. The most basic ones are
sal. The last bit shifted out is stored in the carry flag.
|Shift Right: Shifts the bits in |
|Shifts Left: Shifts the bits in |
|Shift Arithmetic Left: Same as |
|Shift Arithmetic Right: Shifts by n bits to the right. Sign bit is not shifted. Equals to |
The rotate shift instructions are pretty much like logical shifts except that bits shifted off one end of the data are shifted in on the other side. Thus, the data is treated like in a circular structure. The two simplest rotate instructions are
ror which make left and right rotations. The shifted bit is stored in the carry flag.
Additionally, there are
rcl instructions which treat the carry flag as a single bit extension of the actual value and rotate the value and the carry flag.
This code example is taken and edited from the PC Assembly Book in such a way that it can be called within C as a function like
int32_t countActiveBits(int32_t numToRotate).
segment .text global countActiveBits countActiveBits: mov edx, [esp + 4] ; receive number to rotate mov eax, 0 ; eax will contain the count of ON bits mov ecx, 32 ; ecx is the loop counter, 32-bit register count_loop: shl edx, 1 ; shift bit into carry flag JNC skip_inc ; if CF == 0, goto skip_inc INC eax skip_inc: loop count_loop ret
or an alternative (suggested) way would be:
segment .text global countActiveBits countActiveBits: mov edx, [esp + 4] ; receive number to rotate mov eax, 0 ; ebx will contain the count of ON bits mov ecx, 32 ; ecx is the loop counter count_loop: shl edx, 1 ; shift bit into carry flag adc eax, 0 ; add only the carry flag to eax loop count_loop ; loop ret
CAUTION: Make sure the inserted and returned values are 32-bit values. If the return type required a 64-bit value,
EDX:EAX would be returned and that would result in a wrong return unless
xor edx, edx is inserted before returning.
NOTE: As only
EDX are changed, entering the function as well as any
pop instructions are not necessary.