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