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