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