Steps needed to use a real mode library in protected mode as a DLL for the
Borland C++ compiler version 3.0.

1) We have a library called real.lib which we want to use in a protected mode
   program.  For informational purposes only,  here is what the source for my
   library  looks like.  And here are the steps I took to create it, remember
   that you'll be getting this real mode library from a 3rd party.
   bcc -c -ml -N- real.c
   del real.lib
   tlib real.lib +real.obj


------------------------------------------------------------------------------
/* this is a real mode library */
#include <stdio.h>

extern  int Draw_Line(int, int, int, int),
            Fill_In_Params(char *, int *);

extern   char  *foo(void);

int   real_variable = 123;

int   Draw_Line(int x1, int y1, int x2, int y2) {
   /* do something here */
   return(1);
}  /* END OF Draw_Line() */

char  *foo()   {
   return("This is a test");
}  /* END OF foo() */

int   Fill_In_Params(char *param, int *dog_age)   {
   param[0] = 'D';
   param[1] = 'C';
   param[2] = 'M';
   param[3] = '\0';
   *dog_age = 11;
   return(1);
}  /* END OF Fill_In_Params() */
------------------------------------------------------------------------------
   I show you the source so you'll see what the functions  do in the library.
   IT IS FOR INFORMATIONAL PURPOSES ONLY.  IT IS NOT PART OF THE NORMAL STEPS
   TO USE A  REAL  MODE  LIBRARY  WITH A  PROTECTED  MODE  PROGRAM.  YOU CAN,
   HOWEVER, USE THIS AS A WAY TO TEST OUT, AND GET A FEEL FOR THE PROCESS.



1.1) Now let us create a file called RMDLLLIB.C

------------------------------------------------------------------------------
#define  SUCCESS  0
#define  FAILURE  1

#include <stdio.h>   /* for definition of NULL */
#include <memory.h>
#include "phapi.h"

#define  MK_FP(seg, ofs)   ((void far *)\
                           (((unsigned long) (seg) << 16) | (unsigned) (ofs)))

/* step 1 */
static char c; // just need a little bit, you know.
               // It's needed to load up DS, take it out and you'll see what
               // happens.  If you have other statics, you may replace this
               // with them.

extern   void  _far  *  _loadds  RM_malloc(size_t);
extern   void  _far  _loadds  RM_free(void *);

extern   int   Draw_Line(int, int, int, int),
               Fill_In_Params(char *, int *);

extern   char  _far  *foo(void);

extern   void  _far  *  _cdecl   _loadds  malloc(size_t);
extern   void  _far  _loadds  free(void far *);
extern   int   _loadds  test_malloc(void);


int   _loadds  My_Draw_Line(int x1, int y1, int x2, int y2) {
   return(Draw_Line(x1, y1, x2, y2));
}  /* END OF My_Draw_Line() */

char  _far  * _loadds My_foo()   {
   return(foo());
}  /* END OF foo() */

int   _loadds  My_Fill_In_Params(char *param, int *dog_age)   {
   return(Fill_In_Params(param, dog_age));
}  /* END OF My_Fill_In_Params() */

void  _far  *  _loadds  malloc(size_t n)  {
REGS16   regs;
SHORT word_count = sizeof(size_t) / 2;
USHORT   result;

   memset(&regs, 0, sizeof(REGS16));

   result = DosProtFarCall((PFN)RM_malloc, &regs, 0, word_count, n);
   if (result != SUCCESS)  {
      puts("An error occured on the DosProtFarCall.\n");
      return(NULL);
   }  else  {
      return(MAKEP(regs.dx, regs.ax));
   }
}  /* END OF malloc() */

void  _far  _loadds  free(void far *p) {
REGS16   regs;
SHORT    word_count = sizeof(p) / 2;
USHORT   result;

   memset(&regs, 0, sizeof(REGS16));
   result = DosProtFarCall((PFN)RM_free, &regs, 0, word_count, p);
   if (result != SUCCESS)  {
      puts("An error occured on the DosProtFarCall.");
   }
}  /* END OF free() */

int   _loadds  test_malloc()  {
unsigned char  *p;

//
//   disable buffering on stdout so that real mode output shows up.
//
   setvbuf(stdout,NULL,_IONBF,0);

   /* allocate 1K */
   p = malloc(1024);
   /* we don't bother checking for NULL here, because where we do the
      DosAllocRealSeg (RM_malloc in pmlibdll.c) we check to see if
      it was successful, if not we exit out of the program (a nasty thing
      to do in most circumstances (I didn't clean myself up!))           */

   /* do something here with the memory if you like */

   /* free it */
   free(p);
   printf("Allocated and freed 1K\n");
   return(SUCCESS);
}  /* END OF test_malloc() */

