struct vpu_hw_int *vpu_hardware_int_table = 0;
unsigned vpu_hwint_count = 0;
void __interrupt __far dummy_interrupt_mask_handler();
void __far dummy_interrupt_mask_handler_end();

void __interrupt __far dummy_interrupt_mask_handler_cont();
void __interrupt __far dummy_interrupt_mask_handler_prec();
void __far dummy_interrupt_mask_handler_cont_end();
void __far dummy_interrupt_mask_handler_prec_end();

extern unsigned char __far *vpu_inthdlr_stack;

void reset_vpu_interrupt_handlers(void)
{
  unsigned n = vpu_hwint_count;

  while(n--)
    _dos_setvect(vpu_hardware_int_table[n].hw_int, vpu_hardware_int_table[n].prev_int);
}

void set_vpu_interrupt_handlers(void)
{
  unsigned n = vpu_hwint_count;

  while(n--)
    _dos_setvect(vpu_hardware_int_table[n].hw_int, vpu_hardware_int_table[n].handler);
}

inline void *new_vpu_interrupt_mask_handler(unsigned char hw_int, void (__far __interrupt **old_handler)(), unsigned chain)
{
  const void __far *dummy_handler;
  unsigned dummy_handler_len;
  unsigned char *retval;

  if((*old_handler = _dos_getvect(hw_int)) && chain)
  {
    if(chain == 1)
    {
      dummy_handler = dummy_interrupt_mask_handler_cont;
      dummy_handler_len = (unsigned)dummy_interrupt_mask_handler_cont_end - (unsigned)dummy_handler;
    }
    else
    {
      dummy_handler = dummy_interrupt_mask_handler_prec;
      dummy_handler_len = (unsigned)dummy_interrupt_mask_handler_prec_end - (unsigned)dummy_handler;
    }
    retval = malloc(dummy_handler_len);    
  }
  else
  {
    chain = 0;
    dummy_handler = dummy_interrupt_mask_handler;
    dummy_handler_len = (unsigned)dummy_interrupt_mask_handler_end - (unsigned)dummy_handler;    
    retval = malloc(dummy_handler_len);    
  }
  if(!retval) return 0;

  memcpy(retval, dummy_handler, dummy_handler_len);
  
  // replace MOV's source operand with interrupt number
  if(chain < 2)
    retval[0x2] = hw_int;
  else
    retval[0x8] = hw_int;

  return retval;
}

static int set_vpu_hw_int(struct vpu *vpu, unsigned char hw_int, unsigned short vpu_int, unsigned chain)
{
  void __far *new_pointer;
  void __far * __far *func_ptr;
  void (__far __interrupt *old_handler)();
  unsigned n = vpu_hwint_count;

  while(n--)
  {
    if(vpu_hardware_int_table[n].vpu == vpu && vpu_hardware_int_table[n].hw_int == hw_int)
      return 0;
  }

  n = vpu_hwint_count;
  
  if(!vpu_hwint_count++)
  {
    vpu_inthdlr_stack = malloc(256);
    if(!vpu_inthdlr_stack)
      return 0;
    vpu_hardware_int_table = malloc(vpu_hwint_count * sizeof(struct vpu_hw_int));
    if(!vpu_hardware_int_table)
    {
      nomemory:
      --vpu_hwint_count;
      free(vpu_inthdlr_stack);
      return 0;
    }
    vpu_inthdlr_stack += 256;
  }
  else
  {
    new_pointer = realloc(vpu_hardware_int_table, vpu_hwint_count * sizeof(struct vpu_hw_int));
    if(!new_pointer) goto nomemory;
    vpu_hardware_int_table = new_pointer;
  }

  if(!(new_pointer = new_vpu_interrupt_mask_handler(hw_int, &old_handler, chain)))
  {
    vpu_hardware_int_table = realloc(vpu_hardware_int_table, n * sizeof(struct vpu_hw_int));
    goto nomemory;
  }

  vpu_hardware_int_table[n].hw_int = hw_int;
  vpu_hardware_int_table[n].vpu = vpu;
  vpu_hardware_int_table[n].vpu_int = vpu_int;
  vpu_hardware_int_table[n].handler = new_pointer;  
  if((vpu_hardware_int_table[n].prev_int = old_handler) && chain)
  {
    if(chain == 1)
      func_ptr = (void __far*)&((unsigned char __far*)new_pointer)[0xB]; // Set JMP destination to the previous interrupt handler
    else if(chain == 2)
      func_ptr = (void __far*)&((unsigned char __far*)new_pointer)[0x2]; // far call address
    *func_ptr = (void __far*)vpu_hardware_int_table[n].prev_int;
  }
  _dos_setvect(hw_int, new_pointer);

  return 1;
}

static void remove_vpu_hw_int(struct vpu *vpu, unsigned int vpu_int)
{
  unsigned n = vpu_hwint_count;
//  void *func_ptr;

  while(n--)
    if(vpu_hardware_int_table[n].vpu == vpu && vpu_hardware_int_table[n].vpu_int == vpu_int)
      break;

  free(vpu_hardware_int_table[n].handler);
  
  _dos_setvect(vpu_hardware_int_table[n].hw_int, vpu_hardware_int_table[n].prev_int);

  for(;n<vpu_hwint_count-1;n++)
    vpu_hardware_int_table[n] = vpu_hardware_int_table[n+1];

  if(!(vpu_hwint_count = n))
  {
    free(vpu_hardware_int_table);
    free(vpu_inthdlr_stack-256);
  }

  else
    vpu_hardware_int_table = realloc(vpu_hardware_int_table, n* sizeof(struct vpu_hw_int));
}
