OSDN Git Service

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