/*
	VIF cursor
	James Russell - SCEE Technology Group

	===================================================
	*Please* send bug reports to James_Russell@scee.net
	If you fix it yourself but don't tell me, your fix
	may be wiped out when you d/l the next release!
	===================================================

	These functions can be used to examine VIF lists. It uses
	the DMA source chain walking library, so it can be used to
	examine VIF lists embedded in DMA Source Chains.

	The most important function in this module is VIF_disassemble().
	This function assumes that the DMA emulator has been set up with DMA_startTransfer(),
	DMA_sceDmaSend() or DMA_sceDmaSendN().
	It will pull words from the DMA using DMA_emulateCycle() for as long as the DMA has
	the ability to supply them. It will disassemble the data, interpreting them as VIF
	codes.

	Version history:

	Ver		Author		Comments
	===		======		========

	2.11	J Russell	03/10/2002 - Removed redundant code in VIF_disassemble
						Made the code more ANSI/Codewarrior friendly.

    2.07    J Russell   20/02/2002 - Fixed bug where VIF cursor would not
                        report failures if it had a problem trying to get the next
                        DMA word

	0.95	J Russell	12/7/2000 - Added ability to disassemble GS packets
						coming through DIRECT/DIRECTHL vifcodes.
						Add colour output support.
	0.8		J Russell	13/6/2000 - Rearchitected system so that it uses the
						new DMA library like a file system. Much more modular.
	0.7		J Russell	1/6/2000 - First release

*/

/*
	Various notes to programmers that are going to hack this for their own porpoises:
	=================================================================================

	1) Parts of the VIF commands and formats have numbers (codes) associated with them.
	These numbers are dictated by the specification of the EE.
	These codes are sometimes large and not consecutive. It's easier to reference/manipulate
	these via an index rather than the code itself. (eg gVifCodeNames[cmdIndex] is
	easier to write than gVifCodeNames[getIndexFromCmd(cmd)])

	So whereever you see 'Index' in a variable, it means an index number from a consective set
	of numbers starting at 0, rather than the actual code itself, which is from a set of numbers
	the same size, but not necessarily in order or starting from 0.

	The enums that are defined in the file _may_ have a suffix of 'c'. This means
	that the actual code is represented. EG VIF_UNPACKc = 0x60. If it doesn't end in 'c',
	then that's the index number, EG VIF_UNPACK = 12, 'cos it's the 13th VIF code.

*/

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "PS2/PS2Strip/DMADebug/VIFCursor.h"

// Stdlib independent.
#include <stdio.h>
#include <string.h>

// Engine independent.
#include "Debugging/Asura_Assert.h"

// PS2Strip.
#include "PS2/PS2Strip/PS2Strip_PS2Stuff.h"

// DMADebug.
#include "PS2/PS2Strip/DMADebug/Debug.h"
#include "PS2/PS2Strip/DMADebug/DMAEmu.h"

// L I N T ///////////////////////////////////////////////////////////////////

// This code does loads of string concatenations in initialisers and I can't
// really be bothered to change it.
//lint -e786

// B E G I N   N A M E S P A C E /////////////////////////////////////////////

namespace DMADebug
{

// T Y P E S /////////////////////////////////////////////////////////////////

struct VIF_REGISTERS
{
	u_int c[4];
	u_int r[4];
	u_int cycle;
	u_int mask;
	u_int mode;
	u_int itop;
	u_int itops;
	u_int mark;
	u_int code;
};

// G L O B A L S /////////////////////////////////////////////////////////////

static bool gDumpVifCodes      = true;
static bool gVerbose           = true;
static bool gDumpVifRegs       = true;
static bool gDumpVifStats      = true;
static bool gDisassembleDirect = true;
static bool	gDisassembleUnpack = true;

static int gDmaWordOffset;		// offset of the current VIF code into the current DMA transfer word
	// -------------
	// VIF REGISTERS
	// -------------

	// ----------- VIF 0 ONLY registers
	// Control registers
//static u_int		v0_stat;
//static u_int		v0_err;
//	tVIF0_FBRST		v0_fbrst;		// not writeable

	// Other registers
//static u_int v0_num;

	// ----------- VIF 1 ONLY registers
	// Control registers
static u_int		v1_stat;
//static u_int		v1_err;
//	tVIF1_FBRST		v1_fbrst;		// not writeable

	// Other registers
static u_int v1_base;
static u_int v1_ofst;
//static u_int v1_top;
static u_int v1_tops;
//static u_int v1_num;

	// ----------- VIF common registers
static int gChanIdx;	// 0 or 1 - indicates which VIF we are decompressing into
static VIF_REGISTERS vr[2];
static VIFCODE gVifcode;		// current VIF code

static int gVifcodeCount[NUMVIFCODES];		// Holds statistics on each vifcode
		// ------------------------

		// Names for all the vifcodes. I put the prefix of 'VIF_' in here because it makes
		// the output easier to interpret, esp. when there are DMA/GIF codes in there too.
const char *gVifCodeNames[] =
{
	"NOP", "STCYCL", "OFFSET", "BASE", "ITOP", "STMOD",
	"MSKPATH3", "MARK", "FLUSHE", "FLUSH", "FLUSHA", "MSCAL",
	"MSCNT", "MSCALF", "STMASK", "STROW", "STCOL", "MPG",
	"DIRECT", "DIRECTHL", "UNPACK"
};

	// Names of all the modes of unpacking VIF data
const char *gUnpackFormatNames[] =
{
	"S_32", "S_16", "S_8",
	"V2_32", "V2_16", "V2_8",
	"V3_32", "V3_16", "V3_8",
	"V4_32", "V4_16", "V4_8", "V4_5"
};

	// Array used to convert from an unpack format's code index into its actual code
static u_int gUnpackFormat_Indx2Code[13] = {
	UNPACK_S_32c, UNPACK_S_16c, UNPACK_S_8c, UNPACK_V2_32c,
	UNPACK_V2_16c, UNPACK_V2_8c, UNPACK_V3_32c, UNPACK_V3_16c,
	UNPACK_V3_8c, UNPACK_V4_32c, UNPACK_V4_16c, UNPACK_V4_8c, UNPACK_V4_5c
};

