汇编语言在最底层,只有电路的电信号,可以用电压表示电路状态,所以用0和1来代替高低电平。也就有了二进制。处理器处理二进制序列,有时候也成为机器码。
假设
有几个很长的二进制有不同的含义,前人总结了很多套形式,在使用,就需要助记符和缩写来记二进制组合,助记符一般是3个字符,也有一些多个字母的
我们就可以使用助记符来编写程序,也就是汇编语言的程序。汇编语言是程序的最底层语言
ARM汇编用在了什么地方?
我们身边有很多东西都是ARM架构,手机、路由器、遥控器、物联网IOT设备等等,都是ARM架构。
ARM汇编的指令集
- 数据处理指令
- 转义指令
- 程序状态寄存器访问指令
- 加载、存储指令
- 异常产生指令
- 伪指令
1. 数据处理(传送)指令
a=2 b=3 | a+b | a>b | true and false
分号后面可以写注释
1.1 数据传送指令
1----MOV
MOV R1,R0:把寄存器R0的值 或 立即数传递给R1寄存器
示例:
MOV R1,R0
MOV PC,R14 寄存器R14的值传给PC
MOV R1,R0,LSL#3 将寄存器R0的的值左移3位后(相当于十进制乘以8),传递给R1
2----MVN:数据取反传送指令
MVN R1,R0:把寄存器R0的立即数 按位取反 传递给R1寄存器
1.2 算数运算指令
1----ADD 加法
示例:(指令小写也可以)
ADD R0,R1,R2; R0=R1+R2
ADD R0,R1,#256; R0=R1+256
ADD R0,R1,R2,LSL#1 ; R0=R1+(R2<<1)
2----ADC 带进位的加法指令
会将两个操作数相加,再加上CPSR中的C条件标位置的值,将加过放到 目的寄存器中
CPSR是什么?
叫做当前程序状态寄存器(ARM中一般有16个寄存器,0-15,第17个就是CPSR)
CPSR寄存器示意图如下:
- V:溢出标志
- C:进位标志
- Z:零标志
- N:复数标志
- T:状态位
- M:模式位
3----ADDS:相加并影响标志位
4----SUB减法
SUB {条件} 目的寄存器R0,操作数1,操作数2
sub指令使用操作数1 减去 操作数2.并将结果放入操作数R0中
操作数1 一般都是寄存器
操作数2 可以是寄存器,也可以是立即数,也可以是被位移的寄存器
示例:
sub R0,R1,R2 ; R0=R1-R2
sub R0,R1,#256; R0=R1-256
sub R0,R1,R2,LSL#1 ; R0=R1-(R2<<1)
5----SBC 带借位的减法指令
1.3 比较指令
1----直接比较指令CMP
cmp指令用于把一个寄存器的内容和另一个寄存器的内容 或 立即数 进行比较,同时更新CPSR 中条件标示位。
这个执行,会做一次减法运算,不存储结果,只更改条件标志位。
标志位标示的是操作数1与操作数2的关系(大于、小于、等于)
CMP {条件} 操作数1,操作数2
示例:
cmp R0,R1 ;将寄存器R0 的值与R1的值进行相减,并根据结果设置CPSR的标志位
cmp R0,#50 ;将寄存器R0的值 与 立即数50 相减,并根据结果设置CPSR的标志位
2----CMN负数比较指令
cmn 操作数1,操作数2
cmn指令用于把一个寄存器的内容和另一个寄存器的内容 或立即数进行比较,同时更新 CPSR中条件标志位
示例:
cmn R1,R0 ;将R1寄存器和R0寄存器的值相加,并根据结果设置CPSR的标志位
cmn R1,#80 ;将寄存器R1的值 与 立即数 80 相加,并根据结果设置 CPSR 的标志位
1.4 逻辑运算指令
1----AND逻辑与
AND {条件}{S} 目的寄存器,操作数1,操作数2
AND指令用于操作数1与操作数2 进行逻辑运算,结果存入目的寄存器
操作数1 一般都是寄存器,操作数2 可以是寄存器,也可以是被位移的寄存器,也可以是立即数
AND R0,R1,R2 ;R1 && R2的值 给R0寄存器
2----ORR 逻辑或
3----EOR 逻辑异或
4----BIC 位清零
1.5 乘法
(1)MUL 乘法
mul {cond}{S} R0,R1,R2 ;R0=R1xR2
muls R0,R3,R7 ;R0=R3xR7 同时设置CPSR中的N位和Z位
(2)MLA 乘-累加
mla {cond}{S} 目的寄存器,操作数1,操作数2,操作数3 ;目的寄存器=操作数1 x 操作数2 + 操作数3
mla R0,R1,R2,#10 ;R0=R1 x R2 + 10
(3)UMULL 无符号,乘法(64位无符号乘)
umull R1,R0,R5,R8 ;将R5 R8做无符号相乘,结果 低32位R0中,高32位放到R1中
(4)UMLAL 无符号乘法-累加
(5)SMULL 有符号,乘法
(6)SMLAL 有符号乘法-累加
2. 汇编的转移(跳转)指令
用来实现程序流程的跳转,在RAM中,有两种跳转方式
- 使用专门的跳转指令
- 向程序计数器PC写入跳转地址的值
跳转指令向前或向后可以跳32MB的地址空间,写入跳转地址的值可以实现4GB跳转
ARM指令中,常用的指令有4条:
- B:跳转指令
- BL:带返回的跳转指令
- BLX:带返回和状态切换的跳转指令
- BX:带状态切换的跳转指令
2.1无条件跳转
1----B指令
语法:B label(标记label)
示例:
B Label ;无条件跳转到Label处执行
CMP R1,30
2----BX指令
语法:BX label
如果目标地址不是立即数
示例:
BX Label ;无条件跳转到Label处执行
CMP R1,30
2.2 有条件跳转
1----BL指令
语法:BL label
BL 跳转,在跳转之前,会在寄存器中 保留PC的当前内容。所以,我们可以将之前的内容重新加载到PC内,来返回到跳转指令 之后的 那个指令处执行
示例:
B Label ;无条件跳转到Label处执行
CMP R1,30
2----BLX指令
语法:BLX label
BLX 指令跳转到目标地址,并将处理器的工作状态用ARM状态切换到Thumb状态,并将当前内容保存到寄存器中。
当子程序使用Thumb指令(16位的),(Thumb指令是ARM指令集的子集)。本身使用ARM指令。子集的返回值可以通过
3-----其他指令
如:BEQ label 上面的cmp满足 等于,就跳到label处执行
- BEQ:跳转到 等于的地方
- BGT:跳转到大于的地方
- BLT:跳转到小于的地方
- BLE:跳转到小于等于的地方
- BGE:跳转到大于等于的地方
3. 程序状态寄存器访问指令
3.1 MRS
语法:MRS {cond} 通用寄存器,程序状态寄存器(CPSR或SPSR)
MRS将程序状态寄存器的内容 传到 通用寄存器中,一般2种情况:
(1)需要改变当前程序状态寄存器的内容时,用MRS指令,将程序状态寄存器传递给通用寄存器,修改后再写回程序状态寄存器
(2)异常处理或者切换时,保存程序状态寄存器的值
例如:
MRS r0,CPSR ;传送 CPSR的内容到r0
MRS r0,SPSR ;传送 SPSR的内容到r0
3.2 MSR
语法:MSR {cond} 程序状态寄存器(CPSR或SPSR)的域,操作数
MSR指令将操作数的内容传递到 程序状态寄存器中 的 特定域中
域:用于设置程序状态寄存器,需要使用的操作位。32位状态寄存器分为4个域
位[7:0] 控制位 域,用c表示
位[15:8] 扩展位 域,用x表示
位[23:16] 状态位 域,用s表示
位[31:24] 条件标志位 域,用f表示
示例:
MSR CPSR_c,r0 ;将r0的内容传送给CPSR,仅修改CPSR中的控制位域
MSR CPSR,r0 ;将r0的内容传送给CPSR
4. 加载存储指令
ARM 处理器支持加载、存储指令,用于在寄存器和存储器之间传递数据
存储器在CPU外
寄存器(又称缓存),一般整合在CPU内
加载指令:将存储器中的数据传递到寄存器
存储指令:将寄存器中的数据 存储到存储器
4.1 LDR加载指令
语法:LDR{cond} 目的寄存器,<存储器地址>
示例:
LDR r0,[r1] ;将寄存器地址为r1的字数据 传递到 寄存器r0
LDR r0,[r1,#8] ;将寄存器地址为r1+8的字数据 传递到 寄存器r0
LDR r0,[r1,r2] ;将寄存器地址为r1+r2的字数据 传递到 寄存器r0
LDR r0,[r1],r2 ;先将寄存器地址为r1的值 给r0寄存器,后将r1+r2的值给r1
LDR r0,[r1,r2]! ;将寄存器地址为r1+r2的字数据 传递到 寄存器r0,再将r1+r2的值给r1
LDR r0,[r1] r2,LSL#2 ;先将寄存器地址为 r1的值给r0,后将 r1+r2 x 4 的值给r1
LDR r0,[r1, r2,LSL#2 ]! ;将寄存器地址为 r1+r2 x 4 的值给r0,再将新地址 r1+r2 x 4写入r1
4.2 LDRB
语法:LDRB{cond} 目的寄存器 <存储器地址>
LDRB 用于从存储器中将一个8位的字节数据传送到寄存器内,同时将高位24 位清零
示例:
LDRB r0,[r1] ;将寄存器地址为r1的字数据 传递到 寄存器r0,r0的高24位清零
4.3 LDRH
语法:LDRH{cond} 目的寄存器 <存储器地址>
LDRB 用于从存储器中将一个16位的字节数据传送到寄存器内,同时将高位16 位清零
示例:
LDRH r0,[r1] ;将寄存器地址为r1的字数据 传递到 寄存器r0,r0的高16位清零
4.4 STR存储指令
语法:STR{cond} 源寄存器 <存储器地址>
STR 用于从源存储器中将一个32位的字节数据传送到寄存器内
示例:
STR r0,[r1] ;将r0寄存器的内容 存储到 地址为r1的存储器中
STR r0,[r1],#8 ;将r0寄存器的内容 存储到 地址为r1的存储器中,将新地址r1+8给r1
STR r0,[r1,#8] ;将r0寄存器的内容 存储到 地址为r1+8的存储器中
4.5 STRB存储指令
语法:STRB{cond} 源寄存器 <存储器地址>
STRB 用于将寄存器中一个低16位的字节数据 传送到存储器内
示例:
STRB r0,[r1] ;将r0寄存器的低16位的内容 存储到地址为r1的存储器中
STRB r0,[r1],#8 ;将r0寄存器低16位的内容 存储到 地址为r1的存储中
4.5 批量数据加载、存储指令
ARM支持在一片连续的存储单元和多个存储器之间进行数据传递
LDM批量加载指令
STM批量存储指令
语法:LDM {cond} 基址寄存器{!},寄存器列表{A}
{! }后缀,数据传送完毕后,将最后的地址写入基址寄存器,否则 基址寄存器内容不变
- IA 每次传送 后 地址加1
- IB 每次传送 前 地址加1
- DA 每次传送 后 地址减1
- DB 每次传送 前 地址减1
- FD 满递减堆栈
- ED 空递减堆栈
- FA 满递增堆栈
- EA 空递增堆栈
示例:
LDMFD R13!,{R0,R4-R12,PC} ;将 堆栈内容 读取到 寄存器(R0,R4-R12,LR)
5. 异常产生指令
异常指令 产生软件中断,使用用户程序能够调用系统进程
5.1 SWI
语法:SWI {cond} 24位的立即数
如:
swi 0x02 ;指令调用传统 操作 变量为02 的系统进程
5.2 BKPT
语法:BKPT {cond} 16位的立即数
BKPT 指令用于产生断点中断,用于调试
6. 汇编伪代码
(1)AREA
一个汇编程序至少包含一个段。当程序太长的时候,可以分为多个代码段和数据段
区分代码段的开头,我们使用AREA
语法格式:AREA 段名 属性1,属性2,…
CODE属性:用于定义代码段,默认是readonly
DATA属性:用来定义数据段,默认readwrite
readonly:只读
readwrite:可读可写
ALIGN:使用方式为align表达式,在ELF的代码段或数据段是按字对齐。0-31
COMMON:定义通用段,不包含代码和数据
示例:
AREA Init,CODE,READONLY ;这个伪指令定义一个名为Init,属性为只读的代码(code)段
(2)ALIGN
语法:ALIGN {表达式,{,偏移量}}
align 通过添加填充字节的方式,使当前位置满足对齐方式。2的n次方
示例:
AREA init,code,readonly,align=4 ;指定 后面的指令为16 字节对齐
...
指令序列
...
END
(3)CODE16, CODE32
CODE16 通知 编译器,后面的指令序列 都是16位Thumb 指令
CODE32 通知 编译器,后面的指令序列 都是32位ARM 指令
可以在同个代码段使用
示例:
AREA init,code,readonly
ENTRY ;进入
code32 ;从这开始 为32位 ARM指令
LDR R0,[R1] ;存储[R1]的值,加载到寄存器R0
BX R0 ;跳转到R0存的那个 地址,进行执行,且切换到Thumb 工作状态
code 16 ;从这里开始 是16位 Thumb指令
LDR R1,=0x2A
END ;程序结束
(4)ENTRY
语法:entry
用来指定 汇编程序的 入口 。在一个完整的 汇编程序中 ,至少一个 entry ,可以有多个。但是在源文件中 ,只能有一个entry(或可以没有)
示例:
area init , code, readonly
entry
...
end
(5)END
(6)EQU
equ伪指令,定义的符号名必须是唯一的
用于将一个数值或寄存器名 赋值给一个指定的符号名
程序结尾
代码实例
(1)有两个32位值a,b,存放在存储器地址为 0x80010,0x80014中,现在要实现 c=a+b,c存放在0x80018中。
;此段伪代码用来实现 c=a+b(寄存器的个数非常有限,不能浪费)
AREA MYADD, CODE, READONLY ;声明 名称 和属性
ENTRY ;入口
CODE32 ;从此处开始 是 32 位的ARM
LDR R0, =0x80010 ;变量a的地址 存入R0
LDR R1, [R0],#4 ;将存储器R0地址的值 给 寄存器 R1, 且R0更新为R0+4
LDR R2, [R0],#4 ;将存储器R0地址的值 给 寄存器 R2, 且R0更新为R0+4
ADD R1,R1,R2 ;R1=R1+R2
STR R1,[R0] ;存储R1的值到R0,即存到0x80018的位置
END ;程序结束
(2)有一个32位的符号数x,存放在存储器地址为0x90010
实现:如x>0,y=0;x<0,y=5;将y保存到0x90010中。
AREA PANDUAN, CODE, READONLY
ENTRY
CODE32
START LDR R1, =0x90010
LDR R2, [R1]
CMP R2,#0
BGT PLUS ;大于0则跳转到PLUS LABEL
BLT MINUS
END
PLUS MOV R0,#10 ;将立即数10给R0
STR R0,[R1]
END
MINUS MOV R0,#5
STR R0,[R1]
END
(3)有一个32位带符号数x,存放在存储器地址为0x90010
实现:如x>0,y=1;x=0,y=0;x<0,y=-1;将y保存到0x90014中。
AREA PANDUAN, CODE, READONLY
ENTRY
CODE32
START LDR R1, =0x90010
LDR R2, [R1],#4
CMP R2,#0
BGT PLUS
BEQ EQUAL
MOV R0,#-1
B FINISH
PLUS MOV R0,#1
B FINISH
EQUAL MOV R0,#0
B FINISH
FINISH STR R0,[R1]
END
(4)实现1+2+3+…+10
AREA MYLOOP, CODE, READONLY
ENTRY
CODE32
START LDR R0,=0x90010
MOV R1,#10 ;最终加到10
MOV R2,#1 ;加数起始值为1
MOV R3,#0 ;起始和为0
LOOP ADD R3,R3,R2 ;R3=R3+R2
ADD R2,R2,#1 ;R2=R2+1
CMP R2,R1 ;判断是否加到最后一个数
BGT FINISH ;大于就跳转到结束
B LOOP ;如果不大于,就跳转回去重复执行
FINISH STR R3,[R0]
END
(5)实现1+2x3+3x4+…+10x11,存储结果到0x90010
AREA MYLOOP, CODE, READONLY
ENTRY
CODE32
START LDR R0,=0x90010
MOV R1,#10 ;最终加到10
MOV R2,#2 ;加数起始值为2
MOV R3,#0 ;起始和为1
LOOP ADD R4,R2,#1 ;R4=R2+1
MLA R3,R2,R4,R3 ;R3=R2xR4+R3
ADD R2,R2,#1
CMP R2,R1 ;判断是否加到最后一个数
BGT FINISH ;大于就跳转到结束
B LOOP ;如果不大于,就跳转回去重复执行
FINISH STR R3,[R0]
END
(6)求两个数组DATA1和DATA2对应数据的和,并将这个和放到新数组中。
AREA DEFINEDATA,DATA,READWRITE ;定义数据段,类型为data,可读可写
DATA1 DCD 1,2,-2,-1,4,5,6 ;数组data1
DATA2 DCD 1,2,3,-1,-2,-5,-6 ;数组data2
SUM DCD 0,0,0,0,0,0,0 ;数组sum
AREA MYARRAY,CODE,READONLY ;代码段
ENTRY
CODE32
START LDR R1,=DATA1 ;将数组data1的首地址加载到 R1
LDR R2,=DATA2 ;将数组data2的首地址加载到 R2
LDR R3,=SUM ;将数组sum的首地址加载到 R3
MOV R0,#0 ;计数器R0 初始值为0
LOOP LDR R4,[R1],#04 ;将R1地址的值,传给R4,然后修改R1的地址指针
LDR R5,[R2],#04 ;将R2地址的值,传给R5,然后修改R2的地址指针
ADDS R4,R4,R5 ;相加并影响标志位
STR R4,[R3],#04 ;将相加的值R4存到sum的首个地址中,然后修改R3的地址指针
ADD R0,#1 ;R0计数
CMP R0,#6 ;计数器R0和6比较
BGT FINISH ;大于则跳转到FINISH
B LOOP ;否则重复执行
FINISH END
(7)数组a,b分别存放在 0x40000,0x50000为起始地址的区域内,完成
for(int i=1;i<7;i++){
b[i]=a[i];
if(b[i]==0){
b[i]=-1;
}
}
AREA DEFINEDATA,DATA,READWRITE
A1 EQU 0x40000
B1 EQU 0x50000
AREA MYARRAY,CODE,READONLY ;代码段
ENTRY
CODE32
START LDR R1,=A1 ;将数组A的首地址加载到 R1
LDR R2,=B1 ;将数组B的首地址加载到 R2
MOV R0,#1 ;定义i=1
LOOP LDR R3,[R1],#4
CMP R3,#0
BEQ ZERO
STR R3,[R2],#4
HALF ADD R0,R0,#1
CMP R0,#7
BLT LOOP
B FINISH
ZERO MOV R3,#-1
STR R3,[R2],#4
B HALF
FINISH END
AREA MYARRAY,CODE,READONLY ;代码段
A1 EQU 0x40000
B1 EQU 0x50000
NUM EQU 7
ENTRY
CODE32
START LDR R1,=A1
LDR R2,=B1
MOV R0,#1
LOOP LDR R3,[R1],#4
CMP R3,#0
MVNEQ R3,#0 ;如果上面的判断为0,将-1给R3
STR R3,[R2],#4
ADD R0,R0,#1
CMP R0,#NUM
BLT LOOP
END
(8)查询从存储器0x400000开始的100个数中,值为0的个数。将结果存到0x400200地址中。
AREA ENN,CODE,READONLY
ENTRY
CODE32
START LDR R1,=0x400000
LDR R2,=0x400200
MOV R0,#0 ;计数器1
MOV R4,#1 ;计数器2
LOOP LDR R3,[R1],#4
CMP R3,#0
BQE ZERO
HALF ADD R4,R4,#1
CMP R4,#100
BGT FINISH
B LOOP
ZERO ADD R0,R0,#1
B HALF
FINISH STR R0,[R2]
END