add cga_line_xxxxx

This commit is contained in:
Frater 2026-01-29 22:05:57 +01:00
commit 2fb87be644
5 changed files with 528 additions and 246 deletions

View file

@ -42,7 +42,7 @@ The project is intentionally educational, experimental, and minimalist.
- Real-mode execution constraints - Real-mode execution constraints
- **Improve mastery of x86 assembly** - **Improve mastery of x86 assembly**
- Real-mode x86 assembly (8086-compatible) - Real-mode x86 assembly (i386+ compatible)
- Performance-oriented routines - Performance-oriented routines
- Hardware-near programming - Hardware-near programming
- Clear separation between hardware-specific drivers and core logic - Clear separation between hardware-specific drivers and core logic
@ -165,7 +165,7 @@ Le projet est volontairement **éducatif, expérimental et minimaliste**.
- Contraintes du mode réel - Contraintes du mode réel
- **Perfectionner la maîtrise de l’assembleur** - **Perfectionner la maîtrise de l’assembleur**
- Assembleur x86 en mode réel (compatible 8086) - Assembleur x86 en mode réel (i386+ compatible)
- Routines optimisées pour la performance - Routines optimisées pour la performance
- Programmation proche du matériel - Programmation proche du matériel
- Séparation claire entre cœur du BIOS et drivers matériels - Séparation claire entre cœur du BIOS et drivers matériels

View file

@ -55,29 +55,26 @@
%define BKG_TOTAL_BYTES (BKG_TOTAL_DWORDS * 4) ; 64 %define BKG_TOTAL_BYTES (BKG_TOTAL_DWORDS * 4) ; 64
struc mouse struc mouse
.buffer resb 4 ; i8042 input buffer 0..3 .buffer resb 4 ; i8042 input buffer
.idx resb 1 ; index in the buffer 4 .idx resb 1 ; index in the buffer
.packetlen resb 1 ; max buffer len (3 ou 4) 5 .packetlen resb 1 ; max buffer len (3 ou 4)
.blinker resb 1 ; .status resb 1 ; mouse status (button etc)
.wheel resb 1 ; if a packet size is 4; experimental
.status resb 1 ; mouse status (button etc) 6 .x resw 1 ;
.wheel resb 1 ; if a packet size is 4; experimental 7 .y resw 1 ;
.x resw 1 ; 8..9
.y resw 1 ; A..B
; cursor management ; cursor management
.cur_x resw 1 ; preservation des x,y du background C..D .cur_x resw 1 ; preservation des x,y du background
.cur_y resw 1 ; E..F .cur_y resw 1 ;
.cur_addr_start resw 1 ; adresse de départ de la sauvegarde 10..11 .cur_addr_start resw 1 ; adresse de départ de la sauvegarde
.cur_bank_add resw 1 ; + ou - 0x2000 12..13
.cur_drawing resb 1 ; 14 .cur_drawing resb 1 ;
.cur_visible resb 1 ; 15 .cur_visible resb 1 ;
.cur_seg resw 1 ; segment / offset of the pointer 16..17 .cur_counter resb 1 ; compteur de fois que le curseur a été caché (0 = visible, <0 = invisible)
.cur_ofs resw 1 ; 18..19 .cur_seg resw 1 ; segment / offset of the pointer
.cur_mask resb 1 ; pixels mask (bit per pixel) 1A .cur_ofs resw 1 ;
.cur_bit_ofs resb 1 ; 0..7 bits d'offset (x&7) 1B .cur_mask resb 1 ; pixels mask (bit per pixel)
.bkg_saved resb 1 ; background saved 1C .bkg_saved resb 1 ; background saved
alignb 4 ; alignement 4 bytes 1D alignb 4 ; alignement 4 bytes
.bkg_buffer resd 16 ; buffer for saved background .bkg_buffer resd 16 ; buffer for saved background
endstruc endstruc

View file

@ -203,13 +203,6 @@ endless:
mov ax, [BDA_MOUSE + mouse.y] mov ax, [BDA_MOUSE + mouse.y]
call print_word_hex call print_word_hex
mov al,' '
call cga_putc
xor ah,ah
mov al, [BDA_MOUSE + mouse.blinker]
call print_word_hex
jmp endless jmp endless

