一、CMP 指令
CMP 指令用來比較兩個數的大小。在 A64 指令集的實現中,CMP 指令內部調用 SUBS 指令來實現。
1.1、使用立即數的 CMP 指令
使用立即數的 CMP 指令的格式如下。
CMP <Xn|SP>, #<imm>{, <shift>}
上述指令等同于如下指令。
SUBS XZR, <Xn|SP>, #<imm> {, <shift>}
1.2、使用寄存器的 CMP 指令
使用寄存器的 CMP 指令的格式如下。
CMP <Xn|SP>, <R><m>{, <extend> {#<amount>}}
上述指令等同于如下指令。
SUBS XZR, <Xn|SP>, <R><m>{, <extend> {#<amount>}}
1.3、使用移位操作的 CMP 指令
使用移位操作的 CMP 指令的格式如下。
CMP <Xn>, <Xm>{, <shift> #<amount>}
上述指令等同于如下指令。
SUBS XZR, <Xn>, <Xm> {, <shift> #<amount>}
1.4、CMP 指令與條件操作后綴
CMP 指令常常和跳轉指令與條件操作后綴搭配使用,例如條件操作后綴 CS 表示是否發生了無符號數溢出,即 C 標志位是否置位,0 表示 C 標志位沒有置位。
如下代碼使用 CMP 指令來比較如下兩個寄存器。
cmp x1, x2
b.cs label
CMP 指令判斷兩個寄存器是否觸發無符號溢出的計算公式與 SUBS 指令類似:
X1 + NOT(X2) + 1
如果上述過程中發生了無符號數溢出,那么 C 標志位會置 1,則 b.cs 指令將會跳轉到 label 處。
下面的代碼用來比較 3 和 2 兩個立即數。
my_test:mov x1, #3mov x2, #21:cmp x1, x2b.cs 1bret
至于如何比較,需要根據 b 指令后面的條件操作后綴來定。CS 表示判斷是否發生無符號數溢出。根據 CMP 指令的運算公式可得,3 + NOT(2) +1,其中 NOT(2)把立即數 2 按位取反,取反后為 0xFFFFFFFFFFFFFFFD。 3 + 0xFFFFFFFFFFFFFFFD + 1 的最終結果為 1,這個過程中發生了無符號數溢出,C 標志位為 1。所以,b.cs 的判斷條件成立,跳轉到標簽 1 處,繼續執行。
二、移位指令
常見的移位指令如下。
- LSL:邏輯左移指令,最高位會被丟棄,最低位補 0,如下(a)圖所示。
- LSR:邏輯右移指令,最高位補 0,最低位會被丟棄,如下(b)圖所示。
- ASR:算術右移指令,最低位會被丟棄,最高位會按照符號進行擴展,如下(c)圖所示。
- ROR:循環右移指令,最低位會移動到最高位,如下(d)圖所示。
如下代碼使用了 ASR 和 LSR 指令。
ldr w1, =0x8000008a
asr w2, w1, 1
lsr w3, w1, 1
在上述代碼中,ASR 是算術右移指令,把 0x8000008A 右移一位并且對最高位進行有符號擴展,最后結果為 0xC0000045。LSR 是邏輯右移指令,把 0x8000008A 右移一位并且在最高位補 0,最后結果為 0x40000045。
三、位操作指令
3.1、與操作指令
與操作主要有兩條指令。
- AND:按位與操作。
- ANDS:帶條件標志位的與操作,影響 Z 標志位。
3.1.1、AND 指令
AND 指令的格式如下。
AND <Xd|SP>, <Xn>, #<imm>
AND <Xd>, <Xn>, <Xm>{, <shift> #<amount>}
AND 指令支持兩種方式。
- 立即數方式:對 Xn 寄存器的值和立即數 imm 進行與操作,把結果寫入 Xd/SP 寄存器中。
- 寄存器方式:先對 Xm 寄存器的值移位操作,然后再與 Xn 寄存器的值進行與操作,把結果寫入 Xd/SP 寄存器中。
指令參數說明如下:
shift 表示移位操作,支持 LSL、LSR、ASR 以及 ROR。
amount 表示移位數量,取值范圍為 0~63。
3.1.2、ANDS指令
ANDS 指令的格式如下。
ANDS <Xd>, <Xn>, #<imm>
ANDS <Xd>, <Xn>, <Xm>{, <shift> #<amount>}
ANDS 指令支持兩種方式。
- 立即數方式:對 Xn 寄存器的值和立即數 imm 進行與操作,把結果寫入 Xd/SP 寄存器中。
- 寄存器方式:先對 Xm 寄存器的值做移位操作,然后再與 Xn 寄存器的值進行與操作,把結果寫入 Xd/SP 寄存器中。
指令參數說明如下。
shift 表示移位操作,支持 LSL、LSR、ASR 以及 ROR。
amount 表示移位數量,取值范圍為 0~63。
與 AND 指令不一樣的地方是它會根據計算結果來影響 PSTATE 寄存器的 N、 Z、 C、 V 標志位。
如下代碼使用 ANDS 指令來對 0x3 和 0 做“與”操作。
mov x1, #0x3
mov x2, #0ands x3, x1, x2
mrs x0, nzcv
“與”操作的結果為 0。通過讀取 NZCV 寄存器,我們可以看到其中的 Z 標志位置位了。
3.2、或操作指令
3.2.1、ORR指令
ORR(或)操作指令的格式如下。
ORR <Xd|SP>, <Xn>, #<imm>
ORR <Xd>, <Xn>, <Xm>{, <shift> #<amount>}
ORR 指令支持兩種方式。
- 立即數方式:對 寄存器的值與立即數 Xn imm 進行或操作。
- 寄存器方式:先對 Xm 寄存器的值做移位操作,然后再與 Xn 寄存器的值進行或操作。
指令參數說明如下。
shift 表示移位操作,支持 LSL 、LSR 、ASR 以及 ROR。
amount 表示移位數量,取值范圍為 0~63。
3.2.2、EOR指令
EOR (異或)操作指令的格式如下。
EOR <Xd|SP>, <Xn>, #<imm>
EOR <Xd>, <Xn>, <Xm>{, <shift> #<amount>}
EOR 指令支持兩種方式。
- 立即數方式:對 Xn 寄存器的值與立即數 imm 進行異或操作。
- 寄存器方式:先對 Xm 寄存器的值做移位操作,然后再與 Xn 寄存器的值進行異或操作。
指令參數說明如下。
shift 表示移位操作,支持 LSL 、LSR 、ASR 以及 ROR。
amount 表示移位數量,取值范圍為 0~63。
異或操作的真值表如下。
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
從上述真值表可以發現 3 個特點。
- 0 異或任何數 = 任何數。
- 1 異或任何數 = 任何數取反。
- 任何數異或自己都等于 。
利用上述特點,異或操作有如下幾個非常常用的場景。
使某些特定的位翻轉。
例如, 想把 0b10100001 的第 2 位和第 3 位翻轉, 則可以對該數與 0b00000110 進行按位異或運算。
10100001 ^ 00000110 = 10100111
交換兩個數。
例如,交換兩個整數 a=0b10100001 和 b=0b00000110 的值可通過下列語句實現。
a = a^b; //a=10100111
b = b^a; //b=10100001
a = a^b; //a=00000110
在匯編程序里把變量設置為 。
eor x0, x0
判斷兩個整數是否相等。
bool is_identical(int a, int b)
{return ((a ^ b) == 0);
}
3.3、位清除操作指令
BIC (位清除操作)指令指令用于將寄存器中的某些位清零,而保持其他位不變。其核心作用是選擇性地清除(置 0)特定位置的位,常用于權限屏蔽、標志位清除等場景。其格式如下。
BIC <Xd>, <Xn>, <Xm>{, <shift> #<amount>}
BIC 指令支持寄存器方式: 先對 Xm 寄存器的值做移位操作, 然后再與 Xn 寄存器的值進行位清除操作。
指令參數說明如下。
shift 表示移位操作,支持 LSL 、LSR 、ASR 以及 ROR。
amount 表示移位數量,取值范圍為 0~63。
BIC 指令的本質是:
Xd = Xn & (~Xm)
- 將掩碼 Xm 按位取反。
- 將源寄存器(
Xn
)與取反后的掩碼進行按位與操作。
掩碼中為 1 的位會清除對應位,為 0 的位保持不變。
3.4、CLZ 指令
CLZ 指令的格式如下。
CLZ <Xd>, <Xn>
CLZ 指令計算為 1 的最高位前面有幾個為 0 的位。
如下代碼使用了 CLZ 指令。
ldr x1, =0x1100000034578000
clz x0, x1
X1 寄存器里為 1 的最高位是第 60 位,前面還有 3 個為 0 的位,最終 X0 寄存器的值為 3。
四、位段操作指令
4.1、位段插入操作指令
BFI 指令的格式如下。
BFI <Xd>, <Xn>, #<lsb>, #<width>
BFI 指令的作用是用 Xn 寄存器中的 Bit[0, width - 1]替換 Xd 寄存器中的 Bit[lsb, lsb + width - 1], 寄存器中的其他位不變。
BFI 指令常用于設置寄存器的字段。
如下代碼將寄存器 X1 的低 4 位插入到寄存器 X0 的第 8~11 位(共 4 位)。
BFI X0, X1, #8, #4 ; 將X1的低4位插入到X0的第8~11位
步驟解析:
- 假設初始值:
-
X0 = 0xFFFFFFFFFFFFFFFF
(全 1)。X1 = 0x000000000000000F
(低 4 位為 1,其余為 0)。
- 提取源位段:
-
X1
的低 4 位為0b1111
。
- 清除目標位段:
-
X0
的第 8~11 位被清零,變為0xFFFFFFF0FFFFFF
。
- 插入位段:
-
- 源位段
0b1111
左移 8 位后為0x0000000000000F00
。 - 合并后
X0 = 0xFFFFFFF0FFFFFF | 0x0000000000000F00 = 0xFFFFFFF0FFFFFFFF
。
- 源位段
4.2、位段提取操作指令
UBFX 指令的格式如下。
UBFX <Xd>, <Xn>, #<lsb>, #<width>
?
UBFX 指令的作用是提取 Xn 寄存器的 Bit[lsb, lsb + width - 1],然后存儲到 Xd 寄存器中。
UBFX 還有一個變種指令 SBFX,它們之間的區別在于:SBFX 會進行符號擴展,例如,如果 Bit[lsb + width - 1] 為 1,那么寫到 Xd 寄存器之后,所有的高位都必須寫 1,以實現符號擴展。
UBFX 和 SBFX 指令常常用于讀取寄存器的某些字段。
如下代碼從寄存器 X1 的第 8~15 位(共 8 位)提取值,并零擴展到 X0。
UBFX X0, X1, #8, #8 ; 提取X1的第8~15位,存入X0
步驟解析:
- 假設初始值:
-
X1 = 0x0000000000FF00FF
(二進制0b0000...0000111111110000000011111111
)。
- 提取位段:
-
- 第 8~15 位為
0b00000000
(值為 0)。
- 第 8~15 位為
- 零擴展:
-
- 提取的 8 位右移至最低位,左側補零,結果為
0x0000000000000000
。
- 提取的 8 位右移至最低位,左側補零,結果為
- 存入目標寄存器:
-
X0 = 0x0000000000000000
。