/*
    Nintendo/SGI Reality Signal Processor (RSP) emulator

    Written by Ville Linde
*/

#include "cpuintrf.h"
#include "rsp.h"
#include "debugger.h"
#include <math.h>	// sqrt

#define LOG_INSTRUCTION_EXECUTION		0
#define SAVE_DISASM						0
#define SAVE_DMEM						0

#define PRINT_VECREG(x)		mame_printf_debug("V%d: %04X|%04X|%04X|%04X|%04X|%04X|%04X|%04X\n", (x), \
							(UINT16)VREG_S((x),0), (UINT16)VREG_S((x),1), \
							(UINT16)VREG_S((x),2), (UINT16)VREG_S((x),3), \
							(UINT16)VREG_S((x),4), (UINT16)VREG_S((x),5), \
							(UINT16)VREG_S((x),6), (UINT16)VREG_S((x),7))


extern offs_t rsp_dasm_one(char *buffer, offs_t pc, UINT32 op);

#if LOG_INSTRUCTION_EXECUTION
static FILE *exec_output;
#endif


extern UINT32 sp_read_reg(UINT32 reg);
extern void sp_write_reg(UINT32 reg, UINT32 data);
extern READ32_HANDLER( n64_dp_reg_r );
extern WRITE32_HANDLER( n64_dp_reg_w );
extern void sp_set_status(UINT32 status);


enum
{
	RSP_PC = 1,
	RSP_R0,
	RSP_R1,
	RSP_R2,
	RSP_R3,
	RSP_R4,
	RSP_R5,
	RSP_R6,
	RSP_R7,
	RSP_R8,
	RSP_R9,
	RSP_R10,
	RSP_R11,
	RSP_R12,
	RSP_R13,
	RSP_R14,
	RSP_R15,
	RSP_R16,
	RSP_R17,
	RSP_R18,
	RSP_R19,
	RSP_R20,
	RSP_R21,
	RSP_R22,
	RSP_R23,
	RSP_R24,
	RSP_R25,
	RSP_R26,
	RSP_R27,
	RSP_R28,
	RSP_R29,
	RSP_R30,
	RSP_R31,
};

typedef union
{
	UINT64 d[2];
	UINT32 l[4];
	INT16 s[8];
	UINT8 b[16];
} VECTOR_REG;

typedef union
{
	INT64 q;
	INT16 w[4];
} ACCUMULATOR_REG;

typedef struct
{
	UINT32 pc;
	UINT32 r[32];
	VECTOR_REG v[32];
	UINT16 flag[4];

	ACCUMULATOR_REG accum[8];
	INT32 square_root_res;
	INT32 square_root_high;
	INT32 reciprocal_res;
	INT32 reciprocal_high;

	UINT32 ppc;
	UINT32 nextpc;

	int (*irq_callback)(int irqline);
} RSP_REGS;



#define RSREG		((op >> 21) & 0x1f)
#define RTREG		((op >> 16) & 0x1f)
#define RDREG		((op >> 11) & 0x1f)
#define SHIFT		((op >> 6) & 0x1f)

#define RSVAL		(rsp.r[RSREG])
#define RTVAL		(rsp.r[RTREG])
#define RDVAL		(rsp.r[RDREG])

#define SIMM16		((INT32)(INT16)(op))
#define UIMM16		((UINT16)(op))
#define UIMM26		(op & 0x03ffffff)


#define JUMP_ABS(addr)			{ rsp.nextpc = 0x04001000 | (((addr) << 2) & 0xfff); }
#define JUMP_ABS_L(addr,l)		{ rsp.nextpc = 0x04001000 | (((addr) << 2) & 0xfff); rsp.r[l] = rsp.pc + 4; }
#define JUMP_REL(offset)		{ rsp.nextpc = 0x04001000 | ((rsp.pc + ((offset) << 2)) & 0xfff); }
#define JUMP_REL_L(offset,l)	{ rsp.nextpc = 0x04001000 | ((rsp.pc + ((offset) << 2)) & 0xfff); rsp.r[l] = rsp.pc + 4; }
#define JUMP_PC(addr)			{ rsp.nextpc = 0x04001000 | ((addr) & 0xfff); }
#define JUMP_PC_L(addr,l)		{ rsp.nextpc = 0x04001000 | ((addr) & 0xfff); rsp.r[l] = rsp.pc + 4; }


#define VDREG		((op >> 6) & 0x1f)
#define VS1REG		((op >> 11) & 0x1f)
#define VS2REG		((op >> 16) & 0x1f)
#define EL			((op >> 21) & 0xf)

#define VREG_B(reg, offset)		rsp.v[(reg)].b[(15 - (offset))]
#define VREG_S(reg, offset)		rsp.v[(reg)].s[(7 - (offset))]
#define VREG_L(reg, offset)		rsp.v[(reg)].l[(3 - (offset))]

#define VEC_EL_1(x,z)		(vector_elements_1[(x)][(z)])
#define VEC_EL_2(x,z)		(vector_elements_2[(x)][(z)])

#define ACCUM(x)		rsp.accum[(7-(x))].q
#define ACCUM_H(x)		rsp.accum[(7-(x))].w[3]
#define ACCUM_M(x)		rsp.accum[(7-(x))].w[2]
#define ACCUM_L(x)		rsp.accum[(7-(x))].w[1]

#define CARRY_FLAG(x)			((rsp.flag[0] & (1 << ((x)))) ? 1 : 0)
#define CLEAR_CARRY_FLAGS()		{ rsp.flag[0] &= ~0xff; }
#define SET_CARRY_FLAG(x)		{ rsp.flag[0] |= (1 << ((x))); }
#define CLEAR_CARRY_FLAG(x)		{ rsp.flag[0] &= ~(1 << ((x))); }

#define COMPARE_FLAG(x)			((rsp.flag[1] & (1 << ((x)))) ? 1 : 0)
#define CLEAR_COMPARE_FLAGS()	{ rsp.flag[1] &= ~0xff; }
#define SET_COMPARE_FLAG(x)		{ rsp.flag[1] |= (1 << ((x))); }
#define CLEAR_COMPARE_FLAG(x)	{ rsp.flag[1] &= ~(1 << ((x))); }

#define ZERO_FLAG(x)			((rsp.flag[0] & (1 << (8+(x)))) ? 1 : 0)
#define CLEAR_ZERO_FLAGS()		{ rsp.flag[0] &= ~0xff00; }
#define SET_ZERO_FLAG(x)		{ rsp.flag[0] |= (1 << (8+(x))); }
#define CLEAR_ZERO_FLAG(x)		{ rsp.flag[0] &= ~(1 << (8+(x))); }



static RSP_REGS rsp;
static int rsp_icount;

#define ROPCODE(pc)		cpu_readop32(pc)

INLINE UINT8 READ8(UINT32 address)
{
	address = 0x04000000 | (address & 0xfff);
	return program_read_byte_32be(address);
}

INLINE UINT16 READ16(UINT32 address)
{
	address = 0x04000000 | (address & 0xfff);

	if (address & 1)
	{
		//osd_die("RSP: READ16: unaligned %08X at %08X\n", address, rsp.ppc);
		return ((program_read_byte_32be(address+0) & 0xff) << 8) | (program_read_byte_32be(address+1) & 0xff);
	}

	return program_read_word_32be(address);
}

INLINE UINT32 READ32(UINT32 address)
{
	address = 0x04000000 | (address & 0xfff);

	if (address & 3)
	{
		//osd_die("RSP: READ32: unaligned %08X at %08X\n", address, rsp.ppc);
		return ((program_read_byte_32be(address + 0) & 0xff) << 24) |
			   ((program_read_byte_32be(address + 1) & 0xff) << 16) |
			   ((program_read_byte_32be(address + 2) & 0xff) << 8) |
			   ((program_read_byte_32be(address + 3) & 0xff) << 0);
	}

	return program_read_dword_32be(address);
}

INLINE void WRITE8(UINT32 address, UINT8 data)
{
	address = 0x04000000 | (address & 0xfff);
	program_write_byte_32be(address, data);
}

INLINE void WRITE16(UINT32 address, UINT16 data)
{
	address = 0x04000000 | (address & 0xfff);

	if (address & 1)
	{
		//fatalerror("RSP: WRITE16: unaligned %08X, %04X at %08X\n", address, data, rsp.ppc);
		program_write_byte_32be(address + 0, (data >> 8) & 0xff);
		program_write_byte_32be(address + 1, (data >> 0) & 0xff);
		return;
	}

	program_write_word_32be(address, data);
}

INLINE void WRITE32(UINT32 address, UINT32 data)
{
	address = 0x04000000 | (address & 0xfff);

	if (address & 3)
	{
		//osd_die("RSP: WRITE32: unaligned %08X, %08X at %08X\n", address, data, rsp.ppc);
		program_write_byte_32be(address + 0, (data >> 24) & 0xff);
		program_write_byte_32be(address + 1, (data >> 16) & 0xff);
		program_write_byte_32be(address + 2, (data >> 8) & 0xff);
		program_write_byte_32be(address + 3, (data >> 0) & 0xff);
		return;
	}

	program_write_dword_32be(address, data);
}

/*****************************************************************************/

static UINT32 get_cop0_reg(int reg)
{
	if (reg >= 0 && reg < 8)
	{
		return sp_read_reg(reg);
	}
	else if (reg >= 8 && reg < 16)
	{
		return n64_dp_reg_r(reg - 8, 0x00000000);
	}
	else
	{
		fatalerror("RSP: get_cop0_reg: %d", reg);
	}
}

static void set_cop0_reg(int reg, UINT32 data)
{
	if (reg >= 0 && reg < 8)
	{
		sp_write_reg(reg, data);
	}
	else if (reg >= 8 && reg < 16)
	{
		n64_dp_reg_w(reg - 8, data, 0x00000000);
	}
	else
	{
		fatalerror("RSP: set_cop0_reg: %d, %08X\n", reg, data);
	}
}

static void unimplemented_opcode(UINT32 op)
{
#ifdef MAME_DEBUG
	char string[200];
	rsp_dasm_one(string, rsp.ppc, op);
	mame_printf_debug("%08X: %s\n", rsp.ppc, string);
#endif

#if SAVE_DISASM
	{
		char string[200];
		int i;
		FILE *dasm;
		dasm = fopen("rsp_disasm.txt", "wt");

		for (i=0; i < 0x1000; i+=4)
		{
			UINT32 opcode = ROPCODE(0x04001000 + i);
			rsp_dasm_one(string, 0x04001000 + i, opcode);
			fprintf(dasm, "%08X: %08X   %s\n", 0x04001000 + i, opcode, string);
		}
		fclose(dasm);
	}
#endif
#if SAVE_DMEM
	{
		int i;
		FILE *dmem;
		dmem = fopen("rsp_dmem.bin", "wb");

		for (i=0; i < 0x1000; i++)
		{
			fputc(READ8(0x04000000 + i), dmem);
		}
		fclose(dmem);
	}
#endif

	fatalerror("RSP: unknown opcode %02X (%08X) at %08X\n", op >> 26, op, rsp.ppc);
}

