历史上的今天
返回首页

历史上的今天

今天是:2025年03月25日(星期二)

正在发生

2021年03月25日 | ARM汇编语言入门(六)

2021-03-25 来源:eefocus

Part 6:条件状态和分支

在探讨CPSR时我们已经接触了条件状态。我们通过跳转(分支)或者一些只有满足特定条件才执行的指令来控制程序在运行时的执行流。通过CPSR寄存器中的特定bit位来表示条件状态。这些位根据指令每次执行的结果而不断变化。例如,比较运算时如果两个数相等,那么就置CPSR中的Zero位(Z=1),实际上是因为:a - b = 0,这种情况下就是相等状态。如果第一个数大,那么就是大于状态。如果第二个数大,就是小于状态。除此之外,还有小于等于、大于等于等等。


下面的表格列出了可用的条件状态码,描述和标志位:

在下面代码片段中看一下执行条件加法时的实际用法L:


.global main

main:

        mov     r0, #2     /* 初始化变量 */

        cmp     r0, #3     /* 将R0中的值与3比较,负数位置1 */

        addlt   r0, r0, #1 /* 如果上一条比较结果是小于(查看CPSR),则将R0加1 */

        cmp     r0, #3     /* 将R0中的值再与3比较, 零位置1,同时负数位重置为0 */

        addlt   r0, r0, #1 /* 如果上一条比较结果是小于(查看CPSR),则将R0加1 */

        bx      lr

第一条cmp指令结果导致CPSR中的负数位置1(2- 3 = -1)意思是R0小于R3。因为满足小于条件(CPSR中的溢出位不等于负数位V != N)所以接下来的ADDLT指令执行。在执行下一条cmp指令时,R0 = 3。所以清除负数位(3 - 3 = 0,负数位清零),零位置位(Z = 1)。现在溢出位是0,负数位是0,不满足小于条件。所以最后一条ADDLT指令不执行,R0值保持3不变。


Thumb模式下的条件执行

我们在介绍指令集的章节讨论了Thumb状态下的不同。具体而言是Thumb-2版本支持条件执行。某些 ARM 处理器版本支持"IT"指令,允许在 Thumb 状态下支持多达4个条件执行指令。参考:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/BABIJDIC.html。


语法:IT{x{y{z}}} cond


cond 指定 IT 块的第一个指令的条件。

x 指定 IT 块中第二个指令的条件开关。

y 指定 IT 块中第三个指令的条件开关。

z 指定 IT 块中第四个指令的条件开关。

其实IT指令的结构就是“IF-Then-(Else)”,语法都是由字母“T”和“E”构成:


IT:If-Then(下一条指令是条件的);

ITT:If-Then-Then(后两条指令是条件的);

ITE:If-Then-Else(后两条指令是条件的);

ITTE:If-Then-Then-Else(后三条指令是条件的);

ITTEE:If-Then-Then-Else-Else(后四条指令是条件的);

IT块中的每条指令必须指定相同或逻辑相反的条件后缀。意思是,如果使用ITE,那么前两个指令必须有相同的后缀,而第三个必须是逻辑相反的后缀。下面是 ARM 参考手册中的一些示例,说明了这些逻辑:


ITTE   NE           ; 接下来的3条指令都是有条件的。

ANDNE  R0, R0, R1   ; ANDNE不更新条件标志。

ADDSNE R2, R2, #1   ; ADDSNE更新条件标志。

MOVEQ  R2, R3       ; 有条件的移动

ITE    GT           ; 接下来的2条指令都是有条件的。

ADDGT  R1, R0, #55  ; 条件满足大于时进行相加。

ADDLE  R1, R0, #48  ; 条件不满足大于时进行相加。

ITTEE  EQ           ; 接下来的4条指令都是有条件的。

MOVEQ  R0, R1       ; 有条件的MOV

ADDEQ  R2, R2, #10  ; 有条件的ADD

ANDNE  R3, R3, #1   ; 有条件的AND

BNE.W  dloop        ; 分支指令只能在IT块的最后一个指令中使用。

错误示例:


IT     NE           ; 下一条指令是条件的。     

ADD    R0, R0, R1   ; 语法错误,不是有条件的指令。

下面是条件代码和相反代码:

现在使用以下代码来测试:


.syntax unified    @ 非常重要!

.text

.global _start

_start:

    .code 32

    add r3, pc, #1   @ PC的值加1并存储到R3。

    bx r3            @ 跳转到R3中的地址处,并切换运行模式 ->切换到Thumb模式,因为R3最低有效位(LSB) = 1。

    .code 16         @ Thumb模式

    cmp r0, #10      

    ite eq           @ 如果R0等于10...

    addeq r1, #2     @ ... 那么 R1 = R1 + 2

    addne r1, #3     @ ... 否则 R1 = R1 + 3

    bkpt

.code 32


示例中的代码开始在ARM模式下,第一条指令将PC中的地址值加1并存储到R3,然后bx指令跳转到R3中的地址位置,并且模式切换成Thumb模式,因为R3中的值最低有效位为1(0不切换)。为此使用bx(分支+交换)非常重要。


