汇编语言学习笔记(六)-数学运算

在上面几篇已经提到了一些数学运算功能,这篇再详细介绍之,数学运算无非是一些加减乘除,移位等等,就是这些简单的操作造就了丰富多彩的程序世界,可以说程序的根本就是计算。

指令介绍

加减法

首先说加减法,其实加减法本身没什么好说,但是还是跟mov指令一样,AT&T和MASM语法的操作数相反,造成了混乱。对AT&T来说:

	addx source, destination
	subx source, destination

其中x为操作数长度,与mov相同,这里的含义相当于 destination += source; 和 destination -= source

对MASM来说,语法格式为

	add destination, source
	sub destination, source

add 和 sub 指令都会影响进位,零标志,符号标志,溢出标识辅助进位和奇偶标志,方便使用条件控制。

inc和dec指令分别对寄存器或内存进行加一和减一操作,对于AT&T的指令,需要增加操作数长度指令b,l,w,q等,格式为:

	inc operand
	dec operand

相反数

neg 指令求寄存器和内存的相反数:

	neg reg
	neg mem

进位加减法,adc和sbb,这两个指令的格式与add和sub完全相同,所不同的是adc把源操作数,目的操作数和进位标识相加。sbb同样也减去进位标识,这两个指令的存在主要是为了进行大整数运算。

乘法

乘法mul,imul: 其中mul为无符号乘法,乘法的格式也比较简单,而imul为有符号乘法, 却有三种语法格式。语法如下:

以下是AT&T语法格式

	mul source
	
	imul source
	imul source, destination
	imul imm, source, destination

以下是MASM语法格式

	mul source
	
	imul source
	imul destination, source
	imul destination, source, imm

可以看到mul只有源操作数,那目的操作数呢?其实这里是隐含操作数。mul的操作数如下:

被乘数 乘数
AL r/m8 AX
AX r/m16 DX:AX
EAX r/m32 EDX:EAX

最所以积比被乘数宽度大一倍,是因为两个32位的乘数乘起来可能是64位的!

除法div,idiv 除法与乘法相反,mul的操作数表反过来便是div的操作数表,如下表所示:

被除数 除数 余数
AX r/m8 AL AH
DX:AX r/m16 AX DX
EDX:EAX r/m32 EAX EDX

这里跟乘法不同的是64位的操作数除以32位的操作数以后结果还可能大于32位,如此一来便会溢出,所以计算除法要千万小心,确保你的操作数不会出现溢出问题。 语法格式如下:

	; 因为只有一个操作数,所以AT&T语法格式和MASM相同
	div divisor
	div divisor

移位运算

先说一下算术移位和逻辑移位,二者对于左移来说没有什么区别,都是在空位上补0,但对于右移,对于无符号的应该补零,对于有符号的应该补符号位,所以就有了算术和逻辑,算术右移补符号位,一般用于有符号数,逻辑右移不论符号位是什么,一律补0。

循环移位,比如左移,先把所有位向左移一位,把最高位补到最低位的空缺,顺便把CF标志设置为最高位的值。 带进位的循环移位,跟循环移位相比,循环移位CF本身不参与移位,仅仅是记录最高位的值,而带进位的循环移位CF标志也参与移位,即CF放入对地位,高位填充至CF标志。

还有一种移位为双精度移位,拿左移为例,改指令把目的操作数左移指定的位数,左移空出来的位用源操作数的高位来填充,源操作数不变,相当于循环移位但是是对不同的操作数来讲的。

	shl 	逻辑左移
	shr 	逻辑右移
	sal 	算术左移
	sar 	算术右移
	rol 	循环左移
	ror 	循环右移
	rcl 	带进位循环左移
	rcr 	带进位循环右移
	shld 	双精度左移
	shrd 	双精度右移

以下是各种语法规则(imm8为8位立即数)

以下是AT&T语法格式

	; shl shr sal sar rol ror rcl rcr 指令格式相同
	shlx destination
	shlx imm8, destination
	shlx %cl, destination

以下是MASM语法格式

	; shl shr sal sar rol ror rcl rcr 指令格式相同
	shl destination, imm8
	shl destination, cl
	
	; shld shrd 格式相同
	shld destination, source, cl/imm8

与或非运算

对AT&T语法格式 x位操作数宽度

	andx source, destination
	orx source, destination
	xorx source, destination
	notx source, destination

MASM 语法格式

	and destination, source
	or destination, source
	xor destination, source
	not reg/mem

test指令

test 指令与 and 指令相同,不过test指令不改变操作数的值,仅仅置eflags标志。

cmp指令

cmp 指令与 sub 指令相同,唯一的区别是cmp只置标记不保存结果,也就是cmp操作的源、目的操作数不变。

例子

这里由于指令过多,而且写的比较匆忙,没有一一举例,仅仅做了一个简单总结,我的一位挚友对此文每种指令都做了尝试,并指出一些错误,在次表示感谢!以下测试代码由这位挚友提供,由于没有输出,可由ollydbg调试观察结果。

	.386
	.model flat, stdcall
	option casemap:none
	
	include    windows.inc
	include    kernel32.inc
	includelib kernel32.lib
	
	.data
	    res db 5
	    res2 dw 0
	    res3 dd 0
	.code
	start:
	    add res, 5
	    sub res, 3
	    inc res
	    dec res
	    neg res
	
	    ;单操作数乘法
	    mov al, 4    ;被乘数根据位数不同分别默认存放于al(8)、ax(16)、eax(32)
	    mov ah, 8
	    mul ah
	    mov res2, ax ;积根据位数不同分别默认存放于ax(16)、dx:ax(32)、edx:eax(64)
	    ;32位积为什么不能直接存放于eax中呢?是因为刚开始计算机只有16位所以就按这种高位低位的方式来存放
	    ;后面发展成为32位后为了保持兼容性故还是这样存放
	
	    ;双操作数乘法
	    mov eax, 4
	    mov ebx, 8
	    imul eax, ebx
	    mov res3, eax
	
	    ;三操作数乘法
	    mov eax, 4
	    mov ebx, 8
	    imul eax, ebx, 32 ;所谓三操作数就是在最后多乘了一个立即数
	    mov res3, eax
	
	    ;除法
	    mov ax, 33
	    mov dx, 0
	    mov bx, 4
	    div bx
	    mov res, al  ;商
	    mov res, ah  ;余数
	
	    ;移位运算
	    shl res, 5
	    shr res, 2
	    sal res, 1
	    sar res, 2
	    rol res, 2
	    ror res, 2
	    rcl res, 3
	    rcr res, 4
	    ;此指令前两个寄存器必须是大于8位的,左边操作数可以是内存,最后一个必须为8位
	    shld res2, ax, 3
	    shrd res2, ax, 3
	
	    ;逻辑运算
	    mov ax, 0ffffh
	    mov res2, 1
	    and res2, ax
	    or res2, ax
	    mov ax, res2
	    xor res2, ax
	    not res2
	
	    ;test, cmp指令
	    mov ax, 0
	    test res2, ax
	    jz label1
	
	label1:
	    inc res2
	label2:
	    invoke ExitProcess, NULL
	end start
Built with Hugo
主题 StackJimmy 设计