Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Parallel

Taking Up Residence With Coderunner


JUN91: TAKING UP RESIDENCE WITH CODERUNNER

Brad is currently a freelance computer programmer and writer in Columbus, Ohio. He can be reached on CompuServe at 76057, 1656 or on GEnie at R.B. Andrews.


Traditionally, only those programmers thoroughly drilled in the internals of the IBM PC and experienced in assembly language programming have undertaken the writing of TSRs. Now Coderunner, a library of assembly routines from Microsystems Software that provide many TSR functions, takes on this challenge, opening the TSR door for programmers who may be less familiar with assembly language or PC secrets.

This article examines Coderunner and presents Timer, a TSR digital stopwatch that ticks off elapsed time in the upper-right of the screen. Simple as it is, the example will enable us to put the Coderunner package through its paces.

Browsing the Package

The Coderunner package includes the source code for a wide variety of sample TSRs. Among Coderunner's optimized functions are: support for string manipulation, video, a keyboard interface, time and date functions, sound/ delay, file I/O, software interrupts and interrupt traps, COM/LPT support, stack control, hotkey handling, full and tiny schedulers, DOS access, memory management, standard memory allocation, conversion routines, integer operations, and TSR specific items.

While I can't go into each of these, a few do stand out. The full scheduler, for instance, allows for up to 64K events to be active at any time, while the "tiny scheduler" allows fewer events, but requires only a small amount of memory, and works perfectly in the Timer example presented later. Other routines duplicate those in standard C libraries, but because they eliminate some generality that TSRs don't need, the routines are much smaller and more appropriate. The video routines make saving and displaying screen information a breeze; only two calls were required to save and restore the area used for the clock display in the Timer example. Microsystems Software also includes several sample TSR programs that put their routines to work. And because these are useful utilities, which you are free to modify for your own use, they are helpful in learning how to create different kinds of TSRs.

The two Professional Developer's Kits (PDKs) also contain routines useful for TSR tasks. PDK1 is mainly geared toward communication functions, with routines to handle COM interfacing, ANSI terminal management, buffered I/O, flow control, and some ancillary routines to tie up the loose ends. In addition, the sample HyperCOM program provides a working example of how these routines can be used to produce a robust telecom TSR. PDK1 also has support for LIM memory and mixed model programming, in addition to built-in print-spooling routines.

PDK3 is not quite so broad in scope, focusing instead on the problems associated with spawning programs from a TSR. But its routines are by no means limited. The capability to save the system's state and transfer all resident portions of a program, including any graphics screens and palettes, to LIM or disk is vital to anyone who wants to explore this area of programming. The samples included are thorough and useful. The most recent release also adds support for XMS memory, which Windows also supports.

While the manuals and examples have most, if not all, of the information required to get started, the startup process can sometimes seem like learning Spanish from an English-Spanish dictionary. However, this is not really Coderunner's fault--TSRs require that a number of things be kept track of. And while Coderunner simplifies the process, it doesn't remove all the pain. The best way to begin is to read all the informational sections within the manual, skipping the descriptions of the individual routines until they are needed. This approach both gets all the meat out of the manual and postpones some learning until it is required, reducing brain overload.

A Timely Example

I chose the Timer example because it tests two different areas of the Coderunner library: the tiny event scheduler and the hotkey handler. Being able to start with the guts of the clock example provided with the package made my task easy, although a bit of work was required to add hotkey support.

Timer is designed to be in one of three states: 0 if the timer is active, but nothing is displayed; 1 when the timer is active and counting; or 3 when the timer is halted, but the final count is left on the screen. Each press of the hotkey (Shift-F10, in this case) pushes it into the next state.

Timer's code is split into three separate files. TIMERINI.C in Listing One, page 104, contains main( ) and all code needed to install the TSR. TIMERDAT.C in Listing Two, page 104, holds all the data that are not required once the timer is installed, such as the text for the sign-on screen. Due to the nature of the compiler and linker, disposable variables must all be in this file--any that have been placed in the same file with the code will not be disposed of and will instead use up precious memory needlessly.

TIMER.C in Listing Three, page 104, holds the active part of the TSR. While larger TSRs might split this between several files, timer is simple enough that all of the required code and data can easily occupy a single file. MAKEIT.BAT, Listing Four, page 104, is also included so that you can easily rebuild the timer. Though MAKEIT.BAT does have a debug option, I found it simple enough to work on the timer solely in TSR mode. In spite of several reboots when something managed to hang the machine, the program was simple enough that this was the preferred approach; many of the elements of this and most other TSRs will only work in TSR mode.

Returning to main( ) in Listing One, the first step is to ensure that the TSR is not already loaded. To do this, the program scans memory for a matching cfg_rec data structure (defined in Listing Two) that identifies the TSR's four-character ID code and version number, as well as any other information the program may need. If the timer is already installed and the -r switch has been passed from the command line, timer is removed from memory, if possible. Otherwise, a warning message prints out and the loading process is aborted. If this is the first load, the screen is cleared, the desired character attributes are set, and the sign-on screen is displayed.

