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

    Written by Ville Linde
*/

#include "cpuintrf.h"
#include "debugger.h"
#include "rsp.h"

CPU_DISASSEMBLE( rsp );

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

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

#define PRINT_ACCUM(x)     mame_printf_debug("A%d: %08X|%08X\n", (x), \
                            (UINT32)( ( ACCUM(x) >> 32 ) & 0x00000000ffffffff ),    \
                            (UINT32)(   ACCUM(x)         & 0x00000000ffffffff ))

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



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

typedef struct _rsp_state rsp_state;
struct _rsp_state
{
	const rsp_config *config;
	FILE *exec_output;

	UINT32 pc;
	UINT32 r[32];
	VECTOR_REG v[32];
	UINT16 flag[4];
	UINT32 sr;
    UINT32 step_count;

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

	UINT32 ppc;
	UINT32 nextpc;

	cpu_irq_callback irq_callback;
	const device_config *device;
	const address_space *program;
	int icount;
};

INLINE rsp_state *get_safe_token(const device_config *device)
{
	assert(device != NULL);
	assert(device->token != NULL);
	assert(device->type == CPU);
	assert(cpu_get_type(device) == CPU_RSP);
	return (rsp_state *)device->token;
}


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

#define RSVAL		(cpustate->r[RSREG])
#define RTVAL		(cpustate->r[RTREG])
#define RDVAL		(cpustate->r[RDREG])

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


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


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


#define S_VREG_B(offset)			(((15 - (offset)) & 0x07) << 3)
#define S_VREG_S(offset)			(((7 - (offset)) & 0x03) << 4)
#define S_VREG_L(offset)			(((3 - (offset)) & 0x01) << 5)

#define M_VREG_B(offset)			((UINT64)0x00FF << S_VREG_B(offset))
#define M_VREG_S(offset)			((UINT64)0x0000FFFFul << S_VREG_S(offset))
#define M_VREG_L(offset)			((UINT64)0x00000000FFFFFFFFull << S_VREG_L(offset))

#define R_VREG_B(reg, offset)		((cpustate->v[(reg)].d[(15 - (offset)) >> 3] >> S_VREG_B(offset)) & 0x00FF)
#define R_VREG_S(reg, offset)		(INT16)((cpustate->v[(reg)].d[(7 - (offset)) >> 2] >> S_VREG_S(offset)) & 0x0000FFFFul)
#define R_VREG_L(reg, offset)		((cpustate->v[(reg)].d[(3 - (offset)) >> 1] >> S_VREG_L(offset)) & 0x00000000FFFFFFFFull)

#define W_VREG_B(reg, offset, val)	(cpustate->v[(reg)].d[(15 - (offset)) >> 3] = (cpustate->v[(reg)].d[(15 - (offset)) >> 3] & ~M_VREG_B(offset)) | (M_VREG_B(offset) & ((UINT64)(val) << S_VREG_B(offset))))
#define W_VREG_S(reg, offset, val)	(cpustate->v[(reg)].d[(7 - (offset)) >> 2] = (cpustate->v[(reg)].d[(7 - (offset)) >> 2] & ~M_VREG_S(offset)) | (M_VREG_S(offset) & ((UINT64)(val) << S_VREG_S(offset))))
#define W_VREG_L(reg, offset, val)	(cpustate->v[(reg)].d[(3 - (offset)) >> 1] = (cpustate->v[(reg)].d[(3 - (offset)) >> 1] & ~M_VREG_L(offset)) | (M_VREG_L(offset) & ((UINT64)(val) << S_VREG_L(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)				(cpustate->accum[(7-(x))])

#define S_ACCUM_H				(3 << 4)
#define S_ACCUM_M				(2 << 4)
#define S_ACCUM_L				(1 << 4)

#define M_ACCUM_H				(((INT64)0x0000FFFF) << S_ACCUM_H)
#define M_ACCUM_M				(((INT64)0x0000FFFF) << S_ACCUM_M)
#define M_ACCUM_L				(((INT64)0x0000FFFF) << S_ACCUM_L)

#define R_ACCUM_H(x)			((INT16)((ACCUM(x) >> S_ACCUM_H) & 0x00FFFF))
#define R_ACCUM_M(x)			((INT16)((ACCUM(x) >> S_ACCUM_M) & 0x00FFFF))
#define R_ACCUM_L(x)			((INT16)((ACCUM(x) >> S_ACCUM_L) & 0x00FFFF))

