diff --git a/doc/assets/Capture-2026-01-29 210122.png b/doc/assets/Capture-2026-01-29 210122.png new file mode 100644 index 0000000..ef709b4 Binary files /dev/null and b/doc/assets/Capture-2026-01-29 210122.png differ diff --git a/readme.md b/readme.md index f5811ba..2e0906d 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,7 @@ Rather than booting an operating system from disk, the PC starts **directly from The project is intentionally educational, experimental, and minimalist. -![Screenshot of the boot + text test](/doc/assets/Capture-txt-test.png) +![Screenshot of the boot + text test](/doc/assets/Capture-2026-01-29 210122.png) --- @@ -135,7 +135,7 @@ Au lieu de charger un système d’exploitation depuis un disque, le PC **démar Le projet est volontairement **éducatif, expérimental et minimaliste**. -![Screenshot of the boot + text test](/doc/assets/Capture-txt-test.png) +![Screenshot of the boot + text test](/doc/assets/Capture-2026-01-29 210122.png) --- diff --git a/src/bda.asm b/src/bda.asm index 95729d1..079a420 100644 --- a/src/bda.asm +++ b/src/bda.asm @@ -93,8 +93,6 @@ endstruc %define BDA_MOUSE 0x0000 %define BDA_GFX (BDA_MOUSE + mouse_size) -%warning offset Mouse: BAD_MOUSE, Gfx : BDA_GFX - ; ; memory map ; diff --git a/src/boot.asm b/src/boot.asm index 25dd99e..d8c3a10 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -58,7 +58,11 @@ err_vganok db 'VGA Not Initialized',0 cpt_txt db '0123456789A123456789B123456789C123456789D123456789E123456789F123456789G123456789',0 helloworld db 'Hello World !',0 -centre db 'TEXTEAUCENTR',0 + +W_T db '1 - TEXT WHITE - TRANSPARENT',0 +B_T db '2 - TEXT BLACK - TRANSPARENT',0 +W_B db '3 - TEXT WHITE - ON BLACK',0 +B_W db '4 - TEXT BLACK - ON WHITE',0 reset: cli @@ -100,23 +104,54 @@ reset: ; on active le mode graphique GFX_DRV GFX_INIT - mov cx, 300 - mov dx, 102 + mov cx, 290 + mov dx, 80 GFX_DRV GFX_GOTOXY GFX_SET_WRTIE_MODE GFX_TXT_WHITE_TRANSPARENT push cs pop ds - mov si, centre + mov si, W_T + GFX_DRV GFX_WRITE + + mov cx, 290 + mov dx, 90 + GFX_DRV GFX_GOTOXY + + GFX_SET_WRTIE_MODE GFX_TXT_BLACK_TRANSPARENT + + push cs + pop ds + mov si, B_T + GFX_DRV GFX_WRITE + + mov cx, 290 + mov dx, 100 + GFX_DRV GFX_GOTOXY + + GFX_SET_WRTIE_MODE GFX_TXT_WHITE + + push cs + pop ds + mov si, W_B + GFX_DRV GFX_WRITE + + mov cx, 290 + mov dx, 110 + GFX_DRV GFX_GOTOXY + + GFX_SET_WRTIE_MODE GFX_TXT_BLACK + + push cs + pop ds + mov si, B_W GFX_DRV GFX_WRITE ; call mouse_reset call mouse_init - GFX_DRV GFX_CRS_UPDATE - - GFX_SET_WRTIE_MODE GFX_TXT_BLACK_ON_WHITE + GFX_SET_WRTIE_MODE GFX_TXT_BLACK xor cx,cx mov dx,0 @@ -140,6 +175,8 @@ reset: mov dx,24 GFX_SET_WRTIE_MODE 0 + GFX_DRV GFX_CRS_UPDATE + .loopshift: GFX_DRV GFX_GOTOXY mov si, helloworld @@ -152,6 +189,7 @@ reset: mov ax,BDA_DATA_SEG mov ds,ax endless: + GFX_SET_WRTIE_MODE GFX_TXT_BLACK xor cx,cx mov dx,180 GFX_DRV GFX_GOTOXY diff --git a/src/common/debug_cga.asm b/src/common/debug_cga.asm index e67c499..66dd3c0 100644 --- a/src/common/debug_cga.asm +++ b/src/common/debug_cga.asm @@ -60,31 +60,26 @@ dump_ram_words: ; Sortie : Affiche "1A2B" via cga_putc ; ============================================================================= print_word_hex: - push ax ; Sauvegarde AX car on va le modifier (rotation) - push cx - push bx - + pusha + mov dx, ax mov cx, 4 ; 4 caractères hex dans un mot de 16 bits .next_digit: ; On veut afficher les bits 12-15 d'abord. ; L'astuce est de faire une rotation à gauche de 4 bits. ; Le quartet de poids fort se retrouve en bas (bits 0-3). - rol ax, 4 + rol dx, 4 - mov bx, ax ; Copie temporaire - and bl, 0x0F ; On isole les 4 derniers bits (0 à 15) + mov ax, dx ; Copie temporaire + and al, 0x0F ; On isole les 4 derniers bits (0 à 15) - mov al, bl ; AL contient la valeur numérique (0-15) call nibble_to_ascii ; Convertit AL en caractère ASCII call cga_putc ; Affiche le caractère loop .next_digit ; Répète 4 fois - pop bx - pop cx - pop ax ; Restaure l'état original de AX + popa ret ; ============================================================================= diff --git a/src/drivers/gfx_cgam.asm b/src/drivers/gfx_cgam.asm index b817c92..3c337dc 100644 --- a/src/drivers/gfx_cgam.asm +++ b/src/drivers/gfx_cgam.asm @@ -36,12 +36,15 @@ ; sinon le code utilisé sera toujours "shifted" ; %define FULL_MODE_ALLIGNED 1 -; align putch mode -%define GFX_TXT_WHITE_TRANSPARENT 0 -%define GFX_TXT_BLACK_TRANSPARENT 1 -%define GFX_TXT_TRANSP_ON_WHITE 2 -%define GFX_TXT_WHITE_ON_BLACK 3 -%define GFX_TXT_BLACK_ON_WHITE 4 +; +; 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 00000011b +%define GFX_TXT_BLACK 00000010b %macro GFX_DRV 1 call word [cs:graph_driver + ((%1)*2)] @@ -61,6 +64,21 @@ %define GFX_WRITE 6 %define GFX_CRS_UPDATE 7 +; ------------------------------------------------------------ +; COMMENTAIRE SUR LA MÉTHODE D'APPEL +; +; Pour appeler une fonction du driver depuis votre GUI : +; 1. Définissez l'adresse de base du driver (ex: GFX_DRIVER_BASE) +; 2. Utilisez un call indirect via l'offset de la table. +; +; Exemple en ASM pour remplir un rectangle : +; call [GFX_DRIVER_BASE + 9] ; Appelle gfx_fill_rect +; +; Cette méthode isole totalement votre bibliothèque GUI du driver. +; 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. +; ------------------------------------------------------------ + graph_driver: dw cga_init dw cga_putpixel @@ -77,7 +95,6 @@ graph_driver: %include "./common/cursor.asm" %include "./common/chartable.asm" -; ; ; convert al -> AX aligned with "cl" %macro ALING_BYTE 0 @@ -88,7 +105,6 @@ graph_driver: xchg ah,al %endmacro - %macro GFX_SET_WRTIE_MODE 1 push fs @@ -101,21 +117,6 @@ graph_driver: pop ax %endmacro -; ------------------------------------------------------------ -; COMMENTAIRE SUR LA MÉTHODE D'APPEL -; -; Pour appeler une fonction du driver depuis votre GUI : -; 1. Définissez l'adresse de base du driver (ex: GFX_DRIVER_BASE) -; 2. Utilisez un call indirect via l'offset de la table. -; -; Exemple en ASM pour remplir un rectangle : -; call [GFX_DRIVER_BASE + 9] ; Appelle gfx_fill_rect -; -; Cette méthode isole totalement votre bibliothèque GUI du driver. -; 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. -; ------------------------------------------------------------ - ; ------------------------------------------------------------ ; initialise le mode graphique (via l'int 10h) ; @@ -139,7 +140,6 @@ cga_init: cga_none: ret - ; ------------------------------------------------------------ ; Calcule DI + AH=mask pour (CX=x, DX=y) en mode CGA 640x200 ; @@ -243,83 +243,6 @@ get_glyph_offset: add si, font8x8 ret -; --------------------------------------------------------------------------- -; cga_putc_aligned -; In : AL = char (ASCII) -; Uses: DS:GFX_CUR_* (baseDI, shift, etc.) -; Out: avance le curseur d'un caractère (8 pixels) sans recalcul complet -; Notes: -; - Nécessite une font 8x8 accessible en CS (voir get_glyph8x8_cs_si) -; - Transparent: bits 0 du glyph ne touchent pas le fond -; --------------------------------------------------------------------------- - -%ifdef FULL_MODE_ALLIGNED -cga_putc_aligned: - ; Base offset VRAM pour la scanline Y - mov di, [fs:BDA_GFX + gfx.cur_offset] - mov bx, [fs:BDA_GFX + gfx.cur_line_ofs] - mov dl, [fs:BDA_GFX + gfx.cur_mode] - - mov cx,4 -.row_loop: - ; AL = byte glyph pour cette scanline - mov ax, [cs:si] - add si,2 - - cmp dl,0 - jne .black_transparent - - ; white with transparent - or [es:di], al - or [es:di+bx], ah - jmp .next - -.black_transparent: - cmp dl,1 - jne .transparent_white - - ; black with transparent - not ax - and [es:di], al - and [es:di+bx], ah - jmp .next - -.transparent_white: - cmp dl,2 - jne .black_white - - ; transparent on white - not ax - or [es:di], al - or [es:di+bx], ah - jmp .next - -.black_white: - cmp dl,3 - jne .white_transparent - - ; white on black - mov [es:di], al - mov [es:di+bx], ah - jmp .next - -.white_transparent: - not ax - mov [es:di], al - mov [es:di+bx], ah - -.next: - add di,CGA_STRIDE - - loop .row_loop - - ; Avancer curseur d'un caractère (8 pixels) - inc word [fs:BDA_GFX + gfx.cur_offset] - add word [fs:BDA_GFX + gfx.cur_x], 8 - - ret -%endif - ; --------------------------------------------------------------------------- ; cga_putc_unalign ; In : AL = char (ASCII) @@ -328,61 +251,6 @@ cga_putc_aligned: ; - x non aligné (x&7 != 0) ; - écrit sur 2 bytes (di et di+1) dans chaque banque ; --------------------------------------------------------------------------- -cga_putc_unaligned: - ; Base offset VRAM pour la scanline Y - mov di, [fs:BDA_GFX + gfx.cur_offset] - mov bx, [fs:BDA_GFX + gfx.cur_line_ofs] - mov ch, [fs:BDA_GFX + gfx.cur_mode] - mov cl, [fs:BDA_GFX + gfx.cur_shift] - - ; mov bx, 0x2000 ; Banque +1 - - mov bp,4 -.row_loop: - - ; ligne "paire" du gylphe - mov al, [cs:si] - inc si - ALING_BYTE - push ax - - ; ligne "impaire" du gylphe - mov al, [cs:si] - inc si - ALING_BYTE - pop dx - xchg ax, dx - - cmp ch,0 - ja .black_transparent - -; white with transparent - or [es:di], ax - or [es:di+bx], dx - jmp .next - -.black_transparent: -; cmp ch,1 -; jne .transparent_white - -; black with transparent - not ax - not dx - and [es:di], ax - and [es:di+bx], dx -; jmp .next -.next: - add di,CGA_STRIDE - - dec bp - jnz .row_loop - - ; Avancer curseur d'un caractère (8 pixels) - inc word [fs:BDA_GFX + gfx.cur_offset] - add word [fs:BDA_GFX + gfx.cur_x], 8 - - ret - cga_putc: pusha push fs @@ -395,17 +263,76 @@ cga_putc: call get_glyph_offset -%ifdef FULL_MODE_ALLIGNED - cmp byte [fs:BDA_GFX + gfx.cur_shift], 0 - jne .unaligned + ; Base offset VRAM pour la scanline Y + mov di, [fs:BDA_GFX + gfx.cur_offset] + mov bx, [fs:BDA_GFX + gfx.cur_line_ofs] + mov ch, [fs:BDA_GFX + gfx.cur_mode] + mov cl, [fs:BDA_GFX + gfx.cur_shift] + mov bp,4 +.row_loop: + ; gestion du fond de texte + test ch, 00000010b ; transparent background ? + jz .skip_attribut ; oui - call cga_putc_aligned - jmp .done -.unaligned: -%endif + 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 + ALING_BYTE + mov dx,ax + + ; ligne "impaire" du gylphe + mov al, [cs:si] + inc si + ALING_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 bp + jnz .row_loop + + ; Avancer curseur d'un caractère (8 pixels) + inc word [fs:BDA_GFX + gfx.cur_offset] + add word [fs:BDA_GFX + gfx.cur_x], 8 - call cga_putc_unaligned -.done: pop es pop fs popa @@ -569,6 +496,9 @@ cga_cursor_move: mov ax, BDA_DATA_SEG mov ds, ax + cmp byte [BDA_MOUSE + mouse.cur_visible], 0 + je .done + cmp byte [BDA_MOUSE + mouse.cur_drawing],0 jne .done mov byte [BDA_MOUSE + mouse.cur_drawing],1 @@ -594,7 +524,7 @@ cga_cursor_move: ; Stocke 16 DWORDs: chaque DWORD contient (b0|b1<<8|b2<<16) dans les 24 bits bas. ; ----------------------------------------------- cga_cursor_savebg: - cmp byte [BDA_MOUSE + mouse.bkg_saved], 0 + cmp byte [BDA_MOUSE + mouse.bkg_saved], 0 ; le buffer n'a pas encore été restauré jne .done mov byte [BDA_MOUSE + mouse.bkg_saved], 1 @@ -608,38 +538,22 @@ cga_cursor_savebg: ; calcule ES:DI pour (x,y) call cga_calc_addr - ; bank_add = +0x2000 si DI<0x2000 sinon -0x2000 - mov bx, 02000h - cmp di, bx - jl .bank_ok - neg bx -.bank_ok: mov [BDA_MOUSE + mouse.cur_addr_start], di - mov [BDA_MOUSE + mouse.cur_bank_add], bx - - ; DS:SI = buffer (64 bytes) lea si, [BDA_MOUSE + mouse.bkg_buffer] - mov bp, 8 ; 8 itérations = 16 lignes (pair/impair via bank) -.rowpair: + mov cx,16 +.row_loop: ; ---- ligne bank courant ---- mov eax, [es:di] ; lit b0 b1 b2 b3 - and eax, 00FFFFFFh ; ne garder que 3 bytes mov [ds:si], eax ; stocke 4 bytes (le byte haut sera 0) add si, 4 - ; ---- ligne bank opposé ---- - add di, bx ; +0x2000 ou -0x2000 - mov eax, [es:di] - and eax, 00FFFFFFh - mov [ds:si], eax - add si, 4 - - sub di, bx - add di, CGA_STRIDE*2 ; avance de 2 lignes dans bank courant - dec bp - jnz .rowpair - + 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 .done: ret @@ -657,35 +571,20 @@ cga_cursor_restorebg: ; restaurer depuis la dernière position sauvegardée mov di, [BDA_MOUSE + mouse.cur_addr_start] - mov bx, [BDA_MOUSE + mouse.cur_bank_add] - lea si, [BDA_MOUSE + mouse.bkg_buffer] - - mov bp, 8 -.rowpair: + mov cx, 16 +.row_loop: ; ---- ligne bank courant ---- - mov eax, [ds:si] ; saved (24 bits bas) + mov eax, [ds:si] ; saved (32 bits) + mov [es:di], eax ; restore line add si, 4 - mov ecx, [es:di] ; dst actuel - and ecx, 0FF000000h ; garder b3 - and eax, 00FFFFFFh - or eax, ecx - mov [es:di], eax - ; ---- ligne bank opposé ---- - add di, bx - mov eax, [ds:si] - add si, 4 - mov ecx, [es:di] - and ecx, 0FF000000h - and eax, 00FFFFFFh - or eax, ecx - mov [es:di], eax - - sub di, bx - add di, CGA_STRIDE*2 - dec bp - jnz .rowpair + 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 .done: ret @@ -720,6 +619,38 @@ cga_cursor_draw: mov bp, ax .y_ok: + ; 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, [BDA_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: + ; On sauvegarde ce masque pour l'utiliser dans la boucle + ; On utilise la pile pour libérer les registres + push esi ; [ESP] contient maintenant le masque ; --- ADRESSAGE --- mov cx, [BDA_MOUSE + mouse.x] @@ -747,18 +678,25 @@ cga_cursor_draw: ; --- MASQUE AND (EAX) --- mov eax, 0xFFFFFFFF - mov ax, [gs:si] ; masque AND - xchg al, ah ; Redresser pour l'ordre des pixels CGA + mov ax, [gs:si] ; masque AND + xchg al, ah ; Redresser pour l'ordre des pixels CGA bswap eax ror eax, cl + or eax, [esp+2] ; Force les bits clippés à 1 (garde le fond) + ; --- MASQUE XOR (EBX) --- xor ebx, ebx - mov bx, [gs:si+32] ; masque XOR + mov bx, [gs:si+32] ; masque XOR xchg bl, bh bswap ebx ror ebx, cl + mov edx, [esp+2] ; 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 @@ -780,6 +718,7 @@ cga_cursor_draw: dec bp jnz .row_loop + pop esi .exit_total: pop es pop ds diff --git a/src/drivers/mouse_ps2.asm b/src/drivers/mouse_ps2.asm index b3b60df..cc347ef 100644 --- a/src/drivers/mouse_ps2.asm +++ b/src/drivers/mouse_ps2.asm @@ -287,9 +287,15 @@ isr_mouse_handler: ; lire un octet depuis le contrôleur et le stocker dans le buffer in al, i8042_PS2_DATA ; lire octet souris - inc byte [BDA_MOUSE + mouse.blinker] - movzx bx, byte [BDA_MOUSE + mouse.idx] + cmp bl,0 + jne .read_packet + + ; check bit 3 octet 0 pour assurer que le bloc est ok: + test al, 00001000b ; bit 3 = 1 + jz .done_eoi ; allignement erroné + +.read_packet: mov byte [BDA_MOUSE + mouse.buffer + bx], al inc byte [BDA_MOUSE + mouse.idx] @@ -301,11 +307,6 @@ isr_mouse_handler: ; packet complet, on decode les données (ou on jette si c'est incorrect) mov byte [BDA_MOUSE + mouse.idx], 0 ; reset de l'index de lecture - ; check bit 3 octet 0 pour assurer que le bloc est ok: - mov al, byte [BDA_MOUSE + mouse.buffer] - and al, 00001000b ; bit 3 = 1 - je .done_eoi ; allignement erroné - ; debut du décodage des données mov bl, [BDA_MOUSE + mouse.buffer] ; status mov al,bl @@ -331,28 +332,28 @@ isr_mouse_handler: ; --- CLAMPING X --- ; Check minimum (0) - cmp word [fs:BDA_MOUSE + mouse.x], 0 + cmp word [BDA_MOUSE + mouse.x], 0 jge .check_x_max - mov word [fs:BDA_MOUSE + mouse.x], 0 + mov word [BDA_MOUSE + mouse.x], 0 jmp .done_x .check_x_max: ; Check maximum (e.g., 639 for graphics, or pixel width) - cmp word [fs:BDA_MOUSE + mouse.x], GFX_WIDTH-1 ; Replace with variable or define + cmp word [BDA_MOUSE + mouse.x], GFX_WIDTH-1 ; Replace with variable or define jle .done_x - mov word [fs:BDA_MOUSE + mouse.x], GFX_WIDTH-1 + mov word [BDA_MOUSE + mouse.x], GFX_WIDTH-1 .done_x: ; --- CLAMPING Y --- ; Check minimum (0) - cmp word [fs:BDA_MOUSE + mouse.y], 0 + cmp word [BDA_MOUSE + mouse.y], 0 jge .check_y_max - mov word [fs:BDA_MOUSE + mouse.y], 0 + mov word [BDA_MOUSE + mouse.y], 0 jmp .done_y .check_y_max: ; Check maximum - cmp word [fs:BDA_MOUSE + mouse.y], GFX_HEIGHT-1 ; Replace with variable or define + cmp word [BDA_MOUSE + mouse.y], GFX_HEIGHT-1 ; Replace with variable or define jle .done_y - mov word [fs:BDA_MOUSE + mouse.y], GFX_HEIGHT-1 + mov word [BDA_MOUSE + mouse.y], GFX_HEIGHT-1 .done_y: ; check si il y a un 4eme byte a traiter