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