操作数中有3基本类型
MOV destination,source
针对第5条原则举例:
.data
var1 WORD ?
var2 WORD ?
.code
mov ax,var1
mov var2,ax
MOVZX指令(进行全零扩展并传送)将源操作数复制到目的操作数
注意:源操作数不能是常数
举例:
.data
val BYTE 10001111b
.code
movzx ax,val ;AX=0000000010001111b
MOVSX指令(进行全零扩展并传送)将源操作数复制到目的操作数。
操作数在进行符号扩展时,在目的操作数上全部扩展位上重复(复制)长度较小操作数的最高位。
举例1:
.data
val BYTE 10001111b
.code
movsx ax,val ;AX=1111111110001111b
举例2:
mov bx,0A69Bh ;该处的0是一种方便的表示法,用于防止汇编器将常数误认为标识符
movsx eax,bx ;EAX=FFFFA69Bh
movsx edx,bl ;EDX=FFFFFF9Bh
movsx cx,bl ;CX=FF9Bh
LAHF(加载状态标志位到AH)指令将EFLAGS寄存器的低字节复制到AH。
被复制的标志位包括:符号标志位、零标志位、辅助进位标志位、奇偶标志位和进位标志位。使用该指令,可以方便的把标志位副本保管在变量中。
举例:
.data
saveflags BYTE ?
.code
lahf ;将标志位加载到AH
mov saveflags,ah ;用变量保存这些标志位
SAHF(保存AH内容到状态标志位)指令将AH内容复制到EFLAGS(或RFLAGS)寄存器低字节。
举例:
mov ah,saveflags ;加载被保存的标志位到AH中
sahf ;复制到EFLAGS寄存器
XCHG(交换数据)指令交换两个操作数内容
举例:
xchg ax,bx ;交换16位寄存器内容
xchg ah,bl ;交换8位寄存器内容
xchg var1,bx ;交换16位内存操作数和BX寄存器内容
xchg eax,ebx ;交换32位寄存器内容
如果要交换两个内存操作数,则用寄存器作为临时容器,把MOV指令与XCHG指令一起使用
.data
val WORD 1000h
val2 WORD 2000h
.code
mov ax,val ;AX=1000h
xchg ax,val2 ;AX=2000h,val2=1000h
mov val,ax ;val=2000h,AX=1000h
在默认情况下,CPU是顺序加载并执行程序。但是当前指令的执行有可能是由条件的,它会按照CPU状态位的值把控制转向程序中的新位置
汇编语言程序使用条件指令来实现IF语句的高级语句和循环。每条条件指令都包含了一个可能的转向不同内存地址的转移。
控制转移分为两种基本类型:
JMP指令无条件跳转到目标地址,该地址用代码标号来标识,并被汇编器转换为偏移量
当CPU执行一个无条件转移时,目标地址的偏移量被送入指针指令寄存器,从而导致从新地址开始执行
举例:
创建一个循环
top:
.
.
jmp top ;不断循环
JMP是无条件的,因此循环会去无休止地执行下去,除非找到其他办法退出循环。
按照ECX计数器循环,将程序重复特定次数,ECX自动成为计数器,每循环依次计数值减一
执行步骤:
举例:
下面的循环是将AX加1,循环结束时,AX=5,ECX=0
mov ax,0
mov exc,5
L1:
inc ax
loop L1
INC和DEC分别表示寄存器或内存操作数加1或减1
举例:
.data
val WORD 10h
.code
inc val ;val=11h
mov ax,val ;AX=11h
dec ax ;AX=10h
注意:根据目的操作数的值,溢出标志位,零标志位,辅助进位标志位,进位标志位和奇偶标志位会发生一定的变化.但是INC和DEC指令不会影响进位标志位
变量名加上一个位移就形成了一个直接-位移操作数
这样就可以访问那些没有显式标记的内存位置。
举例:
array BYTE 10h,20h,30h,40h,50h
mov al,array ;AL=10h 自动传递数组的第一个字符
mov al,array+1 ;AL=20h
mov al,[array+2];AL=30h
字数组
在16位的字数组中,每个数组元素的偏移量比前一个多两个字节
因此,下面的array+2才能指向该数组的第二个元素
.data
array WORD 10h,20h,30h
.code
mov ax,array ;AX=10h
mov ax,[array+2] ;AX=20h
双字数组
在32位的双字数组中,每个数组元素的偏移量比前一个多4个字节
因此,下面的array+4才能指向第二个元素
.data
array DWORD 10h,20h,30h
.code
mov ax,array ;AX=10h
mov ax,[array+4] ;AX=20h
进位标志位(CF):无符号整数的溢出
比如:如果指令目的操作数为8位,而指令产生的结果大于二进制的1111 1111,那么进位标志置1zhijiepainyi
符号标志位(SF):操作数产生的结果为负数。
如果目的操作数的最高有效位(MSB)置1,则符号标志位置1
奇偶标志位(PF):在一条算术或布尔运算指令执行后,立即判断目的操作数最低有效字节中1的个数是否位偶数
辅助进位标志位(AC):目的操作数最低有效字节中位3有进位,则辅助进位标志位置1
两个无符号整数进行相加时,进位标志位是目的操作数最高有效位进位的副本。也就是说,如果和数超过了目的操作数的存储大小,就认为CF=1.
mov al,0FFh ;AL是8位
add al,1 ;AL=00,CF=1
如果AX的值为00FFh,则对其进行加1操作后,和数不会超过16位,则进位标志位清零。
mov ax,00FFh
add ax,1 ;AX=0100h,CF=0
但是如果AX的值为FFFFh, 则对其进行加1操作后,AX的高位就会产生进位
mov ax,0FFFFh
add ax,1 ;AX=0000,CF=1
从较小的无符号整数中减去较大的无符号整数时,减法操作就会将进位标志位置1
举例:
操作数为8位,计算1-2
mov al,1
sub al,2 ;AL=FFh,CF=1
INC和DEC指令不会影响进位标志位。在非零操作上应用NEG指令总是会将进位标志位置1
辅助进位(AC)标志位意味者目的操作数位3有进位或借位。主要应用于二进制编码的十进制数(BCD)运算。
举例:计算(1+0Fh)
mov al,0Fh
add al,1 ;AC=1
目的操作数最低有效字节中1的个数为偶数时,奇偶(PF)标志位置1
mov al,10001100b
add al,00000010b ;AL=10001110,PF=1
sub al,10000000b ;AL=00001110,PF=0
执行ADD指令后,AL的值为1000 1110(4个0,4个1),PF=1
执行SUB指令后,AL的值为0000 1110(5个0,3个1),PF=0
有符号算术操作结果为负数,则符号标志位置1
举例:4-5
mov eax,4
sub eax,5 ;EAX=-1,SF=1
有符号算术操作结果与目的操作数相比,如果发生上溢或下溢,则溢出标志位置1
举例:
mov al,+127
add al,1 ;OF=1
mov al,-128
sub al,1 ;OF=1
加法测试
硬件如何检测溢出
CPU用一种有趣的机制来检测标志位的状态。计算结果的最高有效位产生的进位与结果的最高位进行异或操作,异或的结果作为溢出标志位的值
NEG指令
如果NEG指令的目的操作数无法正确存储,则该结果无效
举例:
AL中存放的是-128,对其求反,结果为128,但是这个值无法存入AL。则溢出标志位置1,表示AL中存放的是一个无效的结果。
mov al,-128 ;AL=01111111b
neg al ;AL=10000000b,OF=1
如果对+127求反,结果是有效的,则溢出标志位清0
mov al,+127 ;AL=01111111b
neg al ;AL=100000001b,OF=0
运算符和伪指令不是可执行指令,它们由汇编器进行分析。
OFFSET运算符返回数据标号的偏移量(按字节计算),表示该数据标号距离数据段起始地址的距离。
举例:
.data
bval BYTE ?
wval WORD ?
dval DWORD ?
dval2 DWORD ?
假设bval在偏移量为0040 4000(十六进制)的位置,则OFFSET运算符的返回值如下
mov esi,OFFSET bval ;ESI=004040000h
mov esi,OFFSET wval ;ESI=004040001h
mov esi,OFFSET dval ;ESI=004040003h
mov esi,OFFSET dval2 ;ESI=004040007h
OFFSET也可应用于直接—偏移量操作数
设myarry包含5个16位的字。下面的MOV指令首先得到myarray的偏移量,再加4,将形成的结果地址直接传送给ESI。此时,ESI指向数组中的第3个整数。
注意:加4代表加4个字节,而myarray中每个数占两个字节,因此加4就指到第3个整数。
.data
myarray WORD 1,2,3,4,5
.code
mov esi,OFFSET myarray+4
还可以用一个变量的偏移量来初始化另一个双字变量,从而有效地创建一个指针。
举例:
.data
bigarray DWORD 50 DUP(?)
parray DWORD bigarray
.code
mov esi,parray
parray指向bigarray的起始地址,mov指令将该指针的值加载到ESI中,因此,ESI 寄存器就可以指向数组的起始地址。
PTR运算符可以用来重写一个已经被声明过的操作数的大小类型。
举例:
假设想要将一个双字变量mydouble的低16位传送给AX,由于操作数大小不匹配。因此,汇编器不允许这种操作。
.data
mydouble DWORD 12345678h
.code
mov ax,mydouble
但是使用WORD PTR运算符就能将低位字(5678h)传送给AX:
mov ax,WORD PTR mydouble
为什么送入AX的不是1234h?
不论该变量时如何定义的,都可以用3种方法种的任何一种来访问内存。
比如,如果mydouble的偏移量位0000,则以这个偏移量位首地址存放的16位值是5678h.同时也可以检索到1234h,其字地址为mydouble+2,指令如下
mov ax,WORD PTR [mydouble+2] ;1234h
同样,用BYTE PTR 运算符能够把mydouble的单个字节传送到BL:
mov bl,BYTE PTR mydouble ;78h
注意:PTR必须与一个标准汇编数据类型一起使用
这些类型包括:BYTE、 ABYTE WORD 、 SWORD 、 DWORD 、 SDWORD 、 FWORD 、 QWORD 、 TBYTE
应用场景:
将较小的数送入较大的目的操作数
举例:
如下,第一个字复制到EAX低半部分,第二个字复制到高半部分。
.data
wordlist WORD 5678h,1234h
.code
mov eax,DWORD PTR wordlist ;EAX=12345678h
TYPE运算符返回变量单个元素的大小(以字节为单位计算)
举例:
.data
var1 BYTE ?
var2 WORD ?
var3 DWORD ?
var4 QWORD ?
LENGTHOF运算符计算数组中元素的个数,元素个数是由数组标号同一行出现的数值来定义的。
举例:
.data
byte1 BYTE 10,20,30
arr1 WORD 30 DUP(?),0,0
arr2 WORD 5 DUP(3 DUP(?))
arr3 DWORD 1,2,3,4
digitstr BYTE "12345678",0
myarray BYTE 10,20,30,40,50
BYTE 60,70,80,90,100
该LENGTHOF myarray返回值为5
而以下定义数组LENGTHOF myarray 的返回值为10
myarray BYTE 10,20,30,40,50
60,70,80,90,100
SIZEOF运算符返回值等于LENGTHOF 与TYPE返回值的乘积
举例:
intarray 数组的TYPE=2,LENGTHOF=32,则SIZEOF intarray=64
.data
intarray WORD 32 DUP(0)
.code
mov eax,SIZEOF intarray ;EAX=64
ALIGN伪指令将一个变量对其到字节边界、字边界、双字边界或段落边界。
ALIGN bound
bound可取值为:1、2、4、8、16。
当取值为1时,则下一个变量对齐于1字节边界
当取值为2时,则下一个变量对其于偶数地址
当取值为4时,则下一个变量地址为4的倍数
当取值为16时,则下一个变量的地址为16的倍数(一个段落的边界)
为什么要对齐数据?
对于存储偶地址和奇地址的数据来说,CPU处理偶地址数据的速度要快得多。
举例:
bval处于任意位置,但其偏移量为0040 4000,再wval之前插入ALIGN 2伪指令,使得mval对齐于偶地址偏移量
bval BYTE ? ;00404000h
ALIGN 2
wval WORD ? ;00404002h
bval2 BYTE ? ;00404004h
ALIGN 4
dval DWORD ? ;00404008h
dval2 DWORD ? ;0040400ch
上述例子中dval的偏移量本来应该是00404005,但是ALIGN 4伪指令使它的偏移量成为00404008
LABEL伪指令可以插入一个标号,并定义它的大小属性,但是不为这个标号分配存储空间。
LABEL中而可以使用所有的标准大小属性。如BYTE 、WORD 、 DWORD 、QWORD 、TBYTE。
LABEL常见的用法:为数据段中定义的下一个变量提供不同的名称和大小属性。
举例:
在变量val32前定义一个变量val16,属性为WORD
.data
val16 LABEL WORD
val32 DWORD 12345678h
.code
mov ax,val16 ;AX=5678h
mov dx,[val16+2] ;DX=1234h
val16和val32共享同一个内存位置。LABEL伪指令自身不分配内存。
应用场景:
用两个较小的整数组成一个较大的整数。
例如:
两个16位变量组成一个32位变量并加载到EAX中
.data
longval LABEL DWORD
val1 WORD 5678h
val2 WORD 1234h
.code
mov eax,longval ;EAX=12345678h