说说ARM汇编的LDR伪指令

发布者:浅唱清风最新更新时间:2016-04-20 来源: eefocus关键字:ARM  汇编  LDR伪指令 手机看文章 扫描二维码
随时随地手机看文章
我们知道ARM CPU中有一条被广泛使用的指令LDR,它主要是用来从存储器(确切地说是地址空间)中装载数据到通用寄存器。但不论是ARMASM还是GNU ARM AS,都提供了一条与之同名的伪指令LDR,而在实际中使用该伪指令的情况也较多,那他们有什么不同呢?下面我谈谈我的理解。


由于我使用GNU工具链,所以以下的内容都以GNU AS的ARM语法为准。

LDR伪指令的语法形式如下:
LDR , =

这个常量表达式中可以包含Label(在ARM汇编中Label会在连接时解释为一个常数),且其中的常数前不加#符号。

范例demo.s:

.equ STACK_BASE, 0x0c002000
.equ STACK_SIZE, 0x00001000

.text
ldr sp, = STACK_BASE
ldr sl, = STACK_BASE - STACK_SIZE
ldr pc, = entry

这是一个合法的汇编文件,它把堆栈基址设为0x0c002000,栈限设为0x0c001000,然后跳到entry所标识的C程序中执行。

下面我们假设符号“entry”的地址为0x0c000000。

我们如果把上面代码写成:
.text
mov sp, #0x0c002000
mov sl, #0x0c001000
mov pc, #0x0c000000

汇编器会报错:
demo.s: Assembler messages:
demo.s:2: Error: invalid constant -- `mov sp,#0x0c002000'
demo.s:3: Error: invalid constant -- `mov sl,#0x0c001000'

说起这个错误的原因可就话长了,简而言之是因为RISC有一个重要的概念就是所有指令等长。在ARM指令集中,所有指令长度为4字节(Thumb指令是2字节)。那问题就来了,4字节是不可能同时存的下指令控制码和32位立即数的,那么我要把一个32位立即数(比如一个32位地址值)传送给寄存器该怎么办?

RISC CPU提供一个通用的方法就是把地址值作为数据而不是代码,从存储器中相应的位置读入到寄存器中,待会我们会看到这样的例子。

此外ARM还提供另一种方案。由于传送类指令的指令控制码部分(cond, opcode, S, Rd, Rn域)占去了20个字节,那能提供给立即数的就只剩12个位了。

ARM并未使用这12个位来直接存一个12位立即数,而是使用了类似有效数字一样的概念,只存8个字节的有效位和一个4位的位偏移量(偏移单位为2)。这个东西在ARM被叫做术语immed_8,有兴趣的人可以找资料了解一下,到处都有介绍。

可以看出ARM的这个方法能直接使用的立即数是相当有限的,像0xfffffff0这样的数显然无法支持。别着急,ARM的传送类指令中还有一个MVN指令可以解决该问题。显然0x0000000f是一个有效立即数,MVN会先将其取反再传送,这样有效立即数的范围又扩充了一倍。

可就算如此仍有大量的32位立即数是无效的,比如上面那个例子中的0x0c002000和0x0c001000。面对这种问题一是使用RISC的通用方法,二是分次载入。

比如可以这样载入0x0c002000:
.text
mov sp, #0x0c000000
add sp, sp, #0x00002000
或者:
.text
mov sp, #0x0c000000
orr sp, sp, #0x00002000

感觉很狡猾是吧,呵呵。但是要注意它和方法一的一大区别:需要多条指令。那么在一些对指令数目有限制的场合就无法使用它,比如异常向量表处要做长跳转(超过±32MB)的话就只能用方法一;还有就是在同步事务中该操作不是原子的,因此可能需要加锁。

扯了这么多再回到LDR伪指令上来。显然上面的内容是复杂繁琐的,如果然程序员在写程序的时候还要考虑某个数是不是immed_8一定超级麻烦,因此为了减轻程序员的负担才引入了LDR伪指令。

你一定很好奇第一段代码demo.s被GNU AS变成了什么,好,让我们在Linux环境下执行下面的命令:
arm-elf-as -o demo.o demo.s
arm-elf-objdump -D demo.o

结果:
demo.o: file format elf32-littlearm

Disassembly of section .text:

