summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/psx/sio.c
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2015-09-13 08:41:44 +0200
committer Miodrag Milanovic <mmicko@gmail.com>2015-09-13 08:41:44 +0200
commitf88cefad27a1737c76e09d99c9fb43e173506081 (patch)
tree2d8167d03579c46e226471747eb4407bd00ed6fa /src/devices/cpu/psx/sio.c
parente92ac9e0fa8e99869894bea00589bbb526be30aa (diff)
Move all devices into separate part of src tree (nw)
Diffstat (limited to 'src/devices/cpu/psx/sio.c')
-rw-r--r--src/devices/cpu/psx/sio.c360
1 files changed, 360 insertions, 0 deletions
diff --git a/src/devices/cpu/psx/sio.c b/src/devices/cpu/psx/sio.c
new file mode 100644
index 00000000000..abf3c4b8bf1
--- /dev/null
+++ b/src/devices/cpu/psx/sio.c
@@ -0,0 +1,360 @@
+// license:BSD-3-Clause
+// copyright-holders:smf
+/*
+ * PlayStation Serial I/O emulator
+ *
+ * Copyright 2003-2011 smf
+ *
+ */
+
+#include "sio.h"
+
+#define VERBOSE_LEVEL ( 0 )
+
+INLINE void ATTR_PRINTF(3,4) verboselog( running_machine& machine, int n_level, const char *s_fmt, ... )
+{
+ if( VERBOSE_LEVEL >= n_level )
+ {
+ va_list v;
+ char buf[ 32768 ];
+ va_start( v, s_fmt );
+ vsprintf( buf, s_fmt, v );
+ va_end( v );
+ logerror( "%s: %s", machine.describe_context(), buf );
+ }
+}
+
+const device_type PSX_SIO0 = &device_creator<psxsio0_device>;
+const device_type PSX_SIO1 = &device_creator<psxsio1_device>;
+
+psxsio0_device::psxsio0_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ psxsio_device(mconfig, PSX_SIO0, "Sony PSX SIO-0", tag, owner, clock, "psxsio0", __FILE__)
+{
+}
+
+psxsio1_device::psxsio1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ psxsio_device(mconfig, PSX_SIO1, "Sony PSX SIO-1", tag, owner, clock, "psxsio1", __FILE__)
+{
+}
+
+psxsio_device::psxsio_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) :
+ device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_status(SIO_STATUS_TX_EMPTY | SIO_STATUS_TX_RDY),
+ m_rxd(1),
+ m_irq_handler(*this),
+ m_sck_handler(*this),
+ m_txd_handler(*this),
+ m_dtr_handler(*this),
+ m_rts_handler(*this)
+{
+}
+
+void psxsio_device::device_post_load()
+{
+ sio_timer_adjust();
+}
+
+void psxsio_device::device_start()
+{
+ m_irq_handler.resolve_safe();
+ m_sck_handler.resolve_safe();
+ m_txd_handler.resolve_safe();
+ m_dtr_handler.resolve_safe();
+ m_rts_handler.resolve_safe();
+
+ m_timer = timer_alloc( 0 );
+ m_mode = 0;
+ m_control = 0;
+ m_baud = 0;
+ m_rx_data = 0;
+ m_tx_data = 0;
+ m_rx_shift = 0;
+ m_tx_shift = 0;
+ m_rx_bits = 0;
+ m_tx_bits = 0;
+
+ save_item( NAME( m_status ) );
+ save_item( NAME( m_mode ) );
+ save_item( NAME( m_control ) );
+ save_item( NAME( m_baud ) );
+ save_item( NAME( m_rxd ) );
+ save_item( NAME( m_rx_data ) );
+ save_item( NAME( m_tx_data ) );
+ save_item( NAME( m_rx_shift ) );
+ save_item( NAME( m_tx_shift ) );
+ save_item( NAME( m_rx_bits ) );
+ save_item( NAME( m_tx_bits ) );
+}
+
+void psxsio_device::sio_interrupt()
+{
+ verboselog( machine(), 1, "sio_interrupt( %s )\n", tag() );
+ m_status |= SIO_STATUS_IRQ;
+ m_irq_handler(1);
+}
+
+void psxsio_device::sio_timer_adjust()
+{
+ attotime n_time;
+
+ if( ( m_status & SIO_STATUS_TX_EMPTY ) == 0 || m_tx_bits != 0 )
+ {
+ int n_prescaler;
+
+ switch( m_mode & 3 )
+ {
+ case 1:
+ n_prescaler = 1;
+ break;
+ case 2:
+ n_prescaler = 16;
+ break;
+ case 3:
+ n_prescaler = 64;
+ break;
+ default:
+ n_prescaler = 0;
+ break;
+ }
+
+ if( m_baud != 0 && n_prescaler != 0 )
+ {
+ n_time = attotime::from_hz(33868800) * (n_prescaler * m_baud);
+ verboselog( machine(), 2, "sio_timer_adjust( %s ) = %s ( %d x %d )\n", tag(), n_time.as_string(), n_prescaler, m_baud );
+ }
+ else
+ {
+ n_time = attotime::never;
+ verboselog( machine(), 0, "sio_timer_adjust( %s ) invalid baud rate ( %d x %d )\n", tag(), n_prescaler, m_baud );
+ }
+ }
+ else
+ {
+ n_time = attotime::never;
+ verboselog( machine(), 2, "sio_timer_adjust( %s ) finished\n", tag() );
+ }
+
+ m_timer->adjust( n_time );
+}
+
+void psxsio_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
+{
+ verboselog( machine(), 2, "sio tick\n" );
+
+ if( m_tx_bits == 0 &&
+ ( m_control & SIO_CONTROL_TX_ENA ) != 0 &&
+ ( m_status & SIO_STATUS_TX_EMPTY ) == 0 )
+ {
+ m_tx_bits = 8;
+ m_tx_shift = m_tx_data;
+
+ if( type() == PSX_SIO0 )
+ {
+ m_rx_bits = 8;
+ m_rx_shift = 0;
+ }
+
+ m_status |= SIO_STATUS_TX_EMPTY;
+ m_status |= SIO_STATUS_TX_RDY;
+ }
+
+ if( m_tx_bits != 0 )
+ {
+ if( type() == PSX_SIO0 )
+ {
+ m_sck_handler(0);
+ }
+
+ m_txd_handler( m_tx_shift & 1 );
+ m_tx_shift >>= 1;
+ m_tx_bits--;
+
+ if( type() == PSX_SIO0 )
+ {
+ m_sck_handler(1);
+ }
+
+ if( m_tx_bits == 0 &&
+ ( m_control & SIO_CONTROL_TX_IENA ) != 0 )
+ {
+ sio_interrupt();
+ }
+ }
+
+ if( m_rx_bits != 0 )
+ {
+ m_rx_shift = ( m_rx_shift >> 1 ) | ( m_rxd << 7 );
+ m_rx_bits--;
+
+ if( m_rx_bits == 0 )
+ {
+ if( ( m_status & SIO_STATUS_RX_RDY ) != 0 )
+ {
+ m_status |= SIO_STATUS_OVERRUN;
+ }
+ else
+ {
+ m_rx_data = m_rx_shift;
+ m_status |= SIO_STATUS_RX_RDY;
+ }
+
+ if( ( m_control & SIO_CONTROL_RX_IENA ) != 0 )
+ {
+ sio_interrupt();
+ }
+ }
+ }
+
+ sio_timer_adjust();
+}
+
+WRITE32_MEMBER( psxsio_device::write )
+{
+ switch( offset % 4 )
+ {
+ case 0:
+ verboselog( machine(), 1, "psx_sio_w %s data %02x (%08x)\n", tag(), data, mem_mask );
+ m_tx_data = data;
+ m_status &= ~( SIO_STATUS_TX_RDY );
+ m_status &= ~( SIO_STATUS_TX_EMPTY );
+ sio_timer_adjust();
+ break;
+ case 1:
+ verboselog( machine(), 0, "psx_sio_w( %08x, %08x, %08x )\n", offset, data, mem_mask );
+ break;
+ case 2:
+ if( ACCESSING_BITS_0_15 )
+ {
+ m_mode = data & 0xffff;
+ verboselog( machine(), 1, "psx_sio_w %s mode %04x\n", tag(), data & 0xffff );
+ }
+ if( ACCESSING_BITS_16_31 )
+ {
+ verboselog( machine(), 1, "psx_sio_w %s control %04x\n", tag(), data >> 16 );
+ m_control = data >> 16;
+
+ if( ( m_control & SIO_CONTROL_RESET ) != 0 )
+ {
+ verboselog( machine(), 1, "psx_sio_w reset\n" );
+ m_status |= SIO_STATUS_TX_EMPTY | SIO_STATUS_TX_RDY;
+ m_status &= ~( SIO_STATUS_RX_RDY | SIO_STATUS_OVERRUN | SIO_STATUS_IRQ );
+ m_irq_handler(0);
+
+ // toggle DTR to reset controllers, Star Ocean 2, at least, requires it
+ // the precise mechanism of the reset is unknown
+ // maybe it's related to the bottom 2 bits of control which are usually set
+ m_dtr_handler(0);
+ m_dtr_handler(1);
+
+ m_tx_bits = 0;
+ m_rx_bits = 0;
+ m_txd_handler(1);
+ }
+ if( ( m_control & SIO_CONTROL_IACK ) != 0 )
+ {
+ verboselog( machine(), 1, "psx_sio_w iack\n" );
+ m_status &= ~( SIO_STATUS_IRQ );
+ m_control &= ~( SIO_CONTROL_IACK );
+ m_irq_handler(0);
+ }
+ if( ( m_control & SIO_CONTROL_DTR ) != 0 )
+ {
+ m_dtr_handler(0);
+ }
+ else
+ {
+ m_dtr_handler(1);
+ }
+ }
+ break;
+ case 3:
+ if( ACCESSING_BITS_0_15 )
+ {
+ verboselog( machine(), 0, "psx_sio_w( %08x, %08x, %08x )\n", offset, data, mem_mask );
+ }
+ if( ACCESSING_BITS_16_31 )
+ {
+ m_baud = data >> 16;
+ verboselog( machine(), 1, "psx_sio_w %s baud %04x\n", tag(), data >> 16 );
+ }
+ break;
+ default:
+ verboselog( machine(), 0, "psx_sio_w( %08x, %08x, %08x )\n", offset, data, mem_mask );
+ break;
+ }
+}
+
+READ32_MEMBER( psxsio_device::read )
+{
+ UINT32 data;
+
+ switch( offset % 4 )
+ {
+ case 0:
+ data = m_rx_data;
+ m_status &= ~( SIO_STATUS_RX_RDY );
+ m_rx_data = 0xff;
+ verboselog( machine(), 1, "psx_sio_r %s data %02x (%08x)\n", tag(), data, mem_mask );
+ break;
+ case 1:
+ data = m_status;
+ if( ACCESSING_BITS_0_15 )
+ {
+ verboselog( machine(), 1, "psx_sio_r %s status %04x\n", tag(), data & 0xffff );
+ }
+ if( ACCESSING_BITS_16_31 )
+ {
+ verboselog( machine(), 0, "psx_sio_r( %08x, %08x ) %08x\n", offset, mem_mask, data );
+ }
+ break;
+ case 2:
+ data = ( m_control << 16 ) | m_mode;
+ if( ACCESSING_BITS_0_15 )
+ {
+ verboselog( machine(), 1, "psx_sio_r %s mode %04x\n", tag(), data & 0xffff );
+ }
+ if( ACCESSING_BITS_16_31 )
+ {
+ verboselog( machine(), 1, "psx_sio_r %s control %04x\n", tag(), data >> 16 );
+ }
+ break;
+ case 3:
+ data = m_baud << 16;
+ if( ACCESSING_BITS_0_15 )
+ {
+ verboselog( machine(), 0, "psx_sio_r( %08x, %08x ) %08x\n", offset, mem_mask, data );
+ }
+ if( ACCESSING_BITS_16_31 )
+ {
+ verboselog( machine(), 1, "psx_sio_r %s baud %04x\n", tag(), data >> 16 );
+ }
+ break;
+ default:
+ data = 0;
+ verboselog( machine(), 0, "psx_sio_r( %08x, %08x ) %08x\n", offset, mem_mask, data );
+ break;
+ }
+ return data;
+}
+
+WRITE_LINE_MEMBER(psxsio_device::write_rxd)
+{
+ m_rxd = state;
+}
+
+WRITE_LINE_MEMBER(psxsio_device::write_dsr)
+{
+ if (state)
+ {
+ m_status &= ~SIO_STATUS_DSR;
+ }
+ else if ((m_status & SIO_STATUS_DSR) == 0)
+ {
+ m_status |= SIO_STATUS_DSR;
+
+ if( ( m_control & SIO_CONTROL_DSR_IENA ) != 0 )
+ {
+ sio_interrupt();
+ }
+ }
+}