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