4.1 数据传送指令
4.1.3 直接内存操作数
.data
var1 BYTE 10h ;变量名引用的是数据段内的偏移量
mov al var1 ;通过内存操作数地址解析操作数
假设var1的地址偏移量为104
00h, 指令会被会变为机器指令:
A0 00010400
4.1.4 MOV指令
mov destination, source
- 必须大小相同
- 不能同时为内存操作数
- 指令指针寄存器(IP、EIP或RIP)不能作为目标操作数
4.1.5 整数的全零/符号扩展
- MOVEZX指令 将源操作数复制到目的操作数,并把目的操作数0扩展到16位或32位。这条指令只用于无符号整数。
- MOVESX指令 将源操作数复制到目的操作数,并把目的操作数扩展到16位或32位。这条指令只用于有符号整数。
4.1.6 LAHF和SAHF指令
LAHF(加载状态标志位到AH)指令将EFLAGS寄存器的第字节复制到AH。被复制的标志位包括:符号标志位、零标志位、辅助进位标志位、奇偶标志位和进位标志位。使用这条指令可以方便的把标志位保存在变量中。
SAHF(保存AH内容到状态标志位)指令将AH内容复制到EFLAGS(或RFLAGS)寄存器低字节。
4.1.7 XCHG指令
交换两个操作数的内容。
XCHG reg,reg
XCHG reg, mem
XCHG mem, reg
4.1.8 直接-偏移量操作数
arrayB BYTE 10h,20h,30h,40h,50h
mov al, [arrayB+ 1 ] ; 访问第二个字节(注意不是数组中第二个元素)
4.2加法和减法
4.2.1 INC 和 DEC 指令
INC(增加)和 DEC (减少)指令分别表示寄存器或内存操作数加 1 和减 1. 语法如下所示:
INC reg/ mem
DEC reg/ mem
4.2.2 ADD 指令
ADD 指令将长度相同的源操作数和目的操作数进行相加操作 。
ADD dest, source
4.2.3 SUB 指令
SUB 指令从目的操作数中减去源操作数。
SUB dest, source
4.2.4 NEG 指令
NEG (非)指令通过把操作数转换为其二进制补码,将操作数的符号取反。
NEG reg
NEG mem
4.2.5 执行算术表达式
4.2.6 加减法影响的标志位
- CF(CY) 进位标志位意味着无符号整数溢出 。 比如,如果指令目的操作数为 8 位,而指令产生的结果大于二进制的 1111 1111 ,那么进位标志位置 1 。
- OF(OV) 溢出标志位意味着有符号整数溢出 。 比如,指令目的操作数为 16 位,但其产生的负数结果小于十进制的-32768 ,那么溢出标志位置 1 。
- ZF(ZR) 零标志位意味着操作结果为 0 。 比如,如果两个值相等的操作数相减,则零标志位置 1。
- SF(PL) 符号标志位意味着操作产生的结果为负数。 如果目的操作数的最高有效位( MSB )置1, 则符号标志位置 1。
- PF(PL) 奇偶标志位是指,在一条算术或布尔运算指令执行后,立即判断目的操作数最低有效字节中 1 的个数是否为偶数。
- AC 辅助进位标志位置 1 ,意味着目的操作数最低有效字节中位 3 有进位。
INC 和 DEC 指令不会影响进位标志位 。 在非零操作数上应用 NEG 指令总是会将进位标志位置 1 。
4.3 与数据相关的运算符和伪指令
4.3.1 OFFSET 运算符
OFFSET 运算符返回数据标号的偏移量 。 这个偏移量按字节计算,表示的是该数据标号距离数据段起始地址的距离 。
4.3.2 ALIGN 伪指令
ALIGN 伪指令将一个变量对齐到字节边界、字边界、双字边界或段落边界。
ALIGN bound
Bound 可取值有: 1 、 2 、 4 、 8 、 16 。 当取值为 1 时,则下一个变量对齐于 1 字节边界(默认情况) 。 当取值为 2 时,则下一个变量对齐于偶数地址 。 当取值为 4 时,则下一个变量地
址为 4 的倍数 。 当取值为 16 时,则下一个变量地址为 16 的倍数,即一个段落的边界 。 为了满足对齐要求,汇编器会在变量前插入一个或多个空字节 。 为什么要对齐数据? 因为,对于存储于偶地址和奇地址的数据来说, CPU 处理偶地址数据的速度要快得多。
4.3.3 PTR 运算符
PTR 运算符可以用来重写一个已经被声明过的操作数的大小类型。只要试图用不同于汇编器设定的大小属性来访问操作数,那么这个运算符就是必需的 。
例如,假设想要将一个双字变量 myDouble 的低 16 位传送给 AX。
mov ax, WORD PTR myDouble
注意, PTR 必须与一个标准汇编数据类型一起使用,这些类型包括: BYTE 、 SBYTE 、WORD 、 SWORD 、 DWORD 、 SDWORD 、 FWORD 、 QWORD 或 TBYTE 。
4.3.4 TYPE 运算符
TYPE 运算符返回 变量单个元素的大小,这个大小是以字节为单位计算的。
4.3.5 LENGTHOF 运算符
LENGτHOF 运算符计算数组中元素的个数,元素个数是由数组标号同一行出现的数值来定义的 。
4.3.6 SIZEOF 运算符
SIZEOF 运算符返回值等于 LENGTHOF 与 TYPE 返回值的乘积。
4.3.7 LABEL 伪指令
LABEL 伪指令可以插入一个标号,并定义它的大小属性,但是不为这个标号分配存储空间 。 LABEL 中可以使用所有的标准大小属性,如 BYTE、 WORD 、 DWORD 、 QWORD 或 TBYTE。 LABEL 常见的用法是,为数据段中定义的下一个变量提供不同的名称和大小属性。 如下例所示,在变量 val32 前定义了一个变量,名称为 val16 ,属性为 WORD:
.data
vall 6 LABEL WORD
val32 DWORD 1234 5678h
.code
mov ax, val16 ; AX= 5678h
mov dx, [val16+2) ; DX= 1234h
vall6 与 val32 共享同一个内存位置 。 LABEL 伪指令自身不分配内存 。
有时需要用两个较小的整数组成一个较大的整数,如下例所示,两个 16 位变量组成一个 32 位变量并加载到 EAX 中:
.data
LongValue LABEL OWORD
vall WORD 5678h
va12 WORD 1234h
.code
mov eax,LongValue ;EAX = 12345678h
4.4 间接寻址
4.4.1 间接操作数
任何一个 32 位通用寄存器( EAX 、 EBX 、 ECX 、 EDX 、 ESI 、 EDI 、 EBP 和ESP )加上括号就能构成一个间接操作数 。 寄存器中存放的是数据的地址 。
data
byteVal BYTE l0h
.code
mov esi, OFFSE_T byteVal
mov al, [esi] ; AL = l0h
如果目的操作数也是间接操作数,那么新值将存人由寄存器提供地址的内存位置 。 在下面的例子中, BL 寄存器的内容复制到 ESI 寻址的内存地址中:
mov [esi] ,bl
PTR 与间接操作数一起使用
inc BYTE PTR [esi]
4.4.2 数组
间接操作数是步进遍历数组的理想工具。
.data
arrayD DWORD 10000h,20000h,30000h
.code
mov esi, OFFSET arrayD
mov eax, [esi] ;(第一个数 )
add esi,4
add eax, [esi] ; (第二个数)
add esi, 4
add eax, [esi] ;{第三个数)
4.4.3 变址操作数
变址操作数是指,在寄存器上加上常数产生一个有效地址。
data
arrayW WORD 1000h,2000h,3000h
.code
mov esi. OFFSET arrayW
mov ax, [esi] ; AX = lOOOh
mov ax, [esi+2] ; AX = 2000h
mov ax, [esi+4] ; AX = 3000h
4.4.4 指针
ptrB中保存的是arrayB的指针。
arrayB BYTE 10h, 20h, 30h, 40h
ptrB DWORD OFFSET arrayB
使用 TYPEDEF 运算符
PBYTE TYPEDEF PTR BYTE
使用typedef后可以PBYTE进行定义指针。
. data
arrayB BYTE 10h,20h, 30h , 40h
ptrl PBYTE ? ; 未初始化
ptr2 PBYTE arrayB ; 指向一个数组
4.5 JMP 和 LOOP 指令
4.5.1 JMP 指令
JMP 指令无条件跳转到目标地址 ,该地址用代码标号来标识,并被汇编器转换为偏移量。
JMP destination
创建一个循环
top:
.
.
jmp top ; 不断地循环
4.5.2 LOOP指令
LOOP 指令,正式称为按照 ECX 计数器循环,将程序块重复特定次数 。 ECX 自动成为计数器,每循环一次计数值减 1 。
mov ax, 0
mov ecx,5
Ll:
inc ax
loop Ll
4.5.5 复制字符串
.stack 4096
ExitProcess proto,dwExitCode:dword
.data
source BYTE ”This is the source string",0
target BYTE SIZEOF source DUP(0)
.code
main PROC
mov esi, 0 ; 变址寄存器
mov ecx,SIZEOF source ; 循环计数器
Ll: ; 从源字符串获取一个字符
mov al,source[esi] ; 保存到目标字符串
mov target [esi],al ; 指向下一个字符
inc esi ; 重复,直到整个字符串完成
loop Ll
invoke ExitProcess,0
main ENDP
END main
4.6 64位编程
4.6.1 MOV 指令
立即操作数(常数)可以是 8 位、 16 位 、 32 位或 64 位 。 下面为一个 64 位示例:
mov rax, OABCDEFOAFFFFFFFh ; 64位立即操作数
当一个32位常数送入 64 位寄存器时,目标操作数的高 32 位(位 32一位 63 )被清除(等于 0 ):
mov rax, OFFFFFFFFh ;rax=OOOOOOOOFFFFFFFF
向 64 位寄存器送人 16 位或 8 位,常数,其高位也要清零。
传送一个 32 位内存操作数到 EAX (RAX 寄存器的低半部分),就会清除 RAX 的高 32 位:
.data
myDword DWORD 80000000h
.code
mov rax, OFFFFFFFFPFFFFFFFh
mov eax, myDword ; RAX= 0000000080000000
但是,如果是将 8 位或 16 位内存操作数送入 RAX 的低位,那么,目标寄存器的高位不
受影响:
data
myByte BYTE 55h
myWord WORD 6666h
.code
mov ax,myWord ;位 16一位 63 不受影响
mov al, myByte ;位 8 一位 63 不受影响
MOVSXD 指令(符号扩展传送)允许源操作数为 32 位寄存器或内存操作数 。
mov ebx, OFFFFFFFFh
movsxd rax,ebx ; rax = FFFFFFFFFFFFFFFFh
OFFSET 运算符产生 64 位地址,必须用 64 位寄存器或变量来保存 。
data
myArray WORD 1 0 , 20,30,40
.code
mov rsi,OFFSET myArray