转移指令与循环
转移指令可以修改 CS:IP 的值,让 CPU 跳转到其他位置执行代码,从而实现分支、循环、函数调用等控制流。
前置知识
需要了解 01-寄存器 中的 CS:IP 寄存器对(指向下一条要执行的指令)。
转移指令的分类
转移指令可以只修改 IP(段内转移),也可以同时修改 CS 和 IP(段间转移/远转移)。
| 类型 | 修改对象 | 跳转范围 |
|---|---|---|
| 段内短转移 | IP | -128 ~ 127 字节 |
| 段内近转移 | IP | -32768 ~ 32767 字节 |
| 段间转移(远转移) | CS + IP | 整个1MB内存任意位置 |
offset 操作符
offset 由编译器处理,功能是取得标号的偏移地址:
code segment
start:
mov ax, offset start ; ax = 0(start 是段内第一个位置)
s: mov ax, offset s ; ax = s 标号的偏移地址
code ends
offset 在编译时就已经计算好了,是一个常量,不是运行时计算的。
jmp 指令:无条件转移
jmp 是最基本的无条件转移指令,有多种形式。
jmp short 标号(段内短转移)
jmp short s ; 向前或向后跳最多 128/127 字节
; ...(一些代码)
s: ; 跳到这里
机器码中不包含目标地址,而是包含一个 8位位移(相对于 jmp 指令后的第一个字节)。这个位移由编译器在编译时计算。
jmp near ptr 标号(段内近转移)
和 short 类似,但位移是 16位,跳转范围更大(±32KB)。
jmp near ptr s ; 段内近转移,IP = IP + 16位位移
jmp far ptr 标号(段间远转移)
同时修改 CS 和 IP,可以跳到内存中任意位置。
jmp far ptr s ; CS = s所在段的段地址,IP = s在段中的偏移地址
机器码中直接包含目标的段地址和偏移地址(不是位移)。
jmp 寄存器(段内间接转移)
mov ax, 0123H
jmp ax ; IP = AX = 0123H,段内转移到偏移 0123H 处
相当于 push IP / jmp ax,但不影响栈。
jmp word ptr 内存单元(段内间接转移)
mov word ptr ds:[0], 0123H
jmp word ptr ds:[0] ; IP = ds:[0]处的字 = 0123H
jmp dword ptr 内存单元(段间间接转移)
从内存单元读取两个字:低地址字给 IP,高地址字给 CS。
mov word ptr ds:[0], 0123H
mov word ptr ds:[2], 0
jmp dword ptr ds:[0] ; CS=0, IP=0123H,即跳转到 0000:0123
易错点
jmp dword ptr的内存布局:[0]处是IP(低字),[2]处是CS(高字)。不要搞反!
loop 指令:循环
loop 是最常用的循环指令,它默认使用 CX 作为计数器。
格式:loop 标号
执行过程:
CX = CX - 1- 判断 CX 是否为0:
- 如果 CX ≠ 0,跳转到标号处继续执行
- 如果 CX = 0,向下执行(循环结束)
; 计算 1+2+3+...+100
mov ax, 0
mov cx, 100 ; 循环次数放入 CX
s:
add ax, cx ; ax = ax + cx
loop s ; cx--,如果 cx≠0 跳到 s
; 循环结束后 ax = 5050
loop 和 jmp short 的关系
loop实现的是段内短转移(范围 -128~127)。它和dec cx / jnz short 标号的效果类似,但更简洁。
jcxz 指令:条件转移
jcxz(Jump if CX is Zero)是条件转移指令:当 CX = 0 时跳转,否则向下执行。
格式:jcxz 标号
jcxz done ; 如果 CX=0,跳到 done
; CX≠0 时执行的代码
done:
相当于:
cmp cx, 0
je short done
所有条件转移指令都是短转移
jcxz、je、jne、jb、ja等所有条件转移指令(详见 09-标志寄存器)都是段内短转移,跳转范围 -128~127 字节。
根据位移转移的意义
你可能会问:为什么 jmp short 和 loop 用位移而不是直接存目标地址?
好处:这样的程序段可以在内存中任意位置移动(浮动装配),不需要重新计算地址。只要相对位置不变,转移就不会出错。这是早期程序段在内存中浮动的基础。
常见循环模式
模式1:固定次数循环
mov cx, N ; N 是循环次数
s:
; 循环体
loop s
模式2:遍历数组
mov bx, 0 ; 偏移地址从0开始
mov cx, 8 ; 8个元素
s:
mov ax, [bx] ; 读取一个字
; 处理 ax
add bx, 2 ; 移动到下一个字
loop s
模式3:用 dx 暂存数据,cx 用于循环计数
; 问题:cx 被用作循环计数器了,数据怎么办?
; 答案:放到其他寄存器或栈中
mov cx, 10
mov dx, 0 ; dx 用于累加
s:
add dx, cx
loop s
寄存器分工习惯
- CX:循环计数器
- AX:累加器/临时计算
- BX:地址指针(基址)
- DX:数据/扩展存储
- SI/DI:源/目的地址指针
相关
- 01-寄存器 — CS:IP 和 CX 寄存器
- 06-栈 — 栈和转移的关系
- 08-过程调用与算术指令 — call/ret 是更高级的转移
- 09-标志寄存器 — 更多条件转移指令(je/jne/jb/ja等)