1 // Copyright 2009 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "Core/DSP/Interpreter/DSPIntExtOps.h"
6 
7 #include "Core/DSP/DSPMemoryMap.h"
8 #include "Core/DSP/DSPTables.h"
9 #include "Core/DSP/Interpreter/DSPIntUtil.h"
10 
11 // not needed for game ucodes (it slows down interpreter/dspjit32 + easier to compare int VS
12 // dspjit64 without it)
13 //#define PRECISE_BACKLOG
14 
15 // Extended opcodes do not exist on their own. These opcodes can only be
16 // attached to opcodes that allow extending (8 (or 7) lower bits of opcode not used by
17 // opcode). Extended opcodes do not modify program counter $pc register.
18 
19 // Most of the suffixes increment or decrement one or more addressing registers
20 // (the first four, ARx). The increment/decrement is either 1, or the
21 // corresponding "index" register (the second four, IXx). The addressing
22 // registers will wrap in odd ways, dictated by the corresponding wrapping
23 // register, WR0-3.
24 
25 namespace DSP
26 {
WriteToBackLog(int i,int idx,u16 value)27 static void WriteToBackLog(int i, int idx, u16 value)
28 {
29   writeBackLog[i] = value;
30   writeBackLogIdx[i] = idx;
31 }
32 
33 namespace Interpreter::Ext
34 {
IsSameMemArea(u16 a,u16 b)35 static bool IsSameMemArea(u16 a, u16 b)
36 {
37   // LM: tested on Wii
38   return (a >> 10) == (b >> 10);
39 }
40 
41 // DR $arR
42 // xxxx xxxx 0000 01rr
43 // Decrement addressing register $arR.
dr(const UDSPInstruction opc)44 void dr(const UDSPInstruction opc)
45 {
46   WriteToBackLog(0, opc & 0x3, dsp_decrement_addr_reg(opc & 0x3));
47 }
48 
49 // IR $arR
50 // xxxx xxxx 0000 10rr
51 // Increment addressing register $arR.
ir(const UDSPInstruction opc)52 void ir(const UDSPInstruction opc)
53 {
54   WriteToBackLog(0, opc & 0x3, dsp_increment_addr_reg(opc & 0x3));
55 }
56 
57 // NR $arR
58 // xxxx xxxx 0000 11rr
59 // Add corresponding indexing register $ixR to addressing register $arR.
nr(const UDSPInstruction opc)60 void nr(const UDSPInstruction opc)
61 {
62   u8 reg = opc & 0x3;
63 
64   WriteToBackLog(0, reg, dsp_increase_addr_reg(reg, (s16)g_dsp.r.ix[reg]));
65 }
66 
67 // MV $axD.D, $acS.S
68 // xxxx xxxx 0001 ddss
69 // Move value of $acS.S to the $axD.D.
mv(const UDSPInstruction opc)70 void mv(const UDSPInstruction opc)
71 {
72   u8 sreg = (opc & 0x3) + DSP_REG_ACL0;
73   u8 dreg = ((opc >> 2) & 0x3);
74 
75   switch (sreg)
76   {
77   case DSP_REG_ACL0:
78   case DSP_REG_ACL1:
79     WriteToBackLog(0, dreg + DSP_REG_AXL0, g_dsp.r.ac[sreg - DSP_REG_ACL0].l);
80     break;
81   case DSP_REG_ACM0:
82   case DSP_REG_ACM1:
83     WriteToBackLog(0, dreg + DSP_REG_AXL0, dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
84     break;
85   }
86 }
87 
88 // S @$arD, $acS.S
89 // xxxx xxxx 001s s0dd
90 // Store value of $acS.S in the memory pointed by register $arD.
91 // Post increment register $arD.
s(const UDSPInstruction opc)92 void s(const UDSPInstruction opc)
93 {
94   u8 dreg = opc & 0x3;
95   u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
96 
97   switch (sreg)
98   {
99   case DSP_REG_ACL0:
100   case DSP_REG_ACL1:
101     dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg - DSP_REG_ACL0].l);
102     break;
103   case DSP_REG_ACM0:
104   case DSP_REG_ACM1:
105     dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
106     break;
107   }
108   WriteToBackLog(0, dreg, dsp_increment_addr_reg(dreg));
109 }
110 
111 // SN @$arD, $acS.S
112 // xxxx xxxx 001s s1dd
113 // Store value of register $acS.S in the memory pointed by register $arD.
114 // Add indexing register $ixD to register $arD.
sn(const UDSPInstruction opc)115 void sn(const UDSPInstruction opc)
116 {
117   u8 dreg = opc & 0x3;
118   u8 sreg = ((opc >> 3) & 0x3) + DSP_REG_ACL0;
119 
120   switch (sreg)
121   {
122   case DSP_REG_ACL0:
123   case DSP_REG_ACL1:
124     dsp_dmem_write(g_dsp.r.ar[dreg], g_dsp.r.ac[sreg - DSP_REG_ACL0].l);
125     break;
126   case DSP_REG_ACM0:
127   case DSP_REG_ACM1:
128     dsp_dmem_write(g_dsp.r.ar[dreg], dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
129     break;
130   }
131   WriteToBackLog(0, dreg, dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]));
132 }
133 
134 // L $axD.D, @$arS
135 // xxxx xxxx 01dd d0ss
136 // Load $axD.D/$acD.D with value from memory pointed by register $arS.
137 // Post increment register $arS.
l(const UDSPInstruction opc)138 void l(const UDSPInstruction opc)
139 {
140   u8 sreg = opc & 0x3;
141   u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
142 
143   if ((dreg >= DSP_REG_ACM0) && (g_dsp.r.sr & SR_40_MODE_BIT))
144   {
145     u16 val = dsp_dmem_read(g_dsp.r.ar[sreg]);
146     WriteToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) ? 0xFFFF : 0x0000);
147     WriteToBackLog(1, dreg, val);
148     WriteToBackLog(2, dreg - DSP_REG_ACM0 + DSP_REG_ACL0, 0);
149     WriteToBackLog(3, sreg, dsp_increment_addr_reg(sreg));
150   }
151   else
152   {
153     WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[sreg]));
154     WriteToBackLog(1, sreg, dsp_increment_addr_reg(sreg));
155   }
156 }
157 
158 // LN $axD.D, @$arS
159 // xxxx xxxx 01dd d0ss
160 // Load $axD.D/$acD.D with value from memory pointed by register $arS.
161 // Add indexing register $ixS to register $arS.
ln(const UDSPInstruction opc)162 void ln(const UDSPInstruction opc)
163 {
164   u8 sreg = opc & 0x3;
165   u8 dreg = ((opc >> 3) & 0x7) + DSP_REG_AXL0;
166 
167   if ((dreg >= DSP_REG_ACM0) && (g_dsp.r.sr & SR_40_MODE_BIT))
168   {
169     u16 val = dsp_dmem_read(g_dsp.r.ar[sreg]);
170     WriteToBackLog(0, dreg - DSP_REG_ACM0 + DSP_REG_ACH0, (val & 0x8000) ? 0xFFFF : 0x0000);
171     WriteToBackLog(1, dreg, val);
172     WriteToBackLog(2, dreg - DSP_REG_ACM0 + DSP_REG_ACL0, 0);
173     WriteToBackLog(3, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
174   }
175   else
176   {
177     WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[sreg]));
178     WriteToBackLog(1, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
179   }
180 }
181 
182 // LS $axD.D, $acS.m108
183 // xxxx xxxx 10dd 000s
184 // Load register $axD.D with value from memory pointed by register
185 // $ar0. Store value from register $acS.m to memory location pointed by
186 // register $ar3. Increment both $ar0 and $ar3.
ls(const UDSPInstruction opc)187 void ls(const UDSPInstruction opc)
188 {
189   u8 sreg = opc & 0x1;
190   u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
191 
192   dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
193 
194   WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
195   WriteToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
196   WriteToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
197 }
198 
199 // LSN $axD.D, $acS.m
200 // xxxx xxxx 10dd 010s
201 // Load register $axD.D with value from memory pointed by register
202 // $ar0. Store value from register $acS.m to memory location pointed by
203 // register $ar3. Add corresponding indexing register $ix0 to addressing
204 // register $ar0 and increment $ar3.
lsn(const UDSPInstruction opc)205 void lsn(const UDSPInstruction opc)
206 {
207   u8 sreg = opc & 0x1;
208   u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
209 
210   dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
211 
212   WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
213   WriteToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
214   WriteToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
215 }
216 
217 // LSM $axD.D, $acS.m
218 // xxxx xxxx 10dd 100s
219 // Load register $axD.D with value from memory pointed by register
220 // $ar0. Store value from register $acS.m to memory location pointed by
221 // register $ar3. Add corresponding indexing register $ix3 to addressing
222 // register $ar3 and increment $ar0.
lsm(const UDSPInstruction opc)223 void lsm(const UDSPInstruction opc)
224 {
225   u8 sreg = opc & 0x1;
226   u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
227 
228   dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
229 
230   WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
231   WriteToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
232   WriteToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
233 }
234 
235 // LSMN $axD.D, $acS.m
236 // xxxx xxxx 10dd 110s
237 // Load register $axD.D with value from memory pointed by register
238 // $ar0. Store value from register $acS.m to memory location pointed by
239 // register $ar3. Add corresponding indexing register $ix0 to addressing
240 // register $ar0 and add corresponding indexing register $ix3 to addressing
241 // register $ar3.
lsnm(const UDSPInstruction opc)242 void lsnm(const UDSPInstruction opc)
243 {
244   u8 sreg = opc & 0x1;
245   u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
246 
247   dsp_dmem_write(g_dsp.r.ar[3], dsp_op_read_reg_and_saturate(sreg));
248 
249   WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[0]));
250   WriteToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
251   WriteToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
252 }
253 
254 // SL $acS.m, $axD.D
255 // xxxx xxxx 10dd 001s
256 // Store value from register $acS.m to memory location pointed by register
257 // $ar0. Load register $axD.D with value from memory pointed by register
258 // $ar3. Increment both $ar0 and $ar3.
sl(const UDSPInstruction opc)259 void sl(const UDSPInstruction opc)
260 {
261   u8 sreg = opc & 0x1;
262   u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
263 
264   dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
265 
266   WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
267   WriteToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
268   WriteToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
269 }
270 
271 // SLN $acS.m, $axD.D
272 // xxxx xxxx 10dd 011s
273 // Store value from register $acS.m to memory location pointed by register
274 // $ar0. Load register $axD.D with value from memory pointed by register
275 // $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
276 // and increment $ar3.
sln(const UDSPInstruction opc)277 void sln(const UDSPInstruction opc)
278 {
279   u8 sreg = opc & 0x1;
280   u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
281 
282   dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
283 
284   WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
285   WriteToBackLog(1, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
286   WriteToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
287 }
288 
289 // SLM $acS.m, $axD.D
290 // xxxx xxxx 10dd 101s
291 // Store value from register $acS.m to memory location pointed by register
292 // $ar0. Load register $axD.D with value from memory pointed by register
293 // $ar3. Add corresponding indexing register $ix3 to addressing register $ar3
294 // and increment $ar0.
slm(const UDSPInstruction opc)295 void slm(const UDSPInstruction opc)
296 {
297   u8 sreg = opc & 0x1;
298   u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
299 
300   dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
301 
302   WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
303   WriteToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
304   WriteToBackLog(2, DSP_REG_AR0, dsp_increment_addr_reg(DSP_REG_AR0));
305 }
306 
307 // SLMN $acS.m, $axD.D
308 // xxxx xxxx 10dd 111s
309 // Store value from register $acS.m to memory location pointed by register
310 // $ar0. Load register $axD.D with value from memory pointed by register
311 // $ar3. Add corresponding indexing register $ix0 to addressing register $ar0
312 // and add corresponding indexing register $ix3 to addressing register $ar3.
slnm(const UDSPInstruction opc)313 void slnm(const UDSPInstruction opc)
314 {
315   u8 sreg = opc & 0x1;
316   u8 dreg = ((opc >> 4) & 0x3) + DSP_REG_AXL0;
317 
318   dsp_dmem_write(g_dsp.r.ar[0], dsp_op_read_reg_and_saturate(sreg));
319 
320   WriteToBackLog(0, dreg, dsp_dmem_read(g_dsp.r.ar[3]));
321   WriteToBackLog(1, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
322   WriteToBackLog(2, DSP_REG_AR0, dsp_increase_addr_reg(DSP_REG_AR0, (s16)g_dsp.r.ix[0]));
323 }
324 
325 // LD $ax0.d, $ax1.r, @$arS
326 // xxxx xxxx 11dr 00ss
327 // example for "nx'ld $AX0.L, $AX1.L, @$AR3"
328 // Loads the word pointed by AR0 to AX0.H, then loads the word pointed by AR3 to AX0.L.
329 // Increments AR0 and AR3.
330 // If AR0 and AR3 point into the same memory page (upper 6 bits of addr are the same -> games are
331 // not doing that!)
332 // then the value pointed by AR0 is loaded to BOTH AX0.H and AX0.L.
333 // If AR0 points into an invalid memory page (ie 0x2000), then AX0.H keeps its old value. (not
334 // implemented yet)
335 // If AR3 points into an invalid memory page, then AX0.L gets the same value as AX0.H. (not
336 // implemented yet)
ld(const UDSPInstruction opc)337 void ld(const UDSPInstruction opc)
338 {
339   u8 dreg = (opc >> 5) & 0x1;
340   u8 rreg = (opc >> 4) & 0x1;
341   u8 sreg = opc & 0x3;
342 
343   WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
344 
345   if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
346     WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
347   else
348     WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
349 
350   WriteToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
351 
352   WriteToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
353 }
354 
355 // LDAX $axR, @$arS
356 // xxxx xxxx 11sr 0011
ldax(const UDSPInstruction opc)357 void ldax(const UDSPInstruction opc)
358 {
359   u8 sreg = (opc >> 5) & 0x1;
360   u8 rreg = (opc >> 4) & 0x1;
361 
362   WriteToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
363 
364   if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
365     WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
366   else
367     WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
368 
369   WriteToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
370 
371   WriteToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
372 }
373 
374 // LDN $ax0.d, $ax1.r, @$arS
375 // xxxx xxxx 11dr 01ss
ldn(const UDSPInstruction opc)376 void ldn(const UDSPInstruction opc)
377 {
378   u8 dreg = (opc >> 5) & 0x1;
379   u8 rreg = (opc >> 4) & 0x1;
380   u8 sreg = opc & 0x3;
381 
382   WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
383 
384   if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
385     WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
386   else
387     WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
388 
389   WriteToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
390 
391   WriteToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
392 }
393 
394 // LDAXN $axR, @$arS
395 // xxxx xxxx 11sr 0111
ldaxn(const UDSPInstruction opc)396 void ldaxn(const UDSPInstruction opc)
397 {
398   u8 sreg = (opc >> 5) & 0x1;
399   u8 rreg = (opc >> 4) & 0x1;
400 
401   WriteToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
402 
403   if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
404     WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
405   else
406     WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
407 
408   WriteToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
409 
410   WriteToBackLog(3, DSP_REG_AR3, dsp_increment_addr_reg(DSP_REG_AR3));
411 }
412 
413 // LDM $ax0.d, $ax1.r, @$arS
414 // xxxx xxxx 11dr 10ss
ldm(const UDSPInstruction opc)415 void ldm(const UDSPInstruction opc)
416 {
417   u8 dreg = (opc >> 5) & 0x1;
418   u8 rreg = (opc >> 4) & 0x1;
419   u8 sreg = opc & 0x3;
420 
421   WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
422 
423   if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
424     WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
425   else
426     WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
427 
428   WriteToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
429 
430   WriteToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
431 }
432 
433 // LDAXM $axR, @$arS
434 // xxxx xxxx 11sr 1011
ldaxm(const UDSPInstruction opc)435 void ldaxm(const UDSPInstruction opc)
436 {
437   u8 sreg = (opc >> 5) & 0x1;
438   u8 rreg = (opc >> 4) & 0x1;
439 
440   WriteToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
441 
442   if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
443     WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
444   else
445     WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
446 
447   WriteToBackLog(2, sreg, dsp_increment_addr_reg(sreg));
448 
449   WriteToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
450 }
451 
452 // LDNM $ax0.d, $ax1.r, @$arS
453 // xxxx xxxx 11dr 11ss
ldnm(const UDSPInstruction opc)454 void ldnm(const UDSPInstruction opc)
455 {
456   u8 dreg = (opc >> 5) & 0x1;
457   u8 rreg = (opc >> 4) & 0x1;
458   u8 sreg = opc & 0x3;
459 
460   WriteToBackLog(0, (dreg << 1) + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
461 
462   if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
463     WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[sreg]));
464   else
465     WriteToBackLog(1, (rreg << 1) + DSP_REG_AXL1, dsp_dmem_read(g_dsp.r.ar[3]));
466 
467   WriteToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
468 
469   WriteToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
470 }
471 
472 // LDAXNM $axR, @$arS
473 // xxxx xxxx 11dr 1111
ldaxnm(const UDSPInstruction opc)474 void ldaxnm(const UDSPInstruction opc)
475 {
476   u8 sreg = (opc >> 5) & 0x1;
477   u8 rreg = (opc >> 4) & 0x1;
478 
479   WriteToBackLog(0, rreg + DSP_REG_AXH0, dsp_dmem_read(g_dsp.r.ar[sreg]));
480 
481   if (IsSameMemArea(g_dsp.r.ar[sreg], g_dsp.r.ar[3]))
482     WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[sreg]));
483   else
484     WriteToBackLog(1, rreg + DSP_REG_AXL0, dsp_dmem_read(g_dsp.r.ar[3]));
485 
486   WriteToBackLog(2, sreg, dsp_increase_addr_reg(sreg, (s16)g_dsp.r.ix[sreg]));
487 
488   WriteToBackLog(3, DSP_REG_AR3, dsp_increase_addr_reg(DSP_REG_AR3, (s16)g_dsp.r.ix[3]));
489 }
490 
nop(const UDSPInstruction opc)491 void nop(const UDSPInstruction opc)
492 {
493 }
494 }  // namespace Interpreter::Ext
495 
496 // The ext ops are calculated in parallel with the actual op. That means that
497 // both the main op and the ext op see the same register state as input. The
498 // output is simple as long as the main and ext ops don't change the same
499 // register. If they do the output is the bitwise or of the result of both the
500 // main and ext ops.
501 
502 // The ext op are writing their output into the backlog which is
503 // being applied to the real registers after the main op was executed
ApplyWriteBackLog()504 void ApplyWriteBackLog()
505 {
506   // always make sure to have an extra entry at the end w/ -1 to avoid
507   // infinitive loops
508   for (int i = 0; writeBackLogIdx[i] != -1; i++)
509   {
510     u16 value = writeBackLog[i];
511 #ifdef PRECISE_BACKLOG
512     value |= Interpreter::dsp_op_read_reg(writeBackLogIdx[i]);
513 #endif
514     Interpreter::dsp_op_write_reg(writeBackLogIdx[i], value);
515 
516     // Clear back log
517     writeBackLogIdx[i] = -1;
518   }
519 }
520 
521 // This function is being called in the main op after all input regs were read
522 // and before it writes into any regs. This way we can always use bitwise or to
523 // apply the ext command output, because if the main op didn't change the value
524 // then 0 | ext output = ext output and if it did then bitwise or is still the
525 // right thing to do
526 // Only needed for cases when mainop and extended are modifying the same ACC
527 // Games are not doing that + in motorola (similar DSP) dox this is forbidden to do.
ZeroWriteBackLog()528 void ZeroWriteBackLog()
529 {
530 #ifdef PRECISE_BACKLOG
531   // always make sure to have an extra entry at the end w/ -1 to avoid
532   // infinitive loops
533   for (int i = 0; writeBackLogIdx[i] != -1; i++)
534   {
535     Interpreter::dsp_op_write_reg(writeBackLogIdx[i], 0);
536   }
537 #endif
538 }
539 
ZeroWriteBackLogPreserveAcc(u8 acc)540 void ZeroWriteBackLogPreserveAcc(u8 acc)
541 {
542 #ifdef PRECISE_BACKLOG
543   for (int i = 0; writeBackLogIdx[i] != -1; i++)
544   {
545     // acc0
546     if ((acc == 0) &&
547         ((writeBackLogIdx[i] == DSP_REG_ACL0) || (writeBackLogIdx[i] == DSP_REG_ACM0) ||
548          (writeBackLogIdx[i] == DSP_REG_ACH0)))
549       continue;
550 
551     // acc1
552     if ((acc == 1) &&
553         ((writeBackLogIdx[i] == DSP_REG_ACL1) || (writeBackLogIdx[i] == DSP_REG_ACM1) ||
554          (writeBackLogIdx[i] == DSP_REG_ACH1)))
555       continue;
556 
557     Interpreter::dsp_op_write_reg(writeBackLogIdx[i], 0);
558   }
559 #endif
560 }
561 }  // namespace DSP
562