注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

晓风弯月

我无所畏惧的向着我的梦想追寻

 
 
 

日志

 
 

ARM汇编编程基础之六 —— 其它寻址模式与其它指令  

2014-02-23 15:44:07|  分类: LINUX |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

单击,返回主页,查看更多内容

现在我们已经掌握了所有知识,可以编写简单的ARM汇编程序,但如果要编写较为复杂的ARM程序,就必须掌握更多的寻址模式和指令,这就是本文的重点所在。

我们在“基本寻址模式与基本指令”一文中学习了最常用的3种寻址方式。下面介绍其它寻址方式。

1、基址寻址

基址寻址就是将基址寄存器的内容与指令中给出的偏移量相加,形成操作数的有效地址。基址寻址用于访问基址附近的存储单元,常用于查表、数组操作、功能部件寄存器访问等。基址寻址指令举例如下:

LDR R1,[R2,#0x0C]

R2的值+0x0C形成内存地址,读取内存中该地址上的内容,放入R1

其它额外需要了解的内容:

§零偏移。 如:LDR R0,[R1]

§前索引偏移。 如:LDR R0,[R1,#0x04]!,表示将R1的值加上4后作为内存地址,并且指令执行结束时,R1本身的值也要加4。这里!表示要回写

§程序相对偏移。 如:LDR R0,labe1,表示将标号label所代表的地址处存放的内容放入R0,相当于LDR R0, [PC, #某个常数]

§后索引偏移。 如:LDR R0,[R1],#0x04,表示将R1的值作为内存地址,并且指令执行结束时,R1本身的值要加4

2、多寄存器寻址

多寄存器寻址一次可传送几个寄存器值,允许一条指令传送16个寄存器的任何子集或所有寄存器。多寄存器寻址指令举例如下:

LDMIA R1!,{R2-R4,R6} ,它是ldr的多寄存版本,将内存中的4个字放入寄存器R2,R3,R4,R6中

image image

                           指令执行前                                                        指令执行后

两点说明:

1)、R1!中的!号表示在指令执行完成后,要改变(回写)基址寄存器(R1)的值

2)、寄存器列表{R2-R4, R6}中的顺序并不要紧。最终寄存器与内存地址的对应关系是:编号小的寄存器与内存的低地址相对应

两点问题:

1)、为什么内存起地址是0x40000000,而不是0x40000004

2)、为什么内存地址是从0x40000000 ---- 0x4000000C,而不是从0x3FFFFFF4 ----  0x40000000

要解释上面2个问题,其实也很简单。其实多寄存加载指令ldm总共有4个:ldmia、 ldmib、 ldmda、 ldmdb。ia的意思是increase after,ib的意思是increase before,da的意思是decrease after,db的意思是decrease before。以LDMIA R1!, {R2-R4, R6}为例子,这里的ia是指办事(将内存中的数加载到寄存器)之后增加基址寄存器(R1)的值。这条指令的执行过程从逻辑上看,如下:

1)、先办事:将R1的值(0x40000000)作为内存地址,到该地址处取得数(0x01),加载到寄存器R2中

2)、后增加:将R1的值从0x40000000增加为0x40000004

再重复上面的操作3次,分别将内存中的数0x02、0x03、0x04放到寄存器中R3、R4、R6中,最后R1的值变为0x40000010。

这个例子中,如果将ldmia改为ldmib,则R2、R3、R4、R6中存放的是0x02、0x03、0x04、内存0x40000010处的内容,最后R1的值为0x40000010。

除了4条多寄存器加载指令外,还有4条类似的多寄存器存储指令,分别是stria、 strib、 strda、 strdb

3、堆栈寻址

由于ARM指令集没有专门的出栈和入栈指令,所以ARM汇编程序是采用SP作为栈指针,以stm指令完成入栈操作,以ldm指令完成出栈操作。

以入栈后SP的值是增加还是减少为依据,可将堆栈类型划分为递增堆栈(向上生长)和递减堆栈(向下生长);

image

以SP所指向的内存处存放的是栈顶元素还是下一次要入栈的元素,可将堆栈类型划分为满堆栈和空堆栈

image

那么当堆栈类型为空递减堆栈时候,入栈操作应该使用什么指令?出栈操作应该使用什么指令?进一步,如果堆栈类型为空递增、满递增、满递减堆栈,又将如何呢?如果你不看下面的答案,我相信你一定会让这几个问题折磨得做很多的脑力体操,然后感叹ARM指令集的设计者太不为你这样的程序员考虑了,给了你本不应该由你承担的负荷。但事实上正相反,ARM指令集的设计者充分理解了你作为程序员的苦恼,请看下面的答案。

数据块传送 堆栈操作 说明
存储 压栈
STMDA STMED 空递减
STMIA STMEA 空递增
STMDB STMFD 满递减
STMIB STMFA 满递增
数据块传送 堆栈操作 说明
加载 出栈
LDMDA LDMFA 满递增
LDMIA LDMFD 满递减
LDMDB LDMEA 空递增
LDMIB LDMED 空递减

 

这2张表的第一、三列回答了前面你绞尽脑汁回答的问题。而第二列则体现了ARM指令集的设计者对作为程序员的你的充分体贴。第二列中的ED、EA、FD、FA分别表示empty descend(空递减)、 empty ascend(空递增)、 full descend(满递减)、 full ascend(满递增),其含义是说,如果你采用的是空递减(空递增、满递减、满递增)堆栈的话,入栈操作则使用指令STMED(STMEA、STMFD、STMFA),出栈操作则使用指令LDMED(LDMEA、LDMFD、LDMFA)。从此你再也不会为你应该使用ia、ib、da还是db来实现出、入栈操作而苦恼了。