View file

@ -59,10 +59,9 @@
%define GFX_PUTPIXEL 1 %define GFX_PUTPIXEL 1
%define GFX_GETPIXEL 2 %define GFX_GETPIXEL 2
%define GFX_GOTOXY 3 %define GFX_GOTOXY 3
%define GFX_PUTCH_MODE 4 %define GFX_PUTCH 4
%define GFX_PUTCH 5 %define GFX_WRITE 5
%define GFX_WRITE 6 %define GFX_CRS_UPDATE 6
%define GFX_CRS_UPDATE 7
; ------------------------------------------------------------ ; ------------------------------------------------------------
; COMMENTAIRE SUR LA MÉTHODE D'APPEL ; COMMENTAIRE SUR LA MÉTHODE D'APPEL
@ -80,14 +79,16 @@
; ------------------------------------------------------------ ; ------------------------------------------------------------
graph_driver: graph_driver:
dw cga_init dw cga_init ; init de la carte graphique
dw cga_putpixel dw cga_putpixel ; dessin d'un pixel
dw cga_getpixel dw cga_getpixel ; lecture d'un pixel
dw cga_set_charpos dw cga_set_charpos ; gotoxy
dw cga_none dw cga_putc ; dessin d'un caractère
dw cga_putc dw cga_write ; dessin d'une chaine de caractère
dw cga_write dw cga_mouse_cursor_move ; déplacement du curseur
dw cga_cursor_move dw cga_line_vertical ; dessin d'une ligne verticale
; jmp gfx_fill_rect ; Remplissage rectangle (Nouveau) ; jmp gfx_fill_rect ; Remplissage rectangle (Nouveau)
; jmp gfx_invert_rect ; Offset +12: Inversion (Nouveau pour Menus) ; jmp gfx_invert_rect ; Offset +12: Inversion (Nouveau pour Menus)
; jmp gfx_draw_hline ; Offset +15: Ligne horizontale rapide (Nouveau) ; jmp gfx_draw_hline ; Offset +15: Ligne horizontale rapide (Nouveau)
@ -137,6 +138,7 @@ cga_init:
call cga_background call cga_background
ret ret
; dummy functions
cga_none: cga_none:
ret ret
@ -233,9 +235,11 @@ get_glyph_offset:
ja .qmark ; al > 7Eh ('~') ja .qmark ; al > 7Eh ('~')
sub al, 0x20 sub al, 0x20
jmp .ok jmp .ok
.qmark: .qmark:
mov al, '?' mov al, '?'
sub al, 0x20 sub al, 0x20
.ok: .ok:
xor ah, ah xor ah, ah
mov si, ax mov si, ax
@ -269,6 +273,7 @@ cga_putc:
mov ch, [fs:BDA_GFX + gfx.cur_mode] mov ch, [fs:BDA_GFX + gfx.cur_mode]
mov cl, [fs:BDA_GFX + gfx.cur_shift] mov cl, [fs:BDA_GFX + gfx.cur_shift]
mov bp,4 mov bp,4
.row_loop: .row_loop:
; gestion du fond de texte ; gestion du fond de texte
test ch, 00000010b ; transparent background ? test ch, 00000010b ; transparent background ?
@ -350,8 +355,8 @@ cga_write:
je .done je .done
call cga_putc call cga_putc
jmp .loops jmp .loops
.done:
.done:
pop ax pop ax
ret ret
@ -369,6 +374,7 @@ cga_local_putpixel:
; write ; write
cmp bl, 0 cmp bl, 0
je .clear je .clear
.set: .set:
or byte [es:di], ah or byte [es:di], ah
jmp .done jmp .done
@ -391,7 +397,6 @@ cga_putpixel:
push ax push ax
push di push di
push es push es
mov ax, VIDEO_SEG mov ax, VIDEO_SEG
mov es,ax mov es,ax
call cga_calc_addr call cga_calc_addr
@ -399,6 +404,7 @@ cga_putpixel:
; write ; write
cmp bl, 0 cmp bl, 0
je .clear je .clear
.set: .set:
or byte [es:di], ah or byte [es:di], ah
jmp .done jmp .done
@ -447,17 +453,6 @@ cga_background:
rep stosd rep stosd
ret ret
; ------------------------------------------------------------
; Calcule les infos de l'alignement du curseur, basé sur cx=X
; il est important que DS pointe sur le BDA_MOUSE_SEG
; Out: BDA_CURSOR_BITOFF, BDA_CURSOR_BYTES
; ------------------------------------------------------------
cga_cursor_calc_align:
mov al, cl
and al, 7 ; offset = x&7
mov byte [BDA_MOUSE + mouse.cur_bit_ofs], al
ret
; ------------------------------------------------------------ ; ------------------------------------------------------------
; Dessine un pixel, en [ES:DI] accès VRAM direct. ; Dessine un pixel, en [ES:DI] accès VRAM direct.
; ;
@ -468,9 +463,11 @@ cga_cursor_calc_align:
cga_putpixel_fast: cga_putpixel_fast:
cmp bl, 0 cmp bl, 0
je .clear je .clear
.set: .set:
or byte [es:di], ah or byte [es:di], ah
jmp .done jmp .done
.clear: .clear:
not ah not ah
and byte [es:di], ah and byte [es:di], ah
@ -487,7 +484,297 @@ cga_getpixel_fast:
setnz al setnz al
ret ret
cga_cursor_move: ; ------------------------------------------------------------
; cga_line_vertical
; Dessine une ligne de (AX, BX) à (AX, DX) cl: color
; ------------------------------------------------------------
cga_line_vertical:
pusha
push es
; Setup ES = Video Segment (0xB800)
mov si, VIDEO_SEG
mov es, si
call cga_mouse_hide
; Ordonner Y (BX < DX)
cmp bx, dx
jle .y_sorted
xchg bx, dx
.y_sorted:
; Calculer l'adresse du premier point (X, Y_Start)
push dx ; Sauve Y_Fin
push cx ; Sauve Couleur
mov cx, ax ; CX = X (pour cga_calc_addr)
mov dx, bx ; DX = Y_Start (pour cga_calc_addr)
call cga_calc_addr ; Return: DI=Offset, AH=BitMask (ex: 00100000)
pop cx ; Récup Couleur
pop dx ; Récup Y_Fin (dans DX)
; Préparer le masque inverse pour le cas "Noir" (AND)
mov al, ah ; AL = Masque (00100000)
not al ; AL = ~Masque (11011111)
; BX sert maintenant de compteur courant, on a Y_Fin dans DX
; Utilisons BP comme compteur de hauteur.
sub dx, bx ; DX = Hauteur - 1 (delta)
inc dx ; DX = Nombre de pixels à dessiner
.v_loop:
cmp cl, 0
jz .draw_black
.draw_white:
or byte [es:di], ah ; Allumer le bit
jmp .next_line
.draw_black:
and byte [es:di], al ; Eteindre le bit
.next_line:
; --- Logique Next Line CGA (Optimisée) ---
xor di, 0x2000 ; Flip banque (Pair <-> Impair)
test di, 0x2000 ; Si on est passé à 0x2000 (Impair), on a juste descendu de 1 ligne visuelle
jnz .skip_add ; C'est bon.
add di, 80 ; Si on revient à 0x0000 (Pair), on doit avancer de 80 octets
.skip_add:
dec dx
jnz .v_loop
call cga_mouse_show
pop es
popa
ret
; ------------------------------------------------------------
; cga_line_horizontal
; Dessine une ligne de (AX, DX) à (BX, DX) CL: color
; ------------------------------------------------------------
cga_line_horizontal:
pusha
push es
call cga_mouse_hide
mov si, VIDEO_SEG
mov es, si
; 1. Ordonner X (AX < BX)
cmp ax, bx
jle .x_sorted
xchg ax, bx
.x_sorted:
; Calculer l'adresse de départ
; On a besoin de l'adresse de l'octet de AX.
push cx ; Sauve Couleur
push bx ; Sauve X_Fin
mov cx, ax ; CX = X_Start
; DX est déjà Y
call cga_calc_addr ; DI = Offset du premier byte
; AH = Masque du bit précis (pas utile pour le remplissage, on recalculera)
pop bx ; Récup X_Fin
pop cx ; Récup Couleur (CL)
; --- ANALYSE DES OCTETS ---
; Start Byte Index = AX >> 3
; End Byte Index = BX >> 3
mov si, ax ; SI = X_Start
shr si, 3 ; Byte index start
mov bp, bx ; BP = X_Fin
shr bp, 3 ; Byte index end
; Cas spécial : Tout tient dans le même octet ?
cmp si, bp
je .single_byte
; ============================================
; CAS MULTI-BYTE
; ============================================
; --- 1. Gérer l'octet de GAUCHE (Start) ---
; On doit dessiner du bit (AX&7) jusqu'au bit 0 (droite de l'octet)
; Masque = 0xFF >> (AX & 7)
push cx ; Save couleur
mov cx, ax
and cx, 7 ; X % 8
mov al, 0xFF
shr al, cl ; AL = Masque Gauche (ex: si X%8=2, 00111111)
pop cx ; Restore couleur
call .apply_mask_at_di ; Applique AL sur [ES:DI] selon CL
inc di ; Octet suivant
inc si ; Index suivant
; --- 2. Gérer le MILIEU (Full Bytes) ---
; Tant que SI < BP, on remplit tout l'octet
jmp .check_middle
.loop_middle:
cmp cl, 0
jz .middle_black
mov byte [es:di], 0xFF ; Blanc total
jmp .middle_next
.middle_black:
mov byte [es:di], 0x00 ; Noir total
.middle_next:
inc di
inc si
.check_middle:
cmp si, bp
jl .loop_middle ; Continue tant qu'on n'est pas au dernier octet
; Gérer l'octet de DROITE (End) ---
; On doit dessiner du bit 7 jusqu'au bit (BX&7)
; Masque = ~(0xFF >> ((BX & 7) + 1))
; Ex: Si Fin%8 = 0 (1 pixel), shift 1 -> 01111111, NOT -> 10000000. Correct.
push cx
mov cx, bx
and cx, 7
inc cx ; Nb de pixels à garder à gauche
mov al, 0xFF
shr al, cl ; Masque des pixels à IGNORER (droite)
not al ; Masque des pixels à DESSINER (gauche)
pop cx
call .apply_mask_at_di
jmp .done
; ============================================
; CAS SINGLE BYTE (Début et Fin dans le même octet)
; ============================================
.single_byte:
; Masque Start = 0xFF >> (AX & 7)
push cx
mov cx, ax
and cx, 7
mov ah, 0xFF
shr ah, cl ; AH = Masque départ (ex: 00011111)
; Masque End (Pixels à garder jusqu'à BX)
; On veut garder les pixels de 7 jusqu'à BX&7
; Astuce: Masque Combiné = MasqueStart AND MasqueEnd
mov cx, bx
and cx, 7
inc cx
mov al, 0xFF
shr al, cl
not al ; AL = Masque Fin (ex: 11110000)
and al, ah ; Intersection des deux masques
pop cx
call .apply_mask_at_di
jmp .done
; --- Helper interne : Applique le masque AL à l'adresse DI selon couleur CL ---
.apply_mask_at_di:
cmp cl, 0
jz .apply_black
or [es:di], al ; Blanc: OR Masque
ret
.apply_black:
not al ; Noir: AND ~Masque
and [es:di], al
ret
.done:
call cga_mouse_show
pop es
popa
ret
;
; ------------------------------------------------------------
; GESTION DU CURSEUR
; ------------------------------------------------------------
;
; ------------------------------------------------------------
; gère la demande d'effacement du curseur souris
;
; ------------------------------------------------------------
cga_mouse_hide:
pushf ; Sauver l'état des flags (interrupts)
cli ; Désactiver les interruptions (CRITIQUE)
push ax
push ds
mov ax, BDA_DATA_SEG
mov ds, ax
; Décrémenter le compteur
dec byte [BDA_MOUSE + mouse.cur_counter]
; Vérifier si on vient juste de passer en mode caché (c-à-d on est à -1)
cmp byte [BDA_MOUSE + mouse.cur_counter], -1
jne .skip_restore ; Si on est à -2, -3... elle est déjà cachée
; C'est la transition Visible -> Caché : On efface le curseur
call cga_cursor_restorebg
; On marque qu'on ne dessine plus
mov byte [BDA_MOUSE + mouse.bkg_saved], 0
.skip_restore:
pop ds
pop ax
popf ; Restaure les interruptions (STI si elles étaient là)
ret
; ------------------------------------------------------------
; gère la demande d'affichage du curseur souris
;
; ------------------------------------------------------------
cga_mouse_show:
pushf
cli
push ax
push ds
mov ax, BDA_DATA_SEG
mov ds, ax
; Incrémenter le compteur
inc byte [BDA_MOUSE + mouse.cur_counter]
; Vérifier si on est revenu à 0 (Visible)
cmp byte [BDA_MOUSE + mouse.cur_counter], 0
jne .skip_draw ; Si on est encore à -1, -2... on reste caché
; C'est la transition Caché -> Visible : On affiche le curseur
; IMPORTANT : On sauve le fond ACTUEL (qui a peut-être changé pendant le hide)
call cga_cursor_savebg
call cga_cursor_draw
.skip_draw:
pop ds
pop ax
popf
ret
; ------------------------------------------------------------
; gère le déplacement du curseur souris
;
; ------------------------------------------------------------
cga_mouse_cursor_move:
push ax push ax
push ds push ds
push es push es
@ -496,8 +783,8 @@ cga_cursor_move:
mov ax, BDA_DATA_SEG mov ax, BDA_DATA_SEG
mov ds, ax mov ds, ax
cmp byte [BDA_MOUSE + mouse.cur_visible], 0 cmp byte [BDA_MOUSE + mouse.cur_counter], 0
je .done jl .done ; Si < 0, on ne dessine rien !
cmp byte [BDA_MOUSE + mouse.cur_drawing],0 cmp byte [BDA_MOUSE + mouse.cur_drawing],0
jne .done jne .done
@ -518,11 +805,11 @@ cga_cursor_move:
pop ax pop ax
ret ret
; ----------------------------------------------- ; ------------------------------------------------------------
; cga_cursor_savebg_32 ; cga_cursor_savebg
; Sauve 16 lignes (3 bytes/ligne) sous le curseur. ; Sauve 16 lignes (4 bytes/ligne) sous le curseur.
; Stocke 16 DWORDs: chaque DWORD contient (b0|b1<<8|b2<<16) dans les 24 bits bas. ; Stocke 16 DWORDs: chaque DWORD contient les 4 bytes de la ligne
; ----------------------------------------------- ; ------------------------------------------------------------
cga_cursor_savebg: cga_cursor_savebg:
cmp byte [BDA_MOUSE + mouse.bkg_saved], 0 ; le buffer n'a pas encore été restauré cmp byte [BDA_MOUSE + mouse.bkg_saved], 0 ; le buffer n'a pas encore été restauré
jne .done jne .done
@ -542,6 +829,7 @@ cga_cursor_savebg:
lea si, [BDA_MOUSE + mouse.bkg_buffer] lea si, [BDA_MOUSE + mouse.bkg_buffer]
mov cx,16 mov cx,16
.row_loop: .row_loop:
; ---- ligne bank courant ---- ; ---- ligne bank courant ----
mov eax, [es:di] ; lit b0 b1 b2 b3 mov eax, [es:di] ; lit b0 b1 b2 b3
@ -552,17 +840,17 @@ cga_cursor_savebg:
test di, 0x2000 test di, 0x2000
jnz .next_line jnz .next_line
add di, CGA_STRIDE ; avance de 2 lignes dans bank courant add di, CGA_STRIDE ; avance de 2 lignes dans bank courant
.next_line: .next_line:
loop .row_loop loop .row_loop
.done: .done:
ret ret
; ----------------------------------------------- ; ------------------------------------------------------------
; cga_cursor_restorebg_32 ; cga_cursor_restorebg
; Restaure 16 lignes sauvegardées. ; Restaure 16 lignes sauvegardées.
; Pour ne pas écraser le 4e byte voisin: ; ------------------------------------------------------------
; dst = (dst & 0xFF000000) | (saved & 0x00FFFFFF)
; -----------------------------------------------
cga_cursor_restorebg: cga_cursor_restorebg:
cmp byte [BDA_MOUSE + mouse.bkg_saved], 0 cmp byte [BDA_MOUSE + mouse.bkg_saved], 0
je .done je .done
@ -573,6 +861,7 @@ cga_cursor_restorebg:
mov di, [BDA_MOUSE + mouse.cur_addr_start] mov di, [BDA_MOUSE + mouse.cur_addr_start]
lea si, [BDA_MOUSE + mouse.bkg_buffer] lea si, [BDA_MOUSE + mouse.bkg_buffer]
mov cx, 16 mov cx, 16
.row_loop: .row_loop:
; ---- ligne bank courant ---- ; ---- ligne bank courant ----
mov eax, [ds:si] ; saved (32 bits) mov eax, [ds:si] ; saved (32 bits)
@ -583,6 +872,7 @@ cga_cursor_restorebg:
test di, 0x2000 test di, 0x2000
jnz .next_line jnz .next_line
add di, CGA_STRIDE ; avance de 2 lignes dans bank courant add di, CGA_STRIDE ; avance de 2 lignes dans bank courant
.next_line: .next_line:
loop .row_loop loop .row_loop
@ -618,6 +908,7 @@ cga_cursor_draw:
jbe .y_ok ; y<= 200-16 jbe .y_ok ; y<= 200-16
mov bp, ax mov bp, ax
.y_ok: .y_ok:
; On calcule si les 4 octets vont dépasser la ligne (80 octets) ; On calcule si les 4 octets vont dépasser la ligne (80 octets)
; X est en bits (0-639). X >> 3 donne l'octet de départ (0-79). ; X est en bits (0-639). X >> 3 donne l'octet de départ (0-79).
@ -639,11 +930,13 @@ cga_cursor_draw:
jne .check_78 jne .check_78
mov esi, 0x000000FF mov esi, 0x000000FF
jmp .mask_ready jmp .mask_ready
.check_78: .check_78:
cmp cx, 78 cmp cx, 78
jne .check_79 jne .check_79
mov esi, 0x0000FFFF mov esi, 0x0000FFFF
jmp .mask_ready jmp .mask_ready
.check_79: .check_79:
mov esi, 0x00FFFFFF ; Cas extrême (bord droit) mov esi, 0x00FFFFFF ; Cas extrême (bord droit)
@ -667,6 +960,7 @@ cga_cursor_draw:
test cx, 1 test cx, 1
jz .setup_done jz .setup_done
neg ax ; banque -1 neg ax ; banque -1
.setup_done: .setup_done:
mov cx, ax mov cx, ax
@ -719,6 +1013,7 @@ cga_cursor_draw:
jnz .row_loop jnz .row_loop
pop esi pop esi
.exit_total: .exit_total:
pop es pop es
pop ds pop ds

View file

@ -66,9 +66,6 @@ mouse_reset:
; experiemntal ; experiemntal
mov byte [fs:BDA_MOUSE + mouse.wheel],0 mov byte [fs:BDA_MOUSE + mouse.wheel],0
; debug
mov byte [fs:BDA_MOUSE + mouse.blinker],0
ret ret
; ------------------------------------------------------------ ; ------------------------------------------------------------
@ -368,7 +365,7 @@ isr_mouse_handler:
.done: .done:
; update la position du curseur sur l'écran ; update la position du curseur sur l'écran
; en ce moment c'est commented out (debug) ; en ce moment c'est commented out (debug)
call cga_cursor_move call cga_mouse_cursor_move
.done_eoi: .done_eoi:
mov al, 0x20 mov al, 0x20
out i8259_SLAVE_CMD, al ; EOI PIC esclave out i8259_SLAVE_CMD, al ; EOI PIC esclave