1597 lines
37 KiB
NASM
1597 lines
37 KiB
NASM
; =============================================================================
|
|
; Project : Custom BIOS / ROM
|
|
; File : gfx_cgam.asm
|
|
; Author : frater
|
|
;
|
|
; License : GNU General Public License v3.0 or later (GPL-3.0+)
|
|
;
|
|
; This program is free software: you can redistribute it and/or modify
|
|
; it under the terms of the GNU General Public License as published by
|
|
; the Free Software Foundation, either version 3 of the License, or
|
|
; (at your option) any later version.
|
|
;
|
|
; This program is distributed in the hope that it will be useful,
|
|
; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
; GNU General Public License for more details.
|
|
;
|
|
; You should have received a copy of the GNU General Public License
|
|
; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
;
|
|
; =============================================================================
|
|
|
|
;
|
|
; graphics drivers pour carte video/mode CGA-Mono (640x200x2)
|
|
;
|
|
%define SEG_VIDEO 0xB800 ; ou 0xB000
|
|
|
|
%define GFX_MODE 0x06 ; MCGA HiRes (B/W)
|
|
%define GFX_WIDTH 640
|
|
%define GFX_HEIGHT 200
|
|
|
|
%define CGA_STRIDE 80
|
|
%define CGA_ODD_BANK 0x2000
|
|
;
|
|
; bit : descr
|
|
; 0 : text color : 0=black, 1=white
|
|
; 1 : transparent : 1=apply background attribut
|
|
;
|
|
%define GFX_TXT_WHITE_TRANSPARENT 00000000b
|
|
%define GFX_TXT_BLACK_TRANSPARENT 00000001b
|
|
%define GFX_TXT_WHITE 00000010b
|
|
%define GFX_TXT_BLACK 00000011b
|
|
|
|
%define GFX_TXT_TRANSPARENT_BKG 11111111b
|
|
%define GFX_TXT_TRANSPARENT_TEXT 00000010b
|
|
|
|
|
|
%macro GFX 1-*
|
|
; %1 est l'index de la fonction
|
|
; On itère sur les arguments suivants en sens inverse (Convention C)
|
|
|
|
%rep %0 - 1 ; Répéter pour (Nombre d'args - 1)
|
|
%rotate -1 ; Prend le dernier argument
|
|
push %1 ; L'empile
|
|
%endrep
|
|
|
|
%rotate -1 ; On revient au premier argument (l'index de fonction)
|
|
call word [cs:graph_driver + ((%1)*2)]
|
|
|
|
; Nettoyage de la pile (Convention CDECL: l'appelant nettoie)
|
|
; Chaque argument = 2 octets (1 word).
|
|
add sp, (%0 - 1) * 2
|
|
%endmacro
|
|
|
|
; ------------------------------------------------------------
|
|
; TABLE DE SAUT (VECTEURS API)
|
|
; Cette table doit être située au début du driver pour être
|
|
; accessible par la GUI à des offsets fixes.
|
|
; ------------------------------------------------------------
|
|
%define INIT 0
|
|
%define PUTPIXEL 1
|
|
%define GETPIXEL 2
|
|
%define GOTOXY 3
|
|
%define TXT_MODE 4
|
|
%define PUTCH 5
|
|
%define WRITE 6
|
|
%define LINE 7
|
|
%define RECTANGLE 8
|
|
%define RECTANGLE_FILL 9
|
|
%define RECTANGLE_ROUND 10
|
|
%define MOUSE_HIDE 11
|
|
%define MOUSE_SHOW 12
|
|
%define MOUSE_MOVE 13
|
|
%define MOUSE_DRAW 14
|
|
|
|
; ------------------------------------------------------------
|
|
; COMMENTAIRE SUR LA MÉTHODE D'APPEL
|
|
;
|
|
; GFX FUNCTION, Arg1, Arg2, Arg3
|
|
;
|
|
; GFX GFX_PUTPIXEL, 320, 100, 1
|
|
;
|
|
; Si vous changez le code du driver, tant que la table au début
|
|
; ne change pas d'ordre, la GUI n'a pas besoin d'être recompilée.
|
|
; ------------------------------------------------------------
|
|
align 2
|
|
graph_driver:
|
|
dw cga_init ; init de la carte graphique
|
|
dw cga_putpixel ; dessin d'un pixel
|
|
dw cga_getpixel ; lecture d'un pixel
|
|
dw cga_set_charpos ; gotoxy
|
|
dw cga_set_writemode ; mode texte
|
|
dw cga_putc ; dessin d'un caractère
|
|
dw cga_write ; dessin d'une chaine de caractère
|
|
dw cga_line ; dessin d'une ligne (Bresenham) avec décision horizontale/verticale
|
|
dw cga_draw_rect ; dessin d'un rectangle
|
|
dw cga_fill_rect ; dessin d'un rectangle plein
|
|
dw cga_draw_rounded_frame ; dessin d'un rectangle arrondi
|
|
dw cga_mouse_hide ; déplacement du curseur
|
|
dw cga_mouse_show ; déplacement du curseur
|
|
dw cga_mouse_cursor_move ; déplacement du curseur
|
|
dw cga_cursor_draw
|
|
|
|
; ------------------------------------------------------------
|
|
; initialise le mode graphique (via l'int 10h)
|
|
;
|
|
; ce mode est entrelacé, un bit/pixel, 8 pixels par octet
|
|
; ------------------------------------------------------------
|
|
cga_init:
|
|
mov ax, SEG_BDA_CUSTOM
|
|
mov ds, ax
|
|
|
|
; init graphics mode
|
|
mov ah, 0x00 ; AH=00h set video mode
|
|
mov al, GFX_MODE
|
|
int 0x10
|
|
|
|
; dessine un background "check-board"
|
|
call cga_background
|
|
ret
|
|
|
|
; dummy functions
|
|
cga_none:
|
|
ret
|
|
|
|
; ------------------------------------------------------------
|
|
; Calcule DI + AH=mask pour (CX=x, DX=y) en mode CGA 640x200
|
|
;
|
|
; Out: ES=SEG_VIDEO, DI=offset, AH=bitmask (0x80 >> (x&7))
|
|
; ------------------------------------------------------------
|
|
cga_calc_addr:
|
|
; calcul de l'offset 'y':
|
|
; si y est impaire, DI+=0x2000
|
|
; DI = (y>>1)*80 + (x>>3) + (y&1)*0x2000
|
|
mov ax, dx
|
|
shr ax, 1 ; ax = y/2
|
|
mov di, ax
|
|
shl di, 4 ; (y/2)*16
|
|
shl ax, 6 ; (y/2)*64
|
|
add di, ax ; *80
|
|
|
|
mov ax, cx
|
|
shr ax, 3 ; x / 3
|
|
add di, ax ; di = offset banque paire
|
|
|
|
mov bx, dx
|
|
and bx, 1 ; BX = 0 ou 1
|
|
neg bx ; BX = 0 ou 0xFFFF (-1)
|
|
and bx, 0x2000 ; BX = 0 ou 0x2000 (CGA_ODD_BANK)
|
|
add di, bx ; Application de l'offset
|
|
|
|
; calcul du masque du pixel
|
|
and cl, 7
|
|
mov ah, 0x80
|
|
shr ah, cl
|
|
ret
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; gfx_set_writemode (mode)
|
|
; Défini le mode d'écriture :
|
|
;
|
|
; bit : descr
|
|
; 0 : text color : 0=black, 1=white
|
|
; 1 : transparent : 1=apply background attribut
|
|
;
|
|
; si le mode n'est pas transparent, la couleur de fond
|
|
; est l'inverse de la couleur du texte
|
|
; ---------------------------------------------------------------------------
|
|
; --- Définition des arguments ---
|
|
%define .mode word [bp+4]
|
|
cga_set_writemode:
|
|
push bp
|
|
mov bp, sp
|
|
|
|
push fs
|
|
push ax
|
|
mov ax, SEG_BDA_CUSTOM
|
|
mov fs,ax
|
|
|
|
mov ax, .mode
|
|
|
|
mov byte [fs:PTR_GFX + gfx.cur_mode], al
|
|
|
|
pop ax
|
|
pop fs
|
|
leave
|
|
ret
|
|
%undef .mode
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; gfx_set_charpos (x,y)
|
|
; In : CX = x (pixels), DX = y (pixels)
|
|
; Out: variables DS:GFX_CUR_*
|
|
; Notes:
|
|
; - calcule l'offset VRAM de la scanline y: base = (y&1?2000:0) + (y>>1)*80 + (x>>3)
|
|
; - stocke aussi shift = x&7
|
|
; ---------------------------------------------------------------------------
|
|
cga_set_charpos:
|
|
push bp
|
|
mov bp, sp
|
|
|
|
; --- Définition des arguments ---
|
|
%define .x word [bp+4]
|
|
%define .y word [bp+6]
|
|
|
|
pusha
|
|
push fs
|
|
|
|
mov ax,SEG_BDA_CUSTOM
|
|
mov fs,ax
|
|
|
|
; store x,y en pixel
|
|
mov cx, .x
|
|
mov dx, .y
|
|
|
|
mov [fs:PTR_GFX + gfx.cur_x], cx
|
|
mov [fs:PTR_GFX + gfx.cur_y], dx
|
|
|
|
mov ax, cx
|
|
and ax, 0x07
|
|
mov [fs:PTR_GFX + gfx.cur_shift], al
|
|
|
|
; calcul de l'offset de la position 'x,y':
|
|
; si 'y' est paire, DI < 0x2000 & add = 0x2000
|
|
; si 'y' est impaire, DI> 0x2000 & add = -0x2000
|
|
; DI = (y>>1)*80 + (x>>3) + (y&1)*
|
|
mov ax, dx
|
|
shr ax, 1
|
|
mov bx, ax ; bx = dx>>1
|
|
shl bx, 4 ; bx = bx * 16
|
|
shl ax, 6 ; ax = ax * 64
|
|
add bx, ax
|
|
mov ax, cx
|
|
shr ax, 3 ; ax = x/8
|
|
add bx, ax
|
|
mov ax, CGA_ODD_BANK
|
|
|
|
test dl, 1
|
|
jz .even
|
|
add bx, ax
|
|
neg ax
|
|
add ax, CGA_STRIDE
|
|
.even:
|
|
mov [fs:PTR_GFX + gfx.cur_line_ofs], ax
|
|
mov [fs:PTR_GFX + gfx.cur_offset], bx
|
|
|
|
pop fs
|
|
|
|
; clean defs
|
|
%undef .x
|
|
%undef .y
|
|
popa
|
|
leave
|
|
ret
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; get_glyph_offset
|
|
; In : AL = char (ASCII)
|
|
; Out: CS:SI -> 8 bytes
|
|
; ---------------------------------------------------------------------------
|
|
get_glyph_offset:
|
|
cmp al, 0x20
|
|
jb .qmark ; al < 20h (' '
|
|
cmp al, 0x7E
|
|
ja .qmark ; al > 7Eh ('~')
|
|
sub al, 0x20
|
|
jmp .ok
|
|
|
|
.qmark:
|
|
mov al, '?'
|
|
sub al, 0x20
|
|
|
|
.ok:
|
|
xor ah, ah
|
|
mov si, ax
|
|
shl si, 3
|
|
add si, font8x8
|
|
ret
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; cga_putc_unalign (car)
|
|
; - x non aligné (x&7 != 0)
|
|
; - écrit sur 2 bytes (di et di+1) dans chaque banque
|
|
; ---------------------------------------------------------------------------
|
|
; convert al -> AX aligned with "cl"
|
|
%macro ALIGN_BYTE 0
|
|
mov ah,al
|
|
xor al,al
|
|
shr ax,cl
|
|
xchg ah,al
|
|
%endmacro
|
|
|
|
cga_putc:
|
|
push bp
|
|
mov bp, sp
|
|
sub sp, 2 ; 1 word variable locale
|
|
|
|
; --- Définition des arguments ---
|
|
%define .car word [bp+4]
|
|
|
|
; --- Définition des variables locales ---
|
|
%define .cpt word [bp-2]
|
|
|
|
pusha
|
|
push fs
|
|
push es
|
|
|
|
call cga_mouse_hide ; Protection souris
|
|
|
|
mov bx, SEG_VIDEO
|
|
mov es, bx
|
|
mov bx, SEG_BDA_CUSTOM
|
|
mov fs, bx
|
|
|
|
mov ax, .car
|
|
|
|
call get_glyph_offset
|
|
|
|
; Base offset VRAM pour la scanline Y
|
|
mov di, [fs:PTR_GFX + gfx.cur_offset]
|
|
mov bx, [fs:PTR_GFX + gfx.cur_line_ofs]
|
|
mov ch, [fs:PTR_GFX + gfx.cur_mode]
|
|
mov cl, [fs:PTR_GFX + gfx.cur_shift]
|
|
|
|
mov .cpt, 4
|
|
|
|
.row_loop:
|
|
; gestion du fond de texte
|
|
test ch, 00000010b ; transparent background ?
|
|
jz .skip_attribut ; oui
|
|
|
|
test ch, 00000001b ; background blanc ?
|
|
je .bkg_black
|
|
|
|
; background = white
|
|
mov ax, 0xFF00
|
|
shr ax, cl
|
|
xchg al, ah
|
|
or [es:di], ax
|
|
or [es:di+bx], ax
|
|
jmp .skip_attribut
|
|
|
|
.bkg_black: ; OK
|
|
; background = black
|
|
mov ax, 0xFF00
|
|
shr ax, cl
|
|
xchg al, ah
|
|
not ax
|
|
and [es:di], ax
|
|
and [es:di+bx], ax
|
|
|
|
.skip_attribut:
|
|
; ligne "paire" du gylphe
|
|
xor ax, ax
|
|
mov al, [cs:si]
|
|
inc si
|
|
ALIGN_BYTE
|
|
mov dx,ax
|
|
|
|
; ligne "impaire" du gylphe
|
|
mov al, [cs:si]
|
|
inc si
|
|
ALIGN_BYTE
|
|
xchg ax, dx
|
|
|
|
test ch,00000001b
|
|
jz .black_text
|
|
|
|
; white text
|
|
not ax
|
|
not dx
|
|
and [es:di], ax
|
|
and [es:di+bx], dx
|
|
|
|
jmp .next
|
|
|
|
.black_text:
|
|
or [es:di], ax
|
|
or [es:di+bx], dx
|
|
|
|
.next:
|
|
add di,CGA_STRIDE
|
|
|
|
dec .cpt
|
|
jnz .row_loop
|
|
|
|
; Avancer curseur d'un caractère (8 pixels)
|
|
inc word [fs:PTR_GFX + gfx.cur_offset]
|
|
add word [fs:PTR_GFX + gfx.cur_x], 8
|
|
|
|
call cga_mouse_show ; Restauration souris
|
|
|
|
pop es
|
|
pop fs
|
|
popa
|
|
leave
|
|
ret
|
|
|
|
;
|
|
; write string from [DS:SI] to screen
|
|
;
|
|
; --- Définition des arguments ---
|
|
%define .txt_seg word [bp+4]
|
|
%define .txt_ofs word [bp+6]
|
|
cga_write:
|
|
push bp
|
|
mov bp, sp
|
|
push ax
|
|
push ds
|
|
|
|
mov ax, .txt_seg
|
|
mov ds, ax
|
|
mov si, .txt_ofs
|
|
cld
|
|
|
|
.loops:
|
|
lodsb
|
|
cmp al,0
|
|
je .done
|
|
push ax
|
|
call cga_putc
|
|
jmp .loops
|
|
|
|
.done:
|
|
pop ds
|
|
pop ax
|
|
leave
|
|
ret
|
|
%undef .txt_seg
|
|
%undef .txt_ofs
|
|
|
|
; ------------------------------------------------------------
|
|
; cga_putpixel (x,y,color)
|
|
; Dessine un pixel, accès VRAM direct.
|
|
;
|
|
; CX = x (0..639)
|
|
; DX = y (0..199)
|
|
; ES = Target Segment (usually SEG_VIDEO)
|
|
; BL = color (0=black, !=0=white)
|
|
; ------------------------------------------------------------
|
|
; --- Définition des arguments ---
|
|
%define .x word [bp+4]
|
|
%define .y word [bp+6]
|
|
%define .color byte [bp+8]
|
|
cga_putpixel:
|
|
push bp
|
|
mov bp, sp
|
|
pusha
|
|
|
|
mov ax, SEG_VIDEO
|
|
mov es, ax
|
|
|
|
call cga_mouse_hide ; Protection souris
|
|
mov cx, .x
|
|
mov dx, .y
|
|
call cga_calc_addr
|
|
|
|
; write
|
|
cmp .color, 0
|
|
je .clear
|
|
|
|
.set:
|
|
or byte [es:di], ah
|
|
jmp .done
|
|
|
|
.clear:
|
|
not ah
|
|
and byte [es:di], ah
|
|
|
|
.done:
|
|
call cga_mouse_show ; Restauration souris
|
|
popa
|
|
leave
|
|
ret
|
|
; clean defs
|
|
%undef .x
|
|
%undef .y
|
|
%undef .color
|
|
|
|
; ------------------------------------------------------------
|
|
; cga_getpixel (x,y)
|
|
; Lit un pixel, accès VRAM direct.
|
|
;
|
|
; Out: AL=0/1
|
|
; ------------------------------------------------------------
|
|
cga_getpixel:
|
|
push bp
|
|
mov bp, sp
|
|
|
|
; --- Définition des arguments ---
|
|
%define .x word [bp+4]
|
|
%define .y word [bp+6]
|
|
|
|
push di
|
|
push es
|
|
|
|
mov cx, .x
|
|
mov dx, .y
|
|
|
|
mov ax, SEG_VIDEO
|
|
mov es, ax
|
|
call cga_calc_addr
|
|
|
|
mov al, [es:di]
|
|
and al, ah
|
|
setnz al
|
|
|
|
pop es
|
|
pop di
|
|
|
|
; clean defs
|
|
%undef .x
|
|
%undef .y
|
|
leave
|
|
ret
|
|
|
|
; ------------------------------------------------------------
|
|
; Dessine un background "check-board", accès VRAM direct.
|
|
;
|
|
; ------------------------------------------------------------
|
|
cga_background:
|
|
mov ax,SEG_VIDEO
|
|
mov es,ax
|
|
mov di,0x0000
|
|
mov eax,0xaaaaaaaa
|
|
cld
|
|
mov cx,0x800
|
|
rep stosd
|
|
mov di,0x2000
|
|
mov eax,0x55555555
|
|
mov cx,0x800 ; 640/8/4
|
|
rep stosd
|
|
ret
|
|
|
|
; ------------------------------------------------------------
|
|
; cga_line_vertical (x, y0, y1, color)
|
|
;
|
|
; Dessine une ligne x, y0, y1, color
|
|
; ------------------------------------------------------------
|
|
%define .x word [bp+4]
|
|
%define .y1 word [bp+6]
|
|
%define .y2 word [bp+8]
|
|
%define .color byte [bp+10]
|
|
cga_line_vertical:
|
|
push bp
|
|
mov bp, sp
|
|
pusha
|
|
push es
|
|
|
|
; Setup ES = Video Segment
|
|
mov ax, SEG_VIDEO ; Utiliser AX plutot que SI pour setup ES
|
|
mov es, ax
|
|
|
|
call cga_mouse_hide
|
|
|
|
mov bx, .y1
|
|
mov dx, .y2
|
|
|
|
; 1. Ordonner Y (BX = Debut, DX = Fin)
|
|
cmp bx, dx
|
|
jle .y_sorted
|
|
xchg bx, dx
|
|
.y_sorted:
|
|
|
|
; 2. Calculer la hauteur MAINTENANT (avant que DX/BX ne soient modifiés)
|
|
sub dx, bx ; DX = Hauteur - 1
|
|
inc dx ; DX = Hauteur en pixels
|
|
push dx ; Sauvegarde de la hauteur (compteur) sur la pile [1]
|
|
|
|
; 3. Calculer l'adresse de départ
|
|
mov cx, .x ; CX = X
|
|
mov dx, bx ; DX = Y_Start (Argument pour cga_calc_addr)
|
|
call cga_calc_addr ; DI = Offset, AH = Masque Bit
|
|
|
|
; 4. Préparer les registres pour la boucle
|
|
mov al, ah ; AL = Masque
|
|
not al ; AL = ~Masque (pour effacer/noir)
|
|
|
|
; Charger la couleur (CORRECTION BUG COULEUR)
|
|
mov cl, .color ; On charge la couleur dans CL maintenant
|
|
|
|
; Récupérer le compteur de hauteur
|
|
pop dx ; DX = Hauteur restaurée [1]
|
|
|
|
.v_loop:
|
|
cmp cl, 0 ; Test de la couleur (CL)
|
|
jz .draw_black
|
|
|
|
.draw_white:
|
|
or byte [es:di], ah ; Allumer le bit (AH)
|
|
jmp .next_line
|
|
|
|
.draw_black:
|
|
and byte [es:di], al ; Eteindre le bit (AL)
|
|
|
|
.next_line:
|
|
; --- Logique Next Line CGA ---
|
|
xor di, 0x2000 ; Bascule banque 0 <-> 1
|
|
test di, 0x2000 ; Si on est passé à la banque 1 (0x2000)
|
|
jnz .skip_add ; On a visuellement descendu d'une ligne, c'est bon.
|
|
add di, 80 ; Si on revient banque 0, il faut avancer de 80 octets.
|
|
.skip_add:
|
|
|
|
dec dx ; Décrémenter le compteur de hauteur
|
|
jnz .v_loop
|
|
|
|
call cga_mouse_show
|
|
|
|
pop es
|
|
popa
|
|
leave ; Leave gère le 'mov sp, bp / pop bp'
|
|
ret
|
|
; clean defs
|
|
%undef .x
|
|
%undef .y1
|
|
%undef .y2
|
|
%undef .color
|
|
; ------------------------------------------------------------
|
|
; cga_line_horizontal
|
|
; Dessine une ligne horizontale de (x1, y) à (x2, y)
|
|
; ------------------------------------------------------------
|
|
; --- Définition des arguments ---
|
|
%define .x1 word [bp+4]
|
|
%define .x2 word [bp+6]
|
|
%define .y word [bp+8]
|
|
%define .color byte [bp+10]
|
|
cga_line_horizontal:
|
|
push bp
|
|
mov bp, sp
|
|
pusha
|
|
push es
|
|
|
|
; Setup ES = Video Segment
|
|
mov ax, SEG_VIDEO
|
|
mov es, ax
|
|
|
|
call cga_mouse_hide
|
|
|
|
mov ax, .x1
|
|
mov bx, .x2
|
|
|
|
; 1. Ordonner X (AX = Gauche, BX = Droite)
|
|
cmp ax, bx
|
|
jle .x_sorted
|
|
xchg ax, bx
|
|
.x_sorted:
|
|
|
|
; 2. Calculer l'adresse de départ
|
|
mov cx, ax ; CX = X_Start pour cga_calc_addr
|
|
mov dx, .y ; DX = Y
|
|
|
|
push bx ; Sauve X_End
|
|
push ax ; <--- AJOUT CRITIQUE : Sauve X_Start (AX)
|
|
call cga_calc_addr ; DI = Offset VRAM (AX est détruit ici !)
|
|
pop ax ; <--- RESTAURATION : On récupère le bon X1
|
|
pop bx ; Restaure X_End
|
|
|
|
; 3. Préparer la couleur
|
|
mov dl, .color ; DL = Couleur
|
|
|
|
; --- ANALYSE DES OCTETS ---
|
|
mov si, ax
|
|
shr si, 3 ; SI = Index octet start (Calcul correct maintenant)
|
|
|
|
mov cx, bx
|
|
shr cx, 3 ; CX = Index octet end
|
|
|
|
; Cas spécial : Tout dans le même octet ?
|
|
cmp si, cx
|
|
je .single_byte
|
|
|
|
; ============================================
|
|
; CAS MULTI-BYTE (Gauche -> Milieu -> Droite)
|
|
; ============================================
|
|
|
|
; --- A. PARTIE GAUCHE (Start) ---
|
|
push cx ; Sauve Index End
|
|
mov cx, ax
|
|
and cx, 7 ; CL = X1 % 8
|
|
mov dh, 0xFF
|
|
shr dh, cl ; DH = Masque Gauche
|
|
|
|
call .apply_mask ; Applique DH sur [ES:DI]
|
|
|
|
inc di ; Octet suivant
|
|
inc si ; Index suivant
|
|
pop cx ; Restaure Index End
|
|
|
|
; --- B. PARTIE CENTRALE (Full Bytes) ---
|
|
cmp si, cx
|
|
jge .do_right_part
|
|
|
|
mov al, 0x00 ; Noir
|
|
cmp dl, 0
|
|
jz .fill_loop
|
|
mov al, 0xFF ; Blanc
|
|
|
|
.fill_loop:
|
|
mov byte [es:di], al
|
|
inc di
|
|
inc si
|
|
cmp si, cx
|
|
jl .fill_loop
|
|
|
|
.do_right_part:
|
|
; --- C. PARTIE DROITE (End) ---
|
|
mov cx, bx
|
|
and cx, 7
|
|
inc cx
|
|
mov dh, 0xFF
|
|
shr dh, cl
|
|
not dh ; DH = Masque Droite
|
|
|
|
call .apply_mask
|
|
jmp .done
|
|
|
|
; ============================================
|
|
; CAS SINGLE BYTE
|
|
; ============================================
|
|
.single_byte:
|
|
; Masque Gauche
|
|
mov cx, ax
|
|
and cx, 7
|
|
mov dh, 0xFF
|
|
shr dh, cl
|
|
|
|
; Masque Droite
|
|
mov cx, bx
|
|
and cx, 7
|
|
inc cx
|
|
mov ah, 0xFF
|
|
shr ah, cl
|
|
not ah
|
|
|
|
; Intersection
|
|
and dh, ah
|
|
call .apply_mask
|
|
jmp .done
|
|
|
|
; --- Helper local ---
|
|
.apply_mask:
|
|
cmp dl, 0
|
|
jz .apply_black
|
|
or byte [es:di], dh
|
|
ret
|
|
.apply_black:
|
|
not dh
|
|
and byte [es:di], dh
|
|
not dh
|
|
ret
|
|
|
|
.done:
|
|
call cga_mouse_show
|
|
pop es
|
|
popa
|
|
leave
|
|
ret
|
|
; clean defs
|
|
%undef .x1
|
|
%undef .x2
|
|
%undef .y
|
|
|
|
; ------------------------------------------------------------
|
|
; cga_line (x1, y1, x2, y2, color)
|
|
; Algorithme de Bresenham pour tracer une ligne arbitraire
|
|
; ------------------------------------------------------------
|
|
%define .x1 word [bp+4]
|
|
%define .y1 word [bp+6]
|
|
%define .x2 word [bp+8]
|
|
%define .y2 word [bp+10]
|
|
%define .color byte [bp+12]
|
|
cga_line:
|
|
push bp
|
|
mov bp, sp
|
|
pusha ; Sauvegarde de tous les registres (AX, BX, CX, DX, SI, DI)
|
|
push es ; Sauvegarde ES
|
|
|
|
; --- Optimisation : Lignes H/V ---
|
|
mov ax, .y1
|
|
cmp ax, .y2
|
|
je .do_horiz
|
|
mov ax, .x1
|
|
cmp ax, .x2
|
|
je .do_vert
|
|
|
|
; --- Bresenham ---
|
|
mov ax, SEG_VIDEO
|
|
mov es, ax
|
|
|
|
call cga_mouse_hide
|
|
|
|
sub sp, 10 ; Variables locales: dx, dy, sx, sy, err
|
|
|
|
; --- Definitions locales ---
|
|
; BP pointe sur l'ancien BP.
|
|
; PUSHA (16 octets) + PUSH ES (2 octets) = 18 octets
|
|
; Les variables locales commencent donc à BP-18
|
|
%define .dx word [bp-20]
|
|
%define .dy word [bp-22]
|
|
%define .sx word [bp-24]
|
|
%define .sy word [bp-26]
|
|
%define .err word [bp-28]
|
|
|
|
; --- Initialisation Bresenham ---
|
|
; dx = abs(x2 - x1), sx = sign(x2 - x1)
|
|
mov ax, .x2
|
|
sub ax, .x1
|
|
mov bx, 1 ; sx = 1
|
|
jge .calc_dx
|
|
neg ax
|
|
neg bx ; sx = -1
|
|
|
|
.calc_dx:
|
|
mov .dx, ax ; save dx
|
|
mov .sx, bx ; save sx
|
|
|
|
; dy = abs(y2 - y1), sy = sign(y2 - y1)
|
|
mov ax, .y2
|
|
sub ax, .y1
|
|
mov bx, 1 ; sy = 1
|
|
jge .calc_dy
|
|
neg ax
|
|
neg bx ; sy = -1
|
|
|
|
.calc_dy:
|
|
mov .dy, ax ; save dy
|
|
mov .sy, bx ; save sy
|
|
|
|
; err = dx - dy
|
|
mov ax, .dx ; dx
|
|
sub ax, .dy ; dy
|
|
mov .err, ax ; err
|
|
|
|
; Coordonnées courantes
|
|
mov cx, .x1
|
|
mov dx, .y1
|
|
|
|
.loop:
|
|
; Plot pixel (CX, DX)
|
|
push cx
|
|
push dx
|
|
call cga_calc_addr ; Out: DI=offset, AH=mask
|
|
|
|
mov bl, .color
|
|
cmp bl, 0
|
|
je .plot_black
|
|
or byte [es:di], ah
|
|
jmp .plot_next
|
|
|
|
.plot_black:
|
|
not ah
|
|
and byte [es:di], ah
|
|
|
|
.plot_next:
|
|
pop dx
|
|
pop cx
|
|
|
|
; Check fin
|
|
cmp cx, .x2
|
|
jne .step
|
|
cmp dx, .y2
|
|
je .done
|
|
|
|
.step:
|
|
mov ax, .err ; e2 = err
|
|
shl ax, 1 ; e2 = 2*err
|
|
|
|
mov bx, .dy ; dy
|
|
neg bx ; -dy
|
|
cmp ax, bx
|
|
jle .check_y
|
|
|
|
add .err, bx ; err += -dy
|
|
add cx, .sx ; x += sx
|
|
|
|
.check_y:
|
|
mov bx, .dx ; dx
|
|
cmp ax, bx
|
|
jge .loop ; if e2 >= dx, skip y step
|
|
|
|
add .err, bx ; err += dx
|
|
add dx, .sy ; y += sy
|
|
jmp .loop
|
|
|
|
.done:
|
|
call cga_mouse_show
|
|
add sp, 10 ; Libération variables locales
|
|
jmp .exit
|
|
|
|
.do_horiz:
|
|
push word [bp+12] ; color
|
|
push word [bp+6] ; y
|
|
push word [bp+8] ; x2
|
|
push word [bp+4] ; x1
|
|
call cga_line_horizontal
|
|
add sp, 8
|
|
jmp .exit
|
|
|
|
.do_vert:
|
|
push word [bp+12] ; color
|
|
push word [bp+10] ; y2
|
|
push word [bp+6] ; y1
|
|
push word [bp+4] ; x
|
|
call cga_line_vertical
|
|
add sp, 8
|
|
|
|
.exit:
|
|
pop es
|
|
popa
|
|
pop bp
|
|
ret
|
|
|
|
%undef .x1
|
|
%undef .y1
|
|
%undef .x2
|
|
%undef .y2
|
|
%undef .color
|
|
%undef .dx
|
|
%undef .dy
|
|
%undef .sx
|
|
%undef .sy
|
|
%undef .err
|
|
|
|
; ------------------------------------------------------------
|
|
; cga_draw_rect
|
|
; Dessine un rectangle vide (contour)
|
|
; Entrée : x1, y1, x2, y2, color
|
|
; ------------------------------------------------------------
|
|
; Arguments
|
|
%define .x1 word [bp+4]
|
|
%define .y1 word [bp+6]
|
|
%define .x2 word [bp+8]
|
|
%define .y2 word [bp+10]
|
|
%define .color word [bp+12] ; word pour l'alignement pile (mais on utilise byte)
|
|
cga_draw_rect:
|
|
push bp
|
|
mov bp, sp
|
|
pusha
|
|
|
|
; 1. Ordonner X (x1 < x2)
|
|
mov ax, .x1
|
|
mov bx, .x2
|
|
cmp ax, bx
|
|
jle .x_ok
|
|
xchg ax, bx
|
|
mov .x1, ax
|
|
mov .x2, bx
|
|
.x_ok:
|
|
|
|
; 2. Ordonner Y (y1 < y2)
|
|
mov ax, .y1
|
|
mov bx, .y2
|
|
cmp ax, bx
|
|
jle .y_ok
|
|
xchg ax, bx
|
|
mov .y1, ax
|
|
mov .y2, bx
|
|
.y_ok:
|
|
|
|
call cga_mouse_hide
|
|
|
|
; 3. Dessiner les 4 lignes
|
|
; Pour éviter les pixels en double dans les coins, on peut ajuster légèrement,
|
|
; mais pour un driver simple, tracer les 4 lignes brutes est acceptable.
|
|
|
|
GFX LINE, .x1, .y1, .x2, .y1, .color
|
|
GFX LINE, .x1, .y2, .x2, .y2, .color
|
|
|
|
GFX LINE, .x1, .y1, .x1, .y2, .color
|
|
GFX LINE, .x2, .y1, .x2, .y2, .color
|
|
|
|
call cga_mouse_show
|
|
popa
|
|
leave
|
|
ret
|
|
; clean defs
|
|
%undef .x1
|
|
%undef .y1
|
|
%undef .x2
|
|
%undef .y2
|
|
%undef .color
|
|
|
|
; ------------------------------------------------------------
|
|
; cga_fill_rect
|
|
; Dessine un rectangle plein
|
|
; Entrée : x1, y1, x2, y2, pattern_offset (CS:Offset)
|
|
%define .x1 word [bp+4]
|
|
%define .y1 word [bp+6]
|
|
%define .x2 word [bp+8]
|
|
%define .y2 word [bp+10]
|
|
%define .pat_id word [bp+12]
|
|
; ------------------------------------------------------------
|
|
cga_fill_rect:
|
|
push bp
|
|
mov bp, sp
|
|
sub sp, 6 ; Reserve espace pour .pat_idx et .x_end_idx
|
|
; Variable locale pour l'index du motif (y % 8)
|
|
%define .pat_idx word [bp-2]
|
|
%define .x_end_idx word [bp-4]
|
|
%define .pattern_offset word [bp-6]
|
|
|
|
pusha
|
|
push es
|
|
|
|
mov ax, SEG_VIDEO
|
|
mov es, ax
|
|
|
|
; calcul de l'offset de départ du pattern
|
|
mov ax, .pat_id
|
|
shl ax, 3 ; * 8
|
|
add ax, pattern_8x8
|
|
mov .pattern_offset, ax
|
|
|
|
call cga_mouse_hide
|
|
|
|
; --- Tri des coordonnées (X et Y) ---
|
|
; Tri X
|
|
mov ax, .x1
|
|
mov bx, .x2
|
|
cmp ax, bx
|
|
jle .x_sorted
|
|
xchg ax, bx
|
|
mov .x1, ax
|
|
mov .x2, bx
|
|
.x_sorted:
|
|
|
|
; Tri Y
|
|
mov ax, .y1
|
|
mov bx, .y2
|
|
cmp ax, bx
|
|
jle .y_sorted
|
|
xchg ax, bx
|
|
mov .y1, ax
|
|
mov .y2, bx
|
|
.y_sorted:
|
|
|
|
; --- Init Pattern Index ---
|
|
mov ax, .y1
|
|
and ax, 7
|
|
mov .pat_idx, ax
|
|
|
|
; --- 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)
|
|
|
|
; --- Calcul adresse de départ ---
|
|
mov cx, .x1
|
|
mov dx, .y1
|
|
|
|
|
|
; 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
|
|
|
|
; --- Préparer les masques ---
|
|
; Masque Gauche (DH)
|
|
mov cx, .x1
|
|
and cx, 7
|
|
mov dh, 0xFF
|
|
shr dh, cl
|
|
|
|
; Masque Droite (AH)
|
|
mov cx, .x2
|
|
and cx, 7
|
|
inc cx
|
|
mov ah, 0xFF
|
|
shr ah, cl
|
|
not ah
|
|
|
|
; Index octets
|
|
mov si, .x1
|
|
shr si, 3 ; SI = Index start byte
|
|
mov cx, .x2
|
|
shr cx, 3 ; CX = Index end byte
|
|
mov .x_end_idx, cx ; Sauvegarde de l'index de fin pour la boucle horizontale
|
|
|
|
; --- 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.
|
|
; --- BOUCLE VERTICALE ---
|
|
.row_loop:
|
|
push di ; Sauve début ligne
|
|
push si ; Sauve index byte
|
|
|
|
; --- Récupérer le byte du motif pour cette ligne ---
|
|
push bx
|
|
mov bx, .pat_idx
|
|
push si
|
|
mov si, .pattern_offset
|
|
mov dl, [cs:si + bx] ; DL = Pattern Byte
|
|
pop si
|
|
pop bx
|
|
|
|
; --- Remplissage Horizontal ---
|
|
mov cx, .x_end_idx ; Restaurer l'index de fin pour la ligne
|
|
|
|
; Cas Single Byte (Start == End)
|
|
cmp si, cx
|
|
je .single_byte_row
|
|
|
|
; Partie Gauche
|
|
mov al, dh
|
|
call .apply_mask_fill
|
|
inc di
|
|
inc si
|
|
|
|
; Partie Centrale (Boucle tant que SI < CX)
|
|
jmp .check_mid
|
|
.mid_loop:
|
|
mov byte [es:di], dl
|
|
inc di
|
|
inc si
|
|
.check_mid:
|
|
cmp si, cx
|
|
jl .mid_loop
|
|
|
|
; Partie Droite
|
|
mov al, ah
|
|
call .apply_mask_fill
|
|
jmp .row_done
|
|
|
|
.single_byte_row:
|
|
mov al, dh
|
|
and al, ah ; Intersection des masques
|
|
call .apply_mask_fill
|
|
|
|
.row_done:
|
|
pop si
|
|
pop di
|
|
|
|
; Incrémenter index motif pour la prochaine ligne
|
|
inc word .pat_idx
|
|
and word .pat_idx, 7
|
|
|
|
; --- Passage ligne suivante (CGA Bank Switch) ---
|
|
xor di, 0x2000
|
|
test di, 0x2000
|
|
jnz .bank_switch_ok
|
|
add di, 80
|
|
.bank_switch_ok:
|
|
|
|
dec bx ; On décrémente BX (Hauteur) au lieu de BP
|
|
jnz .row_loop
|
|
|
|
call cga_mouse_show
|
|
pop es
|
|
|
|
popa
|
|
leave
|
|
ret
|
|
|
|
; helper
|
|
.apply_mask_fill:
|
|
push ax
|
|
not al
|
|
and byte [es:di], al
|
|
pop ax
|
|
and al, dl ; Motif masqué
|
|
or byte [es:di], al ; Applique
|
|
ret
|
|
; Nettoyage des defines
|
|
%undef .x1
|
|
%undef .y1
|
|
%undef .x2
|
|
%undef .y2
|
|
%undef .pat_idx
|
|
|
|
; ------------------------------------------------------------
|
|
; cga_draw_rounded_frame
|
|
; dessiner un cadre arrondi
|
|
; Entrée : AX=x1, BX=y1, CX=x2, DX=y2
|
|
; ------------------------------------------------------------
|
|
%define .x1 word [bp+4]
|
|
%define .y1 word [bp+6]
|
|
%define .x2 word [bp+8]
|
|
%define .y2 word [bp+10]
|
|
%define .color word [bp+12]
|
|
; Variable locale pour l'index du motif (y % 8)
|
|
%define .coord1 word [bp-2]
|
|
%define .coord2 word [bp-4]
|
|
cga_draw_rounded_frame:
|
|
push bp
|
|
mov bp, sp
|
|
sub sp, 4 ; Reserve espace pour .coord1 et .coord2
|
|
pusha
|
|
|
|
call cga_mouse_hide
|
|
|
|
; Lignes horizontales (raccourcies de 2px pour laisser place à l'arrondi)
|
|
mov ax, .x1
|
|
add ax, 2
|
|
mov .coord1, ax
|
|
mov bx, .x2
|
|
sub bx, 2
|
|
mov .coord2, bx
|
|
GFX LINE, .coord1, .y1, .coord2, .y1, .color
|
|
GFX LINE, .coord1, .y2, .coord2, .y2, .color
|
|
|
|
; Lignes verticales (raccourcies de 1px en haut et en bas)
|
|
mov cx, .y1
|
|
add cx, 2
|
|
mov .coord1, cx
|
|
mov dx, .y2
|
|
sub dx, 2
|
|
mov .coord2, dx
|
|
GFX LINE, .x1, .coord1, .x1, .coord2, .color
|
|
GFX LINE, .x2, .coord1, .x2, .coord2, .color
|
|
|
|
; Ajout des pixels de transition pour adoucir les coins (chanfrein)
|
|
mov ax, .x1
|
|
inc ax ; x1 + 1
|
|
mov bx, .y1
|
|
inc bx ; y1 + 1
|
|
GFX PUTPIXEL, ax, bx, .color ; Coin haut-gauche
|
|
|
|
mov ax, .x1
|
|
inc ax
|
|
mov bx, .y2
|
|
dec bx ; y2 - 1
|
|
GFX PUTPIXEL, ax, bx, .color ; Coin bas-gauche
|
|
|
|
mov ax, .x2
|
|
dec ax ; x2 - 1
|
|
mov bx, .y1
|
|
inc bx ; y1 + 1
|
|
GFX PUTPIXEL, ax, bx, .color ; Coin haut-droite
|
|
|
|
mov ax, .x2
|
|
dec ax
|
|
mov bx, .y2
|
|
dec bx ; y2 - 1
|
|
GFX PUTPIXEL, ax, bx, .color ; Coin bas-droite
|
|
|
|
call cga_mouse_show
|
|
|
|
popa
|
|
leave
|
|
ret
|
|
%undef .x1
|
|
%undef .y1
|
|
%undef .x2
|
|
%undef .y2
|
|
%undef .color
|
|
|
|
; -----------------------------------------------------------------------------
|
|
|
|
; ------------------------------------------------------------
|
|
; 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)
|
|
pusha ; Sauvegarder TOUS les registres 32-bits
|
|
push ds
|
|
|
|
mov ax, SEG_BDA_CUSTOM
|
|
mov ds, ax
|
|
|
|
; Décrémenter le compteur
|
|
dec byte [PTR_MOUSE + mouse.cur_counter]
|
|
|
|
; Vérifier si on vient juste de passer en mode caché (c-à-d on est à -1)
|
|
cmp byte [PTR_MOUSE + mouse.cur_counter], -1
|
|
jne .skip_restore ; Si on est à -2, -3... elle est déjà cachée
|
|
|
|
call cga_cursor_restorebg
|
|
|
|
.skip_restore:
|
|
pop ds
|
|
popa
|
|
popf ; Restaure les interruptions (STI si elles étaient là)
|
|
ret
|
|
|
|
; ------------------------------------------------------------
|
|
; gère la demande d'affichage du curseur souris
|
|
;
|
|
; ------------------------------------------------------------
|
|
cga_mouse_show:
|
|
pushf
|
|
cli
|
|
pusha
|
|
push ds
|
|
|
|
mov ax, SEG_BDA_CUSTOM
|
|
mov ds, ax
|
|
|
|
; Incrémenter le compteur
|
|
inc byte [PTR_MOUSE + mouse.cur_counter]
|
|
|
|
; Vérifier si on est revenu à 0 (Visible)
|
|
cmp byte [PTR_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
|
|
popa
|
|
popf ; restore également le flag d'interruption
|
|
ret
|
|
|
|
|
|
; ------------------------------------------------------------
|
|
; gère le déplacement du curseur souris
|
|
;
|
|
; ------------------------------------------------------------
|
|
cga_mouse_cursor_move:
|
|
pushad
|
|
push ds
|
|
push es
|
|
|
|
; move Data Segment
|
|
mov ax, SEG_BDA_CUSTOM
|
|
mov ds, ax
|
|
|
|
cmp byte [PTR_MOUSE + mouse.cur_counter], 0
|
|
jl .done ; Si < 0, on ne dessine rien !
|
|
|
|
cmp byte [PTR_MOUSE + mouse.cur_drawing],0
|
|
jne .done
|
|
|
|
mov byte [PTR_MOUSE + mouse.cur_drawing],1
|
|
|
|
mov ax, SEG_VIDEO
|
|
mov es, ax
|
|
|
|
call cga_cursor_restorebg
|
|
|
|
call cga_cursor_savebg
|
|
|
|
call cga_cursor_draw
|
|
mov byte [PTR_MOUSE + mouse.cur_drawing],0
|
|
.done:
|
|
pop es
|
|
pop ds
|
|
popad
|
|
ret
|
|
|
|
; ------------------------------------------------------------
|
|
; cga_cursor_savebg
|
|
; Sauve 16 lignes (4 bytes/ligne) sous le curseur.
|
|
; Stocke 16 DWORDs: chaque DWORD contient les 4 bytes de la ligne
|
|
; ------------------------------------------------------------
|
|
cga_cursor_savebg:
|
|
cmp byte [PTR_MOUSE + mouse.bkg_saved], 0 ; le buffer n'a pas encore été restauré
|
|
jne .done
|
|
|
|
push es
|
|
mov ax, SEG_VIDEO
|
|
mov es, ax
|
|
|
|
; mémoriser position courante (utilisée pour restore)
|
|
mov cx, [PTR_MOUSE + mouse.x]
|
|
mov dx, [PTR_MOUSE + mouse.y]
|
|
mov [PTR_MOUSE + mouse.cur_x], cx
|
|
mov [PTR_MOUSE + mouse.cur_y], dx
|
|
|
|
; calcule ES:DI pour (x,y)
|
|
call cga_calc_addr
|
|
|
|
mov [PTR_MOUSE + mouse.cur_addr_start], di
|
|
lea si, [PTR_MOUSE + mouse.bkg_buffer]
|
|
|
|
mov cx,16
|
|
|
|
.row_loop:
|
|
; ---- ligne bank courant ----
|
|
mov eax, [es:di] ; lit b0 b1 b2 b3
|
|
mov [ds:si], eax ; stocke 4 bytes (le byte haut sera 0)
|
|
add si, 4
|
|
|
|
xor di, 0x2000
|
|
test di, 0x2000
|
|
jnz .next_line
|
|
add di, CGA_STRIDE ; avance de 2 lignes dans bank courant
|
|
|
|
.next_line:
|
|
loop .row_loop
|
|
|
|
pop es
|
|
|
|
.done:
|
|
mov byte [PTR_MOUSE + mouse.bkg_saved], 1 ; Flag mis à jour après la copie
|
|
ret
|
|
|
|
; ------------------------------------------------------------
|
|
; cga_cursor_restorebg
|
|
; Restaure 16 lignes sauvegardées.
|
|
; ------------------------------------------------------------
|
|
cga_cursor_restorebg:
|
|
cmp byte [PTR_MOUSE + mouse.bkg_saved], 0
|
|
je .done
|
|
|
|
push es
|
|
mov ax, SEG_VIDEO
|
|
mov es, ax
|
|
|
|
; restaurer depuis la dernière position sauvegardée
|
|
mov di, [PTR_MOUSE + mouse.cur_addr_start]
|
|
lea si, [PTR_MOUSE + mouse.bkg_buffer]
|
|
mov cx, 16
|
|
|
|
.row_loop:
|
|
; ---- ligne bank courant ----
|
|
mov eax, [ds:si] ; saved (32 bits)
|
|
mov [es:di], eax ; restore line
|
|
add si, 4
|
|
|
|
xor di, 0x2000
|
|
test di, 0x2000
|
|
jnz .next_line
|
|
add di, CGA_STRIDE ; avance de 2 lignes dans bank courant
|
|
|
|
.next_line:
|
|
loop .row_loop
|
|
|
|
pop es
|
|
.done:
|
|
mov byte [PTR_MOUSE + mouse.bkg_saved], 0 ; Flag mis à jour après la restauration
|
|
ret
|
|
|
|
; ============================================================
|
|
; cga_cursor_draw
|
|
; Détruit: registres sauvegardés/restaurés (PUSHA/POPA)
|
|
; ============================================================
|
|
cga_cursor_draw:
|
|
push bp
|
|
mov bp, sp
|
|
|
|
; variables
|
|
%define .height word [bp-2]
|
|
%define .mask dword [bp-6]
|
|
sub sp, 6 ; Reserve space for locals
|
|
|
|
pushad
|
|
push ds
|
|
push es
|
|
push gs
|
|
|
|
cld ; Sécurité : Direction avant
|
|
mov ax, SEG_BDA_CUSTOM
|
|
mov ds, ax
|
|
mov ax, SEG_VIDEO
|
|
mov es, ax
|
|
|
|
; --- CLIPPING VERTICAL ---
|
|
mov dx, [ds:PTR_MOUSE + mouse.y]
|
|
mov bx, GFX_HEIGHT
|
|
cmp dx, bx
|
|
jae .exit_total
|
|
|
|
mov ax, 16
|
|
sub bx, dx ; 200-y
|
|
cmp ax, bx
|
|
jbe .y_ok ; y<= 200-16
|
|
|
|
mov ax, bx
|
|
.y_ok:
|
|
mov .height, ax
|
|
|
|
; 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).
|
|
; Si StartByte >= 77, on déborde.
|
|
|
|
mov cx, [PTR_MOUSE + mouse.x]
|
|
shr cx, 3 ; CX = Byte Offset (0-79)
|
|
xor esi, esi ; ESI = Masque de protection (0 = tout dessiner)
|
|
|
|
cmp cx, 76 ; 76 est le dernier offset sûr (76,77,78,79)
|
|
jbe .mask_ready ; Si <= 76, pas de débordement
|
|
|
|
; Calcul du débordement
|
|
; Si CX=77 (Déborde 1 byte) -> Masque 0x000000FF (LSB car bswap)
|
|
; Si CX=78 (Déborde 2 bytes)-> Masque 0x0000FFFF
|
|
; Si CX=79 (Déborde 3 bytes)-> Masque 0x00FFFFFF
|
|
|
|
cmp cx, 77
|
|
jne .check_78
|
|
mov esi, 0x000000FF
|
|
jmp .mask_ready
|
|
|
|
.check_78:
|
|
cmp cx, 78
|
|
jne .check_79
|
|
mov esi, 0x0000FFFF
|
|
jmp .mask_ready
|
|
|
|
.check_79:
|
|
mov esi, 0x00FFFFFF ; Cas extrême (bord droit)
|
|
|
|
.mask_ready:
|
|
mov .mask, esi
|
|
|
|
; --- ADRESSAGE ---
|
|
mov cx, [PTR_MOUSE + mouse.x]
|
|
mov dx, [PTR_MOUSE + mouse.y]
|
|
call cga_calc_addr ; DI = Offset VRAM
|
|
|
|
mov ax, [PTR_MOUSE + mouse.cur_seg]
|
|
mov gs, ax
|
|
mov si, [PTR_MOUSE + mouse.cur_ofs]
|
|
|
|
; --- CALCUL de l'offset entre 2 lignes +0x2000 ou -0x2000 ---
|
|
mov ax, 0x2000 ; Banque +1
|
|
mov cx, [PTR_MOUSE + mouse.y]
|
|
test cx, 1
|
|
jz .setup_done
|
|
neg ax ; banque -1
|
|
|
|
.setup_done:
|
|
mov cx, ax
|
|
|
|
.row_loop:
|
|
; --- CONSTRUCTION DES MASQUES (EAX/EBX) ---
|
|
push cx ; préserver la banque "suivante"
|
|
mov cl, [PTR_MOUSE + mouse.x]
|
|
and cl, 0x07
|
|
|
|
; --- MASQUE AND (EAX) ---
|
|
mov ax, [gs:si] ; masque AND
|
|
rol eax, 16 ; Bascule les 16 bits de poids fort vers le bas
|
|
mov ax, 0xFFFF
|
|
xchg ah, al ; Échange les nouveaux octets de poids faible
|
|
ror eax, cl
|
|
or eax, .mask ; Force les bits clippés à 1 (garde le fond)
|
|
|
|
; --- MASQUE XOR (EBX) ---
|
|
xor ebx, ebx
|
|
mov bx, [gs:si+32] ; masque XOR
|
|
rol ebx, 16
|
|
xchg bh, bl
|
|
ror ebx, cl
|
|
|
|
mov edx, .mask ; charge le masque de protection
|
|
not edx ; inverse le masque de protection
|
|
and ebx, edx ; applique le masque de protection
|
|
|
|
|
|
; --- LECTURE ET MODIFICATION (EDX) ---
|
|
mov edx, [es:di] ; lecture des 16 bits a la position du curseur
|
|
; bswap edx
|
|
xchg dh, dl
|
|
rol edx, 16
|
|
xchg dh, dl
|
|
|
|
and edx, eax ; application du masque AND (AX)
|
|
xor edx, ebx ; application du masque XOR (BX)
|
|
; bswap edx
|
|
xchg dh, dl
|
|
rol edx, 16
|
|
xchg dh, dl
|
|
|
|
mov [es:di], edx ; ecriture du résultat
|
|
|
|
; --- GESTION DES BANQUES ---
|
|
pop cx
|
|
add di, cx ; banque suivante
|
|
test cx,0x8000
|
|
jz .next_line
|
|
add di,80
|
|
|
|
.next_line:
|
|
neg cx ; prochaine banque = -banque
|
|
add si, 2 ; Prochaine ligne du sprite
|
|
dec .height
|
|
jnz .row_loop
|
|
|
|
.exit_total:
|
|
pop gs
|
|
pop es
|
|
pop ds
|
|
popad
|
|
leave
|
|
ret
|
|
%undef .height
|
|
%undef .mask
|