[comp.lang.c] TSR's with TC

sherman@csclea.ncsu.edu (Chris Sherman) (08/22/89)

I'm using TC to rewrite a TSR that was written in MSC.  The program makes
use of a function called _chain_intr.  TC does not have this function.  

_chain_intr, which is usually used in an interrupt handler, accepts the
address of another interrupt handler, and jumps (not calls) over to it.
It is as if the first handler was never called.

What can I do in TC to achieve the same effect?  I thought of calling the
second interrupt handler like you would call any function, given its
address, but I'm not sure this will work correctly every time.  I 
appreciate any help.

Thanx...

Paul C. Sherman
sherman@csclea.ncsu.edu

edb_tom@tor.nhh.no (Tom Ivar Helbekkmo) (08/23/89)

In article <3723@ncsuvx.ncsu.edu>, sherman@csclea.ncsu.edu (Chris Sherman) writes:
> _chain_intr, which is usually used in an interrupt handler, accepts the
> address of another interrupt handler, and jumps (not calls) over to it.
> It is as if the first handler was never called.
> 
> What can I do in TC to achieve the same effect?  I thought of calling the
> second interrupt handler like you would call any function, given its
> address, but I'm not sure this will work correctly every time.

No, you certainly can't do it that way.  In MSC, when you declare a
function "interrupt", you're telling the compiler that it will be used
as an interrupt handler.  The compiler then arranges for it to push all
registers upon entry, *and restore them* on exit, then leave with an
iret instead of a ret.  In this scenario, the _chain_intr() function
restores the registers, and then jumps to the specified place, which had
better be another interrupt handler...  :-)

Correct me if I'm wrong, someone, but unless Turbo C has explicit
support for writing interrupt handlers in C, you'll need to write your
own (at the assembly level) to be able to do this sort of thing.  I
think the Blaise C Tools library is available for Turbo C -- it among
other things helps you do interrupt processing in C.  BCT is, in my
opinion, a very good product, at a reasonable price.

-tih

-- 
Tom Ivar Helbekkmo, NHH, Bergen, Norway.  Telephone:  +47-5-959205
edb_tom@tor.nhh.no, thelbekk@norunit.bitnet, helbekkmo@nhh.uninett

jb@CSUStan.EDU (John Birchfield) (08/29/89)

In article <69@tor.nhh.no> edb_tom@tor.nhh.no (Tom Ivar Helbekkmo) writes:
>In article <3723@ncsuvx.ncsu.edu>, sherman@csclea.ncsu.edu (Chris Sherman) writes:
>> _chain_intr, which is usually used in an interrupt handler, accepts the
>> address of another interrupt handler, and jumps (not calls) over to it.
>> It is as if the first handler was never called.
>> 
>No, you certainly can't do it that way.  ...
>Correct me if I'm wrong, someone, but unless Turbo C has explicit
>support for writing interrupt handlers in C ...

Turbo C allows writing interrupt handlers directly in C using
pretty much the same mechanism as Microsoft.  The following C
program is provided as a very succinct example.
------------------------------------------------------------------------
/*
 *              Turbo C Interrupt Handler Chaining Example
 *
 * You may easily see what your compiler thinks you mean for it to
 * do by compiling with the -S -c options and then looking at the
 * assembly code.
 *                            ---   jb   ---
 *                             Aug 28, 1989
 
#include <dos.h>

void interrupt (*ticker_save) ();
void interrupt ticker ();

int count = 0;
main ()
{
	ticker_save = getvect (8);
	setvect (8, ticker);
	while (count < 30);
	setvect (8, ticker_save);
}

void interrupt ticker ()
{
	count++;
	(ticker_save) ();
}

/*
 * The following is the pertinent assembly code generated by the
 * compiler inside ticker. Note that since the compiler knows about
 * the interrupt characteristics  of the pointer ticker_save,
 * he pushes the flags before calling him.  The only penalty you
 * have is the xtra registers pushed on the stack.  Turbo C also
 * provides meta variables _AX _DX ... so that you can directly
 * manipulate the registers in the function.
 *
 * 	mov	ds,bp
 * ; Line 17
 * 	inc	word ptr DGROUP:_count
 * ; Line 18
 * 	pushf	
 * 	call	dword ptr DGROUP:_ticker_save
 * ; Line 19
 */
