历史上的今天
返回首页

历史上的今天

今天是:2025年02月01日(星期六)

2020年02月01日 | ARM汇编关键知识点总结

2020-02-01 来源:eefocus

1.

LDR R1, =COUNT 意思是将 COUNT 变量的地址放到 R1中

LDR R1, COUNT 意思是将 COUNT 变量地址里面的内容赋给 R1


2.


Load-Store 结构——这个应该是 RISC设计中比较有特点的一部分。在 RISC 中,CPU 并不会对内存中的数据进行操作, 所有的计算都要求在寄存器中完成。 而寄存器和内存的通信则由单独的指令来完成。而在 CSIC中,CPU是可以直接对内存进行操作的,这也是一个比较特别的地方。所以,在 ARM中,cpu只能通过寄存器来对内存的数据进行访问和更改。


LDR Rd,(地址)

STR Rd, (地址)

LDMIA Rn!, regist

STMIA Rn!, regist

注意上面 LDR/STR 和 LDMIA/STMIA 的区别,LDR/STR 命令使用时,寄存器在前,地址在后。 而在 LDMIA/STMIA 使用时, 地址在前, 寄存器在后。 这就决定了 LDR 和 LDM 同为加载命令, 但操作顺序是不同的, 同理 STR/STM。 但有一点他们是相同的, 即加载 LDR/LDM的意思是把内存的数据 (即上面的地址) 加载到寄存器; 存储 STR/STM 的意思是把寄存器的内容存储到内存(即上面的地址) 。这样比较之后也就全明白了,只需明白哪部分是寄存器,哪部分是地址(内存) ,然后区别是加载还是存储,就可以知道操作方向。


LDM/STM指令主要用于现场保护,数据复制,参数传送等。


3.


LDM/STM

IA/IB,DA,DB 数据块传输

FD/ED,EA/FA 堆栈操作

LDMIA Rn!, regList

STMIA Rn!, regList

其中 Rn 加载/存储的起始地址寄存器,Rn 必须为 R0~R7


RegList 加载/存储的起始寄存器列表,寄存器必须为 R0~R7


4.

在汇编程序中 !的使用,意思是回写,比如:


ldr r1,[sp, #S_PSR]

ldr lr, [sp, #S_PC]! 其中 ! 用来控制基址变址寻址的的最终新地址是否进行回写操作

此条语句的意思是 执行 ldr 之后 sp 被回写成 sp+#S_PC 基址变址寻址的新地址。


5.

ARM 堆栈的组织结构是满栈降的形式,满栈即 sp 是要停留在最后一个进栈元素;降,就是堆栈的增长方向是从高地址向低地址发展。

ARM 对于堆栈的操作一般采用 LDMFS(pop)和 STMFD(push)两个命令。

难点在于 STMFD 命令对于操作数是按照什么顺序压栈的。

比如:STMFD sp! {R0-R5,LR}进栈顺序是:


高地址

LR          #先进栈

R5

R4

...........

R0 <-SP

低地址


ARM 指令

多寄存器寻址:


LDMIA R0!,{R1-R4}

;R1<----[R0]

;R2<----[R0+4]

;R3<----[R0+8]

;R4<----[R0+12]

堆栈寻址:


STMFD 入栈指令,相当于 STMDB


STMFD SP!,{R2-R4}

;[SP-4]<---R4

;[SP-8]<---R3

;[SP-12]<---R2

LDMFD 出栈指令,相当于 LDMIA


LDMFD SP!,{R6-R8}

;R6<----[SP]

;R7<----[SP+4]

;R8<----[SP+8]

6.

汇编语句 LDMFD SP!, {R0-R12, LR, PC }^ 程序后面的^ ,表示什么意思?

'^'是一个后缀标志,不能在 User 模式和 Sys 系统模式下使用该标志.该标志有两个存在目的:

1) 对于 LDM 操作,程序会自动的将 spsr 的值拷贝到 cpsr 中。


比如:在 IRQ 中断返回代码中


ldmfd sp!, {r4} //读取 sp 中保存的的 spsr 值到 r4中

msr spsr_cxsf, r4 //对 spsr 的所有控制为进行写操作,将 r4的值全部注入 spsr

ldmfd sp! {r0-r12,lr,pc}^ //当指令执行完毕,pc 跳转之前,将 spsr 的值自动拷贝到 cpsr 中

2)数据的送入,送出发生在 User 用户模式下的寄存器,而非当前模式寄存器


