5.4 汇编语言:算数运算指令集

您所在的位置:网站首页 汇编rcl对哪个操作数进行操作 5.4 汇编语言:算数运算指令集

5.4 汇编语言:算数运算指令集

2024-05-17 14:41| 来源: 网络整理| 查看: 265

算术运算指令集是计算机中的一组基本操作,用于对数字执行常见的算术运算操作。这些指令都是计算机中非常基础的运算指令,可以用于实现所有常见的算术运算操作,并可以通过组合使用实现更加复杂的数学运算。在实际编程中,程序员可以根据具体需求选择合适的运算指令,实现程序中的算术运算操作。

4.1 MOV/INC/DEC/XCHG

MOV/INC/DEC 指令是汇编语言中的三种基本指令,用于在寄存器和内存中进行数据传输和操作。通过这些基本的指令,我们可以完成许多基础的汇编操作,并通过组合使用这些指令,实现更加复杂的功能。

MOV指令:MOV指令用于将数据从一个位置复制到另一个位置 INC指令:INC指令用于将一个寄存器或内存单元中的值加1 DEC指令:DEC指令用于将一个寄存器或内存单元中的值减1 XCHG指令:XCHG指令用于将一个寄存器与另一个寄存器之间进行数据交换

MOV指令: 该指令从源操作数向目标操作数之间复制数据,两个操作数必须尺寸一致,目的操作数不能是CS/EIP/IP等.

00A41000 | B8 24100000 | mov eax,1024 | EAX = 1024 00A41005 | 8BD8 | mov ebx,eax | EBX = 1024 00A41007 | 66:B9 0010 | mov cx,1000 | CX = 1000

MOVZX指令: 零扩展传送,该指令将源操作数的内容复制到目标操作数中,并将该值零扩展(zero-extend)至16位或者32位,该指令适用于无符号整数,其基本格式如下:

01301000 | 66:BB 9BA6 | mov bx,A69B | BX = 0A69B 01301004 | 0FB7C3 | movzx eax,bx | EAX = 0000A69B 01301007 | 0FB6D3 | movzx edx,bl | EDX = 0000009B 0130100A | 66:0FB6CB | movzx cx,bl | CX = 009B

MOVSX指令: 符号扩展传送,该指令将源操作数的内容复制到目标操作数中,并将该值符号扩展(sign-extend)至16位或者是32位,该指令只能用于有符号整数,其基本格式如下:

00FD1000 | 66:BB 9BA6 | mov bx,A69B | BX = 0A69B 00FD1004 | 0FBFC3 | movsx eax,bx | EAX = FFFFA69B 00FD1007 | 0FBED3 | movsx edx,bl | EDX = FFFFFF0B 00FD100A | 66:0FBECB | movsx cx,bl | CX = FF9B

XCHG指令: 数据交换指令,该指令用于交换两个操作数中的内容,但该指令不接受立即数操作数.

00D71000 | B8 00100000 | mov eax,1000 | EAX = 1000h 00D71005 | BB 00200000 | mov ebx,2000 | EBX = 2000h 00D7100A | 93 | xchg ebx,eax | EAX = 2000h;EBX = 1000h

INC/DEC指令: 数据递增与递减,INC指令用于对寄存器或内存数据的递增,DEC指令用于对寄存器或内存数据递减.

00881000 | B8 00100000 | mov eax,1000 | EAX = 1000h 00881005 | 40 | inc eax | EAX = 1001h 00881006 | 40 | inc eax | EAX = 1002h 00881007 | BB 00200000 | mov ebx,2000 | EBX = 2000h 0088100C | 4B | dec ebx | EBX = 1FFFF 0088100D | 4B | dec ebx | EBX = 1FFFE 0088100E | 4B | dec ebx | EBX = 1FFFD 4.2 ADD/SUB

ADD和SUB是计算机汇编语言中的算术运算指令,分别用于实现加法运算和减法运算。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 4.3 NEG/NOT

NEG和NOT指令是计算机汇编语言中的逻辑运算指令,分别用于对操作数进行取反和按位取反操作。NEG指令可以将一个操作数的值取反(即变为相反数),NOT指令可以将一个操作数的二进位取反(0变为1,1变为0)。

这些指令能够增强程序的处理能力和灵活性,可以完成各种逻辑运算,包括对数值和位运算的计算。在一些特定的场景中,NEG和NOT指令非常有用。

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

.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 ; 写出: 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 ; not 二进制取反 xor eax,eax mov al,11110000b not al ; al = 00001111b invoke ExitProcess,0 main ENDP END main 4.4 AND/OR/XOR

