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