	// Array used to convert from an vif code's index into its actual code
static u_int gVifCodes_Indx2Code[21] = {
	VIF_NOPc, VIF_STCYCLc, VIF_OFFSETc, VIF_BASEc, VIF_ITOPc, VIF_STMODc,
	VIF_MSKPATH3c, VIF_MARKc, VIF_FLUSHEc, VIF_FLUSHc, VIF_FLUSHAc,
	VIF_MSCALc, VIF_MSCNTc, VIF_MSCALFc, VIF_STMASKc, VIF_STROWc, VIF_STCOLc,
	VIF_MPGc, VIF_DIRECTc, VIF_DIRECTHLc, VIF_UNPACKc
};

// F U N C T I O N   I M P L E M E N T A T I O N S ///////////////////////////

// (MP - 16.11.2002) In the original lib, this is supposed to capture the
// current VIF0 and VIF1 states so it can accurately simulate the DMA
// transfer. We'll just use initial states. This data has come from a mixture
// of checking register state on the PS2 and consulting the (v7) hardware
// manuals.
void VIF_saveVifRegisters( void )
{
//	v0_stat = 0x00000000; //DGET_VIF0_STAT();
//	v0_err  = 0x00000000; //DGET_VIF0_ERR();
//	v0_num  = 0x00000000; //DGET_VIF0_NUM();

	v1_stat = 0x00000040; //DGET_VIF1_STAT();
//	v1_err  = 0x00000000; //DGET_VIF1_ERR();
	v1_base = 0x00000000; //DGET_VIF1_BASE();
	v1_ofst = 0x00000000; //DGET_VIF1_OFST();
//	v1_top  = 0x00000001; //DGET_VIF1_TOP();
	v1_tops = 0x00000000; //DGET_VIF1_TOPS();
//	v1_num  = 0x00000000; //DGET_VIF1_NUM();

    for( u_int u = 0; u < 2; ++u )
    {
        for( u_int v = 0; v < 4; ++v )
        {
            vr[u].c[v] = 0x00000000; //DGET_VIF{0,1}_{C,R}{0,1,2,3}();
        }
        vr[u].cycle = 0x00000404; //DGET_VIF{0,1}_CYCLE();
	    vr[u].mask  = 0x00000000; //DGET_VIF{0,1}_MASK();
	    vr[u].mode  = 0x00000000; //DGET_VIF{0,1}_MODE();
	    vr[u].itop  = 0x00000000; //DGET_VIF{0,1}_ITOP();
	    vr[u].itops = 0x00000000; //DGET_VIF{0,1}_ITOPS();
	    vr[u].mark  = 0x00000000; //DGET_VIF{0,1}_MARK();
	    vr[u].code  = 0x00000000; //DGET_VIF{0,1}_CODE();
    }
}



// ========================
// Adds a string to the error string.
// This is a wrapper for the DEBUG_addError calls, as
// it also adds in extra information about the tag.

static int AddVifError(int errnum, int severity, const char *error)
{
	char err[1024];

	strcpy(err, "VIF: ");
	strcat(err, error);
	return DEBUG_addError(errnum, severity, err);
}











void VIF_parseConfig(const char *cs) {
	int i;

	gDumpVifCodes = true;
	gVerbose = true;
	gDumpVifRegs = true;
	gDumpVifStats = true;
	gDisassembleDirect = true;
	gDisassembleUnpack = true;

	for(i = 0; i < NUMVIFCODES; i++) {
		gVifcodeCount[i] = 0;
	}

	if(cs != NULL) {
		if(strstr(cs, "-novifcodes") != NULL) {
			gDumpVifCodes = false;
		}

		if(strstr(cs, "-novifverbose") != NULL) {
			gVerbose = false;
		}

		if(strstr(cs, "-novifregs") != NULL) {
			gDumpVifRegs = false;
		}

		if(strstr(cs, "-novifstats") != NULL) {
			gDumpVifStats = false;
		}

		if(strstr(cs, "-nodisdirect") != NULL) {
			gDisassembleDirect = false;
		}

		if(strstr(cs, "-nodisunpack") != NULL) {
			gDisassembleUnpack = false;
		}
	}
}

// --------------------------------------------------------------------






		// Display

static int VIF_outputVifRegisters(void) {
	int err = DMA_ERR_NO_ERROR;
	VIF_REGISTERS *v;
	int i;

	v = &vr[gChanIdx];

	err |= DEBUG_addToOutputBuffer("\n==== VIF REGISTERS ====");

	for(i = 0; i < 4; i++) {
		sprintf(gTempString, "COL%d  : " DMADEBUG_FMT_DATA_8HEX, i, v->c[i]);
		err |= DEBUG_addToOutputBuffer(gTempString);
	}
	for(i = 0; i < 4; i++) {
		sprintf(gTempString, "ROW%d  : " DMADEBUG_FMT_DATA_8HEX, i, v->r[i]);
		err |= DEBUG_addToOutputBuffer(gTempString);
	}

	sprintf(gTempString, "CYCLE : " DMADEBUG_FMT_DATA_8HEX, v->cycle);
	err |= DEBUG_addToOutputBuffer(gTempString);

	sprintf(gTempString, "MASK  : " DMADEBUG_FMT_DATA_8HEX, v->mask);
	err |= DEBUG_addToOutputBuffer(gTempString);

	sprintf(gTempString, "MODE  : " DMADEBUG_FMT_DATA_8HEX, v->mode);
	err |= DEBUG_addToOutputBuffer(gTempString);

	sprintf(gTempString, "MARK  : " DMADEBUG_FMT_DATA_8HEX, v->mark);
	err |= DEBUG_addToOutputBuffer(gTempString);

	if(gChanIdx == 1) {
		sprintf(gTempString, "BASE  : " DMADEBUG_FMT_DATA_8HEX, v1_base);
		err |= DEBUG_addToOutputBuffer(gTempString);

		sprintf(gTempString, "OFFSET: " DMADEBUG_FMT_DATA_8HEX, v1_ofst);
		err |= DEBUG_addToOutputBuffer(gTempString);
	}

	return err;
}


