winedos / Fix resize memory block (int21)

Jukka Heinonen jhei at iki.fi
Wed Feb 19 13:04:53 CST 2003


This patch should fix bug #1286. This bug was due to
faulty emulation of resize memory block function of
interrupt 0x21. 

That emulation had quite a few bugs: 
it tried to move block if not enough memory was available,
it didn't understand that block must be expanded to maximum
size even if that is less than requested size and it returned
wrong block size if there was not enough memory available.

Changelog:
  Move resize memory block to winedos and make it resize in place
  and work correctly even when trying to allocate too much memory.




Index: dlls/winedos/int21.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/int21.c,v
retrieving revision 1.24
diff -u -r1.24 int21.c
--- dlls/winedos/int21.c	11 Feb 2003 22:22:50 -0000	1.24
+++ dlls/winedos/int21.c	19 Feb 2003 18:41:58 -0000
@@ -1365,7 +1365,33 @@
         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 */




Index: dlls/kernel/kernel32.spec
===================================================================
RCS file: /home/wine/wine/dlls/kernel/kernel32.spec,v
retrieving revision 1.90
diff -u -r1.90 kernel32.spec
--- dlls/kernel/kernel32.spec	13 Jan 2003 20:44:14 -0000	1.90
+++ dlls/kernel/kernel32.spec	19 Feb 2003 18:42:00 -0000
@@ -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




Index: msdos/int21.c
===================================================================
RCS file: /home/wine/wine/msdos/int21.c,v
retrieving revision 1.87
diff -u -r1.87 int21.c
--- msdos/int21.c	11 Feb 2003 22:22:50 -0000	1.87
+++ msdos/int21.c	19 Feb 2003 18:42:10 -0000
@@ -1253,23 +1253,6 @@
         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));




Index: msdos/dosmem.c
===================================================================
RCS file: /home/wine/wine/msdos/dosmem.c,v
retrieving revision 1.48
diff -u -r1.48 dosmem.c
--- msdos/dosmem.c	12 Dec 2002 02:16:02 -0000	1.48
+++ msdos/dosmem.c	19 Feb 2003 18:42:12 -0000
@@ -581,89 +581,79 @@
 
 /***********************************************************************
  *           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)
+   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));
+       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( pseg ) *pseg = ((char*)ptr - DOSMEM_dosmem) >> 4;
+   /*
+    * 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;
 
-       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),
-				   (size<orgsize) ? size : orgsize);
-		     /* free old block */
-		     info_block->blocks--;
-		     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;
-		 }
-	     }
-       }
+   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;
    }
-   return (LPVOID)block;
-}
 
+   /*
+    * Adjust available memory if block size changes.
+    */
+   info_block->free += orgsize - dm->size;
+
+   return size;
+}
 
 /***********************************************************************
  *           DOSMEM_Available




Index: memory/global.c
===================================================================
RCS file: /home/wine/wine/memory/global.c,v
retrieving revision 1.76
diff -u -r1.76 global.c
--- memory/global.c	16 Jan 2003 00:21:04 -0000	1.76
+++ memory/global.c	19 Feb 2003 18:42:28 -0000
@@ -308,7 +308,10 @@
         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 @@
     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 @@
     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;
     }




Index: include/miscemu.h
===================================================================
RCS file: /home/wine/wine/include/miscemu.h,v
retrieving revision 1.64
diff -u -r1.64 miscemu.h
--- include/miscemu.h	10 Dec 2002 22:56:44 -0000	1.64
+++ include/miscemu.h	19 Feb 2003 18:42:33 -0000
@@ -169,7 +169,7 @@
 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 */



-- 
Jukka Heinonen <http://www.iki.fi/jhei/>



More information about the wine-patches mailing list