Kerry is a senior programmer at M & R Services Inc. He can be contacted at 1301 5th Ave., Suite 3600, Seattle, WA 98101.
You're probably already saying "Ugh, even if I can mix real-and protected-mode code, why would I want to?" Well, there are times when it makes good sense. Where I work, for example, we recently developed a mathematical tool for actuaries that manipulates large amounts of data, anywhere from many hundreds of kilobytes to several megabytes. The only sensible way to deal with such large amounts of information was to take advantage of protected mode's large address space. But (there's always that "but") some of our files were in Novell's Btrieve format, and Btrieve, as of this writing, doesn't come in a DOS-usable, protected-mode version.
Our alternatives were to redo the code with another file manager that could run in protected mode, or stay with Btrieve and mix addressing modes. Several products on the market include source code that runs under protected mode, and if we started again, I would certainly examine that option. But because time was short and we wanted to minimize our effort, we chose to mix modes.
If you think about it, you can probably come up with similar scenarios of your own. What if, for example, you have a highly specialized math library, and you have only the real-mode object code, or you have a special-purpose device attached to your computer that uses a real-mode device driver. Mixing real-and protected-mode code is a useful technique you could apply in both cases.
It's no secret that real- and protected-mode addresses are very different beasties. Real-mode addresses correspond directly to hardware addresses; protected-mode addresses are logical addresses that don't map directly to hardware addresses. They have to be decoded by the processor's memory-management circuitry.
Because there is no direct correlation between real- and protected-mode addresses, how do you move data between modes? You could (and sometimes should) put the values you need into registers and switch modes. It's fast, it's simple, but it's only good for very small amounts of data, and you would probably have to build an assembly language routine to do it.
Another alternative would be to write what you need to disk and let DOS and the extender slug it out. You could certainly handle large quantities of information, but the performance would be excruciatingly slow.
A third method, and the focus of this article, is to use an intermode call buffer. This is an area of memory guaranteed to be accessible to both real- and protected-mode addressing. You get speedy access to your data, and you can declare enough space to access realistic amounts of information.
The first requirement for a call buffer is that it has to be accessible in real mode. That means it has to live in the first megabyte of memory. (Sorry, no silver bullets here.) The next requirement is to make sure there is an entry in the protected-mode descriptor table that defines a segment of memory with the same length, at the same physical address. You don't have to worry about the details of this unless you really like to get under the hood. The DOS extender will take care of allocating the call buffer space and mapping it to protected-mode space. You determine the size of the call buffer with command-line switches for the extender or with link options.
How do you get the address information on the call buffer? You have to ask the extender to give it to you. There are two ways to do that: The first is to link in the extender's Application Program Interface (API) library and simply make a function call from your protected-mode program. Because the extender starts your program in protected mode, that's easy to do: See APIBTRV.C (Listing One, page 82).
Another way to get the scoop on the call buffer is to issue a software interrupt directly to the extender. That avoids the use of an outside library, but takes a little more effort to figure out and make work. Then you have to dig through the extender documentation to get the particulars for each call you need. I don't know about you, but to me int386() calls are harder to read in the source code than function names. Anyway, 386BTRV.C shows how to use Watcom's int386() to make the calls to the extender.
Protected-mode programs can issue real-mode interrupts and call real-mode procedures. Real-mode programs can call protected-mode procedures. Though this article focuses on the former, the accompanying text box entitled, "The Flip side of the Coin: Going from Real to Protected Mode," provides guidelines for the latter.
DOS extenders use DOS interrupt calls to invoke the functions you need. For example, Phar Lap uses DOS interrupt 0x21, function 0x25. You specify a subfunction code for the extender function you need. The functions take values you specify in a parameter block, switch to real mode, and start execution of the interrupt or procedure you asked for. When the real-mode code returns, these functions clean up and switch back to protected mode.
The sample application is a card file program; see Listing Two (page 84). Although not fancy, it does illustrate all the concepts I've covered. There are two ways to build the application, depending on which Btrieve interface file you use. The 386BTRV.C file (Listing Three, page 86) calls the extender with int386(). APIBTRV.C uses Phar Lap's API library calls from DOSX32.LIB. These interface files were built for Btrieve for DOS, Version 5.10a. They might run with earlier versions of Btrieve, but I haven't tried. The code was compiled with Watcom C, Version 8.0, and targets PharLap's 386|DOS-Extender, Version 3.0. Instructions on how to compile and run the application are in the file headers for APIBTRV.C and 386BTRV.C. Listing Four (page 88) is BTRV_DEF.H, and contains the manifest constants used to interface with Btrieve and the structures used to set up Btrieve files. Listing Five (page 90) is BTRVERRS.H, the file that contains the error codes returned by Btrieve.
When the program starts, a menu will come up allowing you to Find, Create, or List records, or Exit. Find asks for the name to find and displays the record alphabetically greater than or equal to the name you entered. Then it asks if you want to edit the record. Create allows a record to be entered and tells whether it was successful or whether a matching record was already in the file. List summarizes all the records in the file in alphabetic order. Exit closes the file and terminates the program. Please note that the source code shows the Btrieve file being opened in accelerated mode. If your program crashes or stops executing without going through the Exit option, the data in the file will probably be trashed.
The main points of interest for this article are in the Btrieve interface files. These are modeled after the interface files Novell supplies with Btrieve. They take care of setting up and executing the interrupt that requests Btrieve services. The first thing they do is make sure Btrieve is loaded before they issue the interrupt to Btrieve. The Btrieve TSR can be detected by looking at the offset part of its real-mode interrupt vector. APIBTRV gets the vector by calling DX_RMIV_GET(). BTR_INT is the number of the interrupt vector to load, and realAddr is where the vector is returned. The code in 386BTRV builds a register overlay by putting BTR_INT into CL, and 0x2503 into AX. The 0x25 is the extender function code, and 0x03 is a request to fetch a real-mode interrupt vector. After calling DOS via int386(), the vector is in EBX.
If Btrieve is loaded, the next step is to get the intermode call buffer information. APIBTRV calls DX_RMLINK_GET() and gets the buffer's real-mode address in realAddr, the buffer size (in bytes) in buffSize, and a protected-mode far pointer to the buffer in xbuff. The size can be useful, but it's not used in this application. DOS386RealProc gets the address of a real-mode entry point that allows your real-mode code to call back to your protected-mode procedures.
386BTRV gets the current segment register values and puts 0x250D in AX. 0x0D is a request for the vitals on the call buffer. The protected-mode far address is returned in ES:EDX, and that address is placed in xbuff. The real-mode address comes back in EBX, and the size, in bytes, is in ECX. EAX holds the real-mode to protected-mode entry point.
In both cases, the real-mode address is broken into its segment and offset components. The Btrieve file control block, the key buffer, and the data are copied into the call buffer through the structure laid out by xbuff, and the Btrieve parameter block is loaded with the operation code and the real-mode addresses of those copies.
Once the call buffer has been loaded, both interfaces put the Btrieve interrupt number and the real-mode address of the call buffer into a parameter block. APIBTRV then calls DX_REAL_INT with the protected-mode address of the parameter block (not the call buffer). This call sets up the machine registers, switches to real mode, executes the interrupt, then cleans up and returns to APIBTRV. 386BTRV, on the other hand, sets up the register overlay with 0x2511 in AX (0x11 is a request to issue a real-mode interrupt with registers specified), the protected-mode selector of the parameter block (not the call buffer) in DS, and its offset in EDX. Then it calls int386x() to execute the Btrieve request.
When the Btrieve interrupts return, both interfaces copy the data returned by Btrieve from the call buffer back to the application's protected-mode space. The Btrieve status code is returned to whichever routine called BTRV. Nothing to it, eh?
Mixing real-and protected-mode code may not be the most intuitive thing in the world, but it is fairly simple. If you run into trouble, I have found the DOS extender vendors knowledgeable and helpful. Next time you have a memory constraint problem, take a look at mixing modes. You just might find a good solution to your problem.
In addition to allowing protected-mode programs to invoke real-mode code, DOS extenders give you a means of calling protected-mode code from real mode. When your protected-mode code calls the extender to get the information about the intermode call buffer, one of the parameters returned to you is a real-mode far pointer to a function that you can use to call a protected-mode procedure.
To use this entry point, both your real-mode and protected-mode code must already be loaded. The protected-mode code has to pass a protected-mode far pointer to the real-mode code. The best way to do this is to put the pointer into the call buffer along with the address of the real-to-protected-mode entry point. The real-mode code can then get the entry point address from the call buffer and put it in a far function pointer variable. Push the parameters onto the stack just as for a normal function call. If you are using the Watcom compiler, remember to declare your protected-mode function with the cdecl keyword so it will expect the parameters on the stack. Then push either a long word 0 or a real-mode far pointer to a block holding protected-mode segment register values for DS, ES, FS, and GS. Finally, put the protected-mode address of the procedure to execute on the stack. Your call should look something like this: (*rmToPmEntryPoint) (pmFunctionOffset, pmFunctionSelector, &segRegBlock, parm1, parm2, parm3);. Remember, protected-mode far pointers have 16-bit selectors and 32-bit offsets.
The segRegBlock is shaped like this:
struct {
short DS;
short ES;
short FS;
short GS;
} sRegStruct;
If you give the address of such a block, the protected-mode code will use the values in that block for its selector values. If you push 0L, it will use the values those registers had when the protected-mode code first started.
The real-to-protected entry code takes the protected-mode address and the selector values block from the stack. A protected-mode return address is put on the stack, and the stack is adjusted. The protected-mode function will only see the parameters you want it to use. When you declare the protected-mode function, be sure to use the far keyword. You have to do this because the routine has to execute a far return to get back to real mode with the stack correctly aligned. After returning to real mode, the segment register parameter block will have the values that were in the segment registers when the protected-mode function returned. The stack will have zero where the protected routine address and the segment register block address were stored.
There is one nonobvious point to look out for when calling from real to protected mode. When control is given to the protected-mode routine, it is using the same physical stack space as the real-mode code. If you want to use library routines that were compiled with stack checking, you'll have to set the protected-mode stack to the stack segment in use when the protected code started. Otherwise you'll get memory protection violations. Be sure to reset the stack to the real-mode segment before returning to real mode.
--K.L.
_MIXING REAL- AND PROTECTED-MODE CODE_ by Kerry Loynd[LISTING ONE]
/*********************************************************************
* Filename....... APIBTRV.C Version........ 1.2
* Author......... Kerry Loynd
* Comments....... WatCom C interface to the Btrieve Record Manager
This particular incarnation of the BTRV call interface is for the 32-bit
Watcom compiler, and uses int386() to generate calls to BTRIEVE from programs
operating in protected mode under Pharlap's 386|DOS. To compile it use
the command line
C> WCL386 switcher.c apibtrv.c -l=<pharlap path>dosx32
To run the application use
C> BTRIEVE
C> RUN386 -callbuf n switcher <btrFile>
where n is size, in KBytes, of the intermode call buffer. The size of that
buffer must be greater than or equal to the size of the XBUFFER struct plus
the maximum data record size used by your program. BtrFile is the name of the
data file you want to use with the application. Use 1 for this application.
There is a subtle point here that you must watch. ALWAYS pass all the
defined parameters to a call to BTRV. Be especially careful to set *dataLen
to 0 if it is not used in the operation you are performing. Otherwise, you
may find that your program has gone over the wall...
I could have placed a check for an oversize dataLen and returned a status
code, but, since I can't guarantee whether Novell will use the same status
code some time in the future, I did not. NOTE: You should be aware that
Watcom's linker will not successfully search DOSX32.LIB unless you use
Watcom's librarian to extract all the object modules and then re-insert them
into the library. If you want to understand why, call Watcom or Pharlap.
* COPYRIGHT (C) 1991. All Rights Reserved.
* Kerry Loynd Seattle, WA
********************************************************************/
#include <stddef.h>
#include <dos.h>
#include <string.h>
typedef unsigned short int INT;
typedef unsigned long int UINT;
typedef unsigned long ULONG;
typedef unsigned long REAL_ADDR; /* used for real-mode addresses. */
typedef unsigned char UCHAR; /* unsigned 8-bit value */
typedef UCHAR far * FARPTR;
#define SINT short
#define BTR_ERR 20 /* record manager not started */
#define BTR_INT 0x7B /* Btrieve interrupt vector */
#define BTR_OFFSET 0x33 /* Btrieve offset within segment */
#define VARIABLE_ID 0x6176 /* id for variable length records */
/* INT_BLOCK is a structure used by 386|DOS to invoke real mode interrupts. */
typedef struct intblock {
SINT intNumber; /* Interrupt to invoke. */
SINT rds; /* real mode ds. */
SINT res; /* real mode es. */
SINT rfs; /* real mode fs. */
SINT rgs; /* real mode gs. */
unsigned long reax; /* real mode eax. */
unsigned long redx; /* real mode edx. */
} INT_BLOCK;
/* Whatever compiler you use, this structure must be byte aligned. */
typedef struct BTRIEVE_PARMS /* structure passed to Btrieve Rec Mgr */
{
REAL_ADDR bufAddress; /* caller's data buffer real mode address */
INT bufLength; /* length of data buffer */
REAL_ADDR curAddress; /* user position block real mode address */
REAL_ADDR fcbAddress; /* Real mode address of disk FCB */
INT function; /* requested function */
REAL_ADDR keyAddress; /* Real mode address of user's key buffer */
char keyLength; /* length of user's key buffer */
char keyNumber; /* key of reference for request */
REAL_ADDR statAddress; /* Real mode address of status word */
INT xfaceID; /* language identifier */
} BTR_PARMS;
typedef struct xbuffer { /* Structure of intermode call buffer. */
BTR_PARMS xData; /* Btrieve parameter block. */
INT stat; /* status of Btrieve call */
char posBlock[128]; /* Btrieve file control block */
char keyBuff[255]; /* key buffer for this Btrieve file. */
char dataBuffer[1]; /* Data buffer space. */
} XBUFFER;
// The following function prototypes are copied from pharlap.h. They have to
// be declared with cdecl because they expect their parameters on the stack.
extern int cdecl DX_RMIV_GET(UINT, REAL_ADDR *);
extern int cdecl DX_REAL_INT(INT_BLOCK *);
extern int cdecl DX_RMLINK_GET(REAL_ADDR *, REAL_ADDR *, ULONG *, FARPTR *);
SINT cdecl BTRV (SINT operation,
UCHAR *posBlock,
char *dataBuf,
INT *dataLen,
char *keyBuf,
SINT keyNum
)
{
XBUFFER far *xbuff;
SINT realSeg;
SINT realOffset;
REAL_ADDR realAddr;
REAL_ADDR DOS386RealProc;
ULONG buffSize;
INT_BLOCK callBlock;
/* Check to see that the Btrieve Record Manager has been started. */
DX_RMIV_GET (BTR_INT, &realAddr); /* Get real-mode int vector. */
if ((realAddr & 0xFFFF) != BTR_OFFSET) /* is Btrieve installed? */
return (BTR_ERR);
/* Get the real and protected addresses of the call buffer. */
/* DOS386RealProc and buffSize are not used. */
DX_RMLINK_GET (&DOS386RealProc, &realAddr, &buffSize,
(FARPTR *)&xbuff);
realSeg = (realAddr >> 16) & 0xFFFF; /* real-mode segment. */
realOffset = realAddr & 0xFFFF; /* real-mode offset. */
/* Get the key and data info from the caller. */
_fmemcpy (xbuff->posBlock, (char far *)posBlock, 128);
_fmemcpy (xbuff->keyBuff, (char far *)keyBuf, 255);
_fmemcpy (xbuff->dataBuffer, (char far *)dataBuf, *dataLen);
/* Move user parameters to xbuff, where Btrieve can find them. */
xbuff->stat = 0;
xbuff->xData.function = operation;
xbuff->xData.statAddress = realAddr + offsetof (XBUFFER,stat);
xbuff->xData.fcbAddress = realAddr + offsetof (XBUFFER,posBlock);
xbuff->xData.curAddress = xbuff->xData.fcbAddress + 38;
xbuff->xData.bufAddress = realAddr + offsetof (XBUFFER,dataBuffer);
xbuff->xData.bufLength = *dataLen;
xbuff->xData.keyAddress = realAddr + offsetof (XBUFFER, keyBuff);
xbuff->xData.keyLength = 255; /* use max since we don't know */
xbuff->xData.keyNumber = keyNum;
xbuff->xData.xfaceID = VARIABLE_ID;
/* Make call to the Btrieve Record Manager. */
/* Set up for Extended DOS call. Put real-mode interrupt call data into
the call block. */
callBlock.intNumber = BTR_INT;
callBlock.rds = realSeg;
callBlock.redx = realOffset;
DX_REAL_INT (&callBlock); /* Issue real mode int, regs specified.*/
*dataLen = xbuff->xData.bufLength;
/* Copy the key and data info back to the caller. */
_fmemcpy ((char far *)posBlock, xbuff->posBlock, 128);
_fmemcpy ((char far *)keyBuf, xbuff->keyBuff, 255);
_fmemcpy ((char far *)dataBuf, xbuff->dataBuffer, *dataLen);
return (xbuff->stat); /* return status */
}
[LISTING TWO]
// Switcher.c
// This code should be easily portable to compilers other than Watcom's
#include <stdio.h>
#include <stddef.h>
#include <process.h>
#include <ctype.h>
#include <string.h>
#include "btrv_def.h"
#include "btrverrs.h"
#define SINT short
typedef unsigned char UCHAR; /* unsigned 8-bit value */
extern SINT cdecl BTRV (int, UCHAR *, char *, int *, char *, int);
/* CREATE_STRUCT is used to create the card file. */
typedef struct tagCREATE_STRUCT {
FILE_SPEC fileInfo;
KEY_SPEC keyInfo;
} CREATE_STRUCT;
/* CARD_STRUCT is the structure of the card file records */
typedef struct tagCARD_STRUCT {
char name[21];
char address[21];
char city[21];
} CARD_STRUCT;
#define CARD_LEN sizeof (CARD_STRUCT)
#define NAME_KEY 0
static unsigned char posBlock[128]; /* Btrieve's position control */
static char searchKey[255]; /* Search key buffer. MUST be 255 long.*/
static CREATE_STRUCT cardInfo = {
sizeof(CARD_STRUCT), 1024, 1, 0L, NO_FILE_FLAGS, 0, 0,
1, 21, EXTENDED_TYPE_KEY | MODIFIABLE_KEY, 1L, ZSTRING_KEY, 0, 0, 0, 0, 0
};
static void EditRecord (CARD_STRUCT *card)
{
char done = 'n';
char gunkCatcher[80];
while (done != 'y') {
gets(gunkCatcher); /* Flush out the input buffer. */
printf ("\n\nName: %s\n:", card->name);
flushall ();
gets (card->name);
printf ("\nAddress: %s\n:", card->address);
flushall ();
gets (card->address);
printf ("\nCity: %s\n:", card->city);
flushall ();
gets (card->city);
printf ("\n%-21s, %-21s, %-21s\n", card->name,
card->address, card->city);
printf ("\n\nDone? [y/n] ");
flushall ();
done = tolower (getchar());
printf ("Done = |%c|\n", done);
}
}
static void CreateRecord (void)
{
CARD_STRUCT card;
int rc = 0;
int len = CARD_LEN;
memset (&card, '\0', sizeof (CARD_STRUCT));
EditRecord (&card);
rc = BTRV (INSERT_BTR, &posBlock[0], (char *)&card, &len,
searchKey, NAME_KEY);
if (rc != 0)
printf ("Could not insert record %s, BTRV error %d\n",
card.name, rc);
}
static void FindRecord (void)
{
CARD_STRUCT card;
int rc = 0;
int len = CARD_LEN;
gets (searchKey); /* Flush out the input buffer. */
printf ("Name to search on: ");
flushall ();
gets (searchKey);
rc = BTRV (GET_GT_EQ, &posBlock[0], (char *)&card, &len,
searchKey, NAME_KEY);
if (rc == 0) {
printf ("Found %s.\n\nEdit this record?", searchKey);
if (tolower(getchar ()) == 'y') {
EditRecord (&card);
rc = BTRV (UPDATE_BTR, &posBlock[0], (char *)&card, &len,
searchKey, NAME_KEY);
if (rc != 0)
printf ("Could not insert record %s, BTRV error %d\n",
card.name, rc);
}
} else
printf ("Could not find a record.\n");
}
static void ListRecords ()
{
CARD_STRUCT card;
int rc = 0;
int len = CARD_LEN;
printf ("\n\nList of records:\n\n");
rc = BTRV (GET_FIRST, &posBlock[0], (char *)&card, &len,
searchKey, NAME_KEY);
while (rc == 0) {
printf ("%-21s, %-21s, %-21s\n", card.name, card.address,
card.city);
len = CARD_LEN;
rc = BTRV (GET_GT, &posBlock[0], (char *)&card, &len,
searchKey, NAME_KEY);
}
if (rc != END_OF_FILE_BTR)
printf ("\n\nBTRV error %d\n", rc);
printf ("Press any key to continue.\n");
while (!kbhit()); /* wait for a key */
getch (); /* clean out the dregs. */
}
int main (int argc, char **argv)
{
int rc = 0;
int menuChoice = 0;
int len = 0;
char fakeData[1];
if (argc < 2) {
printf ("No card file specified.\n");
exit(1);
}
strcpy (searchKey, argv[1]);
rc = BTRV (OPEN_BTR, &posBlock[0], fakeData, &len, searchKey,
ACCELERATED);
if (rc == BTRIEVE_INACTIVE_BTR) {
printf ("Btrieve isn't loaded.\n");
exit(1);
}
if (rc != 0) {
len = sizeof(CREATE_STRUCT);
rc = BTRV (CREATE_BTR, &posBlock[0], (char *)&cardInfo, &len,
searchKey, NAME_KEY);
if (rc != 0) {
printf ("BTRV create returned %d\n", rc);
BTRV( STOP_BTR, &posBlock[0], fakeData, &len, fakeData,
NAME_KEY);
exit (1);
}
len = 0;
rc = BTRV (OPEN_BTR, &posBlock[0], fakeData, &len, searchKey,
ACCELERATED);
if (rc != 0) {
printf ("BTRV open returned %d\n", rc);
BTRV( STOP_BTR, &posBlock[0], fakeData, &len, fakeData, NAME_KEY);
exit (1);
}
}
menuChoice = 0;
while (menuChoice != 9) {
printf ("\n\n1. Find a record\n");
printf ("2. Create a new record\n");
printf ("3. List records\n");
printf ("9. Exit\n\n:");
scanf ("%d", &menuChoice);
switch (menuChoice) {
case 1:
FindRecord ();
break;
case 2:
CreateRecord ();
break;
case 3:
ListRecords ();
break;
}
}
BTRV (CLOSE_BTR, &posBlock[0], fakeData, &len, searchKey,
NAME_KEY);
BTRV( STOP_BTR, &posBlock[0], fakeData, &len, searchKey,
NAME_KEY);
return 0;
}
[LISTING THREE]
/*********************************************************************
* Filename....... 386BTRV.C
* Version........ 1.2
* Version Date... August 8, 1991
* Author......... Kerry Loynd
* Comments....... WatCom C interface to the Btrieve Record Manager
This particular incarnation of the BTRV call interface is for the
32-bit Watcom compiler, and uses Pharlap API library calls to
generate calls to BTRIEVE from programs operating in protected
mode under Pharlap's 386|DOS. To compile it use the command line
C> WCL386 switcher.c apibtrv.c -l=<pharlap path>dosx32
To run the application use
C> BTRIEVE
C> RUN386 switcher <btrFile>
where n is the size, in KBytes, of the intermode call buffer. The
size of that buffer must be greater than or equal to the size of
the XBUFFER struct plus the maximum data record size used by your
program. BtrFile is the name of the data file you want to use
with the application. Use 1 for this application.
There is a subtle point here that you must watch. ALWAYS pass all
the defined parameters to a call to BTRV. Be especially careful
to set *dataLen to 0 if it is not used in the operation you are
performing. Otherwise, you may find that your program has gone
over the wall...
I could have placed a check for an oversize dataLen and returned
a status code, but, since I can't guarantee whether Novell will
use the same status code some time in the future, I did not.
NOTE: You should be aware that Watcom's linker will not
successfully search DOSX32.LIB unless you use Watcom's librarian
to extract all the object modules and then re-insert them into the
library. If you want to understand why, call Watcom or Pharlap.
*
* COPYRIGHT (C) 1991. All Rights Reserved.
* Kerry Loynd Seattle, WA
* (206)624-7970
********************************************************************/
#include <stddef.h>
#include <dos.h>
#include <string.h>
typedef unsigned short int INT;
typedef unsigned long int UINT;
typedef unsigned long ULONG;
typedef unsigned long REAL_ADDR; /* used for real-mode addresses. */
typedef unsigned char UCHAR; /* unsigned 8-bit value */
typedef UCHAR far * FARPTR;
#define SINT short
#define BTR_ERR 20 /* record manager not started */
#define BTR_INT 0x7B /* Btrieve interrupt vector */
#define BTR_OFFSET 0x33 /* Btrieve offset within segment */
#define VARIABLE_ID 0x6176 /* id for variable length records */
/*
INT_BLOCK is a structure used by 386|DOS to invoke real mode
interrupts.
*/
typedef struct intblock {
SINT intNumber; /* Interrupt to invoke. */
SINT rds; /* real mode ds. */
SINT res; /* real mode es. */
SINT rfs; /* real mode fs. */
SINT rgs; /* real mode gs. */
unsigned long reax; /* real mode eax. */
unsigned long redx; /* real mode edx. */
} INT_BLOCK;
/* Whatever compiler you use, this structure must be byte aligned. */
typedef struct BTRIEVE_PARMS /* structure passed to Btrieve Rec Mgr */
{
REAL_ADDR bufAddress; /* caller's data buffer real mode address */
INT bufLength; /* length of data buffer */
REAL_ADDR curAddress; /* user position block real mode address */
REAL_ADDR fcbAddress; /* Real mode address of disk FCB */
INT function; /* requested function */
REAL_ADDR keyAddress; /* Real mode address of user's key buffer */
char keyLength; /* length of user's key buffer */
char keyNumber; /* key of reference for request */
REAL_ADDR statAddress; /* Real mode address of status word */
INT xfaceID; /* language identifier */
} BTR_PARMS;
typedef struct xbuffer { /* Structure of intermode call buffer. */
BTR_PARMS xData; /* Btrieve parameter block. */
INT stat; /* status of Btrieve call */
char posBlock[128]; /* Btrieve file control block */
char keyBuff[255]; /* key buffer for this Btrieve file. */
char dataBuffer[1]; /* Data buffer space. */
} XBUFFER;
// The following function prototypes are copied from pharlap.h.
// They have to be declared with cdecl because they expect their
// parameters on the stack.
extern int cdecl DX_RMIV_GET(UINT, REAL_ADDR *);
extern int cdecl DX_REAL_INT(INT_BLOCK *);
extern int cdecl DX_RMLINK_GET(REAL_ADDR *, REAL_ADDR *, ULONG *,
FARPTR *);
SINT cdecl BTRV (SINT operation,
UCHAR *posBlock,
char *dataBuf,
INT *dataLen,
char *keyBuf,
SINT keyNum
)
{
XBUFFER far *xbuff;
SINT realSeg;
SINT realOffset;
REAL_ADDR realAddr;
REAL_ADDR DOS386RealProc;
ULONG buffSize;
INT_BLOCK callBlock;
/* */
/* Check to see that the Btrieve Record Manager has been started. */
/* */
DX_RMIV_GET (BTR_INT, &realAddr); /* Get real-mode int vector. */
if ((realAddr & 0xFFFF) != BTR_OFFSET) /* is Btrieve installed? */
return (BTR_ERR);
/* Get the real and protected addresses of the call buffer. */
/* DOS386RealProc and buffSize are not used. */
DX_RMLINK_GET (&DOS386RealProc, &realAddr, &buffSize,
(FARPTR *)&xbuff);
realSeg = (realAddr >> 16) & 0xFFFF; /* real-mode segment. */
realOffset = realAddr & 0xFFFF; /* real-mode offset. */
/* Get the key and data info from the caller. */
_fmemcpy (xbuff->posBlock, (char far *)posBlock, 128);
_fmemcpy (xbuff->keyBuff, (char far *)keyBuf, 255);
_fmemcpy (xbuff->dataBuffer, (char far *)dataBuf, *dataLen);
/* */
/* Move user parameters to xbuff, where Btrieve can find them. */
/* */
xbuff->stat = 0;
xbuff->xData.function = operation;
xbuff->xData.statAddress = realAddr + offsetof (XBUFFER,stat);
xbuff->xData.fcbAddress = realAddr + offsetof (XBUFFER,posBlock);
xbuff->xData.curAddress = xbuff->xData.fcbAddress + 38;
xbuff->xData.bufAddress = realAddr + offsetof (XBUFFER,dataBuffer);
xbuff->xData.bufLength = *dataLen;
xbuff->xData.keyAddress = realAddr + offsetof (XBUFFER, keyBuff);
xbuff->xData.keyLength = 255; /* use max since we don't know */
xbuff->xData.keyNumber = keyNum;
xbuff->xData.xfaceID = VARIABLE_ID;
/* */
/* Make call to the Btrieve Record Manager. */
/* */
/*
Set up for the Extended DOS call. Put the real-mode interrupt
call data into the call block.
*/
callBlock.intNumber = BTR_INT;
callBlock.rds = realSeg;
callBlock.redx = realOffset;
DX_REAL_INT (&callBlock); /* Issue real mode int, regs specified.*/
*dataLen = xbuff->xData.bufLength;
/* Copy the key and data info back to the caller. */
_fmemcpy ((char far *)posBlock, xbuff->posBlock, 128);
_fmemcpy ((char far *)keyBuf, xbuff->keyBuff, 255);
_fmemcpy ((char far *)dataBuf, xbuff->dataBuffer, *dataLen);
return (xbuff->stat); /* return status */
}
[LISTING FOUR]
//BTRV_DEF.H
/* This file contains the manifest constants used to interface with Btrieve
and the structures used to set up Btrieve files. */
#ifndef _BTRV_DEF_H_
#define _BTRV_DEF_H_
#define OPEN_BTR 0
#define CLOSE_BTR 1
#define INSERT_BTR 2
#define UPDATE_BTR 3
#define DELETE_BTR 4
#define GET_EQUAL 5
#define GET_NEXT 6
#define GET_PREVIOUS 7
#define GET_GT 8
#define GET_GT_EQ 9
#define GET_LT 10
#define GET_LT_EQ 11
#define GET_FIRST 12
#define GET_LAST 13
#define CREATE_BTR 14
#define GET_STATUS 15
#define EXTEND_BTR 16
#define SET_DIRECTORY 17
#define GET_DIRECTORY 18
#define BEGIN_TRANS 19
#define END_TRANS 20
#define ABORT_TRANS 21
#define GET_POSITION 22
#define GET_DIRECT 23
#define STEP_NEXT 24
#define STOP_BTR 25
#define VERSION_BTR 26
#define UNLOCK_BTR 27
#define RESET_BTR 28
#define SET_OWNER 29
#define CLEAR_OWNER 30
#define CREATE_SUPP_INDEX 31
#define DROP_SUPP_INDEX 32
#define STEP_FIRST 33
#define STEP_LAST 34
#define STEP_PREVIOUS 35
#define GET_NEXT_EXTENDED 36
#define GET_PREV_EXTENDED 37
#define STEP_NEXT_EXTENDED 38
#define STEP_PREV_EXTENDED 39
#define INSERT_EXTENDED 40
#define GET_KEY 50
#define SINGLE_WAIT_LOCK 100
#define MULTIPLE_WAIT_LOCK 300
#define SINGLE_NOWAIT_LOCK 200
#define MULTIPLE_NOWAIT_LOCK 400
/* These are for key number options. */
#define UNACCELERATED 0
#define ACCELERATED -1
#define READ_ONLY -2
#define VERIFY -3
/* Key flag bit definitions */
#define NO_KEY_FLAGS 0x0000
#define DUPLICATE_KEY 0x0001
#define MODIFIABLE_KEY 0x0002
#define BINARY_KEY 0x0004
#define NULL_KEY 0x0008
#define SEGMENTED_KEY 0x0010
#define ALT_SORT_KEY 0x0020
#define DESCENDING_KEY 0x0040
#define SUPPLEMENTAL_KEY 0x0080
#define EXTENDED_TYPE_KEY 0x0100
#define MANUAL_KEY 0x0200
/* Extended key type definition bits */
#define STRING_KEY 0
#define INTEGER_KEY 1
#define FLOAT_KEY 2
#define DATE_KEY 3
#define TIME_KEY 4
#define DECIMAL_KEY 5
#define MONEY_KEY 6
#define LOGICAL_KEY 7
#define NUMERIC_KEY 8
#define BFLOAT_KEY 9
#define LSTRING_KEY 10
#define ZSTRING_KEY 11
#define UNSIGNED_BINARY_KEY 14
#define AUTOINCREMENT_KEY 15
/* File attribute bit definitions */
#define NO_FILE_FLAGS 0x00
#define VARIABLE_LENGTH_FILE 0x01
#define TRUNCATE_BLANKS_FILE 0x02
#define PREALLOCATION_FILE 0x04
#define DATA_COMPRESSION_FILE 0x08
#define KEY_ONLY_FILE 0x10
#define FREE_SPACE_10_FILE 0x40
#define FREE_SPACE_20_FILE 0x80
#define FREE_SPACE_30_FILE 0xC0
#define BTRV_EQ 1
#define BTRV_GT 2
#define BTRV_LT 3
#define BTRV_NE 4
#define BTRV_GE 5
#define BTRV_LE 6
#define BTRV_AND 1
#define BTRV_OR 2
/* Make sure that the following structures are packed, using whatever mechanism
your compiler defines. */
#pragma pack(1)
/* Typedefs to define the return data from a stat call. */
typedef struct {
short int recordLength;
short int pageSize;
short int indexCount;
long int recordTotal;
short int fileFlags;
char reserved[2];
short int preAlloc;
} STATS;
/* typedefs needed for CREATE operation */
typedef struct {
short int keyPosition;
short int keyLength;
short int keyFlag;
long int keyTotal;
char keyType;
char nullValue;
char rsvp[4];
} KEY_SPEC;
typedef struct {
short int recordLength;
short int pageSize;
short int indexCount;
long int unused;
short int fileFlags;
short int reserved;
short int allocation;
} FILE_SPEC;
/* typedefs needed for EXTENEDED operations */
typedef struct
{
char dataType;
short int fieldLength;
short int fieldOffset;
char comparisonCode;
char andOrExpression;
union
{
int Offset;
char Value[2];
} field2;
}
FILTER_TERM;
typedef struct
{
short int bufferLength;
char command[2];
short int maxSkip;
short int termCount;
}
FILTER_HEAD;
typedef struct
{
short int recordCount;
short int fieldCount;
}
FILTER_TAIL;
typedef struct
{
short int fieldLength;
short int fieldOffset;
}
FILTER_FIELD;
#endif
[LISTING FIVE]//BTRVERRS.H /* This file contains the error codes returned by Btrieve. */ #define SUCCESS_BTR 0 #define INVALID_OP_BTR 1 #define IO_ERROR_BTR 2 #define FILE_NOT_OPEN_BTR 3 #define KEY_NOT_FOUND_BTR 4 #define DUPLICATE_KEY_BTR 5 #define INVALID_KEY_NUM_BTR 6 #define DIFF_KEY_NUM_BTR 7 #define INVALID_POSITION_BTR 8 #define END_OF_FILE_BTR 9 #define MOD_KEY_ERROR_BTR 10 #define BAD_FILE_NAME_BTR 11 #define FILE_NOT_FOUND_BTR 12 #define EXT_FILE_ERROR_BTR 13 #define PREIMG_OPEN_ERR_BTR 14 #define PREIMG_IO_ERR_BTR 15 #define EXPANSION_ERROR_BTR 16 #define CLOSE_ERROR_BTR 17 #define DISK_FULL_BTR 18 #define UNRECOVERABLE_BTR 19 #define BTRIEVE_INACTIVE_BTR 20 #define KEY_BUFF_TOO_SHORT_BTR 21 #define DATA_BUFF_TOO_SHORT_BTR 22 #define POS_BLOCK_LEN_ERR_BTR 23 #define PAGE_SIZE_ERR_BTR 24 #define CREATE_IO_ERR_BTR 25 #define KEY_COUNT_ERR_BTR 26 #define INVALID_KEY_POS_BTR 27 #define INVALID_REC_LEN_BTR 28 #define INVALID_KEY_LEN_BTR 29 #define NOT_BTRIEVE_FILE_BTR 30 #define ALREADY_EXTENDED_BTR 31 #define EXTEND_IO_ERR_BTR 32 #define INVALID_EXT_NAME_BTR 34 #define DIR_ERROR_BTR 35 #define TRANSACTION_ERR_BTR 36 #define ACTIVE_TRANS_BTR 37 #define TRANSACT_CTL_IO_ERR_BTR 38 #define END_TRANSACT_ERR_BTR 39 #define TRANSACT_MAX_FILES_BTR 40 #define OP_NOT_ALLOWED_BTR 41 #define INCOMPLETE_ACCEL_ACCESS_BTR 42 #define INVALID_REC_ACCESS_BTR 43 #define NULL_KEY_PATH_BTR 44 #define INCONSISTENT_KEY_FLAGS_BTR 45 #define ACCESS_DENIED_BTR 46 #define MAX_FILES_OPEN_BTR 47 #define BAD_ALT_DEF_FILE_BTR 48 #define KEY_TYPE_ERR_BTR 49 #define OWNER_ALREADY_SET_BTR 50 #define INVALID_OWNER_BTR 51 #define CACHE_WRITE_ERR_BTR 52 #define BAD_INTERFACE_BTR 53 #define VARIABLE_PAGE_ERR_BTR 54 #define AUTOINCREMENT_ERR_BTR 55 #define INCOMPLETE_INDEX_BTR 56 #define EXP_MEMORY_ERR_BTR 57 #define SQUEEZE_BUFF_TOO_SHORT_BTR 58 #define FILE_EXISTS_BTR 59 #define FILTER_LIMIT_REACHED_BTR 64 #define CONFLICT_BTR 80 #define LOCK_ERR_BTR 81 #define LOST_POSITION_BTR 82 #define READ_OUTSIDE_TRANSACT_BTR 83 #define RECORD_IN_USE_BTR 84 #define FILE_IN_USE_BTR 85 #define FILE_TABLE_FULL_BTR 86 #define HANDLE_TABLE_FULL_BTR 87 #define BAD_OPEN_MODE_BTR 88 #define BAD_LOCK_TYPE_BTR 93 #define PERMISSION_ERR_BTR 94
Copyright © 1992, Dr. Dobb's Journal