summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar Tony Wasserka2014-08-03 16:00:52 +0200
committerGravatar Tony Wasserka2014-08-12 02:17:21 +0200
commit9c781a6c7646a3f30c23adae75e1879b7fc47d0f (patch)
tree76f32e7dc447e661f9f34303609cf4aed23d7a5b /src/core
parentMerge pull request #40 from bentley/master (diff)
downloadyuzu-9c781a6c7646a3f30c23adae75e1879b7fc47d0f.tar.gz
yuzu-9c781a6c7646a3f30c23adae75e1879b7fc47d0f.tar.xz
yuzu-9c781a6c7646a3f30c23adae75e1879b7fc47d0f.zip
Remove the fancy RegisterSet class introduced in 4c2bff61e.
While it was some nice and fancy template usage, it ultimately had many practical issues regarding length of involved expressions under regular usage as well as common code completion tools not being able to handle the structures. Instead, we now use a more conventional approach which is a lot more clean to use.
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,