


                                        
      Ŀ
                                                                        
                                                                        
                                                                        
                                X - 3 2 V M                             
                                                                        
                                                                        
                                                                        
                                                                        
                               Users Manual                             
                                                                        
                                                                        
                            Copyright (c) 1992                          
                                                                        
                                                                        
                                                                        
      
                                        


                                Table Of Contents

   1 Introduction  . . . . . . . . . . . . . . . . . . . . . . . . . . . .  4

   2 UpShot - Online Help  . . . . . . . . . . . . . . . . . . . . . . . .  9

   3 Memory Management . . . . . . . . . . . . . . . . . . . . . . . . . . 12

   4 The X-32 Operating System Interface . . . . . . . . . . . . . . . . . 21

   5 X-32 System Calls . . . . . . . . . . . . . . . . . . . . . . . . . . 35
     5.1 Interrupt 33H Support for Mouse Drivers . . . . . . . . . . . . . 56
     5.2 Other Interrupts Executed Under X-32  . . . . . . . . . . . . . . 57

   6 Writing Interrupt Handlers  . . . . . . . . . . . . . . . . . . . . . 60
     6.1 Special Constraints for Some Interrupts . . . . . . . . . . . . . 64

   7 Hooking Exceptions  . . . . . . . . . . . . . . . . . . . . . . . . . 66


   8 Accessing Memory Below 1 Megabyte . . . . . . . . . . . . . . . . . . 69

   9 Physical Addresses Above 1 Mbyte  . . . . . . . . . . . . . . . . . . 71

   10 Executing Real Mode Code . . . . . . . . . . . . . . . . . . . . . . 73

   11 Compatibility With Other Software  . . . . . . . . . . . . . . . . . 76

   12 Hardware Compatibility . . . . . . . . . . . . . . . . . . . . . . . 78

   13 Miscellaneous Functions and Global . . . . . . . . . . . . . . . . . 80
     13.1 Variables Special functions  . . . . . . . . . . . . . . . . . . 80


                                     -    1 -

     13.2 Global variables . . . . . . . . . . . . . . . . . . . . . . . . 89

   14 Using Virtual Memory . . . . . . . . . . . . . . . . . . . . . . . . 92

   15 Memory Mapped File I/O . . . . . . . . . . . . . . . . . . . . . . . 99

   16 Writing and Using X-32 TSRs  . . . . . . . . . . . . . . . . . . .  105


   17 RUNOS2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  107

   18 Error Messages . . . . . . . . . . . . . . . . . . . . . . . . . .  118

   Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  126


 
                            Chapter 1 - Introduction
 


   The absence of a true 32-bit operating system has left most DOS 
   programmers without adequate support for the 32-bit processors available 
   in modern personal computers. New operating systems are being developed 
   which will provide better support for the increased capabilities of these 
   new processors. However, there will continue to be a large number of 
   users who desire to continue using 16-bit DOS operating systems on 32-bit 
   machines. 

   The X-32 DOS extender provides a simple method of extending 16-bit DOS to 
   support 32-bit applications on computers with 80386 processors and above. 
   X-32 is linked with the 32-bit application and serves as an extension to 
   DOS. There are no other run time files required, everything is contained 
   in the resultant executable. The license included with X-32 allows these 
   executables to be distributed freely without royalty payments or any 

   other fees payable to FlashTek, Inc. 

   All function calls from the application are filtered through X-32 which 
   converts each 32-bit function call from the application to one or more 
   16-bit calls which are compatible with the 16-bit DOS operating system. 
   The advantages include increased available memory, faster code and 
   elimination of the need for far pointers. X-32 is much more compact than 
   most DOS extenders. For small programs, X-32 executables will be only 
   about 15 Kbytes larger than the equivalent large memory model real mode 
   programs. For larger programs, the X-32 executable size is often smaller 
   than the equivalent large memory model 16-bit programs due to the 
   elimination of multiple instructions required to use far pointers. 

   X-32 allocates extended memory from XMS, VCPI, DPMI, INT 15 and VDISK 
   sources. The application can then allocate memory with calls to X-32 and 
   does not need to be aware of the presence or absence of any of the 
   previously mentioned memory management systems. The goal has been to 
   provide programmers with an operating system interface which is 


                                     -    2 -

   essentially a 32-bit DOS. X-32 applications are compatible with the VCPI 
   specification, which enables it to operate with most modern EMS emulaters 

   such as 386MAX and EMM386. It is compatible with the DPMI specification, 
   which means X-32 applications will operate with Windows 3.0 and above in 
   all three modes and also DOS windows within OS/2 2.0. The maximum 
   physical memory X-32 applications can utilize is typically 64M bytes, as 
   limited by the interface to BIOS call INT 15H function 88H, which returns 
   the amount of extended memory. The limitations of the interface to VCPI 
   and DPMI hosts result in a limit of over 1 Gbyte of memory. X-32VM 
   contains a virtual memory manager and can make up to 3.5 GBytes available 
   to the application. This provides the programmer with a doorway into the 
   future of 32-bit programming while preserving compatibility with older 
   operating systems back to DOS 3.0. 

   All examples mentioned in this manual are on disk. Portions of these 
   examples may be found to be useful for the assembly language programmer 
   and can be freely modified and distributed. 

   Installation and Creating Executables Using X-32 

   To write protected mode programs using X-32, the programmer will require 
   a 32- bit code generator in the form of an assembler or compiler and a 

   32-bit linker. The README.X32 file contains a list of currently supported 
   compilers and instructions for installing X-32 for each of the products. 

   The disk that comes with X-32 contains library code for various function 
   calls which are compatible with the compilers listed in the README.X32 
   file. These library files must be linked with the object code generated 
   by the code generator. The link order is important. The startup code 
   contained in an .OBJ file (the exact name is compiler dependent) must be 
   the first item in the linker list. After the linker has finished, a 
   loader file must be attached to the front end of the resultant 
   executable. The name of the file and method of doing this operation is 
   compiler dependent. Information supplied in the README.X32 file details 
   how to accomplish this with each compiler and linker supported. 

   The startup code initializes various aspects of the operating system and 
   forces the various segments to be in the proper order. It is vitally 
   important that the 16-bit segments be linked into the beginning of the 
   executable file and the 32-bit segments later. __argv and __argc are 
   pushed on the stack prior to calling main().  __argv is a 32-bit pointer 
   to the command line string. __argc is the number of arguments in the 

   command line. DS will equal ES and will equal the base address of CS and 
   SS. Thus most pointers will be near 32- bit pointers. The source for the 
   startup code is provided and is fully commented to allow X-32 programmers 
   to modify it for special applications. The source code also contains the 
   source for sbrk which some X-32 programmers may desire to customize. 






                                     -    3 -

 
                        Chapter 2 - UpShot - Online Help
 


   The online help system used for X-32VM is UpShot, from Autumn Hill 
   Software, Inc. Upshot is available from Autumn Hill Software to develop 
   your own help system. UpShot is implemented as a TSR program that can be 
   invoked from within other programs, such as a program editor. UpShot can 
   also be run in a non- resident mode like other programs. UpShot is 
   started from the DOS prompt and has the following comand line syntax: 

           UPSHOT [spec] [/switch ...]
   
           spec = manual(s) (default = *.MNL)
           /1      printer on LPT1
           /2      printer on LPT2
           /3      printer on LPT3
           /G:nnn  go to page nnn

           /H      show help
           /I      install as a TSR
           /L:xxx  lookup phrase xxx
           /M      monochrome display
           /R      remove (uninstall)

   If UpShot is run without the /I, switch then it runs and terminates like 
   a normal program. Using the /I switch "installs" the program as a TSR, 
   and it remains resident. When installed, UpShot is activated by pressing 
   the hot-key combination, which defaults to LEFT-SHIFT-F1. A resident copy 
   of UpShot can be removed from memory by running UpShot with the /R 
   switch, provided that no other TSR has been loaded subsequent to UpShot. 

   By default, UpShot looks for manuals in the current directory and uses 
   the specification '*.MNL'. A different path can be specified on the 
   command line. Note that the path should include the '*.MNL' 
   specification, e.g., 

   UPSHOT  C:\X-32VM\X-32VM.MNL 


   After UpShot has been activated (either by hot-key or from the command 
   line), menus, accelerator keys, and additional instructions are 
   available. 



 
                         Chapter 3 - Memory Management
 


   When X-32 is initializing the system, it first checks for a DPMI host, such
   as Windows 3.0 or above in 386 enhanced mode. If a DPMI host is found, all


                                     -    4 -

   memory is allocated through the host. Additional details of memory management
   under DPMI or under the debugger are available later in this chapter. The
   discussion below assumes that X-32 is not operating under a DPMI host or a
   debugger.
   
   All available extended memory is allocated using VCPI (EMS), XMS, INT 15 and
   VDISK industry standard methods.  Conventional memory is normally allocated
   and used with the extended memory to form the code, data and stack space
   required by the application.
   
   The amount of conventional memory used by X-32 can be controlled by setting
   the initial values of certain global variables contained in the file

   "options.asm".  These values can be modified and linked with X-32
   applications to control certain aspects of X-32 memory usage.
   
   It is sometimes necessary to reserve a small amount of memory for DOS to
   support certain function calls such as int 21h function 67h, spawn or exec.
   __x386_conv_mem_resv specifies how many 4 kilobyte pages X-32 should reserve
   for DOS.
   
   If there is no DPMI host, X-32 will attempt to reserve the number of 4 Kbyte
   pages specified.  This reserved memory will NOT be available to the
   application through protected mode int 21h function 350CH.  It can be used by
   DOS for its own purposes, or it can be allocated by code which executes int
   21h function 48H.
   
   The default value is 0 which means that all conventional memory is allocated
   by X-32 and will be available to the application through int 21h function
   350CH. If the reserve value is set to 255, this will attempt to reserve 255 *
   4 Kbytes or nearly 1 megabyte of conventional memory.  In this case X-32 will
   reserve the maximum amount of memory possible with the specified DOS buffer
   size.  That memory will not be available to the application through function

   call 350CH.  Acceptable values for this variable are 0 - 255.
   
   Another variable which the programmer can adjust is __x386_dos_buffer_size.
   This variable affects the size of the buffer which X-32 uses for
   communicating with DOS.  Increasing the size of this buffer will sometimes
   speed up disk IO but it will reduce the amount of memory available which can
   be allocated by the application.  It will also reduce the amount of
   conventional memory which can be reserved for DOS or allocated with int 21h
   function 48H.
   
   This size must always be an even number of 4 Kbyte pages.  The default size
   is 4 pages or 16 Kbytes.  It must never be set to a value smaller than 1 page
   or 4 Kbytes and must never be larger than 15 pages or 60 Kbytes.  The default
   size is 4 pages or 16 Kbytes.
   
   All of the memory X-32 has allocated is then mapped in as it is needed, using
   the 80386 paging mechanism to appear as two separate blocks of memory (see
   Figure 1). The code, static data and memory allocated through sbrk are
   located in what is referred to as the lower memory block, which starts at
   absolute address 10000000H (base address for DGROUP and SS, CS and DS


                                     -    5 -


   selectors) and grows up as calls to sbrk allocate more memory. The top of the
   upper memory block exists at absolute linear address F0000000H or E0000000H,
   relative to DGROUP. About 5K of X-32 data structures exist at the top of the
   upper block, the stack starts under this and grows down. There is a huge void
   between the memory blocks for which any attempt at access will cause an
   illegal page fault. The lower memory block can be expanded upwards by making
   calls to INT 21H function 350CH (see the chapter The X-32 Operating System
   Interface). This is how the function sbrk() normally allocates additional
   memory. If a page fault occurs due to the stack growing down into the void,
   the upper block is automatically expanded down if there is sufficient memory.
   If there is insufficient memory, X-32 will terminate the application with a
   register dump.
   
   
   Linear Address
   Description
   
   FFFFFFFFH              ---------------------------------
                          Possible physical address mapping
   F0000000H              ---------------------------------

                          About 5k reserved for X-32
                          ---------------------------------
                          Stack space
   upper block pointer    ---------------------------------
                          Empty
   lower block pointer    ---------------------------------
                          Memory allocated through sbrk
                          Static Data
                          Code
                          DGROUP base address
   10000000H              ---------------------------------
                          Reserved for X-32
   00100000H              ---------------------------------
                          Conventional Memory
   00000000H              ---------------------------------
   
   Figure 1. Memory Map, except when running under debugger or DPMI host.
   

   If the application is running under DPMI, a block of memory is allocated 

   from the host and used for 32-bit code and static data. Another block is 
   allocated for the 32-bit stack and each call to sbrk results in yet 
   another block of memory being allocated from the host.  Only a small 
   amount of conventional memory is used, the variable __x386_conv_mem_resv 
   will have no effect under DPMI.  Conventional memory cannot be allocated 
   through function ax = 350CH but can be allocated through function ah = 
   48H.  Only DPMI extended memory will be allocated through 350CH.  The 
   variable __x386_dos_buffer_size can be used to control the size of the 
   buffer X-32 uses for communicating with DOS, the same restrictions apply 
   as to the non DPMI case. 



                                     -    6 -

   If the application is running under the debugger, a single block of 
   memory holds the entire application. The code and static data are loaded 
   into the bottom end of the block. When memory is allocated using sbrk or 
   INT 21H function 350BH, the portion of the block reserved for the heap 
   expands upwards. The stack starts at the upper end of the block and grows 
   downwards. When operating under the debugger, the variables in 
   options.asm will have no effect. 

   Linear Address

   Description
   
   FFFFFFFFH              ---------------------------------
                          Reserved for debugger.
                          Address determined at run time
                          ---------------------------------
                          Stack space
   upper block pointer    ---------------------------------
                          Empty
   lower block pointer    ---------------------------------
                          Memory allocated through sbrk
                          Static Data
                          Code
                          DGROUP base address determined at run time.
                          ---------------------------------
                          Reserved for debugger
   00100000H              ---------------------------------
                          Conventional Memory
   00000000H              ---------------------------------
   

   Figure 2. Debugger Memory Map.
   
   The following discussion applies to all cases. 

   Extended memory which has been allocated by resident programs such as 
   ramdisks or disk caching software is not mapped into the memory map and 
   is therefore protected against accidental overwriting by the application. 
   If it is necessary to access specific addresses above 1 megabyte, the 
   application must make a call to INT 21H function 350AH to map in a 
   specific block of memory. 

   The default CS, DS and SS selectors all have the same base address which 
   points to the DGROUP base address shown in the memory map. The default DS 
   selector has limits which allow any present memory in the memory map to 
   be accessed except the section labeled as code. This cannot be read or 
   written using the DS selector. SS has limits which prevent it from 
   accessing anything between the DGROUP base address and the upper block 
   pointer. SS can access all present memory from the upper block pointer 
   upwards. SS and DS can both read and write to all present memory from the 
   DGROUP base address downwards using negative offsets (or a segment wrap 

   depending on your preferred viewpoint). 



                                     -    7 -

   Some compilers access code and data with both SS and DS, in these cases, 
   the startup code for these compilers may place the default value of DS in 
   SS and also may adjust the limits on DS to enable the DS selector to 
   access data which may be stored in the code segment.  See the source for 
   the startup code for the compiler in question for more information. 

   The CS selector has limits which enable it to execute code anywhere from 
   the DGROUP base address upwards to FFFFFFFFH absolute. It can also be 
   used to read data but can never be used to write to memory. 

   To assist in accessing conventional memory, the 32-bit near pointer 
   stored in the global variable __x386_zero_base_ptr can be used as an 
   offset relative to DS or SS to point to the start of conventional memory. 
   See the section Accessing Memory Below 1 Megabyte. 



 
                Chapter 4 - The X-32 Operating System Interface
 


   Most of the calls to the operating system, such as read and write, can be 
   done from from high level languages such as C or Fortran without any need 
   for the programmer to be aware of the actual interface to X-32. Some 
   programmers may desire to write assembly language programs which 
   interface directly to X- 32. Most of the X-32 function calls are very 
   similar to standard DOS function calls, many of them are identical. The 
   descriptions below provide the information necessary to understand the 
   differences between X-32 function calls and 16-bit DOS function calls. A 
   16-bit DOS reference manual is highly recommended in addition to the 
   descriptions below. 

   All function calls to X-32 are made by loading the registers with 
   appropriate parameters and executing an INT 21H. As mentioned above, most 
   of the function calls are similar to 16-bit DOS calls but there are some 

   calls which are not supported and some calls which are very different. 
   Most notably there are a number of calls made with AH = 25H or 35H which 
   provide X-32 specific services not available in 16-bit DOS. For those 
   calls which are labeled as DOS compatible, the user should consult a DOS 
   technical reference manual for a complete description. Many of the calls 
   differ from 16-bit DOS only in that 32-bit pointers are used rather than 
   16-bit pointers. For those function calls which are labeled "no 
   translation provided," X-32 reflects the 32-bit general registers 
   directly to the 16-bit DOS after loading DS and ES with real mode segment 
   values which point to the "DOS buffer" located in real mode memory. All 
   32-bit registers are returned as 16-bit DOS returned them except the high 
   word of EAX may be cleared; the segment registers are not returned from 
   16-bit DOS and are preserved as they were at the time of the INT 21H. For 
   more information on how to use 16-bit DOS or BIOS functions which are not 
   fully supported by X-32 see the section Interfacing to Real Mode Code. 

   The differences between 16-bit DOS and X-32 INT 21H function calls are 


                                     -    8 -

   outlined below. 



    DOS Function    X-32 Function 

    INT 21H Function 0H identical to 16-bit DOS function 4CH 

    INT 21H Function 1H compatible with 16-bit DOS 

    INT 21H Function 2H compatible with 16-bit DOS 

    INT 21H Function 3H compatible with 16-bit DOS 

    INT 21H Function 4H compatible with 16-bit DOS 

    INT 21H Function 5H compatible with 16-bit DOS 

    INT 21H Function 6H compatible with 16-bit DOS 

    INT 21H Function 7H compatible with 16-bit DOS 

    INT 21H Function 8H compatible with 16-bit DOS 


    INT 21H Function 9H DS:EDX used for pointer rather than DS:DX 

    INT 21H Function AH no translation provided 

    INT 21H Function BH compatible with 16-bit DOS 

    INT 21H Function CH compatible with 16-bit DOS 

    INT 21H Function DH compatible with 16-bit DOS 

    INT 21H Function EH compatible with 16-bit DOS 

    INT 21H Function FH no translation provided 

    INT 21H Function 10H no translation provided 

    INT 21H Function 11H no translation provided 

    INT 21H Function 12H no translation provided 


    INT 21H Function 13H no translation provided 

    INT 21H Function 14H no translation provided 

    INT 21H Function 15H no translation provided 

    INT 21H Function 16H no translation provided 



                                     -    9 -

    INT 21H Function 17H no translation provided 

    INT 21H Function 18H no translation provided 

    INT 21H Function 19H compatible with 16-bit DOS 

    INT 21H Function 1AH DS:EDX is used rather than DS:DX 

    INT 21H Function 1BH no translation provided 

    INT 21H Function 1CH no translation provided 


    INT 21H Function 1DH no translation provided 

    INT 21H Function 1EH no translation provided 

    INT 21H Function 1FH no translation provided 

    INT 21H Function 20H no translation provided 

    INT 21H Function 21H no translation provided 

    INT 21H Function 22H no translation provided 

    INT 21H Function 23H no translation provided 

    INT 21H Function 24H no translation provided 

    INT 21H Function 25H see section on X-32 System Calls 

    INT 21H Function 26H no translation provided 


    INT 21H Function 27H no translation provided 

    INT 21H Function 28H no translation provided 

    INT 21H Function 29H no translation provided 

    INT 21H Function 2AH compatible with 16-bit DOS 

    INT 21H Function 2BH compatible with 16-bit DOS 

    INT 21H Function 2CH compatible with 16-bit DOS 

    INT 21H Function 2DH compatible with 16-bit DOS 

    INT 21H Function 2EH compatible with 16-bit DOS 

    INT 21H Function 2FH ES:EBX is used for pointer rather than ES:BX 

    INT 21H Function 30H compatible with 16-bit DOS 



                                     -   10 -


    INT 21H Function 31H identical to 16-bit DOS except DX is ignored 

    INT 21H Function 32H no translation provided 

    INT 21H Function 33H compatible with 16-bit DOS 

    INT 21H Function 34H no translation provided 

    INT 21H Function 35H see section on X-32 System Calls 

    INT 21H Function 36H compatible with 16-bit DOS 

    INT 21H Function 37H no translation provided 

    INT 21H Function 38H not supported, do not use 

    INT 21H Function 39H DS:EDX is used for pointer rather than DS:DX 

    INT 21H Function 3AH DS:EDX is used for pointer rather than DS:DX 


    INT 21H Function 3BH DS:EDX is used for pointer rather than DS:DX 

    INT 21H Function 3CH DS:EDX is used for pointer rather than DS:DX 

    INT 21H Function 3DH DS:EDX is used for pointer rather than DS:DX 

    INT 21H Function 3EH compatible with 16-bit DOS 

    INT 21H Function 3FH DS:EDX is used for pointer rather than DS:DX 

    INT 21H Function 40H DS:EDX is used for pointer rather than DS:DX 

    INT 21H Function 41H DS:EDX is used for pointer rather than DS:DX 

    INT 21H Function 42H compatible with 16-bit DOS 

    INT 21H Function 43H DS:EDX is used for pointer rather than DS:DX 

    INT 21H Function 44H no translation provided 


    INT 21H Function 45H compatible with 16-bit DOS 

    INT 21H Function 46H compatible with 16-bit DOS 

    INT 21H Function 47H DS:ESI is used for pointer rather than DS:SI 

    INT 21H Function 48H see section on X-32 System Calls 

    INT 21H Function 49H not supported, do not use 

    INT 21H Function 4AH not supported, do not use 


                                     -   11 -


    INT 21H Function 4BH see section on X-32 System Calls 

    INT 21H Function 4CH compatible with 16-bit DOS 

    INT 21H Function 4DH compatible with 16-bit DOS 

    INT 21H Function 4EH DS:EDX is used for pointer rather than DS:DX 


    INT 21H Function 4FH compatible with 16-bit DOS 

    INT 21H Function 50H no translation provided 

    INT 21H Function 51H no translation provided 

    INT 21H Function 52H no translation provided 

    INT 21H Function 53H no translation provided 

    INT 21H Function 54H compatible with 16-bit DOS 

    INT 21H Function 55H no translation provided 

    INT 21H Function 56H DS:EDX and ES:EDI are used for pointers rather 
                    than DS:DX and ES:DI, DS must = ES 

    INT 21H Function 57H compatible with 16-bit DOS 

    INT 21H Function 58H compatible with 16-bit DOS 


    INT 21H Function 59H translation is not provided for return pointer in 
                    ES:DI 

    INT 21H Function 5AH DS:EDX is used for pointer rather than DS:DX, 
                    also the file name information is not returned. 

    INT 21H Function 5BH DS:EDX is used for pointer rather than DS:DX 

    INT 21H Function 5CH compatible with 16-bit DOS 

    INT 21H Function 5DH no translation provided 

    INT 21H Function 5EH no translation provided 

    INT 21H Function 5FH no translation provided 

    INT 21H Function 60H no translation provided 

    INT 21H Function 61H no translation provided 


    INT 21H Function 62H returns the real mode PSP segment value 


                                     -   12 -


    INT 21H Function 63H compatible with 16-bit DOS 

    INT 21H Function 64H no translation provided 

    INT 21H Function 65H no translation provided 

    INT 21H Function 66H compatible with 16-bit DOS 

    INT 21H Function 67H compatible with 16-bit DOS 

    INT 21H Function 68H compatible with 16-bit DOS 

    INT 21H > Function 68H no translation provided for any INT 21H 
                    functions greater than 68H 


   Table 1. DOS INT 21H compatiblity. 




 
                         Chapter 5 - X-32 System Calls
 


   INT 21H function 48H -- Allocate Real Mode Memory
   
   Call with:      AH = 48H
                   BX = number of 16 byte paragraphs to allocate
   Returns:        Carry flag clear if successful
                   AX = real mode segment value
                   EBX = protected mode near pointer
   
                   Carry flag set if unsuccessful
                   AX = DOS error code
                   BX = size of largest available block (16-bit DOS compatible).
   
                   Note each successful call results in a minimum of 4 Kbytes
                   actually being allocated. There is no way to deallocate

                   memory once it is allocated.
   
   INT 21H function 4BH -- Execute a program
   
   Call with:      AH = 4BH
                   AL = 0
                   ES:EBX points to a parameter block as defined below
                   DS:EDX points to an ASCIIZ string which specifies the
                   complete path and file name of the program to execute.
   
   Returns:        Carry flag clear if successful
                   Carry flag set if unsuccessful with AX = DOS error code


                                     -   13 -

                   All registers may be destroyed
   
   The parameter block pointed to by ES:EBX is a different format than 
   16-bit DOS: 
   
   Offset 0                48-bit far pointer to environment string
   Offset 6                48-bit far pointer to command tail string
   

   INT 21H function 4BH is used to execute another program. There are some 
   important differences and restrictions between this and the corresponding 
   16- bit real mode function. Not all of the subfunctions are supported and 
   the parameter block is different. 

   INT 21H with AH = 25H or 35H are special function calls which are not 
   compatible with 16-bit DOS. The subfunction number is passed in AL. Any 
   registers not mentioned are preserved. Any calls to unused subfunction 
   numbers will result in a return with the carry flag set and EAX = 
   A5A5A5A5H. These special function calls are outlined on the following 
   pages. 


   AX = 2501H      Reset data structures
   Call with:      SS = either the application's original SS or the original DS
   Returns:        carry flag cleared if successful carry flag set if caller is
                   operating on the X-32 stack
   
   Resets all internal X-32 data structures to the original values and pops 
   all information off the X-32 stack unless the caller is operating on the 

   X-32 stack, in which case it returns with carry flag set. Interrupt 
   handlers and procedures called by real mode code are called on the X-32 
   stack, the stack is normally returned to the default value upon returning 
   from the interrupt handler or other procedure. INT 21H function 2501H 

   AX = 2502H      Return protected mode interrupt vector
   Call with:      CL = INT #
   Returns:        carry flag cleared
                   ES = CS value for interrupt vector
                   EBX = EIP value for interrupt vector
   
   AX = 2503H      Return real mode interrupt vector
   Call with:      CL = INT #
   Returns:        carry flag cleared
                   EBX = CS:IP of real mode interrupt vector
   
   AX = 2504H      Set protected mode interrupt vector
   Call with:      CL = INT #
                   DS:EDX = CS:EIP for protected mode interrupt vector
   Returns:        carry flag cleared

   
   AX = 2505H      Set real mode interrupt vector
   Call with:      CL = INT #


                                     -   14 -

                   EBX = CS:IP for real mode interrupt vector
   Returns:        carry flag cleared
   
   AX = 2506H      Hook real and protected mode interrupts
   Call with:      CL = INT #
                   DS:EDX = CS:EIP for protected mode interrupt handler
   Returns:        carry flag cleared if successful
   
   This function call hooks both protected and real mode interrupts and 
   reflects them to the same protected mode handler. 
   
   AX = 2507H      Set real and protected mode interrupt vectors
   Call with:      CL = INT #
                   DS:EDX = CS:EIP for protected mode interrupt vector
                   EBX = CS:IP for real mode interrupt vector
   Returns:        carry flag cleared
   

   AX = 2508H      Get base address of selector
   Call with:      BX = selector
   Returns:        carry flag cleared if successful ECX = absolute base
                   address of selector in BX carry flag set if invalid selector
   
   AX = 2509H      Return system segments and selectors
   Call with:      nothing
   Returns:        carry flag cleared
                   high word of EAX = default DS
                   low word of EAX = alias for 16-bit data segment
   
                   high word of EBX = ring 0 selector for 16-bit code segment
                   low word of EBX = 16-bit real mode code segment
   
                   high word of ECX = ring 0 selector for 16 bit data segment
                   low word of ECX = 16 bit real mode data segment
   
                   high word of EDX = selector pointing to absolute zero, 4
                                      Gbyte limit
                   low word of EDX = default SS

   
                   high word of ESI = program segment prefix (PSP) selector
                   low word of ESI = environment selector
   
   AX = 250CH      Return hardware interrupt vector locations
   Call with:      nothing
   Returns:        carry flag cleared
                   AL = actual interrupt for IRQ0
                   AH = actual interrupt for IRQ8
   
   In some situations, the hardware interrupt controllers are reprogrammed 
   to cause hardware interrupts to execute at locations other than the 
   default (IRQ0 = INT 8, IRQ8 = 70H). Under all situations if the 
   application attempts to hook the default location for the interrupts, 
   X-32 will hook the appropriate interrupt. It is recommended that 


                                     -   15 -

   applications wishing to hook hardware interrupts hook the default value 
   and allow X-32 to deal with reprogrammed vectors. 
   
   AX = 250DH      Return real mode interface addresses
   Call with:      nothing

   Returns:        carry flag cleared
                   EAX = CS:IP of "real mode callback" X-32 procedure which will
                   call a protected mode procedure
   
                   EBX = real mode SEG:OFFSET of "dos_buffer" located in real
                         mode memory
                   ECX = size of dos_buffer in bytes, guaranteed to be 4 Kbytes
                         minimum
                   ES:EDX = protected mode selector:offset of "dos_buffer"
   
   The procedure pointed to by EAX can be used by real mode procedures to 
   call protected mode code. The real mode procedure must push a far pointer 
   on the stack to a structure containing the protected mode selector 
   values, or zero, if X-32 should use the default selector values of 0 for 
   GS and FS and the normal 32-bit DS selector for ES and DS. After the 
   selector pointer should be 2 bytes of zeros as place holders for the CS 
   value, followed by 4 bytes containing the offset of the protected mode 
   code to call. The CS value is normally ignored, the default 32-bit CS is 
   used. The layout of the stack and structure are as follows: 
   

                           STACK
                           param n
                           ......
                           param 2
                           param 1
                   dword   seg:offset      pointer to selector structure
                   word    dummy for CS
   SS:ESP  ------->        dword   offset of protected mode code
   
                   SELECTOR STRUCTURE
   
                   struct
                   {
                     unsigned short selector_ds;
                     unsigned short selector_es;
                     unsigned short selector_fs;
                     unsigned short selector_gs;
                   }
   
   The stack should look as above just prior to calling the real mode call 

   back procedure. When the protected mode procedure receives control, 
   SS:ESP will point to the original stack with a quadword return address 
   which will return to the calling real mode procedure. The SS and ESP 
   values will be protected mode values such that the original real mode 
   stack will still be in effect for passing parameters to and from 
   protected mode. The procedure must execute a far return when returning. 


                                     -   16 -


   dos_buffer is a real mode data space guaranteed to be a minimum of 4 
   Kbytes in size. It is used for interfacing to DOS, BIOS and other real 
   mode code from protected mode. It may be used for temporary storage but 
   will be overwritten by I/O calls from protected mode. It should not be 
   used by hardware interrupt handlers. Permanent real mode storage can be 
   allocated in the 16-bit code segment. See the example in Interfacing to 
   Real Mode and INT 21H function 48H. 
   
   AX = 250EH      Call real mode code
   Call with:      EBX = CS:IP pointing to real mode code
                   ECX = number of word parameters to copy from protected stack
                   to real stack
   

   Returns:        carry flag cleared if successful
                   All general registers as real mode code returned them
                   All protected mode selectors unchanged
                   Stack unchanged
   
   All flags except carry flag are as the real mode code returned them. The 
   carry flag is set, indicating failure, if ECX is greater than 63. The 
   real mode procedure must execute a far return to return to protected 
   mode. Parameters can be passed from protected to real mode on the stack, 
   but not real to protected. 
   
   AX = 250FH      Convert protected mode address to real mode address
   Call with:      ES:EBX = protected mode address
   
   Returns:        carry flag cleared if address is less than 1M byte
                   ECX = SEGMENT:OFFSET of real mode address
                   carry flag set if address is greater than 1M byte
                   ECX = absolute 32-bit address
   
   AX = 2511H      Execute interrupt in real mode

   Call with:      DS:EDX = pointer to parameter structure
   
   Returns:        EDX unchanged
   
   All general registers contain the values returned by real mode code. The 
   parameter structure contains updated segments and EDX but no other 
   updates. 
   
           The parameter structure must be as follows:
           struct
           {
             unsigned short interrupt_num;
             unsigned short selector_ds;
             unsigned short selector_es;
             unsigned short selector_fs;
             unsigned short selector_gs;
             unsigned long  register_eax;
             unsigned long  register_edx;


                                     -   17 -

           }
   

   AX = 252BH      Virtual Memory management functions
   Call with:      BH = 5 to lock pages
                   BH = 6 to unlock pages
                   if BL = 1, ES:ECX = start address of memory region
                   if BL = 0, ECX = absolute start address of memory region
                   EDX = size in bytes of memory region
   Returns:        carry flag cleared if successful
                   carry flag set if failure
   
   
   This function can be called under any environment but is ignored if X-32 
   is not using virtual memory of some kind. In this case it always returns 
   success. It returns failure if under a DPMI host, and the host refuses to 
   lock the requested pages, or if there is insufficient physical memory to 
   lock the requested size. 
   
   AX = 2532H      Return exception handler vector
   Call with:      CL = exception # 0 - 15
   Returns:        carry flag cleared if successful ES:EBX = CS:EIP of current
                   exception handler vector carry flag set if CL > 15

   
   AX = 2533H      Set exception handler vector
   Call with:      CL = exception # 0 - 15
                   DS:EDX = CS:EIP of exception handler
   Returns:        carry flag cleared if successful
                   carry flag set if CL > 15
   
   AX = 2535H      Get and set MP and EM bits in CR0
   Call with:      BL = 0 if getting bits, BL = 1 if setting bits.
                   DS:EDX = points to a 1 byte variable which will hold bits 1
                   and 2 of CR0.
   Returns:        carry flag cleared if successful
                   carry flag set if failure
   
                   This function always succeeds unless X-32 is operating
                   under a DPMI host which in turn fails the request.  This
                   need only be used for applications which are setting up a
                   coprocessor emulator.  Bit 1 (2s place) of the variable is
                   used for the MP bit and bit 2 (4s place) is used for the
                   EM bit.

   
   AX = 3501H      Allocate a protected mode selector
   Call with:      nothing
   Returns:        carry flag cleared if successful
                   BX = new selector
                   carry flag set if no more selectors are available
   
   The new selector will be an expand up read/write data selector with limit 
   and base address undefined. 


                                     -   18 -

   
   AX = 3502H      Deallocate a protected mode selector
   Call with:      BX = selector
   Returns:        carry flag cleared if successful
                   carry flag set if invalid selector
   The application should only deallocate selectors which were previously 
   allocated through function 3501H. 
   
   AX = 3503H      Set selector base address
   Call with:      BX = selector
                   ECX = base address

   Returns:        carry flag cleared if success
                   carry flag set if invalid selector
   
   AX = 3504H      Get base address of selector
   Call with:      BX = selector
   Returns:        carry flag cleared if success
                   ECX = absolute base address of selector in BX
                   carry flag set if invalid selector
   
   AX = 3505H      Set selector limit
   Call with:      BX = selector
                   ECX = desired limit
   Returns:        carry flag cleared if success
                   ECX = actual limit
                   carry flag set if no more selectors are available
   
   The limit will be rounded down to the nearest 4k boundary if the 
   requested limit is greater than 1 megabyte. In either case, ECX will 
   return the actual limit set. 
   

   AX = 3508H      Expand DGROUP memory block
   Call with:      ECX = requested size of memory block
                   DS = default DS
   Returns:        carry flag cleared if success
                   EAX = new lowest legal limit for the stack
   
                   carry flag set if failure
                   EAX undefined
                   ECX undefined
   
   The requested size of the memory block must be an even number of 4 Kbyte 
   pages.  This function is normally called by memory allocation functions 
   such as sbrk and should not be called directly by the application.  This 
   function must only be called if function 3509H was called to set up the 
   stack during intialization, it must NOT be called if function 350DH was 
   used to set up the stack. 
   
   AX = 3509H      Reserve a block of memory for the 32-bit stack
   Call with:      ECX = size of block in bytes
                   EBX = current ESP value



                                     -   19 -

                   ESI = the current top of the DGROUP memory block.
                   DS = default DS
   Returns:        carry flag cleared if successful
                   EBX = new value for ESP
                   EDX = suggested new limit for SS
                   ESI = new break location
                   carry flag set if failure
   This function should only be called once during initialization, this is 
   done in the startup code for most applications (an example is in the 
   Watcom startup code).  It is only documented for those people who wish to 
   rewrite or modify the startup code. 
   
   AX = 350AH      Physical address mapping
   Call with:      EBX = absolute physical address
                   ECX = size of area to map in bytes
   Returns:        EBX = near pointer to the requested physical address
                   carry flag clear if success
                   carry flag set if services were refused by DPMI host or if
                   insufficient memory.
   The application should not make repeated calls for the same physical 

   address. Each call allocates memory for page tables and consumes internal 
   data space within X-32. There is no provision for unmapping devices once 
   they have been mapped. Typically, a maximum of 64 devices of 4M bytes 
   each can be mapped before X-32 will fail the call. Under DPMI, the host 
   reserves the right to refuse services at any time. 
   
   AX = 350BH      Update and return the available free memory
   Call with:      DS = default selector for DS
   Returns:        Carry flag cleared
                   EAX contains the maximum amount of memory which can be
                   allocated with call 350CH or 3508H.
   This rechecks extended memory availability, and in the case of VM, 
   rechecks the free disk space prior to returning the amount of free memory 
   left which can be allocated. 
   
   AX = 350CH      Allocate a block of memory
   Call with:      ECX = size of requested block in bytes
                   DS = default DS
   Returns:        Carry flag cleared if successful
                   EAX = near pointer to new block

                   EDX = new lowest legal value for stack
                   Carry flag set if failure
   
   The size of the requested block must be an even number of 4 Kbyte pages. 
   This is normally handled by calls to sbrk() from malloc() or calloc().  
   This function should only be called by applications wich called function 
   350DH for stack set up during intialization, it must NOT be called if 
   function 3509H was called for stack initialization. 
   
   AX = 350DH      Reserve a block of memory for the 32-bit stack
   Call with:      ECX = size of block in bytes
                   EBX = current ESP value


                                     -   20 -

                   DS = default DS
   Returns:        Carry flag cleared if successful
                   EBX = new value for ESP
                   EDX = suggested new limit for SS
                   Carry flag set if failure
   This function should only be called once during initialization, this is 
   done in the code contained in the start up code (an example is in CX.ASM 
   for Zortech) and is only documented for those people who wish to rewrite 

   or modify the startup code. 
   
   AX = 350EH      Map a file to memory (X-32VM ONLY)
   Call with:      BX  = valid open DOS file handle
                   ECX = size of block in bytes
   Returns:        Carry flag cleared if successful
                   ECX = near pointer to MMFIO space
   
                   Carry flag set if failure
                   EAX = 8 if insufficient address space or too many MMFIO calls,
                         25 if operating under a DPMI host,
                         0 if the requested size was 0, any other error code is
                         an error code returned from DOS when attempting to
                         access the file.
   The value passed in ECX can be either larger or smaller than the file.  
   The file may be nothing more than a zero length open file in which case, 
   it will be expanded to hold the information written to the MMFIO space as 
   required. This size of file and the MMFIO space are rounded up to the 
   next 4 Kbyte increment.  A maximum of 16 MMFIO files may be active at any 
   time.  The handle passed to X-32VM will be used for accessing the file, 

   the file pointer associated with that handle will be under the control of 
   X-32VM and the application should not make any assumptions about the 
   location of the file pointer after mapping in a file. 
   
   AX = 350FH      Flush data to file
   Call with:      ECX = pointer to an active MMFIO block
   Returns:        Carry flag cleared if successful
                   Carry flag set if failure
   
   This function updates the disk with any changes which have been made to 
   the MMFIO block.  It only writes those 4K blocks to the disk which have 
   been altered in memory but have not been written to disk yet.  Many of 
   those blocks will be updated in the normal course of Virtual Memory swap 
   activity so this function may not actualy cause a write to disk, but it 
   insures that any changes present only in memory are written to disk.  
   This should be called prior to termination if a MMFIO file is being 
   altered and it is desired to preserve those changes on disk. 


   5.1  Interrupt 33H Support for Mouse Drivers 


   Interrupt 33H is normally used for interfacing to mouse drivers. The 
   function number is passed in AL. Of special interest are functions 9 and 


                                     -   21 -

   12. Function 9 uses ES:EDX for a pointer rather than ES:DX as would be 
   done from 16-bit DOS. Function 12 is not translated. Programmers 
   requiring INT 33H function 12 support should refer to the example in the 
   section Interfacing to Real Mode Code and the function __x386_init_mouse 
   included in funcs32.asm. 

   5.2  Other Interrupts Executed Under X-32 

   Interrupts 21H and 33H have special handling as outlined above. The 
   default handler for all other interrupts switches to real mode, executes 
   the interrupt in real mode and then returns to the 32-bit caller in 
   protected mode. All general registers are preserved both for 
   communicating to the 16-bit handler and for the return value from the 
   16-bit handler.  The real mode segment registers are undefined upon 
   calling the 16-bit code, and the caller's segment selectors are always 
   preserved for the 32-bit application. 


   If it is required to pass segment register values to and from 16-bit 
   interrupt handlers, INT 21H function 2511H can be used to pass real mode 
   segments to and from the real mode handler. This can be used in 
   conjunction with the real mode DOS buffer to pass strings or other data 
   to real mode code. (See function 250DH for information on the DOS 
   buffer.) Examples of this are given in the section Interfacing to Real 
   Mode Code. 

   When operating under DPMI hosts, INT 31H and INT 2FH have special 
   meanings and are handled directly by the DPMI host. 

   Some versions of Windows do not handle protected mode interrupts properly 
   when running in 386 enhanced mode. In particular, some interrupts cannot 
   be executed from a 32-bit stack, since some of the interrupt handlers 
   within Windows only recognize the low word of ESP. INT 31H is one of the 
   interrupts which has this problem. If the X-32 programmer wishes to 
   execute interrupts other than 21H within these versions of Windows, they 
   should allocate memory for a stack and create a selector which points to 
   the base of that block of memory, then switch to an SS:ESP value where 
   ESP is less than 64 Kbytes before executing the interrupt. Neither X-32 

   nor the programmer can intercept the interrupt ahead of Windows so there 
   doesn't appear to be any other workaround for this problem. 



 
                     Chapter 6 - Writing Interrupt Handlers
 


   Function calls 2502 - 2507 serve to get and set protected and real mode 
   interrupt vectors. These provide options for hooking real mode 
   interrupts, protected mode interrupts or both. Applications should 
   restore any altered real mode vectors prior to termination. 



                                     -   22 -

   When the interrupt handler receives control, the stack will be a locked 
   stack of about 256 bytes in size. The SS value will not have the same 
   base address as DS. Parameters will be located on the stack as follows: 

           dword   pointer to "Interrupt structure" to be used with SS
           dword   eflags at time of interrupt with the interrupt flag and trap
                   flag cleared
           dword   CS for IRETD
           dword   EIP for IRETD   <---------- SS:ESP


   The CS:EIP on the stack points to X-32 code which restores the system 
   after the interrupt handler returns. All general registers contain the 
   values they had at the time of interrupt. Further information about the 
   segment registers and CS:EIP at the point of origin can be found in the 
   interrupt structure which is pointed to by SS and the pointer just above 
   eflags on the stack. The interrupt structure has the following format: 

           dword   SS
           dword   ESP
           dword   eflags
           dword   CS
           dword   EIP     eflags:CS:EIP as pushed on the stack by processor
           word    mode    1 if real mode, 0 if prot mode, 2 if exception
           word    INT #   0 - 256 indicating interrupt number
           word    GS
           word    FS
           word    ES
           word    DS
           dword   EAX     <------ SS:pointer to int_struc


   The "mode" word indicates whether the interrupt occurred in real mode or 
   protected mode. Values of SS, ESP, and EIP can be changed in the 
   interrupt structure to effect a change in the processor state upon IRETD. 
   CS cannot be altered if the source was in protected mode; interrupts 
   originating in real mode cannot be returned to protected mode and vice 
   versa. Also, interrupts originating within X-32 must be returned to X-32; 
   these will have a CS value which is not equal to the 32-bit application 
   CS. All code within the 32-bit application will have the same CS value so 
   interrupts originating in the 32- bit application can be returned to any 
   desired location within the application. 

   The segment values contained in DS, ES, FS and GS at the time the 
   interrupt handler gains control may not match the values in the interrupt 
   structure if the CS of origin is not equal to the 32-bit CS. For example, 
   if the originating CS is a real mode segment, the original DS, ES, FS, 
   and GS values cannot be loaded while the processor is in protected mode. 

   If the point of origin is real mode, the handler can alter the values of 
   the returned segment registers by altering the interrupt structure. If 

   the point of origin is within the 32-bit protected mode application, the 
   segment registers are passed both directions in the registers themselves, 


                                     -   23 -

   and altering the values in the interrupt structure will have no effect. 
   If the point of origin is within X-32 (protected mode and CS != 32-bit 
   application CS), the application is free to alter the DS, ES, FS, and GS 
   registers but must not alter the segment values in the interrupt 
   structure. The only interrupts which can originate within X-32 are INT 
   31H, INT 67H, and hardware interrupts. 

   The values in the general registers are passed both from point of origin 
   to handler and from iretd back to point of origin. The eflags value 
   returned to the point of origin is taken from the iretd portion of the 
   stack, not the interrupt structure. 

   The protected mode interrupt handler can process the interrupt and then 
   execute an iretd or pass execution on to the original handler. It is not 
   normally recommended to continue execution and never execute an iretd to 
   the X-32 CS:EIP. This allocates space on the X-32 stack which can never 
   be restored without returning through the normal return path. If it is 
   necessary to do this, the handler can alter the EIP and the SS:ESP values 

   in the interrupt structure and execute an iretd to return to a location 
   in the handler with a stack other than the one originally passed to the 
   handler. If this method is not acceptable, the handler should switch to a 
   stack other than the one passed to the interrupt handler and execute an 
   INT 21H with AX = 2501H to reset the X-32 stack and other internal data 
   structures. 

   The examples in the files DEMOINT1.ASM, DEMOINT2.ASM, and DEMOINT3.ASM 
   demonstrate several techniques which can be used when writing interrupt 
   handlers. 

   6.1  Special Constraints for Some Interrupts 

   There are special constraints which apply to hardware interrupt handlers 
   and handlers for INTS 1CH, 23H and 24H. In any situation where virtual 
   memory could be involved, the code, data, and stack must be locked for 
   the handler. This includes the VM version of X-32 in all cases and the 
   non VM version of X- 32 when operating under a DPMI host. In these cases, 
   all code, data, and stack memory that can be accessed by the interrupt 
   handler must be locked using interrupt 21H, function 252BH. Calling this 

   function when there is no virtual memory is harmless and will serve to 
   insure the code will operate correctly if it is ever used with virtual 
   memory of some form. Do not attempt to lock memory from within an 
   interrupt handler. 

   The example code in DEMOINT8.ASM shows an example of hooking real and 
   protected mode interrupt 8, the timer tick. 



 
                         Chapter 7 - Hooking Exceptions
 



                                     -   24 -


   There are special interrupts which are referred to as exceptions. 
   Exceptions usually occur when there is some type of error, such as an 
   attempt to load an illegal segment value. Refer to a reference on Intel 
   processors for a more complete description of exceptions and their 
   meanings. 

   These special interrupt vectors can only be accessed with function calls 
   2532H and 2533H. The parameters passed on the stack to the exception 
   handler are identical to the normal interrupt, except an error code is 
   passed at SS:[pointer - 4], where "pointer" is the pointer to the 
   interrupt structure. Refer to an Intel data book for a description of the 
   meaning of the error code. 

   Some general protection faults (exception 13) and most page faults 

   (exception 14) are handled by the X-32 operating system. Only those 
   faults which cannot be handled by X-32 are passed on to the application 
   exception handler. 

   Faults which have a CS = application CS can be restarted if the problem 
   can be corrected by altering registers or data and then returning to the 
   point of origin. If the CS of origin is not equal to the application CS 
   the fault occurred within the extender itself and probably means a bad 
   pointer was passed to X-32 by the application for I/O of some kind. 
   Faults occurring within X-32 cannot be restarted at the point of origin. 
   The handler has three options: 

   1.  Terminate the program 

   2.  Branch to the original handler which will dump registers and 
   terminate 

   3.  Switch to a legitimate stack and call INT 21h function 2501H to reset 
   the X-32 system. Then branch to other code and NEVER return. 


   The example in DEMOEXCP.ASM demonstrates hooking general protection 
   faults and shows a handler which resets X-32 and could thereafter branch 
   to other code and continue running after the fault. 



 
                 Chapter 8 - Accessing Memory Below 1 Megabyte
 


   There are numerous cases where it may be desirable to read and write to 
   memory in the first megabyte. There are three methods that can be used: 

   1.      By using the default value of DS or SS and "wrapping" back to the 
   1st megabyte. This method uses a "negative" offset from DGROUP, or, more 
   correctly, "wraps around" to zero from DGROUP, and thus enables the 


                                     -   25 -

   application to access memory in the first megabyte using the default DS 
   or SS values. 

   2.      By using a special selector which points to absolute zero. This 
   is a selector which has a base address of zero, a limit of 4 Gbyte and 
   read/write capability. This selector can be loaded from the global 
   variable _x32_zero_base_selector. 


   3.      By allocating a new selector to point to the specific location 
   desired. The application can call the function _x32_mk_protected_ptr to 
   create a new selector with any desired base address.  This selector 
   could, for example, point to the video buffer. 

   The example in the file DEMOVID.ASM illustrates these three methods of 
   accessing locations below the 1 megabyte boundary. 



 
                  Chapter 9 - Physical Addresses Above 1 Mbyte
 


   Physical memory addresses above 1 megabyte are remapped by X-32 in a 
   manner which makes it difficult or impossible for applications to 
   directly access a specific physical address, without requesting X-32 to 
   map in the specific address for which access is needed. Applications may 
   require access to specific locations in extended memory, or require 
   access to memory mapped I/O devices, such as graphics boards and math 
   coprocessors. These devices are sometimes mapped into the High Memory 
   Area (HMA) just above 1M byte by expanded memory emulators or other 
   memory management devices. If a device has been mapped into the HMA by a 
   memory manager, that device is normally still accessible in the HMA by 
   X-32 programs. More general purpose support for accessing memory, or 
   memory mapped I/O devices, can be obtained by mapping in the device with 
   INT 21H function 350AH. This can also be accessed from the C callable 
   function _x32_map_physical_address, as shown in the example in file 

   DEMOMMIO.ASM: 



 
                     Chapter 10 - Executing Real Mode Code
 


   Real mode functions can be called on a far return stack with INT 21H 
   function 250EH. Real mode interrupts can be accessed directly through INT 
   21H function 2511H. If any function requires a significant amount of 
   interaction with real mode code, it may be advantageous to place that 
   function in a real mode code segment and execute it with the processor in 
   real mode. The real mode function can then be called from protected mode 


                                     -   26 -

   through function 250EH. 

   Real mode code segments can be created by using the start16code and 
   end16code macros, as defined in the file MAC32.ASM. When the program is 
   loaded into memory, all code within the 16-bit code segment will be 
   loaded in conventional memory and, can be called with INT 21H function 
   250EH. 


   Real mode procedures cannot access the code and data which the 32-bit 
   application uses so easily. X-32 copies all file and screen I/O between a 
   real mode data buffer and extended memory in order to provide an 
   interface between 32-bit applications and 16-bit DOS. Applications can 
   also use this buffer if desired. INT 21H function 250DH returns both the 
   real mode and the protected mode address of this buffer. The buffer is 
   typically 16kbytes in size and is guaranteed to be a minimum of 4 Kbytes 
   in size. This buffer should not be accessed by hardware interrupt 
   handlers, and the user must remember that any data stored in the buffer 
   will be overwritten when the 32-bit application makes I/O calls to DOS. 

   Static real mode data can be defined in the 16-bit code segment. 
   Approximately 30 Kbytes of code and data can be added to the real mode 
   code segment in this manner. Real mode memory can also be allocated 
   through int 21H function 48H issued from either real or protected mode.  
   Read the comments in the file, "options.asm" for more information on 
   reserving real mode memory. 

   The example in the file DEMOREAL.ASM demonstrates allocating and 
   accessing 20,000 bytes of real mode memory. It also demonstrates a simple 

   method of creating a general protection fault to enable the programmer to 
   examine the register contents. 

   Some calls to mouse drivers are supported through int 33H.  Other 
   functions such as int 33H function 12 can be supported through additional 
   code. Function 12 is supported through the function _x32_mouse_init, 
   which is included in the X-32 library.  The source code is available in 
   the file FUNCS32.ASM and provides a demonstration of how the developer 
   can provide support for complex interfaces to 16-bit code above and 
   beyond the support offered directly by X-32. The example in file 
   DEMOMOUS.ASM demonstrates how to use the functions _x32_mouse_init and 
   _x32_mouse_term. 



 
                 Chapter 11 - Compatibility With Other Software
 


   X-32 programs are compatible with most other programs which may be 
   resident in the system. X-32 supports all of the commonly used extended 
   memory allocation methods known as: 



                                     -   27 -

   1.      XMS     example: Microsoft HIMEM.SYS
   2.      INT 15  used if no other memory manager is present
   3.      VDISK   example: IBM's VDISK.SYS
   4.      VCPI    example: Qualitas 386 Max Version 5.0 and above
   5.      DPMI    example: Microsoft Windows 3.0 in 386 enhanced mode

   This means that X-32 programs are compatible with modern EMS emulators, 
   ramdisks, diskcaching software, and multitasking devices, like 
   Quarterdeck DESQview. 



   Windows has some special problems with certain interrupts when running on 
   a 32-bit stack. Fortunately, DOS and BIOS calls are fully supported but 
   DPMI calls require the application to switch to an SS:ESP in which ESP is 
   less than 64K prior to issuing the INT 31H. 

   Some older Expanded Memory Specification emulators (EMS) did not support 
   the VCPI standard and are not compatible with X-32 programs. If an X-32 
   program is run on these machines, the program will immediately output the 
   error message: 

           Previously installed software is neither VCPI nor DPMI compatible.

   and terminate. 

   All currently popular memory management programs which run under DOS are 
   compatible with X-32 programs. 



 
                      Chapter 12 - Hardware Compatibility
 


   The most important hardware compatibility issue with X-32 programs is the 
   processor. An 80386 processor or above is required to run X-32 programs. 
   If an earlier processor is detected the following error message will 
   appear: 

           Fatal error, 80386 processor is required.

   Some early 80386 chips have errors which can cause problems with 
   protected mode programs.  Reports of these problems have been exceedingly 
   rare. Symptoms can include unexplained, and often unpredictable, crashes 
   or occasional errors in the last 1 - 3 bytes of file I/O. If this type of 
   problem is encountered with an early machine and cannot be duplicated on 
   a newer 80386, it can probably be fixed by installing a newer 80386 chip. 


   If there is no resident memory manager program (such as HIMEM.SYS), X-32 
   programs have to enable address line 20 (the A20 line) before extended 
   memory can be directly accessed. The X-32 A20 control algorithm is 


                                     -   28 -

   compatible with true AT compatibles, PS2 machines and a number of other 
   A20 control schemes. If X-32 detects a problem enabling the A20 line, the 
   following message will appear: 

           Cannot enable the A20 line, XMS memory manager required.

   If this message appears, it will be necessary to install some type of 
   memory manager such as Microsoft HIMEM.SYS or Quarterdeck QEMM-386. 
   Again, reports of this problem have been exceedingly rare. 



 
                Chapter 13 - Miscellaneous Functions and Global
 


   13.1  Variables Special functions 

   The functions listed below can be called from C and are present in the 
   library files. The source code can be found in the file FUNCS32.ASM. All 
   parameters passed on the stack require 32-bits, except far pointers, 
   which require 64- bits (only 48 bits are used). All far pointers are 
   passed by pushing the selector first, then the offset. 32-bit values are 
   returned in EAX. Far pointers are returned in DX:EAX. 


    unsigned int _cdecl _x32_allcoreleft(void) 

                    This returns the total amount of memory available, not 
                    that which is available in a single block.  This 

                    function sums the memory blocks in the free list of the 
                    heap maintained by the runtime library of the compiler 
                    and the memory available but unallocated from the 
                    extender. In cases where Virtual Memory is in use, it 
                    causes X-32VM to check the free disk space and adjust 
                    free memory to allow for any changes in free disk space 
                    caused by adding or deleting files to the drive which 
                    contains the swap file. 

                    This function is not available for use with some 
                    compilers.  See the README file associated with the 
                    compiler you are using. 

    unsigned int _cdecl _x32_coreleft(void) 

                    This returns the largest block of memory which can be 
                    allocated using functions such as malloc or calloc. In 
                    cases where Virtual Memory is in use, it causes X-32VM 
                    to check the free disk space and adjust free memory to 
                    allow for any changes in free disk space caused by 

                    adding or deleting files to the drive which contains the 


                                     -   29 -

                    swap file. 

                    This function is not available for use with some 
                    compilers.  See the README file associated with the 
                    compiler you are using. 

    void * _cdecl _x32_get_abs_address(void _far *pointer) 

                    This function determines the absolute address of a far 
                    pointer.  To convert this to an address relative to 
                    DGROUP, add _x32_zero_base_ptr to the return value. 

                    Returns: 32-bit absolute address if successful, -1 if 
                    invalid selector 

    int _cdecl _x32_free_protected_ptr(void _far *pointer) 

                    Frees a protected mode selector Returns: 0 if 
                    successful, -1 if invalid selector 


                    Frees a protected mode selector which was previously 
                    allocated through _x32_mk_protected_ptr. There are a 
                    limited number of selectors available for allocation. 
                    This function allows selectors which were previously 
                    allocated to be freed so they can again be allocated 
                    through _x32_mk_protected_ptr. 

    void *_cdecl _x32_map_physcial_address(void *abs_addr, unsigned int 
                    size)            

                    abs_addr = start address of physical memory to map 

                    size = size of region in bytes 

                    This function enables access to specific physical 
                    addresses in memory. It is typically used for accessing 
                    memory mapped I/O devices. The region to be mapped must 
                    be completely above or completely below the 1M byte 
                    boundary; It cannot lap over the boundary itself. The 

                    function will fail and return -1 if there is 
                    insufficient memory or if the DPMI host refuses service. 
                    X-32 must allocate a minimum of 4 Kbytes for each call 
                    to this function. The function returns a near pointer 
                    which can be used to access the actual physical address 
                    requested. The device will not be accessible at the 
                    actual physical address, instead it will appear to be at 
                    the address returned by this function. 

    int _cdecl _x32_memlock(void *far pointer, unsigned int length) 

                    Locks a region of memory in a virtual memory 
                    environment. The far pointer points to the region of 


                                     -   30 -

                    memory to lock; the length is the size of the region in 
                    bytes. 

                    Returns: 0 if success, -1 if failure 

                    All code, data, and stack accessed by hardware interrupt 
                    handlers, or INT 1BH, 23H or 24H handlers must be locked 

                    using this function. This is only a requirement under 
                    systems where virtual memory is enabled, since in non 
                    virtual memory systems all memory is locked. This 
                    function can be called with or without a Virtual Memory 
                    manager; if there is no Virtual Memory manager present, 
                    the function will always return success. 

                    In C and C++ programs the sizeof() operator is the 
                    obvious choice for obtaining the length of static and 
                    global data items, but for code the size is a bit more 
                    obscure.  The following technique works with all 
                    compilers we have tested. 

                    #include "x32.h"
                    
                    void code_to_be_locked(void)
                    {
                      /* Real code rather than just a
                         simple comment should be here. */
                    }

                    int lock_my_code(void)
                    {
                      unsigned int len;
                      len = (char *)code_to_be_locked - (char *)lock_my_code;
                    
                      return _x32_memlock(code_to_be_locked,len);
                    }
                    int unlock_my_code(void)
                    {
                      unsigned int len;
                      len = (char *)code_to_be_locked - (char *)lock_my_code;
                    
                      return _x32_memunlock(code_to_be_locked,len);
                    }


    int _cdecl _x32_memunlock(void _far *pointer, unsigned int length) 

                    Unlock a region of memory (enable it for swapping to 
                    disk). 


                    Returns: 0 if success, -1 if failure 

                    Unlocks a region of memory for paging in a virtual 


                                     -   31 -

                    memory environment. This function can be called at any 
                    time, but is only effective if a virtual memory manager 
                    is running.  See the example for _x32_memlock(). 

    void _far _cdecl *_x32_mk_protected_ptr(void *address) 

                    Creates a protected mode selector The selector will be 
                    read/write with a base address = address (an absolute 
                    address) and a 4 Gbyte limit 

                    Returns: far pointer with 0 for an offset if successful. 
                    If failure, _x32_zero_base_selector is returned for the 
                    selector with a non-zero offset. 

                    The far pointer returned by this function is guaranteed 
                    to point to the linear address specified. The offset 

                    will = 0 if there are selectors available to allocate. 
                    If there are no more selectors, the offset will equal 
                    the requested address and the selector will equal 
                    _x32_zero_base_selector. 


    int _cdecl _x32_mouse_init(void (*_cdecl func)(int mask,int state,int 
                    x,int y),int mask) 

                    Installs the function 'func' as a mouse event handler 
                    (see Microsoft Mouse  Driver specifications for INT 33H 
                    subfunction 12H).  The programmer supplied  function 
                    'func' will be called for the event types specified by 
                    'mask'.   

                    The call to this function also makes a call to the mouse 
                    driver to show the  mouse cursor. 

                    Returns: 0 if failure (mouse driver not installed). 


                    See also the example code DEMOMOUS.C. 


    int _cdecl _x32_mouse_term() 

                    Terminate the use of the previously installed mouse 
                    event handler (installed  with _x32_mouse_init()).  This 
                    function must be called to accomplish a  graceful exit 
                    if _x32_mouse_init() was previously successfully called. 

                    Returns: 0 if failure (mouse driver not installed). 



   13.2  Global variables 



                                     -   32 -

   There are two global variables which will sometimes be of use to the X-32 
   programmer. 



    extrn __x32_zero_base_selector: word (For assembly language 
                    programmers) 

    extern unsigned short _cdecl _x32_zero_base_selector; (For C 
                    programmers) 

                    This contains a special selector which has a 4 Gbyte 
                    limit, read/write capability and a base address of 
                    absolute zero. This is primarily useful for accessing 
                    the first megabyte. 


    extrn __x32_zero_base_ptr: dword (For assembly language programmers) 

    extern void *_cdecl _x32_zero_base_ptr; (For C programmers) 

                    This is a near pointer to the real mode memory at 
                    absolute zero. It can be used with the default value of 
                    DS or SS to read or write to anything in the first 
                    megabyte, including video memory. It can also be used to 

                    convert DGROUP relative addresses to absolute addresses. 
                    Subtract _x32_zero_base_ptr from a DGROUP relative 
                    address to convert to an absolute address; add it to an 
                    absolute address to convert to a DGROUP relative 
                    address. For example, add B8000H to _x32_zero_base_ptr 
                    and use the results as a near pointer to a color video 
                    buffer. 




 
                       Chapter 14 - Using Virtual Memory
 


   Virtual Memory or VM is a method of increasing the apparent memory 
   available on a system by swapping blocks of memory to disk when they are 
   not needed. The 80386 processor has internal hardware support for virtual 
   memory systems, which can greatly increase the computing power available 
   on a given system. 

   The standard X-32 library, X32.LIB, does not implement a virtual memory 
   manager. This version of X-32 will utilize the virtual memory available 
   when operating under a DPMI host which supports VM but will only use 
   physical memory for other situations. 

   The library, X32V.LIB, contains the VM version of X-32 and can be 


                                     -   33 -

   substituted for X32.LIB to provide executables which have a built-in 
   virtual memory manager. When X-32VM programs are running under a DPMI 

   host, they will behave exactly as the non VM versions, since the DPMI 
   host handles all VM management in both situations. 

   One of the major disadvantages of writing applications which require VM 
   is the speed penalty taken for disk I/O whenever the application attempts 
   to access information which exists only on disk. The VM manager is 
   heavily optimized for speed and employs an algorithm which attempts to 
   swap out the least used blocks of memory. When a VM application is 
   executed on a machine with sufficient physical memory, the only speed 
   penalty is the time required to open the swap file at initialization and 
   delete the file at termination. 

   When running under X-32VM, the total memory available for code, data, and 
   stack will be approximately equal to the smallest of the following 
   quantities: 

   1.       Free disk space + size of code segment
   2.       Available physical memory * 256
   3.       3.5 Gbytes


   There can be only one swap file and one .EXE file open for VM use. For 
   very large disks, the maximum swap space will be limited by the maximum 
   partition size for DOS. The .EXE file can be placed in a separate 
   partition to overcome the limitations on partition size. Under DOS 5.0, 
   there can be up to 2 Gbytes/partition. By using separate partitions for 
   the executable and swap files, the 3.5 GByte limit of X-32VM can be 
   reached. 

   If the large memory requirement is predominantly due to code or static 
   data which exists in the .EXE file and is not altered during execution, 
   the X-32VM application may require very little swap file space. Any 4K 
   block of data or code which has been unaltered since reading from the 
   .EXE file will not need to be saved to the swap file and can be read 
   directly from the .EXE file when needed. This code and data will not take 
   up additional disk space beyond the space used in the executable itself. 
   This saves disk space and also increases speed by eliminating the need to 
   write the information to the swap file. 

   The programmer can, in some cases, improve the performance of a VM 
   program by organizing the program into sections such that commonly used 

   code and data fall in the same 4K blocks while seldom accessed code or 
   data fall in other 4K blocks. There can be a speed improvement if data 
   can be grouped according to read only versus read/write memory, since, if 
   only one byte of a 4K block is altered, the VM manager has to write the 
   entire block back to disk before that memory can be swapped. If the block 
   of data (static or global initialized data) has never been altered, it 
   will be accessed through the .EXE file. If it has not been altered since 
   it was last read from the swap file, it will not require saving to the 
   swap file to swap the block out. It is often impractical to organize data 


                                     -   34 -

   and code in the manner described, but, when possible, it can result in 
   dramatic speed improvements. 

   Speed of X-32VM applications can also be improved by reducing the file 
   fragmentation of the hard drive both for the .EXE file and the drive 
   where the swap file is to be opened. There are many utility programs 
   available which can reduce fragmentation. The use of disk caching 
   software is sometimes counter productive with VM programs. The disk cache 
   reduces the amount of physical memory available to X-32 which in turn 
   increases the need for swap file I/O. This effect will vary depending on 
   the characteristics of the application. 


   The swap file is opened when an X-32VM program is initializing the 
   system. The environment is searched for the variables: TEMP or TMP. The 
   first such variable found is used for the path of the swap file. If 
   neither variable is found, drives are checked for free space in 
   alphabetical order starting with C: and ending with the first invalid 
   drive encountered. The swap file is opened in the root directory of the 
   largest drive found. The file is always deleted when X-32 terminates. The 
   name of the swap file is determined by the 16-bit DOS call 5AH "Create a 
   temporary file" and is a unique pseudo random name based on the date and 
   time. 

   To control the location of the swap file, add a temp= or tmp= variable to 
   the environment string. For example to put the swap files in C:\SCRATCH 
   place the following statement in the AUTOEXEC.BAT: 

   set TEMP=C:\SCRATCH 

   This must be set prior to any conflicting TMP= statement since the first 
   found will be used. 


   If an error is encountered with the path specified in the first TMP or 
   TEMP path found, X-32 will revert to checking drives for maximum free 
   space to determine the drive to use. 

   The source .EXE file is always left open during program execution and is 
   used to read code and unaltered static data as required. Thus X-32VM 
   programs should not be run from a floppy disk but should always be 
   installed on a hard drive. The .EXE file does not need to be on the same 
   drive as the temporary swap file. 

   During initialization, X-32VM attempts to determine the available virtual 
   memory based on the free disk space and available physical memory. The 
   space is allocated on disk as it is actually needed, as opposed to when 
   the application allocates memory. It is possible for the application to 
   allocate a large block of memory while there is sufficient free disk 
   space, then reduce the amount of free disk space by writing a large file 
   to disk. X-32 could then be unable to support access to the full amount 
   of memory allocated. If this occurs, it results in the fatal error: FATAL 
   error during virtual memory disk I/O. This problem will only occur on 



                                     -   35 -

   systems where there is insufficient total disk space for the VM and file 
   I/O the application requires. This problem can be partially protected 
   against by taking the following steps: 

   1.      Call _x32_coreleft() prior to allocating memory. When running 
   under X-32VM, this function checks the available disk space and returns 
   the amount of memory which can actually be supported with the disk space 
   presently available. 

   2.      Initialize all allocated memory prior to writing large files to 
   disk. The disk space used for the swap file is only reserved after the 
   allocated virtual memory is initialized. 

   The above steps will not enable the program to run if there is 
   insufficient disk space available but will cause a recoverable error 
   during the application file I/O rather than an unrecoverable error during 
   X-32VM swap file I/O. 



 
                      Chapter 15 - Memory Mapped File I/O
 


   Memory Mapped File IO or MMFIO is a feature which is closely related to 
   Virtual memory and enables the programmer to map a file to memory using 
   the X-32VM Virtual Memory manager.  This operates only with X-32VM (not 
   X-32) and then only when running under a non-DPMI host where X-32VM is 
   allowed full access to the 80386 paging features. 

   Once a file is mapped into a location in memory, the application can 
   access any portion of that file simply by reading or writing to that 
   memory location.  The VM manager will read from disk only if the 
   application attempts to access a 4 kilobyte page which does not currently 
   exist in physical memory, it will write back to disk only if a portion of 
   the file has been modified but has not been recently accessed and the VM 
   manager has decided to use the physical memory containing that page for a 
   newly accessed location.  Thus the VM manager does all the work of 

   reading and writing to the file, and attempting to optimize the pages 
   kept in memory versus those swapped out based on which pages are most 
   frequently accessed. The application can simply behave as if the entire 
   file is present in memory and read and write at will to the linear 
   address in which the file is mapped.  This can dramatically speed up 
   development time and reduce the number of bugs encountered in the 
   application.  It also provides runtime optimization of which portions of 
   the file are kept in memory and often times results in a faster 
   application than would result without MMFIO. 

   Files are mapped to memory from C with the function: _x32_mmfio_open(int 
   fd, int size).  fd is a valid open file handle, size is the size in bytes 
   to map into memory.  This size can be smaller, equal to or bigger than 
   the actual file size.  The actual size used will be rounded up to a size 


                                     -   36 -

   evenly divisible by 4 kilobytes.  This function returns a near pointer to 
   the location corresponding to the start of the file or zero if there is 
   some type of error.  If X-32 fails the call, this function will attempt 
   to allocate sufficient memory to load the entire file directly into 
   memory, this will be much slower than true MMFIO but will allow the 
   application to run under DPMI if required.  The source code for the 

   function is supplied in case the programmer desires to optimize this 
   aspect of the feature.  The actual call to X-32 is done with int 21h, ax 
   = 350eh as shown in the X-32 function calls.  This call always fails 
   under DPMI or if the VM manager is not active (this will be discussed 
   later).  If the size is bigger than the file and portions of the memory 
   location above the top of the existing file are accessed, the file will 
   be expanded to the appropriate size.  Uninitialized portions of the file 
   will be undefined. 

   It is sometimes desirable for the application to insure that any changes 
   present only in memory be written to file.  This is done with the 
   function: _x32_mmfio_flush(void *) which is passed a pointer to a MMFIO 
   block to be flushed to disk.  This does not unmap the file but merely 
   insures that the disk is brought up to date with any changes which exist 
   only in memory.  This is commony used when the application is preparing 
   to terminate although applications protecting themselves against 
   unexpected termination may choose to flush data to disk after every 
   change.  This will not result in any disk IO if there have been no 
   changes made and when changes have been made, only those 4 kilobyte pages 
   affected are written to disk. 


   A maximum of 16 files can be mapped to memory.  Once a file is mapped in, 
   there is no way to unmap it, thus the total number of successful calls to 
   int 21h function 350EH is limited to 16. 

   To enable the use of MMFIO, the X-32VM Virtual Memory manager must be 
   active.  MMFIO cannot be used under DPMI such as Windows or OS/2.  The VM 
   manager is also normally turned off if upon initialization, X-32 
   determines there is more physical memory available than free disk space.  
   It will sometimes be desireable to use MMFIO on systems where there is 
   little free disk space, it these cases, the programmer can take steps to 
   insure the VM manager will be turned on if at all possible.  In the file, 
   options.asm, a variable called __x386_vm controls this characteristic.  
   If this variable is set to 0, the VM manager will always be off.  The 
   programmer would normally just use X-32 without VM rather than setting 
   __x386_vm to zero. If this variable is set to 1, X-32VM will attempt to 
   use the system which will result in the most memory available to the 
   application.  If there is very little free disk space, it will choose to 
   leave the VM manager off. If this variable is 2, X-32VM will have the VM 
   manager enabled unless there is a DPMI host or unless it is impossible to 

   open a swap file.  If the programmer desires to use MMFIO, this variable 
   should be set to 2 rather than the default value of 1. 

   Two more variables in options.asm are used to control how much linear 
   address space is reserved for various purposes.  Code and data reside in 


                                     -   37 -

   the lower memory block which starts at DS:0 and extends upward.  The 
   stack resides in the upper block which starts at DS:E0000000H and extends 
   downward.  MMFIO must fit between the top of the lower block and the 
   bottom of the upper block.  The location reserved for MMFIO is controlled 
   by two variables: 

   __x386_mmfio_bottom and __x386_mmfio_top 

   These are DGROUP relative pointers which determine what address location 
   will be reserved for MMFIO.  Most programmers will find the default 
   values acceptable, this allows 2 Gbytes for the lower block, 1 Gbyte for 
   MMFIO and 1/2 Gbyte for the upper block (stack).  The variables 
   controlling this apportionment can be found in the file options.asm along 
   with a detailed description of how and when these variables should be 
   adjusted. 


   The file demommfi.c demonstrates the use of the functions 
   _x32_mmfio_open() and _x32_mmfio_flush(). 




 
                    Chapter 16 - Writing and Using X-32 TSRs
 


   X-32 is a very compact DOS extender and lends itself well to use as a 
   TSR. 

   The example file, demotsr.asm demonstrates a simple TSR.  X-32 TSRs 
   require about 37 kbytes of conventional memory regardless of the amount 
   of 32 bit code and data. 

   The X-32 TSR must allocate all required memory during initialization and 
   when ready to "Terminate and Stay Resident", it must execute an int 21h 
   with ah = 31h.  There is no value passed in DX for function 31h as there 
   normally would be for 16 bit DOS, X-32 handles all extended and 
   conventional memory deallocation. 

   X-32 TSRs are NOT compatible with Windows in 386 enhanced mode.  They may 

   appear to function but are typically unreliable, especially if other 
   protected mode programs are in use. 

   X-32 TSRs are compatible with the DPMI host provided by 386MAX, and they 
   are compatible with DESQview. 

   Virtual memory X-32VM TSRs can be constructed but should not be used 
   unless absolutely required and then the programmer must insure that the 
   TSR is not activated during a time when disk I/O will cause DOS problems. 
   Remember, DOS is not re-entrant and the programmer has no way of knowing 
   when X-32VM may have to read or write from disk. 


                                     -   38 -


   To make the TSR as compact as possible, the programmer may want to reduce 
   the size of the DOS buffer used by the TSR.  The size of this buffer is 
   controlled by a variable in the file, "options.asm". When building TSRs, 
   __x386_dos_buffer_size should normally be reduced in size to 4 Kbytes 
   which is the minimum size allowed.  This will save 12 Kbytes over the 
   default value of 16 Kbytes resulting in a real mode memory footprint of 
   about 37 Kbytes for most X-32 TSRs. 



 
                              Chapter 17 - RUNOS2
 


   The RUNOS2 product included with X-32VM, converts 32-bit OS/2 version 2 
   executables into 'bound' (dual mode) executables.  Bound executables will 
   operate under both DOS and OS/2.  In this discussion, a DOS box within 
   OS/2 is considered to be DOS, running under OS/2 refers to execution of 
   the application within a text mode OS/2 window. 

   RUNOS2 supports only the compilers listed in the readme and then only 
   text mode applications.  There is no support for PM applications under 
   RUNOS2. 

   RUNOS2 works by installing a special stub executable on the front end of 
   the OS/2 executable.  If the application is run under OS/2, the OS/2 
   loader ignores the stub exe.  If the application is run under DOS, the 
   stub loader gets control and sets up a limited OS/2 environment on which 

   to run the OS/2 application. 

   The RUNOS2 stub exes are actually 32-bit applications built using the 
   X-32 and X-32VM DOS extenders.  Most of the discussion in this manual 
   relating to the DOS extender will also apply to RUNOS2 applications which 
   are running under DOS.  The application can determine if it is running 
   under DOS by using the OS/2 DOSQUERYSYSINFO function to obtain the "max 
   number of PM sessions".  This should equal zero when the application is 
   running under DOS and nonzero when running under OS/2.  If the 
   application is running under DOS, all the BIOS and DOS interrupts will 
   operate just as they normally do under X-32.  In addition to the 
   functions available through various interrupts, the more commonly used 
   text mode OS/2 functions are also supported.  A partial listing of these 
   functions follows: 

   DOSBEEP
   DOSSETFILEINFO
   DOSQUERYPATHINFO
   DOSQUERYHTYPE
   DOSDELETEDIR

   DOSGETDATETIME
   DOSDEVCONFIG


                                     -   39 -

   DOSEXIT
   DOSSETCURRENTDIR
   DOSSETFILEPTR
   DOSCLOSE
   DOSDELETE
   DOSDUPHANDLE
   DOSFINDCLOSE
   DOSFINDFIRST
   DOSFINDNEXT
   DOSCREATEDIR
   DOSMOVE
   DOSSETFILESIZE
   DOSOPEN
   DOSQUERYCURRENTDIR
   DOSQUERYCURRENTDISK
   DOSQUERYFILEINFO
   DOSREAD
   DOSWRITE

   DOSEXECPGM
   DOS32BEEP
   DOSALLOCMEM
   DOS32ALLOCSHAREDMEM
   DOS32GETNAMEDSHAREDMEM
   DOSFREEMEM
   DOS32SETMEM
   DOS32QUERYMEM
   DOSGETINFOBLOCKS
   DOSQUERYSYSINFO
   DOSSETEXCEPTIONHANDLER
   DOSUNSETEXCEPTIONHANDLER
   DOSSETSIGNALEXCEPTIONFOCUS
   DOS32SETRELMAXFH

   Many of these functions are not fully supported in all cases.  Some 
   examples: Functions which relate to file attributes do not support OS/2 
   extended attributes.  Nested exceptions are not supported.  DOSALLOCMEM 
   does not support guard pages. 


   There are four stub exes with the following characteristics: 

   name            approx size     Virtual memory          Floating Point support
   
   fstub1.exe      27249               No                      No
   fstub2.exe      29633               Yes                     No
   fstub3.exe      40892               No                      Yes
   fstub4.exe      43276               Yes                     Yes

   Floating point support is required for any application which uses floats 
   or doubles and is expected to run on a machine without a coprocessor. 

   The linkers which come with some compilers will not properly install the 
   stub exe on their own.  For those compilers which do not properly install 


                                     -   40 -

   RUNOS2 stub exe files, the utility "FBIND.EXE" can be used.  This is 
   itself a bound executable and is used to convert OS/2 exes to bound exes.
   The command line is as follows: 

   fbind exename stubname 


   exename is the name of the exe which is to be modified.  FBIND removes 
   the existing stub exe from this file and installs the stub exe identified 
   by the second command line parameter. 

   stubname is the name of the stub exe such as fstub1 OR it can be the name 
   of a complete OS/2 exe file, even a non dual mode file.  If the specified 
   file is a complete OS/2 executable rather just the stub, FBIND will use 
   just the stub portion of the exe.  In any case, this file is not modified 
   in any way. 

   The size of the target exe will be increased by the size of the new stub 
   minus the size of the original stub. 

   Another utility which can be useful is "RUNOS2.EXE".  This is a DOS only 
   application which will run text mode OS/2 applications under DOS.  The 
   command line parameters are as follows: 

   runos2 progname arg1 arg2 .... 

   Where progname is the name of a text mode OS/2 application.  arg1, arg2 

   etc are the command line arguments to be passed to the application.  This 
   program does not modify the OS/2 application in any way but merely loads 
   and executes it within DOS.  It provides a simple method of testing 
   compatibility of an application with the RUNOS2 stubs.  RUNOS2.exe is 
   equivalent to fstub4.exe which possesses both virtual memory and floating 
   point support. 

   There are several error messages which are specific to the RUNOS2 stub 
   exes and the RUNOS2 utility program.  These are as follows: 

   Insufficient memory. 

   There is not sufficient extended and conventional memory to load and 
   execute the program. 

   Invalid command line, must specify file name. 

   This message occurs only with the RUNOS2 utility, not with the stub exes. 
   This message occurs if you do specify a program name on the command line. 


   error opening or reading input file. 

   There was some type of DOS error while reading the OS/2 exe file.  In the 
   case of the RUNOS2 utility, the file probably was not found. 



                                     -   41 -

   Invalid input file 

   The exe file is not recognized as a valid OS/2 file. 

   Invalid fixup record 

   The exe file contains a fixup which is not recognized by RUNOS2.  This 
   may happen if the exe file was not created with a compiler which is 
   currently supported by RUNOS2. 

   Number of objects exceeds 5 

   At the time of this writing, RUNOS2 supports a maximum of 5 objects in 
   the OS/2 exe.  There will be more objects than this only if additional 
   data or code segments are added to the exe which are not a part of 

   DGROUP.  Inspecting the map file will usually identify the problem.  
   Including an assembly object module with the statement: 

   DGROUP  group segname1,segname2..... 

   where segname1,segname2 etc are the names of the additional segments will 
   usually fix the problem and should not interfere with operation of the 
   program under OS/2. 

   file contains a 16 bit object 

   16-bit code is not fully supported with RUNOS2.  There are some compilers 
   which have small portions of 16 bit code in their runtime libraries for 
   supporting ANSII and POSIX functions.  This IS supported.  Additional 16 
   bit code beyond that will probably result in this error message. 

   an unsupported OS/2 function was found 

   RUNOS2 supports only a subset of the DOS, VIO and KBD OS/2 functions.  It 
   should support all ANSII and POSIX functions for the compilers listed in 

   the readme. 

   out of selectors 

   There are some fixups which require the generation of new selectors.  
   These are very rare but if X-32 runs out of selectors to allocate, it 
   will exit with this message.  This normally is associated with 16 bit 
   code or data.  Try to keep everything in 32 bit code and data segments. 

   Error allocating or setting video buffer selector 

   This indicates that no selectors are available for use with the video 
   buffer. This can only happen if all selectors have been allocated prior 
   to the first call to the OS/2 function, "VIOGETBUF". 

   Display is in graphics mode which is incompatible with this application 



                                     -   42 -

   The display must be in text mode if the applicaiton uses the OS/2 
   functions, VIOGETBUF or VIOWRTCHARSTRATT. 


   A nested exception has occurred which is not supported 

   The application has hooked OS/2 exceptions and has caused a nested 
   exception to occur.  This usually means that the application's exception 
   handler has caused an exception of some kind. 

   In addition to the error messages above, all the normal X-32 error 
   messages can occur including a register dump for applications which do 
   not hook exceptions. 



 
                          Chapter 18 - Error Messages
 


   The following error messages can occur only when X-32 is attempting to 
   initialize the system and is preparing to load and run the 32-bit 
   application. In all cases the error is fatal, and X-32 attempts to 
   restore the system and return control to 16-bit DOS. 

   Fatal error, 80386 processor is required 

   Indicates that an 80286 or below was detected (X-32 requires an 80386 or 
   above). 

   Previously installed software is neither VCPI nor DPMI compatible 

   A TSR has been run on the machine which has left the processor in "V86 
   mode." That TSR does not support the VCPI or the DPMI standard, and so 

   X-32 has no way of executing a 32-bit application.  This TSR may be an 
   expanded memory emulator or some other device which accesses extended 
   memory. Remove any suspicious items from the CONFIG.SYS and AUTOEXEC.BAT 
   files, reboot the machine, and test the program again. 

   Cannot enable the A20 line, XMS memory manager required 

   This message indicates that no XMS, VCPI or DPMI host is present, so X-32 
   was attempting to enable the A20 line without the help of a host and has 
   failed. This is an indication that the computer has non standard A20 
   enabling hardware.  Installing an XMS memory manager such as Microsoft 
   HIMEM.SYS will solve this problem. 

   Insufficient conventional memory to run program 

   There is insufficient conventional memory for the 16-bit code, data, 
   stack, and required X-32 data structures. X-32 programs normally require 
   a minimum of about 38 kbytes of conventional memory. 


                                     -   43 -


   Insufficient extended memory to run program 


   There is insufficient extended memory for the 32-bit code, data, stack 
   and required X-32 data structures. 

   16-bit code is too large 

   X-32 increases the size of the 16-bit code segment at run time to hold 
   additional instructions required. During this process, the 16-bit code 
   segment has exceeded 64 kbytes. This can only happen if large amounts of 
   user-supplied 16-bit code are linked into the application. 

   Fatal error allocating DOS memory 

   This message is printed out in two situations: if there is some type of 
   error when allocating DOS memory with DOS function 4AH, or if the sum of 
   16-bit code, data and stack exceed 64 Kbytes. The second situation can 
   only occur if a large amount (about 35 Kbytes) of user supplied real mode 
   code and data is linked in to the application. 

   Fatal error reading disk 


   Indicates that X-32 was unable to read its own executable file from disk. 

   Fatal error, DPMI host does not support 32-bit applications 

   This message means that the DPMI host installed on the machine does not 
   support 32-bit applications. 

   DPMI failed to enter protected mode 

   The DPMI host has failed to switch the processor to protected mode as 
   requested by X-32. 

   The following error messages may occur during initialization, while the 
   32-bit application is executing or when the application attempts to 
   terminate. In all cases, X-32 terminates the program and returns control 
   to 16-bit DOS. 

   'XMS error # X' where X is a XMS error number. 


   This message means that a call to the XMS memory manager has failed. This 
   may be a call to enable the A20 line in the computer or some other 
   required function call. 

   FATAL error, XMS memory corrupted 

   This message can only occur if the application has used INT 21H AH = 4BH 
   to spawn another application. This indicates that the spawned application 
   has not restored the XMS memory to the original state, and X-32 must 


                                     -   44 -

   terminate the application. 

   DPMI operating system error 

   Any fatal failure of the DPMI host, other than the one mentioned above, 
   will cause this message to be output to screen. 

   Fatal error making VCPI call 

   The VCPI host has returned an error in response to a required function 
   call. Try rebooting the computer to see if previously run programs have 

   damaged the VCPI host. Try removing the VCPI host from the CONFIG.SYS or 
   AUTOEXEC.BAT files. 

   FATAL error during virtual memory disk I/O 

   This message only occurs if X-32VM is in use and indicates that some type 
   of DOS error has occurred while running the application. This error most 
   commonly occurs when the program is executed with the .EXE file on a 
   floppy disk and the floppy disk is removed. The X-32 virtual memory 
   manager occasionally reads portions of the code from the .exe file.  It 
   is recommended that VM programs always be installed on a hard drive and 
   never be executed directly from a floppy drive. 

   Register dump 

   There are a variety of messages associated with fatal processor 
   exceptions. These are usually caused by illegal pointers or some type of 
   illegal memory access. This includes, but is not limited to, attempting 
   to execute data instead of code. The following example demonstrates an 
   attempt to write to the code segment. 


   include mac32.asm
   
   start32code
   public _main
   _main   proc    near
           mov     eax,1           ;define all registers
           mov     ebx,2
           mov     ecx,3
           mov     edx,4
           mov     esi,5
           mov     edi,6
           mov     ebp,7
           mov     cs:[0],eax      ;attempt to write to the code segment
   _main   endp
   end32code
   end
   
   The output from the previous program is as follows:
   



                                     -   45 -

   INTERRUPT 0DH, GENERAL PROTECTION FAULT possible illegal address
   error code = 0000
   eax = 00000001  esi = 00000005          flags = 3246    ds = 0033
   ebx = 00000002  edi = 00000006          eip = 0000038B  es = 0033
   ecx = 00000003  ebp = 00000007          cs = 002B       fs = 0000
   edx = 00000004  esp = DFFFEDF0          ss = 003B       gs = 0000

   The attempt to write to the code segment causes the processor to execute 
   a general protection fault. The default handler for general protection 
   faults dumps all registers. The programmer can often inspect the EIP 
   value in the register dump and compare it to the information in the .MAP 
   file to pinpoint the instruction which caused the problem. 


                                    INDEX

                                            .....R..... 
                                            Real mode memory 22, 35, 42 
   .....E.....                              
   Exceptions 66                            .....T..... 
                                            TSR 104-106 
   .....H.....                              
   Help,                                    .....W..... 
     online 8                               Windows 6, 12, 58, 76-77, 102, 
                                                105 
   .....I.....                              
   Interrupt handlers 60-64                 ....._..... 
   Interrupt handlers,                      _x32_allcoreleft 80 
     hardware 64                            _x32_coreleft 81 
                                            _x32_free_protected_ptr 82 
   .....O.....                              _x32_get_abs_address 82 
   OS/2 6, 102, 107, 112, 114-116           _x32_map_physical_address 83 
                                            _x32_memlock 84 
        

   _x32_memunlock 86 
   _x32_mouse_init 88 
   _x32_mouse_term 89 
   _x32_zero_base_ptr 90 
   _x32_zero_base_selector 89 
















                                     -   46 -

                                   the end 
