/*
Copywrite 1994 by Kevin P. Lawton 

This file is part of the IODEV (Input Output DEVices) component of BOCHS.

The IODEV component is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

The IODEV component is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with the IODEV component; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/



#include <stdio.h>
#include <assert.h>

#include "iodev.h"


typedef struct {
  Bit8u single_PIC;        /* 0=cascaded PIC, 1=master only */
  Bit8u interrupt_offset;  /* programmable interrupt vector offset */
  union {
    Bit8u   slave_connect_mask; /* for master, a bit for each interrupt line
                                   0=not connect to a slave, 1=connected */
    Bit8u   slave_id;           /* for slave, id number of slave PIC */
    } u;
  Bit8u sfnm;              /* specially fully nested mode: 0=no, 1=yes*/
  Bit8u buffered_mode;     /* 0=no buffered mode, 1=buffered mode */
  Bit8u master_slave;      /* master/slave: 0=slave PIC, 1=master PIC */
  Bit8u auto_eoi;          /* 0=manual EOI, 1=automatic EOI */
  Bit8u imr;               /* interrupt mask register, 1=masked */
  Bit8u isr;               /* in service register */
  Bit8u irr;               /* interrupt request register */
  Bit8u read_reg_select;   /* 0=IRR, 1=ISR */
  } bx_pic_t;

static bx_pic_t master_pic, slave_pic;


/* externs */
extern Boolean bx_INTR;
extern Bit8u   bx_INTR_vector;
extern Boolean bx_NMI;




  Bit8u
bx_pic_io_read_handler(Bit32u address)
{


  /*
   8259A PIC
   */


  switch (address) {
    case 0x20:
      if (master_pic.read_reg_select) { /* ISR */
	return(master_pic.isr);
	}
      else { /* IRR */
	return(master_pic.irr);
	}
      break;
    case 0x21:
      return(master_pic.imr);
      break;
    case 0xA0:
      if (slave_pic.read_reg_select) { /* ISR */
	return(slave_pic.isr);
	}
      else { /* IRR */
	return(slave_pic.irr);
	}
      break;
    case 0xA1:
      return(slave_pic.imr);
      break;
    }

  return(0); /* default if not found above */
}

  void
bx_pic_io_write_handler(Bit32u address, Bit8u value)
{
  int irq;
  Bit8u unmasked_requests;

  /*
   8259A PIC
   */

  switch (address) {
    case 0x20:
      switch (value) {
	case 0x0A: /* select read interrupt request register */
	  master_pic.read_reg_select = 0;
	  break;
	case 0x0B: /* select read interrupt in-service register */
	  master_pic.read_reg_select = 1;
	  break;
	case 0x20: /* end of interrupt command */
	  master_pic.isr = 0; /* clear in-service register */
	  if ((unmasked_requests = (master_pic.irr & ~master_pic.imr)) ) {
	    for (irq=7; irq>=0; irq--) {
	      if (unmasked_requests & (1 << irq)) {
		master_pic.irr &= ~(1 << irq);
		master_pic.isr  =  (1 << irq);
		bx_INTR = 1;
		bx_INTR_vector = master_pic.interrupt_offset + irq;
		break;
		} /* if (unmasked_requests & ... */
	      } /* for (irq=7 ... */
	    } /* if (unmasked_requests = ... */
	  break;
	} /* switch (value) */          
      break;

    case 0x21:
      master_pic.imr = value;
      return;
      break;

    case 0xA0:
      switch (value) {
	case 0x0A: /* select read interrupt request register */
	  slave_pic.read_reg_select = 0;
	  break;
	case 0x0B: /* select read interrupt in-service register */
	  slave_pic.read_reg_select = 1;
	  break;
	case 0x20: /* end of interrupt command */
	  slave_pic.isr = 0;  /* clear in-service register */
	  if ((unmasked_requests = (slave_pic.irr & ~slave_pic.imr)) ) {
	    for (irq=7; irq>=0; irq--) {
	      if (unmasked_requests & (1 << irq)) {
		slave_pic.irr &= ~(1 << irq);
		slave_pic.isr  =  (1 << irq);
		bx_INTR = 1;
		bx_INTR_vector = slave_pic.interrupt_offset + irq;
		break;
		} /* if (unmasked_requests & ... */
	      } /* for (irq=7 ... */
	    } /* if (unmasked_requests = ... */
	  break;
	} /* switch (value) */          
      break;

    case 0xA1:
      slave_pic.imr = value;
      return;
      break;
    } /* switch (address) */

  return;
}


  void
