fc5d46b4 |
1 | /* |
2 | * z64 |
3 | * |
4 | * Copyright (C) 2007 ziggy |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License along |
17 | * with this program; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | * |
20 | **/ |
21 | |
22 | #include "rsp_opinfo.h" |
23 | |
24 | static const int vector_elements_2[16][8] = |
25 | { |
26 | { 0, 1, 2, 3, 4, 5, 6, 7 }, // none |
27 | { 0, 1, 2, 3, 4, 5, 6, 7 }, // ??? |
28 | { 0, 0, 2, 2, 4, 4, 6, 6 }, // 0q |
29 | { 1, 1, 3, 3, 5, 5, 7, 7 }, // 1q |
30 | { 0, 0, 0, 0, 4, 4, 4, 4 }, // 0h |
31 | { 1, 1, 1, 1, 5, 5, 5, 5 }, // 1h |
32 | { 2, 2, 2, 2, 6, 6, 6, 6 }, // 2h |
33 | { 3, 3, 3, 3, 7, 7, 7, 7 }, // 3h |
34 | { 0, 0, 0, 0, 0, 0, 0, 0 }, // 0 |
35 | { 1, 1, 1, 1, 1, 1, 1, 1 }, // 1 |
36 | { 2, 2, 2, 2, 2, 2, 2, 2 }, // 2 |
37 | { 3, 3, 3, 3, 3, 3, 3, 3 }, // 3 |
38 | { 4, 4, 4, 4, 4, 4, 4, 4 }, // 4 |
39 | { 5, 5, 5, 5, 5, 5, 5, 5 }, // 5 |
40 | { 6, 6, 6, 6, 6, 6, 6, 6 }, // 6 |
41 | { 7, 7, 7, 7, 7, 7, 7, 7 }, // 7 |
42 | }; |
43 | |
44 | void rsp_get_opinfo(UINT32 op, rsp_opinfo_t * info) |
45 | { |
46 | int op2; |
47 | int i; |
48 | info->op = op; |
49 | switch (op>>26) { |
50 | case 0: /* SPECIAL */ |
51 | op2 = RSP_SPECIAL_OFFS + (op&0x3f); |
52 | break; |
53 | case 0x12: /* COP2 */ |
54 | if (((op>>21)&0x1f) >= 0x10) |
55 | op2 = RSP_COP2_2_OFFS + (op & 0x3f); |
56 | else |
57 | op2 = RSP_COP2_1_OFFS + ((op >> 21) & 0x1f); |
58 | break; |
59 | case 0x32: /* LWC2 */ |
60 | op2 = RSP_LWC2_OFFS + ((op>>11)&0x1f); |
61 | break; |
62 | case 0x3a: /* SWC2 */ |
63 | op2 = RSP_SWC2_OFFS + ((op>>11)&0x1f); |
64 | break; |
65 | default: |
66 | op2 = RSP_BASIC_OFFS + (op>>26); |
67 | if (op2 == RSP_REGIMM) { |
68 | switch (RTREG) |
69 | { |
70 | case 0x00: /* BLTZ */ |
71 | op2 = RSP_BLTZ; |
72 | break; |
73 | case 0x01: /* BGEZ */ |
74 | op2 = RSP_BGEZ; |
75 | break; |
76 | case 0x11: /* BGEZAL */ |
77 | op2 = RSP_BGEZAL; |
78 | break; |
79 | } |
80 | } |
81 | } |
82 | info->op2 = op2; |
83 | |
84 | memset(&info->used, 0, sizeof(info->used)); |
85 | memset(&info->set, 0, sizeof(info->set)); |
86 | info->used.accu = info->used.flag = 0; |
87 | info->set.accu = info->set.flag = 0; |
88 | info->flags = 0; |
89 | |
90 | int dest = (op >> 16) & 0x1f; |
91 | int index = (op >> 7) & 0xf; |
92 | int offset = (op & 0x7f); |
93 | if (offset & 0x40) |
94 | offset |= 0xffffffc0; |
95 | |
96 | switch(op2) { |
97 | case RSP_SPECIAL: |
98 | case RSP_BLTZ: |
99 | info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_USEPC; |
100 | break; |
101 | case RSP_BGEZ: |
102 | info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_USEPC; |
103 | break; |
104 | case RSP_BGEZAL: |
105 | info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_LINK | RSP_OPINFO_USEPC; |
106 | break; |
107 | case RSP_J: |
108 | info->flags = RSP_OPINFO_JUMP; |
109 | break; |
110 | case RSP_JAL: |
111 | info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_LINK | RSP_OPINFO_USEPC; |
112 | break; |
113 | case RSP_BEQ: |
114 | case RSP_BNE: |
115 | case RSP_BLEZ: |
116 | case RSP_BGTZ: |
117 | info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_COND | RSP_OPINFO_USEPC; |
118 | break; |
119 | case RSP_ADDI: |
120 | case RSP_ADDIU: |
121 | case RSP_SLTI: |
122 | case RSP_SLTIU: |
123 | case RSP_ANDI: |
124 | case RSP_ORI: |
125 | case RSP_XORI: |
126 | case RSP_LUI: |
127 | case RSP_COP0: |
128 | case RSP_LB: |
129 | case RSP_LH: |
130 | case RSP_LW: |
131 | case RSP_LBU: |
132 | case RSP_LHU: |
133 | case RSP_SB: |
134 | case RSP_SH: |
135 | case RSP_SW: |
136 | break; |
137 | |
138 | case RSP_SLL: |
139 | case RSP_SRL: |
140 | case RSP_SRA: |
141 | case RSP_SLLV: |
142 | case RSP_SRLV: |
143 | case RSP_SRAV: |
144 | break; |
145 | |
146 | case RSP_JR: |
147 | info->flags = RSP_OPINFO_JUMP; |
148 | break; |
149 | case RSP_JALR: |
150 | info->flags = RSP_OPINFO_JUMP | RSP_OPINFO_LINK | RSP_OPINFO_USEPC; |
151 | break; |
152 | case RSP_BREAK: |
153 | info->flags = RSP_OPINFO_BREAK; |
154 | break; |
155 | |
156 | case RSP_ADD: |
157 | case RSP_ADDU: |
158 | case RSP_SUB: |
159 | case RSP_SUBU: |
160 | case RSP_AND: |
161 | case RSP_OR: |
162 | case RSP_XOR: |
163 | case RSP_NOR: |
164 | case RSP_SLT: |
165 | case RSP_SLTU: |
166 | break; |
167 | |
168 | case RSP_MFC2: |
169 | { |
170 | int el = op >> 7; |
171 | RSP_SET_VEC_I(info->used, VS1REG, ((el+0)&0xf)>>1); |
172 | RSP_SET_VEC_I(info->used, VS1REG, ((el+1)&0xf)>>1); |
173 | break; |
174 | } |
175 | case RSP_CFC2: |
176 | RSP_SET_FLAG_I(info->used, RDREG & 3); |
177 | break; |
178 | case RSP_MTC2: |
179 | { |
180 | int el = op >> 7; |
181 | RSP_SET_VEC_I(info->set, VS1REG, ((el+0)&0xf)>>1); |
182 | RSP_SET_VEC_I(info->set, VS1REG, ((el+1)&0xf)>>1); |
183 | break; |
184 | } |
185 | case RSP_CTC2: |
186 | RSP_SET_FLAG_I(info->set, RDREG & 3); |
187 | break; |
188 | |
189 | |
190 | case RSP_LBV: |
191 | RSP_SET_VEC_I(info->set, dest, index>>1); |
192 | break; |
193 | case RSP_LSV: |
194 | for (i=index; i<index+2; i++) |
195 | RSP_SET_VEC_I(info->set, dest, (i>>1)&7); |
196 | break; |
197 | case RSP_LLV: |
198 | for (i=index; i<index+4; i++) |
199 | RSP_SET_VEC_I(info->set, dest, (i>>1)&7); |
200 | break; |
201 | case RSP_LDV: |
202 | for (i=index; i<index+8; i++) |
203 | RSP_SET_VEC_I(info->set, dest, (i>>1)&7); |
204 | break; |
205 | case RSP_LQV: |
206 | case RSP_LRV: |
207 | // WARNING WARNING WARNING |
208 | // we assume this instruction always used to load the full vector |
209 | // i.e. the address is always 16 bytes aligned |
210 | // for (i=0; i<8; i++) |
211 | // RSP_SET_VEC_I(info->set, dest, i); |
212 | break; |
213 | case RSP_LPV: |
214 | case RSP_LUV: |
215 | case RSP_LHV: |
216 | case RSP_LWV: |
217 | for (i=0; i<8; i++) |
218 | RSP_SET_VEC_I(info->set, dest, i); |
219 | break; |
220 | case RSP_LFV: |
221 | for (i=(index>>1); i<(index>>1)+4; i++) |
222 | RSP_SET_VEC_I(info->set, dest, i); |
223 | break; |
224 | case RSP_LTV: |
225 | { |
226 | // 31 25 20 15 10 6 0 |
227 | // -------------------------------------------------- |
228 | // | 110010 | BBBBB | TTTTT | 01011 | IIII | Offset | |
229 | // -------------------------------------------------- |
230 | // |
231 | // Loads one element to maximum of 8 vectors, while incrementing element index |
232 | |
233 | // FIXME: has a small problem with odd indices |
234 | |
235 | int element; |
236 | int vs = dest; |
237 | int ve = dest + 8; |
238 | if (ve > 32) |
239 | ve = 32; |
240 | |
241 | element = 7 - (index >> 1); |
242 | |
243 | if (index & 1) |
244 | log(M64MSG_ERROR, "RSP: LTV: index = %d\n", index); |
245 | |
246 | for (i=vs; i < ve; i++) |
247 | { |
248 | element = ((8 - (index >> 1) + (i-vs)) << 1); |
249 | RSP_SET_VEC_I(info->set, i, (element & 0xf)>>1); |
250 | RSP_SET_VEC_I(info->set, i, ((element+1) & 0xf)>>1); |
251 | } |
252 | break; |
253 | } |
254 | |
255 | case RSP_SBV: |
256 | RSP_SET_VEC_I(info->used, dest, index>>1); |
257 | break; |
258 | case RSP_SSV: |
259 | for (i=index; i<index+2; i++) |
260 | RSP_SET_VEC_I(info->used, dest, (i>>1)&7); |
261 | break; |
262 | case RSP_SLV: |
263 | for (i=index; i<index+4; i++) |
264 | RSP_SET_VEC_I(info->used, dest, (i>>1)&7); |
265 | break; |
266 | case RSP_SDV: |
267 | for (i=index; i<index+8; i++) |
268 | RSP_SET_VEC_I(info->used, dest, (i>>1)&7); |
269 | break; |
270 | case RSP_SQV: |
271 | case RSP_SRV: |
272 | // WARNING WARNING WARNING |
273 | // we assume this instruction always used to store the full vector |
274 | // i.e. the address is always 16 bytes aligned |
275 | for (i=0; i<8; i++) |
276 | RSP_SET_VEC_I(info->used, dest, i); |
277 | break; |
278 | case RSP_SPV: |
279 | case RSP_SUV: |
280 | case RSP_SHV: |
281 | case RSP_SWV: |
282 | for (i=0; i<8; i++) |
283 | RSP_SET_VEC_I(info->used, dest, i); |
284 | break; |
285 | case RSP_SFV: |
286 | for (i=(index>>1); i<(index>>1)+4; i++) |
287 | RSP_SET_VEC_I(info->used, dest, i); |
288 | break; |
289 | case RSP_STV: |
290 | { |
291 | // 31 25 20 15 10 6 0 |
292 | // -------------------------------------------------- |
293 | // | 111010 | BBBBB | TTTTT | 01011 | IIII | Offset | |
294 | // -------------------------------------------------- |
295 | // |
296 | // Stores one element from maximum of 8 vectors, while incrementing element index |
297 | |
298 | int element; |
299 | int vs = dest; |
300 | int ve = dest + 8; |
301 | if (ve > 32) |
302 | ve = 32; |
303 | |
304 | element = 8 - (index >> 1); |
305 | if (index & 0x1) |
306 | log(M64MSG_ERROR, "RSP: STV: index = %d at %08X\n", index, rsp.ppc); |
307 | |
308 | for (i=vs; i < ve; i++) |
309 | { |
310 | RSP_SET_VEC_I(info->used, i, element & 0x7); |
311 | element++; |
312 | } |
313 | break; |
314 | } |
315 | |
316 | case RSP_VMULF: |
317 | case RSP_VMULU: |
318 | case RSP_VMUDL: |
319 | case RSP_VMUDM: |
320 | case RSP_VMUDN: |
321 | case RSP_VMUDH: |
322 | { |
323 | for (i=0; i < 8; i++) |
324 | { |
325 | int sel = VEC_EL_2(EL, i); |
326 | RSP_SET_VEC_I(info->used, VS1REG, i); |
327 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
328 | RSP_SET_VEC_I(info->set, VDREG, i); |
329 | RSP_SET_ACCU_I(info->set, i, 14); |
330 | } |
331 | break; |
332 | } |
333 | case RSP_VMACF: |
334 | case RSP_VMACU: |
335 | case RSP_VMADL: |
336 | case RSP_VMADM: |
337 | case RSP_VMADN: |
338 | case RSP_VMADH: |
339 | { |
340 | for (i=0; i < 8; i++) |
341 | { |
342 | int sel = VEC_EL_2(EL, i); |
343 | RSP_SET_VEC_I(info->used, VS1REG, i); |
344 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
345 | RSP_SET_VEC_I(info->set, VDREG, i); |
346 | RSP_SET_ACCU_I(info->used, i, 14); |
347 | RSP_SET_ACCU_I(info->set, i, 14); |
348 | } |
349 | break; |
350 | } |
351 | case RSP_VADD: |
352 | case RSP_VSUB: |
353 | { |
354 | for (i=0; i < 8; i++) |
355 | { |
356 | int sel = VEC_EL_2(EL, i); |
357 | RSP_SET_VEC_I(info->used, VS1REG, i); |
358 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
359 | RSP_SET_VEC_I(info->set, VDREG, i); |
360 | RSP_SET_ACCU_I(info->set, i, 2); |
361 | } |
362 | RSP_SET_FLAG_I(info->used, 0); |
363 | RSP_SET_FLAG_I(info->set, 0); |
364 | break; |
365 | } |
366 | case RSP_VABS: |
367 | { |
368 | for (i=0; i < 8; i++) |
369 | { |
370 | int sel = VEC_EL_2(EL, i); |
371 | RSP_SET_VEC_I(info->used, VS1REG, i); |
372 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
373 | RSP_SET_VEC_I(info->set, VDREG, i); |
374 | RSP_SET_ACCU_I(info->set, i, 2); |
375 | } |
376 | break; |
377 | } |
378 | case RSP_VADDC: |
379 | case RSP_VSUBC: |
380 | { |
381 | for (i=0; i < 8; i++) |
382 | { |
383 | int sel = VEC_EL_2(EL, i); |
384 | RSP_SET_VEC_I(info->used, VS1REG, i); |
385 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
386 | RSP_SET_VEC_I(info->set, VDREG, i); |
387 | RSP_SET_ACCU_I(info->set, i, 2); |
388 | } |
389 | RSP_SET_FLAG_I(info->set, 0); |
390 | break; |
391 | } |
392 | case RSP_VSAW: |
393 | switch (EL) |
394 | { |
395 | case 0x08: // VSAWH |
396 | { |
397 | for (i=0; i < 8; i++) |
398 | { |
399 | RSP_SET_VEC_I(info->set, VDREG, i); |
400 | RSP_SET_ACCU_I(info->used, i, 8); |
401 | } |
402 | break; |
403 | } |
404 | case 0x09: // VSAWM |
405 | { |
406 | for (i=0; i < 8; i++) |
407 | { |
408 | RSP_SET_VEC_I(info->set, VDREG, i); |
409 | RSP_SET_ACCU_I(info->used, i, 4); |
410 | } |
411 | break; |
412 | } |
413 | case 0x0a: // VSAWL |
414 | { |
415 | for (i=0; i < 8; i++) |
416 | { |
417 | RSP_SET_VEC_I(info->set, VDREG, i); |
418 | RSP_SET_ACCU_I(info->used, i, 2); |
419 | } |
420 | break; |
421 | } |
422 | default: |
423 | log(M64MSG_ERROR, "RSP: VSAW: el = %d\n", EL); |
424 | } |
425 | break; |
426 | case RSP_VLT: |
427 | { |
428 | for (i=0; i < 8; i++) |
429 | { |
430 | int sel = VEC_EL_2(EL, i); |
431 | RSP_SET_VEC_I(info->used, VS1REG, i); |
432 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
433 | RSP_SET_VEC_I(info->set, VDREG, i); |
434 | RSP_SET_ACCU_I(info->set, i, 2); |
435 | } |
436 | RSP_SET_FLAG_I(info->set, 0); |
437 | RSP_SET_FLAG_I(info->set, 1); |
438 | break; |
439 | } |
440 | case RSP_VEQ: |
441 | case RSP_VNE: |
442 | case RSP_VGE: |
443 | { |
444 | for (i=0; i < 8; i++) |
445 | { |
446 | int sel = VEC_EL_2(EL, i); |
447 | RSP_SET_VEC_I(info->used, VS1REG, i); |
448 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
449 | RSP_SET_VEC_I(info->set, VDREG, i); |
450 | RSP_SET_ACCU_I(info->set, i, 2); |
451 | } |
452 | RSP_SET_FLAG_I(info->used, 0); |
453 | RSP_SET_FLAG_I(info->set, 0); |
454 | RSP_SET_FLAG_I(info->set, 1); |
455 | break; |
456 | } |
457 | case RSP_VCL: |
458 | { |
459 | for (i=0; i < 8; i++) |
460 | { |
461 | int sel = VEC_EL_2(EL, i); |
462 | RSP_SET_VEC_I(info->used, VS1REG, i); |
463 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
464 | RSP_SET_VEC_I(info->set, VDREG, i); |
465 | RSP_SET_ACCU_I(info->set, i, 2); |
466 | } |
467 | RSP_SET_FLAG_I(info->used, 0); |
468 | RSP_SET_FLAG_I(info->used, 1); |
469 | RSP_SET_FLAG_I(info->set, 0); |
470 | RSP_SET_FLAG_I(info->set, 1); |
471 | RSP_SET_FLAG_I(info->set, 2); |
472 | break; |
473 | } |
474 | case RSP_VCH: |
475 | case RSP_VCR: |
476 | { |
477 | for (i=0; i < 8; i++) |
478 | { |
479 | int sel = VEC_EL_2(EL, i); |
480 | RSP_SET_VEC_I(info->used, VS1REG, i); |
481 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
482 | RSP_SET_VEC_I(info->set, VDREG, i); |
483 | RSP_SET_ACCU_I(info->set, i, 2); |
484 | } |
485 | RSP_SET_FLAG_I(info->set, 0); |
486 | RSP_SET_FLAG_I(info->set, 1); |
487 | RSP_SET_FLAG_I(info->set, 2); |
488 | break; |
489 | } |
490 | case RSP_VMRG: |
491 | { |
492 | for (i=0; i < 8; i++) |
493 | { |
494 | int sel = VEC_EL_2(EL, i); |
495 | RSP_SET_VEC_I(info->used, VS1REG, i); |
496 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
497 | RSP_SET_VEC_I(info->set, VDREG, i); |
498 | RSP_SET_ACCU_I(info->set, i, 2); |
499 | } |
500 | RSP_SET_FLAG_I(info->used, 1); |
501 | break; |
502 | } |
503 | case RSP_VAND: |
504 | case RSP_VNAND: |
505 | case RSP_VOR: |
506 | case RSP_VNOR: |
507 | case RSP_VXOR: |
508 | case RSP_VNXOR: |
509 | { |
510 | for (i=0; i < 8; i++) |
511 | { |
512 | int sel = VEC_EL_2(EL, i); |
513 | RSP_SET_VEC_I(info->used, VS1REG, i); |
514 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
515 | RSP_SET_VEC_I(info->set, VDREG, i); |
516 | RSP_SET_ACCU_I(info->set, i, 2); |
517 | } |
518 | break; |
519 | } |
520 | case RSP_VRCP: |
521 | case RSP_VRCPL: |
522 | case RSP_VRCPH: |
523 | case RSP_VRSQL: |
524 | case RSP_VRSQH: |
525 | { |
526 | int del = (VS1REG & 7); |
527 | int sel = VEC_EL_2(EL, del); |
528 | |
529 | RSP_SET_VEC_I(info->used, VS2REG, sel); |
530 | |
531 | for (i=0; i < 8; i++) |
532 | { |
533 | int element = VEC_EL_2(EL, i); |
534 | RSP_SET_VEC_I(info->used, VS2REG, element); |
535 | RSP_SET_ACCU_I(info->set, i, 2); |
536 | } |
537 | |
538 | RSP_SET_VEC_I(info->set, VDREG, del); |
539 | break; |
540 | } |
541 | case RSP_VMOV: |
542 | { |
543 | int element = VS1REG & 7; |
544 | RSP_SET_VEC_I(info->used, VS2REG, VEC_EL_2(EL, 7-element)); |
545 | RSP_SET_VEC_I(info->set, VDREG, element); |
546 | break; |
547 | } |
548 | |
549 | default: |
550 | { |
551 | // char string[200]; |
552 | // rsp_dasm_one(string, 0x800, op); |
553 | // if (strcmp(string, "???")) { |
554 | // printf("%s\n", string); |
555 | // printf("unimplemented opcode\n"); |
556 | // } |
557 | break; |
558 | } |
559 | } |
560 | } |