2015-05-24 06:55:12 +02:00
// Copyright 2009 Dolphin Emulator Project
2015-05-18 01:08:10 +02:00
// Licensed under GPLv2+
2013-04-17 23:29:41 -04:00
// Refer to the license.txt file included.
2010-06-09 01:37:08 +00:00
2014-07-29 20:55:07 -04:00
#include "Common/ChunkFile.h"
2014-09-07 20:06:58 -05:00
#include "Common/CommonTypes.h"
2014-02-17 05:18:15 -05:00
#include "Core/HW/Memmap.h"
#include "VideoBackends/Software/BPMemLoader.h"
#include "VideoBackends/Software/CPMemLoader.h"
#include "VideoBackends/Software/DebugUtil.h"
#include "VideoBackends/Software/OpcodeDecoder.h"
#include "VideoBackends/Software/SWCommandProcessor.h"
#include "VideoBackends/Software/SWStatistics.h"
#include "VideoBackends/Software/SWVertexLoader.h"
#include "VideoBackends/Software/SWVideoConfig.h"
#include "VideoBackends/Software/XFMemLoader.h"
2014-07-08 16:49:33 +02:00
#include "VideoCommon/Fifo.h"
2014-11-29 03:39:24 +01:00
#include "VideoCommon/VertexLoaderUtils.h"
2010-06-09 01:37:08 +00:00
typedef void ( * DecodingFunction )( u32 );
namespace OpcodeDecoder
{
2014-03-09 21:14:26 +01:00
static DecodingFunction currentFunction = nullptr ;
2011-03-16 22:48:17 +00:00
static u32 minCommandSize ;
static u16 streamSize ;
static u16 streamAddress ;
static bool readOpcode ;
static SWVertexLoader vertexLoader ;
static bool inObjectStream ;
static u8 lastPrimCmd ;
2013-02-25 20:05:02 -05:00
void DoState ( PointerWrap & p )
{
p . Do ( minCommandSize );
2015-01-11 00:17:29 -05:00
// Not sure what is wrong with this. Something(s) in here is causing Dolphin to crash/hang when loading states saved from another run of Dolphin. Doesn't seem too important anyway...
2013-02-25 23:49:24 -05:00
//vertexLoader.DoState(p);
2013-02-25 20:05:02 -05:00
p . Do ( readOpcode );
p . Do ( inObjectStream );
p . Do ( lastPrimCmd );
2013-02-25 23:49:24 -05:00
p . Do ( streamSize );
p . Do ( streamAddress );
2013-02-26 18:43:37 -05:00
if ( p . GetMode () == PointerWrap :: MODE_READ )
ResetDecoding ();
2013-02-25 20:05:02 -05:00
}
2014-07-08 14:29:26 +02:00
static void DecodePrimitiveStream ( u32 iBufferSize )
2010-06-09 01:37:08 +00:00
{
2013-04-13 23:54:02 -04:00
u32 vertexSize = vertexLoader . GetVertexSize ();
2010-06-09 01:37:08 +00:00
2011-01-29 08:34:57 +00:00
bool skipPrimitives = g_bSkipCurrentFrame ||
2011-02-03 19:55:30 +00:00
swstats . thisFrame . numDrawnObjects < g_SWVideoConfig . drawStart ||
swstats . thisFrame . numDrawnObjects >= g_SWVideoConfig . drawEnd ;
2011-01-29 08:34:57 +00:00
2013-04-13 23:54:02 -04:00
if ( skipPrimitives )
{
while ( streamSize > 0 && iBufferSize >= vertexSize )
{
2014-08-26 13:37:32 -04:00
g_video_buffer_read_ptr += vertexSize ;
2013-04-13 23:54:02 -04:00
iBufferSize -= vertexSize ;
streamSize -- ;
}
}
else
{
while ( streamSize > 0 && iBufferSize >= vertexSize )
{
vertexLoader . LoadVertex ();
iBufferSize -= vertexSize ;
streamSize -- ;
}
}
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
if ( streamSize == 0 )
{
// return to normal command processing
ResetDecoding ();
}
2010-06-09 01:37:08 +00:00
}
2014-07-08 14:29:26 +02:00
static void ReadXFData ( u32 iBufferSize )
2010-06-09 01:37:08 +00:00
{
2013-10-29 01:23:17 -04:00
_assert_msg_ ( VIDEO , iBufferSize >= ( u32 )( streamSize * 4 ), "Underflow during standard opcode decoding" );
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
u32 pData [ 16 ];
for ( int i = 0 ; i < streamSize ; i ++ )
2015-10-01 09:28:19 +02:00
pData [ i ] = DataRead < u32 > ();
2013-04-13 23:54:02 -04:00
SWLoadXFReg ( streamSize , streamAddress , pData );
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
// return to normal command processing
ResetDecoding ();
2010-06-09 01:37:08 +00:00
}
2014-07-08 14:29:26 +02:00
static void ExecuteDisplayList ( u32 addr , u32 count )
2010-06-09 01:37:08 +00:00
{
2014-08-26 13:37:32 -04:00
u8 * videoDataSave = g_video_buffer_read_ptr ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
u8 * dlStart = Memory :: GetPointer ( addr );
2010-06-09 01:37:08 +00:00
2014-08-26 13:37:32 -04:00
g_video_buffer_read_ptr = dlStart ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
while ( OpcodeDecoder :: CommandRunnable ( count ))
{
OpcodeDecoder :: Run ( count );
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
// if data was read by the opcode decoder then the video data pointer changed
2014-08-26 13:37:32 -04:00
u32 readCount = ( u32 )( g_video_buffer_read_ptr - dlStart );
dlStart = g_video_buffer_read_ptr ;
2010-06-09 01:37:08 +00:00
2013-10-29 01:23:17 -04:00
_assert_msg_ ( VIDEO , count >= readCount , "Display list underrun" );
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
count -= readCount ;
}
2010-06-09 01:37:08 +00:00
2014-08-26 13:37:32 -04:00
g_video_buffer_read_ptr = videoDataSave ;
2010-06-09 01:37:08 +00:00
}
2014-07-08 14:29:26 +02:00
static void DecodeStandard ( u32 bufferSize )
2010-06-09 01:37:08 +00:00
{
2013-10-29 01:23:17 -04:00
_assert_msg_ ( VIDEO , CommandRunnable ( bufferSize ), "Underflow during standard opcode decoding" );
2010-06-09 01:37:08 +00:00
2015-10-01 09:28:19 +02:00
int Cmd = DataRead < u8 > ();
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
if ( Cmd == GX_NOP )
return ;
// Causes a SIGBUS error on Android
// XXX: Investigate
2013-02-26 13:49:00 -06:00
#ifndef ANDROID
2013-04-13 23:54:02 -04:00
// check if switching in or out of an object
2013-04-19 09:21:45 -04:00
// only used for debugging
2013-04-13 23:54:02 -04:00
if ( inObjectStream && ( Cmd & 0x87 ) != lastPrimCmd )
{
inObjectStream = false ;
DebugUtil :: OnObjectEnd ();
}
if ( Cmd & 0x80 && ! inObjectStream )
{
inObjectStream = true ;
lastPrimCmd = Cmd & 0x87 ;
DebugUtil :: OnObjectBegin ();
}
2013-02-26 13:49:00 -06:00
#endif
2014-03-11 00:30:55 +13:00
switch ( Cmd )
2013-04-13 23:54:02 -04:00
{
case GX_NOP :
break ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
case GX_LOAD_CP_REG : //0x08
{
2015-10-01 09:28:19 +02:00
u32 SubCmd = DataRead < u8 > ();
u32 Value = DataRead < u32 > ();
2013-04-13 23:54:02 -04:00
SWLoadCPReg ( SubCmd , Value );
}
break ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
case GX_LOAD_XF_REG :
{
2015-10-01 09:28:19 +02:00
u32 Cmd2 = DataRead < u32 > ();
2013-04-13 23:54:02 -04:00
streamSize = (( Cmd2 >> 16 ) & 15 ) + 1 ;
streamAddress = Cmd2 & 0xFFFF ;
currentFunction = ReadXFData ;
minCommandSize = streamSize * 4 ;
readOpcode = false ;
}
break ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
case GX_LOAD_INDX_A : //used for position matrices
2015-10-01 09:28:19 +02:00
SWLoadIndexedXF ( DataRead < u32 > (), 0xC );
2013-04-13 23:54:02 -04:00
break ;
case GX_LOAD_INDX_B : //used for normal matrices
2015-10-01 09:28:19 +02:00
SWLoadIndexedXF ( DataRead < u32 > (), 0xD );
2013-04-13 23:54:02 -04:00
break ;
case GX_LOAD_INDX_C : //used for postmatrices
2015-10-01 09:28:19 +02:00
SWLoadIndexedXF ( DataRead < u32 > (), 0xE );
2013-04-13 23:54:02 -04:00
break ;
case GX_LOAD_INDX_D : //used for lights
2015-10-01 09:28:19 +02:00
SWLoadIndexedXF ( DataRead < u32 > (), 0xF );
2013-04-13 23:54:02 -04:00
break ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
case GX_CMD_CALL_DL :
{
2015-10-01 09:28:19 +02:00
u32 dwAddr = DataRead < u32 > ();
u32 dwCount = DataRead < u32 > ();
2013-04-13 23:54:02 -04:00
ExecuteDisplayList ( dwAddr , dwCount );
}
break ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
case 0x44 :
// zelda 4 swords calls it and checks the metrics registers after that
break ;
2010-06-09 01:37:08 +00:00
2014-02-16 23:51:41 -05:00
case GX_CMD_INVL_VC : // Invalidate (vertex cache?)
DEBUG_LOG ( VIDEO , "Invalidate (vertex cache?)" );
2013-04-13 23:54:02 -04:00
break ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
case GX_LOAD_BP_REG : //0x61
{
2015-10-01 09:28:19 +02:00
u32 cmd = DataRead < u32 > ();
2013-04-13 23:54:02 -04:00
SWLoadBPReg ( cmd );
}
break ;
2010-06-09 01:37:08 +00:00
2013-10-29 01:23:17 -04:00
// draw primitives
2013-04-13 23:54:02 -04:00
default :
2014-05-08 15:43:41 -07:00
if (( Cmd & 0xC0 ) == 0x80 )
2013-04-13 23:54:02 -04:00
{
2010-06-09 01:37:08 +00:00
u8 vatIndex = Cmd & GX_VAT_MASK ;
2013-04-13 23:54:02 -04:00
u8 primitiveType = ( Cmd & GX_PRIMITIVE_MASK ) >> GX_PRIMITIVE_SHIFT ;
vertexLoader . SetFormat ( vatIndex , primitiveType );
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
// switch to primitive processing
2015-10-01 09:28:19 +02:00
streamSize = DataRead < u16 > ();
2013-04-13 23:54:02 -04:00
currentFunction = DecodePrimitiveStream ;
minCommandSize = vertexLoader . GetVertexSize ();
readOpcode = false ;
INCSTAT ( swstats . thisFrame . numPrimatives );
DEBUG_LOG ( VIDEO , "Draw begin" );
}
else
{
PanicAlert ( "GFX: Unknown Opcode (0x%x). \n " , Cmd );
break ;
}
break ;
}
2010-06-09 01:37:08 +00:00
}
void Init ()
{
2013-04-13 23:54:02 -04:00
inObjectStream = false ;
lastPrimCmd = 0 ;
ResetDecoding ();
2010-06-09 01:37:08 +00:00
}
void ResetDecoding ()
{
2013-04-13 23:54:02 -04:00
currentFunction = DecodeStandard ;
minCommandSize = 1 ;
readOpcode = true ;
2010-06-09 01:37:08 +00:00
}
bool CommandRunnable ( u32 iBufferSize )
{
2013-04-13 23:54:02 -04:00
if ( iBufferSize < minCommandSize )
return false ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
if ( readOpcode )
{
2015-10-01 09:28:19 +02:00
u8 Cmd = DataPeek < u8 > ( 0 );
2013-04-13 23:54:02 -04:00
u32 minSize = 1 ;
2010-06-09 01:37:08 +00:00
2014-03-11 00:30:55 +13:00
switch ( Cmd )
2013-04-13 23:54:02 -04:00
{
case GX_LOAD_CP_REG : //0x08
minSize = 6 ;
break ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
case GX_LOAD_XF_REG :
minSize = 5 ;
break ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
case GX_LOAD_INDX_A : //used for position matrices
minSize = 5 ;
break ;
case GX_LOAD_INDX_B : //used for normal matrices
minSize = 5 ;
break ;
case GX_LOAD_INDX_C : //used for postmatrices
minSize = 5 ;
break ;
case GX_LOAD_INDX_D : //used for lights
minSize = 5 ;
break ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
case GX_CMD_CALL_DL :
minSize = 9 ;
break ;
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
case GX_LOAD_BP_REG : //0x61
minSize = 5 ;
break ;
2010-06-09 01:37:08 +00:00
2013-10-29 01:23:17 -04:00
// draw primitives
2013-04-13 23:54:02 -04:00
default :
2014-05-08 15:43:41 -07:00
if (( Cmd & 0xC0 ) == 0x80 )
2013-04-13 23:54:02 -04:00
minSize = 3 ;
break ;
}
2010-06-09 01:37:08 +00:00
2013-04-13 23:54:02 -04:00
return ( iBufferSize >= minSize );
}
return true ;
2010-06-09 01:37:08 +00:00
}
void Run ( u32 iBufferSize )
{
2013-04-13 23:54:02 -04:00
currentFunction ( iBufferSize );
2010-06-09 01:37:08 +00:00
}
}