diff --git a/src/xenia/apu/audio_system.cc b/src/xenia/apu/audio_system.cc index b331be21a..d7734388d 100644 --- a/src/xenia/apu/audio_system.cc +++ b/src/xenia/apu/audio_system.cc @@ -78,9 +78,8 @@ X_STATUS AudioSystem::Setup() { reinterpret_cast(MMIOWriteRegisterThunk)); // Setup XMA contexts ptr. - registers_.xma_context_array_ptr = uint32_t( - memory()->HeapAlloc(0, kXmaContextSize * kXmaContextCount, - MEMORY_FLAG_PHYSICAL | MEMORY_FLAG_ZERO, 256)); + registers_.xma_context_array_ptr = memory()->SystemHeapAlloc( + kXmaContextSize * kXmaContextCount, 256, kSystemHeapPhysical); // Add all contexts to the free list. for (int i = kXmaContextCount - 1; i >= 0; --i) { xma_context_free_list_.push_back(registers_.xma_context_array_ptr + @@ -92,7 +91,7 @@ X_STATUS AudioSystem::Setup() { thread_state_ = new ThreadState(emulator_->processor()->runtime(), 0, 0, 16 * 1024, 0); thread_state_->set_name("Audio Worker"); - thread_block_ = (uint32_t)memory()->HeapAlloc(0, 2048, MEMORY_FLAG_ZERO); + thread_block_ = memory()->SystemHeapAlloc(2048); thread_state_->context()->r[13] = thread_block_; // Create worker thread. @@ -169,9 +168,9 @@ void AudioSystem::Shutdown() { thread_.join(); delete thread_state_; - memory()->HeapFree(thread_block_, 0); + memory()->SystemHeapFree(thread_block_); - memory()->HeapFree(registers_.xma_context_array_ptr, 0); + memory()->SystemHeapFree(registers_.xma_context_array_ptr); } uint32_t AudioSystem::AllocateXmaContext() { @@ -217,7 +216,7 @@ X_STATUS AudioSystem::RegisterClient(uint32_t callback, uint32_t callback_arg, unused_clients_.pop(); - uint32_t ptr = (uint32_t)memory()->HeapAlloc(0, 0x4, 0); + uint32_t ptr = memory()->SystemHeapAlloc(0x4); auto mem = memory()->membase(); poly::store_and_swap(mem + ptr, callback_arg); diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 7efbbddbb..13b62d3b3 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -55,7 +55,7 @@ Processor::Processor(xe::Memory* memory, ExportResolver* export_resolver) Processor::~Processor() { if (interrupt_thread_block_) { - memory_->HeapFree(interrupt_thread_block_, 2048); + memory_->SystemHeapFree(interrupt_thread_block_); delete interrupt_thread_state_; } @@ -98,7 +98,7 @@ int Processor::Setup() { interrupt_thread_state_ = new ThreadState(runtime_, 0, 0, 16 * 1024, 0); interrupt_thread_state_->set_name("Interrupt"); - interrupt_thread_block_ = memory_->HeapAlloc(0, 2048, MEMORY_FLAG_ZERO); + interrupt_thread_block_ = memory_->SystemHeapAlloc(2048); interrupt_thread_state_->context()->r[13] = interrupt_thread_block_; return 0; diff --git a/src/xenia/cpu/thread_state.cc b/src/xenia/cpu/thread_state.cc index 91c73050c..6c5f5fc93 100644 --- a/src/xenia/cpu/thread_state.cc +++ b/src/xenia/cpu/thread_state.cc @@ -41,7 +41,7 @@ ThreadState::ThreadState(Runtime* runtime, uint32_t thread_id, backend_data_ = runtime->backend()->AllocThreadData(); if (!stack_address) { - stack_address_ = memory()->HeapAlloc(0, stack_size, MEMORY_FLAG_ZERO); + stack_address_ = memory()->SystemHeapAlloc(stack_size); stack_allocated_ = true; } else { stack_address_ = stack_address; @@ -86,7 +86,7 @@ ThreadState::~ThreadState() { free(context_); if (stack_allocated_) { - memory()->HeapFree(stack_address_, stack_size_); + memory()->SystemHeapFree(stack_address_); } } diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index d73a69d74..d92fffaff 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -78,15 +78,9 @@ XThread::~XThread() { if (thread_state_) { delete thread_state_; } - if (scratch_address_) { - kernel_state()->memory()->HeapFree(scratch_address_, 0); - } - if (tls_address_) { - kernel_state()->memory()->HeapFree(tls_address_, 0); - } - if (thread_state_address_) { - kernel_state()->memory()->HeapFree(thread_state_address_, 0); - } + kernel_state()->memory()->SystemHeapFree(scratch_address_); + kernel_state()->memory()->SystemHeapFree(tls_address_); + kernel_state()->memory()->SystemHeapFree(thread_state_address_); if (thread_handle_) { // TODO(benvanik): platform kill @@ -151,8 +145,7 @@ X_STATUS XThread::Create() { // 0x160: last error // So, at offset 0x100 we have a 4b pointer to offset 200, then have the // structure. - thread_state_address_ = - (uint32_t)memory()->HeapAlloc(0, 2048, MEMORY_FLAG_ZERO); + thread_state_address_ = memory()->SystemHeapAlloc(2048); if (!thread_state_address_) { XELOGW("Unable to allocate thread state block"); return X_STATUS_NO_MEMORY; @@ -166,13 +159,12 @@ X_STATUS XThread::Create() { // Allocate thread scratch. // This is used by interrupts/APCs/etc so we can round-trip pointers through. scratch_size_ = 4 * 16; - scratch_address_ = - (uint32_t)memory()->HeapAlloc(0, scratch_size_, MEMORY_FLAG_ZERO); + scratch_address_ = memory()->SystemHeapAlloc(scratch_size_); // Allocate TLS block. const xe_xex2_header_t* header = module->xex_header(); uint32_t tls_size = header->tls_info.slot_count * header->tls_info.data_size; - tls_address_ = (uint32_t)memory()->HeapAlloc(0, tls_size, MEMORY_FLAG_ZERO); + tls_address_ = memory()->SystemHeapAlloc(tls_size); if (!tls_address_) { XELOGW("Unable to allocate thread local storage block"); module->Release(); @@ -421,9 +413,8 @@ void XThread::DeliverAPCs(void* data) { // kernel_routine(apc_address, &normal_routine, &normal_context, // &system_arg1, &system_arg2) uint64_t kernel_args[] = { - apc_address, thread->scratch_address_ + 0, - thread->scratch_address_ + 4, thread->scratch_address_ + 8, - thread->scratch_address_ + 12, + apc_address, thread->scratch_address_ + 0, thread->scratch_address_ + 4, + thread->scratch_address_ + 8, thread->scratch_address_ + 12, }; processor->ExecuteInterrupt(0, kernel_routine, kernel_args, poly::countof(kernel_args)); diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 366b3a987..447e044e3 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -23,9 +23,7 @@ XUserModule::XUserModule(KernelState* kernel_state, const char* path) : XModule(kernel_state, path), xex_(nullptr), execution_info_ptr_(0) {} XUserModule::~XUserModule() { - if (execution_info_ptr_) { - kernel_state()->memory()->HeapFree(execution_info_ptr_, 0); - } + kernel_state()->memory()->SystemHeapFree(execution_info_ptr_); xe_xex2_dealloc(xex_); } @@ -116,8 +114,7 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { // Store execution info for later use. // TODO(benvanik): just put entire xex header in memory somewhere? - execution_info_ptr_ = - uint32_t(kernel_state()->memory()->HeapAlloc(0, 24, MEMORY_FLAG_ZERO, 0)); + execution_info_ptr_ = kernel_state()->memory()->SystemHeapAlloc(24); auto eip = kernel_state()->memory()->membase() + execution_info_ptr_; const auto& ex = xe_xex2_get_header(xex_)->execution_info; poly::store_and_swap(eip + 0x00, ex.media_id); diff --git a/src/xenia/kernel/xam_info.cc b/src/xenia/kernel/xam_info.cc index 742627283..fd41fc4c4 100644 --- a/src/xenia/kernel/xam_info.cc +++ b/src/xenia/kernel/xam_info.cc @@ -127,8 +127,8 @@ SHIM_CALL XamAlloc_shim(PPCContext* ppc_state, KernelState* state) { // Allocate from the heap. Not sure why XAM does this specially, perhaps // it keeps stuff in a separate heap? - uint64_t ptr = state->memory()->HeapAlloc(0, size, MEMORY_FLAG_ZERO); - SHIM_SET_MEM_32(out_ptr, uint32_t(ptr)); + uint32_t ptr = state->memory()->SystemHeapAlloc(size); + SHIM_SET_MEM_32(out_ptr, ptr); SHIM_SET_RETURN_32(X_ERROR_SUCCESS); } @@ -138,7 +138,7 @@ SHIM_CALL XamFree_shim(PPCContext* ppc_state, KernelState* state) { XELOGD("XamFree(%.8X)", ptr); - state->memory()->HeapFree(ptr, 0); + state->memory()->SystemHeapFree(ptr); SHIM_SET_RETURN_32(X_ERROR_SUCCESS); } diff --git a/src/xenia/kernel/xboxkrnl_memory.cc b/src/xenia/kernel/xboxkrnl_memory.cc index 8926ee127..fcd80f1a6 100644 --- a/src/xenia/kernel/xboxkrnl_memory.cc +++ b/src/xenia/kernel/xboxkrnl_memory.cc @@ -418,8 +418,7 @@ SHIM_CALL ExAllocatePoolTypeWithTag_shim(PPCContext* ppc_state, alignment = 4 * 1024; } - uint32_t addr = (uint32_t)state->memory()->HeapAlloc( - 0, adjusted_size, MEMORY_FLAG_ZERO, alignment); + uint32_t addr = state->memory()->SystemHeapAlloc(adjusted_size, alignment); SHIM_SET_RETURN_32(addr); } @@ -429,7 +428,7 @@ SHIM_CALL ExFreePool_shim(PPCContext* ppc_state, KernelState* state) { XELOGD("ExFreePool(%.8X)", base_address); - state->memory()->HeapFree(base_address, 0); + state->memory()->SystemHeapFree(base_address); } SHIM_CALL KeLockL2_shim(PPCContext* ppc_state, KernelState* state) { diff --git a/src/xenia/kernel/xboxkrnl_module.cc b/src/xenia/kernel/xboxkrnl_module.cc index a7d336a32..0c76f70e7 100644 --- a/src/xenia/kernel/xboxkrnl_module.cc +++ b/src/xenia/kernel/xboxkrnl_module.cc @@ -50,14 +50,14 @@ XboxkrnlModule::XboxkrnlModule(Emulator* emulator, KernelState* kernel_state) // Set to a valid value when a remote debugger is attached. // Offset 0x18 is a 4b pointer to a handler function that seems to take two // arguments. If we wanted to see what would happen we could fake that. - uint32_t pKeDebugMonitorData = (uint32_t)memory_->HeapAlloc(0, 256, 0); + uint32_t pKeDebugMonitorData = memory_->SystemHeapAlloc(256); export_resolver_->SetVariableMapping( "xboxkrnl.exe", ordinals::KeDebugMonitorData, pKeDebugMonitorData); poly::store_and_swap(mem + pKeDebugMonitorData, 0); // KeCertMonitorData (?*) // Always set to zero, ignored. - uint32_t pKeCertMonitorData = (uint32_t)memory_->HeapAlloc(0, 4, 0); + uint32_t pKeCertMonitorData = memory_->SystemHeapAlloc(4); export_resolver_->SetVariableMapping( "xboxkrnl.exe", ordinals::KeCertMonitorData, pKeCertMonitorData); poly::store_and_swap(mem + pKeCertMonitorData, 0); @@ -70,7 +70,7 @@ XboxkrnlModule::XboxkrnlModule(Emulator* emulator, KernelState* kernel_state) // // aomega08 says the value is 0x02000817, bit 27: debug mode on. // When that is set, though, allocs crash in weird ways. - uint32_t pXboxHardwareInfo = (uint32_t)memory_->HeapAlloc(0, 16, 0); + uint32_t pXboxHardwareInfo = memory_->SystemHeapAlloc(16); export_resolver_->SetVariableMapping( "xboxkrnl.exe", ordinals::XboxHardwareInfo, pXboxHardwareInfo); poly::store_and_swap(mem + pXboxHardwareInfo + 0, 0); // flags @@ -87,11 +87,11 @@ XboxkrnlModule::XboxkrnlModule(Emulator* emulator, KernelState* kernel_state) // 0x80101000 <- our module structure // 0x80101058 <- pointer to xex header // 0x80101100 <- xex header base - uint32_t ppXexExecutableModuleHandle = (uint32_t)memory_->HeapAlloc(0, 4, 0); + uint32_t ppXexExecutableModuleHandle = memory_->SystemHeapAlloc(4); export_resolver_->SetVariableMapping("xboxkrnl.exe", ordinals::XexExecutableModuleHandle, ppXexExecutableModuleHandle); - uint32_t pXexExecutableModuleHandle = (uint32_t)memory_->HeapAlloc(0, 256, 0); + uint32_t pXexExecutableModuleHandle = memory_->SystemHeapAlloc(256); poly::store_and_swap(mem + ppXexExecutableModuleHandle, pXexExecutableModuleHandle); poly::store_and_swap(mem + pXexExecutableModuleHandle + 0x58, @@ -101,7 +101,7 @@ XboxkrnlModule::XboxkrnlModule(Emulator* emulator, KernelState* kernel_state) // The name of the xex. Not sure this is ever really used on real devices. // Perhaps it's how swap disc/etc data is sent? // Always set to "default.xex" (with quotes) for now. - uint32_t pExLoadedCommandLine = (uint32_t)memory_->HeapAlloc(0, 1024, 0); + uint32_t pExLoadedCommandLine = memory_->SystemHeapAlloc(1024); export_resolver_->SetVariableMapping( "xboxkrnl.exe", ordinals::ExLoadedCommandLine, pExLoadedCommandLine); char command_line[] = "\"default.xex\""; @@ -111,7 +111,7 @@ XboxkrnlModule::XboxkrnlModule(Emulator* emulator, KernelState* kernel_state) // XboxKrnlVersion (8b) // Kernel version, looks like 2b.2b.2b.2b. // I've only seen games check >=, so we just fake something here. - uint32_t pXboxKrnlVersion = (uint32_t)memory_->HeapAlloc(0, 8, 0); + uint32_t pXboxKrnlVersion = memory_->SystemHeapAlloc(8); export_resolver_->SetVariableMapping( "xboxkrnl.exe", ordinals::XboxKrnlVersion, pXboxKrnlVersion); poly::store_and_swap(mem + pXboxKrnlVersion + 0, 2); @@ -123,23 +123,22 @@ XboxkrnlModule::XboxkrnlModule(Emulator* emulator, KernelState* kernel_state) // KeTimeStampBundle (ad) // This must be updated during execution, at 1ms intevals. // We setup a system timer here to do that. - uint32_t pKeTimeStampBundle = (uint32_t)memory_->HeapAlloc(0, 24, 0); + uint32_t pKeTimeStampBundle = memory_->SystemHeapAlloc(24); export_resolver_->SetVariableMapping( "xboxkrnl.exe", ordinals::KeTimeStampBundle, pKeTimeStampBundle); poly::store_and_swap(mem + pKeTimeStampBundle + 0, 0); poly::store_and_swap(mem + pKeTimeStampBundle + 8, 0); poly::store_and_swap(mem + pKeTimeStampBundle + 16, GetTickCount()); poly::store_and_swap(mem + pKeTimeStampBundle + 20, 0); - CreateTimerQueueTimer(×tamp_timer_, nullptr, - [](PVOID param, BOOLEAN timer_or_wait_fired) { - auto timestamp_bundle = - reinterpret_cast(param); - poly::store_and_swap(timestamp_bundle + 16, - GetTickCount()); - }, - mem + pKeTimeStampBundle, 0, - 1, // 1ms - WT_EXECUTEINTIMERTHREAD); + CreateTimerQueueTimer( + ×tamp_timer_, nullptr, + [](PVOID param, BOOLEAN timer_or_wait_fired) { + auto timestamp_bundle = reinterpret_cast(param); + poly::store_and_swap(timestamp_bundle + 16, GetTickCount()); + }, + mem + pKeTimeStampBundle, 0, + 1, // 1ms + WT_EXECUTEINTIMERTHREAD); } void XboxkrnlModule::RegisterExportTable(ExportResolver* export_resolver) { @@ -149,14 +148,14 @@ void XboxkrnlModule::RegisterExportTable(ExportResolver* export_resolver) { return; } - // Build the export table used for resolution. +// Build the export table used for resolution. #include "xenia/kernel/util/export_table_pre.inc" static KernelExport xboxkrnl_export_table[] = { #include "xenia/kernel/xboxkrnl_table.inc" }; #include "xenia/kernel/util/export_table_post.inc" export_resolver->RegisterTable("xboxkrnl.exe", xboxkrnl_export_table, - poly::countof(xboxkrnl_export_table)); + poly::countof(xboxkrnl_export_table)); } XboxkrnlModule::~XboxkrnlModule() { diff --git a/src/xenia/kernel/xboxkrnl_rtl.cc b/src/xenia/kernel/xboxkrnl_rtl.cc index 1b75e50fd..9089aa893 100644 --- a/src/xenia/kernel/xboxkrnl_rtl.cc +++ b/src/xenia/kernel/xboxkrnl_rtl.cc @@ -205,7 +205,7 @@ SHIM_CALL RtlFreeAnsiString_shim(PPCContext* ppc_state, KernelState* state) { return; } uint32_t length = SHIM_MEM_16(string_ptr + 2); - state->memory()->HeapFree(buffer, length); + state->memory()->SystemHeapFree(buffer); SHIM_SET_MEM_16(string_ptr + 0, 0); SHIM_SET_MEM_16(string_ptr + 2, 0); @@ -260,7 +260,7 @@ SHIM_CALL RtlFreeUnicodeString_shim(PPCContext* ppc_state, KernelState* state) { return; } uint32_t length = SHIM_MEM_16(string_ptr + 2); - state->memory()->HeapFree(buffer, length); + state->memory()->SystemHeapFree(buffer); SHIM_SET_MEM_16(string_ptr + 0, 0); SHIM_SET_MEM_16(string_ptr + 2, 0); @@ -292,8 +292,8 @@ SHIM_CALL RtlUnicodeStringToAnsiString_shim(PPCContext* ppc_state, X_STATUS result = X_STATUS_SUCCESS; if (alloc_dest) { - auto buffer_ptr = - state->memory()->HeapAlloc(0, uint32_t(ansi_str.size() + 1), 0); + uint32_t buffer_ptr = + state->memory()->SystemHeapAlloc(uint32_t(ansi_str.size() + 1)); memcpy(SHIM_MEM_ADDR(buffer_ptr), ansi_str.data(), ansi_str.size() + 1); SHIM_SET_MEM_16(destination_ptr + 0, static_cast(ansi_str.size())); diff --git a/src/xenia/kernel/xboxkrnl_video.cc b/src/xenia/kernel/xboxkrnl_video.cc index ee29620e5..087fda8dd 100644 --- a/src/xenia/kernel/xboxkrnl_video.cc +++ b/src/xenia/kernel/xboxkrnl_video.cc @@ -452,28 +452,32 @@ void xe::kernel::xboxkrnl::RegisterVideoExports(ExportResolver* export_resolver, // VdGlobalDevice (4b) // Pointer to a global D3D device. Games only seem to set this, so we don't // have to do anything. We may want to read it back later, though. - uint32_t pVdGlobalDevice = (uint32_t)memory->HeapAlloc(0, 4, 0); + uint32_t pVdGlobalDevice = + memory->SystemHeapAlloc(4, 32, kSystemHeapPhysical); export_resolver->SetVariableMapping("xboxkrnl.exe", ordinals::VdGlobalDevice, pVdGlobalDevice); poly::store_and_swap(mem + pVdGlobalDevice, 0); // VdGlobalXamDevice (4b) // Pointer to the XAM D3D device, which we don't have. - uint32_t pVdGlobalXamDevice = (uint32_t)memory->HeapAlloc(0, 4, 0); + uint32_t pVdGlobalXamDevice = + memory->SystemHeapAlloc(4, 32, kSystemHeapPhysical); export_resolver->SetVariableMapping( "xboxkrnl.exe", ordinals::VdGlobalXamDevice, pVdGlobalXamDevice); poly::store_and_swap(mem + pVdGlobalXamDevice, 0); // VdGpuClockInMHz (4b) // GPU clock. Xenos is 500MHz. Hope nothing is relying on this timing... - uint32_t pVdGpuClockInMHz = (uint32_t)memory->HeapAlloc(0, 4, 0); + uint32_t pVdGpuClockInMHz = + memory->SystemHeapAlloc(4, 32, kSystemHeapPhysical); export_resolver->SetVariableMapping("xboxkrnl.exe", ordinals::VdGpuClockInMHz, pVdGpuClockInMHz); poly::store_and_swap(mem + pVdGpuClockInMHz, 500); // VdHSIOCalibrationLock (28b) // CriticalSection. - uint32_t pVdHSIOCalibrationLock = (uint32_t)memory->HeapAlloc(0, 28, 0); + uint32_t pVdHSIOCalibrationLock = + memory->SystemHeapAlloc(28, 32, kSystemHeapPhysical); export_resolver->SetVariableMapping( "xboxkrnl.exe", ordinals::VdHSIOCalibrationLock, pVdHSIOCalibrationLock); auto hsio_lock = diff --git a/src/xenia/memory.cc b/src/xenia/memory.cc index 0b1a3e88d..a82fcfbc7 100644 --- a/src/xenia/memory.cc +++ b/src/xenia/memory.cc @@ -267,18 +267,18 @@ void Memory::UnmapViews() { void Memory::Zero(uint32_t address, uint32_t size) { uint8_t* p = membase_ + address; - memset(p, 0, size); + std::memset(p, 0, size); } void Memory::Fill(uint32_t address, uint32_t size, uint8_t value) { uint8_t* p = membase_ + address; - memset(p, value, size); + std::memset(p, value, size); } void Memory::Copy(uint32_t dest, uint32_t src, uint32_t size) { uint8_t* pdest = membase_ + dest; const uint8_t* psrc = membase_ + src; - memcpy(pdest, psrc, size); + std::memcpy(pdest, psrc, size); } uint32_t Memory::SearchAligned(uint32_t start, uint32_t end, @@ -328,6 +328,25 @@ void Memory::CancelWriteWatch(uintptr_t watch_handle) { mmio_handler_->CancelWriteWatch(watch_handle); } +uint32_t Memory::SystemHeapAlloc(uint32_t size, uint32_t alignment, + uint32_t system_heap_flags) { + // TODO(benvanik): lightweight pool. + bool is_physical = !!(system_heap_flags & kSystemHeapPhysical); + uint32_t flags = MEMORY_FLAG_ZERO; + if (is_physical) { + flags |= MEMORY_FLAG_PHYSICAL; + } + return HeapAlloc(0, size, flags, alignment); +} + +void Memory::SystemHeapFree(uint32_t address) { + if (!address) { + return; + } + // TODO(benvanik): lightweight pool. + HeapFree(address, 0); +} + uint32_t Memory::HeapAlloc(uint32_t base_address, uint32_t size, uint32_t flags, uint32_t alignment) { // If we were given a base address we are outside of the normal heap and diff --git a/src/xenia/memory.h b/src/xenia/memory.h index fd4debea1..86014dfc6 100644 --- a/src/xenia/memory.h +++ b/src/xenia/memory.h @@ -21,6 +21,12 @@ namespace xe { +enum SystemHeapFlag : uint32_t { + kSystemHeapVirtual = 1 << 0, + kSystemHeapPhysical = 1 << 1, + + kSystemHeapDefault = kSystemHeapVirtual, +}; class MemoryHeap; // TODO(benvanik): move to heap. @@ -75,6 +81,9 @@ class Memory { void* callback_context, void* callback_data); void CancelWriteWatch(uintptr_t watch_handle); + uint32_t SystemHeapAlloc(uint32_t size, uint32_t alignment = 0x20, + uint32_t system_heap_flags = kSystemHeapDefault); + void SystemHeapFree(uint32_t address); uint32_t HeapAlloc(uint32_t base_address, uint32_t size, uint32_t flags, uint32_t alignment = 0x20); int HeapFree(uint32_t address, uint32_t size);