树莓派ARM汇编语言编程十讲(第4讲)

内容简介

树莓派单板机(Raspberry Pi Single Computer)是一种极了不起的产品,用户可以以非常低的成本获得一个Linux环境并带GPIO硬件扩展的迷你计算机系统。新一代树莓派4B还提供了良好的工业物联网和AIoT支持。树莓派单板机拥有完整的生态链,软硬件资源丰富,是嵌入式系统开发和智能硬件产品创新的很好选择。
作为嵌入式系统与嵌入式智能硬件开发基础中的基础,汇编语言是许多从事信息科学和工程领域的技术人员应该掌握的一项基本技能。目前,市场上针对树莓派单板机系统介绍C、Scratch、Python等编程语言与实践方面的资源很多,但鲜有系统针对树莓派单板机ARM汇编语言编程方面的介绍。这里以袁志勇主编的《嵌入式系统原理与应用技术》(北京航空航天大学出版社2019年1月第3版)一书中ARM汇编语言编程知识为基础,采用树莓派单板机及Linux操作系统验证平台,较系统地介绍树莓派ARM汇编语言编程技术与示例。由于准备仓促,不妥之处,还请各位不吝赐教。
第4讲:常用ARM数据处理与程序状态访问指令及树莓派GNU ARM汇编程序举例
第4讲目录
·ARM汇编指令条件执行
·常用ARM数据处理指令
·ARM程序状态访问指令
·树莓派GNU ARM汇编指令条件执行与读写程序状态寄存器程序举例

