[comp.os.msdos.programmer] Handling interrupts in C

bright@nazgul.UUCP (Walter Bright) (05/28/91)

In article <1991May21.140557.16018@cbnewsc.att.com> tjr@cbnewsc.att.com (thomas.j.roberts) writes:
/From article <1991May20.171545.23591@amc.com>, by jwbirdsa@amc.com (James Birdsall):
/>    Anyone writing interrupt handlers using TC++'s "interrupt" keyword
/> should be aware that DS is not preserved into the function. DS is preserved
/> through the function; the value of DS after the interrupt is the same as
/> before. However, the value of DS while the function is executing is not the
/> same as the value when it was called. The entry code pushes all the
/> registers on the stack and then reloads DS with the value for the program's
/> near data segment.
/Note that unlike normal C functions, modifying the parameters to an
/interrupt function within the function will change the values of registers
/used by the caller upon its return - great care is needed! READ THE MANUAL! 
/The truly hard thing to do is to chain to the previous interrupt handler
/with the caller's registers, and arrange for the previous handler to
/return registers to the caller (this is usually what you want to do
/when "wrapping" int 21H, et al). Borland did not include an appropriate
/function in the library (I believe it would really need to be a
/"psuedo-function", as its semantics could not be those of a normal
/C function). Thus, to do this you must use assembly language to
/restore the stack and registers and then jump to the previous
/handler's address (without modifying ANY registers!).

The way Zortech supports interrupts is via a library package. You can
set up a normal C function as an interrupt service routine. Into that
service routine is passed a pointer to the register state at the time
of the interrupt. The registers can thus be examined and modified.
Within the interrupt routine, you can call the previous interrupt handler
via another call to a library function. Upon return from the handler, you
can choose to return from the interrupt or chain to the previous handler,
by having a 0 or non-0 return value.

The interrupt service routine can be set up to be called with its own local
stack, or use the caller's.

The nice thing about this package is it requires no extensions to the compiler,
and the implementation of the library package has been done for DOS, Rational's
286 DOS Extender, and Pharlap's DOS Extender. We will soon have a version
for Windows 3 too. No source code changes are required.

The disadvantage of using this package is it has a little longer interrupt
latency. I'd argue, though, that if you need every watt out of your ISR then
you need to write it in assembly anyway. I've written numerous things with this,
including mouse serial drivers, control-break handlers, and int 21h filters.

Hooking ^C (interrupt x23) to call myhandler() is as simple as:

	int_intercept(0x23,myhandler,0);

and unhooking it is:

	int_restore(0x23);

If you use the package you are not wedded to Zortech, either, unlike if you
use special compiler extensions. The library source is included, and is
trivially convertible to Borland or Microsoft C.

seanna@bnr.ca (Seanna Watson) (05/29/91)

A quick note while on the topic: I discovered tody (after some effort) that
in TC++, interrupt handlers take arguments (...) not (void), as in TC.
It's NOT documented.
/Steve Watson

barry@gpu.utcs.utoronto.ca (Barry Lay) (05/30/91)

MSC appears to have the interrupt keyword for functions as well, and will take
the various register values as parameters.  I would like to know if this is
the way to deal with the "hooks" that you can set up with INT 33h (function
14h, or thereabouts).  That is, can I set up a function which will be called
if, say, mouse button one is pressed.  My understanding of the interrupt
keyword is that a special prologue and epilogue is created which saves all of
the registers (and restores them) and uses IRET rather than RET to return.
What I don't know is what the mouse driver expects the user interrupt routine
to do.

Barry

Ralf.Brown@B.GP.CS.CMU.EDU (05/30/91)

In article <1991May29.044612.8057@bnr.ca>, seanna@bnr.ca (Seanna Watson) wrote:
}A quick note while on the topic: I discovered tody (after some effort) that
}in TC++, interrupt handlers take arguments (...) not (void), as in TC.
}It's NOT documented.

Sure it is.  See page 388 of the TC2 User's Guide.  Interrupt functions have
never had void arguments, although you might have been fooled by the lack
of argument declarations in the prototype for getvect() and setvect().	But
foo() is *not* the same as foo(void).

-- 
{backbone}!cs.cmu.edu!ralf  ARPA: RALF@CS.CMU.EDU   FIDO: Ralf Brown 1:129/53
BITnet: RALF%CS.CMU.EDU@CARNEGIE   AT&Tnet: (412)268-3053 (school)   FAX: ask
DISCLAIMER?  Did  | It isn't what we don't know that gives us trouble, it's
I claim something?| what we know that ain't so.  --Will Rogers

nfs@elan.Princeton.EDU (Norbert Schlenker) (05/31/91)

