堆栈操作
在数据结构的学习中我们已经栈是先进后出的,汇编实现方面其实是通过将寄存器中的值存储(STR)到memory中来达到push的效果,而pop则是从memory中加载(LDR)值放到寄存器中。
堆栈根据栈顶指针指向的空间是否放值(空/满),以及扩张方向(递增/递减)可分为4种类型:空递增(EA),空递减(ED),满递增(FA),满递减(FD)。ARM用的是FD类型。
FD: 满递减 <----- ARM采用的堆栈形式是: FD满递减堆栈
PUSH X sp-- x -> [sp] POP x [sp] -> x sp++
POP R1 ;单寄存器pop
POP {R1-R3} ;多寄存器pop
代码:
调式过程截图:
单步运行到前三条MOV指令结束:
PUSH后:可见从地址0x2000021C处存储了 R0,R1,R2D的值。并且由满递减方式可以观察到存储顺序是R2,R1,R0
继续运行到POP之前:R0R1R2的值改变。
执行POP指令:R0R1R2的值回到最开始的三个MOV结果
数据处理指令
ARM指令的基本格式
<opcode>{<cond>}{S} <Rd>, <Rn> {, <operand2>}
相关指令
- 数据传送指令
MOV{cond}{S} Rd, operand2 ; Rd <-- operand2
MVN{cond}{S} Rd, operand2 ; Rd <— ~operand2(取反)
- 算数运算指令
加减(乘除先略,相比加减更耗时)
ADD: Rd <— Rn + operand2
ADC: Rd <— Rn + operand2 + xPSR.C
SUB: Rd <— Rn - operand2
SBC: Rd <— Rn - operand2 - !xPSR.C 带借位的减法
RSB 逆向减法指令:
RSB:operand2 - Rn -> Rd
RSC: operand2 - Rn - !xPSR.C -> Rd 带借位的逆向减法
- 逻辑运算指令(按位)
AND: AND 与, Rn & operand2 -> Rd 按位与
ORR: OR 或, Rn | operand2 -> Rd 按位或
EOR: EOR 异或 Rn ^ operand2 -> Rd 按位异或
- 比较指令:不需要加S,直接影响xPSR中的标志位。运算结果不保存
CMP Rn, operand2; 比较Rn与operand2的大小 Rn - operand2
TST Rn, operand2 ;Rn & operand2 用来测试Rn中特定的bit位是否为1.
TEQ Rn, operand2 ; Rn ^ operand2
代码
运行结果
执行完MOV和加减法指令后的结果,可通过寄存器的值检验效果:
执行CMP指令后,C标志变为1,说明减法运算没发生借位,R3>R4:
再往后单步运行到最后,观察到R6,R8的值更新了,佐证了R3>R4,且由其值可知逻辑运算是按位来的
总结
感觉学汇编的时候更能理解机器的视角,对数据的处理主要是内存与寄存器之间的转移(加载/存储),以及在进行算数/逻辑运算的时候看到的都是二进制的01,比较指令其实也是在进行算术运算或逻辑运算。这些简单的运算加上堆栈操作,函数的效果就达到了。