bios-OS/src/common/memory.asm
2026-02-10 16:16:55 +01:00

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