// ------------------------------------------------------------------------------------------------
// MyInt.c
//
// COPYRIGHT (C) SEGA OF AMERICA, INC. 1998
// All rights reserved.
//
// Information and source code samples contained herein are 
// provided "as-is", without representations or warranties, and 
// are subject to change without notice.
//
// SEGA OF AMERICA, INC. cannot support modifications or 
// derivative works created from the sample source code provided. 
// Developers may use, reproduce, and modify portions or entire 
// sections of the sample source code for the purposes of creating
// Dreamcast applications.  
//
// Sample source code contained herein may not be appropriate 
// for use under all circumstances.  Please consult Developer 
// documentation provided along with your Dreamcast unit for
// additional information and suggestions.
//
//
// Author: Ralph "Cooksey" Thomas
//
// Tabs:	4
//
// Rev:		05/28/99	File Originated
//			09/28/99	Updated int handler to the G2 compliant version.
// ------------------------------------------------------------------------------------------------

#include <shinobi.h>
#include <sg_chain.h>
#include <am.h>
#include "MyInt.h"

extern volatile KTU32 		gLastSH4ReadIndex;
extern AC_MESSAGE_MODE		gAcMessageMode;
extern volatile KTU32		gAcPollingRate;
extern volatile KTU32		gAcPollingRateCounter;

KTU32 MyAddChain(KTS16 chainTo,AC_ARM_INTERRUPT_HANDLER theInterruptHandler,KTU32 priority,void *theInterruptId)
{
SYCHAIN  theChainId		=	0;

theChainId	=	syChainAddHandler(chainTo,theInterruptHandler,priority,theInterruptId);

return theChainId;
}

void MyRemoveChain(KTU32 theChainId)
{
syChainDeleteHandler(theChainId);
}

void MyCallbackHandler(volatile KTU32 messageNumber)
{
volatile KTU32			theOwner	=	0;
volatile AM_STREAM_PTR	theStream	=	KTNULL;
volatile AM_SOUND_PTR	theSound	=	KTNULL;
AM_USER_CALLBACK		userCallback	=	KTNULL;

if(messageNumber > 0xff)
	{
	amErrorSet(AM_OUT_OF_RANGE_PARAMETER,"_amVoiceIssueCallback - messageNumber is out of range");
	return;
	}

// this fixes problems with the user int message numbers
if(messageNumber >= gAcSystem.maxVoices && messageNumber <= 0xff)
	{
	// this range of message numbers is for client usage, if you want to have the callback
	// do special things here is where you will catch the message...
	return;
	}

// ----- end parameter testing -----

if(gAmVoicePool.channelType[messageNumber]==AM_ONESHOT_VOICE)
	{
	if(gAmVoicePool.channels[messageNumber]==KTFALSE)
		return;

	// get the owner
	theOwner	=	(KTU32)gAmVoicePool.channelOwner[messageNumber];

	theSound	=	(AM_SOUND_PTR)theOwner;

	// 7/22ish Cary@luxoflux.com reported this bug, user callback 
	// was called prior to the voice being free, I changed the order 
	// here to fix that.

	// pop off the system callback
	if(gAmVoicePool.systemCallback != KTNULL)
		gAmVoicePool.systemCallback(messageNumber);

	// now the channel is closed but the voice pool logic is still
	// open, preserve the userCallback and clear the voice pool
	// structure

	if(gAmVoicePool.userCallback[messageNumber] != KTNULL)
		userCallback	=	gAmVoicePool.userCallback[messageNumber];
	
	// now clear the channel
	_amVoiceClearChannel(messageNumber);

	// if a user callback was installed pop it off too
	// at this point the voice channels (physical and logical)
	// are both free and clear and we are on the way out the door.
	if(userCallback != KTNULL)
		userCallback(messageNumber);

	return;
	}
else if(gAmVoicePool.channelType[messageNumber]==AM_MIDI_VOICE)
	{
	// 7/22ish Cary@luxoflux.com reported this bug, user callabck 
	// was called prior to the voice being free, I changed the order 
	// here to fix that.

	// pop off the system callback
	if(gAmVoicePool.systemCallback != KTNULL)
		gAmVoicePool.systemCallback(messageNumber);

	// now the channel is closed but the voice pool logic is still
	// open, preserve the userCallback and clear the voice pool
	// structure

	if(gAmVoicePool.userCallback[messageNumber] != KTNULL)
		userCallback	=	gAmVoicePool.userCallback[messageNumber];
	
	// now clear the channel
	_amVoiceClearChannel(messageNumber);

	// if a user callback was installed pop it off too
	// at this point the voice channels (physical and logical)
	// are both free and clear and we are on the way out the door.
	if(userCallback != KTNULL)
		userCallback(messageNumber);

	return; 
	}
else
	{
	if(gAmVoicePool.streamIsr[messageNumber] != KTNULL)
		{
		// get the owner
		theOwner	=	(KTU32)gAmVoicePool.channelOwner[messageNumber];

		theStream	=	(AM_STREAM_PTR)theOwner;

		// invoke the stream data server ISR
		gAmVoicePool.streamIsr[messageNumber](theOwner);
		
		if(theStream->isrCounter == theStream->interruptsTillEnd)
			{
			if(gAmVoicePool.userCallback[messageNumber] != KTNULL)
				gAmVoicePool.userCallback[messageNumber](messageNumber);
			
			theStream->serviceFlag		=	KTFALSE;
			}
		}

	return;
	}

return; // should never exit from here...
}


