diff options
Diffstat (limited to 'trunk/src/emu/machine/pic8259.c')
-rw-r--r-- | trunk/src/emu/machine/pic8259.c | 475 |
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); |