mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-21 12:59:14 +12:00
metal: initial support
This commit is contained in:
parent
29d9ed7224
commit
f0547d1a71
167 changed files with 28839 additions and 1271 deletions
|
@ -40,7 +40,7 @@ void CPU::runFrame() {
|
|||
execute:
|
||||
const auto exitReason = jit->Run();
|
||||
|
||||
// Handle any scheduler events that need handling.
|
||||
// HandleType any scheduler events that need handling.
|
||||
emu.pollScheduler();
|
||||
|
||||
if (static_cast<u32>(exitReason) != 0) [[unlikely]] {
|
||||
|
@ -55,4 +55,4 @@ void CPU::runFrame() {
|
|||
}
|
||||
}
|
||||
|
||||
#endif // CPU_DYNARMIC
|
||||
#endif // CPU_DYNARMIC
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#if defined(PANDA3DS_DYNAPICA_SUPPORTED) && defined(PANDA3DS_X64_HOST)
|
||||
#include "PICA/dynapica/shader_rec_emitter_x64.hpp"
|
||||
|
||||
#include <immintrin.h>
|
||||
#include <smmintrin.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <immintrin.h>
|
||||
#include <smmintrin.h>
|
||||
|
||||
using namespace Xbyak;
|
||||
using namespace Xbyak::util;
|
||||
|
@ -41,9 +42,15 @@ void ShaderEmitter::compile(const PICAShader& shaderUnit) {
|
|||
// Constants
|
||||
align(16);
|
||||
L(negateVector);
|
||||
dd(0x80000000); dd(0x80000000); dd(0x80000000); dd(0x80000000); // -0.0 4 times
|
||||
dd(0x80000000);
|
||||
dd(0x80000000);
|
||||
dd(0x80000000);
|
||||
dd(0x80000000); // -0.0 4 times
|
||||
L(onesVector);
|
||||
dd(0x3f800000); dd(0x3f800000); dd(0x3f800000); dd(0x3f800000); // 1.0 4 times
|
||||
dd(0x3f800000);
|
||||
dd(0x3f800000);
|
||||
dd(0x3f800000);
|
||||
dd(0x3f800000); // 1.0 4 times
|
||||
|
||||
// Emit prologue first
|
||||
align(16);
|
||||
|
@ -86,7 +93,7 @@ void ShaderEmitter::scanCode(const PICAShader& shaderUnit) {
|
|||
if (isCall(instruction)) {
|
||||
const u32 num = instruction & 0xff;
|
||||
const u32 dest = getBits<10, 12>(instruction);
|
||||
const u32 returnPC = num + dest; // Add them to get the return PC
|
||||
const u32 returnPC = num + dest; // Add them to get the return PC
|
||||
|
||||
returnPCs.push_back(returnPC);
|
||||
} else if (opcode == ShaderOpcodes::EX2) {
|
||||
|
@ -129,23 +136,15 @@ void ShaderEmitter::compileInstruction(const PICAShader& shaderUnit) {
|
|||
|
||||
switch (opcode) {
|
||||
case ShaderOpcodes::ADD: recADD(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::CALL:
|
||||
recCALL(shaderUnit, instruction);
|
||||
break;
|
||||
case ShaderOpcodes::CALLC:
|
||||
recCALLC(shaderUnit, instruction);
|
||||
break;
|
||||
case ShaderOpcodes::CALLU:
|
||||
recCALLU(shaderUnit, instruction);
|
||||
break;
|
||||
case ShaderOpcodes::CMP1: case ShaderOpcodes::CMP2:
|
||||
recCMP(shaderUnit, instruction);
|
||||
break;
|
||||
case ShaderOpcodes::CALL: recCALL(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::CALLC: recCALLC(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::CALLU: recCALLU(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::CMP1:
|
||||
case ShaderOpcodes::CMP2: recCMP(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::DP3: recDP3(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::DP4: recDP4(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::DPH:
|
||||
case ShaderOpcodes::DPHI:
|
||||
recDPH(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::DPHI: recDPH(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::END: recEND(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::EX2: recEX2(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::FLR: recFLR(shaderUnit, instruction); break;
|
||||
|
@ -176,21 +175,30 @@ void ShaderEmitter::compileInstruction(const PICAShader& shaderUnit) {
|
|||
case ShaderOpcodes::BREAKC: Helpers::warn("[Shader JIT] Unimplemented BREAK(C) instruction!"); break;
|
||||
|
||||
// We consider both MAD and MADI to be the same instruction and decode which one we actually have in recMAD
|
||||
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
|
||||
case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F:
|
||||
recMAD(shaderUnit, instruction);
|
||||
break;
|
||||
case 0x30:
|
||||
case 0x31:
|
||||
case 0x32:
|
||||
case 0x33:
|
||||
case 0x34:
|
||||
case 0x35:
|
||||
case 0x36:
|
||||
case 0x37:
|
||||
case 0x38:
|
||||
case 0x39:
|
||||
case 0x3A:
|
||||
case 0x3B:
|
||||
case 0x3C:
|
||||
case 0x3D:
|
||||
case 0x3E:
|
||||
case 0x3F: recMAD(shaderUnit, instruction); break;
|
||||
|
||||
case ShaderOpcodes::SLT:
|
||||
case ShaderOpcodes::SLTI:
|
||||
recSLT(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::SLTI: recSLT(shaderUnit, instruction); break;
|
||||
|
||||
case ShaderOpcodes::SGE:
|
||||
case ShaderOpcodes::SGEI:
|
||||
recSGE(shaderUnit, instruction); break;
|
||||
case ShaderOpcodes::SGEI: recSGE(shaderUnit, instruction); break;
|
||||
|
||||
default:
|
||||
Helpers::panic("Shader JIT: Unimplemented PICA opcode %X", opcode);
|
||||
default: Helpers::panic("Shader JIT: Unimplemented PICA opcode %X", opcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,64 +227,64 @@ const ShaderEmitter::vec4f& ShaderEmitter::getDestRef(const PICAShader& shader,
|
|||
// See shader.hpp header for docs on how the swizzle and negate works
|
||||
template <int sourceIndex>
|
||||
void ShaderEmitter::loadRegister(Xmm dest, const PICAShader& shader, u32 src, u32 index, u32 operandDescriptor) {
|
||||
u32 compSwizzle; // Component swizzle pattern for the register
|
||||
bool negate; // If true, negate all lanes of the register
|
||||
u32 compSwizzle; // Component swizzle pattern for the register
|
||||
bool negate; // If true, negate all lanes of the register
|
||||
|
||||
if constexpr (sourceIndex == 1) { // SRC1
|
||||
if constexpr (sourceIndex == 1) { // SRC1
|
||||
negate = (getBit<4>(operandDescriptor)) != 0;
|
||||
compSwizzle = getBits<5, 8>(operandDescriptor);
|
||||
}
|
||||
else if constexpr (sourceIndex == 2) { // SRC2
|
||||
} else if constexpr (sourceIndex == 2) { // SRC2
|
||||
negate = (getBit<13>(operandDescriptor)) != 0;
|
||||
compSwizzle = getBits<14, 8>(operandDescriptor);
|
||||
}
|
||||
else if constexpr (sourceIndex == 3) { // SRC3
|
||||
} else if constexpr (sourceIndex == 3) { // SRC3
|
||||
negate = (getBit<22>(operandDescriptor)) != 0;
|
||||
compSwizzle = getBits<23, 8>(operandDescriptor);
|
||||
}
|
||||
|
||||
// TODO: Do indexes get applied if src < 0x20?
|
||||
|
||||
// PICA has the swizzle descriptor inverted in comparison to x86. For the PICA, the descriptor is (lowest to highest bits) wzyx while it's xyzw for x86
|
||||
u32 convertedSwizzle = ((compSwizzle >> 6) & 0b11) | (((compSwizzle >> 4) & 0b11) << 2) | (((compSwizzle >> 2) & 0b11) << 4) | ((compSwizzle & 0b11) << 6);
|
||||
// PICA has the swizzle descriptor inverted in comparison to x86. For the PICA, the descriptor is (lowest to highest bits) wzyx while it's xyzw
|
||||
// for x86
|
||||
u32 convertedSwizzle =
|
||||
((compSwizzle >> 6) & 0b11) | (((compSwizzle >> 4) & 0b11) << 2) | (((compSwizzle >> 2) & 0b11) << 4) | ((compSwizzle & 0b11) << 6);
|
||||
|
||||
switch (index) {
|
||||
case 0: [[likely]] { // Keep src as is, no need to offset it
|
||||
const vec4f& srcRef = getSourceRef(shader, src);
|
||||
const uintptr_t offset = uintptr_t(&srcRef) - uintptr_t(&shader); // Calculate offset of register from start of the state struct
|
||||
case 0:
|
||||
[[likely]] { // Keep src as is, no need to offset it
|
||||
const vec4f& srcRef = getSourceRef(shader, src);
|
||||
const uintptr_t offset = uintptr_t(&srcRef) - uintptr_t(&shader); // Calculate offset of register from start of the state struct
|
||||
|
||||
if (compSwizzle == noSwizzle) // Avoid emitting swizzle if not necessary
|
||||
movaps(dest, xword[statePointer + offset]);
|
||||
else // Swizzle is not trivial so we need to emit a shuffle instruction
|
||||
pshufd(dest, xword[statePointer + offset], convertedSwizzle);
|
||||
if (compSwizzle == noSwizzle) // Avoid emitting swizzle if not necessary
|
||||
movaps(dest, xword[statePointer + offset]);
|
||||
else // Swizzle is not trivial so we need to emit a shuffle instruction
|
||||
pshufd(dest, xword[statePointer + offset], convertedSwizzle);
|
||||
|
||||
// Negate the register if necessary
|
||||
if (negate) {
|
||||
pxor(dest, xword[rip + negateVector]);
|
||||
// Negate the register if necessary
|
||||
if (negate) {
|
||||
pxor(dest, xword[rip + negateVector]);
|
||||
}
|
||||
return; // Return. Rest of the function handles indexing which is not used if index == 0
|
||||
}
|
||||
return; // Return. Rest of the function handles indexing which is not used if index == 0
|
||||
}
|
||||
|
||||
case 1: {
|
||||
const uintptr_t addrXOffset = uintptr_t(&shader.addrRegister[0]) - uintptr_t(&shader);
|
||||
movsxd(rax, dword[statePointer + addrXOffset]); // rax = address register x
|
||||
movsxd(rax, dword[statePointer + addrXOffset]); // rax = address register x
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
const uintptr_t addrYOffset = uintptr_t(&shader.addrRegister[1]) - uintptr_t(&shader);
|
||||
movsxd(rax, dword[statePointer + addrYOffset]); // rax = address register y
|
||||
movsxd(rax, dword[statePointer + addrYOffset]); // rax = address register y
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
const uintptr_t loopCounterOffset = uintptr_t(&shader.loopCounter) - uintptr_t(&shader);
|
||||
mov(eax, dword[statePointer + loopCounterOffset]); // rax = loop counter
|
||||
mov(eax, dword[statePointer + loopCounterOffset]); // rax = loop counter
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Helpers::panic("[ShaderJIT]: Unimplemented source index type %d", index);
|
||||
|
||||
default: Helpers::panic("[ShaderJIT]: Unimplemented source index type %d", index);
|
||||
}
|
||||
|
||||
// Swizzle and load register into dest, from [state pointer + rcx + offset] and apply the relevant swizzle
|
||||
|
@ -304,7 +312,7 @@ void ShaderEmitter::loadRegister(Xmm dest, const PICAShader& shader, u32 src, u3
|
|||
shl(rcx, 4); // rcx = rax * sizeof(vec4 of floats) = rax * 16
|
||||
swizzleAndLoadReg(inputOffset);
|
||||
jmp(end);
|
||||
|
||||
|
||||
// If (reg < 0x1F) return tempRegisters[reg - 0x10]
|
||||
L(maybeTemp);
|
||||
cmp(rax, 0x20);
|
||||
|
@ -324,7 +332,7 @@ void ShaderEmitter::loadRegister(Xmm dest, const PICAShader& shader, u32 src, u3
|
|||
jmp(end);
|
||||
|
||||
L(unknownReg);
|
||||
pxor(dest, dest); // Set dest to 0 if we're reading from a garbage register
|
||||
pxor(dest, dest); // Set dest to 0 if we're reading from a garbage register
|
||||
|
||||
L(end);
|
||||
// Negate the register if necessary
|
||||
|
@ -335,20 +343,20 @@ void ShaderEmitter::loadRegister(Xmm dest, const PICAShader& shader, u32 src, u3
|
|||
|
||||
void ShaderEmitter::storeRegister(Xmm source, const PICAShader& shader, u32 dest, u32 operandDescriptor) {
|
||||
const vec4f& destRef = getDestRef(shader, dest);
|
||||
const uintptr_t offset = uintptr_t(&destRef) - uintptr_t(&shader); // Calculate offset of register from start of the state struct
|
||||
const uintptr_t offset = uintptr_t(&destRef) - uintptr_t(&shader); // Calculate offset of register from start of the state struct
|
||||
|
||||
// Mask of which lanes to write
|
||||
u32 writeMask = operandDescriptor & 0xf;
|
||||
if (writeMask == 0xf) { // No lanes are masked, just movaps
|
||||
if (writeMask == 0xf) { // No lanes are masked, just movaps
|
||||
movaps(xword[statePointer + offset], source);
|
||||
} else if (std::popcount(writeMask) == 1) { // Only 1 register needs to be written back. This can be done with a simple shift right + movss
|
||||
int bit = std::countr_zero(writeMask); // Get which PICA register needs to be written to (0 = w, 1 = z, etc)
|
||||
} else if (std::popcount(writeMask) == 1) { // Only 1 register needs to be written back. This can be done with a simple shift right + movss
|
||||
int bit = std::countr_zero(writeMask); // Get which PICA register needs to be written to (0 = w, 1 = z, etc)
|
||||
size_t index = 3 - bit;
|
||||
const uintptr_t laneOffset = offset + index * sizeof(float);
|
||||
|
||||
if (index == 0) { // Bottom lane, no need to shift
|
||||
if (index == 0) { // Bottom lane, no need to shift
|
||||
movss(dword[statePointer + laneOffset], source);
|
||||
} else { // Shift right by 32 * index, then write bottom lane
|
||||
} else { // Shift right by 32 * index, then write bottom lane
|
||||
if (haveAVX) {
|
||||
vpsrldq(scratch1, source, index * sizeof(float));
|
||||
} else {
|
||||
|
@ -363,18 +371,17 @@ void ShaderEmitter::storeRegister(Xmm source, const PICAShader& shader, u32 dest
|
|||
// Don't accidentally overwrite scratch1 if that is what we're writing derp
|
||||
Xmm temp = (source == scratch1) ? scratch2 : scratch1;
|
||||
|
||||
movaps(temp, xword[statePointer + offset]); // Read current value of dest
|
||||
blendps(temp, source, adjustedMask); // Blend with source
|
||||
movaps(xword[statePointer + offset], temp); // Write back
|
||||
movaps(temp, xword[statePointer + offset]); // Read current value of dest
|
||||
blendps(temp, source, adjustedMask); // Blend with source
|
||||
movaps(xword[statePointer + offset], temp); // Write back
|
||||
} else {
|
||||
// Blend algo referenced from Citra
|
||||
const u8 selector = (((writeMask & 0b1000) ? 1 : 0) << 0) |
|
||||
(((writeMask & 0b0100) ? 3 : 2) << 2) |
|
||||
(((writeMask & 0b0010) ? 0 : 1) << 4) |
|
||||
(((writeMask & 0b0001) ? 2 : 3) << 6);
|
||||
|
||||
const u8 selector = (((writeMask & 0b1000) ? 1 : 0) << 0) | (((writeMask & 0b0100) ? 3 : 2) << 2) | (((writeMask & 0b0010) ? 0 : 1) << 4) |
|
||||
(((writeMask & 0b0001) ? 2 : 3) << 6);
|
||||
|
||||
// Reorder instructions based on whether the source == scratch1. This is to avoid overwriting scratch1 if it's the source,
|
||||
// While also having the memory load come first to mitigate execution hazards and give the load more time to complete before reading if possible
|
||||
// While also having the memory load come first to mitigate execution hazards and give the load more time to complete before reading if
|
||||
// possible
|
||||
if (source != scratch1) {
|
||||
movaps(scratch1, xword[statePointer + offset]);
|
||||
movaps(scratch2, source);
|
||||
|
@ -382,16 +389,16 @@ void ShaderEmitter::storeRegister(Xmm source, const PICAShader& shader, u32 dest
|
|||
movaps(scratch2, source);
|
||||
movaps(scratch1, xword[statePointer + offset]);
|
||||
}
|
||||
|
||||
unpckhps(scratch2, scratch1); // Unpack X/Y components of source and destination
|
||||
unpcklps(scratch1, source); // Unpack Z/W components of source and destination
|
||||
shufps(scratch1, scratch2, selector); // "merge-shuffle" dest and source using selecto
|
||||
movaps(xword[statePointer + offset], scratch1); // Write back
|
||||
|
||||
unpckhps(scratch2, scratch1); // Unpack X/Y components of source and destination
|
||||
unpcklps(scratch1, source); // Unpack Z/W components of source and destination
|
||||
shufps(scratch1, scratch2, selector); // "merge-shuffle" dest and source using selecto
|
||||
movaps(xword[statePointer + offset], scratch1); // Write back
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderEmitter::checkCmpRegister(const PICAShader& shader, u32 instruction) {
|
||||
static_assert(sizeof(bool) == 1 && sizeof(shader.cmpRegister) == 2); // The code below relies on bool being 1 byte exactly
|
||||
static_assert(sizeof(bool) == 1 && sizeof(shader.cmpRegister) == 2); // The code below relies on bool being 1 byte exactly
|
||||
const size_t cmpRegXOffset = uintptr_t(&shader.cmpRegister[0]) - uintptr_t(&shader);
|
||||
const size_t cmpRegYOffset = cmpRegXOffset + sizeof(bool);
|
||||
|
||||
|
@ -399,11 +406,12 @@ void ShaderEmitter::checkCmpRegister(const PICAShader& shader, u32 instruction)
|
|||
const uint refY = getBit<24>(instruction);
|
||||
const uint refX = getBit<25>(instruction);
|
||||
|
||||
// refX in the bottom byte, refY in the top byte. This is done for condition codes 0 and 1 which check both x and y, so we can emit a single instruction that checks both
|
||||
// refX in the bottom byte, refY in the top byte. This is done for condition codes 0 and 1 which check both x and y, so we can emit a single
|
||||
// instruction that checks both
|
||||
const u16 refX_refY_merged = refX | (refY << 8);
|
||||
|
||||
switch (condition) {
|
||||
case 0: // Either cmp register matches
|
||||
case 0: // Either cmp register matches
|
||||
// Z flag is 0 if at least 1 of them is set
|
||||
|
||||
// Check if X matches
|
||||
|
@ -418,20 +426,20 @@ void ShaderEmitter::checkCmpRegister(const PICAShader& shader, u32 instruction)
|
|||
// If either of them matches, set Z to 1, else set it to 0
|
||||
xor_(al, 1);
|
||||
break;
|
||||
case 1: // Both cmp registers match
|
||||
case 1: // Both cmp registers match
|
||||
cmp(word[statePointer + cmpRegXOffset], refX_refY_merged);
|
||||
break;
|
||||
case 2: // At least cmp.x matches
|
||||
case 2: // At least cmp.x matches
|
||||
cmp(byte[statePointer + cmpRegXOffset], refX);
|
||||
break;
|
||||
default: // At least cmp.y matches
|
||||
default: // At least cmp.y matches
|
||||
cmp(byte[statePointer + cmpRegYOffset], refY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderEmitter::checkBoolUniform(const PICAShader& shader, u32 instruction) {
|
||||
const u32 bit = getBits<22, 4>(instruction); // Bit of the bool uniform to check
|
||||
const u32 bit = getBits<22, 4>(instruction); // Bit of the bool uniform to check
|
||||
const uintptr_t boolUniformOffset = uintptr_t(&shader.boolUniform) - uintptr_t(&shader);
|
||||
|
||||
test(word[statePointer + boolUniformOffset], 1 << bit);
|
||||
|
@ -450,7 +458,7 @@ void ShaderEmitter::recMOV(const PICAShader& shader, u32 instruction) {
|
|||
const u32 idx = getBits<19, 2>(instruction);
|
||||
const u32 dest = getBits<21, 5>(instruction);
|
||||
|
||||
loadRegister<1>(src1_xmm, shader, src, idx, operandDescriptor); // Load source 1 into scratch1
|
||||
loadRegister<1>(src1_xmm, shader, src, idx, operandDescriptor); // Load source 1 into scratch1
|
||||
storeRegister(src1_xmm, shader, dest, operandDescriptor);
|
||||
}
|
||||
|
||||
|
@ -460,12 +468,12 @@ void ShaderEmitter::recFLR(const PICAShader& shader, u32 instruction) {
|
|||
const u32 idx = getBits<19, 2>(instruction);
|
||||
const u32 dest = getBits<21, 5>(instruction);
|
||||
|
||||
loadRegister<1>(src1_xmm, shader, src, idx, operandDescriptor); // Load source 1 into scratch1
|
||||
loadRegister<1>(src1_xmm, shader, src, idx, operandDescriptor); // Load source 1 into scratch1
|
||||
if (haveSSE4_1) {
|
||||
roundps(src1_xmm, src1_xmm, _MM_FROUND_FLOOR);
|
||||
} else {
|
||||
cvttps2dq(src1_xmm, src1_xmm); // Truncate and convert to integer
|
||||
cvtdq2ps(src1_xmm, src1_xmm); // Convert from integer back to float
|
||||
cvttps2dq(src1_xmm, src1_xmm); // Truncate and convert to integer
|
||||
cvtdq2ps(src1_xmm, src1_xmm); // Convert from integer back to float
|
||||
}
|
||||
|
||||
storeRegister(src1_xmm, shader, dest, operandDescriptor);
|
||||
|
@ -476,71 +484,69 @@ void ShaderEmitter::recMOVA(const PICAShader& shader, u32 instruction) {
|
|||
const u32 src = getBits<12, 7>(instruction);
|
||||
const u32 idx = getBits<19, 2>(instruction);
|
||||
|
||||
const bool writeX = getBit<3>(operandDescriptor); // Should we write the x component of the address register?
|
||||
const bool writeX = getBit<3>(operandDescriptor); // Should we write the x component of the address register?
|
||||
const bool writeY = getBit<2>(operandDescriptor);
|
||||
|
||||
static_assert(sizeof(shader.addrRegister) == 2 * sizeof(s32)); // Assert that the address register is 2 s32s
|
||||
static_assert(sizeof(shader.addrRegister) == 2 * sizeof(s32)); // Assert that the address register is 2 s32s
|
||||
const uintptr_t addrRegisterOffset = uintptr_t(&shader.addrRegister[0]) - uintptr_t(&shader);
|
||||
const uintptr_t addrRegisterYOffset = addrRegisterOffset + sizeof(shader.addrRegister[0]);
|
||||
|
||||
// If no register is being written to then it is a nop. Probably not common but whatever
|
||||
if (!writeX && !writeY) return;
|
||||
loadRegister<1>(src1_xmm, shader, src, idx, operandDescriptor); // Load source 1 into scratch1
|
||||
loadRegister<1>(src1_xmm, shader, src, idx, operandDescriptor); // Load source 1 into scratch1
|
||||
|
||||
// Write both
|
||||
if (writeX && writeY) {
|
||||
cvttps2dq(scratch1, src1_xmm); // Convert all lanes of src1 with truncation
|
||||
movsd(qword[statePointer + addrRegisterOffset], scratch1); // Write back bottom 2 to addr register x and ys
|
||||
}
|
||||
else if (writeX) {
|
||||
cvttss2si(eax, src1_xmm); // Convert bottom lane
|
||||
mov(dword[statePointer + addrRegisterOffset], eax); // Write it back
|
||||
}
|
||||
else if (writeY) {
|
||||
psrldq(src1_xmm, sizeof(float)); // Shift y component to bottom lane
|
||||
cvttss2si(eax, src1_xmm); // Convert bottom lane
|
||||
mov(dword[statePointer + addrRegisterYOffset], eax); // Write it back to y component
|
||||
cvttps2dq(scratch1, src1_xmm); // Convert all lanes of src1 with truncation
|
||||
movsd(qword[statePointer + addrRegisterOffset], scratch1); // Write back bottom 2 to addr register x and ys
|
||||
} else if (writeX) {
|
||||
cvttss2si(eax, src1_xmm); // Convert bottom lane
|
||||
mov(dword[statePointer + addrRegisterOffset], eax); // Write it back
|
||||
} else if (writeY) {
|
||||
psrldq(src1_xmm, sizeof(float)); // Shift y component to bottom lane
|
||||
cvttss2si(eax, src1_xmm); // Convert bottom lane
|
||||
mov(dword[statePointer + addrRegisterYOffset], eax); // Write it back to y component
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderEmitter::recADD(const PICAShader& shader, u32 instruction) {
|
||||
const u32 operandDescriptor = shader.operandDescriptors[instruction & 0x7f];
|
||||
const u32 src1 = getBits<12, 7>(instruction);
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 idx = getBits<19, 2>(instruction);
|
||||
const u32 dest = getBits<21, 5>(instruction);
|
||||
|
||||
loadRegister<1>(src1_xmm, shader, src1, idx, operandDescriptor);
|
||||
loadRegister<2>(src2_xmm, shader, src2, 0, operandDescriptor);
|
||||
addps(src1_xmm, src2_xmm); // Dot product between the 2 register
|
||||
addps(src1_xmm, src2_xmm); // Dot product between the 2 register
|
||||
storeRegister(src1_xmm, shader, dest, operandDescriptor);
|
||||
}
|
||||
|
||||
void ShaderEmitter::recDP3(const PICAShader& shader, u32 instruction) {
|
||||
const u32 operandDescriptor = shader.operandDescriptors[instruction & 0x7f];
|
||||
const u32 src1 = getBits<12, 7>(instruction);
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 idx = getBits<19, 2>(instruction);
|
||||
const u32 dest = getBits<21, 5>(instruction);
|
||||
|
||||
// TODO: Safe multiplication equivalent (Multiplication is not IEEE compliant on the PICA)
|
||||
loadRegister<1>(src1_xmm, shader, src1, idx, operandDescriptor);
|
||||
loadRegister<2>(src2_xmm, shader, src2, 0, operandDescriptor);
|
||||
dpps(src1_xmm, src2_xmm, 0b01111111); // 3-lane dot product between the 2 registers, store the result in all lanes of scratch1 similarly to PICA
|
||||
dpps(src1_xmm, src2_xmm, 0b01111111); // 3-lane dot product between the 2 registers, store the result in all lanes of scratch1 similarly to PICA
|
||||
storeRegister(src1_xmm, shader, dest, operandDescriptor);
|
||||
}
|
||||
|
||||
void ShaderEmitter::recDP4(const PICAShader& shader, u32 instruction) {
|
||||
const u32 operandDescriptor = shader.operandDescriptors[instruction & 0x7f];
|
||||
const u32 src1 = getBits<12, 7>(instruction);
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 idx = getBits<19, 2>(instruction);
|
||||
const u32 dest = getBits<21, 5>(instruction);
|
||||
|
||||
// TODO: Safe multiplication equivalent (Multiplication is not IEEE compliant on the PICA)
|
||||
loadRegister<1>(src1_xmm, shader, src1, idx, operandDescriptor);
|
||||
loadRegister<2>(src2_xmm, shader, src2, 0, operandDescriptor);
|
||||
dpps(src1_xmm, src2_xmm, 0b11111111); // 4-lane dot product between the 2 registers, store the result in all lanes of scratch1 similarly to PICA
|
||||
dpps(src1_xmm, src2_xmm, 0b11111111); // 4-lane dot product between the 2 registers, store the result in all lanes of scratch1 similarly to PICA
|
||||
storeRegister(src1_xmm, shader, dest, operandDescriptor);
|
||||
}
|
||||
|
||||
|
@ -573,7 +579,7 @@ void ShaderEmitter::recDPH(const PICAShader& shader, u32 instruction) {
|
|||
void ShaderEmitter::recMAX(const PICAShader& shader, u32 instruction) {
|
||||
const u32 operandDescriptor = shader.operandDescriptors[instruction & 0x7f];
|
||||
const u32 src1 = getBits<12, 7>(instruction);
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 idx = getBits<19, 2>(instruction);
|
||||
const u32 dest = getBits<21, 5>(instruction);
|
||||
|
||||
|
@ -586,7 +592,7 @@ void ShaderEmitter::recMAX(const PICAShader& shader, u32 instruction) {
|
|||
void ShaderEmitter::recMIN(const PICAShader& shader, u32 instruction) {
|
||||
const u32 operandDescriptor = shader.operandDescriptors[instruction & 0x7f];
|
||||
const u32 src1 = getBits<12, 7>(instruction);
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 idx = getBits<19, 2>(instruction);
|
||||
const u32 dest = getBits<21, 5>(instruction);
|
||||
|
||||
|
@ -599,7 +605,7 @@ void ShaderEmitter::recMIN(const PICAShader& shader, u32 instruction) {
|
|||
void ShaderEmitter::recMUL(const PICAShader& shader, u32 instruction) {
|
||||
const u32 operandDescriptor = shader.operandDescriptors[instruction & 0x7f];
|
||||
const u32 src1 = getBits<12, 7>(instruction);
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 idx = getBits<19, 2>(instruction);
|
||||
const u32 dest = getBits<21, 5>(instruction);
|
||||
|
||||
|
@ -617,13 +623,13 @@ void ShaderEmitter::recRCP(const PICAShader& shader, u32 instruction) {
|
|||
const u32 dest = getBits<21, 5>(instruction);
|
||||
const u32 writeMask = operandDescriptor & 0xf;
|
||||
|
||||
loadRegister<1>(src1_xmm, shader, src, idx, operandDescriptor); // Load source 1 into scratch1
|
||||
rcpss(src1_xmm, src1_xmm); // Compute rcp approximation
|
||||
loadRegister<1>(src1_xmm, shader, src, idx, operandDescriptor); // Load source 1 into scratch1
|
||||
rcpss(src1_xmm, src1_xmm); // Compute rcp approximation
|
||||
|
||||
// If we only write back the x component to the result, we needn't perform a shuffle to do res = res.xxxx
|
||||
// Otherwise we do
|
||||
if (writeMask != 0x8) {// Copy bottom lane to all lanes if we're not simply writing back x
|
||||
shufps(src1_xmm, src1_xmm, 0); // src1_xmm = src1_xmm.xxxx
|
||||
if (writeMask != 0x8) { // Copy bottom lane to all lanes if we're not simply writing back x
|
||||
shufps(src1_xmm, src1_xmm, 0); // src1_xmm = src1_xmm.xxxx
|
||||
}
|
||||
|
||||
storeRegister(src1_xmm, shader, dest, operandDescriptor);
|
||||
|
@ -636,13 +642,13 @@ void ShaderEmitter::recRSQ(const PICAShader& shader, u32 instruction) {
|
|||
const u32 dest = getBits<21, 5>(instruction);
|
||||
const u32 writeMask = operandDescriptor & 0xf;
|
||||
|
||||
loadRegister<1>(src1_xmm, shader, src, idx, operandDescriptor); // Load source 1 into scratch1
|
||||
rsqrtss(src1_xmm, src1_xmm); // Compute rsqrt approximation
|
||||
loadRegister<1>(src1_xmm, shader, src, idx, operandDescriptor); // Load source 1 into scratch1
|
||||
rsqrtss(src1_xmm, src1_xmm); // Compute rsqrt approximation
|
||||
|
||||
// If we only write back the x component to the result, we needn't perform a shuffle to do res = res.xxxx
|
||||
// Otherwise we do
|
||||
if (writeMask != 0x8) {// Copy bottom lane to all lanes if we're not simply writing back x
|
||||
shufps(src1_xmm, src1_xmm, 0); // src1_xmm = src1_xmm.xxxx
|
||||
if (writeMask != 0x8) { // Copy bottom lane to all lanes if we're not simply writing back x
|
||||
shufps(src1_xmm, src1_xmm, 0); // src1_xmm = src1_xmm.xxxx
|
||||
}
|
||||
|
||||
storeRegister(src1_xmm, shader, dest, operandDescriptor);
|
||||
|
@ -668,7 +674,7 @@ void ShaderEmitter::recMAD(const PICAShader& shader, u32 instruction) {
|
|||
vfmadd213ps(src1_xmm, src2_xmm, src3_xmm);
|
||||
storeRegister(src1_xmm, shader, dest, operandDescriptor);
|
||||
}
|
||||
|
||||
|
||||
// If we don't have FMA3, do a multiplication and addition
|
||||
else {
|
||||
// Multiply src1 * src2
|
||||
|
@ -712,7 +718,7 @@ void ShaderEmitter::recSGE(const PICAShader& shader, u32 instruction) {
|
|||
|
||||
loadRegister<1>(src1_xmm, shader, src1, isSGEI ? 0 : idx, operandDescriptor);
|
||||
loadRegister<2>(src2_xmm, shader, src2, isSGEI ? idx : 0, operandDescriptor);
|
||||
|
||||
|
||||
// SSE does not have a cmpgeps instruction so we turn src1 >= src2 to src2 <= src1, result in src2
|
||||
cmpleps(src2_xmm, src1_xmm);
|
||||
andps(src2_xmm, xword[rip + onesVector]);
|
||||
|
@ -722,7 +728,7 @@ void ShaderEmitter::recSGE(const PICAShader& shader, u32 instruction) {
|
|||
void ShaderEmitter::recCMP(const PICAShader& shader, u32 instruction) {
|
||||
const u32 operandDescriptor = shader.operandDescriptors[instruction & 0x7f];
|
||||
const u32 src1 = getBits<12, 7>(instruction);
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
||||
const u32 idx = getBits<19, 2>(instruction);
|
||||
const u32 cmpY = getBits<21, 3>(instruction);
|
||||
const u32 cmpX = getBits<24, 3>(instruction);
|
||||
|
@ -731,20 +737,10 @@ void ShaderEmitter::recCMP(const PICAShader& shader, u32 instruction) {
|
|||
loadRegister<2>(src2_xmm, shader, src2, 0, operandDescriptor);
|
||||
|
||||
// Condition codes for cmpps
|
||||
enum : u8 {
|
||||
CMP_EQ = 0,
|
||||
CMP_LT = 1,
|
||||
CMP_LE = 2,
|
||||
CMP_UNORD = 3,
|
||||
CMP_NEQ = 4,
|
||||
CMP_NLT = 5,
|
||||
CMP_NLE = 6,
|
||||
CMP_ORD = 7,
|
||||
CMP_TRUE = 15
|
||||
};
|
||||
enum : u8 { CMP_EQ = 0, CMP_LT = 1, CMP_LE = 2, CMP_UNORD = 3, CMP_NEQ = 4, CMP_NLT = 5, CMP_NLE = 6, CMP_ORD = 7, CMP_TRUE = 15 };
|
||||
|
||||
// Map from PICA condition codes (used as index) to x86 condition codes
|
||||
static constexpr std::array<u8, 8> conditionCodes = { CMP_EQ, CMP_NEQ, CMP_LT, CMP_LE, CMP_LT, CMP_LE, CMP_TRUE, CMP_TRUE };
|
||||
static constexpr std::array<u8, 8> conditionCodes = {CMP_EQ, CMP_NEQ, CMP_LT, CMP_LE, CMP_LT, CMP_LE, CMP_TRUE, CMP_TRUE};
|
||||
|
||||
// SSE does not offer GT or GE comparisons in the cmpps instruction, so we need to flip the left and right operands in that case and use LT/LE
|
||||
const bool invertX = (cmpX == 4 || cmpX == 5);
|
||||
|
@ -757,37 +753,37 @@ void ShaderEmitter::recCMP(const PICAShader& shader, u32 instruction) {
|
|||
const u8 compareFuncX = conditionCodes[cmpX];
|
||||
const u8 compareFuncY = conditionCodes[cmpY];
|
||||
|
||||
static_assert(sizeof(shader.cmpRegister[0]) == 1 && sizeof(shader.cmpRegister) == 2); // The code below relies on bool being 1 byte exactly
|
||||
static_assert(sizeof(shader.cmpRegister[0]) == 1 && sizeof(shader.cmpRegister) == 2); // The code below relies on bool being 1 byte exactly
|
||||
const size_t cmpRegXOffset = uintptr_t(&shader.cmpRegister[0]) - uintptr_t(&shader);
|
||||
const size_t cmpRegYOffset = cmpRegXOffset + sizeof(bool);
|
||||
|
||||
// Cmp x and y are the same compare function, we can use a single cmp instruction
|
||||
if (cmpX == cmpY) {
|
||||
cmpps(lhs_x, rhs_x, compareFuncX);
|
||||
movq(rax, lhs_x); // Move both comparison results to rax
|
||||
test(eax, eax); // Check bottom 32 bits first
|
||||
setne(byte[statePointer + cmpRegXOffset]); // set cmp.x
|
||||
movq(rax, lhs_x); // Move both comparison results to rax
|
||||
test(eax, eax); // Check bottom 32 bits first
|
||||
setne(byte[statePointer + cmpRegXOffset]); // set cmp.x
|
||||
|
||||
shr(rax, 32); // Check top 32 bits (shr will set the zero flag properly)
|
||||
setne(byte[statePointer + cmpRegYOffset]); // set cmp.y
|
||||
shr(rax, 32); // Check top 32 bits (shr will set the zero flag properly)
|
||||
setne(byte[statePointer + cmpRegYOffset]); // set cmp.y
|
||||
} else {
|
||||
if (haveAVX) {
|
||||
vcmpps(scratch1, lhs_x, rhs_x, compareFuncX); // Perform comparison for X component and store result in scratch1
|
||||
vcmpps(scratch2, lhs_y, rhs_y, compareFuncY); // Perform comparison for Y component and store result in scratch2
|
||||
vcmpps(scratch1, lhs_x, rhs_x, compareFuncX); // Perform comparison for X component and store result in scratch1
|
||||
vcmpps(scratch2, lhs_y, rhs_y, compareFuncY); // Perform comparison for Y component and store result in scratch2
|
||||
} else {
|
||||
movaps(scratch1, lhs_x); // Copy the left hand operands to temp registers
|
||||
movaps(scratch1, lhs_x); // Copy the left hand operands to temp registers
|
||||
movaps(scratch2, lhs_y);
|
||||
|
||||
cmpps(scratch1, rhs_x, compareFuncX); // Perform the compares
|
||||
cmpps(scratch1, rhs_x, compareFuncX); // Perform the compares
|
||||
cmpps(scratch2, rhs_y, compareFuncY);
|
||||
}
|
||||
|
||||
movd(eax, scratch1); // Move results to eax for X and edx for Y
|
||||
movd(eax, scratch1); // Move results to eax for X and edx for Y
|
||||
movq(rdx, scratch2);
|
||||
|
||||
test(eax, eax); // Write back results with setne
|
||||
test(eax, eax); // Write back results with setne
|
||||
setne(byte[statePointer + cmpRegXOffset]);
|
||||
shr(rdx, 32); // We want the y component for the second comparison. This shift will set zero flag to 0 if the comparison is true
|
||||
shr(rdx, 32); // We want the y component for the second comparison. This shift will set zero flag to 0 if the comparison is true
|
||||
setne(byte[statePointer + cmpRegYOffset]);
|
||||
}
|
||||
}
|
||||
|
@ -807,10 +803,10 @@ void ShaderEmitter::recIFC(const PICAShader& shader, u32 instruction) {
|
|||
jnz(elseBlock, T_NEAR);
|
||||
compileUntil(shader, dest);
|
||||
|
||||
if (num == 0) { // Else block is empty,
|
||||
if (num == 0) { // Else block is empty,
|
||||
L(elseBlock);
|
||||
} else { // Else block is NOT empty
|
||||
jmp(endIf, T_NEAR); // Skip executing the else branch if the if branch was ran
|
||||
} else { // Else block is NOT empty
|
||||
jmp(endIf, T_NEAR); // Skip executing the else branch if the if branch was ran
|
||||
L(elseBlock);
|
||||
compileUntil(shader, dest + num);
|
||||
L(endIf);
|
||||
|
@ -832,10 +828,10 @@ void ShaderEmitter::recIFU(const PICAShader& shader, u32 instruction) {
|
|||
jz(elseBlock, T_NEAR);
|
||||
compileUntil(shader, dest);
|
||||
|
||||
if (num == 0) { // Else block is empty,
|
||||
if (num == 0) { // Else block is empty,
|
||||
L(elseBlock);
|
||||
} else { // Else block is NOT empty
|
||||
jmp(endIf, T_NEAR); // Skip executing the else branch if the if branch was ran
|
||||
} else { // Else block is NOT empty
|
||||
jmp(endIf, T_NEAR); // Skip executing the else branch if the if branch was ran
|
||||
L(elseBlock);
|
||||
compileUntil(shader, dest + num);
|
||||
L(endIf);
|
||||
|
@ -888,7 +884,7 @@ void ShaderEmitter::recJMPC(const PICAShader& shader, u32 instruction) {
|
|||
}
|
||||
|
||||
void ShaderEmitter::recJMPU(const PICAShader& shader, u32 instruction) {
|
||||
bool jumpIfFalse = instruction & 1; // If the LSB is 0 we want to compare to true, otherwise compare to false
|
||||
bool jumpIfFalse = instruction & 1; // If the LSB is 0 we want to compare to true, otherwise compare to false
|
||||
const u32 dest = getBits<10, 12>(instruction);
|
||||
|
||||
Label& l = instructionLabels[dest];
|
||||
|
@ -922,13 +918,13 @@ void ShaderEmitter::recLOOP(const PICAShader& shader, u32 instruction) {
|
|||
// Offset of the loop register
|
||||
const uintptr_t loopRegOffset = uintptr_t(&shader.loopCounter) - uintptr_t(&shader);
|
||||
|
||||
movzx(eax, byte[statePointer + uniformOffset]); // eax = loop iteration count
|
||||
movzx(ecx, byte[statePointer + uniformOffset + sizeof(u8)]); // ecx = initial loop counter value
|
||||
movzx(edx, byte[statePointer + uniformOffset + 2 * sizeof(u8)]); // edx = loop increment
|
||||
movzx(eax, byte[statePointer + uniformOffset]); // eax = loop iteration count
|
||||
movzx(ecx, byte[statePointer + uniformOffset + sizeof(u8)]); // ecx = initial loop counter value
|
||||
movzx(edx, byte[statePointer + uniformOffset + 2 * sizeof(u8)]); // edx = loop increment
|
||||
|
||||
add(eax, 1); // The iteration count is actually uniform.x + 1
|
||||
mov(dword[statePointer + loopRegOffset], ecx); // Set loop counter
|
||||
|
||||
add(eax, 1); // The iteration count is actually uniform.x + 1
|
||||
mov(dword[statePointer + loopRegOffset], ecx); // Set loop counter
|
||||
|
||||
// TODO: This might break if an instruction in a loop decides to yield...
|
||||
push(rax); // Push loop iteration counter
|
||||
push(rdx); // Push loop increment
|
||||
|
@ -957,8 +953,8 @@ void ShaderEmitter::recLG2(const PICAShader& shader, u32 instruction) {
|
|||
const u32 writeMask = getBits<0, 4>(operandDescriptor);
|
||||
|
||||
loadRegister<1>(src1_xmm, shader, src, idx, operandDescriptor);
|
||||
call(log2Func); // Result is output in src1_xmm
|
||||
|
||||
call(log2Func); // Result is output in src1_xmm
|
||||
|
||||
if (writeMask != 0x8) { // Copy bottom lane to all lanes if we're not simply writing back x
|
||||
shufps(src1_xmm, src1_xmm, 0); // src1_xmm = src1_xmm.xxxx
|
||||
}
|
||||
|
@ -1147,7 +1143,7 @@ Xbyak::Label ShaderEmitter::emitExp2Func() {
|
|||
align(16);
|
||||
L(subroutine);
|
||||
|
||||
// Handle edge cases
|
||||
// HandleType edge cases
|
||||
ucomiss(src1_xmm, src1_xmm);
|
||||
jp(retLabel);
|
||||
|
||||
|
@ -1277,4 +1273,4 @@ void ShaderEmitter::emitPrintLog(const PICAShader& shaderUnit) {
|
|||
pop(rbp);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
#ifdef PANDA3DS_ENABLE_VULKAN
|
||||
#include "renderer_vk/renderer_vk.hpp"
|
||||
#endif
|
||||
#ifdef PANDA3DS_ENABLE_METAL
|
||||
#include "renderer_mtl/renderer_mtl.hpp"
|
||||
#endif
|
||||
|
||||
constexpr u32 topScreenWidth = 240;
|
||||
constexpr u32 topScreenHeight = 400;
|
||||
|
@ -52,6 +55,12 @@ GPU::GPU(Memory& mem, EmulatorConfig& config) : mem(mem), config(config) {
|
|||
renderer.reset(new RendererVK(*this, regs, externalRegs));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef PANDA3DS_ENABLE_METAL
|
||||
case RendererType::Metal: {
|
||||
renderer.reset(new RendererMTL(*this, regs, externalRegs));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
Helpers::panic("Rendering backend not supported: %s", Renderer::typeToString(config.rendererType));
|
||||
|
@ -355,7 +364,7 @@ PICA::Vertex GPU::getImmediateModeVertex() {
|
|||
|
||||
// Run VS and return vertex data. TODO: Don't hardcode offsets for each attribute
|
||||
shaderUnit.vs.run();
|
||||
|
||||
|
||||
// Map shader outputs to fixed function properties
|
||||
const u32 totalShaderOutputs = regs[PICA::InternalRegs::ShaderOutputCount] & 7;
|
||||
for (int i = 0; i < totalShaderOutputs; i++) {
|
||||
|
|
|
@ -232,7 +232,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
|||
const u32 primType = getBits<8, 2>(primConfig);
|
||||
|
||||
// If we've reached 3 verts, issue a draw call
|
||||
// Handle rendering depending on the primitive type
|
||||
// HandleType rendering depending on the primitive type
|
||||
if (immediateModeVertIndex == 3) {
|
||||
renderer->drawVertices(PICA::PrimType::TriangleList, immediateModeVertices);
|
||||
|
||||
|
|
|
@ -77,8 +77,8 @@ void PICAShader::run() {
|
|||
default: Helpers::panic("Unimplemented PICA instruction %08X (Opcode = %02X)", instruction, opcode);
|
||||
}
|
||||
|
||||
// Handle control flow statements. The ordering is important as the priority goes: LOOP > IF > CALL
|
||||
// Handle loop
|
||||
// HandleType control flow statements. The ordering is important as the priority goes: LOOP > IF > CALL
|
||||
// HandleType loop
|
||||
if (loopIndex != 0) {
|
||||
auto& loop = loopInfo[loopIndex - 1];
|
||||
if (pc == loop.endingPC) { // Check if the loop needs to start over
|
||||
|
@ -91,7 +91,7 @@ void PICAShader::run() {
|
|||
}
|
||||
}
|
||||
|
||||
// Handle ifs
|
||||
// HandleType ifs
|
||||
if (ifIndex != 0) {
|
||||
auto& info = conditionalInfo[ifIndex - 1];
|
||||
if (pc == info.endingPC) { // Check if the IF block ended
|
||||
|
@ -100,7 +100,7 @@ void PICAShader::run() {
|
|||
}
|
||||
}
|
||||
|
||||
// Handle calls
|
||||
// HandleType calls
|
||||
if (callIndex != 0) {
|
||||
auto& info = callInfo[callIndex - 1];
|
||||
if (pc == info.endingPC) { // Check if the CALL block ended
|
||||
|
@ -753,4 +753,4 @@ void PICAShader::jmpu(u32 instruction) {
|
|||
|
||||
if (((boolUniform >> bit) & 1) == test) // Jump if the bool uniform is the value we want
|
||||
pc = dest;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,34 +12,36 @@ static const char* arbitrationTypeToString(u32 type) {
|
|||
}
|
||||
}
|
||||
|
||||
Handle Kernel::makeArbiter() {
|
||||
HandleType Kernel::makeArbiter() {
|
||||
if (arbiterCount >= appResourceLimits.maxAddressArbiters) {
|
||||
Helpers::panic("Overflowed the number of address arbiters");
|
||||
}
|
||||
arbiterCount++;
|
||||
|
||||
Handle ret = makeObject(KernelObjectType::AddressArbiter);
|
||||
HandleType ret = makeObject(KernelObjectType::AddressArbiter);
|
||||
objects[ret].data = new AddressArbiter();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Result CreateAddressArbiter(Handle* arbiter)
|
||||
// Result CreateAddressArbiter(HandleType* arbiter)
|
||||
void Kernel::createAddressArbiter() {
|
||||
logSVC("CreateAddressArbiter\n");
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = makeArbiter();
|
||||
}
|
||||
|
||||
// Result ArbitrateAddress(Handle arbiter, u32 addr, ArbitrationType type, s32 value, s64 nanoseconds)
|
||||
// Result ArbitrateAddress(HandleType arbiter, u32 addr, ArbitrationType type, s32 value, s64 nanoseconds)
|
||||
void Kernel::arbitrateAddress() {
|
||||
const Handle handle = regs[0];
|
||||
const HandleType handle = regs[0];
|
||||
const u32 address = regs[1];
|
||||
const u32 type = regs[2];
|
||||
const s32 value = s32(regs[3]);
|
||||
const s64 ns = s64(u64(regs[4]) | (u64(regs[5]) << 32));
|
||||
|
||||
logSVC("ArbitrateAddress(Handle = %X, address = %08X, type = %s, value = %d, ns = %lld)\n", handle, address,
|
||||
arbitrationTypeToString(type), value, ns);
|
||||
logSVC(
|
||||
"ArbitrateAddress(HandleType = %X, address = %08X, type = %s, value = %d, ns = %lld)\n", handle, address, arbitrationTypeToString(type),
|
||||
value, ns
|
||||
);
|
||||
|
||||
const auto arbiter = getObject(handle, KernelObjectType::AddressArbiter);
|
||||
if (arbiter == nullptr) [[unlikely]] {
|
||||
|
@ -61,7 +63,7 @@ void Kernel::arbitrateAddress() {
|
|||
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 +73,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);
|
||||
|
@ -79,12 +81,9 @@ void Kernel::arbitrateAddress() {
|
|||
break;
|
||||
}
|
||||
|
||||
case ArbitrationType::Signal:
|
||||
signalArbiter(address, value);
|
||||
break;
|
||||
case ArbitrationType::Signal: signalArbiter(address, value); break;
|
||||
|
||||
default:
|
||||
Helpers::panic("ArbitrateAddress: Unimplemented type %s", arbitrationTypeToString(type));
|
||||
default: Helpers::panic("ArbitrateAddress: Unimplemented type %s", arbitrationTypeToString(type));
|
||||
}
|
||||
|
||||
requireReschedule();
|
||||
|
@ -92,8 +91,9 @@ void Kernel::arbitrateAddress() {
|
|||
|
||||
// Signal up to "threadCount" threads waiting on the arbiter indicated by "waitingAddress"
|
||||
void Kernel::signalArbiter(u32 waitingAddress, s32 threadCount) {
|
||||
if (threadCount == 0) [[unlikely]] return;
|
||||
s32 count = 0; // Number of threads we've woken up
|
||||
if (threadCount == 0) [[unlikely]]
|
||||
return;
|
||||
s32 count = 0; // Number of threads we've woken up
|
||||
|
||||
// Wake threads with the highest priority threads being woken up first
|
||||
for (auto index : threadIndices) {
|
||||
|
@ -106,4 +106,4 @@ void Kernel::signalArbiter(u32 waitingAddress, s32 threadCount) {
|
|||
if (count == threadCount && threadCount > 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
#include "kernel.hpp"
|
||||
|
||||
namespace DirectoryOps {
|
||||
enum : u32 {
|
||||
Read = 0x08010042,
|
||||
Close = 0x08020000
|
||||
};
|
||||
enum : u32 { Read = 0x08010042, Close = 0x08020000 };
|
||||
}
|
||||
|
||||
// Helper to convert std::string to an 8.3 filename to mimic how Directory::Read works
|
||||
|
@ -28,7 +25,7 @@ Filename83 convertTo83(const std::string& path) {
|
|||
// Convert a character to add it to the 8.3 name
|
||||
// "Characters such as + are changed to the underscore _, and letters are put in uppercase"
|
||||
// For now we put letters in uppercase until we find out what is supposed to be converted to _ and so on
|
||||
auto convertCharacter = [](char c) { return (char) std::toupper(c); };
|
||||
auto convertCharacter = [](char c) { return (char)std::toupper(c); };
|
||||
|
||||
// List of forbidden character for 8.3 filenames, from Citra
|
||||
// TODO: Use constexpr when C++20 support is solid
|
||||
|
@ -66,7 +63,7 @@ Filename83 convertTo83(const std::string& path) {
|
|||
filenameTooBig = true;
|
||||
break;
|
||||
}
|
||||
filename[validCharacterCount++] = convertCharacter(c); // Append character to filename
|
||||
filename[validCharacterCount++] = convertCharacter(c); // Append character to filename
|
||||
}
|
||||
|
||||
// Truncate name to 6 characters and denote that it is too big
|
||||
|
@ -87,7 +84,7 @@ Filename83 convertTo83(const std::string& path) {
|
|||
return {filename, extension};
|
||||
}
|
||||
|
||||
void Kernel::handleDirectoryOperation(u32 messagePointer, Handle directory) {
|
||||
void Kernel::handleDirectoryOperation(u32 messagePointer, HandleType directory) {
|
||||
const u32 cmd = mem.read32(messagePointer);
|
||||
switch (cmd) {
|
||||
case DirectoryOps::Close: closeDirectory(messagePointer, directory); break;
|
||||
|
@ -96,7 +93,7 @@ void Kernel::handleDirectoryOperation(u32 messagePointer, Handle directory) {
|
|||
}
|
||||
}
|
||||
|
||||
void Kernel::closeDirectory(u32 messagePointer, Handle directory) {
|
||||
void Kernel::closeDirectory(u32 messagePointer, HandleType directory) {
|
||||
logFileIO("Closed directory %X\n", directory);
|
||||
|
||||
const auto p = getObject(directory, KernelObjectType::Directory);
|
||||
|
@ -109,11 +106,11 @@ void Kernel::closeDirectory(u32 messagePointer, Handle directory) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void Kernel::readDirectory(u32 messagePointer, Handle directory) {
|
||||
void Kernel::readDirectory(u32 messagePointer, HandleType directory) {
|
||||
const u32 entryCount = mem.read32(messagePointer + 4);
|
||||
const u32 outPointer = mem.read32(messagePointer + 12);
|
||||
logFileIO("Directory::Read (handle = %X, entry count = %d, out pointer = %08X)\n", directory, entryCount, outPointer);
|
||||
|
||||
|
||||
const auto p = getObject(directory, KernelObjectType::Directory);
|
||||
if (p == nullptr) [[unlikely]] {
|
||||
Helpers::panic("Called ReadDirectory on non-existent directory");
|
||||
|
@ -136,9 +133,9 @@ void Kernel::readDirectory(u32 messagePointer, Handle directory) {
|
|||
bool isDirectory = std::filesystem::is_directory(relative);
|
||||
|
||||
std::u16string nameU16 = relative.u16string();
|
||||
bool isHidden = nameU16[0] == u'.'; // If the first character is a dot then this is a hidden file/folder
|
||||
bool isHidden = nameU16[0] == u'.'; // If the first character is a dot then this is a hidden file/folder
|
||||
|
||||
const u32 entryPointer = outPointer + (count * 0x228); // 0x228 is the size of a single entry
|
||||
const u32 entryPointer = outPointer + (count * 0x228); // 0x228 is the size of a single entry
|
||||
u32 utfPointer = entryPointer;
|
||||
u32 namePointer = entryPointer + 0x20C;
|
||||
u32 extensionPointer = entryPointer + 0x216;
|
||||
|
@ -152,7 +149,7 @@ void Kernel::readDirectory(u32 messagePointer, Handle directory) {
|
|||
mem.write16(utfPointer, u16(c));
|
||||
utfPointer += sizeof(u16);
|
||||
}
|
||||
mem.write16(utfPointer, 0); // Null terminate the UTF16 name
|
||||
mem.write16(utfPointer, 0); // Null terminate the UTF16 name
|
||||
|
||||
// Write 8.3 filename-extension
|
||||
for (auto c : shortFilename) {
|
||||
|
|
|
@ -1,38 +1,25 @@
|
|||
#include "kernel.hpp"
|
||||
|
||||
namespace Commands {
|
||||
enum : u32 {
|
||||
Throw = 0x00010800
|
||||
};
|
||||
enum : u32 { Throw = 0x00010800 };
|
||||
}
|
||||
|
||||
namespace FatalErrorType {
|
||||
enum : u32 {
|
||||
Generic = 0,
|
||||
Corrupted = 1,
|
||||
CardRemoved = 2,
|
||||
Exception = 3,
|
||||
ResultFailure = 4,
|
||||
Logged = 5
|
||||
};
|
||||
enum : u32 { Generic = 0, Corrupted = 1, CardRemoved = 2, Exception = 3, ResultFailure = 4, Logged = 5 };
|
||||
}
|
||||
|
||||
// Handle SendSyncRequest targetting the err:f port
|
||||
// HandleType SendSyncRequest targetting the err:f port
|
||||
void Kernel::handleErrorSyncRequest(u32 messagePointer) {
|
||||
u32 cmd = mem.read32(messagePointer);
|
||||
switch (cmd) {
|
||||
case Commands::Throw:
|
||||
throwError(messagePointer);
|
||||
break;
|
||||
case Commands::Throw: throwError(messagePointer); break;
|
||||
|
||||
default:
|
||||
Helpers::panic("Unimplemented err:f command %08X\n", cmd);
|
||||
break;
|
||||
default: Helpers::panic("Unimplemented err:f command %08X\n", cmd); break;
|
||||
}
|
||||
}
|
||||
|
||||
void Kernel::throwError(u32 messagePointer) {
|
||||
const auto type = mem.read8(messagePointer + 4); // Fatal error type
|
||||
const auto type = mem.read8(messagePointer + 4); // Fatal error type
|
||||
const u32 pc = mem.read32(messagePointer + 12);
|
||||
const u32 pid = mem.read32(messagePointer + 16);
|
||||
logError("Thrown fatal error @ %08X (pid = %X, type = %d)\n", pc, pid, type);
|
||||
|
@ -44,4 +31,4 @@ void Kernel::throwError(u32 messagePointer) {
|
|||
}
|
||||
|
||||
Helpers::panic("Thrown fatal error");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#include "kernel.hpp"
|
||||
#include "cpu.hpp"
|
||||
#include <bit>
|
||||
#include <utility>
|
||||
|
||||
#include "cpu.hpp"
|
||||
#include "kernel.hpp"
|
||||
|
||||
const char* Kernel::resetTypeToString(u32 type) {
|
||||
switch (type) {
|
||||
case 0: return "One shot";
|
||||
|
@ -12,13 +13,13 @@ const char* Kernel::resetTypeToString(u32 type) {
|
|||
}
|
||||
}
|
||||
|
||||
Handle Kernel::makeEvent(ResetType resetType, Event::CallbackType callback) {
|
||||
Handle ret = makeObject(KernelObjectType::Event);
|
||||
HandleType Kernel::makeEvent(ResetType resetType, Event::CallbackType callback) {
|
||||
HandleType ret = makeObject(KernelObjectType::Event);
|
||||
objects[ret].data = new Event(resetType, callback);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Kernel::signalEvent(Handle handle) {
|
||||
bool Kernel::signalEvent(HandleType handle) {
|
||||
KernelObject* object = getObject(handle, KernelObjectType::Event);
|
||||
if (object == nullptr) [[unlikely]] {
|
||||
Helpers::panic("Tried to signal non-existent event");
|
||||
|
@ -52,13 +53,12 @@ bool Kernel::signalEvent(Handle handle) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Result CreateEvent(Handle* event, ResetType resetType)
|
||||
// Result CreateEvent(HandleType* event, ResetType resetType)
|
||||
void Kernel::svcCreateEvent() {
|
||||
const u32 outPointer = regs[0];
|
||||
const u32 resetType = regs[1];
|
||||
|
||||
if (resetType > 2)
|
||||
Helpers::panic("Invalid reset type for event %d", resetType);
|
||||
if (resetType > 2) Helpers::panic("Invalid reset type for event %d", resetType);
|
||||
|
||||
logSVC("CreateEvent(handle pointer = %08X, resetType = %s)\n", outPointer, resetTypeToString(resetType));
|
||||
|
||||
|
@ -66,9 +66,9 @@ void Kernel::svcCreateEvent() {
|
|||
regs[1] = makeEvent(static_cast<ResetType>(resetType));
|
||||
}
|
||||
|
||||
// Result ClearEvent(Handle event)
|
||||
// Result ClearEvent(HandleType event)
|
||||
void Kernel::svcClearEvent() {
|
||||
const Handle handle = regs[0];
|
||||
const HandleType handle = regs[0];
|
||||
const auto event = getObject(handle, KernelObjectType::Event);
|
||||
logSVC("ClearEvent(event handle = %X)\n", handle);
|
||||
|
||||
|
@ -82,9 +82,9 @@ void Kernel::svcClearEvent() {
|
|||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
// Result SignalEvent(Handle event)
|
||||
// Result SignalEvent(HandleType event)
|
||||
void Kernel::svcSignalEvent() {
|
||||
const Handle handle = regs[0];
|
||||
const HandleType handle = regs[0];
|
||||
logSVC("SignalEvent(event handle = %X)\n", handle);
|
||||
KernelObject* object = getObject(handle, KernelObjectType::Event);
|
||||
|
||||
|
@ -98,9 +98,9 @@ void Kernel::svcSignalEvent() {
|
|||
}
|
||||
}
|
||||
|
||||
// Result WaitSynchronization1(Handle handle, s64 timeout_nanoseconds)
|
||||
// Result WaitSynchronization1(HandleType handle, s64 timeout_nanoseconds)
|
||||
void Kernel::waitSynchronization1() {
|
||||
const Handle handle = regs[0];
|
||||
const HandleType handle = regs[0];
|
||||
const s64 ns = s64(u64(regs[2]) | (u64(regs[3]) << 32));
|
||||
logSVC("WaitSynchronization1(handle = %X, ns = %lld)\n", handle, ns);
|
||||
|
||||
|
@ -117,7 +117,7 @@ void Kernel::waitSynchronization1() {
|
|||
}
|
||||
|
||||
if (!shouldWaitOnObject(object)) {
|
||||
acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready
|
||||
acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready
|
||||
regs[0] = Result::Success;
|
||||
} else {
|
||||
// Timeout is 0, don't bother waiting, instantly timeout
|
||||
|
@ -126,7 +126,7 @@ void Kernel::waitSynchronization1() {
|
|||
return;
|
||||
}
|
||||
|
||||
regs[0] = Result::OS::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);
|
||||
|
@ -141,7 +141,7 @@ void Kernel::waitSynchronization1() {
|
|||
}
|
||||
}
|
||||
|
||||
// Result WaitSynchronizationN(s32* out, Handle* handles, s32 handlecount, bool waitAll, s64 timeout_nanoseconds)
|
||||
// Result WaitSynchronizationN(s32* out, HandleType* handles, s32 handlecount, bool waitAll, s64 timeout_nanoseconds)
|
||||
void Kernel::waitSynchronizationN() {
|
||||
// TODO: Are these arguments even correct?
|
||||
s32 ns1 = regs[0];
|
||||
|
@ -149,13 +149,12 @@ void Kernel::waitSynchronizationN() {
|
|||
s32 handleCount = regs[2];
|
||||
bool waitAll = regs[3] != 0;
|
||||
u32 ns2 = regs[4];
|
||||
s32 outPointer = regs[5]; // "out" pointer - shows which object got bonked if we're waiting on multiple objects
|
||||
s32 outPointer = regs[5]; // "out" pointer - shows which object got bonked if we're waiting on multiple objects
|
||||
s64 ns = s64(ns1) | (s64(ns2) << 32);
|
||||
|
||||
logSVC("WaitSynchronizationN (handle pointer: %08X, count: %d, timeout = %lld)\n", handles, handleCount, ns);
|
||||
|
||||
if (handleCount <= 0)
|
||||
Helpers::panic("WaitSyncN: Invalid handle count");
|
||||
if (handleCount <= 0) Helpers::panic("WaitSyncN: Invalid handle count");
|
||||
|
||||
// Temporary hack: Until we implement service sessions properly, don't bother sleeping when WaitSyncN targets a service handle
|
||||
// This is necessary because a lot of games use WaitSyncN with eg the CECD service
|
||||
|
@ -165,11 +164,11 @@ void Kernel::waitSynchronizationN() {
|
|||
return;
|
||||
}
|
||||
|
||||
using WaitObject = std::pair<Handle, KernelObject*>;
|
||||
using WaitObject = std::pair<HandleType, KernelObject*>;
|
||||
std::vector<WaitObject> waitObjects(handleCount);
|
||||
|
||||
// We don't actually need to wait if waitAll == true unless one of the objects is not ready
|
||||
bool allReady = true; // Default initialize to true, set to fault if one of the objects is not ready
|
||||
bool allReady = true; // Default initialize to true, set to fault if one of the objects is not ready
|
||||
|
||||
// Tracks whether at least one object is ready, + the index of the first ready object
|
||||
// This is used when waitAll == false, because if one object is already available then we can skip the sleeping
|
||||
|
@ -177,8 +176,8 @@ void Kernel::waitSynchronizationN() {
|
|||
s32 firstReadyObjectIndex = 0;
|
||||
|
||||
for (s32 i = 0; i < handleCount; i++) {
|
||||
Handle handle = mem.read32(handles);
|
||||
handles += sizeof(Handle);
|
||||
HandleType handle = mem.read32(handles);
|
||||
handles += sizeof(HandleType);
|
||||
|
||||
auto object = getObject(handle);
|
||||
// Panic if one of the objects is not even an object
|
||||
|
@ -190,13 +189,12 @@ void Kernel::waitSynchronizationN() {
|
|||
|
||||
// Panic if one of the objects is not a valid sync object
|
||||
if (!isWaitable(object)) [[unlikely]] {
|
||||
Helpers::panic("Tried to wait on a non waitable object in WaitSyncN. Type: %s, handle: %X\n",
|
||||
object->getTypeName(), handle);
|
||||
Helpers::panic("Tried to wait on a non waitable object in WaitSyncN. Type: %s, handle: %X\n", object->getTypeName(), handle);
|
||||
}
|
||||
|
||||
if (shouldWaitOnObject(object)) {
|
||||
allReady = false; // Derp, not all objects are ready :(
|
||||
} else { /// At least one object is ready to be acquired ahead of time. If it's the first one, write it down
|
||||
allReady = false; // Derp, not all objects are ready :(
|
||||
} else { /// At least one object is ready to be acquired ahead of time. If it's the first one, write it down
|
||||
if (!oneObjectReady) {
|
||||
oneObjectReady = true;
|
||||
firstReadyObjectIndex = i;
|
||||
|
@ -213,12 +211,12 @@ void Kernel::waitSynchronizationN() {
|
|||
// If there's ready objects, acquire the first one and return
|
||||
if (oneObjectReady) {
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = firstReadyObjectIndex; // Return index of the acquired object
|
||||
acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object
|
||||
regs[1] = firstReadyObjectIndex; // Return index of the acquired object
|
||||
acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object
|
||||
return;
|
||||
}
|
||||
|
||||
regs[0] = Result::OS::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);
|
||||
|
@ -227,8 +225,8 @@ void Kernel::waitSynchronizationN() {
|
|||
t.wakeupTick = getWakeupTick(ns);
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
requireReschedule();
|
||||
|
@ -243,4 +241,4 @@ void Kernel::runEventCallback(Event::CallbackType callback) {
|
|||
case Event::CallbackType::DSPSemaphore: serviceManager.getDSP().onSemaphoreEventSignal(); break;
|
||||
default: Helpers::panic("Unimplemented special callback for kernel event!"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,7 @@ namespace FileOps {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
void Kernel::handleFileOperation(u32 messagePointer, Handle file) {
|
||||
void Kernel::handleFileOperation(u32 messagePointer, HandleType file) {
|
||||
const u32 cmd = mem.read32(messagePointer);
|
||||
switch (cmd) {
|
||||
case FileOps::Close: closeFile(messagePointer, file); break;
|
||||
|
@ -30,7 +29,7 @@ void Kernel::handleFileOperation(u32 messagePointer, Handle file) {
|
|||
}
|
||||
}
|
||||
|
||||
void Kernel::closeFile(u32 messagePointer, Handle fileHandle) {
|
||||
void Kernel::closeFile(u32 messagePointer, HandleType fileHandle) {
|
||||
logFileIO("Closed file %X\n", fileHandle);
|
||||
|
||||
const auto p = getObject(fileHandle, KernelObjectType::File);
|
||||
|
@ -48,7 +47,7 @@ void Kernel::closeFile(u32 messagePointer, Handle fileHandle) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void Kernel::flushFile(u32 messagePointer, Handle fileHandle) {
|
||||
void Kernel::flushFile(u32 messagePointer, HandleType fileHandle) {
|
||||
logFileIO("Flushed file %X\n", fileHandle);
|
||||
|
||||
const auto p = getObject(fileHandle, KernelObjectType::File);
|
||||
|
@ -65,13 +64,12 @@ void Kernel::flushFile(u32 messagePointer, Handle fileHandle) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void Kernel::readFile(u32 messagePointer, Handle fileHandle) {
|
||||
void Kernel::readFile(u32 messagePointer, HandleType fileHandle) {
|
||||
u64 offset = mem.read64(messagePointer + 4);
|
||||
u32 size = mem.read32(messagePointer + 12);
|
||||
u32 dataPointer = mem.read32(messagePointer + 20);
|
||||
|
||||
logFileIO("Trying to read %X bytes from file %X, starting from offset %llX into memory address %08X\n",
|
||||
size, fileHandle, offset, dataPointer);
|
||||
logFileIO("Trying to read %X bytes from file %X, starting from offset %llX into memory address %08X\n", size, fileHandle, offset, dataPointer);
|
||||
|
||||
const auto p = getObject(fileHandle, KernelObjectType::File);
|
||||
if (p == nullptr) [[unlikely]] {
|
||||
|
@ -85,7 +83,7 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) {
|
|||
Helpers::panic("Tried to read closed file");
|
||||
}
|
||||
|
||||
// Handle files with their own file descriptors by just fread'ing the data
|
||||
// HandleType files with their own file descriptors by just fread'ing the data
|
||||
if (file->fd) {
|
||||
std::unique_ptr<u8[]> data(new u8[size]);
|
||||
IOFile f(file->fd);
|
||||
|
@ -94,8 +92,7 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) {
|
|||
|
||||
if (!success) {
|
||||
Helpers::panic("Kernel::ReadFile with file descriptor failed");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
for (size_t i = 0; i < bytesRead; i++) {
|
||||
mem.write8(u32(dataPointer + i), data[i]);
|
||||
}
|
||||
|
@ -107,7 +104,7 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Handle files without their own FD, such as SelfNCCH files
|
||||
// HandleType files without their own FD, such as SelfNCCH files
|
||||
auto archive = file->archive;
|
||||
std::optional<u32> bytesRead = archive->readFile(file, offset, size, dataPointer);
|
||||
if (!bytesRead.has_value()) {
|
||||
|
@ -118,14 +115,13 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) {
|
|||
}
|
||||
}
|
||||
|
||||
void Kernel::writeFile(u32 messagePointer, Handle fileHandle) {
|
||||
void Kernel::writeFile(u32 messagePointer, HandleType fileHandle) {
|
||||
u64 offset = mem.read64(messagePointer + 4);
|
||||
u32 size = mem.read32(messagePointer + 12);
|
||||
u32 writeOption = mem.read32(messagePointer + 16);
|
||||
u32 dataPointer = mem.read32(messagePointer + 24);
|
||||
|
||||
logFileIO("Trying to write %X bytes to file %X, starting from file offset %llX and memory address %08X\n",
|
||||
size, fileHandle, offset, dataPointer);
|
||||
logFileIO("Trying to write %X bytes to file %X, starting from file offset %llX and memory address %08X\n", size, fileHandle, offset, dataPointer);
|
||||
|
||||
const auto p = getObject(fileHandle, KernelObjectType::File);
|
||||
if (p == nullptr) [[unlikely]] {
|
||||
|
@ -137,8 +133,7 @@ void Kernel::writeFile(u32 messagePointer, Handle fileHandle) {
|
|||
Helpers::panic("Tried to write closed file");
|
||||
}
|
||||
|
||||
if (!file->fd)
|
||||
Helpers::panic("[Kernel::File::WriteFile] Tried to write to file without a valid file descriptor");
|
||||
if (!file->fd) Helpers::panic("[Kernel::File::WriteFile] Tried to write to file without a valid file descriptor");
|
||||
|
||||
std::unique_ptr<u8[]> data(new u8[size]);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
|
@ -162,7 +157,7 @@ void Kernel::writeFile(u32 messagePointer, Handle fileHandle) {
|
|||
}
|
||||
}
|
||||
|
||||
void Kernel::setFileSize(u32 messagePointer, Handle fileHandle) {
|
||||
void Kernel::setFileSize(u32 messagePointer, HandleType fileHandle) {
|
||||
logFileIO("Setting size of file %X\n", fileHandle);
|
||||
|
||||
const auto p = getObject(fileHandle, KernelObjectType::File);
|
||||
|
@ -191,7 +186,7 @@ void Kernel::setFileSize(u32 messagePointer, Handle fileHandle) {
|
|||
}
|
||||
}
|
||||
|
||||
void Kernel::getFileSize(u32 messagePointer, Handle fileHandle) {
|
||||
void Kernel::getFileSize(u32 messagePointer, HandleType fileHandle) {
|
||||
logFileIO("Getting size of file %X\n", fileHandle);
|
||||
|
||||
const auto p = getObject(fileHandle, KernelObjectType::File);
|
||||
|
@ -220,7 +215,7 @@ void Kernel::getFileSize(u32 messagePointer, Handle fileHandle) {
|
|||
}
|
||||
}
|
||||
|
||||
void Kernel::openLinkFile(u32 messagePointer, Handle fileHandle) {
|
||||
void Kernel::openLinkFile(u32 messagePointer, HandleType fileHandle) {
|
||||
logFileIO("Open link file (clone) of file %X\n", fileHandle);
|
||||
|
||||
const auto p = getObject(fileHandle, KernelObjectType::File);
|
||||
|
@ -247,7 +242,7 @@ void Kernel::openLinkFile(u32 messagePointer, Handle fileHandle) {
|
|||
mem.write32(messagePointer + 12, handle);
|
||||
}
|
||||
|
||||
void Kernel::setFilePriority(u32 messagePointer, Handle fileHandle) {
|
||||
void Kernel::setFilePriority(u32 messagePointer, HandleType fileHandle) {
|
||||
const u32 priority = mem.read32(messagePointer + 4);
|
||||
logFileIO("Setting priority of file %X to %d\n", fileHandle, priority);
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#include <cassert>
|
||||
#include "kernel.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "cpu.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
|
||||
Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config)
|
||||
: cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this, config) {
|
||||
objects.reserve(512); // Make room for a few objects to avoid further memory allocs later
|
||||
objects.reserve(512); // Make room for a few objects to avoid further memory allocs later
|
||||
mutexHandles.reserve(8);
|
||||
portHandles.reserve(32);
|
||||
threadIndices.reserve(appResourceLimits.maxThreads);
|
||||
|
@ -17,7 +19,7 @@ Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config)
|
|||
t.tlsBase = VirtualAddrs::TLSBase + i * VirtualAddrs::TLSSize;
|
||||
t.status = ThreadStatus::Dead;
|
||||
t.waitList.clear();
|
||||
t.waitList.reserve(10); // Reserve some space for the wait list to avoid further memory allocs later
|
||||
t.waitList.reserve(10); // Reserve some space for the wait list to avoid further memory allocs later
|
||||
// The state below isn't necessary to initialize but we do it anyways out of caution
|
||||
t.outPointer = 0;
|
||||
t.waitAll = false;
|
||||
|
@ -79,12 +81,12 @@ void Kernel::setVersion(u8 major, u8 minor) {
|
|||
u16 descriptor = (u16(major) << 8) | u16(minor);
|
||||
|
||||
kernelVersion = descriptor;
|
||||
mem.kernelVersion = descriptor; // The memory objects needs a copy because you can read the kernel ver from config mem
|
||||
mem.kernelVersion = descriptor; // The memory objects needs a copy because you can read the kernel ver from config mem
|
||||
}
|
||||
|
||||
Handle Kernel::makeProcess(u32 id) {
|
||||
const Handle processHandle = makeObject(KernelObjectType::Process);
|
||||
const Handle resourceLimitHandle = makeObject(KernelObjectType::ResourceLimit);
|
||||
HandleType Kernel::makeProcess(u32 id) {
|
||||
const HandleType processHandle = makeObject(KernelObjectType::Process);
|
||||
const HandleType resourceLimitHandle = makeObject(KernelObjectType::ResourceLimit);
|
||||
|
||||
// Allocate data
|
||||
objects[processHandle].data = new Process(id);
|
||||
|
@ -98,7 +100,7 @@ Handle Kernel::makeProcess(u32 id) {
|
|||
|
||||
// Get a pointer to the process indicated by handle, taking into account that 0xFFFF8001 always refers to the current process
|
||||
// Returns nullptr if the handle does not correspond to a process
|
||||
KernelObject* Kernel::getProcessFromPID(Handle handle) {
|
||||
KernelObject* Kernel::getProcessFromPID(HandleType handle) {
|
||||
if (handle == KernelHandles::CurrentProcess) [[likely]] {
|
||||
return getObject(currentProcess, KernelObjectType::Process);
|
||||
} else {
|
||||
|
@ -142,7 +144,7 @@ void Kernel::reset() {
|
|||
for (auto& t : threads) {
|
||||
t.status = ThreadStatus::Dead;
|
||||
t.waitList.clear();
|
||||
t.threadsWaitingForTermination = 0; // No threads are waiting for this thread to terminate cause it's dead
|
||||
t.threadsWaitingForTermination = 0; // No threads are waiting for this thread to terminate cause it's dead
|
||||
}
|
||||
|
||||
for (auto& object : objects) {
|
||||
|
@ -159,7 +161,7 @@ void Kernel::reset() {
|
|||
|
||||
// Allocate handle #0 to a dummy object and make a main process object
|
||||
makeObject(KernelObjectType::Dummy);
|
||||
currentProcess = makeProcess(1); // Use ID = 1 for main process
|
||||
currentProcess = makeProcess(1); // Use ID = 1 for main process
|
||||
|
||||
// Make main thread object. We do not have to set the entrypoint and SP for it as the ROM loader does.
|
||||
// Main thread seems to have a priority of 0x30. TODO: This creates a dummy context for thread 0,
|
||||
|
@ -169,19 +171,17 @@ void Kernel::reset() {
|
|||
setupIdleThread();
|
||||
|
||||
// Create some of the OS ports
|
||||
srvHandle = makePort("srv:"); // Service manager port
|
||||
errorPortHandle = makePort("err:f"); // Error display port
|
||||
srvHandle = makePort("srv:"); // Service manager port
|
||||
errorPortHandle = makePort("err:f"); // Error display port
|
||||
}
|
||||
|
||||
// Get pointer to thread-local storage
|
||||
u32 Kernel::getTLSPointer() {
|
||||
return VirtualAddrs::TLSBase + currentThreadIndex * VirtualAddrs::TLSSize;
|
||||
}
|
||||
u32 Kernel::getTLSPointer() { return VirtualAddrs::TLSBase + currentThreadIndex * VirtualAddrs::TLSSize; }
|
||||
|
||||
// Result CloseHandle(Handle handle)
|
||||
// Result CloseHandle(HandleType handle)
|
||||
void Kernel::svcCloseHandle() {
|
||||
logSVC("CloseHandle(handle = %d) (Unimplemented)\n", regs[0]);
|
||||
const Handle handle = regs[0];
|
||||
const HandleType handle = regs[0];
|
||||
|
||||
KernelObject* object = getObject(handle);
|
||||
if (object != nullptr) {
|
||||
|
@ -242,7 +242,7 @@ void Kernel::getProcessID() {
|
|||
regs[1] = process->getData<Process>()->id;
|
||||
}
|
||||
|
||||
// Result GetProcessInfo(s64* out, Handle process, ProcessInfoType type)
|
||||
// Result GetProcessInfo(s64* out, HandleType process, ProcessInfoType type)
|
||||
void Kernel::getProcessInfo() {
|
||||
const auto pid = regs[1];
|
||||
const auto type = regs[2];
|
||||
|
@ -269,26 +269,25 @@ void Kernel::getProcessInfo() {
|
|||
regs[2] = 0;
|
||||
break;
|
||||
|
||||
case 20: // Returns 0x20000000 - <linear memory base vaddr for process>
|
||||
case 20: // Returns 0x20000000 - <linear memory base vaddr for process>
|
||||
regs[1] = PhysicalAddrs::FCRAM - mem.getLinearHeapVaddr();
|
||||
regs[2] = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
Helpers::panic("GetProcessInfo: unimplemented type %d", type);
|
||||
default: Helpers::panic("GetProcessInfo: unimplemented type %d", type);
|
||||
}
|
||||
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
// Result DuplicateHandle(Handle* out, Handle original)
|
||||
// Result DuplicateHandle(HandleType* out, HandleType original)
|
||||
void Kernel::duplicateHandle() {
|
||||
Handle original = regs[1];
|
||||
HandleType original = regs[1];
|
||||
logSVC("DuplicateHandle(handle = %X)\n", original);
|
||||
|
||||
if (original == KernelHandles::CurrentThread) {
|
||||
regs[0] = Result::Success;
|
||||
Handle ret = makeObject(KernelObjectType::Thread);
|
||||
HandleType ret = makeObject(KernelObjectType::Thread);
|
||||
objects[ret].data = &threads[currentThreadIndex];
|
||||
|
||||
regs[1] = ret;
|
||||
|
@ -379,7 +378,7 @@ void Kernel::getSystemInfo() {
|
|||
regs[2] = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
default:
|
||||
Helpers::warn("GetSystemInfo: Unknown PandaInformation subtype %x\n", subtype);
|
||||
regs[0] = Result::FailurePlaceholder;
|
||||
break;
|
||||
|
|
|
@ -17,37 +17,35 @@ namespace Operation {
|
|||
|
||||
namespace MemoryPermissions {
|
||||
enum : u32 {
|
||||
None = 0, // ---
|
||||
Read = 1, // R--
|
||||
Write = 2, // -W-
|
||||
ReadWrite = 3, // RW-
|
||||
Execute = 4, // --X
|
||||
ReadExecute = 5, // R-X
|
||||
WriteExecute = 6, // -WX
|
||||
ReadWriteExecute = 7, // RWX
|
||||
None = 0, // ---
|
||||
Read = 1, // R--
|
||||
Write = 2, // -W-
|
||||
ReadWrite = 3, // RW-
|
||||
Execute = 4, // --X
|
||||
ReadExecute = 5, // R-X
|
||||
WriteExecute = 6, // -WX
|
||||
ReadWriteExecute = 7, // RWX
|
||||
|
||||
DontCare = 0x10000000
|
||||
};
|
||||
}
|
||||
|
||||
// Returns whether "value" is aligned to a page boundary (Ie a boundary of 4096 bytes)
|
||||
static constexpr bool isAligned(u32 value) {
|
||||
return (value & 0xFFF) == 0;
|
||||
}
|
||||
static constexpr bool isAligned(u32 value) { return (value & 0xFFF) == 0; }
|
||||
|
||||
// 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?
|
||||
void Kernel::controlMemory() {
|
||||
u32 operation = regs[0]; // The base address is written here
|
||||
u32 operation = regs[0]; // The base address is written here
|
||||
u32 addr0 = regs[1];
|
||||
u32 addr1 = regs[2];
|
||||
u32 size = regs[3];
|
||||
u32 perms = regs[4];
|
||||
|
||||
if (perms == MemoryPermissions::DontCare) {
|
||||
perms = MemoryPermissions::ReadWrite; // We make "don't care" equivalent to read-write
|
||||
perms = MemoryPermissions::ReadWrite; // We make "don't care" equivalent to read-write
|
||||
Helpers::panic("Unimplemented allocation permission: DONTCARE");
|
||||
}
|
||||
|
||||
|
@ -57,33 +55,33 @@ void Kernel::controlMemory() {
|
|||
bool x = perms & 0b100;
|
||||
bool linear = operation & Operation::Linear;
|
||||
|
||||
if (x)
|
||||
Helpers::panic("ControlMemory: attempted to allocate executable memory");
|
||||
if (x) Helpers::panic("ControlMemory: attempted to allocate executable memory");
|
||||
|
||||
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" : ""
|
||||
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" : ""
|
||||
);
|
||||
|
||||
switch (operation & 0xFF) {
|
||||
case Operation::Commit: {
|
||||
std::optional<u32> address = mem.allocateMemory(addr0, 0, size, linear, r, w, x, true);
|
||||
if (!address.has_value())
|
||||
Helpers::panic("ControlMemory: Failed to allocate memory");
|
||||
if (!address.has_value()) Helpers::panic("ControlMemory: Failed to allocate memory");
|
||||
|
||||
regs[1] = address.value();
|
||||
break;
|
||||
}
|
||||
|
||||
case Operation::Map:
|
||||
mem.mirrorMapping(addr0, addr1, size);
|
||||
break;
|
||||
case Operation::Map: mem.mirrorMapping(addr0, addr1, size); break;
|
||||
|
||||
case Operation::Protect:
|
||||
Helpers::warn("Ignoring mprotect! Hope nothing goes wrong but if the game accesses invalid memory or crashes then we prolly need to implement this\n");
|
||||
Helpers::warn(
|
||||
"Ignoring mprotect! Hope nothing goes wrong but if the game accesses invalid memory or crashes then we prolly need to implement "
|
||||
"this\n"
|
||||
);
|
||||
break;
|
||||
|
||||
default: Helpers::warn("ControlMemory: unknown operation %X\n", operation); break;
|
||||
|
@ -106,12 +104,12 @@ void Kernel::queryMemory() {
|
|||
regs[2] = info.size;
|
||||
regs[3] = info.perms;
|
||||
regs[4] = info.state;
|
||||
regs[5] = 0; // page flags
|
||||
regs[5] = 0; // page flags
|
||||
}
|
||||
|
||||
// Result MapMemoryBlock(Handle memblock, u32 addr, MemoryPermission myPermissions, MemoryPermission otherPermission)
|
||||
// Result MapMemoryBlock(HandleType memblock, u32 addr, MemoryPermission myPermissions, MemoryPermission otherPermission)
|
||||
void Kernel::mapMemoryBlock() {
|
||||
const Handle block = regs[0];
|
||||
const HandleType block = regs[0];
|
||||
u32 addr = regs[1];
|
||||
const u32 myPerms = regs[2];
|
||||
const u32 otherPerms = regs[3];
|
||||
|
@ -123,21 +121,15 @@ void Kernel::mapMemoryBlock() {
|
|||
|
||||
if (KernelHandles::isSharedMemHandle(block)) {
|
||||
if (block == KernelHandles::FontSharedMemHandle && addr == 0) addr = 0x18000000;
|
||||
u8* ptr = mem.mapSharedMemory(block, addr, myPerms, otherPerms); // Map shared memory block
|
||||
u8* ptr = mem.mapSharedMemory(block, addr, myPerms, otherPerms); // Map shared memory block
|
||||
|
||||
// Pass pointer to shared memory to the appropriate service
|
||||
switch (block) {
|
||||
case KernelHandles::HIDSharedMemHandle:
|
||||
serviceManager.setHIDSharedMem(ptr);
|
||||
break;
|
||||
case KernelHandles::HIDSharedMemHandle: serviceManager.setHIDSharedMem(ptr); break;
|
||||
|
||||
case KernelHandles::GSPSharedMemHandle:
|
||||
serviceManager.setGSPSharedMem(ptr);
|
||||
break;
|
||||
case KernelHandles::GSPSharedMemHandle: serviceManager.setGSPSharedMem(ptr); break;
|
||||
|
||||
case KernelHandles::FontSharedMemHandle:
|
||||
mem.copySharedFont(ptr);
|
||||
break;
|
||||
case KernelHandles::FontSharedMemHandle: mem.copySharedFont(ptr); break;
|
||||
|
||||
case KernelHandles::CSNDSharedMemHandle:
|
||||
serviceManager.setCSNDSharedMem(ptr);
|
||||
|
@ -154,8 +146,8 @@ void Kernel::mapMemoryBlock() {
|
|||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
Handle Kernel::makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission) {
|
||||
Handle ret = makeObject(KernelObjectType::MemoryBlock);
|
||||
HandleType Kernel::makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission) {
|
||||
HandleType ret = makeObject(KernelObjectType::MemoryBlock);
|
||||
objects[ret].data = new MemoryBlock(addr, size, myPermission, otherPermission);
|
||||
|
||||
return ret;
|
||||
|
@ -165,7 +157,7 @@ void Kernel::createMemoryBlock() {
|
|||
const u32 addr = regs[1];
|
||||
const u32 size = regs[2];
|
||||
u32 myPermission = regs[3];
|
||||
u32 otherPermission = mem.read32(regs[13] + 4); // This is placed on the stack rather than r4
|
||||
u32 otherPermission = mem.read32(regs[13] + 4); // This is placed on the stack rather than r4
|
||||
logSVC("CreateMemoryBlock (addr = %08X, size = %08X, myPermission = %d, otherPermission = %d)\n", addr, size, myPermission, otherPermission);
|
||||
|
||||
// Returns whether a permission is valid
|
||||
|
@ -175,10 +167,9 @@ void Kernel::createMemoryBlock() {
|
|||
case MemoryPermissions::Read:
|
||||
case MemoryPermissions::Write:
|
||||
case MemoryPermissions::ReadWrite:
|
||||
case MemoryPermissions::DontCare:
|
||||
return true;
|
||||
case MemoryPermissions::DontCare: return true;
|
||||
|
||||
default: // Permissions with the executable flag enabled or invalid permissions are not allowed
|
||||
default: // Permissions with the executable flag enabled or invalid permissions are not allowed
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@ -197,8 +188,7 @@ void Kernel::createMemoryBlock() {
|
|||
|
||||
// TODO: The address needs to be in a specific range otherwise it throws an invalid address error
|
||||
|
||||
if (addr == 0)
|
||||
Helpers::panic("CreateMemoryBlock: Tried to use addr = 0");
|
||||
if (addr == 0) Helpers::panic("CreateMemoryBlock: Tried to use addr = 0");
|
||||
|
||||
// Implement "Don't care" permission as RW
|
||||
if (myPermission == MemoryPermissions::DontCare) myPermission = MemoryPermissions::ReadWrite;
|
||||
|
@ -209,7 +199,7 @@ void Kernel::createMemoryBlock() {
|
|||
}
|
||||
|
||||
void Kernel::unmapMemoryBlock() {
|
||||
Handle block = regs[0];
|
||||
HandleType block = regs[0];
|
||||
u32 addr = regs[1];
|
||||
logSVC("Unmap memory block (block handle = %X, addr = %08X)\n", block, addr);
|
||||
|
||||
|
|
|
@ -1,29 +1,30 @@
|
|||
#include "kernel.hpp"
|
||||
#include <cstring>
|
||||
|
||||
Handle Kernel::makePort(const char* name) {
|
||||
Handle ret = makeObject(KernelObjectType::Port);
|
||||
portHandles.push_back(ret); // Push the port handle to our cache of port handles
|
||||
#include "kernel.hpp"
|
||||
|
||||
HandleType Kernel::makePort(const char* name) {
|
||||
HandleType ret = makeObject(KernelObjectType::Port);
|
||||
portHandles.push_back(ret); // Push the port handle to our cache of port handles
|
||||
objects[ret].data = new Port(name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Handle Kernel::makeSession(Handle portHandle) {
|
||||
HandleType Kernel::makeSession(HandleType portHandle) {
|
||||
const auto port = getObject(portHandle, KernelObjectType::Port);
|
||||
if (port == nullptr) [[unlikely]] {
|
||||
Helpers::panic("Trying to make session for non-existent port");
|
||||
}
|
||||
|
||||
// Allocate data for session
|
||||
const Handle ret = makeObject(KernelObjectType::Session);
|
||||
const HandleType ret = makeObject(KernelObjectType::Session);
|
||||
objects[ret].data = new Session(portHandle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Get the handle of a port based on its name
|
||||
// If there's no such port, return nullopt
|
||||
std::optional<Handle> Kernel::getPortHandle(const char* name) {
|
||||
std::optional<HandleType> Kernel::getPortHandle(const char* name) {
|
||||
for (auto handle : portHandles) {
|
||||
const auto data = objects[handle].getData<Port>();
|
||||
if (std::strncmp(name, data->name, Port::maxNameLen) == 0) {
|
||||
|
@ -34,7 +35,7 @@ std::optional<Handle> Kernel::getPortHandle(const char* name) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Result ConnectToPort(Handle* out, const char* portName)
|
||||
// Result ConnectToPort(HandleType* out, const char* portName)
|
||||
void Kernel::connectToPort() {
|
||||
const u32 handlePointer = regs[0];
|
||||
// Read up to max + 1 characters to see if the name is too long
|
||||
|
@ -48,14 +49,14 @@ void Kernel::connectToPort() {
|
|||
}
|
||||
|
||||
// Try getting a handle to the port
|
||||
std::optional<Handle> optionalHandle = getPortHandle(port.c_str());
|
||||
std::optional<HandleType> optionalHandle = getPortHandle(port.c_str());
|
||||
if (!optionalHandle.has_value()) [[unlikely]] {
|
||||
Helpers::panic("ConnectToPort: Port doesn't exist\n");
|
||||
regs[0] = Result::Kernel::NotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
Handle portHandle = optionalHandle.value();
|
||||
HandleType portHandle = optionalHandle.value();
|
||||
|
||||
const auto portData = objects[portHandle].getData<Port>();
|
||||
if (!portData->isPublic) {
|
||||
|
@ -63,17 +64,17 @@ void Kernel::connectToPort() {
|
|||
}
|
||||
|
||||
// TODO: Actually create session
|
||||
Handle sessionHandle = makeSession(portHandle);
|
||||
HandleType sessionHandle = makeSession(portHandle);
|
||||
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = sessionHandle;
|
||||
}
|
||||
|
||||
// Result SendSyncRequest(Handle session)
|
||||
// Result SendSyncRequest(HandleType session)
|
||||
// Send an IPC message to a port (typically "srv:") or a service
|
||||
void Kernel::sendSyncRequest() {
|
||||
const auto handle = regs[0];
|
||||
u32 messagePointer = getTLSPointer() + 0x80; // The message is stored starting at TLS+0x80
|
||||
u32 messagePointer = getTLSPointer() + 0x80; // The message is stored starting at TLS+0x80
|
||||
logSVC("SendSyncRequest(session handle = %X)\n", handle);
|
||||
|
||||
// Service calls via SendSyncRequest and file access needs to put the caller to sleep for a given amount of time
|
||||
|
@ -93,7 +94,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] = Result::Success; // r0 goes first here too
|
||||
regs[0] = Result::Success; // r0 goes first here too
|
||||
handleFileOperation(messagePointer, handle);
|
||||
return;
|
||||
}
|
||||
|
@ -101,7 +102,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] = Result::Success; // r0 goes first here too
|
||||
regs[0] = Result::Success; // r0 goes first here too
|
||||
handleDirectoryOperation(messagePointer, handle);
|
||||
return;
|
||||
}
|
||||
|
@ -115,12 +116,12 @@ void Kernel::sendSyncRequest() {
|
|||
}
|
||||
|
||||
const auto sessionData = static_cast<Session*>(session->data);
|
||||
const Handle portHandle = sessionData->portHandle;
|
||||
const HandleType portHandle = sessionData->portHandle;
|
||||
|
||||
if (portHandle == srvHandle) { // Special-case SendSyncRequest targetting the "srv: port"
|
||||
if (portHandle == srvHandle) { // Special-case SendSyncRequest targetting the "srv: port"
|
||||
regs[0] = Result::Success;
|
||||
serviceManager.handleSyncRequest(messagePointer);
|
||||
} else if (portHandle == errorPortHandle) { // Special-case "err:f" for juicy logs too
|
||||
} else if (portHandle == errorPortHandle) { // Special-case "err:f" for juicy logs too
|
||||
regs[0] = Result::Success;
|
||||
handleErrorSyncRequest(messagePointer);
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include "resource_limits.hpp"
|
||||
|
||||
#include "kernel.hpp"
|
||||
|
||||
// Result GetResourceLimit(Handle* resourceLimit, Handle process)
|
||||
// Result GetResourceLimit(HandleType* resourceLimit, HandleType process)
|
||||
// out: r0 -> result, r1 -> handle
|
||||
void Kernel::getResourceLimit() {
|
||||
const auto handlePointer = regs[0];
|
||||
|
@ -20,10 +21,10 @@ void Kernel::getResourceLimit() {
|
|||
regs[1] = processData->limits.handle;
|
||||
}
|
||||
|
||||
// Result GetResourceLimitLimitValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount)
|
||||
// Result GetResourceLimitLimitValues(s64* values, HandleType resourceLimit, LimitableResource* names, s32 nameCount)
|
||||
void Kernel::getResourceLimitLimitValues() {
|
||||
u32 values = regs[0]; // Pointer to values (The resource limits get output here)
|
||||
const Handle resourceLimit = regs[1];
|
||||
u32 values = regs[0]; // Pointer to values (The resource limits get output here)
|
||||
const HandleType resourceLimit = regs[1];
|
||||
u32 names = regs[2]; // Pointer to resources that we should return
|
||||
u32 count = regs[3]; // Number of resources
|
||||
|
||||
|
@ -49,10 +50,10 @@ void Kernel::getResourceLimitLimitValues() {
|
|||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
// Result GetResourceLimitCurrentValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount)
|
||||
// Result GetResourceLimitCurrentValues(s64* values, HandleType resourceLimit, LimitableResource* names, s32 nameCount)
|
||||
void Kernel::getResourceLimitCurrentValues() {
|
||||
u32 values = regs[0]; // Pointer to values (The resource limits get output here)
|
||||
const Handle resourceLimit = regs[1];
|
||||
u32 values = regs[0]; // Pointer to values (The resource limits get output here)
|
||||
const HandleType resourceLimit = regs[1];
|
||||
u32 names = regs[2]; // Pointer to resources that we should return
|
||||
u32 count = regs[3]; // Number of resources
|
||||
logSVC("GetResourceLimitCurrentValues(values = %08X, handle = %X, names = %08X, count = %d)\n", values, resourceLimit, names, count);
|
||||
|
|
|
@ -33,7 +33,7 @@ void Kernel::switchThread(int newThreadIndex) {
|
|||
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
|
||||
cpu.setTLSBase(newThread.tlsBase); // Load CP15 thread-local-storage pointer register
|
||||
|
||||
currentThreadIndex = newThreadIndex;
|
||||
}
|
||||
|
@ -42,21 +42,19 @@ void Kernel::switchThread(int newThreadIndex) {
|
|||
// The threads with higher priority (aka the ones with a lower priority value) should come first in the vector
|
||||
void Kernel::sortThreads() {
|
||||
std::vector<int>& v = threadIndices;
|
||||
std::sort(v.begin(), v.end(), [&](int a, int b) {
|
||||
return threads[a].priority < threads[b].priority;
|
||||
});
|
||||
std::sort(v.begin(), v.end(), [&](int a, int b) { return threads[a].priority < threads[b].priority; });
|
||||
}
|
||||
|
||||
bool Kernel::canThreadRun(const Thread& t) {
|
||||
if (t.status == ThreadStatus::Ready) {
|
||||
return true;
|
||||
} else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1
|
||||
|| t.status == ThreadStatus::WaitSyncAny || t.status == ThreadStatus::WaitSyncAll) {
|
||||
} else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 || t.status == ThreadStatus::WaitSyncAny ||
|
||||
t.status == ThreadStatus::WaitSyncAll) {
|
||||
// TODO: Set r0 to the correct error code on timeout for WaitSync{1/Any/All}
|
||||
return cpu.getTicks() >= t.wakeupTick;
|
||||
}
|
||||
|
||||
// Handle timeouts and stuff here
|
||||
// HandleType timeouts and stuff here
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -100,8 +98,8 @@ void Kernel::rescheduleThreads() {
|
|||
// Case 1: A thread can run
|
||||
if (newThreadIndex.has_value()) {
|
||||
switchThread(newThreadIndex.value());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Case 2: No other thread can run, straight to the idle thread
|
||||
else {
|
||||
switchThread(idleThreadIndex);
|
||||
|
@ -109,30 +107,30 @@ void Kernel::rescheduleThreads() {
|
|||
}
|
||||
|
||||
// Internal OS function to spawn a thread
|
||||
Handle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, ProcessorID id, u32 arg, ThreadStatus status) {
|
||||
int index; // Index of the created thread in the threads array
|
||||
HandleType Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, ProcessorID id, u32 arg, ThreadStatus status) {
|
||||
int index; // Index of the created thread in the threads array
|
||||
|
||||
if (threadCount < appResourceLimits.maxThreads) [[likely]] { // If we have not yet created over too many threads
|
||||
if (threadCount < appResourceLimits.maxThreads) [[likely]] { // If we have not yet created over too many threads
|
||||
index = threadCount++;
|
||||
} else if (aliveThreadCount < appResourceLimits.maxThreads) { // If we have created many threads but at least one is dead & reusable
|
||||
} else if (aliveThreadCount < appResourceLimits.maxThreads) { // If we have created many threads but at least one is dead & reusable
|
||||
for (int i = 0; i < threads.size(); i++) {
|
||||
if (threads[i].status == ThreadStatus::Dead) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else { // There is no thread we can use, we're screwed
|
||||
} else { // There is no thread we can use, we're screwed
|
||||
Helpers::panic("Overflowed thread count!!");
|
||||
}
|
||||
|
||||
aliveThreadCount++;
|
||||
|
||||
threadIndices.push_back(index);
|
||||
Thread& t = threads[index]; // Reference to thread data
|
||||
Handle ret = makeObject(KernelObjectType::Thread);
|
||||
Thread& t = threads[index]; // Reference to thread data
|
||||
HandleType ret = makeObject(KernelObjectType::Thread);
|
||||
objects[ret].data = &t;
|
||||
|
||||
const bool isThumb = (entrypoint & 1) != 0; // Whether the thread starts in thumb mode or not
|
||||
const bool isThumb = (entrypoint & 1) != 0; // Whether the thread starts in thumb mode or not
|
||||
|
||||
// Set up initial thread context
|
||||
t.gprs.fill(0);
|
||||
|
@ -150,7 +148,7 @@ Handle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, Processor
|
|||
t.status = status;
|
||||
t.handle = ret;
|
||||
t.waitingAddress = 0;
|
||||
t.threadsWaitingForTermination = 0; // Thread just spawned, no other threads waiting for it to terminate
|
||||
t.threadsWaitingForTermination = 0; // Thread just spawned, no other threads waiting for it to terminate
|
||||
|
||||
t.cpsr = CPSR::UserMode | (isThumb ? CPSR::Thumb : 0);
|
||||
t.fpscr = FPSCR::ThreadDefault;
|
||||
|
@ -161,8 +159,8 @@ Handle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, Processor
|
|||
return ret;
|
||||
}
|
||||
|
||||
Handle Kernel::makeMutex(bool locked) {
|
||||
Handle ret = makeObject(KernelObjectType::Mutex);
|
||||
HandleType Kernel::makeMutex(bool locked) {
|
||||
HandleType ret = makeObject(KernelObjectType::Mutex);
|
||||
objects[ret].data = new Mutex(locked, ret);
|
||||
|
||||
// If the mutex is initially locked, store the index of the thread that owns it and set lock count to 1
|
||||
|
@ -181,15 +179,15 @@ Handle Kernel::makeMutex(bool locked) {
|
|||
|
||||
void Kernel::releaseMutex(Mutex* moo) {
|
||||
// TODO: Assert lockCount > 0 before release, maybe. The SVC should be safe at least.
|
||||
moo->lockCount--; // Decrement lock count
|
||||
moo->lockCount--; // Decrement lock count
|
||||
|
||||
// If the lock count reached 0 then the thread no longer owns the mootex and it can be given to a new one
|
||||
if (moo->lockCount == 0) {
|
||||
moo->locked = false;
|
||||
|
||||
if (moo->waitlist != 0) {
|
||||
int index = wakeupOneThread(moo->waitlist, moo->handle); // Wake up one thread and get its index
|
||||
moo->waitlist ^= (1ull << index); // Remove thread from waitlist
|
||||
int index = wakeupOneThread(moo->waitlist, moo->handle); // Wake up one thread and get its index
|
||||
moo->waitlist ^= (1ull << index); // Remove thread from waitlist
|
||||
|
||||
// Have new thread acquire mutex
|
||||
moo->locked = true;
|
||||
|
@ -201,8 +199,8 @@ void Kernel::releaseMutex(Mutex* moo) {
|
|||
}
|
||||
}
|
||||
|
||||
Handle Kernel::makeSemaphore(u32 initialCount, u32 maximumCount) {
|
||||
Handle ret = makeObject(KernelObjectType::Semaphore);
|
||||
HandleType Kernel::makeSemaphore(u32 initialCount, u32 maximumCount) {
|
||||
HandleType ret = makeObject(KernelObjectType::Semaphore);
|
||||
objects[ret].data = new Semaphore(initialCount, maximumCount);
|
||||
|
||||
return ret;
|
||||
|
@ -221,7 +219,7 @@ void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) {
|
|||
switch (object->type) {
|
||||
case KernelObjectType::Event: {
|
||||
Event* e = object->getData<Event>();
|
||||
if (e->resetType == ResetType::OneShot) { // One-shot events automatically get cleared after waking up a thread
|
||||
if (e->resetType == ResetType::OneShot) { // One-shot events automatically get cleared after waking up a thread
|
||||
e->fired = false;
|
||||
}
|
||||
break;
|
||||
|
@ -245,15 +243,14 @@ void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) {
|
|||
|
||||
case KernelObjectType::Semaphore: {
|
||||
Semaphore* s = object->getData<Semaphore>();
|
||||
if (s->availableCount <= 0) [[unlikely]] // This should be unreachable but let's check anyways
|
||||
if (s->availableCount <= 0) [[unlikely]] // This should be unreachable but let's check anyways
|
||||
Helpers::panic("Tried to acquire unacquirable semaphore");
|
||||
|
||||
s->availableCount -= 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case KernelObjectType::Thread:
|
||||
break;
|
||||
case KernelObjectType::Thread: break;
|
||||
|
||||
case KernelObjectType::Timer: {
|
||||
Timer* timer = object->getData<Timer>();
|
||||
|
@ -269,36 +266,36 @@ void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) {
|
|||
|
||||
// Wake up one of the threads in the waitlist (the one with highest prio) and return its index
|
||||
// Must not be called with an empty waitlist
|
||||
int Kernel::wakeupOneThread(u64 waitlist, Handle handle) {
|
||||
int Kernel::wakeupOneThread(u64 waitlist, HandleType handle) {
|
||||
if (waitlist == 0) [[unlikely]]
|
||||
Helpers::panic("[Internal error] It shouldn't be possible to call wakeupOneThread when there's 0 threads waiting!");
|
||||
|
||||
// Find the waiting thread with the highest priority.
|
||||
// We do this by first picking the first thread in the waitlist, then checking each other thread and comparing priority
|
||||
int threadIndex = std::countr_zero(waitlist); // Index of first thread
|
||||
int maxPriority = threads[threadIndex].priority; // Set initial max prio to the prio of the first thread
|
||||
waitlist ^= (1ull << threadIndex); // Remove thread from the waitlist
|
||||
int threadIndex = std::countr_zero(waitlist); // Index of first thread
|
||||
int maxPriority = threads[threadIndex].priority; // Set initial max prio to the prio of the first thread
|
||||
waitlist ^= (1ull << threadIndex); // Remove thread from the waitlist
|
||||
|
||||
while (waitlist != 0) {
|
||||
int newThread = std::countr_zero(waitlist); // Get new thread and evaluate whether it has a higher priority
|
||||
if (threads[newThread].priority < maxPriority) { // Low priority value means high priority
|
||||
int newThread = std::countr_zero(waitlist); // Get new thread and evaluate whether it has a higher priority
|
||||
if (threads[newThread].priority < maxPriority) { // Low priority value means high priority
|
||||
threadIndex = newThread;
|
||||
maxPriority = threads[newThread].priority;
|
||||
}
|
||||
|
||||
waitlist ^= (1ull << threadIndex); // Remove thread from waitlist
|
||||
waitlist ^= (1ull << threadIndex); // Remove thread from waitlist
|
||||
}
|
||||
|
||||
Thread& t = threads[threadIndex];
|
||||
switch (t.status) {
|
||||
case ThreadStatus::WaitSync1:
|
||||
t.status = ThreadStatus::Ready;
|
||||
t.gprs[0] = Result::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] = Result::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++) {
|
||||
|
@ -309,44 +306,40 @@ int Kernel::wakeupOneThread(u64 waitlist, Handle handle) {
|
|||
}
|
||||
break;
|
||||
|
||||
case ThreadStatus::WaitSyncAll:
|
||||
Helpers::panic("WakeupOneThread: Thread on WaitSyncAll");
|
||||
break;
|
||||
case ThreadStatus::WaitSyncAll: Helpers::panic("WakeupOneThread: Thread on WaitSyncAll"); break;
|
||||
}
|
||||
|
||||
return threadIndex;
|
||||
}
|
||||
|
||||
// Wake up every single thread in the waitlist using a bit scanning algorithm
|
||||
void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) {
|
||||
void Kernel::wakeupAllThreads(u64 waitlist, HandleType handle) {
|
||||
while (waitlist != 0) {
|
||||
const uint index = std::countr_zero(waitlist); // Get one of the set bits to see which thread is waiting
|
||||
waitlist ^= (1ull << index); // Remove thread from waitlist by toggling its bit
|
||||
const uint index = std::countr_zero(waitlist); // Get one of the set bits to see which thread is waiting
|
||||
waitlist ^= (1ull << index); // Remove thread from waitlist by toggling its bit
|
||||
|
||||
// Get the thread we'll be signalling
|
||||
Thread& t = threads[index];
|
||||
switch (t.status) {
|
||||
case ThreadStatus::WaitSync1:
|
||||
t.status = ThreadStatus::Ready;
|
||||
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
|
||||
break;
|
||||
case ThreadStatus::WaitSync1:
|
||||
t.status = ThreadStatus::Ready;
|
||||
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] = Result::Success; // The thread did not timeout, so write success to r0
|
||||
case ThreadStatus::WaitSyncAny:
|
||||
t.status = ThreadStatus::Ready;
|
||||
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++) {
|
||||
if (t.waitList[i] == handle) {
|
||||
t.gprs[1] = u32(i);
|
||||
break;
|
||||
// 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++) {
|
||||
if (t.waitList[i] == handle) {
|
||||
t.gprs[1] = u32(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case ThreadStatus::WaitSyncAll:
|
||||
Helpers::panic("WakeupAllThreads: Thread on WaitSyncAll");
|
||||
break;
|
||||
case ThreadStatus::WaitSyncAll: Helpers::panic("WakeupAllThreads: Thread on WaitSyncAll"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -404,12 +397,11 @@ void Kernel::sleepThread(s64 ns) {
|
|||
void Kernel::createThread() {
|
||||
u32 priority = regs[0];
|
||||
u32 entrypoint = regs[1];
|
||||
u32 arg = regs[2]; // An argument value stored in r0 of the new thread
|
||||
u32 initialSP = regs[3] & ~7; // SP is force-aligned to 8 bytes
|
||||
u32 arg = regs[2]; // An argument value stored in r0 of the new thread
|
||||
u32 initialSP = regs[3] & ~7; // SP is force-aligned to 8 bytes
|
||||
s32 id = static_cast<s32>(regs[4]);
|
||||
|
||||
logSVC("CreateThread(entry = %08X, stacktop = %08X, arg = %X, priority = %X, processor ID = %d)\n", entrypoint,
|
||||
initialSP, arg, priority, id);
|
||||
logSVC("CreateThread(entry = %08X, stacktop = %08X, arg = %X, priority = %X, processor ID = %d)\n", entrypoint, initialSP, arg, priority, id);
|
||||
|
||||
if (priority > 0x3F) [[unlikely]] {
|
||||
Helpers::panic("Created thread with bad priority value %X", priority);
|
||||
|
@ -429,14 +421,14 @@ void Kernel::createThread() {
|
|||
// void SleepThread(s64 nanoseconds)
|
||||
void Kernel::svcSleepThread() {
|
||||
const s64 ns = s64(u64(regs[0]) | (u64(regs[1]) << 32));
|
||||
//logSVC("SleepThread(ns = %lld)\n", ns);
|
||||
// logSVC("SleepThread(ns = %lld)\n", ns);
|
||||
|
||||
regs[0] = Result::Success;
|
||||
sleepThread(ns);
|
||||
}
|
||||
|
||||
void Kernel::getThreadID() {
|
||||
Handle handle = regs[1];
|
||||
HandleType handle = regs[1];
|
||||
logSVC("GetThreadID(handle = %X)\n", handle);
|
||||
|
||||
if (handle == KernelHandles::CurrentThread) {
|
||||
|
@ -456,7 +448,7 @@ void Kernel::getThreadID() {
|
|||
}
|
||||
|
||||
void Kernel::getThreadPriority() {
|
||||
const Handle handle = regs[1];
|
||||
const HandleType handle = regs[1];
|
||||
logSVC("GetThreadPriority (handle = %X)\n", handle);
|
||||
|
||||
if (handle == KernelHandles::CurrentThread) {
|
||||
|
@ -474,7 +466,7 @@ void Kernel::getThreadPriority() {
|
|||
}
|
||||
|
||||
void Kernel::getThreadIdealProcessor() {
|
||||
const Handle handle = regs[1]; // Thread handle
|
||||
const HandleType handle = regs[1]; // Thread handle
|
||||
logSVC("GetThreadIdealProcessor (handle = %X)\n", handle);
|
||||
|
||||
// TODO: Not documented what this is or what it does. Citra doesn't implement it at all. Return AppCore as the ideal processor for now
|
||||
|
@ -490,7 +482,7 @@ void Kernel::getThreadContext() {
|
|||
}
|
||||
|
||||
void Kernel::setThreadPriority() {
|
||||
const Handle handle = regs[0];
|
||||
const HandleType handle = regs[0];
|
||||
const u32 priority = regs[1];
|
||||
logSVC("SetThreadPriority (handle = %X, priority = %X)\n", handle, priority);
|
||||
|
||||
|
@ -524,9 +516,7 @@ void Kernel::getCurrentProcessorNumber() {
|
|||
// Until we properly implement per-core schedulers, return whatever processor ID passed to svcCreateThread
|
||||
switch (id) {
|
||||
// TODO: This is picked from exheader
|
||||
case ProcessorID::Default:
|
||||
ret = static_cast<s32>(ProcessorID::AppCore);
|
||||
break;
|
||||
case ProcessorID::Default: ret = static_cast<s32>(ProcessorID::AppCore); break;
|
||||
|
||||
case ProcessorID::AllCPUs:
|
||||
ret = static_cast<s32>(ProcessorID::AppCore);
|
||||
|
@ -565,8 +555,7 @@ void Kernel::exitThread() {
|
|||
|
||||
// Remove the index of this thread from the thread indices vector
|
||||
for (int i = 0; i < threadIndices.size(); i++) {
|
||||
if (threadIndices[i] == currentThreadIndex)
|
||||
threadIndices.erase(threadIndices.begin() + i);
|
||||
if (threadIndices[i] == currentThreadIndex) threadIndices.erase(threadIndices.begin() + i);
|
||||
}
|
||||
|
||||
Thread& t = threads[currentThreadIndex];
|
||||
|
@ -576,9 +565,9 @@ void Kernel::exitThread() {
|
|||
// Check if any threads are sleeping, waiting for this thread to terminate, and wake them up
|
||||
// This is how thread joining is implemented in the kernel - you wait on a thread, like any other wait object.
|
||||
if (t.threadsWaitingForTermination != 0) {
|
||||
// TODO: Handle cloned handles? Not sure how those interact with wait object signalling
|
||||
// TODO: HandleType cloned handles? Not sure how those interact with wait object signalling
|
||||
wakeupAllThreads(t.threadsWaitingForTermination, t.handle);
|
||||
t.threadsWaitingForTermination = 0; // No other threads waiting
|
||||
t.threadsWaitingForTermination = 0; // No other threads waiting
|
||||
}
|
||||
|
||||
requireReschedule();
|
||||
|
@ -593,7 +582,7 @@ void Kernel::svcCreateMutex() {
|
|||
}
|
||||
|
||||
void Kernel::svcReleaseMutex() {
|
||||
const Handle handle = regs[0];
|
||||
const HandleType handle = regs[0];
|
||||
logSVC("ReleaseMutex (handle = %x)\n", handle);
|
||||
|
||||
const auto object = getObject(handle, KernelObjectType::Mutex);
|
||||
|
@ -619,18 +608,16 @@ void Kernel::svcCreateSemaphore() {
|
|||
s32 maxCount = static_cast<s32>(regs[2]);
|
||||
logSVC("CreateSemaphore (initial count = %d, max count = %d)\n", initialCount, maxCount);
|
||||
|
||||
if (initialCount > maxCount)
|
||||
Helpers::panic("CreateSemaphore: Initial count higher than max count");
|
||||
if (initialCount > maxCount) Helpers::panic("CreateSemaphore: Initial count higher than max count");
|
||||
|
||||
if (initialCount < 0 || maxCount < 0)
|
||||
Helpers::panic("CreateSemaphore: Negative count value");
|
||||
if (initialCount < 0 || maxCount < 0) Helpers::panic("CreateSemaphore: Negative count value");
|
||||
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = makeSemaphore(initialCount, maxCount);
|
||||
}
|
||||
|
||||
void Kernel::svcReleaseSemaphore() {
|
||||
const Handle handle = regs[1];
|
||||
const HandleType handle = regs[1];
|
||||
const s32 releaseCount = static_cast<s32>(regs[2]);
|
||||
logSVC("ReleaseSemaphore (handle = %X, release count = %d)\n", handle, releaseCount);
|
||||
|
||||
|
@ -641,12 +628,10 @@ void Kernel::svcReleaseSemaphore() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (releaseCount < 0)
|
||||
Helpers::panic("ReleaseSemaphore: Negative count");
|
||||
if (releaseCount < 0) Helpers::panic("ReleaseSemaphore: Negative count");
|
||||
|
||||
Semaphore* s = object->getData<Semaphore>();
|
||||
if (s->maximumCount - s->availableCount < releaseCount)
|
||||
Helpers::panic("ReleaseSemaphore: Release count too high");
|
||||
if (s->maximumCount - s->availableCount < releaseCount) Helpers::panic("ReleaseSemaphore: Release count too high");
|
||||
|
||||
// Write success and old available count to r0 and r1 respectively
|
||||
regs[0] = Result::Success;
|
||||
|
@ -656,10 +641,10 @@ void Kernel::svcReleaseSemaphore() {
|
|||
|
||||
// Wake up threads one by one until the available count hits 0 or we run out of threads to wake up
|
||||
while (s->availableCount > 0 && s->waitlist != 0) {
|
||||
int index = wakeupOneThread(s->waitlist, handle); // Wake up highest priority thread
|
||||
s->waitlist ^= (1ull << index); // Remove thread from waitlist
|
||||
int index = wakeupOneThread(s->waitlist, handle); // Wake up highest priority thread
|
||||
s->waitlist ^= (1ull << index); // Remove thread from waitlist
|
||||
|
||||
s->availableCount--; // Decrement available count
|
||||
s->availableCount--; // Decrement available count
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -675,25 +660,23 @@ bool Kernel::isWaitable(const KernelObject* object) {
|
|||
// Returns whether we should wait on a sync object or not
|
||||
bool Kernel::shouldWaitOnObject(KernelObject* object) {
|
||||
switch (object->type) {
|
||||
case KernelObjectType::Event: // We should wait on an event only if it has not been signalled
|
||||
case KernelObjectType::Event: // We should wait on an event only if it has not been signalled
|
||||
return !object->getData<Event>()->fired;
|
||||
|
||||
case KernelObjectType::Mutex: {
|
||||
Mutex* moo = object->getData<Mutex>(); // mooooooooooo
|
||||
return moo->locked && moo->ownerThread != currentThreadIndex; // If the current thread owns the moo then no reason to wait
|
||||
Mutex* moo = object->getData<Mutex>(); // mooooooooooo
|
||||
return moo->locked && moo->ownerThread != currentThreadIndex; // If the current thread owns the moo then no reason to wait
|
||||
}
|
||||
|
||||
case KernelObjectType::Thread: // Waiting on a thread waits until it's dead. If it's dead then no need to wait
|
||||
case KernelObjectType::Thread: // Waiting on a thread waits until it's dead. If it's dead then no need to wait
|
||||
return object->getData<Thread>()->status != ThreadStatus::Dead;
|
||||
|
||||
case KernelObjectType::Timer: // We should wait on a timer only if it has not been signalled
|
||||
case KernelObjectType::Timer: // We should wait on a timer only if it has not been signalled
|
||||
return !object->getData<Timer>()->fired;
|
||||
|
||||
case KernelObjectType::Semaphore: // Wait if the semaphore count <= 0
|
||||
case KernelObjectType::Semaphore: // Wait if the semaphore count <= 0
|
||||
return object->getData<Semaphore>()->availableCount <= 0;
|
||||
|
||||
default:
|
||||
Helpers::panic("Not sure whether to wait on object (type: %s)", object->getTypeName());
|
||||
return true;
|
||||
default: Helpers::panic("Not sure whether to wait on object (type: %s)", object->getTypeName()); return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "kernel.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
Handle Kernel::makeTimer(ResetType type) {
|
||||
Handle ret = makeObject(KernelObjectType::Timer);
|
||||
HandleType Kernel::makeTimer(ResetType type) {
|
||||
HandleType ret = makeObject(KernelObjectType::Timer);
|
||||
objects[ret].data = new Timer(type);
|
||||
|
||||
if (type == ResetType::Pulse) {
|
||||
|
@ -52,11 +52,9 @@ void Kernel::pollTimers() {
|
|||
}
|
||||
}
|
||||
|
||||
void Kernel::cancelTimer(Timer* timer) {
|
||||
timer->running = false;
|
||||
}
|
||||
void Kernel::cancelTimer(Timer* timer) { timer->running = false; }
|
||||
|
||||
void Kernel::signalTimer(Handle timerHandle, Timer* timer) {
|
||||
void Kernel::signalTimer(HandleType timerHandle, Timer* timer) {
|
||||
timer->fired = true;
|
||||
requireReschedule();
|
||||
|
||||
|
@ -94,7 +92,7 @@ void Kernel::svcCreateTimer() {
|
|||
}
|
||||
|
||||
void Kernel::svcSetTimer() {
|
||||
Handle handle = regs[0];
|
||||
HandleType handle = regs[0];
|
||||
// TODO: Is this actually s64 or u64? 3DBrew says s64, but u64 makes more sense
|
||||
const s64 initial = s64(u64(regs[2]) | (u64(regs[3]) << 32));
|
||||
const s64 interval = s64(u64(regs[1]) | (u64(regs[4]) << 32));
|
||||
|
@ -112,7 +110,7 @@ void Kernel::svcSetTimer() {
|
|||
timer->interval = interval;
|
||||
timer->running = true;
|
||||
timer->fireTick = cpu.getTicks() + Scheduler::nsToCycles(initial);
|
||||
|
||||
|
||||
Scheduler& scheduler = cpu.getScheduler();
|
||||
// Signal an event to poll timers as soon as possible
|
||||
scheduler.removeEvent(Scheduler::EventType::UpdateTimers);
|
||||
|
@ -127,7 +125,7 @@ void Kernel::svcSetTimer() {
|
|||
}
|
||||
|
||||
void Kernel::svcClearTimer() {
|
||||
Handle handle = regs[0];
|
||||
HandleType handle = regs[0];
|
||||
logSVC("ClearTimer (handle = %X)\n", handle);
|
||||
KernelObject* object = getObject(handle, KernelObjectType::Timer);
|
||||
|
||||
|
@ -141,7 +139,7 @@ void Kernel::svcClearTimer() {
|
|||
}
|
||||
|
||||
void Kernel::svcCancelTimer() {
|
||||
Handle handle = regs[0];
|
||||
HandleType handle = regs[0];
|
||||
logSVC("CancelTimer (handle = %X)\n", handle);
|
||||
KernelObject* object = getObject(handle, KernelObjectType::Timer);
|
||||
|
||||
|
@ -152,4 +150,4 @@ void Kernel::svcCancelTimer() {
|
|||
cancelTimer(object->getData<Timer>());
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ u8 Memory::read8(u32 vaddr) {
|
|||
case ConfigMem::FirmRevision: return firm.revision;
|
||||
case ConfigMem::FirmVersionMinor: return firm.minor;
|
||||
case ConfigMem::FirmVersionMajor: return firm.major;
|
||||
case ConfigMem::WifiLevel: return 0; // No wifi :(
|
||||
case ConfigMem::WifiLevel: return 0; // No wifi :(
|
||||
|
||||
case ConfigMem::WifiMac:
|
||||
case ConfigMem::WifiMac + 1:
|
||||
|
@ -171,11 +171,10 @@ u32 Memory::read32(u32 vaddr) {
|
|||
|
||||
case ConfigMem::AppMemAlloc: return appResourceLimits.maxCommit;
|
||||
case ConfigMem::SyscoreVer: return 2;
|
||||
case 0x1FF81000: return 0; // TODO: Figure out what this config mem address does
|
||||
case 0x1FF81000:
|
||||
return 0; // TODO: Figure out what this config mem address does
|
||||
// Wifi MAC: First 4 bytes of MAC Address
|
||||
case ConfigMem::WifiMac:
|
||||
return (u32(MACAddress[3]) << 24) | (u32(MACAddress[2]) << 16) | (u32(MACAddress[1]) << 8) |
|
||||
MACAddress[0];
|
||||
case ConfigMem::WifiMac: return (u32(MACAddress[3]) << 24) | (u32(MACAddress[2]) << 16) | (u32(MACAddress[1]) << 8) | MACAddress[0];
|
||||
|
||||
// 3D slider. Float in range 0.0 = off, 1.0 = max.
|
||||
case ConfigMem::SliderState3D: return Helpers::bit_cast<u32, float>(0.0f);
|
||||
|
@ -185,7 +184,7 @@ u32 Memory::read32(u32 vaddr) {
|
|||
default:
|
||||
if (vaddr >= VirtualAddrs::VramStart && vaddr < VirtualAddrs::VramStart + VirtualAddrs::VramSize) {
|
||||
static int shutUpCounter = 0;
|
||||
if (shutUpCounter < 5) { // Stop spamming about VRAM reads after the first 5
|
||||
if (shutUpCounter < 5) { // Stop spamming about VRAM reads after the first 5
|
||||
shutUpCounter++;
|
||||
Helpers::warn("VRAM read!\n");
|
||||
}
|
||||
|
@ -440,7 +439,7 @@ MemoryInfo Memory::queryMemory(u32 vaddr) {
|
|||
return MemoryInfo(vaddr, pageSize, 0, KernelMemoryTypes::Free);
|
||||
}
|
||||
|
||||
u8* Memory::mapSharedMemory(Handle handle, u32 vaddr, u32 myPerms, u32 otherPerms) {
|
||||
u8* Memory::mapSharedMemory(HandleType handle, u32 vaddr, u32 myPerms, u32 otherPerms) {
|
||||
for (auto& e : sharedMemBlocks) {
|
||||
if (e.handle == handle) {
|
||||
// Virtual Console titles trigger this. TODO: Investigate how it should work
|
||||
|
@ -534,4 +533,4 @@ std::optional<u64> Memory::getProgramID() {
|
|||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
|
6
src/core/renderer_mtl/metal_cpp_impl.cpp
Normal file
6
src/core/renderer_mtl/metal_cpp_impl.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
#define NS_PRIVATE_IMPLEMENTATION
|
||||
#define CA_PRIVATE_IMPLEMENTATION
|
||||
#define MTL_PRIVATE_IMPLEMENTATION
|
||||
#include <Foundation/Foundation.hpp>
|
||||
#include <Metal/Metal.hpp>
|
||||
#include <QuartzCore/QuartzCore.hpp>
|
41
src/core/renderer_mtl/renderer_mtl.cpp
Normal file
41
src/core/renderer_mtl/renderer_mtl.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include "renderer_mtl/renderer_mtl.hpp"
|
||||
|
||||
RendererMTL::RendererMTL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
|
||||
: Renderer(gpu, internalRegs, externalRegs) {}
|
||||
RendererMTL::~RendererMTL() {}
|
||||
|
||||
void RendererMTL::reset() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void RendererMTL::display() {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void RendererMTL::initGraphicsContext(SDL_Window* window) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void RendererMTL::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void RendererMTL::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void RendererMTL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void RendererMTL::drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void RendererMTL::screenshot(const std::string& name) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void RendererMTL::deinitGraphicsContext() {
|
||||
// TODO: implement
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#include "services/ac.hpp"
|
||||
|
||||
#include "ipc.hpp"
|
||||
|
||||
namespace ACCommands {
|
||||
|
@ -72,7 +73,7 @@ void ACService::getLastErrorCode(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x0A, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0); // Hopefully this means no error?
|
||||
mem.write32(messagePointer + 8, 0); // Hopefully this means no error?
|
||||
}
|
||||
|
||||
void ACService::getConnectingInfraPriority(u32 messagePointer) {
|
||||
|
@ -130,10 +131,10 @@ void ACService::registerDisconnectEvent(u32 messagePointer) {
|
|||
const u32 pidHeader = mem.read32(messagePointer + 4);
|
||||
const u32 copyHandleHeader = mem.read32(messagePointer + 12);
|
||||
// Event signaled when disconnecting from AC. TODO: Properly implement it.
|
||||
const Handle eventHandle = mem.read32(messagePointer + 16);
|
||||
const HandleType eventHandle = mem.read32(messagePointer + 16);
|
||||
|
||||
disconnectEvent = eventHandle;
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x30, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#include "services/apt.hpp"
|
||||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
|
||||
namespace APTCommands {
|
||||
enum : u32 {
|
||||
GetLockHandle = 0x00010040,
|
||||
|
@ -84,8 +85,7 @@ void APTService::appletUtility(u32 messagePointer) {
|
|||
u32 outputSize = mem.read32(messagePointer + 12);
|
||||
u32 inputPointer = mem.read32(messagePointer + 20);
|
||||
|
||||
log("APT::AppletUtility(utility = %d, input size = %x, output size = %x, inputPointer = %08X)\n", utility, inputSize, outputSize,
|
||||
inputPointer);
|
||||
log("APT::AppletUtility(utility = %d, input size = %x, output size = %x, inputPointer = %08X)\n", utility, inputSize, outputSize, inputPointer);
|
||||
|
||||
std::vector<u8> out(outputSize);
|
||||
const u32 outputBuffer = mem.read32(messagePointer + 0x104);
|
||||
|
@ -111,9 +111,9 @@ void APTService::getAppletInfo(u32 messagePointer) {
|
|||
mem.write32(messagePointer, IPC::responseHeader(0x06, 7, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
|
||||
mem.write8(messagePointer + 20, 1); // 1 = registered
|
||||
mem.write8(messagePointer + 24, 1); // 1 = loaded
|
||||
// TODO: The rest of this
|
||||
mem.write8(messagePointer + 20, 1); // 1 = registered
|
||||
mem.write8(messagePointer + 24, 1); // 1 = loaded
|
||||
// TODO: The rest of this
|
||||
}
|
||||
|
||||
void APTService::isRegistered(u32 messagePointer) {
|
||||
|
@ -122,7 +122,7 @@ void APTService::isRegistered(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x09, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, 1); // Return that the app is always registered. This might break with home menu?
|
||||
mem.write8(messagePointer + 8, 1); // Return that the app is always registered. This might break with home menu?
|
||||
}
|
||||
|
||||
void APTService::preloadLibraryApplet(u32 messagePointer) {
|
||||
|
@ -144,7 +144,7 @@ void APTService::prepareToStartLibraryApplet(u32 messagePointer) {
|
|||
void APTService::startLibraryApplet(u32 messagePointer) {
|
||||
const u32 appID = mem.read32(messagePointer + 4);
|
||||
const u32 bufferSize = mem.read32(messagePointer + 8);
|
||||
const Handle parameters = mem.read32(messagePointer + 16);
|
||||
const HandleType parameters = mem.read32(messagePointer + 16);
|
||||
const u32 buffer = mem.read32(messagePointer + 24);
|
||||
log("APT::StartLibraryApplet (app ID = %X)\n", appID);
|
||||
|
||||
|
@ -178,7 +178,7 @@ void APTService::checkNew3DS(u32 messagePointer) {
|
|||
log("APT::CheckNew3DS\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x102, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, (model == ConsoleModel::New3DS) ? 1 : 0); // u8, Status (0 = Old 3DS, 1 = New 3DS)
|
||||
mem.write8(messagePointer + 8, (model == ConsoleModel::New3DS) ? 1 : 0); // u8, Status (0 = Old 3DS, 1 = New 3DS)
|
||||
}
|
||||
|
||||
// TODO: Figure out the slight way this differs from APT::CheckNew3DS
|
||||
|
@ -186,7 +186,7 @@ void APTService::checkNew3DSApp(u32 messagePointer) {
|
|||
log("APT::CheckNew3DSApp\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x101, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, (model == ConsoleModel::New3DS) ? 1 : 0); // u8, Status (0 = Old 3DS, 1 = New 3DS)
|
||||
mem.write8(messagePointer + 8, (model == ConsoleModel::New3DS) ? 1 : 0); // u8, Status (0 = Old 3DS, 1 = New 3DS)
|
||||
}
|
||||
|
||||
void APTService::enable(u32 messagePointer) {
|
||||
|
@ -207,14 +207,14 @@ void APTService::initialize(u32 messagePointer) {
|
|||
notificationEvent = kernel.makeEvent(ResetType::OneShot);
|
||||
resumeEvent = kernel.makeEvent(ResetType::OneShot);
|
||||
|
||||
kernel.signalEvent(resumeEvent.value()); // Seems to be signalled on startup
|
||||
kernel.signalEvent(resumeEvent.value()); // Seems to be signalled on startup
|
||||
}
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 3));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0x04000000); // Translation descriptor
|
||||
mem.write32(messagePointer + 12, notificationEvent.value()); // Notification Event Handle
|
||||
mem.write32(messagePointer + 16, resumeEvent.value()); // Resume Event Handle
|
||||
mem.write32(messagePointer + 8, 0x04000000); // Translation descriptor
|
||||
mem.write32(messagePointer + 12, notificationEvent.value()); // Notification Event HandleType
|
||||
mem.write32(messagePointer + 16, resumeEvent.value()); // Resume Event HandleType
|
||||
}
|
||||
|
||||
void APTService::inquireNotification(u32 messagePointer) {
|
||||
|
@ -234,11 +234,11 @@ void APTService::getLockHandle(u32 messagePointer) {
|
|||
}
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1, 3, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success); // Result code
|
||||
mem.write32(messagePointer + 8, 0); // AppletAttr
|
||||
mem.write32(messagePointer + 12, 0); // APT State (bit0 = Power Button State, bit1 = Order To Close State)
|
||||
mem.write32(messagePointer + 16, 0); // Translation descriptor
|
||||
mem.write32(messagePointer + 20, lockHandle.value()); // Lock handle
|
||||
mem.write32(messagePointer + 4, Result::Success); // Result code
|
||||
mem.write32(messagePointer + 8, 0); // AppletAttr
|
||||
mem.write32(messagePointer + 12, 0); // APT State (bit0 = Power Button State, bit1 = Order To Close State)
|
||||
mem.write32(messagePointer + 16, 0); // Translation descriptor
|
||||
mem.write32(messagePointer + 20, lockHandle.value()); // Lock handle
|
||||
}
|
||||
|
||||
// This apparently does nothing on the original kernel either?
|
||||
|
@ -254,7 +254,7 @@ void APTService::sendParameter(u32 messagePointer) {
|
|||
const u32 cmd = mem.read32(messagePointer + 12);
|
||||
const u32 paramSize = mem.read32(messagePointer + 16);
|
||||
|
||||
const u32 parameterHandle = mem.read32(messagePointer + 24); // What dis?
|
||||
const u32 parameterHandle = mem.read32(messagePointer + 24); // What dis?
|
||||
const u32 parameterPointer = mem.read32(messagePointer + 32);
|
||||
log("APT::SendParameter (source app = %X, dest app = %X, cmd = %X, size = %X)", sourceAppID, destAppID, cmd, paramSize);
|
||||
|
||||
|
@ -355,8 +355,8 @@ void APTService::replySleepQuery(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void APTService::setApplicationCpuTimeLimit(u32 messagePointer) {
|
||||
u32 fixed = mem.read32(messagePointer + 4); // MUST be 1.
|
||||
u32 percentage = mem.read32(messagePointer + 8); // CPU time percentage between 5% and 89%
|
||||
u32 fixed = mem.read32(messagePointer + 4); // MUST be 1.
|
||||
u32 percentage = mem.read32(messagePointer + 8); // CPU time percentage between 5% and 89%
|
||||
log("APT::SetApplicationCpuTimeLimit (percentage = %d%%)\n", percentage);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x4F, 1, 0));
|
||||
|
@ -409,15 +409,14 @@ void APTService::theSmashBrosFunction(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void APTService::getWirelessRebootInfo(u32 messagePointer) {
|
||||
const u32 size = mem.read32(messagePointer + 4); // Size of data to read
|
||||
const u32 size = mem.read32(messagePointer + 4); // Size of data to read
|
||||
log("APT::GetWirelessRebootInfo (size = %X)\n", size);
|
||||
|
||||
if (size > 0x10)
|
||||
Helpers::panic("APT::GetWirelessInfo with size > 0x10 bytes");
|
||||
if (size > 0x10) Helpers::panic("APT::GetWirelessInfo with size > 0x10 bytes");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x45, 1, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
mem.write8(messagePointer + 0x104 + i, 0); // Temporarily stub this until we add SetWirelessRebootInfo
|
||||
mem.write8(messagePointer + 0x104 + i, 0); // Temporarily stub this until we add SetWirelessRebootInfo
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "services/boss.hpp"
|
||||
|
||||
#include "ipc.hpp"
|
||||
|
||||
namespace BOSSCommands {
|
||||
|
@ -31,9 +32,7 @@ namespace BOSSCommands {
|
|||
};
|
||||
}
|
||||
|
||||
void BOSSService::reset() {
|
||||
optoutFlag = 0;
|
||||
}
|
||||
void BOSSService::reset() { optoutFlag = 0; }
|
||||
|
||||
void BOSSService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
|
@ -44,8 +43,7 @@ void BOSSService::handleSyncRequest(u32 messagePointer) {
|
|||
case BOSSCommands::GetNsDataIdList:
|
||||
case BOSSCommands::GetNsDataIdList1:
|
||||
case BOSSCommands::GetNsDataIdList2:
|
||||
case BOSSCommands::GetNsDataIdList3:
|
||||
getNsDataIdList(messagePointer, command); break;
|
||||
case BOSSCommands::GetNsDataIdList3: getNsDataIdList(messagePointer, command); break;
|
||||
case BOSSCommands::GetOptoutFlag: getOptoutFlag(messagePointer); break;
|
||||
case BOSSCommands::GetStorageEntryInfo: getStorageEntryInfo(messagePointer); break;
|
||||
case BOSSCommands::GetTaskIdList: getTaskIdList(messagePointer); break;
|
||||
|
@ -99,7 +97,7 @@ void BOSSService::getTaskState(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, 0); // TaskStatus: Report the task finished successfully
|
||||
mem.write32(messagePointer + 12, 0); // Current state value for task PropertyID 0x4
|
||||
mem.write8(messagePointer + 16, 0); // TODO: Figure out what this should be
|
||||
mem.write8(messagePointer + 16, 0); // TODO: Figure out what this should be
|
||||
}
|
||||
|
||||
void BOSSService::getTaskStatus(u32 messagePointer) {
|
||||
|
@ -150,15 +148,15 @@ void BOSSService::getErrorCode(u32 messagePointer) {
|
|||
log("BOSS::GetErrorCode (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x2E, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, Result::Success); // No error code
|
||||
mem.write32(messagePointer + 8, Result::Success); // No error code
|
||||
}
|
||||
|
||||
void BOSSService::getStorageEntryInfo(u32 messagePointer) {
|
||||
log("BOSS::GetStorageEntryInfo (undocumented)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x30, 3, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0); // u32, unknown meaning
|
||||
mem.write16(messagePointer + 12, 0); // s16, unknown meaning
|
||||
mem.write32(messagePointer + 8, 0); // u32, unknown meaning
|
||||
mem.write16(messagePointer + 12, 0); // s16, unknown meaning
|
||||
}
|
||||
|
||||
void BOSSService::sendProperty(u32 messagePointer) {
|
||||
|
@ -170,10 +168,9 @@ void BOSSService::sendProperty(u32 messagePointer) {
|
|||
mem.write32(messagePointer, IPC::responseHeader(0x14, 1, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0); // Read size
|
||||
// TODO: Should this do anything else?
|
||||
// TODO: Should this do anything else?
|
||||
}
|
||||
|
||||
|
||||
void BOSSService::receiveProperty(u32 messagePointer) {
|
||||
const u32 id = mem.read32(messagePointer + 4);
|
||||
const u32 size = mem.read32(messagePointer + 8);
|
||||
|
@ -182,13 +179,13 @@ void BOSSService::receiveProperty(u32 messagePointer) {
|
|||
log("BOSS::ReceiveProperty (id = %d, size = %08X, ptr = %08X) (stubbed)\n", id, size, ptr);
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x16, 2, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0); // Read size
|
||||
mem.write32(messagePointer + 8, 0); // Read size
|
||||
}
|
||||
|
||||
// This seems to accept a KEvent as a parameter and register it for something Spotpass related
|
||||
// I need to update the 3DBrew page when it's known what it does properly
|
||||
void BOSSService::registerNewArrivalEvent(u32 messagePointer) {
|
||||
const Handle eventHandle = mem.read32(messagePointer + 4); // Kernel event handle to register
|
||||
const HandleType eventHandle = mem.read32(messagePointer + 4); // Kernel event handle to register
|
||||
log("BOSS::RegisterNewArrivalEvent (handle = %X)\n", eventHandle);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x8, 1, 0));
|
||||
|
@ -252,5 +249,5 @@ void BOSSService::getNewArrivalFlag(u32 messagePointer) {
|
|||
log("BOSS::GetNewArrivalFlag (stubbed)\n");
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x7, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, 0); // Flag
|
||||
}
|
||||
mem.write8(messagePointer + 8, 0); // Flag
|
||||
}
|
||||
|
|
|
@ -312,7 +312,7 @@ void CAMService::setReceiving(u32 messagePointer) {
|
|||
const u32 portIndex = mem.read8(messagePointer + 8);
|
||||
const u32 size = mem.read32(messagePointer + 12);
|
||||
const u16 transferUnit = mem.read16(messagePointer + 16);
|
||||
const Handle process = mem.read32(messagePointer + 24);
|
||||
const HandleType process = mem.read32(messagePointer + 24);
|
||||
|
||||
const PortSelect port(portIndex);
|
||||
log("CAM::SetReceiving (port = %d)\n", portIndex);
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#include "services/dsp.hpp"
|
||||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
|
||||
namespace DSPCommands {
|
||||
enum : u32 {
|
||||
RecvData = 0x00010040,
|
||||
|
@ -25,10 +26,7 @@ namespace DSPCommands {
|
|||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
HeadphonesNotInserted = 0,
|
||||
HeadphonesInserted = 1
|
||||
};
|
||||
enum : u32 { HeadphonesNotInserted = 0, HeadphonesInserted = 1 };
|
||||
}
|
||||
|
||||
void DSPService::reset() {
|
||||
|
@ -74,7 +72,7 @@ void DSPService::convertProcessAddressFromDspDram(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0xC, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, converted); // Converted address
|
||||
mem.write32(messagePointer + 8, converted); // Converted address
|
||||
}
|
||||
|
||||
void DSPService::loadComponent(u32 messagePointer) {
|
||||
|
@ -94,9 +92,9 @@ void DSPService::loadComponent(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x11, 2, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 1); // Component loaded
|
||||
mem.write32(messagePointer + 8, 1); // Component loaded
|
||||
mem.write32(messagePointer + 12, (size << 4) | 0xA);
|
||||
mem.write32(messagePointer + 16, mem.read32(messagePointer + 20)); // Component buffer
|
||||
mem.write32(messagePointer + 16, mem.read32(messagePointer + 20)); // Component buffer
|
||||
}
|
||||
|
||||
void DSPService::unloadComponent(u32 messagePointer) {
|
||||
|
@ -121,7 +119,7 @@ void DSPService::readPipeIfPossible(u32 messagePointer) {
|
|||
}
|
||||
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write16(messagePointer + 8, u16(data.size())); // Number of bytes read
|
||||
mem.write16(messagePointer + 8, u16(data.size())); // Number of bytes read
|
||||
}
|
||||
|
||||
void DSPService::recvData(u32 messagePointer) {
|
||||
|
@ -153,12 +151,10 @@ DSPService::DSPEvent& DSPService::getEventRef(u32 type, u32 pipe) {
|
|||
case 1: return interrupt1;
|
||||
|
||||
case 2:
|
||||
if (pipe >= pipeCount)
|
||||
Helpers::panic("Tried to access the event of an invalid pipe");
|
||||
if (pipe >= pipeCount) Helpers::panic("Tried to access the event of an invalid pipe");
|
||||
return pipeEvents[pipe];
|
||||
|
||||
default:
|
||||
Helpers::panic("Unknown type for DSP::getEventRef");
|
||||
default: Helpers::panic("Unknown type for DSP::getEventRef");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,8 +166,8 @@ void DSPService::registerInterruptEvents(u32 messagePointer) {
|
|||
|
||||
// The event handle being 0 means we're removing an event
|
||||
if (eventHandle == 0) {
|
||||
DSPEvent& e = getEventRef(interrupt, channel); // Get event
|
||||
if (e.has_value()) { // Remove if it exists
|
||||
DSPEvent& e = getEventRef(interrupt, channel); // Get event
|
||||
if (e.has_value()) { // Remove if it exists
|
||||
totalEventCount--;
|
||||
e = std::nullopt;
|
||||
}
|
||||
|
@ -198,7 +194,7 @@ void DSPService::getHeadphoneStatus(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x1F, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, Result::HeadphonesInserted); // This should be toggleable for shits and giggles
|
||||
mem.write32(messagePointer + 8, Result::HeadphonesInserted); // This should be toggleable for shits and giggles
|
||||
}
|
||||
|
||||
void DSPService::getSemaphoreEventHandle(u32 messagePointer) {
|
||||
|
@ -211,7 +207,7 @@ void DSPService::getSemaphoreEventHandle(u32 messagePointer) {
|
|||
mem.write32(messagePointer, IPC::responseHeader(0x16, 1, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
// TODO: Translation descriptor here?
|
||||
mem.write32(messagePointer + 12, semaphoreEvent.value()); // Semaphore event handle
|
||||
mem.write32(messagePointer + 12, semaphoreEvent.value()); // Semaphore event handle
|
||||
kernel.signalEvent(semaphoreEvent.value());
|
||||
}
|
||||
|
||||
|
@ -249,7 +245,7 @@ void DSPService::writeProcessPipe(u32 messagePointer) {
|
|||
void DSPService::flushDataCache(u32 messagePointer) {
|
||||
const u32 address = mem.read32(messagePointer + 4);
|
||||
const u32 size = mem.read32(messagePointer + 8);
|
||||
const Handle process = mem.read32(messagePointer + 16);
|
||||
const HandleType process = mem.read32(messagePointer + 16);
|
||||
|
||||
log("DSP::FlushDataCache (addr = %08X, size = %08X, process = %X)\n", address, size, process);
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x13, 1, 0));
|
||||
|
@ -259,7 +255,7 @@ void DSPService::flushDataCache(u32 messagePointer) {
|
|||
void DSPService::invalidateDCache(u32 messagePointer) {
|
||||
const u32 address = mem.read32(messagePointer + 4);
|
||||
const u32 size = mem.read32(messagePointer + 8);
|
||||
const Handle process = mem.read32(messagePointer + 16);
|
||||
const HandleType process = mem.read32(messagePointer + 16);
|
||||
|
||||
log("DSP::InvalidateDataCache (addr = %08X, size = %08X, process = %X)\n", address, size, process);
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x14, 1, 0));
|
||||
|
@ -303,4 +299,4 @@ void DSPService::triggerInterrupt1() {
|
|||
if (interrupt1.has_value()) {
|
||||
kernel.signalEvent(*interrupt1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#include "services/fs.hpp"
|
||||
#include "kernel/kernel.hpp"
|
||||
|
||||
#include "io_file.hpp"
|
||||
#include "ipc.hpp"
|
||||
#include "kernel/kernel.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
#ifdef CreateFile // windows.h defines CreateFile & DeleteFile because of course it does.
|
||||
#ifdef CreateFile // windows.h defines CreateFile & DeleteFile because of course it does.
|
||||
#undef CreateDirectory
|
||||
#undef CreateFile
|
||||
#undef DeleteFile
|
||||
|
@ -47,21 +48,18 @@ namespace FSCommands {
|
|||
};
|
||||
}
|
||||
|
||||
void FSService::reset() {
|
||||
priority = 0;
|
||||
}
|
||||
void FSService::reset() { priority = 0; }
|
||||
|
||||
// Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM.
|
||||
void FSService::initializeFilesystem() {
|
||||
const auto sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory
|
||||
const auto sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory
|
||||
const auto nandSharedpath = IOFile::getAppData() / ".." / "SharedFiles" / "NAND";
|
||||
|
||||
const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData
|
||||
const auto formatPath = IOFile::getAppData() / "FormatInfo"; // Create folder for storing archive formatting info
|
||||
const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData
|
||||
const auto formatPath = IOFile::getAppData() / "FormatInfo"; // Create folder for storing archive formatting info
|
||||
const auto systemSaveDataPath = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData";
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
if (!fs::is_directory(nandSharedpath)) {
|
||||
fs::create_directories(nandSharedpath);
|
||||
}
|
||||
|
@ -89,25 +87,21 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) {
|
|||
case ArchiveID::SaveData: return &saveData;
|
||||
case ArchiveID::UserSaveData2: return &userSaveData2;
|
||||
|
||||
case ArchiveID::ExtSaveData:
|
||||
return &extSaveData_sdmc;
|
||||
case ArchiveID::ExtSaveData: return &extSaveData_sdmc;
|
||||
|
||||
case ArchiveID::SharedExtSaveData:
|
||||
return &sharedExtSaveData_nand;
|
||||
case ArchiveID::SharedExtSaveData: return &sharedExtSaveData_nand;
|
||||
|
||||
case ArchiveID::SystemSaveData: return &systemSaveData;
|
||||
case ArchiveID::SDMC: return &sdmc;
|
||||
case ArchiveID::SDMCWriteOnly: return &sdmcWriteOnly;
|
||||
case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI
|
||||
default:
|
||||
Helpers::panic("Unknown archive. ID: %d\n", id);
|
||||
return nullptr;
|
||||
case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI
|
||||
default: Helpers::panic("Unknown archive. ID: %d\n", id); return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Handle> FSService::openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms) {
|
||||
std::optional<HandleType> FSService::openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms) {
|
||||
FileDescriptor opened = archive->openFile(path, perms);
|
||||
if (opened.has_value()) { // If opened doesn't have a value, we failed to open the file
|
||||
if (opened.has_value()) { // If opened doesn't have a value, we failed to open the file
|
||||
auto handle = kernel.makeObject(KernelObjectType::File);
|
||||
|
||||
auto& file = kernel.getObjects()[handle];
|
||||
|
@ -119,9 +113,9 @@ std::optional<Handle> FSService::openFileHandle(ArchiveBase* archive, const FSPa
|
|||
}
|
||||
}
|
||||
|
||||
Rust::Result<Handle, Result::HorizonResult> FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) {
|
||||
Rust::Result<HandleType, 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
|
||||
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];
|
||||
object.data = new DirectorySession(opened.unwrap());
|
||||
|
@ -132,7 +126,7 @@ Rust::Result<Handle, Result::HorizonResult> FSService::openDirectoryHandle(Archi
|
|||
}
|
||||
}
|
||||
|
||||
Rust::Result<Handle, Result::HorizonResult> FSService::openArchiveHandle(u32 archiveID, const FSPath& path) {
|
||||
Rust::Result<HandleType, Result::HorizonResult> FSService::openArchiveHandle(u32 archiveID, const FSPath& path) {
|
||||
ArchiveBase* archive = getArchiveFromID(archiveID, path);
|
||||
|
||||
if (archive == nullptr) [[unlikely]] {
|
||||
|
@ -147,8 +141,7 @@ Rust::Result<Handle, Result::HorizonResult> FSService::openArchiveHandle(u32 arc
|
|||
archiveObject.data = new ArchiveSession(res.unwrap(), path);
|
||||
|
||||
return Ok(handle);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return Err(res.unwrapErr());
|
||||
}
|
||||
}
|
||||
|
@ -157,8 +150,7 @@ FSPath FSService::readPath(u32 type, u32 pointer, u32 size) {
|
|||
std::vector<u8> data;
|
||||
data.resize(size);
|
||||
|
||||
for (u32 i = 0; i < size; i++)
|
||||
data[i] = mem.read8(pointer + i);
|
||||
for (u32 i = 0; i < size; i++) data[i] = mem.read8(pointer + i);
|
||||
|
||||
return FSPath(type, data);
|
||||
}
|
||||
|
@ -217,7 +209,7 @@ void FSService::initializeWithSdkVersion(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void FSService::closeArchive(u32 messagePointer) {
|
||||
const Handle handle = static_cast<u32>(mem.read64(messagePointer + 4)); // TODO: archive handles should be 64-bit
|
||||
const HandleType handle = static_cast<u32>(mem.read64(messagePointer + 4)); // TODO: archive handles should be 64-bit
|
||||
const auto object = kernel.getObject(handle, KernelObjectType::Archive);
|
||||
log("FSService::CloseArchive(handle = %X)\n", handle);
|
||||
|
||||
|
@ -241,7 +233,7 @@ 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, Result::HorizonResult> res = openArchiveHandle(archiveID, archivePath);
|
||||
Rust::Result<HandleType, Result::HorizonResult> res = openArchiveHandle(archiveID, archivePath);
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x80C, 3, 0));
|
||||
if (res.isOk()) {
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
|
@ -254,7 +246,7 @@ void FSService::openArchive(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void FSService::openFile(u32 messagePointer) {
|
||||
const Handle archiveHandle = Handle(mem.read64(messagePointer + 8));
|
||||
const HandleType archiveHandle = HandleType(mem.read64(messagePointer + 8));
|
||||
const u32 filePathType = mem.read32(messagePointer + 16);
|
||||
const u32 filePathSize = mem.read32(messagePointer + 20);
|
||||
const u32 openFlags = mem.read32(messagePointer + 24);
|
||||
|
@ -276,14 +268,14 @@ void FSService::openFile(u32 messagePointer) {
|
|||
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
|
||||
const FilePerms perms(openFlags);
|
||||
|
||||
std::optional<Handle> handle = openFileHandle(archive, filePath, archivePath, perms);
|
||||
std::optional<HandleType> handle = openFileHandle(archive, filePath, archivePath, perms);
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x802, 1, 2));
|
||||
if (!handle.has_value()) {
|
||||
printf("OpenFile failed\n");
|
||||
mem.write32(messagePointer + 4, Result::FS::FileNotFound);
|
||||
} else {
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor"
|
||||
mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor"
|
||||
mem.write32(messagePointer + 12, handle.value());
|
||||
}
|
||||
}
|
||||
|
@ -291,7 +283,7 @@ void FSService::openFile(u32 messagePointer) {
|
|||
void FSService::createDirectory(u32 messagePointer) {
|
||||
log("FS::CreateDirectory\n");
|
||||
|
||||
const Handle archiveHandle = (Handle)mem.read64(messagePointer + 8);
|
||||
const HandleType archiveHandle = (HandleType)mem.read64(messagePointer + 8);
|
||||
const u32 pathType = mem.read32(messagePointer + 16);
|
||||
const u32 pathSize = mem.read32(messagePointer + 20);
|
||||
const u32 pathPointer = mem.read32(messagePointer + 32);
|
||||
|
@ -313,7 +305,7 @@ void FSService::createDirectory(u32 messagePointer) {
|
|||
|
||||
void FSService::openDirectory(u32 messagePointer) {
|
||||
log("FS::OpenDirectory\n");
|
||||
const Handle archiveHandle = (Handle)mem.read64(messagePointer + 4);
|
||||
const HandleType archiveHandle = (HandleType)mem.read64(messagePointer + 4);
|
||||
const u32 pathType = mem.read32(messagePointer + 12);
|
||||
const u32 pathSize = mem.read32(messagePointer + 16);
|
||||
const u32 pathPointer = mem.read32(messagePointer + 24);
|
||||
|
@ -366,7 +358,7 @@ void FSService::openFileDirectly(u32 messagePointer) {
|
|||
}
|
||||
archive = res.unwrap();
|
||||
|
||||
std::optional<Handle> handle = openFileHandle(archive, filePath, archivePath, perms);
|
||||
std::optional<HandleType> handle = openFileHandle(archive, filePath, archivePath, perms);
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x803, 1, 2));
|
||||
if (!handle.has_value()) {
|
||||
printf("OpenFileDirectly failed\n");
|
||||
|
@ -378,7 +370,7 @@ void FSService::openFileDirectly(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void FSService::createFile(u32 messagePointer) {
|
||||
const Handle archiveHandle = Handle(mem.read64(messagePointer + 8));
|
||||
const HandleType archiveHandle = HandleType(mem.read64(messagePointer + 8));
|
||||
const u32 filePathType = mem.read32(messagePointer + 16);
|
||||
const u32 filePathSize = mem.read32(messagePointer + 20);
|
||||
const u32 attributes = mem.read32(messagePointer + 24);
|
||||
|
@ -403,7 +395,7 @@ void FSService::createFile(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void FSService::deleteFile(u32 messagePointer) {
|
||||
const Handle archiveHandle = Handle(mem.read64(messagePointer + 8));
|
||||
const HandleType archiveHandle = HandleType(mem.read64(messagePointer + 8));
|
||||
const u32 filePathType = mem.read32(messagePointer + 16);
|
||||
const u32 filePathSize = mem.read32(messagePointer + 20);
|
||||
const u32 filePathPointer = mem.read32(messagePointer + 28);
|
||||
|
@ -425,7 +417,7 @@ void FSService::deleteFile(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void FSService::deleteDirectory(u32 messagePointer) {
|
||||
const Handle archiveHandle = Handle(mem.read64(messagePointer + 8));
|
||||
const HandleType archiveHandle = HandleType(mem.read64(messagePointer + 8));
|
||||
const u32 filePathType = mem.read32(messagePointer + 16);
|
||||
const u32 filePathSize = mem.read32(messagePointer + 20);
|
||||
const u32 filePathPointer = mem.read32(messagePointer + 28);
|
||||
|
@ -470,8 +462,7 @@ void FSService::formatSaveData(u32 messagePointer) {
|
|||
log("FS::FormatSaveData\n");
|
||||
|
||||
const u32 archiveID = mem.read32(messagePointer + 4);
|
||||
if (archiveID != ArchiveID::SaveData)
|
||||
Helpers::panic("FS::FormatSaveData: Archive is not SaveData");
|
||||
if (archiveID != ArchiveID::SaveData) Helpers::panic("FS::FormatSaveData: Archive is not SaveData");
|
||||
|
||||
// Read path and path info
|
||||
const u32 pathType = mem.read32(messagePointer + 8);
|
||||
|
@ -481,21 +472,15 @@ void FSService::formatSaveData(u32 messagePointer) {
|
|||
// Size of a block. Seems to always be 0x200
|
||||
const u32 blockSize = mem.read32(messagePointer + 16);
|
||||
|
||||
if (blockSize != 0x200 && blockSize != 0x1000)
|
||||
Helpers::panic("FS::FormatSaveData: Invalid SaveData block size");
|
||||
if (blockSize != 0x200 && blockSize != 0x1000) Helpers::panic("FS::FormatSaveData: Invalid SaveData block size");
|
||||
|
||||
const u32 directoryNum = mem.read32(messagePointer + 20); // Max number of directories
|
||||
const u32 fileNum = mem.read32(messagePointer + 24); // Max number of files
|
||||
const u32 directoryBucketNum = mem.read32(messagePointer + 28); // Not sure what a directory bucket is...?
|
||||
const u32 fileBucketNum = mem.read32(messagePointer + 32); // Same here
|
||||
const u32 directoryNum = mem.read32(messagePointer + 20); // Max number of directories
|
||||
const u32 fileNum = mem.read32(messagePointer + 24); // Max number of files
|
||||
const u32 directoryBucketNum = mem.read32(messagePointer + 28); // Not sure what a directory bucket is...?
|
||||
const u32 fileBucketNum = mem.read32(messagePointer + 32); // Same here
|
||||
const bool duplicateData = mem.read8(messagePointer + 36) != 0;
|
||||
|
||||
ArchiveBase::FormatInfo info {
|
||||
.size = blockSize * 0x200,
|
||||
.numOfDirectories = directoryNum,
|
||||
.numOfFiles = fileNum,
|
||||
.duplicateData = duplicateData
|
||||
};
|
||||
ArchiveBase::FormatInfo info{.size = blockSize * 0x200, .numOfDirectories = directoryNum, .numOfFiles = fileNum, .duplicateData = duplicateData};
|
||||
|
||||
saveData.format(path, info);
|
||||
|
||||
|
@ -512,8 +497,8 @@ void FSService::deleteExtSaveData(u32 messagePointer) {
|
|||
log("FS::DeleteExtSaveData (media type = %d, saveID = %llx) (stubbed)\n", mediaType, saveID);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x0852, 1, 0));
|
||||
// TODO: We can't properly implement this yet until we properly support title/save IDs. We will stub this and insert a warning for now. Required for Planet Robobot
|
||||
// When we properly implement it, it will just be a recursive directory deletion
|
||||
// TODO: We can't properly implement this yet until we properly support title/save IDs. We will stub this and insert a warning for now. Required
|
||||
// for Planet Robobot When we properly implement it, it will just be a recursive directory deletion
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
|
@ -521,7 +506,8 @@ void FSService::createExtSaveData(u32 messagePointer) {
|
|||
Helpers::warn("Stubbed call to FS::CreateExtSaveData!");
|
||||
// First 4 words of parameters are the ExtSaveData info
|
||||
// https://www.3dbrew.org/wiki/Filesystem_services#ExtSaveDataInfo
|
||||
// This creates the ExtSaveData with the specified saveid in the specified media type. It stores the SMDH as "icon" in the root of the created directory.
|
||||
// This creates the ExtSaveData with the specified saveid in the specified media type. It stores the SMDH as "icon" in the root of the created
|
||||
// directory.
|
||||
const u8 mediaType = mem.read8(messagePointer + 4);
|
||||
const u64 saveID = mem.read64(messagePointer + 8);
|
||||
const u32 numOfDirectories = mem.read32(messagePointer + 20);
|
||||
|
@ -541,18 +527,13 @@ void FSService::formatThisUserSaveData(u32 messagePointer) {
|
|||
log("FS::FormatThisUserSaveData\n");
|
||||
|
||||
const u32 blockSize = mem.read32(messagePointer + 4);
|
||||
const u32 directoryNum = mem.read32(messagePointer + 8); // Max number of directories
|
||||
const u32 fileNum = mem.read32(messagePointer + 12); // Max number of files
|
||||
const u32 directoryBucketNum = mem.read32(messagePointer + 16); // Not sure what a directory bucket is...?
|
||||
const u32 fileBucketNum = mem.read32(messagePointer + 20); // Same here
|
||||
const u32 directoryNum = mem.read32(messagePointer + 8); // Max number of directories
|
||||
const u32 fileNum = mem.read32(messagePointer + 12); // Max number of files
|
||||
const u32 directoryBucketNum = mem.read32(messagePointer + 16); // Not sure what a directory bucket is...?
|
||||
const u32 fileBucketNum = mem.read32(messagePointer + 20); // Same here
|
||||
const bool duplicateData = mem.read8(messagePointer + 24) != 0;
|
||||
|
||||
ArchiveBase::FormatInfo info {
|
||||
.size = blockSize * 0x200,
|
||||
.numOfDirectories = directoryNum,
|
||||
.numOfFiles = fileNum,
|
||||
.duplicateData = duplicateData
|
||||
};
|
||||
ArchiveBase::FormatInfo info{.size = blockSize * 0x200, .numOfDirectories = directoryNum, .numOfFiles = fileNum, .duplicateData = duplicateData};
|
||||
FSPath emptyPath;
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x080F, 1, 0));
|
||||
|
@ -560,7 +541,7 @@ void FSService::formatThisUserSaveData(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void FSService::controlArchive(u32 messagePointer) {
|
||||
const Handle archiveHandle = Handle(mem.read64(messagePointer + 4));
|
||||
const HandleType archiveHandle = HandleType(mem.read64(messagePointer + 4));
|
||||
const u32 action = mem.read32(messagePointer + 12);
|
||||
const u32 inputSize = mem.read32(messagePointer + 16);
|
||||
const u32 outputSize = mem.read32(messagePointer + 20);
|
||||
|
@ -578,24 +559,22 @@ void FSService::controlArchive(u32 messagePointer) {
|
|||
}
|
||||
|
||||
switch (action) {
|
||||
case 0: // Commit save data changes. Shouldn't need us to do anything
|
||||
case 0: // Commit save data changes. Shouldn't need us to do anything
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
break;
|
||||
|
||||
case 1: // Retrieves a file's last-modified timestamp. Seen in DDLC, stubbed for the moment
|
||||
case 1: // Retrieves a file's last-modified timestamp. Seen in DDLC, stubbed for the moment
|
||||
Helpers::warn("FS::ControlArchive: Tried to retrieve a file's last-modified timestamp");
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
break;
|
||||
|
||||
default:
|
||||
Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action);
|
||||
break;
|
||||
default: Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action); break;
|
||||
}
|
||||
}
|
||||
|
||||
void FSService::getFreeBytes(u32 messagePointer) {
|
||||
log("FS::GetFreeBytes\n");
|
||||
const Handle archiveHandle = (Handle)mem.read64(messagePointer + 4);
|
||||
const HandleType archiveHandle = (HandleType)mem.read64(messagePointer + 4);
|
||||
auto session = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x812, 3, 0));
|
||||
|
@ -639,7 +618,7 @@ void FSService::getArchiveResource(u32 messagePointer) {
|
|||
}
|
||||
|
||||
void FSService::setArchivePriority(u32 messagePointer) {
|
||||
Handle archive = mem.read64(messagePointer + 4);
|
||||
HandleType archive = mem.read64(messagePointer + 4);
|
||||
const u32 value = mem.read32(messagePointer + 12);
|
||||
log("FS::SetArchivePriority (priority = %d, archive handle = %X)\n", value, handle);
|
||||
|
||||
|
@ -673,9 +652,9 @@ void FSService::getThisSaveDataSecureValue(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x86F, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write8(messagePointer + 8, 0); // Secure value does not exist
|
||||
mem.write8(messagePointer + 12, 1); // TODO: What is this?
|
||||
mem.write64(messagePointer + 16, 0); // Secure value
|
||||
mem.write8(messagePointer + 8, 0); // Secure value does not exist
|
||||
mem.write8(messagePointer + 12, 1); // TODO: What is this?
|
||||
mem.write64(messagePointer + 16, 0); // Secure value
|
||||
}
|
||||
|
||||
void FSService::setThisSaveDataSecureValue(u32 messagePointer) {
|
||||
|
@ -731,8 +710,8 @@ void FSService::renameFile(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x805, 1, 0));
|
||||
|
||||
const Handle sourceArchiveHandle = mem.read64(messagePointer + 8);
|
||||
const Handle destArchiveHandle = mem.read64(messagePointer + 24);
|
||||
const HandleType sourceArchiveHandle = mem.read64(messagePointer + 8);
|
||||
const HandleType destArchiveHandle = mem.read64(messagePointer + 24);
|
||||
|
||||
// Read path info
|
||||
const u32 sourcePathType = mem.read32(messagePointer + 16);
|
||||
|
@ -785,4 +764,4 @@ void FSService::getSdmcArchiveResource(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 12, resource.clusterSize);
|
||||
mem.write32(messagePointer + 16, resource.partitionCapacityInClusters);
|
||||
mem.write32(messagePointer + 20, resource.freeSpaceInClusters);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#include "services/hid.hpp"
|
||||
|
||||
#include <bit>
|
||||
|
||||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
#include <bit>
|
||||
|
||||
namespace HIDCommands {
|
||||
enum : u32 {
|
||||
|
@ -86,24 +88,24 @@ void HIDService::disableGyroscopeLow(u32 messagePointer) {
|
|||
|
||||
void HIDService::getGyroscopeLowCalibrateParam(u32 messagePointer) {
|
||||
log("HID::GetGyroscopeLowCalibrateParam\n");
|
||||
constexpr s16 unit = 6700; // Approximately from Citra which took it from hardware
|
||||
constexpr s16 unit = 6700; // Approximately from Citra which took it from hardware
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x16, 6, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
// Fill calibration data (for x/y/z depending on i)
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const u32 pointer = messagePointer + 8 + i * 3 * sizeof(u16); // Pointer to write the calibration info for the current coordinate
|
||||
const u32 pointer = messagePointer + 8 + i * 3 * sizeof(u16); // Pointer to write the calibration info for the current coordinate
|
||||
|
||||
mem.write16(pointer, 0); // Zero point
|
||||
mem.write16(pointer + 1 * sizeof(u16), unit); // Positive unit point
|
||||
mem.write16(pointer + 2 * sizeof(u16), -unit); // Negative unit point
|
||||
mem.write16(pointer, 0); // Zero point
|
||||
mem.write16(pointer + 1 * sizeof(u16), unit); // Positive unit point
|
||||
mem.write16(pointer + 2 * sizeof(u16), -unit); // Negative unit point
|
||||
}
|
||||
}
|
||||
|
||||
void HIDService::getGyroscopeCoefficient(u32 messagePointer) {
|
||||
log("HID::GetGyroscopeLowRawToDpsCoefficient\n");
|
||||
|
||||
constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS
|
||||
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, Helpers::bit_cast<u32, float>(gyroscopeCoeff));
|
||||
|
@ -134,13 +136,13 @@ void HIDService::getIPCHandles(u32 messagePointer) {
|
|||
}
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0xA, 1, 7));
|
||||
mem.write32(messagePointer + 4, Result::Success); // Result code
|
||||
mem.write32(messagePointer + 8, 0x14000000); // Translation descriptor
|
||||
mem.write32(messagePointer + 12, KernelHandles::HIDSharedMemHandle); // Shared memory handle
|
||||
mem.write32(messagePointer + 4, Result::Success); // Result code
|
||||
mem.write32(messagePointer + 8, 0x14000000); // Translation descriptor
|
||||
mem.write32(messagePointer + 12, KernelHandles::HIDSharedMemHandle); // Shared memory handle
|
||||
|
||||
// Write HID event handles
|
||||
for (int i = 0; i < events.size(); i++) {
|
||||
mem.write32(messagePointer + 16 + sizeof(Handle) * i, events[i].value());
|
||||
mem.write32(messagePointer + 16 + sizeof(HandleType) * i, events[i].value());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,20 +151,20 @@ void HIDService::updateInputs(u64 currentTick) {
|
|||
if (sharedMem) {
|
||||
// First, update the pad state
|
||||
if (nextPadIndex == 0) {
|
||||
writeSharedMem<u64>(0x8, readSharedMem<u64>(0x0)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0x0, currentTick); // Write new tick count
|
||||
writeSharedMem<u64>(0x8, readSharedMem<u64>(0x0)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0x0, currentTick); // Write new tick count
|
||||
}
|
||||
|
||||
writeSharedMem<u32>(0x10, nextPadIndex); // Index last updated by the HID module
|
||||
writeSharedMem<u32>(0x1C, newButtons); // Current PAD state
|
||||
writeSharedMem<s16>(0x20, circlePadX); // Current circle pad state
|
||||
writeSharedMem<u32>(0x10, nextPadIndex); // Index last updated by the HID module
|
||||
writeSharedMem<u32>(0x1C, newButtons); // Current PAD state
|
||||
writeSharedMem<s16>(0x20, circlePadX); // Current circle pad state
|
||||
writeSharedMem<s16>(0x22, circlePadY);
|
||||
|
||||
const size_t padEntryOffset = 0x28 + (nextPadIndex * 0x10); // Offset in the array of 8 pad entries
|
||||
nextPadIndex = (nextPadIndex + 1) % 8; // Move to next entry
|
||||
const size_t padEntryOffset = 0x28 + (nextPadIndex * 0x10); // Offset in the array of 8 pad entries
|
||||
nextPadIndex = (nextPadIndex + 1) % 8; // Move to next entry
|
||||
|
||||
const u32 pressed = (newButtons ^ oldButtons) & newButtons; // Pressed buttons
|
||||
const u32 released = (newButtons ^ oldButtons) & oldButtons; // Released buttons
|
||||
const u32 pressed = (newButtons ^ oldButtons) & newButtons; // Pressed buttons
|
||||
const u32 released = (newButtons ^ oldButtons) & oldButtons; // Released buttons
|
||||
oldButtons = newButtons;
|
||||
|
||||
writeSharedMem<u32>(padEntryOffset, newButtons);
|
||||
|
@ -173,12 +175,12 @@ void HIDService::updateInputs(u64 currentTick) {
|
|||
|
||||
// Next, update touchscreen state
|
||||
if (nextTouchscreenIndex == 0) {
|
||||
writeSharedMem<u64>(0xB0, readSharedMem<u64>(0xA8)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0xA8, currentTick); // Write new tick count
|
||||
writeSharedMem<u64>(0xB0, readSharedMem<u64>(0xA8)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0xA8, currentTick); // Write new tick count
|
||||
}
|
||||
writeSharedMem<u32>(0xB8, nextTouchscreenIndex); // Index last updated by the HID module
|
||||
const size_t touchEntryOffset = 0xC8 + (nextTouchscreenIndex * 8); // Offset in the array of 8 touchscreen entries
|
||||
nextTouchscreenIndex = (nextTouchscreenIndex + 1) % 8; // Move to next entry
|
||||
writeSharedMem<u32>(0xB8, nextTouchscreenIndex); // Index last updated by the HID module
|
||||
const size_t touchEntryOffset = 0xC8 + (nextTouchscreenIndex * 8); // Offset in the array of 8 touchscreen entries
|
||||
nextTouchscreenIndex = (nextTouchscreenIndex + 1) % 8; // Move to next entry
|
||||
|
||||
writeSharedMem<u16>(touchEntryOffset, touchScreenX);
|
||||
writeSharedMem<u16>(touchEntryOffset + 2, touchScreenY);
|
||||
|
@ -186,16 +188,16 @@ void HIDService::updateInputs(u64 currentTick) {
|
|||
|
||||
// Next, update accelerometer state
|
||||
if (nextAccelerometerIndex == 0) {
|
||||
writeSharedMem<u64>(0x110, readSharedMem<u64>(0x108)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0x108, currentTick); // Write new tick count
|
||||
writeSharedMem<u64>(0x110, readSharedMem<u64>(0x108)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0x108, currentTick); // Write new tick count
|
||||
}
|
||||
writeSharedMem<u32>(0x118, nextAccelerometerIndex); // Index last updated by the HID module
|
||||
nextAccelerometerIndex = (nextAccelerometerIndex + 1) % 8; // Move to next entry
|
||||
writeSharedMem<u32>(0x118, nextAccelerometerIndex); // Index last updated by the HID module
|
||||
nextAccelerometerIndex = (nextAccelerometerIndex + 1) % 8; // Move to next entry
|
||||
|
||||
// Next, update gyro state
|
||||
if (nextGyroIndex == 0) {
|
||||
writeSharedMem<u64>(0x160, readSharedMem<u64>(0x158)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0x158, currentTick); // Write new tick count
|
||||
writeSharedMem<u64>(0x160, readSharedMem<u64>(0x158)); // Copy previous tick count
|
||||
writeSharedMem<u64>(0x158, currentTick); // Write new tick count
|
||||
}
|
||||
const size_t gyroEntryOffset = 0x178 + (nextGyroIndex * 6); // Offset in the array of 8 touchscreen entries
|
||||
writeSharedMem<u16>(gyroEntryOffset, pitch);
|
||||
|
@ -205,8 +207,8 @@ void HIDService::updateInputs(u64 currentTick) {
|
|||
// Since gyroscope euler angles are relative, we zero them out here and the frontend will update them again when we receive a new rotation
|
||||
roll = pitch = yaw = 0;
|
||||
|
||||
writeSharedMem<u32>(0x168, nextGyroIndex); // Index last updated by the HID module
|
||||
nextGyroIndex = (nextGyroIndex + 1) % 32; // Move to next entry
|
||||
writeSharedMem<u32>(0x168, nextGyroIndex); // Index last updated by the HID module
|
||||
nextGyroIndex = (nextGyroIndex + 1) % 32; // Move to next entry
|
||||
}
|
||||
|
||||
// For some reason, the original developers decided to signal the HID events each time the OS rescanned inputs
|
||||
|
@ -218,4 +220,4 @@ void HIDService::updateInputs(u64 currentTick) {
|
|||
kernel.signalEvent(e.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#include "services/ldr_ro.hpp"
|
||||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "ipc.hpp"
|
||||
#include "kernel.hpp"
|
||||
|
||||
namespace LDRCommands {
|
||||
enum : u32 {
|
||||
Initialize = 0x000100C2,
|
||||
|
@ -65,10 +66,13 @@ namespace SegmentTable {
|
|||
|
||||
namespace SegmentID {
|
||||
enum : u32 {
|
||||
TEXT, RODATA, DATA, BSS,
|
||||
TEXT,
|
||||
RODATA,
|
||||
DATA,
|
||||
BSS,
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace SegmentTable
|
||||
|
||||
namespace NamedExportTable {
|
||||
enum : u32 {
|
||||
|
@ -118,8 +122,8 @@ namespace RelocationPatch {
|
|||
enum : u32 {
|
||||
SegmentOffset = 0,
|
||||
PatchType = 4,
|
||||
IsLastEntry = 5, // For import patches
|
||||
SegmentIndex = 5, // For relocation patches
|
||||
IsLastEntry = 5, // For import patches
|
||||
SegmentIndex = 5, // For relocation patches
|
||||
IsResolved = 6,
|
||||
Addend = 8,
|
||||
};
|
||||
|
@ -129,7 +133,7 @@ namespace RelocationPatch {
|
|||
AbsoluteAddress = 2,
|
||||
};
|
||||
};
|
||||
};
|
||||
}; // namespace RelocationPatch
|
||||
|
||||
struct CROHeaderEntry {
|
||||
u32 offset, size;
|
||||
|
@ -144,12 +148,12 @@ static const std::string CRR_MAGIC("CRR0");
|
|||
class CRO {
|
||||
Memory &mem;
|
||||
|
||||
u32 croPointer; // Origin address of CRO in RAM
|
||||
u32 croPointer; // Origin address of CRO in RAM
|
||||
u32 oldDataSegmentOffset;
|
||||
|
||||
bool isCRO; // False if CRS
|
||||
bool isCRO; // False if CRS
|
||||
|
||||
public:
|
||||
public:
|
||||
CRO(Memory &mem, u32 croPointer, bool isCRO) : mem(mem), croPointer(croPointer), oldDataSegmentOffset(0), isCRO(isCRO) {}
|
||||
~CRO() = default;
|
||||
|
||||
|
@ -159,21 +163,13 @@ public:
|
|||
return mem.readString(moduleName.offset, moduleName.size);
|
||||
}
|
||||
|
||||
u32 getNextCRO() {
|
||||
return mem.read32(croPointer + CROHeader::NextCRO);
|
||||
}
|
||||
|
||||
u32 getPrevCRO() {
|
||||
return mem.read32(croPointer + CROHeader::PrevCRO);
|
||||
}
|
||||
u32 getNextCRO() { return mem.read32(croPointer + CROHeader::NextCRO); }
|
||||
|
||||
void setNextCRO(u32 nextCRO) {
|
||||
mem.write32(croPointer + CROHeader::NextCRO, nextCRO);
|
||||
}
|
||||
u32 getPrevCRO() { return mem.read32(croPointer + CROHeader::PrevCRO); }
|
||||
|
||||
void setPrevCRO(u32 prevCRO) {
|
||||
mem.write32(croPointer + CROHeader::PrevCRO, prevCRO);
|
||||
}
|
||||
void setNextCRO(u32 nextCRO) { mem.write32(croPointer + CROHeader::NextCRO, nextCRO); }
|
||||
|
||||
void setPrevCRO(u32 prevCRO) { mem.write32(croPointer + CROHeader::PrevCRO, prevCRO); }
|
||||
|
||||
void write32(u32 addr, u32 value) {
|
||||
// Note: some games export symbols to the static module, which doesn't contain any segments.
|
||||
|
@ -181,11 +177,11 @@ public:
|
|||
// can't be accessed via mem.write32()
|
||||
auto writePointer = mem.getWritePointer(addr);
|
||||
if (writePointer) {
|
||||
*(u32*)writePointer = value;
|
||||
*(u32 *)writePointer = value;
|
||||
} else {
|
||||
auto readPointer = mem.getReadPointer(addr);
|
||||
if (readPointer) {
|
||||
*(u32*)readPointer = value;
|
||||
*(u32 *)readPointer = value;
|
||||
} else {
|
||||
Helpers::panic("LDR_RO write to invalid address = %X\n", addr);
|
||||
}
|
||||
|
@ -219,11 +215,9 @@ public:
|
|||
return entryOffset + offset;
|
||||
}
|
||||
|
||||
u32 getOnUnresolvedAddr() {
|
||||
return getSegmentAddr(mem.read32(croPointer + CROHeader::OnUnresolved));
|
||||
}
|
||||
u32 getOnUnresolvedAddr() { return getSegmentAddr(mem.read32(croPointer + CROHeader::OnUnresolved)); }
|
||||
|
||||
u32 getNamedExportSymbolAddr(const std::string& symbolName) {
|
||||
u32 getNamedExportSymbolAddr(const std::string &symbolName) {
|
||||
// Note: The CRO contains a trie for fast symbol lookup. For simplicity,
|
||||
// we won't use it and instead look up the symbol in the named export symbol table
|
||||
|
||||
|
@ -233,7 +227,7 @@ public:
|
|||
|
||||
for (u32 namedExport = 0; namedExport < namedExportTable.size; namedExport++) {
|
||||
const u32 nameOffset = mem.read32(namedExportTable.offset + 8 * namedExport + NamedExportTable::NameOffset);
|
||||
|
||||
|
||||
const std::string exportSymbolName = mem.readString(nameOffset, exportStringSize);
|
||||
|
||||
if (symbolName.compare(exportSymbolName) == 0) {
|
||||
|
@ -437,13 +431,16 @@ public:
|
|||
const u32 segmentID = mem.read32(segmentTable.offset + 12 * segment + SegmentTable::ID);
|
||||
switch (segmentID) {
|
||||
case SegmentTable::SegmentID::DATA:
|
||||
*oldDataVaddr = segmentOffset + croPointer; oldDataSegmentOffset = segmentOffset; segmentOffset = dataVaddr; break;
|
||||
*oldDataVaddr = segmentOffset + croPointer;
|
||||
oldDataSegmentOffset = segmentOffset;
|
||||
segmentOffset = dataVaddr;
|
||||
break;
|
||||
case SegmentTable::SegmentID::BSS: segmentOffset = bssVaddr; break;
|
||||
case SegmentTable::SegmentID::TEXT:
|
||||
case SegmentTable::SegmentID::RODATA:
|
||||
if (segmentOffset != 0) segmentOffset += croPointer; break;
|
||||
default:
|
||||
Helpers::panic("Unknown segment ID = %u", segmentID);
|
||||
if (segmentOffset != 0) segmentOffset += croPointer;
|
||||
break;
|
||||
default: Helpers::panic("Unknown segment ID = %u", segmentID);
|
||||
}
|
||||
|
||||
mem.write32(segmentTable.offset + 12 * segment + SegmentTable::Offset, segmentOffset);
|
||||
|
@ -464,9 +461,9 @@ public:
|
|||
case SegmentTable::SegmentID::BSS: segmentOffset = 0; break;
|
||||
case SegmentTable::SegmentID::TEXT:
|
||||
case SegmentTable::SegmentID::RODATA:
|
||||
if (segmentOffset != 0) segmentOffset -= croPointer; break;
|
||||
default:
|
||||
Helpers::panic("Unknown segment ID = %u", segmentID);
|
||||
if (segmentOffset != 0) segmentOffset -= croPointer;
|
||||
break;
|
||||
default: Helpers::panic("Unknown segment ID = %u", segmentID);
|
||||
}
|
||||
|
||||
mem.write32(segmentTable.offset + 12 * segment + SegmentTable::Offset, segmentOffset);
|
||||
|
@ -630,7 +627,9 @@ public:
|
|||
u32 relocationOffset = mem.read32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset);
|
||||
|
||||
if (relocationOffset != 0) {
|
||||
mem.write32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset + croPointer);
|
||||
mem.write32(
|
||||
anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset + croPointer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -644,7 +643,9 @@ public:
|
|||
u32 relocationOffset = mem.read32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset);
|
||||
|
||||
if (relocationOffset != 0) {
|
||||
mem.write32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset - croPointer);
|
||||
mem.write32(
|
||||
anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset - croPointer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -652,7 +653,7 @@ public:
|
|||
}
|
||||
|
||||
bool relocateInternalSymbols(u32 oldDataVaddr) {
|
||||
const u8* header = (u8*)mem.getReadPointer(croPointer);
|
||||
const u8 *header = (u8 *)mem.getReadPointer(croPointer);
|
||||
|
||||
const CROHeaderEntry relocationPatchTable = getHeaderEntry(CROHeader::RelocationPatchTableOffset);
|
||||
const CROHeaderEntry segmentTable = getHeaderEntry(CROHeader::SegmentTableOffset);
|
||||
|
@ -708,7 +709,7 @@ public:
|
|||
for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) {
|
||||
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
||||
const u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset);
|
||||
|
||||
|
||||
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
||||
|
||||
if (symbolName.compare(std::string("__aeabi_atexit")) == 0) {
|
||||
|
@ -720,7 +721,7 @@ public:
|
|||
const u32 exportSymbolAddr = cro.getNamedExportSymbolAddr(std::string("nnroAeabiAtexit_"));
|
||||
if (exportSymbolAddr != 0) {
|
||||
patchBatch(relocationOffset, exportSymbolAddr);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -750,7 +751,7 @@ public:
|
|||
|
||||
if (isResolved == 0) {
|
||||
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
||||
|
||||
|
||||
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
||||
|
||||
// Check every loaded CRO for the symbol (the pain)
|
||||
|
@ -859,7 +860,7 @@ public:
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool clearModules() {
|
||||
const u32 onUnresolvedAddr = getOnUnresolvedAddr();
|
||||
|
||||
|
@ -874,7 +875,7 @@ public:
|
|||
if (indexedOffset == 0) {
|
||||
Helpers::panic("Indexed symbol offset is NULL");
|
||||
}
|
||||
|
||||
|
||||
const u32 relocationOffset = mem.read32(indexedOffset + 8 * indexedImport + IndexedImportTable::RelocationOffset);
|
||||
|
||||
patchBatch(relocationOffset, onUnresolvedAddr, true);
|
||||
|
@ -919,7 +920,7 @@ public:
|
|||
|
||||
if (isResolved == 0) {
|
||||
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
||||
|
||||
|
||||
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
||||
|
||||
// Check our current CRO for the symbol
|
||||
|
@ -983,7 +984,7 @@ public:
|
|||
u32 currentCROPointer = loadedCRS;
|
||||
while (currentCROPointer != 0) {
|
||||
CRO cro(mem, currentCROPointer, true);
|
||||
|
||||
|
||||
const u32 onUnresolvedAddr = cro.getOnUnresolvedAddr();
|
||||
|
||||
const u32 importStringSize = mem.read32(currentCROPointer + CROHeader::ImportStringSize);
|
||||
|
@ -998,7 +999,7 @@ public:
|
|||
|
||||
if (isResolved != 0) {
|
||||
const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset);
|
||||
|
||||
|
||||
const std::string symbolName = mem.readString(nameOffset, importStringSize);
|
||||
|
||||
// Check our current CRO for the symbol
|
||||
|
@ -1106,7 +1107,7 @@ public:
|
|||
}
|
||||
|
||||
CRO crs(mem, loadedCRS, false);
|
||||
|
||||
|
||||
u32 headAddr = crs.getPrevCRO();
|
||||
if (autoLink) {
|
||||
headAddr = crs.getNextCRO();
|
||||
|
@ -1189,9 +1190,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
void LDRService::reset() {
|
||||
loadedCRS = 0;
|
||||
}
|
||||
void LDRService::reset() { loadedCRS = 0; }
|
||||
|
||||
void LDRService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
|
@ -1210,7 +1209,7 @@ void LDRService::initialize(u32 messagePointer) {
|
|||
const u32 crsPointer = mem.read32(messagePointer + 4);
|
||||
const u32 size = mem.read32(messagePointer + 8);
|
||||
const u32 mapVaddr = mem.read32(messagePointer + 12);
|
||||
const Handle process = mem.read32(messagePointer + 20);
|
||||
const HandleType process = mem.read32(messagePointer + 20);
|
||||
|
||||
log("LDR_RO::Initialize (buffer = %08X, size = %08X, vaddr = %08X, process = %X)\n", crsPointer, size, mapVaddr, process);
|
||||
|
||||
|
@ -1258,7 +1257,7 @@ void LDRService::initialize(u32 messagePointer) {
|
|||
|
||||
void LDRService::linkCRO(u32 messagePointer) {
|
||||
const u32 mapVaddr = mem.read32(messagePointer + 4);
|
||||
const Handle process = mem.read32(messagePointer + 12);
|
||||
const HandleType process = mem.read32(messagePointer + 12);
|
||||
|
||||
log("LDR_RO::LinkCRO (vaddr = %X, process = %X)\n", mapVaddr, process);
|
||||
|
||||
|
@ -1287,7 +1286,7 @@ void LDRService::linkCRO(u32 messagePointer) {
|
|||
void LDRService::loadCRR(u32 messagePointer) {
|
||||
const u32 crrPointer = mem.read32(messagePointer + 4);
|
||||
const u32 size = mem.read32(messagePointer + 8);
|
||||
const Handle process = mem.read32(messagePointer + 20);
|
||||
const HandleType process = mem.read32(messagePointer + 20);
|
||||
|
||||
log("LDR_RO::LoadCRR (buffer = %08X, size = %08X, process = %X)\n", crrPointer, size, process);
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 0));
|
||||
|
@ -1304,9 +1303,11 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) {
|
|||
const u32 bssSize = mem.read32(messagePointer + 32);
|
||||
const bool autoLink = mem.read32(messagePointer + 36) != 0;
|
||||
const u32 fixLevel = mem.read32(messagePointer + 40);
|
||||
const Handle process = mem.read32(messagePointer + 52);
|
||||
const HandleType process = mem.read32(messagePointer + 52);
|
||||
|
||||
log("LDR_RO::LoadCRO (isNew = %d, buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", isNew, croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process);
|
||||
log("LDR_RO::LoadCRO (isNew = %d, buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size "
|
||||
"= %08X, auto link = %d, fix level = %X, process = %X)\n",
|
||||
isNew, croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process);
|
||||
|
||||
// Sanity checks
|
||||
if (size < CRO_HEADER_SIZE) {
|
||||
|
@ -1362,7 +1363,7 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) {
|
|||
void LDRService::unloadCRO(u32 messagePointer) {
|
||||
const u32 mapVaddr = mem.read32(messagePointer + 4);
|
||||
const u32 croPointer = mem.read32(messagePointer + 12);
|
||||
const Handle process = mem.read32(messagePointer + 20);
|
||||
const HandleType process = mem.read32(messagePointer + 20);
|
||||
|
||||
log("LDR_RO::UnloadCRO (vaddr = %08X, buffer = %08X, process = %X)\n", mapVaddr, croPointer, process);
|
||||
|
||||
|
@ -1392,4 +1393,4 @@ void LDRService::unloadCRO(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
ServiceManager::ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config)
|
||||
: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem),
|
||||
csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config),
|
||||
gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem),
|
||||
news_u(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {}
|
||||
gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem),
|
||||
ndm(mem), news_u(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {}
|
||||
|
||||
static constexpr int MAX_NOTIFICATION_COUNT = 16;
|
||||
|
||||
|
@ -68,7 +68,7 @@ namespace Commands {
|
|||
};
|
||||
}
|
||||
|
||||
// Handle an IPC message issued using the SendSyncRequest SVC
|
||||
// HandleType 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
|
||||
void ServiceManager::handleSyncRequest(u32 messagePointer) {
|
||||
|
@ -93,7 +93,7 @@ void ServiceManager::registerClient(u32 messagePointer) {
|
|||
}
|
||||
|
||||
// clang-format off
|
||||
static std::map<std::string, Handle> serviceMap = {
|
||||
static std::map<std::string, HandleType> serviceMap = {
|
||||
{ "ac:u", KernelHandles::AC },
|
||||
{ "act:a", KernelHandles::ACT },
|
||||
{ "act:u", KernelHandles::ACT },
|
||||
|
@ -165,9 +165,9 @@ void ServiceManager::enableNotification(u32 messagePointer) {
|
|||
}
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 2));
|
||||
mem.write32(messagePointer + 4, Result::Success); // Result code
|
||||
mem.write32(messagePointer + 8, 0); // Translation descriptor
|
||||
// Handle to semaphore signaled on process notification
|
||||
mem.write32(messagePointer + 4, Result::Success); // Result code
|
||||
mem.write32(messagePointer + 8, 0); // Translation descriptor
|
||||
// HandleType to semaphore signaled on process notification
|
||||
mem.write32(messagePointer + 12, notificationSemaphore.value());
|
||||
}
|
||||
|
||||
|
@ -175,8 +175,8 @@ void ServiceManager::receiveNotification(u32 messagePointer) {
|
|||
log("srv::ReceiveNotification() (STUBBED)\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0xB, 2, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success); // Result code
|
||||
mem.write32(messagePointer + 8, 0); // Notification ID
|
||||
mem.write32(messagePointer + 4, Result::Success); // Result code
|
||||
mem.write32(messagePointer + 8, 0); // Notification ID
|
||||
}
|
||||
|
||||
void ServiceManager::subscribe(u32 messagePointer) {
|
||||
|
@ -195,7 +195,7 @@ void ServiceManager::unsubscribe(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
|
||||
void ServiceManager::sendCommandToService(u32 messagePointer, HandleType handle) {
|
||||
switch (handle) {
|
||||
// Breaking alphabetical order a bit to place the ones I think are most common at the top
|
||||
case KernelHandles::GPU: [[likely]] gsp_gpu.handleSyncRequest(messagePointer); break;
|
||||
|
@ -237,4 +237,4 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
|
|||
case KernelHandles::Y2R: y2r.handleSyncRequest(messagePointer); break;
|
||||
default: Helpers::panic("Sent IPC message to unknown service %08X\n Command: %08X", handle, mem.read32(messagePointer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ void SOCService::handleSyncRequest(u32 messagePointer) {
|
|||
|
||||
void SOCService::initializeSockets(u32 messagePointer) {
|
||||
const u32 memoryBlockSize = mem.read32(messagePointer + 4);
|
||||
const Handle sharedMemHandle = mem.read32(messagePointer + 20);
|
||||
const HandleType sharedMemHandle = mem.read32(messagePointer + 20);
|
||||
log("SOC::InitializeSockets (memory block size = %08X, shared mem handle = %08X)\n", memoryBlockSize, sharedMemHandle);
|
||||
|
||||
// TODO: Does double initialization return an error code?
|
||||
|
@ -30,4 +30,4 @@ void SOCService::initializeSockets(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x01, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
}
|
||||
|
|
16
src/host_shaders/metal_display.metal
Normal file
16
src/host_shaders/metal_display.metal
Normal file
|
@ -0,0 +1,16 @@
|
|||
struct VertexOut {
|
||||
float4 position [[position]];
|
||||
float2 uv;
|
||||
};
|
||||
|
||||
vertex VertexOut vertexMain(uint vid [[vertex_id]]) {
|
||||
VertexOut out;
|
||||
out.uv = float2((vid << 1) & 2, vid & 2);
|
||||
out.position = float4(out.uv * 2.0f + -1.0f, 0.0f, 1.0f);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
fragment float4 fragmentMain(VertexOut in [[stage_in]], texture2d<float> tex [[texture(0)]], sampler samplr [[sampler(0)]]) {
|
||||
return tex.sample(samplr, in.uv);
|
||||
}
|
|
@ -11,7 +11,8 @@
|
|||
#include "input_mappings.hpp"
|
||||
#include "services/dsp.hpp"
|
||||
|
||||
MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), keyboardMappings(InputMappings::defaultKeyboardMappings()), screen(this) {
|
||||
MainWindow::MainWindow(QApplication* app, QWidget* parent)
|
||||
: QMainWindow(parent), keyboardMappings(InputMappings::defaultKeyboardMappings()), screen(this) {
|
||||
setWindowTitle("Alber");
|
||||
// Enable drop events for loading ROMs
|
||||
setAcceptDrops(true);
|
||||
|
@ -90,6 +91,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
|||
const RendererType rendererType = emu->getConfig().rendererType;
|
||||
usingGL = (rendererType == RendererType::OpenGL || rendererType == RendererType::Software || rendererType == RendererType::Null);
|
||||
usingVk = (rendererType == RendererType::Vulkan);
|
||||
usingMtl = (rendererType == RendererType::Metal);
|
||||
|
||||
if (usingGL) {
|
||||
// Make GL context current for this thread, enable VSync
|
||||
|
@ -100,6 +102,8 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
|||
emu->initGraphicsContext(glContext);
|
||||
} else if (usingVk) {
|
||||
Helpers::panic("Vulkan on Qt is currently WIP, try the SDL frontend instead!");
|
||||
} else if (usingMtl) {
|
||||
Helpers::panic("Metal on Qt is currently WIP, try the SDL frontend instead!");
|
||||
} else {
|
||||
Helpers::panic("Unsupported graphics backend for Qt frontend!");
|
||||
}
|
||||
|
@ -264,8 +268,7 @@ void MainWindow::dumpDspFirmware() {
|
|||
case DSPService::ComponentDumpResult::Success: break;
|
||||
case DSPService::ComponentDumpResult::NotLoaded: {
|
||||
QMessageBox messageBox(
|
||||
QMessageBox::Icon::Warning, tr("No DSP firmware loaded"),
|
||||
tr("The currently loaded app has not uploaded a firmware to the DSP")
|
||||
QMessageBox::Icon::Warning, tr("No DSP firmware loaded"), tr("The currently loaded app has not uploaded a firmware to the DSP")
|
||||
);
|
||||
|
||||
QAbstractButton* button = messageBox.addButton(tr("OK"), QMessageBox::ButtonRole::YesRole);
|
||||
|
@ -559,4 +562,4 @@ void MainWindow::pollControllers() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,16 @@ FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMapp
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef PANDA3DS_ENABLE_METAL
|
||||
if (config.rendererType == RendererType::Metal) {
|
||||
window = SDL_CreateWindow("Alber", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 400, 480, SDL_WINDOW_METAL);
|
||||
|
||||
if (window == nullptr) {
|
||||
Helpers::warn("Window creation failed: %s", SDL_GetError());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
emu.initGraphicsContext(window);
|
||||
}
|
||||
|
||||
|
@ -240,7 +250,7 @@ void FrontendSDL::run() {
|
|||
case SDL_MOUSEMOTION: {
|
||||
if (emu.romType == ROMType::None) break;
|
||||
|
||||
// Handle "dragging" across the touchscreen
|
||||
// HandleType "dragging" across the touchscreen
|
||||
if (hid.isTouchScreenPressed()) {
|
||||
const s32 x = event.motion.x;
|
||||
const s32 y = event.motion.y;
|
||||
|
|
|
@ -18,7 +18,7 @@ std::optional<RendererType> Renderer::typeFromString(std::string inString) {
|
|||
{"gl", RendererType::OpenGL}, {"ogl", RendererType::OpenGL}, {"opengl", RendererType::OpenGL},
|
||||
{"vk", RendererType::Vulkan}, {"vulkan", RendererType::Vulkan}, {"vulcan", RendererType::Vulkan},
|
||||
{"sw", RendererType::Software}, {"soft", RendererType::Software}, {"software", RendererType::Software},
|
||||
{"softrast", RendererType::Software},
|
||||
{"softrast", RendererType::Software}, {"mtl", RendererType::Metal}, {"metal", RendererType::Metal}
|
||||
};
|
||||
|
||||
if (auto search = map.find(inString); search != map.end()) {
|
||||
|
@ -34,6 +34,7 @@ const char* Renderer::typeToString(RendererType rendererType) {
|
|||
case RendererType::OpenGL: return "opengl";
|
||||
case RendererType::Vulkan: return "vulkan";
|
||||
case RendererType::Software: return "software";
|
||||
case RendererType::Metal: return "metal";
|
||||
default: return "Invalid";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue