diff --git a/readme.md b/readme.md index d31f747..e2ae618 100644 --- a/readme.md +++ b/readme.md @@ -22,7 +22,7 @@ The project is intentionally educational, experimental, and minimalist. - No reuse of existing BIOS code - Minimal but functional implementation - Compatible with QEMU - - Target architecture: **8086 → 80486** + - Target architecture: **i386+** - **Boot directly from ROM** - No DOS, no bootloader, no disk dependency @@ -145,7 +145,7 @@ Le projet est volontairement **éducatif, expérimental et minimaliste**. - Aucun code BIOS existant réutilisé - Implémentation minimale mais fonctionnelle - Compatible avec QEMU - - Architecture ciblée : **8086 jusqu’au 486** + - Architecture ciblée : **i386+** - **Démarrer directement depuis la ROM** - Sans DOS, sans bootloader, sans disque diff --git a/src/boot.asm b/src/boot.asm index 26170ca..59867cc 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -100,63 +100,68 @@ reset: push cs pop ds -setup_gui: - ; Initialiser le segment de données correct pour le texte du bouton - mov ax, cs ; ou DS selon où sont tes chaines - mov [btn_quit + widget.text_seg], ax + ; Init du système GUI + call gui_init_system - ; GUI WINDOW, 50,50,250,100,helloworld - - ; GFX RECTANGLE_DRAW, 50,50,250,100,0 - - ;GFX LINE_VERT, 50, 50, 100, 0 - ;GFX LINE_VERT, 300, 50, 100, 0 - - ;GFX LINE_HORIZ, 50, 300, 50, 0 - ;GFX LINE_HORIZ, 50, 300, 100, 0 + ; GFX RECTANGLE_FILL, 50, 50, 100, 30, 1 + call build_interface - mov ax,BDA_DATA_SEG - mov ds,ax -endless: +main_loop: + ; 2. Moteur GUI (Gère tout : Loop, HitTest, Draw, Callbacks) + ; DS pointe déjà sur GUI_RAM_SEG + call gui_process_all - ; 1. Récupérer souris - mov ax, [BDA_MOUSE + mouse.x] - mov cx, ax ; CX = Souris X - mov ax, [BDA_MOUSE + mouse.y] - mov dx, ax ; DX = Souris Y - mov bl, [BDA_MOUSE + mouse.status] ; BL = Boutons + ; 3. Afficher souris + ; GFX MOUSE_MOVE - ; 2. Gérer le bouton - mov si, btn_quit ; SI pointe sur l'objet - call gui_update_button ; Met à jour l'état et redessine si besoin + jmp main_loop - ; 3. Vérifier si cliqué (AL = 1) - cmp al, 1 - je action_quitter +; --- Callbacks (Fonctions appelées par le moteur) --- +on_click_quit: + hlt + jmp on_click_quit - ; 4. Afficher le curseur souris (Important : après avoir dessiné le bouton) - ; GFX MOUSE_MOVE - GFX TXT_MODE, GFX_TXT_BLACK - GFX GOTOXY, 0, 180 +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 - mov ax, [BDA_MOUSE + mouse.x] - call print_word_hex + ; Créer Bouton 1 "QUITTER" + call gui_alloc_widget ; Retourne SI = Pointeur nouveau slot + jc .mem_full - GFX PUTCH, ' ' + ; 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], 20 + mov word [gs:si + widget.text_ofs], str_quit + 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 ax, [BDA_MOUSE + mouse.y] - call print_word_hex + ; Créer Bouton 2 "HELLO" + call gui_alloc_widget + jc .mem_full - jmp endless + mov word [gs:si + widget.x], 100 + mov word [gs:si + widget.y], 10 + mov word [gs:si + widget.w], 80 + mov word [gs:si + widget.h], 20 + mov word [gs:si + widget.text_ofs], str_hello + mov word [gs:si + widget.text_seg], cs + ; Pas de callback -action_quitter: - hlt - jmp action_quitter +.mem_full: + ret +; --- Données ROM --- +str_quit db "QUITTER", 0 +str_hello db "HELLO", 0 + ; ----------------------------------------------------------- ; timer ISR (IRQ -> INT) ; ----------------------------------------------------------- @@ -175,21 +180,6 @@ timer_isr: pop ax iret -; Déclaration d'un bouton "Quitter" -btn_quit: - istruc widget - at widget.x, dw 10 - at widget.y, dw 10 - at widget.w, dw 80 - at widget.h, dw 20 - at widget.state, db GUI_STATE_NORMAL - at widget.text_ofs, dw str_quit - at widget.text_seg, dw 0 ; Sera rempli au runtime (CS ou DS) - at widget.id, db 1 - iend - -str_quit db "QUITTER", 0 - ; ------------------------------------------------------------------ ; Padding jusqu'au reset vector ; ------------------------------------------------------------------ diff --git a/src/drivers/gfx_cgam.asm b/src/drivers/gfx_cgam.asm index dda21c7..3b65ecd 100644 --- a/src/drivers/gfx_cgam.asm +++ b/src/drivers/gfx_cgam.asm @@ -837,13 +837,13 @@ cga_fill_rect: pusha push es - ; Setup ES mov ax, VIDEO_SEG mov es, ax call cga_mouse_hide - ; 1. Tri des coordonnées + ; --- 1. Tri des coordonnées (X et Y) --- + ; Tri X mov ax, .x1 mov bx, .x2 cmp ax, bx @@ -853,6 +853,7 @@ cga_fill_rect: mov .x2, bx .x_sorted: + ; Tri Y mov ax, .y1 mov bx, .y2 cmp ax, bx @@ -862,35 +863,35 @@ cga_fill_rect: mov .y2, bx .y_sorted: - ; Calculer hauteur - sub bx, ax ; BX = y2 - y1 - inc bx ; BX = hauteur - mov dx, bx ; DX = Compteur de hauteur + ; --- 2. Calcul de la hauteur --- + mov bx, .y2 + sub bx, .y1 + inc bx ; BX = Hauteur finale + push bx ; SAUVEGARDER LA HAUTEUR SUR LA PILE [STACK A] + ; (Car on va utiliser BX dans cga_calc_addr ou juste après) - ; 2. Calculer l'adresse de départ (Haut-Gauche) - mov cx, .x1 ; CX = x1 - mov dx, .y1 ; DX = y1 - push dx ; Sauve Y1 (car cga_calc_addr peut modifier DX/BX selon implémentation) + ; --- 3. Calcul adresse de départ --- + mov cx, .x1 + mov dx, .y1 - ; Note: cga_calc_addr attend CX=x, DX=y + ; Note: cga_calc_addr attend CX=x, DX=y et retourne DI + ; Si cga_calc_addr modifie BX, la sauvegarde [STACK A] est vitale. call cga_calc_addr ; DI = Offset début ligne - pop bx ; Récupère Y1 dans BX (pour savoir si pair/impair) - - ; 3. Préparer les masques gauche/droite (comme Line Horizontal) - ; Masque Gauche + ; --- 4. Préparer les masques --- + ; Masque Gauche (DH) mov cx, .x1 and cx, 7 mov dh, 0xFF - shr dh, cl ; DH = Masque Gauche + shr dh, cl - ; Masque Droite + ; Masque Droite (AH) mov cx, .x2 and cx, 7 inc cx mov ah, 0xFF shr ah, cl - not ah ; AH = Masque Droite + not ah ; Index octets mov si, .x1 @@ -898,35 +899,32 @@ cga_fill_rect: mov cx, .x2 shr cx, 3 ; CX = Index end byte - ; Récupérer la hauteur (Y2 - Y1 + 1) - mov bp, .y2 - sub bp, .y1 - inc bp ; BP = Hauteur (Loop counter) - - ; Sauvegarder l'état pair/impair initial - ; Si Y1 est impair, le premier saut de ligne doit être "vers le haut" (-0x2000 + 80) - ; ou plus simplement : on gère le flip 0x2000 à chaque ligne. + ; --- 5. Récupérer la hauteur dans un registre sûr --- + pop bx ; RESTAURER HAUTEUR DANS BX [STACK A] + ; BX est maintenant notre compteur de boucle. + ; BP reste intact, donc .color est accessible ! ; --- BOUCLE VERTICALE --- .row_loop: - push di ; Sauve le début de la ligne actuelle - push si ; Sauve index byte start + push di ; Sauve début ligne + push si ; Sauve index byte - ; --- Remplissage Horizontal (copié de line_horizontal) --- + ; --- Remplissage Horizontal --- - ; Cas Single Byte + ; Cas Single Byte (Start == End) cmp si, cx je .single_byte_row ; Partie Gauche - mov al, dh ; Masque Gauche + mov al, dh call .apply_mask_fill inc di inc si - ; Partie Centrale + ; Partie Centrale (Boucle tant que SI < CX) jmp .check_mid .mid_loop: + ; Ici BP est valide, donc .color est correct cmp byte .color, 0 je .blk mov byte [es:di], 0xFF @@ -941,42 +939,36 @@ cga_fill_rect: jl .mid_loop ; Partie Droite - mov al, ah ; Masque Droite + mov al, ah call .apply_mask_fill jmp .row_done .single_byte_row: mov al, dh - and al, ah ; Intersection masques + and al, ah ; Intersection des masques call .apply_mask_fill .row_done: pop si - pop di ; Restaure début de ligne + pop di - ; --- Passage à la ligne suivante (Logique CGA optimisée) --- - xor di, 0x2000 ; Flip banque + ; --- Passage ligne suivante (CGA Bank Switch) --- + xor di, 0x2000 test di, 0x2000 - jnz .bank_switch_ok ; Si on passe de 0->1, on a descendu d'une ligne visuelle - add di, 80 ; Si on passe de 1->0, on remonte, donc faut ajouter 80 + jnz .bank_switch_ok + add di, 80 .bank_switch_ok: - dec bp + dec bx ; On décrémente BX (Hauteur) au lieu de BP jnz .row_loop call cga_mouse_show pop es - ; clean defs - %undef .x1 - %undef .y1 - %undef .x2 popa leave ret -; Helper local pour Fill Rect -; AL = Masque, .color = couleur .apply_mask_fill: cmp byte .color, 0 jz .ap_black @@ -986,6 +978,14 @@ cga_fill_rect: not al and byte [es:di], al ret + + ; Nettoyage des defines + %undef .x1 + %undef .y1 + %undef .x2 + %undef .y2 + %undef .color + ; ; ------------------------------------------------------------ ; GESTION DU CURSEUR diff --git a/src/drivers/mouse_ps2.asm b/src/drivers/mouse_ps2.asm index 9a7ad2c..923b486 100644 --- a/src/drivers/mouse_ps2.asm +++ b/src/drivers/mouse_ps2.asm @@ -306,14 +306,15 @@ isr_mouse_handler: ; debut du décodage des données mov bl, [BDA_MOUSE + mouse.buffer] ; status - mov al,bl - ; --- BOUTONS (Optionnel mais recommandé) --- + + ; --- BOUTONS --- + mov al,bl ; extraction des boutons and al, 00000111b ; Bits 0, 1, 2 = Gauche, Droite, Milieu mov [BDA_MOUSE + mouse.status], al xor ax,ax mov al, byte [BDA_MOUSE + mouse.buffer+1] ; delta X - test bl,00010000b ; signe X + test bl,00010000b ; signe X (4eme bit) jz .x_pos mov ah,0xff ; X est négatif .x_pos: @@ -321,7 +322,7 @@ isr_mouse_handler: xor ax,ax mov al, byte [BDA_MOUSE + mouse.buffer+2] ; delta Y - test bl,00100000b ; signe Y + test bl,00100000b ; signe Y (5eme bit) jz .y_pos mov ah,0xff ; y est négatif .y_pos: diff --git a/src/services/gui_lib.asm b/src/services/gui_lib.asm index 3fd48e5..b4b7863 100644 --- a/src/services/gui_lib.asm +++ b/src/services/gui_lib.asm @@ -21,7 +21,7 @@ ; ============================================================================= ; --- Configuration Mémoire --- -%define GUI_RAM_SEG 0x0800 ; Segment de données UI +%define GUI_RAM_SEG 0x0A00 ; Segment de données UI %define GUI_MAX_WIDGETS 32 ; Nombre max de widgets simultanés ; --- Drapeaux & États --- @@ -39,7 +39,6 @@ struc widget .y resw 1 ; Position Y .w resw 1 ; Largeur .h resw 1 ; Hauteur - .state resb 1 ; État (0=Normal, 1=Hover, 2=Pressed, 3=Disabled) .text_ofs resw 1 ; Offset du texte .text_seg resw 1 ; Segment du texte .event_click resw 1 ; adresse de la fonction "on click" @@ -60,42 +59,46 @@ endstruc ; Entrée : DS doit pointer vers GUI_RAM_SEG ; ----------------------------------------------------------------------------- gui_init_system: - pushad - push ds + push eax + push cx + push es + push di mov ax, GUI_RAM_SEG - mov ds, ax + mov es, ax - mov di, 0 ; On commence au début du segment 0x0800 + xor di, di mov cx, (widget_size * GUI_MAX_WIDGETS) shl cx, 2 ; cx / 4 xor eax, eax rep stosd ; Remplit tout de 0 - pop ds - popad + pop di + pop es + pop cx + pop eax ret ; ----------------------------------------------------------------------------- ; gui_alloc_widget ; Cherche un slot vide et retourne son adresse ; Sortie : -; - Succès : CF=0 (Carry Clear), SI = Offset du widget -; - Echec : CF=1 (Carry Set), SI = Indéfini (ou 0) +; - Succès : CF=0 (Carry Clear), DS:SI = Offset du widget +; - Echec : CF=1 (Carry Set), DS:SI = Indéfini (ou 0) ; ----------------------------------------------------------------------------- gui_alloc_widget: + push ax push cx push bx - push ds mov ax, GUI_RAM_SEG - mov ds, ax + mov gs, ax mov si, 0 ; Début du pool mov cx, GUI_MAX_WIDGETS .loop_find: - cmp byte [si + widget.state], GUI_STATE_FREE + cmp byte [gs:si + widget.state], GUI_STATE_FREE je .found ; Si state == 0, c'est libre ! add si, widget_size ; Sinon on passe au suivant @@ -107,16 +110,16 @@ gui_alloc_widget: .found: ; On initialise le slot trouvé - mov byte [si + widget.state], GUI_STATE_NORMAL ; Marquer comme occupé + mov byte [gs:si + widget.state], GUI_STATE_NORMAL ; Marquer comme occupé ; Reset des champs critiques pour éviter les déchets - mov word [si + widget.callback], 0 + mov word [gs:si + widget.event_click], 0 clc ; Clear Carry Flag .done: - pop ds pop bx pop cx + pop ax ret ; ----------------------------------------------------------------------------- @@ -125,11 +128,25 @@ gui_alloc_widget: ; Entrée : SI = Pointeur widget ; ----------------------------------------------------------------------------- gui_free_widget: - mov byte [si + widget.state], GUI_STATE_FREE + push eax + push es + + mov ax, GUI_RAM_SEG + mov es, ax + + xor di, di + mov cx, (widget_size) + shl cx, 2 ; cx / 4 + xor eax, eax + rep stosd ; Remplit tout de 0 + + mov byte [gs:si + widget.state], GUI_STATE_FREE ; On efface visuellement le widget (optionnel, remplit de blanc) ; Pour l'instant on le marque juste libre, il sera redessiné par-dessus. - ret + pop es + pop eax + ret ; ============================================================================= ; SECTION : MOTEUR D'ÉVÉNEMENTS (EVENT LOOP) @@ -146,31 +163,46 @@ gui_free_widget: ; ----------------------------------------------------------------------------- gui_process_all: pusha + push gs + + mov ax, BDA_DATA_SEG + mov ds, ax + + mov cx, [BDA_MOUSE + mouse.x] + mov dx, [BDA_MOUSE + mouse.y] + mov bl, [BDA_MOUSE + mouse.status] + + mov ax, GUI_RAM_SEG + mov gs, ax mov si, 0 ; Pointeur début tableau mov di, GUI_MAX_WIDGETS ; Compteur .loop_widgets: ; Est-ce que ce slot est occupé ? - cmp byte [si + widget.state], GUI_STATE_FREE + cmp byte [gs:si + widget.state], GUI_STATE_FREE je .next_widget + ; mov ah, [gs:si + widget.state] ; AH = Ancien état du widget + ; --- Logique Widget --- - push bx ; Sauver état boutons souris + ;push bx ; Sauver état boutons souris + push ax call gui_update_logic ; Vérifier collision/clic + ;pop ax ; Si AL=1 (Clic relâché validé), exécuter le callback cmp al, 1 jne .no_action ; Vérifier si un callback est défini - cmp word [si + widget.callback], 0 + cmp word [gs:si + widget.event_click], 0 je .no_action ; APPEL DU CALLBACK (Fonction utilisateur) ; On sauve les registres car le callback peut faire n'importe quoi pusha - call word [si + widget.callback] + call word [gs:si + widget.event_click] popa .no_action: @@ -184,6 +216,7 @@ gui_process_all: dec di jnz .loop_widgets + pop ds popa ret @@ -193,25 +226,25 @@ gui_process_all: ; (Interne) Met à jour l'état d'un seul widget ; Entrée : SI=Widget, CX=MouseX, DX=MouseY, BL=Buttons -; Sortie : AL=1 si Clicked, 0 sinon. Met à jour [SI].state +; Sortie : AL=1 si Clicked, 0 sinon. Met à jour [gs:si].state gui_update_logic: xor ax, ax - cmp byte [si + widget.state], GUI_STATE_DISABLED + cmp byte [gs:si + widget.state], GUI_STATE_DISABLED je .done ; --- Hit Test --- - cmp cx, [si + widget.x] + cmp cx, [gs:si + widget.x] jl .miss - mov bp, [si + widget.x] - add bp, [si + widget.w] + mov bp, [gs:si + widget.x] + add bp, [gs:si + widget.w] cmp cx, bp jg .miss - cmp dx, [si + widget.y] + cmp dx, [gs:si + widget.y] jl .miss - mov bp, [si + widget.y] - add bp, [si + widget.h] + mov bp, [gs:si + widget.y] + add bp, [gs:si + widget.h] cmp dx, bp jg .miss @@ -220,26 +253,26 @@ gui_update_logic: jz .released ; Clic enfoncé - mov byte [si + widget.state], GUI_STATE_PRESSED + mov byte [gs:si + widget.state], GUI_STATE_PRESSED jmp .done -.released: + .released: ; Bouton relâché. Était-il pressé avant ? - cmp byte [si + widget.state], GUI_STATE_PRESSED + cmp byte [gs:si + widget.state], GUI_STATE_PRESSED jne .hover ; Clic validé ! mov al, 1 - mov byte [si + widget.state], GUI_STATE_HOVER + mov byte [gs:si + widget.state], GUI_STATE_HOVER jmp .done -.hover: - mov byte [si + widget.state], GUI_STATE_HOVER + .hover: + mov byte [gs:si + widget.state], GUI_STATE_HOVER jmp .done -.miss: - mov byte [si + widget.state], GUI_STATE_NORMAL -.done: + .miss: + mov byte [gs:si + widget.state], GUI_STATE_NORMAL + .done: ret ; (Interne) Dessine le widget pointé par SI @@ -247,10 +280,10 @@ gui_draw_single_widget: pusha ; Chargement coords - mov ax, [si + widget.x] - mov bx, [si + widget.y] - mov cx, [si + widget.w] - mov dx, [si + widget.h] + mov ax, [gs:si + widget.x] + mov bx, [gs:si + widget.y] + mov cx, [gs:si + widget.w] + mov dx, [gs:si + widget.h] ; Calcul X2, Y2 mov di, ax @@ -259,19 +292,19 @@ gui_draw_single_widget: add bp, dx ; Dispatch selon état - mov al, [si + widget.state] + mov al, [gs:si + widget.state] cmp al, GUI_STATE_PRESSED je .paint_pressed cmp al, GUI_STATE_HOVER je .paint_hover -.paint_normal: + .paint_normal: GFX RECTANGLE_FILL, ax, bx, di, bp, 1 ; Blanc GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 ; Bord Noir GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT jmp .text -.paint_hover: + .paint_hover: GFX RECTANGLE_FILL, ax, bx, di, bp, 1 GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 ; Effet gras @@ -282,36 +315,38 @@ gui_draw_single_widget: GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 ; Restauration coords pour le texte - mov ax, [si + widget.x] - mov bx, [si + widget.y] + mov ax, [gs:si + widget.x] + mov bx, [gs:si + widget.y] jmp .text_setup -.paint_pressed: + .paint_pressed: GFX RECTANGLE_FILL, ax, bx, di, bp, 0 ; Noir GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT jmp .text -.text_setup: + .text_setup: ; Petit hack pour recentrer le texte après l'effet gras - mov cx, [si + widget.w] - mov dx, [si + widget.h] + mov cx, [gs:si + widget.w] + mov dx, [gs:si + widget.h] -.text: + .text: ; --- Centrage Texte (Simplifié) --- push si - mov es, [si + widget.text_seg] - mov di, [si + widget.text_ofs] + mov es, [gs:si + widget.text_seg] + mov di, [gs:si + widget.text_ofs] xor cx, cx -.strlen: + + .strlen: cmp byte [es:di], 0 je .calc inc cx inc di jmp .strlen -.calc: + + .calc: shl cx, 3 ; Largeur texte px push ax ; Sauve X original - mov ax, [si + widget.w] + mov ax, [gs:si + widget.w] sub ax, cx shr ax, 1 pop bx ; Récupère X original dans BX (oups, on veut ajouter) @@ -319,17 +354,17 @@ gui_draw_single_widget: push ax ; X final prêt - mov bx, [si + widget.h] + mov bx, [gs:si + widget.h] sub bx, 8 shr bx, 1 - add bx, [si + widget.y] ; Y final + add bx, [gs:si + widget.y] ; Y final pop cx ; CX = X Final, BX = Y Final GFX GOTOXY, cx, bx - mov dx, [si + widget.text_seg] - mov ax, [si + widget.text_ofs] + mov dx, [gs:si + widget.text_seg] + mov ax, [gs:si + widget.text_ofs] GFX WRITE, dx, ax pop si @@ -347,10 +382,10 @@ gui_draw_button: pusha ; Charger les propriétés depuis la structure - mov ax, [si + widget.x] - mov bx, [si + widget.y] - mov cx, [si + widget.w] - mov dx, [si + widget.h] + mov ax, [gs:si + widget.x] + mov bx, [gs:si + widget.y] + mov cx, [gs:si + widget.w] + mov dx, [gs:si + widget.h] ; Calculer X2 et Y2 pour le rectangle mov di, ax ; X1 @@ -359,13 +394,13 @@ gui_draw_button: add bp, dx ; Y2 = Y + H ; Vérifier l'état - cmp byte [si + widget.state], GUI_STATE_PRESSED + cmp byte [gs:si + widget.state], GUI_STATE_PRESSED je .draw_pressed - cmp byte [si + widget.state], GUI_STATE_HOVER + cmp byte [gs:si + widget.state], GUI_STATE_HOVER je .draw_hover - cmp byte [si + widget.state], GUI_STATE_DISABLED + cmp byte [gs:si + widget.state], GUI_STATE_DISABLED je .draw_disabled .draw_normal: @@ -392,10 +427,10 @@ gui_draw_button: GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 ; Restaurer les coords pour le texte - mov ax, [si + widget.x] - mov bx, [si + widget.y] + mov ax, [gs:si + widget.x] + mov bx, [gs:si + widget.y] mov di, ax - add di, [si + widget.w] + add di, [gs:si + widget.w] GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT jmp .draw_text @@ -425,8 +460,8 @@ gui_draw_button: push si ; 1. Calculer longueur chaine - mov es, [si + widget.text_seg] - mov di, [si + widget.text_ofs] + mov es, [gs:si + widget.text_seg] + mov di, [gs:si + widget.text_ofs] xor cx, cx .strlen: @@ -440,21 +475,21 @@ gui_draw_button: ; CX = Longueur chaine shl cx, 3 ; CX * 8 (largeur en pixels du texte) - mov ax, [si + widget.w] + mov ax, [gs:si + widget.w] sub ax, cx ; Marge horizontale totale shr ax, 1 ; Diviser par 2 - add ax, [si + widget.x] ; X final + add ax, [gs:si + widget.x] ; X final - mov bx, [si + widget.h] + mov bx, [gs:si + widget.h] sub bx, 8 ; Hauteur fonte (8px) shr bx, 1 ; Diviser par 2 - add bx, [si + widget.y] ; Y final + add bx, [gs:si + widget.y] ; Y final ; 2. Dessiner GFX GOTOXY, ax, bx - mov dx, [si + widget.text_seg] - mov ax, [si + widget.text_ofs] + mov dx, [gs:si + widget.text_seg] + mov ax, [gs:si + widget.text_ofs] GFX WRITE, dx, ax pop si @@ -482,27 +517,27 @@ gui_update_button: xor ax, ax ; AL = Return value (0) ; Si désactivé, on ne fait rien - cmp byte [si + widget.state], GUI_STATE_DISABLED + cmp byte [gs:si + widget.state], GUI_STATE_DISABLED je .quit ; 1. Test de collision (Hit Test) ; X >= Widget.X ? - cmp cx, [si + widget.x] + cmp cx, [gs:si + widget.x] jl .no_hit ; X <= Widget.X + Widget.W ? - mov di, [si + widget.x] - add di, [si + widget.w] + mov di, [gs:si + widget.x] + add di, [gs:si + widget.w] cmp cx, di jg .no_hit ; Y >= Widget.Y ? - cmp dx, [si + widget.y] + cmp dx, [gs:si + widget.y] jl .no_hit ; Y <= Widget.Y + Widget.H ? - mov di, [si + widget.y] - add di, [si + widget.h] + mov di, [gs:si + widget.y] + add di, [gs:si + widget.h] cmp dx, di jg .no_hit @@ -513,27 +548,27 @@ gui_update_button: jz .hovering ; -> Clic maintenu - mov byte [si + widget.state], GUI_STATE_PRESSED + mov byte [gs:si + widget.state], GUI_STATE_PRESSED jmp .draw_update .hovering: ; Souris dessus, mais pas de clic ; Si on était en PRESSED juste avant, c'est un "CLICK" valide (Relâchement) - cmp byte [si + widget.state], GUI_STATE_PRESSED + cmp byte [gs:si + widget.state], GUI_STATE_PRESSED jne .just_hover ; C'est un click valide ! mov al, 1 ; Return CLICKED - mov byte [si + widget.state], GUI_STATE_HOVER + mov byte [gs:si + widget.state], GUI_STATE_HOVER jmp .draw_update .just_hover: - mov byte [si + widget.state], GUI_STATE_HOVER + mov byte [gs:si + widget.state], GUI_STATE_HOVER jmp .draw_update .no_hit: ; Souris en dehors - mov byte [si + widget.state], GUI_STATE_NORMAL + mov byte [gs:si + widget.state], GUI_STATE_NORMAL .draw_update: ; Appelle la fonction de dessin pour mettre à jour le visuel