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