diff options
Diffstat (limited to 'src/emu/machine/z80pio.c')
-rw-r--r-- | src/emu/machine/z80pio.c | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/src/emu/machine/z80pio.c b/src/emu/machine/z80pio.c new file mode 100644 index 00000000000..81f293b83c9 --- /dev/null +++ b/src/emu/machine/z80pio.c @@ -0,0 +1,537 @@ +/*************************************************************************** + + Z80 PIO implementation + + based on original version (c) 1997, Tatsuyuki Satoh + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "driver.h" +#include "z80pio.h" +#include "cpu/z80/z80.h" +#include "cpu/z80/z80daisy.h" + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define VERBOSE 0 + +#if VERBOSE +#define VPRINTF(x) logerror x +#else +#define VPRINTF(x) +#endif + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define PIO_MODE0 0x00 /* output mode */ +#define PIO_MODE1 0x01 /* input mode */ +#define PIO_MODE2 0x02 /* i/o mode */ +#define PIO_MODE3 0x03 /* bit mode */ + +/* pio controll port operation (bit 0-3) */ +#define PIO_OP_MODE 0x0f /* mode select */ +#define PIO_OP_INTC 0x07 /* interrupt controll */ +#define PIO_OP_INTE 0x03 /* interrupt enable */ + +/* pio interrupt controll nit */ +#define PIO_INT_ENABLE 0x80 /* ENABLE : 0=disable , 1=enable */ +#define PIO_INT_AND 0x40 /* LOGIC : 0=OR , 1=AND */ +#define PIO_INT_HIGH 0x20 /* LEVEL : 0=low , 1=high */ +#define PIO_INT_MASK 0x10 /* MASK : 0=off , 1=on */ + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct _z80pio +{ + UINT8 vector[2]; /* interrupt vector */ + void (*intr)(int which); /* interrupt callbacks */ + void (*rdyr[2])(int data); /* RDY active callback */ + UINT8 mode[2]; /* mode 00=in,01=out,02=i/o,03=bit*/ + UINT8 enable[2]; /* interrupt enable */ + UINT8 mask[2]; /* mask folowers */ + UINT8 dir[2]; /* direction (bit mode) */ + UINT8 rdy[2]; /* ready pin level */ + UINT8 in[2]; /* input port data */ + UINT8 out[2]; /* output port */ + UINT8 strobe[2]; /* strobe inputs */ + UINT8 int_state[2]; /* interrupt status (daisy chain) */ +}; +typedef struct _z80pio z80pio; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static z80pio pios[MAX_PIO]; + + + +/*************************************************************************** + INTERNAL STATE MANAGEMENT +***************************************************************************/ + +static void set_rdy(z80pio *pio, int ch, int state) +{ + /* set state */ + pio->rdy[ch] = state; + + /* call callback with state */ + if (pio->rdyr[ch]) + (*pio->rdyr[ch])(pio->rdy[ch]); +} + + +static void interrupt_check(int which) +{ + z80pio *pio = pios + which; + + /* if we have a callback, update it with the current state */ + if (pio->intr) + (*pio->intr)((z80pio_irq_state(which) & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE); +} + + +static void update_irq_state(z80pio *pio, int ch) +{ + int old_state = pio->int_state[ch]; + int irq = 0; + int data; + if (pio->mode[ch] == 0x13) return; + + /* only check if interrupts are enabled */ + if (pio->enable[ch] & PIO_INT_ENABLE) + { + /* in mode 3, interrupts are tricky */ + if (pio->mode[ch] == PIO_MODE3) + { + /* fetch input data (ignore output lines) */ + data = pio->in[ch] & pio->dir[ch]; + + /* keep only relevant bits */ + data &= ~pio->mask[ch]; + + /* if active low, invert the bits */ + if (!(pio->enable[ch] & PIO_INT_HIGH)) + data ^= pio->mask[ch]; + + /* if AND logic, interrupt if all bits are set */ + if (pio->enable[ch] & PIO_INT_AND) + irq = (data == pio->mask[ch]); + + /* otherwise, interrupt if at least one bit is set */ + else + irq = (data != 0); + + /* if portB, portA mode 2 check */ + if (ch && (pio->mode[0] == PIO_MODE2)) + { + if (pio->rdy[ch] == 0) + irq = 1; + } + } + + /* otherwise, just interrupt when ready is cleared */ + else + irq = (pio->rdy[ch] == 0); + } + + if (irq) + pio->int_state[ch] |= Z80_DAISY_INT; + else + pio->int_state[ch] &= ~Z80_DAISY_INT; + + if (old_state != pio->int_state[ch]) + interrupt_check(pio - pios); +} + + + +/*************************************************************************** + INITIALIZATION/CONFIGURATION +***************************************************************************/ + +void z80pio_init(int which, z80pio_interface *intf) +{ + z80pio *pio = pios + which; + + assert(which < MAX_PIO); + + memset(pio, 0, sizeof(*pio)); + + pio->intr = intf->intr; + pio->rdyr[0] = intf->rdyA; + pio->rdyr[1] = intf->rdyB; + z80pio_reset(which); + + state_save_register_item_array("z80pio", which, pio->vector); + state_save_register_item_array("z80pio", which, pio->mode); + state_save_register_item_array("z80pio", which, pio->enable); + state_save_register_item_array("z80pio", which, pio->mask); + state_save_register_item_array("z80pio", which, pio->dir); + state_save_register_item_array("z80pio", which, pio->rdy); + state_save_register_item_array("z80pio", which, pio->in); + state_save_register_item_array("z80pio", which, pio->out); + state_save_register_item_array("z80pio", which, pio->strobe); + state_save_register_item_array("z80pio", which, pio->int_state); +} + + +void z80pio_reset(int which) +{ + z80pio *pio = pios + which; + int i; + + for (i = 0; i < 2; i++) + { + pio->mask[i] = 0xff; /* mask all on */ + pio->enable[i] = 0x00; /* disable */ + pio->mode[i] = 0x01; /* mode input */ + pio->dir[i] = 0x01; /* dir input */ + set_rdy(pio, i, 0); /* RDY = low */ + pio->out[i] = 0x00; /* outdata = 0 */ + pio->int_state[i] = 0; + pio->strobe[i] = 0; + } + interrupt_check(which); +} + + + +/*************************************************************************** + CONTROL REGISTER READ/WRITE +***************************************************************************/ + +void z80pio_c_w(int which, int ch, UINT8 data) +{ + z80pio *pio = pios + which; + + /* load direction phase ? */ + if (pio->mode[ch] == 0x13) + { + logerror("PIO-%c Bits %02x\n", 'A' + ch, data); + pio->dir[ch] = data; + pio->mode[ch] = 0x03; + return; + } + + /* load mask folows phase ? */ + if (pio->enable[ch] & PIO_INT_MASK) + { + /* load mask folows */ + pio->mask[ch] = data; + pio->enable[ch] &= ~PIO_INT_MASK; + logerror("PIO-%c interrupt mask %02x\n",'A'+ch,data ); + return; + } + + switch (data & 0x0f) + { + case PIO_OP_MODE: /* mode select 0=out,1=in,2=i/o,3=bit */ + pio->mode[ch] = (data >> 6); + logerror("PIO-%c Mode %x\n", 'A' + ch, pio->mode[ch]); + if (pio->mode[ch] == 0x03) + pio->mode[ch] = 0x13; + return; + + case PIO_OP_INTC: /* interrupt control */ + pio->enable[ch] = data & 0xf0; + pio->mask[ch] = 0x00; + /* when interrupt enable , set vector request flag */ + logerror("PIO-%c Controll %02x\n", 'A' + ch, data); + break; + + case PIO_OP_INTE: /* interrupt enable controll */ + pio->enable[ch] &= ~PIO_INT_ENABLE; + pio->enable[ch] |= (data & PIO_INT_ENABLE); + logerror("PIO-%c enable %02x\n", 'A' + ch, data & 0x80); + break; + + default: + if (!(data & 1)) + { + pio->vector[ch] = data; + logerror("PIO-%c vector %02x\n", 'A' + ch, data); + } + else + logerror("PIO-%c illegal command %02x\n", 'A' + ch, data); + break; + } + + /* interrupt check */ + update_irq_state(pio, ch); +} + + +UINT8 z80pio_c_r(int which, int ch) +{ + logerror("PIO-%c controll read\n", 'A' + ch ); + return 0; +} + + + +/*************************************************************************** + DATA REGISTER READ/WRITE +***************************************************************************/ + +void z80pio_d_w(int which, int ch, UINT8 data) +{ + z80pio *pio = pios + which; + + pio->out[ch] = data; /* latch out data */ + switch (pio->mode[ch]) + { + case PIO_MODE0: /* mode 0 output */ + case PIO_MODE2: /* mode 2 i/o */ + set_rdy(pio, ch, 1); /* ready = H */ + update_irq_state(pio, ch); + return; + + case PIO_MODE1: /* mode 1 input */ + case PIO_MODE3: /* mode 0 bit */ + return; + + default: + logerror("PIO-%c data write,bad mode\n",'A'+ch ); + } +} + + +UINT8 z80pio_d_r(int which, int ch) +{ + z80pio *pio = pios + which; + + switch (pio->mode[ch]) + { + case PIO_MODE0: /* mode 0 output */ + return pio->out[ch]; + + case PIO_MODE1: /* mode 1 input */ + set_rdy(pio, ch, 1); /* ready = H */ + update_irq_state(pio, ch); + return pio->in[ch]; + + case PIO_MODE2: /* mode 2 i/o */ + if (ch) logerror("PIO-B mode 2 \n"); + set_rdy(pio, 1, 1); /* brdy = H */ + update_irq_state(pio, ch); + return pio->in[ch]; + + case PIO_MODE3: /* mode 3 bit */ + return (pio->in[ch] & pio->dir[ch]) | (pio->out[ch] & ~pio->dir[ch]); + } + logerror("PIO-%c data read,bad mode\n",'A'+ch ); + return 0; +} + + + +/*************************************************************************** + PORT I/O +***************************************************************************/ + +void z80pio_p_w(int which, UINT8 ch, UINT8 data) +{ + z80pio *pio = pios + which; + + pio->in[ch] = data; + switch (pio->mode[ch]) + { + case PIO_MODE0: + logerror("PIO-%c OUTPUT mode and data write\n",'A'+ch ); + break; + + case PIO_MODE2: /* only port A */ + ch = 1; /* handshake and IRQ is use portB */ + + case PIO_MODE1: + set_rdy(pio, ch, 0); + update_irq_state(pio, ch); + break; + + case PIO_MODE3: + /* irq check */ + update_irq_state(pio, ch); + break; + } +} + + +int z80pio_p_r(int which, UINT8 ch) +{ + z80pio *pio = pios + which; + + switch (pio->mode[ch]) + { + case PIO_MODE2: /* port A only */ + case PIO_MODE0: + set_rdy(pio, ch, 0); + update_irq_state(pio, ch); + break; + + case PIO_MODE1: + logerror("PIO-%c INPUT mode and data read\n",'A'+ch ); + break; + + case PIO_MODE3: + return (pio->in[ch] & pio->dir[ch]) | (pio->out[ch] & ~pio->dir[ch]); + } + return pio->out[ch]; +} + + +WRITE8_HANDLER( z80pioA_0_p_w ) { z80pio_p_w(0, 0, data); } +WRITE8_HANDLER( z80pioB_0_p_w ) { z80pio_p_w(0, 1, data); } +READ8_HANDLER( z80pioA_0_p_r ) { return z80pio_p_r(0, 0); } +READ8_HANDLER( z80pioB_0_p_r ) { return z80pio_p_r(0, 1); } + +WRITE8_HANDLER( z80pioA_1_p_w ) { z80pio_p_w(1, 0, data); } +WRITE8_HANDLER( z80pioB_1_p_w ) { z80pio_p_w(1, 1, data); } +READ8_HANDLER( z80pioA_1_p_r ) { return z80pio_p_r(1, 0); } +READ8_HANDLER( z80pioB_1_p_r ) { return z80pio_p_r(1, 1); } + + + +/*************************************************************************** + STROBE STATE MANAGEMENT +***************************************************************************/ + +static void z80pio_update_strobe(int which, int ch, int state) +{ + z80pio *pio = pios + which; + + switch (pio->mode[ch]) + { + /* output mode: a positive edge is used by peripheral to acknowledge + the receipt of data */ + case PIO_MODE0: + { + /* ensure valid */ + state = state & 0x01; + + /* strobe changed state? */ + if ((pio->strobe[ch] ^ state) != 0) + { + /* yes */ + if (state != 0) + { + /* positive edge */ + logerror("PIO-%c positive strobe\n",'A' + ch); + + /* ready is now inactive */ + set_rdy(pio, ch, 0); + + /* int enabled? */ + if (pio->enable[ch] & PIO_INT_ENABLE) + { + /* trigger an int request */ + pio->int_state[ch] |= Z80_DAISY_INT; + } + } + } + + /* store strobe state */ + pio->strobe[ch] = state; + + /* check interrupt */ + interrupt_check(which); + } + break; + + /* input mode: strobe is used by peripheral to load data from the peripheral + into port a input register, data loaded into pio when signal is active */ + + default: + break; + } +} + + +void z80pio_astb_w(int which, int state) { z80pio_update_strobe(which, 0, state); } +void z80pio_bstb_w(int which, int state) { z80pio_update_strobe(which, 1, state); } + + + +/*************************************************************************** + DAISY CHAIN INTERFACE +***************************************************************************/ + +int z80pio_irq_state(int which) +{ + z80pio *pio = pios + which; + int state = 0; + int ch; + + /* loop over all channels */ + for (ch = 0; ch < 2; ch++) + { + /* if we're servicing a request, don't indicate more interrupts */ + if (pio->int_state[ch] & Z80_DAISY_IEO) + { + state |= Z80_DAISY_IEO; + break; + } + state |= pio->int_state[ch]; + } + return state; +} + + +int z80pio_irq_ack(int which) +{ + z80pio *pio = pios + which; + int ch; + + /* loop over all channels */ + for (ch = 0; ch < 2; ch++) + + /* find the first channel with an interrupt requested */ + if (pio->int_state[ch] & Z80_DAISY_INT) + { + /* clear interrupt, switch to the IEO state, and update the IRQs */ + pio->int_state[ch] = Z80_DAISY_IEO; + interrupt_check(which); + return pio->vector[ch]; + } + + logerror("z80pio_irq_ack: failed to find an interrupt to ack!"); + return pio->vector[0]; +} + + +void z80pio_irq_reti(int which) +{ + z80pio *pio = pios + which; + int ch; + + /* loop over all channels */ + for (ch = 0; ch < 2; ch++) + + /* find the first channel with an IEO pending */ + if (pio->int_state[ch] & Z80_DAISY_IEO) + { + /* clear the IEO state and update the IRQs */ + pio->int_state[ch] &= ~Z80_DAISY_IEO; + interrupt_check(which); + return; + } + + logerror("z80pio_irq_reti: failed to find an interrupt to clear IEO on!"); +} |