Merge remote-tracking branch 'upstream/master' into CRO

This commit is contained in:
Nomi 2023-09-07 20:06:48 +02:00
commit d50c94cbdc
48 changed files with 4278 additions and 3203 deletions

View file

@ -170,6 +170,8 @@ public:
return env.totalTicks;
}
void clearCache() { jit->ClearCache(); }
void runFrame() {
env.ticksLeft = ticksPerSec / 60;

View file

@ -24,6 +24,7 @@ enum class ROMType {
ELF,
NCSD,
CXI,
HB_3DSX,
};
class Emulator {
@ -99,6 +100,7 @@ class Emulator {
bool loadROM(const std::filesystem::path& path);
bool loadNCSD(const std::filesystem::path& path, ROMType type);
bool load3DSX(const std::filesystem::path& path);
bool loadELF(const std::filesystem::path& path);
bool loadELF(std::ifstream& file);
void initGraphicsContext();

View file

@ -251,4 +251,11 @@ public:
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
ArchiveBase(Memory& mem) : mem(mem) {}
};
struct ArchiveResource {
u32 sectorSize; // Size of a sector in bytes
u32 clusterSize; // Size of a cluster in bytes
u32 partitionCapacityInClusters;
u32 freeSpaceInClusters;
};

View file

@ -9,6 +9,7 @@ public:
u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; }
std::string name() override { return "ExtSaveData::" + backingFolder; }
HorizonResult createDirectory(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;

View file

@ -8,13 +8,16 @@ class SDMCArchive : public ArchiveBase {
public:
SDMCArchive(Memory& mem) : ArchiveBase(mem) {}
u64 getFreeBytes() override { Helpers::panic("SDMC::GetFreeBytes unimplemented"); return 0; }
u64 getFreeBytes() override { return 1_GB; }
std::string name() override { return "SDMC"; }
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
HorizonResult createDirectory(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
};

View file

@ -18,7 +18,8 @@ public:
// Returns whether the cart has a RomFS
bool hasRomFS() {
auto cxi = mem.getCXI();
return (cxi != nullptr && cxi->hasRomFS());
auto hb3dsx = mem.get3DSX();
return (cxi != nullptr && cxi->hasRomFS()) || (hb3dsx != nullptr && hb3dsx->hasRomFs());
}
// Returns whether the cart has an ExeFS (All executable carts should have an ExeFS. This is just here to be safe)

File diff suppressed because it is too large Load diff

View file

@ -137,7 +137,7 @@ namespace Helpers {
return getBits<offset, bits, ValueT, ValueT>(value);
}
#if defined(HELPERS_APPLE_CLANG) || defined(__ANDROID__)
#if defined(HELPERS_APPLE_CLANG) || defined(__ANDROID__) || !defined(__cpp_lib_bit_cast)
template <class To, class From>
constexpr To bit_cast(const From& from) noexcept {
return *reinterpret_cast<const To*>(&from);

View file

@ -83,6 +83,9 @@ private:
bool canThreadRun(const Thread& t);
bool shouldWaitOnObject(KernelObject* object);
void releaseMutex(Mutex* moo);
void cancelTimer(Timer* timer);
void signalTimer(Handle timerHandle, Timer* timer);
void updateTimer(Handle timerHandle, Timer* timer);
// Wake up the thread with the highest priority out of all threads in the waitlist
// Returns the index of the woken up thread
@ -226,4 +229,5 @@ public:
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); }
void signalDSPEvents() { serviceManager.signalDSPEvents(); }
void clearInstructionCache();
};

View file

@ -173,6 +173,19 @@ struct Semaphore {
Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {}
};
struct Timer {
u64 waitlist; // Refer to the getWaitlist function below for documentation
ResetType resetType = ResetType::OneShot;
u64 startTick; // CPU tick the timer started
u64 currentDelay; // Number of ns until the timer fires next time
u64 interval; // Number of ns until the timer fires for the second and future times
bool fired; // Has this timer been signalled?
bool running; // Is this timer running or stopped?
Timer(ResetType type) : resetType(type), startTick(0), currentDelay(0), interval(0), waitlist(0), fired(false), running(false) {}
};
struct MemoryBlock {
u32 addr = 0;
u32 size = 0;
@ -206,21 +219,23 @@ struct KernelObject {
}
// Retrieves a reference to the waitlist for a specified object
// We return a reference because this function is only called in the kernel threading internals
// We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits.
// As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient.
// Each bit corresponds to a thread index and denotes whether the corresponding thread is waiting on this object
// For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object
u64& getWaitlist() {
// This code is actually kinda trash but eh good enough
switch (type) {
case KernelObjectType::Event: return getData<Event>()->waitlist;
case KernelObjectType::Mutex: return getData<Mutex>()->waitlist;
case KernelObjectType::Semaphore: return getData<Mutex>()->waitlist;
case KernelObjectType::Thread: return getData<Thread>()->threadsWaitingForTermination;
// This should be unreachable once we fully implement sync objects
default: [[unlikely]]
// We return a reference because this function is only called in the kernel threading internals
// We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits.
// As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient.
// Each bit corresponds to a thread index and denotes whether the corresponding thread is waiting on this object
// For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object
u64& getWaitlist() {
// This code is actually kinda trash but eh good enough
switch (type) {
case KernelObjectType::Event: return getData<Event>()->waitlist;
case KernelObjectType::Mutex: return getData<Mutex>()->waitlist;
case KernelObjectType::Semaphore: return getData<Mutex>()->waitlist;
case KernelObjectType::Thread: return getData<Thread>()->threadsWaitingForTermination;
case KernelObjectType::Timer: return getData<Timer>()->waitlist;
// This should be unreachable once we fully implement sync objects
default: [[unlikely]]
Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName());
}
}
}
}
};

