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

 

 

 

原文地址:https://www.cnblogs.com/csnd/p/12062301.html