add directory study

This commit is contained in:
gohigh
2024-02-19 00:25:23 -05:00
parent b1306b38b1
commit f3774e2f8c
4001 changed files with 2285787 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,70 @@
<h2>Obtain Real-to-Protected Mode Switch Entry Point
<img src="../0.9.gif" alt="[0.9]" width=22 height=17></h2>
This function can be called in real mode only to test for
the presence of a DPMI host, and to obtain an address of a
mode switch routine that can be called to begin execution in
protected mode. <p>
<b>Call With</b><br>
<img src="r/ax.gif" alt="" width=245 height=59><br>
AX = 1687h <br clear>
<b>Returns</b><br>
<img src="r/ax+bx+cl+dh+dl+si+E+di.gif" alt="" width=245 height=59><br>
<i>If function successful</i><br>
AX = 0<br>
BX = flags
<blockquote><table border=1 cellspacing=0 cellpadding=4>
<tr><th>Bit</th><th>Significance</th></tr>
<tr><td rowspan=2 align=center>0</td><td align=left>0 = 32-bit programs are not supported</td></tr>
<tr><td align=left>1 = 32-bit programs are supported</td></tr>
<tr><td align=center>1-15</td><td align=left>not used</td></tr>
</table></blockquote>
CL = processor type
<blockquote><table border=1 cellspacing=0 cellpadding=4>
<tr><th>Value</th><th>Significance</th></tr>
<tr><td align=center>02H</td><td align=left>80286</td></tr>
<tr><td align=center>03H</td><td align=left>80386</td></tr>
<tr><td align=center>04H</td><td align=left>80486</td></tr>
<tr><td align=center>05H-FFH</td><td align=left>Reserved for future Intel processors</td></tr>
</table></blockquote>
DH = DPMI major version as a decimal number
(represented in binary)<br>
DL = DPMI minor version as a decimal number
(represented in binary)<br>
SI = number of paragraphs required for DPMI host
private data (may be 0)<br>
ES:DI = segment:offset of procedure to call to enter
protected mode<p>
<i>if function unsuccessful (no DPMI host present)</i><br>
AX = nonzero<p>
<h2>Notes</h2>
<ul>
<li>The entry point returned by <a href="2f1687.html">Int 2FH Function
1687H</a> is only called for the first switch to protected mode by a
DPMI client. For further details on the protocol for switching to
protected mode and the environment after switching to protected mode,
see <a href="../ch4.1.html">that page</a>.<p>
<li>Under DPMI hosts, the major version number is returned in DH and
the minor version number is returned in DL. There are two decimal
digits for the minor version number with the least-significant digit
representing the revision number of the minor version number. Under
DPMI version 0.9 hosts, DH is returned as 0, and DL is returned as
decimal 90 (5AH). In hypothetical DPMI version 2.3, DH would be
returned as 2 and DL would be returned as 30 (1EH).<p>
</ul>

View File

@@ -0,0 +1,81 @@
<h2>Allocate DOS Memory Block
<img src="../0.9.gif" alt="[0.9]" width=22 height=17></h2>
Allocates a block of memory from the DOS memory pool, i.e.
memory below the 1 MB boundary that is controlled by DOS.
Such memory blocks are typically used to exchange data with
real mode programs, TSRs, or device drivers. The function
returns both the real mode segment base address of the block
and one or more descriptors that can be used by protected
mode applications to access the block.<p>
<b>Call With</b><br>
<img src="r/ax+bx.gif" alt="" width=245 height=59><br>
AX = 0100H<br>
BX = number of (16-byte) paragraphs desired<p>
<b>Returns</b><br>
<img src="r/ax+bx+dx+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<br>
AX = real mode segment base address of allocated
block<br>
DX = selector for allocated block<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>0007H</td><td>memory control blocks damaged (also returned by
DPMI 0.9 hosts)</td></tr>
<tr><td>0008H</td><td>insufficient memory (also returned by DPMI 0.9
hosts).</td></tr>
<tr><td>8011H</td><td>descriptor unavailable</td></tr>
</table>
BX = size of largest available block in paragraphs<p>
<h2>Notes</h2>
<ul>
<li>If the size of the block requested is greater than 64 KB (BX >
1000H) and the client is a 16-bit program, contiguous descriptors are
allocated and the base selector is returned. The consecutive
selectors for the memory block can be calculated using the value
returned by the Get Selector Increment Value function (<a
href="310003.html">Int 31H Function 0003H</a>). Each descriptor has a
limit of 64 KB, except for the last which has a limit of blocksize MOD
64 KB.<p>
<li>If the DPMI host is 32-bit, the client is 16-bit, and more than
one descriptor is allocated, the limit of the first descriptor will be
set to the size of the entire block. Subsequent descriptors have
limits as described in the previous Note. 16-bit DPMI hosts will
always set the limit of the first descriptor to 64 KB even when
running on an 80386 (or later) machine.<p>
<li>When the client is 32-bit, this function always allocates only one
descriptor.<p>
<li>Client programs should never modify or free any descriptors
allocated by this function. The Free DOS Memory Block function (<a
href="310101.html">Int 31H Function 0101H</a>) will deallocate the
descriptors automatically.<p>
<li>The DOS allocation function (Int 21H Function 48H) is used.<p>
<li>Refer to the rules for descriptor usage in <a href="../descriptor-rules.html">Appendix D</a>.<p>
</ul>

View File

@@ -0,0 +1,34 @@
<h2>Set Real Mode Interrupt Vector
<img src="../0.9.gif" alt="[0.9]" width=22 height=17></h2>
Sets the current virtual machine's real mode interrupt
vector for the specified interrupt.<p>
<b>Call With</b><br>
<img src="r/ax+bl+cx+dx.gif" alt="" width=245 height=59><br>
AX = 0201H<br>
BL = interrupt number<br>
CX:DX = segment:offset of real mode interrupt handler<p>
<b>Returns</b><br>
<img src="r/c.gif" alt="" width=245 height=59><br>
Carry flag = clear (this function always succeeds)<p>
<h2>Notes</h2><ul>
<li>The address passed in CX must be a real mode segment address, not
a selector. Consequently, the interrupt handler must either reside in
DOS memory (i.e. below the 1 MB boundary) or the client must allocate
a real mode callback address. See <a href="310100.html">Int 31H
Functions 0100H</a> and <a href="310303.html">0303H</a>.<p>
<li>If the interrupt being hooked is a hardware interrupt, the memory
that the interrupt handler uses must be locked.<p>
</ul>

View File

@@ -0,0 +1,64 @@
<h2>Set Processor Exception Handler Vector
<img src="../0.9.gif" alt="[0.9]" width=22 height=17></h2>
Sets the address of a handler for a CPU exception or fault, allowing a
protected mode application to intercept processor exceptions (such as
segment not present faults) that are not handled by the DPMI host and
would otherwise generate a fatal error. This function should be
avoided by DPMI 1.0 clients (see Notes).<p>
<b>Call With</b><br>
<img src="r/ax+bl+cx+edx.gif" alt="" width=245 height=59><br>
AX = 0203H<br>
BL = exception/fault number (00H-1FH)<br>
CX:(E)DX = selector:offset of exception handler<p>
<b>Returns</b><br>
<img src="r/ax+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8021H</td><td>invalid value (BL not in range 0-1FH)</td></tr>
<tr><td>8022H</td><td>invalid selector</td></tr>
</table>
<h2>Notes</h2><ul>
<li>The value passed in CX should be a valid protected mode code
(executable) selector, not a real mode segment address.<p>
<li>32-bit clients must supply a 32-bit offset in the EDX register.
If the client's handler chains to the next exception handler, it must
do so using a 32-bit interrupt stack frame.<p>
<li>Every exception is first examined by the DPMI host. If the host
does not handle the exception, it reflects the exception to the first
handler in the protected mode exception handler chain. See <a
href="../ch4.5.html">that page</a> for a complete discussion of the
environment and responsibilities of protected mode exception handlers
installed with this function.<p>
<li>Clients which run under DPMI 1.0 should use <a
href="310212.html">Int 31H Functions 0212H</a> and <a
href="310213.html">0213H</a> to set the addresses of exception
handlers. This function is supported by DPMI 1.0 hosts solely for
compatibility with DPMI 0.9.<p>
<li>Refer to the rules for descriptor usage in <a href="../descriptor-rules.html">Appendix D</a>.
</ul>

View File

@@ -0,0 +1,47 @@
<h2>Set Extended Processor Exception Handler Vector (Protected Mode)
<img src="../1.0.gif" alt="[1.0]" width=22 height=17></h2>
Sets the address of the client's protected mode handler for
the specified protected mode exception.<p>
<b>Call With</b><br>
<img src="r/ax+bl+cx+edx.gif" alt="" width=245 height=59><br>
AX = 0212H<br>
BL = exception/fault number (00H-1FH)<br>
CX:(E)DX = selector:offset of exception handler<p>
<b>Returns</b><br>
<img src="r/ax+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8021H</td><td>invalid value (BL not in range 00H-1FH)</td></tr>
<tr><td>8022H</td><td>invalid selector</td></tr>
</table>
<h2>Notes</h2>
<ul>
<li>DPMI 1.0 clients should use this function in preference to <a
href="310203.html">Int 31H Function 0203H</a>.<p>
<li>The protected mode exceptions are sent to the protected mode
handler of the current client.<p>
<li>Refer to the rules for descriptor usage in <a href="../descriptor-rules.html">Appendix D</a>.<p>
</ul>

View File

@@ -0,0 +1,50 @@
<h2>Set Extended Processor Exception Handler Vector (Real Mode)
<img src="../1.0.gif" alt="[1.0]" width=22 height=17></h2>
Sets the address of the client's protected mode handler for the
specified real mode exception.<p>
<b>Call With</b><br>
<img src="r/ax+bl+cx+edx.gif" alt="" width=245 height=59><br>
AX = 0213H<br>
BL = exception/fault number (00H-1FH)<br>
CX:(E)DX = selector:offset of exception handler<p>
<b>Returns</b><br>
<img src="r/ax+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8021H</td><td>invalid value (BL not in range 00H-1FH)</td></tr>
<tr><td>8022H</td><td>invalid selector</td></tr>
</table>
<h2>Notes</h2>
<ul>
<li>CX:(E)DX does not specify a real-mode segment:offset. The reason
is that this function allows a client to set the address of an
exception handler which will receive control in protected mode when
the specified exception occurs in real mode (i.e. the host will
provide an implied mode switch for the purposes of servicing the
exception, then return to real mode after the handler exits). <p>
<li>Real mode exceptions are sent to the primary client of the virtual
machine. (See Appendix A: Glossary for definition of primary
client.)<p>
<li>Refer to the rules for descriptor usage in <a href="../descriptor-rules.html">Appendix D</a>.
</ul>

View File

@@ -0,0 +1,62 @@
<h2>Allocate Real Mode Callback Address
<img src="../0.9.gif" alt="[0.9]" width=22 height=17></h2>
Returns a unique real mode segment:offset, known as a "real mode
callback," that will transfer control from real mode to a protected
mode procedure. Callback addresses obtained with this function can be
passed by a protected mode program to a real mode application,
interrupt handler, device driver, or TSR, so that the real mode
program can call procedures within the protected mode program or
notify the protected mode program of an event.<p>
<b>Call With</b><br>
<img src="r/ax+D+esi+E+edi.gif" alt="" width=245 height=59><br>
AX = 0303H<br>
DS:(E)SI = selector:offset of protected mode procedure to call<br>
ES:(E)DI = selector:offset of 32H-byte buffer for real mode register
data structure to be used when calling callback routine.<p>
<b>Returns</b><br>
<img src="r/ax+cx+dx+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<br>
CX:DX = segment:offset of real mode callback<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8015H</td><td>callback unavailable</td></tr>
</table>
<h2>Notes</h2><ul>
<li>DPMI hosts must provide a minimum of 16 callback
addresses per client.<p>
<li>A descriptor may be allocated for each callback to hold the real
mode SS descriptor. Real mode callbacks are a limited system resource.
A client should use the Free Real Mode Callback Address function (<a
href="310304.html">Int 31H Function 0304H</a>) to release a callback
that it is no longer using.<p>
<li>For further information on writing real mode callback procedures,
see <a href="../ch4.6.html">that page</a>.<p>
<li>The contents of the real mode register data structure is
not valid after the function call, but only at the time
of the actual callback.<p>
</ul>

View File

@@ -0,0 +1,64 @@
<h2>Allocate Memory Block
<img src="../0.9.gif" alt="[0.9]" width=22 height=17></h2>
Allocates and commits a block of linear memory.<p>
<b>Call With</b><br>
<img src="r/ax+bx+cx.gif" alt="" width=245 height=59><br>
AX = 0501H<br>
BX:CX = size of block (bytes, must be nonzero)<p>
<b>Returns</b><br>
<img src="r/ax+bx+cx+si+di+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<br>
BX:CX = linear address of allocated memory block<br>
SI:DI = memory block handle (used to resize and free
block)<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8012H</td><td>linear memory unavailable</td></tr>
<tr><td>8013H</td><td>physical memory unavailable</td></tr>
<tr><td>8014H</td><td>backing store unavailable</td></tr>
<tr><td>8016H</td><td>handle unavailable</td></tr>
<tr><td>8021H</td><td>invalid value (BX:CX = 0)</td></tr>
</table>
<h2>Notes</h2><ul>
<li>The allocated block is guaranteed to have at least paragraph
alignment.<p>
<li>This function always creates committed pages.<p>
<li>This function does not allocate any descriptors for the memory
block. It is the responsibility of the client to allocate and
initialize any descriptors needed to access the memory with additional
DPMI function calls.<p>
<li>Under DPMI hosts that support virtual memory, the memory block
will be allocated unlocked. The client can lock some or all of the
memory after it is allocated with the Lock Linear Region function (<a
href="310600.html">Int 31H Function 0600H</a>).<p>
<li>Under many DPMI hosts, allocations by this function are page
granular. This means, for example, that if the DPMI host uses a page
size of 4 KB (1000H), an allocation of 1001H bytes will actually
result in an allocation of 2000H bytes. Therefore, it is best to
always allocate memory in multiples of the unit of granularity (under
DPMI 0.9, use 4K bytes), which can be obtained with <a
href="310604.html">Int 31H Function 0604H</a>.<p>
</ul>

View File

@@ -0,0 +1,75 @@
<h2>Allocate Linear Memory Block
<img src="../1.0.gif" alt="[1.0]" width=22 height=17></h2>
Allocates a block of page-aligned linear address space. The
base address of the block may be specified by the client,
and pages within the block may be committed or uncommitted.<p>
<b>Call With</b><br>
<img src="r/ax+ebx+ecx+edx.gif" alt="" width=245 height=59><br>
AX = 0504H<br>
EBX = desired page-aligned linear address of memory
block,
or zero if linear address unspecified<br>
ECX = size of block (bytes, must be nonzero)<br>
EDX = flags
<blockquote><table border=1 cellspacing=0 cellpadding=4>
<tr><th>Bit</th><th>Significance</th></tr>
<tr><td align=center rowspan=2>0</td><td align=left>0 = create uncommitted pages</td></tr>
<tr><td align=left>1 = create committed pages</td></tr>
<tr><td align=center>1-31</td><td align=left>reserved, should be zero</td></tr>
</table></blockquote>
<b>Returns</b><br>
<img src="r/ax+ebx+esi+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<br>
EBX = linear address of memory block<br>
ESI = handle for memory block<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8001H</td><td>unsupported function (16-bit host)</td></tr>
<tr><td>8012H</td><td>linear memory unavailable</td></tr>
<tr><td>8013H</td><td>physical memory unavailable</td></tr>
<tr><td>8014H</td><td>backing store unavailable</td></tr>
<tr><td>8016H</td><td>handle unavailable</td></tr>
<tr><td>8021H</td><td>invalid value (ECX = 0)</td></tr>
<tr><td>8025H</td><td>invalid linear address (EBX not page aligned)</td></tr>
</table>
<h2>Notes</h2><ul>
<li>A DPMI 1.0 host that is 16-bit only will not support this
function.<p>
<li>A 16-bit client of a 32-bit DPMI 1.0 host can use this
function.<p>
<li>The allocated block is always page-aligned. If a specific linear
address is not requested (EBX = 0), the DPMI host allocates the memory
block at any available page-aligned linear address. If a specific
linear address is requested (EBX nonzero), the host either allocates
the block at the specified address or returns <a
href="errors.html">error code</a> 8012H (linear memory
unavailable).<p>
<li><a href="310501.html">Int 31H Function 0501H</a>, which can also
be used to allocate linear memory blocks, does not necessarily
page-align its blocks and does not have the ability to create
uncommitted pages or allocate a block at a specific linear
address. <p>
</ul>

View File

@@ -0,0 +1,96 @@
<h2>Map Device in Memory Block
<img src="../1.0.gif" alt="[1.0]" width=22 height=17></h2>
Maps the physical addresses assigned to a device onto the linear
addresses of a memory block previously allocated with <a
href="310504.html">Int 31H Function 0504H</a>.<p>
<b>Call With</b><br>
<img src="r/ax+ebx+ecx+edx+esi.gif" alt="" width=245 height=59><br>
AX = 0508H<br>
ESI = memory block handle<br>
EBX = offset within memory block of page(s) to be
mapped (must be page-aligned)<br>
ECX = number of pages to map<br>
EDX = physical address of device (must be
page-aligned)<p>
<b>Returns</b><br>
<img src="r/ax+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8001H</td><td>unsupported function (Device Mapping</td></tr>
Capability not supported)
<tr><td>8003H</td><td>system integrity (invalid device address)</td></tr>
<tr><td>8023H</td><td>invalid handle (in ESI)</td></tr>
<tr><td>8025H</td><td>invalid linear address (specified range
is not within specified block or EBX/EDX
is not page-aligned)</td></tr>
</table>
<h2>Notes</h2><ul>
<li>16-bit DPMI hosts will not support this function. A 16-bit client
of a 32-bit DPMI 1.0 host can use this function.<p>
<li>Support of this call by 32-bit DPMI hosts is optional.
Application programs or DOS Extenders which require this call in order
to run are not DPMI Compliant.<p>
<li>Any committed or mapped pages resided in the linear address range
that is being mapped into will be uncommitted or unmapped
automatically by the host.<p>
<li>All pages created by this call have the mapped bit (bit 2) set in
the attributes returned by the Get Page Attributes function (<a
href="310506.html">Int 31H Function 0506H</a>).<p>
<li>This function differs from the Create Physical Address Mapping
function (<a href="310800.html">Int 31H Function 0800H</a>) in that
this function supports mapping of physical devices within an existing
memory block, rather than at an arbitrary linear address. Use of an
existing memory block gives 32-bit programs the ability to access
physical devices with NEAR pointers, which is often highly desirable
for performance reasons.<p>
<li>Unlike <a href="310800.html">Int 31H Function 0800H</a>, this
function allows mapping of addresses below 1 MB that do not lie within
RAM available for use by programs; e.g. this function can be used to
map the refresh buffers of IBM-compatible display adapters.<p>
<li>If the DPMI host is not virtualizing the device, it must disable
any memory caching on the mapped pages; in particular, on the 486 or
later, the PCD (page cache disable) bit must be set in the page table
entries.<p>
<li>DPMI hosts that do not virtualize physical devices can support
this function by creating page table entries that map the physical
device. The page table entries must be marked as mapped so that the
host knows not to attempt freeing of physical memory for the pages
when the memory block is freed.<p>
<li>DPMI hosts are allowed to support this function for some physical
devices and not for others, because mapping of virtualized devices
requires page aliasing in the host - a complex task. DPMI hosts with
partial support for this function may fail the function call on
virtualized devices (such as displays), and allow the call on
non-virtualized devices (such as the Weitek coprocessors). Allowing
the client to map a physical device so that it can be accessed with
NEAR references, for example, may help the client achieve considerably
better performance.<p>
</ul>

View File

@@ -0,0 +1,100 @@
<h2>Map Conventional Memory in Memory Block
<img src="../1.0.gif" alt="[1.0]" width=22 height=17></h2>
Aliases linear addresses below the 1 MB boundary onto the linear
addresses of a memory block previously allocated with <a
href="310504.html">Int 31H Function 0504H</a>.<p>
<b>Call With</b><br>
<img src="r/ax+ebx+ecx+edx+esi.gif" alt="" width=245 height=59><br>
AX = 0509H<br>
ESI = memory block handle<br>
EBX = offset within memory block of page(s) to be
mapped (must be page-aligned)<br>
ECX = number of pages to map<br>
EDX = linear address of conventional memory (must be
page-aligned)<p>
<b>Returns</b><br>
<img src="r/ax+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8001H</td><td>unsupported function (Conventional Memory
Mapping Capability not supported)</td></tr>
<tr><td>8003H</td><td>system integrity (invalid conventional
memory address)</td></tr>
<tr><td>8023H</td><td>invalid handle (in ESI)</td></tr>
<tr><td>8025H</td><td>invalid linear address (specified range
is not within specified block, or EBX/EDX
is not page aligned)</td></tr>
</table>
<h2>Notes</h2><ul>
<li>16-bit DPMI hosts will not support this function. A 16-bit client
of a 32-bit DPMI 1.0 host can use this function.<p>
<li>Support of this call by 32-bit DPMI hosts is optional.
Application programs or DOS Extenders which require this call in order
to run are not DPMI Compliant.<p>
<li>Any committed or mapped pages resided in the linear address range
that is being mapped into will be uncommitted or unmapped
automatically by the host.<p>
<li>A client may only map conventional memory that it already owns;
i.e. memory which the client previously allocated with <a
href="310100.html">Int 31H Function 0100H</a> or by calling DOS's Int
21H Function 48H directly via the translation services.<p>
<li>All pages created by this call have the mapped bit (bit 2) set in
the attributes returned by the Get Page Attributes function (<a
href="310506.html">Int 31H Function 0506H</a>).<p>
<li>DPMI hosts that do not implement virtual memory can support this
function by simply copying page table entries. The entries must be
marked as mapped so that the host knows not to free up those physical
pages when the memory block is freed.<p>
<li>DPMI hosts that provide virtual memory must implement some form of
page aliasing in order to support this function.<p>
<li>The function can provide a large contiguous memory space without
virtual memory support. <p>
<li>Implementors of DPMI hosts which do not provide virtual memory are
encouraged to support this function. Without this function,
conventional memory may be inaccessible to a 32-bit nonsegmented
client, because the client may need contiguous linear memory for its
code and data. 32-bit clients can always guarantee that conventional
memory is not wasted with the following strategy:<ul>
<li>Call DOS to allocate any free conventional memory
<li>If the DPMI host supports virtual memory, call the Mark Real Mode
Region Pageable function (<a href="310602.html">Int 31H Function
0602H</a>) to ensure that the host has not locked down conventional
memory.
<li>If the host does not support virtual memory but supports the Map
Conventional Memory function (<a href="310509.html">Int 31H Function
0509H</a>), allocate a memory block with uncommitted pages, then use
<a href="310509.html">Function 0509H</a> to make the physical memory
allocated below 640 KB addressable in the memory block, and therefore
useable by the 32-bit application program. </ul><p>
</ul>

View File

@@ -0,0 +1,86 @@
<h2>Mark Real Mode Region as Pageable
<img src="../0.9.gif" alt="[0.9]" width=22 height=17></h2>
Advises the DPMI host that the specified memory below the 1 MB
boundary may be paged to disk.<p>
<b>Call With</b><br>
<img src="r/ax+bx+cx+si+di.gif" alt="" width=245 height=59><br>
AX = 0602H<br>
BX:CX = starting linear address of memory to mark as
pageable<br>
SI:DI = size of region to be marked (bytes)<p>
<b>Returns</b><br>
<img src="r/ax+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8002H</td><td>invalid state (region already marked as pageable)</td></tr>
<tr><td>8025H</td><td>invalid linear address (region is above 1 MB boundary)</td></tr>
</table>
<h2>Notes</h2><ul>
<li>If the function returns an error, none of the memory has been
marked as pageable.<p>
<li>If the specified region overlaps part of a page at the beginning
or end of the region, the page(s) will not be marked as pageable.<p>
<li>Pageability information for a real mode region is maintained as a
binary state, not a count. Therefore, multiple calls to this function
for the same region have no effect.<p>
<li>For compatibility with DPMI version 0.9 hosts, a client must call
the Relock Real Mode Region function (<a href="310603.html">Int 31H
Function 0603H</a>) to relock the memory region before terminating.
Memory that remains unlocked after the client has terminated could
result in fatal page faults when another program is executed in the
same address space. DPMI 1.0 hosts automatically relock real mode
memory at client termination.<p>
<li>Under some DPMI hosts, all conventional memory may be locked by
default. If a protected mode program is using memory in the first
megabyte of address space, it is recommended that this function be
used to turn off automatic page locking for regions of memory that
will not be touched at interrupt time.<p>
<li>The client must not mark memory as pageable in regions that it
does not own; i.e. it may only mark as pageable memory that it has
previously allocated with <a href="310100.html">Int 31H Function
0100H</a> or by a direct call to DOS via the translation functions.
For example, marking all free DOS memory as pageable under some DPMI
hosts could cause a page fault to occur while inside of DOS, resulting
in a crash. Also, a client should not mark the DPMI host data area as
pageable.<p>
<li>Note that address space marked as pageable by this function can
still be locked using the Lock Linear Region function (<a
href="310600.html">Int 31H Function 0600H</a>). This function is just
an advisory service to allow memory that does not need to be locked to
be paged out; it disables any automatic locking of real mode memory
performed by the DPMI host.<p>
<li>This function is ignored by DPMI implementations that do not
support virtual memory; the function will return the Carry flag clear
to indicate success, but has no other effect. DPMI hosts which
support virtual memory may also choose to ignore this function, but
such hosts must be able to handle page faults transparently at
arbitrary points during a client's execution, including within
interrupt and exception handlers.<p>
</ul>

View File

@@ -0,0 +1,60 @@
<h2>Relock Real Mode Region
<img src="../0.9.gif" alt="[0.9]" width=22 height=17></h2>
Relocks a memory region that was previously declared as pageable with
the Mark Real Mode Region as Pageable function (<a
href="310602.html">Int 31H Function 0602H</a>).<p>
<b>Call With</b><br>
<img src="r/ax+bx+cx+si+di.gif" alt="" width=245 height=59><br>
AX = 0603H<br>
BX:CX = starting linear address of memory to relock<br>
SI:DI = size of region to relock (bytes)<p>
<b>Returns</b><br>
<img src="r/ax+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8002H</td><td>invalid state (region not marked as
pageable)</td></tr>
<tr><td>8013H</td><td>physical memory unavailable</td></tr>
<tr><td>8025H</td><td>invalid linear address (region is above 1
MB boundary)</td></tr>
</table>
<h2>Notes</h2><ul>
<li>If the function returns an error, none of the memory has been
relocked.<p>
<li>If the specified region overlaps part of a page at the beginning
or end of the region, the page(s) will not be relocked.<p>
<li>This function is ignored by DPMI implementations that do not
support virtual memory; the function will return the Carry flag clear
to indicate success, but has no other effect. DPMI hosts which
support virtual memory may also choose to ignore this function, but
such hosts must be able to handle page faults transparently at
arbitrary points during a client's execution, including within
interrupt and exception handlers.<p>
<li>If <a href="310602.html">Function 0602H</a> is implemented as a
"no-operation" on a particular DPMI host, this function will likewise
do nothing. In other words, this function should not be used to lock
memory, but only to restore the default state of the host's
conventional memory locking.<p>
</ul>

View File

@@ -0,0 +1,91 @@
<h2>Physical Address Mapping
<img src="../0.9.gif" alt="[0.9]" width=22 height=17></h2>
Converts a physical address into a linear address. This function
allows device drivers running under DPMI hosts which use paging to
reach physical memory that is associated with their devices above the
1 MB boundary Examples of such devices are the Weitek numeric
coprocessor (usually mapped at 3 GB), buffers that hold scanner bit
maps, and high-end displays that can be configured to make display
memory appear in extended memory.<p>
<b>Call With</b><br>
<img src="r/ax+bx+cx+si+di.gif" alt="" width=245 height=59><br>
AX = 0800H<br>
BX:CX = physical address of memory<br>
SI:DI = size of region to map (bytes)<p>
<b>Returns</b><br>
<img src="r/ax+bx+cx+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<br>
BX:CX = linear address that can be used to access the
physical memory<br>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8003H</td><td>system integrity (DPMI host memory
region)</td></tr>
<tr><td>8021H</td><td>invalid value (address is below 1 MB
boundary)</td></tr>
</table>
<h2>Notes</h2><ul>
<li>It is the caller's responsibility to allocate and initialize a
descriptor for access to the memory.<p>
<li>This function should only be used by clients that absolutely
require direct access to a memory mapped device at physical addresses
above 1 MB. Clients should not use this function to access memory
below the 1 MB boundary (the real mode addressable region). See also
<a href="310200.html">Int 31H Functions 0002H</a>, <a
href="310508.html">0508H</a>, and <a href="310509.html">0509H</a>.<p>
<li>When this function is called, the DPMI host either creates page
table entries that directly map the physical addresses requested and
returns the linear address of the created page table entries, or else
just returns the linear address of the memory region that is already
used to map the requested device. For example, if the client attempts
to map a Weitek coprocessor and the host already has a linear region
set up to map the Weitek chip and virtualize it, it would simply
return the linear address of the existing region. If the host does
not virtualize the Weitek chip, it would create 16 page table entries
that map the 64 KB Weitek address space and return a linear address
corresponding to the new page table entries.<p>
<li>If the host is not virtualizing the device, it must disable any
memory caching on the mapped pages; in particular, on the 80486 the
host must set the PCD (page cache disable) bit in the page table
entries.<p>
<li>The host is permitted to fail any memory mapping call. However,
the host should support this function whenever possible, to achieve
compatibility with application programs that use memory-mapped devices
of which the host is not aware. Useful guidelines are that the host
should fail any attempt to map addresses below 1 MB, or addresses
which the host considers to be general-purpose RAM memory. Attempts
to map any other physical address should succeed, since the host
should either (a) already know about the device and be able to return
a linear address used to access the device, or (b) assume the program
is attempting to map a legitimate device of which the host has no
knowledge.<p>
<li>Programs and device drivers which need to perform DMA I/O to
physical addresses in a virtualized hardware environment should use
the Virtual DMA Services (see the Glossary entry for the Virtual DMA
Services Specification). Also see page 10 of the DPMI execution
environment section.<p>
</ul>

View File

@@ -0,0 +1,54 @@
<h2>Set Debug Watchpoint
<img src="../0.9.gif" alt="[0.9]" width=22 height=17></h2>
Sets a debug watchpoint at the specified linear address.<p>
<b>Call With</b><br>
<img src="r/ax+bx+cx+dh+dl.gif" alt="" width=245 height=59><br>
AX = 0B00H<br>
BX:CX = linear address of watchpoint<br>
DL = size of watchpoint (1, 2, or 4 bytes)<br>
DH = type of watchpoint
<blockquote><table border=1 cellspacing=0 cellpadding=4>
<tr><th>Value</th><th>Breakpoint Type</th></tr>
<tr><td align=center>0</td><td align=left>execute</td></tr>
<tr><td align=center>1</td><td align=left>write</td></tr>
<tr><td align=center>2</td><td align=left>read/write</td></tr>
</table></blockquote>
<b>Returns</b><br>
<img src="r/ax+bx+c.gif" alt="" width=245 height=59><br>
<i>if function successful</i><br>
Carry flag = clear<br>
BX = watchpoint handle<p>
<i>if function unsuccessful</i><br>
Carry flag = set<br>
AX = <a href="errors.html">error code</a>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>8016H</td><td>too many breakpoints</td></tr>
<tr><td>8021H</td><td>invalid value (in DL or DH)</td></tr>
<tr><td>8025H</td><td>invalid linear address (linear address
not mapped or alignment error)</td></tr>
</table>
<h2>Notes</h2><ul>
<li>Under DPMI 1.0, the handle will be in the range 0-14. Under DPMI
0.9, the handle range is not limited.<p>
<li>The watchpoint handle corresponds to the bit number in the Virtual
DR6 returned in the exception frame (see <a href="310212.html">Int 31H
Function 0212H</a> and page 18 of the DPMI spec).<p>
</ul>

View File

