DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 #include "dosbox.h" 00021 #include "mem.h" 00022 #include "bios.h" 00023 #include "dos_inc.h" 00024 00025 // uncomment for alloc/free debug messages 00026 #define DEBUG_ALLOC 00027 00028 Bitu UMB_START_SEG = 0x9FFF; 00029 /* FIXME: This should be a variable that reflects the last RAM segment. 00030 * That means 0x9FFF if 640KB or more, or a lesser value if less than 640KB */ 00031 //#define UMB_START_SEG 0x9fff 00032 00033 Bit16u first_umb_seg = 0xd000; 00034 Bit16u first_umb_size = 0x2000; 00035 00036 static Bit16u memAllocStrategy = 0x00; 00037 00038 static void DOS_Mem_E_Exit(const char *msg) { 00039 Bit16u mcb_segment=dos.firstMCB; 00040 DOS_MCB mcb(mcb_segment); 00041 DOS_MCB mcb_next(0); 00042 Bitu counter=0; 00043 char name[10]; 00044 char c; 00045 00046 LOG_MSG("DOS MCB dump:\n"); 00047 while ((c=(char)mcb.GetType()) != 'Z') { 00048 if (counter++ > 10000) break; 00049 if (c != 'M') break; 00050 00051 mcb.GetFileName(name); 00052 LOG_MSG(" Type=0x%02x(%c) Seg=0x%04x size=0x%04x name='%s'\n", 00053 mcb.GetType(),c, 00054 mcb_segment+1,mcb.GetSize(),name); 00055 mcb_next.SetPt((Bit16u)(mcb_segment+mcb.GetSize()+1)); 00056 mcb_segment+=mcb.GetSize()+1; 00057 mcb.SetPt(mcb_segment); 00058 } 00059 00060 mcb.GetFileName(name); 00061 c = (char)mcb.GetType(); if (c < 32) c = '.'; 00062 LOG_MSG("FINAL: Type=0x%02x(%c) Seg=0x%04x size=0x%04x name='%s'\n", 00063 mcb.GetType(),c,mcb_segment+1,mcb.GetSize(),name); 00064 LOG_MSG("End dump\n"); 00065 00066 #if C_DEBUG 00067 LOG_MSG("DOS fatal memory error: %s",msg); 00068 throw int(7); // DOS non-fatal error (restart when debugger runs again) 00069 #else 00070 E_Exit("%s",msg); 00071 #endif 00072 } 00073 00074 void DOS_CompressMemory(Bit16u first_segment=0/*default*/) { 00075 Bit16u mcb_segment=dos.firstMCB; 00076 DOS_MCB mcb(mcb_segment); 00077 DOS_MCB mcb_next(0); 00078 Bitu counter=0; 00079 00080 while (mcb.GetType()!='Z') { 00081 if(counter++ > 10000000) DOS_Mem_E_Exit("DOS_CompressMemory: DOS MCB list corrupted."); 00082 mcb_next.SetPt((Bit16u)(mcb_segment+mcb.GetSize()+1)); 00083 if (GCC_UNLIKELY((mcb_next.GetType()!=0x4d) && (mcb_next.GetType()!=0x5a))) DOS_Mem_E_Exit("Corrupt MCB chain"); 00084 if (mcb_segment >= first_segment && (mcb.GetPSPSeg()==MCB_FREE) && (mcb_next.GetPSPSeg()==MCB_FREE)) { 00085 mcb.SetSize(mcb.GetSize()+mcb_next.GetSize()+1); 00086 mcb.SetType(mcb_next.GetType()); 00087 } else { 00088 mcb_segment+=mcb.GetSize()+1; 00089 mcb.SetPt(mcb_segment); 00090 } 00091 } 00092 } 00093 00094 void DOS_FreeProcessMemory(Bit16u pspseg) { 00095 Bit16u mcb_segment=dos.firstMCB; 00096 DOS_MCB mcb(mcb_segment); 00097 Bitu counter = 0; 00098 00099 for (;;) { 00100 if(counter++ > 10000000) DOS_Mem_E_Exit("DOS_FreeProcessMemory: DOS MCB list corrupted."); 00101 if (mcb.GetPSPSeg()==pspseg) { 00102 mcb.SetPSPSeg(MCB_FREE); 00103 } 00104 if (mcb.GetType()==0x5a) break; 00105 if (GCC_UNLIKELY(mcb.GetType()!=0x4d)) DOS_Mem_E_Exit("Corrupt MCB chain"); 00106 mcb_segment+=mcb.GetSize()+1; 00107 mcb.SetPt(mcb_segment); 00108 } 00109 00110 Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); 00111 if (umb_start==UMB_START_SEG) { 00112 DOS_MCB umb_mcb(umb_start); 00113 for (;;) { 00114 if (umb_mcb.GetPSPSeg()==pspseg) { 00115 umb_mcb.SetPSPSeg(MCB_FREE); 00116 } 00117 if (umb_mcb.GetType()!=0x4d) break; 00118 umb_start+=umb_mcb.GetSize()+1; 00119 umb_mcb.SetPt(umb_start); 00120 } 00121 } else if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); 00122 00123 DOS_CompressMemory(); 00124 } 00125 00126 Bit16u DOS_GetMemAllocStrategy() { 00127 return memAllocStrategy; 00128 } 00129 00130 bool DOS_SetMemAllocStrategy(Bit16u strat) { 00131 if ((strat&0x3f)<3) { 00132 memAllocStrategy = strat; 00133 return true; 00134 } 00135 /* otherwise an invalid allocation strategy was specified */ 00136 return false; 00137 } 00138 00139 extern bool dbg_zero_on_dos_allocmem; 00140 00141 void DOS_zeromem(uint16_t seg,uint16_t para) { 00142 uint32_t ofs,cnt; 00143 00144 if (para == 0) return; 00145 00146 ofs = ((uint32_t)seg << 4); 00147 cnt = ((uint32_t)para << 4); 00148 if ((ofs+cnt) > 0x100000) E_Exit("DOS_zeromem out of range"); 00149 while (cnt != 0) { 00150 mem_writeb(ofs++,0); 00151 cnt--; 00152 } 00153 } 00154 00155 bool DOS_AllocateMemory(Bit16u * segment,Bit16u * blocks) { 00156 DOS_CompressMemory(); 00157 Bit16u bigsize=0; 00158 Bit16u mem_strat=memAllocStrategy; 00159 Bit16u mcb_segment=dos.firstMCB; 00160 00161 Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); 00162 if (umb_start==UMB_START_SEG) { 00163 /* start with UMBs if requested (bits 7 or 6 set) */ 00164 if (mem_strat&0xc0) mcb_segment=umb_start; 00165 } else if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); 00166 00167 DOS_MCB mcb(0); 00168 DOS_MCB mcb_next(0); 00169 DOS_MCB psp_mcb(dos.psp()-1); 00170 char psp_name[9]; 00171 psp_mcb.GetFileName(psp_name); 00172 Bit16u found_seg=0,found_seg_size=0; 00173 for (;;) { 00174 mcb.SetPt(mcb_segment); 00175 if (mcb.GetPSPSeg()==MCB_FREE) { 00176 /* Check for enough free memory in current block */ 00177 Bit16u block_size=mcb.GetSize(); 00178 if (block_size<(*blocks)) { 00179 if (bigsize<block_size) { 00180 /* current block is largest block that was found, 00181 but still not as big as requested */ 00182 bigsize=block_size; 00183 } 00184 } else if ((block_size==*blocks) && ((mem_strat & 0x3f)<2)) { 00185 /* MCB fits precisely, use it if search strategy is firstfit or bestfit */ 00186 mcb.SetPSPSeg(dos.psp()); 00187 *segment=mcb_segment+1; 00188 00189 if (dbg_zero_on_dos_allocmem) DOS_zeromem(*segment,*blocks); 00190 #ifdef DEBUG_ALLOC 00191 LOG(LOG_MISC,LOG_DEBUG)("DOS_AllocateMemory(blocks=0x%04x) = 0x%04x-0x%04x",*blocks,*segment,*segment+*blocks-1); 00192 #endif 00193 return true; 00194 } else { 00195 switch (mem_strat & 0x3f) { 00196 case 0: /* firstfit */ 00197 mcb_next.SetPt((Bit16u)(mcb_segment+*blocks+1)); 00198 mcb_next.SetPSPSeg(MCB_FREE); 00199 mcb_next.SetType(mcb.GetType()); 00200 mcb_next.SetSize(block_size-*blocks-1); 00201 mcb.SetSize(*blocks); 00202 mcb.SetType(0x4d); 00203 mcb.SetPSPSeg(dos.psp()); 00204 mcb.SetFileName(psp_name); 00205 //TODO Filename 00206 *segment=mcb_segment+1; 00207 00208 if (dbg_zero_on_dos_allocmem) DOS_zeromem(*segment,*blocks); 00209 #ifdef DEBUG_ALLOC 00210 LOG(LOG_MISC,LOG_DEBUG)("DOS_AllocateMemory(blocks=0x%04x) = 0x%04x-0x%04x",*blocks,*segment,*segment+*blocks-1); 00211 #endif 00212 return true; 00213 case 1: /* bestfit */ 00214 if ((found_seg_size==0) || (block_size<found_seg_size)) { 00215 /* first fitting MCB, or smaller than the last that was found */ 00216 found_seg=mcb_segment; 00217 found_seg_size=block_size; 00218 } 00219 break; 00220 default: /* everything else is handled as lastfit by dos */ 00221 /* MCB is large enough, note it down */ 00222 found_seg=mcb_segment; 00223 found_seg_size=block_size; 00224 break; 00225 } 00226 } 00227 } 00228 /* Onward to the next MCB if there is one */ 00229 if (mcb.GetType()==0x5a) { 00230 if ((mem_strat&0x80) && (umb_start==UMB_START_SEG)) { 00231 /* bit 7 set: try high memory first, then low */ 00232 mcb_segment=dos.firstMCB; 00233 mem_strat&=(~0xc0); 00234 } else { 00235 /* finished searching all requested MCB chains */ 00236 if (found_seg) { 00237 /* a matching MCB was found (cannot occur for firstfit) */ 00238 if ((mem_strat & 0x3f)==0x01) { 00239 /* bestfit, allocate block at the beginning of the MCB */ 00240 mcb.SetPt(found_seg); 00241 00242 mcb_next.SetPt((Bit16u)(found_seg+*blocks+1)); 00243 mcb_next.SetPSPSeg(MCB_FREE); 00244 mcb_next.SetType(mcb.GetType()); 00245 mcb_next.SetSize(found_seg_size-*blocks-1); 00246 00247 mcb.SetSize(*blocks); 00248 mcb.SetType(0x4d); 00249 mcb.SetPSPSeg(dos.psp()); 00250 mcb.SetFileName(psp_name); 00251 //TODO Filename 00252 *segment=found_seg+1; 00253 } else { 00254 /* lastfit, allocate block at the end of the MCB */ 00255 mcb.SetPt(found_seg); 00256 if (found_seg_size==*blocks) { 00257 /* use the whole block */ 00258 mcb.SetPSPSeg(dos.psp()); 00259 //Not consistent with line 124. But how many application will use this information ? 00260 mcb.SetFileName(psp_name); 00261 *segment = found_seg+1; 00262 00263 if (dbg_zero_on_dos_allocmem) DOS_zeromem(*segment,*blocks); 00264 #ifdef DEBUG_ALLOC 00265 LOG(LOG_MISC,LOG_DEBUG)("DOS_AllocateMemory(blocks=0x%04x) = 0x%04x-0x%04x",*blocks,*segment,*segment+*blocks-1); 00266 #endif 00267 return true; 00268 } 00269 *segment = found_seg+1+found_seg_size - *blocks; 00270 mcb_next.SetPt((Bit16u)(*segment-1)); 00271 mcb_next.SetSize(*blocks); 00272 mcb_next.SetType(mcb.GetType()); 00273 mcb_next.SetPSPSeg(dos.psp()); 00274 mcb_next.SetFileName(psp_name); 00275 // Old Block 00276 mcb.SetSize(found_seg_size-*blocks-1); 00277 mcb.SetPSPSeg(MCB_FREE); 00278 mcb.SetType(0x4D); 00279 } 00280 00281 if (dbg_zero_on_dos_allocmem) DOS_zeromem(*segment,*blocks); 00282 #ifdef DEBUG_ALLOC 00283 LOG(LOG_MISC,LOG_DEBUG)("DOS_AllocateMemory(blocks=0x%04x) = 0x%04x-0x%04x",*blocks,*segment,*segment+*blocks-1); 00284 #endif 00285 return true; 00286 } 00287 /* no fitting MCB found, return size of largest block */ 00288 *blocks=bigsize; 00289 DOS_SetError(DOSERR_INSUFFICIENT_MEMORY); 00290 return false; 00291 } 00292 } else mcb_segment+=mcb.GetSize()+1; 00293 } 00294 00295 #ifdef DEBUG_ALLOC 00296 LOG(LOG_MISC,LOG_DEBUG)("DOS_AllocateMemory(blocks=0x%04x) = 0x%04x-0x%04x",*blocks,*segment,*segment+*blocks-1); 00297 #endif 00298 return false; 00299 } 00300 00301 00302 bool DOS_ResizeMemory(Bit16u segment,Bit16u * blocks) { 00303 if (segment < DOS_MEM_START+1) { 00304 LOG(LOG_DOSMISC,LOG_ERROR)("Program resizes %X, take care",segment); 00305 } 00306 00307 #ifdef DEBUG_ALLOC 00308 LOG(LOG_MISC,LOG_DEBUG)("DOS_ResizeMemory(seg=0x%04x) blocks=0x%04x",segment,*blocks); 00309 #endif 00310 00311 DOS_MCB mcb(segment-1); 00312 if ((mcb.GetType()!=0x4d) && (mcb.GetType()!=0x5a)) { 00313 DOS_SetError(DOSERR_MCB_DESTROYED); 00314 return false; 00315 } 00316 00317 Bit16u total=mcb.GetSize(); 00318 DOS_MCB mcb_next(segment+total); 00319 00320 if (*blocks > total) 00321 DOS_CompressMemory(segment-1); 00322 else 00323 DOS_CompressMemory(); 00324 00325 if (*blocks<=total) { 00326 if (GCC_UNLIKELY(*blocks==total)) { 00327 /* Nothing to do */ 00328 return true; 00329 } 00330 /* Shrinking MCB */ 00331 DOS_MCB mcb_new_next(segment+(*blocks)); 00332 mcb.SetSize(*blocks); 00333 mcb_new_next.SetType(mcb.GetType()); 00334 if (mcb.GetType()==0x5a) { 00335 /* Further blocks follow */ 00336 mcb.SetType(0x4d); 00337 } 00338 00339 mcb_new_next.SetSize(total-*blocks-1); 00340 mcb_new_next.SetPSPSeg(MCB_FREE); 00341 mcb.SetPSPSeg(dos.psp()); 00342 DOS_CompressMemory(); 00343 return true; 00344 } 00345 /* MCB will grow, try to join with following MCB */ 00346 if (mcb.GetType()!=0x5a) { 00347 if (mcb_next.GetPSPSeg()==MCB_FREE) { 00348 total+=mcb_next.GetSize()+1; 00349 } 00350 } 00351 if (*blocks<total) { 00352 if (mcb.GetType()!=0x5a) { 00353 /* save type of following MCB */ 00354 mcb.SetType(mcb_next.GetType()); 00355 } 00356 mcb.SetSize(*blocks); 00357 mcb_next.SetPt((Bit16u)(segment+*blocks)); 00358 mcb_next.SetSize(total-*blocks-1); 00359 mcb_next.SetType(mcb.GetType()); 00360 mcb_next.SetPSPSeg(MCB_FREE); 00361 mcb.SetType(0x4d); 00362 mcb.SetPSPSeg(dos.psp()); 00363 return true; 00364 } 00365 00366 /* at this point: *blocks==total (fits) or *blocks>total, 00367 in the second case resize block to maximum */ 00368 00369 if ((mcb_next.GetPSPSeg()==MCB_FREE) && (mcb.GetType()!=0x5a)) { 00370 /* adjust type of joined MCB */ 00371 mcb.SetType(mcb_next.GetType()); 00372 } 00373 mcb.SetSize(total); 00374 mcb.SetPSPSeg(dos.psp()); 00375 if (*blocks==total) return true; /* block fit exactly */ 00376 00377 *blocks=total; /* return maximum */ 00378 DOS_SetError(DOSERR_INSUFFICIENT_MEMORY); 00379 return false; 00380 } 00381 00382 00383 bool DOS_FreeMemory(Bit16u segment) { 00384 //TODO Check if allowed to free this segment 00385 if (segment < DOS_MEM_START+1) { 00386 LOG(LOG_DOSMISC,LOG_ERROR)("Program tried to free %X ---ERROR",segment); 00387 DOS_SetError(DOSERR_MB_ADDRESS_INVALID); 00388 return false; 00389 } 00390 00391 DOS_MCB mcb(segment-1); 00392 if ((mcb.GetType()!=0x4d) && (mcb.GetType()!=0x5a)) { 00393 DOS_SetError(DOSERR_MB_ADDRESS_INVALID); 00394 return false; 00395 } 00396 00397 #ifdef DEBUG_ALLOC 00398 LOG(LOG_MISC,LOG_DEBUG)("DOS_FreeMemory(seg=0x%04x)",segment); 00399 #endif 00400 00401 mcb.SetPSPSeg(MCB_FREE); 00402 // DOS_CompressMemory(); 00403 return true; 00404 } 00405 00406 Bitu GetEMSPageFrameSegment(void); 00407 00408 void DOS_BuildUMBChain(bool umb_active,bool /*ems_active*/) { 00409 unsigned int seg_limit = (unsigned int)(MEM_TotalPages()*256); 00410 00411 /* UMBs are only possible if the machine has 1MB+64KB of RAM */ 00412 if (umb_active && (machine!=MCH_TANDY) && seg_limit >= (0x10000+0x1000-1) && first_umb_seg < GetEMSPageFrameSegment()) { 00413 /* XMS emulation sets UMB size now. 00414 * PCjr mode disables UMB emulation */ 00415 #if 0 00416 if (ems_active) { 00417 /* we can use UMBs up to the EMS page frame */ 00418 /* FIXME: when we make the EMS page frame configurable this will need to be updated */ 00419 first_umb_size = GetEMSPageFrameSegment() - first_umb_seg; 00420 } 00421 else if (machine == MCH_PCJR) { 00422 /* we can use UMBs up to where PCjr wants cartridge ROM */ 00423 first_umb_size = 0xE000 - first_umb_seg; 00424 } 00425 #endif 00426 00427 dos_infoblock.SetStartOfUMBChain((Bit16u)UMB_START_SEG); 00428 dos_infoblock.SetUMBChainState(0); // UMBs not linked yet 00429 00430 DOS_MCB umb_mcb(first_umb_seg); 00431 umb_mcb.SetPSPSeg(0); // currently free 00432 umb_mcb.SetSize(first_umb_size-1); 00433 umb_mcb.SetType(0x5a); 00434 00435 /* Scan MCB-chain for last block */ 00436 Bit16u mcb_segment=dos.firstMCB; 00437 DOS_MCB mcb(mcb_segment); 00438 while (mcb.GetType()!=0x5a) { 00439 mcb_segment+=mcb.GetSize()+1; 00440 mcb.SetPt(mcb_segment); 00441 } 00442 00443 /* A system MCB has to cover the space between the 00444 regular MCB-chain and the UMBs */ 00445 Bit16u cover_mcb=(Bit16u)(mcb_segment+mcb.GetSize()+1); 00446 mcb.SetPt(cover_mcb); 00447 mcb.SetType(0x4d); 00448 mcb.SetPSPSeg(0x0008); 00449 mcb.SetSize(first_umb_seg-cover_mcb-1); 00450 mcb.SetFileName("SC "); 00451 00452 } else { 00453 dos_infoblock.SetStartOfUMBChain(0xffff); 00454 dos_infoblock.SetUMBChainState(0); 00455 } 00456 } 00457 00458 bool DOS_LinkUMBsToMemChain(Bit16u linkstate) { 00459 /* Get start of UMB-chain */ 00460 Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); 00461 if (umb_start!=UMB_START_SEG) { 00462 if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); 00463 return false; 00464 } 00465 00466 if ((linkstate&1)==(dos_infoblock.GetUMBChainState()&1)) return true; 00467 00468 /* Scan MCB-chain for last block before UMB-chain */ 00469 Bit16u mcb_segment=dos.firstMCB; 00470 Bit16u prev_mcb_segment=dos.firstMCB; 00471 DOS_MCB mcb(mcb_segment); 00472 while ((mcb_segment!=umb_start) && (mcb.GetType()!=0x5a)) { 00473 prev_mcb_segment=mcb_segment; 00474 mcb_segment+=mcb.GetSize()+1; 00475 mcb.SetPt(mcb_segment); 00476 } 00477 DOS_MCB prev_mcb(prev_mcb_segment); 00478 00479 switch (linkstate) { 00480 case 0x0000: // unlink 00481 if ((prev_mcb.GetType()==0x4d) && (mcb_segment==umb_start)) { 00482 prev_mcb.SetType(0x5a); 00483 } 00484 dos_infoblock.SetUMBChainState(0); 00485 break; 00486 case 0x0001: // link 00487 if (mcb.GetType()==0x5a) { 00488 if ((mcb_segment+mcb.GetSize()+1) != umb_start) { 00489 LOG_MSG("MCB chain no longer goes to end of memory (corruption?), not linking in UMB!"); 00490 return false; 00491 } 00492 mcb.SetType(0x4d); 00493 dos_infoblock.SetUMBChainState(1); 00494 } 00495 break; 00496 default: 00497 /* NTS: Some programs apparently call this function incorrectly. 00498 * The CauseWay extender in Open Watcom 1.9's installer for example 00499 * calls this function with AX=0x5803 BX=0x58 */ 00500 return false; 00501 } 00502 00503 return true; 00504 } 00505 00506 00507 #include <assert.h> 00508 00509 extern Bit16u DOS_IHSEG; 00510 00511 extern bool enable_dummy_device_mcb; 00512 00513 void DOS_SetupMemory(void) { 00514 unsigned int max_conv; 00515 unsigned int seg_limit; 00516 00517 max_conv = (unsigned int)mem_readw(BIOS_MEMORY_SIZE) << (10u - 4u); 00518 seg_limit = (unsigned int)(MEM_TotalPages()*256); 00519 if (seg_limit > max_conv) seg_limit = max_conv; 00520 UMB_START_SEG = max_conv - 1; 00521 00522 /* Let dos claim a few bios interrupts. Makes DOSBox more compatible with 00523 * buggy games, which compare against the interrupt table. (probably a 00524 * broken linked list implementation) */ 00525 Bit16u ihseg; 00526 Bit16u ihofs; 00527 00528 assert(DOS_IHSEG != 0); 00529 ihseg = DOS_IHSEG; 00530 ihofs = 0xF4; 00531 00532 real_writeb(ihseg,ihofs,(Bit8u)0xCF); //An IRET Instruction 00533 RealSetVec(0x01,RealMake(ihseg,ihofs)); //BioMenace (offset!=4) 00534 if (machine != MCH_PCJR) RealSetVec(0x02,RealMake(ihseg,ihofs)); //BioMenace (segment<0x8000). Else, taken by BIOS NMI interrupt 00535 RealSetVec(0x03,RealMake(ihseg,ihofs)); //Alien Incident (offset!=0) 00536 RealSetVec(0x04,RealMake(ihseg,ihofs)); //Shadow President (lower byte of segment!=0) 00537 RealSetVec(0x0f,RealMake(ihseg,ihofs)); //Always a tricky one (soundblaster irq) 00538 00539 Bit16u mcb_sizes=0; 00540 00541 if (enable_dummy_device_mcb) { 00542 // Create a dummy device MCB with PSPSeg=0x0008 00543 LOG_MSG("Dummy device MCB at segment 0x%x",DOS_MEM_START+mcb_sizes); 00544 DOS_MCB mcb_devicedummy((Bit16u)DOS_MEM_START+mcb_sizes); 00545 mcb_devicedummy.SetPSPSeg(MCB_DOS); // Devices 00546 mcb_devicedummy.SetSize(16); 00547 mcb_devicedummy.SetType(0x4d); // More blocks will follow 00548 mcb_sizes+=1+16; 00549 00550 // We DO need to mark this area as 'SD' but leaving it blank so far 00551 // confuses MEM.EXE (shows ???????) which suggests other software 00552 // might have a problem with it as well. 00553 // mcb_devicedummy.SetFileName("SD "); 00554 } 00555 00556 DOS_MCB mcb((Bit16u)DOS_MEM_START+mcb_sizes); 00557 mcb.SetPSPSeg(MCB_FREE); //Free 00558 mcb.SetType(0x5a); //Last Block 00559 if (machine==MCH_TANDY) { 00560 if (seg_limit < ((384*1024)/16)) 00561 E_Exit("Tandy requires at least 384K"); 00562 /* memory up to 608k available, the rest (to 640k) is used by 00563 the tandy graphics system's variable mapping of 0xb800 */ 00564 /* 00565 mcb.SetSize(0x9BFF - DOS_MEM_START - mcb_sizes); 00566 */ mcb.SetSize(/*0x9BFF*/(seg_limit-0x801) - DOS_MEM_START - mcb_sizes); 00567 } else if (machine==MCH_PCJR) { 00568 DOS_MCB mcb_devicedummy((Bit16u)0x2000); 00569 00570 /* FIXME: The PCjr can have built-in either 64KB or 128KB of RAM. 00571 * RAM beyond 128KB is made possible with expansion sidecars. 00572 * DOSBox-X needs to support memsizekb=64 or memsizekb=128, 00573 * and adjust video ram location appropriately. */ 00574 00575 if (seg_limit < ((256*1024)/16)) 00576 E_Exit("PCjr requires at least 256K"); 00577 /* memory from 128k to 640k is available */ 00578 mcb_devicedummy.SetPt((Bit16u)0x2000); 00579 mcb_devicedummy.SetPSPSeg(MCB_FREE); 00580 mcb_devicedummy.SetSize(/*0x9FFF*/(seg_limit-1) - 0x2000); 00581 mcb_devicedummy.SetType(0x5a); 00582 00583 /* exclude PCJr graphics region */ 00584 mcb_devicedummy.SetPt((Bit16u)0x17ff); 00585 mcb_devicedummy.SetPSPSeg(MCB_DOS); 00586 mcb_devicedummy.SetSize(0x800); 00587 mcb_devicedummy.SetType(0x4d); 00588 00589 /* memory below 96k */ 00590 mcb.SetSize(0x1800 - DOS_MEM_START - (2+mcb_sizes)); 00591 mcb.SetType(0x4d); 00592 } else { 00593 #ifndef DEBUG_ALLOC 00594 /* NTS: Testing suggests we can push as low as 4KB. However, Wikipedia and 00595 * other sites suggest that the IBM PC only went as low as 16KB when 00596 * it first sold, and even that wasn't too typical. But what the hell, 00597 * we'll allow as little as 4KB if not for fun DOS hacking. */ 00598 if (seg_limit < ((4*1024)/16)) 00599 E_Exit("Standard PC requires at least 4K"); 00600 #endif 00601 00602 /* complete memory up to 640k available */ 00603 /* last paragraph used to add UMB chain to low-memory MCB chain */ 00604 mcb.SetSize(/*0x9FFE*/(seg_limit-2) - DOS_MEM_START - mcb_sizes); 00605 } 00606 00607 dos.firstMCB=DOS_MEM_START; 00608 dos_infoblock.SetFirstMCB(DOS_MEM_START); 00609 } 00610 00611 // save state support 00612 void POD_Save_DOS_Memory( std::ostream& stream ) 00613 { 00614 // - pure data 00615 WRITE_POD( &memAllocStrategy, memAllocStrategy ); 00616 } 00617 00618 void POD_Load_DOS_Memory( std::istream& stream ) 00619 { 00620 // - pure data 00621 READ_POD( &memAllocStrategy, memAllocStrategy ); 00622 }