DIV 指令概述
文章目录
-
- 演示了 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
(16位),除数为r/m8
(8位)。 - 计算
AX / r/m8
,商存入AL
,余数存入AH
。 - 若商 >
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
(32位,高16位在DX,低16位在AX),除数为r/m16
(16位)。 - 计算
(DX << 16 + AX) / r/m16
,商存入AX
,余数存入DX
。 - 若商 >
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
(64位,高32位在EDX,低32位在EAX),除数为r/m32
(32位)。 - 计算
(EDX << 32 + EAX) / r/m32
,商存入EAX
,余数存入EDX
。 - 若商 >
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 (余数)
关键注意事项
-
除数不能为0:
若除数为0,直接触发#DE
异常(等同于Linux下的SIGFPE
)。 -
商溢出检测:
DIV 通过比较商与目标寄存器容量判断溢出(如字节除法的商需 ≤255)。 -
被除数布局:
- 16位除法:被除数在
DX:AX
,需手动设置高位(DX)。 - 32位除法:被除数在
EDX:EAX
,高位(EDX)需清零(除非被除数 ≥232)。
- 16位除法:被除数在
-
符号位无关:
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 异常!
总结表
通过合理设置被除数和除数,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 错误
- 商必须 ≤ 255,否则触发
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
)
典型错误场景
-
未初始化高位寄存器
mov ax, 50000 ; 被除数mov bx, 100 ; 除数div bx ; 错误!未设置DX,实际被除数是 DX:AX = 0:50000
-
忽略商溢出
mov ax, 1000 ; AX = 1000mov bl, 1 ; BL = 1div bl ; AL = 1000 > 255 → #DE
-
除数为零
mov eax, 100mov ecx, 0div ecx ; 触发 #DE
总结表
此代码展示了如何安全使用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 = 333
余1
。 - 但
AL
最大只能存储255
(0FFh
),实际商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 = 1000
余0
。 AX = 1000
(03E8h
),DX = 0
。
- 数学结果:
- 关键点:
- 被除数 ≥ 65536 时,必须设置
DX
(如mov dx, 1
表示DX:AX = 1:86A0h = 100000
)。 - 商需 ≤ 65535(否则触发
#DE
)。
- 被除数 ≥ 65536 时,必须设置
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,000
余0
。 EAX = 20000
(4E20h
),EDX = 0
。
- 数学结果:
- 关键点:
- 被除数 ≥ 2³² 时,需设置
EDX
(如mov edx, 1
表示EDX:EAX = 1:00000000h ≈ 4.29亿
)。 - 商需 ≤ 4,294,967,295(否则触发
#DE
)。
- 被除数 ≥ 2³² 时,需设置
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
)。
- 防除零:必须显式检查除数是否为0(
标志位与异常行为
- 标志位:
DIV
执行后所有标志位(CF/OF/SF等)状态未定义,不可依赖。 - 异常:
- 除零:触发
#DE
异常(Linux下为SIGFPE
)。 - 商溢出:同样触发
#DE
(如8位除法时商 > 255)。
- 除零:触发
总结表
AX ≤ 255 * divisor
常见问题解决方案
-
如何避免除零?
test ebx, ebx ; 检查除数是否为零jz error_label ; 跳转到错误处理div ebx ; 安全执行
-
如何检测商溢出?
- 对8位除法:
cmp ax, 255 * divisorja overflow_labeldiv bl
- 对8位除法:
-
如何处理大被除数?
- 32位除法示例:
mov edx, 1 ; EDX:EAX = 1:00000000h (2³²)mov eax, 0mov ebx, 2div ebx ; EAX = 2³¹, EDX = 0
- 32位除法示例:
通过合理设置被除数、检查除数和商范围,可以安全使用 DIV
指令完成无符号除法运算。