diff --git a/include/functions.h b/include/functions.h index fae8153..d5a9b9d 100644 --- a/include/functions.h +++ b/include/functions.h @@ -9,9 +9,6 @@ extern s32 func_8005A990(OSPiHandle *); // bcopy.s extern void _bcopy(void *, void *, u32); -// 3640.s -extern void* func_80002AF8(s32, void*); - // 3A80.c extern uintptr_t convert_addr_to_virt_addr(uintptr_t addr); extern void func_80002F58(void); diff --git a/include/variables.h b/include/variables.h index a4a9ccc..ed741b3 100644 --- a/include/variables.h +++ b/include/variables.h @@ -3,4 +3,14 @@ #include "ultra64.h" +#define POOL_END_4MB 0x80400000 +#define POOL_END_6MB 0x80600000 + +/* + * Dynamic heap with an indetermate amount of space. This pool can either end at 4MB or + * 6MB depending on osMemSize, which is really strange as it should be using the whole + * 8MB of the expansion pak. + */ +extern u8 gPool[1]; + #endif diff --git a/splat.yaml b/splat.yaml index e2346ef..2505858 100644 --- a/splat.yaml +++ b/splat.yaml @@ -36,8 +36,8 @@ segments: - [0x1F70, bin, noppad_1F70] # Extra nop align like earlier. Weird - [0x1F80, c, dp_intro] # dp intro code - [0x28E0, c, memmap] - - [0x2EC0, c, memory] - - [0x3640, asm] # + - [0x2EC0, c, memory_main] # handles the main global pool + - [0x3640, c, memory] # memory_pool - [0x3A80, c] - [0x3FB0, asm] # PRES-JPEG decoder - [0x5580, asm] # there's a split here according to PAL @@ -325,6 +325,8 @@ segments: - [0x68020, bin] # rest of rom part 1 # .data is somewhere in here + - [0x69790, .data, 3A80] + - [0x697A0, bin, rom_data_697A0] - [0x6A1B0, .data, crash_screen] - [0x6A3B0, .data, profiler] - [0x6A3D0, .data, gb_tower] @@ -416,7 +418,7 @@ segments: - {vram: 0x8007F190, type: .bss, name: main} - {vram: 0x80081900, type: .bss, name: rsp} - {vram: 0x80083CA0, type: .bss, name: unk_bss} - - {vram: 0x800A6070, type: .bss, name: memory} + - {vram: 0x800A6070, type: .bss, name: memory_main} - {vram: 0x800A60B0, type: .bss, name: unk_bss_4} - {vram: 0x800A7320, type: .bss, name: controller} - {vram: 0x800A7420, type: .bss, name: unk_bss_3} @@ -429,6 +431,16 @@ segments: - {vram: 0x80103950, type: .bss, name: libultra/io/vimgr} - {vram: 0x80104b70, type: .bss, name: libultra/io/conteepread} - [0x7F980] + + - name: heap + type: code + bss_size: 0x0 # This heap extends to the end of RAM. + start: 0x7F980 + vram: 0x80104BB0 + subsegments: + # .bss + - {vram: 0x80104BB0, type: .bss, name: heap} + - [0x7F980] # Probably the GB Tower Emulator. This fragment is built very strangely. - name: fragment1 diff --git a/src/3A80.c b/src/3A80.c index 7c77042..9dcac6d 100644 --- a/src/3A80.c +++ b/src/3A80.c @@ -2,16 +2,21 @@ #include "memmap.h" #include "memory.h" -extern s32 D_80068B90; -extern u8 D_80104BB0[]; -extern s32 D_800A60B0; +/** + * Based on the context of this variable's usage, it appears to be intended to + * be non-0 whenever the expansion RAM is in, however it is never initialized + * properly. It seems to serve as whereever the new start of memory will be + * located whenever plugged in, but its only functional with the debug profiler. + * It will, however, allocate a much bigger pool if set to non-0. + */ +s32 gExpansionRAMStart = FALSE; + +extern struct MainPool **gMainPool; // gMainPool void func_80003860(void); -s32 func_80002A40(s32, s32); -void func_80002BD0(s32, void *); // type unknown s32 func_80007A58(void); -/* +/** * Convert any valid address to its virtual (KSEG0) counterpart. */ uintptr_t convert_addr_to_virt_addr(uintptr_t addr) { @@ -45,22 +50,24 @@ void HAL_Memcpy(u32* dest, u32* src, int size) { void func_80002F58(void) { // wat? mem sizes are only ever 0x400000 or 0x800000. This check makes no sense. - if ((D_80068B90 != 0) && ((u32) osMemSize > 0x600000U)) { - main_pool_init(&D_80104BB0, 0x80600000); + // Effectively, it checks if the expansion RAM is in. But why not just use all + // of it, or at least do the correct check of osMemSize == 0x800000? + if ((gExpansionRAMStart != 0) && ((u32) osMemSize > 0x600000U)) { + main_pool_init(&gPool, POOL_END_6MB); } else { - main_pool_init(&D_80104BB0, 0x80400000); - D_80068B90 = 0; + main_pool_init(&gPool, POOL_END_4MB); + gExpansionRAMStart = 0; } func_80003860(); - D_800A60B0 = func_80002A40(0x10000, 0); + gMainPool = mem_pool_try_init(0x10000, 0); } -void *func_80002FDC(s32 arg0) { - return func_80002AF8(D_800A60B0, arg0); +void *func_80002FDC(s32 size) { + return mem_pool_alloc(gMainPool, size); } void func_80003004(void *arg0) { - func_80002BD0(D_800A60B0, arg0); + mem_pool_free(gMainPool, arg0); } void HAL_DrawRect(Gfx** dlist, s32 ulx, s32 lrx, u16 color) { @@ -83,15 +90,15 @@ void HAL_DrawRect(Gfx** dlist, s32 ulx, s32 lrx, u16 color) { void func_8000310C(Gfx** dlist) { struct MainPool *pool = main_pool_get_pool(); - s32 temp_s1 = main_pool_get_available() - D_80068B90; + s32 temp_s1 = main_pool_get_available() - gExpansionRAMStart; if (temp_s1 >= 0) { s32 base = 30; s32 sp48 = ((u32) ( K0_TO_PHYS(pool->start)) >> 15) + base; s32 sp44 = ((u32) ( K0_TO_PHYS(pool->listHeadL)) >> 15) + base; - s32 sp40 = ((u32) ( K0_TO_PHYS(pool->listHeadR) - D_80068B90) >> 15) + base; - s32 sp3C = ((u32) ( K0_TO_PHYS(pool->end) - D_80068B90) >> 15) + base; + s32 sp40 = ((u32) ( K0_TO_PHYS(pool->listHeadR) - gExpansionRAMStart) >> 15) + base; + s32 sp3C = ((u32) ( K0_TO_PHYS(pool->end) - gExpansionRAMStart) >> 15) + base; HAL_DrawRect(dlist, base, sp48, 0xFBCB); HAL_DrawRect(dlist, sp48, sp44, 0xFFCB); @@ -104,8 +111,8 @@ void func_8000310C(Gfx** dlist) { s32 base = 30; s32 sp34 = ((u32) ( K0_TO_PHYS(pool->start)) >> 15) + base; s32 sp30 = ((u32) ( K0_TO_PHYS(pool->listHeadL)) >> 15) + base; - s32 sp2C = ((u32) ( K0_TO_PHYS(pool->listHeadR) - D_80068B90) >> 15) + base; - s32 sp28 = ((u32) ( K0_TO_PHYS(pool->end) - D_80068B90) >> 15) + base; + s32 sp2C = ((u32) ( K0_TO_PHYS(pool->listHeadR) - gExpansionRAMStart) >> 15) + base; + s32 sp28 = ((u32) ( K0_TO_PHYS(pool->end) - gExpansionRAMStart) >> 15) + base; HAL_DrawRect(dlist, base, sp34, 0xFBCB); HAL_DrawRect(dlist, sp34, sp2C, 0xFFCB); HAL_DrawRect(dlist, sp2C, sp30, 0xF94B); diff --git a/src/heap.c b/src/heap.c new file mode 100644 index 0000000..8a69878 --- /dev/null +++ b/src/heap.c @@ -0,0 +1,3 @@ +#include + +u8 gPool[1]; // unk determinate size. diff --git a/src/libultra/io/conteepread.c b/src/libultra/io/conteepread.c index 307821a..b42b0b2 100644 --- a/src/libultra/io/conteepread.c +++ b/src/libultra/io/conteepread.c @@ -5,7 +5,6 @@ void __osPackEepReadData(u8 address); OSPifRam __osEepPifRam; -s32 __osEepromRead16K; #define CONT_RANGE_ERROR -1 @@ -37,12 +36,14 @@ s32 osEepromRead(OSMesgQueue *mq, u8 address, u8 *buffer) // @bug: Should be > EEP16K_MAXBLOCKS if (address >= EEP16K_MAXBLOCKS) { ret = -1; - } + } else { + // __osEepromRead16K support seems to have been removed from this particular + // revision. + //__osEepromRead16K = -1; + } break; default: - if (1); ret = CONT_NO_RESPONSE_ERROR; - break; } } diff --git a/src/memory.c b/src/memory.c index b5d088a..c2c0503 100644 --- a/src/memory.c +++ b/src/memory.c @@ -1,373 +1,194 @@ #include -#include #include "memory.h" -static struct MainPool sMemPool; +// seems to allocate a pool area, and init it's MainPool struct? +// the other file is memory_main.c but this one is memory.c? /** - * Initialize the main memory pool. This pool is conceptually a pair of stacks - * that grow inward from the left and right. It therefore only supports - * freeing the object that was most recently allocated from a side. + * Allocate a memory pool from the main pool. This pool supports arbitrary + * order for allocation/freeing. + * Return NULL if there is not enough space in the main pool. */ -void main_pool_init(void *start, void *end) { - sMemPool.start = (void *)(ALIGN16((uintptr_t)start) + 16); - sMemPool.end = (void *)(ALIGN16((uintptr_t)end - 15) - 16); - sMemPool.available = (uintptr_t)sMemPool.end - (uintptr_t)sMemPool.start; - sMemPool.mainState = NULL; - - sMemPool.listHeadL = ((u8*)sMemPool.start - sizeof(struct MainPoolBlock)); - sMemPool.listHeadL->prev = NULL; - sMemPool.listHeadL->next = NULL; - sMemPool.listHeadL->func = NULL; - sMemPool.listHeadL->arg = 0; - - sMemPool.listHeadR = sMemPool.end; - sMemPool.listHeadR->prev = NULL; - sMemPool.listHeadR->next = NULL; - sMemPool.listHeadL->func = NULL; - sMemPool.listHeadL->arg = 0; - - osCreateMesgQueue(&sMemPool.queue, sMemPool.msgs, ARRAY_COUNT(sMemPool.msgs)); - osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); +struct MemoryPool *mem_pool_try_init(u32 size, s32 side) { + struct MainPoolBlock *block; + struct MemoryPool *ret; + + size = ALIGN4(size); + block = main_pool_alloc_node_no_func(size, side); + if (block != NULL) { + ret = mem_pool_init(block, size); + } + return ret; //! UB: Uninitialized return if the earlier call is not done. } /** - * Allocate a block of memory from the pool of given size, and from the - * specified side of the pool (MEMORY_POOL_LEFT or MEMORY_POOL_RIGHT). - * If there is not enough space, return NULL. + * Perform the actual mem pool init. This is called by the earlier function. + * In earlier EAD titles, this was part of mem_pool_try_init. */ -struct MainPoolBlock *main_pool_alloc(u32 size, u32 side) { - struct MainPoolBlock *newListHead; - void *addr = NULL; - - size = ALIGN16(size) + sizeof(struct MainPoolBlock); - if (size != 0 && sMemPool.available >= size) { - if (side == MEMORY_POOL_LEFT) { - sMemPool.available -= size; - newListHead = (void *)((uintptr_t)sMemPool.listHeadL + size); - sMemPool.listHeadL->next = newListHead; - newListHead->prev = sMemPool.listHeadL; - newListHead->next = NULL; - newListHead->func = 0; - newListHead->arg = 0; - addr = ((u8*)sMemPool.listHeadL + sizeof(struct MainPoolBlock)); - sMemPool.listHeadL = newListHead; - } else if (side == MEMORY_POOL_RIGHT) { - sMemPool.available -= size; - newListHead = (void *)((uintptr_t)sMemPool.listHeadR - size); - sMemPool.listHeadR->prev = newListHead; - newListHead->next = sMemPool.listHeadR; - newListHead->prev = NULL; - newListHead->func = 0; - newListHead->arg = 0; - sMemPool.listHeadR = newListHead; - addr = ((u8*)newListHead + sizeof(struct MainPoolBlock)); - } - } - return addr; +// TODO: This function is strange, it cant be using MemoryPool, as it allocates +// more variables than the MemoryPool struct, and it doesnt line up. Whats going +// on with these structs? +struct MainPool* mem_pool_init(struct MainPool *pool, s32 size) { + s32 aligned_size = ALIGN4(size - 3) - 0x28; // whats the deal with 0x28? this size doesnt match any known pool struct. + void *listHeadL = &pool->listHeadL; + + pool->available = aligned_size; + pool->start = listHeadL; + pool->end = listHeadL; + pool->listHeadL = NULL; + pool->listHeadR = aligned_size; + osCreateMesgQueue(&pool->queue, &pool->msgs[0], 1); + osSendMesg(&pool->queue, NULL, 0); + return pool; } /** - * Free a block of memory that was allocated from the pool. The block must be - * the most recently allocated block from its end of the pool, otherwise all - * newer blocks are freed as well. - * Return the amount of free space left in the pool. + * Allocate from a memory pool. Return NULL if there is not enough space. */ -u32 main_pool_free(void *addr, u32 runBlockFunc) { - struct MainPoolBlock *block = (struct MainPoolBlock *)((u8 *)addr - sizeof(struct MainPoolBlock)); - struct MainPoolBlock *oldListHead = (struct MainPoolBlock *)((u8 *)addr - sizeof(struct MainPoolBlock)); +void *mem_pool_alloc(struct MainPool *node, s32 size) { + struct MemoryBlock *freeBlock; + void *addr; - if (oldListHead < sMemPool.listHeadL) { - do { - block = (sMemPool.listHeadL = sMemPool.listHeadL->prev); - if (runBlockFunc) { - // TODO: Fakematch - void (*func)(struct MainPoolBlock *, u32) = block->func; - if (func != 0) { - block->func(block + 1, block->arg); - // TODO: fake here too - if ((!(&sMemPool)) && (!(&sMemPool))) - { - } - } + osRecvMesg(&node->queue, 0, 1); + + addr = NULL; + size = ALIGN4(size) + sizeof(struct MemoryBlock); + freeBlock = (struct MemoryBlock *)&node->end; + + while (freeBlock->next != NULL) { + if (freeBlock->next->size >= size) { + addr = (u8*)freeBlock->next + sizeof(struct MemoryBlock); //get data after header + if (freeBlock->next->size - size <= sizeof(struct MemoryBlock)) { + freeBlock->next = freeBlock->next->next; + } else { + struct MemoryBlock *newBlock = (struct MemoryBlock *)((u8 *)freeBlock->next + size); + newBlock->size = freeBlock->next->size - size; //set size + newBlock->next = freeBlock->next->next; + freeBlock->next->size = size; //set size + freeBlock->next = newBlock; } - sMemPool.available += ((uintptr_t)sMemPool.listHeadL->next - (uintptr_t)sMemPool.listHeadL); - sMemPool.listHeadL->next = NULL; - } while (oldListHead != sMemPool.listHeadL); - } else { - block = sMemPool.listHeadR; - if (oldListHead >= block && oldListHead >= block) { - do { - if (runBlockFunc) { - void (*func)(struct MainPoolBlock *, u32) = block->func; - if (func != NULL) { - func(block + 1, block->arg); - block = sMemPool.listHeadR; - } - } - block = (sMemPool.listHeadR = block->next); - sMemPool.available += ((uintptr_t)block - (uintptr_t)block->prev); - block->prev = NULL; - block = sMemPool.listHeadR; - } while (oldListHead >= sMemPool.listHeadR); + break; } + freeBlock = freeBlock->next; } - - return main_pool_get_available(); + osSendMesg(&node->queue, 0, 0); + return addr; } /** - * Manually allocate and initialize a block given a size and side and its - * function+arguments. + * Free a block that was allocated using mem_pool_alloc. */ -struct MainPoolBlock *main_pool_alloc_node(u32 size, s32 side, s32 arg, void *func) { - struct MainPoolBlock *node; - - osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); - node = main_pool_alloc(size, side); - if (node != NULL) { - main_pool_set_func(node, arg, func); - } - osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); +void mem_pool_free(struct MemoryPool* pool, void* addr) { + struct MemoryBlock* block; + struct MemoryBlock* freeList; - return node; -} - -/** - * Same as above but no function/argument is set. - */ -struct MainPoolBlock *main_pool_alloc_node_no_func(u32 size, s32 side) { - struct MainPoolBlock *node; - - osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); - node = main_pool_alloc(size, side); - osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); - - return node; -} - -/** - * Tries to free a block of memory that was allocated from the pool. Return - * the new available amount of the pool. - */ -u32 main_pool_try_free(struct MainPoolBlock *addr) { - if (addr != NULL) { - osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); - main_pool_free(addr, 1); - osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); - } - - return main_pool_get_available(); -} - -/** - * Resize a block of memory that was allocated from the left side of the pool. - * If the block is increasing in size, it must be the most recently allocated - * block from the left side. - * The block does not move. - */ -struct MainPoolBlock *main_pool_realloc(void *addr, size_t size) { - struct MainPoolBlock *prior = (struct MainPoolBlock *)((u8 *)addr - sizeof(struct MainPoolBlock)); - void *newaddr = NULL; - - osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); - - if (prior->next == sMemPool.listHeadL) { - size_t diff = ((uintptr_t)prior->next - (uintptr_t)addr); - size = ALIGN16(size); - if (diff >= size || sMemPool.available >= (size - diff)) { - s32 arg = prior->arg; - void *func = prior->func; - main_pool_free(addr, 0); - newaddr = main_pool_alloc(size, 0); - main_pool_set_func(newaddr, arg, func); + if (addr != NULL) { + osRecvMesg(&pool->queue, NULL, 1); + block = (struct MemoryBlock*)((u8*)addr - sizeof(struct MemoryBlock)); + freeList = pool->freeList.next; + if (pool->freeList.next == NULL) { + pool->freeList.next = block; + block->next = NULL; + } + else if (block < pool->freeList.next) { + if ((u32)pool->freeList.next == ((u32)block + (u32)block->size)) { + block->size += ((u32) freeList->size); + block->next = freeList->next; + pool->freeList.next = block; + } + else { + block->next = pool->freeList.next; + pool->freeList.next = block; + } + } + else { + while (freeList->next != NULL) { + if (freeList < block && block < freeList->next) { + break; + } + freeList = freeList->next; + } + if (((u32)freeList + (u32)freeList->size) == (u32)block) { + freeList->size += block->size; + block = freeList; + } + else { + block->next = freeList->next; + freeList->next = block; + } + if (block->next != NULL && (u32)block->next == ((u32)block + (u32)block->size)) { + block->size = (u32)block->size + (u32)block->next->size; + block->next = block->next->next; + } } + osSendMesg(&pool->queue, NULL, 0); } - osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); - return newaddr; } -/** - * Return the amount of available memory to use in the pool. - */ -u32 main_pool_get_available(void) { - s32 available = sMemPool.available - sizeof(struct MainPoolBlock); +void *func_80002D10(u32 size, s32 side) { + struct MainPoolBlock* block; + void *ptr = NULL; - if (available < 0) { - available = 0; + size = ALIGN4(size); + ptr = 0; + block = main_pool_alloc_node_no_func(size, side); + if (block != NULL) { + ptr = func_80002DA4(block, size); } - - return available; + return ptr; } -/** - * Push pool state, to be restored later. Return the amount of free space left - * in the pool. - */ -u32 main_pool_push_state(u32 arg) { - struct MainPoolState *state; - struct MainPoolBlock *listHeadL; - struct MainPoolBlock *listHeadR; - uintptr_t available; - - osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); +void func_80002D60(struct MemoryBlock* block) { + s32 size = ALIGN16(block->size + 0x10); - // retrieve the space and head pointers. - available = sMemPool.available; - listHeadL = sMemPool.listHeadL; - listHeadR = sMemPool.listHeadR; - - state = (void*)main_pool_alloc(sizeof(struct MainPoolState), 0); - if (state != NULL) { - /** - * Why is this line here? What this line is doing is backing the pointer up to the - * previous block before this one. in the block alloc function, addr is determined - * by the head plus the size of the block struct, meaning it is returning the - * pointer to the head. - */ - ((struct MainPoolBlock *)((u8*)state-sizeof(struct MainPoolBlock)))->arg = arg; - - // now that the previous block's argument is set, set the newly allocated state's - // fields. - state->prev = sMemPool.mainState; - state->freeSpace = available; - state->listHeadL = listHeadL; - state->listHeadR = listHeadR; - - // add the newly allocated state. - sMemPool.mainState = state; - } - osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); - return main_pool_get_available(); + main_pool_realloc(block, size); + block->next = (void*)(size - 0x10); } -/** - * Restore pool state from a previous call to main_pool_push_state. Return the - * amount of free space left in the pool. - */ -u32 main_pool_pop_state(u32 arg) { - struct MainPoolState *node; - struct MainPoolBlock *argptr; - void *listHeadL; - void *listHeadR; - struct MainPoolState *state; - - argptr = (u32)arg; - osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); - - do { - node = sMemPool.mainState; - listHeadL = node->listHeadL; - listHeadR = node->listHeadR; - sMemPool.available = node->freeSpace; - sMemPool.mainState = node->prev; - - // was the argument passed in 0? - if (argptr == 0) { - break; - } - - // odd reuse of the variable, but what this is doing is backing the ptr up to the - // pool block. Thus, to check the arg variable on the pool block located before the state, - // we will need to cast the next if check. - node = (void*)((u8*)node - sizeof(struct MainPoolState)); - - if ((uintptr_t)argptr == (uintptr_t)((struct MainPoolBlock *)node)->arg) { - // we found the block with the matching string! break. - break; - } - } while(sMemPool.mainState != NULL); - - argptr = sMemPool.listHeadR; - while ((uintptr_t)listHeadR > (uintptr_t)argptr) { - if (argptr->func != NULL) { - argptr->func(argptr + 1, argptr->arg); - } - argptr = argptr->next; - } - - argptr = sMemPool.listHeadL->prev; - while ((uintptr_t)listHeadL <= (uintptr_t)argptr) { - if (argptr->func != NULL) { - argptr->func(argptr + 1, argptr->arg); - } - argptr = argptr->prev; - } +void* func_80002DA4(struct MainPoolState* block, s32 size) { + void* temp_v1 = (void*)((u8*)block + 0x10); - sMemPool.listHeadL = listHeadL; - sMemPool.listHeadR = listHeadR; - osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); - return main_pool_get_available(); + block->listHeadL = 0; + block->freeSpace = ((size & ~3) - 0x10); // this doesnt match an ALIGN4 macro or whatnot + block->listHeadR = temp_v1; + block->prev = temp_v1; + return block; } -/* - * Unused function. Seems at first glance to check for an address within some - * range, and then return the pointer to its data after the block. Perhaps? - * Without this being called, its hard to tell the correct context of this - * function. - */ -void *main_pool_search(uintptr_t addr, s32 *argPtr) { - struct MainPoolBlock *node; - struct MainPoolBlock *otherNode; +s32 func_80002DCC(struct MainPoolState* state, s32 arg1, s32 arg2) { + s32 temp_a2; + s32 temp_a3; + s32 var_v0; + s32 ret = 0; - node = sMemPool.listHeadL->prev; - while (node != NULL) { - int isAddrLater = (addr >= ((uintptr_t) ((u8*)node + sizeof(struct MainPoolBlock)))); - otherNode = node->next; - // seems to be checking for an addr within a block region? Since this function - // is unused, we wont be able to check for the intended context of what could - // call this function. - if (isAddrLater && addr < ((uintptr_t)otherNode & 0xFFFFFFFF)) { - if (argPtr != NULL) { - *argPtr = node->arg; - } - // return the pointer to its block contents. - return (void*)((u8*)(node) + sizeof(struct MainPoolBlock)); - } - node = node->prev; + if (arg2 > 0) { + var_v0 = (((s32)state->prev + (s32)arg2) - 1) & ~(arg2 - 1); + } else { + var_v0 = state->prev; } - - // we've searched thr prev linked list. Now lets go through the next linked list. - node = sMemPool.listHeadR; - otherNode = node->next; - while (otherNode != NULL) { - int isAddrLater = (addr >= ((uintptr_t) ((u8*)node + sizeof(struct MainPoolBlock)))); - struct MainPoolBlock *new_var = otherNode; // bit of a fakematch to force the move reload. - // same as above. - if (isAddrLater && (addr < ((uintptr_t)new_var & 0xFFFFFFFF))) { - if (argPtr != NULL) { - *argPtr = node->arg; - } - // return the pointer to its block contents. - return (void*)((u8*)(node) + sizeof(struct MainPoolBlock)); + temp_a2 = (var_v0 - (s32)state->prev) + arg1; + if (temp_a2 > 0) { + temp_a3 = (s32)state->listHeadL + temp_a2; + if ((s32)state->freeSpace >= temp_a3) { + ret = var_v0; + state->prev = (s32) ((s32)state->prev + temp_a2); + state->listHeadL = temp_a3; } - otherNode = (node = otherNode)->next; } - - return NULL; + return ret; } -/** - * Set the block function and its argument(s) for a given block. - */ -void main_pool_set_func(void *block, s32 arg, void *func) { - struct MainPoolBlock *node = (void*)((uintptr_t)block - sizeof(struct MainPoolBlock)); - node->func = func; - node->arg = arg; +void func_80002E3C(struct MainPoolState* state, s32 size) { + if ((s32)state->freeSpace >= size) { + state->listHeadL = size; + state->prev = (void*)((s32)state->listHeadR + size); + } } -/** - * Get the distance offset from the block's state listHeadL pointer to the current block. - */ -uintptr_t main_pool_get_block_dist(struct MainPoolBlock *block) { - struct MainPoolState *state = ((u8*)block - sizeof(struct MainPoolBlock)); - - return (uintptr_t)state->listHeadL - (uintptr_t)block; -} +void func_80002E64(struct MainPoolState* state) { + void* mem = (u8*)((u32)state + sizeof(struct MainPoolState)); -/** - * Return the pointer to the static memory pool area. - */ -struct MainPool *main_pool_get_pool(void) { - return &sMemPool; + state->listHeadL = 0; + state->listHeadR = mem; + state->prev = mem; } diff --git a/src/memory.h b/src/memory.h index 73e3256..95b1705 100644 --- a/src/memory.h +++ b/src/memory.h @@ -8,8 +8,9 @@ #define MEMORY_POOL_LEFT 0 #define MEMORY_POOL_RIGHT 1 +// structs for the main pool. struct MainPoolState { - /* 0x00 */ u32 freeSpace; + /* 0x00 */ s32 freeSpace; /* 0x04 */ struct MainPoolBlock *listHeadL; /* 0x08 */ struct MainPoolBlock *listHeadR; /* 0x0C */ struct MainPoolState *prev; @@ -33,20 +34,46 @@ struct MainPool { /* 0x30 */ struct MainPoolState *mainState; }; -// functions +// structs used for the smaller pools allocated from the global pool. +struct MemoryBlock { + /* 0x00 */ struct MemoryBlock *next; + /* 0x04 */ u32 size; +}; + +struct MemoryPool { + /* 0x00 */ OSMesg msgs[1]; + /* 0x04 */ OSMesgQueue queue; + /* 0x1C */ size_t available; + /* 0x20 */ struct MemoryBlock *firstBlock; + /* 0x24 */ struct MemoryBlock freeList; +}; + void main_pool_init(void *start, void *end); -struct MainPoolBlock *main_pool_alloc(u32 size, u32 side); +void *main_pool_alloc(u32 size, u32 side); u32 main_pool_free(void *addr, u32 runBlockFunc); -struct MainPoolBlock *main_pool_alloc_node(u32 size, s32 side, s32 arg, void *func); -struct MainPoolBlock *main_pool_alloc_node_no_func(u32 size, s32 side); +void *main_pool_alloc_node(u32 size, s32 side, s32 arg, void *func); +void *main_pool_alloc_node_no_func(u32 size, s32 side); u32 main_pool_try_free(struct MainPoolBlock *addr); -struct MainPoolBlock *main_pool_realloc(void *addr, size_t size); +void *main_pool_realloc(void *addr, size_t size); u32 main_pool_get_available(void); u32 main_pool_push_state(u32 arg); u32 main_pool_pop_state(u32 arg); void *main_pool_search(uintptr_t addr, s32 *argPtr); void main_pool_set_func(void *block, s32 arg, void *func); -uintptr_t main_pool_get_block_dist(struct MainPoolBlock *block); +size_t main_pool_get_block_dist(struct MainPoolBlock *block); struct MainPool *main_pool_get_pool(void); +// 3640.s +struct MemoryPool *mem_pool_try_init(u32 size, s32 side); +struct MainPool* mem_pool_init(struct MainPool *pool, s32 size); +void *mem_pool_alloc(struct MainPool *node, s32 size); + +void mem_pool_free(struct MemoryPool* pool, void* addr); +void *func_80002D10(u32 size, s32 side); +void func_80002D60(struct MemoryBlock* block); +void* func_80002DA4(struct MainPoolState* block, s32 size); +s32 func_80002DCC(struct MainPoolState* state, s32 arg1, s32 arg2); +void func_80002E3C(struct MainPoolState* state, s32 size); +void func_80002E64(struct MainPoolState* state); + #endif /* _MEMORY_H_ */ diff --git a/src/memory_main.c b/src/memory_main.c new file mode 100644 index 0000000..7790ee1 --- /dev/null +++ b/src/memory_main.c @@ -0,0 +1,373 @@ +#include +#include +#include "memory.h" + +static struct MainPool sMemPool; + +/** + * Initialize the main memory pool. This pool is conceptually a pair of stacks + * that grow inward from the left and right. It therefore only supports + * freeing the object that was most recently allocated from a side. + */ +void main_pool_init(void *start, void *end) { + sMemPool.start = (void *)(ALIGN16((uintptr_t)start) + 16); + sMemPool.end = (void *)(ALIGN16((uintptr_t)end - 15) - 16); + sMemPool.available = (uintptr_t)sMemPool.end - (uintptr_t)sMemPool.start; + sMemPool.mainState = NULL; + + sMemPool.listHeadL = ((u8*)sMemPool.start - sizeof(struct MainPoolBlock)); + sMemPool.listHeadL->prev = NULL; + sMemPool.listHeadL->next = NULL; + sMemPool.listHeadL->func = NULL; + sMemPool.listHeadL->arg = 0; + + sMemPool.listHeadR = sMemPool.end; + sMemPool.listHeadR->prev = NULL; + sMemPool.listHeadR->next = NULL; + sMemPool.listHeadL->func = NULL; + sMemPool.listHeadL->arg = 0; + + osCreateMesgQueue(&sMemPool.queue, sMemPool.msgs, ARRAY_COUNT(sMemPool.msgs)); + osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); +} + +/** + * Allocate a block of memory from the pool of given size, and from the + * specified side of the pool (MEMORY_POOL_LEFT or MEMORY_POOL_RIGHT). + * If there is not enough space, return NULL. + */ +void *main_pool_alloc(u32 size, u32 side) { + struct MainPoolBlock *newListHead; + void *addr = NULL; + + size = ALIGN16(size) + sizeof(struct MainPoolBlock); + if (size != 0 && sMemPool.available >= size) { + if (side == MEMORY_POOL_LEFT) { + sMemPool.available -= size; + newListHead = (void *)((uintptr_t)sMemPool.listHeadL + size); + sMemPool.listHeadL->next = newListHead; + newListHead->prev = sMemPool.listHeadL; + newListHead->next = NULL; + newListHead->func = 0; + newListHead->arg = 0; + addr = ((u8*)sMemPool.listHeadL + sizeof(struct MainPoolBlock)); + sMemPool.listHeadL = newListHead; + } else if (side == MEMORY_POOL_RIGHT) { + sMemPool.available -= size; + newListHead = (void *)((uintptr_t)sMemPool.listHeadR - size); + sMemPool.listHeadR->prev = newListHead; + newListHead->next = sMemPool.listHeadR; + newListHead->prev = NULL; + newListHead->func = 0; + newListHead->arg = 0; + sMemPool.listHeadR = newListHead; + addr = ((u8*)newListHead + sizeof(struct MainPoolBlock)); + } + } + return addr; +} + +/** + * Free a block of memory that was allocated from the pool. The block must be + * the most recently allocated block from its end of the pool, otherwise all + * newer blocks are freed as well. + * Return the amount of free space left in the pool. + */ +u32 main_pool_free(void *addr, u32 runBlockFunc) { + struct MainPoolBlock *block = (struct MainPoolBlock *)((u8 *)addr - sizeof(struct MainPoolBlock)); + struct MainPoolBlock *oldListHead = (struct MainPoolBlock *)((u8 *)addr - sizeof(struct MainPoolBlock)); + + if (oldListHead < sMemPool.listHeadL) { + do { + block = (sMemPool.listHeadL = sMemPool.listHeadL->prev); + if (runBlockFunc) { + // TODO: Fakematch + void (*func)(struct MainPoolBlock *, u32) = block->func; + if (func != 0) { + block->func(block + 1, block->arg); + // TODO: fake here too + if ((!(&sMemPool)) && (!(&sMemPool))) + { + } + } + } + sMemPool.available += ((uintptr_t)sMemPool.listHeadL->next - (uintptr_t)sMemPool.listHeadL); + sMemPool.listHeadL->next = NULL; + } while (oldListHead != sMemPool.listHeadL); + } else { + block = sMemPool.listHeadR; + if (oldListHead >= block && oldListHead >= block) { + do { + if (runBlockFunc) { + void (*func)(struct MainPoolBlock *, u32) = block->func; + if (func != NULL) { + func(block + 1, block->arg); + block = sMemPool.listHeadR; + } + } + block = (sMemPool.listHeadR = block->next); + sMemPool.available += ((uintptr_t)block - (uintptr_t)block->prev); + block->prev = NULL; + block = sMemPool.listHeadR; + } while (oldListHead >= sMemPool.listHeadR); + } + } + + return main_pool_get_available(); +} + +/** + * Manually allocate and initialize a block given a size and side and its + * function+arguments. + */ +void *main_pool_alloc_node(u32 size, s32 side, s32 arg, void *func) { + struct MainPoolBlock *node; + + osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); + node = main_pool_alloc(size, side); + if (node != NULL) { + main_pool_set_func(node, arg, func); + } + osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); + + return node; +} + +/** + * Same as above but no function/argument is set. + */ +void *main_pool_alloc_node_no_func(u32 size, s32 side) { + struct MainPoolBlock *node; + + osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); + node = main_pool_alloc(size, side); + osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); + + return node; +} + +/** + * Tries to free a block of memory that was allocated from the pool. Return + * the new available amount of the pool. + */ +u32 main_pool_try_free(struct MainPoolBlock *addr) { + if (addr != NULL) { + osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); + main_pool_free(addr, 1); + osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); + } + + return main_pool_get_available(); +} + +/** + * Resize a block of memory that was allocated from the left side of the pool. + * If the block is increasing in size, it must be the most recently allocated + * block from the left side. + * The block does not move. + */ +void *main_pool_realloc(void *addr, size_t size) { + struct MainPoolBlock *prior = (struct MainPoolBlock *)((u8 *)addr - sizeof(struct MainPoolBlock)); + void *newaddr = NULL; + + osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); + + if (prior->next == sMemPool.listHeadL) { + size_t diff = ((uintptr_t)prior->next - (uintptr_t)addr); + size = ALIGN16(size); + if (diff >= size || sMemPool.available >= (size - diff)) { + s32 arg = prior->arg; + void *func = prior->func; + main_pool_free(addr, 0); + newaddr = main_pool_alloc(size, MEMORY_POOL_LEFT); + main_pool_set_func(newaddr, arg, func); + } + } + osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); + return newaddr; +} + +/** + * Return the amount of available memory to use in the pool. + */ +u32 main_pool_get_available(void) { + s32 available = sMemPool.available - sizeof(struct MainPoolBlock); + + if (available < 0) { + available = 0; + } + + return available; +} + +/** + * Push pool state, to be restored later. Return the amount of free space left + * in the pool. + */ +u32 main_pool_push_state(u32 arg) { + struct MainPoolState *state; + struct MainPoolBlock *listHeadL; + struct MainPoolBlock *listHeadR; + uintptr_t available; + + osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); + + // retrieve the space and head pointers. + available = sMemPool.available; + listHeadL = sMemPool.listHeadL; + listHeadR = sMemPool.listHeadR; + + state = main_pool_alloc(sizeof(struct MainPoolState), MEMORY_POOL_LEFT); + if (state != NULL) { + /** + * Why is this line here? What this line is doing is backing the pointer up to the + * previous block before this one. in the block alloc function, addr is determined + * by the head plus the size of the block struct, meaning it is returning the + * pointer to the head. + */ + ((struct MainPoolBlock *)((u8*)state-sizeof(struct MainPoolBlock)))->arg = arg; + + // now that the previous block's argument is set, set the newly allocated state's + // fields. + state->prev = sMemPool.mainState; + state->freeSpace = available; + state->listHeadL = listHeadL; + state->listHeadR = listHeadR; + + // add the newly allocated state. + sMemPool.mainState = state; + } + osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); + return main_pool_get_available(); +} + +/** + * Restore pool state from a previous call to main_pool_push_state. Return the + * amount of free space left in the pool. + */ +u32 main_pool_pop_state(u32 arg) { + struct MainPoolState *node; + struct MainPoolBlock *argptr; + void *listHeadL; + void *listHeadR; + struct MainPoolState *state; + + argptr = (u32)arg; + osRecvMesg(&sMemPool.queue, NULL, OS_MESG_BLOCK); + + do { + node = sMemPool.mainState; + listHeadL = node->listHeadL; + listHeadR = node->listHeadR; + sMemPool.available = node->freeSpace; + sMemPool.mainState = node->prev; + + // was the argument passed in 0? + if (argptr == 0) { + break; + } + + // odd reuse of the variable, but what this is doing is backing the ptr up to the + // pool block. Thus, to check the arg variable on the pool block located before the state, + // we will need to cast the next if check. + node = (void*)((u8*)node - sizeof(struct MainPoolState)); + + if ((uintptr_t)argptr == (uintptr_t)((struct MainPoolBlock *)node)->arg) { + // we found the block with the matching string! break. + break; + } + } while(sMemPool.mainState != NULL); + + argptr = sMemPool.listHeadR; + while ((uintptr_t)listHeadR > (uintptr_t)argptr) { + if (argptr->func != NULL) { + argptr->func(argptr + 1, argptr->arg); + } + argptr = argptr->next; + } + + argptr = sMemPool.listHeadL->prev; + while ((uintptr_t)listHeadL <= (uintptr_t)argptr) { + if (argptr->func != NULL) { + argptr->func(argptr + 1, argptr->arg); + } + argptr = argptr->prev; + } + + sMemPool.listHeadL = listHeadL; + sMemPool.listHeadR = listHeadR; + osSendMesg(&sMemPool.queue, NULL, OS_MESG_NOBLOCK); + return main_pool_get_available(); +} + +/* + * Unused function. Seems at first glance to check for an address within some + * range, and then return the pointer to its data after the block. Perhaps? + * Without this being called, its hard to tell the correct context of this + * function. + */ +void *main_pool_search(uintptr_t addr, s32 *argPtr) { + struct MainPoolBlock *node; + struct MainPoolBlock *otherNode; + + node = sMemPool.listHeadL->prev; + while (node != NULL) { + int isAddrLater = (addr >= ((uintptr_t) ((u8*)node + sizeof(struct MainPoolBlock)))); + otherNode = node->next; + // seems to be checking for an addr within a block region? Since this function + // is unused, we wont be able to check for the intended context of what could + // call this function. + if (isAddrLater && addr < ((uintptr_t)otherNode & 0xFFFFFFFF)) { + if (argPtr != NULL) { + *argPtr = node->arg; + } + // return the pointer to its block contents. + return (void*)((u8*)(node) + sizeof(struct MainPoolBlock)); + } + node = node->prev; + } + + // we've searched thr prev linked list. Now lets go through the next linked list. + node = sMemPool.listHeadR; + otherNode = node->next; + while (otherNode != NULL) { + int isAddrLater = (addr >= ((uintptr_t) ((u8*)node + sizeof(struct MainPoolBlock)))); + struct MainPoolBlock *new_var = otherNode; // bit of a fakematch to force the move reload. + // same as above. + if (isAddrLater && (addr < ((uintptr_t)new_var & 0xFFFFFFFF))) { + if (argPtr != NULL) { + *argPtr = node->arg; + } + // return the pointer to its block contents. + return (void*)((u8*)(node) + sizeof(struct MainPoolBlock)); + } + otherNode = (node = otherNode)->next; + } + + return NULL; +} + +/** + * Set the block function and its argument(s) for a given block. + */ +void main_pool_set_func(void *block, s32 arg, void *func) { + struct MainPoolBlock *node = (void*)((uintptr_t)block - sizeof(struct MainPoolBlock)); + node->func = func; + node->arg = arg; +} + +/** + * Get the distance offset from the block's state listHeadL pointer to the current block. + */ +size_t main_pool_get_block_dist(struct MainPoolBlock *block) { + struct MainPoolState *state = ((u8*)block - sizeof(struct MainPoolBlock)); + + return (size_t)((uintptr_t)state->listHeadL - (uintptr_t)block); +} + +/** + * Return the pointer to the static memory pool area. + */ +struct MainPool *main_pool_get_pool(void) { + return &sMemPool; +} diff --git a/tools/symbol_addrs.txt b/tools/symbol_addrs.txt index 3139bd1..99dfb3e 100644 --- a/tools/symbol_addrs.txt +++ b/tools/symbol_addrs.txt @@ -582,4 +582,11 @@ main_pool_push_state = 0x80002784; main_pool_pop_state = 0x80002838; main_pool_get_pool = 0x80002A30; main_pool_get_block_dist = 0x80002A24; -main_pool_search = 0x80002960; \ No newline at end of file +main_pool_search = 0x80002960; +text_bss_VRAM = 0x8007ED80; +gHeap = 0x80104BB0; +gMainPool = 0x800A60B0; +mem_pool_try_init = 0x80002A40; +mem_pool_init = 0x80002A88; +mem_pool_alloc = 0x80002AF8; +mem_pool_free = 0x80002BD0; \ No newline at end of file