summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/cpu/psx/sio.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/cpu/psx/sio.c')
-rw-r--r--trunk/src/emu/cpu/psx/sio.c383
1 files changed, 383 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/psx/sio.c b/trunk/src/emu/cpu/psx/sio.c
new file mode 100644
index 00000000000..170934cd8d7
--- /dev/null
+++ b/trunk/src/emu/cpu/psx/sio.c
@@ -0,0 +1,383 @@
+/*
+ * PlayStation Serial I/O emulator
+ *
+ * Copyright 2003-2011 smf
+ *
+ */
+
+#include "sio.h"
+#include "includes/psx.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_SIO = &device_creator<psxsio_device>;
+
+psxsio_device::psxsio_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, PSX_SIO, "PSX SIO", tag, owner, clock)
+{
+ int n;
+
+ for( n = 0; n < 2; n++ )
+ {
+ port[ n ].fn_handler = NULL;
+ }
+}
+
+void psxsio_device::device_reset()
+{
+}
+
+void psxsio_device::device_post_load()
+{
+ int n;
+
+ for( n = 0; n < 2; n++ )
+ {
+ sio_timer_adjust( n );
+ }
+}
+
+void psxsio_device::device_start()
+{
+ int n;
+
+ for( n = 0; n < 2; n++ )
+ {
+ port[ n ].timer = machine().scheduler().timer_alloc( timer_expired_delegate( FUNC( psxsio_device::sio_clock ), this ) );
+ port[ n ].n_status = SIO_STATUS_TX_EMPTY | SIO_STATUS_TX_RDY;
+ port[ n ].n_mode = 0;
+ port[ n ].n_control = 0;
+ port[ n ].n_baud = 0;
+ port[ n ].n_tx = 0;
+ port[ n ].n_rx = 0;
+ port[ n ].n_tx_prev = 0;
+ port[ n ].n_rx_prev = 0;
+ port[ n ].n_rx_data = 0;
+ port[ n ].n_tx_data = 0;
+ port[ n ].n_rx_shift = 0;
+ port[ n ].n_tx_shift = 0;
+ port[ n ].n_rx_bits = 0;
+ port[ n ].n_tx_bits = 0;
+ }
+
+ for( n = 0; n < 2; n++ )
+ {
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_status );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_mode );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_control );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_baud );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_tx );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_rx );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_tx_prev );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_rx_prev );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_rx_data );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_tx_data );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_rx_shift );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_tx_shift );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_rx_bits );
+ state_save_register_item( machine(), "psxsio", NULL, n, port[n].n_tx_bits );
+ }
+}
+
+void psxsio_device::install_handler( int n_port, psx_sio_handler p_f_sio_handler )
+{
+ port[ n_port ].fn_handler = p_f_sio_handler;
+}
+
+void psxsio_device::sio_interrupt( int n_port )
+{
+ psx_sio *sio = &port[ n_port ];
+
+ verboselog( machine(), 1, "sio_interrupt( %d )\n", n_port );
+ sio->n_status |= SIO_STATUS_IRQ;
+ if( n_port == 0 )
+ {
+ psx_irq_set( machine(), PSX_IRQ_SIO0 );
+ }
+ else
+ {
+ psx_irq_set( machine(), PSX_IRQ_SIO1 );
+ }
+}
+
+void psxsio_device::sio_timer_adjust( int n_port )
+{
+ psx_sio *sio = &port[ n_port ];
+ attotime n_time;
+
+ if( ( sio->n_status & SIO_STATUS_TX_EMPTY ) == 0 || sio->n_tx_bits != 0 )
+ {
+ int n_prescaler;
+
+ switch( sio->n_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( sio->n_baud != 0 && n_prescaler != 0 )
+ {
+ n_time = attotime::from_hz(33868800) * (n_prescaler * sio->n_baud);
+ verboselog( machine(), 2, "sio_timer_adjust( %d ) = %s ( %d x %d )\n", n_port, n_time.as_string(), n_prescaler, sio->n_baud );
+ }
+ else
+ {
+ n_time = attotime::never;
+ verboselog( machine(), 0, "sio_timer_adjust( %d ) invalid baud rate ( %d x %d )\n", n_port, n_prescaler, sio->n_baud );
+ }
+ }
+ else
+ {
+ n_time = attotime::never;
+ verboselog( machine(), 2, "sio_timer_adjust( %d ) finished\n", n_port );
+ }
+ sio->timer->adjust( n_time, n_port);
+}
+
+void psxsio_device::sio_clock(void *ptr, int param)
+{
+ int n_port = param;
+ psx_sio *sio = &port[ n_port ];
+ verboselog( machine(), 2, "sio tick\n" );
+
+ if( sio->n_tx_bits == 0 &&
+ ( sio->n_control & SIO_CONTROL_TX_ENA ) != 0 &&
+ ( sio->n_status & SIO_STATUS_TX_EMPTY ) == 0 )
+ {
+ sio->n_tx_bits = 8;
+ sio->n_tx_shift = sio->n_tx_data;
+ if( n_port == 0 )
+ {
+ sio->n_rx_bits = 8;
+ sio->n_rx_shift = 0;
+ }
+ sio->n_status |= SIO_STATUS_TX_EMPTY;
+ sio->n_status |= SIO_STATUS_TX_RDY;
+ }
+
+ if( sio->n_tx_bits != 0 )
+ {
+ sio->n_tx = ( sio->n_tx & ~PSX_SIO_OUT_DATA ) | ( ( sio->n_tx_shift & 1 ) * PSX_SIO_OUT_DATA );
+ sio->n_tx_shift >>= 1;
+ sio->n_tx_bits--;
+
+ if( sio->fn_handler != NULL )
+ {
+ if( n_port == 0 )
+ {
+ sio->n_tx &= ~PSX_SIO_OUT_CLOCK;
+ (*sio->fn_handler)( machine(), sio->n_tx );
+ sio->n_tx |= PSX_SIO_OUT_CLOCK;
+ }
+ (*sio->fn_handler)( machine(), sio->n_tx );
+ }
+
+ if( sio->n_tx_bits == 0 &&
+ ( sio->n_control & SIO_CONTROL_TX_IENA ) != 0 )
+ {
+ sio_interrupt( n_port );
+ }
+ }
+
+ if( sio->n_rx_bits != 0 )
+ {
+ sio->n_rx_shift = ( sio->n_rx_shift >> 1 ) | ( ( ( sio->n_rx & PSX_SIO_IN_DATA ) / PSX_SIO_IN_DATA ) << 7 );
+ sio->n_rx_bits--;
+
+ if( sio->n_rx_bits == 0 )
+ {
+ if( ( sio->n_status & SIO_STATUS_RX_RDY ) != 0 )
+ {
+ sio->n_status |= SIO_STATUS_OVERRUN;
+ }
+ else
+ {
+ sio->n_rx_data = sio->n_rx_shift;
+ sio->n_status |= SIO_STATUS_RX_RDY;
+ }
+ if( ( sio->n_control & SIO_CONTROL_RX_IENA ) != 0 )
+ {
+ sio_interrupt( n_port );
+ }
+ }
+ }
+
+ sio_timer_adjust( n_port );
+}
+
+WRITE32_MEMBER( psxsio_device::write )
+{
+ int n_port = offset / 4;
+ psx_sio *sio = &port[ n_port ];
+
+ switch( offset % 4 )
+ {
+ case 0:
+ verboselog( machine(), 1, "psx_sio_w %d data %02x (%08x)\n", n_port, data, mem_mask );
+ sio->n_tx_data = data;
+ sio->n_status &= ~( SIO_STATUS_TX_RDY );
+ sio->n_status &= ~( SIO_STATUS_TX_EMPTY );
+ sio_timer_adjust( n_port );
+ 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 )
+ {
+ sio->n_mode = data & 0xffff;
+ verboselog( machine(), 1, "psx_sio_w %d mode %04x\n", n_port, data & 0xffff );
+ }
+ if( ACCESSING_BITS_16_31 )
+ {
+ verboselog( machine(), 1, "psx_sio_w %d control %04x\n", n_port, data >> 16 );
+ sio->n_control = data >> 16;
+
+ if( ( sio->n_control & SIO_CONTROL_RESET ) != 0 )
+ {
+ verboselog( machine(), 1, "psx_sio_w reset\n" );
+ sio->n_status |= SIO_STATUS_TX_EMPTY | SIO_STATUS_TX_RDY;
+ sio->n_status &= ~( SIO_STATUS_RX_RDY | SIO_STATUS_OVERRUN | SIO_STATUS_IRQ );
+ }
+ if( ( sio->n_control & SIO_CONTROL_IACK ) != 0 )
+ {
+ verboselog( machine(), 1, "psx_sio_w iack\n" );
+ sio->n_status &= ~( SIO_STATUS_IRQ );
+ sio->n_control &= ~( SIO_CONTROL_IACK );
+ }
+ if( ( sio->n_control & SIO_CONTROL_DTR ) != 0 )
+ {
+ sio->n_tx |= PSX_SIO_OUT_DTR;
+ }
+ else
+ {
+ sio->n_tx &= ~PSX_SIO_OUT_DTR;
+ }
+
+ if( ( ( sio->n_tx ^ sio->n_tx_prev ) & PSX_SIO_OUT_DTR ) != 0 )
+ {
+ if( sio->fn_handler != NULL )
+ {
+ (*sio->fn_handler)( machine(), sio->n_tx );
+ }
+ }
+ sio->n_tx_prev = sio->n_tx;
+
+ }
+ 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 )
+ {
+ sio->n_baud = data >> 16;
+ verboselog( machine(), 1, "psx_sio_w %d baud %04x\n", n_port, data >> 16 );
+ }
+ break;
+ default:
+ verboselog( machine(), 0, "psx_sio_w( %08x, %08x, %08x )\n", offset, data, mem_mask );
+ break;
+ }
+}
+
+READ32_MEMBER( psxsio_device::read )
+{
+ int n_port = offset / 4;
+ psx_sio *sio = &port[ n_port ];
+ UINT32 data;
+
+ switch( offset % 4 )
+ {
+ case 0:
+ data = sio->n_rx_data;
+ sio->n_status &= ~( SIO_STATUS_RX_RDY );
+ sio->n_rx_data = 0xff;
+ verboselog( machine(), 1, "psx_sio_r %d data %02x (%08x)\n", n_port, data, mem_mask );
+ break;
+ case 1:
+ data = sio->n_status;
+ if( ACCESSING_BITS_0_15 )
+ {
+ verboselog( machine(), 1, "psx_sio_r %d status %04x\n", n_port, 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 = ( sio->n_control << 16 ) | sio->n_mode;
+ if( ACCESSING_BITS_0_15 )
+ {
+ verboselog( machine(), 1, "psx_sio_r %d mode %04x\n", n_port, data & 0xffff );
+ }
+ if( ACCESSING_BITS_16_31 )
+ {
+ verboselog( machine(), 1, "psx_sio_r %d control %04x\n", n_port, data >> 16 );
+ }
+ break;
+ case 3:
+ data = sio->n_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 %d baud %04x\n", n_port, data >> 16 );
+ }
+ break;
+ default:
+ data = 0;
+ verboselog( machine(), 0, "psx_sio_r( %08x, %08x ) %08x\n", offset, mem_mask, data );
+ break;
+ }
+ return data;
+}
+
+void psxsio_device::input( int n_port, int n_mask, int n_data )
+{
+ psx_sio *sio = &port[ n_port ];
+ verboselog( machine(), 1, "psx_sio_input( %d, %02x, %02x )\n", n_port, n_mask, n_data );
+ sio->n_rx = ( sio->n_rx & ~n_mask ) | ( n_data & n_mask );
+
+ if( ( sio->n_rx & PSX_SIO_IN_DSR ) != 0 )
+ {
+ sio->n_status |= SIO_STATUS_DSR;
+ if( ( sio->n_rx_prev & PSX_SIO_IN_DSR ) == 0 &&
+ ( sio->n_control & SIO_CONTROL_DSR_IENA ) != 0 )
+ {
+ sio_interrupt( n_port );
+ }
+ }
+ else
+ {
+ sio->n_status &= ~SIO_STATUS_DSR;
+ }
+ sio->n_rx_prev = sio->n_rx;
+}