1、寻址方式
所谓寻址方式就是:处理器根据指令中给出的地址信息来寻找物理地址的方法。
1)立即寻址
立即寻址也叫立即数寻址,这是一种特殊的寻址方式,操作数本身就是在指令中给出的。
只要取出指令也就是取得了操作数,这个操作数被称为立即数,对应的寻址方式也就叫做立即数寻址。
例如:
△:ADD R0,R0,#1
△:ADD R0,R0,#0X3F
2)寄存器寻址
寄存器寻址就是利用寄存器中的数值作为操作数,这种寻址方式是各类微处理器经常使用的一种方式,
也是一种执行效率较高的寻址方式。
例程:
ADD R0,R1,R2
3)寄存器间接寻址
寄存器间接寻址就是以寄存器中的值作为操作数的地址,而操作数本身就放在存储器中。
例如:
ADD R0,R1,[R2]
LDR
4)基址变址寻址
基址变址寻址就是将寄存器(该寄存器一般称为基址寄存器)的内容与指令中给出的地址偏移量相加,
从而得到一个操作数的有效地址:
例如:
LDR R0,[R1,#4]
LDR R0,[R1,#4]!
LDR R0,[R1],#4
4)多寄存器寻址
采用多寄存器寻址方式,一条指令可以完成多个寄存器值的传送。
这种寻址方式可以用一条指令完成传送最多16个通用寄存器的值。
例如:
LDMIA R0,{R1,R2,R3,R4}
注意:该指令的后缀IA表示在每次执行完加载、存储操作后,R0按字长度增加,因此,指令可以将
连续存储单元的值送到R1~R4。
5)相对寻址
与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,
将两者相加之后得到的操作数的有效地址。
例如:
BL NEX
6)堆栈寻址
T堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称作为
堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
递增堆栈:向高地址方向生长
递减堆栈:向低地址方向生长
满堆栈:堆栈指针指向最后压入堆栈的有效数据项
空堆栈:堆栈指针指向下一个要放入数据的空位置
2、ARM指令
1)跳转指令
跳转指令用于实现程序流程的跳转,在ARM程序中有两种方法可以实现程序流程的跳转:
△:使用专门的跳转指令。
△:直接向程序计数器PC写入跳转地址值,通过向程序计数器PC写入跳转地址值,可以实现在4GB的
地址空间中的任意跳转,在跳转之前结合使用MOV LR,PC
等类似的指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用。
在ARM指令集中的跳转指令可以完成从当前指令向前或者向后的32MB的地址空间的跳转,包括以下四条指令:
△:B
△:BL
△:BLX
△:BX
①、B指令:
B{条件}
B指令是最简单的跳转指令。一旦遇到一个B指令,ARM处理器将立即跳转到给定的目标地址,从那里开始继续执行。
例如:
CMP R1,#0
BEQ LABEL
顺便把指令的条件贴出来:
②、BL指令:
BL{条件}
BL是另一个跳转指令,但跳转之前,会在寄存器R14(LR)中保存PC当前值,
因此,可以通过将LR的内容重新加载到PC中,来返回到跳转指令之后的那个指令处执行。
该指令是实现子程序调用的一个基本但常用的手段。
③、BLX指令
BLX 目标地址
BLX指令从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态有ARM状态切换到Thumb状态,
该指令同时将PC的当前内容保存到R14中。
因此,当子程序使用Thumb指令时,而调用者使用ARM指令,可以通过BLX指令实现子程序的调用和处理器
工作状态的切换。
同时,子程序返回可以通过寄存器R14值复制到PC中来完成返回。
④、BX指令
BX{条件} 目标地址
BX指令跳转到指令中指定的目标地址,目标地址的指令既可以是ARM指令,也可以是Thumb指令。
2、数据处理指令
数据处理指令可以分为数据传送指令、算术逻辑运算指令和比较指令等。
数据传送指令用于在寄存器和寄存器之间(这里一定要看清楚是寄存器与寄存器!!!)进行数据的双向传输。
算术逻辑运算指令完成常用的算术和逻辑的运算,该指令不但将运算结果博爱存在目的寄存器中,同时更新CPSR中的相应条件标志位。
1)MOV指令
MOC{条件}{S}
MOV指令完成从一个寄存器、被移位的寄存器加载到目的寄存器。
其中S选项决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
例程:
MOV R1,R0
MOV PC,R14
MOV R1,R0,LSL #3
2)MVN指令
MVN{条件}{S}
MVN指令完成从另一个寄存器、被移位的寄存器、或将一个立即数加载到目的寄存器。
与MOV指令不同之处是在传送之前按位取反了,既把一个取反的值传送给目的寄存器中。
其中S决定指令的操作是否影响CPSR中条件标识为的值,当没有S时指令不更新CPSR中条件标志位的值。
例程:
MVN
3)CMP指令
CMP{条件} 操作数1,操作数2
CMP指令用于把一个寄存器内容和另一个寄存器的内容或者立即数进行比较,同时更新CPSR中
条件标志位的值。
该指令进行一次减法运算,但是不保存结果,只更改条件标志位(其中指令条件上边的图片~~)~~
例如:
CMP R1,RO
CMP R1,#100
4)TST指令
TST{条件}
TST指令用于把一个寄存器的内容和另一个寄存器的内容或者立即数按位的与运算。
并根据结果更新CPSR中条件标志位的值。
操作数1是要测试的数据,而操作数2是一个位掩码,根据测试结果设置相应的标志位。
例程:
TST R1,#%1
5)ADD指令
ADD{条件}{S} 目的寄存器,操作数1,操作数2
ADD指令用于把两个操作数相加,并将结果存放到目的寄存器中。
操作数1一概是一个寄存器,操作数2可以是一个寄存器,被一位的寄存器,或者一个立即数。
例程:
ADD R0,R1,R2
ADD R0,R2,R3,LSL#1
6)SUB指令
不想说~~因为跟ADD指令一模一样,只是一个加一个减~~
7)AND指令
AND{条件}{S} 目的寄存器,操作数1,操作数2
AND指令用于在两个操作数上进行逻辑与运算,并肩结果放置在目的寄存器。
格式跟ADD一样。
例程:
AND R0,R0,#3
8)ORR指令
按位或,格式跟AND指令一样~~
功能:常用于设置操作数1的某些位。
9)BIC指令
BIC{条件}{S} Rd,Rn,operand2
BIC指令用于清除Rn中的某些位,并把结果存放在Rd中,操作数operand2为32位的掩码,如果
掩码中设置了某一位为1,则清除这一位。
例程:
BIC R0,R0,#11
10)MUL指令
MUL{条件}{S}
其中操作数1和操作数2均为32位的有符号数或者无符号数。
11)MRS指令(程序状态寄存器访问指令)
MRS{条件} 通用寄存器,程序状态寄存器(CPSR或SPSR)
MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。
注意:
该指令用于一下情况:
a、当需要改变程序状态寄存器时,可以用MRS将程序状态寄存器的内容读入通用寄存器,
修改后再写回程序状态寄存器。
b、当在异常处理或进程切换时,需要保存程序状态寄存器的值,可以先用该指令读出程序状态寄存器的值,然后保存。
12)MSR指令(与MRS相对应)
MSR{条件} 程序转台寄存器(CPSR或者SPSR)_<域>,操作数
MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中,其中,操作数可以为通用寄存器或立即数。
<域>用于设置程序状态寄存器中需要操作的位,32位的程序状态寄存器可分为4个域:
位[31:24]为条件标志位域,用f表示
位[23:16]为状态位域,用s表示
位[15:8]为扩展为域,用x表示
位[7:0]为控制位域,用c表示
该指令通常用于回复或者改变程序转台寄存器的内容,在使用时,一般要在MSR指令中指明将要操作的域。
例程:
MSR CPSR_C,RO
13)LDR指令(加载指令)
首先介绍一下加载/存储指令:
ARM微处理器支持加载/存储指令用于在寄存器和存储器之间传送数据,加载指令用于将存储器中的数据传送到寄存器,存储指令则完成相反的操作。
LDR{条件} 目的寄存器,<存储器地址>
LDR指令用于从存储器中将一个32位的数据传送到目的寄存器中。
该指令通常用于从存储器中读取32位的子数据到通用寄存器,然后对数据进行处理。
14)LDRB指令
格式通LDR指令一样!
LDRB指令用于从存储器中将一个8位的字节数据传送到目的寄存器中,同时将寄存器的高24位清零。
15)LDRH指令
LDRH指令用于从存储器中将一个16位的半字数据传送到目的寄存器中,同时将寄存器中的高16位清零。
16)STR指令(存储指令)
STR{条件} 源寄存器,<存储器地址>
STR指令用于从源寄存器中将一个32位的字数据传送到存储器中。
17)批量加载/存储指令-LDM/STM
ARM微处理器所支持的批量数据加载/存储指令可以一次在一片连续的存储器单元和多个寄存器之间传送数据,
批量加载指令用于将一片连续的存储器中的数据传送到多个寄存器,批量数据存储指令则完成相反的操作。
LDM{条件}{类型} 基址寄存器{!},寄存器列表
LDM(或STM)指令用于从由基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间
传送数据,该指令的常见用途是将多个寄存器的内容入栈或出栈。
类型:
IA:每次传送后地址加1
IB:每次传送前地址加1
DA:每次传送后地址减1
DB:每次传送前地址减1
FD:满递减堆栈
ED:空递减堆栈
FA:满递增堆栈
EA:空递增堆栈
{!}:
为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。基址寄存器不允许为R15,寄存器列表可以为R0~R15的任意组合。
{^}:
为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据缠讼之外,还将SPSR复制到CPSR。
例程:
STM R13!,{R0,R4-R12,LR}
LDMFD R13!,{R0,R4-R12,PC}
18)SWP指令
SWP{条件} 目的寄存器,源寄存器1,[源寄存器2]
SWP指令用于将源寄存器2所指向的存储器中的字数据传送到目的寄存器中,同时将源寄存器1中的子数据传送到源寄存器2所指向的存储器中。
显然,当源寄存器1和目的寄存器为同一个寄存器时,指令交换该寄存器和存储器的内容。
例程:
SWP R0,R0,[R1]
19)移位指令
ARM微处理器所支持数据的移位操作~~
移位操作在ARM指令集中不作为单独的指令使用,他只能作为指令格式中是一个字段,在汇编语言中表示为指令中的选项。
移位操作包括如下6中:ASL和LSL是等价的!
21)LDR/STR LDM/STM指令的区别
(1)LDR:L表示LOAD,LOAD的含义应该理解为:Load from memory into register。下面这条语句就说明的很清楚:
LDR R1, [R2]
R1<——[R2]
就是把R2所指向的存储单元的内容的值(一个memory地址内的值),读取到R1中(一个register)
(2)STR:S表示STORE,STORE的含义应该理解为:Store from a register into memory。下面这条语句表示的很清楚:
STR R1, [R2]
R1——>[R2]
就是把寄存器R1中的内容“保存”到R2所指向的存储的单元中(一个memory地址)。
显然,这两条语句都有个特点,就是寄存器写在前面(左边)而内存地址写在后面(右边),数据传送的方向则是恰好相反的。
下面对LDM和STM介绍,使用sp来介绍,因为实际使用中,和sp一起使用更多。
(3)LDM:L的含义仍然是LOAD,即是Load from memory into register。
虽然貌似是LDR的升级,但是,千万要注意,这个指令运行的方向和LDR是不一样的,是从左到右运行的。该指令是将内存中堆栈内的数据,批量的赋值给寄存器,即是出栈操作;其中堆栈指针一般对应于SP,注意SP是寄存器R13,实际用到的却是R13中的内存地址,只是该指令没有写为[R13],同时,LDM指令中寄存器和内存地址的位置相对于前面两条指令改变了,下面的例子:
LDMFD SP! , {R0, R1, R2}
实际上可以理解为: LDMFD [SP]!, {R0, R1, R2}
意思为:把sp指向的3个连续地址段(应该是3*4=12字节(因为为r0,r1,r2都是32位))中的数据拷贝到r0,r1,r2这3个寄存器中去。
(4)STM:S的含义仍然是STORE,与LDM是配对使用的,其指令格式上也相似,即区别于STR,是将堆栈指针写在左边,而把寄存器组写在右边。
STMFD SP!, {R0}
同样的,该指令也可理解为: STMFD [SP]!, {R0}
意思是:把R0保存到堆栈(sp指向的地址)中。
显然,这两个堆栈操作指令也有个特点,就是寄存器组写在后面(右边)而堆栈指针写在前面(左边),而且实际上使用的是堆栈指针中的内存地址,这一点与前面两条指令是有区别的。
例程:
GBLA TEST
TEST SETA 0XAA
②、LCALL/LCLL/LCLS
跟①是一样一样的~~只不过是局部变量而已!
③RLIST
格式:
名称 RLIST {寄存器列表}
RLIST伪指令用于对一个通用寄存器列表定义名称,使用该伪指令定义的列表名称可在ARM指令LDM/STM中使用。
在LDM/STM指令中,列表中的寄存器访问次序为根据寄存器的标号由低到高,而与列表中的寄存器排列次序无关。
例程:
REGLIST RLIST {R0-R5,R8,R10}
2)数据定义伪指令
数据定义伪指令一般用于为特定的数据分配存储单元,同时可完成已分配存储单元的初始化。
常见的数据定义伪指令有:
DCB
DCDS
1.DCB指令
格式:
标号 DCB 表达式
DCB伪指令用于分配一个连续的自己存储单元并用伪指令中指定的表达式初始化。
其中,表达式可以为0~255的数字或字符串。
DCB也可以用”=“代替。
例程:
STR DCB "This is a test!"
②、SPACE指令
格式:
标号 SPACE 表达式
SPACE伪指令用于分配一片连续的存储区域并初始化为0。
其中,表达式为要分配的字节数。SPACE可以用”%“代替。
例程:
DATASPACE SPACE 100
③、MAP指令
格式:
MAP 表达式{,基址寄存器}
MAP为指令用于定义一个结构化的内存表的首地址。
MAP也可以用”^“代替。
表达式可以为程序中的标号或数学表达式,基址寄存器为可选项,当基址寄存器选择不存在时,
表达式的值即为内存表的首地址,当该选项存在时,内存表的首地址为表达式的值与基址寄存器的和。
MAP伪指令通常与FIELD伪指令配合使用来定义结构化的内存表。
例程:
MAP 0x100,R0
④FILED指令
通常都与MAP指令一起用。
格式:
标号 FIELD 表达式
FIELD为指令用于定义一个结构化内存表中的数据域,FILED也可用”#“代替。
表达式的值为当前数据域在内存表中所占的字节数。
FIELD伪指令定义内存表中的各个数据域,并可以为每个数据域指定一个标号使其他的指令
引用。
例程:
MAP 0X100
A FIELD 16
B FIELD 32
3)汇编控制伪指令(这个东西不想写了,用到了,一查就哦了~~写多了也没用多了好)
汇编控制伪指令用于控制汇编陈旭的执行流程,常用的汇编控制伪指令包括以下几条:
△:IF、ELSE、ENDIF
△:WHILE、WEND
△:MACRO、WEND
△:MEXIT
4)其它伪指令
△:AREA
△:ALIGN
△:CODE16、CODE32
△:ENTRY
△:END
△:EQU
△:EXPORT(Or GLOBAL)(这个是声明一个u且安居的标号,该标号可在其它文件引用!!和C语言一个道理~~)
△:IMPORT
△:EXTERN
△:GET(Or INCLUDE)
这里我就说下IPMORT指令吧:
IPMORT 标号
IMPORT伪指令用于通知编译器要使用得标号在其他的源文件中定义,但要在当前源文件中引用。
标号在程序中区分大小写。
使用实例:
ARET Init,CODE,READONLY
IMPORT Main