winedos:MCB implementation

Markus Amsler markus.amsler at oribi.org
Fri Sep 10 11:09:26 CDT 2004


That's some of the result of my effort getting FreeCOM running in winedos.
It's quite important to winedos having proper MCB support, a lot of apps 
depend on it.
It also enables running/terminating apps within dos shells, and opens 
the door to TSR.
With some more patches (IO-redirection, TSR comming soon) i'm able to 
run FreeCOM, compile FreeCOM within FreeCOM, run XRAY and getting my TSR 
app starting.

I'm not sure wether this breaks the int31 code, i'm not familiar with 
the dpmi stuff.

Please let me know, if there's something wrong with the patch (design 
problems, naming conventions, formating, ...).

Markus

Changelog:
* implement Memory Control Block
* remove some dosmem seperation hacks
-------------- next part --------------
Index: dlls/winedos/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/winedos/Makefile.in,v
retrieving revision 1.28
diff -u -r1.28 Makefile.in
--- dlls/winedos/Makefile.in	11 Aug 2004 23:59:07 -0000	1.28
+++ dlls/winedos/Makefile.in	10 Sep 2004 14:05:04 -0000
@@ -41,6 +41,7 @@
 	int67.c \
 	interrupts.c \
 	ioports.c \
+	mcb.c \
 	module.c \
 	ppdev.c \
 	relay.c \
Index: dlls/winedos/devices.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/devices.c,v
retrieving revision 1.11
diff -u -r1.11 devices.c
--- dlls/winedos/devices.c	15 Nov 2003 00:13:21 -0000	1.11
+++ dlls/winedos/devices.c	10 Sep 2004 14:05:05 -0000
@@ -475,7 +475,7 @@
   DOS_LOL->sharing_retry_delay	= 1;
   DOS_LOL->ptr_disk_buf		= 0x0;
   DOS_LOL->offs_unread_CON		= 0x0;
-  DOS_LOL->seg_first_MCB		= 0x0;
+  DOS_LOL->seg_first_MCB		= MCB_First();
   DOS_LOL->ptr_first_DPB		= 0x0;
   DOS_LOL->ptr_first_SysFileTable	= 0x0;
   DOS_LOL->ptr_clock_dev_hdr		= 0x0;
Index: dlls/winedos/dosexe.h
===================================================================
RCS file: /home/wine/wine/dlls/winedos/dosexe.h,v
retrieving revision 1.34
diff -u -r1.34 dosexe.h
--- dlls/winedos/dosexe.h	15 Nov 2003 00:13:21 -0000	1.34
+++ dlls/winedos/dosexe.h	10 Sep 2004 14:05:06 -0000
@@ -79,6 +79,18 @@
 typedef void (WINAPI *RMCBPROC)(CONTEXT86*);
 typedef void (WINAPI *INTPROC)(CONTEXT86*);
 
+/* psp and size of MCB needs to be byte aligned */
+typedef WORD WORD_ALIGNED_BYTE DECLSPEC_ALIGN(1);
+
+typedef struct {
+    BYTE type;
+    WORD_ALIGNED_BYTE psp;     /* segment of owner psp */
+    WORD_ALIGNED_BYTE size;    /* in paragraphs */
+    BYTE pad[3];
+    BYTE name[8];
+} MCB;
+
+
 #define DOS_PRIORITY_REALTIME 0  /* IRQ0 */
 #define DOS_PRIORITY_KEYBOARD 1  /* IRQ1 */
 #define DOS_PRIORITY_VGA      2  /* IRQ9 */
@@ -105,6 +117,8 @@
 #define ADD_LOWORD(dw,val)  ((dw) = ((dw) & 0xffff0000) | LOWORD((DWORD)(dw)+(val)))
 
 #define PTR_REAL_TO_LIN(seg,off) ((void*)(((unsigned int)(seg) << 4) + LOWORD(off)))
+#define SEG_TO_LIN(seg) PTR_REAL_TO_LIN((seg),0)
+#define LIN_TO_SEG(addr) ((unsigned int)(addr) >> 4)
 
 /* NOTE: Interrupts might get called from four modes: real mode, 16-bit,
  *       32-bit segmented (DPMI32) and 32-bit linear (via DeviceIoControl).
@@ -356,5 +370,16 @@
 
 /* xms.c */
 extern void WINAPI XMS_Handler(CONTEXT86*);