After the cursor is adjusted, idata_ end and icode_beg are set to allow Coderunner to discard the start-up code when we are finished. Every TSR requires the call to stay_resident to set up the needed Coderunner environment information. install_hk and install_tsc set up the hotkey handler and the tiny scheduler for our desired actions. move_to_lim comes from the PDKI module and will move the TSR to LIM memory if it is available.

Most of the TIMERDAT data (see Listing Two) is standard, but install_list[] bears a closer look. To prevent the linker from stripping out necessary code during the link process, this variable must be set to an array of all the install_xx routines used by the TSR. This is very important; it caused me a bit of consternation when the timer randomly died (and hung the machine) until I placed the proper routines in this array.

Products Mentioned

Microsystems Software Inc. 600 Worcester Road Framingham, MA 01701 508-626-8511 Coderunner: $149 $295 with source PDK1: $99; $195 with source PDK3: $99; $195 with source System requirements: DOS 3.1 or greater for development, DOS 2.1 or greater for TSR use, and Turbo C/C++ Microsoft C, or Zortech C

TIMER.C (see Listing Three) houses the two main routines: popclock and hk_isr. popclock does the actual timing and is called only when the timer is actively running. It compares the current time to the start time, converts this to the proper format, and then prints it out in the top right corner of the screen. As long as which is equal to one, an event is set for the next tick, and the process continues. Hk_isr changes the state to the next in sequence, and performs any special actions required in that mode, such as saving or restoring the information in the screen area. This approach works well in most cases, but a few problems can arise. For example, if that part of the screen has changed since the timer's start, it obviously will not look right when the timer is done. Also, programs that use video screen 1, such as Brief, may have the wrong information displayed there. The first problem is unavoidable without an extensive amount of programming, but the latter could be fixed with some additional code to check alternate screens.

Some Spit and Polish

As with any simple program, several potential enhancements are immediately visible. After the timer has stopped, it should probably be updated in case it is erased by some other user action. The stack size reserved for the program could probably be reduced with the aid of Coderunner's stack checking functions. The start up portion could also use command line parameters, or some other method, to allow the user to select the hotkey used, the timer detail level, and the colors used to display the timer. While the timer shows several elements of a useful TSR, it barely scratches the surface of the features in the Coderunner package.

Conclusion

Coderunner is geared toward use with Turbo C, Turbo C++, or Microsoft C, but is also fully compatible with Zortech C++. I found the optimal system to be the Turbo C 2.0 compiler in conjunction with the Microsoft linker to properly link the various modules. In short, Coderunner and its associated Professional Developer's Kits are a highly useful set of utilities that open the difficult world of TSR programming to anyone who can grasp the C language.


_TAKING UP RESIDENCE WITH CODERUNNER_
by R. Bradley Andrews



[LISTING ONE]
<a name="015a_000a">

#include "cr.h"

/** RESIDENT CODE STACK SPACE **/
#define HK_STK 150      /* Stack size for Hot-Key services */
#define SR_STK 64       /* Stack for COM services */
#define STK_SZ (HK_STK+SR_STK)  /* Size in words */
word isr_stk[STK_SZ+1]; /* Allow allways extra word ! */

extern popclock();  /* Main TSR entry, called by scheduler */

extern hk_isr();    /* Hot-Key service in HELLO.C */
extern word hk_list[];  /* This is in HELLOINI.C for use by isr() function */

/******** Disposable messages in MSICLDAT.C **************/
extern char sms[];       /* Signon screen                */
extern char attrc[],attrm[]; /* Screen attributes used for signon */
extern char already[];       /* Message when already loaded      */
extern char unloaddone[];
extern char unloadproblem[];
extern word init_data_end;   /* Marker for end of disposable data */

/* Dummy function marks start of disposable code  */
init_code_start()
{
}


/* this main is called only once on program load, after that it is discarded */
main()
{  int i;
   char *a;

    if(second_load())   /* Check if TSR already loaded */
        {
        i=str_pos('-',cmd_line);
        if (i && ((cmd_line[i]|0x20)=='r'))
            {
            if(remove_tsr())    /* Remove TSR from memory */
                {
                dspf(unloaddone); /* Inform about unloading */
                }
            else dspf(unloadproblem); /* Inform about unloading */
            }
            else dspf(already); /* Otherwise show Help message */
            mv_crs();   /* Reposition real cursor */
            return(1);  /* Exit with errorlevel 1 */
         }

 /***** Display Signon Screen *****/
    clr_scr();
    a = color ? attrc:attrm; /* Select proper screen attributes */
    crs_x=20; crs_y=7;   /* Location to put signon box      */
    dspf(sms,a);         /* Show the screen                 */

    crs_x=0; crs_y=scr_len-2; /* Move to the bottom of screen    */
    mv_crs();         /* Place real cursor there         */
    crs_y=0;          /* Top line used to display clock  */

  idata_end=&init_data_end;     /* This enables init data disposal. */
  icode_beg=init_code_start;        /* This enables init code disposal. */

  stay_resident(isr_stk,STK_SZ*2);  /* Enable resident mode             */
  install_hk(hk_list,hk_isr,STK_SZ*2,0x7F); /* HOT-KEY Support */

  install_tsc(popclock,2*STK_SZ,1); /* Install tiny scheduler           */
/*  add_tsc_event(8L);      /* Add event to timer, 8 ticks from now  */
                /* Popclock will be called by scheduler  */
  return(0);            /* Set errorlevel to 0                   */

}






