ARM汇编指令 对比记忆 (整理)

ARM汇编指令 对比记忆 (整理)
ARM汇编指令 对比记忆 (整理)

参考资料:

1. Richard Blum,Professional Assembly Language

2. GNU ARM 汇编快速入门,https://www.360docs.net/doc/534353599.html,/u/31996/showart.php?id=326146

3. ARM GNU 汇编伪指令简介,https://www.360docs.net/doc/534353599.html,/jb8164/archive/2008/01/22/41661.aspx

4. GNU汇编使用经验,https://www.360docs.net/doc/534353599.html,/u1/37614/showart_39009

5.html

5. GNU的编译器和开发工具,https://www.360docs.net/doc/534353599.html,/blog-htm-do-showone-uid-34335-itemid-81387-type-blog.html

6. 用GNU工具开发基于ARM的嵌入式系统,https://www.360docs.net/doc/534353599.html,/liren0@126/blog/static/32897598200821211144696/

7. objcopy命令介绍,https://www.360docs.net/doc/534353599.html,/junhua198310/archive/2007/06/27/1669545.aspx

从网上找到一些关于ARM伪指令的资料,现整理如下:

一. Linux汇编行结构

任何汇编行都是如下结构:

[:] [} @ comment

[:] [} @ 注释

Linux ARM 汇编中,任何以冒号结尾的标识符都被认为是一个标号,而不一定非要在一行的开始。

【例1】定义一个"add"的函数,返回两个参数的和。

.section .text, “x”

.global add @ give the symbol add external linkage

add:

ADD r0, r0, r1 @ add input arguments

MOV pc, lr @ return from subroutine

@ end of program

1. LDR

LDR R0, =0X3FF5000 ;伪指令,把0X3FF5000这个地址送给R0

LDR R0, 0XFF ; 把立即数0xff送给R0

LDR R0, =&FF ; &相当于0X

BIC R0, R0, #%1011 ;.#表示立即数,%表示二进制

LDR R1, =0x3ff5000 ;伪指令 R1=0X3FF5000

LDR R1, 0x3ff5000 ;存储器访问指令 R1= [0x3ff5000]

2. adr与ldr比较

adr r0, InitSystem ;

ldr r1, =InitSystem ;

伪指令adr r0,InitSystem 编译时汇编成:sub r0,PC,#offset to InitSystem

LDR r1,=InitSystem ,这种方式读取的地址值在连接时已经被固定了,这种代码

不是位置无关的。遇到LDR伪指令时,汇编编译器将该地址值保存到一个缓冲区(l iteral pool)中,然后将该LDR 伪指令处理成一条基于PC到该数据缓冲区单元的LD R 指令,从而将该地址值读取到寄存器总,这时,要求该数据缓冲区到PC的距离小

于4KB。如果该目标地址值为一个外部地址值或者不在本数据段内,则汇编译器在目标文件中插入一个地址重定位伪操作,当连接器进行连接时生成该地址值。

