我们再编写汇编语言程序时,有时需要把内存中的数据取到寄存器中加以处理,在这种情况下我们应该把指令写成"MOV 寄存器,[偏移地址]"的形式。CPU执行这个指令时就会把内存中"DS:偏移地址"处所储存的数据取到指定寄存器中。
提醒大家注意一点:CPU的引用的段地址默认来自DS寄存器,当然也可以明确指定一个段寄存器作为段地址来源,用DEBUG编程时指令要写成两行:
****:**** ES:[Enter](指定ES为段寄存器,也可指定其它段寄存器) ****:**** MOV 寄存器,[偏移地址][Enter]
CPU执行此指令时就会把内存中ES:[偏移地址]处储存的数据取到指定寄存器中。
偏移地址两侧的"[ ]"是必不可少的。它表示我们要装入寄存器的数据是储存在这个地址处的数据,而不是这个地址本身。我们用DEBUG做个实验:
C:\ASM\>DEBUG[Enter] -d0 f 注意这两个字节 09FE:0000 CD 20 FF 9F 00 9A EE FE-1D F0 4F 03 68 04 8A 03 . ........O.h... -a100[Enter]
09FE:0100 mov ax,[0]
09FE:0103 mov ax,0
09FE:0106 [Enter]
-r 注意"DS:0000"这个存储单元 AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=09FE ES=09FE SS=09FE CS=09FE IP=0100 NV UP EI PL NZ NA PO NC 09FE:0100 A10000 MOV AX,[0000] DS:0000=20CD
通过前面讨论的内容,我们现在就可以推知这条指令执行完后AX寄存器的数据。
① 对于学习过C语言程序设计技术的朋友来说,"间接寻址"并非是什么新鲜东西,还记得"指针"的概念吗?
-t 注意AX寄存器的变化 AX=20CD BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=09FE ES=09FE SS=09FE CS=09FE IP=0103 NV UP EI PL NZ NA PO NC 09FE:0103 B80000 MOV AX,0000
执行指令之后AX寄存器是20CD,这个数来自于存储器。
-t 注意AX寄存器的变化 AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=09FE ES=09FE SS=09FE CS=09FE IP=0103 NV UP EI PL NZ NA PO NC 09FE:0106 BAA682 MOV DX,82A6
指令"MOV AX,0000"中的0000没有加"[]",结果这个0000被送入了AX寄存器。
这个实验很好地说明了"[ ]"的作用。回忆我们上一章所讲的"寻址方式"的概念,自然会明白这里又出现一种新的寻址方式,我们把这种寻址方式称为"直接寻址"。
这种寻址方式适合从内存中取得单一的数据。设想如果我们需要从内存中连续取得多个数据,那么用直接寻址方式就需要写出很多指令,这是很不方便的。于是便引出了"间接寻址"的概念。
间接寻址与直接寻址的区别就在于间接寻址方式中的偏移地址并不出现在"[ ]"中,而是放在一个寄存器中,而这个寄存器被"[ ]"括了起来。在程序PROG-3中,指令MOV BX,102把那串文字的"首地址0102H"放入BX寄存器,那么指令MOV DL,[BX]的含义也就很清楚了,当BX寄存器为102H时,此指令等同于MOV DL,[102],它执行的结果将使DL中出现"P"的ASCII码──50H,当BX寄存器变成103H时,此指令就相当于MOV DL,[103],它执行的结果将使DL中出现"r"。
和直接寻址相比,间接寻址的优势在于置入寄存器中的地址可以通过运算加以改变。我们可以把含有地址的寄存器想象成一个"指针",它指向内存中的某一个单元,若我们改变了这个寄存器中的数据,则这个指针就会指向其它单元,配以"[ ]",我们就可以很方便地取得内存中任意位置的数据。
除此之外,PROG-3中还有一些值得注意的东西:
① 用02功能输出0DH、0AH时,屏幕上并未出现对应的ASCII字符。原因在于0DH、0AH是控制码,02功能将它们解释成"回车"和"换行",输出这两个控制码将使光标移到下一行的最左端,所以第二个"a"打印在第二行。
② 此程序的缺点是仅能输出16个字母的文字串,不能输出任意长度的文字串。PROG3-A和PROG3-B对这个缺点作了改进。
-a100[Enter] 09FE:0100 jmp 111 ;跳过字符串数据区
09FE:0102 db'Hello,World!',0d,0a,0 ;定义一个字符串,结尾为0
09FE:0111 mov bx,0 ;初始化BX寄存器为0 09FE:0114 mov dl,[bx+102] ;DL寄存器置入字符H的ASCII码
09FE:0118 cmp dl,0 ;取到的字符是0码
09FE:011B jz 124 ;如果是0,说明已到字符串结尾,此时转0124H
09FE:011D mov ah,2 ;未到字符串结尾,则选择DOS API的02H功能
09FE:011F int 21 ;调用21H中断,将DL中的字符显示出来
09FE:0121 inc bx ;BX寄存器指向下一个字符
09FE:0122 jmp 114 ;返回0114H继续处理下一个字符
09FE:0124 int 20 ;结束处理,返回DOS 09FE:0126 [Enter]