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