]> 4ch.mooo.com Git - 16.git/blob - 16/cawat/16_mm.c
cf36d26c7a497360332439fb8b026000dc397715
[16.git] / 16 / cawat / 16_mm.c
1 /* Catacomb Armageddon Source Code\r
2  * Copyright (C) 1993-2014 Flat Rock Software\r
3  *\r
4  * This program is free software; you can redistribute it and/or modify\r
5  * it under the terms of the GNU General Public License as published by\r
6  * the Free Software Foundation; either version 2 of the License, or\r
7  * (at your option) any later version.\r
8  *\r
9  * This program is distributed in the hope that it will be useful,\r
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
12  * GNU General Public License for more details.\r
13  *\r
14  * You should have received a copy of the GNU General Public License along\r
15  * with this program; if not, write to the Free Software Foundation, Inc.,\r
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
17  */\r
18 \r
19 // NEWMM.C\r
20 \r
21 /*\r
22 =============================================================================\r
23 \r
24                         ID software memory manager\r
25                         --------------------------\r
26 \r
27 Primary coder: John Carmack\r
28 \r
29 RELIES ON\r
30 ---------\r
31 Quit (char *error) function\r
32 \r
33 \r
34 WORK TO DO\r
35 ----------\r
36 MM_SizePtr to change the size of a given pointer\r
37 \r
38 Multiple purge levels utilized\r
39 \r
40 EMS / XMS unmanaged routines\r
41 \r
42 =============================================================================\r
43 */\r
44 \r
45 //#include "LIB_HEAD.H"
46 #include "16_mm.h"
47 \r
48 /*\r
49 =============================================================================\r
50 \r
51                                                         LOCAL INFO\r
52 \r
53 =============================================================================\r
54 */\r
55 \r
56 #define LOCKBIT         0x80    // if set in attributes, block cannot be moved\r
57 #define PURGEBITS       3               // 0-3 level, 0= unpurgable, 3= purge first\r
58 #define PURGEMASK       0xfffc\r
59 #define BASEATTRIBUTES  0       // unlocked, non purgable\r
60 \r
61 #define MAXUMBS         10\r
62 \r
63 typedef struct mmblockstruct\r
64 {\r
65         unsigned        start,length;\r
66         unsigned        attributes;\r
67         memptr          *useptr;        // pointer to the segment start\r
68         struct mmblockstruct far *next;\r
69 } mmblocktype;\r
70 \r
71 \r
72 //#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!");mmfree=mmfree->next;}\r
73 #define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}\r
74 #define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}\r
75 \r
76 /*\r
77 =============================================================================\r
78 \r
79                                                  GLOBAL VARIABLES\r
80 \r
81 =============================================================================\r
82 */\r
83 \r
84 mminfotype      mminfo;\r
85 memptr          bufferseg;\r
86 boolean         mmerror;\r
87 \r
88 void            (* beforesort) (void);\r
89 void            (* aftersort) (void);\r
90 \r
91 /*\r
92 =============================================================================\r
93 \r
94                                                  LOCAL VARIABLES\r
95 \r
96 =============================================================================\r
97 */\r
98 \r
99 boolean         mmstarted;\r
100
101 void huge       *hugeheap;\r
102 void far        *farheap;\r
103 void            *nearheap;\r
104 \r
105 mmblocktype     far mmblocks[MAXBLOCKS]\r
106                         ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;\r
107 \r
108 boolean         bombonerror;\r
109 \r
110 unsigned        totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;\r
111 \r
112 void            (* XMSaddr) (void);             // far pointer to XMS driver\r
113 \r
114 unsigned        numUMBs,UMBbase[MAXUMBS];\r
115 \r
116 \r
117 /*\r
118 ======================\r
119 =\r
120 = MML_CheckForEMS\r
121 =\r
122 = Routine from p36 of Extending DOS\r
123 =\r
124 =======================\r
125 */\r
126 \r
127 boolean MML_CheckForEMS (void)\r
128 {
129         boolean emmcfems;
130         char    emmname[] = "EMMXXXX0";
131
132         __asm {
133                 mov     dx,OFF=emmname\r
134                 mov     ax,0x3d00\r
135                 int     0x21            // try to open EMMXXXX0 device\r
136                 jc      error\r
137 \r
138                 mov     bx,ax\r
139                 mov     ax,0x4400\r
140 \r
141                 int     0x21            // get device info\r
142                 jc      error\r
143 \r
144                 and     dx,0x80\r
145                 jz      error\r
146 \r
147                 mov     ax,0x4407\r
148 \r
149                 int     0x21            // get status\r
150                 jc      error\r
151                 or      al,al\r
152                 jz      error\r
153 \r
154                 mov     ah,0x3e\r
155                 int     0x21            // close handle\r
156                 jc      error
157                 //\r
158                 // EMS is good\r
159                 //
160                 mov     emmcfems,1
161                 jmp End
162                 error:
163                 //\r
164                 // EMS is bad\r
165                 //\r
166                 mov     emmcfems,0
167                 End:
168         }
169         return(emmcfems);\r
170 }\r
171 \r
172 \r
173 /*\r
174 ======================\r
175 =\r
176 = MML_SetupEMS\r
177 =\r
178 =======================\r
179 */\r
180 \r
181 void MML_SetupEMS (void)\r
182 {\r
183         char    str[80],str2[10];\r
184         unsigned        error;\r
185 \r
186         totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;\r
187 \r
188         __asm {\r
189                 mov     ah,EMS_STATUS\r
190                 int     EMS_INT                                         // make sure EMS hardware is present\r
191                 or      ah,ah\r
192                 jnz     error\r
193 \r
194                 mov     ah,EMS_VERSION\r
195                 int     EMS_INT\r
196                 or      ah,ah\r
197                 jnz     error\r
198                 cmp     al,0x32                                         // only work on ems 3.2 or greater\r
199                 jb      error\r
200 \r
201                 mov     ah,EMS_GETFRAME\r
202                 int     EMS_INT                                         // find the page frame address\r
203                 or      ah,ah\r
204                 jnz     error\r
205                 mov     [EMSpageframe],bx\r
206 \r
207                 mov     ah,EMS_GETPAGES\r
208                 int     EMS_INT                                         // find out how much EMS is there\r
209                 or      ah,ah\r
210                 jnz     error\r
211                 mov     [totalEMSpages],dx\r
212                 mov     [freeEMSpages],bx\r
213                 or      bx,bx\r
214                 jz      noEMS                                           // no EMS at all to allocate\r
215 \r
216                 cmp     bx,4\r
217                 jle     getpages                                        // there is only 1,2,3,or 4 pages\r
218                 mov     bx,4                                            // we can't use more than 4 pages
219
220 getpages:
221                 mov     [EMSpagesmapped],bx\r
222                 mov     ah,EMS_ALLOCPAGES                       // allocate up to 64k of EMS\r
223                 int     EMS_INT\r
224                 or      ah,ah\r
225                 jnz     error\r
226                 mov     [EMShandle],dx
227                 jmp End
228 error:\r
229                 error = _AH;\r
230                 strcpy (str,"MML_SetupEMS: EMS error 0x");\r
231                 itoa(error,str2,16);\r
232                 strcpy (str,str2);\r
233                 printf("%s\n",str);
234                 jmp End
235 noEMS:
236 End:\r
237         }\r
238 }\r
239 \r
240 \r
241 /*\r
242 ======================\r
243 =\r
244 = MML_ShutdownEMS\r
245 =\r
246 =======================\r
247 */\r
248 \r
249 void MML_ShutdownEMS (void)\r
250 {\r
251         if (!EMShandle)\r
252                 return;\r
253 \r
254         __asm
255         {\r
256                 mov     ah,EMS_FREEPAGES\r
257                 mov     dx,[EMShandle]\r
258                 int     EMS_INT\r
259                 or      ah,ah\r
260                 jz      ok
261                 printf("MML_ShutdownEMS: Error freeing EMS!");
262                 ok:\r
263         }\r
264 }\r
265 \r
266 /*\r
267 ====================\r
268 =\r
269 = MM_MapEMS\r
270 =\r
271 = Maps the 64k of EMS used by memory manager into the page frame\r
272 = for general use.  This only needs to be called if you are keeping\r
273 = other things in EMS.\r
274 =\r
275 ====================\r
276 */\r
277 \r
278 void MM_MapEMS (void)\r
279 {\r
280         char    str[80],str2[10];\r
281         unsigned        error;\r
282         int     i;\r
283 \r
284         for (i=0;i<EMSpagesmapped;i++)\r
285         {\r
286                 __asm
287                 {\r
288                         mov     ah,EMS_MAPPAGE\r
289                         mov     bx,[i]                  // logical page\r
290                         mov     al,bl                   // physical page\r
291                         mov     dx,[EMShandle]  // handle\r
292                         int     EMS_INT\r
293                         or      ah,ah\r
294                         jnz     error
295                         jmp End
296                         error:\r
297                         error = _AH;\r
298                         strcpy (str,"MM_MapEMS: EMS error 0x");\r
299                         itoa(error,str2,16);\r
300                         strcpy (str,str2);\r
301                         printf("%s\n",str);
302                         End:\r
303                 }\r
304         }\r
305         return;\r
306 }\r
307 \r
308 //==========================================================================\r
309 \r
310 /*\r
311 ======================\r
312 =\r
313 = MML_CheckForXMS\r
314 =\r
315 = Check for XMM driver\r
316 =\r
317 =======================\r
318 */\r
319 \r
320 boolean MML_CheckForXMS (void)\r
321 {\r
322         numUMBs = 0;\r
323 \r
324         __asm
325         {\r
326                 mov     ax,0x4300\r
327                 int     0x2f                            // query status of installed diver\r
328                 cmp     al,0x80\r
329                 je      good
330                 good:\r
331         }\r
332 /*      return false;\r
333 good:\r
334         return true;*/\r
335 }\r
336 \r
337 \r
338 /*\r
339 ======================\r
340 =\r
341 = MML_SetupXMS\r
342 =\r
343 = Try to allocate all upper memory block\r
344 =\r
345 =======================\r
346 */\r
347 \r
348 void MML_SetupXMS (void)\r
349 {\r
350         unsigned        base,size;\r
351 \r
352         __asm
353         {\r
354                 mov     ax,0x4310\r
355                 int     0x2f\r
356                 mov     [WORD PTR XMSaddr],bx\r
357                 mov     [WORD PTR XMSaddr+2],es         // function pointer to XMS driver\r
358 \r
359 getmemory:\r
360         mov     ah,XMS_ALLOCUMB\r
361         mov     dx,0xffff                                       // try for largest block possible\r
362         call    [DWORD PTR XMSaddr]\r
363         or      ax,ax\r
364         jnz     gotone\r
365 \r
366         cmp     bl,0xb0                                         // error: smaller UMB is available\r
367         jne     done;\r
368 \r
369         mov     ah,XMS_ALLOCUMB\r
370         call    [DWORD PTR XMSaddr]             // DX holds largest available UMB\r
371         or      ax,ax\r
372         jz      done                                            // another error...\r
373 \r
374 gotone:\r
375         mov     [base],bx\r
376         mov     [size],dx\r
377         }\r
378         MML_UseSpace (base,size);\r
379         mminfo.XMSmem += size*16;\r
380         UMBbase[numUMBs] = base;\r
381         numUMBs++;\r
382         if (numUMBs < MAXUMBS)\r
383                 goto getmemory;\r
384 \r
385 done:;\r
386 }\r
387 \r
388 \r
389 /*\r
390 ======================\r
391 =\r
392 = MML_ShutdownXMS\r
393 =\r
394 ======================\r
395 */\r
396 \r
397 void MML_ShutdownXMS (void)\r
398 {\r
399         int     i;\r
400         unsigned        base;\r
401 \r
402         for (i=0;i<numUMBs;i++)\r
403         {\r
404                 base = UMBbase[i];\r
405                 __asm
406                 {\r
407                         mov     ah,XMS_FREEUMB\r
408                         mov     dx,[base]\r
409                         call    [DWORD PTR XMSaddr]
410                 }\r
411         }\r
412 }\r
413 \r
414 //==========================================================================\r
415 \r
416 /*\r
417 ======================\r
418 =\r
419 = MML_UseSpace\r
420 =\r
421 = Marks a range of paragraphs as usable by the memory manager\r
422 = This is used to mark space for the near heap, far heap, ems page frame,\r
423 = and upper memory blocks\r
424 =\r
425 ======================\r
426 */\r
427 \r
428 void MML_UseSpace (unsigned segstart, unsigned seglength)\r
429 {\r
430         mmblocktype far *scan,far *last;\r
431         unsigned        oldend;\r
432         long            extra;\r
433 \r
434         scan = last = mmhead;\r
435         mmrover = mmhead;               // reset rover to start of memory\r
436 \r
437 //\r
438 // search for the block that contains the range of segments\r
439 //\r
440         while (scan->start+scan->length < segstart)\r
441         {\r
442                 last = scan;\r
443                 scan = scan->next;\r
444         }\r
445 \r
446 //\r
447 // take the given range out of the block\r
448 //\r
449         oldend = scan->start + scan->length;\r
450         extra = oldend - (segstart+seglength);\r
451         if (extra < 0)\r
452                 Quit ("MML_UseSpace: Segment spans two blocks!");\r
453 \r
454         if (segstart == scan->start)\r
455         {\r
456                 last->next = scan->next;                        // unlink block\r
457                 FREEBLOCK(scan);\r
458                 scan = last;\r
459         }\r
460         else\r
461                 scan->length = segstart-scan->start;    // shorten block\r
462 \r
463         if (extra > 0)\r
464         {\r
465                 GETNEWBLOCK;\r
466                 mmnew->next = scan->next;\r
467                 scan->next = mmnew;\r
468                 mmnew->start = segstart+seglength;\r
469                 mmnew->length = extra;\r
470                 mmnew->attributes = LOCKBIT;\r
471         }\r
472 \r
473 }\r
474 \r
475 //==========================================================================\r
476 \r
477 /*\r
478 ====================\r
479 =\r
480 = MML_ClearBlock\r
481 =\r
482 = We are out of blocks, so free a purgable block\r
483 =\r
484 ====================\r
485 */\r
486 \r
487 void MML_ClearBlock (void)\r
488 {\r
489         mmblocktype far *scan,far *last;\r
490 \r
491         scan = mmhead->next;\r
492 \r
493         while (scan)\r
494         {\r
495                 if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )\r
496                 {\r
497                         MM_FreePtr(scan->useptr);\r
498                         return;\r
499                 }\r
500                 scan = scan->next;\r
501         }\r
502 \r
503         Quit ("MM_ClearBlock: No purgable blocks!");\r
504 }\r
505 \r
506 \r
507 //==========================================================================\r
508 \r
509 /*\r
510 ===================\r
511 =\r
512 = MM_Startup\r
513 =\r
514 = Grabs all space from turbo with malloc/farmalloc\r
515 = Allocates bufferseg misc buffer\r
516 =\r
517 ===================\r
518 */\r
519 \r
520 static  char *ParmStrings[] = {"noems","noxms",""};\r
521 \r
522 void MM_Startup (void)\r
523 {\r
524         int i;\r
525         unsigned        long length;\r
526         void far        *start;\r
527         unsigned        segstart,seglength,endfree;\r
528 \r
529         if (mmstarted)\r
530                 MM_Shutdown ();\r
531 \r
532 \r
533         mmstarted = true;\r
534         bombonerror = true;\r
535 //\r
536 // set up the linked list (everything in the free list;\r
537 //\r
538         mmhead = NULL;\r
539         mmfree = &mmblocks[0];\r
540         for (i=0;i<MAXBLOCKS-1;i++)\r
541                 mmblocks[i].next = &mmblocks[i+1];\r
542         mmblocks[i].next = NULL;\r
543 \r
544 //\r
545 // locked block of all memory until we punch out free space\r
546 //\r
547         GETNEWBLOCK;\r
548         mmhead = mmnew;                         // this will allways be the first node\r
549         mmnew->start = 0;\r
550         mmnew->length = 0xffff;\r
551         mmnew->attributes = LOCKBIT;\r
552         mmnew->next = NULL;\r
553         mmrover = mmhead;\r
554 \r
555 \r
556 //\r
557 // get all available near conventional memory segments\r
558 //\r
559         length=coreleft();\r
560         start = (void far *)(nearheap = malloc(length));\r
561 \r
562         length -= 16-(FP_OFF(start)&15);\r
563         length -= SAVENEARHEAP;\r
564         seglength = length / 16;                        // now in paragraphs\r
565         segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
566         MML_UseSpace (segstart,seglength);\r
567         mminfo.nearheap = length;\r
568 \r
569 //\r
570 // get all available far conventional memory segments\r
571 //\r
572         length=farcoreleft();\r
573         start = farheap = farmalloc(length);\r
574         length -= 16-(FP_OFF(start)&15);\r
575         length -= SAVEFARHEAP;\r
576         seglength = length / 16;                        // now in paragraphs\r
577         segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
578         MML_UseSpace (segstart,seglength);\r
579         mminfo.farheap = length;\r
580         mminfo.mainmem = mminfo.nearheap + mminfo.farheap;\r
581 \r
582 \r
583 //\r
584 // detect EMS and allocate up to 64K at page frame\r
585 //\r
586         mminfo.EMSmem = 0;\r
587         for (i = 1;i < _argc;i++)\r
588         {\r
589                 if ( US_CheckParm(_argv[i],ParmStrings) == 0)\r
590                         goto emsskip;                           // param NOEMS\r
591         }\r
592 \r
593         if (MML_CheckForEMS())\r
594         {\r
595                 MML_SetupEMS();                                 // allocate space\r
596                 MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);\r
597                 MM_MapEMS();                                    // map in used pages\r
598                 mminfo.EMSmem = EMSpagesmapped*0x4000l;\r
599         }\r
600 \r
601 //\r
602 // detect XMS and get upper memory blocks\r
603 //\r
604 emsskip:\r
605         mminfo.XMSmem = 0;\r
606         for (i = 1;i < _argc;i++)\r
607         {\r
608                 if ( US_CheckParm(_argv[i],ParmStrings) == 0)\r
609                         goto xmsskip;                           // param NOXMS\r
610         }\r
611 \r
612         if (MML_CheckForXMS())\r
613                 MML_SetupXMS();                                 // allocate as many UMBs as possible\r
614 \r
615 //\r
616 // allocate the misc buffer\r
617 //\r
618 xmsskip:\r
619         mmrover = mmhead;               // start looking for space after low block\r
620 \r
621         MM_GetPtr (&bufferseg,BUFFERSIZE);\r
622 }\r
623 \r
624 //==========================================================================\r
625 \r
626 /*\r
627 ====================\r
628 =\r
629 = MM_Shutdown\r
630 =\r
631 = Frees all conventional, EMS, and XMS allocated\r
632 =\r
633 ====================\r
634 */\r
635 \r
636 void MM_Shutdown (void)\r
637 {\r
638   if (!mmstarted)\r
639         return;\r
640 \r
641   farfree (farheap);\r
642   free (nearheap);\r
643   MML_ShutdownEMS ();\r
644   MML_ShutdownXMS ();\r
645 }\r
646 \r
647 //==========================================================================\r
648 \r
649 /*\r
650 ====================\r
651 =\r
652 = MM_GetPtr\r
653 =\r
654 = Allocates an unlocked, unpurgable block\r
655 =\r
656 ====================\r
657 */\r
658 \r
659 void MM_GetPtr (memptr *baseptr,unsigned long size)\r
660 {\r
661         mmblocktype far *scan,far *lastscan,far *endscan\r
662                                 ,far *purge,far *next;\r
663         int                     search;\r
664         unsigned        needed,startseg;\r
665 \r
666         needed = (size+15)/16;          // convert size from bytes to paragraphs\r
667 \r
668         GETNEWBLOCK;                            // fill in start and next after a spot is found\r
669         mmnew->length = needed;\r
670         mmnew->useptr = baseptr;\r
671         mmnew->attributes = BASEATTRIBUTES;\r
672 \r
673         for (search = 0; search<3; search++)\r
674         {\r
675         //\r
676         // first search:        try to allocate right after the rover, then on up\r
677         // second search:       search from the head pointer up to the rover\r
678         // third search:        compress memory, then scan from start\r
679                 if (search == 1 && mmrover == mmhead)\r
680                         search++;\r
681 \r
682                 switch (search)\r
683                 {\r
684                 case 0:\r
685                         lastscan = mmrover;\r
686                         scan = mmrover->next;\r
687                         endscan = NULL;\r
688                         break;\r
689                 case 1:\r
690                         lastscan = mmhead;\r
691                         scan = mmhead->next;\r
692                         endscan = mmrover;\r
693                         break;\r
694                 case 2:\r
695                         MM_SortMem ();\r
696                         lastscan = mmhead;\r
697                         scan = mmhead->next;\r
698                         endscan = NULL;\r
699                         break;\r
700                 }\r
701 \r
702                 startseg = lastscan->start + lastscan->length;\r
703 \r
704                 while (scan != endscan)\r
705                 {\r
706                         if (scan->start - startseg >= needed)\r
707                         {\r
708                         //\r
709                         // got enough space between the end of lastscan and\r
710                         // the start of scan, so throw out anything in the middle\r
711                         // and allocate the new block\r
712                         //\r
713                                 purge = lastscan->next;\r
714                                 lastscan->next = mmnew;\r
715                                 mmnew->start = *(unsigned *)baseptr = startseg;\r
716                                 mmnew->next = scan;\r
717                                 while ( purge != scan)\r
718                                 {       // free the purgable block\r
719                                         next = purge->next;\r
720                                         FREEBLOCK(purge);\r
721                                         purge = next;           // purge another if not at scan\r
722                                 }\r
723                                 mmrover = mmnew;\r
724                                 return; // good allocation!\r
725                         }\r
726 \r
727                         //\r
728                         // if this block is purge level zero or locked, skip past it\r
729                         //\r
730                         if ( (scan->attributes & LOCKBIT)\r
731                                 || !(scan->attributes & PURGEBITS) )\r
732                         {\r
733                                 lastscan = scan;\r
734                                 startseg = lastscan->start + lastscan->length;\r
735                         }\r
736 \r
737 \r
738                         scan=scan->next;                // look at next line\r
739                 }\r
740         }\r
741 \r
742         if (bombonerror)\r
743                 Quit (OUT_OF_MEM_MSG,(size-mminfo.nearheap));\r
744         else\r
745                 mmerror = true;\r
746 }\r
747 \r
748 //==========================================================================\r
749 \r
750 /*\r
751 ====================\r
752 =\r
753 = MM_FreePtr\r
754 =\r
755 = Allocates an unlocked, unpurgable block\r
756 =\r
757 ====================\r
758 */\r
759 \r
760 void MM_FreePtr (memptr *baseptr)\r
761 {\r
762         mmblocktype far *scan,far *last;\r
763 \r
764         last = mmhead;\r
765         scan = last->next;\r
766 \r
767         if (baseptr == mmrover->useptr) // removed the last allocated block\r
768                 mmrover = mmhead;\r
769 \r
770         while (scan->useptr != baseptr && scan)\r
771         {\r
772                 last = scan;\r
773                 scan = scan->next;\r
774         }\r
775 \r
776         if (!scan)\r
777                 Quit ("MM_FreePtr: Block not found!");\r
778 \r
779         last->next = scan->next;\r
780 \r
781         FREEBLOCK(scan);\r
782 }\r
783 //==========================================================================\r
784 \r
785 /*\r
786 =====================\r
787 =\r
788 = MM_SetPurge\r
789 =\r
790 = Sets the purge level for a block (locked blocks cannot be made purgable)\r
791 =\r
792 =====================\r
793 */\r
794 \r
795 void MM_SetPurge (memptr *baseptr, int purge)\r
796 {\r
797         mmblocktype far *start;\r
798 \r
799         start = mmrover;\r
800 \r
801         do\r
802         {\r
803                 if (mmrover->useptr == baseptr)\r
804                         break;\r
805 \r
806                 mmrover = mmrover->next;\r
807 \r
808                 if (!mmrover)\r
809                         mmrover = mmhead;\r
810                 else if (mmrover == start)\r
811                         Quit ("MM_SetPurge: Block not found!");\r
812 \r
813         } while (1);\r
814 \r
815         mmrover->attributes &= ~PURGEBITS;\r
816         mmrover->attributes |= purge;\r
817 }\r
818 \r
819 //==========================================================================\r
820 \r
821 /*\r
822 =====================\r
823 =\r
824 = MM_SetLock\r
825 =\r
826 = Locks / unlocks the block\r
827 =\r
828 =====================\r
829 */\r
830 \r
831 void MM_SetLock (memptr *baseptr, boolean locked)\r
832 {\r
833         mmblocktype far *start;\r
834 \r
835         start = mmrover;\r
836 \r
837         do\r
838         {\r
839                 if (mmrover->useptr == baseptr)\r
840                         break;\r
841 \r
842                 mmrover = mmrover->next;\r
843 \r
844                 if (!mmrover)\r
845                         mmrover = mmhead;\r
846                 else if (mmrover == start)\r
847                         Quit ("MM_SetLock: Block not found!");\r
848 \r
849         } while (1);\r
850 \r
851         mmrover->attributes &= ~LOCKBIT;\r
852         mmrover->attributes |= locked*LOCKBIT;\r
853 }\r
854 \r
855 //==========================================================================\r
856 \r
857 /*\r
858 =====================\r
859 =\r
860 = MM_SortMem\r
861 =\r
862 = Throws out all purgable stuff and compresses movable blocks\r
863 =\r
864 =====================\r
865 */\r
866 \r
867 void MM_SortMem (void)\r
868 {\r
869         mmblocktype far *scan,far *last,far *next;\r
870         unsigned        start,length,source,dest,oldborder;\r
871         int                     playing;\r
872 \r
873         //\r
874         // lock down a currently playing sound\r
875         //\r
876         playing = SD_SoundPlaying ();\r
877         if (playing)\r
878         {\r
879                 switch (SoundMode)\r
880                 {\r
881                 case sdm_PC:\r
882                         playing += STARTPCSOUNDS;\r
883                         break;\r
884                 case sdm_AdLib:\r
885                         playing += STARTADLIBSOUNDS;\r
886                         break;\r
887                 }\r
888                 MM_SetLock(&(memptr)audiosegs[playing],true);\r
889         }\r
890 \r
891 \r
892         SD_StopSound();\r
893 //      oldborder = bordercolor;\r
894 //      VW_ColorBorder (15);\r
895 \r
896         if (beforesort)\r
897                 beforesort();\r
898 \r
899         scan = mmhead;\r
900 \r
901         last = NULL;            // shut up compiler warning\r
902 \r
903         while (scan)\r
904         {\r
905                 if (scan->attributes & LOCKBIT)\r
906                 {\r
907                 //\r
908                 // block is locked, so try to pile later blocks right after it\r
909                 //\r
910                         start = scan->start + scan->length;\r
911                 }\r
912                 else\r
913                 {\r
914                         if (scan->attributes & PURGEBITS)\r
915                         {\r
916                         //\r
917                         // throw out the purgable block\r
918                         //\r
919                                 next = scan->next;\r
920                                 FREEBLOCK(scan);\r
921                                 last->next = next;\r
922                                 scan = next;\r
923                                 continue;\r
924                         }\r
925                         else\r
926                         {\r
927                         //\r
928                         // push the non purgable block on top of the last moved block\r
929                         //\r
930                                 if (scan->start != start)\r
931                                 {\r
932                                         length = scan->length;\r
933                                         source = scan->start;\r
934                                         dest = start;\r
935                                         while (length > 0xf00)\r
936                                         {\r
937                                                 movedata(source,0,dest,0,0xf00*16);\r
938                                                 length -= 0xf00;\r
939                                                 source += 0xf00;\r
940                                                 dest += 0xf00;\r
941                                         }\r
942                                         movedata(source,0,dest,0,length*16);\r
943 \r
944                                         scan->start = start;\r
945                                         *(unsigned *)scan->useptr = start;\r
946                                 }\r
947                                 start = scan->start + scan->length;\r
948                         }\r
949                 }\r
950 \r
951                 last = scan;\r
952                 scan = scan->next;              // go to next block\r
953         }\r
954 \r
955         mmrover = mmhead;\r
956 \r
957         if (aftersort)\r
958                 aftersort();\r
959 \r
960 //      VW_ColorBorder (oldborder);\r
961 \r
962         if (playing)\r
963                 MM_SetLock(&(memptr)audiosegs[playing],false);\r
964 }\r
965 \r
966 \r
967 //==========================================================================\r
968 \r
969 #if 0\r
970 /*\r
971 =====================\r
972 =\r
973 = MM_ShowMemory\r
974 =\r
975 =====================\r
976 */\r
977 \r
978 void MM_ShowMemory (void)\r
979 {\r
980         mmblocktype far *scan;\r
981         unsigned color,temp;\r
982         long    end,owner;\r
983         char    scratch[80],str[10];\r
984 \r
985         VW_SetDefaultColors();\r
986         VW_SetLineWidth(40);\r
987         temp = bufferofs;\r
988         bufferofs = 0;\r
989         VW_SetScreen (0,0);\r
990 \r
991         scan = mmhead;\r
992 \r
993         end = -1;\r
994 \r
995 //CA_OpenDebug ();\r
996 \r
997         while (scan)\r
998         {\r
999                 if (scan->attributes & PURGEBITS)\r
1000                         color = 5;              // dark purple = purgable\r
1001                 else\r
1002                         color = 9;              // medium blue = non purgable\r
1003                 if (scan->attributes & LOCKBIT)\r
1004                         color = 12;             // red = locked\r
1005                 if (scan->start<=end)\r
1006                         Quit ("MM_ShowMemory: Memory block order currupted!");\r
1007                 end = scan->start+scan->length-1;\r
1008                 VW_Hlin(scan->start,(unsigned)end,0,color);\r
1009                 VW_Plot(scan->start,0,15);\r
1010                 if (scan->next->start > end+1)\r
1011                         VW_Hlin(end+1,scan->next->start,0,0);   // black = free\r
1012 \r
1013 #if 0\r
1014 strcpy (scratch,"Size:");\r
1015 ltoa ((long)scan->length*16,str,10);\r
1016 strcat (scratch,str);\r
1017 strcat (scratch,"\tOwner:0x");\r
1018 owner = (unsigned)scan->useptr;\r
1019 ultoa (owner,str,16);\r
1020 strcat (scratch,str);\r
1021 strcat (scratch,"\n");\r
1022 write (debughandle,scratch,strlen(scratch));\r
1023 #endif\r
1024 \r
1025                 scan = scan->next;\r
1026         }\r
1027 \r
1028 //CA_CloseDebug ();\r
1029 \r
1030         IN_Ack();\r
1031         VW_SetLineWidth(64);\r
1032         bufferofs = temp;\r
1033 }\r
1034 #endif\r
1035 \r
1036 //==========================================================================\r
1037 \r
1038 \r
1039 /*\r
1040 ======================\r
1041 =\r
1042 = MM_UnusedMemory\r
1043 =\r
1044 = Returns the total free space without purging\r
1045 =\r
1046 ======================\r
1047 */\r
1048 \r
1049 long MM_UnusedMemory (void)\r
1050 {\r
1051         unsigned free;\r
1052         mmblocktype far *scan;\r
1053 \r
1054         free = 0;\r
1055         scan = mmhead;\r
1056 \r
1057         while (scan->next)\r
1058         {\r
1059                 free += scan->next->start - (scan->start + scan->length);\r
1060                 scan = scan->next;\r
1061         }\r
1062 \r
1063         return free*16l;\r
1064 }\r
1065 \r
1066 //==========================================================================\r
1067 \r
1068 \r
1069 /*\r
1070 ======================\r
1071 =\r
1072 = MM_TotalFree\r
1073 =\r
1074 = Returns the total free space with purging\r
1075 =\r
1076 ======================\r
1077 */\r
1078 \r
1079 long MM_TotalFree (void)\r
1080 {\r
1081         unsigned free;\r
1082         mmblocktype far *scan;\r
1083 \r
1084         free = 0;\r
1085         scan = mmhead;\r
1086 \r
1087         while (scan->next)\r
1088         {\r
1089                 if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))\r
1090                         free += scan->length;\r
1091                 free += scan->next->start - (scan->start + scan->length);\r
1092                 scan = scan->next;\r
1093         }\r
1094 \r
1095         return free*16l;\r
1096 }\r
1097 \r
1098 //==========================================================================\r
1099 \r
1100 /*\r
1101 =====================\r
1102 =\r
1103 = MM_BombOnError\r
1104 =\r
1105 =====================\r
1106 */\r
1107 \r
1108 void MM_BombOnError (boolean bomb)\r
1109 {\r
1110         bombonerror = bomb;\r
1111 }\r
1112 \r
1113 \r