#define W_ACCUM_H(x, y)			(ACCUM(x) = (ACCUM(x) & ~M_ACCUM_H) | (M_ACCUM_H & ((INT64)(y) << S_ACCUM_H)))
#define W_ACCUM_M(x, y)			(ACCUM(x) = (ACCUM(x) & ~M_ACCUM_M) | (M_ACCUM_M & ((INT64)(y) << S_ACCUM_M)))
#define W_ACCUM_L(x, y)			(ACCUM(x) = (ACCUM(x) & ~M_ACCUM_L) | (M_ACCUM_L & ((INT64)(y) << S_ACCUM_L)))

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

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

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


#define ROPCODE(pc)		memory_decrypted_read_dword(cpustate->program, pc)

INLINE UINT8 READ8(rsp_state *cpustate, UINT32 address)
{
	address = 0x04000000 | (address & 0xfff);
	return memory_read_byte_32be(cpustate->program, address);
}

INLINE UINT16 READ16(rsp_state *cpustate, UINT32 address)
{
	address = 0x04000000 | (address & 0xfff);

	if (address & 1)
	{
		//osd_die("RSP: READ16: unaligned %08X at %08X\n", address, cpustate->ppc);
		return ((memory_read_byte_32be(cpustate->program, address+0) & 0xff) << 8) | (memory_read_byte_32be(cpustate->program, address+1) & 0xff);
	}

	return memory_read_word_32be(cpustate->program, address);
}

INLINE UINT32 READ32(rsp_state *cpustate, UINT32 address)
{
	address = 0x04000000 | (address & 0xfff);

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

	return memory_read_dword_32be(cpustate->program, address);
}

INLINE void WRITE8(rsp_state *cpustate, UINT32 address, UINT8 data)
{
	address = 0x04000000 | (address & 0xfff);
	memory_write_byte_32be(cpustate->program, address, data);
}

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

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

	memory_write_word_32be(cpustate->program, address, data);
}

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

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

	memory_write_dword_32be(cpustate->program, address, data);
}

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

static UINT32 get_cop0_reg(rsp_state *cpustate, int reg)
{
	if (reg >= 0 && reg < 8)
	{
		return (cpustate->config->sp_reg_r)(cpustate->program, reg, 0x00000000);
	}
	else if (reg >= 8 && reg < 16)
	{
		return (cpustate->config->dp_reg_r)(cpustate->program, reg - 8, 0x00000000);
	}
	else
	{
		fatalerror("RSP: get_cop0_reg: %d", reg);
	}
}

static void set_cop0_reg(rsp_state *cpustate, int reg, UINT32 data)
{
	if (reg >= 0 && reg < 8)
	{
		(cpustate->config->sp_reg_w)(cpustate->program, reg, data, 0x00000000);
	}
	else if (reg >= 8 && reg < 16)
	{
		(cpustate->config->dp_reg_w)(cpustate->program, reg - 8, data, 0x00000000);
	}
	else
	{
		fatalerror("RSP: set_cop0_reg: %d, %08X\n", reg, data);
	}
}

static void unimplemented_opcode(rsp_state *cpustate, UINT32 op)
{
	if ((cpustate->device->machine->debug_flags & DEBUG_FLAG_ENABLED) != 0)
	{
		char string[200];
		rsp_dasm_one(string, cpustate->ppc, op);
		mame_printf_debug("%08X: %s\n", cpustate->ppc, string);
	}

#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(cpustate, 0x04000000 + i), dmem);
		}
		fclose(dmem);
	}
