DOS PROTECTED MODE INTERFACE (DPMI) SPECIFICATION Protected Mode API For DOS Extended Applications TABLE OF CONTENTS 1. Introduction............................................1 2. General Notes for Protected Mode Programs...............4 2.1 Virtual DOS Environments...........................5 2.1.1 No Virtualization............................5 2.1.2 Partial Virtualization.......................5 2.1.3 Complete Virtualization......................5 2.2 Descriptor Management..............................6 2.3 Interrupt Flag Management..........................7 2.4 Interrupts.........................................8 2.4.1 Hardware Interrupts..........................8 2.4.2 Software Interrupts..........................9 2.5 Virtual Memory and Page Locking...................10 3. Mode and Stack Switching...............................11 3.1 Stacks and Stack Switching........................12 3.1.1 Protected Mode Stack........................12 3.1.2 Locked Protected Mode Stack.................12 3.1.3 Real Mode Stack.............................12 3.1.4 DPMI Host Ring 0 Stack......................12 3.2 Default Interrupt Reflection......................13 3.3 Mode Switching....................................14 3.4 State Saving......................................15 4. Loading DPMI Clients and Extended Applications.........17 5.1 Obtaining the Real to Protected Mode Switch Entry Point.................................................18 5.2 Calling the Real to Protected Mode Switch Entry Point.................................................19 5. Terminating A Protected Mode Program...................22 6. Mode Detection.........................................23 7. LDT Descriptor Management Services.....................24 8.1 Allocate LDT Descriptors..........................25 8.2 Free LDT Descriptor...............................26 8.3 Segment to Descriptor.............................27 8.4 Get Next Selector Increment Value.................28 8.5 Reserved Subfunctions.............................29 8.6 Get Segment Base Address..........................30 8.7 Set Segment Base Address..........................31 8.8 Set Segment Limit.................................32 8.9 Set Descriptor Access Rights......................33 8.10 Create Code Segment Alias Descriptor.............35 8.11 Get Descriptor...................................36 8.12 Set Descriptor...................................37 8. DOS Memory Management Services.........................39 9.1 Allocate DOS Memory Block.........................40 9.2 Free DOS Memory Block.............................41 9.3 Resize DOS Memory Block...........................42 9. Interrupt Services.....................................43 10.1 Get Real Mode Interrupt Vector...................44 10.2 Set Real Mode Interrupt Vector...................45 10.3 Get Processor Exception Handler Vector...........46 10.4 Set Processor Exception Handler Vector...........47 10. Translation Services..................................52 11.1 Simulate Real Mode Interrupt.....................55 11.2 Call Real Mode Procedure With Far Return Frame...56 11.3 Call Real Mode Procedure With Iret Frame.........57 11.4 Allocate Real Mode Call-Back Address.............58 11.5 Free Real Mode Call-Back Address.................62 11.6 Get State Save/Restore Addresses.................63 11.7 Get Raw Mode Switch Addresses....................65 11. Get Version...........................................66 12. Memory Management Services............................67 13.1 Get Free Memory Information......................68 13.2 Allocate Memory Block............................70 13.3 Free Memory Block................................71 13.4 Resize Memory Block..............................72 13. Page Locking Services.................................73 14.1 Lock Linear Region...............................74 14.2 Unlock Linear Region.............................75 14.3 Mark Real Mode Region as Pageable................76 14.4 Relock Real Mode Region..........................77 14. Demand Paging Performance Tuning Services.............79 15.1 Mark Page as Demand Paging Candidate.............81 15.2 Discard Page Contents............................82 15. Physical Address Mapping..............................83 16. Virtual interrupt State Functions.....................84 17.1 Get and Disable Virtual Interrupt State..........85 17.2 Get and Enable Virtual Interrupt State...........86 17.3 Get Virtual Interrupt State......................87 17. DOS and BIOS Calls.....................................8 18. Supported DOS Calls...................................11 8.1 DOS Calls That Are Not Supported..................12 8.1.1 Unsupported Interrupts......................12 8.1.2 Unsupported Interrupt 21h DOS Functions.....12 8.2 Calls That Behave Differently in Protected Mode...13 8.2.1 Functions 25h and 35h -- Set/Get Interrupt Vector............................................13 8.2.2 Function 31h -- Terminate and Stay Resident.13 8.2.3 Function 32h -- Get Current Country Data....13 8.2.4 Functions 3Fh and 40h -- Read/Write File or Device............................................13 8.2.5 Function 44h, Subfunctions 02h, 03h, 04h, and 05h...........................................14 8.2.6 Function 44h, Subfunction 0Ch...............14 8.2.7 Functions 48h, 49h and 4Ah..................14 8.2.8 Function 4Bh -- Load and Execute Program....14 8.2.9 Function 4Ch -- Terminate Process with Return Code.......................................15 8.2.10 Function 65h -- Get Extended Country Information.......................................15 19. Supported BIOS Calls..................................16 9.1 Interrupt 10h -- Video............................17 9.1.1 Register Based Functions (supported):.......17 9.1.2 Function 10h -- Set Palette Registers.......17 9.1.3 Function 13h -- Write String................17 9.1.4 Functions that are not Fully Supported......17 9.2 Interrupt 11h -- Equipment Determination..........18 9.3 Interrupt 12h -- Memory Size Determination........19 9.4 Interrupt 13h -- Diskette / Fixed Disk Interface..20 9.5 Interrupt 14h -- Asynchronous Communications......21 9.6 Interrupt 15h -- System Services..................22 9.6.1 Register Based Functions (supported):.......22 9.6.2 Function C0h -- Return System Configuration Parameters........................................22 9.6.3 Function C1h -- Pointing Device Interface...22 9.6.4 Functions that are Not Supported:...........22 9.7 Interrupt 16h -- Keyboard.........................23 9.8 Interrupt 17h -- Printer..........................24 9.9 Interrupt 1Ah -- System-Timer and Real-Time Clock.25 9.9.1 Register Based Functions (supported):.......25 9.9.2 Function 06h -- Set Real-Time Clock Alarm...25 20. Mouse Driver Interface................................26 10.1 Mouse Calls that Are Supported...................26 10.1.1 Register Based Calls.......................26 10.1.2 Function 09h -- Set Pointer Shape..........26 10.1.3 Function 0Ch -- Set User-Defined Mouse Event Handler.....................................27 10.1.4 Functions 16h and 17h -- Save/Restore Mouse Driver State......................................27 10.2 Mouse Calls that Are Not Supported...............27 21. NETBIOS...............................................28 22. Interrupts 23h and 24h................................29 23. Other APIs............................................94 [Page 1] 1. INTRODUCTION The DOS Protected Mode Interface (DPMI) was defined to allow DOS programs to access the extended memory of PC architecture computers while maintaining system protection. DPMI defines a specific subset of DOS and BIOS calls that can be made by protected mode DOS programs. It also defines a new interface via software interrupt 31h that protected mode programs use to allocate memory, modify descriptors, call real mode software, etc. Any operating system that currently supports virtual DOS sessions should be capable of supporting DPMI without affecting system security. Some DPMI implementations can execute multiple protected mode programs in independent virtual machines. Thus, DPMI applications can behave exactly like any other standard DOS program and can, for example, run in the background or in a window (if the environment supports these features). Programs that run in protected mode also gain all the benefits of virtual memory and can run in 32-bit flat model if desired. Throughout this document, the term "real mode" software is used to refer to code that runs in the low 1 megabyte address space and uses segment:offset addressing. Under many implementations of DPMI, so called real mode software is actually executed in virtual 8086 mode. However, since virtual 8086 mode is a very close approximation of real mode, we will refer to it as real mode in this document. DPMI services are only available to protected mode programs. Programs running in real mode can not use these services. Protected mode programs must use the service described on page 20 to enter protected mode before calling Int 31h services. All Int 31h functions will modify flags and the AX register. All other registers will be preserved unless they are specified as return values. Unsupported calls will return with the carry flag set. Since Int 31h is set up as a trap gate, the interrupt flag will not be modified by any Int 31h calls except for memory management and interrupt flag management calls. All memory management calls may enable interrupts. Interrupt flag management calls will modify the interrupt flag as specified by the call. All Int 31h services are reentrant. Some implementations of DPMI can run 32-bit 80386 specific programs. DPMI functions that take pointers as parameters will use the extended 32-bit registers for offsets (for example, ES:EDI instead of ES:DI) when running 32-bit mode [Page 2] programs. The high word of the 32-bit registers will be ignored when running 16-bit protected mode programs. DPMI services are provided by what will be referred to as the DPMI host program. DPMI services generally consist of two layers: a set of low- level functions to allocate memory, modify descriptors, call real mode software, etc., and higher-level interfaces providing access to MS-DOS, ROM BIOS, and mouse driver functionality via protected mode execution of Int 21h, Int 10h, Int 33h, and so on. The higher-level functions are generally implemented in terms of the lower-level functions and the extant real mode DOS and ROM BIOS interfaces, although DPMI clients should not concern themselves with this. The lower-level functions are described in sections 7 through 16 of this specification, and the higher-level functions are described in sections 17 through 23. [Lacking a copy of the original 1989 DPMI spec, I had to write the above paragraph myself. I would be nice to see how does it compare to the original spec. - MS] [Page 3] 2. GENERAL NOTES FOR PROTECTED MODE PROGRAMS There are a few basic differences between real mode and protected mode that need to be addressed to convert a real mode program to run in protected mode. Programs run at a protection level that prevents them from executing privileged instructions such as lgdt, lidt, etc. The DPMI interface is the only method application programs have for modifying system structures such as descriptors. While DPMI defines a specific set of functions that will be supported by all implementations, there may be minor differences in individual implementations. Programmers should refer to the notes for their DPMI implementation for documentation on detecting the presence of and calling vendor specific extensions. However, any application that is written to adhere only to standard DPMI calls should work correctly under all implementations of DPMI. [Page 4] 2.1 Virtual DOS Environments Many DPMI implementations are simulated "virtual DOS" sessions. In other words, the DOS interface and environment presented to the program are not actually the native interface of the operating system. Hardware interrupts, I/O, and processor exceptions will be virtualized by the operating system. This means, for example, that a DPMI program may receive a simulated keyboard interrupt and read simulated I/O from the keyboard controller ports. In these environments, actual hardware interrupts will be handled by the operating system. The physical interrupts will be invisible to the DPMI application program. If the operating system so chooses, it may reflect a virtual interrupt to the DPMI program. The DPMI program does not need to know, nor should it care, if this is the case. From the program's point of view, the interrupt looks exactly like a "real" interrupt. The operating system will also virtualize I/O to the interrupt controller ports and any other simulated devices. There are basically three levels of virtualization that DPMI implementations can provide: 2.1.1 No Virtualization In general, stand-alone single tasking DPMI implementations will not virtualize any hardware devices. These hose extension programs will execute as standard DOS real mode drivers or programs. These DPMI host drivers will translate protected mode DOS calls to real mode DOS calls. Normally these DPMI hosts will return the processor to real mode (instead of virtual 8086 mode) when calling DOS. [In v0.90 the above paragraph is written as if the DPMI host and the DOS extender are different programs, so I had to rewrite it a bit. It would be nice to see how it looked in the original 1989 spec. - MS] 2.1.2 Partial Virtualization Some environments that execute under DOS will virtualize hardware devices, provide virtual memory, or provide other services that require virtualization of some hardware devices. Under these environments, DPMI applications will always run at a non-privileged ring (usually ring 3). Some or all hardware interrupts will be virtualized, some or all I/O will be virtualized, and virtual memory may be supported. Under these implementations, page locking services usually must be used to lock interrupt and exception handling code. [Page 5] 2.1.3 Complete Virtualization These environments provide a completely simulated DOS environment. The native operating system is something other than MS-DOS. Under these implementations of DPMI, all devices will be virtualized to some extent. Normally, page locking services will be ignored by these implementations since all physical device interrupt and I/O handling will be performed by the operating system. Programs will always run at a non-privileged ring. [Page 6] 2.2 Descriptor Management Protected mode code segments can not be modified. This requires programs to allocate an alias data descriptor if they need to store data in a code segment. Segment arithmetic that works in real mode does not work in protected mode. Some calls will return a range of descriptors. For example, if a 16-bit mode program allocates a block of memory larger than 64K, the call will allocate several, contiguous descriptors. Each descriptor will have a 64K limit except for the final descriptor which will have a limit that contains the remainder of the block. The call will return the first selector in the array. To get to the next selector, your program must add the value returned by Int 31h call 0003h (see page 32). [Page 7] 2.3 Interrupt Flag Management The popf and iret instructions may not modify the state of the interrupt flag since most DPMI implementations will run programs with IOPL < DPL. Programs must execute cli or sti to modify the interrupt flag state. This means that the following code sequence will leave interrupts disabled: ; ; (Assume interrupts are enabled at this point) ; pushf cli . . popf ; Interrupts are still OFF! Note that since some implementations of DPMI will maintain a virtual interrupt state for protected mode DOS programs, the current value of the interrupt flag may not reflect the current virtual interrupt state. Protected mode programs should use the virtual interrupt state services to determine the current interrupt flag state (see page 99). Since cli and sti are privileged instructions, they will cause a protection violation and the DPMI provider will simulate the instruction. Because of the overhead involved in processing the exception, cli and sti should be used as little as possible. In general, you should expect either of these instructions to require at least 300 clocks. [Page 8] 2.4 Interrupts Protected mode programs can hook both hardware and software interrupts using the DPMI get and set protected mode interrupt vector functions (see page 56). All interrupts from hardware devices such as the timer or keyboard controller will always be reflected to the protected mode interrupt handler first. If the protected mode handler jumps to or calls the previous interrupt handler then the interrupt will be reflected to real mode. As in real mode, interrupt procedures can either service the interrupt and iret or they can chain to the next handler in the interrupt chain by executing pushf/call or by jumping to the next handler. The final handler for all protected mode interrupts will reflect the interrupt to real mode. When an interrupt is reflected to real mode, the EAX, EBX, ECX, EDX, ESI, EDI, EBP registers, and flags will all be passed from protected to real mode unaltered. The segment registers will contain undefined values unless an API translator (such as a DOS or BIOS translator) explicitly sets a real mode segment register. DPMI will automatically provide a real mode stack for interrupts that are reflected to real mode. 2.4.1 Hardware Interrupts The interrupt controllers are mapped to the system's default interrupts. On an IBM AT-compatible system, for example, the master interrupt controller is programmed with a base interrupt of 8 and the slave controller has a base of 70h. The virtualized interrupt controllers can be reprogrammed; the base setting may be examined in protected mode with Int 31h function 0400h. Hardware interrupt procedures and all of their data must reside in locked memory. All memory that is touched by hardware interrupt hooks must be locked. The handler will always be called on a locked stack. See page 12 for more details. As in real mode, hardware interrupt handlers are called with interrupts disabled. Since iret will not restore the interrupt flag, hardware interrupt hooks must execute an sti before executing iret or else interrupts will remain disabled. Protected mode hardware interrupt handlers will always be called even for interrupts that occur in real mode. The [Page 9] last hook on the protected mode interrupt chain will reflect the interrupt to real mode. Protected mode hardware interrupt handlers that need to call software running in real mode must either be sure that the real mode software that they are calling will not modify segment registers or they must use the state save service (see page 74) to save and restore the real mode segment registers. However, any interrupt handler that executes completely in protected mode, or uses translation services 0300h, 0301h, or 0302h does not need to save the real mode register state. Therefore, this is not an issue for most interrupt handlers. [I'm not sure as to what to do with the above paragraph. It talks about state saving functions, which may have not existed in the original 1989 spec. Again it would be terrific to look at that spec. - MS] [Page 10] For compatibility with older systems, computers with two interrupt controllers have the BIOS redirect one of the interrupts from the slave controller into the range of the master controller. For example, devices jumpered for IRQ 2 on IBM AT-compatible computers actually interrupt on IRQ 9 (interrupt 71h). In real mode, the BIOS on these systems will convert interrupt 71h to Int 0Ah and EOI the slave controller. A protected mode program that needs access to the redirected interrupt may use variations on either of these techniques: 1. Hook the target interrupt in real mode. This takes advantage of the built in redirection. This is robust on systems where other software has reprogrammed the interrupt controllers, or where the slave interrupt controller may be absent. 2. Hook the actual interrupt in both real and protected mode. In this case, the program must EOI both the slave and master interrupt controllers since the BIOS will not get control. This is more efficient in that there will not be any unnecessary switches to real mode. 2.4.2 Software Interrupts Most software interrupts executed in real mode will not be reflected to the protected mode interrupt hooks. However, some software interrupts are also reflected to protected mode programs when they are called in real mode. These are: INT DESCRIPTION 1Ch BIOS timer tick interrupt 23h DOS Ctrl+C interrupt 24h DOS critical error interrupt Programs should not terminate during interrupts that were reflected from real mode. Terminating the program at this point may prevent the DPMI host from cleaning up properly. Of all software interrupts, only Ints 00h-07h will be called with virtual interrupts disabled. For these interrupts, the handler should return with interrupts enabled. All other interrupts will not modify the interrupt flag state. Since most software interrupts that are executed in real mode are not reflected to protected mode interrupt hooks, programs would be required to install a real mode interrupt hook to monitor these interrupts. [Page 11] 2.5 Virtual Memory and Page Locking Many implementations of DPMI support virtual memory. In these environments, it will be necessary to lock any memory that can be touched while executing inside of DOS. This is necessary because it may not be possible for the operating system to demand load a page if DOS is busy. Some DPMI implementations will not call DOS to read or write virtual memory to disk and under these implementations the page locking services may be ignored. Since the entire DPMI session is virtualized, a page fault can be handled at any point while executing the program. However, under all implementations, DPMI applications should lock interrupt code and data. The lock calls will always return success under implementations that ignore these calls. [Page 12] 3. MODE AND STACK SWITCHING This section contains an overview of how DPMI hosts switch between protected and real mode and handle stack switching. It is important to understand the host maintains the state of the client to prevent overwriting stack data or modifying segment registers. [Page 13] 3.1 Stacks and Stack Switching Every DPMI task runs on four different stacks: An application ring protected mode stack, a locked protected mode stack, a real mode stack, and a DPMI host ring 0 stack. The protected mode stack is the one the DPMI client was running on when it switched into protected mode by calling the protected mode entry point (although the client can switch to another protected mode stack if desired). The locked protected mode stack is provided by the DPMI server and is used for simulating hardware interrupts and processing real mode call-backs. The DPMI host provides the real mode stack, which is usually located in the data area provided by the client. The ring 0 stack is only accessible by the DPMI host. However, this stack may contain state information about the currently running program. 3.1.1 Protected Mode Stack This is the stack that the client uses for normal execution in protected mode. The protected mode stack of a DPMI client can be unlocked if desired. Software interrupts executed in protected mode will be reflected on this stack. 3.1.2 Locked Protected Mode Stack During hardware interrupts, Int 1Ch, Int 23h, Int 24h, exceptions, and real mode call-back handling in protected mode, the DPMI will host automatically switch to a locked protected mode stack. When the interrupt or call returns, the host will return to the original protected mode stack. Note that there is only one, 4K, locked stack provided by the host. The stack will be switched onto the first time an interrupt or call is reflected to protected mode, and will be switched away from when the client returns. Subsequent nested interrupts or calls will not cause a stack switch. Software interrupts do not automatically switch stacks. 3.1.3 Real Mode Stack The DPMI host will provide the client with a real mode stack that is at least 200h bytes in size and will always be locked. Interrupts that are reflected into real mode, as well as calls made using the translation services, will be reflected on this stack. DPMI hosts will not automatically switch stacks for hardware interrupt processing in real mode since DOS performs this function automatically. [Page 14] 3.1.4 DPMI Host Ring 0 Stack DPMI hosts will normally have a stack associated with each DPMI task. The DPMI client will not be able to access this stack in any way -- it is used by the host for execution at ring 0 to handle interrupts and exceptions. This stack will sometimes be used to store state information while switching modes. For example, the original SS:ESP of the protected mode program could be saved on the ring 0 stack while the DPMI host switches onto the locked protected mode stack. [Page 15] 3.2 Default Interrupt Reflection DPMI hosts provide interrupt vectors for all 100h (256 decimal) interrupts for protected mode clients. When the DPMI client initializes, all interrupt vectors will point to code that will automatically reflect the interrupt to real mode (except for Int 31h and Int 21h, AH=4Ch). When a default interrupt reflection handler is executed it will switch to real mode, preserving the EAX, EBX, ECX, EDX, ESI, EDI, and EBP registers and flags, and reflect the interrupt in real mode. When the real mode interrupt returns, the default interrupt reflection code will switch back to protected mode and return with the modified values of EAX, EBX, ECX, EDX, ESI, EDI, EBP, and flags. Segment registers and the stack pointer will not be passed between modes. Therefore, any API that passes pointers or information in segment registers will need to be translated by a DOS extender. [The above paragraph was clearly hobbled by the Committee and needs fixing. Would someone please come up with a copy of the original 1989 spec? - MS] [Page 16] 3.3 Mode Switching There are three different ways a client can force a mode switch between protected and real mode: o Execute the default interrupt reflection handler o Use the translation services to call real mode code o Use a real mode call-back to switch from real to protected mode o Use the raw mode switch functions All mode switches except for the raw mode switches will save some information on the DPMI host's ring 0 stack. This means that programs should not terminate while in nested mode switches unless they are using the raw mode switching services. However, even programs that use raw mode switches should not attempt to terminate from a hardware interrupt or exception handler since the DPMI host performs automatic mode and stack switching to provide these services. [I'm not sure as to what to do with the above paragraph. It talks about raw mode switching functions, which may have not existed in the original 1989 spec. Again it would be terrific to look at that spec. - MS] [Page 17] 3.4 State Saving Because DPMI hosts switch stacks automatically across mode switches, it is sometimes necessary to use the state save/restore functions while using the raw mode switch services. The host will maintain information on the "other" mode's current state. This information will include the CS:(E)IP, SS:(E)SP, and segment register values. Since the DPMI client has no way to directly access these values, it will need to call the state saving functions when performing nested mode switches. For example, during hardware interrupts, the DPMI host will preserve the real mode's segment registers, CS:EIP, and SS:ESP on the ring 0 stack. However, they are not pushed on any stack in the VM -- They are only visible at ring 0. When the raw mode switch functions are called they will overwrite the information saved by the host. At this point, the program would return to the wrong address when the interrupt returned. For more information on state saving, refer to the documentation on page 74. [I'm not sure as to what to do with the above section. It talks about state saving functions, which may have not existed in the original 1989 spec. Again it would be terrific to look at that spec. - MS] [Page 18] 4. LOADING DPMI CLIENTS AND EXTENDED APPLICATIONS All DPMI applications begin execution in real mode. An application must run first as a standard real mode DOS program but it can switch to protected execution by making a few simple calls. DPMI does not define an executable file format for protected mode programs. Instead, programs must provide their own mechanism for loading and fixing up protected mode code. [Page 19] 5.1 Obtaining the Real to Protected Mode Switch Entry Point This function can be called in real mode to detect the presence of DPMI services and to obtain an address that can be used to begin execution in protected mode. To Call AX = 1687h Execute an Int 2Fh (not an Int 31h) Returns If function was successful: AX = 0 BX = Flags Bit 0 = 1 if 32-bit programs are supported CL = Processor type 02h = 80286 03h = 80386 04h = 80486 DH = DPMI major version number DL = DPMI minor version number SI = Number of paragraphs required for DPMI host private data (may be 0) ES:DI = Address of procedure to call to enter protected mode If function was not successful: AX != 0 Programmer's Notes o This function does not perform the actual transition into protected mode. You need to call the address returned in ES:DI, after allocating the private data area for the DPMI host, to perform the actual real to protected mode switch. [Page 20] 5.2 Calling the Real to Protected Mode Switch Entry Point After using Int 2Fh function 1687h, to obtain the protected mode entry point, the DPMI client must call the entry point address as described in this section. To Call AX = Flags Bit 0 = 1 if program is a 32-bit application ES = Real mode segment of DPMI host data area. This must be the size of the data area returned in SI from the previous function. ES will be ignored if the required data size is zero. Call the address returned in ES:DI by the previous function Returns If function was successful: Carry flag is clear. Program is now executing in protected mode. CS = 16-bit selector with base of real mode CS and a 64K limit SS = Selector with base of real mode SS and a 64K limit DS = Selector with base of real mode DS and a 64K limit ES = Selector to program's PSP with a 100h byte limit FS and GS = 0 (if running on an 80386 or 80486) If the program is a 32-bit application the high word of ESP will be 0 All other registers are preserved If function was not successful: Carry flag is set. Program is executing in real mode Programmer's Notes o Once in protected mode, all Int 31h calls that are supported by DPMI can be called. o To terminate the program, execute an Int 21h with AH=4Ch and AL=Error code. This is the standard DOS exit function. Do not use any other DOS termination call -- Only AH=4Ch is supported under DPMI. o Under different implementations of DPMI the privilege ring of a program will change. Programs should make no assumptions about the ring at which they will run. When creating descriptors, programs should set the DPL of the descriptor to the same ring as their initial code segment. Use the lar instruction to determine the protection [Page 21] ring of your program's code segment. All descriptors created by your program should be set to the same protection level. o Programs that specify that they are 32-bit applications will initially run with a 16-bit code segment. Stack and data selectors for 32-bit programs will be 32-bit (the Big bit will be set). However, all Int 31h calls will require 48-bit pointers even though the program is running in a 16-bit code segment. o Unless you have explicitly enabled the A20 address line through the XMS interface, do not assume that memory from 1Mb to 1Mb+64K-16 (the High Memory Area) is addressable once your program is running in protected mode. If you want to be able to access the HMA then you must enable the A20 through XMS before entering protected mode. XMS calls are not supported in protected mode. Note that this restriction is only important for software that wishes to access the HMA. Under all implementations of DPMI the physical A20 address line will always be enabled while executing protected mode code. However, some 80386 specific DPMI implementations simulate 1Mb address wrap for compatibility reasons. Under these DPMI implementations, the HMA will not be accessible unless the A20 is enabled through the XMS interface. o The environment pointer in the current program's PSP will automatically be converted to a descriptor. If you want to free the program's environment memory, you must do so before entering protected mode. In this case, the environment pointer descriptor will point to garbage and should not be used. The DPMI client may change the environment pointer in the PSP after entering protected mode but it must restore it to the selector created by the DPMI host before terminating. o The caller is allowed to modify or free the DS, SS, and CS descriptors allocated by this call. You may not modify the PSP descriptor or environment pointer descriptor in the PSP. See page 30 for information on freeing descriptors. o Note that if DS=SS on entry to this call then only one descriptor will be allocated for both DS and SS. In this case, for example, if you changed the base of the DS descriptor you would also change the base of the stack segment. o For some hosts it may be a good idea for protected mode programs to use some or all of the real mode memory allocated to the real mode program by DOS for protected mode code or data. Protected mode programs that use memory in the first 1Mb should [Page 22] mark the memory as pageable using Int 31h 0602h. See page 90 for details. o The protected mode DTA will be mapped to the real mode DTA when the program enters protected mode. If the DTA address has not been changed from the default at offset 80h in the PSP, then the DTA selector will be the same as the PSP selector. Otherwise, a new descriptor will be allocated. Do not modify or free the DTA descriptor. Use DOS call 2Fh to obtain the address of the DTA. o The DOS Ctrl+Break (Int 23h) and critical error (Int 24h) interrupt handlers will be set to default handlers as described on page 29. [Page 23] Example Code ; ; Get the entry point address and save it ; mov ax, 1687h int 2Fh test ax, ax jnz Cant_Enter_PMode mov [PMode_Entry_Seg], es mov [PMode_Entry_Off], di ; ; Allocate memory for use by DOS extender if necessary ; NOTE: This code assumes that the program has already ; shrunk its memory block so that the DOS ; memory allocation call will work ; test si, si jz Enter_PMode_Now mov bx, si mov ah, 48h int 21h jc Cant_Enter_PMode mov es, ax ; ; Enter protected mode as a 16-bit program ; Enter_PMode_Now: xor ax, ax call DWORD PTR [PMode_Entry_Off] jc Cant_Enter_PMode ; ; The program is running in protected mode now! ; Protected mode initialization code would go here. ; Mark program's real mode memory as pageable, etc. ; . . . ; ; Quit the program and return to real mode DOS ; mov ax, 4C00h int 21h [Page 24] 5. TERMINATING A PROTECTED MODE PROGRAM To terminate a protected mode program execute an Int 21h with AH=4Ch in protected mode. You can return an error code in the AL register. This is the standard DOS terminate API but it must be executed in protected mode to allow the DPMI host to clean up any data structures associated with the protected mode program. Programs should not be terminated from a hardware interrupt, exception handler, or real mode call-back. Programs should only be terminated from their main thread of execution to allow the DPMI host to clean up properly. However, DOS extenders that use the raw mode switch services for all mode transitions can execute the terminate call after switching from real to protected mode. [The above paragraph talks about DOS extenders and about raw mode switches, so it was probably hobbled by the Committee. Again we need to see what did the original 1989 spec say about this. - MS] [Page 25] 6. MODE DETECTION It is possible to write a program or library that can run in either real or protected mode. This function is supplied so that bimodal code can detect at run time whether it is running under protected mode. Code that only runs in protected mode does not need to perform this test. To Call AX = 1686h Execute an Int 2Fh (not an Int 31h) Returns If executing in protected mode under DPMI: AX = 0 If executing in real mode or not under DPMI then: AX != 0 Programmer's Notes o This call will return AX = 0 when the caller is running in protected mode. It will return AX non- zero even when running under environments that support DPMI if the caller is in real (virtual 8086) mode. See page 20 for information on entering protected mode. [Page 26] 7. LDT DESCRIPTOR MANAGEMENT SERVICES The LDT descriptor management services provide interfaces for allocating, freeing, creating, locking and unlocking protected mode descriptors in the current task's Local Descriptor Table (LDT). [Page 27] 8.1 Allocate LDT Descriptors This function is used to allocate one or more descriptors from the task's Local Descriptor Table (LDT). The descriptor(s) allocated must be initialized by the application. To Call AX = 0000h CX = Number of descriptors to allocate Returns If function was successful: Carry flag is clear. AX = Base selector If function was not successful: Carry flag is set. Programmer's Notes o If more than one descriptor was requested, AX will contain the first of a contiguous array of descriptors. You should add the value returned by function 0003h (see page 32) to get to the next selector in the array. o The descriptor will be set to present data type, with a base and limit of zero. o It is up to the caller to fill in the descriptors. o The privilege level of descriptors will match the application's code segment privilege level. When modifying descriptors, always set the DPL to the same privilege ring as your program's code segment. Use the lar instruction to determine the privilege of a descriptor. [Page 28] 8.2 Free LDT Descriptor This function is used to free descriptors that were allocated through the Allocate LDT Descriptors function. To Call AX = 0001h BX = Selector to free Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o Arrays of descriptors are freed by calling this function for each of the individual descriptors. o It is valid to free the descriptors allocated for the program's initial CS, DS, and SS. Other descriptors that were not allocated by function 0000h should never be freed by this function unless otherwise specified. [Page 29] 8.3 Segment to Descriptor This function is used to convert real mode segments into descriptors that are addressable by protected mode programs. To Call AX = 0002h BX = Real mode segment address Returns If function was successful: Carry flag is clear. AX = Selector mapped to real mode segment If function was not successful: Carry flag is set. Programmer's Notes o Multiple calls to this function with the same segment will return the same selector. o Descriptors created by this function should never be modified or freed. For this reason, you should use this function sparingly. If your program needs to examine various real mode addresses using the same selector you should allocate a descriptor and change the base using the Set Segment Base Address function instead of using this function. o The descriptor's limit will be set to 64K. o The intent of this function is to allow programs easy access to commonly used real mode segments such as 40h and A000h. Do not use this service to obtain descriptors to private data areas. [Page 30] 8.4 Get Next Selector Increment Value Some functions such as allocate LDT descriptors and allocate DOS memory can return more than one descriptor. You must call this function to determine the value that must be added to a selector to access the next descriptor in the array. To Call AX = 0003h Returns Carry flag clear (this function always succeeds) AX = Value to add to get to next selector Programmer's Notes o Do not make any assumptions about the value this function will return. o The increment value returned will be a power of two. [Page 31] 8.5 Reserved Subfunctions [FIXME: must describe Lock Selector and Unlock Selector functions. It would be nice to get hold of a spec that has them. If not, I'll describe them myself. - MS] [Page 32] 8.6 Get Segment Base Address This function returns the 32-bit linear base address of the specified segment. To Call AX = 0006h BX = Selector Returns If function was successful: Carry flag is clear. CX:DX = 32-bit linear base address of segment If function was not successful: Carry flag is set. Programmer's Notes o This function will fail if the selector specified in BX is invalid [Page 33] 8.7 Set Segment Base Address This function changes the 32-bit linear base address of the specified selector. To Call AX = 0007h BX = Selector CX:DX = 32-bit linear base address for segment Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o This function will fail if the selector specified in BX is invalid. o Your program should only modify descriptors that were allocated through the Allocate LDT Descriptors function. o The high 8 bits of the base address (contained in CH) will be ignored by 16-bit implementations of DPMI. This is true even when running on 80386 machines. [Page 34] 8.8 Set Segment Limit This function sets the limit for the specified segment. To Call AX = 0008h BX = Selector CX:DX = 32-bit segment limit Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o This function will fail if the selector specified in BX is invalid or the specified limit could not be set. 16-bit DPMI implementations can not set segment limits greater than 0FFFFh (64K) so CX must be zero when calling this function under these implementations of DPMI. o Segment limits greater than 1 meg must be page aligned. That is, limits greater than one megabyte must have the low 12 bits set. o Your program should only modify descriptors that were allocated through the Allocate LDT Descriptors function. o To get the limit of a segment you should use the instruction lsl (load segment limit) which is supported on 80286 and 80386 machines. Note that on 80386 machines you will need to use the 32-bit form of lsl if the segment has a limit greater than 64K. [Page 35] 8.9 Set Descriptor Access Rights This function allows a protected mode program to modify the access rights and type fields of a descriptor. To Call AX = 0009h BX = Selector CL = Access rights/type byte CH = 80386 extended access rights/type byte (32-bit DPMI implementations only) Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o This function will fail if the selector specified in BX is invalid. o Your program should only modify descriptors that were allocated through the Allocate LDT Descriptors function. o To examine the access rights of a descriptor you should use the instruction lar (load access rights) which is supported on 80286 and 80386 machines. o The access rights/type byte passed in CL has the following format: [Page 36] +-------------------------------+ | P | DPL | 1 |C/D|E/C|W/R| A | +-------+-----------------------+ | | | | | | +- 0=>Not Accessed | | | | | | 1=>Accessed | | | | | +-- Data: 0=>Read, 1=>R/W | | | | | Code: Must be 1 (readable) | | | | +-- Data: 0=>Exp-up, 1=>Exp- dn | | | | Code: Must be 0 (non-conform) | | | +-- 0=>Data, 1=>Code | | | | | +-- Must be 1 | | | +-- Must equal caller's CPL | +- 0=>Absent, 1=>Present A parameter which does not meet the above requirements is invalid, and causes the function to return with the carry flag set. o 16-bit DPMI implementations will ignore the extended access rights/type byte passed in CH even if it is running on an 80386 system. 32-bit DPMI implementations interpret the CH parameter as follows: +-------------------------------+ | G |B/D| 0 |Avl| Reserved | +-----------------------+-------+ | | | | +-- Ignored | | | +-- Can be 0 or 1 | | +-- Must be 0 | +-- 0=>Default 16-bit, 1=>Default 32-bit +- 0=>Byte Granular, 1=>Page Granular A parameter which does not meet the above requirements is invalid, and causes the function to return with the carry flag set. [Page 37] 8.10 Create Code Segment Alias Descriptor This function will create a data descriptor that has the same base and limit as the specified code segment descriptor. To Call AX = 000Ah BX = Code segment selector Returns If function was successful: Carry flag is clear. AX = New data selector If function was not successful: Carry flag is set. Programmer's Notes o This function will fail if the selector specified in BX is not a code segment or is invalid. o Use the Free LDT Descriptor function to deallocate the alias descriptor. o The code segment alias descriptor will not track changes to the code descriptor. In other words, if an alias descriptor is created, and then the base or limit of the code segment is changed, the alias descriptor's base or limit would not change. [Page 38] 8.11 Get Descriptor This function copies the descriptor table entry for a specified descriptor into an eight byte buffer. To Call AX = 000Bh BX = Selector ES:(E)DI = Pointer to an 8 byte buffer to receive copy of descriptor Returns If function was successful: Carry flag is clear. ES:(E)DI = Pointer to buffer that contains descriptor If function was not successful: Carry flag is set. Programmer's Notes o This function will fail if the selector specified in BX is invalid or unallocated. o 32-bit programs must use ES:EDI to point to the buffer. 16-bit programs should use ES:DI. [Page 39] 8.12 Set Descriptor This function copies an eight byte buffer into the LDT entry for a specified descriptor. To Call AX = 000Ch BX = Selector ES:(E)DI = Pointer to an 8 byte buffer that contains descriptor Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o This function will fail if the selector specified in BX is invalid. o Your program should only modify descriptors that were allocated through the Allocate LDT Descriptors function. o 32-bit programs must use ES:EDI to point to the buffer. 16-bit programs should use ES:DI. o The type byte (byte 5) follows the same format and restrictions as the access rights/type parameter (in CL) to Set Descriptor Access Rights. The extended type byte (byte 6) follows the same format and restrictions as the extended access rights/type parameter (in CH) to Set Descriptor Access Rights, except the limit field may have any value, except the low order 4 bits (marked "reserved") are used to set the upper 4 bits of the descriptor's limit. [Page 40] 8. DOS MEMORY MANAGEMENT SERVICES Some applications require the ability to allocate memory in the real mode addressable 1 megabyte region. These services allow protected mode applications to allocate and free memory that is directly addressable by real mode software such as networks and DOS device drivers. Often, this memory is used in conjunction with the API translation services to call real mode software that is not directly supported by DPMI. [Page 41] 9.1 Allocate DOS Memory Block This function will allocate a block of memory from the DOS free memory pool. It returns both the real mode segment and one or more descriptors that can be used by protected mode applications to access the block. To Call AX = 0100h BX = Number of paragraphs (16 byte blocks) desired Returns If function was successful: Carry flag is clear. AX = Initial real mode segment of allocated block DX = Selector for allocated block If function was not successful: Carry flag is set. AX = DOS error code: 07h memory control blocks damaged 08h insufficient memory available to allocate as requested BX = Size of largest available block in paragraphs Programmer's Notes o If the size of the block requested is greater than 64K bytes (BX > 1000h) then contiguous descriptors will be allocated. To access the next descriptor for the memory block add the value return by function 0003h (see page 32) to the base selector. If more than one descriptor is allocated under 32- bit DPMI implementations, the limit of the first descriptor will be set to the size of the entire block. All subsequent descriptors will have a limit of 64K except for the final descriptor which will have a limit of Block size MOD 64K. 16-bit DPMI implementations will always set the limit of the first descriptor to 64K even when running on an 80386. o Your program should never modify or deallocate any descriptors allocated by this function. The Free DOS Memory Block function will deallocate the descriptors automatically [Page 42] 9.2 Free DOS Memory Block This function frees memory that was allocated through the Allocate DOS Memory Block function. To Call AX = 0101h DX = Selector of block to free Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. AX = DOS error code: 07h memory control blocks damaged 09h incorrect memory segment specified Programmer's Notes o All descriptors allocated for the memory block are automatically freed and therefore should not be accessed once the block is freed by this function. [Page 43] 9.3 Resize DOS Memory Block This function is used to grow or shrink a memory block that was allocated through the Allocate DOS Memory Block function. To Call AX = 0102h BX = New block size in paragraphs DX = Selector of block to modify Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. AX = DOS error code: 07h memory control blocks damaged 08h insufficient memory available to allocate as requested 09h incorrect memory segment specified BX = Maximum block size possible in paragraphs Programmer's Notes o Growing a memory block is often likely to fail since other DOS block allocations will prevent increasing the size of the block. Also, if the size of a block grows past a 64K boundary then the allocation will fail if the next descriptor in the LDT is not free. Therefore, this function is usually only used to shrink a block. o Shrinking a block may cause some descriptors that were previously allocated to the block to be freed. For example shrinking a block from 140K to 120K would cause the third allocated descriptor to be freed since it is no longer valid. The initial selector will remain unchanged, however, the limits of the remaining two descriptors will change: the first to 120K and the second to 56k. [Page 44] 9. INTERRUPT SERVICES These services allow protected mode applications to intercept real mode interrupts and hook processor exceptions. [Removed reference to protmode interrupts. Would be nice to look at the original 1989 spec to see if it said anything about them here. - MS] [Page 45] 10.1 Get Real Mode Interrupt Vector This function returns the value of the current task's real mode interrupt vector for the specified interrupt. To Call AX = 0200h BL = Interrupt number Returns Carry flag is clear. CX:DX = Segment:Offset of real mode interrupt handler Programmer's Notes o The address returned in CX is a segment, not a selector. Therefore you should not attempt to place the value returned in CX into a segment register in protected mode or a general protection fault may occur. o Note all 100h (256 decimal) interrupt vectors must be supported by the DPMI host. [Page 46] 10.2 Set Real Mode Interrupt Vector This function sets the value of the current task's real mode interrupt vector for the specified interrupt. To Call AX = 0201h BL = Interrupt number CX:DX = Segment:Offset of real mode interrupt handler Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o The address passed in CX must be a real mode segment, not a selector. o If the interrupt being hooked is a hardware interrupt then you must lock the segment that the interrupt handler runs in as well as any memory the handler may touch at interrupt time. o The address contained in CX:DX must be a real mode segment:offset, not a selector:offset. This means that the code for the interrupt handler must either reside in DOS addressable memory or you must use a real mode call-back address. Refer to the section on DOS memory management services on page 43 for information on allocating memory below 1 megabyte. Information on real mode call back addresses can be found on page 68. [Page 47] 10.3 Get Processor Exception Handler Vector This function returns the CS:(E)IP of the current protected mode exception handler for the specified exception number. To Call AX = 0202h BL = Exception/fault number (00h-1Fh) Returns If function was successful: Carry flag is clear. CX:(E)DX = Selector:Offset of exception handler If function was not successful: Carry flag is set. The value passed in BL was invalid. Programmer's Notes o The value returned in CX is a valid protected mode selector, not a real mode segment. o 32-bit mode programs will be returned a 32-bit offset in the EDX register. [Page 48] 10.4 Set Processor Exception Handler Vector This function allows protected mode applications to intercept processor exceptions that are not handled by the DPMI environment. Programs may wish to handle exceptions such as not present segment faults which would otherwise generate a fatal error. Every exception is first examined by the protected mode operating system. If it can not handle the exception it then reflects it through the protected mode exception handler chain. The final handler in the chain may either reflect the exception as an interrupt (as would happen in real mode) or it may terminate the current program. To Call AX = 0203h BL = Exception/fault number (00h-1Fh) CX:(E)DX = Selector:Offset of exception handler Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. The value passed in BL was invalid. Programmer's Notes o The value passed in CX must be a valid protected mode code selector, not a real mode segment. o 32-bit mode programs must supply a 32-bit offset in the EDX register. If your handler chains to the next exception handler it must do so using a 32-bit interrupt stack frame. o The handler should return using a far return instruction. The original SS:(E)SP, CS:(E)IP and flags on the stack, including the interrupt flag, will be restored. o All fault stack frames have an error code. However, the error code is only valid for exceptions 08h, 0Ah, 0Bh, 0Ch, 0Dh, and 0Eh. o The handler must preserve and restore all registers. o The exception handler will be called on a locked stack with interrupts disabled. The original SS, [Page 49] (E)SP, CS, and (E)IP will be pushed on the exception handler stack frame. o The handler must either return from the call by executing a far return or jump to the next handler in the chain (which will execute a far return or chain to the next handler). [Page 50] o The procedure can modify any of the values on the stack pertaining to the exception before returning. This can be used, for example, to jump to a procedure by modifying the CS:IP on the stack. Note that the procedure must not modify the far return address on the stack -- it must return to the original caller. The caller will then restore the flags, CS:(E)IP and SS:(E)SP from the stack frame. o If the DPMI client does not handle an exception, or jumps to the default exception handler, the host will reflect the exception as an interrupt for exceptions 0, 1, 2, 3, 4, 5, and 7. Exceptions 6, and 8-1Fh will be treated as fatal errors and the client will be terminated. o Exception handlers will only be called for exceptions that occur in protected mode. [Page 51] Call-Back Stack Frames Stack frame for 16-bit programs: 15 0 SS SP Flags CS IP Err Code Return CS Return IP <-- SS:SP Stack frame for 32-bit programs: 31 0 SS ESP EFlags CS EIP Error Code Ret CS [Page 52] Return EIP <-- SS:ESP Shaded fields should not be modified. Other fields can be modified before returning from the exception handler. [Page 53] 10. TRANSLATION SERVICES These services are provided so that protected mode programs can call real mode software that DPMI does not support directly. The protected mode program sets up a data structure that contains the values for every register. The data structure is defined as: [Page 54] Offset Register 00h EDI 04h ESI 08h EBP 0Ch Reserved by system 10h EBX 14h EDX 18h ECX 1Ch EAX 20h Flags 22h ES 24h DS 26h FS 28h GS 2Ah IP 2Ch CS 2Eh SP 30h SS You will notice that all of the fields are dwords so that 32 bit registers can be passed to real mode. Most real mode software will ignore the high word of the extended registers. However, you can write a real mode procedure that uses 32-bit registers if you desire. Note that 16-bit DPMI implementations may not pass the high word of 32-bit [Page 55] registers or the FS and GS segment registers to real mode even when running on an 80386 machine. Any interrupt handler or procedure called must return with the stack in the same state as when it was called. This means that the real mode code may switch stacks while it is running but it must return on the same stack that it was called on and it must pop off the entire far return/iret structure. After the call or interrupt is complete, all real mode registers and flags except SS, SP, CS, and IP will be copied back to the real mode call structure so that the caller can examine the real mode return values. Remember that the values in the segment registers should be real mode segments, not protected mode selectors. The translation services will provide a real mode stack if the SS:SP fields are zero. However, the stack provided is relatively small. If the real mode procedure/interrupt routine uses more than 30 words of stack space then you should provide your own real mode stack. It is possible to pass parameters to real mode software on the stack. The following code will call a real mode procedure with 3 word parameters: Protected_Mode_Code: push Param1 push Param2 push Param3 (Set ES:DI to point to call structure) mov cx, 3 ; Copy 3 words mov ax, 0301h ; Call real mode proc int 31h ; Call the procedure add sp, 6 ; Clean up stack The real mode procedure would be called with the following data on the real mode stack: [Page 56] Param1 Param2 Param3 Return CS Return IP <-- Real mode SS:SP [Page 57] If your program needs to perform a series of calls to a real mode API it is sometimes more convenient to use the translation services to call a real mode procedure in your own program. That procedure can then issue the API calls in real mode and then return to protected mode. This also avoids the overhead of a mode switch for each API call. There is also a mechanism for protected mode software to gain control from real mode via a real mode call-back address. Real mode call-backs can be used to hook real mode interrupts or to be called in protected mode by a real mode driver. For example, many mouse drivers will call a specified address whenever the mouse is moved. This service allows the call-back to be handled by software running in protected mode. [Page 62] 11.1 Simulate Real Mode Interrupt This function simulates an interrupt in real mode. It will invoke the CS:IP specified by the real mode interrupt vector and the handler must return by executing an iret. To Call AX = 0300h BL = Interrupt number BH = Flags Bit 0 = 1 resets the interrupt controller and A20 line Other flags reserved and must be 0 CX = Number of words to copy from protected mode to real mode stack ES:(E)DI = Selector:Offset of real mode call structure Returns If function was successful: Carry flag is clear. ES:(E)DI = Selector:Offset of modified real mode call structure If function was not successful: Carry flag is set. Programmer's Notes o The CS:IP in the real mode call structure is ignored by this service. The appropriate interrupt handler will be called based on the value passed in BL. o If the SS:SP fields are zero then a real mode stack will be provided by the DPMI host. Otherwise, the real mode SS:SP will be set to the specified values before the interrupt handler is called. o The flags specified in the real mode call structure will be pushed on the real mode stack iret frame. The interrupt handler will be called with the interrupt and trace flags clear. o When the Int 31h returns, the real mode call register structure will contain the values that were returned by the real mode interrupt handler. o It is up to the caller to remove any parameters that were pushed on the protected mode stack. o 32-bit programs must use ES:EDI to point to the real mode call structure. 16-bit programs should use ES:DI. [Page 58] o The flag to reset the interrupt controller and A20 line is ignored by DPMI implementations that run in Virtual 8086 mode. It causes DPMI implementations that return to real mode to set the interrupt controller and A20 address line hardware to its normal real mode state. [Page 59] 11.2 Call Real Mode Procedure With Far Return Frame This function calls a real mode procedure. The called procedure must execute a far return when it completes. To Call AX = 0301h BH = Flags Bit 0 = 1 resets the interrupt controller and A20 line Other flags reserved and must be 0 CX = Number of words to copy from protected mode to real mode stack ES:(E)DI = Selector:Offset of real mode call structure Returns If function was successful: Carry flag is clear. ES:(E)DI = Selector:Offset of modified real mode call structure If function was not successful: Carry flag is set. Programmer's Notes o The CS:IP in the real mode call structure specifies the address of the real mode procedure to call. o The real mode procedure must execute a far return when it has completed. o If the SS:SP fields are zero then a real mode stack will be provided by the DPMI host. Otherwise, the real mode SS:SP will be set to the specified values before the procedure is called. o When the Int 31h returns, the real mode call structure will contain the values that were returned by the real mode procedure. o It is up to the caller to remove any parameters that were pushed on the protected mode stack. o 32-bit programs must use ES:EDI to point to the real mode call structure. 16-bit programs should use ES:DI. o The flag to reset the interrupt controller and A20 line is ignored by DPMI implementations that run in Virtual 8086 mode. It causes DPMI implementations that return to real mode to set the interrupt controller and A20 address line hardware to its normal real mode state. [Page 60] 11.3 Call Real Mode Procedure With Iret Frame This function calls a real mode procedure. The called procedure must execute an iret when it completes. To Call AX = 0302h BH = Flags Bit 0 = 1 resets the interrupt controller and A20 line Other flags reserved and must be 0 CX = Number of words to copy from protected mode to real mode stack ES:(E)DI = Selector:Offset of real mode call structure Returns If function was successful: Carry flag is clear. ES:(E)DI = Selector:Offset of modified real mode call structure If function was not successful: Carry flag is set. Programmer's Notes o The CS:IP in the real mode call structure specifies the address of the real mode procedure to call. o The real mode procedure must execute an iret when it has completed. o If the SS:SP fields are zero then a real mode stack will be provided by the DPMI host. Otherwise, the real mode SS:SP will be set to the specified values before the procedure is called. o When the Int 31h returns, the real mode call structure will contain the values that were returned by the real mode procedure. o The flags specified in the real mode call structure will be pushed the real mode stack iret frame. The procedure will be called with the interrupt and trace flags clear. o It is up to the caller to remove any parameters that were pushed on the protected mode stack. o 32-bit programs must use ES:EDI to point to the real mode call structure. 16-bit programs should use ES:DI. o The flag to reset the interrupt controller and A20 line is ignored by DPMI implementations that run in Virtual 8086 mode. It causes DPMI [Page 61] implementations that return to real mode to set the interrupt controller and A20 address line hardware to its normal real mode state. [Page 62] 11.4 Allocate Real Mode Call-Back Address This service is used to obtain a unique real mode SEG:OFFSET that will transfer control from real mode to a protected mode procedure. At times it is necessary to hook a real mode interrupt or device call-back in a protected mode driver. For example, many mouse drivers call an address whenever the mouse is moved. Software running in protected mode can use a real mode call-back to intercept the mouse driver calls. To Call AX = 0303h DS:(E)SI = Selector:Offset of procedure to call ES:(E)DI = Selector:Offset of real mode call structure Returns If function was successful: Carry flag is clear. CX:DX = Segment:Offset of real mode call address If function was not successful: Carry flag is set. Call-Back Procedure Parameters Interrupts disabled DS:(E)SI = Selector:Offset of real mode SS:SP ES:(E)DI = Selector:Offset of real mode call structure SS:(E)SP = Locked protected mode API stack All other registers undefined Return from Call-Back Procedure Execute an IRET to return ES:(E)DI = Selector:Offset of real mode call structure to restore (see note) [Page 63] Programmer's Notes o Since the real mode call structure is static, you must be careful when writing code that may be reentered. The simplest method of avoiding reentrancy is to leave interrupts disabled throughout the entire call. However, if the amount of code executed by the call-back is large then you will need to copy the real mode call structure into another buffer. You can then return with ES:(E)DI pointing to the buffer you copied the data to -- it does not have to point to the original real mode call structure. o The called procedure is responsible for modifying the real mode CS:IP before returning. If the real mode CS:IP is left unchanged then the real mode call-back will be executed immediately and your procedure will be called again. Normally you will want to pop a return address off of the real mode stack and place it in the real mode CS:IP. The example code in the next section demonstrates chaining to another interrupt handler and simulating a real mode iret. o To return values to the real mode caller you must modify the real mode call structure. o Remember that all segment values in the real mode call structure will contain real mode segments, not selectors. If you need to examine data pointed to by a real mode seg:offset pointer you should not use the segment to selector service to create a new selector. Instead, allocate a descriptor during initialization and change the descriptor's base to 16 times the real mode segment's value. This is important since selectors allocated though the segment to selector service can never be freed. o DPMI hosts should provide a minimum of 16 call- back addresses per task. [Page 64] Example Code The following code is a sample of a real mode interrupt hook. It hooks the DOS Int 21h and returns an error for the delete file function (AH=41h). Other calls are passed through to DOS. This example is somewhat silly but it demonstrates the techniques used to hook a real mode interrupt. Note that since DOS calls are reflected from protected mode to real mode, the following code will intercept all DOS calls from both real mode and protected mode. ;****************************************************** ; This procedure gets the current Int 21h real mode ; Seg:Offset, allocates a real mode call-back address, ; and sets the real mode Int 21h vector to the call- ; back address. ;****************************************************** Initialization_Code: ; ; Create a code segment alias to save data in ; mov ax, 000Ah mov bx, cs int 31h jc ERROR mov ds, ax ASSUMES DS,_TEXT ; ; Get current Int 21h real mode SEG:OFFSET ; mov ax, 0200h mov bl, 21h int 31h jc ERROR mov [Orig_Real_Seg], cx mov [Orig_Real_Offset], dx ; ; Allocate a real mode call-back ; mov ax, 0303h push ds mov bx, cs mov ds, bx mov si, OFFSET My_Int_21_Hook pop es mov di, OFFSET My_Real_Mode_Call_Struc int 31h jc ERROR ; ; Hook real mode int 21h with the call-back address ; mov ax, 0201h mov bl, 21h [Page 65] int 31h jc ERROR [Page 66] ;****************************************************** ; ; This is the actual Int 21h hook code. It will return ; an "access denied" error for all calls made in real ; mode to delete a file. Other calls will be passed ; through to DOS. ; ; ENTRY: ; DS:SI -> Real mode SS:SP ; ES:DI -> Real mode call structure ; Interrupts disabled ; ; EXIT: ; ES:DI -> Real mode call structure ; ;****************************************************** My_Int_21_Hook: cmp es:[di.RealMode_AH], 41h jne Chain_To_DOS ; ; This is a delete file call (AH=41h). Simulate an ; iret on the real mode stack, set the real mode ; carry flag, and set the real mode AX to 5 to indicate ; an access denied error. ; cld lodsw ; Get real mode ret IP mov es:[di.RealMode_IP], ax lodsw ; Get real mode ret CS mov es:[di.RealMode_CS], ax lodsw ; Get real mode flags or ax, 1 ; Set carry flag mov es:[di.RealMode_Flags], ax add es:[di.RealMode_SP], 6 mov es:[di.RealMode_AX], 5 jmp My_Hook_Exit ; ; Chain to original Int 21h vector by replacing the ; real mode CS:IP with the original Seg:Offset. ; Chain_To_DOS: mov ax, cs:[Orig_Real_Seg] mov es:[di.RealMode_CS], ax mov ax, cs:[Orig_Real_Offset] mov es:[di.RealMode_IP], ax My_Hook_Exit: iret [Page 67] 11.5 Free Real Mode Call-Back Address This function frees a real mode call-back address that was allocated through the allocate real mode call-back address service. To Call AX = 0304h CX:DX = Real mode call-back address to free Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o Real mode call-backs are a limited resource. Your code should free any break point that it is no longer using. [Page 68] 11.6 Get State Save/Restore Addresses When a program uses the raw mode switch services (see page 77) or issues DOS calls from a hardware interrupt handler, it will need to save the state of the current task before changing modes. This service returns the addresses of two procedures used to save the state of the current task's registers. For example, the real mode address is used to save the state of the protected mode registers. The protected mode address is used to save the state of the real mode registers. This can be used to save the state of the alternate mode's registers before they are modified by the mode switch call. The current mode's registers can be saved by simply pushing them on the stack. Note: It is not necessary to call this service if using the translation services 0300h, 0301h or 0302h. It is provided for programs that use the raw mode switch service. To Call AX = 0305h Returns If function was successful: Carry flag is clear AX = Size of buffer in bytes required to save state BX:CX = Real mode address used to save/restore state SI:(E)DI = Protected mode address used to save/restore state If function was not successful: Carry flag is set Parameters To State-Save Procedures Execute a far call to the appropriate address (real or pmode) with: ES:(E)DI = Pointer to state-save buffer AL = 0 to save state AL = 1 to restore state Programmer's Notes o Some implementations of DPMI will not require the state to be saved. In this case, the buffer size returned will be zero. However, it is still valid to call the addresses returned, although they will [Page 69] just return without performing any useful function. o The save/restore functions will not modify any registers. o The address returned in BX:CX must only be called in real mode. The address returned in SI:(E)DI must only be called in protected mode. o 16-bit programs should call the address returned in SI:DI to save the real mode state. 32-bit programs should call the address returned in SI:EDI. [Page 70] Example Code The following code is a sample protected mode timer interrupt handler that saves the state of the real mode registers, issues DOS calls, and restores the state. This code assumes that the Int 31h function 0305h has been executed and that the call address and buffer size have been saved in local variables. Sample_Timer_Code: pushf call FAR PTR cs:[Next_Timer_Handler] sti ; ; Save protected mode registers ; push ds push es pusha ; ; Save real mode registers ; mov ds, cs:[My_Local_DS] mov ax, ss mov es, ax sub sp, [State_Save_Size] mov di, sp xor al, al call [PM_Save_Restore_State] ; ; Raw mode switch here ; . . . ; ; Restore real mode registers ; mov ax, ss mov es, ax mov di, sp mov al, 1 call [PM_Save_Restore_State] add sp, [State_Save_Size] ; ; Restore protected mode registers and return ; popa pop es pop ds iret [I'm not sure if state save/restore services were present in the original 1989 spec. It would be great to look at that spec and see. - MS] [Page 71] 11.7 Get Raw Mode Switch Addresses This function returns addresses that can be jumped to for low-level mode switching.To Call AX = 0306h Returns If function was successful: Carry flag is clear BX:CX = Real -> Protected mode switch address SI:(E)DI = Protected -> Real mode switch address If function was not successful: Carry flag is set Parameters To State-Save Procedures Execute a far jump to the appropriate address (real or pmode) with: AX = New DS CX = New ES DX = New SS (E)BX = New (E)SP SI = New CS (E)DI = New (E)IP The processor will be placed in the desired mode. The DS, ES, SS, (E)SP, CS, and (E)IP will contain the values specified. The (E)BP register will be preserved across the call and so can be used as a pointer. The values in (E)AX, (E)BX, (E)CX, (E)DX, (E)SI, and (E)DI will be undefined. On an 80386 or 80486 the FS and GS segment registers will contain zero after the mode switch. Programmer's Notes o The address returned in BX:CX must only be called in real mode to switch into protected mode. The address returned in SI:(E)DI must only be called in protected mode to switch into real mode. o 16-bit programs should call the address returned in SI:DI to switch from protected to real mode. 32-bit programs should call the address returned in SI:EDI. o It is up to the caller to save and restore the state of the task when using this function to [Page 72] switch modes. This usually requires using the state save function (see page 74). o The parameters must contain segment values appropriate for the mode that is being switched to. If invalid selectors are specified when switching into protected mode, an exception will occur. o Applications may find functions 0300h, 0301h, 0302h, and 0304h more convenient to use than using this type of mode switching. [I'm not sure if raw mode switching services were present in the original 1989 spec. It would be great to look at that spec and see. - MS] [Page 73] 11. GET VERSION Function 0400h returns the version of DPMI services supported. Note that this is not necessarily the version of any operating system that supports DPMI. It should be used by programs to determine what calls are legal in the current environment. To Call AX = 0400h Returns AH = Major version AL = Minor version BX = Flags Bit 0 = 1 if running under an 80386 DPMI implementation Bit 1 = 1 if processor is returned to real mode for reflected interrupts (as opposed to Virtual 8086 mode). Bit 2 = 1 if virtual memory is supported Bit 3 = 1 if direct NETBIOS calls from protected mode are not supported. All other bits are zero and reserved for later use CL = Processor type 02 = 80286 03 = 80386 04 = 80486 DH = Current value of virtual master PIC base interrupt DL = Current value of virtual slave PIC base interrupt Carry flag clear (call can not fail) Programmer's Notes None [I had to write the description of bit 3 myself. It would be nice to compare it to the one in the original 1989 spec. - MS] [Page 74] 12. MEMORY MANAGEMENT SERVICES These functions are provided to allocate linear address space. [Page 75] 13.1 Get Free Memory Information This function is provided so that protected mode applications can determine how much memory is available. Under DPMI implementations that support virtual memory, it is important to consider issues such as the amount of available physical memory. Note that since DPMI applications will often run in multi-tasking environments, this function must be considered only advisory. To Call AX = 0500h ES:(E)DI = Selector:Offset of 30h byte buffer Returns If function was successful: Carry flag is clear. ES:(E)DI = Selector:Offset of buffer with the following structure: [Page 76] Offset Description 00h Largest available free block in bytes 04h Maximum unlocked page allocation 08h Maximum locked page allocation 0Ch Linear addr space size in pages 10h Total number of unlocked pages 14h Number of free pages 18h Total number of physical pages 1Ch Free linear address space in pages 20h Size of paging file/partition in pages 24h-2Fh Reserved If function was not successful: Carry flag is set. [Page 77] Programmer's Notes o 32-bit programs must use ES:EDI to point to the buffer. 16-bit programs should use ES:DI. o DPMI implementations that do not support virtual memory (returned in flags from Get Version call) will only fill in the first field. This value specifies that largest allocation that could be made using function 0501h. Other fields will be set to -1. o Only the first field of this structure is guaranteed to contain a valid value. All fields that are not returned by the DPMI implementation will be set to -1 (0FFFFFFFFh) to indicate that the information is not available. o The field at offset 00h specifies the largest block of contiguous linear memory in bytes that could be allocated if the memory were to be allocated and left unlocked. o The field at offset 04h specifies the number of pages that could be allocated. This is the value returned by field 00h / page size. o The field at offset 08h specifies the largest block of memory in pages that could be allocated and then locked. o The field at offset 0Ch specifies the size of the total linear address space in pages. This includes all linear address space that has already been allocated. o The field at offset 10h specifies the total number of pages that are currently unlocked and could be paged out. This value also contains any free pages. o The field at offset 14h specifies the number of physical pages that currently are not in use. o The field at offset 18h specifies the total number of physical pages that the DPMI host manages. This value includes all free, locked, and unlocked physical pages. o The field at offset 20h specifies the size of the DPMI host's paging partition or file in pages. [Page 78] 13.2 Allocate Memory Block This function allocates and commits linear memory. To Call AX = 0501h BX:CX = Size of memory block to allocate in bytes Returns If function was successful: Carry flag is clear BX:CX = Linear address of allocated memory block SI:DI = Memory block handle (used to resize and free) If function was unsuccessful: Carry flag is set Programmer's Notes o This function does not allocate any selectors for the memory block. It is the responsibility of the caller to allocate and initialize any selectors needed to access the memory. o Under DPMI implementations that support virtual memory the memory block will be allocated unlocked. If some or all of the memory should be locked you will need to use either the lock selector function or the lock linear region function. o Under many implementations of DPMI, allocations will be page granular. This means that an allocation of 1001h bytes will result in an allocation of 2000h bytes. Therefore it is best to always allocate memory in multiples of 4K. [Page 79] 13.3 Free Memory Block This function frees a memory block that was allocate through the allocate memory block function. To Call AX = 0502h SI:DI = Handle of memory block to free Returns If function was successful: Carry flag is clear If function was unsuccessful: Carry flag is set Programmer's Notes o Your program must also free any selectors that it allocated to point to the memory block. [Page 80] 13.4 Resize Memory Block This function changes the size of a memory block that was allocated through the allocate memory block function. To Call AX = 0503h BX:CX = New size of memory block to allocate in bytes SI:DI = Handle of memory block to resize Returns If function was successful: Carry flag is clear BX:CX = New linear address of memory block SI:DI = New handle of memory block If function was unsuccessful: Carry flag is set Programmer's Notes o This function may change the linear address of the memory block and the memory handle. Therefore, you will need to update any selectors that point to the block after resizing it. You must use the new handle instead of the old one. o This function will generate an error if a memory block is resized to 0 bytes. [Page 81] 13. PAGE LOCKING SERVICES These services are only useful under DPMI implementations that support virtual memory. They will be ignored by 16-bit DPMI implementations (although they will always return with carry clear to indicate success). Some implementations of DPMI may ignore these calls. However, if the calls are ignored then the DPMI host will be able to handle page faults at arbitrary points during the application's execution including interrupt and exception handler code. Although memory ranges are specified in bytes, the actual unit of memory that will be locked will be one or more pages. Page locks are maintained as a count. When the count is decremented to zero, the page is unlocked and can be swapped to disk. This means that if a region of memory is locked three times then it must be unlocked three times before the pages will be unlocked. [Page 82] 14.1 Lock Linear Region This function locks a specified linear address range. To Call AX = 0600h BX:CX = Starting linear address of memory to lock SI:DI = Size of region to lock in bytes Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o If this function fails then none of the memory will be locked. o If the specified region overlaps part of a page at the beginning or end of the region, the page(s) will be locked. [Page 83] 14.2 Unlock Linear Region This function unlocks a specified linear address range that was previously locked using the Lock Linear Region function. To Call AX = 0601h BX:CX = Starting linear address of memory to unlock SI:DI = Size of region to unlock in bytes Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o If this function fails then none of the memory will be unlocked. o An error will be returned if the memory was not previously locked or if the specified region is invalid. o If the specified region overlaps part of a page at the beginning or end of the region, the page(s) will be unlocked. o Even if the function succeeds, the memory will remain locked if the lock count is not decremented to zero. [Page 84] 14.3 Mark Real Mode Region as Pageable Under some implementations of DPMI, all memory in virtual 8086 mode is locked by default. If a protected mode program is using memory in the first megabyte of address space, it is a good idea to use this function to turn off automatic page locking for regions of memory that will not be touched at interrupt time. Do not mark memory as pageable in regions that are not owned by your application. For example, you should not mark all free DOS memory as pageable since it may cause a page fault to occur while inside of DOS (causing a crash). Also, do not mark the DPMI host data area as pageable. It is very important to relock any real mode memory using function 0603h before terminating a program. Memory that remains unlocked after a program has terminated could result in fatal page faults when other software is executed in that address space. Note that address space marked as pageable by this function can be locked using function 0600h. This function is just an advisory service to allow memory that does not need to be locked to be paged out. This function just disables any automatic locking of real mode memory performed by the DPMI host. To Call AX = 0602h BX:CX = Starting linear address of memory to mark as pageable SI:DI = Size of region to page in bytes Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o If this function fails then none of the memory will be unlocked. o If the specified region overlaps part of a page at the beginning or end of the region, the page(s) will be not be marked as pageable. [Page 85] o When your program terminates it should call function 0603h to relock the memory region. o Unlike the lock and unlock calls, the pageability of the real mode region is maintained as a binary state, not a count. Therefore, do not call this function multiple times for a given linear region. [Page 86] 14.4 Relock Real Mode Region This function is used to relock memory regions that were marked as pageable by the previous function. To Call AX = 0603h BX:CX = Starting linear address of memory to relock SI:DI = Size of region to page in bytes Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o If this function fails then none of the memory will be relocked. o If the specified region overlaps part of a page at the beginning or end of the region, the page(s) will be not be relocked. [Page 87] 14. DEMAND PAGING PERFORMANCE TUNING SERVICES Some applications will discard memory objects or will not access objects for long periods of time. These services can be used to improve the performance of demand paging. Although these functions are only relevant for DPMI implementations that support virtual memory, other implementations will ignore these functions (it will always return carry clear). Therefore your code can always call these functions regardless of the environment it is running under. Since both of these functions are simply advisory functions, the operating system may choose to ignore them. In any case, your code should function properly even if the functions fail. [Page 88] 15.1 Mark Page as Demand Paging Candidate This function is used to inform the operating system that a range of pages should be placed at the head of the page out candidate list. This will force these pages to be swapped to disk ahead of other pages even if the memory has been accessed recently. However, all memory contents will be preserved. This is useful, for example, if a program knows that a given piece of data will not be accessed for a long period of time. That data is ideal for swapping to disk since the physical memory it now occupies can be used for other purposes. To Call AX = 0700h BX:CX = Starting linear page number of pages to mark SI:DI = Number of pages to mark as paging candidates Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes o This function does not force the pages to be swapped to disk immediately. [I have reconstructed this function from bytes back to pages manually. It's pretty simple, so chances are that my description matches the one in the original 1989 spec word for word, but it would still be nice to verify this. - MS] [Page 89] 15.3 Discard Page Contents This function discards the entire contents of a given linear memory range. It is used after a memory object that occupied a given piece of memory has been discarded. The contents of the region will be undefined the next time the memory is accessed. All values previously stored in this memory will be lost. To Call AX = 0701h BX:CX = Starting linear page number of pages to discard SI:DI = Number of pages to discard Returns If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. Programmer's Notes None [I have reconstructed this function from bytes back to pages manually. It's pretty simple, so chances are that my description matches the one in the original 1989 spec word for word, but it would still be nice to verify this. - MS] [Page 90] 15. PHYSICAL ADDRESS MAPPING Memory mapped devices such as network adapters and displays sometimes have memory mapped at physical addresses that lie outside of the normal 1Mb of memory that is addressable in real mode. Under many implementations of DPMI, all addresses are linear addresses since they use the paging mechanism of the 80386. This service can be used by device drivers to convert a physical address into a linear address. The linear address can then be used to access the device memory. Some implementations of DPMI may not support this call because it could be used to circumvent system protection. This call should only be used by programs that absolutely require direct access to a memory mapped device. To Call AX = 0800h BX:CX = Physical address of memory SI:DI = Size of region to map in bytes Returns If function was successful: Carry flag is clear. BX:CX = Linear address that can be used to access the physical memory If function was not successful: Carry flag is set. Programmer's Notes o Under DPMI implementations that do not use the 80386 paging mechanism, the function will always succeed and the address returned will be equal to the physical address parameter passed into this function. o It is up to the caller to build an appropriate selector to access the memory. o Do not use this service to access memory that is mapped in the first megabyte of address space (the real mode addressable region). [Page 91] 16. VIRTUAL INTERRUPT STATE FUNCTIONS Under many implementations of DPMI, the interrupt flag in protected mode will always be set (interrupts enabled). This is because the program is running under a protected operating system that can not allow programs to disable physical hardware interrupts. However, the operating system will maintain a "virtual" interrupt state for protected mode programs. When the program executes a cli instruction, the program's virtual interrupt state will be disabled, and the program will not receive any hardware interrupts until it executes an sti to reenable interrupts (or calls service 0901h). When a protected mode program executes a pushf instruction, the real processor flags will be pushed onto the stack. Thus, examining the flags pushed on the stack is not sufficient to determine the state of the program's virtual interrupt flag. These services enable programs to get and modify the state of their virtual interrupt flag. The following sample code enters an interrupt critical section and then restores the virtual interrupt state to it's previous state. ; ; Disable interrupts and get previous interrupt state ; mov ax, 0900h int 31h ; ; At this point AX = 0900h or 0901h ; . . . ; ; Restore previous state (assumes AX unchanged) ; int 31h [Page 92] 17.1 Get and Disable Virtual Interrupt State This function will disable the virtual interrupt flag and return the previous state of the virtual interrupt flag. To Call AX = 0900h Returns Carry flag clear (this function always succeeds) Virtual interrupts are disabled AL = 0 if virtual interrupts were previously disabled AL = 1 if virtual interrupts were previously enabled Programmer's Notes o AH will not be changed by this procedure. Therefore, to restore the previous state, simply execute an Int 31h. [Page 93] 17.2 Get and Enable Virtual Interrupt State This function will enable the virtual interrupt flag and return the previous state of the virtual interrupt flag. To Call AX = 0901h Returns Carry flag clear (this function always succeeds) Virtual interrupts are enabled AL = 0 if virtual interrupts were previously disabled AL = 1 if virtual interrupts were previously enabled Programmer's Notes o AH will not be changed by this procedure. Therefore, to restore the previous state, simply execute an Int 31h. [Page 94] 17.3 Get Virtual Interrupt State This function will return the current state of the virtual interrupt flag. To Call AX = 0902h Returns Carry flag clear (this function always succeeds) AL = 0 if virtual interrupts are disabled AL = 1 if virtual interrupts are enabled Programmer's Notes None [Page 95] 17. DOS AND BIOS CALLS Programs running in protected mode can make DOS and BIOS calls just as they would when running in real mode (with some minor exceptions). The only difference is that the code and data of the program can reside in memory above one megabyte, and all pointers use protected mode selectors instead of real mode segments to point to data. As in real mode, DOS is called by executing Int 21h. When an Int 21h is executed in protected mode any data that is required by that call will be copied to a buffer in real mode and then the real mode DOS will be called with a pointer to the copied data. The host is responsible for copying data and translating pointers -- programs that use DPMI need not worry about how [The above two paragraphs contained references to Int 21h being an "extension", so I had to edit them a bit. I would be nice to see how they looked in the original 1989 spec. - MS] For example, lets look at a series of DOS calls to open and read a file: ; ; Open the file with read-only access ; mov ax, 3D00h mov dx, OFFSET File_Name_String int 21h jc Error ; ; Read the first 6000h bytes of the file ; mov bx, ax mov ah, 3Fh mov cx, 6000h mov dx, OFFSET Read_Buffer int 21h jc Error ; ; Close the file ; mov ah, 3Eh int 21h The open file call takes an ASCIIZ pathname as a parameter. Since the address passed to the protected mode Int 21h handler is a selector:offset (DS contains a selector to program's protected mode data segment), real mode DOS would not be able to access the data. The protected mode DOS translator copies the string into a real mode buffer and then calls DOS in real mode with DS:DX pointing to the real mode buffer. The values in other registers are not modified so the call number in AX will not be changed. When real mode DOS returns, the values returned in the flags and all non-segment registers will be returned to the protected mode program. In this case, the carry flag indicates an error and the file handle will be returned in AX. [Page 96] The second DOS call reads part of the file into a buffer. Once again, since the buffer can not be accessed by real mode DOS, the data must be copied through a buffer. The data will be read into a real mode buffer and copied into the protected mode memory. Since the real mode buffer is usually smaller than 6000h bytes the translator will probably have to break the read into several smaller pieces. However, all the copying of data and multiple reads will be invisible to the caller. The read will behave exactly as if the code were being executed in real mode. For the final call (close file), the protected mode Int 21h hook just reflects the interrupt to real mode without translating anything. Since the DOS close file command has no pointer parameters, no translation is necessary. The sample code above is 16-bit code and would work on an 80286 DPMI implementation. However, DPMI supports 32-bit programs on 80386 and 80486 processors. The only difference between 32-bit and 16-bit programs that pointers require a 32-bit offset in the extended register (EDX instead of DX) and that DOS read and write calls take a 32-bit count in ECX. The 32-bit equivalent of the sample code is provided below. ; ; Open the file with read-only access ; mov ax, 3D00h mov edx, OFFSET File_Name_String int 21h jc Error ; ; Read the first 6000h bytes of the file ; mov bx, ax mov ah, 3Fh mov ecx, 6000h mov edx, OFFSET Read_Buffer int 21h jc Error ; ; Close the file ; mov ah, 3Eh int 21h [Page 97] 18. SUPPORTED DOS CALLS This section describes the differences between real mode DOS and BIOS calls and those made in protected mode. Obviously, pointers use protected mode selectors instead of real mode segments. 32-bit programs must use 48-bit pointers and the size parameters for some calls such as file reads and writes will be 32-bit. For example DOS reads and writes use ECX for the size parameter instead of CX for 32-bit programs. See page 9 for an example of 32-bit DOS code. All DOS calls that are not mentioned in this section should work exactly as documented in _The MS-DOS Encyclopedia_. The minimum assumed DOS version is 3.xx. [Page 98] 8.1 DOS Calls That Are Not Supported The following DOS calls are not supported in protected mode. They will fail if called. 8.1.1 Unsupported Interrupts INT DESCRIPTION 20h Terminate Program 25h Absolute Disk Read 26h Absolute Disk Write 27h Terminate and Stay Resident 8.1.2 Unsupported Interrupt 21h DOS Functions AH DESCRIPTION 00h Terminate Process 0Fh Open File with FCB 10h Close File with FCB 14h Sequential Read 15h Sequential Write 16h Create File with FCB 21h Random Read 22h Randon Write 23h Get File Size 24h Set Relative Record 27h Random Block Read 28h Random Block Write [Page 99] 8.2 Calls That Behave Differently in Protected Mode 8.2.1 Function 25h and 35h -- Set/Get Interrupt Vector These functions will set or get the protected mode interrupt vector. They can be used to hook hardware interrupts (such as the timer or keyboard interrupt) as well as hooking any software interrupts your program wishes to monitor. With a few exceptions, software interrupts issued in real mode will _not_ be reflected to protected mode interrupt handlers before being reflected to real mode. See page (?) for more information on hooking interrupts in protected mode programs. 32-bit programs must use 48-bit pointers and must use the iretd instruction to return from interrupts. 8.2.2 Funtion 31h -- Terminate and Stay Resident The value in DX specifies the number of paragraphs of real mode memory to reserve for the program. _The_ _reserved_memory_must_include_any_memory_allocated_for_ _the_DOS_extender_when_the_program_switched_to_ _protected_mode._ All protected mode memory allocated through Int 31h or protected mode calls to DOS function 48h will not be deallocated -- It is up to the T&SR program to free any unneeded protected mode memory. Note that protected mode "pop-up" programs will need to be very careful when saving and restoring the current DOS state to preserve both the real mode and protected mode states of the current task. This type of program must call the state save functions documented on page (?). It must also save and restore the DTA address in _both_ protected mode and real mode. To get the current real mode DTA the program must use the translation services to call the real mode DOS Get DTA function. [Note the reference to state save functions. I'm not sure if they were present in the original 1989 spec. Would be great to look at that spec. - MS] 8.2.3 Function 32h -- Get Current Country Data This call returns a 34-byte buffer that contains a dword call address at offset 12h that is used for case-mapping. This dword will contain a _real_mode_ _address_. If you wish to call the case-mapping procedure you will need to use the DPMI translation service to simulate a real mode far call (see page(?)). 9.2.4 Functions 3Fh and 40h -- Read/Write File or Device 32-bit programs must specify the size of the read or write in the ECX register instead of the CX register. This allows for read and writes of greater than 64K. Note that 16-bit programs are still limited to reads of 0FFFFh bytes. [Page 100] 8.2.5 Function 44h, Subfunctions 02h, 03h, 04h, and 05h These IOCTL subfunctions are used to receive data from a device or send data to a device. Since it is not possible to break the transfers into small pieces, the caller should assume that a transfer of greater than 2K bytes will fail unless the address of the buffer is in the DOS addressable first megabyte. 8.2.6 Function 44h, Subfunction 0Ch Only minor function codes 45h (get iteration count) and 65h (set iteration count) are supported from protected mode. Extensions of this IOCTL for code-page switching (functions function codes 4Ah, 4Ch, 4Dh, 6Ah, and 6Bh) are _not_ supported for protected mode programs. You must use the translation services if you need to use this IOCTL to switch code pages (see page(?)). 8.2.7 Functions 48h, 49h, 4Ah It is recommended that all memory allocations be made through the DPMI memory allocation services (see page (?)). DOS memory allocation calls issued by a protected mode program will allocate extended memory. This memory is _not_ addressable by real mode DOS. 32-bit programs must specify the number of paragraphs to allocate in the EBX register. This allows for memory allocations of greater than 1Mb. To determine the size of the largest available block set (E)BX to -1 and call function 48h. 8.2.8 Function 4Bh -- Load and Execute Program This function can not be used to load a program overlay from a protected mode program. However, you can execute another program using subfunction 0. The program will be executed in real mode. However, the child program can enter protected mode using the DPMI real to protected mode switch API. The environment pointer in the exec parameter block is ignored and should be set to 0 by 16-bit programs. 32-bit programs should place an fword pointer to the command tail at offset 0. [Page 101] 8.2.9 Function 4Ch -- Terminate Process with Return Code This is the only supported form of program termination for protected mode DOS programs. It behaves exactly as it would in real mode. It will free any memory that was allocated by the protected mode program, and return to the parent program. The protected mode Int 23h and Int 24h vectors will be restored to the same value as 8.2.10 Function 65h -- Get Extended Country Information This function is supported for protected mode programs. However, all doubleword parameters returned will contain _real_mode_addresses_. This means the case conversion procedure address and all pointers to tables will contain real mode segment:offset addresses. You must use the translation services to call the case conversion procedure in real mode. [Page 102] 19. SUPPORTED BIOS CALLS All BIOS calls that pass parameters in the AX, BX, CX, DX, SI, DI, and BP registers, and that contain no pointers or segment values in these registers will be supported by all implementations of DPMI (provided, of course, that the API is supported by the machine's BIOS). For the sake of clarity and completeness, this document contains a list of every BIOS API that will be supported, including those that are register based APIs. APIs that are not register based are documented individually. [Page 103] 9.1 Interrupt 10h -- Video 9.1.1 Register Based Functions (supported): AH DESCRIPTION 00h Set Mode 01h Set Cursor Type 02h Set Cursor Position 03h Read Cursor Position 04h Read Light Pen Position 05h Select Active Display Page 06h Scroll Active Page Up 07h Scroll Active Page Down 08h Read Attribute/Char at Cursor Position 09h Write Attribute/Char at Cursor Position 0Ah Write Character at Cursor Position 0Bh Set Color Palette 0Ch Write Dot 0Dh Read Dot 0Eh Write Teletype to Active Page 0Fh Read Current Video State 1Ah Read/Write Display Combination Code 9.1.2 Function 10h -- Set Palette Registers All subfunctions of this API are supported. 9.1.3 Function 13h -- Write String This call is supported provided the string is not longer than 2K bytes. 9.1.4 Functions that are not Fully Supported Many of these functions have APIs that are register based. All register based calls are supported. However, any APIs that contain pointer parameters are not supported under DPMI. AH DESCRIPTION 11h Character Generator 12h Alternate Select 14h Load LCD Character Font 15h Return Physical Display Parameters 1Bh Return Functionality/State Info 1Ch Save/Restore Video State [Page 104] 9.2 Interrupt 11h -- Equipment Determination Since interrupt 11h is register based, it will be supported by all implementations of DPMI. COMPATIBILITY WARNING: EISA machines will destroy the high word of the EAX register on machines with 80386 CPUs. [Page 105] 9.3 Interrupt 12h -- Memory Size Determination Since interrupt 12h is register based, it will be supported by all implementations of DPMI. [Page 106] 9.4 Interrupt 13h -- Diskette / Fixed Disk Interface Application programs have no reason to use this interrupt. In any case, since direct disk access will not be allowed most implementations of DPMI, programs can not rely on these functions. [Page 107] 9.5 Interrupt 14h -- Asynchronous Communications Since all asynchronous communication APIs are register based, they are all supported. AH DESCRIPTION 00h Initialize Communications Port 01h Send Character 02h Receive Character 03h Read Status 04h Extended Initialize 05h Extended Communications Port Control [Page 108] 9.6 Interrupt 15h -- System Services 9.6.1 Register Based Functions (supported): AH DESCRIPTION 00h Turn Cassette Motor On 01h Turn Cassette Motor Off 40h Read/Modify Profiles 42h Request System Power-Off 43h Read System Status 44h Activate/Deactivate Internal Modem 80h Device Open 81h Device Close 82h Program Termination 84h Joystick Support 86h Wait 87h Extended Memory Size C3h Enable/Disable Watchdog Time-Out C4h Porgrammable Option Select (POS) 9.6.2 Function C0h -- Return System Configuration Parameters This call is supported. The pointer to the system descriptor vector will be in ES:EBX for 32-bit programs. 9.6.3 Function C1h -- Pointing Device Interface This interface will _not_ be supported under most implementations of DPMI. Programs that use a mouse are encouraged to use the Int 33h interface documented on page 26. 9.6.4 Functions that are _Not_ Supported: AH DESCRIPTION 02h Read Blocks from Cassette 03h Write Blocks to Cassette 0Fh Format Unit Periodic Interrupt 21h Power-On Self-Test Error Log 41h Wait for External Event 4Fh Keyboard Intercept 83h Wait Event 85h System Request Key Pressed 87h Move Block 89h Switch Processor to Protected Mode 90h Device Busy 91h Interrupt Complete C1h Return Extended BIOS Data Area Seg [Page 109] 9.7 Interrupt 16h -- Keyboard Since all keyboard APIs are register based, they are all supported. AH DESCRIPTION 00h Keyboard Read 01h Keyboard Status 02h Shift Status 03h Set Typematic Rate 04h Keyboard Click Adjustment 05h Keyboard Write 10h Extended Keyboard Read 11h Extended Keyboard Status 12h Extended Shift Status [Page 110] 9.8 Interrupt 17h -- Printer Since all printer APIs are register based, they are all supported. AH DESCRIPTION 00h Print Character 01h Initialize the Printer 02h Read Status [Page 111] 9.9 Interrupt 1Ah -- System-Timer and Real-Time Clock 9.9.1 Register Based Functions (supported): AH DESCRIPTION 00h Read System-Timer Time Counter 01h Set System-Timer Time Counter 02h Read Real-Time Clock Time 03h Set Real-Time Clock Time 04h Read Real-Time Clock Date 05h Set Real-Time Clock Date 07h Set Real-Time Clock Alarm 08h Set Real-Time Clock Activated Power On 09h Read Real-Time Clock Alarm Status 0Ah Read System-Timer Day Counter 0Bh Set System-Timer Day Counter 9.9.2 Function 06h -- Set Real-Time Clock Alarm Although this call is register based and therefore requires no translation before being passed to real mode, the caller is required to hook the _real_mode_ interrupt 4Ah vector to intercept the alarm interrupt. [Page 112] 20. MOUSE DRIVER INTERFACE DPMI supports a subset of the standard Int 33h mouse driver interface for protected mode programs. It may be necessary for programs to call the mouse driver in real mode either before switching to protected mode or by using the translation services to completely save and restore the mouse driver state. 10.1 Mouse Calls that Are Supported 10.1.1 Register Based Calls AH DESCRIPTION 00h Reset Mouse and Get Status 01h Show Mouse Pointer 02h Hide Mouse Pointer 03h Get Mouse Position and Button Status 04h Set Mouse Pointer Position 05h Get Button Press Information 06h Get Button Release Information 07h Set Horizontal Limits for Pointer 08h Set Vertical Limits for Pointer 0Ah Set Text Pointer Type 0Bh Read Mouse Motion Counters 0Dh Turn On Light Pen Emulation 0Eh Turn Off Light Pen Emulation 0Fh Set Mickeys to Pixels Ratio 10h Set Mouse Pointer Exclusion Area 13h Set Double Speed Threshold 15h Get Mouse Save State Buffer Size 1Ah Set Mouse Sensitivity 1Bh Get Mouse Sensitivity 1Ch Set Mouse Interrupt Rate 1Dh Select Pointer Page 1Eh Get Pointer Page 20h Enable Mouse 21h Reset Mouse Driver 22h Set Language for Mouse Driver 23h Get Language Number 24h Get Mouse Information 10.1.2 Function 09h -- Set Pointer Shape This call works exactly as it would in real mode. However, 32-bit programs must use ES:EDX to point to the pointer image buffer. [Page 113] 10.1.3 Function 0Ch -- Set User-Defined Mouse Event Handler 32-bit programs must call this function with ES:EDX = Selector:Offset of the handler and will need to execute a 32-bit far return to return from the event call-back. For both 16-bit and 32-bit programs the protected mode DS will _not_ point to the mouse driver data segment when the event handler is called. Do not rely on any specific value in the DS register when the event handler is called. 10.1.4 Functions 16h and 17h -- Save/Restore Mouse Driver State These calls work exactly as they would in real mode. However, 32-bit programs must use ES:EDX to point to the buffer. 10.2 Mouse Calls that Are Not Supported AH DESCRIPTION 14h Swap User-Defined Event Handlers 18h Set Alternate Event Handler 19h Get Address of Alternate Event Handler 1Fh Disable Mouse Driver [Page 114] 21. NETBIOS Some implementations of DPMI support NetBIOS calls in protected mode, although this is not required. Programs can determine wether [sic] or not the current DPMI implementation supports NetBIOS calls from protected mode by examining the flags returned from the Get Version call (see page (?)). A program that uses NetBIOS and needs to run on any DPMI implementation will need to use the translation services documented on page (?). 32-bit programs can call NetBIOS if it is supported. In this case, ES:EBX must be used to point to the Network Control Block (NCB). However, pointers within the NCB are restricted to a 16-bit offset. Therefore, all buffers must reside within the first 64K of the buffer's segment. [Page 115] 22. INTERRUPTS 23H AND 24H DOS provides two interrupts that programs can hook to handle Ctrl+Break and critical device errors. These interrupts are reflected to protected mode programs if the program hooks the interrupt in protected mode. Although both of these interrupts can be used to terminate a program in real mode, they can not be used to terminate protected mode programs. Protected mode Int 23h and Int 24h handlers must reside in locked memory and all data that they touch must also be locked. This is required to prevent a page fault from occurring at a time when DOS can not be called to read the data in from disk. These interrupt handlers will always be called on a locked stack. 12.1.1 Interrupt 23h Interrupt 23h is the DOS Ctrl+Break interrupt. This interrupt will be reflected to protected mode if a protected mode interrupt handler is installed. Unlike real mode DOS, the interrupt handler _must_return_. This interrupt can not be used to terminate a protected mode program and the value of the carry flag will be ignored when the interrupt returns.It is suggested that you set a flag in your program that will be examined later and then execute an iret to return from the interrupt. Int 23h is ignored for protected mode programs unless it is hooked in protected mode. 12.1.2 Interrupt 24h When Int 24h is called in protected mode, SS:(E)BP will point to a standard Int 24h stack frame. 32-bit programs will be called with a 32-bit iret frame but the rest of the stack frame will be exactly as that of a 16-bit Int 24h. The values on the stack will contain the values passed to DOS in _real_mode_. Therefore, the segment register values on the stack will be real mode segments, not selectors. Protected mode Int 24h handlers _must_iret_. Since programs can not be terminated by a critical error handler, an attempt to abort the program (returning with AL=02h) will be ignored and the DOS call will be failed. The default Int 24h handler will fail all critical errors. Therefore, unless the protected mode Int 24h vector is hooked, all DOS calls that generate a critical error will fail. [Page 116] 23. OTHER APIS In general, any software interrupt interface that passes parameters in the EAX, EBX, ECX, EDX, ESI, EDI, and EBP registers will work as long as none of the registers contains a segment value. In other words, if a software interrupt interface is completely register based without any pointers, segment register, or stack parameters, that API could work under any DPMI implementation. More complex APIs require the caller to use the translation services described on page 58.