爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      Win32汇编 - 算数运算指令总结

      首页 知识中心 其他 文章详情页

      Win32汇编 - 算数运算指令总结

      2023-06-21 06:40:51 阅读次数:97

      Win32,操作数,汇编

      整理复习汇编语言的知识点,以前在学习《Intel汇编语言程序设计 - 第五版》时没有很认真的整理笔记,主要因为当时是以学习理解为目的没有整理的很详细,这次是我第三次阅读此书,每一次阅读都会有新的收获,这次复习,我想把书中的重点,再一次做一个归纳与总结(注:16位汇编部分跳过),并且继续尝试写一些有趣的案例,这些案例中所涉及的指令都是逆向中的重点,一些不重要的我就直接省略了,一来提高自己,二来分享知识,转载请加出处,敲代码备注挺难受的。

      汇编中常用的运算符,加减乘除等,另外包括了移位运算等,移位又分为,算数移位,逻辑移位,循环移位,双精度移位等。

      再次强调:该笔记主要学习的是汇编语言,不是研究编译特性的,不会涉及到编译器的优化与代码还原。


      ADD/SUB指令: ADD/SUB指令将将同尺寸的源操作数和目的操作数相加,且不改变原操作数,相加后的结果存入目的操作数中.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	MyList DWORD 10h,20h,30h,40h
      
      .code
      	main PROC
      		; 将eax于ebx 两数相加: 将相加后的结果放入eax
      		mov eax,1024
      		mov ebx,2048
      		add eax,ebx
      		
      		; 同样两数相减,将结果放到eax中
      		mov eax,1024
      		sub eax,512
      		
      		; 针对数组的相加同样可以
      		mov esi,offset MyList    ; 获取到首地址
      		mov eax,0
      		mov ebx,0
      		
      		mov eax,dword ptr ds:[esi]            ; 找到第一个元素
      		mov ebx,dword ptr ds:[esi + 1 * 4]    ; 找到第二个元素
      		add eax,ebx                           ; 相加操作
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      NEG 取反指令: 该指令通过将数字转换为对应的补码而求出其值的相反数,结合上面的加法与减法案例,我们来模拟编译器处理特定语句的写法Rval = -Xvar + (Yvar - Zvar)

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval SDWORD ?
      	Xval SDWORD 26
      	Yval SDWORD 30
      	Zval SDWORD 40
      .code
      	main PROC
      		; 1.写出: Rval = -Xvar + (Yvar - Zvar) 汇编格式
      		; 首先将Xval的值通过neg取反
      		mov eax,dword ptr ds:[Xval]
      		neg eax
      		
      		; 然后将Yval与Zval相减后复制到Yval
      		mov ebx,dword ptr ds:[Yval]
      		sub ebx,dword ptr ds:[Zval]
      		
      		;最后将两个子项相加后放入到Rval中
      		add eax,ebx
      		mov dword ptr ds:[Rval],eax
      		
      		; 2.写出: Rval = (Xval+Yval) - (Yval+Zval)
      		mov eax,dword ptr ds:[Xval]
      		add eax,dword ptr ds:[Yval]
      		mov dword ptr ds:[Rval],eax
      		
      		mov ebx,dword ptr ds:[Yval]
      		add ebx,dword ptr ds:[Zval]
      	
      		sub dword ptr ds:[Rval],ebx
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      AND/OR/XOR 布尔指令: AND指令是对数据进行与运算,OR指令则是对数据进行或运算,XOR则是异或,这三个指令都是将操作值保存在目的操作数中,需要注意的是这些运算符都是针对二进制数进行操作的.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval DWORD ?
      	String BYTE "hello LYSHARK",0dh,0h
      	
      .code
      	ToString proc
      		mov ecx,lengthof String
      		mov esi,offset String
      	s:	;and byte ptr [esi],11011111b  ; 清除第五位,小写变大写
      		or byte ptr [esi],00100000b    ; 设置第五位,大写变小写
      		inc esi
      		loop s
      		ret
      	ToString endp
      
      	main PROC
      		; and 逻辑与运算
      		xor eax,eax
      		mov al,00111011b
      		and al,00001111b  ; 运算后去除al中的0
      		
      		mov eax,00401024h
      		and eax,0ffh         ; eax = 00000024h
      		
      		mov eax,00401024h
      		and eax,0ffffh       ; eax = 00001024h
      		
      		mov eax,00401024h
      		and eax,0ffff0000h   ; eax = 00400000h
      		
      		; or 逻辑或运算
      		xor edx,edx
      		mov dl,5              ; 二进制值
      		or dl,30h             ; 转为有ASCII码
      		
      		; xor 异或运算
      		mov eax,0ffh
      		xor eax,0ffh          ; eax = 0
      		mov eax,4
      		xor eax,5             ; eax = 1
      
      		mov eax,0401000h
      		xor eax,0400000h      ; eax = 00001000h
      		mov eax,0401000h
      		xor eax,01000h        ; eax = 00400000h
      
      		; 异或可用于检查标志位
      		xor eax,eax
      		mov eax,00001111h
      		xor eax,0          ; 检查基偶标志
      		
      		mov eax,00100101h
      		xor eax,0          ; 影响PF标志
      		
      		call ToString
      
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      SHL/SHR 逻辑移位: SHL常用于对目标操作数执行逻辑左移(无符号数)操作,其左移后最低位以0填充,而移动出去的最高位则会送入CF(进位标志)中,而SHR则相反,对目标操作数执行逻辑右移(无符号数)操作,移出的数据位用0代替,最低位被复制到CF(进位标志)中,原来的进位标志位丢失.

      Intel处理器中定义,执行移位的源操作数的范围必须在0-255之间,在任何处理器上都可以使用CL寄存器存放移位位数,例如在下面的指令中,AL寄存器被左移一位,最高位被复制到了进位标志中,最低位被清零:

      01251006 | B3 8F                | mov al,10001111b                            | AL = 10001111b
      01251008 | D0E3                 | shl al,1                                    | CF = 1,AL = 00011110b
      
      01251006 | B0 01                | mov al,10000000b                            | AL = 10000000b
      01251008 | C0E0 02              | shl al,2                                    | CF = 0,AL = 00000000b
      
      01251006 | B0 01                | mov al,10000000b                            | AL = 10000000b
      01251008 | C0E0 01              | shl al,1                                    | CF = 1,AL = 00000000b
      
      01251006 | B0 01                | mov al,10100000b                            | AL = 10100000b
      01251008 | C0E0 03              | shl al,2                                    | CF = 0,AL = 10000000b
      

      另外使用SHL指令还可以进行2的次幂的高速乘法运算,任何操作数左移动N位,就相当于该操作数乘以2的N次方,如下例子:

      01311002 | B0 05                | mov al,5                                    | AL 左移动1位
      01311004 | D0E0                 | shl al,1                                    | al * 2 = 10
      
      01311007 | B0 05                | mov al,5                                    | AL左移2位
      01311009 | C0E0 02              | shl al,2                                    | al * 4 = 20
      
      01311007 | B0 05                | mov al,5                                    | AL左移3位
      01311009 | C0E0 03              | shl al,3                                    | al * 8 = 40
      

      下面是一个左移计算的案例,我们通过汇编来计算Rval = ((Xval + Yval) - (Yval + Zval)) * 8的结果.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval SDWORD ?
      	Xval SDWORD 50
      	Yval SDWORD 100
      	Zval SDWORD 10
      .code
      	main PROC
      		; Rval = ((Xval + Yval) - (Yval + Zval)) * 8
      		mov eax,dword ptr ds:[Xval]
      		add eax,dword ptr ds:[Yval]
      		
      		mov ebx,dword ptr ds:[Yval]
      		add ebx,dword ptr ds:[Zval]
      		
      		sub eax,ebx
      		
      		; 乘以8也就是左移3位 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
      		shl eax,3
      		mov dword ptr ds:[Rval],eax
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      对目标操作数执行SHR逻辑右移(无符号数)操作,移出的数据位用0代替,最低位被复制到CF进位标志中,原来的进位标志位丢失.

      0131100D | B0 01                | mov al,10001111b                            | AL = 10001111b
      0131100F | D0E8                 | shr al,1                                    | CF = 1,AL = 01000111b
      
      0131100D | B0 01                | mov al,10001111b                            | AL = 10001111b
      0131100F | D0E8                 | shr al,2                                    | CF = 1,AL = 00100011b
      

      另外任何无符号操作数逻辑右移N位,就相当于该操作数除以2的N次方,如下例子:

      01311012 | B2 20                | mov dl,20                                   | DL 右移1位 
      01311014 | D0EA                 | shr dl,1                                    | dl/2 = 10
      
      01311012 | B2 20                | mov dl,20                                   | DL 右移2位 
      01311014 | D0EA                 | shr dl,2                                    | dl/4 = 5
      

      下面是一个右移计算的案例,我们通过汇编来计算Rval = (Xval / 8) + (Yval * 16) - (Zval * 4)的结果.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval SDWORD ?
      	Xval SDWORD 200
      	Yval SDWORD 50
      	Zval SDWORD 10
      .code
      	main PROC
      		; Rval = (Xval / 8) + (Yval * 16) - (Zval * 4)
      		; 混合移位计算: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
      		; 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384
      		
      		; 先通过右移3位,计算除法
      		mov eax,dword ptr ds:[Xval]   ; Xval / 8
      		shr eax,3
      		
      		; 再通过左移4位和2位分别计算乘法
      		mov ebx,dword ptr ds:[Yval]   ; Yval * 16
      		shl ebx,4
      		
      		mov ecx,dword ptr ds:[Zval]   ; Zval * 4
      		shl ecx,2
      		
      		add eax,ebx
      		sub eax,ecx
      		mov dword ptr ds:[Rval],eax
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      上面的这种计算方式属于乘数刚好是2的次幂,如果不是2的次幂则需要拆分后计算,如下案例,为了计算无符号乘以36,可以把36分解成2的5次方和2的2次方,然后利用移位命令高效计算.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval SDWORD ?
      .code
      	main PROC
      		; 次方表: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
      		; 次方表: 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384
      		
      		; 计算 123 * 36
      		; 等式拆分 EAX * 36 => EAX * (32 + 4) => (EAX * 32) + (EAX * 4)
      		mov eax,123
      		mov ebx,eax         ; 拷贝出一份
      		shl eax,5           ; 计算 (EAX * 32)
      		shl ebx,2           ; 计算 (EAX * 4)
      		add eax,ebx         ; 最后相加
      		mov dword ptr ds:[Rval],eax
      		
      		; 计算 123 * 24
      		; 等式拆分: EAX * 24 => EAX * (16 + 8) => (EAX * 16) + (EAX * 8)
      		mov eax,123
      		mov ebx,eax
      		shl eax,4           ; 计算 (EAX * 16)
      		shl ebx,3           ; 计算 (EAX * 8)
      		add eax,ebx
      		mov dword ptr ds:[Rval],eax
      		
      		; 计算 123 * 21
      		; 等式拆分: EAX * 21 => EAX * (16 + 4 + 1) => (EAX * 16) + (EAX * 4) + (EAX * 1)
      		
      		mov eax,123
      		mov ebx,eax
      		mov ecx,eax         ; 得到 (EAX * 1)
      		shl eax,4           ; 计算 (EAX * 16)
      		shl ebx,2           ; 计算 (EAX * 4)
      		add eax,ebx
      		add eax,ecx
      		mov dword ptr ds:[Rval],eax
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      下面是我通过寻找一些规律,能够在不查表的情况下逆推出其左移或者是右移中乘数或除数的具体值,如果比较复杂的话还是直接查表来的容易一些,此处只是一种思考方式.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval DWORD ?
      .code
      	main PROC
      	; 次方表: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
      	; 次方表: 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384
      		
      		; 乘法逆推: 28/7=4
      		mov eax,7                      ; eax=7
      		shl eax,2                      ; 求0x2是乘以几,乘以4
      		mov dword ptr ds:[Rval],eax    ;  eax = 28 计算出: 0x2 => 28/7=4
      		
      		; 乘法逆推: 96/6 = 16 => 4*4=16
      		mov eax,6                      ; eax = 6
      		shl eax,2                      ; 4
      		shl eax,2                      ; 4
      		mov dword ptr ds:[Rval],eax    ; eax = 96
      		
      		; 乘法逆推: 4*4*8
      		mov eax,4                      ; eax = 4
      		shl eax,2                      ; 运行到此处 eax=16 通过16/4 = 4 故乘以4
      		shl eax,3                      ; 运行到此处 eax  =128  通过 128/4=32 , 32/4=8 故乘以8
      		mov dword ptr ds:[Rval],eax
      		
      		; 除法逆推: 7/1.75 = 4
      		mov eax,7                      ; eax = 7
      		shr eax,2                      ; 此处乘以4
      		mov dword ptr ds:[Rval],eax    ; eax = 1.75 => 7/1.75=4
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      SAL/SAR 算数移位: SAL指令与SHL指令等价,SAR指令可以对有符号数进行快速除以2的次幂操作,也可以将一个AX寄存器中的值进行扩展,扩展成EAX.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .code
      	main PROC
      		; SAR => 算数右移
      		mov al,0f0h         ; AL = 11110000b (-16)
      		sar al,1            ; AL = 11111000b (-8)
      		
      		; SAR => 有符号除法 计算-128的,2的3次方
      		; 2次方 => -32 3次方 => -16 4次方 => -8 5次方 => -4
      		mov eax,-128        ; AL = 10000000b
      		sar eax,3           ; AL = 11110000b EAX = -16
      		
      		; SAR => 符号扩展AX扩展到EAX
      		; 先左移EAX 16位,然后算术右移EAX 16位
      		mov ax,-128         ; EAX = ????FF80h
      		shl eax,16          ; EAX = FF800000h
      		sar eax,16          ; EAX = FFFFFF80h
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      ROL/ROR 循环移位: ROL位循环左移,其左移1位后会把最高位同时复制到进位标志位和最低位中,而ROR则是循环右移,其右移1位后,把最低位同时复制到进位标志位和最高位中.

      循环移位和普通移位不同之处在于前者并不会丢失任何数据位,从一端走的数据位会从另一端出现,如循环左移会将高位复制到低位中,循环右移则将低位复制到高位中,但需要注意不论是左移/右移,都是对二进制格式进行操作的.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .code
      	main PROC
      		; ROL => 循环左移
      		mov al,40h        ; AL = 01000000b
      		rol al,1          ; AL = 10000000b , CF = 0
      		rol al,1          ; AL = 00000001b , CF = 1
      		rol al,1          ; AL = 00000010b , CF = 0
      		
      		mov al,00100000b
      		rol al,3          ; AL = 00000001b , CF = 1
      		
      		; ROR => 循环右移
      		mov al,01h        ; AL = 00000001b
      		ror al,1          ; AL = 10000000b , CF = 1
      		ror al,1          ; AL = 01000000b , CF = 0
      		
      		mov al,00000100b
      		ror al,3          ; AL = 10000000b , CF = 1
      		
      		; ROL => 循环左移,交换位组
      		; 交换 高半部分(位4-7) 低半部分(位0-3)
      		mov al,26h
      		rol al,4          ; rol 与 ror 结果一致
      		ror al,4
      
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      RCL/RCR 标志移位: RCL指令在每位左移1位后,把CF进位标志复制到最低有效位中,最高有效位复制到进位标志中, RCR则相反,右移后把CF进位标志复制到最高有效位中,并把最低有效位复制到进位标志中.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .code
      	main PROC
      		; RCL 左移
      		clc            ; 将CF进位标志置0
      		mov bl,88h     ; CF = 0 , BL = 10001000b
      		rcl bl,1       ; CF = 1 , BL = 00010000b
      		rcl bl,1       ; CF = 0 , BL = 00100001b
      		
      		; RCR 右移
      		stc            ; 将CF进位标志置1
      		mov ah,10h     ; CF = 1 , ah = 00010000h
      		rcr ah,1       ; CF = 1 , ah = 10001000h
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      让数组整体左移或右移.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval SDWORD ?
      	ArraySize = 3
      	Array DWORD ArraySize DUP(99999999h)
      	ByteArray BYTE 81h,20h,33h
      	WordArray WORD 810dh,0c064h,93abh
      .code
      	main PROC
      		; 多双字同时右移
      		mov esi,0
      		shr Array[esi + 8],1     ; 高双字
      		rcr Array[esi + 4],1     ; 中间双字
      		rcr Array[esi],1         ; 低双字
      		
      		; 多双字同时左移
      		mov esi,0
      		shl Array[esi+8],1
      		rcl Array[esi+4],1
      		rcl Array[esi],1
      		
      		; 让数组整体右移 (从高字节到低字节)
      		shr [ByteArray + 2],1
      		rcr [ByteArray + 1],1
      		rcr [ByteArray],1
      		
      		; 让数组整体左移 (从低字节到高字节)
      		shl [ByteArray],1
      		rcl [ByteArray + 2],1
      		rcl [ByteArray + 4],1
      
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      MUL/IMUL 乘法指令: MUL/IMUL分别可进行有符号与无符号的乘法运算,通常该指令都接受寄存器操作数,也接受内存操作数,但是不接受立即数,且乘数与被乘数大小必须相同,乘基尺寸是乘数/被乘数的两倍.

      MUL乘法指令有三种格式: 第一种将8位操作数与AL相乘,结果放入AX中,第二种将16位操作数与AX相乘,结果的高16位放入DX低16位放入AX,第三种将32位操作数与EAX相乘,结果高32位放入EDX第32位放入EAX中.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval DWORD ?
      	VarWordA WORD 2000h
      	VarWordB WORD 0100h
      .code
      	main PROC
      		; 执行8位乘法运算 al = al*bl
      		mov al,5h
      		mov bl,10h
      		mul bl
      		mov byte ptr ds:[Rval],al
      		
      		; 执行16位乘法运算
      		xor eax,eax
      		xor edx,edx
      		mov ax,word ptr ds:[VarWordA]
      		mul word ptr ds:[VarWordB]
      		mov word ptr ds:[Rval],ax     ; 低半部分
      		mov word ptr ds:[Rval],dx     ; 高半部分  DX:AX = 00000020h
      		
      		; 执行32位乘法运算
      		xor eax,eax
      		xor edx,edx
      		mov eax,12345h
      		mov ebx,1000h
      		mul ebx
      		mov dword ptr ds:[Rval],eax   ; 低半部分
      		mov dword ptr ds:[Rval],edx   ; 高半部分 EDX:EAX = 0000000012345000h
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      IMUL指令主要用于执行有符号整数的乘法运算,并保留乘积的符号位,且在32位汇编中有三种格式:但操作数格式,双操作数格式,三操作数格式,首先是单操作数模式,该模式把乘积存储在累加器AX中,或者将符号位放入EDX将结果放入EAX中.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	EDX_Rval DWORD ?
      	EAX_Rval DWORD ?
      .code
      	main PROC
      		
      		; 执行8位乘法运算: 48*8 得到的积+192 溢出
      		xor eax,eax
      		mov al,48
      		mov bl,4
      		imul bl          ; CF 进位 = 1 OF 溢出 = 1
      		
      		xor eax,eax
      		mov al,-4
      		mov bl,4
      		imul bl           ; AX=FFF0h OF=0
      		
      		; 执行16位乘法运算: 48*4 得到的积 +192
      		xor eax,eax
      		mov ax,48
      		mov bx,4
      		imul bx
      		mov word ptr ds:[EDX_Rval],dx
      		mov word ptr ds:[EAX_Rval],ax  ; DX:AX = 000000C0h OF=0
      		
      		; 执行32位乘法运算: +4823424 *(-423)
      		xor eax,eax
      		xor ebx,ebx
      		mov eax,+4823424
      		mov ebx,-423
      		imul ebx
      		mov dword ptr ds:[EDX_Rval],edx   ; EDX为符号位
      		mov dword ptr ds:[EAX_Rval],eax   ; EDX:EAX = FFFFFFFF86635D80h OF=0
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      接着就是乘法语句的双操作数与三操作数模式了,在双操作数中第一个操作数必须是寄存器,第二个操作数可以是寄存器或内存等,在三操作数模式中,把乘积存储在第一个操作数中,其他与双操作数类似.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	wordp   SWORD 4
      	dwordp  SDWORD 4
      	Rval DWORD ?
      .code
      	main PROC
      	
      		; 双操作数乘法运算
      		xor eax,eax
      		xor ebx,ebx
      		
      		mov ax,-16                   ; ax = -16
      		mov bx,2                     ; bx = 2
      		imul bx,ax                   ; bx = bx * ax
      		imul bx,2                    ; bx = bx * 2
      		imul bx,word ptr ds:[wordp]  ; bx = bx * wordp
      		mov word ptr ds:[Rval],bx    ; 放入变量中保存
      		
      		; 三操作数乘法运算
      		xor eax,eax
      		xor ebx,ebx
      		
      		imul bx,wordp,-16           ; bx = wordp * -16
      		imul ebx,dwordp,-16         ; ebx = dwordp * -16
      		imul ebx,dwordp,-20         ; ebx = dwordp * -20
      		mov dword ptr ds:[Rval],ebx ; 放入变量中
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      到此为止我们学会了通过移位的方式实现快速乘法运算,也学过使用MUL指令进行乘法计算,接下来我们可以编写两个案例分别通过移位和MUL计算EAX与36相乘的结果,看看哪一个效率更高一些.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .code
      	shl_proc proc
      		mov ecx,10
      	s:
      		push eax
      		mov ebx,eax
      		shl eax,5
      		shl eax,2
      		add eax,ebx
      		pop eax
      		loop s
      		ret
      	shl_proc endp
      	
      	mul_proc proc
      		mov ecx,10
      	s:
      		push eax
      		mov ebx,36
      		mul ebx
      		pop eax
      		loop s
      		ret
      	mul_proc endp
      
      	main PROC
      		mov eax,10
      		call shl_proc
      		
      		
      		mov eax,10
      		call mul_proc
      	
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      DIV/IDIV 除法指令: DIV是无符号除法指令,该指令支持8/16/32位无符号整数的除法运算,指令中唯的寄存器或内存操作数是除数,IDIV则是有符号除法指令,该指令与无符号除法几乎一致,唯一的不同在于有符号除法在进行相除操作时需要符号扩展.

      首先我们先来学习一下DIV无符号除法运算的使用技巧,分别演示8/16/32位无符号除法的使用方式.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval DWORD ?
      	DivIdend QWORD 0000000800300020h
      	DivIsor DWORD 00000100h
      	Div_Eax DWORD ?
      	Div_Edx DWORD ?
      .code
      	main PROC
      		
      		; 执行8位除法: 83/2 商al是41h 余数ah是1
      		xor eax,eax
      		mov ax,0083h                  ; 被除数
      		mov bl,2                      ; 除数
      		div bl                        ; ax = ax / bl
      		mov byte ptr ds:[Rval],ah     ; ah = 01h
      		mov byte ptr ds:[Rval],al     ; al = 41h
      		
      		; 执行16位除法: 8003h/100h 商是80h 余数是3
      		xor edx,edx                   ; 清除edx寄存器
      		mov ax,8003h                  ; 被除数
      		mov cx,100h                   ; 除数
      		div cx                        ; ax = ax / cx
      		mov word ptr ds:[Rval],ax     ; ax = 0080h
      		mov word ptr ds:[Rval],dx     ; dx = 0003h
      		 
      		; 执行32位除法
      		mov edx,dword ptr DivIdend + 4  ; 高双字
      		mov eax,dword ptr DivIdend      ; 低双字
      		div DivIsor                     ; 与被除数相除
      		
      		mov dword ptr ds:[Div_Eax],eax  ; eax = 08003000h
      		mov dword ptr ds:[Div_Edx],edx  ; edx = 00000020h
      
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      针对IDIV有符号数的除法运算,需要对被除数进行除法操作之前,对其进行符号扩展,汇编中有三条扩展命令.

      CBW 指令 将字节符号扩展至字,扩展AL的符号位至AH中,保留了数字的符号.
      CWD 指令 将字符号扩展至双字,指令扩展AX的符号位至DX中.
      CDQ 指令 双字符号扩展至八字节,指令扩展EAX的符号位至EDX中.

      当使用符号扩展指令扩展后,寄存器就可以被用来计算有符号除法了,代码如下所示:

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	ByteVal SBYTE -48
      	WordVal SWORD -5000
      	DworeVal SDWORD +50000
      	Rval DWORD ?
      .code
      	main PROC
      		; 字节扩展至字 -48/5
      		xor eax,eax
      		mov al,byte ptr ds:[ByteVal]  ; al = D0 取出 -48
      		cbw                           ; ax = FFD0 将al扩展至ax
      		mov bl,5                      ; bl = 05
      		idiv bl                       ; ax=ax/bl
      		mov word ptr ds:[Rval],ax     ; 结果: ax = FDF7
      		
      		mov byte ptr ds:[Rval],al     ; AL保存商 -9
      		mov byte ptr ds:[Rval],ah     ; AH保存余数 -3
      		
      		; 字扩展至双字
      		xor eax,eax
      		mov ax,word ptr ds:[WordVal]  ; 除数
      		cwd                           ; 扩展至双字(扩展AX至DX)
      		mov bx,+256                   ; 被除数
      		idiv bx                       ; ax = ax/bx
      		mov word ptr ds:[Rval],ax     ; 商AX=-19
      		mov word ptr ds:[Rval],dx     ; 余数DX=-136
      		
      		; 双字符号扩展至八字节
      		mov eax,dword ptr ds:[DworeVal]
      		cdq                             ; 扩展EAX到EDX
      		mov ebx,-256
      		idiv ebx
      		mov dword ptr ds:[Rval],eax     ; 商 EAX = -195
      		mov dword ptr ds:[Rval],edx     ; 余数 EDX = +80
      
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      学习了前面的这几种计算方式以后,我们就可以将其总结起来实现计算复杂的表达式了,先来三个练手的.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval DWORD ?
      	var1 DWORD 10
      	var2 DWORD 15
      	var3 DWORD 20
      	var4 DWORD 25
      .code
      	main PROC
      		; 实现计算: var4 = (var1 * 5) / (var2 - 3)
      		mov eax,dword ptr ds:[var1] ; 先计算左边 (var1 * 5)
      		mov ebx,5
      		mul dword ptr ds:[ebx]      ; EDX:EAX 乘积
      		
      		mov ebx,dword ptr ds:[var2] ; 计算右边 (var2 - 3)
      		sub ebx,3
      		
      		div dword ptr ds:[ebx]       ; 计算两者的除法
      		mov dword ptr ds:[var4],eax  ; 最后赋值操作
      		
      		; 实现计算: var4 = (var1+var2) * var3
      		mov eax,dword ptr ds:[var1]
      		add eax,dword ptr ds:[var2]   ; 计算前半部分
      		
      		mov ebx,dword ptr ds:[var3]   ; 计算后半部分
      		mul ebx
      		mov dword ptr ds:[var4],eax   ; 最后赋值操作
      		
      		; 实现计算: var1 = (var2/var3) * (var1+var2)
      		mov eax,var2
      		cdq                           ; 扩展为EDX:EAX
      		idiv dword ptr ds:[var3]      ; 计算除法,存入eax
      		
      		mov ebx,dword ptr ds:[var1]   ; 计算加法
      		add ebx,dword ptr ds:[var2]
      		
      		imul dword ptr ds:[ebx]       ; 最后计算乘法
      		mov dword ptr ds:[var1],eax   ; 在eax中取值
      
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      最后我们来实现一个相对复杂的案例,总体的复习一下,该案例计算var4 = (var1 * -5) / (-var2 % var3)返回值,我们可以从右边开始计算,并把右边的值存储到EBX中,然后把被除数符号扩展到EDX,最后使用IDIV计算除法.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval DWORD ?
      	var1 DWORD 54
      	var2 DWORD 56
      	var3 DWORD 5
      	var4 DWORD 52
      .code
      	main PROC
      		; 实现计算: var4 = (var1 * -5) / (-var2 % var3)
      		mov eax,dword ptr ds:[var2]
      		neg eax                      ; 将var2反转
      		
      		cdq                          ; 将被除数符号扩展
      		idiv var3                    ; 除以var3 则 EDX=余数
      		mov ebx,edx                  ; 将余数给EBX
      		
      		mov eax,-5
      		imul var1                    ; 计算 var1 * -5 结果给EAX
      		
      		idiv ebx                     ; eax = eax/ebx
      		mov dword ptr ds:[var4],eax  ; 最后将结果给var4
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      ADC/SBB 扩展加减法: 扩展加减法是指任意尺寸大小数字的加减法,其中ADC指令主要用户实现带进位加法,SBB指令则实现带进位减法,起作用都是将源操作数与目的操作数以及进位等相加减.

      以扩展加法为例,计算两个8位整数相加(FFh+FFh)产生的16位结果将被存放在DL:AL (01feh)中,如果是计算两个32位整数相加(FFFFFFFFh+FFFFFFFFh),则会在EDX和EAX中分别存放00000001h:FFFFFFFEh这两个值,扩展SBB同理.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include kernel32.inc
      includelib kernel32.lib
      
      .data
      	Rval DWORD ?
      .code
      	main PROC
      		; 计算8位加法操作
      		xor edx,edx
      		xor eax,eax
      		mov dl,0                      ; 清0
      		mov al,0ffh                   ; 设置加数
      		add al,0ffh                   ; al = al + 0ffh
      		adc dl,0                      ; 进位加法
      		mov byte ptr ds:[Rval],dl     ; 存放高位
      		mov byte ptr ds:[Rval],al     ; 存放低位
      		
      		; 计算32位加法操作
      		xor edx,edx
      		xor eax,eax
      		mov edx,0
      		mov eax,0ffffffffh
      		add eax,0ffffffffh
      		adc edx,0
      		mov dword ptr ds:[Rval],edx    ; 存放高位
      		mov dword ptr ds:[Rval],eax    ; 存放低位
      		
      		; 计算32位减法操作
      		xor edx,edx
      		xor eax,eax
      		mov edx,1       ; 设置高半部分
      		mov eax,0       ; 设置低半部分
      		sub eax,1       ; eax减去1
      		sbb edx,0       ; 减去1则高半部分为0
      		
      		mov dword ptr ds:[Rval],edx    ; 存放高位
      		mov dword ptr ds:[Rval],eax    ; 存放低位
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      LEA指令计算: Lea指令的使用初衷是取出某个内存的地址,但在汇编手册中可以发现其不止可以取地址同样可以实现算数运算,但这个运算与移位运算符一样只能计算2的次幂,当需要计算一个非次幂数字是,则需要对其进行分析与拆分,来实现对算数的计算.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include user32.inc
      includelib user32.lib
      include kernel32.inc
      includelib kernel32.lib
      
      include msvcrt.inc
      includelib msvcrt.lib
      
      .data
      	x DWORD ?
      	y DWORD ?
      	szFmt BYTE '计算结果: %d',0dh,0ah,0
      .code
      	main PROC
      		; 针对加法的lea指令优化
      		mov dword ptr ds:[x],5
      		mov dword ptr ds:[y],3
      		mov eax,dword ptr ds:[x]
      		mov ebx,dword ptr ds:[y]
      		
      		lea eax,dword ptr ds:[eax + 3]         ; eax = edx + 3
      		invoke crt_printf,addr szFmt,eax
      		
      		lea eax,dword ptr ds:[eax + ebx + 2]   ; eax = eax + ebx + 2
      		invoke crt_printf,addr szFmt,eax
      		
      		; 针对减法的lea指令优化
      		mov dword ptr ds:[x],6
      		mov eax,dword ptr ds:[x]
      		
      		lea eax,dword ptr ds:[eax - 2]         ; eax = eax - 2
      		invoke crt_printf,addr szFmt,eax
      		
      		; 针对乘法的lea指令优化
      		mov dword ptr ds:[x],5
      		mov dword ptr ds:[y],3
      		
      		mov eax,dword ptr ds:[x]
      		xor ebx,ebx
      		lea ebx,dword ptr ds:[eax * 8 + 2]     ; ebx = eax * 8 + 2
      		invoke crt_printf,addr szFmt,ebx
      		
      		; 如果使用lea计算乘法,则乘数必须是2/4/8
      		mov eax,dword ptr ds:[y]               ; eax = 3 => 计算 15 * eax
      		lea edx,dword ptr ds:[eax * 4 + eax]   ; edx = 4eax + eax => 5eax
      		lea edx,dword ptr ds:[edx * 2 + edx]   ; edx = 5eax * 2 + 5eax => 15eax
      		invoke crt_printf,addr szFmt,edx       ; edx = eax * 15 = 45
      		
      		; 如果计算乘法时乘数非2的次幂,则此时需要减
      		mov eax,dword ptr ds:[y]               ; eax = 3 => 计算 eax * 7 + 10
      		lea edx,dword ptr ds:[eax * 8]         ; edx = eax * 8
      		sub edx,eax                            ; edx = edx - 1eax
      		add edx,10                             ; edx = edx + 10
      		invoke crt_printf,addr szFmt,edx       ; edx = eax * 7 + 10
      		
      		mov eax,dword ptr ds:[y]               ; eax = 3 => 计算 eax * 3 - 7
      		lea edx,dword ptr ds:[eax * 2]         ; edx = eax * 2
      		add edx,eax                            ; edx = edx + eax
      		sub edx,7                              ; edx = edx - 7
      		invoke crt_printf,addr szFmt,edx       ; edx = eax * 3 - 7
      
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      除法变乘法: 相比较于乘法运算,除法运算则显得略微复杂些,当计算中被除数为正数时,则可以直接使用sar(算数右移)快速计算除法,如果被除数为负数,则需要使用cdq符号扩展后,然后and edx,xxx之后才能进行相除运算.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include user32.inc
      includelib user32.lib
      include kernel32.inc
      includelib kernel32.lib
      
      include msvcrt.inc
      includelib msvcrt.lib
      
      .data
      	x DWORD ?
      	y DWORD ?
      	z DWORD ?
      	szFmt BYTE '计算结果: %d',0dh,0ah,0
      .code
      	main PROC
      		mov dword ptr ds:[x],5
      		mov dword ptr ds:[y],-3
      		mov dword ptr ds:[z],-10
      
      		; 除数为2的优化方式
      		; 被除数为正数(无需扩展): eax => 5 / 2 = 2
      		mov eax,dword ptr ds:[x]   ; 被除数
      		sar eax,1                  ; 算数右移
      		invoke crt_printf,addr szFmt,eax
      		
      		; 被除数为负数(需要扩展): eax => -3 / 2 = -1
      		mov eax,dword ptr ds:[y]   ; 被除数
      		cdq                        ; 符号扩展
      		sub eax,edx                ; 被除数减去edx符号扩展
      		sar eax,1                  ; 算数右移
      		invoke crt_printf,addr szFmt,eax
      		
      		
      		; 除数为4的优化方式
      		; 被除数为正数(无需扩展): eax => 5 / 4 = 1
      		mov eax,dword ptr ds:[x]
      		sar eax,2
      		invoke crt_printf,addr szFmt,eax
      		
      		; 被除数为负数(需要扩展): eax => -10 / 4 = -2
      		; 抽取出公式来: (x + y - 1) / y
      		mov eax,dword ptr ds:[z]          ; eax = x
      		cdq
      		and edx,3                         ; edx = y-1
      		add eax,edx                       ; eax = eax + edx => x + (y - 1)
      		sar eax,2                         ; 2 => y
      		invoke crt_printf,addr szFmt,eax
      		
      		
      		; 除数为8的优化方式
      		; 被除数为正数(无需扩展): eax => 5 / 8 = 1
      		mov eax,dword ptr ds:[x]
      		sar eax,3
      		invoke crt_printf,addr szFmt,eax
      		
      		; 被除数为负数(需要扩展): eax => -10 / 8 = -1
      		mov eax,dword ptr ds:[z]
      		cdq
      		and edx,7
      		add eax,edx
      		sar eax,3
      		invoke crt_printf,addr szFmt,eax
      
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      除法中的除数与被除数都可以分为有符号与无符号,两种计算方式均有一定差异,其差异如下所示.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include user32.inc
      includelib user32.lib
      include kernel32.inc
      includelib kernel32.lib
      
      include msvcrt.inc
      includelib msvcrt.lib
      
      .data
      	x DWORD ?
      	y DWORD ?
      	z DWORD ?
      	szFmt BYTE '计算结果: %d',0dh,0ah,0
      .code
      	main PROC
      		mov dword ptr ds:[x],5
      		mov dword ptr ds:[y],10
      		mov dword ptr ds:[z],-10
      
      		; 除数为(无符号)正2的次幂的计算过程
      		mov eax,dword ptr ds:[x]
      		shr eax,1                  ; eax = 5 / 2
      		
      		mov eax,dword ptr ds:[x]
      		shr eax,2                  ; eax = 5 / 4
      		
      		mov eax,dword ptr ds:[x]
      		shr eax,3                  ; eax = 5 / 8
      		
      		
      		; 被除数为(有符号)的计算过程
      		mov eax,dword ptr ds:[z]
      		cdq
      		sub eax,edx
      		sar eax,1                  ; eax = -10 / 2
      		;neg eax                    ; 将eax取反
      		invoke crt_printf,addr szFmt,eax
      		
      		mov eax,dword ptr ds:[z]
      		cdq
      		and edx,3
      		add eax,edx
      		sar eax,2                  ; eax = -10 / 4
      		;neg eax
      		invoke crt_printf,addr szFmt,eax
      		
      		mov eax,dword ptr ds:[z]
      		cdq
      		and edx,7
      		add eax,edx
      		sar eax,3                   ; eax = -10 / 8
      		;neg eax
      		invoke crt_printf,addr szFmt,eax
      		
      		
      		; 除数为(有符号)负2的次幂的计算过程
      		mov eax,dword ptr ds:[y]    ; y = 10
      		cdq
      		sub eax,edx
      		sar eax,1                   ; eax = 10 / -2
      		neg eax                     ; 将正数 eax翻转为负数 = -5
      		invoke crt_printf,addr szFmt,eax
      		
      		mov eax,dword ptr ds:[y]    ; y = 10
      		cdq
      		and edx,3
      		add eax,edx
      		sar eax,2                   ; eax = 10 / -4
      		neg eax                     ; eax = -2
      		invoke crt_printf,addr szFmt,eax
      		
      		mov eax,dword ptr ds:[z]    ; z = -10 
      		cdq
      		and edx,7
      		add eax,edx
      		sar eax,3                   ; eax = -10 / -8 
      		neg eax                     ; eax = 1 (负负得正)
      		invoke crt_printf,addr szFmt,eax
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      上方的除法运算被除数均为2的次幂,除数的范围也被限定在了2/4/8这样的范围之内,如果是计算非2的次幂该怎么写呢,如下是计算非2的次幂的计算方式,通常情况下编译器会将除法运算转换为乘法,如果需要知道除数是多少则可以使用公式2^(32+n) / M计算后得出.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include user32.inc
      includelib user32.lib
      include kernel32.inc
      includelib kernel32.lib
      
      include msvcrt.inc
      includelib msvcrt.lib
      
      .data
      	x DWORD ?
      	y DWORD ?
      	z DWORD ?
      	szFmt BYTE '计算结果: %d',0dh,0ah,0
      .code
      	main PROC
      		mov dword ptr ds:[x],5
      		mov dword ptr ds:[y],10
      		mov dword ptr ds:[z],-10
      		
      		; 除法(有符号)非2的幂转换为乘法
      		
      		mov ecx,dword ptr ds:[y]      ; 被除数 ecx = 10 / 3 = 3
      		mov eax,055555556h            ; eax = M值 1431655766
      		imul ecx
      		mov eax,edx                   ; edx = n 计算: 2^(32+n) / M
      		shr eax,01fh                  ; 计算出除数为 2.9999 => 3
      		add edx,eax
      		invoke crt_printf,addr szFmt,edx
      		
      		
      		mov ecx,dword ptr ds:[y]       ; ecx = 10 / 5 = 2
      		mov eax,066666667h             ; 此处的M模值是编译器计算后得到的
      		imul ecx
      		sar edx,1                      ; 想要知道除数是多少,只需要
      		mov eax,edx                    ; 2^(32 + edx) / M = 2^33 / 66666667 = 4.999
      		shr eax,01fh
      		add edx,eax
      		invoke crt_printf,addr szFmt,edx
      		
      		
      		mov ecx,dword ptr ds:[y]       ; ecx = 10 / 6 = 1
      		mov eax,02AAAAAABh             ; eax = 715827883
      		imul ecx
      		mov eax,edx                    ; 2^(32 + edx) / M = 2^32 / 2AAAAAAB = 6
      		shr eax,01fh
      		add edx,eax
      		invoke crt_printf,addr szFmt,edx
      		
      		mov ecx,dword ptr ds:[y]       ; ecx = 10 / 9 = 1
      		mov eax,038E38E39h             ; eax = 954437177 
      		imul ecx
      		sar edx,1                      ; 2^(32 + edx) / M = 2^33 / 38E38E39 = 9
      		mov ecx,edx
      		shr ecx,01fh
      		add edx,ecx
      		invoke crt_printf,addr szFmt,edx
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      

      上方代码中的除法计算是针对有符号数进行的,如果是针对无符号数则需要以下方式计算.

      	.386p
      	.model flat,stdcall
      	option casemap:none
      
      include windows.inc
      include user32.inc
      includelib user32.lib
      include kernel32.inc
      includelib kernel32.lib
      
      include msvcrt.inc
      includelib msvcrt.lib
      
      .data
      	x DWORD ?
      	y DWORD ?
      	z DWORD ?
      	szFmt BYTE '计算结果: %d',0dh,0ah,0
      .code
      	main PROC
      		mov dword ptr ds:[x],-5
      		mov dword ptr ds:[y],10
      		mov dword ptr ds:[z],20
      		
      		; 除法(无符号)非2的次幂(正数)转换为乘法
      		
      		xor edx,edx
      		mov ecx,dword ptr ds:[y]    ; ecx = 10
      		mov eax,0AAAAAAABh          ; ecx / 3 = 3
      		mul ecx
      		shr edx,1
      		invoke crt_printf,addr szFmt,edx
      		
      		; 还原除数: 2 ^(32 + n) / M => 2 ^ (32+2) / 0CCCCCCCDh = 5
      		xor edx,edx
      		mov ecx,dword ptr ds:[y]    ; ecx = 10 => 计算: 10/5
      		mov eax,0CCCCCCCDh          ; eax = M
      		mul ecx
      		shr edx,2                   ; edx= n
      		invoke crt_printf,addr szFmt,edx
      		
      		; 还原除数: 2 ^(32 + n) / M => 2 ^ (32+2) / 0AAAAAAABh = 6
      		xor edx,edx
      		mov ecx,dword ptr ds:[y]     ; ecx = 10 => 计算:10/6
      		mov eax,0AAAAAAABh           ; eax = M
      		mul ecx
      		shr edx,2                    ; edx = n
      		invoke crt_printf,addr szFmt,edx
      		
      		
      		;还原除数: 2 ^(32 + n) / M => 2 ^ 33 / 038E38E39h = 9
      		xor edx,edx
      		mov ecx,dword ptr ds:[z]     ; ecx = 20  => 计算: 20/9
      		mov eax,038E38E39h           ; eax = M
      		mul ecx
      		shr edx,1                    ; edx = n
      		invoke crt_printf,addr szFmt,edx
      		
      		
      		; 除法(无符号)非2的次幂(负数)转换为乘法
      		; 还原除数: 2 ^(32 + n) / M => 2 ^ 33 / 0AAAAAAABh = nge(3) => -3
      		xor edx,edx
      		mov ecx,dword ptr ds:[z]      ; ecx = 20  => 计算: 20/-3
      		mov eax,0AAAAAAABh            ; eax = M
      		mul ecx
      		shr edx,1                     ; edx = n 
      		neg edx                       ; edx=6 结果neg取反
      		invoke crt_printf,addr szFmt,edx
      		
      		; 还原除数: 2 ^(32 + n) / M => 2 ^ 63 / 080000003h = nge(5) => -5
      		xor edx,edx
      		mov ecx,dword ptr ds:[x]       ; ecx = -5 => 计算: -5 / -5
      		mov eax,080000003h             ; eax = M
      		mul ecx
      		shr edx,01fh                   ; edx = n
      		invoke crt_printf,addr szFmt,edx
      		
      		invoke ExitProcess,0
      	main ENDP
      END main
      
      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://my.oschina.net/lyshark/blog/5550991,作者:LyShark,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:python 通过 capstone 反汇编

      下一篇:MariaDB 表的基本操作

      相关文章

      2025-04-22 09:27:37

      C语言程序的编译

      C语言程序的编译

      2025-04-22 09:27:37
      文件 , 汇编 , 生成 , 编译 , 预处理
      2025-04-14 09:31:41

      文心一言 VS 讯飞星火 VS chatgpt (279)-- 算法导论20.3 6题

      文心一言 VS 讯飞星火 VS chatgpt (279)-- 算法导论20.3 6题

      2025-04-14 09:31:41
      log , vEB , 操作 , 操作数
      2025-03-11 09:36:54

      JavaScript 中的双等号(==)和三等号(===)有何不同?何时使用它们?

      JavaScript 中的双等号(==)和三等号(===)有何不同?何时使用它们?

      2025-03-11 09:36:54
      JavaScript , 操作数 , 操作符 , 比较 , 相等 , 类型转换 , 运算符
      2025-03-10 09:53:00

      deeply c-函数栈帧(函数栈帧的过程)

      deeply c-函数栈帧(函数栈帧的过程)

      2025-03-10 09:53:00
      eax , 代码 , 寄存器 , 汇编
      2025-03-10 09:52:47

      【Linux】gcc简介+编译过程

      【Linux】gcc简介+编译过程

      2025-03-10 09:52:47
      gcc , 二进制 , 汇编 , 编译 , 编译器 , 链接 , 静态
      2025-03-10 09:51:02

      前言及汇编(30小时精通C++和外挂实战)

      前言及汇编(30小时精通C++和外挂实战)

      2025-03-10 09:51:02
      C++ , CPU , 代码 , 内存 , 汇编
      2025-03-05 09:10:48

      深入解析编译与链接:程序构建的核心基石

      深入解析编译与链接:程序构建的核心基石

      2025-03-05 09:10:48
      代码 , 汇编 , 编译 , 编译器 , 链接
      2025-02-28 09:28:12

      [闻缺陷则喜]C#调用C++的类

      [闻缺陷则喜]C#调用C++的类

      2025-02-28 09:28:12
      API , DLL , int , void , Win32
      2025-02-14 08:25:42

      【C++贪心 位运算】2571. 将整数减少到零需要的最少操作数|1649

      【C++贪心 位运算】2571. 将整数减少到零需要的最少操作数|1649

      2025-02-14 08:25:42
      学习 , 执行 , 操作数 , 算法
      2025-02-13 08:34:55

      【算法】栈算法——逆波兰表达式求值

      【算法】栈算法——逆波兰表达式求值

      2025-02-13 08:34:55
      后缀 , 操作数 , 操作符 , 表达式 , 题目
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5236625

      查看更多

      最新文章

      【C language】操作符详解

      2025-02-12 09:28:40

      LeetCode刷题:逆波兰表达式求值

      2024-11-08 08:55:53

      遭遇Trojan-Spy.Win32.Delf.uv,Trojan.PSW.Win32.XYOnline,Trojan.PSW.Win32.ZhengTu等2

      2024-11-04 09:32:52

      学习机罢工?原来是中了文件夹变exe文件的病毒Trojan-Dropper.Win32.Flystud.yo

      2024-11-04 09:32:44

      遭遇 gjlbj.vya / Trojan.Win32.Agent.kle 等1

      2024-10-09 09:17:25

      汇编指令学习(ADD,SUB,MUL,DIV,XADD,INC,DEC,NEG)

      2024-04-22 06:40:24

      查看更多

      热门文章

      Win32汇编-实现DLL注入器

      2023-06-21 06:41:05

      Win32汇编:汇编基本知识总结

      2023-06-19 06:57:29

      Win32汇编:算术与伪指令

      2023-06-21 06:35:59

      Win32汇编:过程与宏调用

      2023-06-21 06:37:08

      汇编指令学习(CALL,JMP,RET)

      2023-06-25 07:05:18

      【JVM性能优化】字节码指令集调用执行流程分析(语法分析篇)

      2024-04-16 08:57:13

      查看更多

      热门标签

      linux java python javascript 数组 前端 docker Linux vue 函数 shell git 节点 容器 示例
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      遭遇 gjlbj.vya / Trojan.Win32.Agent.kle 等1

      汇编指令学习(ADD,SUB,MUL,DIV,XADD,INC,DEC,NEG)

      汇编指令学习(CALL,JMP,RET)

      LeetCode刷题:逆波兰表达式求值

      【JVM性能优化】字节码指令集调用执行流程分析(语法分析篇)

      Win32汇编:汇编基本知识总结

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号