From 3afc669e3f5272e8f76700e13479dde9be0e13c7 Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Thu, 6 Jun 2019 16:00:02 +0200 Subject: [PATCH] Dummy memory allocator --- CMakeLists.txt | 1 + alloc.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++ alloc.h | 18 +++++++ emulation.c | 24 +++++---- 4 files changed, 171 insertions(+), 11 deletions(-) create mode 100644 alloc.c create mode 100644 alloc.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 91e1f87..74cb12b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(openswe1r emulation.c export.c shader.c + alloc.c dll/kernel32.c diff --git a/alloc.c b/alloc.c new file mode 100644 index 0000000..36ab084 --- /dev/null +++ b/alloc.c @@ -0,0 +1,139 @@ +// Copyright 2019 OpenSWE1R Maintainers +// Licensed under GPLv2 or any later version +// Refer to the included LICENSE.txt file. + +// This is a temporary placeholder for a memory allocator. + +#include "alloc.h" + +#include +#include +#include +#include +#include +#include + +#include "common.h" + + +typedef struct _Allocator { + unsigned int block_count; + unsigned int block_size; + uint8_t block_usage[1]; +} Allocator; + + +static void block_mark_used(Allocator* allocator, unsigned int block, bool used) { + uint8_t mask = 1 << (block % 8); + if (used) { + allocator->block_usage[block / 8] |= mask; + } else { + allocator->block_usage[block / 8] &= ~mask; + } +} + +static bool block_is_used(Allocator* allocator, unsigned int block) { + uint8_t mask = 1 << (block % 8); + return allocator->block_usage[block / 8] & mask; +} + +static unsigned int count_blocks(Allocator* allocator, unsigned int block, bool used) { + unsigned int count = 0; + while(block < allocator->block_count) { + if (block_is_used(allocator, block) != used) { + break; + } + block++; + count++; + } + return count; +} + +static unsigned int find_free_blocks(Allocator* allocator, unsigned int needed_blocks) { + + unsigned int best_count = 0; + unsigned int best_block = 0; + + unsigned int block = 0; + while(block < allocator->block_count) { + + bool block_used = block_is_used(allocator, block); + unsigned int available_blocks = count_blocks(allocator, block, block_used); + + // Only look at unused blocks that are large enough + if (!block_used && (available_blocks >= needed_blocks)) { + + // Check if we don't have a result yet, or if this is a better fit + if ((best_count == 0) || (available_blocks < best_count)) { + + // Update result + best_block = block; + best_count = available_blocks; + + // If we have an exact fit, use that + if (best_count == needed_blocks) { + break; + } + + } + } + + block += available_blocks; + } + + // Assert that we have a result + assert(best_count != 0); + + // Returns best block address + return best_block; +} + +Allocator* alloc_create(unsigned int size, unsigned int block_size) { + assert(size % block_size == 0); + unsigned int block_count = size / block_size; + + size_t usage_byte_count = (block_count + 7) / 8; + Allocator* allocator = malloc(sizeof(Allocator) + usage_byte_count - 1); + allocator->block_count = block_count; + allocator->block_size = block_size; + memset(allocator->block_usage, 0x00, usage_byte_count); + return allocator; +} + +void alloc_destroy(Allocator* allocator) { + free(allocator); +} + +unsigned int alloc_allocate(Allocator* allocator, unsigned int size) { + + unsigned int aligned_size = AlignUp(size, allocator->block_size); + unsigned int count = aligned_size / allocator->block_size; + + // Don't allow zero-size allocations (breaks algorithm) + assert(count > 0); + + //FIXME: `+ 2` is a hack, so we can recognize block splits + unsigned int block = find_free_blocks(allocator, count + 2) + 1; + + for(unsigned int i = 0; i < count; i++) { + block_mark_used(allocator, block + i, true); + } + + return block * allocator->block_size; +} + +void alloc_free(Allocator* allocator, unsigned int offset) { + + assert(offset % allocator->block_size == 0); + unsigned int block = offset / allocator->block_size; + + unsigned int used_blocks = count_blocks(allocator, block, true); + + // Avoid double-free + assert(used_blocks > 0); + + for(unsigned int i = 0; i < used_blocks; i++) { + block_mark_used(allocator, block + i, false); + } + +} diff --git a/alloc.h b/alloc.h new file mode 100644 index 0000000..9636f46 --- /dev/null +++ b/alloc.h @@ -0,0 +1,18 @@ +// Copyright 2019 OpenSWE1R Maintainers +// Licensed under GPLv2 or any later version +// Refer to the included LICENSE.txt file. + +#ifndef __OPENSWE1R_ALLOC_H__ +#define __OPENSWE1R_ALLOC_H__ + +#include +#include + +typedef struct _Allocator Allocator; + +Allocator* alloc_create(unsigned int size, unsigned int block_size); +void alloc_destroy(Allocator* allocator); +unsigned int alloc_allocate(Allocator* allocator, unsigned int size); +void alloc_free(Allocator* allocator, unsigned int offset); + +#endif diff --git a/emulation.c b/emulation.c index 2357cce..e2c828d 100644 --- a/emulation.c +++ b/emulation.c @@ -19,6 +19,8 @@ #include "emulation.h" #include "exe.h" +#include "alloc.h" + //FIXME: These are hacks (register when mapping instead!)! extern Exe* exe; uint8_t* stack = NULL; @@ -287,27 +289,27 @@ void* MapMemory(uint32_t address, uint32_t size, bool read, bool write, bool exe return memory; } +static Allocator* memoryAllocator = NULL; +static unsigned int memoryBlockSize = 0x1000; + Address Allocate(Size size) { - static uint32_t address = HEAP_ADDRESS; - uint32_t ret = address; - address += size; + if (memoryAllocator == NULL) { + memoryAllocator = alloc_create(heapSize, memoryBlockSize); + } + unsigned int offset = alloc_allocate(memoryAllocator, size); + Address ret = heapAddress + offset; + #if 1 // Debug memset to detect memory errors memset(Memory(ret), 0xDD, size); -#endif - //FIXME: Proper allocator - -#if 1 -//FIXME: This is a hack to fix alignment + to avoid too small allocations -address += 0x1000; -address &= 0xFFFFF000; #endif return ret; } void Free(Address address) { - //FIXME! + unsigned int offset = address - heapAddress; + alloc_free(memoryAllocator, offset); } void* Memory(uint32_t address) {