Merge remote-tracking branch 'origin/GamingProcessingUnit' into dynapica

This commit is contained in:
wheremyfoodat 2023-06-27 18:53:59 +03:00
commit 02d07f29d7
106 changed files with 23630 additions and 11117 deletions

View file

@ -50,7 +50,7 @@ class GPU {
};
u64 getVertexShaderInputConfig() {
return u64(regs[PICAInternalRegs::VertexShaderInputCfgLow]) | (u64(regs[PICAInternalRegs::VertexShaderInputCfgHigh]) << 32);
return u64(regs[PICA::InternalRegs::VertexShaderInputCfgLow]) | (u64(regs[PICA::InternalRegs::VertexShaderInputCfgHigh]) << 32);
}
std::array<AttribInfo, maxAttribCount> attributeInfo; // Info for each of the 12 attributes

View file

@ -1,129 +1,237 @@
#pragma once
#include "helpers.hpp"
namespace PICAInternalRegs {
enum : u32 {
// Rasterizer registers
ViewportWidth = 0x41,
ViewportInvw = 0x42,
ViewportHeight = 0x43,
ViewportInvh = 0x44,
namespace PICA {
namespace InternalRegs {
enum : u32 {
// Rasterizer registers
ViewportWidth = 0x41,
ViewportInvw = 0x42,
ViewportHeight = 0x43,
ViewportInvh = 0x44,
DepthScale = 0x4D,
DepthOffset = 0x4E,
ShaderOutputCount = 0x4F,
DepthScale = 0x4D,
DepthOffset = 0x4E,
ShaderOutputCount = 0x4F,
ShaderOutmap0 = 0x50,
DepthmapEnable = 0x6D,
TexUnitCfg = 0x80,
DepthmapEnable = 0x6D,
// Framebuffer registers
ColourOperation = 0x100,
BlendFunc = 0x101,
BlendColour = 0x103,
AlphaTestConfig = 0x104,
DepthAndColorMask = 0x107,
DepthBufferFormat = 0x116,
ColourBufferFormat = 0x117,
DepthBufferLoc = 0x11C,
ColourBufferLoc = 0x11D,
FramebufferSize = 0x11E,
// Texture registers
TexUnitCfg = 0x80,
Tex0BorderColor = 0x81,
Tex1BorderColor = 0x91,
Tex2BorderColor = 0x99,
TexEnv0Source = 0xC0,
TexEnv1Source = 0xC8,
TexEnv2Source = 0xD0,
TexEnv3Source = 0xD8,
TexEnvUpdateBuffer = 0xE0,
TexEnv4Source = 0xF0,
TexEnv5Source = 0xF8,
TexEnvBufferColor = 0xFD,
// Geometry pipeline registers
VertexAttribLoc = 0x200,
AttribFormatLow = 0x201,
AttribFormatHigh = 0x202,
IndexBufferConfig = 0x227,
VertexCountReg = 0x228,
VertexOffsetReg = 0x22A,
SignalDrawArrays = 0x22E,
SignalDrawElements = 0x22F,
// Framebuffer registers
ColourOperation = 0x100,
BlendFunc = 0x101,
BlendColour = 0x103,
AlphaTestConfig = 0x104,
DepthAndColorMask = 0x107,
DepthBufferFormat = 0x116,
ColourBufferFormat = 0x117,
DepthBufferLoc = 0x11C,
ColourBufferLoc = 0x11D,
FramebufferSize = 0x11E,
Attrib0Offset = 0x203,
Attrib1Offset = 0x206,
Attrib2Offset = 0x209,
Attrib3Offset = 0x20C,
Attrib4Offset = 0x20F,
Attrib5Offset = 0x212,
Attrib6Offset = 0x215,
Attrib7Offset = 0x218,
Attrib8Offset = 0x21B,
Attrib9Offset = 0x21E,
Attrib10Offset = 0x221,
Attrib11Offset = 0x224,
// Geometry pipeline registers
VertexAttribLoc = 0x200,
AttribFormatLow = 0x201,
AttribFormatHigh = 0x202,
IndexBufferConfig = 0x227,
VertexCountReg = 0x228,
VertexOffsetReg = 0x22A,
SignalDrawArrays = 0x22E,
SignalDrawElements = 0x22F,
Attrib0Config2 = 0x205,
Attrib1Config2 = 0x208,
Attrib2Config2 = 0x20B,
Attrib3Config2 = 0x20E,
Attrib4Config2 = 0x211,
Attrib5Config2 = 0x214,
Attrib6Config2 = 0x217,
Attrib7Config2 = 0x21A,
Attrib8Config2 = 0x21D,
Attrib9Config2 = 0x220,
Attrib10Config2 = 0x223,
Attrib11Config2 = 0x226,
Attrib0Offset = 0x203,
Attrib1Offset = 0x206,
Attrib2Offset = 0x209,
Attrib3Offset = 0x20C,
Attrib4Offset = 0x20F,
Attrib5Offset = 0x212,
Attrib6Offset = 0x215,
Attrib7Offset = 0x218,
Attrib8Offset = 0x21B,
Attrib9Offset = 0x21E,
Attrib10Offset = 0x221,
Attrib11Offset = 0x224,
AttribInfoStart = Attrib0Offset,
AttribInfoEnd = Attrib11Config2,
Attrib0Config2 = 0x205,
Attrib1Config2 = 0x208,
Attrib2Config2 = 0x20B,
Attrib3Config2 = 0x20E,
Attrib4Config2 = 0x211,
Attrib5Config2 = 0x214,
Attrib6Config2 = 0x217,
Attrib7Config2 = 0x21A,
Attrib8Config2 = 0x21D,
Attrib9Config2 = 0x220,
Attrib10Config2 = 0x223,
Attrib11Config2 = 0x226,
// Fixed attribute registers
FixedAttribIndex = 0x232,
FixedAttribData0 = 0x233,
FixedAttribData1 = 0x234,
FixedAttribData2 = 0x235,
// Command processor registers
CmdBufSize0 = 0x238,
CmdBufSize1 = 0x239,
CmdBufAddr0 = 0x23A,
CmdBufAddr1 = 0x23B,
CmdBufTrigger0 = 0x23C,
CmdBufTrigger1 = 0x23D,
AttribInfoStart = Attrib0Offset,
AttribInfoEnd = Attrib11Config2,
PrimitiveConfig = 0x25E,
PrimitiveRestart = 0x25F,
// Fixed attribute registers
FixedAttribIndex = 0x232,
FixedAttribData0 = 0x233,
FixedAttribData1 = 0x234,
FixedAttribData2 = 0x235,
// Vertex shader registers
VertexShaderAttrNum = 0x242,
VertexBoolUniform = 0x2B0,
VertexIntUniform0 = 0x2B1,
VertexIntUniform1 = 0x2B2,
VertexIntUniform2 = 0x2B3,
VertexIntUniform3 = 0x2B4,
// Command processor registers
CmdBufSize0 = 0x238,
CmdBufSize1 = 0x239,
CmdBufAddr0 = 0x23A,
CmdBufAddr1 = 0x23B,
CmdBufTrigger0 = 0x23C,
CmdBufTrigger1 = 0x23D,
VertexShaderEntrypoint = 0x2BA,
VertexShaderTransferEnd = 0x2BF,
VertexFloatUniformIndex = 0x2C0,
VertexFloatUniformData0 = 0x2C1,
VertexFloatUniformData1 = 0x2C2,
VertexFloatUniformData2 = 0x2C3,
VertexFloatUniformData3 = 0x2C4,
VertexFloatUniformData4 = 0x2C5,
VertexFloatUniformData5 = 0x2C6,
VertexFloatUniformData6 = 0x2C7,
VertexFloatUniformData7 = 0x2C8,
PrimitiveConfig = 0x25E,
PrimitiveRestart = 0x25F,
VertexShaderInputBufferCfg = 0x2B9,
VertexShaderInputCfgLow = 0x2BB,
VertexShaderInputCfgHigh = 0x2BC,
// Vertex shader registers
VertexShaderAttrNum = 0x242,
VertexBoolUniform = 0x2B0,
VertexIntUniform0 = 0x2B1,
VertexIntUniform1 = 0x2B2,
VertexIntUniform2 = 0x2B3,
VertexIntUniform3 = 0x2B4,
VertexShaderTransferIndex = 0x2CB,
VertexShaderData0 = 0x2CC,
VertexShaderData1 = 0x2CD,
VertexShaderData2 = 0x2CE,
VertexShaderData3 = 0x2CF,
VertexShaderData4 = 0x2D0,
VertexShaderData5 = 0x2D1,
VertexShaderData6 = 0x2D2,
VertexShaderData7 = 0x2D3,
VertexShaderOpDescriptorIndex = 0x2D5,
VertexShaderOpDescriptorData0 = 0x2D6,
VertexShaderOpDescriptorData1 = 0x2D7,
VertexShaderOpDescriptorData2 = 0x2D8,
VertexShaderOpDescriptorData3 = 0x2D9,
VertexShaderOpDescriptorData4 = 0x2DA,
VertexShaderOpDescriptorData5 = 0x2DB,
VertexShaderOpDescriptorData6 = 0x2DC,
VertexShaderOpDescriptorData7 = 0x2DD,
VertexShaderEntrypoint = 0x2BA,
VertexShaderTransferEnd = 0x2BF,
VertexFloatUniformIndex = 0x2C0,
VertexFloatUniformData0 = 0x2C1,
VertexFloatUniformData1 = 0x2C2,
VertexFloatUniformData2 = 0x2C3,
VertexFloatUniformData3 = 0x2C4,
VertexFloatUniformData4 = 0x2C5,
VertexFloatUniformData5 = 0x2C6,
VertexFloatUniformData6 = 0x2C7,
VertexFloatUniformData7 = 0x2C8,
VertexShaderInputBufferCfg = 0x2B9,
VertexShaderInputCfgLow = 0x2BB,
VertexShaderInputCfgHigh = 0x2BC,
VertexShaderTransferIndex = 0x2CB,
VertexShaderData0 = 0x2CC,
VertexShaderData1 = 0x2CD,
VertexShaderData2 = 0x2CE,
VertexShaderData3 = 0x2CF,
VertexShaderData4 = 0x2D0,
VertexShaderData5 = 0x2D1,
VertexShaderData6 = 0x2D2,
VertexShaderData7 = 0x2D3,
VertexShaderOpDescriptorIndex = 0x2D5,
VertexShaderOpDescriptorData0 = 0x2D6,
VertexShaderOpDescriptorData1 = 0x2D7,
VertexShaderOpDescriptorData2 = 0x2D8,
VertexShaderOpDescriptorData3 = 0x2D9,
VertexShaderOpDescriptorData4 = 0x2DA,
VertexShaderOpDescriptorData5 = 0x2DB,
VertexShaderOpDescriptorData6 = 0x2DC,
VertexShaderOpDescriptorData7 = 0x2DD,
};
}
enum class TextureFmt : u32 {
RGBA8 = 0x0,
RGB8 = 0x1,
RGBA5551 = 0x2,
RGB565 = 0x3,
RGBA4 = 0x4,
IA8 = 0x5,
RG8 = 0x6,
I8 = 0x7,
A8 = 0x8,
IA4 = 0x9,
I4 = 0xA,
A4 = 0xB,
ETC1 = 0xC,
ETC1A4 = 0xD,
};
}
enum class ColorFmt : u32 {
RGBA8 = 0x0,
RGB8 = 0x1,
RGBA5551 = 0x2,
RGB565 = 0x3,
RGBA4 = 0x4,
};
enum class DepthFmt : u32 {
Depth16 = 0,
Unknown1 = 1, // Technically selectable, but function is unknown
Depth24 = 2,
Depth24Stencil8 = 3,
};
// Returns the string representation of a texture format
inline constexpr const char* textureFormatToString(TextureFmt fmt) {
switch (fmt) {
case TextureFmt::RGBA8: return "RGBA8";
case TextureFmt::RGB8: return "RGB8";
case TextureFmt::RGBA5551: return "RGBA5551";
case TextureFmt::RGB565: return "RGB565";
case TextureFmt::RGBA4: return "RGBA4";
case TextureFmt::IA8: return "IA8";
case TextureFmt::RG8: return "RG8";
case TextureFmt::I8: return "I8";
case TextureFmt::A8: return "A8";
case TextureFmt::IA4: return "IA4";
case TextureFmt::I4: return "I4";
case TextureFmt::A4: return "A4";
case TextureFmt::ETC1: return "ETC1";
case TextureFmt::ETC1A4: return "ETC1A4";
default: return "Unknown";
}
}
inline constexpr const char* textureFormatToString(ColorFmt fmt) {
return textureFormatToString(static_cast<TextureFmt>(fmt));
}
inline constexpr bool hasStencil(DepthFmt format) { return format == PICA::DepthFmt::Depth24Stencil8; }
// Size occupied by each pixel in bytes
// All formats are 16BPP except for RGBA8 (32BPP) and BGR8 (24BPP)
inline constexpr usize sizePerPixel(TextureFmt format) {
switch (format) {
case TextureFmt::RGB8: return 3;
case TextureFmt::RGBA8: return 4;
default: return 2;
}
}
inline constexpr usize sizePerPixel(ColorFmt format) {
return sizePerPixel(static_cast<TextureFmt>(format));
}
inline constexpr usize sizePerPixel(DepthFmt format) {
switch (format) {
case DepthFmt::Depth16: return 2;
case DepthFmt::Depth24: return 3;
case DepthFmt::Depth24Stencil8: return 4;
default: return 1; // Invalid format
}
}
enum class PrimType : u32 {
TriangleList = 0,
TriangleStrip = 1,
TriangleFan = 2,
GeometryPrimitive = 3,
};
} // namespace PICA

View file

@ -1,5 +1,7 @@
#pragma once
#include <span>
#include "dynarmic/interface/A32/a32.h"
#include "dynarmic/interface/A32/config.h"
#include "dynarmic/interface/exclusive_monitor.h"
@ -132,17 +134,11 @@ public:
return jit->Regs()[index];
}
std::array<u32, 16>& regs() {
return jit->Regs();
}
std::span<u32, 16> regs() { return jit->Regs(); }
// Get reference to array of FPRs. This array consists of the FPRs as single precision values
// Hence why its base type is u32
// Note: Dynarmic keeps 64 VFP registers as VFPv3 extends the VFP register set to 64 registers.
// However the 3DS ARM11 is an ARMv6k processor with VFPv2, so only the first 32 registers are actually used
std::array<u32, 64>& fprs() {
return jit->ExtRegs();
}
// Get reference to array of FPRs. This array consists of the FPRs as single precision values
// Hence why its base type is u32
std::span<u32, 32> fprs() { return std::span(jit->ExtRegs()).first<32>(); }
void setCPSR(u32 value) {
jit->SetCpsr(value);

View file

@ -3,6 +3,7 @@
#include <filesystem>
#include <fstream>
#include <SDL.h>
#include <glad/gl.h>
#include "cpu.hpp"
#include "io_file.hpp"
@ -35,7 +36,7 @@ class Emulator {
public:
Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory), memory(cpu.getTicksRef()) {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
Helpers::panic("Failed to initialize SDL2");
}
@ -45,8 +46,21 @@ public:
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
window = SDL_CreateWindow("Alber", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
if (window == nullptr) {
Helpers::panic("Window creation failed: %s", SDL_GetError());
}
glContext = SDL_GL_CreateContext(window);
if (glContext == nullptr) {
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
}
if(!gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress))) {
Helpers::panic("OpenGL init failed: %s", SDL_GetError());
}
reset();
}

