#include #include "../internal.hpp" #include "dolphin/os.h" static aurora::Module Log("aurora::ar"); static u32 AR_StackPointer; static u32* AR_BlockLength; static u32 AR_FreeBlocks; static BOOL AR_init_flag; #define ARAM_STACK_START 0x4000; // ARAM emulation: allocate a large buffer to simulate the GameCube's Auxiliary RAM. // ARAM "addresses" are offsets into this buffer. On GameCube, ARAM is 16 MB starting // at a base address returned by ARInit. We emulate this by malloc'ing a buffer // and using a simple bump allocator (matching ARAlloc behavior on real hardware). static u8* sAramBuffer = nullptr; // Convert an ARAM "address" (offset) to a real host pointer static u8* aramToHost(u32 aramAddr) { if (!sAramBuffer || aramAddr >= aurora::g_config.mem2Size) { return nullptr; } return sAramBuffer + aramAddr; } u32 ARAlloc(u32 length) { u32 tmp; ASSERTMSGLINE(430, !(length & 0x1F), "ARAlloc(): length is not multiple of 32bytes!"); ASSERTMSGLINE(434, length <= (__AR_Size - __AR_StackPointer), "ARAlloc(): Out of ARAM!"); ASSERTMSGLINE(435, __AR_FreeBlocks, "ARAlloc(): No more free blocks!"); tmp = AR_StackPointer; AR_StackPointer += length; *AR_BlockLength = length; AR_BlockLength += 1; AR_FreeBlocks -= 1; return tmp; } u32 ARFree(u32* length) { AR_BlockLength -= 1; if (length) { *length = *AR_BlockLength; } AR_StackPointer -= *AR_BlockLength; AR_FreeBlocks += 1; return AR_StackPointer; } BOOL ARCheckInit(void) { return AR_init_flag; } u32 ARInit(u32* stack_index_addr, u32 num_entries) { if (aurora::g_config.mem2Size == 0) { Log.warn("ARInit called but no mem2Size specified in AuroraConfig. ARAM will not be available!"); return 0; } if (AR_init_flag == TRUE) { return ARAM_STACK_START; } sAramBuffer = (u8*)calloc(1, aurora::g_config.mem2Size); if (sAramBuffer) { Log.debug("Initialized 0x{:X} bytes of ARAM!", aurora::g_config.mem2Size); } else { Log.fatal("Failed to allocate ARAM!"); } AR_StackPointer = ARAM_STACK_START; AR_FreeBlocks = num_entries; AR_BlockLength = stack_index_addr; AR_init_flag = TRUE; return AR_StackPointer; } u32 ARGetSize(void) { return aurora::g_config.mem2Size; } #pragma mark ARQ void ARQPostRequest(ARQRequest* request, u32 owner, u32 type, u32 priority, uintptr_t source, uintptr_t dest, u32 length, ARQCallback callback) { // Emulate ARAM DMA transfers using memcpy. // type 0 = MRAM -> ARAM, type 1 = ARAM -> MRAM if (type == ARAM_DIR_MRAM_TO_ARAM) { // Main RAM -> ARAM: source is a host pointer (cast to u32), dest is an ARAM offset u8* hostSrc = (u8*)(uintptr_t)source; u8* aramDst = aramToHost(dest); if (aramDst && hostSrc) { memcpy(aramDst, hostSrc, length); } } else { // ARAM -> Main RAM: source is an ARAM offset, dest is a host pointer (cast to u32) u8* aramSrc = aramToHost(source); u8* hostDst = (u8*)(uintptr_t)dest; if (aramSrc && hostDst) { memcpy(hostDst, aramSrc, length); } } // Immediately invoke the callback (synchronous on PC, no DMA latency) if (callback) { callback((uintptr_t)request); } } void ARQInit() { // Nothing to do on PC - ARAM is initialized in ARInit }