Protected Mode API For DOS Extended Applications
Version 0.9
July 26, 1990
HTML markup by John English, June 1997
2. General Notes for Protected Mode Programs
5. Loading DPMI Clients and Extended Applications
6. Terminating A Protected Mode Program
8. LDT Descriptor Management Services
9. DOS Memory Management Services
13. Memory Management Services
15. Demand Paging Performance Tuning Services
17. Virtual interrupt State Functions
18. Get Vendor Specific API Entry Point
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 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. The program(s) that use DPMI services are called DPMI clients. Generally, DPMI clients are two categories:
Figure 1 below shows a picture of how this works. The application code sits on top of a set of base extender functions and APIs. The extender then has separate modules for each type of extension service and code to "fill in the slack" where services are lacking. An example of a typical extender service is protected mode program loading. The actual shipped application is the application code bound in with the extender and all of its styles of client support.
The host support is generally an extension of the base OS functions or a device driver used to extend the base OS functions.
This document is intended to provide a definition of the DPMI services that a DPMI host would be required to implement and that a DPMI client would use.
Figure 1. Application/Extender/Client/Host/OS structure
+----------------------------------------------------------+ | | | +----------------------------------------------------+ | | | | | | | Application Code | | | | | | | +----------------------------------------------------+ | | | | +----------------------------------------------------+ | | | Extender Base (including APIs) | | | | -------------------------------------------------- | | | | DPMI | | | | client | | | +------------+ | | | | VCPI | | | | client | | | +------------+ | | | | XMS | | | | client | | | +------------+ | | | | Top-down | | | | client | | | +-------------+ | | | +----------------------------------------------------------+ +------------+ | | | | | |------------+ | | | | DPMI | | | host | VCPI |------------+ | | | | | | | | | |------------| XMS |-------------+ | | EMS | | Top-down | | | | | (Int 15h) | +----------------------------------------------------+ +----------------------------------------------------+ | | | Operating System (e.g. DOS) | | | +----------------------------------------------------+
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.
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:
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. Extenders which use the services provided by these DPMI host drivers will translate protected mode DOS calls to real mode DOS calls. Normally these extenders will invoke DPMI services to return the processor to real mode (instead of virtual 8086 mode) when calling DOS.
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.
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.
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).
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.
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.
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 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.
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:
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 interruptPrograms 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.
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.
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.
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.
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.
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.
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.
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.
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.
There are three different ways a client can force a mode switch between protected and real mode:
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.
Most Int 31h calls can fail. The DPMI 0.9 specification does not specify error return codes for most calls. When a call fails it will set the carry flag and return with the value in AX unmodified unless otherwise specified. However, future DPMI implementations will return error codes in the AX register. All specific error codes will have the high bit (bit 15) set. If a function returns with carry set and the high bit of AX clear, it should be treated as a general failure. Specific error codes will allow programs running under future DPMI implementations to take appropriate corrective action in some cases.
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.
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.
AX = 1687h Execute an Int 2Fh (not an Int 31h)
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
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.
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
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 preservedIf function was not successful: Carry flag is set. Program is executing in real mode
; ; 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
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.
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.
AX = 1686h Execute an Int 2Fh (not an Int 31h)
If executing in protected mode under DPMI: AX = 0 If executing in real mode or not under DPMI then: AX != 0
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).
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.
AX = 0000h CX = Number of descriptors to allocate
If function was successful: Carry flag is clear. AX = Base selector If function was not successful: Carry flag is set.
This function is used to free descriptors that were allocated through the Allocate LDT Descriptors function.
AX = 0001h BX = Selector to free
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
This function is used to convert real mode segments into descriptors that are addressable by protected mode programs.
AX = 0002h BX = Real mode segment address
If function was successful: Carry flag is clear. AX = Selector mapped to real mode segment If function was not successful: Carry flag is set.
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.
AX = 0003h
Carry flag clear (this function always succeeds) AX = Value to add to get to next selector
Functions 0004h and 0005h are reserved and should not be called.
This function returns the 32-bit linear base address of the specified segment.
AX = 0006h BX = Selector
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.
This function changes the 32-bit linear base address of the specified selector.
AX = 0007h BX = Selector CX:DX = 32-bit linear base address for segment
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
This function sets the limit for the specified segment.
AX = 0008h BX = Selector CX:DX = 32-bit segment limit
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
This function allows a protected mode program to modify the access rights and type fields of a descriptor.
AX = 0009h BX = Selector CL = Access rights/type byte CH = 80386 extended access rights/type byte (32-bit DPMI implementations only)
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
+-------------------------------+ | 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=>PresentA parameter which does not meet the above requirements is invalid, and causes the function to return with the carry flag set.
+-------------------------------+ | 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 GranularA parameter which does not meet the above requirements is invalid, and causes the function to return with the carry flag set.
This function will create a data descriptor that has the same base and limit as the specified code segment descriptor.
AX = 000Ah BX = Code segment selector
If function was successful: Carry flag is clear. AX = New data selector If function was not successful: Carry flag is set.
This function copies the descriptor table entry for a specified descriptor into an eight byte buffer.
AX = 000Bh BX = Selector ES:(E)DI = Pointer to an 8 byte buffer to receive copy of descriptor
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.
This function copies an eight byte buffer into the LDT entry for a specified descriptor.
AX = 000Ch BX = Selector ES:(E)DI = Pointer to an 8 byte buffer that contains descriptor
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
This function attempts to allocate a specific LDT descriptor.
AX = 000Dh BX = Selector
If function was successful: Carry flag is clear. Descriptor has been allocated If function was not successful: Carry flag is set.
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.
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.
AX = 0100h BX = Number of paragraphs (16 byte blocks) desired
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
This function frees memory that was allocated through the Allocate DOS Memory Block function.
AX = 0101h DX = Selector of block to free
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
This function is used to grow or shrink a memory block that was allocated through the Allocate DOS Memory Block function.
AX = 0102h BX = New block size in paragraphs DX = Selector of block to modify
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
These services allow protected mode applications to intercept real and protected mode interrupts and hook processor exceptions.
This function returns the value of the current task's real mode interrupt vector for the specified interrupt.
AX = 0200h BL = Interrupt number
Carry flag is clear. CX:DX = Segment:Offset of real mode interrupt handler
AX = 0201h BL = Interrupt number CX:DX = Segment:Offset of real mode interrupt handler
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
AX = 0202h BL = Exception/fault number (00h-1Fh)
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.
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.
AX = 0203h BL = Exception/fault number (00h-1Fh) CX:(E)DX = Selector:Offset of exception handler
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set. The value passed in BL was invalid.
15------------0 SS SP Flags CS IP Error Code Return CS Return IP <-- SS:SPStack frame for 32-bit programs:
31------------0 SS ESP EFlags CS EIP Error Code Return CS Return EIP <-- SS:ESPShaded fields should not be modified. Other fields can be modified before returning from the exception handler.
This function returns the CS:(E)IP of the current protected mode interrupt handler for the specified interrupt number.
AX = 0204h BL = Interrupt number
Carry flag is clear. CX:(E)DX = Selector:Offset of exception handler
AX = 0205h BL = Interrupt number CX:(E)DX = Selector:Offset of exception handler
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
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 SSYou 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 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 stackThe real mode procedure would be called with the following data on the real mode stack:
Param1 Param2 Param3 Return CS Return IP <-- Real mode SS:SPIf 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.
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.
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
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.
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
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.
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
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.
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.
AX = 0303h DS:(E)SI = Selector:Offset of procedure to call ES:(E)DI = Selector:Offset of real mode call structure
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.
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
Execute an IRET to return ES:(E)DI = Selector:Offset of real mode call structure to restore (see note)
;****************************************************** ; 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 int 31h jc ERROR ;****************************************************** ; ; 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
This function frees a real mode call-back address that was allocated through the allocate real mode call-back address service.
AX = 0304h CX:DX = Real mode call-back address to free
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
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.
AX = 0305h
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
ES:(E)DI = Pointer to state-save buffer AL = 0 to save state AL = 1 to restore state
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
This function returns addresses that can be jumped to for low-level mode switching.
AX = 0306h
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
AX = New DS CX = New ES DX = New SS (E)BX = New (E)SP SI = New CS (E)DI = New (E)IPThe 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.
AX = 0400h
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 is reserved and undefined 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)
These functions are provided to allocate linear address space.
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.
AX = 0500h ES:(E)DI = Selector:Offset of 30h byte buffer
If function was successful: Carry flag is clear. ES:(E)DI = Selector:Offset of buffer with the following structure: 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.
AX = 0501h BX:CX = Size of memory block to allocate in bytes
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
AX = 0502h SI:DI = Handle of memory block to free
If function was successful: Carry flag is clearIf function was unsuccessful: Carry flag is set
AX = 0503h BX:CX = New size of memory block to allocate in bytes SI:DI = Handle of memory block to resize
If function was successful: Carry flag is clear BX:CX = New linear address of memory block SI:DI = New handle of memory blockIf function was unsuccessful: Carry flag is set
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.
This function locks a specified linear address range.
AX = 0600h BX:CX = Starting linear address of memory to lock SI:DI = Size of region to lock in bytes
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
AX = 0601h BX:CX = Starting linear address of memory to unlock SI:DI = Size of region to unlock in bytes
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
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.
AX = 0602h BX:CX = Starting linear address of memory to mark as pageable SI:DI = Size of region to page in bytes
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
AX = 0603h BX:CX = Starting linear address of memory to relock SI:DI = Size of region to page in bytes
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
AX = 0604h
If function was successful: Carry flag is clear BX:CX = Page size in bytes If function was not successful: Carry flag is set
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.
Functions 0700h and 0701h are reserved and should not be called.
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.
AX = 0702h BX:CX = Starting linear address of pages to mark SI:DI = Number of bytes to mark as paging candidates
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
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.
AX = 0703h BX:CX = Starting linear address of pages to discard SI:DI = Number of bytes to discard
If function was successful: Carry flag is clear. If function was not successful: Carry flag is set.
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.
AX = 0800h BX:CX = Physical address of memory SI:DI = Size of region to map in bytes
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.
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
This function will disable the virtual interrupt flag and return the previous state of the virtual interrupt flag.
AX = 0900h
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
AX = 0901h
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
AX = 0902h
Carry flag clear (this function always succeeds) AL = 0 if virtual interrupts are disabled AL = 1 if virtual interrupts are enabled
Some DOS extenders provide extensions to the standard set of DPMI calls. This call is used to obtain an address which must be called to use the extensions. The caller points DS:(E)SI to a null terminated string that specifies the vendor name or some other unique identifier to obtain the specific extension entry point.
AX = 0A00h DS:(E)SI = Pointer to null terminated string
If function was successful: Carry flag is clear ES:(E)DI = Extended API entry point DS, FS, GS, EAX, EBX, ECX, EDX, ESI, and EBP may be modified If function was not successful: Carry flag is set
This function will set a debug watchpoint at a specified linear address.
AX = 0B00h BX:CX = Linear address of watchpoint DL = Size of watchpoint (1, 2, or 4) DH = Type of watchpoint 0 = Execute 1 = Write 2 = Read/Write
If function was successful: Carry flag is clear BX = Debug watchpoint handle If function was not successful: Carry flag is set
This function will clear a debug watchpoint that was set using the Set Debug Watchpoint function.
AX = 0B01h BX = Debug watchpoint handle
If function was successful: Carry flag is clear If function was not successful: Carry flag is set
AX = 0B02h BX = Debug Watchpoint Handle
If function was successful: Carry flag is clear AX = Status flags Bit 0 = 1 if watch point has been executed If function was not successful: Carry flag is set
AX = 0B03h BX = Debug Watchpoint Handle
If function was successful: Carry flag is clear If function was not successful: Carry flag is set
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.
Many programs that use DPMI will be bound to DOS extenders so that they will be able to run under any DOS environment. Existing DOS extenders support APIs that differ from the Int 31h interface. Usually, DOS extenders use an Int 21h multiplex for their extended APIs.
Extenders that support DPMI will need to initialize differently when they are run under DPMI environments. They will need to enter protected mode using the DPMI real to protected mode entry point, install their own API handlers, and then load the DOS extended application program.
DOS extenders should check for the presence of DPMI before attempting to allocate memory or enter protected mode using any other API. DOS extenders should check for APIs in the following order:
DOS Protected Mode Interface Virtual Control Program Interface eXtended Memory Specification Int 15h memory allocationWhen DPMI services are detected, extenders that provide interfaces that extend or are different from the basic DPMI interface will switch into protected mode and initialize any internal data structures. DPMI compatible extenders that provide no API extensions should simply execute the protected mode application in real mode.
DOS extenders typically use Int 21h to implement API extensions. Under DPMI, a DOS extender will need to install an API translation library by hooking Int 21h via then get and set protected mode interrupt vector functions (see page 56). The DOS extender library then gets to see every DOS call executed by the application program. If the API does not have any pointers then the interrupt can be reflected to the original interrupt handler. The default handler will pass the interrupt to real mode. Other APIs can be explicitly mapped by the DOS extender.
WARNING: The translation library code should be in locked memory to prevent page faults while DOS is in a critical section. This could happen, for instance, if a program called DOS reentrantly from an Int 24h (critical error).
Once the API translation library has been initialized, the DOS extender can load the application program using standard DOS calls. Memory should be allocated using the DPMI memory allocation services.
DPMI call 0A00h provides a standard mechanism for providing vendor specific extensions to the standard APIs. To support extensions under a DPMI environment, the translation library should hook the Int 31h chain (using the DOS get/set vector calls) and watch for call 0A00h. When this call is issued with the proper string parameter, the Int 31h hook code should modify ES:(E)DI, clear the carry flag on the stack, and iret without passing the call down the Int 31h chain. If the string passed in ES:(E)DI does not match the extensions supported by the library then the call should be passed down the Int 31h chain.