In article <334@nazgul.UUCP> bright@nazgul.UUCP (Walter Bright) writes:
>The way Zortech supports interrupts is via a library package. You can
>set up a normal C function as an interrupt service routine. Into that
>service routine is passed a pointer to the register state at the time
>of the interrupt. The registers can thus be examined and modified.
>Within the interrupt routine, you can call the previous interrupt handler
>via another call to a library function. Upon return from the handler, you
>can choose to return from the interrupt or chain to the previous handler,
>by having a 0 or non-0 return value.
>
>The interrupt service routine can be set up to be called with its own local
>stack, or use the caller's.

Kudos to Zortech for this.  ZTC's interrupt package is without a doubt
the right way to handle the entire question, unlike the Turbo
interrupt kludge which Microsoft unfortunately picked up (it amazes me
that manufacturers striving for ANSI compatibility would install such an
amazing mess at the same time they are fixing up old kludges by turning
far into _far, etc.)  Zortech's method is altogether incredibly clever.

BUT...

Zortech has yet to ship a working copy of their int_intercept routine.
A caller that asks int_intercept() to use the normal stack will find
that all works well until int_restore() is called, at which point the
heap checker thinks the heap has been trashed.  I thought I had a wild
pointer somewhere and spent days looking for this.  This problem was
reported way back around v2.11 of the library and still exists in the
currently shipping v2.18.  There is a good version on the Zortech BBS.
Let's hope v3.00 is better.

Norbert

jgb@prism.gatech.EDU (James G. Baker) (06/04/91)

In article <1991May29.044612.8057@bnr.ca> seanna@bnr.ca (Seanna Watson) writes:
>A quick note while on the topic: I discovered tody (after some effort) that
>in TC++, interrupt handlers take arguments (...) not (void), as in TC.
>It's NOT documented.

Neat trick.  :-) 
How does one pass arguments to an ISR?  I would not think anything would
be passed, unless you consider the registers "passed" to it.


-- 
BAKER,JAMES G - Undergraduate Lab Instructor, School of Electrical Engineering
____  _    _    Georgia Institute of Technology, Atlanta Georgia, 30332
  |  | _  |_)   uucp: ...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!prism!jgb 
(_|. |_). |_).  Internet: jgb@prism.gatech.edu, jgb@ee, jgb@eecom, jgb@cc

pgt@hpfcso.FC.HP.COM (Paul Tobin) (06/06/91)

* Neat trick.  :-) 
* How does one pass arguments to an ISR?  I would not think anything would
* be passed, unless you consider the registers "passed" to it.

I suppose TC++ could actually catch the interrupt and generate a call
frame for your ISR, eh (what stack is in use at that time)?  I wonder
what good that would do, though?  It could pass in global variables,
but the ISR could get at those itself.  Can anyone think of any other
ideas?

	Paul "my life is constantly interrupted" Tobin
	pgt@hpfcla.fc.hp.com

resnicks@netcom.COM (Steve Resnick) (06/08/91)

In article <15590004@hpfcso.FC.HP.COM> pgt@hpfcso.FC.HP.COM (Paul Tobin) writes:
>* Neat trick.  :-) 
>* How does one pass arguments to an ISR?  I would not think anything would
>* be passed, unless you consider the registers "passed" to it.
>
>I suppose TC++ could actually catch the interrupt and generate a call
>frame for your ISR, eh (what stack is in use at that time)?  I wonder
>what good that would do, though?  It could pass in global variables,
>but the ISR could get at those itself.  Can anyone think of any other
>ideas?


In the Asynch driver I wrote, I have default ISR's for 4 comm ports.
These ISR's look like:

void interrupt IsrCOM1(void)
{
	IsrHandler(0);
}
void interrupt IsrCOM2(void)
{
	IsrHandler(1);
}
.
.
.
void IsrHandler(int PortID)
{
	.
	.
	.
	[Handle the interrupt and return]
}

This allows me to have one real ISR which gets called by the dummy ISR's
identifying which UART needs serivce, rather than duplicating code for
each serial port. The routine, IsrHandler(), is re-entrant, meaning that
it doesn't modify global variables, nor does it have any static variables.

Hope this helps...
Steve

-- 
-------------------------------------------------------------------------------
        resnicks@netcom.com, steve@camphq, IFNA:        1:143/105.0, 
                 co moderator for comp.binaries.os2
 Real life: Steve Resnick. Chief Software Architect, Process Scientific, Inc
 Flames, grammar and spelling errors >/dev/null
 The Asylum OS/2 BBS - (408)263-8017 12/2400,8,1 - Running Maximus CBCS 1.2
-------------------------------------------------------------------------------