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