2013-04-18 03:29:41 +00:00
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
2010-06-09 01:37:08 +00:00
# include "Common.h"
2011-03-16 22:48:17 +00:00
# include "DataReader.h"
2010-06-09 01:37:08 +00:00
# include "OpcodeDecoder.h"
# include "BPMemLoader.h"
# include "CPMemLoader.h"
# include "XFMemLoader.h"
2011-02-03 19:55:30 +00:00
# include "SWVertexLoader.h"
# include "SWStatistics.h"
2010-06-09 01:37:08 +00:00
# include "DebugUtil.h"
2011-02-03 19:55:30 +00:00
# include "SWCommandProcessor.h"
# include "CPMemLoader.h"
2011-01-29 08:34:57 +00:00
# include "SWVideoConfig.h"
2011-01-31 01:28:32 +00:00
# include "HW/Memmap.h"
2010-06-09 01:37:08 +00:00
typedef void ( * DecodingFunction ) ( u32 ) ;
namespace OpcodeDecoder
{
2011-03-16 22:48:17 +00:00
static DecodingFunction currentFunction = NULL ;
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 ) ;
2013-02-26 04:49:24 +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...
//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
}
2010-06-09 01:37:08 +00:00
void DecodePrimitiveStream ( u32 iBufferSize )
{
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 )
{
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-09 01:37:08 +00:00
}
void ReadXFData ( u32 iBufferSize )
{
2013-04-14 03:54:02 +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 + + )
pData [ i ] = DataReadU32 ( ) ;
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
}
void ExecuteDisplayList ( u32 addr , u32 count )
{
2013-04-14 03:54:02 +00:00
u8 * videoDataSave = g_pVideoData ;
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
2013-04-14 03:54:02 +00:00
g_pVideoData = 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
u32 readCount = ( u32 ) ( g_pVideoData - dlStart ) ;
dlStart = g_pVideoData ;
2010-06-09 01:37:08 +00:00
2013-04-14 03:54:02 +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
2013-04-14 03:54:02 +00:00
g_pVideoData = videoDataSave ;
2010-06-09 01:37:08 +00:00
}
void DecodeStandard ( u32 bufferSize )
{
2013-04-14 03:54:02 +00:00
_assert_msg_ ( VIDEO , CommandRunnable ( bufferSize ) , " Underflow during standard opcode decoding " ) ;
2010-06-09 01:37:08 +00:00
2013-04-14 03:54:02 +00:00
int Cmd = DataReadU8 ( ) ;
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
2013-04-14 03:54:02 +00: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 ;
case GX_CMD_INVL_VC : // Invalidate (vertex cache?)
DEBUG_LOG ( VIDEO , " Invalidate (vertex cache?) " ) ;
break ;
case GX_LOAD_BP_REG : //0x61
{
2010-06-09 01:37:08 +00:00
u32 cmd = DataReadU32 ( ) ;
2013-04-14 03:54:02 +00:00
SWLoadBPReg ( cmd ) ;
}
break ;
// draw primitives
default :
if ( Cmd & 0x80 )
{
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
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-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 )
{
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 ;
// draw primitives
default :
if ( Cmd & 0x80 )
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
}
}