summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/hle/service/gsp.cpp36
-rw-r--r--src/core/hw/gpu.cpp47
-rw-r--r--src/core/hw/gpu.h297
3 files changed, 230 insertions, 150 deletions
diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp
index e241b31c8..08e65612e 100644
--- a/src/core/hle/service/gsp.cpp
+++ b/src/core/hle/service/gsp.cpp
@@ -173,11 +173,11 @@ void ExecuteCommand(const Command& command) {
173 case CommandId::SET_COMMAND_LIST_LAST: 173 case CommandId::SET_COMMAND_LIST_LAST:
174 { 174 {
175 auto& params = command.set_command_list_last; 175 auto& params = command.set_command_list_last;
176 WriteGPURegister(GPU::Regs::CommandProcessor + 2, params.address >> 3); 176 WriteGPURegister(GPU_REG_INDEX(command_processor_config.address), params.address >> 3);
177 WriteGPURegister(GPU::Regs::CommandProcessor, params.size >> 3); 177 WriteGPURegister(GPU_REG_INDEX(command_processor_config.size), params.size >> 3);
178 178
179 // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though 179 // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though
180 WriteGPURegister(GPU::Regs::CommandProcessor + 4, 1); 180 WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1);
181 181
182 // TODO: Move this to GPU 182 // TODO: Move this to GPU
183 // TODO: Not sure what units the size is measured in 183 // TODO: Not sure what units the size is measured in
@@ -193,15 +193,15 @@ void ExecuteCommand(const Command& command) {
193 case CommandId::SET_MEMORY_FILL: 193 case CommandId::SET_MEMORY_FILL:
194 { 194 {
195 auto& params = command.memory_fill; 195 auto& params = command.memory_fill;
196 WriteGPURegister(GPU::Regs::MemoryFill, params.start1 >> 3); 196 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[0].address_start), params.start1 >> 3);
197 WriteGPURegister(GPU::Regs::MemoryFill + 1, params.end1 >> 3); 197 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[0].address_end), params.end1 >> 3);
198 WriteGPURegister(GPU::Regs::MemoryFill + 2, params.end1 - params.start1); 198 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[0].size), params.end1 - params.start1);
199 WriteGPURegister(GPU::Regs::MemoryFill + 3, params.value1); 199 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[0].value), params.value1);
200 200
201 WriteGPURegister(GPU::Regs::MemoryFill + 4, params.start2 >> 3); 201 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_start), params.start2 >> 3);
202 WriteGPURegister(GPU::Regs::MemoryFill + 5, params.end2 >> 3); 202 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), params.end2 >> 3);
203 WriteGPURegister(GPU::Regs::MemoryFill + 6, params.end2 - params.start2); 203 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2);
204 WriteGPURegister(GPU::Regs::MemoryFill + 7, params.value2); 204 WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2);
205 break; 205 break;
206 } 206 }
207 207
@@ -220,15 +220,15 @@ void ExecuteCommand(const Command& command) {
220 case CommandId::SET_TEXTURE_COPY: 220 case CommandId::SET_TEXTURE_COPY:
221 { 221 {
222 auto& params = command.image_copy; 222 auto& params = command.image_copy;
223 WriteGPURegister(GPU::Regs::DisplayTransfer, params.in_buffer_address >> 3); 223 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.input_address), params.in_buffer_address >> 3);
224 WriteGPURegister(GPU::Regs::DisplayTransfer + 1, params.out_buffer_address >> 3); 224 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.output_address), params.out_buffer_address >> 3);
225 WriteGPURegister(GPU::Regs::DisplayTransfer + 3, params.in_buffer_size); 225 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.input_size), params.in_buffer_size);
226 WriteGPURegister(GPU::Regs::DisplayTransfer + 2, params.out_buffer_size); 226 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.output_size), params.out_buffer_size);
227 WriteGPURegister(GPU::Regs::DisplayTransfer + 4, params.flags); 227 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags);
228 228
229 // TODO: Should this only be ORed with 1 for texture copies? 229 // TODO: Should this only be ORed with 1 for texture copies?
230 // trigger transfer 230 // trigger transfer
231 WriteGPURegister(GPU::Regs::DisplayTransfer + 6, 1); 231 WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1);
232 break; 232 break;
233 } 233 }
234 234
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index d94c2329b..fd40f8ac0 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -19,7 +19,7 @@
19 19
20namespace GPU { 20namespace GPU {
21 21
22RegisterSet<u32, Regs> g_regs; 22Regs g_regs;
23 23
24u32 g_cur_line = 0; ///< Current vertical screen line 24u32 g_cur_line = 0; ///< Current vertical screen line
25u64 g_last_line_ticks = 0; ///< CPU tick count from last vertical screen line 25u64 g_last_line_ticks = 0; ///< CPU tick count from last vertical screen line
@@ -32,8 +32,8 @@ void SetFramebufferLocation(const FramebufferLocation mode) {
32 switch (mode) { 32 switch (mode) {
33 case FRAMEBUFFER_LOCATION_FCRAM: 33 case FRAMEBUFFER_LOCATION_FCRAM:
34 { 34 {
35 auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>(); 35 auto& framebuffer_top = g_regs.framebuffer_config[0];
36 auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>(); 36 auto& framebuffer_sub = g_regs.framebuffer_config[1];
37 37
38 framebuffer_top.address_left1 = PADDR_TOP_LEFT_FRAME1; 38 framebuffer_top.address_left1 = PADDR_TOP_LEFT_FRAME1;
39 framebuffer_top.address_left2 = PADDR_TOP_LEFT_FRAME2; 39 framebuffer_top.address_left2 = PADDR_TOP_LEFT_FRAME2;
@@ -48,8 +48,8 @@ void SetFramebufferLocation(const FramebufferLocation mode) {
48 48
49 case FRAMEBUFFER_LOCATION_VRAM: 49 case FRAMEBUFFER_LOCATION_VRAM:
50 { 50 {
51 auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>(); 51 auto& framebuffer_top = g_regs.framebuffer_config[0];
52 auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>(); 52 auto& framebuffer_sub = g_regs.framebuffer_config[1];
53 53
54 framebuffer_top.address_left1 = PADDR_VRAM_TOP_LEFT_FRAME1; 54 framebuffer_top.address_left1 = PADDR_VRAM_TOP_LEFT_FRAME1;
55 framebuffer_top.address_left2 = PADDR_VRAM_TOP_LEFT_FRAME2; 55 framebuffer_top.address_left2 = PADDR_VRAM_TOP_LEFT_FRAME2;
@@ -107,13 +107,12 @@ inline void Read(T &var, const u32 raw_addr) {
107 int index = addr / 4; 107 int index = addr / 4;
108 108
109 // Reads other than u32 are untested, so I'd rather have them abort than silently fail 109 // Reads other than u32 are untested, so I'd rather have them abort than silently fail
110 if (index >= Regs::NumIds || !std::is_same<T,u32>::value) 110 if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) {
111 {
112 ERROR_LOG(GPU, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr); 111 ERROR_LOG(GPU, "unknown Read%d @ 0x%08X", sizeof(var) * 8, addr);
113 return; 112 return;
114 } 113 }
115 114
116 var = g_regs[static_cast<Regs::Id>(addr / 4)]; 115 var = g_regs[addr / 4];
117} 116}
118 117
119template <typename T> 118template <typename T>
@@ -122,22 +121,22 @@ inline void Write(u32 addr, const T data) {
122 int index = addr / 4; 121 int index = addr / 4;
123 122
124 // Writes other than u32 are untested, so I'd rather have them abort than silently fail 123 // Writes other than u32 are untested, so I'd rather have them abort than silently fail
125 if (index >= Regs::NumIds || !std::is_same<T,u32>::value) 124 if (index >= Regs::NumIds() || !std::is_same<T,u32>::value) {
126 {
127 ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr); 125 ERROR_LOG(GPU, "unknown Write%d 0x%08X @ 0x%08X", sizeof(data) * 8, data, addr);
128 return; 126 return;
129 } 127 }
130 128
131 g_regs[static_cast<Regs::Id>(index)] = data; 129 g_regs[index] = data;
132 130
133 switch (static_cast<Regs::Id>(index)) { 131 switch (index) {
134 132
135 // Memory fills are triggered once the fill value is written. 133 // Memory fills are triggered once the fill value is written.
136 // NOTE: This is not verified. 134 // NOTE: This is not verified.
137 case Regs::MemoryFill + 3: 135 case GPU_REG_INDEX_WORKAROUND(memory_fill_config[0].value, 0x00004 + 0x3):
138 case Regs::MemoryFill + 7: 136 case GPU_REG_INDEX_WORKAROUND(memory_fill_config[1].value, 0x00008 + 0x3):
139 { 137 {
140 const auto& config = g_regs.Get<Regs::MemoryFill>(static_cast<Regs::Id>(index - 3)); 138 const bool is_second_filler = (index != GPU_REG_INDEX(memory_fill_config[0].value));
139 const auto& config = g_regs.memory_fill_config[is_second_filler];
141 140
142 // TODO: Not sure if this check should be done at GSP level instead 141 // TODO: Not sure if this check should be done at GSP level instead
143 if (config.address_start) { 142 if (config.address_start) {
@@ -152,9 +151,9 @@ inline void Write(u32 addr, const T data) {
152 break; 151 break;
153 } 152 }
154 153
155 case Regs::DisplayTransfer + 6: 154 case GPU_REG_INDEX(display_transfer_config.trigger):
156 { 155 {
157 const auto& config = g_regs.Get<Regs::DisplayTransfer>(); 156 const auto& config = g_regs.display_transfer_config;
158 if (config.trigger & 1) { 157 if (config.trigger & 1) {
159 u8* source_pointer = Memory::GetPointer(config.GetPhysicalInputAddress()); 158 u8* source_pointer = Memory::GetPointer(config.GetPhysicalInputAddress());
160 u8* dest_pointer = Memory::GetPointer(config.GetPhysicalOutputAddress()); 159 u8* dest_pointer = Memory::GetPointer(config.GetPhysicalOutputAddress());
@@ -221,13 +220,13 @@ inline void Write(u32 addr, const T data) {
221 break; 220 break;
222 } 221 }
223 222
224 case Regs::CommandProcessor + 4: 223 case GPU_REG_INDEX(command_processor_config.trigger):
225 { 224 {
226 const auto& config = g_regs.Get<Regs::CommandProcessor>(); 225 const auto& config = g_regs.command_processor_config;
227 if (config.trigger & 1) 226 if (config.trigger & 1)
228 { 227 {
229 // u32* buffer = (u32*)Memory::GetPointer(config.address << 3); 228 // u32* buffer = (u32*)Memory::GetPointer(config.GetPhysicalAddress());
230 ERROR_LOG(GPU, "Beginning 0x%08x bytes of commands from address 0x%08x", config.size, config.address << 3); 229 ERROR_LOG(GPU, "Beginning 0x%08x bytes of commands from address 0x%08x", config.size, config.GetPhysicalAddress());
231 // TODO: Process command list! 230 // TODO: Process command list!
232 } 231 }
233 break; 232 break;
@@ -252,7 +251,7 @@ template void Write<u8>(u32 addr, const u8 data);
252 251
253/// Update hardware 252/// Update hardware
254void Update() { 253void Update() {
255 auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>(); 254 auto& framebuffer_top = g_regs.framebuffer_config[0];
256 u64 current_ticks = Core::g_app_core->GetTicks(); 255 u64 current_ticks = Core::g_app_core->GetTicks();
257 256
258 // Synchronize line... 257 // Synchronize line...
@@ -280,8 +279,8 @@ void Init() {
280// SetFramebufferLocation(FRAMEBUFFER_LOCATION_FCRAM); 279// SetFramebufferLocation(FRAMEBUFFER_LOCATION_FCRAM);
281 SetFramebufferLocation(FRAMEBUFFER_LOCATION_VRAM); 280 SetFramebufferLocation(FRAMEBUFFER_LOCATION_VRAM);
282 281
283 auto& framebuffer_top = g_regs.Get<Regs::FramebufferTop>(); 282 auto& framebuffer_top = g_regs.framebuffer_config[0];
284 auto& framebuffer_sub = g_regs.Get<Regs::FramebufferBottom>(); 283 auto& framebuffer_sub = g_regs.framebuffer_config[1];
285 // TODO: Width should be 240 instead? 284 // TODO: Width should be 240 instead?
286 framebuffer_top.width = 480; 285 framebuffer_top.width = 480;
287 framebuffer_top.height = 400; 286 framebuffer_top.height = 400;
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 42f18a0e7..3065da891 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -4,32 +4,57 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstddef>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "common/bit_field.h" 10#include "common/bit_field.h"
9#include "common/register_set.h"
10 11
11namespace GPU { 12namespace GPU {
12 13
13static const u32 kFrameCycles = 268123480 / 60; ///< 268MHz / 60 frames per second 14static const u32 kFrameCycles = 268123480 / 60; ///< 268MHz / 60 frames per second
14static const u32 kFrameTicks = kFrameCycles / 3; ///< Approximate number of instructions/frame 15static const u32 kFrameTicks = kFrameCycles / 3; ///< Approximate number of instructions/frame
15 16
17// Returns index corresponding to the Regs member labeled by field_name
18// TODO: Due to Visual studio bug 209229, offsetof does not return constant expressions
19// when used with array elements (e.g. GPU_REG_INDEX(memory_fill_config[0])).
20// For details cf. https://connect.microsoft.com/VisualStudio/feedback/details/209229/offsetof-does-not-produce-a-constant-expression-for-array-members
21// Hopefully, this will be fixed sometime in the future.
22// For lack of better alternatives, we currently hardcode the offsets when constant
23// expressions are needed via GPU_REG_INDEX_WORKAROUND (on sane compilers, static_asserts
24// will then make sure the offsets indeed match the automatically calculated ones).
25#define GPU_REG_INDEX(field_name) (offsetof(GPU::Regs, field_name) / sizeof(u32))
26#if defined(_MSC_VER)
27#define GPU_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) (backup_workaround_index)
28#else
29// NOTE: Yeah, hacking in a static_assert here just to workaround the lacking MSVC compiler
30// really is this annoying. This macro just forwards its first argument to GPU_REG_INDEX
31// and then performs a (no-op) cast to size_t iff the second argument matches the expected
32// field offset. Otherwise, the compiler will fail to compile this code.
33#define GPU_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \
34 ((typename std::enable_if<backup_workaround_index == GPU_REG_INDEX(field_name), size_t>::type)GPU_REG_INDEX(field_name))
35#endif
36
16// MMIO region 0x1EFxxxxx 37// MMIO region 0x1EFxxxxx
17struct Regs { 38struct Regs {
18 enum Id : u32 {
19 MemoryFill = 0x00004, // + 5,6,7; second block at 8-11
20
21 FramebufferTop = 0x00117, // + 11a,11b,11c,11d(?),11e...126
22 FramebufferBottom = 0x00157, // + 15a,15b,15c,15d(?),15e...166
23
24 DisplayTransfer = 0x00300, // + 301,302,303,304,305,306
25
26 CommandProcessor = 0x00638, // + 63a,63c
27 39
28 NumIds = 0x01000 40// helper macro to properly align structure members.
29 }; 41// Calling INSERT_PADDING_WORDS will add a new member variable with a name like "pad121",
30 42// depending on the current source line to make sure variable names are unique.
31 template<Id id> 43#define INSERT_PADDING_WORDS_HELPER1(x, y) x ## y
32 struct Struct; 44#define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y)
45#define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)];
46
47// helper macro to make sure the defined structures are of the expected size.
48#if defined(_MSC_VER)
49// TODO: MSVC does not support using sizeof() on non-static data members even though this
50// is technically allowed since C++11. This macro should be enabled once MSVC adds
51// support for that.
52#define ASSERT_MEMBER_SIZE(name, size_in_bytes)
53#else
54#define ASSERT_MEMBER_SIZE(name, size_in_bytes) \
55 static_assert(sizeof(name) == size_in_bytes, \
56 "Structure size and register block length don't match");
57#endif
33 58
34 enum class FramebufferFormat : u32 { 59 enum class FramebufferFormat : u32 {
35 RGBA8 = 0, 60 RGBA8 = 0,
@@ -38,135 +63,191 @@ struct Regs {
38 RGB5A1 = 3, 63 RGB5A1 = 3,
39 RGBA4 = 4, 64 RGBA4 = 4,
40 }; 65 };
41};
42 66
43template<> 67 INSERT_PADDING_WORDS(0x4);
44struct Regs::Struct<Regs::MemoryFill> {
45 u32 address_start;
46 u32 address_end; // ?
47 u32 size;
48 u32 value; // ?
49 68
50 inline u32 GetStartAddress() const { 69 struct {
51 return address_start * 8; 70 u32 address_start;
52 } 71 u32 address_end; // ?
72 u32 size;
73 u32 value; // ?
53 74
54 inline u32 GetEndAddress() const { 75 inline u32 GetStartAddress() const {
55 return address_end * 8; 76 return DecodeAddressRegister(address_start);
56 } 77 }
57};
58static_assert(sizeof(Regs::Struct<Regs::MemoryFill>) == 0x10, "Structure size and register block length don't match");
59 78
60template<> 79 inline u32 GetEndAddress() const {
61struct Regs::Struct<Regs::FramebufferTop> { 80 return DecodeAddressRegister(address_end);
62 using Format = Regs::FramebufferFormat; 81 }
82 } memory_fill_config[2];
83 ASSERT_MEMBER_SIZE(memory_fill_config[0], 0x10);
63 84
64 union { 85 INSERT_PADDING_WORDS(0x10b);
65 u32 size;
66 86
67 BitField< 0, 16, u32> width; 87 struct {
68 BitField<16, 16, u32> height; 88 using Format = Regs::FramebufferFormat;
69 };
70 89
71 u32 pad0[2]; 90 union {
91 u32 size;
72 92
73 u32 address_left1; 93 BitField< 0, 16, u32> width;
74 u32 address_left2; 94 BitField<16, 16, u32> height;
95 };
75 96
76 union { 97 INSERT_PADDING_WORDS(0x2);
77 u32 format;
78 98
79 BitField< 0, 3, Format> color_format; 99 u32 address_left1;
80 }; 100 u32 address_left2;
81 101
82 u32 pad1; 102 union {
103 u32 format;
83 104
84 union { 105 BitField< 0, 3, Format> color_format;
85 u32 active_fb; 106 };
86 107
87 // 0: Use parameters ending with "1" 108 INSERT_PADDING_WORDS(0x1);
88 // 1: Use parameters ending with "2"
89 BitField<0, 1, u32> second_fb_active;
90 };
91 109
92 u32 pad2[5]; 110 union {
111 u32 active_fb;
93 112
94 // Distance between two pixel rows, in bytes 113 // 0: Use parameters ending with "1"
95 u32 stride; 114 // 1: Use parameters ending with "2"
115 BitField<0, 1, u32> second_fb_active;
116 };
96 117
97 u32 address_right1; 118 INSERT_PADDING_WORDS(0x5);
98 u32 address_right2;
99};
100 119
101template<> 120 // Distance between two pixel rows, in bytes
102struct Regs::Struct<Regs::FramebufferBottom> : public Regs::Struct<Regs::FramebufferTop> { 121 u32 stride;
103};
104static_assert(sizeof(Regs::Struct<Regs::FramebufferTop>) == 0x40, "Structure size and register block length don't match");
105 122
106template<> 123 u32 address_right1;
107struct Regs::Struct<Regs::DisplayTransfer> { 124 u32 address_right2;
108 using Format = Regs::FramebufferFormat;
109 125
110 u32 input_address; 126 INSERT_PADDING_WORDS(0x30);
111 u32 output_address; 127 } framebuffer_config[2];
128 ASSERT_MEMBER_SIZE(framebuffer_config[0], 0x100);
112 129
113 inline u32 GetPhysicalInputAddress() const { 130 INSERT_PADDING_WORDS(0x169);
114 return input_address * 8;
115 }
116 131
117 inline u32 GetPhysicalOutputAddress() const { 132 struct {
118 return output_address * 8; 133 using Format = Regs::FramebufferFormat;
119 }
120 134
121 union { 135 u32 input_address;
122 u32 output_size; 136 u32 output_address;
123 137
124 BitField< 0, 16, u32> output_width; 138 inline u32 GetPhysicalInputAddress() const {
125 BitField<16, 16, u32> output_height; 139 return DecodeAddressRegister(input_address);
126 }; 140 }
127 141
128 union { 142 inline u32 GetPhysicalOutputAddress() const {
129 u32 input_size; 143 return DecodeAddressRegister(output_address);
144 }
130 145
131 BitField< 0, 16, u32> input_width; 146 union {
132 BitField<16, 16, u32> input_height; 147 u32 output_size;
133 };
134 148
135 union { 149 BitField< 0, 16, u32> output_width;
136 u32 flags; 150 BitField<16, 16, u32> output_height;
151 };
137 152
138 BitField< 0, 1, u32> flip_data; // flips input data horizontally (TODO) if true 153 union {
139 BitField< 8, 3, Format> input_format; 154 u32 input_size;
140 BitField<12, 3, Format> output_format;
141 BitField<16, 1, u32> output_tiled; // stores output in a tiled format
142 };
143 155
144 u32 unknown; 156 BitField< 0, 16, u32> input_width;
157 BitField<16, 16, u32> input_height;
158 };
145 159
146 // it seems that writing to this field triggers the display transfer 160 union {
147 u32 trigger; 161 u32 flags;
148};
149static_assert(sizeof(Regs::Struct<Regs::DisplayTransfer>) == 0x1C, "Structure size and register block length don't match");
150 162
151template<> 163 BitField< 0, 1, u32> flip_data; // flips input data horizontally (TODO) if true
152struct Regs::Struct<Regs::CommandProcessor> { 164 BitField< 8, 3, Format> input_format;
153 // command list size 165 BitField<12, 3, Format> output_format;
154 u32 size; 166 BitField<16, 1, u32> output_tiled; // stores output in a tiled format
167 };
155 168
156 u32 pad0; 169 INSERT_PADDING_WORDS(0x1);
157 170
158 // command list address 171 // it seems that writing to this field triggers the display transfer
159 u32 address; 172 u32 trigger;
173 } display_transfer_config;
174 ASSERT_MEMBER_SIZE(display_transfer_config, 0x1c);
160 175
161 u32 pad1; 176 INSERT_PADDING_WORDS(0x331);
162 177
163 // it seems that writing to this field triggers command list processing 178 struct {
164 u32 trigger; 179 // command list size
165}; 180 u32 size;
166static_assert(sizeof(Regs::Struct<Regs::CommandProcessor>) == 0x14, "Structure size and register block length don't match"); 181
182 INSERT_PADDING_WORDS(0x1);
183
184 // command list address
185 u32 address;
186
187 INSERT_PADDING_WORDS(0x1);
188
189 // it seems that writing to this field triggers command list processing
190 u32 trigger;
167 191
192 inline u32 GetPhysicalAddress() const {
193 return DecodeAddressRegister(address);
194 }
195 } command_processor_config;
196 ASSERT_MEMBER_SIZE(command_processor_config, 0x14);
168 197
169extern RegisterSet<u32, Regs> g_regs; 198 INSERT_PADDING_WORDS(0x9c3);
199
200#undef INSERT_PADDING_WORDS_HELPER1
201#undef INSERT_PADDING_WORDS_HELPER2
202#undef INSERT_PADDING_WORDS
203
204 static inline int NumIds() {
205 return sizeof(Regs) / sizeof(u32);
206 }
207
208 u32& operator [] (int index) const {
209 u32* content = (u32*)this;
210 return content[index];
211 }
212
213 u32& operator [] (int index) {
214 u32* content = (u32*)this;
215 return content[index];
216 }
217
218private:
219 /*
220 * Most physical addresses which GPU registers refer to are 8-byte aligned.
221 * This function should be used to get the address from a raw register value.
222 */
223 static inline u32 DecodeAddressRegister(u32 register_value) {
224 return register_value * 8;
225 }
226};
227static_assert(std::is_standard_layout<Regs>::value, "Structure does not use standard layout");
228
229// TODO: MSVC does not support using offsetof() on non-static data members even though this
230// is technically allowed since C++11. This macro should be enabled once MSVC adds
231// support for that.
232#ifndef _MSC_VER
233#define ASSERT_REG_POSITION(field_name, position) \
234 static_assert(offsetof(Regs, field_name) == position * 4, \
235 "Field "#field_name" has invalid position")
236
237ASSERT_REG_POSITION(memory_fill_config[0], 0x00004);
238ASSERT_REG_POSITION(memory_fill_config[1], 0x00008);
239ASSERT_REG_POSITION(framebuffer_config[0], 0x00117);
240ASSERT_REG_POSITION(framebuffer_config[1], 0x00157);
241ASSERT_REG_POSITION(display_transfer_config, 0x00300);
242ASSERT_REG_POSITION(command_processor_config, 0x00638);
243
244#undef ASSERT_REG_POSITION
245#endif // !defined(_MSC_VER)
246
247// The total number of registers is chosen arbitrarily, but let's make sure it's not some odd value anyway.
248static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set");
249
250extern Regs g_regs;
170 251
171enum { 252enum {
172 TOP_ASPECT_X = 0x5, 253 TOP_ASPECT_X = 0x5,