[alt.msdos.programmer] Notes about Borland C++ interrupt keyword

jwbirdsa@amc.com (James Birdsall) (05/21/91)

   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.

   This doesn't matter for most interrupt handlers, but if you're trying to
revector an interrupt that uses DS as a parameter (for example, the DOS
interrupt (0x21)), then it becomes a major obstacle.

   This behavior has been observed with Borland C++ 2.0. Presumably it is
the same with TC++ 1.0, and probably plain TC. It is not mentioned as such
in the documentation, although if you read it carefully it is implied.
Anyway, having spent an hour or two tracing the errors caused by this
behavior, I thought it worth posting a public notice to save other people
the trouble.

-- 
James W. Birdsall   WORK: jwbirdsa@amc.com   {uunet,uw-coco}!amc-gw!jwbirdsa
HOME: {uunet,uw-coco}!amc-gw!picarefy!jwbirdsa OTHER: 71261.1731@compuserve.com
================== Kitten: a small homicidal muffin on legs. ==================
=========== "For it is the doom of men that they forget." -- Merlin ===========

dmurdoch@watstat.waterloo.edu (Duncan Murdoch) (05/21/91)

In article <1991May20.171545.23591@amc.com> jwbirdsa@polaris.amc.com () writes:
>
>   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.
>
>   This doesn't matter for most interrupt handlers, but if you're trying to
>revector an interrupt that uses DS as a parameter (for example, the DOS
>interrupt (0x21)), then it becomes a major obstacle.
>
>   This behavior has been observed with Borland C++ 2.0. Presumably it is
>the same with TC++ 1.0, and probably plain TC. It is not mentioned as such
>in the documentation, although if you read it carefully it is implied.
>Anyway, having spent an hour or two tracing the errors caused by this
>behavior, I thought it worth posting a public notice to save other people
>the trouble.

I don't have any of the C flavours, but I'm surprised it's not documented 
there.  In TP, the prologue of an Interrupt procedure is explicitly given;
it says that DS is changed to the ISR's data segment.  If you want the old
DS, you just read the 3rd argument from the end; presumably in BC++ that
would be the 3rd from the beginning.

Duncan Murdoch
dmurdoch@watstat.waterloo.edu

ressler@CS.Cornell.EDU (Gene Ressler) (05/21/91)

In article <1991May20.230908.7178@maytag.waterloo.edu> dmurdoch@watstat.waterloo.edu (Duncan Murdoch) writes:
>In article <1991May20.171545.23591@amc.com> jwbirdsa@polaris.amc.com () writes:
>>
>>   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
 ....

>I don't have any of the C flavours, but I'm surprised it's not documented 
>there.  In TP, the prologue of an Interrupt procedure is explicitly given;
 ....
>Duncan Murdoch
>dmurdoch@watstat.waterloo.edu

