例3 在DEBUG下运行下述程序,查看执行结果,并将其作为可执行文件存入A盘。
MOV AX,0FEH ;被乘数0FEH送AX
MOV CL,2
SHL AX,CL ;被乘数乘以4,结果送AX
MOV BX,AX ;被乘数乘以4的结果送BX保留
MOV CL,2
SHL AX,CL ;被乘数乘以16,结果送AX
ADD AX,BX ;被乘数乘以20,结果在AX中
MOV ,AX ;将积存入DS段第300H—301H号内存单元
MOV AH,4CH ;将功能号4CH送AH
INT 21H ;执行DOS的4CH号功能调用,结束程序返回DOS。
该程序运行结果是将0FEH乘以14H,结果放在DS段第300H—301H号内存单元中。
(1)进入DEBUG,显示300H至301H号内存单元内容
C:\DOS>DEBUG
-D 300 301
1392:0300 00 00 . .
-
(2)用A命令装入程序段并汇编
-A
1392:0100 MOV AX,0FE
1392:0102 MOV CL,2
1392:0104 SHL AX,CL
1392:0106 MOV BX,AX
1392:0108 MOV CL,2
1392:010A SHL AX,CL
1392:010C ADD AX,BX
1392:010E MOV ,AX
1392:0111 MOV AH,4C
1392:0113 INT 21
1392:0116
-
(3)用G命令执行到断点处(程序正常结束前)停止
-T=100,8
AX=13D8 BX=3F80 CX=0000 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=1392 ES=1392 SS=1392 CS=1392 IP=0111 NV UP DI PL NZ NA PO NC
1392:0111 B44C MOV AH,4C
-
(4)用D命令显示300H至301H的内容(最终结果)
-D 300 301
1392:0300 D8 13 ..
-
(5)用R命令指定写盘文件长度
-R BX
BX 3F80
:0
-R CX
CX 0000
:16
-
(6)用N命令命名写盘文件
-N A:YWZCHF.COM
(7)用W命令写盘
-W
-
(8)用Q命令退出DEBUG环境,返回DOS
-Q
C:\DOS>
(9)在DOS环境运行YWZCHF.COM
C:\DOS>A:YWZCHF
C:\DOS>
(10)将YWZCHF.COM装入内存运行
C:\DOS>DEBUG
-N A:YWZCHF.COM
-L
-T=100,8
AX=13D8 BX=3F80 CX=0000 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=1392 ES=1392 SS=1392 CS=1392 IP=0111 NV UP DI PL NZ NA PO NC
1392:0111 B44C MOV AH,4C
-D 300 301
1392:0300 D8 13 ..
(11)用Q命令退出DEBUG环境,返回DOS
-Q
C:\DOS>
例4 在DEBUG环境下,送入一个加法源程序并汇编成可执行代码;将其作为可执行文件JIAFA.COM存储到A盘;在DOS命令行执行可执行文件JIAFA.COM;进入DEBUG,将可执行文件JIAFA.COM装入内存CS:100H处运行,并用T命令查看运算结果。
C:\DOS>debug
-A
169C:0100 MOV AX,8A6D
169C:0103 ADD AX,0382
169C:0106 MOV ,AX
169C:0109 MOV AH,4C
169C:010B INT 21
169C:010D
-R BX
BX 0000
:
-R CX
CX 0000
-N A:JIAFA.COM
-W
-Q
C:\DOS>
C:\DOS>DEBUG
-N A:JIAFA.COM
-L
-G
Program terminated normally
-T=100,3
AX=8DEF BX=0000 CX=0000 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=1392 ES=1392 SS=1392 CS=1392 IP=0109 NV UP DI PL NZ NA PO NC
1392:0111 B4 4C MOV AH,4C
-D 200 201
169C:0200 EF 8D ..
-Q
C:\DOS>
第四节 使用DEBUG调试和运行可执行文件
事实上,在第三节例题中已经对使用DEBUG命令调试和运行可执行文件有所接触。本节只需对使用DEBUG调试和运行可执行文件的一般步骤做一介绍,并通过一个含有错误的程序来对程序调试进行实践。
用户程序经过编辑、汇编、连接后得到一个可执行文件(.EXE),这时借助于调试程序DEBUG对用户程序进行调试,查看程序是否能完成预定功能。对于初学者,如何选用DEBUG中各命令,有效地调试与运行程序,需要一个学习过程。在初次使用DEBUG时,可参照下列步骤进行。
1.调用DEBUG,装入用户程序
可以在调用DEBUG是直接装入用户程序可执行文件,也可以在进入DEBUG环境后使用N命令和L命令装入用户程序可执行文件。无论用哪种方法,装入用户程序可执行文件时,一定要指定文件全名(即文件名和扩展名)。
2.观察寄存器初始状态
程序装入内存后,用R命令查看寄存器内容。从各段寄存器现在的内容,便能了解用户程序各逻辑段(代码段,堆栈段等)在内存的分布及其段基值。R命令亦显示了各通用寄存器和标志寄存器的初始值,显示的第三行就是即将执行的第一条指令。
3.以单步工作方式开始运行程序
首先用T命令顺序执行用户程序的前几条指令,直到段寄存器DS和/或ES已预置为用户的数据段。在用T命令执行程序时,每执行一条指令,显示指令执行后寄存器的变化情况,以便用户查看指令执行结果。
4.观察用户程序数据段初始内容
在第3步执行后DS和/或ES已指向用户程序的数据段和附加段,这时用D命令可查看用户程序的原始数据。
5.继续以单步工作方式运行程序
对于初学者,一般编写的程序比较短,用T命令逐条执行指令,可清楚地了解程序的执行过程:现在执行的是什么指令,执行后的结果在哪里(寄存器,存储单元)?所得结果是否正确?…等等。在逐次使用T命令时,若有需要,可选用D命令了解某些内存单元的变化情况。
用T命令逐条执行程序时,如遇上用户程序中的软中断指令INT(如INT 21H),这时,通常不要用单步工作方式执行INT指令。因为系统提供的软中断指令INT是以中断处理子程序形式实现功能调用,且这种处理子程序常常是较长的。若用T命令去执行INT指令,那么将跳转到相应的功能调用于程序中,要退出该子程序需要化费较多时间。如果既要执行INT指令,又要跳过这段功能调用子程序,则应使用连续工作方式(G命令),且设置断点,其断点应为INT指令的下一条指令。例如要以单步工作方式执行下面一段程序:
10B0:0022 MOV DX,0010
10B0:0026 MOV AH,09
10B0:0028 INT 21
10B0:002A MOV CX,00
当用T命令完成“MOV AH,09”指令后,应使用G命令:
-G 002A
这样,以连续工作方式实现功能调用后,即暂停在偏移量为002A的“MOV CX,00”指令处(未执行),如同用单步工作方式完成INT指令的执行一样。
6.连续工作方式运行程序
在用单步工作方式运行程序后,可再用连续工作方式从头开始运行程序,查看运行结果。在用G命令时,注意指定运行程序的起始地址。若G命令中未指定起始地址,就隐含为从当前CS:IP指向的指令开始。
7.修改程序和数据
经过上面几步后,若发现程序有错,则需要适当进行修改。这时,如果仅需作个别修改,可在DEBUG状态下,使用A命令。这种修改仅仅是临时修改内存中的可执行文件,未涉及源程序。当确认修改正确后,应返回至编辑程序,修改源程序,然后再汇编、连接。
为了确认用户程序的正确性,常常需用几组不同的原始数据去运行程序,查看是否都能获得正确结果。这时,可用E命令在用户程序的数据段和附加段中修改原始数据,然后再用T命令或G命令运行程序,查看运行结果,直到各组数据都能获得正确结果为止。
8.运用断点调试程序
如果已确认程序是正确的,在连续工作方式下,可快速地运行程序;如果已知程序运行结果不正确,用G命令运行程序,中途不停,很难查找错误。改用T命令,虽然可以随意暂停程序的执行,但是运行速度慢,如果运用断点,可快速查找错误。这里的“断点”是程序连续运行时要求暂停的指令位置(地址),用要求暂停的一条指令首字节地址表示。当程序连续运行到这断点地址时,程序就暂停,并显示现在各寄存器内容和下面将要执行的指令(即断点处指令)。为了准确设置断点,可用反汇编命令U 察看源程序。运用断点,可以很快地查找出错误发生在哪一个程序段内,缩小查找错误的范围。然后在预计出错的范围内,再用T命令仔细观察程序运行情况,确定出错原因和位置,完成程序的调试。
例5 现有一个双字加法源程序如下,其中存在错误。现假设已汇编、连结生成了可执行文件SZJiaFa.EXE,存放在C:\DOS目录下。请使用DEBUG对其进行调试。
Code SEGMENT
ASSUME CS:code,DS:code
ORG 100H ;从100H处开始存放下列指令
Start:MOV AX,code ;将DS置成code段的首地址
MOV DS,AX
MOV SI,200H ;取第一个数的首地址
MOV AX, ;将第一个数的低16位取到AX
MOV DI,204H ;取第二个数的首地址
ADD AX, ;第一个数和第二个数的低16应相加
MOV ,AX ;低16位相加的结果送到208H和209H单元
MOV AX, ;取第一个数的高16位送到AX中
ADD AX, ;两个数的高16位相加
MOV ,AX ;高16位相加的结果送到20AH,20BH单元
MOV AX,4C00H ;使用DOS的4CH号功能调用
INT 21H ;进入功能调用,返回DOS
ORG 200H ;从200H处开始存放下列数据
DD 12345678h,654387A9h,0h ;被加数、加数、和
Code ENDS
END start
调试过程:
(1)进入DEBUG并装入可执行文件SZJiaFa.EXE
C:\DOS>DEBUG SZJiaFa.EXE
-
(2)观察寄存器初始状态
-R
AX=0000 BX=0000 CX=020C DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=1892 ES=1892 SS=18A2 CS=18A2 IP=0100 NV UP EI PL NZ NA PO NC
18A2:0100 B8A218 MOV AX,18A2
(3)以单步工作方式开始运行程序
首先用T命令顺序执行用户程序的前l两条指令,将段寄存器DS预置为用户的数据段。
-T
AX=18A2 BX=0000 CX=020C DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=1892 ES=1892 SS=18A2 CS=18A2 IP=0103 NV UP EI PL NZ NA PO NC
18A2:0103 8ED8 MOV DS,AX
-T
AX=18A2 BX=0000 CX=020C DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=18A2 ES=1892 SS=18A2 CS=18A2 IP=0105 NV UP EI PL NZ NA PO NC
18A2:0105 BE0002 MOV SI,0200
(4) 观察用户程序数据段初始内容
-D 200 20F
18A2:0200 78 56 34 12 A9 87 43 65-00 00 00 00 00 74 13 50 xV4...Ce.....t.P
-
(5) 连续工作方式运行程序至返回DOS前(设断点),查看运行结果。为此,现使用U命令反汇编。
-U 100
18A2:0100 B8A218 MOV AX,18A2
18A2:0103 8ED8 MOV DS,AX
18A2:0105 BE0002 MOV SI,0200
18A2:0108 8B04 MOV AX,
18A2:010A BF0402 MOV DI,0204
18A2:010D 0305 ADD AX,
18A2:010F 894408 MOV ,AX
18A2:0112 8B4402 MOV AX,
18A2:0115 034502 ADD AX,
18A2:0118 89440A MOV ,AX
18A2:011B B8004C MOV AX,4C00
18A2:011E CD21 INT 21
-
可见,要执行10条指令,至011B处停止
-G=100,011B
AX=7777 BX=0000 CX=020C DX=0000 SP=0000 BP=0000 SI=0200 DI=0204
DS=18A2 ES=1892 SS=18A2 CS=18A2 IP=011B NV UP EI PL NZ NA PE NC
18A2:011B B8004C MOV AX,4C00
-D 200 20F
18A2:0200 78 56 34 12 A9 87 43 65-21 DE 77 77 43 43 83 06 xV4...Ce!.wwCC..
-
和为7777DE21H,正确。
(6) 再取一组数据,查看运行结果。为此,首先用E命令修改数据。
-E 200 CD,AB,78,56,90,EF,34,12
-D 200 20F
18A2:0200 CD AB 78 56 90 EF 34 12-21 DE 77 77 43 43 83 06 ..xV..4.!.wwCC..
-G=100,11B
AX=68AC BX=0000 CX=020C DX=0000 SP=0000 BP=0000 SI=0200 DI=0204
DS=18A2 ES=1892 SS=18A2 CS=18A2 IP=011B NV UP EI PL NZ NA PE NC
18A2:011B B8004C MOV AX,4C00
-D 200 20F
18A2:0200 CD AB 78 56 90 EF 34 12-5D 9B AC 68 43 43 83 06 ..xV..4.]..hCC..
-
和为68AC9B5DH,错误。说明程序有问题。
(7) 再将断点设在完成低位字加法后,查看运行结果。
-G=100,112
AX=9B5D BX=0000 CX=020C DX=0000 SP=0000 BP=0000 SI=0200 DI=0204
DS=18A2 ES=1892 SS=18A2 CS=18A2 IP=0112 NV UP EI NG NZ NA PO CY
18A2:0112 8B4402 MOV AX, DS:0202=5678
-D 200 20F
18A2:0200 CD AB 78 56 90 EF 34 12-5D 9B AC 68 43 43 83 06 ..xV..4.]..hCC..
-
低位和为9B5D,正确。说明错误可能出在后面
(8) 使用T命令从刚才的断点处向后单步调试,查看运行结果。
-T=112
AX=5678 BX=0000 CX=020C DX=0000 SP=0000 BP=0000 SI=0200 DI=0204
DS=18A2 ES=1892 SS=18A2 CS=18A2 IP=0115 NV UP EI NG NZ NA PO CY
18A2:0115 034502 ADD AX, DS:0206=1234
-T
AX=68AC BX=0000 CX=020C DX=0000 SP=0000 BP=0000 SI=0200 DI=0204
DS=18A2 ES=1892 SS=18A2 CS=18A2 IP=0118 NV UP EI PL NZ NA PE NC
18A2:0118 89440A MOV ,AX DS:020A=68AC
-
AX寄存器的结果为68AC,而应为68AD。可见是本条加法指令使用错误,这里应使用带进位加法指令。
(9)使用A命令装入正确指令后再次运行,察看结果。
-A 115
18A2:0115 ADC AX,
18A2:0118
-G=100,11B
AX=68AD BX=0000 CX=020C DX=0000 SP=0000 BP=0000 SI=0200 DI=0204
DS=18A2 ES=1892 SS=18A2 CS=18A2 IP=011B NV UP EI PL NZ NA PO NC
18A2:011B B8004C MOV AX,4C00
-D 200 20F
18A2:0200 CD AB 78 56 90 EF 34 12-5D 9B AD 68 43 43 83 06 ..xV..4.]..hCC..
-
和为68AD9B5DH,正确。对于这样一个简单程序一般来说不会再有问题。退出后修改源程序即可。
(10)退出
-Q
C:\DOS>
需要说明的是此程序很简单,只需使用T命令逐条单步调试即可。本例采用的调试方法似乎过于繁琐,但这是为了说明程序调试的一般方法,以便读者调试复杂程序时借鉴。