The bit shifting operations are fairly straightforward.
; arithmetic (signed) shifts SHAL Rn ; Rn <<= 1, T = the bit shifted out SHAR Rn ; Rn >>= 1, T = the bit shifted out ; logical (unsigned) shifts SHLL Rn ; Rn <<= 1, T = the bit shifted out SHLR Rn ; Rn >>= 1, T = the bit shifted out SHLL2 Rn ; Rn <<= 2 SHLR2 Rn ; Rn >>= 2 SHLL8 Rn ; Rn <<= 8 SHLR8 Rn ; Rn >>= 8 SHLL16 Rn ; Rn <<= 16 SHLR16 Rn ; Rn >>= 16
You cannot shift by arbitrary constant amounts. Only certain fixed values are permitted. If you want to shift left by, say, 9, you’ll have to construct it from a SHLL8
and a SHLL
.
Note also that SHAL
and SHLL
are functionally equivalent. But they have different encodings, so the designers burned an opcode for a redundant operation.
There are no “large shift” options for right shifts. You can perform multiple one-bit shifts, or use a variable shift:
SHAD Rm, Rn ; if Rm > 0: Rn <<= (31 & Rm) ; if Rm = 0: nop ; if Rm < 0: Rn >>= (31 & -Rm), signed SHLD Rm, Rn ; if Rm > 0: Rn <<= (31 & Rm) ; if Rm = 0: nop ; if Rm < 0: Rn >>= (31 & -Rm), unsigned
Note that these shift instructions shift both left and right, depending on the sign of the shift amount. If you want to shift right by an amount in a register, you therefore need to negate the value, and then shift left.
Finally, we have rotation.
ROTL Rn ; rotate left, T contains carried-out bit ROTR Rn ; rotate right, T contains carried-out bit ROTCL Rn ; 33-bit rotate through T ROTCR Rn ; 33-bit rotate through T
The rotation instructions rotate either a 32-bit or 33-bit value by one position. For the 32-bit rotations, the bit that rotated off the end is copied to T. For the 33-bit rotations, the T flag acts as the 33rd bit.
We saw earlier that there is no NEGV
instruction. To detect overflow from a negation, you just have to check for the value 0x80000000
directly. Here’s the shortest sequence I could come up with:
; branch if Rn equals 0x80000000 rotl Rn ; rotate left one bit dt Rn ; decrement and test for zero bt underflow ; Y: underflow occurred
The result of the DT
is zero if the previous value was 1, and the previous value was 1 if the original value was 0x80000000
.
This is a destructive operation, so do it in a scratch register. You should have one available, since it’s the source register for the NEGV
you were checking.
“There are no “large shift” options for right shifts.”
Can you elaborate? The available operations for left and right shifts look totally symmetrical to me, so I must have misunderstood what is meant here.
On another note, seeing another dive into a to me unfamiliar architecture is very exciting. I was fearing that we ran out of qualifying architectures already. (As far as I remember, you only cover architectures for which a version of Windows existed?)
Derp. I’m not sure what I was thinking when I wrote that.