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