/*****************************************************************************/

static const int vector_elements_1[16][8] =
{
	{ 0, 1, 2, 3, 4, 5, 6, 7 },		// none
	{ 0, 1, 2, 3, 4, 5, 6 ,7 },		// ???
	{ 1, 3, 5, 7, 0, 2, 4, 6 },		// 0q
	{ 0, 2, 4, 6, 1, 3, 5, 7 },		// 1q
	{ 1, 2, 3, 5, 6, 7, 0, 4 },		// 0h
	{ 0, 2, 3, 4, 6, 7, 1, 5 },		// 1h
	{ 0, 1, 3, 4, 5, 7, 2, 6 },		// 2h
	{ 0, 1, 2, 4, 5, 6, 3, 7 },		// 3h
	{ 1, 2, 3, 4, 5, 6, 7, 0 },		// 0
	{ 0, 2, 3, 4, 5, 6, 7, 1 },		// 1
	{ 0, 1, 3, 4, 5, 6, 7, 2 },		// 2
	{ 0, 1, 2, 4, 5, 6, 7, 3 },		// 3
	{ 0, 1, 2, 3, 5, 6, 7, 4 },		// 4
	{ 0, 1, 2, 3, 4, 6, 7, 5 },		// 5
	{ 0, 1, 2, 3, 4, 5, 7, 6 },		// 6
	{ 0, 1, 2, 3, 4, 5, 6, 7 },		// 7
};

static const int vector_elements_2[16][8] =
{
	{ 0, 1, 2, 3, 4, 5, 6, 7 },		// none
	{ 0, 1, 2, 3, 4, 5, 6, 7 },		// ???
	{ 0, 0, 2, 2, 4, 4, 6, 6 },		// 0q
	{ 1, 1, 3, 3, 5, 5, 7, 7 },		// 1q
	{ 0, 0, 0, 0, 4, 4, 4, 4 },		// 0h
	{ 1, 1, 1, 1, 5, 5, 5, 5 },		// 1h
	{ 2, 2, 2, 2, 6, 6, 6, 6 },		// 2h
	{ 3, 3, 3, 3, 7, 7, 7, 7 },		// 3h
	{ 0, 0, 0, 0, 0, 0, 0, 0 },		// 0
	{ 1, 1, 1, 1, 1, 1, 1, 1 },		// 1
	{ 2, 2, 2, 2, 2, 2, 2, 2 },		// 2
	{ 3, 3, 3, 3, 3, 3, 3, 3 },		// 3
	{ 4, 4, 4, 4, 4, 4, 4, 4 },		// 4
	{ 5, 5, 5, 5, 5, 5, 5, 5 },		// 5
	{ 6, 6, 6, 6, 6, 6, 6, 6 },		// 6
	{ 7, 7, 7, 7, 7, 7, 7, 7 },		// 7
};

static void rsp_init(int index, int clock, const void *_config, int (*irqcallback)(int))
{
#if LOG_INSTRUCTION_EXECUTION
	exec_output = fopen("rsp_execute.txt", "wt");
#endif

	rsp.irq_callback = irqcallback;
}

static void rsp_exit(void)
{
#if SAVE_DISASM
	{
		char string[200];
		int i;
		FILE *dasm;
		dasm = fopen("rsp_disasm.txt", "wt");

		for (i=0; i < 0x1000; i+=4)
		{
			UINT32 opcode = ROPCODE(0x04001000 + i);
			rsp_dasm_one(string, 0x04001000 + i, opcode);
			fprintf(dasm, "%08X: %08X   %s\n", 0x04001000 + i, opcode, string);
		}
		fclose(dasm);
	}
#endif
#if SAVE_DMEM
	{
		/*int i;
        FILE *dmem;
        dmem = fopen("rsp_dmem.txt", "wt");

        for (i=0; i < 0x1000; i+=4)
        {
            fprintf(dmem, "%08X: %08X\n", 0x04000000 + i, READ32(0x04000000 + i));
        }
        fclose(dmem);*/

		int i;
		FILE *dmem;
		dmem = fopen("rsp_dmem.bin", "wb");

		for (i=0; i < 0x1000; i++)
		{
			fputc(READ8(0x04000000 + i), dmem);
		}
		fclose(dmem);
	}
#endif

#if LOG_INSTRUCTION_EXECUTION
	fclose(exec_output);
#endif
}

static void rsp_reset(void)
{
	rsp.nextpc = ~0;
}

static void handle_lwc2(UINT32 op)
{
	int i, end;
	UINT32 ea;
	int dest = (op >> 16) & 0x1f;
	int base = (op >> 21) & 0x1f;
	int index = (op >> 7) & 0xf;
	int offset = (op & 0x7f);
	if (offset & 0x40)
		offset |= 0xffffffc0;

	switch ((op >> 11) & 0x1f)
	{
		case 0x00:		/* LBV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 00000 | IIII | Offset |
			// --------------------------------------------------
			//
			// Load 1 byte to vector byte index

			ea = (base) ? rsp.r[base] + offset : offset;
			VREG_B(dest, index) = READ8(ea);
			break;
		}
		case 0x01:		/* LSV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 00001 | IIII | Offset |
			// --------------------------------------------------
			//
			// Loads 2 bytes starting from vector byte index

			ea = (base) ? rsp.r[base] + (offset * 2) : (offset * 2);

			end = index + 2;

			for (i=index; i < end; i++)
			{
				VREG_B(dest, i) = READ8(ea);
				ea++;
			}
			break;
		}
		case 0x02:		/* LLV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 00010 | IIII | Offset |
			// --------------------------------------------------
			//
			// Loads 4 bytes starting from vector byte index

			ea = (base) ? rsp.r[base] + (offset * 4) : (offset * 4);

			end = index + 4;

			for (i=index; i < end; i++)
			{
				VREG_B(dest, i) = READ8(ea);
				ea++;
			}
			break;
		}
		case 0x03:		/* LDV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 00011 | IIII | Offset |
			// --------------------------------------------------
			//
			// Loads 8 bytes starting from vector byte index

			ea = (base) ? rsp.r[base] + (offset * 8) : (offset * 8);

			end = index + 8;

			for (i=index; i < end; i++)
			{
				VREG_B(dest, i) = READ8(ea);
				ea++;
			}
			break;
		}
		case 0x04:		/* LQV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 00100 | IIII | Offset |
			// --------------------------------------------------
			//
			// Loads up to 16 bytes starting from vector byte index

			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			end = index + (16 - (ea & 0xf));
			if (end > 16) end = 16;

			for (i=index; i < end; i++)
			{
				VREG_B(dest, i) = READ8(ea);
				ea++;
			}
			break;
		}
		case 0x05:		/* LRV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 00101 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores up to 16 bytes starting from right side until 16-byte boundary

			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			index = 16 - ((ea & 0xf) - index);
			end = 16;
			ea &= ~0xf;

			for (i=index; i < end; i++)
			{
				VREG_B(dest, i) = READ8(ea);
				ea++;
			}
			break;
		}
		case 0x06:		/* LPV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 00110 | IIII | Offset |
			// --------------------------------------------------
			//
			// Loads a byte as the upper 8 bits of each element

			ea = (base) ? rsp.r[base] + (offset * 8) : (offset * 8);

			for (i=0; i < 8; i++)
			{
				VREG_S(dest, i) = READ8(ea + (((16-index) + i) & 0xf)) << 8;
			}
			break;
		}
		case 0x07:		/* LUV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 00111 | IIII | Offset |
			// --------------------------------------------------
			//
			// Loads a byte as the bits 14-7 of each element

			ea = (base) ? rsp.r[base] + (offset * 8) : (offset * 8);

			for (i=0; i < 8; i++)
			{
				VREG_S(dest, i) = READ8(ea + (((16-index) + i) & 0xf)) << 7;
			}
			break;
		}
		case 0x08:		/* LHV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 01000 | IIII | Offset |
			// --------------------------------------------------
			//
			// Loads a byte as the bits 14-7 of each element, with 2-byte stride

			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			for (i=0; i < 8; i++)
			{
				VREG_S(dest, i) = READ8(ea + (((16-index) + (i<<1)) & 0xf)) << 7;
			}
			break;
		}
		case 0x09:		/* LFV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 01001 | IIII | Offset |
			// --------------------------------------------------
			//
			// Loads a byte as the bits 14-7 of upper or lower quad, with 4-byte stride

			fatalerror("RSP: LFV\n");

			if (index & 0x7)	fatalerror("RSP: LFV: index = %d at %08X\n", index, rsp.ppc);

			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			// not sure what happens if 16-byte boundary is crossed...
			if ((ea & 0xf) > 0)	fatalerror("RSP: LFV: 16-byte boundary crossing at %08X, recheck this!\n", rsp.ppc);

			end = (index >> 1) + 4;

			for (i=index >> 1; i < end; i++)
			{
				VREG_S(dest, i) = READ8(ea) << 7;
				ea += 4;
			}
			break;
		}
		case 0x0a:		/* LWV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 01010 | IIII | Offset |
			// --------------------------------------------------
			//
			// Loads the full 128-bit vector starting from vector byte index and wrapping to index 0
			// after byte index 15

			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			// not sure what happens if 16-byte boundary is crossed...
			if ((ea & 0xf) > 0) fatalerror("RSP: LWV: 16-byte boundary crossing at %08X, recheck this!\n", rsp.ppc);

			end = (16 - index) + 16;

			for (i=(16 - index); i < end; i++)
			{
				VREG_B(dest, i & 0xf) = READ8(ea);
				ea += 4;
			}
			break;
		}
		case 0x0b:		/* LTV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 110010 | BBBBB | TTTTT | 01011 | IIII | Offset |
			// --------------------------------------------------
			//
			// Loads one element to maximum of 8 vectors, while incrementing element index

			// FIXME: has a small problem with odd indices

			int element;
			int vs = dest;
			int ve = dest + 8;
			if (ve > 32)
				ve = 32;

			element = 7 - (index >> 1);

			if (index & 1)	fatalerror("RSP: LTV: index = %d\n", index);

			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			ea = ((ea + 8) & ~0xf) + (index & 1);
			for (i=vs; i < ve; i++)
			{
				element = ((8 - (index >> 1) + (i-vs)) << 1);
				VREG_B(i, (element & 0xf)) = READ8(ea);
				VREG_B(i, ((element+1) & 0xf)) = READ8(ea+1);

				ea += 2;
			}
			break;
		}

		default:
		{
			unimplemented_opcode(op);
			break;
		}
	}
}

static void handle_swc2(UINT32 op)
{
	int i, end;
	int eaoffset;
	UINT32 ea;
	int dest = (op >> 16) & 0x1f;
	int base = (op >> 21) & 0x1f;
	int index = (op >> 7) & 0xf;
	int offset = (op & 0x7f);
	if (offset & 0x40)
		offset |= 0xffffffc0;

	switch ((op >> 11) & 0x1f)
	{
		case 0x00:		/* SBV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 00000 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores 1 byte from vector byte index

			ea = (base) ? rsp.r[base] + offset : offset;
			WRITE8(ea, VREG_B(dest, index));
			break;
		}
		case 0x01:		/* SSV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 00001 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores 2 bytes starting from vector byte index

			ea = (base) ? rsp.r[base] + (offset * 2) : (offset * 2);

			end = index + 2;

			for (i=index; i < end; i++)
			{
				WRITE8(ea, VREG_B(dest, i));
				ea++;
			}
			break;
		}
		case 0x02:		/* SLV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 00010 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores 4 bytes starting from vector byte index

			ea = (base) ? rsp.r[base] + (offset * 4) : (offset * 4);

			end = index + 4;

			for (i=index; i < end; i++)
			{
				WRITE8(ea, VREG_B(dest, i));
				ea++;
			}
			break;
		}
		case 0x03:		/* SDV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 00011 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores 8 bytes starting from vector byte index

			ea = (base) ? rsp.r[base] + (offset * 8) : (offset * 8);

			end = index + 8;

			for (i=index; i < end; i++)
			{
				WRITE8(ea, VREG_B(dest, i));
				ea++;
			}
			break;
		}
		case 0x04:		/* SQV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 00100 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores up to 16 bytes starting from vector byte index until 16-byte boundary

			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			end = index + (16 - (ea & 0xf));

			for (i=index; i < end; i++)
			{
				WRITE8(ea, VREG_B(dest, i & 0xf));
				ea++;
			}
			break;
		}
		case 0x05:		/* SRV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 00101 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores up to 16 bytes starting from right side until 16-byte boundary

			int o;
			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			end = index + (ea & 0xf);
			o = (16 - (ea & 0xf)) & 0xf;
			ea &= ~0xf;

			for (i=index; i < end; i++)
			{
				WRITE8(ea, VREG_B(dest, ((i + o) & 0xf)));
				ea++;
			}
			break;
		}
		case 0x06:		/* SPV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 00110 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores upper 8 bits of each element

			ea = (base) ? rsp.r[base] + (offset * 8) : (offset * 8);
			end = index + 8;

			for (i=index; i < end; i++)
			{
				if ((i & 0xf) < 8)
				{
					WRITE8(ea, VREG_B(dest, ((i & 0xf) << 1)));
				}
				else
				{
					WRITE8(ea, VREG_S(dest, (i & 0x7)) >> 7);
				}
				ea++;
			}
			break;
		}
		case 0x07:		/* SUV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 00111 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores bits 14-7 of each element

			ea = (base) ? rsp.r[base] + (offset * 8) : (offset * 8);
			end = index + 8;

			for (i=index; i < end; i++)
			{
				if ((i & 0xf) < 8)
				{
					WRITE8(ea, VREG_S(dest, (i & 0x7)) >> 7);
				}
				else
				{
					WRITE8(ea, VREG_B(dest, ((i & 0x7) << 1)));
				}
				ea++;
			}
			break;
		}
		case 0x08:		/* SHV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 01000 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores bits 14-7 of each element, with 2-byte stride

			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			for (i=0; i < 8; i++)
			{
				UINT8 d = ((VREG_B(dest, ((index + (i << 1) + 0) & 0xf))) << 1) |
						  ((VREG_B(dest, ((index + (i << 1) + 1) & 0xf))) >> 7);

				WRITE8(ea, d);
				ea += 2;
			}
			break;
		}
		case 0x09:		/* SFV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 01001 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores bits 14-7 of upper or lower quad, with 4-byte stride

			// FIXME: only works for index 0 and index 8

			if (index & 0x7)	mame_printf_debug("RSP: SFV: index = %d at %08X\n", index, rsp.ppc);

			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			eaoffset = ea & 0xf;
			ea &= ~0xf;

			end = (index >> 1) + 4;

			for (i=index >> 1; i < end; i++)
			{
				WRITE8(ea + (eaoffset & 0xf), VREG_S(dest, i) >> 7);
				eaoffset += 4;
			}
			break;
		}
		case 0x0a:		/* SWV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 01010 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores the full 128-bit vector starting from vector byte index and wrapping to index 0
			// after byte index 15

			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			eaoffset = ea & 0xf;
			ea &= ~0xf;

			end = index + 16;

			for (i=index; i < end; i++)
			{
				WRITE8(ea + (eaoffset & 0xf), VREG_B(dest, i & 0xf));
				eaoffset++;
			}
			break;
		}
		case 0x0b:		/* STV */
		{
			// 31       25      20      15      10     6        0
			// --------------------------------------------------
			// | 111010 | BBBBB | TTTTT | 01011 | IIII | Offset |
			// --------------------------------------------------
			//
			// Stores one element from maximum of 8 vectors, while incrementing element index

			int element, eaoffset;
			int vs = dest;
			int ve = dest + 8;
			if (ve > 32)
				ve = 32;

			element = 8 - (index >> 1);
			if (index & 0x1)	fatalerror("RSP: STV: index = %d at %08X\n", index, rsp.ppc);

			ea = (base) ? rsp.r[base] + (offset * 16) : (offset * 16);

			if (ea & 0x1)		fatalerror("RSP: STV: ea = %08X at %08X\n", ea, rsp.ppc);

			eaoffset = (ea & 0xf) + (element * 2);
			ea &= ~0xf;

			for (i=vs; i < ve; i++)
			{
				WRITE16(ea + (eaoffset & 0xf), VREG_S(i, element & 0x7));
				eaoffset += 2;
				element++;
			}
			break;
		}

		default:
		{
			unimplemented_opcode(op);
			break;
		}
	}
}

