Merge pull request #79 from wheremyfoodat/stacc-the-raccs

Better ROM management sorta, implement NCSD/CXI main thread stack properly
This commit is contained in:
wheremyfoodat 2023-07-07 15:36:13 +03:00 committed by GitHub
commit 4d3625b7bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 76 additions and 15 deletions

View file

@ -5,6 +5,7 @@
#include <filesystem>
#include <fstream>
#include <optional>
#include "PICA/gpu.hpp"
#include "cpu.hpp"
@ -25,7 +26,7 @@ class Emulator {
GLStateManager gl;
SDL_Window* window;
SDL_GLContext glContext;
SDL_GameController* gameController;
SDL_GameController* gameController = nullptr;
int gameControllerID;
// Variables to keep track of whether the user is controlling the 3DS analog stick with their keyboard
@ -44,6 +45,8 @@ class Emulator {
std::ifstream loadedELF;
NCSD loadedNCSD;
std::optional<std::filesystem::path> romPath = std::nullopt;
public:
Emulator();

View file

@ -29,7 +29,6 @@ namespace VirtualAddrs {
// Stack for main ARM11 thread.
// Typically 0x4000 bytes, determined by exheader
StackTop = 0x10000000,
StackBottom = 0x0FFFC000,
DefaultStackSize = 0x4000,
NormalHeapStart = 0x08000000,
@ -248,4 +247,5 @@ public:
u32 getUsedUserMem() { return usedUserMemory; }
void setVRAM(u8* pointer) { vram = pointer; }
bool allocateMainThreadStack(u32 size);
};

View file

@ -14,6 +14,13 @@ std::optional<u32> Memory::loadELF(std::ifstream& file) {
return std::nullopt;
}
// Allocate stack space. For ELFs we use the default stack size, which is 16KB
if (!allocateMainThreadStack(VirtualAddrs::DefaultStackSize)) {
// Should be unreachable
printf("Failed to allocate stack space for ELF file\n");
return std::nullopt;
}
auto segNum = reader.segments.size();
printf("Number of segments: %d\n", segNum);
printf(" # Perms Vaddr File Size Mem Size\n");

View file

@ -9,6 +9,27 @@ bool Memory::mapCXI(NCSD& ncsd, NCCH& cxi) {
printf("Text address = %08X, size = %08X\n", cxi.text.address, cxi.text.size);
printf("Rodata address = %08X, size = %08X\n", cxi.rodata.address, cxi.rodata.size);
printf("Data address = %08X, size = %08X\n", cxi.data.address, cxi.data.size);
printf("Stack size: %08X\n", cxi.stackSize);
if (!isAligned(cxi.stackSize)) {
Helpers::warn("CXI has a suspicious stack size of %08X which is not a multiple of 4KB", cxi.stackSize);
}
// Round up the size of the CXI stack size to a page (4KB) boundary, as the OS can only allocate memory this way
u32 stackSize = (cxi.stackSize + pageSize - 1) & -pageSize;
if (stackSize > 512_KB) {
// TODO: Figure out the actual max stack size
Helpers::warn("CXI stack size is %08X which seems way too big. Clamping to 512KB", stackSize);
stackSize = 512_KB;
}
// Allocate stack
if (!allocateMainThreadStack(stackSize)) {
// Should be unreachable
printf("Failed to allocate stack for CXI partition. Requested stack size: %08X\n", stackSize);
return false;
}
// Map code file to memory
auto& code = cxi.codeFile;

View file

@ -28,16 +28,16 @@ void Memory::reset() {
writeTable[i] = 0;
}
// Map stack pages as R/W
// We have 16KB for the stack, so we allocate the last 16KB of APPLICATION FCRAM for the stack
u32 basePaddrForStack = FCRAM_APPLICATION_SIZE - VirtualAddrs::DefaultStackSize;
allocateMemory(VirtualAddrs::StackBottom, basePaddrForStack, VirtualAddrs::DefaultStackSize, true);
// Map (32 * 4) KB of FCRAM before the stack for the TLS of each thread
std::optional<u32> tlsBaseOpt = findPaddr(32 * 4_KB);
if (!tlsBaseOpt.has_value()) { // Should be unreachable but still good to have
Helpers::panic("Failed to allocate memory for thread-local storage");
}
// And map (4 * 32)KB of FCRAM before the stack for the TLS of each thread
u32 basePaddrForTLS = basePaddrForStack;
u32 basePaddrForTLS = tlsBaseOpt.value();
for (int i = 0; i < appResourceLimits.maxThreads; i++) {
u32 vaddr = VirtualAddrs::TLSBase + i * VirtualAddrs::TLSSize;
basePaddrForTLS -= VirtualAddrs::TLSSize;
basePaddrForTLS += VirtualAddrs::TLSSize;
allocateMemory(vaddr, basePaddrForTLS, VirtualAddrs::TLSSize, true);
}
@ -59,6 +59,18 @@ void Memory::reset() {
}
}
bool Memory::allocateMainThreadStack(u32 size) {
// Map stack pages as R/W
std::optional<u32> basePaddr = findPaddr(size);
if (!basePaddr.has_value()) { // Should also be unreachable but still good to have
return false;
}
const u32 stackBottom = VirtualAddrs::StackTop - size;
std::optional<u32> result = allocateMemory(stackBottom, basePaddr.value(), size, true); // Should never be nullopt
return result.has_value();
}
u8 Memory::read8(u32 vaddr) {
const u32 page = vaddr >> pageShift;
const u32 offset = vaddr & pageMask;

View file

@ -63,8 +63,16 @@ void Emulator::reset() {
// Reloading r13 and r15 needs to happen after everything has been reset
// Otherwise resetting the kernel or cpu might nuke them
cpu.setReg(13, VirtualAddrs::StackTop); // Set initial SP
if (romType == ROMType::ELF) { // Reload ELF if we're using one
loadELF(loadedELF);
// If a ROM is active and we reset, reload it. This is necessary to set up stack, executable memory, .data/.rodata/.bss all over again
if (romType != ROMType::None && romPath.has_value()) {
bool success = loadROM(romPath.value());
if (!success) {
romType = ROMType::None;
romPath = std::nullopt;
Helpers::panic("Failed to reload ROM. This should pause the emulator in the future GUI");
}
}
}
@ -284,17 +292,27 @@ bool Emulator::loadROM(const std::filesystem::path& path) {
kernel.initializeFS();
auto extension = path.extension();
bool success; // Tracks if we loaded the ROM successfully
if (extension == ".elf" || extension == ".axf")
return loadELF(path);
success = loadELF(path);
else if (extension == ".3ds")
return loadNCSD(path, ROMType::NCSD);
success = loadNCSD(path, ROMType::NCSD);
else if (extension == ".cxi" || extension == ".app")
return loadNCSD(path, ROMType::CXI);
success = loadNCSD(path, ROMType::CXI);
else {
printf("Unknown file type\n");
return false;
success = false;
}
if (success) {
romPath = path;
} else {
romPath = std::nullopt;
romType = ROMType::None;
}
return success;
}
// Used for loading both CXI and NCSD files since they are both so similar and use the same interface