------------------------------------------------------------------------
-                                                                      -
===                                                                  ===
John Birchfield                                       1575 Quail Ct.
jb@koko.csustan.edu                                   Turlock, CA  95380
                                                      (209) 634-6243

few@quad1.quad.com (Frank Whaley) (08/30/89)

In article <1098@koko.CSUStan.EDU> jb@koko.UUCP (John Birchfield) writes:
>In article <3723@ncsuvx.ncsu.edu>, sherman@csclea.ncsu.edu (Chris Sherman) writes:
>> _chain_intr, which is usually used in an interrupt handler, accepts the
>> address of another interrupt handler, and jumps (not calls) over to it.
>> It is as if the first handler was never called.
>> 
>Turbo C allows writing interrupt handlers directly in C using
>pretty much the same mechanism as Microsoft.  The following C
>program is provided as a very succinct example.

<< succinct example deleted >>

While Turbo C does provide excellent interrupt handling support, the
_chain_intr() function provides one capability not available from the
normal Turbo C facilities:  The ability to preserve original register
contents when passing control to a subsequent (or original, depending
on your point of view) interrupt handler.

Note that the correct synopsis of an interrupt handling function is:
	void interrupt handler(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,fl)
		int bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,fl;
as the register contents are pushed upon entry, and popped upon exit.
While within the handler, DS contains the current DGROUP value and
other registers change as necessary.  If the original interrupt handler
is called from within our handler, it is unlikely that the correct
register values will be passed.

For many interrupt service routines this is inconsequential, as they
do not care what the previous state of the machine was -- for example,
serial port interrupt handlers are responding to a hardware interrupt and
must preserve and restore the machine state at the time of the interrupt.
However, when software interrupts are used for communication between
processes (I use the term loosely :-), the contents of the registers is
very important.  The DOS Services interrupt (0x21) and the Multiplex
interrupt (0x2f) are two examples that expect parameters to be provided
in registers which are modified by the Turbo C interrupt service code.

Fortunately (this is the part you were waiting for) the _chain_intr()
function is not dificult to implement.  Attached is a "dirty" version
(values are popped into code-segment variables) which is missing the
necessary segment/group descriptions, but which demonstrates the
principles of chaining interrupts.

I should note that, if desired, interrupt handlers can examine or modify
the input register values.  For example, to count all "open file" calls
within a program:
	Assuming:
		...
		long opencount = 0;
		void interrupt (*old21)();
		...
		old21 = getvect(0x21);
		setvect(0x21, my21);
		...

	void interrupt my21(bp,di,si,ds,es,dx,cx,bx,ax)
		int bp,di,si,ds,es,dx,cx,bx,ax;
	{
		if ( (ax & 0xff00) == 0x3d00 )
			opencount++;

		_chain_intr(old21);
	}
-----
;-----------------------------------------------------------------------|
;	_chain_intr -- chain interrupt					|
;									|
;	SYNOPSIS							|
;		void _chain_intr(target)				|
;			void interrupt (*target)();			|
;									|
;	DESCRIPTION							|
;		Chain to another interrupt handler, passing original	|
;		 (or possibly modified) register values.		|
;									|
;	RETURNS								|
;		This function returns to the original source of the	|
;		 interrupt.						|
;									|
;-----------------------------------------------------------------------|

	Public	__chain_intr
__chain_intr	Proc	Near

	POP	AX			;  clear return addr
;;;;;;;	POP	AX			;  another for large code models

	POP	Word Ptr CS:target	;  get target addr into jump
	POP	Word Ptr CS:target + 2

	POP	BP			;  restore saved registers
	POP	DI
	POP	SI
	POP	DS
	POP	ES
	POP	DX
	POP	CX
	POP	BX
	POP	AX

	;  we leave original IP, CS and flags on stack

	DB	0EAH			;  JMPF
target	DD	0			;  target address

__chain_intr	EndP
-----
-- 
Frank Whaley
Senior Development Engineer
Quadratron Systems Incorporated
few@quad1.quad.com
uunet!ccicpg!quad1!few

Water separates the people of the world;
Wine unites them.