DPMI 0.9

This documentation contains information about protected mode, the DPMI interface and the DPMI unit itself. This should be a brief introduction into the basics of protected mode programming in general and with FPC using this unit.

The main points which are discussed within this document are :
 

Protected mode

DPMI interface

DPMI unit

DPMI unit function descriptions

This document was compiled by Thomas Schatzl on 1 January 1999. For questions, suggestions, improvements or other things, mail me at tom_at_work@geocities.com

Protected mode

Protected mode was designed to fit the needs of modern multitasking operating systems. Multitasking means that one or more actions (tasks) are seemingly done at the same time. A good example for this is printing a document in the background while doing something else.
These demands for CPUs are roughly: From the above four requirements the first one is the most striking (for GO32V2 programmers), because that means that memory access like in real mode would exactly against the rule. In real mode the programmer could access any memory cell below the 1 MB boundary by simply loading a value between $0000 and $FFFF into any segment register to overwrite important parts of the operating system like the interrupt vector table, parts of COMMAND.COM, and other things which surely cause the CPU to reset.

In protected mode you may get in big troubles, even when loading a random value of a segment address into a segment register, because this causes the processor to stop executing the program. This doesn't reset or crash the processor, but he simply calls an exception (an interrupt) which returns control to the underlying operating system again, which in term simply terminates the program. It's that easy in protected mode.

This is only possible because segment addresses aren't segment addresses anymore, but (segment-)selectors. Selectors are indexes into a table of segment descriptors which describe a single memory area. The CPU gets the base address of the memory segment. Finally he adds the offset address from the pointer to this value to get the real memory address of e.g. a variable. (No more multiply of the segment by 16 and then adding the offset like in real mode, hence memory segments can start at any address)

A descriptor holds more than simply the base address of the segment, but several additional information. These are the base address, the length of the segment and finally some flags. (Please refer to a good book / documentation for further reference, because that's all that a protected mode programmer needs to know as long as he doesn't want to make his own operating system....)

This is where a DPMI extender comes into play.

The DPMI Interface

The DOS Protected Mode Interface (DPMI) was primarily defined to allow DOS programs access to the extended memory area (memory > 1MB) while maintaining system protection. DPMI defines a subset of DOS and BIOS calls which can be made by protected mode DOS programs. It also provides a new interface via Int 31h that protected mode programs use to allocate memory, modify descriptors, call real mode software, etc.
Such a program is commonly called as a DPMI host.

The DPMI unit

This unit provides an interface to the various functions a DPMI host provides for its application with the FPC compiler. Additionally I tried to add several functions to simplify handling with selectors and some other useful procedures and features which are missing in the GO32 unit.

Several issues of the DPMI API are handled by this unit:

In addition to these, DPMI provides several other functions Look at Table 1 for an index to all the different available types, functions, constants and procedures of this unit. Although MEMORY and FARMEM are automatically included within this unit, their documentation isn't included within this document.
For people who want to port their code from the GO32 unit to the DPMI unit, look at Appendix A:Porting from GO32

Descriptions

See Table 1 for a complete list of all types, constants, variables, functions and procedures.

DPMI types, constants and variables

Types

type
    Descriptor = array[0..7] of Byte;
 
This array holds the contents of a descriptor. Since descriptor handling is normally not done by the common programmer, I didn't go into more detail. For further reference what this array exactly contains, please consult a good protected mode documentation.
See also LDT management services

type
    Registers = record
              case integer of
                   0 : (edi, esi, ebp, res1, ebx, edx, ecx, eax : DWord;
                        Flags, es, ds, fs, gs, ip, cs, sp, ss : Word);
                   1 : (di, di2, si, si2, bp, bp2, res2, res3,
                        bx, bx2, dx, dx2, cx, cx2, ax, ax2 : Word);
                   2 : (res4 : array[1..4] of DWord;
                        bl, bh, bl2, bh2, dl, dh, dl2, dh2,
                        cl, ch, cl2, ch2, al, ah, al2, ah2 : Byte);
    end;

This data structure contains the values which must be passed to or are returned by an interrupt call or a real mode callback structure. See at Translation services for further reference about the usage of this structure
 

Constants

const
    fCarry     = $0001;
    fParity    = $0004;
    fAuxiliary = $0010;
    fZero      = $0040;
    fSign      = $0080;
    fTrap      = $0100;
    fInterrupt = $0200;
    fDirection = $0400;
    fOverflow  = $0800;

These constants define the bit location of the various flags within the flags-entry of the 'register' data structure. See registers type.

type
    pm_Addr = record
            offset : DWord;
            selector : Word;
    end;

This is the full definition of a single memory address in protected mode. It is 48 bit in size, since a single memory segment is still 16 bit and it can be up to 32 bits in size. See Interrupt services or Translation services for applications.

type
     rm_Addr = record
             offset : Word;
             segment : Word;
     end;

Definition of a real mode address in segment:offset notation. See Translation or Interrupt services for applications
 
type
     MemInfoBuf = record
                largest_available_free_block : Longint;
                max_unlocked_pages : Longint;
                max_lockable_pages : Longint;
                linear_address_space_size : Longint;
                number_of_unlocked_pages : Longint;
                number_of_free_pages : Longint;
                number_of_physical_pages : Longint;
                free_linear_address_space : Longint;
                size_of_paging_file : Longint;
                reserved : array[0..2] of Longint;
     end;
 
The information block about memory used/allocated of the program which is returned by dpmi_get_free_memory_information() only.
 
Field identifier Description
largest_available_free_block Largest available free block in bytes
max_unlocked_pages Maximum unlocked page allocation
max_lockable_pages Maximum locked page allocation
linear_address_space_size Linear address space size in pages
number_of_unlocked_pages Total number of unlocked pages
number_of_free_pages Number of free pages
number_of_physical_pages Total number of physical pages
free_linear_address_space Free linear address space in pages
size_of_paging_file Size of paging file/partition in pages
reserved Reserved
Only the first entry is guaranteed to contain a valid value, the others contain -1 ($FFFFFFFF) if invalid. To get the page size in bytes, look at dpmi_get_page_size() function.

var
    dpmi_error : DWord;

Contains the last error number occured by a DPMI call. See Error handling too

DPMI functions and procedures

Error handling

Since any of the DPMI calls can fail, I decided to let the programmer install its own error handler which is called automatically at a DPMI call failure. This is done via a special error routine ('handler') which is called everytime an error occurs. This handler can be redefined by the programmer to suit its own purposes. The error handler is a simple procedure which gets the last error code as a dword as an argument.

Such an error code consists of three parts: First comes the warning/fatal bits, then a unit identifier to get to know which unit caused the error and last the error code itself.

The fatal/warning bits are the upper 2 bits of the parameter dword. They may be ignored by the error handler (because the caller decides the type of the error), but it's generally a good idea to halt on a fatal error.
Then comes a 10 bit sized unit identifier, which tells the error handler at which unit the error happened.
And last a 20 bit sized error code, which can be output for reference.

Additionally *every* dpmi_xxxx function returns a boolean result which indicates success or fail, in the case you don't want to care about the dpmi_error variable every time (and added a dummy error handler which does completely nothing).

Btw, error handling will be changed soon (too unflexible now), to something like done in the API unit, so the error codes in the function descriptions aren't entered into the proper row. But there is still Table 2, where the error codes for all functions which return errors are listed with their associated error code.
 
procedure dpmi_set_error_handler(handler : error_proc);
Description: Sets a new error handler for the DPMI unit
Parameters: handler - the address of the new error handler procedure
Return values: none
Error code: none
Notes: The standard error handler updates the dpmi_error variable, writes a message to stdout, and triggers an RTE 216 (General Protection Fault) if the error was a fatal one.
See also: dpmi_error (var), Error handling, Table 2

Initialization services

These function deal with detecting the current mode the cpu runs under
 
function dpmi_get_cpu_mode(var in_pm : Boolean) : Boolean;
Description: Returns information about the current CPU mode
Parameters: none
Return values: in_pm - true if running under protected mode, false if not
Error code: none
Notes: GO32V2 applications generally don't need to care about this.
See also: Initialization services, Table 1
 

LDT management services

The LDT (local descriptor table) management services provide several interfaces to allocate, free, resize, lock and unlock protected mode descriptors for the current task.
 
function dpmi_allocate_ldt_descriptors(number : Word; var basesel : Word) : Boolean;
Description: Allocates one or more descriptors from the local task's LDT. The descriptors allocated must be initializd by the application.
Parameters: number - number of selectors to allocate
Return values: basesel - the base selector to the array of selectors  allocated
Error code: $0000
Notes: If more than one descriptor was requested, basesel contains the first of a contiguous array of descriptors. You should add the value returned by dpmi_get_next_selector_increment_value() to get to the next selector in the array.
See also: LDT management services, dpmi_free_ldt_descriptor(), dpmi_set_segment_base_address(), dpmi_get_segment_base_address(), dpmi_set_segment_limit(), dpmi_get_segment_limit(), dpmi_segment_to_descriptor(), dpmi_get_next_selector_increment_values(), create_selector(), free_selector(), map_physical_memory(), Table 1, Table 2
 
