home *** CD-ROM | disk | FTP | other *** search
/ Virtual Reality Homebrewer's Handbook / vr.iso / vr386 / emmfsppt.c < prev    next >
C/C++ Source or Header  |  1996-03-19  |  13KB  |  513 lines

  1.  
  2. /* EMM memory support: all algoritims, math and assembly by Dave Stampe */
  3. /* MAP-EMM Copyright 26/12/93 by Dave Stampe  */
  4.  
  5. /* Written by  Dave Stampe, December 1993 */
  6.  
  7. /* Contact:  dstampe@sunee.waterloo.edu */
  8.  
  9. /*
  10.  This code is part of the VR-386 project, created by Dave Stampe.
  11.  VR-386 is a desendent of REND386, created by Dave Stampe and
  12.  Bernie Roehl.  Almost all the code has been rewritten by Dave
  13.  Stampre for VR-386.
  14.  
  15.  Copyright (c) 1994 by Dave Stampe:
  16.  May be freely used to write software for release into the public domain
  17.  or for educational use; all commercial endeavours MUST contact Dave Stampe
  18.  (dstampe@psych.toronto.edu) for permission to incorporate any part of
  19.  this software or source code into their products!  Usually there is no
  20.  charge for under 50-100 items for low-cost or shareware products, and terms
  21.  are reasonable.  Any royalties are used for development, so equipment is
  22.  often acceptable payment.
  23.  
  24.  ATTRIBUTION:  If you use any part of this source code or the libraries
  25.  in your projects, you must give attribution to VR-386 and Dave Stampe,
  26.  and any other authors in your documentation, source code, and at startup
  27.  of your program.  Let's keep the freeware ball rolling!
  28.  
  29.  DEVELOPMENT: VR-386 is a effort to develop the process started by
  30.  REND386, improving programmer access by rewriting the code and supplying
  31.  a standard API.  If you write improvements, add new functions rather
  32.  than rewriting current functions.  This will make it possible to
  33.  include you improved code in the next API release.  YOU can help advance
  34.  VR-386.  Comments on the API are welcome.
  35.  
  36.  CONTACT: dstampe@psych.toronto.edu
  37. */
  38.  
  39.  
  40. // an EMM memory manager utility
  41.  
  42. // this one HAS a block-based free() function
  43.  
  44.  
  45. #include <alloc.h>
  46. #include <dos.h>
  47. #include <bios.h>
  48. #include <stdio.h>
  49. #include <stdlib.h>
  50.  
  51. #include "xmem.h"    //  to check types
  52.  
  53.  
  54. //////////////////// EMM INTERFACE ////////////////
  55.  
  56. static unsigned EMMversion;
  57. static unsigned EMMhandle  = 0;    // careful to check before using!
  58. static unsigned EMMsegment;
  59. static unsigned EMMtotpages;
  60. static unsigned EMMfreepages;
  61.  
  62. #define EMM 0x67
  63. #define EMM_STATUS   0x40
  64. #define EMM_SEG      0x41
  65. #define EMM_NPAGES   0x42
  66. #define EMM_ALLOC    0x43
  67. #define EMM_MAP      0x44
  68. #define EMM_FREE     0x45
  69. #define EMM_VERSION  0x46
  70. #define EMM_MAPALL   0x5000
  71.  
  72. static union  REGS  regs;
  73. static struct SREGS sregs;
  74.  
  75. static int EMMsetup()
  76. {
  77.  unsigned i;
  78.  
  79.  char *v = (char far *) _dos_getvect(EMM);
  80.  v =  MK_FP(FP_SEG(v), 10);
  81.  if(strnicmp(v , "EMMXXXX0", 8) ) return 0;   // no EMM!
  82.  
  83.  regs.h.ah = EMM_STATUS;
  84.  int86(EMM,®s,®s);
  85.  if(regs.h.ah) return 0;    // no driver
  86.  
  87.  regs.h.ah = EMM_VERSION;
  88.  int86(EMM,®s,®s);
  89.  EMMversion = regs.h.al;
  90.  if(regs.h.ah) return 0;
  91.  if(EMMversion<0x40) return 0;    // not version 4.0!
  92.  
  93.  regs.h.ah = EMM_SEG;
  94.  int86(EMM,®s,®s);
  95.  EMMsegment = regs.x.bx;
  96.  if(regs.h.ah) return 0;
  97.  
  98.  regs.h.ah = EMM_NPAGES;
  99.  int86(EMM,®s,®s);
  100.  EMMtotpages  = regs.x.dx;
  101.  EMMfreepages = regs.x.bx;
  102.  if(regs.h.ah) return 0;
  103.  
  104.  return EMMfreepages;   // return free EMM, 0 is error
  105. }
  106.  
  107.  
  108. int EMMavail()        // number of free pages
  109. {
  110.  regs.h.ah = EMM_NPAGES;
  111.  int86(EMM,®s,®s);
  112.  EMMtotpages  = regs.x.dx;
  113.  EMMfreepages = regs.x.bx;
  114.  if(regs.h.ah) return 0;     // 0 = none/error
  115.  
  116.  return EMMfreepages;
  117. }
  118.  
  119.  
  120. static unsigned EMMalloc(unsigned pages)    // grab some pages
  121. {
  122.  regs.h.ah = EMM_ALLOC;
  123.  regs.x.bx = pages;
  124.  int86(EMM,®s,®s);
  125.  EMMhandle  = regs.x.dx;
  126.  return regs.h.ah;        // return nonzero if error
  127. }
  128.  
  129.  
  130. static int EMMfree()        // drop the pages we got
  131. {
  132.  if(EMMhandle==0) return -1;    // none allocated!
  133.  
  134.  regs.h.ah = EMM_FREE;
  135.  regs.x.dx = EMMhandle;
  136.  int86(EMM,®s,®s);
  137.  return regs.h.ah;        // return nonzero if error
  138. }
  139.  
  140.               // map page p to window part w
  141.               // one 16K page only (not used)
  142. static int EMMmap(unsigned p_page, unsigned w_page)
  143. {
  144.  regs.h.ah = EMM_MAP;
  145.  regs.h.al = w_page;
  146.  regs.x.bx = p_page;
  147.  int86(EMM,®s,®s);
  148.  return regs.h.ah;        // return nonzero if error
  149. }
  150.  
  151. static unsigned map[8] = {0,0,1,1,2,2,3,3};    // even bytes are pages
  152.  
  153. static int allEMMmap(int start)    // map WHOLE window at once!
  154. {
  155.  map[0] = start;                // initialize map table
  156.  map[2] = start+1;
  157.  map[4] = start+2;
  158.  map[6] = start+3;
  159.  
  160.  regs.x.ax = EMM_MAPALL;        // map all 4 parts of window at once
  161.  regs.x.dx = EMMhandle;
  162.  regs.x.cx = 4;
  163.  regs.x.si = FP_OFF(map);
  164.  sregs.ds  = FP_SEG(map);
  165.  int86x(EMM, ®s, ®s, &sregs);
  166.  return regs.h.ah;        // return nonzero if error
  167. }
  168.  
  169.  
  170. ////////////////// MEMORY BLOCK MANAGER SYSTEM /////////////////
  171. ///
  172. // idea: each byte in a table represents fixed block of memory (512 bytes)
  173. // each byte is offset to next, with the MSB reserved as a "free" flag
  174. // So we have a maximum memory allocation size of 127 blocks (63.5K).
  175.  
  176. // free: simply sets "free" flag.  An additional step is to check if the
  177. // next block in the chain is free too, and combine them if the resultant
  178. // block is small enough.
  179.  
  180. // alloc: searches for free block by incrementing pointer by byte values
  181. // when a free block is found, it either slices a chunk off it, or if it
  182. // is too small, checks to see if the next blocks are free to make a new
  183. // large block.  If not, it finds the next free block and continues.
  184.  
  185.  
  186. #define FAILS 0xFFFF      // returned if alloc fails
  187.  
  188. typedef unsigned char BYTE;
  189.  
  190. static BYTE *fbuf;        // the block table
  191.  
  192. static int size = 8192;   // records number of blocks;
  193.  
  194. #define INITM 64         // initial alloc units
  195.  
  196.  
  197. static reset_block_manager()
  198. {
  199.   if (fbuf) free(fbuf);
  200.   fbuf = NULL;
  201. }
  202.  
  203.  
  204. static int init_block_manager(int blocks)   // clears, sets up initial large blocks
  205. {
  206.  int i;
  207.  
  208.  size = blocks;
  209.  
  210.  fbuf = malloc(size);
  211.  if(fbuf==NULL) return -1;
  212.  
  213.  for(i=0;i<blocks;i++) fbuf[i] = 0xff;
  214.  
  215.  for(i=0;i<blocks-INITM;i+=INITM)
  216.    {
  217.      fbuf[i] = INITM | 128;
  218.    }
  219.  if(i<blocks) fbuf[i] = (blocks - i) | 128;
  220.  
  221.  return 0;
  222. }
  223.  
  224.  
  225. static unsigned scan_free_blocks()    // returns total free blocks
  226. {
  227.  unsigned s = 0;
  228.  unsigned i = 0;
  229.  
  230.  while(i<size)
  231.    {
  232.      if ((fbuf[i]&128)==128) s += fbuf[i] & 0x7F;
  233.      i += fbuf[i] & 0x7F;
  234.    }
  235.  return s;
  236. }
  237.  
  238.  
  239. static unsigned scan_used_blocks()    // returns total blocks allocated
  240. {
  241.  unsigned s = 0;
  242.  unsigned i = 0;
  243.  
  244.  while(i<size)
  245.    {
  246.      if ((fbuf[i]&128)==0) s += fbuf[i];
  247.      i += fbuf[i] & 0x7F;
  248.    }
  249.  return s;
  250. }
  251.  
  252.  
  253. static void free_blocks(unsigned s)    // frees memory, compacts with next
  254. {
  255.   unsigned i, p;
  256.  
  257.   i = fbuf[s];              // record for tests
  258.   fbuf[s] = i | 0x80;            // that's it!
  259.               // now compact a wee bit
  260.   if(fbuf[s+i] & 0x80)                  // is next free too?
  261.     {
  262.       p = fbuf[i+s] & 0x7F;        // size of next
  263.       if (i+p<120)            // block would be too big: forget it
  264.     {
  265.       fbuf[s] = 0x80 | (p+i);    // else make one big block
  266.     }
  267.     }
  268. }
  269.  
  270.  
  271.  
  272. unsigned alloc_blocks(unsigned s)    // allocate memory. See description
  273. {
  274.   unsigned p;        // block remainder
  275.   unsigned i;        // search ptr
  276.   unsigned sacc;     // aggregate accumulator
  277.   unsigned saccptr;  // aggregate start
  278.  
  279.  
  280.   if(s>127 || s==0) return FAILS;    // block too big
  281.  
  282.   i = 0;
  283.   while(i<size)             // scan till end
  284.     {
  285.       if( (fbuf[i]&0x80)==0 )
  286.     {
  287.       i += fbuf[i];  // block allocated: move on
  288.     }
  289.       else if( (fbuf[i]&0x7F) >= s)      // big enough?
  290.     {
  291.       p = (fbuf[i]&0x7F) - s;              // how much left
  292.       if(p)    fbuf[i+s] = p | 128;    // new free block
  293.       fbuf[i] = s;              // alloc block
  294.       return i;
  295.     }
  296.       else                     // scan to see if contiguous free
  297.     {
  298.       saccptr = i;                    // record start
  299.       sacc = fbuf[i] & 0x7F;    // record size to accum
  300.       while(i<size)
  301.         {
  302.           i += fbuf[i] & 0x7F;    // next
  303.           if(fbuf[i] & 0x80)        // free?
  304.         {
  305.           sacc += fbuf[i] & 0x7F;  // add to total
  306.           if(sacc>=s)           // got enough?
  307.             {
  308.               p = sacc - s;                     // how much left
  309.               if(p) fbuf[saccptr+s] = p | 128;  // free block
  310.               fbuf[saccptr] = s;           // alloc block
  311.               return saccptr;
  312.             }
  313.         }
  314.           else
  315.         {
  316.           i = saccptr + sacc;    // we hit a used block! keep looking
  317.           break;
  318.         }
  319.         }
  320.     }
  321.     }
  322.  return FAILS;        // got to end w/o block
  323. }
  324.  
  325.  
  326.  
  327.  
  328. ///////////////////// EMM POINTER ACCESS SYSTEM ////////////////
  329. ///  uses a composite pointer, with the segment and offset
  330. ///  fiddled to contain a page number.  Handles up to 4 Megs
  331.  
  332. int EMM_active = 0;            // is EMM system operating?
  333.  
  334. static long EMMpages;        // pages allocated
  335. static long EMMblocksfree;  // pages allocated
  336. static long EMMblocks;      // 512-byte blocks allocated
  337.  
  338.  
  339. static unsigned allocseg = 0xCF00;  // will contain "magic" segment base
  340. static unsigned allocoff = 0;        // will conain "magic" offset base
  341.  
  342. long alloccount = 0;         // EMM blocks used <DEBUG>
  343.  
  344.  
  345. int initEMMalloc(long wanted)    // inits system arg is desired pages
  346. {                // returns actual pages available
  347.   unsigned avp;
  348.  
  349.   fprintf(stderr,"MAP-EMM Expanded memory access system for VR-386\n");
  350.   fprintf(stderr,"   Copyright (c) 1994 by Dave Stampe\n");
  351.   if(EMMsetup()==0)
  352.     {
  353.       fprintf(stderr,"No EMM driver found!\n");
  354.       return 0;
  355.     }
  356.   fprintf(stderr,"EMM driver version: %d.%d\n", EMMversion>>4, EMMversion & 0x0f);
  357.  
  358.   if(wanted>256) wanted = 256;    // can only handle 256 pages with coding
  359.   avp = EMMavail();        // take what we can get
  360.   if(wanted>avp) wanted = avp;
  361.  
  362.   EMMpages = wanted;       // total pages
  363.   EMMblocks = wanted<<5;   // total blocks
  364.   EMMblocksfree = EMMblocks;
  365.  
  366.   if(init_block_manager(EMMblocks) ||    // startup blocks
  367.       EMMalloc(wanted)  )
  368.     {
  369.       fprintf(stderr,"Error: cannot allocate EMM memory!\n");
  370.       return NULL;
  371.     }
  372.   fprintf(stderr,"%d pages (%ldK) available, allocating %ldK\n",avp, ((long)avp)<<4, wanted<<4);
  373.  
  374.   allEMMmap(0);            // reset EMM map
  375.  
  376.   allocseg = EMMsegment - 0x0100;  // bump segment down so we can use 8 LSB
  377.   allocoff = 0x1000;           // compensates for bump-down
  378.  
  379.   EMM_active = 1;
  380.   return wanted;      // returns actual pages granted
  381. }
  382.  
  383.  
  384.  
  385. void resetEMMalloc()    // shut down system
  386. {
  387.   EMMfree();
  388.   reset_block_manager();    // reset manager
  389.   EMMblocks = 0;
  390.   EMM_active = 0;
  391. }
  392.  
  393.  
  394.             // allocates memory, makes up "magic" pointer to it
  395. void *EMMallocp(long n)
  396. {
  397.   unsigned page, offset;
  398.   long block;
  399.  
  400.   if(!EMM_active) return NULL;
  401.  
  402.   if(n>48000L) return NULL;      // cannot access more than 48K for sure
  403.  
  404. //  n += 20000; //    STRESS TEST
  405.  
  406.   n = (n+511)>>9;        // bump up to block size
  407.   block = alloc_blocks(n);
  408.   if(block==FAILS) return NULL;      // not enough blocks!
  409.   EMMblocksfree -= n;
  410.  
  411.   page = block>>5;        // compute page, offset
  412.   offset = (block<<9) & 0x00003FFF;
  413.  
  414.   alloccount++;
  415.  
  416.   allEMMmap(page);    // map into window
  417.  
  418.   return MK_FP(allocseg | page,
  419.            allocoff + offset - (page<<4) );  // make a pointer into page
  420. }
  421.  
  422.  
  423. void EMMfreep(void *p)
  424. {
  425.   long page, offset;
  426.  
  427.   page = FP_SEG(p) & 0xFF;
  428.   offset = FP_OFF(p) - 0x1000 + page<<4 + page<<14;  // true EMM address
  429.   free_blocks(offset>>9);
  430. }
  431.  
  432.  
  433.  
  434.  
  435. // WARNING: the access routine checks the external map! if you do any
  436. // non-mapped access, you gotta reset it!  Use this routine:
  437.  
  438. void restoreEMM()
  439. {
  440.   if(!EMM_active) return;
  441.   allEMMmap(0);        // restore if external EMM mapping used
  442. }
  443.  
  444.  
  445.  
  446. //////////////////////////////////////////////////////////
  447. ///  REND386 interface
  448.  
  449.  
  450. int accessptr(void *p)    // makes pointer accessible up to 60K
  451. {
  452.   unsigned page;
  453.  
  454.   if(!EMM_active) return 0;
  455.   if(FP_SEG(p) < allocseg) return 0;    // not in EMM
  456.  
  457.   page = FP_SEG(p) & 0x00FF;         // extract EMM page
  458.  
  459.   if (page!=map[0])         // is it accessible now?
  460.     {
  461.       allEMMmap(page);      // map WHOLE window at once!
  462.       return page+1;      // had to map it.
  463.     }
  464.  
  465.   return 0;                        // no mapping required
  466. }
  467.  
  468.  
  469.  
  470.         // the REND386 call: alloc from EMM if possible
  471.         // else do the usual way from DOS
  472. void *extmalloc(long n)
  473. {
  474.   void *p = NULL;
  475.  
  476.   if(EMM_active) p = EMMallocp(n);   // try to get EMM
  477.   if(p) return p;
  478.  
  479.   return malloc(n);      // if not, use MALLOC
  480. }
  481.  
  482.  
  483.         // frees memory: tests to see if EMM, else
  484.         // does usual heap free
  485. void extfree(void *p)
  486. {
  487.   if(EMM_active!=0 && FP_SEG(p)>=allocseg)
  488.     {
  489.       EMMfreep(p);
  490.       return;        // NO EMM FREE YET!
  491.     }
  492.   else
  493.     {
  494.       if(FP_SEG(p)<1)
  495.     {
  496.       fprintf(stderr,"ERROR: ATTEMPT TO FREE NULL POINTER!\n");
  497.       exit(0);
  498.     }
  499.       free(p);
  500.     }
  501. }
  502.  
  503.  
  504. long EMMheapsize()    // EMM left
  505. {
  506.   return EMMblocksfree<<9;
  507. }
  508.  
  509. long EMMheapused()    // EMM used so far
  510. {
  511.   return (EMMblocks-EMMblocksfree)<<9;
  512. }
  513.