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 20: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;