ID Number: Q75295
1.10 1.11 4.20
OS/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 reference words: dblib