@@ -166,3 +166,87 @@ void armEmitMULT1(u32 rd, u32 rs, u32 rt) { emitMult(true, rd, rs, rt, EE_LO1_OF
166166void armEmitMULTU1 (u32 rd, u32 rs, u32 rt) { emitMult (false , rd, rs, rt, EE_LO1_OFFSET , EE_HI1_OFFSET ); }
167167void armEmitDIV1 (u32 rs, u32 rt) { emitDivS (rs, rt, EE_LO1_OFFSET , EE_HI1_OFFSET ); }
168168void armEmitDIVU1 (u32 rs, u32 rt) { emitDivU (rs, rt, EE_LO1_OFFSET , EE_HI1_OFFSET ); }
169+
170+ // ------------------------------------------------------------------------
171+ // Multiply-accumulate (MMI funct 0x00/0x01 MADD/MADDU, 0x20/0x21 MADD1/MADDU1).
172+ // acc = (u64)LO.UL[0] | ((u64)HI.UL[0] << 32) (low 32 of each accumulator word)
173+ // temp = acc + (rs * rt) (signed for MADD/MADD1, unsigned for the U forms)
174+ // LO = (s32)(temp & 0xffffffff) (sign-extended to 64)
175+ // HI = (s32)(temp >> 32) (sign-extended to 64)
176+ // if rd != 0: GPR[rd].UD[0] = LO (R5900 3-operand form)
177+ // The two 32-bit accumulator words are added straight onto the 64-bit product
178+ // (HI word << 32, then LO word), so `acc` never needs its own register and the op
179+ // fits in the two manual scratch registers. Result sign-extension is identical for
180+ // the unsigned forms (the interpreter sign-extends LO/HI regardless). The pipeline-1
181+ // forms select the upper doubleword via lo_off/hi_off, exactly like MULT1.
182+ // ------------------------------------------------------------------------
183+ static void emitMadd (bool sign, u32 rd, u32 rs, u32 rt, u32 lo_off, u32 hi_off)
184+ {
185+ armAsm->Ldr (RSCRATCH2W , a64::MemOperand (RESTATEPTR , EE_GPR_OFFSET (rs)));
186+ armAsm->Ldr (RSCRATCHW , a64::MemOperand (RESTATEPTR , EE_GPR_OFFSET (rt)));
187+
188+ // The live 64-bit product is kept in RSCRATCH (x17), which AsmHelpers removes
189+ // from VIXL's scratch-register list, so it is safe to hold across the macro ops
190+ // below. RSCRATCH2 (x16 == RXVIXLSCRATCH) is VIXL's macro scratch, so it is only
191+ // ever loaded and immediately consumed as a plain operand here, never held across
192+ // a macro.
193+ if (sign)
194+ armAsm->Smull (RSCRATCH , RSCRATCH2W , RSCRATCHW ); // x17 = (s64)rs * (s32)rt
195+ else
196+ armAsm->Umull (RSCRATCH , RSCRATCH2W , RSCRATCHW ); // x17 = (u64)rs * (u32)rt
197+
198+ // temp = product + (HI.UL[0] << 32) + LO.UL[0]. The w-loads zero-extend, so each
199+ // accumulator word contributes exactly its 32 bits with no stray high bits.
200+ armAsm->Ldr (RSCRATCH2W , a64::MemOperand (RESTATEPTR , hi_off)); // HI accumulator word
201+ armAsm->Add (RSCRATCH , RSCRATCH , a64::Operand (RSCRATCH2 , a64::LSL , 32 ));
202+ armAsm->Ldr (RSCRATCH2W , a64::MemOperand (RESTATEPTR , lo_off)); // LO accumulator word
203+ armAsm->Add (RSCRATCH , RSCRATCH , RSCRATCH2 ); // RSCRATCH = temp
204+
205+ // LO = sign-extended low 32 bits of temp; also Rd in the R5900 3-operand form.
206+ armAsm->Sxtw (RSCRATCH2 , RSCRATCHW );
207+ armAsm->Str (RSCRATCH2 , a64::MemOperand (RESTATEPTR , lo_off));
208+ if (rd != 0 )
209+ armAsm->Str (RSCRATCH2 , a64::MemOperand (RESTATEPTR , EE_GPR_OFFSET (rd)));
210+
211+ // HI = sign-extended high 32 bits (asr #32 sign-extends from bit 63 == bit 31 hi).
212+ armAsm->Asr (RSCRATCH , RSCRATCH , 32 );
213+ armAsm->Str (RSCRATCH , a64::MemOperand (RESTATEPTR , hi_off));
214+ }
215+
216+ void armEmitMADD (u32 rd, u32 rs, u32 rt) { emitMadd (true , rd, rs, rt, EE_LO_OFFSET , EE_HI_OFFSET ); }
217+ void armEmitMADDU (u32 rd, u32 rs, u32 rt) { emitMadd (false , rd, rs, rt, EE_LO_OFFSET , EE_HI_OFFSET ); }
218+ void armEmitMADD1 (u32 rd, u32 rs, u32 rt) { emitMadd (true , rd, rs, rt, EE_LO1_OFFSET , EE_HI1_OFFSET ); }
219+ void armEmitMADDU1 (u32 rd, u32 rs, u32 rt) { emitMadd (false , rd, rs, rt, EE_LO1_OFFSET , EE_HI1_OFFSET ); }
220+
221+ // ------------------------------------------------------------------------
222+ // Pipeline-1 HI/LO moves (MMI funct 0x10-0x13: MFHI1/MTHI1/MFLO1/MTLO1).
223+ // Full 64-bit copies to/from the upper doubleword HI1/LO1 — mirror MFHI/MFLO/
224+ // MTHI/MTLO but with the +8 offsets.
225+ // ------------------------------------------------------------------------
226+ void armEmitMFHI1 (u32 rd)
227+ {
228+ if (rd == 0 )
229+ return ;
230+ armAsm->Ldr (RSCRATCH , a64::MemOperand (RESTATEPTR , EE_HI1_OFFSET ));
231+ armAsm->Str (RSCRATCH , a64::MemOperand (RESTATEPTR , EE_GPR_OFFSET (rd)));
232+ }
233+
234+ void armEmitMFLO1 (u32 rd)
235+ {
236+ if (rd == 0 )
237+ return ;
238+ armAsm->Ldr (RSCRATCH , a64::MemOperand (RESTATEPTR , EE_LO1_OFFSET ));
239+ armAsm->Str (RSCRATCH , a64::MemOperand (RESTATEPTR , EE_GPR_OFFSET (rd)));
240+ }
241+
242+ void armEmitMTHI1 (u32 rs)
243+ {
244+ armAsm->Ldr (RSCRATCH , a64::MemOperand (RESTATEPTR , EE_GPR_OFFSET (rs)));
245+ armAsm->Str (RSCRATCH , a64::MemOperand (RESTATEPTR , EE_HI1_OFFSET ));
246+ }
247+
248+ void armEmitMTLO1 (u32 rs)
249+ {
250+ armAsm->Ldr (RSCRATCH , a64::MemOperand (RESTATEPTR , EE_GPR_OFFSET (rs)));
251+ armAsm->Str (RSCRATCH , a64::MemOperand (RESTATEPTR , EE_LO1_OFFSET ));
252+ }
0 commit comments