/***************************************************************************
*	NAME:  IRQ.C
**	COPYRIGHT:
**	"Copyright (c) 1992, by FORTE
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
*  CREATION DATE: 11/18/92
*--------------------------------------------------------------------------*
*     VERSION	DATE	   NAME		DESCRIPTION
*>	1.0	11/18/92		Original
***************************************************************************/

#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include "forte.h"
#include "gf1proto.h"
#include "osproto.h"

/* internal include files */
#include "gf1hware.h"
#include "dma.h"
#include "irq.h"
#include "gf1os.h"

/* #pragma inline */

extern IRQ_ENTRY _gf1_irq[];
extern DMA_ENTRY _gf1_dma[];
extern ULTRA_DATA _gf1_data;

extern void SaveSegRegs();
extern void GetSegRegs();

void
gf1_setvect( int int_number, PVI isr )
{
#ifdef METAWARE
SaveSegRegs();
/* set protected mode irq handler ... */
_setpvect(int_number,isr);
// _setrpvectp(int_number,isr);
#endif

#ifdef WATCOMC
/* set protected mode irq handler ... */
	union REGS	r;

	r.x.eax = 0x0205;
	r.h.bl = int_number;
	r.x.edx = FP_OFF (isr);
	r.w.cx = FP_SEG (isr);
	int386 (0x31, &r, &r);	/* set prot. vector thru DPMI */
#endif /* WATCOMC */

#ifdef MSOFTC
	asm push ds;
	asm lds dx,isr;
	asm	mov ax,int_number;
	asm	mov ah,0x25;
	asm	int 21h;
	asm	pop ds;
#endif

#ifdef BORLANDC
	asm push ds;
	asm lds dx,isr;
	asm	mov ah,0x25;
	_AL = int_number;
	asm	int 21h;
	asm	pop ds;
#endif

}

PVI
gf1_getvect( int int_number )
{
#ifdef METAWARE
return(_getpvect(int_number));
#endif

#ifdef WATCOMC
	union REGS	r;

	r.x.eax = 0x0204;
	r.h.bl = int_number;
	int386 (0x31, &r, &r);
	return ((PVI)MK_FP (r.w.cx, r.x.edx));
#endif

#ifdef MSOFTC
		asm push ds;
		asm	mov ax,int_number;
		asm	mov ah,0x35;
		asm	int 21h;
		asm	mov dx,es;
		asm	mov ax,bx;
		asm	pop ds;
#endif

#ifdef BORLANDC
		asm push ds;
		asm	mov ah,0x35;
		_AL = int_number;
		asm	int 21h;
		asm	mov dx,es;
		asm	mov ax,bx;
		asm	pop ds;
#endif
}

void
ReSetIrqHandlers(int gf1_irq,int midi_irq)
{
int temp_irq;

	temp_irq = gf1_irq;

	if (temp_irq != 0)
		{
		if (gf1_irq > 7)
			gf1_irq += 0x68;
		else
			gf1_irq += 0x08;

		gf1_setvect (gf1_irq,_gf1_data.old_gf1_vec);
		}

	if ((temp_irq != midi_irq) && (midi_irq != 0))
		{
		if (midi_irq > 7)
			midi_irq += 0x68;
		else
			midi_irq += 0x08;

		gf1_setvect (midi_irq,_gf1_data.old_midi_vec);
		}
}

void
SetIrqs(int gf1_irq,int midi_irq)
{
unsigned char val;

#ifdef METAWARE
	_gf1_data.gf1_sema4 = 0;
	_gf1_data.irq_pending = 0;
#endif

	if (gf1_irq != 0)
		{
		/* unmask gf1 interrupt */
		val = inp(_gf1_irq[gf1_irq].imr);
		val &= _gf1_irq[gf1_irq].mask;
		outp(_gf1_irq[gf1_irq].imr,val);

		/* send a specific EOI in case of pending interrupt */
		outp(_gf1_irq[gf1_irq].ocr,_gf1_irq[gf1_irq].spec_eoi);
		}

	if ((midi_irq != gf1_irq) && (midi_irq != 0))
		{
		/* unmask midi interrupt */
		val = inp(_gf1_irq[midi_irq].imr);
		val &= _gf1_irq[midi_irq].mask;
		outp(_gf1_irq[midi_irq].imr,val);
		
		/* send a specific EOI in case of pending interrupt */
		outp(_gf1_irq[midi_irq].ocr,_gf1_irq[midi_irq].spec_eoi);
		}

	/* Un-mask IRQ 2 from first controller if using 2nd controller */
	if (midi_irq > 7 || gf1_irq > 7)
		{
		val = inp(_gf1_irq[2].imr);
		val &= _gf1_irq[2].mask;
		outp(_gf1_irq[2].imr,val);
		/* send a specific EOI in case of pending interrupt */
		outp(_gf1_irq[2].ocr,_gf1_irq[2].spec_eoi);
		}
}


