计组与微机控制
3.9 块移动和数据存储
3.9 ????????
引入:块移动是一次在寄存器组和内存之间移动多个字。
普通写法:
LDR r1, [r0], #4LDR r2, [r0], #4LDR r3, [r0], #4块移动可以用更少指令完成。用多条 LDR 从连续内存加载到多个寄存器。
这种写法清楚但冗长。
规律:
每次读取一个字
指针加 4
下次读取下一个字
自动后索引 `[r0],#4` 很适合这种顺序访问。
多次 STR 实现块保存
页通常展示把多个寄存器写回内存。
规律:STR r1, [r0], #4
STR r2, [r0], #4STR r3, [r0], #4每次写一个寄存器,并更新指针。
这就是保存上下文的基础动作。
一.块移动指令:
LDMIA r0, {r3,r4,r5,r9}
STMIA r0, {r3,r4,r5,r9}
含义:
- LDMIA:从 r0 指向地址开始,连续加载多个字到寄存器;
- STMIA:把多个寄存器连续保存到 r0 指向内存。
注意:
LDMIA r0, {...}
如果没有 `!`,r0 不更新。
一般低编号寄存器对应低地址,高编号寄存器对应高地址。


2.栈操作其实就是特殊的块移动:
把一组寄存器保存到 SP 指向的栈区
或者从 SP 指向的栈区恢复一组寄存器
这就是 ARM 中:
STMFD sp!, {r4-r7,lr}
LDMFD sp!, {r4-r7,pc}
3.块移动模式

ARM 常用 Full Descending。Full Descending 栈:
Full Descending 表示:
- 栈向低地址增长;
- SP 指向最后一个已用单元。
入栈时:SP 先减小,再写数据
出栈时:先读数据,SP 再增大
ARM 为 LDM/STM 提供栈操作别名。
常见:
STMFD sp!, {...} ; 入栈 LDMFD sp!, {...} ; 出栈
现代汇编中也常写:
PUSH {...} POP {...}本质都是多寄存器存取。
典型函数入口:
PUSH {r4-r7, lr}典型函数出口:
POP {r4-r7, pc}`POP {pc}` 会把返回地址装入 PC,实现返回。
二.数据存储
本页开启第 4 章。
“广度”指不同 ISA 的设计差异,例如 ARM、MIPS、CISC。
“深度”指从表面指令深入到:
- 栈帧;
- 参数传递;
- 递归;
- 特权模式;
- 异常;
- 数据传送高级操作。
1 .数据存储和栈
本页引入数据存储和栈。
程序运行时数据大致放在:
- 全局/静态区;
- 堆;
- 栈;
- 寄存器;
- 常量池。
栈用于保存生命周期短、随函数调用层次变化的数据。
本页强调栈和函数调用的关系。
每次函数调用,都可能需要一个新的运行环境:
参数 + 返回地址 + 保存寄存器 + 局部变量
这个运行环境就是栈帧。
2.SP和FP
SP 是栈指针,指向当前栈顶。
FP 是帧指针,指向当前栈帧的固定基准位置。
为什么需要 FP?
因为函数执行过程中 SP 可能变化,例如临时入栈、调用其他函数;用 FP 作为稳定基准,可以用固定偏移访问参数和局部变量。
SP 用来分配和释放栈空间
FP 用来稳定访问当前栈帧
典型访问:
LDR r0, [fp, #8] ; 访问参数STR r1, [fp, #-4] ; 访问局部变量正偏移和负偏移取决于栈帧布局。可以用栈指针来访问栈帧中的临时变量。下图,变量XYZ位于栈指针下12个字节处,可以通过有效地址[r13,#12]访问XYZ。因为栈指针会随着其他信息进入或离开栈而移动,最好构造一个带有独立于栈指针的固定指针的栈帧

3.栈帧偏移
本页通常用图展示 FP 上下的内容。
常见约定:
- FP 附近保存旧 FP、LR;
- FP 正偏移访问调用者传来的参数;
- FP 负偏移访问当前函数局部变量。
但具体偏移不固定,要看 ABI 和编译器。
函数返回前必须释放栈帧。
如果没有恢复 SP:
- 返回地址可能读错;
- 调用者栈帧被破坏;
- 程序无法继续正常运行;
- 调试回溯也会失效。
栈帧操作必须成对。
常见对应:
SUB sp, sp, #N PUSH {...} STMFD sp!, {...}ADD sp, sp, #N POP {...} LDMFD sp!, {...}