
			Legal Interrupts
			~~~~~~~~~~~~~~~~
				A feeble attempt by M.Meany


 A slightly misleading heading. I am only going to cover adding a server to
the vertical blank interrupt chain, not discuss all 15 types of interrupt
that can occur.

 So many programs set up vertical blank interrupts by writing a value 
straight into the 68000's level 3 interrupt vector that you could be
excused for thinking this is the correct method.

 Well strictly speaking, it is! However, the Amiga is a multitasking
machine and several tasks may want to set up interrupts that go bang during
the blanking period. For this reason, exec maintains a list of all the
interrupt servers to call when an interrupt takes place. This way many
interrupt routines get called.

 For the uninitiated, an interrupt is a signal sent to the processor
telling it to suspend current operation and deal with a specific request
immediately. For instance, the Blitter can generate an interrupt. The Amiga
can be happily multitasking along when the Blitter finishes an operation
and send an interrupt to the processor. The processor will switch to
Supervisor mode and jump to a predetermined routine that is waiting for
just such an event to occur. This routine may just start another Blitter
operation in progress and exit, allowing the processor to continue where it
left off.

 Though I've used the Blitter in the above example, there is a more
frequently used interrupt. This is the vertical blank interrupt and occurs
after each generation of the video display, approx. once every fifteth of a
second. This interrupt is used to maintain flicker free displays in games
and demos, as well as acting as a general purpose timer for such things as
'Tracker replayers'.

 So how do you go about adding an interrupt to the above list? This is
really quite straight forward. First initialise an Interrupt structure,
which is just an extended node structure, then call AddIntServer() passing
the address of the Interrupt structure. When you want the interrupt to
cease call RemoveIntServer() passing the Interrupt structure.

 AddIntServer( intNumber, Interrupt )
		 d0	     a1

 RemIntServer( intNumber, Interrupt )
		 d0	     a1

 In both the above cases, the intNumber should be set to INTB_VERTB ( defined 
in hardware/intbits.i ) for a vertical blank interrupt server.

 Preparing The Interrupt Structure
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Here is the Interrupt structure:

is_Node		Struct	Node
is_Data		LONG
is_Code		LONG

is_Node		This is a standard node structure. Note that the nodes
		priority is used in deciding the order that interrupts are
		called. The higher the priority, the sooner the interrupt
		server will be called.
		You can also give the interrupt a name if you so wish. In
		this way, multiple copies of the task that started the
		interrupt will know that the interrupt already exsists.

is_Data		A pointer that is passed to the interrupt server in address
		register a1. This allows you to specify a data area that
		your server can access using a1 ithout having to lea the
		address.

is_Code		A pointer to the server routine itself.

 For example, to set up an Interrupt structure and start the interrupt
running you could use the following code:

		lea		MyInt,a1		a1->Interrupt struct
		move.b		#5,LN_PRI(a1)		priority = 5
		move.l		#IntData,is_Data(a1)	address of data
		move.l		#IntCode,is_Code(a1)	address of server
		moveq.l		#INTB_VERTB,d0		interrupt number
		CALLEXEC	AddIntServer		start it!

 Your server will be installed and called every vertical blank from this
point onwards.

 Interrupt Server Conventions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Interrupt servers must follow certain conventions, vertical blank servers
especially so. Here is an outline of what you should and should not do with
a few tips thrown in:

 On Starting
 ===========
 Register a1 will point to the address stored in the is_Data field of your
Interrupt structure. Use this! It saves time if you reference any variables
from this address.

 Scratch Registers
 =================
 The following registers can be treated as 'scratch' registers:

 d0,d1,*a0,a1,a5,a6

 a0 can be treated as scratch providing the priority of your server is less
than 10. There is a bug in the graphics interrupt server that always expects
the address $dff000 to be in a0. The graphics server has priority 10, so if
you add a server of higher priority remember to leave this value in a0 when
you finish.

 On Finishing
 ============
 Always leave the processors Z flag set on exiting your server. Failure to
do this may result in other important servers not being called. The flag
can be set with the following instruction:

	moveq.l		#0,d0

 End your server with an RTS, not an RTE. The exec controller is the true
handler running in supervisor mode, your server is treated as a subroutine
of this handler!

 An Example
 ~~~~~~~~~~
 By way of an example, how about a NoiseTracker replayer running off a
vertical blank interrupt. Just to make sure that the graphics gets priority
over the replayer, it will be given a priority of 9.

 The program will take the following form:

 1/ Call mt_init.
 2/ Set up interrupt and add it to system.
 3/ Wait for LMB to be pressed
 4/ Remove interrupt
 5/ Exit

 Here is the code, minus the replayer which you will find in the source
file.

		*********************************

		incdir		include2.0:include/
		include		exec/exec_lib.i
		include		hardware/intbits.i

; initialise music data

Start		jsr		_init			Initialise data

		lea		MyInt,a1		a1->Interrupt struct
		move.b		#9,LN_PRI(a1)		priority = 9
		move.l		#IntCode,IS_CODE(a1)	address of server
		moveq.l		#INTB_VERTB,d0		interrupt number
		CALLEXEC	AddIntServer		start it!

wait		btst		#6,$bfe001		LMB pressed?
		bne.s		wait			loop if not
		
		moveq.l		#INTB_VERTB,d0		interrupt number
		lea		MyInt,a1		Interrupt struct
		CALLEXEC	RemIntServer		remove interrupt
		
		jsr		_end			stop music
		
		rts					exit

****************************************************************************
*			The Interrupt Server				   *
****************************************************************************

IntCode		movem.l		a0-a4/d2-d7,-(a7)	Save all registers
		jsr		_music			play routine
		movem.l		(a7)+,a0-a4/d2-d7	Bring back registers
		moveq.l		#0,d0			clear Z flag
		rts					exit


****************************************************************************
*			   Data Section					   *
****************************************************************************

MyInt		ds.l		IS_SIZE			Interrupt structure

			*************************

 To Dave Edwards utter disgust I have used a 'busy wait' loop in the above
example. There is a reason for this, it's quick and easy requiring no set
up.

 There is no reason why this method of adding an interrupt server to the
vertical blank list is not used in demos. Most demos that replace the
interrupt vector at $6c directly usualy call the routine pointed to by this
vector any how, so there is no real gain in 'doing the dirty'. To ensure
your interrupt is called right at the start of the list, give it a priority
of 100 or so.

 As an extra bonus, you can add more than one server. Each server would
then check a flag set by the program proper to see if it is required. If
not it would exit immediately allowing other servers in the chain to get in
on the act. Infact servers could be added and removed during the progress
of a program as required, something you don't see to often in 'doing the
dirty' programs. Food for thought....

					MM.