void MyArmInterruptHandler(void *theInterruptId)
{
volatile KTU32 *		intWriteCursor		=	KTNULL;
volatile KTU32			currentOffset		=	0;
volatile KTU32			Arm7WriteIndex		=	0;
volatile KTU32			startOfArray		=	0;
volatile KTU32			nbrOfVoices			=	0;
volatile KTU32			oldImask			=	0;
volatile KTU32 *		target				=	(volatile KTU32 *)&gAcSystem.intArray[0];
volatile KTU32 *		source				=	gAcSystem.intArrayStartPtr;
volatile KTU32			i					=	0;
volatile KTU32			fifoCount			=	0;

if(((KTU32)theInterruptId) != AC_ARM_INTERRUPT_HANDLER_ID && ((KTU32)theInterruptId) != AC_ARM_POLLING_HANDLER_ID)
	return;

// adjustable polling rate
if(gAcMessageMode==AC_POLL_MESSAGES)
	{
	if(gAcPollingRateCounter > gAcPollingRate)
		{
		gAcPollingRateCounter = 0;
		}
	else
		{
		++gAcPollingRateCounter;
		return;
		}
	}

// ----- Start G2 Countermeasures -----

oldImask	=	get_imask();			// get the old imask value

set_imask(15);							// disable interrupts

acDmaSuspendAll();					// suspend all DMA

acG2FifoCheck();	// wait for the AICA/HOLLY FIFO's to empty

// ------------------------------------

// reset the ARM interrupt status if it is not polling
if(((KTU32)theInterruptId) != AC_ARM_POLLING_HANDLER_ID)
	{
	*gAcSystem.intResetFlag	=	0x20;
//	acSystemResetArmInterrupt(); does a G2WriteLong()
	}

startOfArray = (KTU32)gAcSystem.intArrayStartPtr;

// assert(gAcSystem.maxVoices == 0x40); // Must be binary value

//nbrOfVoices		=	(KTU32)gAcSystem.maxVoices - 1;
nbrOfVoices		=	63;

// get the int array write cursor register
intWriteCursor	=	(KTU32 *)(gAcSystem.soundMemoryBaseAddress + AC_INT_ARRAY_OFFSET);

// get the current write cursor offset
currentOffset	=	(KTU32)(*intWriteCursor);
//acG2ReadLong(intWriteCursor,&currentOffset);

// moved this to above the int msg array read
Arm7WriteIndex	=	currentOffset - gAcSystem.intArrayStartOffset; // byte array in terms of ptrs

// if polling, return if no messages posted
if(((KTU32)theInterruptId) == AC_ARM_POLLING_HANDLER_ID)
	{
	if(gLastSH4ReadIndex==Arm7WriteIndex)
		{
		// no messages pending

		// ----- End G2 Countermeasures -----

		acG2FifoCheck();	// wait for the AICA/HOLLY FIFO's to empty

		acDmaResumeAll();					// resume all DMA

		set_imask(oldImask);				// enable interrupts

		// ------------------------------------

		return;
		}
	}

	
// get the interrupt array
//acG2Read((volatile KTU32 *)&gAcSystem.intArray[0],(volatile KTU32 *)gAcSystem.intArrayStartPtr,(KTU32)64);
while(i < 16) 
	{
	*(target+i)=*(source+i);												// write a long
	
	++i;																	// inc dword counter

	++fifoCount;															// inc FIFO counter
	
	if(fifoCount > 7)														// is FIFO full ?
		{										
		fifoCount	=	0;													// clear FIFO counter

		acG2FifoCheck();	// wait for the AICA/HOLLY FIFO's to empty
		}										
	}

// ----- End G2 Countermeasures -----

acG2FifoCheck();	// wait for the AICA/HOLLY FIFO's to empty

acDmaResumeAll();					// resume all DMA

set_imask(oldImask);				// enable interrupts

// ------------------------------------

// dispatch the messages
while(gLastSH4ReadIndex != Arm7WriteIndex) 
	{
	gAcCallbackHandler(gAcSystem.intArray[gLastSH4ReadIndex]);
	
	gLastSH4ReadIndex++;
	
	gLastSH4ReadIndex &= nbrOfVoices;
	}
	
return;
}


