INF: Programming DB-Library TSR with MS-DOS Extenders

ID: Q75295


The information in this article applies to:
  • Microsoft SQL Server for OS/2, version 4.2


SUMMARY

The MS-DOS DB-Library (DB-Lib) versions 1.1 and later use a terminate-and-stay-resident (TSR) utility to interface with the network. This utility, written to the OpenNet specification, is the connection between DB-Lib and whatever network the TSR is coded to communicate with. Because this utility is a TSR, interrupts are required to allow code execution paths from DB-Lib to the TSR. When the MS-DOS Extenders are used in protected mode, the interrupt driver scheme in DB-Lib fails to correctly interface with the TSR in real mode. This document is designed to provide enough information to the DB-Lib application developer to correctly call the TSR from protected mode to real mode.


MORE INFORMATION

The TSR consists of eight C routines managed by an assembly language interrupt manager. The eight routines are accessed via INT 62H, with AH containing the index to the requested function. The indexes are as follows:


   0 - ConnectionObjectSize
   1 - ConnectionOpen
   2 - ConnectionRead
   3 - ConnectionWrite
   4 - ConnectionTransact
   5 - ConnectionWriteOOB
   6 - ConnectionClose
   7 - ConnectionCheckForData 

The interrupt manager in the TSR examines AH for the index. To verify that INT 62H is targeted to the TSR, the DX value must be -1 and the CX value must be -2. If any of these values are not correct, the TSR calls through the original INT 62H vector.

When the DB-Lib interrupt handler is called by the low-level db-lib routines that want to communicate with the TSR, a series of actions occur. The parameters for the TSR functions are pushed on the stack in the same order as listed below, without the INTRMAP but with the addition of the index value for example, in a call that ultimately would be processed by ConnectionTransact():

   short API
   ConnectionTransact(map,ConnectionObject,readbuffer,writebuffer,writeco
   unt,readcount,readmax,TimeOut,neterrno)
   void *ConnectionObject;
   unsigned char *readbuffer;
   unsigned char *writebuffer;
   unsigned short writecount;
   unsigned short readcount;
   unsigned short readmax;
   short TimeOut;
   short *neterrno; 

The following push sequence occurs:

   push word (2 bytes: this is the index to the TSR function)
   push far void pointer (4 bytes: ConnectionObject)
   push far unsigned char pointer (4 bytes: readbuffer)
   push far unsigned char pointer (4 bytes: writebuffer)
   push word (2 bytes: writecount)
   push word (2 bytes: readcount)
   push word (2 bytes: readmax)
   push word (2 bytes: TimeOut)
   push short far pointer (4 bytes: neterrno) 

Once in the interrupt handler, the above code pushes BP, SI, and DI to save local registers in the usual C function stack frame setup. The db-lib interrupt manager then examines the index to make sure it is in range, then sets CX and DX to the verification values -2 and -1, respectively. An INT 62H is then executed. This results in the flags register being pushed and CS:IP. The TSR now has control. The index value is verified, and the verification values in CX and DX are then checked. The TSR then pushes DS to preserve the application's data segment, then it loads the DGROUP segment for the TSR into DS. The TSR now calls through a jump table to the C routine requested. When the C routine receives control, the INTRMAP is pushed on the stack as shown in the prototypes. On return from the C routine to the interrupt manager in the TSR, the application's DS is popped from the stack and an IRET is executed.

The INTRMAP and TSR function declarations are listed below. Note that ALL pointers are far, and that the TSR is compiled in large model and expects only far pointers (after all, you cannot do an interprocess call and expect to use near pointers).

   API defines to: cdecl far 

The INTRMAP structure represents the state of the stack when the function receives control:

/* This structure defines the extra bytes pushed on the stack by the

** series of calls to get from an application to the TSR entry point
** for each C function. These bytes must be accounted for in order for
** the function to correctly index into the stack to get its parameters.
** Remember, this module is ALWAYS compiled in LARGE model, and all
** pointers passed to it are FAR.

*/ 

typedef struct intrmap
{
    int  intr_man_ds;           /* interrupt manager DS save          */ 
    void far *intr_return;      /* interrupt return address           */ 
    int  intr_flags;            /* flags                              */ 
    int  intr_call_si;          /* save of SI by interrupt caller     */ 
    int  intr_call_di;          /* save of DI by interrupt caller     */ 
    int  intr_call_bp;          /* save of BP by interrupt caller     */ 
    void far *intr_call_ret;    /* return address of interrupt caller */ 
    int index;
} INTRMAP;

