📑 本页目录

计算机系统 · 第六章配套讲义

从一条指令
走到执行结果第六章 · ARM 指令系统

数值在哪里,地址怎么算,谁被写回,标志怎样变,下一条从哪里执行?按这五个问题读懂代码,再把寻址、计算和分支题独立做出来。

覆盖原讲义 6.1—6.4 全部小节及 6.5 的 52 道习题,包含最后七道星号题。页码按独立第六章讲义:正文与习题第 2—40 页,第 1 页为目录。沿用项目既有教材整理版样式。

37 节完整讲解21 幅 SVG 演示52 道习题答题卡12 道综合短自测

编码与条件 → 操作数和地址 → 算术与位运算 → 分支和调用 → 系统、DSP 与浮点

从指令怎样改变状态开始 →

回到第五章:寄存器、权限与异常现场 →

把掌握深度说清楚

重点是读对和算对,再按需要查完整指令表

本章指令多,不要求一遍默写全部编码。每项原书内容都讲到,并区分独立做题的核心与需要认识的背景。对具体代码,先检查目标内核和语法,再写出寄存器、内存、标志及控制流变化。

内容学到什么程度定位
指令集、编码长度、UAL会读格式、判断半字长度;分类表会查01—04
标志、CMP、条件、IT会算 NZCV,选对有/无符号条件,读懂条件块05—07、26、28
寻址、写回、多传输、栈会算 EA/新基址、每个寄存器地址,解释真实编码边界08—14、16
传送、算术、逻辑、移位、位域能写基础指令并手算结果,识别符号与位宽15、19—26、30
调用、零检测与跳转表会追 LR/PC、分清表项地址与目标27—29
非特权、独占、屏障、低功耗与 SVC会解释用途与限制,读懂核心代码17—18、31—33
DSP、打包、FPU 与星号题理解 lane、缩放、符号扩展和浮点步骤;详细变体会查20—21、34—37

重点依据原章习题和后续编程依赖,不代表已知你的考试范围。原题含笔误或跨 A32/T32 的例子时,正文和答题卡都会说明;会解释题意与“这条代码能在 M3/M4 上汇编”是两个检查。

  1. 第一遍按顺序学,先用 SVG 默认例子理解。
  2. 第二遍先遮住结果,自己写 EA、新基址、NZCV、返回地址和数据位串,再换参数核对。
  3. 最后做十二道短自测,再逐题核对 52 道原题;星号题也做,但按标注的原理深度准备。

正文、公式、脚本和 SVG 全部在本文件内。图中是教学模型,不操作真实处理器或电脑内存。答案默认折叠;打印显示全部答案和默认图例。外部手册链接仅供核对,离线阅读不依赖网络。

第六章 · 第 01 节

这一章学什么:把指令读成状态变化

6.1,第 2—3 页;习题 6.1、6.2、6.4—6.6、6.25

掌握要求:能区分指令、指令系统、指令字、汇编表示;读代码时写出数据、地址、标志和 PC 的变化。

背景为什么要讲:第五章给出处理器有哪些状态,本章说明一条条指令怎样改变它们;第七章再组织完整程序。

机器指令是处理器能解释并执行的编码;指令字是表示这条指令的二进制位串;指令系统是某处理器支持的指令集合。汇编语言用助记符、寄存器名、常量、标号等表示操作,由汇编器生成机器码。宏和伪指令可能展开成不同条数的机器指令,不能把所有源代码行都看成一行对应一条硬件操作。

一条指令需要表达操作及必要的操作数/地址信息,有些信息是隐含的,例如 PUSH 隐含使用当前 SP,顺序执行时下一条地址由 PC 与指令长度确定。不是每条指令都显式拥有 Rd、Rn、立即数等全部字段。

本章聚焦 Armv7-M/Armv7E-M 的 Cortex-M3/M4 与 T32。A32、A64 都采用 32 位定长指令编码,T32 使用 16/32 位编码;A64 的“64”主要关联其寄存器和执行架构,不表示每条编码 64 位。M3/M4 不执行 A32/A64。原书第 6.1 节的架构/扩展表是历史定位:A 侧常见虚拟内存 VMSA,R/M 常见保护模型 PMSA,具体产品和扩展仍要核对。

原书列出的 M0/M0+/M1、M3/M4/M7、M23/M33 在基本 Thumb、IT、除法、DSP、FPU、TrustZone 等方面支持不同;Y/N/P/O 分别表示支持、不支持、部分支持、可选。表中指令总数、2019 年版本与扩展清单以查表为主,不用硬背,也不能由“都叫 T32”推断任意指令都兼容。

在本章的 32 位 M profile 语境,字节=8 位,半字=16 位,字=32 位,双字=64 位。双字传输常用一对 32 位寄存器;不意味着整数寄存器突然变成 64 位。

读任何指令,都做四个检查

  1. 操作数是数值,还是地址?是否真正访问内存?
  2. 写了哪个寄存器或内存单元?基址会不会写回?
  3. 哪些标志更新,哪些保留?后面的条件判断读哪一次结果?
  4. PC 顺序前进还是跳转?这条语法在目标内核上是否合法?

下面所有算例均假定相应代码和 RAM 已实现、权限允许;涉及未对齐或不支持的写法会单独指出。重点不是背完所有指令,而是能把这些问题答对。

第六章 · 第 02 节

从第一个半字判断长度,再看编码字段

6.2.1—6.2.2,第 3—5 页;习题 6.3、6.5、6.6

掌握要求:会判断 T32 的 16/32 位长度,知道分类前缀不等于完整合法指令。

背景为什么要讲:原书两张二进制分类表供查读;理解译码入口即可,不必默写每个操作类别的所有位。

T32 指令从半字边界开始。检查当前指令的第一个半字的 bit[15:11]:为 11101、11110、11111 时,表示一条 32 位指令的前半;其他前缀属于 16 位长度的译码入口。识别为 32 位后,还要再取一个半字,不能把第二半字又当新指令起点。

图 6-1只判断长度入口,不能保证完整编码合法;按取指顺序先看低地址半字。

例如 0xE001 的前五位为 11100,按 16 位译码;0xF04F 的前五位为 11110,需要第二半字。判出长度并不证明全部编码合法:其中还有未分配、保留或该内核不支持的组合。

原书 16 位分类入口可以归纳为:00xxxx 常含移位/加减/比较/移动;010000 常规数据处理;010001 特殊数据处理;01001x 为 PC 相对加载;0101xx、011xxx、100xxx 涉及单数据访存;10100x/10101x 生成 PC/SP 相对地址;1011xx 为杂类;11000x/11001x 多存储/多加载;1101xx 条件分支等;11100x 为窄无条件分支。“高六位分类”不是说每条指令都只有固定的六位操作码。还要继续看子字段及特殊保留组合。

32 位分类会结合第一个半字的 op1、op2 及第二半字中的相关位:多寄存器、双字/独占、移位寄存器数据处理、立即数数据处理、分支/杂类、各宽度加载存储、乘法及协处理器类等。原书表 6.5 的作用是展示分层译码,不是要求你靠肉眼完整反汇编所有机器码。

注意字节序:若两个半字按编码顺序为 EA4F、0001,小端内存字节是 4F EA 01 00。不能先把这四字节当一个普通小端 32 位数读成 0x0001EA4F,再去看那个数的最高五位判断第一半字。

第六章 · 第 03 节

汇编语法:逗号、方括号、后缀分别表达什么

6.2.3、6.2.5,第 5—10 页

掌握要求:能识别操作码、操作数、条件、S、.N/.W,区分语法说明符号与真正要输入的符号。

背景为什么要讲:UAL 统一写法便于同一源文件选用不同长度编码;它不让任意参数组合都变合法。

UAL 是 Unified Assembly Language,统一汇编语言。典型写法可读作“助记符及适用后缀 + 操作数列表”,如 ADDS.W R0,R1,R2:加法、更新相应标志、选择宽编码,结果写 R0。

