一 数据传送指令
1.操作数类型
主要包括三种操作数:立即操作数,寄存器操作数
1.1直接使用内存操作数
变量名仅仅是对数据段内偏移地址的引用,下面声明了一个包含数字10H的一个字节被置于数据段内
.data
var1 BYTE 10h
指令使用内存操作数实际上还是使用的操作数的地址,假设var1位于0x10400处,那么将var1送入AL寄存器的汇编指令如下:
MOV AL, var1
编译器将这段指令翻译成
A0 00010400
机器指令的第一个字节是操作码,剩下的部分是变量var1的十六进制的地址值,编写程序时可以使用纯数字地址表示,不过使用变量名更方便
还可以使用这种方法表示
mov al, [var1]
2.mov指令
mov destination, source
要遵循以下规则
两个操作数的尺寸必须一致 两个操作数不能同时为内存操作数
目的操作数不能使CS,EIP和IP
立即数不能直接送至段寄存器
在运行与保护模式下,不应该直接修改段寄存器,一般来说,段寄存器仅由实模式下的程序使用,对段寄存器可以有以下两种格式
mov r/m16, sreg
mov sreg, r/m16
单条MOV指令不能把数据从一个内存位置直接移动到另外一个内存位置, 可以先把内存中的数据移动到寄存器,然后在移动到另外一个内存位置
3.整数的零,符号扩展
复制较小值至较大值中:
movzx指令将源操作数的内容复制到目的操作数中,并将该值零扩展到16位或32位,该指令仅适用于无符号整数。
movzx r32, r/m8
movzx r32, r/m16
movzx r16, r/m8 目的操作数必须是寄存器
movsx将源操作数的内容复制到目的操作数中,并将该值符号扩展到16位或32位。该指令只能用于有符号整数:
movsx r32, r/m8
movsx r32, r/m16
mov sx r16, r/m8
4.LAHF和SAHF
LAHF指令将EFLAGS寄存器的低字节复制到AH寄存器中,被复制的标志包括符号标志,零标志,辅助进位标志,奇偶标志和进位标志。
SAHF指令复制AH寄存器的值至EFLAGS寄存器的低字节。
5.XCHG
XCHG指令交换两个操作数的内容,它有下面三种格式:
XCHG reg, reg
XCHG reg, mem
XCHG mem, reg
XCHG不接受立即数, 其他规则跟MOV指令的操作数遵循同样的规则。
要交换2个内存的值时, 比较方便:
mov eax, var1
XCHG eax, var2
mov var2, eax
6.直接偏移操作数
在变量名后面加上一个偏移值, 可以创建直接偏移操作数,可以通过它来访问没有显示标号的内存地址:
array1 BYTE 10h, 20h, 30h, 40h
如果 mov 指令使用array1作为源操作数,将把数组的第一个字节送到AL
mov al, array1
可以通过在array1的偏移地址上加1来访问数组中的第二个字节
MOV al, [array1 + 1]
同样加2可以访问第三个字节
mov al, [array + 2]
但是编译器不做范围检查,因此对数组的引用要小心
字和双字数组做地址加减是,要乘上元素大小
array1 word 100h, 200h, 300h
访问第二个就需要mov ax, [array1 + 2]
访问第三个 mov ax, [array1 + 4]
二 加法和减法
1. INC和DEC指令
INC和DEC指令从操作数中加1和减1:
INC reg/mem
dec reg/mem
2.ADD指令
ADD指令将同大小的源操作数和目的操作数相加:
ADD 目的操作数,源操作数
加法操作并不会改变源操作数, 相加的结果存储在目的操作数中,其操作数的格式与MOV指令的操作数格式相同
影响标志:进位标志, 零标志,符号标志, 溢出标志, 辅助进位标志和奇偶标志
3.SUB指令
SUB指令将源操作数从目的操作数中减掉,格式与ADD和MOV一样
影响标志:进位标志, 零标志,符号标志, 溢出标志, 辅助进位标志和奇偶标志
4.NEG指令
通过将数值转换为对应的补码而求得其相反数(这里书上说的有点问题啊, 求补和求补码不一样啊, 求补是求负数, 补码是补码,正数的补码是其本身)
注意这里是求补的意思,也就是求该数的负数
NEG reg/mem
5.加法和减法影响的 标志
进位标志用于表示无符号整数运算是否发生溢出, 假设指令的目的操作数为8位,如果大于了1111111那么进位标志就置位
标志位CF是进位标志位(Carry Flag)。 当两个数相加时,若最高位向上形成进位,则CF=1; 当两个数相减时,若最高位向上形成借位,则CF=1; 当两个无符号数相乘时,若乘积的高一半为0,则CF=0; 当两个带符号数相乘时,若乘积的高一半是低一半的符号扩展,则CF=0.
溢出标志用于表示有符号整数运算是否溢出,比如负数和负数, 正数和正数做加法 结果的符号跟操作数不同就产生了溢出
零标志判断结果是否为0
符号位用于判断运算结果是否为负
奇偶标志结果最低有效字节内中为1的位数为偶数置位
辅助进位用户第三位向高位产生进位1时置位,主要用于BCD码
无符号运算影响:零标志, 进位标志, 辅助进位标志
CPU如何检查溢出:将向最高有效位进位值和最高有效位的进位值进行异或。
NEG指令如果对最小负数进行求反, 那么会照成溢出, -127求反那么的128, 128溢出
三 和数据相关的操作符和伪指令
1.OFFSET 操作符
返回数据标号的偏移地址, 偏移地址代表标号距离数据段开始的距离,单位是以字节计算的。
2.ALIGN 指令
将变量的位置按字节,字,双子对齐。
ALIGN 边界值
边界值必须是1,2,4,16,后面一个边界值是前面一个边界值得倍数
3.PTR操作符
可以使用PTR操作符来重载操作数声明的默认尺寸, 这在试图以不同变量声明时所使用的尺寸属性来访问变量时非常有用
.data
muDouble DWORD 12345678h
.code
mov ax, myDouble; 错误
mov ax, WORD PTR myDouble
4.TYPE操作符
返回按字节计算的变量的单个元素的大小
TYPE var
5.LENGTHOF
计算组中元素个数,元素由出现在同一行的值定义:
.data
byte1 BYTE 10, 20, 30
array1 WORD 30 DUP(?), 0, 0
array2 WORD 5 DUP(3 DUP(?))
array3 DWORD 1, 2,3 ,4
digitStr BYTE "12345678", 0
LENGTHOF byte1 3
LENGTHOF array1 30 + 2
LENGTHOF array2 5*3
LENGTHOF array3 4
LENGTHOF digitStr 9
如果声明了跨多行的数组,值返回第一行的元素个数, 可以加逗号连接下一行初始值
6.SIZEOF操作符返回值等于LENGTHOF喝TYPE返回值的乘积
7.LABEL指令允许插入一个标号并赋予其后所定义的变量尺寸,而无需分配任何内存空间,就是给后面的变量去一个别名和不同尺寸类型
.DATA
var1 LABLE WORD
var2 DWORD 12345678h
.code
mov ax, val1 var1 是后面的数据的别名, 但是数据类型为WORD
mov dx, [val1 + 2]
四 间接操作数
在处理数组时,完全使用间接寻址是不且实际的, 我们不可能为每一个数组的每个元素都提供一个不同的标号,也不大可能使用非常多的常量偏移去寻址数组的各个元素,处理数组的唯一可行的方法是用寄存器作为指针并操纵寄存器的值
1.间接操作数
可以使用方括号括起任意的32位通用寄存器,寄存器里面存放着数据的偏移
.data
var1 BYTE 10h
.code
mov esi, OFFSET var1
mov al, [esi]
mov [esi], bl
2.使用PTR与间接操作数组合
有时候在一条指令上下文中,操作数大小通常不明确
inc [ESI] 错误, ESI地址数据类型并不确定
inc BYTE PTR [esi]
3.变址操作数
把常量和寄存器相加以得到一个地址。
第一种是把变量的名字和寄存器集合起来,变量的名字代表变量偏移地址常量,
.data
arrayb BYTE 10h, 20h, 30h
.code
mov esi, 0
mov al, [arrayb + esi]
另一种是变址寄存器和常量偏移联合起来使用,不过是用变址寄存器存放数组基地址,用常量表示偏移。
.data
arrayW WORD 1000h, 2000h, 3000h
.code
mov esi, OFFSET arrayW
mov ax, [esi + 2] ;2000H
带比例因子的变址寻址
.data
arrayD DWORD 1, 2, 3, 4
.code
mov esi, 3
mov eax, arrayD[esi*4]
用上TYPE更灵活
mov esi, 3
mov eax, arrayD[esi * TYPE arrayD]
4.指针
包含其他变量地址的变量称为指针变量
NEAR:相对于数据段开始的32位偏移地址
FAR:48位的段选择子
5.TYPEDEF
允许创建用户自定义的类型
PBYTE TYPEDEF PTR BYTE
.data
arrayB BYTE 10h, 20h, 30h, 40h
ptr1 PBYTE ?
ptr2 PBYTE arrayB
6.使用指针访问数据的例子
INCLUDE Irvine32.inc
PBYTE TYPEDEF PTR BYTE
PWORD TYPEDEF PTR WORD
PDWORD TYPEDEF PTR DWORD
.data
arrayB BYTE 10h, 20h, 30h
arrayW WORD 1, 2, 3
arrayD DWORD 4, 5, 6
ptr1 PBYTE arrayB
ptr2 PWORD arrayW
ptr3 PDWORD arrayD
.code
main PROC
mov esi ,ptr1
xor eax, eax
mov al, [esi]
mov esi, ptr2
xor ebx, ebx
mov bx, [esi]
mov esi, ptr3
mov ecx, [esi]
call DumpRegs
exit
main ENDP
END main
五 JMP和LOOP指令
1. jmp指令
jmp指令导致向代码段的目的地址做无条件转移, 标识目的地址的代码标号将由汇编器翻译成偏移地址, JMP指令格式是:
JMP 目的地址
2.LOOP 目的指令
重复执行一块语句, 执行次数是特定的, ECX是自动用作计数器, 在每次循环之后减1格式如下
loop 目的地址
LOOP指令的执行分成两步, 首先ECX减1, 接着与0比较, 如果ECX不等于0,跳转到目的地址, 如果ECX等于0, 则不发生跳转
mov ax, 0
mov ecx, 5
L1:
inc ax
loop L1
执行完后 ax = 5
循环的目的地址与当前地址只能在距-128~+127字节的范围内, 机器指令的平均大小是3字节左右,因此一个循环平均最多只能包含大约42条指令
例子:整数数组求和
INCLUDE Irvine32.inc
.data
arrayD DWORD 10h, 20h, 30h
.code
main PROC
mov ecx, LENGTHOF arrayD
mov eax, 0
mov edi, OFFSET arrayD
L1:
add eax, [edi]
add edi, TYPE arrayD
loop L1
call DumpRegs
exit
main ENDP
END main