认真写好一篇文章

前言:8086汇编指令概述

计算机的指令通常包含操作码和操作数两部分。指令有单操作数、双操作数和无操作数三种,其中双操作数的两个操作数要用逗号隔开,逗号左边是目的操作数,逗号右边是源操作数。
例如: MOV AX,CX
该指令将CX寄存器中的数值放到AX寄存器中,其中MOV为操作码,AX为目的操作数,CX为源操作数。

@[toc]

1.立即寻址方式

在立即寻址方式下,操作数直接包含在指令中,即源操作数是立即数。
立即数:8位或16位的常数。
例1: MOV AL,26H
该指令将一个8位立即数26H送到AL寄存器中。
例2: MOV CX,2A50H
该指令将一个16位立即数2A50H送到CX寄存器中。其中2AH为高8位,放在高位地址;50H为低8位,放在低位地址。
如下图所示.(代码段从上到下地址越来越高)
在这里插入图片描述
注意:

  • 在所有的指令中,立即数只能作源操作数,不能作目的操作数。
  • 以A~F开头的数字出现在指令中时,前面要加一个数字0,以免与其它符号相混淆。例如,MOV AX,0FF00H

2.寄存器寻址方式

在寄存器寻址方式下,操作数包含在寄存器中,由指令指定寄存器的名称,即源操作数是寄存器。对于16位源操作数,寄存器可以是AX、BX、CX、DX、SI、DI、SP、和BP;对于8位操作数,寄存器可以是AH、AL、BH、BL、CH、CL、DH、DL。

  • 插一个知识点:8086CPU内部的寄存器们
    左边是我为了记住这几种寄存器编出来的方法,死(si)对应S、吧(ba)对应B。。。话糙理不糙。
    在这里插入图片描述

例1: MOV DX,AX
该指令将AX寄存器里存放的16位数据送到DX寄存器中。
例2: MOV DL,AH
该指令将AH寄存器里存放的8位数据送到DL寄存器中。
注意:

  • 源操作数长度必须与目的操作数一致,否则会出错。
    例如:MOV DX,AL 不正确,虽然16位的DX存得下8位的AL,但是汇编程序不知道将AL存在高8位还是低8位;
    再如:MOV AL,DX 这种显然不正确,因为8位的AL存不下16位的DX。

    3.存储器寻址方式

    我们知道,8086有20根地址总线,可寻址2的20次方也就是1M的存储器空间,但是8086的寄存器只有16位,只能表示出2的16次方等于64k的存储空间,于是工程师把1M的存储空间分成几段,每段最大为64k,采用段地址加偏移地址的方式进行寻址,比如在数据段寄存器(设DS=2000H)中偏移地址为200H处存放了一个8位数据23H,实际上在物理内存里的地址是2000H * 10H + 200H = 20200H,该处存放的数据为(20200H)=23H。
    在存储器寻址方式下,指令的源操作数存放在存储器中,根据不同的使用需要,可以选择以下5种寻址的方式:

    (1)直接寻址方式

    所谓直接寻址,跟立即数寻址有一点点相似,所不同的是立即数寻址是把一个常数赋给一个寄存器,直接寻址是把一个常数地址所表示的存储空间里存放的数赋给一个寄存器。这点其实可以类比C语言中常数和常数指针之间的关系,下面举例说明。
  • MOV AX,[2000H]
    该指令将DS:2000H、DS:2001H地址处存放的两个字节数据分别送给AX寄存器的低位AL和高位AH。
    上面已经说过,要想在存储器中找到一个位置,必须要有段地址和偏移地址,该指令没有指明段地址,只给出了偏移地址,这时计算机会默认为使用的段寄存器为数据段寄存器DS。对比指令MOV AX,2000H,这个指令则是将一个常数2000H赋给AX寄存器。
    从C语言的角度理解:前一条指令“相当”于 int a = *(指针常量) ,后一条指令“相当”于int a = 2000;
  • MOV AL,[2000H]
    该指令与上面指令的唯一区别是,该指令将DS:2000H地址处存放的一个字节数据送给AL。
  • MOV AX,ES:[500H]
    上面两个指令都是使用的默认的数据段寄存器DS,如果要想寻址其他段寄存器所指出的存储区进行寻址,需要用ES:[500H]这样的表示方法。
    其实上面的指令就相当于MOV AX,DS:[2000H].
  • MOV AX,SIGN
    这里的SIGN是一个符号地址,需要先把地址赋给这个符号SIGN,有两种方法进行赋值,第一种:SIGN EQU 2000H,这个方法可以近似理解成C语言里的宏定义:#define SIGN 2000;第二种:SIGN DW 2000H,这种方法可以近似理解成C语言里的变量定义:int SIGN = 2000;(其实他们之间有本质的区别,这样类比只是方便理解)。
    采用符号地址的好处:一是方便程序中多次出现同一地址时的修改,只需要改一处就可以改变所有用到的地方的值,不用到处改来改去;二是用符号表示方便阅读。

