Dans nos deux derniers articles, nous avons appris à faire des dégradés et ensuite à programmer un changement de couleur suivant l'index de la ligne en cours. Maintenant il faut mixer les deux.
Comme je l'ai dit dans un article précédent, la carte VGA n'est pas aussi performante que un processeur dédié comme en disposait l'amiga (voir même le C64 dans une moindre mesure).
Nous allons donc re-définir nos instructions et en ajouter une nouvelle.
Nous allons corrigé un problème qui peut se présenter avec certains chipset VGA, la limite des intensités qui varie de 0 à 63. Pour éviter les ennuis, nous allons tout simplement ramener les intensité de notre copperlist à 6 bits (via un shr).
Aucune modification n'est apportée a cette instruction.
Cette instruction est modifiée dans son comportement, les intensités de rouge, vert et bleu sont divisées par 4 avant d'être envoyées au DAC.
Cette instruction prends les valeurs du dernier “SetColor” (y compris la couleur) pour faire varier les intensités jusqu'aux nouvelles valeurs paramètres, et ce depuis la ligne courant jusqu'à la ligne cible.
| Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 |
|---|---|---|---|---|---|
| 0x30 | LineH | LineL | Red | Green | Blue |
Les bytes “LineH” et “LineL” sont les octets de poids fort et faible du mot qui défini la ligne “cible”, celle-ci DOIT être inférieur à copper_maxrow, sans quoi l’exécution des copper s’arrêtent avec un code d'erreur.
Cette instruction n'est pas modifiée.
#include "dos.h" #include "conio.h" typedef unsigned char BYTE; typedef unsigned int WORD; // copper_maxrow is a parameter to avoid copper/raster to turn forever // copper_error must be set to 0 to run int copper_maxrow = 400; BYTE copper_error = 0; #define PRECIS 8 BYTE copperlistv2[] = { 0x20, 0x00, 0x00, 0x00, 0x00, // setColor 0x00 0x00,0x00,0x00 : Black 0x30, 0x00, 0x32, 0xFF, 0xFF, 0x00, // GradiantTo 0x32 0xFF,0xFF,0x00 : Yellow 0x30, 0x00, 0x64, 0xFF, 0x00, 0x00, // GradiantTo 0x64 0x09,0x0f,0x34 : Red 0x30, 0x00, 0x96, 0x00, 0x00, 0x00, // GradiantTo 0x96 0x00,0x00,0x00 : Black 0x30, 0x00, 0xC8, 0xFF, 0xFF, 0x00, // GradiantTo 0x32 0xFF,0xFF,0x00 : Yellow 0x30, 0x00, 0xFA, 0xFF, 0x00, 0x00, // GradiantTo 0x64 0x09,0x0f,0x34 : Red 0x30, 0x01, 0x2C, 0x00, 0x00, 0x00, // GradiantTo 0x96 0x00,0x00,0x00 : Black 0xFF // EOC }; void DrawCopperListv2(char *copperlist) { BYTE color=0; BYTE red_prec=0, green_prec=0, blue_prec=0; // color set or starting of gradient BYTE red_step=0, green_step=0, blue_step=0; // steps (end color - start color) BYTE red_end=0, green_end=0, blue_end=0; // end color WORD line_end=0, line_start=0; // line end line start WORD line_delta=0; // delta between end and start if (copper_error!=0) return; asm { push ds push si lds si,copperlist xor cx,cx // reset cx : line counter xor bx,bx mov dx,0x3DA } // wait for stable know pos (0) w1: asm { in al,dx test al,0x08 jne w1 } w2: asm { in al,dx test al,0x08 je w2 } start:asm { // protection mov al,0x05 // error 1 cmp cx,copper_maxrow // line counter copper> max ? jb start2 // go jmp eocl } // exit start2: asm { lodsb // load copper list operand cmp al,0xFF // eocl ? jne start3 xor al,al jmp eocl } start3: asm { cmp al,0x10 // wait ? jne start4 jmp wait_line } start4:asm { cmp al,0x20 // setcolor ? jne start5 jmp set_color } start5:asm { cmp al,0x30 // gradient ? je gradient mov al,0xFF // unknown command jmp eocl } // ------------------------------------- GRADIENT gradient: asm { mov line_start,cx // preserve line start lodsw mov bh,al // reverse endian mov bl,ah mov line_end,bx // preserve line end // calculate the number of line between line_start and line_end sub bx,cx mov line_delta,bx // pct_max = line count between line_start and end lodsb // load red_end shr al,2 // reduce to 6 bits only mov red_end,al // preserve red target mov ah,al mov bl,red_prec // bl = red start sub al,bl // end - start mov red_step,al // calculate Red Stepping mov red_prec,ah // lodsb // load green_end shr al,2 mov green_end,al mov ah,al mov bl,green_prec // get green start sub al,bl mov green_step,al // calculate green stepping mov green_prec,ah lodsb // load blue_end shr al,2 mov blue_end,al mov ah,al mov bl,blue_prec sub al,bl mov blue_step,al mov blue_prec,ah } // calculate Blue Stepping gr_start: asm { inc cx // cx = cx+1 cmp cx,line_end // gradient complet ? jb gradient_hbl jmp start } // next operant gradient_hbl:asm { mov dx,0x3da } // read input state gr_in_retrace:asm { in al,dx // test if we are redrawing test al,1 jne gr_in_retrace } gr_in_display:asm { in al,dx test al,1 // wait for hbl (horizontal return) je gr_in_display mov ax,cx sub ax,line_start // pct_current(ax) = currentline(cx) - line_start mov bx,line_delta // bx = line_start - line_end xor dx,dx shl ax,PRECIS // increase precision div bx // pct_current / pct_max mov bx,ax // bx = percentage 0..100 cli mov al,color mov dx,0x3c8 out dx,al // select color index inc dx xor ax,ax mov al,red_step imul bl // must be signed multiplication shr ax,PRECIS add al,red_prec out dx,al // set RED to dac mov red_end,al xor ax,ax mov al,green_step imul bl // must be signed multiplication shr ax,PRECIS add al,green_prec out dx,al // set GREEN to dac mov green_end,al xor ax,ax mov al,blue_step imul bl // must be signed multiplication shr ax,PRECIS add al,blue_prec out dx,al // set BLUE to dac mov blue_end,al sti jmp gr_start } // new line // ------------------------------------- WAIT wait_line:asm { lodsw mov bh,al // swap byte endian encoding craps mov bl,ah // bx = line word mov dx,0x3da } // input state wait_next: asm { inc cx // cx = cx+1 cmp cx,bx // current line>= wait_line ? jae wait_end } // YES : next operand please in_retrace:asm { in al,dx // read input state, test if we are redrawing test al,1 jne in_retrace } in_display:asm { in al,dx test al,1 // wait for hbl (horizontal return) je in_display jmp wait_next } // new line wait_end:asm { jmp start } // ------------------------------------- SETCOLOR set_color: asm { cli lodsb // get color index mov color,al mov dx,0x3c8 out dx,al // select color index inc dx // mov dx,0x3c9 lodsb // get RED level shr al,2 mov red_prec,al out dx,al // set RED to dac lodsb // get GREEN level shr al,2 mov green_prec,al out dx,al // set GREEN to dac lodsb // get BLUE level shr al,2 mov blue_prec,al out dx,al // set BLUE to dac sti jmp start } // get next operand eocl:asm { sti pop si pop ds mov copper_error, al // set error (if any) xor al,al // normally we should restore whole DAC's status mov dx,0x3c8 // but we only reset color 0 to black out dx,al inc dx out dx,al // turn to RGB 0,0,0 out dx,al out dx,al } } void main() { unsigned char running=1; textmode(3); clrscr(); while (running) { running=(copper_error?0:1); if (kbhit()) { running=0; } // do some stuffs printf("T h i s I s T h e T e s t\n"); DrawCopperListv2(copperlistv2); } printf("\n error: %i\n",copper_error); }
A cause des limitations du x86 concernant les jump conditionnels (near), il a fallut mettre en place un structure de type switch case; car l'oppérande n'est que de 2 octets et n'est pas capable de faire de sauts conditionnel au delà d'un delta de -126 +127.