00000000 <.text>:
0: e59fd004 ldr sp, [pc, #4] ; c <.text+0xc>
4: e59fa004 ldr sl, [pc, #4] ; 10 <.text+0x10>
8: e59ff004 ldr pc, [pc, #4] ; 14 <.text+0x14>
c: 0c002000 stceq 0, cr2, [r0]
10: 0c001000 stceq 0, cr1, [r0]
14: 00000000 andeq r0, r0, r0
Disassembly of section .data:


由于entry还没连上目标地址,objdump反汇编会认为是0,我们先不管它。另外两条LDR伪指令变成了实际的LDR指令!但目标很奇怪,都是[pc, #4]。那好我们看看[pc, #4]是什么。

我们知道pc中存放的是当前指令的下下条指令的位置,也就是. + 8。那么上面的第一条指令ldr sp, [pc, #4]中的pc就是0x8,pc + 4就是0xc,而[0xc]的内容正是0x0c002000;同理,第二条ldr指令也是如此。显然这里LDR伪指令采用的是RISC通用的方法。

另外要说的是,如果LDR的是一个immed_8或者immed_8的反码数,则会直接被解释成mov或mvn指令。如ldr pc, = 0x0c000000会被解释成mov pc, 0x0c000000。

最后一点补充,我发现arm-elf-gcc通常都用累加法。如C语句中的i = 0x100ffc04;会变成类似于以下的语句:
mov r0, #0x10000004
add r0, r0, #0x000ff000
add r0, r0, #0x00000c00
...
原因不详。

 

添加的内容:

1 指令LDR


 

应用举例:


 

u LDR R0, [R1, #4] ;将内存单元R1+4中的字读取到R0寄存器


 

其中,R1为基址,#4为偏移地址,R0为目标地址。注意,此时不更新R1。


 

u LDR R0, [R1, #-4] ;将内存单元R1-4中的字读取到R0寄存器


 

u LDR R0, [R1, #4]! ;将内存单元R1+4中的字读取到R0寄存器。同时更新R1,R1=R1+4。


 

u LDR R0, [R1], #4 ;将地址为R1的内存单元数据读取到R0寄存器,然后R1=R1+4。


 

2 伪指令LDR


 

ARM中的伪指令不是真正的ARM指令或者Thumb指令,这些伪指令在汇编编译器对源程序进行汇编处理时,被替换为相应的ARM或者Thumb指令(序列)。


 

LDR伪指令将一个32位的常数或者一个地址值读取到寄存器中。


 

语法格式:


 

LDR{cond} register, =[expr | label-expr]


 

其中,register为目标寄存器


 

expr为32位的常量。编译器将根据expr的取值情况,如下处理LDR伪指令:


 

u 当expr所表示的地址值没有超过MOV或MVN指令中的地址取值范围时,编译器用合适的MOV或者MVN指令代替LDR伪指令。


 

应用举例:


 

将0xFF0读取到R1中


 

LDR R1, =0xFF0


 

汇编后得到:


 

MOV R1, 0xFF0


 

 


 

u 当expr表示的地址值超过了MOV或者MVN指令中的地址的取值范围(第二操作数的取值范围)时,编译器将该常数放在数据缓冲区中,同时用一条基于PC的LDR指令读取该常数。


 

LDR R1,=0xFFF


 

汇编后得到:


 

LDR R1, [PC, OFFSET_TO_LPOOL]


 


 

LPOOL DCD 0xFFF


 

关于label-expr的介绍我不是很理解。不理解其中关于“连接重定位伪操作”。(P144)


 

声明:本文为我在学习杜春雷编著的《ARM体系结构与编程》时做的总结笔记,文中摘录了书中的很多内容。

 

补充2:

 

原文地址:http://hi.baidu.com/andylgh/blog/item/17dbdc1f7d102a62f624e4dc.html

 

gnu下的arm汇编伪指令:.word说明
2007-04-08 20:14

 

说说这个.word的作用。

word expression就是在当前位置放一个word型的值,这个值就是expression
举例来说,
_rWTCON:
.word 0x15300000
就是在当前地址,即_rWTCON处放一个值0x15300000

翻译成intel的汇编语句就是:
_rWTCON dw 0x15300000

例如:

ldr r1, _rWTCON

_rWTCON:
.word 0x15300000
不是把地址0x1530 0000 上的内容传递到r1,是把地址_rWTCON上的内容放到r1,而地址_rWTCON上的内容是0x15300000。实际上就是把r1设置为0x15300000


关键字:ARM  汇编  LDR伪指令 引用地址:说说ARM汇编的LDR伪指令

上一篇:GNU ARM汇编入门
下一篇:S3C2440 clock 工作原理

推荐阅读最新更新时间:2024-03-16 14:51

ARM7串口编程要点
一. 串口编程的硬件原理 1. 串口特性: 1 16字节接收FIFO和16字节发送FIFO 2 接收FIFO触发点可设置为1,4,8或14字节。 3 内置波特率发生器。 2. UART0引脚: 1 RxD0 引脚用于UART0接受数据,接受方式为串行输入。 2 TxD0引脚用于UART0发送数据,发送方式为串行发送数据。 3. UART0的结构和工作方式 先看图在说明: 1 VPB总线提供CPU与UART0之间得的通信连接 (CPU内核通过VPB接口对UART0的寄存器进行读写访问.) 2 UART0 接收器模块监视串行输入线RxD0的有效输入。UART0 接收单元的移位寄存器(U0RSR)通过RxD0接收有效的字符。当U0RS
[单片机]
<font color='red'>ARM</font>7串口编程要点
ARM笔记:外部按键中断程序
.extern main .text .global _start _start: b Reset HandleUndef: b HandleUndef HandleSWI: b HandleSWI HandlePrefetchAbort: b HandlePrefetchAbort HandleDataAbort: b HandleDataAbort HandleNotUsed: b HandleNotUsed b HandleIRQ HandleFIQ: b HandleFIQ Reset: ldr sp,=4096 bl dis
[单片机]
STM32(一) 汇编操作GPIO
平台:keil4.72 开发板:stm32F103 72M 测试代码: 汇编: __asm void asm_LED1_TOGGLE(void) { ;GPIOA_ODR EQU 0x4001080C| LDR R0, = 0x4001080C LDR R1, ;R1 = *R0 EOR R1,#0x0100 ;r1 = r1^0x0100 STR R1, ;*R0 = R1 BX LR ; } 对应的C void c_LED1_TOGGLE(void) { GPIOA- ODR ^= GPIO_Pin_8; } 测试用主函数 int main(v
[单片机]
STM32(一) <font color='red'>汇编</font>操作GPIO
ARM裸板程序的开发和编译(以点亮LED灯为例)
制作好交叉编译工具链后,怎么确定是否有效呢,当然是编译一个程序烧写到板上跑一下了。这里我们以点亮LED灯为例描述如何使用我们制作的交叉编译工具链编译裸板程序。 首先,需要了解下Nor flash,Nand flash,片内内存,片外内存,地址空间这几个概念。其中,Nor flash是CPU可以直接通过硬件信号读取的,不需要配置,但是Norflash的写操作是需要配置和实现驱动的。片内内存不需要配置,CPU可以直接通过硬件信号读写。这是因为Nor flash和片内内存在硬件设计上就已经固化了他们的电气特性。而Nand flash的读写都必须配置,并且需要实现驱动。CPU通过相应的Nand flash控制器间接的读写数据。片外内存
[单片机]
<font color='red'>ARM</font>裸板程序的开发和编译(以点亮LED灯为例)
51单片机汇编语言实验(一)-----数字量输入输出
一、实验目的:    学习单片机IO口的输入输出操作 二、实验设备:    PC计算机一台,Dais-52PRO+实验系统一套。 三、实验内容:    1. P1口I/O实验    2. P1口流水灯实验 四、P1口I/O实验:    1、实验原理:    数字量从P1口的低4位输入,从P1口的高4位输出控制发光二极管的亮灭,输入与输出一一对应。    2、实验步骤:   ①将试验箱IO区的P1.0 到P1.3与拨码开关区的K0~K3按下图连线;   ② 将试验箱IO区的P1.4 到P1.7与LED区的L0~L3按下图连线;   ③ 编写程序,经编译、链接无语法错误后装载到实验;   ④ 运行程序,拨动K0到K3,观察L0~L3的
[单片机]
51单片机<font color='red'>汇编</font>语言实验(一)-----数字量输入输出
arm汇编adrl(以及ldr与adr的区别)
看《嵌入式linux应用开发》第六章实例中看到个句: adrl r2,men_cfg_val 最初对adr1相当不解,后来发现,那个不是数字1,而是字母l(认真看头部,有点区别的) 这里记录下ADRL的用法: 功能:将相对于程序或相对于寄存器的地址载入寄存器中。 与 ADR 指令相似。ADRL 生成两个数据处理指令,因此它比 ADR 加载的地址范围要宽。 语法 ADRL{cond} Rd,label 其中: cond:是一个可选的条件代码。Rd:是要加载的寄存器。label:是相对于程序或寄存器的表达式。 上面给出的范围是相对于位于当前指令地址后的、距离当前指令四个字节(在 Thumb 代码中)或两个字(
[单片机]
学习心得:怎样高效学习嵌入式系统知识
1、学习linux   根据我在论坛的了解,我选择学习嵌入式linux,刚好我们学校也重视嵌入式linux,从实验室到课程安排都是关于嵌入式linux方面,天时地利!这里我把学习linux的经验和教训说说。     可以这样说,在论坛里说道学习linux差不多就学习linux内核。于是我电脑里安装了linux就开始看linux内核方面的书了。我记得来学校以前就买到一本陈莉君的讲linux内核的第一版,现在有第二版了。我就开始看那本说,大家说linux内核情景分析不错,我就买了上下册,后来又买了 《深入理解linux内核》,最后也买了毛德操的《嵌入式系统》也是分析linux内核代码的,主要讲arm相关的。     看内核期间是个
[嵌入式]
基于ARM的RFID中间件系统设计
引 言   RFID中间件在RFID读写器和应用程序之间起桥梁作用。应用程序端使用中间件所提供一组通用的应用程序接口(API),即能连到RFID读写器,采集RFID标签数据。即使存储RFID标签情报的数据库软件或后端应用程序增加或改由其他软件取代,或者读写 RFID读写器种类增加等情况发生时,应用端不需修改也能处理,省去多对多连接的维护复杂性问题。嵌入式RFID中间件在RFID的大规模应用中将扮演重要的角色。在具体应用部署过程中,会有大量的嵌入式设备充当边缘中间件的硬件平台。对于众多有意从事中高端RFID读写器开发的硬件厂商而言,借助嵌入式 RFID中间件迅速实现硬件集成基础之上的软件集成,是帮助硬件厂商快速升级产品系列,满足企
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved