1 /*******************************************************************************
3 * (C) Copyright Microsoft Corp. 1986
5 * TITLE: CEMM.EXE - COMPAQ Expanded Memory Manager 386 Driver
6 * EMMLIB.LIB - Expanded Memory Manager Library
8 * MODULE: EMMFUNCT.C - EMM functions code.
14 *******************************************************************************
16 * Date Version Description
17 * -------- -------- -------------------------------------------------------
18 * 06/14/86 Changed status return to return only AH. And added
19 * PFlag to decide on selector versus segment on long
20 * address generation (SBP).
21 * 06/14/86 Moved save_current_map and restore_map to ASM (SBP).
22 * 06/15/86 Changed NULL_HANDLE to 0x0FFF (see emm.h) (SBP).
23 * 06/21/86 Moved MapHandlePage to ASM (SBP).
24 * Handle # passed to client has high byte = NOT (low byte)
25 * as in the Above Board (SBP).
26 * Valid_Handle -> ASM (SBP).
27 * 06/23/86 Make_Addr removed. source_addr and dest_addr added(SBP).
28 * 06/25/86 0.02 Dealloc checks for save area in use (SBP).
29 * 06/28/86 0.02 Name change from CEMM386 to CEMM (SBP).
30 * 06/29/86 0.02 Return after NOT_ENOUGH_FREE_MEM error in Allocate(SBP).
31 * 07/06/86 0.04 Changed _emm_page,_emm_free, & _pft386 to ptrs (SBP).
32 * 07/06/86 0.04 moved SavePageMap and RestorePageMap to .ASM (SBP).
33 * 07/08/86 0.04 moved GetSetPageMap to .ASM (SBP).
34 * 07/09/86 0.04 removed code which places handle # in _pft386
36 * 07/09/86 0.05 fixed bug in deallocate (SBP).
37 * 05/09/88 0.10 modified for MEMM, modifications are indicated in
38 * individual routines (ISP).
40 *******************************************************************************
41 * FUNCTIONAL DESCRIPTION
43 * Paged EMM Driver for the iAPX 386.
45 * The basic concept is to use the 386's page tables to emulate
46 * the functions of an EMM board. There are several constraints
47 * that are a result of poor planning on the LIM specifiers part.
48 * - maximum of 64K instantaneously mapped. this will
49 * be faithfully emulated in this design
50 * - maximum of 8Mb of extended memory can be used.
51 * The actual reason for this is because each board
52 * can only support 128 16Kb pages and the limit of
53 * 4 Aboveboards implies 512 pages maximum. This will
54 * not be adhered to since the limit in unnecessary.
56 * The memory managed by this scheme can be discontiguous but
57 * a 16Kb EMM page can not be composed of discontiguous pieces.
58 * This is not necessary but does simplify the job of managing
61 * The LIM specification implies the existence of a partitioning
62 * of extended memory into `boards'. While this concept is not
63 * meaningfull in the 386 environment, a page to logical board
64 * mapping is provided to support some of the LIM specified
66 * pages 0 to 127 map to board 0
67 * pages 128 to 255 map to board 1
69 * The pages in this case are logical pages and pages on the
70 * same logical board may actually reside on different physical
71 * boards. (In fact, if contiguous memory, a page could actually
72 * be split across 2 different boards.)
74 * A brief note on parameters:
75 * all parameters to EMM functions are passed in registers.
76 * on entry to the EMM dispatch code, the registers are pushed
77 * onto the stack. In order to access them, they are pointed
78 * to by a global variable (regp). Defines are used to name
79 * these parameters and make the code more readable.
83 * 16 bit value that references a block of
84 * allocated memory. Internally, it is an index into a handle
85 * table. Externally, the high byte is the NOT of the low byte
86 * for compatibility with the Above Board EMM driver.
89 * a 16Kb contiguous portion of memory, aligned on a
90 * 16Kb boundary in 8086 address space. In physical
91 * address space it can be aligned on a 4Kb boundary.
94 * 386 page. 4Kb in size and 4Kb aligned in physical
98 * An iAPX 86 style 32 bit pointer. It consists of
99 * a 16 bit offset in the low word and a base
100 * address in the high word.
103 * an EMM page allocated to a handle via allocatepages
104 * function. each such page has a logical page number.
106 * physical page frame
107 * the location in physical 8086 space that an EMM page
108 * gets mapped to. there are 4 such locations. they are
109 * contiguous starting at page_frame_base
112 * this is the physical page in 80386 physical
113 * address space. the address of a 386 page frame
114 * is the value placed in a 80386 page table entry's
116 ******************************************************************************/
118 /******************************************************************************
120 ******************************************************************************/
124 /******************************************************************************
125 EXTERNAL DATA STRUCTURES
126 ******************************************************************************/
130 * this is an array of port addresses, 4 ports per
131 * emulated board. Each emulated board has up to
132 * 128 16Kb EMM pages assigned. The size of the table,
133 * the number of ports used, is map_size
134 * map_size = (<number of 386 pages>/(128*4))*4
136 /*extern unsigned short iomap[]; */
137 /*extern char map_size;*/
141 * This flags is set whenever the user is given the I/O map
143 /*extern char map_known; */
147 * this is a map of the linear addresses of the
148 * 4 16Kb EMM `physical' windows that the user
149 * accesses the EMM pages through. The entries
150 * of this array are far pointers into the page table.
151 * Thus, the address defined by page_frame_base[0]
152 * is the address of the long word that is the page
153 * table entry for the first EMM window. The reason for
154 * this obscurity is in speed of mapping -- it is used
155 * to directly obtain access to the entry to be programmed
157 extern unsigned long page_frame_base[];
161 * This is an array of structures that save the
162 * current mapping state. Size is dynamically determined.
164 extern struct save_map save_map[];
168 * This is an array of handle pointers.
169 * page_index of zero means free
171 extern struct handle_ptr handle_table[];
172 extern Handle_Name Handle_Name_Table[];
173 extern unsigned short handle_table_size; /* number of entries */
174 extern unsigned short handle_count; /* active handle count */
178 * this array contains lists of indexes into the 386
179 * Page Frame Addresses (pft386). Each list is pointed to
180 * by a handle table entry and is sequential/contiguous.
181 * This is so that maphandlepage doesn't have to scan
182 * a list for the specified entry.
184 extern unsigned short *emm_page; /* ptr to _emm_page array */
185 extern unsigned short free_count; /* current free count */
186 extern unsigned short total_pages; /* number being managed */
187 extern unsigned short emmpt_start; /* next free entry in table */
191 * this array is a stack of available page table entries.
192 * each entry is an index into pft386[].
194 extern unsigned short *emm_free; /* ptr to _emm_free array */
195 extern unsigned short free_top;
199 * This array contains addresses of physical page frames
200 * for 386 pages. A page is refered to by an index into
203 extern union pft386 *pft386; /* ptr to page frame table array */
207 * Current status of `HW'. The way this is handled is that
208 * when returning status to caller, normal status is reported
209 * via EMMstatus being moved into AX. Persistant errors
210 * (such as internal datastructure inconsistancies, etc) are
211 * placed in `EMMstatus' as HW failures. All other errors are
212 * transient in nature (out of memory, handles, ...) and are
213 * thus reported by directly setting AX. The EMMstatus variable
214 * is provided for expansion and is not currently being
215 * set to any other value.
217 extern unsigned short EMMstatus;
222 /*unsigned null_count = 0; /* number of attempts to map null pages */
225 /******************************************************************************
227 ******************************************************************************/
228 extern struct handle_ptr *valid_handle(); /* validate handle */
229 extern unsigned far *source_addr(); /* get DS:SI far ptr */
230 extern unsigned far *dest_addr(); /* get ES:DI far ptr */
231 /*extern unsigned AutoUpdate(); /* update auto mode */
232 extern unsigned wcopy();
233 extern unsigned copyout();
234 extern void reallocate();
237 /******************************************************************************
239 ******************************************************************************/
243 * returns: number of available emm pages
245 * 06/09/88 PC added the function
255 * num --- number of pages desired
256 * pto --- offset into emm_page array where the pages got are to be copied
258 * emm_page[] index (pointer to list of allocated pages)
259 * NULL_PAGE means failure.
261 * 05/06/88 ISP Updated for MEMM removed handle as a parameter
265 register unsigned num;
266 register unsigned pto;
268 register unsigned pg;
272 return(NULL_PAGE); /* not enough memory */
273 free_count -= num; /* adjust free count */
275 /* emmpt_start += num; */ /* new offset of avail area */
278 * copy num elements from the emm_free array
279 * to the emm_page table array and update the
280 * corresponding page frame table entry (with a
281 * handle back pointer)
283 wcopy(emm_free+free_top, emm_page+pg, num);
291 * hp --- handle whose pages should be deallocated
293 * Free the pages associated with the handle, but don't free the handle
295 * 05/09/88 ISP Pulled out from the deallocate page routine
299 register struct handle_ptr *hp;
301 register unsigned next;
305 if (hp->page_count == 0) return ;
307 * copy freed pages to top of free stack
309 free_top -= hp->page_count; /* free_top points to new top */
310 free_count += hp->page_count; /* bookkeeping */
311 wcopy(emm_page+hp->page_index, /* addr of first of list */
312 emm_free+free_top, /* addr of free space */
313 hp->page_count); /* # of pages to be freed */
316 * now, the hard part. squeeze the newly created hole
317 * out of the emm_page array. this also requires updating the
318 * handle_table entry via the backlink in the pft386 array.
320 * do this in two phases:
321 * - copy the lower portion up to squeeze the hole out
322 * - readjust the handle table to point to the new
323 * location of the head element
326 next = hp->page_index + hp->page_count;
327 if(next == emmpt_start ) /* any lists below? */
330 emmpt_start -= hp->page_count;
334 new_start = emmpt_start - hp->page_count;
335 wcopy(emm_page+next, /* 1st of rest of list */
336 emm_page+hp->page_index,/* addr of freed area */
337 emmpt_start-next); /* size of block of pages */
340 * loop through the handle table entries, fixing up
341 * their page index fields
343 h_size = hp->page_count;
344 hp->page_count = 0; /* not really necessary */
345 for(hp=handle_table;hp < &handle_table[handle_table_size];hp++)
346 if((hp->page_index != NULL_PAGE) &&
347 (hp->page_index >= next) )
348 hp->page_index -= h_size;
349 emmpt_start = new_start; /* fix emmpt_start */
356 * return current status of EMM subsystem
357 * (which, due to superior design is always just fine)
359 * 05/06/88 ISP No Update needed for MEMM
363 setAH((unsigned char)EMMstatus); /* if we got here, we're OK */
368 * get page frame address
371 * return the address of where the pages get mapped
374 * 05/06/88 ISP Updated this routine from WIN386 sources.
376 GetPageFrameAddress()
378 extern unsigned short PF_Base;
379 extern unsigned short page_frame_pages;
382 * return the 8086 style base address of
383 * the page frame base.
385 if ( page_frame_pages < 4 ) {
386 setAH(EMM_HW_MALFUNCTION); /* GET LOST!!! */
387 if ( PF_Base == 0xFFFF )
388 setBX(0xB000); /* In case error is ignored */
390 setBX(PF_Base); /* stunted page frame */
394 setAH((unsigned char)EMMstatus); /* OK return */
399 * get unallocated page count
403 * bx -- count of free pages
404 * dx -- total number of pages (free and allocated)
406 * 05/06/88 ISP No update needed for MEMM
408 GetUnallocatedPageCount()
412 setAH((unsigned char)EMMstatus);
418 * n_pages (bx) -- allocation size request
420 * allocates the requested number of pages, creates
421 * a handle table entry and returns a handle to the
423 * calls AllocateRawPages
425 * 05/09/88 ISP updated for MEMM. Only handle value returned, not handle
426 * value with high byte as not of handle value. call to get
427 * pages also updated to remove handle parameter.
431 #define n_pages ((unsigned)regp->hregs.x.rbx)
432 if(handle_count == handle_table_size){ /* no more handles? */
433 setAH(NO_MORE_HANDLES); /* nope */
449 * n_pages (bx) -- allocation size request
451 * allocates the requested number of raw pages,
452 * allocating 0 page is Okay
453 * calls allocated pages if non-zero.
455 * CREATED : 08/08/88 PLC
459 #define n_pages ((unsigned)regp->hregs.x.rbx)
460 register unsigned handle; /* handle table index */
461 register struct handle_ptr *hp;
463 if(handle_count == handle_table_size){ /* no more handles? */
464 setAH(NO_MORE_HANDLES); /* nope */
468 if(n_pages > total_pages) {
469 setAH(NOT_ENOUGH_EXT_MEM);
474 * loop through table to
475 * find available handle (page_index = NULL_PAGE)
477 hp = (struct handle_ptr *)handle_table;
478 for(handle=0;handle<handle_table_size;handle++,hp++)
479 if(hp->page_index == NULL_PAGE)
480 break; /* found a free one */
482 * try and allocate pages
484 if((hp->page_index=get_pages(n_pages,emmpt_start)) != NULL_PAGE) {
485 emmpt_start += n_pages;
486 setAH((unsigned char)EMMstatus); /* got them! */
489 setAH(NOT_ENOUGH_FREE_MEM); /* out of pages */
493 hp->page_count=n_pages; /* set count */
497 /* AutoUpdate(); /* update status of Auto mode */
507 * free up the pages and handle table entry associated
510 * 05/09/88 ISP Updated for MEMM. Pulled out free_page routine and
511 * added support for handle name blanking.
515 #define handle ((unsigned)regp->hregs.x.rdx)
516 register struct handle_ptr *hp;
517 struct save_map *smp; /* save map table ptr */
518 long *Name ; /* points to handle name entry to clear */
520 if ( handle == 0 ) { /* Special handle, don't release */
521 int savbx = regp->hregs.x.rbx;
522 regp->hregs.x.rbx = 0;
524 regp->hregs.x.rbx = savbx;
528 if((hp=valid_handle(handle)) == NULL_HANDLE)
529 return; /* invalid handle, error code set */
531 * check for save area in use for this handle
533 if( save_map[ (handle & 0x00FF) ].s_handle != (unsigned)NULL_HANDLE )
535 setAH(SAVED_PAGE_DEALLOC);
539 free_pages(hp); /*free the pages associated with handle*/
540 hp->page_index = NULL_PAGE; /*and then free the handle*/
541 hp->page_count = 0; /*bookkeeping*/
542 Name = (long *)Handle_Name_Table[handle & 0xFF];
543 *(Name+1) = *(Name) = 0L; /* zero the eight byte name */
544 handle_count--; /* one less active handle */
546 /* AutoUpdate(); /* update status of Auto mode */
547 setAH((unsigned char)EMMstatus); /* done */
556 * returns the version number of the emm driver
558 * 05/06/88 ISP No update needed for MEMM
562 setAX( (EMMstatus<<8) | EMM_VERSION );
566 * Get EMM handle count
569 * return the number of active EMM handles
571 * 05/06/88 ISP No update needed for MEMM
576 setAH((unsigned char)EMMstatus);
580 * Get EMM handle pages
584 * return the number of pages allocated to specified handle in BX
586 * 05/09/88 ISP No update needed for MEMM
590 #define handle ((unsigned)regp->hregs.x.rdx)
591 register struct handle_ptr *hp;
593 if((hp=valid_handle(handle))==NULL_HANDLE) /*valid handle? */
595 setBX(hp->page_count);
596 setAH((unsigned char)EMMstatus);
600 * Get All EMM Handle Pages
604 * fill out array of handle/size pairs
606 * 05/09/88 ISP Updated for MEMM (just removed upper byte of handle)
608 GetAllEMMHandlePages()
611 register struct handle_ptr *hp;
612 register unsigned h_index;
615 * scan handle table and for each valid entry,
616 * copy handle and size to user array
621 for(h_index=0;h_index<handle_table_size;h_index++)
623 /* scan table for entries */
624 if(hp->page_index != NULL_PAGE) /* valid entry? */
626 *u_ptr++ = h_index; /* handle */
627 *u_ptr++ = hp->page_count; /*# of pgs for handle*/
629 hp++; /* next entry */
631 setBX(handle_count); /* bx <-- handle count */
632 setAH((unsigned char)EMMstatus);
636 * Get Page Mapping Register I/O Port Array
640 * 05/09/88 ISP Function not supported
642 GetPageMappingRegisterIOArray()
645 setAH(INVALID_FUNCTION);
649 * Get Logical to Physical Page Translation Array
651 * es:di -- pointer to user array
652 * dx ----- EMM handle
654 * 05/09/88 ISP Function not supported
656 GetLogicalToPhysicalPageTrans()
658 setAH(INVALID_FUNCTION);