Files
archived-fifoplayer/source/memory_manager.cpp
2014-01-30 16:39:56 +01:00

165 lines
3.9 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <gccore.h>
#include <vector>
#include <map>
#include "memory_manager.h"
static std::map<u32, aligned_buf> memory_map; // map of memory chunks (indexed by starting address)
aligned_buf::aligned_buf() : buf(NULL), size(0), alignment(DEF_ALIGN)
{
}
aligned_buf::aligned_buf(int alignment) : buf(NULL), size(0), alignment(alignment)
{
}
aligned_buf::~aligned_buf()
{
free(buf);
}
aligned_buf::aligned_buf(const aligned_buf& oth)
{
if (oth.buf)
{
buf = (u8*)memalign(oth.alignment, oth.size);
printf("copied to %p (%x) \n", buf, MEM_VIRTUAL_TO_PHYSICAL(buf));
memcpy(buf, oth.buf, oth.size);
}
else buf = NULL;
size = oth.size;
alignment = oth.alignment;
}
void aligned_buf::resize(int new_size)
{
if (!buf)
{
buf = (u8*)memalign(alignment, new_size);
printf("allocated to %p (%x) - size %x \n", buf, MEM_VIRTUAL_TO_PHYSICAL(buf), new_size);
}
else
{
u8* old_buf = buf;
buf = (u8*)memalign(alignment, new_size);
memcpy(buf, old_buf, std::min(new_size, size));
printf("reallocated to %p (%x)\n", buf, MEM_VIRTUAL_TO_PHYSICAL(buf));
free(old_buf);
}
size = new_size;
}
bool IntersectsMemoryRange(u32 start1, u32 size1, u32 start2, u32 size2)
{
return size1 && size2 && ((start1 >= start2 && start1 < start2 + size2) ||
(start2 >= start1 && start2 < start1 + size1));
}
u32 FixupMemoryAddress(u32 addr)
{
switch (addr >> 28)
{
case 0x0:
case 0x8:
addr &= 0x1FFFFFF; // RAM_MASK
break;
case 0x1:
case 0x9:
case 0xd:
// TODO: Iff Wii
addr &= 0x3FFFFFF; // EXRAM_MASK
break;
default:
printf("CRITICAL: Unkown memory location %x!\n", addr);
exit(0); // I'd rather exit than not noticing this kind of issue...
break;
}
return addr;
}
// TODO: Needs to take care of alignment, too!
// Returns true if memory layout changed
bool PrepareMemoryLoad(u32 start_addr, u32 size)
{
bool ret = false;
start_addr = FixupMemoryAddress(start_addr);
// Make sure alignment of data inside the memory block is preserved
u32 off = start_addr % DEF_ALIGN;
start_addr = start_addr - off;
size += off;
std::vector<u32> affected_elements;
u32 new_start_addr = start_addr;
u32 new_end_addr = start_addr + size - 1;
// Find overlaps with existing memory chunks
for (auto it = memory_map.begin(); it != memory_map.end(); ++it)
{
if (IntersectsMemoryRange(it->first, it->second.size, start_addr, size))
{
affected_elements.push_back(it->first);
if (it->first < new_start_addr)
new_start_addr = it->first;
if (it->first + it->second.size > new_end_addr + 1)
new_end_addr = it->first + it->second.size - 1;
}
}
aligned_buf& new_memchunk(memory_map[new_start_addr]); // creates a new vector or uses the existing one
u32 new_size = new_end_addr - new_start_addr + 1;
// if the new memory range is inside an existing chunk, there's nothing to do
if (new_memchunk.size == new_size)
return false;
// resize chunk to required size, move old content to it, replace old arrays with new one
// NOTE: can't do reserve here because not the whole memory might be covered by existing memory chunks
new_memchunk.resize(new_size);
while (!affected_elements.empty())
{
u32 addr = affected_elements.back();
// first chunk is already in new_memchunk
if (addr != new_start_addr)
{
aligned_buf& src = memory_map[addr];
memcpy(&new_memchunk.buf[addr - new_start_addr], &src.buf[0], src.size);
memory_map.erase(addr);
ret = true;
}
affected_elements.pop_back();
}
// TODO: Handle critical case where memory allocation fails!
return ret;
}
// Must have been reserved via PrepareMemoryLoad first
u8* GetPointer(u32 addr)
{
addr = FixupMemoryAddress(addr);
for (auto it = memory_map.begin(); it != memory_map.end(); ++it)
if (addr >= it->first && addr < it->first + it->second.size)
return &it->second.buf[addr - it->first];
return NULL;
}