汇编语言程序设计
上QQ阅读APP看书,第一时间看更新

第一节 寻址方式

寻址方式.mp4

计算机的一条指令通常包含两部分:

其中,操作码规定了指令应完成的具体操作,在汇编语言中操作码用助记符表示,例如加法ADD、传送MOV,等等。操作数表示指令的操作对象,比如一条加法指令,操作数部分就要给出加数和被加数,它们可能存放在不同的地方,或许在某一寄存器中,或许在存储器的某一存储单元中,指令要通过某种方法表示操作数是哪一种,操作数具体在哪里。指令中提供操作数或操作数地址的方法叫寻址方式。8086/8088 CPU各种指令中所需的操作数主要有3类:寄存器操作数(操作数在CPU的通用寄存器中),存储器操作数(操作数在内存的存储单元中),立即数操作数(操作数是指令中给出的常数)。还有一类是输入/输出端口操作数(操作数在输入/输出的接口寄存器中),仅用在IN和OUT指令中。

下面按操作数的类型介绍8086/8088 CPU的寻址方式。

一、隐含寻址

有的指令中没有明确的操作数字段,操作的对象隐含在指令代码中,这种指令的寻址方式称为隐含寻址或叫固定寻址。例如:

     DAA

这是一条十进制加法调整指令,虽然无操作数字段,但隐含规定是对寄存器AL的内容进行操作。

二、立即寻址

如果操作数是一个常数,就无须寻找,这个常数就包含在指令代码中,立即可以得到这个操作数,因此把这种形式叫立即寻址。把一个常数操作数叫立即数,也是这个原因。

三、寄存器寻址

如果指令要操作的数据在CPU内部的寄存器中,指令就可以直接书写这个寄存器名字表示这个操作数,应用这种提供操作数的方法叫寄存器寻址。例如:

     MOV  AX,BX

这是一条传送指令,把寄存器BX中的内容传送至寄存器AX。如果BX=4258H,那么指令执行后AX=4258H,而BX的值保持不变。

又如:“ADD AX,1234H”是将AX的数与常数1234H相加,对于目标操作数(第一个操作数)是寄存器寻址,源操作数(第二个操作数)则为立即寻址。

四、存储器操作数的寻址方式

存储器操作数的寻址是一个如何在指令中给出该操作数在内存中存放的存储单元地址问题。在程序中,一个存储单元的地址是采用逻辑地址形式表示的,即:

     段基值:偏移量

其中,段基值在某个段寄存器中。不同操作类别的指令自动对应不同的段寄存器,这是隐含约定的。如不想改变这些约定,就无须在指令中给出段值。偏移量(又叫偏移地址或偏移值)表示了该存储单元与段起始地址之间的距离(字节数),它需要在指令中通过某种形式的表达式给出,在对源程序进行汇编时,由汇编程序计算出表达式的值,这个由汇编程序计算出的操作数的偏移量叫作有效地址,用EA表示。

指令中给出存储器操作数地址的方法有两种。一种方法是直接给出操作数的偏移地址,这种方法叫直接寻址。这是最直接、简单的方法,但不便于访问成组的数据。另一种方法是将操作数的偏移量放入某个寄存器中,将其作为地址指针,这种方法叫寄存器间接寻址。若地址指针的内容在程序运行期间进行修改,就能使得用该寻址方式的同一指令,可以对不同存储单元进行操作。下面具体讲述这些寻址方式。

(一)直接寻址

这种寻址方式是在指令中直接给出存储器操作数的偏移地址。有效地址EA可直接由偏移地址得到。这种寻址方式主要用于存取简单变量。

在直接寻址方式的指令中的偏移量可以用常数或变量名表示。

(1)用常数表示。例如:

     MOV  AX,DS:[100H]

该指令是把当前数据段偏移100H的字存储单元内容送至AX。用常数表示时,段寄存器必须指明,不能缺省。

(2)用变量名表示。例如:

     MOV  BX,VAR
     MOV  AH,DA+2

第一条指令是把由变量名VAR所指的存储单元内容传送给BX。第二条指令是把由变量名DA代表的地址偏移再加2的那个字节单元内容送给AH。假设VAR的偏移量为1000H,DA的偏移量为2000H,则上述两条指令等效为:

     MOV  BX,DS:[1000H]
     MOV  AH,DS:[2002H]