一、ARM汇编指令条件执行
ARM处理器根据指令的执行条件是否满足,决定当前指令是否执行。只有在CPSR中的条件码位满足指定的条件时,指令才会被执行。不符合条件的代码依然占用一个时钟周期(相当于执行一个NOP操作)。使用条件码可以实现高效的逻辑操作(节省跳转和条件语句),提高代码效率。所有的ARM指令都可以条件执行,而Thumb指令只有B(跳转)指令具有条件执行功能。如果指令不标明条件代码,将默认为无条件(AL)执行。
ARM汇编语言条件码的书写方法是条件码助记符的位置在指令助记符的后面(因此也称为条件后缀)。
ARM汇编指令条件码助记符、英文含义及标志位说明如下:
EQ: EQual (equals zero), Z (Z=1)
NE: Not Equal, !Z (Z=0)
CS or HS: Carry Set/unsigned Higher or Same, C (C=1)
CC or LO: Carry Clear/unsigned LOwer, !C (C=0)
MI: MInus/negative N set, N and !C (N=1 and C=0)
PL: PLus/positive or zero, !N (N=0)
VS: overflow/oVerflow Set, V (V=1)
VC: no overflow/oVerflow Clear, !V (V=0)
HI: unsigned Higher, C and !Z (C=1 and Z=0)
LS: unsigned Lower or Same, !C or Z (C=0 or Z=1)
GE: signed Greater than or Equal, N=V
LT: signed Less Than, N!=V
GT: signed Greater Than, !Z and N=V (Z=0 and N=V)
LE: signed Less than or Equal, Z or N!=V (Z=1 or N!=V)
AL: ALways (default), Typically use blank (i.e., use sub rather than subal)
例:
MOVEQ R0,R1 @ if Z=1 then R1→R0 else NOP operation
本例数据传送指令MOV后的条件码助记符EQ的意思是:若标志位Z=1,那么把R1寄存器的内容送到R0寄存器,否则什么也不做(仅执行一个周期的NOP空操作)。
例:
以下C语言条件语句:
if (a > b)
a++;
else
b++;
对应的ARM汇编指令:
CMP R0,R1 @ R0(a)与R1(b)比较
ADDHI R0,R0,#1 @若R0>R1,则R0=R0+1
ADDLS R1,R1,#1 @ R0≤1,则R1=R1+1
二、常用ARM数据处理指令
ARM数据处理指令主要完成寄存器中数据的算术和逻辑运算操作。ARM数据处理指令包括:1. 数据传送指令:MOV和MVN;2. 算术指令:ADD、ADC、SUB、SBC、RSB和RSC;3.逻辑指令:AND、ORR、EOR和BIC;4.比较指令:CMP、CMN、TST和TEQ;5.乘法指令:MLA、MUL 、SMLAL、SMULL、UMALA和UMULL。这里仅介绍前4种使用较频繁的ARM数据处理指令。
ARM数据处理指令的特点:
●操作数来源:所有的操作数要么来自寄存器,要么来自立即数,不会来自存储器。
●操作结果:如果有结果,则结果一定是32位宽、或64位宽(长乘法指令),并且放在一个或两个寄存器中,不会写入存储器。
●除乘法指令外,有第2个操作数Operand2:第2操作数有立即数、寄存器、寄存器移位三种形式。
●乘法指令的操作数:全部是寄存器。

  1. 数据传送指令
    (1)数据传送指令
    格式:MOV{cond}{S} Rd,Operand2
    功能:Rd←operand2
    (2)数据取反传送指令(move negative)
    格式:MVN{cond}{S} Rd,Operand2
    功能:Rd←(NOT operand2)
    S选项决定指令操作是否影响CPSR中的条件码标志,若无S时指令操作不更新CPSR中的条件标志码位。
    需说明的是,对于MOV和MVN指令,汇编器会对其进行智能转化。如指令“MOV R1, 0xffffff00”中的立即数是非法的。在汇编时,汇编器将其转化为“MVN R1, 0xff”,这样就不违背立即数的要求。因此对于MOV和MVN指令,可以认为:合法立即数的反码也是合法的立即数。
    例:
    mov r0, r1 @ r0 = r1, 不修改cpsr
    mov r0, #0x0 @ r0 = 0, 不修改cpsr
    movs r0, #0x0 @ r0 = 0, 同时修改cpsr的Z位
    movs r0, #-10 @ r0 = 0xfffffff6, 同时修改cpsr的N位
    mvn r0, r2 @ r0 = NOT r2, 不修改cpsr
    mvn r0, 0xffffffff @r0 = 0x0, 不修改cpsr
    mvns r0, 0xffffffff @ r0 = 0x0, 同时修改cpsr的Z位
    mov r0, r1, LSL #1 @ r0 = r1 << 1
    mov r0, r1, LSR r2 @r0 = r1 >> r2
    movs r3, r1, LSL #2 @ r3=r1<<2, 修改NZCV标志码位
  2. 算术运算指令
    (1)加法运算指令
    格式:ADD{cond}{S} Rd,Rn,Operand2
    功能:Rd←Rn+Operand2
    (2)减法运算指令
    格式:SUB{cond}{S} Rd,Rn,Operand2
    功能:Rd←Rn-Operand2
    (3)逆向减法指令(Reverse Subtract)
    格式:RSB{cond}{S} Rd,Rn,Operand2
    功能:Rd←Operand2-Rn
    (4)带进位加法指令
    格式:ADC{cond}{S} Rd,Rn,Operand2
    功能:Rd←Rn+Operand2+Carry
    (5)带进位减法指令
    格式:SBC{cond}{S} Rd,Rn,Operand2
    功能:Rd←Rn-Operand2-(NOT)Carry
    (6)带进位逆向减法指令(Reverse Subtract with Carry)
    格式:RSC{cond}{S} Rd,Rn,Operand2
    功能:Rd←Operand2-Rn-(NOT)Carry
    说明:
    ●若在这些指令后面加上后缀S,那么这些指令将根据其运算结果修改标志NZCV。
    ●若R15作为Rn使用,则使用的值是当前指令的地址加8。
    ●若R15作为Rd使用,则执行完指令后,程序将转移到结果对应的地址处;若此时指令还加有后缀S,则还会将当前模式的SPSR拷贝到CPSR。我们可以利用这一特性从异常返回。
    ●在有寄存器控制移位的任何数据处理指令中,不能将R15作为Rd或任何操作数来使用。
    例:
    ADD R3, R7, #1020 @ immed_8r为1020(0x3FC),是0xFF循环右移30位
    SUBS R8, R6, #240 @ R8←R6-240,运算完成后将根据结果更新标志
    RSB R4, R4, #1280 @ R4←1280-R4
    RSCLES R0, R5, R0, LSL R4 @ 有条件执行,执行完后更新标志
  3. 逻辑运算指令
    (1)逻辑“与”指令
    格式:AND{cond}{S} Rd,Rn,Operand2
    功能:Rd←Rn&Operand2
    (2)逻辑“或”指令
    格式:ORR{cond}{S} Rd,Rn,Operand2
    功能:Rd←Rn|Operand2
    (3)逻辑“异或”指令(Exclusive OR)
    格式:EOR{cond}{S} Rd,Rn,Operand2
    功能:Rd←Rn⊕Operand2
    (4)位清除指令( Bit Clear)
    格式:BIC{cond}{S} Rd,Rn,Operand2
    功能:Rd←Rn&(NOT Operand2)
    使用这些指令时需注意:若加有后缀S,这些指令执行完后将根据结果修改标志N和Z,在计算Operand2时会更新标志C,不影响标志V。
    例:
    ANDS R0, R0, #0x01 @ R0=R0&0x01,取出最低位数据
    AND R2, R1, R3 @ R2=R1&R3
    @ AND指令可用于提取寄存器中某些位的值。
    ORR R0, R0, #0x0F @ 将R0的低4位置1
    @ ORR指令可用于将寄存器中某些位的值设置成1。
    EOR R1, R1, #0x0F @ 将Rl的低4位取反
    EORS R0, R5, #0x01 @ 将R0←R5异或0x01,并影响标志位
    @ EOR指令可用于将寄存器中某些位的值取反
    @ 将某一位与0异或,该位值不变;与1异或,该位值被求反
    BIC R1, R1, #0x0F @ 将R1的低4位清0,其它位不变
    @ BIC指令可用于将寄存器中某些位的值设置成0
    @ 将某一位与1做BIC操作,该位值被设置成0;将某一位与0做BIC操作,该位值不变
  4. 比较指令
    (1)比较指令
    格式:CMP{cond} Rn,operand2
    功能:NZCV标志←Rn-operand2
    (2)负数比较指令(Compare Negative)
    格式:CMN{cond} Rn,operand2
    功能:NZCV标志←Rn+operand2
    例:
    CMP R2, R9 @ NZCV←R2-R9
    CMN R0, #6400 @ NZCV←R0+#6400
    CMPGT R13, R7, LSL #2 @ 带符号大于
    (3)位测试指令
    格式:TST{cond} Rn&operand2
    功能:NZCV标志←Rn&operand2
    这里,&表示“按位与”运算。
    (4)相等测试指令
    格式:TEQ{cond} Rn,operand2
    功能:NZCV标志←Rn⊕operand2
    这里,⊕表示“按位异或”运算。
    TST指令通常与EQ、NE条件码配合使用。当所有测试位均为0时,EQ有效;而只要有一个测试位不为0,则NE有效。
    TEQ指令与EORS指令的区别在于TEQ指令不保存运算结果。使用TEQ进行相等测试时,常与EQ、NE条件码配合使用。当两个数据相等时,EQ有效;否则NE有效。
    例:
    TST R0, #0x01 @ 判断R0的最低位是否为0
    TST Rl, #0x0F @ 判断R1的低4位是否为0
    TEQ R0, R1 @ 比较R0与R1是否相等, 影响V、C位
    TSTNE R1, R5, ASR R1
    TEQEQ R10, R9
    三、程序状态访问指令
    当需要修改cpsr/spsr的内容时,首先要读取它的值到一个通用寄存器,然后修改某些位,最后将数据写回到状态寄存器(即:修改状态寄存器一般是通过“读取-修改-写回”三个步骤的操作来实现的)。cpsr/spsr不是通用寄存器,不能使用mov指令来读写。在ARM处理器中,只能通过读状态寄存器指令mrs读取cpsr/spsr;只能通过写状态寄存器指令msr写cpsr/spsr。
    1.读状态寄存器指令 (Move PSR status/flags to register)
    格式:MRS{cond} Rd, psr
    功能:Rd←psr。
    本指令将状态寄存器psr的内容传送到目标寄存器中。其中: Rd 为目标寄存器,Rd不允许为R15。psr为CPSR或SPSR。
    例:
    MRS R1, CPSR @ R1←CPSR
    MRS R2, SPSR @ R2←SPSR
    2.写状态寄存器指令 (Move register to PSR status/flags)
    格式:MSR{cond} psr_fields, Rm/#immed
    功能:psr_fields←Rm/#immed。
    其中, psr为CPSR或SPSR;immed为要传送到状态寄存器指定域的8位立即数;Rm为要传送到状态寄存器指定域的数据的源寄存器;fields为指定的传送区域,fields可以是c、x、s、f中的一种或多种且字母必须为小写; c代表控制域(即psr[7:0]),x代表扩展域(即psr[15:8]),s代表状态域(即psr[23:16]),f代表条件码标志域(即psr[31:24])。
    修改状态寄存器一般是通过“读取-修改-写回”三个步骤的操作来实现的。下面是 CPSR的读—修改—写操作的几个示例。
    例:设置进位位C
    MRS R0, CPSR @ R0← CPSR
    ORR R0,R0,#0x20000000 @ 置1进位位C
    MSR CPSR_f, R0 @ CPSR_f← R0[31:24]
    例:从管理模式切换到IRQ模式
    MRS R0, CPSR @ R0← CPSR
    BIC R0,R0,#0x1f @ 低5位清零
    ORR R0,R0,#0x12 @ 设置为IRQ模式
    MSR CPSR_c, R0 @ 传送回CPSR
    几点说明:
    ●关于控制域的修改问题:只有在特权模式下才能修改状态寄存器的控制域[7:0],以实现处理器模式转换,或设置开/关异常中断。
    ●关于T控制位的修改问题:程序中不能通过MSR指令,直接修改CPSR中的T控制位来实现ARM状态/Thumb状态的切换,必须使用BX指令完成处理器状态的切换。
    ●关于用户模式下能够修改的位:在用户模式只能修改“标志码域”,不能对CPSR[23:0]做修改。
    ●关于S后缀的使用问题:在MRS/MSR指令中不可以使用S后缀。
    四、树莓派GNU ARM汇编指令条件执行及读写程序状态寄存器程序举例
    首先,在树莓派Linux终端命令提示符使用nano等编辑器编辑名为pi_asmexp.s的ARM汇编程序,用cat命令显示的ARM汇编指令条件执行及读写程序状态寄存器源程序见图1所示。
    在这里插入图片描述
    图1 树莓派GNU ARM汇编指令条件执行及读写程序状态寄存器程序示例
    用as汇编pi_asmexp.s源程序、ld链接pi_asmexp.o目标代码,执行GDB pi_asmexp命令进入GDB命令调试状态(见图1)。
    输入两次LIST命令显示带行号的所有ARM汇编源程序;输入BREAK 9命令设置断点为第9行的ARM汇编指令addmi R5, R4;输入RUN命令运行到断点处暂停;用I R命令观察寄存器(见图2)。
    在这里插入图片描述
    图2 显示带行号的ARM汇编源程序、设置断点、执行到断点处暂停、观察寄存器

