/***************************************************************************
*	NAME:  IWSYNTH.C $Revision: 1.8 $
**	COPYRIGHT:
**	"Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "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."
****************************************************************************
* $Log: iwsynth.c $
* Revision 1.8  1995/10/13 17:30:28  mleibow
* renamed SET_EFFECT_FINAL_OFFSET to SET_EFFEcT_FINAL_VOLUME to match
* documentation.
* Revision 1.7  1995/09/07 16:33:17  mleibow
* Shut off synth dac when not in use.
* Revision 1.6  1995/04/26 10:25:34  sdsmith
* Fixed up some of the commentary
* ,
* Revision 1.5  1995/04/10 11:25:58  mleibow
* Fixed cleanup code (NMI vector wasn't being reset)
* Revision 1.4  1995/03/24 09:44:15  mleibow
* Added initialization for MPU401 and other emulations
* Revision 1.3  1995/03/22 14:57:44  mleibow
* moved #include "codec.h" to iwl.h
* Revision 1.2  1995/02/24 14:03:18  mleibow
* took out IDECI code
* Revision 1.1  1995/02/23 11:07:20  unknown
* Initial revision
***************************************************************************/

#include <dos.h>
#include "iw.h"
#include "globals.h"
#include "iwl.h"
#include "digital.h"
#include "codec.h"

#ifdef IWL_NMI_FOR_EMULATION
extern OS_INTERRUPT nmi_service;
OS_PINTERRUPT old_nmi_service;
#endif

static int synth_loaded = 0;

/***************************************************************************

FUNCTION DEFINITION:
iwl_ramp_down - ramps the volume of all synth voices to zero

DESCRIPITON:
iwl_ramp_down stops all volume ramping currently taking place and resets
the ramps to bring the volume to zero at the fastest possible rate.

RETURNS: void
*/
static void iwl_ramp_down(void)
{
	unsigned short i;
	unsigned int volume, vc;

	ENTER;
	for(i=0; i < iwl_voices; i++) {
		IWL_SET_PAGE(i);
		IWL_OUT_CONTROL(SET_VOLUME_CONTROL, IWL_STOP);
		IWL_INP_W(GET_VOLUME, volume);
		volume >>= 8;
		if (volume > IW_MIN_OFFSET) {
			IWL_OUT_B(SET_VOLUME_START, IW_MIN_OFFSET);
			IWL_OUT_B(SET_VOLUME_RATE, 63);
			IWL_OUT_B(SET_VOLUME_CONTROL, IWL_DIR_DEC);
			OS_OUTPORTB(iwl_register_select, SET_VOLUME_CONTROL);
			OS_OUTPORTB(iwl_data_high, IWL_DIR_DEC);
		}
	}
	for(i=0; i < iwl_voices; i++) {
		IWL_SET_PAGE(i);
		do {
			IWL_INP_B(GET_VOLUME_CONTROL, vc);
		} while ((vc & (IWL_STOPPED|IWL_STOP)) == 0) ;
		IWL_OUT_CONTROL(SET_CONTROL, IWL_STOP);
	}
	LEAVE;
}

#ifdef DEBUG
extern void stack_check(void);
#endif

static void clear_iw_ints(void)
{
	int i;

	/* Clear all interrupts. */
	OS_OUTPORTB(iwl_register_select, DMA_CONTROL);
	OS_OUTPORTB(iwl_data_high, 0x00);
	OS_OUTPORTB(iwl_register_select, ADLIB_CONTROL);
	OS_OUTPORTB(iwl_data_high, 0x00);
	OS_OUTPORTB(iwl_register_select, SAMPLE_CONTROL);
	OS_OUTPORTB(iwl_data_high, 0x00);

	OS_INPORTB(iwl_status_register);
	OS_OUTPORTB(iwl_register_select, DMA_CONTROL);
	OS_INPORTB(iwl_data_high);
	OS_OUTPORTB(iwl_register_select, SAMPLE_CONTROL);
	OS_INPORTB(iwl_data_high);
	for (i=0; i < 32; i++) {
		OS_OUTPORTB(iwl_register_select, GET_IRQV);
		OS_INPORTB(iwl_data_high);
	}
}

static void iwl_init_synth(void)
{
	unsigned short i;
	iwl_voices = 32; /* interwave always has 32 voices in enhanced mode */

    /* initialize synth */
	iwl_ursti = 7;
	IWL_OUT_B(MASTER_RESET, 0); /* toggle low bit for reset */
	iw_delay();
	iw_delay();
	IWL_OUT_B(MASTER_RESET, 1); /* finish toggle of low bit for reset */

    /* Place the synthesizer into enhanced mode */
	iwl_sgmi = IWL_SGMI_ENH;
	IWL_OUT_B(SET_GLOBAL_MODE, iwl_sgmi);

    /* reset the register array */
	for (i=0; i < iwl_voices; i++) {
		IWL_OUT_CONTROL(SET_CONTROL, IWL_STOP);
		IWL_OUT_CONTROL(SET_VOLUME_CONTROL, IWL_STOP);
		iwl_set_addr_regs(i, 30L, SET_ACC_LOW, SET_ACC_HIGH);
		iwl_set_addr_regs(i, 30L, SET_START_LOW, SET_START_HIGH);
		iwl_set_addr_regs(i, 30L, SET_END_LOW, SET_END_HIGH);
		iwl_set_addr_regs(i, 30L, SET_EFFECT_LOW, SET_EFFECT_HIGH);
		IWL_OUT_W(SET_FREQUENCY, 512);
		IWL_OUT_W(SET_VOLUME, 0);
		IWL_OUT_W(SET_EFFECT_FINAL_VOLUME, 0);
		IWL_OUT_W(SET_EFFECT_VOLUME, 0);
		IWL_OUT_W(SET_LEFT_FINAL_OFFSET, 0);
		IWL_OUT_W(SET_LEFT_OFFSET, 0);
		IWL_OUT_W(SET_RIGHT_FINAL_OFFSET, 0);
		IWL_OUT_W(SET_RIGHT_OFFSET, 0);
		IWL_OUT_B(SET_FREQ_LFO, 0);
		IWL_OUT_B(SET_VOLUME_LFO, 0);
		IWL_OUT_B(SET_EFFECT_ACC_SEL, 0);
		IWL_OUT_B(SET_VOICE_MODE, IWL_SMSI_DAV);
	}
	clear_iw_ints();

	IWL_OUT_B(MASTER_RESET, iwl_ursti); /* enable DAC and IRQ */
}

