OSDN Git Service

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