STMED、STMEA、STMFD、STMFA和LDMED、LDMEA、LDMFD、LDMFA就是所谓的堆栈寻址指令。由此可见:为了对程序员体贴入微,ARM指令集的设计者设计了堆栈寻址指令,其实质就是多寄存寻址指令的快捷方式。

4、寄存器移位寻址

寄存器移位寻址是ARM指令集特有的寻址方式。当第2个操作数是寄存器移位方式时,第2个寄存器操作数在与第1个操作数结合之前,选择进行移位操作。例如:

MOV R0,R2,LSL #3 表示将R2的值逻辑左移3位,结果放入R0,即是R0=R2×8。

移位的方式有以下几种:

image

LSL(logic shift left):逻辑左移

LSR(logic shift right):逻辑右移

ASR(arithmetic shift right):算术右移

ROR(rotate shift right):循环右移

RRX(rotate shift right with extend):带扩展的循环右移。其中的C指的是CPSR的C位

5、相对寻址

相对寻址是基址寻址的一种变通。由程序计数器PC提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到的地址即为操作数的有效地址。例如:

      LOOP
    ...
LOOP    MOV    R6,#1

该条B指令的意思是要跳转到标号LOOP所代表的指令处,其含义相当明显,但你要明白CPU根本不明白标号是个什么东西(事实上在指令的机器码中根本就没有标号这种东西),那么b loop这条指令的机器码会是什么呢?答案是:高8bit是操作码相关内容,低24bit是一个常数,表示从b指令到mov指令之间的内存地址的差值(如果不考虑流水线的影响的话)。由此可见,b loop这条指令相当于add pc, pc, #偏移量常数,典型的相对于PC(当前指令地址)的相对寻址。由于是相对于当前指令地址进行相对寻址,所以无论程序最终运行在内存的何处(即使运行的地址不是它预期的位置),这条B指令都能正确运行。关于相对寻址、程序期望的运行地址等等,我将在“ARM汇编伪指令”一文中详细描述。

随便说一下,前面学到b指令的跳转范围是当前指令的先后32M,为什么是这个范围呢?因为24bit常数用1个比特区别正负,还剩23bit,同时由于ARM指令在内存中的地址其最低2bit一定是0(为什么?请自行思考一下),因此23bit中可以不必表示这2个0,所以23bit可以表示的范围是0 ---- 2^25,即:0 ---- 32M。 

我们在“基本寻址模式与基本指令”一文中学习了最常用的指令。下面介绍其它较为常用的指令。

1、访存指令

LDRH(半字加载);LDRSH (有符号半字加载);STRH(半字存储)

交换指令

助记符

说明

操作

SWP Rd,Rm,[Rn]

寄存器和存储器字数据交换

Rd←[Rn],[Rn]←Rm (Rn≠Rd或Rm)

SWPB Rd,Rm,[Rn]

寄存器和存储器字节数据交换

Rd←[Rn],[Rn]←Rm (Rn≠Rd或Rm)

 

2、数据处理指令

助记符

说明

操作

MVN Rd,operand2

数据非传送

Rd←(~operand2)

 

助记符

说明

操作

RSB Rd, Rn, operand2

逆向减法指令

Rd←operand2-Rn

ADC Rd, Rn, operand2

带进位加法

Rd←Rn+operand2+Carry

SBC Rd, Rn, operand2

带进位减法指令

Rd←Rn-operand2-(NOT)Carry

RSC Rd, Rn, operand2

带进位逆向减法指令

Rd←operand2-Rn-(NOT)Carry

这里要特别提到,ADC指令结合CPSR,可以实现64位整数加法,详情参见“杂项解释”一文

助记符

说明

操作

BIC Rd, Rn, operand2

按位清除指令

Rd←Rn & (~operand2)

其实现功能是:将Rn中对应于operand2中为1的bit位全部清0,然后将结果保存到Rd中

助记符

说明

操作

CMN Rn, operand2

负数比较指令

标志N、Z、C、V←Rn+operand2

TST Rn, operand2

位测试指令

标志N、Z、C←Rn & operand2

TEQ Rn, operand2

相等测试指令

标志N、Z、C←Rn ^ operand2

TST指令测试的是:Rn中所有指定bit位是否全为0(指定的bit位是operand2中为1的所有位);

TEQ指令测试的是:Rn和operand2是否相等。这点上与CMP指令一样,区别在于CMP指令除了可以比较2个数是否相等外,也可以比较2个数谁大谁小,但TEQ不行。

3、乘法指令

助记符

说明

操作

MUL Rd,Rm,Rs

32位乘法指令

Rd←Rm*Rs (Rd≠Rm)

MLA Rd,Rm,Rs,Rn

32位乘加指令

Rd←Rm*Rs+Rn (Rd≠Rm)

UMULL RdLo,RdHi,Rm,Rs

64位无符号乘法指令

(RdLo,RdHi) ←Rm*Rs

UMLAL RdLo,RdHi,Rm,Rs

64位无符号乘加指令

(RdLo,RdHi) ←Rm*Rs+(RdLo,RdHi)

SMULL RdLo,RdHi,Rm,Rs

64位有符号乘法指令

(RdLo,RdHi) ←Rm*Rs

SMLAL RdLo,RdHi,Rm,Rs

64位有符号乘加指令

(RdLo,RdHi) ←Rm*Rs+(RdLo,RdHi)

 

4、协处理器指令

参见“MMU与内存保护的实现”一文

5、杂项指令

SWI:软中断指令,参见“swi与system call的实现”一文

MRS、MSR:程序状态寄存器操作指令,参见“ARM异常处理”一文

  评论这张
 
阅读(283)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017