ARM伪指令详述
- 格式:doc
- 大小:73.00 KB
- 文档页数:13
`load_start`, `load_end`, 和 `run_start` 是 ARM 汇编伪指令,通常用于描述一个代码段的加载和运行开始/结束位置。
这些伪指令通常在嵌入式系统或低级系统编程中使用,以帮助链接器或加载器确定如何加载和运行代码。
1. load_start: 这个伪指令标记了代码段的开始位置,这个位置是在程序被加载到内存中时确定的。
这个标签通常用于确定程序在内存中的基地址。
2. load_end: 这个伪指令标记了代码段的结束位置。
这个标签可以帮助确定程序的大小,从而可以在加载时为其分配足够的内存。
3. run_start: 这个伪指令标记了代码段的运行开始位置。
这通常是在程序开始执行之前,由链接器或加载器确定的地址。
这个地址通常会被用作程序的入口点。
这些伪指令通常在链接脚本中使用,链接脚本是用来描述如何链接程序的各个部分的文件。
通过使用这些伪指令,链接器或加载器可以确定程序在内存中的位置,并正确地执行它。
需要注意的是,这些伪指令的行为可能会根据不同的链接器和加载器而有所不同,因此具体行为可能需要参考相关工具的文档或手册。
ARM汇编语言伪指令ARM汇编语言伪指令ARM中伪指令不是真正的ARM指令或者Thumb指令,这些伪指令在汇编编译时对源程序进行汇编处理时被替换成对应的ARM或Thumb指令(序列)。
ARM伪指令包括ADR、ADRL、LDR和NOP等。
1、ADR(小范围的地址读取伪指令)该指令将基于PC的地址值或基于寄存器的地址值读取到寄存器中。
语法格式ADR{cond} register, expr其中,cond为可选的指令执行的条件register为目标寄存器expr为基于PC或者基于寄存器的地址表达式,其取值范围如下:当地址值不是字对齐时,其取值范围为-255~255.当地址值是字对齐时,其取值范围为-1020~1020当地址值是16字节对齐时,其取值范围将更大在汇编编译器处理源程序时,ADR伪指令被编译器替换成一条合适的指令。
通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能。
因为ADR伪指令中的地址是基于PC或者基于寄存器的,所以ADR读取到的地址为位置无关的地址。
当ADR伪指令中的地址是基于PC时,该地址与ADR伪指令必须在同一个代码段中。
示例start MOV r0,#10 ;因为PC值为当前指令地址值加8字节ADR r4, start ;本ADR伪指令将被编译器替换成SUB r4,pc,#0xc2、ADRL(中等范围的地址读取伪指令)该指令将基于PC或基于寄存器的地址值读取到寄存器中。
ADRL伪指令比ADR伪指令可以读取更大范围的地址。
ADRL伪指令在汇编时被编译器替换成两条指令,即使一条指令可以完成该伪指令的功能。
语法格式ADRL{cond} register,expr示例start MOV r0,#10 ;因为PC值为当前指令地址值加8字节ADRL r4,start+60000 ;本ADRL伪指令将被编译器替换成下面两条指令ADD r4,pc,#0xe800ADD r4,r4,#0x2543、LDR(大范围的地址读取伪指令)LDR伪指令将一个32位的常数或者一个地址值读取到寄存器中语法格式LDR{cond} register, =[expr|label-expr]其中,expr为32位的常量。
ARM汇编指令集汇编指令集的介绍,包括指令和伪指令。
指令和概念指令指令指的是CPU机器指令的助记符,是由CPU的指令集提供的,经过编译之后,会以机器码的形式由CPU读取执⾏伪指令伪指令本质上不是指令,和CPU的机器指令没有任何关系,只是和指令⼀起写在代码中⽽已,是由环境提供的,其⽬的是⽤于指导编译过程,伪指令经过编译后不会⽣成⼆进制机器码,仅仅在编译阶段有效果指令编程风格ARM官⽅风格官⽅风格指令⼀般使⽤⼤写,例如:LDR R0,[R1],Windows中常使⽤这种风格GUN Linux风格指令⼀般使⽤⼩写字母,例如:ldr r0,[r1],Linux环境中常⽤这种风格ARM汇编特点LDR/STR架构1. 采⽤RISC架构,CPU本⾝不能直接读取内存,⽽需要把内存中的数据加载到CPU的通⽤寄存器中,才能被CPU处理2. ldr(load register)将内存中的数据加载到通⽤寄存器3. str(store register)将寄存器内容存⼊内存空间4. ldr和str组合,可以实现ARM CPU和内存的数据交换8种寻址⽅式1. 寄存器寻址:move r1,r2:把r2的值赋值到r1寄存器中2. ⽴即寻址:move r0,#0xFF00:把⽴即数0xFF00赋值给r0寄存器3. 寄存器移位寻址:move r0,r1,lsl #3:把r1左移三位(*8)之后的值赋值给r0寄存器4. 寄存器间接寻址:ldr r1,[r2]:寄存器有中括号,表⽰内存地址对应的数据,所以这⾥r2表⽰⼀个内存地址,[]表⽰取r2指针对应的数据,这句代码的意思是把r2对应的内存中的数据赋值给r15. 基址变址寻址:ldr r1,[r2,#4]:将指针r2的值(内存地址)+4之后指向的数据赋值给r16. 多寄存器寻址:ldmia r1!,{r2 - r7,r12}:这种情况下,r1是⼀个指针,⾥边存放的内存地址,然后以r1⾥边的内存地址为基地址,向后以此加1得到{}⾥的寄存器数量个内存地址,然后将刚才得到的这些内存地址指向的变量的值赋值给{}⾥的对应位置的寄存器,类似从内存中读取数组,然后把数组的元素依次赋值给这些寄存器7. 堆栈寻址:stmfd sp!,{r2 - r7,lr}:和多寄存器类似,区别是将栈SP中连续访问{}数量个字节,然后依次赋值给{}⾥的寄存器8. 相对寻址:beq flag::flag:标号⽤于标记标号后⾯那句指令的地址,常⽤来表⽰⼊⼝点,函数名就是⼀个标号,C语⾔中的goto就可以跳转到⼀个标号,在ARM汇编中⽤指令b flag:就可以跳转到flag:对应的标号处执⾏,和beq flag:是⼀样的,其原理是相对于PC程序位置寄存器做⼀个偏移指令后缀1. ARM中的指令可以带后缀,从⽽丰富该指令的功能,这种形式叫做指令族,常⽤的后缀有:2. B(byte):功能不变,操作长度变为8位(依赖CPU位数,以下相同)3. H(Halfword):功能不变,操作长度变为16位3. H(Halfword):功能不变,操作长度变为16位4. S(signed):功能不变,操作数变为有符号数5. S(S标识):影响CPSR⾥的NZCV标识位,6. 举例:1. ldr指令族:ldrb,ldrh,ldrsb ldrsh,从内存中加载指定长度的数据2. mov指令族:movs r0,#0,结果是0,赋值会影响CPSR的NZCV标识,将Z位置为1条件执⾏后缀1. 条件执⾏后缀⽤于限制该执⾏执⾏的,只有在符合条件之后才能够执⾏该指令2.3. 举例:moveq r0,r1,如果eq成⽴,执⾏mov r0,r1,不成⽴则该条不执⾏,和C语⾔中的条件判断类似4. 条件后缀成⽴与否,不是取决于本条指令,⽽是取决于之前指令运⾏后的结果5. 条件后缀决定了本条指令是否执⾏,不会影响之前和之后指令6. 条件后缀和CPSR的NZCV位相关,例如,如果上⼀句代码执⾏的结果将Z置为1,下⼀句带有eq条件后缀的语句就会被执⾏多级指令流⽔线1. 多级流⽔线⽤于增加处理器处理指令的速度,2. 允许CPU同时异步的执⾏多条指令,⽽⾮上⼀条指令全部执⾏完毕之后才会执⾏下⼀条指令3. 多级可以简单那理解为把⼀条指令分为多个步骤来异步执⾏,例如:1. CPU把⼀条指令分为[取址,解码,执⾏]3个步骤,则为3级指令流⽔线2. 第⼀条指令进⾏取值操作3. 第⼀条指令取值完毕,进⼊解码操作,第⼆条指令紧随其后就开始执⾏取值操作4. 第⼀条指令解码完毕,进⼊执⾏操作,第⼆条指令紧接着进⼊解码操作,同时第三条指令进⼊取值操作5. 第⼀条指令执⾏完毕,第⼆条指令进⼊执⾏操作,第三条指令进⼊解码操作,第四条指令进⼊取值操作,依次类推4. 可见,多级流⽔线可以提⾼同时执⾏指令的数量,从⽽加速指令执⾏5. 需要注意的是,PC指向的是正在取值的指令,⽽⾮正在执⾏的指令,之间的差值就是流⽔线级数和单字节长度的乘积,在中断返回到PC的时候需要注意这个问题ARM指令数据处理指令数据传输指令mov:move,在两个寄存器之间或者⽴即数和寄存器之间传递数据,将后⼀个寄存器上的值或者⽴即数赋值给前⼀个寄存器 例如:mov r1,r0mov r1,#0xFF:将⽴即数0xFF赋值给寄存器r1mvn:和mov⽤法⼀致,区别是mvn会把后⼀个寄存器的值或者⽴即数按位取反后赋值给前⼀个寄存器 例如:mvn r0,#0xFF,则r0的值为0xffffff00(32位数据)算术运算指令add:加法运算sub:减法运算rsb:反减运算adc: 带进位的加法运算sbc: 带进位的减法运算rsc:带进位的反减指令逻辑指令and:与操作orr:或操作eor:异或操作bic:位清除操作⽐较指令cmp:⽐较⼤⼩cmn:取反⽐较tst:按位与运算teq:按位异或运算乘法指令mvl: mla: umull: umlal: smull: smlal:前导0计数clz:统计⼀个数的⼆进制位前⾯有⼏个0CPSR访问指令mrs⽤于读取CPSR和SPSRmsr⽤于写CPSR和SPSRCPSR和SPSRCPSR是程序状态寄存器,整个Soc只有⼀个SPSR在五种异常模式下各有⼀个,⽤于从普通模式进⼊异常模式的时候,保存普通模式下的CPSR,在返回普通模式时可以恢复原来的CPSR跳转分⽀指令b指令: ⽆条件直接跳转,没打算返回bl指令:跳转前把返回地址放⼊lr中,以便返回,常⽤在函数中bx指令:跳转同时切换到ARM模式,⽤于异常处理的跳转内存访问指令ldr:加载指定内存地址的数据到寄存器,按照字节访问str:加载指定寄存器数据到内存地址中,按照字节访问ldm:和ldr功能⼀样,⼀次多字节多寄存器访问stm:和str功能⼀样,⼀次多字节多寄存器访问swp:内存和寄存器互换指令,⼀边读⼀边写,例如:swp r1,r2,[r0]:读取指针r0的数据到r1中,同时把r2的数据赋值给r0指针指向的变量软中断指令swi(software interrupt),在软件层模拟产⽣⼀个中断,这个中断会传送给CPU,常⽤于实现系统调⽤⽴即数⾮法与合法ARM指令都是32为,除了指令标记和操作标记外,只能附带少位数的⽴即数,所以有⾮法与合法之分⾮法⽴即数:合法⽴即数:经过任意位数的移位后,⾮0部分可以⽤8位表⽰就是合法⽴即数协处理器与指令协处理器协处理器属于Soc中另外⼀颗核⼼,⽤于协助主CPU实现某些功能,被主CPU调⽤来执⾏任务,协处理器和MMU,Cache,TLB有功能和管理上的联系ARM设计可以⽀持多达16个协处理器,但是⼀般只实现其中的CP15协处理器指令mrc:读取CP15中的寄存器mcr:向CP15中的寄存器写数据指令⽤法:mcr{<”cond”>} p15,<”opcode_1”>,<”Rd”>,<”Crn”>,<”Crm”>,{<”opcode_2”>} opcode_1:对于CP15永远为0Rd:ARM通⽤寄存器Crn:CP15寄存器,取值范围c0~c15Crm:CP15寄存器,⼀般为c0opcode_2:省略或者为0ldm,stm和栈ldm,stmldr与str只能访问4个字节,当数据较⼤的时候,就会明显的降低效率,这时就需要使⽤到ldm和stm,ldm与stm是⼤量的从寄存器与内存交换数据的⽅式,常⽤于在内存和寄存器之间⼤量读取和写⼊数据:stmia sp {r0 - r12}:stm表⽰进⾏批量数据操作,ia的意思是将r0存⼊SP的内存地址处,然后SP内存地址+4(32位),将r1存⼊该地址,内存地址再+4,存⼊r2,依次存到r12,这就是⼀个寄存器和内存交换⼤量数据的⽰例,在⼀个周期内完成了多个内存地址和多个寄存器的操作。
neon指令集伪指令
Neon指令集是一种SIMD(单指令多数据)扩展指令集,用于ARM架构的处理器。
它提供了一组适用于多媒体和信号处理应用的指令,可以同时对多个数据进行并行处理。
以下是Neon指令集的一些常见指令:
1. 加载和存储指令:
- VLDx:从内存加载数据到寄存器
- VSTx:将寄存器中的数据存储到内存
2. 算术和逻辑指令:
- VADD:向量加法
- VSUB:向量减法
- VMUL:向量乘法
- VDUP:向量复制
- VORR:向量逻辑或
- VEOR:向量逻辑异或
- VAND:向量逻辑与
3. 数据处理指令:
- VDUP:向量复制
- VMOV:向量移动数据
- VCEQ:向量比较相等
- VDUP:向量复制
4. 乘法和除法指令:
- VMUL:向量乘法
- VMLA:向量乘累加
- VMLX:向量乘累加加法
- VDIV:向量除法
5. 数据转换指令:
- VCVT:向量转换数据类型
- VZIP:向量交错排列
此外,Neon还包含一些伪指令,用于控制指令执行顺序和向量操作的设置。
这些伪指令在汇编或高级语言编译成机器码时会被解释器忽略。
常见的Neon伪指令包括:
- .req:定义寄存器或寄存器别名
- .fpu:设置浮点处理单元
- .cpu:定义处理器架构和扩展特性
- .align:对齐内存地址
- .include:在汇编代码中引用其他文件
请注意,上述列举的指令和伪指令仅是一小部分Neon指令集的示例,实际使用时可以根据具体的应用需求选择适当的指令。
1、ASSERT :DEF:ENDIAN_CHANGEASSERT 是断言伪指令,语法是:ASSERT +逻辑表达式def 是逻辑伪操作符,格式为::DEF:label,作用是:判断label是否定义过ARM 伪指令ARM 汇编程序的由机器指令,伪指令和宏指令组成。
伪指令不像机器指令那样在处理器运行期间由机器执行,而是汇编程序对源程序汇编期间由汇编程序处理。
在前面的指令集章节中,我们已经接触了几条常用到的伪指令,如ADR 、ADRL、LDR、NOP 等,把它们和指令集一起介绍是因为它们在汇编时会被合适的机器指令代替,实现真正机器指令操作。
宏是一段独立的程序代码,它是通过伪指令定义的,在程序中使用宏指令即可调用宏。
当程序被汇编时,汇编程序将对每个调用进行展开,用宏定义取代源程序中的宏指令。
1 符号定义伪指令符号定义伪指令用于定义ARM 汇编程序的变量,对变量进行赋值以及定义寄存器名称,该类伪指令如下:全局变量声明:GBLA、GBLL 和GBLS。
局部变量声明:LCLA、LCLL 和LCLS。
变量赋值: SETA、SETL 和SETS。
为一个通用寄存器列表定义名称:RLIST。
为一个协处理器的寄存器定义名称:CN。
为一个协处理定义名称: CP。
为一个VFP 寄存器定义名称:DN 和SN。
为一个FPA 浮点寄存器定义名称:FN。
GBLA、GBLL、GBLS全局变量声明伪指令。
GBLA 伪指令用于声明一个全局的算术变量,并将其初始化为0。
GBLL 伪指令用于声明一个全局的逻辑变量,并将其初始化为{FALSE}。
GBLS 伪指令用于声明一个全局的字符串变量,并将其初始化为空字符串“”。
伪指令格式:GBLA variableGBLL variableGBLS variable其中:variable 定义的全局变量名,在其作用范围内必须惟一。
全局变量的作用范围为包含该变量的源程序。
伪指令应用举例如下:GBLL codedbg ;声明一个全局逻辑变量codebg SETL {TRUE} ;设置变量为{TRUE}…LCLA、LCLL、LCLS局部变量声明伪指令,用于宏定义的体中。
arm export伪指令英文回答:The `arm export` pseudo-instruction is used to export symbols from an ARM assembly file. This allows the symbolsto be used by other modules, such as shared libraries or other assembly files.The `arm export` pseudo-instruction takes the following syntax:arm export symbol [, symbol] ...where `symbol` is the name of the symbol to be exported. Multiple symbols can be exported by separating them with commas.The `arm export` pseudo-instruction must be placed in the global scope of the assembly file, outside of any functions or sections.For example, the following assembly file exports the `my_symbol` symbol:arm export my_symbol..text..global my_symbol.my_symbol:mov r0, #1。
mov pc, lr.This assembly file can then be used by other modules, such as the following shared library:.text..global my_shared_library_function.my_shared_library_function:ldr r0, =my_symbol.blx r0。
ARM汇编伪指令在ARM汇编语言源程序中有些特殊助记符,它们没有相对应的操作码或者机器码,通常称为伪指令,它们所完成的操作称为伪操作。
伪指令在源程序中的作用是为完成汇编程序作各种准备工作的,由汇编程序在源程序的汇编期间进行处理,仅在汇编过程中起作用。
在ARM的汇编程序中,有以下几种伪指令:符号定义伪指令、数据定义伪指令、汇编控制伪指令、宏指令以及其他伪指令。
一、符号定义伪指令作用:用于定义ARM汇编程序中的变量、对变量赋值以及定义寄存器的别名等。
符号定义有如下几种伪指令:用于定义局部变量的LCLA、LCLL和LCLS。
用于定义全局变量的GBLA、GBLL和GBLS。
用于对变量赋值的SETA、SETL和SETS。
为通用寄存器列表定义名称的RLIST。
(1)LCLA、LCLL和LCLS格式:LCLA/LCLL/LCLS 局部变量名说明:LCLA、LCLL和LCLS伪指令用于定义一个汇编程序中的局部变量并初始化。
其中:LCLA定义一个局部的数字变量,初始化为0。
LCLL定义一个局部的逻辑变量,初始化为F。
LCLS定义一个局部的字符串变量,初始化为空串。
这3条伪指令用于声明局部变量,在其局部作用范围内变量名必须惟一,例如在宏内。
例:LCLA num1 ;定义一个局部数字变量,变量名为num1 LCLL I2 ;定义一个逻辑变量,变量名为I2LCLS str3 ;定义一个字符串变量,变量名为str3num1 SETA 0xabcd ;将该变量赋值为0xabcdI2 SETL {FALSE} ;将该变量赋值为真str3 SETS “HELLO”;将该变量赋值为“HELLO”(2)GBLA、GBLL和GBLS格式:GBLA/GBLL/GBLS 变量名说明:GBLA、GBLL和GBLS伪操作定义一个汇编程序中的全局变量并初始化。
其中:GBLA定义一个全局数字变量,并初始化为0。
GBLL定义一个全局逻辑变量,并初始化为F。
PS:在u-boot源码时遇到_armboot_start、_bss_start等这些变量,不知道指向什么地址,于是查了一下,弄清了ARM汇编中“.word”这个伪指令是什么意思了,感觉自己很菜。
借鉴一下网友帖子的内容,关键在帖子最后的总结:汇编和C引用变量的不同:汇编是“绝对”引用,即没有指针的概念,引用得到的就是值;c语言是“间接”引用,相当于指针的概念,引用地址变量,得到的就是该变量所指的内容值。
感谢原作者,以下为原帖:aaronwong: u-boot中代码的疑问(_armboot_start与_start)?---------------------------我使用的是u-boot-1.3.0-rc2。
在cpu/pxa/start.S中,有如下的标号定义:_TEXT_BASE:.word TEXT_BASE /*uboot映像在S DRAM中的重定位地址,我设置为0xa170 0000 */.globl _armboot_start_armboot_start:.word _start /*_start是程序入口,链接完毕它的值应该是0xa170 0000=TEXT_BASE*//* 这句话的意思应该是在_armboot_start标号处,保存了_start的值,也就是说,_armboot_start 是存放_start的地址,该地址对应的存储单元内容是0xa170 0000*//** These are defined in the board-specific linker script. 下面的定义与上面应该是一个意思。
*/.globl _bss_start_bss_start:.word __bss_start======================按照上面的理解,__bss_start是uboot 的bss段起始地址,那么uboot映像的大小就是__bss_start - _start;在relocate代码段中计算uboot的大小时,也体现了这一点。
ARM异常向量表中LDR指令、LDR伪指令的来龙去脉于指令中包含的立即数,也可以来源于一个内存位置存放的内容。
当要赋予的值可以用ARM 指令的立即数表示时,LDR 伪指令用MOV 指令代替,否则LDR 伪指令用LDR 指令代替。
LDR 伪指令的一般形式:LDRR0,=0x01LDRPC,=labelLDR R0,=0x01 这条伪指令可以用MOV 指令代替,因为要赋予的常数0x01可以包含在MOV 指令的立即数中。
而LDR PC, =label 则不一定是用MOV 指令来实现,因为label 是一个地址常数,通过指令中12bit 立即数移位的方式不一定能形成。
若MOV 指令不能实现,则用LDR 指令来实现,实现的过程如下图:即先使用PC+偏移量作为地址访问内存,将此地址的内存加载到目标寄存器中(PC 寄存器)。
当然,指定地址处的内存必须包含正确的值,编译器自动在这个地址中写入正确的label 值。
所以,LDR 伪指令可以实现将任何32bit 的数装入目标寄存器,并且在伪指令LDR PC, =label 中执行之后,PC 的值就是label 的值。
4、回到异常向量表Vector: ; All default exception handlers (except reset) are ; defined as weak symbol definitions. ; If a handler is defined by the application it will take precedence. LDR pc, =resetHandler ; Reset LDR pc, Undefined_Addr ; Undefined instructions LDR pc, SWI_Addr ; Software interrupt (SWI/SYS) LDR pc, Prefetch_Addr ; Prefetch abort LDR pc, Abort_Addr ; Data abort B . ; RESERVED LDR pc, IRQ_Addr ; IRQ LDR。
MACRO伪操作标识宏定义的开始,MEND标识宏定义的结束。
用MACRO 及MEND定义一段代码,称为宏定义体,这样在程序中就可以通过宏指令多次调用该代码段语法格式MACRO{$label} macroname {$parameter {,$parameter}...};code...;codeMEND其中:$labelz在宏指令被展开时,label可被替换成相应的符号,通常是一个标号。
在一个符号前使用$标识程序被汇编时将使用相应的值来替代$后的符号Macroname为所定义的宏的名称$parameter为宏指令的参数。
当宏指令被展开时将被替换成相应的值,类似于函数中的形式参数。
可以在宏定义时为参数指定相应的默认值。
MACRO$HandlerLabel HANDLER $HandleLabel$HandlerLabelsub sp,sp,#4 ;decrement sp(to store jump address)stmfd sp!,{r0} ;PUSH the work register to stack(lr does not push because it return to original address)ldr r0,=$HandleLabel;load the address of HandleXXX to r0ldr r0,[r0] ;load the contents(service routine start address) of HandleXXXstr r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR) MEND;;在程序中调用HandlerFIQ HANDLER HandleFIQHandlerIRQ HANDLER HandleIRQHandlerUndef HANDLER HandleUndefHandlerSWI HANDLER HandleSWIHandlerDabort HANDLER HandleDabortHandlerPabort HANDLER HandlePabort比如第一个为例说明 HandlerFIQ HANDLER HandleFIQ;;程序被汇编后,宏展开的结果HandlerFIQsub sp,sp,#4stmfd sp!,{r0}ldr r0,=HandleFIQldr r0,[r0]str r0,[sp,#4]ldmfd sp!,{r0,pc}下面一句一句分析一下,为了便于分析,假设sp = 0x33ff8000,$HandleLabel = 0x33ffff00,[0x33ffff00] = 0x10000000,r0 = 0x56001234:$HandlerLabel HANDLER $HandleLabel宏的名字叫HANDLER ,有两个参数$HandlerLabel定义一个标号sub sp,sp,#4把栈顶指针减4,留出一个字的空间(用于保存跳转地址的值),sp=0x33ff7ffcstmfd sp!,{r0}首先把sp减4 (sp=0x33ff7ff8),然后把将要使用的r0寄存器入栈,此时[0x33ff7ff8]=0x56001234ldr r0,=$HandleLabel给寄存器r0赋值,r0=0x33ffff00ldr r0,[r0]给寄存器r0赋值,r0=0x10000000str r0,[sp,#4];把寄存器r0保存到0x33ff7ffc (0x33ff7ff8+4),sp没有改变sp=0x33ff7ff8,如果str r0,[sp,#4]!sp 改变此时;[0x33ff7ffc] = 0x10000000ldmfd sp!,{r0,pc}把栈顶的两个字弹出,分别保存到r0、pc,此时sp=0x33ff8000,r0=0x56001234,pc=0x10000000 ,通过比较不难发现,sp和r0在执行前后都没有变化,程序就跳转到0x10000000处执行。
汇编语言伪指令在汇编语言程序里,有一些特殊的助记符,这些助记符与指令系统的助记符不同,它们没有对应的机器码。
这些助记符在源程序中的作用是完成汇编程序的各种准备工作,包括定义变量、分配数据存储空间、控制汇编过程、定义程序入口等。
它们仅仅在汇编的过程中起作用,一旦汇编过程结束,它们的使命也就完成了。
这些助记符称为伪指令,它们所完成的操作称为伪操作。
不同汇编器的伪指令可能存在少量的区别,并非所有的伪指令在任何编译器上都能被识别。
一、符号定义伪指令符号定义(Symbol Definition)伪指令用于定义ARM汇编程序中的变量,对变量赋值和定义寄存器别名等,如表1所列。
表1 符号定义伪指令实例:GBLL P_ON ; 定义全局逻辑变量P_ON P_ON SETL {TRUE} ; 给全局逻辑变量P_ON赋值为真LCLA NUM ; 定义局部数字变量NUM NUM SETA 100 ; 给全局数字变量NUM赋值为100RegList RLIST {R0-R5,R8,R10} ; 定义一个寄存器列表RegList,可用微处理器系统结构与嵌入式系统设计(第3版)2; LDM/STM指令访问该列表二、数据定义伪指令数据定义(Data Denfinition)伪指令一般用于为特定的数据分配存储单元,同时完成对已分配存储单元的初始化工作。
数据定义伪指令如表2所示。
表2 数据定义伪指令从使用方法上来讲,数据定义伪指令可以分为以下3类。
1.SPACE伪指令SPACE用于分配一片连续的存储区,并初始化为0。
其中表达式中的数字表示分配的字节数。
SPACE也可以用%代替。
实例:DataSpace SPACE 100 ; 分配连续100字节的存储单元并初始化为0 2.MAP和FIELD伪指令MAP和伪指令FIELD经常结合在一起使用。
MAP用于定义一个结构化的内存表的首地址,可以用“^”替代。
FIELD用于定义一个结构化的内存表中的数据域,可以用“#”代替。
arm中equ指令ARM中的equ指令ARM处理器是一种广泛应用于嵌入式系统的处理器架构。
在ARM 指令集中,equ指令是一条非常重要的伪指令,用于定义常量或符号的值。
本文将详细介绍equ指令的用法和作用。
一、equ指令的基本用法equ指令的基本语法如下:symbol_name EQU expression其中,symbol_name是常量或符号的名称,EQU是伪指令的操作码,expression是常量或符号的值。
equ指令的作用是将symbol_name定义为expression的值。
例如,我们可以使用equ指令定义一个常量:pi EQU 3.14159这样,pi就被定义为3.14159,我们可以在程序中使用pi来代替具体的数值。
二、equ指令的作用1. 定义常量equ指令最常见的作用就是定义常量。
在程序中,我们经常需要使用一些固定的数值,例如圆周率、常量等。
通过使用equ指令,我们可以将这些数值定义为常量,提高程序的可读性和可维护性。
2. 定义符号除了常量,equ指令还可以用于定义符号。
符号可以是一个地址、一个寄存器、一个标签等。
通过使用equ指令,我们可以为这些符号赋予一个有意义的名称,方便程序的编写和阅读。
3. 简化程序的修改使用equ指令定义常量或符号后,如果需要修改这些常量或符号的值,只需要修改一处定义即可,而不需要在整个程序中逐个查找和修改。
这样可以大大简化程序的修改工作,提高工作效率。
三、equ指令的注意事项1. equ指令只能在伪指令区域使用,不能放在代码区域。
因为equ 指令不是真正的机器指令,而是在汇编阶段被处理的。
2. equ指令定义的常量或符号的值在程序中是不可修改的。
如果需要在程序中修改常量或符号的值,需要使用其他指令或方法实现。
3. equ指令定义的常量或符号的值是在汇编阶段确定的,不会在程序运行时改变。
4. equ指令定义的常量或符号可以在整个程序中使用,包括代码区域和数据区域。
1、AREA语法格式:AREA 段名属性1,属性2,……AREA伪指令用于定义一个代码段或数据段。
其中,段名若以数字开头,则该段名需用―|‖括起来,如|1_test|。
属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。
常用的属性如下:— CODE属性:用于定义代码段,默认为READONL Y。
— DATA属性:用于定义数据段,默认为READWRITE。
— READONL Y属性:指定本段为只读,代码段默认为READONL Y。
— READWRITE属性:指定本段为可读可写,数据段的默认属性为READWRITE。
—ALIGN属性:使用方式为ALIGN 表达式。
在默认时,ELF(可执行连接文件)的代码段和数据段是按字对齐的,表达式的取值范围为0~31,相应的对齐方式为2表达式次方。
—COMMON属性:该属性定义一个通用的段,不包含任何的用户代码和数据。
各源文件中同名的COMMON段共享同一段存储单元。
一个汇编语言程序至少要包含一个段,当程序太长时,也可以将程序分为多个代码段和数据段。
使用示例:AREA Init,CODE,READONL Y指令序列;该伪指令定义了一个代码段,段名为Init,属性为只读2、ALIGN语法格式:ALIGN {表达式{,偏移量}}ALIGN伪指令可通过添加填充字节的方式,使当前位置满足一定的对其方式|。
其中,表达式的值用于指定对齐方式,可能的取值为2的幂,如1、2、4、8、16等。
若未指定表达式,则将当前位置对齐到下一个字的位置。
偏移量也为一个数字表达式,若使用该字段,则当前位置的对齐方式为:2的表达式次幂+偏移量。
使用示例:AREA Init,CODE,READONL Y,ALIEN=3 ;指定后面的指令为8字节对齐。
指令序列END3、CODE16、CODE32语法格式:CODE16(或CODE32)CODE16伪指令通知编译器,其后的指令序列为16位的Thumb指令。
CODE32伪指令通知编译器,其后的指令序列为32位的ARM指令。
若在汇编源程序中同时包含ARM指令和Thumb指令时,可用CODE16伪指令通知编译器其后的指令序列为16位的Thumb指令,CODE32 伪指令通知编译器其后的指令序列为32位的ARM指令。
因此,在使用ARM指令和Thumb指令混合编程的代码里,可用这两条伪指令进行切换,但注意他们只通知编译器其后指令的类型,并不能对处理器进行状态的切换。
使用示例:AREA Init,CODE,READONL Y……CODE32 ;通知编译器其后的指令为32位的ARM指令LDR R0,=NEXT+1 ;将跳转地址放入寄存器R0BX R0 ;程序跳转到新的位置执行,并将处理器切换到Thumb工作状态……CODE16 ;通知编译器其后的指令为16位的Thumb指令NEXT LDR R3,=0x3FF……END ;程序结束4、ENTRY语法格式:ENTRYENTRY伪指令用于指定汇编程序的入口点。
在一个完整的汇编程序中至少要有一个ENTRY (也可以有多个,当有多个ENTRY时,程序的真正入口点由链接器指定),但在一个源文件里最多只能有一个ENTRY(可以没有)。
使用示例:AREA Init,CODE,READONL YENTRY ;指定应用程序的入口点……5、END语法格式:ENDEND伪指令用于通知编译器已经到了源程序的结尾。
使用示例:AREA Init,CODE,READONL Y……END ;指定应用程序的结尾6、EQU语法格式:名称EQU 表达式{,类型}EQU伪指令用于为程序中的常量、标号等定义一个等效的字符名称,类似于C语言中的#define。
其中EQU可用―*‖代替。
名称为EQU伪指令定义的字符名称,当表达式为32位的常量时,可以指定表达式的数据类型,可以有以下三种类型:CODE16、CODE32和DA TA使用示例:Test EQU 50 ;定义标号Test的值为50Addr EQU 0x55,CODE32 ;定义Addr的值为0x55,且该处为32位的ARM指令。
7、EXPORT(或GLOBAL)语法格式:EXPORT 标号{[WEAK]}EXPORT伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件(包括非汇编源文件)中引用。
EXPORT可用GLOBAL代替。
标号在程序中区分大小写,[WEAK]选项声明其他的同名标号优先于该标号被引用。
使用示例:AREA Init,CODE,READONL YEXPORT Stest ;声明一个可全局引用的标号Stest……END8、IMPORT语法格式:IMPORT 标号{[WEAK]}IMPORT伪指令用于通知编译器要使用的标号在其他的源文件(包括非汇编源文件)中定义,但要在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。
标号在程序中区分大小写,[WEAK]选项表示当所有的源文件都没有定义这样一个标号时,编译器也不给出错误信息,在多数情况下将该标号置为0,若该标号为B或BL指令引用,则将B或BL指令置为NOP操作。
使用示例:AREA Init,CODE,READONL YIMPORT Main ;通知编译器当前文件要引用标号Main,但Main在其他源文件中定义……END9、EXTERN语法格式:EXTERN 标号{[WEAK]}EXTERN伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,如果当前源文件实际并未引用该标号,该标号就不会被加入到当前源文件的符号表中。
标号在程序中区分大小写,[WEAK]选项表示当所有的源文件都没有定义这样一个标号时,编译器也不给出错误信息,在多数情况下将该标号置为0,若该标号为B或BL指令引用,则将B或BL指令置为NOP操作。
使用示例:AREA Init,CODE,READONL YEXTERN Main ;通知编译器当前文件要引用标号Main,但Main在其他源文件中定义……END10、GET(或INCLUDE)语法格式:GET 文件名GET伪指令用于将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置进行汇编处理。
可以使用INCLUDE代替GET。
汇编程序中常用的方法是在某源文件中定义一些宏指令,用EQU定义常量的符号名称,用MAP和FIELD定义结构化的数据类型,然后用GET伪指令将这个源文件包含到其他的源文件中。
使用方法与C语言中的―include‖相似。
GET伪指令只能用于包含源文件,包含目标文件需要使用INCBIN伪指令使用示例:AREA Init,CODE,READONL YGET a1.s ;通知编译器当前源文件包含源文件a1.sGE T C:\a2.s ;通知编译器当前源文件包含源文件C:\ a2.s……END11、INCBIN语法格式:INCBIN 文件名INCBIN伪指令用于将一个目标文件或数据文件包含到当前的源文件中,被包含的文件不作任何变动的存放在当前文件中,编译器从其后开始继续处理。
使用示例:AREA Init,CODE,READONL YINCBIN a1.dat ;通知编译器当前源文件包含文件a1.datINCBIN C:\a2.txt ;通知编译器当前源文件包含文件C:\a2.txt……END12、RN语法格式:名称RN 表达式RN伪指令用于给一个寄存器定义一个别名。
采用这种方式可以方便程序员记忆该寄存器的功能。
其中,名称为给寄存器定义的别名,表达式为寄存器的编码。
使用示例:Temp RN R0 ;将R0定义一个别名Temp13、ROUT语法格式:{名称} ROUTROUT伪指令用于给一个局部变量定义作用范围。
在程序中未使用该伪指令时,局部变量的作用范围为所在的AREA,而使用ROUT后,局部变量的作为范围为当前ROUT和下一个ROUT之间。
ARM LINKscat文件在编译时的作用是告诉armlink生成的目标文件(也就是BIN文件时),各部分代码和数据的位置。
在下载时flashtool根据scat文件得知BIN文件的下载位置和大小.scater 文件并不是MTK引入的,而是ADS编译器定义的。
SCA T文件的规范可以查看ADS的帮助文档在介绍armlink的使用方法之前,先介绍要涉及到的一些术语。
映像文件(image):是指一个可执行文件,在执行的时候被加载到处理器中。
一个映像文件有多个线程。
它是ELF(Executable and linking format)格式的。
段(Section):描述映像文件的代码或数据块。
—RO:是Read-only的简写形式。
—RW:是Read-write.的简写形式。
—ZI:是Zero-initialized的简写形式。
输入段(input section):它包含着代码,初始化数据或描述了在应用程序运行之前必须要初始化为0的一段内存。
输出段(output section):它包含了一系列具有相同的RO,RW或ZI属性的输入段。
域(Regions):在一个映像文件中,一个域包含了1至3个输出段。
多个域组织在一起,就构成了最终的映像文件。
Read Only Position Independent(ROPI):它是指一个段,在这个段中代码和只读数据的地址在运行时候可以改变。
Read Write Position Independent(RWPI):它是指一个段,在该段中的可读/写的数据地址在运行期间可以改变。
加载时地址:是指映像文件位于存储器(在该映像文件没有运行时)中的地址。
运行时地址:是指映像文件在运行时的地址。
下面介绍一下armlink命令的语法完整的连接器命令语法如下:armlink [-help] [-vsn] [-partial] [-output file] [-elf] [-reloc][-ro-base address] [-ropi][-rw-base address] [-rwpi] [-split][-scatter file][-debug|-nodebug][-remove?RO/RW/ZI/DBG]|-noremove] [-entry location ][-keep section-id] [-first section-id] [-last section-id] [-libpath pathlist] [-scanlib|-noscanlib] [-locals|-nolocals] [-callgraph] [-info topics] [-map] [-symbols] [-symdefs file] [-edit file] [-xref] [-xreffrom object(section)] [-xrefto object(section)] [-errors file] [-list file] [-verbose][-unmangled |-mangled] [-match crossmangled][-via file] [-strict][-unresolved symbol][-MI|-LI|-BI] [input-file-list]上面各选项的含义分别为:-help这个选项会列出在命令行中常用的一些选项操作。