function dpmi_free_ldt_descriptor(sel : Word) : Boolean;
Description: This function is used to free previously allocated selectors by dpmi_allocate_ldt_descriptors()
Parameters: sel - the selector to de-allocate
Return values: none
Error code: $0001
Notes: Arrays of selectors must be freed individually by calling this function for each single selector. You may use the free_selector() function instead of this too.
See also: LDT management services, dpmi_allocate_ldt_descriptors(), free_selector(), Table 1, Table 2
 
function dpmi_segment_to_descriptor(seg : Word; var sel : Word) : Boolean;
Description: Converts a real mode segment into a descriptor which can be accessed  by protected mode programs.
Parameters: seg - real mode segment address
Return values: sel - selector mapped to real mode address
Error code: $0002
Notes: The selectors limit is always set to $FFFF (64k). Multiple calls to this function with the same segment address will return the same selector. For this reason, selectors created by this selector should never be freed or modified. Use this function sparingly. If you want to examine different real mode segments it is better to allocate a single selector by dpmi_allocate_ldt_descriptors() and change its base address via dpmi_set_segment_base_address() accordingly.
See also: LDT management services, dpmi_allocate_ldt_descriptors(), dpmi_set_segment_limit(), dpmi_set_segment_base_address(), Table 1, Table 2
 
function dpmi_get_next_selector_increment_value(var incval : Word) : Boolean;
Description: Some functions (like allocate_ldt_descriptors()) can return more than one descriptor. You must call this function to determine the value which must be added to a selector to gain access to the next descriptor in the array.
Parameters: none
Return values: incval - value to add to get to next selector
Error code: $0003
Notes: The returned value will be a power of two, but don't make assumptions about the value this function will return.
See also: LDT management services, dpmi_allocate_ldt_descriptors(), Table 1, Table 2
 
function dpmi_get_segment_base_address(sel : Word; var baseaddr : DWord) : Boolean;
Description: This function returns the 32-bit linear base address of the specified segment.
Parameters: sel - selector
Return values: baseaddr - 32 bit linear base address of segment
Error code: $0006
Notes: This function fails if sel is invalid.
See also: LDT management services, dpmi_allocate_ldt_descriptors(), dpmi_set_segment_base_address(), dpmi_set_segment_limit(), dpmi_get_segment_limit(), get_linear_address(), Table 1, Table 2
 
function dpmi_set_segment_base_address(sel : Word; baseaddr : DWord) : Boolean;
Description: This function changes the 32 bit linear address of the specified segment
Parameters: sel - segment to change base address 
baseaddr - new 32 bit base address
Return values: none
Error code: $0007
Notes: This function fails if sel is invalid. You should only modify descriptors that were allocated via dpmi_allocate_ldt_descriptors() before.
See also: LDT management services, dpmi_allocate_ldt_descriptors(), dpmi_get_segment_base_address(), dpmi_set_segment_limit(), dpmi_get_segment_limit(), create_selector(), change_selector(), Table 1, Table 2
 
function dpmi_get_segment_limit(sel : Word; var limit : DWord) : Boolean;
Description: Returns the 32 bit limit of the specified segment
Parameters: sel - selector
Return values: limit - limit of the specified segment in bytes - 1
Error code: See table about Error codes
Notes: This function will fail if the supplied selector is invalid.
See also: LDT management services, dpmi_set_segment_limit(), dpmi_get_segment_base_address(), dpmi_set_segment_base_address(), Table 1, Table 2
 
function dpmi_set_segment_limit(sel : Word; limit : DWord) : Boolean;
Description: This function sets the limit for the specified segment
Parameters: sel - selector 
limit - new 32 bit limit of segment in bytes - 1
Return values: none
Error code: $0008
Notes: This function will fail when the specified selector is invalid or the requested limit couldn't be set. Segment limits greater than 1 MB must be page aligned, that means, the lower 12 bits of the limit field must have the lower 12 bits set. Your program should only modify descriptors that were previously allocated via the dpmi_allocate_ldt_descriptors() function.
See also: LDT management services, dpmi_allocate_ldt_descriptors(), dpmi_get_segment_limit(), dpmi_set_segment_base_address(), dpmi_get_segment_base_address(), dpmi_change_selector(), Table 1, Table 2
 
function dpmi_get_descriptor_access_rights(sel : Word; var rights : Word) : Boolean;
Description: Returns the access rights and type field of the specified descriptor.
Parameters: sel - selector
Return values: rights - access rights / type field of the descriptor
Error code: See notes in Error codes table.
Notes: This function fails if the specified selector is invalid. See a DPMI specification for the exact contents of the rights variable.
See also: LDT management services, dpmi_set_descriptor_access_rights(), dpmi_get_descriptor(), dpmi_set_descriptor(), dpmi_allocate_specific_descriptor(), Table 1, Table 2
 
function dpmi_set_descriptor_access_rights(sel : Word; rights : Word) : Boolean;
Description: This function allows protected mode programs to modify the access rights field of a descriptor
Parameters: sel - selector 
rights - new access rights / type
Return values: none
Error code: $0009
Notes: This function will fail if the selector specified is invalid. Your program should only change the access rights / type of descriptors allocated via the dpmi_allocate_ldt_descriptors() function.
See also: LDT management services, dpmi_get_descriptor_access_rights, dpmi_get_descriptor(), dpmi_set_descriptor(), dpmi_allocate_specific_descriptor(), Table 1, Table 2
 
function dpmi_create_code_segment_alias_descriptor(codesel : Word; var sel : Word) : Boolean;
Description: This function will create a data descriptor that has the same base and limit as the specified code segment descriptor.
Parameters: codesel - code segment selector
Return values: sel - new data segment selector
Error code: $000A
Notes: This function fails if the specified selector is invalid or not a code segment selector. You have to use the dpmi_free_ldt_descriptor() function to free such a descriptor afterwards. The new data segment descriptor will not track changes from the code segment descriptor. With FPC you don't need this function, because you can write to the code segment too (because it is automatically set as read/writeable at startup)
See also: LDT management services, dpmi_get_descriptor_access_rights(), dpmi_set_descriptor_access_rights(), Table 1, Table 2
 
function dpmi_get_descriptor(sel : Word; var descr : Descriptor) : Boolean;
Description: This function copies the descriptor table entry for a specified descriptor into a buffer
Parameters: sel - selector
Return values: descr - 8 byte buffer holding the descriptor values
Error code: $000B
Notes: This function will fail if the selector specified is invalid or unallocated.
See also: LDT management services, dpmi_set_descriptor(), dpmi_allocate_specific_descriptor(), dpmi_set_descriptor_access_rights(), dpmi_get_descriptor_access_rights(), Table 1, Table 2
 
function dpmi_set_descriptor(sel : Word; descr : Descriptor) : Boolean;
Description: This function copies an 8 byte buffer into the LDT entry for a specified descriptor
Parameters: sel - selector 
desc - 8 byte buffer containing descriptor
Return values: none
Error code: $000C
Notes: This function will fail if the selector specified is invalid. You should only modify descriptors allocated by the dpmi_allocate_ldt_descriptors() function. For a complete description of the contents of the 8 byte buffer, please refer to a DPMI specification.
See also: LDT management services, dpmi_get_descriptor(), dpmi_allocate_specific_descriptor(), Table 1, Table 2
 
function dpmi_allocate_specific_descriptor(sel : Word) : Boolean;
Description: This function attempts to allocate a specific LDT descriptor.
Parameters: sel - selector
Return values: none
Error code: $000D
Notes: This function will fail if the specified selector is in use or not an LDT selector. You  need to use the dpmi_free_ldt_descriptor() function to free this descriptor again.
See also: LDT management services, dpmi_get_descriptor(), dpmi_set_descriptor(), Table 1, Table 2
 

Memory management services

These functions are provided to allocate memory in linear address space. This should normally be of no concern to the standard FPC programmer, because FPC automatically 'grows' its heap.
 
function dpmi_get_free_memory_information(var mem : meminfobuf) : Boolean;
Description: 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.
Parameters: mem - 30 byte buffer
Return values: mem - buffer filled out with memory information
Error code: $0002
Notes: To determine the size of pages, call the dpmi_get_page_size() function.
See also: Memory management services, dpmi_get_page_size(), Table 1, Table 2
 
