/********************************************************************* 8255ppi.c Intel 8255 PPI I/O chip NOTE: When port is input, then data present on the ports outputs is 0xff The 8255 PPI has three basic modes: Mode 0: Basic Input/Output Mode 1: Strobed Input/Output Mode 2: Strobed Bi-directional Bus Control Word: bit 7 - Mode set flag (1=active) bit 6-5 - Group A Mode selection 00 - Mode 0 01 - Mode 1 1x - Mode 2 bit 4 - Port A direction (1=input 0=output) bit 3 - Port C upper direction (1=input 0=output) bit 2 - Group B Mode selection 0 - Mode 0 1 - Mode 1 bit 1 - Port B direction (1=input 0=output) bit 0 - Port C lower direction (1=input 0=output) Port A and Port C upper are in group A, and Port B and Port C lower are in group B Mode 0: Basic Input/Output In Mode 0, each of the ports (A, B and C) operate as independent ports for whom direction can be set independently. Port C Usage In Mode 0: bits 7-4 Input/Output A (direction specified by ctrl bit 3) bits 3-0 Input/Output B (direction specified by ctrl bit 0) Mode 1: Strobed Input/Output In Mode 1, Port A and Port B use their resepective parts of Port C to either generate or accept handshaking signals. The STB (strobe) input "loads" data into the port, and the IBF (input buffer full) output is then asserted, and the INTR (interrupt request) output is triggered if interrupts are enabled. Bits 7-6 of Port C remain usable as conventional IO. Group A Port C Usage In Mode 1: bits 7-6 Input/Output (direction specified by ctrl bit 3) bit 5 IBFa (input buffer full A) output bit 4 !STBa (strobe A) input bit 3 INTRa (interrupt request A) output Group B Port C Usage In Mode 1: bit 2 !STBb (strobe B) input bit 1 IBFb (input buffer full B) output bit 0 INTRb (interrupt request B) output Mode 2: Strobed Bi-directional Bus Mode 2 is used to implement a two way handshaking bus. When data is written to port A, the OBF (output buffer full) output will be asserted by the PPI. However, port A will not be asserted unless the ACK input is asserted, otherwise port A will be high impedence. The STB input and IBF output behaves similar to how it does under mode 1. Bits 2-0 of Port C remain usable as conventional IO. Port C Usage In Mode 2: bit 7 !OBFa (output buffer full A) output bit 6 !ACKa (acknowledge A) input bit 5 IBFa (interrupt buffer full A) output bit 4 !STBa (strobe A) input bit 3 INTRa (interrupt A) output bit 2-0 Reserved by Group B KT 10/01/2000 - Added bit set/reset feature for control port - Added more accurate port i/o data handling - Added output reset when control mode is programmed *********************************************************************/ #include "emu.h" #include "8255ppi.h" #include "devhelpr.h" //************************************************************************** // LIVE DEVICE //************************************************************************** // device type definition const device_type PPI8255 = &device_creator; //------------------------------------------------- // ppi8255_device - constructor //------------------------------------------------- ppi8255_device::ppi8255_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, PPI8255, "Intel PPI8255", tag, owner, clock), m_control(0) { } //------------------------------------------------- // device_config_complete - perform any // operations now that the configuration is // complete //------------------------------------------------- void ppi8255_device::device_config_complete() { // inherit a copy of the static data const ppi8255_interface *intf = reinterpret_cast(static_config()); if (intf != NULL) { *static_cast(this) = *intf; } // or initialize to defaults if none provided else { memset(&m_port_a_read, 0, sizeof(m_port_a_read)); memset(&m_port_b_read, 0, sizeof(m_port_b_read)); memset(&m_port_c_read, 0, sizeof(m_port_c_read)); memset(&m_port_a_write, 0, sizeof(m_port_a_write)); memset(&m_port_b_write, 0, sizeof(m_port_b_write)); memset(&m_port_c_write, 0, sizeof(m_port_c_write)); } } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void ppi8255_device::device_start() { m_port_read[0].resolve(m_port_a_read, *this); m_port_read[1].resolve(m_port_b_read, *this); m_port_read[2].resolve(m_port_c_read, *this); m_port_write[0].resolve(m_port_a_write, *this); m_port_write[1].resolve(m_port_b_write, *this); m_port_write[2].resolve(m_port_c_write, *this); /* register for state saving */ save_item(NAME(m_group_a_mode)); save_item(NAME(m_group_b_mode)); save_item(NAME(m_port_a_dir)); save_item(NAME(m_port_b_dir)); save_item(NAME(m_port_ch_dir)); save_item(NAME(m_port_cl_dir)); save_item(NAME(m_obf_a)); save_item(NAME(m_obf_b)); save_item(NAME(m_ibf_a)); save_item(NAME(m_ibf_b)); save_item(NAME(m_inte_a)); save_item(NAME(m_inte_b)); save_item(NAME(m_inte_1)); save_item(NAME(m_inte_2)); save_item(NAME(m_in_mask)); save_item(NAME(m_out_mask)); save_item(NAME(m_read)); save_item(NAME(m_latch)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void ppi8255_device::device_reset() { m_group_a_mode = 0; m_group_b_mode = 0; m_port_a_dir = 0; m_port_b_dir = 0; m_port_ch_dir = 0; m_port_cl_dir = 0; m_obf_a = m_ibf_a = 0; m_obf_b = m_ibf_b = 0; m_inte_a = m_inte_b = m_inte_1 = m_inte_2 = 0; for (int i = 0; i < 3; i++) { m_in_mask[i] = m_out_mask[i] = m_read[i] = m_latch[i] = m_output[i] = 0; } set_mode(0x9b, 0); /* Mode 0, all ports set to input */ } void ppi8255_device::ppi8255_get_handshake_signals(int is_read, UINT8 *result) { UINT8 handshake = 0x00; UINT8 mask = 0x00; /* group A */ if (m_group_a_mode == 1) { if (m_port_a_dir) { handshake |= m_ibf_a ? 0x20 : 0x00; handshake |= (m_ibf_a && m_inte_a) ? 0x08 : 0x00; mask |= 0x28; } else { handshake |= m_obf_a ? 0x00 : 0x80; handshake |= (m_obf_a && m_inte_a) ? 0x08 : 0x00; mask |= 0x88; } } else if (m_group_a_mode == 2) { handshake |= m_obf_a ? 0x00 : 0x80; handshake |= m_ibf_a ? 0x20 : 0x00; handshake |= ((m_obf_a && m_inte_1) || (m_ibf_a && m_inte_2)) ? 0x08 : 0x00; mask |= 0xA8; } /* group B */ if (m_group_b_mode == 1) { if (m_port_b_dir) { handshake |= m_ibf_b ? 0x02 : 0x00; handshake |= (m_ibf_b && m_inte_b) ? 0x01 : 0x00; mask |= 0x03; } else { handshake |= m_obf_b ? 0x00 : 0x02; handshake |= (m_obf_b && m_inte_b) ? 0x01 : 0x00; mask |= 0x03; } } *result &= ~mask; *result |= handshake & mask; } void ppi8255_device::ppi8255_input(int port, UINT8 data) { int changed = 0; m_read[port] = data; /* port C is special */ if (port == 2) { if (((m_group_a_mode == 1) && (m_port_a_dir == 0)) || (m_group_a_mode == 2)) { /* is !ACKA asserted? */ if (m_obf_a && !(data & 0x40)) { m_obf_a = 0; changed = 1; } } if (((m_group_a_mode == 1) && (m_port_a_dir == 1)) || (m_group_a_mode == 2)) { /* is !STBA asserted? */ if (!m_ibf_a && !(data & 0x10)) { m_ibf_a = 1; changed = 1; } } if ((m_group_b_mode == 1) && (m_port_b_dir == 0)) { /* is !ACKB asserted? */ if (m_obf_b && !(data & 0x04)) { m_obf_b = 0; changed = 1; } } if ((m_group_b_mode == 1) && (m_port_b_dir == 1)) { /* is !STBB asserted? */ if (!m_ibf_b && !(data & 0x04)) { m_ibf_b = 1; changed = 1; } } if (changed) { ppi8255_write_port(2); } } } UINT8 ppi8255_device::ppi8255_read_port(int port) { UINT8 result = 0x00; if (m_in_mask[port]) { ppi8255_input(port, m_port_read[port](0)); result |= m_read[port] & m_in_mask[port]; } result |= m_latch[port] & m_out_mask[port]; switch (port) { case 0: /* clear input buffer full flag */ m_ibf_a = 0; break; case 1: /* clear input buffer full flag */ m_ibf_b = 0; break; case 2: /* read special port 2 signals */ ppi8255_get_handshake_signals(1, &result); break; } return result; } READ8_DEVICE_HANDLER_TRAMPOLINE(ppi8255, ppi8255_r) { UINT8 result = 0; offset %= 4; switch(offset) { case 0: /* Port A read */ case 1: /* Port B read */ case 2: /* Port C read */ result = ppi8255_read_port(offset); break; case 3: /* Control word */ result = m_control; break; } return result; } void ppi8255_device::ppi8255_write_port(int port) { UINT8 write_data = m_latch[port] & m_out_mask[port]; write_data |= 0xFF & ~m_out_mask[port]; /* write out special port 2 signals */ if (port == 2) { ppi8255_get_handshake_signals(0, &write_data); } m_output[port] = write_data; m_port_write[port](0, write_data); } WRITE8_DEVICE_HANDLER_TRAMPOLINE(ppi8255, ppi8255_w) { offset %= 4; switch( offset ) { case 0: /* Port A write */ case 1: /* Port B write */ case 2: /* Port C write */ m_latch[offset] = data; ppi8255_write_port(offset); switch(offset) { case 0: if (!m_port_a_dir && (m_group_a_mode != 0)) { m_obf_a = 1; ppi8255_write_port(2); } break; case 1: if (!m_port_b_dir && (m_group_b_mode != 0)) { m_obf_b = 1; ppi8255_write_port(2); } break; } break; case 3: /* Control word */ if (data & 0x80) { set_mode(data & 0x7f, 1); } else { /* bit set/reset */ int bit = (data >> 1) & 0x07; if (data & 1) { m_latch[2] |= (1 << bit); /* set bit */ } else { m_latch[2] &= ~(1 << bit); /* reset bit */ } if (m_group_b_mode == 1) { if (bit == 2) { m_inte_b = data & 1; } } if (m_group_a_mode == 1) { if (bit == 4 && m_port_a_dir) { m_inte_a = data & 1; } if (bit == 6 && !m_port_a_dir) { m_inte_a = data & 1; } } if (m_group_a_mode == 2) { if (bit == 4) { m_inte_2 = data & 1; } if (bit == 6) { m_inte_1 = data & 1; } } ppi8255_write_port(2); } break; } } void ppi8255_device::set_mode(int data, int call_handlers) { /* parse out mode */ m_group_a_mode = (data >> 5) & 3; m_group_b_mode = (data >> 2) & 1; m_port_a_dir = (data >> 4) & 1; m_port_b_dir = (data >> 1) & 1; m_port_ch_dir = (data >> 3) & 1; m_port_cl_dir = (data >> 0) & 1; /* normalize group_a_mode */ if (m_group_a_mode == 3) { m_group_a_mode = 2; } /* Port A direction */ if (m_group_a_mode == 2) { m_in_mask[0] = 0xFF; m_out_mask[0] = 0xFF; /* bidirectional */ } else { if (m_port_a_dir) { m_in_mask[0] = 0xFF; m_out_mask[0] = 0x00; /* input */ } else { m_in_mask[0] = 0x00; m_out_mask[0] = 0xFF; /* output */ } } /* Port B direction */ if (m_port_b_dir) { m_in_mask[1] = 0xFF; m_out_mask[1] = 0x00; /* input */ } else { m_in_mask[1] = 0x00; m_out_mask[1] = 0xFF; /* output */ } /* Port C upper direction */ if (m_port_ch_dir) { m_in_mask[2] = 0xF0; m_out_mask[2] = 0x00; /* input */ } else { m_in_mask[2] = 0x00; m_out_mask[2] = 0xF0; /* output */ } /* Port C lower direction */ if (m_port_cl_dir) { m_in_mask[2] |= 0x0F; /* input */ } else { m_out_mask[2] |= 0x0F; /* output */ } /* now depending on the group modes, certain Port C lines may be replaced * with varying control signals */ switch(m_group_a_mode) { case 0: /* Group A mode 0 */ /* no changes */ break; case 1: /* Group A mode 1 */ /* bits 5-3 are reserved by Group A mode 1 */ m_in_mask[2] &= ~0x38; m_out_mask[2] &= ~0x38; break; case 2: /* Group A mode 2 */ /* bits 7-3 are reserved by Group A mode 2 */ m_in_mask[2] &= ~0xF8; m_out_mask[2] &= ~0xF8; break; } switch(m_group_b_mode) { case 0: /* Group B mode 0 */ /* no changes */ break; case 1: /* Group B mode 1 */ /* bits 2-0 are reserved by Group B mode 1 */ m_in_mask[2] &= ~0x07; m_out_mask[2] &= ~0x07; break; } /* KT: 25-Dec-99 - 8255 resets latches when mode set */ m_latch[0] = m_latch[1] = m_latch[2] = 0; if (call_handlers) { for (int i = 0; i < 3; i++) { ppi8255_write_port(i); } } /* reset flip-flops */ m_obf_a = m_ibf_a = 0; m_obf_b = m_ibf_b = 0; m_inte_a = m_inte_b = m_inte_1 = m_inte_2 = 0; /* store control word */ m_control = data; } void ppi8255_set_port_a_read(device_t *device, const devcb_read8 *config) { downcast(device)->ppi8255_set_port_read(0, *config); } void ppi8255_set_port_b_read(device_t *device, const devcb_read8 *config) { downcast(device)->ppi8255_set_port_read(1, *config); } void ppi8255_set_port_c_read(device_t *device, const devcb_read8 *config) { downcast(device)->ppi8255_set_port_read(2, *config); } void ppi8255_set_port_a_write(device_t *device, const devcb_write8 *config) { downcast(device)->ppi8255_set_port_write(0, *config); } void ppi8255_set_port_b_write(device_t *device, const devcb_write8 *config) { downcast(device)->ppi8255_set_port_write(1, *config); } void ppi8255_set_port_c_write(device_t *device, const devcb_write8 *config) { downcast(device)->ppi8255_set_port_write(2, *config); } void ppi8255_set_port_a( device_t *device, UINT8 data ) { downcast(device)->ppi8255_set_port(0, data); } void ppi8255_set_port_b( device_t *device, UINT8 data ) { downcast(device)->ppi8255_set_port(1, data); } void ppi8255_set_port_c( device_t *device, UINT8 data ) { downcast(device)->ppi8255_set_port(2, data); } UINT8 ppi8255_get_port_a( device_t *device ) { return downcast(device)->ppi8255_get_port(0); } UINT8 ppi8255_get_port_b( device_t *device ) { return downcast(device)->ppi8255_get_port(1); } UINT8 ppi8255_get_port_c( device_t *device ) { return downcast(device)->ppi8255_get_port(2); }