diff options
Diffstat (limited to 'trunk/src/emu/machine/i8255.c')
-rw-r--r-- | trunk/src/emu/machine/i8255.c | 1031 |
1 files changed, 1031 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/i8255.c b/trunk/src/emu/machine/i8255.c new file mode 100644 index 00000000000..b2543a5a231 --- /dev/null +++ b/trunk/src/emu/machine/i8255.c @@ -0,0 +1,1031 @@ +/********************************************************************** + + Intel 8255(A) Programmable Peripheral Interface emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "i8255.h" +#include "machine/devhelpr.h" + + + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + +#define LOG 0 + + +enum +{ + PORT_A = 0, + PORT_B, + PORT_C, + CONTROL +}; + + +enum +{ + GROUP_A = 0, + GROUP_B +}; + + +enum +{ + MODE_0 = 0, + MODE_1, + MODE_2 +}; + + +enum +{ + MODE_OUTPUT = 0, + MODE_INPUT, +}; + + +#define CONTROL_PORT_C_LOWER_INPUT 0x01 +#define CONTROL_PORT_B_INPUT 0x02 +#define CONTROL_GROUP_B_MODE_1 0x04 +#define CONTROL_PORT_C_UPPER_INPUT 0x08 +#define CONTROL_PORT_A_INPUT 0x10 +#define CONTROL_GROUP_A_MODE_MASK 0x60 +#define CONTROL_MODE_SET 0x80 + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type I8255 = &device_creator<i8255_device>; +const device_type I8255A = &device_creator<i8255_device>; + + + +//************************************************************************** +// INLINE HELPERS +//************************************************************************** + +//------------------------------------------------- +// check_interrupt - +//------------------------------------------------- + +inline void i8255_device::check_interrupt(int port) +{ + switch (group_mode(port)) + { + case MODE_1: + switch (port_mode(port)) + { + case MODE_INPUT: + set_intr(port, (m_inte[port] && m_ibf[port])); + break; + + case MODE_OUTPUT: + set_intr(port, (m_inte[port] && m_obf[port])); + break; + } + break; + + case MODE_2: + set_intr(port, ((m_inte1 && m_obf[port]) || (m_inte2 && m_ibf[port]))); + break; + } +} + + +//------------------------------------------------- +// set_ibf - +//------------------------------------------------- + +inline void i8255_device::set_ibf(int port, int state) +{ + if (LOG) logerror("I8255 '%s' Port %c IBF: %u\n", tag(), 'A' + port, state); + + m_ibf[port] = state; + + check_interrupt(port); +} + + +//------------------------------------------------- +// set_obf - +//------------------------------------------------- + +inline void i8255_device::set_obf(int port, int state) +{ + if (LOG) logerror("I8255 '%s' Port %c OBF: %u\n", tag(), 'A' + port, state); + + m_obf[port] = state; + + check_interrupt(port); +} + + +//------------------------------------------------- +// set_inte - +//------------------------------------------------- + +inline void i8255_device::set_inte(int port, int state) +{ + if (LOG) logerror("I8255 '%s' Port %c INTE: %u\n", tag(), 'A' + port, state); + + m_inte[port] = state; + + check_interrupt(port); +} + + +//------------------------------------------------- +// set_inte1 - +//------------------------------------------------- + +inline void i8255_device::set_inte1(int state) +{ + if (LOG) logerror("I8255 '%s' Port A INTE1: %u\n", tag(), state); + + m_inte1 = state; + + check_interrupt(PORT_A); +} + + +//------------------------------------------------- +// set_inte2 - +//------------------------------------------------- + +inline void i8255_device::set_inte2(int state) +{ + if (LOG) logerror("I8255 '%s' Port A INTE2: %u\n", tag(), state); + + m_inte2 = state; + + check_interrupt(PORT_A); +} + + +//------------------------------------------------- +// set_intr - +//------------------------------------------------- + +inline void i8255_device::set_intr(int port, int state) +{ + if (LOG) logerror("I8255 '%s' Port %c INTR: %u\n", tag(), 'A' + port, state); + + m_intr[port] = state; + + output_pc(); +} + + +//------------------------------------------------- +// group_mode - +//------------------------------------------------- + +inline int i8255_device::group_mode(int group) +{ + int mode = 0; + + switch (group) + { + case GROUP_A: + switch ((m_control & CONTROL_GROUP_A_MODE_MASK) >> 5) + { + case 0: mode = MODE_0; break; + case 1: mode = MODE_1; break; + case 2: case 3: mode = MODE_2; break; + } + break; + + case GROUP_B: + mode = m_control & CONTROL_GROUP_B_MODE_1 ? MODE_1 : MODE_0; + break; + } + + return mode; +} + + +//------------------------------------------------- +// port_mode - +//------------------------------------------------- + +inline int i8255_device::port_mode(int port) +{ + int mode = 0; + + switch (port) + { + case PORT_A: mode = m_control & CONTROL_PORT_A_INPUT ? MODE_INPUT : MODE_OUTPUT; break; + case PORT_B: mode = m_control & CONTROL_PORT_B_INPUT ? MODE_INPUT : MODE_OUTPUT; break; + } + + return mode; +} + + +//------------------------------------------------- +// port_c_lower_mode - +//------------------------------------------------- + +inline int i8255_device::port_c_lower_mode() +{ + return m_control & CONTROL_PORT_C_LOWER_INPUT ? MODE_INPUT : MODE_OUTPUT; +} + + +//------------------------------------------------- +// port_c_upper_mode - +//------------------------------------------------- + +inline int i8255_device::port_c_upper_mode() +{ + return m_control & CONTROL_PORT_C_UPPER_INPUT ? MODE_INPUT : MODE_OUTPUT; +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// i8255_device - constructor +//------------------------------------------------- + +i8255_device::i8255_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, I8255, "I8255", tag, owner, clock) +{ + m_intr[PORT_A] = m_intr[PORT_B] = 0; +} + + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void i8255_device::device_config_complete() +{ + // inherit a copy of the static data + const i8255_interface *intf = reinterpret_cast<const i8255_interface *>(static_config()); + if (intf != NULL) + *static_cast<i8255_interface *>(this) = *intf; + + // or initialize to defaults if none provided + else + { + memset(&m_in_pa_cb, 0, sizeof(m_in_pa_cb)); + memset(&m_out_pa_cb, 0, sizeof(m_out_pa_cb)); + memset(&m_in_pb_cb, 0, sizeof(m_in_pb_cb)); + memset(&m_out_pb_cb, 0, sizeof(m_out_pb_cb)); + memset(&m_in_pc_cb, 0, sizeof(m_in_pc_cb)); + memset(&m_out_pc_cb, 0, sizeof(m_out_pc_cb)); + } +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void i8255_device::device_start() +{ + // resolve callbacks + m_in_port_func[PORT_A].resolve(m_in_pa_cb, *this); + m_out_port_func[PORT_A].resolve(m_out_pa_cb, *this); + m_in_port_func[PORT_B].resolve(m_in_pb_cb, *this); + m_out_port_func[PORT_B].resolve(m_out_pb_cb, *this); + m_in_port_func[PORT_C].resolve(m_in_pc_cb, *this); + m_out_port_func[PORT_C].resolve(m_out_pc_cb, *this); + + // register for state saving + save_item(NAME(m_control)); + save_item(NAME(m_output)); + save_item(NAME(m_input)); + save_item(NAME(m_ibf)); + save_item(NAME(m_obf)); + save_item(NAME(m_inte)); + save_item(NAME(m_inte1)); + save_item(NAME(m_inte2)); + save_item(NAME(m_intr)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void i8255_device::device_reset() +{ + set_mode(0x9b); +} + + +//------------------------------------------------- +// read_mode0 - +//------------------------------------------------- + +UINT8 i8255_device::read_mode0(int port) +{ + UINT8 data = 0; + + if (port_mode(port) == MODE_OUTPUT) + { + // read data from output latch + data = m_output[port]; + } + else + { + // read data from port + data = m_in_port_func[port](0); + } + + return data; +} + + +//------------------------------------------------- +// read_mode1 - +//------------------------------------------------- + +UINT8 i8255_device::read_mode1(int port) +{ + UINT8 data = 0; + + if (port_mode(port) == MODE_OUTPUT) + { + // read data from output latch + data = m_output[port]; + } + else + { + // read data from input latch + data = m_input[port]; + + // clear input buffer full flag + set_ibf(port, 0); + + // clear interrupt + set_intr(port, 0); + + // clear input latch + m_input[port] = 0; + } + + return data; +} + + +//------------------------------------------------- +// read_mode2 - +//------------------------------------------------- + +UINT8 i8255_device::read_mode2() +{ + UINT8 data = 0; + + // read data from input latch + data = m_input[PORT_A]; + + // clear input buffer full flag + set_ibf(PORT_A, 0); + + // clear interrupt + set_intr(PORT_A, 0); + + // clear input latch + m_input[PORT_A] = 0; + + return data; +} + + +//------------------------------------------------- +// read_pc - +//------------------------------------------------- + +UINT8 i8255_device::read_pc() +{ + UINT8 data = 0; + UINT8 mask = 0; + + // PC upper + switch (group_mode(GROUP_A)) + { + case MODE_0: + if (port_c_upper_mode() == MODE_OUTPUT) + { + // read data from output latch + data |= m_output[PORT_C] & 0xf0; + } + else + { + // read data from port + mask |= 0xf0; + } + break; + + case MODE_1: + data |= m_intr[PORT_A] ? 0x08 : 0x00; + + if (port_mode(PORT_A) == MODE_OUTPUT) + { + data |= m_obf[PORT_A] ? 0x80 : 0x00; + data |= m_inte[PORT_A] ? 0x40 : 0x00; + mask |= 0x30; + } + else + { + data |= m_ibf[PORT_A] ? 0x20 : 0x00; + data |= m_inte[PORT_A] ? 0x10 : 0x00; + mask |= 0xc0; + } + break; + + case MODE_2: + data |= m_intr[PORT_A] ? 0x08 : 0x00; + data |= m_inte2 ? 0x10 : 0x00; + data |= m_ibf[PORT_A] ? 0x20 : 0x00; + data |= m_inte1 ? 0x40 : 0x00; + data |= m_obf[PORT_A] ? 0x80 : 0x00; + break; + } + + // PC lower + switch (group_mode(GROUP_B)) + { + case MODE_0: + if (port_c_lower_mode() == MODE_OUTPUT) + { + // read data from output latch + data |= m_output[PORT_C] & 0x0f; + } + else + { + // read data from port + mask |= 0x0f; + } + break; + + case MODE_1: + data |= m_inte[PORT_B] ? 0x04 : 0x00; + data |= m_intr[PORT_B] ? 0x01 : 0x00; + + if (port_mode(PORT_B) == MODE_OUTPUT) + { + data |= m_obf[PORT_B] ? 0x02 : 0x00; + } + else + { + data |= m_ibf[PORT_B] ? 0x02 : 0x00; + } + } + + if (mask) + { + // read data from port + data |= m_in_port_func[PORT_C](0) & mask; + } + + return data; +} + + +//------------------------------------------------- +// write_mode0 - +//------------------------------------------------- + +void i8255_device::write_mode0(int port, UINT8 data) +{ + if (port_mode(port) == MODE_OUTPUT) + { + // latch output data + m_output[port] = data; + + // write data to port + m_out_port_func[port](0, data); + } +} + + +//------------------------------------------------- +// write_mode1 - +//------------------------------------------------- + +void i8255_device::write_mode1(int port, UINT8 data) +{ + if (port_mode(port) == MODE_OUTPUT) + { + // latch output data + m_output[port] = data; + + // write data to port + m_out_port_func[port](0, data); + + // set output buffer full flag + set_obf(port, 0); + + // clear interrupt + set_intr(port, 0); + } +} + + +//------------------------------------------------- +// write_mode2 - +//------------------------------------------------- + +void i8255_device::write_mode2(UINT8 data) +{ + // latch output data + m_output[PORT_A] = data; + + // write data to port + m_out_port_func[PORT_A](0, data); + + // set output buffer full flag + set_obf(PORT_A, 0); + + // clear interrupt + set_intr(PORT_A, 0); +} + + +//------------------------------------------------- +// write_pc - +//------------------------------------------------- + +void i8255_device::write_pc(UINT8 data) +{ + int changed = 0; + + if (group_mode(GROUP_A) == MODE_0) + { + // PC upper + if (port_c_upper_mode() == MODE_OUTPUT) + { + m_output[PORT_C] = (data & 0xf0) | (m_output[PORT_C] & 0x0f); + changed = 1; + } + + // PC lower + if (port_c_lower_mode() == MODE_OUTPUT) + { + m_output[PORT_C] = (m_output[PORT_C] & 0xf0) | (data & 0x0f); + changed = 1; + } + } + + if (changed) + { + output_pc(); + } +} + + +//------------------------------------------------- +// output_pc - +//------------------------------------------------- + +void i8255_device::output_pc() +{ + UINT8 data = 0; + UINT8 mask = 0; + + // PC upper + switch (group_mode(GROUP_A)) + { + case MODE_0: + if (port_c_upper_mode() == MODE_OUTPUT) + { + mask |= 0xf0; + } + else + { + // TTL inputs float high + data |= 0xf0; + } + break; + + case MODE_1: + data |= m_intr[PORT_A] ? 0x08 : 0x00; + + if (port_mode(PORT_A) == MODE_OUTPUT) + { + data |= m_obf[PORT_A] ? 0x80 : 0x00; + mask |= 0x30; + } + else + { + data |= m_ibf[PORT_A] ? 0x20 : 0x00; + mask |= 0xc0; + } + break; + + case MODE_2: + data |= m_intr[PORT_A] ? 0x08 : 0x00; + data |= m_ibf[PORT_A] ? 0x20 : 0x00; + data |= m_obf[PORT_A] ? 0x80 : 0x00; + break; + } + + // PC lower + switch (group_mode(GROUP_B)) + { + case MODE_0: + if (port_c_lower_mode() == MODE_OUTPUT) + { + mask |= 0x0f; + } + else + { + // TTL inputs float high + data |= 0x0f; + } + break; + + case MODE_1: + data |= m_intr[PORT_B] ? 0x01 : 0x00; + + if (port_mode(PORT_B) == MODE_OUTPUT) + { + data |= m_obf[PORT_B] ? 0x02 : 0x00; + } + else + { + data |= m_ibf[PORT_B] ? 0x02 : 0x00; + } + } + + data |= m_output[PORT_C] & mask; + + m_out_port_func[PORT_C](0, data); +} + + +//------------------------------------------------- +// set_mode - +//------------------------------------------------- + +void i8255_device::set_mode(UINT8 data) +{ + m_control = data; + + // group A + m_output[PORT_A] = 0; + m_input[PORT_A] = 0; + m_ibf[PORT_A] = 0; + m_obf[PORT_A] = 1; + m_inte[PORT_A] = 0; + m_inte1 = 0; + m_inte2 = 0; + + if (port_mode(PORT_A) == MODE_OUTPUT) + { + m_out_port_func[PORT_A](0, m_output[PORT_A]); + } + else + { + // TTL inputs float high + m_out_port_func[PORT_A](0, 0xff); + } + + if (LOG) + { + logerror("I8255 '%s' Group A Mode: %u\n", tag(), group_mode(GROUP_A)); + logerror("I8255 '%s' Port A Mode: %s\n", tag(), (port_mode(PORT_A) == MODE_OUTPUT) ? "output" : "input"); + logerror("I8255 '%s' Port C Upper Mode: %s\n", tag(), (port_c_upper_mode() == MODE_OUTPUT) ? "output" : "input"); + logerror("I8255 '%s' Group B Mode: %u\n", tag(), group_mode(GROUP_B)); + logerror("I8255 '%s' Port B Mode: %s\n", tag(), (port_mode(PORT_B) == MODE_OUTPUT) ? "output" : "input"); + logerror("I8255 '%s' Port C Lower Mode: %s\n", tag(), (port_c_lower_mode() == MODE_OUTPUT) ? "output" : "input"); + } + + // group B + m_output[PORT_B] = 0; + m_input[PORT_B] = 0; + m_ibf[PORT_B] = 0; + m_obf[PORT_B] = 1; + m_inte[PORT_B] = 0; + + if (port_mode(PORT_B) == MODE_OUTPUT) + { + m_out_port_func[PORT_B](0, m_output[PORT_B]); + } + else + { + // TTL inputs float high + m_out_port_func[PORT_B](0, 0xff); + } + + m_output[PORT_C] = 0; + m_input[PORT_C] = 0; + + output_pc(); +} + + +//------------------------------------------------- +// set_pc_bit - +//------------------------------------------------- + +void i8255_device::set_pc_bit(int bit, int state) +{ + // set output latch bit + m_output[PORT_C] &= ~(1 << bit); + m_output[PORT_C] |= state << bit; + + switch (group_mode(GROUP_A)) + { + case MODE_1: + if (port_mode(PORT_A) == MODE_OUTPUT) + { + switch (bit) + { + case 3: set_intr(PORT_A, state); break; + case 6: set_inte(PORT_A, state); break; + case 7: set_obf(PORT_A, state); break; + } + } + else + { + switch (bit) + { + case 3: set_intr(PORT_A, state); break; + case 4: set_inte(PORT_A, state); break; + case 5: set_ibf(PORT_A, state); break; + } + } + break; + + case MODE_2: + switch (bit) + { + case 3: set_intr(PORT_A, state); break; + case 4: set_inte2(state); break; + case 5: set_ibf(PORT_A, state); break; + case 6: set_inte1(state); break; + case 7: set_obf(PORT_A, state); break; + } + break; + } + + if (group_mode(GROUP_B) == MODE_1) + { + switch (bit) + { + case 0: set_intr(PORT_B, state); break; + case 1: + if (port_mode(PORT_B) == MODE_OUTPUT) + set_obf(PORT_B, state); + else + set_ibf(PORT_B, state); + break; + case 2: set_inte(PORT_B, state); break; + } + } + + output_pc(); +} + + +//------------------------------------------------- +// read - +//------------------------------------------------- + +READ8_MEMBER( i8255_device::read ) +{ + UINT8 data = 0; + + switch (offset & 0x03) + { + case PORT_A: + switch (group_mode(GROUP_A)) + { + case MODE_0: data = read_mode0(PORT_A); break; + case MODE_1: data = read_mode1(PORT_A); break; + case MODE_2: data = read_mode2(); break; + } + if (LOG) logerror("I8255 '%s' Port A Read: %02x\n", tag(), data); + break; + + case PORT_B: + switch (group_mode(GROUP_B)) + { + case MODE_0: data = read_mode0(PORT_B); break; + case MODE_1: data = read_mode1(PORT_B); break; + } + if (LOG) logerror("I8255 '%s' Port B Read: %02x\n", tag(), data); + break; + + case PORT_C: + data = read_pc(); + if (LOG) logerror("I8255 '%s' Port C Read: %02x\n", tag(), data); + break; + + case CONTROL: + data = m_control; + if (LOG) logerror("I8255 '%s' Mode Control Word Read: %02x\n", tag(), data); + break; + } + + return data; +} + + +//------------------------------------------------- +// write - +//------------------------------------------------- + +WRITE8_MEMBER( i8255_device::write ) +{ + switch (offset & 0x03) + { + case PORT_A: + if (LOG) logerror("I8255 '%s' Port A Write: %02x\n", tag(), data); + + switch (group_mode(GROUP_A)) + { + case MODE_0: write_mode0(PORT_A, data); break; + case MODE_1: write_mode1(PORT_A, data); break; + case MODE_2: write_mode2(data); break; + } + break; + + case PORT_B: + if (LOG) logerror("I8255 '%s' Port B Write: %02x\n", tag(), data); + + switch (group_mode(GROUP_B)) + { + case MODE_0: write_mode0(PORT_B, data); break; + case MODE_1: write_mode1(PORT_B, data); break; + } + break; + + case PORT_C: + if (LOG) logerror("I8255 '%s' Port C Write: %02x\n", tag(), data); + + write_pc(data); + break; + + case CONTROL: + if (data & CONTROL_MODE_SET) + { + if (LOG) logerror("I8255 '%s' Mode Control Word: %02x\n", tag(), data); + + set_mode(data); + } + else + { + int bit = (data >> 1) & 0x07; + int state = BIT(data, 0); + + if (LOG) logerror("I8255 '%s' %s Port C Bit %u\n", tag(), state ? "Set" : "Reset", bit); + + set_pc_bit(bit, state); + } + break; + } +} + + +//------------------------------------------------- +// pa_r - +//------------------------------------------------- + +READ8_MEMBER( i8255_device::pa_r ) +{ + return pa_r(); +} + + +//------------------------------------------------- +// pb_r - port A read +//------------------------------------------------- + +UINT8 i8255_device::pa_r() +{ + UINT8 data = 0xff; + + if (port_mode(PORT_A) == MODE_OUTPUT) + { + data = m_output[PORT_A]; + } + + return data; +} + + +//------------------------------------------------- +// pb_r - +//------------------------------------------------- + +READ8_MEMBER( i8255_device::pb_r ) +{ + return pb_r(); +} + + +//------------------------------------------------- +// pb_r - port B read +//------------------------------------------------- + +UINT8 i8255_device::pb_r() +{ + UINT8 data = 0xff; + + if (port_mode(PORT_B) == MODE_OUTPUT) + { + data = m_output[PORT_B]; + } + + return data; +} + + +//------------------------------------------------- +// pc2_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( i8255_device::pc2_w ) +{ + if (group_mode(GROUP_B) == 1) + { + if (port_mode(PORT_B) == MODE_OUTPUT) + { + // port B acknowledge + if (!m_obf[PORT_B] && !state) + { + if (LOG) logerror("I8255 '%s' Port B Acknowledge\n", tag()); + + // clear output buffer flag + set_obf(PORT_B, 1); + } + } + else + { + // port B strobe + if (!m_ibf[PORT_B] && !state) + { + if (LOG) logerror("I8255 '%s' Port B Strobe\n", tag()); + + // read port into latch + m_input[PORT_B] = m_in_port_func[PORT_B](0); + + // set input buffer flag + set_ibf(PORT_B, 1); + } + } + } +} + + +//------------------------------------------------- +// pc4_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( i8255_device::pc4_w ) +{ + if ((group_mode(GROUP_A) == 2) || ((group_mode(GROUP_A) == 1) && (port_mode(PORT_A) == MODE_INPUT))) + { + // port A strobe + if (!m_ibf[PORT_A] && !state) + { + if (LOG) logerror("I8255 '%s' Port A Strobe\n", tag()); + + // read port into latch + m_input[PORT_A] = m_in_port_func[PORT_A](0); + + // set input buffer flag + set_ibf(PORT_A, 1); + } + } +} + + +//------------------------------------------------- +// pc6_w - +//------------------------------------------------- + +WRITE_LINE_MEMBER( i8255_device::pc6_w ) +{ + if ((group_mode(GROUP_A) == 2) || ((group_mode(GROUP_A) == 1) && (port_mode(PORT_A) == MODE_OUTPUT))) + { + // port A acknowledge + if (!m_obf[PORT_A] && !state) + { + if (LOG) logerror("I8255 '%s' Port A Acknowledge\n", tag()); + + // clear output buffer flag + set_obf(PORT_A, 1); + } + } +} |