一. 汇编指令格式
一个完整的ARM指令通常由操作码+操作数组成, 指令的编码格式如下:
{ {s} , {,}}
- 1
- 使用<>标起来的是必选项, 使用{}标起来的是可选项
- opcode是二进制机器指令操作码, 如MOV﹑ADD这些汇编指令都是操作码的指令助记符。
- cond: 执行条件, ARM为减少分支跳转的指令个数,允许类似BEQ、BNE等形式的组合指令。
- S: 是否影响CPSR(状态寄存器)中的标志位, 如SUBS指令会影响CPSR寄存器中的N﹑Z﹑C﹑V标志位,而SUB指令不会。
- Rd: 目标寄存器。
- Rn:第一个操作数的寄存器。
- operand2: 第二个可选的操作数,灵活使用第二个操作数可以提高代码的效率。
二. 存储访问指令
ARM属于RSIC指令集,不能自己对内存里的数据进行直接操作, 只能通过Load/Store的指令来实现, 所以当我们要对内存中的数据进行操作时, 先要将内存中的数据加载到寄存器中, 然后在寄存器中对数据进行处理,最后将结果重新存储在内存中。
寄存器和内存中传输的指令
LDR R1, [R0] ;将R0的值作为地址, 将该地址中的数据保存到R1
STR R1, [R0] ;将R0的值作为地址, 将R1的值存到这个地址空间中.
LDRB/STRB ;每次读取一个字节, LDR/STR默认每次读取4字节.
LDM/STM ;批量加载/储存指令, 在一组寄存器和一片内存之间进行数据传输.
SWP R1,R1,[R0] ;将R1与R0中的地址指向的内存单元的数据进行交换
SWP R1,R2,[R0] ;将[R0]存到R1, 将R2存到[R0]
- 1
- 2
- 3
- 4
- 5
- 6
堆栈格式
FA ->满递增堆栈
FD->满递减堆栈
EA->空递增堆栈
ED->空递减堆栈
LDMFD SP!, {R0-R2,R14} ;将内存中的数据依次加载到R0,R1,R2,R14中
STMFD SP!, {R0-R2,R14} ;将R0,R1,R2,R14中数据依次压入栈中
PUSH {R0-R2,R14} ;将R0,R1,R2,R14依次压入栈中
POP {R0-R2,R14} ;将栈中依次弹出数据到R0,R1,R2,R14中
- 1
- 2
- 3
- 4
- 5
三. 数据传输指令
寄存器和寄存器之间传输的指令, 需要使用MOV, MVN指令,格式如下:
MOV R1 ,#1 ;将立即数1传到寄存器R1中
MOV R1 ,R0 ;将R0寄存器中的值传送到R1寄存器中
MOV PC ,LR ;子程序返回
MVN R0 ,#0XFF ;将立即数0xFF取反后赋值给R0
MVN R0 ,R1 ;将R1取反后赋值给R0
- 1
- 2
- 3
- 4
- 5
四. 算数运算指令
算数运算指令包括加减乘除和与或非等 指令格式如下:
ADD {cond} {S} Rd, Rn, operand2 ; 加法
ADC {cond} {S} Rd, Rn, operand2 ; 带进位加法
SUB {cond} {S} Rd, Rn, operand2 ; 减法
AND {cond} {S} Rd, Rn, operand2 ; 与
ORR {cond} {S} Rd, Rn, operand2 ; 或
EOR {cond} {S} Rd, Rn, operand2 ; 非
BIC {cond} {S} Rd, Rn, operand2 ; 位清除
cond: 执行条件, ARM为减少分支跳转的指令个数,允许类似BEQ、BNE等形式的组合指令。
S: 是否影响CPSR(状态寄存器)中的标志位, 如SUBS指令会影响CPSR寄存器中的N﹑Z﹑C﹑V标志位,而SUB指令不会。
operand2: 第二个可选的操作数,灵活使用第二个操作数可以提高代码的效率。
PS:
ADD R2,R1,#1 ;R2=R1+1
ADC R2,R1,#3 ;R2=R1+3+C //(C是CPSR寄存器中进位)
SUB R2,R1,R0 ;R2=R1-R0
SBC R2,R1,R3 ;R2=R1-R3-C //(C是CPSR寄存器中借位)
AND R2,R0,#3 ;R2=R0&3
BIC R0,R0,#3 ;R0=R0&(~3)
- 1
- 2
- 3
- 4
- 5
- 6
操作数operand2 解析
用法
1.#constant
2.Rm{, shift}
七.在C语言中内嵌汇编代码
为了能在c程序中内嵌汇编代码, ARM编译器在ANSI C标准的基础上扩展了一个关键字__asm. 通过这个关键字, 我们就可以在C程序中内嵌ARM汇编代码. 在C程序中内嵌汇编代码的格式如下.
__asm
{
指令 /*我是注释*/
...
[指令]
}
- 1
- 2
- 3
- 4
- 5
- 6
//main.c
int src[10] = {1,2,3,4,5,6,7,8,9};
int dst[10] = {0};
int data_copy_c(void)
{
for(int i = 0; i < 10; i++)
dst[i] = src[i];
return 0;
}
int data_copy_asm(void)
{
__asm
{
LDR R0, =src
LDR R1, =dst
MOV R2, #10
LOOP:
LDR R3, [R0],#4
STR R3, [R1],#4
SUBS R2, R2, #1
BNE LOOP
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
为了能在C程序中内嵌汇编代码,不同的编译器基于ANSI C标准扩展了不同的关键字,使用的汇编格式可能也可能不太一样,如GUN ARM编译器提供了一个__asm__ 关键字,它的使用方法如下.
__asm__ __volatile__
{
"汇编语句;"
...
"汇编语句;"
}
- 1
- 2
- 3
- 4
- 5
- 6
八.汇编代码调入C语言
在汇编程序中调用C程序, 在调用的时候, 我们要注意根据ATPCS规则来完成参数的通道的传递,并配置好C程序传递参数和保存局部变量所依赖的堆栈环境,然后使用BL指令直接跳转即可.
IMPORT sum
AREA SUM_ASM,CODE,READONLY
EXPORT SUM_ASM
SUM_ASM:
LDR R0, = 0X03;
LDR R1, = 0X04;
BL sum
MOV PC,LR
END
int sum(int a,int b)
{
int result;
result = a + b;
printf("result = %d\n",result);
return result;
}
int main(void)
{
SUM_ASM();
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
在函数调用过程中, 当要传递的参数大于4个数时,除了前4个参数使用寄存器R0-R4传递,剩余的参数要使用堆栈进行传递,这个时候需要编译器通过堆栈指针来进行管理和维护.
评论记录:
回复评论: