136 lines
2.8 KiB
C++
136 lines
2.8 KiB
C++
/*
|
|
* Copyright 2016-2017 gtalent2@gmail.com
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "addresses.hpp"
|
|
#include "panic.hpp"
|
|
|
|
namespace nostalgia {
|
|
namespace core {
|
|
|
|
struct HeapSegment {
|
|
size_t size;
|
|
uint8_t inUse;
|
|
HeapSegment *next;
|
|
|
|
uint8_t *end() {
|
|
return ((uint8_t*) this) + this->size;
|
|
}
|
|
};
|
|
|
|
static HeapSegment *_heapIdx = nullptr;
|
|
|
|
void initHeap() {
|
|
_heapIdx = (HeapSegment*) MEM_WRAM_END;
|
|
_heapIdx--;
|
|
// set size to half of WRAM
|
|
_heapIdx->size = (MEM_WRAM_END - MEM_WRAM_BEGIN) / 2;
|
|
_heapIdx->next = nullptr;
|
|
_heapIdx->inUse = false;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
using namespace nostalgia::core;
|
|
|
|
void *malloc(size_t allocSize) {
|
|
// add space for heap segment header data
|
|
const auto fullSize = allocSize + sizeof(HeapSegment);
|
|
auto seg = _heapIdx;
|
|
HeapSegment *prev = nullptr;
|
|
while (seg && seg->size < fullSize) {
|
|
prev = seg;
|
|
seg = seg->next;
|
|
}
|
|
|
|
// panic if the allocation failed
|
|
if (seg == nullptr) {
|
|
panic("Heap allocation failed");
|
|
}
|
|
|
|
seg = (HeapSegment*) (((uint8_t*) seg) - allocSize);
|
|
if (prev) {
|
|
prev->next = seg;
|
|
}
|
|
// update size for the heap segment now that it is to be considered
|
|
// allocated
|
|
seg->size = fullSize;
|
|
seg->next = (HeapSegment*) (((uint8_t*) seg) + fullSize);
|
|
seg->inUse = true;
|
|
auto out = seg + 1;
|
|
|
|
auto hs = *_heapIdx;
|
|
hs.size -= fullSize;
|
|
if (hs.size == 0) {
|
|
_heapIdx = hs.next;
|
|
} else {
|
|
_heapIdx = (HeapSegment*) (((uint8_t*) _heapIdx) - fullSize);
|
|
*_heapIdx = hs;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
void free(void *ptrIn) {
|
|
// get ptr back down to the meta data
|
|
auto *ptr = ((HeapSegment*) ptrIn) - 1;
|
|
HeapSegment *prev = nullptr;
|
|
HeapSegment *current = _heapIdx;
|
|
while (current && current != ptr) {
|
|
prev = current;
|
|
current = current->next;
|
|
}
|
|
|
|
// ptr was found as a valid memory allocation, deallocate it
|
|
if (current) {
|
|
// move header back to end of segment
|
|
auto newCurrent = (HeapSegment*) current->end() - sizeof(HeapSegment);
|
|
*newCurrent = *current;
|
|
current = newCurrent;
|
|
prev->next = current;
|
|
|
|
// mark as not in use
|
|
current->inUse = false;
|
|
|
|
// join with next if next is also unused
|
|
if (current->next && !current->next->inUse) {
|
|
current->size += current->next->size;
|
|
current->next = current->next->next;
|
|
}
|
|
|
|
// join with prev if prev is also unused
|
|
if (prev && !prev->inUse) {
|
|
prev->size += current->size;
|
|
prev->next = current->next;
|
|
current = prev;
|
|
}
|
|
|
|
// if current is closer heap start than _heapIdx, _heapIdx becomes
|
|
// current
|
|
if (current > _heapIdx) {
|
|
_heapIdx = current;
|
|
}
|
|
}
|
|
}
|
|
|
|
void *operator new(size_t allocSize) {
|
|
return malloc(allocSize);
|
|
}
|
|
|
|
void *operator new[](size_t allocSize) {
|
|
return malloc(allocSize);
|
|
}
|
|
|
|
void operator delete(void *ptr) {
|
|
free(ptr);
|
|
}
|
|
|
|
void operator delete[](void *ptr) {
|
|
free(ptr);
|
|
}
|