+
+/* mcb.c */
+extern void MCB_Init( void );
+extern WORD MCB_First( void );
+extern WORD MCB_Available( void );
+extern WORD MCB_Alloc( WORD size );
+extern BOOL MCB_Free( WORD seg );
+extern WORD MCB_Resize( WORD seg, WORD size, BOOL exact, WORD* maxsize );
+extern void MCB_FreePSP( WORD psp );
+extern BOOL MCB_SetPSP( WORD seg, WORD psp );
+
 
 #endif /* __WINE_DOSEXE_H */
Index: dlls/winedos/int21.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/int21.c,v
retrieving revision 1.71
diff -u -r1.71 int21.c
--- dlls/winedos/int21.c	8 Sep 2004 19:05:43 -0000	1.71
+++ dlls/winedos/int21.c	10 Sep 2004 14:05:19 -0000
@@ -4894,7 +4902,7 @@
                 selector = LOWORD( rv );
             }
             else
-                DOSMEM_GetBlock( bytes, &selector );
+                selector = MCB_Alloc (BX_reg (context));
 
             if (selector)
             {
@@ -4905,7 +4913,7 @@
             {
                 SET_CFLAG(context);
                 SET_AX( context, 0x0008 ); /* insufficient memory */
-                SET_BX( context, DOSMEM_Available() >> 4 );
+                SET_BX( context, MCB_Available() );
             }
         }
 	break;
@@ -4924,7 +4932,7 @@
                     context->SegEs = 0;
             }
             else
-                ok = DOSMEM_FreeBlock( (void*)((DWORD)context->SegEs << 4) );
+                ok = MCB_Free( context->SegEs );
 
             if (!ok)
             {
@@ -4939,7 +4947,8 @@
         TRACE( "RESIZE MEMORY segment %04lX to %d paragraphs\n", 
                context->SegEs, BX_reg(context) );
         {
-            DWORD newsize = (DWORD)BX_reg(context) << 4;
+            WORD maxsize;
+            WORD newsize = BX_reg(context);
             
             if (!ISV86(context) && DOSVM_IsWin16())
             {
@@ -4948,8 +4957,7 @@
             }
             else
             {
-                LPVOID address = (void*)((DWORD)context->SegEs << 4);
-                UINT blocksize = DOSMEM_ResizeBlock( address, newsize, FALSE );
+                UINT blocksize = MCB_Resize( context->SegEs, newsize, FALSE, &maxsize );
 
                 RESET_CFLAG(context);
                 if (blocksize == (UINT)-1)
@@ -4961,7 +4969,7 @@
                 {
                     SET_CFLAG( context );
                     SET_AX( context, 0x0008 );    /* insufficient memory */
-                    SET_BX( context, blocksize >> 4 ); /* new block size */
+                    SET_BX( context, blocksize ); /* new block size */
                 }
             }
         }
Index: dlls/winedos/int31.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/int31.c,v
retrieving revision 1.41
diff -u -r1.41 int31.c
--- dlls/winedos/int31.c	25 Nov 2003 00:42:27 -0000	1.41
+++ dlls/winedos/int31.c	10 Sep 2004 14:05:23 -0000
@@ -470,7 +470,8 @@
     if (!already) {
         if (!context->SegSs) {
             alloc = 1; /* allocate default stack */
-            stack16 = addr = DOSMEM_GetBlock( 64, (UINT16 *)&(context->SegSs) );
+	    context->SegSs = MCB_Alloc( 16 );
+            stack16 = addr = (LPVOID) MAKELONG(0, context->SegSs);
             context->Esp = 64-2;
             stack16 += 32-1;
             if (!addr) {
@@ -511,7 +512,7 @@
         DOSVM_Enter( context );
         TRACE("returned from real-mode call\n");
     }
-    if (alloc) DOSMEM_FreeBlock( addr );
+    if (alloc) MCB_Free( context->SegSs );
     return 0;
 }
 
@@ -649,9 +650,10 @@
 
     if (NewRMCB)
     {
-	LPVOID RMCBmem = DOSMEM_GetBlock(4, &uParagraph);
+    	uParagraph = MCB_Alloc(1);
+ 	LPVOID RMCBmem = (LPVOID) (uParagraph << 4);
 	LPBYTE p = RMCBmem;
-
+ 
 	*p++ = 0xcd; /* RMCB: */
 	*p++ = 0x31; /* int $0x31 */
 /* it is the called procedure's task to change the return CS:EIP
@@ -697,7 +699,7 @@
 	PrevRMCB->next = CurrRMCB->next;
 	    else
 	FirstRMCB = CurrRMCB->next;
-	DOSMEM_FreeBlock(PTR_REAL_TO_LIN(SELECTOROF(CurrRMCB->address),OFFSETOF(CurrRMCB->address)));
+	MCB_Free(CurrRMCB->address);
 	HeapFree(GetProcessHeap(), 0, CurrRMCB);
 	return 0;
     }
@@ -1042,7 +1044,7 @@
                 SET_DX( context, LOWORD(dw) );
             } else {
                 SET_AX( context, 0x0008 ); /* insufficient memory */
-                SET_BX( context, DOSMEM_Available() >> 4 );
+                SET_BX( context, MCB_Available() );
                 SET_CFLAG(context);
             }
             break;
Index: dlls/winedos/module.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/module.c,v
retrieving revision 1.44
diff -u -r1.44 module.c
--- dlls/winedos/module.c	13 Aug 2004 00:39:16 -0000	1.44
+++ dlls/winedos/module.c	10 Sep 2004 14:05:25 -0000
@@ -167,7 +167,8 @@
   while (env[sz++]) sz+=strlen(env+sz)+1;
  } else sz++;
  /* allocate it */
- envblk=DOSMEM_GetBlock(sz+sizeof(WORD)+strlen(name)+1,&seg);
+ seg = MCB_Alloc( ((sz+sizeof(WORD)+strlen(name)+1) >> 4) + 10);
+ envblk = (LPSTR) SEG_TO_LIN (seg);
  /* fill it */
  if (env) {
   memcpy(envblk,env,sz);
@@ -194,6 +195,7 @@
     /* initialize the memory */
     TRACE("Initializing DOS memory structures\n");
     DOSMEM_Init(TRUE);
+    MCB_Init();
     DOSDEV_InstallDOSDevices();
 
     return TRUE;
@@ -265,14 +267,18 @@
 
     /* allocate memory for the executable */
     TRACE("Allocating DOS memory (min=%ld, max=%ld)\n",min_size,max_size);
-    avail=DOSMEM_Available();
+    avail=MCB_Available() << 4;
     if (avail<min_size) {
       ERR("insufficient DOS memory\n");
       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
       goto load_error;
     }
     if (avail>max_size) avail=max_size;
-    psp_start=DOSMEM_GetBlock(avail,&DOSVM_psp);
+    DOSVM_psp = MCB_Alloc (avail>>4);
+    /* A PSP is identified, that the MCB has stored itself as the owner process */
+    MCB_SetPSP (DOSVM_psp, DOSVM_psp);
+    MCB_SetPSP (env_seg, DOSVM_psp);
+    psp_start = SEG_TO_LIN (DOSVM_psp);
     if (!psp_start) {
       ERR("error allocating DOS memory\n");
       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -678,9 +684,8 @@
       DOSVM_SetRMHandler(0x24, psp->savedint24);
       /* FIXME: deallocate file handles etc */
       /* free process's associated memory
-       * FIXME: walk memory and deallocate all blocks owned by process */
-      DOSMEM_FreeBlock( PTR_REAL_TO_LIN(psp->environment,0) );
-      DOSMEM_FreeBlock( PTR_REAL_TO_LIN(DOSVM_psp,0) );
+       * walk memory and deallocate all blocks owned by process */
+      MCB_FreePSP( DOSVM_psp );
       /* switch to parent's PSP */
       DOSVM_psp = parpsp;
       psp_start = (LPBYTE)((DWORD)parpsp << 4);
--- /dev/null	2004-07-13 09:22:32.000000000 +0200
+++ dlls/winedos/mcb.c	2004-09-10 15:29:07.000000000 +0200
@@ -0,0 +1,264 @@
+/*
+ * Memory Control Block (MCB) Management.
+ *
+ * Copyright 2004 Markus Amsler
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "dosexe.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(dosmem);
+
+/*
+ * All function parameters and results are in segments and paragraphs
+ *
+ * FIXME: implement Allocation Strategy
+ */
+
+#define MCB_SEG_FIRST 0x0050  /* FIXME: get it from kernel */
+#define MCB_SEG_LAST  0x9FBF
+
+#define MCB_DUMP(mc) TRACE ("MCB_DUMP base=%p type=%02xh psp=%04xh size=%04xh\n", mc, mc->type, mc->psp , mc->size )
+ 
+#define MCB_NEXT(mc) \
+        (MCB*) ((mc->type==MCB_LAST) ? NULL : (char*)(mc) + ((mc->size + 1) << 4) )
+	 
+
+#define MCB_NORMAL      0x4d
+#define MCB_LAST        0x5a
+
+#define PSP_DOS         0x0060  
+#define PSP_FREE        0
+
+        
+/***********************************************************************
+ *           MCB_Collapse
+ *
+ * Helper function for internal use only.
+ * Atach all following free blocks to this one, even if this one is not free.
+ */
+void MCB_Collapse( MCB* mcb )
+{
+    MCB* next = MCB_NEXT( mcb );
+
+    while (next && next->psp == PSP_FREE)
+    {
+        mcb->size = mcb->size + next->size + 1;
+        mcb->type = next->type;    /* make sure keeping MCB_LAST */
+        next = MCB_NEXT( next );
+    }
+}
+
+
+/***********************************************************************
+ *           MCB_Init
+ *
+ * Initializes the first MCB.
+ */
+void MCB_Init( void )
+{
+    MCB *first = SEG_TO_LIN (MCB_First ());
+    first->type = MCB_LAST;
+    first->psp = PSP_FREE;
+    first->size = MCB_SEG_LAST - MCB_SEG_FIRST - 1 ;
+}
+
+
+/***********************************************************************
+ *           MCB_First
+ *
+ * Get the segment of the first MCB in the chain.
+ */
+WORD MCB_First( void )
+{
+    return MCB_SEG_FIRST;
+}
+
+
+/***********************************************************************
+ *           MCB_Available
+ *
+ * Returns size of the largest free block.
+ */
+WORD MCB_Available( void )
+{
+    UINT  available = 0;
+    UINT  total = 0;
+    MCB *curr = (MCB*) SEG_TO_LIN (MCB_First ());
+    /* loop over all MCB and search the largest free MCB */
+    while (curr)
+    {
+        if (curr->type!=MCB_NORMAL && curr->type!=MCB_LAST)
+        {
+            ERR( "MCB List Corrupt\n" );
+            MCB_DUMP( curr );
+            return 0;
+        }
+        
+        if (curr->psp == PSP_FREE &&
+            curr->size > available )
+            available = curr->size;
+        
+        total += curr->size + 1;
+        curr = MCB_NEXT( curr );
+    }
+    TRACE( " %04xh of %04xh paragraphs available\n", available, total );
+    return available;
+}
+
+ 
+/***********************************************************************
+ *           MCB_Alloc
+ *
+ * Allocates memory
+ * Returns segment of allocated memory, or 0 on error.
+ */
+WORD MCB_Alloc( WORD size )
+{
+    MCB *curr = SEG_TO_LIN (MCB_First ());
+    MCB *next = NULL;
+    WORD psp=DOSVM_psp;
+    
+    if (!psp)
+        psp = PSP_DOS;
+    
+    /* loop over all MCB and search the next large enough MCB */
+    TRACE( "size= 0x%04x\n", size );
+    while (curr)
+    {
+        if (curr->psp == PSP_FREE)
+        {
+            MCB_Collapse( curr );            
+            /* is it large enough (one paragaph for the MCB)? */
+            if (curr->size >= size)
+            {
+                if (curr->size > size)
+                {
+                    /* split curr */
+                    next = (MCB *) ((char*) curr + (size+1) * 16);
+                    next->psp = PSP_FREE;
+                    next->size = curr->size - (size+1);
+                    next->type = curr->type;
+                    curr->type = MCB_NORMAL;
+                    curr->size = size;
+                }
+                /* curr is the found block */
+                curr->psp = psp;
+                return LIN_TO_SEG (curr) + 1;
+            }
+        }
+        curr = MCB_NEXT(curr);
+    }
+    return 0;
+}
+
+
+/***********************************************************************
+ *           MCB_Free
+ *
+ * Frees previously allocated memory
+ */
+BOOL MCB_Free( WORD seg )
+{
+    /* FIXME: check if MCB is valid */   
+    MCB* mcb = (MCB*) SEG_TO_LIN (seg-1);
+    TRACE( "seg= 0x%04x\n", seg );
+    
+    mcb->psp = PSP_FREE;
+    MCB_Collapse( mcb );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *           MCB_Resize
+ *
+ * If exact is FALSE, block is expanded even if there is not
+ * enough space for full requested block size.
+ */
+WORD MCB_Resize( WORD seg, WORD size, BOOL exact, WORD* maxsize )
+{
+    /* FIXME: implement exact */   
+    /* FIXME: check if segment valid*/   
+    MCB* mcb = (MCB*) SEG_TO_LIN (seg-1);
+    MCB* next;
+    TRACE( "seg=%04xh size=%04xh\n", seg, size );
+   
+    /* resize needed? */
+    if (mcb->size == size)
+        return size;
+
+    /* collapse free blocks */
+    MCB_Collapse( mcb );    
+    
+    /* shrink mcb ? */
+    if (mcb->size > size)
+    {
+        *maxsize = mcb->size;
+        next = (MCB *) ((char*) mcb + (size+1) * 16);
+        next->type = mcb->type;
+        next->psp = PSP_FREE;
+        next->size = mcb->size - (size+1);
+        mcb->type = MCB_NORMAL;
+        mcb->size = size;
+        return size;
+    }
+    
+    if (!exact)
+    {
+        *maxsize = mcb->size;
+        return mcb->size;
+    }
+    
+    return -1;
+}
+ 
+
+/***********************************************************************
+ *           MCB_SetPSP
+ *
+ * set psp property of given MCB
+ */
+BOOL MCB_SetPSP( WORD seg, WORD psp )
+{
+    /* FIXME: check if MCB is a valid PSP */
+    
+    MCB* mcb = (MCB*) SEG_TO_LIN (seg-1);
+    TRACE ("seg=%04xh psp=%04xh\n", seg, psp);   
+    
+    mcb->psp = psp;
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *           MCB_FreePSP
+ *
+ * Frees all blocks belonging to given psp
+ */
+void MCB_FreePSP( WORD psp )
+{
+    /* FIXME: collapse blocks ? */
+    MCB *curr = SEG_TO_LIN (MCB_First ());
+    TRACE ("psp=%04xh\n", psp);   
+    while( curr ) /* collapse free blocks */
+    {
+        if (curr->psp == psp)
+            curr->psp = PSP_FREE;
+        curr = MCB_NEXT(curr);
+    }
+}
Index: dlls/kernel/kernel32.spec
===================================================================
RCS file: /home/wine/wine/dlls/kernel/kernel32.spec,v
retrieving revision 1.138
diff -u -r1.138 kernel32.spec
--- dlls/kernel/kernel32.spec	24 Aug 2004 18:46:05 -0000	1.138
+++ dlls/kernel/kernel32.spec	10 Sep 2004 13:43:07 -0000
@@ -1166,11 +1166,7 @@
 # Wine dll separation hacks, these will go away, don't use them
 #
 @ cdecl DOSMEM_AllocSelector(long)
-@ cdecl DOSMEM_Available()
-@ cdecl DOSMEM_FreeBlock(ptr)
-@ cdecl DOSMEM_GetBlock(long ptr)
 @ cdecl DOSMEM_Init(long)
-@ cdecl DOSMEM_ResizeBlock(ptr long long)
 @ cdecl LOCAL_Alloc(long long long)
 @ cdecl LOCAL_Compact(long long long)
 @ cdecl LOCAL_CountFree(long)


More information about the wine-patches mailing list