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