From 4b581d497d0f4ca6ca7b657355a880f803801a49 Mon Sep 17 00:00:00 2001 From: Frater <13979047+saintfrater@users.noreply.github.com> Date: Thu, 12 Feb 2026 23:10:12 +0100 Subject: [PATCH] Fixed 86Box stack issues --- doc/assets/86box-machine.png | Bin 0 -> 46562 bytes doc/assets/86box-video.png | Bin 0 -> 39436 bytes doc/assets/Capture-86Box.png | Bin 0 -> 34446 bytes doc/assets/Capture-QEMU.png | Bin 0 -> 25700 bytes doc/assets/screenshot.md | 12 + doc/build.md | 65 ++ readme.md | 74 +- src/boot.asm | 8 +- src/common/generic.asm | 86 +- src/drivers/gfx_cgam.asm | 1902 +++++++++++++++++----------------- 10 files changed, 1089 insertions(+), 1058 deletions(-) create mode 100644 doc/assets/86box-machine.png create mode 100644 doc/assets/86box-video.png create mode 100644 doc/assets/Capture-86Box.png create mode 100644 doc/assets/Capture-QEMU.png create mode 100644 doc/assets/screenshot.md create mode 100644 doc/build.md diff --git a/doc/assets/86box-machine.png b/doc/assets/86box-machine.png new file mode 100644 index 0000000000000000000000000000000000000000..9b4550d4000f672205ed0d9d3c365d653ddffd33 GIT binary patch literal 46562 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 literal 0 HcmV?d00001 diff --git a/doc/assets/86box-video.png b/doc/assets/86box-video.png new file mode 100644 index 0000000000000000000000000000000000000000..9ca89b4785343a54311bfe5facb75a64cd913139 GIT binary patch literal 39436 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

G55iRgY_ZST{|6&>RJyFq@ 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

`SduHak9HJsmBwH8P7ZtAk zfGs4|?R1ZwZS4}k;GYJcMjETM-ZQdll^g1Vo#I(d-Ftt+Znya&z}4M?sB9R1Uo~9W z80g$${Dw6B&>7kPKOf69_RS?ji6hxYv#P+fcEr3|DsUNj1(;mE!CIb` zgh`s7r-WTw5K*-If}0>ef4UDY=xr-&sXGV!?{VVDj4WLR5p;SNWX7 z@-DO(1cCLLnqV#XkFT-k$X%HE4gp~GBG#q;YPJ6HpRF4do7M*2BGEnW?5x& zx1T|<9LM|cp83wy*O&-Dc1p9vde&-4wI)m0MhK*V9Lml~b(;s5A()g<&@UG*|4 zSIs)4PxGC}86?$A;34gTTv~|Cx{5xjPe5|RCjn|TW-Qf*@iuM}%YMuX=aN+q#cQ%y z%NbNri=QFd@o%lWtR+Nx*&QA^hLtCv0BMJC^ofEy0z{c_MBX8?EkT;mhc8N-O*VgIM!U`f>+9uUw(%ez(HGkk)3(MjYeN06ga?yU_ z%E2M7XEOOvi1{d-BJ?{iQD69)3#?j?S-~q+7+K+a`vbS9q>a9*E9Id;h zgPC7d3SA@53WOil3s+`u*u0w%ajbEI!$MqmW1_^u3fum5iPtPmWLsNc2m%4kyvcva zA8@u4eUyeyY2i|BC_utl%x9k9IFeam7@Rd==ZlO${3-*x%QPrzQka?Yi@Y#fDo(gh zl02PPCxi=NtY~1zdLfEzcDbPClI4Wx3Nz6+*{k0d+9_G-%v<5%TXvc;@5Y`~Hs z3(KI(dIfiBK%2zJ8{y_olzsCwoGCu%#d?wIU~2bFhgBQ*3l@h?OQFc9ISvs^R%j7; zpf9?5GdhL+P=eIA=4o5eL-C8!LM^W_Kuz|b0WC2vh^RZh6_&hOLFV@{no=T_n3pZb5JXASfK literal 0 HcmV?d00001 diff --git a/doc/assets/Capture-86Box.png b/doc/assets/Capture-86Box.png new file mode 100644 index 0000000000000000000000000000000000000000..11ef9788947c282e2b31c240517c6db4f3278823 GIT binary patch literal 34446 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 literal 0 HcmV?d00001 diff --git a/doc/assets/Capture-QEMU.png b/doc/assets/Capture-QEMU.png new file mode 100644 index 0000000000000000000000000000000000000000..3667dbea6c04534d6276e7a7ec7ffef8ce03b3e7 GIT binary patch literal 25700 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 Zh5W)7A1ns{Gl(i}K{{w?J$o2pL literal 0 HcmV?d00001 diff --git a/doc/assets/screenshot.md b/doc/assets/screenshot.md new file mode 100644 index 0000000..a221cc3 --- /dev/null +++ b/doc/assets/screenshot.md @@ -0,0 +1,12 @@ + +![Screenshot of the boot + text test](/doc/assets/Capture-2026-01-29-210122.png) + +Screenshot of early boot version + Text functions + +![Screenshot WIP - Buttons & Sliders](/doc/assets/Capture-2026-02-03-213148.png) + +Screenshot of early buttons & sliders + +![Screenshot WIP - Rounded Buttons & Checkboxes ](/doc/assets/Capture-2026-02-06-232336.png) + +Screenshot of rounded buttons & checkboxes \ No newline at end of file diff --git a/doc/build.md b/doc/build.md new file mode 100644 index 0000000..69d5ce5 --- /dev/null +++ b/doc/build.md @@ -0,0 +1,65 @@ +### Build & Run + +#### Compilation + +From the project root: + +```bash +cd src +nasm -f bin boot.asm -o ..\build\bios.bin +``` + +This produces a raw BIOS ROM image (bios.bin) suitable for direct execution by QEMU. + +#### QEMU Execution + +The QEMU VGA BIOS must be copied into a local rom/ directory. + +Default locations of `vgabios.bin`: + +Windows +`C:\Program Files\qemu\share\vgabios.bin` + +Linux +`/usr/share/qemu/vgabios.bin` + +You can use an video bios from the 86box's project: + +https://github.com/86Box/roms + +Copy it to: `roms/vgabios.bin` + +Then run QEMU with: + +```bash +qemu-system-i386 \ + -bios build/bios.bin \ + -device loader,file=roms/vgabios.bin,addr=0xC0000,force-raw=on \ + -device isa-debugcon,chardev=myconsole \ + -chardev stdio,id=myconsole \ + -k be \ + -display sdl +``` + +This command: + +- Loads the custom BIOS as the system ROM +- Injects the VGA BIOS at 0xC0000 +- Redirects debug output to the console +- Displays graphics using SDL + +#### 86Box Execution + +This is a bit tricky since 86box doesn't allow to pick custom BIOS file. You'll have to choose an existing computer and replace the original BIOS file with the compliled one. + +create a new configuration: +![86Box Config - Machine](/doc/assets/86box-machine.png) + +![86Box Config - video](/doc/assets/86box-video.png) + +- save the configuration +- go to the 86Box's `roms\machines\vli486sv2g\` folder +- rename the `0402.001` file (it's the original machine's BIOS); +- copy the `build\bios.bin` into the 86Bos's `roms\machines\vli486sv2g\` folder and rename as `0402.001` + + diff --git a/readme.md b/readme.md index d949581..f29572e 100644 --- a/readme.md +++ b/readme.md @@ -12,17 +12,9 @@ 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-2026-01-29-210122.png) +![Screenshot 86Box](/doc/assets/Capture-86box.png) -Screenshot of early boot version + Text functions - -![Screenshot WIP - Buttons & Sliders](/doc/assets/Capture-2026-02-03-213148.png) - -Screenshot of early buttons & sliders - -![Screenshot WIP - Rounded Buttons & Checkboxes ](/doc/assets/Capture-2026-02-06-232336.png) - -Screenshot of rounded buttons & checkboxes +You may want to get a look at the [Older screenshots](/doc/assets/screenshot.md) --- @@ -82,53 +74,7 @@ Get a look at the [API](/doc/API.md) for more information. ### Build and Run -#### Compilation - -From the project root: - -```bash -cd src -nasm -f bin boot.asm -o ..\build\bios.bin -``` - -This produces a raw BIOS ROM image (bios.bin) suitable for direct execution by QEMU. - -#### Execution - -The QEMU VGA BIOS must be copied into a local rom/ directory. - -Default locations of `vgabios.bin`: - -Windows -`C:\Program Files\qemu\share\vgabios.bin` - -Linux -`/usr/share/qemu/vgabios.bin` - -You can use an video bios from the 86box's project: - -https://github.com/86Box/roms - -Copy it to: `roms/vgabios.bin` - -Then run QEMU with: - -```bash -qemu-system-i386 \ - -bios build/bios.bin \ - -device loader,file=roms/vgabios.bin,addr=0xC0000,force-raw=on \ - -device isa-debugcon,chardev=myconsole \ - -chardev stdio,id=myconsole \ - -k be \ - -display sdl -``` - -This command: - -- Loads the custom BIOS as the system ROM -- Injects the VGA BIOS at 0xC0000 -- Redirects debug output to the console -- Displays graphics using SDL +[read detailed step to compile an run](/doc/build.md) ### Non-Goals @@ -149,20 +95,6 @@ Ce projet consiste à concevoir et développer un **BIOS/ROM PC compatible enti Au lieu de charger un système d’exploitation depuis un disque, le PC **démarre directement depuis la ROM** vers une **interface graphique minimaliste**, utilisant le mode **CGA Haute Résolution Monochrome (640×200)**. -Le projet est volontairement **éducatif, expérimental et minimaliste**. - -![Screenshot of the boot + text test](/doc/assets/Capture-2026-01-29-210122.png) - -Screenshot of early boot version + Text functions - -![Screenshot WIP - Buttons & Sliders](/doc/assets/Capture-2026-02-03-213148.png) - -Screenshot of early buttons & sliders - -![Screenshot WIP - Rounded Buttons & Checkboxes ](/doc/assets/Capture-2026-02-06-232336.png) - -Screenshot of rounded buttons & checkboxes - --- ### Objectifs diff --git a/src/boot.asm b/src/boot.asm index 84123e8..2a4f1d6 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -152,14 +152,14 @@ main_loop: pop ds DEBUG 1 - ISADBG ISA_RED, 1 + ISADBG ISA_LEFT, 1 ; Init du système GUI call gui_init_system DEBUG 2 - ISADBG ISA_RED, 2 + ISADBG ISA_LEFT, 2 ; --- CRÉATION DYNAMIQUE DES BOUTONS --- @@ -180,7 +180,7 @@ main_loop: GUI OBJ_SET_TEXT, ax, cs, str_option3 DEBUG 3 - ISADBG ISA_RED, 3 + ISADBG ISA_LEFT, 3 ; Créer Slider (Drag) GUI OBJ_CREATE, OBJ_TYPE_SLIDER, 10, 100, 150, 12 @@ -197,7 +197,7 @@ main_loop: mov .value, 0xFADE DEBUG 4 - ISADBG ISA_RED, 4 + ISADBG ISA_LEFT, 4 .loop: call gui_process_all diff --git a/src/common/generic.asm b/src/common/generic.asm index 2f70cae..aff6845 100644 --- a/src/common/generic.asm +++ b/src/common/generic.asm @@ -20,45 +20,59 @@ ; ; ============================================================================= -%macro DEBUG 1 - GFX GOTOXY, 0, 0 - mov ax, %1 - call print_word_hex -%endmacro +%define DISABLE_DEBUGER -; -; Debugger for 86Box -; -%define ISA_DBG_PORT 0x007a -; -%define ISA_RED 0 -%define ISA_GREEN 1 -%define ISA_RIGHT 2 -%define ISA_LEFT 3 -%define ISA_RESET 0xff +%ifndef DISABLE_DEBUGER -%macro ISADBG 2 - push dx - push ax - mov dx, ISA_DBG_PORT - mov al, byte %1 - out dx, al - inc dx - mov al, byte %2 - out dx, al - pop ax - pop dx -%endmacro + %macro DEBUG 1 + GFX GOTOXY, 0, 0 + mov ax, %1 + call print_word_hex + %endmacro -%macro ISADBG_RESET 0 - push dx - push ax - mov dx, ISA_DBG_PORT - mov al, ISA_RESET - out dx, al - pop ax - pop dx -%endmacro + ; + ; Debugger for 86Box + ; + %define ISA_DBG_PORT 0x007a + ; + %define ISA_RED 0 + %define ISA_GREEN 1 + %define ISA_RIGHT 2 + %define ISA_LEFT 3 + %define ISA_RESET 0xff + + %macro ISADBG 2 + push dx + push ax + mov dx, ISA_DBG_PORT + mov al, byte %1 + out dx, al + inc dx + mov al, byte %2 + out dx, al + pop ax + pop dx + %endmacro + + %macro ISADBG_RESET 0 + push dx + push ax + mov dx, ISA_DBG_PORT + mov al, ISA_RESET + out dx, al + pop ax + pop dx + %endmacro +%else + %macro DEBUG 1 + %endmacro + + %macro ISADBG 2 + %endmacro + + %macro ISADBG_RESET 0 + %endmacro +%endif ; ----------------------------------------------------------------------------------- ; PC components I/O ports diff --git a/src/drivers/gfx_cgam.asm b/src/drivers/gfx_cgam.asm index 99361ac..e932cbe 100644 --- a/src/drivers/gfx_cgam.asm +++ b/src/drivers/gfx_cgam.asm @@ -43,20 +43,20 @@ %define GFX_TXT_BLACK 00000011b %macro GFX 1-* - ; %1 est l'index de la fonction - ; On itère sur les arguments suivants en sens inverse (Convention C) + ; %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 + %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)] + %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 + ; Nettoyage de la pile (Convention CDECL: l'appelant nettoie) + ; Chaque argument = 2 octets (1 word). + add sp, (%0 - 1) * 2 %endmacro ; ------------------------------------------------------------ @@ -91,19 +91,19 @@ ; ------------------------------------------------------------ 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_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 ; ------------------------------------------------------------ @@ -112,8 +112,8 @@ graph_driver: ; ce mode est entrelacé, un bit/pixel, 8 pixels par octet ; ------------------------------------------------------------ cga_init: - mov ax, BDA_DATA_SEG - mov ds, ax + mov ax, BDA_DATA_SEG + mov ds, ax ; init graphics mode mov ah, 0x00 ; AH=00h set video mode @@ -126,7 +126,7 @@ cga_init: ; dummy functions cga_none: - ret + ret ; ------------------------------------------------------------ ; Calcule DI + AH=mask pour (CX=x, DX=y) en mode CGA 640x200 @@ -148,16 +148,16 @@ cga_calc_addr: 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 + 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 + ; calcul du masque du pixel + and cl, 7 + mov ah, 0x80 + shr ah, cl ret ; --------------------------------------------------------------------------- @@ -172,25 +172,25 @@ cga_calc_addr: ; est l'inverse de la couleur du texte ; --------------------------------------------------------------------------- cga_set_writemode: - push bp - mov bp, sp + push bp + mov bp, sp - ; --- Définition des arguments --- - %define .mode word [bp+4] + ; --- Définition des arguments --- + %define .mode word [bp+4] - push fs - push ax - mov ax, BDA_DATA_SEG - mov fs,ax + push fs + push ax + mov ax, BDA_DATA_SEG + mov fs,ax - mov ax, .mode + mov ax, .mode - mov byte [fs:BDA_GFX + gfx.cur_mode], al + mov byte [fs:BDA_GFX + gfx.cur_mode], al - pop ax - pop fs - leave - ret + pop ax + pop fs + leave + ret ; --------------------------------------------------------------------------- ; gfx_set_charpos (x,y) @@ -201,62 +201,62 @@ cga_set_writemode: ; - stocke aussi shift = x&7 ; --------------------------------------------------------------------------- cga_set_charpos: - push bp - mov bp, sp + push bp + mov bp, sp - ; --- Définition des arguments --- - %define .x word [bp+4] - %define .y word [bp+6] + ; --- Définition des arguments --- + %define .x word [bp+4] + %define .y word [bp+6] - pusha - push fs + pusha + push fs - mov ax,BDA_DATA_SEG - mov fs,ax + mov ax,BDA_DATA_SEG + mov fs,ax - ; store x,y en pixel - mov cx, .x - mov dx, .y + ; store x,y en pixel + mov cx, .x + mov dx, .y - mov [fs:BDA_GFX + gfx.cur_x], cx - mov [fs:BDA_GFX + gfx.cur_y], dx + mov [fs:BDA_GFX + gfx.cur_x], cx + mov [fs:BDA_GFX + gfx.cur_y], dx - mov ax, cx - and ax, 0x07 - mov [fs:BDA_GFX + gfx.cur_shift], al + mov ax, cx + and ax, 0x07 + mov [fs:BDA_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 + ; 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:BDA_GFX + gfx.cur_line_ofs], ax - mov [fs:BDA_GFX + gfx.cur_offset], bx + test dl, 1 + jz .even + add bx, ax + neg ax + add ax, CGA_STRIDE + .even: + mov [fs:BDA_GFX + gfx.cur_line_ofs], ax + mov [fs:BDA_GFX + gfx.cur_offset], bx - pop fs + pop fs - ; clean defs - %undef .x - %undef .y - popa - leave - ret + ; clean defs + %undef .x + %undef .y + popa + leave + ret ; --------------------------------------------------------------------------- ; get_glyph_offset @@ -264,23 +264,23 @@ cga_set_charpos: ; 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 + 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 + .qmark: + mov al, '?' + sub al, 0x20 - .ok: - xor ah, ah - mov si, ax - shl si, 3 - add si, font8x8 - ret + .ok: + xor ah, ah + mov si, ax + shl si, 3 + add si, font8x8 + ret ; --------------------------------------------------------------------------- ; cga_putc_unalign (car) @@ -289,117 +289,117 @@ get_glyph_offset: ; --------------------------------------------------------------------------- ; convert al -> AX aligned with "cl" %macro ALIGN_BYTE 0 - mov ah,al - xor al,al - shr ax,cl - xchg ah,al + 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 + push bp + mov bp, sp + sub sp, 2 ; 1 word variable locale - ; --- Définition des arguments --- - %define .car word [bp+4] + ; --- Définition des arguments --- + %define .car word [bp+4] - ; --- Définition des variables locales --- - %define .cpt word [bp-2] + ; --- Définition des variables locales --- + %define .cpt word [bp-2] - pusha - push fs - push es + pusha + push fs + push es - call cga_mouse_hide ; Protection souris + call cga_mouse_hide ; Protection souris - mov bx, VIDEO_SEG - mov es, bx - mov bx, BDA_DATA_SEG - mov fs, bx + mov bx, VIDEO_SEG + mov es, bx + mov bx, BDA_DATA_SEG + mov fs, bx - mov ax, .car + mov ax, .car - call get_glyph_offset + call get_glyph_offset - ; 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] + ; 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 .cpt, 4 + mov .cpt, 4 - .row_loop: - ; gestion du fond de texte - test ch, 00000010b ; transparent background ? - jz .skip_attribut ; oui + .row_loop: + ; gestion du fond de texte + test ch, 00000010b ; transparent background ? + jz .skip_attribut ; oui - test ch, 00000001b ; background blanc ? - je .bkg_black + 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 + ; 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 + .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 + .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 + ; ligne "impaire" du gylphe + mov al, [cs:si] + inc si + ALIGN_BYTE + xchg ax, dx - test ch,00000001b - jz .black_text + test ch,00000001b + jz .black_text - ; white text - not ax - not dx - and [es:di], ax - and [es:di+bx], dx + ; white text + not ax + not dx + and [es:di], ax + and [es:di+bx], dx - jmp .next + jmp .next - .black_text: - or [es:di], ax - or [es:di+bx], dx + .black_text: + or [es:di], ax + or [es:di+bx], dx - .next: - add di,CGA_STRIDE + .next: + add di,CGA_STRIDE - dec .cpt - jnz .row_loop + dec .cpt + 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 + ; 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_mouse_show ; Restauration souris + call cga_mouse_show ; Restauration souris - pop es - pop fs - popa - leave - ret + pop es + pop fs + popa + leave + ret ; ; write string from [DS:SI] to screen @@ -408,29 +408,29 @@ cga_putc: %define .txt_seg word [bp+4] %define .txt_ofs word [bp+6] cga_write: - push bp - mov bp, sp - push ax - push ds + push bp + mov bp, sp + push ax + push ds - mov ax, .txt_seg - mov ds, ax - mov si, .txt_ofs - cld + 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 + .loops: + lodsb + cmp al,0 + je .done + push ax + call cga_putc + jmp .loops - .done: - pop ds - pop ax - leave - ret + .done: + pop ds + pop ax + leave + ret %undef .txt_seg %undef .txt_ofs @@ -448,34 +448,34 @@ cga_write: %define .y word [bp+6] %define .color byte [bp+8] cga_putpixel: - push bp - mov bp, sp - pusha + push bp + mov bp, sp + pusha - mov ax, VIDEO_SEG - mov es, ax + mov ax, VIDEO_SEG + mov es, ax - call cga_mouse_hide ; Protection souris - mov cx, .x - mov dx, .y - call cga_calc_addr + call cga_mouse_hide ; Protection souris + mov cx, .x + mov dx, .y + call cga_calc_addr ; write cmp .color, 0 je .clear - .set: + .set: or byte [es:di], ah jmp .done - .clear: + .clear: not ah and byte [es:di], ah - .done: - call cga_mouse_show ; Restauration souris - popa - leave + .done: + call cga_mouse_show ; Restauration souris + popa + leave ret ; clean defs %undef .x @@ -489,21 +489,21 @@ cga_putpixel: ; Out: AL=0/1 ; ------------------------------------------------------------ cga_getpixel: - push bp - mov bp, sp + push bp + mov bp, sp - ; --- Définition des arguments --- - %define .x word [bp+4] - %define .y word [bp+6] + ; --- 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 cx, .x + mov dx, .y - mov ax, VIDEO_SEG - mov es, ax + mov ax, VIDEO_SEG + mov es, ax call cga_calc_addr mov al, [es:di] @@ -513,10 +513,10 @@ cga_getpixel: pop es pop di - ; clean defs - %undef .x - %undef .y - leave + ; clean defs + %undef .x + %undef .y + leave ret ; ------------------------------------------------------------ @@ -547,74 +547,74 @@ cga_background: %define .y2 word [bp+8] %define .color byte [bp+10] cga_line_vertical: - push bp - mov bp, sp - pusha - push es + push bp + mov bp, sp + pusha + push es - ; Setup ES = Video Segment - mov ax, VIDEO_SEG ; Utiliser AX plutot que SI pour setup ES - mov es, ax + ; Setup ES = Video Segment + mov ax, VIDEO_SEG ; Utiliser AX plutot que SI pour setup ES + mov es, ax - call cga_mouse_hide + call cga_mouse_hide - mov bx, .y1 - mov dx, .y2 + mov bx, .y1 + mov dx, .y2 - ; 1. Ordonner Y (BX = Debut, DX = Fin) - cmp bx, dx - jle .y_sorted - xchg bx, dx - .y_sorted: + ; 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] + ; 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 + ; 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) + ; 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 + ; 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] + ; 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 + .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_white: + or byte [es:di], ah ; Allumer le bit (AH) + jmp .next_line - .draw_black: - and byte [es:di], al ; Eteindre le bit (AL) + .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: + .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 + dec dx ; Décrémenter le compteur de hauteur + jnz .v_loop - call cga_mouse_show + call cga_mouse_show - pop es - popa - leave ; Leave gère le 'mov sp, bp / pop bp' - ret + pop es + popa + leave ; Leave gère le 'mov sp, bp / pop bp' + ret ; clean defs %undef .x %undef .y1 @@ -630,136 +630,136 @@ cga_line_vertical: %define .y word [bp+8] %define .color byte [bp+10] cga_line_horizontal: - push bp - mov bp, sp - pusha - push es + push bp + mov bp, sp + pusha + push es - ; Setup ES = Video Segment - mov ax, VIDEO_SEG - mov es, ax + ; Setup ES = Video Segment + mov ax, VIDEO_SEG + mov es, ax - call cga_mouse_hide + call cga_mouse_hide - mov ax, .x1 - mov bx, .x2 + mov ax, .x1 + mov bx, .x2 - ; 1. Ordonner X (AX = Gauche, BX = Droite) - cmp ax, bx - jle .x_sorted - xchg ax, bx - .x_sorted: + ; 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 + ; 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 + 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 + ; 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) + ; --- 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 + 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 spécial : Tout dans le même octet ? + cmp si, cx + je .single_byte - ; ============================================ - ; CAS MULTI-BYTE (Gauche -> Milieu -> Droite) - ; ============================================ + ; ============================================ + ; 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 + ; --- 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] + call .apply_mask ; Applique DH sur [ES:DI] - inc di ; Octet suivant - inc si ; Index suivant - pop cx ; Restaure Index End + 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 + ; --- 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 + 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 + .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 + .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 + call .apply_mask + jmp .done - ; ============================================ - ; CAS SINGLE BYTE - ; ============================================ - .single_byte: - ; Masque Gauche - mov cx, ax - and cx, 7 - mov dh, 0xFF - shr dh, cl + ; ============================================ + ; 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 + ; 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 + ; 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 + ; --- 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 + .done: + call cga_mouse_show + pop es + popa + leave + ret ; clean defs %undef .x1 %undef .x2 @@ -775,145 +775,145 @@ cga_line_horizontal: %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 + 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 + ; --- 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, VIDEO_SEG - mov es, ax + ; --- Bresenham --- + mov ax, VIDEO_SEG + mov es, ax - call cga_mouse_hide + call cga_mouse_hide - sub sp, 10 ; Variables locales: dx, dy, sx, sy, err + 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] + ; --- 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 + ; --- 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 + .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 + ; 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 + .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 + ; err = dx - dy + mov ax, .dx ; dx + sub ax, .dy ; dy + mov .err, ax ; err - ; Coordonnées courantes - mov cx, .x1 - mov dx, .y1 + ; 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 + .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 + 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_black: + not ah + and byte [es:di], ah - .plot_next: - pop dx - pop cx + .plot_next: + pop dx + pop cx - ; Check fin - cmp cx, .x2 - jne .step - cmp dx, .y2 - je .done + ; Check fin + cmp cx, .x2 + jne .step + cmp dx, .y2 + je .done - .step: - mov ax, .err ; e2 = err - shl ax, 1 ; e2 = 2*err + .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 + mov bx, .dy ; dy + neg bx ; -dy + cmp ax, bx + jle .check_y - add .err, bx ; err += -dy - add cx, .sx ; x += sx + 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 + .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 + 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 + .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_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 + .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 + .exit: + pop es + popa + pop bp + ret %undef .x1 %undef .y1 @@ -938,46 +938,46 @@ cga_line: %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 + 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: + ; 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: + ; 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 + 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. + ; 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, .x2, .y1, .color + GFX LINE, .x1, .y2, .x2, .y2, .color - GFX LINE, .x1, .y1, .x1, .y2, .color - GFX LINE, .x2, .y1, .x2, .y2, .color + GFX LINE, .x1, .y1, .x1, .y2, .color + GFX LINE, .x2, .y1, .x2, .y2, .color - call cga_mouse_show - popa - leave - ret + call cga_mouse_show + popa + leave + ret ; clean defs %undef .x1 %undef .y1 @@ -1002,171 +1002,171 @@ cga_draw_rect: %define .pattern_offset word [bp-6] cga_fill_rect: - push bp - mov bp, sp - sub sp, 6 ; Reserve espace pour .pat_idx et .x_end_idx - pusha - push es + push bp + mov bp, sp + sub sp, 6 ; Reserve espace pour .pat_idx et .x_end_idx + pusha + push es - mov ax, VIDEO_SEG - mov es, ax + mov ax, VIDEO_SEG + 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 + ; 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 + 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 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: + ; 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 + ; --- 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 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 + ; --- 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 + ; 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 + ; --- 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 + ; 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 + ; 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 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 + ; --- 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 + ; --- 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 + ; 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 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 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 + ; 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 + .single_byte_row: + mov al, dh + and al, ah ; Intersection des masques + call .apply_mask_fill - .row_done: - pop si - pop di + .row_done: + pop si + pop di - ; Incrémenter index motif pour la prochaine ligne - inc word .pat_idx - and word .pat_idx, 7 + ; 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: + ; --- 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 + dec bx ; On décrémente BX (Hauteur) au lieu de BP + jnz .row_loop - call cga_mouse_show - pop es + call cga_mouse_show + pop es - popa - leave - ret + 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 + ; 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 @@ -1188,63 +1188,63 @@ cga_fill_rect: %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 + push bp + mov bp, sp + sub sp, 4 ; Reserve espace pour .coord1 et .coord2 + pusha - call cga_mouse_hide + 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 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 + ; 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 + ; 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, .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 ; 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 + mov ax, .x2 + dec ax + mov bx, .y2 + dec bx ; y2 - 1 + GFX PUTPIXEL, ax, bx, .color ; Coin bas-droite - call cga_mouse_show + call cga_mouse_show - popa - leave - ret + popa + leave + ret %undef .x1 %undef .y1 %undef .x2 @@ -1262,59 +1262,59 @@ cga_draw_rounded_frame: ; ; ------------------------------------------------------------ cga_mouse_hide: - pushf ; Sauver l'état des flags (interrupts) - cli ; Désactiver les interruptions (CRITIQUE) - pushad ; Sauvegarder TOUS les registres 32-bits - push ds + pushf ; Sauver l'état des flags (interrupts) + cli ; Désactiver les interruptions (CRITIQUE) + pusha ; Sauvegarder TOUS les registres 32-bits + push ds - mov ax, BDA_DATA_SEG - mov ds, ax + mov ax, BDA_DATA_SEG + mov ds, ax - ; Décrémenter le compteur - dec byte [BDA_MOUSE + mouse.cur_counter] + ; 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 + ; 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 - call cga_cursor_restorebg + call cga_cursor_restorebg - .skip_restore: - pop ds - popad - popf ; Restaure les interruptions (STI si elles étaient là) - ret + .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 - pushad - push ds + pushf + cli + pusha + push ds - mov ax, BDA_DATA_SEG - mov ds, ax + mov ax, BDA_DATA_SEG + mov ds, ax - ; Incrémenter le compteur - inc byte [BDA_MOUSE + mouse.cur_counter] + ; 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é + ; 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 + ; 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 - popad - popf ; restore également le flag d'interruption - ret + .skip_draw: + pop ds + popa + popf ; restore également le flag d'interruption + ret ; ------------------------------------------------------------ @@ -1330,8 +1330,8 @@ cga_mouse_cursor_move: mov ax, BDA_DATA_SEG mov ds, ax - cmp byte [BDA_MOUSE + mouse.cur_counter], 0 - jl .done ; Si < 0, on ne dessine rien ! + cmp byte [BDA_MOUSE + mouse.cur_counter], 0 + jl .done ; Si < 0, on ne dessine rien ! cmp byte [BDA_MOUSE + mouse.cur_drawing],0 jne .done @@ -1347,7 +1347,7 @@ cga_mouse_cursor_move: call cga_cursor_draw mov byte [BDA_MOUSE + mouse.cur_drawing],0 - .done: + .done: pop es pop ds popad @@ -1359,82 +1359,82 @@ cga_mouse_cursor_move: ; Stocke 16 DWORDs: chaque DWORD contient les 4 bytes de la ligne ; ------------------------------------------------------------ cga_cursor_savebg: - cmp byte [BDA_MOUSE + mouse.bkg_saved], 0 ; le buffer n'a pas encore été restauré - jne .done + cmp byte [BDA_MOUSE + mouse.bkg_saved], 0 ; le buffer n'a pas encore été restauré + jne .done - push es - mov ax, VIDEO_SEG - mov es, ax + push es + mov ax, VIDEO_SEG + mov es, ax - ; mémoriser position courante (utilisée pour restore) - mov cx, [BDA_MOUSE + mouse.x] - mov dx, [BDA_MOUSE + mouse.y] - mov [BDA_MOUSE + mouse.cur_x], cx - mov [BDA_MOUSE + mouse.cur_y], dx + ; mémoriser position courante (utilisée pour restore) + mov cx, [BDA_MOUSE + mouse.x] + mov dx, [BDA_MOUSE + mouse.y] + mov [BDA_MOUSE + mouse.cur_x], cx + mov [BDA_MOUSE + mouse.cur_y], dx - ; calcule ES:DI pour (x,y) - call cga_calc_addr + ; calcule ES:DI pour (x,y) + call cga_calc_addr - mov [BDA_MOUSE + mouse.cur_addr_start], di - lea si, [BDA_MOUSE + mouse.bkg_buffer] + mov [BDA_MOUSE + mouse.cur_addr_start], di + lea si, [BDA_MOUSE + mouse.bkg_buffer] - mov cx,16 + 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 + .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 + 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 + .next_line: + loop .row_loop - pop es + pop es - .done: - mov byte [BDA_MOUSE + mouse.bkg_saved], 1 ; Flag mis à jour après la copie - ret + .done: + mov byte [BDA_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 [BDA_MOUSE + mouse.bkg_saved], 0 - je .done + cmp byte [BDA_MOUSE + mouse.bkg_saved], 0 + je .done - push es - mov ax, VIDEO_SEG - mov es, ax + push es + mov ax, VIDEO_SEG + mov es, ax - ; restaurer depuis la dernière position sauvegardée - mov di, [BDA_MOUSE + mouse.cur_addr_start] - lea si, [BDA_MOUSE + mouse.bkg_buffer] - mov cx, 16 + ; restaurer depuis la dernière position sauvegardée + mov di, [BDA_MOUSE + mouse.cur_addr_start] + lea si, [BDA_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 + .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 + 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 + .next_line: + loop .row_loop - pop es - .done: - mov byte [BDA_MOUSE + mouse.bkg_saved], 0 ; Flag mis à jour après la restauration - ret + pop es + .done: + mov byte [BDA_MOUSE + mouse.bkg_saved], 0 ; Flag mis à jour après la restauration + ret ; ============================================================ ; gfx_cursor_draw_rm16_i386_self @@ -1443,153 +1443,161 @@ cga_cursor_restorebg: ; Sortie: rien ; Détruit: registres sauvegardés/restaurés (PUSHA/POPA) ; ============================================================ +%define .height word [bp-2] +%define .mask dword [bp-6] cga_cursor_draw: - pushad - push ds - push es - push gs + push bp + mov bp, sp + sub sp, 6 ; Reserve space for locals - mov ax, BDA_DATA_SEG - mov ds, ax - mov ax, VIDEO_SEG - mov es, ax + pushad + push ds + push es + push gs - ; --- CLIPPING VERTICAL --- - mov dx, [ds:BDA_MOUSE + mouse.y] - cmp dx, 200 - jae .exit_total + cld ; Sécurité : Direction avant + mov ax, BDA_DATA_SEG + mov ds, ax + mov ax, VIDEO_SEG + mov es, ax - mov bp, 16 - mov ax, 200 - sub ax, dx ; 200-y - cmp bp, ax - jbe .y_ok ; y<= 200-16 + ; --- CLIPPING VERTICAL --- + mov dx, [ds:BDA_MOUSE + mouse.y] + cmp dx, 200 + jae .exit_total - mov bp, ax + mov ax, 16 + mov bx, 200 + sub bx, dx ; 200-y + cmp ax, bx + jbe .y_ok ; y<= 200-16 - .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 ax, bx - mov cx, [BDA_MOUSE + mouse.x] - shr cx, 3 ; CX = Byte Offset (0-79) - xor esi, esi ; ESI = Masque de protection (0 = tout dessiner) + .y_ok: + mov .height, ax - cmp cx, 76 ; 76 est le dernier offset sûr (76,77,78,79) - jbe .mask_ready ; Si <= 76, pas de débordement + ; 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. - ; 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 + 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, 77 - jne .check_78 - mov esi, 0x000000FF - jmp .mask_ready + cmp cx, 76 ; 76 est le dernier offset sûr (76,77,78,79) + jbe .mask_ready ; Si <= 76, pas de débordement - .check_78: - cmp cx, 78 - jne .check_79 - mov esi, 0x0000FFFF - jmp .mask_ready + ; 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 - .check_79: - mov esi, 0x00FFFFFF ; Cas extrême (bord droit) + cmp cx, 77 + jne .check_78 + mov esi, 0x000000FF + jmp .mask_ready - .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 + .check_78: + cmp cx, 78 + jne .check_79 + mov esi, 0x0000FFFF + jmp .mask_ready - ; --- ADRESSAGE --- - mov cx, [BDA_MOUSE + mouse.x] - mov dx, [BDA_MOUSE + mouse.y] - call cga_calc_addr ; DI = Offset VRAM + .check_79: + mov esi, 0x00FFFFFF ; Cas extrême (bord droit) - mov ax, [BDA_MOUSE + mouse.cur_seg] - mov gs, ax - mov si, [BDA_MOUSE + mouse.cur_ofs] + .mask_ready: + mov .mask, esi - ; --- CALCUL de l'offset entre 2 lignes +0x2000 ou -0x2000 --- - mov ax, 0x2000 ; Banque +1 - mov cx, [BDA_MOUSE + mouse.y] - test cx, 1 - jz .setup_done - neg ax ; banque -1 + ; --- ADRESSAGE --- + mov cx, [BDA_MOUSE + mouse.x] + mov dx, [BDA_MOUSE + mouse.y] + call cga_calc_addr ; DI = Offset VRAM - .setup_done: - mov cx, ax + mov ax, [BDA_MOUSE + mouse.cur_seg] + mov gs, ax + mov si, [BDA_MOUSE + mouse.cur_ofs] - .row_loop: - ; --- CONSTRUCTION DES MASQUES (EAX/EBX) --- - push cx ; préserver la banque "suivante" - mov cl, [BDA_MOUSE + mouse.x] - and cl, 0x07 + ; --- CALCUL de l'offset entre 2 lignes +0x2000 ou -0x2000 --- + mov ax, 0x2000 ; Banque +1 + mov cx, [BDA_MOUSE + mouse.y] + test cx, 1 + jz .setup_done + neg ax ; banque -1 - ; --- MASQUE AND (EAX) --- - mov eax, 0xFFFFFFFF - mov ax, [gs:si] ; masque AND - xchg al, ah ; Redresser pour l'ordre des pixels CGA - ;bswap eax ; equivalence i386+ - xchg ah, al ; Échange les octets de poids faible (AL <-> AH) - rol eax, 16 ; Bascule les 16 bits de poids fort vers le bas - xchg ah, al ; Échange les nouveaux octets de poids faible - ; fin bswap eax equivalence i386+ + .setup_done: + mov cx, ax - ror eax, cl + .row_loop: + ; --- CONSTRUCTION DES MASQUES (EAX/EBX) --- + push cx ; préserver la banque "suivante" + mov cl, [BDA_MOUSE + mouse.x] + and cl, 0x07 - or eax, [esp+2] ; Force les bits clippés à 1 (garde le fond) + ; --- MASQUE AND (EAX) --- + mov eax, 0xFFFFFFFF + mov ax, [gs:si] ; masque AND + xchg al, ah ; Redresser pour l'ordre des pixels CGA + ;bswap eax ; equivalence i386+ + xchg ah, al ; Échange les octets de poids faible (AL <-> AH) + rol eax, 16 ; Bascule les 16 bits de poids fort vers le bas + xchg ah, al ; Échange les nouveaux octets de poids faible + ; fin bswap eax equivalence i386+ - ; --- MASQUE XOR (EBX) --- - xor ebx, ebx - mov bx, [gs:si+32] ; masque XOR - ;xchg bl, bh - ;bswap ebx - rol ebx, 16 - xchg bh, bl - ror ebx, cl + ror eax, 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 + 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 + ;xchg bl, bh + ;bswap ebx + 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 + ; --- 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 + 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 + 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 + ; --- 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 bp - jnz .row_loop + .next_line: + neg cx ; prochaine banque = -banque + add si, 2 ; Prochaine ligne du sprite + dec .height + jnz .row_loop - pop esi - - .exit_total: - pop gs - pop es - pop ds - popad - ret + .exit_total: + pop gs + pop es + pop ds + popad + leave + ret + %undef .height + %undef .mask