1 // Copyright 2009 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 #include "Core/DSP/DSPCore.h"
8 #include "Core/DSP/DSPMemoryMap.h"
9 #include "Core/DSP/DSPTables.h"
10 #include "Core/DSP/Interpreter/DSPIntUtil.h"
11 #include "Core/DSP/Interpreter/DSPInterpreter.h"
12 
13 namespace DSP::Interpreter
14 {
15 // MRR $D, $S
16 // 0001 11dd ddds ssss
17 // Move value from register $S to register $D.
mrr(const UDSPInstruction opc)18 void mrr(const UDSPInstruction opc)
19 {
20   u8 sreg = opc & 0x1f;
21   u8 dreg = (opc >> 5) & 0x1f;
22 
23   if (sreg >= DSP_REG_ACM0)
24     dsp_op_write_reg(dreg, dsp_op_read_reg_and_saturate(sreg - DSP_REG_ACM0));
25   else
26     dsp_op_write_reg(dreg, dsp_op_read_reg(sreg));
27 
28   dsp_conditional_extend_accum(dreg);
29 }
30 
31 // LRI $D, #I
32 // 0000 0000 100d dddd
33 // iiii iiii iiii iiii
34 // Load immediate value I to register $D.
35 //
36 // DSPSpy discovery: This, and possibly other instructions that load a
37 // register, has a different behaviour in S40 mode if loaded to AC0.M: The
38 // value gets sign extended to the whole accumulator! This does not happen in
39 // S16 mode.
lri(const UDSPInstruction opc)40 void lri(const UDSPInstruction opc)
41 {
42   u8 reg = opc & 0x1F;
43   u16 imm = dsp_fetch_code();
44   dsp_op_write_reg(reg, imm);
45   dsp_conditional_extend_accum(reg);
46 }
47 
48 // LRIS $(0x18+D), #I
49 // 0000 1ddd iiii iiii
50 // Load immediate value I (8-bit sign extended) to accumulator register.
lris(const UDSPInstruction opc)51 void lris(const UDSPInstruction opc)
52 {
53   u8 reg = ((opc >> 8) & 0x7) + DSP_REG_AXL0;
54   u16 imm = (s8)opc;
55   dsp_op_write_reg(reg, imm);
56   dsp_conditional_extend_accum(reg);
57 }
58 
59 //----
60 
61 // NX
62 // 1000 -000 xxxx xxxx
63 // No operation, but can be extended with extended opcode.
64 // This opcode is supposed to do nothing - it's used if you want to use
65 // an opcode extension but not do anything. At least according to duddie.
nx(const UDSPInstruction opc)66 void nx(const UDSPInstruction opc)
67 {
68   ZeroWriteBackLog();
69 }
70 
71 //----
72 
73 // DAR $arD
74 // 0000 0000 0000 01dd
75 // Decrement address register $arD.
dar(const UDSPInstruction opc)76 void dar(const UDSPInstruction opc)
77 {
78   g_dsp.r.ar[opc & 0x3] = dsp_decrement_addr_reg(opc & 0x3);
79 }
80 
81 // IAR $arD
82 // 0000 0000 0000 10dd
83 // Increment address register $arD.
iar(const UDSPInstruction opc)84 void iar(const UDSPInstruction opc)
85 {
86   g_dsp.r.ar[opc & 0x3] = dsp_increment_addr_reg(opc & 0x3);
87 }
88 
89 // SUBARN $arD
90 // 0000 0000 0000 11dd
91 // Subtract indexing register $ixD from an addressing register $arD.
92 // used only in IPL-NTSC ucode
subarn(const UDSPInstruction opc)93 void subarn(const UDSPInstruction opc)
94 {
95   u8 dreg = opc & 0x3;
96   g_dsp.r.ar[dreg] = dsp_decrease_addr_reg(dreg, (s16)g_dsp.r.ix[dreg]);
97 }
98 
99 // ADDARN $arD, $ixS
100 // 0000 0000 0001 ssdd
101 // Adds indexing register $ixS to an addressing register $arD.
102 // It is critical for the Zelda ucode that this one wraps correctly.
addarn(const UDSPInstruction opc)103 void addarn(const UDSPInstruction opc)
104 {
105   u8 dreg = opc & 0x3;
106   u8 sreg = (opc >> 2) & 0x3;
107   g_dsp.r.ar[dreg] = dsp_increase_addr_reg(dreg, (s16)g_dsp.r.ix[sreg]);
108 }
109 
110 //----
111 
112 // SBCLR #I
113 // 0001 0010 aaaa aiii
114 // bit of status register $sr. Bit number is calculated by adding 6 to
115 // immediate value I.
sbclr(const UDSPInstruction opc)116 void sbclr(const UDSPInstruction opc)
117 {
118   u8 bit = (opc & 0x7) + 6;
119   g_dsp.r.sr &= ~(1 << bit);
120 }
121 
122 // SBSET #I
123 // 0001 0011 aaaa aiii
124 // Set bit of status register $sr. Bit number is calculated by adding 6 to
125 // immediate value I.
sbset(const UDSPInstruction opc)126 void sbset(const UDSPInstruction opc)
127 {
128   u8 bit = (opc & 0x7) + 6;
129   g_dsp.r.sr |= (1 << bit);
130 }
131 
132 // This is a bunch of flag setters, flipping bits in SR.
srbith(const UDSPInstruction opc)133 void srbith(const UDSPInstruction opc)
134 {
135   ZeroWriteBackLog();
136   switch ((opc >> 8) & 0x7)
137   {
138   case 2:  // M2
139     g_dsp.r.sr &= ~SR_MUL_MODIFY;
140     break;
141   case 3:  // M0
142     g_dsp.r.sr |= SR_MUL_MODIFY;
143     break;
144   case 4:  // CLR15
145     g_dsp.r.sr &= ~SR_MUL_UNSIGNED;
146     break;
147   case 5:  // SET15
148     g_dsp.r.sr |= SR_MUL_UNSIGNED;
149     break;
150   case 6:  // SET16 (CLR40)
151     g_dsp.r.sr &= ~SR_40_MODE_BIT;
152     break;
153   case 7:  // SET40
154     g_dsp.r.sr |= SR_40_MODE_BIT;
155     break;
156   default:
157     break;
158   }
159 }
160 }  // namespace DSP::Interpreter
161