home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1991 / 05 / array.asc next >
Text File  |  1991-03-15  |  34KB  |  842 lines

  1. _ARRAY BOUNDS CHECKING WITH TURBO C_
  2. by Glenn Pearson
  3.  
  4.  
  5.  
  6. [LISTING ONE]
  7.  
  8.          void *p;
  9.          void *q;
  10.        
  11.        /* Make life easier when dealing with segment and offset pointers. */
  12.           static union {void *A;
  13.                  struct {unsigned Offset,Segment;} Word; } LrgPtr;
  14.  
  15.          p = malloc(bytes);
  16.          if (p EQ NULL) error("pmalloc: Out of memory space on heap");
  17.          LrgPtr.A = q = createBoundedWindow(p,bytes);
  18.          /* Lower 3 bits are ALWAYS ON; so we can restore them later: */
  19.          /* Shift to make array smaller, and its length <= LDTSIZE */
  20.          /* Remember the p-q correspondence: */
  21.          malloclist[(LrgPtr.Word.Segment & ~7) >> 3] = p;
  22.          return(q); /* q's Offset is zero */
  23.        }
  24.  
  25. /*=====================================*/
  26.        void pfree(void *q)
  27.          {unsigned i;
  28.  
  29.          /* Make life easier when dealing with segment and offset pointers. */
  30.          static union {void *A;
  31.                struct {unsigned Offset,Segment;} Word; } LrgPtr;
  32.  
  33.          if (q EQ NULL) return;
  34.  
  35.          LrgPtr.A = q;
  36.          if (LrgPtr.Word.Offset != 0)
  37.            error("pfree: Attempt to free improper selector");
  38.          /* Lower 3 bits of a selector are ALWAYS ON: */
  39.          i = (LrgPtr.Word.Segment & ~7) >> 3;
  40.          if (malloclist[i] == NULL)
  41.            error("pfree: Attempt to free unknown window");
  42.          deleteSegOrWin(q); /* First remove bounds-checking window */
  43.          free(malloclist[i]); /* Then free memory */
  44.          malloclist[i] = NULL; /* Prevent screw ups */
  45.        }
  46.  
  47.  
  48. [LISTING TWO]
  49.  
  50. /*
  51. * Hardware-assisted Bounds Checking of Dynamic Arrays and Structures:
  52. * Dynamically allocated arrays and structures do not automatically get a private
  53. * sel with Turboc C 2.0/Ergo (or Microsoft C/Ergo).  To guarantee a private
  54. * sel, this routine sets what Ergo calls a "window" over the array.  The private
  55. * sel is then used for subsequent array accesses, so that bounds checking
  56. * is performed with no run-time overhead (because the chip hardware does it). By
  57. * building this functionality into the "pmalloc" (and calloc, etc.) and "pfree"
  58. * and "pfarfree" routines, all non-static arrays get this ability.
  59. *
  60. * NOTE 1: Code shown is for the far memory model, so all pointers are far;
  61. * Additional explicit casting will be needed for smaller models.
  62. * NOTE 2: If using Turbo C huge model with osx86, any function call that
  63. * passes the address of an automatic (stack) variable must have that
  64. * address explicitly normalized first.  Recommendation: use Microsoft C instead.
  65. *
  66. *******************************************************************************
  67. * --- The following bounds-related utility functions are currently private to
  68. * --- this file, but could be made public as needed:
  69. *              errorstrcat()
  70. *           deleteSegOrWin()
  71. *           createDataWindow()
  72. *              allocateMultipleWindows()
  73. *              deleteMultipleWindows()
  74. *              createBoundedWindow()
  75. *              checksize()
  76. *              markPtoQ()
  77. * --- The following routines provide runtime bounds checking for
  78. * --- protected mode heap memory allocation.  Each routine has the
  79. * --- same syntax as the corresponding Turbo C 2.0 call (except "p" prefix)...
  80. *              pmalloc()
  81. *              pcalloc()
  82. *              prealloc()
  83. *              pfarmalloc()
  84. *              pfarcalloc()
  85. *              pfarrealloc()
  86. *           pfree()
  87. *              pfarfree()
  88. ******************************************************************************/
  89.  
  90. #include <alloc.h>
  91. #include <stdio.h>
  92. #include <dos.h>
  93.  
  94. /**********************FILE GLOBALS********************************************/
  95.  
  96. #define private static
  97. #define forward extern
  98. #define import extern
  99. #define export
  100. #define uint16 unsigned short
  101. #define uint32 unsigned long int
  102. #define int16 short
  103. #define int32 long int
  104. #define EQ ==
  105. #define ERRORSTRCAT
  106. #define ERROR(A,B) errorstrcat(A,B)
  107. #define ERROUT(A) {printf("Fatal Error %s.\n",A);exit(-1);}
  108.  
  109. /* Compile time debug tracing */
  110. /* #define TRACE(A) A */
  111. #define TRACE(A)
  112.  
  113. #define LDTSIZE 1024
  114. /* Reserve arbitary slot space at extreme of LDT table for Ergo work
  115.    space, such as extra windows sometimes needed for malloc */
  116. #define MAXLDTNUM (LDTSIZE-25)
  117. /* Assert: MAXLDTNUM <= LDTSIZE */
  118. #define MAXSEGNUM ((MAXLDTNUM << 3) & 7)
  119.  
  120.  
  121. /* Eclipse's default LDT size (in units of number of entries) is 1024.  The low
  122. end of the LDT will already be taken up by code and data sels, before any
  123. dynamic data allocation occurs.  If the program doesn't do much mallocing, the
  124. default may be sufficient.  Otherwise, you take a generous guess as to the
  125. maximum number of memory requests active at any time, round up to the next
  126. power of 2, and set LDTSIZE to that value.  However, a 286 machine can have at
  127. most 8192 entries in its Local Descriptor Table.  Thus, a program that has, say,
  128. 10,000 simultaneously active dynamic arrays would have to use a more selective
  129. method of assigning private sels than simply universal use of "pmalloc" and
  130. "pfree".
  131.  
  132. LDTSIZE should match the value stored in the os286 kernel.  A larger LDT is
  133. requested by modifying the kernel (the developer's on-disk .EXE file) by
  134. specifying, for example:
  135.      286setup ldtsize 2048
  136. Valid ldtsize values range from 128 to 8192.  This kernel can be subsequently
  137. bound in with the distributed program.  The current value of ldtsize (and all
  138. other settable parameters) can be viewed by:
  139.      286setup -help
  140. Alternatively, for unbound applications, one may specify the ldtsize on the
  141. command line during loading of the kernel TSR:
  142.      os286 ldtsize 2048
  143. This would typically be part of a batch file.  Here, the load image of the
  144. kernel is altered, not the disk file, so the change is not "sticky". */
  145.  
  146. void *malloclist[LDTSIZE]; /* K&R promises all values are
  147.                       initially zero (i.e., NULL) */
  148.  
  149. /* For createBoundedWindow funtion: */
  150. #define SEGMENT 0
  151. #define WINDOW  1
  152. #define REALSEGMENT 2
  153. #define REALWINDOW 3
  154.  
  155. /***************************************************************************/
  156.  
  157. #ifdef ERRORSTRCAT
  158. /*======================================================================== */
  159. void errorstrcat(char *stringA, char *stringB)
  160. {char sss[90];
  161.  
  162.  strcpy(sss,"In ");
  163.  strcat(sss,stringA); strcat(sss,", ");
  164.  strcat(sss,stringB); gxerror(sss);
  165. }
  166. #endif
  167.  
  168. /*======================================================================= */
  169. void private deleteSegOrWin(void *selector)
  170. /*---------------------------------------------------------------------------
  171. * Call to DOS delete-segment service, extended by Ergo to include
  172. * memory "windows" as well.  (Note: Turbo C's free & farfree DO NOT
  173. * use this call).  Memory must have been allocated by DOS interrrupts
  174. * 0x48, 0xe7, or 0xe8; 0xe8 is used by createDataWindow routine here.
  175. * Deleting a window also deletes any child windows.
  176. *------------------------------------------------------------------------- */
  177. { /* regs, sregs, and LrgPtr are declared static to put them into common
  178.      DATA segment */
  179.   static union REGS regs;
  180.   static struct SREGS sregs;
  181.   /* Make life easier when dealing with segment and offset pointers. */
  182.   static union {void *A;
  183.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  184.  
  185.   LrgPtr.A = selector;
  186.   sregs.es = LrgPtr.Word.Segment; /* Offset doesn't matter */
  187.   regs.h.ah = 0x49; /* Delete segment or window */
  188.   intdosx(®s, ®s, &sregs);
  189.   if (regs.h.al EQ 7)
  190.     ERROR("deleteSegOrWin","Delete Segment; Bad Memory Map");
  191.   if (regs.h.al EQ 9)
  192.     ERROR("deleteSegOrWin","Delete Segment; Bad Selector");
  193. }
  194. /* ==================================================================== */
  195. void private *createDataWindow(char *base, uint32 length)
  196. /*---------------------------------------------------------------------------
  197. "createDataWindow" is a call to an Eclipse "extended service" routine,
  198. accessed through software interrupt E8, Function 01.
  199. ----------------------------------------------------------------------------*/
  200. { /* regs, sregs, and LrgPtr are declared static to put them into common
  201.      DATA segment */
  202.   static union REGS regs;
  203.   static struct SREGS sregs;
  204.   /* Make life easier when dealing with segment and offset pointers. */
  205.   static union {void *A;
  206.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  207.  
  208.   regs.h.ah = 0xe8;
  209.   regs.h.al=1; /* Create Data window */
  210.   /* si:bx=base; ds=parent selector */
  211.   LrgPtr.A = base;
  212.   sregs.ds = LrgPtr.Word.Segment;
  213.   regs.x.si = 0L; /* parent selector takes care of high-order base */
  214.   regs.x.bx = LrgPtr.Word.Offset;
  215.   regs.x.cx = (uint16)((length >> 16) & 0x0000ffff);  /* cx:dx=length in bytes */
  216.   regs.x.dx = (uint16)(length & 0x0000ffff);
  217.   intdosx(®s,®s,&sregs);
  218.   LrgPtr.Word.Segment = regs.x.ax; /* Selector or error */
  219.   LrgPtr.Word.Offset = 0;
  220.   if (LrgPtr.Word.Segment > MAXSEGNUM)
  221.     {/* We reserve a little work space at extreme of LDT table; if we've
  222.      encrouched upon it, back off: */
  223.      deleteSegOrWin(LrgPtr.A);
  224.      /* Table is "full" as far as we're concerned */
  225.      LrgPtr.Word.Segment = regs.x.ax = 21U;
  226.     }
  227.   if (regs.x.ax > 26U || regs.x.ax == 21U)
  228.     return (LrgPtr.A); /* Let caller handle "Descriptor Table Full" error */
  229.  
  230.   switch (regs.x.ax) {
  231.    case 9U:
  232.     ERROR("createDataWindow","Memory allocation; Bad Selector"); break;
  233.    case 20U:
  234.     ERROR("createDataWindow","Memory allocation; Bad Type"); break;
  235.    /* case 21U:  Handled by caller...
  236.     ERROR("createDataWindow","Memory allocation; Descriptor Table Full"); break;
  237.     */
  238.    case 23U:
  239.     ERROR("createDataWindow","Memory allocation; Need Local Descriptor"); break;
  240.    case 25U:
  241.     ERROR("createDataWindow","Memory allocation; Bad Base"); break;
  242.    case 26U:
  243.     ERROR("createDataWindow","Memory allocation; Bad Size"); break;
  244.    default:
  245.     ERROR("createDataWindow","Memory allocation; Unknown Error");break;
  246.   }
  247.   return(NULL); /* should never get here; quiet compiler warning */
  248. }
  249. /* =====================================================================*/
  250. void private deleteMultipleWindows(char *base, uint16 count)
  251. /*-----------------------------------------------------------------------
  252.  * Called with the pointer representing the first (lowest LDT) selector
  253.  * in a consecutive set of selectors to be deleted, and the count
  254.  * of the number of selectors in the set.
  255.  * See deleteSegOrWin for possible errors.
  256.  *----------------------------------------------------------------------*/
  257. { int i;
  258.   /* Make life easier when dealing with segment and offset pointers. */
  259.   static union {void *A;
  260.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  261.  
  262.   if (count == 0) ERROR("deleteMultipleWindows","zero count not valid");
  263.  
  264.   LrgPtr.A = base;
  265.   /* Undocumented Ergo recommendation for tiled windows: delete from
  266.      highest to lowest.  8 is increment from one LDT entry to next: */
  267.   LrgPtr.Word.Segment += (count - 1) * 8;
  268.   for (i=0; i < count; i++) {
  269.        deleteSegOrWin(LrgPtr.A);
  270.        LrgPtr.Word.Segment -= 8;
  271.   }
  272. }
  273. /* ==================================================================== */
  274. void private *allocateMultipleWindows(char *base, uint32 length)
  275. /*---------------------------------------------------------------------------
  276. "allocateMultipleWindows" is a call to an Eclipse "extended service" routine,
  277. accessed through software interrupt 0xEA.  It creates tiled windows,
  278. using 32K tiles.  The pointer for the first tile is returned.
  279. (The total number of tiles created is also known, but not returned
  280.  to the caller at this time.)
  281. ----------------------------------------------------------------------------*/
  282. { uint16 numSelectors;
  283.   /* regs, sregs, and LrgPtr are declared static to put them into common
  284.      DATA segment */
  285.   static union REGS regs;
  286.   static struct SREGS sregs;
  287.   /* Make life easier when dealing with segment and offset pointers. */
  288.   static union {void *A;
  289.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  290.  
  291.   regs.h.ah = 0xea; /* Allocate multiple windows */
  292.   /* si:bx and cx:dx are 32-bit offsets, not a paragraph address & offset */
  293.   /* si:bx = stride in bytes = 32K (32K makes for fast math,
  294.      64K has problems with Turbo C) */
  295.   /* Maximum legal value is 64K (si = 1, bx = 0) */
  296.   regs.x.si = 0;
  297.   regs.x.bx = 0x8000; /* 32K */
  298.   /* ds is parent selector */
  299.   LrgPtr.A = base;
  300.   sregs.ds = LrgPtr.Word.Segment; /*(uint16)((((uint32)base) >> 16) & 0x0000ffff);*/
  301.   /* cx:dx=length in bytes: */
  302.   regs.x.cx = (uint16)((length >> 16) & 0x0000ffff);
  303.   regs.x.dx = (uint16)(length & 0x0000ffff);
  304.   intdosx(®s,®s,&sregs);
  305.  
  306.   LrgPtr.Word.Segment = regs.x.ax; /* Selector or error */
  307.   LrgPtr.Word.Offset = 0;
  308.   if (regs.x.ax == 21U)
  309.     return (LrgPtr.A); /* Let caller handle "Descriptor Table Full" error */
  310.  
  311.   numSelectors = regs.x.bx;
  312.   /* 8 is increment between successive LDT entries: */
  313.   if ((LrgPtr.Word.Segment + ((numSelectors-1)*8) ) > MAXSEGNUM)
  314.     {/* We reserve a little work space at extreme of LDT table; if we've
  315.      encrouched upon it, back off: */
  316.      deleteMultipleWindows(LrgPtr.A,numSelectors);
  317.      /* Table is "full" as far as we're concerned */
  318.      LrgPtr.Word.Segment = 21U;
  319.      return (LrgPtr.A); /* Let caller handle "Descriptor Table Full" error */
  320.     }
  321.  
  322.   if (regs.x.ax > 26U) return (LrgPtr.A); /* Everything OK */
  323.  
  324.   switch (regs.x.ax) {
  325.    case 9U:
  326.     ERROR("allocateMultipleWindows","Memory allocation; Bad Selector"); break;
  327.    /* case 21U:  Handled by caller...
  328.     ERROR("allocateMultipleWindows","Memory allocation; Descriptor Table Full"); break;
  329.     */
  330.    case 23U:
  331.     ERROR("allocateMultipleWindows","Memory allocation; Need Local Descriptor"); break;
  332.    case 27U:
  333.     ERROR("allocateMultipleWindows","Memory allocation; Bad Stride"); break;
  334.   }
  335.   return(NULL); /* should never get here; quiet compiler warning */
  336. }
  337. /* ==================================================================== */
  338. void private *createBoundedWindow(char *base, uint32 length)
  339. /*---------------------------------------------------------------------------
  340. "createBoundedWindow" is used with the "pmalloc" and "pfree" routines for
  341. adding bounds-checking to dynamically-allocated objects.  It features an
  342. embedded loop of calls to Ergo's "extended DOS" function 0xED, "Get Segment
  343. or Window Information".
  344.  
  345. Besided dynamic allocation checking, this function can also be used for
  346. bounds-checking of static arrays and structures, such as constant strings.
  347. There is no centralized mechanism (see article).
  348. ----------------------------------------------------------------------------*/
  349. { /* regs, sregs, and LrgPtr are declared static to put them into common
  350.      DATA segment */
  351.   static union REGS regs;
  352.   /* Make life easier when dealing with segment and offset pointers. */
  353.   static union {void *A;
  354.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  355.   uint16 type;
  356.   uint32 offset = 0; /* Offset will accumulate total offset */
  357.  
  358.   TRACE(printf("Call to createBoundedWindow\n");)
  359.   /* Start with initial selector */
  360.   LrgPtr.A = base;
  361.   regs.x.bx = LrgPtr.Word.Segment;
  362.   /* Work up the inheritence tree until a non-WINDOW is found: */
  363.   while (TRUE) {
  364.     /* Next line must be within this loop, since ah is not preserved across
  365.        the subsequent intdos call: */
  366.     regs.h.ah = 0xed; /* Call to Get segment or window information */
  367.     intdos(®s,®s);
  368.     switch (regs.h.al) {
  369.     case 9U:
  370.       ERROR("createBoundedWindow","Memory allocation; Bad Selector");break;
  371.     case 23U:
  372.       ERROR("createBoundedWindow","Memory allocation; Need Local Descriptor");
  373.       break;
  374.     }
  375.     type = regs.h.al;
  376.     if (type == REALSEGMENT OR type == REALWINDOW)
  377.       ERROR("createBoundedWindow","Real segment or window found");
  378.     /* if ERROR, we will not continue here */
  379.     /* Assert: type is either SEGMENT or WINDOW  */
  380.     /* Length in bytes = cx:dx  */
  381.     length = (((uint32)regs.x.cx) << 16) + (uint32)regs.x.dx;
  382.     if (type != WINDOW) break; /* from while */
  383.     /* For WINDOW type, di has it's parent selector: */
  384.     regs.x.bx = regs.x.di;
  385.     /* For WINDOW type, si:bx is the 32 bit offset within the parent: */
  386.     offset += (((uint32)regs.x.si) << 16) + (uint32)regs.x.bx;
  387.   }
  388.   /* For SEGMENT type, si:bx is the 32-bit linear address of the base of
  389.      the segment. */
  390.   /* Add accumulated offset as well */
  391.   base = (char *)((uint32)regs.x.si << 16) + (uint32)regs.x.bx + offset;
  392.  
  393.   /* Now we have the underlying selector:offset location; next,
  394.      build a new window of just the right size on top of it. */
  395.  
  396.   if (length > 0x0000ffff)
  397.     /* If length is greater than 64K, use tiling with 32K windows */
  398.     return(allocateMultipleWindows(base,length));
  399.   else
  400.     return(createDataWindow(base,length));
  401. }
  402. /*===========================================================================*/
  403. void private checksize(uint32 size)
  404. /*----------------------------------------------------------------------------
  405. * Unfortunately, Turbo C/Ergo's version of malloc ( & calloc & realloc)
  406. * delivers a block with a header of 8 bytes (i.e., returns with an offset of
  407. * 0x0008), so the full 64K is not available.  If we don't check for this,
  408. * block descriptor header could be overwritten... disaster!
  409. -----------------------------------------------------------------------------*/
  410. {
  411.   if (size > 0x0000fff7)
  412.     ERROR("checksize","Attempt to allocate more than 64K - 8 bytes");
  413. }
  414. /*========================================================================*/
  415. void private markPtoQ (void *p, uint32 bytes, void *q)
  416. /*--------------------------------------------------------------------------
  417.  * This utility routine is used by the allocators to mark the
  418.  * correspondence between p and q
  419.  *-------------------------------------------------------------------------*/
  420. { /* Make life easier when dealing with segment and offset pointers. */
  421.   static union {void *A;
  422.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  423.  
  424.   void *ptile;
  425.   uint32 qspot;
  426. /* 32K: */
  427. #define TILESIZE 0x00010000
  428.  
  429.   LrgPtr.A = q;
  430.   /* Lower 3 bits are ALWAYS ON; so we can restore them later: */
  431.   /* Shift to make array smaller, and its length <= LDTSIZE */
  432.   qspot = (LrgPtr.Word.Segment & ~7) >> 3;
  433.   if (bytes > 0x0000ffff) {
  434.     /* Tiling is indicated by multiple adjacent entries with the
  435.        same value of p: */
  436.     while (bytes > TILESIZE) {
  437.       malloclist[qspot++] = p; /* Remember the p-q correspondence*/
  438.       bytes = bytes - TILESIZE;
  439.     }
  440.     /* Conclude with last partial tile below */
  441.   }
  442.   malloclist[qspot] = p; /* Remember the p-q correspondence*/
  443.   TRACE(printf("index: %u ",(LrgPtr.Word.Segment & ~7) >> 3);)
  444.   TRACE(printf("returns 0x%lx\n",q);)
  445. }
  446. /* =========================================================================*/
  447. void *pmalloc(uint16 bytes)
  448. {
  449.   void *p;
  450.   void *p2;
  451.   void *q;
  452.   /* Make life easier when dealing with segment and offset pointers. */
  453.   static union {void *A;
  454.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  455.  
  456.   TRACE(printf("[pmalloc] want: %u, ",bytes);)
  457.   checksize((uint32)bytes);
  458.   p = malloc(bytes);
  459.  
  460.   if (p EQ NULL) ERROR("pmalloc","Out of memory space on heap");
  461.  
  462.   LrgPtr.A = q = createBoundedWindow(p,bytes);
  463.   if (LrgPtr.Word.Segment == 21U)
  464.     {/* The local descriptor table is full; "fail soft" by
  465.        foregoing the pleasure of bounds checking.  When freeing occurs,
  466.        the decision about whether the pointer refers to a bounds-checking
  467.        data window like q or just a direct alloc like p is made by viewing the
  468.        offset.  A data window always has a zero offset.  The odds are
  469.        1 in 64K that p has a zero offset. */
  470.     if (((uint32)p & 0x0000ffff) == 0)
  471.       {/* We have to make sure that p doesn't have a zero offset! */
  472.     p2 = malloc((uint16)(bytes & 0x0000ffff));
  473.     /* If we don't succeed next time, periodicity suggests we won't
  474.       succeed n times.  Just complain and fail */
  475.     if (((uint32)p2 & 0x0000ffff) == 0)
  476.       ERROR("pmalloc","Unlikely memory error");
  477.  
  478.     free(p);
  479.     p = p2;
  480.       }
  481.     TRACE(printf("returns 0x%lx\n",p);)
  482.     return(p);
  483.   }
  484.   markPtoQ(p, (uint32)bytes, q);
  485.   return(q);
  486. }
  487. /* =========================================================================*/
  488. void *pcalloc(uint16 nitems, uint16 bytes)
  489. {
  490.   void *p;
  491.   void *p2;
  492.   void *q;
  493.   /* Make life easier when dealing with segment and offset pointers. */
  494.   static union {void *A;
  495.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  496.  
  497.   TRACE(printf("[pcalloc] want: %u * %u = %lu, ",
  498.     nitems,bytes,(uint32)nitems*(uint32)bytes);)
  499.   checksize((uint32)nitems*(uint32)bytes); /*Could be way over 64K - 8 bytes */
  500.   p = calloc(nitems,bytes);
  501.  
  502.   if (p EQ NULL) ERROR("pcalloc","Out of memory space on heap");
  503.  
  504.   LrgPtr.A = q = createBoundedWindow(p,(uint32)nitems*(uint32)bytes);
  505.   if (LrgPtr.Word.Segment == 21U)
  506.     {/* The local descriptor table is full; "fail soft" by
  507.        foregoing the pleasure of bounds checking.  When freeing occurs,
  508.        the decision about whether the pointer refers to a bounds-checking
  509.        data window like q or just a direct alloc like p is made by viewing the
  510.        offset.  A data window always has a zero offset.  The odds are
  511.        1 in 64K that p has a zero offset. */
  512.     if (((uint32)p & 0x0000ffff) == 0)
  513.       {/* We have to make sure that p doesn't have a zero offset! */
  514.     p2 = calloc(nitems,bytes);
  515.     /* If we don't succeed next time, periodicity suggests we won't
  516.       succeed n times.  Just complain and fail */
  517.     if (((uint32)p2 & 0x0000ffff) == 0) ERROR("pcalloc","Unlikely memory error");
  518.  
  519.     free(p);
  520.     p = p2;
  521.       }
  522.     TRACE(printf("returns 0x%lx\n",p);)
  523.     return(p);
  524.   }
  525.  
  526.   markPtoQ(p, (uint32)bytes, q);
  527.   return(q);
  528. }
  529. /*=========================================================================*/
  530. void *prealloc(void *block, uint16 newsize)
  531. { void *p;
  532.   void *p2;
  533.   void *q;
  534.   int16 i;
  535.   /* Make life easier when dealing with segment and offset pointers. */
  536.   static union {void *A;
  537.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  538.  
  539.   TRACE(printf(" [prealloc] with: 0xlx, want: %u, ",block,newsize);)
  540.   if (block == NULL) return(NULL);
  541.  
  542.   checksize((uint32)newsize);
  543.   LrgPtr.A = block;
  544.   if (LrgPtr.Word.Offset != 0)
  545.      /* Assume no bounds protection for this allocation */
  546.      return(realloc(block,newsize));
  547.  
  548.   /* Lower 3 bits of a selector are ALWAYS ON: */
  549.   i = (LrgPtr.Word.Segment & ~7) >> 3;
  550.   TRACE(printf("index: %u ",i);)
  551.   if (malloclist[i] == NULL)
  552.     ERROR("prealloc","Attempt to reallocate unknown window");
  553.  
  554.   if (newsize == 0)
  555.     {deleteSegOrWin(block); /* First remove bounds-checking window */
  556.      realloc(malloclist[i],newsize); /* Should return NULL */
  557.      malloclist[i] = NULL; /* Prevent screw ups */
  558.      TRACE(printf("returns NULL\n");)
  559.      return(NULL);
  560.      }
  561.  
  562.   p = realloc(malloclist[i],newsize); /* adjust memory */
  563.   if (p == NULL)
  564.     {TRACE(printf("returns NULL\n");)
  565.     return(NULL); /* Couldn't do it */
  566.     }
  567.  
  568.   malloclist[i] = NULL; /* Prevent screw ups */
  569.   /* Since realloc may change pointer location as well as size, we'll
  570.      just throw away the old bounds window and get a new one */
  571.   deleteSegOrWin(block);
  572.   LrgPtr.A = q = createBoundedWindow(p,newsize);
  573.   if (LrgPtr.Word.Segment == 21U)
  574.     {/* The local descriptor table is full; "fail soft" by
  575.        foregoing the pleasure of bounds checking.  When freeing occurs,
  576.        the decision about whether the pointer refers to a bounds-checking
  577.        data window like q or just a direct alloc like p is made by viewing the
  578.        offset.  A data window always has a zero offset.  The odds are
  579.        1 in 64K that p has a zero offset. */
  580.     if (((uint32)p & 0x0000ffff) == 0)
  581.       {/* We have to make sure that p doesn't have a zero offset! */
  582.     p2 = malloc(newsize);
  583.     /* If we don't succeed next time, periodicity suggests we won't
  584.       succeed n times.  Just complain and fail */
  585.     if (((uint32)p2 & 0x0000ffff) == 0) ERROR("prealloc","Unlikely memory error");
  586.  
  587.     free(p);
  588.     p = p2;
  589.       }
  590.     TRACE(printf("returns 0x%lx\n",p);)
  591.     return(p);
  592.   }
  593.  
  594.   markPtoQ(p, (uint32)newsize, q);
  595.   return(q);
  596. }
  597. /*==========================================================================*/
  598. void *pfarmalloc(uint32 bytes)
  599. {
  600.   void *p;
  601.   void *p2;
  602.   void *q;
  603.   /* Make life easier when dealing with segment and offset pointers. */
  604.   static union {void *A;
  605.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  606.  
  607.   TRACE(printf("[pfarmalloc] want: %lu, ",bytes);)
  608.   p = farmalloc(bytes);
  609.   if (p EQ NULL) ERROR("pfarmalloc","Out of memory space on heap");
  610.  
  611.   LrgPtr.A = q = createBoundedWindow(p,bytes);
  612.   if (LrgPtr.Word.Segment == 21U)
  613.     {/* The local descriptor table is full; "fail soft" by
  614.        foregoing the pleasure of bounds checking.  When freeing occurs,
  615.        the decision about whether the pointer refers to a bounds-checking
  616.        data window like q or just a direct alloc like p is made by viewing the
  617.        offset.  A data window always has a zero offset.  The odds are
  618.        1 in 64K that p has a zero offset. */
  619.     if (((uint32)p & 0x0000ffff) == 0)
  620.       {/* We have to make sure that p doesn't have a zero offset! */
  621.     p2 = farmalloc(bytes);
  622.     /* If we don't succeed next time, periodicity suggests we won't
  623.       succeed n times.  Just complain and fail */
  624.     if (((uint32)p2 & 0x0000ffff) == 0)
  625.       ERROR("pfarmalloc","Unlikely memory error");
  626.  
  627.     free(p);
  628.     p = p2;
  629.       }
  630.     TRACE(printf("returns 0x%lx\n",p);)
  631.     return(p);
  632.   }
  633.   markPtoQ(p, bytes, q);
  634.   return(q);
  635. }
  636. /*==========================================================================*/
  637. void *pfarcalloc(uint32 nitems, uint32 bytes)
  638. {
  639.   void *p;
  640.   void *p2;
  641.   void *q;
  642.   /* Make life easier when dealing with segment and offset pointers. */
  643.   static union {void *A;
  644.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  645.  
  646.   TRACE(printf("[pfarcalloc] want: %lu * %lu = %lu, ",nitems,bytes,nitems*bytes);)
  647.   /*  We won't check to see if (nitems * bytes) overflows uint32;
  648.       we'll let farcalloc do that job */
  649.   p = farcalloc(nitems, bytes);
  650.   if (p EQ NULL) ERROR("pfarcalloc","Out of memory space on heap");
  651.  
  652.   LrgPtr.A = q = createBoundedWindow(p,nitems*bytes);
  653.   if (LrgPtr.Word.Segment == 21U)
  654.     {/* The local descriptor table is full; "fail soft" by
  655.        foregoing the pleasure of bounds checking.  When freeing occurs,
  656.        the decision about whether the pointer refers to a bounds-checking
  657.        data window like q or just a direct alloc like p is made by viewing the
  658.        offset.  A data window always has a zero offset.  The odds are
  659.        1 in 64K that p has a zero offset. */
  660.     if (((uint32)p & 0x0000ffff) == 0)
  661.       {/* We have to make sure that p doesn't have a zero offset! */
  662.     p2 = farcalloc(nitems, bytes);
  663.     /* If we don't succeed next time, periodicity suggests we won't
  664.       succeed n times.  Just complain and fail */
  665.     if (((uint32)p2 & 0x0000ffff) == 0)
  666.       ERROR("pfarmalloc","Unlikely memory error");
  667.  
  668.     free(p);
  669.     p = p2;
  670.       }
  671.     TRACE(printf("returns 0x%lx\n",p);)
  672.     return(p);
  673.   }
  674.   markPtoQ(p, bytes, q);
  675.   return(q);
  676. }
  677.  
  678. /*=========================================================================*/
  679. void *pfarrealloc(void *block, uint32 newsize)
  680. { void *p;
  681.   void *p2;
  682.   void *q;
  683.   int16 i;
  684.   /* Make life easier when dealing with segment and offset pointers. */
  685.   static union {void *A;
  686.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  687.  
  688.   TRACE(printf("[pfarrealloc] with 0x%lx, want: %lu, ",block, newsize);)
  689.   if (block EQ NULL) return(NULL);
  690.  
  691.   LrgPtr.A = block;
  692.   if (LrgPtr.Word.Offset != 0)
  693.      /* Assume no bounds protection for this allocation */
  694.      return(farrealloc(block,newsize));
  695.  
  696.   /* Lower 3 bits of a selector are ALWAYS ON: */
  697.   i = (LrgPtr.Word.Segment & ~7) >> 3;
  698.   TRACE(printf("index: %u ",i);)
  699.   if (malloclist[i] == NULL)
  700.     ERROR("pfarrealloc","Attempt to reallocate unknown window");
  701.  
  702.   if (newsize == 0)
  703.     {deleteSegOrWin(block); /* First remove bounds-checking window */
  704.      farrealloc(malloclist[i],newsize); /* Should return NULL */
  705.      malloclist[i] = NULL; /* Prevent screw ups */
  706.      TRACE(printf("returns NULL\n");)
  707.      return(NULL);
  708.      }
  709.  
  710.   p = (void *)farrealloc(malloclist[i],newsize); /* adjust memory */
  711.   if (p == NULL)
  712.     {TRACE(printf("returns NULL\n");)
  713.      return(NULL); /* Couldn't do it */
  714.      }
  715.  
  716.   malloclist[i] = NULL; /* Prevent screw ups */
  717.   /* Since realloc may change pointer location as well as size, we'll
  718.      just throw away the old bounds window and get a new one */
  719.   deleteSegOrWin(block);
  720.   LrgPtr.A = q = createBoundedWindow(p,newsize);
  721.   if (LrgPtr.Word.Segment == 21U)
  722.     {/* The local descriptor table is full; "fail soft" by
  723.        foregoing the pleasure of bounds checking.  When freeing occurs,
  724.        the decision about whether the pointer refers to a bounds-checking
  725.        data window like q or just a direct alloc like p is made by viewing the
  726.        offset.  A data window always has a zero offset.  The odds are
  727.        1 in 64K that p has a zero offset. */
  728.     if (((int32)p & 0x0000ffff) == 0)
  729.       {/* We have to make sure that p doesn't have a zero offset! */
  730.     p2 = farmalloc(newsize);
  731.     /* If we don't succeed next time, periodicity suggests we won't
  732.       succeed n times.  Just complain and fail */
  733.     if (((int32)p2 & 0x0000ffff) == 0) ERROR("pfarrealloc","Unlikely memory error");
  734.  
  735.     free(p);
  736.     p = p2;
  737.       }
  738.     TRACE(printf("returns 0x%lx\n",p);)
  739.     return(p);
  740.   }
  741.   markPtoQ(p, newsize, q);
  742.   return(q);
  743. }
  744. /*===========================================================================*/
  745. void pfree(void *q)
  746. /*----------------------------------------------------------------------------
  747. "pfree" is called with a selector q, which is either an Ergo data window
  748. pointer used for bounds checking, or (less routinely) simply the pointer
  749. returned directly by malloc.  Examining the low 16 bits determines
  750. which.  For a window, we look up the stored value of the corresponding
  751. malloc pointer (which is a selector, not the real physical address), and
  752. q's slot in the Local Descriptor Table is freed for use by subsequent
  753. allocations.
  754.  
  755. In any event, Turbo C's "free" is called with the malloc pointer.
  756. --------------------------------------------------------------------------- */
  757.   {uint16 i;
  758.    void *p;
  759.  
  760.   /* Make life easier when dealing with segment and offset pointers. */
  761.   static union {void *A;
  762.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  763.  
  764.   TRACE(printf("[pfree] with: 0x%lx, ",q);)
  765.   if (q EQ NULL) return;
  766.  
  767.   LrgPtr.A = q;
  768.   if (LrgPtr.Word.Offset != 0)
  769.      /* Assume no bounds protection for this allocation */
  770.     {free(q); /* free memory */
  771.      TRACE(printf("\n");)
  772.      return;
  773.      }
  774.   /* Lower 3 bits of a selector are ALWAYS ON: */
  775.   i = (LrgPtr.Word.Segment & ~7) >> 3;
  776.   TRACE(printf("index: %u\n",i);)
  777.   if ((p = malloclist[i]) == NULL)
  778.     ERROR("pfree","Attempt to free unknown window");
  779.  
  780.  /* First remove bounds-checking window(s): */
  781.   while (p == malloclist[i]) {
  782.     deleteSegOrWin(LrgPtr.A);
  783.     LrgPtr.Word.Segment++; /* q may be made of multiple tiles */
  784.     malloclist[i++] = NULL; /* Prevent screw ups */
  785.   }
  786.   /* Turbo C's "free" call does not call DOS 0x49 (the equivalent of
  787.      deleteSegOrWin); if it did, then the deleteSegOrWin(q) call might
  788.      be unnecessary, since q is in some sense a child of malloclist[i].addr) */
  789.   free(p); /* free memory */
  790. }
  791. /*===========================================================================*/
  792. void pfarfree(void *q)
  793. /*----------------------------------------------------------------------------
  794. "pfarfree" is called with a selector q, which is either an Eclipse data window
  795. pointer used for bounds checking, or (less routinely) simply the pointer
  796. returned directly by farmalloc.  Examining the low 16 bits determines
  797. which.  For a window, we look up the stored value of the corresponding
  798. farmalloc pointer (which is a selector, not the real physical address), and
  799. q's slot in the Local Descriptor Table is freed for use by subsequent
  800. allocations.
  801.  
  802. In any event, Turbo C's "farfree" is called with the farmalloc pointer.
  803. --------------------------------------------------------------------------- */
  804.   {uint16 i;
  805.    void *p;
  806.  
  807.   /* Make life easier when dealing with segment and offset pointers. */
  808.   static union {void *A;
  809.      struct {uint16 Offset,Segment;} Word; } LrgPtr;
  810.  
  811.   TRACE(printf("[pfarfree] with: 0x%lx, ",q);)
  812.   if (q EQ NULL) return;
  813.  
  814.   LrgPtr.A = q;
  815.   if (LrgPtr.Word.Offset != 0)
  816.      /* Assume no bounds protection for this allocation */
  817.     {farfree(q); /* free memory */
  818.      TRACE(printf("\n");)
  819.      return;
  820.      }
  821.   /* Lower 3 bits of a selector are ALWAYS ON: */
  822.   i = (LrgPtr.Word.Segment & ~7) >> 3;
  823.   TRACE(printf("index: %u\n",i);)
  824.   if ((p = malloclist[i]) == NULL)
  825.     ERROR("pfarfree","Attempt to free unknown window");
  826.  
  827.  /* First remove bounds-checking window(s): */
  828.   while (p == malloclist[i]) {
  829.     deleteSegOrWin(LrgPtr.A);
  830.     LrgPtr.Word.Segment++; /* q may be made of multiple tiles */
  831.     malloclist[i++] = NULL; /* Prevent screw ups */
  832.   }
  833.   /* Turbo C's "free" call does not call DOS 0x49 (the equivalent of
  834.      deleteSegOrWin); if it did, then the deleteSegOrWin(q) call might
  835.      be unnecessary, since q is in some sense a child of malloclist[i].addr) */
  836.   farfree(p); /* free memory */
  837. }
  838. /*==========================================================================*/
  839.  
  840.  
  841.  
  842.