;MM_ = MemoryManager
;Этот модуль позволяет выделять память маленькими кусочками, оптимально используя
;страницы памяти
;Блоки - это одна или несколько страниц, которые имеют запись
;в MM_BlockInfo и которые в конце имеют стекообразную структуру(в смысле
;растёт к меньшим адресам), заканчивающейся dword 0. В начале блока
;находятся данные. В структуре находятся пары dword'ов: начало
;участков, их конец(посл. байт+1). Эти пары всегда сортируются по
;расположению описываемых
;участков в обратном порядке, т.е. в самом конце блока будет пара данных
;на самый первый участок. Для выделения памяти нужно найти блок, в котором
;достаточно места (причём необходимое место = запрашиваемый объём + место
;под пару с данными в конце блока) и вставить пару с нужными данными.
;Для удаления участка нужно только убрать из пар пару с нужным участком
;и поправить расположение остальных пар.
;begData1.....endData1,begData2...endData2,.......0,beg2,end2,beg1,end1
;выделяет память
;return eax = указатель на выделеный блок
;proc MM_AllocMem stdcall,Size:DWORD
;БАГ в выделении крупных кусков: всегда выделяет новую страницу
align 4
MM_AllocMem:
Size equ ebp+8
begFree equ ebp-8 ;начало
endFree equ ebp-4 ;и конец свободного места от конца инфы до
endArea equ ebp-12
push ebp
mov ebp,esp
add esp,-4*3
push ebx edi esi
;dps 'MEM: '
;dph [Size]
;dps ' '
;начала пар записей
mov edx,[MM_NBlocks]
cmp edx,0
jne .BegTestBlocks ;если блоков нет, то добавить новый
mov ecx,[Size]
call AddBlock
jmp .return
align 4
.BegTestBlocks:
xor ebx,ebx
mov ecx,edx
align 4
.TestBlock: ;проверка блока
;проверка: есть ли место для ещё одной пары
mov edi,[MM_BlocksInfo+ebx]
add edi,[MM_BlocksInfo+ebx+4]
mov [endArea], edi
sub edi,4
cmp dword[edi],0 ;если в блоке нет ни одной записи
jne .NoEmptyBlock
mov eax,[MM_BlocksInfo+ebx]
mov dword[edi-4],eax
mov dword[edi],eax
mov edx,[Size]
add dword[edi],edx
mov dword[edi-8],0
jmp .return
align 4
.NoEmptyBlock:
xor eax,eax
push ecx
or ecx,-1
std
repne scasd
cld
pop ecx
mov eax,[edi+12] ;конец посл участка
add eax,4
cmp eax,edi
jb @f
add ebx,8
dec ecx
jnz .TestBlock
mov ecx,[Size]
call AddBlock
jmp .return
@@:
mov [begFree],eax ;eax = конец посл. участка + 4
mov [endFree],edi ;edi = указатель на конец посл участка - 12
sub dword[begFree],4
add dword[endFree],12
;проверка перед всеми участками
mov edi,[MM_BlocksInfo+ebx]
mov eax,[endArea]
mov eax,[eax-8]
sub eax,[MM_BlocksInfo+ebx]
cmp eax,[Size]
ja .AddInBegBlock
;проверка между участками
mov eax,[endArea]
cmp dword[eax-12],0
je .EndTest ;если в блоке только 1 участок
sub eax,4
@@:
mov edi,[eax-12]
sub edi,[eax]
cmp edi,[Size]
jae .AddInMiddle
sub eax,8
cmp dword[eax-8],0
jne @b
.EndTest:
;проверка после всех блоков
mov eax,[begFree]
mov edi,[endFree]
lea esi,[edi-8] ;8 - место под запись
sub esi,eax
cmp esi,[Size]
ja .AddInEnd
add ebx,8
dec ecx
jnz .TestBlock
mov ecx,[Size]
call AddBlock
jmp .return
align 4
.AddInBegBlock: ;Добавить в начало. В edi начало блока
;pop eax
mov eax,edi
add eax,[MM_BlocksInfo+ebx+4]
sub eax,4
push eax
call MoveRecordsLeft
pop eax
mov [eax-4],edi
push edi
add edi,[Size]
mov [eax],edi
pop eax
jmp .return
align 4
.AddInMiddle: ;Добавить между участками, еах=конец участка перед свободным местом
;pop ecx ;add esp,4
push eax
sub eax,8
call MoveRecordsLeft
pop eax
mov edx,[eax]
mov [eax-12],edx
add edx,[Size]
mov [eax-8],edx
mov eax,[eax]
jmp .return
align 4
.AddInEnd: ;Добавить после участков. еdi=указатель на 2ой элем пары с инфой о посл участке
;add esp,4
mov eax,[edi]
mov [edi-12],eax
push eax
add eax,[Size]
mov [edi-8],eax
pop eax
.return:
pop esi edi ebx
leave
ret 4
restore Xren
restore Size
restore begFree
restore endFree
;eax - первый сдвигаемый dword
;сдвигает пары dword'ов на 8B назад включая dword 0
align 4
proc MoveRecordsLeft
local var1:DWORD,\
var2:DWORD
p2p [var1],[eax]
p2p [var2],[eax-4]
@@:
sub eax,8
cmp dword[var1],0
je @f
push dword[eax]
p2p [eax],[var1]
pop dword[var1]
push dword[eax-4]
p2p [eax-4],[var2]
pop dword[var2]
jmp @b
@@:
mov dword[eax],0
ret
endp
;ecx = размер требуемого участка
;добавляет блок и создаёт в нём участок размером ecx
align 4
proc AddBlock
mov edx,[MM_NBlocks]
inc edx
cmp edx,MM_MAX_BLOCKS
ja .ErrAlloc
push ecx
add ecx,12
test ecx,0FFFh ;округляем до большей границы страницы
jz @f
add ecx,1000h
and ecx,0FFFFF000h
@@:
mcall 68,12,ecx
mov [MM_NBlocks],edx ;заполнение данных о блоке
mov [edx*4*2-4*2+MM_BlocksInfo],eax ;begin
mov [edx*4*2-4+MM_BlocksInfo],ecx ;size
;dps 'Block '
;dph eax
;dps ' '
;dph ecx
;dnl
mov edx,eax
add edx,ecx
mov [edx-8],eax
pop dword[edx-4]
add [edx-4],eax
mov dword[edx-12],0
ret
.ErrAlloc:
pop ecx
xor eax,eax
ret
endp
;-------------------------------------------------------------------------------
;удаляет память
;proc MM_DelMem Pointer:DWORD
align 4
MM_DelMem:
Pointer equ ebp+8
push ebp
mov ebp,esp
; int3
push ebx
mov ecx,[MM_NBlocks]
test ecx,ecx
jnz @f
xor eax,eax
pop ebx
leave
ret 4
@@:
mov eax,[Pointer]
xor ebx,ebx ;ebx - (номер блока)*8
.TestBlocks:
mov edx,[MM_BlocksInfo+ebx]
add edx,[MM_BlocksInfo+ebx+4]
sub edx,8 ;edx - указатель на 1ую пару
.TestMems:
cmp [edx],eax
je .FoundMem
sub edx,8
cmp dword[edx+4],0
jne .TestMems
add ebx,4
loop .TestBlocks
xor eax,eax
pop ebx
leave
ret 4
.FoundMem:
cmp dword[edx-4],0
jz .EndDelMem
.NextMoveMem:
p2p [edx+4],[edx-4]
p2p [edx],[edx-8]
sub edx,8
cmp dword[edx-4],0
jnz .NextMoveMem
.EndDelMem:
mov dword[edx+4],0
mov dword[edx],0
mov eax,1
pop ebx
leave
ret 4
restore Pointer