diff --git a/dlls/kernel/kernel32.spec b/dlls/kernel/kernel32.spec index 48afb0d5707..468be203fd6 100644 --- a/dlls/kernel/kernel32.spec +++ b/dlls/kernel/kernel32.spec @@ -1142,6 +1142,7 @@ @ cdecl DOSMEM_FreeBlock(ptr) DOSMEM_FreeBlock @ cdecl DOSMEM_GetBlock(long ptr) DOSMEM_GetBlock @ cdecl DOSMEM_Init(long) DOSMEM_Init +@ cdecl DOSMEM_ResizeBlock(ptr long long) DOSMEM_ResizeBlock @ cdecl DRIVE_OpenDevice(long long) DRIVE_OpenDevice @ stdcall INT_Int21Handler(ptr) INT_Int21Handler @ cdecl LOCAL_Alloc(long long long) LOCAL_Alloc diff --git a/dlls/winedos/int21.c b/dlls/winedos/int21.c index 7aedb46e0e8..1d17478ed00 100644 --- a/dlls/winedos/int21.c +++ b/dlls/winedos/int21.c @@ -1365,7 +1365,33 @@ void WINAPI DOSVM_Int21Handler( CONTEXT86 *context ) break; case 0x4a: /* RESIZE MEMORY BLOCK */ - INT_Int21Handler( context ); + TRACE( "RESIZE MEMORY segment %04lX to %d paragraphs\n", + context->SegEs, BX_reg(context) ); + { + DWORD newsize = (DWORD)BX_reg(context) << 4; + + if (!ISV86(context) && DOSVM_IsWin16()) + { + FIXME( "Resize memory block - unsupported under Win16\n" ); + } + else + { + LPVOID address = (void*)((DWORD)context->SegEs << 4); + UINT blocksize = DOSMEM_ResizeBlock( address, newsize, FALSE ); + + if (blocksize == (UINT)-1) + { + SET_CFLAG( context ); + SET_AX( context, 0x0009 ); /* illegal address */ + } + else if(blocksize != newsize) + { + SET_CFLAG( context ); + SET_AX( context, 0x0008 ); /* insufficient memory */ + SET_BX( context, blocksize >> 4 ); /* new block size */ + } + } + } break; case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */ diff --git a/include/miscemu.h b/include/miscemu.h index ce3abc370c9..a671b8b444b 100644 --- a/include/miscemu.h +++ b/include/miscemu.h @@ -169,7 +169,7 @@ extern void DOSMEM_Tick(WORD timer); extern WORD DOSMEM_AllocSelector(WORD); extern LPVOID DOSMEM_GetBlock(UINT size, WORD* p); extern BOOL DOSMEM_FreeBlock(void* ptr); -extern LPVOID DOSMEM_ResizeBlock(void* ptr, UINT size, WORD* p); +extern UINT DOSMEM_ResizeBlock(void* ptr, UINT size, BOOL exact); extern UINT DOSMEM_Available(void); extern LPVOID DOSMEM_MapRealToLinear(DWORD); /* real-mode to linear */ extern LPVOID DOSMEM_MapDosToLinear(UINT); /* linear DOS to Wine */ diff --git a/memory/global.c b/memory/global.c index a471e60addf..afcd5b77bce 100644 --- a/memory/global.c +++ b/memory/global.c @@ -308,7 +308,10 @@ HGLOBAL16 WINAPI GlobalReAlloc16( if (!(pArena->flags & GA_MOVEABLE) || !(pArena->flags & GA_DISCARDABLE) || (pArena->lockCount > 0) || (pArena->pageLockCount > 0)) return 0; - HeapFree( GetProcessHeap(), 0, (void *)pArena->base ); + if (pArena->flags & GA_DOSMEM) + DOSMEM_FreeBlock( (void *)pArena->base ); + else + HeapFree( GetProcessHeap(), 0, (void *)pArena->base ); pArena->base = 0; /* Note: we rely on the fact that SELECTOR_ReallocBlock won't @@ -343,18 +346,43 @@ HGLOBAL16 WINAPI GlobalReAlloc16( if (ptr && (size == oldsize)) return handle; /* Nothing to do */ if (pArena->flags & GA_DOSMEM) - newptr = DOSMEM_ResizeBlock(ptr, size, NULL); + { + if (DOSMEM_ResizeBlock(ptr, size, TRUE) == size) + newptr = ptr; + else if(pArena->pageLockCount > 0) + newptr = 0; + else + { + newptr = DOSMEM_GetBlock( size, 0 ); + if (newptr) + { + memcpy( newptr, ptr, oldsize ); + DOSMEM_FreeBlock( ptr ); + } + } + } else - /* if more then one reader (e.g. some pointer has been given out by GetVDMPointer32W16), - only try to realloc in place */ + { + /* + * if more then one reader (e.g. some pointer has been + * given out by GetVDMPointer32W16), + * only try to realloc in place + */ newptr = HeapReAlloc( GetProcessHeap(), - (pArena->pageLockCount > 0)?HEAP_REALLOC_IN_PLACE_ONLY:0, ptr, size ); + (pArena->pageLockCount > 0) ? + HEAP_REALLOC_IN_PLACE_ONLY : 0, + ptr, size ); + } + if (!newptr) { FIXME("Realloc failed lock %d\n",pArena->pageLockCount); if (pArena->pageLockCount <1) { - HeapFree( GetProcessHeap(), 0, ptr ); + if (pArena->flags & GA_DOSMEM) + DOSMEM_FreeBlock( (void *)pArena->base ); + else + HeapFree( GetProcessHeap(), 0, ptr ); SELECTOR_FreeBlock( sel ); memset( pArena, 0, sizeof(GLOBALARENA) ); } @@ -367,15 +395,21 @@ HGLOBAL16 WINAPI GlobalReAlloc16( sel = SELECTOR_ReallocBlock( sel, ptr, size ); if (!sel) { - HeapFree( GetProcessHeap(), 0, ptr ); + if (pArena->flags & GA_DOSMEM) + DOSMEM_FreeBlock( (void *)pArena->base ); + else + HeapFree( GetProcessHeap(), 0, ptr ); memset( pArena, 0, sizeof(GLOBALARENA) ); return 0; } selcount = (size + 0xffff) / 0x10000; if (!(pNewArena = GLOBAL_GetArena( sel, selcount ))) - { - HeapFree( GetProcessHeap(), 0, ptr ); + { + if (pArena->flags & GA_DOSMEM) + DOSMEM_FreeBlock( (void *)pArena->base ); + else + HeapFree( GetProcessHeap(), 0, ptr ); SELECTOR_FreeBlock( sel ); return 0; } diff --git a/msdos/dosmem.c b/msdos/dosmem.c index 67b2de1f852..aeaacbb4603 100644 --- a/msdos/dosmem.c +++ b/msdos/dosmem.c @@ -581,89 +581,79 @@ BOOL DOSMEM_FreeBlock(void* ptr) /*********************************************************************** * DOSMEM_ResizeBlock + * + * Resize DOS memory block in place. Returns block size or -1 on error. + * + * If exact is TRUE, returned value is either old or requested block + * size. If exact is FALSE, block is expanded even if there is not + * enough space for full requested block size. */ -LPVOID DOSMEM_ResizeBlock(void* ptr, UINT size, UINT16* pseg) +UINT DOSMEM_ResizeBlock(void *ptr, UINT size, BOOL exact) { char *block = NULL; dosmem_info *info_block = DOSMEM_InfoBlock(); + dosmem_entry *dm; + dosmem_entry *next; + UINT blocksize; + UINT orgsize; - if( ptr >= (void*)(((char*)DOSMEM_RootBlock()) + sizeof(dosmem_entry)) && - ptr < (void*)DOSMEM_MemoryTop() && !((((char*)ptr) - - DOSMEM_dosmem) & 0xf) ) + if( ptr < (void*)(((char*)DOSMEM_RootBlock()) + sizeof(dosmem_entry)) || + (ptr >= (void*)DOSMEM_MemoryTop() && + !((((char*)ptr) - DOSMEM_dosmem) & 0xf))) + return (UINT)-1; + + dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry)); + if( dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL) ) + return (UINT)-1; + + next = NEXT_BLOCK(dm); + orgsize = dm->size & DM_BLOCK_MASK; + + /* collapse free blocks */ + while( next->size & DM_BLOCK_FREE ) { - dosmem_entry *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry)); - - if( pseg ) *pseg = ((char*)ptr - DOSMEM_dosmem) >> 4; - - if( !(dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL)) - ) - { - dosmem_entry *next = NEXT_BLOCK(dm); - UINT blocksize, orgsize = dm->size & DM_BLOCK_MASK; - - while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */ - { - dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK); - next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL); - next = NEXT_BLOCK(dm); - } - - blocksize = dm->size & DM_BLOCK_MASK; - if (blocksize >= size) - { - block = ((char*)dm) + sizeof(dosmem_entry); - if( blocksize - size > 0x20 ) - { - /* split dm so that the next one stays - * paragraph-aligned (and next gains free bit) */ - - dm->size = (((size + 0xf + sizeof(dosmem_entry)) & ~0xf) - - sizeof(dosmem_entry)); - next = (dosmem_entry*)(((char*)dm) + - sizeof(dosmem_entry) + dm->size); - next->size = (blocksize - (dm->size + - sizeof(dosmem_entry))) | DM_BLOCK_FREE - ; - } else dm->size &= DM_BLOCK_MASK; - - info_block->free += orgsize - dm->size; - } else { - /* the collapse didn't help, try getting a new block */ - block = DOSMEM_GetBlock(size, pseg); - if (block) { - /* we got one, copy the old data there (we do need to, right?) */ - memcpy(block, ((char*)dm) + sizeof(dosmem_entry), - (sizeblocks--; - info_block->free += dm->size; - - dm->size |= DM_BLOCK_FREE; - } else { - /* and Bill Gates said 640K should be enough for everyone... */ - - /* need to split original and collapsed blocks apart again, - * and free the collapsed blocks again, before exiting */ - if( blocksize - orgsize > 0x20 ) - { - /* split dm so that the next one stays - * paragraph-aligned (and next gains free bit) */ - - dm->size = (((orgsize + 0xf + sizeof(dosmem_entry)) & ~0xf) - - sizeof(dosmem_entry)); - next = (dosmem_entry*)(((char*)dm) + - sizeof(dosmem_entry) + dm->size); - next->size = (blocksize - (dm->size + - sizeof(dosmem_entry))) | DM_BLOCK_FREE - ; - } else dm->size &= DM_BLOCK_MASK; - } - } - } + dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK); + next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL); + next = NEXT_BLOCK(dm); } - return (LPVOID)block; -} + blocksize = dm->size & DM_BLOCK_MASK; + + /* + * If collapse didn't help we either expand block to maximum + * available size (exact == FALSE) or give collapsed blocks + * back to free storage (exact == TRUE). + */ + if (blocksize < size) + size = exact ? orgsize : blocksize; + + block = ((char*)dm) + sizeof(dosmem_entry); + if( blocksize - size > 0x20 ) + { + /* + * split dm so that the next one stays + * paragraph-aligned (and next gains free bit) + */ + + dm->size = (((size + 0xf + sizeof(dosmem_entry)) & ~0xf) - + sizeof(dosmem_entry)); + next = (dosmem_entry*)(((char*)dm) + + sizeof(dosmem_entry) + dm->size); + next->size = (blocksize - (dm->size + + sizeof(dosmem_entry))) | DM_BLOCK_FREE; + } + else + { + dm->size &= DM_BLOCK_MASK; + } + + /* + * Adjust available memory if block size changes. + */ + info_block->free += orgsize - dm->size; + + return size; +} /*********************************************************************** * DOSMEM_Available diff --git a/msdos/int21.c b/msdos/int21.c index 98d1672ea00..672e0a9af82 100644 --- a/msdos/int21.c +++ b/msdos/int21.c @@ -1253,23 +1253,6 @@ void WINAPI INT_Int21Handler( CONTEXT86 *context ) bSetDOSExtendedError = !INT21_GetCurrentDirectory(context); break; - case 0x4a: /* RESIZE MEMORY BLOCK */ - TRACE("RESIZE MEMORY segment %04lX to %d paragraphs\n", context->SegEs, BX_reg(context)); - if (!ISV86(context)) - FIXME("RESIZE MEMORY probably insufficient implementation. Expect crash soon\n"); - { - LPVOID *mem = DOSMEM_ResizeBlock(DOSMEM_MapDosToLinear(context->SegEs<<4), - BX_reg(context)<<4,NULL); - if (mem) - SET_AX( context, DOSMEM_MapLinearToDos(mem)>>4 ); - else { - SET_CFLAG(context); - SET_AX( context, 0x0008 ); /* insufficient memory */ - SET_BX( context, DOSMEM_Available()>>4 ); /* not quite right */ - } - } - break; - case 0x4e: /* "FINDFIRST" - FIND FIRST MATCHING FILE */ TRACE("FINDFIRST mask 0x%04x spec %s\n",CX_reg(context), (LPCSTR)CTX_SEG_OFF_TO_LIN(context, context->SegDs, context->Edx));