mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-10-14 02:17:36 +08:00
If a DOS program shrinks it's own PSP segment down, corrupts the MCB block following it, and them does anything that causes allocation, follow apparent MS-DOS behavior by writing a new free MCB block following the PSP segment instead of crashing the emulation with an MCB chain corruption error.
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
// uncomment for alloc/free debug messages
|
||||
#define DEBUG_ALLOC
|
||||
|
||||
uint16_t CONV_MAX_SEG = 0xA000;
|
||||
Bitu UMB_START_SEG = 0x9FFF;
|
||||
/* FIXME: This should be a variable that reflects the last RAM segment.
|
||||
* That means 0x9FFF if 640KB or more, or a lesser value if less than 640KB */
|
||||
@@ -38,7 +39,7 @@ uint16_t first_umb_size = 0x2000;
|
||||
|
||||
static uint16_t memAllocStrategy = 0x00;
|
||||
|
||||
static void DOS_Mem_E_Exit(const char *msg) {
|
||||
static void DOS_Mem_MCBdump(void) {
|
||||
uint16_t mcb_segment=dos.firstMCB;
|
||||
DOS_MCB mcb(mcb_segment);
|
||||
DOS_MCB mcb_next(0);
|
||||
@@ -65,6 +66,10 @@ static void DOS_Mem_E_Exit(const char *msg) {
|
||||
LOG_MSG("FINAL: Type=0x%02x(%c) Seg=0x%04x size=0x%04x name='%s'\n",
|
||||
mcb.GetType(),c,mcb_segment+1,mcb.GetSize(),name);
|
||||
LOG_MSG("End dump\n");
|
||||
}
|
||||
|
||||
static void DOS_Mem_E_Exit(const char *msg) {
|
||||
DOS_Mem_MCBdump();
|
||||
|
||||
#if C_DEBUG
|
||||
LOG_MSG("DOS fatal memory error: %s",msg);
|
||||
@@ -74,7 +79,7 @@ static void DOS_Mem_E_Exit(const char *msg) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void DOS_CompressMemory(uint16_t first_segment=0/*default*/) {
|
||||
void DOS_CompressMemory(uint16_t first_segment=0/*default*/,uint32_t healfrom=0xFFFFu) {
|
||||
uint16_t mcb_segment=dos.firstMCB;
|
||||
DOS_MCB mcb(mcb_segment);
|
||||
DOS_MCB mcb_next(0);
|
||||
@@ -82,8 +87,26 @@ void DOS_CompressMemory(uint16_t first_segment=0/*default*/) {
|
||||
|
||||
while (mcb.GetType()!='Z') {
|
||||
if(counter++ > 10000000) DOS_Mem_E_Exit("DOS_CompressMemory: DOS MCB list corrupted.");
|
||||
mcb_next.SetPt((uint16_t)(mcb_segment+mcb.GetSize()+1));
|
||||
if (GCC_UNLIKELY((mcb_next.GetType()!=0x4d) && (mcb_next.GetType()!=0x5a))) DOS_Mem_E_Exit("Corrupt MCB chain");
|
||||
const uint16_t nseg = (uint16_t)(mcb_segment+mcb.GetSize()+1);
|
||||
mcb_next.SetPt(nseg);
|
||||
if (GCC_UNLIKELY((mcb_next.GetType()!=0x4d) && (mcb_next.GetType()!=0x5a))) {
|
||||
/* there are some programs that chain load other programs, but when they shrink their MCB down
|
||||
* to their EXE resident size they put the stack or other data in the way of the next MCB free
|
||||
* block header. Real MS-DOS appears in this case to just scan up to the last valid block and
|
||||
* then right after it, write a new free MCB block there. We can't do this for the split memory
|
||||
* layout of the PCjr emulation. */
|
||||
if (nseg >= healfrom && (nseg+1u) < CONV_MAX_SEG && !(machine==MCH_PCJR)) {
|
||||
LOG(LOG_DOSMISC,LOG_ERROR)("Corrupted MCB chain, but within the possible memory region of the DOS application.");
|
||||
DOS_Mem_MCBdump();
|
||||
LOG(LOG_DOSMISC,LOG_ERROR)("Declaring all memory past it as a free block. This is apparently MS-DOS behavior.");
|
||||
mcb_next.SetSize(CONV_MAX_SEG - (nseg + 1u));
|
||||
mcb_next.SetPSPSeg(MCB_FREE);
|
||||
mcb_next.SetType('Z');
|
||||
}
|
||||
else {
|
||||
DOS_Mem_E_Exit("Corrupt MCB chain");
|
||||
}
|
||||
}
|
||||
if (mcb_segment >= first_segment && (mcb.GetPSPSeg()==MCB_FREE) && (mcb_next.GetPSPSeg()==MCB_FREE)) {
|
||||
mcb.SetSize(mcb.GetSize()+mcb_next.GetSize()+1);
|
||||
mcb.SetType(mcb_next.GetType());
|
||||
@@ -184,7 +207,12 @@ uint16_t DOS_GetMaximumFreeSize(uint16_t minBlocks)
|
||||
}
|
||||
|
||||
bool DOS_AllocateMemory(uint16_t * segment,uint16_t * blocks) {
|
||||
DOS_CompressMemory();
|
||||
DOS_MCB psp_mcb(dos.psp()-1);
|
||||
|
||||
DOS_CompressMemory(
|
||||
0/*default scan*/,
|
||||
dos.psp()+psp_mcb.GetSize()/*auto-heal broken MCB chain if it occurs anywhere past the PSP memory block*/);
|
||||
|
||||
uint16_t bigsize=0;
|
||||
uint16_t mem_strat=memAllocStrategy;
|
||||
uint16_t mcb_segment=dos.firstMCB;
|
||||
@@ -197,7 +225,6 @@ bool DOS_AllocateMemory(uint16_t * segment,uint16_t * blocks) {
|
||||
|
||||
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);
|
||||
if (umb_start==UMB_START_SEG && (dos.loaded_codepage == 936 || dos.loaded_codepage == 950 || dos.loaded_codepage == 951)) {
|
||||
@@ -640,6 +667,7 @@ void DOS_SetupMemory(void) {
|
||||
/* map memory as normal, the BIOS initialization is the code responsible
|
||||
* for subtracting 32KB from top of system memory for video memory. */
|
||||
mcb.SetSize(/*normally 0x97FF*/(seg_limit-1) - DOS_MEM_START - mcb_sizes);
|
||||
CONV_MAX_SEG = seg_limit;
|
||||
} else if (machine==MCH_PCJR) {
|
||||
/* If there is more than 128KB of RAM, then the MCB chain must be constructed
|
||||
* to exclude video memory. In that case, the BIOS values in the BDA will report
|
||||
@@ -656,6 +684,7 @@ void DOS_SetupMemory(void) {
|
||||
mcb_devicedummy.SetPSPSeg(MCB_FREE);
|
||||
mcb_devicedummy.SetSize(/*0x9FFF*/(seg_limit-1) - 0x2000);
|
||||
mcb_devicedummy.SetType(0x5a);
|
||||
CONV_MAX_SEG = seg_limit;
|
||||
|
||||
/* exclude PCJr graphics region */
|
||||
mcb_devicedummy.SetPt((uint16_t)0x17ff);
|
||||
@@ -670,6 +699,7 @@ void DOS_SetupMemory(void) {
|
||||
else {
|
||||
/* Normal MCB chain, nothing special */
|
||||
mcb.SetSize(/*normally 0x97FF*/(seg_limit-1) - DOS_MEM_START - mcb_sizes);
|
||||
CONV_MAX_SEG = seg_limit;
|
||||
}
|
||||
} else {
|
||||
#ifndef DEBUG_ALLOC
|
||||
@@ -684,6 +714,7 @@ void DOS_SetupMemory(void) {
|
||||
/* complete memory up to 640k available */
|
||||
/* last paragraph used to add UMB chain to low-memory MCB chain */
|
||||
mcb.SetSize(/*0x9FFE*/(seg_limit-2) - DOS_MEM_START - mcb_sizes);
|
||||
CONV_MAX_SEG = seg_limit-1;
|
||||
}
|
||||
|
||||
dos.firstMCB=DOS_MEM_START;
|
||||
|
Reference in New Issue
Block a user