(二)寄存器间接寻址

寄存器间接寻址表示的偏移地址是由三个地址分量的某种组合形式给出,这三个地址分量如下。

(1)基址:由基址寄存器BX或基址指针BP提供的偏移地址。

(2)变址:由源变址寄存器SI或目的变址寄存器DI提供的偏移地址。

(3)位移量:是一个八位或十六位常数。有时在程序中以变量名或标号形式出现,待汇编后换算成它们的偏移值。

三个地址分量的不同组合形成以下几种寻址形式。

1. 基址寻址

基址寻址使用基址寄存器BX或BP做间址寄存器,有3种等价的格式,如表3-1所示。这种寻址方式是将基址寄存器BX或BP的内容与位移量(如果有的话)之和作为操作数的有效地址。

表3-1 基址寻址格式

例如:

     MOV  AH, [BX+VAR]

这条指令的功能是将存放在内存数据段的某个存储单元中的字节数据传送到AH中,这个存储单元在数据段中的偏移地址由BX的内容和符号VAR代表的偏移量相加确定。

2. 变址寻址

变址寻址使用变址寄存器SI或DI做间址寄存器,其格式与基址寻址相同。

例如:

     MOV  AX,ARRY[SI]
     MOV  [DI],BX
     MOV  DX,[SI+Z]
3. 基址变址寻址

基址变址寻址使用一个基址寄存器和一个变址寄存器组合来实现的。有两种形式,如表3-2所示。

表3-2 基址变址寻址的格式

注意以下两点。

(1)能够作为间址寄存器的只能是BX、BP、SI、DI,其他任何寄存器都不具备间址功能。

(2)用BX、SI、DI做间址寄存器寻找操作数时,隐含规定段基值由DS提供。当用BP做间址寄存器来寻找操作数时,隐含规定段基值由SS提供。这一点务必注意。

例如,假设BX和BP中的内容都是1000H,指令“MOV AX,[BX]”是将数据段中偏移地址为1000H的存储单元内容传送给AX,而指令“MOV AX,[BP]”是将堆栈中1000H存储单元的内容传送给AX。传送的数据可能在完全不同的两处。

五、段基值的隐含约定

由上面的叙述可知,用不同的寻址方式访问存储器,段基址的来源是不同的。一般地,随着操作类型的不同,偏移地址的来源也是不一样的。表3-3给出了不同操作类型获得段基址和偏移地址的不同来源。取指令、堆栈操作和串操作的目的这3种操作,它们的段地址分别来自CS、SS和ES,这种隐含约定是不允改变(替代)的,其他的操作则允许改变。由于有了段的隐含约定,就使得要对某个存储单元的数据进行存取时,在程序语句中只需给出偏移地址,机器会根据段的隐含约定,正确地找到这个操作数。

表3-3 逻辑地址的隐含约定

六、隐含段的改变

从表3-3中可以看到,有些操作可以改变段的隐含约定而用其他段替代,这对于要寻址的操作数未处于当前隐含段时的情况是非常有用的。有3种方法可以改变隐含段。

1. 段更换

程序将内存分段后的段基址放入段寄存器中。段寄存器的当前内容就是当前段,程序所涉及的内存单元的偏移量就是相对于当前隐含段的。可以通过更换段寄存器值的方法改变当前段,使之指向操作数所处的段。

例如,程序要处理的数据在当前代码段,那么,将当前代码段寄存器CS的内容放入数据段寄存器DS中,这样,当前数据段就指向了当前代码段,存取当前数据段中的操作数就是存取当前代码段中的内容。可使用下述指令实现:

     PUSH  DS
     PUSH  CS
     POP   DS
2. 用ASSUME伪指令重新指定当前段

当需要改变访问的段时,可以用ASSUME伪指令重新指定段与段寄存器的联系。这样,汇编程序在汇编时就能产生正确的寻址(详见第四章)。

3. 段超越

段超越不是通过改变当前段寄存器的内容而是在指令寻址方式前加入段超越前缀的方法操作非隐含段的数据。

例如: