1c2c66affSColin Finck /*
2c2c66affSColin Finck  * COPYRIGHT:       GPLv2+ - See COPYING in the top level directory
3c2c66affSColin Finck  * PROJECT:         ReactOS Virtual DOS Machine
4c2c66affSColin Finck  * FILE:            subsystems/mvdm/ntvdm/dos/dos32krnl/himem.c
5c2c66affSColin Finck  * PURPOSE:         DOS XMS Driver and UMB Provider
6c2c66affSColin Finck  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7c2c66affSColin Finck  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8c2c66affSColin Finck  *
9c2c66affSColin Finck  * DOCUMENTATION:   Official specifications:
10c2c66affSColin Finck  *                  XMS v2.0: http://www.phatcode.net/res/219/files/xms20.txt
11c2c66affSColin Finck  *                  XMS v3.0: http://www.phatcode.net/res/219/files/xms30.txt
12c2c66affSColin Finck  *
13c2c66affSColin Finck  * About the implementation of UMBs in DOS:
14c2c66affSColin Finck  * ----------------------------------------
15c2c66affSColin Finck  * DOS needs a UMB provider to be able to use chunks of RAM in the C000-EFF0
16c2c66affSColin Finck  * memory region. A UMB provider detects regions of memory that do not contain
17c2c66affSColin Finck  * any ROMs or other system mapped area such as video RAM.
18c2c66affSColin Finck  *
19c2c66affSColin Finck  * Where can UMB providers be found?
20c2c66affSColin Finck  *
21c2c66affSColin Finck  * The XMS specification (see himem.c) provides three APIs to create, free, and
22c2c66affSColin Finck  * resize UMB blocks. As such, DOS performs calls into the XMS driver chain to
23c2c66affSColin Finck  * request UMB blocks and include them into the DOS memory arena.
24c2c66affSColin Finck  * However, is it only the HIMEM driver (implementing the XMS specification)
25c2c66affSColin Finck  * which can provide UMBs? It appears that this is not necessarily the case:
26c2c66affSColin Finck  * for example the MS HIMEM versions do not implement the UMB APIs; instead
27c2c66affSColin Finck  * it is the EMS driver (EMM386) which provides them, by hooking into the XMS
28*fe11f7a2SKatayama Hirofumi MZ  * driver chain (see https://www.betaarchive.com/wiki/index.php?title=Microsoft_KB_Archive/95555: "MS-DOS 5.0
29c2c66affSColin Finck  * and later EMM386.EXE can also be configured to provide UMBs according to the
30c2c66affSColin Finck  * XMS. This causes EMM386.EXE to be a provider of the UMB portion of the XMS.").
31c2c66affSColin Finck  *
32c2c66affSColin Finck  * Some alternative replacements of HIMEM/EMM386 (for example in FreeDOS)
33c2c66affSColin Finck  * implement everything inside only one driver (XMS+EMS+UMB provider).
34c2c66affSColin Finck  * Finally there are some UMB providers that exist separately of XMS and EMS
35c2c66affSColin Finck  * drivers (for example, UMBPCI): they use hardware-specific tricks to discover
36c2c66affSColin Finck  * and provide UMBs.
37c2c66affSColin Finck  *
38c2c66affSColin Finck  * For more details, see:
39*fe11f7a2SKatayama Hirofumi MZ  * https://web.archive.org/web/20150326075136/http://www.freedos.org/technotes/technote/txt/169.txt
40*fe11f7a2SKatayama Hirofumi MZ  * http://www.freedos.org/technotes/technote/txt/202.txt (DEAD_LINK)
41c2c66affSColin Finck  * http://www.uncreativelabs.net/textfiles/system/UMB.TXT
42c2c66affSColin Finck  *
43c2c66affSColin Finck  * This DOS XMS Driver provides the UMB APIs that are implemented on top
44c2c66affSColin Finck  * of the internal Upper Memory Area Manager, in umamgr.c
45c2c66affSColin Finck  */
46c2c66affSColin Finck 
47c2c66affSColin Finck /* INCLUDES *******************************************************************/
48c2c66affSColin Finck 
49c2c66affSColin Finck #include "ntvdm.h"
50c2c66affSColin Finck 
51c2c66affSColin Finck #define NDEBUG
52c2c66affSColin Finck #include <debug.h>
53c2c66affSColin Finck 
54c2c66affSColin Finck #include "emulator.h"
55c2c66affSColin Finck #include "cpu/bop.h"
56c2c66affSColin Finck #include "../../memory.h"
57c2c66affSColin Finck #include "bios/umamgr.h"
58c2c66affSColin Finck 
59c2c66affSColin Finck #include "device.h"
60c2c66affSColin Finck #include "himem.h"
61c2c66affSColin Finck 
62c2c66affSColin Finck #define XMS_DEVICE_NAME "XMSXXXX0"
63c2c66affSColin Finck 
64c2c66affSColin Finck /* BOP Identifiers */
65c2c66affSColin Finck #define BOP_XMS 0x52
66c2c66affSColin Finck 
67c2c66affSColin Finck /* PRIVATE VARIABLES **********************************************************/
68c2c66affSColin Finck 
69c2c66affSColin Finck static const BYTE EntryProcedure[] =
70c2c66affSColin Finck {
71c2c66affSColin Finck     0xEB, // jmp short +0x03
72c2c66affSColin Finck     0x03,
73c2c66affSColin Finck     0x90, // nop
74c2c66affSColin Finck     0x90, // nop
75c2c66affSColin Finck     0x90, // nop
76c2c66affSColin Finck     LOBYTE(EMULATOR_BOP),
77c2c66affSColin Finck     HIBYTE(EMULATOR_BOP),
78c2c66affSColin Finck     BOP_XMS,
79c2c66affSColin Finck     0xCB // retf
80c2c66affSColin Finck };
81c2c66affSColin Finck 
82c2c66affSColin Finck static PDOS_DEVICE_NODE Node = NULL;
83c2c66affSColin Finck static XMS_HANDLE HandleTable[XMS_MAX_HANDLES];
84c2c66affSColin Finck static WORD FreeBlocks = XMS_BLOCKS;
85c2c66affSColin Finck static RTL_BITMAP AllocBitmap;
86c2c66affSColin Finck static ULONG BitmapBuffer[(XMS_BLOCKS + 31) / 32];
87c2c66affSColin Finck 
88c2c66affSColin Finck /*
89c2c66affSColin Finck  * This value is associated to HIMEM's "/HMAMIN=" switch. It indicates the
90c2c66affSColin Finck  * minimum account of space in the HMA a program can use, and is used in
91c2c66affSColin Finck  * conjunction with the "Request HMA" function.
92c2c66affSColin Finck  *
93c2c66affSColin Finck  * NOTE: The "/HMAMIN=" value is in kilo-bytes, whereas HmaMinSize is in bytes.
94c2c66affSColin Finck  *
95c2c66affSColin Finck  * Default value: 0. This causes the HMA to be allocated on a first come,
96c2c66affSColin Finck  * first served basis.
97c2c66affSColin Finck  */
98c2c66affSColin Finck static WORD HmaMinSize = 0;
99c2c66affSColin Finck /*
100c2c66affSColin Finck  * Flag used by "Request/Release HMA" functions, which indicates
101c2c66affSColin Finck  * whether the HMA was reserved or not.
102c2c66affSColin Finck  */
103c2c66affSColin Finck static BOOLEAN IsHmaReserved = FALSE;
104c2c66affSColin Finck 
105c2c66affSColin Finck /*
106c2c66affSColin Finck  * Flag used by "Global Enable/Disable A20" functions, so that they don't
107c2c66affSColin Finck  * need to re-change the state of A20 if it was already enabled/disabled.
108c2c66affSColin Finck  */
109c2c66affSColin Finck static BOOLEAN IsA20Enabled = FALSE;
110c2c66affSColin Finck /*
111c2c66affSColin Finck  * This flag is set to TRUE or FALSE when A20 line was already disabled or
112c2c66affSColin Finck  * enabled when XMS driver was loaded.
113c2c66affSColin Finck  * In case A20 was disabled, we are allowed to modify it. In case A20 was
114c2c66affSColin Finck  * already enabled, we are not allowed to touch it.
115c2c66affSColin Finck  */
116c2c66affSColin Finck static BOOLEAN CanChangeA20 = TRUE;
117c2c66affSColin Finck /*
118c2c66affSColin Finck  * Count for enabling or disabling the A20 line. The A20 line is enabled
119c2c66affSColin Finck  * only if the enabling count is greater than or equal to 0.
120c2c66affSColin Finck  */
121c2c66affSColin Finck static LONG A20EnableCount = 0;
122c2c66affSColin Finck 
123c2c66affSColin Finck /* A20 LINE HELPERS ***********************************************************/
124c2c66affSColin Finck 
XmsLocalEnableA20(VOID)125c2c66affSColin Finck static VOID XmsLocalEnableA20(VOID)
126c2c66affSColin Finck {
127c2c66affSColin Finck     /* Enable A20 only if we can do so, otherwise make the caller believe we enabled it */
128c2c66affSColin Finck     if (!CanChangeA20) goto Quit;
129c2c66affSColin Finck 
130c2c66affSColin Finck     /* The count is zero so enable A20 */
131c2c66affSColin Finck     if (A20EnableCount == 0) EmulatorSetA20(TRUE);
132c2c66affSColin Finck 
133c2c66affSColin Finck     ++A20EnableCount;
134c2c66affSColin Finck 
135c2c66affSColin Finck Quit:
136c2c66affSColin Finck     setAX(0x0001); /* Line successfully enabled */
137c2c66affSColin Finck     setBL(XMS_STATUS_SUCCESS);
138c2c66affSColin Finck     return;
139c2c66affSColin Finck }
140c2c66affSColin Finck 
XmsLocalDisableA20(VOID)141c2c66affSColin Finck static VOID XmsLocalDisableA20(VOID)
142c2c66affSColin Finck {
143c2c66affSColin Finck     UCHAR Result = XMS_STATUS_SUCCESS;
144c2c66affSColin Finck 
145c2c66affSColin Finck     /* Disable A20 only if we can do so, otherwise make the caller believe we disabled it */
146c2c66affSColin Finck     if (!CanChangeA20) goto Quit;
147c2c66affSColin Finck 
148c2c66affSColin Finck     /* If the count is already zero, fail */
149c2c66affSColin Finck     if (A20EnableCount == 0) goto Fail;
150c2c66affSColin Finck 
151c2c66affSColin Finck     --A20EnableCount;
152c2c66affSColin Finck 
153c2c66affSColin Finck     /* The count is zero so disable A20 */
154c2c66affSColin Finck     if (A20EnableCount == 0)
155c2c66affSColin Finck         EmulatorSetA20(FALSE); // Result = XMS_STATUS_SUCCESS;
156c2c66affSColin Finck     else
157c2c66affSColin Finck         Result = XMS_STATUS_A20_STILL_ENABLED;
158c2c66affSColin Finck 
159c2c66affSColin Finck Quit:
160c2c66affSColin Finck     setAX(0x0001); /* Line successfully disabled */
161c2c66affSColin Finck     setBL(Result);
162c2c66affSColin Finck     return;
163c2c66affSColin Finck 
164c2c66affSColin Finck Fail:
165c2c66affSColin Finck     setAX(0x0000); /* Line failed to be disabled */
166c2c66affSColin Finck     setBL(XMS_STATUS_A20_ERROR);
167c2c66affSColin Finck     return;
168c2c66affSColin Finck }
169c2c66affSColin Finck 
170c2c66affSColin Finck /* PRIVATE FUNCTIONS **********************************************************/
171c2c66affSColin Finck 
GetXmsHandleRecord(WORD Handle)1725ef2c451SAmine Khaldi static inline PXMS_HANDLE GetXmsHandleRecord(WORD Handle)
173c2c66affSColin Finck {
174c2c66affSColin Finck     PXMS_HANDLE Entry;
175c2c66affSColin Finck     if (Handle == 0 || Handle >= XMS_MAX_HANDLES) return NULL;
176c2c66affSColin Finck 
177c2c66affSColin Finck     Entry = &HandleTable[Handle - 1];
178c2c66affSColin Finck     return Entry->Size ? Entry : NULL;
179c2c66affSColin Finck }
180c2c66affSColin Finck 
ValidateXmsHandle(PXMS_HANDLE HandleEntry)1815ef2c451SAmine Khaldi static inline BOOLEAN ValidateXmsHandle(PXMS_HANDLE HandleEntry)
182c2c66affSColin Finck {
183c2c66affSColin Finck     return (HandleEntry != NULL && HandleEntry->Handle != 0);
184c2c66affSColin Finck }
185c2c66affSColin Finck 
XmsGetLargestFreeBlock(VOID)186c2c66affSColin Finck static WORD XmsGetLargestFreeBlock(VOID)
187c2c66affSColin Finck {
188c2c66affSColin Finck     WORD Result = 0;
189c2c66affSColin Finck     DWORD CurrentIndex = 0;
190c2c66affSColin Finck     ULONG RunStart;
191c2c66affSColin Finck     ULONG RunSize;
192c2c66affSColin Finck 
193c2c66affSColin Finck     while (CurrentIndex < XMS_BLOCKS)
194c2c66affSColin Finck     {
195c2c66affSColin Finck         RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
196c2c66affSColin Finck         if (RunSize == 0) break;
197c2c66affSColin Finck 
198c2c66affSColin Finck         /* Update the maximum */
199c2c66affSColin Finck         if (RunSize > Result) Result = RunSize;
200c2c66affSColin Finck 
201c2c66affSColin Finck         /* Go to the next run */
202c2c66affSColin Finck         CurrentIndex = RunStart + RunSize;
203c2c66affSColin Finck     }
204c2c66affSColin Finck 
205c2c66affSColin Finck     return Result;
206c2c66affSColin Finck }
207c2c66affSColin Finck 
XmsAlloc(WORD Size,PWORD Handle)208c2c66affSColin Finck static UCHAR XmsAlloc(WORD Size, PWORD Handle)
209c2c66affSColin Finck {
210c2c66affSColin Finck     BYTE i;
211c2c66affSColin Finck     PXMS_HANDLE HandleEntry;
212c2c66affSColin Finck     DWORD CurrentIndex = 0;
213c2c66affSColin Finck     ULONG RunStart;
214c2c66affSColin Finck     ULONG RunSize;
215c2c66affSColin Finck 
216c2c66affSColin Finck     if (Size > FreeBlocks) return XMS_STATUS_OUT_OF_MEMORY;
217c2c66affSColin Finck 
218c2c66affSColin Finck     for (i = 0; i < XMS_MAX_HANDLES; i++)
219c2c66affSColin Finck     {
220c2c66affSColin Finck         HandleEntry = &HandleTable[i];
221c2c66affSColin Finck         if (HandleEntry->Handle == 0)
222c2c66affSColin Finck         {
223c2c66affSColin Finck             *Handle = i + 1;
224c2c66affSColin Finck             break;
225c2c66affSColin Finck         }
226c2c66affSColin Finck     }
227c2c66affSColin Finck 
228c2c66affSColin Finck     if (i == XMS_MAX_HANDLES) return XMS_STATUS_OUT_OF_HANDLES;
229c2c66affSColin Finck 
230c2c66affSColin Finck     /* Optimize blocks */
231c2c66affSColin Finck     for (i = 0; i < XMS_MAX_HANDLES; i++)
232c2c66affSColin Finck     {
233c2c66affSColin Finck         /* Skip free and locked blocks */
234c2c66affSColin Finck         if (HandleEntry->Handle == 0 || HandleEntry->LockCount > 0) continue;
235c2c66affSColin Finck 
236c2c66affSColin Finck         CurrentIndex = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE;
237c2c66affSColin Finck 
238c2c66affSColin Finck         /* Check if there is any free space before this block */
239c2c66affSColin Finck         RunSize = RtlFindLastBackwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
240c2c66affSColin Finck         if (RunSize == 0) break;
241c2c66affSColin Finck 
242c2c66affSColin Finck         /* Move this block back */
243c2c66affSColin Finck         RtlMoveMemory((PVOID)REAL_TO_PHYS(HandleEntry->Address - RunSize * XMS_BLOCK_SIZE),
244c2c66affSColin Finck                       (PVOID)REAL_TO_PHYS(HandleEntry->Address),
245c2c66affSColin Finck                       RunSize * XMS_BLOCK_SIZE);
246c2c66affSColin Finck 
247c2c66affSColin Finck         /* Update the address */
248c2c66affSColin Finck         HandleEntry->Address -= RunSize * XMS_BLOCK_SIZE;
249c2c66affSColin Finck     }
250c2c66affSColin Finck 
251c2c66affSColin Finck     while (CurrentIndex < XMS_BLOCKS)
252c2c66affSColin Finck     {
253c2c66affSColin Finck         RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
254c2c66affSColin Finck         if (RunSize == 0) break;
255c2c66affSColin Finck 
256c2c66affSColin Finck         if (RunSize >= HandleEntry->Size)
257c2c66affSColin Finck         {
258c2c66affSColin Finck             /* Allocate it here */
259c2c66affSColin Finck             HandleEntry->Handle = i + 1;
260c2c66affSColin Finck             HandleEntry->LockCount = 0;
261c2c66affSColin Finck             HandleEntry->Size = Size;
262c2c66affSColin Finck             HandleEntry->Address = XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE;
263c2c66affSColin Finck 
264c2c66affSColin Finck             FreeBlocks -= Size;
265c2c66affSColin Finck             RtlSetBits(&AllocBitmap, RunStart, HandleEntry->Size);
266c2c66affSColin Finck 
267c2c66affSColin Finck             return XMS_STATUS_SUCCESS;
268c2c66affSColin Finck         }
269c2c66affSColin Finck 
270c2c66affSColin Finck         /* Keep searching */
271c2c66affSColin Finck         CurrentIndex = RunStart + RunSize;
272c2c66affSColin Finck     }
273c2c66affSColin Finck 
274c2c66affSColin Finck     return XMS_STATUS_OUT_OF_MEMORY;
275c2c66affSColin Finck }
276c2c66affSColin Finck 
XmsRealloc(WORD Handle,WORD NewSize)277c2c66affSColin Finck static UCHAR XmsRealloc(WORD Handle, WORD NewSize)
278c2c66affSColin Finck {
279c2c66affSColin Finck     DWORD BlockNumber;
2805ef2c451SAmine Khaldi     PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
281c2c66affSColin Finck     DWORD CurrentIndex = 0;
282c2c66affSColin Finck     ULONG RunStart;
283c2c66affSColin Finck     ULONG RunSize;
284c2c66affSColin Finck 
2855ef2c451SAmine Khaldi     if (!ValidateXmsHandle(HandleEntry))
286c2c66affSColin Finck         return XMS_STATUS_INVALID_HANDLE;
287c2c66affSColin Finck 
288c2c66affSColin Finck     if (HandleEntry->LockCount)
289c2c66affSColin Finck         return XMS_STATUS_LOCKED;
290c2c66affSColin Finck 
291c2c66affSColin Finck     /* Get the block number */
292c2c66affSColin Finck     BlockNumber = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE;
293c2c66affSColin Finck 
294c2c66affSColin Finck     if (NewSize < HandleEntry->Size)
295c2c66affSColin Finck     {
296c2c66affSColin Finck         /* Just reduce the size of this block */
297c2c66affSColin Finck         RtlClearBits(&AllocBitmap, BlockNumber + NewSize, HandleEntry->Size - NewSize);
298c2c66affSColin Finck         FreeBlocks += HandleEntry->Size - NewSize;
299c2c66affSColin Finck         HandleEntry->Size = NewSize;
300c2c66affSColin Finck     }
301c2c66affSColin Finck     else if (NewSize > HandleEntry->Size)
302c2c66affSColin Finck     {
303c2c66affSColin Finck         /* Check if we can expand in-place */
304c2c66affSColin Finck         if (RtlAreBitsClear(&AllocBitmap,
305c2c66affSColin Finck                             BlockNumber + HandleEntry->Size,
306c2c66affSColin Finck                             NewSize - HandleEntry->Size))
307c2c66affSColin Finck         {
308c2c66affSColin Finck             /* Just increase the size of this block */
309c2c66affSColin Finck             RtlSetBits(&AllocBitmap,
310c2c66affSColin Finck                        BlockNumber + HandleEntry->Size,
311c2c66affSColin Finck                        NewSize - HandleEntry->Size);
312c2c66affSColin Finck             FreeBlocks -= NewSize - HandleEntry->Size;
313c2c66affSColin Finck             HandleEntry->Size = NewSize;
314c2c66affSColin Finck 
315c2c66affSColin Finck             /* We're done */
316c2c66affSColin Finck             return XMS_STATUS_SUCCESS;
317c2c66affSColin Finck         }
318c2c66affSColin Finck 
319c2c66affSColin Finck         /* Deallocate the current block range */
320c2c66affSColin Finck         RtlClearBits(&AllocBitmap, BlockNumber, HandleEntry->Size);
321c2c66affSColin Finck 
322c2c66affSColin Finck         /* Find a new place for this block */
323c2c66affSColin Finck         while (CurrentIndex < XMS_BLOCKS)
324c2c66affSColin Finck         {
325c2c66affSColin Finck             RunSize = RtlFindNextForwardRunClear(&AllocBitmap, CurrentIndex, &RunStart);
326c2c66affSColin Finck             if (RunSize == 0) break;
327c2c66affSColin Finck 
328c2c66affSColin Finck             if (RunSize >= NewSize)
329c2c66affSColin Finck             {
330c2c66affSColin Finck                 /* Allocate the new range */
331c2c66affSColin Finck                 RtlSetBits(&AllocBitmap, RunStart, NewSize);
332c2c66affSColin Finck 
333c2c66affSColin Finck                 /* Move the data to the new location */
334c2c66affSColin Finck                 RtlMoveMemory((PVOID)REAL_TO_PHYS(XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE),
335c2c66affSColin Finck                               (PVOID)REAL_TO_PHYS(HandleEntry->Address),
336c2c66affSColin Finck                               HandleEntry->Size * XMS_BLOCK_SIZE);
337c2c66affSColin Finck 
338c2c66affSColin Finck                 /* Update the handle entry */
339c2c66affSColin Finck                 HandleEntry->Address = XMS_ADDRESS + RunStart * XMS_BLOCK_SIZE;
340c2c66affSColin Finck                 HandleEntry->Size = NewSize;
341c2c66affSColin Finck 
342c2c66affSColin Finck                 /* Update the free block counter */
343c2c66affSColin Finck                 FreeBlocks -= NewSize - HandleEntry->Size;
344c2c66affSColin Finck 
345c2c66affSColin Finck                 return XMS_STATUS_SUCCESS;
346c2c66affSColin Finck             }
347c2c66affSColin Finck 
348c2c66affSColin Finck             /* Keep searching */
349c2c66affSColin Finck             CurrentIndex = RunStart + RunSize;
350c2c66affSColin Finck         }
351c2c66affSColin Finck 
352c2c66affSColin Finck         /* Restore the old block range */
353c2c66affSColin Finck         RtlSetBits(&AllocBitmap, BlockNumber, HandleEntry->Size);
354c2c66affSColin Finck         return XMS_STATUS_OUT_OF_MEMORY;
355c2c66affSColin Finck     }
356c2c66affSColin Finck 
357c2c66affSColin Finck     return XMS_STATUS_SUCCESS;
358c2c66affSColin Finck }
359c2c66affSColin Finck 
XmsFree(WORD Handle)360c2c66affSColin Finck static UCHAR XmsFree(WORD Handle)
361c2c66affSColin Finck {
362c2c66affSColin Finck     DWORD BlockNumber;
3635ef2c451SAmine Khaldi     PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
364c2c66affSColin Finck 
3655ef2c451SAmine Khaldi     if (!ValidateXmsHandle(HandleEntry))
366c2c66affSColin Finck         return XMS_STATUS_INVALID_HANDLE;
367c2c66affSColin Finck 
368c2c66affSColin Finck     if (HandleEntry->LockCount)
369c2c66affSColin Finck         return XMS_STATUS_LOCKED;
370c2c66affSColin Finck 
371c2c66affSColin Finck     BlockNumber = (HandleEntry->Address - XMS_ADDRESS) / XMS_BLOCK_SIZE;
372c2c66affSColin Finck     RtlClearBits(&AllocBitmap, BlockNumber, HandleEntry->Size);
373c2c66affSColin Finck 
374c2c66affSColin Finck     HandleEntry->Handle = 0;
375c2c66affSColin Finck     FreeBlocks += HandleEntry->Size;
376c2c66affSColin Finck 
377c2c66affSColin Finck     return XMS_STATUS_SUCCESS;
378c2c66affSColin Finck }
379c2c66affSColin Finck 
XmsLock(WORD Handle,PDWORD Address)380c2c66affSColin Finck static UCHAR XmsLock(WORD Handle, PDWORD Address)
381c2c66affSColin Finck {
3825ef2c451SAmine Khaldi     PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
383c2c66affSColin Finck 
3845ef2c451SAmine Khaldi     if (!ValidateXmsHandle(HandleEntry))
385c2c66affSColin Finck         return XMS_STATUS_INVALID_HANDLE;
386c2c66affSColin Finck 
387c2c66affSColin Finck     if (HandleEntry->LockCount == 0xFF)
388c2c66affSColin Finck         return XMS_STATUS_LOCK_OVERFLOW;
389c2c66affSColin Finck 
390c2c66affSColin Finck     /* Increment the lock count */
391c2c66affSColin Finck     HandleEntry->LockCount++;
392c2c66affSColin Finck     *Address = HandleEntry->Address;
393c2c66affSColin Finck 
394c2c66affSColin Finck     return XMS_STATUS_SUCCESS;
395c2c66affSColin Finck }
396c2c66affSColin Finck 
XmsUnlock(WORD Handle)397c2c66affSColin Finck static UCHAR XmsUnlock(WORD Handle)
398c2c66affSColin Finck {
3995ef2c451SAmine Khaldi     PXMS_HANDLE HandleEntry = GetXmsHandleRecord(Handle);
400c2c66affSColin Finck 
4015ef2c451SAmine Khaldi     if (!ValidateXmsHandle(HandleEntry))
402c2c66affSColin Finck         return XMS_STATUS_INVALID_HANDLE;
403c2c66affSColin Finck 
404c2c66affSColin Finck     if (!HandleEntry->LockCount)
405c2c66affSColin Finck         return XMS_STATUS_NOT_LOCKED;
406c2c66affSColin Finck 
407c2c66affSColin Finck     /* Decrement the lock count */
408c2c66affSColin Finck     HandleEntry->LockCount--;
409c2c66affSColin Finck 
410c2c66affSColin Finck     return XMS_STATUS_SUCCESS;
411c2c66affSColin Finck }
412c2c66affSColin Finck 
XmsBopProcedure(LPWORD Stack)413c2c66affSColin Finck static VOID WINAPI XmsBopProcedure(LPWORD Stack)
414c2c66affSColin Finck {
415c2c66affSColin Finck     switch (getAH())
416c2c66affSColin Finck     {
417c2c66affSColin Finck         /* Get XMS Version */
418c2c66affSColin Finck         case 0x00:
419c2c66affSColin Finck         {
420c2c66affSColin Finck             setAX(0x0300); /*    XMS version 3.00 */
421c2c66affSColin Finck             setBX(0x0301); /* Driver version 3.01 */
422c2c66affSColin Finck             setDX(0x0001); /* HMA present */
423c2c66affSColin Finck             break;
424c2c66affSColin Finck         }
425c2c66affSColin Finck 
426c2c66affSColin Finck         /* Request HMA */
427c2c66affSColin Finck         case 0x01:
428c2c66affSColin Finck         {
429c2c66affSColin Finck             /* Check whether HMA is already reserved */
430c2c66affSColin Finck             if (IsHmaReserved)
431c2c66affSColin Finck             {
432c2c66affSColin Finck                 /* It is, bail out */
433c2c66affSColin Finck                 setAX(0x0000);
434c2c66affSColin Finck                 setBL(XMS_STATUS_HMA_IN_USE);
435c2c66affSColin Finck                 break;
436c2c66affSColin Finck             }
437c2c66affSColin Finck 
438fcbdab00SHermès Bélusca-Maïto             // NOTE: We implicitly suppose that we always have HMA.
439c2c66affSColin Finck             // If not, we should fail there with the XMS_STATUS_HMA_DOES_NOT_EXIST
440c2c66affSColin Finck             // error code.
441c2c66affSColin Finck 
442c2c66affSColin Finck             /* Check whether the requested size is above the minimal allowed one */
443c2c66affSColin Finck             if (getDX() < HmaMinSize)
444c2c66affSColin Finck             {
445c2c66affSColin Finck                 /* It is not, bail out */
446c2c66affSColin Finck                 setAX(0x0000);
447c2c66affSColin Finck                 setBL(XMS_STATUS_HMA_MIN_SIZE);
448c2c66affSColin Finck                 break;
449c2c66affSColin Finck             }
450c2c66affSColin Finck 
451c2c66affSColin Finck             /* Reserve it */
452c2c66affSColin Finck             IsHmaReserved = TRUE;
453c2c66affSColin Finck             setAX(0x0001);
454c2c66affSColin Finck             setBL(XMS_STATUS_SUCCESS);
455c2c66affSColin Finck             break;
456c2c66affSColin Finck         }
457c2c66affSColin Finck 
458c2c66affSColin Finck         /* Release HMA */
459c2c66affSColin Finck         case 0x02:
460c2c66affSColin Finck         {
461c2c66affSColin Finck             /* Check whether HMA was reserved */
462c2c66affSColin Finck             if (!IsHmaReserved)
463c2c66affSColin Finck             {
464c2c66affSColin Finck                 /* It was not, bail out */
465c2c66affSColin Finck                 setAX(0x0000);
466c2c66affSColin Finck                 setBL(XMS_STATUS_HMA_NOT_ALLOCATED);
467c2c66affSColin Finck                 break;
468c2c66affSColin Finck             }
469c2c66affSColin Finck 
470c2c66affSColin Finck             /* Release it */
471c2c66affSColin Finck             IsHmaReserved = FALSE;
472c2c66affSColin Finck             setAX(0x0001);
473c2c66affSColin Finck             setBL(XMS_STATUS_SUCCESS);
474c2c66affSColin Finck             break;
475c2c66affSColin Finck         }
476c2c66affSColin Finck 
477c2c66affSColin Finck         /* Global Enable A20 */
478c2c66affSColin Finck         case 0x03:
479c2c66affSColin Finck         {
480c2c66affSColin Finck             /* Enable A20 if needed */
481c2c66affSColin Finck             if (!IsA20Enabled)
482c2c66affSColin Finck             {
483c2c66affSColin Finck                 XmsLocalEnableA20();
484c2c66affSColin Finck                 if (getAX() != 0x0001)
485c2c66affSColin Finck                 {
486c2c66affSColin Finck                     /* XmsLocalEnableA20 failed and already set AX and BL to their correct values */
487c2c66affSColin Finck                     break;
488c2c66affSColin Finck                 }
489c2c66affSColin Finck 
490c2c66affSColin Finck                 IsA20Enabled = TRUE;
491c2c66affSColin Finck             }
492c2c66affSColin Finck 
493c2c66affSColin Finck             setAX(0x0001); /* Line successfully enabled */
494c2c66affSColin Finck             setBL(XMS_STATUS_SUCCESS);
495c2c66affSColin Finck             break;
496c2c66affSColin Finck         }
497c2c66affSColin Finck 
498c2c66affSColin Finck         /* Global Disable A20 */
499c2c66affSColin Finck         case 0x04:
500c2c66affSColin Finck         {
501c2c66affSColin Finck             UCHAR Result = XMS_STATUS_SUCCESS;
502c2c66affSColin Finck 
503c2c66affSColin Finck             /* Disable A20 if needed */
504c2c66affSColin Finck             if (IsA20Enabled)
505c2c66affSColin Finck             {
506c2c66affSColin Finck                 XmsLocalDisableA20();
507c2c66affSColin Finck                 if (getAX() != 0x0001)
508c2c66affSColin Finck                 {
509c2c66affSColin Finck                     /* XmsLocalDisableA20 failed and already set AX and BL to their correct values */
510c2c66affSColin Finck                     break;
511c2c66affSColin Finck                 }
512c2c66affSColin Finck 
513c2c66affSColin Finck                 IsA20Enabled = FALSE;
514c2c66affSColin Finck                 Result = getBL();
515c2c66affSColin Finck             }
516c2c66affSColin Finck 
517c2c66affSColin Finck             setAX(0x0001); /* Line successfully disabled */
518c2c66affSColin Finck             setBL(Result);
519c2c66affSColin Finck             break;
520c2c66affSColin Finck         }
521c2c66affSColin Finck 
522c2c66affSColin Finck         /* Local Enable A20 */
523c2c66affSColin Finck         case 0x05:
524c2c66affSColin Finck         {
525c2c66affSColin Finck             /* This call sets AX and BL to their correct values */
526c2c66affSColin Finck             XmsLocalEnableA20();
527c2c66affSColin Finck             break;
528c2c66affSColin Finck         }
529c2c66affSColin Finck 
530c2c66affSColin Finck         /* Local Disable A20 */
531c2c66affSColin Finck         case 0x06:
532c2c66affSColin Finck         {
533c2c66affSColin Finck             /* This call sets AX and BL to their correct values */
534c2c66affSColin Finck             XmsLocalDisableA20();
535c2c66affSColin Finck             break;
536c2c66affSColin Finck         }
537c2c66affSColin Finck 
538c2c66affSColin Finck         /* Query A20 State */
539c2c66affSColin Finck         case 0x07:
540c2c66affSColin Finck         {
541c2c66affSColin Finck             setAX(EmulatorGetA20());
542c2c66affSColin Finck             setBL(XMS_STATUS_SUCCESS);
543c2c66affSColin Finck             break;
544c2c66affSColin Finck         }
545c2c66affSColin Finck 
546c2c66affSColin Finck         /* Query Free Extended Memory */
547c2c66affSColin Finck         case 0x08:
548c2c66affSColin Finck         {
549c2c66affSColin Finck             setAX(XmsGetLargestFreeBlock());
550c2c66affSColin Finck             setDX(FreeBlocks);
551c2c66affSColin Finck 
552c2c66affSColin Finck             if (FreeBlocks > 0)
553c2c66affSColin Finck                 setBL(XMS_STATUS_SUCCESS);
554c2c66affSColin Finck             else
555c2c66affSColin Finck                 setBL(XMS_STATUS_OUT_OF_MEMORY);
556c2c66affSColin Finck 
557c2c66affSColin Finck             break;
558c2c66affSColin Finck         }
559c2c66affSColin Finck 
560c2c66affSColin Finck         /* Allocate Extended Memory Block */
561c2c66affSColin Finck         case 0x09:
562c2c66affSColin Finck         {
563c2c66affSColin Finck             WORD Handle;
564c2c66affSColin Finck             UCHAR Result = XmsAlloc(getDX(), &Handle);
565c2c66affSColin Finck 
566c2c66affSColin Finck             if (Result == XMS_STATUS_SUCCESS)
567c2c66affSColin Finck             {
568c2c66affSColin Finck                 setAX(1);
569c2c66affSColin Finck                 setDX(Handle);
570c2c66affSColin Finck             }
571c2c66affSColin Finck             else
572c2c66affSColin Finck             {
573c2c66affSColin Finck                 setAX(0);
574c2c66affSColin Finck                 setBL(Result);
575c2c66affSColin Finck             }
576c2c66affSColin Finck 
577c2c66affSColin Finck             break;
578c2c66affSColin Finck         }
579c2c66affSColin Finck 
580c2c66affSColin Finck         /* Free Extended Memory Block */
581c2c66affSColin Finck         case 0x0A:
582c2c66affSColin Finck         {
583c2c66affSColin Finck             UCHAR Result = XmsFree(getDX());
584c2c66affSColin Finck 
585c2c66affSColin Finck             setAX(Result == XMS_STATUS_SUCCESS);
586c2c66affSColin Finck             setBL(Result);
587c2c66affSColin Finck             break;
588c2c66affSColin Finck         }
589c2c66affSColin Finck 
590c2c66affSColin Finck         /* Move Extended Memory Block */
591c2c66affSColin Finck         case 0x0B:
592c2c66affSColin Finck         {
593c2c66affSColin Finck             PVOID SourceAddress, DestAddress;
594c2c66affSColin Finck             PXMS_COPY_DATA CopyData = (PXMS_COPY_DATA)SEG_OFF_TO_PTR(getDS(), getSI());
595c2c66affSColin Finck             PXMS_HANDLE HandleEntry;
596c2c66affSColin Finck 
597c2c66affSColin Finck             if (CopyData->SourceHandle)
598c2c66affSColin Finck             {
5995ef2c451SAmine Khaldi                 HandleEntry = GetXmsHandleRecord(CopyData->SourceHandle);
6005ef2c451SAmine Khaldi                 if (!ValidateXmsHandle(HandleEntry))
601c2c66affSColin Finck                 {
602c2c66affSColin Finck                     setAX(0);
603c2c66affSColin Finck                     setBL(XMS_STATUS_BAD_SRC_HANDLE);
604c2c66affSColin Finck                     break;
605c2c66affSColin Finck                 }
606c2c66affSColin Finck 
607c2c66affSColin Finck                 if (CopyData->SourceOffset >= HandleEntry->Size * XMS_BLOCK_SIZE)
608c2c66affSColin Finck                 {
609c2c66affSColin Finck                     setAX(0);
610c2c66affSColin Finck                     setBL(XMS_STATUS_BAD_SRC_OFFSET);
611c2c66affSColin Finck                 }
612c2c66affSColin Finck 
613c2c66affSColin Finck                 SourceAddress = (PVOID)REAL_TO_PHYS(HandleEntry->Address + CopyData->SourceOffset);
614c2c66affSColin Finck             }
615c2c66affSColin Finck             else
616c2c66affSColin Finck             {
617c2c66affSColin Finck                 /* The offset is actually a 16-bit segment:offset pointer */
618c2c66affSColin Finck                 SourceAddress = FAR_POINTER(CopyData->SourceOffset);
619c2c66affSColin Finck             }
620c2c66affSColin Finck 
621c2c66affSColin Finck             if (CopyData->DestHandle)
622c2c66affSColin Finck             {
6235ef2c451SAmine Khaldi                 HandleEntry = GetXmsHandleRecord(CopyData->DestHandle);
6245ef2c451SAmine Khaldi                 if (!ValidateXmsHandle(HandleEntry))
625c2c66affSColin Finck                 {
626c2c66affSColin Finck                     setAX(0);
627c2c66affSColin Finck                     setBL(XMS_STATUS_BAD_DEST_HANDLE);
628c2c66affSColin Finck                     break;
629c2c66affSColin Finck                 }
630c2c66affSColin Finck 
631c2c66affSColin Finck                 if (CopyData->DestOffset >= HandleEntry->Size * XMS_BLOCK_SIZE)
632c2c66affSColin Finck                 {
633c2c66affSColin Finck                     setAX(0);
634c2c66affSColin Finck                     setBL(XMS_STATUS_BAD_DEST_OFFSET);
635c2c66affSColin Finck                 }
636c2c66affSColin Finck 
637c2c66affSColin Finck                 DestAddress = (PVOID)REAL_TO_PHYS(HandleEntry->Address + CopyData->DestOffset);
638c2c66affSColin Finck             }
639c2c66affSColin Finck             else
640c2c66affSColin Finck             {
641c2c66affSColin Finck                 /* The offset is actually a 16-bit segment:offset pointer */
642c2c66affSColin Finck                 DestAddress = FAR_POINTER(CopyData->DestOffset);
643c2c66affSColin Finck             }
644c2c66affSColin Finck 
645c2c66affSColin Finck             /* Perform the move */
646c2c66affSColin Finck             RtlMoveMemory(DestAddress, SourceAddress, CopyData->Count);
647c2c66affSColin Finck 
648c2c66affSColin Finck             setAX(1);
649c2c66affSColin Finck             setBL(XMS_STATUS_SUCCESS);
650c2c66affSColin Finck             break;
651c2c66affSColin Finck         }
652c2c66affSColin Finck 
653c2c66affSColin Finck         /* Lock Extended Memory Block */
654c2c66affSColin Finck         case 0x0C:
655c2c66affSColin Finck         {
656c2c66affSColin Finck             DWORD Address;
657c2c66affSColin Finck             UCHAR Result = XmsLock(getDX(), &Address);
658c2c66affSColin Finck 
659c2c66affSColin Finck             if (Result == XMS_STATUS_SUCCESS)
660c2c66affSColin Finck             {
661c2c66affSColin Finck                 setAX(1);
662c2c66affSColin Finck 
663c2c66affSColin Finck                 /* Store the LINEAR address in DX:BX */
664c2c66affSColin Finck                 setDX(HIWORD(Address));
665c2c66affSColin Finck                 setBX(LOWORD(Address));
666c2c66affSColin Finck             }
667c2c66affSColin Finck             else
668c2c66affSColin Finck             {
669c2c66affSColin Finck                 setAX(0);
670c2c66affSColin Finck                 setBL(Result);
671c2c66affSColin Finck             }
672c2c66affSColin Finck 
673c2c66affSColin Finck             break;
674c2c66affSColin Finck         }
675c2c66affSColin Finck 
676c2c66affSColin Finck         /* Unlock Extended Memory Block */
677c2c66affSColin Finck         case 0x0D:
678c2c66affSColin Finck         {
679c2c66affSColin Finck             UCHAR Result = XmsUnlock(getDX());
680c2c66affSColin Finck 
681c2c66affSColin Finck             setAX(Result == XMS_STATUS_SUCCESS);
682c2c66affSColin Finck             setBL(Result);
683c2c66affSColin Finck             break;
684c2c66affSColin Finck         }
685c2c66affSColin Finck 
686c2c66affSColin Finck         /* Get Handle Information */
687c2c66affSColin Finck         case 0x0E:
688c2c66affSColin Finck         {
6895ef2c451SAmine Khaldi             PXMS_HANDLE HandleEntry = GetXmsHandleRecord(getDX());
690c2c66affSColin Finck             UINT i;
691c2c66affSColin Finck             UCHAR Handles = 0;
692c2c66affSColin Finck 
6935ef2c451SAmine Khaldi             if (!ValidateXmsHandle(HandleEntry))
694c2c66affSColin Finck             {
695c2c66affSColin Finck                 setAX(0);
696c2c66affSColin Finck                 setBL(XMS_STATUS_INVALID_HANDLE);
697c2c66affSColin Finck                 break;
698c2c66affSColin Finck             }
699c2c66affSColin Finck 
700c2c66affSColin Finck             for (i = 0; i < XMS_MAX_HANDLES; i++)
701c2c66affSColin Finck             {
702c2c66affSColin Finck                 if (HandleTable[i].Handle == 0) Handles++;
703c2c66affSColin Finck             }
704c2c66affSColin Finck 
705c2c66affSColin Finck             setAX(1);
706c2c66affSColin Finck             setBH(HandleEntry->LockCount);
707c2c66affSColin Finck             setBL(Handles);
708c2c66affSColin Finck             setDX(HandleEntry->Size);
709c2c66affSColin Finck             break;
710c2c66affSColin Finck         }
711c2c66affSColin Finck 
712c2c66affSColin Finck         /* Reallocate Extended Memory Block */
713c2c66affSColin Finck         case 0x0F:
714c2c66affSColin Finck         {
715c2c66affSColin Finck             UCHAR Result = XmsRealloc(getDX(), getBX());
716c2c66affSColin Finck 
717c2c66affSColin Finck             setAX(Result == XMS_STATUS_SUCCESS);
718c2c66affSColin Finck             setBL(Result);
719c2c66affSColin Finck             break;
720c2c66affSColin Finck         }
721c2c66affSColin Finck 
722c2c66affSColin Finck         /* Request UMB */
723c2c66affSColin Finck         case 0x10:
724c2c66affSColin Finck         {
725c2c66affSColin Finck             BOOLEAN Result;
726c2c66affSColin Finck             USHORT Segment = 0x0000; /* No preferred segment  */
727c2c66affSColin Finck             USHORT Size = getDX();   /* Size is in paragraphs */
728c2c66affSColin Finck 
729c2c66affSColin Finck             Result = UmaDescReserve(&Segment, &Size);
730c2c66affSColin Finck             if (Result)
731c2c66affSColin Finck                 setBX(Segment);
732c2c66affSColin Finck             else
733c2c66affSColin Finck                 setBL(Size > 0 ? XMS_STATUS_SMALLER_UMB : XMS_STATUS_OUT_OF_UMBS);
734c2c66affSColin Finck 
735c2c66affSColin Finck             setDX(Size);
736c2c66affSColin Finck             setAX(Result);
737c2c66affSColin Finck             break;
738c2c66affSColin Finck         }
739c2c66affSColin Finck 
740c2c66affSColin Finck         /* Release UMB */
741c2c66affSColin Finck         case 0x11:
742c2c66affSColin Finck         {
743c2c66affSColin Finck             BOOLEAN Result;
744c2c66affSColin Finck             USHORT Segment = getDX();
745c2c66affSColin Finck 
746c2c66affSColin Finck             Result = UmaDescRelease(Segment);
747c2c66affSColin Finck             if (!Result)
748c2c66affSColin Finck                 setBL(XMS_STATUS_INVALID_UMB);
749c2c66affSColin Finck 
750c2c66affSColin Finck             setAX(Result);
751c2c66affSColin Finck             break;
752c2c66affSColin Finck         }
753c2c66affSColin Finck 
754c2c66affSColin Finck         /* Reallocate UMB */
755c2c66affSColin Finck         case 0x12:
756c2c66affSColin Finck         {
757c2c66affSColin Finck             BOOLEAN Result;
758c2c66affSColin Finck             USHORT Segment = getDX();
759c2c66affSColin Finck             USHORT Size = getBX(); /* Size is in paragraphs */
760c2c66affSColin Finck 
761c2c66affSColin Finck             Result = UmaDescReallocate(Segment, &Size);
762c2c66affSColin Finck             if (!Result)
763c2c66affSColin Finck             {
764c2c66affSColin Finck                 if (Size > 0)
765c2c66affSColin Finck                 {
766c2c66affSColin Finck                     setBL(XMS_STATUS_SMALLER_UMB);
767c2c66affSColin Finck                     setDX(Size);
768c2c66affSColin Finck                 }
769c2c66affSColin Finck                 else
770c2c66affSColin Finck                 {
771c2c66affSColin Finck                     setBL(XMS_STATUS_INVALID_UMB);
772c2c66affSColin Finck                 }
773c2c66affSColin Finck             }
774c2c66affSColin Finck 
775c2c66affSColin Finck             setAX(Result);
776c2c66affSColin Finck             break;
777c2c66affSColin Finck         }
778c2c66affSColin Finck 
779c2c66affSColin Finck         default:
780c2c66affSColin Finck         {
781c2c66affSColin Finck             DPRINT1("XMS command AH = 0x%02X NOT IMPLEMENTED\n", getAH());
782c2c66affSColin Finck             setBL(XMS_STATUS_NOT_IMPLEMENTED);
783c2c66affSColin Finck         }
784c2c66affSColin Finck     }
785c2c66affSColin Finck }
786c2c66affSColin Finck 
787c2c66affSColin Finck /* PUBLIC FUNCTIONS ***********************************************************/
788c2c66affSColin Finck 
XmsGetDriverEntry(PDWORD Pointer)789c2c66affSColin Finck BOOLEAN XmsGetDriverEntry(PDWORD Pointer)
790c2c66affSColin Finck {
791c2c66affSColin Finck     if (Node == NULL) return FALSE;
792c2c66affSColin Finck     *Pointer = DEVICE_PRIVATE_AREA(Node->Driver);
793c2c66affSColin Finck     return TRUE;
794c2c66affSColin Finck }
795c2c66affSColin Finck 
XmsInitialize(VOID)796c2c66affSColin Finck VOID XmsInitialize(VOID)
797c2c66affSColin Finck {
798c2c66affSColin Finck     RtlZeroMemory(HandleTable, sizeof(HandleTable));
799c2c66affSColin Finck     RtlZeroMemory(BitmapBuffer, sizeof(BitmapBuffer));
800c2c66affSColin Finck     RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, XMS_BLOCKS);
801c2c66affSColin Finck 
802c2c66affSColin Finck     Node = DosCreateDeviceEx(DOS_DEVATTR_IOCTL | DOS_DEVATTR_CHARACTER,
803c2c66affSColin Finck                              XMS_DEVICE_NAME,
804c2c66affSColin Finck                              sizeof(EntryProcedure));
805c2c66affSColin Finck 
806c2c66affSColin Finck     RegisterBop(BOP_XMS, XmsBopProcedure);
807c2c66affSColin Finck 
808c2c66affSColin Finck     /* Copy the entry routine to the device private area */
809c2c66affSColin Finck     RtlMoveMemory(FAR_POINTER(DEVICE_PRIVATE_AREA(Node->Driver)),
810c2c66affSColin Finck                   EntryProcedure,
811c2c66affSColin Finck                   sizeof(EntryProcedure));
812c2c66affSColin Finck }
813c2c66affSColin Finck 
XmsCleanup(VOID)814c2c66affSColin Finck VOID XmsCleanup(VOID)
815c2c66affSColin Finck {
816c2c66affSColin Finck     RegisterBop(BOP_XMS, NULL);
817c2c66affSColin Finck     DosDeleteDevice(Node);
818c2c66affSColin Finck }
819