Skip to content

运算、分支与循环 笔记

用大白话 + C 语言类比来理解汇编。每节先讲"这东西干嘛的",再给能直接抄的模板。


一、算术运算

1.1 加法和减法

最基本的,就像 C 里 a += ba -= b

asm
add ax, bx       ; ax = ax + bx
sub ax, bx       ; ax = ax - bx

自增自减(对应 C 的 i++ / i--):

asm
inc ax            ; ax++
dec ax            ; ax--

取反(把正数变负数、负数变正数):

asm
neg ax            ; ax = -ax    (比如 ax=3 → ax=-3)

比较两个数(用于后面的分支跳转):

asm
cmp ax, bx        ; 计算 ax - bx,但不保存结果,只改变标志位
                   ; 后面紧跟 jxx 指令来判断大小关系

1.2 乘法

乘法分无符号 mul有符号 imul,结果会放到固定的寄存器里:

asm
; --- 8位 × 8位 → 16位结果 ---
mov al, 5
mov bl, 3
mul bl            ; ax = al × bl = 15

; --- 16位 × 16位 → 32位结果(存在 dx:ax 里)---
mov ax, 100
mov bx, 200
mul bx            ; dx:ax = ax × bx = 20000
                  ; dx 放高16位,ax 放低16位

简单记:8位乘法看 al,结果在 ax;16位乘法看 ax,结果在 dx:ax

1.3 除法

除法分无符号 div有符号 idiv商和余数分别存在不同寄存器

asm
; --- 16位 ÷ 8位 ---
mov ax, 17        ; 被除数放 ax
mov bl, 5         ; 除数放 bl
div bl            ; al = 17/5 = 3(商),ah = 17%5 = 2(余数)

; --- 32位 ÷ 16位 ---
mov ax, 17        ; 被除数放 dx:ax
mov dx, 0         ; ← 别忘了清零 dx!(无符号除法)
mov bx, 5
div bx            ; ax = 商,dx = 余数

有符号除法时,要用扩展指令把符号位填到高位:

asm
mov ax, -10       ; 被除数
cwd               ; ★ 把 ax 符号扩展到 dx:ax(dx 全填符号位)
mov bx, 3
idiv bx           ; ax = -3(商),dx = -1(余数)

扩展指令口诀:
cbw = byte→word(al → ax)
cwd = word→dword(ax → dx:ax)
cdq = dword→qword(eax → edx:eax)


二、逻辑运算与移位

2.1 逻辑运算

和 C 语言的位运算一模一样:

asm
and ax, bx        ; ax = ax & bx  (按位与,常用来"清零"某些位)
or  ax, bx        ; ax = ax | bx  (按位或,常用来"置1"某些位)
xor ax, bx        ; ax = ax ^ bx  (按位异或)
not ax            ; ax = ~ax      (按位取反)

两个常用技巧

asm
xor ax, ax        ; 快速把 ax 清零(比 mov ax, 0 更快)
test ax, 1        ; 检查 ax 最低位是不是 1(奇偶判断),配合 jnz/jz

test 就像 and,但只影响标志位、不修改操作数。

2.2 移位

移位 = 把所有二进制位往左或右挪:

asm
shl ax, 1         ; 逻辑左移1位 → ax = ax × 2
shl ax, 3         ; 左移3位     → ax = ax × 8  (需要 .386)
shr ax, 1         ; 逻辑右移1位 → ax = ax / 2  (无符号,高位补0)
sar ax, 1         ; 算术右移1位 → ax = ax / 2  (有符号,高位补符号位)

简单记:左移 = 乘2,右移 = 除2
shl/shr 给无符号数用,sar 给有符号数用。
移位次数要么写常数,要么放 cl 里:shl ax, cl


三、分支(if-else 怎么写)

汇编没有 if,要用 cmp + 条件跳转 来实现。

3.1 核心思路

cmp a, b     ← 比较
jxx label    ← 根据比较结果,决定跳不跳

3.2 跳转指令选哪个?

对照 C 语言来记最简单:

无符号数(把数当正数看)—— 用 above / below:

C 语言写法汇编跳转助记
a > bjajump if above
a >= bjaeabove or equal
a < bjbjump if below
a <= bjbebelow or equal

有符号数(有正有负)—— 用 greater / less:

