/***************************************************************************
*	NAME:  IWISR.C $Revision: 1.7 $
**	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: iwisr.c $
* Revision 1.7  1995/05/25 16:26:56  mleibow
* Fixed IRQ handler lists to allow FAR items in list
* Revision 1.6  1995/05/03 08:26:15  sdsmith
* Changed names with midi to uart
* Revision 1.5  1995/04/10 11:23:37  mleibow
* LUCIDWave needed OS_PINTERRUPT type cast
* Revision 1.4  1995/03/24 09:44:56  mleibow
* Added support for emulating devices with NMI
* Revision 1.3  1995/03/22 14:57:42  mleibow
* moved #include "codec.h" to iwl.h
* Revision 1.2  1995/02/24 14:01:45  mleibow
* removed IDECI code
* Revision 1.1  1995/02/23 11:07:08  unknown
* Initial revision
***************************************************************************/

#if defined(__BORLANDC__)
#include <dos.h>
#endif
#if defined(__WATCOMC__)
#include <i86.h>
#endif
#include "iw.h"
#include "iwl.h"
#include "iwllist.h"
#include "globals.h"
#define NULL 0L

#if defined(__BORLANDC__)
#pragma warn -pro
#endif

#define MAX_IRQ_HANDLERS 4
#define MAX_TIMER_IRQ_HANDLERS 1

typedef void (RFAR *visr)(void);
typedef int  (RFAR *dma_handler)(int);
typedef int  (RFAR *voice_handler)(int);
typedef int  (RFAR *volume_handler)(int);
typedef int  (RFAR *codec_handler)(void);
typedef int  (RFAR *timer_handler)(void);
typedef int  (RFAR *uart_xmit_handler)(unsigned int);
typedef int  (RFAR *uart_recv_handler)(unsigned int, unsigned int);

struct iwl_irq_handler {
  struct iwl_node n;
  int (RFAR *handler)(void);
  int used;
};
static struct iwl_irq_handler dma_handlers[MAX_IRQ_HANDLERS];
static struct iwl_list iwl_dma_handlers;
static struct iwl_irq_handler voice_handlers[MAX_IRQ_HANDLERS];
static struct iwl_list iwl_voice_handlers;
static struct iwl_irq_handler volume_handlers[MAX_IRQ_HANDLERS];
static struct iwl_list iwl_volume_handlers;
static struct iwl_irq_handler timer1_handlers[MAX_TIMER_IRQ_HANDLERS];
static struct iwl_list iwl_timer1_handlers;
static struct iwl_irq_handler timer2_handlers[MAX_TIMER_IRQ_HANDLERS];
static struct iwl_list iwl_timer2_handlers;
static struct iwl_irq_handler uart_xmit_handlers[MAX_IRQ_HANDLERS];
static struct iwl_list iwl_uart_xmit_handlers;
static struct iwl_irq_handler uart_recv_handlers[MAX_IRQ_HANDLERS];
static struct iwl_list iwl_uart_recv_handlers;
static struct iwl_irq_handler codec_handlers[MAX_IRQ_HANDLERS];
static struct iwl_list iwl_codec_handlers;
static struct iwl_irq_handler visr_handlers[MAX_IRQ_HANDLERS];
static struct iwl_list iwl_visr_handlers;

static int irq1_ref_count = 0, irq2_ref_count = 0;

int iwl_adlib_control;

static unsigned char iwl_irq_latch[] = {
	/*    2  3     5     7           11 12       15 */
	0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7
};


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

FUNCTION DEFINITION:
iwl_reset_irq - resets interrupt vectors

RETURNS: void
*/
void iwl_reset_irq(
  int irq_index)  /* interrupt to be reset */
{
  if (irq_index == iwl_irq1) {
    if (irq1_ref_count) {
	irq1_ref_count--;
	if (!irq1_ref_count) {
	  os_unset_irq(iwl_irq1);
//	  iwl_ideci &= ~IWL_IDECI_EICH1;
//	  IWL_OUT_B(IW_IDECI, iwl_ideci);
	}
    }
  }
  else if (iwl_irq1 != iwl_irq2) {
    if (irq2_ref_count) {
	irq2_ref_count--;
	if (!irq2_ref_count) {
	  os_unset_irq(iwl_irq2);
//	  iwl_ideci &= ~IWL_IDECI_EICH2;
//	  IWL_OUT_B(IW_IDECI, iwl_ideci);
	}
    }
  }
}

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

