Učebnice Assembleru 86

Start grafiky a vykreslení bodu

Každý, kdo s už nějakou dobu programuje si řekne: "To snad nemyslí vážně,..." (já bych to taky řekl). Jo, jenže chyba lávky. Poznal jsem už moc "programátorů", kteří vytvořili program částečně nebo úplně nefunkční právě vinou toho, že jeho tvůrce měl problémy s inicializací grafiky. Tak tedy, jak to vlastně s grafikou je?

O tu v Pascalu se starají tzv. grafické ovladače. Mají příponu BGI a borlanďáci je začali používat v době, kdy jste mohli mít počítač s grafickou kartou Hercules, CGI nebo nějakou jinou nestandardinu. I když se dneska běžně používá jen SVGA (což je jak všichni určitě víte vylepšená VGA), tato zajímavá vlastnost Turbo Pascalu zůstala. Když tedy používáte grafiku ve svých programech, obracíte se svými požadavky právě na ovladače. Menším problémem je, že ovladače běžně nejsou součástí kódu EXE. Navíc je program hledá na místě uvedeném při volání procedury InitGraph (gd,gm,'cesta na driver'). Jak tedy zajistit aby program chodil vždy?

  • Nejjednodušším řešením je cestu uvádět prázdnou a driver přidávat do adresáře, odkud program spouštíme. Stejně ale program nebude funkční, když bude volaný z jiného adresáře. Navíc je tady riziko, že si na vás budou "lepší" programátoři ukazovat prstem a posmívat se.
  • Tak tedy jinak. Program převedeme na objekt. K tomu slouží program BINOBJ.EXE, který se dodává s Pascalem. S jeho pomocí se dají přidávat ke kódu programu libovolná data. Stačí jen soubor s nimi převést v Dosu:

  • binobj puvodni.pri novy.obj jmenoprocedury. Tak vytvoříme ze souboru puvodni.pri soubor novy.obj, který je možné přidat k programu ve formě procedury jmenoprocedury. Jak tedy přidat BGI?
    • V dosu driver převedeme:

    • binobj egavga.bgi egavga.obj vgadriver_bgi
      do objektu.
    • Do programu vložíme řádek:

    • procedure vgadriver_bgi;external;{$L egavga.obj}
    • Inicializaci grafiky provedeme:

    • RegisterBGIDriver (@vgadriver_bgi);
      gd:=detect;
      InitGraph (gd, gm,'');

Program je sice funkční ale moc pomalý. Je to proto, že se s každou hloupostí, která souvisí s grafikou obracíte právě na driver. Nejlepší je tedy BGIčka nepoužívat a o celou grafiku se starat sami. Program pak bude mít části:

  • Inicializace grafiky se provádí s pomocí služby INT $10. Inicializace se volá s nastaveným ah=0 a al=kód režimu. (Pro zrychlení se může registr ax naplnit najednou.) Kódy grafických režimů VGA jsou:

  • $ 0 - 40x 25x 16 -  textový
    $ 1 - 40x 25x 16 -  textový
    $ 2 - 80x 25x 16 -  textový
    $ 3 - 80x 25x 16 -  textový
    $ 4 -320x200x  4 - grafický
    $ 5 -230x200x  4 - grafický
    $ 6 -640x200x  2 - grafický
    $ 7 - 80x 25x  2 -  textový
    $ D -320x200x 16 - grafický
    $ E -640x200x 16 - grafický
    $ F -640x350x  2 - grafický
    $10 -640x350x 16 - grafický
    $11 -640x480x  2 - grafický
    $12 -640x480x 16 - grafický
    $13 -320x200x256 - grafický

    Nejpoužívanější jsou $12 - 16tibarevný a $13 - 256tibarevný. Grafiku pak nastartují procedury:

    procedure init12;assembler;
    asm
      mov ax, $0012
      int $10
    end;

    procedure init13;assembler;
    asm
      mov ax, $0013
      int $10
    end;
  • Pro vykreslení bodu je nutné vědět, jak to vlastně je s videopamětí (VRAM). To je část paměti, kde je uloženo vše, co je právě na obrazovce. Do VRAM můžeme přistupovat jako do jiné části paměti. Jediný rozdíl je v tom, že každá změna obsahu se okamžitě projeví změnou obrazu. To, jak je VRAM organizovaná je ovlivěno nastavenim grafické karty.
    • V režimu 320x200x256 je její počátek na adrese $A0000. Každá její slabika nese hodnotu barvy bodu na obrazovce. Na prvním místě je tedy barva prvního bodu v prvním řádku, na druhém místě barva druhého bodu prvního řádku atd. Jestliže bychom chtěli změnit barvu bodu na souřadnici x, y, pak by to v Pascalu provedl řádek: MEM [$A000:x+320*y]:=barva; z násobíme 320, protože je právě 320 slabik na každý řádek. V assembleru by pak procedura pro vykreslení bodu v tomto režimu byla:

    • procedure bod256 (x,y:word;barva:byte);assembler;
      asm
       jmp @dal               {přeskoč definici VRAM}
       @vid:
       dw $0,$a000            {tady začíná VRAM}
       @dal:
       les di,cs:[offset @vid]{načti do es:di adresu VRAM}
       mov di,y               {naber y}
       mov ax,di              {di:=di*320}
       shl di,6
       shl ax,8
       add di,ax
       add di,x               {přírůstek na x}
       mov al,barva
       mov es:[di],al         {barvu do VRAM}
      end;
    • V režimu 640x480x16 jsou barvy bodů na obrazovce uloženy v tzv rovinách. Roviny jsou čtyři pro každou složku R (červenou) G (zelenou) B (modrou) a  jas. Každá rovina má ve VRAM 80 slabik na řádek (80*8= 640 bodů). V každé slabice je 8 bitů (v úrovních 0, 1), které říkají jestli na je obrazovce obsažena barevná složka v příslušném bodu (jestliže je např. první bod na obrazovce bílý (1111), obsahují všechny čtyři roviny 1 v nejvyšším bitu první slabiky). Všechny čtyři roviny jsou na stejné adrese $A0000. To, do které roviny zapisujeme určíme změnou jednoho z vniřních registrů grafické karty. Ta umožňuje několik zápisových režimů, které se hodí pro různé potřeby (např. pro vykreslení myši). Pro vykreslení bodu použijeme proceduru:

    • procedure bod16 (x,y:word;barva:byte);assembler;
      asm
       mov ax,y
       mov bx,ax
       shl ax,6
       shl bx,4
       add ax,bx
       mov bx,x
       mov cl,bl
       shr bx,3
       add bx,ax
       and cl,7
       xor cl,7
       mov ah,1
       shl ah,cl
       mov dx,$3ce
       mov al,8
       out dx,ax
       mov ax,$0205
       out dx,ax
       mov ax,$A000
       mov es,ax
       mov al,es:[bx]
       mov al,barva
       mov es:[bx],al
       mov ax,$FF08
       out dx,ax
       mov ax,$0005
       out dx,ax
      end;

      K pochopení procedury je ale nutné znát dobře kartu VGA. To ale přesahuje rámec tohoto textu. Proto, jestliže chcete dokonale pochopit programování  grafických karet, prostudujte si knihu: Programování grafických adaptérů (Grada).

    Směr