mirror of
https://github.com/holub/mame
synced 2025-06-05 20:33:45 +03:00
996 lines
19 KiB
C++
996 lines
19 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Curt Coder
|
|
/**********************************************************************
|
|
|
|
Intel 8255(A) Programmable Peripheral Interface emulation
|
|
|
|
**********************************************************************/
|
|
|
|
#include "i8255.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, "8255 PPI", tag, owner, clock, "i8255", __FILE__),
|
|
m_in_pa_cb(*this),
|
|
m_in_pb_cb(*this),
|
|
m_in_pc_cb(*this),
|
|
m_out_pa_cb(*this),
|
|
m_out_pb_cb(*this),
|
|
m_out_pc_cb(*this)
|
|
{
|
|
m_intr[PORT_A] = m_intr[PORT_B] = 0;
|
|
m_control = 0;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// device_start - device-specific startup
|
|
//-------------------------------------------------
|
|
|
|
void i8255_device::device_start()
|
|
{
|
|
// resolve callbacks
|
|
m_in_pa_cb.resolve_safe(0);
|
|
m_in_pb_cb.resolve_safe(0);
|
|
m_in_pc_cb.resolve_safe(0);
|
|
m_out_pa_cb.resolve_safe();
|
|
m_out_pb_cb.resolve_safe();
|
|
m_out_pc_cb.resolve_safe();
|
|
|
|
// 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;
|
|
|
|
if (port_mode(port) == MODE_OUTPUT)
|
|
{
|
|
// read data from output latch
|
|
data = m_output[port];
|
|
}
|
|
else
|
|
{
|
|
// read data from port
|
|
data = (port == PORT_A) ? m_in_pa_cb(0) : ((port == PORT_B) ? m_in_pb_cb(0) : m_in_pc_cb(0));
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// read_mode1 -
|
|
//-------------------------------------------------
|
|
|
|
UINT8 i8255_device::read_mode1(int port)
|
|
{
|
|
UINT8 data;
|
|
|
|
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;
|
|
|
|
// 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;
|
|
UINT8 b_mask = 0x0f;
|
|
|
|
// 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:
|
|
b_mask = 0x07;
|
|
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] & b_mask;
|
|
}
|
|
else
|
|
{
|
|
// read data from port
|
|
mask |= b_mask;
|
|
}
|
|
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_pc_cb(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
|
|
if (port == PORT_A)
|
|
m_out_pa_cb((offs_t)0, m_output[port]);
|
|
else if (port == PORT_B)
|
|
m_out_pb_cb((offs_t)0, m_output[port]);
|
|
else
|
|
m_out_pc_cb((offs_t)0, m_output[port]);
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// 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
|
|
if (port == PORT_A)
|
|
m_out_pa_cb((offs_t)0, m_output[port]);
|
|
else if (port == PORT_B)
|
|
m_out_pb_cb((offs_t)0, m_output[port]);
|
|
else
|
|
m_out_pc_cb((offs_t)0, m_output[port]);
|
|
|
|
// 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_pa_cb((offs_t)0, data);
|
|
|
|
// set output buffer full flag
|
|
set_obf(PORT_A, 0);
|
|
|
|
// clear interrupt
|
|
set_intr(PORT_A, 0);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// output_pc -
|
|
//-------------------------------------------------
|
|
|
|
void i8255_device::output_pc()
|
|
{
|
|
UINT8 data = 0;
|
|
UINT8 mask = 0;
|
|
UINT8 b_mask = 0x0f;
|
|
|
|
// 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:
|
|
b_mask = 0x07;
|
|
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 |= b_mask;
|
|
}
|
|
else
|
|
{
|
|
// TTL inputs float high
|
|
data |= b_mask;
|
|
}
|
|
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_pc_cb((offs_t)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_pa_cb((offs_t)0, m_output[PORT_A]);
|
|
}
|
|
else
|
|
{
|
|
// TTL inputs float high
|
|
m_out_pa_cb((offs_t)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_pb_cb((offs_t)0, m_output[PORT_B]);
|
|
}
|
|
else
|
|
{
|
|
// TTL inputs float high
|
|
m_out_pb_cb((offs_t)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;
|
|
default: 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;
|
|
default: 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;
|
|
default: 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;
|
|
default: 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);
|
|
|
|
m_output[PORT_C] = data;
|
|
output_pc();
|
|
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_pb_cb(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_pa_cb(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);
|
|
}
|
|
}
|
|
}
|