在图2中,当执行RUN命令到第9行断点处暂停时,第3~8行的ARM汇编指令执行情况说明如下:
3 _start: mov R5,#4
说明:本指令执行后,R5=4, 不改变条件码标志位NZCV。
4 mov R4,#2
说明:本指令执行后,R4=2, 不改变条件码标志位NZCV。
5 subs R5,R4
说明:本指令执行后,R5=R5-R4=2, sub指令跟后缀s表示改变条件码标志位 NZCV=0010。这里要特别注意的是,在subs指令中,若减法运算发生借位,CPSR的C标志位清零;若减法运算没有发生借位,则C标志位置1。就是说,ARM的subs指令的借位情况与adds指令的进位情况正好相反,这一点与Intel X86 CPU的汇编指令是不同的。
6 moveq R4,#10
说明: mov指令后跟条件码eq (Z=1时执行本指令);因标志位Z=0,故仅执行一个NOP空操作, 且不改变NZCV=0010。
7 subgts R5,R4
说明:sub指令后跟条件码gt (Z=0且C=V时执行本指令);故R5=R5-R4=0, 且改变 NZCV=0110。
8 subeqs R5,R4
说明:sub指令后跟条件码eq (Z=1时执行本指令);因标志位Z=1,故R5=R5-R4=0xfffffffe (即,R5中存储的是-2的补码),且改变NZCV=1000;当前程序状态寄存器CPSR=0x80000010,CPSR[4:0]=M[4:0]=10000,说明本例ARM汇编程序工作于用户模式。
输入STEP单步命令执行第9行的条件执行addmi加法指令:
addmi R5,R4
add指令后跟条件码mi (N=1且C=0时执行本指令),故R5=R5+R4=0, 且不改变NZCV=1000,此时CPSR值还是0x80000010 (见图3)。
再次输入STEP单步命令执行第10行的条件执行addne加法指令:
addne R5,R4
add指令后跟条件码ne (Z=0时执行本指令),故R5=R5+R4=2, 且不改变NZCV=1000,此时CPSR值仍然是0x80000010(见图3)。
在这里插入图片描述
图3 单步执行addmi 和addne条件执行指令
接着单步执行第11行mrs R0,CPSR指令读取CPSR状态至R0寄存器,此时,R0=0x80000010;单步执行第12行ldr R1,=#0xf0000000指令后R1=0xf0000000,执行第13行的msr CPSR_f,R1指令将R1寄存器的bit31~bit24写至状态寄存器的CPSR[31:24]高8位,此时CPSR[31:28]=NZCV=1111,即CPSR值更新为0xf000010 (见图4)。
在这里插入图片描述
图4 单步执行mrs和msr指令读写程序状态寄存器CPSR
End of This Lecture.
(作者Email联系:yuanzywhu@163.com)
发布时间:2020年3月29日
上一讲链接
下一讲链接

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

袁易学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值