文章目录
- MASM 中的 ADD 指令详解
- 一、基本格式
- 二、操作数组合规则
- 三、操作数尺寸支持
- 四、对标志位的影响
- 五、具体示例
- 示例1:寄存器与立即数
- 示例2:寄存器与寄存器
- 示例3:内存操作
- 示例4:带进位的连续加法
- 六、特殊用法
- 1. 地址计算
- 2. 数组访问
- 3. 优化技巧
- 七、注意事项
- 八、编码示例
- 九、性能考虑
- 十、常见错误
- 1. **有符号数溢出(Overflow)**
- 例(8位有符号数):
- 2. **无符号数进位(Carry)**
- 例(8位无符号数):
- 3. 对比示例
- 4. x86 指令对标志位的影响
- 5. 检测方式
- 6. 总结
- 代码示例一
- 简单的ADD指令演示程序
- 关键演示点
- 1. **标志位同时设置**
- 2. **不同类型数据的相同操作**
- 3. **标志位的双重含义**
- 运行观察建议
- 核心理解
- 代码示例二
MASM 中的 ADD 指令详解
一、基本格式
ADD 目标操作数, 源操作数功能:将源操作数与目标操作数相加,结果存回目标操作数。
二、操作数组合规则
| 目标操作数 | 源操作数 | 是否合法 | 示例 |
|---|---|---|---|
| 寄存器 | 立即数 | ✓ | ADD AX, 5 |
| 寄存器 | 寄存器 | ✓ | ADD EAX, EBX |
| 寄存器 | 内存 | ✓ | ADD ECX, [mem] |
| 内存 | 立即数 | ✓ | ADD [mem], 10 |
| 内存 | 寄存器 | ✓ | ADD [mem], EDX |
| 内存 | 内存 | ✗ | 非法操作 |
三、操作数尺寸支持
8位运算:
ADD AL, BL16位运算:
ADD AX, BX32位运算:
ADD EAX, EBX64位运算(x64):
ADD RAX, RBX
四、对标志位的影响
ADD AL, CL ; 影响所有状态标志: ; CF(进位标志):无符号溢出时置1 ; PF(奇偶标志):结果低8位中1的个数为偶数时置1 ; AF(辅助进位):低4位向高4位进位时置1 ; ZF(零标志):结果为0时置1 ; SF(符号标志):结果为负时置1 ; OF(溢出标志):有符号溢出时置1五、具体示例
示例1:寄存器与立即数
MOV AX, 1000h ADD AX, 2000h ; AX = 3000h, CF=0, ZF=0, SF=0示例2:寄存器与寄存器
MOV EAX, 0FFFFFFFFh MOV EBX, 1 ADD EAX, EBX ; EAX = 0, CF=1, ZF=1, OF=0示例3:内存操作
.data value DWORD 12345678h result DWORD ? .code MOV EAX, 87654321h ADD value, EAX ; value = 99999999h ADD EAX, value ; EAX = 0FFFFFFFFh示例4:带进位的连续加法
; 64位加法(使用32位寄存器) MOV EAX, dword ptr [num1_low] MOV EDX, dword ptr [num1_high] ADD EAX, dword ptr [num2_low] ; 低32位相加 ADC EDX, dword ptr [num2_high] ; 高32位带进位相加六、特殊用法
1. 地址计算
ADD ESI, TYPE DWORD ; ESI增加4(DWORD大小)2. 数组访问
MOV EBX, 2 ; 索引 MOV EAX, [array + EBX*4] ; 等价于ADD计算偏移3. 优化技巧
; 使用LEA进行加法(不改变标志位) LEA EAX, [EAX + EBX*2 + 10] ; EAX = EAX + EBX*2 + 10七、注意事项
操作数尺寸必须匹配
ADD AX, BL ; 错误:尺寸不匹配 ADD AL, BL ; 正确:都是8位内存操作数需要类型说明
ADD [mem], 5 ; 错误:未指定尺寸 ADD BYTE PTR [mem], 5 ; 正确 ADD DWORD PTR [mem], 5 ; 正确立即数符号扩展
ADD EAX, -1 ; 实际编码为FFFFFFFFh有符号与无符号的差异
无符号数:看CF(进位标志)
有符号数:看OF(溢出标志)
八、编码示例
.386 .model flat, stdcall .stack 4096 .data num1 DWORD 1000 num2 DWORD 2000 sum DWORD 0 .code main PROC ; 示例1:寄存器加法 MOV EAX, num1 ADD EAX, num2 ; EAX = 3000 MOV sum, EAX ; 示例2:内存直接加法 ADD num1, 500 ; num1 = 1500 ; 示例3:多精度加法 MOV AX, 0FFFFh MOV BX, 1 ADD AX, BX ; AX=0, CF=1, ZF=1 ; 示例4:循环累加 MOV ECX, 10 MOV EAX, 0 L1: ADD EAX, ECX LOOP L1 ; EAX = 10+9+...+1 = 55 RET main ENDP END main九、性能考虑
现代CPU中:ADD指令通常只需要1个时钟周期
依赖链:连续的ADD指令可能形成依赖链
宏融合:某些情况下ADD可与CMP/JCC指令融合
十、常见错误
; 错误示例 ADD 5, AX ; 立即数不能作为目标 ADD [EAX], [EBX] ; 内存到内存不支持 ADD AX, EAX ; 尺寸不匹配 ; 正确写法 ADD AX, 5 ADD AX, BX MOV ECX, [EBX] ADD [EAX], ECXADD指令是x86汇编中最基础、最常用的算术指令,理解其工作原理和标志位影响对于编写正确的汇编程序至关重要。
在 x86 汇编中,有符号数溢出和无符号数进位是两个不同的概念,它们对应不同的标志位:
1.有符号数溢出(Overflow)
- 检测标志:
OF(Overflow Flag) - 溢出条件:两个有符号数相加,结果超出了有符号数能表示的范围(对 8 位是 -128~127,16 位是 -32768~32767,32 位是 -2³¹~2³¹-1,64 位是 -2⁶³~2⁶³-1)。
例(8位有符号数):
mov al, 100 ; 十进制 100(有符号为正) add al, 50 ; 结果 150,但 8 位有符号范围是 -128~127,150 > 127 ; 此时 OF = 1(溢出)mov al, -100 ; 有符号 -100 add al, -50 ; 结果 -150,小于 -128,同样 OF = 1常见溢出情况:
正数 + 正数 → 结果为负(符号位错误)
负数 + 负数 → 结果为正(符号位错误)
2.无符号数进位(Carry)
- 检测标志:
CF(Carry Flag) - 进位条件:两个无符号数相加,结果超出了无符号数能表示的范围(对 8 位是 0~255,16 位是 0~65535,等等)。
例(8位无符号数):
mov al, 200 ; 无符号 200 add al, 100 ; 结果 300,但 8 位无符号最大 255,300 > 255 ; 此时 CF = 1(进位)mov al, 255 ; 无符号 255 add al, 1 ; 结果 256(实际 AL=0),CF = 13. 对比示例
假设 8 位运算:
mov al, 0x80 ; 二进制 10000000 mov bl, 0x80 add al, bl- 有符号视角:
al = -128,bl = -128,和 = -256(超出 8 位有符号范围) →OF = 1 - 无符号视角:
al = 128,bl = 128,和 = 256(超出 8 位无符号范围) →CF = 1
在这个例子中,OF 和 CF 同时为 1。
4. x86 指令对标志位的影响
ADD指令同时影响OF和CF(分别用于有符号溢出和无符号进位判断)。ADC带进位加同样影响两者。INC指令不影响CF,只影响OF(常用于有符号数循环计数)。
5. 检测方式
- 有符号溢出检查:
JO(溢出跳转)或JNO。 - 无符号进位检查:
JC(进位跳转)或JNC。
6. 总结
| 数类型 | 超出范围判断标志 | 示例(8位)导致标志置 1 的情况 |
|---|---|---|
| 有符号数 | OF(溢出标志) | 100 + 50 = 150(>127) |
| 无符号数 | CF(进位标志) | 200 + 100 = 300(>255) |
关键点:
同一个二进制加法,从有符号角度看可能溢出,从无符号角度看可能进位,或者两者同时发生,或者都不发生。CPU 会同时设置 OF 和 CF,程序根据数的解释方式选择检查哪个标志。
代码示例一
简单的ADD指令演示程序
以下是一个精简的ADD指令演示程序,重点展示ADD如何影响标志寄存器的不同情况:
.386 .model flat, stdcall option casemap:none ExitProcess PROTO, dwExitCode:DWORD .data ; 测试数据 byte1 db 127 ; 01111111b byte2 db 128 ; 10000000b byte3 db 255 ; 11111111b ; 标志存储 cf_flag db 0 ; 进位标志 of_flag db 0 ; 溢出标志 zf_flag db 0 ; 零标志 sf_flag db 0 ; 符号标志 ; 结果存储 result db 0 .code main proc ; 演示1: 正数加法 - 无进位,无溢出 ; AL = 50 + 30 = 80 MOV AL, 50 ADD AL, 30 ; 二进制: 00110010 + 00011110 = 01010000 CALL save_flags ; 保存标志状态 ; 演示2: 有符号正溢出 ; AL = 127 + 1 = -128 (有符号溢出) MOV AL, 127 ; 01111111b = +127 ADD AL, 1 ; 10000000b = -128 CALL save_flags ; OF=1, SF=1, ZF=0, CF=0 ; 演示3: 无符号进位 ; AL = 255 + 1 = 0 (无符号进位) MOV AL, 255 ; 11111111b = 255 ADD AL, 1 ; 00000000b = 0 CALL save_flags ; CF=1, ZF=1, OF=0, SF=0 ; 演示4: 负溢出 ; AL = -128 + -1 = 127 (有符号负溢出) MOV AL, -128 ; 10000000b = -128 ADD AL, -1 ; 01111111b = +127 CALL save_flags ; OF=1, SF=0, CF=1 ; 演示5: 符号标志 ; AL = 64 + -100 = -36 MOV AL, 64 ADD AL, -100 ; 结果应为负数 CALL save_flags ; SF=1, OF=0, CF=1 ; 演示6: 零结果 ; AL = -100 + 100 = 0 MOV AL, -100 ADD AL, 100 CALL save_flags ; ZF=1, SF=0, OF=0, CF=1 ; 演示7: 同时有符号溢出和无符号进位 ; AL = -1 + -1 = -2 (无符号: 255 + 255 = 254 带进位) MOV AL, -1 ; 11111111b ADD AL, -1 ; 11111110b CALL save_flags ; SF=1, OF=0, CF=1 ; 演示8: 16位寄存器示例 MOV AX, 32767 ; 0111111111111111b ADD AX, 1 ; 1000000000000000b ; 注意: 16位操作同样影响标志 ; 程序结束 push 0 call ExitProcess main endp ; ============================================ ; 保存标志状态的子程序 ; ============================================ save_flags proc MOV result, AL ; 保存结果 ; 保存进位标志 JC cf_set MOV cf_flag, 0 JMP check_of cf_set: MOV cf_flag, 1 check_of: ; 保存溢出标志 JO of_set MOV of_flag, 0 JMP check_zf of_set: MOV of_flag, 1 check_zf: ; 保存零标志 JZ zf_set MOV zf_flag, 0 JMP check_sf zf_set: MOV zf_flag, 1 check_sf: ; 保存符号标志 JS sf_set MOV sf_flag, 0 JMP done sf_set: MOV sf_flag, 1 done: ; 在这里可以设置断点查看标志状态 RET save_flags endp ; ============================================ ; 演示标志位如何定义数据类型 ; ============================================ type_demo proc ; 同一数据,不同解释 MOV AL, 200 ; 二进制: 11001000b ; 作为无符号数: 200 ADD AL, 100 ; 200 + 100 = 44 (CF=1) JC unsigned_overflow ; 无符号数溢出处理 ; 作为有符号数: -56 ADD AL, -100 ; -56 + -100 = 44 (OF=0, 正确) JO signed_overflow ; 有符号数溢出处理 RET unsigned_overflow: ; 处理无符号溢出 RET signed_overflow: ; 处理有符号溢出 RET type_demo endp end main关键演示点
1.标志位同时设置
每个ADD指令后,所有相关标志位同时被设置:
- CF(进位标志):无符号运算的溢出
- OF(溢出标志):有符号运算的溢出
- ZF(零标志):结果是否为0
- SF(符号标志):结果是否为负
2.不同类型数据的相同操作
; 同样的二进制操作,不同解释 MOV AL, 10000000b ; 二进制: 10000000 ; 解释1: 无符号数128 ADD AL, 1 ; 129, CF=0 ; 解释2: 有符号数-128 ADD AL, 1 ; -127, OF=0 (没有溢出范围)3.标志位的双重含义
; 示例: AL = 11111111b (255或-1) ADD AL, 11111111b ; 加同样的值 ; 无符号视角: 255 + 255 = 254 (CF=1) ; 有符号视角: -1 + -1 = -2 (OF=0)运行观察建议
- 使用调试器(如OllyDbg或x64dbg)单步执行
- 在
save_flags子程序返回后观察:result:存储计算结果cf_flag,of_flag,zf_flag,sf_flag:标志状态
- 注意观察相同的ADD操作如何同时设置多个标志
核心理解
ADD指令本身无类型:
- 它只是执行二进制加法
- 同时设置所有可能的标志位
- 程序员的后续选择定义了数据类型:
- 检查
CF→ 视为无符号数 - 检查
OF→ 视为有符号数 - 检查
ZF→ 比较操作 - 检查
SF→ 符号判断
- 检查
这个精简程序清晰地展示了汇编层面"数据无类型"的本质,以及如何通过标志位检查来赋予数据具体的类型含义。
代码示例二
.386 .model flat, stdcall option casemap:none ExitProcess PROTO, dwExitCode:DWORD .data ; 通用数据定义 data8_1 db 127 ; 01111111b data8_2 db 128 ; 10000000b data8_3 db 255 ; 11111111b data16_1 dw 32767 ; 0111111111111111b data16_2 dw 32768 ; 1000000000000000b data32_1 dd 2147483647 ; 01111111111111111111111111111111b data32_2 dd 2147483648 ; 10000000000000000000000000000000b ; 结果存储 result8 db 0 result16 dw 0 result32 dd 0 ; 标志存储 flags_cf db 0 flags_of db 0 flags_zf db 0 flags_sf db 0 .code ; ============================================ ; 演示1:基本ADD指令 - 8位运算 ; ============================================ demo1_add_basic proc ; 清空结果 MOV result8, 0 ; 示例1: 简单加法 MOV AL, 50 ADD AL, 30 ; AL = 80 MOV result8, AL ; 示例2: 带进位的加法 MOV BL, 200 ADD BL, 100 ; BL = 44 (300 mod 256), CF=1 JC demo1_carry_set JMP demo1_no_carry demo1_carry_set: MOV flags_cf, 1 JMP demo1_next demo1_no_carry: MOV flags_cf, 0 demo1_next: ; 示例3: 零结果 MOV CL, 255 ADD CL, 1 ; CL = 0, ZF=1 JZ demo1_zero_set JMP demo1_not_zero demo1_zero_set: MOV flags_zf, 1 JMP demo1_end demo1_not_zero: MOV flags_zf, 0 demo1_end: RET demo1_add_basic endp ; ============================================ ; 演示2:有符号溢出检测 ; ============================================ demo2_signed_overflow proc ; 清空标志 MOV flags_of, 0 ; 情况1: 正溢出 (127 + 1) MOV AL, 127 ; 最大值 ADD AL, 1 ; 127 + 1 = -128 (溢出) JO demo2_overflow_case1 ; 没有溢出,继续 JMP demo2_case2 demo2_overflow_case1: MOV flags_of, 1 MOV result8, AL ; 保存结果: -128 demo2_case2: ; 情况2: 负溢出 (-128 + -1) MOV BL, -128 ; 最小值 ADD BL, -1 ; -128 + -1 = 127 (溢出) JO demo2_overflow_case2 ; 没有溢出 JMP demo2_case3 demo2_overflow_case2: MOV flags_of, 1 MOV result8, BL ; 保存结果: 127 demo2_case3: ; 情况3: 没有溢出 (100 + 20) MOV CL, 100 ADD CL, 20 ; 120, 没有溢出 JO demo2_overflow_case3 JMP demo2_no_overflow_case3 demo2_overflow_case3: MOV flags_of, 1 JMP demo2_end demo2_no_overflow_case3: MOV flags_of, 0 MOV result8, CL ; 保存结果: 120 demo2_end: RET demo2_signed_overflow endp ; ============================================ ; 演示3:无符号进位检测 ; ============================================ demo3_unsigned_carry proc ; 清空标志 MOV flags_cf, 0 ; 情况1: 产生进位 (255 + 1) MOV AL, 255 ; 无符号最大值 ADD AL, 1 ; 256, 进位 JC demo3_carry_case1 ; 没有进位 JMP demo3_case2 demo3_carry_case1: MOV flags_cf, 1 MOV result8, AL ; 保存结果: 0 demo3_case2: ; 情况2: 没有进位 (200 + 50) MOV BL, 200 ADD BL, 50 ; 250, 没有进位 JC demo3_carry_case2 JMP demo3_no_carry_case2 demo3_carry_case2: MOV flags_cf, 1 JMP demo3_case3 demo3_no_carry_case2: MOV flags_cf, 0 MOV result8, BL ; 保存结果: 250 demo3_case3: ; 情况3: 边界情况 (254 + 1) MOV CL, 254 ADD CL, 1 ; 255, 没有进位 JC demo3_carry_case3 JMP demo3_no_carry_case3 demo3_carry_case3: MOV flags_cf, 1 JMP demo3_end demo3_no_carry_case3: MOV flags_cf, 0 MOV result8, CL ; 保存结果: 255 demo3_end: RET demo3_unsigned_carry endp ; ============================================ ; 演示4:符号标志(SF)检测 ; ============================================ demo4_sign_flag proc ; 清空标志 MOV flags_sf, 0 ; 情况1: 正数加正数得到正数 MOV AL, 50 ADD AL, 30 ; AL = 80, SF=0 JS demo4_negative_case1 JMP demo4_positive_case1 demo4_negative_case1: MOV flags_sf, 1 JMP demo4_case2 demo4_positive_case1: MOV flags_sf, 0 MOV result8, AL ; 保存结果: 80 demo4_case2: ; 情况2: 正数加负数得到负数 MOV BL, 50 ADD BL, -100 ; BL = -50, SF=1 JS demo4_negative_case2 JMP demo4_positive_case2 demo4_negative_case2: MOV flags_sf, 1 MOV result8, BL ; 保存结果: -50 demo4_positive_case2: MOV flags_sf, 0 JMP demo4_case3 demo4_case3: ; 情况3: 负数加负数得到负数 MOV CL, -50 ADD CL, -30 ; CL = -80, SF=1 JS demo4_negative_case3 JMP demo4_positive_case3 demo4_negative_case3: MOV flags_sf, 1 MOV result8, CL ; 保存结果: -80 demo4_positive_case3: MOV flags_sf, 0 demo4_end: RET demo4_sign_flag endp ; ============================================ ; 演示5:零标志(ZF)检测 ; ============================================ demo5_zero_flag proc ; 清空标志 MOV flags_zf, 0 ; 情况1: 非零结果 MOV AL, 50 ADD AL, 30 ; AL = 80, ZF=0 JZ demo5_zero_case1 JMP demo5_nonzero_case1 demo5_zero_case1: MOV flags_zf, 1 JMP demo5_case2 demo5_nonzero_case1: MOV flags_zf, 0 MOV result8, AL ; 保存结果: 80 demo5_case2: ; 情况2: 零结果 (255 + 1) MOV BL, 255 ADD BL, 1 ; BL = 0, ZF=1 JZ demo5_zero_case2 JMP demo5_nonzero_case2 demo5_zero_case2: MOV flags_zf, 1 MOV result8, BL ; 保存结果: 0 demo5_nonzero_case2: MOV flags_zf, 0 demo5_end: RET demo5_zero_flag endp ; ============================================ ; 演示6:溢出与进位的对比 ; ============================================ demo6_overflow_vs_carry proc ; 同时检测OF和CF ; 示例1: 有符号溢出,无符号无进位 (127 + 1) MOV AL, 127 ADD AL, 1 ; AL = -128 ; 检查OF JO demo6_of_set_1 JMP demo6_of_clear_1 demo6_of_set_1: MOV flags_of, 1 demo6_of_clear_1: MOV flags_of, 0 ; 检查CF JC demo6_cf_set_1 JMP demo6_cf_clear_1 demo6_cf_set_1: MOV flags_cf, 1 demo6_cf_clear_1: MOV flags_cf, 0 ; 保存结果 MOV result8, AL ; 示例2: 无符号进位,有符号无溢出 (255 + 1) MOV BL, 255 ADD BL, 1 ; BL = 0 ; 检查OF JO demo6_of_set_2 JMP demo6_of_clear_2 demo6_of_set_2: MOV flags_of, 1 demo6_of_clear_2: MOV flags_of, 0 ; 检查CF JC demo6_cf_set_2 JMP demo6_cf_clear_2 demo6_cf_set_2: MOV flags_cf, 1 demo6_cf_clear_2: MOV flags_cf, 0 ; 保存结果 MOV result8, BL demo6_end: RET demo6_overflow_vs_carry endp ; ============================================ ; 演示7:16位ADD指令 ; ============================================ demo7_add_16bit proc ; 清空结果 MOV result16, 0 ; 示例1: 简单16位加法 MOV AX, 10000 ADD AX, 20000 ; AX = 30000 MOV result16, AX ; 示例2: 16位有符号溢出 MOV BX, 32767 ; 最大值 ADD BX, 1 ; 溢出 JO demo7_overflow_16 JMP demo7_no_overflow_16 demo7_overflow_16: MOV flags_of, 1 MOV result16, BX ; 保存溢出结果 JMP demo7_case3 demo7_no_overflow_16: MOV flags_of, 0 MOV result16, BX demo7_case3: ; 示例3: 16位无符号进位 MOV CX, 65535 ; 无符号最大值 ADD CX, 1 ; 进位 JC demo7_carry_16 JMP demo7_no_carry_16 demo7_carry_16: MOV flags_cf, 1 MOV result16, CX ; 保存结果: 0 JMP demo7_end demo7_no_carry_16: MOV flags_cf, 0 MOV result16, CX demo7_end: RET demo7_add_16bit endp ; ============================================ ; 演示8:32位ADD指令 ; ============================================ demo8_add_32bit proc ; 清空结果 MOV result32, 0 ; 示例1: 简单32位加法 MOV EAX, 1000000000 MOV EBX, 1500000000 ADD EAX, EBX ; EAX = 2500000000 MOV result32, EAX ; 示例2: 32位有符号溢出 MOV ECX, 2147483647 ; 最大值 ADD ECX, 1 ; 溢出 JO demo8_overflow_32 JMP demo8_no_overflow_32 demo8_overflow_32: MOV flags_of, 1 MOV result32, ECX ; 保存溢出结果 JMP demo8_case3 demo8_no_overflow_32: MOV flags_of, 0 MOV result32, ECX demo8_case3: ; 示例3: 32位无符号进位 MOV EDX, 0FFFFFFFFh ; 无符号最大值 ADD EDX, 1 ; 进位 JC demo8_carry_32 JMP demo8_no_carry_32 demo8_carry_32: MOV flags_cf, 1 MOV result32, EDX ; 保存结果: 0 JMP demo8_end demo8_no_carry_32: MOV flags_cf, 0 MOV result32, EDX demo8_end: RET demo8_add_32bit endp ; ============================================ ; 演示9:ADD指令与内存操作数 ; ============================================ demo9_add_memory proc ; 示例1: 寄存器加内存 MOV AL, 100 ADD AL, data8_1 ; AL = 100 + 127 = 227 (溢出) JO demo9_overflow_mem1 JMP demo9_no_overflow_mem1 demo9_overflow_mem1: MOV flags_of, 1 MOV result8, AL JMP demo9_case2 demo9_no_overflow_mem1: MOV flags_of, 0 MOV result8, AL demo9_case2: ; 示例2: 内存加立即数 MOV data8_2, 200 ADD data8_2, 100 ; 内存 = 44 (300 mod 256), CF=1 JC demo9_carry_mem2 JMP demo9_no_carry_mem2 demo9_carry_mem2: MOV flags_cf, 1 MOV AL, data8_2 MOV result8, AL JMP demo9_case3 demo9_no_carry_mem2: MOV flags_cf, 0 MOV AL, data8_2 MOV result8, AL demo9_case3: ; 示例3: 内存加寄存器 MOV data8_3, 50 MOV BL, 30 ADD data8_3, BL ; 内存 = 80 MOV AL, data8_3 MOV result8, AL demo9_end: RET demo9_add_memory endp ; ============================================ ; 演示10:安全加法函数 ; ============================================ demo10_safe_addition proc ; 安全的加法函数,检测溢出 ; 函数签名: safe_add(EAX: 数1, EBX: 数2) ; 返回: EAX = 结果,CF = 是否出错 ; 示例1: 安全的加法 MOV EAX, 1000 MOV EBX, 2000 CALL safe_add_proc ; 保存结果 MOV result32, EAX ; 示例2: 会溢出的加法 MOV EAX, 2147483647 MOV EBX, 1 CALL safe_add_proc ; 保存结果 MOV result32, EAX ; 示例3: 负溢出 MOV EAX, -2147483648 MOV EBX, -1 CALL safe_add_proc ; 保存结果 MOV result32, EAX RET demo10_safe_addition endp ; 安全的加法函数实现 safe_add_proc proc ; 输入: EAX = 数1, EBX = 数2 ; 输出: EAX = 结果,如果溢出则CF=1 ADD EAX, EBX JO safe_add_overflow ; 没有溢出,正常返回 CLC ; 清除进位标志表示成功 RET safe_add_overflow: ; 溢出处理:返回最大值并设置错误标志 MOV EAX, 7FFFFFFFh ; 返回最大值 STC ; 设置进位标志表示错误 RET safe_add_proc endp ; ============================================ ; 主程序:按顺序执行所有演示 ; ============================================ main proc ; 演示1: 基本ADD指令 CALL demo1_add_basic ; 演示2: 有符号溢出检测 CALL demo2_signed_overflow ; 演示3: 无符号进位检测 CALL demo3_unsigned_carry ; 演示4: 符号标志检测 CALL demo4_sign_flag ; 演示5: 零标志检测 CALL demo5_zero_flag ; 演示6: 溢出与进位的对比 CALL demo6_overflow_vs_carry ; 演示7: 16位ADD指令 CALL demo7_add_16bit ; 演示8: 32位ADD指令 CALL demo8_add_32bit ; 演示9: ADD指令与内存操作数 CALL demo9_add_memory ; 演示10: 安全加法函数 CALL demo10_safe_addition ; 程序结束 push 0 call ExitProcess main endp end main