(c)  Copyright 1989 Commodore-Amiga, Inc.   All rights reserved.
The information contained herein is subject to change without notice, and 
is provided "as is" without warranty of any kind, either express or implied.  
The entire risk as to the use of this information is assumed by the user.


Disabling Sprite DMA From a Vertical Blank Interrupt

by Adam Keith Levin

An application program such as a screen blanking utility may need
to  disable sprite DMA in order to temporarily turn off sprites. 
The graphics  macro OFF_SPRITE in the graphics library can be
used for this.  However,  if sprite DMA is turned off with
OFF_SPRITE at the exact instant that a  sprite is being rendered,
the sprite image may be "smeared" vertically on  the display. The
SpriteSwitch() program listed below provides a single function
call which uses a vertical blank interrupt to either enable or
disable sprite DMA cleanly, preventing this smearing.


When You Don't Need SpriteSwitch()

If you need to hide the mouse pointer only for your program, not
for the  entire system, you do not need this routine.  Instead
call Intuition's SetPointer() function with the address of a
window belonging to your program, and a definition for a
transparent pointer image.  For example:

 SHORT height=1, width=16, x_offset=0, y_offset=0;
 USHORT *pointer_data;
 struct Window *this_window;    /*  A valid window pointer.  */

 /*  The smallest possible pointer definition, initialized to
     zeros.  */
 pointer_data =	(USHORT *)AllocMem(12L,MEMF_CHIP|MEMF_CLEAR|MEMF_PUBLIC);
 if (pointer_data)
	SetPointer(this_window, pointer_data, height, width, x_offset,
	y_offset);
 .
 .
 . 

if (pointer_data)
	{
		ClearPointer(this_window);
		FreeMem(pointer_data);
	}


Using this method, the mouse-pointer will disappear whenever
this_window is made active, and reappear when any other window is
made active.  (If you  are opening a window solely for the
purpose of turning off the pointer consider opening a BORDERLESS,
BACKDROP window, which is unobtrusive.)


When You DO Need SpriteSwitch()

Shutting off Sprite DMA will prevent all sprites from being
rendered,  for the entire system.  This is recommended only for
screen-and-mouse  blanker programs or programs that temporarily
take over the system  while running.

In general, applications should use SpriteSwitch() as a
substitute for the graphics macro OFF_SPRITE.  The OFF_SPRITE
macro might cause the  sprite image to smear down the screen if
it happens to take effect while  the sprite is being rendered.

The SpriteSwitch() function is listed below.  An example call is
also  given in the main routine which is provided here only to
exercise  SpriteSwitch(). To shut off sprite DMA, you call
SpriteSwitch() with the  constant SWITCH_OFF.  To turn on sprite
DMA, call SpriteSwitch() with SWITCH_ON.

SpriteSwitch() works by adding a server to the chain of VBLANK
interrupt servers with the AddTOF() system function.  The added
server simply  calls the graphics macro OFF_SPRITE or ON_SPRITE
as appropriate.  Since these  macros are called from within the
VBLANK interrupt, there is no chance that  the sprite is being
rendered at the same time the macro is called, hence,  there is
no way for the sprite to smear.  After the sprite DMA is disabled
(or enabled) SpriteSwitch() calls the system function RemTOF() to
clear the  server from the chain of VBLANK servers.


Important Note

The graphics library's VBLANK server depends upon address
register A0 containing a pointer to the custom chips.  If you add
a server at a priority of ten (10) or greater, you must preserve
the contents of A0 across the call, or otherwise insure that A0
has the correct value. Since this is difficult to do when writing
in C, it is recommended that  VBLANK servers for applications
such as SpriteSwitch() are given a priority of nine (9) or less.


/*  SpriteSwitch
    Cleanly turns Sprite DMA on or off using a Vertical-Blanking interrupt.

    Revision History:
    v1.0	Adam Keith Levin of CATS	Created program.

    Lattice (v5.04) make:  LC -b1 -cfist -L -v -y SpriteSwitch.c
*/

/*  Amiga System includes.  */
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfxmacros.h>
#include <graphics/graphint.h>
#include <hardware/custom.h>
#include <hardware/dmabits.h>
#include <libraries/dos.h>

/*  Lattice-specific includes.  */
#ifdef LATTICE
#include <proto/all.h>
#include <stdlib.h>
#endif

/*  Amiga.lib prototypes.  */
VOID AddTOF(struct Isrvstr *, APTR, APTR);
VOID RemTOF(struct Isrvstr *);

/*  SpriteSwitch request flags.  */
#define SWITCH_ON     0    /*  Turn Sprite DMA on.  */
#define SWITCH_OFF    1    /*  Turn Sprite DMA off.  */

/*  Internal indicator that server has completed.  */
#define SWITCH_DONE  ~0    /*  DO NOT USE!  */

/*  Error codes.  */
#define ERR_NONE    0    /*  No problems encountered.  */
#define ERR_MEMORY  1    /*  Not enough memory.  */
#define ERR_SIGNAL  2    /*  Unable to allocate a signal bit.  */

