www.delorie.com/djgpp/v2faq/faq152.html | search |
| Previous | Next | Up | Top |
First, some background. Hardware interrupts can occur when the processor is either in real mode (like when your program calls some DOS service) or in protected mode. When your program runs under a DPMI host, hardware interrupts are caught by the DPMI host and passed to protected mode first; only if unhandled, they are then reflected to real mode. Therefore, in DPMI mode you can get away with installing only a protected-mode handler. However, if the interrupts happen at a high frequency (say, more than 10 KHz), then the overhead of the interrupt reflection from real to protected mode might be too painful, and you should consider installing a real-mode interrupt handler in addition to the protected-mode one. Such a real-mode handler will be called before the interrupt gets to the DPMI host, and handle the interrupt entirely in real mode, so it must be written in assembly and located in conventional memory (below the 1MB mark). If you need to hook an interrupt with both PM and RM handlers, you must hook the PM interrupt first, then the RM one (because hooking the PM interrupt modifies the RM one). Also, you should know that some DPMI hosts don't allow you to hook the RM interrupt (CWSDPMI does), and some call both handlers, no matter in what mode the interrupt arrived (CWSDPMI will only call one of them); the only way to be sure is to try.
To install a protected-mode interrupt handler, you do this:
__dpmi_get_protected_mode_interrupt_vector
and save the structure it returns (to restore the previous handler address before your program exits).
__dpmi_lock_linear_region
. Failure to lock memory
accessed during the interrupt handling will cause your program to crash. Alternatively, you could set the _CRT0_FLAG_LOCK_MEMORY
bit in the _crt0_startup_flags
variable, or
disable virtual memory by using CWSDPR0.
__dpmi_set_protected_mode_interrupt_vector
and pass it a pointer to a __dpmi_paddr
structure filled with the value returned by _my_cs()
in
the selector
field and the address of your function in the offset32
field.
_go32_dpmi_XXX
functions instead of the bare-bones API wrappers whose names start with __dpmi_.
Specifically:
_go32_dpmi_get_protected_mode_interrupt_vector.
This function puts the selector and offset of the specified interrupt vector into the pm_selector
and
pm_offset
fields of the structure pointed to by its second argument. This data should be saved and later passed to _go32_dpmi_set_protected_mode_interrupt_vector
to restore
the vector on exit.
_go32_dpmi_allocate_iret_wrapper,
passing it the address of your function in the pm_offset
field and the value returned by _my_cs()
in the
pm_selector
field. The pm_offset
field will get replaced with the address of the wrapper function which is a small assembler function that handles everything an interrupt
handler should do on entry and before exit (and what the code GCC generates for an ordinary C function doesn't include); the effect is similar to using the interrupt
or
_interrupt
keyword in some DOS-based compilers.
_go32_dpmi_chain_protected_mode_interrupt_vector.
This will set up a wrapper function which, when called, will call
your handler, then jump to the previous handler after your handler returns. Put the address of your handler into the pm_offset
field and the value of _my_cs
into the
pm_selector
field of the _go32_dpmi_seginfo
structure and pass a pointer to it to this function.
_go32_dpmi_set_protected_mode_interrupt_vector
with the address of the _go32_dpmi_seginfo
structure you got from either
_go32_dpmi_allocate_iret_wrapper
or _go32_dpmi_chain_protected_mode_interrupt_vector.
The problem with writing handlers in C as above is that the wrappers' code and data aren't locked, and in practice you can't lock all of memory the handler itself uses, either. Thus, this approach is
generally unsuitable for production-quality software and should be used only when the program is known not to page (i.e., only the physical memory is used). You might consider disabling virtual
memory to make sure your program doesn't page. To accomplish this, either set the _CRT0_FLAG_LOCK_MEMORY
bit in the _crt0_startup_flags
variable, or use CWSDPR0 or PMODE/DJ
as your DPMI host. In fact, using one of these methods is the recommended way of debugging the first versions of a program that hooks hardware interrupts; only after you are sure that your basic
machinery works should you move to testing it in a setup when paging might happen.
Note that _CRT0_FLAG_LOCK_MEMORY
is only recommended for small programs that run on a machine where enough physical memory is always available, because the startup code currently doesn't
test if memory is indeed locked, and if there's not enough physical memory installed to page in all of the memory your program needs, you can end up with unlocked or partially unlocked memory, which
will crash your program. If you want to make sure all memory is locked, use a DPMI server which disables paging.
To install a real-mode interrupt handler, you do this:
__dpmi_get_real_mode_interrupt_vector
and save the structure it returns (to restore the previous handler address before your program exits).
__dpmi_allocate_dos_memory
and put the code of your handler there with the dosmemput
function. (You could also call one of the
functions which allocate a real-mode call-back, but these will cause a mode switch on every interrupt, which you want to avoid; otherwise there is no point in installing a real-mode handler, right?)
__dpmi_allocate_dos_memory
returned into a __dpmi_raddr
structure (the lower 4 bits into offset16
field, the rest into
segment
field), then call __dpmi_set_real_mode_interrupt_vector.
The DPMI spec says that 3 software interrupts are special, in that they also get reflected to a protected-mode handler. These interrupts are: 1Ch (the timer tick interrupt), 23h (Keyboard Break interrupt), and 24h (Critical Error interrupt). This means that, to catch these interrupts, you need to install a protected-mode handler only. Unlike hardware interrupts, it doesn't make sense to install dual RM and PM handlers for these software interrupts. In particular, Windows will call both RM and PM handlers if you install both, so you effectively wind up handling the same interrupt twice.
For examples of installing and using hardware interrupt handlers, see the sources of the Allegro library, the sample code written by Bill Currie, the Sound Blaster interrupt-driven functions, the mkkbd package, and the libhw library, described under sample DJGPP packages. Alaric B. Williams has written a tutorial on interrupt handling.
The file src/libc/crt0/crt0.S in the DJGPP library sources, djlsrNNN.zip, is one example of the subtleties involved with installing a real-mode interrupt handler.
webmaster donations bookstore | delorie software privacy |
Copyright ⌐ 1998 by Eli Zaretskii | Updated Sep 1998 |
You can help support this site by visiting the advertisers that sponsor it! (only once each, though)