
 (C) Copyright 1992, Qualitas, Inc. All Rights Reserved

 Qualitas C/C++ Libraries for DPMI

 ** Contents of this file **

 1. Introduction
 2. Requirements
 3. Limitations
 4. Building the Example Programs
 5. Building the DPMI libraries
 6. Using the C library
 7. Using the C++ class library

 **********************************************************************

 1. Introduction

 The Qualitas C/C++ Libraries for DPMI provide the C/C++ programmer with
 easy access to DPMI.  The purpose of the libraries is to allow developers
 to gain familiarity with DPMI concepts, to aid in understanding of how
 DPMI can be used, and to serve as a platform for experimentation with
 DPMI hosts via the creation of small model C/C++ applications.

 The directories are structured as follows:

				     top
				      |
	  +------------------+--------+---------+------------------+
	  |		     |			|		   |
	 BC2		    BC3		       MSC6		 MSC7
	  |		     |			|		   |
  +-------+------+   +-------+------+	+-------+------+   +-------+------+ 
  |    |    |	 |   |	  |    |    |	|    |	  |    |   |	|    |	  |
 LIB   |    |	 |  LIB	  |    |    |  LIB   |	  |    |  LIB	|    |	  |
    LIBSRC  |	 |     LIBSRC  |    |	  LIBSRC  |    |     LIBSRC  |	  |
	 INCLUDE |	    INCLUDE |	       INCLUDE |	  INCLUDE |
	      EXAMPLES		 EXAMPLES	    EXAMPLES	      EXAMPLES

 For convenience of installation, the distribution provides a copy of the
 source code for the libraries and examples in each compiler directory.
 The primary differences between compiler directories are (1) the makefiles
 differ, and (2) some of the source code distributed with the compilers
 has been modified for operation in conjunction with the DPMI libraires,
 and those modules are included in their respective directories.

	\		this README file
	.\MSC7LIB	Microsoft C/C++ 7.0 support
	.\MSC6LIB	Microsoft C 6.0 support
	.\BC3LIB	Borland C/C++ 3.0 support
	.\BC2LIB	Borland C/C++ 2.0 support
	.\xxx\LIBSRC	Source code for the DPMI libraries
	.\xxx\INCLUDE	Header files required for source and examples
	.\xxx\EXAMPLES	Source code for the example programs
	
 For example, to install the DPMI libraries for Borland C++ version 3.0,
 use the following commands:

	C> md DPMILIBS
	C> cd DPMILIBS
	C:\DPMILIBS> cd a:\BC3LIB
	C:\DPMILIBS> xcopy a: /s

 **********************************************************************

 2. Requirements

 The requirements depend upon which C compiler you are using:

 Compiler	Processor	Assembler	Linker		Librarian
 --------	---------	---------	------		---------
 Microsoft v7	80386		MASM 6.0	LINK 5.30	LIB 3.20
 Microsoft v6	80286		MASM 6.0+5.1	LINK 5.10	LIB 3.18
 Borland v3	80286		TASM 3.0	TLINK 5.0	TLIB 3.02
 Borland v2	80286		TASM 2.5	TLINK 4.0	TLIB 3.02

 NOTE:	The librarian and assembler are only required if you wish to 
	rebuild the libraries.	They are not required to build the
	examples or other applications.

 Each compiler requires the small model libraries.  The Microsoft compilers
 require the include files for the startup source to rebuild the DPMI
 libraries.

 **********************************************************************

 3. Limitations

 * Only small model is supported.  It is possible, however, to use
   far pointers that are dynamically created, for example, by using
   the MK_FP macro.  Statically initialized far pointers will work
   only if they refer to the default code segment or the default data
   segment.

 * Floating point is not supported.  The run-time requirements create
   complications which are beyond the scope of this implementation.

 * Errors may result from use of run time library functions that require
   running clean-up routines at exit time, if such clean-up routines 
   utilize real mode far pointers created prior to entering protected mode.

 **********************************************************************

 4. Building the Example Programs

 The .\EXAMPLES directory contains the following programs:

 * HELLO.C   A simple C program that illustrates how to enter 
	     protected mode, and how to call the DPMI C library.
 * QSORT.C   A program that sorts stdin to stdout. It uses DPMI to
	     allocate buffers, which enables it to sort very
	     large files in memory.
 * XMEM.CPP  A C++ program that illustrates the use of the classes in
	     the DPMI class libraries that are related to memory usage.
 * XINTR.CPP A C++ program that illustrates the use of the classes in
	     the DPMI class libraries that are related to interrupt handling.
 * XREAL.CPP A C++ program that illustrates the use of the classes in
	     the DPMI class libraries that are related to calling real
	     mode functions and real mode call-backs.
 * XCEPT.CPP A C++ program that illustrates the use of the classes in
	     the DPMI class libraries that are related to exception handling.

 More detail about using the libraries and classes is found below.


 ** To build the examples with Microsoft C 7.0

	- Your current directory must be the MSC7\EXAMPLES directory.
	- CL and LINK for MS C 7.0 must be on your path.
	- Your INCLUDE and LIB environment variables must be set for
	  a normal MS C 7.0 build.
	- Issue the command NMAKE.

 ** To build the examples with Microsoft C 6.0

	NOTE: the C++ examples are not built for MS C 6.0

	- Your current directory must be the MSC6\EXAMPLES directory.
	- CL and LINK for MS C 6.0 must be on your path.
	- Your INCLUDE and LIB environment variables must be set for
	  a normal MS C 6.0 build.
	- Issue the command NMAKE.

 ** To build the examples with Borland C++ 3.0

	- Your current directory must be the BC3\EXAMPLES directory.
	- BCC and TLINK for BC++ 3.0 must be on your path.
	- The BC3LIB variable in the MAKEFILE must be set to the
	  directory where your Borland C++ libraries
	  reside.  Edit MAKEFILE and locate the BC3LIB line.
	  Change the right hand side of the statement
	  as needed for your environment.
	- Issue the command MAKE.

 ** To build the examples with Borland C++ 2.0

	- Your current directory must be the EXAMPLES directory.
	- BCC and TLINK for BC++ 2.0 must be on your path.
	- The BC2LIB variable in the MAKEFILE must be set to the
	  directory where your Borland C++ libraries
	  reside.  Edit MAKEFILE and locate the BC2LIB line.
	  Change the right hand side of the statement
	  as needed for your environment.
	- Issue the command MAKE.

 **********************************************************************

 5. Building the DPMI libraries

 It is not necessary to rebuild the libraries in order to build the examples
 or to build other applications.  However, in the event you wish to modify
 the library source code, the following instructions are provided for 
 rebuilding the libraries:

 ** To build the libraries with Microsoft C 7.0

	- Your current directory must be the MSC7\LIBSRC directory.
	- CL, MASM (v6.0) and LINK for MS C 7.0 must be on your path.
	- Your INCLUDE and LIB environment variables must be set for
	  a normal MS C 7.0 build.
	- The C7DIR variable in the makefile must
	  be set to the directory where your Microsoft C++ distribution
	  resides.  Edit MAKEFILE and locate the C7DIR= line
	  Change the right hand side of the statement
	  as needed for your environment.
	- Issue the command NMAKE.

 ** To build the libraries with Microsoft C 6.0

	NOTE: the C++ libraries are not built for MS C 6.0

	- Your current directory must be the MSC6\LIBSRC directory.
	- CL and LINK for MS C 6.0 must be on your path.
	- Your INCLUDE and LIB environment variables must be set for
	  a normal MS C 6.0 build.
	- The C6DIR variable in the MAKEFILE must
	  be set to the directory where your Microsoft C distribution
	  resides.  Edit MAKEFILE and locate the C6DIR= line.
	  Change the right hand side of the statement
	  as needed for your environment.
	- The ASM51 variable in the MAKEFILE must be set for the
	  5.1 version of MASM in order to assemble the MS C 6.0 startup code.
	- Issue the command NMAKE.


 ** To build the libraries with Borland C++ 3.0

	- Your current directory must be the BC3\LIBSRC directory.
	- BCC, TASM, and TLINK for BC++ 3.0 must be on your path.
	- Issue the command MAKE.

 ** To build the libraries with Borland C++ 2.0

	- Your current directory must be the LIBSRC directory.
	- BCC, TASM, and TLINK for BC++ 2.0 must be on your path.
	- Issue the command MAKE.


 **********************************************************************

 6. Using the C Library

 There is roughly a one-to-one mapping between the set of DPMI version 0.9 
 functions and the entry points of the DPMI C library.	A copy of the
 DPMI specification may be a useful reference.

 A set of three higher level calls is provided for simplified memory
 management. DPMImalloc(), DPMIresize(), and DPMIfree() provide a means
 to allocate and address blocks of memory allocated from DPMI.	These
 calls are implemented using lower level calls of the library.	Refer to
 DPMIMALL.C in the LIBSRC directory to see how.

 Definitions for the C library are found in INCLUDE\DPMI.H.  This file
 contains structure definitions, function prototypes, and descriptions
 of each call.

 Two example programs are provided in the EXAMPLES directory. HELLO.C shows
 basic usage: how to enter protected mode and call a library routine.
 QSORT.C is a useful utillity for sorting large files, which is built
 using the DPMImalloc family of memory management routines.


 **********************************************************************

 7. Using the C++ Class Library

 
 The class library provides a set of C++ classes that abstract several
 important DPMI objects.  The header files provide descriptions and
 brief examples on using the classes.

 Definitions for the DPMI classes are contained in these header files,
 found in the INCLUDE directory:

	DPMIHOST.H	DPMI host class
	SEGMENT.H	All memory related classes
	REALPROC.H	Real procedure class
	REALINT.H	Real interrupt class
	CALLBACK.H	Real mode call-back class
	DPMIINT.H	Interrupt handler classes
	EXCEPTIO.H	Processor exception handler class


 General note: Many of the constructors make calls to DPMI.  Since the
 constructors for statically allocated class instances are called prior
 to main(), declaring instances from the DPMI classes statically may
 result in attempting to call DPMI prior to entering protected mode,
 which probably will cause a system crash.  Therefore, DPMI objects
 should be created dynamically, either as automatics (on a function's
 local stack frame) or with the new operator.  It is certainly allowable
 to statically allocate pointers to DPMI objects, and initialize them
 after entering protected mode in order to gain global addressability.

 *--------------------------------------------------------------------

 Header File: DPMIHOST.H  See examples XMEM, XREAL, XCEPT, XINTR

 Classes	Members
 -------	-------
 DPMIhost
		DPMIhost		Constructor; checks presence of	a
					DPMI host, and gathers needed 
					host information.
		getStatus		Used to verify detection of DPMI host
		enterProtectedMode	Makes the initial switch into 
					protected mode.
		getProcessor		Returns processor code
		getVersion		Returns major and minor version.
		getSelectorDelta	Returns delta between consecutive
					selectors.

 When you declare a DPMIhost object in your program, the constructor tests
 for the presence of a DPMI host on your system.  If found, the constructor
 collects information from the host needed for the switch into protected mode.

 You can determine the success or failure of the constructor by calling the
 getStatus member, which returns DPMIok if the constructor found a DPMI host
 and was successfully initialized.  The members getProcessor, getVersion,
 and getSelectorDelta simply return the appropriate host parameters.

 You use the member enterProtectedMode for the initial switch into protected
 mode, and after that it should not be called.	It first allocates a block
 of DOS memory to satisfy the host's private data area requirements.  It then
 calls the mode switch entry point obtained by the constructor.	 When this
 returns, the processor is running in protected mode.  Finally, this member
 relocates the executable image for protected mode operation (see 
 Implementation Notes for details).  The enterProtectedMode member returns
 TRUE if the processor is successfully switched to protected mode. 
 *--------------------------------------------------------------------

 Header File: SEGMENT.H	 See example XMEM

 Classes	Members
 -------	-------
 Block
		Block			Constructor; allocates raw block
					of memory from DPMI.
		~Block			Destructor; release block to DPMI
		setSize			Resize function reallocates block.
		blockHandle		Returns block handle.
		blockSize		Returns block size.
		blockBase		Returns base linear address.

 AbstractSegment
		segmentSize		Returns size of segment.
		segmentBase		Returns base of segment.
		resize			Resizes segment.
		move			Changes segment base.
		operator+		Adds a segment property.
		operator-		Removes a segment property.
		queryProp		Tests for segment property.
 Segment
 : AbstractSegment
		Segment(void)		Generic constructor.
		Segment(selector_t)	Specific descriptor.
		Segment(AbstractSegment&) Alias to existing segment.
		~Segment(void);		Releases descriptor to DPMI.

 
 CommonRealSegment
 : AbstractSegment
		CommonRealSegment	Constructor

 DOSMemory
 : AbstractSegment
		DOSMemory		Constructor
		~DOSMemory		Destructor


 HugeSegment
 : AbstractSegment
		HugeSegment		Constructor
		~HugeSegment		Destructor

 MemorySegment
 : AbstractSegment, Block
		MemorySegment(uShort)	Constructor
		MemorySegment(uShort,	Constructor for specific descriptor
			selector_t)
		~MemorySegment		Destructor-frees block and descriptor



 HugeMemorySegment
 : HugeSegment, Block


 The various memory classes provide structured access to DPMI's memory
 management facilities.

 Note that of the classes defined, DOSMemory, MemorySegment, and 
 HugeMemorySegment are unique in they that combine allocated memory and 
 descriptors to address that memory.  The other classes are either
 just memory (e.g. Block) or just descriptors (e.g. Segment, HugeSegment).
 The distinctions between memory and descriptors are important to 
 the conceptualization of the memory classes.

 *......................................................................

 The Block class corresponds to raw memory blocks allocated via DPMI
 function 501h.	 Defined operations on Blocks are limited to resize,
 and free.  The class also supports query of the DPMI memory handle and
 query of the block size. Blocks by themselves are not especially useful,
 since the memory is not addressable.  Its purpose is to serve as a 
 base class for MemorySegment and HugeMemorySegment.

 *......................................................................

 AbstractSegment is an abstract class the corresponds to a descriptor.
 DPMI provides several means to create descriptors, all with slightly
 different behavior. Although most members of AbstractSegment are pure,
 a key member that it does implement is ptrTo, which returns a far
 pointer that may be used to reference the linear address space that
 the corresponding descriptor points to.

 *......................................................................

 The SegmentProperty enumeration provides an elegant means to add,
 remove, and query properties of instances of derived classes of the
 AbstractSegment class. It is defined as:

	typedef enum SegmentProperty
	{
		present,
		executable,
		readable,
		writable,
		big,
		expandDown
	} SegmentProp_t;

 The AbstractSegment class defines members operator+(SegmentProp_t) and
 operator-(SegmentProp_t) that allow properties to be added and removed
 with a natural syntax. For example, the code

	Segment mySeg();
	mySeg - present;

 creates a descriptor and makes it not present.
 
 *......................................................................

 The Segment class corresponds to generic, fully modifiable LDT descriptors.
 There are three DPMI services that create such descriptors, and thus
 three constructors for the class. The first corresponds to DPMI function
 0000, which allocates a descriptor.  The second corresponds to DPMI
 function 000Dh, which allocates a specific descriptor.	 The last
 constructor corresponds to DPMI function 000Ah, which creates an alias
 (same base and limit) to an existing segment.

 The default properties are present and writable. New Segments have a
 base of zero and a size of one.

 The argument for the resize method is in bytes, and size==0
 specifies a size of 64 KB.

 Specific descriptors must be in the range 04h to 0FFh.

 *......................................................................

 The CommonRealSegment class corresponds to descriptors created by DPMI
 function 0002h, Convert Paragraph to Selector.	 The intended use of 
 this function is to provide addressability from protected mode for
 commonly accessed regions of the low megabyte, such as the BIOS data
 area (paragraph 40h) or the video display area (B800h etc.).

 DPMI restricts modification of descriptors created by function 0002h, and
 therefore the resize, move, operator+(SegmentProp_t) and 
 operator-(SegmentProp_t) members all return FALSE. Other members are 
 inherited from AbstractSegment.

 *......................................................................

 The DOSMemory class corresponds to DPMI function 100h.	 The constructor
 uses this function to allocate a block of memory from DOS.  DPMI provides
 a descriptor that allows addressability of the block from protected mode.
 The argument to the constructor is the desired size in paragraphs of the
 block.	 If the allocation fails, the ptrTo function returns zero. The
 paragraph address of the block is available via the segmentBase(void)
 member inherited from AbstractSegment.	 

 This code fragment:

	DOSMemory d(0x100);

	char far *p = (char far *)d.ptrTo();

 allocates 0x100 paragraphs of DOS memory and initializes a pointer to
 it.  If the allocation fails, p is a null pointer.

 The resize member for DOSMemory maps to DPMI function 102h, Resize DOS
 Memory Block; similarly, the destructor maps to function 101h, Free
 DOS Memory Block. Because DPMI restricts any other modifications to these
 blocks, the operator+(SegmentProp_t), operator-(SegmentProp_t), and move
 members all return FALSE;

 *......................................................................

 A HugeSegment corresponds to a set of consecutive descriptors, which are
 set up to span a address region of arbitrary size. The difference between
 the base addresses of consecutive descriptors is 64 KB. The argument to 
 the constructor is the size in bytes of the segment.

 For example, the code fragment

	HugeSegment  h(0x28000);
	h.move(0x500000);

 results in the allocation of three descriptors having sizes of 0x10000,
 0x10000, and 0x8000.  Calling the move member sets the base addresses
 of the three descriptors to 0x500000, 0x510000, and 0x520000.

 Operations on a HugeSegment, such as adding or removing a property,
 affect all of its component descriptors.  Likewise, the destructor
 frees all the descriptors.

 HugeSegments cannot be grown if the new desired size requires additional
 descriptors to span the region.  Shrinking a HugeSegment frees descriptors
 no longer required to span it, and these cannot be reallocated to 
 return the segment to its original size.

 *......................................................................

 A MemorySegment is a generic descriptor mapped to a block of DPMI memory.
 The size is restricted by the argument to the constructor to be 64 KB 
 or less.  Declaring a MemorySegment results in allocating both memory
 and a descriptor initialized to address that memory.  

 The code fragment

		MemorySegment m(0x500);
		short far *p = (short far *)m.ptrTo();

 makes p a far pointer to a 0x500 byte block of memory.	 An zero argument
 to the constructor requests a 64 KB block.

 An alternate constructor allows the MemorySegment to be allocated with
 a specific descriptor.

 The resize and property members work as expected, but the move member 
 returns FALSE since DPMI provides no means to move memory blocks.
 

 *......................................................................

 A HugeMemorySegment is to a MemorySegment as a HugeSegment is to a
 Segment.  A HugeMemorySegment is an arbitrarily large block of memory
 with a set of consecutive descriptors that are set up to span it.  The
 argument to the constructor is the size in bytes. As for MemorySegment,
 the move member returns FALSE.


 HugeMemorySegments cannot be grown if the new desired size requires 
 additional descriptors to span the region.  Shrinking a HugeMemorySegment
 frees descriptors no longer required to span it, and these cannot be 
 reallocated to return the segment to its original size.

 *--------------------------------------------------------------------

 Header File: REALPROC.H  See example XREAL

 Classes	Members
 -------	-------
 RealProcedure
		RealProcedure		Constructor.
		operator()		Call operator invokes real
					mode procedure.
		operator char()		char cast extracts char return
					value from real mode call structure.
		operator int()		int cast extracts int return
					value from real mode call structure.
		operator long()		long cast extracts long return
					value from real mode call structure.

 Applications occasionally require certain routines that cannot run in 
 real mode, and must switch to real mode to execute them.  The RealProcedure
 class corresponds to DPMI mechanisms for invoking (far) functions in 
 real mode.

 The arguments for the constructor are the address of the real procedure
 and the number of bytes of arguments the real procedure expects on the
 stack when it is invoked.  The second argument is optional and defaults 
 to zero.  The procedure address is near, and is assumed to be in the
 default code segment.

 To invoke the RealProcedure, simply call it.  The class overrides the
 call operator, and the member operator() makes the necessary DPMI calls
 to run the registered routine in real mode.  The stack arguments, if any,
 are copied to the stack on which the real mode routine is invoked.
 
 The class ensures that DS==SS when the real mode routine executes. Thus
 the real mode routine can use any near pointers.

 An additional feature of the RealProcedure class is the ability to
 return values.	 Each class instance contains a dpmiRegs_t structure that
 is used to pass the register state between modes. Upon return to protected
 mode, this structure holds the register state in effect when the real
 routine returned, and return values are contained therein.  To access
 them, use the cast override members: operator char(), operator int() and
 operator long().  Refer to the example code to see exactly how this is done.
 *--------------------------------------------------------------------

 Header File: REALINT.H	  See example XREAL

 Classes	Members
 -------	-------
 RealInterrupt
		RealInterrupt		Constructor
		operator()		Call operator invokes interrupt
					handler in real mode.

 A RealInterrupt corresponds to the DPMI function that switches to 
 real mode and issues a particular interrupt after installing a
 register state supplied by the caller.	 Unlike the RealProcedure class,
 the interface to RealInterrupt assumes register-based argument passing.

 The argument to the constructor is the number of the interrupt, from
 zero to 255.  The member operator() (call override operator) is used
 to invoke the real mode interrupt.  This requires a dpmiRegs_t 
 structure as an argument.  Upon return from the interrupt, the dpmiRegs_t 
 structure reflects the register state at the time the real mode
 interrupt handler returned.

 The caller is required to set up the dpmiRegs_t structure that is passed
 when the real interrupt is called.  This includes setting up the stack
 (SS:SP fields) and the flags.	Setting SS and SP requests the DPMI host
 to provide a stack.  The flags value should be chosen carefully, 
 especially with regard to the Interrupt Enable bit and the Trace bit.

 An example of using a RealInterrupt is included in the TestCallBack function
 of the XREAL.CPP example.

 *--------------------------------------------------------------------


 Header File: CALLBACK.H  See example XREAL

 Classes	Members
 -------	-------
 CallBack	CallBack		Constructor; allocates real mode 
					call-back and initialize register
					structure; saves callback address
		~CallBack		Destructor; releases call back.
		getCallBackAddress	Returns the call back address.

 DPMI provides a mechanism for call protected mode routines from real
 mode.	Unfortunately, the mechanism is rather complex.	 The CallBack
 class goes a long way in simplifying the task of using real mode
 call backs.

 The single arguemnt to the constructor is the address of the call back
 handler.  The call back handler is a routine that called from real mode
 but runs in protected mode.  The class includes dispatching logic that
 allows you to code the handler as a normal near routine, and allows you to
 assume that DS==SS.  The argument to the handler is a dpmiRegs_t
 structure that reflects the interrupt state at the time of the call.
 Any changes the handler makes to this structure are reflected in the
 register state when the handler returns to real mode.


 The handler should only be invoked by calling to the far callback address
 from real mode.  The callback address is obtained with the getCallBackAddress
 member.  See function TestCallBack in the XREAL example.

 *......................................................................

 Header File: DPMIINT.H	 See examples XINTR, XREAL

 Classes	Members
 -------	-------
 InterruptHandler
		InterruptHandler	Constructor; hooks interrupt.
		~InterruptHandler	Destructor; unhooks interrupt.
		callPrevious		Invokes previous handler.

 RealInterruptHandler
 : InterruptHandler
		RealInterruptHandler	Constructor; hooks real interrupt.
		~RealInterruptHandler	Destructor; unhooks real interrupt.


 The interrupt handling classes make it very easy to create handlers for
 hardware and software interrupts, both in real mode and protected mode.
 The class handles all the necessary interaction with DPMI for saving the
 previous vector, and for setting the interrupt vector to the user's handler.

 The class further simplifies creation of handlers by allowing the user
 to code the handler as a normal function, i.e, the user does not need to
 declare it as _interrupt, and may assume that DS==SS, which is important
 in small model programs.  This is accomplished by the interrupt dispatching
 logic built into the class (see Implementation Notes for more information).
 
 The arguments to the constructor are the interrupt number and the address
 of the routine that the user assigns to handle the interrupt. The argument
 to the user's handler routine is a dpmiRegs_t structure, which reflects
 the register state at the time the interrupt occurs.  Changes to the
 dpmiRegs_t argument made by the handler are reflected as changes to the 
 register state when the handler returns.  For example, a software interrupt 
 handler that returns a value in AX modifies the drAX field of the dpmiRegs_t
 argument. Likewise, a hardware interrupt handler which preserves the 
 register state must make no changes to the dpmiRegs_t argument.

 The member callPrevious is useful for calling the handler that was in
 effect when the user handler was created.  The argument to callPrevious
 is a pointer to the dpmiRegs_t structure, which is necessary in order 
 to present the correct register state to the previous handler.	 Any changes
 made to the register state by the previous handler are reflected in the
 dpmiRegs_t structure upon return. RealInterruptHandlers inherit this member.

 The destructors restore the previous vector.

 *--------------------------------------------------------------------

 Header file: EXCEPTIO.H  See example XCEPT

 Classes	Members
 -------	-------
 ExceptionHandler
		ExceptionHandler	Constructor. Hooks exception.
		~ExceptionHandler	Destructor. Restores vector
		callPrevious		Invokes previous handler.

 The ExceptionHandler class makes it easy to create handlers for 
 processor exceptions, such as General Protection violation, Segment
 Not Present, etc.  As with InterruptHandlers, the class provides
 dispatching such that handlers run with DS==SS, so that all near pointers
 are valid, and handlers are declared as normal near functions.

 The arguments to the constructor are the exception number to handle,
 and the address of the handler.  The class takes care of calling DPMI
 to get the previous vector and set up for the new handler.

 When the handler is invoked, there are two structures passed to it. The
 first is a dpmiRegs_t structure which reflects the register state at 
 the time the exception occurred.  The second is the DPMI exception
 frame, which gives the CS:IP, SS:SP, and flags at the time of the 
 exception.  The handler may modify fields in the register structure
 and/or the exception frame structure, and such changes are observed 
 by the host when the handler returns.

 The member callPrevious is used to invoke the previous handler for the
 exception.  It is passed pointers to the register state and to the 
 exception frame, which it may modify.