80
include/loader/3dsx.hpp Normal file
View file

@ -0,0 +1,80 @@
#pragma once
#include <array>
#include "helpers.hpp"
#include "io_file.hpp"
#include "loader/ncch.hpp"
struct HB3DSX {
// File layout:
// - File header
// - Code, rodata and data relocation table headers
// - Code segment
// - Rodata segment
// - Loadable (non-BSS) part of the data segment
// - Code relocation table
// - Rodata relocation table
// - Data relocation table
// Memory layout before relocations are applied:
// [0..codeSegSize) -> code segment
// [codeSegSize..rodataSegSize) -> rodata segment
// [rodataSegSize..dataSegSize) -> data segment
// Memory layout after relocations are applied: well, however the loader sets it up :)
// The entrypoint is always the start of the code segment.
// The BSS section must be cleared manually by the application.
// File header
struct Header {
// minus char magic[4]
u16 headerSize;
u16 relocHeaderSize;
u32 formatVer;
u32 flags;
// Sizes of the code, rodata and data segments +
// size of the BSS section (uninitialized latter half of the data segment)
u32 codeSegSize, rodataSegSize, dataSegSize, bssSize;
};
// Relocation header: all fields (even extra unknown fields) are guaranteed to be relocation counts.
struct RelocHeader {
u32 absoluteCount; // # of absolute relocations (that is, fix address to post-relocation memory layout)
u32 relativeCount; // # of cross-segment relative relocations (that is, 32bit signed offsets that need to be patched)
// more?
// Relocations are written in this order:
// - Absolute relocs
// - Relative relocs
};
enum class RelocType {
Absolute,
Relative,
};
// Relocation entry: from the current pointer, skip X words and patch Y words
struct Reloc {
u16 skip, patch;
};
// _prm structure
static constexpr std::array<char, 4> PRM_MAGIC = {'_', 'P', 'R', 'M'};
struct PrmStruct {
char magic[4];
u32 pSrvOverride;
u32 aptAppId;
u32 heapSize, linearHeapSize;
u32 pArgList;
u32 runFlags;
};
IOFile file;
static constexpr u32 entrypoint = 0x00100000; // Initial ARM11 PC
u32 romFSSize = 0;
u32 romFSOffset = 0;
bool hasRomFs() const;
std::pair<bool, std::size_t> readRomFSBytes(void *dst, std::size_t offset, std::size_t size);
};

View file