bx_init_pic(void)
{
  master_pic.single_PIC = 0;
  master_pic.interrupt_offset = 0x08; /* IRQ0 = INT 0x08 */
  /* slave PIC connected to IRQ2 of master */
  master_pic.u.slave_connect_mask = 0x04;
  master_pic.sfnm = 0; /* normal nested mode */
  master_pic.buffered_mode = 0; /* unbuffered mode */
  master_pic.master_slave  = 0; /* no meaning, buffered_mode=0 */
  master_pic.auto_eoi      = 0; /* manual EOI from CPU */
  master_pic.imr           = 0xFF; /* all IRQ's initially masked */
  master_pic.isr           = 0x00; /* no IRQ's in service */
  master_pic.irr           = 0x00; /* no IRQ's requested */
  master_pic.read_reg_select = 0; /* IRR */

  slave_pic.single_PIC = 0;
  slave_pic.interrupt_offset = 0x70; /* IRQ8 = INT 0x70 */
  slave_pic.u.slave_id = 0x02; /* slave PIC connected to IRQ2 of master */
  slave_pic.sfnm       = 0; /* normal nested mode */
  slave_pic.buffered_mode = 0; /* unbuffered mode */
  slave_pic.master_slave  = 0; /* no meaning, buffered_mode=0 */
  slave_pic.auto_eoi      = 0; /* manual EOI from CPU */
  slave_pic.imr           = 0xFF; /* all IRQ's initially masked */
  slave_pic.isr           = 0x00; /* no IRQ's in service */
  slave_pic.irr           = 0x00; /* no IRQ's requested */
  slave_pic.read_reg_select = 0; /* IRR */
}

  void
bx_trigger_irq(int irq_no)
{
  int irq_no_bitmask;

  assert(irq_no >= 0  &&  irq_no <= 15);

  if (irq_no <= 7) {
    irq_no_bitmask = 1 << irq_no;
    /* if irq is masked, do nothing */
    if ( master_pic.imr & irq_no_bitmask ) return;
    if ( master_pic.isr ) {
      master_pic.irr |= irq_no_bitmask;
      return;
      }
    /* irq not masked, and no irq being serviced. signal INTR line */
    master_pic.irr &= ~irq_no_bitmask; /* clear request bit */
    master_pic.isr  =  irq_no_bitmask; /* set in-service bit */
    bx_INTR = 1; /* signal the INTR pin */
    bx_INTR_vector = master_pic.interrupt_offset + irq_no; /* the irq that caused the interrupt */
    }
  else {
    irq_no_bitmask = 1 << (irq_no - 8);
    /* if irq is masked, do nothing */
    if ( slave_pic.imr & irq_no_bitmask ) return;
    if (master_pic.imr & 0x04) return; /* IRQ 2 masked */
    if ( slave_pic.isr ) {
      slave_pic.irr |= irq_no_bitmask;
      return;
      }
    /* irq not masked, and no irq being serviced.  also need
       to check IRQ in master since the slave is cascaded to it */
    slave_pic.irr  &= ~irq_no_bitmask; /* clear request bit */
    slave_pic.isr   =  irq_no_bitmask; /* set in-service bit */
    master_pic.irr &= ~0x04;
    master_pic.isr  =  0x04;
    bx_INTR = 1; /* signal the INTR pin */
    bx_INTR_vector = slave_pic.interrupt_offset + (irq_no-8); /* the irq that caused the interrupt */
    }
}