	// -----------------------------------------------------------

static int VIF_outputVifStatistics(void) {
	int err = DMA_ERR_NO_ERROR;
	int i;
	u_int totalCodes = 0;

	err |= DEBUG_addToOutputBuffer("\n==== VIF STATISTICS ====");
	err |= DEBUG_addToOutputBuffer("  Codes      # Codes");
	err |= DEBUG_addToOutputBuffer("  -----      -------");

	DMADebug_Printf(("Number of VIF codes: %d\n", NUMVIFCODES));

	for(i = 0; i < NUMVIFCODES; i++) {
		if(gVifcodeCount[i] > 0) {
			sprintf(gTempString, "  %-14s%4d", gVifCodeNames[i],  gVifcodeCount[i]);
			totalCodes += gVifcodeCount[i];
			err |= DEBUG_addToOutputBuffer(gTempString);
		}
	}
	err |= DEBUG_addToOutputBuffer(   "               =====");
	sprintf(gTempString, "  %14s%4d", "", totalCodes);
	err |= DEBUG_addToOutputBuffer(gTempString);
	return err;
}


















// ========================================
// Find the unpack format used in the UNPACK code, and return
// its index. Returns -1 if there was no such format code.
static char getUnpackFormatIndex( u_int format )
{
	format &= 0xf;
	for( int i = 0; i < 13; ++i )
    {
		if(format == gUnpackFormat_Indx2Code[i])
        {
			return( static_cast<char>(i) );
		}
	}
	return -1;
}


// ====================================
// Given the upper 8 bits of the VIFcode, search for the code and
// return its index. Returns -1 if there was no such format code.

static int getCmdIndex(u_int vifcmd) {
	int i;

	vifcmd &= 0x7f;
	if((vifcmd & VIF_UNPACKc) == VIF_UNPACKc) {		// Is UNPACK?
		vifcmd &= VIF_UNPACKc;
	}
	for(i = 0; i < 21; i++) {
		if(vifcmd == gVifCodes_Indx2Code[i]) {
			return i;
		}
	}
	return -1;
}


// =============================================
// Calculates the number of words after the VIFcode that are data
// (includes the VIF code itself) and inserts it into the length
// field of the VIFcode. Returns TRUE if an error occurred.

static int getVifPacketLength( const VIF_REGISTERS* v )
{
	u_int viflen = 0;
	int err = DMA_ERR_NO_ERROR;

	switch(gVifcode.cmdIndex) {
		case VIF_STMASK:
			viflen = 1 + 1;
			break;

		case VIF_STROW:
		case VIF_STCOL:
			viflen = 1 + 4;
			break;

		case VIF_MPG:
			viflen = 1 + gVifcode.num * 2;
			break;

		case VIF_DIRECT:
		case VIF_DIRECTHL:
			viflen = 1 + gVifcode.imm * 4;
			break;

		case VIF_UNPACK:
			if(gVifcode.unpackFormatIndex == -1) {
				// ===== Invalid unpack format
				sprintf(gTempString,
					"Unknown UNPACK format '%x'.", gVifcode.rawCmd & 0xf);
				err |= AddVifError(ERR_VIF_UNKNOWN_UNPACK_FORMAT, DMA_ERR_FATAL_HALT, gTempString);
				viflen = 1;
			} else {
				u_int cl, wl, vl, vn;
				int bytesPerData, numElementsPerData, totalWordsUsed;

				vl = gVifcode.rawCmd & 0x3;
				vn = (gVifcode.rawCmd >> 2) & 0x3;

				if(vl == 0x3) {				// vl == 3 only for V4_5
					bytesPerData = 2;		// 16 bits of RGBA data
					numElementsPerData = 1;
				} else {
					bytesPerData = (4 >> vl);		// 0=>4, 1=>2, 2=>1
					numElementsPerData = vn + 1;
				}

					// the total number of data words reqd for the unpack depends on
					// the current value of WL & CL. If CL >=WL (skipping write), it
					// is very simple to calculate. But if CL < WL (filling write), it
					// is a little more difficult.
				cl = v->cycle & 0xff;
				wl = (v->cycle >> 8) & 0xff;

				if(cl >= wl) {		// skipping write
					totalWordsUsed = bytesPerData * numElementsPerData * gVifcode.num;
				} else {
					int reqdWrites;		// number of qwords we'll be unpacking to from source data (not from internal regs)

					totalWordsUsed = bytesPerData * numElementsPerData;		// bytes required per data

					reqdWrites = cl * (gVifcode.num / wl);
					if((gVifcode.num % wl) > cl) {
						reqdWrites += cl;
					} else {
						reqdWrites += gVifcode.num % wl;
					}

					totalWordsUsed *= reqdWrites;
				}

				totalWordsUsed = (totalWordsUsed + 3) / 4;			// Round, in case there's any padding.

				viflen = 1 + totalWordsUsed;
			}
			break;

		default:
			viflen = 1;
	}
	gVifcode.length = viflen;

	return err;
}









// =====================================================
// Parses and disassembles a VIFCODE structure,
// and adds it to gOutputBuffer
// Verbose makes the disassembly a little clearer.

static int parseVifCode(u_int *pCode, char *s) {
	int err = DMA_ERR_NO_ERROR;
	char bits[20];
	u_int code;
	VIF_REGISTERS *v;

	v = &vr[gChanIdx];


			// ------------ Fill in the VIFCODE structure
	code = *pCode;
	gVifcode.addr = pCode;
	gVifcode.rawCmd = code >> 24;
	gVifcode.interruptBit = (gVifcode.rawCmd & 0x80) != 0;
	gVifcode.num = (code >> 16) & 0xff;
	gVifcode.imm = code & 0xffff;
	gVifcode.cmdIndex = getCmdIndex(gVifcode.rawCmd);
	if(gVifcode.num == 0) {
		gVifcode.num = 256;
	}

	if(gVifcode.cmdIndex == VIF_UNPACK) {
		gVifcode.unpackFormatIndex = getUnpackFormatIndex(gVifcode.rawCmd);
	}
	if((gVifcode.cmdIndex == VIF_DIRECT || gVifcode.cmdIndex == VIF_DIRECTHL) && gVifcode.imm == 0) {
		gVifcode.imm = 65536;
	}

			// ----------------- Output basic info
	DMADebug_Puts("@@@ VIF - decoding VIFcode:");
	DMADebug_Puts((gVifcode.cmdIndex==-1)?DMADEBUG_RTF_COLOUR_ERROR"*UNKNOWN VIF COMMAND*"DMADEBUG_RTF_COLOUR_NORMAL:gVifCodeNames[gVifcode.cmdIndex]);

	DEBUG_addField(DBGMSG_ENTRY_TITLE, s, DMADEBUG_RTF_COLOUR_VIFTITLE "VIF %-9s" DMADEBUG_RTF_COLOUR_NORMAL " " DMADEBUG_FMT_DATASRCADDR,
				(gVifcode.cmdIndex==-1)?DMADEBUG_RTF_COLOUR_ERROR"*UNKNOWN VIF COMMAND*"DMADEBUG_RTF_COLOUR_NORMAL:gVifCodeNames[gVifcode.cmdIndex],
				pCode);
	DEBUG_addField(DBGMSG_ENTRY_FIELD, s, "["DMADEBUG_FMT_MEMCONTENTS_32"]", code);

		// ----------------------- Error checking

			// ---------- Command name
	if(gVifcode.cmdIndex == -1) {
		err |= AddVifError(ERR_VIF_UNKNOWN_VIFCODE, DMA_ERR_FATAL_HALT, "Unknown VIF code");
	} else {
		gVifcodeCount[gVifcode.cmdIndex]++;		// ------ Update the statistics
	}

			// ---------- MPG alignment check
	if(gVifcode.cmdIndex == VIF_MPG && ((u_int)gVifcode.addr & 0x7) != 0x4) {
		err |= AddVifError(ERR_VIF_MPG_ALIGNMENT, DMA_ERR_FATAL_CONT, "MPG data is not on a 64 bit aligned address.");
	}

			// ----------- VIF1-only codes check
	if(gChan != PS2STRIP_DMA_CHAN_ID_VIF1) {
		switch(gVifcode.cmdIndex) {
			case VIF_DIRECT:
			case VIF_DIRECTHL:
			case VIF_FLUSHA:
			case VIF_FLUSH:
			case VIF_MSKPATH3:
			case VIF_BASE:
			case VIF_OFFSET:
            default:
				sprintf(gTempString, "A VIF code was found (%s) that is not valid for this DMA channel.",
					gVifCodeNames[gVifcode.cmdIndex]);
				err |= AddVifError(ERR_VIF_INVALID_VIFCODE, DMA_ERR_FATAL_CONT, gTempString);
				break;
		}
	}

	if(IsFatal(err)) {
		return err;
	}
			// ------------ Calculate length

	err |= getVifPacketLength(v);
	if(IsFatal(err)) {
		return err;
	}


		// ===== Print out any flags (like interrupt, mask, etc) if any are set.
	strcpy(bits,"[");
	if(gVifcode.interruptBit) strcat(bits,"i");
	if(gVifcode.cmdIndex == VIF_UNPACK) {
		if(gVifcode.rawCmd & 0x10) strcat(bits,"m");
		if(gVifcode.imm & 0x8000) strcat(bits,"r");
		if(gVifcode.imm & 0x4000) strcat(bits,"u");
	}
	strcat(bits,"]");
	if(strlen(bits) != 2) {
		DEBUG_addField(DBGMSG_ENTRY_FIELD, s, "Status bits="DMADEBUG_FMT_DATA_STR, bits);
	}


		// ===== Disassemble the command and arguments.
	switch(gVifcode.cmdIndex) {
		case VIF_NOP:
			break;

		case VIF_STCYCL:
			v->cycle = gVifcode.imm & 0xffff;
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_2HEX, "WL", (gVifcode.imm >> 8) & 0xff);
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_2HEX, "CL", gVifcode.imm & 0xff);
			break;

