DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_memory.cpp
00001 /*
00002  *  Copyright (C) 2002-2019  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
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, 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 }