C 语言写法汇编跳转助记
a > bjgjump if greater
a >= bjgegreater or equal
a < bjljump if less
a <= bjleless or equal

等于/不等于(通用):

C 语言写法汇编跳转助记
a == bjejump if equal
a != bjnenot equal

其他:jz(结果为零)、jnz(结果非零)、jc(有进位)、jnc(无进位)、jo(溢出)、jno(无溢出)

3.3 可以直接套的模板

if-else

asm
; ---- C 语言:if (ax > bx) { A } else { B } ----

    cmp ax, bx
    jle else_part     ; 如果 ax <= bx,跳去 else(注意:条件取反!)
    ; ---- A: then 部分 ----
    jmp end_if        ; 跳过 else
else_part:
    ; ---- B: else 部分 ----
end_if:

关键技巧jxx 里的条件要写成"不满足时跳走",也就是把 C 的条件取反

例如 C 里 if (ax > bx) → 汇编里写 jle else(不大于就跳走)。

if 不带 else(更简单):

asm
; ---- C 语言:if (ax == 0) { A } ----

    cmp ax, 0
    jne skip          ; ax != 0 就跳过
    ; ---- A ----
skip:

多分支(switch-case 风格)

asm
; ---- C 语言:if (ax==1) {A} else if (ax==2) {B} else {C} ----

    cmp ax, 1
    je do_A
    cmp ax, 2
    je do_B
    jmp do_C          ; 都不是,走默认

do_A:
    ; ---- A ----
    jmp done
do_B:
    ; ---- B ----
    jmp done
do_C:
    ; ---- C ----
done:

求较大值 max(ax, bx) → ax

asm
    cmp ax, bx
    jge done          ; ax 已经 >= bx,不用动
    mov ax, bx        ; 否则把 bx 给 ax 
done:

四、循环(for / while 怎么写)

4.1 loop 指令(最常用)

loop = 把 cx 减 1,如果 cx 不为 0 就跳回去。对应 C 的 for (int i = N; i > 0; i--)

asm
; ---- 循环 N 次 ----
    mov cx, N         ; cx = 循环次数
again:
    ; ---- 循环体(随便写)----
    loop again        ; cx--,cx≠0 就跳回 again

实战:计算 1+2+...+100

asm
    xor ax, ax        ; ax = 0,用来累加
    mov cx, 100       ; 循环 100 次
sum:
    add ax, cx        ; ax += cx(cx 从 100 递减到 1)
    loop sum
    ; 结束后 ax = 5050

⚠️ 小心loop 是先 cx-- 再判断,所以如果进入循环前 cx=0,会循环 65536 次!

4.2 while 循环

没有专门指令,用 cmp + jxx + jmp 自己搭:

asm
; ---- C 语言:while (ax != 0) { ax-- } ----

while_start:
    cmp ax, 0
    je while_end      ; ax == 0 → 退出循环
    dec ax            ; ax--(循环体)
    jmp while_start   ; 跳回开头继续判断
while_end:

4.3 do-while 循环

循环体至少执行一次,判断放在最后:

asm
; ---- C 语言:do { ax-- } while (ax != 0) ----

do_start:
    dec ax            ; 循环体
    cmp ax, 0
    jne do_start      ; ax != 0 → 继续循环

4.4 嵌套循环

loop 只认 cx,所以嵌套时要用栈保存外层的 cx

asm
; ---- 外层 5 次 × 内层 10 次 ----
    mov cx, 5
outer:
    push cx           ; ★ 保存外层 cx
    mov cx, 10
inner:
    ; ---- 内层循环体 ----
    loop inner
    pop cx            ; ★ 恢复外层 cx
    loop outer

五、标志位(看不懂跳转条件时来查这里)

CPU 每做一次运算,都会自动设置这些标志位,jxx 就是根据它们来决定跳不跳的:

标志全称什么时候 = 1一句话理解
ZFZero运算结果 = 0"结果是零吗?"
CFCarry无符号运算溢出 / 借位"进位了吗?"
SFSign结果最高位 = 1"结果是负数吗?"(有符号时看这个)
OFOverflow有符号运算溢出"有符号溢出了吗?"

不常用的:PF(结果低 8 位中 1 的个数为偶数)、AF(低 4 位向高 4 位进位,BCD 用)、DF(字符串方向)。