		case VIF_OFFSET:
			v1_ofst = gVifcode.imm & 0x1ff;
			v1_stat = v1_stat & ~(u_int)(1 << 7);	// Clear DBF flag
			v1_tops = v1_base;

			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_4HEX, "OFFSET", gVifcode.imm & 0x1ff);
			break;

		case VIF_BASE:
			v1_base = gVifcode.imm & 0x1ff;

			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_4HEX, "BASE", gVifcode.imm & 0x1ff);

			v1_tops = v1_base;
			if(v1_stat & (1 << 7)) {
				v1_tops += v1_ofst;
			}
			break;

		case VIF_ITOP:
			v->itops = gVifcode.imm & 0x1ff;
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_4HEX, "ITOPS", gVifcode.imm & 0x1ff);
			break;

		case VIF_STMOD:
			{
			const char *modeNames[4] = {
				"No addition processing",
				"Offset Mode (Row+dV->VUMem)",
				"Difference Mode (Row+dV->Row->VUMem)",
				DMADEBUG_RTF_COLOUR_ERROR "*UNDEFINED*" DMADEBUG_RTF_COLOUR_NORMAL
				};

			v->mode = gVifcode.imm & 0x3;
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_STR, "MODE", modeNames[gVifcode.imm & 0x3]);
			}
			break;

		case VIF_MSKPATH3:
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_STR, "Masked", ((gVifcode.imm >> 15) & 1) ? "DISABLE" : "ENABLE");
			break;

		case VIF_MARK:
			v->mark = gVifcode.imm & 0xffff;
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_4HEX, "MARK", gVifcode.imm & 0xffff);
			break;

		case VIF_FLUSHE:
		case VIF_FLUSH:
		case VIF_FLUSHA:
			break;

		case VIF_MSCAL:
		case VIF_MSCALF:
		case VIF_MSCNT:
			v1_stat ^= 1 << 7;		// Flip DBF field
			if(gVifcode.cmdIndex != VIF_MSCNT) {
				DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_4HEX, "VU PC", gVifcode.imm & 0xffff);
			}
			break;

		case VIF_STMASK:		// The output of args for this and the STROW/STCOL codes are handled elsewhere
			break;

		case VIF_STROW:
		case VIF_STCOL:
			break;

		case VIF_MPG:
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_2HEX" instructions", "Len", gVifcode.num);
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_4HEX, "Addr", gVifcode.imm & 0xffff);
			break;

		case VIF_DIRECT:
		case VIF_DIRECTHL:
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_6HEX" qwords", "Len", gVifcode.imm);
			break;

		case VIF_UNPACK:
			{
			int flg =  (gVifcode.imm >> 15) & 0x1;
			int usn = (gVifcode.imm >> 14) & 0x1;
			int addr = (gVifcode.imm & 0x1ff);

			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_DATA_STR, gUnpackFormatNames[gVifcode.unpackFormatIndex]);
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_4HEX, "Addr", addr);
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_2HEX, "Len", gVifcode.num);
			DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_DEC, "USN", usn);

			if(gChanIdx == 1) {
				DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_DEC, "FLG", flg);
			}
			if(gVerbose) {
				if(flg) {
					DEBUG_addField(DBGMSG_ENTRY_FIELD_NL, s, DMADEBUG_RTF_COLOUR_FIELDNAME"VUMem addr" DMADEBUG_RTF_COLOUR_NORMAL ":V1_TOPS(" DMADEBUG_FMT_DATA_4HEX
							")+ADDR(" DMADEBUG_FMT_DATA_4HEX ")=" DMADEBUG_FMT_DATA_4HEX, v1_tops, addr, v1_tops + addr);
				} else {
					DEBUG_addField(DBGMSG_ENTRY_FIELD_NL, s, DMADEBUG_RTF_COLOUR_FIELDNAME"VUMem addr" DMADEBUG_RTF_COLOUR_NORMAL ":" DMADEBUG_FMT_DATA_4HEX, addr);
				}
				DEBUG_addField(DBGMSG_ENTRY_FIELD, s, "Len:"DMADEBUG_FMT_DATA_4HEX" qwords" , gVifcode.num);
			}
			}
			break;

		default:
			Asura_Assert( false, "Logic error, this VIFcode should be caught." );
            break;
	}

	if(gChanIdx == 1) {
		v1_tops = v1_base;
		if(v1_stat & (1<<7)) {		// if dbf flag set
			v1_tops += v1_ofst;
		}
	}

		// ===== Print out the length of the VIFcode + its data.

	if(gVerbose) {
		DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_DEC" word%s", "Code+data", gVifcode.length, (gVifcode.length>1)?"s":"");
	}

	return err;
}







	// ===============================================================================

