summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/psx.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/machine/psx.c')
-rw-r--r--src/mame/machine/psx.c1650
1 files changed, 1650 insertions, 0 deletions
diff --git a/src/mame/machine/psx.c b/src/mame/machine/psx.c
new file mode 100644
index 00000000000..3bb6d93f993
--- /dev/null
+++ b/src/mame/machine/psx.c
@@ -0,0 +1,1650 @@
+/***************************************************************************
+
+ machine/psx.c
+
+ Thanks to Olivier Galibert for IDCT information
+
+***************************************************************************/
+
+#include <stdarg.h>
+#include "driver.h"
+#include "cpu/mips/psx.h"
+#include "includes/psx.h"
+
+#define VERBOSE_LEVEL ( 0 )
+
+INLINE void verboselog( 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 );
+ if( cpu_getactivecpu() != -1 )
+ {
+ logerror( "%08x: %s", activecpu_get_pc(), buf );
+ }
+ else
+ {
+ logerror( "(timer) : %s", buf );
+ }
+ }
+}
+
+UINT32 *g_p_n_psxram;
+size_t g_n_psxramsize;
+
+INLINE void psxwriteword( UINT32 n_address, UINT16 n_data )
+{
+ *( (UINT16 *)( (UINT8 *)g_p_n_psxram + WORD_XOR_LE( n_address ) ) ) = n_data;
+}
+
+INLINE UINT16 psxreadword( UINT32 n_address )
+{
+ return *( (UINT16 *)( (UINT8 *)g_p_n_psxram + WORD_XOR_LE( n_address ) ) );
+}
+
+static UINT32 psx_com_delay = 0;
+
+WRITE32_HANDLER( psx_com_delay_w )
+{
+ COMBINE_DATA( &psx_com_delay );
+ verboselog( 1, "psx_com_delay_w( %08x %08x )\n", data, mem_mask );
+}
+
+READ32_HANDLER( psx_com_delay_r )
+{
+ verboselog( 1, "psx_com_delay_r( %08x )\n", mem_mask );
+ return psx_com_delay;
+}
+
+/* IRQ */
+
+static UINT32 m_n_irqdata;
+static UINT32 m_n_irqmask;
+
+static void psx_irq_update( void )
+{
+ if( ( m_n_irqdata & m_n_irqmask ) != 0 )
+ {
+ verboselog( 2, "psx irq assert\n" );
+ cpunum_set_input_line( 0, MIPS_IRQ0, ASSERT_LINE );
+ }
+ else
+ {
+ verboselog( 2, "psx irq clear\n" );
+ cpunum_set_input_line( 0, MIPS_IRQ0, CLEAR_LINE );
+ }
+}
+
+WRITE32_HANDLER( psx_irq_w )
+{
+ switch( offset )
+ {
+ case 0x00:
+ verboselog( 2, "psx irq data ( %08x, %08x ) %08x -> %08x\n", data, mem_mask, m_n_irqdata, ( m_n_irqdata & mem_mask ) | ( m_n_irqdata & m_n_irqmask & data ) );
+ m_n_irqdata = ( m_n_irqdata & mem_mask ) | ( m_n_irqdata & m_n_irqmask & data );
+ psx_irq_update();
+ break;
+ case 0x01:
+ verboselog( 2, "psx irq mask ( %08x, %08x ) %08x -> %08x\n", data, mem_mask, m_n_irqmask, ( m_n_irqmask & mem_mask ) | data );
+ m_n_irqmask = ( m_n_irqmask & mem_mask ) | data;
+ if( ( m_n_irqmask & ~( 0x1 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80 | 0x100 | 0x200 | 0x400 ) ) != 0 )
+ {
+ verboselog( 0, "psx_irq_w( %08x, %08x, %08x ) unknown irq\n", offset, data, mem_mask );
+ }
+ psx_irq_update();
+ break;
+ default:
+ verboselog( 0, "psx_irq_w( %08x, %08x, %08x ) unknown register\n", offset, data, mem_mask );
+ break;
+ }
+}
+
+READ32_HANDLER( psx_irq_r )
+{
+ switch( offset )
+ {
+ case 0x00:
+ verboselog( 1, "psx_irq_r irq data %08x\n", m_n_irqdata );
+ return m_n_irqdata;
+ case 0x01:
+ verboselog( 1, "psx_irq_r irq mask %08x\n", m_n_irqmask );
+ return m_n_irqmask;
+ default:
+ verboselog( 0, "psx_irq_r unknown register %d\n", offset );
+ break;
+ }
+ return 0;
+}
+
+void psx_irq_set( UINT32 data )
+{
+ verboselog( 2, "psx_irq_set %08x\n", data );
+ m_n_irqdata |= data;
+ psx_irq_update();
+}
+
+/* DMA */
+
+static UINT32 m_p_n_dmabase[ 7 ];
+static UINT32 m_p_n_dmablockcontrol[ 7 ];
+static UINT32 m_p_n_dmachannelcontrol[ 7 ];
+static emu_timer *m_p_timer_dma[ 7 ];
+static psx_dma_read_handler m_p_fn_dma_read[ 7 ];
+static psx_dma_write_handler m_p_fn_dma_write[ 7 ];
+static UINT32 m_p_n_dma_ticks[ 7 ];
+static UINT32 m_p_b_dma_running[ 7 ];
+static UINT32 m_n_dpcp;
+static UINT32 m_n_dicr;
+
+static void dma_start_timer( int n_channel, UINT32 n_ticks )
+{
+ timer_adjust( m_p_timer_dma[ n_channel ], attotime_mul(ATTOTIME_IN_HZ(33868800), n_ticks), n_channel, attotime_zero);
+ m_p_n_dma_ticks[ n_channel ] = n_ticks;
+ m_p_b_dma_running[ n_channel ] = 1;
+}
+
+static void dma_stop_timer( int n_channel )
+{
+ timer_adjust( m_p_timer_dma[ n_channel ], attotime_never, 0, attotime_never);
+ m_p_b_dma_running[ n_channel ] = 0;
+}
+
+static void dma_timer_adjust( int n_channel )
+{
+ if( m_p_b_dma_running[ n_channel ] )
+ {
+ dma_start_timer( n_channel, m_p_n_dma_ticks[ n_channel ] );
+ }
+ else
+ {
+ dma_stop_timer( n_channel );
+ }
+}
+
+static void dma_interrupt_update( void )
+{
+ int n_int;
+ int n_mask;
+
+ n_int = ( m_n_dicr >> 24 ) & 0x7f;
+ n_mask = ( m_n_dicr >> 16 ) & 0xff;
+
+ if( ( n_mask & 0x80 ) != 0 && ( n_int & n_mask ) != 0 )
+ {
+ verboselog( 2, "dma_interrupt_update( %02x, %02x ) interrupt triggered\n", n_int, n_mask );
+ m_n_dicr |= 0x80000000;
+ psx_irq_set( 0x0008 );
+ }
+ else if( ( m_n_dicr & 0x80000000 ) != 0 )
+ {
+ verboselog( 2, "dma_interrupt_update( %02x, %02x ) interrupt cleared\n", n_int, n_mask );
+ m_n_dicr &= ~0x80000000;
+ }
+ else if( n_int != 0 )
+ {
+ verboselog( 2, "dma_interrupt_update( %02x, %02x ) interrupt not enabled\n", n_int, n_mask );
+ }
+
+ m_n_dicr &= 0x00ffffff | ( m_n_dicr << 8 );
+}
+
+static void dma_finished(int n_channel)
+{
+ if( m_p_n_dmachannelcontrol[ n_channel ] == 0x01000401 && n_channel == 2 )
+ {
+ UINT32 n_size;
+ UINT32 n_total;
+ UINT32 n_address = ( m_p_n_dmabase[ n_channel ] & 0xffffff );
+ UINT32 n_adrmask = g_n_psxramsize - 1;
+ UINT32 n_nextaddress;
+
+ if( n_address != 0xffffff )
+ {
+ n_total = 0;
+ for( ;; )
+ {
+ if( n_address == 0xffffff )
+ {
+ m_p_n_dmabase[ n_channel ] = n_address;
+ dma_start_timer( n_channel, 19000 );
+ return;
+ }
+ if( n_total > 65535 )
+ {
+ m_p_n_dmabase[ n_channel ] = n_address;
+ //FIXME:
+ // 16000 below is based on try and error.
+ // Mametesters.org: sfex20103red
+ //dma_start_timer( n_channel, 16 );
+ dma_start_timer( n_channel, 16000 );
+ return;
+ }
+ n_address &= n_adrmask;
+ n_nextaddress = g_p_n_psxram[ n_address / 4 ];
+ n_size = n_nextaddress >> 24;
+ m_p_fn_dma_write[ n_channel ]( n_address + 4, n_size );
+ //FIXME:
+ // The following conditions will cause an endless loop.
+ // If stopping the transfer is correct I cannot judge
+ // The patch is meant as a hint for somebody who knows
+ // the hardware.
+ // Mametesters.org: psyforce0105u5red, raystorm0111u1red
+ if ((n_nextaddress & 0xffffff) != 0xffffff)
+ if (n_address == g_p_n_psxram[ (n_nextaddress & 0xffffff) / 4])
+ break;
+ if (n_address == (n_nextaddress & 0xffffff) )
+ break;
+ n_address = ( n_nextaddress & 0xffffff );
+
+ n_total += ( n_size + 1 );
+ }
+ }
+ }
+
+ m_p_n_dmachannelcontrol[ n_channel ] &= ~( ( 1L << 0x18 ) | ( 1L << 0x1c ) );
+
+ m_n_dicr |= 1 << ( 24 + n_channel );
+ dma_interrupt_update();
+ dma_stop_timer( n_channel );
+}
+
+static TIMER_CALLBACK( dma_finished_callback )
+{
+ dma_finished(param);
+}
+
+void psx_dma_install_read_handler( int n_channel, psx_dma_read_handler p_fn_dma_read )
+{
+ m_p_fn_dma_read[ n_channel ] = p_fn_dma_read;
+}
+
+void psx_dma_install_write_handler( int n_channel, psx_dma_read_handler p_fn_dma_write )
+{
+ m_p_fn_dma_write[ n_channel ] = p_fn_dma_write;
+}
+
+WRITE32_HANDLER( psx_dma_w )
+{
+ static int n_channel;
+ n_channel = offset / 4;
+ if( n_channel < 7 )
+ {
+ switch( offset % 4 )
+ {
+ case 0:
+ verboselog( 2, "dmabase( %d ) = %08x\n", n_channel, data );
+ m_p_n_dmabase[ n_channel ] = data;
+ break;
+ case 1:
+ verboselog( 2, "dmablockcontrol( %d ) = %08x\n", n_channel, data );
+ m_p_n_dmablockcontrol[ n_channel ] = data;
+ break;
+ case 2:
+ verboselog( 2, "dmachannelcontrol( %d ) = %08x\n", n_channel, data );
+ m_p_n_dmachannelcontrol[ n_channel ] = data;
+ if( ( m_p_n_dmachannelcontrol[ n_channel ] & ( 1L << 0x18 ) ) != 0 && ( m_n_dpcp & ( 1 << ( 3 + ( n_channel * 4 ) ) ) ) != 0 )
+ {
+ INT32 n_size;
+ UINT32 n_address;
+ UINT32 n_nextaddress;
+ UINT32 n_adrmask;
+
+ n_adrmask = g_n_psxramsize - 1;
+
+ n_address = ( m_p_n_dmabase[ n_channel ] & n_adrmask );
+ n_size = m_p_n_dmablockcontrol[ n_channel ];
+ if( ( m_p_n_dmachannelcontrol[ n_channel ] & 0x200 ) != 0 )
+ {
+ UINT32 n_ba;
+ n_ba = m_p_n_dmablockcontrol[ n_channel ] >> 16;
+ if( n_ba == 0 )
+ {
+ n_ba = 0x10000;
+ }
+ n_size = ( n_size & 0xffff ) * n_ba;
+ }
+
+ if( m_p_n_dmachannelcontrol[ n_channel ] == 0x01000000 &&
+ m_p_fn_dma_read[ n_channel ] != NULL )
+ {
+ verboselog( 1, "dma %d read block %08x %08x\n", n_channel, n_address, n_size );
+ m_p_fn_dma_read[ n_channel ]( n_address, n_size );
+ dma_finished( n_channel );
+ }
+ else if( m_p_n_dmachannelcontrol[ n_channel ] == 0x01000200 &&
+ m_p_fn_dma_read[ n_channel ] != NULL )
+ {
+ verboselog( 1, "dma %d read block %08x %08x\n", n_channel, n_address, n_size );
+ m_p_fn_dma_read[ n_channel ]( n_address, n_size );
+ if( n_channel == 1 )
+ {
+ dma_start_timer( n_channel, 26000 );
+ }
+ else
+ {
+ dma_finished( n_channel );
+ }
+ }
+ else if( m_p_n_dmachannelcontrol[ n_channel ] == 0x01000201 &&
+ m_p_fn_dma_write[ n_channel ] != NULL )
+ {
+ verboselog( 1, "dma %d write block %08x %08x\n", n_channel, n_address, n_size );
+ m_p_fn_dma_write[ n_channel ]( n_address, n_size );
+ dma_finished( n_channel );
+ }
+ else if( m_p_n_dmachannelcontrol[ n_channel ] == 0x11050100 &&
+ m_p_fn_dma_write[ n_channel ] != NULL )
+ {
+ /* todo: check this is a write not a read... */
+ verboselog( 1, "dma %d write block %08x %08x\n", n_channel, n_address, n_size );
+ m_p_fn_dma_write[ n_channel ]( n_address, n_size );
+ dma_finished( n_channel );
+ }
+ else if( m_p_n_dmachannelcontrol[ n_channel ] == 0x11150100 &&
+ m_p_fn_dma_write[ n_channel ] != NULL )
+ {
+ /* todo: check this is a write not a read... */
+ verboselog( 1, "dma %d write block %08x %08x\n", n_channel, n_address, n_size );
+ m_p_fn_dma_write[ n_channel ]( n_address, n_size );
+ dma_finished( n_channel );
+ }
+ else if( m_p_n_dmachannelcontrol[ n_channel ] == 0x01000401 &&
+ n_channel == 2 &&
+ m_p_fn_dma_write[ n_channel ] != NULL )
+ {
+ verboselog( 1, "dma %d write linked list %08x\n",
+ n_channel, m_p_n_dmabase[ n_channel ] );
+
+ dma_finished( n_channel );
+ }
+ else if( m_p_n_dmachannelcontrol[ n_channel ] == 0x11000002 &&
+ n_channel == 6 )
+ {
+ verboselog( 1, "dma 6 reverse clear %08x %08x\n",
+ m_p_n_dmabase[ n_channel ], m_p_n_dmablockcontrol[ n_channel ] );
+ if( n_size > 0 )
+ {
+ n_size--;
+ while( n_size > 0 )
+ {
+ n_nextaddress = ( n_address - 4 ) & 0xffffff;
+ g_p_n_psxram[ n_address / 4 ] = n_nextaddress;
+ n_address = n_nextaddress;
+ n_size--;
+ }
+ g_p_n_psxram[ n_address / 4 ] = 0xffffff;
+ }
+ dma_start_timer( n_channel, 2150 );
+ }
+ else
+ {
+ verboselog( 0, "dma %d unknown mode %08x\n", n_channel, m_p_n_dmachannelcontrol[ n_channel ] );
+ }
+ }
+ else if( m_p_n_dmachannelcontrol[ n_channel ] != 0 )
+ {
+ verboselog( 1, "psx_dma_w( %04x, %08x, %08x ) channel not enabled\n", offset, m_p_n_dmachannelcontrol[ n_channel ], mem_mask );
+ }
+ break;
+ default:
+ verboselog( 1, "psx_dma_w( %04x, %08x, %08x ) Unknown dma channel register\n", offset, data, mem_mask );
+ break;
+ }
+ }
+ else
+ {
+ switch( offset % 4 )
+ {
+ case 0x0:
+ verboselog( 1, "psx_dma_w( %04x, %08x, %08x ) dpcp\n", offset, data, mem_mask );
+ m_n_dpcp = ( m_n_dpcp & mem_mask ) | data;
+ break;
+ case 0x1:
+ m_n_dicr = ( m_n_dicr & mem_mask ) |
+ ( ~mem_mask & 0x80000000 & m_n_dicr ) |
+ ( ~data & ~mem_mask & 0x7f000000 & m_n_dicr ) |
+ ( data & ~mem_mask & 0x00ffffff );
+/* todo: find out whether to do this instead of dma_interrupt_update()
+
+ if( ( m_n_dicr & 0x7f000000 ) != 0 )
+ {
+ m_n_dicr &= ~0x80000000;
+ }
+*/
+ verboselog( 1, "psx_dma_w( %04x, %08x, %08x ) dicr -> %08x\n", offset, data, mem_mask, m_n_dicr );
+ dma_interrupt_update();
+ break;
+ default:
+ verboselog( 0, "psx_dma_w( %04x, %08x, %08x ) Unknown dma control register\n", offset, data, mem_mask );
+ break;
+ }
+ }
+}
+
+READ32_HANDLER( psx_dma_r )
+{
+ static int n_channel;
+ n_channel = offset / 4;
+ if( n_channel < 7 )
+ {
+ switch( offset % 4 )
+ {
+ case 0:
+ verboselog( 1, "psx_dma_r dmabase[ %d ] ( %08x )\n", n_channel, m_p_n_dmabase[ n_channel ] );
+ return m_p_n_dmabase[ n_channel ];
+ case 1:
+ verboselog( 1, "psx_dma_r dmablockcontrol[ %d ] ( %08x )\n", n_channel, m_p_n_dmablockcontrol[ n_channel ] );
+ return m_p_n_dmablockcontrol[ n_channel ];
+ case 2:
+ verboselog( 1, "psx_dma_r dmachannelcontrol[ %d ] ( %08x )\n", n_channel, m_p_n_dmachannelcontrol[ n_channel ] );
+ return m_p_n_dmachannelcontrol[ n_channel ];
+ default:
+ verboselog( 0, "psx_dma_r( %08x, %08x ) Unknown dma channel register\n", offset, mem_mask );
+ break;
+ }
+ }
+ else
+ {
+ switch( offset % 4 )
+ {
+ case 0x0:
+ verboselog( 1, "psx_dma_r dpcp ( %08x )\n", m_n_dpcp );
+ return m_n_dpcp;
+ case 0x1:
+ verboselog( 1, "psx_dma_r dicr ( %08x )\n", m_n_dicr );
+ return m_n_dicr;
+ default:
+ verboselog( 0, "psx_dma_r( %08x, %08x ) Unknown dma control register\n", offset, mem_mask );
+ break;
+ }
+ }
+ return 0;
+}
+
+/* Root Counters */
+
+static emu_timer *m_p_timer_root[ 3 ];
+static UINT16 m_p_n_root_count[ 3 ];
+static UINT16 m_p_n_root_mode[ 3 ];
+static UINT16 m_p_n_root_target[ 3 ];
+static UINT32 m_p_n_root_start[ 3 ];
+
+#define RC_STOP ( 0x01 )
+#define RC_RESET ( 0x04 ) /* guess */
+#define RC_COUNTTARGET ( 0x08 )
+#define RC_IRQTARGET ( 0x10 )
+#define RC_IRQOVERFLOW ( 0x20 )
+#define RC_REPEAT ( 0x40 )
+#define RC_CLC ( 0x100 )
+#define RC_DIV ( 0x200 )
+
+static UINT32 psxcpu_gettotalcycles( void )
+{
+ /* TODO: should return the start of the current tick. */
+ return cpunum_gettotalcycles(0) * 2;
+}
+
+static int root_divider( int n_counter )
+{
+ if( n_counter == 0 && ( m_p_n_root_mode[ n_counter ] & RC_CLC ) != 0 )
+ {
+ /* TODO: pixel clock, probably based on resolution */
+ return 5;
+ }
+ else if( n_counter == 1 && ( m_p_n_root_mode[ n_counter ] & RC_CLC ) != 0 )
+ {
+ return 2150;
+ }
+ else if( n_counter == 2 && ( m_p_n_root_mode[ n_counter ] & RC_DIV ) != 0 )
+ {
+ return 8;
+ }
+ return 1;
+}
+
+static UINT16 root_current( int n_counter )
+{
+ if( ( m_p_n_root_mode[ n_counter ] & RC_STOP ) != 0 )
+ {
+ return m_p_n_root_count[ n_counter ];
+ }
+ else
+ {
+ UINT32 n_current;
+ n_current = psxcpu_gettotalcycles() - m_p_n_root_start[ n_counter ];
+ n_current /= root_divider( n_counter );
+ n_current += m_p_n_root_count[ n_counter ];
+ if( n_current > 0xffff )
+ {
+ /* TODO: use timer for wrap on 0x10000. */
+ m_p_n_root_count[ n_counter ] = n_current;
+ m_p_n_root_start[ n_counter ] = psxcpu_gettotalcycles();
+ }
+ return n_current;
+ }
+}
+
+static int root_target( int n_counter )
+{
+ if( ( m_p_n_root_mode[ n_counter ] & RC_COUNTTARGET ) != 0 ||
+ ( m_p_n_root_mode[ n_counter ] & RC_IRQTARGET ) != 0 )
+ {
+ return m_p_n_root_target[ n_counter ];
+ }
+ return 0x10000;
+}
+
+static void root_timer_adjust( int n_counter )
+{
+ if( ( m_p_n_root_mode[ n_counter ] & RC_STOP ) != 0 )
+ {
+ timer_adjust( m_p_timer_root[ n_counter ], attotime_never, n_counter, attotime_never);
+ }
+ else
+ {
+ int n_duration;
+
+ n_duration = root_target( n_counter ) - root_current( n_counter );
+ if( n_duration < 1 )
+ {
+ n_duration += 0x10000;
+ }
+
+ n_duration *= root_divider( n_counter );
+
+ timer_adjust( m_p_timer_root[ n_counter ], attotime_mul(ATTOTIME_IN_HZ(33868800), n_duration), n_counter, attotime_zero);
+ }
+}
+
+static TIMER_CALLBACK( root_finished )
+{
+ int n_counter = param;
+
+ verboselog( 2, "root_finished( %d ) %04x\n", n_counter, root_current( n_counter ) );
+// if( ( m_p_n_root_mode[ n_counter ] & RC_COUNTTARGET ) != 0 )
+ {
+ /* TODO: wrap should be handled differently as RC_COUNTTARGET & RC_IRQTARGET don't have to be the same. */
+ m_p_n_root_count[ n_counter ] = 0;
+ m_p_n_root_start[ n_counter ] = psxcpu_gettotalcycles();
+ }
+ if( ( m_p_n_root_mode[ n_counter ] & RC_REPEAT ) != 0 )
+ {
+ root_timer_adjust( n_counter );
+ }
+ if( ( m_p_n_root_mode[ n_counter ] & RC_IRQOVERFLOW ) != 0 ||
+ ( m_p_n_root_mode[ n_counter ] & RC_IRQTARGET ) != 0 )
+ {
+ psx_irq_set( 0x10 << n_counter );
+ }
+}
+
+WRITE32_HANDLER( psx_counter_w )
+{
+ int n_counter;
+
+ verboselog( 1, "psx_counter_w ( %08x, %08x, %08x )\n", offset, data, mem_mask );
+
+ n_counter = offset / 4;
+
+ switch( offset % 4 )
+ {
+ case 0:
+ m_p_n_root_count[ n_counter ] = data;
+ m_p_n_root_start[ n_counter ] = psxcpu_gettotalcycles();
+ break;
+ case 1:
+ m_p_n_root_count[ n_counter ] = root_current( n_counter );
+ m_p_n_root_start[ n_counter ] = psxcpu_gettotalcycles();
+ m_p_n_root_mode[ n_counter ] = data;
+
+ if( ( m_p_n_root_mode[ n_counter ] & RC_RESET ) != 0 )
+ {
+ m_p_n_root_count[ n_counter ] = 0;
+ }
+// if( ( data & 0xfca6 ) != 0 ||
+// ( ( data & 0x0100 ) != 0 && n_counter != 0 && n_counter != 1 ) ||
+// ( ( data & 0x0200 ) != 0 && n_counter != 2 ) )
+// {
+// mame_printf_debug( "mode %d 0x%04x\n", n_counter, data & 0xfca6 );
+// }
+ break;
+ case 2:
+ m_p_n_root_target[ n_counter ] = data;
+ break;
+ default:
+ verboselog( 0, "psx_counter_w( %08x, %08x, %08x ) unknown register\n", offset, mem_mask, data );
+ return;
+ }
+
+ root_timer_adjust( n_counter );
+}
+
+READ32_HANDLER( psx_counter_r )
+{
+ int n_counter;
+ UINT32 data;
+
+ n_counter = offset / 4;
+
+ switch( offset % 4 )
+ {
+ case 0:
+ data = root_current( n_counter );
+ break;
+ case 1:
+ data = m_p_n_root_mode[ n_counter ];
+ break;
+ case 2:
+ data = m_p_n_root_target[ n_counter ];
+ break;
+ default:
+ verboselog( 0, "psx_counter_r( %08x, %08x ) unknown register\n", offset, mem_mask );
+ return 0;
+ }
+ verboselog( 1, "psx_counter_r ( %08x, %08x ) %08x\n", offset, mem_mask, data );
+ return data;
+}
+
+/* SIO */
+
+#define SIO_BUF_SIZE ( 8 )
+
+static UINT32 m_p_n_sio_status[ 2 ];
+static UINT32 m_p_n_sio_mode[ 2 ];
+static UINT32 m_p_n_sio_control[ 2 ];
+static UINT32 m_p_n_sio_baud[ 2 ];
+static UINT32 m_p_n_sio_tx[ 2 ];
+static UINT32 m_p_n_sio_rx[ 2 ];
+static UINT32 m_p_n_sio_tx_prev[ 2 ];
+static UINT32 m_p_n_sio_rx_prev[ 2 ];
+static UINT32 m_p_n_sio_tx_data[ 2 ];
+static UINT32 m_p_n_sio_rx_data[ 2 ];
+static UINT32 m_p_n_sio_tx_shift[ 2 ];
+static UINT32 m_p_n_sio_rx_shift[ 2 ];
+static UINT32 m_p_n_sio_tx_bits[ 2 ];
+static UINT32 m_p_n_sio_rx_bits[ 2 ];
+
+static emu_timer *m_p_timer_sio[ 2 ];
+static psx_sio_handler m_p_f_sio_handler[ 2 ];
+
+#define SIO_STATUS_TX_RDY ( 1 << 0 )
+#define SIO_STATUS_RX_RDY ( 1 << 1 )
+#define SIO_STATUS_TX_EMPTY ( 1 << 2 )
+#define SIO_STATUS_OVERRUN ( 1 << 4 )
+#define SIO_STATUS_DSR ( 1 << 7 )
+#define SIO_STATUS_IRQ ( 1 << 9 )
+
+#define SIO_CONTROL_TX_ENA ( 1 << 0 )
+#define SIO_CONTROL_IACK ( 1 << 4 )
+#define SIO_CONTROL_RESET ( 1 << 6 )
+#define SIO_CONTROL_TX_IENA ( 1 << 10 )
+#define SIO_CONTROL_RX_IENA ( 1 << 11 )
+#define SIO_CONTROL_DSR_IENA ( 1 << 12 )
+#define SIO_CONTROL_DTR ( 1 << 13 )
+
+static void sio_interrupt( int n_port )
+{
+ verboselog( 1, "sio_interrupt( %d )\n", n_port );
+ m_p_n_sio_status[ n_port ] |= SIO_STATUS_IRQ;
+ if( n_port == 0 )
+ {
+ psx_irq_set( 0x80 );
+ }
+ else
+ {
+ psx_irq_set( 0x100 );
+ }
+}
+
+static void sio_timer_adjust( int n_port )
+{
+ attotime n_time;
+ if( ( m_p_n_sio_status[ n_port ] & SIO_STATUS_TX_EMPTY ) == 0 || m_p_n_sio_tx_bits[ n_port ] != 0 )
+ {
+ int n_prescaler;
+
+ switch( m_p_n_sio_mode[ n_port ] & 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_p_n_sio_baud[ n_port ] != 0 && n_prescaler != 0 )
+ {
+ n_time = attotime_mul(ATTOTIME_IN_HZ(33868800), n_prescaler * m_p_n_sio_baud[n_port]);
+ verboselog( 2, "sio_timer_adjust( %d ) = %f ( %d x %d )\n", n_port, n_time, n_prescaler, m_p_n_sio_baud[ n_port ] );
+ }
+ else
+ {
+ n_time = attotime_never;
+ verboselog( 0, "sio_timer_adjust( %d ) invalid baud rate ( %d x %d )\n", n_port, n_prescaler, m_p_n_sio_baud[ n_port ] );
+ }
+ }
+ else
+ {
+ n_time = attotime_never;
+ verboselog( 2, "sio_timer_adjust( %d ) finished\n", n_port );
+ }
+ timer_adjust( m_p_timer_sio[ n_port ], n_time, n_port, attotime_zero);
+}
+
+static TIMER_CALLBACK( sio_clock )
+{
+ int n_port = param;
+ verboselog( 2, "sio tick\n" );
+
+ if( m_p_n_sio_tx_bits[ n_port ] == 0 &&
+ ( m_p_n_sio_control[ n_port ] & SIO_CONTROL_TX_ENA ) != 0 &&
+ ( m_p_n_sio_status[ n_port ] & SIO_STATUS_TX_EMPTY ) == 0 )
+ {
+ m_p_n_sio_tx_bits[ n_port ] = 8;
+ m_p_n_sio_tx_shift[ n_port ] = m_p_n_sio_tx_data[ n_port ];
+ if( n_port == 0 )
+ {
+ m_p_n_sio_rx_bits[ n_port ] = 8;
+ m_p_n_sio_rx_shift[ n_port ] = 0;
+ }
+ m_p_n_sio_status[ n_port ] |= SIO_STATUS_TX_EMPTY;
+ m_p_n_sio_status[ n_port ] |= SIO_STATUS_TX_RDY;
+ }
+
+ if( m_p_n_sio_tx_bits[ n_port ] != 0 )
+ {
+ m_p_n_sio_tx[ n_port ] = ( m_p_n_sio_tx[ n_port ] & ~PSX_SIO_OUT_DATA ) | ( ( m_p_n_sio_tx_shift[ n_port ] & 1 ) * PSX_SIO_OUT_DATA );
+ m_p_n_sio_tx_shift[ n_port ] >>= 1;
+ m_p_n_sio_tx_bits[ n_port ]--;
+
+ if( m_p_f_sio_handler[ n_port ] != NULL )
+ {
+ if( n_port == 0 )
+ {
+ m_p_n_sio_tx[ n_port ] &= ~PSX_SIO_OUT_CLOCK;
+ m_p_f_sio_handler[ n_port ]( m_p_n_sio_tx[ n_port ] );
+ m_p_n_sio_tx[ n_port ] |= PSX_SIO_OUT_CLOCK;
+ }
+ m_p_f_sio_handler[ n_port ]( m_p_n_sio_tx[ n_port ] );
+ }
+
+ if( m_p_n_sio_tx_bits[ n_port ] == 0 &&
+ ( m_p_n_sio_control[ n_port ] & SIO_CONTROL_TX_IENA ) != 0 )
+ {
+ sio_interrupt( n_port );
+ }
+ }
+
+ if( m_p_n_sio_rx_bits[ n_port ] != 0 )
+ {
+ m_p_n_sio_rx_shift[ n_port ] = ( m_p_n_sio_rx_shift[ n_port ] >> 1 ) | ( ( ( m_p_n_sio_rx[ n_port ] & PSX_SIO_IN_DATA ) / PSX_SIO_IN_DATA ) << 7 );
+ m_p_n_sio_rx_bits[ n_port ]--;
+
+ if( m_p_n_sio_rx_bits[ n_port ] == 0 )
+ {
+ if( ( m_p_n_sio_status[ n_port ] & SIO_STATUS_RX_RDY ) != 0 )
+ {
+ m_p_n_sio_status[ n_port ] |= SIO_STATUS_OVERRUN;
+ }
+ else
+ {
+ m_p_n_sio_rx_data[ n_port ] = m_p_n_sio_rx_shift[ n_port ];
+ m_p_n_sio_status[ n_port ] |= SIO_STATUS_RX_RDY;
+ }
+ if( ( m_p_n_sio_control[ n_port ] & SIO_CONTROL_RX_IENA ) != 0 )
+ {
+ sio_interrupt( n_port );
+ }
+ }
+ }
+
+ sio_timer_adjust( n_port );
+}
+
+void psx_sio_input( int n_port, int n_mask, int n_data )
+{
+ verboselog( 1, "psx_sio_input( %d, %02x, %02x )\n", n_port, n_mask, n_data );
+ m_p_n_sio_rx[ n_port ] = ( m_p_n_sio_rx[ n_port ] & ~n_mask ) | ( n_data & n_mask );
+
+ if( ( m_p_n_sio_rx[ n_port ] & PSX_SIO_IN_DSR ) != 0 )
+ {
+ m_p_n_sio_status[ n_port ] |= SIO_STATUS_DSR;
+ if( ( m_p_n_sio_rx_prev[ n_port ] & PSX_SIO_IN_DSR ) == 0 &&
+ ( m_p_n_sio_control[ n_port ] & SIO_CONTROL_DSR_IENA ) != 0 )
+ {
+ sio_interrupt( n_port );
+ }
+ }
+ else
+ {
+ m_p_n_sio_status[ n_port ] &= ~SIO_STATUS_DSR;
+ }
+ m_p_n_sio_rx_prev[ n_port ] = m_p_n_sio_rx[ n_port ];
+}
+
+WRITE32_HANDLER( psx_sio_w )
+{
+ int n_port;
+
+ n_port = offset / 4;
+
+ switch( offset % 4 )
+ {
+ case 0:
+ verboselog( 1, "psx_sio_w %d data %02x (%08x)\n", n_port, data, mem_mask );
+ m_p_n_sio_tx_data[ n_port ] = data;
+ m_p_n_sio_status[ n_port ] &= ~( SIO_STATUS_TX_RDY );
+ m_p_n_sio_status[ n_port ] &= ~( SIO_STATUS_TX_EMPTY );
+ sio_timer_adjust( n_port );
+ break;
+ case 1:
+ verboselog( 0, "psx_sio_w( %08x, %08x, %08x )\n", offset, data, mem_mask );
+ break;
+ case 2:
+ if( ACCESSING_LSW32 )
+ {
+ m_p_n_sio_mode[ n_port ] = data & 0xffff;
+ verboselog( 1, "psx_sio_w %d mode %04x\n", n_port, data & 0xffff );
+ }
+ if( ACCESSING_MSW32 )
+ {
+ verboselog( 1, "psx_sio_w %d control %04x\n", n_port, data >> 16 );
+ m_p_n_sio_control[ n_port ] = data >> 16;
+
+ if( ( m_p_n_sio_control[ n_port ] & SIO_CONTROL_RESET ) != 0 )
+ {
+ verboselog( 1, "psx_sio_w reset\n" );
+ m_p_n_sio_status[ n_port ] |= SIO_STATUS_TX_EMPTY | SIO_STATUS_TX_RDY;
+ m_p_n_sio_status[ n_port ] &= ~( SIO_STATUS_RX_RDY | SIO_STATUS_OVERRUN | SIO_STATUS_IRQ );
+ }
+ if( ( m_p_n_sio_control[ n_port ] & SIO_CONTROL_IACK ) != 0 )
+ {
+ verboselog( 1, "psx_sio_w iack\n" );
+ m_p_n_sio_status[ n_port ] &= ~( SIO_STATUS_IRQ );
+ m_p_n_sio_control[ n_port ] &= ~( SIO_CONTROL_IACK );
+ }
+ if( ( m_p_n_sio_control[ n_port ] & SIO_CONTROL_DTR ) != 0 )
+ {
+ m_p_n_sio_tx[ n_port ] |= PSX_SIO_OUT_DTR;
+ }
+ else
+ {
+ m_p_n_sio_tx[ n_port ] &= ~PSX_SIO_OUT_DTR;
+ }
+
+ if( ( ( m_p_n_sio_tx[ n_port ] ^ m_p_n_sio_tx_prev[ n_port ] ) & PSX_SIO_OUT_DTR ) != 0 )
+ {
+ if( m_p_f_sio_handler[ n_port ] != NULL )
+ {
+ m_p_f_sio_handler[ n_port ]( m_p_n_sio_tx[ n_port ] );
+ }
+ }
+ m_p_n_sio_tx_prev[ n_port ] = m_p_n_sio_tx[ n_port ];
+
+ }
+ break;
+ case 3:
+ if( ACCESSING_LSW32 )
+ {
+ verboselog( 0, "psx_sio_w( %08x, %08x, %08x )\n", offset, data, mem_mask );
+ }
+ if( ACCESSING_MSW32 )
+ {
+ m_p_n_sio_baud[ n_port ] = data >> 16;
+ verboselog( 1, "psx_sio_w %d baud %04x\n", n_port, data >> 16 );
+ }
+ break;
+ default:
+ verboselog( 0, "psx_sio_w( %08x, %08x, %08x )\n", offset, data, mem_mask );
+ break;
+ }
+}
+
+READ32_HANDLER( psx_sio_r )
+{
+ UINT32 data;
+ int n_port;
+
+ n_port = offset / 4;
+
+ switch( offset % 4 )
+ {
+ case 0:
+ data = m_p_n_sio_rx_data[ n_port ];
+ m_p_n_sio_status[ n_port ] &= ~( SIO_STATUS_RX_RDY );
+ m_p_n_sio_rx_data[ n_port ] = 0xff;
+ verboselog( 1, "psx_sio_r %d data %02x (%08x)\n", n_port, data, mem_mask );
+ break;
+ case 1:
+ data = m_p_n_sio_status[ n_port ];
+ if( ACCESSING_LSW32 )
+ {
+ verboselog( 1, "psx_sio_r %d status %04x\n", n_port, data & 0xffff );
+ }
+ if( ACCESSING_MSW32 )
+ {
+ verboselog( 0, "psx_sio_r( %08x, %08x ) %08x\n", offset, mem_mask, data );
+ }
+ break;
+ case 2:
+ data = ( m_p_n_sio_control[ n_port ] << 16 ) | m_p_n_sio_mode[ n_port ];
+ if( ACCESSING_LSW32 )
+ {
+ verboselog( 1, "psx_sio_r %d mode %04x\n", n_port, data & 0xffff );
+ }
+ if( ACCESSING_MSW32 )
+ {
+ verboselog( 1, "psx_sio_r %d control %04x\n", n_port, data >> 16 );
+ }
+ break;
+ case 3:
+ data = m_p_n_sio_baud[ n_port ] << 16;
+ if( ACCESSING_LSW32 )
+ {
+ verboselog( 0, "psx_sio_r( %08x, %08x ) %08x\n", offset, mem_mask, data );
+ }
+ if( ACCESSING_MSW32 )
+ {
+ verboselog( 1, "psx_sio_r %d baud %04x\n", n_port, data >> 16 );
+ }
+ break;
+ default:
+ data = 0;
+ verboselog( 0, "psx_sio_r( %08x, %08x ) %08x\n", offset, mem_mask, data );
+ break;
+ }
+ return data;
+}
+
+void psx_sio_install_handler( int n_port, psx_sio_handler p_f_sio_handler )
+{
+ m_p_f_sio_handler[ n_port ] = p_f_sio_handler;
+}
+
+/* MDEC */
+
+static int mdec_decoded = 0;
+static int mdec_offset = 0;
+static UINT16 mdec_output[ 24 * 16 ];
+
+#define DCTSIZE ( 8 )
+#define DCTSIZE2 ( DCTSIZE * DCTSIZE )
+
+static INT32 m_p_n_mdec_quantize_y[ DCTSIZE2 ];
+static INT32 m_p_n_mdec_quantize_uv[ DCTSIZE2 ];
+static INT32 m_p_n_mdec_cos[ DCTSIZE2 ];
+static INT32 m_p_n_mdec_cos_precalc[ DCTSIZE2 * DCTSIZE2 ];
+
+static UINT32 m_n_mdec0_command;
+static UINT32 m_n_mdec0_address;
+static UINT32 m_n_mdec0_size;
+static UINT32 m_n_mdec1_command;
+static UINT32 m_n_mdec1_status;
+
+static UINT16 m_p_n_mdec_clamp8[ 256 * 3 ];
+static UINT16 m_p_n_mdec_r5[ 256 * 3 ];
+static UINT16 m_p_n_mdec_g5[ 256 * 3 ];
+static UINT16 m_p_n_mdec_b5[ 256 * 3 ];
+
+static UINT32 m_p_n_mdec_zigzag[ DCTSIZE2 ] =
+{
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63
+};
+
+static INT32 m_p_n_mdec_unpacked[ DCTSIZE2 * 6 * 2 ];
+
+#define MDEC_COS_PRECALC_BITS ( 21 )
+
+static void mdec_cos_precalc( void )
+{
+ UINT32 n_x;
+ UINT32 n_y;
+ UINT32 n_u;
+ UINT32 n_v;
+ INT32 *p_n_precalc;
+
+ p_n_precalc = m_p_n_mdec_cos_precalc;
+
+ for( n_y = 0; n_y < 8; n_y++ )
+ {
+ for( n_x = 0; n_x < 8; n_x++ )
+ {
+ for( n_v = 0; n_v < 8; n_v++ )
+ {
+ for( n_u = 0; n_u < 8; n_u++ )
+ {
+ *( p_n_precalc++ ) =
+ ( ( m_p_n_mdec_cos[ ( n_u * 8 ) + n_x ] *
+ m_p_n_mdec_cos[ ( n_v * 8 ) + n_y ] ) >> ( 30 - MDEC_COS_PRECALC_BITS ) );
+ }
+ }
+ }
+ }
+}
+
+static void mdec_idct( INT32 *p_n_src, INT32 *p_n_dst )
+{
+ UINT32 n_yx;
+ UINT32 n_vu;
+ INT32 p_n_z[ 8 ];
+ INT32 *p_n_data;
+ INT32 *p_n_precalc;
+
+ p_n_precalc = m_p_n_mdec_cos_precalc;
+
+ for( n_yx = 0; n_yx < DCTSIZE2; n_yx++ )
+ {
+ p_n_data = p_n_src;
+
+ memset( p_n_z, 0, sizeof( p_n_z ) );
+
+ for( n_vu = 0; n_vu < DCTSIZE2 / 8; n_vu++ )
+ {
+ p_n_z[ 0 ] += p_n_data[ 0 ] * p_n_precalc[ 0 ];
+ p_n_z[ 1 ] += p_n_data[ 1 ] * p_n_precalc[ 1 ];
+ p_n_z[ 2 ] += p_n_data[ 2 ] * p_n_precalc[ 2 ];
+ p_n_z[ 3 ] += p_n_data[ 3 ] * p_n_precalc[ 3 ];
+ p_n_z[ 4 ] += p_n_data[ 4 ] * p_n_precalc[ 4 ];
+ p_n_z[ 5 ] += p_n_data[ 5 ] * p_n_precalc[ 5 ];
+ p_n_z[ 6 ] += p_n_data[ 6 ] * p_n_precalc[ 6 ];
+ p_n_z[ 7 ] += p_n_data[ 7 ] * p_n_precalc[ 7 ];
+ p_n_data += 8;
+ p_n_precalc += 8;
+ }
+
+ *( p_n_dst++ ) = ( p_n_z[ 0 ] + p_n_z[ 1 ] + p_n_z[ 2 ] + p_n_z[ 3 ] +
+ p_n_z[ 4 ] + p_n_z[ 5 ] + p_n_z[ 6 ] + p_n_z[ 7 ] ) >> ( MDEC_COS_PRECALC_BITS + 2 );
+ }
+}
+
+INLINE UINT16 mdec_unpack_run( UINT16 n_packed )
+{
+ return n_packed >> 10;
+}
+
+INLINE INT32 mdec_unpack_val( UINT16 n_packed )
+{
+ return ( ( (INT32)n_packed ) << 22 ) >> 22;
+}
+
+static UINT32 mdec_unpack( UINT32 n_address )
+{
+ UINT8 n_z;
+ INT32 n_qscale;
+ UINT16 n_packed;
+ UINT32 n_block;
+ INT32 *p_n_block;
+ INT32 p_n_unpacked[ 64 ];
+ INT32 *p_n_q;
+
+ p_n_q = m_p_n_mdec_quantize_uv;
+ p_n_block = m_p_n_mdec_unpacked;
+
+ for( n_block = 0; n_block < 6; n_block++ )
+ {
+ memset( p_n_unpacked, 0, sizeof( p_n_unpacked ) );
+
+ if( n_block == 2 )
+ {
+ p_n_q = m_p_n_mdec_quantize_y;
+ }
+ n_packed = psxreadword( n_address );
+ n_address += 2;
+ if( n_packed == 0xfe00 )
+ {
+ break;
+ }
+
+ n_qscale = mdec_unpack_run( n_packed );
+ p_n_unpacked[ 0 ] = mdec_unpack_val( n_packed ) * p_n_q[ 0 ];
+
+ n_z = 0;
+ for( ;; )
+ {
+ n_packed = psxreadword( n_address );
+ n_address += 2;
+
+ if( n_packed == 0xfe00 )
+ {
+ break;
+ }
+ n_z += mdec_unpack_run( n_packed ) + 1;
+ if( n_z > 63 )
+ {
+ break;
+ }
+ p_n_unpacked[ m_p_n_mdec_zigzag[ n_z ] ] = ( mdec_unpack_val( n_packed ) * p_n_q[ n_z ] * n_qscale ) / 8;
+ }
+ mdec_idct( p_n_unpacked, p_n_block );
+ p_n_block += DCTSIZE2;
+ }
+ return n_address;
+}
+
+INLINE INT32 mdec_cr_to_r( INT32 n_cr )
+{
+ return ( 1435 * n_cr ) >> 10;
+}
+
+INLINE INT32 mdec_cr_to_g( INT32 n_cr )
+{
+ return ( -731 * n_cr ) >> 10;
+}
+
+INLINE INT32 mdec_cb_to_g( INT32 n_cb )
+{
+ return ( -351 * n_cb ) >> 10;
+}
+
+INLINE INT32 mdec_cb_to_b( INT32 n_cb )
+{
+ return ( 1814 * n_cb ) >> 10;
+}
+
+INLINE UINT16 mdec_clamp_r5( INT32 n_r )
+{
+ return m_p_n_mdec_r5[ n_r + 128 + 256 ];
+}
+
+INLINE UINT16 mdec_clamp_g5( INT32 n_g )
+{
+ return m_p_n_mdec_g5[ n_g + 128 + 256 ];
+}
+
+INLINE UINT16 mdec_clamp_b5( INT32 n_b )
+{
+ return m_p_n_mdec_b5[ n_b + 128 + 256 ];
+}
+
+INLINE void mdec_makergb15( UINT32 n_address, INT32 n_r, INT32 n_g, INT32 n_b, INT32 *p_n_y, UINT16 n_stp )
+{
+ mdec_output[ WORD_XOR_LE( n_address + 0 ) / 2 ] = n_stp |
+ mdec_clamp_r5( p_n_y[ 0 ] + n_r ) |
+ mdec_clamp_g5( p_n_y[ 0 ] + n_g ) |
+ mdec_clamp_b5( p_n_y[ 0 ] + n_b );
+
+ mdec_output[ WORD_XOR_LE( n_address + 2 ) / 2 ] = n_stp |
+ mdec_clamp_r5( p_n_y[ 1 ] + n_r ) |
+ mdec_clamp_g5( p_n_y[ 1 ] + n_g ) |
+ mdec_clamp_b5( p_n_y[ 1 ] + n_b );
+}
+
+static void mdec_yuv2_to_rgb15( void )
+{
+ INT32 n_r;
+ INT32 n_g;
+ INT32 n_b;
+ INT32 n_cb;
+ INT32 n_cr;
+ INT32 *p_n_cb;
+ INT32 *p_n_cr;
+ INT32 *p_n_y;
+ UINT32 n_x;
+ UINT32 n_y;
+ UINT32 n_z;
+ UINT16 n_stp;
+ int n_address = 0;
+
+ if( ( m_n_mdec0_command & ( 1L << 25 ) ) != 0 )
+ {
+ n_stp = 0x8000;
+ }
+ else
+ {
+ n_stp = 0x0000;
+ }
+
+ p_n_cr = &m_p_n_mdec_unpacked[ 0 ];
+ p_n_cb = &m_p_n_mdec_unpacked[ DCTSIZE2 ];
+ p_n_y = &m_p_n_mdec_unpacked[ DCTSIZE2 * 2 ];
+
+ for( n_z = 0; n_z < 2; n_z++ )
+ {
+ for( n_y = 0; n_y < 4; n_y++ )
+ {
+ for( n_x = 0; n_x < 4; n_x++ )
+ {
+ n_cr = *( p_n_cr );
+ n_cb = *( p_n_cb );
+ n_r = mdec_cr_to_r( n_cr );
+ n_g = mdec_cr_to_g( n_cr ) + mdec_cb_to_g( n_cb );
+ n_b = mdec_cb_to_b( n_cb );
+
+ mdec_makergb15( ( n_address + 0 ), n_r, n_g, n_b, p_n_y, n_stp );
+ mdec_makergb15( ( n_address + 32 ), n_r, n_g, n_b, p_n_y + 8, n_stp );
+
+ n_cr = *( p_n_cr + 4 );
+ n_cb = *( p_n_cb + 4 );
+ n_r = mdec_cr_to_r( n_cr );
+ n_g = mdec_cr_to_g( n_cr ) + mdec_cb_to_g( n_cb );
+ n_b = mdec_cb_to_b( n_cb );
+
+ mdec_makergb15( ( n_address + 16 ), n_r, n_g, n_b, p_n_y + DCTSIZE2, n_stp );
+ mdec_makergb15( ( n_address + 48 ), n_r, n_g, n_b, p_n_y + DCTSIZE2 + 8, n_stp );
+
+ p_n_cr++;
+ p_n_cb++;
+ p_n_y += 2;
+ n_address += 4;
+ }
+ p_n_cr += 4;
+ p_n_cb += 4;
+ p_n_y += 8;
+ n_address += 48;
+ }
+ p_n_y += DCTSIZE2;
+ }
+ mdec_decoded = ( 16 * 16 ) / 2;
+}
+
+INLINE UINT16 mdec_clamp8( INT32 n_r )
+{
+ return m_p_n_mdec_clamp8[ n_r + 128 + 256 ];
+}
+
+INLINE void mdec_makergb24( UINT32 n_address, INT32 n_r, INT32 n_g, INT32 n_b, INT32 *p_n_y, UINT32 n_stp )
+{
+ mdec_output[ WORD_XOR_LE( n_address + 0 ) / 2 ] = ( mdec_clamp8( p_n_y[ 0 ] + n_g ) << 8 ) | mdec_clamp8( p_n_y[ 0 ] + n_r );
+ mdec_output[ WORD_XOR_LE( n_address + 2 ) / 2 ] = ( mdec_clamp8( p_n_y[ 1 ] + n_r ) << 8 ) | mdec_clamp8( p_n_y[ 0 ] + n_b );
+ mdec_output[ WORD_XOR_LE( n_address + 4 ) / 2 ] = ( mdec_clamp8( p_n_y[ 1 ] + n_b ) << 8 ) | mdec_clamp8( p_n_y[ 1 ] + n_g );
+}
+
+static void mdec_yuv2_to_rgb24( void )
+{
+ INT32 n_r;
+ INT32 n_g;
+ INT32 n_b;
+ INT32 n_cb;
+ INT32 n_cr;
+ INT32 *p_n_cb;
+ INT32 *p_n_cr;
+ INT32 *p_n_y;
+ UINT32 n_x;
+ UINT32 n_y;
+ UINT32 n_z;
+ UINT32 n_stp;
+ int n_address = 0;
+
+ if( ( m_n_mdec0_command & ( 1L << 25 ) ) != 0 )
+ {
+ n_stp = 0x80008000;
+ }
+ else
+ {
+ n_stp = 0x00000000;
+ }
+
+ p_n_cr = &m_p_n_mdec_unpacked[ 0 ];
+ p_n_cb = &m_p_n_mdec_unpacked[ DCTSIZE2 ];
+ p_n_y = &m_p_n_mdec_unpacked[ DCTSIZE2 * 2 ];
+
+ for( n_z = 0; n_z < 2; n_z++ )
+ {
+ for( n_y = 0; n_y < 4; n_y++ )
+ {
+ for( n_x = 0; n_x < 4; n_x++ )
+ {
+ n_cr = *( p_n_cr );
+ n_cb = *( p_n_cb );
+ n_r = mdec_cr_to_r( n_cr );
+ n_g = mdec_cr_to_g( n_cr ) + mdec_cb_to_g( n_cb );
+ n_b = mdec_cb_to_b( n_cb );
+
+ mdec_makergb24( ( n_address + 0 ), n_r, n_g, n_b, p_n_y, n_stp );
+ mdec_makergb24( ( n_address + 48 ), n_r, n_g, n_b, p_n_y + 8, n_stp );
+
+ n_cr = *( p_n_cr + 4 );
+ n_cb = *( p_n_cb + 4 );
+ n_r = mdec_cr_to_r( n_cr );
+ n_g = mdec_cr_to_g( n_cr ) + mdec_cb_to_g( n_cb );
+ n_b = mdec_cb_to_b( n_cb );
+
+ mdec_makergb24( ( n_address + 24 ), n_r, n_g, n_b, p_n_y + DCTSIZE2, n_stp );
+ mdec_makergb24( ( n_address + 72 ), n_r, n_g, n_b, p_n_y + DCTSIZE2 + 8, n_stp );
+
+ p_n_cr++;
+ p_n_cb++;
+ p_n_y += 2;
+ n_address += 6;
+ }
+ p_n_cr += 4;
+ p_n_cb += 4;
+ p_n_y += 8;
+ n_address += 72;
+ }
+ p_n_y += DCTSIZE2;
+ }
+ mdec_decoded = ( 24 * 16 ) / 2;
+}
+
+static void mdec0_write( UINT32 n_address, INT32 n_size )
+{
+ int n_index;
+
+ verboselog( 2, "mdec0_write( %08x, %08x )\n", n_address, n_size );
+ switch( m_n_mdec0_command >> 28 )
+ {
+ case 0x3:
+ verboselog( 1, "mdec decode %08x %08x %08x\n", m_n_mdec0_command, n_address, n_size );
+ m_n_mdec0_address = n_address;
+ m_n_mdec0_size = n_size * 4;
+ m_n_mdec1_status |= ( 1L << 29 );
+ break;
+ case 0x4:
+ verboselog( 1, "mdec quantize table %08x %08x %08x\n", m_n_mdec0_command, n_address, n_size );
+ n_index = 0;
+ while( n_size > 0 )
+ {
+ if( n_index < DCTSIZE2 )
+ {
+ m_p_n_mdec_quantize_y[ n_index + 0 ] = ( g_p_n_psxram[ n_address / 4 ] >> 0 ) & 0xff;
+ m_p_n_mdec_quantize_y[ n_index + 1 ] = ( g_p_n_psxram[ n_address / 4 ] >> 8 ) & 0xff;
+ m_p_n_mdec_quantize_y[ n_index + 2 ] = ( g_p_n_psxram[ n_address / 4 ] >> 16 ) & 0xff;
+ m_p_n_mdec_quantize_y[ n_index + 3 ] = ( g_p_n_psxram[ n_address / 4 ] >> 24 ) & 0xff;
+ }
+ else if( n_index < DCTSIZE2 * 2 )
+ {
+ m_p_n_mdec_quantize_uv[ n_index + 0 - DCTSIZE2 ] = ( g_p_n_psxram[ n_address / 4 ] >> 0 ) & 0xff;
+ m_p_n_mdec_quantize_uv[ n_index + 1 - DCTSIZE2 ] = ( g_p_n_psxram[ n_address / 4 ] >> 8 ) & 0xff;
+ m_p_n_mdec_quantize_uv[ n_index + 2 - DCTSIZE2 ] = ( g_p_n_psxram[ n_address / 4 ] >> 16 ) & 0xff;
+ m_p_n_mdec_quantize_uv[ n_index + 3 - DCTSIZE2 ] = ( g_p_n_psxram[ n_address / 4 ] >> 24 ) & 0xff;
+ }
+ n_index += 4;
+ n_address += 4;
+ n_size--;
+ }
+ break;
+ case 0x6:
+ verboselog( 1, "mdec cosine table %08x %08x %08x\n", m_n_mdec0_command, n_address, n_size );
+ n_index = 0;
+ while( n_size > 0 )
+ {
+ m_p_n_mdec_cos[ n_index + 0 ] = (INT16)( ( g_p_n_psxram[ n_address / 4 ] >> 0 ) & 0xffff );
+ m_p_n_mdec_cos[ n_index + 1 ] = (INT16)( ( g_p_n_psxram[ n_address / 4 ] >> 16 ) & 0xffff );
+ n_index += 2;
+ n_address += 4;
+ n_size--;
+ }
+ mdec_cos_precalc();
+ break;
+ default:
+ verboselog( 0, "mdec unknown command %08x %08x %08x\n", m_n_mdec0_command, n_address, n_size );
+ break;
+ }
+}
+
+static void mdec1_read( UINT32 n_address, INT32 n_size )
+{
+ UINT32 n_this;
+ UINT32 n_nextaddress;
+
+ verboselog( 2, "mdec1_read( %08x, %08x )\n", n_address, n_size );
+ if( ( m_n_mdec0_command & ( 1L << 29 ) ) != 0 && m_n_mdec0_size != 0 )
+ {
+ while( n_size > 0 )
+ {
+ if( mdec_decoded == 0 )
+ {
+ if( (int)m_n_mdec0_size <= 0 )
+ {
+ mame_printf_debug( "ran out of data %08x\n", n_size );
+ m_n_mdec0_size = 0;
+ break;
+ }
+
+ n_nextaddress = mdec_unpack( m_n_mdec0_address );
+ m_n_mdec0_size -= n_nextaddress - m_n_mdec0_address;
+ m_n_mdec0_address = n_nextaddress;
+
+ if( ( m_n_mdec0_command & ( 1L << 27 ) ) != 0 )
+ {
+ mdec_yuv2_to_rgb15();
+ }
+ else
+ {
+ mdec_yuv2_to_rgb24();
+ }
+ mdec_offset = 0;
+ }
+
+ n_this = mdec_decoded;
+ if( n_this > n_size )
+ {
+ n_this = n_size;
+ }
+ mdec_decoded -= n_this;
+
+ memcpy( (UINT8 *)g_p_n_psxram + n_address, (UINT8 *)mdec_output + mdec_offset, n_this * 4 );
+ mdec_offset += n_this * 4;
+ n_address += n_this * 4;
+ n_size -= n_this;
+ }
+
+ if( (int)m_n_mdec0_size < 0 )
+ {
+ mame_printf_debug( "ran out of data %d\n", m_n_mdec0_size );
+ }
+ }
+ else
+ {
+ mame_printf_debug( "mdec1_read no conversion :%08x:%08x:\n", m_n_mdec0_command, m_n_mdec0_size );
+ }
+ m_n_mdec1_status &= ~( 1L << 29 );
+}
+
+WRITE32_HANDLER( psx_mdec_w )
+{
+ switch( offset )
+ {
+ case 0:
+ verboselog( 2, "mdec 0 command %08x\n", data );
+ m_n_mdec0_command = data;
+ break;
+ case 1:
+ verboselog( 2, "mdec 1 command %08x\n", data );
+ m_n_mdec1_command = data;
+ break;
+ }
+}
+
+READ32_HANDLER( psx_mdec_r )
+{
+ switch( offset )
+ {
+ case 0:
+ verboselog( 2, "mdec 0 status %08x\n", 0 );
+ return 0;
+ case 1:
+ verboselog( 2, "mdec 1 status %08x\n", m_n_mdec1_status );
+ return m_n_mdec1_status;
+ }
+ return 0;
+}
+
+static void gpu_read( UINT32 n_address, INT32 n_size )
+{
+ psx_gpu_read( &g_p_n_psxram[ n_address / 4 ], n_size );
+}
+
+static void gpu_write( UINT32 n_address, INT32 n_size )
+{
+ psx_gpu_write( &g_p_n_psxram[ n_address / 4 ], n_size );
+}
+
+void psx_machine_init( void )
+{
+ int n;
+
+ /* irq */
+ m_n_irqdata = 0;
+ m_n_irqmask = 0;
+
+ /* dma */
+ m_n_dpcp = 0;
+ m_n_dicr = 0;
+
+ m_n_mdec0_command = 0;
+ m_n_mdec0_address = 0;
+ m_n_mdec0_size = 0;
+ m_n_mdec1_command = 0;
+ m_n_mdec1_status = 0;
+
+ for( n = 0; n < 7; n++ )
+ {
+ dma_stop_timer( n );
+ }
+
+ for( n = 0; n < 2; n++ )
+ {
+ m_p_n_sio_status[ n ] = SIO_STATUS_TX_EMPTY | SIO_STATUS_TX_RDY;
+ m_p_n_sio_mode[ n ] = 0;
+ m_p_n_sio_control[ n ] = 0;
+ m_p_n_sio_baud[ n ] = 0;
+ m_p_n_sio_tx[ n ] = 0;
+ m_p_n_sio_rx[ n ] = 0;
+ m_p_n_sio_tx_prev[ n ] = 0;
+ m_p_n_sio_rx_prev[ n ] = 0;
+ m_p_n_sio_rx_data[ n ] = 0;
+ m_p_n_sio_tx_data[ n ] = 0;
+ m_p_n_sio_rx_shift[ n ] = 0;
+ m_p_n_sio_tx_shift[ n ] = 0;
+ m_p_n_sio_rx_bits[ n ] = 0;
+ m_p_n_sio_tx_bits[ n ] = 0;
+ }
+
+ psx_gpu_reset();
+}
+
+static void psx_postload( void )
+{
+ int n;
+
+ psx_irq_update();
+
+ for( n = 0; n < 7; n++ )
+ {
+ dma_timer_adjust( n );
+ }
+
+ for( n = 0; n < 3; n++ )
+ {
+ root_timer_adjust( n );
+ }
+
+ for( n = 0; n < 2; n++ )
+ {
+ sio_timer_adjust( n );
+ }
+
+ mdec_cos_precalc();
+}
+
+void psx_driver_init( void )
+{
+ int n;
+
+ for( n = 0; n < 7; n++ )
+ {
+ m_p_timer_dma[ n ] = timer_alloc( dma_finished_callback );
+ m_p_fn_dma_read[ n ] = NULL;
+ m_p_fn_dma_write[ n ] = NULL;
+ }
+
+ for( n = 0; n < 3; n++ )
+ {
+ m_p_timer_root[ n ] = timer_alloc( root_finished );
+ }
+
+ for( n = 0; n < 2; n++ )
+ {
+ m_p_timer_sio[ n ] = timer_alloc( sio_clock );
+ }
+
+ for( n = 0; n < 256; n++ )
+ {
+ m_p_n_mdec_clamp8[ n ] = 0;
+ m_p_n_mdec_clamp8[ n + 256 ] = n;
+ m_p_n_mdec_clamp8[ n + 512 ] = 255;
+
+ m_p_n_mdec_r5[ n ] = 0;
+ m_p_n_mdec_r5[ n + 256 ] = ( n >> 3 );
+ m_p_n_mdec_r5[ n + 512 ] = ( 255 >> 3 );
+
+ m_p_n_mdec_g5[ n ] = 0;
+ m_p_n_mdec_g5[ n + 256 ] = ( n >> 3 ) << 5;
+ m_p_n_mdec_g5[ n + 512 ] = ( 255 >> 3 ) << 5;
+
+ m_p_n_mdec_b5[ n ] = 0;
+ m_p_n_mdec_b5[ n + 256 ] = ( n >> 3 ) << 10;
+ m_p_n_mdec_b5[ n + 512 ] = ( 255 >> 3 ) << 10;
+ }
+
+ for( n = 0; n < 2; n++ )
+ {
+ m_p_f_sio_handler[ n ] = NULL;
+ }
+
+ psx_dma_install_read_handler( 1, mdec1_read );
+ psx_dma_install_read_handler( 2, gpu_read );
+
+ psx_dma_install_write_handler( 0, mdec0_write );
+ psx_dma_install_write_handler( 2, gpu_write );
+
+ state_save_register_global( m_n_irqdata );
+ state_save_register_global( m_n_irqmask );
+ state_save_register_global_array( m_p_n_dmabase );
+ state_save_register_global_array( m_p_n_dmablockcontrol );
+ state_save_register_global_array( m_p_n_dmachannelcontrol );
+ state_save_register_global_array( m_p_n_dma_ticks );
+ state_save_register_global_array( m_p_b_dma_running );
+ state_save_register_global( m_n_dpcp );
+ state_save_register_global( m_n_dicr );
+ state_save_register_global_array( m_p_n_root_count );
+ state_save_register_global_array( m_p_n_root_mode );
+ state_save_register_global_array( m_p_n_root_target );
+ state_save_register_global_array( m_p_n_root_start );
+
+ state_save_register_global_array( m_p_n_sio_status );
+ state_save_register_global_array( m_p_n_sio_mode );
+ state_save_register_global_array( m_p_n_sio_control );
+ state_save_register_global_array( m_p_n_sio_baud );
+ state_save_register_global_array( m_p_n_sio_tx );
+ state_save_register_global_array( m_p_n_sio_rx );
+ state_save_register_global_array( m_p_n_sio_tx_prev );
+ state_save_register_global_array( m_p_n_sio_rx_prev );
+ state_save_register_global_array( m_p_n_sio_rx_data );
+ state_save_register_global_array( m_p_n_sio_tx_data );
+ state_save_register_global_array( m_p_n_sio_rx_shift );
+ state_save_register_global_array( m_p_n_sio_tx_shift );
+ state_save_register_global_array( m_p_n_sio_rx_bits );
+ state_save_register_global_array( m_p_n_sio_tx_bits );
+
+ state_save_register_global( m_n_mdec0_command );
+ state_save_register_global( m_n_mdec0_address );
+ state_save_register_global( m_n_mdec0_size );
+ state_save_register_global( m_n_mdec1_command );
+ state_save_register_global( m_n_mdec1_status );
+ state_save_register_global_array( m_p_n_mdec_quantize_y );
+ state_save_register_global_array( m_p_n_mdec_quantize_uv );
+ state_save_register_global_array( m_p_n_mdec_cos );
+
+ state_save_register_func_postload( psx_postload );
+}