This is the documentation for PMODE 3.0 DPMI/VCPI/XMS/raw protected mode interface kernel. Copyright (c) 1994, Tran (a.k.a. Thomas Pytel). PMODE is publicly available and is not confidential or proprietary. I, Thomas Pytel, reserve all rights to the source code. However, feel free to use or distribute it in any manner you wish. All I ask, if you use this code in some production, is credits for it. ------------------------------------------------------------------------------ Contents: --------- 0 - Introduction 0.0 - Disclaimer 0.1 - Description 1 - Overview 1.0 - Initialization and termination 1.1 - Segments, selectors, and descriptors 1.2 - Stacks and mode switching 1.3 - Interrupts 1.4 - Real mode callbacks 1.5 - PMODE specifics 2 - Functions 2.0 - Function 0000h - Allocate Descriptors 2.1 - Function 0001h - Free Descriptor 2.2 - Function 0003h - Get Selector Increment Value 2.3 - Function 0006h - Get Segment Base Address 2.4 - Function 0007h - Set Segment Base Address 2.5 - Function 0008h - Set Segment Limit 2.6 - Function 0009h - Set Descriptor Access Rights 2.7 - Function 000Ah - Create Alias Descriptor 2.8 - Function 000Bh - Get Descriptor 2.9 - Function 000Ch - Set Descriptor 2.10 - Function 000Eh - Get Multiple Descriptors 2.11 - Function 000Fh - Set Multiple Descriptors 2.12 - Function 0200h - Get Real Mode Interrupt Vector 2.13 - Function 0201h - Set Real Mode Interrupt Vector 2.14 - Function 0204h - Get Protected Mode Interrupt Vector 2.15 - Function 0205h - Set Protected Mode Interrupt Vector 2.16 - Function 0300h - Simulate Real Mode Interrupt 2.17 - Function 0301h - Call Real Mode Procedure With Far Return Frame 2.18 - Function 0302h - Call Real Mode Procedure With IRET Frame 2.19 - Function 0303h - Allocate Real Mode Callback Address 2.20 - Function 0304h - Free Real Mode Callback Address 2.21 - Function 0305h - Get State Save/Restore Addresses 2.22 - Function 0306h - Get Raw Mode Switch Addresses 2.23 - Function 0400h - Get Version 2.24 - Function 0500h - Get Free Memory Information 2.25 - Function 0501h - Allocate Memory Block 2.26 - Function 0502h - Free Memory Block 2.27 - Function 0503h - Resize Memory Block 2.28 - Function 050Ah - Get Memory Block Size and Base 2.29 - Function 0900h - Get and Disable Virtual Interrupt State 2.30 - Function 0901h - Get and Enable Virtual Interrupt State 2.31 - Function 0902h - Get Virtual Interrupt State 3 - Miscellaneous 3.0 - Updates 3.1 - Glossary 3.2 - Differences among modes 3.3 - Notes 3.4 - Final word ------------------------------------------------------------------------------ 0 - Introduction: ----------------- This document will not attempt to explain the workings of protected mode. If you are new to protected mode coding, I suggest you get yourself a good book. I also suggest you get your hands on some good DPMI documentation as a reference and background for understanding PMODE. This document is only intended to explain the workings of PMODE for the purpose of using it directly or for writing a shell or high level language interface. 0.0 Disclaimer: --------------- Legal: I exclude any and all implied warranties, including warranties of merchantability and fitness for a particular purpose. I make no warranty or representation, either express or implied, with respect to this source code, its quality, performance, merchantability, or fitness for a particular purpose. I shall have no liability for special, incidental or consequential damages arising out of or resulting from the use or modification of this source code. English: If you fuck up, its your own problem. 0.1 Description: ---------------- PMODE 3.0 is basically a DOS extender. It allows DOS programs to run in full protected mode. PMODE will take care of all the system details, the descriptor tables, extended memory management, interrupts, etc... It does not matter what kind of system is already in place. DPMI, VCPI, XMS, and a clean system will all be handled appropriately. If DPMI is in place, PMODE will do basically nothing, and your code will be talking directly to the DPMI host. But if it is not, PMODE will provide a subset of DPMI functionality to your code. It is slightly annoying that most of the DPMI interface uses pairs of 16bit registers for 32bit values. This goes back to the 80286 support of DPMI. Code using PMODE can set up its own descriptors. It can run across real mode, 16bit protected mode, and 32bit protected mode. Full extended memory management is provided. Blocks of extended memory can be allocated, resized, and freed. PMODE does not manage low memory, Your code is responsible for the memory below the 1M boundary. The PMODE kernel is well suited for a shell to extend its functionality. A shell to provide extended or simplified services to assembly or high level code. I wrote PMODE with attention to speed. Wherever I could control it, I tried to ensure that code using PMODE would run as fast as possible. I also tried to make sure PMODE is very stable. Under DPMI, your code is a slave to the DPMI host's whims. Almost definately running at CPL 3, which makes it quite slow. There is nothing I can do about that. Well, locating and messing with the DPMI host's system tables might not be too hard, but too unpredictable. Running in protected mode at CPL 3 is better than real mode CPL 3, because you can avoid loading segment registers very often (which is slow) by setting up flat memory. However, if DPMI is not present, you can be sure your code will be running as fast as it possibly can. Under a VCPI, XMS, or raw system, PMODE will run your code at CPL 0. There are no I/O permission bitmap checks on port accesses or task switching. Under an XMS or raw system, real mode calls are executed in actual real mode rather than the slower V86 mode, which is actually protected mode at CPL 3. Under VCPI, real mode calls are executed in V86 mode, which is what the VCPI server normally runs DOS in. Also, under VCPI, paging is enabled. It is a minor speed degredation factor, but one that is avoided under XMS or raw. I've been coding 386 protected mode systems for a couple of years now. I tried to make PMODE as clean as possible. But it was coded from scratch. Though I tested it extensively, there is unfortunately always the possibility of a bug. I am pretty confident it is clean though, because I went over EVERY line of code when finally done with it. I also plugged it into some old programs using a previous version of PMODE, which did well to help me find a few bugs. ------------------------------------------------------------------------------ 1 - Overview: ------------- I adopted the DPMI interface for PMODE to make code that works with PMODE very portable to other extenders. Only a subset of DPMI is supported, both to keep the size of the kernel down, and because I am lazy (the latter being the more important factor). The interface to PMODE is INT 31h in protected mode. Functions are available for descriptors, interrupt vectors, extended memory, real mode callbacks, and calling real mode interrupts and procedures. PMODE works as a subset of DPMI 1.0 rather than DPMI 0.9. This means that error codes are returned from unsuccessful function calls, and some functions are available that are not available under DPMI 0.9. Note though, that if the system is already under DPMI 0.9, PMODE code is not active, so error codes will not be returned and only DPMI 0.9 functions will be available. 1.0 - Initialization and termination: ------------------------------------- There are only two functions immediately callable in PMODE. They are _pm_info and _pm_init. _pm_info returns some information about the current type of system and the low memory requirements for protected mode. No matter what the system, DPMI, VCPI, XMS, or raw, a low memory buffer is required for protected mode operation. The size of this buffer is returned from _pm_info. Your code is responsible for providing that buffer to _pm_init. _pm_init switches the system into protected mode. If DPMI is in place, all that is done is a switch into 32bit protected mode. If the DPMI host does not support 32bit protected mode, _pm_init will return an error. 32bit protected mode does not necessarily mean your code has to run in a 32bit segment with default 32bit instructions. It just means that it is possible. Since DPMI is defined for 80286 computers, and you might even find DPMI that is capable of 32bit protected mode refusing this request on a 386 system. _pm_init will return with the carry flag set and an error code in AX if an error ocurred while trying to switch into protected mode. If the switch to protected mode was successful, the carry flag will be clear, and the system will be in protected mode. The CS segment register will have been converted to a protected mode selector corresponding to a descriptor mapping the same memory as it did in real mode. Likewise, the DS and SS segment registers will be converted to selectors. If DS and SS were equal before the call to _pm_init, the same selector may be returned in both. ES will contain a selector for your program's PSP. The environment segment at PSP:2ch will also be converted to a selector if it was a non-zero value before the call to _pm_init. FS and GS will be returned as 0 (NULL selector). Your code will now be running in a 16bit protected mode code segment, with full access to the protected mode INT 31h functions. If the system is DPMI, this is the last your code will have talked to PMODE, and from now on, will be using the DPMI host directly. Both functions are FAR, and the full calling format is as follows: ) _pm_info - Get protected mode info: In: None Out: AX - return code: 0000h - successful 0001h - no 80386+ detected 0002h - system already in protected mode and no VCPI or DPMI found 0003h - DPMI - host is not 32bit CF - set on error, if no error: BX - number of paragraphs needed for protected mode data (may be 0) CL - processor type: 03h - 80386 04h - 80486 05h - 80586 06h-FFh - reserved for future use CH - protected mode type: 00h - raw 01h - XMS 02h - VCPI 03h - DPMI ) _pm_init - Initialize protected mode: In: ES - real mode segment for protected mode data (ignored if not needed) Out: AX - return code: 0000h - successful 0001h - no 80386+ detected 0002h - system already in protected mode and no VCPI or DPMI found 0003h - DPMI - host is not 32bit 0004h - could not enable A20 gate 0005h - DPMI - could not enter 32bit protected mode 0006h - DPMI - could not allocate needed selectors CF - set on error, if no error: ESP - high word clear CS - 16bit selector for real mode CS with limit of 64k SS - 32bit selector for real mode SS with limit of 64k DS - 32bit selector for real mode DS with limit of 64k ES - 32bit selector for PSP with limit of 100h FS - 0 (NULL selector) GS - 0 (NULL selector) The CS, DS, and SS selectors returned from _pm_init can be modified or freed by your code. The PSP selector and the converted environment selector in the PSP may not be. There is a special case when the CS selector returned from _pm_init may not be freed or modified. That is when the FAR CALL to _pm_init came from the PMODE_TEXT segment. In this case, PMODE will return its own code selector which maps PMODE_TEXT. This is just a minor optimization provided for any shell that has its code in PMODE_TEXT to save a descriptor. To terminate under PMODE, your code must issue an INT 21h function 4ch in protected mode. Just like in real mode, AL is the return code. Your code should only terminate from the main stream of execution. That is, do not try to quit from a protected mode IRQ handler or a real mode callback. Before termination, your code has the following responsibilities: ) Restore any real mode interrupt vectors which were hooked. ) Free any extended memory blocks that were allocated. 1.1 - Segments, selectors, and descriptors: ------------------------------------------- As you know (I hope), in protected mode, selectors are used in place of actual segment values in segment registers. Selectors are basically indexes into system tables which contain all the information about segments in descriptors. You can think of selectors as handles to segments. They are independent of the actual location and size of the segment in memory. Under PMODE, your code can allocate its own selectors and descriptors. You can set up code segments, data segments, and your own stack segments. These segments can be either 16bit, 32bit, or a mix. The best use of the flexibility of protected mode is to set up very large segments, in effect, eliminating the need for segmentation. You can set up a code descriptor, and set its size to 4G. Then a data descriptor of that same size and with the same base address. When all memory is addressable from a single segment, there is little need for other segments. But they are available, possibly for code modules to be loaded from disk into seperate segments. After allocating a descriptor, you code can set it up in one shot, with Set Descriptor function (000ch). Or you can set the base address, limit, and access rights/type seperately. You can also allocate a descriptor and have it automatically set to the same base address and size as another descriptor using the Create Alias Descriptor function (000ah). You code can also read a whole descriptor or base address of a descriptor. There is no Get Segment Limit function because the LSL instruction performs that function. The LAR instruction returns the access rights/type of a descriptor. Technically, it is possible for DPMI to deny requests to set up very large segments for protection reasons. But no DPMI host that I know of does this. They all do protection at the paging level. DPMI 1.0 specifies a function that returns an absolute ceiling for large segments. Allowing for flat memory, but falling short of the full 4G linear address range. I would not worry about this. If DPMI 1.0 hosts started to deny requests to set segment sizes to 4G, many protected mode extended programs would cease to function. When setting up a data descriptor which will be used as a stack segment, be aware that the B bit will determine whether PUSHes and POPs on that stack use SP or ESP as the top of stack pointer. However, even if you use a stack with the B bit clear (using SP), ESP should still be the top of stack pointer. Which means that when using a 16bit stack segment, the high word of ESP MUST be clear. 1.2 - Stacks and mode switching: -------------------------------- Within your main stream of execution, your code can set up its own stack. But there are times when a stack is provided to your code and your code should stay on that stack. At those times, your code may switch stacks during processing, but should return on the same stack it was called. This is during servicing of hardware interrupts or real mode callbacks in protected mode. Switching between protected mode and real mode can be accomplished in one of many ways. In protected mode, the default IRQ handlers switch to real mode to execute the real mode handler for the specific IRQ that was called. A software INT instruction in protected mode is also, by default, sent to on to real mode for processing. There are three specific functions which allow you to call real mode interrupts and procedures in a much more structured manner. There are real mode callbacks. These are basically addresses in real mode which, when called in real mode, transfer control to protected mode routines defined by your code. And finally, there is raw mode switching. Your code can obtain the addresses of a real mode routine which will switch the system into protected mode, and a protected mode routine which will switch the system into real mode. This is the lowest level, and quickest, method of switching modes. IRQ and INT redirection is discussed later, as are real mode callbacks. The INT 31h functions 0300h, 0301h, and 0302h allow your code to call real mode interrupts of FAR routines. Since protected mode selectors are not valid in real mode, your must pass the values to load into the segment registers in real mode, for the interrupt or routine, in a memory structure. This structure also contains the general registers as you want to pass them to real mode. Using these INT 31h functions, you can specify a portion of data from the protected mode stack to be put on the real mode stack for the interrupt or procedure call. You also do not have to provide a real mode stack. If you set the SS and SP fields in the register structure to 0, PMODE will provide a real mode stack for the real mode call. If you prefer, however, you can provide the stack yourself. Upon return from the real mode call, the register structure will contain the values that were passed back in the registers from the real mode interrupt handler or procedure. The CS, IP, SS, and SP fields will remain unmodified though. Using the raw mode switching routines is the fastest way to switch between modes. However, if these functions are to be used, special measures must be taken to preserve the state of the system. If using raw mode switching, you must use the state save/restore fuctions whose addresses you can obtain with INT 31h function 0305h. The state is saved in a buffer you provide. The stack is a good place for this buffer. Some example code is in order: buffersize dd ? ; size of state buffer pmstate df ? ; selector:offset of state routine pmtorm df ? ; selector:offset of switch routine rmstate dd ? ; segment:offset of state routine rmtopm dd ? ; segment:offset of switch routine ; this code gets and stores the addresses of the various routines mov ax,305h ; get addresses of state save/restore int 31h ; routines movzx eax,ax ; zero high word of EAX mov buffersize,eax ; size of state buffer mov word ptr rmstate[0],cx ; offset of real mode state routine mov word ptr rmstate[2],bx ; segment of real mode state routine mov dword ptr pmstate[0],edi ; offset of protected mode routine mov word ptr pmstate[4],si ; selector of protected mode routine mov ax,306h ; get addresses of mode switch int 31h ; routines mov word ptr rmtopm[0],cx ; offset of real mode switch routine mov word ptr rmtopm[2],bx ; segment of real mode switch routine mov dword ptr pmtorm[0],edi ; offset of protected mode routine mov word ptr pmtorm[4],si ; selector of protected mode routine ; this code saves the state and jumps to real mode sub esp,buffersize ; allocate buffer space on stack mov edi,esp ; set ES:EDI = SS:ESP, buffer address mov ax,ss mov es,ax xor al,al ; set AL = 0, save state call pmstate ; save state mov ax,real_mode_DS_value ; set values for real mode registers mov cx,real_mode_ES_value mov dx,real_mode_SS_value mov bx,real_mode_SP_value mov si,real_mode_CS_value mov di,real_mode_IP_value jmp pmtorm ; switch to real mode ; this would restore the state after a return from real mode mov edi,esp ; set ES:EDI = SS:ESP, buffer address mov ax,ss mov es,ax mov al,1 ; set AL = 1, restore state call pmstate ; restore state add esp,buffersize ; discard stack buffer space Real mode code to save/restore the state and call protected mode would be similar, except that EBX would be used to pass a value for ESP, and EDI would be used to pass EIP rather than IP in switching modes. Also, ES:DI would be used as the state buffer rather than ES:EDI. 1.3 - Interrupts: ----------------- When protected mode is first entered, all interrupts except those providing DPMI functionality are directed to a handler which will pass them on to real mode. That is, a software INT instruction executed by your code in protected mode will cause the CPU to be switched to real mode and the interrupt will be re-issued in real mode. After the interrupt handler returns, the system will be switched back to protected mode. All general registers (EAX, EBX, ECX, EDX, ESI, EDI, and EBP) in protected mode are passed on to the real mode handler, and the general registers and flags are passed back from real mode. The segment registers are not passed on to real mode since segment registers have different meanings in protected mode and real mode. This means that you can call simple interrupt routines which do not require values in segment registers, such as the keyboard BIOS function 0 to read a character from the keyboard, by just setting AH to 0 and issuing an INT 16h in protected mode. If you need to pass segment registers to a real mode interrupt handler, you must use the INT 31h function 0300h. IRQs are likewise passed on to real mode. PMODE will not, for the sake of a little speed increase, pass any registers to or from a real mode IRQ handler. A real DPMI host probably will pass the general registers just as it would for a software INT instruction. You may hook a protected mode interrupt vector for any interrupt, 0-0ffh, and process the interrupt entirely in protected mode if you wish. Or you can do some processing in protected mode, then chain to the real mode handler by passing control to the previous handler for the interrupt your code hooked. If an IRQ occurs in real mode, by default, it will not be passed on to its protected mode handler. Rather, it will go directly to its real mode handler. Under a real DPMI host, the IRQ will probably be sent on to its protected mode handler first. If you wish send IRQs that occur in real mode to a protected mode handler, set up a real mode callback for that interrupt vector. Or faster than that, install your own real mode routine which will switch to your protected mode code using the raw mode switching routines. PMODE will provide a real mode stack for both the software INT redirection and the hardware IRQ redirection to real mode. If a real mode stack is unavailable because of too many nested calls to real mode, the PC speaker will be turned on and the machine will be hung. I prefer this to clunky exception code messing up my nice and pretty extender. In protected mode, when interrupt handlers are called, the interrupt flag is not disabled like it is in real mode. This is done only for IRQs and interrupts 0-7. Handlers for all other interrupts in protected mode must not assume the interrupt flag has been cleared for them. If they need interrupts disabled, they must do it themselves. Under DPMI, the interrupt flag may need to be virtualized. I will spare you a long explanation because any good DPMI text will give that to you. But I will give you some rules: ) You must not assume anything about instructions that would normally affect the interrupt flag. Instructions like POPF and IRETD may have no effect on the current status of the interrupt flag. Also, PUSHF or an INT may not store the interrupt flag correctly, so do not trust them to get information on the interrupt flag. ) The only things you can be sure will affect the interrupt flag are CLI, STI, and INT 31h functions 0900h and 0901h. ) If you need to learn the current status of the interrupt flag, you must use one of the INT 31h functions, 0900h to clear and get the status of the interrupt flag, 0901h to set and get the status of the flag, or 0902h which simply returns the current value of the interrupt flag. ) Since IRETD may not affect the interrupt flag, you should re-enable interrupts in a protected mode IRQ handler. This is because the interrupt flag will have been cleared upon entry to the handler. When writing interrupt handlers, you must terminate them with an IRETD, not a simple IRET. For hardware IRQ handlers, you may want to make sure that any code and data that may be touched by the IRQ handler resides in low memory, below 1M. This memory is locked under DPMI, and will prevent having to swap from disk at interrupt time under DPMI hosts which support virtual memory. One other thing you should remember about IRQ handlers. IRQ 2 is really IRQ 9. Devices which say they use IRQ 2 are actually using IRQ 9. In real mode, the BIOS handler for IRQ 9 redirects it to the handler for IRQ 2. There is no such redirection done in protected mode. So if you wish to write a handler for IRQ 2 in protected mode, you must put it on IRQ 9. And remember to send the EOI to the second interrupt controller. The BIOS IRQ 9 handler does that, but you don't have the BIOS anymore. 1.4 - Real mode callbacks: -------------------------- Real mode callbacks allow code running in real mode to call protected mode procedures in a transparent manner. The real mode code thinks it is passing control to another real mode procedure. This is, in reality, a real mode callback. Which is basically a small routine which stores the values of the real mode registers in a structure, then switches to protected mode and passes control to a protected mode routine you specify. Callbacks are allocated and freed just like descriptors or memory. When allocating a callback, your code specifies the address of the protected mode routine that is to gain control when the real mode callback is called. You must also specify the selector:offset of a memory structure that is to recieve the contents of the registers in real mode. The format of this structure is the same as for INT 31h functions 0300h, 0301h, and 0302h. When the protected mode routine for a real mode callback gets control, interrupts will be disabled, and the following registers are defined: DS:ESI - selector:offset corresponding to real mode SS:SP ES:EDI - selector:offset of real mode register data structure SS:ESP - protecterd mode stack provided by PMODE or the DPMI host The real mode register data structure has the following format: Offset Length Contents 00h 4 EDI 04h 4 ESI 08h 4 EBP 0ch 4 reserved, ignored 10h 4 EBX 14h 4 EDX 18h 4 ECX 1ch 4 EAX 20h 2 CPU status flags 22h 2 ES 24h 2 DS 26h 2 FS 28h 2 GS 2ah 2 IP, undefined 2ch 2 CS, undefined 2eh 2 SP 30h 2 SS All fields except the CS and IP are filled in with the contents of the real mode registers when the real mode callback got control. The protected mode callback procedure can extract its parameters from the register data structure and/or the real mode stack. Remember that the segment registers contain real mode segment addresses, not protected mode selectors. The protected mode callback procedure exits with an IRETD with the address of the real mode register data structure in ES:EDI. Information can be passed back to real mode by modifying the contents of the register data structure and the real mode stack. The protected mode callback routine is responsible for setting the correct address for the resumption of execution in real mode in the CS:IP fields of the register data structure. It is also responsible for updating the SS:SP fields in the register data structure to remove the address of the calling real mode routine from the real mode stack. The real mode register data structure and the DS selector used to map the real mode SS segment are static. This is not a problem if you leave interrupts disabled throughout the protected mode callback routine. But if you intend to re-enable interrupts, you must make sure you do not use the DS selector anymore. If you need to access the real mode stack after enabling interrupts, you must create an alias descriptor for the DS selector passed to your callback procedure. Or you can take other measures, but just remember that the original DS selector is no longer safe after enabling interrupts in a protected mode callback routine. Since the real mode register data structure is also static, you should take special care if you wish to enable interrupts in a callback. Since the data structure is needed for the exit from the callback, you can not simply discard it. You should make a copy of the data structure. You can then pass back the copy when you exit the callback procedure. The ES:EDI upon exit is not required to be the same as on entry. 1.5 - PMODE specifics: ---------------------- PMODE makes several variables public. These control certain things when running under VCPI, XMS, or a raw system. Under DPMI, these variables do absolutely nothing. The size of the low memory protected mode buffer required for _pm_init is directly affected by these variables. Thus, they must be set to the same values for the call to _pm_info and _pm_init. You should only modify these variables before switching into protected mode. ) _pm_pagetables - This specifies the number of page tables you want to have under VCPI. Each page table requires 4k and maps 4M of linear memory. Page tables only define linear address space, not actual physical memory. The amount of extended memory that will be available to your code will be the lesser of linear and physical memory in the system. Setting a higher number of page tables will not give you any more physical memory than is available in the system, but it will give you more linear address space where physical memory can be mapped. This helps reduce fragmentation of memory when allocating extended memory blocks under VCPI. Never set this variable to 0, since the first page table maps the low megabyte of real mode memory. ) _pm_selectors - This is the total number of descriptors you want PMODE to make available to your code for allocation. The range for this variable is 0 to about 8150. The actual max number of descriptors that can exist in the global descriptor table, which is where descriptors under PMODE reside, is 8191. But some of these descriptors are used by PMODE. Also, DPMI may not be able to provide quite as many as 8000 descriptors. Each descriptor takes up 8 bytes of space in the low memory protected mode buffer. ) _pm_callbacks - This is the number of real mode callbacks you want PMODE to provide for allocation. Each callback takes up 25 bytes of space. You may set this variable to 0. ) _pm_rmstacklen - This is the size of the real mode stack, in paragraphs, that is provided for IRQ and INT redirection to real mode. Also for INT 31h functions 0300h, 0301h, and 0302h when the SS:SP field in the register structure is zero. ) _pm_rmstacks - This is the number of real mode stacks you want present in case of nested calls to real mode. There must be at least one. ) _pm_pmstacklen - This is the size of the protected mode stack, in paragraphs, to provide for protected mode procedures which handle real mode callbacks. ) _pm_pmstacks - This is the number of protected mode stacks you want present in case of nested callbacks. If your code is not going to be using real mode callbacks, you may set this variable, along with _pm_pmstacklen and _pm_callbacks, to zero. ------------------------------------------------------------------------------ 2 - Functions: -------------- PMODE duplicates a subset of DPMI protected mode functions. These functions are available ONLY in protected through INT 31h. They provide descriptor services, extended memory services, interrupt services, translation services, and some other misc things. A function is called by setting AX to the function code, setting any other registers for the function, and executing an INT 31h. Upon return, the carry flag will be clear if the function was successful. If the carry flag is set, the function failed. In this case, an error code will be placed in AX. However, DPMI 0.9 will not return error codes, just the carry flag set on errors. All other registers are preserved unless otherwise stated. 2.0 - Function 0000h - Allocate Descriptors: -------------------------------------------- Allocates one or more descriptors in the client's descriptor table. The descriptor(s) allocated must be initialized by the application with other function calls. In: AX = 0000h CX = number of descriptors to allocate Out: if successful: AX = base selector if failed: AX = error code: 8011h - descriptor unavailable 8021h - invalid value (CX = 0) (VCPI/XMS/raw only) Notes: ) If more that one descriptor was requested, the function returns a base selector referencing the first of a contiguous array of descriptors. The selector values for subsequent descriptors in the array can be calculated by adding the value returned by INT 31h function 0003h. ) The allocated descriptor(s) will be set to expand-up writeable data, with the present bit set and a base and limit of zero. The privilege level of the descriptor(s) will match the client's code segment privilege level, 2.1 - Function 0001h - Free Descriptor: --------------------------------------- Frees a descriptor. In: AX = 0001h BX = selector for the descriptor to free Out: if failed: AX = error code: 8022h - invalid selector Notes: ) Each descriptor allocated with INT 31h function 0000h must be freed individually with the function. Even if it was previously allocated as part of a contiguous array of descriptors. ) Under DPMI 1.0/VCPI/XMS/raw, any segment registers which contain the selector being freed are zeroed by this function. 2.2 - Function 0003h - Get Selector Increment Value: ---------------------------------------------------- The Allocate Descriptors function (0000h) can allocate an array of contiguous descriptors, but only return a selector for the first descriptor. The value returned by this function can be used to calculate the selectors for subsequent descriptors in the array. In: AX = 0003h Out: always successful: AX = selector increment value Notes: ) The increment value is always a power of two. 2.3 - Function 0006h - Get Segment Base Address: ------------------------------------------------ Returns the 32bit linear base address from the descriptor table for the specified segment. In: AX = 0006h BX = selector Out: if successful: CX:DX = 32bit linear base address of segment if failed: AX = error code: 8022h - invalid selector Notes: ) Client programs must use the LSL instruction to query the limit for a descriptor. 2.4 - Function 0007h - Set Segment Base Address: ------------------------------------------------ Sets the 32bit linear base address field in the descriptor for the specified segment. In: AX = 0007h BX = selector CX:DX = 32bit linear base address of segment Out: if failed: AX = error code: 8022h - invalid selector 8025h - invalid linear address (changing the base would cause the descriptor to reference a linear address range outside that allowed for DPMI clients) (DPMI 1.0 only) Notes: ) Under DPMI 1.0/VCPI/XMS/raw, any segment register which contains the selector specified in register BX will be reloaded. DPMI 0.9 may do this, but it is not guaranteed. ) I hope you have enough sense not to try to modify your current CS or SS descriptor. 2.5 - Function 0008h - Set Segment Limit: ----------------------------------------- Sets the limit field in the descriptor for the specified segment. In: AX = 0008h BX = selector CX:DX = 32bit segment limit Out: if failed: AX = error code: 8021h - invalid value (the limit is > 1M, but the low 12 bits are not set) 8022h - invalid selector 8025h - invalid linear address (changing the base would cause the descriptor to reference a linear address range outside that allowed for DPMI clients) (DPMI 1.0 only) Notes: ) The value supplied to the function in CX:DX is the byte length of the segment-1. ) Segment limits greater than or equal to 1M must be page aligned. That is, they must have the low 12 bits set. ) This function has an implicit effect on the "G" bit in the segment's descriptor. ) Client programs must use the LSL instruction to query the limit for a descriptor. ) Under DPMI 1.0/VCPI/XMS/raw, any segment register which contains the selector specified in register BX will be reloaded. DPMI 0.9 may do this, but it is not guaranteed. ) I hope you have enough sense not to try to modify your current CS or SS descriptor. 2.6 - Function 0009h - Set Descriptor Access Rights: ---------------------------------------------------- Modifies the access rights field in the descriptor for the specified segment. In: AX = 0009h BX = selector CX = access rights/type word Out: if failed: AX = error code: 8021h - invalid value (access rights/type word invalid) 8022h - invalid selector 8025h - invalid linear address (changing the base would cause the descriptor to reference a linear address range outside that allowed for DPMI clients) (DPMI 1.0 only) Notes: ) The access rights/type word passed to the function in CX has the following format: Bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | G |B/D| 0 | ? | ? | 1 | DPL | 1 |C/D|E/C|W/R| A | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ G - 0=byte granular, 1=page granular B/D - 0=default 16bit, 1=default 32bit DPL - must be equal to caller's CPL C/D - 0=data, 1=code E/C - data: 0=expand-up, 1=expand-down code: must be 0 (non-conforming) W/R - data: 0=read, 1=read/write code: must be 1 (readable) A - 0=not accessed, 1=accessed 0 - must be 0 1 - must be 1 ? - ignored ) Client programs should use the LAR instruction to examine the access rights of a descriptor. ) Under DPMI 1.0/VCPI/XMS/raw, any segment register which contains the selector specified in register BX will be reloaded. DPMI 0.9 may do this, but it is not guaranteed. ) I hope you have enough sense not to try to modify your current CS or SS descriptor. 2.7 - Function 000Ah - Create Alias Descriptor: ----------------------------------------------- Creates a new data descriptor that has the same base and limit as the specified descriptor. In: AX = 000ah BX = selector Out: if successful: AX = data selector (alias) if failed: AX = error code: 8011h - descriptor unavailable 8022h - invalid selector Notes: ) The selector supplied to the function may be either a data descriptor or a code descriptor. The alias descriptor created is always an expand-up writeable data segment. ) The descriptor alias returned by this function will not track changes to the original descriptor. 2.8 - Function 000Bh - Get Descriptor: -------------------------------------- Copies the descriptor table entry for the specified selector into an 8 byte buffer. In: AX = 000bh BX = selector ES:EDI = selector:offset of 8 byte buffer Out: if successful: buffer pointed to by ES:EDI contains descriptor if failed: AX = error code: 8022h - invalid selector 2.9 - Function 000Ch - Set Descriptor: -------------------------------------- Copies the contents of an 8 byte buffer into the descriptor for the specified selector. In: AX = 000ch BX = selector ES:EDI = selector:offset of 8 byte buffer containing descriptor Out: if failed: AX = error code: 8021h - invalid value (access rights/type word invalid) 8022h - invalid selector 8025h - invalid linear address (changing the base would cause the descriptor to reference a linear address range outside that allowed for DPMI clients) (DPMI 1.0 only) ) The descriptors access rights/type word at offset 5 within the descriptor follows the same format and restrictions as the access rights/type parameter CX to the Set Descriptor Access Rights function (0009h). ) Under DPMI 1.0/VCPI/XMS/raw, any segment register which contains the selector specified in register BX will be reloaded. DPMI 0.9 may do this, but it is not guaranteed. ) I hope you have enough sense not to try to modify your current CS or SS descriptor or the descriptor of the buffer. 2.10 - Function 000Eh - Get Multiple Descriptors: ------------------------------------------------- Copies one or more descriptor table entries into a buffer. In: AX = 000eh CX = number of descriptors to copy ES:EDI = selector:offset of a buffer in the following format: Offset Length Contents 00h 2 Selector #1 (set by client) 02h 8 Descriptor #1 (returned by host) 0ah 2 Selector #2 (set by client) 0ch 8 Descriptor #2 (returned by host) ... ... ... Out: if successful: buffer contains copies of the descriptors for the specified selectors if failed: AX = error code: 8022h - invalid selector CX = number of descriptors successfully copied Notes: ) If an error occurs because of an invalid selector or descriptor, the function returns the number of descriptors which were successfully copied in CX. All of the descriptors which were copied prior to the one that failed are valid. ) This function is not present under DPMI 0.9. 2.11 - Function 000Fh - Set Multiple Descriptors: ------------------------------------------------- Copies one or more descriptors from a client buffer into the descriptor table. In: AX = 000fh CX = number of descriptors to copy ES:EDI = selector:offset of a buffer in the following format: Offset Length Contents 00h 2 Selector #1 02h 8 Descriptor #1 0ah 2 Selector #2 0ch 8 Descriptor #2 ... ... ... Out: if failed: AX = error code: 8021h - invalid value (access rights/type word invalid) 8022h - invalid selector 8025h - invalid linear address (changing the base would cause the descriptor to reference a linear address range outside that allowed for DPMI clients) (DPMI 1.0 only) CX = number of descriptors successfully copied Notes: ) If an error occurs because of an invalid selector or descriptor, the function returns the number of descriptors which were successfully copied in CX. All of the descriptors which were copied prior to the one that failed are valid. ) The descriptors access rights/type word at offset 5 within the descriptor follows the same format and restrictions as the access rights/type parameter CX to the Set Descriptor Access Rights (0009h) function. ) Under DPMI 1.0/VCPI/XMS/raw, any segment register which contains a selector specified in the data structure will be reloaded. DPMI 0.9 may do this, but it is not guaranteed. ) I hope you have enough sense not to try to modify your current CS or SS descriptor or the descriptor of the buffer. ) This function is not present under DPMI 0.9. 2.12 - Function 0200h - Get Real Mode Interrupt Vector: ------------------------------------------------------- Returns the real mode segment:offset for the specified interrupt vector. In: AX = 0200h BL = interrupt number Out: always successful: CX:DX = segment:offset of real mode interrupt handler Notes: ) The value returned in CX is a real mode segment address, not a protected mode selector. 2.13 - Function 0201h - Set Real Mode Interrupt Vector: ------------------------------------------------------- Sets the real mode segment:offset for the specified interrupt vector. In: AX = 0201h BL = interrupt number CX:DX = segment:offset of real mode interrupt handler Notes: ) The value passed in CX must be a real mode segment address, not a protected mode selector. Consequently, the interrupt handler must either reside in DOS memory (below the 1M boundary) or the client must allocate a real mode callback address. 2.14 - Function 0204h - Get Protected Mode Interrupt Vector: ------------------------------------------------------------ Returns the address of the current protected mode interrupt handler for the specified interrupt. In: AX = 0204h BL = interrupt number Out: always successful: CX:EDX = selector:offset of protected mode interrupt handler Notes: ) The value returned in CX is a valid protected mode selector, not a real mode segment address. 2.15 - Function 0205h - Set Protected Mode Interrupt Vector: ------------------------------------------------------------ Sets the address of the protected mode interrupt handler for the specified interrupt. In: AX = 0205h BL = interrupt number CX:EDX = selector offset of protected mode interrupt handler Out: if failed: AX = error code: 8022h - invalid selector Notes: ) The value passed in CX must be a valid protected mode selector, not a real mode segment address. 2.16 - Function 0300h - Simulate Real Mode Interrupt: ----------------------------------------------------- Simulates an interrupt in real mode. The function transfers control to the address specified by the real mode interrupt vector. The real mode handler must return by executing an IRET. In: AX = 0300h BL = interrupt number BH = must be 0 CX = number of words to copy from the protected mode stack to the real mode stack ES:EDI = selector:offset of real mode register data structure in the following format: Offset Length Contents 00h 4 EDI 04h 4 ESI 08h 4 EBP 0ch 4 reserved, ignored 10h 4 EBX 14h 4 EDX 18h 4 ECX 1ch 4 EAX 20h 2 CPU status flags 22h 2 ES 24h 2 DS 26h 2 FS 28h 2 GS 2ah 2 IP (reserved, ignored) 2ch 2 CS (reserved, ignored) 2eh 2 SP 30h 2 SS Out: if successful: ES:EDI = selector offset of modified real mode register data structure if failed: AX = error code: 8012h - linear memory unavailable (stack) 8013h - physical memory unavailable (stack) (DPMI 1.0 only) 8014h - backing store unavailable (stack) (DPMI 1.0 only) 8021h - invalid value (CX too large) (DPMI 1.0 only) Notes: ) The CS:IP in the real mode register data structure is ignored by this function. The appropriate interrupt handler will be called based on the value passed in BL. ) If the SS:SP fields in the real mode register data structure are zero, a real mode stack will be provided by the host. Otherwise the real mode SS:SP will be set to the specified values before the interrupt handler is called. ) The flags specified in the real mode register data structure will be put on the real mode interrupt handler's IRET frame. The interrupt handler will be called with the interrupt and trace flags clear. ) Values placed in the segment register positions of the data structure must be valid for real mode. That is, the values must be paragraph addresses, not protected mode selectors. ) The target real mode handler 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 must return on the same stack that it was called on and must return with an IRET. ) When this function returns, the real mode register data structure will contain the values that were returned by the real mode interrupt handler. The CS:IP and SS:SP values will be unmodified in the data structure. ) It is the caller's responsibility to remove any parameters that were pushed on the protected mode stack. 2.17 - Function 0301h - Call Real Mode Procedure With Far Return Frame: ----------------------------------------------------------------------- Simulates a FAR CALL to a real mode procedure. The called procedure must return by executing a RETF instruction. In: AX = 0301h BH = must be 0 CX = number of words to copy from the protected mode stack to the real mode stack ES:EDI = selector:offset of real mode register data structure in the following format: Offset Length Contents 00h 4 EDI 04h 4 ESI 08h 4 EBP 0ch 4 reserved, ignored 10h 4 EBX 14h 4 EDX 18h 4 ECX 1ch 4 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 Out: if successful: ES:EDI = selector offset of modified real mode register data structure if failed: AX = error code: 8012h - linear memory unavailable (stack) 8013h - physical memory unavailable (stack) (DPMI 1.0 only) 8014h - backing store unavailable (stack) (DPMI 1.0 only) 8021h - invalid value (CX too large) (DPMI 1.0 only) Notes: ) The CS:IP in the real mode register data structure specifies the address of the real mode procedure to call. ) If the SS:SP fields in the real mode register data structure are zero, a real mode stack will be provided by the host. Otherwise the real mode SS:SP will be set to the specified values before the procedure is called. ) Values placed in the segment register positions of the data structure must be valid for real mode. That is, the values must be paragraph addresses, not protected mode selectors. ) The target real mode procedure 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 must return on the same stack that it was called on and must return with a RETF and should not clear the stack of any parameters that were passed to it on the stack. ) When this function returns, the real mode register data structure will contain the values that were returned by the real mode procedure. The CS:IP and SS:SP values will be unmodified in the data structure. ) It is the caller's responsibility to remove any parameters that were pushed on the protected mode stack. 2.18 - Function 0302h - Call Real Mode Procedure With IRET Frame: ----------------------------------------------------------------- Simulates a FAR CALL with flags pushed on the stack to a real mode procedure. The real mode procedure must return by executing an IRET instruction or a RETF 2. In: AX = 0301h BH = must be 0 CX = number of words to copy from the protected mode stack to the real mode stack ES:EDI = selector:offset of real mode register data structure in the following format: Offset Length Contents 00h 4 EDI 04h 4 ESI 08h 4 EBP 0ch 4 reserved, ignored 10h 4 EBX 14h 4 EDX 18h 4 ECX 1ch 4 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 Out: if successful: ES:EDI = selector offset of modified real mode register data structure if failed: AX = error code: 8012h - linear memory unavailable (stack) 8013h - physical memory unavailable (stack) (DPMI 1.0 only) 8014h - backing store unavailable (stack) (DPMI 1.0 only) 8021h - invalid value (CX too large) (DPMI 1.0 only) Notes: ) The CS:IP in the real mode register data structure specifies the address of the real mode procedure to call. ) If the SS:SP fields in the real mode register data structure are zero, a real mode stack will be provided by the host. Otherwise the real mode SS:SP will be set to the specified values before the procedure is called. ) The flags specified in the real mode register data structure will be put on the real mode procedure's IRET frame. The procedure will be called with the interrupt and trace flags clear. ) Values placed in the segment register positions of the data structure must be valid for real mode. That is, the values must be paragraph addresses, not protected mode selectors. ) The target real mode procedure 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 must return on the same stack that it was called on and must return with an IRET or discard the flags from the stack with a RETF 2 and should not clear the stack of any parameters that were passed to it on the stack. ) When this function returns, the real mode register data structure will contain the values that were returned by the real mode procedure. The CS:IP and SS:SP values will be unmodified in the data structure. ) It is the caller's responsibility to remove any parameters that were pushed on the protected mode stack. 2.19 - Function 0303h - Allocate Real Mode Callback Address: ------------------------------------------------------------ Returns a unique real mode segment:offset, known as a "real mode callback", that will transfer control from real mode to a protected mode procedure. Callback addresses obtained with this function can be passed by a protected mode program to a real mode application, interrupt handler, device driver, TSR, etc... so that the real mode program can call procedures within the protected mode program. In: AX = 0303h DS:ESI = selector:offset of protected mode procedure to call ES:EDI = selector:offset of 32h byte buffer for real mode register data structure to be used when calling the callback routine. Out: if successful: CX:DX = segment:offset of real mode callback if failed: AX = error code: 8015h - callback unavailable Notes: ) A descriptor may be allocated for each callback to hold the real mode SS descriptor. Real mode callbacks are a limited system resource. A client should release a callback that it is no longer using. 2.20 - Function 0304h - Free Real Mode Callback Address: -------------------------------------------------------- Releases a real mode callback address that was previously allocated with the Allocate Real Mode Callback Address function (0303h). In: AX = 0304h CX:DX = segment:offset of real mode callback to be freed Out: if failed: AX = error code: 8024h - invalid callback address Notes: ) Real mode callbacks are a limited system resource. A client should release any callback that it is no longer using. 2.21 - Function 0305h - Get State Save/Restore Addresses: --------------------------------------------------------- Returns the address of two procedures used to save and restore the state of the current task's registers in the mode (protected or real) which is not currently executing. In: AX = 0305h Out: always successful: AX = size of buffer in bytes required to save state BX:CX = segment:offset of real mode routine used to save/restore state SI:EDI = selector:offset of protected mode routine used to save/restore state Notes: ) The real mode segment:offset returned by this function should be called only in real mode to save/restore the state of the protected mode registers. The protected mode selector:offset returned by this function should be called only in protected mode to save/restore the state of the real mode registers. ) Both of the state save/restore procedures are entered by a FAR CALL with the following parameters: AL = 0 to save state = 1 to restore state ES:(E)DI = (selector or segment):offset of state buffer The state buffer must be at least as large as the value returned in AX by INT 31h function 0305h. The state save/restore procedures do not modify any registers. DI must be used for the buffer offset in real mode, EDI must be used in protected mode. ) Some DPMI hosts and VCPI/XMS/raw will not require the state to be saved, indicating this by returning a buffer size of zero in AX. In such cases, that addresses returned by this function can still be called, although they will simply return without performing any useful function. ) Clients do not need to call the state save/restore procedures before using INT 31h function 0300h, 0301h, or 0302h. The state save/restore procedures are provided for clients that use the raw mode switch services only. 2.22 - Function 0306h - Get Raw Mode Switch Addresses: ------------------------------------------------------ Returns addresses that can be called for low level mode switching. In: AX = 0306h Out: always successful: BX:CX = segment:offset of real to protected mode switch procedure SI:EDI = selector:offset of protected to real mode switch procedure Notes: ) The real mode segment:offset returned by this function should be called only in real mode to switch to protected mode. The protected mode selector:offset returned by this function should be called only in protected mode to switch to real mode. ) The mode switch procedures are entered by a FAR JMP to the appropriate address with the following parameters: AX = new DS CX = new ES DX = new SS (E)BX = new (E)SP SI = new CS (E)DI = new (E)IP The processor is placed into the desired mode, and the DS, ES, SS, (E)SP, CS, and (E)IP registers are updated with the specific values. In other words, execution of the client continues in the requested mode at the address provided in registers SI:(E)DI. The values specified to be placed into the segment registers must be appropriate for the destination mode. That is, segment addresses for real mode, and selectors for protected mode. The values in EAX, EBX, ECX, EDX, ESI, and EDI after the mode switch are undefined. EBP will be preserved across the mode switch call so it can be used as a pointer. FS and GS will contain zero after the mode switch. If interrupts are disabled when the mode switch procedure is invoked, they will not be re-enabled by the host (even temporarily). ) It is up to the client to save and restore the state of the task when using this function to switch modes. This requires the state save/restore procedures whose addresses can be obtained with INT 31h function 0305h. 2.23 - Function 0400h - Get Version: ------------------------------------ Returns the version of the DPMI Specification implemented by the DPMI host. The client can use this information to determine what functions are available. In: AX = 0400h Out: always successful: AH = DPMI major version as a binary number (VCPI/XMS/raw returns 1) AL = DPMI minor version as a binary number (VCPI/XMS/raw returns 0) BX = flags: Bits Significance 0 0 = host is 16bit (PMODE never runs under one of these) 1 = host is 32bit 1 0 = CPU returned to V86 mode for reflected interrupts 1 = CPU returned to real mode for reflected interrupts 2 0 = virtual memory not supported 1 = virtual memory supported 3-15 reserved CL = processor type: 03h = 80386 04h = 80486 05h = 80586 06h-ffh = reserved DH = current value of master PIC base interrupt (low 8 IRQs) DL = current value of slave PIC base interrupt (high 8 IRQs) Notes: ) The major and minor version numbers are binary, not BCD. So a DPMI 0.9 implementation would return AH as 0 and AL as 5ah (90). 2.24 - Function 0500h - Get Free Memory Information: ---------------------------------------------------- Returns information about the amount of available memory. Since DPMI clients could be running in a multitasking environment, the information returned by this function should be considered advisory. In: AX = 0500h ES:EDI = selector:offset of 48 byte buffer Out: if successful: buffer is filled with the following information: if failed: AX = error code: 8010h - internal resource unavailable (stack) (XMS only) Offset Length Contents 00h 4 Largest available free block in bytes 04h 2ch Other fields only supplied by DPMI Notes: ) Only the first field of the structure is guaranteed to contain a valid value. Any fields that are not supported by the host will be set to -1 (0ffffffffh) to indicate that the information is not available. 2.25 - Function 0501h - Allocate Memory Block: ---------------------------------------------- Allocates a block of extended memory. In: AX = 0501h BX:CX = size of block in bytes (must be non-zero) Out: if successful: BX:CX = linear address of allocated memory block SI:DI = memory block handle (used to resize and free block) if failed: AX = error code: 8010h - internal resource unavailable (stack) (XMS only) 8012h - linear memory unavailable (DPMI 1.0/VCPI only) 8013h - physical memory unavailable 8014h - backing store unavailable (DPMI 1.0 only) 8016h - handle unavailable (DPMI 1.0/XMS only) 8021h - invalid value (BX:CX = 0) Notes: ) The allocated block is guaranteed to have at least paragraph alignment. ) This function does not allocate any descriptors for the memory block. It is the responsibility of the client to allocate and initialize any descriptors needed to access the memory with additional function calls. ) The allocations by this function could be paragraph, kilobyte, or page aligned. That is, the value you request could be rounded up to the next paragraph, kilobyte, or page value. 2.26 - Function 0502h - Free Memory Block: ------------------------------------------ Frees a memory block previously allocated with the Allocate Memory Block function (0501h). In: AX = 0502h SI:DI = memory block handle Out: if failed: AX = error code: 8010h - internal resource unavailable (stack) (XMS only) 8023h - invalid handle Notes: ) No descriptors are freed by this call. It is the client's responsibility to free any descriptors that it previously allocated to map the memory block. Descriptors should be freed before memory blocks. 2.27 - Function 0503h - Resize Memory Block: -------------------------------------------- Changes the size of a memory block previously allocated with the Allocate Memory Block function (0501h). In: AX = 0503h BX:CX = new size of block in bytes (must be non-zero) SI:DI = memory block handle Out: BX:CX = new linear address of memory block SI:DI = new memory block handle if failed: AX = error code: 8010h - internal resource unavailable (stack) (XMS only) 8012h - linear memory unavailable (DPMI 1.0/VCPI only) 8013h - physical memory unavailable 8014h - backing store unavailable (DPMI 1.0 only) 8016h - handle unavailable (DPMI 1.0/XMS only) 8021h - invalid value (BX:CX = 0) 8023h - invalid handle Notes: ) After this function returns successfully, the previous handle for the memory block is invalid and should not be used anymore. ) It is the client's responsibility to update any descriptors that map the memory block with the new linear address after resizing the block. 2.28 - Function 050Ah - Get Memory Block Size and Base: ------------------------------------------------------- Returns the size and base of a memory block that was previously allocated with the Allocate Memory Block function (0501h). In: AX = 050ah SI:DI = memory block handle Out: if successful: BX:CX = linear address of memory block SI:DI = size of memory block (bytes) if failed: AX = error code: 8010h - internal resource unavailable (stack) (XMS only) 8023h - invalid handle Notes: ) This function is not present under DPMI 0.9. 2.29 - Function 0900h - Get and Disable Virtual Interrupt State: ---------------------------------------------------------------- Disables the virtual interrupt flag and returns the previous state of it. In: AX = 0900h Out: always successful: AL = 0 if virtual interrupts were previously disabled AL = 1 if virtual interrupts were previously enabled Notes: ) AH is not changed by this function. Therefore the previous state can be restored by simply executing another INT 31h. ) A client that does not need to know the prior interrupt state can execute the CLI instruction rather than calling this function. The instruction may be trapped by a DPMI host and should be assumed to be very slow. 2.30 - Function 0901h - Get and Enable Virtual Interrupt State: --------------------------------------------------------------- Enables the virtual interrupt flag and returns the previous state of it. In: AX = 0901h Out: always successful: AL = 0 if virtual interrupts were previously disabled AL = 1 if virtual interrupts were previously enabled Notes: ) AH is not changed by this function. Therefore the previous state can be retstored by simply executing another INT 31h. ) A client that does not need to know the prior interrupt state can execute the STI instruction rather than calling this function. The instruction may be trapped by a DPMI host and should be assumed to be very slow. 2.31 - Function 0902h - Get Virtual Interrupt State: ---------------------------------------------------- Returns the current state of the virtual interrupt flag. In: AX = 0902h Out: always successful: AL = 0 if virtual interrupts are disabled AL = 1 if virtual interrupts are enabled Notes: ) This function should be used in preference to the PUSHF instruction to examine the interrupt flag, because the PUSHF instruction returns the physical interrupt flag rather than the virtualized interrupt flag. On some DPMI hosts, the physical interrupt flag will always be enabled, even when the hardware interrupts are not being passed through to the client. 2.32 - Function FFFFh - Special Fluffy Magical Function: -------------------------------------------------------- Does special fluffy magical things. In: AX = ffffh Out: if successful: AX = 1234h (normal fluffy magical number) BX = 56ijh (special fluffy magical number) CX = 89abh (nothing really special) DX = cdefh (again, just a boring number) EX = return value from beyond FX = an acronym for 'effects' GX = huh? HX = size of special fluffy magical buffer if failed: AX = error code: 8001h - get a grip on reality! Notes: ) The special fluffy magical buffer must not exceed the special fluffy magical constant in size, which is defined in the special fluffy magical place. ) Well, maybe it exists... Maybe if you try calling it many many many many many many many many times in a row it will succeed. ------------------------------------------------------------------------------ 3 - Miscellenaous: ------------------ Some final things about PMODE, including some low level tech info if you are just curious. 3.0 - Updates: -------------- Here are the changes from any previous versions of PMODE 3.0. PMODE 3.0: ) First release of PMODE 3.0. PMODE 3.01: ) Slightly optimized VCPI/XMS/raw mode switching routines. ) Fixed bug with INT 31h translation functions 0300h, 0301h, and 0302h not returning correct flags. ) Fixed bug with software INT redirection calls clearing the interrupt flag. 3.1 - Glossary: --------------- Bottom up allocation - A method of extended memory allocation which relies on control blocks specifying the start of free extended memory. This is the VDISK extended memory allocation scheme. Client - A program which uses DPMI INT 2fh and INT 31h services to run in protected mode. CPL, Current Privilege Level - The privilege level of the currently executing code. Descriptor - An 8 byte structure which defines a segment type, its base address and limit, and the type of access allowed to it. DPL, Descriptor Privilege Level - The privilege level of a descriptor. Descriptor protection is based on certain rules of the CPL and DPL of a descriptor that code may want to access. DPMI, DOS Protected Mode Interface - An interface for protected mode DOS programs to manage memory, interrupts, exceptions, debugging registers, and coprocessor emulation in a well behaved manner which allows them to coexist with other protected mode programs and operating systems. Exception - An interrupt that occurs because of some violation of protection rules. Extended memory - Memory which lies above the 1M boundary and can only be addressed in protected mode. GDT, Global Descriptor Table - A descriptor table which can contain many types of descriptors besides the regular code and data descriptors. This can include TSS descriptors and LDT descriptors. Host - A program which provides DPMI protected mode services. IDT, Interrupt Descriptor Table - A descriptor table which contains the gate descriptors to the handlers for the 256 interrupts. LDT, Local Descriptor Table - A descriptor table which usually contains all the descriptors of a particular task. Each task in a 386 multitasking system can have its own LDT whereas there is only one GDT for the entire system. Linear memory - Address space rather than actual physical RAM of ROM. Page - A 4k chunk of memory. The 80386 can map any 4k chunk of physical memory to any linear address. There are also some protection rules that can apply to pages, such as read-only, or privilege level checking on access. Page Directory - Sort of a master page table which maps page tables instead of pages. Page Table - A 4k table containing 1024 entries for pages. Each page table maps 4 megabytes of linear memory to physical memory. Physical memory - The actual physical memory present in a system. Privilege level - A numeric value representing the freedom of system access and how much protection applies to code. This value ranges from 0 (most free (godlike)) to 3 (least free (lowly slave)). RPL, Requestor Privilege Level - The privilege level code requests for a specific selector access. It is contained in the low two bits of the selector. Segment - A specific linear chunk of memory. In real mode, segments are limited to the first megabyte of memory and are always 64k in length. In protected mode, a segment can start anywhere in the entire 4 gigabyte address space of the 80386 and can be that long. Selector - An index in protected mode into a descriptor table. Selectors are used in place of segments in protected mode in the segment registers. Top down allocation - A method of extended memory management which relies on the BIOS INT 15h function 88h. A program which needs to allocate extended memory will hook INT 15h and return a smaller extended memory size. The program is then free to use the memory between the previous top of extended memory and what it returns as the top of extended memory without worrying about other programs overwriting its extended memory. TSS, Task State Segment - A special memory structure used in task switching and protection. V86 mode - Actually it is protected mode, running at a privilege level of 3. Segment registers are used in the same manner as in real mode, with segment addresses rather than selectors. The advantage is that paging and other protected tasks can be active, which includes other V86 mode tasks. The disadvantage is that it is slower than real mode. VCPI, Virtual Control Program Interface - The predecessor to DPMI. VCPI extends the EMS interface to allow DOS programs to run in protected mode in the presence of EMS emulators or other 80386 control programs. Virtual memory - Extra memory beyond the actual physical memory present in a system. There is not really any more physical memory in the system, but an operating system or DPMI host can give that illusion by swapping the contents of physical memory to and from a disk and mapping that physical memory to different linear addresses. XMS, eXtended Memory Specification - A handle based extended memory management interface. 3.2 - Differences between modes: -------------------------------- Some differences between DPMI, VCPI, XMS, and raw protected mode: ) DPMI: Client descriptors reside in a LDT. VCPI/XMS/raw: Client descriptors reside in the GDT. ) DPMI: Code runs at CPL 3. (I don't know of any DPMI that doesn't) VCPI/XMS/raw: Code runs at CPL 0. (Much faster) ) DPMI/VCPI: Real mode calls are executed in V86 mode. ) XMS/raw: Real mode calls are executed in real mode. (Much faster) ) DPMI/VCPI: Paging is enabled. XMS/raw: Paging is disabled. (Not really very much faster, but what the hell) ) DPMI: IRET(D) and POPF(D) may not affect the interrupt flag. PUSHF(D) may not store the interrupt flag. VCPI/XMS/raw: IRET(D) and POPF(D) affect the real interrupt flag. PUSHF(D) stores the real interrupt flag. ) DPMI: INTs 1Ch, 23h, and 24h from real mode are sent to protected mode first. This means if a protected mode handler is installed for these interrupts, it will get control. ) VCPI/XMS/raw: DPMI dox are not too clear on the method these interrupts are called in. Callbacks I would guess. But I am too lazy to investigate. And frankly, I don't give a shit. So the VCPI/XMS/raw kernel does not support this. ) DPMI: Seperates exceptions from other low interrupts and IRQs. That is, an exception 8 would never erroneously go to the handler for IRQ 0. ) VCPI/XMS/raw: Hey, exceptions are bad, you should not be getting them in the first place. Reprogramming the interrupt controllers or running the client code at a lower privilege is not worth the slowdown for me. ) DPMI: Theoretically, DPMI may refuse a request to set a segment limit to 4G. I have not yet found a DPMI that will refuse this. They all do protection at the paging level. And a high limit is necessary for flat mode and negative offsets. ) VCPI/XMS/raw: There is no protection, no segment limit or base address will ever be denied. ) DPMI: Memory allocation is almost always page granular, but not necessarily. VCPI: Memory allocation is page granular (4k chunks). XMS: Memory allocation is kilobyte granular. raw: Memory allocation is paragraph granular. ) DPMI/VCPI: Low memory linear addresses may or may not be the actual physical addresses. Extended memory addresses are almost sure not to be. XMS/raw: All linear addresses are physical addresses. 3.3 - Notes: ------------ Here are some misc and low level technical details about PMODE and some points I want to emphasize. Some of them may seem very obscure, with no real need to list. But for the sake of thorough documentation, they are here: ) In protected mode, ESP must always be the stack pointer. Meaning, even if using a 16bit stack segment, the high word of ESP MUST be 0. ) When calling the raw mode switching routine to switch into protected mode, you must supply the full ESP and EIP. Even if the stack or code segments being switched to are 16bit. ) If the call to init protected mode was from a segment other than PMODE_TEXT under DPMI, and a descriptor can not be allocated for that code segment, immediate termination results. But this should never happen. A DPMI host that can not supply even one descriptor to its protected mode clients defies logic. But for the sake of covering all possible screw-ups, this condition is checked for. ) DPMI 1.0/VCPI/XMS/raw will reload any segment registers for which the descriptor is changed through an INT 31 function. DPMI 0.9 does not. (Actually it does, through pushing and then popping any segment registers it uses. But it is not guaranteed to reload any or all of them). ) DPMI 1.0/VCPI/XMS/raw will zero any segment registers freed with INT 31 function 0001h. DPMI 0.9 may or may not. ) I dont know about DPMI with respect to reloading or freeing a selector which is currently loaded into SS, but the VCPI/XMS/raw system will not reload or zero SS. You should not be modifying your stack or code descriptor as you are using it. This could be bad, even if the INT 31 were handled through a task gate (which would be slow). ) Remember that DPMI 0.9 does not return error codes as DPMI 1.0/VCPI/XMS/raw do. ) Reasons for calls to real mode executing in actual real mode rather than V86 mode (XMS/raw): ) V86 call system can not be used under VCPI very successfully. (Yes, VCPI runs its real mode in V86. But VCPI MUST be in control in this case.) ) Real mode runs faster than V86 mode. ) INT 15h and XMS extended memory functions will work. ) Other protected mode programs that run DOS functions in real mode will work. ) It is faster to switch between protected/real mode than protected/V86. ) Use LAR to find out the current CPL for setting descriptor access right. ) You must always set the present bit when setting descriptor access rights. ) Under the VCPI/XMS/raw system, the AVL bit of descriptors is used to keep track of free and used descriptors. The value you pass for this bit when setting descriptor access rights will be ignored. ) When switching modes using the raw switching routines, make sure there is some space on both stacks (real and protected). Specific DPMI requirements may vary, but 64 bytes is enough for VCPI/XMS/raw. ) In protected mode, remember to use IRETD, not IRET. When DPMI documentation refers to using IRET, it is actually referring to the 32bit version of the instruction under 32bit systems, which is IRETD. ) Remember that free memory information is just advisory. A TSR or another task in a multitasking system might grab some memory in between a call to INT 31 function 0500h and INT 31 function 0501h, even if you disable interrupts. ) The _pm_? variables are not checked for validity. So don't set them outside reasonable bounds. For example, don't ask for 20h real mode stacks of size 1000h paragraphs. This is two megabytes of real mode stack space. This is way too much, considering that all real mode available low memory encompasses 640k. ) You should allocate memory in large blocks. Memory space is subject to fragmentation. Although you can help the situation a little under VCPI by setting a high number of page tables. This will not increase the physical memory available, but it will increase the address space available to put that memory into linear chunks. ) I tried to balance clean, well designed code, with size and speed. True, some things are not as absolutely optimal as they can be. But the source is right here. Very clean and commented. If you truly need those last ounces of speed, feel free to modify it (this does not free you from the obligation of crediting me for it). ) I can not control what DPMI does. But under XMS/raw, code in protected mode runs at the fastest speed possible. That is, there is no privilege checking to get in the way. No exception will rip control away from sensitive instructions. Not even paging, with its memory references every time a page table entry is not found in the TLB. Under VCPI, all of this applies except the paging. Which really isn't that bad. ) Under VCPI, free memory information is validated in function 0500h by actually allocating that memory and releasing it before passing the information to your program. This because under some multitasking systems, the VCPI function for getting the memory available may return information for the whole system. While the multitasking system may impose allocation limits on the specific task your code is part of. ) Under VCPI/XMS/raw, INT 31 function 0500h will return only the first field of the buffer set. All the other fields will be set to 0ffffffffh. ) Under VCPI/XMS/raw, an allocate descriptor INT 31 function 0000h called with CX = 0 will return error 8021h. DPMI dox dont state this, and I dont know if DPMI returns an error on CX = 0. ) INT 31 functions 0300h, 0301h, and 0302h will always inform you if there is not enough real mode stack space. But and IRQ or INT redirection can not. In this case, the PC speaker will be turned on, and the machine will be hung. This is better than allowing it to overrun data below it with unpredictable results. And hey, I dont need no stinkin debug code cluttering up my nice and pretty extender. I just need it to tell me in case something like this happenes. If you want debug code, go and hack it in yourself. ) DPMI dox dont state this, but the alias descriptor INT 31 function 000ah creates is always an expand-up and writeable data descriptor. No matter what type the source descriptor is. ) You should limit yourself to allocating as few individual memory blocks as possible. Under XMS, there is usually a strict limit on how many blocks can be allocated (normally 32). ) DPMI dox state that the field between EBP and EBX should be zero upon an INT 31 function 0300h, but is ignored by functions 0301h and 0302h. That is just a stupid typo, the field will be ignored by function 0300h. ) Under XMS an extra 15 bytes will be allocated for possible aligning of the XMS memory block on a paragraph. Though an XMS block will probably already be aligned on at least a paragraph boundary, this is not defined in the XMS standard. And to keep the possibility of problems at nil, this is done. ) Be aware that memory allocation functions under XMS use real mode calls and real mode stack space defined with _pm_rmstacklen and _pm_rmstacks. If there is not enough stack space for the call to the real mode XMS driver, error code 8010h (resource unavailable) will be returned. ) If an XMS memory lock fails, which is used in memory allocation functions, error 8010h will be returned. A memory lock failure is not due to memory not being available. But rather, some internal XMS crap. But it should never happen anyway. ) The raw system checks both INT 15h and the VDISK low to high extended memory allocation scheme to get its available extended memory area. ) The raw system allocates extended memory on an as-needed basis from the top down. INT 15h function 88h is hooked and the total amount of memory allocated using the kernel function 0501h is subtracted from the amount of memory returned from the previous INT 15h handler. This is so that you can execute other protected mode programs from within your programs and they will have extended memory available (if you left any). ) A protected mode IRQ handler or real mode callback must return on the same stack it was called with. ) A real mode routine called with functions 0300h, 0301h, or 0302h must return on the same stack it was called with. ) You should make no assumptions about the low memory protected mode data area needed by PMODE. It can range from very small to very large. And if a DPMI host is present, it is unpredictable. ) Make sure you do not access, read or write, extended memory outside the blocks you allocate. Even if there is no physical memory there, you will probably get exceptions under DPMI/VCPI. ) When setting descriptor access rights, remember that the B bit of stack descriptors determines whether PUSHes and POPs use SP (B=0) or ESP (B=1). ) If you enable interrupts in a callback, you MUST assume DS is no longer valid. Even if you are sure your callback will not be re-entered. This is because PMODE uses the same DS selector for ALL real mode callbacks. ) The reserved field between EBP and EBX in the register structure used during a callback is used by PMODE to preserve the high word of ESP for real mode. ) You should assume the INT 31h extended memory functions to be slow. Especially under VCPI, where a call has to be made to the VCPI server for each 4k of memory allocated, freed, and two calls for each 4k verified. ) Under PMODE, software INT redirection calls only pass back the carry, parity, aux, zero, sign, and overflow flags from the real mode interrupt handler. ) When using the raw mode switching routines, keep in mind that they use some stack space on both the real mode and protected mode stacks. Never set the destination stack to the same location in memory as the stack being switched from. ------------------------------------------------------------------------------ 3.4 - Final word: ----------------- I like this latest PMODE a lot. For an extender, it is clean, solid, fast, and small. It is not limited to assembly code. It does not need control at startup, but may be initialized at any time. It can be turned into basically any type of extender. It can work with high level languages. You will probably also like it because it is free, and the source code is provided. The DPMI interface assures portability and long life. Enjoy protected mode, and remember the credits. If you really really really really must contact me, try tran@phantom.com. I do not guarantee a response, so don't get pissed off if you don't get one. L8r... Tran...