ZJZJ / ICS NOTES
学习空间整本阅读
COMPUTER SYSTEMS · PEKING UNIVERSITY

04 · 机器级编程基础

来源:第 4 讲;第 5 讲 pp.2~8;小班第 4 讲;教材 §3.1~3.5。

历史与抽象层次#

x86 经 8086 的 16 位、80386 的 IA-32 32 位,发展到 x86-64;AMD64 是对 x86 的 64 位扩展,Intel 的 Itanium/IA-64 是不同路线,不能把 IA-64 当作 x86-64 的别名。历史兼容性解释了寄存器别名与多种指令形式。课件中 Coffee Lake 等参数是历史案例,不是 2026 年最新硬件。

机器码是处理器解码的字节;汇编是可读表示。x86 指令变长,反汇编必须从正确的边界开始。ISA 规定可见语义,不要求“每条指令都只用一个时钟周期”。

AT&T 语法#

指令 源,目的;寄存器加 %,立即数加 $,内存由地址表达式表示。movq $8,%rax 写常数,movq 8,%rax 读取绝对地址 8 的内存,含义完全不同。Intel 语法常是目的在前,不可混读。

后缀 b/w/l/q 对应 1/2/4/8 字节;l 是 longword,q 是 quadword。能从寄存器确定宽度时汇编器可能接受省略后缀;仅有立即数和内存时应明确宽度。寄存器名字必须匹配操作宽度,例如 movq %eax,%rbx 不合法。

全部通用寄存器与局部访问#

64 位 32 位 16 位 低 8 位
rax/rbx/rcx/rdx eax/ebx/ecx/edx ax/bx/cx/dx al/bl/cl/dl
rsi/rdi/rbp/rsp esi/edi/ebp/esp si/di/bp/sp sil/dil/bpl/spl
r8~r15 r8d~r15d r8w~r15w r8b~r15b

历史高字节 ah/bh/ch/dh 是对应低 16 位的高 8 位,不能与需要 REX 前缀的某些操作数组合使用。

**写 32 位通用寄存器会清零对应 64 位寄存器高 32 位;写 8/16 位通常保留其他位。**若 rax 原为 FFFFFFFFFFFFFFFF,movb $1,%al 后为 FFFFFFFFFFFFFF01;movl $1,%eax 后为 0000000000000001。读取 eax 不会自行改写 rax。

寻址与 LEA#

D(Rb,Ri,S) 的有效地址 = D + R[Rb] + S·R[Ri]
S ∈ {1,2,4,8},省略的项按默认规则处理

Rb 是基址,Ri 是索引,D 是有符号位移。比例因子适合常见元素大小。%rsp 不能作为普通 SIB 索引寄存器。位移前不写 $。

设 rdx=0x1000、rcx=3:0x10(%rdx,%rcx,8) 地址为 0x1028。

数据传送和扩展#

普通 mov 支持立即数→寄存器/内存、寄存器→寄存器/内存、内存→寄存器,不支持两个显式内存操作数直接相搬,也不能以立即数为目的。

指令族 含义与细节
movb/w/l/q 同宽传送,不改变条件码
movzbl / movzbq / movzwl / movzwq 零扩展,名字中前后宽度分别指源和目标
movsbl / movsbq / movswl / movswq / movslq 符号扩展
cltq eax 符号扩展到 rax,无显式操作数
cqto 把 rax 符号扩展到 rdx:rax,准备有符号除法
movabsq 可用完整 64 位立即数装入寄存器

没有必要用 movzlq 做 32→64 零扩展,写 32 位寄存器自然清高位。movq 的常见立即数形式使用可符号扩展的 imm32,不能无条件认为任意 64 位立即数都能编码在此形式中;汇编器可能选择其他编码。

算术与逻辑指令#

add S,D 得到 D+S;sub S,D 得到 D-S;二/三操作数 imul 保留目标宽度内的积。inc/dec 加减 1;neg 求补码负值;not 按位取反。and/or/xor 位运算,sal/shl 左移,sar 算术右移,shr 逻辑右移。

变量移位计数通常放在 %cl;64 位移位指令使用计数低 6 位、32 位用低 5 位。这是指令规则,不改变 C 的越界移位规定。

操作 条件码影响(常用规则)
add/sub/neg/cmp 按结果设置相关标志;neg 非零操作数使 CF=1
inc/dec 设置算术结果标志,但保持 CF
and/or/xor/test 更新 ZF/SF/PF;CF=OF=0
mov/lea/not 不修改条件码
shift 计数 0 不改标志;OF 通常只在计数 1 时有定义,须按指令查表

不要把乘除法后的所有标志都当成可用比较结果;例如 imul 主要通过 CF/OF 指示截断,除法后相关算术标志未定义。

双倍宽乘除#

64 位单操作数 mulq S:无符号 rdx:rax = rax*S;imulq S:有符号的 128 位乘积。二操作数 imulq S,D 与单操作数形式不同,不能混淆隐含寄存器。

divq S/idivq S 用 rdx:rax 作为 128 位被除数,商写 rax、余数写 rdx。无符号 64 位被除数通常先清 rdx;有符号先 cqto。除数为零或商超出目标寄存器范围触发除法异常;TMin / -1 是常见边界。idiv 商向零截断,余数与被除数同号或为零。

# long x / long y,x 在 rdi,y 在 rsi
movq %rdi, %rax
cqto
idivq %rsi          # 商 rax,余数 rdx

解读代码的方法#

为每个寄存器写一个符号表达式,逐条替换;只在内存读写时引入 M[address]。例如:

leaq (%rdi,%rdi,2), %rax    # 3*x
leaq (%rax,%rsi,4), %rax    # 3*x + 4*y
subq %rdx, %rax            # 3*x + 4*y - z
ret

计数访问时先看题目是否排除取指。movq (%rdi),%rax 有地址寄存器读取、数据寄存器写入和一次数据内存读取;与“几个操作数”不是同一个计数口径。2025 阶段卷 swap 四条 mov 按其口径共 8 次通用寄存器访问、4 次数据内存访问。

自检#

能检查任意 mov 是否有非法操作数组合;区分地址计算和解引用;追踪子寄存器写入;解释乘除的隐含寄存器;把复杂算式还原为 C,并保留类型/溢出的前提。教材作业 3.58、3.59。