OSDN Git Service

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