@@ -0,0 +1,51 @@
<h2>Terminate and Stay Resident
<img src="../1.0.gif" alt="[1.0]" width=22 height=17></h2>
A resident service provider uses this function after its
initialization to terminate execution while leaving its protected mode
memory (and optionally some real mode memory) allocated.<p>
<b>Call With</b><br>
<img src="r/ax+bl+dx.gif" alt="" width=245 height=59><br>
AX = 0C01H<br>
BL = return code<br>
DX = number of paragraphs (16-byte blocks) of DOS
memory to reserve<p>
<b>Returns</b><br>
Nothing (this call never returns)
<h2>Notes</h2><ul>
<li>This function should only be used by DPMI clients which only
provide resident services to other DPMI protected mode clients. If
the objective is only to provide resident services to real mode
programs, the client should use the DPMI translation service <a
href="310300.html">Int 31H Function 0300H</a> to invoke DOS's Int 21H
Function 31H directly.<p.
<li>The value in DX only specifies the size of DOS allocated memory to
reserve. Any protected mode memory owned by the program remains
allocated unless it is explicitly released before executing this
function. Note that the value in DX must either be 0 or a minimum of
6. If DX is 0, the DPMI host executes a DOS real mode terminate
function (Int 21H Function 4CH), and no real mode memory is reserved.
If DX is nonzero, the DPMI host requests the DOS real mode
terminate-and-stay-resident function (Int 21H Function 31H).<p>
<li>If the client has not made a prior call to <a
href="310c00.html">Int 31H Function 0C00H</a>, the client will simply
be terminated.<p>
<li>For further details on programming of resident service providers,
see <a href="../ch4.8.html">that page</a>.<p>
</ul>

View File