FUNCTION DEFINITION:
iwl_set_irq - sets the interrupt vectors used by the synthesizer

RETURNS: void

SEE ALSO:
iwl_midi_irq_service
iwl_irq_service
*/
void	iwl_set_irq(
  int irq_index)   /* interrupt vector to be set */
{
  if (irq_index == iwl_irq1) {
    if (!irq1_ref_count) {
      os_set_irq(iwl_irq1, (OS_PINTERRUPT)iwl_irq1_service);
//      iwl_ideci |= IWL_IDECI_EICH1;
//      IWL_OUT_B(IW_IDECI, iwl_ideci);
    }
    irq1_ref_count++;
  }
  else if (iwl_irq1 != iwl_irq2 && !iwl_emulation) {
    if (!irq2_ref_count) {
      os_set_irq(iwl_irq2, (OS_PINTERRUPT)iwl_irq2_service);
//      iwl_ideci |= IWL_IDECI_EICH2;
//      IWL_OUT_B(IW_IDECI, iwl_ideci);
    }
    irq2_ref_count++;
  }
}

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

FUNCTION DEFINITION:
iwl_isr_init - initialize all interrupt handling data

DESCRIPTION:
iwl_isr_init initializes all interrupt handling flags and interrupts
handler pointers to zero.

RETURNS: void
*/
void	iwl_isr_init(void)
{
  int i;

  iwl_InitList(&iwl_dma_handlers);
  iwl_InitList(&iwl_voice_handlers);
  iwl_InitList(&iwl_volume_handlers);
  iwl_InitList(&iwl_timer1_handlers);
  iwl_InitList(&iwl_timer2_handlers);
  iwl_InitList(&iwl_uart_xmit_handlers);
  iwl_InitList(&iwl_uart_recv_handlers);
  iwl_InitList(&iwl_codec_handlers);
  iwl_InitList(&iwl_visr_handlers);
  for (i=0; i<MAX_IRQ_HANDLERS; i++) {
    dma_handlers[i].handler = 0;
    dma_handlers[i].used = 0;
    voice_handlers[i].handler = 0;
    voice_handlers[i].used = 0;
    volume_handlers[i].handler = 0;
    volume_handlers[i].used = 0;
    uart_xmit_handlers[i].handler = 0;
    uart_recv_handlers[i].used = 0;
    codec_handlers[i].used = 0;
    visr_handlers[i].used = 0;
  }
  for (i=0; i<MAX_TIMER_IRQ_HANDLERS; i++) {
    timer1_handlers[i].handler = 0;
    timer1_handlers[i].used = 0;
    timer2_handlers[i].handler = 0;
    timer2_handlers[i].used = 0;
  }
}

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

FUNCTION DEFINITION:
iw_add_handler - adds an interrupt handler routine

DESCRIPTION:

RETURNS: int - IW_OK if successful
               IW_NO_MORE_HANDLERS if all handlers are already in use
