> 技术文档 > DIV 指令概述

DIV 指令概述


文章目录

      • **DIV 指令:你的汇编除法小助手**
        • **1. 这个计算器很挑食**
        • **2. 三种尺寸可选(就像买衣服)**
        • **3. 常见翻车现场**
        • **4. 安全驾驶指南**
        • **5. 为什么这么设计?**
        • **一句话总结**
      • **DIV 指令概述**
      • **1. 字节除法(8位除数)**
      • **2. 字除法(16位除数)**
      • **3. 双字除法(32位除数)**
      • **关键注意事项**
      • **常见错误场景**
        • 错误1:未初始化高位寄存器
        • 错误2:忽略商溢出
      • **总结表**
    • 演示了 DIV 指令的三种用法
      • 代码说明
        • 1. **8位DIV演示**
        • 2. **16位DIV演示**
        • 3. **32位DIV演示**
        • 4. **安全除法实践**
        • 标志位说明
      • 典型错误场景
      • 总结表
      • **1. 8位DIV指令演示(AX / 8位除数)**
        • **场景**:
      • **2. 16位DIV指令演示(DX:AX / 16位除数)**
        • **场景**:
      • **3. 32位DIV指令演示(EDX:EAX / 32位除数)**
        • **场景**:
      • **4. 安全除法示例(防除零和溢出)**
        • **场景**:
      • **标志位与异常行为**
      • **总结表**
      • **常见问题解决方案**

好的,我用更通俗易懂的方式来解释 DIV 指令的用法和注意事项,就像朋友聊天一样:


DIV 指令:你的汇编除法小助手

想象 DIV 指令就像是一个专门做除法的计算器,但它有几个怪癖你需要知道:

1. 这个计算器很挑食
  • 只吃无符号数:给它负数它会懵圈(要用 IDIV 来处理有符号数)
  • 讨厌除零:如果你让它除以0,它会直接\"炸掉\"(触发异常)
  • 结果放的位置很讲究:商和余数总是放在固定的寄存器里
2. 三种尺寸可选(就像买衣服)

(1) 小号(8位版)

  • 被除数:AX(16位)
  • 除数:8位寄存器或内存
  • 结果:
    • 商:AL(8位)
    • 余数:AH(8位)
  • 坑点:如果商超过255(比如1000÷3≈333),计算器会\"死机\"

(2) 中号(16位版)

  • 被除数:DX:AX(32位,像两个16位拼起来的)
  • 除数:16位寄存器或内存
  • 结果:
    • 商:AX(16位)
    • 余数:DX(16位)
  • 使用技巧
    • 如果被除数小于65536,记得把DX清零
    • 就像这样:
      mov dx, 0 ; 清空高16位mov ax, 50000 ; 被除数mov bx, 100 ; 除数div bx ; 结果:AX=500, DX=0

(3) 大号(32位版)

  • 被除数:EDX:EAX(64位巨无霸)
  • 除数:32位寄存器或内存
  • 结果:
    • 商:EAX(32位)
    • 余数:EDX(32位)
  • 重要提示
    • 处理大数时别忘了设置EDX
    • 就像这样:
      mov edx, 0 ; 清空高32位mov eax, 1000000000 ; 被除数mov ebx, 100000 ; 除数div ebx ; 结果:EAX=10000, EDX=0
3. 常见翻车现场

(1) 忘记准备完整的被除数

mov ax, 100000 ; 想计算100000÷100mov bx, 100div bx ; 错误!应该先把DX设为1(因为100000 > 65535)

(2) 结果太大装不下

mov ax, 1000mov bl, 1div bl ; AL想存1000,但最大只能255,直接崩溃

(3) 最蠢的错误:除以零

mov eax, 100mov ecx, 0div ecx ; 电脑:你认真的吗?(╯°□°)╯︵ ┻━┻
4. 安全驾驶指南
  • 每次使用前检查

    cmp divisor, 0je error_handler ; 如果除数为零就跳转到错误处理
  • 预估结果大小

    ; 对于8位除法,检查AX是否≤255×除数mov ax, 1000mov bl, 5cmp ax, 255*5 ; 255×5=1275ja too_big ; 如果1000>1275就跳转div bl
5. 为什么这么设计?
  • 历史原因:早期CPU寄存器少,要省着用
  • 效率考虑:硬件除法电路需要固定输入输出位置
  • 安全机制:用异常而不是标志位来报错更可靠
一句话总结

DIV 就像个固执的老会计:

  • 你必须把数字按它要求的格式摆好(AX/DX:AX/EDX:EAX)
  • 它只接受无符号数
  • 如果账目不对(除零或结果太大),它会直接摔账本(触发异常)
  • 但只要遵守规则,它能快速准确地给你商和余数