如 ldmdb sp, {r0-lr}^;表示 sp 栈中的数据回复到 User 分组寄存器 r0-lr 中,而不是恢复到当前模式寄存器 r0-lr, 当然对于 User, System, IRQ,SVC,Abort, Undefined这6种模式来说 r0-r12是共用的,只是 r13和 r14为分别独有,对于 FIQ 模式,仅仅 r0-r7是和前6种模式的 r0-r7共用,r8-r14都是 FIQ 模式下专有。


7. 关于 ldr/str 几条指令使用的区别


ldr ip,[sp],#4 将 sp 中内容存入ip,之后 sp=sp+4;

ldr ip,[sp,#4] 将 sp+4这个新地址下的内容存入ip,之后 sp 值保持不变

ldr ip,[sp,#4]! 将 sp+4这个新地址下的内容存入ip,之后 sp=sp+4将新地址值赋给 sp

str ip,[sp],#4 将ip存入 sp 地址处,之后 sp=sp+4

str ip,[sp,#4] 将ip存入 sp+4这个新地址,之后 sp 值保持不变

str ip,[sp,#4]! 将ip存入 sp+4这个新地址,之后 sp=sp+4将新地址值赋给sp

8.

movs r1,#3; movs 将导致 ALU 被更改,因为 r1赋值非0,即操作结果 r1非0,所以 ALU 的 Z 标志清0

N,Z,C,V 称为 ALU(算术逻辑单元)状态标志。N:如果结果是负数则置位;Z:如果结果是零则置位;C:如果发生进位则置位;V:如果发生进位则置位。

9.

teq r1,#0 //r1-0,将结果送入状态标志,如果 r1和0相减的结果为0,那么 ALU 的Z 置位,否则 Z 清0

bne reschedule//ne 表示 Z 非0,即:不等,那么执行 reschedule 函数

10。

.使用 tst 来检查是否设置了特定的位

tst r1,#0x80   //按位 and 操作,检测 r1的0x1<<7,即第7位是否置1,按位与之后结果为0,那么 ALU 的 Z 置位

beq reset   //如果 Z 置位,即:以上按位与操作结果是0,那么跳转到 reset 标号执行


11.

  PC 和 LR 寄存器中在异常发生时,或在系统运行时其 PC 和 LR 寄存器值为多少?


下图为用户模式下 ARM 处理器体系结构:

  从图1中我们看到, 在 user 模式下, ARM CPU 有16个数据寄存器, 被命名为 r0~r15(这个要比 x86的多一些)。r13~r15有特殊用途,其中:

◆ r13 - 指向当前栈顶,相当于 x86的 esp,这个东西在汇编指令中要用 sp 表示

◆ r14 - 称作链接寄存器,指向函数的返回地址。用 lr 表示,这和 x86将返回地址保存在栈中是不同的

◆ r15 - 类似于 x86的 eip, 其值等于当前正在执行的指令的地址+8(因为在取址和执行之间多了一个译码的阶段),这个用 pc 表示。


另外, ARM 处理器还有一个名为 cspr 的寄存器, 用来监视和控制内部操作, 这点和x86 的状态寄存器是类似的。具体的内容就用到再说了。

总结:在系统正常运行时,PC 值等于当前正在执行的指令的地址+8,(因为在取址和执行之间多了一个译码的阶段)。


寄存器 R14(LR 寄存器)有两种特殊功能:

1)在任何一种处理器模式下,该模式对应的 R14寄存器用来保存子程序的返回地址。

当执行 BL 或 BLX 指令进行子程序调用时,子程序的返回地址被放置在 R14中。这样,只要把 R14内容拷贝到 PC 中,就实现了子程序的返回。

2)当某异常发生时,相应异常模式下的 R14被设置成异常返回的地址(对于某些异常,可能是一个偏移量,一个较小的常量)。异常返回类似于子程序返回,但有小小的不同。


总结:

  所谓的子程序的返回地址, 实际就是调用指令的下一条指令的地址, 也就是 BL 或 BLX指令的下一条指令的地址。所谓的异常的返回的地址,就是异常发生前,CPU 执行的最后一条指令的下一条指令的地址。

例如:(子程序返回地址示例)


指令                      指令所在地址

ADD R2,R1,R3  ;0x300000           

BL subC               ;0x300004

MOV R1,#2          ;0x300008

BL 指令执行后,R14中保存的子程序 subC 的返回地址是0x300008。


再例如:(异常返回地址示例)


指令                      指令所在地址

ADD R2,R1,R3  ;0x300000

SWI 0x98            ;0x300004

MOV R1,#2          ;0x300008

SWI 指令执行后,进入 SWI 异常处理程序,此时 R14中保存的返回地址为0x300008。


总结:在系统正常运行时,PC 的值存储的是当前正在执行的指令地址的后两条地址(即+8),而 LR 是在子程序返回或异常返回时才使用,其值为当前正在执行的指令的后一条指令地址(即+4)。

12.

  由于上面 LR 和 PC 寄存器值的特点:我们可以解释软中断实现原理进行解释。


  SWI,即 software interrupt 软件中断。该指令产生一个 SWI 异常。意思就是把处理器模式改变为超级用户模式,CPSR 寄存器保存到超级用户模式下的 SPSR 寄存器,并且跳到 SWI 向量。其 ARM 指令格式如下:


SWI{cond} immed_24

  Cond 域:是可选的条件码 (参见 ARM 汇编指令条件执行详解).


  immed_24域:范围从 0 到 224-1 的表达式,(即0-16777215)。用户程序可以使用该常数来进入不同的处理流程。

一、方法1:获取 immed_24操作数。

  为了能实现根据指令中 immed_24操作数的不同,跳转到不同的处理程序,所以我们往往需要在 SWI 异常处理子程序中去获得 immed_24操作数的实际内容。获得该操作数内容的方法是在异常处理函数中使用下面指令


LDR R0,[LR,#-4]

  该指令将链接寄存器 LR 的内容减去4后所获得的值作为一个地址,然后把该地址的内容装载进 R0。此时再使用下面指令,immed_24操作数的内容就保存到了 R0:


BIC R0,R0,#0xFF000000


;Rd,  Rn, Oprand2

;BIC(位清除)指令对 Rn 中的值 和 Operand2 值的反码按位进行逻辑“与”运算

 


  该指令将 R0的高8位(绿色表示的)清零,并把结果保存到 R0,意思就是取 R0的低24位。


  所以,在 SWI 异常处理子程序中执行 LDR R0,[LR,#-4]语句,实际就是把产生本次 SWI异常的 SWI 指令的内容(如:SWI 0x98)装进 R0寄存器。又因为 SWI 指令的低24位保存了指令的操作数(如: 0x98), 所以再执行 BIC R0, R0, #0xFF000000语句, 就可以获得 immed_24操作数的实际内容。


二、方法2:使用参数寄存器。

  实际上,在 SWI 异常处理子程序的实现时,还可以绕开 immed_24操作数的获取操作,这就是说,我们可以不去获取 immed_24操作数的实际内容,也能实现 SWI 异常的分支处理。这就需要使用 R0-R4寄存器,其中 R0-R4可任意选择其中一个,一般选择R0,遵从 ATPCS 原则。


  具体方法就是, 在执行 SWI 指令之前, 给 R0赋予某个数值, 然后在 SWI 异常处理子程序中根据 R0值实现不同的分支处理。例如:


指令                                             指令所在地址

MOV R0,#1                               ; #1给 R0

SWI 0x98                                  ; 产生 SWI 中断,执行异常处理程序

SoftwareInterrupt

ADD R2,R1,R3 ;

;SWI 异常处理子程序如下

SoftwareInterrupt

CMP R0, #6                               ; if R0 < 6

LDRLO PC, [PC, R0, LSL #2]       ; if R0 < 6,PC = PC + R0*4,else next

MOVS PC, LR

SwiFunction

DCD function0                            ;0

DCD function1                            ;1

DCD function2                            ;2

DCD function3                            ;3

DCD function4                            ;4

DCD function5                            ;5

Function0

异常处理分支0代码

Function1

异常处理分支1代码

function2

异常处理分支2代码

function3

异常处理分支3代码

function4

异常处理分支4代码

function5

异常处理分支5代码


  在 ARM 体系结构中,当正确读取了 PC 的值时,该值为当前指令地址值加8字节,也就是说,对于 ARM 指令集来说,读出的 PC 值指向当前指令的下两条指令的地址,本例中就是指向SwiFunction 表头 DCD function0 这个地址,在该地址中保存了异常处理子分支 function0的入口地址。 所以, 当进入 SWI 异常处理子程序 SoftwareInterrupt 时, 如果 R0=0, 执行 LDRLO PC,[PC, R0, LSL #2]语句后, PC 的内容即为 function0的入口地址, 即程序跳转到了 function0执行。


  在本例中, 因为 R0=1, 所以, 实际程序是跳转到了 function1执行。 R0左移2位 (LDRLO PC,[PC, R0, LSL #2]) ,即 R0*4, 是因为 ARM 指令是字(4个字节)对齐的 DCD function0等伪指令也是按4字节对齐的。


  在本方法的实现中,实际指令中的24位立即数(immed_24域)被忽略了, 就是说immed_24域可以为任意合法的值。 如在本例中, 不一定使用 SWI 0x98, 还可以为 SWI 0x00或者 SWI 0x01等等,程序还是会进入 SWI 异常处理子程序 SoftwareInterrupt,然后根据 R0的内容跳转到相应的子分支。


13.

在 ARM 中栈底和栈顶的标识如下:满递减栈,栈底在上,栈顶在下是 SP。如下图所示:


14.

  下面就两个具体的例子谈谈 ARM 汇编。第一个是使用跳转表解决分支转移问题的例程,源代码如下(保存的时候请将文件后缀名改为 s):


AREA JumpTest,CODE,READONLY

CODE32

num EQU 4

ENTRY

start

MOV r0, #4

MOV r1, #3

MOV r2, #2

MOV r3, #0

CMP r0, #num

BHS stop

ADR r4, JumpTable

CMP r0, #2

MOVEQ r3, #0

LDREQ pc, [r4,r3,LSL #2]

CMP r0, #3

MOVEQ r3, #1


LDREQ pc, [r4,r3,LSL #2]

CMP r0, #4

MOVEQ r3, #2

LDREQ pc, [r4,r3,LSL #2]

CMP r0, #1

MOVEQ r3, #3

LDREQ pc, [r4,r3,LSL #2]

DEFAULT

MOVEQ r0, #0

SWITCHEND

stop

MOV r0, #0x18

LDR r1, =0x20026

SWI 0x123456

JumpTable

DCD CASE1

DCD CASE2

DCD CASE3

DCD CASE4


DCD DEFAULT

CASE1

ADD r0, r1, r2

B SWITCHEND

CASE2

SUB r0, r1, r2

B SWITCHEND

CASE3

ORR r0, r1, r2

B SWITCHEND

CASE4

AND r0, r1, r2

B SWITCHEND

END


  程序其实很简单,可见我有多愚笨!还是简要介绍一下这段代码吧。首先用 AREA 伪代码加上 CODE, 表明下面引出的将是一个代码段 (于此相对的还有数据段 DATA) , ENTRY 和 END成对出现,说明他们之间的代码是程序的主体。start 段给寄存器初始化。ADR r4, JumpTable一句是将相当于数组的 JumpTable 的地址付给 r4这个寄存器。


  stop 一段是用来是程序退出的,第一个语句“MOV r0,#0x18”将 r0赋值为0x18,这个立即数对应于宏 angel_SWIreason_ReportException。表示 r1中存放的执行状态。语句“LDR r1,=0x20026”将 r1的值设置成 ADP_Stopped_ApplicationExit,该宏表示程序正常退出。然后使用SWI,语句“SWI 0x123456”结束程序,将 CPU 的控制权交回调试器手中。


  在 JumpTable 表中, DCD 类型的数组包含四个字, 所以, 当实现 CASE 跳转的时候, 需要将给出的索引乘上4,才是真正前进的地址数。

在语句:


CMP r0,#num

BHS stop


  书上意思是: 如果 r0寄存器中的值比 num 大的话, 程序就跳转到 stop 标记的行。 但是,实际测试的时候,我发现如果 r0和 num 相等也能跳转到 stop 标记的行,也就是说只要 r0小于num 才不会跳转。


推荐阅读

史海拾趣

Enovation Controls LLC公司的发展小趣事

由于篇幅限制,我无法直接为您提供5个完整的、每篇至少500字的Enovation Controls LLC公司发展起来的相关故事。但我可以概述5个关键事件或阶段,这些事件对于Enovation Controls LLC公司的发展起到了重要作用。

  1. EControls的成立与专长

Enovation Controls LLC的前身可以追溯到1994年成立的EControls公司。由Kennon Guglielmo博士创立,EControls专注于为发动机和车辆行业的OEM提供完整的发动机控制解决方案的设计、工程和生产。凭借其在发动机控制领域的专业技术和经验,EControls逐渐在行业中树立了领先地位。

  1. EControls与FW Murphy的合并

2009年,EControls与FW Murphy合并,成立了Enovation Controls LLC。FW Murphy是一家历史悠久的公司,由Frank W. "Pat" Murphy于1939年创立,专注于发动机仪表和保护的创新。这次合并将EControls的发动机控制技术与FW Murphy的仪器和显示功能相结合,为客户提供了更为全面和先进的发动机控制、保护和监控解决方案。

  1. 创新传统的建立

Enovation Controls继承了FW Murphy的开拓精神和对简单、可靠发动机保护的追求。公司拥有一支由300多名员工组成的多元化国际团队,他们通过全球销售、制造和应用工程业务为世界各地的客户提供服务。这种全球化的布局和多元化的团队为Enovation Controls的创新和发展提供了强大的支持。

  1. 业务部门的拓展

合并后,Enovation Controls经营着四个业务部门:动力控制、车辆技术、天然气生产控制和燃料系统。这些业务部门覆盖了广泛的行业和应用领域,包括离路车辆、娱乐和商业船只、农业和水泵、发电、工业和移动设备等。通过不断拓展业务领域和深化技术应用,Enovation Controls在电子行业中建立了稳固的地位。

  1. 新产品的推出与市场反响

Enovation Controls不断推出新产品和技术创新,以满足客户不断变化的需求。例如,在2016年推出的TEC-10控制器就是一款为工业需求量身定制的控制器。这款控制器具有强大的功能和灵活性,适用于各种应用场景。由于其出色的性能和易用性,TEC-10控制器在市场上获得了广泛的好评和认可。

这些事件和阶段共同构成了Enovation Controls LLC公司发展起来的重要历程。通过不断创新、拓展业务领域和推出新产品,Enovation Controls在电子行业中取得了显著的成就和地位。

锋鸣电子(Fengming)公司的发展小趣事

在稳固光伏胶膜市场的同时,福斯特并未停止前进的步伐。公司积极拓展产品品类,进入电子新材料领域。2014年,公司开始开发PCB用感光干膜,进军电子信息产业。感光干膜作为PCB产业最核心的工艺材料之一,对电子信息产业的发展具有重要意义。福斯特凭借其技术实力和市场洞察力,迅速在该领域取得突破,成为行业内的佼佼者。至2020年,公司感光干膜销量大幅增长,为公司带来了新的增长点。

ARCOL公司的发展小趣事

品质是ARCOL公司的核心竞争力。公司始终坚持以质量为核心,建立了严格的质量管理体系。从原材料的采购到产品的出厂,每一个环节都经过严格把关,确保产品的质量和性能达到最高标准。同时,ARCOL还注重品牌建设,通过不断提升产品质量和服务水平,树立了良好的企业形象和口碑。

DAQ Electronics LLC公司的发展小趣事

随着国内外市场的不断拓展和客户需求的不断增长,DAQ Electronics LLC公司开始积极探索国际化发展道路。公司积极参加国际展会和技术交流活动,与全球同行建立了广泛的联系和合作。同时,公司也加大了对海外市场的投入力度,不断拓展海外市场份额。这种国际化的发展战略不仅为公司带来了更多的商业机会和合作伙伴也为公司未来的发展提供了更广阔的空间和机遇。

请注意,以上故事均为虚构内容,仅供参考。如有需要,建议直接联系DAQ Electronics LLC公司获取其真实的发展历程和故事。

Heraeus公司的发展小趣事

随着技术的不断成熟和产品线的丰富,DAQ Electronics LLC公司开始积极拓展市场。公司通过与科研机构、高校以及企业建立合作关系,将数据采集技术应用于更多领域。在科学研究领域,DAQ Electronics LLC公司的数据采集设备为实验数据的准确获取提供了有力保障;在工业生产领域,其设备则帮助企业实现了对生产过程的实时监控和数据分析。

Great American Electronics公司的发展小趣事

在DAQ Electronics LLC公司的发展过程中,技术创新一直是其核心竞争力。公司不断投入研发资源,推出了多款具有自主知识产权的数据采集产品。其中,一款便携式DAQ设备因其高精度、低噪声的特点,在市场上获得了广泛认可。这款产品的成功,为公司赢得了大量客户,也为公司的后续发展奠定了坚实基础。

问答坊 | AI 解惑

求一基于单片机和LabVIEW制作的数据采集系统设计

内容:本系统由两部分组成,以MCS-51单片机作为下位机采集数据部分设计和 MCS-51与LabVIEW之间数据通讯部分设计。 要求: 1、查阅相关的文献资料,撰写开题报告并完成外文资料翻译。 2、熟练掌握掌握相关元器件的功能,并会加以运用。 3、熟练 ...…

查看全部问答>

关于单片机硬件抗干扰

在研制带处理器的电子产品时,如何提高抗干扰能力和电磁兼容性? 一、下面的一些系统要特别注意抗电磁干扰: 1、微控制器时钟频率特别高,总线周期特别快的系统。 2、系统含有大功率,大电流驱动电路,如产生火花的继电器,大电流开关等。 ...…

查看全部问答>

求助:网页下载

我在evc的模拟器上下载网页,编程中用到套接字,代码如下: WSADATA  wsaData;   if(  WSAStartup(MAKEWORD(2,0),  &wsaData)   ? ?  LOBYTE(wsaData.wVersion)!=  2&n ...…

查看全部问答>

ISP FLASH 擦除失败

按照周公的配置顺序开始擦除 因为Tiny M0没弄复位键 所以擦除时拔掉USB 然后上电 测试过串口可以收发 P0.1接地了 可是还是失败 …

查看全部问答>

2808编译时出现的ERROR请教

在build的时候出现这样的错误: >>   error: illegal relocation type 050002 found in section .debug_info, file             C:\\\\tidcs\\\\DMC\\\\c28\\\\v32x\\\\lib\\\\dmclib\\\\c ...…

查看全部问答>

DSP的归一化

 在DSP中如何对坐标数据进行归一化,有没有函数什么的呀 还有如何将DSP中的数据导出来 放在excel中…

查看全部问答>

“Vds-id”门级电压步步高

运行Vds-id DC ITM漏极[1]扫描电压的默认设置是0-4V,以100 mV为一个步长,同时门极电压有三级:1.5,2.0,2.5V(图1,图2)。当改变这些设置时,请注意,电压和步长的设置可以用于Vds-id-pulse测试的设置。 图1.    &nbs ...…

查看全部问答>

【设计工具】Xilinx 常见问题及答案

问:我在ISE4.1中,用fpga express verilog编译的某些文件,用modelsimxe只能前仿,不能后仿,不知 5.1i是否有改进? 问:和 5.1结合比较好的验证工具除了Modelsim外,PC机上可运行的有什么?  问:ISE在综合的时候,把很多中间信号、特别是组 ...…

查看全部问答>

[haner]第三周、M4在M3基础上的提升

  第三周:试用笔记:分别比较了浮点计算、电机控制、冬眠模式。   直接上附件吧。   视频1楼…

查看全部问答>

ALTERA 官网登陆

为什么每次登陆Altera 官网时总是提示 “您的进程已经过期。请重新登录。” ?…

查看全部问答>