Both my TC++ manual (PG, pg 48) and my TC2.0 manual
(UG, pg 319) state that DS is set.  What else could
be done to provide access to static data?  This `behavior'
is, practically speaking, the only one possible.
(...that is, short of allowing statics in the code
segment (ugh).  I believe MSC provides this option.)
Chalk up one more for the joys of segmented
architectures. ;->

Gene

tjr@cbnewsc.att.com (thomas.j.roberts) (05/21/91)

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.
> 
>    This doesn't matter for most interrupt handlers, but if you're trying to
> revector an interrupt that uses DS as a parameter (for example, the DOS
> interrupt (0x21)), then it becomes a major obstacle.

This is NECESSARY for Turbo C / Turbo C++ / Borland C++ functions to
work correctly (they may reference static variables). It is well
documented in the manuals.

If you need the caller's DS (or any other register), it (they) are
available as arguments to your interrupt function. This is not an obstacle,
it is an essential feature. I am astounded that you are attempting to use 
this complicated CPU-dependent and compiler-dependent feature without
CAREFULLY reading the manuals.

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!). It can be done,
but is subtle.....

Tom Roberts
att!ihlpl!tjrob  TJROB@IHLPL.ATT.COM

sorrow@oak.circa.ufl.edu (05/21/91)

In article <1991May20.230908.7178@maytag.waterloo.edu>, dmurdoch@watstat.waterloo.edu (Duncan Murdoch) writes:
|>In article <1991May20.171545.23591@amc.com> jwbirdsa@polaris.amc.com () writes:
|>>
|>>   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.
|>>
|>>   This doesn't matter for most interrupt handlers, but if you're trying to
|>>revector an interrupt that uses DS as a parameter (for example, the DOS
|>>interrupt (0x21)), then it becomes a major obstacle.
|>>
|>>   This behavior has been observed with Borland C++ 2.0. Presumably it is
|>>the same with TC++ 1.0, and probably plain TC. It is not mentioned as such
|>>in the documentation, although if you read it carefully it is implied.
|>>Anyway, having spent an hour or two tracing the errors caused by this
|>>behavior, I thought it worth posting a public notice to save other people
|>>the trouble.

Actually, I found out that I must be doing something terribly wrong.  When
I installed my own mouse interrupt handler, if the function was declared
as "void far interrupt" it would die.  "void far" worked just fine.  I just
don't USE the interrupt keyword.  The documentation is too skimpy on it
for me to be able to use it decently.

Brian

ekalenda@cup.portal.com (Edward John Kalenda) (05/22/91)

>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.
>
>This doesn't matter for most interrupt handlers, but if you're trying to
>revector an interrupt that uses DS as a parameter (for example, the DOS
>interrupt (0x21)), then it becomes a major obstacle.

This is also the behaviour under Microsoft C. You can have access to all
the registers pushed if you declare them as shorts in the formal parameter
list of the function. The return address and flags are also available
this way. I had to look at the code generated to see the order they push
things so it is a bit version dependant but it worked for one project I
did.

Ed
ekalenda@cup.portal.com

anicolao@watcgl.waterloo.edu (Alex Nicolaou) (05/22/91)

In article <00948EEF.C05FAA40@MAPLE.CIRCA.UFL.EDU> sorrow@oak.circa.ufl.edu writes:
>Actually, I found out that I must be doing something terribly wrong.  When
>I installed my own mouse interrupt handler, if the function was declared
>as "void far interrupt" it would die.  "void far" worked just fine.  

 ** Hmm. What do you mean you "installed my own mouse interrupt handler"? If
you mean via the services provided by the mouse driver (12h or 24h, I think)
which make the mouse driver call your function, then NO, you CANNOT use the
interrupt keyword; for a very simple reason: you aren't the interrupt handler,
you are being called by it, and you are supposed to be a routine. If you use
an interrupt function, the BC++ compiler will put an iret instruction in 
for you, and that will terminate the interrupt handling prematurely - your
Microsoft compatible driver will not have finished its work and your computer
will dissappear into limbo. Incidentally, if you are doing this and then 
accessing data, you'd better be sure that you're restoring DS yourself, since
the MS mouse driver won't do it for you - if you compile in Medium or Large
models this is an absolute must, in smaller models you ought to do it for 
painless changes later.

>I just
>don't USE the interrupt keyword.  The documentation is too skimpy on it
>for me to be able to use it decently.

 ** Interrupts are a pain, but that is not a function of the documentation.
Yes, I am another borland fan :-)

alex

grimlok@hubcap.clemson.edu (Mike Percy) (05/22/91)

sorrow@oak.circa.ufl.edu writes:

>Actually, I found out that I must be doing something terribly wrong.  When
>I installed my own mouse interrupt handler, if the function was declared
>as "void far interrupt" it would die.  "void far" worked just fine.  I just
>don't USE the interrupt keyword.  The documentation is too skimpy on it
>for me to be able to use it decently.
 
If you are talking about mouse function 12(?), theone that lets you have
the mouse driver call your function whenever certain mouse events
happen, then you are on the right track.  The "interrupt" handler you
give to the mouse is not of type void interrupt.  TurboC interrupt
functions push regs to the stack, do your stuff, pop the regs and --
IRET.  This is what screws you.  The driver does a simple far CALL to your
routine, expecting to have control come back to it with a far RET.  The
IRET is simply wrong here.  Declaring your mouse function to be void far
fixes this problem, but you still cannot access global/static data
except by sheer accident, as DS will likely not be pointing to your
DGROUP.  Declaring it as void huge does this trick.  But the mouse
driver also apparantly assumes a called-saved register scheme, while the
C compiler assumes a caller-saved scheme.  Your routine can trash
registers the mouse driver expected you to leave alone.  I use #pragma
savregs.  This guarantees that a huge function will not change the value
of any of the register when it is entered. I like the pragma rather than
declaring the function as _saveregs. _saveregs pushes all register and
pops them at the end.  This is costly in time/space.  The #pragma
saveregs costs more at compile time, but is much smaller/faster at
runtime.

In short, when installing a mouse handler (function 12), I declare it

#pragma saveregs
void huge handler(void)
{
  /* handler code */
} 
 
I spent a lot of time before getting this to work...


"I don't know about your brain, but mine is really...bossy."
Mike Percy                    grimlok@hubcap.clemson.edu
ISD, Clemson University       mspercy@clemson.BITNET
(803)656-3780                 mspercy@clemson.clemson.edu

sorrow@oak.circa.ufl.edu (05/22/91)

In article <1991May21.195106.1530@watcgl.waterloo.edu>, anicolao@watcgl.waterloo.edu (Alex Nicolaou) writes:
|>In article <00948EEF.C05FAA40@MAPLE.CIRCA.UFL.EDU> sorrow@oak.circa.ufl.edu writes:
|>>Actually, I found out that I must be doing something terribly wrong.  When
|>>I installed my own mouse interrupt handler, if the function was declared
|>>as "void far interrupt" it would die.  "void far" worked just fine.  
|>
|> ** Hmm. What do you mean you "installed my own mouse interrupt handler"? If
|>you mean via the services provided by the mouse driver (12h or 24h, I think)
|>which make the mouse driver call your function, then NO, you CANNOT use the
|>interrupt keyword; for a very simple reason: you aren't the interrupt handler,

Thanks for the info.  Right you are.  I did use the functions, and didn't think
to even think about the fact that INT0x33 was still in place and thus I wasn't
the interrupt handler.  Whoops. (:->

Brian

jwbirdsa@amc.com (James Birdsall) (05/22/91)

   Allow me to clarify my earlier posting. Also please note that I was in a
bad mood that day and not feeling kindly disposed toward the compiler in
question.

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.
>> 
>>    This doesn't matter for most interrupt handlers, but if you're trying to
>> revector an interrupt that uses DS as a parameter (for example, the DOS
>> interrupt (0x21)), then it becomes a major obstacle.
>
>This is NECESSARY for Turbo C / Turbo C++ / Borland C++ functions to
>work correctly (they may reference static variables). 

   No. It is HANDY. There are other ways that interrupt functions could be
allowed to address static data. They would require special code generation
and run more slowly, so I can understand why they did it that way. Indeed,
the fact that one can access global variables should have tipped me off,
and as soon as I found out what was happening, I knew why.

>It is well
>documented in the manuals.

   Maybe. I took a fast look and didn't see it. Considering the experiences
I've had with Borland documentation in the past, I wasn't hopeful.

>If you need the caller's DS (or any other register), it (they) are
>available as arguments to your interrupt function. This is not an obstacle,
>it is an essential feature. I am astounded that you are attempting to use 
>this complicated CPU-dependent and compiler-dependent feature without
>CAREFULLY reading the manuals.

   I was attempting to throw together a fast test program to determine
whether setvect(), which I was trying to use in an entirely separate
program to revector int 21h to an assembly-language handler, was corrupting 
DOS. It was based on their example under setvect(), which worked fine as
given but crashed the system when revectoring 0x21 to a function which
simply called the old handler. When I revectored manually, fprintf() would 
emit a line of junk but the system didn't crash, and suddenly I was 
enlightened.
   The original problem turned out to be a stack misalignment error
aggravated by a confused debugger. The debugger would hang when the new
handler chained back to the original handler, which was NOT the location of
the bug that took out the system when the debugger was not used. Thus, it
appeared that something was munging part of DOS and the only candidate I
had was setvect(), because the program worked fine without it and the new
handler was not writing to anyplace it wasn't supposed to...

   After a day like that (I didn't identify the original problem until
after posting the article), you may understand why I was feeling betrayed
by my compiler. :)

-- 
James W. Birdsall   WORK: jwbirdsa@amc.com   {uunet,uw-coco}!amc-gw!jwbirdsa
HOME: {uunet,uw-coco}!amc-gw!picarefy!jwbirdsa OTHER: 71261.1731@compuserve.com
"The OS shouldn't die every time the controller drools on a sector." -- a sysop
=========== "For it is the doom of men that they forget." -- Merlin ===========

jml@stekt.oulu.fi (Lepp{j{rvi Jouni) (05/22/91)

As stated earlier, this 'feature' is indeed needed. I however, come to
think a side effect not mentioned in the previous postings (?) : DS is
changed, but SS is still the one the interrupted program / a
program which called an interrupt had. This means that pointers to any
'automatic' variables (in small data models) are, in fact, incorrect.
Even if the programmer doesn't use these directly, some library
function assumed to be safe (~no DOS calls) might. In most cases this
is not an issue, since an interrupt handler performing anything stack
intensive _should_ swap to a stack of its own (usually a static array)
due to the fact that the stack size at call time is usually unknown.
However, this might be the cause of *the mystery bug*. (You know, when
there seems to be nothing wrong with the code after 100 or so checks
etc ;-) 

(Swapping the stack, of course, takes the register arguments out of
reach. (The reason (BTW) why function arguments (such as the
registers) and automatic variables work from the foreign stack, is
that they are accessed relative to BP, which by default is an offset
relative to SS.))


--
- Jouni Lepp{j{rvi / jml@stekt.oulu.fi   -
- '.. but only maybe, life is a joy .. ' -

b-davis%cai.utah.edu@cs.utah.edu (Brad Davis) (05/22/91)

In article <1991May21.195106.1530@watcgl.waterloo.edu> anicolao@watcgl.waterloo.edu (Alex Nicolaou) writes:
> ** Interrupts are a pain, but that is not a function of the documentation.

As Alex said interrupts are a pain (any form of multi-tasking is) but
you can always have the compiler generate assembler to read and check.
If you can't read 8086 code and/or don't know what the hardware does you
shouldn't be trying to write interrupt handlers.

>Yes, I am another borland fan :-)

Me too (and so is my fully interrupt driven, buffered, handshaking,
with up calls to other routines serial port driver, all written in
Wizard/Turbo/Borland C).
-- 
Brad Davis	..!uunet.uu.net!cs.utah.edu!cai.utah.edu!b-davis
		b-davis@cs.utah.edu, b-davis@cai.utah.edu
One drunk driver can ruin your whole day.

dave@tygra.Michigan.COM (David Conrad) (05/27/91)

In article <1991May21.195106.1530@watcgl.waterloo.edu> anicolao@watcgl.waterloo.edu (Alex Nicolaou) writes:
>; for a very simple reason: you aren't the interrupt handler,
>you are being called by it, and you are supposed to be a routine. If you use
>an interrupt function, the BC++ compiler will put an iret instruction in 
>for you, and that will terminate the interrupt handling prematurely
>
>alex

???  You make it sound as if that iret is going to somehow magically return
to the program which generated the interrupt - it'll return to the caller,
i.e. the interrupt handler, but it will pop an extra word off the stack
(looking for the flags), corrupting the caller's stack.

(Of course, the previous message said something about, "I just DON'T use
the interrupt keyword - the documentation is too skimpy."  As if the
interrupt keyword was simply a matter of style, having no consequence in
terms of code generated.  (Sorry, I deleted that part of the article.)
Confusion abounds.)
--
David Conrad, dave@michigan.com
-- 
=  CAT-TALK Conferencing Network, Computer Conferencing and File Archive  =
-  1-313-343-0800, 300/1200/2400/9600 baud, 8/N/1. New users use 'new'    - 
=  as a login id.  AVAILABLE VIA PC-PURSUIT!!! (City code "MIDET")        =
   E-MAIL Address: dave@Michigan.COM