希望这样解释更生动好记!实际使用时多检查边界条件就好。
以下是关于 DIV(无符号除法) 指令的详细说明,按操作数大小分类整理,并附注意事项和示例:


DIV 指令概述

功能:执行无符号除法,将被除数(AX、DX:AX 或 EDX:EAX)除以源操作数(除数),结果存储商和余数。
关键特性

  • 仅支持无符号运算。
  • 若商超出目标寄存器容量或除数为0,触发 #DE(除法错误)异常。
  • 标志位(CF/OF/SF等)执行后为未定义状态。

1. 字节除法(8位除数)

操作码F6 /6
操作数DIV r/m8
寄存器布局

被除数 除数 商 余数 商最大值 AX r/m8 AL AH 255

操作过程

  1. 被除数为 AX(16位),除数为 r/m8(8位)。
  2. 计算 AX / r/m8,商存入 AL,余数存入 AH
  3. 若商 > 0FFH(255),触发 #DE 异常。

示例

mov ax, 1000 ; AX = 1000 (03E8h)mov bl, 3 ; BL = 3div bl ; AL = 333 (商,但超过255!触发 #DE 异常)

2. 字除法(16位除数)

操作码F7 /6
操作数DIV r/m16
寄存器布局

被除数 除数 商 余数 商最大值 DX:AX r/m16 AX DX 65,535

操作过程

  1. 被除数为 DX:AX(32位,高16位在DX,低16位在AX),除数为 r/m16(16位)。
  2. 计算 (DX << 16 + AX) / r/m16,商存入 AX,余数存入 DX
  3. 若商 > 0FFFFH(65,535),触发 #DE 异常。

示例

mov dx, 0 ; DX:AX = 00030000h (196,608)mov ax, 30000h ; mov bx, 2 ; BX = 2div bx ; AX = 98,304 (商), DX = 0 (余数)

3. 双字除法(32位除数)

操作码F7 /6
操作数DIV r/m32
寄存器布局

被除数 除数 商 余数 商最大值 EDX:EAX r/m32 EAX EDX 232-1

操作过程

  1. 被除数为 EDX:EAX(64位,高32位在EDX,低32位在EAX),除数为 r/m32(32位)。
  2. 计算 (EDX << 32 + EAX) / r/m32,商存入 EAX,余数存入 EDX
  3. 若商 > 0FFFFFFFFH(4,294,967,295),触发 #DE 异常。

示例

mov edx, 0 ; EDX:EAX = 0000000100000000h (4,294,967,296)mov eax, 100000000h mov ebx, 2 ; EBX = 2div ebx  ; EAX = 2,147,483,648 (商), EDX = 0 (余数)

关键注意事项

  1. 除数不能为0
    若除数为0,直接触发 #DE 异常(等同于Linux下的 SIGFPE)。

  2. 商溢出检测
    DIV 通过比较商与目标寄存器容量判断溢出(如字节除法的商需 ≤255)。

  3. 被除数布局

    • 16位除法:被除数在 DX:AX,需手动设置高位(DX)。
    • 32位除法:被除数在 EDX:EAX,高位(EDX)需清零(除非被除数 ≥232)。
  4. 符号位无关
    DIV 始终处理无符号数。若需有符号除法,应使用 IDIV 指令。


常见错误场景

错误1:未初始化高位寄存器
mov ax, 1000 ; AX = 1000mov bl, 3 ; BL = 3div bl  ; 正确:AX / BLmov ax, 1000 ; AX = 1000mov bx, 3 ; BX = 3div bx  ; 错误!未设置DX,导致被除数实际为 DX:AX = 0:1000
错误2:忽略商溢出
mov ax, 1000 ; AX = 1000 (03E8h)mov bl, 1 ; BL = 1div bl  ; AL = 1000 > 255,触发 #DE 异常!

总结表

类型 被除数 除数 商 余数 最大商 字节除法 AX r/m8 AL AH 255 字除法 DX:AX r/m16 AX DX 65,535 双字除法 EDX:EAX r/m32 EAX EDX 4,294,967,295

通过合理设置被除数和除数,DIV 指令可高效完成无符号除法运算,但需严格防范除零和溢出问题。

演示了 DIV 指令的三种用法

以下是修改后的代码,演示了 DIV 指令的三种用法(8位、16位和32位无符号除法),并添加了详细的注释说明:

; 设置处理器模式和内存模型.586 ; 使用 586 指令集.model flat, stdcall ; 平坦内存模型,stdcall 调用约定option casemap:none ; 区分大小写; 引入库文件includelib kernel32.lib ; Windows API 库includelib msvcrt.lib ; C 运行时库.data ; 数据段定义 ; 测试数据 byteDivisor db 3 ; 8位除数 wordDivisor dw 100 ; 16位除数 dwordDivisor dd 50000 ; 32位除数 ; 结果存储 byteQuotient db ? byteRemainder db ? wordQuotient dw ? wordRemainder dw ? dwordQuotient dd ? dwordRemainder dd ? ; 错误检测 divisionErrorOccurred db 0 ; 标记是否发生除法错误.code ; 代码段main proc ; --------------------------- ; 1. 8位DIV指令演示 (AX / 8位除数) ; --------------------------- mov ax, 1000 ; AX = 1000 (被除数) mov bl, byteDivisor ; BL = 3 (除数) div bl ; AL = AX / BL (商), AH = AX % BL (余数) ; 预期结果: AL = 333 (但超过255,会触发 #DE 异常) mov byteQuotient, al mov byteRemainder, ah ; 捕获除法错误 jnc no_error1 mov divisionErrorOccurred, 1no_error1: ; --------------------------- ; 2. 16位DIV指令演示 (DX:AX / 16位除数) ; --------------------------- mov dx, 0 ; DX:AX = 100000 (被除数高16位清零) mov ax, 100000 ; mov bx, wordDivisor ; BX = 100 (除数) div bx ; AX = (DX:AX) / BX (商), DX = 余数 ; 预期结果: AX = 1000, DX = 0 mov wordQuotient, ax mov wordRemainder, dx ; --------------------------- ; 3. 32位DIV指令演示 (EDX:EAX / 32位除数) ; --------------------------- mov edx, 0 ; EDX:EAX = 1000000000 (被除数高32位清零) mov eax, 1000000000 mov ebx, dwordDivisor ; EBX = 50000 (除数) div ebx  ; EAX = (EDX:EAX) / EBX (商), EDX = 余数 ; 预期结果: EAX = 20000, EDX = 0 mov dwordQuotient, eax mov dwordRemainder, edx ; --------------------------- ; 4. 安全除法示例(防止除零和溢出) ; ---------------------------safe_division: mov eax, 5000 ; 被除数 mov ecx, 0 ; 尝试除零 jecxz division_by_zero ; 检测除数为零 div ecx  ; 若继续执行会触发 #DE jmp division_donedivision_by_zero: mov divisionErrorOccurred, 1division_done: ; --------------------------- ; 程序退出 ; --------------------------- xor eax, eax ; 返回码 0 retmain endpend main

代码说明

1. 8位DIV演示
  • 操作AX (16位) 除以 8位除数 (BL)
  • 关键点
    • 商必须 ≤ 255,否则触发 #DE 异常
    • 示例中 1000 / 3 = 333 会溢出(333 > 255)
    • 实际应用中需预先检查:if (AX / BL) > 255 then 错误
2. 16位DIV演示
  • 操作DX:AX (32位组合) 除以 16位除数 (BX)
  • 关键点
    • 被除数高16位(DX)必须正确设置(若被除数 < 65536,DX=0)
    • 商必须 ≤ 65535
    • 示例 100000 / 100 = 1000 是安全的
3. 32位DIV演示
  • 操作EDX:EAX (64位组合) 除以 32位除数 (EBX)
  • 关键点
    • 被除数高32位(EDX)需手动管理
    • 商必须 ≤ 2³²-1
    • 示例 1,000,000,000 / 50,000 = 20,000 合法
4. 安全除法实践
  • 防除零:用 jecxz 检测除数为零
  • 防溢出:预先计算商的范围(如8位除法时检查 AX ≤ (255 * divisor)
标志位说明
  • DIV 执行后所有标志位(CF/OF等)状态未定义
  • 需通过异常处理捕获错误(如 #DE

典型错误场景

  1. 未初始化高位寄存器

    mov ax, 50000 ; 被除数mov bx, 100 ; 除数div bx ; 错误!未设置DX,实际被除数是 DX:AX = 0:50000
  2. 忽略商溢出

    mov ax, 1000 ; AX = 1000mov bl, 1 ; BL = 1div bl ; AL = 1000 > 255 → #DE
  3. 除数为零

    mov eax, 100mov ecx, 0div ecx  ; 触发 #DE

总结表

类型 被除数 除数 商寄存器 余数寄存器 最大商 8位除法 AX r/m8 AL AH 255 16位除法 DX:AX r/m16 AX DX 65,535 32位除法 EDX:EAX r/m32 EAX EDX 4,294,967,295

此代码展示了如何安全使用DIV指令,并通过预检查避免常见错误。

以下是针对代码中每种 DIV 指令场景的详细解释,包括操作原理、预期结果和潜在问题:


1. 8位DIV指令演示(AX / 8位除数)

场景
mov ax, 1000 ; AX = 1000 (03E8h)mov bl, 3 ; BL = 3 (除数)div bl ; AL = AX / BL, AH = AX % BL
  • 操作原理
    • 被除数为 AX(16位),除数为 BL(8位)。
    • 计算 AX / BL,商存入 AL,余数存入 AH
  • 预期结果
    • 数学结果:1000 / 3 = 3331
    • AL 最大只能存储 2550FFh),实际商 333 > 255
  • 实际行为
    • 触发 #DE(除法错误)异常,因为商溢出。
    • 若未处理异常,程序会崩溃。
  • 关键点
    • 必须预先检查if (AX) > (255 * divisor) 则拒绝运算。
    • 8位除法适用于小数值(被除数 ≤ 65535,且商 ≤ 255)。

2. 16位DIV指令演示(DX:AX / 16位除数)

场景
mov dx, 0 ; 高16位清零mov ax, 100000 ; DX:AX = 000186A0h (100,000)mov bx, 100 ; BX = 100 (除数)div bx ; AX = 商, DX = 余数
  • 操作原理
    • 被除数为 DX:AX(32位组合),除数为 BX(16位)。
    • 计算 (DX << 16 + AX) / BX,商存入 AX,余数存入 DX
  • 预期结果
    • 数学结果:100000 / 100 = 10000
    • AX = 100003E8h),DX = 0
  • 关键点
    • 被除数 ≥ 65536 时,必须设置 DX(如 mov dx, 1 表示 DX:AX = 1:86A0h = 100000)。
    • 商需 ≤ 65535(否则触发 #DE)。

3. 32位DIV指令演示(EDX:EAX / 32位除数)

场景
mov edx, 0 ; 高32位清零mov eax, 1000000000 ; EDX:EAX = 3B9ACA00h (1,000,000,000)mov ebx, 50000 ; EBX = 50000 (除数)div ebx  ; EAX = 商, EDX = 余数
  • 操作原理
    • 被除数为 EDX:EAX(64位组合),除数为 EBX(32位)。
    • 计算 (EDX << 32 + EAX) / EBX,商存入 EAX,余数存入 EDX
  • 预期结果
    • 数学结果:1,000,000,000 / 50,000 = 20,0000
    • EAX = 200004E20h),EDX = 0
  • 关键点
    • 被除数 ≥ 2³² 时,需设置 EDX(如 mov edx, 1 表示 EDX:EAX = 1:00000000h ≈ 4.29亿)。
    • 商需 ≤ 4,294,967,295(否则触发 #DE)。

4. 安全除法示例(防除零和溢出)

场景
mov eax, 5000 ; 被除数mov ecx, 0 ; 尝试除零jecxz division_by_zero ; 检测除数为零div ecx  ; 若执行会触发 #DEjmp division_donedivision_by_zero: mov divisionErrorOccurred, 1
  • 操作原理
    • 使用 jecxz 检测除数 ECX 是否为零。
    • 若除数为零,跳过 DIV 指令,标记错误。
  • 关键点
    • 防除零:必须显式检查除数是否为0(DIV 不会自动处理)。
    • 防溢出:需预先计算商是否超过目标寄存器容量(如8位除法时检查 AX ≤ 255 * divisor)。

标志位与异常行为

  • 标志位DIV 执行后所有标志位(CF/OF/SF等)状态未定义,不可依赖。
  • 异常
    • 除零:触发 #DE 异常(Linux下为 SIGFPE)。
    • 商溢出:同样触发 #DE(如8位除法时商 > 255)。

总结表

场景 被除数范围 除数范围 商范围 注意事项 8位DIV AX: 0-65535 1-255 AL: 0-255 检查 AX ≤ 255 * divisor 16位DIV DX:AX: 0-2³²-1 1-65535 AX: 0-65535 大数需设置DX高位 32位DIV EDX:EAX: 0-2⁶⁴-1 1-2³²-1 EAX: 0-2³²-1 极大数需设置EDX高位

常见问题解决方案

  1. 如何避免除零?

    test ebx, ebx ; 检查除数是否为零jz error_label ; 跳转到错误处理div ebx ; 安全执行
  2. 如何检测商溢出?

    • 对8位除法:
      cmp ax, 255 * divisorja overflow_labeldiv bl
  3. 如何处理大被除数?

    • 32位除法示例:
      mov edx, 1 ; EDX:EAX = 1:00000000h (2³²)mov eax, 0mov ebx, 2div ebx ; EAX = 2³¹, EDX = 0

通过合理设置被除数、检查除数和商范围,可以安全使用 DIV 指令完成无符号除法运算。