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

@ -1,10 +1,51 @@
#include "PICA/gpu.hpp"
#include <array>
#include <cstdio>
#include <cstddef>
#include "PICA/float_types.hpp"
#include "PICA/regs.hpp"
#include <cstdio>
using namespace Floats;
// A representation of the output vertex as it comes out of the vertex shader, with padding and all
struct OutputVertex {
using vec2f = OpenGL::Vector<f24, 2>;
using vec3f = OpenGL::Vector<f24, 3>;
using vec4f = OpenGL::Vector<f24, 4>;
union {
struct {
vec4f positions; // Vertex position
vec4f quaternion; // Quaternion specifying the normal/tangent frame (for fragment lighting)
vec4f colour; // Vertex color
vec2f texcoord0; // Texcoords for texture unit 0 (Only U and V, W is stored separately for 3D textures!)
vec2f texcoord1; // Texcoords for TU 1
f24 texcoord0_w; // W component for texcoord 0 if using a 3D texture
u32 padding; // Unused
vec3f view; // View vector (for fragment lighting)
u32 padding2; // Unused
vec2f texcoord2; // Texcoords for TU 2
} s;
// The software, non-accelerated vertex loader writes here and then reads specific components from the above struct
f24 raw[0x20];
};
OutputVertex() {}
};
#define ASSERT_POS(member, pos) static_assert(offsetof(OutputVertex, s.member) == pos * sizeof(f24), "OutputVertex struct is broken!");
ASSERT_POS(positions, 0)
ASSERT_POS(quaternion, 4)
ASSERT_POS(colour, 8)
ASSERT_POS(texcoord0, 12)
ASSERT_POS(texcoord1, 14)
ASSERT_POS(texcoord0_w, 16)
ASSERT_POS(view, 18)
ASSERT_POS(texcoord2, 22)
GPU::GPU(Memory& mem) : mem(mem), renderer(*this, regs) {
vram = new u8[vramSize];
mem.setVRAM(vram); // Give the bus a pointer to our VRAM
@ -51,36 +92,37 @@ void GPU::drawArrays(bool indexed) {
}
}
Vertex* vertices = new Vertex[Renderer::vertexBufferSize];
static std::array<Vertex, Renderer::vertexBufferSize> vertices;
template <bool indexed, bool useShaderJIT>
void GPU::drawArrays() {
// Base address for vertex attributes
// The vertex base is always on a quadword boundary because the PICA does weird alignment shit any time possible
const u32 vertexBase = ((regs[PICAInternalRegs::VertexAttribLoc] >> 1) & 0xfffffff) * 16;
const u32 vertexCount = regs[PICAInternalRegs::VertexCountReg]; // Total # of vertices to transfer
const u32 vertexBase = ((regs[PICA::InternalRegs::VertexAttribLoc] >> 1) & 0xfffffff) * 16;
const u32 vertexCount = regs[PICA::InternalRegs::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 = Helpers::getBits<8, 2>(primConfig);
if (primType != 0 && primType != 1 && primType != 3) Helpers::panic("[PICA] Tried to draw unimplemented shape %d\n", primType);
const u32 primConfig = regs[PICA::InternalRegs::PrimitiveConfig];
const PICA::PrimType primType = static_cast<PICA::PrimType>(Helpers::getBits<8, 2>(primConfig));
if (primType == PICA::PrimType::TriangleFan) Helpers::panic("[PICA] Tried to draw unimplemented shape %d\n", primType);
if (vertexCount > Renderer::vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize");
if ((primType == 0 && vertexCount % 3) || (primType == 1 && vertexCount < 3)) {
if ((primType == PICA::PrimType::TriangleList && vertexCount % 3) ||
(primType == PICA::PrimType::TriangleStrip && vertexCount < 3)) {
Helpers::panic("Invalid vertex count for primitive. Type: %d, vert count: %d\n", primType, vertexCount);
}
// Get the configuration for the index buffer, used only for indexed drawing
u32 indexBufferConfig = regs[PICAInternalRegs::IndexBufferConfig];
u32 indexBufferConfig = regs[PICA::InternalRegs::IndexBufferConfig];
u32 indexBufferPointer = vertexBase + (indexBufferConfig & 0xfffffff);
bool shortIndex = Helpers::getBit<31>(indexBufferConfig); // Indicates whether vert indices are 16-bit or 8-bit
// 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
u64 vertexCfg = u64(regs[PICAInternalRegs::AttribFormatLow]) | (u64(regs[PICAInternalRegs::AttribFormatHigh]) << 32);
u64 vertexCfg = u64(regs[PICA::InternalRegs::AttribFormatLow]) | (u64(regs[PICA::InternalRegs::AttribFormatHigh]) << 32);
if constexpr (!indexed) {
u32 offset = regs[PICAInternalRegs::VertexOffsetReg];
u32 offset = regs[PICA::InternalRegs::VertexOffsetReg];
log("PICA::DrawArrays(vertex count = %d, vertexOffset = %d)\n", vertexCount, offset);
} else {
log("PICA::DrawElements(vertex count = %d, index buffer config = %08X)\n", vertexCount, indexBufferConfig);
@ -91,14 +133,14 @@ void GPU::drawArrays() {
}
// Total number of input attributes to shader. Differs between GS and VS. Currently stubbed to the VS one, as we don't have geometry shaders.
const u32 inputAttrCount = (regs[PICAInternalRegs::VertexShaderInputBufferCfg] & 0xf) + 1;
const u32 inputAttrCount = (regs[PICA::InternalRegs::VertexShaderInputBufferCfg] & 0xf) + 1;
const u64 inputAttrCfg = getVertexShaderInputConfig();
for (u32 i = 0; i < vertexCount; i++) {
u32 vertexIndex; // Index of the vertex in the VBO
if constexpr (!indexed) {
vertexIndex = i + regs[PICAInternalRegs::VertexOffsetReg];
vertexIndex = i + regs[PICA::InternalRegs::VertexOffsetReg];
} else {
if (shortIndex) {
auto ptr = getPointerPhys<u16>(indexBufferPointer);
@ -204,32 +246,42 @@ void GPU::drawArrays() {
std::memcpy(&shaderUnit.vs.inputs[mapping], &currentAttributes[j], sizeof(vec4f));
}
if constexpr (useShaderJIT) {
if constexpr (useShaderJIT) {
shaderJIT.run(shaderUnit.vs);
} else {
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));
std::memcpy(&vertices[i].UVs, &shaderUnit.vs.outputs[2], 2 * sizeof(f24));
OutputVertex out;
// Map shader outputs to fixed function properties
const u32 totalShaderOutputs = regs[PICA::InternalRegs::ShaderOutputCount] & 7;
for (int i = 0; i < totalShaderOutputs; i++) {
const u32 config = regs[PICA::InternalRegs::ShaderOutmap0 + i];
for (int j = 0; j < 4; j++) { // pls unroll
const u32 mapping = (config >> (j * 8)) & 0x1F;
out.raw[mapping] = shaderUnit.vs.outputs[i][j];
}
}
std::memcpy(&vertices[i].position, &out.s.positions, sizeof(vec4f));
std::memcpy(&vertices[i].colour, &out.s.colour, sizeof(vec4f));
std::memcpy(&vertices[i].texcoord0, &out.s.texcoord0, 2 * sizeof(f24));
std::memcpy(&vertices[i].texcoord1, &out.s.texcoord1, 2 * sizeof(f24));
std::memcpy(&vertices[i].texcoord0_w, &out.s.texcoord0_w, sizeof(f24));
std::memcpy(&vertices[i].texcoord2, &out.s.texcoord2, 2 * sizeof(f24));
//printf("(x, y, z, w) = (%f, %f, %f, %f)\n", (double)vertices[i].position.x(), (double)vertices[i].position.y(), (double)vertices[i].position.z(), (double)vertices[i].position.w());
//printf("(r, g, b, a) = (%f, %f, %f, %f)\n", (double)vertices[i].colour.r(), (double)vertices[i].colour.g(), (double)vertices[i].colour.b(), (double)vertices[i].colour.a());
//printf("(u, v ) = (%f, %f)\n", vertices[i].UVs.u(), vertices[i].UVs.v());
}
// The fourth type is meant to be "Geometry primitive". TODO: Find out what that is
static constexpr std::array<OpenGL::Primitives, 4> primTypes = {
OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle
};
const auto shape = primTypes[primType];
renderer.drawVertices(shape, vertices, vertexCount);
renderer.drawVertices(primType, std::span(vertices).first(vertexCount));
}
Vertex GPU::getImmediateModeVertex() {
Vertex v;
const int totalAttrCount = (regs[PICAInternalRegs::VertexShaderAttrNum] & 0xf) + 1;
const int totalAttrCount = (regs[PICA::InternalRegs::VertexShaderAttrNum] & 0xf) + 1;
// Copy immediate mode attributes to vertex shader unit
for (int i = 0; i < totalAttrCount; i++) {
@ -240,11 +292,11 @@ Vertex GPU::getImmediateModeVertex() {
shaderUnit.vs.run();
std::memcpy(&v.position, &shaderUnit.vs.outputs[0], sizeof(vec4f));
std::memcpy(&v.colour, &shaderUnit.vs.outputs[1], sizeof(vec4f));
std::memcpy(&v.UVs, &shaderUnit.vs.outputs[2], 2 * sizeof(f24));
std::memcpy(&v.texcoord0, &shaderUnit.vs.outputs[2], 2 * sizeof(f24));
printf("(x, y, z, w) = (%f, %f, %f, %f)\n", (double)v.position.x(), (double)v.position.y(), (double)v.position.z(), (double)v.position.w());
printf("(r, g, b, a) = (%f, %f, %f, %f)\n", (double)v.colour.r(), (double)v.colour.g(), (double)v.colour.b(), (double)v.colour.a());
printf("(u, v ) = (%f, %f)\n", v.UVs.u(), v.UVs.v());
printf("(u, v ) = (%f, %f)\n", v.texcoord0.u(), v.texcoord0.v());
return v;
}

View file

@ -33,7 +33,7 @@ u32 GPU::readInternalReg(u32 index) {
}
void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
using namespace PICAInternalRegs;
using namespace PICA::InternalRegs;
if (index > regNum) {
Helpers::panic("Tried to write to invalid GPU register. Index: %X, value: %08X\n", index, value);
@ -68,7 +68,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
case ColourBufferFormat: {
u32 format = getBits<16, 3>(value);
renderer.setColourFormat(format);
renderer.setColourFormat(static_cast<PICA::ColorFmt>(format));
break;
}
@ -79,8 +79,8 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
}
case DepthBufferFormat: {
u32 fmt = value & 0x3;
renderer.setDepthFormat(fmt);
u32 format = value & 0x3;
renderer.setDepthFormat(static_cast<PICA::DepthFmt>(format));
break;
}
@ -137,7 +137,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
if (fixedAttribIndex < 12) [[likely]] {
shaderUnit.vs.fixedAttributes[fixedAttribIndex++] = attr;
} else if (fixedAttribIndex == 15) { // Otherwise if it's 15, we're submitting an immediate mode vertex
const uint totalAttrCount = (regs[PICAInternalRegs::VertexShaderAttrNum] & 0xf) + 1;
const uint totalAttrCount = (regs[PICA::InternalRegs::VertexShaderAttrNum] & 0xf) + 1;
if (totalAttrCount <= immediateModeAttrIndex) {
printf("Broken state in the immediate mode vertex submission pipeline. Failing silently\n");
immediateModeAttrIndex = 0;
@ -151,13 +151,13 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
immediateModeVertices[immediateModeVertIndex++] = v;
// Get primitive type
const u32 primConfig = regs[PICAInternalRegs::PrimitiveConfig];
const u32 primConfig = regs[PICA::InternalRegs::PrimitiveConfig];
const u32 primType = getBits<8, 2>(primConfig);
// If we've reached 3 verts, issue a draw call
// Handle rendering depending on the primitive type
if (immediateModeVertIndex == 3) {
renderer.drawVertices(OpenGL::Triangle, &immediateModeVertices[0], 3);
renderer.drawVertices(PICA::PrimType::TriangleList, immediateModeVertices);
switch (primType) {
// Triangle or geometry primitive. Draw a triangle and discard all vertices

View file

@ -3,7 +3,7 @@
namespace fs = std::filesystem;
FSResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
if (size == 0)
Helpers::panic("ExtSaveData file does not support size == 0");
@ -15,22 +15,22 @@ FSResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
p += fs::path(path.utf16_string).make_preferred();
if (fs::exists(p))
return FSResult::AlreadyExists;
return Result::FS::AlreadyExists;
// Create a file of size "size" by creating an empty one, seeking to size - 1 and just writing a 0 there
IOFile file(p.string().c_str(), "wb");
if (file.seek(size - 1, SEEK_SET) && file.writeBytes("", 1).second == 1) {
return FSResult::Success;
return Result::Success;
}
return FSResult::FileTooLarge;
return Result::FS::FileTooLarge;
}
Helpers::panic("ExtSaveDataArchive::OpenFile: Failed");
return FSResult::Success;
return Result::Success;
}
FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
HorizonResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path))
Helpers::panic("Unsafe path in ExtSaveData::DeleteFile");
@ -43,7 +43,7 @@ FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
}
if (!fs::is_regular_file(p)) {
return FSResult::FileNotFound;
return Result::FS::FileNotFoundAlt;
}
std::error_code ec;
@ -55,11 +55,11 @@ FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
Helpers::warn("ExtSaveData::DeleteFile: fs::remove failed\n");
}
return FSResult::Success;
return Result::Success;
}
Helpers::panic("ExtSaveDataArchive::DeleteFile: Unknown path type");
return FSResult::Success;
return Result::Success;
}
FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) {
@ -95,7 +95,7 @@ std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path)
return backingFolder + std::to_string(saveLow) + std::to_string(saveHigh);
}
Rust::Result<ArchiveBase*, FSResult> ExtSaveDataArchive::openArchive(const FSPath& path) {
Rust::Result<ArchiveBase*, HorizonResult> ExtSaveDataArchive::openArchive(const FSPath& path) {
if (path.type != PathType::Binary || path.binary.size() != 12) {
Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive");
}
@ -105,13 +105,13 @@ Rust::Result<ArchiveBase*, FSResult> ExtSaveDataArchive::openArchive(const FSPat
// fs::path formatInfopath = IOFile::getAppData() / "FormatInfo" / (getExtSaveDataPathFromBinary(path) + ".format");
// Format info not found so the archive is not formatted
// if (!fs::is_regular_file(formatInfopath)) {
// return isShared ? Err(FSResult::NotFormatted) : Err(FSResult::NotFoundInvalid);
// return isShared ? Err(Result::FS::NotFormatted) : Err(Result::FS::NotFoundInvalid);
//}
return Ok((ArchiveBase*)this);
}
Rust::Result<DirectorySession, FSResult> ExtSaveDataArchive::openDirectory(const FSPath& path) {
Rust::Result<DirectorySession, HorizonResult> ExtSaveDataArchive::openDirectory(const FSPath& path) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path))
Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory");
@ -121,18 +121,18 @@ Rust::Result<DirectorySession, FSResult> ExtSaveDataArchive::openDirectory(const
if (fs::is_regular_file(p)) {
printf("ExtSaveData: OpenArchive used with a file path");
return Err(FSResult::UnexpectedFileOrDir);
return Err(Result::FS::UnexpectedFileOrDir);
}
if (fs::is_directory(p)) {
return Ok(DirectorySession(this, p));
} else {
return Err(FSResult::FileNotFound);
return Err(Result::FS::FileNotFoundAlt);
}
}
Helpers::panic("ExtSaveDataArchive::OpenDirectory: Unimplemented path type");
return Err(FSResult::Success);
return Err(Result::Success);
}
std::optional<u32> ExtSaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) {

View file

@ -21,14 +21,14 @@ namespace MediaType {
};
};
FSResult NCCHArchive::createFile(const FSPath& path, u64 size) {
HorizonResult NCCHArchive::createFile(const FSPath& path, u64 size) {
Helpers::panic("[NCCH] CreateFile not yet supported");
return FSResult::Success;
return Result::Success;
}
FSResult NCCHArchive::deleteFile(const FSPath& path) {
HorizonResult NCCHArchive::deleteFile(const FSPath& path) {
Helpers::panic("[NCCH] Unimplemented DeleteFile");
return FSResult::Success;
return Result::Success;
}
FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms) {
@ -48,7 +48,7 @@ FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms)
return NoFile;
}
Rust::Result<ArchiveBase*, FSResult> NCCHArchive::openArchive(const FSPath& path) {
Rust::Result<ArchiveBase*, HorizonResult> NCCHArchive::openArchive(const FSPath& path) {
if (path.type != PathType::Binary || path.binary.size() != 16) {
Helpers::panic("NCCHArchive::OpenArchive: Invalid path");
}

View file

@ -4,7 +4,7 @@
namespace fs = std::filesystem;
FSResult SaveDataArchive::createFile(const FSPath& path, u64 size) {
HorizonResult SaveDataArchive::createFile(const FSPath& path, u64 size) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path))
Helpers::panic("Unsafe path in SaveData::CreateFile");
@ -13,28 +13,28 @@ FSResult SaveDataArchive::createFile(const FSPath& path, u64 size) {
p += fs::path(path.utf16_string).make_preferred();
if (fs::exists(p))
return FSResult::AlreadyExists;
return Result::FS::AlreadyExists;
IOFile file(p.string().c_str(), "wb");
// If the size is 0, leave the file empty and return success
if (size == 0) {
return FSResult::Success;
return Result::Success;
}
// If it is not empty, seek to size - 1 and write a 0 to create a file of size "size"
else if (file.seek(size - 1, SEEK_SET) && file.writeBytes("", 1).second == 1) {
return FSResult::Success;
return Result::Success;
}
return FSResult::FileTooLarge;
return Result::FS::FileTooLarge;
}
Helpers::panic("SaveDataArchive::OpenFile: Failed");
return FSResult::Success;
return Result::Success;
}
FSResult SaveDataArchive::createDirectory(const FSPath& path) {
HorizonResult SaveDataArchive::createDirectory(const FSPath& path) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path))
Helpers::panic("Unsafe path in SaveData::OpenFile");
@ -43,19 +43,19 @@ FSResult SaveDataArchive::createDirectory(const FSPath& path) {
p += fs::path(path.utf16_string).make_preferred();
if (fs::is_directory(p))
return FSResult::AlreadyExists;
return Result::FS::AlreadyExists;
if (fs::is_regular_file(p)) {
Helpers::panic("File path passed to SaveData::CreateDirectory");
}
bool success = fs::create_directory(p);
return success ? FSResult::Success : FSResult::UnexpectedFileOrDir;
return success ? Result::Success : Result::FS::UnexpectedFileOrDir;
} else {
Helpers::panic("Unimplemented SaveData::CreateDirectory");
}
}
FSResult SaveDataArchive::deleteFile(const FSPath& path) {
HorizonResult SaveDataArchive::deleteFile(const FSPath& path) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path))
Helpers::panic("Unsafe path in SaveData::DeleteFile");
@ -68,7 +68,7 @@ FSResult SaveDataArchive::deleteFile(const FSPath& path) {
}
if (!fs::is_regular_file(p)) {
return FSResult::FileNotFound;
return Result::FS::FileNotFoundAlt;
}
std::error_code ec;
@ -80,11 +80,11 @@ FSResult SaveDataArchive::deleteFile(const FSPath& path) {
Helpers::warn("SaveData::DeleteFile: fs::remove failed\n");
}
return FSResult::Success;
return Result::Success;
}
Helpers::panic("SaveDataArchive::DeleteFile: Unknown path type");
return FSResult::Success;
return Result::Success;
}
FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) {
@ -121,7 +121,7 @@ FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& pe
return FileError;
}
Rust::Result<DirectorySession, FSResult> SaveDataArchive::openDirectory(const FSPath& path) {
Rust::Result<DirectorySession, HorizonResult> SaveDataArchive::openDirectory(const FSPath& path) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path))
Helpers::panic("Unsafe path in SaveData::OpenDirectory");
@ -131,34 +131,34 @@ Rust::Result<DirectorySession, FSResult> SaveDataArchive::openDirectory(const FS
if (fs::is_regular_file(p)) {
printf("SaveData: OpenDirectory used with a file path");
return Err(FSResult::UnexpectedFileOrDir);
return Err(Result::FS::UnexpectedFileOrDir);
}
if (fs::is_directory(p)) {
return Ok(DirectorySession(this, p));
} else {
return Err(FSResult::FileNotFound);
return Err(Result::FS::FileNotFoundAlt);
}
}
Helpers::panic("SaveDataArchive::OpenDirectory: Unimplemented path type");
return Err(FSResult::Success);
return Err(Result::Success);
}
Rust::Result<ArchiveBase::FormatInfo, FSResult> SaveDataArchive::getFormatInfo(const FSPath& path) {
Rust::Result<ArchiveBase::FormatInfo, HorizonResult> SaveDataArchive::getFormatInfo(const FSPath& path) {
const fs::path formatInfoPath = getFormatInfoPath();
IOFile file(formatInfoPath, "rb");
// If the file failed to open somehow, we return that the archive is not formatted
if (!file.isOpen()) {
return Err(FSResult::NotFormatted);
return Err(Result::FS::NotFormatted);
}
FormatInfo ret;
auto [success, bytesRead] = file.readBytes(&ret, sizeof(FormatInfo));
if (!success || bytesRead != sizeof(FormatInfo)) {
Helpers::warn("SaveData::GetFormatInfo: Format file exists but was not properly read into the FormatInfo struct");
return Err(FSResult::NotFormatted);
return Err(Result::FS::NotFormatted);
}
return Ok(ret);
@ -168,7 +168,7 @@ void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo&
const fs::path saveDataPath = IOFile::getAppData() / "SaveData";
const fs::path formatInfoPath = getFormatInfoPath();
// Delete all contents by deleting the directory then recreating it
// Delete all contents by deleting the directory then recreating it
fs::remove_all(saveDataPath);
fs::create_directories(saveDataPath);
@ -177,16 +177,16 @@ void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo&
file.writeBytes(&info, sizeof(info));
}
Rust::Result<ArchiveBase*, FSResult> SaveDataArchive::openArchive(const FSPath& path) {
Rust::Result<ArchiveBase*, HorizonResult> SaveDataArchive::openArchive(const FSPath& path) {
if (path.type != PathType::Empty) {
Helpers::panic("Unimplemented path type for SaveData archive: %d\n", path.type);
return Err(FSResult::NotFoundInvalid);
return Err(Result::FS::NotFoundInvalid);
}
const fs::path formatInfoPath = getFormatInfoPath();
// Format info not found so the archive is not formatted
if (!fs::is_regular_file(formatInfoPath)) {
return Err(FSResult::NotFormatted);
return Err(Result::FS::NotFormatted);
}
return Ok((ArchiveBase*)this);

View file

@ -1,14 +1,14 @@
#include "fs/archive_sdmc.hpp"
#include <memory>
FSResult SDMCArchive::createFile(const FSPath& path, u64 size) {
HorizonResult SDMCArchive::createFile(const FSPath& path, u64 size) {
Helpers::panic("[SDMC] CreateFile not yet supported");
return FSResult::Success;
return Result::Success;
}
FSResult SDMCArchive::deleteFile(const FSPath& path) {
HorizonResult SDMCArchive::deleteFile(const FSPath& path) {
Helpers::panic("[SDMC] Unimplemented DeleteFile");
return FSResult::Success;
return Result::Success;
}
FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms) {
@ -16,9 +16,9 @@ FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms)
return FileError;
}
Rust::Result<ArchiveBase*, FSResult> SDMCArchive::openArchive(const FSPath& path) {
Rust::Result<ArchiveBase*, HorizonResult> SDMCArchive::openArchive(const FSPath& path) {
printf("SDMCArchive::OpenArchive: Failed\n");
return Err(FSResult::NotFormatted);
return Err(Result::FS::NotFormatted);
}
std::optional<u32> SDMCArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) {

View file

@ -9,14 +9,14 @@ namespace PathType {
};
};
FSResult SelfNCCHArchive::createFile(const FSPath& path, u64 size) {
HorizonResult SelfNCCHArchive::createFile(const FSPath& path, u64 size) {
Helpers::panic("[SelfNCCH] CreateFile not yet supported");
return FSResult::Success;
return Result::Success;
}
FSResult SelfNCCHArchive::deleteFile(const FSPath& path) {
HorizonResult SelfNCCHArchive::deleteFile(const FSPath& path) {
Helpers::panic("[SelfNCCH] Unimplemented DeleteFile");
return FSResult::Success;
return Result::Success;
}
FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& perms) {
@ -40,10 +40,10 @@ FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& pe
return NoFile; // No file descriptor needed for RomFS
}
Rust::Result<ArchiveBase*, FSResult> SelfNCCHArchive::openArchive(const FSPath& path) {
Rust::Result<ArchiveBase*, HorizonResult> SelfNCCHArchive::openArchive(const FSPath& path) {
if (path.type != PathType::Empty) {
Helpers::panic("Invalid path type for SelfNCCH archive: %d\n", path.type);
return Err(FSResult::NotFoundInvalid);
return Err(Result::FS::NotFoundInvalid);
}
return Ok((ArchiveBase*)this);

View file

@ -26,7 +26,7 @@ Handle Kernel::makeArbiter() {
// Result CreateAddressArbiter(Handle* arbiter)
void Kernel::createAddressArbiter() {
logSVC("CreateAddressArbiter\n");
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = makeArbiter();
}
@ -43,7 +43,7 @@ void Kernel::arbitrateAddress() {
const auto arbiter = getObject(handle, KernelObjectType::AddressArbiter);
if (arbiter == nullptr) [[unlikely]] {
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
@ -52,16 +52,16 @@ void Kernel::arbitrateAddress() {
}
if (type > 4) [[unlikely]] {
regs[0] = SVCResult::InvalidEnumValueAlt;
regs[0] = Result::FND::InvalidEnumValue;
return;
}
// This needs to put the error code in r0 before we change threads
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
switch (static_cast<ArbitrationType>(type)) {
// Puts this thread to sleep if word < value until another thread arbitrates the address using SIGNAL
case ArbitrationType::WaitIfLess: {
s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed
s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed
if (word < value) {
sleepThreadOnArbiter(address);
}
@ -71,7 +71,7 @@ void Kernel::arbitrateAddress() {
// Puts this thread to sleep if word < value until another thread arbitrates the address using SIGNAL
// If the thread is put to sleep, the arbiter address is decremented
case ArbitrationType::DecrementAndWaitIfLess: {
s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed
s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed
if (word < value) {
mem.write32(address, word - 1);
sleepThreadOnArbiter(address);

View file

@ -7,12 +7,6 @@ namespace DirectoryOps {
};
}
namespace Result {
enum : u32 {
Success = 0
};
}
void Kernel::handleDirectoryOperation(u32 messagePointer, Handle directory) {
const u32 cmd = mem.read32(messagePointer);
switch (cmd) {

View file

@ -38,7 +38,7 @@ bool Kernel::signalEvent(Handle handle) {
// One-shot events get cleared once they are acquired by some thread and only wake up 1 thread at a time
if (event->resetType == ResetType::OneShot) {
int index = wakeupOneThread(event->waitlist, handle); // Wake up one thread with the highest priority
event->waitlist ^= (1ull << index); // Remove thread from waitlist
event->waitlist ^= (1ull << index); // Remove thread from waitlist
event->fired = false;
} else {
wakeupAllThreads(event->waitlist, handle);
@ -64,11 +64,11 @@ void Kernel::svcCreateEvent() {
logSVC("CreateEvent(handle pointer = %08X, resetType = %s)\n", outPointer, resetTypeToString(resetType));
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = makeEvent(static_cast<ResetType>(resetType));
}
// Result ClearEvent(Handle event)
// Result ClearEvent(Handle event)
void Kernel::svcClearEvent() {
const Handle handle = regs[0];
const auto event = getObject(handle, KernelObjectType::Event);
@ -76,15 +76,15 @@ void Kernel::svcClearEvent() {
if (event == nullptr) [[unlikely]] {
Helpers::panic("Tried to clear non-existent event (handle = %X)", handle);
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
event->getData<Event>()->fired = false;
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
}
// Result SignalEvent(Handle event)
// Result SignalEvent(Handle event)
void Kernel::svcSignalEvent() {
const Handle handle = regs[0];
logSVC("SignalEvent(event handle = %X)\n", handle);
@ -92,15 +92,15 @@ void Kernel::svcSignalEvent() {
if (object == nullptr) {
Helpers::panic("Signalled non-existent event: %X\n", handle);
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
} else {
// We must signalEvent after setting r0, otherwise the r0 of the new thread will ne corrupted
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
signalEvent(handle);
}
}
// Result WaitSynchronization1(Handle handle, s64 timeout_nanoseconds)
// Result WaitSynchronization1(Handle handle, s64 timeout_nanoseconds)
void Kernel::waitSynchronization1() {
const Handle handle = regs[0];
const s64 ns = s64(u64(regs[1]) | (u64(regs[2]) << 32));
@ -110,7 +110,7 @@ void Kernel::waitSynchronization1() {
if (object == nullptr) [[unlikely]] {
Helpers::panic("WaitSynchronization1: Bad event handle %X\n", handle);
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
@ -120,16 +120,16 @@ void Kernel::waitSynchronization1() {
if (!shouldWaitOnObject(object)) {
acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
rescheduleThreads();
} else {
// Timeout is 0, don't bother waiting, instantly timeout
if (ns == 0) {
regs[0] = SVCResult::Timeout;
regs[0] = Result::OS::Timeout;
return;
}
regs[0] = SVCResult::Timeout; // This will be overwritten with success if we don't timeout
regs[0] = Result::OS::Timeout; // This will be overwritten with success if we don't timeout
auto& t = threads[currentThreadIndex];
t.waitList.resize(1);
@ -180,7 +180,7 @@ void Kernel::waitSynchronizationN() {
// Panic if one of the objects is not even an object
if (object == nullptr) [[unlikely]] {
Helpers::panic("WaitSynchronizationN: Bad object handle %X\n", handle);
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
@ -206,16 +206,16 @@ void Kernel::waitSynchronizationN() {
// We only need to wait on one object. Easy...?!
if (!waitAll) {
// If there's ready objects, acquire the first one and return
// If there's ready objects, acquire the first one and return
if (oneObjectReady) {
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = firstReadyObjectIndex; // Return index of the acquired object
acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object
rescheduleThreads();
return;
}
regs[0] = SVCResult::Timeout; // This will be overwritten with success if we don't timeout
regs[0] = Result::OS::Timeout; // This will be overwritten with success if we don't timeout
// If the thread wakes up without timeout, this will be adjusted to the index of the handle that woke us up
regs[1] = 0xFFFFFFFF;
t.waitList.resize(handleCount);
@ -223,7 +223,7 @@ void Kernel::waitSynchronizationN() {
t.outPointer = outPointer;
t.waitingNanoseconds = ns;
t.sleepTick = cpu.getTicks();
for (s32 i = 0; i < handleCount; i++) {
t.waitList[i] = waitObjects[i].first; // Add object to this thread's waitlist
waitObjects[i].second->getWaitlist() |= (1ull << currentThreadIndex); // And add the thread to the object's waitlist

View file

@ -14,12 +14,6 @@ namespace FileOps {
};
}
namespace Result {
enum : u32 {
Success = 0
};
}
void Kernel::handleFileOperation(u32 messagePointer, Handle file) {
const u32 cmd = mem.read32(messagePointer);
@ -90,7 +84,7 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) {
if (!file->isOpen) {
Helpers::panic("Tried to read closed file");
}
// Handle files with their own file descriptors by just fread'ing the data
if (file->fd) {
std::unique_ptr<u8[]> data(new u8[size]);

View file

@ -149,7 +149,7 @@ u32 Kernel::getTLSPointer() {
// Result CloseHandle(Handle handle)
void Kernel::svcCloseHandle() {
logSVC("CloseHandle(handle = %d) (Unimplemented)\n", regs[0]);
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
}
// u64 GetSystemTick()
@ -169,7 +169,7 @@ void Kernel::outputDebugString() {
std::string message = mem.readString(pointer, size);
logDebugString("[OutputDebugString] %s\n", message.c_str());
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
}
void Kernel::getProcessID() {
@ -178,11 +178,11 @@ void Kernel::getProcessID() {
logSVC("GetProcessID(process: %s)\n", getProcessName(pid).c_str());
if (process == nullptr) [[unlikely]] {
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = process->getData<Process>()->id;
}
@ -194,7 +194,7 @@ void Kernel::getProcessInfo() {
logSVC("GetProcessInfo(process: %s, type = %d)\n", getProcessName(pid).c_str(), type);
if (process == nullptr) [[unlikely]] {
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
@ -215,7 +215,7 @@ void Kernel::getProcessInfo() {
Helpers::panic("GetProcessInfo: unimplemented type %d", type);
}
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
}
// Result DuplicateHandle(Handle* out, Handle original)
@ -224,7 +224,7 @@ void Kernel::duplicateHandle() {
logSVC("DuplicateHandle(handle = %X)\n", original);
if (original == KernelHandles::CurrentThread) {
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
Handle ret = makeObject(KernelObjectType::Thread);
objects[ret].data = &threads[currentThreadIndex];

View file

@ -36,7 +36,7 @@ static constexpr bool isAligned(u32 value) {
return (value & 0xFFF) == 0;
}
// Result ControlMemory(u32* outaddr, u32 addr0, u32 addr1, u32 size,
// Result ControlMemory(u32* outaddr, u32 addr0, u32 addr1, u32 size,
// MemoryOperation operation, MemoryPermission permissions)
// This has a weird ABI documented here https://www.3dbrew.org/wiki/Kernel_ABI
// TODO: Does this need to write to outaddr?
@ -64,7 +64,7 @@ void Kernel::controlMemory() {
if (!isAligned(addr0) || !isAligned(addr1) || !isAligned(size)) {
Helpers::panic("ControlMemory: Unaligned parameters\nAddr0: %08X\nAddr1: %08X\nSize: %08X", addr0, addr1, size);
}
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" : ""
);
@ -90,7 +90,7 @@ void Kernel::controlMemory() {
default: Helpers::panic("ControlMemory: unknown operation %X\n", operation);
}
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
}
// Result QueryMemory(MemoryInfo* memInfo, PageInfo* pageInfo, u32 addr)
@ -102,7 +102,7 @@ void Kernel::queryMemory() {
logSVC("QueryMemory(mem info pointer = %08X, page info pointer = %08X, addr = %08X)\n", memInfo, pageInfo, addr);
const auto info = mem.queryMemory(addr);
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = info.baseAddr;
regs[2] = info.size;
regs[3] = info.perms;
@ -110,7 +110,7 @@ void Kernel::queryMemory() {
regs[5] = 0; // page flags
}
// Result MapMemoryBlock(Handle memblock, u32 addr, MemoryPermission myPermissions, MemoryPermission otherPermission)
// Result MapMemoryBlock(Handle memblock, u32 addr, MemoryPermission myPermissions, MemoryPermission otherPermission)
void Kernel::mapMemoryBlock() {
const Handle block = regs[0];
u32 addr = regs[1];
@ -146,7 +146,7 @@ void Kernel::mapMemoryBlock() {
Helpers::panic("MapMemoryBlock where the handle does not refer to a known piece of kernel shared mem");
}
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
}
Handle Kernel::makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission) {
@ -180,13 +180,13 @@ void Kernel::createMemoryBlock() {
// Throw error if the size of the shared memory block is not aligned to page boundary
if (!isAligned(size)) {
regs[0] = SVCResult::UnalignedSize;
regs[0] = Result::OS::MisalignedSize;
return;
}
// Throw error if one of the permissions is not valid
if (!isPermValid(myPermission) || !isPermValid(otherPermission)) {
regs[0] = SVCResult::InvalidCombination;
regs[0] = Result::OS::InvalidCombination;
return;
}
@ -199,6 +199,6 @@ void Kernel::createMemoryBlock() {
if (myPermission == MemoryPermissions::DontCare) myPermission = MemoryPermissions::ReadWrite;
if (otherPermission == MemoryPermissions::DontCare) otherPermission = MemoryPermissions::ReadWrite;
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = makeMemoryBlock(addr, size, myPermission, otherPermission);
}

View file

@ -43,7 +43,7 @@ void Kernel::connectToPort() {
if (port.size() > Port::maxNameLen) {
Helpers::panic("ConnectToPort: Port name too long\n");
regs[0] = SVCResult::PortNameTooLong;
regs[0] = Result::OS::PortNameTooLong;
return;
}
@ -51,7 +51,7 @@ void Kernel::connectToPort() {
std::optional<Handle> optionalHandle = getPortHandle(port.c_str());
if (!optionalHandle.has_value()) [[unlikely]] {
Helpers::panic("ConnectToPort: Port doesn't exist\n");
regs[0] = SVCResult::ObjectNotFound;
regs[0] = Result::Kernel::NotFound;
return;
}
@ -65,7 +65,7 @@ void Kernel::connectToPort() {
// TODO: Actually create session
Handle sessionHandle = makeSession(portHandle);
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = sessionHandle;
}
@ -80,7 +80,7 @@ void Kernel::sendSyncRequest() {
if (KernelHandles::isServiceHandle(handle)) {
// The service call might cause a reschedule and change threads. Hence, set r0 before executing the service call
// Because if the service call goes first, we might corrupt the new thread's r0!!
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
serviceManager.sendCommandToService(messagePointer, handle);
return;
}
@ -88,7 +88,7 @@ void Kernel::sendSyncRequest() {
// Check if our sync request is targetting a file instead of a service
bool isFileOperation = getObject(handle, KernelObjectType::File) != nullptr;
if (isFileOperation) {
regs[0] = SVCResult::Success; // r0 goes first here too
regs[0] = Result::Success; // r0 goes first here too
handleFileOperation(messagePointer, handle);
return;
}
@ -96,7 +96,7 @@ void Kernel::sendSyncRequest() {
// Check if our sync request is targetting a directory instead of a service
bool isDirectoryOperation = getObject(handle, KernelObjectType::Directory) != nullptr;
if (isDirectoryOperation) {
regs[0] = SVCResult::Success; // r0 goes first here too
regs[0] = Result::Success; // r0 goes first here too
handleDirectoryOperation(messagePointer, handle);
return;
}
@ -105,7 +105,7 @@ void Kernel::sendSyncRequest() {
const auto session = getObject(handle, KernelObjectType::Session);
if (session == nullptr) [[unlikely]] {
Helpers::panic("SendSyncRequest: Invalid handle");
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
@ -113,10 +113,10 @@ void Kernel::sendSyncRequest() {
const Handle portHandle = sessionData->portHandle;
if (portHandle == srvHandle) { // Special-case SendSyncRequest targetting the "srv: port"
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
serviceManager.handleSyncRequest(messagePointer);
} else if (portHandle == errorPortHandle) { // Special-case "err:f" for juicy logs too
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
handleErrorSyncRequest(messagePointer);
} else {
const auto portData = objects[portHandle].getData<Port>();

View file

@ -10,13 +10,13 @@ void Kernel::getResourceLimit() {
logSVC("GetResourceLimit (handle pointer = %08X, process: %s)\n", handlePointer, getProcessName(pid).c_str());
if (process == nullptr) [[unlikely]] {
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
const auto processData = static_cast<Process*>(process->data);
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = processData->limits.handle;
}
@ -29,7 +29,7 @@ void Kernel::getResourceLimitLimitValues() {
const KernelObject* limit = getObject(resourceLimit, KernelObjectType::ResourceLimit);
if (limit == nullptr) [[unlikely]] {
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
@ -46,7 +46,7 @@ void Kernel::getResourceLimitLimitValues() {
count--;
}
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
}
// Result GetResourceLimitCurrentValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount)
@ -59,7 +59,7 @@ void Kernel::getResourceLimitCurrentValues() {
const KernelObject* limit = getObject(resourceLimit, KernelObjectType::ResourceLimit);
if (limit == nullptr) [[unlikely]] {
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
@ -75,7 +75,7 @@ void Kernel::getResourceLimitCurrentValues() {
count--;
}
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
}
s32 Kernel::getCurrentResourceValue(const KernelObject* limit, u32 resourceName) {

View file

@ -1,8 +1,11 @@
#include <bit>
#include <cassert>
#include <cstring>
#include "kernel.hpp"
#include "arm_defs.hpp"
// This header needs to be included because I did stupid forward decl hack so the kernel and CPU can both access each other
#include "kernel.hpp"
// This header needs to be included because I did stupid forward decl hack so the kernel and CPU can both access each
// other
#include "cpu.hpp"
#include "resource_limits.hpp"
@ -20,17 +23,17 @@ void Kernel::switchThread(int newThreadIndex) {
}
// Backup context
std::memcpy(&oldThread.gprs[0], &cpu.regs()[0], 16 * sizeof(u32)); // Backup the 16 GPRs
std::memcpy(&oldThread.fprs[0], &cpu.fprs()[0], 32 * sizeof(u32)); // Backup the 32 FPRs
oldThread.cpsr = cpu.getCPSR(); // Backup CPSR
oldThread.fpscr = cpu.getFPSCR(); // Backup FPSCR
std::memcpy(oldThread.gprs.data(), cpu.regs().data(), cpu.regs().size_bytes()); // Backup the 16 GPRs
std::memcpy(oldThread.fprs.data(), cpu.fprs().data(), cpu.fprs().size_bytes()); // Backup the 32 FPRs
oldThread.cpsr = cpu.getCPSR(); // Backup CPSR
oldThread.fpscr = cpu.getFPSCR(); // Backup FPSCR
// Load new context
std::memcpy(&cpu.regs()[0], &newThread.gprs[0], 16 * sizeof(u32)); // Load 16 GPRs
std::memcpy(&cpu.fprs()[0], &newThread.fprs[0], 32 * sizeof(u32)); // Load 32 FPRs
cpu.setCPSR(newThread.cpsr); // Load CPSR
cpu.setFPSCR(newThread.fpscr); // Load FPSCR
cpu.setTLSBase(newThread.tlsBase); // Load CP15 thread-local-storage pointer register
std::memcpy(cpu.regs().data(), newThread.gprs.data(), cpu.regs().size_bytes()); // Load 16 GPRs
std::memcpy(cpu.fprs().data(), newThread.fprs.data(), cpu.fprs().size_bytes()); // Load 32 FPRs
cpu.setCPSR(newThread.cpsr); // Load CPSR
cpu.setFPSCR(newThread.fpscr); // Load FPSCR
cpu.setTLSBase(newThread.tlsBase); // Load CP15 thread-local-storage pointer register
currentThreadIndex = newThreadIndex;
}
@ -47,7 +50,7 @@ void Kernel::sortThreads() {
bool Kernel::canThreadRun(const Thread& t) {
if (t.status == ThreadStatus::Ready) {
return true;
} else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1
} else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1
|| t.status == ThreadStatus::WaitSyncAny || t.status == ThreadStatus::WaitSyncAll) {
const u64 elapsedTicks = cpu.getTicks() - t.sleepTick;
@ -81,7 +84,7 @@ std::optional<int> Kernel::getNextThread() {
void Kernel::switchToNextThread() {
std::optional<int> newThreadIndex = getNextThread();
if (!newThreadIndex.has_value()) {
log("Kernel tried to switch to the next thread but none found. Switching to random thread\n");
assert(aliveThreadCount != 0);
@ -101,7 +104,7 @@ void Kernel::switchToNextThread() {
// See if there;s a higher priority, ready thread and switch to that
void Kernel::rescheduleThreads() {
std::optional<int> newThreadIndex = getNextThread();
if (newThreadIndex.has_value() && newThreadIndex.value() != currentThreadIndex) {
threads[currentThreadIndex].status = ThreadStatus::Ready;
switchThread(newThreadIndex.value());
@ -273,12 +276,12 @@ int Kernel::wakeupOneThread(u64 waitlist, Handle handle) {
switch (t.status) {
case ThreadStatus::WaitSync1:
t.status = ThreadStatus::Ready;
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
break;
case ThreadStatus::WaitSyncAny:
t.status = ThreadStatus::Ready;
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
// Get the index of the event in the object's waitlist, write it to r1
for (size_t i = 0; i < t.waitList.size(); i++) {
@ -308,12 +311,12 @@ void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) {
switch (t.status) {
case ThreadStatus::WaitSync1:
t.status = ThreadStatus::Ready;
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
break;
case ThreadStatus::WaitSyncAny:
t.status = ThreadStatus::Ready;
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
// Get the index of the event in the object's waitlist, write it to r1
for (size_t i = 0; i < t.waitList.size(); i++) {
@ -352,7 +355,7 @@ void Kernel::sleepThread(s64 ns) {
}
}
// Result CreateThread(s32 priority, ThreadFunc entrypoint, u32 arg, u32 stacktop, s32 threadPriority, s32 processorID)
// Result CreateThread(s32 priority, ThreadFunc entrypoint, u32 arg, u32 stacktop, s32 threadPriority, s32 processorID)
void Kernel::createThread() {
u32 priority = regs[0];
u32 entrypoint = regs[1];
@ -365,11 +368,11 @@ void Kernel::createThread() {
if (priority > 0x3F) [[unlikely]] {
Helpers::panic("Created thread with bad priority value %X", priority);
regs[0] = SVCResult::BadThreadPriority;
regs[0] = Result::OS::OutOfRange;
return;
}
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = makeThread(entrypoint, initialSP, priority, id, arg, ThreadStatus::Ready);
rescheduleThreads();
}
@ -379,7 +382,7 @@ void Kernel::svcSleepThread() {
const s64 ns = s64(u64(regs[0]) | (u64(regs[1]) << 32));
//logSVC("SleepThread(ns = %lld)\n", ns);
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
sleepThread(ns);
}
@ -388,18 +391,18 @@ void Kernel::getThreadID() {
logSVC("GetThreadID(handle = %X)\n", handle);
if (handle == KernelHandles::CurrentThread) {
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = currentThreadIndex;
return;
}
const auto thread = getObject(handle, KernelObjectType::Thread);
if (thread == nullptr) [[unlikely]] {
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = thread->getData<Thread>()->index;
}
@ -408,14 +411,14 @@ void Kernel::getThreadPriority() {
logSVC("GetThreadPriority (handle = %X)\n", handle);
if (handle == KernelHandles::CurrentThread) {
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = threads[currentThreadIndex].priority;
} else {
auto object = getObject(handle, KernelObjectType::Thread);
if (object == nullptr) [[unlikely]] {
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
} else {
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = object->getData<Thread>()->priority;
}
}
@ -427,20 +430,20 @@ void Kernel::setThreadPriority() {
logSVC("SetThreadPriority (handle = %X, priority = %X)\n", handle, priority);
if (priority > 0x3F) {
regs[0] = SVCResult::BadThreadPriority;
regs[0] = Result::OS::OutOfRange;
return;
}
if (handle == KernelHandles::CurrentThread) {
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
threads[currentThreadIndex].priority = priority;
} else {
auto object = getObject(handle, KernelObjectType::Thread);
if (object == nullptr) [[unlikely]] {
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
} else {
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
object->getData<Thread>()->priority = priority;
}
}
@ -476,7 +479,7 @@ void Kernel::svcCreateMutex() {
bool locked = regs[1] != 0;
logSVC("CreateMutex (locked = %s)\n", locked ? "yes" : "no");
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = makeMutex(locked);
}
@ -487,18 +490,18 @@ void Kernel::svcReleaseMutex() {
const auto object = getObject(handle, KernelObjectType::Mutex);
if (object == nullptr) [[unlikely]] {
Helpers::panic("Tried to release non-existent mutex");
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
Mutex* moo = object->getData<Mutex>();
// A thread can't release a mutex it does not own
if (!moo->locked || moo->ownerThread != currentThreadIndex) {
regs[0] = SVCResult::InvalidMutexRelease;
regs[0] = Result::Kernel::InvalidMutexRelease;
return;
}
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
releaseMutex(moo);
}
@ -513,7 +516,7 @@ void Kernel::svcCreateSemaphore() {
if (initialCount < 0 || maxCount < 0)
Helpers::panic("CreateSemaphore: Negative count value");
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = makeSemaphore(initialCount, maxCount);
}
@ -525,7 +528,7 @@ void Kernel::svcReleaseSemaphore() {
const auto object = getObject(handle, KernelObjectType::Semaphore);
if (object == nullptr) [[unlikely]] {
Helpers::panic("Tried to release non-existent semaphore");
regs[0] = SVCResult::BadHandle;
regs[0] = Result::Kernel::InvalidHandle;
return;
}
@ -537,7 +540,7 @@ void Kernel::svcReleaseSemaphore() {
Helpers::panic("ReleaseSemaphore: Release count too high");
// Write success and old available count to r0 and r1 respectively
regs[0] = SVCResult::Success;
regs[0] = Result::Success;
regs[1] = s->availableCount;
// Bump available count
s->availableCount += releaseCount;

View file

@ -11,45 +11,221 @@ using namespace Helpers;
const char* vertexShader = R"(
#version 410 core
layout (location = 0) in vec4 coords;
layout (location = 1) in vec4 vertexColour;
layout (location = 2) in vec2 inUVs_texture0;
layout (location = 0) in vec4 a_coords;
layout (location = 1) in vec4 a_vertexColour;
layout (location = 2) in vec2 a_texcoord0;
layout (location = 3) in vec2 a_texcoord1;
layout (location = 4) in float a_texcoord0_w;
layout (location = 5) in vec2 a_texcoord2;
out vec4 colour;
out vec2 tex0_UVs;
out vec4 v_colour;
out vec3 v_texcoord0;
out vec2 v_texcoord1;
out vec2 v_texcoord2;
flat out vec4 v_textureEnvColor[6];
flat out vec4 v_textureEnvBufferColor;
// TEV uniforms
uniform uint u_textureEnvColor[6];
uniform uint u_textureEnvBufferColor;
vec4 abgr8888ToVec4(uint abgr) {
const float scale = 1.0 / 255.0;
return vec4(
scale * float(abgr & 0xffu),
scale * float((abgr >> 8) & 0xffu),
scale * float((abgr >> 16) & 0xffu),
scale * float(abgr >> 24)
);
}
void main() {
gl_Position = coords;
colour = vertexColour;
gl_Position = a_coords;
v_colour = a_vertexColour;
// Flip y axis of UVs because OpenGL uses an inverted y for texture sampling compared to the PICA
tex0_UVs = vec2(inUVs_texture0.x, 1.0 - inUVs_texture0.y);
v_texcoord0 = vec3(a_texcoord0.x, 1.0 - a_texcoord0.y, a_texcoord0_w);
v_texcoord1 = vec2(a_texcoord1.x, 1.0 - a_texcoord1.y);
v_texcoord2 = vec2(a_texcoord2.x, 1.0 - a_texcoord2.y);
for (int i = 0; i < 6; i++) {
v_textureEnvColor[i] = abgr8888ToVec4(u_textureEnvColor[i]);
}
v_textureEnvBufferColor = abgr8888ToVec4(u_textureEnvBufferColor);
}
)";
const char* fragmentShader = R"(
#version 410 core
in vec4 colour;
in vec2 tex0_UVs;
in vec4 v_colour;
in vec3 v_texcoord0;
in vec2 v_texcoord1;
in vec2 v_texcoord2;
flat in vec4 v_textureEnvColor[6];
flat in vec4 v_textureEnvBufferColor;
out vec4 fragColour;
uniform uint u_alphaControl;
uniform uint u_textureConfig;
// TEV uniforms
uniform uint u_textureEnvSource[6];
uniform uint u_textureEnvOperand[6];
uniform uint u_textureEnvCombiner[6];
uniform uint u_textureEnvScale[6];
uniform uint u_textureEnvUpdateBuffer;
// Depth control uniforms
uniform float u_depthScale;
uniform float u_depthOffset;
uniform bool u_depthmapEnable;
uniform sampler2D u_tex0;
uniform sampler2D u_tex1;
uniform sampler2D u_tex2;
vec4 tevSources[16];
vec4 tevNextPreviousBuffer;
bool tevUnimplementedSourceFlag = false;
// OpenGL ES 1.1 reference pages for TEVs (this is what the PICA200 implements):
// https://registry.khronos.org/OpenGL-Refpages/es1.1/xhtml/glTexEnv.xml
vec4 tevFetchSource(uint src_id) {
if (src_id >= 6u && src_id < 13u) {
tevUnimplementedSourceFlag = true;
}
return tevSources[src_id];
}
vec4 tevGetColorAndAlphaSource(int tev_id, int src_id) {
vec4 result;
vec4 colorSource = tevFetchSource((u_textureEnvSource[tev_id] >> (src_id * 4)) & 15u);
vec4 alphaSource = tevFetchSource((u_textureEnvSource[tev_id] >> (src_id * 4 + 16)) & 15u);
uint colorOperand = (u_textureEnvOperand[tev_id] >> (src_id * 4)) & 15u;
uint alphaOperand = (u_textureEnvOperand[tev_id] >> (12 + src_id * 4)) & 7u;
// TODO: figure out what the undocumented values do
switch (colorOperand) {
case 0u: result.rgb = colorSource.rgb; break; // Source color
case 1u: result.rgb = 1.0 - colorSource.rgb; break; // One minus source color
case 2u: result.rgb = vec3(colorSource.a); break; // Source alpha
case 3u: result.rgb = vec3(1.0 - colorSource.a); break; // One minus source alpha
case 4u: result.rgb = vec3(colorSource.r); break; // Source red
case 5u: result.rgb = vec3(1.0 - colorSource.r); break; // One minus source red
case 8u: result.rgb = vec3(colorSource.g); break; // Source green
case 9u: result.rgb = vec3(1.0 - colorSource.g); break; // One minus source green
case 12u: result.rgb = vec3(colorSource.b); break; // Source blue
case 13u: result.rgb = vec3(1.0 - colorSource.b); break; // One minus source blue
default: break;
}
// TODO: figure out what the undocumented values do
switch (alphaOperand) {
case 0u: result.a = alphaSource.a; break; // Source alpha
case 1u: result.a = 1.0 - alphaSource.a; break; // One minus source alpha
case 2u: result.a = alphaSource.r; break; // Source red
case 3u: result.a = 1.0 - alphaSource.r; break; // One minus source red
case 4u: result.a = alphaSource.g; break; // Source green
case 5u: result.a = 1.0 - alphaSource.g; break; // One minus source green
case 6u: result.a = alphaSource.b; break; // Source blue
case 7u: result.a = 1.0 - alphaSource.b; break; // One minus source blue
default: break;
}
return result;
}
vec4 tevCalculateCombiner(int tev_id) {
vec4 source0 = tevGetColorAndAlphaSource(tev_id, 0);
vec4 source1 = tevGetColorAndAlphaSource(tev_id, 1);
vec4 source2 = tevGetColorAndAlphaSource(tev_id, 2);
uint colorCombine = u_textureEnvCombiner[tev_id] & 15u;
uint alphaCombine = (u_textureEnvCombiner[tev_id] >> 16) & 15u;
vec4 result = vec4(1.0);
// TODO: figure out what the undocumented values do
switch (colorCombine) {
case 0u: result.rgb = source0.rgb; break; // Replace
case 1u: result.rgb = source0.rgb * source1.rgb; break; // Modulate
case 2u: result.rgb = min(vec3(1.0), source0.rgb + source1.rgb); break; // Add
case 3u: result.rgb = clamp(source0.rgb + source1.rgb - 0.5, 0.0, 1.0); break; // Add signed
case 4u: result.rgb = mix(source1.rgb, source0.rgb, source2.rgb); break; // Interpolate
case 5u: result.rgb = max(source0.rgb - source1.rgb, 0.0); break; // Subtract
case 6u: result.rgb = vec3(4.0 * dot(source0.rgb - 0.5 , source1.rgb - 0.5)); break; // Dot3 RGB
case 7u: result = vec4(4.0 * dot(source0.rgb - 0.5 , source1.rgb - 0.5)); break; // Dot3 RGBA
case 8u: result.rgb = min(source0.rgb * source1.rgb + source2.rgb, 1.0); break; // Multiply then add
case 9u: result.rgb = min((source0.rgb + source1.rgb) * source2.rgb, 1.0); break; // Add then multiply
default: break;
}
if (colorCombine != 7u) { // The color combiner also writes the alpha channel in the "Dot3 RGBA" mode.
// TODO: figure out what the undocumented values do
// TODO: test if the alpha combiner supports all the same modes as the color combiner.
switch (alphaCombine) {
case 0u: result.a = source0.a; break; // Replace
case 1u: result.a = source0.a * source1.a; break; // Modulate
case 2u: result.a = min(1.0, source0.a + source1.a); break; // Add
case 3u: result.a = clamp(source0.a + source1.a - 0.5, 0.0, 1.0); break; // Add signed
case 4u: result.a = mix(source1.a, source0.a, source2.a); break; // Interpolate
case 5u: result.a = max(0.0, source0.a - source1.a); break; // Subtract
case 8u: result.a = min(1.0, source0.a * source1.a + source2.a); break; // Multiply then add
case 9u: result.a = min(1.0, (source0.a + source1.a) * source2.a); break; // Add then multiply
default: break;
}
}
result.rgb *= float(1 << (u_textureEnvScale[tev_id] & 3u));
result.a *= float(1 << ((u_textureEnvScale[tev_id] >> 16) & 3u));
return result;
}
void main() {
if ((u_textureConfig & 1u) != 0) { // Render texture 0 if enabled
fragColour = texture(u_tex0, tex0_UVs);
} else {
fragColour = colour;
vec2 tex2UV = (u_textureConfig & (1u << 13)) != 0u ? v_texcoord1 : v_texcoord2;
// TODO: what do invalid sources and disabled textures read as?
// And what does the "previous combiner" source read initially?
tevSources[0] = v_colour; // Primary/vertex color
tevSources[1] = vec4(vec3(0.5), 1.0); // Fragment primary color
tevSources[2] = vec4(vec3(0.5), 1.0); // Fragment secondary color
if ((u_textureConfig & 1u) != 0u) tevSources[3] = texture(u_tex0, v_texcoord0.xy);
if ((u_textureConfig & 2u) != 0u) tevSources[4] = texture(u_tex1, v_texcoord1);
if ((u_textureConfig & 4u) != 0u) tevSources[5] = texture(u_tex2, tex2UV);
tevSources[13] = vec4(0.0); // Previous buffer
tevSources[15] = vec4(0.0); // Previous combiner
tevNextPreviousBuffer = v_textureEnvBufferColor;
for (int i = 0; i < 6; i++) {
tevSources[14] = v_textureEnvColor[i]; // Constant color
tevSources[15] = tevCalculateCombiner(i);
tevSources[13] = tevNextPreviousBuffer;
if (i < 4) {
if ((u_textureEnvUpdateBuffer & (0x100u << i)) != 0u) {
tevNextPreviousBuffer.rgb = tevSources[15].rgb;
}
if ((u_textureEnvUpdateBuffer & (0x1000u << i)) != 0u) {
tevNextPreviousBuffer.a = tevSources[15].a;
}
}
}
fragColour = tevSources[15];
if (tevUnimplementedSourceFlag) {
// fragColour = vec4(1.0, 0.0, 1.0, 1.0);
}
// Get original depth value by converting from [near, far] = [0, 1] to [-1, 1]
@ -119,10 +295,10 @@ const char* displayVertexShader = R"(
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];
UV = texcoords[gl_VertexID];
}
)";
@ -144,10 +320,10 @@ void Renderer::reset() {
// Init the colour/depth buffer settings to some random defaults on reset
colourBufferLoc = 0;
colourBufferFormat = ColourBuffer::Formats::RGBA8;
colourBufferFormat = PICA::ColorFmt::RGBA8;
depthBufferLoc = 0;
depthBufferFormat = DepthBuffer::Formats::Depth16;
depthBufferFormat = PICA::DepthFmt::Depth16;
if (triangleProgram.exists()) {
const auto oldProgram = OpenGL::getProgram();
@ -179,16 +355,28 @@ void Renderer::initGraphicsContext() {
alphaControlLoc = OpenGL::uniformLocation(triangleProgram, "u_alphaControl");
texUnitConfigLoc = OpenGL::uniformLocation(triangleProgram, "u_textureConfig");
textureEnvSourceLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvSource");
textureEnvOperandLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvOperand");
textureEnvCombinerLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvCombiner");
textureEnvColorLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvColor");
textureEnvScaleLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvScale");
textureEnvUpdateBufferLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvUpdateBuffer");
textureEnvBufferColorLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvBufferColor");
depthScaleLoc = OpenGL::uniformLocation(triangleProgram, "u_depthScale");
depthOffsetLoc = OpenGL::uniformLocation(triangleProgram, "u_depthOffset");
depthmapEnableLoc = OpenGL::uniformLocation(triangleProgram, "u_depthmapEnable");
glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex0"), 0); // Init sampler object
// Init sampler objects
glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex0"), 0);
glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex1"), 1);
glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex2"), 2);
OpenGL::Shader vertDisplay(displayVertexShader, OpenGL::Vertex);
OpenGL::Shader fragDisplay(displayFragmentShader, OpenGL::Fragment);
displayProgram.create({ vertDisplay, fragDisplay });
displayProgram.use();
displayProgram.use();
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object
vbo.createFixedSize(sizeof(Vertex) * vertexBufferSize, GL_STREAM_DRAW);
@ -202,27 +390,53 @@ void Renderer::initGraphicsContext() {
// Colour attribute
vao.setAttributeFloat<float>(1, 4, sizeof(Vertex), offsetof(Vertex, colour));
vao.enableAttribute(1);
// UV attribute
vao.setAttributeFloat<float>(2, 2, sizeof(Vertex), offsetof(Vertex, UVs));
// UV 0 attribute
vao.setAttributeFloat<float>(2, 2, sizeof(Vertex), offsetof(Vertex, texcoord0));
vao.enableAttribute(2);
// UV 1 attribute
vao.setAttributeFloat<float>(3, 2, sizeof(Vertex), offsetof(Vertex, texcoord1));
vao.enableAttribute(3);
// UV 0 W-component attribute
vao.setAttributeFloat<float>(4, 1, sizeof(Vertex), offsetof(Vertex, texcoord0_w));
vao.enableAttribute(4);
// UV 2 attribute
vao.setAttributeFloat<float>(5, 2, sizeof(Vertex), offsetof(Vertex, texcoord2));
vao.enableAttribute(5);
dummyVBO.create();
dummyVAO.create();
// Create texture and framebuffer for the 3DS screen
const u32 screenTextureWidth = 2 * 400; // Top screen is 400 pixels wide, bottom is 320
const u32 screenTextureHeight = 2 * 240; // Both screens are 240 pixels tall
auto prevTexture = OpenGL::getTex2D();
screenTexture.create(screenTextureWidth, screenTextureHeight, GL_RGBA8);
screenTexture.bind();
screenTexture.setMinFilter(OpenGL::Linear);
screenTexture.setMagFilter(OpenGL::Linear);
glBindTexture(GL_TEXTURE_2D, prevTexture);
screenFramebuffer.createWithDrawTexture(screenTexture);
screenFramebuffer.bind(OpenGL::DrawAndReadFramebuffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
Helpers::panic("Incomplete framebuffer");
// TODO: This should not clear the framebuffer contents. It should load them from VRAM.
GLint oldViewport[4];
glGetIntegerv(GL_VIEWPORT, oldViewport);
OpenGL::setViewport(screenTextureWidth, screenTextureHeight);
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0);
OpenGL::clearColor();
OpenGL::setViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
reset();
}
void Renderer::getGraphicsContext() {
OpenGL::disableScissor();
OpenGL::setViewport(400, 240);
vbo.bind();
vao.bind();
triangleProgram.use();
}
// Set up the OpenGL blending context to match the emulated PICA
void Renderer::setupBlending() {
const bool blendingEnabled = (regs[PICAInternalRegs::ColourOperation] & (1 << 8)) != 0;
const bool blendingEnabled = (regs[PICA::InternalRegs::ColourOperation] & (1 << 8)) != 0;
// Map of PICA blending equations to OpenGL blending equations. The unused blending equations are equivalent to equation 0 (add)
static constexpr std::array<GLenum, 8> blendingEquations = {
@ -242,7 +456,7 @@ void Renderer::setupBlending() {
OpenGL::enableBlend();
// Get blending equations
const u32 blendControl = regs[PICAInternalRegs::BlendFunc];
const u32 blendControl = regs[PICA::InternalRegs::BlendFunc];
const u32 rgbEquation = blendControl & 0x7;
const u32 alphaEquation = getBits<8, 3>(blendControl);
@ -252,7 +466,7 @@ void Renderer::setupBlending() {
const u32 alphaSourceFunc = getBits<24, 4>(blendControl);
const u32 alphaDestFunc = getBits<28, 4>(blendControl);
const u32 constantColor = regs[PICAInternalRegs::BlendColour];
const u32 constantColor = regs[PICA::InternalRegs::BlendColour];
const u32 r = constantColor & 0xff;
const u32 g = getBits<8, 8>(constantColor);
const u32 b = getBits<16, 8>(constantColor);
@ -265,9 +479,93 @@ void Renderer::setupBlending() {
}
}
void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count) {
void Renderer::setupTextureEnvState() {
// TODO: Only update uniforms when the TEV config changed. Use an UBO potentially.
static constexpr std::array<u32, 6> ioBases = {
PICA::InternalRegs::TexEnv0Source, PICA::InternalRegs::TexEnv1Source,
PICA::InternalRegs::TexEnv2Source, PICA::InternalRegs::TexEnv3Source,
PICA::InternalRegs::TexEnv4Source, PICA::InternalRegs::TexEnv5Source
};
u32 textureEnvSourceRegs[6];
u32 textureEnvOperandRegs[6];
u32 textureEnvCombinerRegs[6];
u32 textureEnvColourRegs[6];
u32 textureEnvScaleRegs[6];
for (int i = 0; i < 6; i++) {
const u32 ioBase = ioBases[i];
textureEnvSourceRegs[i] = regs[ioBase];
textureEnvOperandRegs[i] = regs[ioBase + 1];
textureEnvCombinerRegs[i] = regs[ioBase + 2];
textureEnvColourRegs[i] = regs[ioBase + 3];
textureEnvScaleRegs[i] = regs[ioBase + 4];
}
glUniform1uiv(textureEnvSourceLoc, 6, textureEnvSourceRegs);
glUniform1uiv(textureEnvOperandLoc, 6, textureEnvOperandRegs);
glUniform1uiv(textureEnvCombinerLoc, 6, textureEnvCombinerRegs);
glUniform1uiv(textureEnvColorLoc, 6, textureEnvColourRegs);
glUniform1uiv(textureEnvScaleLoc, 6, textureEnvScaleRegs);
glUniform1ui(textureEnvUpdateBufferLoc, regs[PICA::InternalRegs::TexEnvUpdateBuffer]);
glUniform1ui(textureEnvBufferColorLoc, regs[PICA::InternalRegs::TexEnvBufferColor]);
}
void Renderer::bindTexturesToSlots() {
static constexpr std::array<u32, 3> ioBases = {
PICA::InternalRegs::Tex0BorderColor, PICA::InternalRegs::Tex1BorderColor, PICA::InternalRegs::Tex2BorderColor
};
for (int i = 0; i < 3; i++) {
if ((regs[PICA::InternalRegs::TexUnitCfg] & (1 << i)) == 0) {
continue;
}
const size_t ioBase = ioBases[i];
const u32 dim = regs[ioBase + 1];
const u32 config = regs[ioBase + 2];
const u32 height = dim & 0x7ff;
const u32 width = getBits<16, 11>(dim);
const u32 addr = (regs[ioBase + 4] & 0x0FFFFFFF) << 3;
u32 format = regs[ioBase + (i == 0 ? 13 : 5)] & 0xF;
glActiveTexture(GL_TEXTURE0 + i);
Texture targetTex(addr, static_cast<PICA::TextureFmt>(format), width, height, config);
OpenGL::Texture tex = getTexture(targetTex);
tex.bind();
}
glActiveTexture(GL_TEXTURE0);
// Update the texture unit configuration uniform if it changed
const u32 texUnitConfig = regs[PICA::InternalRegs::TexUnitCfg];
if (oldTexUnitConfig != texUnitConfig) {
oldTexUnitConfig = texUnitConfig;
glUniform1ui(texUnitConfigLoc, texUnitConfig);
}
}
void Renderer::drawVertices(PICA::PrimType primType, std::span<const Vertex> vertices) {
// The fourth type is meant to be "Geometry primitive". TODO: Find out what that is
static constexpr std::array<OpenGL::Primitives, 4> primTypes = {
OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle
};
const auto primitiveTopology = primTypes[static_cast<usize>(primType)];
// TODO: We should implement a GL state tracker that tracks settings like scissor, blending, bound program, etc
// This way if we attempt to eg do multiple glEnable(GL_BLEND) calls in a row, it will say "Oh blending is already enabled"
// And not actually perform the very expensive driver call for it
OpenGL::disableScissor();
vbo.bind();
vao.bind();
triangleProgram.use();
// Adjust alpha test if necessary
const u32 alphaControl = regs[PICAInternalRegs::AlphaTestConfig];
const u32 alphaControl = regs[PICA::InternalRegs::AlphaTestConfig];
if (alphaControl != oldAlphaControl) {
oldAlphaControl = alphaControl;
glUniform1ui(alphaControlLoc, alphaControl);
@ -277,7 +575,7 @@ void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 c
OpenGL::Framebuffer poop = getColourFBO();
poop.bind(OpenGL::DrawAndReadFramebuffer);
const u32 depthControl = regs[PICAInternalRegs::DepthAndColorMask];
const u32 depthControl = regs[PICA::InternalRegs::DepthAndColorMask];
const bool depthEnable = depthControl & 1;
const bool depthWriteEnable = getBit<12>(depthControl);
const int depthFunc = getBits<4, 3>(depthControl);
@ -288,9 +586,9 @@ void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 c
GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL
};
const float depthScale = f24::fromRaw(regs[PICAInternalRegs::DepthScale] & 0xffffff).toFloat32();
const float depthOffset = f24::fromRaw(regs[PICAInternalRegs::DepthOffset] & 0xffffff).toFloat32();
const bool depthMapEnable = regs[PICAInternalRegs::DepthmapEnable] & 1;
const float depthScale = f24::fromRaw(regs[PICA::InternalRegs::DepthScale] & 0xffffff).toFloat32();
const float depthOffset = f24::fromRaw(regs[PICA::InternalRegs::DepthOffset] & 0xffffff).toFloat32();
const bool depthMapEnable = regs[PICA::InternalRegs::DepthmapEnable] & 1;
// Update depth uniforms
if (oldDepthScale != depthScale) {
@ -308,30 +606,12 @@ void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 c
glUniform1i(depthmapEnableLoc, depthMapEnable);
}
// Hack for rendering texture 1
if (regs[0x80] & 1) {
const u32 dim = regs[0x82];
const u32 config = regs[0x83];
const u32 height = dim & 0x7ff;
const u32 width = getBits<16, 11>(dim);
const u32 addr = (regs[0x85] & 0x0FFFFFFF) << 3;
const u32 format = regs[0x8E] & 0xF;
Texture targetTex(addr, static_cast<Texture::Formats>(format), width, height, config);
OpenGL::Texture tex = getTexture(targetTex);
tex.bind();
}
// Update the texture unit configuration uniform if it changed
const u32 texUnitConfig = regs[PICAInternalRegs::TexUnitCfg];
if (oldTexUnitConfig != texUnitConfig) {
oldTexUnitConfig = texUnitConfig;
glUniform1ui(texUnitConfigLoc, texUnitConfig);
}
setupTextureEnvState();
bindTexturesToSlots();
// TODO: Actually use this
float viewportWidth = f24::fromRaw(regs[PICAInternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0;
float viewportHeight = f24::fromRaw(regs[PICAInternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0;
float viewportWidth = f24::fromRaw(regs[PICA::InternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0;
float viewportHeight = f24::fromRaw(regs[PICA::InternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0;
OpenGL::setViewport(viewportWidth, viewportHeight);
// Note: The code below must execute after we've bound the colour buffer & its framebuffer
@ -352,8 +632,8 @@ void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 c
}
}
vbo.bufferVertsSub(vertices, count);
OpenGL::draw(primType, count);
vbo.bufferVertsSub(vertices);
OpenGL::draw(primitiveTopology, vertices.size());
}
constexpr u32 topScreenBuffer = 0x1f000000;
@ -361,20 +641,11 @@ constexpr u32 bottomScreenBuffer = 0x1f05dc00;
// Quick hack to display top screen for now
void Renderer::display() {
OpenGL::disableBlend();
OpenGL::disableDepth();
OpenGL::disableScissor();
OpenGL::bindScreenFramebuffer();
colourBufferCache[0].texture.bind();
displayProgram.use();
dummyVAO.bind();
OpenGL::setClearColor(0.0, 0.0, 1.0, 1.0); // Clear screen colour
OpenGL::clearColor();
OpenGL::setViewport(0, 240, 400, 240); // Actually draw our 3DS screen
OpenGL::draw(OpenGL::TriangleStrip, 4);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
screenFramebuffer.bind(OpenGL::ReadFramebuffer);
glBlitFramebuffer(0, 0, 400, 480, 0, 0, 400, 480, GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
void Renderer::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
@ -424,8 +695,8 @@ void Renderer::bindDepthBuffer() {
tex = depthBufferCache.add(sampleBuffer).texture.m_handle;
}
if (DepthBuffer::Formats::Depth24Stencil8 != depthBufferFormat) Helpers::panic("TODO: Should we remove stencil attachment?");
auto attachment = depthBufferFormat == DepthBuffer::Formats::Depth24Stencil8 ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
if (PICA::DepthFmt::Depth24Stencil8 != depthBufferFormat) Helpers::panic("TODO: Should we remove stencil attachment?");
auto attachment = depthBufferFormat == PICA::DepthFmt::Depth24Stencil8 ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
}
@ -450,4 +721,28 @@ void Renderer::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32
const u32 outputWidth = outputSize & 0xffff;
const u32 outputGap = outputSize >> 16;
auto framebuffer = colourBufferCache.findFromAddress(inputAddr);
// If there's a framebuffer at this address, use it. Otherwise go back to our old hack and display framebuffer 0
// Displays are hard I really don't want to try implementing them because getting a fast solution is terrible
OpenGL::Texture& tex = framebuffer.has_value() ? framebuffer.value().get().texture : colourBufferCache[0].texture;
tex.bind();
screenFramebuffer.bind(OpenGL::DrawFramebuffer);
OpenGL::disableBlend();
OpenGL::disableDepth();
OpenGL::disableScissor();
displayProgram.use();
// Hack: Detect whether we are writing to the top or bottom screen by checking output gap and drawing to the proper part of the output texture
// We consider output gap == 320 to mean bottom, and anything else to mean top
if (outputGap == 320) {
OpenGL::setViewport(40, 0, 320, 240); // Bottom screen viewport
} else {
OpenGL::setViewport(0, 240, 400, 240); // Top screen viewport
}
dummyVAO.bind();
OpenGL::draw(OpenGL::TriangleStrip, 4); // Actually draw our 3DS screen
}

View file

@ -43,34 +43,34 @@ u64 Texture::sizeInBytes() {
u64 pixelCount = u64(size.x()) * u64(size.y());
switch (format) {
case Formats::RGBA8: // 4 bytes per pixel
case PICA::TextureFmt::RGBA8: // 4 bytes per pixel
return pixelCount * 4;
case Formats::RGB8: // 3 bytes per pixel
case PICA::TextureFmt::RGB8: // 3 bytes per pixel
return pixelCount * 3;
case Formats::RGBA5551: // 2 bytes per pixel
case Formats::RGB565:
case Formats::RGBA4:
case Formats::RG8:
case Formats::IA8:
case PICA::TextureFmt::RGBA5551: // 2 bytes per pixel
case PICA::TextureFmt::RGB565:
case PICA::TextureFmt::RGBA4:
case PICA::TextureFmt::RG8:
case PICA::TextureFmt::IA8:
return pixelCount * 2;
case Formats::A8: // 1 byte per pixel
case Formats::I8:
case Formats::IA4:
case PICA::TextureFmt::A8: // 1 byte per pixel
case PICA::TextureFmt::I8:
case PICA::TextureFmt::IA4:
return pixelCount;
case Formats::I4: // 4 bits per pixel
case Formats::A4:
case PICA::TextureFmt::I4: // 4 bits per pixel
case PICA::TextureFmt::A4:
return pixelCount / 2;
case Formats::ETC1: // Compressed formats
case Formats::ETC1A4: {
case PICA::TextureFmt::ETC1: // Compressed formats
case PICA::TextureFmt::ETC1A4: {
// Number of 4x4 tiles
const u64 tileCount = pixelCount / 16;
// Tiles are 8 bytes each on ETC1 and 16 bytes each on ETC1A4
const u64 tileSize = format == Formats::ETC1 ? 8 : 16;
const u64 tileSize = format == PICA::TextureFmt::ETC1 ? 8 : 16;
return tileCount * tileSize;
}
@ -111,9 +111,9 @@ u32 Texture::getSwizzledOffset_4bpp(u32 u, u32 v, u32 width) {
// Get the texel at position (u, v)
// fmt: format of the texture
// data: texture data of the texture
u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
u32 Texture::decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, const void* data) {
switch (fmt) {
case Formats::RGBA4: {
case PICA::TextureFmt::RGBA4: {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
auto ptr = static_cast<const u8*>(data);
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
@ -126,7 +126,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (alpha << 24) | (b << 16) | (g << 8) | r;
}
case Formats::RGBA5551: {
case PICA::TextureFmt::RGBA5551: {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
auto ptr = static_cast<const u8*>(data);
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
@ -139,7 +139,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (alpha << 24) | (b << 16) | (g << 8) | r;
}
case Formats::RGB565: {
case PICA::TextureFmt::RGB565: {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
auto ptr = static_cast<const u8*>(data);
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
@ -151,7 +151,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (0xff << 24) | (b << 16) | (g << 8) | r;
}
case Formats::RG8: {
case PICA::TextureFmt::RG8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
auto ptr = static_cast<const u8*>(data);
@ -162,7 +162,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (0xff << 24) | (b << 16) | (g << 8) | r;
}
case Formats::RGB8: {
case PICA::TextureFmt::RGB8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 3);
auto ptr = static_cast<const u8*>(data);
@ -173,7 +173,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (0xff << 24) | (b << 16) | (g << 8) | r;
}
case Formats::RGBA8: {
case PICA::TextureFmt::RGBA8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 4);
auto ptr = static_cast<const u8*>(data);
@ -185,7 +185,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (alpha << 24) | (b << 16) | (g << 8) | r;
}
case Formats::IA4: {
case PICA::TextureFmt::IA4: {
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
auto ptr = static_cast<const u8*>(data);
@ -197,7 +197,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
}
case Formats::A4: {
case PICA::TextureFmt::A4: {
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
auto ptr = static_cast<const u8*>(data);
@ -209,7 +209,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (alpha << 24) | (0 << 16) | (0 << 8) | 0;
}
case Formats::A8: {
case PICA::TextureFmt::A8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
auto ptr = static_cast<const u8*>(data);
const u8 alpha = ptr[offset];
@ -218,7 +218,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (alpha << 24) | (0 << 16) | (0 << 8) | 0;
}
case Formats::I4: {
case PICA::TextureFmt::I4: {
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
auto ptr = static_cast<const u8*>(data);
@ -230,7 +230,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
}
case Formats::I8: {
case PICA::TextureFmt::I8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
auto ptr = static_cast<const u8*>(data);
const u8 intensity = ptr[offset];
@ -239,7 +239,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
}
case Formats::IA8: {
case PICA::TextureFmt::IA8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
auto ptr = static_cast<const u8*>(data);
@ -249,8 +249,8 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
}
case Formats::ETC1: return getTexelETC(false, u, v, size.u(), data);
case Formats::ETC1A4: return getTexelETC(true, u, v, size.u(), data);
case PICA::TextureFmt::ETC1: return getTexelETC(false, u, v, size.u(), data);
case PICA::TextureFmt::ETC1A4: return getTexelETC(true, u, v, size.u(), data);
default:
Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
@ -271,24 +271,4 @@ void Texture::decodeTexture(const void* data) {
texture.bind();
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.u(), size.v(), GL_RGBA, GL_UNSIGNED_BYTE, decoded.data());
}
std::string Texture::textureFormatToString(Texture::Formats fmt) {
switch (fmt) {
case Formats::A4: return "A4";
case Formats::A8: return "A8";
case Formats::ETC1: return "ETC1";
case Formats::ETC1A4: return "ETC1A4";
case Formats::I4: return "I4";
case Formats::I8: return "I8";
case Formats::IA4: return "IA4";
case Formats::IA8: return "IA8";
case Formats::RG8: return "RG8";
case Formats::RGB565: return "RGB565";
case Formats::RGB8: return "RGB8";
case Formats::RGBA4: return "RGBA4";
case Formats::RGBA5551: return "RGBA5551";
case Formats::RGBA8: return "RGBA8";
default: return "Unknown";
}
}

View file

@ -7,12 +7,6 @@ namespace ACCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void ACService::reset() {}
void ACService::handleSyncRequest(u32 messagePointer) {

View file

@ -7,12 +7,6 @@ namespace ACTCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void ACTService::reset() {}
void ACTService::handleSyncRequest(u32 messagePointer) {

View file

@ -8,12 +8,6 @@ namespace AMCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void AMService::reset() {}
void AMService::handleSyncRequest(u32 messagePointer) {

View file

@ -25,12 +25,6 @@ namespace APTCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
// https://www.3dbrew.org/wiki/NS_and_APT_Services#Command
namespace APTTransitions {
enum : u32 {
@ -154,7 +148,7 @@ void APTService::inquireNotification(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0xB, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, static_cast<u32>(NotificationType::None));
mem.write32(messagePointer + 8, static_cast<u32>(NotificationType::None));
}
void APTService::getLockHandle(u32 messagePointer) {

View file

@ -14,12 +14,6 @@ namespace BOSSCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void BOSSService::reset() {
optoutFlag = 0;
}

View file

@ -8,12 +8,6 @@ namespace CAMCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void CAMService::reset() {}
void CAMService::handleSyncRequest(u32 messagePointer) {

View file

@ -8,12 +8,6 @@ namespace CECDCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void CECDService::reset() {
infoEvent = std::nullopt;
}

View file

@ -16,12 +16,6 @@ namespace CFGCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void CFGService::reset() {}
void CFGService::handleSyncRequest(u32 messagePointer) {
@ -79,7 +73,7 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
};
for (int i = 0; i < 8; i++) {
mem.write32(output + i * 4, std::bit_cast<u32, float>(STEREO_CAMERA_SETTINGS[i]));
mem.write32(output + i * 4, Helpers::bit_cast<u32, float>(STEREO_CAMERA_SETTINGS[i]));
}
} else if (size == 0x1C && blockID == 0xA0000) { // Username
writeStringU16(output, u"Pander");
@ -89,7 +83,7 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
} else if (size == 4 && blockID == 0xD0000) { // Agreed EULA version (first 2 bytes) and latest EULA version (next 2 bytes)
log("Read EULA info\n");
mem.write16(output, 0x0202); // Agreed EULA version = 2.2 (Random number. TODO: Check)
mem.write16(output + 2, 0x0202); // Latest EULA version = 2.2
mem.write16(output + 2, 0x0202); // Latest EULA version = 2.2
} else if (size == 0x800 && blockID == 0xB0001) { // UTF-16 name for our country in every language at 0x80 byte intervals
constexpr size_t languageCount = 16;
constexpr size_t nameSize = 0x80; // Max size of each name in bytes
@ -105,7 +99,7 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
std::u16string name = u"Pandington"; // Note: This + the null terminator needs to fit in 0x80 bytes
for (int i = 0; i < languageCount; i++) {
u32 pointer = output + i * nameSize;
u32 pointer = output + i * nameSize;
writeStringU16(pointer, name);
}
} else if (size == 4 && blockID == 0xB0003) { // Coordinates (latidude and longtitude) as s16

View file

@ -7,12 +7,6 @@ namespace DlpSrvrCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void DlpSrvrService::reset() {}
void DlpSrvrService::handleSyncRequest(u32 messagePointer) {

View file

@ -25,7 +25,6 @@ namespace DSPCommands {
namespace Result {
enum : u32 {
Success = 0,
HeadphonesNotInserted = 0,
HeadphonesInserted = 1
};
@ -215,7 +214,7 @@ void DSPService::registerInterruptEvents(u32 messagePointer) {
const u32 channel = mem.read32(messagePointer + 8);
const u32 eventHandle = mem.read32(messagePointer + 16);
log("DSP::RegisterInterruptEvents (interrupt = %d, channel = %d, event = %d)\n", interrupt, channel, eventHandle);
// The event handle being 0 means we're removing an event
if (eventHandle == 0) {
DSPEvent& e = getEventRef(interrupt, channel); // Get event

View file

@ -17,12 +17,6 @@ namespace FRDCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void FRDService::reset() {}
void FRDService::handleSyncRequest(u32 messagePointer) {
@ -91,7 +85,7 @@ void FRDService::getMyProfile(u32 messagePointer) {
mem.write32(messagePointer + 4, Result::Success);
// TODO: Should maybe make these user-configurable. Not super important though
mem.write8(messagePointer + 8, static_cast<u8>(Regions::USA)); // Region
mem.write8(messagePointer + 8, static_cast<u8>(Regions::USA)); // Region
mem.write8(messagePointer + 9, static_cast<u8>(CountryCodes::US)); // Country
mem.write8(messagePointer + 10, 2); // Area (this should be Washington)
mem.write8(messagePointer + 11, static_cast<u8>(LanguageCodes::English)); // Language

View file

@ -32,14 +32,6 @@ namespace FSCommands {
};
}
namespace ResultCode {
enum : u32 {
Success = 0,
FileNotFound = 0xC8804464, // TODO: Verify this
Failure = 0xFFFFFFFF,
};
}
void FSService::reset() {
priority = 0;
}
@ -96,15 +88,15 @@ std::optional<Handle> FSService::openFileHandle(ArchiveBase* archive, const FSPa
auto& file = kernel.getObjects()[handle];
file.data = new FileSession(archive, path, archivePath, opened.value());
return handle;
} else {
return std::nullopt;
}
}
Rust::Result<Handle, FSResult> FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) {
Rust::Result<DirectorySession, FSResult> opened = archive->openDirectory(path);
Rust::Result<Handle, Result::HorizonResult> FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) {
Rust::Result<DirectorySession, Result::HorizonResult> opened = archive->openDirectory(path);
if (opened.isOk()) { // If opened doesn't have a value, we failed to open the directory
auto handle = kernel.makeObject(KernelObjectType::Directory);
auto& object = kernel.getObjects()[handle];
@ -116,15 +108,15 @@ Rust::Result<Handle, FSResult> FSService::openDirectoryHandle(ArchiveBase* archi
}
}
Rust::Result<Handle, FSResult> FSService::openArchiveHandle(u32 archiveID, const FSPath& path) {
Rust::Result<Handle, Result::HorizonResult> FSService::openArchiveHandle(u32 archiveID, const FSPath& path) {
ArchiveBase* archive = getArchiveFromID(archiveID, path);
if (archive == nullptr) [[unlikely]] {
Helpers::panic("OpenArchive: Tried to open unknown archive %d.", archiveID);
return Err(FSResult::NotFormatted);
return Err(Result::FS::NotFormatted);
}
Rust::Result<ArchiveBase*, FSResult> res = archive->openArchive(path);
Rust::Result<ArchiveBase*, Result::HorizonResult> res = archive->openArchive(path);
if (res.isOk()) {
auto handle = kernel.makeObject(KernelObjectType::Archive);
auto& archiveObject = kernel.getObjects()[handle];
@ -175,7 +167,7 @@ void FSService::handleSyncRequest(u32 messagePointer) {
void FSService::initialize(u32 messagePointer) {
log("FS::Initialize\n");
mem.write32(messagePointer, IPC::responseHeader(0x801, 1, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
}
// TODO: Figure out how this is different from Initialize
@ -184,7 +176,7 @@ void FSService::initializeWithSdkVersion(u32 messagePointer) {
log("FS::InitializeWithSDKVersion(version = %d)\n", version);
mem.write32(messagePointer, IPC::responseHeader(0x861, 1, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
}
void FSService::closeArchive(u32 messagePointer) {
@ -196,10 +188,10 @@ void FSService::closeArchive(u32 messagePointer) {
if (object == nullptr) {
log("FSService::CloseArchive: Tried to close invalid archive %X\n", handle);
mem.write32(messagePointer + 4, ResultCode::Failure);
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
} else {
object->getData<ArchiveSession>()->isOpen = false;
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
}
}
@ -211,15 +203,15 @@ void FSService::openArchive(u32 messagePointer) {
auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize);
log("FS::OpenArchive(archive ID = %d, archive path type = %d)\n", archiveID, archivePathType);
Rust::Result<Handle, FSResult> res = openArchiveHandle(archiveID, archivePath);
Rust::Result<Handle, Result::HorizonResult> res = openArchiveHandle(archiveID, archivePath);
mem.write32(messagePointer, IPC::responseHeader(0x80C, 3, 0));
if (res.isOk()) {
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
mem.write64(messagePointer + 8, res.unwrap());
} else {
log("FS::OpenArchive: Failed to open archive with id = %d. Error %08X\n", archiveID, (u32)res.unwrapErr());
mem.write32(messagePointer + 4, static_cast<u32>(res.unwrapErr()));
mem.write32(messagePointer + 4, res.unwrapErr());
mem.write64(messagePointer + 8, 0);
}
}
@ -237,7 +229,7 @@ void FSService::openFile(u32 messagePointer) {
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
if (archiveObject == nullptr) [[unlikely]] {
log("FS::OpenFile: Invalid archive handle %d\n", archiveHandle);
mem.write32(messagePointer + 4, ResultCode::Failure);
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
return;
}
@ -251,9 +243,9 @@ void FSService::openFile(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x802, 1, 2));
if (!handle.has_value()) {
printf("OpenFile failed\n");
mem.write32(messagePointer + 4, ResultCode::FileNotFound);
mem.write32(messagePointer + 4, Result::FS::FileNotFound);
} else {
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor"
mem.write32(messagePointer + 12, handle.value());
}
@ -270,13 +262,13 @@ void FSService::createDirectory(u32 messagePointer) {
KernelObject* archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
if (archiveObject == nullptr) [[unlikely]] {
log("FS::CreateDirectory: Invalid archive handle %d\n", archiveHandle);
mem.write32(messagePointer + 4, ResultCode::Failure);
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
return;
}
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
const auto dirPath = readPath(pathType, pathPointer, pathSize);
const FSResult res = archive->createDirectory(dirPath);
const Result::HorizonResult res = archive->createDirectory(dirPath);
mem.write32(messagePointer, IPC::responseHeader(0x809, 1, 0));
mem.write32(messagePointer + 4, static_cast<u32>(res));
@ -292,7 +284,7 @@ void FSService::openDirectory(u32 messagePointer) {
KernelObject* archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
if (archiveObject == nullptr) [[unlikely]] {
log("FS::OpenDirectory: Invalid archive handle %d\n", archiveHandle);
mem.write32(messagePointer + 4, ResultCode::Failure);
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
return;
}
@ -302,7 +294,7 @@ void FSService::openDirectory(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x80B, 1, 2));
if (dir.isOk()) {
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 12, dir.unwrap());
} else {
printf("FS::OpenDirectory failed\n");
@ -324,14 +316,14 @@ void FSService::openFileDirectly(u32 messagePointer) {
auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize);
ArchiveBase* archive = getArchiveFromID(archiveID, archivePath);
if (archive == nullptr) [[unlikely]] {
Helpers::panic("OpenFileDirectly: Tried to open unknown archive %d.", archiveID);
}
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
const FilePerms perms(openFlags);
Rust::Result<ArchiveBase*, FSResult> res = archive->openArchive(archivePath);
Rust::Result<ArchiveBase*, Result::HorizonResult> res = archive->openArchive(archivePath);
if (res.isErr()) [[unlikely]] {
Helpers::panic("OpenFileDirectly: Failed to open archive with given path");
}
@ -342,7 +334,7 @@ void FSService::openFileDirectly(u32 messagePointer) {
if (!handle.has_value()) {
Helpers::panic("OpenFileDirectly: Failed to open file with given path");
} else {
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 12, handle.value());
}
}
@ -360,16 +352,16 @@ void FSService::createFile(u32 messagePointer) {
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
if (archiveObject == nullptr) [[unlikely]] {
log("FS::OpenFile: Invalid archive handle %d\n", archiveHandle);
mem.write32(messagePointer + 4, ResultCode::Failure);
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
return;
}
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
FSResult res = archive->createFile(filePath, size);
Result::HorizonResult res = archive->createFile(filePath, size);
mem.write32(messagePointer, IPC::responseHeader(0x808, 1, 0));
mem.write32(messagePointer + 4, static_cast<u32>(res));
mem.write32(messagePointer + 4, res);
}
void FSService::deleteFile(u32 messagePointer) {
@ -382,14 +374,14 @@ void FSService::deleteFile(u32 messagePointer) {
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
if (archiveObject == nullptr) [[unlikely]] {
log("FS::DeleteFile: Invalid archive handle %d\n", archiveHandle);
mem.write32(messagePointer + 4, ResultCode::Failure);
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
return;
}
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
FSResult res = archive->deleteFile(filePath);
Result::HorizonResult res = archive->deleteFile(filePath);
mem.write32(messagePointer, IPC::responseHeader(0x804, 1, 0));
mem.write32(messagePointer + 4, static_cast<u32>(res));
}
@ -409,12 +401,12 @@ void FSService::getFormatInfo(u32 messagePointer) {
}
mem.write32(messagePointer, IPC::responseHeader(0x845, 5, 0));
Rust::Result<ArchiveBase::FormatInfo, FSResult> res = archive->getFormatInfo(path);
Rust::Result<ArchiveBase::FormatInfo, Result::HorizonResult> res = archive->getFormatInfo(path);
// If the FormatInfo was returned, write them to the output buffer. Otherwise, write an error code.
if (res.isOk()) {
ArchiveBase::FormatInfo info = res.unwrap();
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, info.size);
mem.write32(messagePointer + 12, info.numOfDirectories);
mem.write32(messagePointer + 16, info.numOfFiles);
@ -458,7 +450,7 @@ void FSService::formatSaveData(u32 messagePointer) {
saveData.format(path, info);
mem.write32(messagePointer, IPC::responseHeader(0x84C, 1, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
}
void FSService::formatThisUserSaveData(u32 messagePointer) {
@ -478,7 +470,7 @@ void FSService::formatThisUserSaveData(u32 messagePointer) {
.duplicateData = duplicateData
};
FSPath emptyPath;
mem.write32(messagePointer, IPC::responseHeader(0x080F, 1, 0));
saveData.format(emptyPath, info);
}
@ -497,13 +489,13 @@ void FSService::controlArchive(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x80D, 1, 0));
if (archiveObject == nullptr) [[unlikely]] {
log("FS::ControlArchive: Invalid archive handle %d\n", archiveHandle);
mem.write32(messagePointer + 4, ResultCode::Failure);
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
return;
}
switch (action) {
case 0: // Commit save data changes. Shouldn't need us to do anything
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
break;
default:
Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action);
@ -519,7 +511,7 @@ void FSService::getFreeBytes(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x812, 3, 0));
if (session == nullptr) [[unlikely]] {
log("FS::GetFreeBytes: Invalid archive handle %d\n", archiveHandle);
mem.write32(messagePointer + 4, ResultCode::Failure);
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
return;
}
@ -531,7 +523,7 @@ void FSService::getPriority(u32 messagePointer) {
log("FS::GetPriority\n");
mem.write32(messagePointer, IPC::responseHeader(0x863, 2, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, priority);
}
@ -540,13 +532,13 @@ void FSService::setPriority(u32 messagePointer) {
log("FS::SetPriority (priority = %d)\n", value);
mem.write32(messagePointer, IPC::responseHeader(0x862, 1, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
priority = value;
}
void FSService::isSdmcDetected(u32 messagePointer) {
log("FS::IsSdmcDetected\n");
mem.write32(messagePointer, IPC::responseHeader(0x817, 2, 0));
mem.write32(messagePointer + 4, ResultCode::Success);
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Whether SD is detected. For now we emulate a 3DS without an SD.
}

View file

@ -30,13 +30,6 @@ namespace GXCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
SuccessRegisterIRQ = 0x2A07 // TODO: Is this a reference to the Ricoh 2A07 used in PAL NES systems?
};
}
void GPUService::reset() {
privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle
interruptEvent = std::nullopt;
@ -100,7 +93,7 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) {
}
mem.write32(messagePointer, IPC::responseHeader(0x13, 2, 2));
mem.write32(messagePointer + 4, Result::SuccessRegisterIRQ); // First init returns a unique result
mem.write32(messagePointer + 4, Result::GSP::SuccessRegisterIRQ); // First init returns a unique result
mem.write32(messagePointer + 8, 0); // TODO: GSP module thread index
mem.write32(messagePointer + 12, 0); // Translation descriptor
mem.write32(messagePointer + 16, KernelHandles::GSPSharedMemHandle);
@ -120,7 +113,7 @@ void GPUService::requestInterrupt(GPUInterrupt type) {
u8& interruptCount = sharedMem[1];
u8 flagIndex = (index + interruptCount) % 0x34;
interruptCount++;
sharedMem[2] = 0; // Set error code to 0
sharedMem[0xC + flagIndex] = static_cast<u8>(type); // Write interrupt type to queue
@ -187,7 +180,7 @@ void GPUService::writeHwRegsWithMask(u32 messagePointer) {
u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer
u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer
log("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);
// Check for alignment
@ -202,7 +195,7 @@ void GPUService::writeHwRegsWithMask(u32 messagePointer) {
if (ioAddr >= 0x420000) {
Helpers::panic("GSP::GPU::writeHwRegs offset too big");
}
ioAddr += 0x1EB00000;
for (u32 i = 0; i < size; i += 4) {
const u32 current = gpu.readReg(ioAddr);
@ -301,13 +294,13 @@ void GPUService::processCommandBuffer() {
commandsLeft--;
}
}
}
}
// Fill 2 GPU framebuffers, buf0 and buf1, using a specific word value
void GPUService::memoryFill(u32* cmd) {
u32 control = cmd[7];
// buf0 parameters
u32 start0 = cmd[1]; // Start address for the fill. If 0, don't fill anything
u32 value0 = cmd[2]; // Value to fill the framebuffer with
@ -331,9 +324,31 @@ void GPUService::memoryFill(u32* cmd) {
}
}
static u32 VaddrToPaddr(u32 addr) {
if (addr >= VirtualAddrs::VramStart && addr < (VirtualAddrs::VramStart + VirtualAddrs::VramSize)) [[likely]] {
return addr - VirtualAddrs::VramStart + PhysicalAddrs::VRAM;
}
else if (addr >= VirtualAddrs::LinearHeapStartOld && addr < VirtualAddrs::LinearHeapEndOld) {
return addr - VirtualAddrs::LinearHeapStartOld + PhysicalAddrs::FCRAM;
}
else if (addr >= VirtualAddrs::LinearHeapStartNew && addr < VirtualAddrs::LinearHeapEndNew) {
return addr - VirtualAddrs::LinearHeapStartNew + PhysicalAddrs::FCRAM;
}
else if (addr == 0) {
return 0;
}
Helpers::warn("[GSP::GPU VaddrToPaddr] Unknown virtual address %08X", addr);
// Obviously garbage address
return 0xF3310932;
}
void GPUService::triggerDisplayTransfer(u32* cmd) {
const u32 inputAddr = cmd[1];
const u32 outputAddr = cmd[2];
const u32 inputAddr = VaddrToPaddr(cmd[1]);
const u32 outputAddr = VaddrToPaddr(cmd[2]);
const u32 inputSize = cmd[3];
const u32 outputSize = cmd[4];
const u32 flags = cmd[5];

View file

@ -6,12 +6,6 @@ namespace LCDCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void LCDService::reset() {}
void LCDService::handleSyncRequest(u32 messagePointer) {

View file

@ -13,13 +13,6 @@ namespace HIDCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
Failure = 0xFFFFFFFF
};
}
void HIDService::reset() {
sharedMem = nullptr;
accelerometerEnabled = false;
@ -90,7 +83,7 @@ void HIDService::getGyroscopeCoefficient(u32 messagePointer) {
constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS
mem.write32(messagePointer, IPC::responseHeader(0x15, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, std::bit_cast<u32, float>(gyroscopeCoeff));
mem.write32(messagePointer + 8, Helpers::bit_cast<u32, float>(gyroscopeCoeff));
}
void HIDService::getIPCHandles(u32 messagePointer) {
@ -155,7 +148,7 @@ void HIDService::updateInputs(u64 currentTick) {
writeSharedMem<u16>(touchEntryOffset, touchScreenX);
writeSharedMem<u16>(touchEntryOffset + 2, touchScreenY);
writeSharedMem<u8>(touchEntryOffset + 4, touchScreenPressed ? 1 : 0);
// Next, update accelerometer state
if (nextAccelerometerIndex == 0) {
writeSharedMem<u64>(0x110, readSharedMem<u64>(0x108)); // Copy previous tick count

View file

@ -8,12 +8,6 @@ namespace LDRCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void LDRService::reset() {}
void LDRService::handleSyncRequest(u32 messagePointer) {

View file

@ -13,12 +13,6 @@ namespace MICCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void MICService::reset() {
micEnabled = false;
shouldClamp = false;

View file

@ -11,12 +11,6 @@ namespace NDMCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void NDMService::reset() {}
void NDMService::handleSyncRequest(u32 messagePointer) {

View file

@ -10,12 +10,6 @@ namespace NFCCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void NFCService::reset() {
tagInRangeEvent = std::nullopt;
tagOutOfRangeEvent = std::nullopt;

View file

@ -7,12 +7,6 @@ namespace NIMCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void NIMService::reset() {}
void NIMService::handleSyncRequest(u32 messagePointer) {

View file

@ -3,18 +3,12 @@
namespace PTMCommands {
enum : u32 {
GetStepHistory = 0x000B00C2,
GetStepHistory = 0x000B00C2,
GetTotalStepCount = 0x000C0000,
ConfigureNew3DSCPU = 0x08180040
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void PTMService::reset() {}
void PTMService::handleSyncRequest(u32 messagePointer) {

View file

@ -1,13 +1,15 @@
#include "services/service_manager.hpp"
#include <map>
#include "ipc.hpp"
#include "kernel.hpp"
ServiceManager::ServiceManager(std::array<u32, 16>& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel)
ServiceManager::ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel)
: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem),
cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), frd(mem), fs(mem, kernel),
gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem),
ptm(mem), y2r(mem, kernel) {}
cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), frd(mem), fs(mem, kernel),
gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem),
ptm(mem), y2r(mem, kernel) {}
static constexpr int MAX_NOTIFICATION_COUNT = 16;
@ -58,12 +60,6 @@ namespace Commands {
};
}
namespace Result {
enum : u32 {
Success = 0
};
}
// Handle an IPC message issued using the SendSyncRequest SVC
// The parameters are stored in thread-local storage in this format: https://www.3dbrew.org/wiki/IPC#Message_Structure
// messagePointer: The base pointer for the IPC message

View file

@ -29,12 +29,6 @@ namespace Y2RCommands {
};
}
namespace Result {
enum : u32 {
Success = 0,
};
}
void Y2RService::reset() {
transferEndInterruptEnabled = false;
transferEndEvent = std::nullopt;