*/
int iw_add_handler(
  unsigned char handler_type,
  int (RFAR *handler)(void)) /* pointer to caller's handler routine */
{
  int i, rc = IW_OK, max_handlers;
  struct iwl_list *handler_list;
  struct iwl_irq_handler RFAR *handler_array;

  switch (handler_type) {
    case IW_IRQ_HANDLER_DMA:
      handler_list = &iwl_dma_handlers;
      handler_array = dma_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_VOICE:
      handler_list = &iwl_voice_handlers;
      handler_array = voice_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_VOLUME:
      handler_list = &iwl_volume_handlers;
      handler_array = volume_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_TIMER1:
      handler_list = &iwl_timer1_handlers;
      handler_array = timer1_handlers;
      max_handlers = MAX_TIMER_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_TIMER2:
      handler_list = &iwl_timer2_handlers;
      handler_array = timer2_handlers;
      max_handlers = MAX_TIMER_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_UART_XMIT:
      handler_list = &iwl_uart_xmit_handlers;
      handler_array = uart_xmit_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_UART_RECV:
      handler_list = &iwl_uart_recv_handlers;
      handler_array = uart_recv_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_CODEC:
      handler_list = &iwl_codec_handlers;
      handler_array = codec_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_VISR:
      handler_list = &iwl_visr_handlers;
      handler_array = visr_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    default:
      handler_list = NULL;
      break;
  }
  if (handler_list) {
    for (i=0; i<max_handlers; i++) {
      if (!handler_array[i].used) break;
    }
    if (i != max_handlers) {
      handler_array[i].handler = handler;
      handler_array[i].used = 1;
      iwl_AddNode(handler_list, &(handler_array[i].n), NULL, IWL_APPEND);
    }
    else rc = IW_NO_MORE_HANDLERS;
  }
  else rc = IW_NO_MORE_HANDLERS;
  return(rc);
}

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

FUNCTION DEFINITION:
iw_remove_handler - removes an interrupt handler routine

DESCRIPTION: handler = 0 removes all handlers

RETURNS: int - IW_OK if successful
               IW_NO_MORE_HANDLERS if all handlers are already in use
*/
int iw_remove_handler(
  unsigned char handler_type,
  int (RFAR *handler)(void)) /* pointer to caller's handler routine */
{
  int i, rc = IW_OK, max_handlers;
  struct iwl_list *handler_list;
  struct iwl_irq_handler RFAR *handler_array, RFAR *h;

  switch (handler_type) {
    case IW_IRQ_HANDLER_DMA:
      handler_list = &iwl_dma_handlers;
      handler_array = dma_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_VOICE:
      handler_list = &iwl_voice_handlers;
      handler_array = voice_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_VOLUME:
      handler_list = &iwl_volume_handlers;
      handler_array = volume_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_TIMER1:
      handler_list = &iwl_timer1_handlers;
      handler_array = timer1_handlers;
      max_handlers = MAX_TIMER_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_TIMER2:
      handler_list = &iwl_timer2_handlers;
      handler_array = timer2_handlers;
      max_handlers = MAX_TIMER_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_UART_XMIT:
      handler_list = &iwl_uart_xmit_handlers;
      handler_array = uart_xmit_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_UART_RECV:
      handler_list = &iwl_uart_recv_handlers;
      handler_array = uart_recv_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_CODEC:
      handler_list = &iwl_codec_handlers;
      handler_array = codec_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    case IW_IRQ_HANDLER_VISR:
      handler_list = &iwl_visr_handlers;
      handler_array = visr_handlers;
      max_handlers = MAX_IRQ_HANDLERS;
      break;
    default:
      handler_list = NULL;
      break;
  }
  if (handler_list) {
    if (!handler) {
	for (i=0; i<max_handlers; i++) {
	    handler_array[i].used = 0;
	}
	iwl_InitList(handler_list);
    }
    else {
      for ( h = (struct iwl_irq_handler RFAR *)handler_list;
	    h != 0;
	    h = (struct iwl_irq_handler RFAR *)h->n.next
	  )
	  if (h->handler == handler) {
	    h->used = 0;
	    iwl_DeleteNode(handler_list, (struct iwl_node RFAR *)h);
	  }
    }
  }
  return(rc);
}

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

FUNCTION DEFINITION:
process_midi - determine cause of MIDI interrupt and act

DESCRIPTION:
After receiving control from the interrupt service routine, 
process_midi_interrupt determines the cause of the interrupt by checking 
the status registers in the synthsizer.  Based on this determination,
control is transferred to the appropriate handler set by the caller.

