// license:MAME // copyright-holders:smf /* * 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" #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(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, PSX_MDEC, "Sony PSX MDEC", tag, owner, clock, "psxmdec", __FILE__) { } 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 *p_n_psxram, UINT32 n_address ) { 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 *p_n_psxram, UINT32 n_address, INT32 n_size ) { 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 *p_n_psxram, UINT32 n_address, INT32 n_size ) { 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 ) { osd_printf_debug( "ran out of data %08x\n", n_size ); n_0_size = 0; break; } n_nextaddress = mdec_unpack( p_n_psxram, 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; while((psxreadword( p_n_psxram, n_0_address ) == 0xfe00) && n_0_size) { n_0_address += 2; // eat up 0xfe00 n_0_size -= 2; } } 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 ) { osd_printf_debug( "ran out of data %d\n", n_0_size ); } } else { osd_printf_debug( "mdec1_read no conversion :%08x:%08x:\n", n_0_command, n_0_size ); } if((int)n_0_size <= 0) 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; }