ARM --- 汇编指令

2 汇编

2.1 汇编基础

2.1.1 汇编文件中的符号信息

1.位操作:伪操作不是一条指令,知识给编译器使用,知道编译器如何对代码进行编译,不占用代码段的任何空间

.text

.gloal

.end

.data

.word

.short

.byte

.if

.else

.endif ....

2.汇编指令:汇编指令汇编编译器编译成32位的机器码,放到代码段空间,汇编指令可以完成特定的功能

3.伪指令:本身不是一条汇编指令,编译器可以将其编译生成多条指令来完成一条伪指令的功能

4.注释: @单行注释 与编译器有关

多行注释:/**/ .if .endif

2.1.2 最基本的汇编指令的语法格式

{cond}{s} Rd, Rn , oprand_shifter

指令码 指令名

{cond} 条件码,实现指令的有条件执行

{s} 状态位 加s,指令的执行结果影响CPSR 寄存器的NZCV位的变化

Rd:目标寄存器

Rn:第一个操作寄存器

oprand_shifter:第二个操作数 可以是寄存器 可以是立即数 可以是经移位的寄存器

不区分大小写

2.2 汇编指令

2.2.1数据操作指令
2.2.1.1数据搬移指令
mov mvn
 mov  mvn
 @没有第一操作数
 ​
 @mov{cond}{s} Rd, oprand_shifter
 ​
 @mvn{cond}{s} Rd, oprand_shifter
 .text   @代码段
 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
             @函数的入口地址
     mov r0,#01  @汇编指令 第二操作数为立即数
     mov r1 ,r0  @第二操作数为寄存器
     mvn r2,#0xff @第二操作数为立即数 ,对第二操作数取反在存入
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束
 ​

立即数

存放在0-11位上,其中0-7位存放0-255之间的数,8-11位上存放偏移量/2

如何判断某个数是立即数:

首先通过要判断的那个数,找到一个0-255(0x00-0xff)之间的数,然后将这个数据循环右移偶数维,如果可以得到要判断的那个数,说明是立即数

指令不同,立即数规则不同

有效数

如果一个数取反后是立即数说明这个数是有效数,也可以存储

伪指令

ldr Rd , =任意32位的整数

ldr r0, = 0x12345678

2.2.1.2算数运算指令

add:普通的加法指令,不需要考虑进位标志位

adc:需要考虑进位标志位
 .text   @代码段
 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
 ​
 @练习,实现两个64位数据的加法运算
 @第一个64位的数据存到r0(第32位),r1(高32位)
 @第二个64位的数据存到r2,r3
 @结果存在r4 ,r5
     mov r0, #0xFFFFFFFE
     mov r1, #3
     mov r2, #4
     mov r3, #5
     
     @完成低32位数据的加法
     @要加s,这样溢出才会影响SPSR的C位,进位
     adds r4,r0,r2  @r4 = 0xFFFFFFFE + 0x4 = 0x2
     
     @完成高32位数据的加法
     adc r5,r1,r3   @r5 = 0x3 + 0x5 + C = 0x9
 ​
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束
sub:普通的减法法指令,不需要考虑借位标志位
sbc:需要考虑借位标志位
 .text   @代码段
 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
 ​
 @练习,实现两个64位数据的加法运算
 @第一个64位的数据存到r0(第32位),r1(高32位)
 @第二个64位的数据存到r2,r3
 @结果存在r4 ,r5
     mov r0, #0xFFFFFFFE
     mov r1, #3
     mov r2, #4
     mov r3, #5
     
     @完成低32位数据的加法
     @要加s,这样溢出才会影响SPSR的C位,进位
     adds r4,r0,r2  @r4 = 0xFFFFFFFE + 0x4 = 0x2
     
     @完成高32位数据的加法
     adc r5,r1,r3   @r5 = 0x3 + 0x5 + C = 0x9
 ​
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束
mul:乘法指令

乘法指令的操作数只能是寄存器

 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
 ​
 ​
 ​
     mov r0, #3
     mov r1, #4
 ​
     mul r2,r0,r1   @r2= 0x3 * 0x4 = 0xc
     
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束
     

div:除法指令(ARM-V8架构支持,V7架构不支持)

格式:

{cond}{s} Rd,Rn,oprand_shifter

2.2.1.3移位操作指令

指令码

lsl:逻辑左移/无符号数左移 logical(逻辑) shift(转移) left(左)
lsr:逻辑右移/无符号数右移
asr:算术右移/有符号数右移
ror:循环右移
 