@@ -0,0 +1,141 @@
Note that DPMI 0.9 hosts are <em>not</em> required to return a valid
error code in AX. You may only rely on the carry flag being set if an
error occurred. <p>
Nearly all Int 31H function calls can fail, either because of client
errors, unavailable resources, or internal host problems. Most
failures due to client errors and all failures due to unavailable
resources are reported to the client via error codes. Some client
errors, such as passing an invalid pointer in a function call, may
cause the host to fault; the client can detect these events by
installing an exception handler. <p>
Internal host errors are handled in a host-specific manner and
generally not reported to clients with an error code. The only
exception to this is the case when a host cannot allocate internal
resources. Any Int 31H function is capable of returning error code
8010H to indicate this condition.<p>
A DPMI 1.0 host signals an error by returning from a function with the
Carry flag set and an error code in AX. If the error code has bit 15
clear (0), the DPMI host is passing a DOS error code through to the
client; for a list of these error codes, consult a DOS technical
reference. If the error code has bit 15 set (1), it is generated
within the DPMI host, and is interpreted according to the list below.
All DPMI 1.0 hosts are required to check for the error conditions
listed in this specification, and must return the error codes that are
documented for each function.<p>
If Int 31H is invoked with an function number that is not defined in
this specification, the DPMI host will return the "Unsupported
Function" error code 8001H. The table lists all defined error codes
and their messages. Unused error codes are reserved for the later
versions of the DPMI spcifications.<p>
<table border=1 cellspacing=0 cellpadding=4>
<tr><th>Error Code</th>
<th>Name</th>
<th>Explanation</th></tr>
<tr><td>0007H</td>
<td align=left>Memory configuration blocks damaged</td>
<td align=left>The operating system has detected corruption in the real-mode memory arena.</td></tr>
<tr><td>0008H</td>
<td align=left>Insufficient memory</td>
<td align=left>There is not enough real-mode memory to satisfy the request.</td></tr>
<tr><td>0009H</td>
<td align=left>Incorrect memory segment specified</td>
<td align=left>The segment value specified was not one provided by the operating system</td></tr>
<tr><td>8001H</td>
<td align=left>Unsupported function</td>
<td align=left>Returned in response to any function call which is not implemented by this host, because the requested function is either undefined or optional.</td></tr>
<tr><td>8002H</td>
<td align=left>Invalid state</td>
<td align=left>Some object is in the wrong state for the requested operation.</td></tr>
<tr><td>8003H</td>
<td align=left>System integrity</td>
<td align=left>The requested operation would endanger system integrity, e.g., a request to map linear addresses onto system code or data.</td></tr>
<tr><td>8004H</td>
<td align=left>Deadlock</td>
<td align=left>Host detected a deadlock situation.</td></tr>
<tr><td>8005H</td>
<td align=left>Request cancelled</td>
<td align=left>A pending serialization request was cancelled.</td></tr>
<tr><td>8010H</td>
<td align=left>Resource Unavailable</td>
<td align=left>The DPMI host cannot allocate internal resources to complete an operation.</td></tr>
<tr><td>8011H</td>
<td align=left>Descriptor unavailable</td>
<td align=left>Host is unable to allocate a descriptor.</td></tr>
<tr><td>8012H</td>
<td align=left>Linear memory unavailable</td>
<td align=left>Host is unable to allocate the required linear memory.</td></tr>
<tr><td>8013H</td>
<td align=left>Physical memory unavailable</td>
<td align=left>Host is unable to allocate the required physical memory.</td></tr>
<tr><td>8014H</td>
<td align=left>Backing store unavailable</td>
<td align=left>Host is unable to allocate the required backing store.</td></tr>
<tr><td>8015H</td>
<td align=left>Callback unavailable</td>
<td align=left>Host is unable to allocate the required callback address.</td></tr>
<tr><td>8016H</td>
<td align=left>Handle unavailable</td>
<td align=left>Host is unable to allocate the required handle.</td></tr>
<tr><td>8017H</td>
<td align=left>Lock count exceeded</td>
<td align=left>A locking operation exceeds the maximum count maintained by the host.</td></tr>
<tr><td>8018H</td>
<td align=left>Resource owned exclusively</td>
<td align=left>A request for serialization of a shared memory block could not be satisfied because it is already serialized exclusively by another client.</td></tr>
<tr><td>8019H</td>
<td align=left>Resource owned shared</td>
<td align=left>A request for exclusive serialization of a shared memory block could not be satisfied because it is already serialized shared by another client.</td></tr>
<tr><td>8021H</td>
<td align=left>Invalid value</td>
<td align=left>A numeric or flag parameter has an invalid value.</td></tr>
<tr><td>8022H</td>
<td align=left>Invalid selector</td>
<td align=left>A selector does not correspond to a valid descriptor.</td></tr>
<tr><td>8023H</td>
<td align=left>Invalid handle</td>
<td align=left>A handle parameter is invalid.</td></tr>
<tr><td>8024H</td>
<td align=left>Invalid callback</td>
<td align=left>A callback parameter is invalid.</td></tr>
<tr><td>8025H</td>
<td align=left>Invalid linear address</td>
<td align=left>A linear address range (either supplied as a parameter or implied by the call) is invalid.</td></tr>
<tr><td>8026H</td>
<td align=left>Invalid request</td>
<td align=left>The request is not supported by the underlying hardware.</td></tr>
</table>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,125 @@
DPMI clients are loaded in real mode. In order to enter protected
mode, the client must first call <a href="api/2f1687.html">Int 2FH
Function 1687H</a> to test for the presence of a DPMI host and obtain
the address of the real-to-protected mode switch entry point. <a
href="api/2f1687.html">Function 1687H</a> also returns information
about the DPMI host's capabilities and the size of a private data area
which will be used by the host to hold client-specific data
structures. After allocating the required private data area via the
normal real mode memory allocation interface (DOS Int 21H Function
48H), the client makes a <tt>FAR CALL</tt> to the mode switch entry
point with the following parameters: <p>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td rowspan=5 align=center>AX</td><td colspan=2>flags</td></tr>
<tr><th align=center>Bit</th><th>Significance</th></tr>
<tr><td align=center rowspan=2>0</td><td align=left>0 = 16-bit application</td></tr>
<tr><td align=left>1 = 32-bit application</td></tr>
<tr><td align=center rowspan=1>1-15</td><td align=left>Reserved, should be zero</td></tr>
<tr><td align=center>ES</td><td colspan=2>real mode segment of DPMI
host private data area (must be at least as large as the size returned
in SI by <a href="api/2f1687.html">Int 2FH Function 1687H</a>; ES is
ignored if the size was zero.</td></tr>
</table><p>
If the Carry flag is set upon return, the mode switch was
unsuccessful, the client is still running in real mode, and register
AX contains one of the following error codes: <p>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td align=center>8011H</td><td align=left>descriptor unavailable
(cannot allocate descriptors for CS, DS, ES, SS, PSP, and environment
pointer)</td></tr>
<tr><td align=center>8021H</td><td align=left>invalid value (32-bit
program specified but not supported)</td></tr>
</table><p>
If the mode switch was successful, the mode switch routine returns to
the call in protected mode with the Carry flag clear and the following
results:<p>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td align=center>CS</td><td align=left>16-bit selector with base
of real mode CS and a 64KB limit</td></tr>
<tr><td align=center>SS</td><td align=left>selector with base of real
mode SS and a 64 KB limit</td></tr>
<tr><td align=center>DS</td><td align=left>selector with base of real
mode DS and a 64 KB limit</td></tr>
<tr><td align=center>ES</td><td align=left>selector to program's PSP
with a 100H byte limit</td></tr>
<tr><td align=center>FS</td><td align=left>0 (if running on an 80386
or later)</td></tr>
<tr><td align=center>GS</td><td align=left>0 (if running on an 80386
or later)</td></tr>
</table><p>
All other registers are preserved, except that for 32-bit clients the
high word of ESP will be forced to zero. 32-bit clients will
initially run with a 16-bit code segment, but all Int 31H calls will
still require 48-bit pointers, and the stack and data descriptors will
be 32-bit (the Big bit will be set in the descriptors). Note that if
DS+SS at the time of the mode switch call, only one descriptor may be
allocated, and the same selector may be returned in DS and SS. The
client is allowed to modify or free the CS, DS and SS descriptors
allocated by the mode switch routine. <p>
The environment pointer in the client program's PSP is automatically
converted to a selector during the mode switch. If the client wishes
to free the memory occupied by the environment, it should do so <i>
before </i> entering protected mode and zero the word at PSP:2CH
(segment address of the environment>. The client may change the
environment point in the PSP after entering protected mode but it must
restore it to the selector created by the DPMI host before
terminating. The client should <i>not</I> modify or free the PSP or
environment descriptors supplied by the DPMI host.<p>
<b> Example:</b> The following code illustrates how a DPMI client
would obtain the address of the mode switch entry point, allocate the
DPMI host private data area, and enter protected mode.
<pre>
modesw dd 0 ; far pointer to DPMI host's
; mode switch entry point
.
.
.
mov ax,1687h ; get address of DPMI host's
<a href="api/2f1687.html">int 2fh</a> ; mode switch entry point
or ax,ax ; exit if no DPMI host
jnz error
mov word ptr modesw,di ; save far pointer to host's
mov word ptr modesw+2,es ; mode switch entry point
or si,si ; check private data area size
jz @@1 ; jump if no private data area
mov bx,si ; allocate DPMI private area
mov ah,48h ; allocate memory
int 21h ; transfer to DOS
jc error ; jump, allocation failed
mov es,ax ; let ES=segment of data area
@@1: mov ax,0 ; bit 0=0 indicates 16-bit app
call modesw ; switch to protected mode
jc error ; jump if mode switch failed
; else we're in prot. mode now
.
.
.
</pre>

View File

@@ -0,0 +1,98 @@
A protected mode client terminates by executing an Int 21H in
protected mode, passing the value 4CH in register AH and a return code
in register AL. (This mimics the Int 21H Function 4CH termination
used by DOS applications in real mode.> The client has the following
responsibilities before termination:
<ul>
<li> real mode memory that was unlocked for paging with <a
href="api/310602.html">Int 31H Function 0602H</a> must be relocked
with <a href="api/310603.html">Int 31H Function 0603H</a>;<p>
<li> interrupts hooked by the client for real mode with <a
href="api/310201.html">Int 31H Function 0201H</a> must be released by
restoring the address of the original owner of the interrupt.<p>
<li> protected mode handlers for real mode exceptions installed with
<a href="api/310213.html">Int 31H Function 0213H</a> should be cleaned
up if possible.<p>
</ul>
When the DPMI host detects an Int 21H Function 4CH termination
request, it takes the following actions (the detailed comparison of
DPMI version 0.9 and version 1.0 host termination handling is in
Appendix C, page 158>:
<ul>
<li> any extended memory blocks that were previously allocated with <a
href="api/310501.html">Function 0501H</a> or <a
href="api/310504.html">0504H</a> are unlocked and freed (this is the
only cleanup action required by a DPMI V 0.9 host>;<p>
<li> the client's local descriptor table (LDT) is freed in its
entirety by DPMI 1.0 hosts. (A DPMI version 0.9 client should clean
up its own segment descriptors before its termination since some DPMI
version 0.9 hosts may not free the terminating client's segment
descriptors if the client is not the primary client);<p>
<li> physical address mappings created with <a
href="api/310800.html">Int 31H Function 0800H</a> are freed;<p>
<li> mappings created with <a href="api/310508.html">Int 31H Functions
0508H</a> or <a href="api/310509.html">0509H</a> are destroyed;<p>
<li> the client's interrupt descriptor table (IDT) is freed in it's
entirety and any client exception handlers installed through <a
href="api/310203.html">Functions 0203H</a>, <a
href="api/310212.html">0212H</a> and <a
href="api/310213.html">0213H</a> are deregistered;<p>
<li> any real mode regions that were unlocked with <a
href="api/310602.html">Function 0602H</a> are relocked;<p>
<li> any real mode callbacks that were allocated with <a
href="api/310303.html">Function 0303H</a> are deallocated;<p>
<li> the client's shared memory block allocations and serializations
are freed;<p>
<li> any debug watchpoints that were set with <a
href="api/310b00.html">Function 0B00H</a> are cleared;<p>
<li> the coprocessor state (if any) is restored to the default.<p>
</ul>
After the DPMI host performs the cleanup activities listed above, it
will switch to real mode and re-issue the Int 21H Function 4CH
interrupt, passing the return code from the DPMI client down to DOS.
DOS will then terminate the client as a real mode process by releasing
its DOS memory blocks (whether allocated by real mode Int 21H Function
48H or by <a href="api/310100.html">DPMI Function 0100H</a>), flushing
file buffers, closing file and device handles, and so on.<p>
Clients should only terminate from their main thread of execution, and
should not issue the protected mode Int 21H Function 4CH from within a
hardware interrupt handler, exception handler, or real mode callback.
Client may, however, terminate from within a protected mode routine
that has been entered via the DPMI raw mode switch service. Clients
which wish to terminate-and-stay-resident to provide services to
protected mode clients should use <a href="api/310c01.html">DPMI
Function 0C01H</a> rather than Int 21H Function 31H.<p>
<i>Note:</i> Although the DPMI host monitors for Int 21H Function 4Ch
in protected mode, it ignores all other Int 21H Functions. DOS
Extenders typically install an interrupt 21H handler of their own in
order to trap and service DOS function requests by a protected mode
application; thus, the DOS Extender's Int 21H handler will always see
the Function 4CH termination request first. The DOS Extender should
perform any cleanup activities of its own and then pass the
termination request to the DPMI host by chaining the original owner of
the protected mode Int 21H vector.<p>

View File

@@ -0,0 +1,209 @@
At one point in its execution or another, every DPMI client runs on
four different stacks: an application stack, a locked protected mode
stack, a real mode stack, and a DPMI host stack. It is important to
understand how the host maintains these stacks to fully understand the
protected mode environment.<p>
The <i>application stack</i> is the primary stack that the DPMI client
executes on. It is initially the real mode stack that the client was
on when it switched into protected mode, although nothing prevents the
client from switching protected mode stacks at any point after the
initial mode switch. Th application stack can be unlocked if desired.
Software interrupts executed in protected mode are reflected on this
stack.<p>
The <i>locked protected mode stack</i> is provided by the DPMI host.
The host automatically switches to this stack during servicing of
hardware interrupts, software interrupts 1CH, 23H, and 24H, all
exceptions, and during the execution of real mode callbacks.
Subsequent nested interrupts or calls will not cause a stack switch.
If the client switches off this stack, the new stack must also be
locked and will become the protected mode stack until it switches
back. When the interrupt or call returns, the host switches back to
the original protected mode stack. Note that the host must provide a
minimum of one 4 KB locked stack, and that software interrupts other
than 1CH, 23H, and 24H do <i>not</i> use this stack. (Refer Appendix
D for descriptor usage rule of locked stack.)<p>
The <i>real mode stack</i> is also provided by the DPMI host, and is
usually located in the DPMI host data area allocated by the client
prior to its initial switch into protected mode. The real mode stack
is at least 200H bytes in size and is always located in locked memory.
Interrupts that are reflected to real mode, as well as calls to real
mode interrupt handlers or procedures via <a
href="api/310300.html">Int 31H Functions 0300H</a>, <a
href="api/310301.html">0301H</a>, or <a
href="api/310302.html">0302H</a>, will use this stack.<p>
A <i>DPMI host stack</i> is only accessible to the DPMI host; it is
used by the host to handle interrupts and exceptions that occur while
the host is executing on behalf of the client. For example, when the
client requests a mode switch, the original SS:(E)SP of the protected
mode program can be saved on the host stack while the DPMI host
switches onto the locked protected mode stack.<p>
There are four different ways that a client can force a mode switch
between protected and real mode:
<ul>
<li> Execute the default interrupt reflection handler (all interrupts
other than Int 31H and Int 21H Function 4CH are initialized by the
DPMI host to point to a handler that reflects the interrupt to real
mode);<p>
<li> Use the DPMI translation services (<a href="api/310303.html">Int
31H Function 0303H</a>, <a href="api/310301.html">0301H</a>, and <a
href="api/310302.html">0302H</a>) to call a real mode interrupt
handler or procedure;<p>
<li> Allocate a real mode callback address with <a
href="api/310303.html">Int 31H Function 0303H</a>; when a real mode
program transfer control to the callback address, the DPMI host will
switch the CPU from real mode into protected mode;<p>
<li> Use the DPMI raw mode switch functions, whose addresses are
obtained with <a href="api/310306.html">Int 31H Function 0306H</a>.<p>
</ul>
All of these mode switches except for the raw mode switches may save
some information on the DPMI host's stack. This means that clients
should not terminate within nested mode switches unless they are using
the raw mode switching services. However, even clients that use raw
mode switches should not attempt to terminate from a hardware
interrupt or exception handler or real mode callback because the DPMI
host performs automatic mode and stack switching during these
events.<p>
Clients that use the raw mode switch services and perform nested mode
switches must use the DPMI state save/restore functions (whose
addresses may be obtained with <a href="api/310305.html">Int 31H
Function 0305H</a>), causing the host to maintain information on the
"other" mode's current state. This information includes the CS:(E)IP,
SS:(E)SP, and other segment register contents; values that the client
has no way to access directly. For example, during the service of a
hardware interrupt that occurs in real mode, the DPMI host may
preserve the real mode CS:(E)IP, SS:(E)SP, and segment registers on
the host stack, causing a return to the wrong address when the handler
finally executes the IRET.<p>
<b>Example:</b> This example illustrates code that saves the state of
the real mode registers using the DPMI save/restore function, switches
to real mode using the raw mode switch service, issues a DOS call to
open a file, switches back to protected mode using the raw mode switch
service, and restores the state of the real mode registers using the
save/restore function. The protected mode registers are saved by
pushing them on the stack in the usual fashion. The example is
intended only to show the logical sequence of execution; in a real
program, the real mode and protection mode variables and functions
would likely reside in separate segments.
<pre>
savsiz dw 0 ; size of state information
realsrs dd 0 ; far pointer to real mode
; save/restore state entry point
protsrs dd 0 ; far pointer to protected mode
; save/restore state entry point
realrms dd 0 ; far pointer to real mode
; raw mode switch entry point
protrms dd 0 ; far pointer to protected mode
; raw mode switch entry point
protdw dw 0 ; placeholder for protected mode DS
protip dw 0 ; placeholder for protected mode IP
protcs dw 0 ; placeholder for protected mode CS
protsp dw 0 ; placeholder for protected mode SP
protss dw 0 ; placeholder for protected mode SS
.
.
.
; this code is executed during
; application initialization
mov ax,305h ; get addresses of DPMI host's
<a href="api/310305.html">int 31h</a> ; state save/restore entry points
mov savsiz,ax ; save state info buffer size
mov word ptr realsrs,cx ; BX:CX = state save/restore
mov word ptr realsrs+2,bx ; entry point for real mode
mov word ptr protsrs,di ; SI:DI = state save/restore
mov word ptr protsrs+2,si ; entry point for protected mode
mov ax,306h ; get address of DPMI host's
<a href="api/310306.html">int 31h</a> ; raw mode switch entry points
mov savsiz,ax ; save state info buffer size
mov word ptr realrms,cx ; BX:CX = raw mode switch
mov word ptr realrms+2,bx ; entry piont for real mode
mov word ptr protrms,di ; SI:DI = raw mode switch
mov word ptr protrms+2,si ; entry point for protected mode
; must also initialize the
; sp and realss variables
.
.
.
; this code is executed during
; program execution
callopenfile proc
pusha ; save protected mode registers
push es
sub sp,savsiz ; allocate space on current stack
mov di,sp ; to save real mode state
mov ax,ss ; set ES:DI = address of buffer
mov es,ax ; to receive state information
xor al,al ; AL=0 for save state request
call protsrs ; call state save/restore routine
mov protds,ds ; save current DS for switch back
mov protss,ss ; save current SS
mov protsp,sp ; save current SP
mov protip,offset returnfromreal ; save return IP
mov protcs,cs ; save return CS
mov ax,seg filename ; load real mode DS
mov ds,realss ; load real mode SS
mov bx,realsp ; load real mode SP
mov si,seg openfile ; load real mode CS
mov di,offset openfile ; load real mode IP
jmp protrms ; go to openfile
returnfromreal:
mov ax,ss ; let ES:DI = address of buffer
mov es,ax ; holding state information
mov di,sp
mov al,1 ; AL=1 to restore state
call protsrs ; call state restore routine
add sp,savsiz ; discard state info buffer
pop es
popa ; restore protected mode registers
ret
callopenfile endp
.
.
.
; this code is executed in real mode
openfile proc
mov dx,offset filename
mov ah,3dh ; issue Open File DOS call
int 21h
jc openerr ; check for error (not shown here)
mov filehandle,bx ; save file handle
mov ax,protds ; load protected mode DS
mov dx,protss ; load protected mode SS
mov bx,protsp ; load protected mode SP
mov si,protcs ; load protected mode CS
mov di,protip ; load protected mode IP
jmp realrms
openfile endp
</pre>

View File

@@ -0,0 +1,46 @@
Under many DPMI hosts, interrupts will always remain enabled in
protected mode (i.e. the interrupt flag will be set at all times) to
allow preemptive multitasking. Such hosts will maintain a virtual
interrupt state for each virtual machine, trapping the execution of
instructions that ordinarily affect the hardware interrupt flag and
adjusting the client's virtual interrupt flag accordingly. When the
virtual interrupt flag is cleared by the client's execution of
<tt>CLI</tt> or call to <a href="api/310900.html">DPMI function Int
31H 0900H</a>, the program will not receive any hardware interrupts
until it sets the flag again with <tt>STI</tt> or calls <a
href="api/310901.html">Function 0901H</a>. DPMI clients should not
use the <tt>PUSHF</tt> instruction to examine their interrupt status.
This is because <tt>PUSHF</tt> pushes the real processor flags onto
the stack, which do not reflect the state of the client's virtual
interrupt flag. Similarly, clients cannot use <tt>IRET(D)</tt> or
<tt>POPF</tt> to alter the interrupt flag, because these instructions
access the physical interrupt flag and are ignored by the CPU due to
the client's privilege level. <p>
<b>Example:</b> The following source code demonstrates how a client
would disable virtual interrupts prior to entry to an
interrupt-critical section of code, then restore the virtual interrupt
flag to its previous state at the end of the critical section:
<pre>
mov ax,0900h ; get previous virtual interrupt
<a href="api/310900.html">int 31h</a> ; flag and disable interrupts
push ax ; save value 0900H or 0901H
.
. ; interrupt-critical code goes here
.
pop ax
<a href="api/310900.html">int 31h</a> ; restore previous interrupt flag
</pre>
If the client already knows (or does not care about) the previous
state of the virtual interrupt flag, it can use <tt>CLI</tt> and
<tt>STI</tt> instead of <a href="api/310900.html">DPMI functions
0900H</a> and <a href="api/310901.html">0901H</a>. The programmer
should assume that the execution of either of these instructions will
be slow. <p>

View File

@@ -0,0 +1,55 @@
The programmable interrupt controllers are mapped by the DPMI host to
the system's default interrupt assignments. On an IBM AT-compatible
system, for example, the master interrupt controller (IRQ0 through
IRQ7) is programmed to use a base interrupt level of 8 and the slave
controller (IRQ8 through IRQ15) uses a base interrupt level of 70H.<p>
All of the code and data that may be touched by hardware interrupt
handlers must reside in locked memory to avoid page faults at
interrupt time. The handler will always be called on a locked stack.
As in real mode, hardware interrupt handlers are called with virtual
interrupts disabled and the trace flag reset. In systems where the
CPU's interrupt flag is virtualized, <tt>IRET</tt> may not restore the
interrupt flag. Therefore, clients should execute a <tt>STI</tt>
before executing <tt>IRET</tt> or else interrupts will remain
disabled. <p>
Protected mode hardware interrupt handlers that call a real mode
routine must either ensure that the real mode code will not modify
segment registers or user the DPMI state save/restore services (see
page 94). However, any interrupt handler that executes completely in
protected mode, or uses the translation services (<a
href="api/310300.html">Int 31H Functions 0300H</a>, <a
href="api/310301.html">0301H</a>, or <a
href="api/310302.html">0302H</a>), does not need to save the real mode
register state. <p>
Personal computers with two programmable interrupt controllers usually
have a BIOS that redirects one of the interrupts from the slave
controller into the range of the master controller for compatibility
with older, 8086/88-based systems. For example, devices jumpered for
IRQ2 on PC/AT-compatible computers actually interrupt on IRQ 9 (Int
71H), but the BIOS on these systems converts Int 71H to Int 0AH yet
sends the EOI command (appropriately) to the slave controller. A
protected mode client that needs access to the redirected interrupt
might use a variation on one of the following techniques:
<ul>
<li> Install only a real mode handler for the target interrupt, taking
advantage of the built-in redirection. This method is robust on
systems where other software has reprogrammed the interrupt
controllers, or where the slave interrupt controller may be
absent. <p>
<li> Install both real mode and protected mode handlers for the target
interrupt. In such cases, the program must send the EOI command to
both the slave and master interrupt controllers since the BIOS is
never called. This method is more efficient in that there are not any
unnecessary switches to real mode. <p>
</ul>

View File

@@ -0,0 +1,28 @@
Ordinarily, a handler installed with <a href="api/310205.html">DPMI
Function 0205H</a> will only service software interrupts that are
executed in protected mode; real mode software interrupts are passed
to handlers installed with DOS Int 21H Function 25H issued from real
mode, <a href="api/310201.html">DPMI Int 31H Function 0201H</a>, or by
direct manipulation of the interrupt vector table at real mode address
0000:0000. However, there are three real mode software interrupts
that a DPMI host will always reflect to a protected mode handler, if
one is installed:
<ul>
Int 1CH - ROM BIOS timer tick interrupt<br>
Int 23H - DOS Ctrl+C interrupt<br>
Int 24H - DOS critical error interrupt<br>
</ul>
Clients should never terminate during the processing of interrupts
that were reflected from real mode. Such a termination might prevent
the DPMI host from cleaning up the client's resources properly. <p>
Protected mode handlers for software interrupts 0-7 are called with
virtual interrupts disabled and trace flag reset, and these handlers
should return with interrupts enabled. All other software interrupts
do not modify the interrupt flag state. <p>

View File

@@ -0,0 +1,35 @@
When a DPMI client switches into protected mode, a unique interrupt
descriptor table (IDT) is created for the client by the DPMI host.
Initially, all software interrupts (except for Int 31H, Int 2FH and
Int 21H Function 4CH) or external hardware interrupts are directed to
a handler that simply reflects the interrupt to real mode; i.e. the
DPMI host's default handler simply switches the CPU into real mode and
re-issues the interrupt, so that it can be serviced by the original
real mod owner of the interrupt. The contents of the general
registers and flags are passed to the real mode handler and the
modified registers and flags are returned to the protected mode
handler. Segment registers and the stack pointer are not passed
between modes; the contents of the segment registers after the switch
to real mode are undefined, and the DPMI host automatically supplies a
valid real mode stack. <p>
DPMI clients can install their own distinct real mode or protected
mode handlers for software and external hardware interrupts with <a
href="api/310201.html">Functions 0201H</a> and <a
href="api/310205.html">0205H</a> respectively. If a protected mode
handler is installed, it is called instead of any real mode handler or
the DPMI host's default handler. Just as in real mode, the protected
mode handler can either service the interrupt and terminate with an
IRET, or transfer to the next handler in the chain by executing a
<tt>PUSHF/CALL</tt> or a <tt>FAR JMP</tt>. The final handler in the
protected mode handler chain (the DPMI host's default handler) will
reflect the interrupt to real mode. <p>
<hr>
<img src="/icons/menu.sm.gif"><a href="ch4.4.1.html">Virtual Interrupts</a><br>
<img src="/icons/menu.sm.gif"><a href="ch4.4.2.html">Hardware Interrupts</a><br>
<img src="/icons/menu.sm.gif"><a href="ch4.4.3.html">Software Interrupts</a><br>

View File

@@ -0,0 +1,308 @@
Exceptions are interrupts that are generated internally by the CPU
when certain conditions are detected during the execution of a
program. Examples of such conditions are: use of an invalid selector,
use of a selector for which the program has insufficient privileges,
use of an offset outside the limits of a segment, execution of an
illegal opcode, or division by zero. The DPMI host distinguishes
between exceptions and external hardware interrupts or software
interrupts. <p>
Handlers for exceptions can only be installed with <a
href="api/310203.html">Int 31H Functions 0203H</a>, <a
href="api/310212.html">0212H</a>, or <a
href="api/310213.html">0213H</a>. If the client does not install a
handler for a particular exception, or installs a handler but chains
to the host's default handler, the host reflects the exception as a
real mode interrupt for exceptions 0,1,2,3,4,5, and 7. The default
behavior of exceptions 6 and 8-1FH is to terminate the client (some
hosts may decide that they have to terminate the VM because the fault
came from real mode code or it is in a non-terminatable state).<p>
<a href="api/310203.html">Function 0203H</a> was defined in DPMI version 0.9 and continues to be
supported in DPMI version 1.0 for compatibility reasons. Exception
handlers installed with <a href="api/310203.html">Function 0203H</a> are only called for exceptions
that occur in protected mode. All exceptions are examined by the DPMI
host. The host processes any exception that it is responsible for,
such as page fault for virtual memory management. These <i>transparent
exceptions</i> are never passed to the client exception handlers. All
other exceptions become <i>visible exceptions</i> to a client and are
passed to the client exception handler (if any) from the DPMI host.
The client exception handlers must return with a <tt>FAR RETURN</tt>,
with interrupts disabled on a locked stack, and with SS, (E)SP, CS,
and (E)IP registers at the point of exception pushed on the stack.
All other registers are unchanged from their contents at the point of
exception. The stack frame for 16-bit handlers installed with
Function 0203H has the following format:
<pre>
15 0
+---------------+
| SS |
|---------------| 0EH
| SP |
|---------------| 0CH
| Flags |
|---------------| 0AH
| CS |
|---------------| 08H
| IP |
|---------------| 06H
| Error Code |
|---------------| 04H
| Return CS |
|---------------| 02H
| Return IP |
|---------------| 00H <-- SS:SP
</pre>
The stack frame for 32-bit handlers installed with <a
href="api/310203.html">Function 0203H</a> has the following format:
<pre>
31 15 0
+---------------+---------------|
| Reserved | SS |
+---------------+---------------| 1CH
| ESP |
|-------------------------------| 18H
| EFLAGS |
|---------------+---------------| 14H
| Reserved | CS |
|---------------+---------------| 10H
| EIP |
|-------------------------------| 0CH
| Error Code |
|---------------+---------------| 08H
| | Return CS |
|---------------+---------------| 04H
| Return EIP |
|-------------------------------| 00H <-- SS:ESP
</pre>
The error code in the stack frame is only valid for the following
exceptions:
<table>
<tr><td align=center>08H</td><td align=left>Double fault</td></tr>
<tr><td align=center>0AH</td><td align=left>Invalid TSS</td></tr>
<tr><td align=center>0BH</td><td align=left>Segment not present</td></tr>
<tr><td align=center>0CH</td><td align=left>Stack fault</td></tr>
<tr><td align=center>0DH</td><td align=left>General protection fault</td></tr>
<tr><td align=center>0EH</td><td align=left>Page Fault</td></tr>
</table>
In the case of other exceptions and faults, the value of the error
code is undefined. The fields marked <tt>Return CS</tt>, <tt>Return
(E)IP</tt>, and <tt>Reserved</tt> in the stack frame should should not
be modified, but anything else in the stack frame can be altered by
the client before returning from the exception handler. <p>
The exception handler must preserve and restore all registers, and
must either jump to the next handler in the chain or terminate with a
<tt>RETF</tt> (far return) instruction. In the latter case, the
original SS:(E)SP, CS:(E)IP and flags on the stack, including the
interrupt flag, will be restored. The exception handler can arrange
to transfer control to a more general error-handling routine within
the application by modifying the CS:(E)IP that is stored in the stack
frame above the Return CS:(E)IP. <p>
DPMI version 1.0 supports an expanded stack frame for exception
handlers, and the ability to install separate handlers for exceptions
which occur in real mode and in protected mode with <a
href="api/310210.html">Functions 0212H</a> and <a
href="api/310213.html">0213H</a>. The expanded frame is defined on
the stack <i>above</i> the frame previously described for handlers
installed with <a href="api/310203.html">Function 0203H</a>. This
allows DPMI 0.9-compatible handlers and DPMI 1.0-compatible handlers
to coexist in the same handler chain; old handlers will be oblivious
to the additional information available beyond the old stack frame,
while new handlers can ignore the old frame and use only the expanded
frame higher up on the stack. <p>
The format of the expanded stack frame for both 16-bit and 32-bit
handlers installed with <a href="api/310212.html">Functions 0212H</a>
and <a href="api/310213.html">0213H</a> is as follows:
<pre>
31 15 0
+---------------------------------------------------------------+ 58H
| PTE |
|---------------------------------------------------------------| 54H
| CR2 |
|---------------------------------------------------------------| 50H
| Reserved | GS |
|---------------------------------------------------------------| 4CH
| Reserved | FS |
|---------------------------------------------------------------| 48H
| Reserved | DS |
|---------------------------------------------------------------| 44H
| Reserved | ES |
|---------------------------------------------------------------| 40H
| Reserved | SS |
|---------------------------------------------------------------| 3CH
| ESP |
|---------------------------------------------------------------| 38H
| EFLAGS |
|-------------------------------+-------------------------------| 34H
| Exception information bits | CS |
|-------------------------------+-------------------------------| 30H
| EIP |
|-------------------------------+-------------------------------| 2CH
| Error Code |
|-------------------------------+-------------------------------| 28H
| Reserved | Return CS (32-bit) or 0 |
|-------------------------------+-------------------------------| 24H
| Return EIP (32-bit) or CS:IP (16-bit) |
|-------------------------------+-------------------------------| 20H+SS:(E)SP
</pre>
A 32-bit stack frame image is always presented, even for 16-bit
handlers, and the offset from SS:(E)SP to the expanded stack frame is
always 20H (32) regardless of the handler type. The DS, ES, FS, and
GS registers are saved for both real and protected mode. The client
can inspect the VM bit in EFLAGS to determine the mode at the point of
exception. The CS field at SS:(E)SP+24H is zero if the handler is
running in 16-bit protected mode. <p>
The exception information bits at SS:(E)SP+32H have the following
meanings: <p>
<table border=1 cellspacing=0 cellpadding=4>
<tr> <th>Bit</th> <th colspan=2>Significance</th> </tr>
<tr> <td align=center rowspan=2>0</td>
<td align=center>0</td> <td align=left>exception occurred in the
client</td></tr>
<td align=center>1</td> <td align=left>exception occurred in the host
(most likely due to page fault or invalid selector passed to host in
an Int 31H call)</td></tr>
<tr> <td align=center rowspan=2>1</td>
<td align=center>0</td> <td align=left>exception can be retried</td></tr>
<td align=center>1</td> <td align=left>Exception cannot be retried,
handler should perform whatever cleanup is possible</td></tr>
<tr> <td align=center rowspan=2>2</td>
<td align=center>0</td> <td align=left>host exception should be
retried (invalid selector or page causing fault corrected by exception
handler, this is the default)</td></tr>
<td align=center>1</td> <td align=left>host exception is being
redirected somewhere other than a retry address</td></tr>
<tr> <td align=center>3</td><td colspan=2
align=left>reserved</td></tr>
</table><p>
Bits 0 through 2 of the exception information bits are relevant on
hosts which support the Exceptions Restartability capability (see <a
href="api/310401.html">Int 31H Function 0401H</a>). Bits 0 and 1 of
the exception information bits are supplied to the client by the host.
The default state of bit 2 as set by the host is zero, and the client
may st the bit to 1 before returning from the exception handler.<p>
Bit 0-14 of the error code at SS:(E)SP+28H are the "virtual" DR6 on
debug (Int 1) exceptions, and correspond to debug breakpoint 0-14. In
other words, if bits 0 and 2 are set in the error code field on an Int
1 exception, then debug watchpoints 0 and 2 have fired. The handle
returned by the Set Debug Watchpoint (<a
href="api/310b00.html">Function 0B00H</a>) corresponds to the bit
number in the virtual DR6. Bit 15 of the virtual DR6 is set (1) if
the Int 1 is due to the trap flag. Breakpoints may be virtualized,
and there is no guarantee of correspondence with the actual hardware.
The provision for up to 15 breakpoints is made for future CPUs or
external debugging hardware (80386 and 80486 CPUs support only four
hardware breakpoints). <p>
The PTE and CR2 fields of the expanded stack frame at SS:(E)SP+50H and
54H respectively are only valid for page faults (Int 0EH). Bits 0-7
of the PTE (page table entry) field are from the actual PTE and may be
virtualized by the host; the remaining bits of the PTE field are
undefined. The CR2 field contains the linear address that caused the
fault. <p>
Exception handlers installed with <a href="api/310212.html">Functions
0212H</a> and <a href="api/310213.html">0213H</a> may terminate in any
of the following three ways:
<ul>
<li> <tt>RETF</tt> from the old-style stack frame (only modifications
to the old-style stack frame will be recognized and a client may not
use this type of return for real-mode exceptions);<p>
<li> Discard the old-style stack frame by adding 20H (32) to (E)SP,
then <tt>RETF</tt> from the new-style (expanded) stack frame);<p>
<li> <tt>FAR JUMP</tt> to previous owner of the exception (the previous
owner should never be <tt>CALL</tt>ed).<p>
</ul>
The fields at offsets 2CH through 4FH in the expanded stack frame may
be modified by an exception handler. Note that the handler should
only modify the values in the particular frame (SS:(E)SP)+0 or
SS:(E)SP+20H) that it will use for the <tt>RETF</tt>. Altered values
in the other frame are ignored by the DPMI host. Real mode exceptions
do not have valid data in the old-style frame. A real mode exception
handler must discard the old-style stack frame if it returns.<p>
<b>Example:</b> The following code illustrates how a client would
install its own exception handler for general protection (GP) faults.
The actual handler does nothing more than reach into the stack frame
and alter the return address, so that control within the application
restarts at a different point after the exception handler exits.
<pre>
prevgp dd 0 ; address of previous
; GP fault handler
.
. ; this code is executed during
. ; application initialization...
mov ax,0210h ; get address of previous
mov bl,13 ; owner of GP fault vector
<a href="api/310210.html">int 31h</a>
mov word ptr prevgp,dx ; save as far pointer
mov word ptr prevgp+2,cx
mov ax,0212h ; install our GP fault handler
mov bl,13
mov cx,cs ; CX:DX = handler address
mov dx,offset _TEXT:gpfisr
<a href="api/310212.html">int 31h</a>
jc init9 ; jump, couldn't install
. ; continue with initialization
.
.
gpfisr proc far ; this is the actual exception
; handler for GP faults
add sp,20h ; discard "old" stack frame
push bp ; point CS:IP in stack frmae to
mov bp,sp ; GP fault error message routine
mov word ptr [bp+0eh],offset _TEXT: gpferr
mov word ptr [bp+12h],cs
pop bp
ret ; now return from exception
gpfisr endp
gpferr proc near ; this routine executes after
; GPFISR returns to DPMI host
mov ax,4c01h ; terminate DPMI client with
int 21h ; nonzero return code
gpferr endp
</pre>

View File

@@ -0,0 +1,231 @@
The DPMI real mode callback mechanism allows a DPMI protected mode
client to be called as a subroutine by real mode programs in a
transparent manner. That is, a real mode program can use a real mode
callback to pass information to the DPMI client, or obtain services
provided by the DPMI client, without necessarily being aware of
protected mode or extended memory in any way. The callback mechanism
can be thought of as the converse of <a href="api/310300.html">DPMI
Int 31H Functions 0300H</a>, <a href="api/310301.html">0301H</a>, and
<a href="api/310302.html">0302H</a>, which allow a DPMI client to pass
information to a real mode program, or obtain services from a real mode
program, in a manner that is similarly transparent to the real mode
program. <p>
In order to make a real mode callback available, the DPMI client must
first call <a href="api/310303.html">Int 31H Function 0303H</a> with
the selector and offset of the protected mode routine which will
receive control when the callback is entered, and the selector and
offset of a real mode register data structure (in the same format as
used for <a href="api/310300.html">Int 31H Functions 0300H</a>, <a
href="api/310301.html">0301H</a>, and <a
href="api/310302.html">0302H</a>). <a href="api/310303.html">Function
0303H</a> will return a real mode address (segment and offset) for the
callback entry point that can be passed to a real mode program via a
software interrupt or far call (<a href="api/310300.html">Int 31H
Functions 0300H</a>, <a href="api/310301.html">0301H</a>, or <a
href="api/310302.html">0302H</a>), a DOS memory block, or any other
convenient mechanism. <p>
When the real mode program executes a <tt>FAR CALL</tt> to the real
mode callback address supplied to it by the DPMI client, the DPMI host
saves the contents of all real mode registers into the DPMI client's
real mode register data structure, switches the CPU into protected
mode, and enters the DPMI client's callback routine with the following
conditions:
<ul>
<li> Interrupts disabled
<li> CS:(E)IP = selector:offset specified in the original call to
<a href="api/310303.html">Int 31H Function 0303H</a>
<li> DS:(E)SI = selector:offset corresponding to real mode SS:SP
<li> ES:(E)DI = selector:offset of real mode register data structure
<li> SS:(E)SP = locked protected mode stack provided by DPMI host
<li> All other registers undefined
</ul>
The format of the real mode register data structure is as follows:
(Note that the content of the 32H bytes data structure are undefined
at the time of the original <a href="api/310303.html">Int 31H Function
0303H</a> call.) <p>
<blockquote><table border=1 cellspacing=0 cellpadding=4>
<tr><th>Offset</th><th>Length</th><th>Contents</th></tr>
<tr><td align=center>00H</td><td align=center>4</td><td align=left>DI or EDI</td></tr>
<tr><td align=center>04H</td><td align=center>4</td><td align=left>SI or ESI</td></tr>
<tr><td align=center>08H</td><td align=center>4</td><td align=left>BP or EBP</td></tr>
<tr><td align=center>0CH</td><td align=center>4</td><td align=left>reserved, should be zero</td></tr>
<tr><td align=center>10H</td><td align=center>4</td><td align=left>BX or EBX</td></tr>
<tr><td align=center>14H</td><td align=center>4</td><td align=left>DX or EDX</td></tr>
<tr><td align=center>18H</td><td align=center>4</td><td align=left>CX or ECX</td></tr>
<tr><td align=center>1CH</td><td align=center>4</td><td align=left>AX or EAX</td></tr>
<tr><td align=center>20H</td><td align=center>2</td><td align=left>CPU status flags</td></tr>
<tr><td align=center>22H</td><td align=center>2</td><td align=left>ES</td></tr>
<tr><td align=center>24H</td><td align=center>2</td><td align=left>DS</td></tr>
<tr><td align=center>26H</td><td align=center>2</td><td align=left>FS</td></tr>
<tr><td align=center>28H</td><td align=center>2</td><td align=left>GS</td></tr>
<tr><td align=center>2AH</td><td align=center>2</td><td align=left>IP</td></tr>
<tr><td align=center>2CH</td><td align=center>2</td><td align=left>CS</td></tr>
<tr><td align=center>2EH</td><td align=center>2</td><td align=left>SP</td></tr>
<tr><td align=center>30H</td><td align=center>2</td><td align=left>SS</td></tr>
</table></blockquote>
The callback procedure can then extract its parameters from the real
mode register data structure and/or copy parameters from the real mode
stack to the protected mode stack. Recall that the segment register
fields of the real mode register data structure contain segment or
paragraph addresses that are not valid in protected mode. Far
pointers passed in the real mode register data structure must be
translated to virtual addresses before they can be used. The
recommended procedure is for the DPMI client to allocate a selector
for this purpose during its initialization, then use <a
href="api/310007.html">Int 31H Function 0007H</a> within the callback
procedure to set the segment base to 16 times the value found in the
real mode segment register. The DPMI client should <i>not</i> use <a
href="api/310002.html">Int 31H Function 0002</a> for this purpose,
because selectors allocated by <a href="api/310002.html">Function
0002</a> can never be freed. <p>
The callback procedure exits by executing an <tt>IRET</tt> with the
address of the real mode register data structure in ES:(E)DI, passing
information back to the real mode caller by modifying the contents of
the real mode register data structure and/or manipulating the contents
of the real mode stack. The callback procedure is responsible for
setting the proper address for resumption of real mode execution into
the real mode register data structure; typically, this is accomplished
by extracting the return address from the real mode stack and placing
it into the CS:IP fields of the real mode register data structure.
After the <tt>IRET</tt>, the DPMI host switches the CPU back into real
mode, loads all registers (including CS:IP) with the contents of the
real mode register data structure, and finally returns control to the
real mode program.<p>
Since the real mode call structure and the selector used for the real
mode SS are static, care must be taken when writing DPMI client
callback procedures that may be reentered (for example, by a real mode
program that services hardware interrupt). The simplest method of
avoiding reentrancy is to leave interrupts disabled throughout the
entire callback procedure. However, if the amount of code executed by
the callback is large, the client may find it more desirable to copy
the real mode register data structure into a dynamically allocated
buffer and then re-enable interrupts and not use the incoming DS any
more. The real mode register data structure pointed to by ES:(E)DI
upon return from the callback procedure is <i>not</i> required to be
at the same address as the original real mode register data
structure.<p>
DPMI hosts must provide a minimum of 16 callback addresses per virtual
machine. Real mode callbacks are a limited system resource. A DPMI
client should always use <a href="api/310304.html">Int 31H Function
0304H</a> to free any callbacks that it is no longer using. <p>
<b>Example:</b> The following code is an example of a real mode
interrupt hook. It hooks the DOS Int 21h and returns an error for the
delete file function (AH=41H). Other calls are passed through to DOS.
This example demonstrates the techniques used to hook a real mode
interrupt. Note that since DOS calls are reflected from protected
mode to real mode, the following code will intercept all DOS calls
from both real mode and protected mode.
<pre>
;******************************************************
;
; This procedure gets the current Int 21H real mode
; Seg:Offset, allocates a real mode call-back address,
; and sets the real mode Int 21h vector to the call-
; back address.
;
;******************************************************
Initialization_Code:
; Create a code segment alias to save data in
;
mov ax, 000Ah
mov bx, cs
<a href="api/31000a.html">int 31h</a>
jc ERROR
mov ds, ax
assume ds:_TEXT
; Get current Int 21h real mode SEG:OFFSET
;
mov ax, 0200h
mov bl, 21h
<a href="api/310200.html">int 31h</a>
jc ERROR
mov [Orig_Real_Seg], cx
mov [Orig_Real_Offset], dx
; Allocate a real mode call-back
;
mov ax, 0303h
push ds
mov bx, cs
mov ds, bx
mov si, OFFSET My_Int_21_Hook
pop es
mov di, OFFSET My_Real_Mode_Call_Struc
<a href="api/310303.html">int 31h</a>
jc ERROR
; Hook real mode int 21h with the call-back
; address
;
mov ax, 0201h
mov bl, 21h
int 21h
jc ERROR
;******************************************************
;
; This is the actual Int 21h hook code. It will return
; an "access denied" error for all calls made in real
; mode to delete a file. Other calls will be passed
; through to DOS.
;
; ENTRY:
; DS:SI -> Real mode SS:SP
; ES:DI -> Real mode call structure
; Interrupts disabled
;
; EXIT:
; ES:DI -> Real mode call structure
;
******************************************************
My_Int_21_Hook:
cmp es: [di.RealMode_AH], 41h
jne Chain_To_DOS
;
; This is a delete file call (AH=41h). Simulate
; an iret on the real mode stack, set the real
; mode carry flag, and set the real mode AX to 5
; to indicate an access denied error.
;
cld
lodsw ; Get real mode ret IP
mov es:[di.RealMode_IP], ax
lodsw ; Get real mode ret CS
mov es:[di.RealMode_CS], ax
lodsw ; Get real mode flags
or ax,1 ; St carry flag
mov es:[di.RealMode_Flags], ax
add es:[di.RealMode_SP], 6
mov es:[di.RealMode_AX], 5
jmp My_Hook_Exit
;
; Chain to original Int 21h vector by replacing
; the real mode CS;IP with the original Seg:Offset.
;
Chain_To_Dos:
mov ax, cs:[Orig_Real_Seg]
mov es:[di.RealMode_CS], ax
mov ax, cs:[Orig_Real_Offset]
mov es:[di.RealMode_IP], ax
My_Hook_Exit:
iret
</pre>

View File

@@ -0,0 +1,190 @@
Shared memory blocks can be used for inter-client communication or
simply to hold tables or subroutine libraries that are needed by more
than one client. Explicit use of shared blocks is necessary because
each VM has its own linear address space, and thus cannot inspect or
modify the memory owned by a client in another virtual machine. The
basic strategy for use of a shared memory block is as follows:
<ul>
<li> Allocate shared memory block;<p>
<li> Establish addressability for the shared block;<p>
<li> Make a successful serialization request for the shared block
(i.e. obtain ownership or right of access to the block);<p>
<li> Access the code or data in the shared block; <p>
<li> Free the serialization of the shared block;<p>
<li> Deallocate the shared memory block.<p>
</ul>
Shared memory blocks are allocated with <a href="api/310d00.html">Int 31H Function 0D00H</a>. The
client passes the address of a data structure that specifies the ASCIIZ
name and requested size of the shared block to the host; the host
returns the block's handle, linear base address, and actual size in
the same structure. The block's true size is determined by the first
client to allocate the block, and the block will have the same linear
address for all clients which allocate it. <p>
After the shared block is allocated, the client must allocate one or
more descriptors that will be used to address the block with <a
href="api/310000.html">Int 31H Function 0000H</a>. Once descriptor(s)
have been allocated and initialized to point to a shared memory block
through separate LDT management calls, the client has the physical
capability to read, write, or execute addresses within the block as
allowed by the access rights/type byte. The client should synchronize
with any other clients which might have addressability to the same
block, to avoid race conditions or corruption of data. This
synchronization is accomplished with <a href="api/310d02.html">Int 31H
Function 0D02H</a> (Serialize on Shared Memory) and <a
href="api/310d03.html">Int 31H Function 0D03H</a> (Free Serialization
on Shared Memory). Serialization can be thought of as representing
ownership or right of access to a shared memory block.<p>
In essence, <a href="api/310d02.html">Int 31H Functions 0D02H</a> and
<a href="api/310d03.html">0D03H</a> treat the handle of a shared
memory block as a semaphore. The client can request exclusive
(read/write) or shared (read-only) serialization with <a
href="api/310d02.html">Int 31H Function 0D02H</a>, and the host will
grant the serialization if no other client has already obtained a
conflicting serialization on the same memory block. The client can
then go ahead and manipulate the shared memory block, releasing the
serialization with <a href="api/310d03.html">Int 31H Function
0D03H</a> when it is finished using the block. If the <a
href="api/310d02.html">Int 31H Function 0D02H</a> serialization request
fails, the client will either be suspended until the serialization is
available, or the function will return with an error code, depending
on the parameters supplied by the client. <p>
The first paragraph (16 bytes) of the shared memory block (or the
entire shared block, if smaller than 16 bytes) will always be
initialized to zero on the first allocation and can be used by clients
as an "area initialized" indicator. For example, a shared memory
block might be used by a suite of cooperating client programs to hold
a table of static data or a subroutine library. The first client to
allocate the shared memory block can obtain exclusive ownership of the
block with <a href="api/310d02.html">Int 31H Function 0D02H</a>, load
the necessary data or code into the block from disk, set the first 16
bytes of the block to a nonzero value, and finally release its
ownership of the block with <a href="api/310d03.html">Int 31H Function
0D03H</a>. Other clients that allocate the shared memory block can
check the "area initialized" indicator and know that the desired code
or data is already present in memory. <p>
When a client has finished using the shared memory block, it should
deallocate the shared block with <a href="api/310d01.html">Int 31H
Function 0D01H</a>. After the block is deallocated, the linear
addresses within the block are no longer valid for the current client,
and may cause an exception if accessed. However, the block is not
actually destroyed until all clients which have allocated the block
have also deallocated it. <p>
Note that a client can make multiple (nested) allocation requests for
the same shared memory block, and should assume that each allocation
request will return a distinct handle. The shared block will remain
physically accessible to the client until each of its handles to the
block have been deallocated. Similarly, a client can make multiple
serialization requests for the same block, and will retain "ownership"
of the block until a corresponding number of deserialization requests
have been issued. Lastly, allocation of zero-length shared memory
blocks is explicitly allowed, so that clients can use the handles
resulting from such allocations as pure semaphores. <p>
<b>Example:</b> The following code illustrates how shared memory can
be used to load code and data that can be used by more than one DPMI
client. Note that no serialization calls are required if the memory
is already initialized.
<pre>
memreqstruc struc
length dd ? ; number of bytes requested
actual dd ? ; number of bytes allocated
handle dd ? ; handle for shared memory block
base dd ? ; linear address of shared block
nameptr dp ? ; pointer to shared memory name
dw 0 ; reserved, must be zero
dd 0 ; reserved, must be zero
memreqstruc ends
memname db 'MYBLOCK',0
memreq memreqstruc <> ; allocate request block
.
.
.
mov word ptr memreq.length,2000h ; set reqeusted length
mov word ptr memreq.length+2,0 ; of shared block to 8 KB
; initialize nameptr
mov dword ptr memreq.nameptr, offset memname
mov word ptr memreq.nameptr+4, ds
mov di,ds ; ES:DI = address of shared
mov es,di ; memory request structure
mov di,offset memreq
mov ax,0d00h ; DPMI fxn 0D00H = allocate
<a href="api/310d00.html">int 31h</a> ; shared memory block
jc error ; jump if allocation failed
mov cx,1 ; allocate one LDT descriptor
mov ax,0 ; using DPMI Function 0000h
<a href="api/310000.html">int 31h</a>
jc error ; jump, no descriptor available
mov bx,ax ; let BX = new selector
mov dx,word ptr memreq.base ; let CX:DX = linear base
mov cx,word ptr memreq.base+2 ; address of shared block
mov ax,0007h ; set descriptor base address
<a href="api/310007.html">int 31h</a> ; using DPMI Function 0007H
jc error ; jump, function failed
mov dx,word ptr memreq.actual ; set CX:DX = length-1
mov cx,word ptr memreq.actual+2 ; of shared memory block
sub dx,0
sbb cx,0 ; (BX still = selector)
mov ax,8 ; set descriptor limit using
<a href="api/310008.html">int 31h</a> ; DPMI Function 0008H
jc error ; jump, function failed
mov es,bx ; ES = selector for shared block
mov ax,es:[0] ; is block already initialized
or ax,ax
jnz @@1 ; jump if it's initialized
; not initialized, get ownership
; of the shared memory block
mov di,word ptr memreq.handle ; SI:DI = handle for
mov si,word ptr memreq.handle+2 ; shared memory block
mov dx,0 ; exclusive + wait for ownership
mov ax,0d02h ; DPMI Fxn 0D02H = serialize
<a href="api/310d02.html">int 31h</a>
jc error ; jump if serialization failed
mov ax,es: [0] ; check again if someone else
or ax,ax ; already initialized block
jnz @@2 ; jump if it's initialized
.
. ; load code into the shared
. ; memory block here...
.
@@2: ; now release ownership of
; the shared memory block
mov di,word ptr memreq.handle ; SI:DI = handle for
mov si,word ptr memreq.hanlde+2 ; shared memory block
mov dx,0 ; serialization type = exclusive
mov ax,0d03h ; DPMI Fxn 0D03H = release
<a href="api/310d03.html">int 31h</a>
jc error ; jump if serialization failed
@@1: ; finished initializing the
; shared memory block
</pre>

View File

@@ -0,0 +1,100 @@
A DPMI client that provides resident protected mode services (also
called a "protected mode TSR") must install itself using <a
href="api/310c00.html">Int 31H Functions 0C00H</a> and <a
href="api/310c01.html">0C01H</a>. The protected mode TSR first
declares its intent to remain resident by calling <a
href="api/310c00.html">Function 0C00H</a>, providing the DPMI host
with the code and data descriptors and callback entry points for
16-bit and/or 32-bit protected mode. If the TSR does not wish to
provide services in a particular mode, it provides a code descriptor
for that mode containing all zero bytes. The protected mode TSR then
terminates and stays resident by calling <a href="api/310c01.html">Int
31H Function 0C01H</a>. Note that after this function call, the TSR's
original addressing context is destroyed; its LDT and IDT no longer
exist, although any extended memory blocks it owned at the time of
termination remain allocated. <p>
Whenever another DPMI client <i>in the same virtual machine</i> loads
or terminates, the DPMI host will inspect its list of protected mode
TSRs. If a particular TSR has indicated that it can execute in the
active client's mode, the DPMI host will automatically allocate two
LDT descriptors in the active client's context, initialize them to the
values specified in the protected mode TSR's original <a
href="api/310c00.html">Function 0C00H</a> call, and enter the TSR via
a <tt>FAR CALL</tt> at the offset appropriate to the current mode,
passing the following values:<p>
<table border=1 cellspacing=0 cellpadding=4>
<tr><td>CS</td><td align=left>executable selector which maps the same
memory as the code descriptor in the <a href="api/310c00.html">Int 31H
Function 0C00H</a> data structure for the current mode (16-bit or
32-bit)</td></tr>
<tr><td>(E)IP</td><td align=left>Offset specified in the <a
href="api/310c00.html">Int 31H Function 0C00H</a> data structure for
the current mode (16-bit or 32-bit)</td></tr>
<tr><td>DS</td><td align=left>read/write data selector which maps the
same memory as the data descriptor specified in the <a
href="api/310c00.html">Int 31H Function 0C00H</a> data structure for
the current mode (16-bit or 32-bit)</td></tr>
<tr><td>ES</td><td align=left>0</td></tr>
<tr><td>FS</td><td align=left>0</td></tr>
<tr><td>GS</td><td align=left>0</td></tr>
<tr><td>AX</td><td align=left>Reason for callback: 0=DPMI client
loading, 1=DPMI client terminating</td></tr>
<tr><td>BX</td><td align=left>unique handle for the client within the
virtual machine</td></tr>
</table><p>
When a new DPMI client is loaded and executes the initial switch to
protected mode, the appropriate callback procedure in the protected
mode TSR will be entered by a <tt>FAR CALL</tt> with AX=0
<i>before</i> the DPMI host returns to the new program. The TSR may
then hook interrupts, create descriptors, or allocate memory blocks in
the new client's context prior to the client's access to such
protected mode resources. For example, the initialization callback
gives the TSR an opportunity to insert itself in the chain of handlers
for any arbitrary interrupt or exception. The TSRs are invoked in the
order of their installation but are removed in the reverse order. The
TSR may also need to construct per-client data structures in its own
memory, and can use the value supplied to it in BX as a "handle" for
the client. The TSR must exit from the initialization callback with a
<tt>RETF</tt>.
Similarly, when a DPMI client terminates using Int 21H Function 4CH or
<a href="api/310c01.html">Int 31H Function 0C01H</a>, the TSR's
callback procedure will be entered by a <tt>FAR CALL</tt> with AX=1
<i>before</i> the active client's LDT or IDT has been destroyed. The
protected mode TSR can then perform any client termination
responsibilities of which the client is unaware (e.g. unmapping of
physical memory), release any protected mode resources which it has
acquired on behalf of the client (e.g. ownership of shared memory),
and deallocate any per-client data structures of its own. The
termination callback must exit with a <tt>RETF</tt> and indicate an
action to the DPMI host as follows:
<ul>
Carry = clear to keep resident services in memory, or<br>
Carry = set to remove resident services from memory.
</ul>
A resident service provider should only be removed from memory of the
last client of the virtual machine they are servicing.<p>
<a href="api/310c00.html">Int 31H Functions 0C00H</a> and <a
href="api/310c01.html">0C01H</a> should only be used by DPMI clients
which intend to provide resident services to other protected mode
clients. If the objective is only to provide resident services to
real mode programs, the client should use the DPMI translation service
<a href="api/310300.html">Int 31H Function 0300H</a> to invoke DOS's
Int 31H Function 31H directly. <p>

View File

@@ -0,0 +1,111 @@
Programs that use DPMI services are called DPMI clients. Generally,
DPMI clients fall into one of two categories:<p>
<ul>
<li> DOS Extenders which use DPMI services as building blocks for a
more extensive interface that is exported to application programs running under their control;<p>
<li> Application programs that call DPMI directly.<p>
</ul>
In the near term, most client programs will need to be able to run in
several different environments, each providing a different interface
and range of services. It is recommended that clients test for the
existence of such environments in the following order:<p>
<ul>
<li> DOS Protected Mode Interface (DPMI)-compatible host;<p>
<li> Virtual Control Program Interface (VCPI)-compatible server;<p>
<li> eXtended Memory Specification (XMS)-compatible driver;<p>
<li> <i>Top-Down memory allocation</i> (See Appendix A: Glossary) or
bottom-up (VDISK-compatible) memory allocation.<p>
</ul>
Figure 2 on page 21 illustrates a typical DPMI client configuration,
consisting of a DOS Extender and a protected-mode application. The
application code relies on the DOS Extender functions and APIs. The
DOS Extender contains separate modules for each possible environment,
and code to implement those services that are lacking in a particular
environment. <p>
Existing DOS extenders support APIs that differ from the Int 31h
interface. Usually, DOS extenders use an Int 21h multiplex for their
extended APIs. Extenders that support DPMI will need to initialize
differently when they are run under DPMI environments. They will need
to enter protected mode using the DPMI real to protected mode entry
point, install their own API handlers, and then load the DOS extended
application program. <p>
<b>Figure 2.</b> An example of a DPMI client consisting of a DOS
Extender and a protected-mode application. The client should be able
to run in the presence of DPMI, VCPI, or XMS environments or in the
absence of all three.
<pre>
+----------------------------------------------------------+
| |
| +----------------------------------------------------+ |
| | | |
| | Application Code | |
| | | |
| +----------------------------------------------------+ |
| |
| +----------------------------------------------------+ |
| | Extender Base (including APIs) | |
| | -------------------------------------------------- | |
| | DPMI | |
| | client | |
| +------------+ | |
| | VCPI | |
| | client | |
| +------------+ | |
| | XMS | |
| | client | |
| +------------+ | |
| | Top-down | |
| | client | |
| +-------------+ |
| |
+----------------------------------------------------------+
+------------+
| |
| |
| |------------+
| | |
| DPMI | |
| host | VCPI |------------+
| | | |
| | | |
| |------------| XMS |-------------+
| | EMS | | Top-down |
| | | | (Int 15h) |
+----------------------------------------------------+
+----------------------------------------------------+
| |
| Operating System (e.g. DOS) |
| |
+----------------------------------------------------+
</pre>
<hr>
<img src="/icons/menu.sm.gif"><a href="ch4.1.html">Client Initialization</a><br>
<img src="/icons/menu.sm.gif"><a href="ch4.2.html">Client Termination</a><br>
<img src="/icons/menu.sm.gif"><a href="ch4.3.html">Stacks and Mode Switching</a><br>
<img src="/icons/menu.sm.gif"><a href="ch4.4.html">Handling Interrupts</a><br>
<img src="/icons/menu.sm.gif"><a href="ch4.5.html">Handling CPU Exceptions</a><br>
<img src="/icons/menu.sm.gif"><a href="ch4.6.html">Using Real-Mode Callbacks</a><br>
<img src="/icons/menu.sm.gif"><a href="ch4.7.html">Using Shared Memory</a><br>
<img src="/icons/menu.sm.gif"><a href="ch4.8.html">Writing Resident Service Providers</a><br>

View File

@@ -0,0 +1,85 @@
<img src="0.9.gif" alt="[0.9]"> <a href="api/310100.html"><tt>31 0100</tt> - Allocate DOS Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310000.html"><tt>31 0000</tt> - Allocate LDT Descriptors</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310504.html"><tt>31 0504</tt> - Allocate Linear Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310501.html"><tt>31 0501</tt> - Allocate Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310303.html"><tt>31 0303</tt> - Allocate Real Mode Callback Address</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d00.html"><tt>31 0d00</tt> - Allocate Shared Memory</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000d.html"><tt>31 000d</tt> - Allocate Specific LDT Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310301.html"><tt>31 0301</tt> - Call Real Mode Procedure With Far Return Frame</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310302.html"><tt>31 0302</tt> - Call Real Mode Procedure With IRET Frame</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b01.html"><tt>31 0b01</tt> - Clear Debug Watchpoint</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000a.html"><tt>31 000a</tt> - Create Alias Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310703.html"><tt>31 0703</tt> - Discard Page Contents</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310101.html"><tt>31 0101</tt> - Free DOS Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310001.html"><tt>31 0001</tt> - Free LDT Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310502.html"><tt>31 0502</tt> - Free Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310801.html"><tt>31 0801</tt> - Free Physical Address Mapping</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310304.html"><tt>31 0304</tt> - Free Real Mode Callback Address</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d03.html"><tt>31 0d03</tt> - Free Serialization on Shared Memory</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d01.html"><tt>31 0d01</tt> - Free Shared Memory</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/2f1686.html"><tt>2f 1686</tt> - Get CPU Mode</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310e00.html"><tt>31 0e00</tt> - Get Coprocessor Status</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310401.html"><tt>31 0401</tt> - Get DPMI Capabilities</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000b.html"><tt>31 000b</tt> - Get Descriptor</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310210.html"><tt>31 0210</tt> - Get Extended Processor Exception Handler Vector (Protected Mode)</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310211.html"><tt>31 0211</tt> - Get Extended Processor Exception Handler Vector (Real Mode)</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310500.html"><tt>31 0500</tt> - Get Free Memory Information</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31050a.html"><tt>31 050a</tt> - Get Memory Block Size and Base</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31050b.html"><tt>31 050b</tt> - Get Memory Information</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31000e.html"><tt>31 000e</tt> - Get Multiple Descriptors</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310506.html"><tt>31 0506</tt> - Get Page Attributes</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310604.html"><tt>31 0604</tt> - Get Page Size</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310202.html"><tt>31 0202</tt> - Get Processor Exception Handler Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310204.html"><tt>31 0204</tt> - Get Protected Mode Interrupt Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310306.html"><tt>31 0306</tt> - Get Raw Mode Switch Addresses</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310200.html"><tt>31 0200</tt> - Get Real Mode Interrupt Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310006.html"><tt>31 0006</tt> - Get Segment Base Address</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310003.html"><tt>31 0003</tt> - Get Selector Increment Value</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310305.html"><tt>31 0305</tt> - Get State Save/Restore Addresses</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b02.html"><tt>31 0b02</tt> - Get State of Debug Watchpoint</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310a00.html"><tt>31 0a00</tt> - Get Vendor-Specific API Entry Point</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/2f168a.html"><tt>2f 168a</tt> - Get Vendor-Specific API Entry Point</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310400.html"><tt>31 0400</tt> - Get Version</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310902.html"><tt>31 0902</tt> - Get Virtual Interrupt State</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310900.html"><tt>31 0900</tt> - Get and Disable Virtual Interrupt State</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310901.html"><tt>31 0901</tt> - Get and Enable Virtual Interrupt State</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310c00.html"><tt>31 0c00</tt> - Install Resident Service Provider Callback</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310600.html"><tt>31 0600</tt> - Lock Linear Region</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310509.html"><tt>31 0509</tt> - Map Conventional Memory in Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310508.html"><tt>31 0508</tt> - Map Device in Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310702.html"><tt>31 0702</tt> - Mark Page as Demand Paging Candidate</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310602.html"><tt>31 0602</tt> - Mark Real Mode Region as Pageable</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/2f1687.html"><tt>2f 1687</tt> - Obtain Real-to-Protected Mode Switch Entry Point</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310800.html"><tt>31 0800</tt> - Physical Address Mapping</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/2f1680.html"><tt>2f 1680</tt> - Release Current Virtual Machine's Time Slice</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310603.html"><tt>31 0603</tt> - Relock Real Mode Region</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310004.html"><tt>31 0004</tt> - Reserved</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310005.html"><tt>31 0005</tt> - Reserved</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310700.html"><tt>31 0700</tt> - Reserved</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310701.html"><tt>31 0701</tt> - Reserved</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b03.html"><tt>31 0b03</tt> - Reset Debug Watchpoint</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310102.html"><tt>31 0102</tt> - Resize DOS Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310505.html"><tt>31 0505</tt> - Resize Linear Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310503.html"><tt>31 0503</tt> - Resize Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310002.html"><tt>31 0002</tt> - Segment to Descriptor</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d02.html"><tt>31 0d02</tt> - Serialize on Shared Memory</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310e01.html"><tt>31 0e01</tt> - Set Coprocessor Emulation</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b00.html"><tt>31 0b00</tt> - Set Debug Watchpoint</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310009.html"><tt>31 0009</tt> - Set Descriptor Access Rights</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000c.html"><tt>31 000c</tt> - Set Descriptor</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310212.html"><tt>31 0212</tt> - Set Extended Processor Exception Handler Vector (Protected Mode)</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310213.html"><tt>31 0213</tt> - Set Extended Processor Exception Handler Vector (Real Mode)</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31000f.html"><tt>31 000f</tt> - Set Multiple Descriptors</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310507.html"><tt>31 0507</tt> - Set Page Attributes</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310203.html"><tt>31 0203</tt> - Set Processor Exception Handler Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310205.html"><tt>31 0205</tt> - Set Protected Mode Interrupt Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310201.html"><tt>31 0201</tt> - Set Real Mode Interrupt Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310007.html"><tt>31 0007</tt> - Set Segment Base Address</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310008.html"><tt>31 0008</tt> - Set Segment Limit</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310300.html"><tt>31 0300</tt> - Simulate Real Mode Interrupt</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310c01.html"><tt>31 0c01</tt> - Terminate and Stay Resident</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310601.html"><tt>31 0601</tt> - Unlock Linear Region</a><br>

View File

@@ -0,0 +1,105 @@
<h2>Initialization Services</h2>
<img src="1.0.gif" alt="[1.0]"> <a href="api/2f1680.html"><tt>2f 1680</tt> - Release Current Virtual Machine's Time Slice</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/2f1686.html"><tt>2f 1686</tt> - Get CPU Mode</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/2f1687.html"><tt>2f 1687</tt> - Obtain Real-to-Protected Mode Switch Entry Point</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/2f168a.html"><tt>2f 168a</tt> - Get Vendor-Specific API Entry Point</a><br>
<h2>LDT Management Services</h2>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310000.html"><tt>31 0000</tt> - Allocate LDT Descriptors</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310001.html"><tt>31 0001</tt> - Free LDT Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310002.html"><tt>31 0002</tt> - Segment to Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310003.html"><tt>31 0003</tt> - Get Selector Increment Value</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310006.html"><tt>31 0006</tt> - Get Segment Base Address</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310007.html"><tt>31 0007</tt> - Set Segment Base Address</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310008.html"><tt>31 0008</tt> - Set Segment Limit</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310009.html"><tt>31 0009</tt> - Set Descriptor Access Rights</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000a.html"><tt>31 000a</tt> - Create Alias Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000b.html"><tt>31 000b</tt> - Get Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000c.html"><tt>31 000c</tt> - Set Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000d.html"><tt>31 000d</tt> - Allocate Specific LDT Descriptor</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31000e.html"><tt>31 000e</tt> - Get Multiple Descriptors</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31000f.html"><tt>31 000f</tt> - Set Multiple Descriptors</a><br>
<h2>Extended Memory Management Services</h2>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310500.html"><tt>31 0500</tt> - Get Free Memory Information</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310501.html"><tt>31 0501</tt> - Allocate Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310502.html"><tt>31 0502</tt> - Free Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310503.html"><tt>31 0503</tt> - Resize Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310504.html"><tt>31 0504</tt> - Allocate Linear Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310505.html"><tt>31 0505</tt> - Resize Linear Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310506.html"><tt>31 0506</tt> - Get Page Attributes</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310507.html"><tt>31 0507</tt> - Set Page Attributes</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310508.html"><tt>31 0508</tt> - Map Device in Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310509.html"><tt>31 0509</tt> - Map Conventional Memory in Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31050a.html"><tt>31 050a</tt> - Get Memory Block Size and Base</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31050b.html"><tt>31 050b</tt> - Get Memory Information</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310800.html"><tt>31 0800</tt> - Physical Address Mapping</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310801.html"><tt>31 0801</tt> - Free Physical Address Mapping</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d00.html"><tt>31 0d00</tt> - Allocate Shared Memory</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d01.html"><tt>31 0d01</tt> - Free Shared Memory</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d02.html"><tt>31 0d02</tt> - Serialize on Shared Memory</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d03.html"><tt>31 0d03</tt> - Free Serialization on Shared Memory</a><br>
<h2>DOS Memory Management Services</h2>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310100.html"><tt>31 0100</tt> - Allocate DOS Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310101.html"><tt>31 0101</tt> - Free DOS Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310102.html"><tt>31 0102</tt> - Resize DOS Memory Block</a><br>
<h2>Interrupt Management Services</h2>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310200.html"><tt>31 0200</tt> - Get Real Mode Interrupt Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310201.html"><tt>31 0201</tt> - Set Real Mode Interrupt Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310202.html"><tt>31 0202</tt> - Get Processor Exception Handler Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310203.html"><tt>31 0203</tt> - Set Processor Exception Handler Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310204.html"><tt>31 0204</tt> - Get Protected Mode Interrupt Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310205.html"><tt>31 0205</tt> - Set Protected Mode Interrupt Vector</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310210.html"><tt>31 0210</tt> - Get Extended Processor Exception Handler Vector (Protected Mode)</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310211.html"><tt>31 0211</tt> - Get Extended Processor Exception Handler Vector (Real Mode)</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310212.html"><tt>31 0212</tt> - Set Extended Processor Exception Handler Vector (Protected Mode)</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310213.html"><tt>31 0213</tt> - Set Extended Processor Exception Handler Vector (Real Mode)</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310900.html"><tt>31 0900</tt> - Get and Disable Virtual Interrupt State</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310901.html"><tt>31 0901</tt> - Get and Enable Virtual Interrupt State</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310902.html"><tt>31 0902</tt> - Get Virtual Interrupt State</a><br>
<h2>Translation Services</h2>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310300.html"><tt>31 0300</tt> - Simulate Real Mode Interrupt</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310301.html"><tt>31 0301</tt> - Call Real Mode Procedure With Far Return Frame</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310302.html"><tt>31 0302</tt> - Call Real Mode Procedure With IRET Frame</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310303.html"><tt>31 0303</tt> - Allocate Real Mode Callback Address</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310304.html"><tt>31 0304</tt> - Free Real Mode Callback Address</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310305.html"><tt>31 0305</tt> - Get State Save/Restore Addresses</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310306.html"><tt>31 0306</tt> - Get Raw Mode Switch Addresses</a><br>
<h2>Page Management Services</h2>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310600.html"><tt>31 0600</tt> - Lock Linear Region</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310601.html"><tt>31 0601</tt> - Unlock Linear Region</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310602.html"><tt>31 0602</tt> - Mark Real Mode Region as Pageable</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310603.html"><tt>31 0603</tt> - Relock Real Mode Region</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310604.html"><tt>31 0604</tt> - Get Page Size</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310702.html"><tt>31 0702</tt> - Mark Page as Demand Paging Candidate</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310703.html"><tt>31 0703</tt> - Discard Page Contents</a><br>
<h2>Debug Support Services</h2>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b00.html"><tt>31 0b00</tt> - Set Debug Watchpoint</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b01.html"><tt>31 0b01</tt> - Clear Debug Watchpoint</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b02.html"><tt>31 0b02</tt> - Get State of Debug Watchpoint</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b03.html"><tt>31 0b03</tt> - Reset Debug Watchpoint</a><br>
<h2>Miscellaneous Services</h2>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310400.html"><tt>31 0400</tt> - Get Version</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310401.html"><tt>31 0401</tt> - Get DPMI Capabilities</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310a00.html"><tt>31 0a00</tt> - Get Vendor-Specific API Entry Point</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310c00.html"><tt>31 0c00</tt> - Install Resident Service Provider Callback</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310c01.html"><tt>31 0c01</tt> - Terminate and Stay Resident</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310e00.html"><tt>31 0e00</tt> - Get Coprocessor Status</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310e01.html"><tt>31 0e01</tt> - Set Coprocessor Emulation</a><br>
<h2>Reserved Function Numbers</h2>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310004.html"><tt>31 0004</tt> - Reserved</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310005.html"><tt>31 0005</tt> - Reserved</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310700.html"><tt>31 0700</tt> - Reserved</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310701.html"><tt>31 0701</tt> - Reserved</a><br>

View File

@@ -0,0 +1,36 @@
The Int 2FH and Int 31H functions supported by DPMI version 1.0 hosts
are described in this section. Each function entry has five parts:
<ul>
<li> The function name, interrupt and function number, and the DPMI
version where the function is first defined. All subsequent DPMI
versions can be assumed to support the function as documented unless
explicitly noted otherwise. <p>
<li> A brief description of the function's purpose and usage. <p>
<li> The parameters supplied by the DPMI client when it makes the
function call. <p>
<li> The results returned for the function by the DPMI host. <p>
<li> Programmer's notes giving more detailed information about the
function and/or describing special uses of the function. <p>
</ul>
<hr>
<img src="/icons/menu.sm.gif" alt="*">
<a href="ch5.a.html">DPMI Functions by Name</a><br>
<img src="/icons/menu.sm.gif" alt="*">
<a href="ch5.n.html">DPMI Functions by Number</a><br>
<img src="/icons/menu.sm.gif" alt="*">
<a href="ch5.g.html">DPMI Functions by Functional Group</a><br>

View File

@@ -0,0 +1,100 @@
<img src="1.0.gif" alt="[1.0]"> <a href="api/2f1680.html"><tt>2f 1680</tt> - Release Current Virtual Machine's Time Slice</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/2f1686.html"><tt>2f 1686</tt> - Get CPU Mode</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/2f1687.html"><tt>2f 1687</tt> - Obtain Real-to-Protected Mode Switch Entry Point</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/2f168a.html"><tt>2f 168a</tt> - Get Vendor-Specific API Entry Point</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310000.html"><tt>31 0000</tt> - Allocate LDT Descriptors</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310001.html"><tt>31 0001</tt> - Free LDT Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310002.html"><tt>31 0002</tt> - Segment to Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310003.html"><tt>31 0003</tt> - Get Selector Increment Value</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310004.html"><tt>31 0004</tt> - Reserved</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310005.html"><tt>31 0005</tt> - Reserved</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310006.html"><tt>31 0006</tt> - Get Segment Base Address</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310007.html"><tt>31 0007</tt> - Set Segment Base Address</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310008.html"><tt>31 0008</tt> - Set Segment Limit</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310009.html"><tt>31 0009</tt> - Set Descriptor Access Rights</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000a.html"><tt>31 000a</tt> - Create Alias Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000b.html"><tt>31 000b</tt> - Get Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000c.html"><tt>31 000c</tt> - Set Descriptor</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/31000d.html"><tt>31 000d</tt> - Allocate Specific LDT Descriptor</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31000e.html"><tt>31 000e</tt> - Get Multiple Descriptors</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31000f.html"><tt>31 000f</tt> - Set Multiple Descriptors</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310100.html"><tt>31 0100</tt> - Allocate DOS Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310101.html"><tt>31 0101</tt> - Free DOS Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310102.html"><tt>31 0102</tt> - Resize DOS Memory Block</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310200.html"><tt>31 0200</tt> - Get Real Mode Interrupt Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310201.html"><tt>31 0201</tt> - Set Real Mode Interrupt Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310202.html"><tt>31 0202</tt> - Get Processor Exception Handler Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310203.html"><tt>31 0203</tt> - Set Processor Exception Handler Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310204.html"><tt>31 0204</tt> - Get Protected Mode Interrupt Vector</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310205.html"><tt>31 0205</tt> - Set Protected Mode Interrupt Vector</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310210.html"><tt>31 0210</tt> - Get Extended Processor Exception Handler Vector (Protected Mode)</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310211.html"><tt>31 0211</tt> - Get Extended Processor Exception Handler Vector (Real Mode)</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310212.html"><tt>31 0212</tt> - Set Extended Processor Exception Handler Vector (Protected Mode)</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310213.html"><tt>31 0213</tt> - Set Extended Processor Exception Handler Vector (Real Mode)</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310300.html"><tt>31 0300</tt> - Simulate Real Mode Interrupt</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310301.html"><tt>31 0301</tt> - Call Real Mode Procedure With Far Return Frame</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310302.html"><tt>31 0302</tt> - Call Real Mode Procedure With IRET Frame</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310303.html"><tt>31 0303</tt> - Allocate Real Mode Callback Address</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310304.html"><tt>31 0304</tt> - Free Real Mode Callback Address</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310305.html"><tt>31 0305</tt> - Get State Save/Restore Addresses</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310306.html"><tt>31 0306</tt> - Get Raw Mode Switch Addresses</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310400.html"><tt>31 0400</tt> - Get Version</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310401.html"><tt>31 0401</tt> - Get DPMI Capabilities</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310500.html"><tt>31 0500</tt> - Get Free Memory Information</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310501.html"><tt>31 0501</tt> - Allocate Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310502.html"><tt>31 0502</tt> - Free Memory Block</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310503.html"><tt>31 0503</tt> - Resize Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310504.html"><tt>31 0504</tt> - Allocate Linear Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310505.html"><tt>31 0505</tt> - Resize Linear Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310506.html"><tt>31 0506</tt> - Get Page Attributes</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310507.html"><tt>31 0507</tt> - Set Page Attributes</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310508.html"><tt>31 0508</tt> - Map Device in Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310509.html"><tt>31 0509</tt> - Map Conventional Memory in Memory Block</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31050a.html"><tt>31 050a</tt> - Get Memory Block Size and Base</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/31050b.html"><tt>31 050b</tt> - Get Memory Information</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310600.html"><tt>31 0600</tt> - Lock Linear Region</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310601.html"><tt>31 0601</tt> - Unlock Linear Region</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310602.html"><tt>31 0602</tt> - Mark Real Mode Region as Pageable</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310603.html"><tt>31 0603</tt> - Relock Real Mode Region</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310604.html"><tt>31 0604</tt> - Get Page Size</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310700.html"><tt>31 0700</tt> - Reserved</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310701.html"><tt>31 0701</tt> - Reserved</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310702.html"><tt>31 0702</tt> - Mark Page as Demand Paging Candidate</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310703.html"><tt>31 0703</tt> - Discard Page Contents</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310800.html"><tt>31 0800</tt> - Physical Address Mapping</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310801.html"><tt>31 0801</tt> - Free Physical Address Mapping</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310900.html"><tt>31 0900</tt> - Get and Disable Virtual Interrupt State</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310901.html"><tt>31 0901</tt> - Get and Enable Virtual Interrupt State</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310902.html"><tt>31 0902</tt> - Get Virtual Interrupt State</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310a00.html"><tt>31 0a00</tt> - Get Vendor-Specific API Entry Point</a><p>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b00.html"><tt>31 0b00</tt> - Set Debug Watchpoint</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b01.html"><tt>31 0b01</tt> - Clear Debug Watchpoint</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b02.html"><tt>31 0b02</tt> - Get State of Debug Watchpoint</a><br>
<img src="0.9.gif" alt="[0.9]"> <a href="api/310b03.html"><tt>31 0b03</tt> - Reset Debug Watchpoint</a><p>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310c00.html"><tt>31 0c00</tt> - Install Resident Service Provider Callback</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310c01.html"><tt>31 0c01</tt> - Terminate and Stay Resident</a><p>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d00.html"><tt>31 0d00</tt> - Allocate Shared Memory</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d01.html"><tt>31 0d01</tt> - Free Shared Memory</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d02.html"><tt>31 0d02</tt> - Serialize on Shared Memory</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310d03.html"><tt>31 0d03</tt> - Free Serialization on Shared Memory</a><p>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310e00.html"><tt>31 0e00</tt> - Get Coprocessor Status</a><br>
<img src="1.0.gif" alt="[1.0]"> <a href="api/310e01.html"><tt>31 0e01</tt> - Set Coprocessor Emulation</a><p>

View File

@@ -0,0 +1,79 @@
The following table shows the DPMI client's restrictions on usage of
previously allocated descriptors as input parameters to DPMI
functions. The columns represent the ways the DPMI host allocates
descriptors for its clients. (The first two columns represent LDT
descriptor management functions which allocate descriptors, the third
column represents DOS memory functions, and the last column represents
"other" descriptors, i.e., unallocated descriptors or descriptors used
by the DPMI host for internal purposes.) Each row represents a set of
functions where a client passes those previously allocated
descriptor(s) to the host as input parameters. A `N' indicates that
an "Invalid Selector" error will be generated if the given descriptor
is used in the specified function.<p>
Note that a `Y' for a given entry does not indicate that the function
will succeed, only that it will not generate an "Invalid Selector"
error. Similarly, an `N' does not necessarily indicate a descriptor
is invalid for referencing memory, only that it cannot be used with
that particular function. This chart does not address the usage of
descriptors in pointers. <p>
For example, descriptors allocated by the Allocate LDT Descriptor function
may be used in any of the interrogation and modification functions of
LDT Descriptor Management, as well as the functions which set
exception handlers and interrupt vectors. They may not be passed to
Allocate Specific LDT Descriptor or the DOS Memory Block Functions.<p>
<table border=1 cellspacing=0 cellpadding=4>
<tr> <td>&nbsp;</td> <th colspan=4>Descriptor Allocators</th></tr>
<tr> <th>Functions referring to allocated descriptors</th>
<td>Allocate LDT Descriptor, Allocate Specfic LDT Descriptor, Create Alias descriptr, Initial CS, DS, SS</td>
<td>Segment to Descriptor, PSP, Environment Pointer, Callback DS, Locked Stack SS</td>
<td>Allocate/Resize DOS Memory Block</td>
<td> GDT-based Descriptor, System Descriptor, Unallocated Descriptor</td></tr>
<tr><td>Interrogation Functions: Get Segment Base Address, Get Descriptor, Get Multiple Descriptor, Create Segment Alias</td>
<td align=center>Y</td> <td align=center>Y</td> <td align=center>Y</td> <td align=center>N</td></tr>
<tr><td> Modification Functions: Set Segment Base Address, Set Segment Limit, Set Descriptor Access Rights, Set Descriptor, Set Multiple Descriptors, Free LDT Descriptor</td>
<td align=center>Y</td> <td align=center>N</td> <td align=center>N</td> <td align=center>N</td></tr>
<tr><td> Allocate Specific LDT Descriptor</td>
<td align=center>N</td> <td align=center>N</td> <td align=center>N</td> <td align=center>Y(1)</td></tr>
<tr><td>Free/Resize DOS Memory Block</td>
<td align=center>N</td> <td align=center>N</td> <td align=center>Y</td> <td align=center>N</td></tr>
<tr><td>Set Exception Handler/Interrupt Vector</td>
<td align=center>Y</td> <td align=center>Y(3)</td> <td align=center>Y</td> <td align=center>Y(2)</td></tr>
</table><p>
Notes:
<ol>
<li> Unallocated descriptors within the range of the LDT only.
<li> GDT-Based segment descriptors only.
<li> Although this call will succeed, a fault will result if the
exception or interrupt occurs, since the segment can never be made
executable.
</ul>

View File

@@ -0,0 +1,47 @@
<i>This reference requires a WWW browser that supports tables for best
results. Painstakingly typed in without permission. The DPMI
Specification is Copyright <20> The DPMI Committee, 1989-1991.
Unsupported <a href="dpmi-specs.tar.gz">download (100K)</a> if you
want.<p>
<h1>Notice:</h1>
This HTML translation originally from <a href="http://www.delorie.com/djgpp/doc/dpmi/">DJ Delorie's Site</a>.
Not responsible for errors or omissions due to accident,
negligence, or malice. This was hard to do, so <a
href="http://www.delorie.com/donations.html">please send money</a> [to them].</i><p>
<h2><img src="/icons/menu.gif"> <a href="ch4.html">DPMI Client Implementation Notes</a></h2>
<ul>
<a href="ch4.1.html">Client Initialization</a><br>
<a href="ch4.2.html">Client Termination</a><br>
<a href="ch4.3.html">Stacks and Mode Switching</a><br>
<a href="ch4.4.html">Handling Interrupts</a><br>
<ul>
<a href="ch4.4.1.html">Virtual Interrupts</a><br>
<a href="ch4.4.2.html">Hardware Interrupts</a><br>
<a href="ch4.4.3.html">Software Interrupts</a><br>
</ul>
<a href="ch4.5.html">Handling CPU Exceptions</a><br>
<a href="ch4.6.html">Using Real-Mode Callbacks</a><br>
<a href="ch4.7.html">Using Shared Memory</a><br>
<a href="ch4.8.html">Writing Resident Service Providers</a><br>
</ul><p>
<h2><img src="/icons/menu.gif"> <a href="ch5.html">DPMI Function Reference</a></h2>
<ul>
<a href="ch5.a.html">DPMI Functions by Name</a><br>
<a href="ch5.n.html">DPMI Functions by Number</a><br>
<a href="ch5.g.html">DPMI Functions by Functional Group</a><br>
</ul>
<h2><img src="/icons/menu.gif" alt="*"> <a href="api/errors.html">DPMI Error Codes</a></h2>
<h2><img src="/icons/menu.gif" alt="*"> <a href="descriptor-rules.html">Descriptor Usage Rules</a></h2>

Binary file not shown.

View File

@@ -0,0 +1,100 @@
FLAT REAL / REAL BIG / UNREAL MODE (v1.2)
Flat Real mode, Real Big mode and UnReal mode are three names with the very same meaning, I will call it FLAT in this text.
Since the first PC-XT, people have searched for methods to get access to more and more memory for their DOS programs: EMS, UMBs, XMS, DPMI. With the i386, Intel has given 32-bit power to the PC's. Here's an example to take advantage of the 32-bit capabilities of the i386 and compatibles without the need of protected mode, DOS-extenders and/or special interfaces like VCPI & DPMI.
FLAT simply allows you to use 32-bit access on top of the normal 16-bit addressing. 32-bit access is possible as the well-known 64KB limits are just registers on the i386 and can be altered to almost any value up to 4GB. FLAT is not new, Microsoft's HIMEM.SYS already uses it since 1988 for its 'Move Extended Memory Block' service.
To understand how FLAT works, you have to understand how the i386 works. The i386 is not made as one but several seperate units; an ALU, an INSTRUCTION PREFETCHER, a SEGMENTATION UNIT, a PAGING UNIT, etc. Most of these units have no clue about the state of the CPU; Real or Protected Mode.
The SEGMENTATION UNIT only holds the BASE, the LIMIT and some attributes of the segment registers. These values are used for addressing rather than the value of the segment registers. When a segment register is assigned a value, in Real Mode and in a V86 task the BASE is loaded with 16 times this value so the addressing is compatible with the 8086. Most of the other fields, including LIMIT, are unaffected. In normal Protected Mode operation this value is used as an index for the GLOBAL/LOCAL DESCRIPTOR TABLE from where all fields BASE, LIMIT and attributes are fetched. So, to alter the LIMITs, we have to be in Protected Mode.
The SEGMENTATION UNIT will report a fault when an instruction is trying to address beyond a LIMIT, or when an instruction is trying to do an illegal access like writing to a read-only segment. This fault will raise interrupt number 13. In Protected Mode there's an exception handler that will handle this. In normal DOS operation (Real Mode), there is _no_ exception handler. In fact, in the PC design interrupt #13 is used for handling IRQ number 5. This is the reason DOS will hang the system when 32-bit access is used. While the IRQ #5 handler expects the address of the _next_ instruction is pushed onto the stack, exception #13 will push the instruction which caused the exception. When the IRQ #5 handler does an IRET, the CPU tries to execute the very same instruction, resulting in an exception #13 (again), etc, etc..
After a RESET or a switch to a V86 task, all segment LIMITs have a value of 64KB, this to be compatible with the 8086 and the 80286. As the LIMITs can't be altered from within a V86 task, FLAT will never work here. FLAT is thus incompatible with environments/programs where a V86 task is used to simulate DOS, including:
- MS-Windows 3.x in 'Enhanced Mode'
- MS-Windows NT
- OS/2 2.x, Warp
- Emm386, Qemm, etc.. simulating UMBs
Some environments/programs have an option to disable the DOS simulation:
- MS-Windows'95; check the 'MS-DOS Mode' option
- Emm386 simulating EMS; enter 'Emm386 OFF' at the DOS prompt
Due to a bug in Qemm (7.02), its simulation cannot be disabled after being enabled. As long Qemm stays disabled FLAT will work. I do not have experience with other EMS simulators (e.g 386max)
FLAT is fully compatible with:
- DOS 2.0 and above
- MS-Windows 3.x in 'Real Mode' and 'Standard Mode'
- DesqView
- Himem/XMS/UMB drivers
- EMS drivers
All FLAT actually does it jump to Protected Mode, alter the segment LIMITs using a DESCRIPTOR with a 4GB LIMIT, and jump back to Real Mode. As other programs/TSRs may enter Protected Mode, there's the possibility that the LIMITs are altered to 64KB again. This is why I have implemented FLAT as an exception handler. To allow IRQ #5 to be handled, the exception handler first checks the Interrupt Controller if IRQ #5 is 'In Service'. If so, it calls the IRQ #5 handler. FLAT will terminate the program if it detects an instruction that is causing an exception #13, even when the LIMITs are (re)set to 4GB.
To activate FLAT, just call the FLAT_install routine, to remove/deactive it, call FLAT_destall. As FLAT must be installed on top of the interrupt #13 handler, FLAT has to be deactivated first before any changes to this interrupt vector can take place. As said above, interrupt #13 is normally used for IRQ #5.
FLAT is called FLAT because with 32-bit access the whole 4GB address space of the 386 can be accessed with only using offsets. But as the addressing mechanism needs a segment the format [0000:<32-bit offset>] is used.
Using XMS, when an EMB is locked, the physical base address of the EMB is returned. This base address can be used as base for access to the EMB:
mov ah,09h ; Allocate EMB
mov dx,256 ; 256KB
call XMS_driver ; Do it!
test ax,ax ; Error?
jz alloc_error
mov ah,0Ch ; Lock EMB
call XMS_driver ; Do it!
test ax,ax ; Error?
jz lock_error
mov di,dx ; DX has high word of 256KB chunk
shl edi,16
mov di,bx ; BX has low word of 256KB chunk
xor eax,eax ; Clear EAX
mov es,ax ; ES:EDI now points to first address of 256KB chunk
mov ecx,10000h ; 256KB equals 64K dwords
rep stos dword ptr es:[edi] ; Clear 256KB chunk
32-bit access is not specially meant for extended memory, it can be used for conventional memory as well. DOS allows memory allocations larger than 64KB which now is addressable as one big chunk rather than separate chunks of 64KB and/or less:
mov ah,48h ; Allocate Memory
mov bx,4000h ; 256KB (16K paragraphs)
int 21h ; Do it!
jc alloc_error ; Out of memory?
mov es,ax ; 256KB can be accessed using es:00000000 through es:0003FFFFh
xor eax,eax ; Clear EAX
xor edi,edi ; ES:EDI now points to first address of 256KB chunk
mov ecx,10000h ; 256KB equals 64K dwords
rep stos dword ptr es:[edi] ; Clear 256KB chunk
With the new VESA VBE Core Standard 2.0, the entire video memory of a SVGA or other video adapter that support linear/flat addressing can be accessed as one large chunk of memory somewhere in the 4GB(/16MB) address space of the 386(sx):
mov ax,4F01h ; Get VBE Mode Information
mov cx,100h ; Mode : 640x400, 256 colours
les di,ModeInfoBlockPtr ; ES:DI now points to ModeInfoBlock structure
int 10h ; Do it!
cmp ax,4Fh ; Error?
jne VBE_error
mov ax,4F02h ; Set VBE Mode
mov bx,0C100h ; Mode : 640x400, 256 colours, linear/flat, don't clear display
int 10h ; Do it!
cmp ax,4Fh ; Error?
jne VBE_error
xor eax,eax ; Clear EAX
mov edi,dword ptr es:di[28h] ; ModeInfoBlock[28h] = 'PhysBasePtr'
mov es,ax ; ES:EDI now points to first address of 256KB video buffer
mov ecx,10000h ; 640 x 400 bytes equals 256000 bytes equals about 64K dwords
rep stos dword ptr es:[edi] ; Clear 256KB video buffer
Accessing video memory without the need of bankswitching really speeds up video performance. In terms of pixels per second, drawing lines in the 1600x1200,256 colours graphics mode is now faster than in the 320x200, 256 colours mode.
The only limitation of FLAT is your imagination. And you'll need that as no high-level language DOS compiler I've seen (yet) supports 32-bit addressing without the need of some kind of DOS-extender. However, FLAT will link smoothly with almost any 16-bit DOS compiler. I use Turbo Pascal 5.5 and 6.0 myself for the body of the programs I'm writing, and use some assembly at the places I need the 32-bit addressing. See EX2 for a very nice example.
Herman Dullink
Groningen (the emulator city; CPC, MSX, ZX :-)
the Netherlands
+31-50-132829
csg669@wing.rug.nl (fast)
herman.dullink@prgbbs.idn.nl (1 day slower)

View File

@@ -0,0 +1,414 @@
HOW TO KICK OUT A MEMORY MANAGER
Warning : this article is meant for coders that are already experienced
enough with system programming. Sorry, but the concepts we'll have to use
are complicated enough to prevent me from explaining everything from the
very start. I think that this article will be long enough like that.
If you read this coding corner since its first issue (it was in Imphobia #7)
you should already know that one of my occupations was trying to find a way
to kick out the memory managers that likes to disable the use of the flat
real mode, or more generally to disable the acces to code privilege ring #0
(CPL0). Well, here is it.
The LOADALL option was not the good one. It is not supported on all types of
CPU. For example my intel 486DX2 does not support it. Moreover this instruct
seems to be a priviledged one, so it was impossible to use it to access to
CPL0.
However there must be a way to access to CPL0 with a memory manager loaded.
Do you think that Micro$oft Windows runs in enhanced 386 mode with a CPL3 ?
I don't. So there was only one possible solution : they used some kind of
undocumented interface to gain total control to the CPU.
This interface is known as the "Windows Global EMM Import Specification" and
we will just write GEMMIS in the rest of this text.
Here I need to thank my friend Cedric BERMOND aka LCA / Infiny for his great
help. He pointed out to me the Micro$oft Windows argument, and he told me
about the following references. He implemented his own system at the same
time as me, and we had a lot of chats about the problems we encountered and
the solutions we found. Without him the development of this system would
have been impossible. The code example included with this article is written
by me, but it's the result of a collaboration between us.
LCA will soon (before the Assembly'95) release his own protected mode memory
manager, featuring :
* user/supervisor protection system, with the user running at CPL0
* own linker, for a true flat segmentation model (for example the screen
really is at A0000 as opposed to pmode)
* paging disabled by default
* full debugger and disassembler system
Some references about the GEMMIS :
* Ralf Brown's interrupt list. Provides some quite detailed information
about the undocumented functions and structures that we will use, but with
no explanations at all. When I read this at the first time I believed that
this functions were a part of the Windows kernel and I skipped the rest. I
was wrong, this is some undocumented functions made FOR windows to allow
him to access to CPL0
* Dr Dobb's Journal, September 1994, Undocumented Corner,
"The Windows Global EMM Import Interface". This article provides some ex-
planations and gives the general layout we will have to use, but with a
lack of detail.
This problem becomes even more important today that the organizers of The
Assembly 95 said in a pre-invitation textfile that at this party the demos
will have to run with EMM386.EXE loaded. This sucks badly, however in this
article you have the solution. The only annoying thing is that I guess that
the organizer's reason is not a technical one, it is just because they want
to sell their damn CD's.... :(
Okay, now let's start with the serious things.
PART ONE - THE LAYOUT
We have two basic needs :
* switching to real, unprotected mode
* allocate some high memory
To satisfy this needs we will have to use the following layout :
* call int 2f undocumented function 1605h
"Micro$oft Windows - WINDOWS ENHANCED MODE & 286 DOSX INIT BROADCAST"
This call will fool the memory manager and he will believe that we are
Windows trying to initialize itself. The memory manager will suddenly become
very friendly with us. He will also provide us a pointer to a function that
we can call to switch to real, unprotected mode.
* before the switch we will allocate all available XMS and EMS memory
* In order to be able to use the allocated memory blocks we must know their
physical addresses. With the XMS it's easy, we just have to 'lock' them.
With the EMS it's harder because we have no documented way to know about
which physical pages are allocated to our EMS handle. That's no problem, the
switch to real mode function will return this information in a table. We
just need to know the physical address where this table will be, and we need
to use another undocumented function for that : we must do an IOCTL read on
the "EMMXXXX0" device, and the read will return the desired address along
with the version of the GEMMIS protocol. This function is referenced in RB's
interrupt list as "Memory Managers - GET EMM IMPORT STRUCTURE ADDRESS"
* then we use the switch to real mode function that was provided to us at
the first step. It fills us a table with all the information we could need
about the memory mapping and the pages allocated to our EMS handle, then it
gives us acces to the real mode.
* we need to analyse this table. It is located in high memory , so we will
have to switch to flat real mode (or in protected mode if you prefer it) in
order to access this table. It won't be a big problem : now we are in real
mode and the memory manager is not there to annoy us. But we must take care
of not enabling the interrupts : the UMB's and EMS page frames does not
exist anymore, and an interrupt would most likely hang the system.
* after that we have several ways to cope with the interrupts : the simplest
one is to be sure to NEVER call the any system interrupt during your demo,
the medium one is to switch back to V86 mode before any interrupt (it's easy
to do, we just have to use the same function that allowed us to acces to
real mode), and the hardest one (the one used by Micro$oft Windows) is to
code your own memory manager that will be able to run the system with the
same mapping that the DOS-memory manager used.
* When we have finished playing with the computer we must give it back to
the system. We just have to switch it back to V86 mode.
* Then we must call int 2f undocumented function 1606h
"Micro$oft Windows - WINDOWS ENHANCED MODE & 286 DOSX EXIT BROADCAST"
This will return the memory manager to his normal, unfriendly state.
PART TWO - THE DETAILS
* first step : call int 2f undocumented function 1605h "Micro$oft Windows -
WINDOWS ENHANCED MODE & 286 DOSX INIT BROADCAST" at the entry of this call
we must set ax=1605h (function number), and bx=cx=dx=si=ds=es=0. di will be
set of the Windows version number we want to simulate. For example we can
use 30ah for Windows version 3.10 then we call int 2fh. If the memory mana-
ger accepts to run windows he will return cx=0, in any other case we will
have to exit. The memory manager will return in ds:si a far pointer to a
modeswitch routine, used later to switch between V86 and real mode. It can
also return 0:0, and in this case we won't be able to enter real mode. The
memory manager will create a EMMXXXX0 device if there is not already one.
* then we have to allocate the memory. The simplest way is to allocate XMS
memory, because we can then use the documented XMS function 0ch to lock the
memory block and get his address. We want to allocate ALL the XMS memory and
perhaps it will already be fragmentated, so we should be able to handle
several XMS blocks. However some memory managers won't allow us to allocate
all the memory using XMS allocations, or sometimes they won't even support
the XMS standard. So we must also allocate some EMS memory. If we want to be
100% sure of our memory allocations we can use standard XMS & EMS functions
to copy a known string in all our allocated memory blocks. Later we will be
able to verify that this string is present at the expected physical addres-
ses. This provides us a way to verify the physical adresses we will use. In
order to later identify our EMS handle it is best to assign it a name.
* we must get the GEMMIS address with an IOCTL read on the "EMMXXXX0" device
"Memory Managers - GET EMM IMPORT STRUCTURE ADDRESS" we will first verify
that there is an EMM present, using the standard method defined in lotus/
intel/microsoft Expanded Memory Specification (EMS) version 4.0 : we must
open a handle for the EMMXXXX0 device, using the same function as for a file
open, i.e. a call to int 21h with ax=3d00h and ds:dx pointing to the device
name. If the handle exists (that should be the case) we issue a 'get device
information' IOCTL, i.e. a call to int 21h with ax=4400h and the handle to
the EMMXXXX0 device in bx. This call will return us some information about
our handle in dx, and we must verify that bits 14 and 7 are set, indicating
that this handle is associated to a device driver and not a file, and that
this device driver accepts IOCTL reads. We will then issue a 'get output
status' IOCTL on this device, i.e. a call to int 21h with ax=4407h and the
device handle in bx. we expect to have ax=255 upon exit, indicating that the
device is ready. At this step we know that there is an EMM in memory, but we
don't know which version of the EMM specification does it support. We close
our device handle (using standard file close function ah=3eh bx=device
handle) and we ask for EMM version, using int 67h function ah=46h. The
version returned in al should be at least 40h i.e. 4.0 now this memory mana-
ger should be recent enough to support the GEMMIS specification, so we will
attempt an GEMMIS IOCTL read on it. We must open again the EMMXXXX0 device
(same process as above) and issue an IOCTL read on it i.e. a call to int 21h
with ax=4402h bx=the device handle ds:dx pointing on a 6 bytes buffer and
cx=6=number of bytes to read. The first byte of the buffer must be pre-
filled with a 01h value. (it's used by the GEMMIS protocol as a sub-function
value) upon exit we should have AX=6=nb bytes read, and the buffer will be
filled with a dword value indicating the physical memory address of the
GEMMIS data table, and a word value indicating the version of the GEMMIS
specification. (format for the version word : one byte for the major number,
and one byte for the minor number). We expect the version to be at least 1.0
(the current version is 1.11 but is not supported by every manager. Version
1.0 will be enough to know the pages allocated to our EMS block) then we can
close again the device driver.
* we want to switch to real mode. We will use the modeswitch function
provided at the first step of the GEMMIS procedure. if we call it with ax=0
it will switch us to real mode. The carry flag should be cleared upon exit
to indicate a success. This function can (and will) destroy every register
except CS,IP,SS and SP. the DS,ES,FS,GS segments registers will also be des-
troyed. The switch to real mode will of course destroy the UMB's and EMS
frames, so we take care not to enable the interrupts after this step. We
must clear the interrupt flag before the call, and we can expect it to be
still cleared after the call. This function will not only switch us to real
mode, it will also fill up the GEMMIS data block with the V86 memory mapping
using a data format that I won't detail here, but you can look at the
GEMMIS.DOC file that I spread you with my code example, it's an extract from
RB's interrupt list and it's very detailed.
* Now we run in real mode (at CPL0) so we can do what we want with the CPU.
It's time to initialize flat real mode, or protected mode at CPL0 or whate-
ver else we wanted to have acces to. We must just have access to all the
physical memory, because the GEMMIS data table will be located in high memo-
ry and we won't be able to acces it from [non-flat] real mode. Nothing spe-
cial to explain here, I'll assume that you already know how to enter into
flat real mode.
* Now that we have access to all the physical memory it's time to analyse
the the GEMMIS data table. Just look at its format in the GEMMIS.DOC file,
and at my tips in the next section. This analysis will allow us to get the
physical addresses of the pages of our EMS handle. The interesting part of
the GEMMIS data block will be the EMS handle info records, however we will
need to use the rest of the GEMMIS data block to find them. We musn't rely
on the EMS handle number to identify our EMS handle, it is necessary to use
the EMS handle name we assigned. (because 386max won't give us the right
handle numbers).
* We have done with the GEMMIS protocol. The only problem now is that we
can't enable interrupts, because the interrupt handlers could try to access
to an UMB or to map some EMS pages and they don't exist anymore. So we can
either keep the interrupts disabled, or create some new interrupt handlers
that will switch back the CPU to V86 mode (using the modeswitch function),
call the old interrupt handler, and switch the CPU to flat real mode again.
(or to protected mode if you prefer, do what you like)
* At the end of our program (or when an interrupt occurs) we will want to
switch back the CPU to V86 mode, in order to restore the UMB and EMS native
structure of the DOS system. We just have to call the modeswitch function
with AX=1. Again, this call will destroy every register except CS,IP,SS,SP.
and we must call it with the interrupts disabled.
* At the end of our code we will have to fake a Windows exit. We just have
to call int 2fh with ax=1606h and dx=0
Phew, we are done with this shitty GEMMIS protocol ! Yes, but there is more
to come.....
PART THREE - THE PROBLEMS
Here I have to explain that this interface was keept very secret. There was
no official documentation about it and some of the memory manager makers did
not had the specification in the hands when they had to support it. For
example it is known that Novell had to reverse engineer some code to support
the last version of the specification. This explains why the GEMMIS protocol
is so badly implemented in several memory managers. Some data records are
sometimes not filled for example.... :(
By the way I think it shows us a very bad attitude from Micro$oft. They
sometimes are accusated to monopolyse the market but I just have to say that
this is true, because I cannot admit that someone keeps secret a specifica-
tion that is necessary for a memory manager to support Windows, and for a
Windows-like program to support the memory managers. It's a shame.
So here is a few tips that you must use if you want your GEMMIS implementa-
tion to support every memory manager around (you should read this part of
the article with a printed copy of GEMMIS.DOC near you. This is only imple-
mentation details, so you won't understand anything if you don't have the
format of the GEMMIS data table in the hands)
* at offset 4 in the GEMMIS data you should find a word containing the
GEMMIS version number. Don't use it. Several memory managers, for example
QEMM386 v6.0, won't fill it. Instead you should use the version number
provided by the GEMMIS IOCTL read, at offset 4 in the 6-bytes import buffer.
* don't rely on the informations provided by GEMMIS version 1.10 : several
memory managers still only implement version 1.0 (for example 386max)
* in the EMS frame status records, don't use the "flags for non-EMS frames"
byte (located at offset 5) : several memory managers, including 386max and
QEMM386, won't fill it. Use the "EMS frame type" at offset 0 instead.
* use the "number of UMB frame descriptors following" byte at offset 18Bh in
the GEMMIS data. Sometimes, for example if there is 386max loaded, the UMB
frame descriptor table will not be entirely filled but this byte will always
have the right value.
* the "number of EMS handle info records following" appears to be reliable.
* in each EMS handle info record, ignore the "handle number" byte. Better
use the "EMS handle's name" to identify your EMS handle. This is necessary
with 386max. The "physical address of page table entries forming page map"
entry points to a table giving us the physical memory adresses used for each
4K-page of the EMS handle. This table contains 4*"number of 16K pages for
handle" double words, and each or this double words gives us the physical
address of one 4K-page used for the EMS handle. The lower 12 bits of this
double words should be ignored because they will be filled with some
garbage.
* once again : don't use the information provided by GEMMIS version 1.10,
even Micro$oft's EMM386 v4.49 does not seems to fill it... You can just know
the structure in order to skip them, thus giving you access to the GEMMIS
version 1.11 records (the memory manager name)
* when you allocate your XMS memory, you will probably want to use the XMS
function 08h "Query Free Extended Memory". At this step don't rely on
getting an errorlevel in BL. Every XMS function returns some errorlevels in
BL, and zero means OK, but obviously Micro$oft just forgot the line saying
"BL = 00h if the function succeeds" in their original "eXtended Memory
Specification (XMS), ver 3.0" publication. The programmers at QuarterDeck
probably are not intelligent enough to understand this evidence, so when
everything is OK QEMM386 does not modify the BL register. So you should set
BL to zero before the call, and also test for AX=0 (no available XMS memory)
instead of just relying on the errorlevel in BL.
* QEMM is harder to support than the others. With QEMM you will have to
restart the whole protocol if you want to temporarily switch back to V86
mode. Here is the thing you will have to code if you want to switch back to
V86 mode for a little time (for launching a system interrupt for example) :
disable flat real mode, switch to native V86 mode using the modeswitch func-
tion, fake a windows exit, call your system interrupt, fake another windows
init, switch back to real mode with the modeswitch function, enable flat
real mode again. You may think it's stupid to fake a windows exit just for
entering windows again later, yes it's stupid but remember : we don't try to
be smart, we just try to emulate Windows code. ;-)
PART FOUR - THE IMPLEMENTATION
As an example I'll show you a simple flat real mode initializer. This imple-
mentation supports RAW, XMS and EMS memory allocations. It's a "do-nothing"
implementation, i.e. it just initializes flat real mode, then restores the
previous mode and exits to dos. Well this should be enough for demonstration
purposes.
It's written in turbo assembler 2.0, and I compile it with "tasm gemmis" and
"tlink /m/3 gemmis". I guess it could also be compiled with further versions
of turbo assembler, but I didn't tested this.
It should be very easy to use this code : just include what you want between
the init and end portions of the code, it could be a flat real mode program
or a protected mode initialization with CPL0, and it will work. You can use
the 'available_memory_table', it will give you the adresses of every alloca-
ted memory block. The number of such blocks will be stored in 'nb_available_
memory'
The init of this code may seem slow, the main reason for this is that I fil-
led every allocated memory block with a known string in order to verify that
the physical adresses are the right ones after the flat real mode switch.
There is two standards provided for interrupt support: The easyest one is to
just call the interrupt, there will be a handler installed which will trap
the interrupt and switch back to native V86 mode to execute it. Then the
system will return to flat real mode and you will not have to worry about
the modeswitch. However it will be a bit slow. (by the way you should trap
the timer interrupt yourself if you want to use this code in a demonstration
in order to avoid all this unnecessary mode switches)
The other method for the interrupt support is to ask the memory manager to
disable the flat real mode in a code section delimited by two function
calls. In this section you won't be in flat real mode, but you will be able
to call every interrupt without any unnecessary delay. I call that a native
mode code section.
Look at my code example : you just have to call mem32_init to setup every-
thing. At exit you will be in flat real mode and all the free high memory
will be allocated to you. You will be able to use the data in
available_memory_table to get some memory : it contains the starting and
ending physical adresses of every allocated memory blocks. The number of
such blocks will be stored in nb_available_memory.
When you want to define a native mode code section you'll just have to call
the mem32_native and mem32_flatreal functions.
When you want to exit your proggy just jump to the exit label, with DS:DX
pointing to a message you want to display.
As an example proggy I just included a proggy that dumps the GEMMIS v1.0
data table. It's damn slow because I use DOS interrupts to display the
information and this causes a lot of mode switches. In a real code you
probably would like to build your own string displayer and it would be
pretty much faster.
This implementation has been successfully tested with the following memory
managers : HIMEM.SYS alone, EMM386.EXE v4.49, QEMM386.SYS v6.00 beta,
386MAX.SYS v6.02
PART FIVE - GOING FURTHER
In my code example I used flat real mode, but it could be easy to change the
library to use protected mode if you prefer it. By the way a lot of protec-
ted mode coders insists on the fact that protected mode is faster because it
eliminates the need for prefixes before 32-bit instructs, but do they know
that it's also possible to use some 32-bit flat real mode without this ugly
prefixes ??? most of them does not seems to.
The obvious problem with this little example is that you have a delay each
time you want to call an interrupt. Because of that you will want to make
most of your init work in each part in a native mode code section, without
the benefits of privilege level 0, and then start your part without any call
to a V86 interrupt.
The alternate method, a much better one but far beyond the scope of this
introduction, would be to have your own V86 mode handler which would be able
to run the native system interrupts. This way you could bypass all the open
windows/close windows shit. If someone goes this way, I would be very happy
to get a little words from him. This is the way microsoft windows handles
the whole thing.
PART SIX - GOODBYE
Demomakers : the only thing you have to remember is that if this article and
the associated code example is useful for you I would be happy to get a lit-
tle greet. It would be nice also to say hello to my friend LCA/Infiny. If
you encounter some incompatibility problem with some hardware or with some
memory manager please let us know. Now I'm more or less obliged to include
the following disclaimer :
You can use this code example as you wish if it's for your private use. I
would prefer to receive a letter just to know that someone uses it, but it's
not necessary. Do what you want.
Here's an adress if you want to join me :
Walken / IMPACT Studios [coder]
Michel LESPINASSE
18 rue Jean Giono
80090 Amiens
France

View File

@@ -0,0 +1,86 @@
--------W-2F1605-----------------------------
INT 2F - MS Windows - WINDOWS ENHANCED MODE & 286 DOSX INIT BROADCAST
AX = 1605h
ES:BX = 0000h:0000h
DS:SI = 0000h:0000h
CX = 0000h
DX = flags
bit 0 = 0 if Windows enhanced-mode initialization
bit 0 = 1 if Microsoft 286 DOS extender initialization
bits 1-15 reserved (undefined)
DI = version number (major in upper byte, minor in lower)
Return: CX = 0000h if okay for Windows to load
CX = FFFFh (other registers unchanged) if Windows 3.0 in standard mode
CX <> 0 if Windows should not load
ES:BX -> startup info structure (see #1410)
DS:SI -> virtual86 mode enable/disable callback or 0000h:0000h
(see #1413)
Notes: the Windows enhanced mode loader and Microsoft 286 DOS extender will
broadcast an INT 2F/AX=1605h call when initializing. Any DOS device
driver or TSR can watch for this broadcast and return the appropriate
values. If the driver or TSR returns CX <> 0, it is also its
responsibility to display an error message.
each handler must first chain to the prior INT 2F handler with
registers unchanged before processing the call
if the handler requires local data on a per-VM basis, it must store the
returned ES:BX in the "next" field of a startup info structure and
return a pointer to that structure in ES:BX
a single TSR may set the V86 mode enable/disable callback; if DS:SI is
already nonzero, the TSR must fail the initialization by setting CX
nonzero
MSD checks for Windows 3.0 running in standard mode by testing whether
CX=FFFFh and other registers are unchanged on return
Novell DOS v7.0 (Update 8 - Update 11) TASKMGR in multitasking mode
uses this broadcast, even if TASKMGR.INI sets WinPresent= to OFF
Micrsoft's EMM386.EXE for DOS 5+ when installed with the NOEMS option
changes its driver name from EMMQXXX0 to EMMXXXX0 while Windows is
active
SeeAlso: AX=1606h,AX=1608h,AX=4B05h
Format of Windows Startup Information Structure:
Offset Size Description (Table 1410)
00h 2 BYTEs major, minor version of info structure
02h DWORD pointer to next startup info structure or 0000h:0000h
06h DWORD pointer to ASCIZ name of virtual device file or 0000h:0000h
0Ah DWORD virtual device reference data (see #1412)
(only used if above nonzero)
0Eh DWORD pointer to instance data records (see #1411) or 0000h:0000h
Format of one Instance Item in array:
Offset Size Description (Table 1411)
00h DWORD address of instance data (end of array if 0000h:0000h)
04h WORD size of instance data
Format of Virtual Device Reference Data:
Offset Size Description (Table 1412)
00h DWORD physical address of ??? or 00000000h
04h DWORD physical address of ??? table
08h DWORD "DEST_PAGE" address to which pages must be mapped
0Ch N DWORDs "SRC_PAGE" physical addresses of the pages
00000000h = end of table
Note: EMM386.EXE sets the first pointer to the start of the device driver
chain, the second pointer to a field of 40h bytes followed by a
16-bit offset to the end of the SRC_PAGE table, and DEST_PAGE to
the start segment of the UMB area
(Table 1413)
Values Windows virtual mode enable/disable procedure is called with:
AX = 0000h disable V86 mode
AX = 0001h enable V86 mode
interrupts disabled
Return: CF set on error
CF clear if successful
interrupts disabled
--------W-2F1606-----------------------------
INT 2F - MS Windows - WINDOWS ENHANCED MODE & 286 DOSX EXIT BROADCAST
AX = 1606h
DX = flags
bit 0 = 0 if Windows enhanced-mode exit
bit 0 = 1 if Microsoft 286 DOS extender exit
bits 1-15 reserved (undefined)
Notes: if the init broadcast fails (AX=1605h returned CX <> 0), then this
broadcast will be issued immediately.
this call will be issued in real mode
Novell DOS v7.0 (Update 8 - Update 10) TASKMGR in multitasking mode
uses this broadcast, even if TASKMGR.INI sets WinPresent= to OFF
SeeAlso: AX=1605h,AX=1609h

View File

@@ -0,0 +1,156 @@
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ
<EFBFBD>Ĵ This text comes from IMPHOBIA Issue IX - February 1995 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
THE LOADALL INSTRUCTION
In Imphobia Issue #8, Walken/Impact
Studios wrote a nice article about
memory management. However, for some
reason he seems to like flat-real-mode
(FRM) which is in my belief a totally
wrong way of programming since FRM
requires a 'clean' boot. Anyway, the
discussion of the advantages and
disadvantages of using FRM as oposed
to true flat-model protected mode are
not at issue here. Besides, there's
too many coders who just blindly think
FRM or PMode is better whithout ever
having performed some serious tests.
For those who think PMode is better, I
strongly suggest checking the 386/486
references on segment management and
memory access. For those who think FRM
is better... just speed-test your
program on a Pentium <g>.
Some info about the LOADALL
instruction first. There's a 286
LOADALL instruction and a 386/486/Pen-
tium LOADALL instruction, they have
different opcodes, and work slightly
different. Since no-one wants to code
a 286 protected mode program nowadays,
I won't cover the 286 variant of the
LOADALL instruction. Strangely however
some of the BIOSes will 'emulate' the
286 variant, by hooking it into the
"bad opcode" interrupt. The LOADALL
instruction is not documented by intel
in their CPU reference guides. LOADALL
is used in the VDISK program by
Microsoft, OS/2 and Windows 95, and
supposedly also in Windows NT.
The 286 variant has opcode 0Fh, 05h.
The 386+ variant has opcode 0Fh, 07h.
As said before, any further reference
to LOADALL assumes the 386 variant
unless otherwise mentionned.
LOADALL loads a 204-byte table pointed
to by ES:EDI into the registers. Note
however, that ES:EDI must point to a
valid memory location layed out by the
current memory model, meaning. When
in real mode, ES points to a segment,
and EDI points to an offset within
that segment (thus, only the lower 16
bit offset is used). When in PMode,
ES is a selector, and EDI an offset
within that selector page. When in
FRM, ES is a segment, and EDI a 32-bit
offset within that segment.
The table layout is as follows.
Offset Size CPU register loaded
------ ---- ------------------------
0h 4 CR0
4h 4 EFLAGS
8h 4 EIP
Ch 4 EDI
10h 4 ESI
14h 4 EBP
18h 4 ESP
1Ch 4 EBX
20h 4 EDX
24h 4 ECX
28h 4 EAX
2Ch 4 DR6
30h 4 DR7
34h 4 TR (Task Register)
38h 4 LDT
3Ch 4 GS (zero-extended)
40h 4 FS (zero-extended)
44h 4 DS (zero-extended)
48h 4 SS (zero-extended)
4Ch 4 CS (zero-extended)
50h 4 ES (zero-extended)
54h 12 TSS descriptor cache
60h 12 IDT descriptor cache
6Ch 12 GDT descriptor cache
78h 12 LDT descriptor cache
84h 12 GS descriptor cache
90h 12 FS descriptor cache
9Ch 12 DS descriptor cache
A8h 12 SS descriptor cache
B4h 12 CS descriptor cache
C0h 12 ES descriptor cache
The descriptor cache entries are:
Byte 0 Must be 0
Byte 1 Access-rights byte,
like access-rights byte
in a descriptor
Bytes 2-3 Must be 0
Bytes 4-7 32-bit base address
of the segment
Bytes 8-11 32-bit segment limit
I suggest aliging the table on a page
boundary (16 bytes, or lower 4 bits of
the offset must be zero). While this
is not a requirement for the 386 and
486, it may be a requirement when
running on a Pentium (which is
probably why a pentium-patch was
issued for Windows 95 for running
DPMI-DOS programs).
Now... something the speed guru's
might be interested in... how fast is
LOADALL. Well. according to some
tests I've done, LOADALL clocks in at
about 105 clockcycles on a 386, and
125 on a 486 which is bad. But not so
bad if you take into account the usual
number of clocks it take to switch
from PMode to real and back.
RESTRICTIONS
LOADALL performs no checking on the
values loaded into the registers, so
no exception will occur even if an
illegal value is loaded. Thus, the
processor can potential be put into a
strange state. If an illegal
descriptor value is set, no exception
occurs from the execution of LOADALL.
An exception will occur, however, when
an access using that descriptor is
attempted.
LOADALL can be executed in protected
mode, but only at the most privileged
level (level 0). So when you would
want to use LOADALL when a memory
manager is installed, you would first
have to shut-down the memory manager
by issuing a VCPI call to request ring
0 privileges.
Tasmaniac / ACiD / HypernovA

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,318 @@
[ Article crossposted from comp.lang.asm.x86 ]
[ Author was Jerzy Tarasiuk ]
[ Posted on 25 May 1995 17:37:03 +0200 ]
Real and Protected Modes.
Beginning from 80286, Intel CPUs have ability to work in Protected Mode
(older CPUs have Real Mode only). For compatibility reasons, all CPUs
start in Real Mode after reset. Below are presented main differences
between Real Mode and Protected modes for Intel CPUs. Note there are:
Real Mode, Protected Mode, Virtual 8086 Mode (they will be frequently
called RM, PM, VM86, respectively; also 286+(386+) will mean Intel
80286(80386) or better).
There are some differences between these modes in memory addressing
(PM can address all memory, while RM can't unless it is set in PM on
386+, and VM86 cannot unless using PM supporting it to remap memory
- this way EMM386 works); instruction set (some instruction are not
allowed in RM), privileges (something can be forbidden in PM for less
privileged code, many operations are forbidden in VM86), interrupt
handling. PM supports multitasking, PM can run tasks in VM86 (the
VM86 cannot function alone, must have PM code supporting it; it works
similarly 8086 CPU with few enhancements except interrupt servicing
which goes through PM). PM cannot store data to code segment (unless
by aliasing; MOV CS:[BX],AX is illegal in PM). VM86 and PM on 386+ can
have selective I/O port access restrictions (some ports can be accessed
without causing exception and other can't).
Memory addressing and Paging.
In any mode, opcode defines some offset and segment of referenced memory
address, e.g. mov ax,es:[bx+si+1] - segment es, offset bx+si+1, push si
- segment ss, offset sp-2, opcode itself is referenced by segment cs and
offset ip; the address is translated to Linear Address by adding the
offset to base of the segment and the Linear Address is then translated
to Physical Address which is outputed by CPU on its address pins.
In RM or VM86, the base is segment*10h; in PM the base is taken from
descriptor table (LDT or GDT) and can have any value.
The value in segment register is called "selector" and its bits 15-3
specify offset in LDT or GDT (the offset is multiply of 8), bit 2 is 0
for GDT, 1 for LDT, bits 1-0 specify RPL (Requested Privilege Level).
Unless Paging (possible in PM and VM86, on 386+ only) is enabled,
Physical Address = Linear. With Paging, low 12 bits of Linear Address
go to Physical, other are used as index to two-level page tables
(first bits 31-22 select page directory, then bits 21-12 select page).
Paging can also restrict access to some pages (in a way non-privileged
code can read it only or has no access at all), or define non-present
pages which have assigned physical addresses and put in memory in a way
transparent to program when access to their Linear Address is attempted.
Note Linear Address space is 4GB on 386+, and probably no system has so
much physical memory: Paging makes system able to simulate it has.
Segment has also limit. Initially, the limit is 0FFFFh for all segment
registers and cannot be changed in RM or VM86. In PM it is loaded from
LDT or GDT when segment register is loaded. On 286 in PM the limit can
be up to 0FFFFh, on 386+ in PM it can be up to 0FFFFFFFFh.
Also, PM allows "expand down" segments which allow access from address
limit+1 to maximum possible value of limit (depend on segment type).
Privilege Levels and Rules.
In RM, CPU has full privileges. In PM and VM86, they can be restricted.
This reduces possibility of making disasters by bad code.
Base rules: cannot access more privileged data or call less privileged
code than own privilege (although can return to less privileged code).
Additional: call to more privileged code cannot use any target address
caller wants, it can use addresses specified by system only; call to
more privileged code must change stack to make sure enough stack space
is available for called code (so caller cannot cause crash in it).
There are 4 levels: level 0 is full privilege (except Debug Registers,
which can be protected from access even from level 0; some instructions
are reserved for level 0 only), the bigger level the less privileges
are. Few terms used for Privilege Levels: CPL - Current PL, DPL -
Descriptor PL, RPL - Requested PL (in selector), IOPL (in flags) -
max CPL allowing I/O sensitive opcodes (CLI, STI, PUSHF, POPF,...).
Unless accessing Conforming Code segment, privilege rules require
max(CPL,RPL)<=DPL. To execute code (by FAR CALL or JMP) need DPL<=CPL
(note unless it is Conforming, must be DPL=CPL and RPL<=CPL) - cannot
call less privileged procedure, for example. To transfer control to
code with less PL (more privileged), must CALL via call gate (in such
a case, need max(CPL,RPL)<=gate_DPL, but for code the gate refers to
may be code_DPL<gate_DPL; the gate is entry in GDT or LDT; privilege
rules require also target_code_DPL <= CPL for CALL, = for JMP), this
also requires TR to point to valid TSS because it switches stack: old
SS:[E]SP are pushed on new stack, then parameters (as defined in call
gate) are pushed, finally CS:[E]IP are pushed. On return from the call
CPU detects RPL of CS on stack > CPL and switches stack back (if =, no
stack switch, < inhibited by privilege rules), for proper functioning
parameter counts on RET and in call gate must match. For stack segment
DPL must be equal CPL (so in more privileged mode no crash is possible
due to incorrect stack setting in less privileged, and in the less
privileged there is no access to more privileged mode stack).
The RPL is for system to block possibility to pass a pointer from user
code which is invalid in user mode and valid in system: system uses RPL
as for user code and gets access violation error in such a case.
It can be done using ARPL opcode which adjusts RPL for a selector, and
sets ZF if changed (to inform OS invalid access might be attempted).
OS uses it to set RPL of the pointer to CPL of the application code.
It is possible to check what access having to a segment by opcodes like
VERR, VERW, LAR, LSL. They all set ZF if having access, clear if not.
First two simply verify R/W access, LAR gets bits defining access right
for a segment, LSL gives the segment limit value. These opcodes allow
checking what would cause access violation, instead getting the error.
Some instructions are allowed at CPL=0 only. They are:
Clear Task<73>Switched Flag (CLTS), Halt Processor (HLT), loading some
system registers (GDTR,IDTR,LDTR,MSW,TR), any access to CRx,DRx,TRx.
Some other require CPL<=IOPL. They are: IN, INS, OUT, OUTS, CLI, STI.
Also, POPF behavior depends on CPL: if CPL>0, IOPL and VM aren't
changed by POPF, if CPL>IOPL, IF (interrupt enable) isn't changed.
Interrupts.
In every mode, there is an array containing information what action is
to be taken in case of interrupt. Its first entry corresponds to INT 0,
next to INT 1, and so on. It is called IDT(Interrupt Descriptor Table).
In RM, each entry in the IDT is simply far address of interrupt service
routine. Initially IDT is located at address 0 and has 100h entries
(400h bytes; some CPU-s have its limit 0FFFFh but the remainder isn't
accessible in RM); on pre-80286 CPUs the IDT address and size cannot be
changed, on 286+ can load and store them using LIDT and SIDT opcodes.
In PM the IDT has 8-byte entries which can be interrupt, trap or task
gates. Trap differs from interrupt by leaving interrupt flag same as
in interrupted code. Task gate causes calling another task. They all
have DPLs and interrupt instruction causes General Protection error
if CPL > interrupt or trap gate DPL. However, other interrupt sources
have "CPL 0" - they can access any gate needed.
Some conditions can cause an Exception. They are (for 80386): divide
error (0), debug exceptions (1), non-maskable interrupt (2), breakpoint
(3), overflow (4, on into opcode), bounds check (5, on bound opcode),
invalid opcode (6), coprocessor not available (7), double fault (8,E),
coprocessor segment overrun (9,P), invalid TSS (10,PE), segment not
present (11,PE), stack error (12,E), general protection error (13,E),
page fault (14,PE), coprocessor error (16); marked by P can occur in
PM and VM86 only, marked by E push error code on stack if they occur
in PM or VM86 (so stack is: error, IP, CS, flags; the error code is
usually either 0 or selector causing the exception (in case selector is
invalid or non-accessible), with flags on low order bits: bit 0 means
external source, bit 1 IDT selector, bit 2 LDT; for page fault it is
set of flags (bits 3-31 undefined): bit 0 set if page protection
violation, 1 if writing, 2 if user mode), most of them push IP of
opcode causing them, except 3,4,9 which push IP of next opcode.
Note: interrupt cannot be serviced at PL>CPL (unless via task switch),
attempt to do it causes General Protection error.
Interrupt processing in PM is more complicated when interrupt handler
has Privilege Level other than current code. It is handled similarly
CALL via gate: stack is switched, new SS:SP are taken from TSS, old
SS:SP are pushed on the new stack, then flags, CS, IP and eventually
error code (for some exceptions) are pushed.
In VM86 interrupt pushes GS,FS,DS,ES,SS,ESP,EFLAGS,CS,EIP (exception
also error code) onto PL 0 stack. There is VM bit in EFLAGS set to tell
interrupt occured in VM86. Note IDT must contain task gates and 80386
trap or interrupt gates pointing to a non-conforming code segment with
DPL=0 only - interrupt service must come through PL 0 or task switch.
The VM86 itself has CPL 3 and is allowed in 386 task only.
Descriptor Tables (PM only).
Global Descriptor Table(GDT) can contain descriptors of any type except
interrupt and trap gates. It is necessary for PM. First entry in GDT
isn't used - it corresponds to null selector which can be loaded into
segment register but causes exception if used for memory addressing.
Local Descriptor Table(LDT) can contain "normal" segment descriptors
(not e.g. TSS) and call or task gates only. Usually every task has its
own LDT (changed on task switch). The LDT must have descriptor in GDT.
Interrupt Descriptor Table(IDT) was discussed in "Interrupts" section.
"Normal" segment descriptors are referenced when a segment register is
loaded and they describe a memory area and give some access to it.
Bit 2 of selector used selects table: 0 means GDT, 1 means LDT.
Other descriptors can be Task State Segment(TSS), and gates. They can
be referenced "as a code segment", e.g. by far jump or call and they
cause transferring control to task or code segment referenced by them.
It is kind of indirect jump or call (they contain target selector).
TSS or gate pointing to TSS cause task switch. Gate can be used to
transfer control to more privileged code not accessible directly.
TSS can be also referenced by LTR (Load Task Register) opcode and it
is done once during PM initialization. LDT descriptor can be loaded
into LDTR(register) by LLDT opcode and usually it is done once.
Segment and System Descriptors.
The following segment types (in byte [descriptor+5]) are supported
(for all bit 7 means present in memory, bits 5-6 keep DPL which says
what is maximum CPL which can access the descriptor, the restriction is
for all descriptors, not segments only, except conforming segments):
10h+flags - data: bit 1 - writable, bit 2 - expand down
18h+flags - code: bit 1 - readable, bit 2 - conforming
for both, bit 0 is set by any access. The descriptor also contains
limit in word [0] (in 386 segments extended to bits 0-3 of byte [6])
and base in bytes [2..4] (in 386 segments extended to byte [7]).
Byte [6] keeps few additional flags: bit 7 - granularity (limit is in
4kB pages; e.g. limit 0 means 0..0FFFh accessible), bit 6 - 32-bit
addressing (applies to code and stack - use EIP, ESP, makes expand down
segment upper limit 4GB), bit 5 must be 0, bit 4 is for programmer.
01h+flags - TSS: bit 1 - busy, bit 3 - 386 TSS
02h - LDT
04h+flags - call gate
05h - task gate
06h+flags - interrupt gate: bit 0 - trap, bit 3 - 386.
for all gates, word[2] keeps selector, word[0] and word[3] keep offset
of called code (ignored for task gate), byte[4] keeps word count (0-31)
for copying in case of inter-level call (call gate only, else ignored);
TSS and LDT have base and limit in same form as code and data segments
have, they can have bit 7 set in byte [6] to specify limit in pages.
Word [6] should be 0 for the descriptor to mean the same on 286/386.
LDT is similar GDT, except not all descriptor types are allowed.
TSS holds entire task state (all registers: general, segment, flags,
ip, ldtr); it also keeps link to caller TSS (valid if the task was
activated by INT or CALL) and stacks (SS and [E]SP) for PL 0,1,2
(they are used when more privileged code is invoked via gate from less
privileged). 386 TSS has also debug trap bit (if set, causes INT 1 on
task switch to the TSS), I/O bit map (saying which I/O addresses can
be accessed when CPL>IOPL without General Protection exception), and
CR3 value for the task (can remap memory on task switch).
Page tables:
both page directory and page table entries keep referenced address in
bits 31-12, have bits 11-9 reserved for programmer, must have bits 8,7,
4,3 set to 0; bit 5 is called A (accessed), it is set by CPU on access
to the entry, bit 6 is called D (dirty), it is set if referenced memory
is written; bit 0 is called P (present), all other are ignored if it is
not set; bit 2 allows user (CPL=3) access if set, bit 1 allows user to
write (together with bit 2 only), for CPL<3 read/write is allowed for
any setting of bits 1 and 2 (no protection against system this way).
Note page table entries used are usually cached by CPU: modifying them
in memory may cause no mapping change until the cache is reloaded. The
cache is flushed every time CR3 (which points to first page directory
entry) is loaded. Bits 0-11 of CR3 must be 0 (directory page-aligned).
Addressing through page tables: CR3+(Linear_Address SHR 20) AND 0FFCh
is address in Page Directory, the entry at the address contains Page
Table address; Page Table address + (Linear_Address SHR 10) AND 0FFCh
is address in Page Table and the entry at the address contains base
address of the page, combine it with bits 11-0 of Linear_Address and
the result is Physical Address. In case of any error, CR2 is set to the
Linear Address causing the error and error code explains what error.
Note: if Paging is enabled, CR3 must keep Physical Address of Page
Directory and all other addresses are Linear Addresses.
Switching to Protected Mode or back to Real Mode:
First: to get control in case of crash, need store in dword [0467h]
address where control is to be passed, and put 0Ah in CMOS register 0Fh
(by CLI; MOV AL,8Fh; OUT 70h,AL; (1us delay) MOV AL,0Ah; OUT 71h,AL;).
Also: normally, some circuitry in PC compatibles disables address line
A20; must enable it. If you use HIMEM, it can be enabled by a request
to HIMEM. If you also have DOS=HIGH, it is usually enabled, as it is
enabled by any DOS call. In other cases, you must send output port
value to keyboard controller to enable it before switching to PM.
Switch to PM: required is loading GDTR, then can enable protection by
setting CR0/MSW bit 0 (MOV EAX,CR0; OR AL,1; MOV CR0,EAX; or SMSW AX;
OR AL,1; LMSW AX; first on 386+, second on 286+); it is recommended
to load IDTR immediately before or after mode switch (same IDT can't be
valid in both modes); immediately after mode change should execute JMP
to flush prefetch queue which may be partially decoded (the decoding
may be mode dependent); need load CS and SS - they contain invalid
selectors and e.g. interrupt causes them to be put on stack and crash
on IRET; it is also recommended to load all segment registers (they can
be loaded with 0 to contain invalid selector and cause exception if any
of them is used to address memory) and LDTR; before first task switch
must load TR (selector of valid free TSS descriptor; the TSS will be
used to store state on task switch).
These is also a BIOS call which switches to PM and changes external
interrupt vector mapping (normally 1st controller has 08h..0Fh, 2nd
70h..77h, the 1st conflicts with some CPU exceptions; however it is
easy to distinguish external interrupt from an exception), it also
enables address line A20. See INT 15h, AH=89h description.
Returning to RM: it can be done by clearing bit 0 in CR0 but it needs
some preparation: must disable paging (go to code/stack which has
linear addresses same as physical, clear PG bit in CR0, clear CR3), go
to code segment with limit=64k and load all segment registers except
CS with valid descriptor of 64kB read/write expand-up byte-granular
present segment (attribute byte=93h, extended attribute=0) - otherwise
you can get RM with e.g. read-only or 32kB ES, which will soon cause
crash. After clearing the bit 0 of CR0 execute far jump to load CS and
flush prefetch queue and load segment registers for RM.
This is not available on 80286 which has no CR0 register (the Protect
Enable bit cannot be cleared by LMSW). The only way to get to RM again
is resetting the CPU: it can be done by the following code: CLI;
XOR CX,CX; wait_kbd_ctrlr_input_empty: IN AL,64h; TEST AL,2; LOOPNZ
wait_kbd_ctrlr_input_empty; MOV AL,0FEh; OUT 64h,AL; HLT; or by CPU
shutdown (resulting in case of exception while servicing double fault).
Note most programs running system in VM86 provide interface to switch
to PM and back to VM86, it is called VCPI (Virtual Control Program
Interface), can be tested for presence and invoked by INT 67h,AH=0DEh.
It requires 3 entries in GDT to be reserved for VCPI provider.

View File

@@ -0,0 +1,328 @@
A CRASH COURSE IN PROTECTED MODE
By Adam Seychell
After my release of DOS32 V1.2 a lot of people were asking for basic
help in protected mode programming. If you already know what a selector is
then there is probably no need for you to read this file.
Ok you know all about the 8086 ( or a 386 in real mode ) architecture
and what to know about this fantastic protected mode stuff. I'll start off
saying that I think real mode on the 386 is like driving a car that is stuck
in first gear. There is the potential of a lot of power but it is not being
used. It really degrades the 386 processor and was not designed to normally
operate in this mode. Even the Intel data book states "Real mode is required
primarily to set up the processor for Protected Mode operation".
SEGMENTATION OF THE INTEL 80x86
A segment is a block of memory that starts at a fixed base address and
has a set length. As you should already know that *every* memory reference
by the CPU requires both a SEGMENT value and a OFFSET value to be specified.
The OFFSET value is the location relative to the base address of the segment.
The SEGMENT value contains the information for the segment. I am going to
explain very basically how this SEGMENT value is interpreted by the 80386 to
give the parameters of segments.
In protected mode this SEGMENT value is interpreted completely different
than in real mode and/or Virtual 86 mode. The SEGMENT values are now called
"selectors". You'll see why when finished reading this file. So whenever you
load a segment register you are loading it with a selector.
The Selector is word length and contains three different fields.
Bits 0..1 Request Privilege level ( just set this to zero )
Bit 2 Table Indicator 0 = Global Descriptor Table
1 = Local Descriptor Table
Bits 3..15 The INDEX field value of the desired descriptor in the GDT
This index value points to a descriptor in the table.
The GLOBAL DESCRIPTOR TABLE (GDT)
The Global Descriptor Table ( GDT ) is a table of DESCRIPTORS and it is
stored in memory. The address of this table is given in a special 386
register called the global descriptor table register. There always must be a
GDT when in protected mode because it is in this table where all of the
segments are defined.
Each DESCRIPTOR ( stored in the GDT ) contains the complete information
about a segment. It is a description of the segment. Each Descriptor is 64
bits long and contains many different fields. I'll explain the fields later.
The INDEX field ( stored in bits 3..15 of any segment register ) selects a
descriptor to use for the type of segment wanted. So the only segments the
programmer can use are the available descriptors in the GDT.
Example:
Suppose you what to access location 012345h in your data segment and
you were told that the descriptor for your data segment is descriptor
number 6 in the Global Descriptor Table. Assume that the Global Descriptor
Table has already been set up and built for you ( example, as in DOS32).
Solution:
We need to load a segment register (SS,DS,FS,GS,ES) with a value so
that it will select (or index ) descriptor number 6 of the GDT. Then
reference the address with a instruction that will use this loaded
segment register.
One of the segment registers (FS,DS,GS,SS,CS or ES) must be loaded with the
following three fields,
Request Privilege level ( Bits 0..1 ) = 0 (always)
Table Indicator ( bit 2 ) = 0
Index ( bits 3..15 ) = 6
mov ax,0000000110000b ;load DS with the selector value
mov ds,ax
mov byte ptr DS:[ 012345h ],0 ; Using the DS segment register
The 386 has hardware for a complete multitasking system. There are
several different types of descriptors available in the GDT for managing
multitasking. You don't need to know about all the different descriptors just
to program in protected mode. Just the info above is enough. All you need to
know to program in protected mode is what descriptors are available to you
and what are the selector values to these descriptors. The base address of
the segment may also be known. See the file DOS32.DOC for obtaining the
selector values.
There are two groups of descriptors
1) CODE/DATA descriptors which are used for any code and data segments.
2) SYSTEM descriptors are used for the multitasking system of the 386. These
type of descriptors will never need to be used for programming applications.
Format of a code and data descriptor
BITS description if the field
----------------------------------------------------------------
0..15 SEGMENT LIMIT 0...15
16..39 SEGMENT BASE 0..23
40 (A) accessed bit
41..43 (TYPE) 0 = SEE BELOW
44 (0) 0 = code/data descriptor 1 = system descriptor
45..46 (DPL) Descriptor Privilege level
47 (P) Segment Present bit
48..50 SEGMENT LIMIT 16..19
51..52 (AVL) 2 bits available for the OS
53 zero for future processors
54 Default Operation size used by code descriptors only
55 Granularly: 1 = segment limit = limit field *1000h
0 = segment limit = limit field
56..63 SEGMENT BASE 24..31
format of TYPE field
bit 2 Executable (E) 0 = Descriptor is data type
1 Expansion Direction (ED) 0 = Expand up
1 = Expand up
0 Writeable (W) W = 0 data segment is read only
W = 1 data segment is R/W
bit 2 Executable (E) 1 = Descriptor is code type
1 Conforming (C) ( I don't understand )
0 Readable (R) R = 0 Code segment execute only
R = 1 Code segment is Readable
I'd better stop here, I am confusing myself. As you can see there is
more to a segment that just it's base address and limit.
The three descriptors that are available in DOS32 all have limits of
0ffffffffh (4GB). This means that the offsets can be any value.
For example, the instruction XOR EAX,ES:[0FFFFFFFFh] is allowed.
If you happen to load an invalid selector value into one of the segment
registers then the 386 will report an General Protection exception
( interrupt 13 ). In protected mode this exception is also used for many
other illegal operations.
The LINEAR ADDRESS and PHYSICAL ADDRESS
All the address translations described above is done by the 386
segmentation unit. The segmentation unit looks up the descriptor tables,
segment selectors, offsets and then outputs a 32bit linear address. This
linear address is calculated by the segmentation unit in the following
manner.
Linear address = base address of segment + offset.
The segment base address is found in the Base address field ( bits 16..39 &
56..63 ) of a descriptor which is located in the Global Descriptor Table.
The index field ( bits 3..15) of the segment register selects the descriptor
to use.
An example of the linear address of the instruction.
MOV EAX, ES:[EDX*8+012345678h]
where EDX = 100h and ES equals a selector wich points to a descriptor with
the base field equal to 02000000h.
The linear address = 2000000h + ( 100h*8 + 012345678h ) = 014345E78h
Just to make things even more complicated the 386 has an second memory
managing unit called the Paging Unit. The linear address calculated above may
still not be the physical RAM location the 386 is addressing. This linear
address has yet to go through another stage, the paging unit. If you are
having trouble with what I've said so far then you may want to take a coffee
break before continuing because this is even worse.
The 32bit linear address is directed to the paging unit. The paging unit
divides the linear address into three sections.
bits 0..12 Offset in a page
bits 13..23 Points to the page entry in the page table
bits 24..31 Points to the directory entry in the page directory table
The page table contains 1024 double word entires. In each of these entries
is the physical address of a 4KB page. The page directory also contains 1024
double word entries. Each of these directory entries hold a physical address
of a page table. See intel Documentation for the exact format of the page
table entries and directory table entries. The physical base address of the
DIRECTORY TABLE is in control register 3 ( CR3 )
This means if every entry in the directory table is used then there would
be 1024 page tables available. Because each page table hold 1024 page
addresses then there would be a total of 1024*1024 4KB pages.
The addresses in the page tables are all physical addresses. i.e the output
of the CPU address bus pins. The paging unit can be enabled or disabled
depending on wether bit 31 of CR0 is set. If the paging in disabled then the
linear address simply equals the physical address. If paging is enabled the
the linear address is translated by the paging unit to form a physical
address. Please note that it is possible to set up the page tables and
directory such that the linear address equals the physical address. This is
what DOS32 does for the first 1MB of linear address space.
If you can not understand my hopeless attempt of describing the paging unit
then the diagram below might help.
The 32 bit linear address from the segmentation unit
-------------------------------------------------------------------------
| BITS 0..12 | BITS 13..23 | BITS 24..31 |
| | | |
-------------------------------------------------------------------------
| | |
|------------------------ | |
| | |
| | |
| | |
A Page in memory (4KB) | | |
-------------------------- | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | <------- | |
| | | |
| | | |
| | | |
|---> ------------------------- | |
| | |
| | |
| | |
| PAGE TABLE Offset | |
| -------------------------- | |
| | | 4092 | |
| -------------------------- | |
| . . | |
| . . | |
| . . | |
| -------------------------- | |
| | | 12 | |
| -------------------------- | |
|---<| address of page | 8 <--------- |
-------------------------- |
| | 4 |
-------------------------- |
| | 0 |
|--> -------------------------- |
| |
| |
| |
| |
| |
| DIRECTORY TABLE Offset |
| -------------------------- |
| | | 4092 |
| -------------------------- |
| . . |
| . . |
| . . |
| -------------------------- |
|---<| address of page table | 12 <-----------------------------
--------------------------
| | 8
--------------------------
| | 4
--------------------------
| | 0
|--> --------------------------
|
|
|
| ----------------------------------------------
--------<-| CR3 |
----------------------------------------------
This was only meant to be a rough introduction to the protected mode
segmentation mechanism of the 80386+. I hope I did not make this sound
too complicated so that you have been put off with the whole idea of
protected mode programming. If you want to know more then I suggest you buy a
book on the 80386. The "Intel Programmers Reference guide" is the most
detailed book around. The info here is only meant to give you an idea of how
protected mode works.
Please note that DOS32 does *ALL* of the setting up needed for protected
mode. Don't worry if you couldn't understand half the stuff I was talking
about. You don't have to know about any stupid things like the descriptor
format, selector index fields, privilege levels ,paging, tables, ect , ect.
What you do need to know are the selector values for all the descriptors that
are available to your program. Then the segment registers can simply be
loaded with these known selector values. DOS32 uses only 3 descriptors ( or
segments ) as described in DOS32.DOC.


View File

@@ -0,0 +1,329 @@
PROTECTED MODE ( A more detailed approch )
Written by Yann Stephen
Global Descriptor Table Register (GDTR) :
The contents of the global table register define a table in the 80386DX's
physical memory address space called the Global Descriptor Table (GDT). This
global descriptor table is one important element of the 80386DX's memory
management system.
GDTR is a 48-bit register that is located inside the 80386DX. The lower two
bytes of this register, which are identified as LIMIT, specify the size in
byte of the GDT. The decimal value of LIMIT is one less than the actual size
of the table. For instance, if LIMIT equals 00FFh the table is 256 bytes in
length. Since LIMIT has 16 bits, the GDT can be up to 65,536 bytes long. The
upper four bytes of the GDTR, which are labelled BASE, locate the beginning
of
the GDT in physical memory. This 32-bit base address allows the table to be
positioned anywhere in the 80386DX's address space.
The GDT provides a mechanism for defining the characteristics of the
80386DX's global memory address space. Global memory is a general system
resource that is shared by many or all software tasks. That is, storage
locations in global memory are accessible by any task that runs on the
microprocessor. This table contains what are called system segment
descriptors. It is these descriptors that identify the characteristics of the
segments of global memory. For instance, a segment descriptor provides
information about the size, starting point, and access rights of a global
memory segment. Each descriptor is eight bytes long, thus our earlier example
of a 256-byte table provides enough storage space for just 32 descriptors.
Remember that the size of the global descriptor table can be expanded simply
by changing the value of LIMIT in the GDTR under software control. If the
table is increased to its maximum size of 65,563 bytes, it can hold up to
8,192 descriptors.
Question 1.1 :
How many descriptors can be stored the global descriptor table when the size
of LIMIT is 0FFFh.
The value of the BASE and LIMIT must be loaded into the GDTR before the
80386DX is switched from real mode of operation to the protected mode.
Special instruction are provided for this purpose in the system control
instruction set of the 80386 DX. Once the 80386DX is in protected mode, the
location of the table is typically not changed.
Interrupt Descriptor Table Register (IDTR) :
Just like the global descriptor table register, the interrupt descriptor
table register (IDTR) defines a table in physical memory. However, this table
contains what are called interrupt descriptors, not segment descriptors. For
this reason it is known as the Interrupt Descriptor Table (IDT).This
register and table of descriptors provide the mechanism by which the
microprocessor passes program control to interrupt and exception routines.
Just like the GDTR, the IDTR is 48 bits in length. Again, the lower
two bytes of the register (LIMIT) define the table size. That is, the size of
the table equals LIMIT+1 bytes. Since two bytes define the size, the IDT can
also be up to 65,536 bytes long. But the 80386DX only supports up to 256
interrupts and exceptions; therefore, the size of the IDT should not be set
to support more than 256 interrupts. The upper three bytes of IDTR (BASE)
identify the starting address of the IDT in physical memory. The type of
descriptor used in the IDT are what are called interrupt gates. These gates
provide a means for passing program control to the beginning of an interrupt
service routine. Each gate is eight bytes long and contains both attributes
and a starting address for the service routine.
Question 1.2 :
What is the maximum value that should be assigned to the limit in the IDTR?
This table can also be located anywhere in the linear address space
addressable with the 80386DX's 32-bit address. Just like the GDTR, the IDTR
needs to be loaded before the 80386DX is switched from the real mode to
protected mode. Special instructions are provided for loading and saving the
contents of the IDTR. Once the location of the table is set, it is typically
not changed after entering the protected mode.
Question 1.3 :
What is the address range of the last descriptor in the interrupt descriptor
table defined by base address 00011000h and limit 01FFh
Local Descriptor Table Register (LDTR) :
The Local Descriptor Table Register (LDTR) is also part of the 80386DX's
memory management support mechanism. Each task can have access to its own
private table descriptor table in addition to the global descriptor table.
This private table is called the local descriptor table (LDT) and defines a
local memory address space for use by the task. The LDT holds segment
descriptors that provide access space for use by the task. The LDT holds
segment descriptors that provide access to code and data in segments of
memory that are reserved for the current task. Since each task can have its
own segment of local memory, the protected-mode software system may contain
local descriptor tables. Whenever a selector is loaded into the LDTR, the
corresponding descriptor is transparently read from global memory and loaded
into the local descriptor table cache within the 80386DX. It is this
descriptor that defines the local descriptor table.
Assume that every time a selector is loaded into the LDTR, a local descriptor
table descriptor is cached and a new LDT is activated.
Control Registers :
The protected-mode model includes the four system control registers,
identified as CR0 through CR3 :
31 23 15 7 0
-------------------------------------------------------
| Page Directory Base Register (PDBR)| Reserved | CR3
-------------------------------------------------------
| Page Fault Linear Address | CR2
-------------------------------------------------------
| RESERVED | CR1
-------------------------------------------------------
|P| |R|T|E|M|P| CR0
|G| RESERVED | |S|M|P|E|
-------------------------------------------------------
Notice that the lower five bits of CR0 are system control flags. These bits
make up what are known as the machine status word (MSW). The most significant
bit of CR0 and registers CR2 and CR3 are used by the 80386DX's paging
mechanism. Let us consider by examining the machine status word bits of CR0.
They contain information about the 80386DX's protected-mode configuration and
status. The four bit labelled PE, MP, EM and R are control bit that define
the protected mode system configuration. The fifth bit, TS, is a status bit.
These bits can be examined or modified through software.
The protected-mode enable (PE) bit determines if the 80386DX is in
the real or protected mode. At reset, PE is cleared. This enables the real
mode of operation. To enter the protected mode, we simply switch PE to 1
through software. Once in the protected mode, the 80386DX can be switched
back to real mode under software control by clearing the PE bit. It can also
be returned to real mode by hardware reset.
The math present (MP) bit is set to 1 to indicate that a numeric
coprocessor is present in the microcomputer system. On the other hand, if the
system is to be configured so that a software emulator is used to perform
numeric operations instead of a coprocessor, the emulate (EM) bit is set to
1.
Only one of these two bits can be set at a time. Finally, the extension type
(R) is used to indicate whether an 80387DX or 80287 numeric coprocessor is
in use. Logic 1 in R indicates that an 80387DX is installed. The last bit in
the MSW, task switched (TS), automatically gets set whenever the 80386DX
switched from one task to another. It can be cleared under software control.
The protected mode software architecture of the 80386DX also supports
paged memory operation. Paging is turned on by switching the PG bit in CR0 to
logic 1. Now addressing of physical memory is implemented with an address
translation mechanism that consists of a page directory and page table that
are both held in the physical memory. This register holds a 20-bit page
directory base address that points to the beginning of the page directory. A
page fault error occurs during the page translation process if the page is
not
present in memory. In this case, the 80386DX saves the address at which the
page fault occurred in register CR2. This address is denoted as page fault
linear address.
Task Register (TR):
The task register is one of the key elements in the protected mode task
switching mechanism of the 80386DX microprocessor. This register holds a
16-bit index value called a selector. The initial selector must be loaded
into TR under software control. This starts the initial task. After this is
done, the selector is changed automatically whenever the 80386DX executes an
instruction that performs a task switching.
TR is used to locate a descriptor in the global descriptor table. Notice that
when a selector is loaded into TR, the corresponding task state segment (TSS)
descriptor automatically gets read from memory and loaded into on-chip task
descriptor cache. This descriptor defines a block of memory called the task
called the task state segment (TSS). It does this by providing the starting
address base (BASE) and the size (LIMIT) of the segment. Every task has it
own TSS. The TSS holds the information needed to initiate the task, such as
initial values for the user-accessible registers.
Registers with Changed Functionality :
The segment registers are now called the segment selector register, and
instead of holding a base address they are loaded with what is known as a
selector. The selector does not directly specify a storage location in
memory. Instead, it selects a descriptor that defines the size and
characteristics of segment of memory.
15 8 2 0
-------------------------------------------------------
| INDEX |TI|RPL|
-------------------------------------------------------
SELECTOR
Bits Name Function
1-0 Requested Indicates selector privilege level desired
Privilege
Level (RPL)
2 Table Indicator TI = 0 use Global Descriptor Table (GDT)
(TI)
TI = 1 use Local Descriptor Table (LDT)
15-3 INDEX SELECT descriptor entry in table
TI bit select the table to be used when accessing a segment descriptor,
because two tables are active at the same time GDT and LDT.
The index is used as a pointer to a specific descriptor entry in the table
selected by the TI bit.
Protected-Mode system control instruction set
Instruction Description Mode
LGDT S Load the global descriptor table register. S specifies Both
the memory location that contains the first byte of the
6 bytes to be loaded into the GDTR.
SGDT D Store the global descriptor table register. D specifies Both
the memory location that gets the first of the six bytes
to be stored from the GDTR.
LIDT S Load the interrupt descriptor table register. S specifies Both
the memory location that contains the first byte of the
6 bytes to be loaded into the IDTR.
SIDT D Store the interrupt descriptor table register. D specifies Both
the memory location that gets the first of the six bytes
to be stored from the IDTR.
LMSW S Load the machine status word. S is an operand to specify Both
the word to be loaded into MSW.
SMSW D Store the machine status word. D is an operand to specify Both
the word location or register where the MSW is to be
saved.
LLDT S Load the local descriptor table register. S specifies the
Protec operand to specify a word to be loaded into the LDTR.
SLDT D Store the local descriptor table register. D is an operand
Protec to specify the word location where the LDTR is to be saved.
LTR S Load the task register. S is an operand to specify a word
Protec to be loaded into TR (Task Register).
STR D Store the task register. D is an operand to specify the
Protec word location where the TR is to be stored.
LAR D,S Load access rights byte. S specifies the selector for the
Protec descriptor whose access byte is loaded into the upper byte
of the D operand. The low byte specified by D is cleared.
The zero flag is set if the loading completes successfully;
otherwise it is cleared.
LSL R16,S Load segment limit. S specifies the selector for the
Protec descriptor whose limit word is loaded into the word
register operand R16. The zero flag is set if the
loading completes successfully; otherwise it is cleared.
ARPL D,R16 Adjust RPL field of the selector. D specifies the selector
Protec whose RPL field is increased to match the PRL field in the
register. The zero flag is set if successful;otherwise it
is cleared.
VERR S Verify read access. S specifies the selector for the Protec
segment to be verified for read operation, If successful
the zero flag is set; otherwise it is reset.
VERW S Verify write access. S specifies the selector for the
Protec segment to be verified for write operation, If successful
the zero flag is set; otherwise it is reset.
CLTS Clear task switched flag.
Protec
A few examples of these new instructions :
LGDT [INIT_GDTR]
Loads the GDTR with the base and limit pointed to by address INIT_GDTR to
create a global descriptor table in memory. This instruction is meant to be
used during system initialisation and before switching the 80386DX to the
protected mode.
Once loaded the current contents of the GDTR can be saved in memory by
executing the store global table (SGDT) instruction.
SGDT [SAVE_GDTR]
The instruction load machine status word (LMSW) and store machine status word
(SMSW) are provided to load and store the contents of the machine status word
(MSW), respectively. These are the instructions that are used to switch the
80386DX from real to protected mode. To do this we must set the least
significant bit in the MSW to 1. This can be done by first reading the
contents of the machine word , modifying the LSB (PE), and then writing the
modified value back into the MSW part of CR0. The instruction sequence that
follows will switch an 80386DX operating in real mode to protected mode:
SMSW AX ;read from the MSW
OR AX,1 ;modify the PE bit
LMSW AX ;write to the MSW
Solution 1.1 : Each descriptor takes up eight bytes; therefore, a 4096-byte
table can hold :
Descriptors = 4096/8 = 512
Solution 1.2 : The maximum number of interrupt descriptors than can be used
in an 80386DX microcomputer system is 256. Therefore, the maximum table size
in bytes is :
IDT (size) = 8*256 = 1000h bytes
LIMIT = 1000h-1 = 0FFFh (We start from zero)
Solution 1.3 : From the values of the base and limit, we find that the table
is located in the address range
IDT (start) = 00011000h
IDT ( end ) = 000111FFh
The last descriptor in this table takes up the eight bytes of the memory from
address 000111F8h through 000111FFh.


View File

@@ -0,0 +1,67 @@
Greetings, Tran (a.k.a. Thomas Pytel) typing here. This bunch of files
comprises two versions of my protected mode DOS extender PMODE. They are both
publicly available and are not confidential or proprietary. I, Thomas Pytel,
reserve all rights to the source code. However, feel free to use or distribute
them in any manner you wish. All I ask, if you use this code in some
production, is credits.
This package is meant mainly as an update for coders already using PMODE,
But you can use it to start from scratch. The code and documentation may not
be thoroughly clear, in this case if you need more examples get some of the
other PMODE releases (like PMC or any demo source codes I have released) for
more practical examples.
------------------------------------------------------------------------------
PMODE v2.5 and PMODE v3.03 are provided here in the form of source code,
along with various examples of their use. Why two versions? PMODE 2 is the
predecessor to PMODE 3, but it is also slightly different in intended
functionality. PMODE 2 is a dedicated ASM DOS extender, a protected mode
system for game/demo code and other standalone programs. PMODE 3 is a more
general purpose protected mode kernel, suitable for almost any protected mode
need (high level language extender).
PMODE 3 can do what PMODE 2 does, in fact, I converted PMODE 2.4 to a shell
which uses PMODE 3 as its kernel and it is included here. The drawback to this
is that PMODE 3 gobbles up more low memory. PMODE 2 has proven itself quite
stable through the time its been in use. It is a small, fast, stable, albeit
a tad bit limited extender. There is no sense in trashing it just because
something newer and cleaner (in source) has come along. Each version has its
appropriate uses, it is up to you to decide.
The major difference between PMODE 2 and PMODE 3 is that PMODE 2 is a more
complete system. It sets up 32bit execution and memory pools (low and high)
and handles other minor nuances of going from the DOS command line to
protected mode code and back. PMODE 3 is designed to be the low level end of
a more complete system (such as PMC, an extender for Borland C++ 4.0 I did
with it). Technically speaking, more can be done with PMODE 3. But I still
suggest using PMODE 2 for games and demos (if you don't mind all ASM coding).
It would be nice if someone out there used PMODE 3 in an extender for Watcom
C++. I am not pursuing PMC because BC4 is just plain crap (though it is usable
in some instances). Don't worry about using/supporting one version over the
other. Taking code between two different protected mode systems is trivial
compared to going from real mode to protected mode.
------------------------------------------------------------------------------
The source, documentation, and appropriate examples are included for both
versions of PMODE in their appropriate separate archives. The batch files
which build the examples assume you have TASM and TLINK in your path.
One word of caution, QEMMs DPMI host QDPMI has serious problems with memory
allocation, mode switching, and IRQs. Keep this in mind when testing stuff
under it. If something screws up (usually gives you a QDPMI exception), try it
with a different DPMI host.
TLINK also has problems with protected mode. Segment alignment has had its
share of problems over time, as has referencing real mode segment addresses
above the first 64k of a 32bit segment (fixup overflows).
This package is dated as of the beginning of September of 1994. Enjoy
protected mode. You can send me mail (but don't expect a response, it is not
possible due to account restrictions) at tran@phantom.com, so don't bother...
Oh well...
Tran...

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,155 @@
;----
; Example program switching to Protected and back to Real Mode
; Jerzy Tarasiuk 8-Jun-1995
;
code segment
assume cs:code
stk segment para stack 'stack'
dw 100h dup(?)
stk ends
assume cs:code,ss:stk,ds:Nothing,es:Nothing
.386P
;-
; GDT to be used in Protected Mode
;
mygdt dq 0 ; null descriptor
gdtcd dw -1,0,9A00h,0 ; 64kB code segment, base to be set
dw -1,0,9200h,008Fh ; 4GB R/W segment, base=0
dw -1,0,9200h,0 ; 64kB R/W segment, base=0
;---
; pm_mem - access memory (peek/poke a byte) in Protected Mode
;
; entry EBX=address, CX=0 for peek to CL or CX!=0 for poke CL
; changes EAX,EDX,GDTR,CL
;
; requires A20 to be enabled if to be used on a PC compatible
; (A20 is usually enabled if DOS is loaded into HMA)
;
pm_mem proc
pushf
push ds
mov ax,cs
movzx eax,ax
shl eax,4 ; eax=base for code segment
mov dword ptr gdtcd[2],eax
mov byte ptr gdtcd[5],9Ah ; set segment attribute
mov dx,offset mygdt
movzx edx,dx
add eax,edx ; eax=base for GDT
push eax
push 20h
movzx eax,sp
cli ; make sure no ISR will interfere now
lgdt fword ptr ss:[eax] ; LGDT is necessary before switch to PM
add sp,6
mov eax,cr0
or al,1
mov cr0,eax ; sets Protected Mode
db 0eah ; far jump to set CS & clear prefetch queue
dw pm_in
dw 8
pm_in: mov dx,10h
mov ds,dx ; load long segment descriptor from GDT into DS
jcxz pm_get
mov [ebx],cl
pm_get: mov cl,[ebx]
mov dl,18h
mov ds,dx ; load 64kB segment descriptor from GDT into DS
and al,not 1
mov cr0,eax ; sets Real Mode
db 0eah ; far jump to restore CS & clear prefetch queue
dd pm_out ; it MUST be jump - far return crashed!
pm_out: pop ds
popf
ret
pm_mem endp
;---
; vfycpu - make sure have proper CPU, in Real Mode, and A20 enabled
;
; changes AX,DX
; returns AL=error code if anything wrong
; error codes: 040h - not 32-bit CPU (PM,A20 not tested)
; 001h - already in PM (386+, A20 not tested)
; 0FFh - A20 disabled (32-bit CPU in RM)
;
vfycpu proc
pushf ; save flags
cli ; make sure no ISR will interfere now
;
pushf ; 1. make sure have at least 386
pop ax ; AX=flags
xor ah,40h ; toggle NT
push ax ; stack: modified_flags,original_flags
popf
pushf ; stack: modified_flags_from_cpu,original_flags
pop dx ; DX=flags passed via CPU
mov al,dh
xor al,ah
jnz vcfail ; improper CPU
;
smsw ax ; 2. make sure are in Real Mode
and al,1
jnz vcfail ; if already in PM (maybe VM86)
;
push ds ; 3. make sure A20 is enabled
push es
push bx
xor bx,bx
mov ds,bx
mov ax,-1
mov es,ax
xor [bx],ah ; change byte[000000h]
mov al,es:[bx+10h] ; get byte[100000h]
xor [bx],ah ; change byte[000000h]
xor al,es:[bx+10h] ; compare byte[100000h] with its previous value
; 0 if unchanged, -1 means [000000h]==[100000h]
pop bx
pop es
pop ds
;
vcfail: popf ; restore flags
test al,al ; test error code
ret
vfycpu endp
;---
; sample program
; - check if have proper CPU, exit with error message if bad
; - peek byte[0FFFFFFF0h] in PM
; - peek byte[0FFFEFFF0h] in PM
; - exit (status = byte obtatained)
;
badcpum db "Not 80386+ CPU!",0Dh,0Ah,'$'
inpmerm db "Already in Protected Mode!",0Dh,0Ah,'$'
bada20m db "Address line A20 disabled!",0Dh,0Ah,'$'
start: call vfycpu ; make sure having proper CPU and mode
add al,al
jz cpuok ; .. OK
lea dx,bada20m
jc error
lea dx,badcpum
js error
lea dx,inpmerm
error: push cs ; not proper - error message and exit
pop ds
mov ah,9
int 21h
mov ax,4cffh ; exit, status=-1
int 21h
cpuok: xor cx,cx ; peek a byte
mov ebx,-10h ; from 0FFFFFFF0h
call pm_mem
xor cx,cx ; peek a byte
mov ebx,-10010h ; from 0FFFEFFF0h
call pm_mem
exit: mov ah,4ch ; exit
mov al,cl ; status = the byte
int 21h
code ends
end start

View File

@@ -0,0 +1,737 @@
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ
<20> Real Memory Mode <20>
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD> Introduction <20>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
The vast majority of programmers probably hate Intel's segmented memory
scheme, personally I think it has it's advantages (says me...waiting for
the letter bombs to arrive), but it is a pain to use when you've got large
data structures in memory that you want to move around. In any case being
limited to 640K of base memory is a pain (even I agree to this) and if
you're thinking of writing a real kick-ass game you'll need more.
The most obvious way around segmented memory is to use protected mode.
Unfortunately this has it's problems. For starters it's notoriously
difficult to set up (unless you use an already made extender). You also
have to be extremely careful with your programming in protected mode, one
access to memory you're not supposed to access and up pops the notorious
"General Protection Fault" (remember your first Windows program?). But the
biggest problem with protected mode is the slowdown, many instructions take
a lot longer to execute in protected mode than they do in real mode.
It'd be nice to be able to access all of memory, including xms, in a linear
fashion while still being able to keep the CPU in real mode, and there is!
If you are going to do any serious programming with extended mode then
you'll have to be competent in assembly, but the C++ classes included at
the end of this article will get you started and could even be used for
some simple applications.
Let's say you are accessing memory by using the es:[ebx] pair. In normal
real mode the value of ebx can never be greater than 0xFFFF, if it is and
you try to access memory then a 0Dh interrupt is generated. On my 486 it
sometimes worked ok even though the interrupts were being generated, on the
386's I've tried it on it crashed the system every time. Real memory mode
is a way of simply setting up the CPU so that this interrupt isn't
generated and the correct memory is accessed.
How safe is it? To be honest I have no idea. I can tell you however that I
have successfully used it in one commercial project (da boss told me to)
and countless of my own experimental programs and I've never had a problem
with it. I believe Origin also uses it for their VOODOO memory manager in
Ultima 7.
I first learnt about this technique from a text file written by the demo
group PROGREX. The file is called REALMEM and It's available on
x2ftp.oulu.fi in the /pub/msdos/programming/docs directory.
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD> Technical Stuff <20>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
The CPU maintains a number of lists called "Descriptor Tables". These are
normally used in protected mode, the operating system (or dos extender)
fills this list with a bunch of 8-byte entries, each entry specifies one
block of memory that a program can access. An entry contains info such as
where the block can start and how long it is. There is one list called the
"Global Descriptor Table" which all programs can access. In a multi-tasking
situation each program running also has a "Local Descriptor Table" which is
a block specifying it's own memory area. If you try to access memory
outside the memory specified in these lists then boy are you in trouble!!
In regular CPU real mode the segment limits are set at 64K, so you can
never access outside this area without generating the notorious General
Protection Fault. Real memory mode simply switches to protected mode and
sets up the Global Descriptor Table so that there is one segment starting
at address 0000:0000 and it is 4 gigabytes long. It then switches back to
real mode and the Global Descriptor Table holds. The only other thing we
must do is enable the A20 line so that we can access all the upper memory.
Fortunately HIMEM.SYS has a function to do this for us.
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD> Advantages and Disadvantages <20>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
Real memory mode will not work if EMM386.EXE is installed in the
system, and there's probably a few other memory managers it won't work
with either. The reason for this is that EMM386 puts the system in V86
mode. This is no big drama really, I never have it installed on my
system because it does cause problems with many protected mode
programs, but you'll have to make this clear in your user
documentation.
All CPU instructions will work in their regular real mode ways. This
means that instructions such as movsb will move a byte from ds:[si] to
es:[di], NOT ds:[esi] to es:[edi]. In other words you can't use string
instructions to move memory around xms. This hasn't been too much of a
problem for me, if you use normal optimisation techniques such as
unrolled loops the speed decrease is negligable (although obviously the
code is a tad more clumsy).
(Late change: I *think* you can use string instructions in their 32-bit
modes if you put the address-size prefix byte 67h before the
instruction, although I have yet to test this).
The code supplied with this file must have HIMEM.SYS installed.
HIMEM.SYS contains a number of functions we need, such as finding out
how much XMS memory is present and available, and enabling the A20
line.
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD> The realmem and XMS array C++ Classes <20>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
To help you get started with real memory mode I've written a simple
Borland C++ 3.1 class. The class sets up real memory mode when the
object is created and shuts it down again when it's destroyed. Make
sure you only create one instance of this class, it can be either
global (blech) or local. The realmem class allocates all available XMS,
and trying to allocate another instance will return an ERROR_NOXMS
error.
The 'status' variable is used to determine whether an error has
occurred. These are the values it can contain:
There is also a range of classes you can use to access extended memory
from within your C++ program. They are extremely slow however, so if you
want to do any serious xms programming then I suggest writing your own
routines to access xms directly. The classes included are:
This is the class to set up real memory mode:
realmem
This class contains these variables:
int status; // holds any error messages if they occurr
unsigned XmsKBytes; // number of xms kilobytes allocated
unsigned long XmsBytes; // number of xms bytes allocated
unsigned long XmsStart; // the starting address of available xms memory
These classes are used to manipulate xms memory:
xmschar
xmsint
xmslong
The last 3 classes contain a variable called address which is the absolute
address of the variable. You can specify this address in the constructor if
you like, like this:
realmem rm;
xmschar buffer(rm.XmsStart); // set up a buffer at the start of xms
The classes can then be used as arrays:
unsigned i;
for (i=0; i<64000; i++) buffer[i] = 0; // clear first 64K in the buffer
Feel free to overload some of the other operators for these classes.
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD> TEST.CPP <20>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
Here's a very simple demo program to show you how to use the C++ classes.
First of all it attempts to set up real memory mode. If it can then it uses
the first 64000 bytes in extended memory as a virtual buffer for the
graphics screen. Next it switches over to graphics mode 13h, fills the
virtual bufer with random pixels and copies it to the screen. It then waits
for the user to press a key, switches back to normal mode and cleans up.
Compile this source along with realmem.cpp. Note that this program is
REALLY slow. Like I said, if you want to do anything serious you'll have to
write your own assembly routines to quickly access memory.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include "realmem.h"
void main()
{
realmem xms;
xmschar buffer;
char far * screen;
long i;
// Make sure we switched over to xms ok
if (xms.status != ERROR_OK)
{
printf("REALMEM error # %i occurred! 'Moutta here...",xms.status);
}
else
{
// Point buffer to start of xms and set up screen pointer
buffer.address = xms.XmsStart;
screen = (char far *)0xA0000000;
// Tell the user to wait a tik
printf("Filling virtual buffer with random pixels, one tik...");
// Fill xms virtual buffer with random pixels
for (i=0; i<64000; i++) buffer[i] = random(256);
// Switch to graphics mode 13h
asm mov ax,0x13
asm int 0x10
// Now copy the entire virtual buffer to the graphics screen
for (i=0; i<64000; i++) screen[i] = buffer[i];
// Wait for a keypress then switch back to text mode
getch();
asm mov ax,3
asm int 0x10
}
}
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD> REALMEM.H <20>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
#ifndef __REALMEM__
#define __REALMEM__
#define ERROR_OK 0
#define ERROR_INV86 1
#define ERROR_NODRIVER 2
#define ERROR_WRONGVERSION 3
#define ERROR_NOFUNC 4
#define ERROR_VDISK 5
#define ERROR_A20 6
#define ERROR_NOXMS 7
#define ERROR_NOMEM 8
#define ERROR_NOHANDLES 9
#define ERROR_INVALIDHANDLE 10
#define ERROR_LOCKOVERFLOW 11
#define ERROR_LOCKFAIL 12
#define ERROR_UNKNOWN 13
class realmem
{
public:
realmem();
~realmem();
int status;
unsigned XmsKBytes;
unsigned long XmsBytes;
unsigned long XmsStart;
private:
unsigned XmsHandle;
unsigned long XmsDriver;
int InV86 ();
void LoadGDT ();
int DriverPresent ();
unsigned long GetDriverAddress ();
int WrongVersion ();
unsigned XmsAvail ();
int EnableA20 ();
int DisableA20 ();
unsigned XmsAlloc (unsigned kbytes);
void XmsFree (unsigned handle);
unsigned long XmsLock (unsigned handle);
void XmsUnlock (unsigned handle);
};
class xmschar
{
public:
xmschar();
xmschar(unsigned long addr);
xmschar operator [] (unsigned long offset);
operator = (char value);
operator char();
unsigned long address;
};
class xmsint
{
public:
xmsint();
xmsint(unsigned long addr);
xmsint operator [] (unsigned long offset);
operator = (int value);
operator int();
unsigned long address;
};
class xmslong
{
public:
xmslong();
xmslong(unsigned long addr);
xmslong operator [] (unsigned long offset);
operator = (long value);
operator long();
unsigned long address;
};
#endif // __REALMEM__
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD> REALMEM.CPP <20>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
#include <dos.h>
#include "realmem.h"
#pragma inline
asm .386p
unsigned char
MemoryCode[16]={0,0,0,0,0,0,0,0,0xFF,0xFF,0,0,0,0x92,0xCF,0xFF};
unsigned char Mem48[6];
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> realmem class <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
realmem::realmem()
{
status = ERROR_OK;
// Make sure we are not in V86 mode
if (InV86()) return;
// Load the Global Descriptor Table
asm mov Mem48[0],16
asm mov eax,seg MemoryCode
asm shl eax,4
asm mov bx,offset MemoryCode
asm movzx ebx,bx
asm add eax,ebx
asm mov dword ptr Mem48[2],eax
asm lgdt pword ptr Mem48
asm mov bx,08h
asm push ds
asm cli
asm mov eax,cr0
asm or eax,1
asm mov cr0,eax
asm jmp protection_enabled
protection_enabled:
asm mov gs,bx
asm mov fs,bx
asm mov es,bx
asm mov ds,bx
asm and al,0FEh
asm mov cr0,eax
asm jmp protection_disabled
protection_disabled:
asm sti
asm pop ds
// Make sure an xms driver is present
if (!DriverPresent()) return;
// Get the driver function call address
XmsDriver = GetDriverAddress();
// Make sure the version number is at least 2
if (WrongVersion()) return;
// Find out how much xms is available
XmsKBytes = XmsAvail();
if (status != 0) return;
XmsBytes=(unsigned long)XmsKBytes * 1024L;
// Enable the A20 line
EnableA20();
if (status != 0) return;
// Allocate some xms
XmsHandle = XmsAlloc(XmsKBytes);
if (status != 0) return;
// Lock the block into place and get it's address
XmsStart = XmsLock(XmsHandle);
}
realmem::~realmem()
{
// Unlock the memory block
XmsUnlock(XmsHandle);
// Free the block
XmsFree(XmsHandle);
// Disable the A20 line
DisableA20();
}
// This function tests if the cpu is in V86 mode, if it is then a memory
// manager such as EMM386.EXE is probably running.
int realmem::InV86()
{
unsigned result;
asm mov eax,cr0
asm and ax,1
asm mov word ptr result,ax
if (result) status = ERROR_INV86;
return result;
}
// This function makes sure the HIMEM.SYS in installed.
int realmem::DriverPresent()
{
struct REGPACK regs;
regs.r_ax = 0x4300;
intr(0x2F,&regs);
regs.r_ax &= 0xFF;
if (regs.r_ax != 0x80)
{
status = ERROR_NODRIVER;
return 0;
}
return 1;
}
// This function gets an address used to call HIMEM.SYS functions directly.
unsigned long realmem::GetDriverAddress()
{
unsigned long address;
asm mov ax,0x4310
asm int 0x2F
asm mov ax,es
asm shl eax,16
asm mov ax,bx
asm mov [address+0],bx
asm mov [address+2],es
return address;
}
// This function makes sure that the HIMEM.SYS version is at least 2.0
int realmem::WrongVersion()
{
unsigned long addr = XmsDriver;
char version;
asm mov ah,0
asm xor dx,dx
asm call dword ptr addr
asm mov version,ah
if (version < 2)
{
status = ERROR_WRONGVERSION;
return 1;
}
return 0;
}
// This function returns the amount of free XMS memory available
unsigned realmem::XmsAvail()
{
unsigned long addr = XmsDriver;
unsigned kbytes;
asm mov ah,8
asm xor dx,dx
asm call dword ptr addr
asm mov word ptr kbytes,dx
if (kbytes == 0) status = ERROR_NOXMS;
return kbytes;
}
// This function calls HIMEM.STS to enable the A20 line so we can access
// extended memory.
int realmem::EnableA20()
{
unsigned long addr = XmsDriver;
unsigned a,b;
asm mov ah,5
asm call dword ptr addr
asm mov a,ax
asm mov b,bx
if (a != 1)
{
b &= 0xFF;
switch (b)
{
case 0x80: status = ERROR_NOFUNC; return 0;
case 0x81: status = ERROR_VDISK; return 0;
default: status = ERROR_A20; return 0;
}
}
return 1;
}
// This function calls HIMEM.STS to disable the A20 line.
int realmem::DisableA20()
{
unsigned long addr = XmsDriver;
unsigned a,b;
asm mov ah,6
asm call dword ptr addr
asm mov a,ax
asm mov b,bx
if (a != 1)
{
b &= 0xFF;
switch (b)
{
case 0x80: status = ERROR_NOFUNC; return 0;
case 0x81: status = ERROR_VDISK; return 0;
default: status = ERROR_A20; return 0;
}
}
return 1;
}
// This function calls HIMEM.SYS to allocate a block of extended memory.
unsigned realmem::XmsAlloc(unsigned kbytes)
{
unsigned long addr = XmsDriver;
unsigned result,handle;
unsigned char error;
asm mov ah,9
asm mov dx,kbytes
asm call dword ptr addr
asm mov result,ax
asm mov error,bl
asm mov handle,dx
if (result != 1)
{
switch (error)
{
case 0x80: status = ERROR_NOFUNC; return 0;
case 0x81: status = ERROR_VDISK; return 0;
case 0xA0: status = ERROR_NOMEM; return 0;
case 0xA1: status = ERROR_NOHANDLES; return 0;
default: status = ERROR_UNKNOWN; return 0;
}
}
return handle;
}
// This function calls HIMEM.SYS to lock an allocated block of memory into
// place. This ensures that HIMEM.SYS won't go moving memory around on us.
unsigned long realmem::XmsLock(unsigned handle)
{
unsigned long addr = XmsDriver;
unsigned long address;
unsigned result;
unsigned char error;
asm mov ah,0Ch
asm mov dx,handle
asm call dword ptr addr
asm shl edx,16
asm mov dx,bx
asm mov address,edx
asm mov result,ax
asm mov error,bl
if (result != 1)
{
switch (error)
{
case 0x80: status = ERROR_NOFUNC; break;
case 0x81: status = ERROR_VDISK; break;
case 0xA2: status = ERROR_INVALIDHANDLE; break;
case 0xAC: status = ERROR_LOCKOVERFLOW; break;
case 0xAD: status = ERROR_LOCKFAIL; break;
default: status = ERROR_UNKNOWN; break;
}
}
return address;
}
// This function calls HIMEM.SYS to unlock our allocated memory block.
void realmem::XmsUnlock(unsigned handle)
{
unsigned long addr = XmsDriver;
asm mov ah,0Dh
asm mov dx,handle
asm call dword ptr addr
}
// This function frees our allocated memory block.
void realmem::XmsFree(unsigned handle)
{
unsigned long addr = XmsDriver;
asm mov ah,0Ah
asm mov dx,handle
asm call dword ptr addr
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> xmschar class <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
xmschar::xmschar()
{
address = 0;
};
xmschar::xmschar(unsigned long addr)
{
address = addr;
};
xmschar xmschar::operator [] (unsigned long offset)
{
return xmschar(address + offset);
}
xmschar::operator = (char value)
{
unsigned long addr = address;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov al,value
asm mov fs:[ebx],al
return value;
};
xmschar::operator char()
{
unsigned long addr = address;
char result;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov al,fs:[ebx]
asm mov result,al
return result;
};
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> xmsint class <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
xmsint::xmsint()
{
address = 0;
};
xmsint::xmsint(unsigned long addr)
{
address = addr;
};
xmsint xmsint::operator [] (unsigned long offset)
{
return xmsint(address + offset);
}
xmsint::operator = (int value)
{
unsigned long addr = address;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov ax,value
asm mov fs:[ebx],ax
return value;
};
xmsint::operator int()
{
unsigned long addr = address;
int result;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov ax,fs:[ebx]
asm mov result,ax
return result;
};
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> xmslong class <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
xmslong::xmslong()
{
address = 0;
};
xmslong::xmslong(unsigned long addr)
{
address = addr;
};
xmslong xmslong::operator [] (unsigned long offset)
{
return xmslong(address + offset);
}
xmslong::operator = (long value)
{
unsigned long addr = address;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov eax,value
asm mov fs:[ebx],eax
return value;
};
xmslong::operator long()
{
unsigned long addr = address;
long result;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov eax,fs:[ebx]
asm mov result,eax
return result;
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,7 @@
<html>
<head>
<meta http-equiv="refresh" content="0;url=/Linux.old/sabre/os/articles">
</head>
<body lang="zh-CN">
</body>
</html>

View File

@@ -0,0 +1,756 @@
RealMem Memory Mode
-------------------
By Mark Feldman (pcgpe@geocities.com)
Disclaimer
----------
I assume no responsibility whatsoever for any effect that this file, the
information contained therein or the use thereof has on you, your sanity,
computer, spouse, children, pets or anything else related to you or your
existance. No warranty is provided nor implied with this information.
Introduction
------------
Intel's segmented memory scheme is a pain to use when you've got
large data structures in memory that you want to move around. Being limited
to 640K of base memory is also somewhat restrictive and if you're trying to
write a decent game you'll probably need more.
The most obvious way around segmented memory is to use protected mode.
Unfortunately this has it's problems. For starters it's notoriously
difficult to set up (unless you use an already made extender). You also
have to be extremely careful with your programming in protected mode, one
access to memory you're not supposed to access and up pops the notorious
"General Protection Fault" (remember your first Windows program?).
It'd be nice to be able to access all of memory, including xms, in a linear
fashion while still being able to keep the CPU in real mode, and there is!
If you are going to do any serious programming with realmem mode then
you'll have to be competent in assembly, but the C++ classes included at the
end of this article will get you started and could even be used for some
simple applications.
Let's say you are accessing memory by using the es:[ebx] pair. In normal
real mode the value of ebx can never be greater than 0xFFFF, if it is and
you try to access memory then a 0Dh interrupt is generated. On my 486
it sometimes worked ok even though the interrupts were being generated, on
the 386's I've tried it on it crashed the system every time. Real memory mode
is a way of simply setting up the CPU so that this interrupt isn't generated
and the correct memory is accessed.
How safe is it? To be honest I have no idea. I can tell you however that I
have successfully used it in one commercial project (da boss told me to)
and countless of my own experimental programs and I've never had a
problem with it. I believe Origin also uses it for their VOODOO memory
manager in Ultima 7.
I first learned about this technique from a text file written by the demo
group PROGREX. The file is called REALMEM and It's available on
x2ftp.oulu.fi in the /pub/msdos/programming/docs directory.
Technical Stuff
---------------
The CPU maintains a number of lists called "Descriptor Tables". These are
normally used in protected mode, the operating system (or dos extender)
fills this list with a bunch of 8-byte entries, each entry specifies one
block of memory that a program can access. An entry contains info such
as where the block can start and how long it is. There is one list called
the "Global Descriptor Table" which all programs can access. In a multi-
tasking situation each program running also has a "Local Descriptor Table"
which is a block specifying it's own memory area. If you try to access
memory outside the memory specified in these lists then boy are you in
trouble!!
In regular CPU real mode the segment limits are set at 64K, so you can
never access outside this area without generating a General Protection
Fault. Real memory mode simply switches to protected mode and sets up the
Global Descriptor Table so that there is one segment starting at address
0000:0000 and it is 4 gigabytes long. It then switches back to real mode
and the Global Descriptor Table holds. The only other thing we must do is
enable the A20 line so that we can access all the upper memory.
Fortunately HIMEM.SYS has a function to do this for us.
*** PENTIUM UPDATE ***
It would appear that REALMEM is not as attractive on the Pentium as it was
with earlier machines. In order to use 32-bit addressing with the CPU in
16-bit mode you need to prefix all relevent instructions with the address
size prefix (67h). In general, prefix bytes can only execute in the U pipe
and they cannot pair with anything in the V pipe. In other words, 1-cyle
instructions can take up to 4 *TIMES* as long to execute as they would in
32-bit mode.
The situation could in fact be a lot worse. In 16-bit mode a simple
instruction like MOV DS:[EDI],EAX contains 2 prefixes: an address size
prefix (67h) and a data size prefix (66h). Assuming each prefix takes up
both pipes for a full cycle, this simple instruction would take up to 6
*TIMES* as long to run on a Pentium in real mode than it does in protected
mode. I haven't managed to actually time this yet, but either way things
don't look good for REALMEM on Pentiums.
Advantages and Disadvantages
----------------------------
Real memory mode will not work if EMM386.EXE is installed in the system, and
there's probably a few other memory managers it won't work with either. (THIS
ARTICLE WAS ALSO WRITTEN BEFORE WIN95 WAS RELEASED. REALMEM WON'T RUN IN A DOS
SHELL, THUS FORCING YOUR USERS TO BOOT IN DOS MODE. YECH! I've heard a rumor
that there is a way to get it to work in a dos shell, but so far I haven't been
able to figure out how.) The reason for this is that EMM386 puts the system in
V86 mode. This is no big drama really, I never have it installed on my system
because it does cause problems with many protected mode programs, but you'll
have to make this clear in your user documentation.
All CPU instructions will work in their regular real mode ways. This means
that instructions such as movsb will move a byte from ds:[si] to es:[di],
NOT ds:[esi] to es:[edi]. If you want to use 32-bit addresses for string
instructions then you need to explicitly tell your assembler to add an
address size prefix byte to the front of them, eg:
rep movs word ptr es:[edi], ds:[esi]
This is equivelent to rep movs, but it adds the address size prefix
byte to the instruction.
The code supplied with this file must have HIMEM.SYS installed. HIMEM.SYS
contains a number of functions we need, such as finding out how much XMS
memory is present and available, and enabling the A20 line.
The realmem and XMS array C++ Classes
-------------------------------------
To help you get started with real memory mode I've written a simple Borland
C++ 3.1 class. The class sets up real memory mode when the object is created
and shuts it down again when it's destroyed. Make sure you only create one
instance of this class, it can be either global (blech) or local. The
realmem class allocates all available XMS, and trying to allocate another
instance will return an ERROR_NOXMS error.
The 'status' variable is used to determine whether an error has occurred.
These are the values it can contain:
There is also a range of classes you can use to access extended memory
from within your C++ program. They are extremely slow however, so if you
want to do any serious xms programming then I suggest writing your own
routines to access xms directly. The classes included are:
This is the class to set up real memory mode:
realmem
This class contains these variables:
int status; // holds any error messages if they occurr
unsigned XmsKBytes; // number of xms kilobytes allocated
unsigned long XmsBytes; // number of xms bytes allocated
unsigned long XmsStart; // the starting address of available xms memory
These classes are used to manipulate xms memory:
xmschar
xmsint
xmslong
The last 3 classes contain a variable called address which is the absolute
address of the variable. You can specify this address in the constructor
if you like, like this:
realmem rm;
xmschar buffer(rm.XmsStart); // set up a buffer at the start of xms
The classes can then be used as arrays:
unsigned i;
for (i=0; i&lt;64000; i++) buffer[i] = 0; // clear first 64K in the buffer
Feel free to overload some of the other operators for these classes.
TEST.CPP
--------
Here's a very simple demo program to show you how to use the C++ classes.
First of all it attempts to set up real memory mode. If it can then it uses
the first 64000 bytes in extended memory as a virtual buffer for the
graphics screen. Next it switches over to graphics mode 13h, fills the
virtual bufer with random pixels and copies it to the screen. It then
waits for the user to press a key, switches back to normal mode and cleans
up. Compile this source along with realmem.cpp. Note that this program is
REALLY slow. Like I said, if you want to do anything serious you'll have to
write your own assembly routines to quickly access memory.
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;conio.h&gt;
#include "realmem.h"
void main()
{
realmem xms;
xmschar buffer;
char far * screen;
long i;
// Make sure we switched over to xms ok
if (xms.status != ERROR_OK)
{
printf("REALMEM error # %i occurred! 'Moutta here...",xms.status);
}
else
{
// Point buffer to start of xms and set up screen pointer
buffer.address = xms.XmsStart;
screen = (char far *)0xA0000000;
// Tell the user to wait a tik
printf("Filling virtual buffer with random pixels, one tik...");
// Fill xms virtual buffer with random pixels
for (i=0; i&lt;64000; i++) buffer[i] = random(256);
// Switch to graphics mode 13h
asm mov ax,0x13
asm int 0x10
// Now copy the entire virtual buffer to the graphics screen
for (i=0; i&lt;64000; i++) screen[i] = buffer[i];
// Wait for a keypress then switch back to text mode
getch();
asm mov ax,3
asm int 0x10
}
}
REALMEM.H
---------
#ifndef __REALMEM__
#define __REALMEM__
#define ERROR_OK 0
#define ERROR_INV86 1
#define ERROR_NODRIVER 2
#define ERROR_WRONGVERSION 3
#define ERROR_NOFUNC 4
#define ERROR_VDISK 5
#define ERROR_A20 6
#define ERROR_NOXMS 7
#define ERROR_NOMEM 8
#define ERROR_NOHANDLES 9
#define ERROR_INVALIDHANDLE 10
#define ERROR_LOCKOVERFLOW 11
#define ERROR_LOCKFAIL 12
#define ERROR_UNKNOWN 13
class realmem
{
public:
realmem();
~realmem();
int status;
unsigned XmsKBytes;
unsigned long XmsBytes;
unsigned long XmsStart;
private:
unsigned XmsHandle;
unsigned long XmsDriver;
int InV86 ();
void LoadGDT ();
int DriverPresent ();
unsigned long GetDriverAddress ();
int WrongVersion ();
unsigned XmsAvail ();
int EnableA20 ();
int DisableA20 ();
unsigned XmsAlloc (unsigned kbytes);
void XmsFree (unsigned handle);
unsigned long XmsLock (unsigned handle);
void XmsUnlock (unsigned handle);
};
class xmschar
{
public:
xmschar();
xmschar(unsigned long addr);
xmschar operator [] (unsigned long offset);
operator = (char value);
operator char();
unsigned long address;
};
class xmsint
{
public:
xmsint();
xmsint(unsigned long addr);
xmsint operator [] (unsigned long offset);
operator = (int value);
operator int();
unsigned long address;
};
class xmslong
{
public:
xmslong();
xmslong(unsigned long addr);
xmslong operator [] (unsigned long offset);
operator = (long value);
operator long();
unsigned long address;
};
#endif // __REALMEM__
REALMEM.CPP
-----------
#include &lt;dos.h&gt;
#include "realmem.h"
#pragma inline
asm .386p
unsigned char MemoryCode[16]={0,0,0,0,0,0,0,0,0xFF,0xFF,0,0,0,0x92,0xCF,0xFF};
unsigned char Mem48[6];
// ----------------- realmem class ------------------------
realmem::realmem()
{
status = ERROR_OK;
// Make sure we are not in V86 mode
if (InV86()) return;
// Load the Global Descriptor Table
asm mov Mem48[0],16
asm mov eax,seg MemoryCode
asm shl eax,4
asm mov bx,offset MemoryCode
asm movzx ebx,bx
asm add eax,ebx
asm mov dword ptr Mem48[2],eax
asm lgdt pword ptr Mem48
asm mov bx,08h
asm push ds
asm cli
asm mov eax,cr0
asm or eax,1
asm mov cr0,eax
asm jmp protection_enabled
protection_enabled:
asm mov gs,bx
asm mov fs,bx
asm mov es,bx
asm mov ds,bx
asm and al,0FEh
asm mov cr0,eax
asm jmp protection_disabled
protection_disabled:
asm sti
asm pop ds
// Make sure an xms driver is present
if (!DriverPresent()) return;
// Get the driver function call address
XmsDriver = GetDriverAddress();
// Make sure the version number is at least 2
if (WrongVersion()) return;
// Find out how much xms is available
XmsKBytes = XmsAvail();
if (status != 0) return;
XmsBytes=(unsigned long)XmsKBytes * 1024L;
// Enable the A20 line
EnableA20();
if (status != 0) return;
// Allocate some xms
XmsHandle = XmsAlloc(XmsKBytes);
if (status != 0) return;
// Lock the block into place and get it's address
XmsStart = XmsLock(XmsHandle);
}
realmem::~realmem()
{
// Unlock the memory block
XmsUnlock(XmsHandle);
// Free the block
XmsFree(XmsHandle);
// Disable the A20 line
DisableA20();
}
// This function tests if the cpu is in V86 mode, if it is then a memory
// manager such as EMM386.EXE is probably running.
int realmem::InV86()
{
unsigned result;
asm mov eax,cr0
asm and ax,1
asm mov word ptr result,ax
if (result) status = ERROR_INV86;
return result;
}
// This function makes sure the HIMEM.SYS in installed.
int realmem::DriverPresent()
{
struct REGPACK regs;
regs.r_ax = 0x4300;
intr(0x2F,&regs);
regs.r_ax &= 0xFF;
if (regs.r_ax != 0x80)
{
status = ERROR_NODRIVER;
return 0;
}
return 1;
}
// This function gets an address used to call HIMEM.SYS functions directly.
unsigned long realmem::GetDriverAddress()
{
unsigned long address;
asm mov ax,0x4310
asm int 0x2F
asm mov ax,es
asm shl eax,16
asm mov ax,bx
asm mov [address+0],bx
asm mov [address+2],es
return address;
}
// This function makes sure that the HIMEM.SYS version is at least 2.0
int realmem::WrongVersion()
{
unsigned long addr = XmsDriver;
char version;
asm mov ah,0
asm xor dx,dx
asm call dword ptr addr
asm mov version,ah
if (version &lt; 2)
{
status = ERROR_WRONGVERSION;
return 1;
}
return 0;
}
// This function returns the amount of free XMS memory available
unsigned realmem::XmsAvail()
{
unsigned long addr = XmsDriver;
unsigned kbytes;
asm mov ah,8
asm xor dx,dx
asm call dword ptr addr
asm mov word ptr kbytes,dx
if (kbytes == 0) status = ERROR_NOXMS;
return kbytes;
}
// This function calls HIMEM.STS to enable the A20 line so we can access
// extended memory.
int realmem::EnableA20()
{
unsigned long addr = XmsDriver;
unsigned a,b;
asm mov ah,5
asm call dword ptr addr
asm mov a,ax
asm mov b,bx
if (a != 1)
{
b &= 0xFF;
switch (b)
{
case 0x80: status = ERROR_NOFUNC; return 0;
case 0x81: status = ERROR_VDISK; return 0;
default: status = ERROR_A20; return 0;
}
}
return 1;
}
// This function calls HIMEM.STS to disable the A20 line.
int realmem::DisableA20()
{
unsigned long addr = XmsDriver;
unsigned a,b;
asm mov ah,6
asm call dword ptr addr
asm mov a,ax
asm mov b,bx
if (a != 1)
{
b &= 0xFF;
switch (b)
{
case 0x80: status = ERROR_NOFUNC; return 0;
case 0x81: status = ERROR_VDISK; return 0;
default: status = ERROR_A20; return 0;
}
}
return 1;
}
// This function calls HIMEM.SYS to allocate a block of extended memory.
unsigned realmem::XmsAlloc(unsigned kbytes)
{
unsigned long addr = XmsDriver;
unsigned result,handle;
unsigned char error;
asm mov ah,9
asm mov dx,kbytes
asm call dword ptr addr
asm mov result,ax
asm mov error,bl
asm mov handle,dx
if (result != 1)
{
switch (error)
{
case 0x80: status = ERROR_NOFUNC; return 0;
case 0x81: status = ERROR_VDISK; return 0;
case 0xA0: status = ERROR_NOMEM; return 0;
case 0xA1: status = ERROR_NOHANDLES; return 0;
default: status = ERROR_UNKNOWN; return 0;
}
}
return handle;
}
// This function calls HIMEM.SYS to lock an allocated block of memory into
// place. This ensures that HIMEM.SYS won't go moving memory around on us.
unsigned long realmem::XmsLock(unsigned handle)
{
unsigned long addr = XmsDriver;
unsigned long address;
unsigned result;
unsigned char error;
asm mov ah,0Ch
asm mov dx,handle
asm call dword ptr addr
asm shl edx,16
asm mov dx,bx
asm mov address,edx
asm mov result,ax
asm mov error,bl
if (result != 1)
{
switch (error)
{
case 0x80: status = ERROR_NOFUNC; break;
case 0x81: status = ERROR_VDISK; break;
case 0xA2: status = ERROR_INVALIDHANDLE; break;
case 0xAC: status = ERROR_LOCKOVERFLOW; break;
case 0xAD: status = ERROR_LOCKFAIL; break;
default: status = ERROR_UNKNOWN; break;
}
}
return address;
}
// This function calls HIMEM.SYS to unlock our allocated memory block.
void realmem::XmsUnlock(unsigned handle)
{
unsigned long addr = XmsDriver;
asm mov ah,0Dh
asm mov dx,handle
asm call dword ptr addr
}
// This function frees our allocated memory block.
void realmem::XmsFree(unsigned handle)
{
unsigned long addr = XmsDriver;
asm mov ah,0Ah
asm mov dx,handle
asm call dword ptr addr
}
// ----------------- xmschar class ------------------------
xmschar::xmschar()
{
address = 0;
};
xmschar::xmschar(unsigned long addr)
{
address = addr;
};
xmschar xmschar::operator [] (unsigned long offset)
{
return xmschar(address + offset);
}
xmschar::operator = (char value)
{
unsigned long addr = address;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov al,value
asm mov fs:[ebx],al
return value;
};
xmschar::operator char()
{
unsigned long addr = address;
char result;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov al,fs:[ebx]
asm mov result,al
return result;
};
// ----------------- xmsint class ------------------------
xmsint::xmsint()
{
address = 0;
};
xmsint::xmsint(unsigned long addr)
{
address = addr;
};
xmsint xmsint::operator [] (unsigned long offset)
{
return xmsint(address + offset);
}
xmsint::operator = (int value)
{
unsigned long addr = address;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov ax,value
asm mov fs:[ebx],ax
return value;
};
xmsint::operator int()
{
unsigned long addr = address;
int result;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov ax,fs:[ebx]
asm mov result,ax
return result;
};
// ----------------- xmslong class ------------------------
xmslong::xmslong()
{
address = 0;
};
xmslong::xmslong(unsigned long addr)
{
address = addr;
};
xmslong xmslong::operator [] (unsigned long offset)
{
return xmslong(address + offset);
}
xmslong::operator = (long value)
{
unsigned long addr = address;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov eax,value
asm mov fs:[ebx],eax
return value;
};
xmslong::operator long()
{
unsigned long addr = address;
long result;
asm xor ax,ax
asm mov fs,ax
asm mov ebx,addr
asm mov eax,fs:[ebx]
asm mov result,eax
return result;
};

View File

@@ -0,0 +1,557 @@
<HTML><HEAD>
<TITLE>Segment Registers: Real mode vs. Protected mode</TITLE>
</HEAD>
<h2>Segment Registers: Real mode vs. Protected mode</h2><p>
Content by: <A HREF="mailto:johnfine@erols.com">johnfine@erols.com</A><br>
HTML formatting by: volunteer_needed<p>
This page discusses the major difference between real mode and protected mode,
which is the behavior of segment registers.<p>
<UL>
<LI><A href="#simple">The simple but wrong version</A>
<LI><A href="#true">The way it really works</A>
<LI><A href="#why">Examples (why you can't trust the simple version)</A>
<UL>
<LI><A href="#switch2">Switching to pmode</A>
<LI><A href="#switchF">Switching from pmode</A>
<LI><A href="#flat">Flat real mode</A>
<LI><A href="#reset">CPU reset</A>
<LI><A href="#change">Changing the GDT or LDT</A>
</UL>
<LI>Related topics
<UL>
<LI><A href="#wraparound">segment wraparound</A>
<LI><A href="#null">NULL Selector</A>
</UL>
</UL>
<P>
<H3><A name="simple">The simple but wrong version</A></H3>
Most memory accesses implicitly or explicitly use a segment register.
If you don't understand that aspect of it yet, please look at
<A href="#basics">Segment register basics</A>.<p>
In real mode the CPU shifts the segment register value left by four places
(multiplying it by 16) and adds the 16 bit offset to get a 20 bit physical
address.<p>
In pmode the value in a segment register is not a segment number. It is
a "selector". Bit 2 of the selector tells the CPU whether to use the
GDT or LDT. Bits 3 to 15 of the selector index to one of the descriptors
in the GDT or IDT.<p>
The CPU adds the 16-bit or 32-bit offset (from the address portion of
the instruction) to the "base" value from the descriptor. The result
is a 32-bit "linear" address.<p>
The descriptor also contains a "limit" (length minus one) for the
segment. The CPU will generate a fault if you attempt to access
beyond the limit of a segment.<p>
Bits 0 and 1 of the selector are known as the "RPL". The current
priviledge level is known as the "CPL". Those values are checked
against the descriptor priviledge "DPL" to see if the access
should be permitted.<p>
<H3><A name="true">The way it really works</A></H3>
<A NAME="hidden"></A>
Each segment register is really four registers:
<UL>
<LI>A selector register
<LI>A base register
<LI>A limit register
<LI>An attribute register
</UL><p>
In all modes, every access to memory that uses a segment register uses
the base, limit, and attribute portions of the segment register and
does not use the selector portion.<p>
Every direct access to a segment register (PUSHing it on the stack,
MOVing it to a general register etc.) uses only the selector
portion. The base, limit, and attribute portions are either very
hard or impossible to read (depending on CPU type). They are
often called the "hidden" part of the segment register because
they are so hard to read.<p>
Intel documentation refers to the hidden part of the segment
register as a "descriptor cache". This name obscures the
actual behavior of the "hidden" part.<p>
<A NAME="ModeWriteSeg"></A>
In real mode (or V86 mode), when you
<A HREF="#WriteSeg">write any 16-bit value to a segment register</A>,
the value you write goes into the selector and 16 times that value
goes into the base. The limit and attribute are not changed.<p>
In pmode, any
<A HREF="#WriteSeg">write to a segment register</A>
causes a descriptor to
be fetched from the GDT or LDT and unpacked into the base, limit
and attribute portion of the segment register. (Special
exception for the <A href="#null">NULL Selector</A>).<p>
When the CPU <A href="#switch2">switchs</A>
between real mode and pmode, the segment
registers do not automatically change. The selectors still
contain the exact bit pattern that was loaded into them in
the previous mode. The hidden parts still contain the values
they contained before, so the segment registers can still be
used to access whatever segments they refered to before the
switch.<p>
When the CPU switches between V86 mode and pmode it updates
all the segment registers to have values which are consistent
with the new mode. This is the reason that tricks like
<A href="#flat">flat real mode</A>
don't work in V86 mode.<p>
<FONT size="-1">
When the CPU switches to "System Manangement Mode" from any other
mode it stores the entire contents of the segment register (hidden
as well as selectors) in an SMM work area. On some CPU's, this is
the only way to read the hidden part of the segment registers.<p>
When the CPU switches from SMM to any other mode it reads the entire
contents of the segment registers from that work area. It does not
perform many of the consistency checks that would occur under other
circumstances, so it is possible to create something like flat real
mode for V86, but there are many further problems.<p>
</FONT>
<H3><A name="why">Examples (why you can't trust the simple version)</A></H3>
In many situations the selectors will be "out of sync" with the base,
limit and attribute values.<p>
If you think of the hidden part as a "descriptor cache", you will
find it very hard to work with these situations. With a cache,
you would have no idea when the values will pop back into sync, or
what you can/can't safely do while they are out of sync.<p>
Knowing that they are a
<A HREF="#hidden">hidden portion</A>
of the segment registers,
means that you know they will change as described above, when
you write to the segment registers. Clearly you need to have
interrupts disabled whenever the segment registers are out
of sync (except for stable cases like flat real mode),
because any interrupt routine might save and "restore" a
segment register and that "restored" segment register will
have only the same selector as was saved, not the same hidden
part.<p>
<H3><A name="switch2">Switching to pmode</A></H3>
Switching to pmode is real easy. Just<p>
<PRE>
mov eax, cr0
inc ax
mov cr0, eax
</PRE>
It is also real hard, because when you first switch to pmode, the slightest
mistake will cause your CPU to "triple fault". That makes your system
reboot. It doesn't tell you much about what you did wrong, just that you
did SOMETHING wrong.<p>
When you first switch to pmode, your CS register still has the same base,
limit and attribute values it had before. Don't worry about the fact
that the CS selector is now "incorrect" for pmode. As long as you don't
have any interrupts or far CALLs, the CS selector doesn't matter. You
can execute lots of code, even do near JMPs and CALLs and RETs, all
without loading a pmode compatible CS selector.<p>
Similarly, your data, stack, etc. segment registers retain their base,
limit and attributes. This brings me to a great debugging aid for
mode switching routines:<p>
I will assume that your early pmode code does not use GS, and that
your display is in text mode and that your display regen buffer is at B8000.
(Make the obvious change if that last item is wrong). Put the
following code well before you switch to pmode:<p>
<PRE>
mov ax, 0B800h
mov gs, ax
mov byte [gs:0], '1' ;<A href="#syntax">NASM Syntax</A>
</PRE>
Now take lines like these:<p>
<PRE>
mov byte [gs:2], '2'
mov byte [gs:4], '3'
mov byte [gs:6], '4'
etc.
</PRE>
and distribute them through the following code. DO NOT WORRY about the
fact that GS has a selector that is incompatible with pmode. DO NOT
load a pmode compatible selector into GS (until you are sure your GDT
etc. work well enough that you probably don't need this debugging aid
anymore anyway).<p>
When your program triple faults, watch the display carefully. The
sequence of characters will be visible for a moment (even after the
triple fault), so you can see how far your code got before it died.<p>
Before you can change any segment register within pmode, you need to
have a valid GDT. You can do the LGDT instruction before or after
switching to pmode. It is safe before switching because the value
in the GDT register is ignored in real mode; It doesn't affect
the loading of segment registers; It doesn't even need to be valid.
Doing the LGDT after switching to pmode is safe because all your
segment registers still have valid base, limit and attributes, so
memory references (like loading the GDT psuedo-descriptor into the
GDT register) are still OK.<p>
Many people misunderstand the extra level of indirection involved
in pointing to the GDT. The LGDT instruction encodes an address
(usual memory addressing rules). That address is the offset
(within the indicated or implicit segment) of the psuedo-descriptor.
The psuedo-descriptor in turn contains the address of the GDT.
THAT address is a linear address and is not based on the
contents of any segment register.<p>
In all my examples I put that psuedo-descriptor in the same
location as the GDT itself. This is possible because the CPU
never uses the first eight bytes of the GDT. (See
<A href="#null">NULL Selector</A>).<p>
If your code is a DOS program, or is loaded in some other form in which you
cannot know linear addresses until run time, then you must patch the
psuedo-descriptor to get the correct linear address.<p>
<PRE>
xor eax, eax
mov ax, ds
shl eax, 4 ;eax = linear address of data segment
add [GDT+2], eax ;patch psuedo-descriptor
lgdt [GDT]
. . .
GDT dw GDT_length - 1
dd GDT
dw 0
;First descriptor goes here
</PRE>
Before enabling or using any interrupts, remember to set up a pmode compatible
Interrupt Descriptor Table. Like LGDT, LIDT may be executed before or after
entering pmode; However, unlike the GDT, the IDT does affect the CPU within
real mode. Interrupts should remain disabled from before the earlier of
the LIDT or the switch to pmode, until after the later of the LIDT or loading
pmode compatible selectors into all segment registers touched (including save/
restore) by any interrupt.<p>
<A name="cr0fast">WARNING:</A> A 386 needs a very tiny delay (any instruction would be
more than enough) after switching to pmode, before it can correctly load a selector
into a segment register. In one version of my
<A HREF="#flat">switch to flat real mode</A>
I had the selector value in a general register before switching to pmode, and
the very first instruction after switching to pmode was a fast instruction to
MOV that selector to a segment register. Depending on instruction alignment,
it could corrupt the hidden part of the segment register (on a 386 only).
You can safely write to a segment register with the very first instruction
after switching to pmode if it is a slow instruction like a POP or a far JMP,
but not if it is a fast instruction like "MOV DS,BX".
Normally you wouldn't even notice this problem because it is more natural
to move the selector to a register right before you move it to the
segment register.<p>
<H3><A name="switchF">Switching from pmode</A></H3>
In switching from pmode, you usually need to reload your segment registers
twice.<p>
Before switching from pmode you must put real mode compatible values
in the limit and attribute part of each segment register. You
cannot change the limit or attribute while you are in real mode, so
you must set the values you want before you exit pmode.<p>
After switching from pmode you must put real mode compatible values
in the segment selectors. Immediately after you exit pmode the
segment registers still have the selector and base values that were
loaded when you fixed the limit and attribute values (as described
above). As with the switch TO pmode, you can use the segment
registers with those base values to execute code and access data
as long as you want after switching. However, you should understand
the consequences of having the selector and base values out of sync.
As soon as any interrupt service routine saves and restores a
segment register, the pmode base will be lost and the base will
become 16 times the selector.<p>
There is a default size bit in each of the CS and SS attribute parts.
It comes from bit 54 in the descriptor (that is the D_BIG bit, for
those using my
<A HREF="descript.htm#gdtinc">gdt.inc</A>
definitions). In the CS register the
default size has a major effect. It sets the default operand and
address size to 16 or 32. If you exited to real mode with that bit
still set, you would get very strange results (and not very useful,
because the BIOS wouldn't be usable and the IVT would still
function in 16-bit real mode).<p>
If you exit to real mode with the default size bit still set in
SS (a common mistake), you get a much subtler problem. AFAIK,
the sole effect of the default size bit in SS is to cause all
implicit uses of SP to use ESP instead. Implicit uses of SP
are PUSH, POP, CALL, RET, INT, etc.; If the upper half of
ESP is cleared, you won't immediately notice that it is
being used. However, there are several things that programs
might do that will crash as a result of that SS bit. My
favorite is:<p>
<PRE>
xor sp, sp
push something
</PRE>
Normally that predecrements sp to FFFE and writes the value there.
You may not like the results if the value gets written to ss:FFFFFFFE
instead.<p>
<H3><A name="flat">Flat real mode</A></H3>
Flat real mode, AKA "unreal mode" is the mode you get to if you return
to real mode from pmode leaving a 4GB limit in some or all of
ds, es, fs, gs, ss.<p>
Flat real mode has the major advantage over PMODE, that it is compatible
with the BIOS and DOS. In fact, if you are running HIMEM.SYS without
EMM386.SYS you are probably already in flat real mode.<p>
Getting into flat real mode is pretty easy:<p>
<PRE>
cli ;Don't allow interrupts during this
push ds ;Save real mode selectors
push es
xor eax, eax ;Patch the GDT psuedo-descriptor
mov ax, ds ; (assuming ds points the segment containing the GDT)
shl eax, 4
add [GDT+2], eax
lgdt [GDT] ;Load the GDT
mov eax, cr0 ;Switch to pmode
inc ax
mov cr0, eax
mov bx, flat_data ;Our only pmode selector
mov ds, bx ;Install 4Gb limits <A HREF="#cr0fast">(warning)</A>
mov es, bx
dec ax ;Switch back to real mode
mov cr0, eax
pop es ;Restore real mode selectors
pop ds
sti ;Notice, I never needed to change the CS while in pmode.
. . .
%include "<A HREF="descript.htm#gdtinc">gdtn.inc</A>"
GDT start_gdt
flat_data desc 0, 0xFFFFF, D_DATA+D_WRITE+D_BIG_LIM
end_gdt
</PRE>
Most people seem to build their GDT's in hex rather than using macros such as
those defined in <A HREF="descript.htm#gdtinc">gdtn.inc</A>.
If you really want to do it that way, the four lines above that create
the GDT could be replaced by:
<PRE>
GDT dw 0xF, GDT, 0, 0, 0xFFFF, 0, 0x9200, 0x8F
flat_data equ 8
</PRE>
I think the hex version is unreadable and error prone.<p>
A common fallacy about flat real mode is that it is incompatible with programs
that rely on
<A name="wraparound">segment wraparound</A>.<p>
On an 8086, offsets within a segment are truely and consistently 16 bits.
If you access ds:[bx+si+7000h] and bx=7000h and si=7000h, then you are
asking for 7000h*3 which is 15000h; But of course you actually get
offset 5000h because offsets are 16 bits.<p>
Similarly, if you access a word at offset [0FFFFh], the low byte of
the word will come from offset 0FFFFh and the high byte will come
from offset 0, not from the byte that physically follows the low
byte.<p>
On a 386+, there is only a slight difference. In 16-bit addressing,
7000h + 7000h + 7000h still equals 5000h; However, accesing the
word at offset FFFFh now gives you a protection violation rather
than a wraparound.<p>
In flat real mode, there is another slight difference. In 16-bit addressing,
7000h + 7000h + 7000h STILL equals 5000h (despite nonsense to the contrary
that I have seen written); However, accesing the
word at offset FFFFh now gives you the full word at that location rather
than either a wraparound or a protection violation.<p>
In the unlikely event that some program relied on the protection
violation, it would be incompatible with flat real mode. If it
relied on the wraparound, it wouldn't run correctly on a 386+
anyway.<p>
Flat real mode is incompatible with V86 mode, so if you are running
EMM386 etc. which put your DOS session in V86 mode, then you can't
switch to flat real mode. Also certain BIOS interrupts and other
programs, that temporarily switch in and out of pmode, will trash
your extended limits and take you back out of flat real mode.<p>
Another common fallacy is that 32-bit addressing modes are possible
in flat real mode but impossible in ordinary real mode. In fact, the
modes themselves are always valid; Only the limits change. In
real mode you could do:<p>
<PRE>
mov al, [ecx]
</PRE>
I have even found reasons to do that in ordinary real mode. It allows
you to use the cx register for an address (as long as the high bits of
ecx are clear). It just doesn't allow that address to be above 64K.<p>
Flat real mode doesn't allow any new addressing modes; It just allows
larger offsets (that otherwise would have caused protection violations)
in the 32-bit addressing modes that were already available (though rarely
useful) in real mode.<p>
<H3><A name="reset">CPU reset</A></H3>
You probably don't care unless you are designing a BIOS or a motherboard,
but a 386+ CPU starts up with a strange value in its CS registers.<p>
The EIP is FFF0 and the CS selector is F000, so it would seem that it
starts at physical address FFFF0, just like an 8086 (which starts at
FFFF:0).<p>
Actually the hidden base portion of the CS is FFFF0000, not F0000, so
the actual starting address is FFFFFFF0.<p>
As long as you don't write to the CS register in any way, that base value
will remain and you can do near JMP, CALL and RET, to execute code in
FFFFxxxx. As soon as you do the first far JMP, CALL, INT, etc. the base
value will be back in sync with the selector and you can't access above
10FFEFh again until you switch to pmode.<p>
<H3><A name="change">Changing the GDT or LDT</A></H3>
While you are in pmode, you may have reason to execute another LGDT instruction,
or to change the contents of a descriptor.<p>
These actions do not affect the base, limit or attributes of any segment
register. The new descriptor values will only be used when you write to
a segment register.<p>
In this sense the "descriptor cache" acts very unlike a cache and very
unlike the "TLB" which is the cache for the paging system.<p>
When you change an entry in a page directory or page table, it is very
hard to predict how long you can safely use the old "stale" entry in the
TLB. In general you can't. When you write a new CR3 value (ananogous
to doing another LGDT) the CPU flushes the TLB and immediately gets rid
of all the "stale" values. When you do a new LGDT the "descriptor cache"
isn't touched.<p>
<H3><A name="basics">Segment register basics</A></H3>
Most memory accesses implicitly or explicitly use a segment register.
<UL>
<LI>To fetch instructions the CPU unconditionally uses the CS register.
<LI>For most data accesses the CPU uses the DS register by default.
<LI>When implicitly using DI or EDI in a string instruction, the
CPU unconditionally uses the ES register.
<LI>When implicitly using SP or ESP as a stack pointer the CPU
unconditionally uses the SS register.
<LI>When using BP, EBP, or ESP explicitly as a base register the
CPU uses the SS register by default.
</UL>
In the cases in which DS or SS would be used by default,
the choice of segment register may be overridden by a segment prefix.<p>
The CPU first computes an offset. In 16-bit addressing the CPU
<A HREF="#wraparound">truncates the offset to the low 16 bits</A>.
The CPU then adds the offset to the segment-base provided by the
selected segment register. This yields the
<A HREF="paging.htm#linear">linear address</A> that will be accessed.
<H3><A NAME="WriteSeg">Writes to a segment register</A></H3>
When I refer to "writing to a segment register", I mean any action that
puts a 16-bit value into a segment register.<p>
The obvious example is something like:<PRE> MOV DS,AX </PRE>
However the same rules apply to many other situations, including:
<UL>
<LI>POP to a segment register.
<LI>FAR JMP or CALL puts a value in CS.
<LI>IRET or FAR RET puts a value in CS.
<LI>Both hardware and software interrupts put a value in CS.
<LI>A ring transition puts a value in both SS and CS.
<LI>A task switch loads all the segment registers from a TSS.
</UL>
All of these writes to segment registers behave
<A HREF="#ModeWriteSeg">as described above</A>
according to the current mode of the CPU.<p>
When you are writing code that crosses mode boundaries, you should
consider all possible segment register writes. Things like saving
and restoring a segment register, that are normally transparent,
may change the
<A HREF="#hidden">hidden part</A> of the segment register, when
executed after a mode change.
<H3><A name="null">NULL Selector</A></H3>
In pmode, a selector value of zero gets special handling.<p>
The general rule would say that a selector of zero refers to the
zeroth descriptor in the GDT. But this does not occur.<p>
The CPU never refers to the zeroth descriptor in the GDT.
The
<A HREF="descript.htm#null">first 8 bytes of the GDT</A>
are available for any use you like and
their contents will not affect the behavior of the GDT.<p>
When you
<A HREF="#WriteSeg">write</A>
a zero to DS, ES, FS or GS in pmode the CPU sets the attributes
of that segment register to mark it unusable, but does not generate
a fault at that time. If you then use that segment, you will
get a fault.<p>
The purpose of the NULL Selector is to cover situations in which
you do not have valid contents for a segment register. If you
left old invalid contents in the register and simply didn't
use that register, you might run into trouble when some routine
saves and restores the register. On restoring the register, it
might generate a fault.<p>
The NULL selector may be freely saved and restored without
causing a fault.<p>
Trying to put the NULL selector into the CS register will generate
a fault on the instruction that attempts that. It will NOT load the
NULL selector and generate the fault on the next attempt to fetch an instruction.
I am not sure whether putting NULL in SS generates an immediate fault
or waits until the next use of SS.
<H3><A name="syntax">NASM Syntax</A></H3>
All the example here use NASM Syntax.<p>
If you want to use MASM or TASM etc. I hope the meaning of the
NASM instructions is clear enough that you can translate to
the assembler you choose.<p>
NASM is free, so you may be better off downloading a copy of NASM
and using that, rather than trying to translate syntax.<p>
I have made several enhancements to NASM. You can find both my
enhanced version of NASM and links to standard NASM on my web page:<p>
<A HREF="http://www.erols.com/johnfine/">http://www.erols.com/johnfine/</A><p>

View File

@@ -0,0 +1,163 @@
;VooDoo init!
;This will setup the system into a special mode. This code will crash if
; EMM386, Windoze and other PMODE software is loaded. You must also
; enable the a20 thru XMS if himem.sys is loaded.
; once complete the following will happen in real mode.
; - mov ax,[ebx] is legal now
; - code will still have a 64K limit range (IP still used not EIP)
;This is presented to learn from - I really suggest not using this technique
;as it's old and crappy. Gamez to use this : Ultima 7. This was the day
;I hated the PC, but things got better as DPMI and VCPI were introduced
.386p
vd_desc struc
lmt dw 0
bsl dw 0
bsm db 0
typel db 0
typeh db 0
bsh db 0
vd_desc ENDS
.code
cli ;No ints
xor eax,eax
mov ax,cs
mov ds,ax
shl eax,4
mov ds:[oldcs.bsl],ax
mov ds:[oldds.bsl],ax
shr eax,16
mov ds:[oldcs.bsm],al
mov ds:[oldds.bsm],al
mov eax,code32
shl eax,4
add dword ptr ds:gdt32[2],eax
add dword ptr ds:idt32[2],eax
mov ds:[scode32.bsl],ax
mov ds:[sdata32.bsl],ax
shr eax,16
mov ds:[scode32.bsm],al
mov ds:[sdata32.bsm],al
mov ds:[scode32.bsh],ah
mov ds:[sdata32.bsh],ah ;All mem ptr are calculated
lgdt fword ptr ds:gdt32 ;Load the GDT
mov eax,cr0
or al,1
mov cr0,eax ;Hop to Prot. mode
db 0eah
dw main32,8 ;far jmp 08:main
realmode16:
lidt fword ptr ds:defidt ;load the IDT
mov eax,cr0
and al,0feh
mov cr0,eax ;Kill the P-mode bit
db 0eah
dw realmode,Code_start ;Another far jmp
realmode:
mov ebx,10000h ;
mov ax,[ebx] ;Oh my god this shit works !!
;Jmp here to your own code !!! VERY IMPORTANT !
mov ax,4c00h
int 21h
defidt dw 3ffh,0,0 ;The normal IDT
ENDS
code32 segment para public use32
assume cs:code32,ds:code32
main32:
lidt fword ptr cs:idt32 ;The PROT MODE IDT
mov ax,10h
mov ds,ax
mov es,ax
mov fs,ax
mov ss,ax ;Just loading some segs
mov ax,30h
mov gs,ax ;This is the videoseg
xor esp,esp
mov esp,offset stackend ;What could that be ???
call enablea20
; jmp gone ;Testing exc6
exit:
mov ax,20h
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax ;Loading the segs back
db 0eah
dw realmode16,0,18h ;Far jmp 18:realmode16
gone:
db 10 dup(0feh)
idt32 dw 187h,idt,0
gdt32 dw 224,dummy,0
dummy vd_desc <>
scode32 vd_desc <0ffffh,0,0,10011010b,11001111b,0>
sdata32 vd_desc <0ffffh,0,0,10010010b,11001111b,0>
oldcs vd_desc <0ffffh,0,0,10011010b,10000000b,0>
oldds vd_desc <0ffffh,0,0,10010010b,10000000b,0>
bios vd_desc <0ffffh,0,0,10010010b,11001111b,0>
vseg vd_desc <0ffffh,8000h,0bh,10010010b,11001111b,0>
idt dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
dw inter,8,8e00h,0
inter:
jmp exit
enablea20:
call enablea201
jnz short enablea20done
mov al,0d1h
out 64h,al
call enablea201
jnz short enablea20done
mov al,0dfh
out 60h,al
enablea201:
mov ecx,20000h
enablea201l:
jmp short $+2
in al,64h
test al,2
loopnz enablea201l
enablea20done:
ret
pile db 400 dup(?)
stackend:
ENDS
END