转移指令与循环

转移指令可以修改 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 标号

执行过程:

  1. CX = CX - 1
  2. 判断 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

所有条件转移指令都是短转移

jcxzjejnejbja 等所有条件转移指令(详见 09-标志寄存器)都是段内短转移,跳转范围 -128~127 字节。

根据位移转移的意义

你可能会问:为什么 jmp shortloop 用位移而不是直接存目标地址?

好处:这样的程序段可以在内存中任意位置移动(浮动装配),不需要重新计算地址。只要相对位置不变,转移就不会出错。这是早期程序段在内存中浮动的基础。

常见循环模式

模式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:源/目的地址指针

相关