307 lines
No EOL
7.6 KiB
NASM
307 lines
No EOL
7.6 KiB
NASM
; =============================================================================
|
|
; Project : Custom BIOS / ROM
|
|
; File : memory.asm
|
|
; Author : Gemini
|
|
;
|
|
; Description :
|
|
; Gestionnaire de mémoire dynamique (Heap) simple pour mode réel.
|
|
; Utilise un algorithme "First-Fit" avec fusion des blocs libres (coalescing).
|
|
;
|
|
; Layout Mémoire :
|
|
; [STATUS (1b)] [SIZE (2b)] [NEXT (2b)] [ DATA ... ]
|
|
;
|
|
; =============================================================================
|
|
|
|
%define MEM_HEAP_SEG 0x2000 ; Segment du tas (Phys: 0x20000)
|
|
%define MEM_HEAP_SIZE 0x8000 ; Taille totale (32 Ko)
|
|
|
|
%define MEM_STATUS_FREE 0
|
|
%define MEM_STATUS_USED 1
|
|
|
|
; --- API Codes ---
|
|
%define MEM_ALLOC 0
|
|
%define MEM_GET_SIZE 1
|
|
%define MEM_FREE 2
|
|
%define MEM_RESOLVE 3
|
|
%define MEM_INIT 4
|
|
|
|
; --- Macro API ---
|
|
; Usage: MEM FUNCTION, [ARG1], [ARG2]...
|
|
%macro MEM 1-*
|
|
%rep %0 - 1
|
|
%rotate -1
|
|
push %1
|
|
%endrep
|
|
%rotate -1
|
|
call word [cs:mem_api_table + ((%1)*2)]
|
|
add sp, (%0 - 1) * 2
|
|
%endmacro
|
|
|
|
mem_api_table:
|
|
dw mem_alloc
|
|
dw mem_get_size
|
|
dw mem_free
|
|
dw mem_resolve
|
|
dw mem_init
|
|
|
|
struc mem_block
|
|
.status resb 1 ; État du bloc (0=Libre, 1=Occupé)
|
|
.pad resb 1 ; Padding pour alignement 16-bits
|
|
.size resw 1 ; Taille des données (sans le header)
|
|
.next resw 1 ; Offset du prochain bloc (0 = Fin)
|
|
endstruc
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; mem_init
|
|
; Initialise le gestionnaire de mémoire.
|
|
; Crée un seul gros bloc libre couvrant tout le segment.
|
|
; -----------------------------------------------------------------------------
|
|
mem_init:
|
|
push ax
|
|
push es
|
|
push di
|
|
|
|
mov ax, MEM_HEAP_SEG
|
|
mov es, ax
|
|
xor di, di
|
|
|
|
; Initialisation du premier bloc (Header)
|
|
mov byte [es:di + mem_block.status], MEM_STATUS_FREE
|
|
|
|
; Taille disponible = Total - TailleHeader
|
|
mov ax, MEM_HEAP_SIZE
|
|
sub ax, mem_block_size
|
|
mov word [es:di + mem_block.size], ax
|
|
|
|
mov word [es:di + mem_block.next], 0
|
|
|
|
pop di
|
|
pop es
|
|
pop ax
|
|
ret
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; mem_alloc
|
|
; Alloue un bloc de mémoire.
|
|
;
|
|
; Stack: [BP+4] = Taille demandée (word)
|
|
; Output: AX = Handle (Offset du bloc de données) ou 0 si échec
|
|
; -----------------------------------------------------------------------------
|
|
%define .size_requested word [bp+4]
|
|
; variables
|
|
%define .curr_ptr word [bp-2]
|
|
mem_alloc:
|
|
push bp
|
|
mov bp, sp
|
|
sub sp, 2
|
|
|
|
push bx
|
|
push cx
|
|
push es
|
|
push di
|
|
|
|
; Alignement de la taille demandée sur 2 octets (Word align)
|
|
mov ax, .size_requested
|
|
test ax, 1
|
|
jz .size_aligned
|
|
inc word .size_requested
|
|
.size_aligned:
|
|
mov .curr_ptr, 0 ; On commence la recherche à l'offset 0
|
|
|
|
mov ax, MEM_HEAP_SEG
|
|
mov es, ax
|
|
|
|
.scan_loop:
|
|
mov di, .curr_ptr ; DI = Ptr courant
|
|
|
|
; 1. Le bloc est-il LIBRE ?
|
|
cmp byte [es:di + mem_block.status], MEM_STATUS_FREE
|
|
jne .next_block
|
|
|
|
; 2. Tentative de FUSION (Coalescing) avec les blocs suivants
|
|
.try_merge:
|
|
mov bx, [es:di + mem_block.next]
|
|
cmp bx, 0
|
|
je .check_size ; Pas de suivant, on vérifie la taille
|
|
|
|
; Si le suivant est aussi libre, on fusionne
|
|
cmp byte [es:bx + mem_block.status], MEM_STATUS_FREE
|
|
jne .check_size
|
|
|
|
; Nouvelle Taille = TailleActuelle + Header + TailleSuivant
|
|
mov ax, [es:bx + mem_block.size]
|
|
add ax, mem_block_size
|
|
add [es:di + mem_block.size], ax
|
|
|
|
; Mise à jour du chainage (saute le bloc fusionné)
|
|
mov ax, [es:bx + mem_block.next]
|
|
mov [es:di + mem_block.next], ax
|
|
|
|
jmp .try_merge ; On boucle pour voir si le suivant du suivant est libre
|
|
|
|
.check_size:
|
|
; 3. Vérifier si la taille est suffisante
|
|
mov ax, .size_requested ; Taille demandée (Argument)
|
|
cmp [es:di + mem_block.size], ax
|
|
jb .next_block ; Trop petit
|
|
|
|
; 4. Allocation (Split si possible)
|
|
; Calculer l'espace restant
|
|
mov cx, [es:di + mem_block.size]
|
|
sub cx, ax ; CX = Reste
|
|
|
|
; Peut-on créer un nouveau bloc dans le reste ? (Header + au moins 1 octet)
|
|
cmp cx, mem_block_size + 1
|
|
jb .allocate_full
|
|
|
|
; --- SPLIT ---
|
|
; Marquer le bloc actuel comme utilisé avec la taille demandée
|
|
mov byte [es:di + mem_block.status], MEM_STATUS_USED
|
|
mov [es:di + mem_block.size], ax
|
|
|
|
; Créer le nouveau header dans l'espace restant
|
|
mov bx, di
|
|
add bx, mem_block_size
|
|
add bx, ax ; BX = Offset du nouveau bloc
|
|
|
|
mov byte [es:bx + mem_block.status], MEM_STATUS_FREE
|
|
|
|
sub cx, mem_block_size ; Taille utile du nouveau bloc
|
|
mov [es:bx + mem_block.size], cx
|
|
|
|
; Insérer dans la liste chainée
|
|
mov dx, [es:di + mem_block.next]
|
|
mov [es:bx + mem_block.next], dx
|
|
mov [es:di + mem_block.next], bx
|
|
|
|
jmp .success
|
|
|
|
.allocate_full:
|
|
; On prend tout le bloc sans le couper
|
|
mov byte [es:di + mem_block.status], MEM_STATUS_USED
|
|
|
|
.success:
|
|
; Retourner le handle (Offset des DONNÉES = DI + Header)
|
|
mov ax, di
|
|
add ax, mem_block_size
|
|
jmp .done
|
|
|
|
.next_block:
|
|
; Passer au bloc suivant
|
|
mov di, .curr_ptr
|
|
mov ax, [es:di + mem_block.next]
|
|
mov .curr_ptr, ax
|
|
cmp ax, 0
|
|
jne .scan_loop
|
|
|
|
; Échec : Plus de mémoire ou fragmentation trop élevée
|
|
xor ax, ax
|
|
|
|
.done:
|
|
pop di
|
|
pop es
|
|
pop cx
|
|
pop bx
|
|
leave ; Restaure SP et BP
|
|
ret
|
|
%undef .curr_ptr
|
|
%undef .size_requested
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; mem_free
|
|
; Libère un bloc mémoire.
|
|
; Stack: [BP+4] = Handle (word)
|
|
; -----------------------------------------------------------------------------
|
|
%define .handle word [bp+4]
|
|
mem_free:
|
|
push bp
|
|
mov bp, sp
|
|
|
|
push es
|
|
push di
|
|
push bx
|
|
|
|
mov ax, .handle ; Handle
|
|
test ax, ax
|
|
jz .done ; Sécurité Handle NULL
|
|
|
|
; Sécurité : Vérifier si le handle est dans le segment
|
|
cmp ax, MEM_HEAP_SIZE
|
|
ja .done
|
|
|
|
mov bx, MEM_HEAP_SEG
|
|
mov es, bx
|
|
|
|
; Retrouver le header (Handle - HeaderSize)
|
|
mov di, ax
|
|
sub di, mem_block_size
|
|
|
|
; Marquer simplement comme libre (le coalescing se fera au prochain alloc)
|
|
mov byte [es:di + mem_block.status], MEM_STATUS_FREE
|
|
|
|
.done:
|
|
pop bx
|
|
pop di
|
|
pop es
|
|
leave
|
|
ret
|
|
%undef .handle
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; mem_get_size
|
|
; Retourne la taille utile d'un bloc.
|
|
; Stack: [BP+4] = Handle (word)
|
|
; Output: AX = Taille
|
|
; -----------------------------------------------------------------------------
|
|
%define .handle word [bp+4]
|
|
mem_get_size:
|
|
push bp
|
|
mov bp, sp
|
|
|
|
push es
|
|
push di
|
|
|
|
mov ax, .handle ; Handle
|
|
test ax, ax
|
|
jz .error
|
|
|
|
mov bx, MEM_HEAP_SEG
|
|
mov es, bx
|
|
|
|
mov di, ax
|
|
sub di, mem_block_size
|
|
|
|
mov ax, [es:di + mem_block.size]
|
|
jmp .done
|
|
|
|
.error:
|
|
xor ax, ax
|
|
.done:
|
|
pop di
|
|
pop es
|
|
leave
|
|
ret
|
|
%undef .handle
|
|
|
|
; -----------------------------------------------------------------------------
|
|
; mem_resolve
|
|
; Convertit un handle en pointeur complet Segment:Offset.
|
|
; Stack: [BP+4] = Handle (word)
|
|
; Output: DX:AX = Segment:Offset
|
|
; ES:DI = Segment:Offset (pour utilisation immédiate avec stos/movs)
|
|
; -----------------------------------------------------------------------------
|
|
%define .handle word [bp+4]
|
|
mem_resolve:
|
|
push bp
|
|
mov bp, sp
|
|
|
|
mov ax, .handle ; Handle
|
|
mov dx, MEM_HEAP_SEG
|
|
mov es, dx
|
|
mov di, ax ; L'offset est le handle lui-même
|
|
|
|
; DX contient déjà le segment, AX contient déjà l'offset
|
|
|
|
leave
|
|
ret
|
|
%undef .handle |