RETURNS: void
*/
static void process_midi(unsigned int iwl_irq_status)
{
  struct iwl_irq_handler RFAR *h;
  unsigned int midi_status, data;

    midi_status = (unsigned int)OS_INPORTB(iwl_uart_control);
    if (iwl_irq_status & UART_IRQ_SEND) {
	    /* Call uart transmit data processing function.... */
      for ( h = (struct iwl_irq_handler RFAR *)iwl_uart_xmit_handlers.head;
	    h != 0;
	    h = (struct iwl_irq_handler RFAR *)h->n.next
	  )
	  if ((*((uart_xmit_handler)h->handler))(midi_status)) break;
    }
    if (iwl_irq_status & UART_IRQ_RCV) {
      data = (unsigned int)OS_INPORTB(iwl_uart_data);
	    /* Call midi receive data processing function */
      for ( h = (struct iwl_irq_handler RFAR *)iwl_uart_recv_handlers.head;
	    h != 0;
	    h = (struct iwl_irq_handler RFAR *)h->n.next
	  )
	  if ((*((uart_recv_handler)h->handler))(midi_status, data))
	break;
    }
}

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

FUNCTION DEFINITION:
iwl_process_irq1_interrupt - determine cause of synthesizer interrupt and act

DESCRIPTION:
After receiving control from the interrupt service routine, 
process_interrupt determines the cause of the interrupt by checking 
the status registers in the synthsizer.  Based on this determination,
control is transferred to the appropriate handler set by the caller.

RETURNS: void
*/
#if _MSC_VER
#pragma optimize("", off)
#endif
void	iwl_process_irq1_interrupt(void)
{
  struct iwl_irq_handler RFAR *h;
  unsigned int iwl_irq_status = 0;
  unsigned char iwl_codec_irq_status = 0;
  unsigned char irqv_data;
  unsigned int voice;

  OS_PUSH_DISABLE();
    /* handle interrupt */
  while (1) {
	/* interrupts are disabled by check_irq_status(); */
	/* interrupt MUST remain disabled upon exit of this loop */
#if !defined(IWL_NO_MIDI) && !defined(IWL_NO_SYNTH)
    iwl_irq_status = OS_INPORTB(iwl_status_register);
#endif
#if !defined(IWL_NO_CODEC) && !defined(IWL_CODEC_IRQ2) && !defined(IWL_SYNTH_IRQ2)
    iwl_codec_irq_status = OS_INPORTB(iwl_codec_status) & 0x01;
#endif
#ifndef IWL_NO_MIDI
#ifndef IWL_MIDI_IRQ1
    if (iwl_irq1 != iwl_irq2 && !iwl_emulation) {
    	iwl_irq_status &= ~(UART_IRQ_SEND|UART_IRQ_RCV);
    }
#else
    iwl_irq_status &= ~(UART_IRQ_SEND|UART_IRQ_RCV);
#endif
    if (iwl_irq_status & (UART_IRQ_SEND|UART_IRQ_RCV)) {
      process_midi(iwl_irq_status);
    }
#endif
    if (iwl_irq_status == 0 && !(iwl_flags & F_GEN_TC) && !iwl_codec_irq_status) break;
#if !defined(IWL_CODEC_IRQ2) && !defined(IWL_NO_CODEC)
    if (iwl_codec_irq_status) {
      for ( h = (struct iwl_irq_handler RFAR *)iwl_codec_handlers.head;
	    h != 0;
	    h = (struct iwl_irq_handler RFAR *)h->n.next
	  )
	if ((*((codec_handler)h->handler))()) break;
    }
#endif
#if !defined(IWL_SYNTH_IRQ2) && !defined(NO_SYNTH)
    if (iwl_irq_status & DMA_TC_BIT || iwl_flags & F_GEN_TC) {
	    /* Clear out interrupt. */
      OS_OUTPORTB(iwl_register_select, DMA_CONTROL);
      if (OS_INPORTB(iwl_data_high) & IW_DMA_IRQ_PRESENT || iwl_flags & F_GEN_TC) {
	iwl_flags &= ~F_GEN_TC;
	for ( h = (struct iwl_irq_handler RFAR *)iwl_dma_handlers.head;
	      h != 0;
	      h = (struct iwl_irq_handler RFAR *)h->n.next
	    )
		if ((*((dma_handler)h->handler))(IW_DMA)) break;
      }
    }
    if (iwl_irq_status & TIMER_1) { /* Check general purpose timer. */
	    /* Reset timer interrupt. */
      OS_OUTPORTB(iwl_register_select, ADLIB_CONTROL);
      OS_OUTPORTB(iwl_data_high, iwl_adlib_control & ~4);
      OS_OUTPORTB(iwl_data_high, iwl_adlib_control);
      for ( h = (struct iwl_irq_handler RFAR *)iwl_timer1_handlers.head;
	    h != 0;
	    h = (struct iwl_irq_handler RFAR *)h->n.next
	  )
	  if ((*((timer_handler)h->handler))()) break;
    }
    if (iwl_irq_status & TIMER_2) {
	    /* Reset timer interrupt. */
      OS_OUTPORTB(iwl_register_select, ADLIB_CONTROL);
      OS_OUTPORTB(iwl_data_high, iwl_adlib_control & ~8);
      OS_OUTPORTB(iwl_data_high, iwl_adlib_control);
      for ( h = (struct iwl_irq_handler RFAR *)iwl_timer2_handlers.head;
	    h != 0;
	    h = (struct iwl_irq_handler RFAR *)h->n.next
	  )
	  if ((*((timer_handler)h->handler))()) break;
    }
    if (iwl_irq_status & (IWL_WAVETABLE|IWL_ENVELOPE)) {
      for (;;) {
	OS_OUTPORTB(iwl_register_select, GET_IRQV_NC);
	irqv_data = OS_INPORTB(iwl_data_high);
	if ((irqv_data & 0xC0) == 0xC0) break;
	voice = irqv_data & 0x1F;
		/* Is the interrupt from a wavetable address. */
	if (!(irqv_data & 0x80)) {
	  for ( h = (struct iwl_irq_handler RFAR *)iwl_voice_handlers.head;
		h != 0;
		h = (struct iwl_irq_handler RFAR *)h->n.next
	      )
		      if ((*((voice_handler)h->handler))(voice)) break;
	}
		/* Is the interrupt from a volume envelope. */
	if (!(irqv_data & 0x40)) {
	  for ( h = (struct iwl_irq_handler RFAR *)iwl_volume_handlers.head;
		h != 0;
		h = (struct iwl_irq_handler RFAR *)h->n.next
	      )
		      if ((*((volume_handler)h->handler))(voice)) break;
	}
        OS_OUTPORTB(iwl_register_select, GET_IRQV);
        OS_INPORTB(iwl_data_high);
      }
    }
#endif
  }
  OS_POP_FLAGS();
}
#if _MSC_VER
#pragma optimize("", on)
#endif

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