INLINE UINT16 SATURATE_ACCUM(int accum, int slice, UINT16 negative, UINT16 positive)
{
	if ((INT16)ACCUM_H(accum) < 0)
	{
		if ((UINT16)(ACCUM_H(accum)) != 0xffff)
		{
			return negative;
		}
		else
		{
			if ((INT16)ACCUM_M(accum) >= 0)
			{
				return negative;
			}
			else
			{
				if (slice == 0)
				{
					return ACCUM_L(accum);
				}
				else if (slice == 1)
				{
					return ACCUM_M(accum);
				}
			}
		}
	}
	else
	{
		if ((UINT16)(ACCUM_H(accum)) != 0)
		{
			return positive;
		}
		else
		{
			if ((INT16)ACCUM_M(accum) < 0)
			{
				return positive;
			}
			else
			{
				if (slice == 0)
				{
					return ACCUM_L(accum);
				}
				else
				{
					return ACCUM_M(accum);
				}
			}
		}
	}

	return 0;
}

#define WRITEBACK_RESULT() 					\
	do {									\
		VREG_S(VDREG, 0) = vres[0];			\
		VREG_S(VDREG, 1) = vres[1];			\
		VREG_S(VDREG, 2) = vres[2];			\
		VREG_S(VDREG, 3) = vres[3];			\
		VREG_S(VDREG, 4) = vres[4];			\
		VREG_S(VDREG, 5) = vres[5];			\
		VREG_S(VDREG, 6) = vres[6];			\
		VREG_S(VDREG, 7) = vres[7];			\
	} while(0)


