From 4b62e01e82b86474a590f2678b0131b0fc3ac864 Mon Sep 17 00:00:00 2001 From: Frater <13979047+saintfrater@users.noreply.github.com> Date: Sat, 7 Feb 2026 16:03:29 +0100 Subject: [PATCH] add memory lib + maps --- doc/MEMORY_MAP.md | 60 ++++---- src/boot.asm | 189 ++++++++++++------------- src/common/debug_cga.asm | 83 ++++++----- src/common/memory.asm | 294 +++++++++++++++++++++++++++++++++++++++ src/common/patterns.asm | 21 +++ src/drivers/gfx_cgam.asm | 23 --- src/drivers/memory.asm | 196 -------------------------- src/gui/draw.asm | 2 +- src/gui/lib.asm | 38 ++--- src/gui/window.asm | 2 +- 10 files changed, 509 insertions(+), 399 deletions(-) create mode 100644 src/common/memory.asm create mode 100644 src/common/patterns.asm delete mode 100644 src/drivers/memory.asm diff --git a/doc/MEMORY_MAP.md b/doc/MEMORY_MAP.md index 0d0f942..c7d310e 100644 --- a/doc/MEMORY_MAP.md +++ b/doc/MEMORY_MAP.md @@ -8,18 +8,20 @@ The processor starts in Real Mode, addressing 1 MB of memory. | Physical Start | Physical End | Size | Description | Segment | Source File | | :--- | :--- | :--- | :--- | :--- | :--- | -| **`0x00000`** | **`0x003FF`** | 1 KB | **IVT** (Interrupt Vector Table) | `0x0000` | `boot.asm` / `generic.asm` | -| **`0x00400`** | **`0x004FF`** | 256 B | **BDA** (BIOS Data Area) Standard | `0x0040` | `common/bda.asm` | -| **`0x00500`** | **`0x005FF`** | ~256 B | **Custom BDA** (Driver Data) | `0x0050` | `common/bda.asm` | -| ... | ... | ... | *Free / Reserved* | | | -| **`0x08000`** | **`0x09FFF`** | 8 KB | **Stack Bottom** | `0x0800` | `boot.asm` | -| **`0x0A000`** | **`0x0AFFF`** | ~4 KB | **GUI RAM** (Widget Allocation) | `0x0A00` | `gui/lib.asm` | -| **`0x0B000`** | **`0x17FFE`** | ~52 KB | **Stack Top** (Grows downwards) | `0x0800` | `boot.asm` | -| ... | ... | ... | *Conventional RAM Free* | | | -| **`0xB8000`** | **`0xBFFFF`** | 32 KB | **VRAM** (CGA / MCGA) | `0xB800` | `drivers/gfx_cgam.asm` | -| **`0xC0000`** | **`0xDFFFF`** | 128 KB | **Option ROMs** (e.g., VGA BIOS) | `0xC000` | `boot.asm` | -| ... | ... | ... | *System Reserved* | | | -| **`0xF0000`** | **`0xFFFFF`** | 64 KB | **System BIOS ROM** (Code) | `0xF000` | `boot.asm` | +| **`0x00000`** | **`0x003FF`** | 1 KB | **IVT** (Interrupt Vector Table) | `0x0000` | `memory.asm` | +| **`0x00400`** | **`0x004FF`** | 256 B | **BDA** (BIOS Data Area) Standard | `0x0040` | `memory.asm` | +| **`0x00500`** | **`0x005FF`** | ~256 B | **Custom BDA** (Driver Data) | `0x0050` | `memory.asm` | +| **`0x00600`** | **`0x07FFF`** | 30.5 KB | **Free** (Low Memory) | | | +| **`0x08000`** | **`0x17FFF`** | 64 KB | **Stack** (Grows downwards) | `0x0800` | `memory.asm` | +| **`0x18000`** | **`0x18FFF`** | ~4 KB | **GUI RAM** (Widget Allocation) | `0x0A00` | `gui/lib.asm` | +| **`0x19000`** | **`0x9FBFF`** | 539 KB | **Free** (Conventional RAM) | | | +| **`0x9FC00`** | **`0x9FFFF`** | 1 KB | **EBDA** (Extended BIOS Data Area) | `0x9FC0` | `memory.asm` | +| **`0xA0000`** | **`0xAFFFF`** | 64 KB | **VGA RAM** (Graphics Modes) | `0xA000` | `Hardware` | +| **`0xB0000`** | **`0xB7FFF`** | 32 KB | **MDA RAM** (Monochrome Text) | `0xB000` | `Hardware` | +| **`0xB8000`** | **`0xBFFFF`** | 32 KB | **VRAM** (CGA / MCGA) | `0xB800` | `memory.asm` | +| **`0xC0000`** | **`0xDFFFF`** | 128 KB | **Option ROMs** (e.g., VGA BIOS) | `0xC000` | `memory.asm` | +| **`0xE0000`** | **`0xEFFFF`** | 64 KB | **System Reserved** (BIOS Extension) | `0xE000` | `Hardware` | +| **`0xF0000`** | **`0xFFFFF`** | 64 KB | **System BIOS ROM** (Code) | `0xF000` | `memory.asm` | ### Detailed Zones @@ -50,6 +52,10 @@ The project separates the standard IBM BDA from its own variables to avoid confl * **Even Lines (0, 2, 4...)**: Offset `0x0000` - `0x1F3F` (Bank 0). * **Odd Lines (1, 3, 5...)**: Offset `0x2000` - `0x3F3F` (Bank 1). +#### VGA RAM & System Reserved +* **VGA RAM (`0xA0000`)**: Standard 64 KB memory window for VGA graphics modes. Although this project primarily uses the CGA memory at `0xB8000`, this area is physically present when the VGA hardware is emulated. +* **System Reserved (`0xE0000`)**: Reserved memory area, typically used for motherboard BIOS extensions or specific hardware mapping. + #### ROM * **Option ROMs (`0xC0000`)**: Scanned by `setup_load_rom` to initialize the VGA BIOS. * **System ROM (`0xF0000`)**: Contains the compiled binary code. The Reset Vector is at `0xFFFF0`. @@ -64,18 +70,20 @@ Le processeur démarre en mode réel, adressant 1 Mo de mémoire. | Adresse Physique (Début) | Adresse Physique (Fin) | Taille | Description | Segment | Fichier Source | | :--- | :--- | :--- | :--- | :--- | :--- | -| **`0x00000`** | **`0x003FF`** | 1 Ko | **IVT** (Interrupt Vector Table) | `0x0000` | `boot.asm` / `generic.asm` | -| **`0x00400`** | **`0x004FF`** | 256 o | **BDA** (BIOS Data Area) Standard | `0x0040` | `common/bda.asm` | -| **`0x00500`** | **`0x005FF`** | ~256 o | **Custom BDA** (Données Drivers) | `0x0050` | `common/bda.asm` | -| ... | ... | ... | *Mémoire Libre / Réservée* | | | -| **`0x08000`** | **`0x09FFF`** | 8 Ko | **Bas de la Pile** (Stack Bottom) | `0x0800` | `boot.asm` | -| **`0x0A000`** | **`0x0AFFF`** | ~4 Ko | **GUI RAM** (Allocation Widgets) | `0x0A00` | `gui/lib.asm` | -| **`0x0B000`** | **`0x17FFE`** | ~52 Ko | **Haut de la Pile** (Stack Top) | `0x0800` | `boot.asm` | -| ... | ... | ... | *RAM Conventionnelle Libre* | | | -| **`0xB8000`** | **`0xBFFFF`** | 32 Ko | **VRAM** (CGA / MCGA) | `0xB800` | `drivers/gfx_cgam.asm` | -| **`0xC0000`** | **`0xDFFFF`** | 128 Ko | **Option ROMs** (ex: VGA BIOS) | `0xC000` | `boot.asm` | -| ... | ... | ... | *Réservé Système* | | | -| **`0xF0000`** | **`0xFFFFF`** | 64 Ko | **System BIOS ROM** (Code) | `0xF000` | `boot.asm` | +| **`0x00000`** | **`0x003FF`** | 1 Ko | **IVT** (Interrupt Vector Table) | `0x0000` | `memory.asm` | +| **`0x00400`** | **`0x004FF`** | 256 o | **BDA** (BIOS Data Area) Standard | `0x0040` | `memory.asm` | +| **`0x00500`** | **`0x005FF`** | ~256 o | **Custom BDA** (Données Drivers) | `0x0050` | `memory.asm` | +| **`0x00600`** | **`0x07FFF`** | 30.5 Ko | **Libre** (Mémoire Basse) | | | +| **`0x08000`** | **`0x17FFF`** | 64 KB | **Stack** (Grows downwards) | `0x0800` | `memory.asm` | +| **`0x18000`** | **`0x18FFF`** | ~4 KB | **GUI RAM** (Widget Allocation) | `0x0A00` | `gui/lib.asm` | +| **`0x19000`** | **`0x9FBFF`** | 539 Ko | **Libre** (RAM Conventionnelle) | | | +| **`0x9FC00`** | **`0x9FFFF`** | 1 Ko | **EBDA** (Extended BIOS Data Area) | `0x9FC0` | `memory.asm` | +| **`0xA0000`** | **`0xAFFFF`** | 64 Ko | **VGA RAM** (Modes Graphiques) | `0xA000` | `Matériel` | +| **`0xB0000`** | **`0xB7FFF`** | 32 Ko | **MDA RAM** (Texte Monochrome) | `0xB000` | `Matériel` | +| **`0xB8000`** | **`0xBFFFF`** | 32 Ko | **VRAM** (CGA / MCGA) | `0xB800` | `memory.asm` | +| **`0xC0000`** | **`0xDFFFF`** | 128 Ko | **Option ROMs** (ex: VGA BIOS) | `0xC000` | `memory.asm` | +| **`0xE0000`** | **`0xEFFFF`** | 64 Ko | **Réservé Système** (Extension BIOS) | `0xE000` | `Matériel` | +| **`0xF0000`** | **`0xFFFFF`** | 64 Ko | **System BIOS ROM** (Code) | `0xF000` | `memory.asm` | ### Détails des Zones @@ -106,6 +114,10 @@ Le projet sépare la BDA standard IBM de ses propres variables pour éviter les * **Lignes Paires (0, 2, 4...)** : Offset `0x0000` à `0x1F3F` (Bank 0). * **Lignes Impaires (1, 3, 5...)** : Offset `0x2000` à `0x3F3F` (Bank 1). +#### VGA RAM & Réservé Système +* **VGA RAM (`0xA0000`)** : Fenêtre mémoire standard de 64 Ko pour les modes graphiques VGA. Bien que ce projet utilise principalement la mémoire CGA à `0xB8000`, cette zone est physiquement présente lorsque le matériel VGA est émulé. +* **Réservé Système (`0xE0000`)** : Zone mémoire réservée, typiquement utilisée pour les extensions du BIOS de la carte mère ou le mappage de matériel spécifique. + #### ROM * **Option ROMs (`0xC0000`)** : Espace scanné par `setup_load_rom` pour initialiser le BIOS VGA. * **System ROM (`0xF0000`)** : Contient le code binaire compilé. Le Vecteur de Reset est situé à `0xFFFF0`. \ No newline at end of file diff --git a/src/boot.asm b/src/boot.asm index 6789b65..4563879 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -38,10 +38,11 @@ ; times 0xFFFF - ($ - $$) db 0xFF section .text - bits 16 + bits 16 %include "./common/chartable.asm" %include "./common/cursor.asm" +%include "./common/patterns.asm" %include "./common/generic.asm" ; definition du BDA @@ -52,6 +53,8 @@ section .text %include "./drivers/mouse_ps2.asm" %include "./drivers/keyboard_ps2.asm" +%include "./common/debug_cga.asm" + ; GUI %include "./gui/lib.asm" %include "./gui/draw.asm" @@ -102,123 +105,123 @@ reset: push cs pop ds - ; Init du système GUI - call gui_init_system + ; Init du système GUI + call gui_init_system call build_interface main_loop: - ; 2. Moteur GUI (Gère tout : Loop, HitTest, Draw, Callbacks) - ; DS pointe déjà sur GUI_RAM_SEG - call gui_process_all + call gui_process_all - ; 3. Afficher souris - ; GFX MOUSE_MOVE + GUI GUI_GET_VAL, 5 ; slider - jmp main_loop + ;GFX GOTOXY, 5, 20 + ;call print_word_hex + + jmp main_loop ; --- Callbacks (Fonctions appelées par le moteur) --- on_click_quit: - hlt - jmp on_click_quit + hlt + jmp on_click_quit build_interface: - ; --- CRÉATION DYNAMIQUE DES BOUTONS --- - ; On reste dans CS pour lire les arguments, - ; mais on configure DS=GUI_RAM_SEG pour l'allocation + ; --- CRÉATION DYNAMIQUE DES BOUTONS --- + ; On reste dans CS pour lire les arguments, + ; mais on configure DS=GUI_RAM_SEG pour l'allocation - ; Créer Bouton 1 "QUITTER" - call gui_alloc_widget ; Retourne SI = Pointeur nouveau slot - jc .mem_full + ; Créer Bouton 1 "QUITTER" + call gui_alloc_widget ; Retourne SI = Pointeur nouveau slot + jc .mem_full - ; Remplir les propriétés - mov word [gs:si + widget.x], 10 - mov word [gs:si + widget.y], 10 - mov word [gs:si + widget.w], 80 - mov word [gs:si + widget.h], 16 - mov word [gs:si + widget.text_ofs], str_quit - ; mov byte [gs:si + widget.user_id], 1 ; Style "Bouton par défaut" (OK) - mov word [gs:si + widget.text_seg], cs ; Texte est dans la ROM - mov word [gs:si + widget.event_click], on_click_quit ; Fonction à appeler - mov byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON + ; Remplir les propriétés + mov word [gs:si + widget.x], 10 + mov word [gs:si + widget.y], 10 + mov word [gs:si + widget.w], 80 + mov word [gs:si + widget.h], 16 + mov word [gs:si + widget.text_ofs], str_quit + ; mov byte [gs:si + widget.user_id], 1 ; Style "Bouton par défaut" (OK) + mov word [gs:si + widget.text_seg], cs ; Texte est dans la ROM + mov word [gs:si + widget.event_click], on_click_quit ; Fonction à appeler + mov byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON - ; Créer Bouton 2 "HELLO" - call gui_alloc_widget - jc .mem_full + ; Créer Bouton 2 "HELLO" + call gui_alloc_widget + jc .mem_full - mov word [gs:si + widget.x], 100 - mov word [gs:si + widget.y], 50 - mov word [gs:si + widget.w], 80 - mov word [gs:si + widget.h], 16 - mov word [gs:si + widget.text_ofs], str_hello - mov word [gs:si + widget.text_seg], cs - mov byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON + mov word [gs:si + widget.x], 100 + mov word [gs:si + widget.y], 50 + mov word [gs:si + widget.w], 80 + mov word [gs:si + widget.h], 16 + mov word [gs:si + widget.text_ofs], str_hello + mov word [gs:si + widget.text_seg], cs + mov byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON - ; Créer checkbox 3 "option 1" - call gui_alloc_widget - jc .mem_full + ; Créer checkbox 3 "option 1" + call gui_alloc_widget + jc .mem_full - mov word [gs:si + widget.x], 200 - mov word [gs:si + widget.y], 50 - mov word [gs:si + widget.w], 100 - mov word [gs:si + widget.h], 15 - mov word [gs:si + widget.text_ofs], str_option1 - mov word [gs:si + widget.text_seg], cs - mov byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX - ; Pas de callback + mov word [gs:si + widget.x], 200 + mov word [gs:si + widget.y], 50 + mov word [gs:si + widget.w], 100 + mov word [gs:si + widget.h], 15 + mov word [gs:si + widget.text_ofs], str_option1 + mov word [gs:si + widget.text_seg], cs + mov byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX + ; Pas de callback - ; Créer checkbox 3 "option 2" - call gui_alloc_widget - jc .mem_full + ; Créer checkbox 3 "option 2" + call gui_alloc_widget + jc .mem_full - mov word [gs:si + widget.x], 200 - mov word [gs:si + widget.y], 50+16 - mov word [gs:si + widget.w], 100 - mov word [gs:si + widget.h], 15 - mov word [gs:si + widget.text_ofs], str_option2 - mov word [gs:si + widget.text_seg], cs - mov byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX + mov word [gs:si + widget.x], 200 + mov word [gs:si + widget.y], 50+16 + mov word [gs:si + widget.w], 100 + mov word [gs:si + widget.h], 15 + mov word [gs:si + widget.text_ofs], str_option2 + mov word [gs:si + widget.text_seg], cs + mov byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX - ; Créer checkbox 3 "option 2" - call gui_alloc_widget - jc .mem_full + ; Créer checkbox 3 "option 2" + call gui_alloc_widget + jc .mem_full - mov word [gs:si + widget.x], 200 - mov word [gs:si + widget.y], 50+16*2 - mov word [gs:si + widget.w], 100 - mov word [gs:si + widget.h], 15 - mov word [gs:si + widget.text_ofs], str_option3 - mov word [gs:si + widget.text_seg], cs - mov byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX + mov word [gs:si + widget.x], 200 + mov word [gs:si + widget.y], 50+16*2 + mov word [gs:si + widget.w], 100 + mov word [gs:si + widget.h], 15 + mov word [gs:si + widget.text_ofs], str_option3 + mov word [gs:si + widget.text_seg], cs + mov byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX - ; Créer Slider (Drag) - call gui_alloc_widget - jc .mem_full - mov word [gs:si + widget.x], 10 - mov word [gs:si + widget.y], 100 - mov word [gs:si + widget.w], 150 - mov word [gs:si + widget.h], 12 - mov byte [gs:si + widget.type], WIDGET_TYPE_SLIDER - mov byte [gs:si + widget.attr_mode], SLIDER_HORIZONTAL - mov word [gs:si + widget.attr_min], 10 ; X Min - mov word [gs:si + widget.attr_max], 140 ; X Max (Widget.x + W - ThumbW) - mov word [gs:si + widget.attr_val], 10 ; Position initiale - mov byte [gs:si + widget.thumb_pct], 15 ; Curseur fait 15% de la largeur + ; Créer Slider (Drag) + call gui_alloc_widget + jc .mem_full + mov word [gs:si + widget.x], 10 + mov word [gs:si + widget.y], 100 + mov word [gs:si + widget.w], 150 + mov word [gs:si + widget.h], 12 + mov byte [gs:si + widget.type], WIDGET_TYPE_SLIDER + mov byte [gs:si + widget.attr_mode], SLIDER_HORIZONTAL + mov word [gs:si + widget.attr_min], 10 ; X Min + mov word [gs:si + widget.attr_max], 140 ; X Max (Widget.x + W - ThumbW) + mov word [gs:si + widget.attr_val], 10 ; Position initiale + mov byte [gs:si + widget.thumb_pct], 15 ; Curseur fait 15% de la largeur ; mov word [gs:si + widget.event_drag], on_drag_slider ; Créer Slider (Drag) - call gui_alloc_widget - jc .mem_full - mov word [gs:si + widget.x], 400 - mov word [gs:si + widget.y], 10 - mov word [gs:si + widget.w], 16 - mov word [gs:si + widget.h], 150 - mov byte [gs:si + widget.type], WIDGET_TYPE_SLIDER - mov byte [gs:si + widget.attr_mode], SLIDER_VERTICAL - mov word [gs:si + widget.attr_min], 10 ; X Min - mov word [gs:si + widget.attr_max], 140 ; X Max (Widget.x + W - ThumbW) - mov word [gs:si + widget.attr_val], 140 ; Position initiale - mov byte [gs:si + widget.thumb_pct], 25 ; Curseur fait 15% de la largeur + call gui_alloc_widget + jc .mem_full + mov word [gs:si + widget.x], 400 + mov word [gs:si + widget.y], 10 + mov word [gs:si + widget.w], 16 + mov word [gs:si + widget.h], 150 + mov byte [gs:si + widget.type], WIDGET_TYPE_SLIDER + mov byte [gs:si + widget.attr_mode], SLIDER_VERTICAL + mov word [gs:si + widget.attr_min], 10 ; X Min + mov word [gs:si + widget.attr_max], 140 ; X Max (Widget.x + W - ThumbW) + mov word [gs:si + widget.attr_val], 140 ; Position initiale + mov byte [gs:si + widget.thumb_pct], 25 ; Curseur fait 15% de la largeur ; mov word [gs:si + widget.event_drag], on_drag_slider .mem_full: diff --git a/src/common/debug_cga.asm b/src/common/debug_cga.asm index c922407..3e40c5e 100644 --- a/src/common/debug_cga.asm +++ b/src/common/debug_cga.asm @@ -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 \ No newline at end of file +.letter: ; Convertit 10 -> 'A', 11 -> 'B', etc. + add al, 'A' - 10 + ret \ No newline at end of file diff --git a/src/common/memory.asm b/src/common/memory.asm new file mode 100644 index 0000000..e65675a --- /dev/null +++ b/src/common/memory.asm @@ -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 diff --git a/src/common/patterns.asm b/src/common/patterns.asm new file mode 100644 index 0000000..26440a2 --- /dev/null +++ b/src/common/patterns.asm @@ -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) diff --git a/src/drivers/gfx_cgam.asm b/src/drivers/gfx_cgam.asm index b373ef6..ae2ea5f 100644 --- a/src/drivers/gfx_cgam.asm +++ b/src/drivers/gfx_cgam.asm @@ -106,29 +106,6 @@ graph_driver: dw cga_mouse_show ; déplacement du curseur dw cga_mouse_cursor_move ; déplacement du curseur - -; ============================================================================= -; 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) - ; ------------------------------------------------------------ ; initialise le mode graphique (via l'int 10h) ; diff --git a/src/drivers/memory.asm b/src/drivers/memory.asm deleted file mode 100644 index 188051f..0000000 --- a/src/drivers/memory.asm +++ /dev/null @@ -1,196 +0,0 @@ -; ======================= -; API -; ======================= -%define MEM_INIT 0 -%define MEM_DETECT 2 -%define MEM_ALLOC 4 -%define MEM_FREE 6 -%define MEM_DUMP_RESET 8 -%define MEM_DUMP_NEXT 10 - -memory_api: - dw mem_init - dw mem_detect - dw mem_alloc - dw mem_free - dw mem_dump_reset - dw mem_dump_next - -; ======================= -; Contexte en RAM (DS = CTX_SEG) -; ======================= -heap_seg dw 0 -heap_start dw 0 -heap_end dw 0 -free_head dw 0 -dump_cursor dw 0 - -; ======================= -; Constantes -; ======================= -%define HDR_SIZE 8 -%define FTR_SIZE 2 -%define MIN_BLK 12 ; aligné (>= HDR+FTR) -%define FLAG_USED 1 - -%define MEM_SEG_DEB 0x0800 ; 0x0800:0000 = 0x8000 (32KB) évite IVT/BDA + stack 7C00 -%define MEM_SEG_END 0xA000 ; 0xA000:0000 = 0xA0000 (début zone vidéo) -%define MEM_SEG_STEP 0x0040 ; 1KB = 0x400 bytes = 0x40 paragraphs - -; --------------------------------------------------------------------------- -; Détection RAM conventionnelle -; Teste la RAM de MEM_SEG_DEB -> MEM_SEG_END (0xA000, 640KB), par pas de 1KB. -; Méthode: sauvegarde 1 mot, écrit 2 patterns, relit, restaure. -; -; Résultats: -; AX = taille détectée en Ko (KB) à partir de 0 jusqu’au “top conventionnel” -; DX = top_segment (segment du premier Ko NON valide) (optionnel) -; -; Précautions: -; - Ne testez PAS une zone où se trouve votre pile / variables. -; - Si votre pile est à 0000:7C00 (phys 0x7C00), commencez au moins à 0x0800. -; - Ne testez pas au-delà de MEM_SEG_END (0xA000: début VGA/vidéo). -; --------------------------------------------------------------------------- -memory_setup: - xor ax, ax ; AX = compteur KB trouvés - mov dx, MEM_SEG_DEB ; DX = segment courant testé - mov cx, MEM_SEG_END ; CX = segment fin (exclu) - xor di, di ; tester au début du bloc 1KB: ES:0000 - -.loop_seg: - cmp dx, cx - jae .done - mov es, dx - - ; sauvegarder le mot existant - mov bx, [es:di] - - ; pattern 1 - mov word [es:di], 0x55AA - cmp word [es:di], 0x55AA - jne .fail_restore - - ; pattern 2 (inverse) - mov word [es:di], 0xAA55 - cmp word [es:di], 0xAA55 - jne .fail_restore - - ; restaurer - mov [es:di], bx - - ; OK: avancer d’1KB - inc ax ; +1KB valide - add dx, MEM_SEG_STEP - jmp .loop_seg - -.fail_restore: - ; restaurer avant de sortir - mov [es:di], bx - -.done: - ; AX contient le nb de KB valides *à partir de MEM_SEG_DEB*. - ; Si vous voulez une taille “depuis 0KB”, ajoutez MEM_SEG_DEB*16/1024 = MEM_SEG_DEB/64. - ; - ; base_kb = MEM_SEG_DEB / 0x0040 (car 1KB = 0x40 segments) - mov bx, MEM_SEG_DEB - shr bx, 6 ; /64 = /0x40 => KB de base - add ax, bx ; AX = taille conventionnelle totale en KB (approx. 0..640) - ; DX = segment du premier bloc NON valide (top) - ; DX est déjà positionné (segment courant) - - ; stocker l'information dans le BDA - mov bx, BDA_SEGMENT - mov ds, bx - mov [BDA_INFO_MEM_SIZE], ax - mov [BDA_INFO_MEM_SEG], dx - - ret - -; ----------------------- -; mem_detect -; AX=conv_kb, DX=ebda_seg, CF=0 -; ----------------------- -mem_detect: - push ds - xor ax, ax - int 12h ; AX = KiB conventionnelle - mov bx, ax ; conv_kb -> BX - - ; Lire EBDA segment depuis BDA:040Eh - ; On suppose DS peut être changé temporairement - mov ax, 0x0040 - mov ds, ax - mov dx, [0x000E] ; EBDA segment (souvent 0x9FC0 etc), 0 si absent - pop ds - - mov ax, bx ; AX=conv_kb - clc - ret - -; ----------------------- -; mem_init -; Entrée (proposée) : -; AX = HEAP_SEG (0 => auto) -; BX = heap_start_offset (0 => défaut 0x0200) -; Sortie: -; CF=0 ok, CF=1 err -; ----------------------- -mem_init: - ; 1) déterminer heap_seg et heap_end en évitant EBDA - ; 2) créer 1 seul bloc libre [heap_start..heap_end) - - ; Implémentation typique: - ; - si AX=0 : choisir un segment RAM “safe” (ex: 0x7000) OU segment du contexte - ; - heap_start = (BX!=0 ? BX : 0x0200) - ; - heap_end = 0xFFFE (ou plus petit selon usage) - - ; Puis initialiser le 1er bloc libre: - ; [heap_start+0] size = heap_end-heap_start - ; [heap_start+2] flags=0 - ; [heap_start+4] next_free=0 - ; [heap_start+6] prev_free=0 - ; footer à (heap_start+size-2) - - clc - ret - -; ----------------------- -; mem_alloc -; CX = bytes demandés -; Sortie: ES=heap_seg, DI=payload, CF=0 si ok sinon CF=1 -; ----------------------- -mem_alloc: - ; need = align2(CX) + HDR_SIZE + FTR_SIZE - ; parcourir free list (first-fit) - ; split si reste >= MIN_BLK - ; marquer alloué, unlink free list - ; ES=heap_seg, DI=blk_off + HDR_SIZE - ret - -; ----------------------- -; mem_free -; Entrée: ES:DI = payload -; Sortie: CF=0 ok sinon CF=1 -; ----------------------- -mem_free: - ; blk_off = DI - HDR_SIZE - ; vérifier, marquer libre - ; coalescer prev/next via footer/header - ; relinker dans free list - ret - -; ----------------------- -; mem_dump_reset / mem_dump_next -; ----------------------- -mem_dump_reset: - mov ax, [heap_start] - mov [dump_cursor], ax - clc - ret - -mem_dump_next: - ; si dump_cursor >= heap_end => CF=1 - ; lire size/flags à dump_cursor - ; renvoyer AL état, CX taille payload, DI=offset header, ES=heap_seg - ; dump_cursor += size - ret diff --git a/src/gui/draw.asm b/src/gui/draw.asm index aa8875c..f856049 100644 --- a/src/gui/draw.asm +++ b/src/gui/draw.asm @@ -1,6 +1,6 @@ ; ============================================================================= ; Project : Custom BIOS / ROM -; File : gui_lib.asm +; File : gui/draw.asm ; Author : frater ; ; License : GNU General Public License v3.0 or later (GPL-3.0+) diff --git a/src/gui/lib.asm b/src/gui/lib.asm index 6457868..d966b2f 100644 --- a/src/gui/lib.asm +++ b/src/gui/lib.asm @@ -1,6 +1,6 @@ ; ============================================================================= ; Project : Custom BIOS / ROM -; File : gui_lib.asm +; File : gui/lib.asm ; Author : frater ; ; License : GNU General Public License v3.0 or later (GPL-3.0+) @@ -21,7 +21,7 @@ ; ============================================================================= ; --- Configuration --- -%define GUI_RAM_SEG 0x0A00 ; Segment de données UI +%define GUI_RAM_SEG 0x1800 ; Segment de données UI (Safe: après Stack, avant Heap) %define GUI_MAX_WIDGETS 32 ; Nombre max de widgets simultanés ; Dimensions @@ -47,6 +47,23 @@ %define SLIDER_HORIZONTAL 1 %define SLIDER_VERTICAL 2 +; ============================================================================= +; SECTION : API ACTIONS +; ============================================================================= + +%define GUI_CREATE 0 +%define GUI_DESTROY 1 +%define GUI_GET_STATE 2 +%define GUI_GET_TYPE 3 +%define GUI_GET_VAL 4 + +gui_api_table: + dw gui_api_create + dw gui_api_destroy + dw gui_api_get_state + dw gui_api_get_type + dw gui_api_get_val + ; --- Structure d'un OBJET (Bouton, etc) --- struc widget .state resb 1 ; État (0=libre, >0=utilisé) @@ -78,10 +95,6 @@ struc widget alignb 2 ; Alignement mémoire pour performance endstruc -; ============================================================================= -; SECTION : API ACTIONS -; ============================================================================= - %macro GUI 1-* %rep %0 - 1 %rotate -1 @@ -92,19 +105,6 @@ endstruc add sp, (%0 - 1) * 2 %endmacro -%define GUI_CREATE 0 -%define GUI_DESTROY 1 -%define GUI_GET_STATE 2 -%define GUI_GET_TYPE 3 -%define GUI_GET_VAL 4 - -gui_api_table: - dw gui_api_create - dw gui_api_destroy - dw gui_api_get_state - dw gui_api_get_type - dw gui_api_get_val - ; ----------------------------------------------------------------------------- ; gui_api_create ; Crée un widget et retourne son ID diff --git a/src/gui/window.asm b/src/gui/window.asm index 57b73a9..da6d9bd 100644 --- a/src/gui/window.asm +++ b/src/gui/window.asm @@ -1,6 +1,6 @@ ; ============================================================================= ; Project : Custom BIOS / ROM -; File : gui_window.asm +; File : gui/window.asm ; Author : frater ; ; License : GNU General Public License v3.0 or later (GPL-3.0+)