[comp.sys.atari.st] Mfpint

ACPS5589@RYERSON.BITNET (George Borges) (01/31/89)

I need help.

How can I install my own interrupt routine (in C) for the Ring Indicator
line and the Carrier Detect line?  Currently, my routines DO execute but
the program bombs (usually two or three of them) immediately afterwards.

Can anyone lead me to a source of info or post a small example program
to demonstrate how Mfpint() works?

Thx,
George.

apratt@atari.UUCP (Allan Pratt) (02/01/89)

In article <8901302240.AA10005@ucbvax.Berkeley.EDU>
ACPS5589@RYERSON.BITNET (George Borges) writes:

> How can I install my own interrupt routine (in C) for the Ring Indicator
> line and the Carrier Detect line?  Currently, my routines DO execute but
> the program bombs (usually two or three of them) immediately afterwards.

You can't do it in C unless your compiler or library have some tricks. 
Specifically, C routines end with RTS (return from subroutine) while
interrupt handlers need to end with RTE (return from exception).  MWC
has a library call, I think, which arranges for the calling procedure to
end with RTE rather than RTS -- I think it plays games with the stack to
achieve this.  Check their documentation. 

However, the problem goes even deeper.  Interrupt handlers must not
change any registers (except SR; that's on the exception frame).  Most C
compilers can't be told to save & restore *all* the registers they use. 

Usually, if I *really* want to have the exception handler in C, I have a
little assembly language to save registers, call the C procedure, then
restore the registers and RTE:

	_ring:	movem.l	d0-d2/a0-a2,-(sp)	; alcyon clobbers these
		jsr	_myring
		movem.l	(sp)+,d0-d2/a0-a2
		rte

Then use "extern int ring()" and "Mfpint(xx,ring)" to install it.

Better still, do the installation in assembly, so you can read & store
the old handler's address, and restore it when you leave.  Mfpint
(foolishly) doesn't return the old value of the vector, so you can't use
it to save & restore things. 

============================================
Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else.	  ...ames!atari!apratt

ACPS5589@RYERSON.BITNET (George Borges) (02/14/89)

----------------------------Original message----------------------------
     Thanks to those two folks who replied to me.  However, I *still*
cannot get Mfpint() to work properly.  Perhaps I am somewhat dense.
Here's what I'm trying to do:

....
Jenabint( 14 );    /* enable ring indicator interrupt on MFP chip */
Mfpint( 14, interrupt );  /* set vector to point to my function */

... print stuff on screen in endless loop....

interrupt()
{
     setrte();   /* in MWC 2.0 .. return from exception rather than rts */
     Cconws("RING!!!");
}
.....

And that's basically it.  I disable the interrupt after it occurs.
The interrupt routine *does* execute but my program bombs (usually 2 or 3
of them).

Can a knowledgeable soul help a fellow cretin in need?  A fragment of working
C code would be most appreciated.  Thanx

George Borges   <ACPS5589@RYERSON.BITNET>

dag@per2.UUCP (Daniel A. Glasser) (02/16/89)

In article <8902140121.AA03228@ucbvax.Berkeley.EDU>, ACPS5589@RYERSON.BITNET (George Borges) writes:
> ----------------------------Original message----------------------------
>      Thanks to those two folks who replied to me.  However, I *still*
> cannot get Mfpint() to work properly.  Perhaps I am somewhat dense.
> Here's what I'm trying to do:
> ....
> Jenabint( 14 );    /* enable ring indicator interrupt on MFP chip */
> Mfpint( 14, interrupt );  /* set vector to point to my function */
> 
> ... print stuff on screen in endless loop....
> 
> interrupt()
> {
>      setrte();   /* in MWC 2.0 .. return from exception rather than rts */
>      Cconws("RING!!!");
> }
> 
> And that's basically it.  I disable the interrupt after it occurs.
> The interrupt routine *does* execute but my program bombs (usually 2 or 3
> of them).
> 
> George Borges   <ACPS5589@RYERSON.BITNET>

Okay, here's the straight dope from the dope who wrote the setrte function
for MWC...

You cannot safely use GEMDOS functions from within an interrupt routine.
GEMDOS is not reentrant.  The docs have the misleading language saying that
the GEMDOS functions cannot be called recursively, but what they mean is that
you cannot reenter GEMDOS while a GEMDOS function is in progress.

When you trap to GEMDOS to do the Cconws you are entering GEMDOS.  The
GEMDOS trap handler puts the current context information into a fixed
location, sets its own stack to another fixed location, and does what
you asked.  If a GEMDOS function is active when this happens, the return
context gets overwritten as does the GEMDOS stack.

This is not what you, or anybody else, wants.

Although, technically, you can call BIOS and XBIOS functions from interrupt
level routines, it is not a good idea in general.  It is better to set flags
for the application "user-mode" routines to deal with.

The example in the MWC manual does use Cconws, but it is an example of how
to catch a processor exception which will not occur in the execution of any
GEMDOS routines (that I know of).

Here is an example of how to use setrte() on an interrupt based on some
external interrupt:

short ringing;

main()
{
	...
	Mfpint(14, interrupt);
	ringing = 0;
	Jenabint(14);

	for (;;) {
		...
		while (ringing--) {		/* don't wory about ints. */
			Cconws("Ring!!!");	/* we'll catch it next time */
		}
		...
	}

	Jdisint(14);
}

interrupt()
{
	setrte();
	++ringing;	/* Atomic update	*/
}

I've not had a chance to check this out, but it should work.

Note also that your stack space is limited when you are in an interrupt
routine.  You don't know what you may be interrupting.  Don't waste time
in a interrupt routine.

					Daniel Glasser
-- 
 _____________________________________________________________________________
    Daniel A. Glasser                           One of those things that goes
    uwvax!persoft!dag                           "BUMP!!!(ouch)" in the night. 
 ---Persoft, Inc.---------465 Science Drive-------Madison, WI 53711-----------

blochowi@cat42.CS.WISC.EDU (Jason Blochowiak) (02/16/89)

	It's been awhile since I've dealt with the low level stuff on the ST,
but some general things:

	When enabling an interrupt, you almost always initialize the vector
before you enable the interrupt. So, in this case, you'd do:

{
	Mfpint(14,interrupt);
	Jenabint(14);
}

	Also, it makes life easier if you use the constants included in the
header files, e.g. "Mfpint(RING_INT,interrupt" (this is not a real constant,
just an example).

	One possible reason for the routine bombing: You say that in your
"other" routine (we'll call it "loop()"), you "...print stuff on screen in
endless loop...". You then also try to print from within your interrupt
routine. Perhaps there's a conflict? Even if there isn't, it'd probably be
cleaner to set a flag, and then have loop() check it. So, we have:

	int	rings;

loop()	/* Set the interrupt up & loop	*/
{
	rings = 0;		/* Initialize the ring count	*/
	Mfpint(14,interrupt);	/* Install my ring handler	*/
	Jenabint(14);		/* Enable interrupt		*/

	for (;;)		/* Loop forever			*/
	{
		if (!rings)	/* If no rings, say so...	*/
			printf("No rings...\n");
		else		/* Otherwise, show # of rings	*/
		{
			printf("There've been %d rings\n",rings);
			rings = 0;
		}
	}
}

interrupt()
{
	setrte();	/* MWC's function so we can rte instead of rts	*/
	rings++;	/* Increment the ring count			*/
}

	I didn't use Cconws because I didn't feel like dealing with printing
a decimal conversion... Also, note that this code chunk doesn't deal with what
would happen if there was a ring after the printf() in the else {}, but before
the "rings = 0" statement - it would lose a ring.

	I hope this helped...
 ------------------------------------------------------------------------------
		Jason Blochowiak (blochowi@garfield.cs.wisc.edu)
			"Not your average iconoclast..."
 ------------------------------------------------------------------------------

sommel@wn2.sci.kun.nl (Ron Sommeling) (02/17/89)

In article <825@per2.UUCP>, dag@per2.UUCP (Daniel A. Glasser) writes:
> 
> Although, technically, you can call BIOS and XBIOS functions from interrupt
> level routines, it is not a good idea in general.  It is better to set flags
> for the application "user-mode" routines to deal with.

No, you can't safely call any BIOS or XBIOS function from within an
interrupt. Take a look at the trap 13/14 trap-handler! Some registers
are copied to an area pointed to by saveptr and then saveptr is updated.
If an interrupt occurs after saving some registers in this area but
before saveptr is updated, then the saved registers will be overwritten
if you call BIOS/XBIOS routines in that interrupt. I can't see why
Atari decided to save the registers in the area pointed to by saveptr
instead of on the (supervisor)stack. If they did the latter then
BIOS/XBIOS would be reentrant.

						Ron Sommeling

email: sommel@wn2.sci.kun.nl

apratt@atari.UUCP (Allan Pratt) (02/23/89)

In article <336@wn2.sci.kun.nl> sommel@wn2.sci.kun.nl (Ron Sommeling) writes:
> In article <825@per2.UUCP>, dag@per2.UUCP (Daniel A. Glasser) writes:
> > 
> > Although, technically, you can call BIOS and XBIOS functions from interrupt
> > level routines, it is not a good idea in general.  It is better to set flags
> > for the application "user-mode" routines to deal with.
> 
> No, you can't safely call any BIOS or XBIOS function from within an
> interrupt.

That is not the case.  You CAN call BIOS or XBIOS routines from
interrupts.  The way to do it is documented (in the documentation, of
all places, not the disassembly!).  You subtract 46 ($2e) from the
system variable 'savptr' which is at $4a2.  THEN you use the TRAP
instruction.  When the trap returns, you add $2e to savptr again. 

With this approach, one can write a BIOS call in C, because when the
dispatcher calls the [X]BIOS function, the arguments are right on the
stack, just as virtually all C compilers expect them to be.  A couple
of BIOS calls *are* written in C, notably Rwabs().

I don't know that I'd have made this decision if I'd designed the BIOS,
but it does work, and it *is* reentrant, if you do what the
documentation says to do.

THIS IS NOT AN ENDORSEMENT OF USING BIOS FROM INTERRUPT LEVEL.  If you
find your program is doing that, re-think your program.  There are not
very many reasons to call BIOS like that, and a number of reasons not
to: speed is the most important.  Furthermore, NEVER ATTEMPT PRINTER OR
DISK I/O FROM INTERRUPT LEVEL, because aside from the time it will take,
timers and things aren't running. 

============================================
Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else.	  ...ames!atari!apratt