static void handle_vector_ops(UINT32 op)
{
	int i;
	INT16 vres[8];

	// Opcode legend:
	//    E = VS2 element type
	//    S = VS1, Source vector 1
	//    T = VS2, Source vector 2
	//    D = Destination vector

	switch (op & 0x3f)
	{
		case 0x00:		/* VMULF */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 000000 |
			// ------------------------------------------------------
			//
			// Multiplies signed integer by signed integer * 2

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (INT32)(INT16)VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)VREG_S(VS2REG, sel);
				if (s1 == -32768 && s2 == -32768)
				{
					// overflow
					ACCUM_H(del) = 0;
					ACCUM_M(del) = -32768;
					ACCUM_L(del) = -32768;
					vres[del] = 0x7fff;
				}
				else
				{
					INT64 r =  s1 * s2 * 2;
					r += 0x8000;	// rounding ?
					ACCUM_H(del) = (r < 0) ? 0xffff : 0;		// sign-extend to 48-bit
					ACCUM_M(del) = (INT16)(r >> 16);
					ACCUM_L(del) = (UINT16)(r);
					vres[del] = ACCUM_M(del);
				}
			}
			WRITEBACK_RESULT();

			break;
		}

		case 0x01:		/* VMULU */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 000001 |
			// ------------------------------------------------------
			//

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (INT32)(INT16)VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)VREG_S(VS2REG, sel);
				INT64 r = s1 * s2 * 2;
				r += 0x8000;	// rounding ?

				ACCUM_H(del) = (UINT16)(r >> 32);
				ACCUM_M(del) = (UINT16)(r >> 16);
				ACCUM_L(del) = (UINT16)(r);

				if (r < 0)
				{
					vres[del] = 0;
				}
				else if (((INT16)(ACCUM_H(del)) ^ (INT16)(ACCUM_M(del))) < 0)
				{
					vres[del] = -1;
				}
				else
				{
					vres[del] = ACCUM_M(del);
				}
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x04:		/* VMUDL */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 000100 |
			// ------------------------------------------------------
			//
			// Multiplies unsigned fraction by unsigned fraction
			// Stores the higher 16 bits of the 32-bit result to accumulator
			// The low slice of accumulator is stored into destination element

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				UINT32 s1 = (UINT32)(UINT16)VREG_S(VS1REG, del);
				UINT32 s2 = (UINT32)(UINT16)VREG_S(VS2REG, sel);
				UINT32 r = s1 * s2;

				ACCUM_H(del) = 0;
				ACCUM_M(del) = 0;
				ACCUM_L(del) = (UINT16)(r >> 16);

				vres[del] = ACCUM_L(del);
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x05:		/* VMUDM */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 000101 |
			// ------------------------------------------------------
			//
			// Multiplies signed integer by unsigned fraction
			// The result is stored into accumulator
			// The middle slice of accumulator is stored into destination element

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (INT32)(INT16)VREG_S(VS1REG, del);
				INT32 s2 = (UINT16)VREG_S(VS2REG, sel);	// not sign-extended
				INT32 r =  s1 * s2;

				ACCUM_H(del) = (r < 0) ? 0xffff : 0;		// sign-extend to 48-bit
				ACCUM_M(del) = (INT16)(r >> 16);
				ACCUM_L(del) = (UINT16)(r);

				vres[del] = ACCUM_M(del);
			}
			WRITEBACK_RESULT();
			break;

		}

		case 0x06:		/* VMUDN */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 000110 |
			// ------------------------------------------------------
			//
			// Multiplies unsigned fraction by signed integer
			// The result is stored into accumulator
			// The low slice of accumulator is stored into destination element

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (UINT16)VREG_S(VS1REG, del);		// not sign-extended
				INT32 s2 = (INT32)(INT16)VREG_S(VS2REG, sel);
				INT32 r = s1 * s2;

				ACCUM_H(del) = (r < 0) ? 0xffff : 0;		// sign-extend to 48-bit
				ACCUM_M(del) = (INT16)(r >> 16);
				ACCUM_L(del) = (UINT16)(r);

				vres[del] = ACCUM_L(del);
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x07:		/* VMUDH */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 000111 |
			// ------------------------------------------------------
			//
			// Multiplies signed integer by signed integer
			// The result is stored into highest 32 bits of accumulator, the low slice is zero
			// The highest 32 bits of accumulator is saturated into destination element

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (INT32)(INT16)VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)VREG_S(VS2REG, sel);
				INT32 r = s1 * s2;

				ACCUM_H(del) = (INT16)(r >> 16);
				ACCUM_M(del) = (UINT16)(r);
				ACCUM_L(del) = 0;

				if (r < -32768) r = -32768;
				if (r >  32767)	r = 32767;
				vres[del] = (INT16)(r);
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x08:		/* VMACF */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 001000 |
			// ------------------------------------------------------
			//
			// Multiplies signed integer by signed integer * 2
			// The result is added to accumulator

			for (i=0; i < 8; i++)
			{
				UINT16 res;
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (INT32)(INT16)VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)VREG_S(VS2REG, sel);
				INT32 r = s1 * s2;

				ACCUM(del) += (INT64)(r) << 17;
				res = SATURATE_ACCUM(del, 1, 0x8000, 0x7fff);

				vres[del] = res;
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x09:		/* VMACU */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 001001 |
			// ------------------------------------------------------
			//

			for (i=0; i < 8; i++)
			{
				UINT16 res;
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (INT32)(INT16)VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)VREG_S(VS2REG, sel);
				INT32 r1 = s1 * s2;
				UINT32 r2 = (UINT16)ACCUM_L(del) + ((UINT16)(r1) * 2);
				UINT32 r3 = (UINT16)ACCUM_M(del) + (UINT16)((r1 >> 16) * 2) + (UINT16)(r2 >> 16);

				ACCUM_L(del) = (UINT16)(r2);
				ACCUM_M(del) = (UINT16)(r3);
				ACCUM_H(del) += (UINT16)(r3 >> 16) + (UINT16)(r1 >> 31);

				//res = SATURATE_ACCUM(del, 1, 0x0000, 0xffff);
				if ((INT16)ACCUM_H(del) < 0)
				{
					res = 0;
				}
				else
				{
					if (ACCUM_H(del) != 0)
					{
						res = 0xffff;
					}
					else
					{
						if ((INT16)ACCUM_M(del) < 0)
						{
							res = 0xffff;
						}
						else
						{
							res = ACCUM_M(del);
						}
					}
				}

				vres[del] = res;
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x0c:		/* VMADL */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 001100 |
			// ------------------------------------------------------
			//
			// Multiplies unsigned fraction by unsigned fraction
			// Adds the higher 16 bits of the 32-bit result to accumulator
			// The low slice of accumulator is stored into destination element

			for (i=0; i < 8; i++)
			{
				UINT16 res;
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				UINT32 s1 = (UINT32)(UINT16)VREG_S(VS1REG, del);
				UINT32 s2 = (UINT32)(UINT16)VREG_S(VS2REG, sel);
				UINT32 r1 = s1 * s2;
				UINT32 r2 = (UINT16)ACCUM_L(del) + (r1 >> 16);
				UINT32 r3 = (UINT16)ACCUM_M(del) + (r2 >> 16);

				ACCUM_L(del) = (UINT16)(r2);
				ACCUM_M(del) = (UINT16)(r3);
				ACCUM_H(del) += (INT16)(r3 >> 16);

				res = SATURATE_ACCUM(del, 0, 0x0000, 0xffff);

				vres[del] = res;
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x0d:		/* VMADM */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 001101 |
			// ------------------------------------------------------
			//
			// Multiplies signed integer by unsigned fraction
			// The result is added into accumulator
			// The middle slice of accumulator is stored into destination element

			for (i=0; i < 8; i++)
			{
				UINT16 res;
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				UINT32 s1 = (INT32)(INT16)VREG_S(VS1REG, del);
				UINT32 s2 = (UINT16)VREG_S(VS2REG, sel);	// not sign-extended
				UINT32 r1 = s1 * s2;
				UINT32 r2 = (UINT16)ACCUM_L(del) + (UINT16)(r1);
				UINT32 r3 = (UINT16)ACCUM_M(del) + (r1 >> 16) + (r2 >> 16);

				ACCUM_L(del) = (UINT16)(r2);
				ACCUM_M(del) = (UINT16)(r3);
				ACCUM_H(del) += (UINT16)(r3 >> 16);
				if ((INT32)(r1) < 0)
					ACCUM_H(del) -= 1;

				res = SATURATE_ACCUM(del, 1, 0x8000, 0x7fff);

				vres[del] = res;
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x0e:		/* VMADN */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 001110 |
			// ------------------------------------------------------
			//
			// Multiplies unsigned fraction by signed integer
			// The result is added into accumulator
			// The low slice of accumulator is stored into destination element

			for (i=0; i < 8; i++)
			{
				UINT16 res;
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (UINT16)VREG_S(VS1REG, del);		// not sign-extended
				INT32 s2 = (INT32)(INT16)VREG_S(VS2REG, sel);
				UINT32 r1 = s1 * s2;
				UINT32 r2 = (UINT16)ACCUM_L(del) + (UINT16)(r1);
				UINT32 r3 = (UINT16)ACCUM_M(del) + (r1 >> 16) + (r2 >> 16);

				ACCUM_L(del) = (UINT16)(r2);
				ACCUM_M(del) = (UINT16)(r3);
				ACCUM_H(del) += (UINT16)(r3 >> 16);
				if ((INT32)(r1) < 0)
					ACCUM_H(del) -= 1;

				res = SATURATE_ACCUM(del, 0, 0x0000, 0xffff);

				vres[del] = res;
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x0f:		/* VMADH */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 001111 |
			// ------------------------------------------------------
			//
			// Multiplies signed integer by signed integer
			// The result is added into highest 32 bits of accumulator, the low slice is zero
			// The highest 32 bits of accumulator is saturated into destination element

			for (i=0; i < 8; i++)
			{
				UINT16 res;
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (INT32)(INT16)VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)VREG_S(VS2REG, sel);
				INT64 r = s1 * s2;

				ACCUM(del) += (INT64)(r) << 32;

				res = SATURATE_ACCUM(del, 1, 0x8000, 0x7fff);

				vres[del] = res;
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x10:		/* VADD */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 010000 |
			// ------------------------------------------------------
			//
			// Adds two vector registers and carry flag, the result is saturated to 32767

			// TODO: check VS2REG == VDREG

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (INT32)(INT16)VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)VREG_S(VS2REG, sel);
				INT32 r = s1 + s2 + CARRY_FLAG(del);

				ACCUM_L(del) = (INT16)(r);

				if (r > 32767) r = 32767;
				if (r < -32768) r = -32768;
				vres[del] = (INT16)(r);
			}
			CLEAR_ZERO_FLAGS();
			CLEAR_CARRY_FLAGS();
			WRITEBACK_RESULT();
			break;
		}

		case 0x11:		/* VSUB */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 010001 |
			// ------------------------------------------------------
			//
			// Subtracts two vector registers and carry flag, the result is saturated to -32768

			// TODO: check VS2REG == VDREG

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (INT32)(INT16)VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)VREG_S(VS2REG, sel);
				INT32 r = s1 - s2 - CARRY_FLAG(del);

				ACCUM_L(del) = (INT16)(r);

				if (r > 32767) r = 32767;
				if (r < -32768) r = -32768;

				vres[del] = (INT16)(r);
			}
			CLEAR_ZERO_FLAGS();
			CLEAR_CARRY_FLAGS();
			WRITEBACK_RESULT();
			break;
		}

		case 0x13:		/* VABS */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 010011 |
			// ------------------------------------------------------
			//
			// Changes the sign of source register 2 if source register 1 is negative and stores
			// the result to destination register

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT16 s1 = (INT16)VREG_S(VS1REG, del);
				INT16 s2 = (INT16)VREG_S(VS2REG, sel);

				if (s1 < 0)
				{
					if (s2 == -32768)
					{
						vres[del] = 32767;
					}
					else
					{
						vres[del] = -s2;
					}
				}
				else if (s1 > 0)
				{
					vres[del] = s2;
				}
				else
				{
					vres[del] = 0;
				}

				ACCUM_L(del) = vres[del];
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x14:		/* VADDC */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 010100 |
			// ------------------------------------------------------
			//
			// Adds two vector registers, the carry out is stored into carry register

			// TODO: check VS2REG = VDREG

			CLEAR_ZERO_FLAGS();
			CLEAR_CARRY_FLAGS();

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (UINT32)(UINT16)VREG_S(VS1REG, del);
				INT32 s2 = (UINT32)(UINT16)VREG_S(VS2REG, sel);
				INT32 r = s1 + s2;

				vres[del] = (INT16)(r);
				ACCUM_L(del) = (INT16)(r);

				if (r & 0xffff0000)
				{
					SET_CARRY_FLAG(del);
				}
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x15:		/* VSUBC */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 010101 |
			// ------------------------------------------------------
			//
			// Subtracts two vector registers, the carry out is stored into carry register

			// TODO: check VS2REG = VDREG

			CLEAR_ZERO_FLAGS();
			CLEAR_CARRY_FLAGS();

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT32 s1 = (UINT32)(UINT16)VREG_S(VS1REG, del);
				INT32 s2 = (UINT32)(UINT16)VREG_S(VS2REG, sel);
				INT32 r = s1 - s2;

				vres[del] = (INT16)(r);
				ACCUM_L(del) = (UINT16)(r);

				if ((UINT16)(r) != 0)
				{
					SET_ZERO_FLAG(del);
				}
				if (r & 0xffff0000)
				{
					SET_CARRY_FLAG(del);
				}
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x1d:		/* VSAW */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 011101 |
			// ------------------------------------------------------
			//
			// Stores high, middle or low slice of accumulator to destination vector

			switch (EL)
			{
				case 0x08:		// VSAWH
				{
					for (i=0; i < 8; i++)
					{
						VREG_S(VDREG, i) = ACCUM_H(i);
					}
					break;
				}
				case 0x09:		// VSAWM
				{
					for (i=0; i < 8; i++)
					{
						VREG_S(VDREG, i) = ACCUM_M(i);
					}
					break;
				}
				case 0x0a:		// VSAWL
				{
					for (i=0; i < 8; i++)
					{
						VREG_S(VDREG, i) = ACCUM_L(i);
					}
					break;
				}
				default:	fatalerror("RSP: VSAW: el = %d\n", EL);
			}
			break;
		}

		case 0x20:		/* VLT */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 100000 |
			// ------------------------------------------------------
			//
			// Sets compare flags if elements in VS1 are less than VS2
			// Moves the element in VS2 to destination vector

			rsp.flag[1] = 0;

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);

				if (VREG_S(VS1REG, del) < VREG_S(VS2REG, sel))
				{
					vres[del] = VREG_S(VS1REG, del);
					SET_COMPARE_FLAG(del);
				}
				else if (VREG_S(VS1REG, del) == VREG_S(VS2REG, sel))
				{
					vres[del] = VREG_S(VS1REG, del);
					if (ZERO_FLAG(del) != 0 && CARRY_FLAG(del) != 0)
					{
						SET_COMPARE_FLAG(del);
					}
				}
				else
				{
					vres[del] = VREG_S(VS2REG, sel);
				}

				ACCUM_L(del) = vres[del];
			}

			CLEAR_ZERO_FLAGS();
			CLEAR_CARRY_FLAGS();
			WRITEBACK_RESULT();
			break;
		}

		case 0x21:		/* VEQ */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 100001 |
			// ------------------------------------------------------
			//
			// Sets compare flags if elements in VS1 are equal with VS2
			// Moves the element in VS2 to destination vector

			rsp.flag[1] = 0;

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);

				vres[del] = VREG_S(VS2REG, sel);
				ACCUM_L(del) = vres[del];

				if (VREG_S(VS1REG, del) == VREG_S(VS2REG, sel))
				{
					if (ZERO_FLAG(del) == 0)
					{
						SET_COMPARE_FLAG(del);
					}
				}
			}

			CLEAR_ZERO_FLAGS();
			CLEAR_CARRY_FLAGS();
			WRITEBACK_RESULT();
			break;
		}

		case 0x22:		/* VNE */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 100010 |
			// ------------------------------------------------------
			//
			// Sets compare flags if elements in VS1 are not equal with VS2
			// Moves the element in VS2 to destination vector

			rsp.flag[1] = 0;

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);

				vres[del] = VREG_S(VS1REG, del);
				ACCUM_L(del) = vres[del];

				if (VREG_S(VS1REG, del) != VREG_S(VS2REG, sel))
				{
					SET_COMPARE_FLAG(del);
				}
				else
				{
					if (ZERO_FLAG(del) != 0)
					{
						SET_COMPARE_FLAG(del);
					}
				}
			}

			CLEAR_ZERO_FLAGS();
			CLEAR_CARRY_FLAGS();
			WRITEBACK_RESULT();
			break;
		}

		case 0x23:		/* VGE */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 100011 |
			// ------------------------------------------------------
			//
			// Sets compare flags if elements in VS1 are greater or equal with VS2
			// Moves the element in VS2 to destination vector

			rsp.flag[1] = 0;

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);

				if (VREG_S(VS1REG, del) == VREG_S(VS2REG, sel))
				{
					if (ZERO_FLAG(del) == 0 || CARRY_FLAG(del) == 0)
					{
						SET_COMPARE_FLAG(del);
					}
				}
				else if (VREG_S(VS1REG, del) > VREG_S(VS2REG, sel))
				{
					SET_COMPARE_FLAG(del);
				}

				if (COMPARE_FLAG(del) != 0)
				{
					vres[del] = VREG_S(VS1REG, del);
				}
				else
				{
					vres[del] = VREG_S(VS2REG, sel);
				}

				ACCUM_L(del) = vres[del];
			}

			CLEAR_ZERO_FLAGS();
			CLEAR_CARRY_FLAGS();
			WRITEBACK_RESULT();
			break;
		}

		case 0x24:		/* VCL */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 100100 |
			// ------------------------------------------------------
			//
			// Vector clip low

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT16 s1 = VREG_S(VS1REG, del);
				INT16 s2 = VREG_S(VS2REG, sel);

				if (CARRY_FLAG(del) != 0)
				{
					if (ZERO_FLAG(del) != 0)
					{
						if (COMPARE_FLAG(del) != 0)
						{
							ACCUM_L(del) = -(UINT16)s2;
						}
						else
						{
							ACCUM_L(del) = s1;
						}
					}
					else
					{
						if (rsp.flag[2] & (1 << (del)))
						{
							if (((UINT32)(INT16)(s1) + (UINT32)(INT16)(s2)) > 0x10000)
							{
								ACCUM_L(del) = s1;
								CLEAR_COMPARE_FLAG(del);
							}
							else
							{
								ACCUM_L(del) = -((UINT16)s2);
								SET_COMPARE_FLAG(del);
							}
						}
						else
						{
							if (((UINT32)(INT16)(s1) + (UINT32)(INT16)(s2)) != 0)
							{
								ACCUM_L(del) = s1;
								CLEAR_COMPARE_FLAG(del);
							}
							else
							{
								ACCUM_L(del) = -((UINT16)s2);
								SET_COMPARE_FLAG(del);
							}
						}
					}
				}
				else
				{
					if (ZERO_FLAG(del) != 0)
					{
						if (rsp.flag[1] & (1 << (8+del)))
						{
							ACCUM_L(del) = s2;
						}
						else
						{
							ACCUM_L(del) = s1;
						}
					}
					else
					{
						if (((INT32)(UINT16)s1 - (INT32)(UINT16)s2) >= 0)
						{
							ACCUM_L(del) = s2;
							rsp.flag[1] |= (1 << (8+del));
						}
						else
						{
							ACCUM_L(del) = s1;
							rsp.flag[1] &= ~(1 << (8+del));
						}
					}
				}

				vres[del] = ACCUM_L(del);
			}
			CLEAR_ZERO_FLAGS();
			CLEAR_CARRY_FLAGS();
			rsp.flag[2] = 0;
			WRITEBACK_RESULT();
			break;
		}

		case 0x25:		/* VCH */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 100101 |
			// ------------------------------------------------------
			//
			// Vector clip high

			CLEAR_ZERO_FLAGS();
			CLEAR_CARRY_FLAGS();
			rsp.flag[1] = 0;
			rsp.flag[2] = 0;

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT16 s1 = VREG_S(VS1REG, del);
				INT16 s2 = VREG_S(VS2REG, sel);

				if ((s1 ^ s2) < 0)
				{
					SET_CARRY_FLAG(del);
					if (s2 < 0)
					{
						rsp.flag[1] |= (1 << (8+del));
					}

					if (s1 + s2 <= 0)
					{
						if (s1 + s2 == -1)
						{
							rsp.flag[2] |= (1 << (del));
						}
						SET_COMPARE_FLAG(del);
						vres[del] = -((UINT16)s2);
					}
					else
					{
						vres[del] = s1;
					}

					if (s1 + s2 != 0)
					{
						if (s1 != ~s2)
						{
							SET_ZERO_FLAG(del);
						}
					}
				}
				else
				{
					if (s2 < 0)
					{
						SET_COMPARE_FLAG(del);
					}
					if (s1 - s2 >= 0)
					{
						rsp.flag[1] |= (1 << (8+del));
						vres[del] = s2;
					}
					else
					{
						vres[del] = s1;
					}

					if ((s1 - s2) != 0)
					{
						if (s1 != ~s2)
						{
							SET_ZERO_FLAG(del);
						}
					}
				}

				ACCUM_L(del) = vres[del];
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x26:		/* VCR */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 100110 |
			// ------------------------------------------------------
			//
			// Vector clip reverse

			rsp.flag[0] = 0;
			rsp.flag[1] = 0;
			rsp.flag[2] = 0;

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				INT16 s1 = VREG_S(VS1REG, del);
				INT16 s2 = VREG_S(VS2REG, sel);

				if ((INT16)(s1 ^ s2) < 0)
				{
					if (s2 < 0)
					{
						rsp.flag[1] |= (1 << (8+del));
					}
					if ((s1 + s2) <= 0)
					{
						ACCUM_L(del) = ~((UINT16)s2);
						SET_COMPARE_FLAG(del);
					}
					else
					{
						ACCUM_L(del) = s1;
					}
				}
				else
				{
					if (s2 < 0)
					{
						SET_COMPARE_FLAG(del);
					}
					if ((s1 - s2) >= 0)
					{
						ACCUM_L(del) = s2;
						rsp.flag[1] |= (1 << (8+del));
					}
					else
					{
						ACCUM_L(del) = s1;
					}
				}

				vres[del] = ACCUM_L(del);
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x27:		/* VMRG */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 100111 |
			// ------------------------------------------------------
			//
			// Merges two vectors according to compare flags

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				if (COMPARE_FLAG(del) != 0)
				{
					vres[del] = VREG_S(VS1REG, del);
				}
				else
				{
					vres[del] = VREG_S(VS2REG, VEC_EL_2(EL, sel));
				}

				ACCUM_L(del) = vres[del];
			}
			WRITEBACK_RESULT();
			break;
		}
		case 0x28:		/* VAND */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 101000 |
			// ------------------------------------------------------
			//
			// Bitwise AND of two vector registers

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				vres[del] = VREG_S(VS1REG, del) & VREG_S(VS2REG, sel);
				ACCUM_L(del) = vres[del];
			}
			WRITEBACK_RESULT();
			break;
		}
		case 0x29:		/* VNAND */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 101001 |
			// ------------------------------------------------------
			//
			// Bitwise NOT AND of two vector registers

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				vres[del] = ~((VREG_S(VS1REG, del) & VREG_S(VS2REG, sel)));
				ACCUM_L(del) = vres[del];
			}
			WRITEBACK_RESULT();
			break;
		}
		case 0x2a:		/* VOR */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 101010 |
			// ------------------------------------------------------
			//
			// Bitwise OR of two vector registers

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				vres[del] = VREG_S(VS1REG, del) | VREG_S(VS2REG, sel);
				ACCUM_L(del) = vres[del];
			}
			WRITEBACK_RESULT();
			break;
		}
		case 0x2b:		/* VNOR */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 101011 |
			// ------------------------------------------------------
			//
			// Bitwise NOT OR of two vector registers

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				vres[del] = ~((VREG_S(VS1REG, del) | VREG_S(VS2REG, sel)));
				ACCUM_L(del) = vres[del];
			}
			WRITEBACK_RESULT();
			break;
		}
		case 0x2c:		/* VXOR */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 101100 |
			// ------------------------------------------------------
			//
			// Bitwise XOR of two vector registers

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				vres[del] = VREG_S(VS1REG, del) ^ VREG_S(VS2REG, sel);
				ACCUM_L(del) = vres[del];
			}
			WRITEBACK_RESULT();
			break;
		}
		case 0x2d:		/* VNXOR */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | TTTTT | DDDDD | 101101 |
			// ------------------------------------------------------
			//
			// Bitwise NOT XOR of two vector registers

			for (i=0; i < 8; i++)
			{
				int del = VEC_EL_1(EL, i);
				int sel = VEC_EL_2(EL, del);
				vres[del] = ~((VREG_S(VS1REG, del) ^ VREG_S(VS2REG, sel)));
				ACCUM_L(del) = vres[del];
			}
			WRITEBACK_RESULT();
			break;
		}

		case 0x30:		/* VRCP */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | ?FFFF | DDDDD | 110000 |
			// ------------------------------------------------------
			//
			// Calculates reciprocal

			int del = (VS1REG & 7);
			int sel = VEC_EL_2(EL, 7-del);
			INT32 rec;

			rec = (INT16)(VREG_S(VS2REG, sel));

			if (rec == 0)
			{
				// divide by zero -> overflow
				rec = 0x7fffffff;
			}
			else
			{
				int negative = 0;
				if (rec < 0)
				{
					rec = ~rec+1;
					negative = 1;
				}
				for (i = 15; i > 0; i--)
				{
					if (rec & (1 << i))
					{
						rec &= ((0xffc0) >> (15 - i));
						i = 0;
					}
				}
				rec = (INT32)(0x7fffffff / (double)rec);
				for (i = 31; i > 0; i--)
				{
					if (rec & (1 << i))
					{
						rec &= ((0xffff8000) >> (31 - i));
						i = 0;
					}
				}
				if (negative)
				{
					rec = ~rec;
				}
			}

			for (i=0; i < 8; i++)
			{
				int element = VEC_EL_2(EL, i);
				ACCUM_L(i) = VREG_S(VS2REG, element);
			}

			rsp.reciprocal_res = rec;

			VREG_S(VDREG, del) = (UINT16)(rsp.reciprocal_res);			// store low part
			break;
		}

		case 0x31:		/* VRCPL */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | ?FFFF | DDDDD | 110001 |
			// ------------------------------------------------------
			//
			// Calculates reciprocal low part

			int del = (VS1REG & 7);
			int sel = VEC_EL_2(EL, 7-del);
			INT32 rec;

			rec = ((UINT16)(VREG_S(VS2REG, sel)) | ((UINT32)(rsp.reciprocal_high) << 16));

			if (rec == 0)
			{
				// divide by zero -> overflow
				rec = 0x7fffffff;
			}
			else
			{
				int negative = 0;
				if (rec < 0)
				{
					if (((UINT32)(rec & 0xffff0000) == 0xffff0000) && ((INT16)(rec & 0xffff) < 0))
					{
						rec = ~rec+1;
					}
					else
					{
						rec = ~rec;
					}
					negative = 1;
				}
				for (i = 31; i > 0; i--)
				{
					if (rec & (1 << i))
					{
						rec &= ((0xffc00000) >> (31 - i));
						i = 0;
					}
				}
				rec = (0x7fffffff / rec);
				for (i = 31; i > 0; i--)
				{
					if (rec & (1 << i))
					{
						rec &= ((0xffff8000) >> (31 - i));
						i = 0;
					}
				}
				if (negative)
				{
					rec = ~rec;
				}
			}

			for (i=0; i < 8; i++)
			{
				int element = VEC_EL_2(EL, i);
				ACCUM_L(i) = VREG_S(VS2REG, element);
			}

			rsp.reciprocal_res = rec;

			VREG_S(VDREG, del) = (UINT16)(rsp.reciprocal_res);			// store low part
			break;
		}

		case 0x32:		/* VRCPH */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | ?FFFF | DDDDD | 110010 |
			// ------------------------------------------------------
			//
			// Calculates reciprocal high part

			int del = (VS1REG & 7);
			int sel = VEC_EL_2(EL, 7-del);

			rsp.reciprocal_high = VREG_S(VS2REG, sel);

			for (i=0; i < 8; i++)
			{
				int element = VEC_EL_2(EL, i);
				ACCUM_L(i) = VREG_S(VS2REG, element);		// perhaps accumulator is used to store the intermediate values ?
			}

			VREG_S(VDREG, del) = (INT16)(rsp.reciprocal_res >> 16);	// store high part
			break;
		}

		case 0x33:		/* VMOV */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | ?FFFF | DDDDD | 110011 |
			// ------------------------------------------------------
			//
			// Moves element from vector to destination vector

			int element = VS1REG & 7;
			VREG_S(VDREG, element) = VREG_S(VS2REG, VEC_EL_2(EL, 7-element));
			break;
		}

		case 0x35:		/* VRSQL */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | ?FFFF | DDDDD | 110101 |
			// ------------------------------------------------------
			//
			// Calculates reciprocal square-root low part

			int del = (VS1REG & 7);
			int sel = VEC_EL_2(EL, 7-del);
			INT32 sqr;

			sqr = (UINT16)(VREG_S(VS2REG, sel)) | ((UINT32)(rsp.square_root_high) << 16);

			if (sqr == 0)
			{
				// square root on 0 -> overflow
				sqr = 0x7fffffff;
			}
			else if (sqr == 0xffff8000)
			{
				// overflow ?
				sqr = 0xffff8000;
			}
			else
			{
				int negative = 0;
				if (sqr < 0)
				{
					if (((UINT32)(sqr & 0xffff0000) == 0xffff0000) && ((INT16)(sqr & 0xffff) < 0))
					{
						sqr = ~sqr+1;
					}
					else
					{
						sqr = ~sqr;
					}
					negative = 1;
				}
				for (i = 31; i > 0; i--)
				{
					if (sqr & (1 << i))
					{
						sqr &= (0xff800000 >> (31 - i));
						i = 0;
					}
				}
				sqr = (INT32)(0x7fffffff / sqrt(sqr));
				for (i = 31; i > 0; i--)
				{
					if (sqr & (1 << i))
					{
						sqr &= (0xffff8000 >> (31 - i));
						i = 0;
					}
				}
				if (negative)
				{
					sqr = ~sqr;
				}
			}

			for (i=0; i < 8; i++)
			{
				int element = VEC_EL_2(EL, i);
				ACCUM_L(i) = VREG_S(VS2REG, element);
			}

			rsp.square_root_res = sqr;

			VREG_S(VDREG, del) = (UINT16)(rsp.square_root_res);			// store low part
			break;
		}

		case 0x36:		/* VRSQH */
		{
			// 31       25  24     20      15      10      5        0
			// ------------------------------------------------------
			// | 010010 | 1 | EEEE | SSSSS | ?FFFF | DDDDD | 110110 |
			// ------------------------------------------------------
			//
			// Calculates reciprocal square-root high part

			int del = (VS1REG & 7);
			int sel = VEC_EL_2(EL, 7-del);

			rsp.square_root_high = VREG_S(VS2REG, sel);

			for (i=0; i < 8; i++)
			{
				int element = VEC_EL_2(EL, i);
				ACCUM_L(i) = VREG_S(VS2REG, element);		// perhaps accumulator is used to store the intermediate values ?
			}

			VREG_S(VDREG, del) = (INT16)(rsp.square_root_res >> 16);	// store high part
			break;
		}

		default:	unimplemented_opcode(op); break;
	}
}

