什么是Debug?
Debug是DOS、Windows都提供的实模式(8086方式)程序的调试工具。使用它,可以查看CPU各种寄存器中的内容、内存的情况和在机器码级跟踪程序的运行。
我们用到的Debng功能。
用Debug的R命令查看、改变CPU寄存器的内容;
用Debug的D命令查看内存中的内容:
用Debug的E命令改写内存中的内容;
用Debug的U命令将内存中的机器指令翻译成汇编指令;
用Debug的T命令执行一条机器指令:
用Debug的A命令以汇编指令的格式在内存中写入一条机器指令。
寄存器
内存中字的存储
CPU中,用16 位寄存器来存储一个字。高8位存放高位字节,低8位存放低位字节。在内存中存储时,由于内存单元是字节单元(一个单元存放一个字节),则一个字要用两个地址连续的内存单元来存放,这个字的低位字节存放在低地址单元中,高位字节存放在高地址单元中。比如我们从0地址开始存放20000,这种情况如图所示。
在图3.1中,我们用0、1两个内存单元存放数据20000(4E20H)。0、1两个内存单元用来存储一个字,这两个单元可以看作一个起始地址为0的字单元(存放一个字的内存单元,由0、1两个字节单元组成)。对于这个字单元来说,0号单元是低地址单元,1号单元是高地址单元,则字型数据4E20H的低位字节存放在0号单元中,高位字节存放在1号单元中。同理,将2、3号单元看作一个字单元,它的起始地址为2。在这个字单元中存放数据18(0012H), 则在2号单元中存放低位字节12H, 在3号单元中存放高位字节O0H。
我们提出字单元的概念:字单元,即存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。
我们将起始地址为N的字单元简称为N地址字单元。比如一个字单元由2、3两个内存单元组成,则这个字单元的起始地址为2,我们可以说这是2地址字单元。
0地址单元中存放的字节型数据: 20H;
0地址字单元中存放的字型数据:4E20H;
2地址单元中存放的字节型数据: 12H:
2地址字单元中存放的字型数据: 0012H;
1地址字单元,即起始地址为1的字单元,它由1号单元和2号单元组成,用这两个单元存储一个字型数据,高位放在2号单元中,即: 12H,低位放在1号单元中,即: 4EH,它们组成字型数据是124EH,大小为:4686。
任何两个地址连续的内存单元,N号单元和N+1号单元,可以将它们看成两个内存单元,也可看成一个地址为N的字单元中的高位字节单元和低位字节单元。
DS和[address]
CPU要读写一个内存单元的时候,必须先给出这个内存单元的地址,在8086PC中,内存地址由段地址和偏移地址组成。8086CPU 中有一个DS寄存器,通常用来存放要访问数据的段地址。比如我们要读取10000H单元的内容,可以用如下的程序段进行。
mov bx, 1000H
mov ds, bx
mov al, [0]
上面的3条指令将10000H(1000:0)中的数据读到al中。
详细说明指令的含义。
mov al, [0]
前面我们使用mov指令,可完成两种传送:①将数据直接送入寄存器:②将一个寄存器中的内容送入另一个寄存器。也可以使用mov指令将一个内存单元中的内容送入一个寄存器中。从哪一个内存单元送到哪一个寄存器中呢?在指令中必须指明。寄存器用寄存器名来指明,内存单元则需用内存单元的地址来指明。显然,此时mov指令的格式应该是: mov寄存器名,内存单元地址。
如何用mov指令从10000H中读取数据。1000H用段地址和偏移地址表示为1000:0,我们先将段地址1000放入ds,然后用mov al,[0]完成传送。mov指令中的[]说明操作对象是一个内存单元,[]中的0说明这个内存单元的偏移地址是0,它的段地址默认放在ds中,指令执行时,8086CPU 会自动从ds中取出。
mov bx, 1000H
mov ds, bx
若要用mov al,[0]完成数据从1000:0 单元到al的传送,这条指令执行时,ds中的内容应为段地址1000H,所以在这条指令之前应该将1000H送入ds。
如何把一个数据送入寄存器呢?我们以前用类似“mov ax,1" 这样的指令来完成,从理论上讲,我们可以用相似的方式: mov ds,1000H, 来将1000H 送入ds。 可是,现实并非如此,8086CPU 不支持将数据直接送入段寄存器的操作,ds 是一个段寄存器,所以movds,1000H这条指令是非法的。那么如何将1000H送入ds呢?只好用一个寄存器来进行中转,即先将1000H送入一个一般的寄存器,如bx,再将bx中的内容送入ds。
字的传送
前面我们用mov指令在寄存器和内存之间进行字节型数据的传送。因为8086CPU是16位结构,有16根数据线,所以,可以一次性传送16 位的数据,也就是说可以一次性传送一个字。只要在mov指令中给出16 位的寄存器就可以进行16位数据的传送了。
mov bx, 1000H
mov ds, bx
mov ax, [0] ;1000:0处的字型数据送入ax
mov [0] , CX ;cx中的16位数据送到1000:0处
mov、add、sub指令
mov、add、sub 指令,它们都带有两个操作对象。到现在,我们知道,mov指令可以有以下几种形式。
mov寄存器,数据 比如: mov ax,8
mov寄存器,寄存器 比如: mov ax,bx
mov寄存器,内存单元 比如: mov ax,[0]
mov内存单元,寄存器 比如: mov [O],ax
mov段寄存器,寄存器 比如: mov ds,ax
(1)既然有“mov段寄存器,寄存器”,从寄存器向段寄存器传送数据,那么也应该有“'mov寄存器,段寄存器”,从段寄存器向寄存器传送数据。一个合理的设想是:8086CPU内部有寄存器到段寄存器的通路,那么也应该有相反的通路。
图3.4中,用A命令在一个预设的地址0B39:0100处,用汇编的形式mov ax,ds 写入指令,再用T命令执行,可以看到执行的结果,段寄存器ds中的值送到了寄存器ax中。通过验证我们知道,“mov 寄存器,段寄存器”是正确的指令。
(2)既然有“mov内存单元,寄存器”,从寄存器向内存单元传送数据,那么也应该有“mov内存单元,段寄存器”,从段寄存器向内存单元传送数据。比如我们可以将段寄存器cs中的内容送入内存1000H处。
mov ax, 1000H
mov ds, ax
mov [0],cs
在Debug中进行试验,如图3.5所示。
图3.5中,当CS:IP指向0B39:0105的时候,Debug 显示当前的指令mov [0000],cs,因为这是一条访问内存的指令,Debug 还显示出指令要访问的内存单元中的内容。由于指令中的CS是一个16位寄存器,所以要访问(写入)的内存单元是一个字单元,它的偏移地址为0,段地址在ds中,Debug在屏幕右边显示出“DS:0000 =0000”,我们可以知道这个字单元中的内容为0。
mov [0000],cs 执行后,CS中的数据(0B39H)被写入1000:0 处,1000:1 单元存放0BH,1000:0 单元存放39H。
最后,用D命令从1000:0开始查看指令执行后内存中的情况,注意1000:0、1000:1两个单元的内容。
'mov段寄存器,内存单元”也应该可行,比如我们可以用10000H处存放的字型数据设置ds(即将0000H处存放的字型数据送入ds),指令如下。
mov ax, 1000H
mov ds,ax
mov ds, [0]
add和sub指令同mov一样,都有两个操作对象。它们也可以有以下几种形式。
add 寄存器,数据 比如: add ax,8
add寄存器,寄存器 比如: add ax,bx
add寄存器,内存单元 比如: add ax,[0]
add内存单元,寄存器 比如: add [0],ax
sub寄存器,数据 比如: sub ax,9
sub寄存器,寄存器 比如: sub ax,bx
sub 寄存器,内存单元 比如: sub ax,[0]
sub内存单元, 寄存器 比如: sub [0],ax
数据段
对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。我们可以将一组长度为 NN<=64KB)、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段。比如用123B0H~123B9H这段内存空间来存放数据,我们就可以认为,123B0H~123B9H这段内存是一个数据段,它的段地址为123BH,长度为10个字节。
如何访问数据段中的数据呢?将一段内存当作数据段,可以在具体操作的时候,用ds存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元。
小结
字在内存中存储时,要用两个地址连续的内存单元来存放,字的低位字节存放在低地址单元中,高位字节存放在高地址单元中。
用mov指令访问内存单元,可以在mov指令中只给出单元的偏移地址,此时,段地址默认在DS寄存器中。
[address]表示个偏移 地址为address 的内存单元。
在内存和寄存器之间传送字型数据时,高地址单元和高8位寄存器、低地址单元和低8位寄存器相对应。
mov、add、sub 是具有两个操作对象的指令。jmp是具有一个操作对象的指令。