summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/cpu/psx/mdec.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/cpu/psx/mdec.c')
-rw-r--r--trunk/src/emu/cpu/psx/mdec.c572
1 files changed, 572 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/psx/mdec.c b/trunk/src/emu/cpu/psx/mdec.c
new file mode 100644
index 00000000000..08a319e6856
--- /dev/null
+++ b/trunk/src/emu/cpu/psx/mdec.c
@@ -0,0 +1,572 @@
+/*
+ * PlayStation Motion Decoder emulator
+ *
+ * Copyright 2003-2011 smf
+ *
+ * Thanks to Oliver Galibert for help figuring out IDCT
+ *
+ */
+
+#include "emu.h"
+#include "dma.h"
+#include "mdec.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_MDEC = &device_creator<psxmdec_device>;
+
+psxmdec_device::psxmdec_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, PSX_MDEC, "PSX MDEC", tag, owner, clock)
+{
+}
+
+void psxmdec_device::device_reset()
+{
+ n_0_command = 0;
+ n_0_address = 0;
+ n_0_size = 0;
+ n_1_command = 0;
+ n_1_status = 0;
+ n_offset = 0;
+ n_decoded = 0;
+}
+
+void psxmdec_device::device_post_load()
+{
+ mdec_cos_precalc();
+}
+
+void psxmdec_device::device_start()
+{
+ for( int n = 0; n < 256; n++ )
+ {
+ p_n_clamp8[ n ] = 0;
+ p_n_clamp8[ n + 256 ] = n;
+ p_n_clamp8[ n + 512 ] = 255;
+
+ p_n_r5[ n ] = 0;
+ p_n_r5[ n + 256 ] = ( n >> 3 );
+ p_n_r5[ n + 512 ] = ( 255 >> 3 );
+
+ p_n_g5[ n ] = 0;
+ p_n_g5[ n + 256 ] = ( n >> 3 ) << 5;
+ p_n_g5[ n + 512 ] = ( 255 >> 3 ) << 5;
+
+ p_n_b5[ n ] = 0;
+ p_n_b5[ n + 256 ] = ( n >> 3 ) << 10;
+ p_n_b5[ n + 512 ] = ( 255 >> 3 ) << 10;
+ }
+
+ save_item( NAME( n_0_command ) );
+ save_item( NAME( n_0_address ) );
+ save_item( NAME( n_0_size ) );
+ save_item( NAME( n_1_command ) );
+ save_item( NAME( n_1_status ) );
+ save_item( NAME( p_n_quantize_y ) );
+ save_item( NAME( p_n_quantize_uv ) );
+ save_item( NAME( p_n_cos ) );
+}
+
+#ifdef UNUSED_FUNCTION
+INLINE void psxwriteword( UINT32 *p_n_psxram, UINT32 n_address, UINT16 n_data )
+{
+ *( (UINT16 *)( (UINT8 *)p_n_psxram + WORD_XOR_LE( n_address ) ) ) = n_data;
+}
+#endif
+
+INLINE UINT16 psxreadword( UINT32 *p_n_psxram, UINT32 n_address )
+{
+ return *( (UINT16 *)( (UINT8 *)p_n_psxram + WORD_XOR_LE( n_address ) ) );
+}
+
+static const 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
+};
+
+void psxmdec_device::mdec_cos_precalc()
+{
+ UINT32 n_x;
+ UINT32 n_y;
+ UINT32 n_u;
+ UINT32 n_v;
+ INT32 *p_n_precalc = p_n_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++ ) =
+ ( ( p_n_cos[ ( n_u * 8 ) + n_x ] *
+ p_n_cos[ ( n_v * 8 ) + n_y ] ) >> ( 30 - MDEC_COS_PRECALC_BITS ) );
+ }
+ }
+ }
+ }
+}
+
+void psxmdec_device::mdec_idct( INT32 *p_n_src, INT32 *p_n_dst )
+{
+ INT32 *p_n_precalc = p_n_cos_precalc;
+
+ for( UINT32 n_yx = 0; n_yx < DCTSIZE2; n_yx++ )
+ {
+ INT32 p_n_z[ 8 ];
+ INT32 *p_n_data = p_n_src;
+
+ memset( p_n_z, 0, sizeof( p_n_z ) );
+
+ for( UINT32 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;
+}
+
+UINT32 psxmdec_device::mdec_unpack( UINT32 n_address )
+{
+ psx_state *p_psx = machine().driver_data<psx_state>();
+ UINT32 *p_n_psxram = p_psx->m_p_n_psxram;
+
+ UINT8 n_z;
+ INT32 n_qscale;
+ UINT16 n_packed;
+ INT32 *p_n_block;
+ INT32 p_n_unpacked[ 64 ];
+ INT32 *p_n_q;
+
+ p_n_q = p_n_quantize_uv;
+ p_n_block = m_p_n_unpacked;
+
+ for( UINT32 n_block = 0; n_block < 6; n_block++ )
+ {
+ memset( p_n_unpacked, 0, sizeof( p_n_unpacked ) );
+
+ if( n_block == 2 )
+ {
+ p_n_q = p_n_quantize_y;
+ }
+ n_packed = psxreadword( p_n_psxram, 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( p_n_psxram, 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;
+}
+
+UINT16 psxmdec_device::mdec_clamp_r5( INT32 n_r ) const
+{
+ return p_n_r5[ n_r + 128 + 256 ];
+}
+
+UINT16 psxmdec_device::mdec_clamp_g5( INT32 n_g ) const
+{
+ return p_n_g5[ n_g + 128 + 256 ];
+}
+
+UINT16 psxmdec_device::mdec_clamp_b5( INT32 n_b ) const
+{
+ return p_n_b5[ n_b + 128 + 256 ];
+}
+
+void psxmdec_device::mdec_makergb15( UINT32 n_address, INT32 n_r, INT32 n_g, INT32 n_b, INT32 *p_n_y, UINT16 n_stp )
+{
+ p_n_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 );
+
+ p_n_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 );
+}
+
+void psxmdec_device::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( ( n_0_command & ( 1L << 25 ) ) != 0 )
+ {
+ n_stp = 0x8000;
+ }
+ else
+ {
+ n_stp = 0x0000;
+ }
+
+ p_n_cr = &m_p_n_unpacked[ 0 ];
+ p_n_cb = &m_p_n_unpacked[ DCTSIZE2 ];
+ p_n_y = &m_p_n_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;
+ }
+ n_decoded = ( 16 * 16 ) / 2;
+}
+
+UINT16 psxmdec_device::mdec_clamp8( INT32 n_r ) const
+{
+ return p_n_clamp8[ n_r + 128 + 256 ];
+}
+
+void psxmdec_device::mdec_makergb24( UINT32 n_address, INT32 n_r, INT32 n_g, INT32 n_b, INT32 *p_n_y, UINT32 n_stp )
+{
+ p_n_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 );
+ p_n_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 );
+ p_n_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 );
+}
+
+void psxmdec_device::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( ( n_0_command & ( 1L << 25 ) ) != 0 )
+ {
+ n_stp = 0x80008000;
+ }
+ else
+ {
+ n_stp = 0x00000000;
+ }
+
+ p_n_cr = &m_p_n_unpacked[ 0 ];
+ p_n_cb = &m_p_n_unpacked[ DCTSIZE2 ];
+ p_n_y = &m_p_n_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;
+ }
+ n_decoded = ( 24 * 16 ) / 2;
+}
+
+void psxmdec_device::dma_write( UINT32 n_address, INT32 n_size )
+{
+ psx_state *p_psx = machine().driver_data<psx_state>();
+ UINT32 *p_n_psxram = p_psx->m_p_n_psxram;
+ int n_index;
+
+ verboselog( machine(), 2, "mdec0_write( %08x, %08x )\n", n_address, n_size );
+
+ switch( n_0_command >> 28 )
+ {
+ case 0x3:
+ verboselog( machine(), 1, "mdec decode %08x %08x %08x\n", n_0_command, n_address, n_size );
+ n_0_address = n_address;
+ n_0_size = n_size * 4;
+ n_1_status |= ( 1L << 29 );
+ break;
+ case 0x4:
+ verboselog( machine(), 1, "mdec quantize table %08x %08x %08x\n", n_0_command, n_address, n_size );
+ n_index = 0;
+ while( n_size > 0 )
+ {
+ if( n_index < DCTSIZE2 )
+ {
+ p_n_quantize_y[ n_index + 0 ] = ( p_n_psxram[ n_address / 4 ] >> 0 ) & 0xff;
+ p_n_quantize_y[ n_index + 1 ] = ( p_n_psxram[ n_address / 4 ] >> 8 ) & 0xff;
+ p_n_quantize_y[ n_index + 2 ] = ( p_n_psxram[ n_address / 4 ] >> 16 ) & 0xff;
+ p_n_quantize_y[ n_index + 3 ] = ( p_n_psxram[ n_address / 4 ] >> 24 ) & 0xff;
+ }
+ else if( n_index < DCTSIZE2 * 2 )
+ {
+ p_n_quantize_uv[ n_index + 0 - DCTSIZE2 ] = ( p_n_psxram[ n_address / 4 ] >> 0 ) & 0xff;
+ p_n_quantize_uv[ n_index + 1 - DCTSIZE2 ] = ( p_n_psxram[ n_address / 4 ] >> 8 ) & 0xff;
+ p_n_quantize_uv[ n_index + 2 - DCTSIZE2 ] = ( p_n_psxram[ n_address / 4 ] >> 16 ) & 0xff;
+ p_n_quantize_uv[ n_index + 3 - DCTSIZE2 ] = ( p_n_psxram[ n_address / 4 ] >> 24 ) & 0xff;
+ }
+ n_index += 4;
+ n_address += 4;
+ n_size--;
+ }
+ break;
+ case 0x6:
+ verboselog( machine(), 1, "mdec cosine table %08x %08x %08x\n", n_0_command, n_address, n_size );
+ n_index = 0;
+ while( n_size > 0 )
+ {
+ p_n_cos[ n_index + 0 ] = (INT16)( ( p_n_psxram[ n_address / 4 ] >> 0 ) & 0xffff );
+ p_n_cos[ n_index + 1 ] = (INT16)( ( p_n_psxram[ n_address / 4 ] >> 16 ) & 0xffff );
+ n_index += 2;
+ n_address += 4;
+ n_size--;
+ }
+ mdec_cos_precalc();
+ break;
+ default:
+ verboselog( machine(), 0, "mdec unknown command %08x %08x %08x\n", n_0_command, n_address, n_size );
+ break;
+ }
+}
+
+void psxmdec_device::dma_read( UINT32 n_address, INT32 n_size )
+{
+ psx_state *p_psx = machine().driver_data<psx_state>();
+ UINT32 *p_n_psxram = p_psx->m_p_n_psxram;
+ UINT32 n_this;
+ UINT32 n_nextaddress;
+
+ verboselog( machine(), 2, "mdec1_read( %08x, %08x )\n", n_address, n_size );
+ if( ( n_0_command & ( 1L << 29 ) ) != 0 && n_0_size != 0 )
+ {
+ while( n_size > 0 )
+ {
+ if( n_decoded == 0 )
+ {
+ if( (int)n_0_size <= 0 )
+ {
+ mame_printf_debug( "ran out of data %08x\n", n_size );
+ n_0_size = 0;
+ break;
+ }
+
+ n_nextaddress = mdec_unpack( n_0_address );
+ n_0_size -= n_nextaddress - n_0_address;
+ n_0_address = n_nextaddress;
+
+ if( ( n_0_command & ( 1L << 27 ) ) != 0 )
+ {
+ mdec_yuv2_to_rgb15();
+ }
+ else
+ {
+ mdec_yuv2_to_rgb24();
+ }
+ n_offset = 0;
+ }
+
+ n_this = n_decoded;
+ if( n_this > n_size )
+ {
+ n_this = n_size;
+ }
+ n_decoded -= n_this;
+
+ memcpy( (UINT8 *)p_n_psxram + n_address, (UINT8 *)p_n_output + n_offset, n_this * 4 );
+ n_offset += n_this * 4;
+ n_address += n_this * 4;
+ n_size -= n_this;
+ }
+
+ if( (int)n_0_size < 0 )
+ {
+ mame_printf_debug( "ran out of data %d\n", n_0_size );
+ }
+ }
+ else
+ {
+ mame_printf_debug( "mdec1_read no conversion :%08x:%08x:\n", n_0_command, n_0_size );
+ }
+ n_1_status &= ~( 1L << 29 );
+}
+
+WRITE32_MEMBER( psxmdec_device::write )
+{
+ switch( offset )
+ {
+ case 0:
+ verboselog( machine(), 2, "mdec 0 command %08x\n", data );
+ n_0_command = data;
+ break;
+ case 1:
+ verboselog( machine(), 2, "mdec 1 command %08x\n", data );
+ n_1_command = data;
+ break;
+ }
+}
+
+READ32_MEMBER( psxmdec_device::read )
+{
+ switch( offset )
+ {
+ case 0:
+ verboselog( machine(), 2, "mdec 0 status %08x\n", 0 );
+ return 0;
+ case 1:
+ verboselog( machine(), 2, "mdec 1 status %08x\n", n_1_status );
+ return n_1_status;
+ }
+ return 0;
+}