小结: 从上面几种指令方法可以看到一个共同点,就是所有的源操作数都是立即数或是变量,是直接存放在代码段里的,这也是这种方式跟以下几种方式的区别。

(2)寄存器间接寻址方式

寄存器间接寻址方式跟直接寻址方式唯一的区别就是,把直接寻址方式中用立即数表示的源操作数[2000H]换成了基址或变址寄存器BX、BP、SI或DI,寄存器里存的还是一个地址常数,可以说是新壶装旧酒,给地址常数穿了件衣服。

  • MOV AX,[SI]
    设SI=2000H。
    该指令将DS:2000H、DS:2001H地址处存放的两个字节数据分别送给AX寄存器的低位AL和高位AH。跟MOV AX,[2000H]的作用完全没差。
  • MOV AX,ES:[SI]
    设SI=2000H。
    不啰嗦了,这个跟MOV AX,ES:[2000H]的作用又是完全没差。

小结: 从上面两种指令方法也可以看到一个共同点,就是所有的源操作数都是存放在基址或变址寄存器里的。

(3)寄存器相对寻址方式

相对寻址的概念相对晦涩,咱直接上例子

  • MOV AX,COUNT[SI]
    这个方式跟前面的寄存器间接寻址方式唯一的一个区别就是,在偏移地址[SI]的基础上,再偏上一个COUNT。
    设SI=2000H,COUNT=400H,那么该指令就是将DS:[2000H+400H]地址处存放的数据送给AX。
    其实就相当于MOV AX,[SI+COUNT]。
  • MOV AX,ES:COUNT[SI]
    还是一样的配方,还是熟悉的味道,不多说了。

小结: 从上面两种指令方法又可以看到一个共同点,就是所有的源操作数都是基址或变址寄存器里的存放的数值的基础上加了一个COUNT,当然,COUNT也可以是其他表示,比如HAHAHA。

(4)基址变址寻址方式

这种方式,也没有什么奇淫巧技,无聊的很,不说概念了,看例子。

  • MOV AX, [BX][SI]
    这个就相当于把上一种方式里的COUNT换成了[BX]。
    设BX=400H,那么该指令就是将DS:[2000H+400H]地址处存放的数据送给AX。

    (5)相对基址变址寻址方式

    像贪吃蛇一样,再给源操作数加上一节。。。
  • MOV AX,COUNT[BX][SI]
    猜也能猜出来,这条指令的作用是:将DS:[COUNT+BX+SI]地址处存放的数据送给AX。
    设COUNT=40H,BX=2000H,SI=400H,那么该指令就是将DS:[2000H+400H+40H]地址处存放的数据送给AX。

大注意: 以上五种方式在使用中要注意
在这里插入图片描述在这里插入图片描述

4.其他寻址方式

(1)隐含寻址

指令中不指明操作数,但隐含规定的寻址方式。例如指令DAA,它的含义是对寄存器AL中的数据进行十进制数调整,结果仍留在AL中。

(2)I/O端口寻址

8086有直接端口和间接端口两种寻址方式。

  • 直接端口寻址,端口地址由指令提供,它是一个8位立即数,因此能寻址256个端口。
  • 例如:* IN AL,63H
    表示将端口63H中的内容送进AL寄存器。
  • 间接端口寻址,端口地址由DX寄存器提供,能寻址2的16次方等于64k个I/O端口。
  • 例如:*
    MOV DX,233H
    IN AL,DX
    表示将端口233H中的内容送进AL寄存器。