符号/名字 作用 例子
Rd / Rt 目标或传输寄存器占位名 LDR R8,[R1,#4] 中 R8 为加载目标
Rn / Rm 源或基址/偏移寄存器占位名 含义由指令定义决定
# 立即数前缀 #10、#0x10 分别是十进制 10、十六进制 16
[...] 在实际访存语句中表示地址表达式 [R1,#4] 是计算地址去访存
! 相应寻址形式中的基址写回 [R1,#4]!
{R1-R4} 实际代码中的寄存器列表 包含 R1、R2、R3、R4
; 本讲义采用的 Arm 风格注释 GNU 工具需按其注释语法转换

手册中的 <Rd>、{S}、[cond] 等是占位或可选语法说明,不全部照字面输入。尤其不能把手册的“方括号表示可选”与实际 LDR 的“方括号表示地址”混淆。

S 表示按该条指令规则更新标志,不是“所有 APSR 位都重写”。.N/.W 选择窄/宽指令编码,不改变访问的数据宽度;LDR.W 仍可取一个 32 位字,LDRB.W 仍只取一个字节。MOVW 的 W 则是助记符的一部分,和 MOV.W 是两件事。

不是所有指令都能任意加 S、.N、.W 或条件后缀。没有对应编码时汇编器应拒绝,不能靠后缀强迫硬件提供能力。不同工具还会选择不同别名或省略形式,本讲义优先给清楚、可核对的写法。GNU 汇编器的 Arm 语法与操作码说明

第六章 · 第 04 节

MOV、MOVS、MOV.W:看懂同一种操作的不同编码

6.2.5,第 7—10 页

掌握要求:能解释功能、长度与标志三个维度,能把两个半字顺序与小端字节写对。

背景为什么要讲:T1/T2/T3 是某条指令说明里的编码编号,不是整个架构通用的三个指令等级。

以不在 IT 块内的 R1→R0 复制为例:

汇编 编码半字(按取指顺序) 字节数 标志行为
MOV R0,R1 4608 2 不更新算术标志
MOVS R0,R1 0008 2 更新 N/Z;此复制形式保留 C/V
MOV.W R0,R1 EA4F 0001 4 不更新算术标志
MOVS.W R0,R1 EA5F 0001 4 此复制形式更新 N/Z,保留 C/V
图 6-2限定不在 IT 块内、低寄存器间复制,用相应编码展示长度与 S 的差别。半字按取指顺序排列,字节按小端显示。

16 位 MOV 的寄存器字段和支持的寄存器范围与窄 MOVS 不同;MOVS 的窄复制可看作移位量为 0 的相应移位别名。宽 MOV 留出更多编码空间表达寄存器及 S 等选择。同一数据结果可以有不同编码长度和标志副作用。

原书脚注说 MOV 不可能产生某些宽编码,与同页 MOV.W 示例相矛盾,不应照背。T1/T2/T3 要连同具体指令页读取;不能推出“所有 T1 都不改标志,所有 T2 都改标志”。IT 块中的部分窄指令还存在上下文相关的标志行为,因此要看实际编码与上下文。

做编码题先把字段填入相应模板,再拼成半字,最后按需要写内存字节。读程序结果题则先问有没有 S、它究竟更新哪几位,通常不需要背全部机器码。

第六章 · 第 05 节

N、Z、C、V、Q:特别把减法的 C 讲清楚

6.2.4,第 6—7 页;习题 6.8

掌握要求:会算 32 位加减的 N/Z/C/V,解释有无符号是解释方式而非不同加法电路。

背景为什么要讲:后面比较、分支、多字长运算都依赖这里;标志不能靠“结果看起来像负数”猜。

N=结果 bit31,Z=结果全零,C 由相应算术/移位规则决定,V 表示有符号范围溢出。32 位加法结果保留低 32 位,C 记录无符号加法是否产生第 33 位;减法通常按加补码实现,C=1 表示没有借位,即无符号被减数不小于减数。

运算 低 32 位结果 N Z C V 为什么
0xFFFFFFFF+1 0x00000000 0 1 1 0 无符号进位;有符号 −1+1 不溢出
0x7FFFFFFF+1 0x80000000 1 0 0 1 无无符号进位;正数相加越过有符号上限
0−1 0xFFFFFFFF 1 0 0 0 无符号需要借位,有符号结果 −1 合法
0x80000000−1 0x7FFFFFFF 0 0 1 1 无符号没有借位;有符号越过下限
图 6-3假定执行相应更新标志的 ADDS/SUBS。低 32 位结果、无符号进位/无借位、有符号溢出分别计算。

Q 不是普通的“上一条指令是否溢出”:它是相关指令可能置位的黏性饱和/溢出状态,未发生新事件时往往保留旧值;需要按规定清除。M4 的 GE[3:0] 则由部分 SIMD 指令提供逐通道信息,不是普通有符号分支条件 GE 的四位编码。

要读懂“最后一次更新该标志的操作”。LDR/MOV 或无 S 的算术不一定更新;SSAT 常只涉及 Q;MULS 常更新 N/Z 而保留 C/V。不能把所有标志都归给刚执行的最后一条指令。

第六章 · 第 06 节

CMP 后怎样选条件:有符号和无符号不能混

6.2.4、6.4.8—6.4.9;习题 6.7

掌握要求:会用 EQ/NE、HI/LS、CS/CC、GT/LE、GE/LT,能解释 N 与 V 为什么要结合。

背景为什么要讲:条件表要全见过;常用条件必须会选,冷门条件可按标志查表。

CMP R0,R1 按 R0−R1 计算标志,不把差写回 R0。之后比较同样的位串,如果你要按无符号解释,就用 HI/LS、HS/LO 等;如果按补码有符号解释,就用 GT/LE、GE/LT。EQ/NE 仅判断是否相等,两种解释都适用。

条件 判断式 主要意义
EQ / NE Z=1 / Z=0 等于 / 不等于
CS(HS) / CC(LO) C=1 / C=0 无符号 ≥ / <;也用于相应进位条件
MI / PL N=1 / N=0 结果最高位为 1 / 0,非完整有符号大小比较
VS / VC V=1 / V=0 有符号溢出 / 未溢出
HI / LS C=1 且 Z=0 / C=0 或 Z=1 无符号 > / ≤
GE / LT N=V / N≠V 有符号 ≥ / <
GT / LE Z=0 且 N=V / Z=1 或 N≠V 有符号 > / ≤
AL 恒真 无条件;一般省略
图 6-4先按同一次 CMP 生成标志,再分别判断 unsigned 的 HI/HS/LO/LS 与 signed 的 GT/GE/LT/LE。

例:R0=0xFFFFFFFF,R1=1,CMP 后 N=1、Z=0、C=1、V=0。无符号是 4294967295>1,HI 成立;有符号是 −1<1,LT 成立。两者都正确,只是在回答不同问题。

为什么不只看 N?0x80000000−1 得 0x7FFFFFFF,N=0,但原来的有符号 −2147483648 明明小于 1。这是减法发生 V=1,需用 N≠V 判断。原书表 6.6 将 LE 的中文写成无符号,是笔误;LE 是有符号 ≤。

第六章 · 第 07 节

IT 块:直线执行中的条件选择

6.2.5、6.4.9 的条件执行,第 9—10、30—31 页;习题 6.7、6.41

掌握要求:会写 IT、ITT、ITE、ITTE/ITTEE 等,并按 T/E 对照条件;分清条件执行与条件跳转。

背景为什么要讲:用于短小分支,未必比跳转总更快;性能要看路径与实现。

M3/M4 的普通条件数据处理通常须放在 IT 块中。IT{x{y{z}}} cond 最多控制后面四条指令,第一条隐含 T,额外字母依次描述第二、三、四条。T 表示与首条件一致,E 表示取相反条件;不是 E 就永远不执行。

CMP R0, #0
ITE EQ
MOVEQ R1, #10
MOVNE R1, #20

若 R0=0,只执行第一条赋值;否则只执行第二条。源代码中的 EQ/NE 要与 IT 模式匹配。不要在不了解上下文时直接单写 MOVEQ 并假设所有汇编器都自动补 IT;自动插入属于工具选项,不是机器保证。

图 6-5假定块内没有修改条件标志,每一条都是数据处理示意;第一条隐含 T。蓝色表示执行,灰色表示此条件下跳过其数据效果。

ITTEE EQ 控制四条,条件依次是 EQ、EQ、NE、NE;ITETT NE 则是 NE、EQ、NE、NE。字母数算不清时,把“第一 T + 后缀字母”逐项展开。

IT 块不能随意嵌套;改变 PC 的分支等须遵守只能位于块末等限制,某些指令不允许在块内。中途更新标志会影响后续条件,且部分编码有专门限制。入门例子应避免在块内修改条件依赖的 APSR,而不是背成“架构下绝无任何能更新标志的块内情况”。

BEQ 用标志决定改不改 PC,MOVEQ 用条件决定改不改数据寄存器。前者跳转后从另一处取指;后者保持直线位置前进。IT 不需要把每条普通指令都扩出四位条件字段,这正是它与本章编码格式的联系。

第六章 · 第 08 节

立即数:先问是哪条指令的哪一种编码

6.3.1,第 11—12 页;习题 6.10—6.12

掌握要求:能解释字段有限,区分 imm8、imm12、MOVW/MOVT 与修饰立即数,不把一套规则推广给所有指令。

背景为什么要讲:修饰立即数算法用于读编码,日常可借汇编器判断;常见合法/非法例子必须知道原因。

立即数包含在指令自身,MOVS R0,#66 不去 RAM 取 66。32 位编码还要容纳操作码和寄存器等,不可能让每条指令同时直接携带任意 32 位常量,但不同指令有不同表示办法。

形式 典型限制/用途
窄 MOVS 的立即数 8 位,目标通常为低寄存器
MOVW / MOVT 各提供 16 位常量,组合构造 32 位值
ADDW / SUBW 对相应寄存器加减未移位 imm12,即 0—4095 等编码约束
某些宽数据处理指令 采用 Thumb 修饰立即数(modified immediate)
LDR/STR 的地址偏移 按该访问的编码、缩放和写回形式限制,不能套数据运算立即数表

修饰立即数包含一个字节、字节重复(00ab00ab、ab00ab00、abababab)和特定旋转的位图等。精确算法是在旋转分支构造带最高有效 1 的 8 位模式再进行相应 32 位旋转;并非“任意 8 位数在任意 16 位寄存器里转几圈”。零重复组合等还受编码规则限制。

编码查读补充:若 imm12[11:10]=00,由 [9:8] 选择低 imm8 的直接零扩展或上述三种重复(重复分支的 imm8 不能为 0);否则先构造 0x80 | imm12[6:0],再按 imm12[11:7] 给出的量做 32 位 ROR,该分支旋转量为 8—31。理解“有限字段经规则展开”即可,日常常量是否合法可交汇编器核对,不需穷举记忆全部值。

原书的 0x01FC 可由八位模式扩展旋转形成;0x07FC 的连续 1 超出该类位图的表示能力。但 0x07FC 仍可用于 MOVW 或相应 ADDW 等,不能称为所有指令的非法立即数。原书 ADDS R0,R0,#0x1234 也不能直接因为“十六进制不大”就认定合法,应先用 MOVW 装入另一个寄存器,再做寄存器加法。

任意 32 位常量可用 MOVW+MOVT,或 LDR Rd,=常量 伪指令交给工具选择合适序列。伪指令的加载常量不等于从那个数值表示的地址读数据,详见第 14—15 节。

第六章 · 第 09 节

数值与地址:寄存器直接、间接和移位操作数

6.3 概述、6.3.2—6.3.4,第 11—12 页;习题 6.10、6.12、6.13

掌握要求:会把操作数分为立即数、寄存器值、存储器内容;正确区分读内存与参与地址/算术计算。

背景为什么要讲:源文“ADD 直接带 [R2]”等例子跨了指令集边界,需要在这里纠正。

设 R1=0x20000100,那个地址的内存字为 7:MOV R0,R1 得到的是 0x20000100 这个数值;LDR R0,[R1] 才读出内存中的 7。前者为寄存器直接,后者为寄存器间接。存储器地址本身也是一个整数,区别由指令怎么使用它决定。

M3/M4 采用加载/存储分工,普通 ADD 不能写成 ADD R0,R1,[R2]。要先加载:

LDR R3, [R2]
ADD R0, R1, R3

数据处理的移位寄存器操作数,是把寄存器值先移位再参与运算,如 ADD R0,R1,R2,LSL #2 表示 R1+4×R2,没有内存访问,也不把移位结果写回 R2。这里所谓“寻址”是原书宽泛的操作数形成方式。

独立移位指令可用寄存器给移位量,例如 LSL R0,R2,R1;原书的 MOV R0,R2,LSL R1 在支持这种别名的工具中可转成相应寄存器移位指令,本讲义优先写显式 LSL 便于识别。不能由此推断普通 ADD 等也能任意附带寄存器指定移位量。立即数嵌入移位、寄存器控制的独立移位、访存地址中的缩放各有自己的编码限制,应以目标编码核对。

第六章 · 第 10 节

三种索引:先算本次地址,再单独算写回

6.3.5—6.3.7、6.4.2 的前三类模式;习题 6.14—6.19

掌握要求:能分别写出有效地址 EA 与执行后基址 Rn;遇到连续代码不擅自把基址重置。

背景为什么要讲:这是本章寻址题的核心;改变数据宽度不会改变偏移的字节单位。

设执行前基址 B=0x20000100,立即数偏移 d=8,分别独立执行下列指令:

写法 本次 EA 执行后的 R1 口诀
LDR R0,[R1,#8] B+8 B 加偏移访问,不写回
LDR R0,[R1,#8]! B+8 B+8 加偏移访问,且写回
LDR R0,[R1],#8 B B+8 用旧基址访问,之后写回
图 6-6按立即数索引的逻辑模型计算。先求本次 EA,再求新基址;真实映射、权限与非对齐支持仍需检查。

每次都先写两列,内存访问地址与执行后的基址是两个结果。! 不是“再读一遍内存”,逗号放在 ] 外也不是排版差异。偏移可以为负,依具体编码范围;如 −4 表示地址退 4 字节,而非退四个字。

寄存器偏移 LDR R0,[R1,R4] 的 EA=R1+R4,一般不改两个源寄存器。M3/M4 的这种寄存器偏移形式不能随意加 !;原题 6.18 的 LDR R0,[R1,R3]! 可先解释其想表达 EA=旧 R1+R3 并写回,但它不是可直接使用的 T32 写法。可改成明确两条 ADD R1,R1,R3、LDR R0,[R1];这两条整体并非一次不可分割的访问。Arm M3 访存指令说明

原题 6.19 的 0xB 怎么看

如果三行分别从相同 B 开始:偏移形式 EA=B+0xB、不写回;前变址 EA=B+0xB、写回 B+0xB;后变址 EA=B、写回 B+0xB。若三行连续执行,第二行已改变 R1,第三行必须使用那个新值,最终 R1=B+0x16。

0xA/0xB 这类偏移还可能让字访问未对齐。本节先求逻辑 EA;实际是否成功,要结合 Normal/Device、指令和非对齐配置,不能仅凭地址计算正确就断言一定读成功。带写回的加载还要避开基址与目标寄存器重叠等受限组合。

第六章 · 第 11 节

缩放寄存器偏移:数组下标怎样变成字节地址

6.3.5、6.4.2 的寄存器移位模式,第 12—13、20 页;习题 6.14、6.17—6.18

掌握要求:会把元素下标乘元素字节数,用 LSL 形成偏移;基址和下标均不因普通偏移形式而写回。

背景为什么要讲:数组、结构体和查表都要用到;这是桶形移位器参与地址计算的具体用途。

若 R1 指向 32 位数组 a 的首元素,R2=i,则 a[i] 的地址为:

EA=B+i×4=B+(i≪2)
LDR R3, [R1,R2,LSL #2]   ; 读取 a[i] 的一个字

字节数组无需乘 4,半字数组常乘 2。下标本身不是字节偏移;题目若已经给 R2 为字节偏移,再左移 2 位就多乘了一遍。

M3/M4 的这类单数据访存缩放通常支持 LSL #0—3,源寄存器与具体编码另有约束。不能把数据处理中的 ASR/ROR 或任意寄存器给移位量直接塞进这个访存形式。LDRD/STRD 等也不自动具有这里列出的每种寄存器偏移形式。

原书表 6.15 把同一寻址框架展开为 LDRB/LDRSB/LDRH/LDRSH/LDR 与 STRB/STRH/STR。它们主要变化的是传输宽度与加载扩展:地址公式按所写参数算,结果扩展另按指令算。例如 LDRB R8,[R1,#3] 读一个字节,而不是因为 R8 为 32 位就读四字节。

第六章 · 第 12 节

LDM/STM:寄存器号越小,对应地址越低

6.3.8、6.4.2 的多加载/多存储,第 14—15、20—21 页;习题 6.20、6.21

掌握要求:能独立画 IA/IB/DA/DB 的地址表与写回,明确 M3/M4 支持 IA/DB 的边界。

背景为什么要讲:四模式是经典 Arm 的通用模型;原题同时包含本内核不支持的 IB/DA,也要讲清原理。

LDM=Load Multiple,连续内存字加载到多个寄存器;STM=Store Multiple,多个寄存器写到连续内存字。列表中的寄存器可以不连续,但实际映射遵循最低寄存器号对应最低传输地址,不由你书写列表的先后决定。

设 B 为原基址,n 为寄存器数,每字 4 字节:

模式 最低传输地址 最高传输地址 若写回,基址成为
IA B B+4n−4 B+4n
IB B+4 B+4n B+4n
DA B−4n+4 B B−4n
DB B−4n B−4 B−4n
图 6-7原基址固定为 0x20000100。低寄存器号对应低地址;IB/DA 仅作经典模型对照,M3/M4 对应 T32 不提供这些形式。

例:B=0x20000100,列表 {R1-R4},DB 模式对应 R1→0x200000F0、R2→F4、R3→F8、R4→FC。不是 R1 先去 B−4,然后 R2 再去 B−8。原书“每取一个再递减”的口头说明容易在这里导致逆序,应先算整个区间再按寄存器号填。

! 只决定是否把调整后的基址写回,不决定传输地址有没有递增/递减。原题 STMIA R9,{R1-R4} 无 !,传输照常覆盖四个连续字,但 R9 不变。

T32 的 M3/M4 对应多传输使用 IA、DB;STMIB/STMDA 不应当作它们能直接汇编的指令。原题 6.21 仍可按上表解释经典模型,并说明目标内核限制。寄存器列表大小、SP/PC/LR、基址是否也在列表里等均有编码约束,入门例子避免重叠。

第六章 · 第 13 节

PUSH/POP 与 FD 别名:批量压栈不是逐项反序写入

6.3.9、6.4.2 的压栈/出栈,第 15、21 页;习题 6.22—6.24

掌握要求:会写 FD 栈的存取对应关系,算 SP 与各寄存器地址;理解返回地址为何要保存。

背景为什么要讲:接上第五章的满递减栈,后续函数调用必须用得熟练。

M3/M4 的常用栈是满递减:压栈向低地址生长,弹出后 SP 增大。主要别名关系:

含义 常见写法 相应地址模式
压栈 PUSH {R1-R4} STMDB SP!,{R1-R4};也写 STMFD
弹栈 POP {R1-R4} LDMIA SP!,{R1-R4};也写 LDMFD
图 6-8固定保存四个字,旧 SP=0x20001000。图按低地址到高地址显示;POP 后旧比特可仍留在 RAM,但不再属于当前活动帧。

SP 原为 0x20001000,压四个字后 SP=0x20000FF0;从低到高分别保存 R1、R2、R3、R4。一次 PUSH {R1-R4} 与四条“先 R1、再 R2、再 R3、再 R4”的单独 PUSH 布局不同。POP 也按寄存器号与地址映射,不是任意从列表末尾开始。

原题 6.22 的 STMFD SP!,{R1-R8} 与 STMDB 为同一满递减存储含义,n=8、SP 减 32;6.23 的 LTMFD 是拼写错误,应为 LDMFD,与 LDMIA 相应,SP 加 32。

FD、EA 等是“栈的组织名称”,IA、DB 等是“具体多传输地址模式名称”。同样 FD 栈,存时用 DB,取时用 IA。原书 LDMEA/STMEA 作为其他组织的背景,应按“存与取互为恢复”理解,不能只见一个字母就把 LDMDB 当本章标准 POP。

PUSH/POP 的可用寄存器受窄/宽编码限制;涉及 R8 等可能需宽编码。浮点 VPUSH/VPOP 还能保存 S/D 寄存器,64 位 D 视图是传输单位,不因此获得双精度算术。栈区需真实可写、容量足够并维持相应对齐,不能为了少写一条指令破坏调用约定。

第六章 · 第 14 节

PC 相对:地址、该地址的内容和文字池要分三步

6.3.10、6.4.2 的 PC 相对模式,第 15—16、21—22 页;习题 6.24

掌握要求:会用当前指令地址 +4 与相应对齐计算 literal 地址,区分 ADR、LDR 地址形式和 LDR 伪指令。

背景为什么要讲:文字池解决大常量编码问题;下一章才系统学习数据定义和汇编控制。

Thumb 读取 PC 有架构规定的偏移,常见为当前指令地址 A+4;字面量加载和 ADR 等还会使用按字对齐的 PC 基址。不能把源码行号当字节地址,也不能一概把“PC+4”理解为跳过下一条源代码行。

图 6-9展示正向、按字缩放例子的基准计算:A+4,按字对齐,再加偏移。指令地址不带函数指针的 Thumb 标识位。

以一条位于 A=0x08001002 的字面量 LDR 为例:可见 PC=A+4=0x08001006,按字对齐得 0x08001004。LDR R0,[PC,#0xC] 从 0x08001010 读字,而非从 A+0xC 读取。具体偏移范围和正负由编码决定。

写法 得到什么
ADR R0,label 生成附近标号地址,不读取该地址的数据
LDR R0,[R1] 读取 R1 指向的内存字
LDR R0,[PC,#offset] 读取 PC 相对位置的内存字
LDR R0,=0x12345678 伪指令:使 R0 得常量 0x12345678,由工具选择实现

文字池(literal pool,原文也叫文本池)是代码附近存放常量的数据区。若伪指令由 literal LDR 实现,它访问的是池中存常量的地址,不是去 0x12345678 读未知数据。工具也可能把可表示常量转换为其他指令,不保证所有 LDR= 都产生同一序列。

原书 ADR 的 ±4095 等范围对应相关宽编码,窄形式范围、方向和缩放不同;标签距离超界需调整布局。分支的 PC 相对基准与取数据的对齐规则也要分开,见第 27、29 节。

第六章 · 第 15 节

内部传送:MOVW/MOVT、MVN、MRS/MSR

6.4.1,第 16—17 页;习题 6.9、6.35

掌握要求:会构造 32 位常量,知道 MOV 不代替 LDR,特殊 CPU 寄存器不用普通内存访存语法。

背景为什么要讲:指令表每一项都要认识;这几条是系统初始化与常量加载的基础。

MOV 复制寄存器值或装载可编码的立即数,不直接读取任意内存单元。内存到通用寄存器通常用 LDR 系列。因此 MOV R0,[R1] 不能取代 LDR R0,[R1]。

MOVW 写入一个 16 位立即数到目标低半并清零高半;MOVT 修改高 16 位,保留低半。构造 0x12345678:

MOVW R0, #0x5678      ; R0=0x00005678
MOVT R0, #0x1234      ; R0=0x12345678

顺序不能随意交换:先 MOVT 再 MOVW,后者会清掉之前的高半。MVN 则是按位取反,MVN R0,R1 得 ~R1,不等于数学取负 −R1;补码取负通常还要加 1,或用 RSB 从 0 减。

MRS 从特殊寄存器读到一般寄存器,MSR 反向写入,例如 MRS R0,PRIMASK、MSR CONTROL,R0;修改需满足权限与同步要求。SCB/NVIC 的内存映射寄存器则通过地址访问。第五章已区分 CPU 特殊寄存器与 MMIO,此处把对应指令接上。

第六章 · 第 16 节

LDR/STR 的宽度:加载扩展,存储截取

6.4.2、第 17—22 页;习题 6.26—6.28、6.34、6.51、6.52

掌握要求:会根据宽度和有无符号选指令,计算 8/16 位值进入 32 位寄存器后的结果。

背景为什么要讲:原书各寻址模式反复列不同大小,本节用一套规则解释它们,避免机械背重复表。

读取指令 读取多少 高位怎样填 相应写入
LDRB 8 位 零扩展到 32 位 STRB 只写源寄存器低 8 位
LDRSB 8 位 用 bit7 符号扩展 存同样 8 位,不另外分 signed 存储
LDRH 16 位 零扩展到 32 位 STRH 只写低 16 位
LDRSH 16 位 用 bit15 符号扩展 同上
LDR 32 位 已为一个字 STR 写一个字
LDRD 64 位 写入两个 32 位寄存器 STRD 从两个寄存器写双字
图 6-10原始数据必须放得进所选 8/16 位宽。先解释该宽度的符号位,再扩展到 32 位;不额外读取其他内存字节。

同一个内存字节 0x88:LDRB 得 0x00000088,即 136;LDRSB 得 0xFFFFFF88,即 −120。扩展保留原类型的数值意义,不是把负数一律补 1:正的有符号数也补 0。

读 R1+3 的字节到 R8:LDRB R8,[R1,#3];读 R1+4 半字:LDRH R8,[R1,#4];读 R1+8 字:LDR R8,[R1,#8]。若要求有符号字节/半字,要改 SB/SH,不能只改注释。

加载目标仍是 32 位;STRB/STRH 不先把源寄存器变窄,而只取低部去写,其他内存字节不应被当成一起写掉。对齐、支持的地址形式和基址/目标重叠仍需按指令检查。原书 immediate-offset、前变址、后变址、寄存器缩放表都沿用这些数据规则。

第六章 · 第 17 节

带 T 的非特权访存:内核替用户检查访问

6.4.2 的非特权等级加载/存储,第 22 页;习题 6.45

掌握要求:说明这些指令为什么存在,以及它们不会把调用者整体永久降权或给用户提权。

背景为什么要讲:属于系统编程背景,现阶段不必实现完整用户缓冲区 API,但要懂保护边界。

特权操作系统要替用户读写一段缓冲区,不能只凭自己“权限够大”就把用户本不该访问的数据传出去。LDRT/STRT 等允许特权代码按非特权数据访问规则完成该次访问,从而发现不合法的用户地址。

原书列出 LDRBT、LDRSBT、LDRHT、LDRSHT、LDRT,以及 STRBT、STRHT、STRT;B/H、SB/SH 的宽度与扩展含义和前节一致,T 在这里表示该类非特权访问语义。其立即数偏移等限制须查具体编码。

这不把 Handler 变成非特权模式,也不让用户绕过 MPU。数据曾由特权程序写过,不意味着它必然受保护;真正限制来自区域权限与核心系统规则。访问被拒绝可能产生相应 Fault,系统还需设计错误处理,不能把一条访存当完整、无竞争的安全检查协议。

第六章 · 第 18 节

独占访问:STREX 的第一个寄存器是状态

6.4.2 的排他式访问,第 23 页

掌握要求:读懂 LDREX/STREX 的数据与状态寄存器,知道 0 成功、非零失败以及重试。

背景为什么要讲:第五章讲原理,本节补上字节/半字/字与具体操作数顺序。

LDREXB/H、LDREX 分别按字节、半字、字加载并建立相应独占保留;STREXB/H、STREX 尝试条件存储。STREX R2,R1,[R0] 不是把 R2 写出去,而是尝试写 R1,结果状态放 R2。

retry:
    LDREX R1, [R0]
    ADD   R1, R1, #1
    STREX R2, R1, [R0]
    CMP   R2, #0
    BNE   retry

成功状态为 0,失败时该次存储不发生,应重新读、重新计算。CLREX 用于清除本地独占保留,例如放弃一条尝试路径;它不清掉软件定义的“锁变量值”。相应操作需匹配地址、大小、对齐、内存类型,并满足寄存器限制。

完整锁/信号量还需软件协议、必要屏障和系统独占监视支持。跨 DMA/多核的竞争能否被正确监控,取决于芯片;本循环不是任意 MMIO 寄存器都能使用的读改写模板。

第六章 · 第 19 节

加减、进位与借位:把 64 位运算接起来

6.4.3,第 23—24 页;习题 6.9、6.50

掌握要求:会写 ADD/ADC、SUB/SBC 的关系,理解 RSB,能追踪两字组成的 64 位加减。

背景为什么要讲:相同位宽加法通常共用电路;跨字运算必须正确传递低字的 C。

ADD 得 A+B;ADC 得 A+B+C。SUB 得 A−B;SBC 得 A−B−(1−C),不能误写成 A−B−C。RSB(Reverse Subtract)交换相减方向,例如 RSB R0,R1,#0 得 −R1 的补码结果。ADDW/SUBW 主要提供相应直接立即数形式,W 不代表 64 位加法。

两对寄存器 {R1,R0}、{R3,R2} 分别为高字、低字,64 位加法:

ADDS R0, R0, R2       ; 先低字,产生 C
ADC  R1, R1, R3       ; 高字加上低字进位
0x00000001FFFFFFFF+0x0000000200000001=0x0000000400000000
图 6-11两步之间不插入破坏 C 的指令。R1/R3 是高字,R0/R2 是低字;SBC 使用 1−C 作为借位。

64 位减法对应先 SUBS R0,R0,R2,再 SBC R1,R1,R3。例如 0x0000000200000000−0x0000000100000001:低字 0−1 得 FFFFFFFF、C=0;高字 2−1−1=0,结果为 0x00000000FFFFFFFF。

低字与高字之间不要插入会破坏 C 的指令。最终想得到完整 64 位的零/符号等状态,还需按需求组合;只看高字操作的 Z 不足以说明两个字全为零。

第六章 · 第 20 节

MUL、长乘法、MLA/MLS:先决定要留下多少位

6.4.3、6.4.12 的公共乘法表,第 24、36 页;习题 6.9、6.47、6.50

掌握要求:能区分 32 位低结果与 64 位完整结果,读懂 RdLo/RdHi 与累加关系。

背景为什么要讲:乘法是 DSP 的基础,结果位宽和符号解释会决定用哪条指令。

MUL 取 32×32 乘积的低 32 位。对同样的输入位串,有符号与无符号乘法的低 32 位相同,但完整 64 位的高部可能不同;因此不能简单把 MUL 叫成“只会无符号乘法”。有符号结果是否溢出,也不能靠 MUL 不更新 V 来证明没有。

指令 数学含义 结果
MUL Rn×Rm 的低部 32 位
MLA Ra+Rn×Rm 的低部 32 位
MLS Ra−Rn×Rm 的低部 32 位
UMULL / SMULL 无符号 / 有符号完整乘积 RdHi:RdLo 共 64 位
UMLAL / SMLAL 64 位原累加值加相应乘积 64 位
图 6-12同一输入位串,有无符号完整乘积的高部可能不同;MUL 只保留低 32 位,不提供完整溢出判定。

例:位串 FFFFFFFF 与 00000002。UMULL 得 00000001:FFFFFFFE;SMULL 把前者解释为 −1,得 FFFFFFFF:FFFFFFFE。低字一样,高字不同。64 位寄存器对的写法里 RdLo 通常先写在操作数列表,不要因数学显示“高:低”就倒填。

MAC(Multiply-Accumulate,乘并累加)对应点积 ∑xihi,常见于 FIR 滤波、相关、矩阵乘法。MLA 可减少乘后再加的指令,但要选择足够累加位宽。MULS 的某些窄形式可改 N/Z;这些长乘及 MAC 通常不提供普通 S 后缀,不能一概假定更新 NZCV。

第六章 · 第 21 节

除法与定点缩放:同一位串可以代表不同数

6.4.3 与扩展背景;习题 6.48—6.50

掌握要求:区分 SDIV/UDIV,理解整数与定点共用电路的条件,能做简单 Q15 乘法缩放。

背景为什么要讲:星号题仍然讲到,但不要求现在设计完整乘除法器。

UDIV 按无符号除法;SDIV 按有符号补码,商向零截断。例如 SDIV 的 −7/3=−2;若同样的 0xFFFFFFF9 当无符号,商就完全不同。整数低位乘积可以相同,不代表除法也能不分符号。

没有直接给余数的这一对指令,可用商 q 后 MLS Rrem,Rq,Rden,Rnum 求 n−q×d。除零行为取决于 CCR.DIV_0_TRP 等规定,不能把它想成总会得到数学有效值;普通除法也不自动靠 V 报告一切问题。

定点小数把整数 I 解释成 x=I×2−f。若加减两数使用相同缩放,整数加减电路可直接用于定点加减;若缩放不同,先对齐。乘法需调整结果的小数位位置,并考虑舍入、截断和溢出。

Q15 示例:0.5↔16384=0x4000
0x4000×0x4000=0x10000000(30 个小数位)
右移 15 位得 0x2000=8192⇒8192/32768=0.25

这解释了为什么 DSP 需要乘加、算术移位、饱和和符号扩展。浮点则还需处理指数、对阶、规格化与舍入,见第 37 节;不能把定点/浮点的差异说成只换一条注释。

第六章 · 第 22 节

逻辑指令与位掩码:设置、清零、翻转各用什么

6.4.4,第 24 页;习题 6.29—6.33

掌握要求:会用 ORR、BIC、EOR 操作指定 bit,会区分 AND/ORR/ORN/EOR。

背景为什么要讲:寄存器配置、GPIO 和标志集合都依赖掩码;这里处理的是寄存器值,还不自动写回外设。

指令 按位运算 常用目的
AND A & B 只保留掩码允许的位
ORR A | B 掩码为 1 的位置置 1
EOR A XOR B 掩码为 1 的位置取反
BIC A & ~B 掩码为 1 的位置清 0
ORN A | ~B 与“取反后的第二操作数”相或
MVN ~B 全位取反
图 6-13完整结果按 32 位计算,下方仅放大低 8 位。ORN 对第二源全部 32 位取反,不只是图中八位。

本章 bit 编号从 0 开始,bit2 掩码为 1≪2=4:

EOR R0, R0, #4       ; bit2 翻转
ORR R0, R0, #4       ; bit2 置 1
BIC R0, R0, #4       ; bit2 清 0
AND R0, R0, R1       ; R0 与 R1 按位与,结果写 R0

这四行是四个独立要求,不是说应该连续全做一遍。翻转两次会还原;置位和清位重复做则保持同样结果。ORR 与 ORN 的区别是第二操作数是否先取反,ORN 不是 NOR;EOR 也不是 OR。

原书表 6.24 把若干 EOR 行写成 |,应改为 XOR;BIC/ORR/EOR 的真值关系以本表为准。若是在内存映射寄存器上做读改写,还需配合 LDR/STR 并考虑副作用和并发,不能把寄存器内一条运算等同外设原子更新。

第六章 · 第 23 节

LSL、LSR、ASR、ROR、RRX:把移出的位画出来

6.3.4、6.4.5,第 12、25—26 页;习题 6.36—6.38

掌握要求:会算五种移位、移出位与 C,知道算术右移保符号,RRX 使用原 C。

背景为什么要讲:桶形移位器还可参与其他数据处理;零和超过字长的移位属于查规则层,不能忽略。

操作 空位/回填 右移一个位的例子,输入 0x80000001
LSL 低位补 0,高位丢弃 左移 1 得 0x00000002
LSR 高位补 0 0x40000000
ASR 高位复制原符号位 0xC0000000
ROR 移出的低位回到高端 0xC0000000
RRX 只右移 1 位,高端填原 C C=0 得 0x40000000;C=1 得 0xC0000000
图 6-14LSL/LSR/ASR/ROR 按寄存器给量语义展示,不表示每个数都能写进立即数编码。RRX 固定 1 位,忽略移位量选择;原 C 只在 RRX 参与回填。

这组数恰好使 ASR 与 ROR 的结果相同,不代表二者相同;改用 0x80000000,ASR #1 得 C0000000,ROR #1 得 40000000。RRX 把“原 C + 32 位寄存器”当扩展通道,输出的旧 bit0 可用于更新 C;ROR 不以原 C 作为回填位。

带 S 的对应指令按规则更新 N/Z/C,V 通常保留;不带 S 时 APSR 的 C 不因图中出现一个“移出位”就自动改变。RRX 即使不改标志,仍要读取原 C 来填高位。

移位量的边界别用一种规则包办

立即数编码常见 LSL #0—31、LSR/ASR #1—32、ROR #1—31;寄存器给量则使用相应低 8 位,可出现 0、32、>32。寄存器量为 0 常保留结果与 C;LSL/LSR 为 32 时结果 0,C 分别来自原 bit0/bit31,超过 32 时结果与 C 为 0;ASR≥32 则全填符号位。ROR 非零的 32 倍数结果不变但更新 C 的规则与“移位量为 0”不同。

因此“最多只能移 32 位”不是全部寄存器形式的准确限制。日常做题先明确编码/移位形式,再处理边界;图采用寄存器给量的模型并明确 S 开关。

第六章 · 第 24 节

扩展与字节反转:改变表示宽度不等于数值转换

6.4.6,第 26—27 页;习题 6.34、6.51、6.52

掌握要求:会算 SXTB/SXTH/UXTB/UXTH 和 REV/REV16/REVSH,知道符号位来自截取的数据。

背景为什么要讲:访存加载时可扩展,寄存器内部也可扩展;两者分开认识才能看懂 DSP 数据整理。

SXTB/SXTH 取源低 8/16 位做符号扩展,UXTB/UXTH 取同样的低部补零。R0=0x22AA8765 时:SXTB 和 UXTB 都得到 0x65;SXTH 得 0xFFFF8765;UXTH 得 0x00008765。符号取自被截取部分的 bit7/bit15,不是一律看原寄存器 bit31。

这些指令的某些宽形式还可先 ROR #8/#16/#24,再取低部,方便提取另一个字节/半字。保留数值的前提是解释原数据类型;不同类型使用不同扩展,否则 0x88 可能从 −120 误变 136。

图 6-15按高字节到低字节展示寄存器位串,不是画内存地址。REVSH 的高半字由交换后低半字的符号位扩展。

REV 把四字节反序;REV16 在两个半字内部各交换字节;REVSH 只交换低半字的两个字节,再以交换后的 bit15符号扩展。输入 0x12345678:REV 得 0x78563412,REV16 得 0x34127856;输入 0x44558899,REVSH 得 0xFFFF9988。

原书示例 REVH 应写 REV16。这些是字节重排,不是把每个字节的 bit 全部倒序;位序反转用下一节 RBIT。也不是把浮点位串变为其整数数值的转换指令。

第六章 · 第 25 节

位域、CLZ、RBIT:按范围处理一段 bit

6.4.7,第 27—28 页

掌握要求:能根据 lsb 与 width 划范围,手算 BFC/BFI/UBFX/SBFX;知道 CLZ 与 RBIT 的用途。

背景为什么要讲:用于寄存器字段与压缩数据,常比连续掩码移位更直接;不需要背每个二进制操作码。

位域从最低位置 lsb 开始,宽度 width,覆盖 lsb 到 lsb+width−1。必须满足 lsb≥0、width≥1、lsb+width≤32;因此 lsb=0 时可以覆盖 32 位,原书“仅 1—31 位”不完整。

指令 功能
BFC Rd,#lsb,#width 把目标该段清 0,其余位保留
BFI Rd,Rn,#lsb,#width 把 Rn 的低 width 位放进目标该段,其余位保留
UBFX Rd,Rn,#lsb,#width 提取该段到低部,再零扩展
SBFX Rd,Rn,#lsb,#width 提取该段到低部,以字段最高位做符号扩展
CLZ Rd,Rn 从 bit31 向低数连续 0;全 0 返回 32
RBIT Rd,Rn 反转全部 32 位的位序
图 6-16BFC 处理目标,BFI 插入源低部,UBFX/SBFX 提取源字段。CLZ/RBIT 不使用 lsb/width。蓝色标出当前关注的位段。

BFC R0,#4,#8 清 bit4—11。原 R0=0x12345678,清除掩码 0x00000FF0 后得 0x12345008;原书附近文字的 0x1234F00F 与该操作不符。

BFI R1,R0,#8,#16 将 R0 低 16 位插到 R1 的 bit8—23。R0=12345678、R1=AABBCCDD 时,R1 变 AA5678DD,不是把 R0 自己的 bit8—23 原位搬过去。

UBFX/SBFX 若取出 8 位 0xF0,分别得 0x000000F0 和 0xFFFFFFF0。RBIT 输入 1 得 0x80000000;REV 输入 1 得 0x01000000,二者不要混。CLZ 常用于规格化、查找最高有效位等,知道用途即可。

第六章 · 第 26 节

CMP、CMN、TST、TEQ:计算但不保存结果

6.4.8,第 28 页;习题 6.39

掌握要求:能说出隐藏的运算,知道它们更新哪些标志且不覆写源寄存器。

背景为什么要讲:条件逻辑不一定要先产生一个新的数据寄存器结果,这组指令专门留下判断依据。

指令 内部运算 保存什么
CMP A,B A−B 算术 NZCV,差值不写回
CMN A,B A+B 算术 NZCV,和不写回
TST A,B A AND B 相应 N/Z/C,逻辑结果不写回,V 保留
TEQ A,B A XOR B 相应 N/Z/C,逻辑结果不写回,V 保留

CMN 的名字有“negative”,但实际是相加,不是先把 A 取负再比较,也不是直接把 CMP 的所有标志取反。TST 常检测某些 bit 是否为 0;TEQ 可用于位串异或为零的判断。

TST R0, #4
BNE bit2_is_one       ; AND 结果非零,说明 bit2 为 1

它们不再另加 S 后缀。TST/TEQ 的 C 来自相应移位/立即数扩展规则,可能保留也可能更新;不能说“所有测试总会改全部标志”,也不能把它们的 C 当作减法无借位结果。第 06 节的有符号/无符号大小条件应与真正 CMP 的上下文配合。

第六章 · 第 27 节

B、BL、BX、BLX:调用要能找到回来的路

6.4.9,第 28—30 页;习题 6.9、6.40

掌握要求:区分改 PC 与同时建立 LR,解释嵌套调用为什么保存 LR;知道寄存器分支的 Thumb 标识。

背景为什么要讲:完整函数约定在第七章,本节先把控制流和返回链条建立起来。

B 跳到标号,不建立新的返回链接;BL 跳到标号并把返回信息放 LR;BX 按寄存器给出的目标分支;BLX 的相应寄存器形式还建立返回链接。M3/M4 仍在 Thumb,BX/BLX 的合法目标 bit0 应标识 Thumb。

图 6-17示意固定代码地址,f 的原返回链接在调用 g 前已保存。图中采用恢复 LR 再 BX LR 返回的路径;正文 POP 到 PC 是另一种路径。此图不替代完整 ABI、栈布局和异常返回规则。

若 4 字节 BL 位于 0x08000000,下一条地址为 0x08000004,LR 典型得到 0x08000005,低位表示 Thumb。BX LR 回到下一条实际指令,而不是去奇数字节取一条未对齐指令。

函数自身还调用另一个函数,第二次 BL 会覆盖 LR,必须在合适位置保护原返回链接,例如 PUSH {R4,LR}、调用、POP {R4,PC},并遵守对齐/保存约定。B 并非任何情况下都不能用于函数相关控制流:尾调用等可有意不建立新链接;习题要答的是普通可返回调用为什么用 BL/BLX 更方便。

跳转范围按编码查,不能只认助记符

形式 相对 A+4 的典型字节位移范围
B.N −2048 到 +2046
B.W / BL 的宽直接分支 −2²⁴ 到 +2²⁴−2(约 ±16 MiB)
Bcond.N(块外) −256 到 +254
Bcond.W(块外) −2²⁰ 到 +2²⁰−2(约 ±1 MiB)
IT 中受条件控制的宽 B 使用相应无条件分支编码的范围,并遵守块末限制
BX/BLX 寄存器 目标来自寄存器,但仍须有效、可执行、状态合法

原书把普通 B 的范围写成 ±1 MB,混入了宽条件分支范围,应分开。写 PC 的不同指令也有各自规则;第五章 MOV 写 PC 与 BX 的区分仍然适用,不要笼统说“所有 PC 数值必须奇数”。

第六章 · 第 28 节

BEQ、CBZ 与 MOVEQ:同样在判断,走法不同

6.4.9,第 29—31 页;习题 6.41、6.42

掌握要求:会选择标志条件分支与零检测分支,知道 CBZ 不改 APSR、只支持有限的前向范围。

背景为什么要讲:代码变短不总是适用范围变大;要同时检查寄存器、距离和标志依赖。

CMP R0,#0+BEQ label 先更新标志,再根据 Z 跳转;CBZ R0,label 直接检查寄存器是否为 0 并跳转,不修改 APSR。CBNZ 对应非零,原书的 CNBZ 是拼写错误。

CBZ/CBNZ 只允许低寄存器 R0—R7,且为短前向分支;从 PC=A+4 起的位移为 0—126、按 2 字节计,等价于从当前指令 A 看 +4—+130。不能拿它直接做向后的循环回边,也不能放进不允许的 IT 上下文。

普通 BEQ 的目标是标号/相对目标,不是寄存器操作数。原书 BEQ R1 的寄存器跳转解释不成立。若要在 EQ 时跳到 R1 给出的地址,可在合适上下文写 IT EQ 后 BXEQ R1,目标仍须合法,且分支位于块末。

BL 的条件执行在 T32 下也要核对 IT 等规则,不应由 A32 的习惯直接推“任意 BLcond 都自带四位条件字段”。MOVEQ 则通常在 IT 中决定是否写数据,PC 保持顺序推进;二者比较时既要说“条件来自哪里”,也要说“条件满足后改变什么”。

第六章 · 第 29 节

TBB/TBH:查表地址与跳转基准不是一回事

6.4.9 的跳转表,第 31—32 页

掌握要求:能独立完成“算表项地址 → 读取无符号项 → 乘 2 → 加分支 PC”的过程。

背景为什么要讲:用于 switch 等多分支,原书的文字把表基址与跳转基准混在一起,需要明确拆开。

TBB 访问字节表,语法 TBB [Rn,Rm];TBH 访问半字表,语法 TBH [Rn,Rm,LSL #1]。设该条 32 位指令位于 A,表基址 B 在 Rn,下标 i 在 Rm:

TBB 表项地址=B+i,e=Mem8[B+i]
TBH 表项地址=B+2i,e=Mem16[B+2i]
目标地址=A+4+2e
图 6-18表项值作为题目已知条件输入,并不读取电脑内存。先按项宽定位表项,目标始终从分支指令后 A+4 加 2e;还需程序自行检查真实表下标。

表放在哪里由 Rn 决定;跳到哪里从该分支指令后的位置 A+4 起算。表项存的是半字单位的无符号偏移,不是完整 32 位目标地址。原文“相对表首地址跳转”仅在表首恰好也是那个基准时才成立。Arm M4 的 TBB/TBH 指令定义

例:TBB 位于 0x08000100,R0=0x08000200,R1=2,表地址 0x08000202 内的字节是 0x12,则目标=0x08000104+0x24=0x08000128,不是 0x08000224。

TBB 的表项最大 255,对应前向 510 字节;TBH 最大 65535,对应 131070 字节。表项索引距离和目标分支距离是两套量。Rn 可按规则用 PC 指向紧接指令的表,不能用 SP;Rm 不能为 SP/PC,并且要先检查下标范围。它们不修改条件标志;IT 内还须遵守块末等限制。

原书 DCB 用来定义字节数据,具体伪指令到第七章学习。构造表项时用 (目标−分支后基准)/2,再确认偏移非负且放得进相应表项,不能把任意标签差直接塞进去。

第六章 · 第 30 节

SSAT/USAT:输出范围变窄,寄存器仍是 32 位

6.4.10,第 32—33 页;习题 6.43

掌握要求:会按指定宽度计算有符号/无符号饱和值,知道输入解释和 Q 的黏性。

背景为什么要讲:第五章讲削顶原因,本节落实到指令;饱和并不恢复已经丢失的精度。

SSAT 把输入夹到 n 位有符号范围;USAT 把其有符号输入值夹到 n 位无符号输出范围。结果仍放在 32 位寄存器,负的 SSAT 结果会符号扩展。可选移位在饱和前进行,须满足具体编码限制。

SSATn(x)=min(2n−1−1,max(−2n−1,x))
USATn(x)=min(2n−1,max(0,x))
图 6-19输入按 32 位有符号值解释,不加可选预移位。SSAT n=1—32,USAT n=0—31;结果放回 32 位,发生饱和置 Q,否则保留旧 Q。
输入位串(按有符号) SSAT #16 USAT #16
00020000 00007FFF 0000FFFF
00008000 00007FFF 00008000
00007FFF 00007FFF 00007FFF
00000000 00000000 00000000
FFFF8000(−32768) FFFF8000 00000000
FFFF7FFF(−32769) FFFF8000 00000000
FFFFFFFF(−1) FFFFFFFF 00000000

SSAT 的 n 可按规则为 1—32,USAT 为 0—31;USAT #0 的合法输出只有 0。USAT 不是先把 FFFFFFFF 当 4294967295,再夹到最大值,本例它按 −1 夹到 0。

溢出是数学结果超出表示范围的现象,截断/回绕是一种处理结果;饱和是另一种夹到上下限的处理。不能把“丢掉高位”直接当作溢出的完整定义。SSAT/USAT 发生饱和会置 Q,未饱和时通常不清旧 Q,NZCV 并非一并重算。

若先用普通 32 位加法把真实结果溢出丢掉,再做 SSAT #32,已无法恢复那个数学结果;必须在正确位宽和合适阶段使用饱和或更宽中间结果。信号幅度削顶后仍有失真,只是避免回绕导致的突然反向。

第六章 · 第 31 节

屏障指令:顺序、完成、执行上下文再对照

6.4.11,第 33—34 页;习题 6.44

掌握要求:能分别解释 DMB、DSB、ISB,不把它们视为同一种“清空所有东西”。

背景为什么要讲:第五章已有系统含义,本节要求把名字和实际指令对应起来。

指令 核心目的 本章需要会说的话
DMB 约束相关存储器访问顺序 防止需要有序的前后访问被越过
DSB 等待规定范围的先前操作完成再继续 不只排顺序,还要等待完成
ISB 同步后续指令执行上下文 后续按更新后的上下文重新取指/执行

M3/M4 即使顺序执行指令,也可能有写缓冲和互连延迟;指令退休、总线完成、外设内部动作完成不是同一时刻。因此不能因为没有通用乱序执行,就删除所有屏障。

原表将 DMB 概括成“前面全部访问完成才执行后面指令”,过于接近 DSB,且省略访问范围。ISB 也不是保证把全部数据 Cache 写回。具体系统控制、休眠、共享数据协议要按手册选择,volatile 不能替代,DSB 也不替代设备状态轮询。

第六章 · 第 32 节

WFI、WFE、SEV:等待中断与等待事件

6.4.11,第 33—34 页

掌握要求:解释 WFI/WFE 区别,知道 WFE 会消费已记录事件,不保证每次执行都真的休眠。

背景为什么要讲:低功耗不是普通算术题重点,但与中断和实时系统有关,需要知道用途。

WFI=Wait For Interrupt,等待中断相关唤醒条件;WFE=Wait For Event,等待事件;SEV=Send Event,发送事件,作用包括本处理器的事件记录,并可由系统传播给其他处理器。

WFE 有一个简化的事件记录模型:记录为 1 时,执行 WFE 清掉记录并直接继续;记录为 0 时,才可能等待后续事件。不是把所有事件按次数排进 FIFO,已经记录时多个事件可能合并。

图 6-20只展示等待机制的关键区别。实际唤醒与 ISR 响应还受芯片集成、中断条件和屏蔽影响;图中“等待”不保证任何状态下都立即睡眠。

唤醒以后是否立即进入 ISR,与能否从等待中醒来不是同一判断,还涉及 IRQ 使能、屏蔽、优先级和电源/时钟集成。存在已满足的条件时,WFI 也不保证一定停住。原书“立即进入休眠”应按这种条件理解。

深睡眠、异常退出后睡眠等还与 SCB 的 SCR 和芯片电源控制配合。这里只需认识用途和事件记录行为,暂不要求设计无竞争的完整睡眠协议;不能不查芯片就复制一段指令宣称任何外设都能唤醒。

第六章 · 第 33 节

SVC、CPS、BKPT、NOP:系统类指令各管一层

6.4.11,第 33—34 页

掌握要求:知道 SVC 请求服务、CPS 改屏蔽、BKPT 与调试有关,不能把它们当普通分支或任意提权。

背景为什么要讲:这是本章杂类表的其余内容,详细服务与启动程序在下一章延伸。

SVC #imm 产生编号 11 的 SVCall 异常,常用于用户任务请求可信服务。立即数字段可表示服务编号,但内核还需解释参数并实施权限检查。它不是直接把任意地址当函数入口调用,也不保证任何优先级/屏蔽上下文都能正常服务。若当前状态不允许,可能引出错误升级,不能在 NMI/HardFault 里把 SVC 当普通函数调用。

CPSID i              ; 设置 PRIMASK,限制可配置异常激活
CPSIE i              ; 清除 PRIMASK
CPSID f              ; 设置 FAULTMASK
CPSIE f              ; 清除 FAULTMASK

这些操作受特权等规则约束,清某个屏蔽位不等于清所有外设屏蔽和 Pending。临界区应保存恢复原状态,而不是结束一律 CPSIE i;第五章已经给出保存 PRIMASK 的做法。FAULTMASK 的范围与返回清除规则也不能与 PRIMASK 混用。

BKPT #imm 用于断点,按调试配置可能进入停机、调试处理或相应 Fault,没接调试器时不要假定程序会安全暂停等你。NOP 没有所需的数据运算效果,但占指令位置和执行资源,不能凭几个 NOP 得到跨所有时钟配置都准确的延时。

第六章 · 第 34 节

M4 的 SIMD:四个小加法与一个大加法不同

6.4.12 的 SIMD 与表 6.38—6.39,第 35—36 页;习题 6.46

掌握要求:理解 lane 内并行、跨 lane 不传普通加法进位,区分有无符号、饱和和半分系列。

背景为什么要讲:星号题要求理解背景和技术思想,不要求背完数十个变体,但原表各类都要见过。

SIMD=Single Instruction Multiple Data,单指令处理多份数据。图像像素、多个音频采样常用 8/16 位,32 位寄存器可装四字节或两个半字,一条相应指令对各 lane 独立运算。它不等于多核,也不意味着普通 ADD 自动隔断字节之间的进位。

图 6-21按高 lane 到低 lane 展示。ADD32 可跨字节传进位;其他操作在 lane 内独立。并行 Q/UQ 饱和操作不更新 APSR.Q。

原书 ADD8/SUB8 表示四对字节,ADD16/SUB16 表示两对半字;ASX 把第二源高低半字交换后,高通道做加、低通道做减;SAX 则高通道做减、低通道做加。下面前缀决定具体数值处理:

基本操作 S(有符号) U(无符号) Q(有符号饱和) UQ(无符号饱和) SH(有符号半分) UH(无符号半分)
ADD8 SADD8 UADD8 QADD8 UQADD8 SHADD8 UHADD8
SUB8 SSUB8 USUB8 QSUB8 UQSUB8 SHSUB8 UHSUB8
ADD16 SADD16 UADD16 QADD16 UQADD16 SHADD16 UHADD16
SUB16 SSUB16 USUB16 QSUB16 UQSUB16 SHSUB16 UHSUB16
ASX SASX UASX QASX UQASX SHASX UHASX
SAX SSAX USAX QSAX UQSAX SHSAX UHSAX

半分系列在扩展中间运算后按规则除 2/移位,不能先把窄结果截断丢掉进位再平均。例:无符号字节 250+10,正确半分为 130,而不是先回绕成 4 再除 2 得 2。

原书“Q/UQ 前缀的并行饱和运算都更新 Q”不准确。QADD8/QADD16 及相应并行 UQ 系列不改变 Q;SSAT/USAT、标量 QADD 等另按自身规则。S/U 非饱和系列的部分指令更新 GE,判据按有无符号、加减分别规定;不能将 GE 简化为普通“是否负数”位。Arm 作者对并行饱和标志的勘误说明

原表 SUB16 的有符号助记符缺 S,应为 SSUB16。知道命名构成并能解释 lane 独立性即达到本节基本要求,写优化 DSP 程序时再逐条查寄存器、溢出、GE/Q 细节。

第六章 · 第 35 节

M4 的半字乘加:BB、BT、TB、TT 选择哪两半

6.4.12 的乘法/MAC,表 6.40—6.41,第 36—37 页;习题 6.47

掌握要求:按两个后缀字母选择第一、第二源半字,理解有符号乘积与累加位宽。

背景为什么要讲:原书只给部分 DSP 乘加例子,学习命名和适用算法即可,不要求现在实现完整滤波器。

SMULxy 选择两个源的一个 16 位半字做有符号乘法,结果为 32 位;SMLAxy 再加一个 32 位累加数。x 对应第一源,y 对应第二源,B=Bottom(低半字),T=Top(高半字)。

后缀 第一源 第二源
BB 低 16 位 低 16 位
BT 低 16 位 高 16 位
TB 高 16 位 低 16 位
TT 高 16 位 高 16 位

例如 R1=0x0002FFFD、R2=0x00040005。R1 低半字按有符号是 −3,高半字为 2;R2 低为 5,高为 4。SMULBT 得 −3×4=−12;SMULTB 得 2×5=10。若累加数为 100,SMLABT 得 88。

系数和采样可打包到寄存器里,这些指令减少解包与中间操作,适合点积、滤波、相关等。对可能累加很多项的算法,仍要检查 32 位是否足够,必要时选择更长累加形式;部分 DSP 累加还会按规则影响 Q,不能只看名字就推所有标志。

第六章 · 第 36 节

打包和解包:要抽 byte0、byte2,不是最高字节

6.4.12 的表 6.42,第 37 页;联系习题 6.51、6.52

掌握要求:解释 PKHBT/PKHTB 与扩展组合的功能,能追踪哪部分保留、哪部分换入。

背景为什么要讲:SIMD 之前常要准备 lane,原书列举的几条均保留用途;旋转和扩展细节可查。

指令 主要结果
PKHBT 保留 Rn 低半字,与 Rm(可先 LSL)的高半字打包
PKHTB 保留 Rn 高半字,与 Rm(可先 ASR)的低半字打包
SXTB16 取 Rm 的 byte0、byte2,各符号扩展成半字,组成结果
SXTAB16 上述两份扩展值分别与 Rn 两个半字相加
UXTAH Rm(可先 ROR)低半字零扩展,再与 Rn 相加

例:Rn=0x11223344,Rm=0xAABBCCDD。PKHBT 不移位时得 AABB3344;Rm 先 LSL #16 后则取其高半字 CCDD,结果 CCDD3344。PKHTB 若 Rm 先 ASR #16,低半字为 AABB,结果 1122AABB。

SXTB16 选 bit[7:0] 与 bit[23:16],不是最低与最高字节。输入 0x118022FF,两个源字节分别 FF、80,扩展后结果 0xFF80FFFF;原书“最高字节”表述应纠正。可选 ROR #8/#16/#24 会先改变源布局,再按相应位置抽取。

“打包”不是压缩算法,也不自动保护数值范围;它是在寄存器位段中摆放数据。要按 lane 类型决定符号/零扩展,否则后面的 SIMD 即使算得完全正确,处理的数也已经不是你想要的那个数。

第六章 · 第 37 节

浮点指令和星号题:值转换与位串搬运不同

6.4.12 的浮点表 6.43,第 37—38 页;习题 6.48、6.49、6.51、6.52

掌握要求:认识 M4F 单精度运算、传送与转换,解释浮点比定点增加了哪些处理步骤。

背景为什么要讲:本章只要求理解表中用途与硬件区别;具体浮点编码、舍入和异常模式可查规范。

带并启用 FPU 的 M4 支持相应 FPv4-SP-D16 指令,普通 M4 不保证有 FPU。S0—S31 是 32 位寄存器视图,D0—D15 为重叠的 64 位视图。可以传送 64 位数据,不等于支持 F64 算术或任意单/双精度转换。

原表涉及的指令组 意义与正确理解
VADD.F32 / VMUL.F32 / VSQRT.F32 单精度加、乘、平方根
VMLA.F32 乘并累加,按该指令的中间舍入规则
VFMA.F32 融合乘加,组合计算后一次舍入,与 VMLA 不保证逐位相同
VMOV 传送位串;不自动完成整数到浮点的数值转换
VCVT 等转换 按支持的类型做数值/格式转换,如整数与单精度、部分半精度格式
VLDR / VSTR 浮点寄存器与内存间加载/存储,S/D 指定相应传输视图
VPUSH / VPOP 浮点寄存器组压栈/出栈
VSTMIA / VSTMDB 连续多存储,按列表和相应地址模式处理
VMRS 从 FPSCR 等允许的浮点系统状态读出,按语法选择目标

原表写 VMRS.F32 Rt,FPCSR 不规范,常见正确写法为 VMRS R0,FPSCR。浮点比较产生的状态与 CPU 的 APSR 也不是自动完全同一个东西,必要时用规定方式传递;涉及 NaN 的浮点条件不能直接套第 06 节整数比较表。

星号题为什么还要讲浮点电路

定点的比例因子事先约定好,相同尺度的加减可直接用整数电路;浮点的每个数自带符号、指数和有效数。浮点加法需比较指数、对阶、有效数运算、规格化、舍入,再处理零/无穷/NaN 等情况;乘法还需指数运算与结果规格化。差别不仅是“多放一个小数点”。

整数与定点乘积共用电路,仍要按缩放选结果位并处理舍入/溢出;扩展指令则把不同宽度的 signed/unsigned 值送入更宽运算而保持原数值意义。知道这些因果,就能回答原章最后几道星号题,不必在本章手绘完整浮点加法器。

每个原书小节都有去处

完整覆盖表

原书内容对应讲解
6.1指令系统、A32/A64/T32、支持表01
6.2.116 位编码与长度前缀02
6.2.232 位分层编码分类02
6.2.3UAL、操作数与后缀03
6.2.4APSR、条件码05、06
6.2.5MOV 编码和 IT 示例04、07
6.3.1立即数寻址与限制08
6.3.2寄存器直接寻址09
6.3.3寄存器间接寻址09
6.3.4寄存器移位操作数09、23
6.3.5基址与三种偏移10、11
6.3.6前变址/写回10
6.3.7后变址10
6.3.8多寄存器 IA/IB/DA/DB12
6.3.9SP 与栈别名13
6.3.10PC 相对、ADR、文字池14
6.4.1MOV/MOVW/MOVT/MVN/MRS/MSR15
6.4.2九组访存:宽度、三种索引、缩放、多传输、栈、PC、非特权、独占10、11、12、13、14、16、17、18
6.4.3算术、MAC、长乘和除法19、20、21
6.4.4逻辑与位掩码22
6.4.5五种移位与桶形移位器23
6.4.6扩展、旋转与字节反转24
6.4.7位域、CLZ、RBIT25
6.4.8比较/测试26
6.4.9分支、调用、CBZ、IT、跳转表07、27、28、29
6.4.10SSAT/USAT 与波形削顶30
6.4.11屏障、休眠、系统异常与调试31、32、33
6.4.12SIMD 变体、额外 MAC、打包和浮点34、35、36、37
6.552 道习题,含 6.46—6.52 星号题逐题答题卡 →

第 6.1 节的历史支持表、二进制分类入口、冷门指令变体、低功耗与浮点背景均保留用途及掌握边界;重复的访存宽度/索引表用统一规则解释。现阶段不要求自行设计完整锁、OS 服务、浮点加法器或高性能 DSP 库,这些背景用于理解后续程序,不省略它们的存在与原因。

先自己算,再展开

十二道综合短自测

  1. 第一个半字 F04F 与 E001 分别表示几字节指令?还需检查什么?
  2. 32 位 ADDS 的 FFFFFFFF+1,结果及 N/Z/C/V 分别是什么?
  3. CMP 的 80000000−1,有符号 LT 和无符号 HI 哪个成立?为什么不能只看 N?
  4. MOVW R0,#0x5678 后 MOVT R0,#0x1234 得什么?反过来为何不同?
  5. R1=20000100,三种 LDR [R1,#8]、[R1,#8]!、[R1],#8 分别独立执行,写出 EA/新 R1。
  6. STMDB R9!,{R2,R4,R7},原 R9=20000100,三个地址和新 R9 是多少?
  7. 指令在 08001002,LDR R0,[PC,#0xC] 的字面量地址是多少?
  8. 同一内存字节 88 分别经 LDRB/LDRSB 读入,两个结果及数值意义是什么?
  9. 输入 80000000,ASR #1 和 ROR #1 分别是什么?RRXS 输入 80000001、旧 C=0 呢?
  10. R0=12345678,BFC #4,#8 得什么?R1=AABBCCDD,BFI R1,R0,#8,#16 又得什么?两次都使用原 R0。
  11. TBB 在 08000100,表基址 08000200,下标 2,读得表项 12(十六进制)。目标是多少?
  12. M4 的 UADD8 输入 00FF00FF 与 00010001 得什么?UQADD8 又得什么?后者会把 Q 置 1 吗?
展开答案与过关要求
  1. 分别为 4 字节、2 字节;长度识别后还需核对剩余字段/内核支持,不能由前缀证明合法。
  2. 00000000,N=0、Z=1、C=1、V=0。无符号进位,有符号 −1+1 不溢出。
  3. LT、HI 都成立,分别回答 signed/unsigned;结果 7FFFFFFF,N=0、V=1、C=1、Z=0。
  4. 12345678;MOVW 会清高半,顺序反过来最后只剩 00005678。
  5. (20000108,20000100)、(20000108,20000108)、(20000100,20000108)。
  6. R2→200000F4,R4→200000F8,R7→200000FC,新 R9=200000F4。按寄存器号对应递增地址。
  7. 08001010:先 A+4=08001006,再按字对齐为 08001004,最后加 C。
  8. 00000088=136 与 FFFFFF88=−120,后者按 bit7 扩展。
  9. C0000000 与 40000000;RRXS 得 40000000,C=1。
  10. BFC 得 12345008;BFI 得 AA5678DD,插入的是原 R0 低 16 位。
  11. 08000128:基准为分支地址+4,再加表项×2;不是表基址+偏移。
  12. UADD8 得 00000000,逐字节进位信息可见 GE=0101;UQADD8 得 00FF00FF,Q 保留,不能因发生并行饱和就断言置 Q。

过关要求:EA/基址、标志、寄存器结果、跳转目标至少能写出中间过程。出错时回对应讲解,不必重新背整张指令表;星号题能用自己的话解释缩放、lane 与数值解释的原因即可。

原题 [6-1]—[6-52]

52 道习题答题卡

保留原题顺序并归纳题意。先独立作答,再展开核对;原题 6.9 完整填表、四种 STM 模式与最后七道星号题均包含。对于跨内核形式或缺少访问条件的题,限定条件也是答案的一部分。

习题 6.1 / 四个基本名词

解释指令、指令系统、汇编语言、指令字。

展开答题要点

机器指令规定处理器执行的操作;指令系统是支持的指令集合;汇编语言用助记符等描述操作并由工具转换;指令字是对应的二进制编码。伪指令/宏不保证一行对应一条硬件指令。

回到第 01 节完整讲解 →

习题 6.2 / Arm 指令集

Arm 有哪些主要指令集?

展开答题要点

本章沿用 A32、A64、T32 名称:A32/A64 主要为 32 位定长编码,T32 混合 16/32 位;具体内核只实现其架构允许部分,M3/M4 不执行 A32/A64。不能把原书历史表当成所有现今产品完整列表。

回到第 01 节完整讲解 →

习题 6.3 / 区分指令长度

T32 怎样区分 16/32 位编码?

展开答题要点

看当前指令第一个半字的 bit[15:11]:11101、11110、11111 为 32 位指令前半,其余走 16 位译码入口。第二半字不再作为新指令头;识别长度不等于全部编码合法。

回到第 02 节完整讲解 →

习题 6.4 / Word 与 Halfword

本章中的字、半字怎样定义?

展开答题要点

在这里的 32 位 M profile 语境:字=32 位=4 字节,半字=16 位=2 字节;字节为 8 位,双字为 64 位。数据宽度不等于每条指令编码长度。

回到第 01 节完整讲解 →

习题 6.5 / 二进制编码格式

解释指令二进制编码格式。

展开答题要点

规定指令位串中哪些位表达操作、寄存器、立即数、地址方式、条件等,以及如何组合/解释;实际字段由具体指令编码决定,不是一套固定字段能覆盖所有指令。

回到第 02 节完整讲解 →

习题 6.6 / 指令的要素

一条指令需要哪些信息?

展开答题要点

至少要能确定操作及必要的操作数/地址、结果位置和控制流等信息;可显式编码也可隐含,例如 PUSH 使用 SP,普通顺序指令的后继由 PC/长度得到。不是每条都显式列所有源目标。

回到第 01 节完整讲解 →

习题 6.7 / 条件执行

解释条件执行指令。

展开答题要点

按 APSR 条件决定是否产生相应效果。M3/M4 普通条件数据处理常由 IT 块控制,块内后缀必须匹配;条件 B 可直接按其编码检查标志。条件执行不一定改变 PC。

回到第 07 节完整讲解 →

习题 6.8 / N Z C V Q

说明这些标志取值。

展开答题要点

N 为结果最高位,Z 为结果全零;加法 C 为无符号进位,减法 C=1 为无借位;V 为有符号溢出;Q 由相关饱和/DSP 指令按规则置位且常为黏性,未饱和不自动清旧 Q,并非所有带 Q 的并行指令都改它。

回到第 05 节完整讲解 →

习题 6.9 / 19 个助记符填表

补全原题的助记符、英文和中文功能表。

展开答题要点
助记符 英文 中文
ADD Add 加法
ADC Add with Carry 带进位加法
SUB Subtract 减法
MUL Multiply, 32-bit result 乘法,取低 32 位
SDIV Signed Divide 有符号除法
MLA Multiply with Accumulate 乘并累加
MLS Multiply and Subtract 从累加源减去乘积
ASR Arithmetic Shift Right 算术右移
LSR Logical Shift Right 逻辑右移
MOV Move 寄存器/立即数等数据移动
LDR Load Register with word 从内存加载字到寄存器
STR Store Register word 将寄存器字写内存
LDM Load Multiple registers 从连续内存加载多个字
STM Store Multiple registers 多个寄存器字写连续内存
B Branch 跳转
BL Branch with Link 跳转并建立返回链接
BLX Branch with Link and Exchange 按相应寄存器目标跳转并建立链接
CMP Compare 相减比较,只保留标志
IT If-Then 控制后续条件执行块

回到第 03 节完整讲解 →

习题 6.10 / 寻址术语

解释寻址、操作数/指令寻址、立即数与几类寻址。

展开答题要点

寻址是按编码信息确定所需操作数或后继指令位置;立即数内含在指令,寄存器直接使用其中数值,寄存器移位先形成变换后的操作数;间接使用寄存器值作为地址读内存。操作数寻址与下一条指令的寻址要分开。

回到第 09 节完整讲解 →

习题 6.11 / 立即数限制

M3 指令的立即数有什么限制?

展开答题要点

受每种编码的字段与扩展算法限制。窄 imm8、MOVW/T 的 imm16、ADDW 等 imm12、Thumb 修饰立即数、访存缩放偏移规则不同。0x07FC 不适用于某种修饰立即数不表示它对 MOVW/ADDW 也非法。大常量可拆成 MOVW/T 或用 LDR= 伪指令。

回到第 08 节完整讲解 →

习题 6.12 / 操作数的位置

操作数可放在哪里?

展开答题要点

指令自身的立即数、一般/特殊寄存器、数据内存或代码附近常量池等。在哪儿与怎么取由指令定义决定;地址本身也可以是一个寄存器数值,不能把它与目标内存内容混淆。

回到第 09 节完整讲解 →

习题 6.13 / 直接与间接

比较寄存器寻址和寄存器间接寻址。

展开答题要点

直接把寄存器值当操作数,如 MOV R0,R1;间接把寄存器值当内存地址,如 LDR R0,[R1]。前者不因此访问数据内存,后者读取目标内容。

回到第 09 节完整讲解 →

习题 6.14 / 偏移量形式

寄存器偏移/基址偏移的 offset 有几种形式?

展开答题要点

原理上常见立即数、寄存器值、缩放寄存器值。M3/M4 的相应普通访存支持特定 LSL #0—3 等形式;正负、宽度和写回限制按编码,不能将全部数据处理移位都挪来。

回到第 11 节完整讲解 →

习题 6.15 / 三种索引对照

比较偏移、前变址、后变址。

展开答题要点

设原基址 B、偏移 d:普通偏移 EA=B+d、基址不变;前变址 EA=B+d、基址写回 B+d;后变址 EA=B、基址写回 B+d。先写本次 EA,再写执行后基址。

回到第 10 节完整讲解 →

习题 6.16 / 感叹号的意义

解释 [Rn,offset]! 中的 !。

展开答题要点

表示该编码支持的基址写回,访问地址 B+offset 还被更新到 Rn。它不表示重复访问、取反或改变数据宽度;M3/M4 的寄存器偏移 LDR 不可随意加 !。

回到第 10 节完整讲解 →

习题 6.17 / 两条 LDR 的地址

解释 LDR R0,[R1,#0xA];LDR R0,[R1,R4]。

展开答题要点

第一条读 Mem32[R1+0xA] 到 R0;第二条读 Mem32[R1+R4] 到 R0。普通偏移都不写回基址/偏移寄存器。0xA 可能导致字未对齐,要另查内存类型与配置,算出 EA 不等于保证访问成功。

回到第 10 节完整讲解 →

习题 6.18 / 寄存器写回形式的边界

解释 LDR R0,[R9,R1];LDR R0,[R1,R3]!。

展开答题要点

第一条 EA=旧 R9+旧 R1,加载到 R0,不写回。第二条想表达 EA=旧 R1+R3 且写回 R1,但该寄存器偏移带写回不是 M3/M4 的对应 T32 编码;可显式 ADD R1,R1,R3 后 LDR R0,[R1],说明两条的语义与非原子性。

回到第 10 节完整讲解 →

习题 6.19 / 三种 0xB 索引

解释 [R1,#0xB]、[R1,#0xB]!、[R1],#0xB 三种 LDR。

展开答题要点

若各自从同一 B 开始:EA/新 R1 依次为 (B+0xB,B)、(B+0xB,B+0xB)、(B,B+0xB)。若三行连续执行,第三行使用第二行已写回的基址,最终 R1=B+0x16。还需核对字未对齐支持。

回到第 10 节完整讲解 →

习题 6.20 / LDM 语法

解释 LDM{addr_mode} Rn{!},{registers}。

展开答题要点

LDM 从连续内存读多个字;addr_mode 决定整个地址区间,Rn 为原基址,! 为相应写回,列表确定寄存器集合与数量。最低寄存器号对应最低地址;M3/M4 使用相应 IA/DB 编码,具体列表和重叠还有限制。

回到第 12 节完整讲解 →

习题 6.21 / 四种 STM 模式

解释 STMIA R9,{R1-R4};STMIB/STMDA/STMDB R9!,{R1-R4}。

展开答题要点

设 B 为原 R9,列表按 R1、R2、R3、R4 对应以下递增地址:

写法 R1—R4 对应地址 新 R9
IA,无 ! B、B+4、B+8、B+12 B
IB,有 ! B+4、B+8、B+12、B+16 B+16
DA,有 ! B−12、B−8、B−4、B B−16
DB,有 ! B−16、B−12、B−8、B−4 B−16

IB/DA 是经典模型解释,不是 M3/M4 可直接使用的相应 T32 指令。

回到第 12 节完整讲解 →

习题 6.22 / STMFD 与 STMDB

比较 STMFD SP!,{R1-R8} 与 STMDB SP!,{R1-R8}。

展开答题要点

相应满递减存储别名,8 字共 32 字节,最低 R1 在旧 SP−32,最高 R8 在旧 SP−4,新 SP=旧 SP−32。可用寄存器和编码仍须满足目标限制。

回到第 13 节完整讲解 →

习题 6.23 / LDMFD 与 LDMIA

解释原题 LTMFD SP!,{R1-R8} 与 LDMIA SP!,{R1-R8}。

展开答题要点

LTMFD 为笔误,应 LDMFD。满递减栈弹出相应使用 LDMIA:R1 从旧 SP,直到 R8 从旧 SP+28,新 SP=旧 SP+32。不能因为名字 FD 就改用 DB 弹出。

回到第 13 节完整讲解 →

习题 6.24 / 栈与 PC 相对基址

这两类寻址基址分别在哪里?

展开答题要点

栈寻址使用当前 SP(按模式/CONTROL 映射 MSP/PSP);PC 相对用架构规定的 PC 值,Thumb 常为指令地址+4,literal/ADR 等还需按字对齐。不能把当前源码行地址直接当全部相对寻址基准。

回到第 14 节完整讲解 →

习题 6.25 / 指令类别

一般指令集包含哪些类别?

展开答题要点

数据传送、存储器加载/存储、算术、逻辑、移位、格式转换、位域、比较测试、分支调用、系统/异常/屏障/低功耗等;M4 还涉及 DSP/SIMD、打包、额外乘加及可选 FPU。理解每类解决什么问题,无需仅靠数量背目录。

回到第 01 节完整讲解 →

习题 6.26 / 加载字节到 R8

从 R1 数值+3 的地址读取一个字节到 R8。

展开答题要点

LDRB R8,[R1,#3],零扩展。若题目明确要求有符号字节则 LDRSB。这里题面的 [R1] 按寄存器内容理解,不是先读 Mem[R1] 再加 3。

回到第 16 节完整讲解 →

习题 6.27 / 加载半字到 R8

从 R1 数值+4 的地址读取一个半字到 R8。

展开答题要点

LDRH R8,[R1,#4],零扩展;有符号要求则 LDRSH。基址不写回,实际对齐与权限仍检查。

回到第 16 节完整讲解 →

习题 6.28 / 加载字到 R8

从 R1 数值+8 的地址读取一个字到 R8。

展开答题要点

LDR R8,[R1,#8],读取 32 位字,不写回 R1。后缀宽编码与数据宽度不是同一维度。

回到第 16 节完整讲解 →

习题 6.29 / bit2 翻转

把 R0 的第 2 位取反。

展开答题要点

按 bit0 为最低位:EOR R0,R0,#4。掩码=1<<2,不改其他位;重复做两次恢复原值。

回到第 22 节完整讲解 →

习题 6.30 / bit2 置 1

把 R0 的第 2 位置 1。

展开答题要点

ORR R0,R0,#4,掩码对应位为 1 的位置设 1,其余保留。

回到第 22 节完整讲解 →

习题 6.31 / bit2 清 0

把 R0 的第 2 位置 0。

展开答题要点

BIC R0,R0,#4,或用可合法构造的取反掩码做 AND。BIC 不等于把整个 R0 清零。

回到第 22 节完整讲解 →

习题 6.32 / R0 和 R1 按位与

写出 R0 与 R1 按位与指令。

展开答题要点

若结果要求留在 R0:AND R0,R0,R1。若要保留两个源,可用另一目标,如 AND R2,R0,R1;题面未规定结果位置时需说明选择。

回到第 22 节完整讲解 →

习题 6.33 / AND ORR ORN EOR

解释这四条的差别。

展开答题要点

AND=A&B;ORR=A|B;ORN=A|~B;EOR=A XOR B。ORN 不是 ~(A|B),EOR 的公式不能像原表误写成 OR。

回到第 22 节完整讲解 →

习题 6.34 / 符号扩展

什么是符号扩展?

展开答题要点

窄有符号补码转更宽补码时,复制原数据最高符号位填充高位以保持值。8 位 0x88 扩成 32 位为 FFFFFF88(−120);零扩展则 00000088(136)。正的有符号数也补 0。

回到第 24 节完整讲解 →

习题 6.35 / MOV 能否读内存

MOV 可直接从任意内存单元传到寄存器吗?

展开答题要点

本章 M3/M4 普通 MOV 不直接访问任意数据内存;用 LDR 系列。MOV 可搬寄存器/立即数;LDR Rd,=常量又是伪指令,不能把这几种形式混淆。

回到第 15 节完整讲解 →

习题 6.36 / LSR 与 ASR

两种右移有什么区别?

展开答题要点

LSR 高位补 0,适合相应无符号位处理;ASR 高位复制符号位。80000000 右移 1 分别为 40000000、C0000000。ASR 负数移位的舍入也不能随意等同 SDIV 向零截断。

回到第 23 节完整讲解 →

习题 6.37 / LSR 与 ROR

两者的区别?

展开答题要点

LSR 丢掉低端移出位并在高端补 0;ROR 将移出位循环回高端。00000001 右移 1:LSR 得 0,ROR 得 80000000。

回到第 23 节完整讲解 →

习题 6.38 / ROR 与 RRX

两者的区别?

展开答题要点

ROR 在 32 位内部循环,不以原 C 回填;RRX 固定右移 1,原 C 进入 bit31,原 bit0 可成为新 C。是否实际写 APSR 要看 S 等规则。

回到第 23 节完整讲解 →

习题 6.39 / CMP 与 CMN

两者的区别?

展开答题要点

CMP 内部 A−B,CMN 内部 A+B,按相应算术规则更新 NZCV且不保存数据结果。CMN 不是把 CMP 标志逐位取反。

回到第 26 节完整讲解 →

习题 6.40 / 调用为何要 Link

为什么普通函数调用适合 BL/BLX,而普通 B 不方便?

展开答题要点

BL/BLX 除了跳转还建立 LR 返回链接,普通 B 不做这个工作;嵌套调用需保护原 LR。尾调用等场景可以有意使用 B,因此不能绝对说 B 永不能与函数控制流有关。

回到第 27 节完整讲解 →

习题 6.41 / 条件分支与 MOVEQ

比较 B/BL 的条件控制与 MOVEQ。

展开答题要点

条件分支满足后改 PC,BL 相应还建立 LR;MOVEQ 在 IT 等上下文里只条件更新目标数据,PC 顺序推进。T32 的独立 Bcond 与 IT 控制的 BL/普通指令编码不同,不能套 A32 一般化。

回到第 28 节完整讲解 →

习题 6.42 / CBZ 与 CMP+BEQ

CBZ 相比 CMP 后 BEQ 有何异同?

展开答题要点

都可实现寄存器为零则跳;CBZ 不改 APSR、仅 R0—R7、短前向范围且有上下文限制。CMP+BEQ 更新标志且编码选择更灵活,可用于其他距离/方向,但不是所有条件下代码大小时延都相同。

回到第 28 节完整讲解 →

习题 6.43 / 饱和与溢出

解释两者差别。

展开答题要点

溢出是超出表示范围的现象;饱和是把结果夹到上下限的处理方法,截断回绕是另一种结果。饱和仍有精度/波形失真,不能恢复之前已被窄运算丢掉的数学结果。

回到第 30 节完整讲解 →

习题 6.44 / 屏障

定性解释屏障指令作用。

展开答题要点

处理访问顺序、完成和后续执行上下文:DMB 约束相关访问顺序;DSB 等待规定先前操作完成;ISB 同步后续取指执行上下文。顺序执行内核也可能因写缓冲需要它们;volatile 不替代。

回到第 31 节完整讲解 →

习题 6.45 / 非特权访存的用途

为何提供非特权加载/存储?

展开答题要点

让特权系统在替用户访问缓冲区时按用户权限规则执行该次访问,避免仅凭自身高权限泄露/写坏受限区域。LDRT/STRT 等不让用户提权,也不永久改变当前模式;还需完整错误和并发处理。

回到第 17 节完整讲解 →

习题 6.46 / SIMD 背景与思想 *

说明 SIMD 为什么出现、怎样工作。

展开答题要点

音频、图像等包含大量相同操作的短数据,单个大寄存器可容纳多个 lane,一条指令在 lane 内并行处理,减少重复指令与打包开销。它与普通大整数跨字节进位、多核都不同;先准备合适类型与布局。

回到第 34 节完整讲解 →

习题 6.47 / MAC 适合哪些算法 *

乘并累加适用什么算法?

展开答题要点

点积、FIR 滤波、相关、卷积、矩阵乘法等反复求 Σ(xᵢhᵢ) 的算法。注意系数/样本符号、定点缩放和累加宽度,不能因有 MAC 就忽略溢出。

回到第 35 节完整讲解 →

习题 6.48 / 整数与定点共电路 *

什么条件下能用相同电路做整数与定点运算?

展开答题要点

位串运算与缩放解释相容时可共用:相同小数位尺度的加减可直接用整数电路;乘法的结果小数位数相加,须再缩放/舍入;不同尺度需先对齐,并处理有无符号与溢出。

回到第 21 节完整讲解 →

习题 6.49 / 浮点与定点电路 *

浮点运算电路比定点多什么?

展开答题要点

浮点需处理符号、指数与有效数,对阶、规格化、舍入以及特殊值/状态;加法与乘法的指数处理不同。定点比例因子预先约定,但仍需溢出与缩放,不是浮点只多存一个小数点。

回到第 37 节完整讲解 →

习题 6.50 / 有无符号乘除 *

为何常用不同指令处理有无符号乘除?

展开答题要点

同位串解释不同,完整乘积高部与除法商可能不同;低 32 位乘积是模 2³² 的结果,可由同一 MUL 处理,但 UMULL/SMULL 及 UDIV/SDIV 要区分。不能说每种情况下都必须两套不同低位乘法。

回到第 20 节完整讲解 →

习题 6.51 / DSP 为何需要扩展 *

DSP 为什么需要符号扩展和补零?

展开答题要点

短采样/系数搬到宽寄存器计算时要保持原数值。signed 窄值应复制符号位,unsigned 应补零;否则负采样会误作很大的正数或无符号数误作负数,后续 MAC 再准确也处理错了输入。

回到第 24 节完整讲解 →

习题 6.52 / 扩展为何分 signed/unsigned *

为什么数据扩展用不同指令?

展开答题要点

源位串本身不包含独立的类型标签,相同 0x80 可能代表 −128 或 128。符号扩展与零扩展需要不同高位填充规则,指令由程序员/编译器明确选择;不由处理器凭值自动猜类型。

回到第 24 节完整讲解 →

对照原书时别机械照抄

最需要纠正和限定的地方

易误解处核对后的理解定位
所有立即数共用一种旋转规则按各指令编码判断,MOVW/T、ADDW 与修饰立即数不同08
ADD 可以直接带 [R2]先 LDR,再做寄存器算术09
寄存器偏移 LDR 随意加 !M3/M4 对应编码不支持该组合10
DB 从 R1 对应 B−4 开始逆排先算完整区间,最低寄存器号对应最低地址12
STMIB/STMDA 是 M3/M4 现成指令作为经典模型解释,实际 T32 仅相应 IA/DB12
LTMFD、REVH、CNBZ应分别核对为 LDMFD、REV16、CBNZ13、24、28
C=借位;LE=无符号 ≤减法 C=无借位;LE 是有符号 ≤05、06、19
EOR 用或运算公式;MVN 是数学取负EOR 为异或;MVN 为逐位取反15、22
BFC 示例出现冲突数值12345678 清 bit4—11 得 1234500825
B 普通范围 ±1 MB;BEQ 可直接取寄存器目标区分窄/宽/条件编码,寄存器目标用相应 BX27、28
TBB 从表基址加目标偏移表地址由 B 定,目标从指令后 A+4 起算29
USAT 把输入先当无符号;所有 Q 前缀改 QUSAT 输入按有符号;并行 Q/UQ 系列与标量规则分开30、34
SXTB16 抽最低/最高字节抽 byte0 与 byte2,各扩展为半字36
VMRS.F32 / FPCSR;64 位传送等于双精度算术核对 VMRS R0,FPSCR;M4F 数据视图与算术能力分开37

学完后应该能独立做到

把五张“结果单”写出来

  1. 数据结果:传送、扩展、算术、逻辑、移位和位域的最终 32 位值。
  2. 地址结果:EA 与写回分开,多寄存器按完整区间排列,文字池按正确 PC 基准。
  3. 标志结果:NZCV、黏性 Q 与 SIMD GE 按具体指令判断,条件码按数值类型选择。
  4. 控制流结果:条件块执行哪几条,BL 如何建立/覆盖 LR,TBB 怎样形成目标。
  5. 合法性结果:内核支持、立即数字段、寄存器组合、对齐/权限以及工具别名都核对。

基础题需要不看示范写出中间过程;星号题要能解释并行、缩放、扩展与浮点步骤。完整操作码表和冷门变体以查读为主。下一章把这些操作组织成程序,并接上开发工具、伪指令与 C/汇编调用。

整理依据与核对说明

依据用户提供的《计算机原理与嵌入式系统讲义》第六章《ARM 指令系统》及 52 道习题重新讲解。材料中的说明按学习内容处理;本文件沿用项目内前几章的离线 HTML 体例。

主要范围为 M3/M4 的 T32;历史 A32 模型、不同 M 内核、DSP 与可选 FPU 已分别注明。部分编码、非法组合和算例通过独立汇编、反汇编及指令模拟核对;模拟用于检查示例,不构成具体 MCU 时序、权限、外设或功耗的保证。

Arm Cortex-M3 指令参考 · Arm Cortex-M4 指令参考 · GNU Arm 汇编器说明 · 并行饱和等勘误核对

21 幅 SVG 和全部公式保存在本文件中;无需打开外部链接即可阅读和做题。打印使用默认算例并显示全部答案。具体指令行为最终以目标架构、内核版本和工具说明为准。