]> 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                 or      ax,ax                           // AJR: bugfix 10/8/92\r
431                 jz      error2\r
432                 mov     e,4\r
433                 jmp     End2\r
434 #ifdef __BORLANDC__\r
435         }\r
436 #endif\r
437         error2:\r
438 #ifdef __BORLANDC__\r
439         __asm {\r
440 #endif\r
441                 mov     err,bl\r
442                 mov     errorflag,1\r
443                 jmp End2\r
444 #ifdef __BORLANDC__\r
445         }\r
446 #endif\r
447 End2:\r
448 #ifdef __WATCOMC__\r
449         }\r
450 #endif\r
451 \r
452         if(errorflag==false)\r
453         {\r
454                 gvar->mmi.XMSmem = XMSAvail4 * (dword)1024;\r
455                 gvar->pm.xmm.XMSAvail = XMSAvail4;\r
456                 gvar->pm.xmm.XMSHandle = XMSHandle;\r
457 \r
458                 gvar->pm.xmm.XMSPresent = true;\r
459                 printf("        %u      %u      %u\n", gvar->mmi.XMSmem, gvar->pm.xmm.XMSAvail, XMSAvail4);\r
460                 getch();\r
461         }else printf("  %u      errorflag=%02X\n", XMSAvail4, err);\r
462 error:\r
463         return(gvar->pm.xmm.XMSPresent);\r
464 /*\r
465         gvar->pm.xmm.XMSPresent = false;                                        // Assume failure\r
466         gvar->pm.xmm.XMSAvail = 0;\r
467 \r
468 __asm   mov     ax,0x4300\r
469 __asm   int     XMS_INT                                         // Check for presence of XMS driver\r
470         if (_AL != 0x80)\r
471                 goto error;\r
472 \r
473 \r
474 __asm   mov     ax,0x4310\r
475 __asm   int     XMS_INT                                                 // Get address of XMS driver\r
476 __asm   mov     [WORD PTR XMSDriver],bx\r
477 __asm   mov     [WORD PTR XMSDriver+2],es               // function pointer to XMS driver\r
478 \r
479         XMS_CALL(XMS_QUERYFREE);                        // Find out how much XMS is available\r
480         gvar->pm.xmm.XMSAvail = _AX;\r
481         if (!_AX)                               // AJR: bugfix 10/8/92\r
482                 goto error;\r
483 \r
484         gvar->pm.xmm.XMSAvail &= ~(PMPageSizeKB - 1);   // Round off to nearest page size\r
485         if (gvar->pm.xmm.XMSAvail < (PMPageSizeKB * 2)) // Need at least 2 pages\r
486                 goto error;\r
487 \r
488         _DX = gvar->pm.xmm.XMSAvail;\r
489         XMS_CALL(XMS_ALLOC);                            // And do the allocation\r
490         gvar->pm.xmm.XMSHandle = _DX;\r
491 \r
492         if (!_AX)                               // AJR: bugfix 10/8/92\r
493         {\r
494                 gvar->pm.xmm.XMSAvail = 0;\r
495                 goto error;\r
496         }\r
497 \r
498         gvar->mmi.XMSmem = gvar->pm.xmm.XMSAvail * 1024;\r
499 \r
500         gvar->pm.xmm.XMSPresent = true;\r
501 error:\r
502         printf("XMSstart        %u      %u\n", gvar->mmi.XMSmem, gvar->pm.xmm.XMSAvail);\r
503         return(gvar->pm.xmm.XMSPresent);*/\r
504 }\r
505 \r
506 //\r
507 //      PML_XMSCopy() - Copies a main/EMS page to or from XMS\r
508 //              Will round an odd-length request up to the next even value\r
509 //\r
510 void\r
511 PML_XMSCopy(boolean toxms,byte far *addr,word xmspage,word length, global_game_variables_t *gvar)\r
512 {\r
513         dword   xoffset;\r
514         struct\r
515         {\r
516                 dword   length;\r
517                 word            source_handle;\r
518                 dword   source_offset;\r
519                 word            target_handle;\r
520                 dword   target_offset;\r
521         } copy;\r
522 \r
523         if (!addr)\r
524         {\r
525                 Quit("PML_XMSCopy: zero address\n");\r
526                 //return;\r
527         }\r
528 \r
529         xoffset = (dword)xmspage * PMPageSize;\r
530 \r
531         copy.length = (length + 1) & ~1;\r
532         copy.source_handle = toxms? 0 : gvar->pm.xmm.XMSHandle;\r
533         copy.source_offset = toxms? (long)addr : xoffset;\r
534         copy.target_handle = toxms? gvar->pm.xmm.XMSHandle : 0;\r
535         copy.target_offset = toxms? xoffset : (long)addr;\r
536 \r
537         __asm {\r
538                 push si\r
539         }\r
540         _SI = (word)&copy;\r
541         XMS_CALL(XMS_MOVE);\r
542         __asm {\r
543                 pop     si\r
544         }\r
545         if (!_AX)\r
546         {\r
547                 Quit("PML_XMSCopy: Error on copy");\r
548                 //return;\r
549         }\r
550 }\r
551 \r
552 #if 1\r
553 #define PML_CopyToXMS(s,t,l,gvar)       PML_XMSCopy(true,(s),(t),(l),(gvar))\r
554 #define PML_CopyFromXMS(t,s,l,gvar)     PML_XMSCopy(false,(t),(s),(l),(gvar))\r
555 #else\r
556 //\r
557 //      PML_CopyToXMS() - Copies the specified number of bytes from the real mode\r
558 //              segment address to the specified XMS page\r
559 //\r
560 void\r
561 PML_CopyToXMS(byte far *source,int targetpage,word length,global_game_variables_t *gvar)\r
562 {\r
563         PML_XMSCopy(true,source,targetpage,length, global_game_variables_t *gvar);\r
564 }\r
565 \r
566 //\r
567 //      PML_CopyFromXMS() - Copies the specified number of bytes from an XMS\r
568 //              page to the specified real mode address\r
569 //\r
570 void\r
571 PML_CopyFromXMS(byte far *target,int sourcepage,word length, global_game_variables_t *gvar)\r
572 {\r
573         PML_XMSCopy(false,target,sourcepage,length, global_game_variables_t *gvar);\r
574 }\r
575 #endif\r
576 \r
577 //\r
578 //      PML_ShutdownXMS()\r
579 //\r
580 void\r
581 PML_ShutdownXMS(global_game_variables_t *gvar)\r
582 {\r
583         boolean errorflag=false;\r
584         word XMSHandle = gvar->pm.xmm.XMSHandle;\r
585         if (gvar->pm.xmm.XMSPresent)\r
586         {\r
587                 __asm {\r
588                         mov     dx,[XMSHandle]\r
589                         //XMS_CALL(XMS_FREE);\r
590                         mov     ah,XMS_FREE\r
591                         call    [DWORD PTR XMSDriver]\r
592                         or      bl,bl\r
593                         jz      errorxs\r
594                         jmp     Endxs\r
595 #ifdef __BORLANDC__\r
596                 }\r
597 #endif\r
598                         errorxs:\r
599 #ifdef __BORLANDC__\r
600                 __asm {\r
601 #endif\r
602                         //mov   err,ah\r
603                         mov     errorflag,1\r
604                         jmp     Endxs\r
605 #ifdef __BORLANDC__\r
606                 }\r
607 #endif\r
608                         Endxs:\r
609 #ifdef __WATCOMC__\r
610                 }\r
611 #endif\r
612                 if(errorflag==true)\r
613                 {\r
614                         Quit("PML_ShutdownXMS: Error freeing XMS");\r
615                         //return;\r
616                 }\r
617         }\r
618 }\r
619 \r
620 /////////////////////////////////////////////////////////////////////////////\r
621 //\r
622 //      Main memory code\r
623 //\r
624 /////////////////////////////////////////////////////////////////////////////\r
625 \r
626 //\r
627 //      PM_SetMainMemPurge() - Sets the purge level for all allocated main memory\r
628 //              blocks. This shouldn't be called directly - the PM_LockMainMem() and\r
629 //              PM_UnlockMainMem() macros should be used instead.\r
630 //\r
631 void\r
632 PM_SetMainMemPurge(int level, global_game_variables_t *gvar)\r
633 {\r
634         int     i;\r
635 \r
636         if(gvar->pm.mm.MainPresent)\r
637         for (i = 0;i < PMMaxMainMem;i++)\r
638         {\r
639 #ifdef __DEBUG_PM__\r
640                 printf("PM_SetMainMemPurge()    info of gvar->pm.mm.MainMemPages[i]\n");\r
641                 printf("&       %Fp,    %Fp\n", &gvar->pm.mm.MainMemPages[i],   &(gvar->pm.mm.MainMemPages[i]));\r
642 #endif\r
643                 if (gvar->pm.mm.MainMemPages[i])\r
644                         MM_SetPurge(&(gvar->pm.mm.MainMemPages[i]),level, gvar);\r
645         }\r
646 \r
647         else\r
648         {\r
649                 Quit("MainPresent IS NULL\n");\r
650         }\r
651 }\r
652 \r
653 //\r
654 //      PM_CheckMainMem() - If something besides the Page Mgr makes requests of\r
655 //              the Memory Mgr, some of the Page Mgr's blocks may have been purged,\r
656 //              so this function runs through the block list and checks to see if\r
657 //              any of the blocks have been purged. If so, it marks the corresponding\r
658 //              page as purged & unlocked, then goes through the block list and\r
659 //              tries to reallocate any blocks that have been purged.\r
660 //      This routine now calls PM_LockMainMem() to make sure that any allocation\r
661 //              attempts made during the block reallocation sweep don't purge any\r
662 //              of the other blocks. Because PM_LockMainMem() is called,\r
663 //              PM_UnlockMainMem() needs to be called before any other part of the\r
664 //              program makes allocation requests of the Memory Mgr.\r
665 //\r
666 void\r
667 PM_CheckMainMem(global_game_variables_t *gvar)\r
668 {\r
669         boolean                 allocfailed;\r
670         int                             i,n;\r
671         memptr                  *p;\r
672         PMBlockAttr             *used;\r
673         PageListStruct  far *page;\r
674 \r
675         if (!gvar->pm.mm.MainPresent)\r
676                 return;\r
677 \r
678         for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.fi.ChunksInFile;i++,page++)\r
679         {\r
680                 n = page->mainPage;\r
681                 if (n != -1)                                            // Is the page using main memory?\r
682                 {\r
683                         if (!gvar->pm.mm.MainMemPages[n])                       // Yep, was the block purged?\r
684                         {\r
685                                 page->mainPage = -1;            // Yes, mark page as purged & unlocked\r
686                                 page->locked = pml_Unlocked;\r
687                         }\r
688                 }\r
689         }\r
690 \r
691         // Prevent allocation attempts from purging any of our other blocks\r
692         PM_LockMainMem(gvar);\r
693         allocfailed = false;\r
694         for (i = 0,p = gvar->pm.mm.MainMemPages,used = gvar->pm.mm.MainMemUsed; i < PMMaxMainMem;i++,p++,used++)\r
695         {\r
696                 if (!*p)                                                        // If the page got purged\r
697                 {\r
698                         if (*used & pmba_Allocated)             // If it was allocated\r
699                         {\r
700                                 *used &= ~pmba_Allocated;       // Mark as unallocated\r
701                                 gvar->pm.mm.MainPagesAvail--;                   // and decrease available count\r
702                         }\r
703 \r
704                         if (*used & pmba_Used)                  // If it was used\r
705                         {\r
706                                 *used &= ~pmba_Used;            // Mark as unused\r
707                                 gvar->pm.MainPagesUsed--;                       // and decrease used count\r
708                         }\r
709 \r
710                         if (!allocfailed)\r
711                         {\r
712                                 MM_BombOnError(false, gvar);\r
713                                 MM_GetPtr(p,PMPageSize, gvar);          // Try to reallocate\r
714                                 if (gvar->mm.mmerror)                                   // If it failed,\r
715                                         allocfailed = true;                     //  don't try any more allocations\r
716                                 else                                                    // If it worked,\r
717                                 {\r
718                                         *used |= pmba_Allocated;        // Mark as allocated\r
719                                         gvar->pm.mm.MainPagesAvail++;                   // and increase available count\r
720                                 }\r
721                                 MM_BombOnError(true, gvar);\r
722                         }\r
723                 }\r
724         }\r
725         if (gvar->mm.mmerror)\r
726                 gvar->mm.mmerror = false;\r
727 }\r
728 \r
729 //\r
730 //      PML_StartupMainMem() - Allocates as much main memory as is possible for\r
731 //              the Page Mgr. The memory is allocated as non-purgeable, so if it's\r
732 //              necessary to make requests of the Memory Mgr, PM_UnlockMainMem()\r
733 //              needs to be called.\r
734 //\r
735 void\r
736 PML_StartupMainMem(global_game_variables_t *gvar)\r
737 {\r
738         int             i,n;\r
739         memptr  *p;\r
740 \r
741         gvar->pm.mm.MainPagesAvail = 0;\r
742         gvar->pm.mm.MainPresent = false;\r
743         MM_BombOnError(false, gvar);\r
744         for (i = 0,p = gvar->pm.mm.MainMemPages;i < PMMaxMainMem;i++,p++)\r
745         {\r
746                 MM_GetPtr(p,PMPageSize, gvar);\r
747                 if (gvar->mm.mmerror)\r
748                         break;\r
749 \r
750                 gvar->pm.mm.MainPagesAvail++;\r
751                 gvar->pm.mm.MainMemUsed[i] = pmba_Allocated;\r
752         }\r
753         MM_BombOnError(true, gvar);\r
754         if (gvar->mm.mmerror)\r
755                 gvar->mm.mmerror = false;\r
756         if (gvar->pm.mm.MainPagesAvail < PMMinMainMem)\r
757         {\r
758                 Quit("PM_SetupMainMem: Not enough main memory");\r
759                 //return;\r
760         }\r
761         gvar->pm.mm.MainPresent = true;\r
762 }\r
763 \r
764 //\r
765 //      PML_ShutdownMainMem() - Frees all of the main memory blocks used by the\r
766 //              Page Mgr.\r
767 //\r
768 void\r
769 PML_ShutdownMainMem(global_game_variables_t *gvar)\r
770 {\r
771         int             i;\r
772         memptr  *p;\r
773 \r
774         // DEBUG - mark pages as unallocated & decrease page count as appropriate\r
775         for (i = 0,p = gvar->pm.mm.MainMemPages;i < PMMaxMainMem;i++,p++)\r
776                 if (*p)\r
777                         MM_FreePtr(p, gvar);\r
778 }\r
779 \r
780 /////////////////////////////////////////////////////////////////////////////\r
781 //\r
782 //      File management code\r
783 //\r
784 /////////////////////////////////////////////////////////////////////////////\r
785 \r
786 //\r
787 //      PML_ReadFromFile() - Reads some data in from the page file\r
788 //\r
789 void\r
790 PML_ReadFromFile(byte far *buf,long offset,word length, global_game_variables_t *gvar)\r
791 {\r
792         if (!buf)\r
793         {\r
794                 Quit("PML_ReadFromFile: Null pointer");\r
795                 //return;\r
796         }\r
797         if (!offset)\r
798         {\r
799                 Quit("PML_ReadFromFile: Zero offset");\r
800                 //return;\r
801         }\r
802         if (lseek(gvar->pm.fi.PageFile,offset,SEEK_SET) != offset)\r
803         {\r
804                 Quit("PML_ReadFromFile: Seek failed");\r
805                 //return;\r
806         }\r
807         if (!CA_FarRead(gvar->pm.fi.PageFile,buf,length, gvar))\r
808         {\r
809                 Quit("PML_ReadFromFile: Read failed");\r
810                 //return;\r
811         }\r
812 }\r
813 \r
814 //\r
815 //      PML_OpenPageFile() - Opens the page file and sets up the page info\r
816 //\r
817 void\r
818 PML_OpenPageFile(global_game_variables_t *gvar)\r
819 {\r
820         int                             i;\r
821         long                    size;\r
822                                 //__SEGA buf;\r
823         memptr          buf;\r
824         dword           far *offsetptr;\r
825         word                    far *lengthptr;\r
826         PageListStruct  far *page;\r
827 \r
828         gvar->pm.fi.PageFile = open(gvar->pm.fi.PageFileName,O_RDONLY + O_BINARY);\r
829         if (gvar->pm.fi.PageFile == -1)\r
830         {\r
831                 Quit("PML_OpenPageFile: Unable to open page file");\r
832                 //return;\r
833         }\r
834 \r
835         // Read in header variables\r
836         read(gvar->pm.fi.PageFile,&gvar->pm.fi.ChunksInFile,sizeof(gvar->pm.fi.ChunksInFile));\r
837         read(gvar->pm.fi.PageFile,&gvar->pm.fi.PMSpriteStart,sizeof(gvar->pm.fi.PMSpriteStart));\r
838         read(gvar->pm.fi.PageFile,&gvar->pm.fi.PMSoundStart,sizeof(gvar->pm.fi.PMSoundStart));\r
839 \r
840         // Allocate and clear the page list\r
841         gvar->pm.PMNumBlocks = gvar->pm.fi.ChunksInFile;\r
842         MM_GetPtr((memptr *)gvar->pm.PMSegPages, sizeof(PageListStruct) * (gvar->pm.PMNumBlocks), gvar);\r
843         MM_SetLock((memptr *)gvar->pm.PMSegPages,true, gvar);\r
844         gvar->pm.PMPages = (PageListStruct far *)gvar->pm.PMSegPages;\r
845         _fmemset(gvar->pm.PMPages,0,sizeof(PageListStruct) * gvar->pm.PMNumBlocks);\r
846 \r
847         // Read in the chunk offsets\r
848         size = sizeof(dword) * gvar->pm.fi.ChunksInFile;\r
849         MM_GetPtr(&buf, size, gvar);\r
850         if (!CA_FarRead(gvar->pm.fi.PageFile,(byte far *)buf,size, gvar))\r
851         {\r
852                 Quit("PML_OpenPageFile: Offset read failed");\r
853                 //return;\r
854         }\r
855         offsetptr = (dword far *)buf;\r
856         for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.fi.ChunksInFile;i++,page++)\r
857                 page->offset = *offsetptr++;\r
858         MM_FreePtr(&buf, gvar);\r
859 \r
860         // Read in the chunk lengths\r
861         size = sizeof(word) * gvar->pm.fi.ChunksInFile;\r
862         MM_GetPtr(&buf,size, gvar);\r
863         if (!CA_FarRead(gvar->pm.fi.PageFile,(byte far *)buf,size, gvar))\r
864         {\r
865                 Quit("PML_OpenPageFile: Length read failed");\r
866                 //return;\r
867         }\r
868         lengthptr = (word far *)buf;\r
869         for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.fi.ChunksInFile;i++,page++)\r
870                 page->length = *lengthptr++;\r
871         MM_FreePtr(&buf, gvar);\r
872 }\r
873 \r
874 //\r
875 //  PML_ClosePageFile() - Closes the page file\r
876 //\r
877 void\r
878 PML_ClosePageFile(global_game_variables_t *gvar)\r
879 {\r
880         if (gvar->pm.fi.PageFile != -1)\r
881                 close(gvar->pm.fi.PageFile);\r
882         if (gvar->pm.PMSegPages)\r
883         {\r
884                 MM_SetLock((memptr)gvar->pm.PMSegPages,false, gvar);\r
885                 MM_FreePtr((memptr)gvar->pm.PMSegPages, gvar);\r
886         }\r
887 }\r
888 \r
889 /////////////////////////////////////////////////////////////////////////////\r
890 //\r
891 //      Allocation, etc., code\r
892 //\r
893 /////////////////////////////////////////////////////////////////////////////\r
894 \r
895 //\r
896 //      PML_GetEMSAddress()\r
897 //\r
898 //              Page is in EMS, so figure out which EMS physical page should be used\r
899 //              to map our page in. If normal page, use EMS physical page 3, else\r
900 //              use the physical page specified by the lock type\r
901 //\r
902 #ifndef __DEBUG_2__\r
903 #pragma argsused        // DEBUG - remove lock parameter\r
904 memptr\r
905 PML_GetEMSAddress(int page,PMLockType lock, global_game_variables_t *gvar)\r
906 {\r
907         int             i,emspage;\r
908         word    emsoff,emsbase,offset;\r
909 \r
910         emsoff = page & (PMEMSSubPage - 1);\r
911         emsbase = page - emsoff;\r
912 \r
913         emspage = -1;\r
914         // See if this page is already mapped in\r
915         for (i = 0;i < EMSFrameCount;i++)\r
916         {\r
917                 if (gvar->pm.emm.EMSList[i].baseEMSPage == emsbase)\r
918                 {\r
919                         emspage = i;    // Yep - don't do a redundant remapping\r
920                         break;\r
921                 }\r
922         }\r
923 \r
924         // If page isn't already mapped in, find LRU EMS frame, and use it\r
925         if (emspage == -1)\r
926         {\r
927                 dword last = LONG_MAX;\r
928                 for (i = 0;i < EMSFrameCount;i++)\r
929                 {\r
930                         if (gvar->pm.emm.EMSList[i].lastHit < last)\r
931                         {\r
932                                 emspage = i;\r
933                                 last = gvar->pm.emm.EMSList[i].lastHit;\r
934                         }\r
935                 }\r
936 \r
937                 gvar->pm.emm.EMSList[emspage].baseEMSPage = emsbase;\r
938                 PML_MapEMS(page / PMEMSSubPage,emspage, gvar);\r
939         }\r
940 \r
941         if (emspage == -1)\r
942                 Quit("PML_GetEMSAddress: EMS find failed");\r
943 \r
944         gvar->pm.emm.EMSList[emspage].lastHit = gvar->pm.PMFrameCount;\r
945         offset = emspage * EMSPageSizeSeg;\r
946         offset += emsoff * PMPageSizeSeg;\r
947         return((memptr)(gvar->pm.emm.EMSPageFrame + offset));\r
948 }\r
949 #else\r
950 memptr\r
951 PML_GetEMSAddress(int page,PMLockType lock, global_game_variables_t *gvar)\r
952 {\r
953         word    emspage;\r
954 \r
955         emspage = (lock < pml_EMSLock)? 3 : (lock - pml_EMSLock);\r
956 \r
957         PML_MapEMS(page / PMEMSSubPage,emspage);\r
958 \r
959         return((memptr)(EMSPageFrame + (emspage * EMSPageSizeSeg)\r
960                         + ((page & (PMEMSSubPage - 1)) * PMPageSizeSeg)));\r
961 }\r
962 #endif\r
963 \r
964 //\r
965 //      PM_GetPageAddress() - Returns the address of a given page\r
966 //              Maps in EMS if necessary\r
967 //              Returns nil if block isn't cached into Main Memory or EMS\r
968 //\r
969 //\r
970 memptr\r
971 PM_GetPageAddress(int pagenum, global_game_variables_t *gvar)\r
972 {\r
973         PageListStruct  far *page;\r
974 \r
975         page = &gvar->pm.PMPages[pagenum];\r
976         if (page->mainPage != -1)\r
977                 return(gvar->pm.mm.MainMemPages[page->mainPage]);\r
978         else if (page->emsPage != -1)\r
979                 return(PML_GetEMSAddress(page->emsPage,page->locked, gvar));\r
980         else\r
981                 return(nil);\r
982 }\r
983 \r
984 //\r
985 //      PML_GiveLRUPage() - Returns the page # of the least recently used\r
986 //              present & unlocked main/EMS page (or main page if mainonly is true)\r
987 //\r
988 int\r
989 PML_GiveLRUPage(boolean mainonly, global_game_variables_t *gvar)\r
990 {\r
991         int                             i,lru;\r
992         long                    last;\r
993         PageListStruct  far *page;\r
994 \r
995         for (i = 0,page = gvar->pm.PMPages,lru = -1,last = LONG_MAX;i < gvar->pm.fi.ChunksInFile;i++,page++)\r
996         {\r
997                 if\r
998                 (\r
999                         (page->lastHit < last)\r
1000                 &&      ((page->emsPage != -1) || (page->mainPage != -1))\r
1001                 &&      (page->locked == pml_Unlocked)\r
1002                 &&      (!(mainonly && (page->mainPage == -1)))\r
1003                 )\r
1004                 {\r
1005                         last = page->lastHit;\r
1006                         lru = i;\r
1007                 }\r
1008         }\r
1009 \r
1010         if (lru == -1)\r
1011                 Quit("PML_GiveLRUPage: LRU Search failed");\r
1012         return(lru);\r
1013 }\r
1014 \r
1015 //\r
1016 //      PML_GiveLRUXMSPage() - Returns the page # of the least recently used\r
1017 //              (and present) XMS page.\r
1018 //      This routine won't return the XMS page protected (by XMSProtectPage)\r
1019 //\r
1020 int\r
1021 PML_GiveLRUXMSPage(global_game_variables_t *gvar)\r
1022 {\r
1023         int                             i,lru;\r
1024         long                    last;\r
1025         PageListStruct  far *page;\r
1026 \r
1027         for (i = 0,page = gvar->pm.PMPages,lru = -1,last = LONG_MAX;i < gvar->pm.fi.ChunksInFile;i++,page++)\r
1028         {\r
1029                 if\r
1030                 (\r
1031                         (page->xmsPage != -1)\r
1032                 &&      (page->lastHit < last)\r
1033                 &&      (i != gvar->pm.xmm.XMSProtectPage)\r
1034                 )\r
1035                 {\r
1036                         last = page->lastHit;\r
1037                         lru = i;\r
1038                 }\r
1039         }\r
1040         return(lru);\r
1041 }\r
1042 \r
1043 //\r
1044 //      PML_PutPageInXMS() - If page isn't in XMS, find LRU XMS page and replace\r
1045 //              it with the main/EMS page\r
1046 //\r
1047 void\r
1048 PML_PutPageInXMS(int pagenum, global_game_variables_t *gvar)\r
1049 {\r
1050         int                             usexms;\r
1051         PageListStruct  far *page;\r
1052 \r
1053         if (!gvar->pm.xmm.XMSPresent)\r
1054                 return;\r
1055 \r
1056         page = &gvar->pm.PMPages[pagenum];\r
1057         if (page->xmsPage != -1)\r
1058                 return;                                 // Already in XMS\r
1059 \r
1060         if (gvar->pm.XMSPagesUsed < gvar->pm.xmm.XMSPagesAvail)\r
1061                 page->xmsPage = gvar->pm.XMSPagesUsed++;\r
1062         else\r
1063         {\r
1064                 usexms = PML_GiveLRUXMSPage(gvar);\r
1065                 if (usexms == -1)\r
1066                         Quit("PML_PutPageInXMS: No XMS LRU");\r
1067                 page->xmsPage = gvar->pm.PMPages[usexms].xmsPage;\r
1068                 gvar->pm.PMPages[usexms].xmsPage = -1;\r
1069         }\r
1070         PML_CopyToXMS(PM_GetPageAddress(pagenum, gvar),page->xmsPage,page->length, gvar);\r
1071 }\r
1072 \r
1073 //\r
1074 //      PML_TransferPageSpace() - A page is being replaced, so give the new page\r
1075 //              the old one's address space. Returns the address of the new page.\r
1076 //\r
1077 memptr\r
1078 PML_TransferPageSpace(int orig,int new, global_game_variables_t *gvar)\r
1079 {\r
1080         memptr                  addr;\r
1081         PageListStruct  far *origpage,far *newpage;\r
1082 \r
1083         if (orig == new)\r
1084                 Quit("PML_TransferPageSpace: Identity replacement");\r
1085 \r
1086         origpage = &gvar->pm.PMPages[orig];\r
1087         newpage = &gvar->pm.PMPages[new];\r
1088 \r
1089         if (origpage->locked != pml_Unlocked)\r
1090                 Quit("PML_TransferPageSpace: Killing locked page");\r
1091 \r
1092         if ((origpage->emsPage == -1) && (origpage->mainPage == -1))\r
1093                 Quit("PML_TransferPageSpace: Reusing non-existent page");\r
1094 \r
1095         // Copy page that's about to be purged into XMS\r
1096         PML_PutPageInXMS(orig, gvar);\r
1097 \r
1098         // Get the address, and force EMS into a physical page if necessary\r
1099         addr = PM_GetPageAddress(orig, gvar);\r
1100 \r
1101         // Steal the address\r
1102         newpage->emsPage = origpage->emsPage;\r
1103         newpage->mainPage = origpage->mainPage;\r
1104 \r
1105         // Mark replaced page as purged\r
1106         origpage->mainPage = origpage->emsPage = -1;\r
1107 \r
1108         if (!addr)\r
1109                 Quit("PML_TransferPageSpace: Zero replacement");\r
1110 \r
1111         return(addr);\r
1112 }\r
1113 \r
1114 //\r
1115 //      PML_GetAPageBuffer() - A page buffer is needed. Either get it from the\r
1116 //              main/EMS free pool, or use PML_GiveLRUPage() to find which page to\r
1117 //              steal the buffer from. Returns a far pointer to the page buffer, and\r
1118 //              sets the fields inside the given page structure appropriately.\r
1119 //              If mainonly is true, free EMS will be ignored, and only main pages\r
1120 //              will be looked at by PML_GiveLRUPage().\r
1121 //\r
1122 byte far *\r
1123 PML_GetAPageBuffer(int pagenum,boolean mainonly, global_game_variables_t *gvar)\r
1124 {\r
1125         byte                    far *addr = nil;\r
1126         int                             i,n;\r
1127         PMBlockAttr             *used;\r
1128         PageListStruct  far *page;\r
1129 \r
1130         page = &gvar->pm.PMPages[pagenum];\r
1131         if ((gvar->pm.EMSPagesUsed < gvar->pm.emm.EMSPagesAvail) && !mainonly)\r
1132         {\r
1133                 // There's remaining EMS - use it\r
1134                 page->emsPage = gvar->pm.EMSPagesUsed++;\r
1135                 addr = PML_GetEMSAddress(page->emsPage,page->locked, gvar);\r
1136         }\r
1137         else if (gvar->pm.MainPagesUsed < gvar->pm.mm.MainPagesAvail)\r
1138         {\r
1139                 // There's remaining main memory - use it\r
1140                 for (i = 0,n = -1,used = gvar->pm.mm.MainMemUsed;i < PMMaxMainMem;i++,used++)\r
1141                 {\r
1142                         if ((*used & pmba_Allocated) && !(*used & pmba_Used))\r
1143                         {\r
1144                                 n = i;\r
1145                                 *used |= pmba_Used;\r
1146                                 break;\r
1147                         }\r
1148                 }\r
1149                 if (n == -1)\r
1150                         Quit("PML_GetPageBuffer: MainPagesAvail lied");\r
1151                 addr = gvar->pm.mm.MainMemPages[n];\r
1152                 if (!addr)\r
1153                         Quit("PML_GetPageBuffer: Purged main block");\r
1154                 page->mainPage = n;\r
1155                 gvar->pm.MainPagesUsed++;\r
1156         }\r
1157         else\r
1158                 addr = PML_TransferPageSpace(PML_GiveLRUPage(mainonly, gvar),pagenum, gvar);\r
1159 \r
1160         if (!addr)\r
1161                 Quit("PML_GetPageBuffer: Search failed");\r
1162         return(addr);\r
1163 }\r
1164 \r
1165 //\r
1166 //      PML_GetPageFromXMS() - If page is in XMS, find LRU main/EMS page and\r
1167 //              replace it with the page from XMS. If mainonly is true, will only\r
1168 //              search for LRU main page.\r
1169 //      XMSProtectPage is set to the page to be retrieved from XMS, so that if\r
1170 //              the page from which we're stealing the main/EMS from isn't in XMS,\r
1171 //              it won't copy over the page that we're trying to get from XMS.\r
1172 //              (pages that are being purged are copied into XMS, if possible)\r
1173 //\r
1174 memptr\r
1175 PML_GetPageFromXMS(int pagenum,boolean mainonly, global_game_variables_t *gvar)\r
1176 {\r
1177         byte                    far *checkaddr;\r
1178         memptr                  addr = nil;\r
1179         PageListStruct  far *page;\r
1180 \r
1181         page = &gvar->pm.PMPages[pagenum];\r
1182         if (gvar->pm.xmm.XMSPresent && (page->xmsPage != -1))\r
1183         {\r
1184                 gvar->pm.xmm.XMSProtectPage = pagenum;\r
1185                 checkaddr = PML_GetAPageBuffer(pagenum,mainonly, gvar);\r
1186                 if (FP_OFF(checkaddr))\r
1187                         Quit("PML_GetPageFromXMS: Non segment pointer");\r
1188                 addr = (memptr)FP_SEG(checkaddr);\r
1189                 PML_CopyFromXMS(addr,page->xmsPage,page->length, gvar);\r
1190                 gvar->pm.xmm.XMSProtectPage = -1;\r
1191         }\r
1192 \r
1193         return(addr);\r
1194 }\r
1195 \r
1196 //\r
1197 //      PML_LoadPage() - A page is not in main/EMS memory, and it's not in XMS.\r
1198 //              Load it into either main or EMS. If mainonly is true, the page will\r
1199 //              only be loaded into main.\r
1200 //\r
1201 void\r
1202 PML_LoadPage(int pagenum,boolean mainonly, global_game_variables_t *gvar)\r
1203 {\r
1204         byte                    far *addr;\r
1205         PageListStruct  far *page;\r
1206 \r
1207         addr = PML_GetAPageBuffer(pagenum,mainonly, gvar);\r
1208         page = &gvar->pm.PMPages[pagenum];\r
1209         PML_ReadFromFile(addr,page->offset,page->length, gvar);\r
1210 }\r
1211 \r
1212 //\r
1213 //      PM_GetPage() - Returns the address of the page, loading it if necessary\r
1214 //              First, check if in Main Memory or EMS\r
1215 //              Then, check XMS\r
1216 //              If not in XMS, load into Main Memory or EMS\r
1217 //\r
1218 #pragma warn -pia\r
1219 memptr\r
1220 PM_GetPage(int pagenum, global_game_variables_t *gvar)\r
1221 {\r
1222         memptr  result;\r
1223 \r
1224         if (pagenum >= gvar->pm.fi.ChunksInFile)\r
1225                 Quit("PM_GetPage: Invalid page request");\r
1226 \r
1227 #ifdef __DEBUG_2__      // for debugging\r
1228         __asm {\r
1229                 mov     dx,STATUS_REGISTER_1\r
1230                 in      al,dx\r
1231                 mov     dx,ATR_INDEX\r
1232                 mov     al,ATR_OVERSCAN\r
1233                 out     dx,al\r
1234                 mov     al,10   // bright green\r
1235                 out     dx,al\r
1236         }\r
1237 #endif\r
1238 \r
1239         if (!(result = PM_GetPageAddress(pagenum, gvar)))\r
1240         {\r
1241                 boolean mainonly = (pagenum >= gvar->pm.fi.PMSoundStart);\r
1242 if (!gvar->pm.PMPages[pagenum].offset)  // JDC: sparse page\r
1243         Quit ("Tried to load a sparse page!");\r
1244                 if (!(result = PML_GetPageFromXMS(pagenum,mainonly, gvar)))\r
1245                 {\r
1246                         if (gvar->pm.PMPages[pagenum].lastHit ==  gvar->pm.PMFrameCount)\r
1247                                 gvar->pm.PMThrashing++;\r
1248 \r
1249                         PML_LoadPage(pagenum,mainonly, gvar);\r
1250                         result = PM_GetPageAddress(pagenum, gvar);\r
1251                 }\r
1252         }\r
1253         gvar->pm.PMPages[pagenum].lastHit =  gvar->pm.PMFrameCount;\r
1254 \r
1255 #ifdef __DEBUG_2__      // for debugging\r
1256         __asm{\r
1257                 mov     dx,STATUS_REGISTER_1\r
1258                 in      al,dx\r
1259                 mov     dx,ATR_INDEX\r
1260                 mov     al,ATR_OVERSCAN\r
1261                 out     dx,al\r
1262                 mov     al,3    // blue\r
1263                 out     dx,al\r
1264                 mov     al,0x20 // normal\r
1265                 out     dx,al\r
1266 #endif\r
1267 \r
1268         return(result);\r
1269 }\r
1270 #pragma warn +pia\r
1271 \r
1272 //\r
1273 //      PM_SetPageLock() - Sets the lock type on a given page\r
1274 //              pml_Unlocked: Normal, page can be purged\r
1275 //              pml_Locked: Cannot be purged\r
1276 //              pml_EMS?: Same as pml_Locked, but if in EMS, use the physical page\r
1277 //                                      specified when returning the address. For sound stuff.\r
1278 //\r
1279 void\r
1280 PM_SetPageLock(int pagenum,PMLockType lock, global_game_variables_t *gvar)\r
1281 {\r
1282         if (pagenum < gvar->pm.fi.PMSoundStart)\r
1283                 Quit("PM_SetPageLock: Locking/unlocking non-sound page");\r
1284 \r
1285         gvar->pm.PMPages[pagenum].locked = lock;\r
1286 }\r
1287 \r
1288 //\r
1289 //      PM_Preload() - Loads as many pages as possible into all types of memory.\r
1290 //              Calls the update function after each load, indicating the current\r
1291 //              page, and the total pages that need to be loaded (for thermometer).\r
1292 //\r
1293 void\r
1294 PM_Preload(boolean (*update)(word current,word total), global_game_variables_t *gvar)\r
1295 {\r
1296         int                             i,j,\r
1297                                         page,oogypage;\r
1298         word                    current,total,\r
1299                                         totalnonxms,totalxms,\r
1300                                         mainfree,maintotal,\r
1301                                         emsfree,emstotal,\r
1302                                         xmsfree,xmstotal;\r
1303         memptr                  addr;\r
1304         PageListStruct  far *p;\r
1305 \r
1306         mainfree = (gvar->pm.mm.MainPagesAvail - gvar->pm.MainPagesUsed) + (gvar->pm.emm.EMSPagesAvail - gvar->pm.EMSPagesUsed);\r
1307         xmsfree = (gvar->pm.xmm.XMSPagesAvail - gvar->pm.XMSPagesUsed);\r
1308 \r
1309         xmstotal = maintotal = 0;\r
1310 \r
1311         for (i = 0;i < gvar->pm.fi.ChunksInFile;i++)\r
1312         {\r
1313                 if (!gvar->pm.PMPages[i].offset)\r
1314                         continue;                       // sparse\r
1315 \r
1316                 if ( gvar->pm.PMPages[i].emsPage != -1 || gvar->pm.PMPages[i].mainPage != -1 )\r
1317                         continue;                       // already in main mem\r
1318 \r
1319                 if ( mainfree )\r
1320                 {\r
1321                         maintotal++;\r
1322                         mainfree--;\r
1323                 }\r
1324                 else if ( xmsfree && (gvar->pm.PMPages[i].xmsPage == -1) )\r
1325                 {\r
1326                         xmstotal++;\r
1327                         xmsfree--;\r
1328                 }\r
1329         }\r
1330 \r
1331 \r
1332         total = maintotal + xmstotal;\r
1333 \r
1334         if (!total)\r
1335                 return;\r
1336 \r
1337         page = 0;\r
1338         current = 0;\r
1339 \r
1340 //\r
1341 // cache main/ems blocks\r
1342 //\r
1343         while (maintotal)\r
1344         {\r
1345                 while ( !gvar->pm.PMPages[page].offset || gvar->pm.PMPages[page].mainPage != -1\r
1346                         ||      gvar->pm.PMPages[page].emsPage != -1 )\r
1347                         page++;\r
1348 \r
1349                 if (page >= gvar->pm.fi.ChunksInFile)\r
1350                         Quit ("PM_Preload: Pages>=gvar->pm.fi.ChunksInFile");\r
1351 \r
1352                 PM_GetPage(page, gvar);\r
1353 \r
1354                 page++;\r
1355                 current++;\r
1356                 maintotal--;\r
1357                 update(current,total);\r
1358         }\r
1359 \r
1360 //\r
1361 // load stuff to XMS\r
1362 //\r
1363         if (xmstotal)\r
1364         {\r
1365                 for (oogypage = 0 ; gvar->pm.PMPages[oogypage].mainPage == -1 ; oogypage++)\r
1366                 ;\r
1367                 addr = PM_GetPage(oogypage, gvar);\r
1368                 if (!addr)\r
1369                         Quit("PM_Preload: XMS buffer failed");\r
1370 \r
1371                 while (xmstotal)\r
1372                 {\r
1373                         while ( !gvar->pm.PMPages[page].offset || gvar->pm.PMPages[page].xmsPage != -1 )\r
1374                                 page++;\r
1375 \r
1376                         if (page >= gvar->pm.fi.ChunksInFile)\r
1377                                 Quit ("PM_Preload: Pages>=gvar->pm.fi.ChunksInFile");\r
1378 \r
1379                         p = &gvar->pm.PMPages[page];\r
1380 \r
1381                         p->xmsPage = gvar->pm.XMSPagesUsed++;\r
1382                         if (gvar->pm.XMSPagesUsed > gvar->pm.xmm.XMSPagesAvail)\r
1383                                 Quit("PM_Preload: Exceeded XMS pages");\r
1384                         if (p->length > PMPageSize)\r
1385                                 Quit("PM_Preload: Page too long");\r
1386 \r
1387                         PML_ReadFromFile((byte far *)addr,p->offset,p->length, gvar);\r
1388                         PML_CopyToXMS((byte far *)addr,p->xmsPage,p->length, gvar);\r
1389 \r
1390                         page++;\r
1391                         current++;\r
1392                         xmstotal--;\r
1393                         update(current,total);\r
1394                 }\r
1395 \r
1396                 p = &gvar->pm.PMPages[oogypage];\r
1397                 PML_ReadFromFile((byte far *)addr,p->offset,p->length, gvar);\r
1398         }\r
1399 \r
1400         update(total,total);\r
1401 }\r
1402 \r
1403 /////////////////////////////////////////////////////////////////////////////\r
1404 //\r
1405 //      General code\r
1406 //\r
1407 /////////////////////////////////////////////////////////////////////////////\r
1408 \r
1409 //\r
1410 //      PM_NextFrame() - Increments the frame counter and adjusts the thrash\r
1411 //              avoidence variables\r
1412 //\r
1413 //              If currently in panic mode (to avoid thrashing), check to see if the\r
1414 //                      appropriate number of frames have passed since the last time that\r
1415 //                      we would have thrashed. If so, take us out of panic mode.\r
1416 //\r
1417 //\r
1418 void\r
1419 PM_NextFrame(global_game_variables_t *gvar)\r
1420 {\r
1421         int     i;\r
1422 \r
1423         // Frame count overrun - kill the LRU hit entries & reset frame count\r
1424         if (++gvar->pm.PMFrameCount >= LONG_MAX - 4)\r
1425         {\r
1426                 for (i = 0;i < gvar->pm.PMNumBlocks;i++)\r
1427                         gvar->pm.PMPages[i].lastHit = 0;\r
1428                 gvar->pm.PMFrameCount = 0;\r
1429         }\r
1430 \r
1431 //#if 0\r
1432         for (i = 0;i < gvar->pm.fi.PMSoundStart;i++)\r
1433         {\r
1434                 if (gvar->pm.PMPages[i].locked)\r
1435                 {\r
1436                         char buf[40];\r
1437                         sprintf(buf,"PM_NextFrame: Page %d is locked",i);\r
1438                         Quit(buf);\r
1439                 }\r
1440         }\r
1441 //#endif\r
1442 \r
1443         if (gvar->pm.PMPanicMode)\r
1444         {\r
1445                 // DEBUG - set border color\r
1446                 if ((!gvar->pm.PMThrashing) && (!--gvar->pm.PMPanicMode))\r
1447                 {\r
1448                         // DEBUG - reset border color\r
1449                 }\r
1450         }\r
1451         if (gvar->pm.PMThrashing >= PMThrashThreshold)\r
1452                 gvar->pm.PMPanicMode = PMUnThrashThreshold;\r
1453         gvar->pm.PMThrashing = false;\r
1454 }\r
1455 \r
1456 //\r
1457 //      PM_Reset() - Sets up caching structures\r
1458 //\r
1459 void\r
1460 PM_Reset(global_game_variables_t *gvar)\r
1461 {\r
1462         int                             i;\r
1463         PageListStruct  far *page;\r
1464 \r
1465         gvar->pm.xmm.XMSPagesAvail = gvar->pm.xmm.XMSAvail / PMPageSizeKB;\r
1466 \r
1467         gvar->pm.emm.EMSPagesAvail = gvar->pm.emm.EMSAvail * (EMSPageSizeKB / PMPageSizeKB);\r
1468         gvar->pm.emm.EMSPhysicalPage = 0;\r
1469 \r
1470         gvar->pm.MainPagesUsed = gvar->pm.EMSPagesUsed = gvar->pm.XMSPagesUsed = 0;\r
1471 \r
1472         gvar->pm.PMPanicMode = false;\r
1473 \r
1474         gvar->pm.fi.PageFile = -1;\r
1475         gvar->pm.xmm.XMSProtectPage = -1;\r
1476 \r
1477         // Initialize page list\r
1478         for (i = 0,page = gvar->pm.PMPages;i < gvar->pm.PMNumBlocks;i++,page++)\r
1479         {\r
1480                 page->mainPage = -1;\r
1481                 page->emsPage = -1;\r
1482                 page->xmsPage = -1;\r
1483                 page->locked = false;\r
1484         }\r
1485 }\r
1486 \r
1487 //\r
1488 //      PM_Startup() - Start up the Page Mgr\r
1489 //\r
1490 void\r
1491 PM_Startup(global_game_variables_t *gvar)\r
1492 {\r
1493         boolean nomain,noems,noxms;\r
1494         int             i;\r
1495 \r
1496         if (gvar->pm.PMStarted)\r
1497                 return;\r
1498 \r
1499         //strcpy(&(gvar->pm.fi.PageFileName), "VSWAP.");\r
1500 \r
1501         nomain = noems = noxms = false;\r
1502         for (i = 1;i <\r
1503 #ifdef __WATCOMC__\r
1504         __argc\r
1505 #endif\r
1506 #ifdef __BORLANDC__\r
1507         _argc\r
1508 #endif\r
1509         ;i++)\r
1510         {\r
1511                 switch (US_CheckParm(\r
1512 #ifdef __WATCOMC__\r
1513         __argv[i]\r
1514 #endif\r
1515 #ifdef __BORLANDC__\r
1516         _argv[i]\r
1517 #endif\r
1518                 ,ParmStrings))\r
1519                 {\r
1520                 case 0:\r
1521                         nomain = true;\r
1522                         break;\r
1523                 case 1:\r
1524                         noems = true;\r
1525                         break;\r
1526                 case 2:\r
1527                         noxms = true;\r
1528                         break;\r
1529                 }\r
1530         }\r
1531 \r
1532         //0000+=+=PML_OpenPageFile(gvar);\r
1533 \r
1534         if (!noems && MML_CheckForEMS())\r
1535                 PML_StartupEMS(gvar);\r
1536         if (!noxms && MML_CheckForXMS())\r
1537                 PML_StartupXMS(gvar);\r
1538 \r
1539         if (!nomain && !gvar->pm.emm.EMSPresent)\r
1540         {\r
1541                 printf("PM_Startup: No main or EMS\n");\r
1542                 exit(-5);\r
1543                 //return;\r
1544         }\r
1545         PML_StartupMainMem(gvar);\r
1546 \r
1547         PM_Reset(gvar);\r
1548 \r
1549         gvar->pm.PMStarted = true;\r
1550 }\r
1551 \r
1552 //\r
1553 //      PM_Shutdown() - Shut down the Page Mgr\r
1554 //\r
1555 void\r
1556 PM_Shutdown(global_game_variables_t *gvar)\r
1557 {\r
1558         if(MML_CheckForXMS()) PML_ShutdownXMS(gvar);\r
1559         if(MML_CheckForEMS()) PML_ShutdownEMS(gvar);\r
1560 \r
1561         if (!gvar->pm.PMStarted)\r
1562                 return;\r
1563 \r
1564         PML_ClosePageFile(gvar);\r
1565 \r
1566         PML_ShutdownMainMem(gvar);\r
1567 }\r