/********************************************************************** 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(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<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<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);