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