/***************************************************************************

FUNCTION DEFINITION:
iw_init_synth - initalize the InterWave synthesizer

DESCRIPITON:
This routine initializes the synthesizer by hooking the necessary
interrupt vectors and stopping any current activity in the synth.

This routine should only be called after a call to iw_init_kernel().

RETURNS: int - IW_OK
               IW_CARD_NOT_FOUND
		   other error codes
*/
int	iw_init_synth(struct load_kernel_cfg RFAR *cfg)
{
#ifdef IWL_NMI_FOR_EMULATION
	OS_PINTERRUPT chk_nmi_handler;
#endif

	if (synth_loaded) {
		synth_loaded += 1;
		return(IW_OK);
	}

	if (iwl_synth_base) {
		OS_PUSH_DISABLE();

#ifdef IWL_NMI_FOR_EMULATION
		old_nmi_service = os_getvect(2);
		os_setvect(2, (OS_PINTERRUPT)nmi_service);
		chk_nmi_handler = os_getvect(2);
	/* some machines have NMI vector forced to a specific location. */
	/* the following code checks for that problem.  LucidWave's DOS */
	/* extender doesn't actually set the same address that you */
	/* tell it for PM vectors.  So, the following code checks */
	/* the old routine against the old routine, and not if the */
	/* new routine != the old routine */
		if (chk_nmi_handler == old_nmi_service) {
			OS_POP_FLAGS();
			return(IW_CANT_SET_NMI_HANDLER);
		}
		iwl_set_irq(iwl_irq1);
#else
#ifdef IWL_SYNTH_IRQ2
		iwl_umcr |= IWL_UMCR_GF122;
		OS_OUTPORTB(iwl_mix_control, iwl_umcr);
		iwl_set_irq(iwl_irq2);
#else
		iwl_set_irq(iwl_irq1);
#endif
#ifndef NO_MIDI
#ifdef IWL_MIDI_IRQ1
		iwl_set_irq(iwl_irq1);
#else
		iwl_set_irq(iwl_irq2);
#endif  //IWL_MIDI_IRQ1
#endif  //!NO_MIDI
#endif	//IWL_NMI_FOR_EMULATION

		iwl_init_synth();

		iwl_flags |= F_IW_SYNTH_LOADED;
		synth_loaded += 1;
		OS_POP_FLAGS();

		return(IW_OK);
	}
	else
		return(IW_CARD_NOT_FOUND);
}

/***************************************************************************

FUNCTION DEFINITION:
iwl_reset_synth - shutdown all activity in the synthesizer

DESCRIPITON:
iwl_reset_synth stops all activity on all voices, ramps the volumes to
zero, and clears all interrupt conditions.

RETURNS: void
*/
void iwl_reset_synth(void)
{
	OS_PUSH_DISABLE();

    /* Set all volumes to zero */
	iwl_ramp_down();

	iwl_init_synth();
	iwl_ursti &= ~2; /* disable synth dac */
	IWL_OUT_B(MASTER_RESET, iwl_ursti); /* disable DAC */

	OS_POP_FLAGS();
}

/***************************************************************************

FUNCTION DEFINITION:
iw_close_synth - shutdown the InterWave synthesizer

DESCRIPITON:
iw_close_synth stops all activity in the synth, ramps all voice volumes to
zero, and clears interrupt status.

RETURNS: int - IW_OK
*/
int	iw_close_synth(void)
{
	if (!(iwl_flags & F_IW_SYNTH_LOADED))
		return(IW_NOT_LOADED);
	if (--synth_loaded)
		return(IW_OK);
	OS_PUSH_DISABLE();
	iwl_reset_synth();

#ifdef IWL_NMI_FOR_EMULATION
	iwl_reset_irq(iwl_irq1);
	os_setvect(2, old_nmi_service);
#else
#ifdef IWL_SYNTH_IRQ2
	iwl_umcr &= ~IWL_UMCR_GF122;
	OS_OUTPORTB(iwl_mix_control, iwl_umcr);
	iwl_reset_irq(iwl_irq2);
#else
	iwl_reset_irq(iwl_irq1);
#endif

#ifndef NO_MIDI
#ifdef IWL_MIDI_IRQ1
	iwl_reset_irq(iwl_irq1);
#else
	iwl_reset_irq(iwl_irq2);
#endif
#endif
#endif

	iwl_ideci &= ~IWL_IDECI_EA98;
	IWL_OUT_B(IW_IDECI, iwl_ideci);

	OS_POP_FLAGS();
	iwl_flags &= ~F_IW_SYNTH_LOADED;
#ifdef DEBUG
	stack_check();
#endif
	return(IW_OK);
}