void  main()   {
   /* why do I need this?! (cuz ms wants it) */
}  /* END OF dummy main() */
------------------------------------------------------------------------------

1.2) Compile it using bcc -ml! -f -N- -Oi -c rmdlllib.c
1.3) Note: Please see your manual regarding mixing real and protected mode code


2) We will now bcc286 -keep -realdll rmdlllib.obj real.lib in order to create
   rmdlllib.dll. We use -keep so you can see what bcc286 is doing behind your
   back (if your interested that is). real.lib, as you  probably remember, is
   our  original  real  mode  library,  whose goal it is of ours to use. Here
   below is rmdlllib.def
   
------------------------------------------------------------------------------
LIBRARY rmdlllib
DESCRIPTION 'SAMPLE REAL MODE DLL'
EXPORTS
        _My_Draw_Line
        _My_foo
        _My_Fill_In_Params
        _real_variable
	_test_malloc
IMPORTS
	_RM_malloc=mainapp._RM_malloc
	_RM_free=mainapp._RM_free
	
------------------------------------------------------------------------------

2.1) Note: Please see your manual regarding mixing real and protected mode code


3) Let us now create rmdlllib.lib for our use later by doing a
   \borlandc\bin\implib rmdlllib.lib rmdlllib.dll


4) Now we are going to get pretty dirty here.  I'll create a  file called
   pmlibdll.c which will do the actual interfacing by using some Phar Lap
   calls. We will compile it by doing a bcc -c -ml -N- pmlibdll.c
------------------------------------------------------------------------------
#define  SUCCESS  0
#define  FAILURE  1

#include <stdio.h>   /* for definition of NULL */
#include <stdlib.h>  /* for exit */
#include <dos.h>
#include <mem.h>
#include "phapi.h"   /* or <phapi.h> */

struct   hp {
   USHORT   para,
            sel;
   void  *next;
};

typedef  struct   hp heap;

extern   int   _loadds  test_malloc(void);

extern   int   init_malloc(PFN, PFN);

static   heap  *first;

extern   void  _far  *  _loadds  RM_malloc(size_t);
extern   void  _far  _loadds  RM_free(void *);



extern   int   _far  My_Draw_Line(int, int, int, int),
                     My_Fill_In_Params(char far *, int far *);

extern   char  _far  *My_foo(void);

extern   void  init_lowmem_buf(void);

REGS16   regs;

static   BYTE  far   *lowmem_buf = 0;
static   BYTE  far   *realptr_lowmem_buf;

char  _far  *foo()   {
char  _far  *result;
   if (DosRealFarCall(DosProtToReal((PVOID)My_foo), &regs, 0, 0)) {
      return("DosRealFarCall failed");
   }
   if (!regs.dx)  {
      return("NULL");
   }
   result = DosRealToProt((REALPTR)MAKEP(regs.dx, regs.ax));
   if (result) {
      return(result);
   }
   if (DosMapRealSeg(regs.dx, 0x10000, &regs.dx))  {
      return("DosMapRealSeg failed");
   }
   return(MAKEP(regs.dx, regs.ax));
}  /* END OF foo() */

int   Draw_Line(int x1, int y1, int x2, int y2) {
   DosRealFarCall(DosProtToReal((PVOID)My_Draw_Line), &regs, 0, 4, x1, y1, x2, y2);
   return(regs.ax);
}  /* END OF Draw_Line() */

int   Fill_In_Params(char far *param, int far *dog_age)  {
   DosRealFarCall(DosProtToReal((PVOID)My_Fill_In_Params), &regs, 0, 4,
      &realptr_lowmem_buf[0], &realptr_lowmem_buf[4]);
   /* you may want to pass the size of memory to be copied, but I'll simply
      hardwire it in for simplicity's (?) sake.                              */
   _fmemcpy(param, &lowmem_buf[0], 4); /* param is only 4 chars long */
   _fmemcpy(dog_age, &lowmem_buf[4], sizeof(int));
   return(regs.ax);
}  /* END OF Fill_In_Params() */

int   lets_test_malloc()   {
   DosRealFarCall(DosProtToReal((PVOID)test_malloc), &regs, 0, 0);
}  /* END OF test_malloc() */

void  init_lowmem_buf() {
USHORT   sel,
         para;
   /* allocate 1K of conventional (real) memory */
   if (DosAllocRealSeg(1024, &para, &sel) != SUCCESS) {
      printf("Insufficient conventional memory\n");
      exit(FAILURE);
   }
   lowmem_buf = MAKEP(sel, 0);
   realptr_lowmem_buf = MAKEP(para, 0);
}  /* END OF init_lowmem_buf() */