@ -11,6 +11,7 @@
#include "handles.hpp"
#include "helpers.hpp"
#include "loader/ncsd.hpp"
#include "loader/3dsx.hpp"
#include "services/region_codes.hpp"
namespace PhysicalAddrs {
@ -167,10 +168,12 @@ public:
void* getReadPointer(u32 address);
void* getWritePointer(u32 address);
std::optional<u32> loadELF(std::ifstream& file);
std::optional<u32> load3DSX(const std::filesystem::path& path);
std::optional<NCSD> loadNCSD(Crypto::AESEngine& aesEngine, const std::filesystem::path& path);
std::optional<NCSD> loadCXI(Crypto::AESEngine& aesEngine, const std::filesystem::path& path);
bool mapCXI(NCSD& ncsd, NCCH& cxi);
bool map3DSX(HB3DSX& hb3dsx, const HB3DSX::Header& header);
u8 read8(u32 vaddr);
u16 read16(u32 vaddr);
@ -221,6 +224,14 @@ public:
}
}
HB3DSX* get3DSX() {
if (loaded3DSX.has_value()) {
return &loaded3DSX.value();
} else {
return nullptr;
}
}
// Returns whether "addr" is aligned to a page (4096 byte) boundary
static constexpr bool isAligned(u32 addr) {
return (addr & pageMask) == 0;
@ -256,6 +267,7 @@ public:
// Backup of the game's CXI partition info, if any
std::optional<NCCH> loadedCXI = std::nullopt;
std::optional<HB3DSX> loaded3DSX = std::nullopt;
// File handle for reading the loaded ncch
IOFile CXIFile;

View file

@ -25,6 +25,9 @@ class CAMService {
void driverInitialize(u32 messagePointer);
void getMaxLines(u32 messagePointer);
void getBufferErrorInterruptEvent(u32 messagePointer);
void setContrast(u32 messagePointer);
void setFrameRate(u32 messagePointer);
void setTransferLines(u32 messagePointer);
public:
CAMService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}

View file

@ -1,4 +1,5 @@
#pragma once
#include "config.hpp"
#include "fs/archive_ext_save_data.hpp"
#include "fs/archive_ncch.hpp"
#include "fs/archive_save_data.hpp"
@ -9,7 +10,6 @@
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
// Yay, more circular dependencies
class Kernel;
@ -40,7 +40,10 @@ class FSService {
std::optional<Handle> openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms);
FSPath readPath(u32 type, u32 pointer, u32 size);
const EmulatorConfig& config;
// Service commands
void abnegateAccessRight(u32 messagePointer);
void createDirectory(u32 messagePointer);
void createExtSaveData(u32 messagePointer);
void createFile(u32 messagePointer);
@ -50,12 +53,12 @@ class FSService {
void deleteFile(u32 messagePointer);
void formatSaveData(u32 messagePointer);
void formatThisUserSaveData(u32 messagePointer);
void getArchiveResource(u32 messagePointer);
void getFreeBytes(u32 messagePointer);
void getFormatInfo(u32 messagePointer);
void getPriority(u32 messagePointer);
void getThisSaveDataSecureValue(u32 messagePointer);
void theGameboyVCFunction(u32 messagePointer);
void initialize(u32 messagePointer);
void initializeWithSdkVersion(u32 messagePointer);
void isSdmcDetected(u32 messagePointer);
@ -64,15 +67,17 @@ class FSService {
void openDirectory(u32 messagePointer);
void openFile(u32 messagePointer);
void openFileDirectly(u32 messagePointer);
void setArchivePriority(u32 messagePointer);
void setPriority(u32 messagePointer);
void setThisSaveDataSecureValue(u32 messagePointer);
// Used for set/get priority: Not sure what sort of priority this is referring to
u32 priority;
public:
FSService(Memory& mem, Kernel& kernel)
FSService(Memory& mem, Kernel& kernel, const EmulatorConfig& config)
: mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem), selfNcch(mem),
ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1), userSaveData2(mem, ArchiveID::UserSaveData2), kernel(kernel) {}
ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1), userSaveData2(mem, ArchiveID::UserSaveData2), kernel(kernel), config(config) {}
void reset();
void handleSyncRequest(u32 messagePointer);

View file

@ -12,7 +12,9 @@ class HTTPService {
bool initialized = false;
// Service commands
void createRootCertChain(u32 messagePointer);
void initialize(u32 messagePointer);
void rootCertChainAddDefaultCert(u32 messagePointer);
public:
HTTPService(Memory& mem) : mem(mem) {}

View file

@ -17,6 +17,7 @@ class MICService {
// Service commands
void getEventHandle(u32 messagePointer);
void getGain(u32 messagePointer);
void getPower(u32 messagePointer);
void isSampling(u32 messagePointer);
void mapSharedMem(u32 messagePointer);
void setClamp(u32 messagePointer);

View file

@ -16,6 +16,7 @@ class PTMService {
// Service commands
void configureNew3DSCPU(u32 messagePointer);
void getAdapterState(u32 messagePointer);
void getBatteryChargeState(u32 messagePointer);
void getBatteryLevel(u32 messagePointer);
void getStepHistory(u32 messagePointer);
void getTotalStepCount(u32 messagePointer);

View file

@ -84,6 +84,7 @@ class ServiceManager {
void receiveNotification(u32 messagePointer);
void registerClient(u32 messagePointer);
void subscribe(u32 messagePointer);
void unsubscribe(u32 messagePointer);
public:
ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config);

View file

@ -64,10 +64,14 @@ class Y2RService {
// Service commands
void driverInitialize(u32 messagePointer);
void driverFinalize(u32 messagePointer);
void getTransferEndEvent(u32 messagePointer);
void getBlockAlignment(u32 messagePointer);
void getInputLines(u32 messagePointer);
void getInputLineWidth(u32 messagePointer);
void getOutputFormat(u32 messagePointer);
void isBusyConversion(u32 messagePointer);
void pingProcess(u32 messagePointer);
void setTransferEndInterrupt(u32 messagePointer);
void getTransferEndEvent(u32 messagePointer);
void setAlpha(u32 messagePointer);
void setBlockAlignment(u32 messagePointer);