.code 16


在Thumb模式下,首先比较R0和10,结果将负数位N置位(0 - 10 = -10)。之后使用If-Then-Else块,因为零位Z(Zero)没有被置位所以ADDEQ指令被跳过,然后因为结果不相等所以执行ADDNE指令。


在 GDB 中单步执行此代码会干扰结果,因为你要在 ITE 块中执行这两个指令。 但是,在 GDB 中运行代码而不设置断点并单步执行每个指令将生成正确的结果设置 R1 = 3。


分支

分支(跳转)允许我们跳转到另一个代码段。当你需要跳过(或者重复)某块代码或者跳转到指定的函数的时候,分支很有用。此类情形中最佳的示例是IF和循环。先来看看IF案例。


.global main

main:

        mov     r1, #2     /* 设置初始变量a */

        mov     r2, #3     /* 设置初始变量b */

        cmp     r1, r2     /* 比较两个变量值看哪个更大 */

        blt     r1_lower   /* 因为R2更大(N==1),跳转到r1_lower */

        mov     r0, r1     /* 如果没有跳转, 例如R1的值更大(或者相等),则将R1的值存储到R0 */

        b       end        /* 结束 */

r1_lower:

        mov r0, r2         /* R1小于R2时跳转到此处, 将R2的值存储到R0 */

        b end              /* 结束 */

end:

        bx lr              /* THE END */

上面代码是比较两个初始值并返回最大值,C语言伪代码:


int main() {

   int max = 0;

   int a = 2;

   int b = 3;

   if(a < b) {

    max = b;

   }

   else {

    max = a;

   }

   return max;

}

现在再看一下怎么使用条件分支实现循环:


.global main

main:

        mov     r0, #0     /* 设置初始变量a */

loop:

        cmp     r0, #4     /* 比较a==4 */

        beq     end        /* 如果a==4,结束 */

        add     r0, r0, #1 /* 否则将R0中的值递增1 */

        b loop             /* 跳转到loop开始位置 */

end:

        bx lr              /* THE END */

C语言伪代码:


int main() {

   int a = 0;

   while(a < 4) {

   a= a+1;

   }

   return a;

}



B、BX、BLX指令

有三种类型的分支指令:


普通分支(B)

简单的跳转到一个函数。



带链接的跳转(BL)

将PC+4的值保存到LR寄存器,然后跳转。



带状态切换的跳转(BX)和带状态切换及链接的跳转(BLX)

与B和BL一致,只是添加了工作状态的切换(ARM模式-Thumb模式)。

需要寄存器作为第一个操作数。



BX、BLX用来切换ARM模式到Thumb模式。


.text

.global _start

_start:

     .code 32         @ ARM mode

     add r2, pc, #1   @ put PC+1 into R2

     bx r2            @ branch + exchange to R2

    .code 16          @ Thumb mode

     mov r0, #1

