1 // license:BSD-3-Clause
2 // copyright-holders:smf
3 /*
4  * PSXCPU disassembler for the MAME project written by smf
5  *
6  */
7 
8 #include "emu.h"
9 #include "psx.h"
10 #include "gte.h"
11 
12 #include "psxdefs.h"
13 #include "psxdasm.h"
14 
15 
make_signed_hex_str_16(uint32_t value)16 std::string psxcpu_disassembler::make_signed_hex_str_16( uint32_t value )
17 {
18 	if( value & 0x8000 )
19 	{
20 		return util::string_format("-$%x", -value & 0xffff );
21 	}
22 	else
23 	{
24 		return util::string_format("$%x", value & 0xffff );
25 	}
26 }
27 
28 const char *const psxcpu_disassembler::s_cpugenreg[] =
29 {
30 	"zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
31 	"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
32 	"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
33 	"t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
34 };
35 
36 const char *const psxcpu_disassembler::s_cp0genreg[] =
37 {
38 	"!Index", "!Random", "!EntryLo", "BPC", "!Context", "BDA", "TAR", "DCIC",
39 	"BadA", "BDAM", "!EntryHi", "BPCM", "SR", "Cause", "EPC", "PRId",
40 	"cp0r16", "cp0r17", "cp0r18", "cp0r19", "cp0r20", "cp0r21", "cp0r22", "cp0r23",
41 	"cp0r24", "cp0r25", "cp0r26", "cp0r27", "cp0r28", "cp0r29", "cp0r30", "cp0r31"
42 };
43 
44 const char *const psxcpu_disassembler::s_cp0ctlreg[] =
45 {
46 	"cp0cr0", "cp0cr1", "cp0cr2", "cp0cr3", "cp0cr4", "cp0cr5", "cp0cr6", "cp0cr7",
47 	"cp0cr8", "cp0cr9", "cp0cr10", "cp0cr11", "cp0cr12", "cp0cr13", "cp0cr14", "cp0cr15",
48 	"cp0cr16", "cp0cr17", "cp0cr18", "cp0cr19", "cp0cr20", "cp0cr21", "cp0cr22", "cp0cr23",
49 	"cp0cr24", "cp0cr25", "cp0cr26", "cp0cr27", "cp0cr28", "cp0cr29", "cp0cr30", "cp0cr31"
50 };
51 
52 const char *const psxcpu_disassembler::s_cp1genreg[] =
53 {
54 	"cp1r0", "cp1r1", "cp1r2", "cp1r3", "cp1r4", "cp1r5", "cp1r6", "cp1r7",
55 	"cp1r8", "cp1r9", "cp1r10", "cp1r11", "cp1r12", "cp1r13", "cp1r14", "cp1r15",
56 	"cp1r16", "cp1r17", "cp1r18", "cp1r19", "cp1r20", "cp1r21", "cp1r22", "cp1r22",
57 	"cp1r23", "cp1r24", "cp1r25", "cp1r26", "cp1r27", "cp1r28", "cp1r29", "cp1r30"
58 };
59 
60 const char *const psxcpu_disassembler::s_cp1ctlreg[] =
61 {
62 	"cp1cr0", "cp1cr1", "cp1cr2", "cp1cr3", "cp1cr4", "cp1cr5", "cp1cr6", "cp1cr7",
63 	"cp1cr8", "cp1cr9", "cp1cr10", "cp1cr11", "cp1cr12", "cp1cr13", "cp1cr14", "cp1cr15",
64 	"cp1cr16", "cp1cr17", "cp1cr18", "cp1cr19", "cp1cr20", "cp1cr21", "cp1cr22", "cp1cr23",
65 	"cp1cr24", "cp1cr25", "cp1cr26", "cp1cr27", "cp1cr28", "cp1cr29", "cp1cr30", "cp1cr31"
66 };
67 
68 const char *const psxcpu_disassembler::s_cp2genreg[] =
69 {
70 	"vxy0", "vz0", "vxy1", "vz1", "vxy2", "vz2", "rgb", "otz",
71 	"ir0", "ir1", "ir2", "ir3", "sxy0", "sxy1", "sxy2", "sxyp",
72 	"sz0", "sz1", "sz2", "sz3", "rgb0", "rgb1", "rgb2", "cp2cr23",
73 	"mac0", "mac1", "mac2", "mac3", "irgb", "orgb", "lzcs", "lzcr"
74 };
75 
76 const char *const psxcpu_disassembler::s_cp2ctlreg[] =
77 {
78 	"r11r12", "r13r21", "r22r23", "r31r32", "r33", "trx", "try", "trz",
79 	"l11l12", "l13l21", "l22l23", "l31l32", "l33", "rbk", "gbk", "bbk",
80 	"lr1lr2", "lr3lg1", "lg2lg3", "lb1lb2", "lb3", "rfc", "gfc", "bfc",
81 	"ofx", "ofy", "h", "dqa", "dqb", "zsf3", "zsf4", "flag"
82 };
83 
84 const char *const psxcpu_disassembler::s_cp3genreg[] =
85 {
86 	"cp3r0", "cp3r1", "cp3r2", "cp3r3", "cp3r4", "cp3r5", "cp3r6", "cp3r7",
87 	"cp3r8", "cp3r9", "cp3r10", "cp3r11", "cp3r12", "cp3r13", "cp3r14", "cp3r15",
88 	"cp3r16", "cp3r17", "cp3r18", "cp3r19", "cp3r20", "cp3r21", "cp3r22", "cp3r22",
89 	"cp3r23", "cp3r24", "cp3r25", "cp3r26", "cp3r27", "cp3r28", "cp3r29", "cp3r30"
90 };
91 
92 const char *const psxcpu_disassembler::s_cp3ctlreg[] =
93 {
94 	"cp3cr0", "cp3cr1", "cp3cr2", "cp3cr3", "cp3cr4", "cp3cr5", "cp3cr6", "cp3cr7",
95 	"cp3cr8", "cp3cr9", "cp3cr10", "cp3cr11", "cp3cr12", "cp3cr13", "cp3cr14", "cp3cr15",
96 	"cp3cr16", "cp3cr17", "cp3cr18", "cp3cr19", "cp3cr20", "cp3cr21", "cp3cr22", "cp3cr23",
97 	"cp3cr24", "cp3cr25", "cp3cr26", "cp3cr27", "cp3cr28", "cp3cr29", "cp3cr30", "cp3cr31"
98 };
99 
100 const char *const psxcpu_disassembler::s_gtesf[] =
101 {
102 	" sf=0", " sf=12"
103 };
104 
105 const char *const psxcpu_disassembler::s_gtemx[] =
106 {
107 	"rm", "lm", "cm", "0"
108 };
109 
110 const char *const psxcpu_disassembler::s_gtev[] =
111 {
112 	"v0", "v1", "v2", "ir"
113 };
114 
115 const char *const psxcpu_disassembler::s_gtecv[] =
116 {
117 	"tr", "bk", "fc", "0"
118 };
119 
120 const char *const psxcpu_disassembler::s_gtelm[] =
121 {
122 	" lm=s16", " lm=u15"
123 };
124 
effective_address(uint32_t pc,uint32_t op)125 std::string psxcpu_disassembler::effective_address( uint32_t pc, uint32_t op )
126 {
127 	if( m_config && m_config->pc() == pc )
128 	{
129 		return util::string_format("%s(%s) ; 0x%08x", make_signed_hex_str_16( INS_IMMEDIATE( op ) ), s_cpugenreg[ INS_RS( op ) ],
130 								   (uint32_t)( m_config->r( INS_RS( op ) ) + (int16_t)INS_IMMEDIATE( op ) ) );
131 	}
132 	return util::string_format("%s(%s)", make_signed_hex_str_16( INS_IMMEDIATE( op ) ), s_cpugenreg[ INS_RS( op ) ] );
133 }
134 
relative_address(uint32_t pc,uint32_t op)135 uint32_t psxcpu_disassembler::relative_address( uint32_t pc, uint32_t op )
136 {
137 	uint32_t nextpc = pc + 4;
138 	if( m_config && m_config->pc() == pc && m_config->delayr() == PSXCPU_DELAYR_PC )
139 	{
140 		nextpc = m_config->delayv();
141 	}
142 
143 	return nextpc + ( PSXCPU_WORD_EXTEND( INS_IMMEDIATE( op ) ) << 2 );
144 }
145 
jump_address(uint32_t pc,uint32_t op)146 uint32_t psxcpu_disassembler::jump_address( uint32_t pc, uint32_t op )
147 {
148 	uint32_t nextpc = pc + 4;
149 	if( m_config && m_config->pc() == pc && m_config->delayr() == PSXCPU_DELAYR_PC )
150 	{
151 		nextpc = m_config->delayv();
152 	}
153 	return ( nextpc & 0xf0000000 ) + ( INS_TARGET( op ) << 2 );
154 }
155 
upper_address(uint32_t op,offs_t pos,const data_buffer & opcodes)156 std::string psxcpu_disassembler::upper_address( uint32_t op, offs_t pos, const data_buffer &opcodes )
157 {
158 	uint32_t nextop = opcodes.r32( pos );
159 
160 	if( INS_OP( nextop ) == OP_ORI && INS_RT( op ) == INS_RS( nextop ) )
161 	{
162 		return util::string_format("$%04x ; 0x%08x", INS_IMMEDIATE( op ), ( INS_IMMEDIATE( op ) << 16 ) | INS_IMMEDIATE( nextop ) );
163 	}
164 	else if( INS_OP( nextop ) == OP_ADDIU && INS_RT( op ) == INS_RS( nextop ) )
165 	{
166 		return util::string_format("$%04x ; 0x%08x", INS_IMMEDIATE( op ), ( INS_IMMEDIATE( op ) << 16 ) + (int16_t) INS_IMMEDIATE( nextop ) );
167 	}
168 	else
169 	{
170 		return util::string_format("$%04x", INS_IMMEDIATE( op ) );
171 	}
172 }
173 
psxcpu_disassembler(config * conf)174 psxcpu_disassembler::psxcpu_disassembler(config *conf) : m_config(conf)
175 {
176 }
177 
opcode_alignment() const178 u32 psxcpu_disassembler::opcode_alignment() const
179 {
180 	return 4;
181 }
182 
disassemble(std::ostream & stream,offs_t pc,const data_buffer & opcodes,const data_buffer & params)183 offs_t psxcpu_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
184 {
185 	uint32_t op;
186 	uint32_t flags = 0;
187 	offs_t pos = pc;
188 	op = opcodes.r32( pos );
189 	pos += 4;
190 
191 	std::streampos current_pos = stream.tellp();
192 
193 	switch( INS_OP( op ) )
194 	{
195 	case OP_SPECIAL:
196 		switch( INS_FUNCT( op ) )
197 		{
198 		case FUNCT_SLL:
199 			if( op == 0 )
200 			{
201 				/* the standard nop is "sll     zero,zero,$0000" */
202 				util::stream_format( stream, "nop" );
203 			}
204 			else
205 			{
206 				util::stream_format( stream, "sll     %s,%s,$%02x", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RT( op ) ], INS_SHAMT( op ) );
207 			}
208 			break;
209 		case FUNCT_SRL:
210 			util::stream_format( stream, "srl     %s,%s,$%02x", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RT( op ) ], INS_SHAMT( op ) );
211 			break;
212 		case FUNCT_SRA:
213 			util::stream_format( stream, "sra     %s,%s,$%02x", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RT( op ) ], INS_SHAMT( op ) );
214 			break;
215 		case FUNCT_SLLV:
216 			util::stream_format( stream, "sllv    %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RT( op ) ], s_cpugenreg[ INS_RS( op ) ] );
217 			break;
218 		case FUNCT_SRLV:
219 			util::stream_format( stream, "srlv    %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RT( op ) ], s_cpugenreg[ INS_RS( op ) ] );
220 			break;
221 		case FUNCT_SRAV:
222 			util::stream_format( stream, "srav    %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RT( op ) ], s_cpugenreg[ INS_RS( op ) ] );
223 			break;
224 		case FUNCT_JR:
225 			util::stream_format( stream, "jr      %s", s_cpugenreg[ INS_RS( op ) ] );
226 			if( INS_RS( op ) == 31 )
227 			{
228 				flags = STEP_OUT;
229 			}
230 			break;
231 		case FUNCT_JALR:
232 			util::stream_format( stream, "jalr    %s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RS( op ) ] );
233 			flags = STEP_OVER | step_over_extra( 1 );
234 			break;
235 		case FUNCT_SYSCALL:
236 			util::stream_format( stream, "syscall $%05x", INS_CODE( op ) );
237 			flags = STEP_OVER;
238 			break;
239 		case FUNCT_BREAK:
240 			util::stream_format( stream, "break   $%05x", INS_CODE( op ) );
241 			flags = STEP_OVER;
242 			break;
243 		case FUNCT_MFHI:
244 			util::stream_format( stream, "mfhi    %s", s_cpugenreg[ INS_RD( op ) ] );
245 			break;
246 		case FUNCT_MTHI:
247 			util::stream_format( stream, "mthi    %s", s_cpugenreg[ INS_RS( op ) ] );
248 			break;
249 		case FUNCT_MFLO:
250 			util::stream_format( stream, "mflo    %s", s_cpugenreg[ INS_RD( op ) ] );
251 			break;
252 		case FUNCT_MTLO:
253 			util::stream_format( stream, "mtlo    %s", s_cpugenreg[ INS_RS( op ) ] );
254 			break;
255 		case FUNCT_MULT:
256 			util::stream_format( stream, "mult    %s,%s", s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
257 			break;
258 		case FUNCT_MULTU:
259 			util::stream_format( stream, "multu   %s,%s", s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
260 			break;
261 		case FUNCT_DIV:
262 			util::stream_format( stream, "div     %s,%s", s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
263 			break;
264 		case FUNCT_DIVU:
265 			util::stream_format( stream, "divu    %s,%s", s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
266 			break;
267 		case FUNCT_ADD:
268 			util::stream_format( stream, "add     %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
269 			break;
270 		case FUNCT_ADDU:
271 			util::stream_format( stream, "addu    %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
272 			break;
273 		case FUNCT_SUB:
274 			util::stream_format( stream, "sub     %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
275 			break;
276 		case FUNCT_SUBU:
277 			util::stream_format( stream, "subu    %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
278 			break;
279 		case FUNCT_AND:
280 			util::stream_format( stream, "and     %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
281 			break;
282 		case FUNCT_OR:
283 			util::stream_format( stream, "or      %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
284 			break;
285 		case FUNCT_XOR:
286 			util::stream_format( stream, "xor     %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
287 			break;
288 		case FUNCT_NOR:
289 			util::stream_format( stream, "nor     %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
290 			break;
291 		case FUNCT_SLT:
292 			util::stream_format( stream, "slt     %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
293 			break;
294 		case FUNCT_SLTU:
295 			util::stream_format( stream, "sltu    %s,%s,%s", s_cpugenreg[ INS_RD( op ) ], s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ] );
296 			break;
297 		}
298 		break;
299 	case OP_REGIMM:
300 		switch( INS_RT_REGIMM( op ) )
301 		{
302 		case RT_BLTZ:
303 			if( INS_RT( op ) == RT_BLTZAL )
304 			{
305 				util::stream_format( stream, "bltzal  %s,$%08x", s_cpugenreg[ INS_RS( op ) ], relative_address( pc, op ) );
306 				flags = STEP_OVER | step_over_extra( 1 );
307 			}
308 			else
309 			{
310 				util::stream_format( stream, "bltz    %s,$%08x", s_cpugenreg[ INS_RS( op ) ], relative_address( pc, op ) );
311 			}
312 			break;
313 		case RT_BGEZ:
314 			if( INS_RT( op ) == RT_BGEZAL )
315 			{
316 				util::stream_format( stream, "bgezal  %s,$%08x", s_cpugenreg[ INS_RS( op ) ], relative_address( pc, op ) );
317 				flags = STEP_OVER | step_over_extra( 1 );
318 			}
319 			else
320 			{
321 				util::stream_format( stream, "bgez    %s,$%08x", s_cpugenreg[ INS_RS( op ) ], relative_address( pc, op ) );
322 			}
323 			break;
324 		}
325 		break;
326 	case OP_J:
327 		util::stream_format( stream, "j       $%08x", jump_address( pc, op ) );
328 		break;
329 	case OP_JAL:
330 		util::stream_format( stream, "jal     $%08x", jump_address( pc, op ) );
331 		flags = STEP_OVER | step_over_extra( 1 );
332 		break;
333 	case OP_BEQ:
334 		util::stream_format( stream, "beq     %s,%s,$%08x", s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ], relative_address( pc, op ) );
335 		break;
336 	case OP_BNE:
337 		util::stream_format( stream, "bne     %s,%s,$%08x", s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ], relative_address( pc, op ) );
338 		break;
339 	case OP_BLEZ:
340 		util::stream_format( stream, "blez    %s,%s,$%08x", s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ], relative_address( pc, op ) );
341 		break;
342 	case OP_BGTZ:
343 		util::stream_format( stream, "bgtz    %s,%s,$%08x", s_cpugenreg[ INS_RS( op ) ], s_cpugenreg[ INS_RT( op ) ], relative_address( pc, op ) );
344 		break;
345 	case OP_ADDI:
346 		util::stream_format( stream, "addi    %s,%s,%s", s_cpugenreg[ INS_RT( op ) ], s_cpugenreg[ INS_RS( op ) ], make_signed_hex_str_16( INS_IMMEDIATE( op ) ) );
347 		break;
348 	case OP_ADDIU:
349 		util::stream_format( stream, "addiu   %s,%s,%s", s_cpugenreg[ INS_RT( op ) ], s_cpugenreg[ INS_RS( op ) ], make_signed_hex_str_16( INS_IMMEDIATE( op ) ) );
350 		break;
351 	case OP_SLTI:
352 		util::stream_format( stream, "slti    %s,%s,%s", s_cpugenreg[ INS_RT( op ) ], s_cpugenreg[ INS_RS( op ) ], make_signed_hex_str_16( INS_IMMEDIATE( op ) ) );
353 		break;
354 	case OP_SLTIU:
355 		util::stream_format( stream, "sltiu   %s,%s,%s", s_cpugenreg[ INS_RT( op ) ], s_cpugenreg[ INS_RS( op ) ], make_signed_hex_str_16( INS_IMMEDIATE( op ) ) );
356 		break;
357 	case OP_ANDI:
358 		util::stream_format( stream, "andi    %s,%s,$%04x", s_cpugenreg[ INS_RT( op ) ], s_cpugenreg[ INS_RS( op ) ], INS_IMMEDIATE( op ) );
359 		break;
360 	case OP_ORI:
361 		util::stream_format( stream, "ori     %s,%s,$%04x", s_cpugenreg[ INS_RT( op ) ], s_cpugenreg[ INS_RS( op ) ], INS_IMMEDIATE( op ) );
362 		break;
363 	case OP_XORI:
364 		util::stream_format( stream, "xori    %s,%s,$%04x", s_cpugenreg[ INS_RT( op ) ], s_cpugenreg[ INS_RS( op ) ], INS_IMMEDIATE( op ) );
365 		break;
366 	case OP_LUI:
367 		util::stream_format( stream, "lui     %s,%s", s_cpugenreg[ INS_RT( op ) ], upper_address( op, pos, opcodes ) );
368 		break;
369 	case OP_COP0:
370 		switch( INS_RS( op ) )
371 		{
372 		case RS_MFC:
373 			util::stream_format( stream, "mfc0    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp0genreg[ INS_RD( op ) ] );
374 			break;
375 		case RS_CFC:
376 			util::stream_format( stream, "!cfc0    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp0ctlreg[ INS_RD( op ) ] );
377 			break;
378 		case RS_MTC:
379 			util::stream_format( stream, "mtc0    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp0genreg[ INS_RD( op ) ] );
380 			break;
381 		case RS_CTC:
382 			util::stream_format( stream, "!ctc0    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp0ctlreg[ INS_RD( op ) ] );
383 			break;
384 		case RS_BC:
385 		case RS_BC_ALT:
386 			switch( INS_BC( op ) )
387 			{
388 			case BC_BCF:
389 				util::stream_format( stream, "bc0f    $%08x", relative_address( pc, op ) );
390 				break;
391 			case BC_BCT:
392 				util::stream_format( stream, "bc0t    $%08x", relative_address( pc, op ) );
393 				break;
394 			}
395 			break;
396 		default:
397 			switch( INS_CO( op ) )
398 			{
399 			case 1:
400 				switch( INS_CF( op ) )
401 				{
402 				case CF_TLBR:
403 					util::stream_format( stream, "!tlbr" );
404 					break;
405 				case CF_TLBWI:
406 					util::stream_format( stream, "!tlbwi" );
407 					break;
408 				case CF_TLBWR:
409 					util::stream_format( stream, "!tlbwr" );
410 					break;
411 				case CF_TLBP:
412 					util::stream_format( stream, "!tlbp" );
413 					break;
414 				case CF_RFE:
415 					util::stream_format( stream, "rfe" );
416 					break;
417 				default:
418 					util::stream_format(stream, "cop0    $%07x", INS_COFUN(op));
419 					break;
420 				}
421 				break;
422 			}
423 			break;
424 		}
425 		break;
426 	case OP_COP1:
427 		switch( INS_RS( op ) )
428 		{
429 		case RS_MFC:
430 			util::stream_format( stream, "mfc1    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp1genreg[ INS_RD( op ) ] );
431 			break;
432 		case RS_CFC:
433 			util::stream_format( stream, "cfc1    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp1ctlreg[ INS_RD( op ) ] );
434 			break;
435 		case RS_MTC:
436 			util::stream_format( stream, "mtc1    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp1genreg[ INS_RD( op ) ] );
437 			break;
438 		case RS_CTC:
439 			util::stream_format( stream, "ctc1    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp1ctlreg[ INS_RD( op ) ] );
440 			break;
441 		case RS_BC:
442 		case RS_BC_ALT:
443 			switch( INS_BC( op ) )
444 			{
445 			case BC_BCF:
446 				util::stream_format( stream, "bc1f    $%08x", relative_address( pc, op ) );
447 				break;
448 			case BC_BCT:
449 				util::stream_format( stream, "bc1t    $%08x", relative_address( pc, op ) );
450 				break;
451 			}
452 			break;
453 		default:
454 			switch( INS_CO( op ) )
455 			{
456 			case 1:
457 				util::stream_format( stream, "cop1    $%07x", INS_COFUN( op ) );
458 				break;
459 			}
460 			break;
461 		}
462 		break;
463 	case OP_COP2:
464 		switch( INS_RS( op ) )
465 		{
466 		case RS_MFC:
467 			util::stream_format( stream, "mfc2    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp2genreg[ INS_RD( op ) ] );
468 			break;
469 		case RS_CFC:
470 			util::stream_format( stream, "cfc2    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp2ctlreg[ INS_RD( op ) ] );
471 			break;
472 		case RS_MTC:
473 			util::stream_format( stream, "mtc2    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp2genreg[ INS_RD( op ) ] );
474 			break;
475 		case RS_CTC:
476 			util::stream_format( stream, "ctc2    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp2ctlreg[ INS_RD( op ) ] );
477 			break;
478 		case RS_BC:
479 		case RS_BC_ALT:
480 			switch( INS_BC( op ) )
481 			{
482 			case BC_BCF:
483 				util::stream_format( stream, "bc2f    $%08x", relative_address( pc, op ) );
484 				break;
485 			case BC_BCT:
486 				util::stream_format( stream, "bc2t    $%08x", relative_address( pc, op ) );
487 				break;
488 			}
489 			break;
490 		default:
491 			switch( INS_CO( op ) )
492 			{
493 			case 1:
494 				switch( GTE_FUNCT( op ) )
495 				{
496 				case 0x00: // drop through to RTPS
497 				case 0x01:
498 					util::stream_format( stream, "rtps%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
499 					break;
500 				case 0x06:
501 					util::stream_format( stream, "nclip" );
502 					break;
503 				case 0x0c:
504 					util::stream_format( stream, "op%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
505 					break;
506 				case 0x10:
507 					util::stream_format( stream, "dpcs%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
508 					break;
509 				case 0x11:
510 					util::stream_format( stream, "intpl%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
511 					break;
512 				case 0x12:
513 					util::stream_format( stream, "mvmva%s%s %s + %s * %s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ],
514 						s_gtecv[ GTE_CV( op ) ], s_gtemx[ GTE_MX( op ) ], s_gtev[ GTE_V( op ) ] );
515 					break;
516 				case 0x13:
517 					util::stream_format( stream, "ncds%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
518 					break;
519 				case 0x14:
520 					util::stream_format( stream, "cdp%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
521 					break;
522 				case 0x16:
523 					util::stream_format( stream, "ncdt%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
524 					break;
525 				case 0x1b:
526 					util::stream_format( stream, "nccs%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
527 					break;
528 				case 0x1c:
529 					util::stream_format( stream, "cc%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
530 					break;
531 				case 0x1e:
532 					util::stream_format( stream, "ncs%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
533 					break;
534 				case 0x20:
535 					util::stream_format( stream, "nct%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
536 					break;
537 				case 0x28:
538 					util::stream_format( stream, "sqr%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
539 					break;
540 				case 0x1a: // end of NCDT
541 				case 0x29:
542 					util::stream_format( stream, "dpcl%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
543 					break;
544 				case 0x2a:
545 					util::stream_format( stream, "dpct%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
546 					break;
547 				case 0x2d:
548 					util::stream_format( stream, "avsz3" );
549 					break;
550 				case 0x2e:
551 					util::stream_format( stream, "avsz4" );
552 					break;
553 				case 0x30:
554 					util::stream_format( stream, "rtpt%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
555 					break;
556 				case 0x3d:
557 					util::stream_format( stream, "gpf%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
558 					break;
559 				case 0x3e:
560 					util::stream_format( stream, "gpl%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
561 					break;
562 				case 0x3f:
563 					util::stream_format( stream, "ncct%s%s", s_gtesf[ GTE_SF( op ) ], s_gtelm[ GTE_LM( op ) ] );
564 					break;
565 				default:
566 					util::stream_format(stream, "cop2    $%07x", INS_COFUN(op));
567 					break;
568 				}
569 			}
570 			break;
571 		}
572 		break;
573 	case OP_COP3:
574 		switch( INS_RS( op ) )
575 		{
576 		case RS_MFC:
577 			util::stream_format( stream, "mfc3    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp3genreg[ INS_RD( op ) ] );
578 			break;
579 		case RS_CFC:
580 			util::stream_format( stream, "cfc3    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp3ctlreg[ INS_RD( op ) ] );
581 			break;
582 		case RS_MTC:
583 			util::stream_format( stream, "mtc3    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp3genreg[ INS_RD( op ) ] );
584 			break;
585 		case RS_CTC:
586 			util::stream_format( stream, "ctc3    %s,%s",  s_cpugenreg[ INS_RT( op ) ], s_cp3ctlreg[ INS_RD( op ) ] );
587 			break;
588 		case RS_BC:
589 		case RS_BC_ALT:
590 			switch( INS_BC( op ) )
591 			{
592 			case BC_BCF:
593 				util::stream_format( stream, "bc3f    $%08x", relative_address( pc, op ) );
594 				break;
595 			case BC_BCT:
596 				util::stream_format( stream, "bc3t    $%08x", relative_address( pc, op ) );
597 				break;
598 			}
599 			break;
600 		default:
601 			switch( INS_CO( op ) )
602 			{
603 			case 1:
604 				util::stream_format( stream, "cop3    $%07x", INS_COFUN( op ) );
605 				break;
606 			}
607 			break;
608 		}
609 		break;
610 	case OP_LB:
611 		util::stream_format( stream, "lb      %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
612 		break;
613 	case OP_LH:
614 		util::stream_format( stream, "lh      %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
615 		break;
616 	case OP_LWL:
617 		util::stream_format( stream, "lwl     %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
618 		break;
619 	case OP_LW:
620 		util::stream_format( stream, "lw      %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
621 		break;
622 	case OP_LBU:
623 		util::stream_format( stream, "lbu     %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
624 		break;
625 	case OP_LHU:
626 		util::stream_format( stream, "lhu     %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
627 		break;
628 	case OP_LWR:
629 		util::stream_format( stream, "lwr     %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
630 		break;
631 	case OP_SB:
632 		util::stream_format( stream, "sb      %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
633 		break;
634 	case OP_SH:
635 		util::stream_format( stream, "sh      %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
636 		break;
637 	case OP_SWL:
638 		util::stream_format( stream, "swl     %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
639 		break;
640 	case OP_SW:
641 		util::stream_format( stream, "sw      %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
642 		break;
643 	case OP_SWR:
644 		util::stream_format( stream, "swr     %s,%s", s_cpugenreg[ INS_RT( op ) ], effective_address( pc, op ) );
645 		break;
646 	case OP_LWC0:
647 		util::stream_format( stream, "lwc0    %s,%s", s_cp0genreg[ INS_RT( op ) ], effective_address( pc, op ) );
648 		break;
649 	case OP_LWC1:
650 		util::stream_format( stream, "lwc1    %s,%s", s_cp1genreg[ INS_RT( op ) ], effective_address( pc, op ) );
651 		break;
652 	case OP_LWC2:
653 		util::stream_format( stream, "lwc2    %s,%s", s_cp2genreg[ INS_RT( op ) ], effective_address( pc, op ) );
654 		break;
655 	case OP_LWC3:
656 		util::stream_format( stream, "lwc3    %s,%s", s_cp2genreg[ INS_RT( op ) ], effective_address( pc, op ) );
657 		break;
658 	case OP_SWC0:
659 		util::stream_format( stream, "swc0    %s,%s", s_cp0genreg[ INS_RT( op ) ], effective_address( pc, op ) );
660 		break;
661 	case OP_SWC1:
662 		util::stream_format( stream, "swc1    %s,%s", s_cp1genreg[ INS_RT( op ) ], effective_address( pc, op ) );
663 		break;
664 	case OP_SWC2:
665 		util::stream_format( stream, "swc2    %s,%s", s_cp2genreg[ INS_RT( op ) ], effective_address( pc, op ) );
666 		break;
667 	case OP_SWC3:
668 		util::stream_format( stream, "swc3    %s,%s", s_cp2genreg[ INS_RT( op ) ], effective_address( pc, op ) );
669 		break;
670 	}
671 
672 	// fall back if we have not emitted anything
673 	if (current_pos == stream.tellp())
674 	{
675 		util::stream_format(stream, "dw      $%08x", op);
676 	}
677 
678 	return ( pos - pc ) | flags | SUPPORTED;
679 }
680