.text   @代码段
 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
             @函数的入口地址
     mov r0, #0xFF
     
     @按位左移,低位补0
     lsl r1,r0,#0x4 @将r0的值左移4位后赋值给r1
     
     @按位右移,高位补0
     lsr r2,r1,#4
     
     @算术右移,高位补符号位
     mov r0 , #0xFF000000
     asr r3,r0,#4
     
     
     @循环右移,低位移除,补到高位
     ror r4,r3,#5    @r4 = 0x07F80000
 ​
 ​
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束
 ​
 ​
 @第二个操作数可以是移位操作寄存器
 mov r0,r1, lsl #4 @ r0 = r1<<4

指令格式

{cond}{s} Rd,Rn,oprand_shifter

2.2.1.4位运算指令
and:按位与运算
orr:按位或运算
eor:按位异或运算
bic:按位清除
.text   @代码段
 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
             @函数的入口地址
     mov r0, #0xFF
     
     @按位左移,低位补0
     lsl r1,r0,#0x4 @将r0的值左移4位后赋值给r1
     
     @按位右移,高位补0
     lsr r2,r1,#4
     
     @算术右移,高位补符号位
     mov r0 , #0xFF000000
     asr r3,r0,#4
     
     
     @循环右移,低位移除,补到高位
     ror r4,r3,#5    @r4 = 0x07F80000
 ​
 ​
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束
 ​
 ​
 @第二个操作数可以是移位操作寄存器
 mov r0,r1, lsl #4 @ r0 = r1<<4

2.2.1.5比较指令
cmp:比较两数的大小

指令格式

cmp{cond} Rn,oprand_shifter

注意:

比较指令的本质就是做减法运算

指令没有目标寄存器,只有第一个操作寄存器和第二个操作数

指令的执行结果最终影响的是cpsr的NZCV位,并且不需要加s

比较指令和条件码配合使用

 .text   @代码段
 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
             @函数的入口地址
 ​
     ldr r1,=0x000000FF
     ldr r0,=0x0000FFFF
     cmp r0,r1
     
 ​
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束
 ​
 @小于:N置1
 @大于:C置1
 @等于:Z置1,C置1

2.2.2 跳转指令
b/bl{cond} Lebel(标签/汇编函数名):跳转到Lebel标签下的第一条指令

Label:汇编指令

  1. 跳转指令的本质就是修改PC值

  2. 有去有回就用bl,函数调用

  3. 有去无回就是用b,stop: b stop

 .text   @代码段
 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
             @函数的入口地址
 ​
     mov r0, #3
     mov r1, #4
     
     @调用汇编函数
     bl add_func  @自动保存调准质量的下一条指令的地址到LR中
                 @让PC执行函数的入口地址,
     nop @空指令
     @ldr pc , =stop
     b stop     @跳出循环
     @定义汇编的函数
     add_func:
         add r0,r0,r1
         mov pc,lr
     

     
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束
2.2.3 load/Store内存读写指令
1>单寄存器操作指令
ldr:将内存中的数据读到寄存器中
str:将寄存器中的数据写道内存中
ldrh:一次只能读写半字 2字节
strh:一次只能读写半字 2字节
ldrb:一次读写一字节
ldrb:一次读写一字节

ld:load

st:store

r:register

格式:

ldr{cond} Rd, [Rm]

Rm 寄存器中的数据将被看出一个内存的地址,将[Rm]指向的内存空间中的数据读到Rd寄存器

str{cond} Rd, [Rm]

