]> 4ch.mooo.com Git - 16.git/blob - 16/exmmtest/16_mm.c
dd6ac5191fc7ab3c172726a65e6c2165ea047e0b
[16.git] / 16 / exmmtest / 16_mm.c
1 /* Catacomb Apocalypse Source Code
2  * Copyright (C) 1993-2014 Flat Rock Software
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 // NEWMM.C
20
21 /*
22 =============================================================================
23
24                         ID software memory manager
25                         --------------------------
26
27 Primary coder: John Carmack
28
29 RELIES ON
30 ---------
31 Quit (char *error) function
32
33
34 WORK TO DO
35 ----------
36 MM_SizePtr to change the size of a given pointer
37
38 Multiple purge levels utilized
39
40 EMS / XMS unmanaged routines
41
42 =============================================================================
43 */
44 /*
45
46 Open Watcom port by sparky4
47
48 */
49 #include "16_mm.h"
50 #pragma hdrstop
51
52 #pragma warn -pro
53 #pragma warn -use
54
55 /*
56 =============================================================================
57
58                                                  GLOBAL VARIABLES
59
60 =============================================================================
61 */
62
63 void            (* beforesort) (void);
64 void            (* aftersort) (void);
65 void            (* XMSaddr) (void);             // far pointer to XMS driver
66
67 /*
68 =============================================================================
69
70                                                  LOCAL VARIABLES
71
72 =============================================================================
73 */
74
75 static  char *ParmStringsexmm[] = {"noems","noxms",""};
76
77 /*
78 ======================
79 =
80 = MML_CheckForEMS
81 =
82 = Routine from p36 of Extending DOS
83 =
84 =======================
85 */
86
87 boolean MML_CheckForEMS(void)
88 {
89         boolean emmcfems;
90         static char     emmname[] = "EMMXXXX0"; //fix by andrius4669
91 //              mov     dx,OFFSET emmname
92         __asm {
93                 //LEA   DX, emmname     //fix by andrius4669
94                 mov     dx,OFFSET emmname       //fix by andrius4669
95                 mov     ax,0x3d00
96                 int     0x21            // try to open EMMXXXX0 device
97                 jc      error
98
99                 mov     bx,ax
100                 mov     ax,0x4400
101
102                 int     0x21            // get device info
103                 jc      error
104
105                 and     dx,0x80
106                 jz      error
107
108                 mov     ax,0x4407
109
110                 int     0x21            // get status
111                 jc      error
112                 or      al,al
113                 jz      error
114
115                 mov     ah,0x3e
116                 int     0x21            // close handle
117                 jc      error
118                 //
119                 // EMS is good
120                 //
121                 mov     emmcfems,1
122                 jmp End
123                 error:
124                 //
125                 // EMS is bad
126                 //
127                 mov     emmcfems,0
128                 End:
129         }
130         End:
131         error:
132         return(emmcfems);
133 }
134
135
136 /*
137 ======================
138 =
139 = MML_SetupEMS
140 =
141 =======================
142 */
143
144 byte MML_SetupEMS(mminfo_t *mm)
145 {
146         byte    str[160];
147         byte    err;
148         boolean errorflag=false;
149
150         unsigned int EMSVer = 0;
151         //byte  EMS_status;
152         unsigned        totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;
153         totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;
154
155         __asm
156                 {
157                 mov     ah,EMS_STATUS
158                 int     EMS_INT                                         // make sure EMS hardware is present
159                 or      ah,ah
160                 //mov   [EMS_status],ah
161                 jnz     error
162
163                 mov     ah,EMS_VERSION
164                 int     EMS_INT
165                 or      ah,ah
166                 jnz     error
167                 mov     [EMSVer],ax                             //      set EMSVer
168                 cmp     al,0x32                                         // only work on ems 3.2 or greater
169                 jb      error
170
171                 mov     ah,EMS_GETFRAME
172                 int     EMS_INT                                         // find the page frame address
173                 or      ah,ah
174                 jnz     error
175                 mov     [EMSpageframe],bx
176
177                 mov     ah,EMS_GETPAGES
178                 int     EMS_INT                                         // find out how much EMS is there
179                 or      ah,ah
180                 jnz     error
181                 mov     [totalEMSpages],dx
182                 mov     [freeEMSpages],bx
183                 or      bx,bx
184                 jz      noEMS                                           // no EMS at all to allocate
185                                                                                         //EXPAND DONG!!!!
186                 cmp     [EMSVer],0x40
187                 jb      low
188                 cmp     bx,[freeEMSpages]
189                 jle     getpages
190                 mov     bx,[freeEMSpages]
191                 jmp     getpages
192
193 low:
194                 cmp     bx,4
195                 jle     getpages                                        // there is only 1,2,3,or 4 pages
196                 mov     bx,4                                            // we can't use more than 4 pages
197
198 getpages:
199                 mov     [EMSpagesmapped],bx
200                 mov     ah,EMS_ALLOCPAGES                       // allocate up to 64k of EMS
201                 int     EMS_INT
202                 or      ah,ah
203                 jnz     error
204                 mov     [EMShandle],dx
205                 jmp End
206 error:
207                 mov     err,ah
208                 mov     errorflag,1
209                 jmp End
210 noEMS:
211 End:
212         }
213         if(errorflag==true)
214         {
215                 //err = CPURegs.h.ah;
216                 strcpy(str,"MM_SetupEMS: EMS error ");
217                 //itoa(err,str2,16);
218                 MM_EMSerr(&str, err);
219                 printf("%s\n",str);
220                 return err;
221         }
222         mm->totalEMSpages=totalEMSpages;
223         mm->freeEMSpages=freeEMSpages;
224         mm->EMSpageframe=EMSpageframe;
225         mm->EMSpagesmapped=EMSpagesmapped;
226         mm->EMShandle=EMShandle;
227         mm->EMSVer=EMSVer;
228         return 0;
229 }
230
231
232 /*
233 ======================
234 =
235 = MML_ShutdownEMS
236 =
237 =======================
238 */
239
240 void MML_ShutdownEMS(mminfo_t *mm)
241 {
242         boolean errorflag=false;
243         unsigned EMShandle=mm->EMShandle;
244
245         if(!EMShandle)
246                 return;
247         __asm
248         {
249                 mov     ah,EMS_FREEPAGES
250                 mov     dx,[EMShandle]
251                 int     EMS_INT
252                 or      ah,ah
253                 jz      ok
254                 mov     errorflag,1
255                 ok:
256         }
257         if(errorflag==true) printf("MML_ShutdownEMS: Error freeing EMS!\n");    //++++ add something
258 }
259
260 /*
261 ====================
262 =
263 = MM_MapEMS
264 =
265 = Maps the 64k of EMS used by memory manager into the page frame
266 = for general use.  This only needs to be called if you are keeping
267 = other things in EMS.
268 =
269 ====================
270 */
271
272 byte MM_MapEMS(mminfo_t *mm, mminfotype *mmi)
273 {
274         byte    str[160];
275         unsigned        EMShandle;
276         byte err;
277         boolean errorflag=false;
278         int     i;
279         EMShandle=mm->EMShandle;
280
281         for (i=0;i<4/*MAPPAGES*/;i++)
282         {
283                 __asm
284                 {
285                         mov     ah,EMS_MAPPAGE
286                         mov     bx,[i]                  // logical page
287                         mov     al,bl                   // physical page
288                         mov     dx,[EMShandle]  // handle
289                         int     EMS_INT
290                         or      ah,ah
291                         jnz     error
292                         jmp End
293                         error:
294                         mov     err,ah
295                         mov     errorflag,1
296                         End:
297                 }
298                 if(errorflag==true)
299                 {
300                         //err = CPURegs.h.ah;
301                         strcpy(str,"MM_MapEMS: EMS error ");
302                         //itoa(err,str2,16);
303                         MM_EMSerr(str, err);
304                         printf("%s\n",str);
305                         //printf("FACK! %x\n", err);
306                         return err;
307                 }
308         }
309         mmi->EMSmem = (i)*0x4000lu;
310         return 0;
311 }
312
313 byte MM_MapXEMS(mminfo_t *mm, mminfotype *mmi)
314 {
315 //SUB EMS.MapXPages (PhysicalStart, LogicalStart, NumPages, Handle)
316
317         //Maps up to 4 logical EMS pages to physical pages in the page frame, where:
318         //PhysicalStart = Physical page first logical page is mapped to
319         //LogicalStart  = First logical page to map
320         //NumPages      = Number of pages to map (1 to 4)
321         //Handle        = EMS handle logical pages are allocated to
322
323   /*//Create a buffer containing the page information
324 //  FOR x = 0 TO NumPages - 1
325 //    MapInfo$ = MapInfo$ + MKI$(LogicalStart + x) + MKI$(PhysicalStart + x)
326 //  NEXT*/
327
328 //  Regs.ax = 0x5000                           //Map the pages in the buffer
329 //  Regs.cx = NumPages                         //to the pageframe
330 //  Regs.dx = Handle
331 //  Regs.ds = VARSEG(MapInfo$)
332 //  Regs.si = SADD(MapInfo$)
333 //  InterruptX 0x67, Regs, Regs
334 //      EMS.Error = (Regs.ax AND 0xFF00&) \ 0x100  //Store the status code
335
336 //END SUB
337         byte    str[160];
338         byte err;
339         word    EMShandle;
340         boolean errorflag=false;
341         int     i;
342         EMShandle=mm->EMShandle;
343
344         if(mm->EMSVer<0x40)
345                 return 5;
346
347         for (i=0;i<MAPPAGES;i++)
348         {
349                 __asm
350                 {
351                         mov     ah,EMS_MAPXPAGE
352                         mov     cx,[i]                  // logical page
353                         mov     al,bl                   // physical page
354                         mov     dx,[EMShandle]  // handle
355                         int     EMS_INT
356                         or      ah,ah
357                         jnz     error
358                         jmp End
359                         error:
360                         mov     err,ah
361                         mov     errorflag,1
362                         End:
363                 }
364                 if(errorflag==true)
365                 {
366                         //err = CPURegs.h.ah;
367                         //strcpy(str,"MM_MapXEMS: EMS error 0x");
368                         strcpy(str,"MM_MapXEMS: EMS error ");
369                         //itoa(err,str2,16);
370                         MM_EMSerr(&str, err);
371                         printf("%s\n",str);
372                         //printf("%s%x\n",str, err);
373                         //printf("FACK! %x\n", err);
374                         return err;
375                 }
376         }
377         mmi->EMSmem = (i)*0x4000lu;
378         return 0;
379 }
380
381 //==========================================================================
382
383 /*
384 ======================
385 =
386 = MML_CheckForXMS
387 =
388 = Check for XMM driver
389 =
390 =======================
391 */
392
393 boolean MML_CheckForXMS(mminfo_t *mm)
394 {
395         boolean errorflag=false;
396         mm->numUMBs = 0;
397
398         __asm
399         {
400                 mov     ax,0x4300
401                 int     0x2f                            // query status of installed diver
402                 cmp     al,0x80
403                 je      good
404                 mov     errorflag,1
405                 good:
406         }
407         if(errorflag==true) return false;
408         else return true;
409 }
410
411
412 /*
413 ======================
414 =
415 = MML_SetupXMS
416 =
417 = Try to allocate all upper memory block
418 =
419 =======================
420 */
421
422 void MML_SetupXMS(mminfo_t *mm, mminfotype *mmi)
423 {
424         unsigned        base,size;
425
426 getmemory:
427         __asm
428         {
429                 mov     ax,0x4310
430                 int     0x2f
431                 mov     [WORD PTR XMSaddr],bx
432                 mov     [WORD PTR XMSaddr+2],es         // function pointer to XMS driver
433
434                 mov     ah,XMS_ALLOCUMB
435                 mov     dx,0xffff                                       // try for largest block possible
436                 //mov     ax,dx                                         // Set available Kbytes.
437                 call    [DWORD PTR XMSaddr]
438                 or      ax,ax
439                 jnz     gotone
440
441                 cmp     bl,0xb0                                         // error: smaller UMB is available
442                 jne     done;
443
444                 mov     ah,XMS_ALLOCUMB
445                 call    [DWORD PTR XMSaddr]             // DX holds largest available UMB
446                 or      ax,ax
447                 jz      done                                            // another error...
448
449 gotone:
450                 mov     [base],bx
451                 mov     [size],dx
452 done:
453         }
454         printf("base=%u ", base); printf("size=%u\n", size);
455         MML_UseSpace(base,size, mm);
456         mmi->XMSmem += size*16;
457         mm->UMBbase[mm->numUMBs] = base;
458         mm->numUMBs++;
459         if(mm->numUMBs < MAXUMBS)
460                 goto getmemory;
461 }
462
463
464 /*
465 ======================
466 =
467 = MML_ShutdownXMS
468 =
469 ======================
470 */
471
472 void MML_ShutdownXMS(mminfo_t *mm)
473 {
474         int     i;
475         unsigned        base;
476
477         for (i=0;i<mm->numUMBs;i++)
478         {
479                 base = mm->UMBbase[i];
480                 __asm
481                 {
482                         mov     ah,XMS_FREEUMB
483                         mov     dx,[base]
484                         call    [DWORD PTR XMSaddr]
485                 }
486         }
487 }
488
489 //==========================================================================
490
491 /*
492 ======================
493 =
494 = MML_UseSpace
495 =
496 = Marks a range of paragraphs as usable by the memory manager
497 = This is used to mark space for the near heap, far heap, ems page frame,
498 = and upper memory blocks
499 =
500 ======================
501 */
502
503 void MML_UseSpace(/*d*/word segstart, dword seglength, mminfo_t *mm)
504 {
505         mmblocktype huge *scan,huge *last;
506         word            segm;
507         dword   oldend;
508         dword           extra;
509
510         scan = last = mm->mmhead;
511         mm->mmrover = mm->mmhead;               // reset rover to start of memory
512
513 //
514 // search for the block that contains the range of segments
515 //
516         while(scan->start+scan->length < segstart)
517         {
518                 last = scan;
519                 scan = scan->next;
520         }
521
522         //find out how many blocks it spans!
523         if(seglength>0xffffu)
524         {
525 //              segm=seglength/0x4000u;
526                 segm=seglength/0xffffu;
527         }
528         else segm=1;
529
530         //++++emsver stuff!
531         if(segm>1/*extra>0xfffflu*/)
532         {
533                 /*__asm
534                 {
535                         push    ds
536                         mov     ax,ds
537                         inc             ax
538                         mov     ds,ax
539                 }*/
540
541
542 //MML_UseSpace(?segstart?, ?length?, mm);
543
544                 /*__asm
545                 {
546                         pop ds
547                 }*/
548                 //printf("MML_UseSpace: Segment spans two blocks!\n");
549         }
550
551 //
552 // take the given range out of the block
553 //
554         oldend = scan->start + scan->length;
555         extra = oldend - (segstart+seglength);
556 /*
557 printf("segm=%u ", segm);
558 printf("ex=%lu  ", extra);
559 printf("start+seglen=%lu        ", segstart+seglength);
560 printf("len=%u  ", scan->length);
561 printf("segsta=%x       ", segstart);
562 printf("seglen=%lu\n", seglength);
563 */
564 //segu:
565 //++++todo: linked list of segment!
566 //printf("segm=%lu\n", segm);
567         if(segstart == scan->start)
568         {
569                 last->next = scan->next;                        // unlink block
570                 MM_FreeBlock(scan, mm);
571                 scan = last;
572         }
573         else
574                 scan->length = segstart-scan->start;    // shorten block
575
576 //      segm--;
577
578         if(extra > 0)
579         {
580                 MM_GetNewBlock(mm);
581                 mm->mmnew->next = scan->next;
582                 scan->next = mm->mmnew;
583                 mm->mmnew->start = segstart+seglength;
584                 mm->mmnew->length = extra;
585                 mm->mmnew->attributes = LOCKBIT;
586         }//else if(segm>0) goto segu;
587
588 }
589
590 //==========================================================================
591
592 /*
593 ====================
594 =
595 = MML_ClearBlock
596 =
597 = We are out of blocks, so free a purgable block
598 =
599 ====================
600 */
601
602 void MML_ClearBlock(mminfo_t *mm)
603 {
604         mmblocktype huge *scan,huge *last;
605
606         scan = mm->mmhead->next;
607
608         while(scan)
609         {
610                 if(!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS))
611                 {
612                         MM_FreePtr(scan->useptr, mm);
613                         return;
614                 }
615                 scan = scan->next;
616         }
617
618         printf("MM_ClearBlock: No purgable blocks!\n");
619 }
620
621
622 //==========================================================================
623
624 /*
625 ===================
626 =
627 = MM_Startup
628 =
629 = Grabs all space from turbo with malloc/farmalloc
630 = Allocates bufferseg misc buffer
631 =
632 ===================
633 */
634
635 void MM_Startup(mminfo_t *mm, mminfotype *mmi)
636 {
637         int i;
638         dword length,seglength;
639         //dword length; word seglength;
640         void huge       *start;
641         word    segstart;//,endfree;
642
643         if(mm->mmstarted)
644                 MM_Shutdown(mm);
645
646         mm->mmstarted = true;
647         mm->bombonerror = true;
648
649 //
650 // set up the linked list (everything in the free list;
651 //
652         //printf("              linked list making!\n");
653         mm->mmhead = NULL;
654         mm->mmfree = &(mm->mmblocks[0]);
655         for(i=0;i<MAXBLOCKS-1;i++)
656         {
657                 mm->mmblocks[i].next = &(mm->mmblocks[i+1]);
658         }
659         mm->mmblocks[i].next = NULL;
660
661 //
662 // locked block of all memory until we punch out free space
663 //
664         //printf("              newblock making!\n");
665         MM_GetNewBlock(mm);
666         mm->mmhead = mm->mmnew;                         // this will allways be the first node
667         mm->mmnew->start = 0;
668         mm->mmnew->length = 0xffff;
669         mm->mmnew->attributes = LOCKBIT;
670         mm->mmnew->next = NULL;
671         mm->mmrover = mm->mmhead;
672
673 //
674 // get all available near conventional memory segments
675 //
676 //----  length=coreleft();
677         printf("                nearheap making!\n");
678         _heapgrow();
679         length=_memmax();//(dword)GetFreeSize();
680         start = (void huge *)(mm->nearheap = malloc(length));
681         length -= 16-(FP_OFF(start)&15);
682         length -= SAVENEARHEAP;
683         seglength = length / 16;                        // now in paragraphs
684         segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
685         MML_UseSpace(segstart,seglength, mm);
686         mmi->nearheap = length;
687         printf("start=%FP       segstart=%X     seglen=%lu      len=%lu\n", start, segstart, seglength, length);
688         printf("                near heap ok!\n");
689
690 //
691 // get all available far conventional memory segments
692 //
693 //----  length=farcoreleft();
694         printf("                farheap making!\n");
695         _fheapgrow();
696         length=(dword)GetFarFreeSize();//0xffffUL*4UL;
697         //start = mm->farheap = halloc(length, 1);
698         start = mm->farheap = _fmalloc(length);
699         length -= 16-(FP_OFF(start)&15);
700         length -= SAVEFARHEAP;
701         seglength = length / 16;                        // now in paragraphs
702         segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;
703         MML_UseSpace(segstart,seglength, mm);
704         mmi->farheap = length;
705         printf("start=%FP       segstart=%X     seglen=%lu      len=%lu\n", start, segstart, seglength, length);
706         printf("                far heap ok!\n");
707
708         mmi->mainmem = mmi->nearheap + mmi->farheap;
709
710         getch();
711
712 //
713 // detect EMS and allocate up to 64K at page frame
714 //
715 printf("                EMS1\n");
716 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");     //bug!
717         mmi->EMSmem = 0;
718         for(i = 1;i < __argc;i++)
719         {
720                 if(US_CheckParm(__argv[i],ParmStringsexmm) == 0)
721                         goto emsskip;                           // param NOEMS
722         }
723 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");     //bug!
724         if(MML_CheckForEMS())
725         {
726 printf("                EMS2\n");
727 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");     //bug!
728                 MML_SetupEMS(mm);                                       // allocate space
729 printf("                EMS3\n");
730 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");     //bug!
731                 //TODO: EMS4! AND EMS 3.2 MASSIVE DATA HANDLMENT!
732                 MML_UseSpace(mm->EMSpageframe,(MAPPAGES)*0x4000lu, mm);
733 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");     //bug!
734 printf("                EMS4\n");
735                 //if(mm->EMSVer<0x40)
736                         MM_MapEMS(mm, mmi);                                     // map in used pages
737                 //else
738                         //MM_MapXEMS(mm, mmi);                                  // map in used pages
739         }
740
741 //
742 // detect XMS and get upper memory blocks
743 //
744 emsskip:
745         mmi->XMSmem = 0;
746         for(i = 1;i < __argc;i++)
747         {
748                 if(US_CheckParm(__argv[i],ParmStringsexmm) == 0)
749                         goto xmsskip;                           // param NOXMS
750         }
751 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");     //bug!
752         if(MML_CheckForXMS(mm))
753         {
754 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");     //bug!
755 printf("                XMS!\n");
756                 MML_SetupXMS(mm, mmi);                                  // allocate as many UMBs as possible
757         }
758
759 //
760 // allocate the misc buffer
761 //
762 xmsskip:
763         mm->mmrover = mm->mmhead;               // start looking for space after low block
764
765         MM_GetPtr(&(mm->bufferseg),BUFFERSIZE, mm, mmi);
766 }
767
768 //==========================================================================
769
770 /*
771 ====================
772 =
773 = MM_Shutdown
774 =
775 = Frees all conventional, EMS, and XMS allocated
776 =
777 ====================
778 */
779
780 void MM_Shutdown(mminfo_t *mm)
781 {
782         if(!(mm->mmstarted))
783                 return;
784
785         _ffree(mm->farheap);    printf("                far freed\n");
786         free(mm->nearheap);     printf("                near freed\n");
787         if(MML_CheckForEMS()){ MML_ShutdownEMS(mm); printf("            EMS freed\n"); }
788         if(MML_CheckForXMS(mm)){ MML_ShutdownXMS(mm); printf("          XMS freed\n"); }
789 }
790
791 //==========================================================================
792
793 /*
794 ====================
795 =
796 = MM_GetPtr
797 =
798 = Allocates an unlocked, unpurgable block
799 =
800 ====================
801 */
802
803 void MM_GetPtr(memptr *baseptr,dword size, mminfo_t *mm, mminfotype *mmi)
804 {
805         mmblocktype huge *scan,huge *lastscan,huge *endscan,huge *purge,huge *next;
806         int                     search;
807         unsigned        needed,startseg;
808
809         needed = (size+15)/16;          // convert size from bytes to paragraphs
810
811         MM_GetNewBlock(mm);                             // fill in start and next after a spot is found
812         mm->mmnew->length = needed;
813         mm->mmnew->useptr = baseptr;
814         mm->mmnew->attributes = BASEATTRIBUTES;
815
816         for(search = 0; search<3; search++)
817         {
818         //
819         // first search:        try to allocate right after the rover, then on up
820         // second search:       search from the head pointer up to the rover
821         // third search:        compress memory, then scan from start
822                 if(search == 1 && mm->mmrover == mm->mmhead)
823                         search++;
824
825                 switch(search)
826                 {
827                 case 0:
828                         lastscan = mm->mmrover;
829                         scan = mm->mmrover->next;
830                         endscan = NULL;
831                         break;
832                 case 1:
833                         lastscan = mm->mmhead;
834                         scan = mm->mmhead->next;
835                         endscan = mm->mmrover;
836                         break;
837                 case 2:
838                         MM_SortMem(mm);
839                         lastscan = mm->mmhead;
840                         scan = mm->mmhead->next;
841                         endscan = NULL;
842                         break;
843                 }
844
845                 startseg = lastscan->start + lastscan->length;
846
847                 while(scan != endscan)
848                 {
849                         if(scan->start - startseg >= needed)
850                         {
851                         //
852                         // got enough space between the end of lastscan and
853                         // the start of scan, so throw out anything in the middle
854                         // and allocate the new block
855                         //
856                                 purge = lastscan->next;
857                                 lastscan->next = mm->mmnew;
858                                 mm->mmnew->start = *(unsigned *)baseptr = startseg;
859                                 mm->mmnew->next = scan;
860                                 while(purge != scan)
861                                 {       // free the purgable block
862                                         next = purge->next;
863                                         MM_FreeBlock(purge, mm);
864                                         purge = next;           // purge another if not at scan
865                                 }
866                                 mm->mmrover = mm->mmnew;
867                                 return; // good allocation!
868                         }
869
870                         //
871                         // if this block is purge level zero or locked, skip past it
872                         //
873                         if((scan->attributes & LOCKBIT)
874                                 || !(scan->attributes & PURGEBITS) )
875                         {
876                                 lastscan = scan;
877                                 startseg = lastscan->start + lastscan->length;
878                         }
879
880
881                         scan=scan->next;                // look at next line
882                 }
883         }
884
885         if (mm->bombonerror)
886         {
887                 printf(OUT_OF_MEM_MSG,(size-mmi->nearheap));
888                 exit(-5);
889         }
890         else
891                 mm->mmerror = true;
892 }
893
894 //==========================================================================
895
896 /*
897 ====================
898 =
899 = MM_FreePtr
900 =
901 = Allocates an unlocked, unpurgable block
902 =
903 ====================
904 */
905
906 void MM_FreePtr(memptr *baseptr, mminfo_t *mm)
907 {
908         mmblocktype huge *scan,huge *last;
909
910         last = mm->mmhead;
911         scan = last->next;
912
913         if(baseptr == mm->mmrover->useptr)      // removed the last allocated block
914                 mm->mmrover = mm->mmhead;
915
916         while(scan->useptr != baseptr && scan)
917         {
918                 last = scan;
919                 scan = scan->next;
920         }
921
922         if(!scan)
923         {
924                 printf("MM_FreePtr: Block not found!\n");
925                 return;
926         }
927
928         last->next = scan->next;
929
930         MM_FreeBlock(scan, mm);
931 }
932 //==========================================================================
933
934 /*
935 =====================
936 =
937 = MM_SetPurge
938 =
939 = Sets the purge level for a block (locked blocks cannot be made purgable)
940 =
941 =====================
942 */
943
944 void MM_SetPurge(memptr *baseptr, int purge, mminfo_t *mm)
945 {
946         mmblocktype huge *start;
947
948         start = mm->mmrover;
949
950         do
951         {
952                 if(mm->mmrover->useptr == baseptr)
953                         break;
954
955                 mm->mmrover = mm->mmrover->next;
956
957                 if(!mm->mmrover)
958                         mm->mmrover = mm->mmhead;
959                 else if(mm->mmrover == start)
960                 {
961                         printf("MM_SetPurge: Block not found!");
962                         return;
963                 }
964
965         } while(1);
966
967         mm->mmrover->attributes &= ~PURGEBITS;
968         mm->mmrover->attributes |= purge;
969 }
970
971 //==========================================================================
972
973 /*
974 =====================
975 =
976 = MM_SetLock
977 =
978 = Locks / unlocks the block
979 =
980 =====================
981 */
982
983 void MM_SetLock(memptr *baseptr, boolean locked, mminfo_t *mm)
984 {
985         mmblocktype huge *start;
986
987         start = mm->mmrover;
988
989         do
990         {
991                 if(mm->mmrover->useptr == baseptr)
992                         break;
993
994                 mm->mmrover = mm->mmrover->next;
995
996                 if(!mm->mmrover)
997                         mm->mmrover = mm->mmhead;
998                 else if(mm->mmrover == start)
999                 {
1000                         printf("MM_SetLock: Block not found!");
1001                         return;
1002                 }
1003
1004         } while(1);
1005
1006         mm->mmrover->attributes &= ~LOCKBIT;
1007         mm->mmrover->attributes |= locked*LOCKBIT;
1008 }
1009
1010 //==========================================================================
1011
1012 /*
1013 =====================
1014 =
1015 = MM_SortMem
1016 =
1017 = Throws out all purgable stuff and compresses movable blocks
1018 =
1019 =====================
1020 */
1021
1022 void MM_SortMem(mminfo_t *mm)
1023 {
1024         mmblocktype huge *scan,huge *last,huge *next;
1025         unsigned        start,length,source,dest,oldborder;
1026         int                     playing;
1027
1028         //
1029         // lock down a currently playing sound
1030         //
1031 /*++++  playing = SD_SoundPlaying ();
1032         if(playing)
1033         {
1034                 switch (SoundMode)
1035                 {
1036                 case sdm_PC:
1037                         playing += STARTPCSOUNDS;
1038                         break;
1039                 case sdm_AdLib:
1040                         playing += STARTADLIBSOUNDS;
1041                         break;
1042                 }
1043                 MM_SetLock(&(memptr)audiosegs[playing],true);
1044         }
1045
1046
1047         SD_StopSound();*/
1048 //      oldborder = bordercolor;
1049 //      VW_ColorBorder (15);
1050
1051         if(beforesort)
1052                 beforesort();
1053
1054         scan = mm->mmhead;
1055
1056         last = NULL;            // shut up compiler warning
1057
1058         while(scan)
1059         {
1060                 if(scan->attributes & LOCKBIT)
1061                 {
1062                 //
1063                 // block is locked, so try to pile later blocks right after it
1064                 //
1065                         start = scan->start + scan->length;
1066                 }
1067                 else
1068                 {
1069                         if(scan->attributes & PURGEBITS)
1070                         {
1071                         //
1072                         // throw out the purgable block
1073                         //
1074                                 next = scan->next;
1075                                 MM_FreeBlock(scan, mm);
1076                                 last->next = next;
1077                                 scan = next;
1078                                 continue;
1079                         }
1080                         else
1081                         {
1082                         //
1083                         // push the non purgable block on top of the last moved block
1084                         //
1085                                 if(scan->start != start)
1086                                 {
1087                                         length = scan->length;
1088                                         source = scan->start;
1089                                         dest = start;
1090                                         while(length > 0xf00)
1091                                         {
1092                                                 movedata(source,0,dest,0,0xf00*16);
1093                                                 length -= 0xf00;
1094                                                 source += 0xf00;
1095                                                 dest += 0xf00;
1096                                         }
1097                                         movedata(source,0,dest,0,length*16);
1098
1099                                         scan->start = start;
1100                                         *(unsigned *)scan->useptr = start;
1101                                 }
1102                                 start = scan->start + scan->length;
1103                         }
1104                 }
1105
1106                 last = scan;
1107                 scan = scan->next;              // go to next block
1108         }
1109
1110         mm->mmrover = mm->mmhead;
1111
1112         if(aftersort)
1113                 aftersort();
1114
1115 //      VW_ColorBorder (oldborder);
1116
1117 /*++++  if(playing)
1118                 MM_SetLock(&(memptr)audiosegs[playing],false);*/
1119 }
1120
1121
1122 //==========================================================================
1123
1124 //****#if 0
1125 /*
1126 =====================
1127 =
1128 = MM_ShowMemory
1129 =
1130 =====================
1131 */
1132
1133 void MM_ShowMemory(/*page_t *page, */mminfo_t *mm)
1134 {
1135         mmblocktype huge *scan;
1136         word color,temp;
1137         long    end,owner;
1138         word chx,chy;
1139         byte    scratch[160],str[16];
1140
1141 //****  VW_SetDefaultColors();
1142 //****  VW_SetLineWidth(40);
1143 //++++mh        temp = bufferofs;
1144 //++++mh        bufferofs = 0;
1145 //****  VW_SetScreen (0,0);
1146
1147         scan = mm->mmhead;
1148
1149         end = -1;
1150
1151 CA_OpenDebug ();
1152
1153         chx=0;
1154         chy=0;
1155
1156         while(scan)
1157         {
1158                 if(scan->attributes & PURGEBITS)
1159                         color = 5;              // dark purple = purgable
1160                 else
1161                         color = 9;              // medium blue = non purgable
1162                 if(scan->attributes & LOCKBIT)
1163                         color = 12;             // red = locked
1164                 if(scan->start<=end)
1165                 {
1166                         //printf(");
1167                         write(debughandle,"\nMM_ShowMemory: Memory block order currupted!\n",strlen("\nMM_ShowMemory: Memory block order currupted!\n"));
1168                         //modexprint(&page, chx, chy, 1, 0, 24, "\nMM_ShowMemory: Memory block order currupted!\n");
1169                         return;
1170                 }
1171                 end = scan->start+scan->length-1;
1172                 chy = scan->start/320;
1173                 chx = scan->start%320;
1174                                 //modexhlin(page, scan->start, (unsigned)end, chy, color);
1175                                 //for(chx=scan->start;chx+4>=(word)end;chx+=4)
1176                                 //{
1177 //++++                                  modexClearRegion(page, chx, chy, 4, 4, color);
1178                                 //}
1179
1180 //++++          VW_Hlin(scan->start,(unsigned)end,0,color);
1181
1182 //++++          VW_Plot(scan->start,0,15);
1183 //++++                          modexClearRegion(page, chx, chy, 4, 4, 15);
1184                 if(scan->next->start > end+1)
1185 //++++                  VW_Hlin(end+1,scan->next->start,0,0);   // black = free
1186                         //for(chx=scan->next->start;chx+4>=(word)end+1;chx+=4)
1187                         //{
1188 //++++                          chx+=scan->next->start;
1189 //++++                          modexClearRegion(page, chx, chy, 4, 4, 2);
1190                         //}
1191                                         //modexhlin(page, end+1,scan->next->start, chy, 0);
1192
1193 /*
1194                 end = scan->length-1;
1195                 y = scan->start/320;
1196                 x = scan->start%320;
1197                 VW_Hlin(x,x+end,y,color);
1198                 VW_Plot(x,y,15);
1199                 if (scan->next && scan->next->start > end+1)
1200                         VW_Hlin(x+end+1,x+(scan->next->start-scan->start),y,0); // black = free
1201 */
1202
1203 //****#if 0
1204 printf("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");     //bug!
1205 strcpy(scratch,"Seg:");
1206 ultoa (scan->start,str,16);
1207 strcat (scratch,str);
1208 strcat (scratch,"\tSize:");
1209 ultoa ((dword)scan->length,str,10);
1210 strcat (scratch,str);
1211 strcat (scratch,"\tOwner:0x");
1212 owner = (unsigned)scan->useptr;
1213 ultoa (owner,str,16);
1214 strcat (scratch,str);
1215 strcat (scratch,"\n");
1216 write(debughandle,scratch,strlen(scratch));
1217 //modexprint(page, chx, chy, 1, 0, 24, &scratch);
1218 //++++chy+=4;
1219 //fprintf(stdout, "%s", scratch);
1220 //****#endif
1221
1222                 scan = scan->next;
1223         }
1224
1225 CA_CloseDebug ();
1226
1227 //++++mh        IN_Ack();
1228 //****  VW_SetLineWidth(64);
1229 //++++mh        bufferofs = temp;
1230 }
1231 //****#endif
1232
1233 //==========================================================================
1234
1235 /*
1236 =====================
1237 =
1238 = MM_DumpData
1239 =
1240 =====================
1241 */
1242
1243 void MM_DumpData(mminfo_t *mm)
1244 {
1245         mmblocktype far *scan,far *best;
1246         long    lowest,oldlowest;
1247         word    owner;
1248         byte    lock,purge;
1249         FILE    *dumpfile;
1250
1251
1252         //++++free(mm->nearheap);
1253         dumpfile = fopen ("mmdump.16","w");
1254         if (!dumpfile){
1255                 printf("MM_DumpData: Couldn't open MMDUMP.16!\n");
1256                 return;
1257         }
1258
1259         lowest = -1;
1260         do
1261         {
1262                 oldlowest = lowest;
1263                 lowest = 0xffff;
1264
1265                 scan = mm->mmhead;
1266                 while (scan)
1267                 {
1268                         owner = (word)scan->useptr;
1269
1270                         if (owner && owner<lowest && owner > oldlowest)
1271                         {
1272                                 best = scan;
1273                                 lowest = owner;
1274                         }
1275
1276                         scan = scan->next;
1277                 }
1278
1279                 if (lowest != 0xffff)
1280                 {
1281                         if (best->attributes & PURGEBITS)
1282                                 purge = 'P';
1283                         else
1284                                 purge = '-';
1285                         if (best->attributes & LOCKBIT)
1286                                 lock = 'L';
1287                         else
1288                                 lock = '-';
1289                         fprintf (dumpfile,"0x%p (%c%c) = %u\n"
1290                         ,(word)lowest,lock,purge,best->length);
1291                 }
1292
1293         } while (lowest != 0xffff);
1294
1295         fclose(dumpfile);
1296         printf("MMDUMP.16 created.\n");
1297 }
1298
1299 //==========================================================================
1300
1301
1302 /*
1303 ======================
1304 =
1305 = MM_UnusedMemory
1306 =
1307 = Returns the total free space without purging
1308 =
1309 ======================
1310 */
1311
1312 dword MM_UnusedMemory(mminfo_t *mm)
1313 {
1314         dword free;
1315         mmblocktype huge *scan;
1316
1317         free = 0;
1318         scan = mm->mmhead;
1319
1320         while(scan->next)
1321         {
1322                 free += scan->next->start - (scan->start + scan->length);
1323                 scan = scan->next;
1324         }
1325
1326 //      return free*16l;
1327         return free;
1328 }
1329
1330 //==========================================================================
1331
1332
1333 /*
1334 ======================
1335 =
1336 = MM_TotalFree
1337 =
1338 = Returns the total free space with purging
1339 =
1340 ======================
1341 */
1342
1343 dword MM_TotalFree(mminfo_t *mm)
1344 {
1345         dword free;
1346         mmblocktype huge *scan;
1347
1348         free = 0;
1349         scan = mm->mmhead;
1350
1351         while(scan->next)
1352         {
1353                 if((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))
1354                         free += scan->length;
1355                 free += scan->next->start - (scan->start + scan->length);
1356                 scan = scan->next;
1357         }
1358
1359 //      return free*16l;
1360         return free;
1361 }
1362
1363 //==========================================================================
1364
1365 /*
1366 =====================
1367 =
1368 = MM_Report
1369 =
1370 =====================
1371 */
1372
1373 void MM_Report(/*page_t *page, */mminfo_t *mm, mminfotype *mmi)
1374 {
1375         if(MML_CheckForEMS())
1376         {
1377                 printf("EMM v%x.%x available\n", mm->EMSVer>>4,mm->EMSVer&0x0F);
1378                 printf("totalEMSpages=%u\n", mm->totalEMSpages);
1379                 printf("freeEMSpages=%u\n", mm->freeEMSpages);
1380                 printf("EMSpageframe=%x\n", mm->EMSpageframe);
1381         }
1382         if(MML_CheckForXMS(mm)) printf("XMSaddr=%X\n", *XMSaddr);
1383         printf("near=%lu\n", mmi->nearheap);
1384         printf("far=%lu\n", mmi->farheap);
1385         printf("EMSmem=%lu\n", mmi->EMSmem);
1386         printf("XMSmem=%lu\n", mmi->XMSmem);
1387         printf("mainmem=%lu\n", mmi->mainmem);
1388         printf("UnusedMemory=%lu\n", MM_UnusedMemory(mm));
1389         printf("TotalFree=%lu\n", MM_TotalFree(mm));
1390         //mmi->nearheap+mmi->farheap+
1391         printf("TotalUsed=%lu\n", mmi->mainmem+mmi->EMSmem+mmi->XMSmem);//+);
1392 //      printf("\n");
1393 //      printf("UnusedMemory=%lu kb\n", MM_UnusedMemory()/10248);
1394 //      printf("TotalFree=%lu kb\n", MM_TotalFree()/10248);
1395 }
1396
1397 //==========================================================================
1398
1399 /*
1400 =====================
1401 =
1402 = MM_EMSerr
1403 =
1404 =====================
1405 */
1406
1407 void MM_EMSerr(byte *stri, byte err)
1408 {
1409         //Returns a text string describing the error code in EMS.Error.
1410         switch(err)
1411         {
1412                 case 0x0:
1413                         strcat(stri, "successful");
1414                 break;
1415                 case 0x80:
1416                         strcat(stri, "internal error");
1417                 break;
1418                 case 0x81:
1419                         strcat(stri, "hardware malfunction");
1420                 break;
1421                 case 0x82:
1422                         strcat(stri, "busy .. retry later");
1423                 break;
1424                 case 0x83:
1425                         strcat(stri, "invalid handle");
1426                 break;
1427                 case 0x84:
1428                         strcat(stri, "undefined function requested by application");
1429                 break;
1430                 case 0x85:
1431                         strcat(stri, "no more handles available");
1432                 break;
1433                 case 0x86:
1434                         strcat(stri, "error in save or restore of mapping context");
1435                 break;
1436                 case 0x87:
1437                         strcat(stri, "insufficient memory pages in system");
1438                 break;
1439                 case 0x88:
1440                         strcat(stri, "insufficient memory pages available");
1441                 break;
1442                 case 0x89:
1443                         strcat(stri, "zero pages requested");
1444                 break;
1445                 case 0x8A:
1446                         strcat(stri, "invalid logical page number encountered");
1447                 break;
1448                 case 0x8B:
1449                         strcat(stri, "invalid physical page number encountered");
1450                 break;
1451                 case 0x8C:
1452                         strcat(stri, "page-mapping hardware state save area is full");
1453                 break;
1454                 case 0x8D:
1455                         strcat(stri, "save of mapping context failed");
1456                 break;
1457                 case 0x8E:
1458                         strcat(stri, "restore of mapping context failed");
1459                 break;
1460                 case 0x8F:
1461                         strcat(stri, "undefined subfunction");
1462                 break;
1463                 case 0x90:
1464                         strcat(stri, "undefined attribute type");
1465                 break;
1466                 case 0x91:
1467                         strcat(stri, "feature not supported");
1468                 break;
1469                 case 0x92:
1470                         strcat(stri, "successful, but a portion of the source region has been overwritten");
1471                 break;
1472                 case 0x93:
1473                         strcat(stri, "length of source or destination region exceeds length of region allocated to either source or destination handle");
1474                 break;
1475                 case 0x94:
1476                         strcat(stri, "conventional and expanded memory regions overlap");
1477                 break;
1478                 case 0x95:
1479                         strcat(stri, "offset within logical page exceeds size of logical page");
1480                 break;
1481                 case 0x96:
1482                         strcat(stri, "region length exceeds 1 MB");
1483                 break;
1484                 case 0x97:
1485                         strcat(stri, "source and destination EMS regions have same handle and overlap");
1486                 break;
1487                 case 0x98:
1488                         strcat(stri, "memory source or destination type undefined");
1489                 break;
1490                 case 0x9A:
1491                         strcat(stri, "specified alternate map register or DMA register set not supported");
1492                 break;
1493                 case 0x9B:
1494                         strcat(stri, "all alternate map register or DMA register sets currently allocated");
1495                 break;
1496                 case 0x9C:
1497                         strcat(stri, "alternate map register or DMA register sets not supported");
1498                 break;
1499                 case 0x9D:
1500                         strcat(stri, "undefined or unallocated alternate map register or DMA register set");
1501                 break;
1502                 case 0x9E:
1503                         strcat(stri, "dedicated DMA channels not supported");
1504                 break;
1505                 case 0x9F:
1506                         strcat(stri, "specified dedicated DMA channel not supported");
1507                 break;
1508                 case 0xA0:
1509                         strcat(stri, "no such handle name");
1510                 break;
1511                 case 0xA1:
1512                         strcat(stri, "a handle found had no name, or duplicate handle name");
1513                 break;
1514                 case 0xA2:
1515                         strcat(stri, "attempted to wrap around 1M conventional address space");
1516                 break;
1517                 case 0xA3:
1518                         strcat(stri, "source array corrupted");
1519                 break;
1520                 case 0xA4:
1521                         strcat(stri, "operating system denied access");
1522                 break;
1523                 default:
1524                         strcat(stri, "undefined error");
1525         }
1526 }
1527
1528 //==========================================================================
1529
1530 /*
1531 =====================
1532 =
1533 = MM_BombOnError
1534 =
1535 =====================
1536 */
1537
1538 void MM_BombOnError(boolean bomb, mminfo_t *mm)
1539 {
1540         mm->bombonerror = bomb;
1541 }
1542
1543 void MM_GetNewBlock(mminfo_t *mm)
1544 {
1545         if(!mm->mmfree)
1546                 MML_ClearBlock(mm);
1547         mm->mmnew=mm->mmfree;
1548         mm->mmfree=mm->mmfree->next;
1549         /*if(!(mm->mmnew=mm->mmfree))
1550         {
1551                 printf("MM_GETNEWBLOCK: No free blocks!");
1552                 return;
1553         }
1554         mm->mmfree=mm->mmfree->next;*/
1555 }
1556
1557 void MM_FreeBlock(mmblocktype *x, mminfo_t *mm)
1558 {
1559         x->useptr=NULL;
1560         x->next=mm->mmfree;
1561         mm->mmfree=x;
1562 }
1563
1564 void MM_seguin(void)
1565 {
1566         __asm
1567         {
1568                 push    ds
1569                 mov     ax,ds
1570                 inc             ax
1571                 mov     ds,ax
1572         }
1573 }
1574
1575 void MM_segude(void)
1576 {
1577         __asm
1578         {
1579                 pop ds
1580         }
1581 }
1582
1583 /*
1584 pull data from far and put it into ds var
1585 mov ax,es:si
1586 mov x,ax
1587 */
1588 /*
1589 ss stack segment
1590 sp top of stack
1591 bp bottem of stack
1592 */