/* * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dosbox.h" #include "mem.h" #include "dos_inc.h" #define UMB_START_SEG 0x9fff static Bit16u memAllocStrategy = 0x00; static void DOS_CompressMemory(void) { Bit16u mcb_segment=dos.firstMCB; DOS_MCB mcb(mcb_segment); DOS_MCB mcb_next(0); while (mcb.GetType()!=0x5a) { mcb_next.SetPt((Bit16u)(mcb_segment+mcb.GetSize()+1)); if (GCC_UNLIKELY((mcb_next.GetType()!=0x4d) && (mcb_next.GetType()!=0x5a))) E_Exit("Corrupt MCB chain"); if ((mcb.GetPSPSeg()==MCB_FREE) && (mcb_next.GetPSPSeg()==MCB_FREE)) { mcb.SetSize(mcb.GetSize()+mcb_next.GetSize()+1); mcb.SetType(mcb_next.GetType()); } else { mcb_segment+=mcb.GetSize()+1; mcb.SetPt(mcb_segment); } } } void DOS_FreeProcessMemory(Bit16u pspseg) { Bit16u mcb_segment=dos.firstMCB; DOS_MCB mcb(mcb_segment); for (;;) { if (mcb.GetPSPSeg()==pspseg) { mcb.SetPSPSeg(MCB_FREE); } if (mcb.GetType()==0x5a) break; if (GCC_UNLIKELY(mcb.GetType()!=0x4d)) E_Exit("Corrupt MCB chain"); mcb_segment+=mcb.GetSize()+1; mcb.SetPt(mcb_segment); } Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); if (umb_start==UMB_START_SEG) { DOS_MCB umb_mcb(umb_start); for (;;) { if (umb_mcb.GetPSPSeg()==pspseg) { umb_mcb.SetPSPSeg(MCB_FREE); } if (umb_mcb.GetType()!=0x4d) break; umb_start+=umb_mcb.GetSize()+1; umb_mcb.SetPt(umb_start); } } else if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); DOS_CompressMemory(); } Bit16u DOS_GetMemAllocStrategy() { return memAllocStrategy; } bool DOS_SetMemAllocStrategy(Bit16u strat) { if ((strat&0x3f)<3) { memAllocStrategy = strat; return true; } /* otherwise an invalid allocation strategy was specified */ return false; } bool DOS_AllocateMemory(Bit16u * segment,Bit16u * blocks) { DOS_CompressMemory(); Bit16u bigsize=0; Bit16u mem_strat=memAllocStrategy; Bit16u mcb_segment=dos.firstMCB; Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); if (umb_start==UMB_START_SEG) { /* start with UMBs if requested (bits 7 or 6 set) */ if (mem_strat&0xc0) mcb_segment=umb_start; } else if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); DOS_MCB mcb(0); DOS_MCB mcb_next(0); DOS_MCB psp_mcb(dos.psp()-1); char psp_name[9]; psp_mcb.GetFileName(psp_name); Bit16u found_seg=0,found_seg_size=0; for (;;) { mcb.SetPt(mcb_segment); if (mcb.GetPSPSeg()==MCB_FREE) { /* Check for enough free memory in current block */ Bit16u block_size=mcb.GetSize(); if (block_size<(*blocks)) { if (bigsizetotal, in the second case resize block to maximum */ if ((mcb_next.GetPSPSeg()==MCB_FREE) && (mcb.GetType()!=0x5a)) { /* adjust type of joined MCB */ mcb.SetType(mcb_next.GetType()); } mcb.SetSize(total); mcb.SetPSPSeg(dos.psp()); if (*blocks==total) return true; /* block fit exactly */ *blocks=total; /* return maximum */ DOS_SetError(DOSERR_INSUFFICIENT_MEMORY); return false; } bool DOS_FreeMemory(Bit16u segment) { //TODO Check if allowed to free this segment if (segment < DOS_MEM_START+1) { LOG(LOG_DOSMISC,LOG_ERROR)("Program tried to free %X ---ERROR",segment); DOS_SetError(DOSERR_MB_ADDRESS_INVALID); return false; } DOS_MCB mcb(segment-1); if ((mcb.GetType()!=0x4d) && (mcb.GetType()!=0x5a)) { DOS_SetError(DOSERR_MB_ADDRESS_INVALID); return false; } mcb.SetPSPSeg(MCB_FREE); // DOS_CompressMemory(); return true; } void DOS_BuildUMBChain(bool umb_active,bool ems_active) { if (umb_active && (!IS_TANDY_ARCH)) { Bit16u first_umb_seg = 0xd000; Bit16u first_umb_size = 0x2000; if(ems_active) first_umb_size = 0x1000; dos_infoblock.SetStartOfUMBChain(UMB_START_SEG); dos_infoblock.SetUMBChainState(0); // UMBs not linked yet DOS_MCB umb_mcb(first_umb_seg); umb_mcb.SetPSPSeg(0); // currently free umb_mcb.SetSize(first_umb_size-1); umb_mcb.SetType(0x5a); /* Scan MCB-chain for last block */ Bit16u mcb_segment=dos.firstMCB; DOS_MCB mcb(mcb_segment); while (mcb.GetType()!=0x5a) { mcb_segment+=mcb.GetSize()+1; mcb.SetPt(mcb_segment); } /* A system MCB has to cover the space between the regular MCB-chain and the UMBs */ Bit16u cover_mcb=(Bit16u)(mcb_segment+mcb.GetSize()+1); mcb.SetPt(cover_mcb); mcb.SetType(0x4d); mcb.SetPSPSeg(0x0008); mcb.SetSize(first_umb_seg-cover_mcb-1); mcb.SetFileName("SC "); } else { dos_infoblock.SetStartOfUMBChain(0xffff); dos_infoblock.SetUMBChainState(0); } } bool DOS_LinkUMBsToMemChain(Bit16u linkstate) { /* Get start of UMB-chain */ Bit16u umb_start=dos_infoblock.GetStartOfUMBChain(); if (umb_start!=UMB_START_SEG) { if (umb_start!=0xffff) LOG(LOG_DOSMISC,LOG_ERROR)("Corrupt UMB chain: %x",umb_start); return false; } if ((linkstate&1)==(dos_infoblock.GetUMBChainState()&1)) return true; /* Scan MCB-chain for last block before UMB-chain */ Bit16u mcb_segment=dos.firstMCB; Bit16u prev_mcb_segment=dos.firstMCB; DOS_MCB mcb(mcb_segment); while ((mcb_segment!=umb_start) && (mcb.GetType()!=0x5a)) { prev_mcb_segment=mcb_segment; mcb_segment+=mcb.GetSize()+1; mcb.SetPt(mcb_segment); } DOS_MCB prev_mcb(prev_mcb_segment); switch (linkstate) { case 0x0000: // unlink if ((prev_mcb.GetType()==0x4d) && (mcb_segment==umb_start)) { prev_mcb.SetType(0x5a); } dos_infoblock.SetUMBChainState(0); break; case 0x0001: // link if (mcb.GetType()==0x5a) { mcb.SetType(0x4d); dos_infoblock.SetUMBChainState(1); } break; default: LOG_MSG("Invalid link state %x when reconfiguring MCB chain",linkstate); return false; } return true; } void DOS_SetupMemory(void) { /* Let dos claim a few bios interrupts. Makes DOSBox more compatible with * buggy games, which compare against the interrupt table. (probably a * broken linked list implementation) */ Bit16u ihseg = 0x70; Bit16u ihofs = 0xF4; real_writeb(ihseg,ihofs,(Bit8u)0xCF); //An IRET Instruction RealSetVec(0x01,RealMake(ihseg,ihofs)); //BioMenace (offset!=4) RealSetVec(0x02,RealMake(ihseg,ihofs)); //BioMenace (segment<0x8000) RealSetVec(0x03,RealMake(ihseg,ihofs)); //Alien Incident (offset!=0) RealSetVec(0x04,RealMake(ihseg,ihofs)); //Shadow President (lower byte of segment!=0) RealSetVec(0x0f,RealMake(ihseg,ihofs)); //Always a tricky one (soundblaster irq) // Create a dummy device MCB with PSPSeg=0x0008 DOS_MCB mcb_devicedummy((Bit16u)DOS_MEM_START); mcb_devicedummy.SetPSPSeg(MCB_DOS); // Devices mcb_devicedummy.SetSize(1); mcb_devicedummy.SetType(0x4d); // More blocks will follow // mcb_devicedummy.SetFileName("SD "); Bit16u mcb_sizes=2; // Create a small empty MCB (result from a growing environment block) DOS_MCB tempmcb((Bit16u)DOS_MEM_START+mcb_sizes); tempmcb.SetPSPSeg(MCB_FREE); tempmcb.SetSize(4); mcb_sizes+=5; tempmcb.SetType(0x4d); // Lock the previous empty MCB DOS_MCB tempmcb2((Bit16u)DOS_MEM_START+mcb_sizes); tempmcb2.SetPSPSeg(0x40); // can be removed by loadfix tempmcb2.SetSize(16); mcb_sizes+=17; tempmcb2.SetType(0x4d); DOS_MCB mcb((Bit16u)DOS_MEM_START+mcb_sizes); mcb.SetPSPSeg(MCB_FREE); //Free mcb.SetType(0x5a); //Last Block if (machine==MCH_TANDY) { /* memory up to 608k available, the rest (to 640k) is used by the tandy graphics system's variable mapping of 0xb800 */ mcb.SetSize(0x9BFF - DOS_MEM_START - mcb_sizes); } else if (machine==MCH_PCJR) { /* memory from 128k to 640k is available */ mcb_devicedummy.SetPt((Bit16u)0x2000); mcb_devicedummy.SetPSPSeg(MCB_FREE); mcb_devicedummy.SetSize(0x9FFF - 0x2000); mcb_devicedummy.SetType(0x5a); /* exclude PCJr graphics region */ mcb_devicedummy.SetPt((Bit16u)0x17ff); mcb_devicedummy.SetPSPSeg(MCB_DOS); mcb_devicedummy.SetSize(0x800); mcb_devicedummy.SetType(0x4d); /* memory below 96k */ mcb.SetSize(0x1800 - DOS_MEM_START - (2+mcb_sizes)); mcb.SetType(0x4d); } else { /* complete memory up to 640k available */ /* last paragraph used to add UMB chain to low-memory MCB chain */ mcb.SetSize(0x9FFE - DOS_MEM_START - mcb_sizes); } dos.firstMCB=DOS_MEM_START; dos_infoblock.SetFirstMCB(DOS_MEM_START); }