Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/goddard/gd_memory.c
7858 views
1
#ifndef USE_SYSTEM_MALLOC
2
#include <PR/ultratypes.h>
3
4
#include "debug_utils.h"
5
#include "gd_memory.h"
6
#include "renderer.h"
7
8
/**
9
* @file gd_memory.c
10
*
11
* This file contains the functions need to manage allocation in
12
* goddard's heap. However, the actual, useable allocation functions
13
* are `gd_malloc()`, `gd_malloc_perm()`, and `gd_malloc_temp()`, as
14
* well as `gd_free()`. This file is for managing the underlying memory
15
* block lists.
16
*/
17
18
/* bss */
19
static struct GMemBlock *sFreeBlockListHead;
20
static struct GMemBlock *sUsedBlockListHead;
21
static struct GMemBlock *sEmptyBlockListHead;
22
23
/* Forward Declarations */
24
void empty_mem_block(struct GMemBlock *);
25
struct GMemBlock *into_free_memblock(struct GMemBlock *);
26
struct GMemBlock *make_mem_block(u32, u8);
27
u32 print_list_stats(struct GMemBlock *, s32, s32);
28
29
/**
30
* Empty a `GMemBlock` into a default state. This empty block
31
* doesn't point to any data, nor does it have any size. The
32
* block is removed from whatever list is was in, and is added
33
* to the empty block list.
34
*/
35
void empty_mem_block(struct GMemBlock *block) {
36
if (block->next != NULL) {
37
block->next->prev = block->prev;
38
}
39
40
if (block->prev != NULL) {
41
block->prev->next = block->next;
42
}
43
44
switch (block->blockType) {
45
case G_MEM_BLOCK_FREE:
46
if (block->prev == NULL) {
47
sFreeBlockListHead = block->next;
48
}
49
break;
50
case G_MEM_BLOCK_USED:
51
if (block->prev == NULL) {
52
sUsedBlockListHead = block->next;
53
}
54
break;
55
}
56
57
block->next = sEmptyBlockListHead;
58
if (block->next != NULL) {
59
sEmptyBlockListHead->prev = block;
60
}
61
62
sEmptyBlockListHead = block;
63
block->prev = NULL;
64
block->ptr = NULL;
65
block->size = 0;
66
}
67
68
/**
69
* Transform a `GMemBlock` into a free block that points to memory available
70
* for allocation.
71
*
72
* @returns pointer to the free `GMemBlock` */
73
struct GMemBlock *into_free_memblock(struct GMemBlock *block) {
74
struct GMemBlock *freeBlock;
75
void *ptr;
76
u8 permanence;
77
u32 space;
78
79
ptr = block->ptr;
80
space = block->size;
81
permanence = block->permFlag;
82
83
empty_mem_block(block);
84
freeBlock = make_mem_block(G_MEM_BLOCK_FREE, permanence);
85
freeBlock->ptr = ptr;
86
freeBlock->size = space;
87
freeBlock->permFlag = permanence;
88
89
return freeBlock;
90
}
91
92
/**
93
* Allocate a new `GMemBlock` structure of the given type and permanence.
94
* It does not assign any heap space to the new block.
95
*
96
* @param blockType either `G_MEM_BLOCK_FREE` or `G_MEM_BLOCK_USED`
97
* @param permFlag some sort of permanence value, where setting one the upper
98
* four bits imply a permanent block, while setting one the lower
99
* four bits imply a temporary block
100
* @returns a pointer to the new `GMemBlock`
101
*/
102
struct GMemBlock *make_mem_block(u32 blockType, u8 permFlag) {
103
struct GMemBlock *newMemBlock;
104
105
if (sEmptyBlockListHead == NULL) {
106
sEmptyBlockListHead = (struct GMemBlock *) gd_allocblock(sizeof(struct GMemBlock));
107
108
if (sEmptyBlockListHead == NULL) {
109
fatal_printf("MakeMemBlock() unable to allocate");
110
}
111
112
sEmptyBlockListHead->next = NULL;
113
sEmptyBlockListHead->prev = NULL;
114
}
115
116
newMemBlock = sEmptyBlockListHead;
117
if ((sEmptyBlockListHead = newMemBlock->next) != NULL) {
118
newMemBlock->next->prev = NULL;
119
}
120
121
switch (blockType) {
122
case G_MEM_BLOCK_FREE:
123
newMemBlock->next = sFreeBlockListHead;
124
if (newMemBlock->next != NULL) {
125
sFreeBlockListHead->prev = newMemBlock;
126
}
127
sFreeBlockListHead = newMemBlock;
128
break;
129
case G_MEM_BLOCK_USED:
130
newMemBlock->next = sUsedBlockListHead;
131
if (newMemBlock->next != NULL) {
132
sUsedBlockListHead->prev = newMemBlock;
133
}
134
sUsedBlockListHead = newMemBlock;
135
break;
136
default:
137
fatal_printf("unkown memblock type");
138
}
139
newMemBlock->prev = NULL;
140
newMemBlock->blockType = (u8) blockType;
141
newMemBlock->permFlag = permFlag;
142
143
return newMemBlock;
144
}
145
146
/**
147
* Free memory allocated on the goddard heap.
148
*
149
* @param ptr pointer to heap allocated memory
150
* @returns size of memory freed
151
* @retval 0 `ptr` did not point to a valid memory block
152
*/
153
u32 gd_free_mem(void *ptr) {
154
register struct GMemBlock *curBlock;
155
u32 bytesFreed;
156
register u8 *targetBlock = ptr;
157
158
for (curBlock = sUsedBlockListHead; curBlock != NULL; curBlock = curBlock->next) {
159
if (targetBlock == curBlock->ptr) {
160
bytesFreed = curBlock->size;
161
into_free_memblock(curBlock);
162
return bytesFreed;
163
}
164
}
165
166
fatal_printf("Free() Not a valid memory block");
167
return 0;
168
}
169
170
/**
171
* Request a pointer to goddard heap memory of at least `size` and
172
* of the same `permanence`.
173
*
174
* @return pointer to heap
175
* @retval NULL could not fulfill the request
176
*/
177
void *gd_request_mem(u32 size, u8 permanence) {
178
struct GMemBlock *foundBlock = NULL;
179
struct GMemBlock *curBlock;
180
struct GMemBlock *newBlock;
181
182
newBlock = make_mem_block(G_MEM_BLOCK_USED, permanence);
183
curBlock = sFreeBlockListHead;
184
185
while (curBlock != NULL) {
186
if (curBlock->permFlag & permanence) {
187
if (curBlock->size == size) {
188
foundBlock = curBlock;
189
break;
190
} else {
191
if (curBlock->size > size) {
192
if (foundBlock != NULL) { /* find closest sized block */
193
if (curBlock->size < foundBlock->size) {
194
foundBlock = curBlock;
195
}
196
} else {
197
foundBlock = curBlock;
198
}
199
}
200
}
201
}
202
curBlock = curBlock->next;
203
}
204
205
if (foundBlock == NULL) {
206
return NULL;
207
}
208
209
if (foundBlock->size > size) { /* split free block */
210
newBlock->ptr = foundBlock->ptr;
211
newBlock->size = size;
212
213
foundBlock->size -= size;
214
foundBlock->ptr += size;
215
} else if (foundBlock->size == size) { /* recycle whole free block */
216
newBlock->ptr = foundBlock->ptr;
217
newBlock->size = size;
218
empty_mem_block(foundBlock);
219
}
220
221
return newBlock->ptr;
222
}
223
224
/**
225
* Add memory of `size` at `addr` to the goddard heap for later allocation.
226
*
227
* @returns `GMemBlock` that contains info about the new heap memory
228
*/
229
struct GMemBlock *gd_add_mem_to_heap(u32 size, void *addr, u8 permanence) {
230
struct GMemBlock *newBlock;
231
/* eight-byte align the new block's data stats */
232
size = (size - 8) & ~7;
233
addr = (void *)(((uintptr_t) addr + 8) & ~7);
234
235
newBlock = make_mem_block(G_MEM_BLOCK_FREE, permanence);
236
newBlock->ptr = addr;
237
newBlock->size = size;
238
239
return newBlock;
240
}
241
242
/**
243
* NULL the various `GMemBlock` list heads
244
*/
245
void init_mem_block_lists(void) {
246
sFreeBlockListHead = NULL;
247
sUsedBlockListHead = NULL;
248
sEmptyBlockListHead = NULL;
249
}
250
251
/**
252
* Print information (size, entries) about the `GMemBlock` list. It can print
253
* information for individual blocks as well as summary info for the entry list.
254
*
255
* @param block `GMemBlock` to start reading the list
256
* @param printBlockInfo If `TRUE`, print information about every block
257
* in the list
258
* @param permanence Limit info printed to blocks with this permanence
259
* @returns number of entries
260
*/
261
u32 print_list_stats(struct GMemBlock *block, s32 printBlockInfo, s32 permanence) {
262
u32 entries = 0;
263
u32 totalSize = 0;
264
265
while (block != NULL) {
266
if (block->permFlag & permanence) {
267
entries++;
268
if (printBlockInfo) {
269
gd_printf(" %6.2fk (%d bytes)\n",
270
(f32) block->size / 1024.0, //? 1024.0f
271
block->size);
272
}
273
totalSize += block->size;
274
}
275
block = block->next;
276
}
277
278
gd_printf("Total %6.2fk (%d bytes) in %d entries\n",
279
(f32) totalSize / 1024.0, //? 1024.0f
280
totalSize, entries);
281
282
return entries;
283
}
284
285
/**
286
* Print summary information about all used, free, and empty
287
* `GMemBlock`s.
288
*/
289
void mem_stats(void) {
290
struct GMemBlock *list;
291
292
gd_printf("Perm Used blocks:\n");
293
list = sUsedBlockListHead;
294
print_list_stats(list, FALSE, PERM_G_MEM_BLOCK);
295
gd_printf("\n");
296
297
gd_printf("Perm Free blocks:\n");
298
list = sFreeBlockListHead;
299
print_list_stats(list, FALSE, PERM_G_MEM_BLOCK);
300
gd_printf("\n");
301
302
gd_printf("Temp Used blocks:\n");
303
list = sUsedBlockListHead;
304
print_list_stats(list, FALSE, TEMP_G_MEM_BLOCK);
305
gd_printf("\n");
306
307
gd_printf("Temp Free blocks:\n");
308
list = sFreeBlockListHead;
309
print_list_stats(list, FALSE, TEMP_G_MEM_BLOCK);
310
gd_printf("\n");
311
312
gd_printf("Empty blocks:\n");
313
list = sEmptyBlockListHead;
314
print_list_stats(list, FALSE, PERM_G_MEM_BLOCK | TEMP_G_MEM_BLOCK);
315
}
316
#else
317
void mem_stats(void) {
318
}
319
#endif
320
321