共计 3765 个字符,预计需要花费 10 分钟才能阅读完成。
- 加法指令 ADD、ADDS、ADCS
- 减法指令 SUB、SUBS、SBC,SBCS,CMP
- 位操作 AND, ANDS, ORR、EOR、BFI、UBFX、SBFX
1. 加法指令
加法指令有 ADD、ADDS、ADCS。ADD 一般性加法指令,ADCS 带 C 标记位运算的加法指令,ADDS 影响 C 标记位的加法运算。
1.1 ADD
a = a + b, 没有进位标记,也不会利用进位标记
-
ADD (extended register) :
- Define:
ADD <Xd|SP>, <Xn|SP>, <Wm>, {<extend> {#<amount>}}
- Example1:
add x0, x1, x2
(x0 = x1 + x2) - Example2:
add x0, x1, x2, lsl #5
(x0 = x1 + (x2 << 5) )
- Define:
-
ADD (immediate):
- Define:
ADD <Xd|SP>, <Xn|SP>, #<imm>{, lsl <#shift>}
, note shift supports #0 and #12 only. - Example1:
add x1, x2, #8
(x1 = x2 + 8) - Example2:
add x1, x2, #8, lsl #12
(x1 = x2 + (8 << 12) )
- Define:
-
ADD (shifted register):
- Define:
ADD <Xd>, <Xn>, <Xm>{, <shift> #<amount>}
, note #amount range 0 to 63 - Note: LSL when shift = 0, LSR when shift = 1, ASR when shift = 2
- Example1:
add x1, x2, x3, asr #2
- Define:
1.2 ADDS
(a,C) = a + b, 带进位标记的加法,用法和 ADD 一样
1.3 ADCS
(a,C) = a + b + C,带进位标记的加法,且须要加上 C 标记位,用法和 ADD 一样。留神,如果加法溢出的时候 C 标记位会置位为 1,比方,a = 0xFFFFFFFFFFFFFFFF, b = 1,此时,加法溢出,C 置位 1。
1.4 ADR
a = b + PC, 以后程序的 PC 值加上给定的地址偏移
-
ADR
- Define:
ADR <Xd>, <label>
- Note, no 32-bit
- Note, <label> range ±1MB, offset from the address of this instruction.
- Example01:
adr x1, #25
- Define:
1.5 对于查看 C flag 的办法
办法 1:应用 MSR/MRS 指令
msr NZCV, xzr // clear the NZCV
mrs x0, NZCV // 查看 NZCV 寄存器,NZCV 在高位 28 - 32 bits
办法 2:应用 ADCS 的 + C 个性
adcs x0, zxr, xzr
让两个 0 寄存器相加 0+0+ c 就能够失去 C 标记位的值
2. 减法指令
减法指令蕴含 SBC,SBCS。请参考 ARMv8 手册,C6.2.231 C6-1299
2.1 SUB
a = a – b, 没有进位标记,也不会利用进位标记。应用办法和 ADD 统一。
2.2 SUBS
(a,N) = a – b, 会置标记位 N。应用办法和 SUBS 统一,减成正数的时候,其余地位补 1。
2.3 SBC
a = a – b – 1 + C
-
SBC (Subtract with Carry):
- Define:
SBC <Xd>, <Xn>, <Xm>
- Example:
sbc x0, xzr, xzr
- Define:
2.4 SBCS
(a, N) = a – b – 1 + C,如果减出正数的话,N 会被置位
-
SBC (Subtract with Carry, setting N flag):
- Define:
SBCS <Xd>, <Xn>, <Xm>
- Example:
sbcs x0, xzr, x1
- Define:
2.5 CMP
比拟指令,实际上也应用 SBC 实现的, cmp x1, x2
- 若 x1 > x2, NCZV = 0100
- 若 x1 = x2, NCZV = 0110
- 若 x1 < x2, NCZV = 1000
Define 1: CMP <Xn|SP>, <R><m>{, <extend> {#<amount>}}
Define 2: CMP <Xn|SP>, #<imm>{, <shift>}
Define 3: CMP <Xn|SP>, <Xm>{, <shift> #<amount>}
Example:
* The function cmp_and_return_test:
* if a >= b return 1
* if a < b return 0
test_cmp:
cmp x0, x1 // if x0 >= x1, C is 1; if x0 < x1 C is 0
adcs x0, xzr, xzr // 0 + 0 + C
3. 位操作
位操作蕴含 AND, ANDS, ORR、EOR、BFI、UBFX、SBFX,别离是与、与置位标记位、或、异或、插入、无符号提取、有符号提取。
3.1 ORR
a = a | b;
Define 1: ORR <Xd|SP>, <Xn>, #<imm>
Define 2: ORR <Xd|SP>, <Xn>, <Xm>{, <shift> #<amount>}
test_orr:
// ORR test 0xAA oor 0x55 = 0xFF
// 0xFF oor 0x00 = 0xFF
// 0xFF oor 0xFF = 0xFF
// 0x00 oor 0x00 = 0x00
mov x0, xzr
mov x1, #0xAA
mov x2, #0x55
orr x1, x1, x2
mov x1, #0xFF
orr x1, x1, xzr
mov x1, #0xFF
orr x1, x1, x1
orr x1, xzr, xzr
ret
3.2 EOR
a = a ^ b;
Define 1: EOR <Xd|SP>, <Xn>, #<imm>
Define 2: EOR <Xd|SP>, <Xn>, <Xm>{, <shift> #<amount>}
test_eor:
// test 2 exchange the value x1 = 0x07, x2 = 0xAA
// using the orr, just use two register.
// x1 = x1^x2
// x2 = x2^x1
// x1 = x1^x2
ldr x1, =0x07
ldr x2, =0xAA
eor x1, x1, x2
eor x2, x2, x1
eor x1, x1, x2
ret
几个 EOR 的小技巧:
- 翻转某些位: 比方把右数第 0 位到第 3 位翻转:1010 1001 ^ 0000 1111 = 1010 0110
- 替换数值:a=a^b; b=b^a; a=a^b,不借助第三个变量
- 置 0:a^a
- 判断相等 a^b == 0
3.3 AND
3.3.1 AND
a = a & b;
Define 1: AND <Xd|SP>, <Xn>, #<imm>
Define 2: AND <Xd|SP>, <Xn>, <Xm>{, <shift> #<amount>}
msr NZCV, xzr // clear the NZCV
ldr x1, =0xAA
ldr x2, =0x0
// test AND, no Z flag. x1 = x1&x2
and x1, x1, x2
mrs x0, NZCV
3.3.2 ANDS
(a, z) = a & b. 如果 a 和 b 与的后果为 0,z flag 置位
// test ANDS, z flag, if the result is 0, Z is 1
msr NZCV, xzr // clear the NZCV
mov x0, xzr
ldr x1, =0xAA
ands x1, x1, x2
mrs x0, NZCV
3.4 BFI
Define 1: BFI <Xd>, <Xn>, #<lsb>, #<width>
从 Xn 寄存器外面从低位开始,插入到 Xd 寄存器从 #<lsb> 开始,#<width> 长度。
读取 Xn 寄存器的低位开始计算,插入到 Xd 寄存器从 #<lsb> 开始,#<width> 长度。这个没有方法管制 Xd 的地位,只能从 Xd 的最低位开始。
test_bfi:
// 0000 0000 0000 1010
// |
// V
// 0000 0000 0000 0000 -> 0000 1010 0000 0000
ldr x1, =0x000A
mov x2, xzr
bfi x2, x1, #0x8, #0x4
// 0000 0000 0000 1010
// |
// V
// 0000 0101 0000 0000 -> 0000 1010 0000 0000
ldr x1, =0x000A
mov x2, #0x0500
bfi x2, x1, #0x8, #0x4
ret
3.5 UBFX/SBFX
Define: UBFX <Xd>, <Xn>, #<lsb>, #<width>
Define: SBFX <Xd>, <Xn>, #<lsb>, #<width>
读取 Xn 寄存器的 #<lsb> 开始,#<width> 长度开始计算,替换到 Xd 寄存器低位的地位#<width> 长度。这个没有方法管制 Xd 的地位,只能从 Xd 的最低位开始。SBFX 是有符号的,替换之后 Xd 其余位 0 变为 1。
// x1: 0000 0000 1111 0000
// |
// V
// x2: 0000 0000 0000 0000 -> 0000 0000 0000 1111
mov x1, #0x00F0
mov x2, xzr
ubfx x2, x1, #0x4, #0x4
// 1000 0000 1111 0000
// |
// V
// x1: 0000 0000 0000 0000 -> 1111 1111 1111 1111
mov x1, #0x80F0
mov x2, xzr
sbfx x2, x1, #0x4, #0x4
Ref
[1] Arm Armv8-A Architecture Registers-NZCV, Condition Flags
[2] ARM Cortex-A Series Programmer’s Guide for ARMv8-A – Arithmetic and logical operations
[3] ARM 架构(三)ARMv8 Programm Model Overview
[4] ARMv8 官网手册学习笔记 (三):寄存器