unsigned short API ConnectionRead(map,ConnectionObject,
buffer,readsize,MaxSize,TimeOut,neterrno)
INTRMAP map;
void *ConnectionObject;
unsigned char  *buffer;         // 512 bytes long
unsigned short  readsize;
unsigned short MaxSize;
unsigned short TimeOut;
unsigned short *neterrno;

short API
ConnectionWrite(map,ConnectionObject,buffer,Writecount,neterrno)
INTRMAP map;

void *ConnectionObject;

unsigned char *buffer;          // 512 bytes long
short Writecount;
short *neterrno;

short API
ConnectionTransact(map,ConnectionObject,readbuffer,writebuffer,writeco
unt,readcount,readmax,TimeOut,neterrno)
INTRMAP map;

void *ConnectionObject;

unsigned char *readbuffer;      // 512 bytes long
unsigned char *writebuffer;     // 512 bytes long
unsigned short writecount;
unsigned short readcount;
unsigned short readmax;
short TimeOut;
short *neterrno;

short API ConnectionWriteOOB(map,ConnectionObject, buffer,
OOBsize,neterrno)
INTRMAP map;

void *ConnectionObject;

unsigned char *buffer;          // OOBsize bytes long
short OOBsize;
short *neterrno;

short API ConnectionOpen(map,ConnectionObject,ServerName,neterrno)
INTRMAP map;

void *ConnectionObject;
char *ServerName;       // null terminated string, variable length

short *neterrno;

short API ConnectionClose(map,ConnectionObject,neterrno)
INTRMAP map;

void *ConnectionObject;

short *neterrno;

short API
ConnectionCheckForData(map,ConnectionObject,bytesavail,neterrno)
INTRMAP map;

void *ConnectionObject;

long *bytesavail;          /* pointer to buffer for number of bytes
available */ 
short *neterrno; 

All return values are returned in AX.

For an MS-DOS Extender to operate with this TSR, several conditions must be met:
  • The buffers that are passed to the TSR MUST be in real mode and all other pointers to buffers MUST be in real mode (such as the connection object pointer buffer).


  • The application can call the function ConnectionObjectSize() by calling the routine interrupt_call(0) or can intercept the return from that call when dbopen() initiates it and intercept the return value to store it. The return value in AX is the size of the connection object.


  • The interrupt_call() routine is the db-lib interrupt manager handler [0 is the index for the ConnectionObjectSize() routine] and is an assembly routine with a C entry convention. To map the connection object to real mode, you have to determine its size in program start, then allocate a real mode buffer whenever a call to any of these routines occurs [except ConnectionObjectSize() itself] and copy that connection object to the real mode buffer.


  • All other references by a pointer must be mapped to real mode.


  • To correctly intercept the interrupt manager calls, the Extender application should grab the INT 62H at the start of the program, remapping that vector to another unused vector.


  • The location the new vector points to MUST contain the string "DBLIBRARY" after a jmp near instruction. This string in the TSR code is used to determine the presence of the TSR. Inside the TSR's entry point for the interrupt is the following code:
    
           jmp     short start     ; jump around signature
           db      'DBLIBRARY'     ; signature
           start:
               ...                     ; execution code
     


If the above string is not present, on any call to dbopen() [or dbinit()], dbopen() will post an error that the TSR is not loaded. This occurs only if the application was substituting a new INT 62H vector for the DBNMPIPE.EXE vector. With the interrupt substitution completed, your application would receive control with the stack set up as shown on entry to the TSR's interrupt manager. It is the same register configuration as on entry to each TSR function, except that DS has not been pushed; therefore, to have the stack set up, remove the intr_man_ds function declaration from the INTRMAP structure.

Now the Extender routine has to map the buffers into real mode, using a buffer size as indicated for each function. Push these onto a real mode stack as indicated for the requested function. The index value can be examined from the stack to determine what the current stack configuration is. Then, switch to real mode and do the new INT, whatever that may be. Upon return to the Extender routine, the real mode buffers have to be remapped to protected mode, along with any other values set into pointer buffers (neterrno, and so on). Switch out of real mode and IRET to db-lib. Protected mode is now successfully integrated into real mode. Once in real mode in the TSR, the network redirector calls execute correctly, because the switch to real mode has already been accomplished.

Upon exit from the Extender program, the INT 62H vector MUST be restored to allow other applications to use the TSR and to allow ENDDBLIB.EXE to unload the TSR.

Additional query words: dblib

Keywords : kbprg SSrvDB_Lib
Version : 4.2
Platform : OS/2
Issue type :


Last Reviewed: March 11, 1999
© 2000 Microsoft Corporation. All rights reserved. Terms of Use.