ARM汇编语言(4) 指令、伪操作、伪指令学习
LDR R0,[R1]:指令,将R1指向的内存地址存放的内容加载到R0中;
本文引用地址:https://www.eepw.com.cn/article/201611/317563.htmLDR R0,LABEL:指令,将标号LABEL所代表的内存地址处存放的内容加载到R0中;
LDR R0,=10000:伪指令,将常熟10000赋予R0,采用LDR指令+文字池的方式实现;
LDR R0,=LABEL:伪指令,将标号LABEL所代表的内存地址赋予R0;
指令部分:
伪操作部分:
符号定义伪操作:定义变量,对变量进行赋值,定义寄存器名称
GBLA:全局的算术变量,初始化为0;
GBLL:全局的逻辑变量,初始化为{FALSE};
GBLS:全局的串变量,初始化为“”;
LCLA:局部的算术变量,初始化为0;
LCLL:局部的逻辑变量,初始化为{FALSE};
LCLS:局部的串变量,初始化为“”;
SETA:给算术变量赋值;
SETL:给逻辑变量赋值;
SETS:给串变量赋值;
RLIST:为一个通用寄存器列表定义名称;
CN:为一个协处理器的寄存器定义名称;
CP:为一个协处理器定义名称;
DN:为一个双精度的VFP寄存器定义名称;
SN:为一个单精度的VFP寄存器定义名称;
FN:为一个FPA浮点寄存器定义名称;
数据定义伪操作
LTORG:声明一个数据缓冲池的开始;
MAP:定义一个结构化的内存表的首地址,同义词为^;
FIELD:定义一个结构化内存表中的数据域,同义词#;
SPACE:分配一块内存单元,并用0初始化,同义词%;
{label} SPACE exprexpr表示分配的内存字节数;
DCB:分配一段字节内存单元,并用expr初始化之,同义词=;
{label} DCB expr, {expr}...expr是-128~255的数值或字符串;
DCD,DCDU:分配一段字内存单元,分配的内存都是字对齐的,并用expr初始化之,同义词&,DCDU分配的内存单元不严格字对齐;
DCDO:分配一段字内存单元,分配的内存都是字对齐的,并将每个字单元的内容初始化为expr标号基于静态基址寄存器R9的偏移量;
DCFD,DCFDU:
DCFS,DCFSU:
DCI:(ARM)分配一段字内存单元,分配的内存都是字对齐的,并用expr初始化;(Thumb)分配一段半字内存单元,分配的内存都是半字对齐的,并用expr初始化;
DCQ,DCQU:
DCW,DCWU:
汇编控制伪操作
IF,ELSE,ENDIF:根据条件将一段源代码包括在汇编语言程序中或者将其排除在程序之外;
IF logical expression
instructions or directives
ELSE
instructions or directives
ENDIF
WHILE,WEND:根据条件重复汇编相同的或者几乎相同的一段源代码;
WHILE logical expression
instructions or directives
WEND
MACRO,MEND:定义宏定义体;
MEXIT:从宏中跳转出去;
栈中数据帧描述伪操作;
信息报告伪操作;
其它伪操作:
CODE16,CODE32:
EQU:为数字常量,基于寄存器的值和程序中的标号定义一个字符名称,同义词*;
name EQU expr {, type}
AREA:
ENTRY:
END:
ALIGN:
EXPORT:
GLOBAL:
IMPORT:
EXTERN:
GET:
INCLUDE:
INCBIN:
KEEP:
NOFP:
REQUIRE:
REQUIRE8:
PRESERVE8:
RN:
ROUT:
伪指令部分:伪指令不是真正的指令,在汇编编译器对源程序进行汇编处理时被替换成对应的ARM或者Thumb指令;
ADR(小范围的地址读取伪指令):将基于PC的地址值或者基于寄存器的地址值读取到寄存器中;
ADR{cond} register, expr
expr是基于PC或者基于寄存器的地址表达式,取值范围如下:
地址值不是字对齐时,取值范围-255~255;
地址值是字对齐时,取值范围-1020~1020;
地址值是16字节对齐时,取值范围更大;
ADRL(中等范围的地址读取伪指令):将基于PC的地址值或者基于寄存器的地址值读取到寄存器中;
ADRL{cond} register, expr
expr是基于PC或者基于寄存器的地址表达式,取值范围如下:
地址不是字对齐时,-64KB~64KB;
地址是字对齐时,-256KB~256KB;
地址是16字节对齐时,取值范围更大;
LDR:将一个32位的常数或者一个地址值读取到寄存器中;
LDR{cond} register, ={expr | label-expr}
expr为32位常量;
label-expr为基于PC的地址表达式或者外部表达式;
NOP:汇编时被替换成ARM中的空操作;
实例程序:
1、
AREA LDR_Code, CODE, READONLY
ENTRY
LDR r0, =src
LDR r1, =dst
MOV r2, r0
MOV r3, r1
MOV r5, #100
LDR r6, =100
LDR r7, =999999
srcDCD 0, 1;, 2, 3, 4, 5, 6, 7, 8, 9
dstDCD 0, 0;, 0, 0, 0, 0, 0, 0, 0, 0
END
反汇编代码:
$a
LDR_Code
0x00000000: e59f0024 $... LDR r0,0x2c
0x00000004: e59f1024 $... LDR r1,0x30
0x00000008: e1a02000 . .. MOV r2,r0
0x0000000c: e1a03001 .0.. MOV r3,r1
0x00000010: e3a05064 dP.. MOV r5,#0x64
0x00000014: e3a06064 d`.. MOV r6,#0x64
0x00000018: e59f7014 .p.. LDR r7,0x34
src
$d
0x0000001c: 00000000 .... DCD 0
0x00000020: 00000001 .... DCD 1
dst
0x00000024: 00000000 .... DCD 0
0x00000028: 00000000 .... DCD 0
0x0000002c: 0000001c .... DCD 28
0x00000030: 00000024 $... DCD 36
0x00000034: 000f423f ?B.. DCD 999999
(1)为LDR伪指令生成的代码,似乎有问题,不是基于PC的值,还是有默认的规则?
使用GNU ARM Assembly将上面的代码重新实现一次:
.section .text
.global _start
_start:
LDR r0, =src
LDR r1, =dst
LDR r2, =1000
LDR r3, =5555
MOV r4, r2
MOV r5, r3
.section .data
src: .word 0, 0
dst: .word 0, 1
编译通过了,不确定代码有没有问题,后面再检查
将上面的代码使用arm-none-eabi-as编译不链接,然后使用arm-none-eabi-objdump反汇编:
Disassembly of section .text:
00000000 <_start>:
0: e59f0010 ldr r0, [pc, #16] ; 18 <_start+0x18>
4: e59f1010 ldr r1, [pc, #16] ; 1c <_start+0x1c>
8: e3a02ffa mov r2, #1000 ; 0x3e8
c: e59f300c ldr r3, [pc, #12] ; 20 <_start+0x20>
10: e1a04002 mov r4, r2
14: e1a05003 mov r5, r3
18: 00000000 andeq r0, r0, r0
1c: 00000008 andeq r0, r0, r8
20: 000015b3 strheq r1, [r0], -r3
诚如文档中对LDR的介绍:
LDR r0, =src
LDR r1, =dst
LDR r2, =1000
LDR r3, =5555
这四条命令都进行了处理,以LDR r0, =src为例:
0: e59f0010 ldr r0, [pc, #16] ; 18 <_start+0x18>
src的值被存储,ldr指令将[pc, #16]地址中的值加载到寄存器r0中,
ARM处理器中,pc的值为当前执行的指令的地址值加上8,因此,执行该条
指令时,pc的值为8,此时pc加上16,则为十进制的24,十六进制的
0x18,但是此时地址单元0x18中存储的却是一条指令:
andeq r0, r0, r0
为什么?
将之前编译生成的.o文件使用arm-none-eabi-ld进行连接,生成可执行文件,
然后反汇编,此时代码变为:
Disassembly of section .text:
00008000 <_start>:
8000: e59f0010 ldr r0, [pc, #16] ; 8018 <_start+0x18>
8004: e59f1010 ldr r1, [pc, #16] ; 801c <_start+0x1c>
8008: e3a02ffa mov r2, #1000 ; 0x3e8
800c: e59f300c ldr r3, [pc, #12] ; 8020 <_start+0x20>
8010: e1a04002 mov r4, r2
8014: e1a05003 mov r5, r3
8018: 00010024 andeq r0, r1, r4, lsr #32
801c: 0001002c andeq r0, r1, ip, lsr #32
8020: 000015b3 strheq r1, [r0], -r3
此时,src的值应该存放在0x8000 + 8 + 16,8和16都是十进制的,因此应该是0x8018,
但是0x8018地址单元中为:
andeq r0, r1, r4, lsr #32
lsr在上一篇寻址方式中有所介绍,此时r4中的值通过上面的指令可知为十进制的1000,十六进制的0x3E8,
0x3E8的二进制:
0000 0000 0000 0000 0000 0011 1110 1000,执行lsr #32操作,右移32位,则变为0,
r1中此时不管是什么值,AND指令执行按位取与操作,指令的执行结果自然是0,存放到r0寄存器中?
此处应该是个pool?为什么是指令?
(2)$a、$d分别表示什么意思?
摘录自:Using asThegnuAssembler的Mapping Symbols章节
The ARM ELF specification requires that special symbols be inserted into object files to
mark certain features:
$a At the start of a region of code containing ARM instructions.
$t At the start of a region of code containing THUMB instructions.
$d At the start of a region of data.
待补充...
评论