FUNCTION DEFINITION:
iwl_process_irq2_interrupt - determine cause of synthesizer interrupt and act

DESCRIPTION:
After receiving control from the interrupt service routine, 
process_interrupt determines the cause of the interrupt by checking 
the status registers in the synthsizer.  Based on this determination,
control is transferred to the appropriate handler set by the caller.

RETURNS: void
*/
#if _MSC_VER
#pragma optimize("", off)
#endif
void	iwl_process_irq2_interrupt(void)
{
  unsigned int iwl_irq_status = 0;
  unsigned char iwl_codec_irq_status = 0;

  OS_PUSH_DISABLE();
    /* handle interrupt */
  while (1) {
	/* interrupts are disabled by check_irq_status(); */
	/* interrupt MUST remain disabled upon exit of this loop */
#if !defined(IWL_NO_MIDI) && !defined(IWL_NO_SYNTH)
    iwl_irq_status = OS_INPORTB(iwl_status_register);
#ifndef IWL_SYNTH_IRQ2
    iwl_irq_status &= (UART_IRQ_SEND|UART_IRQ_RCV);
#endif
#endif
#if !defined(IWL_NO_CODEC) && (defined(IWL_CODEC_IRQ2) || defined(IWL_SYNTH_IRQ2))
    iwl_codec_irq_status = OS_INPORTB(iwl_codec_status) & 0x01;
#endif
    if (iwl_irq_status == 0 && !(iwl_flags & F_GEN_TC) && !iwl_codec_irq_status) break;
#if  !defined(IWL_NO_MIDI) && !defined(IWL_MIDI_IRQ1)
    if (iwl_irq_status & (UART_IRQ_SEND|UART_IRQ_RCV)) {
      process_midi(iwl_irq_status);
    }
#endif
#if !defined(IWL_NO_CODEC) && (defined(IWL_CODEC_IRQ2) || defined(IWL_SYNTH_IRQ2))
    if (iwl_codec_irq_status) {
      for ( h = (struct iwl_irq_handler RFAR *)iwl_codec_handlers.head;
	    h != 0;
	    h = (struct iwl_irq_handler RFAR *)h->n.next
	  )
	if ((*((codec_handler)h->handler))()) break;
    }
#endif
#if defined(IWL_SYNTH_IRQ2) && !defined(NO_SYNTH)
    if (iwl_irq_status & DMA_TC_BIT || iwl_flags & F_GEN_TC) {
	    /* Clear out interrupt. */
      OS_OUTPORTB(iwl_register_select, DMA_CONTROL);
      if (OS_INPORTB(iwl_data_high) & IW_DMA_IRQ_PRESENT || iwl_flags & F_GEN_TC) {
	iwl_flags &= ~F_GEN_TC;
	for ( h = (struct iwl_irq_handler RFAR *)iwl_dma_handlers.head;
	      h != 0;
	      h = (struct iwl_irq_handler RFAR *)h->n.next
	    )
		if ((*((dma_handler)h->handler))(IW_DMA)) break;
      }
    }
    if (iwl_irq_status & TIMER_1) { /* Check general purpose timer. */
	    /* Reset timer interrupt. */
      OS_OUTPORTB(iwl_register_select, ADLIB_CONTROL);
      OS_OUTPORTB(iwl_data_high, iwl_adlib_control & ~4);
      OS_OUTPORTB(iwl_data_high, iwl_adlib_control);
      for ( h = (struct iwl_irq_handler RFAR *)iwl_timer1_handlers.head;
	    h != 0;
	    h = (struct iwl_irq_handler RFAR *)h->n.next
	  )
	  if ((*((timer_handler)h->handler))()) break;
    }
    if (iwl_irq_status & TIMER_2) {
	    /* Reset timer interrupt. */
      OS_OUTPORTB(iwl_register_select, ADLIB_CONTROL);
      OS_OUTPORTB(iwl_data_high, iwl_adlib_control & ~8);
      OS_OUTPORTB(iwl_data_high, iwl_adlib_control);
      for ( h = (struct iwl_irq_handler RFAR *)iwl_timer2_handlers.head;
	    h != 0;
	    h = (struct iwl_irq_handler RFAR *)h->n.next
	  )
	  if ((*((timer_handler)h->handler))()) break;
    }
    if (iwl_irq_status & (IWL_WAVETABLE|IWL_ENVELOPE)) {
      for (;;) {
	OS_OUTPORTB(iwl_register_select, GET_IRQV_NC);
	irqv_data = OS_INPORTB(iwl_data_high);
	if ((irqv_data & 0xC0) == 0xC0) break;
	voice = irqv_data & 0x1F;
		/* Is the interrupt from a wavetable address. */
	if (!(irqv_data & 0x80)) {
	  for ( h = (struct iwl_irq_handler RFAR *)iwl_voice_handlers.head;
		h != 0;
		h = (struct iwl_irq_handler RFAR *)h->n.next
	      )
	      if ((*((voice_handler)h->handler))(voice)) break;
	}
		/* Is the interrupt from a volume envelope. */
	if (!(irqv_data & 0x40)) {
	  for ( h = (struct iwl_irq_handler RFAR *)iwl_volume_handlers.head;
		h != 0;
		h = (struct iwl_irq_handler RFAR *)h->n.next
	      )
	      if ((*((volume_handler)h->handler))(voice)) break;
	}
        OS_OUTPORTB(iwl_register_select, GET_IRQV);
        OS_INPORTB(iwl_data_high);
      }
    }
#endif
  }
  OS_POP_FLAGS();
}
#if _MSC_VER
#pragma optimize("", on)
#endif
