温故而知新:
- [BX]的作用:作为偏移地址与DS配合
- loop和cx合作
- Debug的-g 偏移地址 命令和 -p 命令
5.4 Debug和汇编编译器masm对指令的不同处理
本节知识点为下面的课程的顺利进行提供一点预备知识。
我们在Debug中写过类似的指令:
mov ax,[0]
表示将ds:0处的数据送入ax中。
但是在汇编源程序黑哦能,指令mov ax,[0]
被编译器当做指令“mov ax,0”处理。
下面通过具体的例子看一下Debug和汇编编译器masm对形如“mov ax,[0]”之类指令的不同处理。
任务:将内存2000:0、2000:1、2000:2、2000:3单元中的数据送入al、bl、cl、dl中。
(1) 在Debug中编程实现:
mov ax,2000
mov ds,ax
mov al,[0]
mov bl,[1]
mov cl,[2]
mov dl,[3]
(2)在汇编源程序实现:
assume cs:code
code segment
mov ax,2000h
mov ds,ax
mov al,[0]
mov bl,[1]
mov cl,[2]
mov dl,[3]
mov ax,4c00h
int 21h
code ends
end
我们看一下两种实现的实际实施情况:
(1)Debug中的情况如图5.16所示。
(2)将汇编源程序储存为compare.asm,用masm、link生成compare.exe,用Debug加载compare.exe,如图5.17所示。
图5.16、图5.17中我们很明显地看出,Debug和编译器masm对形如“mov ax,[0]”这类指令在解释上的不同。我们在Debug中和源程序中写入同样形式的指令:“mov ax,[0]”、“mov bx,[1]”、“mov cx,[2]”、“mov dx,[3]”,但Debug和编辑器对这些指令中的“[idata]”确有不同的解释。Debug将它解释为“[idata]”是一个内存单元,“idata”是内存单元的偏移地址;而编译器将“[idata]”解释为“idata”。
那么我们如何在源程序中实现将内存2000:0、2000:1、2000:2、2000:3单元中的数据送入al、bl、cl、dl中呢?
目前的方法是,可将编译地址送入bx寄存器中,用[bx]的方式来访问内存单元。比如我们可以这样访问2000:0单元:
mov ax,2000h
mov ds,ax ;段地址2000h送入ds
mov bx,0 ;偏移地址0送入bx
mov al,[bx] ;ds:bx单元的数据送入al
这样做是可以,可是比较麻烦,我们要用bx来简介地给出内存单元的偏移地址。我们还是希望能够像在Debug中那样,在"[]"中直接给出内存单元的偏移地址。这样做,在汇编程序中也是可以的,只不过,要在“[]”的前面显式地给出段地址所在的段寄存器。比如我们可以这样访问2000:0单元:
mov ax,2000h
mov ds,ax
mov al,ds:[0]
比较一下汇编源程序中以下指令的含义。
mov al,[0]
,含义:(al)=0
,将常量0送入al中(与mov al,0
含义相同);
mov al,ds:[0]
,含义:(al)=((ds)*16+0)
,将内存单元中的数据送入al中;
mov al,[bx]
,含义:(al)=((ds)*16+(bx))
,将内存单元中的数据送入al中;
mov al,ds:[bx]
,含义:与mov al,[bx]
相同。
从上面的比较中可以看出:
(1)在汇编程序中,如果用指令访问一个内存单元,则在指令中必须用“[...]”来表示内存单元,如果在“[]”里用一个常量idata直接给出内存单元的偏移地址,就要在“[]”的前面显式地给出段地址所在的段寄存器。比如
mov al,ds:0
如果没有在“[]”的前面显式地给出段地址所在的段寄存器,比如
mov al,[0]
那么,编译器masm将把指令中的“[idata]”解释为“idata”。
(2)如果在“[]”里用寄存器,比如bx,间接地给出内存单元的偏移地址,则段地址默认在ds中。当然,也可以显式地给出段地址所在的段寄存器。