本章思考题
1.请简述N、Z、C、V这4个条件标志位的作用。
2.下面两条ADD指令能否编译成功?
addx0,x1,1,LSL1
3.下面的示例代码中,X0寄存器的值是多少?
movx1,0xffffffffffffffffmovx2,3movx2,0x8aubfxx0,x2,4sbfxx1,x2,4
11.下面是用C语言来读取pmcr_el0寄存器Bit[15:11]的值,请使用汇编代码来实现。
val=read_sysreg(pmcr_el0)val=val11;val=0x1f;
本章主要介绍A64指令集中的算术运算和移位指令。
4.1条件操作码A64指令集沿用了A32指令集中的条件操作,在PSTATE寄存器中有4个条件标志位,即N、Z、C、V,如表4.1所示。
表4.1条件标志位
条件标志位
描述
N
负数标志(上一次运算结果为负值)
Z
零结果标志(上一次运算结果为零)
C
进位标志(上一次运算结果发生了无符号数溢出)
V
溢出标志(上一次运算结果发生了有符号数溢出)
常见的条件操作后缀如表4.2所示。
表4.2常见的条件操作后缀
后缀
含义(整数运算)
条件标志位
条件码
EQ
相等
Z=1
0b0000
NE
不相等
Z=0
0b0001
CS/HS
发生了无符号数溢出
C=1
0b0010
CC/LO
没有发生无符号数溢出
C=0
0b0011
MI
负数
N=1
0b0100
PL
正数或零
N=0
0b0101
VS
溢出
V=1
0b0110
VC
未溢出
V=0
0b0111
HI
无符号数大于
(C=1)(Z=0)
0b1000
LS
无符号数小于或等于
(C=0)||(Z=1)
0b1001
GE
有符号数大于或等于
N==V
0b1010
LT
有符号数小于
N!=V
0b1011
GT
有符号数大于
(Z==0)(N==V)
0b1100
LE
有符号数小于或等于
(Z==1)||(N!=V)
0b1101
AL
无条件执行
—
0b1110
NV
无条件执行
—
0b1111
4.2加法与减法指令下面介绍常见的与加法和减法相关的指令。
4.2.1ADD指令普通的加法指令有下面几种用法。
使用立即数的加法。
使用寄存器的加法。
使用移位操作的加法。
1.使用立即数的加法指令使用立即数的加法指令格式如下。
ADDXd|SP,Xn|SP,0”。当sh字段为1时,表示“LSL1//把x1寄存器的值加上立即数1,结果写入x0寄存器中addx0,x1,4096addx0,x1,1,LSL1'2.使用寄存器的加法指令
使用寄存器的加法指令格式如下。
ADDXd|SP,Xn|SP,Rm{,ext{12movx2,amount}这条指令的作用是先把Xm寄存器做一些移位操作,然后再加上Xn寄存器的值,结果写入Xd寄存器中。
指令编码如图4.3所示。

▲图4.3使用移位操作的加法指令的编码
Xd:目标寄存器,它对应指令编码中的Rd字段。
Xn:第一个源操作数,它对应指令编码中的Rn字段。
Xm:第二个源操作数,它对应指令编码中的Rm字段。
shift:移位操作,它对应指令编码中的shift字段。当shift=00时,表示LSL操作。当shift=01时,表示LSR操作。当shift=10时,表示ASR操作。
amount:移位的数量,取值范围是0~63,它对应指令编码中的imm6字段。
【例4-5】如下代码用于实现移位操作加法。
addx0,x1,x2,LSL3//x0=x1+x23
【例4-6】下面的代码是错误的。
addx0,x1,x2,LSL64
amount参数已经超过了取值范围,汇编器会报错,报错的信息如下。
:Assemblermessages::Error:shiftamountoutofrange0to63atoperand3–'addx0,x1,x1,LSL64'4.2.2ADDS指令
ADDS指令是ADD指令的变种,唯一的区别是指令执行结果会影响PSTATE寄存器的N、Z、C、V标志位,例如当计算结果发生无符号数溢出时,C=1。
【例4-7】下面的代码使用了ADDS指令。
movx1,0xffffffffffffffffaddsx0,x1,2adcx0,x1,x2mrsx3,nzcv
ADC指令的计算过程是0xFFFFFFFFFFFFFFFF+2+C,因为0xFFFFFFFFFFFFFFFF+2的过程中已经触发了无符号数溢出,C=1,所以最终计算X0寄存器的值为2。若读取NZCV寄存器,我们发现C标志位也被置位了。
4.2.4SUB指令普通的减法指令与加法指令类似,也有下面几种用法。
使用立即数的减法。
使用寄存器的减法。
使用移位操作的减法。
1.使用立即数的减法指令使用立即数的减法指令格式如下。
SUBXd|SP,Xn|SP,0”。当sh字段为1时,表示“LSL1//把x1寄存器的值减去立即数1,结果写入x0寄存器subx0,x1,4097subx0,x1,1,LSL1'2.使用寄存器的减法指令
使用寄存器的减法指令格式如下。
SUBXd|SP,Xn|SP,Rm{,ext{12movx2,amount}这条指令的作用是先把Xm寄存器做一些移位操作,然后使Xn寄存器中的值减去Xm寄存器中的值,把结果写入Xd寄存器中。
指令编码如图4.8所示。

▲图4.8使用移位操作的减法指令的编码
Xd:目标寄存器,它对应指令编码中的Rd字段。
Xn:第一个源操作数,它对应指令编码中的Rn字段。
Xm:第二个源操作数,它对应指令编码中的Rm字段。
shift:移位操作,它对应指令编码中的shift字段。当shift=00时,表示LSL操作。当shift=01时,表示LSR操作。当shift=10时,表示ASR操作。
amount:移位的数量,取值范围是0~63,它对应指令编码中的imm6字段。
【例4-13】下面的代码用于实现移位操作减法。
addx0,x1,x2,LSL3//x0=x1-x23
【例4-14】下面的代码是错误的。
subx0,x1,x2,LSL64
上述示例代码中的amount参数超过了取值范围,汇编器会报错,报错的信息如下。
:Assemblermessages::Error:shiftamountoutofrange0to63atoperand3--'subx0,x1,x1,LSL64'4.2.5SUBS指令
SUBS指令是SUB指令的变种,唯一的区别是指令执行结果会影响PSTATE寄存器的N、Z、C、V标志位。SUBS指令判断是否影响N、Z、C、V标志位的方法比较特别,对应的伪代码如下。
operand2=NOT(imm);(result,nzcv)=AddWithCarry(operand1,operand2,'1');,Z,C,V=nzcv;
首先,把第二个操作数做取反操作。然后,根据式(4-1)计算。
operand1+NOT(operand2)+1(4-1)
NOT(operand2)表示把operand2按位取反。在这个计算过程中要考虑是否影响N、Z、C、V标志位。当计算结果发生无符号数溢出时,C=1;当计算结果为负数时,N=1。
【例4-15】如下代码会导致C标志位为1。
movx1,0x3movx2,0x1subsx0,x1,x2mrsx3,nzcv
SUBS指令仅仅执行“3−1”的操作,为什么会发生无符号溢出呢?
第二个操作数为X2寄存器的值,对应值为1,按位取反之后为0xFFFFFFFFFFFFFFFE。根据计算公式,计算3+0xFFFFFFFFFFFFFFFE+1,这个过程会发生无符号数溢出,因此4个标志位中的C=1,最终计算结果为2。因此,最后一行读取NZCV寄存器的值——0x20000000。
【例4-16】如下代码会导致C和Z标志位都置1。
movx1,0x3movx2,0x3subsx0,x1,x2mrsx3,nzcv
第二个操作数为X2寄存器的值,该值为3,按位取反之后为0xFFFFFFFFFFFFFFFC。根据公式计算3+0xFFFFFFFFFFFFFFFC+1的过程中会发生无符号数溢出,因此C=1。另外,最终结果为0,所以Z=1。
4.2.6SBC指令SBC是进位的减法指令,也就是最终的计算结果需要考虑PSTATE寄存器的C标志位。SBC指令的格式如下。
SBCXd,Xn,Xm
下面是SBC指令中对应的伪代码。
operand2=NOT(operand2);(result,-)=AddWithCarry(operand1,operand2,);X[d]=result;
所以,SBC指令的计算过程是,首先对第二个操作数做取反操作,然后把第一个操作数、第二个操作数相加,这个过程会影响PSTATE寄存器的C标志位,最后把C标志位加上。
综上所述,SBC指令的计算公式为
Xd=Xn+NOT(Xm)+C(4-2)
指令编码如图4.9所示。

▲图4.9SBC指令的编码
Xd:目标寄存器,它对应指令编码中的Rd字段。
Xn:第一个源操作数,它对应指令编码中的Rn字段。
Xm:第二个源操作数,它对应指令编码中的Rm字段。
【例4-17】如下代码使用了SBC指令。
movx1,1sbcx0,x1,x2mrsx3,nzcv
SBC指令的计算过程是3+NOT(1)+C。NOT(1)表示对立即数1按位取反,结果为0xFFFFFFFFFFFFFFFE。那么,计算3+0xFFFFFFFFFFFFFFFE的过程中会发生无符号数溢出,C=1,再加上C标志位,最后计算结果为2。
4.3CMP指令CMP指令用来比较两个数的大小。在A64指令集的实现中,CMP指令内部调用SUBS指令来实现。
1.使用立即数的CMP指令使用立即数的CMP指令的格式如下。
CMPXn|SP,imm{,shift}2.使用寄存器的CMP指令使用寄存器的CMP指令的格式如下。
CMPXn|SP,Rm{,ext{amount}}3.使用移位操作的CMP指令使用移位操作的CMP指令的格式如下。
CMPXn,Xm{,shiftamount}4.CMP指令与条件操作后缀CMP指令常常和跳转指令与条件操作后缀搭配使用,例如条件操作后缀CS表示是否发生了无符号数溢出,即C标志位是否置位,CC表示C标志位没有置位。
【例4-18】使用CMP指令来比较如下两个寄存器。
cmpx1,
CMP指令判断两个寄存器是否触发无符号溢出的计算公式与SUBS指令类似:
X1+NOT(X2)+1(4-3)
如果上述过程中发生了无符号数溢出,那么C标志位会置1,则指令将会跳转到label处。
【例4-19】下面的代码用来比较3和2两个立即数。
my_test:movx1,21:cmpx1,
至于如何比较,需要根据b指令后面的条件操作后缀来定。CS表示判断是否发生无符号数溢出。根据式(4-3)可得,3+NOT(2)+1,其中NOT(2)把立即数2按位取反,取反后为0xFFFFFFFFFFFFFFFD。3+0xFFFFFFFFFFFFFFFD+1的最终结果为1,这个过程中发生了无符号数溢出,C标志位为1。所以,的判断条件成立,跳转到标签1处,继续执行。
【例4-20】下面的代码比较X1和X2寄存器的值大小。
my_test:movx1,21:cmpx1,
在比较X1和X2寄存器的值大小时,判断条件为LS,表示无符号数小于或者等于。那么,这个比较过程中,我们就不需要判断C标志位了,直接判断X1寄存器的值是否小于或者等于X2寄存器的值即可,因此这里b指令不会跳转到标签1处。
本文截选自《ARM64体系结构编程与实践》第4章:A64指令集2——算术与移位指令

本书旨在详细介绍ARM64体系结构的相关技术。本书首先介绍了ARM64体系结构的基础知识、搭建树莓派实验环境的方法,然后讲述了ARM64指令集中的加载与存储指令、算术与移位指令、比较与跳转等指令以及ARM64指令集中的陷阱,接着讨论了GNU汇编器、链接器、链接脚本、GCC内嵌汇编代码、异常处理、中断处理、GIC-V2,最后剖析了内存管理、高速缓存、缓存一致性、TLB管理、内存屏障指令、原子操作、操作系统等内容。
本书适合嵌入式开发人员阅读。