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