void
ResetIrqs(int gf1_irq,int midi_irq)
{
unsigned char val;

	/* unmask gf1 interrupt */
	if ((gf1_irq != 2) && (gf1_irq != 0))
		{
		/* turn mask bit back on ... */
		val = inp(_gf1_irq[gf1_irq].imr);
		val |= ~_gf1_irq[gf1_irq].mask;
		outp(_gf1_irq[gf1_irq].imr,val);
		}

	if ((midi_irq != 2) && (midi_irq != 0))
		{
		/* unmask midi interrupt */
		val = inp(_gf1_irq[midi_irq].imr);
		val |= ~_gf1_irq[midi_irq].mask;
		outp(_gf1_irq[midi_irq].imr,val);
		}

}

/* static */ void
handle_dma_tc(void)
{
DMA_ENTRY *tdma;
unsigned char val;

	/* First, check to see if we need to service dram dma terminal count */
	/* Note: This read also clears the pending irq ... */
	ENTER_CRITICAL;

	outp(_gf1_data.reg_select,DMA_CONTROL);
	val = inp(_gf1_data.data_hi);

	LEAVE_CRITICAL;

	if (val & DMA_IRQ_PENDING)
		{
		tdma = &_gf1_dma[_gf1_data.dram_dma_chan-1];

		if (tdma->flags & TWO_FLAG)
			{
			UltraDmaNext(tdma,FALSE);	/* handle cross over page */
			}
		else
			{
			tdma->flags &= ~DMA_PENDING;
			/* show foreground its done */
			_gf1_data.flags &= ~DRAM_DMA_BUSY;
			tdma->amnt_sent += tdma->cur_size;		/* accumulate totals */

			/* Call playback processing function.... */
			_gf1_data.dram_dma_tc_func();
			}
		}

	/* Now check recording's terminal count */
	/* Note: This read also clears the pending irq ... */
	ENTER_CRITICAL;

	outp(_gf1_data.reg_select,SAMPLE_CONTROL);
	val = inp(_gf1_data.data_hi);

	LEAVE_CRITICAL;

	if (val & ADC_IRQ_PENDING)
		{
		tdma = &_gf1_dma[_gf1_data.adc_dma_chan-1];

		if (tdma->flags & TWO_FLAG)
			{
			UltraDmaNext(tdma,TRUE);	/* handle cross over page */
			}
		else
			{
			tdma->flags &= ~DMA_PENDING;
			/* show foreground its done */
			_gf1_data.flags &= ~ADC_DMA_BUSY;
			tdma->amnt_sent += tdma->cur_size;	/* accumulate totals */

			/* Call ADC processing function.... */
			_gf1_data.record_dma_tc_func();
			}
		}
}

static void
handle_voice(void)
{
unsigned char irq_source;
unsigned int voice;
unsigned long wave_ignore;
unsigned long volume_ignore;
unsigned long voice_bit;
unsigned char temp1;
unsigned char temp2;

/* clear the ignore flags. These flags are needed because we get lots of */
/* 'double' interrupts. This will only allow one interrupt per voice */
wave_ignore = 0L;
volume_ignore = 0L;

/* The GF1 has a fifo (sort of) of all pending wave table irq's. You  */
/* should stay here & service ALL pending waveform IRQ's before returning. */
while (TRUE)
	{
	ENTER_CRITICAL;

	outp(_gf1_data.reg_select,GET_IRQV);
	irq_source = inp(_gf1_data.data_hi);

	LEAVE_CRITICAL;

	voice = irq_source & 0x1F;	/* pick off the voice # */
	irq_source &= (VOICE_VOLUME_IRQ | VOICE_WAVE_IRQ);/* isolate the irq bits */

	if (irq_source == (VOICE_VOLUME_IRQ | VOICE_WAVE_IRQ))/* negative logic */
		{
		break;					/* No pending irqs left ... */
		}

	voice_bit = 1L << (long)voice;
	/* See if any waveform irqs first */
	if (!(irq_source & VOICE_WAVE_IRQ))
		{
		/* See if we have already serviced this voice ... */
		if (!(wave_ignore & voice_bit))
			{
			wave_ignore |= voice_bit;

			outp(_gf1_data.voice_select,voice);
			outp(_gf1_data.reg_select,GET_CONTROL);
			temp1 = inp (_gf1_data.data_hi);

			outp(_gf1_data.reg_select,GET_VOLUME_CONTROL);
			temp2 = inp (_gf1_data.data_hi);

			/* If EITHER looping is on OR ROLLOVER is on, don't stop voice */
			if (!((temp1 & VC_LOOP_ENABLE) || (temp2 & VC_ROLLOVER)))
				{
				UltraStopVoice(voice);
				}

			/* Call waveform processing function.... */
			_gf1_data.wavetable_func(voice);

			}
		}

	if (!(irq_source & VOICE_VOLUME_IRQ))
		{
		if (!(volume_ignore & voice_bit))
			{
			volume_ignore |= voice_bit;

			outp(_gf1_data.voice_select,voice);
			outp(_gf1_data.reg_select,GET_VOLUME_CONTROL);
			temp1 = inp (_gf1_data.data_hi);

			/* If volume looping is enabled, don't stop it */
			if (!(temp1 & VL_LOOP_ENABLE))
				{
				UltraStopVolume(voice);
				}

			/* Call envelope processing function.... */
			_gf1_data.volume_func(voice);
			}
		}
	}
}

