高级语言里写 for (i = 1; i <= 10; i++) 一行搞定。汇编里没有 for,只有比较 + 跳转。这一篇用 04_loop.asm 打印 1 到 10,把循环拆成你能看见的每一步。

这是「x86 汇编入门」系列的第 4 篇。上一篇实现了算术和数字打印。这一篇通过 04_loop.asm,学习条件跳转指令和循环结构的汇编写法。

一、比较与跳转

cmp a, b 做减法但不保存结果,只设置 CPU 标志位。然后根据标志位跳转:

指令 条件
je 相等 (ZF=1)
jne 不相等
jl 小于(有符号)
jle 小于等于
jg 大于
jge 大于等于
jmp 无条件跳转

二、用跳转拼出循环

04_loop.asm 的逻辑等价于:

1
2
3
for (int i = 1; i <= 10; i++) {
printf("当前数字: %d\n", i);
}

汇编实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mov     r12, 1                  ; 计数器从 1 开始

.loop_start:
cmp r12, 10
jg .loop_end ; i > 10 则退出

; 打印 "当前数字: " + i + 换行
...

inc r12
jmp .loop_start

.loop_end:
; sys_exit

三个要素一目了然:初始化mov r12, 1)、条件判断cmp + jg)、更新与回跳inc + jmp)。

三、为什么计数器用 r12?

前面提过:syscall 会修改 raxrcxr11。循环里每次打印都要调 sys_write,如果计数器放在 rcx,一轮下来值就乱了。

callee-saved 寄存器rbxrbpr12r15)在系统调用前后保持不变,适合当循环变量。

四、运行输出

1
2
make
./build/04_loop
1
2
3
4
5
当前数字: 1
当前数字: 2
当前数字: 3
...
当前数字: 10

五、和高级语言循环对照

C / Python 汇编
i = 1 mov r12, 1
i <= 10 cmp r12, 10 + jg .loop_end
i++ inc r12
回到循环头 jmp .loop_start

没有花指令,就是标签和跳转。理解这一点,以后看编译器生成的汇编会轻松很多。

六、小结

本篇要点:

  • cmp + 条件跳转实现分支
  • jmp 实现循环回跳
  • 循环计数器优先用 callee-saved 寄存器
  • 复用 print_number 打印当前数字

x86 汇编入门系列第 4 篇完。下一篇进入函数调用——call / ret、栈帧,以及用递归算阶乘。