Insane Reality issue #8 - (c)opyright 1996 Immortal Riot/Genesis - REALITY.008 Article: XMS Memory Author: Rajaat [IRG] % Using XMS Memory by Rajaat [IRG] % ____________________________________ Heres another cool article from Rajaat, one of the more active and senior members of IRG. Read it, and learn how your virus can use XMS memory. We here at IRG love you. Since this doc deals with the XMS 3 Specifications, we have included the official XMS 3.0 Specifications after this text. Further more, since the XMS 3.0 Specifications deal mainly with HIMEM.SYS we have included the entire and original HIMEM.SYS source after that. - _Sepultura_ ============================================================================= % XMS Memory % - by Rajaat Most advanced viruses are memory resident nowadays. They allocate a piece of memory and put their code there. This usually shows the user that there is less conventional memory left than usual, and does seem suspicious. There are viruses that cover the memory loss by using MCB stealth, but these techniques can be quite troublesome. Another way to get the memory you need is by using extended memory. % The advantages % Using extended memory leaves you the possibility to allocate less (or no) space at all in conventional memory. If you use a little piece of conventional memory in a way that it does not change the amount of free available memory, it might not raise questions of users that know how much memory they have left for their DOS applications. Another good point is that there are no virus scanners that check extended memory, because it doesn't contain directly executable code. % The disadvantages % You read it above, code in XMS memory can't be executed. In order to use the code in XMS, you have to swap it back to conventional memory and then execute it. This means you have to allocate some temporary memory to execute the code. Another drawback is the fact that it needs a loader in conventional memory to swap code from XMS. A minor consideration is the fact that this requires at least a 80286 processor. % Implementation % If you want to use XMS memory, you must first check for the presence of a XMS driver. This code fragment shows you how to check for HIMEM.SYS, the device driver that allows you to use XMS: -------------------- code fragment mov ax,4300h ; function : install check int 2fh cmp al,80h ; al = 80h if installed jnz no_himem_installed ; himem.sys is active in memory no_himem_installed: -------------------- end of code fragment The next step is to get the Application Programmers Interface (API) of the XMS driver. This is done fairly easy, and works almost like requesting a pointer of a software interrupt. -------------------- code fragment mov ax,4310h ; function : query XMS API int 2fh -------------------- end of code fragment Now ES:BX holds the entrypoint of the HIMEM.SYS API. Store this doubleword for later use, all XMS related functions are must be called through this pointer. Now you can choose what you want to do. There is also a possibility to use the High Memory Area (HMA). This is the first 64 kilobyte block located from the 1st megabyte. This HMA block is often used to load the command interpreter in, and some viruses (most of them written by Q) are using it. Discussion of this HMA block in this text is out of scope. % Allocating XMS memory % Before moving anything to XMS memory, you first have to allocate a block of memory. The nice method requires that you first query the free memory, but since a virus has to be short, you can better ommit this check and directly allocate the required memory. If this call fails for some reason, you will get an error anyway. To understand how you have to work with XMS you have to see it as a file. You ask for a certain amount of XMS memory and after issueing the call you'll get the handle, which you can use to access the memory. Or, if the call fails, you will get an error. -------------------- code fragment mov ah,09h ; function : allocate memory mov dx,1 ; memory size (1 kilobyte) call dword ptr xms_api_ptr or ax,ax jz error ; handle for memory block in dx error: ; error code in bl -------------------- end of code fragment If you look in the Ralph Brown interrupt list you will see that this call would need the amount of kilobytes you want to request. This amount is given in kilobytes. MEM.EXE of dos will report a XMS memory loss round up to 16kb, so if you allocated 1 kilobyte, MEM.EXE shows 16kb XMS memory decrease. The MEM.EXE that belongs to Nemesis antivirus monitor will correctly show that 1kb is allocated. Now that you have allocated memory, you want to use it. Accessing the memory is done by using an EMM table. This is a self defined table you hand over the XMS API. The API will use the table for it's read and write requests. The structure of an EMM table is thus: EMM_Table struct Stream_Length dd ? ; amount of bytes to move (even number) Src_Handle dw ? ; source handle or real-mode address if 0 Src_Real dd ? ; source real-mode address Dst_Handle dw ? ; destination handle or real-mode address if 0 Dst_Real dd ? ; destination real-mode address ends EMM_Table This table needs some explanation. The table is used for both ways of memory movement, from and to XMS. Stream_Length must be the amount of bytes you want to transfer. This number must be even, otherwise your system may crash. Src_Handle is the XMS handle you need if you want to transfer from XMS. If this word value is zero, a handle will not be used and Src_Real will be used as source address. It will then move bytes from conventional memory. Dst_Handle is the XMS you need if you want to transfer to XMS. This is the value returned in DX by function 09h of the XMS API. If this value is 0, a transfer to conventional memory is used. The real-mode address then is specified in Dst_Real. To finally transfer the memory you have to point DS:SI to the EMM table and call the XMS API with function 0bh. Here is an example that transfers the VGA screen display to extended memory. Remember that this code fragment doesn't work alone, and that you have to fill the tables yourself. -------------------- code fragment push cs pop ds mov ah,0bh ; function : move memory lea si,emm_table call dword ptr cs:[xms_api] ; call XMS API ; error checks and other things xms_api dd xxxxyyyy ; far pointer to XMS API emm_table: dd 25*160 ; screen is 25*80*2 (char+attrib) dw 0 dd 0b8000000h ; transfer from B800:0000 dw 1 ; destination handle 1 ; obtained with function 09h dd 0 -------------------- end of code fragment To read the memory back is as simple, you just change the source handle to the handle of the XMS page you are using and you set the destination handle to 0. The last thing you have to change is the doubleword of the destination real memory. Point it to the memory segment and offset where you want to transfer the code to. Another possibility is to transfer from a XMS handle to a XMS handle, just by filling the both the source and destination handle. Another trick that is quite neat is that you can transfer memory blocks in conventional memory using this method. By zeroing both the source and destination handle, and filling the Src_Real and Dst_Real you can transfer memory chunks in conventional memory. This way you can transfer your virus into another memory region without setting any heuristic flags defined in TBAV and Suspicious. To see how you can implement XMS functionality in your virus, look at the source code of Hamanu virus somewhere else in this issue. ;============================================================================ ;=[BEGIN XMS3SPEC.TXT]======================================================= This file is located on x2ftp.oulu.fi _________________________________________________________________ eXtended Memory Specification (XMS), ver 3.0 January 1991 Copyright (c) 1988, Microsoft Corporation, Lotus Development Corporation, Intel Corporation, and AST Research, Inc. Microsoft Corporation Box 97017 One Microsoft Way Redmond, WA 98073 LOTUS (r) INTEL (r) MICROSOFT (r) AST (r) Research This specification was jointly developed by Microsoft Corporation, Lotus Development Corporation, Intel Corporation,and AST Research, Inc. Although it has been released into the public domain and is not confidential or proprietary, the specification is still the copyright and property of Microsoft Corporation, Lotus Development Corporation, Intel Corporation, and AST Research, Inc. Disclaimer of Warranty MICROSOFT CORPORATION, LOTUS DEVELOPMENT CORPORATION, INTEL CORPORATION, AND AST RESEARCH, INC., EXCLUDE ANY AND ALL IMPLIED WARRANTIES, INCLUDING WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. NEITHER MICROSOFT NOR LOTUS NOR INTEL NOR AST RESEARCH MAKE ANY WARRANTY OF REPRESENTATION, EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS SPECIFICATION, ITS QUALITY, PERFORMANCE, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. NEITHER MICROSOFT NOR LOTUS NOR INTEL NOR AST RESEARCH SHALL HAVE ANY LIABILITY FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RESULTING FROM THE USE OR MODIFICATION OF THIS SPECIFICATION. This specification uses the following trademarks: Intel is a registered trademark of Intel Corporation, Microsoft is a registered trademark of Microsoft Corporation, Lotus is a registered trademark of Lotus Development Corporation, and AST is a registered trademark of AST Research, Inc. Extended Memory Specification The purpose of this document is to define the Extended Memory Specification (XMS) version 3.00 for MS-DOS. XMS allows DOS programs to utilize additional memory found in Intel's 80286 and 80386 based machines in a consistent, machine independent manner. With some restrictions, XMS adds almost 64K to the 640K which DOS programs can access directly. Depending on available hardware, XMS may provide even more memory to DOS programs. XMS also provides DOS programs with a standard method of storing data in extended memory. To be considered fully XMS 3.0 compliant, all calls except those associated with UMB support must be implemented. UMB functions 10h, 11h and 12h are optional for XMS 3.0 and may return the Function Not Implemented error code, 80h. DEFINITIONS: ------------ Extended Memory: Memory in 80286 and 80386 based machines which is located above the 1MB address boundary. High Memory Area (HMA): The first 64K of extended memory. The High Memory Area is unique because code can be executed in it while in real mode. The HMA officially starts at FFFF:10h and ends at FFFF:FFFFh making it 64K-16 bytes in length. Upper Memory Blocks (UMBs): Blocks of memory available on some 80x86 based machines which are located between DOS's 640K limit and the 1MB address boundary. The number, size, and location of these blocks vary widely depending upon the types of hardware adapter cards installed in the machine. Extended Memory Blocks (EMBs): Blocks of extended memory located above the HMA which can only be used for data storage. A20 Line: The 21st address line of 80x86 CPUs. Enabling the A20 line allows access to the HMA. XMM: An Extended Memory Manager. A DOS device driver which implements XMS. XMMs are machine specific but allow programs to use extended memory in a machine-independent manner. HIMEM.SYS: The Extended Memory Manager currently being distributed by Microsoft. Helpful Diagram: | | Top of Memory | | | | | /\ | | /||\ | | || | | || | | | | | | | | Possible Extended Memory Block | | | | | | | | || | | || | | \||/ | | \/ | | | | | | Other EMBs could exist above 1088K (1MB+64K) | | | | | | | 1088K | | | | | The High Memory Area | | | | | | | 1024K or 1MB | | | /\ | | /||\ | | || | | || | | | | | | Possible Upper Memory Block | | | | || | | || | | \||/ | | \/ | | | | Other UMBs could exist between 640K and 1MB | | | | | 640K | | | | | | | Conventional or DOS Memory | | | | | | | | | | | + + 0K DRIVER INSTALLATION: -------------------- An XMS driver is installed by including a DEVICE= statement in the machine's CONFIG.SYS file. It must be installed prior to any other devices or TSRs which use it. An optional parameter after the driver's name (suggested name "/HMAMIN=") indicates the minimum amount of space in the HMA a program can use. Programs which use less than the minimum will not be placed in the HMA. See "Prioritizing HMA Usage" below for more information. A second optional parameter (suggested name "/NUMHANDLES=") allows users to specify the maximum number of extended memory blocks which may be allocated at any time. NOTE: XMS requires DOS 3.00 or above. THE PROGRAMMING API: -------------------- The XMS API Functions are accessed via the XMS driver's Control Function. The address of the Control Function is determined via INT 2Fh. First, a program should determine if an XMS driver is installed. Next, it should retrieve the address of the driver's Control Function. It can then use any of the available XMS functions. The functions are divided into several groups: 1. Driver Information Functions (0h) 2. HMA Management Functions (1h-2h) 3. A20 Management Functions (3h-7h) 4. Extended Memory Management Functions (8h-Fh) 5. Upper Memory Management Functions (10h-11h) DETERMINING IF AN XMS DRIVER IS INSTALLED: ------------------------------------------ The recommended way of determining if an XMS driver is installed is to set AH=43h and AL=00h and then execute INT 2Fh. If an XMS driver is available, 80h will be returned in AL. Example: ; Is an XMS driver installed? mov ax,4300h int 2Fh cmp al,80h jne NoXMSDriver CALLING THE API FUNCTIONS: -------------------------- Programs can execute INT 2Fh with AH=43h and AL=10h to obtain the address of the driver's control function. The address is returned in ES:BX. This function is called to access all of the XMS functions. It should be called with AH set to the number of the API function requested. The API function will put a success code of 0001h or 0000h in AX. If the function succeeded (AX=0001h), additional information may be passed back in BX and DX. If the function failed (AX=0000h), an error code may be returned in BL. Valid error codes have their high bit set. Developers should keep in mind that some of the XMS API functions may not be implemented by all drivers and will return failure in all cases. Example: ; Get the address of the driver's control function mov ax,4310h int 2Fh mov word ptr [XMSControl],bx ; XMSControl is a DWORD mov word ptr [XMSControl+2],es ; Get the XMS driver's version number mov ah,00h call [XMSControl] ; Get XMS Version Number NOTE: Programs should make sure that at least 256 bytes of stack space is available before calling XMS API functions. API FUNCTION DESCRIPTIONS: -------------------------- The following XMS API functions are available: 0h) Get XMS Version Number 1h) Request High Memory Area 2h) Release High Memory Area 3h) Global Enable A20 4h) Global Disable A20 5h) Local Enable A20 6h) Local Disable A20 7h) Query A20 8h) Query Free Extended Memory 9h) Allocate Extended Memory Block Ah) Free Extended Memory Block Bh) Move Extended Memory Block Ch) Lock Extended Memory Block Dh) Unlock Extended Memory Block Eh) Get Handle Information Fh) Reallocate Extended Memory Block 10h) Request Upper Memory Block 11h) Release Upper Memory Block 12h) Realloc Upper Memory Block 88h) Query any Free Extended Memory 89h) Allocate any Extended Memory Block 8Eh) Get Extended EMB Handle 8Fh) Realloc any Extended Memory Each is described below. Get XMS Version Number (Function 00h): -------------------------------------- ARGS: AH = 00h RETS: AX = XMS version number BX = Driver internal revision number DX = 0001h if the HMA exists, 0000h otherwise ERRS: None This function returns with AX equal to a 16-bit BCD number representing the revision of the DOS Extended Memory Specification which the driver implements (e.g. AX=0235h would mean that the driver implemented XMS version 2.35). BX is set equal to the driver's internal revision number mainly for debugging purposes. DX indicates the existence of the HMA (not its availability) and is intended mainly for installation programs. NOTE: This document defines version 3.00 of the specification. Request High Memory Area (Function 01h): ---------------------------------------- ARGS: AH = 01h If the caller is a TSR or device driver, DX = Space needed in the HMA by the caller in bytes If the caller is an application program, DX = FFFFh RETS: AX = 0001h if the HMA is assigned to the caller, 0000h otherwise ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = 90h if the HMA does not exist BL = 91h if the HMA is already in use BL = 92h if DX is less than the /HMAMIN= parameter This function attempts to reserve the 64K-16 byte high memory area for the caller. If the HMA is currently unused, the caller's size parameter is compared to the /HMAMIN= parameter on the driver's command line. If the value passed by the caller is greater than or equal to the amount specified by the driver's parameter, the request succeeds. This provides the ability to ensure that programs which use the HMA efficiently have priority over those which do not. NOTE: See the sections "Prioritizing HMA Usage" and "High Memory Area Restrictions" below for more information. Release High Memory Area (Function 02h): ---------------------------------------- ARGS: AH = 02h RETS: AX = 0001h if the HMA is successfully released, 0000h otherwise ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = 90h if the HMA does not exist BL = 93h if the HMA was not allocated This function releases the high memory area and allows other programs to use it. Programs which allocate the HMA must release it before exiting. When the HMA has been released, any code or data stored in it becomes invalid and should not be accessed. Global Enable A20 (Function 03h): --------------------------------- ARGS: AH = 03h RETS: AX = 0001h if the A20 line is enabled, 0000h otherwise ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = 82h if an A20 error occurs This function attempts to enable the A20 line. It should only be used by programs which have control of the HMA. The A20 line should be turned off via Function 04h (Global Disable A20) before a program releases control of the system. NOTE: On many machines, toggling the A20 line is a relatively slow operation. Global Disable A20 (Function 04h): ---------------------------------- ARGS: AH = 04h RETS: AX = 0001h if the A20 line is disabled, 0000h otherwise ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = 82h if an A20 error occurs BL = 94h if the A20 line is still enabled This function attempts to disable the A20 line. It should only be used by programs which have control of the HMA. The A20 line should be disabled before a program releases control of the system. NOTE: On many machines, toggling the A20 line is a relatively slow operation. Local Enable A20 (Function 05h): -------------------------------- ARGS: AH = 05h RETS: AX = 0001h if the A20 line is enabled, 0000h otherwise ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = 82h if an A20 error occurs This function attempts to enable the A20 line. It should only be used by programs which need direct access to extended memory. Programs which use this function should call Function 06h (Local Disable A20) before releasing control of the system. NOTE: On many machines, toggling the A20 line is a relatively slow operation. Local Disable A20 (Function 06h): --------------------------------- ARGS: AH = 06h RETS: AX = 0001h if the function succeeds, 0000h otherwise ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = 82h if an A20 error occurs BL = 94h if the A20 line is still enabled This function cancels a previous call to Function 05h (Local Enable A20). It should only be used by programs which need direct access to extended memory. Previous calls to Function 05h must be canceled before releasing control of the system. NOTE: On many machines, toggling the A20 line is a relatively slow operation. Query A20 (Function 07h): ------------------------- ARGS: AH = 07h RETS: AX = 0001h if the A20 line is physically enabled, 0000h otherwise ERRS: BL = 00h if the function succeeds BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected This function checks to see if the A20 line is physically enabled. It does this in a hardware independent manner by seeing if "memory wrap" occurs. Query Free Extended Memory (Function 08h): ------------------------------------------ ARGS: AH = 08h RETS: AX = Size of the largest free extended memory block in K-bytes DX = Total amount of free extended memory in K-bytes ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = A0h if all extended memory is allocated This function returns the size of the largest available extended memory block in the system. NOTE: The 64K HMA is not included in the returned value even if it is not in use. Allocate Extended Memory Block (Function 09h): ---------------------------------------------- ARGS: AH = 09h DX = Amount of extended memory being requested in K-bytes RETS: AX = 0001h if the block is allocated, 0000h otherwise DX = 16-bit handle to the allocated block ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = A0h if all available extended memory is allocated BL = A1h if all available extended memory handles are in use This function attempts to allocate a block of the given size out of the pool of free extended memory. If a block is available, it is reserved for the caller and a 16-bit handle to that block is returned. The handle should be used in all subsequent extended memory calls. If no memory was allocated, the returned handle is null. NOTE: Extended memory handles are scarce resources. Programs should try to allocate as few as possible at any one time. When all of a driver's handles are in use, any free extended memory is unavailable. Free Extended Memory Block (Function 0Ah): ------------------------------------------ ARGS: AH = 0Ah DX = Handle to the allocated block which should be freed RETS: AX = 0001h if the block is successfully freed, 0000h otherwise ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = A2h if the handle is invalid BL = ABh if the handle is locked This function frees a block of extended memory which was previously allocated using Function 09h (Allocate Extended Memory Block). Programs which allocate extended memory should free their memory blocks before exiting. When an extended memory buffer is freed, its handle and all data stored in it become invalid and should not be accessed. Move Extended Memory Block (Function 0Bh): ------------------------------------------ ARGS: AH = 0Bh DS:SI = Pointer to an Extended Memory Move Structure (see below) RETS: AX = 0001h if the move is successful, 0000h otherwise ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = 82h if an A20 error occurs BL = A3h if the SourceHandle is invalid BL = A4h if the SourceOffset is invalid BL = A5h if the DestHandle is invalid BL = A6h if the DestOffset is invalid BL = A7h if the Length is invalid BL = A8h if the move has an invalid overlap BL = A9h if a parity error occurs Extended Memory Move Structure Definition: ExtMemMoveStruct struc Length dd ? ; 32-bit number of bytes to transfer SourceHandle dw ? ; Handle of source block SourceOffset dd ? ; 32-bit offset into source DestHandle dw ? ; Handle of destination block DestOffset dd ? ; 32-bit offset into destination block ExtMemMoveStruct ends This function attempts to transfer a block of data from one location to another. It is primarily intended for moving blocks of data between conventional memory and extended memory, however it can be used for moving blocks within conventional memory and within extended memory. NOTE: If SourceHandle is set to 0000h, the SourceOffset is interpreted as a standard segment:offset pair which refers to memory that is directly accessible by the processor. The segment:offset pair is stored in Intel DWORD notation. The same is true for DestHandle and DestOffset. SourceHandle and DestHandle do not have to refer to locked memory blocks. Length must be even. Although not required, WORD-aligned moves can be significantly faster on most machines. DWORD aligned move can be even faster on 80386 machines. If the source and destination blocks overlap, only forward moves (i.e. where the source base is less than the destination base) are guaranteed to work properly. Programs should not enable the A20 line before calling this function. The state of the A20 line is preserved. This function is guaranteed to provide a reasonable number of interrupt windows during long transfers. Lock Extended Memory Block (Function 0Ch): ------------------------------------------ ARGS: AH = 0Ch DX = Extended memory block handle to lock RETS: AX = 0001h if the block is locked, 0000h otherwise DX:BX = 32-bit physical address of the locked block ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = A2h if the handle is invalid BL = ACh if the block's lock count overflows BL = ADh if the lock fails This function locks an extended memory block and returns its base address as a 32-bit physical address. Locked memory blocks are guaranteed not to move. The 32-bit pointer is only valid while the block is locked. Locked blocks should be unlocked as soon as possible. NOTE: A block does not have to be locked before using Function 0Bh (Move Extended Memory Block). "Lock counts" are maintained for EMBs. Unlock Extended Memory Block (Function 0Dh): -------------------------------------------- ARGS: AH = 0Dh DX = Extended memory block handle to unlock RETS: AX = 0001h if the block is unlocked, 0000h otherwise ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = A2h if the handle is invalid BL = AAh if the block is not locked This function unlocks a locked extended memory block. Any 32-bit pointers into the block become invalid and should no longer be used. Get EMB Handle Information (Function 0Eh): ------------------------------------------ ARGS: AH = 0Eh DX = Extended memory block handle RETS: AX = 0001h if the block's information is found, 0000h otherwise BH = The block's lock count BL = Number of free EMB handles in the system DX = The block's length in K-bytes ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = A2h if the handle is invalid This function returns additional information about an extended memory block to the caller. NOTE: To get the block's base address, use Function 0Ch (Lock Extended Memory Block). Reallocate Extended Memory Block (Function 0Fh): ------------------------------------------------ ARGS: AH = 0Fh BX = New size for the extended memory block in K-bytes DX = Unlocked extended memory block handle to reallocate RETS: AX = 0001h if the block is reallocated, 0000h otherwise ERRS: BL = 80h if the function is not implemented BL = 81h if a VDISK device is detected BL = A0h if all available extended memory is allocated BL = A1h if all available extended memory handles are in use BL = A2h if the handle is invalid BL = ABh if the block is locked This function attempts to reallocate an unlocked extended memory block so that it becomes the newly specified size. If the new size is smaller than the old block's size, all data at the upper end of the old block is lost. Request Upper Memory Block (Function 10h): ------------------------------------------ ARGS: AH = 10h DX = Size of requested memory block in paragraphs RETS: AX = 0001h if the request is granted, 0000h otherwise BX = Segment number of the upper memory block If the request is granted, DX = Actual size of the allocated block in paragraphs otherwise, DX = Size of the largest available UMB in paragraphs ERRS: BL = 80h if the function is not implemented BL = B0h if a smaller UMB is available BL = B1h if no UMBs are available This function attempts to allocate an upper memory block to the caller. If the function fails, the size of the largest free UMB is returned in DX. NOTE: By definition UMBs are located below the 1MB address boundary. The A20 Line does not need to be enabled before accessing an allocated UMB. UMBs are paragraph aligned. To determine the size of the largest available UMB, attempt to allocate one with a size of FFFFh. UMBs are unaffected by EMS calls. Release Upper Memory Block (Function 11h): ------------------------------------------ ARGS: AH = 11h DX = Segment number of the upper memory block RETS: AX = 0001h if the block was released, 0000h otherwise ERRS: BL = 80h if the function is not implemented BL = B2h if the UMB segment number is invalid This function frees a previously allocated upper memory block. When an UMB has been released, any code or data stored in it becomes invalid and should not be accessed. Reallocate Upper Memory Block (Function 12h) ARGS: AH = 12h BX = New size for UMB in paragraphs DX = Segment number of the UMB to reallocate RETS: AX = 1 if the block was reallocated, 0 otherwise ERRS: BL = 80h if the function is not implemented BL = B0h if no UMB large enough to satisfy the request is avail able. In this event, DX is returned with the size of the larg est UMB that is available. BL = B2h if the UMB segment number is invalid This function attempts to reallocate an Upper Memory Block to a newly specified size. If the new size is smaller than the old block's size, all data at the upper end of the block is lost. Super Extended Memory Support These changes are intended to provide support for extended memory pools up to 4 Gb in size. The current XMS API, since it uses 16-bit values to specify block sizes in Kb, is limited to 64 Mb maximum block size. Future machines are expected to support memory above 64 MB. This support is implemented in the form of extensions to existing functions, rather than entirely new entry points, to allow for more efficient implementations. Programs should generally use the existing functions, instead of these extended ones, unless they have an explicit need to deal with memory above 64 Mb. Query Any Free Extended Memory (Function 88h) Entry: AH = 88h Exit: EAX = Size of largest free extended memory block in Kb. BL = 0 if no error occurs, otherwise it takes an error code. ECX = Highest ending address of any memory block. EDX = Total amount of free memory in Kb. Errors: BL = 80h if the function is not implemented. BL = 81h if a VDISK device is detected. BL = A0h if all extended memory is allocated. This function uses 32-bit values to return the size of available memory, thus allowing returns up to 4GByte. Additionally, it returns the highest known physical memory address, that is, the physical address of the last byte of memory. There may be discontinuities in the memory map below this address. The memory pool reported on is the same as that reported on by the existing Query Free Extended Memory function. If the highest memory address is not more than 64 Mb, then these two functions will return the same results. Because of its reliance on 32-bit registers, this function is only available on 80386 and higher processors. XMS drivers on 80286 machines should return error code 80h if this function is called. If error code 81h is returned, the value in ECX will still be valid. If error code A0h is returned, EAX and EDX will be 0, and ECX will still be valid. Allocate Any Extended Memory (Function 89h) Entry: AH = 89h EDX = Amount of extended memory requested, in Kb. Exit: AX = 1 if the block is allocated, 0 if not DX = Handle to allocated block. Errors: BL = 80h if the function is not implemented. BL = 81h if a VDISK device is detected. BL = A0h if all available extended memory is allocated. BL = A1h if all available extended memory handles are in use. This function is similar to the existing Allocate Extended Memory, except that it uses a 32-bit instead of a 16-bit value to specify the amount of memory requested. It allocates from the same memory and handle pool as the current function. Since it requires a 32-bit register, this function can be supported only on 80386 and higher processors, and XMS drivers on 80286 machines should return error code 80h. Get Extended EMB Handle Information (Function 8Eh) Entry: AH = 8Eh DX = Extended memory block handle. Exit: AX = 1 if the block's information is found, 0 if not BH = Block lock count CX = Number of free EMB handles in the system EDX = Block's length in Kb. Errors: BL = 80h if the function is not implemented. BL = 81h if a VDISK device is detected. BL = A2h if the handle is invalid. This function is similar to the Get EMB Handle Information function. Since it uses a 32-bit register to report the block size, it can be used to get information on blocks larger than 64 Mb. It also uses a 16-bit instead of 8-bit register to report the number of free handles, allowing the handle pool to be extended beyond 256 entries. Because of its reliance on a 32-bit register, this function is available on 80386 and higher processors. XMS drivers on 80286 machines should return error code 80h if this function is called. Reallocate Any Extended Memory (Function 8Fh) Entry: AH = 8Fh EBX = New size for extended memory block, in Kb. DX = Unlocked handle for memory block to be resized. Exit: AX = 1 if the block is reallocated, 0 if not Errors: BL = 80h if the function is not implemented. BL = 81h if a VDISK device is detected. BL = A0h if all available extended memory is allocated. BL = A1h if all available extended memory handles are in use. BL = A2h if the handle is invalid. BL = ABh if the block is locked. This function is similar to the existing Reallocate Extended Memory, except that it uses a 32-bit instead of a 16-bit value to specify the amount of memory requested. It allocates from the same memory and handle pool as the current function. Since it requires a 32-bit register, this function can be supported only on 80386 and higher processors, and XMS drivers on 80286 machines should return error code 80h. PRIORITIZING HMA USAGE: ----------------------- For DOS users to receive the maximum benefit from the High Memory Area, programs which use the HMA must store as much of their resident code in it as is possible. It is very important that developers realize that the HMA is allocated as a single unit. For example, a TSR program which grabs the HMA and puts 10K of code into it may prevent a later TSR from putting 62K into the HMA. Obviously, regular DOS programs would have more memory available to them below the 640K line if the 62K TSR was moved into the HMA instead of the 10K one. The first method for dealing with conflicts such as this is to require programs which use the HMA to provide a command line option for disabling this feature. It is crucial that TSRs which do not make full use of the HMA provide such a switch on their own command line (suggested name "/NOHMA"). The second method for optimizing HMA usage is through the /HMAMIN= parameter on the XMS device driver line. The number after the parameter is defined to be the minimum amount of HMA space (in K-bytes) used by any driver or TSR. For example, if "DEVICE=HIMEM.SYS /HMAMIN=48" is in a user's CONFIG.SYS file, only programs which request at least 48K would be allowed to allocate the HMA. This number can be adjusted either by installation programs or by the user himself. If this parameter is not specified, the default value of 0 is used causing the HMA to be allocated on a first come, first served basis. Note that this problem does not impact application programs. If the HMA is available when an application program starts, the application is free to use as much or as little of the HMA as it wants. For this reason, applications should pass FFFFh in DX when calling Function 01h. HIGH MEMORY AREA RESTRICTIONS: ------------------------------ - Far pointers to data located in the HMA cannot be passed to DOS. DOS normalizes any pointer which is passed into it. This will cause data addresses in the HMA to be invalidated. - Disk I/O directly into the HMA (via DOS, INT 13h, or otherwise) is not recommended. - Programs, especially drivers and TSRs, which use the HMA *MUST* use as much of it as possible. If a driver or TSR is unable to use at least 90% of the available HMA (typically ~58K), they must provide a command line switch for overriding HMA usage. This will allow the user to configure his machine for optimum use of the HMA. - Device drivers and TSRs cannot leave the A20 line permanently turned on. Several applications rely on 1MB memory wrap and will overwrite the HMA if the A20 line is left enabled potentially causing a system crash. - Interrupt vectors must not point into the HMA. This is a result of the previous restriction. Note that interrupt vectors can point into any allocated upper memory blocks however. ERROR CODE INDEX: ----------------- If AX=0000h when a function returns and the high bit of BL is set, BL=80h if the function is not implemented 81h if a VDISK device is detected 82h if an A20 error occurs 8Eh if a general driver error occurs 8Fh if an unrecoverable driver error occurs 90h if the HMA does not exist 91h if the HMA is already in use 92h if DX is less than the /HMAMIN= parameter 93h if the HMA is not allocated 94h if the A20 line is still enabled A0h if all extended memory is allocated A1h if all available extended memory handles are in use A2h if the handle is invalid A3h if the SourceHandle is invalid A4h if the SourceOffset is invalid A5h if the DestHandle is invalid A6h if the DestOffset is invalid A7h if the Length is invalid A8h if the move has an invalid overlap A9h if a parity error occurs AAh if the block is not locked ABh if the block is locked ACh if the block's lock count overflows ADh if the lock fails B0h if a smaller UMB is available B1h if no UMBs are available B2h if the UMB segment number is invalid IMPLEMENTATION NOTES FOR DOS XMS DRIVERS: ----------------------------------------- - A DOS XMS driver's control function must begin with code similar to the following: XMMControl proc far jmp short XCControlEntry ; For "hookability" nop ; NOTE: The jump must be a short nop ; jump to indicate the end of nop ; any hook chainThe nop's ; allow a far jump to be ; patched in. XCControlEntry: - XMS drivers must preserve all registers except those containing returned values across any function call. - XMS drivers are required to hook INT 15h and watch for calls to functions 87h (Block Move) and 88h (Extended Memory Available). The INT 15h Block Move function must be hooked so that the state of the A20 line is preserved across the call. The INT 15h Extended Memory Available function must be hooked to return 0h to protect the HMA. - In order to maintain compatibility with existing device drivers, DOS XMS drivers must not hook INT 15h until the first non-Version Number call to the control function is made. - XMS drivers are required to check for the presence of drivers which use the IBM VDISK allocation scheme. Note that it is not sufficient to check for VDISK users at installation time but at the time when the HMA is first allocated. If a VDISK user is detected, the HMA must not be allocated. Microsoft will publish a standard method for detecting drivers which use the VDISK allocation scheme. - XMS drivers which have a fixed number of extended memory handles (most do) should implement a command line parameter for adjusting that number (suggested name "/NUMHANDLES=") - XMS drivers should make sure that the major DOS version number is greater than or equal to 3 before installing themselves. - UMBs cannot occupy memory addresses that can be banked by EMS 4.0. EMS 4.0 takes precedence over UMBs for physically addressable memory. - All driver functions must be re-entrant. Care should be taken to not leave interrupts disabled for long periods of time. - Allocation of a zero length extended memory buffer is allowed. Programs which hook XMS drivers may need to reserve a handle for private use via this method. Programs which hook an XMS driver should pass all requests for zero length EMBs to the next driver in the chain. - Drivers should control the A20 line via an "enable count." Local En- able only enables the A20 line if the count is zero. It then increments the count. Local Disable only disables A20 if the count is one. It then decrements the count. Global Enable/Disable keeps a flag which indicates the state of A20. They use Local Enable/Disable to actually change the state. - Drivers should always check the physical A20 state in the local Enable-Disab le calls, to see that the physical state matches the internal count. If the physical state does not match, it should be modified so that it matches the internal count. This avoids problems wi th applications that modify A20 directly. IMPLEMENTATION OF CODE FOR HOOKING THE XMS DRIVER: In order to support the hooking of the XMS driver by multiple pieces of code, the following code sample should be followed. Use of other methods for hooking the XMS driver will not work in many cases. This method is the official supported one. The basic strategy is: Find the XMS driver header which has the "near jump" dispatch. Patch the near jump to a FAR jump which jumps to my HOOK XMS driver header. NOTES: o This architecture allows the most recent HOOKer to undo his XMS driver hook at any time without having to worry about damaging a "hook chain". o This architecture allows the complete XMS hook chain to be enumerated at any time. There are no "hidden hooks". o This architecture allows the HOOKer to not have to worry about installing an "INT 2F hook" to hook the AH=43h INT 2Fs handled by the XMS driver. The base XMS driver continues to be the only one installed on INT 2Fh AH=43h. This avoids all of the problems of undoing a software interrupt hook. ; ; When I wish to CHAIN to the previous XMS driver, I execute a FAR JMP ; to the address stored in this DWORD. ; PrevXMSControlAddr dd ? ; ; The next two data items are needed ONLY if I desire to be able to undo ; my XMS hook. ; PrevXMSControlJmpVal stores the previos XMS dispatch near jump offset ; value that is used to unhook my XMS hook ; PrevXMSControlBase stores the address of the XMS header that I hooked ; PrevXMSControlBase dd ? PrevXMSControlJmpVal db ? ; ; This is MY XMS control header. ; MyXMSControlFunc proc FAR jmp short XMSControlEntry nop nop nop XMSControlEntry: ...... Chain: jmp cs:[PrevXMSControlAddr] MyXMSControlFunc endp ....... ; ; This is the code which installs my hook into the XMS driver. ; ; ; See if there is an XMS driver to hook ; mov ax,4300h int 2Fh cmp al,80h jne NoXMSDrvrToHookError ; ; Get the current XMS driver Control address ; mov ax,4310h int 2Fh NextXMSHeader: mov word ptr [PrevXMSControlAddr+2],es mov word ptr [PrevXMSControlBase+2],es mov word ptr [PrevXMSControlBase],bx mov cx,word ptr es:[bx] cmp cl,0EBh ; Near JUMP je ComputeNearJmp cmp cl,0EAh ; Far JUMP jne XMSDrvrChainMessedUpError ComputeFarJmp: mov si,word ptr es:[bx+1] ; Offset of jump mov es,word ptr es:[bx+1+2] ; Seg of jump mov bx,si jmp short NextXMSHeader ComputeNearJmp: cmp word ptr es:[bx+2],9090h ; Two NOPs? jne XMSDrvrChainMessedUpError ; No cmp byte ptr es:[bx+4],90h ; Total of 3 NOPs? jne XMSDrvrChainMessedUpError ; No mov di,bx ; Save pointer to header xor ax,ax mov al,ch ; jmp addr of near jump mov [PrevXMSControlJmpVal],al add ax,2 ; NEAR JMP is 2 byte instructio n add bx,ax ; Target of jump mov word ptr [PrevXMSControlAddr],bx ; ; Now INSTALL my XMS HOOK ; cli ; Disable INTs in case someone calls ; XMS at interrupt time mov byte ptr es:[di],0EAh ; Far Immed. JUMP instruction mov word ptr es:[di+1],offset MyXMSControlFunc mov word ptr es:[di+3],cs sti ..... ; ; Deinstall my XMS hook. This can be done IF AND ONLY IF my XMS header ; still contains the near jump dispatch ; cmp byte ptr [MyXMSControlFunc],0EBh jne CantDeinstallError mov al,0EBh mov ah,[PrevXMSControlJmpVal] les bx,[PrevXMSControlBase] cli ; Disable INTs in case someone calls ; XMS at interrupt time mov word ptr es:[bx],ax mov word ptr es:[bx+2],9090h mov byte ptr es:[bx+4],90h sti .... IMPLEMENTATION NOTES FOR HIMEM.SYS: ----------------------------------- - HIMEM.SYS currently supports true AT-compatibles, 386 AT machines, IBM PS/2s, AT&T 6300 Plus systems and Hewlett Packard Vectras. - If HIMEM finds that it cannot properly control the A20 line or if there is no extended memory available when HIMEM.SYS is invoked, the driver does not install itself. HIMEM.SYS displays the message "High Memory Area Unavailable" when this situation occurs. - If HIMEM finds that the A20 line is already enabled when it is invoked, it will NOT change the A20 line's state. The assumption is that whoever enabled it knew what they were doing. HIMEM.SYS displays the message "A20 Line Permanently Enabled" when this situation occurs. - HIMEM.SYS is incompatible with IBM's VDISK.SYS driver and other drivers which use the VDISK scheme for allocating extended memory. However, HIMEM does attempt to detect these drivers and will not allocate the HMA if one is found. - HIMEM.SYS supports the optional "/HMAMIN=" parameter. The valid values are decimal numbers between 0 and 63. - By default, HIMEM.SYS has 32 extended memory handles available for use. This number may be adjusted with the "/NUMHANDLES=" parameter. The maximum value for this parameter is 128 and the minimum is 0. Each handle currently requires 6 bytes of resident space. Copyright (c) 1988, Microsoft Corporation _________________________________________________________________ Copyright ©1996 Paul Hart Site Administrator ;=[END XMS3SPEC.TXT]========================================================= ;=[BEGIN HIMEM]============================================================== himem.obj: himem.asm xm286.asm xm386.asm masm himem; himem.exe: himem.obj link himem; himem.sys: himem.exe exe2bin himem.exe himem.sys ;=[END HIMEM]================================================================ ;=[BEGIN HIMEM.ASM]========================================================== ;***************************************************************************** ;* * ;* HIMEM.ASM - Chip Anderson * ;* * ;* Extended Memory Specification Driver - * ;* Copyright 1988, Microsoft Corporation * ;* * ;* The HMA was originally envisioned by Ralph Lipe. * ;* Original XMS spec designed by Aaron Reynolds, Chip Anderson, and * ;* Tony Gosling. Additional spec suggestions by Phil Barrett and David * ;* Weise of Microsoft, Ed McNierney of Lotus and Bob Smith of Qualitas. * ;* * ;* MoveExtMemory function written by Tony Gosling. * ;* * ;* AT&T 6300 support added by Bill Hall, Olivetti ATC, Cupertino, CA * ;* HP Vectra support added by Ralph Peterson, Hewlett Packard Corp. * ;* * ;***************************************************************************** XMSVersion equ 0200h HimemVersion equ 0203h ; Version - ; ; 1.00 - Written 4/17/88 ; 1.01 - Added Global/Temporary Enable of A20 4/18/88 ; 1.02 - Don't use DOS to change interrupts 4/19/88 ; - Return 1 for success in Disable cases 4/19/88 ; - Prevent disables if A20 enabled at start 4/19/88 ; - Added INT 2F handler check 4/19/88 ; - Used smarter hooking routine 4/19/88 ; 1.03 - Optimized some code 4/20/88 ; - Temporarily Disable properly 4/20/88 ; - Disable INT's while changing vars 4/20/88 ; 1.04 - Check to see if A20 works during init 4/23/88 ; - Fixed PS/2 support 4/23/88 ; 1.05 - Added QueryExtMemory and AllocExtMemory 4/25/88 ; 1.06 - Never remove the INT 15h hook once it is in 4/27/88 ; - Changed ExtMemory calls to provide "full" MM 4/27/88 ; - Force A20 line off on PS2's at init time 4/27/88 ; 1.07 - Added Multiple A20 Handler support 5/01/88 ; - Added popff macro 5/01/88 ; - REP MOVSW words not bytes 5/04/88 ; 1.08 - Made popff relocateable 5/04/88 ; - Added support for the AT&T 6300 Plus 5/05/88 ; - Added support for the HP Vectra 5/06/88 ; 1.09 - Added CEMM recognition code 5/16/88 ; - Don't automatically trash BX in HMMControl 5/17/88 ; 1.10 - Change PS/2 ID routine to use Watchdog timer 6/02/88 ; - Changed CEMM recognition string to just VDISK 6/07/88 ; - Fixed 2 instance bug 6/14/88 ; 1.11 - Changed INT 2F multiplex number to 43h 6/23/88 ; - Fixed HP Vectra support for older Vectras 6/23/88 ; - Fixed Block Move register trashing bug 6/25/88 ; 2.00 - Updated to XMS v2.00 7/10/88 ; - Reworked initialization code 7/12/88 ; - Reworked reentrancy issues 7/13/88 ; - Finialized A20 handling 7/14/88 ; - Added parameter support 7/14/88 ; 2.01 - Official version with Vecta and 6300 support 7/15/88 ; 2.02 - Removed INT 1 from MoveExtMemory 7/19/88 ; - Fixed minor problems in QueryExtMemory 7/19/88 ; 2.03 - Added 386 Big Mode MoveBlock 8/04/88 ; - Added Compaq "Built-In" Memory support 8/05/88 ; - Fixed 64K-free Installation bug 8/05/88 ; - Change PS/2 detection code yet again 8/08/88 ; - Fixed "A20 On" Message bug (ugh) 8/09/88 ; - Fixed A20 Init testing code 8/09/88 ; - Cleaned up for publication 8/09/88 name Himem title 'HIMEM.SYS - Microsoft XMS Device Driver' ;*--------------------------------------------------------------------------* ;* EQUATES * ;*--------------------------------------------------------------------------* DEFHANDLES equ 32 ; Default # of EMB handles MAXHANDLES equ 128 ; Max # of EMB handles cXMSFunctions equ 12h ; = # of last function+1! FREEFLAG equ 00000001b ; EMB Flags USEDFLAG equ 00000010b UNUSEDFLAG equ 00000100b ; XMS Error Codes. ERR_NOTIMPLEMENTED equ 080h ERR_VDISKFOUND equ 081h ERR_A20 equ 082h ERR_GENERAL equ 08Eh ERR_UNRECOVERABLE equ 08Fh ERR_HMANOTEXIST equ 090h ERR_HMAINUSE equ 091h ERR_HMAMINSIZE equ 092h ERR_HMANOTALLOCED equ 093h ERR_OUTOMEMORY equ 0A0h ERR_OUTOHANDLES equ 0A1h ERR_INVALIDHANDLE equ 0A2h ERR_SHINVALID equ 0A3h ERR_SOINVALID equ 0A4h ERR_DHINVALID equ 0A5h ERR_DOINVALID equ 0A6h ERR_LENINVALID equ 0A7h ERR_OVERLAP equ 0A8h ERR_PARITY equ 0A9h ERR_EMBUNLOCKED equ 0AAh ERR_EMBLOCKED equ 0ABh ERR_LOCKOVERFLOW equ 0ACh ERR_LOCKFAIL equ 0ADh ERR_UMBSIZETOOBIG equ 0B0h ERR_NOUMBS equ 0B1h ERR_INVALIDUMB equ 0B2h .386p ; In order to address memory above 1 MB on the AT&T 6300 PLUS, it is ; necessary to use the special OS-MERGE hardware to activate lines ; A20 to A23. However, these lines can be disabled only by resetting ; the processor. The address to which to return after reset are placed ; at 40:A2, noted here as RealLoc1. BiosSeg SEGMENT USE16 AT 40h ; Used to locate 6300 PLUS reset address org 00A2h RealLoc1 dd 0 BiosSeg ends ; Macro to avoid the 286 POPF bug. Performs a harmless IRET to simulate a ; popf. Some 286s allow interrupts to sneak in during a real popf. popff macro push cs call pPPFIRet ; Defined as the offset of any IRET endm ;*--------------------------------------------------------------------------* ;* SEGMENT DEFINITION * ;*--------------------------------------------------------------------------* CODE SEGMENT PARA PUBLIC USE16 'CODE' assume cs:code,ds:code,es:code org 0 ; The Driver Header definition. Header dd -1 ; Link to next driver, -1 = end of list dw 1010000000000000b ; Device attributes, Non-IBM bit set dw Strategy ; "Stategy" entry point dw Interrupt ; "Interrupt" entry point db 'XMSXXXX0' ; Device name ;**************************************************************************** ;* * ;* Data Structures and Global Variables - * ;* * ;**************************************************************************** ; The driver Request Header structure definition. ReqHdr struc ReqLen db ? Unit db ? Command db ? Status dw ? Reserved db 8 dup (?) Units db ? Address dd ? pCmdLine dd ? ReqHdr ends ; An EMB Handle structure definition. Handle struc ; Handle Table Entry Flags db ? ; Unused/InUse/Free cLock db ? ; Lock count Base dw ? ; 32-bit base address in K Len dw ? ; 32-bit length in K Handle ends ; Extended Memory Move Block structure definition. MoveExtendedStruc struc bCount dd ? ; Length of block to move SourceHandle dw ? ; Handle for souce SourceOffset dd ? ; Offset into source DestHandle dw ? ; Handle for destination DestOffset dd ? ; Offset into destination MoveExtendedStruc ends ; The Global variables. pPPFIRet dw PPFIRet ; The offset of an IRET for the POPFF macro pReqHdr dd ? ; Pointer to MSDOS Request Header structure pInt15Vector dw 15h*4,0 ; Pointer to the INT 15 Vector PrevInt15 dd 0 ; Original INT 15 Vector PrevInt2F dd 0 ; Original INT 2F Vector fHMAInUse db 0 ; High Memory Control Flag, != 0 -> In Use fCanChangeA20 db 0 ; A20 Enabled at start? fHMAMayExist db 0 ; True if the HMA could exist at init time fHMAExists db 0 ; True if the HMA exists fVDISK db 0 ; True if a VDISK device was found EnableCount dw 0 ; A20 Enable/Disable counter fGlobalEnable dw 0 ; Global A20 Enable/Disable flag KiddValley dw 0 ; The address of the handle table KiddValleyTop dw 0 ; Points to the end of the handle table MinHMASize dw 0 ; /HMAMIN= parameter value cHandles dw DEFHANDLES ; # of handles to allocate cImplementedFuncs db cXMSFunctions-3 ; Omit the UMB functions ; and ReallocEMB A20Handler dw 0 ; Offset of the A20 Handler BIMBase dw 0 ; Base address and Lenght of remaining Compaq BIMLength dw 0 ; Built-In Memory (set at Init time) MemCorr dw 0 ; KB of memory at FA0000 on AT&T 6300 Plus. ; This is used to correct INT 15h, ; Function 88h return value. OldStackSeg dw 0 ; Stack segment save area for 6300 Plus. ; Needed during processor reset. ;*--------------------------------------------------------------------------* ;* * ;* Strategy - * ;* * ;* Called by MS-DOS when ever the driver is accessed. * ;* * ;* ARGS: ES:BX = Address of Request Header * ;* RETS: Nothing * ;* REGS: Preserved * ;* * ;*--------------------------------------------------------------------------* Strategy proc far ; Save the address of the request header. mov word ptr cs:[pReqHdr],bx mov word ptr cs:[pReqHdr][2],es ret Strategy endp ;*--------------------------------------------------------------------------* ;* * ;* Interrupt - * ;* * ;* Called by MS-DOS immediately after Strategy routine * ;* * ;* ARGS: None * ;* RETS: Return code in Request Header's Status field * ;* REGS: Preserved * ;* * ;*--------------------------------------------------------------------------* Interrupt proc far ; Save the registers including flags. push ax ; We cannot use pusha\popa because push bx ; we could be on an 8086 at this point push cx push dx push ds push es push di push si push bp pushf ; Set DS=CS for access to global variables. push cs pop ds les di,[pReqHdr] ; ES:DI = Request Header mov bl,es:[di.Command] ; Get Function code in BL or bl,bl ; Only Function 00h (Init) is legal jz short IInit cmp bl,16 ; Test for "legal" DOS functions jle short IOtherFunc IBogusFunc: mov ax,8003h ; Return "Unknown Command" jmp short IExit IOtherFunc: xor ax,ax ; Return zero for unsupported functions jmp short IExit ; Initialize the driver. IInit: call InitDriver les di,[pReqHdr] ; Restore ES:DI = Request Header IExit: or ax,0100h ; Turn on the "Done" bit mov es:[di.Status],ax ; Store return code ; Restore the registers. popff pop bp pop si pop di pop es pop ds pop dx pop cx pop bx pop ax ret Interrupt endp ;*--------------------------------------------------------------------------* ;* * ;* Int2FHandler - * ;* * ;* Hooks Function 43h, Subfunction 10h to return the * ;* address of the High Memory Manager Control function. * ;* Also returns 80h if Function 43h, Subfunction 0h is requested. * ;* * ;* ARGS: AH = Function, AL = Subfunction * ;* RETS: ES:BX = Address of XMMControl function (if AX=4310h) * ;* AL = 80h (if AX=4300) * ;* REGS: Preserved except for ES:BX (if AX=4310h) * ;* Preserved except for AL (if AX=4300h) * ;* * ;*--------------------------------------------------------------------------* Int2FHandler proc far sti ; Flush any queued interrupts cmp ah,43h ; Function 43h? jne short I2FNextInt or al,al ; Subfunction 0? jne short I2FNextSub ; No, continue ; Indicate that an XMS driver is installed. mov al,80h ; Return 80h in AL PPFIRet: iret ; Label sets up the POPFF macro I2FNextSub: cmp al,10h ; Subfunction 10? jne short I2FNextInt ; No, goto next handler ; Return the address of the XMS Control function in ES:BX. push cs pop es mov bx,offset XMMControl iret ; Continue down the Int 2F chain. I2FNextInt: cli ; Disable interrupts again jmp cs:[PrevInt2F] Int2FHandler endp ;*--------------------------------------------------------------------------* ;* * ;* ControlJumpTable - * ;* * ;* Contains the address for each of the XMS Functions. * ;* * ;*--------------------------------------------------------------------------* ControlJumpTable label word dw Version ; Function 00h dw RequestHMA ; Function 01h dw ReleaseHMA ; Function 02h dw GlobalEnableA20 ; Function 03h dw GlobalDisableA20 ; Function 04h dw LocalEnableA20 ; Function 05h dw LocalDisableA20 ; Function 06h dw IsA20On ; Function 07h dw QueryExtMemory ; Function 08h dw AllocExtMemory ; Function 09h dw FreeExtMemory ; Function 0Ah dw MoveBlock ; Function 0Bh dw LockExtMemory ; Function 0Ch dw UnlockExtMemory ; Function 0Dh dw GetExtMemoryInfo ; Function 0Eh ; We don't implement Realloc in this version. ; dw ReallocExtMemory ; Function 0Fh ; We don't implement the UMB functions. ; dw RequestUMB ; Function 14 ; dw ReleaseUMB ; Function 15 ;*--------------------------------------------------------------------------* ;* * ;* XMMControl - * ;* * ;* Main Entry point for the Extended Memory Manager * ;* * ;* ARGS: AH = Function, AL = Optional parm * ;* RETS: AX = Function Success Code, BL = Optional Error Code * ;* REGS: AX, BX, DX and ES may not be preserved depending on function * ;* * ;* INTERNALLY REENTRANT * ;* * ;*--------------------------------------------------------------------------* XMMControl proc far jmp short XCControlEntry ; For "hookability" nop ; NOTE: The jump must be a nop ; short jump to indicate nop ; the end of any hook chain. ; The nop's allow a far jump ; to be patched in. XCControlEntry: ; Preserve the following registers. push cx push si push di push ds push es pushf ; Save DS in ES. push ds pop es ; NOTE: ES cannot be used for parms! ; Set DS equal to CS. push cs pop ds ; Preserve the current function number. push ax ; Is this a call to "Get XMS Version"? or ah,ah jz short XCCallFunc ; Yes, don't hook INT 15h yet ; Is this a valid function number? cmp ah,[cImplementedFuncs] jb short XCCheckHook pop ax ; No, Un-preserve AX and return an error xor ax,ax mov bl,ERR_NOTIMPLEMENTED jmp short XCExit ; Is INT 15h already hooked? XCCheckHook:pushf cli ; This is a critical section cmp word ptr [PrevInt15][2],0 ; Is the segment non-zero? jne short XCCheckVD ; Try to claim all remaining extended memory. call HookInt15 ; Was a VDISK device found? XCCheckVD: popff ; End of critical section cmp [fVDISK],0 je short XCCallFunc pop ax ; Yes, Un-preserve AX and return an error xor ax,ax mov bl,ERR_VDISKFOUND xor dx,dx jmp short XCExit ; Call the appropriate API function. XCCallFunc: pop ax ; Restore AX mov al,ah xor ah,ah shl ax,1 mov di,ax ; NOTE: DI cannot be used for parms! call word ptr [ControlJumpTable+di] ; Restore the preserved registers. XCExit: popff ; NOTE: Flags must be restored immediately pop es ; after the call to the API functions. pop ds pop di pop si pop cx ret XMMControl endp ;*--------------------------------------------------------------------------* ;* * ;* Get XMS Version Number - FUNCTION 00h * ;* * ;* Returns the XMS version number * ;* * ;* ARGS: None * ;* RETS: AX = XMS Version Number * ;* BX = Internal Driver Version Number * ;* DX = 1 if HMA exists, 0 if it doesn't * ;* REGS: AX, BX and DX are clobbered * ;* * ;* INTERNALLY REENTRANT * ;* * ;*--------------------------------------------------------------------------* Version proc near mov ax,XMSVersion mov bx,HimemVersion xor dh,dh ; Is Int 15h hooked? cmp word ptr [PrevInt15][2],0 ; Is the segment non-zero? jne short VHooked mov dl,[fHMAMayExist] ; No, return the status at ret ; init time. VHooked: mov dl,[fHMAExists] ; Yes, return the real status ret Version endp ;*--------------------------------------------------------------------------* ;* * ;* HookInt15 - * ;* * ;* Insert the INT 15 hook * ;* * ;* ARGS: None * ;* RETS: None * ;* REGS: AX, BX, CS, DI, SI, and Flags are clobbered * ;* * ;* EXTERNALLY NON-REENTRANT * ;* Interrupts must be disabled before calling this function. * ;* * ;*--------------------------------------------------------------------------* HookInt15 proc near push es ; Has a VDISK device been installed? call IsVDISKIn cmp [fVDISK],0 je short HINoVD ; No, continue pop es ; Yes, return without hooking ret HINoVD: mov ah,88h ; Is 64K of Extended memory around? int 15h sub ax,[MemCorr] ; 6300 Plus may have memory at FA0000h cmp ax,64 jb short HIInitMemory ; Less than 64K free? Then no HMA. mov [fHMAExists],1 HIInitMemory: ; Init the first handle to be one huge free block. mov bx,[KiddValley] mov [bx.Flags],FREEFLAG mov [bx.Len],ax mov [bx.Base],1024 cmp [fHMAExists],0 je short HICont add [bx.Base],64 ; See if any Compaq "Built In Memory" exists. HICont: mov ax,[BIMBase] or ax,ax jz short HIHookEmHorns mov cx,[BIMLength] ; Fill out the next handle entry. add bx,size Handle mov [bx.Flags],FREEFLAG mov [bx.Len],cx mov [bx.Base],ax ; Save the current INT 15 vector. HIHookEmHorns: les si,dword ptr pInt15Vector ; Exchange the old vector with the new one. mov ax,offset Int15Handler xchg ax,es:[si][0] mov word ptr [PrevInt15][0],ax mov ax,cs xchg ax,es:[si][2] mov word ptr [PrevInt15][2],ax pop es ret HookInt15 endp ;*--------------------------------------------------------------------------* ;* * ;* IsVDISKIn - * ;* * ;* Looks for drivers which use the IBM VDISK method of allocating * ;* Extended Memory. XMS is incompatible with the VDISK method. * ;* * ;* ARGS: None * ;* RETS: None. Sets "fVDISK" accordingly * ;* REGS: AX, CX, SI, DI and Flags are clobbered * ;* * ;* INTERNALLY REENTRANT * ;* * ;*--------------------------------------------------------------------------* pVDISK label dword dw 00013h dw 0FFFFh szVDISK db 'VDISK' IsVDISKIn proc near ; Look for "VDISK" starting at the 4th byte of extended memory. call LocalEnableA20 ; Turn on A20 push ds push es ; Set up the comparison. lds si,cs:pVDISK push cs pop es mov di,offset szVDISK mov cx,5 cld rep cmpsb ; Do the comparison pop es ; Restore ES and DS pop ds jz short IVIFoundIt mov [fVDISK],0 ; No VDISK device found jmp short IVIExit ; "VDISK" was found. IVIFoundIt: mov [fVDISK],1 IVIExit: call LocalDisableA20 ret ; Turn off A20 IsVDISKIn endp ;*--------------------------------------------------------------------------* ;* * ;* Int15Handler - * ;* * ;* Hooks Function 88h to return zero as the amount of extended * ;* memory available in the system. * ;* * ;* Hooks Function 87h and preserves the state of A20 across the * ;* block move. * ;* * ;* ARGS: AH = Function, AL = Subfunction * ;* RETS: AX = 0 (if AH == 88h) * ;* REGS: AX is clobbered * ;* * ;*--------------------------------------------------------------------------* I15RegSave dw ? Int15Handler proc far ; Is this a request for the amount of free extended memory? cmp ah,88h jne short I15HCont ; No, continue xor ax,ax ; Yes, return zero iret ; Is it a Block Move? I15HCont: cmp ah,87h jne short I15HNext ; No, continue ; Int 15h Block Move: cli ; Make sure interrupts are off ; Store the A20 line's state. pusha ; Preserve the registers call IsA20On mov cs:[I15RegSave],ax popa ; Restore the registers ; Call the previous Int 15h handler. pushf ; Simualate an interrupt call cs:[PrevInt15] pusha ; Preserve previous handler's return ; Was A20 on before? cmp cs:[I15RegSave],0 je short I15HExit ; No, continue mov ax,1 call cs:[A20Handler] ; Yes, turn A20 back on I15HExit: popa ; Restore the previous handler's return iret ; Continue down the Int 15h chain. I15HNext: jmp cs:[PrevInt15] Int15Handler endp ;*--------------------------------------------------------------------------* ;* * ;* RequestHMA - FUNCTION 01h * ;* * ;* Give caller control of the High Memory Area if it is available. * ;* * ;* ARGS: DX = HMA space requested in bytes * ;* RETS: AX = 1 if the HMA was reserved, 0 otherwise. BL = Error Code * ;* REGS: AX, BX and Flags clobbered * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* RequestHMA proc near cli ; This is a non-reentrant function. ; Flags are restored after the return. mov bl,ERR_HMAINUSE cmp [fHMAInUse],1 ; Is the HMA already allocated? je short RHRetErr mov bl,ERR_HMANOTEXIST cmp [fHMAExists],0 ; Is the HMA available? je short RHRetErr mov bl,ERR_HMAMINSIZE cmp dx,[MinHMASize] ; Is this guy allowed in? jb short RHRetErr mov ax,1 mov [fHMAInUse],al ; Reserve the High Memory Area xor bl,bl ; Clear the error code ret RHRetErr: xor ax,ax ; Return failure with error code in BL ret RequestHMA endp ;*--------------------------------------------------------------------------* ;* * ;* ReleaseHMA - FUNCTION 02h * ;* * ;* Caller is releasing control of the High Memory area * ;* * ;* ARGS: None * ;* RETS: AX = 1 if control is released, 0 otherwise. BL = Error Code * ;* REGS: AX, BX and Flags clobbered * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* ReleaseHMA proc near cli ; This is a non-reentrant function ; Is the HMA currently in use? mov al,[fHMAInUse] or al,al jz short RLHRetErr ; No, return error ; Release the HMA and return success. mov [fHMAInUse],0 mov ax,1 xor bl,bl ret RLHRetErr: xor ax,ax mov bl,ERR_HMANOTALLOCED ret ReleaseHMA endp ;*--------------------------------------------------------------------------* ;* * ;* GlobalEnableA20 - FUNCTION 03h * ;* * ;* Globally enable the A20 line * ;* * ;* ARGS: None * ;* RETS: AX = 1 if the A20 line is enabled, 0 otherwise. BL = Error * ;* REGS: AX, BX and Flags clobbered * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* GlobalEnableA20 proc near cli ; This is a non-reentrant function ; Is A20 already globally enabled? cmp [fGlobalEnable],1 je short GEARet ; Attempt to enable the A20 line. call LocalEnableA20 or ax,ax jz short GEAA20Err ; Mark A20 as globally enabled. mov [fGlobalEnable],1 ; Return success. GEARet: mov ax,1 xor bl,bl ret ; Some A20 error occured. GEAA20Err: mov bl,ERR_A20 xor ax,ax ret GlobalEnableA20 endp ;*--------------------------------------------------------------------------* ;* * ;* GlobalDisableA20 - FUNCTION 04h * ;* * ;* Globally disable the A20 line * ;* * ;* ARGS: None * ;* RETS: AX = 1 if the A20 line is disabled, 0 otherwise. BL = Error * ;* REGS: AX, BX and Flags are clobbered * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* GlobalDisableA20 proc near cli ; This is a non-reentrant function ; Is A20 already globally disabled? cmp [fGlobalEnable],0 je short GDARet ; Attempt to disable the A20 line. call LocalDisableA20 or ax,ax jz short GDAA20Err ; Mark A20 as globally disabled. mov [fGlobalEnable],0 ; Return success. GDARet: mov ax,1 xor bl,bl ret ; Some A20 error occured. GDAA20Err: mov bl,ERR_A20 xor ax,ax ret GlobalDisableA20 endp ;*--------------------------------------------------------------------------* ;* * ;* LocalEnableA20 - FUNCTION 05h * ;* * ;* Locally enable the A20 line * ;* * ;* ARGS: None * ;* RETS: AX = 1 if the A20 line is enabled, 0 otherwise. BL = Error * ;* REGS: AX, BX and Flags clobbered * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* LocalEnableA20 proc near cli ; This is a non-reentrant function cmp [fCanChangeA20],1 ; Can we change A20? jne short LEARet ; No, don't touch A20 ; Only actually enable A20 if the count is zero. cmp [EnableCount],0 jne short LEAIncIt ; Attempt to enable the A20 line. mov ax,1 call [A20Handler] ; Call machine-specific A20 handler or ax,ax jz short LEAA20Err LEAIncIt: inc [EnableCount] ; Return success. LEARet: mov ax,1 xor bl,bl ret ; Some A20 error occurred. LEAA20Err: mov bl,ERR_A20 xor ax,ax ret LocalEnableA20 endp ;*--------------------------------------------------------------------------* ;* * ;* LocalDisableA20 - FUNCTION 06h * ;* * ;* Locally disable the A20 line * ;* * ;* ARGS: None * ;* RETS: AX = 1 if the A20 line is disabled, 0 otherwise. BL = Error * ;* REGS: AX, BX and Flags are clobbered * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* LocalDisableA20 proc near cli ; This is a non-reentrant function cmp [fCanChangeA20],0 ; Can we change A20? je short LDARet ; No, don't touch A20 ; Make sure the count's not zero. cmp [EnableCount],0 je short LDAA20Err ; Only actually disable if the count is one. cmp [EnableCount],1 jne short LDADecIt xor ax,ax call [A20Handler] ; Call machine-specific A20 handler or ax,ax jz short LDAA20Err LDADecIt: dec [EnableCount] ; Return success. LDARet: mov ax,1 xor bl,bl ret ; Some A20 error occurred. LDAA20Err: mov bl,ERR_A20 xor ax,ax ret LocalDisableA20 endp ;*--------------------------------------------------------------------------* ;* * ;* IsA20On - FUNCTION 07h * ;* * ;* Returns the state of the A20 line * ;* * ;* ARGS: None * ;* RETS: AX = 1 if the A20 line is enabled, 0 otherwise * ;* REGS: AX, CX, DI, SI and Flags clobbered * ;* * ;* INTERNALLY REENTRANT * ;* * ;*--------------------------------------------------------------------------* LowMemory label dword ; Set equal to 0000:0080 dw 00080h dw 00000h HighMemory label dword dw 00090h ; Set equal to FFFF:0090 dw 0FFFFh IsA20On proc near push ds push es lds si,cs:LowMemory ; Compare the four words at 0000:0080 les di,cs:HighMemory ; with the four at FFFF:0090 mov cx,4 cld repe cmpsw pop es pop ds xor ax,ax jcxz short IAONoWrap ; Are the two areas the same? inc ax ; No, return A20 Enabled IAONoWrap: xor bl,bl ret ; Yes, return A20 Disabled IsA20On endp ;*--------------------------------------------------------------------------* ;* * ;* QueryExtMemory - FUNCTION 08h * ;* * ;* Returns the size of the largest free extended memory block in K * ;* * ;* ARGS: None * ;* RETS: AX = Size of largest free block in K. BL = Error code * ;* DX = Total amount of free extended memory in K * ;* REGS: AX, BX, DX, DI, SI and Flags clobbered * ;* * ;* INTERNALLY REENTRANT * ;* * ;*--------------------------------------------------------------------------* QueryExtMemory proc near ; Init the error code in DL. mov dl,ERR_OUTOMEMORY ; Scan for the largest block marked FREE. xor di,di ; DI = Max. size found so far xor si,si ; SI = Total amount of free memory mov bx,KiddValley mov cx,[cHandles] ; Loop through the handle table QEMLoop: cmp [bx.Flags],FREEFLAG jne short QEMBottom ; Add this free block to the total. add si,[bx.Len] ; CHANGED 7/19/88 ; Is this block larger? mov ax,[bx.Len] cmp di,ax jae short QEMBottom ; CHANGED 7/19/88 ; Yes, save it away. mov di,ax xor dl,dl ; We ain't Out o' Memory QEMBottom: add bx,SIZE Handle loop QEMLoop ; Setup the return. mov ax,di mov bl,dl ; Retrieve the error code mov dx,si ; CHANGED 7/19/88 ret QueryExtMemory endp ;*--------------------------------------------------------------------------* ;* * ;* AllocExtMemory - FUNCTION 09h * ;* * ;* Reserve a block of extended memory * ;* * ;* ARGS: DX = Amount of K being requested * ;* RETS: AX = 1 of successful, 0 otherwise. BL = Error Code * ;* DX = 16-bit handle to the allocated block * ;* REGS: AX, BX, DX and Flags clobbered * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* ; Algorithm - ; ; Scan the Handle Table looking for BOTH an unused handle and ; a free block which is large enough: ; ; 1. If both are found - ; Mark the free block as used and adjust its size. ; Make the unused handle a free block containing the remaining ; unallocated portion of the original free block. ; ; 2. If only an unused handle is found - ; We're out of memory. ; ; 3. If only a properly sized free block is found - ; We only have one handle left. ; Mark the free block as used. The requester gets all of the ; block's memory. ; ; 4. If neither are found - ; We're out of memory. hFreeBlock dw ? hUnusedBlock dw ? AllocExtMemory proc near cli ; This is a non-reentrant function ; Scan the handle table looking for BOTH an unused handle and ; a free block which is large enough. xor ax,ax mov [hFreeBlock],ax mov [hUnusedBlock],ax mov bx,KiddValley mov cx,[cHandles] ; Loop through the handle table ; Have we already found a free block which is large enough? AEMhLoop: cmp [hFreeBlock],0 jne short AEMUnused ; Yes, see if this one is unused ; Is this block free? cmp [bx.Flags],FREEFLAG jne short AEMUnused ; No, see if it is unused ; Is it large enough? cmp dx,[bx.Len] ja short AEMNexth ; No, get the next handle ; Save this guy away. mov [hFreeBlock],bx jmp short AEMBottom AEMUnused: ; Have we already found an unused handle? cmp [hUnusedBlock],0 jne short AEMNexth ; Yes, get the next handle ; Is this block unused? cmp [bx.Flags],UNUSEDFLAG jne short AEMNexth ; No, get the next handle ; Save this guy away. mov [hUnusedBlock],bx ; Have we found what we are looking for? cmp [hFreeBlock],0 je short AEMNexth AEMBottom: cmp [hUnusedBlock],0 jne short AEMGotBoth ; Yes, continue AEMNexth: ; Get the next handle add bx,SIZE Handle loop AEMhLoop ; We are at the end of the handle table and we didn't find both ; things we were looking for. Did we find a free block? mov si,[hFreeBlock] or si,si jnz short AEMRetSuc ; Yes, Case 3 - Alloc the entire block ; Did we find an unused handle? cmp [hUnusedBlock],0 je short AEMOOHandles ; No, Case 4 - We're out of handles ; Case 2 - Is this a request for a zero-length handle? or dx,dx jnz short AEMOOMemory ; No, we're out of memory ; Reserve the zero-length handle. mov si,[hUnusedBlock] mov [hFreeBlock],si jmp short AEMRetSuc AEMGotBoth: ; We're at Case 1 above. ; Mark the free block as used (done below) and adjust its size. ; Make the unused handle a free block containing the remaining ; unallocated portion of the original free block. mov si,[hFreeBlock] mov di,[hUnusedBlock] ; Unused.Base = Old.Base + request mov ax,[si].Base add ax,dx mov [di].Base,ax ; New.Len = request mov ax,dx xchg [si].Len,ax ; Unused.Len = Old.Len - request sub ax,dx mov [di].Len,ax mov [di].Flags,FREEFLAG ; Unused.Flags = FREE AEMRetSuc: ; Complete the allocation. mov [si].Flags,USEDFLAG ; New.Flags = USED mov dx,[hFreeBlock] mov ax,1 xor bl,bl ret AEMOOMemory:mov bl,ERR_OUTOMEMORY jmp short AEMErrRet AEMOOHandles: mov bl,ERR_OUTOHANDLES AEMErrRet: xor ax,ax ; Return failure mov dx,ax ret AllocExtMemory endp ;*--------------------------------------------------------------------------* ;* * ;* ValidateHandle - * ;* * ;* Validates an extended memory block handle * ;* * ;* ARGS: DX = 16-bit handle to the extended memory block * ;* RETS: Carry is set if the handle is valid * ;* REGS: Preserved except the carry flag * ;* * ;*--------------------------------------------------------------------------* ValidateHandle proc near pusha ; Save everything mov bx,dx ; Move the handle into BX ; The handle must be equal to or above "KiddValley". cmp bx,[KiddValley] jb short VHOne ; The handle must not be above "KiddValleyTop". cmp bx,[KiddValleyTop] ja short VHOne ; (The handle-"KiddValley") must be a multiple of a handle's size. sub dx,[KiddValley] mov ax,dx xor dx,dx mov cx,SIZE Handle div cx or dx,dx ; Any remainder? jnz short VHOne ; Yup, it's bad ; Does the handle point to a currently USED block? cmp [bx.Flags],USEDFLAG jne short VHOne ; This handle is not being used. ; The handle looks good to me... popa ; Restore everything stc ; Return success ret VHOne: ; It's really bad. popa ; Restore everything clc ; Return failure ret ValidateHandle endp ;*--------------------------------------------------------------------------* ;* * ;* FreeExtMemory - FUNCTION 0Ah * ;* * ;* Frees a block of extended memory which was allocated via AllocExt * ;* * ;* ARGS: DX = 16-bit handle to the extended memory block * ;* RETS: AX = 1 if successful, 0 otherwise. BL = Error code * ;* REGS: AX, BX, CX, DX, SI, DI and Flags clobbered * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* FreeExtMemory proc near cli ; This is a non-reentrant function ; Make sure that the handle is valid. call ValidateHandle jnc short FEMBadh mov si,dx ; Move the handle into SI ; Make sure that the handle points to a FREE block. cmp [si].cLock,0 jne short FEMLockedh ; Mark the block as FREE and cancel any locks. mov [si].Flags,FREEFLAG mov [si].cLock,0 ; Is an adjacent block also free? FEMScanIt: mov bx,[si].Base ; BX = Bottom of block mov ax,bx add ax,[si].Len ; AX = Top of block ; Search for an adjacent FREE block. mov di,[KiddValley] ; DI = Handle being scanned mov cx,cHandles ; Loop through the handle table FEMLoopTop: cmp [di].Flags,FREEFLAG ; Is this block free? jne short FEMNexth ; No, continue ; Is this block just above the one we are freeing? mov dx,[di].Base cmp dx,ax je short FEMBlockAbove ; Yes, coaless upwards ; Is it just below? add dx,[di].Len cmp dx,bx je short FEMBlockBelow ; Yes, coaless downwards FEMNexth: add di,SIZE Handle loop FEMLoopTop ; No adjacent blocks to coaless. mov ax,1 ; Return success xor bl,bl ret ; Exchange the pointer to the "upper" and "lower" blocks. FEMBlockBelow: xchg si,di ; Move the free block above into the current handle. FEMBlockAbove: mov dx,[si].Len add dx,[di].Len ; Combine the lengths mov [si].Len,dx mov [di].Flags,UNUSEDFLAG ; Mark old block's handle as UNUSED jmp short FEMScanIt ; Rescan the list FEMBadh: mov bl,ERR_INVALIDHANDLE jmp short FEMErrExit FEMLockedh: mov bl,ERR_EMBLOCKED FEMErrExit: xor ax,ax ; Return failure ret FreeExtMemory endp ;*--------------------------------------------------------------------------* ;* * ;* LockExtMemory - FUNCTION 0Ch * ;* * ;* Locks a block of extended memory * ;* * ;* ARGS: DX = 16-bit handle to the extended memory block * ;* RETS: AX = 1 of successful, 0 otherwise. BL = Error code * ;* DX:BX = 32-bit linear address of the base of the memory block * ;* REGS: AX, BX, DX and Flags clobbered * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* LockExtMemory proc near cli ; This is a non-reentrant function ; Is the handle valid? call ValidateHandle jnc short LEMBadh mov bx,dx ; Move the handle into BX ; Are we at some preposterously large limit? cmp [bx.cLock],0FFh je short LEMOverflow ; Lock the block. inc [bx.cLock] ; Return the 32-bit address of its base. mov dx,[bx.Base] mov bx,dx shr dx,6 shl bx,10 ; Return success. mov ax,1 ret LEMBadh: mov bl,ERR_INVALIDHANDLE jmp short LEMErrExit LEMOverflow:mov bl,ERR_LOCKOVERFLOW LEMErrExit: xor ax,ax ; Return failure mov dx,ax ret LockExtMemory endp ;*--------------------------------------------------------------------------* ;* * ;* UnlockExtMemory - FUNCTION 0Dh * ;* * ;* Unlocks a block of extended memory * ;* * ;* ARGS: DX = 16-bit handle to the extended memory block * ;* RETS: AX = 1 if successful, 0 otherwise. BL = Error code * ;* REGS: AX, BX and Flags clobbered * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* UnlockExtMemory proc near cli ; This is a non-reentrant function ; Is the handle valid? call ValidateHandle jnc short UEMBadh mov bx,dx ; Move the handle into BX ; Does the handle point to a locked block? cmp [bx.cLock],0 je short UEMUnlocked ; No, return error ; Unlock the block. dec [bx.cLock] mov ax,1 ; Return success xor bl,bl ret UEMUnlocked:mov bl,ERR_EMBUNLOCKED jmp short UEMErrExit UEMBadh: mov bl,ERR_INVALIDHANDLE UEMErrExit: xor ax,ax ret UnlockExtMemory endp ;*--------------------------------------------------------------------------* ;* * ;* GetExtMemoryInfo - FUNCTION 0Eh * ;* * ;* Gets other information about an extended memory block * ;* * ;* ARGS: DX = 16-bit handle to the extended memory block * ;* RETS: AX = 1 if successful, 0 otherwise. BL = Error code * ;* BH = EMB's lock count * ;* BL = Total number of unused handles in the system * ;* DX = EMB's length * ;* REGS: AX, BX, CX, DX and Flags clobbered * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* GetExtMemoryInfo proc near cli ; This is a non-reentrant function ; Is the handle valid? call ValidateHandle jnc short GEMBadh mov si,dx ; Move the handle into SI ; Count the number of handles which are not currently being used. xor al,al mov bx,[KiddValley] mov cx,[cHandles] ; Loop through the handle table GEMLoop: cmp [bx.Flags],USEDFLAG ; Is this handle in use? je short GEMNexth ; Yes, continue inc al ; No, increment the count GEMNexth: add bx,SIZE Handle loop GEMLoop ; Setup the return. mov dx,[si.Len] ; Length in DX mov bh,[si.cLock] ; Lock count in BH mov bl,al mov ax,1 ret GEMBadh: mov bl,ERR_INVALIDHANDLE xor ax,ax ret GetExtMemoryInfo endp ;*--------------------------------------------------------------------------* ;* * ;* ReallocExtMemory - FUNCTION 0Fh * ;* * ;* Reallocates a block of extended memory * ;* * ;* ARGS: DX = 16-bit handle to the extended memory block * ;* RETS: AX = 1 if successful, 0 otherwise. BL = Error code * ;* REGS: * ;* * ;* INTERNALLY NON-REENTRANT * ;* * ;*--------------------------------------------------------------------------* ; FUNCTION NOT YET IMPLEMENTED ;*--------------------------------------------------------------------------* ;* * ;* NOTE: RequestUMB and ReleaseUMB will not be implemented by HIMEM. * ;* * ;*--------------------------------------------------------------------------* ;*--------------------------------------------------------------------------* ;* * ;* MoveExtMemory - FUNCTION 0Bh * ;* * ;* Copys a block of memory from anywhere to anywhere * ;* * ;* ARGS: ES:SI = Pointer to an Extended Memory Block Move Structure * ;* (NOTE: Originally passed in as DS:SI) * ;* RETS: AX = 1 of successful, 0 otherwise. BL = Error code. * ;* REGS: Everybody clobbered * ;* * ;* INTERNALLY REENTRANT (believe it or not) * ;* * ;*--------------------------------------------------------------------------* EVEN ; Must be word aligned. MoveBlock: MEM3_Data label byte ; Start of MoveBlock data MoveBlock286: include xm286.asm EndMoveBlock286: ;*--------------------------------------------------------------------------* MoveBlock386: include xm386.asm EndMoveBlock386: ;**************************************************************************** ;* * ;* A20 Handler Section: * ;* * ;* The Init code copies the proper A20 Handler in place just below the * ;* proper MoveBlock routine. * ;* * ;* NOTE: A20 HANDLERS MUST ONLY HAVE RELATIVE JUMPS! HOWEVER ANY CALLS TO * ;* FUNCTIONS OUTSIDE OF THE HANDLER MUST BE NON-RELATIVE! The best * ;* method is to call thru a variable such as ControlJumpTable[n]. * ;* * ;**************************************************************************** ;*--------------------------------------------------------------------------* ;* * ;* AT_A20Handler - HARDWARE DEP. * ;* * ;* Enable/Disable the A20 line on non-PS/2 machines * ;* * ;* ARGS: AX = 0 for Disable, 1 for Enable * ;* RETS: AX = 1 for success, 0 otherwise * ;* REGS: AX, CX and Flags clobbered * ;* * ;*--------------------------------------------------------------------------* AT_A20Handler proc near or ax,ax jz short AAHDisable AAHEnable: call Sync8042 ; Make sure the Keyboard Controller is Ready jnz short AAHErr mov al,0D1h ; Send D1h out 64h,al call Sync8042 jnz short AAHErr mov al,0DFh ; Send DFh out 60h,al call Sync8042 jnz short AAHErr ; Wait for the A20 line to settle down (up to 20usecs) mov al,0FFh ; Send FFh (Pulse Output Port NULL) out 64h,al call Sync8042 jnz short AAHErr jmp short AAHExit AAHDisable: call Sync8042 ; Make sure the Keyboard Controller is Ready jnz short AAHErr mov al,0D1h ; Send D1h out 64h,al call Sync8042 jnz short AAHErr mov al,0DDh ; Send DDh out 60h,al call Sync8042 jnz short AAHErr ; Wait for the A20 line to settle down (up to 20usecs) mov al,0FFh ; Send FFh (Pulse Output Port NULL) out 64h,al call Sync8042 AAHExit: mov ax,1 ret AAHErr: xor ax,ax ret AT_A20Handler endp ;*--------------------------------------------------------------------------* Sync8042 proc near xor cx,cx S8InSync: in al,64h and al,2 loopnz S8InSync ret Sync8042 endp EndAT_A20Handler: ;*--------------------------------------------------------------------------* ;* * ;* PS2_A20Handler - HARDWARE DEP. * ;* * ;* Enable/Disable the A20 line on PS/2 machines * ;* * ;* ARGS: AX = 0 for Disable, 1 for Enable * ;* RETS: AX = 1 for success, 0 otherwise * ;* REGS: AX, CX and Flags clobbered * ;* * ;*--------------------------------------------------------------------------* PS2_PORTA equ 0092h PS2_A20BIT equ 00000010b PS2_A20Handler proc near or ax,ax jz short PAHDisable PAHEnable: in al,PS2_PORTA ; Get the current A20 state test al,PS2_A20BIT ; Is A20 already on? jnz short PAHErr or al,PS2_A20BIT ; Turn on the A20 line out PS2_PORTA,al xor cx,cx ; Make sure we loop for awhile PAHIsItOn: in al,PS2_PORTA ; Loop until the A20 line comes on test al,PS2_A20BIT loopz PAHIsItOn jz short PAHErr ; Unable to turn on the A20 line jmp short PAHExit PAHDisable: in al,PS2_PORTA ; Get the current A20 state and al,NOT PS2_A20BIT ; Turn off the A20 line out PS2_PORTA,al xor cx,cx ; Make sure we loop for awhile PAHIsItOff: in al,PS2_PORTA ; Loop until the A20 line goes off test al,PS2_A20BIT loopnz PAHIsItOff jnz short PAHErr ; Unable to turn off the A20 line PAHExit: mov ax,1 ret PAHErr: xor ax,ax ret PS2_A20Handler endp EndPS2_A20Handler: ;*--------------------------------------------------------------------------* ;* * ;* $6300Plus_A20Handler - HARDWARE DEP. * ;* * ;* Enable/Disable address lines A20-A23 on AT&T 6300 Plus * ;* * ;* ARGS: AX = 0 for Disable, 1 for Enable * ;* RETS: AX = 1 for success, 0 otherwise * ;* REGS: AX, BX, and Flags clobbered * ;* * ;* Note: Don't want to do two back to back disables on PLUS, * ;* so we call IsA20On to see if it is necessary. * ;* Warning: The calcuation of the ReturnBack label depends on the * ;* expectation that this routine is being moved at init time. * ;* * ;*--------------------------------------------------------------------------* PLUS_PORT equ 03F20h PLUS_STATUS equ 03FA0h PLUS_SET equ 10000000b ; Turn on A20-A23 PLUS_RESET equ 00010000b ; Turn off A20-A23 and point to our routine $6300PLUS_A20Handler proc near mov bx,ax push dx mov dx,PLUS_STATUS in al,dx pop dx and ax,1 cmp ax,bx jne short $6AHEnable mov ax,1 ret ; No, just return $6AHEnable: pushf sti mov al,PLUS_SET or bx,bx ; Zero if disable jnz short $6AHNext mov al,PLUS_RESET $6AHNext: push dx ; Set/reset the port mov dx,PLUS_PORT out dx,al pop dx or bx,bx jnz short $6AHNext1 call $6300Reset ; Reset the processor $6AHNext1: popff mov ax,1 ret $6300Plus_A20Handler endp ;*--------------------------------------------------------------------------* ;* * ;* $6300Reset - HARDWARE DEP. * ;* * ;* Reset the 80286 in order to turn off the address lines on the 6300 PLUS. * ;* This is the only way to do this on the current hardware. * ;* The processor itself is reset by reading or writing port 03F00h. * ;* * ;* Uses flags. * ;* * ;*--------------------------------------------------------------------------* $6300Reset proc near pusha ; Save world push ds ; Save segments push es mov ax,BiosSeg ; Point to the BIOS segment mov ds,ax ; ds -> 40h ; Setup the reset return address. assume ds:nothing push word ptr ds:[RealLoc1] ; Save what might have been here push word ptr ds:[RealLoc1+2] ; Load our return address, remembering that we will be relocated ; at init time. mov word ptr ds:[RealLoc1+2],cs mov ax,cs:[A20Handler] add ax,offset ReturnBack-offset $6300Plus_A20Handler mov word ptr ds:[RealLoc1],ax mov cs:[OldStackSeg],ss ; Save the stack segment, too ; Reset the processor - turning off A20 in the process. mov dx,03F00h in ax,dx ; We shouldn't get here. Halt the machine if we do. nop nop nop nop cli hlt ReturnBack: mov ss,cs:[OldStackSeg] ; Start the recovery pop word ptr ds:[RealLoc1+2] ; ROM code has set ds->40h pop word ptr ds:[RealLoc1] pop es pop ds assume ds:code xor al,al mov dx,PLUS_PORT out dx,al popa ret $6300Reset endp End6300Plus_Handler: ;*--------------------------------------------------------------------------* ;* * ;* HP_A20Handler - HARDWARE DEP. * ;* * ;* Enable/Disable the A20 line on HP Vectra machines * ;* * ;* ARGS: AX = 0 for Disable, 1 for Enable * ;* RETS: AX = 1 for success, 0 otherwise * ;* REGS: AX, CX and Flags clobbered * ;* * ;*--------------------------------------------------------------------------* HP_A20Handler proc near or ax,ax jz short HAHDisable HAHEnable: call HPSync8042 ; Make sure the Keyboard Controller is ready jnz short HAHErr mov al,0DFh ; Send DFh out 64h,al call HPSync8042 jnz short HAHErr mov al,0DFh ; Send second DFh out 64h,al call HPSync8042 jnz short HAHErr jmp short HAHExit HAHDisable: call HPSync8042 ; Make sure the Keyboard Controller is Ready jnz short HAHErr mov al,0DDh ; Send DDh out 64h,al call HPSync8042 jnz short HAHErr mov al,0DDh ; Send second DDh out 64h,al call HPSync8042 jnz short HAHErr HAHExit: mov ax,1 ret HAHErr: xor ax,ax ret HP_A20Handler endp ;*--------------------------------------------------------------------------* HPSync8042 proc near xor cx,cx H8InSync: in al,64h and al,2 loopnz H8InSync ret HPSync8042 endp EndHP_A20Handler: ;*--------------------------------------------------------------------------* ;* * ;* Extended Memory Handle Table - * ;* * ;*--------------------------------------------------------------------------* ; Actually, the handle table will be located just after the end of whichever ; A20 Handler is installed and will overwrite the others. In large cases ; however, it could extend beyond all of these guys and crunch the Init code. ; Hence this buffer. If the driver ever gets over 64K (heaven forbid) this ; scheme should be reworked. ; Guarantee space for all handles. Kinda overkill but any extra will be ; discarded with the Init code. HandleFiller dw MAXHANDLES * SIZE Handle DUP(?) ;**************************************************************************** ;* * ;* NOTE: Code below here will be discarded after driver initialization. * ;* * ;**************************************************************************** ;*--------------------------------------------------------------------------* ;* * ;* InitDriver - * ;* * ;* Called when driver is Initialized. * ;* * ;* ARGS: ES:DI = Address of the Request Header * ;* RETS: AX = 0, pHdr.Address = Bottom of resident driver code * ;* REGS: AX, CX and Flags are clobbered * ;* * ;*--------------------------------------------------------------------------* InitLine dw MoveBlock ; The line which defines what will be dis- ; carded after the driver is initialized InitDriver proc near ; Display the sign on message. mov ah,9h mov dx,offset SignOnMsg int 21h ; Is this DOS 3.00 or higher? mov ah,30h int 21h ; Get DOS versions number cmp al,3 jae short IDCheckXMS mov dx,offset BadDOSMsg jmp short IDFlushMe2 ; Is another XMS driver already installed? IDCheckXMS: mov ax,4300h int 2Fh cmp al,80h ; Is INT 2F hooked? jne short IDNotInYet mov dx,offset NowInMsg jmp short IDFlushMe2 ; What kind of processor are we on? IDNotInYet: call MachineCheck cmp ax,1 ; Is it an 8086/8088? jne short IDProcOK mov dx,offset On8086Msg jmp short IDFlushMe ; Is there any Compaq BIM? IDProcOK: call GetBIMMemory or ax,ax jz short IDNoBIM ; How much extended memory is installed? call GetInt15Memory cmp ax,64 ; Is there >= 64K of extended? jb short IDNoHMA ; Nope - No HMA jmp short IDHMAOK ; Yup - continue ; How much extended memory is installed? IDNoBIM: call GetInt15Memory cmp ax,64 ; Is there >= 64K of extended? jae short IDHMAOK ; Yup - continue or ax,ax ; Is there any extended? jnz short IDNoHMA ; Yup - No HMA ; We can't find any memory to manage. mov dx,offset NoExtMemMsg IDFlushMe2: jmp short IDFlushMe ; There isn't enough for the HMA, but there is some extended ; memory which we can manage. IDNoHMA: mov dx,offset NoHMAMsg mov ah,9h int 21h jmp short IDFinalInit IDHMAOK: mov [fHMAMayExist],1 ; Install the proper MoveBlock function. IDFinalInit:call InstallMoveBlock ; Install the proper A20 handler. call InstallA20 ; Was A20 on already? call IsA20On xor ax,1 mov [fCanChangeA20],al ; Yes, don't ever disable it cmp al,0 jne short IDDiddler ; Display the "A20 Already On" message. mov dx,offset A20OnMsg mov ah,9h ; CHANGED - 8/9/88 int 21h ; Can we successfully diddle A20? CHANGED - 8/9/88 IDDiddler: call LocalEnableA20 ; Try to enable A20 or ax,ax ; Did we do it? jz short IDBadA20 ; Nope - phoosh ourselves call IsA20On ; Is A20 on? or ax,ax jz short IDBadA20 ; Nope - phoosh ourselves call LocalDisableA20 ; Try to disable A20 or ax,ax ; Did we do it? jnz short IDA20Cont ; Yup - continue cmp [fCanChangeA20],1 ; Has A20 been permenantly enabled? je short IDGetParms ; Yup - continue jmp short IDBadA20 ; Nope - phoosh ourselves IDA20Cont: call IsA20On ; Is A20 off? or ax,ax jz short IDGetParms ; Yup - continue ; A20 ain't doing it. IDBadA20: mov dx,offset BadA20Msg ; Display the message in DX followed by the "Flush" message. IDFlushMe: mov ah,9h int 21h mov dx,offset FlushMsg mov ah,9h int 21h ; Discard the entire driver. xor ax,ax mov [InitLine],ax les di,[pReqHdr] mov es:[di.Units],al jmp short IDReturn ; Process the command line parameters. IDGetParms: call GetParms ; Initialize the Handle Table. call InitHandles ; Display the success messages. cmp [fHMAMayExist],0 je short IDIgnition mov dx,offset HMAOKMsg mov ah,9h int 21h ; "Turn On" the driver. IDIgnition: call HookInt2F ; Discard the initialization code. IDReturn: les di,[pReqHdr] mov ax,[InitLine] mov word ptr es:[di.Address][0],ax mov word ptr es:[di.Address][2],cs ; Return success. xor ax,ax ret InitDriver endp ;*--------------------------------------------------------------------------* ;* * ;* HookInt2F - * ;* * ;* Insert the INT 2F hook * ;* * ;* ARGS: None * ;* RETS: None * ;* REGS: AX, SI, ES and Flags are clobbered * ;* * ;* EXTERNALLY NON-REENTRANT * ;* Interrupts must be disabled before calling this function. * ;* * ;*--------------------------------------------------------------------------* HookInt2F proc near ; Save the current INT 2F vector cli xor ax,ax mov es,ax mov si,2Fh * 4 ; ES:SI = Address of 2F vector ; Exchange the old vector with the new one mov ax,offset Int2FHandler xchg ax,es:[si][0] mov word ptr [PrevInt2F][0],ax mov ax,cs xchg ax,es:[si][2] mov word ptr [PrevInt2F][2],ax sti ret HookInt2F endp ;*--------------------------------------------------------------------------* ;* * ;* MachineCheck - * ;* * ;* Determines the CPU type. * ;* * ;* ARGS: None * ;* RETS: AX = 1 if we're on an 8086/8088 or an 80186, 0 otherwise * ;* REGS: AX and Flags are clobbered * ;* * ;*--------------------------------------------------------------------------* ; NOTE: This is the "official" Intel method for determining CPU type. MachineCheck proc near xor ax,ax ; Move 0 into the Flags register push ax popf pushf ; Try and get it back out pop ax and ax,0F000h ; If the top four bits are set... cmp ax,0F000h je short MC_8086 ; ...it's an 8086 machine mov ax,0F000h ; Move F000h into the Flags register push ax popf pushf ; Try and get it back out pop ax and ax,0F000h ; If the top four bits aren't set... jz short MC_80286 ; ...it's an 80286 machine ; We're on an 80386 machine mov ax,3 ret MC_80286: ; We're on an 80286 machine mov ax,2 ret MC_8086: ; We're on an 8086 machine mov ax,1 ret MachineCheck endp ;*--------------------------------------------------------------------------* ;* * ;* GetBIMMemory - * ;* * ;* Look for Compaq 'Built In Memory' and add it to the pool of * ;* available memory * ;* * ;* ARGS: None * ;* RETS: AX = Amount of BIM memory found * ;* REGS: AX, BX, CX, and Flags are clobbered * ;* * ;*--------------------------------------------------------------------------* ; "Built In Memory" (BIM) starts at FE00:0000h and grows downward. It is ; controlled by a data structure at F000:FFE0h. Changing the data structure ; involves un-write-protecting the ROMs (!) by flipping bit 1 of 80C00000. pBIMSTRUCT equ 0FFE0h AVAILABLE equ 0 ; Set to -1 if BIM isn't around TOTALBIM equ 2 ; Total amount of BIM in the system AVAILBIM equ 4 ; Amount of BIM available in paragraphs LASTUSED equ 6 ; Paragraph address of last (lowest) used ; paragraph of BIM pCOMPAQ label dword dw 0FFE8h dw 0F000h szCOMPAQ db '03COMPAQ' pRAMRELOC label dword dw 00000h dw 080C0h GDT_TYPE struc dw 0,0,0,0 dw 0,0,0,0 S_LIMIT dw 1 S_BASE_LOW dw 0 S_BASE_HI db 0 S_RIGHTS db 93h S_RESERVED dw 0 D_LIMIT dw 1 D_BASE_LOW dw 0000h D_BASE_HI db 0C0h D_RIGHTS db 93h D_RES386 db 0 D_BASE_XHI db 080h dw 0,0,0,0 dw 0,0,0,0 GDT_TYPE ends BIMGDT GDT_TYPE <> BIMBuffer dw ? GetBIMMemory proc near xor ax,ax ; Are we on a Compaq 386 machine? push es ; Set up the comparison. les di,cs:pCOMPAQ mov si,offset szCOMPAQ mov cx,8 cld rep cmpsb ; Do the comparison jne short FCMNoMem2 ; Nope, return ; Is there a 32-bit memory board installed? mov bx,pBIMSTRUCT mov bx,es:[bx] ; De-reference the pointer mov dx,es:[bx+AVAILABLE] ; -1 means no board is installed inc dx jz short FCMNoMem2 ; Nope, return ; How much memory is available and where does it start? mov dx,es:[bx+AVAILBIM] ; Size in paragraphs or dx,dx ; Any left? jz short FCMNoMem mov cx,dx ; CX = Size in paragraphs mov ax,es:[bx+LASTUSED] sub ax,cx ; AX = Starting location - F0000h ; in paragraphs push es ; Save for a rainy day... push bx push ax ; Change AX to the starting location in K. shr ax,4 add ax,0F000h shr ax,2 ; Change CX to the size in K. shr cx,6 ; Store away for use by HookInt15(). mov [BIMBase],ax mov [BIMLength],cx ; Un-WriteProtect the ROMs. mov si,offset BIMGDT ; Set up the BlockMove GDT mov ax,cs mov es,ax mov cx,16 mul cx add ax,offset BIMBuffer adc dl,0 mov [si.S_BASE_LOW],ax mov [si.S_BASE_HI],dl mov cx,1 mov word ptr [BIMBuffer],0FEFEh ; FEh unlocks the ROMs mov ah,87h ; Do the BlockMove int 15h or ah,ah ; Was there an error? jz short FCMReserve ; Nope - continue ; Return error. pop ax ; Clean up pop bx pop es xor ax,ax mov [BIMBase],ax mov [BIMLength],ax FCMNoMem2: jmp short FCMNoMem ; Change the ROM values to reserve the BIM stuff. FCMReserve: pop ax pop bx pop es mov word ptr es:[bx+AVAILBIM],0 ; Reserve all remaining BIM mov word ptr es:[bx+LASTUSED],ax ; Re-WriteProtect the ROMs. push cs pop es mov si,offset BIMGDT ; Set up the BlockMove GDT mov word ptr [BIMBuffer],0FCFCh ; FCh unlocks the ROMs mov ah,87h ; Do the BlockMove int 15h mov ax,1 ; Return success FCMNoMem: pop es ret GetBIMMemory endp ;*--------------------------------------------------------------------------* ;* * ;* GetInt15Memory - * ;* * ;* Returns the amount of memory INT 15h, Function 88h says is free * ;* * ;* ARGS: None * ;* RETS: AX = Amount of free extended memory in K-bytes * ;* REGS: AX and Flags are clobbered * ;* * ;*--------------------------------------------------------------------------* GetInt15Memory proc near ; Check for 6300 Plus (to set "MemCorr"). call Is6300Plus ; Get the amount of extended memory Int 15h says is around. mov ah,88h clc int 15h ; Is Function 88h around? jnc short GIM100 xor ax,ax ; No, return 0 ret GIM100: sub ax,[MemCorr] ; Compensate for 6300 Plus machines ret GetInt15Memory endp ;*--------------------------------------------------------------------------* ;* * ;* InstallMoveBlock - HARDWARE DEP. * ;* * ;* Attempt to install the proper MoveBlock function. * ;* * ;* ARGS: None * ;* RETS: None * ;* REGS: AX, CX, DI, SI, ES and Flags are clobbered * ;* * ;*--------------------------------------------------------------------------* InstallMoveBlock proc near ; Are we on a 386 machine? call MachineCheck cmp ax,3 je short IMBOn386 ; Yes, install the 386 routine ; Install the 286 MoveBlock routine. mov [InitLine],offset EndMoveBlock286 ret ; Install the 386 MoveBlock routine. IMBOn386: mov si,offset MoveBlock386 mov cx,(offset EndMoveBlock386 - offset MoveBlock386) ; REP MOV the routine into position. cld push cs pop es mov di,offset MoveBlock add [InitLine],cx rep movsb call InitMoveBlock386 ret InstallMoveBlock endp ;*--------------------------------------------------------------------------* ;* * ;* InitMoveBlock386 - HARDWARE DEP. * ;* * ;* Initializes the 386 MoveBlock routine * ;* * ;* ARGS: None * ;* RETS: None * ;* REGS: AX, CX, DX and Flags are clobbered * ;* * ;*--------------------------------------------------------------------------* InitMoveBlock386 proc near mov ax,cs ; dx has CS of codeg mov [patch3],ax ; Patch code mov cx,16 mul cx mov [descCS].LO_apDesc386,ax ; Set up selector for our CS mov [descCS].MID_apDesc386,dl add ax,offset code:OurGDT ; Calculate Base of GDT adc dx,0 mov [GDTPtr.LO_apBaseGdt],ax mov [GDTPtr.HI_apBaseGdt],dx mov ax,offset code:MoveExtended386+MEM3_Offset mov ControlJumpTable[0Bh*2],ax ret InitMoveBlock386 endp ;*--------------------------------------------------------------------------* ;* * ;* InstallA20 - HARDWARE DEP. * ;* * ;* Attempt to install the proper A20 Handler * ;* * ;* ARGS: None * ;* RETS: None * ;* REGS: AX, CX, DI, SI, ES and Flags are clobbered * ;* * ;*--------------------------------------------------------------------------* InstallA20 proc near ; Are we on a 6300 Plus? call Is6300Plus or ax,ax jz short IAChkPS2 ; Yes, relocate 6300 Plus A20 handler. mov si,offset $6300PLUS_A20Handler mov cx,(offset End6300PLUS_Handler - offset $6300PLUS_A20Handler) jmp short IAMoveIt ; Are we on a PS/2? IAChkPS2: call IsPS2Machine cmp ax,1 jne short IACheckHP ; Yes, relocate the PS/2 A20 handler. mov si,offset PS2_A20Handler mov cx,(offset EndPS2_A20Handler - offset PS2_A20Handler) jmp short IAMoveIt ; Are we on a HP Vectra? IACheckHP: call IsHPMachine cmp ax,1 jne short IAOnAT ; Yes, relocate the HP A20 handler. mov si,offset HP_A20Handler mov cx,(offset EndHP_A20Handler - offset HP_A20Handler) jmp short IAMoveIt IAOnAT: mov si,offset AT_A20Handler mov cx,(offset EndAT_A20Handler - offset AT_A20Handler) ; REP MOV the proper handler into position. IAMoveIt: cld push cs pop es mov di,[InitLine] mov [A20Handler],di add [InitLine],cx rep movsb ret InstallA20 endp ;*--------------------------------------------------------------------------* ;* * ;* GetParms - * ;* * ;* Get any parameters off of the HIMEM command line * ;* * ;* ARGS: None * ;* RETS: None * ;* REGS: AX, BX, CX, DX, DI, SI, ES and Flags clobbered * ;* * ;* Side Effects: cHandles and MinHMASize may be changed * ;* * ;*--------------------------------------------------------------------------* GPRegSave dw ? GetParms proc near push ds cld les di,[pReqHdr] lds si,es:[di.pCmdLine] ; DS:SI points to first char ; after "DEVICE=" ; Scan until we see a frontslash or the end of line. GPBadParm: GPNextChar: lodsb cmp al,'/' je short GPGotOne cmp al,13 jne short GPNextChar GPDatsAll: pop ds ret ; Save what we found and get the number after it. GPGotOne: lodsb mov cs:[GPRegSave],ax ; Scan past the rest of the parm for a number, EOL, or a space. GPNeedNum: lodsb cmp al,' ' je short GPBadParm cmp al,13 je short GPBadParm cmp al,'0' jb short GPNeedNum cmp al,'9' ja short GPNeedNum ; Read the number at DS:SI into DX. xor dx,dx GPNumLoop: sub al,'0' cbw add dx,ax lodsb cmp al,' ' je short GPNumDone cmp al,13 je short GPNumDone cmp al,'0' jb short GPBadParm cmp al,'9' ja short GPBadParm shl dx,1 ; Stupid multiply DX by 10 mov bx,dx shl dx,1 shl dx,1 add dx,bx jmp short GPNumLoop ; Which parameter are we dealing with here? GPNumDone: xchg ax,cs:[GPRegSave] cmp al,'H' ; HMAMIN= parameter? je short GPGotMin cmp al,'N' ; NUMHANDLES= parameter? jne short GPBadParm ; Process /NUMHANDLES= parameter. GPGotHands: cmp dx,MAXHANDLES ja short GPBadParm mov cs:[cHandles],dx ; Print descriptive message. mov dx,offset StartMsg call GPPrintIt mov ax,cs:[cHandles] call GPPrintAX mov dx,offset HandlesMsg call GPPrintIt jmp short GPNextParm ; Process /HMAMIN= parameter. GPGotMin: cmp dx,64 ja short GPBadParm push dx mov cs:[MinHMASize],dx ; Print a descriptive message. mov dx,offset HMAMINMsg call GPPrintIt mov ax,cs:[MinHMASize] call GPPrintAX mov dx,offset KMsg call GPPrintIt pop dx mov cl,10 ; Convert from K to bytes shl dx,cl mov cs:[MinHMASize],dx ; Were we at the end of the line? GPNextParm: mov ax,cs:[GPRegSave] cmp al,13 je short GPExit jmp GPNextChar GPExit: pop ds ret GetParms endp ;*--------------------------------------------------------------------------* GPPrintIt proc near push ds ; Save current DS push cs ; Set DS=CS pop ds mov ah,9h int 21h pop ds ; Restore DS ret GPPrintIt endp ;*--------------------------------------------------------------------------* GPPrintAX proc near mov cx,10 xor dx,dx div cx or ax,ax jz short GPAPrint push dx call GPPrintAX pop dx GPAPrint: add dl,'0' mov ah,2h int 21h ret GPPrintAX endp ;*--------------------------------------------------------------------------* ;* * ;* InitHandles - * ;* * ;* Initialize the Extended Memory Handle Table * ;* * ;* ARGS: None * ;* RETS: None * ;* REGS: AX, BX, CX, and Flags are clobbered * ;* * ;*--------------------------------------------------------------------------* InitHandles proc near ; Allocate room for the Handle table at the end. mov ax,[InitLine] mov [KiddValley],ax mov ax,SIZE Handle mov cx,[cHandles] mul cx add [InitLine],ax ; Init the Handle table. mov bx,KiddValley xor ax,ax mov cx,[cHandles] IHTabLoop: mov [bx.Flags],UNUSEDFLAG mov [bx.cLock],al mov [bx.Base],ax mov [bx.Len],ax add bx,SIZE Handle loop IHTabLoop ; Store away the top of the table for handle validation. mov [KiddValleyTop],bx ret InitHandles endp ;*--------------------------------------------------------------------------* ;* * ;* Is6300Plus HARDWARE DEP. * ;* * ;* Check for AT&T 6300 Plus * ;* * ;* ARGS: None * ;* RETS: AX = 1 if we're on an AT&T 6300 Plus, 0 otherwise * ;* REGS: AX, flags used. * ;* * ;* Side Effects: MemCorr value updated to 384 if necessary. * ;* * ;*--------------------------------------------------------------------------* Is6300Plus proc near xor ax,ax push bx mov bx,0FC00h ; Look for 'OL' at FC00:50 mov es,bx cmp es:[0050h],'LO' jne short I6PNotPlus ; Not found mov bx,0F000h mov es,bx cmp word ptr es:[0FFFDh],0FC00h ; Look for 6300 PLUS jne short I6PNotPlus in al,66h ; Look for upper extended memory and al,00001111b cmp al,00001011b jne short I6PNoMem mov [MemCorr],384 ; Save value I6PNoMem: mov ax,1 ; We found a PLUS I6PNotPlus: pop bx ret Is6300Plus endp ;*--------------------------------------------------------------------------* ;* * ;* IsPS2Machine HARDWARE DEP. * ;* * ;* Check for PS/2 machine * ;* * ;* ARGS: None * ;* RETS: AX = 1 if we're on a valid PS/2 machine, 0 otherwise * ;* REGS: AX and Flags clobbered * ;* * ;*--------------------------------------------------------------------------* IsPS2Machine proc near mov ah,0C0h ; Get System Description Vector stc int 15h jc short IPMNoPS2 ; Error? Not a PS/2. ; Do we have a "Micro Channel" computer? mov al,byte ptr es:[bx+5] ; Get "Feature Information Byte 1" test al,00000010b ; Test the "Micro Channel Implemented" bit jz short IPMNoPS2 IPMFoundIt: xor ax,ax ; Disable A20. Fixes PS2 Ctl-Alt-Del bug call PS2_A20Handler mov ax,1 ret IPMNoPS2: xor ax,ax ret IsPS2Machine endp ;*--------------------------------------------------------------------------* ;* * ;* IsHPMachine HARDWARE DEP. * ;* * ;* Check for HP Vectra Machine * ;* * ;* ARGS: None * ;* RETS: AX = 1 if we're on a HP Vectra machine, 0 otherwise * ;* REGS: AX, ES and Flags clobbered * ;* * ;*--------------------------------------------------------------------------* IsHPMachine proc near mov ax,0F000h mov es,ax mov ax,word ptr es:[0F8h] cmp ax,'PH' je short IHMFoundIt xor ax,ax ret IHMFoundIt: mov ax,1 ret IsHPMachine endp ;*--------------------------------------------------------------------------* ;* DRIVER MESSAGES * ;*--------------------------------------------------------------------------* SignOnMsg db 13,10,'HIMEM: DOS XMS Driver, Version 2.03 - 8/09/88' db 13,10,'Copyright 1988, Microsoft Corp.' db 13,10,'$' BadDOSMsg db 13,10,'ERROR: HIMEM.SYS requires DOS 3.00 or higher.$' NowInMsg db 13,10,'ERROR: An Extended Memory Manager is already installed.$' On8086Msg db 13,10,'ERROR: HIMEM.SYS requires an 80x86-based machine.$' NoExtMemMsg db 13,10,'ERROR: No available extended memory was found.$' BadA20Msg db 13,10,'ERROR: Unrecognized A20 hardware.$' FlushMsg db 13,10,' XMS Driver not installed.',13,10,13,10,'$' StartMsg db 13,10,'$' HandlesMsg db ' extended memory handles available.$' HMAMINMsg db 13,10,'Minimum HMA size set to $' KMsg db 'K.$' NoHMAMsg db 13,10,'WARNING: The High Memory Area is unavailable.',13,10,'$' A20OnMsg db 13,10,'WARNING: The A20 Line was already enabled.',13,10,'$' HMAOKMsg db 13,10,'64K High Memory Area is available.',13,10,13,10,'$' db 'This program is the property of Microsoft Corporation.' db 64 dup(0) ; For internationalization code ends end ;=[END HIMEM.ASM]============================================================ ;=[BEGIN XM286.ASM]========================================================== ;******************************************************************************* ; ; MoveExtended286 ; XMM Move Extended Memory Block for the 80286 ; Use Int 15h, Block Move ; ; Entry: ; ES:BX Points to a MoveExtendedStruc ; ; Return: ; AX = 1 Success ; AX = 0 Failure ; Error Code in BL ; ; Registers Destroyed: ; Flags ; ; WARNING ; ======= ; ; This routine enables interrupts and can be re-entered ; ; Notes: ; The case of copying from conventional to conventional memory ; is not treated specially in this example. ; ; History: ; Wed Jul 13 - AWG - Original version ;------------------------------------------------------------------------------- ifndef XM286INCLUDED public MoveExtMemory endif MoveExtMemory proc near sti ; Be nice push bp ; Set up stack frame so we mov bp, sp ; can have local variables sub sp, 18+(6*8) ; Space for local variables Count = -4 ; Local DWORD for byte count MEReturn = -6 ; Local WORD for return code SrcHandle = -8 DstHandle = -10 SrcLinear = -14 DstLinear = -18 GDT = -18-(6*8) ; Space for 6 GDT entries pusha push ds push es xor ax, ax mov [bp.MEReturn], ax ; Assume success mov [bp.SrcHandle], ax mov [bp.DstHandle], ax mov ax, word ptr es:[si.bCount] ; Pick up length specified mov word ptr [bp.Count], ax mov cx, word ptr es:[si.bCount+2] mov word ptr [bp.Count+2], cx or cx, ax jcxz short MEM2_Exit ; Exit immediately if zero lea bx, [si.SourceHandle] ; Normalize Source call GetLinear286 ; Linear address in DX:AX jc short MEM2_SrcError ; Have Dest Error Code mov word ptr [bp.SrcLinear], ax ; Save Linear address mov word ptr [bp.SrcLinear+2], dx mov [bp.SrcHandle], bx ; Save Handle for Unlock lea bx, [si.DestHandle] ; Normalize Destination call GetLinear286 jc short MEM2_Error mov word ptr [bp.DstLinear], ax ; Save Linear address mov word ptr [bp.DstLinear+2], dx mov [bp.DstHandle], bx ; Save Handle for Unlock shr word ptr [bp.Count+2], 1 ; Make word count rcr word ptr [bp.Count], 1 jc short MEM2_InvCount ; Odd count not allowed ;***********************************************; ; ; ; The XMS Spec states that a reasonable number ; ; of interrupt windows are guaranteed. This ; ; loop should be tuned to provide such. ; ; ; ;-----------------------------------------------; MEM2_MoveLoop: mov cx, 512 ; Must be less than 8000h cmp word ptr [bp.Count+2], 0 ; Lots to do? ja short MEM2_MaxSize cmp word ptr [bp.Count], cx jae short MEM2_MaxSize mov cx, word ptr [bp.Count] ; Just what is left jcxz short MEM2_Exit MEM2_MaxSize: push cx call DoMoveBlock pop cx jc short MEM2_Error sub word ptr [bp.Count], cx ; Subtract what we just did sbb word ptr [bp.Count+2], 0 xor dx, dx ; Get byte count in DX:CX shl cx, 1 rcl dx, 1 add word ptr [bp.SrcLinear], cx adc word ptr [bp.SrcLinear+2], dx add word ptr [bp.DstLinear], cx adc word ptr [bp.DstLinear+2], dx jmp short MEM2_MoveLoop MEM2_Exit: pop es pop ds mov bx, [bp.SrcHandle] ; Unlock Handles if necessary or bx, bx jz short NoSrcHandle dec [bx.cLock] ; Unlock Source NoSrcHandle: mov bx, [bp.DstHandle] or bx, bx jz short NoDstHandle dec [bx.cLock] ; Unlock Destination NoDstHandle: popa ; Restore original registers mov ax, 1 cmp word ptr [bp.MEReturn], 0 jz short MEM2_Success dec ax mov bl, byte ptr [bp.MEReturn] MEM2_Success: mov sp, bp ; Unwind stack pop bp ret MEM2_SrcError: cmp bl, ERR_LENINVALID ; Invalid count je short MEM2_Error ; yes, no fiddle sub bl, 2 ; Convert to Source error code jmp short MEM2_Error MEM2_InvCount: mov bl, ERR_LENINVALID MEM2_Error: mov byte ptr [bp.MEReturn], bl ; Pass error code through jmp short MEM2_Exit ;******************************************************************************* ; ; GetLinear286 ; Convert Handle and Offset (or 0 and SEG:OFFSET) into Linear address ; Locks Handle if necessary ; Nested with MoveExtended286 to access local variables ; ; Entry: ; ES:BX Points to structure containing: ; Handle dw ; Offset dd ; [BP.Count] Count of bytes to move ; ; Return: ; BX Handle of block (0 if conventional) ; AX:DX Linear address ; CARRY => Error ; Error code in BL ; ; Registers Destroyed: ; Flags, CX ; ;------------------------------------------------------------------------------- GetLinear286 proc near push si push di cli ; NO INTERRUPTS mov si, word ptr es:[bx+2] ; Offset from start of handle mov di, word ptr es:[bx+4] ; in DI:SI mov bx, word ptr es:[bx] ; Handle in bx or bx, bx jz short GL2_Conventional test [bx.Flags], USEDFLAG ; Valid Handle? jz short GL2_InvHandle mov ax, [bx.Len] ; Length of Block mov cx, 1024 mul cx ; mul is faster on the 286 sub ax, si sbb dx, di ; DX:AX = max possible count jc short GL2_InvOffset ; Base past end of block sub ax, word ptr [bp.Count] sbb dx, word ptr [bp.Count+2] jc short GL2_InvCount ; Count too big inc [bx.cLock] ; Lock the Handle mov ax, [bx.Base] mul cx add ax, si ; Linear address adc dx, di ; in DX:AX GL2_OKExit: clc GL2_Exit: sti pop di pop si ret GL2_Conventional: mov ax, di ; Convert SEG:OFFSET into mov dx, 16 ; 24 bit address mul dx add ax, si adc dx, 0 ; DX:AX has base address mov di, dx mov si, ax add si, word ptr [bp.Count] ; Get End of Block + 1 in DI:SI adc di, word ptr [bp.Count+2] cmp di, 010h ; 32-bit cmp ja short GL2_InvCount jb short GL2_OKExit cmp si, 0FFF0h jbe short GL2_OKExit ; Must be < 10FFEFh + 2 GL2_InvCount: mov bl, ERR_LENINVALID jmp short GL2_Error GL2_InvHandle: mov bl, ERR_DHINVALID ; Dest handle invalid jmp short GL2_Error GL2_InvOffset: mov bl, ERR_DOINVALID ; Dest Offset invalid GL2_Error: stc jmp short GL2_Exit GetLinear286 endp ;******************************************************************************* ; ; DoMoveBlock ; Set up GDT and call int 15h Move Block ; Nested within MoveExtended286 ; See 80286 programmer's reference manual for GDT entry format ; See Int 15h documentation for Move Block function ; ; Entry: ; CX Word count for move ; [BP.SrcLinear] Linear address of the source ; [BP.DstLinear] Linear address of the destination ; [BP.GDT] GDT for Block Move ; ; Interrupts are ON ; ; Return: ; CARRY => Error ; Error code in BL ; ; Registers Destroyed: ; Flags, AX, CX ; ;------------------------------------------------------------------------------- DoMoveBlock proc near push ds mov ax, ss mov ds, ax mov es, ax lea di, [bp.GDT] mov si, di ; Parameter to Block Move push cx mov cx, 6*8/2 ; Words in the GDT xor ax, ax rep stosw ; Clean it out lea di, [bp.GDT+2*8] ; Source Descriptor dec ax ; Limit FFFFh stosw mov ax, word ptr [bp.SrcLinear] stosw mov al, byte ptr [bp.SrcLinear+2] mov ah, 93h ; Access rights stosw ; Source Descriptor done lea di, [bp.GDT+3*8] ; Destination Descriptor mov ax, 0FFFFh ; Limit FFFFh stosw mov ax, word ptr [bp.DstLinear] stosw mov al, byte ptr [bp.DstLinear+2] mov ah, 93h ; Access rights stosw ; Destination Descriptor done pop cx mov ah, 87h int 15h ; Block Move jc short DMB286_Error DMB_Exit: pop ds ret DMB286_Error: xor bh, bh mov bl, al mov bl, cs:Int15Err286[bx] ; Pick up correct error code stc jmp short DMB_Exit Int15Err286 db 0, ERR_PARITY, ERR_LENINVALID, ERR_A20 DoMoveBlock endp MoveExtMemory endp ;=[END XM286.ASM]============================================================ ;=[BEGIN XM386.ASM]========================================================== ;************************************************************************** ;* * * * 386 PROTECT info * * * ;************************************************************************** ;* * 386 Descriptor template DESC386 STRUC limDesc386 dw 0 ; limit bits (0..15) LO_apDesc386 dw 0 ; base bits (0..15) MID_apDesc386 db 0 ; base bits (16..23) accessDesc386 db 0 ; accessDesc386 byte granDesc386 db 0 ; granularity byte HI_apDesc386 db 0 ; base bits (24..31) DESC386 ENDS GDT386 STRUC limitGdt dw ? LO_apBaseGdt dw ? HI_apBaseGdt dw ? GDT386 ENDS Zero segment use32 at 0 org 13*4 Int13Vector label dword Zero ends ;MEM3_Code segment use16 para public 'CODE' ;MEM3_Code ends ;codeg group code, MEM3_Code ;MEM3_Code segment assume cs:code, ds:code, es:nothing MEM3_Start label byte ; Start of relocatable code ;MEM3_Data label byte ; Start of our data MEM3_Offset = offset code:MEM3_Data-offset code:MEM3_Start OurGDT equ byte ptr ($+MEM3_Offset) ; Simple GDT DESC386 <> descCS equ byte ptr ($+MEM3_Offset) DESC386 <0FFFFh,0,0,09Fh,0,0> ; Conforming CS descRealBig equ byte ptr ($+MEM3_Offset) DESC386 <0FFFFh,0,0,093h,0cfh,0> ; Page Granularity ; 4Gb Limit GDTLen EQU ($+MEM3_Offset-OurGDT) GDTPtr equ qword ptr ($+MEM3_Offset) GDT386 OldInt13 equ dword ptr ($+MEM3_Offset) dd 0 ; Old contents of int 13 vector GDTMoveBlock equ byte ptr ($+MEM3_Offset) ; 386 Template OK for Move Block DESC386 <> ; Nul Descriptor DESC386 <> ; GDT Descriptor descSource equ byte ptr ($+MEM3_Offset) DESC386 <0FFFFh,0,0,93h,0,0> ; Source Segment Descriptor descDest equ byte ptr ($+MEM3_Offset) DESC386 <0FFFFh,0,0,93h,0,0> ; Destination Segment Descriptor DESC386 <> ; BIOS use DESC386 <> ; BIOS use ;******************************************************************************* ; ; MoveExtended386 ; XMM Move Extended Memory Block for the 80386 ; ; Entry: ; ES:BX Points to structure containing: ; bCount dd ? ; Length of block to move ; SourceHandle dw ? ; Handle for souce ; SourceOffset dd ? ; Offset into source ; DestHandle dw ? ; Handle for destination ; DestOffset dd ? ; Offset into destination ; ; Return: ; AX = 1 Success ; AX = 0 Failure ; Error code in BL ; ; Registers Destroyed: ; Flags ; ;------------------------------------------------------------------------------- MoveExtended386 proc near sti ; Be nice push bp ; Set up stack frame so we mov bp, sp ; can have local variables sub sp, 18 Count = -4 ; Local DWORD for byte count Return = -6 ; Local WORD for return code SrcHandle = -8 DstHandle = -10 SrcLinear = -14 DstLinear = -18 push eax ; Save upper word of registers push ecx push esi push edi push bx xor ax, ax mov [bp.Return], ax ; Assume success mov [bp.SrcHandle], ax mov [bp.DstHandle], ax mov ecx, es:[si.bCount] mov [bp.Count], ecx shr dword ptr [bp.Count], 1 ; No odd byte counts jc MEM3_InvCount jz MEM3_Exit ; Exit immediately if zero lea bx, [si.SourceHandle] ; Normalize Source call GetLinear386 ; Linear address in edi jc MEM3_SrcError ; Have Dest Error Code xchg esi, edi ; Save source address in ESI mov [bp.SrcHandle], bx ; Save Handle for Unlock lea bx, [di.DestHandle] call GetLinear386 ; Normalize Destination jc MEM3_Error mov [bp.DstHandle], bx ; Save Handle for Unlock smsw ax shr ax, 1 ; Protected mode? jc MEM3_MoveBlock ; if so, use int 15h ; Must preserve DS call word ptr ControlJumpTable[5*2] ; Call LocalEnableA20() cmp ax, 1 jne MEM3_Error xor cx, cx mov es, cx assume es:Zero mov ax, cs shl eax, 16 mov ax, offset code:Int13Handler+MEM3_Offset cli push [OldInt13] ; For reentrancy xchg eax, [Int13Vector] ; Install our int 13 handler mov [OldInt13], eax sti push ds mov ds, cx assume ds:Zero mov ecx, [bp.Count] shr ecx, 1 ; Now DWORD count ; Odd word count in carry ; Now we have: ; ESI = 32 bit Source Linear Address ; EDI = 32 bit Destination Linear Address ; DS = ES = 0 ; If the limit of DS or ES is still the Real Mode ; default of 64k and ESI or EDI is greater than 64k, ; these instructions will fault with an int 13. ; In this case, our int 13 handler will set up ; the descriptors to have 4Gb limits (real big mode) ; and will iret to the faulting instruction. ; The following persuades masm to output ; both a 66h and 67h prefix Fault0: rep movs dword ptr [esi], dword ptr [edi] ; DWORDS first ; THE NEXT INSTRUCTION MUST HAVE ADDRESS SIZE OVERRIDE db 67h ; CHIP BUG - DO NOT REMOVE nop ; CHIP BUG - DO NOT REMOVE rcl ecx, 1 Fault1: rep movs word ptr [esi], word ptr [edi] ; Now the odd word ; THE NEXT INSTRUCTION MUST HAVE ADDRESS SIZE OVERRIDE db 67h ; CHIP BUG - DO NOT REMOVE nop ; CHIP BUG - DO NOT REMOVE pop ds assume ds:code pop eax ; saved [OldInt13] cli ; NECESSARY xchg eax, [OldInt13] ; OldInt13 potentially INVALID mov [Int13Vector], eax ; Deinstall our handler sti call word ptr ControlJumpTable[6*2] ; Call LocalDisableA20() cmp ax, 1 jne short MEM3_Error MEM3_Exit: mov bx, [bp.SrcHandle] ; Unlock Handles if necessary or bx, bx jz short MEM3_NoSrcHandle dec [bx.cLock] ; Unlock Source MEM3_NoSrcHandle: mov bx, [bp.DstHandle] or bx, bx jz short MEM3_NoDstHandle dec [bx.cLock] ; Unlock Destination MEM3_NoDstHandle: pop bx ; Restore original registers pop edi pop esi pop ecx pop eax mov ax, 1 cmp word ptr [bp.Return], 0 je short MEM3_Success dec ax ; AX = 0 for error mov bl, byte ptr [bp.Return] MEM3_Success: mov sp, bp ; Unwind stack pop bp ret MEM3_InvCount: mov bl, ERR_LENINVALID jmp short MEM3_Error MEM3_SrcError: cmp bl, ERR_LENINVALID ; Invalid count je short MEM3_Error ; yes, no fiddle sub bl, 2 ; Convert to Source error code MEM3_Error: mov [bp.Return], bl jmp short MEM3_Exit ;******************************************************************************* ; ; GetLinear386 ; Convert Handle and Offset (or 0 and SEG:OFFSET) into Linear address ; Locks Handle if necessary ; Nested with MoveExtended386 to access local variables ; ; Entry: ; ES:BX Points to structure containing: ; Handle dw ; Offset dd ; ECX Count of bytes to move ; ; Return: ; BX Handle of block (0 if conventional) ; EDI Linear address ; CARRY => Error ; ; Registers Destroyed: ; EAX ; ;------------------------------------------------------------------------------- GetLinear386 proc near cli ; NO INTERRUPTS mov edi, dword ptr es:[bx+2] ; Offset from start of handle mov bx, word ptr es:[bx] ; Handle in bx or bx, bx jz short GL3_Conventional cmp [bx.Flags], USEDFLAG ; Valid Handle? jne short GL3_InvHandle movzx eax, [bx.Len] ; Length of Block shl eax, 10 ; now in bytes sub eax, edi ; EAX = max possible count jb short GL3_InvOffset ; Base past end of block cmp eax, ecx jb short GL3_InvCount ; Count too big inc [bx.cLock] ; Lock Handle movzx eax, [bx.Base] shl eax, 10 ; Base byte address add edi, eax ; Linear address GL3_OKExit: clc sti ret GL3_Conventional: movzx eax, di ; Offset in EAX shr edi, 16 shl edi, 4 ; Segment*16 in EDI add edi, eax ; Linear address in EDI mov eax, edi add eax, ecx cmp eax, 10FFF0h ; Max addressable inc. HMA jbe short GL3_OKExit GL3_InvCount: mov bl, ERR_LENINVALID jmp short GL3_Error GL3_InvHandle: mov bl, ERR_DHINVALID ; Dest handle invalid jmp short GL3_Error GL3_InvOffset: mov bl, ERR_DOINVALID ; Dest Offset invalid GL3_Error: stc sti ret GetLinear386 endp ;******************************************************************************* ; ; Int13Handler ; Handler for int 13 during our rep moves ; If it is a real interrupt, jump to the old handler ; If it is a fault, set Real Big Mode and return ; ; Entry: ; ; Return: ; ; Registers Destroyed: ; BX, DS, ES if fault from one of our instructions, otherwise ; NONE ; ;------------------------------------------------------------------------------- Int13Handler proc far assume cs:code, ds:nothing, es:nothing push bp mov bp, sp ; Base to look at faulting address push ax mov al, 0Bh ; Party on PIC to see if interrupt out 20h, al in al, 20h ; ISR test al, 20h ; IRQ5, int 13 jnz short NotOurInt13 mov ax, cs cmp [bp+4], ax ; Fault from our cs? jne short NotOurInt13 ; no, SOMETHING IS FUNNY! cmp word ptr [bp+2], offset code:Fault0+MEM3_Offset je short LoadDescriptorCache cmp word ptr [bp+2], offset code:Fault1+MEM3_Offset jne short NotOurInt13 ; Not one of our instructions ???? LoadDescriptorCache: mov bx, descRealBig - OurGDT ; Special 4Gb selector lgdt qword ptr cs:[GDTPtr] mov eax, cr0 or al,1 mov cr0, eax ; Go into Protected Mode ; NOTE: NMIs will kill us!!! db 0eah ; jmp far flush_prot dw offset code:flush_prot+MEM3_Offset ; Clears the prefetch dw descCS - OurGDT flush_prot: mov es, bx ; Set up the segments we want mov ds, bx and al, 0FEh mov cr0, eax ; Return to Real Mode db 0EAH ; jmp far flush_real dw offset code:flush_real+MEM3_Offset patch3 equ word ptr ($+MEM3_Offset) dw 0 flush_real: xor ax, ax mov ds, ax mov es, ax pop ax pop bp iret ; Back to faulting instruction NotOurInt13: pop ax pop bp jmp cs:[OldInt13] Int13Handler endp ;******************************************************************************* ; ; MEM3_MoveBlock ; Set up GDT and call int 15h Move Block ; Nested within MoveExtended386 ; See 80286 programmer's reference manual for GDT entry format ; See Int 15h documentation for Move Block function ; ; Entry: ; [BP.Count] Word count for move ; ESI Linear address of the source ; EDI Linear address of the destination ; ; Interrupts are ON ; ; Return: ; CARRY => Error ; Error code in BL ; ; Registers Destroyed: ; Flags, EAX, ECX, ESI, EDI, ES ; ;------------------------------------------------------------------------------- MEM3_MoveBlock: assume ds:code mov [bp.SrcLinear], esi mov [bp.DstLinear], edi mov ax, ds mov es, ax assume es:code DMB_loop: mov ecx, 512 ; Do max of # words left or cmp ecx, [bp.Count] ; or max Move Block allows jbe short DMB0 mov ecx, [bp.Count] DMB0: push ecx lea si, [GDTMoveBlock] ; Pointer to GDT for Block Move lea di, [descSource.LO_apDesc386] ; Source Descriptor mov eax, dword ptr [bp.SrcLinear] CLI ; No interrupts until int 15h ; Allows reentrancy stosw shr eax, 16 stosb ; Source Descriptor done lea di, [descDest.LO_apDesc386] ; Destination Descriptor mov eax, dword ptr [bp.DstLinear] stosw shr eax, 16 stosb ; Destination Descriptor done clc ; MUST DO THIS, int 15h doesn't bother mov ah, 87h ; Block Move - Assumes protect int 15h ; mode code will allow interrupts STI pop ecx jc short DMB_Error sub [bp.Count], ecx jz MEM3_Exit ; All done shl ecx, 1 ; Back to byte count add [bp.SrcLinear], ecx ; Update source for next chunk add [bp.DstLinear], ecx ; Update destination jmp short DMB_loop DMB_Error: xor bh, bh mov bl, al mov bl, cs:[Int15Err][bx] ; Pick up correct error code jmp MEM3_Error Int15Err equ byte ptr ($+MEM3_Offset) db 0, ERR_PARITY, ERR_LENINVALID, ERR_A20 MoveExtended386 endp MEM3_End label byte ; End of relocatable code ;=[END XM386.ASM]============================================================