#endif

	fatalerror("RSP: unknown opcode %02X (%08X) at %08X\n", op >> 26, op, cpustate->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 CPU_INIT( rsp )
{
	rsp_state *cpustate = get_safe_token(device);
    int regIdx;
    int accumIdx;
	cpustate->config = (const rsp_config *)device->static_config;

	if (LOG_INSTRUCTION_EXECUTION)
		cpustate->exec_output = fopen("rsp_execute.txt", "wt");

	cpustate->irq_callback = irqcallback;
	cpustate->device = device;
	cpustate->program = memory_find_address_space(device, ADDRESS_SPACE_PROGRAM);

#if 1
    // Inaccurate.  RSP registers power on to a random state...
	for(regIdx = 0; regIdx < 32; regIdx++ )
	{
		cpustate->r[regIdx] = 0;
		cpustate->v[regIdx].d[0] = 0;
		cpustate->v[regIdx].d[1] = 0;
	}
	cpustate->flag[0] = 0;
	cpustate->flag[1] = 0;
	cpustate->flag[2] = 0;
	cpustate->flag[3] = 0;
	cpustate->square_root_res = 0;
	cpustate->square_root_high = 0;
	cpustate->reciprocal_res = 0;
	cpustate->reciprocal_high = 0;
#endif

    // ...except for the accumulators.
    // We're not calling mame_rand() because initializing something with mame_rand()
    //   makes me retch uncontrollably.
    for(accumIdx = 0; accumIdx < 8; accumIdx++ )
    {
        cpustate->accum[accumIdx] = 0;
    }

	cpustate->sr = RSP_STATUS_HALT;
    cpustate->step_count = 0;
}

static CPU_EXIT( rsp )
{
	rsp_state *cpustate = get_safe_token(device);

#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;
#if 0
		dmem = fopen("rsp_dmem.txt", "wt");

		for (i=0; i < 0x1000; i+=4)
		{
			fprintf(dmem, "%08X: %08X\n", 0x04000000 + i, READ32(cpustate, 0x04000000 + i));
		}
		fclose(dmem);
#endif
		dmem = fopen("rsp_dmem.bin", "wb");

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

	if (cpustate->exec_output)
		fclose(cpustate->exec_output);
	cpustate->exec_output = NULL;
}

static CPU_RESET( rsp )
{
	rsp_state *cpustate = get_safe_token(device);
	cpustate->nextpc = ~0;
}

static void handle_lwc2(rsp_state *cpustate, 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) ? cpustate->r[base] + offset : offset;
			W_VREG_B(dest, index, READ8(cpustate, 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) ? cpustate->r[base] + (offset * 2) : (offset * 2);

			end = index + 2;

			for (i=index; i < end; i++)
			{
				W_VREG_B(dest, i, READ8(cpustate, 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) ? cpustate->r[base] + (offset * 4) : (offset * 4);

			end = index + 4;

			for (i=index; i < end; i++)
			{
				W_VREG_B(dest, i, READ8(cpustate, 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) ? cpustate->r[base] + (offset * 8) : (offset * 8);

			end = index + 8;

			for (i=index; i < end; i++)
			{
				W_VREG_B(dest, i, READ8(cpustate, 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) ? cpustate->r[base] + (offset * 16) : (offset * 16);

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

			for (i=index; i < end; i++)
			{
				W_VREG_B(dest, i, READ8(cpustate, 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) ? cpustate->r[base] + (offset * 16) : (offset * 16);

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

			for (i=index; i < end; i++)
			{
				W_VREG_B(dest, i, READ8(cpustate, 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) ? cpustate->r[base] + (offset * 8) : (offset * 8);

			for (i=0; i < 8; i++)
			{
				W_VREG_S(dest, i, READ8(cpustate, 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) ? cpustate->r[base] + (offset * 8) : (offset * 8);

			for (i=0; i < 8; i++)
			{
				W_VREG_S(dest, i, READ8(cpustate, 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) ? cpustate->r[base] + (offset * 16) : (offset * 16);

			for (i=0; i < 8; i++)
			{
				W_VREG_S(dest, i, READ8(cpustate, 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, cpustate->ppc);

			ea = (base) ? cpustate->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", cpustate->ppc);

			end = (index >> 1) + 4;

			for (i=index >> 1; i < end; i++)
			{
				W_VREG_S(dest, i, READ8(cpustate, 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) ? cpustate->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", cpustate->ppc);

			end = (16 - index) + 16;

			for (i=(16 - index); i < end; i++)
			{
				W_VREG_B(dest, i & 0xf, READ8(cpustate, 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) ? cpustate->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);
				W_VREG_B(i, (element & 0xf), READ8(cpustate, ea));
				W_VREG_B(i, ((element+1) & 0xf), READ8(cpustate, ea+1));

				ea += 2;
			}
			break;
		}

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

static void handle_swc2(rsp_state *cpustate, 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) ? cpustate->r[base] + offset : offset;
			WRITE8(cpustate, ea, R_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) ? cpustate->r[base] + (offset * 2) : (offset * 2);

			end = index + 2;

			for (i=index; i < end; i++)
			{
				WRITE8(cpustate, ea, R_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) ? cpustate->r[base] + (offset * 4) : (offset * 4);

			end = index + 4;

			for (i=index; i < end; i++)
			{
				WRITE8(cpustate, ea, R_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) ? cpustate->r[base] + (offset * 8) : (offset * 8);

			end = index + 8;

			for (i=index; i < end; i++)
			{
				WRITE8(cpustate, ea, R_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) ? cpustate->r[base] + (offset * 16) : (offset * 16);

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

			for (i=index; i < end; i++)
			{
				WRITE8(cpustate, ea, R_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) ? cpustate->r[base] + (offset * 16) : (offset * 16);

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

			for (i=index; i < end; i++)
			{
				WRITE8(cpustate, ea, R_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) ? cpustate->r[base] + (offset * 8) : (offset * 8);
			end = index + 8;

			for (i=index; i < end; i++)
			{
				if ((i & 0xf) < 8)
				{
					WRITE8(cpustate, ea, R_VREG_B(dest, ((i & 0xf) << 1)));
				}
				else
				{
					WRITE8(cpustate, ea, R_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) ? cpustate->r[base] + (offset * 8) : (offset * 8);
			end = index + 8;

			for (i=index; i < end; i++)
			{
				if ((i & 0xf) < 8)
				{
					WRITE8(cpustate, ea, R_VREG_S(dest, (i & 0x7)) >> 7);
				}
				else
				{
					WRITE8(cpustate, ea, R_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) ? cpustate->r[base] + (offset * 16) : (offset * 16);

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

				WRITE8(cpustate, 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, cpustate->ppc);

			ea = (base) ? cpustate->r[base] + (offset * 16) : (offset * 16);

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

			end = (index >> 1) + 4;

			for (i=index >> 1; i < end; i++)
			{
				WRITE8(cpustate, ea + (eaoffset & 0xf), R_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) ? cpustate->r[base] + (offset * 16) : (offset * 16);

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

			end = index + 16;

			for (i=index; i < end; i++)
			{
				WRITE8(cpustate, ea + (eaoffset & 0xf), R_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;
			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, cpustate->ppc);

			ea = (base) ? cpustate->r[base] + (offset * 16) : (offset * 16);

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

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

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

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

INLINE UINT16 SATURATE_ACCUM(rsp_state *cpustate, int accum, int slice, UINT16 negative, UINT16 positive)
{
	if ((INT16)R_ACCUM_H(accum) < 0)
	{
		if ((UINT16)(R_ACCUM_H(accum)) != 0xffff)
		{
			return negative;
		}
		else
		{
			if ((INT16)R_ACCUM_M(accum) >= 0)
			{
				return negative;
			}
			else
			{
				if (slice == 0)
				{
					return R_ACCUM_L(accum);
				}
				else if (slice == 1)
				{
					return R_ACCUM_M(accum);
				}
			}
		}
	}
	else
	{
		if ((UINT16)(R_ACCUM_H(accum)) != 0)
		{
			return positive;
		}
		else
		{
			if ((INT16)R_ACCUM_M(accum) < 0)
			{
				return positive;
			}
			else
			{
				if (slice == 0)
				{
					return R_ACCUM_L(accum);
				}
				else
				{
					return R_ACCUM_M(accum);
				}
			}
		}
	}

	return 0;
}

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

#if 0
static float float_round(float input)
{
    INT32 integer = (INT32)input;
    float fraction = input - (float)integer;
    float output = 0.0f;
    if( fraction >= 0.5f )
    {
        output = (float)( integer + 1 );
    }
    else
    {
        output = (float)integer;
    }
    return output;
}
#endif

static void handle_vector_ops(rsp_state *cpustate, 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)R_VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)R_VREG_S(VS2REG, sel);
				if (s1 == -32768 && s2 == -32768)
				{
					// overflow
					W_ACCUM_H(del, 0);
					W_ACCUM_M(del, -32768);
					W_ACCUM_L(del, -32768);
					vres[del] = 0x7fff;
				}
				else
				{
					INT64 r =  s1 * s2 * 2;
					r += 0x8000;	// rounding ?
					W_ACCUM_H(del, (r < 0) ? 0xffff : 0);		// sign-extend to 48-bit
					W_ACCUM_M(del, (INT16)(r >> 16));
					W_ACCUM_L(del, (UINT16)(r));
					vres[del] = R_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)R_VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)R_VREG_S(VS2REG, sel);
				INT64 r = s1 * s2 * 2;
				r += 0x8000;	// rounding ?

				W_ACCUM_H(del, (UINT16)(r >> 32));
				W_ACCUM_M(del, (UINT16)(r >> 16));
				W_ACCUM_L(del, (UINT16)(r));

				if (r < 0)
				{
					vres[del] = 0;
				}
				else if (((INT16)(R_ACCUM_H(del)) ^ (INT16)(R_ACCUM_M(del))) < 0)
				{
					vres[del] = -1;
				}
				else
				{
					vres[del] = R_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)R_VREG_S(VS1REG, del);
				UINT32 s2 = (UINT32)(UINT16)R_VREG_S(VS2REG, sel);
				UINT32 r = s1 * s2;

				W_ACCUM_H(del, 0);
				W_ACCUM_M(del, 0);
				W_ACCUM_L(del, (UINT16)(r >> 16));

				vres[del] = R_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)R_VREG_S(VS1REG, del);
				INT32 s2 = (UINT16)R_VREG_S(VS2REG, sel);	// not sign-extended
				INT32 r =  s1 * s2;

				W_ACCUM_H(del, (r < 0) ? 0xffff : 0);		// sign-extend to 48-bit
				W_ACCUM_M(del, (INT16)(r >> 16));
				W_ACCUM_L(del, (UINT16)(r));

				vres[del] = R_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)R_VREG_S(VS1REG, del);		// not sign-extended
				INT32 s2 = (INT32)(INT16)R_VREG_S(VS2REG, sel);
				INT32 r = s1 * s2;

				W_ACCUM_H(del, (r < 0) ? 0xffff : 0);		// sign-extend to 48-bit
				W_ACCUM_M(del, (INT16)(r >> 16));
				W_ACCUM_L(del, (UINT16)(r));

				vres[del] = R_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)R_VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)R_VREG_S(VS2REG, sel);
				INT32 r = s1 * s2;

				W_ACCUM_H(del, (INT16)(r >> 16));
				W_ACCUM_M(del, (UINT16)(r));
				W_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)R_VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)R_VREG_S(VS2REG, sel);
				INT32 r = s1 * s2;

				ACCUM(del) += (INT64)(r) << 17;
				res = SATURATE_ACCUM(cpustate, 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)R_VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)R_VREG_S(VS2REG, sel);
				INT32 r1 = s1 * s2;
				UINT32 r2 = (UINT16)R_ACCUM_L(del) + ((UINT16)(r1) * 2);
				UINT32 r3 = (UINT16)R_ACCUM_M(del) + (UINT16)((r1 >> 16) * 2) + (UINT16)(r2 >> 16);

				W_ACCUM_L(del, (UINT16)(r2));
				W_ACCUM_M(del, (UINT16)(r3));
				W_ACCUM_H(del, (UINT16)R_ACCUM_H(del) + (UINT16)(r3 >> 16) + (UINT16)(r1 >> 31));

				//res = SATURATE_ACCUM(cpustate, del, 1, 0x0000, 0xffff);
				if ((INT16)R_ACCUM_H(del) < 0)
				{
					res = 0;
				}
				else
				{
					if (R_ACCUM_H(del) != 0)
					{
						res = 0xffff;
					}
					else
					{
						if ((INT16)R_ACCUM_M(del) < 0)
						{
							res = 0xffff;
						}
						else
						{
							res = R_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)R_VREG_S(VS1REG, del);
				UINT32 s2 = (UINT32)(UINT16)R_VREG_S(VS2REG, sel);
				UINT32 r1 = s1 * s2;
				UINT32 r2 = (UINT16)R_ACCUM_L(del) + (r1 >> 16);
				UINT32 r3 = (UINT16)R_ACCUM_M(del) + (r2 >> 16);

				W_ACCUM_L(del, (UINT16)(r2));
				W_ACCUM_M(del, (UINT16)(r3));
				W_ACCUM_H(del, (INT16)R_ACCUM_H(del) + (INT16)(r3 >> 16));

				res = SATURATE_ACCUM(cpustate, 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)R_VREG_S(VS1REG, del);
				UINT32 s2 = (UINT16)R_VREG_S(VS2REG, sel);	// not sign-extended
				UINT32 r1 = s1 * s2;
				UINT32 r2 = (UINT16)R_ACCUM_L(del) + (UINT16)(r1);
				UINT32 r3 = (UINT16)R_ACCUM_M(del) + (r1 >> 16) + (r2 >> 16);

				W_ACCUM_L(del, (UINT16)(r2));
				W_ACCUM_M(del, (UINT16)(r3));
				W_ACCUM_H(del, (UINT16)R_ACCUM_H(del) + (UINT16)(r3 >> 16));
				if ((INT32)(r1) < 0)
					W_ACCUM_H(del, (UINT16)R_ACCUM_H(del) - 1);

				res = SATURATE_ACCUM(cpustate, 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)R_VREG_S(VS1REG, del);		// not sign-extended
				INT32 s2 = (INT32)(INT16)R_VREG_S(VS2REG, sel);
				UINT32 r1 = s1 * s2;
				UINT32 r2 = (UINT16)R_ACCUM_L(del) + (UINT16)(r1);
				UINT32 r3 = (UINT16)R_ACCUM_M(del) + (r1 >> 16) + (r2 >> 16);

				W_ACCUM_L(del, (UINT16)(r2));
				W_ACCUM_M(del, (UINT16)(r3));
				W_ACCUM_H(del, (UINT16)R_ACCUM_H(del) + (UINT16)(r3 >> 16));
				if ((INT32)(r1) < 0)
					W_ACCUM_H(del, (UINT16)R_ACCUM_H(del) - 1);

				res = SATURATE_ACCUM(cpustate, 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)R_VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)R_VREG_S(VS2REG, sel);
				INT64 r = s1 * s2;

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

				res = SATURATE_ACCUM(cpustate, 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)R_VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)R_VREG_S(VS2REG, sel);
				INT32 r = s1 + s2 + CARRY_FLAG(del);

				W_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)R_VREG_S(VS1REG, del);
				INT32 s2 = (INT32)(INT16)R_VREG_S(VS2REG, sel);
				INT32 r = s1 - s2 - CARRY_FLAG(del);

				W_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)R_VREG_S(VS1REG, del);
				INT16 s2 = (INT16)R_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;
				}

				W_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)R_VREG_S(VS1REG, del);
				INT32 s2 = (UINT32)(UINT16)R_VREG_S(VS2REG, sel);
				INT32 r = s1 + s2;

				vres[del] = (INT16)(r);
				W_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)R_VREG_S(VS1REG, del);
				INT32 s2 = (UINT32)(UINT16)R_VREG_S(VS2REG, sel);
				INT32 r = s1 - s2;

				vres[del] = (INT16)(r);
				W_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++)
					{
						W_VREG_S(VDREG, i, R_ACCUM_H(i));
					}
					break;
				}
				case 0x09:		// VSAWM
				{
					for (i=0; i < 8; i++)
					{
						W_VREG_S(VDREG, i, R_ACCUM_M(i));
					}
					break;
				}
				case 0x0a:		// VSAWL
				{
					for (i=0; i < 8; i++)
					{
						W_VREG_S(VDREG, i, R_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

			cpustate->flag[1] = 0;

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

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

				W_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

			cpustate->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] = R_VREG_S(VS2REG, sel);
				W_ACCUM_L(del, vres[del]);

				if (R_VREG_S(VS1REG, del) == R_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

			cpustate->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] = R_VREG_S(VS1REG, del);
				W_ACCUM_L(del, vres[del]);

				if (R_VREG_S(VS1REG, del) != R_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

			cpustate->flag[1] = 0;

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

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

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

				W_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 = R_VREG_S(VS1REG, del);
				INT16 s2 = R_VREG_S(VS2REG, sel);

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

				vres[del] = R_ACCUM_L(del);
			}
			CLEAR_ZERO_FLAGS();
			CLEAR_CARRY_FLAGS();
			cpustate->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();
			cpustate->flag[1] = 0;
			cpustate->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 = R_VREG_S(VS1REG, del);
				INT16 s2 = R_VREG_S(VS2REG, sel);

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

					if (s1 + s2 <= 0)
					{
						if (s1 + s2 == -1)
						{
							cpustate->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)
					{
						cpustate->flag[1] |= (1 << (8+del));
						vres[del] = s2;
					}
					else
					{
						vres[del] = s1;
					}

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

				W_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

			cpustate->flag[0] = 0;
			cpustate->flag[1] = 0;
			cpustate->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 = R_VREG_S(VS1REG, del);
				INT16 s2 = R_VREG_S(VS2REG, sel);

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

				vres[del] = R_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] = R_VREG_S(VS1REG, del);
				}
				else
				{
					vres[del] = R_VREG_S(VS2REG, VEC_EL_2(EL, sel));
				}

				W_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] = R_VREG_S(VS1REG, del) & R_VREG_S(VS2REG, sel);
				W_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] = ~((R_VREG_S(VS1REG, del) & R_VREG_S(VS2REG, sel)));
				W_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] = R_VREG_S(VS1REG, del) | R_VREG_S(VS2REG, sel);
				W_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] = ~((R_VREG_S(VS1REG, del) | R_VREG_S(VS2REG, sel)));
				W_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] = R_VREG_S(VS1REG, del) ^ R_VREG_S(VS2REG, sel);
				W_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] = ~((R_VREG_S(VS1REG, del) ^ R_VREG_S(VS2REG, sel)));
				W_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 = EL & 7;
			INT32 rec;

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

			if (rec == 0)
			{
				// divide by zero -> overflow
				rec = 0x7fffffff;
			}
			else
			{
				int sign = 0;
				int exp = 0;
				int mantissa = 0;

				if (rec < 0)
				{
					rec = -rec;	// rec = MINUS rec
					sign = 1;
				}

				// restrict to 10-bit mantissa
				for (i = 15; i >= 0; i--)
				{
					if (rec & (1 << i))
					{
						exp = i;
						mantissa = (rec << (15 - i)) >> 6;
						break;
					}
				}

				if (mantissa == 0x200)
				{
					rec = 0x7fffffff;
				}
				else
				{
					rec = 0xffffffffU / mantissa;

					//
					// simulate rounding error
					//
					// This has been verified on the real hardware.
					//
					// I was able to replicate this exact behaviour by using a five-round
					// Newton reciprocal method using floorf() on intermediate results
					// to force the use of IEEE 754 32bit floats.
					// However, for the sake of portability, we'll use integer arithmetic.
					//
					if (rec & 0x800)
						rec += 1;

					rec <<= 8;
				}

				// restrict result to 17 significant bits
				rec &= 0x7fffc000;

				rec >>= exp;

				if (sign)
				{
					rec = ~rec;	// rec = BITWISE NOT rec
				}
			}

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

			cpustate->reciprocal_res = rec;

			W_VREG_S(VDREG, del, (UINT16)(cpustate->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, del);
			INT32 rec;

			rec = ((UINT16)(R_VREG_S(VS2REG, sel)) | ((UINT32)(cpustate->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);
				W_ACCUM_L(i, R_VREG_S(VS2REG, element));
			}

			cpustate->reciprocal_res = rec;

			W_VREG_S(VDREG, del, (UINT16)(cpustate->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, del);

			cpustate->reciprocal_high = R_VREG_S(VS2REG, sel);

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

			W_VREG_S(VDREG, del, (INT16)(cpustate->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;
			W_VREG_S(VDREG, element, R_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, del);
			INT32 sqr;

			sqr = (UINT16)(R_VREG_S(VS2REG, sel)) | ((UINT32)(cpustate->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((double)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);
				W_ACCUM_L(i, R_VREG_S(VS2REG, element));
			}

			cpustate->square_root_res = sqr;

			W_VREG_S(VDREG, del, (UINT16)(cpustate->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, del);

			cpustate->square_root_high = R_VREG_S(VS2REG, sel);

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

			W_VREG_S(VDREG, del, (INT16)(cpustate->square_root_res >> 16));	// store high part
			break;
		}

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

static CPU_EXECUTE( rsp )
{
	rsp_state *cpustate = get_safe_token(device);
	UINT32 op;

	cpustate->icount = cycles;

	cpustate->pc = 0x4001000 | (cpustate->pc & 0xfff);

	if( cpustate->sr & ( RSP_STATUS_HALT | RSP_STATUS_BROKE ) )
	{
		cpustate->icount = MIN(cpustate->icount, 0);
	}

	while (cpustate->icount > 0)
	{
		cpustate->ppc = cpustate->pc;
		debugger_instruction_hook(device, cpustate->pc);

		op = ROPCODE(cpustate->pc);
		if (cpustate->nextpc != ~0)
		{
			cpustate->pc = cpustate->nextpc;
			cpustate->nextpc = ~0;
		}
		else
		{
			cpustate->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 */
					{
						(cpustate->config->sp_set_status)(cpustate->device, 0x3);
						cpustate->icount = MIN(cpustate->icount, 1);

						if (LOG_INSTRUCTION_EXECUTION) fprintf(cpustate->exec_output, "\n---------- break ----------\n\n");

						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(cpustate, 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(cpustate, 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(cpustate, RDREG); break;
					case 0x04:	/* MTC0 */		set_cop0_reg(cpustate, RDREG, RTVAL); break;
					default:	unimplemented_opcode(cpustate, 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 = R_VREG_B(VS1REG, (el+0) & 0xf);
						UINT16 b2 = R_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)
                        {
                            if (RDREG == 2)
                            {
                                // Anciliary clipping flags
                                RTVAL = cpustate->flag[RDREG] & 0x00ff;
                            }
                            else
                            {
                                // All other flags are 16 bits but sign-extended at retrieval
                                RTVAL = (UINT32)cpustate->flag[RDREG] | ( ( cpustate->flag[RDREG] & 0x8000 ) ? 0xffff0000 : 0 );
                            }
                        }
						break;
					}
					case 0x04:	/* MTC2 */
					{
						// 31       25      20      15      10     6         0
						// ---------------------------------------------------
						// | 010010 | 00100 | TTTTT | DDDDD | IIII | 0000000 |
						// ---------------------------------------------------
						//

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

						cpustate->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(cpustate, op);
						break;
					}

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

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

			default:
			{
				unimplemented_opcode(cpustate, 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, cpustate->ppc, op);

			fprintf(cpustate->exec_output, "%08X: %s", cpustate->ppc, string);

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

			fprintf(cpustate->exec_output, "| ");

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

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

			fprintf(cpustate->exec_output, "\n");

		}

		--cpustate->icount;

		if( cpustate->sr & RSP_STATUS_SSTEP )
		{
            if( cpustate->step_count )
            {
                cpustate->step_count--;
            }
            else
            {
                cpustate->sr |= RSP_STATUS_BROKE;
            }
		}

		if( cpustate->sr & ( RSP_STATUS_HALT | RSP_STATUS_BROKE ) )
		{
			cpustate->icount = MIN(cpustate->icount, 0);
		}

	}

	return cycles - cpustate->icount;
}




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

static CPU_SET_INFO( rsp )
{
	rsp_state *cpustate = get_safe_token(device);

	switch (state)
	{
		/* --- the following bits of info are set as 64-bit signed integers --- */
		case CPUINFO_INT_PC:
        case CPUINFO_INT_REGISTER + RSP_PC:             cpustate->pc = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R0:             cpustate->r[0] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R1:             cpustate->r[1] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R2:             cpustate->r[2] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R3:             cpustate->r[3] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R4:             cpustate->r[4] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R5:             cpustate->r[5] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R6:             cpustate->r[6] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R7:             cpustate->r[7] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R8:             cpustate->r[8] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R9:             cpustate->r[9] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R10:            cpustate->r[10] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R11:            cpustate->r[11] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R12:            cpustate->r[12] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R13:            cpustate->r[13] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R14:            cpustate->r[14] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R15:            cpustate->r[15] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R16:            cpustate->r[16] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R17:            cpustate->r[17] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R18:            cpustate->r[18] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R19:            cpustate->r[19] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R20:            cpustate->r[20] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R21:            cpustate->r[21] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R22:            cpustate->r[22] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R23:            cpustate->r[23] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R24:            cpustate->r[24] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R25:            cpustate->r[25] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R26:            cpustate->r[26] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R27:            cpustate->r[27] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R28:            cpustate->r[28] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R29:            cpustate->r[29] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_R30:            cpustate->r[30] = info->i;        break;
        case CPUINFO_INT_SP:
        case CPUINFO_INT_REGISTER + RSP_R31:            cpustate->r[31] = info->i;        break;
        case CPUINFO_INT_REGISTER + RSP_SR:             cpustate->sr = info->i;           break;
        case CPUINFO_INT_REGISTER + RSP_NEXTPC:         cpustate->nextpc = info->i;       break;
        case CPUINFO_INT_REGISTER + RSP_STEPCNT:        cpustate->step_count = info->i;   break;
	}
}

CPU_GET_INFO( rsp )
{
	rsp_state *cpustate = (device != NULL && device->token != NULL) ? get_safe_token(device) : NULL;

	switch(state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */
		case CPUINFO_INT_CONTEXT_SIZE:					info->i = sizeof(rsp_state);					break;
		case CPUINFO_INT_INPUT_LINES:					info->i = 1;							break;
		case CPUINFO_INT_DEFAULT_IRQ_VECTOR:			info->i = 0;							break;
		case DEVINFO_INT_ENDIANNESS:					info->i = ENDIANNESS_LITTLE;					break;
		case CPUINFO_INT_CLOCK_MULTIPLIER:				info->i = 1;							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_PROGRAM:	info->i = 32;					break;
		case CPUINFO_INT_ADDRBUS_WIDTH_PROGRAM: info->i = 32;					break;
		case CPUINFO_INT_ADDRBUS_SHIFT_PROGRAM: info->i = 0;					break;
		case CPUINFO_INT_DATABUS_WIDTH_DATA:	info->i = 0;					break;
		case CPUINFO_INT_ADDRBUS_WIDTH_DATA: 	info->i = 0;					break;
		case CPUINFO_INT_ADDRBUS_SHIFT_DATA: 	info->i = 0;					break;
		case CPUINFO_INT_DATABUS_WIDTH_IO:		info->i = 0;					break;
		case CPUINFO_INT_ADDRBUS_WIDTH_IO: 		info->i = 0;					break;
		case CPUINFO_INT_ADDRBUS_SHIFT_IO: 		info->i = 0;					break;

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

		case CPUINFO_INT_PREVIOUSPC:					info->i = cpustate->ppc;						break;

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

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

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case CPUINFO_FCT_SET_INFO:						info->setinfo = CPU_SET_INFO_NAME(rsp);			break;
		case CPUINFO_FCT_INIT:							info->init = CPU_INIT_NAME(rsp);					break;
		case CPUINFO_FCT_RESET:							info->reset = CPU_RESET_NAME(rsp);				break;
		case CPUINFO_FCT_EXIT:							info->exit = CPU_EXIT_NAME(rsp);					break;
		case CPUINFO_FCT_EXECUTE:						info->execute = CPU_EXECUTE_NAME(rsp);			break;
		case CPUINFO_FCT_BURN:							info->burn = NULL;						break;
		case CPUINFO_FCT_DISASSEMBLE:					info->disassemble = CPU_DISASSEMBLE_NAME(rsp);			break;
		case CPUINFO_PTR_INSTRUCTION_COUNTER:			info->icount = &cpustate->icount;				break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_NAME:							strcpy(info->s, "RSP");					break;
		case DEVINFO_STR_FAMILY:					strcpy(info->s, "RSP");					break;
		case DEVINFO_STR_VERSION:					strcpy(info->s, "1.0");					break;
		case DEVINFO_STR_SOURCE_FILE:						strcpy(info->s, __FILE__);				break;
		case DEVINFO_STR_CREDITS:					strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team");	break;

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

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

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