x86 彙編/移位和旋轉
在邏輯移位指令(也稱為無符號移位)中,從末尾移出的位會消失(除了最後一個位進入進位標誌),並且空格始終填充零。邏輯移位最適合用於無符號數。
| shr cnt, dest | GAS 語法 |
| shr dest, cnt | Intel 語法 |
將dest邏輯右移cnt位。
| shl cnt, dest | GAS 語法 |
| shl dest, cnt | Intel 語法 |
將dest邏輯左移cnt位。
示例 (GAS 語法)
movw $ff00,%ax # ax=1111.1111.0000.0000 (0xff00, unsigned 65280, signed -256)
shrw $3,%ax # ax=0001.1111.1110.0000 (0x1fe0, signed and unsigned 8160)
# (logical shifting unsigned numbers right by 3
# is like integer division by 8)
shlw $1,%ax # ax=0011.1111.1100.0000 (0x3fc0, signed and unsigned 16320)
# (logical shifting unsigned numbers left by 1
# is like multiplication by 2)
在算術移位(也稱為有符號移位)中,與邏輯移位類似,從末尾移出的位會消失(除了最後一個位進入進位標誌)。但在算術移位中,空格的填充方式會保留被移位數字的符號。因此,算術移位更適合於二進位制補碼格式的有符號數。
| sar cnt, dest | GAS 語法 |
| sar dest, cnt | Intel 語法 |
將dest算術右移cnt位。空格用符號位(以保持原始值的符號)填充,即原始最高位。
| sal cnt, dest | GAS 語法 |
| sal dest, cnt | Intel 語法 |
將dest算術左移cnt位。底部的位不會影響符號,因此底部位用零填充。此指令等同於 SHL。
示例 (GAS 語法)
movw $ff00,%ax # ax=1111.1111.0000.0000 (0xff00, unsigned 65280, signed -256)
salw $2,%ax # ax=1111.1100.0000.0000 (0xfc00, unsigned 64512, signed -1024)
# (arithmetic shifting left by 2 is like multiplication by 4 for
# negative numbers, but has an impact on positives with most
# significant bit set (i.e. set bits shifted out))
sarw $5,%ax # ax=1111.1111.1110.0000 (0xffe0, unsigned 65504, signed -32)
# (arithmetic shifting right by 5 is like integer division by 32
# for negative numbers)
雙精度移位操作的名稱有些誤導,因此在本頁中它們被列為擴充套件移位指令。
它們可用於 16 位和 32 位資料實體(暫存器/記憶體位置)。src 運算元始終是暫存器,dest 運算元可以是暫存器或記憶體位置,cnt 運算元是立即位元組值或 CL 暫存器。在 64 位模式下,也可以定址 64 位資料。
| shld cnt, src, dest | GAS 語法 |
| shld dest, src, cnt | Intel 語法 |
shld 執行的操作是將 dest 中最重要的 cnt 位移出,但不是用零填充最低有效位,而是用 src 的最重要的 cnt 位填充。
| shrd cnt, src, dest | GAS 語法 |
| shrd dest, src, cnt | Intel 語法 |
類似地,shrd 操作將 dest 中最低有效的 cnt 位移出,並用 src 運算元的最低有效位填充最重要的 cnt 位。
英特爾的命名法具有誤導性,因為移位不作用於雙倍的基本運算元大小(即指定 32 位運算元不會使其成為 64 位移位):src 運算元始終保持不變。
此外,英特爾的參考手冊[1] 指出,當 cnt 大於運算元大小時,結果是未定義的,但至少對於 32 位和 64 位資料大小,已觀察到移位操作是透過 (cnt mod n) 執行的,其中 n 是資料大小。
示例 (GAS 語法)
xorw %ax,%ax # ax=0000.0000.0000.0000 (0x0000)
notw %ax # ax=1111.1111.1111.1111 (0xffff)
movw $0x5500,%bx # bx=0101.0101.0000.0000
shrdw $4,%ax,%bx # bx=1111.0101.0101.0000 (0xf550), ax is still 0xffff
shldw $8,%bx,%ax # ax=1111.1111.1111.0101 (0xfff5), bx is still 0xf550
其他示例(十進位制數用於解釋概念,而不是二進位制數)
# ax = 1234 5678
# bx = 8765 4321
shrd $3, %ax, %bx # ax = 1234 5678 bx = 6788 7654
# ax = 1234 5678
# bx = 8765 4321
shld $3, %ax, %bx # bx = 5432 1123 ax = 1234 5678
在旋轉指令中,從暫存器末尾移出的位會反饋到空格中。
| ror offset, variable | GAS 語法 |
| ror variable, offset | Intel 語法 |
將variable右移offset位。以下是這種操作的圖形表示
╭──────────────────╮ %al old │ 0 0 1 0'0 1 1 1 │ ror 1, %al ╰─╮╲ ╲ ╲ ╲ ╲ ╲╰─╯ %al new 1 0 0 1'0 0 1 1
旋轉位數offset被掩碼到最低 5 位(或在 64 位模式下為 6 位)。這等同於 操作,即整數除法的餘數(注意:)。這意味著,你永遠無法進行一次或多次“完整”旋轉。
Variable必須是暫存器或記憶體位置。Offset可以是- 立即值(其中值
1有一個專門的操作碼), - 或者
cl暫存器(即ecx的最低位元組)。
- 立即值(其中值
ror 僅在掩碼後的 offset 為非零時才會改變標誌。 CF 變成最近旋轉的位,因此在 ror 的情況下,result 的 MSB(“符號”。
此外,如果掩碼後的 offset = 1,OF ≔ result[MSB] ⊻ result[MSB−1],因此 OF 告訴我們“符號”是否發生了變化。
| rol offset, variable | GAS 語法 |
| rol variable, offset | Intel 語法 |
將variable左移offset位。
運算元和修改的標誌與 ror 相差無幾。但是,在掩碼後的 offset = 1 的情況下,OF 的定義不同,儘管實際上意義相同。對於 rol 1, x,OF ≔ result[MSB] ⊻ result[LSB]。
請注意,在 rol 的情況下,CF 包含 LSB。
類似於移位操作,旋轉操作可以使用進位位作為它移位的“額外”位。
| rcr cnt, dest | GAS 語法 |
| rcr dest, cnt | Intel 語法 |
將dest向右旋轉cnt位,並使用進位位。
| rcl cnt, dest | GAS 語法 |
| rcl dest, cnt | Intel 語法 |
將dest向左旋轉cnt位,並使用進位位。
除非另有說明,這些指令可以接受一個或兩個引數。如果只提供一個引數,則假定它是暫存器或記憶體地址,並且要移位/旋轉的位數為1(但是,這可能取決於使用的彙編器)。 shrl $1, %eax 等效於 shrl %eax (GAS 語法)。
- ↑ 英特爾® 64 和 IA-32 架構軟體開發人員手冊 第 2 卷 (PDF, 6.2 MB)