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