View file

@ -10,6 +10,9 @@
#include "helpers.hpp"
#include "memory.hpp"
#include "result.hpp"
#include "result/result.hpp"
using Result::HorizonResult;
namespace PathType {
enum : u32 {
@ -97,7 +100,7 @@ struct FileSession {
u32 priority = 0; // TODO: What does this even do
bool isOpen;
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) :
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) :
archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
// For cloning a file session
@ -128,16 +131,6 @@ struct DirectorySession {
// Otherwise the fd of the opened file is returned (or nullptr if the opened file doesn't require one)
using FileDescriptor = std::optional<FILE*>;
enum class FSResult : u32 {
Success = 0,
AlreadyExists = 0x82044BE,
FileTooLarge = 0x86044D2,
FileNotFound = 0xC8804470,
NotFoundInvalid = 0xC8A04478, // Also a not found error code used here and there in the FS module.
NotFormatted = 0xC8A04554, // Trying to access an archive that needs formatting and has not been formatted
UnexpectedFileOrDir = 0xE0C04702
};
class ArchiveBase {
public:
struct FormatInfo {
@ -149,7 +142,7 @@ public:
protected:
using Handle = u32;
static constexpr FileDescriptor NoFile = nullptr;
static constexpr FileDescriptor FileError = std::nullopt;
Memory& mem;
@ -176,12 +169,12 @@ protected:
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
if (pathString[0] != Char('/')) return false;
// Counts how many folders sans the root our file is nested under.
// Counts how many folders sans the root our file is nested under.
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
// If it's 0 then this is the FS root.
// If it's > 0 then we're in a subdirectory of the root.
int level = 0;
// Split the string on / characters and see how many of the substrings are ".."
size_t pos = 0;
while ((pos = pathString.find(Char('/'))) != String::npos) {
@ -202,27 +195,27 @@ protected:
public:
virtual std::string name() = 0;
virtual u64 getFreeBytes() = 0;
virtual FSResult createFile(const FSPath& path, u64 size) = 0;
virtual FSResult deleteFile(const FSPath& path) = 0;
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
virtual HorizonResult deleteFile(const FSPath& path) = 0;
virtual Rust::Result<FormatInfo, FSResult> getFormatInfo(const FSPath& path) {
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false });
}
virtual FSResult createDirectory(const FSPath& path) {
virtual HorizonResult createDirectory(const FSPath& path) {
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
return FSResult::AlreadyExists;
return Result::FS::AlreadyExists;
}
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
virtual Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) = 0;
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
virtual Rust::Result<DirectorySession, FSResult> openDirectory(const FSPath& path) {
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
return Err(FSResult::FileNotFound);
return Err(Result::FS::FileNotFoundAlt);
}
virtual void format(const FSPath& path, const FormatInfo& info) {
@ -232,6 +225,6 @@ public:
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
// Returns the number of bytes read, or nullopt if the read failed
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
ArchiveBase(Memory& mem) : mem(mem) {}
};

View file

@ -9,11 +9,11 @@ public:
u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; }
std::string name() override { return "ExtSaveData::" + backingFolder; }
FSResult createFile(const FSPath& path, u64 size) override;
FSResult deleteFile(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
Rust::Result<DirectorySession, FSResult> openDirectory(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;

View file

@ -8,10 +8,10 @@ public:
u64 getFreeBytes() override { Helpers::panic("NCCH::GetFreeBytes unimplemented"); return 0; }
std::string name() override { return "NCCH"; }
FSResult createFile(const FSPath& path, u64 size) override;
FSResult deleteFile(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;

View file

@ -8,17 +8,17 @@ public:
u64 getFreeBytes() override { Helpers::panic("SaveData::GetFreeBytes unimplemented"); return 0; }
std::string name() override { return "SaveData"; }
FSResult createDirectory(const FSPath& path) override;
FSResult createFile(const FSPath& path, u64 size) override;
FSResult deleteFile(const FSPath& path) override;
HorizonResult createDirectory(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
Rust::Result<DirectorySession, FSResult> openDirectory(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
void format(const FSPath& path, const FormatInfo& info) override;
Rust::Result<FormatInfo, FSResult> getFormatInfo(const FSPath& path) override;
Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) override;
std::filesystem::path getFormatInfoPath() {
return IOFile::getAppData() / "FormatInfo" / "SaveData.format";

View file

@ -1,5 +1,8 @@
#pragma once
#include "archive_base.hpp"
#include "result/result.hpp"
using Result::HorizonResult;
class SDMCArchive : public ArchiveBase {
public:
@ -8,10 +11,10 @@ public:
u64 getFreeBytes() override { Helpers::panic("SDMC::GetFreeBytes unimplemented"); return 0; }
std::string name() override { return "SDMC"; }
FSResult createFile(const FSPath& path, u64 size) override;
FSResult deleteFile(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
};

View file

@ -8,10 +8,10 @@ public:
u64 getFreeBytes() override { return 0; }
std::string name() override { return "SelfNCCH"; }
FSResult createFile(const FSPath& path, u64 size) override;
FSResult deleteFile(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;

View file

@ -1,14 +1,23 @@
#pragma once
#include <cstdarg>
#include <climits>
#include <cstdarg>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <iterator>
#include <type_traits>
#include <utility>
#include <vector>
#include "termcolor.hpp"
// We have to detect and special-case AppleClang at the moment since its C++20 support is finicky and doesn't quite support std::bit_cast
#if defined(__clang__) && defined(__apple_build_version__)
#define HELPERS_APPLE_CLANG
#else
#include <bit>
#endif
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
@ -22,78 +31,74 @@ using s32 = std::int32_t;
using s64 = std::int64_t;
namespace Helpers {
[[noreturn]] static void panic(const char* fmt, ...) {
std::va_list args;
va_start(args, fmt);
std::cout << termcolor::on_red << "[FATAL] ";
std::vprintf (fmt, args);
std::cout << termcolor::reset << "\n";
va_end(args);
[[noreturn]] static void panic(const char* fmt, ...) {
std::va_list args;
va_start(args, fmt);
std::cout << termcolor::on_red << "[FATAL] ";
std::vprintf(fmt, args);
std::cout << termcolor::reset << "\n";
va_end(args);
exit(1);
}
exit(1);
}
static void warn(const char* fmt, ...) {
std::va_list args;
va_start(args, fmt);
std::cout << termcolor::on_red << "[Warning] ";
std::vprintf (fmt, args);
std::cout << termcolor::reset << "\n";
va_end(args);
}
static void warn(const char* fmt, ...) {
std::va_list args;
va_start(args, fmt);
std::cout << termcolor::on_red << "[Warning] ";
std::vprintf(fmt, args);
std::cout << termcolor::reset << "\n";
va_end(args);
}
static std::vector <u8> loadROM(std::string directory) {
std::ifstream file (directory, std::ios::binary);
if (file.fail())
panic("Couldn't read %s", directory.c_str());
static std::vector<u8> loadROM(std::string directory) {
std::ifstream file(directory, std::ios::binary);
if (file.fail()) panic("Couldn't read %s", directory.c_str());
std::vector<u8> ROM;
std::vector<u8> ROM;
file.unsetf(std::ios::skipws);
ROM.insert(ROM.begin(),
std::istream_iterator<uint8_t>(file),
std::istream_iterator<uint8_t>());
file.unsetf(std::ios::skipws);
ROM.insert(ROM.begin(), std::istream_iterator<uint8_t>(file), std::istream_iterator<uint8_t>());
file.close();
file.close();
printf ("%s loaded successfully\n", directory.c_str());
return ROM;
}
printf("%s loaded successfully\n", directory.c_str());
return ROM;
}
static constexpr bool buildingInDebugMode() {
#ifdef NDEBUG
return false;
#endif
static constexpr bool buildingInDebugMode() {
#ifdef NDEBUG
return false;
#endif
return true;
}
return true;
}
static void debug_printf (const char* fmt, ...) {
if constexpr (buildingInDebugMode()) {
std::va_list args;
va_start(args, fmt);
std::vprintf (fmt, args);
va_end(args);
}
}
static void debug_printf(const char* fmt, ...) {
if constexpr (buildingInDebugMode()) {
std::va_list args;
va_start(args, fmt);
std::vprintf(fmt, args);
va_end(args);
}
}
/// Sign extend an arbitrary-size value to 32 bits
static constexpr u32 inline signExtend32 (u32 value, u32 startingSize) {
auto temp = (s32) value;
auto bitsToShift = 32 - startingSize;
return (u32) (temp << bitsToShift >> bitsToShift);
}
/// Sign extend an arbitrary-size value to 32 bits
static constexpr u32 inline signExtend32(u32 value, u32 startingSize) {
auto temp = (s32)value;
auto bitsToShift = 32 - startingSize;
return (u32)(temp << bitsToShift >> bitsToShift);
}
/// Sign extend an arbitrary-size value to 16 bits
static constexpr u16 signExtend16 (u16 value, u32 startingSize) {
auto temp = (s16) value;
auto bitsToShift = 16 - startingSize;
return (u16) (temp << bitsToShift >> bitsToShift);
}
/// Sign extend an arbitrary-size value to 16 bits
static constexpr u16 signExtend16(u16 value, u32 startingSize) {
auto temp = (s16)value;
auto bitsToShift = 16 - startingSize;
return (u16)(temp << bitsToShift >> bitsToShift);
}
/// Create a mask with `count` number of one bits.
template<typename T, usize count>
static constexpr T ones () {
template <typename T, usize count>
static constexpr T ones() {
constexpr usize bitsize = CHAR_BIT * sizeof(T);
static_assert(count <= bitsize, "count larger than bitsize of T");
@ -104,74 +109,74 @@ namespace Helpers {
}
/// Extract bits from an integer-type
template<usize offset, typename T>
static constexpr T getBit (T value) {
return (value >> offset) & T(1);
template <usize offset, typename T>
static constexpr T getBit(T value) {
return (value >> offset) & T(1);
}
/// Extract bits from an integer-type
template<usize offset, usize bits, typename T>
static constexpr T getBits (T value) {
return (value >> offset) & ones<T, bits>();
template <usize offset, usize bits, typename T>
static constexpr T getBits(T value) {
return (value >> offset) & ones<T, bits>();
}
/// Check if a bit "bit" of value is set
static constexpr bool isBitSet (u32 value, int bit) {
return (value >> bit) & 1;
}
/// Check if a bit "bit" of value is set
static constexpr bool isBitSet(u32 value, int bit) { return (value >> bit) & 1; }
/// rotate number right
template <typename T>
static constexpr T rotr (T value, int bits) {
constexpr auto bitWidth = sizeof(T) * 8;
bits &= bitWidth - 1;
return (value >> bits) | (value << (bitWidth - bits));
}
/// rotate number right
template <typename T>
static constexpr T rotr(T value, int bits) {
constexpr auto bitWidth = sizeof(T) * 8;
bits &= bitWidth - 1;
return (value >> bits) | (value << (bitWidth - bits));
}
// rotate number left
template <typename T>
static constexpr T rotl (T value, int bits) {
constexpr auto bitWidth = sizeof(T) * 8;
bits &= bitWidth - 1;
return (value << bits) | (value >> (bitWidth - bits));
}
// rotate number left
template <typename T>
static constexpr T rotl(T value, int bits) {
constexpr auto bitWidth = sizeof(T) * 8;
bits &= bitWidth - 1;
return (value << bits) | (value >> (bitWidth - bits));
}
/// Used to make the compiler evaluate beeg loops at compile time for the tablegen
template <typename T, T Begin, class Func, T ...Is>
static constexpr void static_for_impl( Func&& f, std::integer_sequence<T, Is...> ) {
( f( std::integral_constant<T, Begin + Is>{ } ),... );
}
/// Used to make the compiler evaluate beeg loops at compile time for the tablegen
template <typename T, T Begin, class Func, T... Is>
static constexpr void static_for_impl(Func&& f, std::integer_sequence<T, Is...>) {
(f(std::integral_constant<T, Begin + Is>{}), ...);
}
template <typename T, T Begin, T End, class Func>
static constexpr void static_for(Func&& f) {
static_for_impl<T, Begin>( std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{ } );
}
template <typename T, T Begin, T End, class Func>
static constexpr void static_for(Func&& f) {
static_for_impl<T, Begin>(std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{});
}
// For values < 0x99
static constexpr inline u8 incBCDByte(u8 value) {
return ((value & 0xf) == 0x9) ? value + 7 : value + 1;
}
};
// For values < 0x99
static constexpr inline u8 incBCDByte(u8 value) { return ((value & 0xf) == 0x9) ? value + 7 : value + 1; }
#ifdef HELPERS_APPLE_CLANG
template <class To, class From>
constexpr To bit_cast(const From& from) noexcept {
return *reinterpret_cast<const To*>(&from);
}
#else
template <class To, class From>
constexpr To bit_cast(const From& from) noexcept {
return std::bit_cast<To, From>(from);
}
#endif
}; // namespace Helpers
// UDLs for memory size values
constexpr size_t operator""_KB(unsigned long long int x) {
return 1024ULL * x;
}
constexpr size_t operator""_MB(unsigned long long int x) {
return 1024_KB * x;
}
constexpr size_t operator""_GB(unsigned long long int x) {
return 1024_MB * x;
}
constexpr size_t operator""_KB(unsigned long long int x) { return 1024ULL * x; }
constexpr size_t operator""_MB(unsigned long long int x) { return 1024_KB * x; }
constexpr size_t operator""_GB(unsigned long long int x) { return 1024_MB * x; }
// useful macros
// likely/unlikely
#ifdef __GNUC__
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
#define likely(x) __builtin_expect((x), 1)
#define unlikely(x) __builtin_expect((x), 0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif

View file

@ -2,10 +2,12 @@
#include <array>
#include <cassert>
#include <limits>
#include <span>
#include <string>
#include <vector>
#include "kernel_types.hpp"
#include "helpers.hpp"
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "resource_limits.hpp"
@ -14,7 +16,7 @@
class CPU;
class Kernel {
std::array<u32, 16>& regs;
std::span<u32, 16> regs;
CPU& cpu;
Memory& mem;

View file

@ -4,33 +4,7 @@
#include "fs/archive_base.hpp"
#include "handles.hpp"
#include "helpers.hpp"
namespace SVCResult {
enum : u32 {
Success = 0,
Failure = 0xFFFFFFFF,
ObjectNotFound = 0xD88007FA,
// Different calls return a different value for these ones
InvalidEnumValue = 0xD8E007ED,
InvalidEnumValueAlt = 0xD8E093ED,
BadHandle = 0xD8E007F7,
BadHandleAlt = 0xD9001BF7,
InvalidCombination = 0xE0E01BEE, // Used for invalid memory permission combinations
UnalignedAddr = 0xE0E01BF1,
UnalignedSize = 0xE0E01BF2,
BadThreadPriority = 0xE0E01BFD,
PortNameTooLong = 0xE0E0181E,
// Returned when a thread stops waiting due to timing out
Timeout = 0x9401BFE,
// Returned when a thread releases a mutex it does not own
InvalidMutexRelease = 0xD8E0041F
};
}
#include "result/result.hpp"
enum class KernelObjectType : u8 {
AddressArbiter, Archive, Directory, File, MemoryBlock, Process, ResourceLimit, Session, Dummy,
@ -125,7 +99,7 @@ struct Thread {
ThreadStatus status;
Handle handle; // OS handle for this thread
int index; // Index of the thread. 0 for the first thread, 1 for the second, and so on
// The waiting address for threads that are waiting on an AddressArbiter
u32 waitingAddress;

View file

@ -33,7 +33,10 @@ namespace VirtualAddrs {
NormalHeapStart = 0x08000000,
LinearHeapStartOld = 0x14000000, // If kernel version < 0x22C
LinearHeapEndOld = 0x1C000000,
LinearHeapStartNew = 0x30000000,
LinearHeapEndNew = 0x40000000,
// Start of TLS for first thread. Next thread's storage will be at TLSBase + 0x1000, and so on
TLSBase = 0xFF400000,

View file

@ -29,7 +29,20 @@
#include <type_traits>
#include <utility>
#include "gl3w.h"
#include <glad/gl.h>
// Check if we have C++20. If yes, we can add C++20 std::span support
#ifdef _MSVC_LANG // MSVC does not properly define __cplusplus without a compiler flag...
#if _MSVC_LANG >= 202002L
#define OPENGL_HAVE_CPP20
#endif
#elif __cplusplus >= 202002L
#define OPENGL_HAVE_CPP20
#endif // MSVC_LANG
#ifdef OPENGL_HAVE_CPP20
#include <span>
#endif
// Uncomment the following define if you want GL objects to automatically free themselves when their lifetime ends
// #define OPENGL_DESTRUCTORS
@ -390,17 +403,30 @@ namespace OpenGL {
void free() { glDeleteBuffers(1, &m_handle); }
// Reallocates the buffer on every call. Prefer the sub version if possible.
template <typename VertType>
void bufferVerts(VertType* vertices, int vertCount, GLenum usage = GL_DYNAMIC_DRAW) {
glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertCount, vertices, usage);
}
template <typename VertType>
void bufferVerts(VertType* vertices, int vertCount, GLenum usage = GL_DYNAMIC_DRAW) {
glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertCount, vertices, usage);
}
// Only use if you used createFixedSize
template <typename VertType>
void bufferVertsSub(VertType* vertices, int vertCount, GLintptr offset = 0) {
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertCount, vertices);
}
};
// Only use if you used createFixedSize
template <typename VertType>
void bufferVertsSub(VertType* vertices, int vertCount, GLintptr offset = 0) {
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertCount, vertices);
}
// If C++20 is available, add overloads that take std::span instead of raw pointers
#ifdef OPENGL_HAVE_CPP20
template <typename VertType>
void bufferVerts(std::span<const VertType> vertices, GLenum usage = GL_DYNAMIC_DRAW) {
glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertices.size(), vertices.data(), usage);
}
template <typename VertType>
void bufferVertsSub(std::span<const VertType> vertices, GLintptr offset = 0) {
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertices.size(), vertices.data());
}
#endif
};
enum DepthFunc {
Never = GL_NEVER, // Depth test never passes
@ -498,8 +524,6 @@ namespace OpenGL {
static GLint getProgram() { return get<GLint>(GL_CURRENT_PROGRAM); }
static bool scissorEnabled() { return isEnabled(GL_SCISSOR_TEST); }
static bool versionSupported(int major, int minor) { return gl3wIsSupported(major, minor); }
[[nodiscard]] static GLint uniformLocation(GLuint program, const char* name) {
return glGetUniformLocation(program, name);
}

View file

@ -1,10 +1,14 @@
#pragma once
#include <array>
#include <span>
#include "PICA/float_types.hpp"
#include "helpers.hpp"
#include "logger.hpp"
#include "opengl.hpp"
#include "surface_cache.hpp"
#include "textures.hpp"
#include "PICA/regs.hpp"
// More circular dependencies!
class GPU;
@ -12,7 +16,11 @@ class GPU;
struct Vertex {
OpenGL::vec4 position;
OpenGL::vec4 colour;
OpenGL::vec2 UVs;
OpenGL::vec2 texcoord0;
OpenGL::vec2 texcoord1;
Floats::f24 texcoord0_w;
u32 padding; // pad so that texcoord2 is 64-bit aligned
OpenGL::vec2 texcoord2;
};
class Renderer {
@ -24,7 +32,16 @@ class Renderer {
OpenGL::VertexBuffer vbo;
GLint alphaControlLoc = -1;
GLint texUnitConfigLoc = -1;
// TEV configuration uniform locations
GLint textureEnvSourceLoc = -1;
GLint textureEnvOperandLoc = -1;
GLint textureEnvCombinerLoc = -1;
GLint textureEnvColorLoc = -1;
GLint textureEnvScaleLoc = -1;
GLint textureEnvUpdateBufferLoc = -1;
GLint textureEnvBufferColorLoc = -1;
// Depth configuration uniform locations
GLint depthOffsetLoc = -1;
GLint depthScaleLoc = -1;
@ -41,55 +58,56 @@ class Renderer {
SurfaceCache<ColourBuffer, 10> colourBufferCache;
SurfaceCache<Texture, 256> textureCache;
OpenGL::uvec2 fbSize; // The size of the framebuffer (ie both the colour and depth buffer)'
OpenGL::uvec2 fbSize; // The size of the framebuffer (ie both the colour and depth buffer)'
u32 colourBufferLoc; // Location in 3DS VRAM for the colour buffer
PICA::ColorFmt colourBufferFormat; // Format of the colours stored in the colour buffer
u32 colourBufferLoc; // Location in 3DS VRAM for the colour buffer
ColourBuffer::Formats colourBufferFormat; // Format of the colours stored in the colour buffer
// Same for the depth/stencil buffer
u32 depthBufferLoc;
DepthBuffer::Formats depthBufferFormat;
PICA::DepthFmt depthBufferFormat;
// Dummy VAO/VBO for blitting the final output
OpenGL::VertexArray dummyVAO;
OpenGL::VertexBuffer dummyVBO;
static constexpr u32 regNum = 0x300; // Number of internal PICA registers
static constexpr u32 regNum = 0x300; // Number of internal PICA registers
const std::array<u32, regNum>& regs;
OpenGL::Texture screenTexture;
OpenGL::Framebuffer screenFramebuffer;
OpenGL::Framebuffer getColourFBO();
OpenGL::Texture getTexture(Texture& tex);
MAKE_LOG_FUNCTION(log, rendererLogger)
void setupBlending();
void bindDepthBuffer();
void setupTextureEnvState();
void bindTexturesToSlots();
public:
public:
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs) : gpu(gpu), regs(internalRegs) {}
void reset();
void display(); // Display the 3DS screen contents to the window
void initGraphicsContext(); // Initialize graphics context
void getGraphicsContext(); // Set up graphics context for rendering
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer
void drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count); // Draw the given vertices
void display(); // Display the 3DS screen contents to the window
void initGraphicsContext(); // Initialize graphics context
void getGraphicsContext(); // Set up graphics context for rendering
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer
void drawVertices(PICA::PrimType primType, std::span<const Vertex> vertices); // Draw the given vertices
void setFBSize(u32 width, u32 height) {
fbSize.x() = width;
fbSize.y() = height;
}
void setColourFormat(ColourBuffer::Formats format) { colourBufferFormat = format; }
void setColourFormat(u32 format) { colourBufferFormat = static_cast<ColourBuffer::Formats>(format); }
void setDepthFormat(DepthBuffer::Formats format) { depthBufferFormat = format; }
void setDepthFormat(u32 format) {
if (format == 1) {
void setColourFormat(PICA::ColorFmt format) { colourBufferFormat = format; }
void setDepthFormat(PICA::DepthFmt format) {
if (format == PICA::DepthFmt::Unknown1) {
Helpers::panic("[PICA] Undocumented depth-stencil mode!");
}
depthBufferFormat = static_cast<DepthBuffer::Formats>(format);
depthBufferFormat = format;
}
void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }

View file

@ -13,6 +13,7 @@
// Are concerned. We could overload the == operator, but that implies full equality
// Including equality of the allocated OpenGL resources, which we don't want
// - A "valid" member that tells us whether the function is still valid or not
// - A "location" member which tells us which location in 3DS memory this surface occupies
template <typename SurfaceType, size_t capacity>
class SurfaceCache {
// Vanilla std::optional can't hold actual references
@ -40,6 +41,15 @@ public:
return std::nullopt;
}
OptionalRef findFromAddress(u32 address) {
for (auto& e : buffer) {
if (e.location == address && e.valid)
return e;
}
return std::nullopt;
}
// Adds a surface object to the cache and returns it
SurfaceType& add(const SurfaceType& surface) {
if (size >= capacity) {

View file

@ -1,4 +1,5 @@
#pragma once
#include "PICA/regs.hpp"
#include "boost/icl/interval.hpp"
#include "helpers.hpp"
#include "opengl.hpp"
@ -7,18 +8,8 @@ template <typename T>
using Interval = boost::icl::right_open_interval<T>;
struct ColourBuffer {
enum class Formats : u32 {
RGBA8 = 0,
BGR8 = 1,
RGB5A1 = 2,
RGB565 = 3,
RGBA4 = 4,
Trash1 = 5, Trash2 = 6, Trash3 = 7 // Technically selectable, but their function is unknown
};
u32 location;
Formats format;
PICA::ColorFmt format;
OpenGL::uvec2 size;
bool valid;
@ -30,7 +21,7 @@ struct ColourBuffer {
ColourBuffer() : valid(false) {}
ColourBuffer(u32 loc, Formats format, u32 x, u32 y, bool valid = true)
ColourBuffer(u32 loc, PICA::ColorFmt format, u32 x, u32 y, bool valid = true)
: location(loc), format(format), size({x, y}), valid(valid) {
u64 endLoc = (u64)loc + sizeInBytes();
@ -78,31 +69,14 @@ struct ColourBuffer {
size.x() == other.size.x() && size.y() == other.size.y();
}
// Size occupied by each pixel in bytes
// All formats are 16BPP except for RGBA8 (32BPP) and BGR8 (24BPP)
size_t sizePerPixel() {
switch (format) {
case Formats::BGR8: return 3;
case Formats::RGBA8: return 4;
default: return 2;
}
}
size_t sizeInBytes() {
return (size_t)size.x() * (size_t)size.y() * sizePerPixel();
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
}
};
struct DepthBuffer {
enum class Formats : u32 {
Depth16 = 0,
Garbage = 1,
Depth24 = 2,
Depth24Stencil8 = 3
};
u32 location;
Formats format;
PICA::DepthFmt format;
OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer
bool valid;
@ -113,7 +87,7 @@ struct DepthBuffer {
DepthBuffer() : valid(false) {}
DepthBuffer(u32 loc, Formats format, u32 x, u32 y, bool valid = true) :
DepthBuffer(u32 loc, PICA::DepthFmt format, u32 x, u32 y, bool valid = true) :
location(loc), format(format), size({x, y}), valid(valid) {
u64 endLoc = (u64)loc + sizeInBytes();
@ -121,10 +95,6 @@ struct DepthBuffer {
range = Interval<u32>(loc, (u32)endLoc);
}
bool hasStencil() {
return format == Formats::Depth24Stencil8;
}
void allocate() {
// Create texture for the FBO, setting up filters and the like
// Reading back the current texture is slow, but allocate calls should be few and far between.
@ -167,18 +137,7 @@ struct DepthBuffer {
size.x() == other.size.x() && size.y() == other.size.y();
}
// Size occupied by each pixel in bytes
size_t sizePerPixel() {
switch (format) {
case Formats::Depth16: return 2;
case Formats::Depth24: return 3;
case Formats::Depth24Stencil8: return 4;
default: return 1; // Invalid format
}
}
size_t sizeInBytes() {
return (size_t)size.x() * (size_t)size.y() * sizePerPixel();
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
}
};

View file

@ -1,6 +1,7 @@
#pragma once
#include <array>
#include <string>
#include "PICA/regs.hpp"
#include "boost/icl/interval.hpp"
#include "helpers.hpp"
#include "opengl.hpp"
@ -9,28 +10,9 @@ template <typename T>
using Interval = boost::icl::right_open_interval<T>;
struct Texture {
enum class Formats : u32 {
RGBA8 = 0,
RGB8 = 1,
RGBA5551 = 2,
RGB565 = 3,
RGBA4 = 4,
IA8 = 5,
RG8 = 6,
I8 = 7,
A8 = 8,
IA4 = 9,
I4 = 10,
A4 = 11,
ETC1 = 12,
ETC1A4 = 13,
Trash1 = 14, Trash2 = 15 // TODO: What are these?
};
u32 location;
u32 config; // Magnification/minification filter, wrapping configs, etc
Formats format;
PICA::TextureFmt format;
OpenGL::uvec2 size;
bool valid;
@ -41,7 +23,7 @@ struct Texture {
Texture() : valid(false) {}
Texture(u32 loc, Formats format, u32 x, u32 y, u32 config, bool valid = true)
Texture(u32 loc, PICA::TextureFmt format, u32 x, u32 y, u32 config, bool valid = true)
: location(loc), format(format), size({x, y}), config(config), valid(valid) {
u64 endLoc = (u64)loc + sizeInBytes();
@ -62,7 +44,7 @@ struct Texture {
void free();
u64 sizeInBytes();
u32 decodeTexel(u32 u, u32 v, Formats fmt, const void* data);
u32 decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, const void* data);
// Get the morton interleave offset of a texel based on its U and V values
static u32 mortonInterleave(u32 u, u32 v);
@ -70,12 +52,9 @@ struct Texture {
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
// Returns the string representation of a texture format
static std::string textureFormatToString(Formats fmt);
// Returns the format of this texture as a string
std::string formatToString() {
return textureFormatToString(format);
return PICA::textureFormatToString(format);
}
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture

View file

@ -0,0 +1,8 @@
#pragma once
#include "result_common.hpp"
#include "result_kernel.hpp"
#include "result_os.hpp"
#include "result_fnd.hpp"
#include "result_fs.hpp"
#include "result_gsp.hpp"

View file

@ -0,0 +1,206 @@
#pragma once
#include <climits>
#include <cstdint>
#include <type_traits>
namespace Result {
enum class HorizonResultLevel : uint32_t {
Success = 0,
Info = 1,
Status = 25,
Temporary = 26,
Permanent = 27,
Usage = 28,
Reinitialize = 29,
Reset = 30,
Fatal = 31,
};
enum class HorizonResultSummary : uint32_t {
Success = 0,
NothingHappened = 1,
WouldBlock = 2,
OutOfResource = 3,
NotFound = 4,
InvalidState = 5,
NotSupported = 6,
InvalidArgument = 7,
WrongArgument = 8,
Canceled = 9,
StatusChanged = 10,
Internal = 11,
};
enum class HorizonResultModule : uint32_t {
Common = 0,
Kernel = 1,
Util = 2,
FileServer = 3,
LoaderServer = 4,
TCB = 5,
OS = 6,
DBG = 7,
DMNT = 8,
PDN = 9 ,
GSP = 10,
I2C = 11,
GPIO = 12,
DD = 13,
CODEC = 14,
SPI = 15,
PXI = 16,
FS = 17,
DI = 18,
HID = 19,
CAM = 20,
PI = 21,
PM = 22,
PM_LOW = 23,
FSI = 24,
SRV = 25,
NDM = 26,
NWM = 27,
SOC = 28,
LDR = 29,
ACC = 30,
RomFS = 31,
AM = 32,
HIO = 33,
Updater = 34,
MIC = 35,
FND = 36,
MP = 37,
MPWL = 38,
AC = 39,
HTTP = 40,
DSP = 41,
SND = 42,
DLP = 43,
HIO_LOW = 44,
CSND = 45,
SSL = 46,
AM_LOW = 47,
NEX = 48,
Friends = 49,
RDT = 50,
Applet = 51,
NIM = 52,
PTM = 53,
MIDI = 54,
MC = 55,
SWC = 56,
FatFS = 57,
NGC = 58,
CARD = 59,
CARDNOR = 60,
SDMC = 61,
BOSS = 62,
DBM = 63,
Config = 64,
PS = 65,
CEC = 66,
IR = 67,
UDS = 68,
PL = 69,
CUP = 70,
Gyroscope = 71,
MCU = 72,
NS = 73,
News = 74,
RO = 75,
GD = 76,
CardSPI = 77,
EC = 78,
WebBrowser = 79,
Test = 80,
ENC = 81,
PIA = 82,
ACT = 83,
VCTL = 84,
OLV = 85,
NEIA = 86,
NPNS = 87,
AVD = 90,
L2B = 91,
MVD = 92,
NFC = 93,
UART = 94,
SPM = 95,
QTM = 96,
NFP = 97,
};
class HorizonResult {
private:
static const uint32_t DescriptionBits = 10;
static const uint32_t ModuleBits = 8;
static const uint32_t ReservedBits = 3;
static const uint32_t SummaryBits = 6;
static const uint32_t LevelBits = 5;
static const uint32_t DescriptionOffset = 0;
static const uint32_t ModuleOffset = DescriptionOffset + DescriptionBits;
static const uint32_t SummaryOffset = ModuleOffset + ModuleBits + ReservedBits;
static const uint32_t LevelOffset = SummaryOffset + SummaryBits;
static_assert(DescriptionBits + ModuleBits + SummaryBits + LevelBits + ReservedBits == sizeof(uint32_t) * CHAR_BIT, "Invalid Result size");
uint32_t m_value;
constexpr inline uint32_t getBitsValue(int offset, int amount) {
return (m_value >> offset) & ~(~0 << amount);
}
static constexpr inline uint32_t makeValue(uint32_t description, uint32_t module, uint32_t summary, uint32_t level) {
return (description << DescriptionOffset) | (module << ModuleOffset) | (summary << SummaryOffset) | (level << LevelOffset);
}
public:
constexpr HorizonResult() {}
constexpr HorizonResult(uint32_t value) : m_value(value) {}
constexpr HorizonResult(uint32_t description, HorizonResultModule module, HorizonResultSummary summary, HorizonResultLevel level) : m_value(makeValue(description, static_cast<uint32_t>(module), static_cast<uint32_t>(summary), static_cast<uint32_t>(level))) {}
constexpr operator uint32_t() const { return m_value; }
constexpr inline uint32_t getDescription() {
return getBitsValue(DescriptionOffset, DescriptionBits);
}
constexpr inline HorizonResultModule getModule() {
return static_cast<HorizonResultModule>(getBitsValue(ModuleOffset, ModuleBits));
}
constexpr inline HorizonResultSummary getSummary() {
return static_cast<HorizonResultSummary>(getBitsValue(SummaryOffset, SummaryBits));
}
constexpr inline HorizonResultLevel getLevel() {
return static_cast<HorizonResultLevel>(getBitsValue(LevelOffset, LevelBits));
}
constexpr inline uint32_t getRawValue() {
return m_value;
}
constexpr inline bool isSuccess() {
return m_value == 0;
}
constexpr inline bool isFailure() {
return m_value != 0;
}
};
static_assert(std::is_trivially_destructible<HorizonResult>::value, "std::is_trivially_destructible<HorizonResult>::value");
#define DEFINE_HORIZON_RESULT_MODULE(ns, value) \
namespace ns::Detail {\
static constexpr HorizonResultModule ModuleId = HorizonResultModule::value; \
}
#define DEFINE_HORIZON_RESULT(name, description, summary, level) \
static constexpr HorizonResult name(description, Detail::ModuleId, HorizonResultSummary::summary, HorizonResultLevel::level);
static constexpr HorizonResult Success(0);
static constexpr HorizonResult FailurePlaceholder(UINT32_MAX);
};

View file

@ -0,0 +1,8 @@
#pragma once
#include "result_common.hpp"
DEFINE_HORIZON_RESULT_MODULE(Result::FND, FND);
namespace Result::FND {
DEFINE_HORIZON_RESULT(InvalidEnumValue, 1005, InvalidArgument, Permanent);
};

View file

@ -0,0 +1,18 @@
#pragma once
#include "result_common.hpp"
DEFINE_HORIZON_RESULT_MODULE(Result::FS, FS);
namespace Result::FS {
// TODO: Verify this
DEFINE_HORIZON_RESULT(FileNotFound, 100, NotFound, Status);
// TODO: Verify this
DEFINE_HORIZON_RESULT(FileNotFoundAlt, 112, NotFound, Status);
// Also a not found error code used here and there in the FS module.
DEFINE_HORIZON_RESULT(NotFoundInvalid, 120, InvalidState, Status);
DEFINE_HORIZON_RESULT(AlreadyExists, 190, NothingHappened, Info);
DEFINE_HORIZON_RESULT(FileTooLarge, 210, OutOfResource, Info);
// Trying to access an archive that needs formatting and has not been formatted
DEFINE_HORIZON_RESULT(NotFormatted, 340, InvalidState, Status);
DEFINE_HORIZON_RESULT(UnexpectedFileOrDir, 770, NotSupported, Usage);
};

View file

@ -0,0 +1,8 @@
#pragma once
#include "result_common.hpp"
DEFINE_HORIZON_RESULT_MODULE(Result::GSP, GSP);
namespace Result::GSP {
DEFINE_HORIZON_RESULT(SuccessRegisterIRQ, 519, Success, Success);
};

View file

@ -0,0 +1,15 @@
#pragma once
#include "result_common.hpp"
DEFINE_HORIZON_RESULT_MODULE(Result::Kernel, Kernel);
namespace Result::Kernel {
// Returned when a thread releases a mutex it does not own
DEFINE_HORIZON_RESULT(InvalidMutexRelease, 31, InvalidArgument, Permanent);
DEFINE_HORIZON_RESULT(NotFound, 1018, NotFound, Permanent);
DEFINE_HORIZON_RESULT(InvalidEnumValue, 1005, InvalidArgument, Permanent);
DEFINE_HORIZON_RESULT(InvalidHandle, 1015, InvalidArgument, Permanent);
static_assert(InvalidHandle == 0xD8E007F7, "conversion broken");
};

View file

@ -0,0 +1,14 @@
#pragma once
#include "result_common.hpp"
DEFINE_HORIZON_RESULT_MODULE(Result::OS, OS);
namespace Result::OS {
DEFINE_HORIZON_RESULT(PortNameTooLong, 30, InvalidArgument, Usage);
DEFINE_HORIZON_RESULT(InvalidHandle, 1015, WrongArgument, Permanent);
DEFINE_HORIZON_RESULT(InvalidCombination, 1006, InvalidArgument, Usage);
DEFINE_HORIZON_RESULT(MisalignedAddress, 1009, InvalidArgument, Usage);
DEFINE_HORIZON_RESULT(MisalignedSize, 1010, InvalidArgument, Usage);
DEFINE_HORIZON_RESULT(OutOfRange, 1021, InvalidArgument, Usage);
DEFINE_HORIZON_RESULT(Timeout, 1022, StatusChanged, Info);
};

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class ACService {
Handle handle = KernelHandles::AC;

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class ACTService {
Handle handle = KernelHandles::ACT;

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class AMService {
Handle handle = KernelHandles::AM;

View file

@ -4,6 +4,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
// Yay, more circular dependencies
class Kernel;

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class BOSSService {
Handle handle = KernelHandles::BOSS;

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class CAMService {
Handle handle = KernelHandles::CAM;

View file

@ -4,6 +4,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class Kernel;

View file

@ -4,6 +4,7 @@
#include "logger.hpp"
#include "memory.hpp"
#include "region_codes.hpp"
#include "result/result.hpp"
class CFGService {
Handle handle = KernelHandles::CFG;

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
// Please forgive me for how everything in this file is named
// "dlp:SRVR" is not a nice name to work with

View file

@ -4,6 +4,7 @@
#include "helpers.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
namespace DSPPipeType {
enum : u32 {

View file

@ -4,6 +4,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
// It's important to keep this struct to 16 bytes as we use its sizeof in the service functions in frd.cpp
struct FriendKey {

View file

@ -8,6 +8,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
// Yay, more circular dependencies
class Kernel;
@ -29,8 +30,8 @@ class FSService {
ExtSaveDataArchive sharedExtSaveData_nand;
ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath);
Rust::Result<Handle, FSResult> openArchiveHandle(u32 archiveID, const FSPath& path);
Rust::Result<Handle, FSResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
Rust::Result<Handle, HorizonResult> openArchiveHandle(u32 archiveID, const FSPath& path);
Rust::Result<Handle, HorizonResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
std::optional<Handle> openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms);
FSPath readPath(u32 type, u32 pointer, u32 size);
@ -62,7 +63,7 @@ public:
sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"),
sdmc(mem), selfNcch(mem), ncch(mem), kernel(kernel)
{}
void reset();
void handleSyncRequest(u32 messagePointer);
// Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM.

View file

@ -6,6 +6,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
enum class GPUInterrupt : u8 {
PSC0 = 0, // Memory fill completed

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class LCDService {
Handle handle = KernelHandles::LCD;

View file

@ -5,6 +5,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
namespace HID::Keys {
enum : u32 {

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class LDRService {
Handle handle = KernelHandles::LDR_RO;

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class MICService {
Handle handle = KernelHandles::MIC;

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class NDMService {
Handle handle = KernelHandles::NDM;

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
// You know the drill
class Kernel;

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class NIMService {
Handle handle = KernelHandles::NIM;

View file

@ -3,6 +3,7 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class PTMService {
Handle handle = KernelHandles::PTM;

View file

@ -1,6 +1,8 @@
#pragma once
#include <array>
#include <optional>
#include <span>
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
@ -14,16 +16,16 @@
#include "services/cfg.hpp"
#include "services/dlp_srvr.hpp"
#include "services/dsp.hpp"
#include "services/hid.hpp"
#include "services/frd.hpp"
#include "services/fs.hpp"
#include "services/gsp_gpu.hpp"
#include "services/gsp_lcd.hpp"
#include "services/hid.hpp"
#include "services/ldr_ro.hpp"
#include "services/mic.hpp"
#include "services/ndm.hpp"
#include "services/nfc.hpp"
#include "services/nim.hpp"
#include "services/ndm.hpp"
#include "services/ptm.hpp"
#include "services/y2r.hpp"
@ -31,7 +33,7 @@
class Kernel;
class ServiceManager {
std::array<u32, 16>& regs;
std::span<u32, 16> regs;
Memory& mem;
Kernel& kernel;
@ -69,8 +71,8 @@ class ServiceManager {
void registerClient(u32 messagePointer);
void subscribe(u32 messagePointer);
public:
ServiceManager(std::array<u32, 16>& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel);
public:
ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel);
void reset();
void initializeFS() { fs.initializeFilesystem(); }
void handleSyncRequest(u32 messagePointer);