• Intel汇编语言程序设计学习-第四章 数据传送、寻址和算术运算-上


    数据传送、寻址和算术运算

    4.1  数据传送指令

    4.1.1  简介

        第一段一大推,就是为了说明汇编相比高级语言来说比较麻烦需要注意很多细节,但是换来的好处是给开发者带来了更大的灵活性。

    读者如果肯花时间彻底掌握本章内容,那么本书的后续部分将更容易学习。对以后变得越来越复杂的例子程序的理解,将在很大程度上依赖于对本章提供的基本工具的掌握。

    4.1.2  操作数类型

        本章讲述三种类型的操作数:立即操作数(immediate)、寄存器操作数(register)和内存操作数(memory)。在这三者当中,只有内存操作数稍微有点复杂。下表列出的操作数的简写符号是从Intel IA-32手册上摘录下来的。

     

    4.1.3  直接内存操作数

        3.4节已经解释过,变量名仅仅是对数据段内偏移地址的引用。下面的声明表示一个包含数字10h的字节被置于数据段内:

    .data

    var1 BYTE 10h

        指令使用内存操作数时实际上使用的书操作数的地址。假设var1位于偏移10400h处,那么把var1AL寄存器的汇编指令如下:

    mov AL,var1

    MASM将这条指令汇编成如下的机器指令:

    A0 00010400

        机器指令的第一个字节是操作码,剩下的部分是变量var1的十六进制的32位地址值。编写程序时仅使用纯数字地址表示内存操作是可以的,不过像var1这样的符号名使得引用内存时更加方便一些。

    一些开发者更喜欢使用下面的方式表示直接操作数,因为方括号按时了要进行寻址操作:

    mov al,[var1]

    MASM允许使用这种方式。mov al,[var1+5] (这称为直接偏移操作数。)

    4.1.4  MOV指令  

        MOV指令从源操作数向目的操作数复制数据。作为数据传送指令,几乎每个程序中都会用到MOV。其基本格式是:第一个操作数是目的(destination)操作数,第二个操作数是源(source)操作数:

    MOV destination,source

    指令运行后,目的操作数的内容被改变而源操作数的内容保持不变。

    MOV指令对操作数的使用是非常灵活的,只要遵循以下规则即可:

    1.两个操作数的尺寸必须一致。

    2.两个操作数不能同时为内存操作数。

    3.目的操作数不能是CS,EIPIP

    4.立即数不能直接送至段寄存器。

    下面是MOV指令格式列表,但寄存器(reg)是不包括段寄存器的:

        MOV reg  ,reg

        MOV mem ,reg

        MOV reg  ,mem

        MOV mem ,imm

        MOV reg  ,imm

        在运行于保护模式下时,程序不应直接修改段寄存器。一般来说,段寄存器仅应由实地址模式下运行的程序使用,对段寄存器的操作可以有一下两种格式,唯一的例外是CS不能用作目的操作数:

    MOV  r/m16 ,sreg

    MOV  sreg ,r/m16

        内存之间的移动:单条MOV指令不能把数据从一个内存位置直接移动到另外一个内存位置。

    作为一种替代方法,在送至目的操作数之前,可以先把源操作数移入一个寄存器中:

    .data

      var1 WORD ?

      var2 WORD ?

    .code

      mov ax ,var1

      mov var2,ax

     

    4.1.5  整数的零/符号扩展

    复制较小值至较大值中

    尽管不能直接使用MOV指令把数据从一个尺寸较小的操作复制到一个尺寸较大的操作数中,但有时确实需要这样的移动数据。例如,假设字count(无符号16位数)必须送到ECX32位)中,一个简单的解决方法是先把ECX清零,然后再把count送到CX中:

      .data

      count WORD 1

      .code

      mov ecx ,0

      mov cx,count

    但是注意一个问题,就是操作负数的时候会有问题。如:

      .data

      signedVal SWORD -1

      .code

      mov ecx ,0

      mov cx,signedVal

    这种情况,我们可以以FFFFFFFFh填充ECX,然后复制signedValCX中,那么最终结果将是正确的:

    .data

    signedVal SWORD -1

    signedVa2 SDWORD 0

    .code

    mov ecx ,0FFFFFFFFh

    mov cx,signedVal

    mov signedVa2,ecx

     

    这样虽然可以解决问题,但是比较麻烦,所以引入了MOVZXMOVSX指令,已处理有符号和无符号整数。

    MOVZX指令

    MOVZXmove with zero-extend,零扩展传送)指令将源操作数的内容复制到目的操作数中,并将该值扩展(zero-extend)至16位或32位。该指令仅适用于无符号整数,它有如下三种格式:

    Movzx r32 ,r/m8

    Movzx r32,r/m16

    Movzx r16,r/m8

    在此三种格式中第一个操作数是目的操作数而第二个操作数是源操作数,目的操作数必须是寄存器。下面的指令把二进制10001111AX:

    mov b1 ,10001111b

    movzx ax,bl

    下图解释了8位源操作数是如何扩展成16位目的操作数的:

     

    下面的例子中所有的操作数全部使用寄存器,演示了所有可能的尺寸格式组合:

     

    下面的例子使用内存操作数作为源操作数,产生的结果同上:

     

    MOVSX指令

    movzx指令类似,但是它是处理有符号整数的。

     

    4.1.6  LAHFSAHF指令

    LAHFload status flags into AH指令将EFLAGS寄存器的低字节复制到AH寄存器,被复制的标志包括:符号标志、零标志、辅助进位标志、奇偶标志和进位标志。使用该指令可以方便地将标志值保存在变量中:

    .data

    saveflags BYTE 0

    b1 BYTE 0

    .code

    LAHF

    mov saveflags ,ah

    SAHFstore AH into status flags指令复制AH寄存器的值至EFLAGS寄存器的低字节,例如,可以用如下指令回复刚才保存在变量中的标志:

    mov ah ,saveflags

    SAHF

    4.1.7  XCHG指令

    XCHGexchange data)指令交换两个操作数的内容,它有下面三种格式:

    XCHG reg,reg

    XCHG reg,mem

    XCHG mem,reg

    XCHG指令不接受立即数操作数,除此点不同之外,XCHG指令的操作数与MOV指令的操作数遵循同样的规则。在用到数组排序的应用程序中,XCHG指令提供了交换连个数组元素的简便方法,以下是一些使用XCHG指令的例子:

      xchg ax,bx  ;交换两个16位寄存器的内同

      xchg ah,al  ;交换两个8位寄存器的内容

      xchg var1,bx;交换16位的内存操作数和BX寄存器内容

      xchg eax,ebx;交换两个32位寄存器的内同

    若要交换两个内存操作数,需要使用一个寄存器作为临时存储容器,并把mov指令和xchg指令结合起来使用:

    mov  ax ,val1

    xchg ax ,val2

    mov  val1 ,ax  


    4.1.8  直接偏移操作数

    在变量名称后加上一个偏移值,可以创建直接偏移(direct-offset)操作数,可以通过它来访问没有显示标号的内存地址。我们以一个名为arrayB的字节数组开始枚举:

    .data

    arrayB BYTE 10h ,20h ,30h ,40h ,50h

    .code

    mov al ,arrayB

    mov al ,[arrayB+1]

    mov al ,[arrayB+2]

    如果是双字节或者是其他,注意偏移的时候地址是2或者其他等,比较简单不细说了。

        范围检查:MASM不对有效地址进行范围检查,对下面的语句,汇编器将原样翻译。如果执行下面的语句,就能够取得数组之外的一个内存字节。这可能会造成一个非常隐蔽的逻辑错误,因此开发者在检查对数组的引用时应该格外小心

    4.1.9  例子程序(数据传送)

    算是对4.1的一个总结

    TITLE Data Transfer Examples  (Moves.asm)

    INCLUDE Irvine32.inc

    .data

    val1 WORD 1000h

    val2 WORD 2000h

    arrayB BYTE 10h ,20h ,30h ,40h ,50h

    arrayW WORD 100h ,200h ,300h

    arrayD DWORD 10000h ,20000h

    .code

    main PROC

    ;MOVZX

        mov   bx ,0A65Bh       

        movzx eax,bx            ;EAX = 0000A64Bh

    movzx cx ,bl            ;CX = 009Bh

    ;MOVSX

        mov   bx ,0A69Bh

    movsx eax,bx            ;EAX = FFFFA69Bh

    movsx edx,bl            ;EDX = FFFFFF9Bh

    mov   bl,7Bh

    movsx cx,bl             ;CX = 007Bh

    ;内存到内存的交换

        mov   ax ,val1          ;AX = 1000h        

    xchg  ax ,val2          ;AX=2000h ,val2=1000h

    mov   val1 ,ax          ;val1 = 2000h

    ;直接偏移寻址(字节数组)

        mov al ,arrayB          ;AL = 10h

    mov al ,[arrayB+1]      ;AL = 20h

    mov al ,[arrayB+2]      ;AL = 30h

    ;直接偏移寻址(字数组)

        mov ax ,arrayW          ;AX = 100h

    mov ax ,[arrayW+2]      ;AX = 200h

        exit

    main ENDP

    END main

    4.2  加法和减法

    本章着重讲述整数的加法和减法;第7张讲述整数的乘法和出发;第17章介绍如何进行浮点运算,使用另外一套与整数运算完全不同指令。

    4.2.1  INCEDC指令

    INCincrement)和DECdecrement)指令从操作数中加1或减1,格式是:

    INC  reg/mem

    DEC  reg/mem

    下面是一些例子:

    .data

    myWord WORD 1000h

    .code

    Inc   myWord  ;4097 并不是1001h 书上写错了

    mov  bx ,myWord

    dec   bx       ;1000h  

    4.2.2  ADD指令

    ADD指令将同尺寸的源操作数和目的操作数相加,格式是:

    ADD 目的操作数,源操作数,例子:

    .data

    var1 DWORD 10000h

    var2 DWORD 20000h

    .code

    mov eax ,var1  ;EAX = 10000h

    add eax ,var2  ;EAX = 30000h

    4.2.3  SUB指令

    SUB指令将源操作数从目的操作数中减掉,操作数格式与ADDMOV指令操作数相同。

    SUB 目的操作数 ,源操作数

    例子:

    .data

    var1 DWORD 30000h

    var2 DWORD 10000h

    .code

    mov eax ,var1  ;EAX = 30000h

    sub eax ,var2  ;EAX = 20000h

    有一种执行减法而无需使用额外的数字电路单元的简单方法:对源操作数求补,然后把源才做书和目的操作数相加。例如4-1可以看做是 4 + -1

     

    影响的标志:SUB指令根据目标操作数的值相应的修改进位标志、零标志。符号标志、溢出标志、辅助进位标志和奇偶标志。

    4.2.4  NEG指令

    NEGnegate)指令通过将数字转换为对应的补码而求得其相反数:

    NEG reg

    NEG mem

    4.2.5  实现算术表达式

    Rval = -Xval + (Yval - Zval);

    汇编实现

    .data

    Rval SDWORD ?

    Xval SDWORD 26

    Yval SDWORD 30

    Zval SDWORD 40

    .code

     mov eax ,Xval

     neg eax

     mov ebx ,Yval

     sub ebx ,Zval

     add eax ,ebx

     mov Rval ,eax

    4.2.6  加法和减法影响的标志位

    4.2.7  例子程序(AddSub3

    TITLE Addition and Subtraction  (AddSub3.asm)

    INCLUDE Irvine32.inc

    .data

    Rval SDWORD ?

    Xval SDWORD 26

    Yval SDWORD 30

    Zval SDWORD 40

    .code

    main PROC

    ;INC and DEC

      mov  ax ,1000h

      inc  ax            ;1000h + 1

      dec  ax            ;1000h

    ;Rval = -Xval + (Yval - Zval)

      mov  eax ,Xval

      neg  eax

      mov  ebx ,Yval

      sub  ebx ,Zval

      add  eax ,ebx

      mov  Rval ,eax

    ;零标志的例子

      mov  cx ,1

      sub  cx ,1           ;ZF = 1

      mov  ax ,0FFFFh    

      inc  ax              ;ZF = 1

    ;符号标志的例子

      mov  cx ,0

      sub  cx ,1           ;SF = 1

      mov  ax ,7FFFh

      add  ax ,2           ;SF = 1

    ;进位标志的例子

      mov al ,0FFh

      add al ,1            ;OF = 1

      mov al ,-128

      sub al ,1            ;OF = 1

     

      exit

    main ENDP

    END main

     

     

     

  • 相关阅读:
    6 【程序6 最大公约数和最小公倍数】
    5 【程序5 判断分数等级】
    4 【程序4 分解质因数】
    3 【程序3 水仙花数】
    2【程序2 输出素数】
    1 【程序1 不死神兔】
    终极解决傻X阿里钱盾新手开店及老卖家复核身份证照片模糊无法对焦问题
    struct和typedef struct彻底明白了
    CentOS 6.5 编译安装 LNMP环境
    Another MySQL daemon already running with the same unix socket
  • 原文地址:https://www.cnblogs.com/csnd/p/12062302.html
Copyright © 2020-2023  润新知