void
gf1_handler(void)
{
unsigned char irq_source;
unsigned int  midi_status;
unsigned int  data;

#ifdef METAWARE
	if (_gf1_data.gf1_sema4)
		{
		_gf1_data.irq_pending = 1;
		return;
		}
#endif

/* IRQ's that will be vectored here */

/* 1) DAC sampling. (PC DMA) */
/* 2) Voice Volume Envelope */
/* 3) WAVE table end */
/* 4) DMA to dram. */
/* 5) Possible MIDI */

	/* Handle ALL irqs that may be pending */
	while (TRUE)
		{
		/* First, find out who has an interrupt pending */
		irq_source = inp(_gf1_data.irq_status);
		if (irq_source == 0)
			break;				/* No more irqs ... */

		if (irq_source & DMA_TC_IRQ)
			{
			handle_dma_tc();
			}

		if (irq_source & (MIDI_TX_IRQ|MIDI_RX_IRQ))
			{
			midi_status = (unsigned int)inp(_gf1_data.midi_control);

			if (irq_source & MIDI_TX_IRQ)
				{
				/* Call midi transmit data processing function.... */
				_gf1_data.midi_xmit_func(midi_status);
				}

			if (irq_source & MIDI_RX_IRQ)
				{
				/* reading data will clear IRQ */
				data = (unsigned int)inp(_gf1_data.midi_data);

				/* Call midi recieve data processing function.... */
				_gf1_data.midi_recv_func(midi_status,data);
				}
			}

		if (irq_source & GF1_TIMER1_IRQ)
			{
			ENTER_CRITICAL;
			outp( _gf1_data.reg_select, TIMER_CONTROL );
			outp( _gf1_data.data_hi, _gf1_data.timer_ctrl & ~0x04);

			outp( _gf1_data.reg_select, TIMER_CONTROL );
			outp( _gf1_data.data_hi, _gf1_data.timer_ctrl );

			_gf1_data.timer1_func();
			LEAVE_CRITICAL;
			}

		if (irq_source & GF1_TIMER2_IRQ)
			{
			ENTER_CRITICAL;

			outp( _gf1_data.reg_select, TIMER_CONTROL );
			outp( _gf1_data.data_hi, _gf1_data.timer_ctrl & ~0x08);

			outp( _gf1_data.reg_select, TIMER_CONTROL );
			outp( _gf1_data.data_hi, _gf1_data.timer_ctrl );

			_gf1_data.timer2_func();
			LEAVE_CRITICAL;
			}

		if (irq_source & (WAVETABLE_IRQ | ENVELOPE_IRQ))
			{
			handle_voice();
			}
		}
	_gf1_data.aux_irq_func();	/* aux interrupt handler (ultramax) */
}

#ifdef METAWARE
static _Far _INTERRPT void
#else
static void interrupt
#endif
gf1_irq_handler(void)
{
int irq_num;

#ifdef METAWARE
GetSegRegs();
#endif
irq_num = _gf1_data.gf1_irq_num;

/* clear PC's interrupt controller(s) */
outp(_gf1_irq[irq_num].ocr,_gf1_irq[irq_num].spec_eoi);

/* gotta send EOI to BOTH controllers */
if (irq_num > 7)
	outp(OCR1,EOI);

gf1_handler();		/* Go to handler */
}

#ifdef METAWARE
static _Far _INTERRPT void
#else
static void interrupt
#endif
midi_irq_handler(void)
{
int irq_num;

irq_num = _gf1_data.midi_irq_num;

/* clear PC's interrupt controller(s) */
outp(_gf1_irq[irq_num].ocr,_gf1_irq[irq_num].spec_eoi);

/* gotta send EOI to BOTH controllers */
if (irq_num > 7)
	outp(OCR1,EOI);

gf1_handler();		/* Go to handler (same for now ...) */

}

/* This is the function that gets executed if no other handler is defined */
void
default_func(void)
{
}

void
SetIrqHandlers(int gf1_irq,int midi_irq)
{
int temp_irq;

	temp_irq = gf1_irq;

	if (temp_irq != 0)
		{
		if (gf1_irq > 7)
			gf1_irq += 0x68;
		else
			gf1_irq += 0x08;

		_gf1_data.old_gf1_vec = gf1_getvect (gf1_irq);
		gf1_setvect (gf1_irq,gf1_irq_handler);
		}

	if ((midi_irq != 0) && (midi_irq != temp_irq))
		{
		if (midi_irq > 7)
			midi_irq += 0x68;
		else
			midi_irq += 0x08;

		_gf1_data.old_midi_vec = gf1_getvect (midi_irq);
		gf1_setvect (midi_irq,midi_irq_handler);
		}
}

#ifdef METAWARE
void
leave_critical(void)
{
	_gf1_data.gf1_sema4--;
	if (_gf1_data.gf1_sema4 == 0)
		{
		if (_gf1_data.irq_pending)
			{
			_gf1_data.irq_pending = 0;
			gf1_handler();
			}
		}
}
#endif