static u_int gUnpackedQWord[4];
static bool gUnpackedQWord_write[4];		// whether or not this element has been written to

	// ========================

static int gUnpackWordOffset; // This is the field offset within the current word within the
								// current DMA transfer word.

		// Grabs 1 element (which may be a subset of a word) for an UNPACK. All UNPACKS require
		// at least 1 element, at most 4. Each element can be 8/16/32 bits depending on format
static int grabUnpackElement(int vl, int usn, u_int *result) {
	int elementsPerWord = (1 << vl);
	int err = DMA_ERR_NO_ERROR;

	DMADebug_Puts("-->Entering grabUnpackElement");
		// gUnpackWordOffset is in units of elements
		// It should be reset to 0 at the start of every UNPACK.

		// First, guarantee that we are reading from a valid DMA qword
	if(gDmaWordOffset >= 4) {					// grab another qword if necessary
		DMADebug_Puts("-->Need new DMA word to get element");

		err |= DMA_emulateDMACycle();
		if(IsFatal(err) || gDmaTransferQWord == NULL) {
			err |= AddVifError(ERR_VIF_CURSOR_AT_END, DMA_ERR_FATAL_HALT,
				"DMA prematurely ended when UNPACKing data.");
		}
		gDmaWordOffset = (gDmaTransferQWordIsTag) ? 2 : 0;
	}

	if(IsFatal(err)) {
		return err;
	}
	*result = gDmaTransferQWord[gDmaWordOffset];
//printf("source data is %08x\n", *result);
	switch(vl) {
		case 0:	// 32 bit;
					// do nothing
			break;
		case 1: // 16 bit
			*result >>= (16 * gUnpackWordOffset);
			*result &= 0x0000ffff;
			if(usn == 0 && (*result & 0x8000)) {	// sign extend
				*result |= 0xffff0000;
			}
			break;
		case 2: // 8 bit
			*result >>= (8 * gUnpackWordOffset);
			*result &= 0x000000ff;
			if(usn == 0 && (*result & 0x80)) {	// sign extend
				*result |= 0xffffff00;
			}
			break;
		default:
			Asura_Assert( false, "LOGIC ERROR - This case should never be reached (grabUnpackElement)" );
			break;
	}

	gUnpackWordOffset++;

	if(gUnpackWordOffset >= elementsPerWord) {		// if we need to grab a new word.
		DMADebug_Puts("-->Need new word to get next element");

		gVifcode.length--;

		gUnpackWordOffset = 0;
		gDmaWordOffset++;
	}

	DMADebug_Puts("<--Leaving grabUnpackElement");
	
	return err;
}



		// Unpacks a single quadword for output. Will gather the elements from the DMA stream
		// that it requires for the unpack

static int unpackSingleQWord(void) {
	int err = DMA_ERR_NO_ERROR;
	int usn = (gVifcode.imm >> 14) & 1;
	int i;
	int vl = gVifcode.rawCmd & 0x3;
	int vn = (gVifcode.rawCmd >> 2) & 0x3;

	u_int elements[4];			// there can only ever be min 1, max 4 elements in a word.

		// Retrieve all the elements required to unpack this VIF code.
	if(gVifcode.unpackFormatIndex == UNPACK_V4_5) {	// RGB format - special case
		vn = 0;		// this is a special case hack to get the 16 bits reqd for the RGBA
		vl = 1;
	}
	for(i = 0; i < vn + 1; i++) {
		err |= grabUnpackElement(vl, usn, &elements[i]);
		if(IsFatal(err)) {
			err |= AddVifError(ERR_VIF_CURSOR_AT_END, DMA_ERR_FATAL_HALT,
				"Error obtaining next field for UNPACK");
			return err;
		}
	}

	gUnpackedQWord_write[0] =			// flags set if we wrote to this part of the qword or not.
	gUnpackedQWord_write[1] =
	gUnpackedQWord_write[2] =
	gUnpackedQWord_write[3] = true;

	if(gVifcode.unpackFormatIndex == UNPACK_V4_5) {	// RGB format
		gUnpackedQWord[0] = ((elements[0]) & 0x1f) << 3;
		gUnpackedQWord[1] = ((elements[0] >> 5) & 0x1f) << 3;
		gUnpackedQWord[2] = ((elements[0] >> 10) & 0x1f) << 3;
		gUnpackedQWord[3] = ((elements[0] >> 15) & 0x1) << 7;
	} else if (vn == 0) {		// S formats
		gUnpackedQWord[0] =
		gUnpackedQWord[1] =
		gUnpackedQWord[2] =
		gUnpackedQWord[3] = elements[0];
	} else {		// All other formats
		for(i = 0; i < 4; i++) {
			if(i < vn + 1) {
				gUnpackedQWord[i] = elements[i];
			} else {
				gUnpackedQWord_write[i] = false;
			}
		}
	}
	return err;
}


// ----------------------------------------------------------
// Emulates writing a quadword to VU memory.

