WIP : mouse cursor

This commit is contained in:
Frater 2026-01-26 20:57:16 +01:00
commit baddae1256
2 changed files with 384 additions and 376 deletions

View file

@ -1,43 +1,324 @@
; On doit répartir sur 2 octets :
; mask0 = glyph >> shift
; mask1 = glyph << (8-shift)
;
; Utilise AH pour conserver glyph original
mov ah, al
; ============================================================
; gfx_cursor_draw_rm16_i386_self
; Entrée:
; DS = BDA_DATA_SEG
; Sortie: rien
; Détruit: registres sauvegardés/restaurés (PUSHA/POPA)
; ============================================================
cga_cursor_draw:
push ds
push es
push fs
push gs
pushad
; mask0 dans AL
mov al, ah
shr al, cl
; mask1 dans AH
mov ah, ah
mov dl, 8
sub dl, cl
mov cl, dl
mov dl, ah
shl dl, cl
mov ah, dl
; restaurer shift dans CL (on en a besoin pour les lignes suivantes)
mov cl, [ds:GFX_CUR_SHIFT]
; -----------------------------
; ES = VRAM
; -----------------------------
mov ax, VIDEO_SEG
mov es, ax
; Si mask0/mask1 nuls, saute ce côté
%if GFX_CHAR_COL = 1
test al, al
jz .skip0_set
or [es:di], al
.skip0_set:
test ah, ah
jz .skip1_set
or [es:di+1], ah
.skip1_set:
%else
test al, al
jz .skip0_clr
not al
and [es:di], al
.skip0_clr:
test ah, ah
jz .skip1_clr
not ah
and [es:di+1], ah
.skip1_clr:
%endif
mov ax, BDA_DATA_SEG
mov ds, ax
; -----------------------------
; GS:SI = sprite (AND puis XOR à +32)
; -----------------------------
mov ax, [BDA_MOUSE + mouse.cur_seg]
mov gs, ax
mov si, [BDA_MOUSE + mouse.cur_ofs]
; -----------------------------
; Charger x,y
; CX = x, DX = y
; -----------------------------
mov cx, [BDA_MOUSE + mouse.x]
mov dx, [BDA_MOUSE + mouse.y]
; off = x & 7 (stocké dans BL)
mov bl, cl
and bl, 7
; width = min(16, 640-x) -> DL
mov ax, GFX_WIDTH
sub ax, cx
cmp ax, 16
jbe .w_ok
mov ax, 16
.w_ok:
test al, al
jz .done
mov dl, al ; DL = width (1..16)
; height = min(16, 200-y) -> BP (compteur)
mov bp, GFX_HEIGHT
sub bp, dx
cmp bp, 16
jbe .h_ok
mov bp, 16
.h_ok:
test bp, bp
jz .done
; bytes_to_touch = ((off + width + 7) >> 3) -> BH
mov ah, bl
add ah, dl
add ah, 7
shr ah, 3
mov bh, ah ; BH = 1..3
; clipmask16 = 0xFFFF << (16-width) -> (on le recalculera par ligne)
; parité initiale (y&1) -> CH
mov ch, byte [BDA_MOUSE + mouse.y]
and ch, 1
; -----------------------------
; Calcul DI = adresse VRAM pour (x,y)
; 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/8
add di, ax
test byte [BDA_MOUSE + mouse.y], 1
jz .addr_even
add di, CGA_ODD_BANK
.addr_even:
; row index dans AL
xor ax, ax ; AL = 0 (row)
.row_loop:
; conserver row + (off/bytes) sur pile, pour libérer BL/BH si besoin
push ax
push bx ; BL=off, BH=bytes_to_touch
; -----------------------------------------
; Charger AND/XOR pour la ligne (row=AL)
; CX = AND, DX = XOR
; -----------------------------------------
xor ah, ah
shl ax, 1 ; AX = row*2
mov bx, si
add bx, ax
mov cx, [gs:bx] ; AND word
add bx, 32
mov dx, [gs:bx] ; XOR word
; restaurer row dans AL
pop bx ; restore off/bytes into BL/BH
pop ax ; restore row in AL
; -----------------------------------------
; clipmask16 = 0xFFFF << (16-width)
; width est dans DL
; clipmask -> BX
; -----------------------------------------
mov bx, 0FFFFh
mov cl, 16
sub cl, dl
shl bx, cl ; BX = clipmask
; AND' = AND | (~clipmask)
mov ax, bx
not ax
or cx, ax ; CX = AND'
; XOR' = XOR & clipmask
and dx, bx ; DX = XOR'
; -----------------------------------------
; Construire seg 16-bit depuis VRAM (ES:DI)
; w0 = (b0<<8)|b1
; seg = (w0<<off) | (b2>>(8-off)) si off!=0 et bytes==3
; seg -> AX
; -----------------------------------------
; AX = w0
mov ah, [es:di] ; b0
mov al, [es:di+1] ; b1
; if off==0 -> seg ready
test bl, bl
jz .seg_ready
; seg = w0 << off
mov cl, bl
shl ax, cl
; if bytes==3, OR b2>>(8-off)
cmp bh, 3
jne .seg_ready
mov cl, 8
sub cl, bl ; 8-off
xor bh, bh ; BH=0, BL=off (on évite bpl etc)
mov bl, [es:di+2] ; BL=b2
shr bx, cl ; BX = b2>>(8-off)
or ax, bx
; restaurer BL=off, BH=bytes: (on ne peut pas, on a écrasé)
; => ne jamais écraser BL/BH. Donc on refait proprement:
; (ce bloc est remplacé ci-dessous)
; --- on ne doit pas passer ici ---
.seg_ready:
; *** IMPORTANT ***
; Le bloc ci-dessus a montré le piège (écraser BL/BH).
; On fait le chemin correct ci-dessous, sans toucher BL/BH.
; Refaire seg proprement sans écraser BL/BH:
; AX = w0 (recharge)
mov ah, [es:di]
mov al, [es:di+1]
test bl, bl
jz .seg2_ready
mov cl, bl
shl ax, cl
cmp bh, 3
jne .seg2_ready
mov cl, 8
sub cl, bl ; 8-off
xor ah, ah
mov al, [es:di+2] ; AL=b2
; AX = b2
shr ax, cl ; AX = b2>>(8-off)
; OR into seg: need seg in another reg -> use BX temp
mov bx, [es:di] ; not ok (word read uses little endian, avoid)
; simplest: use stack temp
push ax ; save (b2>>(8-off))
; reload seg again and OR
mov ah, [es:di]
mov al, [es:di+1]
mov cl, bl
shl ax, cl
pop bx
or ax, bx
.seg2_ready:
; -----------------------------------------
; newseg = (seg & AND') ^ XOR'
; seg in AX
; -----------------------------------------
and ax, cx
xor ax, dx ; AX=newseg
; -----------------------------------------
; Ecriture vers VRAM
; - off==0 : b0=AH, b1=AL (si bytes>=2)
; - off!=0 : b0 partiel, b1 complet si bytes>=2, b2 partiel si bytes==3
; -----------------------------------------
test bl, bl
jnz .write_unaligned
.write_aligned:
mov [es:di], ah
cmp bh, 1
je .after_write
mov [es:di+1], al
jmp .after_write
.write_unaligned:
; mask0 = (1<<(8-off))-1 (bits bas de b0)
mov cl, 8
sub cl, bl
mov dh, 1
shl dh, cl
dec dh ; DH = mask0
; val0 = newseg >> (8+off)
mov bx, ax ; BX = newseg
mov cl, bl
add cl, 8
shr bx, cl ; BL = val0 (low 8)
; b0 = (old & ~mask0) | (val0 & mask0)
mov dl, [es:di] ; old b0
mov cl, dh ; CL=mask0
not cl
and dl, cl ; keep upper bits
not cl ; CL=mask0
and bl, cl ; val0 masked
or dl, bl
mov [es:di], dl
cmp bh, 1
je .after_write
; b1 = (newseg >> off) & 0xFF
mov bx, ax
mov cl, bl ; BUG: BL now val0, not off
; => on doit recharger off depuis BDA_MOUSE.x &7, ou le sauver.
; On le sauve au début de la fonction dans une variable: ici on le recharge (coût faible)
mov bl, [BDA_MOUSE + mouse.x] ; low byte x
and bl, 7
mov cl, bl
shr bx, cl
mov [es:di+1], bl
cmp bh, 2
je .after_write
; b2 partiel (bits hauts off)
; mask2 = 0xFF << (8-off)
mov cl, 8
sub cl, bl ; bl=off
mov dl, 0FFh
shl dl, cl ; DL=mask2
; part2 = (newseg << (8-off)) & 0xFF
mov bx, ax ; BX=newseg
shl bx, cl ; BL=part2
; b2 = (old & ~mask2) | (part2 & mask2)
mov dh, [es:di+2] ; old b2
mov cl, dl ; CL=mask2
not cl
and dh, cl
not cl
and bl, cl
or dh, bl
mov [es:di+2], dh
.after_write:
; -----------------------------------------
; next row
; -----------------------------------------
inc al
dec bp
jz .done
; stepping CGA:
; even->odd: +0x2000
; odd ->even: -0x2000 + 80
test ch, ch
jz .even_to_odd
; odd -> even
sub di, CGA_ODD_BANK
add di, CGA_STRIDE
xor ch, ch
jmp .row_loop
.even_to_odd:
add di, CGA_ODD_BANK
mov ch, 1
jmp .row_loop
.done:
popad
pop gs
pop fs
pop es
pop ds
ret

View file

@ -139,6 +139,37 @@ cga_init:
cga_none:
ret
; ------------------------------------------------------------
; Calcule DI + AH=mask pour (CX=x, DX=y) en mode CGA 640x200
;
; Out: ES=VIDEO_SEG, 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
add di, ax
test dl, 1
jz .even
add di, CGA_ODD_BANK
.even:
; masque bit = 0x80 >> (x&7)
push cx
and cl, 7
mov ah, 080h
shr ah, cl
pop cx
ret
; ---------------------------------------------------------------------------
; gfx_set_charpos
; In : CX = x (pixels), DX = y (pixels)
@ -185,7 +216,6 @@ cga_set_charpos:
mov [fs:BDA_GFX + gfx.cur_line_ofs], ax
mov [fs:BDA_GFX + gfx.cur_offset], bx
pop fs
popa
ret
@ -321,7 +351,7 @@ cga_putc_unaligned:
xchg ax, dx
cmp ch,0
jne .black_transparent
ja .black_transparent
; white with transparent
or [es:di], ax
@ -338,31 +368,6 @@ cga_putc_unaligned:
and [es:di], ax
and [es:di+bx], dx
; jmp .next
; .transparent_white:
; cmp ch,2
; jne .black_white
;
; ; transparent on white
; not ax
; or [es:di], ax
; or [es:di+bx], dx
; jmp .next
;
; .black_white:
; cmp ch,3
; jne .white_transparent
;
; ; white on black
; mov [es:di], ax
; mov [es:di+bx], dx
; jmp .next
;
; .white_transparent:
; not ax
; mov [es:di], ax
; mov [es:di+bx], dx
;
.next:
add di,CGA_STRIDE
@ -420,37 +425,6 @@ cga_write:
pop ax
ret
; ------------------------------------------------------------
; Calcule DI + AH=mask pour (CX=x, DX=y) en mode CGA 640x200
;
; Out: ES=VIDEO_SEG, 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
add di, ax
test dl, 1
jz .even
add di, CGA_ODD_BANK
.even:
; masque bit = 0x80 >> (x&7)
push cx
and cl, 7
mov ah, 080h
shr ah, cl
pop cx
ret
; ------------------------------------------------------------
; Dessine un pixel, accès VRAM direct.
;
@ -584,7 +558,7 @@ cga_getpixel_fast:
ret
cga_cursor_move:
pusha
push ax
push ds
push es
@ -607,7 +581,7 @@ cga_cursor_move:
.done:
pop es
pop ds
popa
pop ax
ret
; ============================================================
@ -618,9 +592,11 @@ cga_cursor_move:
; Détruit: registres sauvegardés/restaurés (PUSHA/POPA)
; ============================================================
cga_cursor_draw:
push ds
push es
push fs
push gs
pusha
pushad
; -----------------------------
; ES = VRAM
@ -628,6 +604,8 @@ cga_cursor_draw:
mov ax, VIDEO_SEG
mov es, ax
mov ax, BDA_DATA_SEG
mov ds, ax
; -----------------------------
; GS:SI = sprite (AND puis XOR à +32)
; -----------------------------
@ -642,291 +620,40 @@ cga_cursor_draw:
mov cx, [BDA_MOUSE + mouse.x]
mov dx, [BDA_MOUSE + mouse.y]
; off = x & 7 (stocké dans BL)
mov bl, cl
and bl, 7
; get DI = offset de départ x,y
; on ignore le bitmask retourné
call cga_calc_addr
; width = min(16, 640-x) -> DL
mov ax, GFX_WIDTH
sub ax, cx
cmp ax, 16
jbe .w_ok
mov ax, 16
.w_ok:
test al, al
jz .done
mov dl, al ; DL = width (1..16)
mov ax, CGA_ODD_BANK
; height = min(16, 200-y) -> BP (compteur)
mov bp, GFX_HEIGHT
sub bp, dx
cmp bp, 16
jbe .h_ok
mov bp, 16
.h_ok:
test bp, bp
jz .done
test cx, 0x0001 ; y est pair: +0x2000
; bytes_to_touch = ((off + width + 7) >> 3) -> BH
mov ah, bl
add ah, dl
add ah, 7
shr ah, 3
mov bh, ah ; BH = 1..3
; clipmask16 = 0xFFFF << (16-width) -> (on le recalculera par ligne)
; parité initiale (y&1) -> CH
mov ch, byte [BDA_MOUSE + mouse.y]
and ch, 1
; -----------------------------
; Calcul DI = adresse VRAM pour (x,y)
; 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/8
add di, ax
test byte [BDA_MOUSE + mouse.y], 1
jz .addr_even
add di, CGA_ODD_BANK
neg ax ; y impair: -0x2000
.addr_even:
; conservation du pas entre les 2 banques de VRAM
mov [BDA_MOUSE + mouse.cur_bank_add], ax
; row index dans AL
xor ax, ax ; AL = 0 (row)
; calcul du bit rotation du curseur
mov ax, cx
and ax, 0x07
mov [BDA_MOUSE + mouse.cur_bit_ofs], al
; curseur = 16 lignes
mov cx, 16
.row_loop:
; conserver row + (off/bytes) sur pile, pour libérer BL/BH si besoin
push ax
push bx ; BL=off, BH=bytes_to_touch
xor eax,eax
xor ebx,ebx
; -----------------------------------------
; Charger AND/XOR pour la ligne (row=AL)
; CX = AND, DX = XOR
; -----------------------------------------
xor ah, ah
shl ax, 1 ; AX = row*2
mov bx, si
add bx, ax
mov cx, [gs:bx] ; AND word
add bx, 32
mov dx, [gs:bx] ; XOR word
; restaurer row dans AL
pop bx ; restore off/bytes into BL/BH
pop ax ; restore row in AL
; -----------------------------------------
; clipmask16 = 0xFFFF << (16-width)
; width est dans DL
; clipmask -> BX
; -----------------------------------------
mov bx, 0FFFFh
mov cl, 16
sub cl, dl
shl bx, cl ; BX = clipmask
; AND' = AND | (~clipmask)
mov ax, bx
not ax
or cx, ax ; CX = AND'
; XOR' = XOR & clipmask
and dx, bx ; DX = XOR'
; -----------------------------------------
; Construire seg 16-bit depuis VRAM (ES:DI)
; w0 = (b0<<8)|b1
; seg = (w0<<off) | (b2>>(8-off)) si off!=0 et bytes==3
; seg -> AX
; -----------------------------------------
; AX = w0
mov ah, [es:di] ; b0
mov al, [es:di+1] ; b1
; if off==0 -> seg ready
test bl, bl
jz .seg_ready
; seg = w0 << off
mov cl, bl
shl ax, cl
; if bytes==3, OR b2>>(8-off)
cmp bh, 3
jne .seg_ready
mov cl, 8
sub cl, bl ; 8-off
xor bh, bh ; BH=0, BL=off (on évite bpl etc)
mov bl, [es:di+2] ; BL=b2
shr bx, cl ; BX = b2>>(8-off)
or ax, bx
; restaurer BL=off, BH=bytes: (on ne peut pas, on a écrasé)
; => ne jamais écraser BL/BH. Donc on refait proprement:
; (ce bloc est remplacé ci-dessous)
; --- on ne doit pas passer ici ---
.seg_ready:
; *** IMPORTANT ***
; Le bloc ci-dessus a montré le piège (écraser BL/BH).
; On fait le chemin correct ci-dessous, sans toucher BL/BH.
; Refaire seg proprement sans écraser BL/BH:
; AX = w0 (recharge)
mov ah, [es:di]
mov al, [es:di+1]
test bl, bl
jz .seg2_ready
mov cl, bl
shl ax, cl
cmp bh, 3
jne .seg2_ready
mov cl, 8
sub cl, bl ; 8-off
xor ah, ah
mov al, [es:di+2] ; AL=b2
; AX = b2
shr ax, cl ; AX = b2>>(8-off)
; OR into seg: need seg in another reg -> use BX temp
mov bx, [es:di] ; not ok (word read uses little endian, avoid)
; simplest: use stack temp
push ax ; save (b2>>(8-off))
; reload seg again and OR
mov ah, [es:di]
mov al, [es:di+1]
mov cl, bl
shl ax, cl
pop bx
or ax, bx
.seg2_ready:
; -----------------------------------------
; newseg = (seg & AND') ^ XOR'
; seg in AX
; -----------------------------------------
and ax, cx
xor ax, dx ; AX=newseg
; -----------------------------------------
; Ecriture vers VRAM
; - off==0 : b0=AH, b1=AL (si bytes>=2)
; - off!=0 : b0 partiel, b1 complet si bytes>=2, b2 partiel si bytes==3
; -----------------------------------------
test bl, bl
jnz .write_unaligned
.write_aligned:
mov [es:di], ah
cmp bh, 1
je .after_write
mov [es:di+1], al
jmp .after_write
.write_unaligned:
; mask0 = (1<<(8-off))-1 (bits bas de b0)
mov cl, 8
sub cl, bl
mov dh, 1
shl dh, cl
dec dh ; DH = mask0
; val0 = newseg >> (8+off)
mov bx, ax ; BX = newseg
mov cl, bl
add cl, 8
shr bx, cl ; BL = val0 (low 8)
; b0 = (old & ~mask0) | (val0 & mask0)
mov dl, [es:di] ; old b0
mov cl, dh ; CL=mask0
not cl
and dl, cl ; keep upper bits
not cl ; CL=mask0
and bl, cl ; val0 masked
or dl, bl
mov [es:di], dl
cmp bh, 1
je .after_write
; b1 = (newseg >> off) & 0xFF
mov bx, ax
mov cl, bl ; BUG: BL now val0, not off
; => on doit recharger off depuis BDA_MOUSE.x &7, ou le sauver.
; On le sauve au début de la fonction dans une variable: ici on le recharge (coût faible)
mov bl, [BDA_MOUSE + mouse.x] ; low byte x
and bl, 7
mov cl, bl
shr bx, cl
mov [es:di+1], bl
cmp bh, 2
je .after_write
; b2 partiel (bits hauts off)
; mask2 = 0xFF << (8-off)
mov cl, 8
sub cl, bl ; bl=off
mov dl, 0FFh
shl dl, cl ; DL=mask2
; part2 = (newseg << (8-off)) & 0xFF
mov bx, ax ; BX=newseg
shl bx, cl ; BL=part2
; b2 = (old & ~mask2) | (part2 & mask2)
mov dh, [es:di+2] ; old b2
mov cl, dl ; CL=mask2
not cl
and dh, cl
not cl
and bl, cl
or dh, bl
mov [es:di+2], dh
.after_write:
; -----------------------------------------
; next row
; -----------------------------------------
inc al
dec bp
jz .done
; stepping CGA:
; even->odd: +0x2000
; odd ->even: -0x2000 + 80
test ch, ch
jz .even_to_odd
; odd -> even
sub di, CGA_ODD_BANK
add di, CGA_STRIDE
xor ch, ch
jmp .row_loop
.even_to_odd:
add di, CGA_ODD_BANK
mov ch, 1
jmp .row_loop
;
.done:
popa
popad
pop gs
pop fs
pop es
pop ds
ret
; -----------------------------------------------