static int rsp_execute(int cycles)
{
	UINT32 op;

	rsp_icount = cycles;

	rsp.pc = 0x4001000 | (rsp.pc & 0xfff);

	while (rsp_icount > 0)
	{
		rsp.ppc = rsp.pc;
		CALL_MAME_DEBUG;

		op = ROPCODE(rsp.pc);
		if (rsp.nextpc != ~0)
		{
			rsp.pc = rsp.nextpc;
			rsp.nextpc = ~0;
		}
		else
		{
			rsp.pc += 4;
		}

		switch (op >> 26)
		{
			case 0x00:	/* SPECIAL */
			{
				switch (op & 0x3f)
				{
					case 0x00:	/* SLL */		if (RDREG) RDVAL = (UINT32)RTVAL << SHIFT; break;
					case 0x02:	/* SRL */		if (RDREG) RDVAL = (UINT32)RTVAL >> SHIFT; break;
					case 0x03:	/* SRA */		if (RDREG) RDVAL = (INT32)RTVAL >> SHIFT; break;
					case 0x04:	/* SLLV */		if (RDREG) RDVAL = (UINT32)RTVAL << (RSVAL & 0x1f); break;
					case 0x06:	/* SRLV */		if (RDREG) RDVAL = (UINT32)RTVAL >> (RSVAL & 0x1f); break;
					case 0x07:	/* SRAV */		if (RDREG) RDVAL = (INT32)RTVAL >> (RSVAL & 0x1f); break;
					case 0x08:	/* JR */		JUMP_PC(RSVAL); break;
					case 0x09:	/* JALR */		JUMP_PC_L(RSVAL, RDREG); break;
					case 0x0d:	/* BREAK */
					{
						sp_set_status(0x3);
						rsp_icount = 0;

#if LOG_INSTRUCTION_EXECUTION
						fprintf(exec_output, "\n---------- break ----------\n\n");
#endif
						break;
					}
					case 0x20:	/* ADD */		if (RDREG) RDVAL = (INT32)(RSVAL + RTVAL); break;
					case 0x21:	/* ADDU */		if (RDREG) RDVAL = (INT32)(RSVAL + RTVAL); break;
					case 0x22:	/* SUB */		if (RDREG) RDVAL = (INT32)(RSVAL - RTVAL); break;
					case 0x23:	/* SUBU */		if (RDREG) RDVAL = (INT32)(RSVAL - RTVAL); break;
					case 0x24:	/* AND */		if (RDREG) RDVAL = RSVAL & RTVAL; break;
					case 0x25:	/* OR */		if (RDREG) RDVAL = RSVAL | RTVAL; break;
					case 0x26:	/* XOR */		if (RDREG) RDVAL = RSVAL ^ RTVAL; break;
					case 0x27:	/* NOR */		if (RDREG) RDVAL = ~(RSVAL | RTVAL); break;
					case 0x2a:	/* SLT */		if (RDREG) RDVAL = (INT32)RSVAL < (INT32)RTVAL; break;
					case 0x2b:	/* SLTU */		if (RDREG) RDVAL = (UINT32)RSVAL < (UINT32)RTVAL; break;
					default:	unimplemented_opcode(op); break;
				}
				break;
			}

			case 0x01:	/* REGIMM */
			{
				switch (RTREG)
				{
					case 0x00:	/* BLTZ */		if ((INT32)(RSVAL) < 0) JUMP_REL(SIMM16); break;
					case 0x01:	/* BGEZ */		if ((INT32)(RSVAL) >= 0) JUMP_REL(SIMM16); break;
					case 0x11:	/* BGEZAL */	if ((INT32)(RSVAL) >= 0) JUMP_REL_L(SIMM16, 31); break;
					default:	unimplemented_opcode(op); break;
				}
				break;
			}

			case 0x02:	/* J */			JUMP_ABS(UIMM26); break;
			case 0x03:	/* JAL */		JUMP_ABS_L(UIMM26, 31); break;
			case 0x04:	/* BEQ */		if (RSVAL == RTVAL) JUMP_REL(SIMM16); break;
			case 0x05:	/* BNE */		if (RSVAL != RTVAL) JUMP_REL(SIMM16); break;
			case 0x06:	/* BLEZ */		if ((INT32)RSVAL <= 0) JUMP_REL(SIMM16); break;
			case 0x07:	/* BGTZ */		if ((INT32)RSVAL > 0) JUMP_REL(SIMM16); break;
			case 0x08:	/* ADDI */		if (RTREG) RTVAL = (INT32)(RSVAL + SIMM16); break;
			case 0x09:	/* ADDIU */		if (RTREG) RTVAL = (INT32)(RSVAL + SIMM16); break;
			case 0x0a:	/* SLTI */		if (RTREG) RTVAL = (INT32)(RSVAL) < ((INT32)SIMM16); break;
			case 0x0b:	/* SLTIU */		if (RTREG) RTVAL = (UINT32)(RSVAL) < (UINT32)((INT32)SIMM16); break;
			case 0x0c:	/* ANDI */		if (RTREG) RTVAL = RSVAL & UIMM16; break;
			case 0x0d:	/* ORI */		if (RTREG) RTVAL = RSVAL | UIMM16; break;
			case 0x0e:	/* XORI */		if (RTREG) RTVAL = RSVAL ^ UIMM16; break;
			case 0x0f:	/* LUI */		if (RTREG) RTVAL = UIMM16 << 16; break;

			case 0x10:	/* COP0 */
			{
				switch ((op >> 21) & 0x1f)
				{
					case 0x00:	/* MFC0 */		if (RTREG) RTVAL = get_cop0_reg(RDREG); break;
					case 0x04:	/* MTC0 */		set_cop0_reg(RDREG, RTVAL); break;
					default:	unimplemented_opcode(op); break;
				}
				break;
			}

			case 0x12:	/* COP2 */
			{
				switch ((op >> 21) & 0x1f)
				{
					case 0x00:	/* MFC2 */
					{
						// 31       25      20      15      10     6         0
						// ---------------------------------------------------
						// | 010010 | 00000 | TTTTT | DDDDD | IIII | 0000000 |
						// ---------------------------------------------------
						//

						int el = (op >> 7) & 0xf;
						UINT16 b1 = VREG_B(VS1REG, (el+0) & 0xf);
						UINT16 b2 = VREG_B(VS1REG, (el+1) & 0xf);
						if (RTREG) RTVAL = (INT32)(INT16)((b1 << 8) | (b2));
						break;
					}
					case 0x02:	/* CFC2 */
					{
						// 31       25      20      15      10            0
						// ------------------------------------------------
						// | 010010 | 00010 | TTTTT | DDDDD | 00000000000 |
						// ------------------------------------------------
						//

						if (RTREG) RTVAL = rsp.flag[RDREG];
						break;
					}
					case 0x04:	/* MTC2 */
					{
						// 31       25      20      15      10     6         0
						// ---------------------------------------------------
						// | 010010 | 00100 | TTTTT | DDDDD | IIII | 0000000 |
						// ---------------------------------------------------
						//

						int el = (op >> 7) & 0xf;
						VREG_B(VS1REG, (el+0) & 0xf) = (RTVAL >> 8) & 0xff;
						VREG_B(VS1REG, (el+1) & 0xf) = (RTVAL >> 0) & 0xff;
						break;
					}
					case 0x06:	/* CTC2 */
					{
						// 31       25      20      15      10            0
						// ------------------------------------------------
						// | 010010 | 00110 | TTTTT | DDDDD | 00000000000 |
						// ------------------------------------------------
						//

						rsp.flag[RDREG] = RTVAL & 0xffff;
						break;
					}

					case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
					case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
					{
						handle_vector_ops(op);
						break;
					}

					default:	unimplemented_opcode(op); break;
				}
				break;
			}

			case 0x20:	/* LB */		if (RTREG) RTVAL = (INT32)(INT8)READ8(RSVAL + SIMM16); break;
			case 0x21:	/* LH */		if (RTREG) RTVAL = (INT32)(INT16)READ16(RSVAL + SIMM16); break;
			case 0x23:	/* LW */		if (RTREG) RTVAL = READ32(RSVAL + SIMM16); break;
			case 0x24:	/* LBU */		if (RTREG) RTVAL = (UINT8)READ8(RSVAL + SIMM16); break;
			case 0x25:	/* LHU */		if (RTREG) RTVAL = (UINT16)READ16(RSVAL + SIMM16); break;
			case 0x28:	/* SB */		WRITE8(RSVAL + SIMM16, RTVAL); break;
			case 0x29:	/* SH */		WRITE16(RSVAL + SIMM16, RTVAL); break;
			case 0x2b:	/* SW */		WRITE32(RSVAL + SIMM16, RTVAL); break;
			case 0x32:	/* LWC2 */		handle_lwc2(op); break;
			case 0x3a:	/* SWC2 */		handle_swc2(op); break;

			default:
			{
				unimplemented_opcode(op);
				break;
			}
		}

#if LOG_INSTRUCTION_EXECUTION
		{
			int i, l;
			static UINT32 prev_regs[32];
			static VECTOR_REG prev_vecs[32];
			char string[200];
			rsp_dasm_one(string, rsp.ppc, op);

			fprintf(exec_output, "%08X: %s", rsp.ppc, string);

			l = strlen(string);
			if (l < 36)
			{
				for (i=l; i < 36; i++)
				{
					fprintf(exec_output, " ");
				}
			}

			fprintf(exec_output, "| ");

			for (i=0; i < 32; i++)
			{
				if (rsp.r[i] != prev_regs[i])
				{
					fprintf(exec_output, "R%d: %08X ", i, rsp.r[i]);
				}
				prev_regs[i] = rsp.r[i];
			}

			for (i=0; i < 32; i++)
			{
				if (rsp.v[i].d[0] != prev_vecs[i].d[0] || rsp.v[i].d[1] != prev_vecs[i].d[1])
				{
					fprintf(exec_output, "V%d: %04X|%04X|%04X|%04X|%04X|%04X|%04X|%04X ", i,
					(UINT16)VREG_S(i,0), (UINT16)VREG_S(i,1), (UINT16)VREG_S(i,2), (UINT16)VREG_S(i,3), (UINT16)VREG_S(i,4), (UINT16)VREG_S(i,5), (UINT16)VREG_S(i,6), (UINT16)VREG_S(i,7));
				}
				prev_vecs[i].d[0] = rsp.v[i].d[0];
				prev_vecs[i].d[1] = rsp.v[i].d[1];
			}

			fprintf(exec_output, "\n");

		}
#endif

		--rsp_icount;
	}

	return cycles - rsp_icount;
}