function dpmi_allocate_memory_block(size : DWord; var linearaddr : DWord; var blockhandle : DWord) : Boolean;
Description: This function allocates and commits linear memory.
Parameters: size - size of requested memory block in bytes
Return values: linearaddr - linear address of allocated memory block 
blockhandle - memory block handle (used to resize and free memory)
Error code: $0002
Notes: This function does not allocate any selectors for the memory block. It is up to the application to allocate and initialize selectors needed for access. The block is allocated unlocked. Allocations will be page granular,  this means that an allocation of $1001 bytes will result in allocation of $2000 bytes. Therefore it's best to always allocate memory in multiplies of 4K.
See also: Memory management services, dpmi_free_memory_block(), dpmi_resize_memory_block(), Table 1, Table 2
 
function dpmi_free_memory_block(blockhandle : DWord) : Boolean;
Description: This function frees a memory block that was allocated trough the dpmi_allocate_memory_block() function.
Parameters: blockhandle - handle of memory block to free
Return values: none
Error code: $0002
Notes: Your programs must also free selectors that it allocated to access the memory.
See also: Memory management services, dpmi_allocate_memory_block(), Table 1, Table 2
 
function dpmi_resize_memory_block(newsize : DWord; var blockhandle : DWord; var linearaddr : DWord) : Boolean;
Description: This function changes the size of a memory block that was allocated through the dpmi_allocate_memory_block() function.
Parameters: newsize - new size of memory block in bytes 
blockhandle - handle of memory block to resize
Return values: linearaddr - new linear address of memory block 
blockhandle - new blockhandle of memory block
Error code: $0002
Notes: This function may change the linear address of the memory block and its handle. Therefore, you'll need to update any selectors that point to the block after resizing it. You must use the new handle instead of the old one.
See also: Memory management services, dpmi_allocate_memory_block(), dpmi_free_memory_block(), Table 1, Table 2
 

Physical address mapping

Memory mapped devices such as network or display adapters sometimes have memory mapped at physical addresses beyond the normal 1 Mb of real-mode addressable memory space. This service can be used to convert physical addresses of such mapped memory into linear addresses. This 32 bit linear address then can be used to access this memory.
 
function dpmi_physical_address_mapping(physaddr : DWord; size : DWord; var linearaddr : DWord) : Boolean;
Description: Converts physical memory addresses of devices into 32 bit linear addresses
Parameters: physaddr - physical address of memory range 
size - size of memory region in bytes - 1
Return values: linearaddr - linear address of device memory
Error code:
Notes: The returned linear address can be used to access the devices' memory. The application must create a valid descriptor to this memory before accessing it. Do not use this service to access memory that is mapped into the first megabyte of address space. (The physical address equals the linear address in this memory area only)
See also: Physical address mapping, map_physical_memory(), Table 1, Table 2

DOS memory management

Some programs require the ability to allocate memory in the real mode addressable < 1 mb region. These following 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 translation services to call real mode software that is not directly supported by DPMI.

