format ELF section '.text' executable public malloc public free public realloc public mf_init ;multithread: ;uncomment this for thread-safe version ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Memory allocator for MenuetOS ;; ;; Halyavin Andrey halyavin@land.ru, 2006 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; allocated mem block structure ;; ;; +0: bit 0 - used flag ;; ;; bits 31..1 - block size ;; ;; +4: address of prev block ;; ;; +8 .. +(blocksize) - allocated memory ;; ;; +(blocksize) - next block ;; ;; ;; ;; free mem block structure ;; ;; +0: bit 0 - used flag ;; ;; bits 31..1 - block size ;; ;; +4: address of prev block ;; ;; +8: prev free block ;; ;; +12: next free block ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; memblock.size=0 memblock.prevblock=4 memblock.prevfreeblock=8 memblock.nextfreeblock=12 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; mf_init ;; ;; Initialize memory map for dynamic use ;; ;; input: eax: starting address or 0 ;; ;; output: none ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mf_init: push ebx push ecx test eax,eax jnz .noautodet sub esp,1024 mov ebx,esp mov ecx,-1 mov eax,9 int 0x40 mov eax,[esp+26] add esp,1024 .noautodet: add eax,15 and eax,not 15 mov [heapsmallblocks],eax add eax,2048 mov [heapstart],eax mov [heapfreelist],eax mov [heaplastblock],eax mov ecx,eax if defined heapstartsize add ecx,heapstartsize else add ecx,4096 end if add ecx,4095 and ecx,not 4095 push eax mov eax,64 mov ebx,1 int 0x40 pop eax mov [eax+memblock.prevblock],dword 0 mov [heapend],ecx mov [eax+memblock.size],ecx sub [eax+memblock.size],eax xor ebx,ebx mov dword [eax+memblock.prevfreeblock],heapfreelist-memblock.nextfreeblock mov [eax+memblock.nextfreeblock],ebx mov [heapmutex],ebx push edi mov edi,[heapsmallblocks] mov ecx,512 xor eax,eax rep stosd pop edi pop ecx pop ebx ret if defined multithread heaplock: push eax push ebx push ecx mov eax,68 mov ebx,1 .loop: xchg ecx,[heapmutex] test ecx,ecx jz .endloop int 0x40 ;change task jmp .loop .endloop: pop ecx pop ebx pop eax ret heapunlock: mov [heapmutex],dword 0 ret end if ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; heap_split_block ;; ;; Split free block to allocated block and free one. ;; ;; input: ;; ;; eax - size of allocated block ;; ;; ebx - block ;; ;; output: ;; ;; eax - real size of allocated block ;; ;; ebx - pointer to new block ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; heap_split_block: push ecx mov ecx,[ebx+memblock.size] sub ecx,16 cmp ecx,eax jge .norm inc dword [ebx+memblock.size] mov eax,ecx xor ebx,ebx pop ecx ret .norm: add ecx,16 mov [ebx+memblock.size],eax inc dword [ebx+memblock.size] mov [ebx+eax+memblock.prevblock],ebx add ebx,eax sub ecx,eax mov [ebx+memblock.size],ecx mov ecx,eax mov eax,ebx call heap_fix_right mov eax,ecx pop ecx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; heap_add_free_block ;; ;; Add free block to one of free block lists. ;; ;; input: ;; ;; eax - address of free block ;; ;; output: ;; ;; none ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; heap_add_free_block: cmp dword [eax+memblock.size],4096 push ebx jge .bigblock mov ebx,[eax+memblock.size] shr ebx,1 add ebx,[heapsmallblocks] push dword [ebx] pop dword [eax+memblock.nextfreeblock] mov [ebx],eax mov dword [eax+memblock.prevfreeblock],ebx sub dword [eax+memblock.prevfreeblock],memblock.nextfreeblock mov ebx,[eax+memblock.nextfreeblock] test ebx,ebx jz .no_next_block mov [ebx+memblock.prevfreeblock],eax .no_next_block: pop ebx ret .bigblock: mov ebx,[heapfreelist] mov [eax+memblock.nextfreeblock],ebx mov [heapfreelist],eax mov dword [eax+memblock.prevfreeblock],heapfreelist-memblock.nextfreeblock ; mov ebx,[eax+memblock.nextfreeblock] test ebx,ebx jz .no_next_big_block mov [ebx+memblock.prevfreeblock],eax .no_next_big_block: pop ebx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; heap_remove_block ;; ;; Remove free block from the list of free blocks. ;; ;; input: ;; ;; eax - free block ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; heap_remove_block: push ebx push ecx mov ecx,[eax+memblock.prevfreeblock] mov ebx,[eax+memblock.nextfreeblock] mov [ecx+memblock.nextfreeblock],ebx test ebx,ebx jz .no_next_block mov [ebx+memblock.prevfreeblock],ecx .no_next_block: pop ecx pop ebx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; mf_alloc ;; allocates a block of memory in heap ;; intput: eax: size of block ;; output: eax: address of allocated memory block or 0 if there's no mem. ;; allocator will not create new nodes that contain less that 8b of space, ;; and minimal allocation is actually 16 bytes - 8 for node and 8 for user. ;; allocator will never create non-aligned memory blocks. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mf_alloc: test eax,eax jg .not_null ; test that we are not allocating null size block xor eax,eax ret .not_null: if defined multithread call heaplock end if push edi ; push edx push ecx push ebx add eax,7 and eax,not 7 ; make sure that block size is aligned lea edi,[eax+8] ; desired block size cmp edi,4096 jge .general_cycle mov ebx,[heapsmallblocks] xor ecx,ecx shr edi,1 .smallloop: cmp [ebx+edi],ecx jnz .smallblockfound add edi,4 cmp edi,2048 jl .smallloop lea edi,[eax+8] jmp .general_cycle .smallblockfound: lea ecx,[eax+8] mov eax,[ebx+edi] call heap_remove_block mov ebx,eax xchg eax,ecx call heap_split_block test ebx,ebx jz .perfect_small_block mov eax,ebx call heap_add_free_block .perfect_small_block: lea eax,[ecx+8] jmp .ret .general_cycle: ;edi - size needed mov eax,[heapfreelist] .loop: test eax,eax jz .new_mem cmp [eax+memblock.size],edi jge .blockfound mov eax,[eax+memblock.nextfreeblock] jmp .loop .blockfound: call heap_remove_block mov ebx,eax mov ecx,eax mov eax,edi call heap_split_block test ebx,ebx jz .perfect_block mov eax,ebx call heap_add_free_block .perfect_block: lea eax,[ecx+8] .ret: if defined multithread call heapunlock end if pop ebx pop ecx ; pop edx pop edi ret .new_mem: mov eax,edi add eax,4095 and eax,not 4095 mov ecx,[heapend] add [heapend],eax push eax mov eax,64 push ebx push ecx mov ecx,[heapend] mov ebx,1 int 0x40 pop ecx pop ebx pop eax mov [ecx+memblock.size],eax mov eax,[heaplastblock] mov [ecx+memblock.prevblock],eax mov [heaplastblock],ecx mov eax,ecx call heap_add_free_block jmp .general_cycle ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; heap_fix_right ;; ;; input: ;; ;; eax - pointer to free block ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; heap_fix_right: push ebx mov ebx,eax add ebx,[eax+memblock.size] cmp ebx,[heapend] jz .endblock mov [ebx+memblock.prevblock],eax pop ebx ret .endblock: mov [heaplastblock],eax pop ebx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; heap_merge_left ;; ;; input: ;; ;; eax - pointer to free block ;; ;; output: ;; ;; eax - pointer to merged block ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; heap_merge_left: push ebx mov ebx,[eax+memblock.prevblock] test ebx,ebx jz .ret test byte [ebx+memblock.size],1 jnz .ret xchg eax,ebx call heap_remove_block mov ebx,[ebx+memblock.size] add [eax+memblock.size],ebx call heap_fix_right .ret: pop ebx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; heap_merge_right ;; ;; input: ;; ;; eax - pointer to free block ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; heap_merge_right: push ebx mov ebx,eax add ebx,[eax+memblock.size] cmp ebx,[heapend] jz .ret test byte [ebx+memblock.size],1 jnz .ret xchg eax,ebx call heap_remove_block xchg eax,ebx mov ebx,[ebx+memblock.size] add [eax+memblock.size],ebx call heap_fix_right .ret: pop ebx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; mf_free ;; ;; input: ;; ;; eax - pointer ;; ;; output: ;; ;; eax=1 - ok ;; ;; eax=0 - failed ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mf_free: test eax,eax jnz .no_null inc eax ret .no_null: if defined multithread call heaplock end if sub eax,8 dec dword [eax+memblock.size] call heap_merge_left call heap_merge_right call heap_add_free_block .ret: if defined multithread call heapunlock end if ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; heap_try_reloc ;; input: ;; eax - address ;; ebx - new size ;; output: ;; ebx=1 - ok ;; ebx=0 - failed ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; heap_try_reloc: push eax sub eax,8 add ebx,15 dec dword [eax+memblock.size] and ebx,not 7 cmp [eax+memblock.size],ebx jge .truncate push ebx mov ebx,eax add ebx,[eax+memblock.size] cmp ebx,[heapend] jz .fail ;todo: we can allocate new mem here test [ebx+memblock.size],byte 1 jnz .fail xchg eax,ebx call heap_remove_block mov eax,[eax+memblock.size] add [ebx+memblock.size],eax mov eax,ebx call heap_fix_right pop ebx .truncate: xchg eax,ebx call heap_split_block test ebx,ebx jz .no_last_block mov eax,ebx call heap_add_free_block call heap_merge_right .no_last_block: xor ebx,ebx pop eax inc ebx ret .fail: pop ebx xor ebx,ebx pop eax inc dword [eax-8+memblock.size] ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; mf_realloc ;; input: ;; eax - pointer ;; ebx - new size ;; output: ;; eax - new pointer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mf_realloc: push ebx if defined multithread call heaplock end if call heap_try_reloc test ebx,ebx jnz .ret ;allocate new memory push eax mov eax,[esp+4] call mf_alloc test eax,eax jz .fail push esi push edi push ecx mov edi,eax mov esi,[esp+12] mov ecx,[esi-8+memblock.size] shr ecx,2 rep movsd pop ecx pop edi pop esi xchg eax,[esp] call mf_free .fail: pop eax .ret: if defined multithread call heapunlock end if pop ebx ret ;C entries malloc: mov eax,[esp+4] call mf_alloc ret free: mov eax,[esp+4] call mf_free ret realloc: mov edx,ebx mov eax,[esp+4] mov ebx,[esp+8] call mf_realloc mov ebx,edx ret section '.bss' writeable heapsmallblocks rd 1 heapstart rd 1 heapend rd 1 heapfreelist rd 1 heapmutex rd 1 heaplastblock rd 1