// license:BSD-3-Clause // copyright-holders:hap, Charles MacDonald /********************************************************************** Sega 315-5296 I/O chip Sega 100-pin QFP, with 8 bidirectional I/O ports, and 3 output pins. It also has chip select(/FMCS) and clock(CKOT) for a peripheral device. Commonly used from the late 80s up until Sega Model 2. The I/O chip has 64 addresses: $00-0F : I/O ports, security, configuration registers $10-1F : Unused (no effect when read or written) $20-3F : Unused (enables /FMCS output, eg. to YM2151 /CS) On System 16 derivatives, the unused locations return the 68000 prefetch value off the bus when read. TODO: - complete emulation of CNT register **********************************************************************/ #include "emu.h" #include "machine/315_5296.h" DEFINE_DEVICE_TYPE(SEGA_315_5296, sega_315_5296_device, "315_5296", "Sega 315-5296 I/O") //------------------------------------------------- // sega_315_5296_device - constructor //------------------------------------------------- sega_315_5296_device::sega_315_5296_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, SEGA_315_5296, tag, owner, clock), m_in_port_cb(*this), m_out_port_cb(*this), m_out_cnt_cb(*this), m_dir_override(0xff) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void sega_315_5296_device::device_start() { // resolve callbacks m_in_port_cb.resolve_all_safe(0xff); m_out_port_cb.resolve_all_safe(); m_out_cnt_cb.resolve_all_safe(); // register for savestates save_item(NAME(m_output_latch)); save_item(NAME(m_cnt)); save_item(NAME(m_dir)); save_item(NAME(m_dir_override)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void sega_315_5296_device::device_reset() { // all ports are set to input m_dir = 0; // clear output ports memset(m_output_latch, 0, sizeof(m_output_latch)); m_cnt = 0; for (int i = 0; i < 8; i++) m_out_port_cb[i]((offs_t)i, 0); for (auto & elem : m_out_cnt_cb) elem(0); } //------------------------------------------------- uint8_t sega_315_5296_device::read(offs_t offset) { offset &= 0x3f; switch (offset) { // port A to H case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7: // if the port is configured as an output, return the last thing written if (m_dir & m_dir_override & 1 << offset) return m_output_latch[offset]; // otherwise, return an input port return m_in_port_cb[offset](offset); // 'SEGA' protection case 0x8: return 'S'; case 0x9: return 'E'; case 0xa: return 'G'; case 0xb: return 'A'; // CNT register & mirror case 0xc: case 0xe: return m_cnt; // port direction register & mirror case 0xd: case 0xf: return m_dir; default: break; } return 0xff; } void sega_315_5296_device::write(offs_t offset, uint8_t data) { offset &= 0x3f; switch (offset) { // port A to H case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7: // if the port is configured as an output, write it if (m_dir & 1 << offset) m_out_port_cb[offset](offset, data); m_output_latch[offset] = data; break; // CNT register case 0xe: // d0-2: CNT0-2 output pins // note: When CNT2 is configured as clock output, bit 2 of this register has // no effect on the output level of CNT2. for (int i = 0; i < 3; i++) m_out_cnt_cb[i](data >> i & 1); // d3: CNT2 output mode (1= Clock output, 0= Programmable output) // d4,5: CNT2 clock divider (0= CLK/4, 1= CLK/8, 2= CLK/16, 3= CLK/2) // d6,7: CKOT clock divider (0= CLK/4, 1= CLK/8, 2= CLK/16, 3= CLK/2) // TODO.. m_cnt = data; break; // port direction register case 0xf: // refresh output ports if the direction changed for (int i = 0; i < 8; i++) { if ((m_dir ^ data) & (1 << i)) { logerror("Port %c configured for output\n", 'A' + i); m_out_port_cb[i]((offs_t)i, (data & 1 << i) ? m_output_latch[i] : 0); } } m_dir = data; break; default: break; } }