[comp.sys.m68k] 68K interrupt problem

schmitz@fas.ri.cmu.edu (Donald Schmitz) (11/27/89)

I tried to mail this but our mailer gave up. Hope it is of general 
interrest:

>The scenario:
>	I have a main routine and a counter interrupt handler both
>which were written in C. I went into the assembly source for the
>interrupt handler and changed RTS to RTE and placed an instruction to
>save all the registers at the beginning of the routine and restore
>them at the end. I'm also a beginner at 68000 assembly coding so I'm
>not sure these are the correct commands.
>
>		movem.l d0-d7/a0-a6,-(a7)	/* save registers */
>		disable_counter_interrupts
>		/* do any processing here
>		enable_counter_interrupts
>		movem.l (a7)+,d0-d7/a0-a6	/* restore registers */
>		RTE
>
>When I have a very simple main routine with just a printf statement in it,
>this works fine. I have a counter interrupt occuring every 200ms and
>toggling a status light. When I add processing in the main loop
>however, the interrupts seem to occur about 3 or 4 times and then the
>processor seems to be off in the weeds somewhere. Whats going on here?

Dave, 

It looks like you have done everything right, however it is hard to
tell what your main program is doing to the state of processor, probably
in some of the libraries like printf.  One thing to try is to set the
status register interrupt mask to 7 as soon as you enter the interrupt
handler, ie. make the first instruction be:

	movw	#2700,sr

This will prevent your interrupt handler from being interrupted, by
something like the uart being used by printf.  This approach can lead to
reduced performance if you are servicing lots of interrupts (low priority
handlers lock out high priority ones), but it makes understanding what is
happening much easier.  Also, you should not have to save/unsave all of the
registers, with some/(most ?) 68K C compilers you only have to save/unsave
d0, d1, a0, and a1 in the code you patch in, the routine will save whatever
else it uses itself.

Next, are you sure the entire program is running in supervisor mode?  If you
aren't, the interrupt handler changes stack pointers on you, and you have to
be sure both stack pointers are pointing into valid, separate stack spaces
before you start everything.

Another thing, is this really a 68000?  68020/68881 machines need more state
saving because of the floating point unit.  If this is the case, let me know
and I'll send you sample code.

Finally, there is an old hack to make this more transparent to a C program,
it uses a bug/feature in  the C pre-processor that seems to be fairly
standard.  Create a file reentrant.h that contains the following (this
assumes the SUN C compiler/assembler, if you are using something different,
you may have to change the instruction mnemonics or the asm() syntax).  It
is hard to understand what this does, but look at the code it generates
and you will see what is going on, the macro generates a stub routine called 
by the exception/interrupt that in turn calls the C subroutine you write.

/* ===============REENTRANT FUNCTION CALLS ON THE 68000==============

Author:		V.R.Pratt
Date:		Sept. 7, 1980.

This package permits the use of the expression
	reentrant(fun)
in place of the usual way to begin a function definition, namely
	fun()

Invoking reentrant(fun) generates a self-contained function named
'fun' which pushes d0,d1,a0,a1 on the stack, calls '_fun', then
pops a1,a0,d1,d0 back off the stack and does an 'rte'
(return from exception).  

Example usage:

reentrant(KbdServ) {
    return(ACIA1Data&0177);
}

================================================================= */

#ifndef REENTRANT_H
#define REENTRANT_H
#define REENTRANT

#define reentrant(fun) reentrant_(fun,_/**/fun, __/**/fun)

#define reentrant_(fun,_fun,__fun)\
asm("	.text ");\
asm("	.globl	_fun");\
asm("_fun:");\
asm("	movw	#0x2700,sr");\
asm("	moveml	a0/a1/d0/d1,sp@-");\
asm("	jsr	__fun");\
asm("	moveml	sp@+,a0/a1/d0/d1");\
asm("	rte");\
int fun();\
_fun()

#endif REENTRANT_H

____
Now, in your C code, define your interrupt handler something like:

#include <reentrant.h>

reentrant(myhandler)
{
/* Your handler routine goes here */
}

And to set up the interrupt handler, do something like:

int * vec;

main()
{
/* set up interrupt vector */
   vec = (int *)(VECTOR * 4); /* get pointer into exception vector table */
   *vec = (int)myhandler;

/* now turn the interrupts on, and do the rest of the main stuff */
}

If you have trouble with any of this, try to mail me more details.

Don Schmitz  -  schmitz@fas.ri.cmu.edu