void  _far  *  _loadds  RM_malloc(size_t n)  {
heap  *h,
      *i;
USHORT   para,
         sel,
         ret;

   ret = DosAllocRealSeg((ULONG)n, &para, &sel);
   if (ret != SUCCESS)  {
      printf("DosAllocRealSeg failure code=%d\n", ret);
      exit(4);
   }
   if (first == NULL)   {
      first = malloc(sizeof(*first));
      first->next = NULL;
      first->para = para;
      first->sel = sel;
   }  else  {
      h = first;
      while (h->next != NULL) {
         h = h->next;
      }
      h->next = malloc(sizeof(*h));
      if (h->next == NULL) {
         printf("Fatal Error:  Unable to allocate heap control block.\nExiting!\n");
         exit(3);
      }  else  {
         h = h->next;
         h->para = para;
         h->sel = sel;
         h->next = NULL;
      }
   }
   return(MAKEP(para, 0));
}  /* END OF RM_malloc() */

void  _far  _loadds  RM_free(void *p)  {
heap  *h,
      *i;
USHORT   para,
         ret;

   if (p == NULL) {
      printf("Fatal Error:  Attempting to free NULL pointer.\n");
      exit(5);
   }

   para = FP_SEG(p);
   h = first;
   i = NULL;
   while ((h->para != para) && (h->next != NULL))  {
      i = h;
      h = h->next;
   }

   if (h->para != para) {
      printf("Fatal Error:  Bad REAL mode pointer.\nExiting!");
      exit(4);
   }

   ret = DosFreeSeg(h->sel);
   if (ret != 0)  {
      printf("Non-fatal Error:  Unable to free segment.\n");
   }

   if (i == NULL) {
      first = h->next;
   }  else  {
      i->next = h->next;
   }
   free(h);
}  /* END OF RM_free() */
------------------------------------------------------------------------------


5) Two more steps, we're almost there. This is our main application, here is 
   how I chose to compile it bcc -c -ml -N- mainapp.c

------------------------------------------------------------------------------
#define  SUCCESS  0

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include "phapi.h"

extern   void  Init_It(void _far *);

extern   int   Draw_Line(int, int, int, int),
               Fill_In_Params(char *, int *);

extern   char  *foo(void);

extern   void  init_lowmem_buf(void);

extern   int   _far  real_variable; /* variable from real mode */

extern   int   lets_test_malloc(void);

char  first_param[4];
int   second_param;

void  main()   {
int   ret_code;

   setvbuf(stdout, NULL, _IONBF, 0);   /* flush buffer quicker! */
 
   init_lowmem_buf();   /* if it fails, it exits the pgm */

   printf("Draw_Line(1, 2, 3, 4)=%d\n", Draw_Line(1, 2, 3, 4));

   printf("foo returned '%s'\n", foo());
   
   printf("Calling Fill_In_Params(0x%X)\n", first_param);
   ret_code = Fill_In_Params(&first_param[0], &second_param);
   printf("ret_code=%d first_param = '%s' second_param=%d\n", ret_code, first_param, second_param);
   
   printf("real_variable=%d\n", real_variable);

   /* tests a real mode DLL calling back to protected mode code */
   lets_test_malloc();

   printf("Done.\n");
}  /* END OF main() */

------------------------------------------------------------------------------

5.1) One more thing must be done before we go further, we must create
     mainapp.def, this will enable us to do a malloc call from real->prot
------------------------------------------------------------------------------
NAME MAINAPP
DESCRIPTION 'Test program for calling back to PROT mode from real mode'
STACKSIZE 4096
STUB 'gorun286.exe'

EXPORTS
   _RM_malloc
   _RM_free
------------------------------------------------------------------------------

6) This is the  final  step.  We have to link it all together now by
   bcc286 -keep mainapp.obj pmlibdll.obj rmdlllib.lib mainapp.def
   I chose to use bcc286 to link it together, because it is rather difficult
   to do by hand, but, as  before, I used the -keep command line argument to
   enable you to look at what bcc286 is doing behind your back.

   Below is the output from run286 mainapp
------------------------------------------------------------------------------
Draw_Line(1, 2, 3, 4)=1
foo returned 'This is a test'
Calling Fill_In_Params(0x21A)
ret_code=1 first_param = 'DCM' second_param=11
real_variable=123
Allocated and freed 1K
Done.
------------------------------------------------------------------------------