static int writeQuadword(u_int addr, u_int maskbits, bool flg, u_int writeCycle) {
	int err = DMA_ERR_NO_ERROR;
	int i;
	int mode = vr[gChanIdx].mode & 0x3;
	char *s;
	u_int val;
	u_int address_mask;

	if(flg && gChanIdx != 1) {
		err |= AddVifError(ERR_VIF_INVALID_CHANNEL, DMA_ERR_FATAL_HALT, "FLG flag is set on a VIF0 channel!");
		return err;
	}

	address_mask = (gChan == PS2STRIP_DMA_CHAN_ID_VIF0) ? 0x0ff : 0x3ff;

	if(addr > address_mask) {
		err |= AddVifError(WARN_VIF_INVALID_ADDRESS, DMA_ERR_WARNING,
				"Attempting to write to invalid VU address! (will wrap around)");

		addr &= address_mask;
	}

	if(writeCycle > 3) {				// Note to self - CHECK THIS actually happens
		writeCycle = 3;
	}

	s = gTempString;
	if(flg) {
		int wrapped_addr = (v1_tops + addr) & address_mask;
		s += sprintf(s, DMADEBUG_FMT_INDENT " TOPS("DMADEBUG_RTF_COLOUR_ADDRESS"%04x"DMADEBUG_RTF_COLOUR_NORMAL")+"DMADEBUG_RTF_COLOUR_ADDRESS"%04x"DMADEBUG_RTF_COLOUR_NORMAL"=%04x : [",
				v1_tops, addr, wrapped_addr );
		addr = wrapped_addr;
	} else {
		s += sprintf(s, DMADEBUG_FMT_INDENT DMADEBUG_RTF_COLOUR_ADDRESS" %04x"DMADEBUG_RTF_COLOUR_NORMAL " : [", addr);
	}

	for(i = 0; i < 4; i++) {
		s += sprintf(s, DMADEBUG_RTF_COLOUR_FIELDNAME"%c"DMADEBUG_RTF_COLOUR_NORMAL"=", "XYZW"[i]);
		switch(maskbits & 0x3) {
			case 0:		// Nothing is done
				if(gVifcode.unpackFormatIndex == UNPACK_V4_5) { // RGB format, ignores MODE.
					mode = 0;
				}
				switch(mode) {			// normal, addition decompression, etc
					case 0:			// nothing changes the input
						if( !gUnpackedQWord_write[i] ) {
							err |= AddVifError(WARN_VIF_INDETERMINATE_WRITE, DMA_ERR_WARNING,
								"You are writing an indeterminant value to an unmasked field!");
							val = 0;
							s += sprintf(s, DMADEBUG_RTF_COLOUR_ERROR"INDETERMINATE"DMADEBUG_RTF_COLOUR_NORMAL);
						} else {
							val = gUnpackedQWord[i];
							s += sprintf(s, DMADEBUG_FMT_DATA_8HEX, val);
						}
						break;

					case 1:				// offset mode (Row + dV -> VU Mem)
						val = gUnpackedQWord[i] + vr[gChanIdx].r[i];
						s += sprintf(s, "[+R%d]="DMADEBUG_FMT_DATA_8HEX, i, val);
						break;

					case 2:				// Difference mode (Row + dV -> Row -> VU Mem)
						vr[gChanIdx].r[i] = gUnpackedQWord[i] + vr[gChanIdx].r[i];
						val = vr[gChanIdx].r[i];
						s += sprintf(s, "[+=R%d]="DMADEBUG_FMT_DATA_8HEX, i, val);
						break;

					default:
						err |=  AddVifError(ERR_VIF_INVALID_MODE, DMA_ERR_FATAL_HALT,
							"MODE register is set to 3, which is 'undefined'");
						s += sprintf(s, "INVALID - MODE INCORRECT");
						break;
				}
				break;

			case 1:		// write row register
				val = vr[gChanIdx].r[i];
				s += sprintf(s, "R%d:"DMADEBUG_FMT_DATA_8HEX, i, val);
				break;

			case 2:		// Write column register
				val = vr[gChanIdx].c[writeCycle];
				s += sprintf(s, "C%d:"DMADEBUG_FMT_DATA_8HEX, writeCycle, val);
				break;

			case 3:		// Write is masked
				s += sprintf(s, DMADEBUG_RTF_COLOUR_BG_RED DMADEBUG_RTF_COLOUR_FG_BLACK "**MASKED** "DMADEBUG_RTF_COLOUR_NORMAL);
				break;
		}
		if(i != 3) {
			s += sprintf(s, ".");
		}
		maskbits >>= 2;
	}
	strcat(s, "]");
	err |= DEBUG_addToOutputBuffer(gTempString);
	return err;
}



// -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	// This would normally be part of VIF_disassemble, but it's too big and bulky to fit into
	// that function, so it's on its own.
	//
	// It disassembles an UNPACK, assuming that the VIFcode structure has already been parsed

static int handleUnpack(char *s) {
	u_int wl = (vr[gChanIdx].cycle >> 8) & 0xff;
	u_int cl = (vr[gChanIdx].cycle) & 0xff;
	u_int clCounter, wlCounter;
	u_int writeAddr;
	u_int maskbits;
	bool flg = (gVifcode.imm >> 15) & 1;
	int err = DMA_ERR_NO_ERROR;

			// This is the offset into the current word of the data to extract. (measured in units, so for X_32 it will
			// always be 0, for X_16 it will be 0 or 1, and for X_8 it will be 0, 1, 2, or 3
	gUnpackWordOffset = 0;

	wlCounter = 0;						// Note to self - does doing an unpack necessarily reset these values? Check.
	clCounter = 0;
	writeAddr = gVifcode.imm & 0x1ff;		// This value is in VU address unit (i.e 0 is first qword, 1 is 2nd qword, etc)

	if(cl >= wl && wl == 0) {
		err |= AddVifError(ERR_VIF_INVALID_CYCLE, DMA_ERR_FATAL_HALT,
			"CL >= WL (Skipping write), but WL == 0! WL must be > 0");
	}

	DEBUG_addField(DBGMSG_ENTRY_FIELD_NL, s, "Using: "DMADEBUG_FMT_NAME_DATA_2HEX, "WL", wl);
	DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_2HEX, "CL", cl);
	DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_RTF_COLOUR_FIELDNAME "Mask is" DMADEBUG_RTF_COLOUR_NORMAL " "DMADEBUG_FMT_DATA_STR, (gVifcode.rawCmd & 0x10) ? "Enabled":"Disabled");
	if(gVifcode.rawCmd & 0x10) {		// Only display current value of MASK if it's enabled
		DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_8HEX, "MASK", vr[gChanIdx].mask);
	}
	DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_DEC, "MODE", vr[gChanIdx].mode & 0x3);

	DEBUG_addToOutputBuffer(s);
	s[0] = 0;

			// Now begin actually disassembling the UNPACK

	while(!IsFatal(err) && gVifcode.num > 0) {
		gUnpackedQWord_write[0] =			// flags set if we wrote to this part of the qword or not.
		gUnpackedQWord_write[1] =			// start off as all false
		gUnpackedQWord_write[2] =
		gUnpackedQWord_write[3] = false;

		if(gVifcode.rawCmd & 0x10) {	// If 'm' bit is set
				// Note to self - another thing to test - what happens to wlcounter when it goes above 3, considering
				// WL and CL are both 8 bit fields? Does it wrap around?
			maskbits = vr[gChanIdx].mask >> (8 * ((wlCounter > 3) ? 3 : wlCounter));
		} else {
			maskbits = 0;
		}

		if(cl >= wl) {		// Skipping write
			if(wlCounter < wl) {
				err |= unpackSingleQWord();		// Retrieve and unpack a word
				if(IsFatal(err)) break;
				err |= writeQuadword(writeAddr, maskbits, flg, wlCounter);
				gVifcode.num--;
			}

		} else {		// cl < wl - Filling write
			if(clCounter < cl) {
				err |= unpackSingleQWord();
				if(IsFatal(err)) break;
			}
			err |= writeQuadword(writeAddr, maskbits, flg, wlCounter);
			gVifcode.num--;

		}
		writeAddr++;
		wlCounter++;
		clCounter++;

		if((cl >= wl && clCounter >= cl) // skipping write
				|| (cl < wl && wlCounter >= wl)) {		// filling write
			clCounter = 0;
			wlCounter = 0;
		}
	}

	if(gUnpackWordOffset != 0 && gVifcode.length == 1) {
			// Skips past any padding
		gVifcode.length--;
		gDmaWordOffset++;
	}
	return err;
}


	// ===============================================================================
		// This function should ONLY be used by VIF_disassemble - do not use it elsewhere!