LDR r1,=InitSystem 汇编成:LDR R1,[PC,#offset to Litpool1]

------------------------------------------------------

adr用来加载地址,例如adr r0,var1

ldr用来加载地址处的内容,例如ldr r0,var1

上面的这种语法只能从.text段中加载

但ldr r0,=var1可从任意段中加载地址

ldr有伪指令和非伪指令,伪指令后面的立即数前加=

ADR在编译时会被替换成一条add或者sub指令,如果替换不了则报错。相对PC寻址ADRL会被替换成两条指令,替换不了报错误。相对PC或者积存器寻址

这两条指令依据立即数的对齐方式不同,允许的立即数范围也不同。

LDR则是产生文字池的方式加载常量,基于PC的相对寻址,专用加载32bit立即数.

通过反汇编可以很容易看出LDR和ADR区别:

假设入口点地址为0x8000

AREA LDRlabel, CODE, READONLY

ENTRY ; Mark first instruction to execute

start

BL func1 ; Branch to first subroutine

stop

MOV r0, #0x18 ; angel_SWIreason_ReportException

LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit

LDR r1,=0xff

SWI 0x123456 ; ARM semihosting SWI

func1

LDR r0, =start ; => LDR R0,[PC, #offset to Litpool 1]

ADR r2,start

LDR r1, =Darea +12 ; => LDR R1,[PC, #offset to Litpool 1]

ADR r3,Darea+12

MOV pc,lr ; Return

LTORG ; Literal Pool 1

Darea SPACE 8000 ; Clears a 8000 byte area of memory,

; starting at the current location,

; to zero.

END

反汇编后:

start [0xeb000003] bl func1

stop [0xe3a00018] mov r0,#0x18

00008008 [0xe59f1018] ldr r1,0x00008028 ; = #0x00020026 0000800c [0xe3a010ff] mov r1,#0xff

00008010 [0xef123456] swi 0x123456

func1 [0xe59f0010] ldr r0,0x0000802c ; = #0x00008000

00008018 [0xe24f2020] sub r2,pc,#0x20 ; #0x8000

0000801c [0xe59f100c] ldr r1,0x00008030 ; = #0x00008040 00008020 [0xe28f3018] add r3,pc,#0x18 ; #0x8040

00008024 [0xe1a0f00e] mov pc,r14

00008028 [0x00020026] dcd 0x00020026&...

0000802c [0x00008000] dcd 0x00008000....

00008030 [0x00008040] dcd 0x00008040@...

Darea [0x00000000] dcd 0x00000000....

00008038 [0x00000000] dcd 0x00000000....

0000803c [0x00000000] dcd 0x00000000....

00008040 [0x00000000] dcd 0x00000000....

00008044 [0x00000000] dcd 0x00000000....

……….

3. MOV

MOV加载8位立即数

8位立即数即第2操作数,必须可由一个8位常数循环移位偶数位得到,如0xf0000000,0xf00000001都是合法的

4. 数据回写

例如:

ldr r0,[r1]!

stmdb sp!,{r0,r4}

!用于前索引方式中表示数据回写,例如:ldr r0,[r1,#4]!

后索引方式不用!, 数据始终回写,例如:ldr r0,[r1],#4

对堆栈方式中用!, 表示堆栈自动增加或者减少

访问内存的LDR/STR指令索引方式

ldr r0,[r1,#4]前索引, 先加

ldr r0,[r1],#4后索引, 后加

5. DCD和SPACE

DCD

分配一个地址并初始化为指定的表达式

如 DCD 0x8000

就是分配一个32位的地址,其内容是0x8000

如果用上标号label,那么这个label相当于一个变量,如

label DCD 0X8000

这样引用label就是使用了0x8000这个数值。

SPACE

分配一段指定长度的内存空间并初始化为0

label SPACE 0x8000

就是分配一段长度为0x8000的空间,并初始化为0

Data1 DCD 1,2,3

Data2 SPACE 12

反汇编后的结果:

Data1 [0x00000001] dcd 0x00000001 ....

00008040 [0x00000002] dcd 0x00000002 ....

00008044 [0x00000003] dcd 0x00000003 ....

Data2 [0x00000000] dcd 0x00000000 ....

0000804c [0x00000000] dcd 0x00000000 ....

00008050 [0x00000000] dcd 0x00000000 ....

6. LTORG与LDR

LTORG是与LDR联合使用的literal pool,可以在函数尾部声明, 这样相对PC偏移最少, 如果不用LTORG, 则编译器自动在(整个)程序末尾声名,但这样偏移有可能太大而编译不通过.

ARM立即数,LDR和MOV的区别

Mov 是把立即数赋给一个寄存器,但对立即数的范围有要求。只能是由8bit 连续有效位通过偶数次移位能得到的数。如果立即数超出这个范围,就没办法用一条MOV指令给寄存器赋值。

LDR除了普通的读数之外,也有给寄存器赋立即数的功能。

你只要写LDR R0,=0xabcdef

它没有立即数范围的限制。因为这是一条伪指令。如果立即数在MOV的要求内,那就用一条汇编来实现。如果不在Mov的范围内,就用其它方式实现,如变成两条指令,或从PC偏移地址读一个32位数给寄存器。

MOV是从一个寄存器或者移位的寄存器或者立即数的值传递到另外一个寄存

从本质上是寄存器到寄存器的传递,为什么会有立即数,其实也是有限制的立即数,不是所有立即数都可以传递的

这个立即数要符合一个8位数循环右移偶数位的取值

原因是,MOV本身就是一条32bit指令,除了指令码本身,它不可能再带一个可以表示32bit的数字,所以用了其中的12bit来表示立即数,其中4bit表示移位的位数(循环右移,且数值x2),8bit用来表示要移位的一个基数。

还有一点是关于ldr的,其实ldr可以装载一个32bit立即数的说法并不确切,

因为实际上并不是这一条语句装载了一个32bit立即数,比如

ldr r1,=0x12345678

其实真正的汇编代码是将某个地址的值传递给r1,就是说需要一个地址存放0x12345678这个立即数,实际上可以看作是一条伪指令

而且如果这个立即数可以用mov指令的形式来表达,会被编译器实际用mov 来代替

比如:

ldr r1,=0x10

会变成

mov r1,#0x10

ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str

指令。想把数据从内存中某处读取到寄存器中,只能使用ldr。比如:

ldr r0, 0x12345678

就是把0x12345678这个地址中的值存放到r0中。

而mov不能干这个活,mov只能在寄存器之间移动数据,或者把立即数移动到寄存器中,这个和x86这种CISC架构的芯片区别最大的地方。 x86

中没有ldr这种指令,因为x86的mov指令可以将数据从内存中移动到寄存器中。

另外还有一个就是ldr伪指令,虽然ldr伪指令和ARM的ldr指令很像,但是作用不太一样。ldr伪指令可以在立即数前加上=,以表示把一个地址写

到某寄存器中,比如:

ldr r0, =0x12345678

这样,就把0x12345678这个地址写到r0中了。所以,ldr伪指令和mov 是比较相似的。只不过mov指令限制了立即数的长度为8位,也就是不能超过512。而ldr 伪指令没有这个限制。如果使用ldr伪指令时,后面跟的立即数没有超过8位,那么在实际汇编的时候该ldr伪指令是被转换为mov指令的。

LDR 的两种用法

1)LDR pc, =MyHandleIRQ 表示将MyHandleIRQ符号放入pc寄存器中

2)LDR PC,MyHandleIRQ 表示将读取存储器中MyHandleIRQ符号所表示的地址中的值,及需要多读一次存储器。

LDR R1,=COUNT这条伪指令,是怎样完成将COUNT的地址赋给R1,有兴趣的可以看它编译后的结果。这条指令实际上会编译成一条LDR指令和一条DCD伪指令。

ldr伪指令和ldr指令不是一个同东西。

LDR 和STR——用于字和无符号字节

指令格式:

LDR/STR{cond}{T} Rd,<地址>

LDR/STR{cond}B{T} Rd,<地址>

LDR{cond}{T} Rd,<地址>加载指定地址的字数据到Rd中;

STR{cond}{T} Rd,<地址> 存储Rd中的字数据到指定的地址单元中;

LDR{cond}B{T} Rd,<地址> 指令加载指定地址的字节数据到Rd的的最低字节中(Rd的高24位清零);

STR{cond}B{T} Rd,<地址> 指令存储Rd中的最低字节数据到指定的地址单元中。

T为可选后缀,若有T,那么即使处理器是在特权模式下,存储系统也将访问看成处理器是在用户模式下,T 在用户模式下无效,不能与前索引偏移一起使用T。

地址部分可用的形式有4种:

?零偏移(zero offset) [Rn],Rn的值作为传送数据的地址。如:

LDR R0,[R1];

?前索引偏移(pre-indexed offset) [Rn,Flexoffset]{!}在数据传送之前,将偏移量Flexoffse t加到Rn 中。其结果作为传送数据的存储器地址。若使用后缀“!”,则结果写回到Rn 中,且Rn 不允许是R15,如:

LDRB R0,[R1,#8]

LDR R0,[R1,#8]!

?程序相对偏移(program relative) label(label 必须是在当前指令的土4KB范围内) 。

程序相对偏移是前索引形式的另一种版本。从PC 计算偏移量,并将PC 作为Rn 生成前索引指令,不能使用后缀“!”,如:

LDR R0,place ;

place地址装入R0

?后索引偏移(post-indexed offset) [Rn],Flexoffset。在数据传送后,将偏移量Flexoffset 加到Rn 中,结果写回到Rn,Rn 不允许是R15,如:

LDR R0,[R1],R2,LSL#2 ;

将存储器地址为R1 的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。

偏移量Flexoffset可以是下两种形式之:

1) 取值范围是-4095 到+4095 的整数的表达式,经常是数字常量,如:

STR R5,[R7],#--8

2) 一个寄存器再加上移位(移位由立即数指定),如:

{-}Rm{,shift}

其中:

- :可选负号。若带符号“一”,则从Rn 中减去偏移量。否则,将偏移量加到Rn 中。

Rm :内含偏移量的寄存器。Rm 不允许是R15。

Shift:Rm 的可选移位方法。可以是下列形式的任何一种:

ASR n :算术右移n 位(1<=n<=32)

LSL n :逻辑左移n 位(1<=n<=31)

LSR n :逻辑右移n 位(1<=n<=32)

ROR n :循环右移n 位(1<=n<=31)

RRX :循环右移1 位,带扩展。

AND―――――逻辑"与"操作指令

指令格式:

AND{cond}{S} Rd,Rn,operand2

AND指令将操作数operand2 与Rn 的值按位逻辑"与",结果存放到目的寄存器Rd 中。若设置S,则根据运算结果影响N、Z位,在计算第二操作数时,更新C位,不影响V位(指令ORR、EOR、BIC 对标志位的影响同AND 指令)。

指令示例:

ANDS R1,R1,R2 ;R1=R1&R2,并根据运算的结果更新标志位

AND R0,R0,#0x0F ;R0=R0&0x0F,取出R0最低4位数据。

ORR―――――逻辑"或"操作指令

指令格式:ORR{cond}{S} Rd,Rn,operand2 ORR指令将操作数operand2 与Rn 的值按位逻辑"或",结果存放到目的寄存器Rd 中。指令示例:

ORRS R1,R1,R2 ;R1=R1|R2,并根据运算的结果更新标志位

ORR R0,R0,#0x0F ;R0=R0|0x0F,将R0最低4位置1,其余位不变。

BIC―――――位清除指令

指令格式:

BIC{cond}{S} Rd,Rn,operand2

BIC指令将Rn 的值与操作数operand2 的反码按位逻辑"与",结果存放到目的寄存器Rd 中。指令示例:BIC R0,R0,#0x0F ;将R0最低4位清零,其余位不变。

CMP―――――比较指令

指令格式:

CMP{cond} Rn,operand2

CMP指令用Rn的值减去操作数operand2 ,并将结果的状态(Rn 与operand2比较是大、小、相等)反映在CPSR中,以便后面的指令根据条件标志决定程序的走向。CMP指令与SUBS指令完成的操作一样,只是CMP指令只减,不存结果。

指令示例:

cmp R0,R1 ;比较R0,R1

beq stop ;R0=R1跳到stop

blt less ;R0

.

.

.

Less:

.

.

.

Stop:

.

;GPIO寄存器宏定义GPFCON EQU 0x56000050 GPFDAT EQU 0x56000054 GPFUP EQU 0x56000058

EXPORT LEDTEST

AREA LEDTESTASM,CODE,READONLY ;该伪指令定义了一个代码段,段名为LEDTESTASM,属性只读

LEDTEST

;设置GPF4-GPF7为output

ldr r0,=GPFCON

ldr r1,[r0]

bic r1,r1,#0xff00

orr r1,r1,#0x5500

str r1,[r0]

;禁止GPF4-GPF7端口的上拉电阻

ldr r0,=GPFUP

ldr r1,[r0]

orr r1,r1,#0xf0

str r1,[r0]

looptest

;将数据端口F的数据寄存器的地址附给寄存器r2

ldr r2,=GPFDAT

ldr r3,[r2]

bic r3,r3,#0xf0

orr r3,r3,#0xb0

str r3,[r2];GPF6 output 0

ldr r0,=0x2fffff

bl delay ;调用延迟子程序

ldr r3,[r2]

bic r3,r3,#0xf0

orr r3,r3,#0x70

str r3,[r2];GPF7 output 0

ldr r0,=0x2fffff ;初始计数值

bl delay ;调用延迟子程序

ldr r3,[r2]

bic r3,r3,#0xf0

orr r3,r3,#0xd0

str r3,[r2];GPF5 output 0

ldr r0,=0x2fffff

bl delay ;调用延迟子程序

ldr r3,[r2]

bic r3,r3,#0xf0

orr r3,r3,#0xe0

str r3,[r2];GPF4 output 0

ldr r0,=0x2fffff

bl delay ;调用延迟子程序

b looptest

delay

sub r0,r0,#1 ;r0=r0-1

cmp r0,#0x0 ;将r0的值与0相比较

bne delay ;比较的结果不为0(r0不为0),继续调用delay,否则执行下一条语句

mov pc,lr ;返回

END ;程序结束符

相关主题
相关文档
最新文档