add directory study
This commit is contained in:
BIN
study/sabre/os/files/ProtectedMode/386INTEL.zip
Normal file
BIN
study/sabre/os/files/ProtectedMode/386INTEL.zip
Normal file
Binary file not shown.
BIN
study/sabre/os/files/ProtectedMode/386P_101.ZIP
Normal file
BIN
study/sabre/os/files/ProtectedMode/386P_101.ZIP
Normal file
Binary file not shown.
BIN
study/sabre/os/files/ProtectedMode/386P_200.ZIP
Normal file
BIN
study/sabre/os/files/ProtectedMode/386P_200.ZIP
Normal file
Binary file not shown.
1316
study/sabre/os/files/ProtectedMode/386P_200.txt
Normal file
1316
study/sabre/os/files/ProtectedMode/386P_200.txt
Normal file
File diff suppressed because it is too large
Load Diff
8656
study/sabre/os/files/ProtectedMode/3_OPCODES.txt
Normal file
8656
study/sabre/os/files/ProtectedMode/3_OPCODES.txt
Normal file
File diff suppressed because it is too large
Load Diff
1252
study/sabre/os/files/ProtectedMode/3_PART1.txt
Normal file
1252
study/sabre/os/files/ProtectedMode/3_PART1.txt
Normal file
File diff suppressed because it is too large
Load Diff
6184
study/sabre/os/files/ProtectedMode/3_PART2.txt
Normal file
6184
study/sabre/os/files/ProtectedMode/3_PART2.txt
Normal file
File diff suppressed because it is too large
Load Diff
1856
study/sabre/os/files/ProtectedMode/3_PART3.txt
Normal file
1856
study/sabre/os/files/ProtectedMode/3_PART3.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
study/sabre/os/files/ProtectedMode/DOS32V3B.ZIP
Normal file
BIN
study/sabre/os/files/ProtectedMode/DOS32V3B.ZIP
Normal file
Binary file not shown.
7722
study/sabre/os/files/ProtectedMode/DPMI09.txt
Normal file
7722
study/sabre/os/files/ProtectedMode/DPMI09.txt
Normal file
File diff suppressed because it is too large
Load Diff
4057
study/sabre/os/files/ProtectedMode/DPMI1.0.txt
Normal file
4057
study/sabre/os/files/ProtectedMode/DPMI1.0.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
100
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/api/310509.html
Normal file
100
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/api/310509.html
Normal 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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
141
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/api/errors.html
Normal file
141
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/api/errors.html
Normal 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 |
125
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.1.html
Normal file
125
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.1.html
Normal 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>
|
||||
|
||||
|
||||
98
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.2.html
Normal file
98
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.2.html
Normal 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>
|
||||
|
||||
|
||||
209
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.3.html
Normal file
209
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.3.html
Normal 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>
|
||||
|
||||
|
||||
46
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.4.1.html
Normal file
46
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.4.1.html
Normal 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>
|
||||
|
||||
|
||||
55
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.4.2.html
Normal file
55
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.4.2.html
Normal 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>
|
||||
|
||||
|
||||
28
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.4.3.html
Normal file
28
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.4.3.html
Normal 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>
|
||||
|
||||
|
||||
35
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.4.html
Normal file
35
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.4.html
Normal 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>
|
||||
|
||||
|
||||
308
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.5.html
Normal file
308
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.5.html
Normal 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>
|
||||
|
||||
|
||||
231
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.6.html
Normal file
231
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.6.html
Normal 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>
|
||||
|
||||
|
||||
190
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.7.html
Normal file
190
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.7.html
Normal 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>
|
||||
|
||||
|
||||
100
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.8.html
Normal file
100
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.8.html
Normal 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>
|
||||
|
||||
|
||||
111
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.html
Normal file
111
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch4.html
Normal 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>
|
||||
|
||||
|
||||
85
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch5.a.html
Normal file
85
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch5.a.html
Normal 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>
|
||||
|
||||
|
||||
105
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch5.g.html
Normal file
105
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch5.g.html
Normal 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>
|
||||
|
||||
|
||||
36
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch5.html
Normal file
36
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch5.html
Normal 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>
|
||||
|
||||
|
||||
100
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch5.n.html
Normal file
100
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/ch5.n.html
Normal 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>
|
||||
|
||||
|
||||
@@ -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> </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>
|
||||
|
||||
|
||||
BIN
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/dpmi-specs.tar.gz
Normal file
BIN
study/sabre/os/files/ProtectedMode/DPMI1.0Spec/dpmi-specs.tar.gz
Normal file
Binary file not shown.
47
study/sabre/os/files/ProtectedMode/DPMI1.htm
Normal file
47
study/sabre/os/files/ProtectedMode/DPMI1.htm
Normal 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>
|
||||
|
||||
|
||||
BIN
study/sabre/os/files/ProtectedMode/FLAT.ZIP
Normal file
BIN
study/sabre/os/files/ProtectedMode/FLAT.ZIP
Normal file
Binary file not shown.
100
study/sabre/os/files/ProtectedMode/FLAT.txt
Normal file
100
study/sabre/os/files/ProtectedMode/FLAT.txt
Normal 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)
|
||||
414
study/sabre/os/files/ProtectedMode/GEMMIS.txt
Normal file
414
study/sabre/os/files/ProtectedMode/GEMMIS.txt
Normal 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
|
||||
86
study/sabre/os/files/ProtectedMode/GEM_SPEC.txt
Normal file
86
study/sabre/os/files/ProtectedMode/GEM_SPEC.txt
Normal 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
|
||||
156
study/sabre/os/files/ProtectedMode/LOADALL.txt
Normal file
156
study/sabre/os/files/ProtectedMode/LOADALL.txt
Normal 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
|
||||
BIN
study/sabre/os/files/ProtectedMode/PM-9-94.ZIP
Normal file
BIN
study/sabre/os/files/ProtectedMode/PM-9-94.ZIP
Normal file
Binary file not shown.
1453
study/sabre/os/files/ProtectedMode/PMODE-OS.txt
Normal file
1453
study/sabre/os/files/ProtectedMode/PMODE-OS.txt
Normal file
File diff suppressed because it is too large
Load Diff
318
study/sabre/os/files/ProtectedMode/PMODE.txt
Normal file
318
study/sabre/os/files/ProtectedMode/PMODE.txt
Normal 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.
|
||||
|
||||
328
study/sabre/os/files/ProtectedMode/PMODECrashCourse.txt
Normal file
328
study/sabre/os/files/ProtectedMode/PMODECrashCourse.txt
Normal 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.
|
||||
|
||||
|
||||
329
study/sabre/os/files/ProtectedMode/PMODEDetailedApproach.txt
Normal file
329
study/sabre/os/files/ProtectedMode/PMODEDetailedApproach.txt
Normal 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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
67
study/sabre/os/files/ProtectedMode/PMODES.txt
Normal file
67
study/sabre/os/files/ProtectedMode/PMODES.txt
Normal 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...
|
||||
|
||||
2021
study/sabre/os/files/ProtectedMode/PMODEW.txt
Normal file
2021
study/sabre/os/files/ProtectedMode/PMODEW.txt
Normal file
File diff suppressed because it is too large
Load Diff
1329
study/sabre/os/files/ProtectedMode/PMTUT.txt
Normal file
1329
study/sabre/os/files/ProtectedMode/PMTUT.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
study/sabre/os/files/ProtectedMode/PMTUT02.zip
Normal file
BIN
study/sabre/os/files/ProtectedMode/PMTUT02.zip
Normal file
Binary file not shown.
BIN
study/sabre/os/files/ProtectedMode/PMW121.ZIP
Normal file
BIN
study/sabre/os/files/ProtectedMode/PMW121.ZIP
Normal file
Binary file not shown.
155
study/sabre/os/files/ProtectedMode/SIMPL_PM.asm
Normal file
155
study/sabre/os/files/ProtectedMode/SIMPL_PM.asm
Normal 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
|
||||
737
study/sabre/os/files/ProtectedMode/Unreal.txt
Normal file
737
study/sabre/os/files/ProtectedMode/Unreal.txt
Normal 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,®s);
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
1013
study/sabre/os/files/ProtectedMode/VCPI.txt
Normal file
1013
study/sabre/os/files/ProtectedMode/VCPI.txt
Normal file
File diff suppressed because it is too large
Load Diff
1003
study/sabre/os/files/ProtectedMode/VDMA_API.txt
Normal file
1003
study/sabre/os/files/ProtectedMode/VDMA_API.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
study/sabre/os/files/ProtectedMode/dpmispec1.0.pdf
Normal file
BIN
study/sabre/os/files/ProtectedMode/dpmispec1.0.pdf
Normal file
Binary file not shown.
7
study/sabre/os/files/ProtectedMode/index.html
Normal file
7
study/sabre/os/files/ProtectedMode/index.html
Normal 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>
|
||||
756
study/sabre/os/files/ProtectedMode/realmem.txt
Normal file
756
study/sabre/os/files/ProtectedMode/realmem.txt
Normal 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<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 <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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 <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];
|
||||
|
||||
|
||||
// ----------------- 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,®s);
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
// ----------------- 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;
|
||||
};
|
||||
|
||||
557
study/sabre/os/files/ProtectedMode/segments.html
Normal file
557
study/sabre/os/files/ProtectedMode/segments.html
Normal 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>
|
||||
163
study/sabre/os/files/ProtectedMode/voodoo.asm
Normal file
163
study/sabre/os/files/ProtectedMode/voodoo.asm
Normal 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
|
||||
Reference in New Issue
Block a user