]> 4ch.mooo.com Git - 16.git/blobdiff - src/lib/16_mm.c
started to work on mm libery~
[16.git] / src / lib / 16_mm.c
index 79bb851e2791039c27638767a01ef46e36af3834..52fdf8e9d385ddb3fec5c2b110713d1933dac320 100644 (file)
-/* Catacomb Armageddon Source Code
- * Copyright (C) 1993-2014 Flat Rock Software
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-// NEWMM.C
-
-/*
-=============================================================================
-
-                       ID software memory manager
-                       --------------------------
-
-Primary coder: John Carmack
-
-RELIES ON
----------
-Quit (char *error) function
-
-
-WORK TO DO
-----------
-MM_SizePtr to change the size of a given pointer
-
-Multiple purge levels utilized
-
-EMS / XMS unmanaged routines
-
-=============================================================================
-*/
-
-#include "src/lib/16_mm.h"
-
-/*
-=============================================================================
-
-                                                       LOCAL INFO
-
-=============================================================================
-*/
-
-#define LOCKBIT                0x80    // if set in attributes, block cannot be moved
-#define PURGEBITS      3               // 0-3 level, 0= unpurgable, 3= purge first
-#define PURGEMASK      0xfffc
-#define BASEATTRIBUTES 0       // unlocked, non purgable
-
-#define MAXUMBS                10
-
-typedef struct mmblockstruct
-{
-       unsigned        start,length;
-       unsigned        attributes;
-       memptr          *useptr;        // pointer to the segment start
-       struct mmblockstruct far *next;
-} mmblocktype;
-
-
-//#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!");mmfree=mmfree->next;}
-#define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}
-#define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}
-
-/*
-=============================================================================
-
-                                                GLOBAL VARIABLES
-
-=============================================================================
-*/
-
-mminfotype     mminfo;
-memptr         bufferseg;
-boolean                mmerror;
-
-void           (* beforesort) (void);
-void           (* aftersort) (void);
-
-/*
-=============================================================================
-
-                                                LOCAL VARIABLES
-
-=============================================================================
-*/
-
-boolean                mmstarted;
-
-void huge      *hugeheap;
-void far       *farheap;
-void           *nearheap;
-
-mmblocktype    far mmblocks[MAXBLOCKS]
-                       ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;
-
-boolean                bombonerror;
-
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// NEWMM.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+                       ID software memory manager\r
+                       --------------------------\r
+\r
+Primary coder: John Carmack\r
+\r
+RELIES ON\r
+---------\r
+Quit (char *error) function\r
+\r
+\r
+WORK TO DO\r
+----------\r
+MM_SizePtr to change the size of a given pointer\r
+\r
+Multiple purge levels utilized\r
+\r
+EMS / XMS unmanaged routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+#include "src/lib/16_mm.h"\r
+
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+mminfotype     mminfo;\r
+memptr         bufferseg;\r
+boolean                mmerror;\r
+\r
+void           (* beforesort) (void);\r
+void           (* aftersort) (void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean                mmstarted;\r
+\r
+void far       *farheap;\r
+void           *nearheap;\r
+\r
+mmblocktype    far mmblocks[MAXBLOCKS]\r
+                       ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;\r
+\r
+boolean                bombonerror;\r
+\r
 unsigned       totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;
-
-void           (* XMSaddr) (void);             // far pointer to XMS driver
-
+unsigned int EMSVer;\r
+\r
+void           (* XMSaddr) (void);             // far pointer to XMS driver\r
+\r
 unsigned       numUMBs,UMBbase[MAXUMBS];
 
-
-/*
-======================
-=
-= MML_CheckForEMS
-=
-= Routine from p36 of Extending DOS
-=
-=======================
-*/
-
-boolean MML_CheckForEMS (void)
-{
-       boolean emmcfems;
-       char    emmname[] = "EMMXXXX0";
-//             mov     dx,OFFSET emmname
-       __asm {
-               LEA     DX, emmname     //fix by andrius4669
-               mov     ax,0x3d00
-               int     0x21            // try to open EMMXXXX0 device
-               jc      error
-
-               mov     bx,ax
-               mov     ax,0x4400
-
-               int     0x21            // get device info
-               jc      error
-
-               and     dx,0x80
-               jz      error
-
-               mov     ax,0x4407
-
-               int     0x21            // get status
-               jc      error
-               or      al,al
-               jz      error
-
-               mov     ah,0x3e
-               int     0x21            // close handle
-               jc      error
-               //
-               // EMS is good
-               //
-               mov     emmcfems,1
-               jmp End
-               error:
-               //
-               // EMS is bad
-               //
-               mov     emmcfems,0
-               End:
-       }
-       return(emmcfems);
-}
-
-
-/*
-======================
-=
-= MML_SetupEMS
-=
-=======================
-*/
-
-void MML_SetupEMS (void)
-{
-       char    str[80],str2[10];
-       unsigned        err;
-       boolean errorflag=false;
-       union REGS CPURegs;
-
-       totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;
-
-       __asm
-               {
-               mov     ah,EMS_STATUS
-               int     EMS_INT                                         // make sure EMS hardware is present
-               or      ah,ah
-               jnz     error
-
-               mov     ah,EMS_VERSION
-               int     EMS_INT
-               or      ah,ah
-               jnz     error
-               cmp     al,0x32                                         // only work on ems 3.2 or greater
-               jb      error
-
-               mov     ah,EMS_GETFRAME
-               int     EMS_INT                                         // find the page frame address
-               or      ah,ah
-               jnz     error
-               mov     [EMSpageframe],bx
-
-               mov     ah,EMS_GETPAGES
-               int     EMS_INT                                         // find out how much EMS is there
-               or      ah,ah
-               jnz     error
-               mov     [totalEMSpages],dx
-               mov     [freeEMSpages],bx
-               or      bx,bx
-               jz      noEMS                                           // no EMS at all to allocate
-
-               cmp     bx,4
-               jle     getpages                                        // there is only 1,2,3,or 4 pages
-               mov     bx,4                                            // we can't use more than 4 pages
-
-getpages:
-               mov     [EMSpagesmapped],bx
-               mov     ah,EMS_ALLOCPAGES                       // allocate up to 64k of EMS
-               int     EMS_INT
-               or      ah,ah
-               jnz     error
-               mov     [EMShandle],dx
-               jmp End
-error:
-               mov     errorflag,1
-               jmp End
-noEMS:
-End:
-       }
-       if(errorflag==true)
-       {
-               err = CPURegs.h.ah;
-               strcpy(str,"MML_SetupEMS: EMS error 0x");
-               itoa(err,str2,16);
-               strcpy(str,str2);
-               printf("%s\n",str);
-       }
-}
-
-
-/*
-======================
-=
-= MML_ShutdownEMS
-=
-=======================
-*/
-
-void MML_ShutdownEMS (void)
-{
-       boolean errorflag=false;
-       if (!EMShandle)
-               return;
-       __asm
-       {
-               mov     ah,EMS_FREEPAGES
-               mov     dx,[EMShandle]
-               int     EMS_INT
-               or      ah,ah
-               jz      ok
-               mov     errorflag,1
-               ok:
-       }
-       if(errorflag==true) printf("MML_ShutdownEMS: Error freeing EMS!");      //++++ add something
-}
-
-/*
-====================
-=
-= MM_MapEMS
-=
-= Maps the 64k of EMS used by memory manager into the page frame
-= for general use.  This only needs to be called if you are keeping
-= other things in EMS.
-=
-====================
-*/
-
-void MM_MapEMS (void)
-{
-       char    str[80],str2[10];
-       unsigned        err;
-       boolean errorflag=false;
-       int     i;
-       union REGS CPURegs;
-
-       for (i=0;i<EMSpagesmapped;i++)
-       {
-               __asm
-               {
-                       mov     ah,EMS_MAPPAGE
-                       mov     bx,[i]                  // logical page
-                       mov     al,bl                   // physical page
-                       mov     dx,[EMShandle]  // handle
-                       int     EMS_INT
-                       or      ah,ah
-                       jnz     error
-                       jmp End
-                       error:
-                       mov     errorflag,1
-                       End:
-               }
-               if(errorflag==true)
-               {
-                       err = CPURegs.h.ah;
-                       strcpy(str,"MM_MapEMS: EMS error 0x");
-                       itoa(err,str2,16);
-                       strcpy(str,str2);
-                       printf("%s\n",str);
-               }
-       }
-       return;
-}
-
-//==========================================================================
-
-/*
-======================
-=
-= MML_CheckForXMS
-=
-= Check for XMM driver
-=
-=======================
-*/
-
-boolean MML_CheckForXMS (void)
-{
-       boolean errorflag=false;
-       numUMBs = 0;
-
-       __asm
-       {
-               mov     ax,0x4300
-               int     0x2f                            // query status of installed diver
-               cmp     al,0x80
-               je      good
-               mov     errorflag,1
-               good:
-       }
-       if(errorflag==true) return false;
-       else return true;
-}
-
-
-/*
-======================
-=
-= MML_SetupXMS
-=
-= Try to allocate all upper memory block
-=
-=======================
-*/
-
-void MML_SetupXMS (void)
-{
-       unsigned        base,size;
-
-       __asm
-       {
-               mov     ax,0x4310
-               int     0x2f
-               mov     [WORD PTR XMSaddr],bx
-               mov     [WORD PTR XMSaddr+2],es         // function pointer to XMS driver
-       }
-getmemory:
-       __asm
-       {
-               mov     ah,XMS_ALLOCUMB
-               mov     dx,0xffff                                       // try for largest block possible
-               call    [DWORD PTR XMSaddr]
-               or      ax,ax
-               jnz     gotone
-
-               cmp     bl,0xb0                                         // error: smaller UMB is available
-               jne     done;
-
-               mov     ah,XMS_ALLOCUMB
-               call    [DWORD PTR XMSaddr]             // DX holds largest available UMB
-               or      ax,ax
-               jz      done                                            // another error...
-
-gotone:
-               mov     [base],bx
-               mov     [size],dx
-done:
-       }
-       MML_UseSpace (base,size);
-       mminfo.XMSmem += size*16;
-       UMBbase[numUMBs] = base;
-       numUMBs++;
-       if (numUMBs < MAXUMBS)
-               goto getmemory;
-}
-
-
-/*
-======================
-=
-= MML_ShutdownXMS
-=
-======================
-*/
-
-void MML_ShutdownXMS (void)
-{
-       int     i;
-       unsigned        base;
-
-       for (i=0;i<numUMBs;i++)
-       {
-               base = UMBbase[i];
-               __asm
-               {
-                       mov     ah,XMS_FREEUMB
-                       mov     dx,[base]
-                       call    [DWORD PTR XMSaddr]
-               }
-       }
-}
-
-//==========================================================================
-
-/*
-======================
-=
-= MML_UseSpace
-=
-= Marks a range of paragraphs as usable by the memory manager
-= This is used to mark space for the near heap, far heap, ems page frame,
-= and upper memory blocks
-=
-======================
-*/
-
-void MML_UseSpace (unsigned segstart, unsigned seglength)
-{
-       mmblocktype far *scan,far *last;
-       unsigned        oldend;
-       long            extra;
-
-       scan = last = mmhead;
-       mmrover = mmhead;               // reset rover to start of memory
-
-//
-// search for the block that contains the range of segments
-//
-       while (scan->start+scan->length < segstart)
-       {
-               last = scan;
-               scan = scan->next;
-       }
-
-//
-// take the given range out of the block
-//
-       oldend = scan->start + scan->length;
-       extra = oldend - (segstart+seglength);
-       if (extra < 0)
-       {
-               printf("MML_UseSpace: Segment spans two blocks!");
-               return;
-       }
-               
-
-       if (segstart == scan->start)
-       {
-               last->next = scan->next;                        // unlink block
-               FREEBLOCK(scan);
-               scan = last;
-       }
-       else
-               scan->length = segstart-scan->start;    // shorten block
-
-       if (extra > 0)
-       {
-               GETNEWBLOCK;
-               mmnew->next = scan->next;
-               scan->next = mmnew;
-               mmnew->start = segstart+seglength;
-               mmnew->length = extra;
-               mmnew->attributes = LOCKBIT;
-       }
-
-}
-
-//==========================================================================
-
-/*
-====================
-=
-= MML_ClearBlock
-=
-= We are out of blocks, so free a purgable block
-=
-====================
-*/
-
-void MML_ClearBlock (void)
-{
-       mmblocktype far *scan,far *last;
-
-       scan = mmhead->next;
-
-       while (scan)
-       {
-               if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )
-               {
-                       MM_FreePtr(scan->useptr);
-                       return;
-               }
-               scan = scan->next;
-       }
-
-       printf("MM_ClearBlock: No purgable blocks!");
-}
-
-
-//==========================================================================
-
-/*
-===================
-=
-= MM_Startup
-=
-= Grabs all space from turbo with malloc/farmalloc
-= Allocates bufferseg misc buffer
-=
-===================
-*/
-
 static char *ParmStringsexmm[] = {"noems","noxms",""};
