diff options
Diffstat (limited to 'src/devices/cpu/psx/mdec.c')
-rw-r--r-- | src/devices/cpu/psx/mdec.c | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/src/devices/cpu/psx/mdec.c b/src/devices/cpu/psx/mdec.c new file mode 100644 index 00000000000..57c6b3393b4 --- /dev/null +++ b/src/devices/cpu/psx/mdec.c @@ -0,0 +1,572 @@ +// license:BSD-3-Clause +// 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::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; +} |