For easier access to this region the FPC team decided to automatically load the %fs segment register with a selector which maps the entire 1 Mb memory region. So you must make sure that this segment register never changes in your code, because some functions rely on this. Additionally they automatically provide a pre-allocated transfer buffer to be available for temporary use (e.g. as long as you don't call certain FPC functions).

Some tips about address calculation follow:

To obtain the linear address (offset) from the DOS memory selector, you need to calculate the address like it was in real mode. This linear address then can be used to copy it to protected mode heap.

(linear_address := segment * 16 + offset)

In reverse, to generate a segment:offset address out of a linear address do the following:

segment := linear_address div 16;
offset := linear_address and 15;

This can be useful when you need to pass the segment:offset address of e.g. the transfer buffer to a real mode interrupt.
 
function dpmi_allocate_DOS_memory_block(size : DWord; var realaddr : rm_Addr; var sel : Word) : Boolean;or
function dpmi_allocate_DOS_memory_block (size : DWord; var realseg : Word; var sel : Word) : Boolean;
Description: This function will allocate a block of memory from the DOS memory pool. It returns both the real mode memory segment and a descriptor which can be used by protected mode applications.
Parameters: size - size of requested DOS memory block in bytes
Return values: realaddr - DOS real mode segment:offset address of the allocated memory block
realseg - DOS real mode segment of the allocated memory block 
sel - protected mode selector for allocated block
Error code: $0002
Notes: Your program should never modify or deallocate any descriptors allocated by this function. The dpmi_free_DOS_memory_block() function will automatically deallocate the descriptors
See also: DOS memory managment, dpmi_free_DOS_memory_block(), dpmi_resize_DOS_memory_block(), Table 1, Table 2
 
function dpmi_free_DOS_memory_block(sel : Word) : Boolean;
Description: This function frees memory that was previously  allocated by the dpmi_allocate_DOS_memory_block() function. 
Parameters: sel - selector
Return values: none
Error code: $0002
Notes: The descriptor allocated for the memory block is automatically freed and therefore should not be accessed once the freed by this function.
See also: DOS memory managment, dpmi_allocate_DOS_memory_block(), Table 1, Table 2
 
function dpmi_resize_DOS_memory_block(newsize : DWord; sel : Word) : Boolean;
Description: This function is used to grow or shrink a memory block that was previously allocated by the dpmi_allocated_DOS_memory_block() function.
Parameters: newsize - new block size in bytes 
sel - selector of block to modify
Return values: none
Error code: $0002
Notes: Growing a DOS memory block is often likely to fail because other DOS memory allocations will prevent increasing the size of the block. Therefore, this function is usually only used to shrink a block
See also: DOS memory managment, dpmi_allocate_DOS_memory_block(), dpmi_free_DOS_memory_block(), Table 1, Table 2

Interrupt services

These services allow protected mode programs to hook to interrupts and intercept processor exceptions.

All interrupts from hardware (like keyboard, or timer) will always be reflected to the protected mode handler first. If the protected mode handler jumps 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 via iret or they can chain to the next interrupt handler in an interrupt chain. The final handler for all protected mode handlers will reflect the interrupt to real mode (e.g. the real mode interrupt gets executed).
When an interrupt is reflected to real mode %eax, %ebx, %ecx, %edx, %esi, %edi, %ebp registers and flags will all be passed unaltered from protected to real mode. The segment registers contain undefined values. The DPMI host automatically provides a real mode stack for interrupts that are reflected to real mode.
 

Hardware Interrupts

The interrupt controllers are mapped to the system's default interrupts (e.g. the master interrupt controller has a base interrupt of $8 and the slave controller has a base of $70).
Hardware interrupt procedures and all of their data must reside in locked memory. All that memory touched by hardware interrupt hooks must be locked. The handler will always be called on a locked stack.
As in real mode, hardware interrupts are called with interrupts disabled. Since iret doesn't restore the interrupt flag, the handler must enable interrupts via a sti instruction, else interrupts will remain disabled.
 

Software interrupts

Most software interrupts executed in real mode will not be reflected to protected mode interrupt hooks. However, there are some (*Ausnahmen*): Int $1C (BIOS timer interrupt), Int $23 (DOS Ctrl+C interrupt), Int $24 (DOS critical error interrupt).
 
function dpmi_get_rm_interrupt(number : Byte; var addr : rm_Addr) : Boolean;
Description: This function returns the value of the current task's real mode interrupt vector for the specified interrupt
Parameters: number - interrupt number
Return values: rm_Addr - address of real mode interrupt vector
Error code:
Notes: The returned address is a real mode segment:offset address, not a protected mode one.
See also: Interrupt services, dpmi_set_rm_interrupt(), dpmi_get_pm_interrupt(), dpmi_set_pm_interrupt(), dpmi_get_exception_handler(), dpmi_set_exception_handler(), dpmi_simulate_rm_interrupt(), dpmi_allocate_rm_callback(), intr(), realintr(), rm_Addr (Type), Table 1, Table 2
 
function dpmi_set_rm_interrupt(number : Byte; addr : rm_Addr) : Boolean;
Description: This function sets the value of the current task's real mode vector for the specified interrupt.
Parameters: number - interrupt number to set 
addr - address of real mode interrupt
Return values: none
Error code: $0002
Notes: The address passed to this function must be a real mode segment:offset address, not a protected mode selector:offset address. This means that the code for the interrupt handler must either be in DOS addressable memory or you must use a real mode callback address. Refer to dpmi_allocate_DOS_memory() on allocating memory below 1 Mb. Information on real mode callbacks can be found at the Translation services section.
See also: Interrupt services, dpmi_get_rm_interrupt(), dpmi_get_pm_interrupt(), dpmi_set_pm_interrupt(), dpmi_get_exception_handler(), dpmi_set_exception_handler(), dpmi_simulate_rm_interrupt(), dpmi_allocate_rm_callback(), intr(), realintr(), rm_Addr (Type), Table 1, Table 2
 
function dpmi_get_exception_handler(number : Byte; var addr : pm_Addr) : Boolean;
Description: This function returns the address of the current protected mode exception handler for the specified exception number.
Parameters: number - exception number
Return values: addr - selector:offset of the exception
Error code: $0002
Notes: This function fails if the number passed was invalid.
See also: Interrupt services, dpmi_set_exception_handler(), dpmi_get_rm_interrupt(), dpmi_set_rm_interrupt(), dpmi_get_pm_interrupt(), dpmi_set_pm_interrupt(), dpmi_allocate_rm_callback(), dpmi_simulate_rm_interrupt(), intr(), realintr(), pm_Addr (Type), Table 1, Table 2
 
function dpmi_set_exception_handler(number : Byte; addr : pm_Addr) : Boolean;
Description: This function allows protected mode programs to intercept processor exceptions that are not handled by the DPMI environment. Programs may wish to handle exceptions such as protection faults which would otherwise generate a fatal error.
Parameters: number - exception/fault number ($00-$1F) 
pm_addr - selector:offset address of exception handler
Return values: none
Error code: $0002
Notes: Every exception is first examined by the protected mode operating system. If it cannot handle it, 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 or terminate the program. This function fails if the exception/fault number passed is invalid. For further reference on how to deal with exceptions (and setting up a handler), please consult a DPMI reference.
See also: Interrupt services, dpmi_get_exception_handler(), dpmi_get_rm_interrupt(), dpmi_set_rm_interrupt(), dpmi_get_pm_interrupt(), dpmi_set_pm_interrupt(), dpmi_allocate_rm_callback(), dpmi_simulate_rm_interrupt(), intr(), realintr(), pm_Addr (Type), Table 1, Table 2
 
function dpmi_get_pm_interrupt(number : Byte; var addr : pm_Addr) : Boolean;
Description: This function returns the current selector:offset address for the specified protected mode interrupt handler.
Parameters: number - interrupt number
Return values: addr - protected mode selector:offset vector of interrupt 
Error code: $0002
Notes: The address returned is a protected mode selector:offset address, not a real mode address. All 256 interrupts are supported by the DPMI host.
See also: Interrupt services, dpmi_set_pm_interrupt(), dpmi_set_exception_handler(), dpmi_get_exception_handler(), dpmi_get_rm_interrupt(), dpmi_set_rm_interrupt(), dpmi_allocate_rm_callback(), dpmi_simulate_rm_interrupt(), intr(), realintr(), pm_Addr (Type), Table 1, Table 2
 
function dpmi_set_pm_interrupt(number : Byte; addr : pm_Addr) : Boolean;
Description: Sets the specified protected mode interrupt vector of the current task.
Parameters: number - interrupt number to set 
addr - new protected mode address of interrupt.
Return values: none
Error code: $0002
Notes: The address passed to this function must be a valid selector:offset protected mode address. 
See also: Interrupt services, dpmi_get_pm_interrupt(), dpmi_set_rm_interrupt(), dpmi_get_rm_interrupt(), dpmi_set_exception_handler(), dpmi_get_exception_handler(), dpmi_allocate_rm_callback(), dpmi_simulate_rm_interrupt(), intr(), realintr(), pm_Addr (Type), Table 1, Table 2

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 mode operating system that does 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 exectues a 'cli' instruction, the programs virtual interrupt state will be disabled, and the program will not receive any hardware interrupts until it executes a 'sti' to reenable interrupts.

 
function dpmi_get_and_disable_virtual_interrupts(var prevstate : Boolean) : Boolean;
Description: This function will disable the virtual interrupt state and return the previous state of the virtual interrupt flag.
Parameters: none
Return values: prevstate - previous virtual interrupt state
Error code:
Notes: Virtual interrupts are disabled after this call. 
See also: Virtual interrupt state functions, dpmi_get_and_enable_virtual_interrupts(), dpmi_get_virtual_interrupt_state() Table 1, Table 2
 
function dpmi_get_and_enable_virtual_interrupts(var prevstate : Boolean) : Boolean;
Description: This function will enable virtual interrupts and return the previous state of the virtual interrupt flag
Parameters: none
Return values: prevstate - previous virtual interrupt state
Error code:
Notes: Virtual interrupts are enabled after this call
See also: Virtual interrupt state functions, dpmi_get_and_disable_virtual_interrupts(), dpmi_get_virtual_interrupt_state() Table 1, Table 2
 
function dpmi_get_virtual_interrupt_state(var state : Boolean) : Boolean;
Description: Returns the current state of the virtual interrupt flag
Parameters: none
Return values: state - current virtual interrupt flag state
Error code:
Notes: none
See also: Virtual interrupt state functions, dpmi_get_and_enable_virtual_interrupts(), dpmi_get_and_disable_virtual_interrupts(), Table 1, Table 2

Translation services

These services are provided so that protected mode programs can call real mode software that DPMI does not support directly. The registers structure is passed by the DPMI host to these programs.

All functions automatically provide a locked real-mode stack of about 30 words.

If your program needs to perform a series of calls to a real mode API it's 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.

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 to 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.
 
function dpmi_simulate_rm_interrupt(number : Byte; var regs : Registers) : Boolean;
Description: This function simulates an interrupt in real-mode.
Parameters: number - interrupt to call 
regs - register values supplied to interrupt
Return values: regs - register values changed by interrupt
Error code: $0002
Notes:
See also: Translation services, intr(), realintr(), registers (Type), dpmi_call_rm_procedure_with_iret_frame(), Table 1, Table 2
 
function dpmi_call_rm_procedure_with_retf_frame(var regs : Registers) : Boolean;
Description: This function calls a real mode procedure. The called procedure must end with a far return when it completes.
Parameters: regs - register values supplied to real-mode procedure 
Return values: regs - register values modified by real mode procedure
Error code: $0002
Notes: The cs:ip in the register values structure specifies the address of procedure to call. Note that all given segment registers in this structure need to be real mode segments.
See also: Translation services, Table 1, Table 2
 
function dpmi_call_rm_procedure_with_iret_frame(var regs : Registers) : Boolean;
Description: This function calls a real-mode procedure which must return with an iret instruction
Parameters: regs - registers supplied to real-mode procedure
Return values: regs - register values modified by real-mode procedure
Error code:
Notes: The cs:ip fields in the register values structure specifies the address of the procedure to call.
See also: Translation services, Table 1, Table 2
 
function dpmi_allocate_rm_callback(procaddr : pm_Addr; regs : pm_Addr; var realaddr : rm_Addr) : Boolean;
Description: This function is used to obtain a unique real-mode segment-offset address that will transfer control from real-mode to a protected mode procedure.
Parameters: procaddr - selector:offset of procedure to call 
regs - selector:offset of real mode call structure
Return values: realaddr - segment:offset of real mode call address
Error code:
Notes: Call-back procedure parameters at entry: 
Interrupts disabled 
%DS:%ESI = selector:offset of real mode SS:SP 
%ES:%EDI = selector:offset of real mode call structure 
%SS:%ESP =Locked protected mode API stack 

Return from callback procedure: 
Execute an iret to return 
%ES:%EDI = selector:offset of real mode call structure 

The called procedur is responsible for modifying the real-mode CS:IP before returning. If the real mode CS:IP is left unchanged the real-mode callback will be executed immediately and your protected mode procedure will be called again. Normally you'll pop a return address off the real-mode stack and place it in the real mode CS:IP. 
To return values to the real-mode caller you must modify the real-mode call structure. Remember that all segment values in the real mode call structure will be real-mode segments, not selectors.

See also: Translation services, Table 1, Table 2
 
function dpmi_free_rm_callback(procaddr : rm_Addr) : Boolean;
Description: This function frees a real mode call-back address that was previously allocated through the dpmi_allocate_rm_callback() function.
Parameters: rm_Addr - real-mode segment:offset callback address to free
Return values: none
Error code: $0002
Notes: Real mode callbacks are a limited resource. They should be freed when no longer used.
See also: Translation services, Table 1, Table 2

Page locking services

Many DPMI implementations supply virtual memory (as CWSDPMI and Win9x does). In these environments it is 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 while DOS is busy.
Under all DPMI implementations, applications should lock interrupt code and data. The lock calls will always return success under implementations that ignore these calls.

Although memory ranges are in specified in bytes, the actual unit of memory that is 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 if a region is locked three times then it must be unlocked three times before the pages will be unlocked.
 
function dpmi_lock_linear_region(linaddr : DWord; size : DWord) : Boolean;
Description: This function locks a specified memory range
Parameters: linaddr - starting linear address of memory range to lock 
size - size of region to lock in bytes
Return values: none
Error code:
Notes: If this function fails, no memory was locked. If the specified region overlaps part of a page at the beginning or end, the whole page(s) will be locked.
See also: Page locking services, Table 1, Table 2
 
function dpmi_unlock_linear_region(linaddr : DWord; size : DWord) : Boolean;
Description: Unlocks a specified linear address memory range that was previously locked by dpmi_lock_linear_region().
Parameters: linaddr - starting linear address of memory range 
size - size of region to be locked in bytes
Return values: none
Error code: $0002
Notes: If the function fails, none of the memory will be unlocked. An error will be returned if the memory was not previously locked or if the specified region is invalid. If the specified region overlaps part of a page at the beginning or end, the page(s) will be unlocked. Even if the function succeeds, the memory will remain locked if the lock count is not decremented to zero.
See also: Page locking services, Table 1, Table 2
 
function dpmi_mark_rm_region_as_pageable(startaddr : DWord; size : DWord) : Boolean;
Description: Normally some DPMI implementations lock the DOS real mode memory by default to prevent disk swapping of this memory. If a protected mode program is using DOS memory, it is a good idea to use this function to turn off automatic page locking for regions of memory that are not touched by interrupts
Parameters: startaddr - starting linear address of memory to be marked as pageable 
size - size of memory to page in bytes
Return values: none
Error code:
Notes: Do not mark memory not owned by your program as pageable. It is very important to relock any real mode memory using dpmi_relock_rm_region() before terminating the program. Memory that remains unlocked after program exit may cause fatal page faults when other software is accessing the same address space. Note that address space marked as pageable may be locked by using dpmi_lock_linear_region(). This function is only an advisory for the DPMI host to allow memory that doesn't be locked to be paged out. This function just disables any automatic locking of real-mode memory performed by the DPMI host. If this function fails then none of the memory will be unlocked. If the specified region overlaps part of a page at the beginning or end these page(s) will not marked as pageable.
See also: Page locking services, Table 1, Table 2
 
function dpmi_relock_rm_region(startaddr : DWord; size : DWord) : Boolean;
Description: This function is used to relock real-mode memory previously locked via dpmi_mark_rm_region_as_pageable().
Parameters: startaddr - starting linear address of memory to be relocked 
size - size of memory region in bytes
Return values: none
Error code:
Notes: If this function fails, none of the memory specified will be relocked. If the specified region overlaps part of a page at the beginning or end of the region, the page(s) will not be relocked.
See also: Page locking services, Table 1, Table 2
 
function dpmi_get_page_size(var pagesize : DWord) : Boolean;
Description: Returns the size of a single memory page
Parameters: none
Return values: pagesize - size of page in bytes
Error code: none (this function never fails)
Notes: The typical size is 4 kb, but don't make any assumptions on this.
See also: Page locking services, Table 1, Table 2

Demand paging performance tuning services

Some applications will discard memory objects or will not access memory objects for long periods of time. These services can be used to improve performance of demand paging.

Although these functions are only relevant for DPMI implementations that support virtual memory (both CWSDPMI and Win9x do this), other implementations may ignore this (although they always succeed).

Since both of this functions are simply advisory, the operating system may choose to ignore these calls.
 
function dpmi_mark_page_as_demand_paging_candidate(startaddr : DWord; size : DWord) : Boolean;
Description: This function is used to inform the operating system that a range of pages should be placed on top of the page out candidate list. This will force these pages to be swapped to disk even if they were accessed recently. However all memory contents will be preserved.
Parameters: startaddr - starting linear address of pages to mark 
size - size of region in bytes to mark as paging candidates
Return values: none
Error code:
Notes: This function does not force the pages to be swapped immediately. Partial pages will not be marked.
See also: Demand paging performance tuning services, Table 1, Table 2
 
function dpmi_discard_page_contents(startaddr : DWord; size : DWord) : Boolean;
Description: This function discards the entire memory contents of a given linear memory range. It is used after a memory object that occupied a given piece of memory has been discarded.
Parameters: startaddr - starting linear address of pages to discard 
size - number of bytes to discard
Return values: none
Error code:
Notes: The contents of the memory region will be undefined the next time memory is accessed. All values previously stored in this memory will be lost. Partial pages will not be discarded.
See also: Demand paging performance tuning services, Table 1, Table 2

Miscellaneous services

function dpmi_get_version(var ver : Word; var flags : Word; var processor : Byte; picbase : Word) : Boolean;
Description: Returns the version of the DPMI services supported.
Parameters: none
Return values: ver - the hi-byte is the major version number, the low-byte the minor version number 
flags - refer to DPMI specification 
processor - processor type (2=286..4=486) 
picbase - refer to DPMI specification
Error code: $0002
Notes: none :)
See also: Miscellanous services, Table 1, Table 2

