MacroInterpreter.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. using Ryujinx.HLE.Gpu.Exceptions;
  2. using Ryujinx.HLE.Gpu.Memory;
  3. using System;
  4. using System.Collections.Generic;
  5. namespace Ryujinx.HLE.Gpu.Engines
  6. {
  7. class MacroInterpreter
  8. {
  9. private const int MaxCallCountPerRun = 500;
  10. private int CallCount;
  11. private enum AssignmentOperation
  12. {
  13. IgnoreAndFetch = 0,
  14. Move = 1,
  15. MoveAndSetMaddr = 2,
  16. FetchAndSend = 3,
  17. MoveAndSend = 4,
  18. FetchAndSetMaddr = 5,
  19. MoveAndSetMaddrThenFetchAndSend = 6,
  20. MoveAndSetMaddrThenSendHigh = 7
  21. }
  22. private enum AluOperation
  23. {
  24. AluReg = 0,
  25. AddImmediate = 1,
  26. BitfieldReplace = 2,
  27. BitfieldExtractLslImm = 3,
  28. BitfieldExtractLslReg = 4,
  29. ReadImmediate = 5
  30. }
  31. private enum AluRegOperation
  32. {
  33. Add = 0,
  34. AddWithCarry = 1,
  35. Subtract = 2,
  36. SubtractWithBorrow = 3,
  37. BitwiseExclusiveOr = 8,
  38. BitwiseOr = 9,
  39. BitwiseAnd = 10,
  40. BitwiseAndNot = 11,
  41. BitwiseNotAnd = 12
  42. }
  43. private NvGpuFifo PFifo;
  44. private INvGpuEngine Engine;
  45. public Queue<int> Fifo { get; private set; }
  46. private int[] Gprs;
  47. private int MethAddr;
  48. private int MethIncr;
  49. private bool Carry;
  50. private int OpCode;
  51. private int PipeOp;
  52. private int Pc;
  53. public MacroInterpreter(NvGpuFifo PFifo, INvGpuEngine Engine)
  54. {
  55. this.PFifo = PFifo;
  56. this.Engine = Engine;
  57. Fifo = new Queue<int>();
  58. Gprs = new int[8];
  59. }
  60. public void Execute(NvGpuVmm Vmm, int[] Mme, int Position, int Param)
  61. {
  62. Reset();
  63. Gprs[1] = Param;
  64. Pc = Position;
  65. FetchOpCode(Mme);
  66. while (Step(Vmm, Mme));
  67. //Due to the delay slot, we still need to execute
  68. //one more instruction before we actually exit.
  69. Step(Vmm, Mme);
  70. }
  71. private void Reset()
  72. {
  73. for (int Index = 0; Index < Gprs.Length; Index++)
  74. {
  75. Gprs[Index] = 0;
  76. }
  77. MethAddr = 0;
  78. MethIncr = 0;
  79. Carry = false;
  80. CallCount = 0;
  81. }
  82. private bool Step(NvGpuVmm Vmm, int[] Mme)
  83. {
  84. int BaseAddr = Pc - 1;
  85. FetchOpCode(Mme);
  86. if ((OpCode & 7) < 7)
  87. {
  88. //Operation produces a value.
  89. AssignmentOperation AsgOp = (AssignmentOperation)((OpCode >> 4) & 7);
  90. int Result = GetAluResult();
  91. switch (AsgOp)
  92. {
  93. //Fetch parameter and ignore result.
  94. case AssignmentOperation.IgnoreAndFetch:
  95. {
  96. SetDstGpr(FetchParam());
  97. break;
  98. }
  99. //Move result.
  100. case AssignmentOperation.Move:
  101. {
  102. SetDstGpr(Result);
  103. break;
  104. }
  105. //Move result and use as Method Address.
  106. case AssignmentOperation.MoveAndSetMaddr:
  107. {
  108. SetDstGpr(Result);
  109. SetMethAddr(Result);
  110. break;
  111. }
  112. //Fetch parameter and send result.
  113. case AssignmentOperation.FetchAndSend:
  114. {
  115. SetDstGpr(FetchParam());
  116. Send(Vmm, Result);
  117. break;
  118. }
  119. //Move and send result.
  120. case AssignmentOperation.MoveAndSend:
  121. {
  122. SetDstGpr(Result);
  123. Send(Vmm, Result);
  124. break;
  125. }
  126. //Fetch parameter and use result as Method Address.
  127. case AssignmentOperation.FetchAndSetMaddr:
  128. {
  129. SetDstGpr(FetchParam());
  130. SetMethAddr(Result);
  131. break;
  132. }
  133. //Move result and use as Method Address, then fetch and send paramter.
  134. case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend:
  135. {
  136. SetDstGpr(Result);
  137. SetMethAddr(Result);
  138. Send(Vmm, FetchParam());
  139. break;
  140. }
  141. //Move result and use as Method Address, then send bits 17:12 of result.
  142. case AssignmentOperation.MoveAndSetMaddrThenSendHigh:
  143. {
  144. SetDstGpr(Result);
  145. SetMethAddr(Result);
  146. Send(Vmm, (Result >> 12) & 0x3f);
  147. break;
  148. }
  149. }
  150. }
  151. else
  152. {
  153. //Branch.
  154. bool OnNotZero = ((OpCode >> 4) & 1) != 0;
  155. bool Taken = OnNotZero
  156. ? GetGprA() != 0
  157. : GetGprA() == 0;
  158. if (Taken)
  159. {
  160. Pc = BaseAddr + GetImm();
  161. bool NoDelays = (OpCode & 0x20) != 0;
  162. if (NoDelays)
  163. {
  164. FetchOpCode(Mme);
  165. }
  166. return true;
  167. }
  168. }
  169. bool Exit = (OpCode & 0x80) != 0;
  170. return !Exit;
  171. }
  172. private void FetchOpCode(int[] Mme)
  173. {
  174. OpCode = PipeOp;
  175. PipeOp = Mme[Pc++];
  176. }
  177. private int GetAluResult()
  178. {
  179. AluOperation Op = (AluOperation)(OpCode & 7);
  180. switch (Op)
  181. {
  182. case AluOperation.AluReg:
  183. {
  184. AluRegOperation AluOp = (AluRegOperation)((OpCode >> 17) & 0x1f);
  185. return GetAluResult(AluOp, GetGprA(), GetGprB());
  186. }
  187. case AluOperation.AddImmediate:
  188. {
  189. return GetGprA() + GetImm();
  190. }
  191. case AluOperation.BitfieldReplace:
  192. case AluOperation.BitfieldExtractLslImm:
  193. case AluOperation.BitfieldExtractLslReg:
  194. {
  195. int BfSrcBit = (OpCode >> 17) & 0x1f;
  196. int BfSize = (OpCode >> 22) & 0x1f;
  197. int BfDstBit = (OpCode >> 27) & 0x1f;
  198. int BfMask = (1 << BfSize) - 1;
  199. int Dst = GetGprA();
  200. int Src = GetGprB();
  201. switch (Op)
  202. {
  203. case AluOperation.BitfieldReplace:
  204. {
  205. Src = (int)((uint)Src >> BfSrcBit) & BfMask;
  206. Dst &= ~(BfMask << BfDstBit);
  207. Dst |= Src << BfDstBit;
  208. return Dst;
  209. }
  210. case AluOperation.BitfieldExtractLslImm:
  211. {
  212. Src = (int)((uint)Src >> Dst) & BfMask;
  213. return Src << BfDstBit;
  214. }
  215. case AluOperation.BitfieldExtractLslReg:
  216. {
  217. Src = (int)((uint)Src >> BfSrcBit) & BfMask;
  218. return Src << Dst;
  219. }
  220. }
  221. break;
  222. }
  223. case AluOperation.ReadImmediate:
  224. {
  225. return Read(GetGprA() + GetImm());
  226. }
  227. }
  228. throw new ArgumentException(nameof(OpCode));
  229. }
  230. private int GetAluResult(AluRegOperation AluOp, int A, int B)
  231. {
  232. switch (AluOp)
  233. {
  234. case AluRegOperation.Add:
  235. {
  236. ulong Result = (ulong)A + (ulong)B;
  237. Carry = Result > 0xffffffff;
  238. return (int)Result;
  239. }
  240. case AluRegOperation.AddWithCarry:
  241. {
  242. ulong Result = (ulong)A + (ulong)B + (Carry ? 1UL : 0UL);
  243. Carry = Result > 0xffffffff;
  244. return (int)Result;
  245. }
  246. case AluRegOperation.Subtract:
  247. {
  248. ulong Result = (ulong)A - (ulong)B;
  249. Carry = Result < 0x100000000;
  250. return (int)Result;
  251. }
  252. case AluRegOperation.SubtractWithBorrow:
  253. {
  254. ulong Result = (ulong)A - (ulong)B - (Carry ? 0UL : 1UL);
  255. Carry = Result < 0x100000000;
  256. return (int)Result;
  257. }
  258. case AluRegOperation.BitwiseExclusiveOr: return A ^ B;
  259. case AluRegOperation.BitwiseOr: return A | B;
  260. case AluRegOperation.BitwiseAnd: return A & B;
  261. case AluRegOperation.BitwiseAndNot: return A & ~B;
  262. case AluRegOperation.BitwiseNotAnd: return ~(A & B);
  263. }
  264. throw new ArgumentOutOfRangeException(nameof(AluOp));
  265. }
  266. private int GetImm()
  267. {
  268. //Note: The immediate is signed, the sign-extension is intended here.
  269. return OpCode >> 14;
  270. }
  271. private void SetMethAddr(int Value)
  272. {
  273. MethAddr = (Value >> 0) & 0xfff;
  274. MethIncr = (Value >> 12) & 0x3f;
  275. }
  276. private void SetDstGpr(int Value)
  277. {
  278. Gprs[(OpCode >> 8) & 7] = Value;
  279. }
  280. private int GetGprA()
  281. {
  282. return GetGprValue((OpCode >> 11) & 7);
  283. }
  284. private int GetGprB()
  285. {
  286. return GetGprValue((OpCode >> 14) & 7);
  287. }
  288. private int GetGprValue(int Index)
  289. {
  290. return Index != 0 ? Gprs[Index] : 0;
  291. }
  292. private int FetchParam()
  293. {
  294. int Value;
  295. //If we don't have any parameters in the FIFO,
  296. //keep running the PFIFO engine until it writes the parameters.
  297. while (!Fifo.TryDequeue(out Value))
  298. {
  299. if (!PFifo.Step())
  300. {
  301. return 0;
  302. }
  303. }
  304. return Value;
  305. }
  306. private int Read(int Reg)
  307. {
  308. return Engine.Registers[Reg];
  309. }
  310. private void Send(NvGpuVmm Vmm, int Value)
  311. {
  312. //This is an artificial limit that prevents excessive calls
  313. //to VertexEndGl since that triggers rendering, and in the
  314. //case that something is bugged and causes an absurd amount of
  315. //draw calls, this prevents the system from freezing (and throws instead).
  316. if (MethAddr == 0x585 && ++CallCount > MaxCallCountPerRun)
  317. {
  318. GpuExceptionHelper.ThrowCallCoundExceeded();
  319. }
  320. NvGpuPBEntry PBEntry = new NvGpuPBEntry(MethAddr, 0, Value);
  321. Engine.CallMethod(Vmm, PBEntry);
  322. MethAddr += MethIncr;
  323. }
  324. }
  325. }