这里的技巧是获得当前PC的值,加1然后保存到一个寄存器,然后跳转(并且切换状态模式)到这个寄存器内的地址。可以看到加指令(add r2, pc, #1)获取到有效的PC地址值(当前PC内的值+8=0x805C)然后加1(0x805C + 1 = 0x805D)。接下来,我们跳转的地址( 0x805D = 10000000 01011101)最低有效位为1,那么意味着地址不是4字节(32bit)对齐的。跳转到这样的地址不会导致非对齐问题。在GDB中运行的样子(含GEF):


注意上面的gif图片是在低版本的GEF下创建的,所以你的显示界面可能不一样,但是逻辑是一样的。


条件分支

分支也可以有条件地执行,用于在满足特定条件时跳转到函数。我们看一个使用BEQ应用条件分支的例子,这是一段没太有用的汇编代码,只不过是在寄存器等于特定值时将一个值移动到寄存器并跳转到另一个函数的过程。


.text

.global _start

_start:

   mov r0, #2

   mov r1, #2

   add r0, r0, r1

   cmp r0, #4

   beq func1

   add r1, #5

   b func2

func1:

   mov r1, r0

   bx  lr

func2:

   mov r0, r1

   bx  lr


推荐阅读

史海拾趣

FORYARD公司的发展小趣事

在快速发展的同时,FORYARD始终不忘企业的社会责任和可持续发展目标。公司积极推广绿色生产和环保理念,采用环保材料和节能技术降低生产过程中的碳排放和资源消耗。此外,FORYARD还积极参与社会公益事业和慈善活动,为贫困地区的教育和医疗事业贡献力量。这些举措不仅提升了公司的社会形象和品牌价值,也为实现可持续发展目标做出了积极贡献。

Digitron公司的发展小趣事

随着数字化和智能化时代的到来,Digitron公司也积极拥抱变革。公司投入大量资源用于数字化转型和智能化升级,通过引入先进的生产管理系统和人工智能技术提高生产效率和产品质量。同时,Digitron公司还积极探索物联网、大数据等新技术在电子行业的应用,为客户提供更加智能、便捷的解决方案。这些努力使Digitron公司在激烈的市场竞争中保持领先地位并实现了可持续发展。

FCT Electronics公司的发展小趣事

在国内市场取得成功后,FCT Electronics开始将目光投向国际市场。公司积极参加国际电子展会,与多家国际知名企业建立了合作关系。凭借出色的产品性能和专业的客户服务,FCT Electronics的测试设备逐渐在海外市场上占据了一席之地,为公司的国际化发展奠定了坚实基础。

科山芯创(COSINE)公司的发展小趣事

随着产品的不断推出和市场的认可,科山芯创开始积极拓展市场。他们与国内外多家知名企业建立了合作关系,产品广泛应用于通讯网络、物联网、工业自动控制等多个领域。同时,科山芯创还积极参加各种行业展会和交流活动,与业界同行进行深入交流,共同推动电子行业的发展。

Aptiv公司的发展小趣事

随着数字化时代的到来,Aptiv积极拥抱数字化转型,通过引入先进的信息技术和管理系统,提升了企业的运营效率和市场竞争力。例如,公司向汽车行业某公司发出EDI对接邀请,计划通过EDI系统实现双方的数据共享和业务协同,从而提高了工作效率和下单准确性。这种数字化转型不仅有助于Aptiv在激烈的市场竞争中保持领先地位,还为整个电子行业的数字化转型提供了有益的借鉴和参考。

这五个故事展示了Aptiv公司在电子行业中的发展历程和成就,从技术创新到全球布局、创新合作、社会责任以及数字化转型等方面,都体现了公司的实力和担当。Aptiv以其卓越的技术实力和市场表现,成为了电子行业中一颗璀璨的明星。

Adaptive Interconnect Electronics, Inc. [AIE]公司的发展小趣事

为了适应全球电子市场的快速发展,AIE公司积极实施全球化战略。公司在多个国家和地区设立了分支机构或办事处,以便更好地服务当地客户。同时,AIE还积极参加国际电子展会和论坛,与全球各地的合作伙伴和客户建立了广泛的联系。这些举措不仅拓展了AIE的市场份额,也提升了公司的国际影响力。

问答坊 | AI 解惑

KEIL PDF教程,二,三,四

KEIL PDF教程,二,三,四…

查看全部问答>

如何调试跟踪Android源代码 ZT

Android平台代码很多,关系也很复杂,通常我们调试程序的时候是无法跟踪到Android framework code里的,如果能调试跟踪Android source code将给我们这些初学者带来很多便利,本文将给出一个调试跟踪Android source code 的方法。 1.首先,得到Andr ...…

查看全部问答>

电源杂讯干扰的处理

电源杂讯干扰的处理 创易电子整理出品,创易更懂电子, http://52edk.taobao.com/ 全系列阻容感一本全掌控。…

查看全部问答>

问一个连接错误!大家来看看

错误内容: JiuArmDlg.obj : error LNK2001: unresolved external symbol CLSID_DataLinks JiuArmDlg.obj : error LNK2001: unresolved external symbol IID_IDBPromptInitialize ARMV4IDbg/JiuArm.exe : fatal error LNK1120: 2 unresolved ext ...…

查看全部问答>

AVR 关于 ATmega48 掉电喊醒的问题

正在用ATmega48在做一个东西,需要在不需要的时候进入掉电模式,然后通过INT0进行喊醒。 现在的问题是:需要用低电平持续一段时间才可以喊醒,那么就必须设置INT0为低电平触发,但是我设成低电平触发后,紧接着进入掉电模式,我并没有给唤醒信号 ...…

查看全部问答>

iesample与iesimple浏览器问题!

嘿各位!     在CE5.0上通过GPRS连接上网,当设置APN为CMNET的时候,WWW和WAP网站都能上,但是如果设置为CMWAP用我们的浏览器(仿照iesimple做的)就不能浏览网站,如果用微软自带的浏览器(iesample),设置代理服务器地址为10.0.0.172就 ...…

查看全部问答>

lc谐振,rc谐振,lrc谐振。。。频率响应等问题。

lc谐振,rc谐振,lrc谐振。。。频率响应等问题。…

查看全部问答>

mega128串口问题求教

请大家帮忙看下这段mega128串口程序有什么问题,ICCAVR下的,没法接收数据啊 [code]#include #include //================================== void port_init(void) { PORTA = 0xFF; DDRA  = 0x00; PORTB = 0xFF; DDRB ...…

查看全部问答>

招聘Windows XP程序员(上海宝山)

要求:熟练掌握Windows XP驱动程序编程技术,试工期间工资2000元/月,地址是上海宝山区泰和西路2945号,有意向者请留下联系方式。…

查看全部问答>

一个问题,大家帮帮我,谢谢ALL

公司让我用EVC做个图形程序。 就是,一个对话框内,一个随时间变化而温度发生变化的曲线,横坐标是时间t,纵坐标是温度T,我自己理解就是正弦曲线那种。 小弟刚刚学习EVC图形编程,别说动的曲线,静止曲线都还不会做。所以,没什么思路! 所以请 ...…

查看全部问答>