第四章 数据传送、寻址和算术运算

来源:互联网 发布:开淘宝亏钱了 编辑:程序博客网 时间:2024/06/07 03:53

4.1 数据传送指令

4.1.3 直接内存操作数

.datavar1 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,regXCHG reg, memXCHG mem, reg

4.1.8 直接-偏移量操作数

arrayB BYTE 10h,20h,30h,40h,50hmov al, [arrayB+ 1 ]    ; 访问第二个字节(注意不是数组中第二个元素)

4.2加法和减法

4.2.1 INC 和 DEC 指令

INC(增加)和 DEC (减少)指令分别表示寄存器或内存操作数加 1 和减 1. 语法如下所示:

INC reg/ memDEC reg/ mem

4.2.2 ADD 指令

ADD 指令将长度相同的源操作数和目的操作数进行相加操作 。

ADD dest, source

4.2.3 SUB 指令

SUB 指令从目的操作数中减去源操作数。

SUB dest, source

4.2.4 NEG 指令

NEG (非)指令通过把操作数转换为其二进制补码,将操作数的符号取反。

NEG regNEG 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:

.datavall 6 LABEL WORDval32 DWORD 1234 5678h.codemov ax, val16       ; AX= 5678hmov 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 )加上括号就能构成一个间接操作数 。 寄存器中存放的是数据的地址 。

databyteVal BYTE l0h.codemov esi, OFFSE_T byteValmov al, [esi] ; AL = l0h

如果目的操作数也是间接操作数,那么新值将存人由寄存器提供地址的内存位置 。 在下面的例子中, BL 寄存器的内容复制到 ESI 寻址的内存地址中:

mov [esi] ,bl

PTR 与间接操作数一起使用

inc BYTE PTR [esi]

4.4.2 数组

间接操作数是步进遍历数组的理想工具。

.dataarrayD DWORD 10000h,20000h,30000h.codemov esi, OFFSET arrayDmov eax, [esi] ;(第一个数 )add esi,4add eax, [esi] ; (第二个数)add esi, 4add eax, [esi] ;{第三个数)

4.4.3 变址操作数

变址操作数是指,在寄存器上加上常数产生一个有效地址。

dataarrayW WORD 1000h,2000h,3000h.codemov esi. OFFSET arrayWmov ax, [esi]      ; AX = lOOOhmov ax, [esi+2]    ; AX = 2000hmov ax, [esi+4]    ; AX = 3000h

4.4.4 指针

ptrB中保存的是arrayB的指针。

arrayB BYTE 10h, 20h, 30h, 40hptrB DWORD OFFSET arrayB

使用 TYPEDEF 运算符

PBYTE TYPEDEF PTR BYTE

使用typedef后可以PBYTE进行定义指针。

. dataarrayB BYTE 10h,20h, 30h , 40hptrl 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,5Ll:    inc ax    loop Ll

4.5.5 复制字符串

.stack 4096ExitProcess proto,dwExitCode:dword.datasource BYTE ”This is the source string",0target BYTE SIZEOF source DUP(0).codemain PROC    mov esi, 0              ; 变址寄存器    mov ecx,SIZEOF source   ; 循环计数器Ll:                         ; 从源字符串获取一个字符    mov al,source[esi]          ; 保存到目标字符串    mov target [esi],al         ; 指向下一个字符    inc esi                     ; 重复,直到整个字符串完成    loop Llinvoke ExitProcess,0main ENDPEND 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 位:

.datamyDword DWORD 80000000h.codemov rax, OFFFFFFFFPFFFFFFFhmov eax, myDword ; RAX= 0000000080000000

但是,如果是将 8 位或 16 位内存操作数送入 RAX 的低位,那么,目标寄存器的高位不
受影响:

datamyByte BYTE 55hmyWord WORD 6666h.codemov ax,myWord  ;位 16一位 63 不受影响mov al, myByte ;位 8 一位 63 不受影响

MOVSXD 指令(符号扩展传送)允许源操作数为 32 位寄存器或内存操作数 。

mov ebx, OFFFFFFFFhmovsxd rax,ebx ; rax = FFFFFFFFFFFFFFFFh

OFFSET 运算符产生 64 位地址,必须用 64 位寄存器或变量来保存 。

datamyArray WORD 1 0 , 20,30,40.codemov rsi,OFFSET myArray
原创粉丝点击