2 * Copyright (c) 1997 Helmut Wirth <hfwirth@ping.at>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice immediately at the beginning of the file, witout modification,
10 * this list of conditions, and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * $FreeBSD: src/usr.bin/doscmd/xms.c,v 1.6.2.1 2002/04/25 11:04:51 tg Exp $
29 * $DragonFly: src/usr.bin/doscmd/xms.c,v 1.3 2003/10/04 22:36:43 hmp Exp $
33 * XMS memory manmagement
35 * To emulate DOS extended memory (EMM) we use an implementation of
36 * HIMEM.SYS driver capabitlities, according to the XMS 3.0 Spec.
37 * The actual memory allocated via XMS calls from DOS is allocated
38 * via malloc by the emulator. Maximum memory allocation is configureable.
41 * The original author of this file, some parts are still here
42 * Linux dosemu programmers. I looked into their code.
45 #include <sys/types.h>
46 #include <sys/param.h>
54 /* Extended memory handle management */
56 static XMS_handle xms_hand
[NUM_HANDLES
];
57 int num_free_handle
= NUM_HANDLES
;
59 /* This is planned to be selectable from .doscmdrc */
60 u_long xms_maxsize
= DEFAULT_EMM_SIZE
;
61 static u_long xms_free_mem
;
62 static u_long xms_used_mem
;
63 static u_char vec_grabbed
;
65 /* Address entry for zero size allocated handles */
66 #define XMS_NULL_ALLOC 0xffffffff
68 /* High memory area (HMA) management */
69 static u_char HMA_allocated
= 0;
70 static short HMA_a20
= -1;
71 static int HMA_fd_off
, HMA_fd_on
;
73 /* high memory mapfiles */
74 static char memfile
[] = "/tmp/doscmd.XXXXXX";
76 /* Upper memory block (UMB) management */
77 UMB_block
*UMB_freelist
= NULL
;
78 UMB_block
*UMB_alloclist
= NULL
;
80 /* Calls to emulator */
82 static u_char xms_trampoline
[] = {
92 /* Local prototypes */
93 static void add_block(UMB_block
**listp
, UMB_block
*blk
);
94 static UMB_block
*create_block(u_long addr
, u_long size
);
95 static void disable_a20(void);
96 static void enable_a20(void);
97 static int get_free_handle(void);
98 static void merge_blocks(void);
99 static void xms_entry(regcontext_t
*REGS
);
101 /* Init the entire module */
105 /* Initialize handle table: xms_handle.addr == 0 means free */
106 bzero((void *)xms_hand
, sizeof(XMS_handle
) * NUM_HANDLES
);
107 xms_free_mem
= xms_maxsize
;
111 /* Initialize UMB blocks */
112 /* 0xD0000 to 0xDffff */
113 add_block(&UMB_freelist
, create_block(0xd0000, 64*1024));
114 /*XXX check for EMS emulation, when it is done! */
115 /* 0xE0000 to 0xEffff */
117 /* This is used as window for EMS, will be configurable ! */
118 /* add_block(&UMB_freelist, create_block(0xe0000, 64*1024)); */
122 xms_vector
= insert_generic_trampoline(
123 sizeof(xms_trampoline
), xms_trampoline
);
124 register_callback(xms_vector
+ 5, xms_entry
, "xms");
128 * UMB management routines: UMBs normally lie between 0xd0000 and
129 * 0xefff0 in VM86 memory space and are accessible for all DOS applictions.
130 * We could enable more space, but with the emulator we do not
131 * need many drivers, so I think 2 * 64kB will suffice. If EMS emulation
132 * exists, a 64kB segment (0xe0000 - 0xeffff for example) is needed for
133 * the EMS mapping, in this case we have 64kB UMB space. This is more than
134 * many PCs are able to do.
135 * This emulation does only the management for the memory, the memory
136 * is present and read/write/excutable for VM86 applications.
139 /* Add a block to a list, maintain ascending start address order */
142 add_block(UMB_block
**listp
, UMB_block
*blk
)
146 /* No blocks there, attach the new block to the head */
147 if (*listp
== NULL
) {
151 /* Insert at the start */
153 if (blk
->addr
< bp
->addr
) {
158 /* Not at the start, insert into the list */
159 for (; bp
!= NULL
; bp
= bp
->next
) {
160 if (blk
->addr
> bp
->addr
) {
169 /* Append to the end of the list */
176 /* Find a block with address addr in the alloc list */
178 find_allocated_block(u_long addr
)
182 if (UMB_alloclist
== NULL
)
185 for (bp
= UMB_alloclist
; bp
!= NULL
; bp
= bp
->next
)
186 if (bp
->addr
== addr
)
191 /* Remove a block blk from a list, the block must exist on the list */
193 remove_block(UMB_block
**listp
, UMB_block
*blk
)
201 *listp
= (*listp
)->next
;
206 if (bp
->next
== blk
) {
207 bp
->next
= bp
->next
->next
;
213 fatal("XMS: UMB remove_block did not find block\n");
216 /* Try to merge neighbouring blocks in the free list */
223 if (UMB_freelist
== NULL
)
227 endaddr
= bp
->addr
+ bp
->size
;
228 if (bp
->next
!= NULL
&& endaddr
== bp
->next
->addr
) {
229 /* Merge the current and the next block */
230 UMB_block
*mergebp
= bp
->next
;
231 bp
->size
+= mergebp
->size
;
232 bp
->next
= mergebp
->next
;
235 /* Goto next block */
238 } while (bp
!= NULL
);
241 /* Try to find a free block of size exactly siz */
243 find_exact_block(u_long siz
)
247 if (UMB_freelist
== NULL
)
250 for (bp
= UMB_freelist
; bp
!= NULL
; bp
= bp
->next
)
256 /* Try to find a block with a size bigger than requested. If there is
257 * no such block, return the block with the biggest size. If there is
258 * no free block at all, return NULL
261 find_block(u_long siz
)
264 UMB_block
*biggest
= NULL
;
266 if (UMB_freelist
== NULL
)
269 for (bp
= UMB_freelist
; bp
!= NULL
; bp
= bp
->next
) {
272 if (biggest
== NULL
) {
276 if (biggest
->size
< bp
->size
)
282 /* Create a block structure, memory is allocated. The structure lives
283 * until the block is merged into another block, then it is freed */
285 create_block(u_long addr
, u_long size
)
289 if ((blk
= malloc(sizeof(UMB_block
))) == NULL
)
290 fatal ("XMS: Cannot allocate UMB structure\n");
299 * initHMA(): The first 64kB of memory are mapped from 1MB (0x100000)
300 * again to emulate the address wrap around of the 808x. The HMA area
301 * is a cheap trick, usable only with 80386 and higher. The 80[345..]86
302 * does not have this address wrap around. If more than 1MB is installed
303 * the processor can address more than 1MB: load 0xFFFF to the segment
304 * register and using the full offset of 0xffff the resulting highest
305 * address is (0xffff << 4) + 0xffff = 0x10ffef. Nearly 64kB are accessible
306 * from real or VM86 mode. The mmap calls emulate the address wrap by
307 * mapping the lowest 64kB the the first 64kB after the 1MB limit.
308 * In hardware this is achieved by setting and resetting the a20 bit,
309 * an ugly compatibility hack: The hardware simlpy clamps the address 20
310 * line of the processor to low and hence the wrap around is forced.
311 * This is switchable via the BIOS or via HIMEM.SYS and therefore the
312 * first 64kB over 1MB can be enabled or disabled at will by software.
313 * DOS uses this trick to load itself high, if the memory is present.
314 * We emulate this behaviour by mapping and unmapping the HMA area.
315 * (Linux has implemented this feature using shared memory (SHM) calls.)
317 * This routine is called from doscmd.c at startup. A20 is disabled after
326 * We need two files, one for the wrap around mapping and one
327 * for the HMA contents
330 mfd
= mkstemp(memfile
);
333 fprintf(stderr
, "memfile: %s\n", strerror(errno
));
334 fprintf(stderr
, "High memory will not be mapped\n");
336 /* We need this for XMS services. If it fails, turn HMA off */
341 HMA_fd_off
= squirrel_fd(mfd
);
343 lseek(HMA_fd_off
, 64 * 1024 - 1, 0);
344 write(HMA_fd_off
, "", 1);
346 mfd
= mkstemp(memfile
);
349 fprintf(stderr
, "memfile: %s\n", strerror(errno
));
350 fprintf(stderr
, "High memory will not be mapped\n");
352 /* We need this for XMS services. If it fails, turn HMA off */
357 HMA_fd_on
= squirrel_fd(mfd
);
359 lseek(HMA_fd_on
, 64 * 1024 - 1, 0);
360 write(HMA_fd_on
, "", 1);
362 if (mmap((caddr_t
)0x000000, 0x100000,
363 PROT_EXEC
| PROT_READ
| PROT_WRITE
,
364 MAP_ANON
| MAP_FIXED
| MAP_SHARED
,
365 -1, 0) == MAP_FAILED
) {
366 perror("Error mapping HMA, HMA disabled: ");
372 if (mmap((caddr_t
)0x000000, 64 * 1024,
373 PROT_EXEC
| PROT_READ
| PROT_WRITE
,
374 MAP_FILE
| MAP_FIXED
| MAP_SHARED
,
375 HMA_fd_off
, 0) == MAP_FAILED
) {
376 perror("Error mapping HMA, HMA disabled: ");
382 if (mmap((caddr_t
)0x100000, 64 * 1024,
383 PROT_EXEC
| PROT_READ
| PROT_WRITE
,
384 MAP_FILE
| MAP_FIXED
| MAP_SHARED
,
385 HMA_fd_off
, 0) == MAP_FAILED
) {
386 perror("Error mapping HMA, HMA disabled: ");
396 /* Enable the a20 "address line" by unmapping the 64kB over 1MB */
397 static void enable_a20(void)
402 /* Unmap the wrap around portion (fd = HMA_fd_off) */
403 /* XXX Not sure about this: Should I unmap first, then map new or
404 * does it suffice to map new "over' the existing mapping ? Both
405 * works (define to #if 0 next line and some lines below to try.
408 if (munmap((caddr_t
)0x100000, 64 * 1024) < 0) {
409 fatal("HMA unmapping error: %s\nCannot recover\n", strerror(errno
));
412 /* Map memory for the HMA with fd = HMA_fd_on */
413 if (mmap((caddr_t
)0x100000, 64 * 1024,
414 PROT_EXEC
| PROT_READ
| PROT_WRITE
,
415 MAP_FILE
| MAP_FIXED
| MAP_SHARED
,
416 HMA_fd_on
, 0) == MAP_FAILED
) {
417 fatal("HMA mapping error: %s\nCannot recover\n", strerror(errno
));
421 /* Disable the a20 "address line" by mapping the 64kB over 1MB again */
422 static void disable_a20(void)
427 /* Unmap the HMA (fd = HMA_fd_on) */
428 if (munmap((caddr_t
)0x100000, 64 * 1024) < 0) {
429 fatal("HMA unmapping error: %s\nCannot recover\n", strerror(errno
));
432 /* Remap the wrap around area */
433 if (mmap((caddr_t
)0x100000, 64 * 1024,
434 PROT_EXEC
| PROT_READ
| PROT_WRITE
,
435 MAP_FILE
| MAP_FIXED
| MAP_SHARED
,
436 HMA_fd_off
, 0) == MAP_FAILED
) {
437 fatal("HMA mapping error: %s\nCannot recover\n", strerror(errno
));
443 * This handles calls to int15 function 88: BIOS extended memory
444 * request. XMS spec says: "In order to maintain compatibility with existing
445 * device drivers, DOS XMS drivers must not hook INT 15h until the first
446 * non-Version Number call to the control function is made."
450 get_raw_extmemory_info(regcontext_t
*REGS
)
455 R_AX
= xms_maxsize
/ 1024;
460 /* Handle management routine: Find next free handle */
463 get_free_handle(void)
466 /* Linear search, there are only a few handles */
467 for (i
= 0; i
< NUM_HANDLES
; i
++) {
468 if (xms_hand
[i
].addr
== 0)
475 /* Installation check */
477 int2f_43(regcontext_t
*REGS
)
481 case 0x00: /* installation check */
485 case 0x10: /* get handler address */
486 PUTVEC(R_ES
, R_BX
, xms_vector
);
495 /* Main call entry point for the XMS handler from DOS */
497 xms_entry(regcontext_t
*REGS
)
503 /* If the HMA feature is disabled these calls are "not managed" */
505 if (R_AH
== XMS_ALLOCATE_HIGH_MEMORY
|| R_AH
== XMS_FREE_HIGH_MEMORY
||
506 R_AH
== XMS_GLOBAL_ENABLE_A20
|| R_AH
== XMS_GLOBAL_DISABLE_A20
||
507 R_AH
== XMS_LOCAL_ENABLE_A20
|| R_AH
== XMS_LOCAL_DISABLE_A20
||
508 R_AH
== XMS_QUERY_A20
) {
510 R_BL
= XMS_HMA_NOT_MANAGED
;
516 case XMS_GET_VERSION
:
517 debug(D_XMS
, "XMS: Get Version\n");
518 R_AX
= XMS_VERSION
; /* 3.0 */
519 R_BX
= XMS_REVISION
; /* internal revision 0 */
520 R_DX
= (HMA_a20
< 0) ? 0x0000 : 0x0001;
524 * XXX Not exact! Spec says compare size to a HMAMIN parameter and
525 * refuse HMA, if space is too small. With MSDOS 5.0 and higher DOS
526 * itself uses the HMA (DOS=HIGH), so I think we can safely ignore
529 case XMS_ALLOCATE_HIGH_MEMORY
:
530 debug(D_XMS
, "XMS: Allocate HMA\n");
533 R_BL
= XMS_HMA_ALREADY_USED
;
541 case XMS_FREE_HIGH_MEMORY
:
542 debug(D_XMS
, "XMS: Free HMA\n");
549 R_BL
= XMS_HMA_NOT_ALLOCATED
;
553 case XMS_GLOBAL_ENABLE_A20
:
554 debug(D_XMS
, "XMS: Global enable A20\n");
562 case XMS_GLOBAL_DISABLE_A20
:
563 debug(D_XMS
, "XMS: Global disable A20\n");
572 * This is an accumulating call. Every call increments HMA_a20.
573 * Caller must use LOCAL_DISBALE_A20 once for each previous call
574 * to LOCAL_ENABLE_A20.
576 case XMS_LOCAL_ENABLE_A20
:
577 debug(D_XMS
, "XMS: Local enable A20\n");
585 case XMS_LOCAL_DISABLE_A20
:
586 debug(D_XMS
, "XMS: Local disable A20\n");
597 * Disabled because DOS permanently scans this, to avoid endless output.
600 debug(D_XMS
, "XMS: Query A20\n"); */
602 R_AX
= (HMA_a20
> 0) ? 0x1 : 0x0;
606 case XMS_QUERY_FREE_EXTENDED_MEMORY
:
607 /* DOS MEM.EXE chokes, if the HMA is enabled and the reported
608 * free space includes the HMA. So we subtract 64kB from the
609 * space reported, if the HMA is enabled.
612 R_EAX
= R_EDX
= xms_free_mem
/ 1024;
614 R_EAX
= R_EDX
= (xms_free_mem
/ 1024) - 64;
616 if (xms_free_mem
== 0)
620 debug(D_XMS
, "XMS: Query free EMM: Returned %dkB\n", R_AX
);
623 case XMS_ALLOCATE_EXTENDED_MEMORY
:
629 debug(D_XMS
, "XMS: Allocate EMM: ");
630 /* Enough handles ? */
631 if ((hnum
= get_free_handle()) == 0) {
633 R_BL
= XMS_OUT_OF_HANDLES
;
634 debug(D_XMS
, " Out of handles\n");
638 req_siz
= R_DX
* 1024;
640 /* Enough memory ? */
641 if (req_siz
> xms_free_mem
) {
644 debug(D_XMS
, " No memory left\n");
648 xms_hand
[hindx
].size
= req_siz
;
649 xms_hand
[hindx
].num_locks
= 0;
652 * Not sure about that: Is it possible to reserve a handle
653 * but with no memory attached ? XMS specs are unclear on
654 * that point. Linux implementation does it this way.
657 /* This handle is reserved, but has size 0 and no address */
658 xms_hand
[hindx
].addr
= XMS_NULL_ALLOC
;
660 if ((mem
= malloc(req_siz
)) == NULL
)
661 fatal("XMS: Cannot malloc !");
662 xms_hand
[hindx
].addr
= (u_long
)mem
;
664 xms_free_mem
-= req_siz
;
665 xms_used_mem
+= req_siz
;
670 debug(D_XMS
, " Allocated %d kB, handle %d\n",
671 req_siz
/ 1024, hnum
);
675 case XMS_FREE_EXTENDED_MEMORY
:
679 debug(D_XMS
, "XMS: Free EMM: ");
681 if (hnum
> NUM_HANDLES
|| hnum
== 0) {
683 R_BL
= XMS_INVALID_HANDLE
;
684 debug(D_XMS
, " Invalid handle\n");
689 if (xms_hand
[hindx
].addr
== 0) {
691 R_BL
= XMS_INVALID_HANDLE
;
692 debug(D_XMS
, " Invalid handle\n");
694 } else if (xms_hand
[hindx
].num_locks
> 0) {
696 R_BL
= XMS_BLOCK_IS_LOCKED
;
697 debug(D_XMS
, " Is locked\n");
700 if (xms_hand
[hindx
].addr
!= XMS_NULL_ALLOC
) {
701 free((void *)xms_hand
[hindx
].addr
);
702 xms_free_mem
+= xms_hand
[hindx
].size
;
703 xms_used_mem
-= xms_hand
[hindx
].size
;
705 xms_hand
[hindx
].addr
= 0;
706 xms_hand
[hindx
].size
= 0;
707 xms_hand
[hindx
].num_locks
= 0;
709 debug(D_XMS
, " Success for handle %d\n", hnum
);
716 case XMS_MOVE_EXTENDED_MEMORY_BLOCK
:
718 u_long srcptr
, dstptr
;
719 u_long srcoffs
, dstoffs
;
721 const struct EMM
*eptr
;
724 debug(D_XMS
, "XMS: Move EMM block: ");
725 eptr
= (struct EMM
*)MAKEPTR(R_DS
, R_SI
);
727 /* Sanity check: Don't allow eptr pointing to emulator data */
728 if (((u_long
)eptr
+ sizeof(struct EMM
)) >= 0x100000) {
730 R_BL
= XMS_GENERAL_ERROR
;
731 debug(D_XMS
, " Offset to EMM structure wrong\n");
735 /* Validate handles and offsets */
737 if (eptr
->src_handle
> NUM_HANDLES
) {
739 R_BL
= XMS_INVALID_SOURCE_HANDLE
;
740 debug(D_XMS
, " Invalid handle\n");
743 if (eptr
->dst_handle
> NUM_HANDLES
) {
745 R_BL
= XMS_INVALID_DESTINATION_HANDLE
;
746 debug(D_XMS
, " Invalid handle\n");
749 srcidx
= eptr
->src_handle
- 1;
750 dstidx
= eptr
->dst_handle
- 1;
751 srcoffs
= eptr
->src_offset
;
752 dstoffs
= eptr
->dst_offset
;
754 /* Length must be even, see XMS spec */
757 R_BL
= XMS_INVALID_LENGTH
;
758 debug(D_XMS
, " Length not even\n");
761 if (eptr
->src_handle
!= 0) {
762 srcptr
= xms_hand
[srcidx
].addr
;
763 if (srcptr
== 0 || srcptr
== XMS_NULL_ALLOC
) {
765 R_BL
= XMS_INVALID_SOURCE_HANDLE
;
766 debug(D_XMS
, " Invalid source handle\n");
769 if ((srcoffs
+ n
) > xms_hand
[srcidx
].size
) {
771 R_BL
= XMS_INVALID_SOURCE_OFFSET
;
772 debug(D_XMS
, " Invalid source offset\n");
777 srcptr
= VECPTR(srcoffs
);
778 /* Sanity check: Don't allow srcptr pointing to
779 * emulator data above 1M
781 if ((srcptr
+ n
) >= 0x100000) {
783 R_BL
= XMS_GENERAL_ERROR
;
784 debug(D_XMS
, " Source segment invalid\n");
789 if (eptr
->dst_handle
!= 0) {
790 dstptr
= xms_hand
[dstidx
].addr
;
791 if (dstptr
== NULL
|| dstptr
== XMS_NULL_ALLOC
) {
793 R_BL
= XMS_INVALID_DESTINATION_HANDLE
;
794 debug(D_XMS
, " Invalid dest handle\n");
797 if ((dstoffs
+ n
) > xms_hand
[dstidx
].size
) {
799 R_BL
= XMS_INVALID_DESTINATION_OFFSET
;
800 debug(D_XMS
, " Invalid dest offset\n");
805 dstptr
= VECPTR(dstoffs
);
806 /* Sanity check: Don't allow dstptr pointing to
807 * emulator data above 1M
809 if ((dstptr
+ n
) >= 0x100000) {
811 R_BL
= XMS_GENERAL_ERROR
;
812 debug(D_XMS
, " Dest segment invalid\n");
816 memmove((void *)dstptr
, (void *)srcptr
, n
);
817 debug(D_XMS
, "Moved from %08lx to %08lx, %04x bytes\n",
824 case XMS_LOCK_EXTENDED_MEMORY_BLOCK
:
828 debug(D_XMS
, "XMS: Lock EMM block\n");
830 if (hnum
> NUM_HANDLES
|| hnum
== 0) {
832 R_BL
= XMS_INVALID_HANDLE
;
836 if (xms_hand
[hindx
].addr
== 0) {
838 R_BL
= XMS_INVALID_HANDLE
;
841 if (xms_hand
[hindx
].num_locks
== 255) {
843 R_BL
= XMS_BLOCK_LOCKCOUNT_OVERFLOW
;
846 xms_hand
[hindx
].num_locks
++;
849 * The 32 bit "physical" address is returned here. I hope
850 * the solution to simply return the linear address of the
851 * malloced area is good enough. Most DOS programs won't
852 * need this anyway. It could be important for future DPMI.
854 R_BX
= xms_hand
[hindx
].addr
& 0xffff;
855 R_DX
= (xms_hand
[hindx
].addr
& 0xffff0000) >> 16;
859 case XMS_UNLOCK_EXTENDED_MEMORY_BLOCK
:
863 debug(D_XMS
, "XMS: Unlock EMM block\n");
865 if (hnum
> NUM_HANDLES
|| hnum
== 0) {
867 R_BL
= XMS_INVALID_HANDLE
;
871 if (xms_hand
[hindx
].addr
== 0) {
873 R_BL
= XMS_INVALID_HANDLE
;
876 if (xms_hand
[hindx
].num_locks
== 0) {
878 R_BL
= XMS_BLOCK_NOT_LOCKED
;
881 xms_hand
[hindx
].num_locks
--;
887 case XMS_GET_EMB_HANDLE_INFORMATION
:
891 debug(D_XMS
, "XMS: Get handle information: DX=%04x\n", R_DX
);
893 if (hnum
> NUM_HANDLES
|| hnum
== 0) {
895 R_BL
= XMS_INVALID_HANDLE
;
899 if (xms_hand
[hindx
].addr
== 0) {
901 R_BL
= XMS_INVALID_HANDLE
;
905 R_BH
= xms_hand
[hindx
].num_locks
;
906 R_BL
= num_free_handle
;
907 R_DX
= xms_hand
[hindx
].size
/ 1024;
911 case XMS_RESIZE_EXTENDED_MEMORY_BLOCK
:
918 debug(D_XMS
, "XMS: Resize EMM block\n");
920 req_siz
= R_BX
* 1024;
921 if (hnum
> NUM_HANDLES
|| hnum
== 0) {
923 R_BL
= XMS_INVALID_HANDLE
;
927 if (xms_hand
[hindx
].addr
== 0) {
929 R_BL
= XMS_INVALID_HANDLE
;
932 if (xms_hand
[hindx
].num_locks
> 0) {
934 R_BL
= XMS_BLOCK_IS_LOCKED
;
937 sizediff
= req_siz
- xms_hand
[hindx
].size
;
940 if ((sizediff
+ xms_used_mem
) > xms_maxsize
) {
947 if (sizediff
== 0) { /* Never trust DOS programs */
953 xms_used_mem
+= sizediff
;
954 xms_free_mem
-= sizediff
;
956 if (xms_hand
[hindx
].addr
== XMS_NULL_ALLOC
) {
957 if ((mem
= malloc(req_siz
)) == NULL
)
958 fatal("XMS: Cannot malloc !");
959 xms_hand
[hindx
].addr
= (u_long
)mem
;
960 xms_hand
[hindx
].size
= req_siz
;
962 if ((mem
= realloc((void *)xms_hand
[hindx
].addr
,req_siz
))
964 fatal("XMS: Cannot realloc !");
965 xms_hand
[hindx
].addr
= (u_long
)mem
;
966 xms_hand
[hindx
].size
= req_siz
;
974 case XMS_ALLOCATE_UMB
:
979 debug(D_XMS
, "XMS: Allocate UMB: DX=%04x\n", R_DX
);
981 /* Some programs try to allocate 0 bytes. XMS spec says
982 * nothing about this. So the driver grants the request
983 * but it rounds up to the next paragraph size (1) and
984 * returns this amount of memory
989 /* First try to find an exact fit */
990 if ((bp
= find_exact_block(req_siz
)) != NULL
) {
991 /* Found ! Move block from free list to alloc list */
992 remove_block(&UMB_freelist
, bp
);
993 add_block(&UMB_alloclist
, bp
);
996 R_BX
= bp
->addr
>> 4;
999 /* Try to find a block big enough */
1000 bp
= find_block(req_siz
);
1003 R_BL
= XMS_NO_UMBS_AVAILABLE
;
1006 } else if (bp
->size
< req_siz
) {
1008 R_BL
= XMS_REQUESTED_UMB_TOO_BIG
;
1009 R_DX
= bp
->size
/ 16;
1013 /* Found a block large enough. Split it into the size
1014 * we need, rest remains on the free list. New block
1015 * goes to the alloc list
1017 newbp
= create_block(bp
->addr
, req_siz
);
1018 bp
->addr
+= req_siz
;
1019 bp
->size
-= req_siz
;
1020 add_block(&UMB_alloclist
, newbp
);
1022 R_BX
= newbp
->addr
>> 4;
1023 R_DX
= req_siz
/ 16;
1028 case XMS_DEALLOCATE_UMB
:
1033 debug(D_XMS
, "XMS: Deallocate UMB: DX=%04x\n", R_DX
);
1034 req_addr
= R_DX
<< 4;
1035 if ((blk
= find_allocated_block(req_addr
)) == NULL
) {
1037 R_BL
= XMS_INVALID_UMB_SEGMENT
;
1039 /* Move the block from the alloc list to the free list
1040 * and try to do garbage collection
1042 remove_block(&UMB_alloclist
, blk
);
1043 add_block(&UMB_freelist
, blk
);
1053 * If the option DOS=UMB is enabled, DOS grabs the entire UMB
1054 * at boot time. In any other case this is used to load resident
1055 * utilities. I don't think this function is neccesary here.
1057 case XMS_REALLOCATE_UMB
:
1058 debug(D_XMS
, "XMS: Reallocate UMB\n");
1060 R_BL
= XMS_NOT_IMPLEMENTED
;
1063 /* Some test programs use this call */
1064 case XMS_QUERY_FREE_EXTENDED_MEMORY_LARGE
:
1065 /* DOS MEM.EXE chokes, if the HMA is enabled and the reported
1066 * free space includes the HMA. So we subtract 64kB from the
1067 * space reported, if the HMA is enabled.
1070 R_EAX
= R_EDX
= xms_free_mem
/ 1024;
1072 R_EAX
= R_EDX
= (xms_free_mem
/ 1024) - 64;
1073 /* ECX should return the highest address of any memory block
1074 * We return 1MB + size of extended memory
1076 R_ECX
= 1024 * 1024 + xms_maxsize
-1;
1077 if (xms_free_mem
== 0)
1081 debug(D_XMS
, "XMS: Query free EMM(large): Returned %dkB\n", R_AX
);
1084 /* These are the same as the above functions, but they use 32 bit
1085 * registers (i.e. EDX instead of DX). This is for allocations of
1086 * more than 64MB. I think this will hardly be used in the emulator
1087 * It seems to work without them, but the functions are in the XMS 3.0
1088 * spec. If something breaks because they are not here, I can implement
1091 case XMS_ALLOCATE_EXTENDED_MEMORY_LARGE
:
1092 case XMS_FREE_EXTENDED_MEMORY_LARGE
:
1093 debug(D_XMS
, "XMS: %02x function called, not implemented\n", R_AH
);
1095 R_BL
= XMS_NOT_IMPLEMENTED
;
1099 debug(D_ALWAYS
, "XMS: Unimplemented function %02x, \n", R_AH
);
1101 R_BL
= XMS_NOT_IMPLEMENTED
;