---------------------------------------------------------------------------- ISC V3.66 by: Ofer Laor (AKA LeucroTTA) ---------------------------------------------------------------------------- ISC- Interrupt Service Class ----------------------------------------------- hooking interrupts into C++ objects made easy ----------------------------------------------- ---------------------------------------------------------------------------- NEW in version ---------------------------------------------------------------------------- þ ver 3.66 - ISC destructor turned virtual. Encorperated a __NOT_TSR__ macro for the guys that are using ISC in embedded systems (via Paradigm systems' "Paradigm Locate" package), as well as interrupt handlers in non-TSRs. To use this feature define __NOT_TSR__ before including ISC.h (or place this macro in the defines section in Options|Compiler options. Killed the ASSUME DS!=SS problem (no specific settings neccessary for SMALL & COMPACT models). Also, Added KBD class that hooks keyboard (for programs with a hotkey). Added a DOSFREE example (DOSFREE + KBD) that snaps a picture of the screen and appends to a file called . Dosfree allocates more stack for it's ISC (so that sprintf, and other stack memory hogs will not crash the system). More docs on the derived classes can be found here also. +++ ISC turned PD. Info about that can be found in the end of this file. ---------------------------------------------------------------------------- OVERVIEW ---------------------------------------------------------------------------- This article describes a class for Turbo / Borland C++ (checked with versions 3.x), for hooking interrupts into C++ objects. For those of us who need to write heavy TSRs or regular programs that need interrupt handlers - hooking the interrupt handlers into the C++ code was a bit of a fuss. Borland made it easy enough for us to hook interrupts into C code through the "interrupt" keyword, but objects could not use this feature. The reason for this is that every C++ object needs an identifier to be passed to it when using one of it's functions. This identifier (called "this") is the real name of the object and is used by it to access data and function members. When a member function is called - (a.foo() for example) a is the "this" and foo() is the function to be called. When the actual call is made - a is pushed unto the stack and foo() is called. Now, foo() uses data members through "this" and knows it's own name through it. If you could somehow get foo's address and called it (something that C++ prohibits and protects its programmers from) you would not pass on the right "this" and cause a malfunction when foo() accesses an internal data member or calls a virtual function. What people would usually do is to make a static interrupt function for each of the interrupts in the system. This is a common solution (in lack of a better one). ISC offers a different approach - and a more useful one. A class that uses ISC will derive from it and overload a function called isr() - (Interrupt Service Routine). Now all that needs doing is activating the interrupt (activate(int_num)) and the interrupt is hooked into that OBJECT!!! What this means is that if you have a class for Serial communications that works through interrupts - you don't have to make a different interrupt function for each of the possible ports you'd be using- you would write the class as you would any other. Each of the instances of the Serial class you are writing will hook into a different interrupt (or the same one if needs be). ISC works in a simple way, it produces what Windows 3.x programmers call a thunk (a small piece of code that contains your data segment and sets it just before the real call is made- or in this case the id of your object) for each of the objects that are currently hooked into interrupts through ISC. ISC reproduces the thunk for each object. Each Thunk contains in it's code - the id of the object that owns it. Then it calls a special dispatcher that handles the call to the right ISC derived object. The dispatcher also sets up the stack for all the ISC users (so that no stack cramming is needed in your interrupt routines). The dispatcher also sets up a structure that contains the registers that the interrupt entered through (so you may modify them or use them if they are needed). In the first two versions of this class I tried to utilize (without much success) properties in the local C++ compilers (how the virtual function tables work) and tried to call virtual functions directly from ASM. While this was fairly easy to do (a bit of reverse engineering) - it was compiler dependent (and even compiler version dependent). I have seen the light and this version is COMPILER INDEPENDENT (although the ASM routines are written to work specifically under tasm - and a bit of semantic changes might be needed to make it work under different assemblers). A close look will have taught you that porting the code to your local machine, and compiler is a simple enough task! Since the ISC class is mainly for use in TSRs (although it is usable in regular programs just as well)- these are some of the pitfalls that TSR writers in C++ will encounter: þ ISC CANNOT be compiled under the TINY memory model (there is a problem with the segment locations in the asm module. As it couldn't be solved up till now, I don't think I'll spend more time with it... ---------------------------------------------------------------------------- ISC functions. ---------------------------------------------------------------------------- ISC provides your classes with the ability to hook into hardware and software interrupts. it does this by providing 2 "overloadable" functions: both named isr. * HARDWARE INTERRUPTS: virtual void isr(void); just overload this function and when your class is activated the isr will be called whenever the hardware calls it. * SOFTWARE INTERRUPTS: virtual void isr(IREGS& regs); IREGS is NOT compatible with the regular REGS union although it is very close to it (it has MORE registers than REGS). The 32 bit option is not supported in the interrupt keyword - so I have not incorporated it into ISC. * IREGS contains two structures: struct IBYTEREGS { unsigned char al, ah, bl, bh; unsigned char cl, ch, dl, dh; }; struct IWORDREGS { unsigned int ax, bx, cx, dx; unsigned int si, di, ds, es, cs, flags, ip, bp; }; you may access IWORDREGS and IBYTEREGS through .x and .h :- IREGS regs; ... regs.x.ax= regs.h.bl+ regs.h.dl; all of these registers are what the register situation was BEFORE the interrupt was called. if you change the content of the regs parameter that is passed to you in the isr function - this will be the value of that register when the interrupt exits. * void activate(const int in_num) - will hook the interrupt handler to the function isr (software). The default software isr calls the hardware isr, so that if you hook to the hardware isr - you will be called as required. But if you overload both - you must literally call the hardware interrupt handler in order for it to work. This function prevents the programmer from activating an ISC derived class twice (you must deactivate it first). * void deactivate(void) - will unhook you from the interrupt. The destructor of ISC also does that - so unless you'd like to unhook and rehook to another interrupt - you shouldn't need this function. The function enables you to call activate again (for rehooking to another interrupt, for example). some GENERAL PURPOSE functions are also available here as part of ISC - since I use ISC to build TSR's in C++ (for example a real time communication program that runs in the background)- I though it useful to provide TSR functions here as well. These STATIC functions ( is_TSR and TSR) take care of managing your TSR needs (the most basic ones). * static int is_TSR(const char *app_name)- will figure out if your program is running already as a TSR. You must choose a unique application name and put it's name as parameter. Returns (1) if you are RESIDENT already and (0) if you're not. I used a simple method for this - (for more info. read the source file). * static int TSR(const int exit_code= 0, unsigned long extra_heap= 0)- will keep you resident in memory. if you call TSR() it will attempt to stay resident (minimal memory possible) and return 0 to DOS. You can return any exit code by calling TSR(1) for example. If your TSR needs more room to grow (extra interrupt-time heap for example) put the extra heap (in bytes) in the second parameter. Remember that in the LARGER models malloc and new use the far heap so that you may not be able to use your preallocated memory in them (and most likely no heap at all will be available in those memory models at all). Problem with run time heap (SMALLER MEMORY MODELS ONLY) is that when you allocate past the max heap you declared (in parameter extra_heap) the program will crash (in a probably very bizarre way!!!). USE THOUGHTFULLY. The method for finding the minimum memory necessary for reallocation is an algorithm I am quite proud of- it works in all the memory models and with a lot less memory that the sources Borland provides in their help of KEEP. It can be converted to C very easily (change new into malloc!). This is a brand new method (new from V3.6) and is not prone to heap failures and possible problems (as the previous versions were). þ One ISC specific static function is reallocStack(unsigned long stacklen):- * static int reallocStack(unsigned stackLen)- will reallocate the hardware interrupt stack frame to the specified size. If you need to call this function you must:- either call it while no ISC derived class is active or simply disable interrupts before you call it (don't forget to re-enable them). ---------------------------------------------------------------------------- ISC data members. ---------------------------------------------------------------------------- þ* There are 2 data members of major importance - int_num and old_vect; both must not be changed (since deactivate uses them to recover the state of the interrupt as it was before ISC was used. int_num - contains the number of the interrupt hook. old_vect - is a pointer to the function that was the previous interrupt vector. It can be called - by -> old_vect(); DO NOT CALL IT WITH PARAMETERS!!!! To get the registers to be as they were use PSEUDO VARIABLE (_AX, _BX...) or call the vector in some alternative way!!! Beware of changing _BP (will demolish automatic parameter referencing), and _DS (will demolish static parameter referencing). *þ There are 3 static data members that refer to the hardware stack frame:- StackFrame- points to the actual stack frame. StackFrameLen- contains the current stack frame length. ISCcount- holds the number of ISC objects that currently exist in the system. ---------------------------------------------------------------------------- EXAMPLES ---------------------------------------------------------------------------- þ There are 4 examples:- þþ EXAMPLE1:- is a resident that keeps a timer tick watcher on screen (by incrementing the first char on the top left of the screen once every timer tick). Also, it keeps a keyboard interrupt watcher right next to it. This example shows how to make an ISC derivative- how to check if already resident. How to use 2 ISCs together... þþ EXAMPLE2:- is an example of a software interrupt - it is a software interrupt that beeps with a length and pitch which are passed as register parameters. The main routine will test it and verify that it had not failed through return values (also through register as well as flags). ** - when using ISC classes on automatic (local vars) rememeber:- NOT ALL COMPILERS call DESTRUCTORS for automatic vars if exit() or do-alike funcs (like abort()) run (they simply do not call the destructors), so that loca ISC vars might not be freed and the interrupt will stay hooked, resulting in a crash in most cases). So, using stack ISC (or derivitives) is ill advised unless you know what you're doing... þþ EXAMPLE3:- is a more complex example of a class that has to do a lot of processing (each one taking longer than the timer tick interval). it slows the computer down (so before running it). þþ EXAMPLE4:- sends a novell look alike "send" message onto the screen (which stops the forground and waits for the user to free it). Pretty annonying program, so it before running. ---------------------------------------------------------------------------- MORE DIRECTORIES:- an overview ---------------------------------------------------------------------------- þ Also included are some more files and directories:- þþ ISC :- This directory contains all that you need in order to work with ISC. (Include ISC.cpp and ISCSERVE.asm into your project and #include ISC.h in your source files). þþ KBD :- keyboard hooking. example can be found in DOSFREE/EXAMPLE. þþ DOSFREE :- allows you to execute dos functions (except 0-ch meaning that line input through scanf is forbidden) - such as opening a file and reading writing or deleting it. þþþ an example can be found that hooks a keyboard (via the kbd class), and snap a shot from the screen into a file called snap.dat in the local directory. The example program (called snap) is a parellel to a snap program that is found in the "DOS ENCYLOPEDIA" from Microsoft press. The hotkey for this program is SHIFT-TAB. þþ SERIAL :- Some more advanced ISC derivative classes that handle interrupt driven serial and modem communications. Although ISC has changed in the past year - no changes have been made to these sources. Previously these sources were distributed under XSERIAL.arj and XSERIAL1.arj. Also included is a sample of a Buffer class I used that a colleague of mine wrote... þþþ there are two examples in serial- RECEIVE and TRANSMIT:- RECEIVE will set COM1 to 9600baud, 8bytes, 1stopbit, No parity- and put on the screen anything that comes in from the port. The program will stop if a key is typed in, as well as if the string "hello there!!!" is entered from the com port. TRANSMIT will transmit the string "hello there!!!" to com1 (same port settings as receive), wait until the string is completely sent and exit. These classes works great in TSRs (that's what I use it for). ---------------------------------------------------------------------------- KBD ---------------------------------------------------------------------------- This class makes hooking into keyboard hotkeys very easy. þ Constructor:- KBD(unsigned char scan_code, unsigned char controls= 0, int kill_key= 1); first parameter is the hot-key itself (KB_A is the letter A , etc.). the hot-key's shift state (ALT/CTRL/SHIFT) is found in controls. The control codes start with KBS (KBS_LSHIFT, etc.). For Ctrl-Alt-A you would pass KB_A as the scan_code, KBS_CTRL| KBS_ALT as the controls parameter. The third parameter is kill_key. To kill the key is to make the hot-key not visible to the forground app. The default is 1 (meaning make the hot-key invisible to forground). þ To hook a hotkey, just derive from KBD and overload the virtual function hotkey. This function gets called if the hotkey matches. þþ An example is joined with DOSFREE (take a look at DOSFREE's example). ---------------------------------------------------------------------------- DOSFREE ---------------------------------------------------------------------------- This class allows using DOS functions inside your TSRs in an easy manner. þ Go - call this function when you want your app to run (use this instead of calling AppFunc directly. þ AppFunc - overload this function (by deriving to DOSFREE), this function is called when it is safe to use dos. Call this function using Go. Do not use int21h funcs 0-0ch within this function. þþ There is an example of DOSFREE in directory examples. The example uses the KBD class to hook a hotkey, and DOSFREE to photograph the screen and save it into a file. The hotkey for this program is SHIFT-TAB. (Window's users beware..). ---------------------------------------------------------------------------- SERIAL CLASSES ---------------------------------------------------------------------------- These classes are 3:- SERIAL_PORT, XONOFF, MODEM. þ To use serial port comunications you need only know the following:- create a serial port with serial port num. as constructor param (1 is com1, 1-4 are allowed in this way). For compatible systems, a diffrent constructor is provided that allows changing every param in the serial port. If you prefer changing/ setting the com port on a later occasion use activate with com num param. To set the serial port's communication parameters use function- void set_up_port(const long baud_rate,const BYTE word_len, const BYTE parity, const BYTE stop_bits); for example:- com1.set_up_port(9600, 8, 'N', 1) sets up com1 object with a 9600 baud rate, 8 bits, no parity, and 1 stop bit. to read from that object use com1>> my_char (my_char is an unsigned char). to write to that object use com1<< my_char (ditto). to peek at the next char that arrived without pulling it from the buffer use: com1> my_peek_char þ MODEM, XONOFF adhere to the same principles but have additional functions and will also handle the flow control (outgoing only). þ The BUFF class is a buffer class that adheres to the same I/O operators, and can be used as FIFO, or as an array (however the user wishes). This class was jointly designed and written with my colleague- Ram Machness, god bless him (;-D). ---------------------------------------------------------------------------- NOTE ---------------------------------------------------------------------------- þ If you need to hook with ISC to a software interrupt like int13h or int21h don't forget that when you call { old_vect() } you must set the correct registers to what they were after the isr was called (these register values are in the IREGS parameter passed to isr). Usually the pseudo variables _AX, _BX, _FLAGS, etc... will do just fine. Also remember that the values of the registers after the software interrupts are called might be needed by the forground too (so you can put them back into the IREGS parameter). WARNING- if you do not do these things you will probably either crash the forground or mash things up. You may encounter problems using BP and DS in the interrupt (SO HOOKING INT21 is out of the question). þ Use and abuse!!! ---------------------------------------------------------------------------- --> THE BOTTOM LINE <-- ---------------------------------------------------------------------------- þþ since I am quite poor and FOOBAR, this and previous versions of ISC have now been declared ShareWare. What this means to you - user is that you get my support upon problems (Email or textmail), but you need to pay a cheap little sum of money - which is $30 bucks cheap (larger donations also accepted). You may still change the code without putting my name in it!!! But please pay up so that even more wonderful versions of ISC and derived classes can reach the PD market safely... registered users will receive some more lovely classes that I will not include in the PD ver. of ISC!!! SO - PAY UP!!!! +++++ |_ -| Ofer Laor (AKA LeucroTTa) @|o O|@ 27 KKL St., Kiriat Bialik (27000) | ^ | Israel. |---| telephone @ work (972) - 3 - 9684643 - @ home (972) - 4 - 701845 EMail:- s5919325@techst02.technion.ac.il