DOS/4G and DOS/4GW FAQ: Interrupt Handling
[Previous Section] *
[Index of FAQ] *
[Next Section]
-
My program installs a hardware interrupt handler.
The program runs fine under plain DOS but crashes under DPMI. What might
be happening?
-
Why can't I get the WATCOM library signal()
function to work with Ctrl-Break?
-
I'm writing a hardware interrupt handler.
Can you give me some tips?
-
Does DOS/4G support VESA Int 10h Function 4F00?
-
Is it true that the Watcom function _disable()
only disables interrupts for the process, and not the processor?
-
Is there any way around this, for example, using an asm 'cli' call?
-
Can all published DOS interrupts be emulated with protected-mode code?
-
So, what DOS interrupts are handled by protected-mode code?
1.
My program installs a hardware interrupt handler.
The program runs fine under plain DOS but crashes under DPMI. What might
be happening?
Have you locked your handler in 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 out 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 Interrupt 31h/600h
(Lock Linear Region) to lock an address range in memory.
If you fail to lock all of your code and data, your program may run
under DOS/4G, but fail under the DOS/4G Virtual Memory Manager or under
another DPMI host, such as Windows or OS/2
You should also lock the code and data of a mouse callback function.
2.
Why can't I get the WATCOM library signal()
function to work with Ctrl-Break?
The signal function in some versions of the WATCOM C/C++ library had a
bug that causes signal(SIGBREAK) not to work. Calling signal(SIGBREAK)
installs a null interrupt handler for Crtl-Break (Interrupt 1Bh),
instead of installing your handler.
You can work around this problem by hooking Interrupt 1Bh directly.
3.
I'm writing a hardware interrupt handler.
Can you give me some tips?
* It's more like handling interrupts in real mode than not.
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.
* Minimize the amount of time spent in your interrupt handlers.
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 you handler returns.
In general, it's a good idea to handle interrupts as quickly as
possible.
* To support high (greater than 1000 per second) interrupt rates,
minimize the amount of time spent in the DOS extender as a result of
interrupt-related mode switches by installing separate real mode and
protected mode handlers.
If you use a passup interrupt handler, so that interrupts received in
real mode are resignaled 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.
* If you can't just set a flag and return, STI.
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 - and then STI, to give
priority to other hardware interrupts.
* If you STI, you should CLI.
Because some DPMI hosts virtualize the interrupt flag, if you do an
STI in your handler, 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
might 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).
* Add a reentrancy check.
If your handler doesn't complete its work by the time the next
interrupt is signaled, then the 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 STI,
obviously).
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. 'systems 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.
* Switch to your own stack.
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 to the
handler, and switch back before returning.
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.
Note that switching stacks by itself won't prevent transfer stack
overflows of the kind described above.
4.
Does DOS/4G support VESA Int 10h Function 4F00?
No, DOS/4G doesn't directly support VESA Int 10h Function 4F00h.
However, DOS/4G provides tools that you can easily make these VESA
interrupt calls. Any of the VESA extensions to int 10h will require that
you translate your program's linear addresses to
real mode addresses and back again. You may also need to allocate
data buffers or control blocks in the lower 1MB. DOS/4G has APIs to do
these things, so it will be really easy to do.
5.
Is it true that the Watcom function _disable()
only disables interrupts for the process, and not the processor?
That depends upon where you are running your DOS/4GW program.
The _disable() function just does a 'cli' instruction. How that
affects your program/process/processor depends upon what operating
system you are using.
In plain DOS, _disable() does indeed disable all interrupts, because
there is only one 'process'.
If you are running in Win3.1 or Win95, the 'cli' is virtualized, and
only interrupts for your process are disabled. Windows provides a int 2fh
call you can make to truly disable interrupts., although I don't have my
interrupt list here. You could also write a VXD.
In NT, the 'cli' is virtualized, and only interrupts for your
process are disabled. You would have to write a NT device driver
to get interrupts disabled totally.
OS/2 is similar to NT in this respect.
6.
Is there any way around this, for example, using an asm 'cli' call?
Since _disable() just results in a 'cli', using an asm 'cli' won't.
You would have to do something different for each operating
system you are running in.
7.
Can all published DOS interrupts be emulated with protected-mode code?
While a protected-mode system could be coded this way, we don't do
this in our DOS extenders. Rather almost all DOS and BIOS interrupts
are resignaled in real mode, after switching from protected mode.
Of course, there is some copying of data and translation of pointers
to make it work transparently, but we chose to code our systems to
work this way to maximize compatibility.
By reflecting most interrupts to real mode, we insure that your
DOS-extended program will be maximally compatible with existing
software and systems. For example, if a TSR hooks the disk reading
interrupt, because we reflect the disk reading interrupts to real
mode, we insure that the TSR hook will be called.
The downside to this approach is that every DOS and BIOS call causes
a switch to real mode and back again. Although most computers can
perform such switching at a rate of tens of thousands of switches
per second, the switching time can have a noticible impact on the
performance of programs which heavily use DOS or BIOS services.
For example, a program which reads a disk file by calling DOS to read
one byte at a time will likely run much slower in protected mode than
in real mode. The solution is simple: buffer your disk reads to read,
for example, 512 bytes per call to DOS. With such buffering, the
extra time taken to perform mode switches will hardly be noticible.
Of course, you are free to do emulate DOS interrupt handling in your
own code!
8.
So, what DOS interrupts are handled by protected-mode code?
Basically, interrupts which allocate or free memory and interrupts
which deal with installing interrupt handlers are handled by our
DOS extenders. Of course, even though a memory allocation interrupt
is handled by protected-mode code, that doesn't mean that a real-mode
interrupt will not be signalled -- because the protected-mode code
may need to allocate DOS memory.
|