Učebnice Assembleru 86

Stmívání obrazovky

Určitě to znáte... Většina her při vykreslování obrazovky vás nezatěžuje sledováním toho, jak postupně vykreslují obrazovku. Místo toho se obrazovka zatemní a po vykreslení se ukáže obrazovka v celé kráse najednou. Je to machrovina, která není vůbec náročná na programování.

Celý efekt je založen na rychlé změně palety. Každá barva, kterou grafická karta zobrazuje se skládá ze tří barevných složek: R-červená, G-zelená, B-modrá. Poměr hodnot a úroveň jednotlivých složek určuje každou barvu ve spektru. Grafická karta VGA umožňuje určit hodnoty pro každou složku RGB v rozpětí 0-63 (paleta). Z toho vyplývá, že můžeme zobrazit 64*64*64=262144 barev. Problém je, že VGA umožňuje maximálně 256ti barevný režim (něco jiného je SVGA, tam už je i režim se 16ti miliony barev). Přesto si může programátor nastavit hodnoty složek RGB pro každou barvu z 256 (podle režimu). Nastavení se provádí vysláním kódu první zapisované barvy (např. 0) na port $3c8 a vysláním hodnot složek RGB za sebou na port $3c9 pro každou barvu. Naopak čtění se provádí vysláním kódu první čtené barvy na port $3c7 a přečtením hodnot složek všech barev za sebou z portu $3c9. V případě 256ti barevného režimu tedy pracujeme s 256*3=768 hodnotami. Postupné stmívání a rozsvěcení obrazovky se pak provádí poměrným snižováním a zvyšováním hodnot jednotlivých složek všech barev. Protože se při této činnosti provádí mnoho přesunů dat, nabízí se kritické části vytvořit v assembleru.

Celý program bych tedy udělal asi následovně:

{$G+}
uses crt;
procedure bod256 (x,y:word;barva:byte);assembler;
asm
 jmp @dal
 @vid:
 dw $0,$a000
 @dal:
 les di,cs:[offset @vid]
 mov di,y
 mov ax,di
 shl di,6
 shl ax,8
 add di,ax
 add di,x
 mov al,barva
 mov es:[di],al
end;

procedure cti (cil:pointer);assembler;
asm
 mov al,0       {první barva}
 mov dx,$3c7    {adresa portu VGA}
 out dx,al
 mov cx,768     {256 barev *3 složky}
 les di,cil     {sem to přesunem}
 mov dx,$3c9    {z toho portu}
 rep insb       {tak jedem}
end;

procedure nastav (zdroj:pointer);assembler;
asm
 mov al,0       {první barva}
 mov dx,$3c8    {adresa portu VGA}
 out dx,al
 mov cx,768     {256*3}
 push ds        {zachovat DS}
 lds si,zdroj   {nastav adresu zdroje}
 mov dx,$3c9    {sem to přesunem}
 rep outsb      {tak, a je to tam}
 pop ds         {obnovit ds}
end;

var i,j:word;
    puvodni,nova:array [0..767] of byte;
begin
 asm                        {režim 320*200*256}
  mov ax,$0013
  int $10
 end;
 for i:=0 to 320*200-1 do   {nějakou tu vatu}
  bod256 (i,0,i div 320);
 cti (@puvodni);            {zachovat původní paletu}
 for j:=31 downto 0 do      {32 kroků postupné změny}
 begin
  for i:=0 to 768 do nova[i]:=trunc((puvodni[i]/32)*j);
  nastav (@nova);
  delay (100);              {počkej chvilku}
 end;
 for j:=0 to 31 do          {32 kroků postupné změny}
 begin
  for i:=0 to 768 do nova[i]:=trunc((puvodni[i]/32)*j);
  nastav (@nova);
  delay (100);
 end;
 nastav (@puvodni);         {obnov na původní}
 readln;
end.

Protože je mezi jednotlivými kroky změny dost dlouhý delay, můžeme ho v případě, že máme pomalý počítač, trochu snížit.

Směr