The DPMI real mode callback mechanism allows a DPMI protected mode client to be called as a subroutine by real mode programs in a transparent manner. That is, a real mode program can use a real mode callback to pass information to the DPMI client, or obtain services provided by the DPMI client, without necessarily being aware of protected mode or extended memory in any way. The callback mechanism can be thought of as the converse of DPMI Int 31H Functions 0300H, 0301H, and 0302H, which allow a DPMI client to pass information to a real mode program, or obtain services from a real mode program, in a manner that is similarly transparent to the real mode program.
In order to make a real mode callback available, the DPMI client must first call Int 31H Function 0303H with the selector and offset of the protected mode routine which will receive control when the callback is entered, and the selector and offset of a real mode register data structure (in the same format as used for Int 31H Functions 0300H, 0301H, and 0302H). Function 0303H will return a real mode address (segment and offset) for the callback entry point that can be passed to a real mode program via a software interrupt or far call (Int 31H Functions 0300H, 0301H, or 0302H), a DOS memory block, or any other convenient mechanism.
When the real mode program executes a FAR CALL to the real mode callback address supplied to it by the DPMI client, the DPMI host saves the contents of all real mode registers into the DPMI client's real mode register data structure, switches the CPU into protected mode, and enters the DPMI client's callback routine with the following conditions:
The callback procedure can then extract its parameters from the real mode register data structure and/or copy parameters from the real mode stack to the protected mode stack. Recall that the segment register fields of the real mode register data structure contain segment or paragraph addresses that are not valid in protected mode. Far pointers passed in the real mode register data structure must be translated to virtual addresses before they can be used. The recommended procedure is for the DPMI client to allocate a selector for this purpose during its initialization, then use Int 31H Function 0007H within the callback procedure to set the segment base to 16 times the value found in the real mode segment register. The DPMI client should not use Int 31H Function 0002 for this purpose, because selectors allocated by Function 0002 can never be freed.
Offset Length Contents 00H 4 DI or EDI 04H 4 SI or ESI 08H 4 BP or EBP 0CH 4 reserved, should be zero 10H 4 BX or EBX 14H 4 DX or EDX 18H 4 CX or ECX 1CH 4 AX or EAX 20H 2 CPU status flags 22H 2 ES 24H 2 DS 26H 2 FS 28H 2 GS 2AH 2 IP 2CH 2 CS 2EH 2 SP 30H 2 SS
The callback procedure exits by executing an IRET with the address of the real mode register data structure in ES:(E)DI, passing information back to the real mode caller by modifying the contents of the real mode register data structure and/or manipulating the contents of the real mode stack. The callback procedure is responsible for setting the proper address for resumption of real mode execution into the real mode register data structure; typically, this is accomplished by extracting the return address from the real mode stack and placing it into the CS:IP fields of the real mode register data structure. After the IRET, the DPMI host switches the CPU back into real mode, loads all registers (including CS:IP) with the contents of the real mode register data structure, and finally returns control to the real mode program.
Since the real mode call structure and the selector used for the real mode SS are static, care must be taken when writing DPMI client callback procedures that may be reentered (for example, by a real mode program that services hardware interrupt). The simplest method of avoiding reentrancy is to leave interrupts disabled throughout the entire callback procedure. However, if the amount of code executed by the callback is large, the client may find it more desirable to copy the real mode register data structure into a dynamically allocated buffer and then re-enable interrupts and not use the incoming DS any more. The real mode register data structure pointed to by ES:(E)DI upon return from the callback procedure is not required to be at the same address as the original real mode register data structure.
DPMI hosts must provide a minimum of 16 callback addresses per virtual machine. Real mode callbacks are a limited system resource. A DPMI client should always use Int 31H Function 0304H to free any callbacks that it is no longer using.
Example: The following code is an example 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 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 assume 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 21h 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 ; St 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