Rm 寄存器中的数据将被看出一个内存的地址,将Rd寄存器中的数据写到[Rm]指向的地址

 .text   @代码段
 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
             @函数的入口地址
     @不严谨
 ​
     ldr r0,=0x40000800
     ldr r1,=0x12345678
     
     str r1,[r0]
     ldr r2,[r0]
     
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束

 @ldr和str指令的特定用法
 ​
 @将[Rm,#num]指向的地址空间的数据,读到Rd寄存器中,
 @Rm中的地址不变
 ​
 ldr/ldrh/ldtd Rd,[Rm,#num] 
 ​
 ​
 @将[RM]指向的地址空间的数据,读到Rd寄存器中
 @同时更新Rm中的地址,Rm=Rm+num
 ​
 ldr/ldrh/ldtd Rd,[Rm],#num
 ​
 @将[RM+num]指向的地址空间的数据,读到Rd寄存器中
 @同时更新Rm中的地址,Rm=Rm+num
 @!表示更新Rm寄存器中的地址
 ​
 ldr/ldrh/ldtd Rd,[Rm,#num]!
 ​
 @将Rd寄存器中的数据写到[Rm+num]指向的地址空间中
 @Rm中的地址不变
 ​
 str/strh/strd Rd,[Rm,#num] 
 ​
 ​
 @将Rd寄存器中的数据写到[Rm]指向的地址空间中
 @同时更新Rm中的地址,Rm=Rm+num
 ​
 str/strh/strd Rd,[Rm],#num
 ​
 @将Rd寄存器中的数据写到[Rm,#num]指向的地址空间中
 @同时更新Rm中的地址,Rm=Rm+num
 @!表示更新Rm寄存器中的地址
 ​
 str/strh/strd Rd,[Rm,#num]!
 .text   @代码段
 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
             @函数的入口地址
     @不严谨
 ​
     ldr r0,=0x40000800
     ldr r1,=0x12345678
     ldr r2,=0x22222222
     ldr r3,=0x33333333
     
     str r1,[r0,#4]
     str r2,[r0],#4
     str r3,[r0,#4]!
     
     ldr r0,=0x40000800
     
     ldr r4,[r0,#4]
     ldr r5,[r0],#4
     ldr r6,[r0,#4]!
     
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束
 ​
 ldr/str指令中的num必须是4的整数倍
 ldrh/strh指令中num必须是2的整数倍
 ldrb/strb指令中的num必须是1的整数倍

2>多寄存器操作指令

m:mutil

ldm:将内存空间中连续的多个数据督导多个寄存器中

stm:将多个寄存器中的数据写到连续的内存空间中

格式:

ldm/stm Rm,{寄存器列表}

  1. Rm寄存器中的数据将被看出内存的地址

  2. 寄存器中的寄存器如果连续就是用-隔开,如果不连续就用,隔开

  3. 寄存器列表中的寄存器要求从小到达的编号进行书写

3>栈操作指令

栈的种类:

增栈:压栈之后栈指针向高地址移动

减栈:压栈之后栈指针向低地址移动

空栈:当前栈指针指向的空间没有有效的数据,可以先进性压栈,然后将栈指针移动到下一个空的位置

满栈:当前栈指针指向的空间由有效的数据,需要先移动栈指针指向一个没有有效数据的空间然后再压入数据之后此时栈指针指向的空间又有有效数据

栈的操作方式:4种

满赠栈:Full Ascending stmfa/ldmfa

满减栈:Full Descending stmfd/ldmfd

空增栈:Empty Ascending stmea/ldmea

空减栈:Empty Decending stmed/lmed

ARM处理器默认采用的是满减栈

格式:

ldmfd/stmfd sp!,{寄存器列表}

  1. sp寄存器中的数据将被看成指向的栈空间的地址

  2. 寄存器中的寄存器如果连续就是用-隔开,如果不连续就用,隔开

  3. 寄存器列表中的寄存器要求从小到达的编号进行书写

  4. 每次压栈或者出栈之后都需要更改sp寄存器中记录的地址

 .text   @代码段
 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
             @函数的入口地址
     @初始化栈指针
     ldr sp,=0x40000820
     mov r0,#0x100
     mov r1,#0x200
     bl add_funcl
     add r2,r0,r1  @r2 = r0 + r1 = 0x300
     b stop
     
     add_funcl:
         stmfd sp!,{r0-r1} @压栈保存现场
         mov r0,#0x300
         mov r1,#0x400
         add r3,r0,r1  @r3 = r0 + r1 = 0x700
         ldmfd sp!,{r0-r1} @出栈恢复现场
         mov pc,lr
     
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束
2.2.4 特殊功能寄存器读写指令

msr :将普通寄存器中国的数据写到特殊寄存器中

mrs:将特殊寄存器中国的数据写到普通寄存器中

msr cpsr,#num/Rm:将一个立即数或者寄存器中的值赋值给cpsr寄存器

mrs Rd,cpsr :将cpsr寄存器中的值赋值给Rd寄存器

系统上电默认处于svc模式,修改cpsr寄存器的模式位(M[4:0])位,切换到用户模式

CPSR中的默认值为0xD3 1101 0011

修改为用户模式

CPSR中的默认值为0xD0 1101 0000

 .text   @代码段
 .global _start  @将_start标签声明为一个全局的函数
 _start:   @_start:标签的定义,等价于c语言的函数名
             @函数的入口地址
     @不严谨
     @msr cpsr , #0xD0 
     
     @方法2
     mrs r0,cpsr
     bic r0,r0,#0x1F
     orr r0,r0,#0xD0
     msr cpsr,r0
     
     
 stop:  @stop标签
     b stop @跳转指令,跳转到stop标签下的指令 类似goto
     
     @裸机的程序中必须有一个死循环
 .end @代码段结束

你可能感兴趣的:(ARM,arm开发,arm)