DeviceState.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Runtime.CompilerServices;
  8. namespace Ryujinx.Graphics.Device
  9. {
  10. public class DeviceState<TState> : IDeviceState where TState : unmanaged
  11. {
  12. private const int RegisterSize = sizeof(int);
  13. public TState State;
  14. private readonly BitArray _readableRegisters;
  15. private readonly BitArray _writableRegisters;
  16. private readonly Dictionary<int, Func<int>> _readCallbacks;
  17. private readonly Dictionary<int, Action<int>> _writeCallbacks;
  18. private readonly Dictionary<int, string> _fieldNamesForDebug;
  19. private readonly Action<string> _debugLogCallback;
  20. public DeviceState(IReadOnlyDictionary<string, RwCallback> callbacks = null, Action<string> debugLogCallback = null)
  21. {
  22. int size = (Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize;
  23. _readableRegisters = new BitArray(size);
  24. _writableRegisters = new BitArray(size);
  25. _readCallbacks = new Dictionary<int, Func<int>>();
  26. _writeCallbacks = new Dictionary<int, Action<int>>();
  27. if (debugLogCallback != null)
  28. {
  29. _fieldNamesForDebug = new Dictionary<int, string>();
  30. _debugLogCallback = debugLogCallback;
  31. }
  32. var fields = typeof(TState).GetFields();
  33. int offset = 0;
  34. for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
  35. {
  36. var field = fields[fieldIndex];
  37. var regAttr = field.GetCustomAttributes<RegisterAttribute>(false).FirstOrDefault();
  38. int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
  39. for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
  40. {
  41. _readableRegisters[(offset + i) / RegisterSize] = regAttr?.AccessControl.HasFlag(AccessControl.ReadOnly) ?? true;
  42. _writableRegisters[(offset + i) / RegisterSize] = regAttr?.AccessControl.HasFlag(AccessControl.WriteOnly) ?? true;
  43. }
  44. if (callbacks != null && callbacks.TryGetValue(field.Name, out var cb))
  45. {
  46. if (cb.Read != null)
  47. {
  48. _readCallbacks.Add(offset, cb.Read);
  49. }
  50. if (cb.Write != null)
  51. {
  52. _writeCallbacks.Add(offset, cb.Write);
  53. }
  54. }
  55. if (debugLogCallback != null)
  56. {
  57. _fieldNamesForDebug.Add(offset, field.Name);
  58. }
  59. offset += sizeOfField;
  60. }
  61. Debug.Assert(offset == Unsafe.SizeOf<TState>());
  62. }
  63. public virtual int Read(int offset)
  64. {
  65. if (Check(offset) && _readableRegisters[offset / RegisterSize])
  66. {
  67. int alignedOffset = Align(offset);
  68. if (_readCallbacks.TryGetValue(alignedOffset, out Func<int> read))
  69. {
  70. return read();
  71. }
  72. else
  73. {
  74. return GetRef<int>(alignedOffset);
  75. }
  76. }
  77. return 0;
  78. }
  79. public virtual void Write(int offset, int data)
  80. {
  81. if (Check(offset) && _writableRegisters[offset / RegisterSize])
  82. {
  83. int alignedOffset = Align(offset);
  84. if (_fieldNamesForDebug != null && _fieldNamesForDebug.TryGetValue(alignedOffset, out string fieldName))
  85. {
  86. _debugLogCallback($"{typeof(TState).Name}.{fieldName} = 0x{data:X}");
  87. }
  88. GetRef<int>(alignedOffset) = data;
  89. if (_writeCallbacks.TryGetValue(alignedOffset, out Action<int> write))
  90. {
  91. write(data);
  92. }
  93. }
  94. }
  95. private bool Check(int offset)
  96. {
  97. return (uint)Align(offset) < Unsafe.SizeOf<TState>();
  98. }
  99. public ref T GetRef<T>(int offset) where T : unmanaged
  100. {
  101. if ((uint)(offset + Unsafe.SizeOf<T>()) > Unsafe.SizeOf<TState>())
  102. {
  103. throw new ArgumentOutOfRangeException(nameof(offset));
  104. }
  105. return ref Unsafe.As<TState, T>(ref Unsafe.AddByteOffset(ref State, (IntPtr)offset));
  106. }
  107. private static int Align(int offset)
  108. {
  109. return offset & ~(RegisterSize - 1);
  110. }
  111. }
  112. }