2020-06-02 12:44:34 -04:00
|
|
|
#include <PR/ultratypes.h>
|
|
|
|
|
2019-08-25 00:46:40 -04:00
|
|
|
#include "debug_utils.h"
|
2020-06-02 12:44:34 -04:00
|
|
|
#include "gd_memory.h"
|
2019-08-25 00:46:40 -04:00
|
|
|
#include "renderer.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file gd_memory.c
|
|
|
|
*
|
|
|
|
* This file contains the functions need to manage allocation in
|
|
|
|
* goddard's heap. However, the actual, useable allocation functions
|
|
|
|
* are `gd_malloc()`, `gd_malloc_perm()`, and `gd_malloc_temp()`, as
|
|
|
|
* well as `gd_free()`. This file is for managing the underlying memory
|
|
|
|
* block lists.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* bss */
|
|
|
|
static struct GMemBlock *sFreeBlockListHead;
|
|
|
|
static struct GMemBlock *sUsedBlockListHead;
|
|
|
|
static struct GMemBlock *sEmptyBlockListHead;
|
|
|
|
|
|
|
|
/* Forward Declarations */
|
|
|
|
void empty_mem_block(struct GMemBlock *);
|
|
|
|
struct GMemBlock *into_free_memblock(struct GMemBlock *);
|
|
|
|
struct GMemBlock *make_mem_block(u32, u8);
|
|
|
|
u32 print_list_stats(struct GMemBlock *, s32, s32);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Empty a `GMemBlock` into a default state. This empty block
|
|
|
|
* doesn't point to any data, nor does it have any size. The
|
|
|
|
* block is removed from whatever list is was in, and is added
|
|
|
|
* to the empty block list.
|
|
|
|
*/
|
|
|
|
void empty_mem_block(struct GMemBlock *block) {
|
2019-09-01 15:50:50 -04:00
|
|
|
if (block->next != NULL) {
|
2019-08-25 00:46:40 -04:00
|
|
|
block->next->prev = block->prev;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
|
2019-09-01 15:50:50 -04:00
|
|
|
if (block->prev != NULL) {
|
2019-08-25 00:46:40 -04:00
|
|
|
block->prev->next = block->next;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
switch (block->blockType) {
|
|
|
|
case G_MEM_BLOCK_FREE:
|
2019-09-01 15:50:50 -04:00
|
|
|
if (block->prev == NULL) {
|
2019-08-25 00:46:40 -04:00
|
|
|
sFreeBlockListHead = block->next;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
break;
|
|
|
|
case G_MEM_BLOCK_USED:
|
2019-09-01 15:50:50 -04:00
|
|
|
if (block->prev == NULL) {
|
2019-08-25 00:46:40 -04:00
|
|
|
sUsedBlockListHead = block->next;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
block->next = sEmptyBlockListHead;
|
2019-09-01 15:50:50 -04:00
|
|
|
if (block->next != NULL) {
|
2019-08-25 00:46:40 -04:00
|
|
|
sEmptyBlockListHead->prev = block;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
sEmptyBlockListHead = block;
|
|
|
|
block->prev = NULL;
|
2019-11-03 14:36:27 -05:00
|
|
|
block->ptr = NULL;
|
2019-08-25 00:46:40 -04:00
|
|
|
block->size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transform a `GMemBlock` into a free block that points to memory available
|
|
|
|
* for allocation.
|
|
|
|
*
|
|
|
|
* @returns pointer to the free `GMemBlock` */
|
|
|
|
struct GMemBlock *into_free_memblock(struct GMemBlock *block) {
|
|
|
|
struct GMemBlock *freeBlock;
|
2019-11-03 14:36:27 -05:00
|
|
|
void *ptr;
|
2019-08-25 00:46:40 -04:00
|
|
|
u8 permanence;
|
|
|
|
u32 space;
|
|
|
|
|
2019-11-03 14:36:27 -05:00
|
|
|
ptr = block->ptr;
|
2019-08-25 00:46:40 -04:00
|
|
|
space = block->size;
|
|
|
|
permanence = block->permFlag;
|
|
|
|
|
|
|
|
empty_mem_block(block);
|
|
|
|
freeBlock = make_mem_block(G_MEM_BLOCK_FREE, permanence);
|
2019-11-03 14:36:27 -05:00
|
|
|
freeBlock->ptr = ptr;
|
2019-08-25 00:46:40 -04:00
|
|
|
freeBlock->size = space;
|
|
|
|
freeBlock->permFlag = permanence;
|
|
|
|
|
|
|
|
return freeBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate a new `GMemBlock` structure of the given type and permanence.
|
|
|
|
* It does not assign any heap space to the new block.
|
|
|
|
*
|
|
|
|
* @param blockType either `G_MEM_BLOCK_FREE` or `G_MEM_BLOCK_USED`
|
|
|
|
* @param permFlag some sort of permanence value, where setting one the upper
|
|
|
|
* four bits imply a permanent block, while setting one the lower
|
|
|
|
* four bits imply a temporary block
|
|
|
|
* @returns a pointer to the new `GMemBlock`
|
|
|
|
*/
|
|
|
|
struct GMemBlock *make_mem_block(u32 blockType, u8 permFlag) {
|
|
|
|
struct GMemBlock *newMemBlock;
|
|
|
|
|
|
|
|
if (sEmptyBlockListHead == NULL) {
|
|
|
|
sEmptyBlockListHead = (struct GMemBlock *) gd_allocblock(sizeof(struct GMemBlock));
|
|
|
|
|
2019-09-01 15:50:50 -04:00
|
|
|
if (sEmptyBlockListHead == NULL) {
|
2019-08-25 00:46:40 -04:00
|
|
|
fatal_printf("MakeMemBlock() unable to allocate");
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
sEmptyBlockListHead->next = NULL;
|
|
|
|
sEmptyBlockListHead->prev = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
newMemBlock = sEmptyBlockListHead;
|
2019-09-01 15:50:50 -04:00
|
|
|
if ((sEmptyBlockListHead = newMemBlock->next) != NULL) {
|
2019-08-25 00:46:40 -04:00
|
|
|
newMemBlock->next->prev = NULL;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
switch (blockType) {
|
|
|
|
case G_MEM_BLOCK_FREE:
|
|
|
|
newMemBlock->next = sFreeBlockListHead;
|
2019-09-01 15:50:50 -04:00
|
|
|
if (newMemBlock->next != NULL) {
|
2019-08-25 00:46:40 -04:00
|
|
|
sFreeBlockListHead->prev = newMemBlock;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
sFreeBlockListHead = newMemBlock;
|
|
|
|
break;
|
|
|
|
case G_MEM_BLOCK_USED:
|
|
|
|
newMemBlock->next = sUsedBlockListHead;
|
2019-09-01 15:50:50 -04:00
|
|
|
if (newMemBlock->next != NULL) {
|
2019-08-25 00:46:40 -04:00
|
|
|
sUsedBlockListHead->prev = newMemBlock;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
sUsedBlockListHead = newMemBlock;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fatal_printf("unkown memblock type");
|
|
|
|
}
|
|
|
|
newMemBlock->prev = NULL;
|
|
|
|
newMemBlock->blockType = (u8) blockType;
|
|
|
|
newMemBlock->permFlag = permFlag;
|
|
|
|
|
|
|
|
return newMemBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Free memory allocated on the goddard heap.
|
|
|
|
*
|
|
|
|
* @param ptr pointer to heap allocated memory
|
|
|
|
* @returns size of memory freed
|
|
|
|
* @retval 0 `ptr` did not point to a valid memory block
|
|
|
|
*/
|
|
|
|
u32 gd_free_mem(void *ptr) {
|
|
|
|
register struct GMemBlock *curBlock;
|
|
|
|
u32 bytesFreed;
|
2019-11-03 14:36:27 -05:00
|
|
|
register u8 *targetBlock = ptr;
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
for (curBlock = sUsedBlockListHead; curBlock != NULL; curBlock = curBlock->next) {
|
2019-11-03 14:36:27 -05:00
|
|
|
if (targetBlock == curBlock->ptr) {
|
2019-08-25 00:46:40 -04:00
|
|
|
bytesFreed = curBlock->size;
|
|
|
|
into_free_memblock(curBlock);
|
|
|
|
return bytesFreed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fatal_printf("Free() Not a valid memory block");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Request a pointer to goddard heap memory of at least `size` and
|
|
|
|
* of the same `permanence`.
|
|
|
|
*
|
|
|
|
* @return pointer to heap
|
|
|
|
* @retval NULL could not fulfill the request
|
|
|
|
*/
|
|
|
|
void *gd_request_mem(u32 size, u8 permanence) {
|
|
|
|
struct GMemBlock *foundBlock = NULL;
|
|
|
|
struct GMemBlock *curBlock;
|
|
|
|
struct GMemBlock *newBlock;
|
|
|
|
|
|
|
|
newBlock = make_mem_block(G_MEM_BLOCK_USED, permanence);
|
|
|
|
curBlock = sFreeBlockListHead;
|
|
|
|
|
|
|
|
while (curBlock != NULL) {
|
|
|
|
if (curBlock->permFlag & permanence) {
|
|
|
|
if (curBlock->size == size) {
|
|
|
|
foundBlock = curBlock;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (curBlock->size > size) {
|
|
|
|
if (foundBlock != NULL) { /* find closest sized block */
|
2019-09-01 15:50:50 -04:00
|
|
|
if (curBlock->size < foundBlock->size) {
|
2019-08-25 00:46:40 -04:00
|
|
|
foundBlock = curBlock;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
} else {
|
|
|
|
foundBlock = curBlock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
curBlock = curBlock->next;
|
|
|
|
}
|
|
|
|
|
2019-09-01 15:50:50 -04:00
|
|
|
if (foundBlock == NULL) {
|
2019-08-25 00:46:40 -04:00
|
|
|
return NULL;
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
if (foundBlock->size > size) { /* split free block */
|
2019-11-03 14:36:27 -05:00
|
|
|
newBlock->ptr = foundBlock->ptr;
|
2019-08-25 00:46:40 -04:00
|
|
|
newBlock->size = size;
|
|
|
|
|
|
|
|
foundBlock->size -= size;
|
2019-11-03 14:36:27 -05:00
|
|
|
foundBlock->ptr += size;
|
2019-08-25 00:46:40 -04:00
|
|
|
} else if (foundBlock->size == size) { /* recycle whole free block */
|
2019-11-03 14:36:27 -05:00
|
|
|
newBlock->ptr = foundBlock->ptr;
|
2019-08-25 00:46:40 -04:00
|
|
|
newBlock->size = size;
|
|
|
|
empty_mem_block(foundBlock);
|
|
|
|
}
|
|
|
|
|
2019-11-03 14:36:27 -05:00
|
|
|
return newBlock->ptr;
|
2019-08-25 00:46:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add memory of `size` at `addr` to the goddard heap for later allocation.
|
|
|
|
*
|
|
|
|
* @returns `GMemBlock` that contains info about the new heap memory
|
|
|
|
*/
|
2019-11-03 14:36:27 -05:00
|
|
|
struct GMemBlock *gd_add_mem_to_heap(u32 size, void *addr, u8 permanence) {
|
2019-08-25 00:46:40 -04:00
|
|
|
struct GMemBlock *newBlock;
|
|
|
|
/* eight-byte align the new block's data stats */
|
|
|
|
size = (size - 8) & ~7;
|
2019-11-03 14:36:27 -05:00
|
|
|
addr = (void *)(((uintptr_t) addr + 8) & ~7);
|
2019-08-25 00:46:40 -04:00
|
|
|
|
|
|
|
newBlock = make_mem_block(G_MEM_BLOCK_FREE, permanence);
|
2019-11-03 14:36:27 -05:00
|
|
|
newBlock->ptr = addr;
|
2019-08-25 00:46:40 -04:00
|
|
|
newBlock->size = size;
|
|
|
|
|
|
|
|
return newBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* NULL the various `GMemBlock` list heads
|
|
|
|
*/
|
|
|
|
void init_mem_block_lists(void) {
|
|
|
|
sFreeBlockListHead = NULL;
|
|
|
|
sUsedBlockListHead = NULL;
|
|
|
|
sEmptyBlockListHead = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Print information (size, entries) about the `GMemBlock` list. It can print
|
|
|
|
* information for individual blocks as well as summary info for the entry list.
|
|
|
|
*
|
|
|
|
* @param block `GMemBlock` to start reading the list
|
|
|
|
* @param printBlockInfo If `TRUE`, print information about every block
|
|
|
|
* in the list
|
|
|
|
* @param permanence Limit info printed to blocks with this permanence
|
|
|
|
* @returns number of entries
|
|
|
|
*/
|
|
|
|
u32 print_list_stats(struct GMemBlock *block, s32 printBlockInfo, s32 permanence) {
|
|
|
|
u32 entries = 0;
|
|
|
|
u32 totalSize = 0;
|
|
|
|
|
|
|
|
while (block != NULL) {
|
|
|
|
if (block->permFlag & permanence) {
|
|
|
|
entries++;
|
2019-09-01 15:50:50 -04:00
|
|
|
if (printBlockInfo) {
|
2019-08-25 00:46:40 -04:00
|
|
|
gd_printf(" %6.2fk (%d bytes)\n",
|
|
|
|
(f32) block->size / 1024.0, //? 1024.0f
|
|
|
|
block->size);
|
2019-09-01 15:50:50 -04:00
|
|
|
}
|
2019-08-25 00:46:40 -04:00
|
|
|
totalSize += block->size;
|
|
|
|
}
|
|
|
|
block = block->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
gd_printf("Total %6.2fk (%d bytes) in %d entries\n",
|
|
|
|
(f32) totalSize / 1024.0, //? 1024.0f
|
|
|
|
totalSize, entries);
|
|
|
|
|
|
|
|
return entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Print summary information about all used, free, and empty
|
|
|
|
* `GMemBlock`s.
|
|
|
|
*/
|
|
|
|
void mem_stats(void) {
|
|
|
|
struct GMemBlock *list;
|
|
|
|
|
|
|
|
gd_printf("Perm Used blocks:\n");
|
|
|
|
list = sUsedBlockListHead;
|
|
|
|
print_list_stats(list, FALSE, PERM_G_MEM_BLOCK);
|
|
|
|
gd_printf("\n");
|
|
|
|
|
|
|
|
gd_printf("Perm Free blocks:\n");
|
|
|
|
list = sFreeBlockListHead;
|
|
|
|
print_list_stats(list, FALSE, PERM_G_MEM_BLOCK);
|
|
|
|
gd_printf("\n");
|
|
|
|
|
|
|
|
gd_printf("Temp Used blocks:\n");
|
|
|
|
list = sUsedBlockListHead;
|
|
|
|
print_list_stats(list, FALSE, TEMP_G_MEM_BLOCK);
|
|
|
|
gd_printf("\n");
|
|
|
|
|
|
|
|
gd_printf("Temp Free blocks:\n");
|
|
|
|
list = sFreeBlockListHead;
|
|
|
|
print_list_stats(list, FALSE, TEMP_G_MEM_BLOCK);
|
|
|
|
gd_printf("\n");
|
|
|
|
|
|
|
|
gd_printf("Empty blocks:\n");
|
|
|
|
list = sEmptyBlockListHead;
|
|
|
|
print_list_stats(list, FALSE, PERM_G_MEM_BLOCK | TEMP_G_MEM_BLOCK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
::::::
|
|
|
|
MMMMMMMMMM ::::::
|
|
|
|
MMMMMMMMMMMMMMMMMM::::
|
|
|
|
SSSSSS::::SS:: SSSSSS
|
|
|
|
SS::SS::::::SS::::::SSSS
|
|
|
|
SS::SSSS::::::SS::::::SS
|
|
|
|
SSSS::::::::SSSSSSSSSS
|
|
|
|
::::::::::::::SS
|
|
|
|
SSSSSSSSSSMMSSSSSSMMSS
|
|
|
|
SSSSSSSSSSSSSSMMSSSSSSMM SS
|
|
|
|
::::SSSSSSSSSSSSMMMMMMMMMM SS
|
|
|
|
:::::: MMSSMMMMMM::MM::MMSSSS
|
|
|
|
:: SS MMMMMMMMMMMMMMMMMMSSSS
|
|
|
|
SSSSSSMMMMMMMMMMMMMMMMMMSSSS
|
|
|
|
SSSSSSMMMMMMMMMMMMMM
|
|
|
|
SS MMMMMMMM
|
|
|
|
*/
|