666 lines
34 KiB
Plaintext
666 lines
34 KiB
Plaintext
DEBUG
|
||
|
||
|
||
This tutorial is made to present an overview of the DEBUG.COM program for
|
||
the IBM PC. This utility can be extremely useful, when used correctly. It is
|
||
almost a must for Assembler Language programmers, and can also provide an
|
||
insight into the operation of the machine at the bit level. It has several
|
||
nice features, including the ability to display and change any of the registers
|
||
in the IBMPC, start and stop program execution at any time, change the program,
|
||
and look at diskettes, sector by sector. DEBUG works at the machine code
|
||
level, but it does also have the ability to disassemble machine code, and (at
|
||
dos 2.0), assemble instructions directly into machine code.
|
||
|
||
The procedure for starting DEBUG and command syntax will not be covered
|
||
here, as they are well documented in the DOS manual. What we will do is show
|
||
some examples of the various commands and the response which is expected. Note
|
||
that the segment registers will probably not be exactly what is shown. This is
|
||
normal, and should be expected.
|
||
|
||
For the examples, I will be using the demo program CLOCK.COM in the XA4
|
||
database. For those of you with the IBM assembler (MASM), the source can be
|
||
down loaded. If you do not have the assembler, or have another assembler, the
|
||
file CLOCK.HEX has been up loaded. It can be converted to a .COM file using
|
||
any of the existing HEX conversion programs on the SIG. See the file CLOCK.DOC
|
||
for more information.
|
||
|
||
|
||
|
||
STARTING DEBUG
|
||
|
||
There are two ways to start DEBUG with a file. Both ways produce the same
|
||
results, and either can be used.
|
||
|
||
In the Command Line: A>debug clock.com <ENTER>
|
||
|
||
Separate from the command line: A>debug <ENTER>
|
||
-n clock.com
|
||
-l
|
||
|
||
With either method, you will get the DEBUG prompt of a hyphen (-). DEBUG
|
||
has loaded your program and is ready to run. The description of each instruc-
|
||
tion will assume this as a starting point, unless otherwise mentioned. If at
|
||
any time you get different results, check your procedure carefully. If it is
|
||
correct, please leave me a message. I have tried to check everything, but I
|
||
have been known to make a mistake or two (anyway).
|
||
|
||
If you do have problems, you can enter the command Q (Quit) any time you
|
||
have the DEBUG prompt (-). This should return you to the DOS prompt.
|
||
|
||
|
||
RUNNING DEBUG
|
||
|
||
DISPLAY COMMANDS
|
||
|
||
|
||
Register command
|
||
|
||
The first thing we should look at are the registers, using the R command.
|
||
If you type in an R with no parameters, the registers should be displayed as
|
||
so:
|
||
|
||
AX=0000 BX=0000 CX=0446 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
|
||
DS=6897 ES=6897 SS=6897 CS=6897 IP=0100 NV UP DI PL NZ NA PE NC
|
||
6897:0100 E96B01 JMP 026E
|
||
|
||
CX contains the length of the file (0446h or 1094d). If the file were
|
||
larger than 64K, BX would contain the high order of the size. This is very
|
||
important to remember when using the Write command, as this is the size of the
|
||
file to be written. Remember, once the file is in memory, DEBUG has no idea
|
||
how large the file is, or if you may have added to it. The amount of data to
|
||
be written will be taken from the BX and CX registers.
|
||
|
||
If we want to change one of the registers, we enter R and the register
|
||
name. Let's place 1234 (hexadecimal) in the AX register:
|
||
|
||
-R AX R and AX register
|
||
AX 0000 Debug responds with register and contents
|
||
: 1234 : is the prompt for entering new contents. We respond 1234
|
||
- Debug is waiting for the next command.
|
||
|
||
Now if we display the registers, we see the following:
|
||
|
||
AX=1234 BX=0000 CX=0446 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
|
||
DS=6897 ES=6897 SS=6897 CS=6897 IP=0100 NV UP DI PL NZ NA PE NC
|
||
6897:0100 E96B01 JMP 026E
|
||
|
||
Note that nothing has changed, with the exception of the AX register. The new
|
||
value has been placed in it, as we requested. One note. The Register command
|
||
can only be used for 16 bit registers (AX, BX, etc.). It cannot change the 8
|
||
bit registers (AH, AL, BH, etc.). To change just AH, for instance, you must
|
||
enter the the data in the AX register, with your new AH and the old AL values.
|
||
|
||
|
||
Dump command
|
||
|
||
One of the other main features of DEBUG is the ability to display areas of
|
||
storage. Unless you are real good at reading 8088 machine language, the Dump
|
||
command is mostly used to display data (text, flags, etc.). To display code,
|
||
the Unassemble command below is a better choice. If we enter the Dump command
|
||
at this time, DEBUG will default to the start of the program. It uses the DS
|
||
register as it's default, and, since this is a .COM file, begins at DS:0100.
|
||
It will by default display 80h (128d) bytes of data, or the length you specify.
|
||
The next execution of the Dump command will display the following 80h bytes,
|
||
and so on. For example, the first execution of D will display DS:0100 for 80h
|
||
bytes, the next one DS:0180 for 80h bytes, etc. Of course, absolute segment
|
||
and segment register overrides can be used, but only hex numbers can be used
|
||
for the offset,e D will display DS:0100 for 80h
|
||
bytes, the next one DS:0180 for 80h bytes, etc. Of course, absolute segment
|
||
and segment register overrides can be used, but only hex numbers can be used
|
||
for the offset. That is, D DS:BX is invalid.
|
||
|
||
With our program loaded, if we enter the Dump command, we will see this:
|
||
|
||
6897:0100 E9 6B 01 43 4C 4F 43 4B-2E 41 53 4D 43 6F 70 79 ik.CLOCK.ASMCopy
|
||
6897:0110 72 69 67 68 74 20 28 43-29 20 31 39 38 33 4A 65 right (C) 1983Je
|
||
6897:0120 72 72 79 20 44 2E 20 53-74 75 63 6B 6C 65 50 75 rry D. StucklePu
|
||
6897:0130 62 6C 69 63 20 64 6F 6D-61 69 6E 20 73 6F 66 74 blic domain soft
|
||
6897:0140 77 61 72 65 00 00 00 00-00 00 00 00 00 00 00 00 ware............
|
||
6897:0150 00 00 00 00 00 00 00 00-00 24 00 00 00 00 00 00 .........$......
|
||
6897:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
|
||
6897:0170 00 00 00 00 00 00 00 00-00 00 00 00 44 4F 53 20 ............DOS
|
||
|
||
Notice that the output from the Dump command is divided into three parts.
|
||
On the left, we have the address of the first byte on the line. This is in the
|
||
format Segment:Offset.
|
||
|
||
Next comes the hex data at that location. Debug will always start the
|
||
second line at a 16 byte boundary; that is, if you entered D 109, you would get
|
||
7 bytes of information on the first line (109-10F), and the second line would
|
||
start at 110. The last line of data would have the remaining 9 bytes of data,
|
||
so 80h bytes are still displayed.
|
||
|
||
The third area is the ASCII representation of the data. Only the standard
|
||
ASCII character set is displayed. Special characters for the IBMPC are not
|
||
displayed; rather periods (.) are shown in their place. This makes searching
|
||
for plain text much easier to do.
|
||
|
||
Dump can be used to display up to 64K bytes of data, with one restriction:
|
||
It cannot cross a segment boundary. That is, D 0100 l f000 is valid (display
|
||
DS:0100 to DS:F0FF), but D 9000 l 8000 is not (8000h +9000h = 11000h and
|
||
crosses a segment boundary).
|
||
|
||
Since 64K is 10000h and cannot fit into four hex characters, Dump uses
|
||
0000 to indicate 64K. To display a complete segment, enter D 0 l 0. This will
|
||
display the total 64K segment.
|
||
|
||
If, at any time you want to suspend the display of data, Cntl-NumLock
|
||
works as usual. If you want to terminate the display, Cntl-Break will stop it
|
||
and return you to the DEBUG prompt.
|
||
|
||
|
||
|
||
Search
|
||
|
||
Search is used to find the occurrence of a specific byte or series of
|
||
bytes within a segment. The address parameters are the same as for the Dump
|
||
command, so we will not duplicate them here. However, we also need the data to
|
||
be searched for. This data can be entered as either hexadecimal or character
|
||
data. Hexadecimal data is entered as bytes, with a space or a comma as the
|
||
separator. Character data is enclosed by single or double quotes. Hex and
|
||
character data can be mixed in the same request, i.e. S 0 l 100 12 34 'abc' 56
|
||
is valid, and requests a search from DS:0000 through DS:00FF for the sequence
|
||
of 12h 34h a b c 56h, in that order. Upper case characters are different than
|
||
lower case characters, and a match will not be found if the case does not
|
||
match. For instance, 'ABC' is not the same as 'abc' or 'Abc' or any other
|
||
combination of upper and lower case characters. However, 'ABC' is identical to
|
||
"ABC", since the single and double quotes are separators only.
|
||
|
||
An example is looking for the string 'Sat'. Here's what would happen:
|
||
|
||
-S 0 l 0 'Sat'
|
||
6897:0235
|
||
-
|
||
Again, the actual segment would be different in your system, but the offset
|
||
should be the same. If we then displayed the data, we would find the string
|
||
'Saturday' at this location. We could also search on 'turda', or any other
|
||
combination of characters in the string. If we wanted to find every place we
|
||
did an Int 21h (machine code for Int is CD), we would do the following:
|
||
|
||
-S 0 l 0 cd 21
|
||
6897:0050
|
||
6897:0274
|
||
6897:027F
|
||
6897:028B
|
||
6897:02AD
|
||
6897:02B4
|
||
6897:0332
|
||
6897:0345
|
||
6897:034C
|
||
6897:043A
|
||
6897:0467
|
||
6897:047A
|
||
6897:0513
|
||
6897:0526
|
||
6897:0537
|
||
6897:0544
|
||
-
|
||
|
||
DEBUG found the hex data CD 21 at the above locations. This does not mean that
|
||
all these addresses are INT 21's, only that that data was there. It could (and
|
||
most likely is) an instruction, but it could also be an address, the last part
|
||
of a JMP instruction, etc. You will have to manually inspect the code at that
|
||
area to make sure it is an INT 21. (You don't expect the machine to do every-
|
||
thing, do you?).
|
||
|
||
|
||
Compare command
|
||
|
||
Along the same lines of Dump and Search commands, we have the Compare
|
||
command. Compare will take two blocks of memory and compare them, byte for
|
||
byte. If the two addresses do not contain the same information, both addresses
|
||
are displayed, with their respective data bytes. As an example, we will com-
|
||
pare DS:0100 with DS:0200 for a length of 8.
|
||
|
||
-C 0100 l 8 0200
|
||
6897:0100 E9 65 6897:0200
|
||
6897:0101 6B 70 6897:0201
|
||
6897:0102 01 74 6897:0202
|
||
6897:0103 43 65 6897:0203
|
||
6897:0104 4C 6D 6897:0204
|
||
6897:0105 4F 62 6897:0205
|
||
6897:0106 43 65 6897:0206
|
||
6897:0107 4B 72 6897:0207
|
||
|
||
None of the eight bytes compared, so we got output for each byte. If we
|
||
had gotten a match on any of the bytes, DEBUG would have skipped that byte. if
|
||
all of the locations requested matched, DEBUG would have simply responded with
|
||
another prompt. No other message is displayed. This is useful for comparing
|
||
two blocks of data from a file, or a program with the BIOS ROM. Otherwise, I
|
||
have not found a great amount of use for it.
|
||
|
||
|
||
|
||
Unassemble command
|
||
|
||
For debugging, one of the main commands you will use is the Unassemble
|
||
command. This command will take machine code and convert it to instructions.
|
||
Addressing is the same as for previous commands with one exception: Since we
|
||
are now working with code (the previous commands are mainly for data), the
|
||
default register is the CS register. In a .COM program, this makes very little
|
||
difference, unless you reset the DS register yourself. However, in a .EXE
|
||
file, it can make a lot of difference, as the CS and DS registers are set to
|
||
different values.
|
||
|
||
Unassemble data can lead to some interesting results. For instance, in
|
||
our example, CS:IP is set to 6897:0100. If we look at the program, we see a
|
||
JMP as the first instruction, followed by data. If we just enter U, we will
|
||
start at CS:IP (6897:0100) and start unassembling data. What we will get is a
|
||
good instruction, followed by more or less nonsense. For instance:
|
||
|
||
-U
|
||
6897:0100 E96B01 JMP 026E
|
||
6897:0103 43 INC BX
|
||
6897:0104 4C DEC SP
|
||
6897:0105 4F DEC DI
|
||
6897:0106 43 INC BX
|
||
6897:0107 4B DEC BX
|
||
|
||
And so on, through 6897:011D. We know the INC BX, DEC SP, etc. are not valid
|
||
instructions, but DEBUG doesn't, so we do have to look at the code. After
|
||
working with DEBUG a little, you will be able to spot code versus data with the
|
||
Unassemble command. For now, suffice to say that the first instruction will
|
||
take us to CS:026E and we can start from there.
|
||
|
||
If we Unassemble CS:026E, we will find something which looks a little more
|
||
like what we expect. We get:
|
||
|
||
-U 26E
|
||
6897:026E 8D167802 LEA DX,[0278]
|
||
6897:0272 B409 MOV AH,09
|
||
6897:0274 CD21 INT 21
|
||
6897:0276 EB05 JMP 027D
|
||
6897:0278 1B5B32 SBB BX,[BP+DI+32]
|
||
6897:027B 4A DEC DX
|
||
6897:027C 24B4 AND AL,B4
|
||
6897:027E 30CD XOR CH,CL
|
||
6897:0280 213C AND [SI],DI
|
||
6897:0282 027D0A ADD BH,[DI+0A]
|
||
6897:0285 8D167C01 LEA DX,[017C]
|
||
6897:0289 B409 MOV AH,09
|
||
6897:028B CD21 INT 21
|
||
6897:028D CD20 INT 20
|
||
|
||
|
||
The first few instructions look fine. But, after the JMP 027D, things
|
||
start to look a little funny. Also, note that there is no instruction starting
|
||
at 027D. We have instructions at 027C and 027E, but not 027D. This is again
|
||
because DEBUG doesn't know data from instructions. At 027C, we should (and do)
|
||
have the end of our data. But, this also translates into a valid AND instruc-
|
||
tion, so DEBUG will treat it as such. If we wanted the actual instruction at
|
||
027D, we could enter U 027D and get it, but from here, we don't know what it
|
||
is. what I'm trying to say is, DEBUG will do what ever you tell it. If you
|
||
tell it to Unassemble data, it will do so to the best of its ability. So, you
|
||
have to make sure you have instructions where you think you do.
|
||
|
||
|
||
DATA ENTRY COMMANDS
|
||
|
||
Enter
|
||
|
||
The Enter command is used to place bytes of data in memory. It has two
|
||
modes: Display/Modify and Replace. The difference is in where the data is
|
||
specified - in the Enter command itself, or after the prompt.
|
||
|
||
If you enter E address alone, you are in display/modify mode. DEBUG will
|
||
prompt you one byte at a time, displaying the current byte followed by a
|
||
period. At this time, you have the option of entering one or two hexadecimal
|
||
characters. If you hit the space bar, DEBUG will not modify the current byte,
|
||
but go on to the next byte of data. If you go too far, the hyphen (-) will
|
||
back up one byte each time it is pressed.
|
||
|
||
E 103
|
||
6897:0103 43.41 4C.42 4F.43 43. 4B.45
|
||
6897:0108 2E.46 41.40 53.-
|
||
6897:0109 40.47 53.
|
||
|
||
In this example, we entered E 103. DEBUG responded with the address and the
|
||
information at that byte (43). We entered the 41 and DEBUG automatically
|
||
showed the next byte of data (4C). Again, we entered 42, debug came back. The
|
||
next byte was 4F, we changed it to 43. At 106, 43 was fine with us, so we just
|
||
hit the space bar. DEBUG did not change the data, and went on to the following
|
||
bytes. After entering 40 at location 109, we found we had entered a bad value.
|
||
The hyphen key was pressed, and DEBUG backed up one byte, displaying the
|
||
address and current contents. Note that it has changed from the original value
|
||
(41) to the value we typed in (40). We then type in the correct value and
|
||
terminate by pressing the ENTER key.
|
||
|
||
As you can see, this can be very awkward, especially where large amounts
|
||
of data are concerned. Also, if you need ASCII data, you have to look up each
|
||
character and enter its hex value. Not easy, to be sure. That's where the
|
||
Replace mod of operation comes in handy. Where the Display/Modify mode is
|
||
handy for changing a few bytes at various offsets, the Replace mode is for
|
||
changing several bytes of information at one time. Data can be entered in
|
||
hexadecimal or character format, and multiple bytes can be entered at one time
|
||
without waiting for the prompt. If you wanted to store the characters 'My
|
||
name' followed by a hexadecimal 00 starting at location 103, you would enter:
|
||
|
||
E 103 'My name' 0
|
||
|
||
As in the Search command, data can be entered in character (in quotes) or
|
||
hexadecimal forms and can be mixed in the same command. This is the most
|
||
useful way of entering large amounts of data into memory.
|
||
|
||
|
||
Fill
|
||
|
||
The Fill command is useful for storing a lot of data of the same data. It
|
||
differs from the Enter command in that the list will be repeated until the
|
||
requested amount of memory is filled. If the list is longer than the amount of
|
||
memory to be filled, the extra items are ignored. Like the Enter command, it
|
||
will take hexadecimal or character data. Unlike the Enter command, though,
|
||
large amounts of data can be stored without specifying every character. As an
|
||
example, to clear 32K (8000h) of memory to 00h, you only need to enter:
|
||
|
||
F 0 L 8000 0
|
||
|
||
Which translates into Fill, starting at DS:0000 for a Length of 32K (8000) with
|
||
00h. If the data were entered as '1234', the memory would be filled with the
|
||
repeating string '123412341234', etc. Usually, it is better to enter small
|
||
amounts of data with the Enter command, because an error in the length parame-
|
||
ter of the Fill command can destroy a lot of work. The Enter command, however,
|
||
will only change the number of bytes actually entered, minimizing the effects
|
||
of a parameter error.
|
||
|
||
|
||
Move
|
||
|
||
The Move command does just what it says - it moves data around inside the
|
||
machine. It takes bytes from with the starting address and moves it to the
|
||
ending address. If you need to add an instruction into a program, it can be
|
||
used to make room for the instruction. Beware, though. Any data or labels
|
||
referenced after the move will not be in the same place. Move can be used to
|
||
save a part of the program in free memory while you play with the program, and
|
||
restore it at any time. It can also be used to copy ROM BIOS into memory,
|
||
where it can be written to a file or played with to your heart's content. You
|
||
can then change things around in BIOS without having to worry about programming
|
||
a ROM.
|
||
|
||
M 100 L 200 ES:100
|
||
|
||
This will move the data from DS:0100 to DS:02FF (Length 200) to the address
|
||
pointed to by ES:0100. Later, if we want to restore the data, we can say:
|
||
|
||
M ES:100 L 200 100
|
||
|
||
which will move the data back to its starting point. Unless the data has been
|
||
changed while at the temporary location (ES:0100), we will restore the data to
|
||
its original state.
|
||
|
||
Assemble
|
||
|
||
I purposely left the Assemble command to the end, as it is the most complex of
|
||
the data entry commands. It will take the instructions in the assembler lan-
|
||
guage and convert them to machine code directly. Some of the things it can't
|
||
do, however, are: reference labels, set equates, use macros, or anything else
|
||
which cannot be translated to a value. Data locations have to be referenced by
|
||
the physical memory address, segment registers, if different from the defaults,
|
||
must be specified, and RET instructions must specify the type (NEAR or FAR) of
|
||
return to be used. Also, if an instruction references data but not registers
|
||
(i.e. Mov [278],5), the Byte ptr or Word ptr overrides must be specified. One
|
||
other restriction: To tell DEBUG the difference between moving 1234h into AX
|
||
and moving the data from location 1234 into AX, the latter is coded as Mov
|
||
AX,[1234], where the brackets indicate the reference is an addressed location.
|
||
The differences between MASM and DEBUG are as follows:
|
||
|
||
MASM DEBUG Comments
|
||
|
||
Mov AX,1234 Mov AX,1234 Place 1234 into AX
|
||
Mov AX,L1234 Mov AX,[1234] Contents of add. 1234 to AX
|
||
Mov AX,CS:1234 CS:Mov AX,[1234] Move from offset of CS.
|
||
Movs Byte ptr ... Movesb Move byte string
|
||
Movs Word ptr ... Movsw Move word string
|
||
Ret Ret Near return
|
||
Ret Retf Far return
|
||
|
||
Also, Jmp instructions will be assembled automatically to Short, Near, or Far
|
||
Jmps. However, the Near and Far operands can be used to override the displace-
|
||
ment if you do need them. Let's try a very simple routine to clear the screen.
|
||
|
||
-A 100
|
||
6897:0100 mov ax,600
|
||
6897:0103 mov cx,0
|
||
6897:0106 mov dx,184f
|
||
6897:0109 mov bh,07
|
||
6897:010B int 10
|
||
6897:010D int 20
|
||
6897:010F
|
||
-
|
||
|
||
We are using BIOS interrupt 10h, which is the video interrupt. (If you
|
||
would like more information on the interrupt, there is a very good description
|
||
in the Technical Reference Manual.) We need to call BIOS with AX=600, BH=7,
|
||
CX=0, and DX=184Fh. First we had to load the registers, which we did at in the
|
||
first four instructions. The statement at offset 6897:010B actually called
|
||
BIOS. The INT 20 at offset 010D is for safety only. We really don't need it,
|
||
but with it in, the program will stop automatically. Without the INT 20, and
|
||
if we did not stop, DEBUG would try and execute whatever occurs at 010F. If
|
||
this happens to be a valid program (unlikely), we would just execute the
|
||
program. Usually, though, we will find it to be invalid, and will probably
|
||
hang the system, requiring a cntl-alt-del (maybe) or a power-off and on again
|
||
(usually). So, be careful and double check your work!
|
||
|
||
Now, we need to execute the program. To do this, enter the G command, a G
|
||
followed by the enter key. If you have entered the program correctly, the
|
||
screen will clear and you will get a message "Program terminated normally".
|
||
(More on the Go command later).
|
||
|
||
Again, I cannot stress the importance of checking your work when using the
|
||
Assemble command. The commands may assemble correctly, but cause a lot of
|
||
problems. This is especially important for the Jmp and Call commands; since
|
||
they cause an interruption in the flow of the program, they can cause the
|
||
program to jump into the middle of an instruction, causing VERY unpredictable
|
||
results.
|
||
|
||
|
||
I/O commands
|
||
|
||
Name
|
||
|
||
The Name command has just one purpose - specifying the name of a file which
|
||
DEBUG is going to Load or Write. It does nothing to change memory or execute a
|
||
program, but does prepare a file control block for DEBUG to work with. If you
|
||
are going to load a program, you can specify any parameters on the same line,
|
||
just like in DOS. One difference is, the extension MUST be specified. The
|
||
default is no extension. DEBUG will load or write any file, but the full file
|
||
name must be entered.
|
||
|
||
-n chkdsk.com /f
|
||
|
||
This statement prepares DEBUG for loading the program CHKDSK.COM passing the /f
|
||
switch to the program. When the Load (see below) command is executed, DEBUG
|
||
will load CHKDSK.COM and set up the parameter list (/f) in the program's input
|
||
area.
|
||
|
||
|
||
Load
|
||
|
||
The Load command has two formats. The first one will load a program which
|
||
has been specified by the Name command into storage, set the various registers,
|
||
and prepare for execution. Any program parameters in the Name command will be
|
||
set into the Program Segment Prefix, and the program will be ready to run. If
|
||
the file is a .HEX file, it is assumed to have valid hexadecimal characters
|
||
representing memory values, two hexadecimal characters per byte. Files are
|
||
loaded starting at CS:0100 or at the address specified in the command. For
|
||
.COM. .HEX and .EXE files, the program will be loaded, the registers set, and
|
||
CS:IP set to the first instruction in the program. For other files, the
|
||
registers are undetermined, but basically, the segment registers are set to the
|
||
segment of the PSP (100h bytes before the code is actually loaded), and BX and
|
||
CX are set to the file length. Other registers are undetermined
|
||
|
||
-n clock.com
|
||
-l
|
||
|
||
|
||
This sequence will load clock.com into memory, set IP to the entry point of
|
||
0100, and CX will contain 0446, the hexadecimal size of the file. The program
|
||
is now ready to run.
|
||
|
||
The second form of the Load command does not use the Name command. It is
|
||
used to load absolute sectors from the disk (hard or soft) into memory. The
|
||
sector count starts with the first sector of track 0 and continuing to the end
|
||
of the track. The next sector is track 0, second side (if double sided), and
|
||
continues to the end of that sector. Then, back to the first side, track 1,
|
||
and so on, until the end of the disk. Up to 80h (128d) sectors can be loaded
|
||
at one time. To use, you must specify starting address, drive (0=A, 1=B,
|
||
etc.), starting sector, and number of sectors to load.
|
||
|
||
-l 100 0 10 20
|
||
|
||
This instruction tells DEBUG to load, starting at DS:0100, from drive A, sector
|
||
10h for 20h sectors. drive (0=A, 1=B,
|
||
etc.), starting sector, and number of sectors to load.
|
||
|
||
|
||
This instruction tells DEBUG to load, starting at DS:0100, from drive A, sector
|
||
10h for 20h sectors. DEBUG can sometimes be used this way to recover part of
|
||
the information on a damaged sector. If you get an error, check the memory
|
||
location for that data. Often times, part of the data has been transferred
|
||
before the error occurs and the remainder (especially for text files) can be
|
||
manually entered. Also, repetitive retrys will sometimes get the information
|
||
into memory. This can then be rewritten on the same diskette (see the Write
|
||
command below), or copied to the same sector on another diskette. In this way,
|
||
the data on a damaged disk can sometimes be recovered.
|
||
|
||
|
||
Write
|
||
|
||
The write command is very similar to the Load command. Both have two
|
||
modes of operation, and both will operate on files or absolute sectors. As you
|
||
have probably guessed, the Write command is the opposite of the Load command.
|
||
Since all the parameters are the same, we will not cover the syntax in detail.
|
||
However, one thing worth mentioning: When using the file mode of the Write
|
||
command, the amount of data to be written is specified in BX and CX, with BX
|
||
containing the high-order file size. The start address can be specified or is
|
||
defaulted to CS:0100. Also, files with an extension of .EXE or .HEX cannot be
|
||
written out, and error message to that effect will be displayed. If you do
|
||
need to change a .EXE or .HEX file, simply rename and load it, make your
|
||
changes, save it and name it back to its original filename.
|
||
|
||
-
|
||
|
||
This is the Line input port for the first Asynchronous adapter. Your data may
|
||
be different, as it depends on the current status of the port. It indicates
|
||
the data in the register at the time it was read was 7Dh. Depending on the
|
||
port, this data may change, as the ports are not controlled by the PC.
|
||
|
||
|
||
Output
|
||
|
||
As you can probably guess, the Output command is the reverse of the Input
|
||
command. You can use the Output command to send a single byte of data to a
|
||
port. Note that certain ports can cause the system to hang (especially those
|
||
dealing with system interrupts and the keyboard), so be careful with what you
|
||
send where!
|
||
|
||
-o 3fc 1
|
||
-
|
||
|
||
Port 3FCh is the modem control register for the first asynchronous port. Send-
|
||
ing a 01h to this port turns on the DTR (Data Terminal Ready) bit. A 00h will
|
||
turn all the bits off. If you have a modem which indicates this bit, you can
|
||
watch the light flash as you turn the bit on and off.
|
||
|
||
|
||
EXECUTION COMMANDS
|
||
|
||
Go
|
||
|
||
The Go command is used to start program execution. A very versatile
|
||
command, it can be used to start the execution at any point in the program, and
|
||
optionally stop at any of ten points (breakpoints) in the program. If no
|
||
breakpoints are set (or the breakpoints are not executed), program execution
|
||
continues until termination, in which case the message "Program terminated
|
||
normally" is sent. If a breakpoint is executed, program execution stops, the
|
||
current registers are displayed, and the DEBUG prompt is displayed. Any of the
|
||
DEBUG commands can be executed, including the Go command to continue execution.
|
||
Note that the Go command CANNOT be terminated by Cntl-break. This is one of
|
||
the few commands which cannot be interrupted while executing.
|
||
|
||
-g =100
|
||
|
||
The Go command without breakpoints starts program execution at the address (in
|
||
this case CS:0100) in the command. The equal sign before the address is
|
||
required. (Without the equal sign, the address is taken as a breakpoint.) If
|
||
no starting address is specified, program execution starts at CS:IP. In this
|
||
case, since no breakpoints are specified, CLOCK.COM will continue execution
|
||
until the cntl-break key is pressed and the program terminates. At this time,
|
||
you will get the message "Program terminated normally". Note that, after the
|
||
termination message, the program should be reloaded before being executed.
|
||
Also, any memory alterations (storing data, etc.) will not be restored unless
|
||
the program is reloaded.
|
||
|
||
-g 276 47c 528 347
|
||
|
||
This version of the control command will start the program and set breakpoints
|
||
at CS:276, CS:47C, CS:528 and CS:347. These correspond to locations in
|
||
CLOCK.COM after the screen is cleared, and the day, date and time are dis-
|
||
played, respectively. The program will stop at whichever breakpoint it hits
|
||
first. Note that the second and third breakpoints will only be displayed at
|
||
two times - when the program is started and at midnight. If you care to stay
|
||
up (or just change the time in the computer), and set a breakpoint at 47C, t
|
||
will stop when the program is started, and again at midnight.
|
||
|
||
Some notes about breakpoints. The execution stops just before the instruc-
|
||
tion is executed. Setting a breakpoint at the current instruction address will
|
||
not execute any instructions. DEBUG will set the breakpoint first, then try to
|
||
execute the instruction, causing another breakpoint. Also, the breakpoints use
|
||
Interrupt 3 to stop execution. DEBUG intercepts interrupt 3 to stop the pro-
|
||
gram execution and display the registers. Finally, breakpoints are not saved
|
||
between Go commands. Any breakpoints you want will be have to be set with each
|
||
Go command.
|
||
|
||
|
||
Trace
|
||
|
||
Along the same lines as Go is the Trace command. The difference is that,
|
||
while Go executes a whole block of code at one time, the Trace command executes
|
||
instructions one at a time, displaying the registers after each instruction.
|
||
Like the Go instruction, execution can be started at any address. The start
|
||
address again must be preceeded by an equal sign. However, the Trace command
|
||
also has a parameter to indicate how many instructions are to be executed.
|
||
|
||
-t =100 5
|
||
|
||
This Trace command will start at CS:100 and execute five instructions. Without
|
||
the address, execution will start at the current CS:IP value and continue for
|
||
five instructions. T alone will execute one instruction.
|
||
|
||
When using Trace to follow a program, it is best to go around calls to DOS
|
||
and interrupts, as some of the routines involved can be lengthy. Also, DOS
|
||
cannot be Traced, and doing so has a tendency to hang the system. Therefore,
|
||
Trace to the call or interrupt and Go to the next address after the call or
|
||
interrupt.
|
||
|
||
|
||
ARITHMETIC COMMANDS
|
||
|
||
Hexarithmetic
|
||
|
||
The Hexarithmetic command is handy for adding and subtracting hexadecimal
|
||
numbers. It has just two parameters - the two numbers to be added and subtrac-
|
||
ted. DEBUG's response is the sum and difference of the numbers. The numbers
|
||
can be one to four hexadecimal digits long. The addition and subtraction are
|
||
unsigned, and no carry or borrow is shown beyond the fourth (high order) digit.
|
||
|
||
-h 5 6
|
||
000B FFFF
|
||
-h 5678 1234
|
||
68AC 4444
|
||
-
|
||
|
||
In the first example, we are adding 0005 and 0006. The sum is 000B, the
|
||
difference is -1. However, since there is no carry, we get FFFF. In the
|
||
second example, the sum of 5678 and 1234 is 68AC, and the difference is 4444.
|
||
|
||
|
||
WRAPUP
|
||
|
||
If you give it a chance, DEBUG can be a very useful tool for the IBMPC.
|
||
It is almost a requirement for debugging assembler language programs, as no
|
||
nice error messages are produced at run time. DEBUG does work at the base
|
||
machine level, so you need some experience to use it effectively, but with
|
||
practice, it will be your most useful assembler language debugging tool.
|
||
|
||
|
||
|