-
-void MM_Startup (void)
-{
-       int i;
-       dword length;
-       void far        *start;
-       unsigned        segstart,seglength,endfree;
-
-       if (mmstarted)
-               MM_Shutdown ();
-
-
-       mmstarted = true;
-       bombonerror = true;
-//
-// set up the linked list (everything in the free list;
-//
-       mmhead = NULL;
-       mmfree = &mmblocks[0];
-       for (i=0;i<MAXBLOCKS-1;i++)
-               mmblocks[i].next = &mmblocks[i+1];
-       mmblocks[i].next = NULL;
-
-//
-// locked block of all memory until we punch out free space
-//
-       GETNEWBLOCK;
-       mmhead = mmnew;                         // this will allways be the first node
-       mmnew->start = 0;
-       mmnew->length = 0xffff;
-       mmnew->attributes = LOCKBIT;
-       mmnew->next = NULL;
-       mmrover = mmhead;
-
-
-//
-// get all available near conventional memory segments
-//
-//---- length=coreleft();
-       length=_memavl();
-       start = (void far *)(nearheap = malloc(length));
-
-       length -= 16-(FP_OFF(start)&15);
-       length -= SAVENEARHEAP;
-       seglength = length / 16;                        // now in paragraphs
-       segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
-       MML_UseSpace (segstart,seglength);
-       mminfo.nearheap = length;
-
-//
-// get all available far conventional memory segments
-//
-//---- length=farcoreleft();
-       length=_memmax();
-       start = farheap = _fmalloc(length);
-       length -= 16-(FP_OFF(start)&15);
-       length -= SAVEFARHEAP;
-       seglength = length / 16;                        // now in paragraphs
-       segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
-       MML_UseSpace (segstart,seglength);
-       mminfo.farheap = length;
-       mminfo.mainmem = mminfo.nearheap + mminfo.farheap;
-
-
-//
-// detect EMS and allocate up to 64K at page frame
-//
-       mminfo.EMSmem = 0;
-       for (i = 1;i < __argc;i++)
-       {
-               if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)
-                       goto emsskip;                           // param NOEMS
-       }
-
-       if (MML_CheckForEMS())
-       {
-               MML_SetupEMS();                                 // allocate space
-               MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);
-               MM_MapEMS();                                    // map in used pages
-               mminfo.EMSmem = EMSpagesmapped*0x4000l;
-       }
-
-//
-// detect XMS and get upper memory blocks
-//
-emsskip:
-       mminfo.XMSmem = 0;
-       for (i = 1;i < __argc;i++)
-       {
-               if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)
-                       goto xmsskip;                           // param NOXMS
-       }
-
-       if (MML_CheckForXMS())
-               MML_SetupXMS();                                 // allocate as many UMBs as possible
-
-//
-// allocate the misc buffer
-//
-xmsskip:
-       mmrover = mmhead;               // start looking for space after low block
-
-       MM_GetPtr (&bufferseg,BUFFERSIZE);
-}
-
-//==========================================================================
-
-/*
-====================
-=
-= MM_Shutdown
-=
-= Frees all conventional, EMS, and XMS allocated
-=
-====================
-*/
-
-void MM_Shutdown (void)
-{
-  if (!mmstarted)
-       return;
-
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForEMS\r
+=\r
+= Routine from p36 of Extending DOS\r
+=\r
+=======================\r
+*/\r
+\r
+boolean MML_CheckForEMS (void)\r
+{\r
+       boolean emmcfems;\r
+       char    emmname[] = "EMMXXXX0";\r
+//             mov     dx,OFFSET emmname\r
+       __asm {\r
+               LEA     DX, emmname     //fix by andrius4669\r
+               mov     ax,0x3d00\r
+               int     0x21            // try to open EMMXXXX0 device\r
+               jc      error\r
+\r
+               mov     bx,ax\r
+               mov     ax,0x4400\r
+\r
+               int     0x21            // get device info\r
+               jc      error\r
+\r
+               and     dx,0x80\r
+               jz      error\r
+\r
+               mov     ax,0x4407\r
+\r
+               int     0x21            // get status\r
+               jc      error\r
+               or      al,al\r
+               jz      error\r
+\r
+               mov     ah,0x3e\r
+               int     0x21            // close handle\r
+               jc      error\r
+               //\r
+               // EMS is good\r
+               //\r
+               mov     emmcfems,1\r
+               jmp End\r
+               error:\r
+               //\r
+               // EMS is bad\r
+               //\r
+               mov     emmcfems,0\r
+               End:\r
+       }\r
+       return(emmcfems);\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupEMS (void)\r
+{\r
+       char    str[80],str2[10];\r
+       unsigned        err;\r
+       boolean errorflag=false;\r
+       union REGS CPURegs;\r
+\r
+       EMSVer = 0;\r
+       totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;\r
+\r
+       __asm\r
+               {\r
+               mov     ah,EMS_STATUS\r
+               int     EMS_INT                                         // make sure EMS hardware is present\r
+               or      ah,ah\r
+               jnz     error\r
+\r
+               mov     ah,EMS_VERSION\r
+               int     EMS_INT\r
+               or      ah,ah\r
+               jnz     error\r
+               mov     [EMSVer],ax                             //      set EMSVer\r
+               cmp     al,0x32                                         // only work on ems 3.2 or greater\r
+               jb      error\r
+\r
+               mov     ah,EMS_GETFRAME\r
+               int     EMS_INT                                         // find the page frame address\r
+               or      ah,ah\r
+               jnz     error\r
+               mov     [EMSpageframe],bx\r
+\r
+               mov     ah,EMS_GETPAGES\r
+               int     EMS_INT                                         // find out how much EMS is there\r
+               or      ah,ah\r
+               jnz     error\r
+               mov     [totalEMSpages],dx\r
+               mov     [freeEMSpages],bx\r
+               or      bx,bx\r
+               jz      noEMS                                           // no EMS at all to allocate\r
+\r
+               cmp     bx,4\r
+               jle     getpages                                        // there is only 1,2,3,or 4 pages\r
+               mov     bx,4                                            // we can't use more than 4 pages\r
+\r
+getpages:\r
+               mov     [EMSpagesmapped],bx\r
+               mov     ah,EMS_ALLOCPAGES                       // allocate up to 64k of EMS\r
+               int     EMS_INT\r
+               or      ah,ah\r
+               jnz     error\r
+               mov     [EMShandle],dx\r
+               jmp End\r
+error:\r
+               mov     errorflag,1\r
+               jmp End\r
+noEMS:\r
+End:\r
+       }\r
+       if(errorflag==true)\r
+       {\r
+               err = CPURegs.h.ah;\r
+               strcpy(str,"MML_SetupEMS: EMS error 0x");\r
+               itoa(err,str2,16);\r
+               strcpy(str,str2);\r
+               printf("%s\n",str);\r
+       }\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_ShutdownEMS (void)\r
+{\r
+       boolean errorflag=false;\r
+       if (!EMShandle)\r
+               return;\r
+       __asm\r
+       {\r
+               mov     ah,EMS_FREEPAGES\r
+               mov     dx,[EMShandle]\r
+               int     EMS_INT\r
+               or      ah,ah\r
+               jz      ok\r
+               mov     errorflag,1\r
+               ok:\r
+       }\r
+       if(errorflag==true) printf("MML_ShutdownEMS: Error freeing EMS!");      //++++ add something\r
+}\r
+\r
+/*\r
+====================\r
+=\r
+= MM_MapEMS\r
+=\r
+= Maps the 64k of EMS used by memory manager into the page frame\r
+= for general use.  This only needs to be called if you are keeping\r
+= other things in EMS.\r
+=\r
+====================\r
+*/\r
+\r
+void MM_MapEMS (void)\r
+{\r
+       char    str[80],str2[10];\r
+       unsigned        err;\r
+       boolean errorflag=false;\r
+       int     i;\r
+       union REGS CPURegs;\r
+\r
+       for (i=0;i<EMSpagesmapped;i++)\r
+       {\r
+               __asm\r
+               {\r
+                       mov     ah,EMS_MAPPAGE\r
+                       mov     bx,[i]                  // logical page\r
+                       mov     al,bl                   // physical page\r
+                       mov     dx,[EMShandle]  // handle\r
+                       int     EMS_INT\r
+                       or      ah,ah\r
+                       jnz     error\r
+                       jmp End\r
+                       error:\r
+                       mov     errorflag,1\r
+                       End:\r
+               }\r
+               if(errorflag==true)\r
+               {\r
+                       err = CPURegs.h.ah;\r
+                       strcpy(str,"MM_MapEMS: EMS error 0x");\r
+                       itoa(err,str2,16);\r
+                       strcpy(str,str2);\r
+                       printf("%s\n",str);\r
+               }\r
+       }\r
+       return;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForXMS\r
+=\r
+= Check for XMM driver\r
+=\r
+=======================\r
+*/\r
+\r
+boolean MML_CheckForXMS (void)\r
+{\r
+       boolean errorflag=false;\r
+       numUMBs = 0;\r
+\r
+       __asm\r
+       {\r
+               mov     ax,0x4300\r
+               int     0x2f                            // query status of installed diver\r
+               cmp     al,0x80\r
+               je      good\r
+               mov     errorflag,1\r
+               good:\r
+       }\r
+       if(errorflag==true) return false;\r
+       else return true;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupXMS\r
+=\r
+= Try to allocate all upper memory block\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupXMS (void)\r
+{\r
+       unsigned        base,size;\r
+\r
+       __asm\r
+       {\r
+               mov     ax,0x4310\r
+               int     0x2f\r
+               mov     [WORD PTR XMSaddr],bx\r
+               mov     [WORD PTR XMSaddr+2],es         // function pointer to XMS driver\r
+       }\r
+getmemory:\r
+       __asm\r
+       {\r
+               mov     ah,XMS_ALLOCUMB\r
+               mov     dx,0xffff                                       // try for largest block possible\r
+               call    [DWORD PTR XMSaddr]\r
+               or      ax,ax\r
+               jnz     gotone\r
+\r
+               cmp     bl,0xb0                                         // error: smaller UMB is available\r
+               jne     done;\r
+\r
+               mov     ah,XMS_ALLOCUMB\r
+               call    [DWORD PTR XMSaddr]             // DX holds largest available UMB\r
+               or      ax,ax\r
+               jz      done                                            // another error...\r
+\r
+gotone:\r
+               mov     [base],bx\r
+               mov     [size],dx\r
+done:\r
+       }\r
+       MML_UseSpace (base,size);\r
+       mminfo.XMSmem += size*16;\r
+       UMBbase[numUMBs] = base;\r
+       numUMBs++;\r
+       if (numUMBs < MAXUMBS)\r
+               goto getmemory;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownXMS\r
+=\r
+======================\r
+*/\r
+\r
+void MML_ShutdownXMS (void)\r
+{\r
+       int     i;\r
+       unsigned        base;\r
+\r
+       for (i=0;i<numUMBs;i++)\r
+       {\r
+               base = UMBbase[i];\r
+               __asm\r
+               {\r
+                       mov     ah,XMS_FREEUMB\r
+                       mov     dx,[base]\r
+                       call    [DWORD PTR XMSaddr]\r
+               }\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_UseSpace\r
+=\r
+= Marks a range of paragraphs as usable by the memory manager\r
+= This is used to mark space for the near heap, far heap, ems page frame,\r
+= and upper memory blocks\r
+=\r
+======================\r
+*/\r
+\r
+void MML_UseSpace (unsigned segstart, unsigned seglength)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+       unsigned        oldend;\r
+       long            extra;\r
+\r
+       scan = last = mmhead;\r
+       mmrover = mmhead;               // reset rover to start of memory\r
+\r
+//\r
+// search for the block that contains the range of segments\r
+//\r
+       while (scan->start+scan->length < segstart)\r
+       {\r
+               last = scan;\r
+               scan = scan->next;\r
+       }\r
+\r
+//\r
+// take the given range out of the block\r
+//\r
+       oldend = scan->start + scan->length;\r
+       extra = oldend - (segstart+seglength);\r
+       if (extra < 0)\r
+       {\r
+               printf("MML_UseSpace: Segment spans two blocks!");\r
+               return;\r
+       }\r
+\r
+\r
+       if (segstart == scan->start)\r
+       {\r
+               last->next = scan->next;                        // unlink block\r
+               FREEBLOCK(scan);\r
+               scan = last;\r
+       }\r
+       else\r
+               scan->length = segstart-scan->start;    // shorten block\r
+\r
+       if (extra > 0)\r
+       {\r
+               GETNEWBLOCK;\r
+               mmnew->next = scan->next;\r
+               scan->next = mmnew;\r
+               mmnew->start = segstart+seglength;\r
+               mmnew->length = extra;\r
+               mmnew->attributes = LOCKBIT;\r
+       }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MML_ClearBlock\r
+=\r
+= We are out of blocks, so free a purgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MML_ClearBlock (void)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+\r
+       scan = mmhead->next;\r
+\r
+       while (scan)\r
+       {\r
+               if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )\r
+               {\r
+                       MM_FreePtr(scan->useptr);\r
+                       return;\r
+               }\r
+               scan = scan->next;\r
+       }\r
+\r
+       printf("MM_ClearBlock: No purgable blocks!");\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= MM_Startup\r
+=\r
+= Grabs all space from turbo with malloc/farmalloc\r
+= Allocates bufferseg misc buffer\r
+=\r
+===================\r
+*/\r
+\r
+void MM_Startup (void)\r
+{\r
+       int i;\r
+       dword length;\r
+       void far        *start;\r
+       unsigned        segstart,seglength,endfree;\r
+\r
+       if (mminfo.mmstarted)\r
+               MM_Shutdown ();\r
+\r
+\r
+       mminfo.mmstarted = true;\r
+       mminfo.bombonerror = true;\r
+//\r
+// set up the linked list (everything in the free list;\r
+//\r
+       mmhead = NULL;\r
+       mmfree = &mmblocks[0];\r
+       for (i=0;i<MAXBLOCKS-1;i++)\r
+               mmblocks[i].next = &mmblocks[i+1];\r
+       mmblocks[i].next = NULL;\r
+\r
+//\r
+// locked block of all memory until we punch out free space\r
+//\r
+       GETNEWBLOCK;\r
+       mmhead = mmnew;                         // this will allways be the first node\r
+       mmnew->start = 0;\r
+       mmnew->length = 0xffff;\r
+       mmnew->attributes = LOCKBIT;\r
+       mmnew->next = NULL;\r
+       mmrover = mmhead;\r
+\r
+\r
+//\r
+// get all available near conventional memory segments\r
+//\r
+//---- length=coreleft();\r
+       _nheapgrow();\r
+       length=_memavl();\r
+       start = (void far *)(nearheap = malloc(length));\r
+\r
+       length -= 16-(FP_OFF(start)&15);\r
+       length -= SAVENEARHEAP;\r
+       seglength = length / 16;                        // now in paragraphs\r
+       segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+       MML_UseSpace (segstart,seglength);\r
+       mminfo.nearheap = length;\r
+\r
+//\r
+// get all available far conventional memory segments\r
+//\r
+//---- length=farcoreleft();\r
+       _fheapgrow();\r
+       length=_memavl();\r
+       start = farheap = _fmalloc(length);\r
+       length -= 16-(FP_OFF(start)&15);\r
+       length -= SAVEFARHEAP;\r
+       seglength = length / 16;                        // now in paragraphs\r
+       segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+       MML_UseSpace (segstart,seglength);\r
+       mminfo.farheap = length;\r
+       mminfo.mainmem = mminfo.nearheap + mminfo.farheap;\r
+\r
+\r
+//\r
+// detect EMS and allocate up to 64K at page frame\r
+//\r
+       mminfo.EMSmem = 0;\r
+       for (i = 1;i < __argc;i++)\r
+       {\r
+               if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)\r
+                       goto emsskip;                           // param NOEMS\r
+       }\r
+\r
+       if (MML_CheckForEMS())\r
+       {\r
+               //printf("EMS1\n");\r
+               MML_SetupEMS();                                 // allocate space\r
+               //printf("EMS2\n");\r
+               MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);\r
+               //printf("EMS3\n");\r
+               MM_MapEMS();                                    // map in used pages\r
+               //printf("EMS4\n");\r
+               mminfo.EMSmem = EMSpagesmapped*0x4000l;\r
+       }\r
+\r
+//\r
+// detect XMS and get upper memory blocks\r
+//\r
+emsskip:\r
+       mminfo.XMSmem = 0;\r
+       for (i = 1;i < __argc;i++)\r
+       {\r
+               if ( US_CheckParm(__argv[i],ParmStringsexmm) == 0)\r
+                       goto xmsskip;                           // param NOXMS\r
+       }\r
+\r
+       if (MML_CheckForXMS())\r
+       {\r
+//             printf("XMS!\n");\r
+//++++         MML_SetupXMS();                                 // allocate as many UMBs as possible\r
+       }\r
+\r
+//\r
+// allocate the misc buffer\r
+//\r
+xmsskip:\r
+       mmrover = mmhead;               // start looking for space after low block\r
+\r
+       MM_GetPtr (&bufferseg,BUFFERSIZE);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_Shutdown\r
+=\r
+= Frees all conventional, EMS, and XMS allocated\r
+=\r
+====================\r
+*/\r
+\r
+void MM_Shutdown (void)\r
+{\r
+  if (!mminfo.mmstarted)\r
+       return;\r
+\r
   _ffree (farheap);
+  printf("far freed\n");\r
   free (nearheap);
-  hfree(hugeheap);
+  printf("near freed\n");\r
+  //hfree(hugeheap);
+  printf("huge freed\n");\r
   MML_ShutdownEMS ();
-  MML_ShutdownXMS ();
-}
-
-//==========================================================================
-
-/*
-====================
-=
-= MM_GetPtr
-=
-= Allocates an unlocked, unpurgable block
-=
-====================
-*/
-
-void MM_GetPtr (memptr *baseptr,dword size)
-{
-       mmblocktype far *scan,far *lastscan,far *endscan
-                               ,far *purge,far *next;
-       int                     search;
-       unsigned        needed,startseg;
-
-       needed = (size+15)/16;          // convert size from bytes to paragraphs
-
-       GETNEWBLOCK;                            // fill in start and next after a spot is found
-       mmnew->length = needed;
-       mmnew->useptr = baseptr;
-       mmnew->attributes = BASEATTRIBUTES;
-
-       for (search = 0; search<3; search++)
-       {
-       //
-       // first search:        try to allocate right after the rover, then on up
-       // second search:       search from the head pointer up to the rover
-       // third search:        compress memory, then scan from start
-               if (search == 1 && mmrover == mmhead)
-                       search++;
-
-               switch (search)
-               {
-               case 0:
-                       lastscan = mmrover;
-                       scan = mmrover->next;
-                       endscan = NULL;
-                       break;
-               case 1:
-                       lastscan = mmhead;
-                       scan = mmhead->next;
-                       endscan = mmrover;
-                       break;
-               case 2:
-                       MM_SortMem ();
-                       lastscan = mmhead;
-                       scan = mmhead->next;
-                       endscan = NULL;
-                       break;
-               }
-
-               startseg = lastscan->start + lastscan->length;
-
-               while (scan != endscan)
-               {
-                       if (scan->start - startseg >= needed)
-                       {
-                       //
-                       // got enough space between the end of lastscan and
-                       // the start of scan, so throw out anything in the middle
-                       // and allocate the new block
-                       //
-                               purge = lastscan->next;
-                               lastscan->next = mmnew;
-                               mmnew->start = *(unsigned *)baseptr = startseg;
-                               mmnew->next = scan;
-                               while ( purge != scan)
-                               {       // free the purgable block
-                                       next = purge->next;
-                                       FREEBLOCK(purge);
-                                       purge = next;           // purge another if not at scan
-                               }
-                               mmrover = mmnew;
-                               return; // good allocation!
-                       }
-
-                       //
-                       // if this block is purge level zero or locked, skip past it
-                       //
-                       if ( (scan->attributes & LOCKBIT)
-                               || !(scan->attributes & PURGEBITS) )
-                       {
-                               lastscan = scan;
-                               startseg = lastscan->start + lastscan->length;
-                       }
-
-
-                       scan=scan->next;                // look at next line
-               }
-       }
-
-       if (bombonerror)
-               printf(OUT_OF_MEM_MSG,(size-mminfo.nearheap));
-       else
-               mmerror = true;
-}
-
-//==========================================================================
-
-/*
-====================
-=
-= MM_FreePtr
-=
-= Allocates an unlocked, unpurgable block
-=
-====================
-*/
-
-void MM_FreePtr (memptr *baseptr)
-{
-       mmblocktype far *scan,far *last;
-
-       last = mmhead;
-       scan = last->next;
-
-       if (baseptr == mmrover->useptr) // removed the last allocated block
-               mmrover = mmhead;
-
-       while (scan->useptr != baseptr && scan)
-       {
-               last = scan;
-               scan = scan->next;
-       }
-
-       if (!scan)
-       {
-               printf("MM_FreePtr: Block not found!");
-               return;
-       }
-
-       last->next = scan->next;
-
-       FREEBLOCK(scan);
-}
-//==========================================================================
-
-/*
-=====================
-=
-= MM_SetPurge
-=
-= Sets the purge level for a block (locked blocks cannot be made purgable)
-=
-=====================
-*/
-
-void MM_SetPurge (memptr *baseptr, int purge)
-{
-       mmblocktype far *start;
-
-       start = mmrover;
-
-       do
-       {
-               if (mmrover->useptr == baseptr)
-                       break;
-
-               mmrover = mmrover->next;
-
-               if (!mmrover)
-                       mmrover = mmhead;
-               else if (mmrover == start)
-               {
-                       printf("MM_SetPurge: Block not found!");
-                       return;
-               }
-
-       } while (1);
-
-       mmrover->attributes &= ~PURGEBITS;
-       mmrover->attributes |= purge;
-}
-
-//==========================================================================
-
-/*
-=====================
-=
-= MM_SetLock
-=
-= Locks / unlocks the block
-=
-=====================
-*/
-
-void MM_SetLock (memptr *baseptr, boolean locked)
-{
-       mmblocktype far *start;
-
-       start = mmrover;
-
-       do
-       {
-               if (mmrover->useptr == baseptr)
-                       break;
-
-               mmrover = mmrover->next;
-
-               if (!mmrover)
-                       mmrover = mmhead;
-               else if (mmrover == start)
-               {
-                       printf("MM_SetLock: Block not found!");
-                       return;
-               }
-
-       } while (1);
-
-       mmrover->attributes &= ~LOCKBIT;
-       mmrover->attributes |= locked*LOCKBIT;
-}
-
-//==========================================================================
-
-/*
-=====================
-=
-= MM_SortMem
-=
-= Throws out all purgable stuff and compresses movable blocks
-=
-=====================
-*/
-
-void MM_SortMem (void)
-{
-       mmblocktype far *scan,far *last,far *next;
-       unsigned        start,length,source,dest,oldborder;
-       int                     playing;
-
-       //
-       // lock down a currently playing sound
-       //
-/*++++ playing = SD_SoundPlaying ();
-       if (playing)
-       {
-               switch (SoundMode)
-               {
-               case sdm_PC:
-                       playing += STARTPCSOUNDS;
-                       break;
-               case sdm_AdLib:
-                       playing += STARTADLIBSOUNDS;
-                       break;
-               }
-               MM_SetLock(&(memptr)audiosegs[playing],true);
-       }
-
-
-       SD_StopSound();*/
-//     oldborder = bordercolor;
-//     VW_ColorBorder (15);
-
-       if (beforesort)
-               beforesort();
-
-       scan = mmhead;
-
-       last = NULL;            // shut up compiler warning
-
-       while (scan)
-       {
-               if (scan->attributes & LOCKBIT)
-               {
-               //
-               // block is locked, so try to pile later blocks right after it
-               //
-                       start = scan->start + scan->length;
-               }
-               else
-               {
-                       if (scan->attributes & PURGEBITS)
-                       {
-                       //
-                       // throw out the purgable block
-                       //
-                               next = scan->next;
-                               FREEBLOCK(scan);
-                               last->next = next;
-                               scan = next;
-                               continue;
-                       }
-                       else
-                       {
-                       //
-                       // push the non purgable block on top of the last moved block
-                       //
-                               if (scan->start != start)
-                               {
-                                       length = scan->length;
-                                       source = scan->start;
-                                       dest = start;
-                                       while (length > 0xf00)
-                                       {
-                                               movedata(source,0,dest,0,0xf00*16);
-                                               length -= 0xf00;
-                                               source += 0xf00;
-                                               dest += 0xf00;
-                                       }
-                                       movedata(source,0,dest,0,length*16);
-
-                                       scan->start = start;
-                                       *(unsigned *)scan->useptr = start;
-                               }
-                               start = scan->start + scan->length;
-                       }
-               }
-
-               last = scan;
-               scan = scan->next;              // go to next block
-       }
-
-       mmrover = mmhead;
-
-       if (aftersort)
-               aftersort();
-
-//     VW_ColorBorder (oldborder);
-
-/*++++ if (playing)
-               MM_SetLock(&(memptr)audiosegs[playing],false);*/
-}
-
-
-//==========================================================================
-
-//****#if 0
-/*
-=====================
-=
-= MM_ShowMemory
-=
-=====================
-*/
-
-void MM_ShowMemory (void)
-{
-       mmblocktype far *scan;
-       unsigned color,temp;
-       long    end,owner;
-       char    scratch[80],str[10];
-
-//**** VW_SetDefaultColors();
-//**** VW_SetLineWidth(40);
-//++++mh       temp = bufferofs;
-//++++mh       bufferofs = 0;
-//**** VW_SetScreen (0,0);
-
-       scan = mmhead;
-
-       end = -1;
-
-//CA_OpenDebug ();
-
-       while (scan)
-       {
-               if (scan->attributes & PURGEBITS)
-                       color = 5;              // dark purple = purgable
-               else
-                       color = 9;              // medium blue = non purgable
-               if (scan->attributes & LOCKBIT)
-                       color = 12;             // red = locked
-               if (scan->start<=end)
-               {
-                       printf("MM_ShowMemory: Memory block order currupted!");
-                       return;
-               }
-               end = scan->start+scan->length-1;
-//****         VW_Hlin(scan->start,(unsigned)end,0,color);
-//****         VW_Plot(scan->start,0,15);
-//****         if (scan->next->start > end+1)
-//****                 VW_Hlin(end+1,scan->next->start,0,0);   // black = free
-
-//****#if 0
-strcpy (scratch,"Size:");
-ltoa ((long)scan->length*16,str,10);
-strcat (scratch,str);
-strcat (scratch,"\tOwner:0x");
-owner = (unsigned)scan->useptr;
-ultoa (owner,str,16);
-strcat (scratch,str);
-strcat (scratch,"\n");
-//++++write (debughandle,scratch,strlen(scratch));
-printf("%s\n", scratch);
-//****#endif
-
-               scan = scan->next;
-       }
-
-//CA_CloseDebug ();
-
-//++++mh       IN_Ack();
-//**** VW_SetLineWidth(64);
-//++++mh       bufferofs = temp;
-}
-//****#endif
-
-//==========================================================================
-
-
-/*
-======================
-=
-= MM_UnusedMemory
-=
-= Returns the total free space without purging
-=
-======================
-*/
-
-long MM_UnusedMemory (void)
-{
-       unsigned free;
-       mmblocktype far *scan;
-
-       free = 0;
-       scan = mmhead;
-
-       while (scan->next)
-       {
-               free += scan->next->start - (scan->start + scan->length);
-               scan = scan->next;
-       }
-
-       return free*16l;
-}
-
-//==========================================================================
-
-
-/*
-======================
-=
-= MM_TotalFree
-=
-= Returns the total free space with purging
-=
-======================
-*/
-
-long MM_TotalFree (void)
-{
-       unsigned free;
-       mmblocktype far *scan;
-
-       free = 0;
-       scan = mmhead;
-
-       while (scan->next)
-       {
-               if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))
-                       free += scan->length;
-               free += scan->next->start - (scan->start + scan->length);
-               scan = scan->next;
-       }
-
-       return free*16l;
-}
-
-//==========================================================================
-
-/*
-=====================
-=
-= MM_BombOnError
-=
-=====================
-*/
-
-void MM_BombOnError (boolean bomb)
-{
-       bombonerror = bomb;
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//      US_CheckParm() - checks to see if a string matches one of a set of
-//              strings. The check is case insensitive. The routine returns the
-//              index of the string that matched, or -1 if no matches were found
-//
-///////////////////////////////////////////////////////////////////////////
-int
-US_CheckParm(char *parm,char **strings)
-{
-       char    cp,cs,
-                       *p,*s;
-       int             i;
-
-       while (!isalpha(*parm)) // Skip non-alphas
-               parm++;
-
-       for (i = 0;*strings && **strings;i++)
-       {
-               for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)
-               {
-                       cs = *s++;
-                       if (!cs)
-                               return(i);
-                       cp = *p++;
-
-                       if (isupper(cs))
-                               cs = tolower(cs);
-                       if (isupper(cp))
-                               cp = tolower(cp);
-               }
-       }
-       return(-1);
-}
-
+  printf("EMS freed\n");\r
+//++++  MML_ShutdownXMS ();
+  printf("XMS freed\n");\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_GetPtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_GetPtr (memptr *baseptr,dword size)\r
+{\r
+       mmblocktype far *scan,far *lastscan,far *endscan\r
+                               ,far *purge,far *next;\r
+       int                     search;\r
+       unsigned        needed,startseg;\r
+\r
+       needed = (size+15)/16;          // convert size from bytes to paragraphs\r
+\r
+       GETNEWBLOCK;                            // fill in start and next after a spot is found\r
+       mmnew->length = needed;\r
+       mmnew->useptr = baseptr;\r
+       mmnew->attributes = BASEATTRIBUTES;\r
+\r
+       for (search = 0; search<3; search++)\r
+       {\r
+       //\r
+       // first search:        try to allocate right after the rover, then on up\r
+       // second search:       search from the head pointer up to the rover\r
+       // third search:        compress memory, then scan from start\r
+               if (search == 1 && mmrover == mmhead)\r
+                       search++;\r
+\r
+               switch (search)\r
+               {\r
+               case 0:\r
+                       lastscan = mmrover;\r
+                       scan = mmrover->next;\r
+                       endscan = NULL;\r
+                       break;\r
+               case 1:\r
+                       lastscan = mmhead;\r
+                       scan = mmhead->next;\r
+                       endscan = mmrover;\r
+                       break;\r
+               case 2:\r
+                       MM_SortMem ();\r
+                       lastscan = mmhead;\r
+                       scan = mmhead->next;\r
+                       endscan = NULL;\r
+                       break;\r
+               }\r
+\r
+               startseg = lastscan->start + lastscan->length;\r
+\r
+               while (scan != endscan)\r
+               {\r
+                       if (scan->start - startseg >= needed)\r
+                       {\r
+                       //\r
+                       // got enough space between the end of lastscan and\r
+                       // the start of scan, so throw out anything in the middle\r
+                       // and allocate the new block\r
+                       //\r
+                               purge = lastscan->next;\r
+                               lastscan->next = mmnew;\r
+                               mmnew->start = *(unsigned *)baseptr = startseg;\r
+                               mmnew->next = scan;\r
+                               while ( purge != scan)\r
+                               {       // free the purgable block\r
+                                       next = purge->next;\r
+                                       FREEBLOCK(purge);\r
+                                       purge = next;           // purge another if not at scan\r
+                               }\r
+                               mmrover = mmnew;\r
+                               return; // good allocation!\r
+                       }\r
+\r
+                       //\r
+                       // if this block is purge level zero or locked, skip past it\r
+                       //\r
+                       if ( (scan->attributes & LOCKBIT)\r
+                               || !(scan->attributes & PURGEBITS) )\r
+                       {\r
+                               lastscan = scan;\r
+                               startseg = lastscan->start + lastscan->length;\r
+                       }\r
+\r
+\r
+                       scan=scan->next;                // look at next line\r
+               }\r
+       }\r
+\r
+       if (mminfo.bombonerror)\r
+               printf(OUT_OF_MEM_MSG,(size-mminfo.nearheap));\r
+       else\r
+               mminfo.mmerror = true;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_FreePtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_FreePtr (memptr *baseptr)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+\r
+       last = mmhead;\r
+       scan = last->next;\r
+\r
+       if (baseptr == mmrover->useptr) // removed the last allocated block\r
+               mmrover = mmhead;\r
+\r
+       while (scan->useptr != baseptr && scan)\r
+       {\r
+               last = scan;\r
+               scan = scan->next;\r
+       }\r
+\r
+       if (!scan)\r
+       {\r
+               printf("MM_FreePtr: Block not found!");\r
+               return;\r
+       }\r
+\r
+       last->next = scan->next;\r
+\r
+       FREEBLOCK(scan);\r
+}\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetPurge\r
+=\r
+= Sets the purge level for a block (locked blocks cannot be made purgable)\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetPurge (memptr *baseptr, int purge)\r
+{\r
+       mmblocktype far *start;\r
+\r
+       start = mmrover;\r
+\r
+       do\r
+       {\r
+               if (mmrover->useptr == baseptr)\r
+                       break;\r
+\r
+               mmrover = mmrover->next;\r
+\r
+               if (!mmrover)\r
+                       mmrover = mmhead;\r
+               else if (mmrover == start)\r
+               {\r
+                       printf("MM_SetPurge: Block not found!");\r
+                       return;\r
+               }\r
+\r
+       } while (1);\r
+\r
+       mmrover->attributes &= ~PURGEBITS;\r
+       mmrover->attributes |= purge;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetLock\r
+=\r
+= Locks / unlocks the block\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetLock (memptr *baseptr, boolean locked)\r
+{\r
+       mmblocktype far *start;\r
+\r
+       start = mmrover;\r
+\r
+       do\r
+       {\r
+               if (mmrover->useptr == baseptr)\r
+                       break;\r
+\r
+               mmrover = mmrover->next;\r
+\r
+               if (!mmrover)\r
+                       mmrover = mmhead;\r
+               else if (mmrover == start)\r
+               {\r
+                       printf("MM_SetLock: Block not found!");\r
+                       return;\r
+               }\r
+\r
+       } while (1);\r
+\r
+       mmrover->attributes &= ~LOCKBIT;\r
+       mmrover->attributes |= locked*LOCKBIT;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SortMem\r
+=\r
+= Throws out all purgable stuff and compresses movable blocks\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SortMem (void)\r
+{\r
+       mmblocktype far *scan,far *last,far *next;\r
+       unsigned        start,length,source,dest,oldborder;\r
+       int                     playing;\r
+\r
+       //\r
+       // lock down a currently playing sound\r
+       //\r
+/*++++ playing = SD_SoundPlaying ();\r
+       if (playing)\r
+       {\r
+               switch (SoundMode)\r
+               {\r
+               case sdm_PC:\r
+                       playing += STARTPCSOUNDS;\r
+                       break;\r
+               case sdm_AdLib:\r
+                       playing += STARTADLIBSOUNDS;\r
+                       break;\r
+               }\r
+               MM_SetLock(&(memptr)audiosegs[playing],true);\r
+       }\r
+\r
+\r
+       SD_StopSound();*/\r
+//     oldborder = bordercolor;\r
+//     VW_ColorBorder (15);\r
+\r
+       if (beforesort)\r
+               beforesort();\r
+\r
+       scan = mmhead;\r
+\r
+       last = NULL;            // shut up compiler warning\r
+\r
+       while (scan)\r
+       {\r
+               if (scan->attributes & LOCKBIT)\r
+               {\r
+               //\r
+               // block is locked, so try to pile later blocks right after it\r
+               //\r
+                       start = scan->start + scan->length;\r
+               }\r
+               else\r
+               {\r
+                       if (scan->attributes & PURGEBITS)\r
+                       {\r
+                       //\r
+                       // throw out the purgable block\r
+                       //\r
+                               next = scan->next;\r
+                               FREEBLOCK(scan);\r
+                               last->next = next;\r
+                               scan = next;\r
+                               continue;\r
+                       }\r
+                       else\r
+                       {\r
+                       //\r
+                       // push the non purgable block on top of the last moved block\r
+                       //\r
+                               if (scan->start != start)\r
+                               {\r
+                                       length = scan->length;\r
+                                       source = scan->start;\r
+                                       dest = start;\r
+                                       while (length > 0xf00)\r
+                                       {\r
+                                               movedata(source,0,dest,0,0xf00*16);\r
+                                               length -= 0xf00;\r
+                                               source += 0xf00;\r
+                                               dest += 0xf00;\r
+                                       }\r
+                                       movedata(source,0,dest,0,length*16);\r
+\r
+                                       scan->start = start;\r
+                                       *(unsigned *)scan->useptr = start;\r
+                               }\r
+                               start = scan->start + scan->length;\r
+                       }\r
+               }\r
+\r
+               last = scan;\r
+               scan = scan->next;              // go to next block\r
+       }\r
+\r
+       mmrover = mmhead;\r
+\r
+       if (aftersort)\r
+               aftersort();\r
+\r
+//     VW_ColorBorder (oldborder);\r
+\r
+/*++++ if (playing)\r
+               MM_SetLock(&(memptr)audiosegs[playing],false);*/\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+//****#if 0\r
+/*\r
+=====================\r
+=\r
+= MM_ShowMemory\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_ShowMemory (void)\r
+{\r
+       mmblocktype far *scan;\r
+       unsigned color,temp;//, i;\r
+       long    end,owner;\r
+       char    scratch[80],str[10];\r
+\r
+//**** VW_SetDefaultColors();\r
+//**** VW_SetLineWidth(40);\r
+//++++mh       temp = bufferofs;\r
+//++++mh       bufferofs = 0;\r
+//**** VW_SetScreen (0,0);\r
+\r
+       scan = mmhead;\r
+\r
+       end = -1;\r
+\r
+//CA_OpenDebug ();\r
+\r
+       while (scan)\r
+       {\r
+               if (scan->attributes & PURGEBITS)\r
+                       color = 5;              // dark purple = purgable\r
+               else\r
+                       color = 9;              // medium blue = non purgable\r
+               if (scan->attributes & LOCKBIT)\r
+                       color = 12;             // red = locked\r
+               if (scan->start<=end)\r
+               {\r
+                       printf("MM_ShowMemory: Memory block order currupted!");\r
+                       return;\r
+               }\r
+               end = scan->start+scan->length-1;\r
+//++++         VW_Hlin(scan->start,(unsigned)end,0,color);\r
+//++++         VW_Plot(scan->start,0,15);\r
+               if (scan->next->start > end+1)\r
+//++++                 VW_Hlin(end+1,scan->next->start,0,0);   // black = free\r
+\r
+//****#if 0\r
+printf("Location:");\r
+printf("%Fp\t", scan->start);\r
+strcpy (scratch,"Size:");\r
+ltoa ((long)scan->length*16,str,10);\r
+strcat (scratch,str);\r
+strcat (scratch,"\tOwner:0x");\r
+owner = (unsigned)scan->useptr;\r
+ultoa (owner,str,16);\r
+strcat (scratch,str);\r
+strcat (scratch,"\n");\r
+//++++write (debughandle,scratch,strlen(scratch));\r
+fprintf(stdout, "%s", scratch);\r
+//****#endif\r
+\r
+               scan = scan->next;\r
+       }\r
+\r
+//CA_CloseDebug ();\r
+\r
+//++++mh       IN_Ack();\r
+//**** VW_SetLineWidth(64);\r
+//++++mh       bufferofs = temp;\r
+}\r
+//****#endif\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_UnusedMemory\r
+=\r
+= Returns the total free space without purging\r
+=\r
+======================\r
+*/\r
+\r
+dword MM_UnusedMemory (void)\r
+{\r
+       unsigned free;\r
+       mmblocktype far *scan;\r
+\r
+       free = 0;\r
+       scan = mmhead;\r
+\r
+       while (scan->next)\r
+       {\r
+               free += scan->next->start - (scan->start + scan->length);\r
+               scan = scan->next;\r
+       }\r
+\r
+//     return free*16l;\r
+       return free;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_TotalFree\r
+=\r
+= Returns the total free space with purging\r
+=\r
+======================\r
+*/\r
+\r
+dword MM_TotalFree (void)\r
+{\r
+       unsigned free;\r
+       mmblocktype far *scan;\r
+\r
+       free = 0;\r
+       scan = mmhead;\r
+\r
+       while (scan->next)\r
+       {\r
+               if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))\r
+                       free += scan->length;\r
+               free += scan->next->start - (scan->start + scan->length);\r
+               scan = scan->next;\r
+       }\r
+\r
+//     return free*16l;\r
+       return free;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_Report\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_Report(void)\r
+{\r
+       printf("EMM %x available\n", EMSVer);\r
+       printf("totalEMSpages=%u\n", totalEMSpages);\r
+       printf("freeEMSpages=%u\n", freeEMSpages);\r
+       printf("EMSpageframe=%Fp\n", EMSpageframe);\r
+       printf("near=%lu\n", mminfo.nearheap);\r
+       printf("far=%lu\n", mminfo.farheap);\r
+       printf("EMSmem=%lu\n", mminfo.EMSmem);\r
+       printf("XMSmem=%lu\n", mminfo.XMSmem);\r
+       printf("mainmem=%lu\n", mminfo.mainmem);\r
+       printf("UnusedMemory=%lu\n", MM_UnusedMemory());\r
+       printf("TotalFree=%lu\n", MM_TotalFree());\r
+//     printf("\n");\r
+//     printf("UnusedMemory=%lu kb\n", MM_UnusedMemory()/10248);\r
+//     printf("TotalFree=%lu kb\n", MM_TotalFree()/10248);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_EMSVer\r
+=\r
+=====================\r
+\r
+\r
+int MM_EMSVer(void)\r
+{\r
+       int EMSver;\r
+       __asm\r
+       {\r
+               mov             ah,EMS_VERSION\r
+               int             EMS_INT\r
+               mov             EMSver,ax\r
+       }\r
+       return(EMSver);\r
+}*/\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_BombOnError\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_BombOnError (boolean bomb)\r
+{\r
+       mminfo.bombonerror = bomb;\r
+}\r
+\r
+//==========================================================================\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CheckParm() - checks to see if a string matches one of a set of\r
+//              strings. The check is case insensitive. The routine returns the\r
+//              index of the string that matched, or -1 if no matches were found\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+int\r
+US_CheckParm(char *parm,char **strings)\r
+{\r
+       char    cp,cs,\r
+                       *p,*s;\r
+       int             i;\r
+\r
+       while (!isalpha(*parm)) // Skip non-alphas\r
+               parm++;\r
+\r
+       for (i = 0;*strings && **strings;i++)\r
+       {\r
+               for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)\r
+               {\r
+                       cs = *s++;\r
+                       if (!cs)\r
+                               return(i);\r
+                       cp = *p++;\r
+\r
+                       if (isupper(cs))\r
+                               cs = tolower(cs);\r
+                       if (isupper(cp))\r
+                               cp = tolower(cp);\r
+               }\r
+       }\r
+       return(-1);\r
+}\r