Bill Catchings and Mark L. Van Name are free-lance writers and independent computer consultants based in Raleigh, N.C.
The Turbo Debugger (TD) installation program is easy to use, asks few questions, and is almost foolproof. The first thing you notice about TD is its attractiveness, especially on a VGA monitor. The 50-line VGA display contains a lot of information, while still remaining readable.
A major part of the screen displays the source code of your assembler, C, or Pascal main routine. You can debug the source code, but to compile or edit it you must leave TD. You can call the editor of your choice from within TD as long as the editor of your choice fits into the memory limitations of running TD. We called Epsilon while debugging a 3OK-executable file with 6K of source. Larger editors might not fit in the remaining memory space.
When you return from editing a source file, TD marks the file as modified. To cause the object code to reflect your changes, you must exit TD and recompile the source. You can't push to DOS and recompile, because TD doesn't leave enough memory to hold any of Borland's compilers.
TD's interface is similar to that of the current Turbo languages. Its basic display has a menu bar across the top of the screen and a line of command key labels across the bottom.
You can get to the menu bar by pressing F10. You can also go directly to any menu by pressing the Alt key in combination with the first letter of the chosen menu. Once you've entered a menu, you can choose an option by pointing to it or by entering its first letter.
The line of command key labels initially displays TD's global command key assignments. Some of these key combinations are shortcut equivalents to menu choices. If you hold down the Alt key for a couple of seconds the label line will switch to a second line of global command keys that can be entered by pressing Alt plus a function key. These two sets of command keys are available in any TD window.
TD also has local key assignments that vary between windows. To see the local keys that are currently available, hold down Ctrl for a couple of seconds and the bottom label line will change to display them. You can trigger these local commands by pressing Ctrl plus a single mnemonic character; Ctrl-w, for example, is the watch command.
Many of these commands are context-sensitive and follow an object/verb metaphor. Move the cursor to an object on the screen and then enter a command. TD will perform that command on the selected object. This "point-and-pick" approach cries out for a mouse, but TD, somewhat surprisingly, will not work with a mouse.
Some of TD's command-key sequences will be familiar if you've used either Turbo C or Turbo Pascal. Alt-F5, for example, will switch from TD's display to your program's output screen; Alt-X will exit TD.
Between the menu bar and the key labels, TD displays 13 different types of windows, although it initially displays only two -- the Module and Watch windows.
Unlike CodeView's limited, tiled windows, TD's windows are the "real thing." They can be overlapped, moved, and resized, although each window has a fixed minimum size. The active window has a double-line border and a highlighted title with every window displaying a number in its upper border.
TD pushes the constraints of the window concept even further by dividing some windows into smaller units it calls "panes." Panes display different aspects of a single window, and sometimes even use different local command keys. Panes cannot be rearranged or explicitly resized, but TD will automatically adjust them when the window size changes.
TD can also automatically track your cursor location. All of the functions in TD are context-sensitive. In particular, pressing the F1 key will call up a help message relevant to the cursor's current location.
Typically, the largest window is TD's initial Module window, which will display as much of your source code as it can hold. You can move around in the code with the standard arrow and PageUp/PageDown keys. A triangle on the left border will mark the line on which execution is currently paused.
Because the Module window is, in many ways, TD's main window, it gives you access to quite a few of TD's most powerful features. For example, you can execute your program in several different ways via command keys.
If you want to test a complete run through of your program, press F9 (Run) and the program will execute until it either completes successfully or bombs. A more cautious approach can be taken with any of three different single-step commands.
The "trace into" command (F7) will run your program one source line at a time. If a line is a call to another routine, this command follows that branch into the new routine and lets you single-step through the code there. You can also single-step through a routine without going into any routines it calls with the step over command (F8). This command treats routine calls as single instructions.
Another command, animate (Alt-F4), causes TD to single-step continuously, although you can stop execution at any time by pressing any key. (One negative note, however, is that TD lacks an undo feature like that of the debugger in Microsoft QuickC 2.0. TD's undo command (Alt-F6) only allows you to restore a window you've deleted, not undo the effects of a line of code.) After it has read each line of code, TD updates all of its windows, and you can sit back and watch the program run.
You can also instruct TD to run until it executes a return with the until return command (Alt-F8). This command is useful when you're stuck in a subroutine and want to get out, as can happen when your code enters a library routine for which you don't have source code.
You can stop your program at any time by entering Ctrl-Break. This approach is fine as long as your program has easily identifiable stopping points, such as requests for input.
Sometimes, though, the finer level of control found with breakpoints is needed. Although the terminology differs, TD offers all of the breakpoint facilities of CodeView.
CodeView gives you three different breakpoint options. A breakpoint stops execution at a given code location. A watchpoint stops the program when an expression becomes true. Finally, a tracepoint stops the program when it modifies a specific memory location.
TD unites these three mechanisms into one common breakpoint facility. A TD breakpoint has four possible parts. The main component is a location. The location can be either a single line of code (a CodeView breakpoint) or an indicator that it is global.
The next component is a condition that must be true for execution to stop. For breakpoints with single locations, this condition can only be "always." For global breakpoints, you can enter a specific condition.
You can use this condition to stop execution when an expression becomes true (CodeView's watchpoint). You can write the expression in your choice of C, Pascal, or assembler; TD supports full expressions in any of these languages. However, caution must be taken in writing these expressions, because serious side effects can be caused by the use of such items as i++.
A condition can be used to tell TD to stop when a memory location changes (CodeView's tracepoint).
A final condition feature lets you stop program execution when a hardware interrupt occurs. This is a nice capability if you want to use TD in conjunction with a hardware debugger such as Atron's 386 Source Probe.
The third component of a TD breakpoint is a count, which defaults to 1. This count tells TD how many times it should let the breakpoint occur before it takes over.
You can control the action that TD takes at that point with the final breakpoint component. You can stop TD execution, log the value of an expression, or even execute another piece of code.
All of the breakpoints that occur in your program and the types of each can be seen in the Breakpoint's window. This window is summoned via the View menu in the menu bar, which has an option for each of TD's windows.
The current state of your variables can also be seen by using one of three TD windows. The first of these is the Watch window, another initial TD window. Place the cursor on a variable and press Ctrl-w (watch), and the variable will appear in this window. You can display an unlimited number of variables here. Any time the value of a variable in this window changes, the new value will appear.
The Inspector window also keeps track of variables, but a separate window must be used to display each variable. You can, however, have many Inspector windows active at once. To bring up an Inspector window, place the cursor on a variable and press Ctrl-i (inspect). Once this has been done, a single command (F3) will close all of the active Inspector windows.
The main difference between the variable displays in these two windows is their appearance. The single Watch window has a necessarily crammed display, while each Inspector window has more room for its one variable. The Watch window is handy for simple variables, while an Inspector window's additional space is better for complex structures.
Both window types understand C structures and Pascal records, as well as other variable types. This is a definite improvement over CodeView, which lacks this capability. Consider, for example, the pointers in a linked list of structures. Inspect the first element. To see the next element, move the cursor to the next structure pointer and press the Ctrl-i again. You can easily wander through a linked list.
The third way to examine variables is through the two panes of the Variable's window. The left pane displays the global variables, while the right shows your current routine's local variables.
In any of these three windows you can change the value of a variable. Just position the cursor on the variable, enter Ctrl-c (change), and type a new value.
While breakpoints and variables are important, it's TD's other windows that provide the necessary information about your program's execution.
The Stack window, for example, shows the routines in the stack form. Its local commands let you examine any routine in the stack. In a similar manner, you can look at the local variables for any stack frame.
The User Screen window displays the screen output of your program. If you have a color monitor that supports multiple pages, TD will use that feature to speed switching between its display and your program's. You can examine any ASCII file via TD's File window.
There are times when a high-level language isn't enough. To get to the assembly code, you first need to bring up the CPU window. This complex window has five panes.
The first is the code pane. It can display code in any of three ways: by showing only assembler and no source code; a display of each source line above the assembler lines that it generated; or each routine in the language in which it was written.
One of the CPU window's local commands, Ctrl-c (caller), takes you to the line of code that called the current routine. TD gets this information from the stack. If the right stack frame isn't located, TD may become "confused" and work incorrectly.
Another local command, Ctrl-n (new) CS:IP, lets you start execution at any line of code, which is a handy way to skip unwanted code.
The second pane is the data pane, which looks a lot like the old DOS DEBUG D command. It displays lines that show a hex address, a hex data value, and the ASCII characters for that value. TD goes a step further than DEBUG, providing you with options that display values as eight hex bytes, four hex words, two hex long words, or one float. These options can save you the byte-swapping hassles that plague DEBUG.
TD also solves another of DEBUG's problems. Because DEBUG is the currently executing program, the screen addresses and interrupt vectors displayed always show information about DEBUG itself, not about your program. TD shows your program's screen values and interrupt vectors in its data pane. It has to lie to do this, of course, because it is the currently executing program, but in this case fiction is much more useful than truth.
Another nifty feature of the data pane is its local command, Ctrl-F (follow). This command lets you "walk" down a chain of pointers.
The CPU window's third pane, the Stack pane, displays the top few 32-bit quantities on the stack. As in the data pane, you can follow pointers on the stack.
The final two panes are the Registers and Flags panes. The Registers pane shows all of the registers in either 16- or 32-bit format. The Flags pane displays all of the flags.
You can change any of the items, including code, in any of these panes. TD contains a full assembler to process code that you enter although, as we noted earlier, TD does not allow you to save code changes.
Two more of TD's windows, the Registers and Dump windows, are basically equivalent to the Registers and Data panes of the CPU window.
TD also contains a Log window, which provides a handy way to retrace your steps. The log contains error messages and other information, such as the output of any breakpoint actions. The log can be kept in this window or stored in a log file. CodeView lacks such a log function.
TD's final window, the Numeric Coprocessor window, displays the full state of your PC's math coprocessor chip or emulator. TD will automatically detect whether you're using an emulator or a chip. If you're using a chip, it will tell you which kind. This window is great for debugging floating point assembler code, which is another feature lacking in CodeView.
In addition to all of its windows and commands, TD offers many other useful features, several of which make it easier for you to perform repetitive tasks.
For example, every TD menu, unlike those found in Turbo C, will remember your last choice. TD also compiles a history list for every area in which you can type data. TD will remember the last ten responses for most prompt boxes. You can move to the one you want, edit it if necessary, and then press Enter to execute it again.
TD will also let you define keystroke macros, which can contain as many keystrokes as you want.
If you don't want to lose your carefully chosen window layouts and macros, this information, as well as other settings, can be saved.
In addition to all of its other features, TD also offers several interesting options for more advanced debugging environments. Two displays can be hooked to your PC, allowing you to view TD in one and display your program's output in the other. This capability is great for debugging highly interactive applications.
Because TD.EXE is a 17OK-executable program, TD also offers many ways to conserve memory.
If your computer has EMS memory, you can start by moving TD's symbols into EMS. TD saves and restores the EMS driver's state in the process, so that programs that use the driver can still take advantage of this memory-saving feature. This technique won't recover much memory, but sometimes every little bit helps.
A more radical approach is to use TD's remote debugging capabilities. To accomplish this you must run your program on one machine and TD on another. By connecting the serial ports of the two PCs, TD can "talk" to your program over that connection at speeds of up to 115K-bits per sec. With this approach, the only memory you will lose on the PC with your program is the 15K that TD's communication program, TDREMOTE.EXE, requires. You can also use a TD program called TDRF.EXE on the debugging PC to transfer files between the two systems.
If you've got a 386, TD can use the 386's virtual mode. TD's 386 version, TD386.EXE, is a standard part of the package. This program requires 700K of extended memory and loads above the 1M line. The program you're debugging loads exactly where it would during normal execution. In addition to saving memory, this approach is great for finding bugs that are dependent on the program's position in memory.
TD386.EXE uses a device driver, TDH386.SYS, that works only on Real mode programs. TD itself offers 386 instructions beyond those restricted to Protected mode. It can also use the 386's hardware debugging registers for greater speed.
If your program is close to fitting into memory, but can't quite make it, the TDPACK.EXE utility may do the trick. It shrinks the debugging information in an .EXE. When you've finished debugging, that information can be removed with the TDSTRIP.EXE utility, which allows you to avoid recompiling your program without the debug switch.
Two other useful utilities help TD to work with code from other compilers. TDMAP.EXE combines a program's .EXE and .MAP (output from Microsoft LINK) files so that you can do symbolic debugging. In contrast, CodeView can't use .MAP files, and forces you to compile with full debugging on (-v for C) for source-level debugging.
TD can work on any file that CodeView can handle, courtesy of the TDCONVRT.EXE utility that converts files to TD's format. This utility will work on any executable program, including one produced by Microsoft's Fortran or Basic compilers. This is one instance where TD falls short of CodeView, as CodeView can accept input in either language's syntax, while TD is limited to assembler, C, and Pascal syntax.
We're sold on TD as the current ideal debugging environment on a flat-screen VGA monitor in 50-line mode, with a 25-MHz 386, with 4 Mbytes of extended memory running the whole show. If you do much programming, TD may represent the best $149.95 you can spend.
Turbo Debugger 1.0. Requirements: 100 percent IBM-compatible and IBM PS/2-compatible systems; PC-DOS or MS-DOS 2.0 or later, 384K RAM minimum; hard disk recommended but not required; will work with any monitor. Price: $149.95 (includes Turbo Assembler). Available options: Comes as part of the Turbo C Professional and Turbo Pascal Professional packages, each of which costs $249.95. Current Turbo C and Turbo Pascal users can upgrade to the Professional packages for $99.95 each. Company information: Borland International, 1800 Green Hills Road, P.0. Box 660001, Scotts Valley, CA 95066-0001, 408-438-8400.
Copyright © 1989, Dr. Dobb's Journal