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