ARMv8指令集架构 |
您所在的位置:网站首页 › arm指令集系统 › ARMv8指令集架构 |
系列上篇:ARMv8寄存器组 系统寄存器数据处理或加载/存储指令不能直接使用系统寄存器。相反,需要将系统寄存器的内容读入寄存器X,对其进行操作,然后写回系统寄存器。有两个用于访问系统寄存器的专用指令: MRS Xd, 将系统寄存器读入Xd. MSR , Xn 写入Xn系统寄存器。 系统寄存器由名称指定,例如SCTLR_EL1:MRS X0, SCTLR_EL1 读SCTLR_EL1入X0. 系统寄存器名称以_ELx. 指定_ELx访问寄存器所需的最低权限。 数据处理汇编指令 算术和逻辑运算逻辑和整数算术指令的基本格式如下图所示。 指令的组成部分如下: operation定义了指令的功能。例如,ADD执行加法,AND执行逻辑与。可以在操作中添加S来设置标志。例如,ADD变成ADDS,这个S告诉处理器根据指令的结果更新ALU标志。Destination是指令的目的地,它总是一个寄存器,指定了操作结果应放在的位置。大多数指令只有一个目的寄存器。一些指令有两个目的寄存器。当目的寄存器是W寄存器时,对应的X寄存器的高32位被置为0。操作数(operand) 1始终是寄存器。这是指令的第一个输入。操作数(operand) 2是一个寄存器或常量,它是指令的第二个输入。当操作数2是一个寄存器时,它可以包含一个shift。当操作数2是一个常量时,它被编码在指令本身内。这意味着可用常数的范围是有限的。有一些特殊情况,例如MOV和MVN指令。MOV将一个常量或另一个寄存器的内容移动到目标寄存器中。MOV和MVN只需要一个输入操作数,可以是寄存器,也可以是常量,如下所示: MOV X0, #1 将通用寄存器X0的值设为1。 MVN W0, W1将通用寄存器W0的值设为~W1。 浮点操作遵循与整数数据处理指令相同的格式,并使用浮点寄存器。与整数数据处理指令一样,操作的大小决定了所使用的寄存器的大小。浮点指令的operation总是以F开头。例如下面这个指令设置半精度的H0 = H1 / H2: FDIV H0, H1, H2 下面这个指令设置单精度的S0 = S1 + S2: FADD S0, S1, S2 下面这个指令设置双精度的D0 = D1 - D2: FSUB D0, D1, D2 使用FMOV复制寄存器之间的文字位模式。还有一些指令可以转换为最接近的表示,如下图所示: 在此示例中,假设X0包含值 2(正整数 2):X0 = 0x0000_0000_0000_0002 然后,执行以下序列: FMOV D0, X0 SCVTF D1, X0 两条指令都“复制”X0到D寄存器中。然而,结果却截然不同: D0 = 0x0000_0000_0000_0002 = 9.88131e-324 D1 = 0x4000_0000_0000_0002 = 2.0 复制FMOV的文字位模式,当解释为浮点值时,这是一个非常不同的值。将SCVTF值转换X0为最接近的浮点等效值。 同样,FCVTxx可用于将浮点值转换为其最接近的整数表示形式(即转换成2)。 位运算有一组指令用于操作寄存器内的位。下图为示例:
Size表示允许处理数据长度,有X和W两种。X表示64位,W表示32位。 下面这条指令从地址中load 32位到W0: LDR W0, [ ] 下面这条指令从地址中load 64位到X0: LDR X0, [] Size字段允许load子寄存器大小的数据量。 下面这条指令将W0的底部字节(B 8位)存储到地址:STRB W0, [] 下面这条指令将W0的底部半字(H 16位)存储到地址:STRH W0, [] 最后这条指令将X0的底部字(W 32位)存储到地址: STRW X0, [] Sign当只加载部分寄存器值时,其余部分默认是零扩展。Sign表示按照加载值的最高位扩展 A64的load/store寻址模式有以下几种: 基址寄存器模式最简单的寻址形式是单个寄存器。基址寄存器是一个X寄存器,它包含被访问数据的完整的或绝对的虚拟地址,如下图所示,LDR指令把X1寄存器中的值作为内存地址,读出其中的数值到W0寄存器中。 如下图所示。X1包含基址,#12是基址的字节偏移量。访问的地址是X1 + 12。偏移量可以是一个常量,也可以是另一个寄存器。例如,这种类型的寻址可能用于结构体。只需维护一个指向结构体基地址的指针,便可通过不同的偏移量来选择不同的成员。 只能以立即数的形式提供offset 索引模式一共有两种变体:预索引模式(pre-index),该变体在访问内存前先进行移位操作(修改基寄存器的值);另一种变体是后索引模式(post-index),该变体在访问内存后才修改基寄存器的值。在指令语法中,在方括号后边添加感叹号来表示预索引模式, 如下图所示。预索引寻址类似于偏移寻址,不同之处在于base pointer会随着指令的执行而更新。图中,先把X1中的内存地址加上偏移量12,存入X1,然后存入W0。指令完成后X1的值为X1 + 12。 使用后索引寻址,从基指针中的地址加载值,然后更新指针。 如下图,LDR先把X1内存地址的值加载到W0,然后把内存地址偏移12,新的内存地址存入X1。后索引寻址对于出栈很有用。该指令从堆栈指针所指向的位置加载值,然后将堆栈指针移动到堆栈中的下一个完整位置。 除了单个寄存器的load和store,A64指令集还有load pair(LDP)和store pair(STP)指令。这些成对指令将两个寄存器从存储器中传入和传出。 第一条指令将[X0]加载到W3,并将[X0 + 4](按字长度增加)加载到W7:LDP W3, W7, [X0]。 load pair和store pair指令通常用于栈的压入和弹出。 第一条指令将X0和X1压入堆栈: STP X0, X1, [SP, #-16]! 第二条指令从堆栈中弹出X0和X1: LDP X0, X1, [SP], #16 记住,在AArch64中,堆栈指针SP必须是128位对齐的。 相对寻址相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。寻址方式基于当前PC,因此是地址无关操作 根据不同的编码格式,偏移量有三种: LDR/STR中的偏移量和其他指令中的立即数还不是完全一样 所有的ARM指令都是32 bits固定长度 一条ARM指令语法格式分为如下几个部分: {cond}{S},, 其中,内的项是必须的,{}内的项是可选的, opcode:指令助记符,如ADD、SUB、MOV等; cond:条件码助记符,如EQ(0000)、NE(0001)、AL(1110)**(无条件执行)**等; S:如果指令有S后缀,则该指令的操作会影响CPSR的值; Rd:目标寄存器; Rn:包含第一个源操作数的寄存器; shifter_operand:表示第二个源操作数,可以为寄存器或立即数。例如:1-- 立即数 add r1,r2,#10 ; 2-- 寄存器 addeqs r1,r2,r3 @ r1=r2+r3 ; 3-- 寄存器移位 add r1,r2,r3,LSL #2 @ r1 = r2 +r3*4) 立即数编码格式举例: 从上面可以看出,并不是所有整数都是立即数。 立即数有一些比较明显的判断特征: (1) 首先将数据转换成二进制形式。 (2) 如果1的个数大于8,则一定不是立即数。 (3) 如果最高和最低位的1之间有超过24个0,则一定不是立即数。 (4) 掐头去尾,分别去掉最高和最低位的1两侧最大偶数个0,如果剩下的位数仍然大于8,则一定不是立即数。 比如:mov r0, # 0x4FF @不是立即数报错Error: invalid constant (101) after fixup 第一步:0100 1111 1111 第二步:其中1的个数是9个,大于8个,判定不是立即数 所以,我们在ARM汇编中如何规避立即数这个问题呢? 其实可以使用ARM汇编LDR伪指令,例如直接把MOV指令变为, LDR R1,=0x12345678这样编译器就不会报错了。但这种方法也有弊端会增加开销和影响执行效率。同时ARM汇编中还有有效数的概念,比如 MOV R1,#0xFFFFFFFF 指令中 0xFFFFFFFF 不是立即数,但是是有效数,编译器最自动把原指令变换为 MVN R1,#0,也不会报错。有效数判定:原数是立即数或者原数反码是立即数 LDR / STR指令编码
同理,不做赘述
对于上面两条指令,offset并不满足ldr编码规则。第一种是负数,第二种不是8的倍数。 但实际都可以编译通过,因为在编译过程中其被替换成
STR(immediate)的指令编码方式与LDR(immediate)类似,各字段编码方式相同,此处不再赘述 Register 指令编码 LDR(register)指令编码
: 移位量,可选,当<extend>不是LSL时默认为#0, ② 对于32位形式,取值为0(默认值)或2(S=1) ②对于64位形式,取值为0(默认值)或3(S=1) 举例:根据手册,64位形式中,合法的移位值只有0和3。编译如下移位值不合法的指令,
|
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |