diff --git a/src/drivers/gfx.asm b/src/drivers/gfx.asm index b2419fc..078687d 100644 --- a/src/drivers/gfx.asm +++ b/src/drivers/gfx.asm @@ -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 \ No newline at end of file + 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<>(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 \ No newline at end of file diff --git a/src/drivers/gfx_cgam.asm b/src/drivers/gfx_cgam.asm index 1062da7..9292f18 100644 --- a/src/drivers/gfx_cgam.asm +++ b/src/drivers/gfx_cgam.asm @@ -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<>(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 ; -----------------------------------------------