/*****************************************************************************/

static void rsp_get_context(void *dst)
{
	/* copy the context */
	if (dst)
		*(RSP_REGS *)dst = rsp;
}


static void rsp_set_context(void *src)
{
	/* copy the context */
	if (src)
		rsp = *(RSP_REGS *)src;
}


/*****************************************************************************/

#ifdef MAME_DEBUG
static offs_t rsp_dasm(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram)
{
	UINT32 op = LITTLE_ENDIANIZE_INT32(*(UINT32 *)opram);
	return rsp_dasm_one(buffer, pc, op);
}
#endif /* MAME_DEBUG */

/*****************************************************************************/

static void rsp_set_info(UINT32 state, cpuinfo *info)
{
	switch (state)
	{
		/* --- the following bits of info are set as 64-bit signed integers --- */
		case CPUINFO_INT_PC:
		case CPUINFO_INT_REGISTER + RSP_PC:				rsp.pc = info->i;						break;

		case CPUINFO_INT_REGISTER + RSP_R0:				rsp.r[0] = info->i;						break;
		case CPUINFO_INT_REGISTER + RSP_R1:				rsp.r[1] = info->i;						break;
		case CPUINFO_INT_REGISTER + RSP_R2:				rsp.r[2] = info->i;						break;
		case CPUINFO_INT_REGISTER + RSP_R3:				rsp.r[3] = info->i;						break;
		case CPUINFO_INT_REGISTER + RSP_R4:				rsp.r[4] = info->i;						break;
		case CPUINFO_INT_REGISTER + RSP_R5:				rsp.r[5] = info->i;						break;
		case CPUINFO_INT_REGISTER + RSP_R6:				rsp.r[6] = info->i;						break;
		case CPUINFO_INT_REGISTER + RSP_R7:				rsp.r[7] = info->i;						break;
		case CPUINFO_INT_REGISTER + RSP_R8:				rsp.r[8] = info->i;						break;
		case CPUINFO_INT_REGISTER + RSP_R9:				rsp.r[9] = info->i;						break;
		case CPUINFO_INT_REGISTER + RSP_R10:			rsp.r[10] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R11:			rsp.r[11] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R12:			rsp.r[12] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R13:			rsp.r[13] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R14:			rsp.r[14] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R15:			rsp.r[15] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R16:			rsp.r[16] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R17:			rsp.r[17] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R18:			rsp.r[18] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R19:			rsp.r[19] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R20:			rsp.r[20] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R21:			rsp.r[21] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R22:			rsp.r[22] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R23:			rsp.r[23] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R24:			rsp.r[24] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R25:			rsp.r[25] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R26:			rsp.r[26] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R27:			rsp.r[27] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R28:			rsp.r[28] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R29:			rsp.r[29] = info->i;					break;
		case CPUINFO_INT_REGISTER + RSP_R30:			rsp.r[30] = info->i;					break;
		case CPUINFO_INT_SP:
		case CPUINFO_INT_REGISTER + RSP_R31:			rsp.r[31] = info->i;					break;
	}
}

