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