

My intent is to focus on using the ML assembler, understanding DOS
programming, and perhaps being able to use the assembler to make routines that work with
other languages like Quick BASIC or C. Although the debuggers can be used to create
programs as we've already done, they aren't very easy to use for anything more than rather
trivial programming. The don't support symbolic labels, for example, and you need to
anticipate where things will be and count bytes, manually. The assembler is much better to
use for creating programs of any significance.
However, debuggers are quite useful for testing bits of code and
learning exactly what happens in the process. Sometimes, it's not clear what the
documentation from Intel is saying about an instruction, for example, and dropping into
the debugger to write that instruction and test it with some register values is rather
easy and it tells you right away what is going on. In that sense, they can supplement the
documentation. They can also help you find out where you went wrong in writing a routine,
too, or in narrowing down where a problem resides so that you can better focus your
attention.
Let's first walk though the creation of the second program more
thoroughly, now, using DEBUG.
Using DEBUG
First, we started up DEBUG by typing in the DOS command to invoke
it:
C>debug «
DOS first has to find the DEBUG.EXE program. It does this by looking
in the current directory and, if not found, then by looking in each directory mentioned in
your DOS PATH variable. Once found, DOS runs it like it does any other program. When it
does, DEBUG sets up a memory segment for you to use and then displays the '-' prompt,
waiting for your input to it.
At this point, we typed 'a' and hit ENTER. This tells the DEBUG
program that you want to try assembling some instructions into the memory. Other than the
first time we enter this command, DEBUG will start assembling at the place we left off
last time we tried assembling code. But since this is the first time we entered this
command, DEBUG starts at offset 0100h, which is where all .COM programs start executing.
-a «
102D:0100
After the 'a' command is entered, DEBUG displays that address and
waiting for input. The line shows two four-digit hexidecimal numbers, separated by a colon
':'. The first number is the segment and the second is the offset. Each time you start up
DEBUG, you may get a different segment value. But that's not usually important. It's the
offset that counts. All .COM programs will start at offset 0100h, because that's how DOS
arranges things in memory when it runs them.
We then entered an instruction to place a number into the DX
register.
102D:0100 mov dx, 10b
This number, it turns out, is the offset address for the message
that will be printed. If you go back to the previous page and look at the DOS
documentation for Function 09h, you'll see that this is a pointer to the literal string to
be printed. How do I know what the offset address will be, here? Well, I cheated by
looking ahead. Let's continue on and you'll see what I mean. Also note that DEBUG always
takes its numbers in hexadecimal format -- the "10B" shown above is the
hexadecimal address, not some decimal value.
The next instruction was then entered:
102D:0103 mov ah, 9
This instruction sets the DOS Function number, so that DOS will
provide the desired service (printing out a string of characters onto the screen.)
The next instruction (shown next, below) actually makes a function
call into DOS, asking it to perform the indicated function request:
102D:0105 int 21
The next two instructions shown below do something similar, except
this time it's Function 4Ch, which is asking DOS to terminate (kill, abort, murder,
whatever you want to call it) this program and to return control of the computer back to
DOS:
102D:0107 mov ah, 4c
102D:0109 int 21
Again, you see that register AH is being set to the desired DOS
function and then the actual instruction needed to call DOS is used. This is one of those
rare DOS functions where DOS doesn't all the program to continue running. So, at this
point, the program no longer continues.
Now, it's at this point that I decided to enter in the literal
string of characters to be displayed -- after the last instruction asking DOS to kill the
program. It's safe to add literals at this point, since it is certain that the program
cannot accidentally try to execute this data (which would NOT be good.)
102D:010B db "Hello out there!", 0d, 0a, '$'
102D:011E «
This is the literal string we want printed out by DOS onto the
screen. Those two hexadecimal values, 0Dh and 0Ah, are the special ASCII characters for
carriage return and line feed, respectively. The last character on this line tells DOS
where the literal string ends, so that DOS Function 09h will know when to stop printing.
If you read the doc I included on DOS Function 09h, you'll see them mention it.
Take note that the offset address displayed by DEBUG right at the
point where I start entering this string is 010Bh. Just as I'd placed into the DX register
earlier! I knew I should use that value in the first instruction because I'd already tried
to enter in the program (in an earlier attempt) and had discovered how much space this
program would need. 010Bh is the first address that follows the end of the program's code,
so I decided that this is a good place to add in the literal string.
Then ENTER is used on a blank line above (offset address 011Eh) to
tell DEBUG I'm done with assembling code. DEBUG will then re-display the '-' prompt and
let me enter in a new command.
Looking above at the line where I ended the assembly command by
hitting ENTER on a blank line, you can see that the offset address there is 011Eh. I also
know that the program starts at offset address 0100h. The difference between these is
001Eh, which is the actual size of my total program (code + data.) When I try and write
the program to a disk file (shortly), DEBUG looks at value of the CX register to decide
how many bytes to write -- actually, it looks at the combined BX and CX register pair,
with register BX taken as the high order 16 bits of a 32-bit value, but BX is zero (we'll
verify this, later.) So I need to update the value in the CX register to reflect the
program size. To change it, I use the 'r' command and specify the register, CX:
-r cx «
CX 0000
:
At this point, DEBUG has given me a colon ':' prompt and it is
waiting for the value to put into the CX register. I enter 1E:
:1e «
Now, I've told DEBUG how big my program is. It's time to tell DEBUG
the name I want to use, for the DOS file to save it:
-n lesson02.com «
That was easy, actually. Just use the 'n' command and specify the
filename. Now DEBUG knows the size and the name I want to use.
Now, just tell DEBUG to save the file. This is done with the
following 'w' (write) command:
-w «
Writing 0001E bytes
It confirms the size it wrote, so just check that when you do it to
make sure it matches the value placed into the CX register. You can see here that it does.
Now, I just quit from DEBUG and that takes me back to DOS:
-q «
C>
That's it, a fuller explanation of each step I used in the debugger
program, DEBUG. These explanations are also quite similar to those I used in GRDB, as
well. One difference in GRDB is that the write command it uses allows me to specify more
information directly, so I was able to bypass a few of the above details. Other than that,
it's quite similar.
Before I end here, let's quickly examine this second program we'd
created using DEBUG, by asking it to display the register values at the outset (I
mentioned earlier that I 'knew' that BX was zero, for example.) To do that, just start up
DEBUG while specifying the name of the program:
C>debug lesson02.com «
-r «
AX=0000 BX=0000 CX=001E DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=1091 ES=1091 SS=1091 CS=1091 IP=0100 NV UP EI PL NZ NA PO NC
1091:0100 BA0C01 MOV DX,010B
-q «
C>
By typing in the 'r' command without specifying a register, DEBUG
instead simply displays all of the register values. These values are typical for what they
look like when a .COM program starts executing. Note that CX here also represents the
number of bytes read by DEBUG when it loaded lesson02.com.
Last updated: Thursday, July 08, 2004 12:19
|