summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar bunnei2014-08-25 16:12:10 -0400
committerGravatar bunnei2014-08-25 16:12:10 -0400
commit97fd8fc38d4f9c288779cddb06538860124c6263 (patch)
treebc99e0fceaae732f9c8d4831fcdb8f661b49ccb8 /src/core
parentMerge pull request #75 from xsacha/qt5 (diff)
parentPica/Rasterizer: Clarify a TODO. (diff)
downloadyuzu-97fd8fc38d4f9c288779cddb06538860124c6263.tar.gz
yuzu-97fd8fc38d4f9c288779cddb06538860124c6263.tar.xz
yuzu-97fd8fc38d4f9c288779cddb06538860124c6263.zip
Merge pull request #50 from neobrain/pica
Further work on Pica emulation
Diffstat (limited to 'src/core')
-rw-r--r--src/core/hle/service/gsp.cpp105
-rw-r--r--src/core/hle/service/gsp.h30
-rw-r--r--src/core/hw/gpu.h4
3 files changed, 112 insertions, 27 deletions
diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp
index 635f50a53..46c5a8ddd 100644
--- a/src/core/hle/service/gsp.cpp
+++ b/src/core/hle/service/gsp.cpp
@@ -36,41 +36,57 @@ static inline u8* GetCommandBuffer(u32 thread_id) {
36 0x800 + (thread_id * sizeof(CommandBuffer))); 36 0x800 + (thread_id * sizeof(CommandBuffer)));
37} 37}
38 38
39static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) {
40 if (0 == g_shared_memory)
41 return nullptr;
42
43 _dbg_assert_msg_(GSP, screen_index < 2, "Invalid screen index");
44
45 // For each thread there are two FrameBufferUpdate fields
46 u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
47 return (FrameBufferUpdate*)Kernel::GetSharedMemoryPointer(g_shared_memory, offset);
48}
49
39/// Gets a pointer to the interrupt relay queue for a given thread index 50/// Gets a pointer to the interrupt relay queue for a given thread index
40static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { 51static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) {
41 return (InterruptRelayQueue*)Kernel::GetSharedMemoryPointer(g_shared_memory, 52 return (InterruptRelayQueue*)Kernel::GetSharedMemoryPointer(g_shared_memory,
42 sizeof(InterruptRelayQueue) * thread_id); 53 sizeof(InterruptRelayQueue) * thread_id);
43} 54}
44 55
45/// Write a GSP GPU hardware register 56void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) {
46void WriteHWRegs(Service::Interface* self) {
47 u32* cmd_buff = Service::GetCommandBuffer();
48 u32 reg_addr = cmd_buff[1];
49 u32 size = cmd_buff[2];
50
51 // TODO: Return proper error codes 57 // TODO: Return proper error codes
52 if (reg_addr + size >= 0x420000) { 58 if (base_address + size_in_bytes >= 0x420000) {
53 ERROR_LOG(GPU, "Write address out of range! (address=0x%08x, size=0x%08x)", reg_addr, size); 59 ERROR_LOG(GPU, "Write address out of range! (address=0x%08x, size=0x%08x)",
60 base_address, size_in_bytes);
54 return; 61 return;
55 } 62 }
56 63
57 // size should be word-aligned 64 // size should be word-aligned
58 if ((size % 4) != 0) { 65 if ((size_in_bytes % 4) != 0) {
59 ERROR_LOG(GPU, "Invalid size 0x%08x", size); 66 ERROR_LOG(GPU, "Invalid size 0x%08x", size_in_bytes);
60 return; 67 return;
61 } 68 }
62 69
63 u32* src = (u32*)Memory::GetPointer(cmd_buff[0x4]); 70 while (size_in_bytes > 0) {
71 GPU::Write<u32>(base_address + 0x1EB00000, *data);
64 72
65 while (size > 0) { 73 size_in_bytes -= 4;
66 GPU::Write<u32>(reg_addr + 0x1EB00000, *src); 74 ++data;
67 75 base_address += 4;
68 size -= 4;
69 ++src;
70 reg_addr += 4;
71 } 76 }
72} 77}
73 78
79/// Write a GSP GPU hardware register
80void WriteHWRegs(Service::Interface* self) {
81 u32* cmd_buff = Service::GetCommandBuffer();
82 u32 reg_addr = cmd_buff[1];
83 u32 size = cmd_buff[2];
84
85 u32* src = (u32*)Memory::GetPointer(cmd_buff[0x4]);
86
87 WriteHWRegs(reg_addr, size, src);
88}
89
74/// Read a GSP GPU hardware register 90/// Read a GSP GPU hardware register
75void ReadHWRegs(Service::Interface* self) { 91void ReadHWRegs(Service::Interface* self) {
76 u32* cmd_buff = Service::GetCommandBuffer(); 92 u32* cmd_buff = Service::GetCommandBuffer();
@@ -100,6 +116,40 @@ void ReadHWRegs(Service::Interface* self) {
100 } 116 }
101} 117}
102 118
119void SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) {
120 u32 base_address = 0x400000;
121 if (info.active_fb == 0) {
122 WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].address_left1), 4, &info.address_left);
123 WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].address_right1), 4, &info.address_right);
124 } else {
125 WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].address_left2), 4, &info.address_left);
126 WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].address_right2), 4, &info.address_right);
127 }
128 WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].stride), 4, &info.stride);
129 WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].color_format), 4, &info.format);
130 WriteHWRegs(base_address + 4 * GPU_REG_INDEX(framebuffer_config[screen_id].active_fb), 4, &info.shown_fb);
131}
132
133/**
134 * GSP_GPU::SetBufferSwap service function
135 *
136 * Updates GPU display framebuffer configuration using the specified parameters.
137 *
138 * Inputs:
139 * 1 : Screen ID (0 = top screen, 1 = bottom screen)
140 * 2-7 : FrameBufferInfo structure
141 * Outputs:
142 * 1: Result code
143 */
144void SetBufferSwap(Service::Interface* self) {
145 u32* cmd_buff = Service::GetCommandBuffer();
146 u32 screen_id = cmd_buff[1];
147 FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2];
148 SetBufferSwap(screen_id, *fb_info);
149
150 cmd_buff[1] = 0; // No error
151}
152
103/** 153/**
104 * GSP_GPU::RegisterInterruptRelayQueue service function 154 * GSP_GPU::RegisterInterruptRelayQueue service function
105 * Inputs: 155 * Inputs:
@@ -127,6 +177,7 @@ void RegisterInterruptRelayQueue(Service::Interface* self) {
127/** 177/**
128 * Signals that the specified interrupt type has occurred to userland code 178 * Signals that the specified interrupt type has occurred to userland code
129 * @param interrupt_id ID of interrupt that is being signalled 179 * @param interrupt_id ID of interrupt that is being signalled
180 * @todo This should probably take a thread_id parameter and only signal this thread?
130 */ 181 */
131void SignalInterrupt(InterruptId interrupt_id) { 182void SignalInterrupt(InterruptId interrupt_id) {
132 if (0 == g_interrupt_event) { 183 if (0 == g_interrupt_event) {
@@ -152,7 +203,7 @@ void SignalInterrupt(InterruptId interrupt_id) {
152} 203}
153 204
154/// Executes the next GSP command 205/// Executes the next GSP command
155void ExecuteCommand(const Command& command) { 206void ExecuteCommand(const Command& command, u32 thread_id) {
156 // Utility function to convert register ID to address 207 // Utility function to convert register ID to address
157 auto WriteGPURegister = [](u32 id, u32 data) { 208 auto WriteGPURegister = [](u32 id, u32 data) {
158 GPU::Write<u32>(0x1EF00000 + 4 * id, data); 209 GPU::Write<u32>(0x1EF00000 + 4 * id, data);
@@ -179,11 +230,6 @@ void ExecuteCommand(const Command& command) {
179 // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though 230 // TODO: Not sure if we are supposed to always write this .. seems to trigger processing though
180 WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1); 231 WriteGPURegister(GPU_REG_INDEX(command_processor_config.trigger), 1);
181 232
182 // TODO: Move this to GPU
183 // TODO: Not sure what units the size is measured in
184 g_debugger.CommandListCalled(params.address,
185 (u32*)Memory::GetPointer(params.address),
186 params.size);
187 SignalInterrupt(InterruptId::P3D); 233 SignalInterrupt(InterruptId::P3D);
188 break; 234 break;
189 } 235 }
@@ -223,6 +269,15 @@ void ExecuteCommand(const Command& command) {
223 SignalInterrupt(InterruptId::PPF); 269 SignalInterrupt(InterruptId::PPF);
224 SignalInterrupt(InterruptId::P3D); 270 SignalInterrupt(InterruptId::P3D);
225 SignalInterrupt(InterruptId::DMA); 271 SignalInterrupt(InterruptId::DMA);
272
273 // Update framebuffer information if requested
274 for (int screen_id = 0; screen_id < 2; ++screen_id) {
275 FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
276 if (info->is_dirty)
277 SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
278
279 info->is_dirty = false;
280 }
226 break; 281 break;
227 } 282 }
228 283
@@ -265,7 +320,7 @@ void TriggerCmdReqQueue(Service::Interface* self) {
265 g_debugger.GXCommandProcessed((u8*)&command_buffer->commands[i]); 320 g_debugger.GXCommandProcessed((u8*)&command_buffer->commands[i]);
266 321
267 // Decode and execute command 322 // Decode and execute command
268 ExecuteCommand(command_buffer->commands[i]); 323 ExecuteCommand(command_buffer->commands[i], thread_id);
269 324
270 // Indicates that command has completed 325 // Indicates that command has completed
271 command_buffer->number_commands = command_buffer->number_commands - 1; 326 command_buffer->number_commands = command_buffer->number_commands - 1;
@@ -278,7 +333,7 @@ const Interface::FunctionInfo FunctionTable[] = {
278 {0x00020084, nullptr, "WriteHWRegsWithMask"}, 333 {0x00020084, nullptr, "WriteHWRegsWithMask"},
279 {0x00030082, nullptr, "WriteHWRegRepeat"}, 334 {0x00030082, nullptr, "WriteHWRegRepeat"},
280 {0x00040080, ReadHWRegs, "ReadHWRegs"}, 335 {0x00040080, ReadHWRegs, "ReadHWRegs"},
281 {0x00050200, nullptr, "SetBufferSwap"}, 336 {0x00050200, SetBufferSwap, "SetBufferSwap"},
282 {0x00060082, nullptr, "SetCommandList"}, 337 {0x00060082, nullptr, "SetCommandList"},
283 {0x000700C2, nullptr, "RequestDma"}, 338 {0x000700C2, nullptr, "RequestDma"},
284 {0x00080082, nullptr, "FlushDataCache"}, 339 {0x00080082, nullptr, "FlushDataCache"},
diff --git a/src/core/hle/service/gsp.h b/src/core/hle/service/gsp.h
index b25dbb7bc..a09d59dbb 100644
--- a/src/core/hle/service/gsp.h
+++ b/src/core/hle/service/gsp.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstddef>
8
7#include "common/bit_field.h" 9#include "common/bit_field.h"
8#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
9 11
@@ -64,6 +66,34 @@ struct InterruptRelayQueue {
64static_assert(sizeof(InterruptRelayQueue) == 0x40, 66static_assert(sizeof(InterruptRelayQueue) == 0x40,
65 "InterruptRelayQueue struct has incorrect size"); 67 "InterruptRelayQueue struct has incorrect size");
66 68
69struct FrameBufferInfo {
70 BitField<0, 1, u32> active_fb; // 0 = first, 1 = second
71
72 u32 address_left;
73 u32 address_right;
74 u32 stride; // maps to 0x1EF00X90 ?
75 u32 format; // maps to 0x1EF00X70 ?
76 u32 shown_fb; // maps to 0x1EF00X78 ?
77 u32 unknown;
78};
79static_assert(sizeof(FrameBufferInfo) == 0x1c, "Struct has incorrect size");
80
81struct FrameBufferUpdate {
82 BitField<0, 1, u8> index; // Index used for GSP::SetBufferSwap
83 BitField<0, 1, u8> is_dirty; // true if GSP should update GPU framebuffer registers
84 u16 pad1;
85
86 FrameBufferInfo framebuffer_info[2];
87
88 u32 pad2;
89};
90static_assert(sizeof(FrameBufferUpdate) == 0x40, "Struct has incorrect size");
91// TODO: Not sure if this padding is correct.
92// Chances are the second block is stored at offset 0x24 rather than 0x20.
93#ifndef _MSC_VER
94static_assert(offsetof(FrameBufferUpdate, framebuffer_info[1]) == 0x20, "FrameBufferInfo element has incorrect alignment");
95#endif
96
67/// GSP command 97/// GSP command
68struct Command { 98struct Command {
69 BitField<0, 8, CommandId> id; 99 BitField<0, 8, CommandId> id;
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index d20311a00..7186bfa84 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -42,7 +42,7 @@ struct Regs {
42// depending on the current source line to make sure variable names are unique. 42// depending on the current source line to make sure variable names are unique.
43#define INSERT_PADDING_WORDS_HELPER1(x, y) x ## y 43#define INSERT_PADDING_WORDS_HELPER1(x, y) x ## y
44#define INSERT_PADDING_WORDS_HELPER2(x, y) INSERT_PADDING_WORDS_HELPER1(x, y) 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)]; 45#define INSERT_PADDING_WORDS(num_words) u32 INSERT_PADDING_WORDS_HELPER2(pad, __LINE__)[(num_words)]
46 46
47// helper macro to make sure the defined structures are of the expected size. 47// helper macro to make sure the defined structures are of the expected size.
48#if defined(_MSC_VER) 48#if defined(_MSC_VER)
@@ -53,7 +53,7 @@ struct Regs {
53#else 53#else
54#define ASSERT_MEMBER_SIZE(name, size_in_bytes) \ 54#define ASSERT_MEMBER_SIZE(name, size_in_bytes) \
55 static_assert(sizeof(name) == size_in_bytes, \ 55 static_assert(sizeof(name) == size_in_bytes, \
56 "Structure size and register block length don't match"); 56 "Structure size and register block length don't match")
57#endif 57#endif
58 58
59 enum class FramebufferFormat : u32 { 59 enum class FramebufferFormat : u32 {