2010-06-09 01:37:08 +00:00
// Copyright (C) 2003-2009 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
# 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 ) ;
// not sure how to save this... It seems to be the only thing left that is really important.
// uncommenting this will prevent all error messages, and any crashes/hangs on load, but then obviously it'll segfault once you restart dolphin.
2013-02-26 01:05:02 +00:00
//p.Do(currentFunction);
}
2010-06-09 01:37:08 +00:00
void DecodePrimitiveStream ( u32 iBufferSize )
{
u32 vertexSize = vertexLoader . GetVertexSize ( ) ;
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
if ( skipPrimitives )
2010-06-09 01:37:08 +00:00
{
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 ( ) ;
}
}
void ReadXFData ( u32 iBufferSize )
{
_assert_msg_ ( VIDEO , iBufferSize > = ( u32 ) ( streamSize * 4 ) , " Underflow during standard opcode decoding " ) ;
u32 pData [ 16 ] ;
for ( int i = 0 ; i < streamSize ; i + + )
pData [ i ] = DataReadU32 ( ) ;
2011-02-03 19:55:30 +00:00
SWLoadXFReg ( streamSize , streamAddress , pData ) ;
2010-06-09 01:37:08 +00:00
// return to normal command processing
ResetDecoding ( ) ;
}
void ExecuteDisplayList ( u32 addr , u32 count )
{
u8 * videoDataSave = g_pVideoData ;
2011-01-31 01:28:32 +00:00
u8 * dlStart = Memory : : GetPointer ( addr ) ;
2010-06-09 01:37:08 +00:00
g_pVideoData = dlStart ;
while ( OpcodeDecoder : : CommandRunnable ( count ) )
{
OpcodeDecoder : : Run ( count ) ;
// if data was read by the opcode decoder then the video data pointer changed
2011-03-16 22:48:17 +00:00
u32 readCount = ( u32 ) ( g_pVideoData - dlStart ) ;
2010-06-09 01:37:08 +00:00
dlStart = g_pVideoData ;
_assert_msg_ ( VIDEO , count > = readCount , " Display list underrun " ) ;
count - = readCount ;
}
g_pVideoData = videoDataSave ;
}
void DecodeStandard ( u32 bufferSize )
{
_assert_msg_ ( VIDEO , CommandRunnable ( bufferSize ) , " Underflow during standard opcode decoding " ) ;
int Cmd = DataReadU8 ( ) ;
if ( Cmd = = GX_NOP )
return ;
// check if switching in or out of an object
// only used for debuggging
if ( inObjectStream & & ( Cmd & 0x87 ) ! = lastPrimCmd )
{
inObjectStream = false ;
DebugUtil : : OnObjectEnd ( ) ;
}
if ( Cmd & 0x80 & & ! inObjectStream )
{
inObjectStream = true ;
lastPrimCmd = Cmd & 0x87 ;
DebugUtil : : OnObjectBegin ( ) ;
}
switch ( Cmd )
{
case GX_NOP :
break ;
case GX_LOAD_CP_REG : //0x08
{
u32 SubCmd = DataReadU8 ( ) ;
u32 Value = DataReadU32 ( ) ;
2011-02-03 19:55:30 +00:00
SWLoadCPReg ( SubCmd , Value ) ;
2010-06-09 01:37:08 +00:00
}
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
2011-02-03 19:55:30 +00:00
SWLoadIndexedXF ( DataReadU32 ( ) , 0xC ) ;
2010-06-09 01:37:08 +00:00
break ;
case GX_LOAD_INDX_B : //used for normal matrices
2011-02-03 19:55:30 +00:00
SWLoadIndexedXF ( DataReadU32 ( ) , 0xD ) ;
2010-06-09 01:37:08 +00:00
break ;
case GX_LOAD_INDX_C : //used for postmatrices
2011-02-03 19:55:30 +00:00
SWLoadIndexedXF ( DataReadU32 ( ) , 0xE ) ;
2010-06-09 01:37:08 +00:00
break ;
case GX_LOAD_INDX_D : //used for lights
2011-02-03 19:55:30 +00:00
SWLoadIndexedXF ( DataReadU32 ( ) , 0xF ) ;
2010-06-09 01:37:08 +00:00
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
{
u32 cmd = DataReadU32 ( ) ;
2011-02-03 19:55:30 +00:00
SWLoadBPReg ( cmd ) ;
2010-06-09 01:37:08 +00:00
}
break ;
// draw primitives
default :
if ( Cmd & 0x80 )
{
u8 vatIndex = Cmd & GX_VAT_MASK ;
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 ;
2011-02-03 19:55:30 +00:00
INCSTAT ( swstats . thisFrame . numPrimatives ) ;
2010-06-09 01:37:08 +00:00
DEBUG_LOG ( VIDEO , " Draw begin " ) ;
}
else
{
PanicAlert ( " GFX: Unknown Opcode (0x%x). \n " , Cmd ) ;
break ;
}
break ;
}
}
void Init ( )
{
inObjectStream = false ;
lastPrimCmd = 0 ;
ResetDecoding ( ) ;
}
void ResetDecoding ( )
{
currentFunction = DecodeStandard ;
minCommandSize = 1 ;
readOpcode = true ;
}
bool CommandRunnable ( u32 iBufferSize )
{
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 ;
}
void Run ( u32 iBufferSize )
{
currentFunction ( iBufferSize ) ;
}
}