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