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