Commonly used combinations of the above ("Time savers")

These are functions which do multiple calls to the DPMI unit for often repeated function sequences. This was done in order to make your life easier and the code more understandable.

A list which DPMI functions what function calls can be seen in Table 5.
 
function create_selector(var sel : Word; baseaddr, size : DWord) : Boolean;
Description: Allocates a single new selector and initializes it to the specified values
Parameters: baseaddr - linear 32 bit base address of new selector 
size - new 32 bit segment limit in bytes - 1
Return values: none
Error code: See notes
Notes: This function calls several different DPMI functions, the error code returned is the one from the DPMI functions called which caused the error.
See also: Commonly used combinations, Table 1, Table 5
 
function change_selector(sel : Word; new_baseaddr, new_size : DWord) : Boolean;
Description: Changes base address and limit of a single selector.
Parameters: new_baseaddr - new linear 32 bit base address of selector 
new_size - new 32 bit segment limit in bytes - 1
Return values: none
Error code: See notes
Notes: This function calls several different DPMI functions, the error code returned is the one from the DPMI functions called which caused the error.
See also: Commonly used combinations, Table 1, Table 5
 
function free_selector(sel : Word) : Boolean;
Description: Frees a previously allocated selector
Parameters: sel - selector to free
Return values: none
Error code: See dpmi_free_ldt_descriptor()
Notes: See dpmi_free_ldt_descriptor()
See also: Commonly used combinations, Table 1, Table 5
 
function get_linear_address(sel : Word; offset : DWord) : DWord;
Description: Returns the 32 bit linear address of a protected mode selector:offset address
Parameters: sel - selector 
offset - offset into selector
Return values: 32 bit linear address
Error code: See dpmi_get_segment_base_address()
Notes: See dpmi_get_segment_base_address()
See also: Commonly used combinations, Table 1, Table 5
 
function map_physical_memory(physaddr : DWord; size : DWord; var sel : Word) : Boolean;
Description: Maps a memory area of a physical device to a single descriptor
Parameters: physaddr - physical address of device to map 
size - size of memory region to map in bytes - 1
Return values: sel - selector to memory region
Error code: See notes
Notes: This function uses several different DPMI calls, so the error code returned is the one from the function which caused the error.
See also: Commonly used combinations, Table 1, Table 5
 
function intr(num : Byte; var r : registers) : Boolean;
Description: Issues a real mode interrupt.
Parameters: num - number of interrupt 
r - supplied registers data structure
Return values: r - registers data structure with the values changed by the interrupt
Error code: See dpmi_simulate_rm_interrupt()
Notes: See dpmi_simulate_rm_interrupt()
See also: Commonly used combinations, Table 1, Table 5
 
function realintr(num : Byte; var r : registers) : Boolean;
Description: Issues a real mod interrupt
Parameters: num - number of interrupt 
r - supplied registers data structure
Return values: r - register values data structure changed by interrupt
Error code: See dpmi_simulate_rm_interrupt()
Notes: See dpmi_simulate_rm_interrupt()
See also: Commonly used combinations, Table 1, Table 5
 
function lock_data(var data; size : DWord) : Boolean;
Description: Locks a memory range in the %ds segment
Parameters: data - address of data to be locked 
size - size of memory range to be locked
Return values: none
Error code: See dpmi_lock_linear_region()
Notes: See dpmi_lock_linear_region()
See also: Commonly used combinations, Table 1, Table 5
 
function lock_code(faddr : pointer; size : DWord) : Boolean;
Description: Locks a memory range in the %cs segment
Parameters: faddr - starting address to data (typically code) to be locked 
size - size of memory range to be locked
Return values: none
Error code: See dpmi_lock_linear_region()
Notes: See dpmi_lock_linear_region()
See also: Commonly used combinations, Table 1, Table 5
 
function unlock_data(var data; size : DWord) : Boolean;
Description: Unlocks a memory range in the %ds segment
Parameters: data - address of data to be unlocked 
size - size of memory region to be unlocked
Return values: none
Error code: See dpmi_unlock_linear_region()
Notes: See dpmi_unlock_linear_region()
See also: Commonly used combinations, Table 1, Table 5
 
function unlock_code(faddr : Pointer; size : DWord) : Boolean;
Description: Unlocks a memory range in the %cs segment
Parameters: faddr - starting address of region to be unlocked 
size - size of memory range in bytes
Return values: none
Error code: See dpmi_unlock_linear_region()
Notes: See dpmi_unlock_linear_region()
See also: Commonly used combinations, Table 1, Table 5

Segment registers access

The following functions give FPC programs full read access to the different segment registers.
 
function CSeg : Word;
Description:  Returns the contents of the %cs segment register
Parameters: none
Return values: current value of %cs
Error code: none (This function never fails)
Notes: It is allowed under FPC (GO32V2) to write to the code segment by default.
See also: Segment register access, dseg(), dsegalias(), eseg(), fseg(), gseg(), sseg(), Table 1
 
