![]() |
![]() |
||
|
[Book Overview]
[Prev]
[Next] | |||
You will notice that my example code earlier in this chapter dealt only with software interrupts. Hardware interrupts can work, but there are some complications. The problems are associated with how interrupts are mapped and the difference in treatment of interrupts in Protected and Real modes.
The issue is very complicated and it behooves us to start with the handling of hardware interrupts from the point of view of the XT; that is, with an 8088 or 8086 CPU.
IRQ 0-7
The PC model XTs are based upon the 8086 CPU and have a hardware interrupt controller chip that allows eight devices to interrupt the CPU. That is,the chip has eight inputs, labelled IRQ0 to IRQ7 and one output labelled IRQ (Interrupt ReQuest) that feeds into the maskable interrupt pin of the CPU.
A flag named IF (Interrupt Flag) enables this IRQ input with the STI instruction or disables it with the CLI instruction (see page 33).
The interrupt controller chip can be, and is, programmed to map IRQ0 to IRQ7 to any group of eight entries in the IVT or IDT (look ahead to page 268 for the relationship between the IVT and IDT) (see page 185 for an introduction to the interrupt controller chip).
The XT maps IRQ0 through 7 to entries 8 to 0Fh in the IVT. Thus if you were to access these by software interrupt, you would execute "INT 8" to "INT 0Fh".
The IBM model AT, based upon the 80286 CPU, introduced more hardware interrupts,by cascading a second interrupt controller chip, as shown in Figure 10.1.
Figure 1: AT Hardware Interrupts
At power-on, the interrupt controller chips are programmed to map to certain entries in the IVT. When an interrupt arrives, IRQ is forwarded to the CPU,and the CPU then interrogates the controller chip, which passes the interrupt number "n" to the CPU over the data bus. The CPU then looks up that entry in the IVT and goes to the interrupt service routine.
Exception handling conflict
When the CPU is operating in Real mode, INT-0 is what is called a processorexception; that is, an interrupt generated by the CPU itself, not by the program or by external hardware. Ditto for INT-1.
I have shown INT-6 and -7 as reserved, which is the case for the XT. However on the AT, the 286 CPU uses these for "invalid op-code" and "device not available", respectively. Again, these are exceptions.
There is a very serious problem with this arrangement. With the 286 and 386,Intel uses the first 16 entries of the interrupt table and now we must refer to the IDT as exceptions when the CPU is operating in Protected mode.
However, the hardware interrupts IRQ0 through 7 are mapped into INT-8 to -Fh. Quandary how is this conflict resolved?
Windows remapping of vectors
Windows (and OS/2) map IRQ0 through Fh elsewhere in the IDT, at INT-50h to -5Fh. Obviously, these entries would point to the same routines as before,but even so, there is room here for trouble.
You might deduce from this that if you wanted to hook the original INT-8,you should instead hook INT-50h. This is valid, but only to a certain extent.Windows can be in Protected or V86 mode at the time of interrupt, and in the latter case we have to go back to the IVT in the V86 virtual machine currently active.
Therefore, we (may) actually have to hook two (or more) vectors. Headache!
Windows' Standard Mode Hardware Interrupts
Somewhere earlier in the book I promised not to mention Standard mode again,as it's history almost. Maybe in some third world countries it's all the rage. However, the following is extremely interesting, and I've put it in for the education it gives us about the warp and weave of interrupt handling.
INT-51h
What I have for you here is a useful program that hooks INT-9, the keyboard hardware interrupt except to illustrate how Standard mode works, I have hooked INT-51h!
This keyboard hook can be very useful for filtering whatever comes from the keyboard before Windows has a chance to see it. Note that INT-51h is invoked every time a key is pressed or released, with bit-7 of the scancode distinguishing which.
POSTMESSAGE()
Hardware interrupts are somewhat more delicate than their software cousins for example you can't call Windows functions from them with one exception: POSTMESSAGE(). Microsoft especially made sure that this would work from the hardware interrupt level, so that a hardware interrupt service routine can signal a Windows application.
There is a particular problem with these hardware interrupts, due to the way they are mapped. With Windows in Standard mode I have shown on the previous pages that the keyboard interrupt maps to INT-51h in the IDT, with certain qualifications, and this example code hooks that vector. This point is elaborated on a little later.
Incidentally, if you need to know which mode Windows is running in, there is a function that will do that for you; GETWINFLAGS(). I haven't shown the call to GETWINFLAGS() in the example below, but in a practical program you could include it.
A skeleton hardware interrupt handler
What follows is just an extraction of the bare essentials to get a hardware interrupt working the flesh can go on later.
Listing 3: A Skeleton Hardware Interrupt Handler
Hooking/ unhooking the vector
A WM_CREATE message is sent when the window is first created, so this is a convenient time to hook the vector. Therefore a call to INSTALLINT() is included.
Similarly, upon exit it is necessary to unhook the vector, otherwise Windows will crash. Unhooking on receipt of the WM_DESTROY message is most appropriate.This code simply uses INT-21h/AH = 25h to restore the old vector, which has previously been saved in "offsetint" and"selectorint" by the installint() procedure.
POSTMESSAGE()
The interrupt service routine will be entered every time a key is pressed or released, and all that I have done inside it is call POSTMESSAGE()to send a WM_USER message to the window's callback function DPMICALLBACK().
WM_USER
WM_USER equates to a message number that is not used by Windows as a message,so it is free for an application to use. A range of such numbers is available for an application to use: look in WINDOWS.INC.
Ok, now for the installint function:
Listing 4: The installint Function
I can put the interrupt service routine in the same procedure as the install code, if I wish, but before listing it, I want to comment on the above code.
To be able to get at data in the service routine (I'll call it an ISRfrom now on), I had to create a data alias; that is, a data selector that points to the code segment. This enables me to write to the code segment.
Into the code segment I saved the handle (hwnd) of the application's window.The reason for this is that within the ISR I called POSTMESSAGE(), which needs the handle as a parameter.
Calling the old handler
You can see that I hooked the vector and saved the old vector, but I also put the old vector into INT-60h. That is, I hooked INT-60h so that it now points to the Windows keyboard handler. This is convenient, because from within the ISR I wanted to be able to call the old ISR, for proper handling of the keyboard input.
Note that there are other ways of doing this, such as by use of a CALL instruction.
Now for the ISR:
See how simple the ISR is! I was able to call the original keyboard handler for proper handling of the key press/release, though note that I could have put the "INT 60h" at the end of the ISR if required.
I accessed "hwndcs", the handle of the window passed as data in the code segment, and then called POSTMESSAGE().
Note that I did not make use of aliasing in this simple skeleton.
I chose to explicitly push the parameters onto the stack prior to the CALL,rather than use the PASCAL qualifier TASM's generation of code with the PASCAL qualifier is horribly inefficient, so I felt better about doing it this way.
Enhanced Mode Hardware Interrupts
INT-9 keyboard handler
So what about Windows in Enhanced mode? Remember that Windows 95 can onlyrun in Enhanced mode.
I mentioned earlier that Windows gets up to some tricky business, and for both Standard and Enhanced modes reflects the INT-51h to INT-9.
However, this mechanism is different in each case, as Enhanced mode is able to make use of virtual machines, with the result that hooking eitherINT-51h or INT-9 will work in Standard mode, but in Enhanced modeonly INT-9 will work.
Real mode keyboard handler
So the earlier example code that I wrote to hook INT-51h for illustration purposes simply needs to be modified to hook INT-9, and it will work in both Standard and Enhanced modes. Unfortunately there is is still one complication DOS.
I keep hoping it will go away but it won't. The hardware interrupt handler developed in this chapter will work with any number of Windows applications multitasking, but not when a DOS program is running. In the former case, it doesn't matter if the program containing the ISR is iconized and another WinApp has the active window still, all key presses will in real time be routed to the ISR and be posted to the iconized program and Windows will call the iconized program's callback function, giving it the message, even though it is iconized.
So you'll always get the beeps when pressing and releasing a key.
However, if you run the "DOS Prompt" program, the beeps will stop. Upon exiting back to Windows, the beeps will start once again.
If you really must have the ISR continuing to function when the CPU is running a V86 or Real mode program, refer to Chapter 11, as I decided to make the handling of Real mode a special chapter all on its own. See also the footnote on page 258.
What the program "does"
I suppose you do realise by now what the example program does it beeps the loudspeaker every time you press or release a key.
Because the ISR only posts a message to the main Windows program, it is what I would class as pseudo-real-time response. Don't forget, however, that the ISR shares the same code segment as the main program, and by way of a data alias, data can be passed to and fro. Or the actual WinApp data segment can be readily accessed.
For example, harking back to the problem that my colleagues had they wanted to measure an external parameter at precise intervals and log it for internal analysis. The interrupt mechanism provided the precise intervals,and the ISR could have read the parameter from the input port and recorded it, then exited. Simple enough.
You will find the program on the Companion Disk in \ISR1.