summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/pic8259.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/pic8259.c')
-rw-r--r--trunk/src/emu/machine/pic8259.c475
1 files changed, 475 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/pic8259.c b/trunk/src/emu/machine/pic8259.c
new file mode 100644
index 00000000000..6c79303d4be
--- /dev/null
+++ b/trunk/src/emu/machine/pic8259.c
@@ -0,0 +1,475 @@
+/**********************************************************************
+
+ 8259 PIC interface and emulation
+
+ The 8259 is a programmable interrupt controller used to multiplex
+ interrupts for x86 and other computers. The chip is set up by
+ writing a series of Initialization Command Words (ICWs) after which
+ the chip is operational and capable of dispatching interrupts. After
+ this, Operation Command Words (OCWs) can be written to control further
+ behavior.
+
+**********************************************************************/
+
+#include "emu.h"
+#include "machine/pic8259.h"
+
+#define IRQ_COUNT 8
+
+#define LOG_ICW 0
+#define LOG_OCW 0
+#define LOG_GENERAL 0
+
+typedef enum
+{
+ STATE_ICW1,
+ STATE_ICW2,
+ STATE_ICW3,
+ STATE_ICW4,
+ STATE_READY
+} pic8259_state_t;
+
+typedef struct pic8259 pic8259_t;
+
+struct pic8259
+{
+ devcb_resolved_write_line out_int_func;
+
+ devcb_resolved_read_line sp_en_func;
+
+ devcb_resolved_read8 read_slave_ack_func;
+
+ emu_timer *timer;
+
+ pic8259_state_t state;
+
+ UINT8 isr;
+ UINT8 irr;
+ UINT8 prio;
+ UINT8 imr;
+ UINT8 irq_lines;
+
+ UINT8 input;
+ UINT8 ocw3;
+
+ UINT8 master;
+ /* ICW1 state */
+ UINT32 level_trig_mode : 1;
+ UINT32 vector_size : 1;
+ UINT32 cascade : 1;
+ UINT32 icw4_needed : 1;
+ UINT32 vector_addr_low;
+ /* ICW2 state */
+ UINT8 base;
+ UINT8 vector_addr_high;
+
+ /* ICW3 state */
+ UINT8 slave;
+
+ /* ICW4 state */
+ UINT32 nested : 1;
+ UINT32 mode : 2;
+ UINT32 auto_eoi : 1;
+ UINT32 is_x86 : 1;
+};
+
+
+INLINE pic8259_t *get_safe_token(device_t *device) {
+ assert( device != NULL );
+ assert( device->type() == PIC8259 );
+ return ( pic8259_t *) downcast<legacy_device_base *>(device)->token();
+}
+
+
+static TIMER_CALLBACK( pic8259_timerproc )
+{
+ device_t *device = (device_t *)ptr;
+ pic8259_t *pic8259 = get_safe_token(device);
+ int irq;
+ UINT8 mask;
+
+ /* check the various IRQs */
+ for (irq = 0; irq < IRQ_COUNT; irq++)
+ {
+ mask = 1 << irq;
+
+ /* is this IRQ in service? */
+ if (pic8259->isr & mask)
+ {
+ if (LOG_GENERAL)
+ logerror("pic8259_timerproc(): PIC IRQ #%d still in service\n", irq);
+ break;
+ }
+
+ /* is this IRQ pending and enabled? */
+ if ((pic8259->state == STATE_READY) && (pic8259->irr & mask) && !(pic8259->imr & mask))
+ {
+ if (LOG_GENERAL)
+ logerror("pic8259_timerproc(): PIC triggering IRQ #%d\n", irq);
+ if (!BIT(pic8259->ocw3, 2))
+ pic8259->out_int_func(1);
+ return;
+ }
+ }
+ if (!BIT(pic8259->ocw3, 2))
+ pic8259->out_int_func(0);
+}
+
+
+INLINE void pic8259_set_timer(pic8259_t *pic8259)
+{
+ pic8259->timer->adjust(attotime::zero);
+}
+
+
+static void pic8259_set_irq_line(device_t *device, int irq, int state)
+{
+ pic8259_t *pic8259 = get_safe_token(device);
+ UINT8 mask = (1 << irq);
+
+ if (state)
+ {
+ /* setting IRQ line */
+ if (LOG_GENERAL)
+ logerror("pic8259_set_irq_line(): PIC set IRQ line #%d\n", irq);
+
+ if(pic8259->level_trig_mode || (!pic8259->level_trig_mode && !(pic8259->irq_lines & mask)))
+ pic8259->irr |= mask;
+ pic8259->irq_lines |= mask;
+ }
+ else
+ {
+ /* clearing IRQ line */
+ if (LOG_GENERAL)
+ logerror("pic8259_set_irq_line(): PIC cleared IRQ line #%d\n", irq);
+
+ pic8259->irq_lines &= ~mask;
+ pic8259->irr &= ~mask;
+ }
+ if (pic8259->mode & 0x02) pic8259->irr = pic8259->irq_lines;
+ pic8259_set_timer(pic8259);
+}
+
+WRITE_LINE_DEVICE_HANDLER( pic8259_ir0_w ) { pic8259_set_irq_line(device, 0, state); }
+WRITE_LINE_DEVICE_HANDLER( pic8259_ir1_w ) { pic8259_set_irq_line(device, 1, state); }
+WRITE_LINE_DEVICE_HANDLER( pic8259_ir2_w ) { pic8259_set_irq_line(device, 2, state); }
+WRITE_LINE_DEVICE_HANDLER( pic8259_ir3_w ) { pic8259_set_irq_line(device, 3, state); }
+WRITE_LINE_DEVICE_HANDLER( pic8259_ir4_w ) { pic8259_set_irq_line(device, 4, state); }
+WRITE_LINE_DEVICE_HANDLER( pic8259_ir5_w ) { pic8259_set_irq_line(device, 5, state); }
+WRITE_LINE_DEVICE_HANDLER( pic8259_ir6_w ) { pic8259_set_irq_line(device, 6, state); }
+WRITE_LINE_DEVICE_HANDLER( pic8259_ir7_w ) { pic8259_set_irq_line(device, 7, state); }
+
+int pic8259_acknowledge(device_t *device)
+{
+ pic8259_t *pic8259 = get_safe_token(device);
+ UINT8 mask;
+ int irq;
+
+ for (irq = 0; irq < IRQ_COUNT; irq++)
+ {
+ mask = 1 << irq;
+
+ /* is this IRQ pending and enabled? */
+ if ((pic8259->irr & mask) && !(pic8259->imr & mask))
+ {
+ if (LOG_GENERAL)
+ logerror("pic8259_acknowledge(): PIC acknowledge IRQ #%d\n", irq);
+ if (!pic8259->level_trig_mode)
+ pic8259->irr &= ~mask;
+
+ if (!pic8259->auto_eoi)
+ pic8259->isr |= mask;
+
+ pic8259_set_timer(pic8259);
+
+ if ((pic8259->cascade!=0) && (pic8259->master!=0) && (mask & pic8259->slave)) {
+ // it's from slave device
+ return pic8259->read_slave_ack_func(irq);
+ } else {
+ if (pic8259->is_x86) {
+ /* For x86 mode*/
+ return irq + pic8259->base;
+ } else {
+ /* in case of 8080/85) */
+ return 0xcd0000 + (pic8259->vector_addr_high << 8) + pic8259->vector_addr_low + (irq << (3-pic8259->vector_size));
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+
+READ8_DEVICE_HANDLER( pic8259_r )
+{
+ pic8259_t *pic8259 = get_safe_token(device);
+
+ /* NPW 18-May-2003 - Changing 0xFF to 0x00 as per Ruslan */
+ UINT8 data = 0x00;
+
+ switch(offset)
+ {
+ case 0: /* PIC acknowledge IRQ */
+ if ( pic8259->ocw3 & 0x04 )
+ {
+ /* Polling mode */
+ if ( pic8259->isr & ~pic8259->imr )
+ pic8259_acknowledge( device );
+
+ if ( pic8259->irr & ~pic8259->imr )
+ {
+ int irq;
+ for ( irq = 0; irq < IRQ_COUNT; irq++ )
+ {
+ if ( ( 1 << irq ) & pic8259->irr & ~pic8259->imr )
+ {
+ data = 0x80 | irq;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ switch ( pic8259->ocw3 & 0x03 )
+ {
+ case 2:
+ data = pic8259->irr;
+ break;
+ case 3:
+ data = pic8259->isr & ~pic8259->imr;
+ break;
+ default:
+ data = 0x00;
+ break;
+ }
+ }
+ break;
+
+ case 1: /* PIC mask register */
+ data = pic8259->imr;
+ break;
+ }
+ return data;
+}
+
+
+
+WRITE8_DEVICE_HANDLER( pic8259_w )
+{
+ pic8259_t *pic8259 = get_safe_token(device);
+
+ switch(offset)
+ {
+ case 0: /* PIC acknowledge IRQ */
+ if (data & 0x10)
+ {
+ /* write ICW1 - this pretty much resets the chip */
+ if (LOG_ICW)
+ logerror("pic8259_w(): ICW1; data=0x%02X\n", data);
+
+ pic8259->imr = 0x00;
+ pic8259->isr = 0x00;
+ pic8259->irr = 0x00;
+ pic8259->level_trig_mode = (data & 0x08) ? 1 : 0;
+ pic8259->vector_size = (data & 0x04) ? 1 : 0;
+ pic8259->cascade = (data & 0x02) ? 0 : 1;
+ pic8259->icw4_needed = (data & 0x01) ? 1 : 0;
+ pic8259->vector_addr_low = (data & 0xe0);
+ pic8259->state = STATE_ICW2;
+ }
+ else if (pic8259->state == STATE_READY)
+ {
+ if ((data & 0x98) == 0x08)
+ {
+ /* write OCW3 */
+ if (LOG_OCW)
+ logerror("pic8259_w(): OCW3; data=0x%02X\n", data);
+
+ pic8259->ocw3 = data;
+ }
+ else if ((data & 0x18) == 0x00)
+ {
+ int n = data & 7;
+ UINT8 mask = 1 << n;
+
+ /* write OCW2 */
+ if (LOG_OCW)
+ logerror("pic8259_w(): OCW2; data=0x%02X\n", data);
+
+ switch (data & 0xe0)
+ {
+ case 0x00:
+ pic8259->prio = 0;
+ break;
+ case 0x20:
+ for (n = 0, mask = 1<<pic8259->prio; n < 8; n++, mask = (mask<<1) | (mask>>7))
+ {
+ if (pic8259->isr & mask)
+ {
+ pic8259->isr &= ~mask;
+ pic8259->irr &= ~mask;
+ break;
+ }
+ }
+ break;
+ case 0x40:
+ break;
+ case 0x60:
+ if( pic8259->isr & mask )
+ {
+ pic8259->isr &= ~mask;
+ }
+ break;
+ case 0x80:
+ pic8259->prio = (pic8259->prio + 1) & 7;
+ break;
+ case 0xa0:
+ for (n = 0, mask = 1<<pic8259->prio; n < 8; n++, mask = (mask<<1) | (mask>>7))
+ {
+ if( pic8259->isr & mask )
+ {
+ pic8259->isr &= ~mask;
+ pic8259->prio = (pic8259->prio + 1) & 7;
+ break;
+ }
+ }
+ break;
+ case 0xc0:
+ pic8259->prio = n & 7;
+ break;
+ case 0xe0:
+ if( pic8259->isr & mask )
+ {
+ pic8259->isr &= ~mask;
+ pic8259->irr &= ~mask;
+ pic8259->prio = (pic8259->prio + 1) & 7;
+ }
+ break;
+ }
+ }
+ }
+ break;
+
+ case 1:
+ switch(pic8259->state)
+ {
+ case STATE_ICW1:
+ break;
+
+ case STATE_ICW2:
+ /* write ICW2 */
+ if (LOG_ICW)
+ logerror("pic8259_w(): ICW2; data=0x%02X\n", data);
+
+ pic8259->base = data & 0xf8;
+ pic8259->vector_addr_high = data ;
+ if (pic8259->cascade)
+ pic8259->state = STATE_ICW3;
+ else
+ pic8259->state = pic8259->icw4_needed ? STATE_ICW4 : STATE_READY;
+ break;
+
+ case STATE_ICW3:
+ /* write ICW3 */
+ if (LOG_ICW)
+ logerror("pic8259_w(): ICW3; data=0x%02X\n", data);
+
+ pic8259->slave = data;
+ pic8259->state = pic8259->icw4_needed ? STATE_ICW4 : STATE_READY;
+ break;
+
+ case STATE_ICW4:
+ /* write ICW4 */
+ if (LOG_ICW)
+ logerror("pic8259_w(): ICW4; data=0x%02X\n", data);
+
+ pic8259->nested = (data & 0x10) ? 1 : 0;
+ pic8259->mode = (data >> 2) & 3;
+ pic8259->auto_eoi = (data & 0x02) ? 1 : 0;
+ pic8259->is_x86 = (data & 0x01) ? 1 : 0;
+ pic8259->state = STATE_READY;
+ break;
+
+ case STATE_READY:
+ /* write OCW1 - set interrupt mask register */
+ if (LOG_OCW)
+ logerror("pic8259_w(): OCW1; data=0x%02X\n", data);
+
+ pic8259->imr = data;
+ break;
+ }
+ break;
+ }
+ pic8259_set_timer(pic8259);
+}
+
+
+
+static DEVICE_START( pic8259 )
+{
+ pic8259_t *pic8259 = get_safe_token(device);
+ const struct pic8259_interface *intf = (const struct pic8259_interface *)device->static_config();
+
+ assert(intf != NULL);
+
+ pic8259->timer = device->machine().scheduler().timer_alloc( FUNC(pic8259_timerproc), (void *)device );
+
+ /* resolve callbacks */
+ pic8259->out_int_func.resolve(intf->out_int_func, *device);
+ pic8259->sp_en_func.resolve(intf->sp_en_func, *device);
+ pic8259->read_slave_ack_func.resolve(intf->read_slave_ack_func, *device);
+}
+
+
+static DEVICE_RESET( pic8259 ) {
+ pic8259_t *pic8259 = get_safe_token(device);
+
+ pic8259->state = STATE_READY;
+ pic8259->isr = 0;
+ pic8259->irr = 0;
+ pic8259->irq_lines = 0;
+ pic8259->prio = 0;
+ pic8259->imr = 0;
+ pic8259->input = 0;
+ pic8259->ocw3 = 2;
+ pic8259->level_trig_mode = 0;
+ pic8259->vector_size = 0;
+ pic8259->cascade = 0;
+ pic8259->icw4_needed = 0;
+ pic8259->base = 0;
+ pic8259->slave = 0;
+ pic8259->nested = 0;
+ pic8259->mode = 0;
+ pic8259->auto_eoi = 0;
+ pic8259->is_x86 = 0;
+ pic8259->vector_addr_low = 0;
+ pic8259->vector_addr_high = 0;
+
+ pic8259->master = pic8259->sp_en_func();
+}
+
+
+DEVICE_GET_INFO( pic8259 ) {
+ switch ( state ) {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(pic8259_t); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(pic8259); break;
+ case DEVINFO_FCT_STOP: /* nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(pic8259); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "Intel PIC8259"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "PIC8259"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.00"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright the MAME and MESS Teams"); break;
+ }
+}
+
+
+DEFINE_LEGACY_DEVICE(PIC8259, pic8259);