function DSeg : Word;
Description:  Returns the contents of the %ds segment register
Parameters: none
Return values: current value of %ds
Error code: none (This function never fails)
Notes: %ds and %es must always contain the same value, as some FPC function rely on this
See also: Segment register access, cseg(), dsegalias(), eseg(), fseg(), gseg(), sseg(), Table 1
 
function DSegAlias : Word;
Description: Returns ___djgpp_ds_alias, which is a copy of the %ds segment register.
Parameters: none
Return values: value of ___djgpp_ds_alias
Error code: none (This function never fails)
Notes: The only difference between %ds and this value is, that the limit of ___djgpp_ds_alias isn't set to zero at a run-time error. This is used by FPC so that the program automatically exits at the next %ds-access. 
It is good to use this value instead of the standard %ds register in real-mode callbacks, so that run-time errors do not occur while executing in real-mode (done automatically).
See also: Segment register access, cseg(), dseg(), eseg(), fseg(), gseg(), sseg(), Table 1
 
function ESeg : Word;
Description:  Returns the contents of the %es segment register
Parameters: none
Return values: current value of %es
Error code: none (This function never fails)
Notes: %ds and %es must always contain the same value, as some FPC function rely on this
See also: Segment register access, cseg(), dseg(), dsegalias(), fseg(), gseg(), sseg(), Table 1
 
function FSeg : Word;
Description:  Returns the contents of the %fs segment register
Parameters: none
Return values: current value of %fs
Error code: none (This function never fails)
Notes: %fs stores the selector to DOS real mode memory by default. Some FPC functions rely on this, so make sure that you restore it after use.
See also: Segment register access, cseg(), dseg(), dsegalias(), eseg(), gseg(), sseg(), Table 1
 
function GSeg : Word;
Description: Returns the contents of the %gs segment register
Parameters: none
Return values: current value of %gs
Error code: none (This function never fails)
Notes: This is the only segment register that can be changed without restoring it.
See also: Segment register access, cseg(), dseg(), dsegalias(), eseg(), fseg(), sseg(), Table 1
 
function SSeg : Word;
Description: Returns the contents of the %ss segment register
Parameters: none
Return values: current value of %s
Error code: none (This function never fails)
Notes:
See also: Segment register access, cseg(), dseg(), dsegalias(), eseg(), fseg(), gseg(), Table 1

Port access

 Port accesses are done via the following functions using DPMI in FPC
 
procedure outportb(port : Word; data : Byte);
Description: Sends a single byte to the specified port address
Parameters: port - port address to send data to 
data - data byte sent
Return values: none
Error code: none (This function never fails)
Notes:
See also: Port access, outportw(), outportl(), inportb(), inportw(), inportl(), Table 1
 
procedure outportw(port : Word; data : Word);
Description: Sends a single word to the specified port address
Parameters: port - port address to send data to 
data - data word sent
Return values: none
Error code: none (This function never fails)
Notes:
See also: Port access, outportb(), outportl(), inportb(), inportw(), inportl(), Table 1
 
procedure outportl(port : Word; data : Longint);
Description: Sends a single longint to the specified port address
Parameters: port - port address to send data to 
data - data longint sent
Return values: none
Error code: none (This function never fails)
Notes:
See also: Port access, outportb(), outportw(), inportb(), inportw(), inportl(), Table 1
 
function inportb(port : Word) : Byte;
Description: Reads a single byte from the specified port address
Parameters: port - Port address to get data from
Return values: Data byte read
Error code: none (This function never fails)
Notes:
See also: Port access, outportb(), outportw(), outportl(), inportw(), inportl(), Table 1
 
function inportw(port : Word) : Word;
Description: Reads a single word from the specified port address
Parameters: port - Port address to get data from
Return values: Data word read
Error code: none (This function never fails)
Notes:
See also: Port access, outportb(), outportw(), outportl(), inportb(), inportl(), Table 1
 
function inportl(port : Word) : Longint;
Description: Reads a single longint from the specified port address
Parameters: port - Port address to get data from
Return values: Data Longint read
Error code: none (This function never fails)
Notes:
See also: Port access, outportb(), outportw(), outportl(), inportb(), inportw(), Table 1

Enable / disable hardware interrupts

Enable or disable hardware interrupts in time critical code.
 
procedure Enable;
Description: Enables hardware interrupts
Parameters: none
Return values: none
Error code: none (This function never fails)
Notes: Issues a sti instruction
See also: Enable/Disable hardware interrupts, disable(), Table 1
 
procedure Disable;
Description: Disables all hardware interrupts
Parameters: none
Return values: none
Error code: none (This function never fails)
Notes: Hardware interrupts shouldn't be turned off for longer periods of time, because several components of a modern PC rely on this.
See also: Enable/Disable hardware interrupts, enable(), Table 1

Transfer buffer access

The transfer buffer is a pre-allocated DOS memory area, which can be freely used by applications for temporary use, e.g. buffers for real-mode interrupts which demand that the buffer is located in real-mode memory area (<1MB boundary).
 
function tb_size : DWord;
Description: Returns the size of the pre-allocated transfer buffer.
Parameters: none
Return values: Size in bytes of the buffer
Error code: none (This function never fails)
Notes: Don't make any assumptions about the size of this buffer, although it is typically 16 kb.
See also: Transfer buffer, DOS memory management, tb_size(), Table 1
 
function tb_address : DWord;
Description: Returns the linear offset of the transfer buffer from the %fs segment.
Parameters: none
Return values: Offset of buffer from the beginning of the %fs segment
Error code: none (This function never fails)
Notes: The fseg() function can be used to determine the full 48 bit protected mode selector:offset address for copying purposes. 
The real mode segment:offset address of this block is (tb_address() shr 4):(tb_address() and $F) as usual..
See also: Transfer buffer, DOS memory management, tb_address(), Table 1

"Near pointer" handling

Accessing memory via full 48 bit pointers (selector:offset) is sometimes very annoying, especially when you need to have access to devices that don't reside in the %ds segment range, e.g. linear framebuffer of modern video cards.
Especially if speed is a matter, the repeated reloading of segment registers slows down code execution dramatically since this takes several cycles on modern cpu's.
The solution is extending a segment's limit to 4 GB and then accessing the memory via a single 32 bit "near-pointer" (also called FLAT pointers).

This is what the following functions do: They extend the limit of the %ds and %es segment register to $FFFFFFFF and return control to your application.
Since the limit doesn't affect system functions and procedures, everything works fine as long as FPC does not try to grow the heap. FPC does this in a way that the %ds segment base may change, additionally it GPF's with a 'Not enough memory' error, because it notices that the segment limit is at the maximum and it can't increase it any further.

So you have several options when designing programs:

