2015-05-24 04:55:12 +00:00
// Copyright 2009 Dolphin Emulator Project
2015-05-17 23:08:10 +00:00
// Licensed under GPLv2+
2013-04-18 03:29:41 +00:00
// Refer to the license.txt file included.
2010-06-09 01:37:08 +00:00
2014-07-30 00:55:07 +00:00
# include "Common/ChunkFile.h"
2014-09-08 01:06:58 +00:00
# include "Common/CommonTypes.h"
2014-02-17 10:18:15 +00: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 14:49:33 +00:00
# include "VideoCommon/Fifo.h"
2014-11-29 02:39:24 +00:00
# include "VideoCommon/VertexLoaderUtils.h"
2010-06-09 01:37:08 +00:00
typedef void ( * DecodingFunction ) ( u32 ) ;
namespace OpcodeDecoder
{
2014-03-09 20:14:26 +00: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-26 01:05:02 +00:00
void DoState ( PointerWrap & p )
{
p . Do ( minCommandSize ) ;
2015-01-11 05:17:29 +00: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-26 04:49:24 +00:00
//vertexLoader.DoState(p);
2013-02-26 01:05:02 +00:00
p . Do ( readOpcode ) ;
p . Do ( inObjectStream ) ;
p . Do ( lastPrimCmd ) ;
2013-02-26 04:49:24 +00:00
p . Do ( streamSize ) ;
p . Do ( streamAddress ) ;
2013-02-26 23:43:37 +00:00
if ( p . GetMode ( ) = = PointerWrap : : MODE_READ )
ResetDecoding ( ) ;
2013-02-26 01:05:02 +00:00
}
2014-07-08 12:29:26 +00:00
static void DecodePrimitiveStream ( u32 iBufferSize )
2010-06-09 01:37:08 +00:00
{
2013-04-14 03:54:02 +00: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-14 03:54:02 +00:00
if ( skipPrimitives )
{
while ( streamSize > 0 & & iBufferSize > = vertexSize )
{
2014-08-26 17:37:32 +00:00
g_video_buffer_read_ptr + = vertexSize ;
2013-04-14 03:54:02 +00:00
iBufferSize - = vertexSize ;
streamSize - - ;
}
}
else
{
while ( streamSize > 0 & & iBufferSize > = vertexSize )
{
vertexLoader . LoadVertex ( ) ;
iBufferSize - = vertexSize ;
streamSize - - ;
}
}
if ( streamSize = = 0 )
{
// return to normal command processing
ResetDecoding ( ) ;
}
2010-06-09 01:37:08 +00:00
}
2014-07-08 12:29:26 +00:00
static void ReadXFData ( u32 iBufferSize )
2010-06-09 01:37:08 +00:00
{
2013-10-29 05:23:17 +00:00
_assert_msg_ ( VIDEO , iBufferSize > = ( u32 ) ( streamSize * 4 ) , " Underflow during standard opcode decoding " ) ;
2010-06-09 01:37:08 +00:00
2013-04-14 03:54:02 +00:00
u32 pData [ 16 ] ;
for ( int i = 0 ; i < streamSize ; i + + )
2015-10-01 07:28:19 +00:00
pData [ i ] = DataRead < u32 > ( ) ;
2013-04-14 03:54:02 +00:00
SWLoadXFReg ( streamSize , streamAddress , pData ) ;
2010-06-09 01:37:08 +00:00
2013-04-14 03:54:02 +00:00
// return to normal command processing
ResetDecoding ( ) ;
2010-06-09 01:37:08 +00:00
}
2014-07-08 12:29:26 +00:00
static void ExecuteDisplayList ( u32 addr , u32 count )
2010-06-09 01:37:08 +00:00
{
2014-08-26 17:37:32 +00:00
u8 * videoDataSave = g_video_buffer_read_ptr ;
2010-06-09 01:37:08 +00:00
2013-04-14 03:54:02 +00:00
u8 * dlStart = Memory : : GetPointer ( addr ) ;
2010-06-09 01:37:08 +00:00
2014-08-26 17:37:32 +00:00
g_video_buffer_read_ptr = dlStart ;
2010-06-09 01:37:08 +00:00
2013-04-14 03:54:02 +00:00
while ( OpcodeDecoder : : CommandRunnable ( count ) )
{
OpcodeDecoder : : Run ( count ) ;
2010-06-09 01:37:08 +00:00
2013-04-14 03:54:02 +00:00
// if data was read by the opcode decoder then the video data pointer changed
2014-08-26 17:37:32 +00: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 05:23:17 +00:00
_assert_msg_ ( VIDEO , count > = readCount , " Display list underrun " ) ;
2010-06-09 01:37:08 +00:00
2013-04-14 03:54:02 +00:00
count - = readCount ;
}
2010-06-09 01:37:08 +00:00
2014-08-26 17:37:32 +00:00
g_video_buffer_read_ptr = videoDataSave ;
2010-06-09 01:37:08 +00:00
}
2014-07-08 12:29:26 +00:00
static void DecodeStandard ( u32 bufferSize )
2010-06-09 01:37:08 +00:00
{
2013-10-29 05:23:17 +00:00
_assert_msg_ ( VIDEO , CommandRunnable ( bufferSize ) , " Underflow during standard opcode decoding " ) ;
2010-06-09 01:37:08 +00:00
2015-10-01 07:28:19 +00:00
int Cmd = DataRead < u8 > ( ) ;
2010-06-09 01:37:08 +00:00
2013-04-14 03:54:02 +00:00
if ( Cmd = = GX_NOP )
return ;
// Causes a SIGBUS error on Android
// XXX: Investigate
2013-02-26 19:49:00 +00:00
# ifndef ANDROID
2013-04-14 03:54:02 +00:00
// check if switching in or out of an object
2013-04-19 13:21:45 +00:00
// only used for debugging
2013-04-14 03:54:02 +00:00
if ( inObjectStream & & ( Cmd & 0x87 ) ! = lastPrimCmd )
{
inObjectStream = false ;
DebugUtil : : OnObjectEnd ( ) ;
}
if ( Cmd & 0x80 & & ! inObjectStream )
{
inObjectStream = true ;
lastPrimCmd = Cmd & 0x87 ;
DebugUtil : : OnObjectBegin ( ) ;
}
2013-02-26 19:49:00 +00:00
# endif
2014-03-10 11:30:55 +00:00
switch ( Cmd )
2013-04-14 03:54:02 +00:00
{
case GX_NOP :
break ;
case GX_LOAD_CP_REG : //0x08
{
2015-10-01 07:28:19 +00:00
u32 SubCmd = DataRead < u8 > ( ) ;
u32 Value = DataRead < u32 > ( ) ;
2013-04-14 03:54:02 +00:00
SWLoadCPReg ( SubCmd , Value ) ;
}
break ;
case GX_LOAD_XF_REG :
{
2015-10-01 07:28:19 +00:00
u32 Cmd2 = DataRead < u32 > ( ) ;
2013-04-14 03:54:02 +00:00
streamSize = ( ( Cmd2 > > 16 ) & 15 ) + 1 ;
streamAddress = Cmd2 & 0xFFFF ;
currentFunction = ReadXFData ;
minCommandSize = streamSize * 4 ;
readOpcode = false ;
}
break ;
case GX_LOAD_INDX_A : //used for position matrices
2015-10-01 07:28:19 +00:00
SWLoadIndexedXF ( DataRead < u32 > ( ) , 0xC ) ;
2013-04-14 03:54:02 +00:00
break ;
case GX_LOAD_INDX_B : //used for normal matrices
2015-10-01 07:28:19 +00:00
SWLoadIndexedXF ( DataRead < u32 > ( ) , 0xD ) ;
2013-04-14 03:54:02 +00:00
break ;
case GX_LOAD_INDX_C : //used for postmatrices
2015-10-01 07:28:19 +00:00
SWLoadIndexedXF ( DataRead < u32 > ( ) , 0xE ) ;
2013-04-14 03:54:02 +00:00
break ;
case GX_LOAD_INDX_D : //used for lights
2015-10-01 07:28:19 +00:00
SWLoadIndexedXF ( DataRead < u32 > ( ) , 0xF ) ;
2013-04-14 03:54:02 +00:00
break ;
case GX_CMD_CALL_DL :
{
2015-10-01 07:28:19 +00:00
u32 dwAddr = DataRead < u32 > ( ) ;
u32 dwCount = DataRead < u32 > ( ) ;
2013-04-14 03:54:02 +00:00
ExecuteDisplayList ( dwAddr , dwCount ) ;
}
break ;
case 0x44 :
// zelda 4 swords calls it and checks the metrics registers after that
break ;
2014-02-17 04:51:41 +00:00
case GX_CMD_INVL_VC : // Invalidate (vertex cache?)
DEBUG_LOG ( VIDEO , " Invalidate (vertex cache?) " ) ;
2013-04-14 03:54:02 +00:00
break ;
case GX_LOAD_BP_REG : //0x61
{
2015-10-01 07:28:19 +00:00
u32 cmd = DataRead < u32 > ( ) ;
2013-04-14 03:54:02 +00:00
SWLoadBPReg ( cmd ) ;
}
break ;
2013-10-29 05:23:17 +00:00
// draw primitives
2013-04-14 03:54:02 +00:00
default :
2014-05-08 22:43:41 +00:00
if ( ( Cmd & 0xC0 ) = = 0x80 )
2013-04-14 03:54:02 +00:00
{
2010-06-09 01:37:08 +00:00
u8 vatIndex = Cmd & GX_VAT_MASK ;
2013-04-14 03:54:02 +00:00
u8 primitiveType = ( Cmd & GX_PRIMITIVE_MASK ) > > GX_PRIMITIVE_SHIFT ;
vertexLoader . SetFormat ( vatIndex , primitiveType ) ;
// switch to primitive processing
2015-10-01 07:28:19 +00:00
streamSize = DataRead < u16 > ( ) ;
2013-04-14 03:54:02 +00: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-14 03:54:02 +00:00
inObjectStream = false ;
lastPrimCmd = 0 ;
ResetDecoding ( ) ;
2010-06-09 01:37:08 +00:00
}
void ResetDecoding ( )
{
2013-04-14 03:54:02 +00:00
currentFunction = DecodeStandard ;
minCommandSize = 1 ;
readOpcode = true ;
2010-06-09 01:37:08 +00:00
}
bool CommandRunnable ( u32 iBufferSize )
{
2013-04-14 03:54:02 +00:00
if ( iBufferSize < minCommandSize )
return false ;
if ( readOpcode )
{
2015-10-01 07:28:19 +00:00
u8 Cmd = DataPeek < u8 > ( 0 ) ;
2013-04-14 03:54:02 +00:00
u32 minSize = 1 ;
2014-03-10 11:30:55 +00:00
switch ( Cmd )
2013-04-14 03:54:02 +00:00
{
case GX_LOAD_CP_REG : //0x08
minSize = 6 ;
break ;
case GX_LOAD_XF_REG :
minSize = 5 ;
break ;
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 ;
case GX_CMD_CALL_DL :
minSize = 9 ;
break ;
case GX_LOAD_BP_REG : //0x61
minSize = 5 ;
break ;
2013-10-29 05:23:17 +00:00
// draw primitives
2013-04-14 03:54:02 +00:00
default :
2014-05-08 22:43:41 +00:00
if ( ( Cmd & 0xC0 ) = = 0x80 )
2013-04-14 03:54:02 +00:00
minSize = 3 ;
break ;
}
return ( iBufferSize > = minSize ) ;
}
return true ;
2010-06-09 01:37:08 +00:00
}
void Run ( u32 iBufferSize )
{
2013-04-14 03:54:02 +00:00
currentFunction ( iBufferSize ) ;
2010-06-09 01:37:08 +00:00
}
}