Example 3 Run the following program under DEBUG, examine the execution result, and save it as an executable file to drive A.
MOV AX,0FEH ;put multiplicand 0FEH into AX
MOV CL,2
SHL AX,CL ;multiply the multiplicand by 4, result to AX
MOV BX,AX ;save the result of multiplicand times 4 in BX
MOV CL,2
SHL AX,CL ;multiply the multiplicand by 16, result to AX
ADD AX,BX ;multiplicand times 20, result in AX
MOV ,AX ;store the product in memory units 300H—301H of DS segment
MOV AH,4CH ;put function number 4CH into AH
INT 21H ;execute DOS function call 4CH, end the program and return to DOS.
The result of running this program is that 0FEH is multiplied by 14H, and the result is placed in memory units 300H—301H of the DS segment.
(1) Enter DEBUG and display the contents of memory units 300H to 301H
C:\DOS>DEBUG
-D 300 301
1392:0300 00 00 . .
-
(2) Use the A command to load the program segment and assemble it
-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) Use the G command to execute up to the breakpoint (before normal program termination) and stop
-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) Use the D command to display the contents of 300H to 301H (final result)
-D 300 301
1392:0300 D8 13 ..
-
(5) Use the R command to specify the file length for writing to disk
-R BX
BX 3F80
:0
-R CX
CX 0000
:16
-
(6) Use the N command to name the file to be written
-N A:YWZCHF.COM
(7) Use the W command to write to disk
-W
-
(8) Use the Q command to exit the DEBUG environment and return to DOS
-Q
C:\DOS>
(9) Run YWZCHF.COM in the DOS environment
C:\DOS>A:YWZCHF
C:\DOS>
(10) Load YWZCHF.COM into memory and run it
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) Use the Q command to exit the DEBUG environment and return to DOS
-Q
C:\DOS>
Example 4 In the DEBUG environment, enter an addition source program and assemble it into executable code; store it as the executable file JIAFA.COM on drive A; execute the executable file JIAFA.COM from the DOS command line; enter DEBUG, load the executable file JIAFA.COM into memory at CS:100H and run it, and use the T command to view the calculation result.
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>
Section 4 Using DEBUG to debug and run executable files
In fact, in the examples in Section 3 we have already had some contact with using DEBUG commands to debug and run executable files. This section only gives a general introduction to the usual steps of using DEBUG to debug and run executable files, and uses a program containing errors to practice program debugging.
After a user program has been edited, assembled, and linked, an executable file (.EXE) is obtained. At this point, with the help of the DEBUG debugger, the user program can be debugged to see whether it can complete its intended function. For beginners, how to choose among the commands in DEBUG and effectively debug and run a program requires a learning process. When using DEBUG for the first time, you may follow the steps below.
1.Call DEBUG and load the user program
You can either directly load the user program executable file when calling DEBUG, or enter the DEBUG environment first and then use the N and L commands to load the user program executable file. No matter which method is used, when loading the user program executable file, the full file name (that is, filename and extension) must be specified.
2.Observe the initial register state
After the program is loaded into memory, use the R command to view register contents. From the current contents of the segment registers, you can understand the distribution in memory and segment base values of the program's logical segments (code segment, stack segment, etc.). The R command also displays the initial values of the general-purpose registers and the flag register. The third displayed line is the first instruction about to be executed.
3.Start running the program in single-step mode
First use the T command to execute the first few instructions of the user program in sequence until the segment register DS and/or ES has been preset to the user data segment. When using the T command to execute the program, after each instruction, the changes in the registers are displayed so that the user can examine the execution result of the instruction.
4.Observe the initial contents of the user program data segment
After step 3, DS and/or ES already points to the user program's data segment and extra segment. At this point, the D command can be used to view the original data of the user program.
5.Continue running the program in single-step mode
For beginners, the programs they write are generally short. Using the T command to execute instructions one by one makes it possible to clearly understand the program's execution process: what instruction is being executed now, where is the result after execution (register, memory unit)? Is the result correct? ... and so on. When using the T command repeatedly, if necessary, the D command can be used to check changes in some memory units.
When using the T command to execute a program one instruction at a time, if an INT soft interrupt instruction is encountered in the user program (such as INT 21H), then usually the INT instruction should not be executed in single-step mode. This is because the INT soft interrupt instruction provided by the system implements function calls in the form of interrupt handling subroutines, and such handling subroutines are often quite long. If the T command is used to execute an INT instruction, execution will jump into the corresponding function call subroutine, and it will take quite some time to exit that subroutine. If you both want to execute the INT instruction and skip over that function call subroutine, you should use continuous mode (the G command) and set a breakpoint at the instruction following the INT instruction. For example, to execute the following piece of program in single-step mode:
10B0:0022 MOV DX,0010
10B0:0026 MOV AH,09
10B0:0028 INT 21
10B0:002A MOV CX,00
After using the T command to complete the “MOV AH,09” instruction, you should use the G command:
-G 002A
In this way, after carrying out the function call in continuous mode, execution pauses at the “MOV CX,00” instruction with offset 002A (not yet executed), just as if the INT instruction had been completed in single-step mode.
6.Run the program in continuous mode
After running the program in single-step mode, you can run it again from the beginning in continuous mode to inspect the result. When using the G command, note that you should specify the starting address of the program. If no starting address is specified in the G command, it is implicitly taken as the instruction pointed to by the current CS:IP.
7.Modify the program and data
After the above steps, if an error is found in the program, suitable modifications need to be made. At this point, if only individual changes are needed, the A command can be used under DEBUG. Such modification only temporarily changes the executable file in memory and does not involve the source program. After confirming that the modification is correct, you should return to the editor, modify the source program, and then assemble and link again.
In order to confirm the correctness of the user program, it is often necessary to use several different sets of original data to run the program and see whether all of them produce correct results. At this point, the E command can be used to modify the original data in the user program's data segment and extra segment, and then the T or G command can be used to run the program and inspect the results, until every set of data produces correct results.
8.Use breakpoints to debug the program
If the program has already been confirmed to be correct, then in continuous mode it can be run quickly; if it is already known that the program result is incorrect, then using the G command to run the program without stopping in the middle makes it difficult to find the error. If you switch to the T command, although you can pause program execution at will, the running speed is slow. If breakpoints are used, errors can be located quickly. The “breakpoint” here is the instruction position (address) where the program is required to pause during continuous execution, represented by the address of the first byte of the instruction where the pause is required. When the program runs continuously to that breakpoint address, the program pauses and displays the current contents of the registers and the next instruction to be executed (that is, the instruction at the breakpoint). To set breakpoints accurately, the disassemble command U can be used to inspect the source program. By using breakpoints, you can quickly determine in which section of the program the error occurs, narrowing the range to be checked. Then, within the suspected range, use the T command to observe the program's execution carefully, determine the cause and location of the error, and complete the debugging of the program.
Example 5 There is now a doubleword addition source program as follows, containing an error. Assume it has already been assembled and linked into the executable file SZJiaFa.EXE, stored in the C:\DOS directory. Please use DEBUG to debug it.
Code SEGMENT
ASSUME CS:code,DS:code
ORG 100H ;start storing the following instructions from 100H
Start:MOV AX,code ;set DS to the starting address of the code segment
MOV DS,AX
MOV SI,200H ;get the starting address of the first number
MOV AX, ;get the low 16 bits of the first number into AX
MOV DI,204H ;get the starting address of the second number
ADD AX, ;the low 16 bits of the first and second numbers should be added
MOV ,AX ;send the result of the low 16-bit addition to units 208H and 209H
MOV AX, ;get the high 16 bits of the first number into AX
ADD AX, ;add the high 16 bits of the two numbers
MOV ,AX ;send the result of the high 16-bit addition to units 20AH and 20BH
MOV AX,4C00H ;use DOS function call 4CH
INT 21H ;enter the function call and return to DOS
ORG 200H ;start storing the following data from 200H
DD 12345678h,654387A9h,0h ;augend, addend, sum
Code ENDS
END start
Debugging process:
(1) Enter DEBUG and load the executable file SZJiaFa.EXE
C:\DOS>DEBUG SZJiaFa.EXE
-
(2) Observe the initial register state
-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) Start running the program in single-step mode
First use the T command to sequentially execute the first two instructions of the user program, presetting segment register DS to the user's data segment.
-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) Observe the initial contents of the user program data segment
-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) Run the program in continuous mode up to before returning to DOS (set a breakpoint) and inspect the result. For this, first use the U command to disassemble.
-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
-
It can be seen that 10 instructions are to be executed, stopping at 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..
-
The sum is 7777DE21H, correct.
(6) Now use another set of data and inspect the result. For this, first use the E command to modify the data.
-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..
-
The sum is 68AC9B5DH, which is wrong. This shows the program has a problem.
(7) Set the breakpoint again after the low-word addition is completed, and inspect the result.
-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..
-
The low-word sum is 9B5D, correct. This indicates the error may be in the later part.
(8) Use the T command to single-step debug from the previous breakpoint onward and inspect the result.
-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
-
The result in AX is 68AC, but it should be 68AD. It can be seen that this addition instruction is wrong; a carry-add instruction should be used here.
(9) Use the A command to enter the correct instruction, then run it again and inspect the result.
-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..
-
The sum is 68AD9B5DH, correct. For such a simple program, generally there should be no more problems. After exiting, just modify the source program.
(10) Exit
-Q
C:\DOS>
What needs to be explained is that this program is very simple; ordinarily it would only require using the T command for instruction-by-instruction single-step debugging. The debugging method used in this example may seem overly cumbersome, but that is in order to explain the general method of program debugging, so that readers can refer to it when debugging more complex programs.