AND、OR、和XOR指令是计算机汇编语言中的布尔运算指令,用于对二进制数进行逻辑运算。AND指令可以将两个操作数的二进制数分别按位进行“与”(and)操作,OR指令可以将两个操作数的二进制数分别按位进行“或”(or)操作,XOR指令可以将两个操作数的二进制数分别按位进行“异或”(xor)操作。使用这些指令,我们可以在汇编语言程序中进行各种布尔运算,实现各种逻辑控制和计算。

这些指令可以操作的目的操作数包括寄存器和存储器单元,而源操作数可以是立即数、寄存器或存储器单元。这些指令也是汇编语言编程中非常基础和常用的操作。

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

.386p .model flat,stdcall option casemap:none include windows.inc include kernel32.inc includelib kernel32.lib .data 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 4.5 SHL/SHR

SHL/SHR指令是计算机汇编语言中的逻辑移位指令,用于对二进制数字节或字进行逻辑位移操作。其中,SHL(shift left)指令用于将操作数左移指定的位数,而SHR(shift right)指令则用于将操作数右移指定的位数。这些指令可以用于实现各种算法和数据处理操作。

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

使用乘法运算的效率是非常低的,通过右移操作可以显著提高程序的计算乘法的效率,在乘数是2的次幂的情况下才可以使用,如果不是则需要拆分后继续计算,我们先来通过汇编来计算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 4.6 SAL/SAR

SAL和SAR指令是计算机汇编语言中的算数移位指令,它们和逻辑移位指令相似,都是对二进制数字节或字进行位移操作。但是SAL和SAR指令可以用于进行带符号的算术操作,实现各种算法和数据处理操作。通过使用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 4.7 ROL/ROR

ROL/ROR指令是计算机汇编语言中的循环移位指令,它们可以将某个二进制数字进行指定位数的移位,并将移出的位重新放置到高位或低位。ROL指令向左循环移位,ROR指令向右循环移位。通过使用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 4.8 RCL/RCR

RCL/RCR指令是计算机汇编语言中的标志位移位指令,它们可以将某个二进制数字进行指定位数的移位,同时会将操作数的最高位和进位标志位呈现为下一轮移位的输入。RCL指令向左移位,RCR指令向右移位。通过使用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 4.9 MUL/IMUL

MUL指令和IMUL指令是计算机汇编语言中用于进行乘法运算的指令,它们可以将两个操作数相乘,获取最终的计算结果。通过使用MUL和IMUL指令,我们可以在汇编语言程序中进行乘法运算操作,实现各种算法和数据处理操作。需要注意的是,MUL指令和IMUL指令都会改变标志寄存器中的CF和OF标志位,因此在使用这些指令时要注意处理这些标志位。

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 4.10 DIV/IDIV

DIV指令和IDIV指令是计算机汇编语言中用于进行除法运算的指令,DIV用于无符号整数的除法运算,IDIV用于带符号整数的除法运算。这两条指令均使用32位被除数,得到32位的商和余数。如果被除数是16位的,则由DX:AX提供了32位的被除数。或者如果被除数是8位的,则AL提供了8位的被除数。通过使用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 4.11 ADC/SBB

ADC和SBB是计算机汇编语言中的指令,用于完成带进位的扩展加法和扩展减法。通过使用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 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 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 ; 存放低位 ; 计算32位减法操作 执行sbb相当于edx中的值5减去原始值1 mov edx,5h ; 设置 edx = 5 mov eax,80000000h ; eax = 80000000h sub eax,90000000h ; eax - 90000000h = F0000000 sbb edx,0 ; edx = edx - 1 = 4 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 ; 符号扩展edx要么为1要么为0 and edx,7 ; 被除数 2^n - 1 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 = 5 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:[z] ; 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 ^ 62 / 040000001h = 4294967292 ; 输入的时候以QWORD输入.然后再点击一下变成DWORD则可以看到表达为-4 ; 此时直接对其NOT取反则可以得到原始的被除数的绝对值,前面加上负数即可 xor edx,edx mov ecx,dword ptr ds:[y] ; ecx = 10 => 计算: 10 / -3 mov eax,040000001h ; eax = M mul ecx shr edx,01eh ; edx = n invoke crt_printf,addr szFmt,edx invoke ExitProcess,0 main ENDP END main

本文作者: 王瑞本文链接: https://www.lyshark.com/post/b4026f94.html版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3