1 // Copyright 2010 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 // Additional copyrights go to Duddie and Tratax (c) 2004
6
7 // Multiplier and product register control
8
9 #include <cstddef>
10
11 #include "Common/CommonTypes.h"
12
13 #include "Core/DSP/DSPCore.h"
14 #include "Core/DSP/Jit/x64/DSPEmitter.h"
15
16 using namespace Gen;
17
18 namespace DSP::JIT::x64
19 {
20 // Returns s64 in RAX
21 // In: RCX = s16 a, RAX = s16 b
multiply()22 void DSPEmitter::multiply()
23 {
24 // prod = (s16)a * (s16)b; //signed
25 IMUL(64, R(ECX));
26
27 // Conditionally multiply by 2.
28 // if ((g_dsp.r.sr & SR_MUL_MODIFY) == 0)
29 const OpArg sr_reg = m_gpr.GetReg(DSP_REG_SR);
30 TEST(16, sr_reg, Imm16(SR_MUL_MODIFY));
31 FixupBranch noMult2 = J_CC(CC_NZ);
32 // prod <<= 1;
33 ADD(64, R(RAX), R(RAX));
34 SetJumpTarget(noMult2);
35 m_gpr.PutReg(DSP_REG_SR, false);
36 // return prod;
37 }
38
39 // Returns s64 in RAX
40 // Clobbers RDX
multiply_add()41 void DSPEmitter::multiply_add()
42 {
43 // s64 prod = dsp_get_long_prod() + dsp_get_multiply_prod(a, b, sign);
44 multiply();
45 MOV(64, R(RDX), R(RAX));
46 get_long_prod();
47 ADD(64, R(RAX), R(RDX));
48 // return prod;
49 }
50
51 // Returns s64 in RAX
52 // Clobbers RDX
multiply_sub()53 void DSPEmitter::multiply_sub()
54 {
55 // s64 prod = dsp_get_long_prod() - dsp_get_multiply_prod(a, b, sign);
56 multiply();
57 MOV(64, R(RDX), R(RAX));
58 get_long_prod();
59 SUB(64, R(RAX), R(RDX));
60 // return prod;
61 }
62
63 // Only MULX family instructions have unsigned/mixed support.
64 // Returns s64 in EAX
65 // In: RCX = s16 a, RAX = s16 b
66 // Returns s64 in RAX
multiply_mulx(u8 axh0,u8 axh1)67 void DSPEmitter::multiply_mulx(u8 axh0, u8 axh1)
68 {
69 // s64 result;
70
71 // if ((axh0==0) && (axh1==0))
72 // result = dsp_multiply(val1, val2, 1); // unsigned support ON if both ax?.l regs are used
73 // else if ((axh0==0) && (axh1==1))
74 // result = dsp_multiply(val1, val2, 2); // mixed support ON (u16)axl.0 * (s16)axh.1
75 // else if ((axh0==1) && (axh1==0))
76 // result = dsp_multiply(val2, val1, 2); // mixed support ON (u16)axl.1 * (s16)axh.0
77 // else
78 // result = dsp_multiply(val1, val2, 0); // unsigned support OFF if both ax?.h regs are used
79
80 // if ((sign == 1) && (g_dsp.r.sr & SR_MUL_UNSIGNED)) //unsigned
81 const OpArg sr_reg = m_gpr.GetReg(DSP_REG_SR);
82 TEST(16, sr_reg, Imm16(SR_MUL_UNSIGNED));
83 FixupBranch unsignedMul = J_CC(CC_NZ);
84 // prod = (s16)a * (s16)b; //signed
85 MOVSX(64, 16, RAX, R(RAX));
86 IMUL(64, R(RCX));
87 FixupBranch signedMul = J(true);
88
89 SetJumpTarget(unsignedMul);
90 DSPJitRegCache c(m_gpr);
91 m_gpr.PutReg(DSP_REG_SR, false);
92 if ((axh0 == 0) && (axh1 == 0))
93 {
94 // unsigned support ON if both ax?.l regs are used
95 // prod = (u32)(a * b);
96 MOVZX(64, 16, RCX, R(RCX));
97 MOVZX(64, 16, RAX, R(RAX));
98 MUL(64, R(RCX));
99 }
100 else if ((axh0 == 0) && (axh1 == 1))
101 {
102 // mixed support ON (u16)axl.0 * (s16)axh.1
103 // prod = a * (s16)b;
104 X64Reg tmp = m_gpr.GetFreeXReg();
105 MOV(64, R(tmp), R(RAX));
106 MOVZX(64, 16, RAX, R(RCX));
107 IMUL(64, R(tmp));
108 m_gpr.PutXReg(tmp);
109 }
110 else if ((axh0 == 1) && (axh1 == 0))
111 {
112 // mixed support ON (u16)axl.1 * (s16)axh.0
113 // prod = (s16)a * b;
114 MOVZX(64, 16, RAX, R(RAX));
115 IMUL(64, R(RCX));
116 }
117 else
118 {
119 // unsigned support OFF if both ax?.h regs are used
120 // prod = (s16)a * (s16)b; //signed
121 MOVSX(64, 16, RAX, R(RAX));
122 IMUL(64, R(RCX));
123 }
124
125 m_gpr.FlushRegs(c);
126 SetJumpTarget(signedMul);
127
128 // Conditionally multiply by 2.
129 // if ((g_dsp.r.sr & SR_MUL_MODIFY) == 0)
130 TEST(16, sr_reg, Imm16(SR_MUL_MODIFY));
131 FixupBranch noMult2 = J_CC(CC_NZ);
132 // prod <<= 1;
133 ADD(64, R(RAX), R(RAX));
134 SetJumpTarget(noMult2);
135 m_gpr.PutReg(DSP_REG_SR, false);
136 // return prod;
137 }
138
139 //----
140
141 // CLRP
142 // 1000 0100 xxxx xxxx
143 // Clears product register $prod.
144 // Magic numbers taken from duddie's doc
145
146 // 00ff_(fff0 + 0010)_0000 = 0100_0000_0000, conveniently, lower 40bits = 0
147
148 // It's not ok, to just zero all of them, correct values should be set because of
149 // direct use of prod regs by AX/AXWII (look @that part of ucode).
clrp(const UDSPInstruction opc)150 void DSPEmitter::clrp(const UDSPInstruction opc)
151 {
152 int offset = static_cast<int>(offsetof(SDSP, r.prod.val));
153 // 64bit move to memory does not work. use 2 32bits
154 MOV(32, MDisp(R15, offset + 0 * sizeof(u32)), Imm32(0xfff00000U));
155 MOV(32, MDisp(R15, offset + 1 * sizeof(u32)), Imm32(0x001000ffU));
156 }
157
158 // TSTPROD
159 // 1000 0101 xxxx xxxx
160 // Test prod regs value.
161
162 // flags out: --xx xx0x
tstprod(const UDSPInstruction opc)163 void DSPEmitter::tstprod(const UDSPInstruction opc)
164 {
165 if (FlagsNeeded())
166 {
167 // s64 prod = dsp_get_long_prod();
168 get_long_prod();
169 // Update_SR_Register64(prod);
170 Update_SR_Register64();
171 }
172 }
173
174 //----
175
176 // MOVP $acD
177 // 0110 111d xxxx xxxx
178 // Moves multiply product from $prod register to accumulator $acD register.
179
180 // flags out: --xx xx0x
movp(const UDSPInstruction opc)181 void DSPEmitter::movp(const UDSPInstruction opc)
182 {
183 u8 dreg = (opc >> 8) & 0x1;
184
185 // s64 acc = dsp_get_long_prod();
186 get_long_prod();
187 // dsp_set_long_acc(dreg, acc);
188 set_long_acc(dreg);
189 // Update_SR_Register64(acc);
190 if (FlagsNeeded())
191 {
192 Update_SR_Register64();
193 }
194 }
195
196 // MOVNP $acD
197 // 0111 111d xxxx xxxx
198 // Moves negative of multiply product from $prod register to accumulator
199 // $acD register.
200
201 // flags out: --xx xx0x
movnp(const UDSPInstruction opc)202 void DSPEmitter::movnp(const UDSPInstruction opc)
203 {
204 u8 dreg = (opc >> 8) & 0x1;
205
206 // s64 acc = -dsp_get_long_prod();
207 get_long_prod();
208 NEG(64, R(EAX));
209 // dsp_set_long_acc(dreg, acc);
210 set_long_acc(dreg);
211 // Update_SR_Register64(acc);
212 if (FlagsNeeded())
213 {
214 Update_SR_Register64();
215 }
216 }
217
218 // MOVPZ $acD
219 // 1111 111d xxxx xxxx
220 // Moves multiply product from $prod register to accumulator $acD
221 // register and sets (rounds) $acD.l to 0
222
223 // flags out: --xx xx0x
movpz(const UDSPInstruction opc)224 void DSPEmitter::movpz(const UDSPInstruction opc)
225 {
226 u8 dreg = (opc >> 8) & 0x01;
227
228 // s64 acc = dsp_get_long_prod_round_prodl();
229 get_long_prod_round_prodl();
230 // dsp_set_long_acc(dreg, acc);
231 set_long_acc(dreg);
232 // Update_SR_Register64(acc);
233 if (FlagsNeeded())
234 {
235 Update_SR_Register64();
236 }
237 }
238
239 // ADDPAXZ $acD, $axS
240 // 1111 10sd xxxx xxxx
241 // Adds secondary accumulator $axS to product register and stores result
242 // in accumulator register. Low 16-bits of $acD ($acD.l) are set (round) to 0.
243
244 // flags out: --xx xx0x
addpaxz(const UDSPInstruction opc)245 void DSPEmitter::addpaxz(const UDSPInstruction opc)
246 {
247 u8 dreg = (opc >> 8) & 0x1;
248 u8 sreg = (opc >> 9) & 0x1;
249
250 // s64 ax = dsp_get_long_acx(sreg);
251 X64Reg tmp1 = m_gpr.GetFreeXReg();
252 get_long_acx(sreg, tmp1);
253 MOV(64, R(RDX), R(tmp1));
254 // s64 res = prod + (ax & ~0xffff);
255 AND(64, R(RDX), Imm32(~0xffff));
256 // s64 prod = dsp_get_long_prod_round_prodl();
257 get_long_prod_round_prodl();
258 ADD(64, R(RAX), R(RDX));
259
260 // s64 oldprod = dsp_get_long_prod();
261 // dsp_set_long_acc(dreg, res);
262 // res = dsp_get_long_acc(dreg);
263 // Update_SR_Register64(res, isCarry(oldprod, res), false);
264 if (FlagsNeeded())
265 {
266 get_long_prod(RDX);
267 MOV(64, R(RCX), R(RAX));
268 set_long_acc(dreg, RCX);
269 Update_SR_Register64_Carry(EAX, tmp1);
270 }
271 else
272 {
273 set_long_acc(dreg, RAX);
274 }
275 m_gpr.PutXReg(tmp1);
276 }
277
278 //----
279
280 // MULAXH
281 // 1000 0011 xxxx xxxx
282 // Multiply $ax0.h by $ax0.h
mulaxh(const UDSPInstruction opc)283 void DSPEmitter::mulaxh(const UDSPInstruction opc)
284 {
285 // s64 prod = dsp_multiply(dsp_get_ax_h(0), dsp_get_ax_h(0));
286 dsp_op_read_reg(DSP_REG_AXH0, RCX, RegisterExtension::Sign);
287 MOV(64, R(RAX), R(RCX));
288 multiply();
289 // dsp_set_long_prod(prod);
290 set_long_prod();
291 }
292
293 //----
294
295 // MUL $axS.l, $axS.h
296 // 1001 s000 xxxx xxxx
297 // Multiply low part $axS.l of secondary accumulator $axS by high part
298 // $axS.h of secondary accumulator $axS (treat them both as signed).
mul(const UDSPInstruction opc)299 void DSPEmitter::mul(const UDSPInstruction opc)
300 {
301 u8 sreg = (opc >> 11) & 0x1;
302
303 // u16 axl = dsp_get_ax_l(sreg);
304 dsp_op_read_reg(DSP_REG_AXL0 + sreg, RCX, RegisterExtension::Sign);
305 // u16 axh = dsp_get_ax_h(sreg);
306 dsp_op_read_reg(DSP_REG_AXH0 + sreg, RAX, RegisterExtension::Sign);
307 // s64 prod = dsp_multiply(axh, axl);
308 multiply();
309 // dsp_set_long_prod(prod);
310 set_long_prod();
311 }
312
313 // MULAC $axS.l, $axS.h, $acR
314 // 1001 s10r xxxx xxxx
315 // Add product register to accumulator register $acR. Multiply low part
316 // $axS.l of secondary accumulator $axS by high part $axS.h of secondary
317 // accumulator $axS (treat them both as signed).
318
319 // flags out: --xx xx0x
mulac(const UDSPInstruction opc)320 void DSPEmitter::mulac(const UDSPInstruction opc)
321 {
322 u8 rreg = (opc >> 8) & 0x1;
323 u8 sreg = (opc >> 11) & 0x1;
324
325 // s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
326 get_long_acc(rreg);
327 MOV(64, R(RDX), R(RAX));
328 get_long_prod();
329 ADD(64, R(RAX), R(RDX));
330 PUSH(64, R(RAX));
331 // u16 axl = dsp_get_ax_l(sreg);
332 dsp_op_read_reg(DSP_REG_AXL0 + sreg, RCX, RegisterExtension::Sign);
333 // u16 axh = dsp_get_ax_h(sreg);
334 dsp_op_read_reg(DSP_REG_AXH0 + sreg, RAX, RegisterExtension::Sign);
335 // s64 prod = dsp_multiply(axl, axh);
336 multiply();
337 // dsp_set_long_prod(prod);
338 set_long_prod();
339 // dsp_set_long_acc(rreg, acc);
340 POP(64, R(RAX));
341 set_long_acc(rreg);
342 // Update_SR_Register64(dsp_get_long_acc(rreg));
343 if (FlagsNeeded())
344 {
345 Update_SR_Register64();
346 }
347 }
348
349 // MULMV $axS.l, $axS.h, $acR
350 // 1001 s11r xxxx xxxx
351 // Move product register to accumulator register $acR. Multiply low part
352 // $axS.l of secondary accumulator $axS by high part $axS.h of secondary
353 // accumulator $axS (treat them both as signed).
354
355 // flags out: --xx xx0x
mulmv(const UDSPInstruction opc)356 void DSPEmitter::mulmv(const UDSPInstruction opc)
357 {
358 u8 rreg = (opc >> 8) & 0x1;
359
360 // s64 acc = dsp_get_long_prod();
361 get_long_prod();
362 PUSH(64, R(RAX));
363 mul(opc);
364 // dsp_set_long_acc(rreg, acc);
365 POP(64, R(RAX));
366 set_long_acc(rreg);
367 // Update_SR_Register64(dsp_get_long_acc(rreg));
368 if (FlagsNeeded())
369 {
370 Update_SR_Register64();
371 }
372 }
373
374 // MULMVZ $axS.l, $axS.h, $acR
375 // 1001 s01r xxxx xxxx
376 // Move product register to accumulator register $acR and clear (round) low part
377 // of accumulator register $acR.l. Multiply low part $axS.l of secondary
378 // accumulator $axS by high part $axS.h of secondary accumulator $axS (treat
379 // them both as signed).
380
381 // flags out: --xx xx0x
mulmvz(const UDSPInstruction opc)382 void DSPEmitter::mulmvz(const UDSPInstruction opc)
383 {
384 u8 rreg = (opc >> 8) & 0x1;
385
386 // s64 acc = dsp_get_long_prod_round_prodl();
387 get_long_prod_round_prodl(RDX);
388 // dsp_set_long_acc(rreg, acc);
389 set_long_acc(rreg, RDX);
390 mul(opc);
391 // Update_SR_Register64(dsp_get_long_acc(rreg));
392 if (FlagsNeeded())
393 {
394 Update_SR_Register64(RDX, RCX);
395 }
396 }
397
398 //----
399
400 // MULX $ax0.S, $ax1.T
401 // 101s t000 xxxx xxxx
402 // Multiply one part $ax0 by one part $ax1.
403 // Part is selected by S and T bits. Zero selects low part, one selects high part.
mulx(const UDSPInstruction opc)404 void DSPEmitter::mulx(const UDSPInstruction opc)
405 {
406 u8 treg = ((opc >> 11) & 0x1);
407 u8 sreg = ((opc >> 12) & 0x1);
408
409 // u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
410 dsp_op_read_reg(DSP_REG_AXL0 + sreg * 2, RCX, RegisterExtension::Sign);
411 // u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
412 dsp_op_read_reg(DSP_REG_AXL1 + treg * 2, RAX, RegisterExtension::Sign);
413 // s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
414 multiply_mulx(sreg, treg);
415 // dsp_set_long_prod(prod);
416 set_long_prod();
417 }
418
419 // MULXAC $ax0.S, $ax1.T, $acR
420 // 101s t01r xxxx xxxx
421 // Add product register to accumulator register $acR. Multiply one part
422 // $ax0 by one part $ax1. Part is selected by S and
423 // T bits. Zero selects low part, one selects high part.
424
425 // flags out: --xx xx0x
mulxac(const UDSPInstruction opc)426 void DSPEmitter::mulxac(const UDSPInstruction opc)
427 {
428 u8 rreg = (opc >> 8) & 0x1;
429 u8 treg = (opc >> 11) & 0x1;
430 u8 sreg = (opc >> 12) & 0x1;
431
432 // s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
433 X64Reg tmp1 = m_gpr.GetFreeXReg();
434 get_long_acc(rreg, tmp1);
435 get_long_prod();
436 ADD(64, R(tmp1), R(RAX));
437 // u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
438 dsp_op_read_reg(DSP_REG_AXL0 + sreg * 2, RCX, RegisterExtension::Sign);
439 // u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
440 dsp_op_read_reg(DSP_REG_AXL1 + treg * 2, RAX, RegisterExtension::Sign);
441 // s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
442 multiply_mulx(sreg, treg);
443
444 // dsp_set_long_prod(prod);
445 set_long_prod();
446 // dsp_set_long_acc(rreg, acc);
447 set_long_acc(rreg, tmp1);
448 // Update_SR_Register64(dsp_get_long_acc(rreg));
449 if (FlagsNeeded())
450 {
451 Update_SR_Register64(tmp1);
452 }
453 m_gpr.PutXReg(tmp1);
454 }
455
456 // MULXMV $ax0.S, $ax1.T, $acR
457 // 101s t11r xxxx xxxx
458 // Move product register to accumulator register $acR. Multiply one part
459 // $ax0 by one part $ax1. Part is selected by S and
460 // T bits. Zero selects low part, one selects high part.
461
462 // flags out: --xx xx0x
mulxmv(const UDSPInstruction opc)463 void DSPEmitter::mulxmv(const UDSPInstruction opc)
464 {
465 u8 rreg = ((opc >> 8) & 0x1);
466 u8 treg = (opc >> 11) & 0x1;
467 u8 sreg = (opc >> 12) & 0x1;
468
469 // s64 acc = dsp_get_long_prod();
470 X64Reg tmp1 = m_gpr.GetFreeXReg();
471 get_long_prod(tmp1);
472 // u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
473 dsp_op_read_reg(DSP_REG_AXL0 + sreg * 2, RCX, RegisterExtension::Sign);
474 // u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
475 dsp_op_read_reg(DSP_REG_AXL1 + treg * 2, RAX, RegisterExtension::Sign);
476 // s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
477 multiply_mulx(sreg, treg);
478
479 // dsp_set_long_prod(prod);
480 set_long_prod();
481 // dsp_set_long_acc(rreg, acc);
482 set_long_acc(rreg, tmp1);
483 // Update_SR_Register64(dsp_get_long_acc(rreg));
484 if (FlagsNeeded())
485 {
486 Update_SR_Register64(tmp1);
487 }
488 m_gpr.PutXReg(tmp1);
489 }
490
491 // MULXMV $ax0.S, $ax1.T, $acR
492 // 101s t01r xxxx xxxx
493 // Move product register to accumulator register $acR and clear (round) low part
494 // of accumulator register $acR.l. Multiply one part $ax0 by one part $ax1
495 // Part is selected by S and T bits. Zero selects low part,
496 // one selects high part.
497
498 // flags out: --xx xx0x
mulxmvz(const UDSPInstruction opc)499 void DSPEmitter::mulxmvz(const UDSPInstruction opc)
500 {
501 u8 rreg = (opc >> 8) & 0x1;
502 u8 treg = (opc >> 11) & 0x1;
503 u8 sreg = (opc >> 12) & 0x1;
504
505 // s64 acc = dsp_get_long_prod_round_prodl();
506 X64Reg tmp1 = m_gpr.GetFreeXReg();
507 get_long_prod_round_prodl(tmp1);
508 // u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
509 dsp_op_read_reg(DSP_REG_AXL0 + sreg * 2, RCX, RegisterExtension::Sign);
510 // u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
511 dsp_op_read_reg(DSP_REG_AXL1 + treg * 2, RAX, RegisterExtension::Sign);
512 // s64 prod = dsp_multiply_mulx(sreg, treg, val1, val2);
513 multiply_mulx(sreg, treg);
514
515 // dsp_set_long_prod(prod);
516 set_long_prod();
517 // dsp_set_long_acc(rreg, acc);
518 set_long_acc(rreg, tmp1);
519 // Update_SR_Register64(dsp_get_long_acc(rreg));
520 if (FlagsNeeded())
521 {
522 Update_SR_Register64(tmp1);
523 }
524 m_gpr.PutXReg(tmp1);
525 }
526
527 //----
528
529 // MULC $acS.m, $axT.h
530 // 110s t000 xxxx xxxx
531 // Multiply mid part of accumulator register $acS.m by high part $axS.h of
532 // secondary accumulator $axS (treat them both as signed).
mulc(const UDSPInstruction opc)533 void DSPEmitter::mulc(const UDSPInstruction opc)
534 {
535 u8 treg = (opc >> 11) & 0x1;
536 u8 sreg = (opc >> 12) & 0x1;
537
538 // u16 accm = dsp_get_acc_m(sreg);
539 get_acc_m(sreg, ECX);
540 // u16 axh = dsp_get_ax_h(treg);
541 dsp_op_read_reg(DSP_REG_AXH0 + treg, RAX, RegisterExtension::Sign);
542 // s64 prod = dsp_multiply(accm, axh);
543 multiply();
544 // dsp_set_long_prod(prod);
545 set_long_prod();
546 }
547
548 // MULCAC $acS.m, $axT.h, $acR
549 // 110s t10r xxxx xxxx
550 // Multiply mid part of accumulator register $acS.m by high part $axS.h of
551 // secondary accumulator $axS (treat them both as signed). Add product
552 // register before multiplication to accumulator $acR.
553
554 // flags out: --xx xx0x
mulcac(const UDSPInstruction opc)555 void DSPEmitter::mulcac(const UDSPInstruction opc)
556 {
557 u8 rreg = (opc >> 8) & 0x1;
558 u8 treg = (opc >> 11) & 0x1;
559 u8 sreg = (opc >> 12) & 0x1;
560
561 // s64 acc = dsp_get_long_acc(rreg) + dsp_get_long_prod();
562 get_long_acc(rreg);
563 MOV(64, R(RDX), R(RAX));
564 get_long_prod();
565 ADD(64, R(RAX), R(RDX));
566 PUSH(64, R(RAX));
567 // u16 accm = dsp_get_acc_m(sreg);
568 get_acc_m(sreg, ECX);
569 // u16 axh = dsp_get_ax_h(treg);
570 dsp_op_read_reg(DSP_REG_AXH0 + treg, RAX, RegisterExtension::Sign);
571 // s64 prod = dsp_multiply(accm, axh);
572 multiply();
573 // dsp_set_long_prod(prod);
574 set_long_prod();
575 // dsp_set_long_acc(rreg, acc);
576 POP(64, R(RAX));
577 set_long_acc(rreg);
578 // Update_SR_Register64(dsp_get_long_acc(rreg));
579 if (FlagsNeeded())
580 {
581 Update_SR_Register64();
582 }
583 }
584
585 // MULCMV $acS.m, $axT.h, $acR
586 // 110s t11r xxxx xxxx
587 // Multiply mid part of accumulator register $acS.m by high part $axT.h of
588 // secondary accumulator $axT (treat them both as signed). Move product
589 // register before multiplication to accumulator $acR.
590 // possible mistake in duddie's doc axT.h rather than axS.h
591
592 // flags out: --xx xx0x
mulcmv(const UDSPInstruction opc)593 void DSPEmitter::mulcmv(const UDSPInstruction opc)
594 {
595 u8 rreg = (opc >> 8) & 0x1;
596 u8 treg = (opc >> 11) & 0x1;
597 u8 sreg = (opc >> 12) & 0x1;
598
599 // s64 acc = dsp_get_long_prod();
600 get_long_prod();
601 PUSH(64, R(RAX));
602 // u16 accm = dsp_get_acc_m(sreg);
603 get_acc_m(sreg, ECX);
604 // u16 axh = dsp_get_ax_h(treg);
605 dsp_op_read_reg(DSP_REG_AXH0 + treg, RAX, RegisterExtension::Sign);
606 // s64 prod = dsp_multiply(accm, axh);
607 multiply();
608 // dsp_set_long_prod(prod);
609 set_long_prod();
610 // dsp_set_long_acc(rreg, acc);
611 POP(64, R(RAX));
612 set_long_acc(rreg);
613 // Update_SR_Register64(dsp_get_long_acc(rreg));
614 if (FlagsNeeded())
615 {
616 Update_SR_Register64();
617 }
618 }
619
620 // MULCMVZ $acS.m, $axT.h, $acR
621 // 110s t01r xxxx xxxx
622 // (fixed possible bug in duddie's description, s->t)
623 // Multiply mid part of accumulator register $acS.m by high part $axT.h of
624 // secondary accumulator $axT (treat them both as signed). Move product
625 // register before multiplication to accumulator $acR, set (round) low part of
626 // accumulator $acR.l to zero.
627
628 // flags out: --xx xx0x
mulcmvz(const UDSPInstruction opc)629 void DSPEmitter::mulcmvz(const UDSPInstruction opc)
630 {
631 u8 rreg = (opc >> 8) & 0x1;
632 u8 treg = (opc >> 11) & 0x1;
633 u8 sreg = (opc >> 12) & 0x1;
634
635 // s64 acc = dsp_get_long_prod_round_prodl();
636 get_long_prod_round_prodl();
637 PUSH(64, R(RAX));
638 // u16 accm = dsp_get_acc_m(sreg);
639 get_acc_m(sreg, ECX);
640 // u16 axh = dsp_get_ax_h(treg);
641 dsp_op_read_reg(DSP_REG_AXH0 + treg, RAX, RegisterExtension::Sign);
642 // s64 prod = dsp_multiply(accm, axh);
643 multiply();
644 // dsp_set_long_prod(prod);
645 set_long_prod();
646 // dsp_set_long_acc(rreg, acc);
647 POP(64, R(RAX));
648 set_long_acc(rreg);
649 // Update_SR_Register64(dsp_get_long_acc(rreg));
650 if (FlagsNeeded())
651 {
652 Update_SR_Register64();
653 }
654 }
655
656 //----
657
658 // MADDX ax0.S ax1.T
659 // 1110 00st xxxx xxxx
660 // Multiply one part of secondary accumulator $ax0 (selected by S) by
661 // one part of secondary accumulator $ax1 (selected by T) (treat them both as
662 // signed) and add result to product register.
maddx(const UDSPInstruction opc)663 void DSPEmitter::maddx(const UDSPInstruction opc)
664 {
665 u8 treg = (opc >> 8) & 0x1;
666 u8 sreg = (opc >> 9) & 0x1;
667
668 // u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
669 dsp_op_read_reg(DSP_REG_AXL0 + sreg * 2, RCX, RegisterExtension::Sign);
670 // u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
671 dsp_op_read_reg(DSP_REG_AXL1 + treg * 2, RAX, RegisterExtension::Sign);
672 // s64 prod = dsp_multiply_add(val1, val2);
673 multiply_add();
674 // dsp_set_long_prod(prod);
675 set_long_prod();
676 }
677
678 // MSUBX $(0x18+S*2), $(0x19+T*2)
679 // 1110 01st xxxx xxxx
680 // Multiply one part of secondary accumulator $ax0 (selected by S) by
681 // one part of secondary accumulator $ax1 (selected by T) (treat them both as
682 // signed) and subtract result from product register.
msubx(const UDSPInstruction opc)683 void DSPEmitter::msubx(const UDSPInstruction opc)
684 {
685 u8 treg = (opc >> 8) & 0x1;
686 u8 sreg = (opc >> 9) & 0x1;
687
688 // u16 val1 = (sreg == 0) ? dsp_get_ax_l(0) : dsp_get_ax_h(0);
689 dsp_op_read_reg(DSP_REG_AXL0 + sreg * 2, RCX, RegisterExtension::Sign);
690 // u16 val2 = (treg == 0) ? dsp_get_ax_l(1) : dsp_get_ax_h(1);
691 dsp_op_read_reg(DSP_REG_AXL1 + treg * 2, RAX, RegisterExtension::Sign);
692 // s64 prod = dsp_multiply_sub(val1, val2);
693 multiply_sub();
694 // dsp_set_long_prod(prod);
695 set_long_prod();
696 }
697
698 // MADDC $acS.m, $axT.h
699 // 1110 10st xxxx xxxx
700 // Multiply middle part of accumulator $acS.m by high part of secondary
701 // accumulator $axT.h (treat them both as signed) and add result to product
702 // register.
maddc(const UDSPInstruction opc)703 void DSPEmitter::maddc(const UDSPInstruction opc)
704 {
705 u8 treg = (opc >> 8) & 0x1;
706 u8 sreg = (opc >> 9) & 0x1;
707
708 // u16 accm = dsp_get_acc_m(sreg);
709 get_acc_m(sreg, ECX);
710 // u16 axh = dsp_get_ax_h(treg);
711 dsp_op_read_reg(DSP_REG_AXH0 + treg, RAX, RegisterExtension::Sign);
712 // s64 prod = dsp_multiply_add(accm, axh);
713 multiply_add();
714 // dsp_set_long_prod(prod);
715 set_long_prod();
716 }
717
718 // MSUBC $acS.m, $axT.h
719 // 1110 11st xxxx xxxx
720 // Multiply middle part of accumulator $acS.m by high part of secondary
721 // accumulator $axT.h (treat them both as signed) and subtract result from
722 // product register.
msubc(const UDSPInstruction opc)723 void DSPEmitter::msubc(const UDSPInstruction opc)
724 {
725 u8 treg = (opc >> 8) & 0x1;
726 u8 sreg = (opc >> 9) & 0x1;
727
728 // u16 accm = dsp_get_acc_m(sreg);
729 get_acc_m(sreg, ECX);
730 // u16 axh = dsp_get_ax_h(treg);
731 dsp_op_read_reg(DSP_REG_AXH0 + treg, RAX, RegisterExtension::Sign);
732 // s64 prod = dsp_multiply_sub(accm, axh);
733 multiply_sub();
734 // dsp_set_long_prod(prod);
735 set_long_prod();
736 }
737
738 // MADD $axS.l, $axS.h
739 // 1111 001s xxxx xxxx
740 // Multiply low part $axS.l of secondary accumulator $axS by high part
741 // $axS.h of secondary accumulator $axS (treat them both as signed) and add
742 // result to product register.
madd(const UDSPInstruction opc)743 void DSPEmitter::madd(const UDSPInstruction opc)
744 {
745 u8 sreg = (opc >> 8) & 0x1;
746
747 // u16 axl = dsp_get_ax_l(sreg);
748 dsp_op_read_reg(DSP_REG_AXL0 + sreg, RCX, RegisterExtension::Sign);
749 // u16 axh = dsp_get_ax_h(sreg);
750 dsp_op_read_reg(DSP_REG_AXH0 + sreg, RAX, RegisterExtension::Sign);
751 // s64 prod = dsp_multiply_add(axl, axh);
752 multiply_add();
753 // dsp_set_long_prod(prod);
754 set_long_prod();
755 }
756
757 // MSUB $axS.l, $axS.h
758 // 1111 011s xxxx xxxx
759 // Multiply low part $axS.l of secondary accumulator $axS by high part
760 // $axS.h of secondary accumulator $axS (treat them both as signed) and
761 // subtract result from product register.
msub(const UDSPInstruction opc)762 void DSPEmitter::msub(const UDSPInstruction opc)
763 {
764 u8 sreg = (opc >> 8) & 0x1;
765
766 // u16 axl = dsp_get_ax_l(sreg);
767 dsp_op_read_reg(DSP_REG_AXL0 + sreg, RCX, RegisterExtension::Sign);
768 // u16 axh = dsp_get_ax_h(sreg);
769 dsp_op_read_reg(DSP_REG_AXH0 + sreg, RAX, RegisterExtension::Sign);
770 // s64 prod = dsp_multiply_sub(axl, axh);
771 multiply_sub();
772 // dsp_set_long_prod(prod);
773 set_long_prod();
774 }
775
776 } // namespace DSP::JIT::x64
777