<a name="015a_000b">
<a name="015a_000c">
[LISTING TWO]
<a name="015a_000c">

/* this module contains ALL disposable data only.  The marker at the end
   called init_end_data is just a marker to the end of disposable data
   NO CODE CAN BE PUT INTO THIS MODULE, DATA ONLY */

#include "cr.h"

/* Video attributes for signon screen. The last one is for the Clock */

/*char attrc[]={0x17,0x1E,0x13,0x1F,0x1B,0x17}; /* Color screen attributes */
/*char attrm[]={0x07,0x0F,0x07,0x07,0x07,0x70}; /* Mono screen attributes  */

/* This is the signon message */
char sms[]= "`0"
        "[_________________________[`m"
        "[`1       Timer 1.00        `0[`m"
        "[`2      DDDDDDDDDDD        `0[`m"
        "[`4        Author:          `0[`m"
        "[`4   R. Bradley Andrews    `0[`m"
        "[\\\\\\\\\\\\\\\\\\\\\\\\\[`n`5";

char already[]="`nError - Timer already present."
               "`nEnter MSICLOCK -R to unload.`n";
word hk_list[]={M_RS+0x44,0}; /* Right-Shift Enter  */

char unloaddone[]="Timer has been removed from memory.`n";
char unloadproblem[]="Timer could not be removed from memory.";
char _tsr_name[]="Timer";

struct cfg_rec config_block = { /* This record stays in only one module */
    sizeof(config_block),   /* Configuration block size */
    'T','I','M','E',    /* Program ID string */
    100,            /* Version 1.00 */
    };

/****  STORE THIS IMMEDIATELY AFTER THE LAST DISPOSABLE DATA ITEM ****/

/****  1. Store all install_?? type function pointers into install_list ****/
fp install_list[]={install_tsc, install_hk, install_bk};  /* Only tiny scheduler used */

/****  2. Put Marker for the end-of-init-data (must be = NONZERO) ****/
word init_data_end=1;
/************************************************************************/






<a name="015a_000d">
<a name="015a_000e">
[LISTING THREE]
<a name="015a_000e">

#include "cr.h"

/** RESIDENT CODE STACK SPACE **/
#define HK_STK 150      /* Stack size for Hot-Key services */
#define SR_STK 100      /* Stack for COM services */
#define STK_SZ (HK_STK+SR_STK)  /* Size in words */

#define kWidth  13      /* width of display string */

char time_str[]=" 00:00:00:00 ";
char blank_str[]="             ";

word sbuf[kWidth];

char attrc[]={0x17,0x1E,0x13,0x1F,0x1B,0x17}; /* Color screen attributes */
char attrm[]={0x07,0x0F,0x07,0x07,0x07,0x70}; /* Mono screen attributes  */

long start_ticks;

int which=0;

popclock(void)
{  register char *t,*s;
    register long cur_ticks;
   struct time_rec cur_time;

    if (which==1) {
        add_tsc_event(1L);  /* Setup next event in 1/3 second */
    }
    cur_ticks = bios_ticks();
    cur_ticks = cur_ticks - start_ticks;
    ticks2time(cur_ticks, &cur_time);

    t=&cur_time.hours;  /* Prepare to convert hh:mm:ss */
    s=time_str+1;
    do cv_b2dec((byte)*t,s), s+=3;     /* Convert 1 byte to decimal */
      while (--t>=&cur_time.sec100);  /* Until done with seconds */

    chk_video();          /* Obtain current video settings */
    crs_x=scr_width-kWidth;   /* Set cursor to upper right corner */
    dsp(time_str);        /* Display time string there */

}
hk_isr()
{
    if (!which) {
        crs_x=scr_width-kWidth;   /* Set cursor to upper right corner */
        get_block(kWidth,1,sbuf);
        start_ticks = bios_ticks();
        add_tsc_event(1L);      /* Add event to timer, 8 ticks from now  */
        which++;
    }
    else    {
        if (which == 1 ) {
            which++;
        }
        else {
            crs_x=scr_width-kWidth;   /* Set cursor to upper right corner */
            put_block(kWidth,1,sbuf);
            which = 0;
        }
    }
}





<a name="015a_000f">
<a name="015a_0010">
[LISTING FOUR]
<a name="015a_0010">

REM  Use switch  d   for debug mode
REM
if %1x==dx goto compdebug
tcc -c -I..\ -DPDK1 timer*.c
goto linkit
rem
:compdebug
tcc -c -v -I..\ -DDBG timer*.c
:linkit
del timerini.lib
lib timerini +timerini;
if %1x==dx goto dbg
:lnk1
link ..\r0 timerdat timer ..\r1,timer,,..\k1 ..\cr  timerini/NOI/NOD/M;
goto exit
:dbg
tlink ..\r0 timerdat timer ..\r1,timer,,..\cr  timerini/c/m/v;
:exit


Copyright © 1991, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.