1 // Copyright (c) 2012- PPSSPP Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 #include <cstdio>
19 
20 #include "Core/MemMap.h"
21 #include "GPU/ge_constants.h"
22 #include "GPU/GPU.h"
23 #include "GPU/GPUState.h"
24 
GeDescribeVertexType(u32 op,char * buffer,int len)25 void GeDescribeVertexType(u32 op, char *buffer, int len) {
26 	bool through = (op & GE_VTYPE_THROUGH_MASK) == GE_VTYPE_THROUGH;
27 	int tc = (op & GE_VTYPE_TC_MASK) >> GE_VTYPE_TC_SHIFT;
28 	int col = (op & GE_VTYPE_COL_MASK) >> GE_VTYPE_COL_SHIFT;
29 	int nrm = (op & GE_VTYPE_NRM_MASK) >> GE_VTYPE_NRM_SHIFT;
30 	int pos = (op & GE_VTYPE_POS_MASK) >> GE_VTYPE_POS_SHIFT;
31 	int weight = (op & GE_VTYPE_WEIGHT_MASK) >> GE_VTYPE_WEIGHT_SHIFT;
32 	int weightCount = ((op & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT) + 1;
33 	int morphCount = (op & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT;
34 	int idx = (op & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;
35 
36 	static const char *colorNames[] = {
37 		NULL,
38 		"unsupported1",
39 		"unsupported2",
40 		"unsupported3",
41 		"BGR 565",
42 		"ABGR 1555",
43 		"ABGR 4444",
44 		"ABGR 8888",
45 	};
46 	static const char *typeNames[] = {
47 		NULL,
48 		"u8",
49 		"u16",
50 		"float",
51 	};
52 	static const char *typeNamesI[] = {
53 		NULL,
54 		"u8",
55 		"u16",
56 		"u32",
57 	};
58 	static const char *typeNamesS[] = {
59 		NULL,
60 		"s8",
61 		"s16",
62 		"float",
63 	};
64 
65 	char *w = buffer, *end = buffer + len;
66 	if (through)
67 		w += snprintf(w, end - w, "through, ");
68 	if (typeNames[tc] && w < end)
69 		w += snprintf(w, end - w, "%s texcoords, ", typeNames[tc]);
70 	if (colorNames[col] && w < end)
71 		w += snprintf(w, end - w, "%s colors, ", colorNames[col]);
72 	if (typeNames[nrm] && w < end)
73 		w += snprintf(w, end - w, "%s normals, ", typeNamesS[nrm]);
74 	if (typeNames[pos] && w < end)
75 		w += snprintf(w, end - w, "%s positions, ", typeNamesS[pos]);
76 	if (typeNames[weight] && w < end)
77 		w += snprintf(w, end - w, "%s weights (%d), ", typeNames[weight], weightCount);
78 	else if (weightCount > 1 && w < end)
79 		w += snprintf(w, end - w, "unknown weights (%d), ", weightCount);
80 	if (morphCount > 0 && w < end)
81 		w += snprintf(w, end - w, "%d morphs, ", morphCount);
82 	if (typeNamesI[idx] && w < end)
83 		w += snprintf(w, end - w, "%s indexes, ", typeNamesI[idx]);
84 
85 	if (w < buffer + 2)
86 		snprintf(buffer, len, "none");
87 	// Otherwise, get rid of the pesky trailing comma.
88 	else if (w < end)
89 		w[-2] = '\0';
90 }
91 
GeDisassembleOp(u32 pc,u32 op,u32 prev,char * buffer,int bufsize)92 void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
93 	u32 cmd = op >> 24;
94 	u32 data = op & 0xFFFFFF;
95 
96 	// Handle control and drawing commands here directly. The others we delegate.
97 	switch (cmd)
98 	{
99 	case GE_CMD_NOP:
100 		if (data != 0)
101 			snprintf(buffer, bufsize, "NOP: data= %06x", data);
102 		else
103 			snprintf(buffer, bufsize, "NOP");
104 		break;
105 
106 		// Pretty sure this is some sort of NOP to eat some pipelining issue,
107 		// often seen after CALL instructions.
108 	case GE_CMD_NOP_FF:
109 		if (data != 0)
110 			snprintf(buffer, bufsize, "NOP_FF: data= %06x", data);
111 		else
112 			snprintf(buffer, bufsize, "NOP_FF");
113 		break;
114 
115 	case GE_CMD_BASE:
116 		snprintf(buffer, bufsize, "BASE: %06x", data);
117 		break;
118 
119 	case GE_CMD_VADDR:
120 		snprintf(buffer, bufsize, "VADDR: %06x => %08x", data, gstate_c.getRelativeAddress(data));
121 		break;
122 
123 	case GE_CMD_IADDR:
124 		snprintf(buffer, bufsize, "IADDR: %06x => %08x", data, gstate_c.getRelativeAddress(data));
125 		break;
126 
127 	case GE_CMD_PRIM:
128 		{
129 			u32 count = data & 0xFFFF;
130 			u32 type = (data >> 16) & 7;
131 			static const char* types[8] = {
132 				"POINTS",
133 				"LINES",
134 				"LINE_STRIP",
135 				"TRIANGLES",
136 				"TRIANGLE_STRIP",
137 				"TRIANGLE_FAN",
138 				"RECTANGLES",
139 				"CONTINUE_PREVIOUS",
140 			};
141 			if (gstate.vertType & GE_VTYPE_IDX_MASK)
142 				snprintf(buffer, bufsize, "DRAW PRIM %s: count= %i vaddr= %08x, iaddr= %08x", type < 7 ? types[type] : "INVALID", count, gstate_c.vertexAddr, gstate_c.indexAddr);
143 			else
144 				snprintf(buffer, bufsize, "DRAW PRIM %s: count= %i vaddr= %08x", type < 7 ? types[type] : "INVALID", count, gstate_c.vertexAddr);
145 		}
146 		break;
147 
148 	case GE_CMD_BEZIER:
149 		{
150 			int bz_ucount = data & 0xFF;
151 			int bz_vcount = (data >> 8) & 0xFF;
152 			if (data & 0xFF0000)
153 				snprintf(buffer, bufsize, "DRAW BEZIER: %i x %i (extra %x)", bz_ucount, bz_vcount, data >> 16);
154 			else
155 				snprintf(buffer, bufsize, "DRAW BEZIER: %i x %i", bz_ucount, bz_vcount);
156 		}
157 		break;
158 
159 	case GE_CMD_SPLINE:
160 		{
161 			int sp_ucount = data & 0xFF;
162 			int sp_vcount = (data >> 8) & 0xFF;
163 			int sp_utype = (data >> 16) & 0x3;
164 			int sp_vtype = (data >> 18) & 0x3;
165 			if (data & 0xF00000)
166 				snprintf(buffer, bufsize, "DRAW SPLINE: %i x %i (type %ix%i, extra %x)", sp_ucount, sp_vcount, sp_utype, sp_vtype, data >> 20);
167 			else
168 				snprintf(buffer, bufsize, "DRAW SPLINE: %i x %i (type %ix%i)", sp_ucount, sp_vcount, sp_utype, sp_vtype);
169 		}
170 		break;
171 
172 	case GE_CMD_JUMP:
173 		{
174 			u32 target = (((gstate.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF;
175 			snprintf(buffer, bufsize, "JUMP: %08x to %08x", pc, target);
176 		}
177 		break;
178 
179 	case GE_CMD_CALL:
180 		{
181 			u32 retval = pc + 4;
182 			u32 target = gstate_c.getRelativeAddress(op & 0xFFFFFF);
183 			snprintf(buffer, bufsize, "CALL: %08x to %08x, ret=%08x", pc, target, retval);
184 		}
185 		break;
186 
187 	case GE_CMD_RET:
188 		if (data)
189 			snprintf(buffer, bufsize, "RET: data= %06x", data);
190 		else
191 			snprintf(buffer, bufsize, "RET");
192 		break;
193 
194 	case GE_CMD_SIGNAL:
195 		snprintf(buffer, bufsize, "SIGNAL %06x", data);
196 		break;
197 
198 	case GE_CMD_FINISH:
199 		snprintf(buffer, bufsize, "FINISH %06x", data);
200 		break;
201 
202 	case GE_CMD_END:
203 		switch (prev >> 24)
204 		{
205 		case GE_CMD_SIGNAL:
206 			{
207 				snprintf(buffer, bufsize, "END - ");
208 				// TODO: see http://code.google.com/p/jpcsp/source/detail?r=2935#
209 				int behaviour = (prev >> 16) & 0xFF;
210 				int signal = prev & 0xFFFF;
211 				int enddata = data & 0xFFFFFF;
212 				// We should probably defer to sceGe here, no sense in implementing this stuff in every GPU
213 				switch (behaviour) {
214 				case 1:
215 					snprintf(buffer, bufsize, "Signal with wait. signal/end: %04x %04x", signal, enddata);
216 					break;
217 				case 2:
218 					snprintf(buffer, bufsize, "Signal without wait. signal/end: %04x %04x", signal, enddata);
219 					break;
220 				case 3:
221 					snprintf(buffer, bufsize, "Signal with pause. signal/end: %04x %04x", signal, enddata);
222 					break;
223 				case 8:
224 					snprintf(buffer, bufsize, "Signal with sync. signal/end: %04x %04x", signal, enddata);
225 					break;
226 				case 0x10:
227 					snprintf(buffer, bufsize, "Signal with jump. signal/end: %04x %04x", signal, enddata);
228 					break;
229 				case 0x11:
230 					snprintf(buffer, bufsize, "Signal with call. signal/end: %04x %04x", signal, enddata);
231 					break;
232 				case 0x12:
233 					snprintf(buffer, bufsize, "Signal with return. signal/end: %04x %04x", signal, enddata);
234 					break;
235 				default:
236 					snprintf(buffer, bufsize, "UNKNOWN Signal UNIMPLEMENTED %i! signal/end: %04x %04x", behaviour, signal, enddata);
237 					break;
238 				}
239 			}
240 			break;
241 		case GE_CMD_FINISH:
242 			if (data)
243 				snprintf(buffer, bufsize, "END: data= %06x", data);
244 			else
245 				snprintf(buffer, bufsize, "END");
246 			break;
247 		default:
248 			snprintf(buffer, bufsize, "END: %06x, not finished (%08x)", data, prev);
249 			break;
250 		}
251 		break;
252 
253 	case GE_CMD_BJUMP:
254 		{
255 			u32 target = (((gstate.base & 0x00FF0000) << 8) | (op & 0xFFFFFC)) & 0x0FFFFFFF;
256 			snprintf(buffer, bufsize, "BBOX_JUMP: target= %08x", target);
257 		}
258 		break;
259 
260 	case GE_CMD_BOUNDINGBOX:
261 		snprintf(buffer, bufsize, "BBOX_TEST: %06x", data);
262 		break;
263 
264 	case GE_CMD_ORIGIN:
265 		snprintf(buffer, bufsize, "ORIGIN: %06x", data);
266 		break;
267 
268 	case GE_CMD_VERTEXTYPE:
269 		{
270 			int len = snprintf(buffer, bufsize, "SetVertexType: ");
271 			GeDescribeVertexType(op, buffer + len, bufsize - len);
272 		}
273 		break;
274 
275 	case GE_CMD_OFFSETADDR:
276 		snprintf(buffer, bufsize, "OffsetAddr: %06x", data);
277 		break;
278 
279 	case GE_CMD_REGION1:
280 		{
281 			int x1 = data & 0x3ff;
282 			int y1 = data >> 10;
283 			if (data & 0xF00000)
284 				snprintf(buffer, bufsize, "Region TL: %d %d (extra %x)", x1, y1, data >> 20);
285 			else
286 				snprintf(buffer, bufsize, "Region TL: %d %d", x1, y1);
287 		}
288 		break;
289 
290 	case GE_CMD_REGION2:
291 		{
292 			int x2 = data & 0x3ff;
293 			int y2 = data >> 10;
294 			if (data & 0xF00000)
295 				snprintf(buffer, bufsize, "Region BR: %d %d (extra %x)", x2, y2, data >> 20);
296 			else
297 				snprintf(buffer, bufsize, "Region BR: %d %d", x2, y2);
298 		}
299 		break;
300 
301 	case GE_CMD_DEPTHCLAMPENABLE:
302 		snprintf(buffer, bufsize, "Depth clamp enable: %i", data);
303 		break;
304 
305 	case GE_CMD_CULLFACEENABLE:
306 		snprintf(buffer, bufsize, "CullFace enable: %i", data);
307 		break;
308 
309 	case GE_CMD_TEXTUREMAPENABLE:
310 		snprintf(buffer, bufsize, "Texture map enable: %i", data);
311 		break;
312 
313 	case GE_CMD_LIGHTINGENABLE:
314 		snprintf(buffer, bufsize, "Lighting enable: %i", data);
315 		break;
316 
317 	case GE_CMD_FOGENABLE:
318 		snprintf(buffer, bufsize, "Fog enable: %i", data);
319 		break;
320 
321 	case GE_CMD_DITHERENABLE:
322 		snprintf(buffer, bufsize, "Dither enable: %i", data);
323 		break;
324 
325 	case GE_CMD_OFFSETX:
326 		snprintf(buffer, bufsize, "Offset X: %i", data);
327 		break;
328 
329 	case GE_CMD_OFFSETY:
330 		snprintf(buffer, bufsize, "Offset Y: %i", data);
331 		break;
332 
333 	case GE_CMD_TEXSCALEU:
334 		snprintf(buffer, bufsize, "Texture U scale: %f", getFloat24(data));
335 		break;
336 
337 	case GE_CMD_TEXSCALEV:
338 		snprintf(buffer, bufsize, "Texture V scale: %f", getFloat24(data));
339 		break;
340 
341 	case GE_CMD_TEXOFFSETU:
342 		snprintf(buffer, bufsize, "Texture U offset: %f", getFloat24(data));
343 		break;
344 
345 	case GE_CMD_TEXOFFSETV:
346 		snprintf(buffer, bufsize, "Texture V offset: %f", getFloat24(data));
347 		break;
348 
349 	case GE_CMD_SCISSOR1:
350 		{
351 			int x1 = data & 0x3ff;
352 			int y1 = data >> 10;
353 			if (data & 0xF00000)
354 				snprintf(buffer, bufsize, "Scissor TL: %i, %i (extra %x)", x1, y1, data >> 20);
355 			else
356 				snprintf(buffer, bufsize, "Scissor TL: %i, %i", x1, y1);
357 		}
358 		break;
359 	case GE_CMD_SCISSOR2:
360 		{
361 			int x2 = data & 0x3ff;
362 			int y2 = data >> 10;
363 			if (data & 0xF00000)
364 				snprintf(buffer, bufsize, "Scissor BR: %i, %i (extra %x)", x2, y2, data >> 20);
365 			else
366 				snprintf(buffer, bufsize, "Scissor BR: %i, %i", x2, y2);
367 		}
368 		break;
369 
370 	case GE_CMD_MINZ:
371 		{
372 			float zMin = getFloat24(data) / 65535.f;
373 			snprintf(buffer, bufsize, "MinZ: %f", zMin);
374 		}
375 		break;
376 
377 	case GE_CMD_MAXZ:
378 		{
379 			float zMax = getFloat24(data) / 65535.f;
380 			snprintf(buffer, bufsize, "MaxZ: %f", zMax);
381 		}
382 		break;
383 
384 	case GE_CMD_FRAMEBUFPTR:
385 		{
386 			snprintf(buffer, bufsize, "FramebufPtr: %08x", data);
387 		}
388 		break;
389 
390 	case GE_CMD_FRAMEBUFWIDTH:
391 		{
392 			snprintf(buffer, bufsize, "FramebufWidth: %x, address high %02x", data & 0xFFFF, data >> 16);
393 		}
394 		break;
395 
396 	case GE_CMD_FRAMEBUFPIXFORMAT:
397 		snprintf(buffer, bufsize, "FramebufPixelFormat: %i", data);
398 		break;
399 
400 	case GE_CMD_TEXADDR0:
401 	case GE_CMD_TEXADDR1:
402 	case GE_CMD_TEXADDR2:
403 	case GE_CMD_TEXADDR3:
404 	case GE_CMD_TEXADDR4:
405 	case GE_CMD_TEXADDR5:
406 	case GE_CMD_TEXADDR6:
407 	case GE_CMD_TEXADDR7:
408 		snprintf(buffer, bufsize, "Texture address %i: %06x", cmd-GE_CMD_TEXADDR0, data);
409 		break;
410 
411 	case GE_CMD_TEXBUFWIDTH0:
412 	case GE_CMD_TEXBUFWIDTH1:
413 	case GE_CMD_TEXBUFWIDTH2:
414 	case GE_CMD_TEXBUFWIDTH3:
415 	case GE_CMD_TEXBUFWIDTH4:
416 	case GE_CMD_TEXBUFWIDTH5:
417 	case GE_CMD_TEXBUFWIDTH6:
418 	case GE_CMD_TEXBUFWIDTH7:
419 		snprintf(buffer, bufsize, "Texture BUFWIDTH %i: %06x", cmd-GE_CMD_TEXBUFWIDTH0, data);
420 		break;
421 
422 	case GE_CMD_CLUTADDR:
423 		snprintf(buffer, bufsize, "CLUT base addr: %06x", data);
424 		break;
425 
426 	case GE_CMD_CLUTADDRUPPER:
427 		snprintf(buffer, bufsize, "CLUT addr upper %08x", data);
428 		break;
429 
430 	case GE_CMD_LOADCLUT:
431 		// This could be used to "dirty" textures with clut.
432 		if (data)
433 			snprintf(buffer, bufsize, "Clut load: %08x, %d bytes, %06x", gstate.getClutAddress(), (data & 0x3F) << 5, data & 0xFFFFC0);
434 		else
435 			snprintf(buffer, bufsize, "Clut load");
436 		break;
437 
438 	case GE_CMD_TEXMAPMODE:
439 		snprintf(buffer, bufsize, "Tex map mode: %06x", data);
440 		break;
441 
442 	case GE_CMD_TEXSHADELS:
443 		snprintf(buffer, bufsize, "Tex shade light sources: %06x", data);
444 		break;
445 
446 	case GE_CMD_CLUTFORMAT:
447 		{
448 			const char *clutformats[] = {
449 				"BGR 5650",
450 				"ABGR 1555",
451 				"ABGR 4444",
452 				"ABGR 8888",
453 			};
454 			snprintf(buffer, bufsize, "Clut format: %06x (%s)", data, clutformats[data & 3]);
455 		}
456 		break;
457 
458 	case GE_CMD_TRANSFERSRC:
459 		{
460 			if (data & 0xF)
461 				snprintf(buffer, bufsize, "Block transfer src: %06x (extra: %x)", data & ~0xF, data & 0xF);
462 			else
463 				snprintf(buffer, bufsize, "Block transfer src: %06x", data);
464 			// Nothing to do, the next one prints
465 		}
466 		break;
467 
468 	case GE_CMD_TRANSFERSRCW:
469 		{
470 			u32 xferSrc = (gstate.transfersrc & 0x00FFFFFF) | ((data & 0xFF0000) << 8);
471 			u32 xferSrcW = data & 0x3FF;
472 			if (data & ~0xFF03FF)
473 				snprintf(buffer, bufsize, "Block transfer src: %08x	W: %i (extra %x)", xferSrc, xferSrcW, data);
474 			else
475 				snprintf(buffer, bufsize, "Block transfer src: %08x	W: %i", xferSrc, xferSrcW);
476 			break;
477 		}
478 
479 	case GE_CMD_TRANSFERDST:
480 		{
481 			// Nothing to do, the next one prints
482 			if (data & 0xF)
483 				snprintf(buffer, bufsize, "Block transfer dst: %06x (extra: %x)", data & ~0xF, data & 0xF);
484 			else
485 				snprintf(buffer, bufsize, "Block transfer dst: %06x", data);
486 		}
487 		break;
488 
489 	case GE_CMD_TRANSFERDSTW:
490 		{
491 			u32 xferDst = (gstate.transferdst & 0x00FFFFFF) | ((data & 0xFF0000) << 8);
492 			u32 xferDstW = data & 0x3FF;
493 			if (data & ~0xFF03FF)
494 				snprintf(buffer, bufsize, "Block transfer dest: %08x	W: %i (extra %x)", xferDst, xferDstW, data);
495 			else
496 				snprintf(buffer, bufsize, "Block transfer dest: %08x	W: %i", xferDst, xferDstW);
497 			break;
498 		}
499 
500 	case GE_CMD_TRANSFERSRCPOS:
501 		{
502 			u32 x = (data & 1023);
503 			u32 y = ((data>>10) & 1023);
504 			if (data & 0xF00000)
505 				snprintf(buffer, bufsize, "Block transfer src rect TL: %i, %i (extra %x)", x, y, data >> 20);
506 			else
507 				snprintf(buffer, bufsize, "Block transfer src rect TL: %i, %i", x, y);
508 			break;
509 		}
510 
511 	case GE_CMD_TRANSFERDSTPOS:
512 		{
513 			u32 x = (data & 1023);
514 			u32 y = ((data>>10) & 1023);
515 			if (data & 0xF00000)
516 				snprintf(buffer, bufsize, "Block transfer dest rect TL: %i, %i (extra %x)", x, y, data >> 20);
517 			else
518 				snprintf(buffer, bufsize, "Block transfer dest rect TL: %i, %i", x, y);
519 			break;
520 		}
521 
522 	case GE_CMD_TRANSFERSIZE:
523 		{
524 			u32 w = (data & 1023)+1;
525 			u32 h = ((data>>10) & 1023)+1;
526 			if (data & 0xF00000)
527 				snprintf(buffer, bufsize, "Block transfer rect size: %i x %i (extra %x)", w, h, data >> 20);
528 			else
529 				snprintf(buffer, bufsize, "Block transfer rect size: %i x %i", w, h);
530 			break;
531 		}
532 
533 	case GE_CMD_TRANSFERSTART:
534 		if (data & ~1)
535 			snprintf(buffer, bufsize, "Block transfer start: %d (extra %x)", data & 1, data & ~1);
536 		else
537 			snprintf(buffer, bufsize, "Block transfer start: %d", data);
538 		break;
539 
540 	case GE_CMD_TEXSIZE0:
541 	case GE_CMD_TEXSIZE1:
542 	case GE_CMD_TEXSIZE2:
543 	case GE_CMD_TEXSIZE3:
544 	case GE_CMD_TEXSIZE4:
545 	case GE_CMD_TEXSIZE5:
546 	case GE_CMD_TEXSIZE6:
547 	case GE_CMD_TEXSIZE7:
548 		{
549 			int w = 1 << (data & 0xf);
550 			int h = 1 << ((data>>8) & 0xf);
551 			snprintf(buffer, bufsize, "Texture size %i: %06x, width : %d, height : %d", cmd - GE_CMD_TEXSIZE0, data, w, h);
552 		}
553 		break;
554 
555 	case GE_CMD_ZBUFPTR:
556 		{
557 			snprintf(buffer, bufsize, "Zbuf ptr: %06x", data);
558 		}
559 		break;
560 
561 	case GE_CMD_ZBUFWIDTH:
562 		snprintf(buffer, bufsize, "Zbuf width: %06x", data);
563 		break;
564 
565 	case GE_CMD_AMBIENTCOLOR:
566 		snprintf(buffer, bufsize, "Ambient color: %06x", data);
567 		break;
568 
569 	case GE_CMD_AMBIENTALPHA:
570 		snprintf(buffer, bufsize, "Ambient alpha: %06x", data);
571 		break;
572 
573 	case GE_CMD_MATERIALAMBIENT:
574 		snprintf(buffer, bufsize, "Material ambient color: %06x", data);
575 		break;
576 
577 	case GE_CMD_MATERIALDIFFUSE:
578 		snprintf(buffer, bufsize, "Material diffuse color: %06x", data);
579 		break;
580 
581 	case GE_CMD_MATERIALEMISSIVE:
582 		snprintf(buffer, bufsize, "Material emissive color: %06x", data);
583 		break;
584 
585 	case GE_CMD_MATERIALSPECULAR:
586 		snprintf(buffer, bufsize, "Material specular color: %06x", data);
587 		break;
588 
589 	case GE_CMD_MATERIALALPHA:
590 		snprintf(buffer, bufsize, "Material alpha color: %06x", data);
591 		break;
592 
593 	case GE_CMD_MATERIALSPECULARCOEF:
594 		snprintf(buffer, bufsize, "Material specular coef: %f", getFloat24(data));
595 		break;
596 
597 	case GE_CMD_SHADEMODE:
598 		if (data & ~1)
599 			snprintf(buffer, bufsize, "Shade: %06x (%s, extra %x)", data, data ? "gouraud" : "flat", data);
600 		else
601 			snprintf(buffer, bufsize, "Shade: %06x (%s)", data, data ? "gouraud" : "flat");
602 		break;
603 
604 	case GE_CMD_LIGHTMODE:
605 		if (data & ~1)
606 			snprintf(buffer, bufsize, "Lightmode: %06x (%s, extra %x)", data, data ? "separate spec" : "single color", data);
607 		else
608 			snprintf(buffer, bufsize, "Lightmode: %06x (%s)", data, data ? "separate spec" : "single color");
609 		break;
610 
611 	case GE_CMD_LIGHTTYPE0:
612 	case GE_CMD_LIGHTTYPE1:
613 	case GE_CMD_LIGHTTYPE2:
614 	case GE_CMD_LIGHTTYPE3:
615 		snprintf(buffer, bufsize, "Light %i type: %06x", cmd-GE_CMD_LIGHTTYPE0, data);
616 		break;
617 
618 	case GE_CMD_LX0:case GE_CMD_LY0:case GE_CMD_LZ0:
619 	case GE_CMD_LX1:case GE_CMD_LY1:case GE_CMD_LZ1:
620 	case GE_CMD_LX2:case GE_CMD_LY2:case GE_CMD_LZ2:
621 	case GE_CMD_LX3:case GE_CMD_LY3:case GE_CMD_LZ3:
622 		{
623 			int n = cmd - GE_CMD_LX0;
624 			int l = n / 3;
625 			int c = n % 3;
626 			float val = getFloat24(data);
627 			snprintf(buffer, bufsize, "Light %i %c pos: %f", l, c+'X', val);
628 		}
629 		break;
630 
631 	case GE_CMD_LDX0:case GE_CMD_LDY0:case GE_CMD_LDZ0:
632 	case GE_CMD_LDX1:case GE_CMD_LDY1:case GE_CMD_LDZ1:
633 	case GE_CMD_LDX2:case GE_CMD_LDY2:case GE_CMD_LDZ2:
634 	case GE_CMD_LDX3:case GE_CMD_LDY3:case GE_CMD_LDZ3:
635 		{
636 			int n = cmd - GE_CMD_LDX0;
637 			int l = n / 3;
638 			int c = n % 3;
639 			float val = getFloat24(data);
640 			snprintf(buffer, bufsize, "Light %i %c dir: %f", l, c+'X', val);
641 		}
642 		break;
643 
644 	case GE_CMD_LKA0:case GE_CMD_LKB0:case GE_CMD_LKC0:
645 	case GE_CMD_LKA1:case GE_CMD_LKB1:case GE_CMD_LKC1:
646 	case GE_CMD_LKA2:case GE_CMD_LKB2:case GE_CMD_LKC2:
647 	case GE_CMD_LKA3:case GE_CMD_LKB3:case GE_CMD_LKC3:
648 		{
649 			int n = cmd - GE_CMD_LKA0;
650 			int l = n / 3;
651 			int c = n % 3;
652 			float val = getFloat24(data);
653 			snprintf(buffer, bufsize, "Light %i %c att: %f", l, c+'X', val);
654 		}
655 		break;
656 
657 	case GE_CMD_LAC0:case GE_CMD_LAC1:case GE_CMD_LAC2:case GE_CMD_LAC3:
658 	case GE_CMD_LDC0:case GE_CMD_LDC1:case GE_CMD_LDC2:case GE_CMD_LDC3:
659 	case GE_CMD_LSC0:case GE_CMD_LSC1:case GE_CMD_LSC2:case GE_CMD_LSC3:
660 		{
661 			float r = (float)(data & 0xff)/255.0f;
662 			float g = (float)((data>>8) & 0xff)/255.0f;
663 			float b = (float)(data>>16)/255.0f;
664 
665 			int l = (cmd - GE_CMD_LAC0) / 3;
666 			int t = (cmd - GE_CMD_LAC0) % 3;
667 			snprintf(buffer, bufsize, "Light %i color %i: %f %f %f", l, t, r, g, b);
668 		}
669 		break;
670 
671 	case GE_CMD_VIEWPORTXSCALE:
672 	case GE_CMD_VIEWPORTYSCALE:
673 	case GE_CMD_VIEWPORTXCENTER:
674 	case GE_CMD_VIEWPORTYCENTER:
675 		snprintf(buffer, bufsize, "Viewport param %i: %f", cmd-GE_CMD_VIEWPORTXSCALE, getFloat24(data));
676 		break;
677 	case GE_CMD_VIEWPORTZSCALE:
678 		{
679 			float zScale = getFloat24(data) / 65535.f;
680 			snprintf(buffer, bufsize, "Viewport Z scale: %f", zScale);
681 		}
682 		break;
683 	case GE_CMD_VIEWPORTZCENTER:
684 		{
685 			float zOff = getFloat24(data) / 65535.f;
686 			snprintf(buffer, bufsize, "Viewport Z pos: %f", zOff);
687 		}
688 		break;
689 
690 	case GE_CMD_LIGHTENABLE0:
691 	case GE_CMD_LIGHTENABLE1:
692 	case GE_CMD_LIGHTENABLE2:
693 	case GE_CMD_LIGHTENABLE3:
694 		snprintf(buffer, bufsize, "Light %i enable: %d", cmd-GE_CMD_LIGHTENABLE0, data);
695 		break;
696 
697 	case GE_CMD_CULL:
698 		snprintf(buffer, bufsize, "Cull: %06x", data);
699 		break;
700 
701 	case GE_CMD_PATCHDIVISION:
702 		{
703 			int patch_div_s = data & 0xFF;
704 			int patch_div_t = (data >> 8) & 0xFF;
705 			if (data & 0xFF0000)
706 				snprintf(buffer, bufsize, "Patch subdivision: %i x %i (extra %x)", patch_div_s, patch_div_t, data & 0xFF0000);
707 			else
708 				snprintf(buffer, bufsize, "Patch subdivision: %i x %i", patch_div_s, patch_div_t);
709 		}
710 		break;
711 
712 	case GE_CMD_PATCHPRIMITIVE:
713 		snprintf(buffer, bufsize, "Patch Primitive: %d", data);
714 		break;
715 
716 	case GE_CMD_PATCHFACING:
717 		snprintf(buffer, bufsize, "Patch Facing: %d", data);
718 		break;
719 
720 	case GE_CMD_REVERSENORMAL:
721 		snprintf(buffer, bufsize, "Reverse normal: %d", data);
722 		break;
723 
724 	case GE_CMD_MATERIALUPDATE:
725 		snprintf(buffer, bufsize, "Material update: %d", data);
726 		break;
727 
728 
729 	//////////////////////////////////////////////////////////////////
730 	//	CLEARING
731 	//////////////////////////////////////////////////////////////////
732 	case GE_CMD_CLEARMODE:
733 		{
734 			const char *clearModes[] = {
735 				"on",
736 				"on, color",
737 				"on, alpha/stencil",
738 				"on, color, alpha/stencil",
739 				"on, depth",
740 				"on, color, depth",
741 				"on, alpha/stencil, depth",
742 				"on, color, alpha/stencil, depth",
743 			};
744 
745 			const char *mode;
746 			if (data & 1)
747 				mode = clearModes[(data >> 8) & 7];
748 			else
749 				mode = "off";
750 			snprintf(buffer, bufsize, "Clear mode: %06x (%s)", data, mode);
751 		}
752 		break;
753 
754 
755 	//////////////////////////////////////////////////////////////////
756 	//	ALPHA BLENDING
757 	//////////////////////////////////////////////////////////////////
758 	case GE_CMD_ALPHABLENDENABLE:
759 		snprintf(buffer, bufsize, "Alpha blend enable: %d", data);
760 		break;
761 
762 	case GE_CMD_BLENDMODE:
763 		{
764 			const char *blendModes[] = {
765 				"add",
766 				"subtract",
767 				"reverse subtract",
768 				"min",
769 				"max",
770 				"abs subtract",
771 				"unsupported1",
772 				"unsupported2",
773 			};
774 			const char *blendFactorsA[16] = {
775 				"dst",
776 				"1.0 - dst",
777 				"src.a",
778 				"1.0 - src.a",
779 				"dst.a",
780 				"1.0 - dst.a",
781 				"2.0 * src.a",
782 				"1.0 - 2.0 * src.a",
783 				"2.0 * dst.a",
784 				"1.0 - 2.0 * dst.a",
785 				"fixed",
786 				"fixed2",
787 				"fixed3",
788 				"fixed4",
789 				"fixed5",
790 			};
791 			const char *blendFactorsB[16] = {
792 				"src",
793 				"1.0 - src",
794 				"src.a",
795 				"1.0 - src.a",
796 				"dst.a",
797 				"1.0 - dst.a",
798 				"2.0 * src.a",
799 				"1.0 - 2.0 * src.a",
800 				"2.0 * dst.a",
801 				"1.0 - 2.0 * dst.a",
802 				"fixed",
803 				"fixed2",
804 				"fixed3",
805 				"fixed4",
806 				"fixed5",
807 			};
808 
809 			const char *blendFactorA = blendFactorsA[(data >> 0) & 0xF];
810 			const char *blendFactorB = blendFactorsB[(data >> 4) & 0xF];
811 			const char *blendMode = blendModes[(data >> 8) & 0x7];
812 
813 			if (data & ~0xFF0007FF)
814 				snprintf(buffer, bufsize, "Blend mode: %s %s, %s (extra: %06x)", blendMode, blendFactorA, blendFactorB, data & ~0xFF0007FF);
815 			else
816 				snprintf(buffer, bufsize, "Blend mode: %s %s, %s", blendMode, blendFactorA, blendFactorB);
817 		}
818 		break;
819 
820 	case GE_CMD_BLENDFIXEDA:
821 		snprintf(buffer, bufsize, "Blend fix A: %06x", data);
822 		break;
823 
824 	case GE_CMD_BLENDFIXEDB:
825 		snprintf(buffer, bufsize, "Blend fix B: %06x", data);
826 		break;
827 
828 	case GE_CMD_ALPHATESTENABLE:
829 		snprintf(buffer, bufsize, "Alpha test enable: %d", data);
830 		break;
831 
832 	case GE_CMD_ALPHATEST:
833 		{
834 			const char *alphaTestFuncs[] = { " NEVER ", " ALWAYS ", " == ", " != ", " < ", " <= ", " > ", " >= " };
835 			snprintf(buffer, bufsize, "Alpha test settings: %06x ((c & %02x)%s%02x)", data, (data >> 16) & 0xFF, alphaTestFuncs[data & 7], (data >> 8) & 0xFF);
836 		}
837 		break;
838 
839 	case GE_CMD_ANTIALIASENABLE:
840 		snprintf(buffer, bufsize, "Antialias enable: %d", data);
841 		break;
842 
843 	case GE_CMD_PATCHCULLENABLE:
844 		snprintf(buffer, bufsize, "Patch cull enable: %d", data);
845 		break;
846 
847 	case GE_CMD_COLORTESTENABLE:
848 		snprintf(buffer, bufsize, "Color test enable: %d", data);
849 		break;
850 
851 	case GE_CMD_LOGICOPENABLE:
852 		snprintf(buffer, bufsize, "Logic op enable: %d", data);
853 		break;
854 
855 	case GE_CMD_TEXFUNC:
856 		{
857 			const char *texfuncs[] = {
858 				"modulate",
859 				"decal",
860 				"blend",
861 				"replace",
862 				"add",
863 				"unsupported1",
864 				"unsupported2",
865 				"unsupported3",
866 			};
867 			if (data & ~0x10107)
868 				snprintf(buffer, bufsize, "TexFunc %i %s %s%s (extra %x)", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? " color double" : "", data);
869 			else
870 				snprintf(buffer, bufsize, "TexFunc %i %s %s%s", data & 7, data & 0x100 ? "RGBA" : "RGB", texfuncs[data & 7], data & 0x10000 ? " color double" : "");
871 		}
872 		break;
873 
874 	case GE_CMD_TEXFILTER:
875 		{
876 			int min = data & 7;
877 			int mag = (data >> 8) & 1;
878 			if (data & ~0x107)
879 				snprintf(buffer, bufsize, "TexFilter min: %i mag: %i (extra %x)", min, mag, data);
880 			else
881 				snprintf(buffer, bufsize, "TexFilter min: %i mag: %i", min, mag);
882 		}
883 		break;
884 
885 	case GE_CMD_TEXENVCOLOR:
886 		snprintf(buffer, bufsize, "TexEnvColor %06x", data);
887 		break;
888 
889 	case GE_CMD_TEXMODE:
890 		snprintf(buffer, bufsize, "TexMode %06x (%s, %d levels, %s)", data, data & 1 ? "swizzle" : "no swizzle", (data >> 16) & 7, (data >> 8) & 1 ? "separate cluts" : "shared clut");
891 		break;
892 
893 	case GE_CMD_TEXFORMAT:
894 		{
895 			const char *texformats[] = {
896 				"5650",
897 				"5551",
898 				"4444",
899 				"8888",
900 				"CLUT4",
901 				"CLUT8",
902 				"CLUT16",
903 				"CLUT32",
904 				"DXT1",
905 				"DXT3",
906 				"DXT5",
907 				"unsupported1",
908 				"unsupported2",
909 				"unsupported3",
910 				"unsupported4",
911 				"unsupported5",
912 			};
913 			snprintf(buffer, bufsize, "TexFormat %06x (%s)", data, texformats[data & 0xF]);
914 		}
915 		break;
916 
917 	case GE_CMD_TEXFLUSH:
918 		if (data)
919 			snprintf(buffer, bufsize, "TexFlush: %x", data);
920 		else
921 			snprintf(buffer, bufsize, "TexFlush");
922 		break;
923 
924 	case GE_CMD_TEXSYNC:
925 		if (data)
926 			snprintf(buffer, bufsize, "TexSync: %x", data);
927 		else
928 			snprintf(buffer, bufsize, "TexSync");
929 		break;
930 
931 	case GE_CMD_TEXWRAP:
932 		if (data & ~0x0101)
933 			snprintf(buffer, bufsize, "TexWrap %s s, %s t (extra %x)", data & 1 ? "clamp" : "wrap", data & 0x100 ? "clamp" : "wrap", data);
934 		else
935 			snprintf(buffer, bufsize, "TexWrap %s s, %s t", data & 1 ? "clamp" : "wrap", data & 0x100 ? "clamp" : "wrap");
936 		break;
937 
938 	case GE_CMD_TEXLEVEL:
939 		if (data & ~0xFF0003)
940 			snprintf(buffer, bufsize, "TexLevel mode: %i Offset: %i (extra %x)", data&3, data >> 16, data);
941 		else
942 			snprintf(buffer, bufsize, "TexLevel mode: %i Offset: %i", data&3, data >> 16);
943 		break;
944 
945 	case GE_CMD_FOG1:
946 		snprintf(buffer, bufsize, "Fog1 %f", getFloat24(data));
947 		break;
948 
949 	case GE_CMD_FOG2:
950 		snprintf(buffer, bufsize, "Fog2 %f", getFloat24(data));
951 		break;
952 
953 	case GE_CMD_FOGCOLOR:
954 		snprintf(buffer, bufsize, "FogColor %06x", data);
955 		break;
956 
957 	case GE_CMD_TEXLODSLOPE:
958 		snprintf(buffer, bufsize, "TexLodSlope %06x", data);
959 		break;
960 
961 	//////////////////////////////////////////////////////////////////
962 	//	Z/STENCIL TESTING
963 	//////////////////////////////////////////////////////////////////
964 
965 	case GE_CMD_ZTESTENABLE:
966 		if (data & ~1)
967 			snprintf(buffer, bufsize, "Z test enable: %d (extra %x)", data & 1, data);
968 		else
969 			snprintf(buffer, bufsize, "Z test enable: %d", data & 1);
970 		break;
971 
972 	case GE_CMD_STENCILOP:
973 		{
974 			const char *stencilOps[] = { "KEEP", "ZERO", "REPLACE", "INVERT", "INCREMENT", "DECREMENT", "unsupported1", "unsupported2" };
975 			snprintf(buffer, bufsize, "Stencil op: fail=%s, pass/depthfail=%s, pass=%s", stencilOps[data & 7], stencilOps[(data >> 8) & 7], stencilOps[(data >> 16) & 7]);
976 		}
977 		break;
978 
979 	case GE_CMD_STENCILTEST:
980 		{
981 			const char *zTestFuncs[] = { "NEVER", "ALWAYS", " == ", " != ", " < ", " <= ", " > ", " >= " };
982 			snprintf(buffer, bufsize, "Stencil test: %06x (%02x %s (c & %02x))", data, (data >> 8) & 0xFF, zTestFuncs[data & 7], (data >> 16) & 0xFF);
983 		}
984 		break;
985 
986 	case GE_CMD_STENCILTESTENABLE:
987 		snprintf(buffer, bufsize, "Stencil test enable: %d", data);
988 		break;
989 
990 	case GE_CMD_ZTEST:
991 		{
992 			const char *zTestFuncs[] = { "NEVER", "ALWAYS", " == ", " != ", " < ", " <= ", " > ", " >= " };
993 			snprintf(buffer, bufsize, "Z test mode: %i (%s)", data, zTestFuncs[data & 7]);
994 		}
995 		break;
996 
997 	case GE_CMD_MORPHWEIGHT0:
998 	case GE_CMD_MORPHWEIGHT1:
999 	case GE_CMD_MORPHWEIGHT2:
1000 	case GE_CMD_MORPHWEIGHT3:
1001 	case GE_CMD_MORPHWEIGHT4:
1002 	case GE_CMD_MORPHWEIGHT5:
1003 	case GE_CMD_MORPHWEIGHT6:
1004 	case GE_CMD_MORPHWEIGHT7:
1005 		{
1006 			int index = cmd - GE_CMD_MORPHWEIGHT0;
1007 			float weight = getFloat24(data);
1008 			snprintf(buffer, bufsize, "MorphWeight %i = %f", index, weight);
1009 		}
1010 		break;
1011 
1012 	case GE_CMD_DITH0:
1013 	case GE_CMD_DITH1:
1014 	case GE_CMD_DITH2:
1015 	case GE_CMD_DITH3:
1016 		snprintf(buffer, bufsize, "DitherMatrix %i = %06x",cmd-GE_CMD_DITH0,data);
1017 		break;
1018 
1019 	case GE_CMD_LOGICOP:
1020 		{
1021 			const char *logicOps[] = {
1022 				"clear",
1023 				"and",
1024 				"reverse and",
1025 				"copy",
1026 				"inverted and",
1027 				"noop",
1028 				"xor",
1029 				"or",
1030 				"negated or",
1031 				"equivalence",
1032 				"inverted",
1033 				"reverse or",
1034 				"inverted copy",
1035 				"inverted or",
1036 				"negated and",
1037 				"set",
1038 			};
1039 			snprintf(buffer, bufsize, "LogicOp: %06x (%s)", data, logicOps[data & 0xF]);
1040 		}
1041 		break;
1042 
1043 	case GE_CMD_ZWRITEDISABLE:
1044 		snprintf(buffer, bufsize, "ZMask: %06x", data);
1045 		break;
1046 
1047 	case GE_CMD_COLORTEST:
1048 		{
1049 			const char *colorTests[] = {"NEVER", "ALWAYS", " == ", " != "};
1050 			snprintf(buffer, bufsize, "ColorTest: %06x (ref%s(c & cmask))", data, colorTests[data & 3]);
1051 		}
1052 		break;
1053 
1054 	case GE_CMD_COLORREF:
1055 		snprintf(buffer, bufsize, "ColorRef: %06x", data);
1056 		break;
1057 
1058 	case GE_CMD_COLORTESTMASK:
1059 		snprintf(buffer, bufsize, "ColorTestMask: %06x", data);
1060 		break;
1061 
1062 	case GE_CMD_MASKRGB:
1063 		snprintf(buffer, bufsize, "MaskRGB: %06x", data);
1064 		break;
1065 
1066 	case GE_CMD_MASKALPHA:
1067 		snprintf(buffer, bufsize, "MaskAlpha: %06x", data);
1068 		break;
1069 
1070 	case GE_CMD_WORLDMATRIXNUMBER:
1071 		if (data & ~0xF)
1072 			snprintf(buffer, bufsize, "World # %i (extra %x)", data & 0xF, data);
1073 		else
1074 			snprintf(buffer, bufsize, "World # %i", data & 0xF);
1075 		break;
1076 
1077 	case GE_CMD_WORLDMATRIXDATA:
1078 		snprintf(buffer, bufsize, "World data # %f", getFloat24(data));
1079 		break;
1080 
1081 	case GE_CMD_VIEWMATRIXNUMBER:
1082 		if (data & ~0xF)
1083 			snprintf(buffer, bufsize, "VIEW # %i (extra %x)", data & 0xF, data);
1084 		else
1085 			snprintf(buffer, bufsize, "VIEW # %i", data & 0xF);
1086 		break;
1087 
1088 	case GE_CMD_VIEWMATRIXDATA:
1089 		snprintf(buffer, bufsize, "VIEW data # %f", getFloat24(data));
1090 		break;
1091 
1092 	case GE_CMD_PROJMATRIXNUMBER:
1093 		if (data & ~0xF)
1094 			snprintf(buffer, bufsize, "PROJECTION # %i (extra %x)", data & 0xF, data);
1095 		else
1096 			snprintf(buffer, bufsize, "PROJECTION # %i", data & 0xF);
1097 		break;
1098 
1099 	case GE_CMD_PROJMATRIXDATA:
1100 		snprintf(buffer, bufsize, "PROJECTION matrix data # %f", getFloat24(data));
1101 		break;
1102 
1103 	case GE_CMD_TGENMATRIXNUMBER:
1104 		if (data & ~0xF)
1105 			snprintf(buffer, bufsize, "TGEN # %i (extra %x)", data & 0xF, data);
1106 		else
1107 			snprintf(buffer, bufsize, "TGEN # %i", data & 0xF);
1108 		break;
1109 
1110 	case GE_CMD_TGENMATRIXDATA:
1111 		snprintf(buffer, bufsize, "TGEN data # %f", getFloat24(data));
1112 		break;
1113 
1114 	case GE_CMD_BONEMATRIXNUMBER:
1115 		if (data & ~0x7F)
1116 			snprintf(buffer, bufsize, "BONE #%i (extra %x)", data & 0x7F, data);
1117 		else
1118 			snprintf(buffer, bufsize, "BONE #%i", data & 0x7F);
1119 		break;
1120 
1121 	case GE_CMD_BONEMATRIXDATA:
1122 		snprintf(buffer, bufsize, "BONE data #%i %f", gstate.boneMatrixNumber & 0x7f, getFloat24(data));
1123 		break;
1124 
1125 	default:
1126 		snprintf(buffer, bufsize, "Unknown: %08x", op);
1127 		break;
1128 	}
1129 }
1130 
1131