void rsp_get_info(UINT32 state, cpuinfo *info)
{
	switch(state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */
		case CPUINFO_INT_CONTEXT_SIZE:					info->i = sizeof(rsp);					break;
		case CPUINFO_INT_INPUT_LINES:					info->i = 1;							break;
		case CPUINFO_INT_DEFAULT_IRQ_VECTOR:			info->i = 0;							break;
		case CPUINFO_INT_ENDIANNESS:					info->i = CPU_IS_LE;					break;
		case CPUINFO_INT_CLOCK_DIVIDER:					info->i = 1;							break;
		case CPUINFO_INT_MIN_INSTRUCTION_BYTES:			info->i = 4;							break;
		case CPUINFO_INT_MAX_INSTRUCTION_BYTES:			info->i = 4;							break;
		case CPUINFO_INT_MIN_CYCLES:					info->i = 1;							break;
		case CPUINFO_INT_MAX_CYCLES:					info->i = 1;							break;

		case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_PROGRAM:	info->i = 32;					break;
		case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_PROGRAM: info->i = 32;					break;
		case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_PROGRAM: info->i = 0;					break;
		case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_DATA:	info->i = 0;					break;
		case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_DATA: 	info->i = 0;					break;
		case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_DATA: 	info->i = 0;					break;
		case CPUINFO_INT_DATABUS_WIDTH + ADDRESS_SPACE_IO:		info->i = 0;					break;
		case CPUINFO_INT_ADDRBUS_WIDTH + ADDRESS_SPACE_IO: 		info->i = 0;					break;
		case CPUINFO_INT_ADDRBUS_SHIFT + ADDRESS_SPACE_IO: 		info->i = 0;					break;

		case CPUINFO_INT_INPUT_STATE:					info->i = CLEAR_LINE;					break;

		case CPUINFO_INT_PREVIOUSPC:					info->i = rsp.ppc;						break;

		case CPUINFO_INT_PC:	/* intentional fallthrough */
		case CPUINFO_INT_REGISTER + RSP_PC:				info->i = rsp.pc;						break;

		case CPUINFO_INT_REGISTER + RSP_R0:				info->i = rsp.r[0];						break;
		case CPUINFO_INT_REGISTER + RSP_R1:				info->i = rsp.r[1];						break;
		case CPUINFO_INT_REGISTER + RSP_R2:				info->i = rsp.r[2];						break;
		case CPUINFO_INT_REGISTER + RSP_R3:				info->i = rsp.r[3];						break;
		case CPUINFO_INT_REGISTER + RSP_R4:				info->i = rsp.r[4];						break;
		case CPUINFO_INT_REGISTER + RSP_R5:				info->i = rsp.r[5];						break;
		case CPUINFO_INT_REGISTER + RSP_R6:				info->i = rsp.r[6];						break;
		case CPUINFO_INT_REGISTER + RSP_R7:				info->i = rsp.r[7];						break;
		case CPUINFO_INT_REGISTER + RSP_R8:				info->i = rsp.r[8];						break;
		case CPUINFO_INT_REGISTER + RSP_R9:				info->i = rsp.r[9];						break;
		case CPUINFO_INT_REGISTER + RSP_R10:			info->i = rsp.r[10];					break;
		case CPUINFO_INT_REGISTER + RSP_R11:			info->i = rsp.r[11];					break;
		case CPUINFO_INT_REGISTER + RSP_R12:			info->i = rsp.r[12];					break;
		case CPUINFO_INT_REGISTER + RSP_R13:			info->i = rsp.r[13];					break;
		case CPUINFO_INT_REGISTER + RSP_R14:			info->i = rsp.r[14];					break;
		case CPUINFO_INT_REGISTER + RSP_R15:			info->i = rsp.r[15];					break;
		case CPUINFO_INT_REGISTER + RSP_R16:			info->i = rsp.r[16];					break;
		case CPUINFO_INT_REGISTER + RSP_R17:			info->i = rsp.r[17];					break;
		case CPUINFO_INT_REGISTER + RSP_R18:			info->i = rsp.r[18];					break;
		case CPUINFO_INT_REGISTER + RSP_R19:			info->i = rsp.r[19];					break;
		case CPUINFO_INT_REGISTER + RSP_R20:			info->i = rsp.r[20];					break;
		case CPUINFO_INT_REGISTER + RSP_R21:			info->i = rsp.r[21];					break;
		case CPUINFO_INT_REGISTER + RSP_R22:			info->i = rsp.r[22];					break;
		case CPUINFO_INT_REGISTER + RSP_R23:			info->i = rsp.r[23];					break;
		case CPUINFO_INT_REGISTER + RSP_R24:			info->i = rsp.r[24];					break;
		case CPUINFO_INT_REGISTER + RSP_R25:			info->i = rsp.r[25];					break;
		case CPUINFO_INT_REGISTER + RSP_R26:			info->i = rsp.r[26];					break;
		case CPUINFO_INT_REGISTER + RSP_R27:			info->i = rsp.r[27];					break;
		case CPUINFO_INT_REGISTER + RSP_R28:			info->i = rsp.r[28];					break;
		case CPUINFO_INT_REGISTER + RSP_R29:			info->i = rsp.r[29];					break;
		case CPUINFO_INT_REGISTER + RSP_R30:			info->i = rsp.r[30];					break;
		case CPUINFO_INT_SP:
		case CPUINFO_INT_REGISTER + RSP_R31:			info->i = rsp.r[31];					break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case CPUINFO_PTR_GET_CONTEXT:					info->getcontext = rsp_get_context;		break;
		case CPUINFO_PTR_SET_CONTEXT:					info->setcontext = rsp_set_context;		break;
		case CPUINFO_PTR_SET_INFO:						info->setinfo = rsp_set_info;			break;
		case CPUINFO_PTR_INIT:							info->init = rsp_init;					break;
		case CPUINFO_PTR_RESET:							info->reset = rsp_reset;				break;
		case CPUINFO_PTR_EXIT:							info->exit = rsp_exit;					break;
		case CPUINFO_PTR_EXECUTE:						info->execute = rsp_execute;			break;
		case CPUINFO_PTR_BURN:							info->burn = NULL;						break;
#ifdef MAME_DEBUG
		case CPUINFO_PTR_DISASSEMBLE:					info->disassemble = rsp_dasm;			break;
#endif /* MAME_DEBUG */
		case CPUINFO_PTR_INSTRUCTION_COUNTER:			info->icount = &rsp_icount;				break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case CPUINFO_STR_NAME:							strcpy(info->s, "RSP");					break;
		case CPUINFO_STR_CORE_FAMILY:					strcpy(info->s, "RSP");					break;
		case CPUINFO_STR_CORE_VERSION:					strcpy(info->s, "1.0");					break;
		case CPUINFO_STR_CORE_FILE:						strcpy(info->s, __FILE__);				break;
		case CPUINFO_STR_CORE_CREDITS:					strcpy(info->s, "Copyright (C) 2005");	break;

		case CPUINFO_STR_FLAGS:							strcpy(info->s, " ");					break;

		case CPUINFO_STR_REGISTER + RSP_PC:				sprintf(info->s, "PC: %08X", rsp.pc);	break;

		case CPUINFO_STR_REGISTER + RSP_R0:				sprintf(info->s, "R0: %08X", rsp.r[0]); break;
		case CPUINFO_STR_REGISTER + RSP_R1:				sprintf(info->s, "R1: %08X", rsp.r[1]); break;
		case CPUINFO_STR_REGISTER + RSP_R2:				sprintf(info->s, "R2: %08X", rsp.r[2]); break;
		case CPUINFO_STR_REGISTER + RSP_R3:				sprintf(info->s, "R3: %08X", rsp.r[3]); break;
		case CPUINFO_STR_REGISTER + RSP_R4:				sprintf(info->s, "R4: %08X", rsp.r[4]); break;
		case CPUINFO_STR_REGISTER + RSP_R5:				sprintf(info->s, "R5: %08X", rsp.r[5]); break;
		case CPUINFO_STR_REGISTER + RSP_R6:				sprintf(info->s, "R6: %08X", rsp.r[6]); break;
		case CPUINFO_STR_REGISTER + RSP_R7:				sprintf(info->s, "R7: %08X", rsp.r[7]); break;
		case CPUINFO_STR_REGISTER + RSP_R8:				sprintf(info->s, "R8: %08X", rsp.r[8]); break;
		case CPUINFO_STR_REGISTER + RSP_R9:				sprintf(info->s, "R9: %08X", rsp.r[9]); break;
		case CPUINFO_STR_REGISTER + RSP_R10:			sprintf(info->s, "R10: %08X", rsp.r[10]); break;
		case CPUINFO_STR_REGISTER + RSP_R11:			sprintf(info->s, "R11: %08X", rsp.r[11]); break;
		case CPUINFO_STR_REGISTER + RSP_R12:			sprintf(info->s, "R12: %08X", rsp.r[12]); break;
		case CPUINFO_STR_REGISTER + RSP_R13:			sprintf(info->s, "R13: %08X", rsp.r[13]); break;
		case CPUINFO_STR_REGISTER + RSP_R14:			sprintf(info->s, "R14: %08X", rsp.r[14]); break;
		case CPUINFO_STR_REGISTER + RSP_R15:			sprintf(info->s, "R15: %08X", rsp.r[15]); break;
		case CPUINFO_STR_REGISTER + RSP_R16:			sprintf(info->s, "R16: %08X", rsp.r[16]); break;
		case CPUINFO_STR_REGISTER + RSP_R17:			sprintf(info->s, "R17: %08X", rsp.r[17]); break;
		case CPUINFO_STR_REGISTER + RSP_R18:			sprintf(info->s, "R18: %08X", rsp.r[18]); break;
		case CPUINFO_STR_REGISTER + RSP_R19:			sprintf(info->s, "R19: %08X", rsp.r[19]); break;
		case CPUINFO_STR_REGISTER + RSP_R20:			sprintf(info->s, "R20: %08X", rsp.r[20]); break;
		case CPUINFO_STR_REGISTER + RSP_R21:			sprintf(info->s, "R21: %08X", rsp.r[21]); break;
		case CPUINFO_STR_REGISTER + RSP_R22:			sprintf(info->s, "R22: %08X", rsp.r[22]); break;
		case CPUINFO_STR_REGISTER + RSP_R23:			sprintf(info->s, "R23: %08X", rsp.r[23]); break;
		case CPUINFO_STR_REGISTER + RSP_R24:			sprintf(info->s, "R24: %08X", rsp.r[24]); break;
		case CPUINFO_STR_REGISTER + RSP_R25:			sprintf(info->s, "R25: %08X", rsp.r[25]); break;
		case CPUINFO_STR_REGISTER + RSP_R26:			sprintf(info->s, "R26: %08X", rsp.r[26]); break;
		case CPUINFO_STR_REGISTER + RSP_R27:			sprintf(info->s, "R27: %08X", rsp.r[27]); break;
		case CPUINFO_STR_REGISTER + RSP_R28:			sprintf(info->s, "R28: %08X", rsp.r[28]); break;
		case CPUINFO_STR_REGISTER + RSP_R29:			sprintf(info->s, "R29: %08X", rsp.r[29]); break;
		case CPUINFO_STR_REGISTER + RSP_R30:			sprintf(info->s, "R30: %08X", rsp.r[30]); break;
		case CPUINFO_STR_REGISTER + RSP_R31:			sprintf(info->s, "R31: %08X", rsp.r[31]); break;
	}
}
