mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 07:05:40 +12:00
Trongle
This commit is contained in:
parent
75070ca6ef
commit
be4fae5104
33 changed files with 341 additions and 70 deletions
|
@ -55,7 +55,7 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services
|
||||||
src/core/services/ndm.cpp
|
src/core/services/ndm.cpp
|
||||||
)
|
)
|
||||||
set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp
|
set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp
|
||||||
src/core/PICA/shader_interpreter.cpp
|
src/core/PICA/shader_interpreter.cpp src/core/PICA/renderer_opengl.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp include/termcolor.hpp
|
set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp include/termcolor.hpp
|
||||||
|
@ -66,6 +66,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp inc
|
||||||
include/services/gsp_gpu.hpp include/services/gsp_lcd.hpp include/arm_defs.hpp
|
include/services/gsp_gpu.hpp include/services/gsp_lcd.hpp include/arm_defs.hpp
|
||||||
include/PICA/gpu.hpp include/PICA/regs.hpp include/services/ndm.hpp
|
include/PICA/gpu.hpp include/PICA/regs.hpp include/services/ndm.hpp
|
||||||
include/PICA/shader.hpp include/PICA/shader_unit.hpp include/PICA/float_types.hpp
|
include/PICA/shader.hpp include/PICA/shader_unit.hpp include/PICA/float_types.hpp
|
||||||
|
include/logger.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <array>
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
#include "opengl.hpp"
|
#include "opengl.hpp"
|
||||||
#include "PICA/float_types.hpp"
|
#include "PICA/float_types.hpp"
|
||||||
|
@ -12,12 +13,18 @@ class GPU {
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
ShaderUnit shaderUnit;
|
ShaderUnit shaderUnit;
|
||||||
u8* vram = nullptr;
|
u8* vram = nullptr;
|
||||||
|
MAKE_LOG_FUNCTION(log, gpuLogger)
|
||||||
|
|
||||||
static constexpr u32 maxAttribCount = 12; // Up to 12 vertex attributes
|
static constexpr u32 maxAttribCount = 12; // Up to 12 vertex attributes
|
||||||
static constexpr u32 regNum = 0x300;
|
static constexpr u32 regNum = 0x300;
|
||||||
static constexpr u32 vramSize = 6_MB;
|
static constexpr u32 vramSize = 6_MB;
|
||||||
std::array<u32, regNum> regs; // GPU internal registers
|
std::array<u32, regNum> regs; // GPU internal registers
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
OpenGL::vec4 position;
|
||||||
|
OpenGL::vec4 colour;
|
||||||
|
};
|
||||||
|
|
||||||
// Read a value of type T from physical address paddr
|
// Read a value of type T from physical address paddr
|
||||||
// This is necessary because vertex attribute fetching uses physical addresses
|
// This is necessary because vertex attribute fetching uses physical addresses
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -71,8 +78,27 @@ class GPU {
|
||||||
u32 fixedAttribCount = 0; // How many attribute components have we written? When we get to 4 the attr will actually get submitted
|
u32 fixedAttribCount = 0; // How many attribute components have we written? When we get to 4 the attr will actually get submitted
|
||||||
std::array<u32, 3> fixedAttrBuff; // Buffer to hold fixed attributes in until they get submitted
|
std::array<u32, 3> fixedAttrBuff; // Buffer to hold fixed attributes in until they get submitted
|
||||||
|
|
||||||
|
OpenGL::Framebuffer fbo;
|
||||||
|
OpenGL::Texture fboTexture;
|
||||||
|
OpenGL::Program triangleProgram;
|
||||||
|
OpenGL::Program displayProgram;
|
||||||
|
|
||||||
|
OpenGL::VertexArray vao;
|
||||||
|
OpenGL::VertexBuffer vbo;
|
||||||
|
|
||||||
|
// Dummy VAO/VBO for blitting the final output
|
||||||
|
OpenGL::VertexArray dummyVAO;
|
||||||
|
OpenGL::VertexBuffer dummyVBO;
|
||||||
|
|
||||||
|
static constexpr u32 vertexBufferSize = 0x1000;
|
||||||
|
void drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GPU(Memory& mem);
|
GPU(Memory& mem);
|
||||||
|
void initGraphicsContext(); // Initialize graphics context
|
||||||
|
void getGraphicsContext(); // Set up the graphics context for rendering
|
||||||
|
void display(); // Display the screen contents onto our window
|
||||||
|
|
||||||
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control);
|
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control);
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,8 @@ namespace PICAInternalRegs {
|
||||||
FixedAttribData1 = 0x234,
|
FixedAttribData1 = 0x234,
|
||||||
FixedAttribData2 = 0x235,
|
FixedAttribData2 = 0x235,
|
||||||
|
|
||||||
|
PrimitiveConfig = 0x25E,
|
||||||
|
|
||||||
// Vertex shader registers
|
// Vertex shader registers
|
||||||
VertexShaderTransferEnd = 0x2BF,
|
VertexShaderTransferEnd = 0x2BF,
|
||||||
VertexFloatUniformIndex = 0x2C0,
|
VertexFloatUniformIndex = 0x2C0,
|
||||||
|
|
|
@ -158,6 +158,8 @@ public:
|
||||||
void runFrame() {
|
void runFrame() {
|
||||||
env.ticksLeft = 268111856 / 60;
|
env.ticksLeft = 268111856 / 60;
|
||||||
const auto exitReason = jit->Run();
|
const auto exitReason = jit->Run();
|
||||||
Helpers::panic("Exit reason: %d\nPC: %08X", (u32)exitReason, getReg(15));
|
if (static_cast<u32>(exitReason) != 0) [[unlikely]] {
|
||||||
|
Helpers::panic("Exit reason: %d\nPC: %08X", static_cast<u32>(exitReason), getReg(15));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -43,4 +43,5 @@ public:
|
||||||
|
|
||||||
bool loadELF(std::filesystem::path& path);
|
bool loadELF(std::filesystem::path& path);
|
||||||
bool loadELF(std::ifstream& file);
|
bool loadELF(std::ifstream& file);
|
||||||
|
void initGraphicsContext() { gpu.initGraphicsContext(); }
|
||||||
};
|
};
|
|
@ -5,6 +5,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
#include "resource_limits.hpp"
|
#include "resource_limits.hpp"
|
||||||
#include "services/service_manager.hpp"
|
#include "services/service_manager.hpp"
|
||||||
|
@ -58,7 +59,7 @@ class Kernel {
|
||||||
}
|
}
|
||||||
|
|
||||||
objects.push_back(KernelObject(handleCounter, type));
|
objects.push_back(KernelObject(handleCounter, type));
|
||||||
printf("Created %s object with handle %d\n", kernelObjectTypeToString(type), handleCounter);
|
log("Created %s object with handle %d\n", kernelObjectTypeToString(type), handleCounter);
|
||||||
return handleCounter++;
|
return handleCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +84,9 @@ class Kernel {
|
||||||
std::string getProcessName(u32 pid);
|
std::string getProcessName(u32 pid);
|
||||||
const char* resetTypeToString(u32 type);
|
const char* resetTypeToString(u32 type);
|
||||||
|
|
||||||
|
MAKE_LOG_FUNCTION(log, kernelLogger)
|
||||||
|
MAKE_LOG_FUNCTION(logSVC, svcLogger)
|
||||||
|
|
||||||
// SVC implementations
|
// SVC implementations
|
||||||
void arbitrateAddress();
|
void arbitrateAddress();
|
||||||
void clearEvent();
|
void clearEvent();
|
||||||
|
@ -110,4 +114,6 @@ public:
|
||||||
void setVersion(u8 major, u8 minor);
|
void setVersion(u8 major, u8 minor);
|
||||||
void serviceSVC(u32 svc);
|
void serviceSVC(u32 svc);
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.requestGPUInterrupt(type); }
|
||||||
};
|
};
|
39
include/logger.hpp
Normal file
39
include/logger.hpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace Log {
|
||||||
|
// Our logger class
|
||||||
|
template <bool enabled>
|
||||||
|
class Logger {
|
||||||
|
public:
|
||||||
|
void log(const char* fmt, ...) {
|
||||||
|
if constexpr (!enabled) return;
|
||||||
|
|
||||||
|
std::va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
std::vprintf(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Our loggers here. Enable/disable by toggling the template param
|
||||||
|
static Logger<false> kernelLogger;
|
||||||
|
static Logger<false> svcLogger;
|
||||||
|
static Logger<false> gpuLogger;
|
||||||
|
|
||||||
|
// Service loggers
|
||||||
|
static Logger<false> aptLogger;
|
||||||
|
static Logger<false> fsLogger;
|
||||||
|
static Logger<false> hidLogger;
|
||||||
|
static Logger<false> gspGPULogger;
|
||||||
|
static Logger<false> gspLCDLogger;
|
||||||
|
static Logger<false> ndmLogger;
|
||||||
|
static Logger<false> srvLogger;
|
||||||
|
|
||||||
|
#define MAKE_LOG_FUNCTION(functionName, logger) \
|
||||||
|
template <typename... Args> \
|
||||||
|
void functionName(const char* fmt, Args... args) { \
|
||||||
|
Log::logger.log(fmt, args...); \
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,7 +57,7 @@ namespace OpenGL {
|
||||||
void bind() { glBindVertexArray(m_handle); }
|
void bind() { glBindVertexArray(m_handle); }
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void setAttributeFloat(GLuint index, GLint size, GLsizei stride, const void* offset, bool normalized = false) {
|
void setAttributeFloat(GLuint index, GLint size, GLsizei stride, const void* offset, bool normalized = GL_FALSE) {
|
||||||
if constexpr (std::is_same<T, GLfloat>()) {
|
if constexpr (std::is_same<T, GLfloat>()) {
|
||||||
glVertexAttribPointer(index, size, GL_FLOAT, normalized, stride, offset);
|
glVertexAttribPointer(index, size, GL_FLOAT, normalized, stride, offset);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
|
||||||
class APTService {
|
class APTService {
|
||||||
Handle handle = KernelHandles::APT;
|
Handle handle = KernelHandles::APT;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
MAKE_LOG_FUNCTION(log, aptLogger)
|
||||||
|
|
||||||
// Service commands
|
// Service commands
|
||||||
void getLockHandle(u32 messagePointer);
|
void getLockHandle(u32 messagePointer);
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
|
||||||
class FSService {
|
class FSService {
|
||||||
Handle handle = KernelHandles::FS;
|
Handle handle = KernelHandles::FS;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
MAKE_LOG_FUNCTION(log, fsLogger)
|
||||||
|
|
||||||
// Service commands
|
// Service commands
|
||||||
void initialize(u32 messagePointer);
|
void initialize(u32 messagePointer);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "PICA/gpu.hpp"
|
#include "PICA/gpu.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
|
||||||
enum class GPUInterrupt : u8 {
|
enum class GPUInterrupt : u8 {
|
||||||
|
@ -26,6 +27,7 @@ class GPUService {
|
||||||
// This is the PID of that process
|
// This is the PID of that process
|
||||||
u32 privilegedProcess;
|
u32 privilegedProcess;
|
||||||
|
|
||||||
|
MAKE_LOG_FUNCTION(log, gspGPULogger)
|
||||||
void processCommandBuffer();
|
void processCommandBuffer();
|
||||||
|
|
||||||
// Service commands
|
// Service commands
|
||||||
|
@ -40,6 +42,7 @@ class GPUService {
|
||||||
// GSP commands processed via TriggerCmdReqQueue
|
// GSP commands processed via TriggerCmdReqQueue
|
||||||
void processCommandList(u32* cmd);
|
void processCommandList(u32* cmd);
|
||||||
void memoryFill(u32* cmd);
|
void memoryFill(u32* cmd);
|
||||||
|
void triggerDisplayTransfer(u32* cmd);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GPUService(Memory& mem, GPU& gpu, u32& currentPID) : mem(mem), gpu(gpu), currentPID(currentPID) {}
|
GPUService(Memory& mem, GPU& gpu, u32& currentPID) : mem(mem), gpu(gpu), currentPID(currentPID) {}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
|
||||||
class LCDService {
|
class LCDService {
|
||||||
Handle handle = KernelHandles::LCD;
|
Handle handle = KernelHandles::LCD;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
MAKE_LOG_FUNCTION(log, gspLCDLogger)
|
||||||
|
|
||||||
// Service commands
|
// Service commands
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
|
||||||
class HIDService {
|
class HIDService {
|
||||||
Handle handle = KernelHandles::HID;
|
Handle handle = KernelHandles::HID;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
MAKE_LOG_FUNCTION(log, hidLogger)
|
||||||
|
|
||||||
// Service commands
|
// Service commands
|
||||||
void getIPCHandles(u32 messagePointer);
|
void getIPCHandles(u32 messagePointer);
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
|
||||||
class NDMService {
|
class NDMService {
|
||||||
Handle handle = KernelHandles::NDM;
|
Handle handle = KernelHandles::NDM;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
MAKE_LOG_FUNCTION(log, ndmLogger)
|
||||||
|
|
||||||
// Service commands
|
// Service commands
|
||||||
void overrideDefaultDaemons(u32 messagePointer);
|
void overrideDefaultDaemons(u32 messagePointer);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <array>
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
#include "services/apt.hpp"
|
#include "services/apt.hpp"
|
||||||
#include "services/hid.hpp"
|
#include "services/hid.hpp"
|
||||||
|
@ -12,6 +13,7 @@
|
||||||
class ServiceManager {
|
class ServiceManager {
|
||||||
std::array<u32, 16>& regs;
|
std::array<u32, 16>& regs;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
MAKE_LOG_FUNCTION(log, srvLogger)
|
||||||
|
|
||||||
APTService apt;
|
APTService apt;
|
||||||
HIDService hid;
|
HIDService hid;
|
||||||
|
|
|
@ -30,10 +30,6 @@ void GPU::reset() {
|
||||||
// TODO: Reset blending, texturing, etc here
|
// TODO: Reset blending, texturing, etc here
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
|
||||||
printf("GPU: Clear buffer\nStart: %08X End: %08X\nValue: %08X Control: %08X\n", startAddress, endAddress, value, control);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPU::drawArrays(bool indexed) {
|
void GPU::drawArrays(bool indexed) {
|
||||||
if (indexed)
|
if (indexed)
|
||||||
drawArrays<true>();
|
drawArrays<true>();
|
||||||
|
@ -48,13 +44,22 @@ void GPU::drawArrays() {
|
||||||
const u32 vertexBase = ((regs[PICAInternalRegs::VertexAttribLoc] >> 1) & 0xfffffff) * 16;
|
const u32 vertexBase = ((regs[PICAInternalRegs::VertexAttribLoc] >> 1) & 0xfffffff) * 16;
|
||||||
const u32 vertexCount = regs[PICAInternalRegs::VertexCountReg]; // Total # of vertices to transfer
|
const u32 vertexCount = regs[PICAInternalRegs::VertexCountReg]; // Total # of vertices to transfer
|
||||||
|
|
||||||
|
// Configures the type of primitive and the number of vertex shader outputs
|
||||||
|
const u32 primConfig = regs[PICAInternalRegs::PrimitiveConfig];
|
||||||
|
const u32 primType = (primConfig >> 8) & 3;
|
||||||
|
if (primType != 0) Helpers::panic("[PICA] Tried to draw non-triangle shape %d\n", primType);
|
||||||
|
if (vertexCount % 3) Helpers::panic("[PICA] Vertex count not a multiple of 3");
|
||||||
|
if (vertexCount > vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize");
|
||||||
|
|
||||||
|
Vertex vertices[vertexBufferSize];
|
||||||
|
|
||||||
// Stuff the global attribute config registers in one u64 to make attr parsing easier
|
// Stuff the global attribute config registers in one u64 to make attr parsing easier
|
||||||
// TODO: Cache this when the vertex attribute format registers are written to
|
// TODO: Cache this when the vertex attribute format registers are written to
|
||||||
u64 vertexCfg = u64(regs[PICAInternalRegs::AttribFormatLow]) | (u64(regs[PICAInternalRegs::AttribFormatHigh]) << 32);
|
u64 vertexCfg = u64(regs[PICAInternalRegs::AttribFormatLow]) | (u64(regs[PICAInternalRegs::AttribFormatHigh]) << 32);
|
||||||
|
|
||||||
if constexpr (!indexed) {
|
if constexpr (!indexed) {
|
||||||
u32 offset = regs[PICAInternalRegs::VertexOffsetReg];
|
u32 offset = regs[PICAInternalRegs::VertexOffsetReg];
|
||||||
printf("PICA::DrawArrays(vertex count = %d, vertexOffset = %d)\n", vertexCount, offset);
|
log("PICA::DrawArrays(vertex count = %d, vertexOffset = %d)\n", vertexCount, offset);
|
||||||
} else {
|
} else {
|
||||||
Helpers::panic("[PICA] Indexed drawing");
|
Helpers::panic("[PICA] Indexed drawing");
|
||||||
}
|
}
|
||||||
|
@ -96,7 +101,6 @@ void GPU::drawArrays() {
|
||||||
for (component = 0; component < componentCount; component++) {
|
for (component = 0; component < componentCount; component++) {
|
||||||
float val = *ptr++;
|
float val = *ptr++;
|
||||||
attribute[component] = f24::fromFloat32(val);
|
attribute[component] = f24::fromFloat32(val);
|
||||||
printf("Component %d: %f\n", component, (double)val);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -111,11 +115,13 @@ void GPU::drawArrays() {
|
||||||
attribute[component] = (component == 3) ? f24::fromFloat32(1.0) : f24::fromFloat32(0.0);
|
attribute[component] = (component == 3) ? f24::fromFloat32(1.0) : f24::fromFloat32(0.0);
|
||||||
component++;
|
component++;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Attribute %d, type: %d, component count: %d\n", attrCount, attribType, componentCount);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shaderUnit.vs.run(); // Run vertex shader for vertex
|
shaderUnit.vs.run();
|
||||||
|
std::memcpy(&vertices[i].position, &shaderUnit.vs.outputs[0], sizeof(vec4f));
|
||||||
|
std::memcpy(&vertices[i].colour, &shaderUnit.vs.outputs[1], sizeof(vec4f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawVertices(OpenGL::Triangle, vertices, vertexCount);
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
using namespace Floats;
|
using namespace Floats;
|
||||||
|
|
||||||
u32 GPU::readReg(u32 address) {
|
u32 GPU::readReg(u32 address) {
|
||||||
printf("Ignoring read from GPU register %08X\n", address);
|
log("Ignoring read from GPU register %08X\n", address);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,13 +13,13 @@ void GPU::writeReg(u32 address, u32 value) {
|
||||||
const u32 index = (address - 0x1EF01000) / sizeof(u32);
|
const u32 index = (address - 0x1EF01000) / sizeof(u32);
|
||||||
writeInternalReg(index, value, 0xffffffff);
|
writeInternalReg(index, value, 0xffffffff);
|
||||||
} else {
|
} else {
|
||||||
printf("Ignoring write to external GPU register %08X. Value: %08X\n", address, value);
|
log("Ignoring write to external GPU register %08X. Value: %08X\n", address, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GPU::readInternalReg(u32 index) {
|
u32 GPU::readInternalReg(u32 index) {
|
||||||
if (index > regNum) {
|
if (index > regNum) {
|
||||||
printf("Tried to read invalid GPU register. Index: %X\n", index);
|
Helpers::panic("Tried to read invalid GPU register. Index: %X\n", index);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
printf("GPU: Wrote to unimplemented internal reg: %X, value: %08X\n", index, newValue);
|
log("GPU: Wrote to unimplemented internal reg: %X, value: %08X\n", index, newValue);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
160
src/core/PICA/renderer_opengl.cpp
Normal file
160
src/core/PICA/renderer_opengl.cpp
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
#include "PICA/gpu.hpp"
|
||||||
|
#include "opengl.hpp"
|
||||||
|
|
||||||
|
// This is all hacked up to display our first triangle
|
||||||
|
|
||||||
|
const char* vertexShader = R"(
|
||||||
|
#version 420 core
|
||||||
|
|
||||||
|
layout (location = 0) in vec4 coords;
|
||||||
|
layout (location = 1) in vec4 vertexColour;
|
||||||
|
|
||||||
|
out vec4 colour;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = coords;
|
||||||
|
colour = vertexColour;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
const char* fragmentShader = R"(
|
||||||
|
#version 420 core
|
||||||
|
|
||||||
|
in vec4 colour;
|
||||||
|
out vec4 fragColour;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
fragColour = colour;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
const char* displayVertexShader = R"(
|
||||||
|
#version 420 core
|
||||||
|
out vec2 UV;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
const vec4 positions[4] = vec4[](
|
||||||
|
vec4(-1.0, 1.0, 1.0, 1.0), // Top-left
|
||||||
|
vec4(1.0, 1.0, 1.0, 1.0), // Top-right
|
||||||
|
vec4(-1.0, -1.0, 1.0, 1.0), // Bottom-left
|
||||||
|
vec4(1.0, -1.0, 1.0, 1.0) // Bottom-right
|
||||||
|
);
|
||||||
|
|
||||||
|
// The 3DS displays both screens' framebuffer rotated 90 deg counter clockwise
|
||||||
|
// So we adjust our texcoords accordingly
|
||||||
|
const vec2 texcoords[4] = vec2[](
|
||||||
|
vec2(1.0, 1.0), // Top-right
|
||||||
|
vec2(1.0, 0.0), // Bottom-right
|
||||||
|
vec2(0.0, 1.0), // Top-left
|
||||||
|
vec2(0.0, 0.0) // Bottom-left
|
||||||
|
);
|
||||||
|
|
||||||
|
gl_Position = positions[gl_VertexID];
|
||||||
|
UV = texcoords[gl_VertexID];
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
const char* displayFragmentShader = R"(
|
||||||
|
#version 420 core
|
||||||
|
in vec2 UV;
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
uniform sampler2D u_texture; // TODO: Properly init this to 0 when I'm not lazy
|
||||||
|
void main() {
|
||||||
|
FragColor = texture(u_texture, UV);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
void GPU::initGraphicsContext() {
|
||||||
|
// Set up texture for top screen
|
||||||
|
fboTexture.create(400, 240, GL_RGBA8);
|
||||||
|
fboTexture.bind();
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
fbo.createWithDrawTexture(fboTexture);
|
||||||
|
fbo.bind(OpenGL::DrawFramebuffer);
|
||||||
|
|
||||||
|
OpenGL::setViewport(400, 240);
|
||||||
|
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
OpenGL::clearColor();
|
||||||
|
|
||||||
|
OpenGL::Shader vert(vertexShader, OpenGL::Vertex);
|
||||||
|
OpenGL::Shader frag(fragmentShader, OpenGL::Fragment);
|
||||||
|
triangleProgram.create({ vert, frag });
|
||||||
|
|
||||||
|
OpenGL::Shader vertDisplay(displayVertexShader, OpenGL::Vertex);
|
||||||
|
OpenGL::Shader fragDisplay(displayFragmentShader, OpenGL::Fragment);
|
||||||
|
displayProgram.create({ vertDisplay, fragDisplay });
|
||||||
|
|
||||||
|
vbo.createFixedSize(sizeof(Vertex) * vertexBufferSize, GL_STREAM_DRAW);
|
||||||
|
vbo.bind();
|
||||||
|
vao.create();
|
||||||
|
vao.bind();
|
||||||
|
|
||||||
|
// Position (x, y, z, w) attributes
|
||||||
|
vao.setAttributeFloat<float>(0, 4, sizeof(Vertex), offsetof(Vertex, position));
|
||||||
|
vao.enableAttribute(0);
|
||||||
|
// Colour attribute
|
||||||
|
vao.setAttributeFloat<float>(1, 4, sizeof(Vertex), offsetof(Vertex, colour));
|
||||||
|
vao.enableAttribute(1);
|
||||||
|
|
||||||
|
dummyVBO.create();
|
||||||
|
dummyVAO.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::getGraphicsContext() {
|
||||||
|
OpenGL::disableDepth();
|
||||||
|
OpenGL::disableScissor();
|
||||||
|
OpenGL::setViewport(400, 240);
|
||||||
|
fbo.bind(OpenGL::DrawAndReadFramebuffer);
|
||||||
|
|
||||||
|
vbo.bind();
|
||||||
|
vao.bind();
|
||||||
|
triangleProgram.use();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count) {
|
||||||
|
vbo.bufferVertsSub(vertices, count);
|
||||||
|
OpenGL::draw(primType, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 topScreenBuffer = 0x1f000000;
|
||||||
|
constexpr u32 bottomScreenBuffer = 0x1f300000;
|
||||||
|
|
||||||
|
// Quick hack to display top screen for now
|
||||||
|
void GPU::display() {
|
||||||
|
OpenGL::disableScissor();
|
||||||
|
OpenGL::bindScreenFramebuffer();
|
||||||
|
fboTexture.bind();
|
||||||
|
displayProgram.use();
|
||||||
|
|
||||||
|
dummyVBO.bind();
|
||||||
|
dummyVAO.bind();
|
||||||
|
OpenGL::setViewport(0, 240, 400, 240);
|
||||||
|
|
||||||
|
OpenGL::draw(OpenGL::TriangleStrip, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
||||||
|
log("GPU: Clear buffer\nStart: %08X End: %08X\nValue: %08X Control: %08X\n", startAddress, endAddress, value, control);
|
||||||
|
|
||||||
|
const float r = float((value >> 24) & 0xff) / 255.0;
|
||||||
|
const float g = float((value >> 16) & 0xff) / 255.0;
|
||||||
|
const float b = float((value >> 8) & 0xff) / 255.0;
|
||||||
|
const float a = float(value & 0xff) / 255.0;
|
||||||
|
|
||||||
|
if (startAddress == topScreenBuffer) {
|
||||||
|
log("GPU: Cleared top screen\n");
|
||||||
|
} else if (startAddress == bottomScreenBuffer) {
|
||||||
|
log("GPU: Tried to clear bottom screen\n");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
log("GPU: Clearing some unknown buffer\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGL::setClearColor(r, g, b, a);
|
||||||
|
OpenGL::clearColor();
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ void Kernel::arbitrateAddress() {
|
||||||
const s32 value = s32(regs[3]);
|
const s32 value = s32(regs[3]);
|
||||||
const s64 ns = s64(u64(regs[4]) | (u64(regs[5]) << 32));
|
const s64 ns = s64(u64(regs[4]) | (u64(regs[5]) << 32));
|
||||||
|
|
||||||
printf("ArbitrateAddress(Handle = %X, address = %08X, type = %s, value = %d, ns = %lld)\n", handle, address,
|
logSVC("ArbitrateAddress(Handle = %X, address = %08X, type = %s, value = %d, ns = %lld)\n", handle, address,
|
||||||
arbitrationTypeToString(type), value, ns);
|
arbitrationTypeToString(type), value, ns);
|
||||||
|
|
||||||
const auto arbiter = getObject(handle, KernelObjectType::AddressArbiter);
|
const auto arbiter = getObject(handle, KernelObjectType::AddressArbiter);
|
||||||
|
@ -68,7 +68,7 @@ void Kernel::arbitrateAddress() {
|
||||||
}
|
}
|
||||||
|
|
||||||
case ArbitrationType::Signal:
|
case ArbitrationType::Signal:
|
||||||
printf("Broken ArbitrateAddress (type == SIGNAL)\n");
|
logSVC("Broken ArbitrateAddress (type == SIGNAL)\n");
|
||||||
switchThread(0);
|
switchThread(0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ void Kernel::createEvent() {
|
||||||
if (resetType > 2)
|
if (resetType > 2)
|
||||||
Helpers::panic("Invalid reset type for event %d", resetType);
|
Helpers::panic("Invalid reset type for event %d", resetType);
|
||||||
|
|
||||||
printf("CreateEvent(handle pointer = %08X, resetType = %s)\n", outPointer, resetTypeToString(resetType));
|
logSVC("CreateEvent(handle pointer = %08X, resetType = %s)\n", outPointer, resetTypeToString(resetType));
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = SVCResult::Success;
|
||||||
regs[1] = makeEvent(static_cast<ResetType>(resetType));
|
regs[1] = makeEvent(static_cast<ResetType>(resetType));
|
||||||
|
@ -33,7 +33,7 @@ void Kernel::createEvent() {
|
||||||
void Kernel::clearEvent() {
|
void Kernel::clearEvent() {
|
||||||
const Handle handle = regs[0];
|
const Handle handle = regs[0];
|
||||||
const auto event = getObject(handle, KernelObjectType::Event);
|
const auto event = getObject(handle, KernelObjectType::Event);
|
||||||
printf("ClearEvent(event handle = %d)\n", handle);
|
logSVC("ClearEvent(event handle = %d)\n", handle);
|
||||||
|
|
||||||
if (event == nullptr) [[unlikely]] {
|
if (event == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = SVCResult::BadHandle;
|
||||||
|
@ -56,8 +56,6 @@ void Kernel::waitSynchronization1() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("WaitSynchronization1(handle = %X, ns = %lld) (STUBBED)\n", handle, ns);
|
logSVC("WaitSynchronization1(handle = %X, ns = %lld) (STUBBED)\n", handle, ns);
|
||||||
serviceManager.requestGPUInterrupt(GPUInterrupt::VBlank0);
|
|
||||||
serviceManager.requestGPUInterrupt(GPUInterrupt::VBlank1);
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = SVCResult::Success;
|
||||||
}
|
}
|
|
@ -118,13 +118,13 @@ u32 Kernel::getTLSPointer() {
|
||||||
|
|
||||||
// Result CloseHandle(Handle handle)
|
// Result CloseHandle(Handle handle)
|
||||||
void Kernel::svcCloseHandle() {
|
void Kernel::svcCloseHandle() {
|
||||||
printf("CloseHandle(handle = %d) (Unimplemented)\n", regs[0]);
|
logSVC("CloseHandle(handle = %d) (Unimplemented)\n", regs[0]);
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = SVCResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// u64 GetSystemTick()
|
// u64 GetSystemTick()
|
||||||
void Kernel::getSystemTick() {
|
void Kernel::getSystemTick() {
|
||||||
printf("GetSystemTick()\n");
|
logSVC("GetSystemTick()\n");
|
||||||
|
|
||||||
u64 ticks = cpu.getTicks();
|
u64 ticks = cpu.getTicks();
|
||||||
regs[0] = u32(ticks);
|
regs[0] = u32(ticks);
|
||||||
|
@ -138,7 +138,7 @@ void Kernel::outputDebugString() {
|
||||||
const u32 size = regs[1];
|
const u32 size = regs[1];
|
||||||
|
|
||||||
std::string message = mem.readString(pointer, size);
|
std::string message = mem.readString(pointer, size);
|
||||||
printf("[OutputDebugString] %s\n", message.c_str());
|
logSVC("[OutputDebugString] %s\n", message.c_str());
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = SVCResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ void Kernel::getProcessInfo() {
|
||||||
const auto pid = regs[1];
|
const auto pid = regs[1];
|
||||||
const auto type = regs[2];
|
const auto type = regs[2];
|
||||||
const auto process = getProcessFromPID(pid);
|
const auto process = getProcessFromPID(pid);
|
||||||
printf("GetProcessInfo(process: %s, type = %d)\n", getProcessName(pid).c_str(), type);
|
logSVC("GetProcessInfo(process: %s, type = %d)\n", getProcessName(pid).c_str(), type);
|
||||||
|
|
||||||
if (process == nullptr) [[unlikely]] {
|
if (process == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = SVCResult::BadHandle;
|
||||||
|
@ -170,7 +170,7 @@ void Kernel::getProcessInfo() {
|
||||||
// Result GetThreadId(u32* threadId, Handle thread)
|
// Result GetThreadId(u32* threadId, Handle thread)
|
||||||
void Kernel::duplicateHandle() {
|
void Kernel::duplicateHandle() {
|
||||||
Handle original = regs[1];
|
Handle original = regs[1];
|
||||||
printf("DuplicateHandle(handle = %X)\n", original);
|
logSVC("DuplicateHandle(handle = %X)\n", original);
|
||||||
|
|
||||||
if (original == KernelHandles::CurrentThread) {
|
if (original == KernelHandles::CurrentThread) {
|
||||||
printf("[Warning] Duplicated current thread. This might be horribly broken!\n");
|
printf("[Warning] Duplicated current thread. This might be horribly broken!\n");
|
||||||
|
|
|
@ -49,7 +49,7 @@ void Kernel::controlMemory() {
|
||||||
Helpers::panic("ControlMemory: Unaligned parameters\nAddr0: %08X\nAddr1: %08X\nSize: %08X", addr0, addr1, size);
|
Helpers::panic("ControlMemory: Unaligned parameters\nAddr0: %08X\nAddr1: %08X\nSize: %08X", addr0, addr1, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("ControlMemory(addr0 = %08X, addr1 = %08X, size = %08X, operation = %X (%c%c%c)%s\n",
|
logSVC("ControlMemory(addr0 = %08X, addr1 = %08X, size = %08X, operation = %X (%c%c%c)%s\n",
|
||||||
addr0, addr1, size, operation, r ? 'r' : '-', w ? 'w' : '-', x ? 'x' : '-', linear ? ", linear" : ""
|
addr0, addr1, size, operation, r ? 'r' : '-', w ? 'w' : '-', x ? 'x' : '-', linear ? ", linear" : ""
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ void Kernel::queryMemory() {
|
||||||
const u32 pageInfo = regs[1];
|
const u32 pageInfo = regs[1];
|
||||||
const u32 addr = regs[2];
|
const u32 addr = regs[2];
|
||||||
|
|
||||||
printf("QueryMemory(mem info pointer = %08X, page info pointer = %08X, addr = %08X)\n", memInfo, pageInfo, addr);
|
logSVC("QueryMemory(mem info pointer = %08X, page info pointer = %08X, addr = %08X)\n", memInfo, pageInfo, addr);
|
||||||
|
|
||||||
const auto info = mem.queryMemory(addr);
|
const auto info = mem.queryMemory(addr);
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = SVCResult::Success;
|
||||||
|
@ -92,7 +92,7 @@ void Kernel::mapMemoryBlock() {
|
||||||
const u32 addr = regs[1];
|
const u32 addr = regs[1];
|
||||||
const u32 myPerms = regs[2];
|
const u32 myPerms = regs[2];
|
||||||
const u32 otherPerms = regs[3];
|
const u32 otherPerms = regs[3];
|
||||||
printf("MapMemoryBlock(block = %d, addr = %08X, myPerms = %X, otherPerms = %X\n", block, addr, myPerms, otherPerms);
|
logSVC("MapMemoryBlock(block = %d, addr = %08X, myPerms = %X, otherPerms = %X\n", block, addr, myPerms, otherPerms);
|
||||||
|
|
||||||
if (!isAligned(addr)) [[unlikely]] {
|
if (!isAligned(addr)) [[unlikely]] {
|
||||||
Helpers::panic("MapMemoryBlock: Unaligned address");
|
Helpers::panic("MapMemoryBlock: Unaligned address");
|
||||||
|
|
|
@ -56,7 +56,7 @@ void Kernel::connectToPort() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle portHandle = optionalHandle.value();
|
Handle portHandle = optionalHandle.value();
|
||||||
printf("ConnectToPort(handle pointer = %X, port = \"%s\")\n", handlePointer, port.c_str());
|
logSVC("ConnectToPort(handle pointer = %X, port = \"%s\")\n", handlePointer, port.c_str());
|
||||||
|
|
||||||
const auto portData = objects[portHandle].getData<Port>();
|
const auto portData = objects[portHandle].getData<Port>();
|
||||||
if (!portData->isPublic) {
|
if (!portData->isPublic) {
|
||||||
|
@ -76,7 +76,7 @@ void Kernel::sendSyncRequest() {
|
||||||
const auto handle = regs[0];
|
const auto handle = regs[0];
|
||||||
u32 messagePointer = getTLSPointer() + 0x80; // The message is stored starting at TLS+0x80
|
u32 messagePointer = getTLSPointer() + 0x80; // The message is stored starting at TLS+0x80
|
||||||
|
|
||||||
printf("SendSyncRequest(session handle = %X)\n", handle);
|
logSVC("SendSyncRequest(session handle = %X)\n", handle);
|
||||||
|
|
||||||
// The sync request is being sent at a service rather than whatever port, so have the service manager intercept it
|
// The sync request is being sent at a service rather than whatever port, so have the service manager intercept it
|
||||||
if (KernelHandles::isServiceHandle(handle)) {
|
if (KernelHandles::isServiceHandle(handle)) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ void Kernel::getResourceLimit() {
|
||||||
const auto handlePointer = regs[0];
|
const auto handlePointer = regs[0];
|
||||||
const auto pid = regs[1];
|
const auto pid = regs[1];
|
||||||
const auto process = getProcessFromPID(pid);
|
const auto process = getProcessFromPID(pid);
|
||||||
printf("GetResourceLimit (handle pointer = %08X, process: %s)\n", handlePointer, getProcessName(pid).c_str());
|
logSVC("GetResourceLimit (handle pointer = %08X, process: %s)\n", handlePointer, getProcessName(pid).c_str());
|
||||||
|
|
||||||
if (process == nullptr) [[unlikely]] {
|
if (process == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = SVCResult::BadHandle;
|
||||||
|
@ -33,7 +33,7 @@ void Kernel::getResourceLimitLimitValues() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("GetResourceLimitLimitValues(values = %08X, handle = %X, names = %08X, count = %d)\n", values, resourceLimit, names, count);
|
logSVC("GetResourceLimitLimitValues(values = %08X, handle = %X, names = %08X, count = %d)\n", values, resourceLimit, names, count);
|
||||||
// printf("[Warning] We do not currently support any resource maximum aside from the application ones");
|
// printf("[Warning] We do not currently support any resource maximum aside from the application ones");
|
||||||
while (count != 0) {
|
while (count != 0) {
|
||||||
const u32 name = mem.read32(names);
|
const u32 name = mem.read32(names);
|
||||||
|
@ -62,7 +62,7 @@ void Kernel::getResourceLimitCurrentValues() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("GetResourceLimitCurrentValues(values = %08X, handle = %X, names = %08X, count = %d)\n", values, resourceLimit, names, count);
|
logSVC("GetResourceLimitCurrentValues(values = %08X, handle = %X, names = %08X, count = %d)\n", values, resourceLimit, names, count);
|
||||||
while (count != 0) {
|
while (count != 0) {
|
||||||
const u32 name = mem.read32(names);
|
const u32 name = mem.read32(names);
|
||||||
// TODO: Unsure if this is supposed to be s32 or u32. Shouldn't matter as the kernel can't create so many resources
|
// TODO: Unsure if this is supposed to be s32 or u32. Shouldn't matter as the kernel can't create so many resources
|
||||||
|
|
|
@ -75,7 +75,7 @@ void Kernel::createThread() {
|
||||||
u32 initialSP = regs[3] & ~7; // SP is force-aligned to 8 bytes
|
u32 initialSP = regs[3] & ~7; // SP is force-aligned to 8 bytes
|
||||||
s32 id = static_cast<s32>(regs[4]);
|
s32 id = static_cast<s32>(regs[4]);
|
||||||
|
|
||||||
printf("CreateThread(entry = %08X, stacktop = %08X, priority = %X, processor ID = %d)\n", entrypoint,
|
logSVC("CreateThread(entry = %08X, stacktop = %08X, priority = %X, processor ID = %d)\n", entrypoint,
|
||||||
initialSP, priority, id);
|
initialSP, priority, id);
|
||||||
|
|
||||||
if (priority > 0x3F) [[unlikely]] {
|
if (priority > 0x3F) [[unlikely]] {
|
||||||
|
@ -99,7 +99,7 @@ void Kernel::sleepThreadOnArbiter(u32 waitingAddress) {
|
||||||
|
|
||||||
void Kernel::getThreadID() {
|
void Kernel::getThreadID() {
|
||||||
Handle handle = regs[1];
|
Handle handle = regs[1];
|
||||||
printf("GetThreadID(handle = %X)\n", handle);
|
logSVC("GetThreadID(handle = %X)\n", handle);
|
||||||
|
|
||||||
if (handle == KernelHandles::CurrentThread) {
|
if (handle == KernelHandles::CurrentThread) {
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = SVCResult::Success;
|
||||||
|
|
|
@ -24,7 +24,7 @@ void APTService::handleSyncRequest(u32 messagePointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void APTService::getLockHandle(u32 messagePointer) {
|
void APTService::getLockHandle(u32 messagePointer) {
|
||||||
printf("APT::GetLockHandle (Failure)\n");
|
log("APT::GetLockHandle (Failure)\n");
|
||||||
mem.write32(messagePointer + 4, Result::Failure); // Result code
|
mem.write32(messagePointer + 4, Result::Failure); // Result code
|
||||||
mem.write32(messagePointer + 16, 0); // Translation descriptor
|
mem.write32(messagePointer + 16, 0); // Translation descriptor
|
||||||
}
|
}
|
|
@ -26,11 +26,11 @@ void FSService::handleSyncRequest(u32 messagePointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSService::initialize(u32 messagePointer) {
|
void FSService::initialize(u32 messagePointer) {
|
||||||
printf("FS::Initialize (failure)\n");
|
log("FS::Initialize (failure)\n");
|
||||||
mem.write32(messagePointer + 4, Result::Failure);
|
mem.write32(messagePointer + 4, Result::Failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSService::openArchive(u32 messagePointer) {
|
void FSService::openArchive(u32 messagePointer) {
|
||||||
printf("FS::OpenArchive (failure)\n");
|
log("FS::OpenArchive (failure)\n");
|
||||||
mem.write32(messagePointer + 4, Result::Failure);
|
mem.write32(messagePointer + 4, Result::Failure);
|
||||||
}
|
}
|
|
@ -14,10 +14,11 @@ namespace ServiceCommands {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commands written to shared memory and processed by TriggerCmdReqQueue
|
// Commands written to shared memory and processed by TriggerCmdReqQueue
|
||||||
namespace GPUCommands {
|
namespace GXCommands {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
ProcessCommandList = 1,
|
ProcessCommandList = 1,
|
||||||
MemoryFill = 2
|
MemoryFill = 2,
|
||||||
|
TriggerDisplayTransfer = 3
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@ void GPUService::handleSyncRequest(u32 messagePointer) {
|
||||||
void GPUService::acquireRight(u32 messagePointer) {
|
void GPUService::acquireRight(u32 messagePointer) {
|
||||||
const u32 flag = mem.read32(messagePointer + 4);
|
const u32 flag = mem.read32(messagePointer + 4);
|
||||||
const u32 pid = mem.read32(messagePointer + 12);
|
const u32 pid = mem.read32(messagePointer + 12);
|
||||||
printf("GSP::GPU::AcquireRight (flag = %X, pid = %X)\n", flag, pid);
|
log("GSP::GPU::AcquireRight (flag = %X, pid = %X)\n", flag, pid);
|
||||||
|
|
||||||
if (flag != 0) {
|
if (flag != 0) {
|
||||||
Helpers::panic("GSP::GPU::acquireRight with flag != 0 needs to perform additional initialization");
|
Helpers::panic("GSP::GPU::acquireRight with flag != 0 needs to perform additional initialization");
|
||||||
|
@ -76,7 +77,7 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) {
|
||||||
|
|
||||||
const u32 flags = mem.read32(messagePointer + 4);
|
const u32 flags = mem.read32(messagePointer + 4);
|
||||||
const u32 eventHandle = mem.read32(messagePointer + 12);
|
const u32 eventHandle = mem.read32(messagePointer + 12);
|
||||||
printf("GSP::GPU::RegisterInterruptRelayQueue (flags = %X, event handle = %X)\n", flags, eventHandle);
|
log("GSP::GPU::RegisterInterruptRelayQueue (flags = %X, event handle = %X)\n", flags, eventHandle);
|
||||||
|
|
||||||
mem.write32(messagePointer + 4, Result::SuccessRegisterIRQ);
|
mem.write32(messagePointer + 4, Result::SuccessRegisterIRQ);
|
||||||
mem.write32(messagePointer + 8, 0); // TODO: GSP module thread index
|
mem.write32(messagePointer + 8, 0); // TODO: GSP module thread index
|
||||||
|
@ -101,7 +102,7 @@ void GPUService::writeHwRegs(u32 messagePointer) {
|
||||||
u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned
|
u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned
|
||||||
const u32 size = mem.read32(messagePointer + 8); // Size in bytes
|
const u32 size = mem.read32(messagePointer + 8); // Size in bytes
|
||||||
u32 dataPointer = mem.read32(messagePointer + 16);
|
u32 dataPointer = mem.read32(messagePointer + 16);
|
||||||
printf("GSP::GPU::writeHwRegs (GPU address = %08X, size = %X, data address = %08X)\n", ioAddr, size, dataPointer);
|
log("GSP::GPU::writeHwRegs (GPU address = %08X, size = %X, data address = %08X)\n", ioAddr, size, dataPointer);
|
||||||
|
|
||||||
// Check for alignment
|
// Check for alignment
|
||||||
if ((size & 3) || (ioAddr & 3) || (dataPointer & 3)) {
|
if ((size & 3) || (ioAddr & 3) || (dataPointer & 3)) {
|
||||||
|
@ -135,7 +136,7 @@ void GPUService::writeHwRegsWithMask(u32 messagePointer) {
|
||||||
u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer
|
u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer
|
||||||
u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer
|
u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer
|
||||||
|
|
||||||
printf("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n",
|
log("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n",
|
||||||
ioAddr, size, dataPointer, maskPointer);
|
ioAddr, size, dataPointer, maskPointer);
|
||||||
|
|
||||||
// Check for alignment
|
// Check for alignment
|
||||||
|
@ -172,14 +173,14 @@ void GPUService::flushDataCache(u32 messagePointer) {
|
||||||
u32 address = mem.read32(messagePointer + 4);
|
u32 address = mem.read32(messagePointer + 4);
|
||||||
u32 size = mem.read32(messagePointer + 8);
|
u32 size = mem.read32(messagePointer + 8);
|
||||||
u32 processHandle = handle = mem.read32(messagePointer + 16);
|
u32 processHandle = handle = mem.read32(messagePointer + 16);
|
||||||
printf("GSP::GPU::FlushDataCache(address = %08X, size = %X, process = %X\n", address, size, processHandle);
|
log("GSP::GPU::FlushDataCache(address = %08X, size = %X, process = %X\n", address, size, processHandle);
|
||||||
|
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUService::setLCDForceBlack(u32 messagePointer) {
|
void GPUService::setLCDForceBlack(u32 messagePointer) {
|
||||||
u32 flag = mem.read32(messagePointer + 4);
|
u32 flag = mem.read32(messagePointer + 4);
|
||||||
printf("GSP::GPU::SetLCDForceBlank(flag = %d)\n", flag);
|
log("GSP::GPU::SetLCDForceBlank(flag = %d)\n", flag);
|
||||||
|
|
||||||
if (flag != 0) {
|
if (flag != 0) {
|
||||||
printf("Filled both LCDs with black\n");
|
printf("Filled both LCDs with black\n");
|
||||||
|
@ -193,9 +194,6 @@ void GPUService::triggerCmdReqQueue(u32 messagePointer) {
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
void GPUService::processCommandBuffer() {
|
void GPUService::processCommandBuffer() {
|
||||||
if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet
|
if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet
|
||||||
return;
|
return;
|
||||||
|
@ -208,14 +206,14 @@ void GPUService::processCommandBuffer() {
|
||||||
// Commands start at byte 0x20 of the command buffer, each being 0x20 bytes long
|
// Commands start at byte 0x20 of the command buffer, each being 0x20 bytes long
|
||||||
u32* cmd = reinterpret_cast<u32*>(&cmdBuffer[0x20]);
|
u32* cmd = reinterpret_cast<u32*>(&cmdBuffer[0x20]);
|
||||||
|
|
||||||
printf("Processing %d GPU commands\n", commandsLeft);
|
log("Processing %d GPU commands\n", commandsLeft);
|
||||||
std::this_thread::sleep_for(1000ms);
|
|
||||||
|
|
||||||
while (commandsLeft != 0) {
|
while (commandsLeft != 0) {
|
||||||
u32 cmdID = cmd[0] & 0xff;
|
u32 cmdID = cmd[0] & 0xff;
|
||||||
switch (cmdID) {
|
switch (cmdID) {
|
||||||
case GPUCommands::ProcessCommandList: processCommandList(cmd); break;
|
case GXCommands::ProcessCommandList: processCommandList(cmd); break;
|
||||||
case GPUCommands::MemoryFill: memoryFill(cmd); break;
|
case GXCommands::MemoryFill: memoryFill(cmd); break;
|
||||||
|
case GXCommands::TriggerDisplayTransfer: triggerDisplayTransfer(cmd); break;
|
||||||
default: Helpers::panic("GSP::GPU::ProcessCommands: Unknown cmd ID %d", cmdID);
|
default: Helpers::panic("GSP::GPU::ProcessCommands: Unknown cmd ID %d", cmdID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +247,11 @@ void GPUService::memoryFill(u32* cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GPUService::triggerDisplayTransfer(u32* cmd) {
|
||||||
|
log("GSP::GPU::TriggerDisplayTransfer (Stubbed)\n");
|
||||||
|
requestInterrupt(GPUInterrupt::PPF); // Send "Display transfer finished" interrupt
|
||||||
|
}
|
||||||
|
|
||||||
// Actually send command list (aka display list) to GPU
|
// Actually send command list (aka display list) to GPU
|
||||||
void GPUService::processCommandList(u32* cmd) {
|
void GPUService::processCommandList(u32* cmd) {
|
||||||
u32 address = cmd[1] & ~7; // Buffer address
|
u32 address = cmd[1] & ~7; // Buffer address
|
||||||
|
@ -305,5 +308,6 @@ void GPUService::processCommandList(u32* cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("GPU::GSP::processCommandList. Address: %08X, size in bytes: %08X\n", address, size);
|
log("GPU::GSP::processCommandList. Address: %08X, size in bytes: %08X\n", address, size);
|
||||||
|
requestInterrupt(GPUInterrupt::P3D); // Send an IRQ when command list processing is over
|
||||||
}
|
}
|
|
@ -24,7 +24,7 @@ void HIDService::handleSyncRequest(u32 messagePointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HIDService::getIPCHandles(u32 messagePointer) {
|
void HIDService::getIPCHandles(u32 messagePointer) {
|
||||||
printf("HID::GetIPCHandles (Failure)\n");
|
log("HID::GetIPCHandles (Failure)\n");
|
||||||
mem.write32(messagePointer + 4, Result::Failure); // Result code
|
mem.write32(messagePointer + 4, Result::Failure); // Result code
|
||||||
mem.write32(messagePointer + 8, 0x14000000); // Translation descriptor
|
mem.write32(messagePointer + 8, 0x14000000); // Translation descriptor
|
||||||
}
|
}
|
|
@ -25,11 +25,11 @@ void NDMService::handleSyncRequest(u32 messagePointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NDMService::overrideDefaultDaemons(u32 messagePointer) {
|
void NDMService::overrideDefaultDaemons(u32 messagePointer) {
|
||||||
printf("NDM::OverrideDefaultDaemons(stubbed)\n");
|
log("NDM::OverrideDefaultDaemons(stubbed)\n");
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NDMService::suspendDaemons(u32 messagePointer) {
|
void NDMService::suspendDaemons(u32 messagePointer) {
|
||||||
printf("NDM::SuspendDaemons(stubbed)\n");
|
log("NDM::SuspendDaemons(stubbed)\n");
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
|
@ -54,7 +54,7 @@ void ServiceManager::handleSyncRequest(u32 messagePointer) {
|
||||||
|
|
||||||
// https://www.3dbrew.org/wiki/SRV:RegisterClient
|
// https://www.3dbrew.org/wiki/SRV:RegisterClient
|
||||||
void ServiceManager::registerClient(u32 messagePointer) {
|
void ServiceManager::registerClient(u32 messagePointer) {
|
||||||
printf("srv::registerClient (Stubbed)\n");
|
log("srv::registerClient (Stubbed)\n");
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ void ServiceManager::getServiceHandle(u32 messagePointer) {
|
||||||
u32 handle = 0;
|
u32 handle = 0;
|
||||||
|
|
||||||
std::string service = mem.readString(messagePointer + 4, 8);
|
std::string service = mem.readString(messagePointer + 4, 8);
|
||||||
printf("srv::getServiceHandle (Service: %s, nameLength: %d, flags: %d)\n", service.c_str(), nameLength, flags);
|
log("srv::getServiceHandle (Service: %s, nameLength: %d, flags: %d)\n", service.c_str(), nameLength, flags);
|
||||||
|
|
||||||
if (service == "APT:S") {
|
if (service == "APT:S") {
|
||||||
handle = KernelHandles::APT;
|
handle = KernelHandles::APT;
|
||||||
|
@ -90,7 +90,7 @@ void ServiceManager::getServiceHandle(u32 messagePointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServiceManager::enableNotification(u32 messagePointer) {
|
void ServiceManager::enableNotification(u32 messagePointer) {
|
||||||
printf("srv::EnableNotification()\n");
|
log("srv::EnableNotification()\n");
|
||||||
|
|
||||||
mem.write32(messagePointer + 4, Result::Success); // Result code
|
mem.write32(messagePointer + 4, Result::Success); // Result code
|
||||||
mem.write32(messagePointer + 8, 0); // Translation descriptor
|
mem.write32(messagePointer + 8, 0); // Translation descriptor
|
||||||
|
|
|
@ -23,20 +23,29 @@ void Emulator::render() {
|
||||||
|
|
||||||
void Emulator::run() {
|
void Emulator::run() {
|
||||||
while (window.isOpen()) {
|
while (window.isOpen()) {
|
||||||
runFrame();
|
// Clear window to black
|
||||||
OpenGL::setClearColor(1.0, 0.0, 0.0, 1.0);
|
OpenGL::bindScreenFramebuffer();
|
||||||
OpenGL::setViewport(400, 240 * 2);
|
|
||||||
OpenGL::disableScissor();
|
OpenGL::disableScissor();
|
||||||
|
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
OpenGL::clearColor();
|
OpenGL::clearColor();
|
||||||
|
|
||||||
|
gpu.getGraphicsContext(); // Give the GPU a rendering context
|
||||||
|
runFrame(); // Run 1 frame of instructions
|
||||||
|
gpu.display();
|
||||||
|
|
||||||
|
// Send VBlank interrupts
|
||||||
|
kernel.sendGPUInterrupt(GPUInterrupt::VBlank0);
|
||||||
|
kernel.sendGPUInterrupt(GPUInterrupt::VBlank1);
|
||||||
|
window.display();
|
||||||
|
|
||||||
sf::Event event;
|
sf::Event event;
|
||||||
while (window.pollEvent(event)) {
|
while (window.pollEvent(event)) {
|
||||||
if (event.type == sf::Event::Closed) {
|
if (event.type == sf::Event::Closed) {
|
||||||
Helpers::panic("Bye :(");
|
printf("Bye :(\n");
|
||||||
|
window.close();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.display();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ int main (int argc, char *argv[]) {
|
||||||
Helpers::panic("Failed to initialize OpenGL");
|
Helpers::panic("Failed to initialize OpenGL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emu.initGraphicsContext();
|
||||||
|
|
||||||
auto elfPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "SimplerTri.elf");
|
auto elfPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "SimplerTri.elf");
|
||||||
if (!emu.loadELF(elfPath)) {
|
if (!emu.loadELF(elfPath)) {
|
||||||
// For some reason just .c_str() doesn't show the proper path
|
// For some reason just .c_str() doesn't show the proper path
|
||||||
|
|
Loading…
Add table
Reference in a new issue