static int getNewDmaWord(void) {
	int dmaerr = DMA_ERR_NO_ERROR;

	dmaerr |= DMA_emulateDMACycle();
	if(gDmaTransferQWord == NULL) {
		if(gVifcode.length > 0) {
			dmaerr |= AddVifError(ERR_VIF_CURSOR_AT_END, DMA_ERR_FATAL_HALT,
				"VIF tried to obtain more DMA data for decoding, but DMA has ended!");
		}
	}
	gDmaWordOffset = (gDmaTransferQWordIsTag) ? 2 : 0;
	return dmaerr;
}

			// ====================================
			// This function assumes that the DMA emulator has been set up with DMA_startTransfer.
			// It will pull words from the DMA as long as the DMA has the ability to supply them,
			// and disassemble them.

int VIF_disassemble(void) {
	int err = DMA_ERR_NO_ERROR, i;
	char s[1024];
	unsigned __int64 opcode;
	u_int mask;


			// ====================================
			// This function assumes that the DMA emulator has been set up with DMA_startTransfer.
			// It will pull words from the DMA as long as the DMA has the ability to supply them,
			// and disassemble them.

	DMADebug_Puts("--> Entering VIF_disassemble");
	gVifcode.length = 0;
	gDmaWordOffset = 4;		// Implies that we haven't transferred any DMA words yet (valid vals are 0-3)

	for(i = 0; i < NUMVIFCODES; i++) {		// Reset statistics counters
		gVifcodeCount[i] = 0;
	}

	if(gChan != PS2STRIP_DMA_CHAN_ID_VIF0  &&  gChan != PS2STRIP_DMA_CHAN_ID_VIF1) {
		err |= AddVifError(ERR_VIF_INVALID_CHANNEL, DMA_ERR_FATAL_HALT,
			"Cannot examine VIF packets on a non VIF DMA channel (this is not PS2STRIP_DMA_CHAN_ID_VIF0 or PS2STRIP_DMA_CHAN_ID_VIF1!!)");
		return err;
	}

	gChanIdx = (gChan == PS2STRIP_DMA_CHAN_ID_VIF0)? 0 : 1;

	do {
		if(gDmaWordOffset >= 4) {		// i.e., if we need to poll the DMA for a new qword
			DMADebug_Puts("Getting new Quad word for disassemble");
			err |= getNewDmaWord();
			if(IsFatal(err)) {
				DMADebug_Puts("new Quad word for disassemble failed!");
				break;
			}
			DMADebug_Printf(("New QWord is at %08x\n", (u_int)gDmaTransferQWord));
			if(gDmaTransferQWord == NULL) {			// DMA transfer is complete/terminated
				break;
			}
		}

		s[0] = 0;		// clear output string

					// Disassemble VIF code
		err |= parseVifCode(&gDmaTransferQWord[gDmaWordOffset], s);
		if(IsFatal(err)) {
			DMADebug_Puts("error in new Vif code parsed");
			DEBUG_addToOutputBuffer(s);
			break;
		}

		gDmaWordOffset++;
		gVifcode.length--;
				// These are all the codes that may have data following them (MPG, UNPACK, STROW/COL etc)
		switch(gVifcode.cmdIndex) {
			case VIF_MPG:
				DMADebug_Puts("decoding MPG packet");
				if((gDmaWordOffset & 1) != 0) {
			        err |= AddVifError(ERR_VIF_MPG_ALIGNMENT, DMA_ERR_FATAL_CONT,
							"MPG data is not on a 64 bit aligned address.");
					break;
				}

				DEBUG_addField(DBGMSG_ENTRY_FIELD_NL, s, "[Skipping " DMADEBUG_FMT_DATA_DEC " words]", gVifcode.length);

				while(gVifcode.length > 0) {
					if(gDmaWordOffset == 4) {
						err |= getNewDmaWord();
						if(IsFatal(err) || gDmaTransferQWord == NULL) {
							err |= AddVifError(ERR_VIF_MPG_INCOMPLETE, DMA_ERR_FATAL_HALT,
									"Error obtaining more data for MPG VIFcode. Premature DMA termination?");
							break;
						}
					}
					opcode = gDmaTransferQWord[gDmaWordOffset];
					opcode = (opcode << 32) | gDmaTransferQWord[gDmaWordOffset+1];
					gDmaWordOffset += 2;
					gVifcode.length -=2;
				}
				break;

			case VIF_UNPACK:
				DMADebug_Puts("decoding UNPACK packet");

				if(gDisassembleUnpack) {
					err |= handleUnpack(s);
					if(gVifcode.length > 0) {
				        err |= AddVifError(ERR_VIF_UNPACK_TOO_SHORT, DMA_ERR_FATAL_HALT,
							"UNPACK Error - unpack did not complete. Premature DMA termination?");
					}
				} else {
					DEBUG_addToOutputBuffer(s);
					s[0] = 0;
					DMADebug_Puts("Skipping UNPACK data (not disassembling)");
					while(gVifcode.length > 0) {
						if(gDmaWordOffset == 4) {
							err |= getNewDmaWord();
							if(IsFatal(err) || gDmaTransferQWord == NULL) {
								break;
							}
						}
						gVifcode.length--;
						gDmaWordOffset++;
					}
				}
				if(IsFatal(err)) {
					break;
				}
				break;

			case VIF_STROW:
			case VIF_STCOL:
				DMADebug_Puts("decoding STROW/COL packet");
				for(i = 0; i < 4; i++) {
					if(gDmaWordOffset == 4) {
						err |= getNewDmaWord();
						if(IsFatal(err) || gDmaTransferQWord == NULL) {
							err |= AddVifError(ERR_VIF_STROWCOL_INCOMPLETE, DMA_ERR_FATAL_HALT,
									(gVifcode.cmdIndex == VIF_STROW) ? 
										"Error obtaining more data for STROW VIFcode. Premature DMA termination?" :
										"Error obtaining more data for STCOL VIFcode. Premature DMA termination?");
						}
					}
					if(!IsFatal(err)) {
							// Set the row/column registers accordingly
						if(gVifcode.cmdIndex == VIF_STROW) {
							vr[gChanIdx].r[i] = gDmaTransferQWord[gDmaWordOffset];
						} else {
							vr[gChanIdx].c[i] = gDmaTransferQWord[gDmaWordOffset];
						}

						sprintf(gTempString, "%f", *(float*)&gDmaTransferQWord[gDmaWordOffset]);
						DEBUG_addField(DBGMSG_ENTRY_FIELD_NL, s, DMADEBUG_RTF_COLOUR_FIELDNAME "%c%d" DMADEBUG_RTF_COLOUR_NORMAL " :"DMADEBUG_FMT_DATA_8HEX" ("DMADEBUG_FMT_DATA_FLOAT")",
										(gVifcode.cmdIndex == VIF_STROW) ? 'R' : 'C',
										i,
										gDmaTransferQWord[gDmaWordOffset],
										gTempString);
						gDmaWordOffset++;
						gVifcode.length--;
					}
				}
				break;

			case VIF_DIRECT:
			case VIF_DIRECTHL:
				DMADebug_Puts("decoding DIRECT/DIRECTHL packet");
				DEBUG_addToOutputBuffer(s);
				s[0] = 0;

				if(gDmaWordOffset != 4) {
					err |= AddVifError(ERR_GIF_LIST_ALIGNMENT, DMA_ERR_FATAL_HALT,
							"GIF tag is not aligned to a quadword boundary!");
				}
				Asura_Assert( !(gVifcode.length & 0x3), "Logic error! length for DIRECT/HL should be a multiple of 4!!" );

				while(gVifcode.length > 0 && !IsFatal(err)) {
					err |= getNewDmaWord();
					if(IsFatal(err) || gDmaTransferQWord == NULL) {
						break;
					}
					if(gDisassembleDirect) {
						gGifInput = gDmaTransferQWord;
						err |= GIF_emulateGifCycle();
					}
					gDmaWordOffset = 4;		// will force getting a new DMA transfer word.
					gVifcode.length -=4;
				}
				if(gDisassembleDirect && !IsFatal(err) && !GIF_isIdle()) {
					err |= AddVifError(WARN_VIF_DIRECT_INCOMPLETE, DMA_ERR_WARNING,
							"DIRECT/DIRECTHL ended, but GIF is expecting more data.");
				}
				break;

			case VIF_STMASK:
				DMADebug_Puts("decoding STMASK packet");
				if(gDmaWordOffset == 4) {
					err |= getNewDmaWord();
					if(IsFatal(err) || gDmaTransferQWord == NULL) {
						err |= AddVifError(ERR_VIF_STMASK_INCOMPLETE, DMA_ERR_FATAL_HALT,
								"Error obtaining more data for STMASK VIFcode. Premature DMA termination?");
					}
				}
				if(!IsFatal(err)) {
					mask = gDmaTransferQWord[gDmaWordOffset];
					vr[gChanIdx].mask = mask;
					DEBUG_addField(DBGMSG_ENTRY_FIELD, s, DMADEBUG_FMT_NAME_DATA_8HEX, "MASK", mask);

					gDmaWordOffset++;
					gVifcode.length--;
				}
				break;
		}

		if(gDumpVifCodes || IsFatal(err)) {
			if(strlen(s) > 0) {
				DEBUG_addToOutputBuffer(s);
			}
		}

		if(err == DMA_ERR_NO_ERROR) {
			Asura_Assert( !gVifcode.length, "Logic error, length should be zero when exiting this switch statement." );
		}
	} while(!IsFatal(err));
	
	if(gDumpVifRegs) {
		err |= VIF_outputVifRegisters();
	}

	if(gDumpVifStats) {
		err |= VIF_outputVifStatistics();
	}

	DMADebug_Puts("<---Leaving VIF_disassemble");
	return err;
}



			// This function is the same as the one above (it disassembles a VIF list, pulling data from DMA)
			// but sets up the transfer using the same args as used for sceDmaSend()

int VIF_disassembleDmaList( PS2STRIP_DMA_CHAN_ID eChanID, void* tag )
{
	int err = DMA_ERR_NO_ERROR;
	DMADebug_Puts("--->Entering VIF_disassemble");

	err = DMA_sceDmaSend( eChanID, tag );
	
	if(IsFatal(err))
    {
		Asura_Assert( false, "Problem opening DMA for VIF Disassemble." );
//		Exit(1);
	}

	err = VIF_disassemble();

	DMADebug_Puts("<---Leaving VIF_disassemble");
	return err;
}

			// As above, but for sceDmaSendN.

int VIF_disassembleDmaListN( PS2STRIP_DMA_CHAN_ID eChanID, void* tag, int numqwords )
{
	int err = DMA_ERR_NO_ERROR;

	DMADebug_Puts("--->Entering VIF_disassembleN");
	err |= DMA_sceDmaSendN( eChanID, tag, numqwords );

	if(IsFatal(err))
    {
		Asura_Assert( false, "Problem opening DMA for VIF Disassemble." );
//		Exit(1);
	}

	err = VIF_disassemble();

	DMADebug_Puts("<---Leaving VIF_disassembleN");
	return err;
}

// E N D   N A M E S P A C E /////////////////////////////////////////////////

}
