ARM汇编与栈操作指南
1.ROM与RAM的分类
2.立即数
判断条件:
- 如果某个数的数值范围是0~0xFF之间,那么这个数一定是立即数;
- 把某个数展开成2进制,这个数的最高位1至最低位1之间的二进制数序列的位数不能超过8位;
- 这个数的二进制序列凑够8位之后的的右边必须为偶数个连续的 0
3.栈的分类
1. 栈的生长方向:“减” vs “增”
减栈 (Descending Stack):栈在内存中从高地址向低地址方向生长(即“由高到低”)。每次压栈(Push)操作,栈指针的值会减小。
增栈 (Ascending Stack):栈在内存中从低地址向高地址方向生长(即“由低到高”)。每次压栈(Push)操作,栈指针的值会增大。
2. 栈指针的位置:“满” vs “空”
满栈 (Full Stack):栈指针 SP 总是指向最后一个入栈的数据(也就是栈顶元素)。在压栈操作之前,需要先移动SP指向下一个空闲位置,然后再存入数据。
空栈 (Empty Stack):栈指针 SP 总是指向下一个空闲的位置。在压栈操作时,可以直接存入数据,然后再移动SP指向下一个空闲位置。
3. 满减栈 (Full Descending - FD)
这是 最常见 的方式,ARM和许多其他处理器默认采用这种方式。
满 (Full):书记员(SP)的便签上记录的,永远是最后一个摆放了货物的那个格子。
减 (Descending):货架是从编号大的格子向编号小的格子依次摆放货物(栈向低地址增长)。
操作步骤(压栈/PUSH):
先移动书记员:书记员先向下(向编号小的方向)移动一格,并在便签上记下这个新位置。
再摆放货物:然后把新货物放进这个新格子。
(所以他的便签始终指向最后一个有货物的格子)口诀:先减后存
4. 空减栈 (Empty Descending - ED)
空 (Empty):书记员(SP)的便签上记录的,永远是下一个将要摆放货物的空格子。
减 (Descending):货架是从编号大的格子向编号小的格子依次摆放货物(栈向低地址增长)。
操作步骤(压栈/PUSH):
先摆放货物:直接把新货物放进书记员便签当前指着的那个空格子。
再移动书记员:然后书记员向下(向编号小的方向)移动一格,在便签上记下下一个空位置。
(所以他的便签始终指向下一个空位置)口诀:先存后减
5. 满增栈 (Full Ascending - FA)
满 (Full):书记员(SP)的便签上记录的,永远是最后一个摆放了货物的那个格子。
增 (Ascending):货架是从编号小的格子向编号大的格子依次摆放货物(栈向高地址增长)。
操作步骤(压栈/PUSH):
先移动书记员:书记员先向上(向编号大的方向)移动一格,并在便签上记下这个新位置。
再摆放货物:然后把新货物放进这个新格子。
(他的便签始终指向最后一个有货物的格子)口诀:先增后存
6. 空增栈 (Empty Ascending - EA)
空 (Empty):书记员(SP)的便签上记录的,永远是下一个将要摆放货物的空格子。
增 (Ascending):货架是从编号小的格子向编号大的格子依次摆放货物(栈向高地址增长)。
操作步骤(压栈/PUSH):
先摆放货物:直接把新货物放进书记员便签当前指着的那个空格子。
再移动书记员:然后书记员向上(向编号大的方向)移动一格,在便签上记下下一个空位置。
(他的便签始终指向下一个空位置)口诀:先存后增
补充:ARM内核中使用的是满减栈
入栈:stmfd sp!, {r0-r12, lr}
出栈:ldmfd sp!, {r0-r12, lr}
4.ARM汇编语言
1.简单的指令
MOV Rd, #(立即数b); 将立即数b写入到Rd中
LDR Rd, =(任意数c);将任意数c写入Rd中
ADD Rd, Rm, Rn; 将Rm与Rn求和,写入到Rd中
SUB Rd, Rm, Rn;将Rm与Rn作差,写入到Rd中
BIC Rd, Rd, #(立即数); 将Rd指定为清零,写入到Rd中
ORR Rd, Rd, #(立即数); 将Rd指定为置1,写入到Rd中
CMP Rm ,Rn; 比较Rm与Rn的大小
2.STR指令
ARM汇编语言 C语言 STR Rd, [Rm] *Rd = Rm; STR Rd, [Rm, #4] *(Rd + 1) = Rm; STR Rd, [Rm], #4 *Rd++ = Rm; STR Rd, [Rm, #4]! *++Rd = Rm; LDR Rd, [Rm] Rd = *Rm;
3.b, bl, bx指令的区别
b:
b指令类似c语言的goto语句,能够实现无条件跳转。跳转时需要一个lable,表示要跳转到什么地方去,跳转时不修改LR(链接寄存器);
bl:
bl指令跳转时会修改LR,会将跳转指令的下一条指令的地址保存于LR中
bx:
bx指令会将某个通用寄存器当前的值(例如LR中的地址)保存于PC中,因此与bl配合使用可以实现从被调函数中返回到主调函数;
5.函数调用
(1)ARM汇编语言中调用C语言
参数传递:
前4个参数(或前4个32位参数)通过寄存器 R0, R1, R2, R3 传递
第5个及之后的参数通过栈传递(按从右向左的顺序压栈)
64位参数(如
double
或long long
)可能占用两个寄存器返回值:
32位及以下的返回值通过 R0 寄存器返回
64位返回值通过 R0 和 R1 寄存器返回
栈对齐:
栈指针(SP)必须是 8字节对齐 的
寄存器保护:
函数调用会破坏 R0-R3, R12 (IP) 和 R14 (LR)
如果汇编代码需要使用 R4-R11,必须在调用前保存它们
5.函数声明
import 函数名
; 假设要调用C函数: int add(int a, int b, int c, int d, int e); ; 前4个参数通过寄存器传递,第5个通过栈传递.text .global main main:; 保护需要保存的寄存器PUSH {R4, LR} ; 保存R4和返回地址; 设置前4个参数MOV R0, #1 ; a = 1MOV R1, #2 ; b = 2MOV R2, #3 ; c = 3MOV R3, #4 ; d = 4; 第5个参数通过栈传递MOV R4, #5 ; e = 5PUSH {R4} ; 将第5个参数压栈; 调用C函数BL add; 清理栈空间 (移除第5个参数)ADD SP, SP, #4; 恢复寄存器并返回POP {R4, PC}对应的C函数 int add(int a, int b, int c, int d, int e) {return a + b + c + d + e; }
(2)C语言中调用ARM汇编语言
函数声明:
在C代码中使用
extern
声明汇编函数汇编代码中使用
.global
或.globl
导出函数名参数获取:
前4个参数通过 R0, R1, R2, R3 获取
额外参数通过栈获取(使用SP访问)
返回值:
通过 R0 返回32位及以下数据
通过 R0 和 R1 返回64位数据
寄存器保护:
汇编函数必须保护 R4-R11, R13 (SP) 和 R14 (LR)
如果使用这些寄存器,必须在使用前保存,返回前恢复
栈对齐:
必须保持栈指针(SP)的8字节对齐
C代码: // 声明外部汇编函数 extern int multiply(int a, int b);int main() {int result = multiply(5, 7);return result; }汇编代码: .text .global multiply multiply:; 函数入口,保护需要使用的寄存器PUSH {R4, R5} ; 保护R4和R5; 获取参数 (R0 = a, R1 = b)MOV R4, R0 ; 将a保存到R4MOV R5, R1 ; 将b保存到R5; 执行乘法操作MUL R0, R4, R5 ; R0 = R4 * R5; 恢复寄存器并返回POP {R4, R5}BX LR ; 返回到调用者
5.代码练习
//汇编代码area reset, readonly, codepreserve8code32entry;ldr r0, =0x40000000;ldr r1, =0x3456781A;str r1, [r0, #4] ;*(r0 + 1) = r1 ;str r1, [r0], #4 ;*r0++ = r1;str r1, [r0, #4]! ;*++r0 = r1;ldr r0, =0x40000000;ldr r3, [r0];ldr r0, = 0x40000004;ldr r4, [r0];add r5, r3, r4;ldr r0, =0x40000008;str r5, [r0];mov r0, #0;orr r0, r0, #(1 << 7) ;0000 0000 0000 0000 0000 0000 1000 0000;ldr r0, =0x7FFFFFFF ;adds r0, r0, #1;mov r0, #3;mov r1, #5;mov r2, #4;cmp r0, r1;movls r3, r1;movge r3, r0;cmp r3, r2;movls r3, r2;b lable ;branch;mov r0, #3;mov r1, #5
;lable
; mov r0, #5
; mov r1, #3
; mov r2, #4
; cmp r0, r1
; bls less
; bgt great
;great
; mov r3, r0
; b lable
;less
; mov r3, r1 ;lable
; cmp r3, r2
; bgt finished
; mov r3, r2
; mov r0, #1 ;i
; mov r1, #0 ;sum;loop
; cmp r0, #100
; bgt finished
; add r1, r1, r0
; add r0, r0, #1
; b loopldr pc, =_start_asm_maxstmfd sp!, {r0-r12, lr}bl _asm_minldmfd sp!, {r0-r12, lr} cmp r0, r1movge r2, r0movlt r2, r1bx lrexport _asm_min
_asm_mincmp r0, r1movle r2, r0movgt r2, r1mov r0, r2bx lr _startldr sp, =0x40001000mrs r0, cpsr;xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxx0 0000bic r0, r0, #0x1Forr r0, r0, #0x10 ;0001 0000msr cpsr_c, r0ldr sp, =0x40001000sub sp, sp, #1024import mainb main;stmfd sp!, {r0-r12, lr};import c_max;mov r0, #2;mov r1, #3;bl c_max;ldmfd sp!, {r0-r12, lr};mov r0, #11;mov r1, #22finishedb finished end//c代码
int c_add(int a, int b, int c, int d, int e)
{int ret;ret = a + b + c + d + e;return ret;
}int c_max(int a, int b)
{return a > b ? a : b;
}extern int _asm_min(int a, int b);void delay(int n)
{while(n--);
}int main(void)
{int ret;ret = _asm_min(10, 20);while(1){delay(ret);}
}