/*  The Switch_Data structure is used to pass data from the
    spriteSwitch() routine to the spriteSwitchServer() routine.
*/
struct Switch_Data
{
    UWORD state;        /*  One of the SpriteSwitch request flags.  */
    struct Task *task;  /*  The task to be signalled.  */
    ULONG signal;       /*  The signal to use.  */
};


/*  The SpriteSwitch Server.
    Calling "OFF_SPRITE" or "ON_SPRITE" during the Vertical-Blanking Interval
    assures that Sprite DMA will not be shut off while a Sprite is being
    rendered, so their imagery will not get "smeared".
*/
int spriteSwitchServer(struct Switch_Data *switch_data)
{
extern struct Custom far custom;    /* Needed by the _SPRITE macros. */

    /*  Only run through this once.  */
    if (SWITCH_DONE != switch_data->state)
    {
        switch((int)switch_data->state)
        {
            case (SWITCH_OFF):
                OFF_SPRITE;
                break;
            case (SWITCH_ON):
                ON_SPRITE;
                break;
            default:
                break;
        }

        /*  Use the state variable to show that the server has already run.  */
        switch_data->state = SWITCH_DONE;

        Signal(switch_data->task, switch_data->signal);
    }

    return(NULL);
}


/*  SpriteSwitch.
    Called with a UWORD containing one of the Sprite request flags.
    It initializes a Switch_Data structure with said request flag,
    the task's address (from FindTask(NULL)) and a signal bit (from
    (AllocSignal(-1)).  It installs the SpriteSwitch Server, Wait()s
    for it to signal that the request was completed, and removes it.
    Returns the constant ERR_NONE if successful, ERR_MEMORY if unable
    to allocate enough memory, and ERR_SIGNAL if unable to allocate
    a signal bit.
*/
UWORD spriteSwitch(UWORD desired_state)
{
BYTE signal_bit;
UWORD return_value;
/*  The Interrupt Server structure.  When memory is allocated for it,
    all it's fields will be set to NULL, including the priority.
    If a priority other than zero were needed, it could be set
    before the AddTOF() call with:
    isrvstr.is_Node.ln_Pri = (BYTE)my_priority;
    Do not use a priority greater than 9!  (Refer to article for reason).
*/
struct Isrvstr *isrvstr = NULL;

/*  The Switch_Data structure.  */
struct Switch_Data *switch_data = NULL;

    isrvstr = (struct Isrvstr *)
        AllocMem((ULONG)sizeof(struct Isrvstr), MEMF_CLEAR|MEMF_PUBLIC);
    if (NULL != isrvstr)
    {
        switch_data = (struct Switch_Data *)
            AllocMem((ULONG)sizeof(struct Switch_Data), MEMF_PUBLIC);
        if (NULL != switch_data)
        {
            /*  Attempt to allocate a signal bit.  */
            signal_bit = AllocSignal(-1L);
            if (-1 != signal_bit)
            {
                /*  Initialize the Switch_Data structure.  */
                switch_data->state = desired_state;
                switch_data->task = FindTask(NULL);
                switch_data->signal = (1L << signal_bit);

                /*  Add the server.  */
                AddTOF(isrvstr, (APTR)spriteSwitchServer, (APTR)switch_data);

                /*  Wait for it to signal that it has completed.  */
                Wait(1L << signal_bit);

                /*  Remove the server.  */
                RemTOF(isrvstr);

                /*  Free the signal bit.  */
                FreeSignal(signal_bit);

                return_value = ERR_NONE;
            }
            else
                return_value = ERR_SIGNAL;

            FreeMem((VOID *)switch_data, (ULONG)sizeof(struct Switch_Data));
        }
        else
            return_value = ERR_MEMORY;

        FreeMem((VOID *)isrvstr, (ULONG)sizeof(struct Isrvstr));
    }
    else
        return_value = ERR_MEMORY;

    return(return_value);
}


/*  stdio.h is needed by the main routine.  */
#include <stdio.h>

/*  This test routine exercises spriteSwitch() by turning off
    Sprite DMA for five seconds and then turning it back on.
*/
VOID main(VOID)
{
UWORD exit_value;


/*  NOTE:  You may need to OpenLibrary() the graphics library here if your
           compiler complains of an unresolved reference to "GfxBase".
           This is because the module in amiga.lib which contains AddTOF()
           and RemTOF() (which do not require the graphics library) also
           contains calls to routines which do require the graphics library.
*/

    exit_value = spriteSwitch(SWITCH_OFF);
    if (ERR_NONE == exit_value)
    {
        printf("Sprite DMA shut off.\n");

        Delay(5L * TICKS_PER_SECOND);

        exit_value = spriteSwitch(SWITCH_ON);
        if (ERR_NONE == exit_value)
            printf("Sprite DMA turned back on.\n");
        else
        {
            printf("Unable to allocate signal bit.\n");
            printf("Sprite DMA not turned back on.\n");
        }
    }
    else
    {
        printf("Unable to allocate signal bit.\n");
        printf("Sprite DMA not shut off.\n");
    }

    exit((int)exit_value);
}



