// block_alloc.dsa // Fixed-size block allocator // // Memory layout: // [base + 0]: free list head pointer (pointer to first free block, or 0 if none) // [base + 4]: block size // [base + 8]: total blocks // [base + 12]: base address of block pool // [base + 16+]: block pool (each block starts with a 4-byte next pointer) // // Usage: // include block_alloc "./lib/memory/block_alloc.dsa" // // For init: // push num_blocks (e.g., 32) // push block_size (e.g., 64 bytes) // call block_alloc::init // pop block_size // pop num_blocks // ; result in spr+8 (allocator handle) // // For alloc: // push allocator_handle // call block_alloc::alloc // pop allocator_handle // ; result in spr+8 (pointer to block, or 0 if out of memory) // // For free: // push block_pointer // push allocator_handle // call block_alloc::free // pop allocator_handle // pop block_pointer dw heap_start: 0x30000 // Start of our heap area // Initialize the allocator // Args: block_size, num_blocks // Returns: allocator handle (pointer to metadata) init: push bpr mov spr, bpr ldw bpr, rg0, 8 // block_size ldw bpr, rg1, 12 // num_blocks // Allocate metadata (16 bytes) + pool space ldw heap_start, rg2 // base address for this allocator mov rg2, rg3 // save base in rg3 // Calculate total size needed: 16 + (block_size * num_blocks) // We'll use a simple multiplication by repeated addition mov rg0, rg4 // block_size to rg4 mov rg1, rg5 // num_blocks to rg5 lli 0, acc // accumulator for multiplication _multiply_loop: cmp rg5, zero jeq _multiply_done add acc, rg4, acc dec rg5 jmp _multiply_loop _multiply_done: // acc now contains block_size * num_blocks addi acc, 16 // add metadata size // Update heap_start for next allocation add rg2, acc, acc stw acc, heap_start // Now set up metadata at rg3 (base) // [base + 0]: free list head (will point to first block) // [base + 4]: block_size // [base + 8]: total blocks // [base + 12]: pool base address addi rg3, 16, rg6 // rg6 = pool base stw rg6, rg3 // store pool base as free list head initially stw rg0, rg3, 4 // store block_size stw rg1, rg3, 8 // store total blocks stw rg6, rg3, 12 // store pool base address // Now initialize the free list // Each block's first 4 bytes point to the next block // rg6 = current block pointer // rg0 = block_size // rg1 = num_blocks (counter) dec rg1 // we'll count down from num_blocks-1 _init_loop: cmp rg1, zero jeq _init_loop_done // Calculate next block address: current + block_size add rg6, rg0, rg7 // rg7 = next block address // Store next pointer at current block stw rg7, rg6 // Move to next block mov rg7, rg6 dec rg1 jmp _init_loop _init_loop_done: // Last block points to null (0) lli 0, acc stw acc, rg6 // Return allocator handle (base address - 16 to get back to metadata start) stw rg3, bpr, 8 mov bpr, spr pop bpr return // Allocate a block // Args: allocator_handle // Returns: pointer to block (or 0 if out of memory) alloc: push bpr mov spr, bpr ldw bpr, rg0, 8 // allocator handle (metadata base) // Load free list head ldw rg0, rg1 // rg1 = free list head // Check if free list is empty cmp rg1, zero jeq _alloc_fail // Free list is not empty, pop the first block // Load the next pointer from the block we're allocating ldw rg1, rg2 // rg2 = next free block // Update free list head to point to next block stw rg2, rg0 // Return the allocated block (rg1) stw rg1, bpr, 8 jmp _alloc_done _alloc_fail: // No free blocks, return 0 lli 0, acc stw acc, bpr, 8 _alloc_done: mov bpr, spr pop bpr return // Free a block // Args: allocator_handle, block_pointer // Returns: nothing (but could return error code if block is invalid) free: push bpr mov spr, bpr ldw bpr, rg0, 8 // allocator handle ldw bpr, rg6, 12 // pointer to the block pointer to free ldw rg6, rg1 // rg1 = block pointer to free // Load current free list head ldw rg0, rg2 // rg2 = current head // Set the freed block's next pointer to current head stw rg2, rg1 // Update free list head to point to freed block stw rg1, rg0 // Update the freed block's previous pointer to NULL lli 0, rg1 stw rg1, rg6 mov bpr, spr pop bpr return // Debug function: get stats // Args: allocator_handle // Returns: nothing (but could populate a stats structure) get_stats: push bpr mov spr, bpr ldw bpr, rg0, 8 // allocator handle // Count free blocks by traversing the free list ldw rg0, rg1 // rg1 = free list head lli 0, rg2 // rg2 = counter count_loop: cmp rg1, zero jeq count_done inc rg2 ldw rg1, rg1 // move to next block jmp count_loop count_done: // rg2 now contains number of free blocks // Could store this somewhere or return it stw rg2, bpr, 8 mov bpr, spr pop bpr return