📄 dos4gwqa.gml
字号:
.*
.ix '&dos4gprd' 'asynchronous interrupts'
.np
Under &dos4gprd, there is a convenient way to handle asynchronous
interrupts and an efficient way to handle them.
.np
Because your CPU may be in either protected mode (when 32-bit code is
executing) or real mode (a DOS or BIOS call) when a hardware interrupt
comes in, you have to be prepared to handle interrupts in either mode.
Otherwise, you may miss interrupts.
.np
You can handle both real-mode and protected-mode interrupts with a
single handler, if 1) the interrupt is in the auto-passup range, 8 to
2Eh; and 2) you install a handler with INT 21h/25h or _dos_setvect();
3) you do not install a handler for the same interrupt using any other
mechanism. &dos4gprd will route both protected-mode interrupts and
real-mode interrupts to your protected-mode handler. This is the
convenient way.
.np
The efficient way is to install separate real-mode and protected-mode
handlers for your interrupt, so your CPU won't need to do unnecessary
mode switches. Writing a real-mode handler is tricky; all you can
reasonably expect to do is save data in a buffer and IRET. Your
protected-mode code can periodically check the buffer and process any
queued data. (Remember, protected-mode code can access data and
execute code in low memory, but real-mode code can't access data or
execute code in extended memory.)
.np
For performance, it doesn't matter how you install the real-mode
handler, but we recommend the DPMI function INT 31h/201h for
portability.
.np
It does matter how you install the protected-mode handler. You can't
install it directly into the IDT, because a DPMI provider must
distinguish between interrupts and exceptions and maintain separate
handler chains. Installing with INT 31h/205h is the recommended way to
install your protected-mode handler for both performance and
portability.
.np
If you install a protected-mode handler with INT 21h/25h, both
interrupts and exceptions will be funneled to your handler, to mimic
DOS. Since DPMI exception handlers and interrupt handlers are called
with different stack frames, &dos4gprd executes a layer of code to
cover these differences up; the same layer is used to support the
DOS/4G API (not part of &dos4gprd). This layer is the reason that
hooking with INT 21h/25h is less efficient than hooking with INT
31h/205h.
.*
.keep
.note 4b. Handling asynchronous interrupts in the second IRQ range.
.*
.ix '&dos4gprd' 'int 70h-77h'
.np
Because the second IRQ range (normally INTs 70h-77h) is outside the
&dos4gprd auto-passup range (8-2Eh, excluding 21h) you may not handle
these interrupts with a single handler, as described above (the
"convenient" method). You must install separate real-mode and
protected-mode handlers (the "efficient" method).
.np
DOS/4G does allow you to specify additional passup interrupts,
however.
.*
.keep
.note 4c. Asynchronous interrupt handlers and DPMI.
.*
.np
.ix '&dos4gprd' 'locking memory'
The DPMI specification requires that all code and data referenced by a
hardware interrupt handler MUST be locked at interrupt time. A DPMI
virtual memory manager can use the DOS file system to swap pages of
memory to and from the disk; because DOS is not reentrant, a DPMI host
is not required to be able to handle page faults during asynchronous
interrupts. Use INT 31h/600h (Lock Linear Region) to lock an address
range in memory.
.np
If you fail to lock all of your code and data, your program may run
under &dos4gprd, but fail under the &dos4gprd Virtual Memory Manager
or under another DPMI host such as Windows or OS/2.
.np
You should also lock the code and data of a mouse callback function.
.*
.keep
.note 4d. &company signal() function and Ctrl-Break.
.*
.ix '&dos4gprd' 'Ctrl-Break handling'
.np
In earlier versions of the &company C/C++ library, there was a bug
that caused signal(SIGBREAK) not to work.
Calling signal(SIGBREAK) did not actually install an interrupt handler
for Ctrl-Break (INT 1Bh), so Ctrl-Break would terminate the
application rather than invoking the signal handler.
.np
With these earlier versions of the library, you could work around this
problem by hooking INT 1Bh directly.
With release 10.0, this problem has been fixed.
.*
.keep
.note 4e. More tips on writing hardware interrupt handlers.
.*
.ix '&dos4gprd' 'interrupt handlers'
.in -0.3i
.begbull
.bull
It's more like handling interrupts in real mode than not.
.np
The same problems arise when writing hardware interrupt handlers for
protected mode as arise for real mode. We assume you know how to write
real-mode handlers; if our suggestions don't seem clear, you might
want to brush up on real-mode interrupt programming.
.bull
Minimize the amount of time spent in your interrupt handlers.
.np
When your interrupt handlers are called, interrupts are disabled. This
means that no other system tasks can be performed until you enable
interrupts (an STI instruction) or until your handler returns. In
general, it's a good idea to handle interrupts as quickly as possible.
.bull
Minimize the amount of time spent in the DOS extender by installing
separate real-mode and protected-mode handlers.
.np
If you use a passup interrupt handler, so that interrupts received in
real mode are resignalled in protected mode by the extender, your
application has to switch from real mode to protected mode to real
mode once per interrupt. Mode switching is a time-consuming process,
and interrupts are disabled during a mode switch. Therefore, if you're
concerned about performance, you should install separate handlers for
real-mode and protected-mode interrupts, eliminating the mode switch.
.bull
If you can't just set a flag and return, enable interrupts (STI).
.np
Handlers that do more than just set a flag or store data in a buffer
should re-enable interrupts as soon as it's safe to do so. In other
words, save your registers on the stack, establish your addressing
conventions, switch stacks if you're going to &mdash. and then enable
interrupts (STI), to give priority to other hardware interrupts.
.bull
If you enable interrupts (STI), you should disable interrupts (CLI).
.np
Because some DPMI hosts virtualize the interrupt flag, if you do an
STI in your handler, you should be sure to do a CLI before you return.
(CLI, then switch back to the original stack if you switched away,
then restore registers, then IRET.) If you don't do this, the IRET
will not necessarily restore the previous interrupt flag state, and
your program may crash. This is a difference from real-mode
programming, and it tends to show up as a problem when you try running
your program in a Windows or OS/2 DOS box for the first time (but not
before).
.bull
Add a reentrancy check.
.np
If your handler doesn't complete its work by the time the next
interrupt is signalled, then interrupts can quickly nest to the point
of overflowing the transfer stack. This is a design flaw in your
program, not in the DOS extender; a real-mode DOS program can have
exactly the same behavior. If you can conceive of a situation where
your interrupt handler can be called again before the first instance
returns, you need to code in a reentrancy check of some sort (before
you switch stacks and enable interrupts (STI), obviously).
.np
Remember that interrupts can take different amounts of time to execute
on different machines; the CPU manufacturer, CPU speed, speed of
memory accesses, and CMOS settings (e.g. "system BIOS shadowing") can
all affect performance in subtle ways. We recommend you program
defensively and always check for unexpected reentry, to avoid transfer
stack overflows.
.bull
Switch to your own stack.
.np
Interrupt handlers are called on a stack that typically has only a
small amount of stack available (512 bytes or less). If you need to
use more stack than this, you have to switch to your own stack on
entry into the handler, and switch back before returning.
.np
If you want to use C run-time library functions, which are compiled
for flat memory model (SS == DS, and the base of CS == the base of
DS), you need to switch back to a stack in the flat data segment
first.
.np
Note that switching stacks by itself won't prevent transfer stack
overflows of the kind described above.
.endbull
.in +0.3i
.*
.endnote
.*
.section Memory Management
.*
.begnote $setptnt 2
.*
.note 5a. Using the realloc() function.
.*
.ix '&dos4gprd' 'realloc'
.np
In versions of &company C/C++ prior to 9.5b, there was a bug in the
library implementation of realloc() under &dos4gprd and &dos4gprd
Professional. This bug was corrected by &company in the 9.5b release.
.*
.keep
.note 5b. Using all of physical memory.
.*
.ix '&dos4gprd' 'memory addressability'
.np
&dos4gprd Professional is currently limited to 64 MB of physical
memory. We do not expect to be able to fix this problem for at least
six months. If you need more than 64 MB of memory, you must use
virtual memory.
.*
.endnote
.*
.section DOS, BIOS, and Mouse Services
.*
.begnote $setptnt 2
.*
.note 6a. Speeding up file I/O.
.*
.ix '&dos4gprd' 'DOS file I/O'
.ix 'DOS file I/O'
.np
The best way to speed up DOS file I/O in &dos4gprd is to write large
blocks (up to 65535 bytes, or the largest number that will fit in a
16-bit int) at a time from a buffer in low memory. In this case,
&dos4gprd has to copy the least amount of data and make the fewest
number of DOS calls in order to process the I/O request.
.np
.ix 'setvbuf'
Low memory is allocated through INT 31h/0100h, Allocate DOS Memory
Block. You can convert the real-mode segment address returned by INT
31h/0100h to a pointer (suitable for passing to setvbuf()) by shifting
it left four bits.
.*
.keep
.note 6b. Spawning.
.*
.ix '&dos4gprd' 'spawning'
.np
It is possible to spawn one &dos4gprd application from another.
However, two copies of the DOS extender will be loaded into memory.
DOS/4G supports loading of multiple programs atop a single extender,
as well as DLLs.
.*
.keep
.note 6c. Mouse callbacks.
.*
.ix '&dos4gprd' 'mouse support'
.np
&dos4gprd Professional now supports the INT 31h interface for managing
real-mode callbacks. However, you don't need to bother with them for
their single most important application: mouse callback functions.
Just register your protected-mode mouse callback function as you would
in real mode, by issuing INT 33h/0Ch with the event mask in CX and the
function address in ES:EDX, and your function will work as expected.
.np
Because a mouse callback function is called asynchronously, the same
locking requirement exists for a mouse callback function as for a
hardware interrupt handler. See (4c) above.
.*
.keep
.note 6d. VESA support.
.*
.ix '&dos4gprd' 'VESA support'
.np
While &dos4gprd automatically handles most INT 10h functions so that
you can you can issue them from protected mode, it does not translate
the INT 10h VESA extensions. The workaround is to use INT 31h/300h
(Simulate Real-Mode Interrupt).
.*
.endnote
.*
.section Virtual Memory
.*
.begnote $setptnt 2
.*
.note 7a. Testing for the presence of VMM.
.*
.ix '&dos4gprd' 'VMM'
.ix 'VMM'
.ix 'virtual memory manager'
.np
INT 31h/400h returns a value (BX, bit 2) that tells if virtual memory
is available. Under a DPMI host such as Windows 3.1, this will be the
host's virtual memory manager, not &dos4gprd's.
.np
You can test for the presence of a DOS/4G-family DOS extender with INT
31h/0A00h, with a pointer to the null-terminated string "RATIONAL
DOS/4G" in DS:ESI. If the function returns with carry clear, a
DOS/4G-family extender is running.
.*
.keep
.note 7b. Reserving memory for a spawned application.
.*
.ix 'DOS4GVM' 'DELETESWAP'
.ix 'DELETESWAP virtual memory option'
.np
If you spawn one &dos4gprd application from another, you should set
the DELETESWAP configuration option (i.e., SET DOS4GVM=deleteswap) so
that the two applications don't try to use the same swap file. You
should also set the MAXMEM option low enough so that the parent
application doesn't take all available physical memory; memory that's
been reserved by the parent application is not available to the child
application.
.*
.keep
.note 7c. Instability under VMM.
.*
.ix '&dos4gprd' 'VMM instability'
.np
A program that hooks hardware interrupts, and works fine without VMM
but crashes sporadically with it, probably needs to lock the code and
data for its hardware interrupt handlers down in memory. &dos4gprd
does not support page faults during hardware interrupts, because DOS
services may not be available at that time. See (4c) and (6c) above.
.np
Memory can be locked down with INT 31h/600h (Lock Linear Region).
.*
.keep
.note 7d. Running out of memory with a huge virtual address space.
.*
.ix '&dos4gprd' 'out of memory'
.ix 'DOS4GVM' 'VIRTUALSIZE'
.ix 'VIRTUALSIZE virtual memory option'
.np
Because &dos4gprd has to create page tables to describe your virtual
address space, we recommend that you set your VIRTUALSIZE parameter
just large enough to accommodate your program. If you set your
VIRTUALSIZE to 4 GB, the physical memory occupied by the page tables
will be 4 MB, and that memory will not be available to &dos4gprd..
.*
.keep
.note 7e. Reducing the size of the swap file.
.*
.ix 'DOS4GVM' 'VIRTUALSIZE'
.ix 'VIRTUALSIZE virtual memory option'
.ix 'DOS4GVM' 'SWAPMIN'
.ix 'SWAPMIN virtual memory option'
.ix 'DOS4GVM' 'SWAPINC'
.ix 'SWAPINC virtual memory option'
.np
&dos4gprd will normally create a swap file equal to your VIRTUALSIZE
setting, for efficiency. However, if you set the SWAPMIN parameter to
a size (in KB), &dos4gprd will start with a swap file of that size,
and will grow the swap file when it has to. The SWAPINC value (default
64 KB) controls the incremental size by which the swap file will grow.
.*
.keep
.note 7f. Deleting the swap file.
.*
.ix 'DOS4GVM' 'DELETESWAP'
.ix 'DELETESWAP virtual memory option'
.ix 'DOS4GVM' 'SWAPNAME'
.ix 'SWAPNAME virtual memory option'
.np
The DELETESWAP option has two effects: telling &dos4gprd to delete the
swap file when it exits, and causing &dos4gprd to provide a unique
swap file name if an explicit SWAPNAME setting was not given.
.np
DELETESWAP is required if one &dos4gprd application is to spawn
another; see (7b) above.
.*
.keep
.note 7g. Improving demand-load performance of large static arrays.
.*
.ix '&dos4gprd' 'demand-loading'
.np
&dos4gprd demand-loading feature normally cuts the load time of a
large program drastically. However, if your program has large amounts
of global, zero-initialized data (storage class BSS), the &company
startup code will explicitly zero it (version 9.5a or earlier).
Because the zeroing operation touches every page of the data, the
benefits of demand-loading are lost.
.np
Demand loading can be made fast again by taking advantage of the fact
that &dos4gprd automatically zeroes pages of BSS data as they are
loaded. You can make this change yourself by inserting a few lines
into the startup routine, assembling it (MASM 6.0 will work), and
listing the modified object module first when you link your program.
.np
Here are the changes for
.fi \watcom\src\startup\386\cstart3r.asm
(startup module from the C/C++ 9.5 compiler, library using register
calling conventions). You can modify the workaround easily for other
&company compilers:
.code begin
... ; cstart3r.asm, circa line 332
; end of _BSS segment (start of STACK)
mov ecx,offset DGROUP:_end
; start of _BSS segment
mov edi,offset DGROUP:_edata
;-------------------------------; RSI OPTIMIZATION
mov eax, edi ; minimize _BSS initialization loop
or eax, 0FFFh ; compute address of first page after
inc eax ; start of _BSS
cmp eax, ecx ; if _BSS extends onto that page,
jae allzero ; then we can rely on the loader
mov ecx, eax ; zeroing the remaining pages
allzero: ;
;-------------------------------; END RSI OPTIMIZATION
sub ecx,edi ; calc # of bytes in _BSS segment
mov dl,cl ; save bottom 2 bits of count in edx
shr ecx,2 ; calc # of dwords
sub eax,eax ; zero the _BSS segment
rep stosd ; ...
mov cl,dl ; get bottom 2 bits of count
and cl,3 ; ...
rep stosb ; ...
...
.code end
.np
Note that the 9.5b and later versions of the &company C library
already contain this enhancement.
.*
.keep
.note 7h. How should I configure VM for best performance?
.*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -