[comp.os.msdos.programmer] MSC, interrupts & functions

ctl8588@rigel.tamu.edu (LAUGHLIN, CHET) (09/26/90)

I have successfully written a program that uses interrupt 1Ch to beep
the speaker every few seconds.  However, after trying to spruce it up
I have noticed that functions called by an interrupt (declared void
interrupt far) cannot call other functions declared simply void or int.
After reviewing the Microsoft manual I could find no clarification or
discussion of what limitations are placed on a function called by an
interrupt.  Can anyone enlighten me?? Possibly I'm not suppose to
call the C library also???

Below is a simplified code segment example.  In addition, the documents
on how to restore a vector after your finished with it are skimpy, I
assume I'm doing the right thing.

void beep()
{
   printf("beep\n");
}

void interrupt far count()
{
   if (cbeep_count > beep_count)
   {
      beep();                           /* beep ..crashes here */
      cbeep_count = 0;                  /* reset counter */
   }
   else
      ++cbeep_count;
}

-Chet Laughlin
  "I have no opinions as I do not exist - my lawyer told me so"

marcb@hp-ptp.HP.COM (Marc Brandis) (09/27/90)

>I have successfully written a program that uses interrupt 1Ch to beep
>the speaker every few seconds.  However, after trying to spruce it up
>I have noticed that functions called by an interrupt (declared void
>interrupt far) cannot call other functions declared simply void or int.
>After reviewing the Microsoft manual I could find no clarification or
>discussion of what limitations are placed on a function called by an
>interrupt.  Can anyone enlighten me?? Possibly I'm not suppose to
>call the C library also???
>
>Below is a simplified code segment example.  In addition, the documents
>on how to restore a vector after your finished with it are skimpy, I
>assume I'm doing the right thing.
>
>void beep()
>{
>   printf("beep\n");
>}
>
>void interrupt far count()
>{
>   if (cbeep_count > beep_count)
>   {
>      beep();                           /* beep ..crashes here */
>      cbeep_count = 0;                  /* reset counter */
>   }
>   else
>      ++cbeep_count;
>}
>
>-Chet Laughlin

I do not see any reason, why you should not be able to call any 
procedure that is reentrant. Reentrancy means (in this context),
that you can interrupt some procedure p and then call the same procedure
p from the interrupt handler. As a rule of thumb, procedures that do
not use static variables (or globals, anything that is not lying on
the stack) are reentrant. I think several parts of the MSC library
are not (!) reentrant.

Moreover, you should make sure that your interrupt handler takes only
a small amount of time to execute, so printf is certainly something
that you should avoid (and beep as well). Consider that you get an
interrupt every x milliseconds. If your interrupt handler takes more
than x milliseconds to execute, then the 2nd interrupt interrupts 
your handler while it is handling the first interrupt. This stacking
up of interrupts continues until the system dies with a stack over-
flow.

If you cannot avoid to have an interrupt handler that may take more
than this x milliseconds to execute, you should build in some code
that detects when the handler is interrupted itself and do something
reasonable with it. Make sure that these actions are atomic. For
more information on how to do it, see literature about concurrent
programming.


(* I speak only for myself.
	Marc-Michael Brandis
	Institut fuer Computersysteme
	ETH Zentrum
	CH-8092 Zuerich, Switzerland
	e-mail: brandis@inf.ethz.ch
		brandis@iis.ethz.ch
   Temporarily at HP, marcb@hp-ptp.ptp.hp.com
*)

stever@Octopus.COM (Steve Resnick ) (09/29/90)

In article <10850002@hp-ptp.HP.COM> marcb@hp-ptp.HP.COM (Marc Brandis) writes:
>>I have successfully written a program that uses interrupt 1Ch to beep
>>the speaker every few seconds.  However, after trying to spruce it up
>>I have noticed that functions called by an interrupt (declared void
>>interrupt far) cannot call other functions declared simply void or int.
>>After reviewing the Microsoft manual I could find no clarification or
>>discussion of what limitations are placed on a function called by an
>>interrupt.  Can anyone enlighten me?? Possibly I'm not suppose to
>>call the C library also???
>>
>>Below is a simplified code segment example.  In addition, the documents
>>on how to restore a vector after your finished with it are skimpy, I
>>assume I'm doing the right thing.
>>
>>void beep()
>>{
>>   printf("beep\n");
>>}
>>
>>void interrupt far count()
>>{
>>   if (cbeep_count > beep_count)
>>   {
>>      beep();                           /* beep ..crashes here */
>>      cbeep_count = 0;                  /* reset counter */
>>   }
>>   else
>>      ++cbeep_count;
>>}
>>
>>-Chet Laughlin
>
>I do not see any reason, why you should not be able to call any 
>procedure that is reentrant. Reentrancy means (in this context),
>that you can interrupt some procedure p and then call the same procedure
>p from the interrupt handler. As a rule of thumb, procedures that do
>not use static variables (or globals, anything that is not lying on
>the stack) are reentrant. I think several parts of the MSC library
>are not (!) reentrant.
>

printf in this case is NOT re-entrant. printf() writes a string to stdout, 
which in turn makes a call to DOS to write the string. MS DOS is not 
re-entrant either. Another thing to note, if MSC uses the same stdio structures
as TC does, then *any* stdio call should be considered non-reentrant. (look
at stdio.h). 

I write a lot of concurrent code under DESQview on MS DOS. Under DESQview, the
environment allows for a single .EXE file to have several concurrent tasks. The
problem of re-entrancy has come up there a lot, too. MS DOS is taken into
account for, but the standard library, which gets shared between 2 or more
tasks, is not. The programmer has to take into account those considerations.
After a couple of years of working with TC and DESQview, I have found that a
large number of the standard library (especially stdio) is non-reentrant.
As Mr. Brandis pointed out, the re-entrancy can be determined largely by
how a function operates: If it uses static, or global variables it is not
re-entrant, if it doesn't it might be. The trick is to experiment a little, or
write your own re-entrant routines.

Cheers!
Steve

-- 
----------------------------------------------------------------------------
steve.resnick@f105.n143.z1.FIDONET.ORG - or - apple!camphq!105!steve.resnick
Flames, grammar errors, spelling errrors >/dev/nul
----------------------------------------------------------------------------

srini@ultra.com (S. Srinivasan) (09/29/90)

>I have successfully written a program that uses interrupt 1Ch to beep
>the speaker every few seconds.  However, after trying to spruce it up
>I have noticed that functions called by an interrupt (declared void
>interrupt far) cannot call other functions declared simply void or int.
>
>
The one thing to watch out for is that you do not overextend yourself 
in doing things in interrupt mode. Some of the reasons for this are:
1. some library calls may not be re-entrant.  For example, a call to
   print something would issue another interrupt to DOS.  If this 
   print were called from within an interrupt vector, then you have
   an interrupt within an interrupt.  Which may not be too bad, but
   if you don't know the OS, then dont mess with it.

2. You might be blowing your stack if interrupts nest or if you are
   making other function calls from your interrupt vector.
   Your default stack with MSC is 2K.  Generally enough, but ...

The solution in a single-tasking system like DOS is simple but crude.
This is recommended for those who dont know DOS too well, (like me!).

main()
{
	char	int_happened = FALSE;

	while (TRUE)
	{
	    if (int_happened)
	    {
	        beep();
		int_happened = FALSE;
	    }
	}
}


/* No longer invoked in interrupt-mode */
void beep()
{
   printf("beep\n");
}


/* Does not call any other functions */
void interrupt far count()
{
   if (cbeep_count > beep_count)
   {
      int_happened = TRUE;
      cbeep_count = 0;                  /* reset counter */
   }
   else
      ++cbeep_count;
}

Depending on how your code is structured, you may not have to loop
on this 'int_happened' variable everywhere in your code, but ...!


srini@ultra.com