add memory lib + maps

This commit is contained in:
Frater 2026-02-07 16:03:29 +01:00
commit 4b62e01e82
10 changed files with 521 additions and 411 deletions

View file

@ -29,29 +29,29 @@
; =============================================================================
dump_ram_words:
jcxz .done ; Sécurité : si CX=0, on sort
jcxz .done ; Sécurité : si CX=0, on sort
.loop:
lodsw ; Charge [DS:SI] dans AX et incrémente SI de 2
; Note: x86 est Little Endian. Si RAM = 12 34,
; AX vaudra 0x3412.
lodsw ; Charge [DS:SI] dans AX et incrémente SI de 2
; Note: x86 est Little Endian. Si RAM = 12 34,
; AX vaudra 0x3412.
push cx ; Sauvegarde le compteur de boucle principal
push si ; Sauvegarde le pointeur (si cga_putc le modifie)
push cx ; Sauvegarde le compteur de boucle principal
push si ; Sauvegarde le pointeur (si cga_putc le modifie)
call print_word_hex ; Affiche AX sous forme "XXXX"
call print_word_hex ; Affiche AX sous forme "XXXX"
; (Optionnel) Ajouter un espace entre chaque mot pour la lisibilité
mov al, ' '
call cga_putc
; (Optionnel) Ajouter un espace entre chaque mot pour la lisibilité
mov al, ' '
call cga_putc
pop si ; Restaure SI
pop cx ; Restaure CX
pop si ; Restaure SI
pop cx ; Restaure CX
loop .loop ; Décrémente CX et boucle si != 0
loop .loop ; Décrémente CX et boucle si != 0
.done:
ret
ret
; =============================================================================
; Fonction : print_word_hex
@ -60,31 +60,30 @@ dump_ram_words:
; Sortie : Affiche "1A2B" via cga_putc
; =============================================================================
print_word_hex:
pusha
mov dx, ax
mov cx, 4 ; 4 caractères hex dans un mot de 16 bits
pusha
mov dx, ax
mov cx, 4 ; 4 caractères hex dans un mot de 16 bits
.next_digit:
; On veut afficher les bits 12-15 d'abord.
; L'astuce est de faire une rotation à gauche de 4 bits.
; Le quartet de poids fort se retrouve en bas (bits 0-3).
rol dx, 4
.next_digit:
; On veut afficher les bits 12-15 d'abord.
; L'astuce est de faire une rotation à gauche de 4 bits.
; Le quartet de poids fort se retrouve en bas (bits 0-3).
rol dx, 4
mov ax, dx ; Copie temporaire
and al, 0x0F ; On isole les 4 derniers bits (0 à 15)
mov ax, dx ; Copie temporaire
and al, 0x0F ; On isole les 4 derniers bits (0 à 15)
call nibble_to_ascii ; Convertit AL en caractère ASCII
call nibble_to_ascii ; Convertit AL en caractère ASCII
; call cga_putc ; Affiche le caractère
xor bx, bx
mov bl, al
push bx
call cga_putc
add sp, 2
loop .next_digit ; Répète 4 fois
xor bx, bx
mov bl, al
push bx
call cga_putc
add sp, 2
loop .next_digit ; Répète 4 fois
popa
ret
popa
ret
; =============================================================================
; Fonction : nibble_to_ascii
@ -93,12 +92,12 @@ print_word_hex:
; Sortie : AL (caractère '0'-'9' ou 'A'-'F')
; =============================================================================
nibble_to_ascii:
cmp al, 9
ja .letter ; Si > 9, c'est une lettre (A-F)
cmp al, 9
ja .letter ; Si > 9, c'est une lettre (A-F)
; Sinon c'est un chiffre : 0x0 -> '0'
add al, '0'
ret
add al, '0' ; Sinon c'est un chiffre : 0x0 -> '0'
ret
.letter:
add al, 'A' - 10 ; Convertit 10 -> 'A', 11 -> 'B', etc.
ret
.letter: ; Convertit 10 -> 'A', 11 -> 'B', etc.
add al, 'A' - 10
ret

294
src/common/memory.asm Normal file
View file

@ -0,0 +1,294 @@
; =============================================================================
; 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
; --- 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
struc mem_block
.status resb 1 ; État du bloc (0=Libre, 1=Occupé)
.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
; -----------------------------------------------------------------------------
mem_alloc:
push bp
mov bp, sp
sub sp, 2
%define .curr_ptr word [bp-2]
%define .size_requested word [bp+4]
push bx
push cx
push es
push di
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
%undef .curr_ptr
%undef .size_requested
leave ; Restaure SP et BP
ret
; -----------------------------------------------------------------------------
; mem_free
; Libère un bloc mémoire.
; Stack: [BP+4] = Handle (word)
; -----------------------------------------------------------------------------
mem_free:
push bp
mov bp, sp
%define .handle word [bp+4]
push es
push di
push bx
mov ax, .handle ; Handle
test ax, ax
jz .done ; Sécurité Handle NULL
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
%undef .handle
leave
ret
; -----------------------------------------------------------------------------
; mem_get_size
; Retourne la taille utile d'un bloc.
; Stack: [BP+4] = Handle (word)
; Output: AX = Taille
; -----------------------------------------------------------------------------
mem_get_size:
push bp
mov bp, sp
%define .handle word [bp+4]
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
%undef .handle
leave
ret
; -----------------------------------------------------------------------------
; 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)
; -----------------------------------------------------------------------------
mem_resolve:
push bp
mov bp, sp
mov ax, [bp+4] ; 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

21
src/common/patterns.asm Normal file
View file

@ -0,0 +1,21 @@
; =============================================================================
; SECTION : PATTERNS & FILL
; https://paulsmith.github.io/classic-mac-patterns/
; =============================================================================
;
; pattern ID
%define PATTERN_BLACK 0
%define PATTERN_GRAY_DARK 1
%define PATTERN_GRAY_MID 2
%define PATTERN_GRAY_LIGHT 3
%define PATTERN_WHITE_LIGHT 4
%define PATTERN_WHITE 5
align 8
pattern_8x8:
dq 0x0000000000000000 ; pattern_black (Tout à 0 = Noir)
dq 0x2200880022008800 ; pattern_gray_dark (Majorité de 0)
dq 0x2288228822882288 ; pattern_gray_mid
dq 0xAA55AA55AA55AA55 ; pattern_gray_light (50/50)
dq 0x77DD77DD77DD77DD ; pattern_white_light (Majorité de 1 = Clair)
dq 0xFFFFFFFFFFFFFFFF ; pattern_white (Tout à 1 = Blanc)