简单算术运算汇编指令和CPU状态标志位介绍

文章目录

  • 数据传送指令
    • 操作数类型
    • MOV指令
    • MOVZX指令(只用于无符号整数)
    • MOVSX指令(只用于有符号整数)
    • LAHF和SAHF指令
    • XCHG指令
    • JMP指令
    • LOOP指令
    • INC(增加)和DEC(减少)指令
    • 直接偏移量操作数
  • CPU标志状态位
    • 无符号运算:零标志位、进位标志位和辅助进位标志位
      • 加法和进位标志位
      • 减法和进位标志位
      • 辅助进位标志位
      • 奇偶标志位
    • 有符号运算:符号标志位和溢出标志位
      • 符号标志位
      • 溢出标志位
  • 与数据相关的运算符和伪指令
    • OFFSET运算符
    • PTR运算符
    • TYPE运算符
    • LENGTHOF运算符
    • SIZEOF运算符
  • 伪指令
    • ALIGN伪指令
    • LABEL伪指令

数据传送指令

操作数类型

操作数中有3基本类型

  • 立即数:使用数字文本表达式
  • 寄存器操作数:使用CPU内已命名的寄存器
  • 内存操作数:引用内存位置

MOV指令

  • MOV指令将源操作数复制到目的操作数
  • 目的操作数的内容会发生改变,源操作数不会改变
MOV destination,source
  • 使用原则
  • 两个操作数必须是同样的大小
  • 两个操作数不能同时为内存操作数
  • 指令指针寄存器(IP、EIP、RIP)不能作为目的操作数
  • 单条MOV指令不能用于直接将数据从一个内存位置传送到另一个内存位置
  • 在源操作数的值赋给内存操作数之前,必须先将该数值传送给一个寄存器

针对第5条原则举例:

.data
var1  WORD ?
var2  WORD ?

.code
mov  ax,var1
mov  var2,ax

MOVZX指令(只用于无符号整数)

MOVZX指令(进行全零扩展并传送)将源操作数复制到目的操作数
注意:源操作数不能是常数

举例:

.data
val  BYTE           10001111b

.code
movzx  ax,val       ;AX=0000000010001111b

MOVSX指令(只用于有符号整数)

MOVSX指令(进行全零扩展并传送)将源操作数复制到目的操作数。
操作数在进行符号扩展时,在目的操作数上全部扩展位上重复(复制)长度较小操作数的最高位。

举例1:

.data
val BYTE 10001111b

.code
movsx  ax,val    ;AX=1111111110001111b

简单算术运算汇编指令和CPU状态标志位介绍_第1张图片

举例2:

mov    bx,0A69Bh   ;该处的0是一种方便的表示法,用于防止汇编器将常数误认为标识符
movsx  eax,bx      ;EAX=FFFFA69Bh
movsx  edx,bl      ;EDX=FFFFFF9Bh
movsx  cx,bl       ;CX=FF9Bh

LAHF和SAHF指令

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(交换数据)指令交换两个操作数内容

  • 使用原则
  • 除了不适用立即数做操作数,XCHG指令操作数的要求与MOV指令操作数要求一样

举例:

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

JMP指令

在默认情况下,CPU是顺序加载并执行程序。但是当前指令的执行有可能是由条件的,它会按照CPU状态位的值把控制转向程序中的新位置

汇编语言程序使用条件指令来实现IF语句的高级语句和循环。每条条件指令都包含了一个可能的转向不同内存地址的转移。

控制转移分为两种基本类型:

  • 无条件转移:无论什么情况都会转移到新地址.新地址加载到指令指针寄存器,使得程序在新地址进行执行。使用JMP指令实现
  • 条件转移:满足某种条件,则程序出现分支。CPU根据 ECX和标志寄存器的内容来判断真/条件

JMP指令无条件跳转到目标地址,该地址用代码标号来标识,并被汇编器转换为偏移量

当CPU执行一个无条件转移时,目标地址的偏移量被送入指针指令寄存器,从而导致从新地址开始执行

举例:
创建一个循环

top:
     .
     .
     jmp  top   ;不断循环

JMP是无条件的,因此循环会去无休止地执行下去,除非找到其他办法退出循环

LOOP指令

按照ECX计数器循环,将程序重复特定次数,ECX自动成为计数器,每循环依次计数值减一

执行步骤:

  1. ECX减一
  2. 将ECX与0比较,如果ECX不等于0,则跳转到由目标给出的标号。如果ECX等于0则不发生跳转,并将控制循环传递到循环后面的指令。

举例:
下面的循环是将AX加1,循环结束时,AX=5,ECX=0

mov ax,0
mov exc,5

L1:
      inc   ax
      loop  L1

INC(增加)和DEC(减少)指令

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

CPU标志状态位

  • 进位标志位(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

简单算术运算汇编指令和CPU状态标志位介绍_第2张图片
如果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

简单算术运算汇编指令和CPU状态标志位介绍_第3张图片

INC和DEC指令不会影响进位标志位。在非零操作上应用NEG指令总是会将进位标志位置1

辅助进位标志位

辅助进位(AC)标志位意味者目的操作数位3有进位或借位主要应用于二进制编码的十进制数(BCD)运算。

举例:计算(1+0Fh)

mov  al,0Fh
add  al,1  ;AC=1

简单算术运算汇编指令和CPU状态标志位介绍_第4张图片

奇偶标志位

目的操作数最低有效字节中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用一种有趣的机制来检测标志位的状态。计算结果的最高有效位产生的进位与结果的最高位进行异或操作,异或的结果作为溢出标志位的值

简单算术运算汇编指令和CPU状态标志位介绍_第5张图片

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运算符:返回变量与其所在段起始地址之间的距离
  • PTR运算符 :重写操作数默认的大小类型
  • TYPE运算符:返回一个操作数或数组中每个元素的大小(按字节计算)
  • LENHTHOF运算符:返回数组中元素的个数
  • SIZEOF运算符:返回数组初始化时使用的字节数

OFFSET运算符

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运算符

PTR运算符可以用来重写一个已经被声明过的操作数的大小类型。

举例:
假设想要将一个双字变量mydouble的低16位传送给AX,由于操作数大小不匹配。因此,汇编器不允许这种操作。

.data
mydouble  DWORD  12345678h

.code
mov ax,mydouble 

但是使用WORD PTR运算符就能将低位字(5678h)传送给AX:

mov ax,WORD PTR mydouble

为什么送入AX的不是1234h?

因为采用小端存储格式,即低位字节存放于变量的起始地址
简单算术运算汇编指令和CPU状态标志位介绍_第6张图片

不论该变量时如何定义的,都可以用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运算符

TYPE运算符返回变量单个元素的大小(以字节为单位计算)

举例:

  • TYPE为字节,返回值是1;
  • TYPE为字,返回值是2;
  • TYPE为双字,返回值是4;
  • TYPE为四字,返回值是8
.data
var1  BYTE   ?
var2  WORD   ?
var3  DWORD  ?
var4  QWORD  ?

简单算术运算汇编指令和CPU状态标志位介绍_第7张图片

LENGTHOF运算符

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

简单算术运算汇编指令和CPU状态标志位介绍_第8张图片
注意

  • 如果数组定义中出现了嵌套的DUP运算符则LENFTHOF返回的是两个数值的乘积
  • 如果数组定义占据了多个程序行则LENGTHOF只针对第一行定义的数据
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运算符

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伪指令将一个变量对其到字节边界、字边界、双字边界或段落边界。

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伪指令可以插入一个标号,并定义它的大小属性,但是不为这个标号分配存储空间。

  • 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

你可能感兴趣的:(汇编语言)