Arthur Shipman is a computer consultant working in the upstate New York area. He has a B.A. in mathematics from Mt. St. Mary College. He writes in Assembler, BASIC, and C on MS-DOS machines. You can contact him at P.O. Box 390, Westbrookville, NY 12785.
When I graduated from my Commodore 64 to an AT clone, the clone came with a monochrome monitor and an EGA card. I later upgraded to a Nanao Flexscan color monitor, leaving me with an unused monochrome monitor. So I picked up a second video card, and now have a dual-monitor system.
For a long time my monochrome monitor sat idle and blank. Aside from Lotus 1-2-3, I found only two utilities that take advantage of a dual monitor system. (See the "Utilities For A Second Monitor" sidebar.) Then, as my C skills improved, I realized that I could easily exploit the second monitor for debugging my code. All I needed was a set of functions that would write directly to the second monitor.
The self-contained file mono.c (Listing 1) provides all the functions needed for constant feedback, status, and debugging reports on the monochrome monitor of an IBM-compatible while a program is running on the color screen. mono.c provides two functions comparable to standard C output functions: mono(), a variant of puts(), and mprintf(), a printf() to the mono monitor.
I created these functions as debuggers, not as normal display functions. This is a no-frills package. You don't have the option to clear the mono display. You don't get to position the cursor prior to outputting text. And you can't use the mono as your primary monitor while sending debug reports to the color display.The advantage, however, is freedom from display formatting concerns. The functions are line-oriented, like puts(), sending the equivalent of a newline after every message printed.
The mono() function contains two static variables that remember the current screen location on the monochrome monitor. The variable segment holds the base address at which the monochrome write starts. It is updated by one display line after every write. Before the write, mono() checks to see if segment still equals its original value. If so, then this is the first call to mono(), and the monochrome screen is cleared.
One line on the display consists of 80 display locations, each interposed with an attribute byte a total of 160 bytes per display line. Since incrementing an address segment by one adds 16 to an absolute address, mono() adds only 10 to the segment for a jump of 160 bytes. Each time the segment increases by 10, mono sends an apparent newline to the display.
After each mono write, segment's value is increased. The value is then tested to see whether it is below the bottom line of the screen. If so, mono calls scroll () and reduces the value of segment to the bottom line of the screen. scroll() is a static function, visible only from within the mono.c file. Thus, it won't conflict with any other function of the same name that you may have in your code. scroll() exists for internal use by the mono() function and cannot be called directly.
mono() is a debugging function. It offers a means to check your program as it runs, without interfering with the color display monitor your program is using. I use it simply to track function calls made within the code I'm debugging. Each new function I write begins with a call to mono(), passing the new function's own name as a parameter string. mono() dutifully reports the function's name on the mono monitor as the new function executes. This activity in no way interferes with the display on the primary monitor.
I also use mono() to indicate which path has been taken through an if-else construct. It also works in switch statements, and both within and after loops of all kinds. However, mono() can only accept a string parameter. It won't produce an intelligible message that shows variable names and their values. mprintf() does that.
mprintf() uses C's variable argument macros to construct a string from the parameters you provide, which it then sends to mono(). mprintf() accepts parameters in the same form as the standard printf() function. mprintf() receives the format string and the list of variables, and calls vsprintf() to plug the variables into the string. A call to mono() then displays it.
Be aware that mprintf() as written allows only 100 bytes of string space. If vsprintf creates a longer string, memory will be overwritten. But you can even use these debugging functions to help you discover this memory overwrite. Inserting a second call to mono in mprintf will report the length of the string that vsprintf creates.
The file mono.c is self-contained. It includes all the #includes needed to compile properly. It was written and tested on Turbo C 1.5. To my knowledge, it uses only standard C. It compiles with no warnings and no errors in the TC integrated environment with options set to report all problems found.
The file contains a main() function that I used for testing and developing these debugging routines. main(), however, is commented out by means of the #if 0 and #endif preprocessor directives. Though not needed by the debugging routines, main provides examples of calls to the debugging routines to help you get started.