]> 4ch.mooo.com Git - 16.git/blob - src/lib/16_pm.c
[16_ca needs huge amounts of work and I should remember what needs to be done soon...
[16.git] / src / lib / 16_pm.c
1 /* Project 16 Source Code~\r
2  * Copyright (C) 2012-2017 sparky4 & pngwen & andrius4669 & joncampbell123 & yakui-lover\r
3  *\r
4  * This file is part of Project 16.\r
5  *\r
6  * Project 16 is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 3 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * Project 16 is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>, or\r
18  * write to the Free Software Foundation, Inc., 51 Franklin Street,\r
19  * Fifth Floor, Boston, MA 02110-1301 USA.\r
20  *\r
21  */\r
22 \r
23 //\r
24 //      ID_PM.C\r
25 //      Id Engine's Page Manager v1.0\r
26 //      Primary coder: Jason Blochowiak\r
27 //\r
28 \r
29 #include "src/lib/16_pm.h"\r
30 #pragma hdrstop\r
31 \r
32 //      Main Mem specific variables\r
33         //boolean                       MainPresent;\r
34 /*      memptr                  MainMemPages[PMMaxMainMem];\r
35         PMBlockAttr             gvar->pm.mm.MainMemUsed[PMMaxMainMem];\r
36         int                             gvar->pm.mm.MainPagesAvail;*/\r
37 \r
38 //      EMS specific variables\r
39         //boolean                       EMSPresent;\r
40 /*      word                    gvar->pm.emm.EMSAvail,gvar->pm.emm.EMSPagesAvail,gvar->pm.emm.EMSHandle,\r
41                                         gvar->pm.emm.EMSPageFrame,gvar->pm.emm.EMSPhysicalPage;\r
42         gvar->pm.emm.EMSListStruct      gvar->pm.emm.EMSList[EMSFrameCount];*/\r
43 \r
44 //      XMS specific variables\r
45         //boolean                       XMSPresent;\r
46         //word                  gvar->pm.xmm.XMSAvail,gvar->pm.xmm.XMSPagesAvail,gvar->pm.xmm.XMSHandle;\r
47         dword                   XMSDriver;      //hard to put in gvar\r
48         word                            XMSVer; //hard to put in gvar\r
49 /*      int                             gvar->pm.xmm.XMSProtectPage = -1;\r
50 \r
51 //      File specific variables\r
52         char                    gvar->pm.fi.PageFileName[13] = {"VSWAP."};\r
53         int                             PageFile = -1;\r
54         word                    gvar->pm.fi.ChunksInFile;\r
55         word                    PMSpriteStart,PMSoundStart;*/\r
56 \r
57 //      General usage variables\r
58 /*      boolean                 PMStarted,\r
59                                         gvar->pm.PMPanicMode,\r
60                                         gvar->pm.PMThrashing;\r
61         word                    gvar->pm.XMSPagesUsed,\r
62                                         gvar->pm.EMSPagesUsed,\r
63                                         MainPagesUsed,\r
64                                         gvar->pm.PMNumBlocks;\r
65         long                    PMFrameCount;\r
66         PageListStruct  far *gvar->pm.PMPages,\r
67                                         _seg *gvar->pm.PMSegPages;*/\r
68 \r
69 static union REGS CPURegs;\r
70 \r
71 #define _AX CPURegs.x.ax\r
72 #define _BX CPURegs.x.bx\r
73 #define _CX CPURegs.x.cx\r
74 #define _DX CPURegs.x.dx\r
75 \r
76 #define _SI CPURegs.x.si\r
77 \r
78 #define _AH CPURegs.h.ah\r
79 #define _AL CPURegs.h.al\r
80 #define _BH CPURegs.h.bh\r
81 #define _BL CPURegs.h.bl\r
82 #define _CH CPURegs.h.ch\r
83 #define _CL CPURegs.h.cl\r
84 #define _DH CPURegs.h.dh\r
85 #define _DL CPURegs.h.dl\r
86 \r
87 #ifdef __WATCOMC__\r
88 #define geninterrupt(n) int86(n,&CPURegs,&CPURegs);\r
89 #endif\r
90 \r
91 static  char            *ParmStrings[] = {"nomain","noems","noxms",nil};\r
92 \r
93 /////////////////////////////////////////////////////////////////////////////\r
94 //\r
95 //      EMS Management code\r
96 //\r
97 /////////////////////////////////////////////////////////////////////////////\r
98 \r
99 //\r
100 //      PML_MapEMS() - Maps a logical page to a physical page\r
101 //\r
102 byte\r
103 PML_MapEMS(word logical, byte physical, global_game_variables_t *gvar)\r
104 {\r
105         byte    err=0, str[160];\r
106         unsigned        EMShandle;\r
107         //int   i;\r
108 \r
109         boolean errorflag=false;\r
110         EMShandle=gvar->pm.emm.EMSHandle;\r
111 \r
112         __asm {\r
113                 mov     ah,EMS_MAPPAGE\r
114                 mov     al,physical\r
115                 mov     bx,logical\r
116                 mov     dx,EMShandle\r
117                 int     EMS_INT\r
118                 or      ah,ah\r
119                 jnz     errorme\r
120                 jmp     Endme\r
121 #ifdef __BORLANDC__\r
122         }\r
123 #endif\r
124                 errorme:\r
125 #ifdef __BORLANDC__\r
126         __asm {\r
127 #endif\r
128                 mov     err,ah\r
129                 mov     errorflag,1\r
130 #ifdef __BORLANDC__\r
131         }\r
132 #endif\r
133                 Endme:\r
134 #ifdef __WATCOMC__\r
135         }\r
136 #endif\r
137         if(errorflag==true)\r
138         {\r
139                 strcpy(str,"MM_MapEMS: EMS error ");\r
140                 MM_EMSerr(str, err);\r
141                 printf("%s\n",str);\r
142                 Quit (gvar, "PML_MapEMS: Page mapping failed\n");\r
143                 return err;\r
144         }\r
145         return 0;\r
146 }\r
147 \r
148 //\r
149 //      PML_StartupEMS() - Sets up EMS for Page Mgr's use\r
150 //              Checks to see if EMS driver is present\r
151 //      Verifies that EMS hardware is present\r
152 //              Make sure that EMS version is 3.2 or later\r
153 //              If there's more than our minimum (2 pages) available, allocate it (up\r
154 //                      to the maximum we need)\r
155 //\r
156 //      Please call MML_CheckForEMS() before calling this function.\r
157 //      MML_CheckForEMS is not local despite the name wwww.\r
158 //\r
159 \r
160 boolean\r
161 PML_StartupEMS(global_game_variables_t *gvar)\r
162 {\r
163         int             i;\r
164 #ifdef __PM__NOHOGEMS__\r
165         long    size;\r
166 #endif\r
167         byte    err=0, str[64];\r
168 \r
169         boolean errorflag=false;\r
170         unsigned        EMSVer;\r
171         unsigned        totalEMSpages,freeEMSpages,EMSPageFrame,EMSHandle,EMSAvail;\r
172         totalEMSpages = freeEMSpages = EMSPageFrame = EMSHandle = EMSAvail = EMSVer = 0;        // set all to 0~\r
173         gvar->pm.emm.EMSPresent = false;                        // Assume that we'll fail\r
174         gvar->pm.emm.EMSAvail = gvar->mmi.EMSmem = 0;\r
175 \r
176         __asm {\r
177                 //MML_CheckForEMS() takes care of what the code did here\r
178                 mov     ah,EMS_STATUS\r
179                 int     EMS_INT\r
180                 jc      error1                  // make sure EMS hardware is present\r
181 \r
182                 mov     ah,EMS_VERSION\r
183                 int     EMS_INT                 // only work on EMS 3.2 or greater (silly, but...)\r
184                 or      ah,ah\r
185                 jnz     error1\r
186                 mov     [EMSVer],ax             // set EMSVer\r
187                 cmp     al,0x32                 // only work on ems 3.2 or greater\r
188                 jb      error1\r
189 \r
190                 mov     ah,EMS_GETFRAME\r
191                 int     EMS_INT                 // find the page frame address\r
192                 or      ah,ah\r
193                 jnz     error1\r
194                 mov     [EMSPageFrame],bx\r
195 \r
196                 mov     ah,EMS_GETPAGES\r
197                 int     EMS_INT                 // find out how much EMS is there\r
198                 or      ah,ah\r
199                 jnz     error1\r
200                 or      bx,bx\r
201                 jz      noEMS                   // no EMS at all to allocate\r
202                 cmp     bx,2\r
203                 jl      noEMS                   // Require at least 2 pages (32k)\r
204                 mov     [totalEMSpages],dx\r
205                 mov     [freeEMSpages],bx\r
206                 mov     [EMSAvail],bx\r
207                 jmp     End1\r
208 #ifdef __BORLANDC__\r
209         }\r
210 #endif\r
211         error1:\r
212 #ifdef __BORLANDC__\r
213         __asm {\r
214 #endif\r
215                 mov     err,ah\r
216                 mov     errorflag,1\r
217                 jmp     End1\r
218 #ifdef __BORLANDC__\r
219         }\r
220 #endif\r
221 noEMS:\r
222 End1:\r
223 #ifdef __WATCOMC__\r
224         }\r
225 #endif\r
226 #ifdef __PM__NOHOGEMS__\r
227         if(errorflag==false)\r
228         {\r
229                 // Don't hog all available EMS\r
230                 size = gvar->pm.emm.EMSAvail * (long)EMSPageSize;\r
231                 if (size - (EMSPageSize * 2) > (gvar->pm.fi.ChunksInFile * (long)PMPageSize))\r
232                 {\r
233                         size = (gvar->pm.fi.ChunksInFile * (long)PMPageSize) + EMSPageSize;\r
234                         gvar->pm.emm.EMSAvail = size / EMSPageSize;\r
235                 }\r
236         }\r
237 #endif\r
238         __asm {\r
239                 mov     ah,EMS_ALLOCPAGES\r
240                 mov     bx,[EMSAvail];\r
241                 int     EMS_INT\r
242                 or      ah,ah\r
243                 jnz     error2\r
244                 mov     [EMSHandle],dx\r
245                 jmp     End2\r
246 #ifdef __BORLANDC__\r
247         }\r
248 #endif\r
249         error2:\r
250 #ifdef __BORLANDC__\r
251         __asm {\r
252 #endif\r
253                 mov     err,ah\r
254                 mov     errorflag,1\r
255                 jmp     End2\r
256 #ifdef __BORLANDC__\r
257         }//end of assembly\r
258 #endif\r
259 End2:\r
260 #ifdef __WATCOMC__\r
261         }//end of assembly\r
262 #endif\r
263         if(errorflag==true)\r
264         {\r
265                 strcpy(str,"PML_StartupEMS: EMS error ");\r
266                 MM_EMSerr(str, err);\r
267                 printf("%s\n",str);\r
268                 return(gvar->pm.emm.EMSPresent);\r
269         }\r
270         gvar->mmi.EMSmem = EMSAvail * (dword)EMSPageSize;\r
271 \r
272         // Initialize EMS mapping cache\r
273         for (i = 0;i < EMSFrameCount;i++)\r
274                 gvar->pm.emm.EMSList[i].baseEMSPage = -1;\r
275 \r
276         gvar->pm.emm.EMSPresent = true;                 // We have EMS\r
277         gvar->pm.emm.EMSPageFrame = EMSPageFrame;\r
278         gvar->pm.emm.EMSAvail = EMSAvail;\r
279         gvar->pm.emm.EMSVer = EMSVer;\r
280         gvar->pm.emm.EMSHandle = EMSHandle;\r
281         gvar->pm.emm.freeEMSpages = freeEMSpages;\r
282         gvar->pm.emm.totalEMSpages = totalEMSpages;\r
283 \r
284         return(gvar->pm.emm.EMSPresent);\r
285 }\r
286 \r
287 //\r
288 //      PML_ShutdownEMS() - If EMS was used, deallocate it\r
289 //\r
290 void\r
291 PML_ShutdownEMS(global_game_variables_t *gvar)\r
292 {\r
293         word EMSHandle;\r
294         byte err=0, str[64];\r
295 \r
296         boolean errorflag=false;\r
297         EMSHandle=gvar->pm.emm.EMSHandle;\r
298 \r
299         if (gvar->pm.emm.EMSPresent)\r
300         {\r
301                 __asm {\r
302                         mov     ah,EMS_FREEPAGES\r
303                         mov     dx,[EMSHandle]\r
304                         int     EMS_INT\r
305                         jc      errores\r
306                         jmp     Endes\r
307 #ifdef __BORLANDC__\r
308                 }\r
309 #endif\r
310                         errores:\r
311 #ifdef __BORLANDC__\r
312                 __asm {\r
313 #endif\r
314                         mov     err,ah\r
315                         mov     errorflag,1\r
316                         jmp     Endes\r
317 #ifdef __BORLANDC__\r
318                 }\r
319 #endif\r
320                         Endes:\r
321 #ifdef __WATCOMC__\r
322                 }\r
323 #endif\r
324                 if(errorflag==true)\r
325                 {\r
326                         strcpy(str,"PML_ShutdownEMS: Error freeing EMS ");\r
327                         MM_EMSerr(str, err);\r
328                         printf("%s\n",str);\r
329                         Quit (gvar, "PML_ShutdownEMS: Error freeing EMS\n");\r
330                         //return;\r
331                 }\r
332         }\r
333 }\r
334 \r
335 /////////////////////////////////////////////////////////////////////////////\r
336 //\r
337 //      XMS Management code\r
338 //\r
339 /////////////////////////////////////////////////////////////////////////////\r
340 \r
341 //\r
342 //      PML_StartupXMS() - Starts up XMS for the Page Mgr's use\r
343 //              Checks for presence of an XMS driver\r
344 //              Makes sure that there's at least a page of XMS available\r
345 //              Allocates any remaining XMS (rounded down to the nearest page size)\r
346 //\r
347 boolean\r
348 PML_StartupXMS(global_game_variables_t *gvar)\r
349 {\r
350 //TODO: translate the _REG into working assembly\r
351 //#define STARTUPXMSASM\r
352         byte err;\r
353         word XMSAvail, XMSHandle;//, XMSVer;\r
354         boolean errorflag=false;\r
355         word e=0;\r
356         gvar->pm.xmm.XMSPresent = false;                                        // Assume failure\r
357         XMSAvail = gvar->mmi.XMSmem = 0;\r
358 \r
359         __asm {\r
360                 mov     ax,0x4300\r
361                 int     XMS_INT                                         // Check for presence of XMS driver\r
362                 cmp     al,0x80\r
363                 jne     error1\r
364                 mov     e,1\r
365 \r
366                 mov     ax,0x4310\r
367                 int     XMS_INT                                                 // Get address of XMS driver\r
368                 mov     [WORD PTR XMSDriver],bx\r
369                 mov     [WORD PTR XMSDriver+2],es               // function pointer to XMS driver\r
370 \r
371                 mov     ah,XMS_VERSION\r
372                 call    [DWORD PTR XMSDriver]                                           //; Get XMS Version Number\r
373                 mov     [XMSVer],ax\r
374                 mov     e,2\r
375 \r
376 #ifdef STARTUPXMSASM\r
377                 mov     ah,XMS_QUERYFREE                        // Find out how much XMS is available\r
378                 call    [DWORD PTR XMSDriver]\r
379                 mov     XMSAvail,ax\r
380                 or      ax,ax                           // AJR: bugfix 10/8/92\r
381                 jz      error1\r
382                 mov     e,3\r
383 #endif\r
384                 jmp     End1\r
385 #ifdef __BORLANDC__\r
386         }\r
387 #endif\r
388         error1:\r
389 #ifdef __BORLANDC__\r
390         __asm {\r
391 #endif\r
392                 mov     err,bl\r
393                 mov     errorflag,1\r
394                 jmp End1\r
395 #ifdef __BORLANDC__\r
396         }\r
397 #endif\r
398 End1:\r
399 #ifdef __WATCOMC__\r
400         }\r
401 #endif\r
402         if(errorflag==true) goto error;\r
403 #ifndef STARTUPXMSASM\r
404         XMS_CALL(XMS_QUERYFREE);                        // Find out how much XMS is available\r
405         XMSAvail = _AX;\r
406         if (!_AX)                               // AJR: bugfix 10/8/92\r
407         {\r
408                 errorflag = true;\r
409                 err = _BL;\r
410                 goto error;\r
411         }\r
412         e++;\r
413 #endif\r
414 \r
415 #ifdef __DEBUG_PM__\r
416         printf("XMSVer=%02X     ", XMSVer);\r
417         printf("XMSAvail=%u\n", XMSAvail);\r
418         getch();\r
419 #endif\r
420         XMSAvail &= ~(PMPageSizeKB - 1);        // Round off to nearest page size\r
421         if (XMSAvail < (PMPageSizeKB * 2))      // Need at least 2 pages\r
422         {\r
423                 errorflag=true;\r
424                 goto error;\r
425         }\r
426 #ifdef STARTUPXMSASM\r
427         __asm {\r
428                 mov     dx,XMSAvail\r
429                 mov     ah,XMS_ALLOC                            // And do the allocation\r
430                 call    [DWORD PTR XMSDriver]\r
431                 mov     XMSHandle,dx\r
432                 or      ax,ax                           // AJR: bugfix 10/8/92\r
433                 jz      error2\r
434                 mov     e,4\r
435                 jmp     End2\r
436 #ifdef __BORLANDC__\r
437         }\r
438 #endif\r
439         error2:\r
440 #ifdef __BORLANDC__\r
441         __asm {\r
442 #endif\r
443                 mov     err,bl\r
444                 mov     errorflag,1\r
445                 jmp End2\r
446 #ifdef __BORLANDC__\r
447         }\r
448 #endif\r
449 End2:\r
450 #ifdef __WATCOMC__\r
451         }\r
452 #endif\r
453 #else\r
454         _DX = XMSAvail;\r
455         XMS_CALL(XMS_ALLOC);                            // And do the allocation\r
456         XMSHandle = _DX;\r
457         if (!_AX)                               // AJR: bugfix 10/8/92\r
458         {\r
459                 errorflag=true;\r
460                 err = _BL;\r
461                 goto error;\r
462         }\r
463         e++;\r
464 #endif\r
465 error:\r
466         if(errorflag==false)\r
467         {\r
468                 gvar->mmi.XMSmem = (dword)(XMSAvail) * 1024;\r
469                 gvar->pm.xmm.XMSAvail = XMSAvail;\r
470                 gvar->pm.xmm.XMSHandle = XMSHandle;\r
471                 //gvar->pm.xmm.XMSVer = XMSVer;\r
472                 gvar->pm.xmm.XMSPresent = true;\r
473 #ifdef __DEBUG_PM__\r
474                 printf("        XMSmem=%lu      XMSAvail=%u\n", gvar->mmi.XMSmem, XMSAvail);\r
475 #endif\r
476         }\r
477         else\r
478         {\r
479 #ifdef __DEBUG_PM__\r
480                 //printf("XMSHandle\n");\r
481                 //printf("      1=%u    2=%u    3=%u    4=%u\n", XMSHandle1, XMSHandle2, XMSHandle3, XMSHandle4);\r
482                 //printf("      2=%u    ", XMSHandle);\r
483                 //printf("      %u", gvar->pm.xmm.XMSHandle);\r
484                 printf("        err=%02X        e=%u\n", err, e);\r
485 #endif\r
486         }\r
487         return(gvar->pm.xmm.XMSPresent);\r
488 }\r
489 \r
490 //\r
491 //      PML_XMSCopy() - Copies a main/EMS page to or from XMS\r
492 //              Will round an odd-length request up to the next even value\r
493 //\r
494 void\r
495 PML_XMSCopy(boolean toxms,byte far *addr,word xmspage,word length, global_game_variables_t *gvar)\r
496 {\r
497         dword   xoffset;\r
498         struct\r
499         {\r
500                 dword   length;\r
501                 word            source_handle;\r
502                 dword   source_offset;\r
503                 word            target_handle;\r
504                 dword   target_offset;\r
505         } copy;\r
506 \r
507         if (!addr)\r
508         {\r
509                 Quit (gvar, "PML_XMSCopy: zero address\n");\r
510                 //return;\r
511         }\r
512 \r
513         xoffset = (dword)xmspage * PMPageSize;\r
514 \r
515         copy.length = (length + 1) & ~1;\r
516         copy.source_handle = toxms? 0 : gvar->pm.xmm.XMSHandle;\r
517         copy.source_offset = toxms? (long)addr : xoffset;\r
518         copy.target_handle = toxms? gvar->pm.xmm.XMSHandle : 0;\r
519         copy.target_offset = toxms? xoffset : (long)addr;\r
520 \r
521         __asm {\r
522                 push si\r
523         }\r
524         _SI = (word)&copy;\r
525         XMS_CALL(XMS_MOVE);\r
526         __asm {\r
527                 pop     si\r
528         }\r
529         if (!_AX)\r
530         {\r
531                 Quit (gvar, "PML_XMSCopy: Error on copy");\r
532                 //return;\r
533         }\r
534 }\r
535 \r
536 #if 1\r
537 #define PML_CopyToXMS(s,t,l,gvar)       PML_XMSCopy(true,(s),(t),(l),(gvar))\r
538 #define PML_CopyFromXMS(t,s,l,gvar)     PML_XMSCopy(false,(t),(s),(l),(gvar))\r
539 #else\r
540 //\r
541 //      PML_CopyToXMS() - Copies the specified number of bytes from the real mode\r
542 //              segment address to the specified XMS page\r
543 //\r
544 void\r
545 PML_CopyToXMS(byte far *source,int targetpage,word length,global_game_variables_t *gvar)\r
546 {\r
547         PML_XMSCopy(true,source,targetpage,length, global_game_variables_t *gvar);\r
548 }\r
549 \r
550 //\r
551 //      PML_CopyFromXMS() - Copies the specified number of bytes from an XMS\r
552 //              page to the specified real mode address\r
553 //\r
554 void\r
555 PML_CopyFromXMS(byte far *target,int sourcepage,word length, global_game_variables_t *gvar)\r
556 {\r
557         PML_XMSCopy(false,target,sourcepage,length, global_game_variables_t *gvar);\r
558 }\r
559 #endif\r
560 \r
561 //\r
562 //      PML_ShutdownXMS()\r
563 //\r
564 void\r
565 PML_ShutdownXMS(global_game_variables_t *gvar)\r
566 {\r
567         boolean errorflag=false;\r
568         word XMSHandle = gvar->pm.xmm.XMSHandle;\r
569         if (gvar->pm.xmm.XMSPresent)\r
570         {\r
571                 __asm {\r
572                         mov     dx,[XMSHandle]\r
573                         //XMS_CALL(XMS_FREE);\r
574                         mov     ah,XMS_FREE\r
575                         call    [DWORD PTR XMSDriver]\r
576                         or      bl,bl\r
577                         jz      errorxs\r
578                         jmp     Endxs\r
579 #ifdef __BORLANDC__\r
580                 }\r
581 #endif\r
582                         errorxs:\r
583 #ifdef __BORLANDC__\r
584                 __asm {\r
585 #endif\r
586                         //mov   err,ah\r
587                         mov     errorflag,1\r
588                         jmp     Endxs\r
589 #ifdef __BORLANDC__\r
590                 }\r
591 #endif\r
592                         Endxs:\r
593 #ifdef __WATCOMC__\r
594                 }\r
595 #endif\r
596                 if(errorflag==true)\r
597                 {\r
598                         Quit (gvar, "PML_ShutdownXMS: Error freeing XMS");\r
599                         //return;\r
600                 }\r
601         }\r
602 }\r
603 \r
604 /////////////////////////////////////////////////////////////////////////////\r
605 //\r
606 //      Main memory code\r
607 //\r
608 /////////////////////////////////////////////////////////////////////////////\r
609 \r
610 //\r
611 //      PM_SetMainMemPurge() - Sets the purge level for all allocated main memory\r
612 //              blocks. This shouldn't be called directly - the PM_LockMainMem() and\r
613 //              PM_UnlockMainMem() macros should be used instead.\r
614 //\r
615 void\r
616 PM_SetMainMemPurge(int level, global_game_variables_t *gvar)\r
617 {\r
618         int     i;\r
619 \r
620         if(gvar->pm.mm.MainPresent)\r
621         for (i = 0;i < PMMaxMainMem;i++)\r
622         {\r
623 #ifdef __DEBUG_PM__\r
624                 printf("PM_SetMainMemPurge()    info of gvar->pm.mm.MainMemPages[i]\n");\r
625                 printf("&       %Fp,    %Fp\n", &gvar->pm.mm.MainMemPages[i],   &(gvar->pm.mm.MainMemPages[i]));\r
626 #endif\r
627                 if (gvar->pm.mm.MainMemPages[i])\r
628                         MM_SetPurge(&(gvar->pm.mm.MainMemPages[i]),level, gvar);\r
629         }\r
630 \r
631         else\r
632         {\r
633                 Quit (gvar, "MainPresent IS NULL\n");\r
634         }\r
635 }\r
636 \r
637 //\r
638 //      PM_CheckMainMem() - If something besides the Page Mgr makes requests of\r
639 //              the Memory Mgr, some of the Page Mgr's blocks may have been purged,\r
640 //              so this function runs through the block list and checks to see if\r
641 //              any of the blocks have been purged. If so, it marks the corresponding\r
642 //              page as purged & unlocked, then goes through the block list and\r
643 //              tries to reallocate any blocks that have been purged.\r
644 //      This routine now calls PM_LockMainMem() to make sure that any allocation\r
645 //              attempts made during the block reallocation sweep don't purge any\r
646 //              of the other blocks. Because PM_LockMainMem() is called,\r
647 //              PM_UnlockMainMem() needs to be called before any other part of the\r
648 //              program makes allocation requests of the Memory Mgr.\r
649 //\r
650 void\r
651 PM_CheckMainMem(global_game_variables_t *gvar)\r
652 {\r
653         boolean                 allocfailed;\r
654         int                             i,n;\r
655         memptr                  *p;\r
656         PMBlockAttr             *used;\r
657         PageListStruct  far *page;\r
658 \r
659         if (!gvar->pm.mm.MainPresent)\r
660                 return;\r
661 \r
662         for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.fi.ChunksInFile;i++,page++)\r
663         {\r
664                 n = page->mainPage;\r
665                 if (n != -1)                                            // Is the page using main memory?\r
666                 {\r
667                         if (!gvar->pm.mm.MainMemPages[n])                       // Yep, was the block purged?\r
668                         {\r
669                                 page->mainPage = -1;            // Yes, mark page as purged & unlocked\r
670                                 page->locked = pml_Unlocked;\r
671                         }\r
672                 }\r
673         }\r
674 \r
675         // Prevent allocation attempts from purging any of our other blocks\r
676         PM_LockMainMem(gvar);\r
677         allocfailed = false;\r
678         for (i = 0,p = gvar->pm.mm.MainMemPages,used = gvar->pm.mm.MainMemUsed; i < PMMaxMainMem;i++,p++,used++)\r
679         {\r
680                 if (!*p)                                                        // If the page got purged\r
681                 {\r
682                         if (*used & pmba_Allocated)             // If it was allocated\r
683                         {\r
684                                 *used &= ~pmba_Allocated;       // Mark as unallocated\r
685                                 gvar->pm.mm.MainPagesAvail--;                   // and decrease available count\r
686                         }\r
687 \r
688                         if (*used & pmba_Used)                  // If it was used\r
689                         {\r
690                                 *used &= ~pmba_Used;            // Mark as unused\r
691                                 gvar->pm.MainPagesUsed--;                       // and decrease used count\r
692                         }\r
693 \r
694                         if (!allocfailed)\r
695                         {\r
696                                 MM_BombOnError(false, gvar);\r
697                                 MM_GetPtr(p,PMPageSize, gvar);          // Try to reallocate\r
698                                 if (gvar->mm.mmerror)                                   // If it failed,\r
699                                         allocfailed = true;                     //  don't try any more allocations\r
700                                 else                                                    // If it worked,\r
701                                 {\r
702                                         *used |= pmba_Allocated;        // Mark as allocated\r
703                                         gvar->pm.mm.MainPagesAvail++;                   // and increase available count\r
704                                 }\r
705                                 MM_BombOnError(true, gvar);\r
706                         }\r
707                 }\r
708         }\r
709         if (gvar->mm.mmerror)\r
710                 gvar->mm.mmerror = false;\r
711 }\r
712 \r
713 //\r
714 //      PML_StartupMainMem() - Allocates as much main memory as is possible for\r
715 //              the Page Mgr. The memory is allocated as non-purgeable, so if it's\r
716 //              necessary to make requests of the Memory Mgr, PM_UnlockMainMem()\r
717 //              needs to be called.\r
718 //\r
719 void\r
720 PML_StartupMainMem(global_game_variables_t *gvar)\r
721 {\r
722         int             i;//,n;\r
723         memptr  *p;\r
724 \r
725         gvar->pm.mm.MainPagesAvail = 0;\r
726         gvar->pm.mm.MainPresent = false;\r
727         MM_BombOnError(false, gvar);\r
728         for (i = 0,p = gvar->pm.mm.MainMemPages;i < PMMaxMainMem;i++,p++)\r
729         {\r
730                 MM_GetPtr(p,PMPageSize, gvar);\r
731                 if (gvar->mm.mmerror)\r
732                         break;\r
733 \r
734                 gvar->pm.mm.MainPagesAvail++;\r
735                 gvar->pm.mm.MainMemUsed[i] = pmba_Allocated;\r
736         }\r
737         MM_BombOnError(true, gvar);\r
738         if (gvar->mm.mmerror)\r
739                 gvar->mm.mmerror = false;\r
740         if (gvar->pm.mm.MainPagesAvail < PMMinMainMem)\r
741         {\r
742                 Quit (gvar, "PM_SetupMainMem: Not enough main memory");\r
743                 //return;\r
744         }\r
745         gvar->pm.mm.MainPresent = true;\r
746 }\r
747 \r
748 //\r
749 //      PML_ShutdownMainMem() - Frees all of the main memory blocks used by the\r
750 //              Page Mgr.\r
751 //\r
752 void\r
753 PML_ShutdownMainMem(global_game_variables_t *gvar)\r
754 {\r
755         int             i;\r
756         memptr  *p;\r
757 \r
758         // DEBUG - mark pages as unallocated & decrease page count as appropriate\r
759         for (i = 0,p = gvar->pm.mm.MainMemPages;i < PMMaxMainMem;i++,p++)\r
760                 if (*p)\r
761                         MM_FreePtr(p, gvar);\r
762 }\r
763 \r
764 /////////////////////////////////////////////////////////////////////////////\r
765 //\r
766 //      File management code\r
767 //\r
768 /////////////////////////////////////////////////////////////////////////////\r
769 \r
770 //\r
771 //      PML_ReadFromFile() - Reads some data in from the page file\r
772 //\r
773 void\r
774 PML_ReadFromFile(byte far *buf,long offset,word length, global_game_variables_t *gvar)\r
775 {\r
776         if (!buf)\r
777         {\r
778                 Quit (gvar, "PML_ReadFromFile: Null pointer");\r
779                 //return;\r
780         }\r
781         if (!offset)\r
782         {\r
783                 Quit (gvar, "PML_ReadFromFile: Zero offset");\r
784                 //return;\r
785         }\r
786         if (lseek(gvar->pm.fi.PageFile,offset,SEEK_SET) != offset)\r
787         {\r
788                 Quit (gvar, "PML_ReadFromFile: Seek failed");\r
789                 //return;\r
790         }\r
791         if (!CA_FarRead(gvar->pm.fi.PageFile,buf,length, gvar))\r
792         {\r
793                 Quit (gvar, "PML_ReadFromFile: Read failed");\r
794                 //return;\r
795         }\r
796 }\r
797 \r
798 //\r
799 //      PML_OpenPageFile() - Opens the page file and sets up the page info\r
800 //\r
801 void\r
802 PML_OpenPageFile(global_game_variables_t *gvar)\r
803 {\r
804         int                             i;\r
805         long                    size;\r
806         void                    _seg *buf;\r
807         dword           far *offsetptr;\r
808         word                    far *lengthptr;\r
809         PageListStruct  far *page;\r
810 \r
811         gvar->pm.fi.PageFile = open(gvar->pm.fi.PageFileName,O_RDONLY + O_BINARY);\r
812         if (gvar->pm.fi.PageFile == -1)\r
813         {\r
814                 Quit (gvar, "PML_OpenPageFile: Unable to open page file");\r
815                 //return;\r
816         }\r
817 \r
818         // Read in header variables\r
819         read(gvar->pm.fi.PageFile,&gvar->pm.fi.ChunksInFile,sizeof(gvar->pm.fi.ChunksInFile));\r
820         read(gvar->pm.fi.PageFile,&gvar->pm.fi.PMSpriteStart,sizeof(gvar->pm.fi.PMSpriteStart));\r
821         read(gvar->pm.fi.PageFile,&gvar->pm.fi.PMSoundStart,sizeof(gvar->pm.fi.PMSoundStart));\r
822 \r
823         // Allocate and clear the page list\r
824         gvar->pm.PMNumBlocks = gvar->pm.fi.ChunksInFile;\r
825         MM_GetPtr((memptr *)&gvar->pm.PMSegPages, sizeof(PageListStruct) * (gvar->pm.PMNumBlocks), gvar);\r
826         MM_SetLock((memptr *)&gvar->pm.PMSegPages,true, gvar);\r
827         gvar->pm.PMPages = (PageListStruct far *)gvar->pm.PMSegPages;\r
828         _fmemset(gvar->pm.PMPages,0,sizeof(PageListStruct) * gvar->pm.PMNumBlocks);\r
829 \r
830         // Read in the chunk offsets\r
831         size = sizeof(dword) * gvar->pm.fi.ChunksInFile;\r
832         MM_GetPtr((memptr *)&buf, size, gvar);\r
833         if (!CA_FarRead(gvar->pm.fi.PageFile,(byte far *)buf,size, gvar))\r
834         {\r
835                 Quit (gvar, "PML_OpenPageFile: Offset read failed");\r
836                 //return;\r
837         }\r
838         offsetptr = (dword far *)buf;\r
839         for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.fi.ChunksInFile;i++,page++)\r
840                 page->offset = *offsetptr++;\r
841         MM_FreePtr((memptr *)&buf, gvar);\r
842 \r
843         // Read in the chunk lengths\r
844         size = sizeof(word) * gvar->pm.fi.ChunksInFile;\r
845         MM_GetPtr(&buf,size, gvar);\r
846         if (!CA_FarRead(gvar->pm.fi.PageFile,(byte far *)buf,size, gvar))\r
847         {\r
848                 Quit (gvar, "PML_OpenPageFile: Length read failed");\r
849                 //return;\r
850         }\r
851         lengthptr = (word far *)buf;\r
852         for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.fi.ChunksInFile;i++,page++)\r
853                 page->length = *lengthptr++;\r
854         MM_FreePtr((memptr *)&buf, gvar);\r
855 }\r
856 \r
857 //\r
858 //  PML_ClosePageFile() - Closes the page file\r
859 //\r
860 void\r
861 PML_ClosePageFile(global_game_variables_t *gvar)\r
862 {\r
863         if (gvar->pm.fi.PageFile != -1)\r
864                 close(gvar->pm.fi.PageFile);\r
865         if (gvar->pm.PMSegPages)\r
866         {\r
867                 MM_SetLock((memptr *)&gvar->pm.PMSegPages,false, gvar);\r
868                 MM_FreePtr((void _seg *)&gvar->pm.PMSegPages, gvar);\r
869         }\r
870 }\r
871 \r
872 /////////////////////////////////////////////////////////////////////////////\r
873 //\r
874 //      Allocation, etc., code\r
875 //\r
876 /////////////////////////////////////////////////////////////////////////////\r
877 \r
878 //\r
879 //      PML_GetEMSAddress()\r
880 //\r
881 //              Page is in EMS, so figure out which EMS physical page should be used\r
882 //              to map our page in. If normal page, use EMS physical page 3, else\r
883 //              use the physical page specified by the lock type\r
884 //\r
885 #ifndef __DEBUG_2__\r
886 #pragma argsused        // DEBUG - remove lock parameter\r
887 memptr\r
888 PML_GetEMSAddress(int page,PMLockType lock, global_game_variables_t *gvar)\r
889 {\r
890         int             i,emspage;\r
891         word    emsoff,emsbase,offset;\r
892 \r
893         emsoff = page & (PMEMSSubPage - 1);\r
894         emsbase = page - emsoff;\r
895 \r
896         emspage = -1;\r
897         // See if this page is already mapped in\r
898         for (i = 0;i < EMSFrameCount;i++)\r
899         {\r
900                 if (gvar->pm.emm.EMSList[i].baseEMSPage == emsbase)\r
901                 {\r
902                         emspage = i;    // Yep - don't do a redundant remapping\r
903                         break;\r
904                 }\r
905         }\r
906 \r
907         // If page isn't already mapped in, find LRU EMS frame, and use it\r
908         if (emspage == -1)\r
909         {\r
910                 dword last = LONG_MAX;\r
911                 for (i = 0;i < EMSFrameCount;i++)\r
912                 {\r
913                         if (gvar->pm.emm.EMSList[i].lastHit < last)\r
914                         {\r
915                                 emspage = i;\r
916                                 last = gvar->pm.emm.EMSList[i].lastHit;\r
917                         }\r
918                 }\r
919 \r
920                 gvar->pm.emm.EMSList[emspage].baseEMSPage = emsbase;\r
921                 PML_MapEMS(page / PMEMSSubPage,emspage, gvar);\r
922         }\r
923 \r
924         if (emspage == -1)\r
925                 Quit (gvar, "PML_GetEMSAddress: EMS find failed");\r
926 \r
927         gvar->pm.emm.EMSList[emspage].lastHit = gvar->pm.PMFrameCount;\r
928         offset = emspage * EMSPageSizeSeg;\r
929         offset += emsoff * PMPageSizeSeg;\r
930         return((memptr)(gvar->pm.emm.EMSPageFrame + offset));\r
931 }\r
932 #else\r
933 memptr\r
934 PML_GetEMSAddress(int page,PMLockType lock, global_game_variables_t *gvar)\r
935 {\r
936         word    emspage;\r
937 \r
938         emspage = (lock < pml_EMSLock)? 3 : (lock - pml_EMSLock);\r
939 \r
940         PML_MapEMS(page / PMEMSSubPage,emspage);\r
941 \r
942         return((memptr)(EMSPageFrame + (emspage * EMSPageSizeSeg)\r
943                         + ((page & (PMEMSSubPage - 1)) * PMPageSizeSeg)));\r
944 }\r
945 #endif\r
946 \r
947 //\r
948 //      PM_GetPageAddress() - Returns the address of a given page\r
949 //              Maps in EMS if necessary\r
950 //              Returns nil if block isn't cached into Main Memory or EMS\r
951 //\r
952 //\r
953 memptr\r
954 PM_GetPageAddress(int pagenum, global_game_variables_t *gvar)\r
955 {\r
956         PageListStruct  far *page;\r
957 \r
958         page = &gvar->pm.PMPages[pagenum];\r
959         if (page->mainPage != -1)\r
960                 return(gvar->pm.mm.MainMemPages[page->mainPage]);\r
961         else if (page->emsPage != -1)\r
962                 return(PML_GetEMSAddress(page->emsPage,page->locked, gvar));\r
963         else\r
964                 return(nil);\r
965 }\r
966 \r
967 //\r
968 //      PML_GiveLRUPage() - Returns the page # of the least recently used\r
969 //              present & unlocked main/EMS page (or main page if mainonly is true)\r
970 //\r
971 int\r
972 PML_GiveLRUPage(boolean mainonly, global_game_variables_t *gvar)\r
973 {\r
974         int                             i,lru;\r
975         long                    last;\r
976         PageListStruct  far *page;\r
977 \r
978         for (i = 0,page = gvar->pm.PMPages,lru = -1,last = LONG_MAX;i < gvar->pm.fi.ChunksInFile;i++,page++)\r
979         {\r
980                 if\r
981                 (\r
982                         (page->lastHit < last)\r
983                 &&      ((page->emsPage != -1) || (page->mainPage != -1))\r
984                 &&      (page->locked == pml_Unlocked)\r
985                 &&      (!(mainonly && (page->mainPage == -1)))\r
986                 )\r
987                 {\r
988                         last = page->lastHit;\r
989                         lru = i;\r
990                 }\r
991         }\r
992 \r
993         if (lru == -1)\r
994                 Quit (gvar, "PML_GiveLRUPage: LRU Search failed");\r
995         return(lru);\r
996 }\r
997 \r
998 //\r
999 //      PML_GiveLRUXMSPage() - Returns the page # of the least recently used\r
1000 //              (and present) XMS page.\r
1001 //      This routine won't return the XMS page protected (by XMSProtectPage)\r
1002 //\r
1003 int\r
1004 PML_GiveLRUXMSPage(global_game_variables_t *gvar)\r
1005 {\r
1006         int                             i,lru;\r
1007         long                    last;\r
1008         PageListStruct  far *page;\r
1009 \r
1010         for (i = 0,page = gvar->pm.PMPages,lru = -1,last = LONG_MAX;i < gvar->pm.fi.ChunksInFile;i++,page++)\r
1011         {\r
1012                 if\r
1013                 (\r
1014                         (page->xmsPage != -1)\r
1015                 &&      (page->lastHit < last)\r
1016                 &&      (i != gvar->pm.xmm.XMSProtectPage)\r
1017                 )\r
1018                 {\r
1019                         last = page->lastHit;\r
1020                         lru = i;\r
1021                 }\r
1022         }\r
1023         return(lru);\r
1024 }\r
1025 \r
1026 //\r
1027 //      PML_PutPageInXMS() - If page isn't in XMS, find LRU XMS page and replace\r
1028 //              it with the main/EMS page\r
1029 //\r
1030 void\r
1031 PML_PutPageInXMS(int pagenum, global_game_variables_t *gvar)\r
1032 {\r
1033         int                             usexms;\r
1034         PageListStruct  far *page;\r
1035 \r
1036         if (!gvar->pm.xmm.XMSPresent)\r
1037                 return;\r
1038 \r
1039         page = &gvar->pm.PMPages[pagenum];\r
1040         if (page->xmsPage != -1)\r
1041                 return;                                 // Already in XMS\r
1042 \r
1043         if (gvar->pm.XMSPagesUsed < gvar->pm.xmm.XMSPagesAvail)\r
1044                 page->xmsPage = gvar->pm.XMSPagesUsed++;\r
1045         else\r
1046         {\r
1047                 usexms = PML_GiveLRUXMSPage(gvar);\r
1048                 if (usexms == -1)\r
1049                         Quit (gvar, "PML_PutPageInXMS: No XMS LRU");\r
1050                 page->xmsPage = gvar->pm.PMPages[usexms].xmsPage;\r
1051                 gvar->pm.PMPages[usexms].xmsPage = -1;\r
1052         }\r
1053         PML_CopyToXMS(PM_GetPageAddress(pagenum, gvar),page->xmsPage,page->length, gvar);\r
1054 }\r
1055 \r
1056 //\r
1057 //      PML_TransferPageSpace() - A page is being replaced, so give the new page\r
1058 //              the old one's address space. Returns the address of the new page.\r
1059 //\r
1060 memptr\r
1061 PML_TransferPageSpace(int orig,int new, global_game_variables_t *gvar)\r
1062 {\r
1063         memptr                  addr;\r
1064         PageListStruct  far *origpage,far *newpage;\r
1065 \r
1066         if (orig == new)\r
1067                 Quit (gvar, "PML_TransferPageSpace: Identity replacement");\r
1068 \r
1069         origpage = &gvar->pm.PMPages[orig];\r
1070         newpage = &gvar->pm.PMPages[new];\r
1071 \r
1072         if (origpage->locked != pml_Unlocked)\r
1073                 Quit (gvar, "PML_TransferPageSpace: Killing locked page");\r
1074 \r
1075         if ((origpage->emsPage == -1) && (origpage->mainPage == -1))\r
1076                 Quit (gvar, "PML_TransferPageSpace: Reusing non-existent page");\r
1077 \r
1078         // Copy page that's about to be purged into XMS\r
1079         PML_PutPageInXMS(orig, gvar);\r
1080 \r
1081         // Get the address, and force EMS into a physical page if necessary\r
1082         addr = PM_GetPageAddress(orig, gvar);\r
1083 \r
1084         // Steal the address\r
1085         newpage->emsPage = origpage->emsPage;\r
1086         newpage->mainPage = origpage->mainPage;\r
1087 \r
1088         // Mark replaced page as purged\r
1089         origpage->mainPage = origpage->emsPage = -1;\r
1090 \r
1091         if (!addr)\r
1092                 Quit (gvar, "PML_TransferPageSpace: Zero replacement");\r
1093 \r
1094         return(addr);\r
1095 }\r
1096 \r
1097 //\r
1098 //      PML_GetAPageBuffer() - A page buffer is needed. Either get it from the\r
1099 //              main/EMS free pool, or use PML_GiveLRUPage() to find which page to\r
1100 //              steal the buffer from. Returns a far pointer to the page buffer, and\r
1101 //              sets the fields inside the given page structure appropriately.\r
1102 //              If mainonly is true, free EMS will be ignored, and only main pages\r
1103 //              will be looked at by PML_GiveLRUPage().\r
1104 //\r
1105 byte far *\r
1106 PML_GetAPageBuffer(int pagenum,boolean mainonly, global_game_variables_t *gvar)\r
1107 {\r
1108         byte                    far *addr = nil;\r
1109         int                             i,n;\r
1110         PMBlockAttr             *used;\r
1111         PageListStruct  far *page;\r
1112 \r
1113         page = &gvar->pm.PMPages[pagenum];\r
1114         if ((gvar->pm.EMSPagesUsed < gvar->pm.emm.EMSPagesAvail) && !mainonly)\r
1115         {\r
1116                 // There's remaining EMS - use it\r
1117                 page->emsPage = gvar->pm.EMSPagesUsed++;\r
1118                 addr = PML_GetEMSAddress(page->emsPage,page->locked, gvar);\r
1119         }\r
1120         else if (gvar->pm.MainPagesUsed < gvar->pm.mm.MainPagesAvail)\r
1121         {\r
1122                 // There's remaining main memory - use it\r
1123                 for (i = 0,n = -1,used = gvar->pm.mm.MainMemUsed;i < PMMaxMainMem;i++,used++)\r
1124                 {\r
1125                         if ((*used & pmba_Allocated) && !(*used & pmba_Used))\r
1126                         {\r
1127                                 n = i;\r
1128                                 *used |= pmba_Used;\r
1129                                 break;\r
1130                         }\r
1131                 }\r
1132                 if (n == -1)\r
1133                         Quit (gvar, "PML_GetPageBuffer: MainPagesAvail lied");\r
1134                 addr = gvar->pm.mm.MainMemPages[n];\r
1135                 if (!addr)\r
1136                         Quit (gvar, "PML_GetPageBuffer: Purged main block");\r
1137                 page->mainPage = n;\r
1138                 gvar->pm.MainPagesUsed++;\r
1139         }\r
1140         else\r
1141                 addr = PML_TransferPageSpace(PML_GiveLRUPage(mainonly, gvar),pagenum, gvar);\r
1142 \r
1143         if (!addr)\r
1144                 Quit (gvar, "PML_GetPageBuffer: Search failed");\r
1145         return(addr);\r
1146 }\r
1147 \r
1148 //\r
1149 //      PML_GetPageFromXMS() - If page is in XMS, find LRU main/EMS page and\r
1150 //              replace it with the page from XMS. If mainonly is true, will only\r
1151 //              search for LRU main page.\r
1152 //      XMSProtectPage is set to the page to be retrieved from XMS, so that if\r
1153 //              the page from which we're stealing the main/EMS from isn't in XMS,\r
1154 //              it won't copy over the page that we're trying to get from XMS.\r
1155 //              (pages that are being purged are copied into XMS, if possible)\r
1156 //\r
1157 memptr\r
1158 PML_GetPageFromXMS(int pagenum,boolean mainonly, global_game_variables_t *gvar)\r
1159 {\r
1160         byte                    far *checkaddr;\r
1161         memptr                  addr = nil;\r
1162         PageListStruct  far *page;\r
1163 \r
1164         page = &gvar->pm.PMPages[pagenum];\r
1165         if (gvar->pm.xmm.XMSPresent && (page->xmsPage != -1))\r
1166         {\r
1167                 gvar->pm.xmm.XMSProtectPage = pagenum;\r
1168                 checkaddr = PML_GetAPageBuffer(pagenum,mainonly, gvar);\r
1169                 if (FP_OFF(checkaddr))\r
1170                         Quit (gvar, "PML_GetPageFromXMS: Non segment pointer");\r
1171                 addr = (memptr)FP_SEG(checkaddr);\r
1172                 PML_CopyFromXMS(addr,page->xmsPage,page->length, gvar);\r
1173                 gvar->pm.xmm.XMSProtectPage = -1;\r
1174         }\r
1175 \r
1176         return(addr);\r
1177 }\r
1178 \r
1179 //\r
1180 //      PML_LoadPage() - A page is not in main/EMS memory, and it's not in XMS.\r
1181 //              Load it into either main or EMS. If mainonly is true, the page will\r
1182 //              only be loaded into main.\r
1183 //\r
1184 void\r
1185 PML_LoadPage(int pagenum,boolean mainonly, global_game_variables_t *gvar)\r
1186 {\r
1187         byte                    far *addr;\r
1188         PageListStruct  far *page;\r
1189 \r
1190         addr = PML_GetAPageBuffer(pagenum,mainonly, gvar);\r
1191         page = &gvar->pm.PMPages[pagenum];\r
1192         PML_ReadFromFile(addr,page->offset,page->length, gvar);\r
1193 }\r
1194 \r
1195 //\r
1196 //      PM_GetPage() - Returns the address of the page, loading it if necessary\r
1197 //              First, check if in Main Memory or EMS\r
1198 //              Then, check XMS\r
1199 //              If not in XMS, load into Main Memory or EMS\r
1200 //\r
1201 #pragma warn -pia\r
1202 memptr\r
1203 PM_GetPage(int pagenum, global_game_variables_t *gvar)\r
1204 {\r
1205         memptr  result;\r
1206 \r
1207         if (pagenum >= gvar->pm.fi.ChunksInFile)\r
1208                 Quit (gvar, "PM_GetPage: Invalid page request");\r
1209 \r
1210 //#ifdef __DEBUG_2__    // for debugging\r
1211         __asm {\r
1212                 mov     dx,STATUS_REGISTER_1\r
1213                 in      al,dx\r
1214                 mov     dx,ATR_INDEX\r
1215                 mov     al,ATR_OVERSCAN\r
1216                 out     dx,al\r
1217                 mov     al,10   // bright green\r
1218                 out     dx,al\r
1219         }\r
1220 //#endif\r
1221 \r
1222         if (!(result = PM_GetPageAddress(pagenum, gvar)))\r
1223         {\r
1224                 boolean mainonly = (pagenum >= gvar->pm.fi.PMSoundStart);\r
1225 if (!gvar->pm.PMPages[pagenum].offset)  // JDC: sparse page\r
1226         Quit (gvar, "Tried to load a sparse page!");\r
1227                 if (!(result = PML_GetPageFromXMS(pagenum,mainonly, gvar)))\r
1228                 {\r
1229                         if (gvar->pm.PMPages[pagenum].lastHit ==  gvar->pm.PMFrameCount)\r
1230                                 gvar->pm.PMThrashing++;\r
1231 \r
1232                         PML_LoadPage(pagenum,mainonly, gvar);\r
1233                         result = PM_GetPageAddress(pagenum, gvar);\r
1234                 }\r
1235         }\r
1236         gvar->pm.PMPages[pagenum].lastHit =  gvar->pm.PMFrameCount;\r
1237 \r
1238 //#ifdef __DEBUG_2__    // for debugging\r
1239         __asm{\r
1240                 mov     dx,STATUS_REGISTER_1\r
1241                 in      al,dx\r
1242                 mov     dx,ATR_INDEX\r
1243                 mov     al,ATR_OVERSCAN\r
1244                 out     dx,al\r
1245                 mov     al,3    // blue\r
1246                 out     dx,al\r
1247                 mov     al,0x20 // normal\r
1248                 out     dx,al\r
1249         }\r
1250 //#endif\r
1251 \r
1252         return(result);\r
1253 }\r
1254 #pragma warn +pia\r
1255 \r
1256 //\r
1257 //      PM_SetPageLock() - Sets the lock type on a given page\r
1258 //              pml_Unlocked: Normal, page can be purged\r
1259 //              pml_Locked: Cannot be purged\r
1260 //              pml_EMS?: Same as pml_Locked, but if in EMS, use the physical page\r
1261 //                                      specified when returning the address. For sound stuff.\r
1262 //\r
1263 void\r
1264 PM_SetPageLock(int pagenum,PMLockType lock, global_game_variables_t *gvar)\r
1265 {\r
1266         if (pagenum < gvar->pm.fi.PMSoundStart)\r
1267                 Quit (gvar, "PM_SetPageLock: Locking/unlocking non-sound page");\r
1268 \r
1269         gvar->pm.PMPages[pagenum].locked = lock;\r
1270 }\r
1271 \r
1272 //\r
1273 //      PM_Preload() - Loads as many pages as possible into all types of memory.\r
1274 //              Calls the update function after each load, indicating the current\r
1275 //              page, and the total pages that need to be loaded (for thermometer).\r
1276 //\r
1277 void\r
1278 PM_Preload(boolean (*update)(word current,word total), global_game_variables_t *gvar)\r
1279 {\r
1280         int                             i,//j,\r
1281                                         page,oogypage;\r
1282         word                    current,total,\r
1283                                         //totalnonxms,totalxms,\r
1284                                         mainfree,maintotal,\r
1285                                         //emstotal,emsfree,\r
1286                                         xmsfree,xmstotal;\r
1287         memptr                  addr;\r
1288         PageListStruct  __far *p;\r
1289 \r
1290         mainfree = (gvar->pm.mm.MainPagesAvail - gvar->pm.MainPagesUsed) + (gvar->pm.emm.EMSPagesAvail - gvar->pm.EMSPagesUsed);\r
1291         xmsfree = (gvar->pm.xmm.XMSPagesAvail - gvar->pm.XMSPagesUsed);\r
1292 \r
1293         xmstotal = maintotal = 0;\r
1294 \r
1295         for (i = 0;i < gvar->pm.fi.ChunksInFile;i++)\r
1296         {\r
1297                 if (!gvar->pm.PMPages[i].offset)\r
1298                         continue;                       // sparse\r
1299 \r
1300                 if ( gvar->pm.PMPages[i].emsPage != -1 || gvar->pm.PMPages[i].mainPage != -1 )\r
1301                         continue;                       // already in main mem\r
1302 \r
1303                 if ( mainfree )\r
1304                 {\r
1305                         maintotal++;\r
1306                         mainfree--;\r
1307                 }\r
1308                 else if ( xmsfree && (gvar->pm.PMPages[i].xmsPage == -1) )\r
1309                 {\r
1310                         xmstotal++;\r
1311                         xmsfree--;\r
1312                 }\r
1313         }\r
1314 \r
1315 \r
1316         total = maintotal + xmstotal;\r
1317 \r
1318         if (!total)\r
1319                 return;\r
1320 \r
1321         page = 0;\r
1322         current = 0;\r
1323 \r
1324 //\r
1325 // cache main/ems blocks\r
1326 //\r
1327         while (maintotal)\r
1328         {\r
1329                 while ( !gvar->pm.PMPages[page].offset || gvar->pm.PMPages[page].mainPage != -1\r
1330                         ||      gvar->pm.PMPages[page].emsPage != -1 )\r
1331                         page++;\r
1332 \r
1333                 if (page >= gvar->pm.fi.ChunksInFile)\r
1334                         Quit (gvar, "PM_Preload: Pages>=gvar->pm.fi.ChunksInFile");\r
1335 \r
1336                 PM_GetPage(page, gvar);\r
1337 \r
1338                 page++;\r
1339                 current++;\r
1340                 maintotal--;\r
1341                 update(current,total);\r
1342         }\r
1343 \r
1344 //\r
1345 // load stuff to XMS\r
1346 //\r
1347         if (xmstotal)\r
1348         {\r
1349                 for (oogypage = 0 ; gvar->pm.PMPages[oogypage].mainPage == -1 ; oogypage++)\r
1350                 ;\r
1351                 addr = PM_GetPage(oogypage, gvar);\r
1352                 if (!addr)\r
1353                         Quit (gvar, "PM_Preload: XMS buffer failed");\r
1354 \r
1355                 while (xmstotal)\r
1356                 {\r
1357                         while ( !gvar->pm.PMPages[page].offset || gvar->pm.PMPages[page].xmsPage != -1 )\r
1358                                 page++;\r
1359 \r
1360                         if (page >= gvar->pm.fi.ChunksInFile)\r
1361                                 Quit (gvar, "PM_Preload: Pages>=gvar->pm.fi.ChunksInFile");\r
1362 \r
1363                         p = &gvar->pm.PMPages[page];\r
1364 \r
1365                         p->xmsPage = gvar->pm.XMSPagesUsed++;\r
1366                         if (gvar->pm.XMSPagesUsed > gvar->pm.xmm.XMSPagesAvail)\r
1367                                 Quit (gvar, "PM_Preload: Exceeded XMS pages");\r
1368                         if (p->length > PMPageSize)\r
1369                                 Quit (gvar, "PM_Preload: Page too long");\r
1370 \r
1371                         PML_ReadFromFile((byte far *)addr,p->offset,p->length, gvar);\r
1372                         PML_CopyToXMS((byte far *)addr,p->xmsPage,p->length, gvar);\r
1373 \r
1374                         page++;\r
1375                         current++;\r
1376                         xmstotal--;\r
1377                         update(current,total);\r
1378                 }\r
1379 \r
1380                 p = &gvar->pm.PMPages[oogypage];\r
1381                 PML_ReadFromFile((byte far *)addr,p->offset,p->length, gvar);\r
1382         }\r
1383 \r
1384         update(total,total);\r
1385 }\r
1386 \r
1387 /////////////////////////////////////////////////////////////////////////////\r
1388 //\r
1389 //      General code\r
1390 //\r
1391 /////////////////////////////////////////////////////////////////////////////\r
1392 \r
1393 //\r
1394 //      PM_NextFrame() - Increments the frame counter and adjusts the thrash\r
1395 //              avoidence variables\r
1396 //\r
1397 //              If currently in panic mode (to avoid thrashing), check to see if the\r
1398 //                      appropriate number of frames have passed since the last time that\r
1399 //                      we would have thrashed. If so, take us out of panic mode.\r
1400 //\r
1401 //\r
1402 void\r
1403 PM_NextFrame(global_game_variables_t *gvar)\r
1404 {\r
1405         int     i;\r
1406 \r
1407         // Frame count overrun - kill the LRU hit entries & reset frame count\r
1408         if (++gvar->pm.PMFrameCount >= LONG_MAX - 4)\r
1409         {\r
1410                 for (i = 0;i < gvar->pm.PMNumBlocks;i++)\r
1411                         gvar->pm.PMPages[i].lastHit = 0;\r
1412                 gvar->pm.PMFrameCount = 0;\r
1413         }\r
1414 \r
1415 //#if 0\r
1416         for (i = 0;i < gvar->pm.fi.PMSoundStart;i++)\r
1417         {\r
1418                 if (gvar->pm.PMPages[i].locked)\r
1419                 {\r
1420                         char buf[40];\r
1421                         sprintf(buf,"PM_NextFrame: Page %d is locked",i);\r
1422                         Quit(gvar, buf);\r
1423                 }\r
1424         }\r
1425 //#endif\r
1426 \r
1427         if (gvar->pm.PMPanicMode)\r
1428         {\r
1429                 // DEBUG - set border color\r
1430                 if ((!gvar->pm.PMThrashing) && (!--gvar->pm.PMPanicMode))\r
1431                 {\r
1432                         // DEBUG - reset border color\r
1433                 }\r
1434         }\r
1435         if (gvar->pm.PMThrashing >= PMThrashThreshold)\r
1436                 gvar->pm.PMPanicMode = PMUnThrashThreshold;\r
1437         gvar->pm.PMThrashing = false;\r
1438 }\r
1439 \r
1440 //\r
1441 //      PM_Reset() - Sets up caching structures\r
1442 //\r
1443 void\r
1444 PM_Reset(global_game_variables_t *gvar)\r
1445 {\r
1446         int                             i;\r
1447         PageListStruct  far *page;\r
1448 \r
1449         gvar->pm.xmm.XMSPagesAvail = gvar->pm.xmm.XMSAvail / PMPageSizeKB;\r
1450 \r
1451         gvar->pm.emm.EMSPagesAvail = gvar->pm.emm.EMSAvail * (EMSPageSizeKB / PMPageSizeKB);\r
1452         gvar->pm.emm.EMSPhysicalPage = 0;\r
1453 \r
1454         gvar->pm.MainPagesUsed = gvar->pm.EMSPagesUsed = gvar->pm.XMSPagesUsed = 0;\r
1455 \r
1456         gvar->pm.PMPanicMode = false;\r
1457 \r
1458         gvar->pm.fi.PageFile = -1;\r
1459         gvar->pm.xmm.XMSProtectPage = -1;\r
1460 \r
1461         // Initialize page list\r
1462         for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.PMNumBlocks;i++,page++)\r
1463         {\r
1464                 page->mainPage = -1;\r
1465                 page->emsPage = -1;\r
1466                 page->xmsPage = -1;\r
1467                 page->locked = false;\r
1468         }\r
1469 }\r
1470 \r
1471 //\r
1472 //      PM_Startup() - Start up the Page Mgr\r
1473 //\r
1474 void\r
1475 PM_Startup(global_game_variables_t *gvar)\r
1476 {\r
1477         boolean nomain,noems,noxms;\r
1478         int             i;\r
1479 \r
1480         if (gvar->pm.PMStarted)\r
1481                 return;\r
1482 \r
1483         //0000+=+=strcpy(&(gvar->pm.fi.PageFileName), "VSWAP.");\r
1484 \r
1485         nomain = noems = noxms = false;\r
1486         for (i = 1;i < _argc;i++)\r
1487         {\r
1488                 switch (US_CheckParm(_argv[i],ParmStrings))\r
1489                 {\r
1490                 case 0:\r
1491                         nomain = true;\r
1492                         break;\r
1493                 case 1:\r
1494                         noems = true;\r
1495                         break;\r
1496                 case 2:\r
1497                         noxms = true;\r
1498                         break;\r
1499                 }\r
1500         }\r
1501 \r
1502         //0000+=+=PML_OpenPageFile(gvar);\r
1503 \r
1504         if (!noems && MML_CheckForEMS())\r
1505                 PML_StartupEMS(gvar);\r
1506         if (!noxms && MML_CheckForXMS())\r
1507                 PML_StartupXMS(gvar);\r
1508         if(!nomain)\r
1509                 PML_StartupMainMem(gvar);\r
1510 \r
1511         if (!gvar->pm.mm.MainPresent && !gvar->pm.emm.EMSPresent && !gvar->pm.xmm.XMSPresent)\r
1512         {\r
1513                 Quit (gvar, "PM_Startup: No main or EMS\n");\r
1514                 //return;\r
1515         }\r
1516 \r
1517         PM_Reset(gvar);\r
1518 \r
1519         gvar->pm.PMStarted = true;\r
1520 }\r
1521 \r
1522 //\r
1523 //      PM_Shutdown() - Shut down the Page Mgr\r
1524 //\r
1525 void\r
1526 PM_Shutdown(global_game_variables_t *gvar)\r
1527 {\r
1528         if(MML_CheckForXMS()) PML_ShutdownXMS(gvar);\r
1529         if(MML_CheckForEMS()) PML_ShutdownEMS(gvar);\r
1530 \r
1531         if (!gvar->pm.PMStarted)\r
1532                 return;\r
1533 \r
1534         //0000+=+=PML_ClosePageFile(gvar);\r
1535 \r
1536         PML_ShutdownMainMem(gvar);\r
1537 }\r