Use near-pointer mapping carefully ! This may destroy everything on your computer, since your program can write everywhere to memory space then ;)
(It's like it was in real-mode times then, only with the difference that you have up to 4 GB of memory available)
 
function dpmi_enable_nearptr : Boolean;
Description: Enables "near-pointer" mapping by setting 32 bit limit of %ds and %es to $FFFFFFFF
Parameters: none
Return values: none
Error code: $F000
Notes: Near pointer mapping can be disabled at any time by issuing a dpmi_disable_nearptr() call.
See also: "Near pointer" handling, dpmi_disable_nearptr(), dpmi_nearptr_enabled(), dpmi_nearptr_address_mapping(), Table 1
 
function dpmi_disable_nearptr : Boolean;
Description: Disables "near-pointer" mapping by restoring the old values of %ds and %es again
Parameters: none
Return values: none
Error code: $F001
Notes: "Near pointer" mapping can be enabled and disabled at any time, but once you disabled near pointers, you'll need to restore pointers which where gained via dpmi_nearptr_address_mapping() again since the base segment address may have changed. All other FPC functions and procedures work as usual.
See also: "Near pointer" handling, dpmi_enable_nearptr(), dpmi_nearptr_enabled(), dpmi_nearptr_address_mapping(), Table 1
 
function dpmi_nearptr_enabled(var enabled : Boolean) : Boolean;
Description: Returns the current status of near pointer mapping
Parameters: none
Return values: enabled - true if "near pointers" are enabled, false if not
Error code: none (This function never fails)
Notes: none
See also: "Near pointer" handling, dpmi_enable_nearptr(), dpmi_disable_nearptr(), dpmi_nearptr_address_mapping(), Table 1
 
function dpmi_nearptr_address_mapping(physaddr, size : DWord; var nearptr : Pointer) : Boolean;
Description: Returns a 32 bit "near pointer" to a physical memory area (e.g. VGA)
Parameters: physaddr - the physical address of the device 
size - the memory size of the physical device to be mapped - 1
Return values: nearptr - the 32 bit "near pointer" to the phyiscal memory area
Error code: $F002
Notes: Fails if not in near pointer mode. This function uses several different other DPMI functions, if they fail, they return their own error code.
See also: "Near pointer" handling, dpmi_enable_nearptr(), dpmi_disable_nearptr(), dpmi_nearptr_enabled(), Table 1

Appendix A:Porting from GO32

The entire DPMI unit (including MEMORY and FARMEM) completely replace the GO32 unit. This is why this appendix is included: it should give you some hints and general rules for doing this in the easiest way possible. I compiled several tables showing you how to use DPMI functions instead of GO32 functions.

See Table 3 for a rough comparison between the two units, and some suggestions which DPMI functions you could use for what GO32 function.

In Table 4 a comparison between the different syntax of the units is made



Table 1 - Index to DPMI unit functions, types and variables sorted by category
 
Constants 
Types 
Variables 
Functions 

Mode detection 

LDT descriptor management  Memory management services  Physical address mapping  DOS memory managment  Interrupt services  Virtual interrupt state functions  Translation services  Page locking services  Miscellaneous services  Common combinations ("Time savers")  Segment register access  Port access  Transfer buffer access  Near pointer handling 
Procedures 

Error handling 

Port access  Hardware interrupts enable / disable 
 
Table 2 - Error codes returned by DPMI functions
 

DPMI function

Error codes returned

Notes


function dpmi_get_cpu_mode() (none) 1
function dpmi_allocate_ldt_descriptors() $0000
function dpmi_free_ldt_descriptor() $0001
function dpmi_segment_to_descriptor() $0002
function dpmi_get_next_selector_increment_value() ($0003) 1
function dpmi_get_segment_base_address() $0006
function dpmi_set_segment_base_address() $0007
function dpmi_get_segment_limit() See notes 2
function dpmi_set_segment_limit() $0008
function dpmi_get_descriptor_access_rights() See notes 2
function dpmi_set_descriptor_access_rights() $0009
function dpmi_create_code_segment_alias_descriptor() $000A
function dpmi_get_descriptor() $000B
function dpmi_set_descriptor() $000C
function dpmi_allocate_specific_descriptor() $000D
function dpmi_get_free_memory_information() ($0500) 1
function dpmi_allocate_memory_block() $0501
function dpmi_free_memory_block() $0502
function dpmi_resize_memory_block() $0503
function dpmi_phyiscal_address_mapping() $0800
function dpmi_allocate_DOS_memory_block() $0100
function dpmi_free_DOS_memory_block() $0101
function dpmi_resize_DOS_memory_block() $0102
function dpmi_get_rm_interrupt() ($0200) 1
function dpmi_set_rm_interrupt() $0201
function dpmi_get_exception_handler() $0202
function dpmi_set_exception_handler() $0203
function dpmi_get_pm_interrupt() $0204
function dpmi_set_pm_interrupt() $0205
function dpmi_get_and_disable_virtual_interrupts() ($0900) 1
function dpmi_get_and_enable_virtual_interrupts() ($0901) 1
function dpmi_get_virtual_interrupt_state() ($0902) 1
function dpmi_simulate_rm_interrupt() $0300
function dpmi_call_rm_procedure_with_retf_frame() $0301
function dpmi_call_rm_procedure_with_iret_frame() $0302
function dpmi_allocate_rm_callback() $0303
function dpmi_free_rm_callback() $0304
funciton dpmi_get_version() $0400 1
function dpmi_lock_linear_region() $0600
function dpmi_unlock_linear_region() $0601
function dpmi_mark_rm_region_as_pageable() $0602
function dpmi_relock_rm_region() $0603
function dpmi_get_page_size() $0604
function dpmi_mark_page_as_demand_paging_candidate() $0702
function dpmi_discard_page_contents() $0703
function create_selector() See notes 3
function change_selector() See notes 3
function free_selector() See notes 3
function get_linear_address() See notes 3
function map_physical_memory() See notes 3
function intr() See notes 3
function realintr() See notes 3
function lock_data() See notes 3
function lock_code() See notes 3
function unlock_data() See notes 3
function unlock_code() See notes 3
function dpmi_enable_nearptr() $F000 4
function dpmi_disable_nearptr() $F001
function dpmi_nearptr_enabled() (none) 1
function dpmi_nearptr_address_mapping() $F002
Notes:
  1. This function always succeeds, so you'll never encounter a error here
  2. Check the boolean result for success/fail
  3. This is a combination of several subsequent DPMI calls. The error code is the one returned by the DPMI call failed
  4. May occur both as warning or as fatal error
Table 3 - Porting from GO32
 

GO32 term

Equivalent DPMI term

Notes



Constants

carryflag See flag constants 1
parityflag See flag constants 1
auxcarryflag  See flag constants 1
zeroflag  See flag constants 1
signflag  See flag constants 1
trapflag  See flag constants 1
interruptflag See flag constants 1
directionflag See flag constants 1
overflowflag See flag constants 1


Types

tmeminfo See MemInfoBuf
tseginfo See pm_Addr, rm_Addr
trealregs See registers
registers See registers


Variables

dosmemselector See fseg()
int31error See dpmi_error, dpmi_set_error_handler() 2


Functions & Procedures

function allocate_ldt_descriptors()  See dpmi_allocate_ldt_descriptors()create_selector()
function free_ldt_descriptor() See dpmi_free_ldt_descriptor(), free_selector()
function segment_to_descriptor() See dpmi_segment_to_descriptor()
function get_next_selector_increment_value() See dpmi_get_next_selector_increment_value()
function get_segment_base_address() See dpmi_get_segment_base_address()
function set_segment_base_address() See dpmi_set_segment_base_address(), create_selector()
function set_segment_limit() See dpmi_set_segment_limit()
function set_descriptor_access_rights() See dpmi_set_descriptor_access_rights()
function create_code_segment_alias_descriptor() See dpmi_create_code_segment_alias_descriptor()
function get_linear_addr() See dpmi_physical_address_mapping(), map_physical_memory(), get_linear_address()
function get_segment_limit() See dpmi_get_segment_limit()
function get_descriptor_access_right() See dpmi_get_descriptor_access_rights()
function get_page_size() See dpmi_get_page_size()
function map_device_in_memory_block() See notes 3
function realintr() See dpmi_simulate_rm_interrupt(), intr(), realintr()
function global_dos_alloc() See dpmi_allocate_DOS_memory_block()
function global_dos_free() See dpmi_free_DOS_memory_block()
procedure seg_fillchar() See notes
procedure seg_fillword() See notes
function get_meminfo() See dpmi_get_free_memory_information()
function get_pm_interrupt() See dpmi_get_pm_interrupt()
function set_pm_interrupt() See dpmi_set_pm_interrupt()
function get_rm_interrupt() See dpmi_get_rm_interrupt()
function set_rm_interrupt() See dpmi_set_rm_interrupt()
function get_exception_handler() See dpmi_get_exception_handler()
function set_exception_handler() See dpmi_set_exception_handler()
function get_pm_exception_handler() See notes 3, 4
function set_pm_exception_handler() See notes 3, 4
function free_rm_callback() See dpmi_free_rm_callback()
function get_rm_callback() See dpmi_allocate_rm_callback()
function get_cs() See cseg() 1
function get_ds() See dseg(), dsegalias() 1
function get_ss() See sseg() 1
function allocate_memory_block() See dpmi_allocate_memory_block() 5, 6
function free_memory_block() See dpmi_free_memory_block() 6
function request_linear_region() See notes 3, 4
function lock_linear_region() See dpmi_lock_linear_region()
function lock_data() See lock_data()
function lock_code() See lock_code()
function unlock_linear_region() See dpmi_unlock_linear_region()
function unlock_data() See unlock_data()
function unlock_code() See unlock_code()
procedure disable() See disable()
procedure enable() See enable()
function inportb() See inportb()
function inportw() See inportw()
function inportl() See inportl()
procedure outportb() See outportb()
procedure outportw() See outportw()
procedure outportl() See outportl()
function get_run_mode() See notes 7
function transfer_buffer() See tb_address()
function tb_segment() See tb_address()
function tb_offset() See tb_address()
function tb_size() See tb_size()
procedure copytodos() See notes 8
procedure copyfromdos() See notes 8
procedure dosmemput() See notes 8
procedure dosmemget() See notes 8
procedure dosmemmove() See seg_memcpy() 9
procedure dosmemfillchar() See seg_memsetb() 9
procedure dosmemfillword() See seg_memsetw() 9
 
Notes:
  1. Actually more than these are supplied by DPMI
  2. The dpmi_error variable is updated by the error-handler supplied by dpmi_set_error_handler() by default
  3. DPMI 1.0 function, does not work at all.
  4. Works only with CWSDPMI under real DOS (not Windows9x DOS-Box) in FPC
  5. Buggy implementation in GO32
  6. DPMI allows resizing of these blocks via dpmi_resize_memory_block() too
  7. Obsolete, because DPMI only supports GO32V2 model
  8. Use equivalent seg_memcpy() or seg_memsetX() commands supplied by MEMORY.
  9. See the documentation for the MEMORY unit (memory.htm included in this package)
Table 4 - Syntax comparison

Btw, this table shows only a *direct* translation of these GO32 calls. It's often much better and easier to use other functions or "time-saver" functions. See Table 5
 

GO32 syntax example

DPMI syntax example

Notes


selector := allocate_ldt_descriptors(2)  dpmi_allocate_ldt_descriptors(2, selector) 1
free_ldt_descriptor(selector) dpmi_free_ldt_descriptor(selector) 2
selector := segment_to_descriptor($A000) dpmi_segment_to_descriptor($A000, selector)
selincr := get_next_selector_increment_value() dpmi_get_next_selector_increment_value(selincr)
base_address := get_segment_base_address(selector) dpmi_get_segment_base_address(selector, base_address)
set_segment_base_address(selector, new_address) dpmi_set_segment_base_address(selector, new_address) 1
set_segment_limit(selector, new_limit) dpmi_set_segment_limit(selector, limit)
set_descriptor_access_rights(selector, new_rights) dpmi_set_descriptor_access_rights(selector, new_rights)
datasel := create_code_segment_alias_descriptor(code_sel) dpmi_create_code_segment_alias_descriptor(code_sel, data_sel)
linear_addr := get_linear_addr(phys_addr, size) dpmi_physical_address_mapping(phys_addr, size, linear_addr) 3
limit := get_segment_limit(selector) dpmi_get_segment_limit(selector, limit)
rights := get_descriptor_access_right(selector) dpmi_get_descriptor_access_rights(selector, rights)
page_size := get_page_size() dpmi_get_page_size(page_size)
function map_device_in_memory_block() See notes 4
function realintr(reg_num, reg_buffer) dpmi_simulate_rm_interrupt(reg_num, reg_buffer) 5
result := global_dos_alloc(size); 
realseg := word(result); 
dos_selector := word(result shr 16);
dpmi_allocate_DOS_memory_block(size, realseg, dos_selector)
global_dos_free(dos_selector) dpmi_free_DOS_memory_block(dos_selector)
procedure seg_fillchar(selector, offset, size, char(byte_value)) seg_memsetb(selector, offset, size, byte_value) 6, 7
seg_fillword(selector, offset, size, word_value) seg_memsetw(selector, offset, size, word_value) 6, 7
get_meminfo(buffer) dpmi_get_free_memory_information(buffer)
get_pm_interrupt(number, tseginfo_buffer) dpmi_get_pm_interrupt(number, pm_Addr(buffer)) 8
set_pm_interrupt(number, tseginfo_buffer) dpmi_set_pm_interrupt(number, pm_Addr(buffer)) 8
get_rm_interrupt(number, tseginfo_buffer) dpmi_get_rm_interrupt(number, rm_Addr(buffer)) 8
set_rm_interrupt(number, tseginfo(buffer)) dpmi_set_rm_interrupt(number, rm_Addr(buffer)) 8
get_exception_handler(number, tseginfo(buffer)) dpmi_get_exception_handler(number, pm_Addr(buffer)) 8
set_exception_handler(number, tseginfo(buffer)) dpmi_set_exception_handler(number, pm_Addr(buffer)) 8
get_pm_exception_handler() See notes 4
set_pm_exception_handler() See notes 4
free_rm_callback(tseginfo(intaddr)) dpmi_free_rm_callback(rm_Addr(intaddr))
get_rm_callback(pointer(func_addr), registers, tseginfo(rmcb)) dpmi_allocate_rm_callback(pm_Addr(func_Addr), pm_Addr(registers), rm_Addr(rmcb)) 7
cs := get_cs() cs := cseg() 6
ds := get_ds() ds := dseg() 6, 9
ss := get_ss() ss := sseg() 6
linaddr := allocate_memory_block(size) dpmi_allocate_memory_block(size, handle, linaddr) 10
free_memory_block(handle) dpmi_free_memory_block(handle) 11
function request_linear_region() See notes 4
lock_linear_region(linear_addr, size) dpmi_lock_linear_region(linear_addr, size) 6
lock_data(buffer, size) lock_data(buffer, size) 6
lock_code(func_addr, size) lock_code(func_addr, size) 6
unlock_linear_region(linear_addr, size) dpmi_unlock_linear_region(linear_addr, size) 6
unlock_data(buffer, size) unlock_data(buffer, size) 6
unlock_code(func_addr, size) unlock_code(func_addr, size) 6
disable() disable() 6
enable() enable() 6
data := inportb(port) data := inportb(port) 6
data := inportw(port) data := inportw(port) 6
data := inportl(port) data := inportl(port) 6
outportb(port, data) outportb(port, data) 6
outportw(port, data) outportw(port, data) 6
outportl(port, data) outportl(port, data) 6
get_run_mode() See notes 12
address := transfer_buffer() address := tb_address() 6
seg := tb_segment() seg := tb_address() shr 4
ofs := tb_offset() ofs := tb_address() and $F
size := tb_size() size := tb_size() 6
copytodos(buffer, size) seg_memcpy(dseg, dword(@buffer), fseg, tb_address(), size) 7
copyfromdos(buffer, size) seg_memcpy(fseg, tb_address(), dseg, dword(@address), size)) 7
dosmemput(seg, ofs, buffer, size) seg_memcpy(dseg, dword(@buffer), fseg, seg shl 4 + ofs, size) 7
procedure dosmemget(seg, ofs, buffer, size) seg_memcpy(fseg, seg shl  4 + ofs, dseg, dword(@buffer), size) 7
dosmemmove(srcseg, srcofs, dstseg, dstofs, size) seg_memcpy(fseg, srcseg shl 4 + ofs, fseg, dstseg shl4 + dstofs, size) 7
dosmemfillchar(seg, ofs, size, char(value)) seg_memsetb(fseg, seg shl 4 + ofs, size, byte(value)) 7, 8
dosmemfillword(seg, ofs, size, value) seg_memsetw(fseg, seg shl 4 + ofs, size, value) 7
seg_move(srcsel, srcofs, dstsel, dstofs, size) seg_memcpy(srcsel, srcofs, dstsel, dstofs, size) 6, 7
seg_fillchar(sel, ofs, size, char(value)) seg_memsetb(sel, ofs, size, char(value)) 6, 7
seg_fillword(sel, ofs, size, value) seg_memsetw(sel, ofs, size, value) 6, 7
Notes:

  1. You could use create_selector() too
  2. free_selector() could be used too
  3. Better use map_physical memory for short
  4. DPMI 1.0 function won''t work on a DPMI 0.9 host (as CWSDPMI and Win9x DOS box is), so there is no equivalent in DPMI
  5. Use realintr(), or even easier intr() which does the same
  6. Same syntax
  7. By using MEMORY which is automatically included within DPMI via a uses clause
  8. 'Typecasts' indicate that a different type of a passed buffer is expected in DPMI
  9. dseg_alias() could be used too
  10. The GO32 function is missing an important parameter (handle)
  11. Since you don't get a handle for such a GO32 memory block, this function is useless there
  12. Obsolete since DPMI only works in GO32V2 mode
