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