其它寻址模式与其它指令

来源:互联网 发布:淘宝联盟结算规则 编辑:程序博客网 时间:2024/05/16 06:26
我们在“基本寻址模式与基本指令”一文中学习了最常用的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指令集的设计者充分理解了你作为程序员的苦恼,请看下面的答案。

数据块传送堆栈操作说明存储压栈STMDASTMED空递减STMIASTMEA空递增STMDBSTMFD满递减STMIBSTMFA满递增数据块传送堆栈操作说明加载出栈LDMDALDMFA满递增LDMIALDMFD满递减LDMDBLDMEA空递增LDMIBLDMED空递减

 

这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提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到的地址即为操作数的有效地址。例如:

    B    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异常处理”一文


原创粉丝点击