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