OSDN Git Service

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