Table 5 - "Time saver" procedures and their equivalent DPMI and GO32 function calls
 
 

"Time saver" function

DPMI calls

Notes

function create_selector(selector, baseaddr, size) dpmi_allocate_ldt_descriptors(1, selector) and dpmi_set_segment_base_address(selector, baseaddr) and 
dpmi_set_segment_limit(selector, size)
function change_selector(selector, new_baseaddr, new_size) dpmi_set_segment_base_address(selector, new_baseaddr) and dpmi_set_segment_limit(selector, new_size)
function free_selector(selector) dpmi_free_ldt_descriptor(selector)
function get_linear_address(selector, offset) dpmi_get_segment_base_address(selector, baseaddr) adding offset 
 
map_physical_memory(phys_addr, limit, selector) dpmi_allocate_ldt_descriptors(1, selector) and dpmi_physical_address_mapping(phys_addr, limit, linearaddr) and dpmi_set_segment_base_address(selector, linearaddr) and dpmi_set_segment_limit(selector, size)
intr(number, registers) dpmi_simulate_rm_interrupt(number, registers)
realintr(number, registers) dpmi_simulate_rm_interrupt(number, registers)
lock_data(buffer, size) dpmi_lock_linear_region(get_linear_address(dseg, dword(@buffer)), size)
lock_code(func_addr, size) dpmi_lock_linear_region(get_linear_address(cseg, dword(func_addr)), size)
unlock_data(buffer, size) dpmi_unlock_linear_region(get_linear_address(dseg, dword(@buffer)), size)
unlock_code(buffer, size) dpmi_unlock_linear_region(get_linear_address(cseg, dword(func_addr)), size)


This document is (c) Thomas Schatzl, 1. January 1999.