]> 4ch.mooo.com Git - 16.git/blob - src/lib/16_pm.c
pdir
[16.git] / src / lib / 16_pm.c
1 //\r
2 //      ID_PM.C\r
3 //      Id Engine's Page Manager v1.0\r
4 //      Primary coder: Jason Blochowiak\r
5 //\r
6 \r
7 #include "src/lib/16_pm.h"\r
8 #pragma hdrstop\r
9 \r
10 //      Main Mem specific variables\r
11         boolean                 MainPresent;\r
12         memptr                  MainMemPages[PMMaxMainMem];\r
13         PMBlockAttr             MainMemUsed[PMMaxMainMem];\r
14         int                             MainPagesAvail;\r
15 \r
16 //      EMS specific variables\r
17         boolean                 EMSPresent;\r
18         word                    EMSAvail,EMSPagesAvail,EMSHandle,\r
19                                         EMSPageFrame,EMSPhysicalPage;\r
20         EMSListStruct   EMSList[EMSFrameCount];\r
21 \r
22 //      XMS specific variables\r
23         boolean                 XMSPresent;\r
24         word                    XMSAvail,XMSPagesAvail,XMSHandle;\r
25         longword                XMSDriver;\r
26         int                             XMSProtectPage = -1;\r
27 \r
28 //      File specific variables\r
29         char                    PageFileName[13] = {"VSWAP."};\r
30         int                             PageFile = -1;\r
31         word                    ChunksInFile;\r
32         word                    PMSpriteStart,PMSoundStart;\r
33 \r
34 //      General usage variables\r
35         boolean                 PMStarted,\r
36                                         PMPanicMode,\r
37                                         PMThrashing;\r
38         word                    XMSPagesUsed,\r
39                                         EMSPagesUsed,\r
40                                         MainPagesUsed,\r
41                                         PMNumBlocks;\r
42         long                    PMFrameCount;\r
43         PageListStruct  far *PMPages,\r
44                                         _seg *PMSegPages;\r
45 \r
46 static  char            *ParmStrings[] = {"nomain","noems","noxms",nil};\r
47 \r
48 /////////////////////////////////////////////////////////////////////////////\r
49 //\r
50 //      EMS Management code\r
51 //\r
52 /////////////////////////////////////////////////////////////////////////////\r
53 \r
54 //\r
55 //      PML_MapEMS() - Maps a logical page to a physical page\r
56 //\r
57 void\r
58 PML_MapEMS(word logical,word physical)\r
59 {
60         union REGS CPURegs;\r
61         CPURegs.h.al = physical;\r
62         CPURegs.x.bx = logical;\r
63         CPURegs.x.dx = EMSHandle;\r
64         CPURegs.h.ah = EMS_MAPPAGE;\r
65         __asm
66         {
67                 int     EMS_INT
68         }\r
69 \r
70         if(CPURegs.h.ah)\r
71                 printf("PML_MapEMS: Page mapping failed\n");\r
72 }\r
73 \r
74 //\r
75 //      PML_StartupEMS() - Sets up EMS for Page Mgr's use\r
76 //              Checks to see if EMS driver is present\r
77 //      Verifies that EMS hardware is present\r
78 //              Make sure that EMS version is 3.2 or later\r
79 //              If there's more than our minimum (2 pages) available, allocate it (up\r
80 //                      to the maximum we need)\r
81 //\r
82 \r
83         char    EMMDriverName[9] = "EMMXXXX0";\r
84 \r
85 boolean\r
86 PML_StartupEMS(void)\r
87 {\r
88         int             i;\r
89         long    size;\r
90 \r
91         EMSPresent = false;                     // Assume that we'll fail\r
92         EMSAvail = 0;\r
93 \r
94         _DX = (word)EMMDriverName;\r
95         _AX = 0x3d00;\r
96         geninterrupt(0x21);                     // try to open EMMXXXX0 device\r
97 asm     jnc     gothandle\r
98         goto error;\r
99 \r
100 gothandle:\r
101         _BX = _AX;\r
102         _AX = 0x4400;\r
103         geninterrupt(0x21);                     // get device info\r
104 asm     jnc     gotinfo;\r
105         goto error;\r
106 \r
107 gotinfo:\r
108 asm     and     dx,0x80\r
109         if (!_DX)\r
110                 goto error;\r
111 \r
112         _AX = 0x4407;\r
113         geninterrupt(0x21);                     // get status\r
114 asm     jc      error\r
115         if (!_AL)\r
116                 goto error;\r
117 \r
118         _AH = 0x3e;\r
119         geninterrupt(0x21);                     // close handle\r
120 \r
121         _AH = EMS_STATUS;\r
122         geninterrupt(EMS_INT);\r
123         if (_AH)\r
124                 goto error;                             // make sure EMS hardware is present\r
125 \r
126         _AH = EMS_VERSION;\r
127         geninterrupt(EMS_INT);\r
128         if (_AH || (_AL < 0x32))        // only work on EMS 3.2 or greater (silly, but...)\r
129                 goto error;\r
130 \r
131         _AH = EMS_GETFRAME;\r
132         geninterrupt(EMS_INT);\r
133         if (_AH)\r
134                 goto error;                             // find the page frame address\r
135         EMSPageFrame = _BX;\r
136 \r
137         _AH = EMS_GETPAGES;\r
138         geninterrupt(EMS_INT);\r
139         if (_AH)\r
140                 goto error;\r
141         if (_BX < 2)\r
142                 goto error;             // Require at least 2 pages (32k)\r
143         EMSAvail = _BX;\r
144 \r
145         // Don't hog all available EMS\r
146         size = EMSAvail * (long)EMSPageSize;\r
147         if (size - (EMSPageSize * 2) > (ChunksInFile * (long)PMPageSize))\r
148         {\r
149                 size = (ChunksInFile * (long)PMPageSize) + EMSPageSize;\r
150                 EMSAvail = size / EMSPageSize;\r
151         }\r
152 \r
153         _AH = EMS_ALLOCPAGES;\r
154         _BX = EMSAvail;\r
155         geninterrupt(EMS_INT);\r
156         if (_AH)\r
157                 goto error;\r
158         EMSHandle = _DX;\r
159 \r
160         mminfo.EMSmem += EMSAvail * (long)EMSPageSize;\r
161 \r
162         // Initialize EMS mapping cache\r
163         for (i = 0;i < EMSFrameCount;i++)\r
164                 EMSList[i].baseEMSPage = -1;\r
165 \r
166         EMSPresent = true;                      // We have EMS\r
167 \r
168 error:\r
169         return(EMSPresent);\r
170 }\r
171 \r
172 //\r
173 //      PML_ShutdownEMS() - If EMS was used, deallocate it\r
174 //\r
175 void\r
176 PML_ShutdownEMS(void)\r
177 {\r
178         if (EMSPresent)\r
179         {\r
180         asm     mov     ah,EMS_FREEPAGES\r
181         asm     mov     dx,[EMSHandle]\r
182         asm     int     EMS_INT\r
183                 if (_AH)\r
184                         printf("PML_ShutdownEMS: Error freeing EMS\n");\r
185         }\r
186 }\r
187 \r
188 /////////////////////////////////////////////////////////////////////////////\r
189 //\r
190 //      XMS Management code\r
191 //\r
192 /////////////////////////////////////////////////////////////////////////////\r
193 \r
194 //\r
195 //      PML_StartupXMS() - Starts up XMS for the Page Mgr's use\r
196 //              Checks for presence of an XMS driver\r
197 //              Makes sure that there's at least a page of XMS available\r
198 //              Allocates any remaining XMS (rounded down to the nearest page size)\r
199 //\r
200 boolean\r
201 PML_StartupXMS(void)\r
202 {\r
203         XMSPresent = false;                                     // Assume failure\r
204         XMSAvail = 0;\r
205 \r
206 asm     mov     ax,0x4300\r
207 asm     int     XMS_INT                                         // Check for presence of XMS driver\r
208         if (_AL != 0x80)\r
209                 goto error;\r
210 \r
211 \r
212 asm     mov     ax,0x4310\r
213 asm     int     XMS_INT                                                 // Get address of XMS driver\r
214 asm     mov     [WORD PTR XMSDriver],bx\r
215 asm     mov     [WORD PTR XMSDriver+2],es               // function pointer to XMS driver\r
216 \r
217         XMS_CALL(XMS_QUERYFREE);                        // Find out how much XMS is available\r
218         XMSAvail = _AX;\r
219         if (!_AX)                               // AJR: bugfix 10/8/92\r
220                 goto error;\r
221 \r
222         XMSAvail &= ~(PMPageSizeKB - 1);        // Round off to nearest page size\r
223         if (XMSAvail < (PMPageSizeKB * 2))      // Need at least 2 pages\r
224                 goto error;\r
225 \r
226         _DX = XMSAvail;\r
227         XMS_CALL(XMS_ALLOC);                            // And do the allocation\r
228         XMSHandle = _DX;\r
229 \r
230         if (!_AX)                               // AJR: bugfix 10/8/92\r
231         {\r
232                 XMSAvail = 0;\r
233                 goto error;\r
234         }\r
235 \r
236         mminfo.XMSmem += XMSAvail * 1024;\r
237 \r
238         XMSPresent = true;\r
239 error:\r
240         return(XMSPresent);\r
241 }\r
242 \r
243 //\r
244 //      PML_XMSCopy() - Copies a main/EMS page to or from XMS\r
245 //              Will round an odd-length request up to the next even value\r
246 //\r
247 void\r
248 PML_XMSCopy(boolean toxms,byte far *addr,word xmspage,word length)\r
249 {\r
250         dword   xoffset;\r
251         struct\r
252         {\r
253                 longword        length;\r
254                 word            source_handle;\r
255                 longword        source_offset;\r
256                 word            target_handle;\r
257                 longword        target_offset;\r
258         } copy;\r
259 \r
260         if (!addr)\r
261                 printf("PML_XMSCopy: zero address\n");\r
262 \r
263         xoffset = (longword)xmspage * PMPageSize;\r
264 \r
265         copy.length = (length + 1) & ~1;\r
266         copy.source_handle = toxms? 0 : XMSHandle;\r
267         copy.source_offset = toxms? (long)addr : xoffset;\r
268         copy.target_handle = toxms? XMSHandle : 0;\r
269         copy.target_offset = toxms? xoffset : (long)addr;\r
270 \r
271 asm     push si\r
272         _SI = (word)&copy;\r
273         XMS_CALL(XMS_MOVE);\r
274 asm     pop     si\r
275         if (!_AX)\r
276                 Quit("PML_XMSCopy: Error on copy");\r
277 }\r
278 \r
279 #if 1\r
280 #define PML_CopyToXMS(s,t,l)    PML_XMSCopy(true,(s),(t),(l))\r
281 #define PML_CopyFromXMS(t,s,l)  PML_XMSCopy(false,(t),(s),(l))\r
282 #else\r
283 //\r
284 //      PML_CopyToXMS() - Copies the specified number of bytes from the real mode\r
285 //              segment address to the specified XMS page\r
286 //\r
287 void\r
288 PML_CopyToXMS(byte far *source,int targetpage,word length)\r
289 {\r
290         PML_XMSCopy(true,source,targetpage,length);\r
291 }\r
292 \r
293 //\r
294 //      PML_CopyFromXMS() - Copies the specified number of bytes from an XMS\r
295 //              page to the specified real mode address\r
296 //\r
297 void\r
298 PML_CopyFromXMS(byte far *target,int sourcepage,word length)\r
299 {\r
300         PML_XMSCopy(false,target,sourcepage,length);\r
301 }\r
302 #endif\r
303 \r
304 //\r
305 //      PML_ShutdownXMS()\r
306 //\r
307 void\r
308 PML_ShutdownXMS(void)\r
309 {\r
310         if (XMSPresent)\r
311         {\r
312                 _DX = XMSHandle;\r
313                 XMS_CALL(XMS_FREE);\r
314                 if (_BL)\r
315                         Quit("PML_ShutdownXMS: Error freeing XMS");\r
316         }\r
317 }\r
318 \r
319 /////////////////////////////////////////////////////////////////////////////\r
320 //\r
321 //      Main memory code\r
322 //\r
323 /////////////////////////////////////////////////////////////////////////////\r
324 \r
325 //\r
326 //      PM_SetMainMemPurge() - Sets the purge level for all allocated main memory\r
327 //              blocks. This shouldn't be called directly - the PM_LockMainMem() and\r
328 //              PM_UnlockMainMem() macros should be used instead.\r
329 //\r
330 void\r
331 PM_SetMainMemPurge(int level)\r
332 {\r
333         int     i;\r
334 \r
335         for (i = 0;i < PMMaxMainMem;i++)\r
336                 if (MainMemPages[i])\r
337                         MM_SetPurge(&MainMemPages[i],level);\r
338 }\r
339 \r
340 //\r
341 //      PM_CheckMainMem() - If something besides the Page Mgr makes requests of\r
342 //              the Memory Mgr, some of the Page Mgr's blocks may have been purged,\r
343 //              so this function runs through the block list and checks to see if\r
344 //              any of the blocks have been purged. If so, it marks the corresponding\r
345 //              page as purged & unlocked, then goes through the block list and\r
346 //              tries to reallocate any blocks that have been purged.\r
347 //      This routine now calls PM_LockMainMem() to make sure that any allocation\r
348 //              attempts made during the block reallocation sweep don't purge any\r
349 //              of the other blocks. Because PM_LockMainMem() is called,\r
350 //              PM_UnlockMainMem() needs to be called before any other part of the\r
351 //              program makes allocation requests of the Memory Mgr.\r
352 //\r
353 void\r
354 PM_CheckMainMem(void)\r
355 {\r
356         boolean                 allocfailed;\r
357         int                             i,n;\r
358         memptr                  *p;\r
359         PMBlockAttr             *used;\r
360         PageListStruct  far *page;\r
361 \r
362         if (!MainPresent)\r
363                 return;\r
364 \r
365         for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)\r
366         {\r
367                 n = page->mainPage;\r
368                 if (n != -1)                                            // Is the page using main memory?\r
369                 {\r
370                         if (!MainMemPages[n])                   // Yep, was the block purged?\r
371                         {\r
372                                 page->mainPage = -1;            // Yes, mark page as purged & unlocked\r
373                                 page->locked = pml_Unlocked;\r
374                         }\r
375                 }\r
376         }\r
377 \r
378         // Prevent allocation attempts from purging any of our other blocks\r
379         PM_LockMainMem();\r
380         allocfailed = false;\r
381         for (i = 0,p = MainMemPages,used = MainMemUsed;i < PMMaxMainMem;i++,p++,used++)\r
382         {\r
383                 if (!*p)                                                        // If the page got purged\r
384                 {\r
385                         if (*used & pmba_Allocated)             // If it was allocated\r
386                         {\r
387                                 *used &= ~pmba_Allocated;       // Mark as unallocated\r
388                                 MainPagesAvail--;                       // and decrease available count\r
389                         }\r
390 \r
391                         if (*used & pmba_Used)                  // If it was used\r
392                         {\r
393                                 *used &= ~pmba_Used;            // Mark as unused\r
394                                 MainPagesUsed--;                        // and decrease used count\r
395                         }\r
396 \r
397                         if (!allocfailed)\r
398                         {\r
399                                 MM_BombOnError(false);\r
400                                 MM_GetPtr(p,PMPageSize);                // Try to reallocate\r
401                                 if (mmerror)                                    // If it failed,\r
402                                         allocfailed = true;                     //  don't try any more allocations\r
403                                 else                                                    // If it worked,\r
404                                 {\r
405                                         *used |= pmba_Allocated;        // Mark as allocated\r
406                                         MainPagesAvail++;                       // and increase available count\r
407                                 }\r
408                                 MM_BombOnError(true);\r
409                         }\r
410                 }\r
411         }\r
412         if (mmerror)\r
413                 mmerror = false;\r
414 }\r
415 \r
416 //\r
417 //      PML_StartupMainMem() - Allocates as much main memory as is possible for\r
418 //              the Page Mgr. The memory is allocated as non-purgeable, so if it's\r
419 //              necessary to make requests of the Memory Mgr, PM_UnlockMainMem()\r
420 //              needs to be called.\r
421 //\r
422 void\r
423 PML_StartupMainMem(void)\r
424 {\r
425         int             i,n;\r
426         memptr  *p;\r
427 \r
428         MainPagesAvail = 0;\r
429         MM_BombOnError(false);\r
430         for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++)\r
431         {\r
432                 MM_GetPtr(p,PMPageSize);\r
433                 if (mmerror)\r
434                         break;\r
435 \r
436                 MainPagesAvail++;\r
437                 MainMemUsed[i] = pmba_Allocated;\r
438         }\r
439         MM_BombOnError(true);\r
440         if (mmerror)\r
441                 mmerror = false;\r
442         if (MainPagesAvail < PMMinMainMem)\r
443                 Quit("PM_SetupMainMem: Not enough main memory");\r
444         MainPresent = true;\r
445 }\r
446 \r
447 //\r
448 //      PML_ShutdownMainMem() - Frees all of the main memory blocks used by the\r
449 //              Page Mgr.\r
450 //\r
451 void\r
452 PML_ShutdownMainMem(void)\r
453 {\r
454         int             i;\r
455         memptr  *p;\r
456 \r
457         // DEBUG - mark pages as unallocated & decrease page count as appropriate\r
458         for (i = 0,p = MainMemPages;i < PMMaxMainMem;i++,p++)\r
459                 if (*p)\r
460                         MM_FreePtr(p);\r
461 }\r
462 \r
463 /////////////////////////////////////////////////////////////////////////////\r
464 //\r
465 //      File management code\r
466 //\r
467 /////////////////////////////////////////////////////////////////////////////\r
468 \r
469 //\r
470 //      PML_ReadFromFile() - Reads some data in from the page file\r
471 //\r
472 void\r
473 PML_ReadFromFile(byte far *buf,long offset,word length)\r
474 {\r
475         if (!buf)\r
476                 Quit("PML_ReadFromFile: Null pointer");\r
477         if (!offset)\r
478                 Quit("PML_ReadFromFile: Zero offset");\r
479         if (lseek(PageFile,offset,SEEK_SET) != offset)\r
480                 Quit("PML_ReadFromFile: Seek failed");\r
481         if (!CA_FarRead(PageFile,buf,length))\r
482                 Quit("PML_ReadFromFile: Read failed");\r
483 }\r
484 \r
485 //\r
486 //      PML_OpenPageFile() - Opens the page file and sets up the page info\r
487 //\r
488 void\r
489 PML_OpenPageFile(void)\r
490 {\r
491         int                             i;\r
492         long                    size;\r
493         void                    _seg *buf;\r
494         longword                far *offsetptr;\r
495         word                    far *lengthptr;\r
496         PageListStruct  far *page;\r
497 \r
498         PageFile = open(PageFileName,O_RDONLY + O_BINARY);\r
499         if (PageFile == -1)\r
500                 Quit("PML_OpenPageFile: Unable to open page file");\r
501 \r
502         // Read in header variables\r
503         read(PageFile,&ChunksInFile,sizeof(ChunksInFile));\r
504         read(PageFile,&PMSpriteStart,sizeof(PMSpriteStart));\r
505         read(PageFile,&PMSoundStart,sizeof(PMSoundStart));\r
506 \r
507         // Allocate and clear the page list\r
508         PMNumBlocks = ChunksInFile;\r
509         MM_GetPtr(&(memptr)PMSegPages,sizeof(PageListStruct) * PMNumBlocks);\r
510         MM_SetLock(&(memptr)PMSegPages,true);\r
511         PMPages = (PageListStruct far *)PMSegPages;\r
512         _fmemset(PMPages,0,sizeof(PageListStruct) * PMNumBlocks);\r
513 \r
514         // Read in the chunk offsets\r
515         size = sizeof(longword) * ChunksInFile;\r
516         MM_GetPtr(&buf,size);\r
517         if (!CA_FarRead(PageFile,(byte far *)buf,size))\r
518                 Quit("PML_OpenPageFile: Offset read failed");\r
519         offsetptr = (longword far *)buf;\r
520         for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)\r
521                 page->offset = *offsetptr++;\r
522         MM_FreePtr(&buf);\r
523 \r
524         // Read in the chunk lengths\r
525         size = sizeof(word) * ChunksInFile;\r
526         MM_GetPtr(&buf,size);\r
527         if (!CA_FarRead(PageFile,(byte far *)buf,size))\r
528                 Quit("PML_OpenPageFile: Length read failed");\r
529         lengthptr = (word far *)buf;\r
530         for (i = 0,page = PMPages;i < ChunksInFile;i++,page++)\r
531                 page->length = *lengthptr++;\r
532         MM_FreePtr(&buf);\r
533 }\r
534 \r
535 //\r
536 //  PML_ClosePageFile() - Closes the page file\r
537 //\r
538 void\r
539 PML_ClosePageFile(void)\r
540 {\r
541         if (PageFile != -1)\r
542                 close(PageFile);\r
543         if (PMSegPages)\r
544         {\r
545                 MM_SetLock(&(memptr)PMSegPages,false);\r
546                 MM_FreePtr(&(void _seg *)PMSegPages);\r
547         }\r
548 }\r
549 \r
550 /////////////////////////////////////////////////////////////////////////////\r
551 //\r
552 //      Allocation, etc., code\r
553 //\r
554 /////////////////////////////////////////////////////////////////////////////\r
555 \r
556 //\r
557 //      PML_GetEMSAddress()\r
558 //\r
559 //              Page is in EMS, so figure out which EMS physical page should be used\r
560 //              to map our page in. If normal page, use EMS physical page 3, else\r
561 //              use the physical page specified by the lock type\r
562 //\r
563 #if 1\r
564 #pragma argsused        // DEBUG - remove lock parameter\r
565 memptr\r
566 PML_GetEMSAddress(int page,PMLockType lock)\r
567 {\r
568         int             i,emspage;\r
569         word    emsoff,emsbase,offset;\r
570 \r
571         emsoff = page & (PMEMSSubPage - 1);\r
572         emsbase = page - emsoff;\r
573 \r
574         emspage = -1;\r
575         // See if this page is already mapped in\r
576         for (i = 0;i < EMSFrameCount;i++)\r
577         {\r
578                 if (EMSList[i].baseEMSPage == emsbase)\r
579                 {\r
580                         emspage = i;    // Yep - don't do a redundant remapping\r
581                         break;\r
582                 }\r
583         }\r
584 \r
585         // If page isn't already mapped in, find LRU EMS frame, and use it\r
586         if (emspage == -1)\r
587         {\r
588                 longword last = MAXLONG;\r
589                 for (i = 0;i < EMSFrameCount;i++)\r
590                 {\r
591                         if (EMSList[i].lastHit < last)\r
592                         {\r
593                                 emspage = i;\r
594                                 last = EMSList[i].lastHit;\r
595                         }\r
596                 }\r
597 \r
598                 EMSList[emspage].baseEMSPage = emsbase;\r
599                 PML_MapEMS(page / PMEMSSubPage,emspage);\r
600         }\r
601 \r
602         if (emspage == -1)\r
603                 Quit("PML_GetEMSAddress: EMS find failed");\r
604 \r
605         EMSList[emspage].lastHit = PMFrameCount;\r
606         offset = emspage * EMSPageSizeSeg;\r
607         offset += emsoff * PMPageSizeSeg;\r
608         return((memptr)(EMSPageFrame + offset));\r
609 }\r
610 #else\r
611 memptr\r
612 PML_GetEMSAddress(int page,PMLockType lock)\r
613 {\r
614         word    emspage;\r
615 \r
616         emspage = (lock < pml_EMSLock)? 3 : (lock - pml_EMSLock);\r
617 \r
618         PML_MapEMS(page / PMEMSSubPage,emspage);\r
619 \r
620         return((memptr)(EMSPageFrame + (emspage * EMSPageSizeSeg)\r
621                         + ((page & (PMEMSSubPage - 1)) * PMPageSizeSeg)));\r
622 }\r
623 #endif\r
624 \r
625 //\r
626 //      PM_GetPageAddress() - Returns the address of a given page\r
627 //              Maps in EMS if necessary\r
628 //              Returns nil if block isn't cached into Main Memory or EMS\r
629 //\r
630 //\r
631 memptr\r
632 PM_GetPageAddress(int pagenum)\r
633 {\r
634         PageListStruct  far *page;\r
635 \r
636         page = &PMPages[pagenum];\r
637         if (page->mainPage != -1)\r
638                 return(MainMemPages[page->mainPage]);\r
639         else if (page->emsPage != -1)\r
640                 return(PML_GetEMSAddress(page->emsPage,page->locked));\r
641         else\r
642                 return(nil);\r
643 }\r
644 \r
645 //\r
646 //      PML_GiveLRUPage() - Returns the page # of the least recently used\r
647 //              present & unlocked main/EMS page (or main page if mainonly is true)\r
648 //\r
649 int\r
650 PML_GiveLRUPage(boolean mainonly)\r
651 {\r
652         int                             i,lru;\r
653         long                    last;\r
654         PageListStruct  far *page;\r
655 \r
656         for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)\r
657         {\r
658                 if\r
659                 (\r
660                         (page->lastHit < last)\r
661                 &&      ((page->emsPage != -1) || (page->mainPage != -1))\r
662                 &&      (page->locked == pml_Unlocked)\r
663                 &&      (!(mainonly && (page->mainPage == -1)))\r
664                 )\r
665                 {\r
666                         last = page->lastHit;\r
667                         lru = i;\r
668                 }\r
669         }\r
670 \r
671         if (lru == -1)\r
672                 Quit("PML_GiveLRUPage: LRU Search failed");\r
673         return(lru);\r
674 }\r
675 \r
676 //\r
677 //      PML_GiveLRUXMSPage() - Returns the page # of the least recently used\r
678 //              (and present) XMS page.\r
679 //      This routine won't return the XMS page protected (by XMSProtectPage)\r
680 //\r
681 int\r
682 PML_GiveLRUXMSPage(void)\r
683 {\r
684         int                             i,lru;\r
685         long                    last;\r
686         PageListStruct  far *page;\r
687 \r
688         for (i = 0,page = PMPages,lru = -1,last = MAXLONG;i < ChunksInFile;i++,page++)\r
689         {\r
690                 if\r
691                 (\r
692                         (page->xmsPage != -1)\r
693                 &&      (page->lastHit < last)\r
694                 &&      (i != XMSProtectPage)\r
695                 )\r
696                 {\r
697                         last = page->lastHit;\r
698                         lru = i;\r
699                 }\r
700         }\r
701         return(lru);\r
702 }\r
703 \r
704 //\r
705 //      PML_PutPageInXMS() - If page isn't in XMS, find LRU XMS page and replace\r
706 //              it with the main/EMS page\r
707 //\r
708 void\r
709 PML_PutPageInXMS(int pagenum)\r
710 {\r
711         int                             usexms;\r
712         PageListStruct  far *page;\r
713 \r
714         if (!XMSPresent)\r
715                 return;\r
716 \r
717         page = &PMPages[pagenum];\r
718         if (page->xmsPage != -1)\r
719                 return;                                 // Already in XMS\r
720 \r
721         if (XMSPagesUsed < XMSPagesAvail)\r
722                 page->xmsPage = XMSPagesUsed++;\r
723         else\r
724         {\r
725                 usexms = PML_GiveLRUXMSPage();\r
726                 if (usexms == -1)\r
727                         Quit("PML_PutPageInXMS: No XMS LRU");\r
728                 page->xmsPage = PMPages[usexms].xmsPage;\r
729                 PMPages[usexms].xmsPage = -1;\r
730         }\r
731         PML_CopyToXMS(PM_GetPageAddress(pagenum),page->xmsPage,page->length);\r
732 }\r
733 \r
734 //\r
735 //      PML_TransferPageSpace() - A page is being replaced, so give the new page\r
736 //              the old one's address space. Returns the address of the new page.\r
737 //\r
738 memptr\r
739 PML_TransferPageSpace(int orig,int new)\r
740 {\r
741         memptr                  addr;\r
742         PageListStruct  far *origpage,far *newpage;\r
743 \r
744         if (orig == new)\r
745                 Quit("PML_TransferPageSpace: Identity replacement");\r
746 \r
747         origpage = &PMPages[orig];\r
748         newpage = &PMPages[new];\r
749 \r
750         if (origpage->locked != pml_Unlocked)\r
751                 Quit("PML_TransferPageSpace: Killing locked page");\r
752 \r
753         if ((origpage->emsPage == -1) && (origpage->mainPage == -1))\r
754                 Quit("PML_TransferPageSpace: Reusing non-existent page");\r
755 \r
756         // Copy page that's about to be purged into XMS\r
757         PML_PutPageInXMS(orig);\r
758 \r
759         // Get the address, and force EMS into a physical page if necessary\r
760         addr = PM_GetPageAddress(orig);\r
761 \r
762         // Steal the address\r
763         newpage->emsPage = origpage->emsPage;\r
764         newpage->mainPage = origpage->mainPage;\r
765 \r
766         // Mark replaced page as purged\r
767         origpage->mainPage = origpage->emsPage = -1;\r
768 \r
769         if (!addr)\r
770                 Quit("PML_TransferPageSpace: Zero replacement");\r
771 \r
772         return(addr);\r
773 }\r
774 \r
775 //\r
776 //      PML_GetAPageBuffer() - A page buffer is needed. Either get it from the\r
777 //              main/EMS free pool, or use PML_GiveLRUPage() to find which page to\r
778 //              steal the buffer from. Returns a far pointer to the page buffer, and\r
779 //              sets the fields inside the given page structure appropriately.\r
780 //              If mainonly is true, free EMS will be ignored, and only main pages\r
781 //              will be looked at by PML_GiveLRUPage().\r
782 //\r
783 byte far *\r
784 PML_GetAPageBuffer(int pagenum,boolean mainonly)\r
785 {\r
786         byte                    far *addr = nil;\r
787         int                             i,n;\r
788         PMBlockAttr             *used;\r
789         PageListStruct  far *page;\r
790 \r
791         page = &PMPages[pagenum];\r
792         if ((EMSPagesUsed < EMSPagesAvail) && !mainonly)\r
793         {\r
794                 // There's remaining EMS - use it\r
795                 page->emsPage = EMSPagesUsed++;\r
796                 addr = PML_GetEMSAddress(page->emsPage,page->locked);\r
797         }\r
798         else if (MainPagesUsed < MainPagesAvail)\r
799         {\r
800                 // There's remaining main memory - use it\r
801                 for (i = 0,n = -1,used = MainMemUsed;i < PMMaxMainMem;i++,used++)\r
802                 {\r
803                         if ((*used & pmba_Allocated) && !(*used & pmba_Used))\r
804                         {\r
805                                 n = i;\r
806                                 *used |= pmba_Used;\r
807                                 break;\r
808                         }\r
809                 }\r
810                 if (n == -1)\r
811                         Quit("PML_GetPageBuffer: MainPagesAvail lied");\r
812                 addr = MainMemPages[n];\r
813                 if (!addr)\r
814                         Quit("PML_GetPageBuffer: Purged main block");\r
815                 page->mainPage = n;\r
816                 MainPagesUsed++;\r
817         }\r
818         else\r
819                 addr = PML_TransferPageSpace(PML_GiveLRUPage(mainonly),pagenum);\r
820 \r
821         if (!addr)\r
822                 Quit("PML_GetPageBuffer: Search failed");\r
823         return(addr);\r
824 }\r
825 \r
826 //\r
827 //      PML_GetPageFromXMS() - If page is in XMS, find LRU main/EMS page and\r
828 //              replace it with the page from XMS. If mainonly is true, will only\r
829 //              search for LRU main page.\r
830 //      XMSProtectPage is set to the page to be retrieved from XMS, so that if\r
831 //              the page from which we're stealing the main/EMS from isn't in XMS,\r
832 //              it won't copy over the page that we're trying to get from XMS.\r
833 //              (pages that are being purged are copied into XMS, if possible)\r
834 //\r
835 memptr\r
836 PML_GetPageFromXMS(int pagenum,boolean mainonly)\r
837 {\r
838         byte                    far *checkaddr;\r
839         memptr                  addr = nil;\r
840         PageListStruct  far *page;\r
841 \r
842         page = &PMPages[pagenum];\r
843         if (XMSPresent && (page->xmsPage != -1))\r
844         {\r
845                 XMSProtectPage = pagenum;\r
846                 checkaddr = PML_GetAPageBuffer(pagenum,mainonly);\r
847                 if (FP_OFF(checkaddr))\r
848                         Quit("PML_GetPageFromXMS: Non segment pointer");\r
849                 addr = (memptr)FP_SEG(checkaddr);\r
850                 PML_CopyFromXMS(addr,page->xmsPage,page->length);\r
851                 XMSProtectPage = -1;\r
852         }\r
853 \r
854         return(addr);\r
855 }\r
856 \r
857 //\r
858 //      PML_LoadPage() - A page is not in main/EMS memory, and it's not in XMS.\r
859 //              Load it into either main or EMS. If mainonly is true, the page will\r
860 //              only be loaded into main.\r
861 //\r
862 void\r
863 PML_LoadPage(int pagenum,boolean mainonly)\r
864 {\r
865         byte                    far *addr;\r
866         PageListStruct  far *page;\r
867 \r
868         addr = PML_GetAPageBuffer(pagenum,mainonly);\r
869         page = &PMPages[pagenum];\r
870         PML_ReadFromFile(addr,page->offset,page->length);\r
871 }\r
872 \r
873 //\r
874 //      PM_GetPage() - Returns the address of the page, loading it if necessary\r
875 //              First, check if in Main Memory or EMS\r
876 //              Then, check XMS\r
877 //              If not in XMS, load into Main Memory or EMS\r
878 //\r
879 #pragma warn -pia\r
880 memptr\r
881 PM_GetPage(int pagenum)\r
882 {\r
883         memptr  result;\r
884 \r
885         if (pagenum >= ChunksInFile)\r
886                 Quit("PM_GetPage: Invalid page request");\r
887 \r
888 #if 0   // for debugging\r
889 asm     mov     dx,STATUS_REGISTER_1\r
890 asm     in      al,dx\r
891 asm     mov     dx,ATR_INDEX\r
892 asm     mov     al,ATR_OVERSCAN\r
893 asm     out     dx,al\r
894 asm     mov     al,10   // bright green\r
895 asm     out     dx,al\r
896 #endif\r
897 \r
898         if (!(result = PM_GetPageAddress(pagenum)))\r
899         {\r
900                 boolean mainonly = (pagenum >= PMSoundStart);\r
901 if (!PMPages[pagenum].offset)   // JDC: sparse page\r
902         Quit ("Tried to load a sparse page!");\r
903                 if (!(result = PML_GetPageFromXMS(pagenum,mainonly)))\r
904                 {\r
905                         if (PMPages[pagenum].lastHit == PMFrameCount)\r
906                                 PMThrashing++;\r
907 \r
908                         PML_LoadPage(pagenum,mainonly);\r
909                         result = PM_GetPageAddress(pagenum);\r
910                 }\r
911         }\r
912         PMPages[pagenum].lastHit = PMFrameCount;\r
913 \r
914 #if 0   // for debugging\r
915 asm     mov     dx,STATUS_REGISTER_1\r
916 asm     in      al,dx\r
917 asm     mov     dx,ATR_INDEX\r
918 asm     mov     al,ATR_OVERSCAN\r
919 asm     out     dx,al\r
920 asm     mov     al,3    // blue\r
921 asm     out     dx,al\r
922 asm     mov     al,0x20 // normal\r
923 asm     out     dx,al\r
924 #endif\r
925 \r
926         return(result);\r
927 }\r
928 #pragma warn +pia\r
929 \r
930 //\r
931 //      PM_SetPageLock() - Sets the lock type on a given page\r
932 //              pml_Unlocked: Normal, page can be purged\r
933 //              pml_Locked: Cannot be purged\r
934 //              pml_EMS?: Same as pml_Locked, but if in EMS, use the physical page\r
935 //                                      specified when returning the address. For sound stuff.\r
936 //\r
937 void\r
938 PM_SetPageLock(int pagenum,PMLockType lock)\r
939 {\r
940         if (pagenum < PMSoundStart)\r
941                 Quit("PM_SetPageLock: Locking/unlocking non-sound page");\r
942 \r
943         PMPages[pagenum].locked = lock;\r
944 }\r
945 \r
946 //\r
947 //      PM_Preload() - Loads as many pages as possible into all types of memory.\r
948 //              Calls the update function after each load, indicating the current\r
949 //              page, and the total pages that need to be loaded (for thermometer).\r
950 //\r
951 void\r
952 PM_Preload(boolean (*update)(word current,word total))\r
953 {\r
954         int                             i,j,\r
955                                         page,oogypage;\r
956         word                    current,total,\r
957                                         totalnonxms,totalxms,\r
958                                         mainfree,maintotal,\r
959                                         emsfree,emstotal,\r
960                                         xmsfree,xmstotal;\r
961         memptr                  addr;\r
962         PageListStruct  far *p;\r
963 \r
964         mainfree = (MainPagesAvail - MainPagesUsed) + (EMSPagesAvail - EMSPagesUsed);\r
965         xmsfree = (XMSPagesAvail - XMSPagesUsed);\r
966 \r
967         xmstotal = maintotal = 0;\r
968 \r
969         for (i = 0;i < ChunksInFile;i++)\r
970         {\r
971                 if (!PMPages[i].offset)\r
972                         continue;                       // sparse\r
973 \r
974                 if ( PMPages[i].emsPage != -1 || PMPages[i].mainPage != -1 )\r
975                         continue;                       // already in main mem\r
976 \r
977                 if ( mainfree )\r
978                 {\r
979                         maintotal++;\r
980                         mainfree--;\r
981                 }\r
982                 else if ( xmsfree && (PMPages[i].xmsPage == -1) )\r
983                 {\r
984                         xmstotal++;\r
985                         xmsfree--;\r
986                 }\r
987         }\r
988 \r
989 \r
990         total = maintotal + xmstotal;\r
991 \r
992         if (!total)\r
993                 return;\r
994 \r
995         page = 0;\r
996         current = 0;\r
997 \r
998 //\r
999 // cache main/ems blocks\r
1000 //\r
1001         while (maintotal)\r
1002         {\r
1003                 while ( !PMPages[page].offset || PMPages[page].mainPage != -1\r
1004                         ||      PMPages[page].emsPage != -1 )\r
1005                         page++;\r
1006 \r
1007                 if (page >= ChunksInFile)\r
1008                         Quit ("PM_Preload: Pages>=ChunksInFile");\r
1009 \r
1010                 PM_GetPage(page);\r
1011 \r
1012                 page++;\r
1013                 current++;\r
1014                 maintotal--;\r
1015                 update(current,total);\r
1016         }\r
1017 \r
1018 //\r
1019 // load stuff to XMS\r
1020 //\r
1021         if (xmstotal)\r
1022         {\r
1023                 for (oogypage = 0 ; PMPages[oogypage].mainPage == -1 ; oogypage++)\r
1024                 ;\r
1025                 addr = PM_GetPage(oogypage);\r
1026                 if (!addr)\r
1027                         Quit("PM_Preload: XMS buffer failed");\r
1028 \r
1029                 while (xmstotal)\r
1030                 {\r
1031                         while ( !PMPages[page].offset || PMPages[page].xmsPage != -1 )\r
1032                                 page++;\r
1033 \r
1034                         if (page >= ChunksInFile)\r
1035                                 Quit ("PM_Preload: Pages>=ChunksInFile");\r
1036 \r
1037                         p = &PMPages[page];\r
1038 \r
1039                         p->xmsPage = XMSPagesUsed++;\r
1040                         if (XMSPagesUsed > XMSPagesAvail)\r
1041                                 Quit("PM_Preload: Exceeded XMS pages");\r
1042                         if (p->length > PMPageSize)\r
1043                                 Quit("PM_Preload: Page too long");\r
1044 \r
1045                         PML_ReadFromFile((byte far *)addr,p->offset,p->length);\r
1046                         PML_CopyToXMS((byte far *)addr,p->xmsPage,p->length);\r
1047 \r
1048                         page++;\r
1049                         current++;\r
1050                         xmstotal--;\r
1051                         update(current,total);\r
1052                 }\r
1053 \r
1054                 p = &PMPages[oogypage];\r
1055                 PML_ReadFromFile((byte far *)addr,p->offset,p->length);\r
1056         }\r
1057 \r
1058         update(total,total);\r
1059 }\r
1060 \r
1061 /////////////////////////////////////////////////////////////////////////////\r
1062 //\r
1063 //      General code\r
1064 //\r
1065 /////////////////////////////////////////////////////////////////////////////\r
1066 \r
1067 //\r
1068 //      PM_NextFrame() - Increments the frame counter and adjusts the thrash\r
1069 //              avoidence variables\r
1070 //\r
1071 //              If currently in panic mode (to avoid thrashing), check to see if the\r
1072 //                      appropriate number of frames have passed since the last time that\r
1073 //                      we would have thrashed. If so, take us out of panic mode.\r
1074 //\r
1075 //\r
1076 void\r
1077 PM_NextFrame(void)\r
1078 {\r
1079         int     i;\r
1080 \r
1081         // Frame count overrun - kill the LRU hit entries & reset frame count\r
1082         if (++PMFrameCount >= MAXLONG - 4)\r
1083         {\r
1084                 for (i = 0;i < PMNumBlocks;i++)\r
1085                         PMPages[i].lastHit = 0;\r
1086                 PMFrameCount = 0;\r
1087         }\r
1088 \r
1089 #if 0\r
1090         for (i = 0;i < PMSoundStart;i++)\r
1091         {\r
1092                 if (PMPages[i].locked)\r
1093                 {\r
1094                         char buf[40];\r
1095                         sprintf(buf,"PM_NextFrame: Page %d is locked",i);\r
1096                         Quit(buf);\r
1097                 }\r
1098         }\r
1099 #endif\r
1100 \r
1101         if (PMPanicMode)\r
1102         {\r
1103                 // DEBUG - set border color\r
1104                 if ((!PMThrashing) && (!--PMPanicMode))\r
1105                 {\r
1106                         // DEBUG - reset border color\r
1107                 }\r
1108         }\r
1109         if (PMThrashing >= PMThrashThreshold)\r
1110                 PMPanicMode = PMUnThrashThreshold;\r
1111         PMThrashing = false;\r
1112 }\r
1113 \r
1114 //\r
1115 //      PM_Reset() - Sets up caching structures\r
1116 //\r
1117 void\r
1118 PM_Reset(void)\r
1119 {\r
1120         int                             i;\r
1121         PageListStruct  far *page;\r
1122 \r
1123         XMSPagesAvail = XMSAvail / PMPageSizeKB;\r
1124 \r
1125         EMSPagesAvail = EMSAvail * (EMSPageSizeKB / PMPageSizeKB);\r
1126         EMSPhysicalPage = 0;\r
1127 \r
1128         MainPagesUsed = EMSPagesUsed = XMSPagesUsed = 0;\r
1129 \r
1130         PMPanicMode = false;\r
1131 \r
1132         // Initialize page list\r
1133         for (i = 0,page = PMPages;i < PMNumBlocks;i++,page++)\r
1134         {\r
1135                 page->mainPage = -1;\r
1136                 page->emsPage = -1;\r
1137                 page->xmsPage = -1;\r
1138                 page->locked = false;\r
1139         }\r
1140 }\r
1141 \r
1142 //\r
1143 //      PM_Startup() - Start up the Page Mgr\r
1144 //\r
1145 void\r
1146 PM_Startup(void)\r
1147 {\r
1148         boolean nomain,noems,noxms;\r
1149         int             i;\r
1150 \r
1151         if (PMStarted)\r
1152                 return;\r
1153 \r
1154         nomain = noems = noxms = false;\r
1155         for (i = 1;i < _argc;i++)\r
1156         {\r
1157                 switch (US_CheckParm(_argv[i],ParmStrings))\r
1158                 {\r
1159                 case 0:\r
1160                         nomain = true;\r
1161                         break;\r
1162                 case 1:\r
1163                         noems = true;\r
1164                         break;\r
1165                 case 2:\r
1166                         noxms = true;\r
1167                         break;\r
1168                 }\r
1169         }\r
1170 \r
1171         PML_OpenPageFile();\r
1172 \r
1173         if (!noems)\r
1174                 PML_StartupEMS();\r
1175         if (!noxms)\r
1176                 PML_StartupXMS();\r
1177 \r
1178         if (nomain && !EMSPresent)
1179         {\r
1180                 printf("PM_Startup: No main or EMS\n");
1181                 return;
1182         }\r
1183         else\r
1184                 PML_StartupMainMem();\r
1185 \r
1186         PM_Reset();\r
1187 \r
1188         PMStarted = true;\r
1189 }\r
1190 \r
1191 //\r
1192 //      PM_Shutdown() - Shut down the Page Mgr\r
1193 //\r
1194 void\r
1195 PM_Shutdown(void)\r
1196 {\r
1197         PML_ShutdownXMS();\r
1198         PML_ShutdownEMS();\r
1199 \r
1200         if (!PMStarted)\r
1201                 return;\r
1202 \r
1203         PML_ClosePageFile();\r
1204 \r
1205         PML_ShutdownMainMem();\r
1206 }\r