Algoritmy rovinne grafiky

     Obrazek vytvareny na obrazovce se sklada z ruznych
elementarnich objektu: bodu, usecek, kruhu, elips atd.
Zobrazeni techto objektu je ruzne slozite. Bodu odpovida
jeden bit obrazove pameti, zde neni zadny problem.
U slozitejsich objektu (cara, kruh) je situace ponekud
slozitejsi. Z ciste matematickeho hlediska jsou tyto objekty
slozeny z nekonecneho poctu bodu. Obrazovka samozrejme
nemuze zobrazit tak velky pocet nekonecne malych bodu a tak
musi byt provedena urcita aproximace, kdy se vykresli pixely
lezici nejblize skutecnemu umisteni krivky. Algoritmy pro
rovinou grafiku maji za ukol, co nejrychleji
a nejjednoduseji, urcit polohu aproximovanych pixelu.
Z techto algoritmu je nejjednodussi algoritmus pro kresleni
usecek. Rozebereme ho zde tedy ponekud podrobneji.

Bresenhamuv algoritmus pro kresbu usecky
     Z matematiky si jeste mozna pamatujete, ze rovnici
primky lze zapsat ve tvaru:

     y = mx + b,                         ( 1 )

kde m je smernice a b posun na ose y. Je-li usecka urcena
pocatecnim a koncovym bodem (souradnicmi bodu [x1,y1] a
[x2,y2]), muzeme smernici a posunuti vyjadrit nasledujicimi
vztahy:
           y2-y1     ëy
     m =  ------- = ----
           x2-x1     ëx
                                         ( 2 )
          x2y1-x1y2     x2y1-x1y2
     b = ----------- = -----------.
            x2-x1           ëx

Algoritmy pro zobrazeni usecky vychazeji z rovnic 1 a 2.
Na nejakem intervalu ëx se souradnice y zmeni o hodnotu ëy
podle nasledujiciho vztahu:

     ëy = m ëx.

     J.E.Bresenham vymyslel algoritmus, ktery pri generovani
usecky vystaci s celociselnou aritmetikou. Na obrazku 10 je
nakreslena cast obrazovky, na ktere maji byt zobrazeny
pixely usecky. Po nakresleni leveho koncoveho bodu usecky
mame rozhodnout, zda nasledujici bod bude vykreslen na
pozici se souradnici y stejnou jako predchozi bod nebo o
jedna vetsi. Z techto dvou moznosti umisteni nasledujiciho
pixelu vybereme tu, ktera lezi blize skutecne poloze bodu
usecky.
     Popiseme si zde zpusob, jak pomoci Bresenhamova
algoritmu vykreslit usecku s kladnou smernici mensi nez 1.
Ostatni smery jsou pouze obmenou dale uvdedneho postupu. V
pripade, ze usecka ma kladnou smernici mensi nez jedna je
ridici osou osa x. Znamena to, ze postupne pro vsechny
souradnice x menene o jednicku, urcime jim odpovidajici
souradnice y. Predpokokladejme, ze pixel o souradnicich
[xi,yi] jiz byl nakreslen a my mame rozhodnou o poloze
nasledujiciho. Na vyber mame ze dvou moznosti [xi+1,yi] a
[xi+1,yi+1]. Vzdalenost techto dvou bodu od skutecneho bodu
usecky je na obraazku 10 vyznacena jako d1 a d2. Pro
skutecnou souradnci y plati:

     y = m (xi + 1) + b.

Pak plati:

     d1 = y - yi       = m (xi + 1) + b - yi

     d2 = yi + 1 - y   = yi + 1 - m (xi + 1) - b

Rozdil techto dvou vzdalenosti je:

     ëd = d1 - d2 = 2m (xi + 1) - 2yi + 2b - 1    ( 3 )

Podle promene ëd muzeme urcit, ktery ze dvou pixelu lezi
blize skutecnemu umisteni usecky. Kladna hodnota ëd, znamena
ze d1 > d2 a tudiz blizsi je pixel o souradnicich
[xi+1,yi+1]. Naopak zaporna hodnota ëd (d1 < d2) vybere
pixel o souradnicich [xi+1,yi]. Neni tedy dulezita hodnota
ëd, ale jeji znamenko. Bohuzel ëd neni diky m celocislena.
Celou rovnici 3 vsak muzeme vynasobit ëx a dostavame:

     pi = ëd ëx = 2 ëy xi - 2 ëx yi + 2 ëy + ëx (2b - 1),  ( 4 )

kde 2 ëy + ëx (2b - 1) je konstanta, ktera bude pri
nasledujicich upravach vyloucena. Hodnotu p, podle jejihoz
znamenka urcujeme polohu nasledujicich pixelu nazveme
predikci. Predikci nasledujciho bodu pi+1 muzeme zapsat
jako:

     pi+1 = 2 ëy xi+1 - 2 ëx yi+1 + konstanta        ( 5 )

Rovnice 4 a 5 muzeme odecist, vyjadrime tak pi+1 pomoci pi:

     pi+1 = pi + 2 ëy - 2 ëx (yi+1 - yi),            ( 6 )

za predpokladu, ze xi+1 = xi + 1.
     Nasledujici vztahy vyjadruji zavislost hodnoty pi+1 na
hodnote pi:
   /---------------------------------------------\
   | pi < 0      =>    pi+1 = pi + 2 ëy          |
   |---------------------------------------------|
   | pi n 0      =>    pi+1 = pi + 2 ëy - 2 ëx   |
   \---------------------------------------------/
Prvni hodnota predikce p1 se ziska dosazenim pocatecniho
bodu usecky do rovnice 4. Dostaneme p1 = 2 ëy - ëx.
     Predikce tvori zakladni kriterium pro vyber pixelu
tvoricich rastrovy obraz usecky. Hodnotu pi pro kazdy pixel
postupne aktualizujeme podle jednoduchych vztahu v tabulce.
Znamenko predikce urcuje polohu nasledujiciho pixelu. Pokud
je predikce zaporna, y souradnice nasledujiciho pixelu se
nemeni. Pokud je kladna, zvetsi se o jedna.
     V pripade, kdy je smernice vetsi nez jedna zamenime
souradnice x a y a algoritmus zustane stejny. Pokud je
smernice usecky zaporna, jedna ze souradnic se zmensuje a
zbytek postupu je shodny.

                     Priklad na zaver

     Nektere zde uvedene poznatky shrneme do ukazkoveho
programu. Je jim jednoducha graficka knihovna, ktera dovede
pouze kreslit cary ruznymi barvami a ruznym zpusobem je
kombinovat s jiz nakreslenymi objekty. Knihovna je ve tvaru
unity pro Turbo Pascal, coz je pro vetsinu z vas asi
nejdostupnejsi a nejsrozumitelnejsi programovaci jazyk. Pro
dosazeni vysoke rychlosti jsou jeji klicove casti napsane
v assembleru.

Unit SGraph;

interface

Const
  { Konstanty pro typ kresleni }
  NormalPut = 0;              { Prepisovani }
  ANDPut    = 8;              { Logicke AND }
  ORPut     = 16;             { Logicke OR }
  XORPut    = 24;             { Logicke XOR }

{ Hlavicky exportovanych procedur }
procedure Line( X1, Y1, X2, Y2: word);  { Kresleni cary }
procedure InitGraph;                    { Inicializace grafiky }
procedure CloseGraph;                   { Obnoveni puvodniho videomodu }
procedure SetColor( Color: byte);       { Nastaveni barvy pro kresleni car }
procedure SetWriteMode( Mode: byte);    { Volba druhu kombinace kreslenych a
                                          jiz zobrazenych dat }

implementation

Const
  ActColor: byte = 15;          { Promena slouzici k ulozeni aktualni barvy
                                  Standardne je nastavena bila barva }
  WriteMode: byte = 0;          { Zapisovaci rezim 0 = prepisovani
                                                   8 = AND
                                                  16 = OR
                                                  24 = XOR          }
  BytesPerRow = 80;             { Pocet byte v jedne radce (640/8 = 80) }
  IsGraphicsMode: boolean
                  = false;      { Indikace zda jsme v grafice }
  PixelsPerByte      = 3;            { Pocet bodu v jednom bite udany
                                       v bitovych posuvech }
  ModMask            = 7;            { Bitova maska pro ziskani MOD 8 (zbytku
                                       po deleni osmi }
  BMask              = 128;          { Bitova maska pro kresleni bodu }
  VRAMSegment        = $a000;        { Segment pocatku obrazove pameti }
  GraphicsAR         = $3ce;         { Port Graphics 1 and 2 Address Register }
  ModeRegister       = 5;            { Index registru zapisovaciho modu }
  DataRotate         = 3;            { Index registru pro rotaci a kombinaci dat }
  BitMask            = 8;            { Index registru bitove masky }

var
   OldVMode: byte;                   { Promena slouzici pro uchovani cisla
                                       zobrazovaciho rezimu }
   delta: word;                      { Pomocne promene pro uchovani 2dx nebo 2dy}

procedure Line( X1, Y1, X2, Y2: word); assembler;
{ Kresli caru z X1, Y1 do X2, Y2 }
const
     neg_dx     = 2;            { Znamenko od delta x }
     neg_dy     = 4;            { Znamenko od delta y }

asm
        push    BP              { Uchovani registru BP }
        push    DS              {                 a DS }

        mov     AX, X1          { Nacteni souradnic do registru procesoru }
        mov     BX, Y1
        mov     CX, X2
        mov     DX, Y2

        xor     BP, BP          { Smazani registru BP }
        mov     DI, CX          { Spocitani hodnoty deltax }
        sub     DI, AX
        jg      @p_dx           { Je deltax kladne ? }
        or      BP, neg_dx      { ne => uloz znamenko do BP }
        neg     DI              {       absolutni hodnota DI tj. deltax }

  @p_dx:
        mov     SI, DX          { Urceni hodnoty deltay }
        sub     SI, BX
        jg      @p_dy           { Je deltay kladne ? }
        or      BP, neg_dy      { ne => uloz znamenko do BP }
        neg     SI              {       absolutni hodnota delaty }

  @p_dy:
        cmp     DI, SI          { Porovnani deltax a deltay }
        jb      @vertical_dir   { deltax > deltay <=> absolutni hodnota smernice > 1
                                                          => vertikalni smer
                                  deltax <= deltay <=> absolutni hodnota smernice <= 1
                                                          => horizontalni smer          }

  @horizontal_dir:
        test    BP, neg_dx      { Porovnani X1 a X2 }
        jz      @hor_init       { Je smer zprava doleva ? }
        xchg    AX, CX          { ne => vymen souradnice pocatku a konce cary }
        xchg    BX, DX
        xor     BP, neg_dy      { uprav znamenko deltay }

  @hor_init:                    { Vypocet adresy prvniho bodu }
        mov     CX, AX          { Do CX X1 }
        xchg    AX, BX          { BX = X1, AX = X2 }
        mov     DX, BytesPerRow        { DX = Pocet byte na jedne radce (80) }
        mul     DX                     { AX:DX = Y1 * 80 }
        shr     BX, PixelsPerByte      { BX = BX div 8 }
        add     BX, AX                 { V BX je offset prvniho bodu cary }

        mov     AX, DS                 { Do ES presuneme DS }
        mov     ES, AX

        mov     AX, VRAMSegment        { Segment obrazove pameti do DS }
        mov     DS, AX
        mov     DX, GraphicsAR            { Port adresoveho registru grafickeho kontroleru }
        mov     AX, ModeRegister + $0200  { Nastavi zapisovaci mod 2 }
        out     DX, AX

        mov     AL, DataRotate            { Nastavi zpusob kombinace dat z CPU }
        mov     AH, ES: WriteMode         { s latch-registry }
        out     DX, AX

        and     CX, ModMask             { CX = X1 and 7 }
        mov     AH, BMask               { AH = 80h, tj. nastaven je levy pixel }
        shr     AH, CL                  { Rotace AH. Po rotaci je v AH bitova maska bodu }

        mov     DX, BP                  { Uchova znamenka deltax a deltay v DX }

        mov     CX, DI                  { CX = deltax }
        inc     CX                      { CX = deltax - 1, tj. pocet bodu na care }
                                        { Vypocet predikce do BP: }
        shl     SI, 1                   { SI = 2*deltay }
        mov     BP, SI                  { BP = 2*deltay }
        sub     BP, DI                  { BP = 2*deltax - deltay }
        shl     DI, 1                   { DI = 2*deltax }

        mov     ES: delta, DI           { Ulozeni hodnoty 2*deltax }

        mov     DI, BX                  { Do DI offset adresy 1. bodu }

        test    DX, neg_dy              { Urceni smeru }
        pushf                           { Uchovani flagu }
        mov     DX, GraphicsAR          { Nastavi index graf. kontroleru na Bit mask reg. }
        mov     AL, BitMask
        out     DX, AL
        inc     DX                      { DX = datovy port graf. kontroleru }
        popf                            { Obnovi flagy s vysledkem testu smeru }
        jnz     @hor_neg_dy_init        { Rozskok, podle znamenka smernice }

  @hor_pos_dy_init:                     { Kladna smernice, y souradnice je zvysovana }
        mov     BL, ES: ActColor        { BL = Barva cary }
        mov     BH, BL                  { BH = Barva cary }
        mov     AL, AH                  { AL = Bitova maska prvniho bodu }

  @hor_pos_dy:
        cmp      BP, 0                  { Test predikce }
        jng      @hor_pos_dy_L1
        out      DX, AL                 { predikce >= 0 }
        mov      BL, BH                 { BL = Barva bodu }
        xchg     [DI], BL               { Nakresli bod }
        xor      AL, AL                 { Vynuluje stradanou masku }
        sub      BP, ES: delta          { Upravi predikaci P = P - 2*deltax }
        add      DI, BytesPerRow        { Zvetseni y souradnice }
  @hor_pos_dy_L1:
        add      BP, SI                 { Uprava predikce P = P + 2*deltay }
        ror      AH, 1                  { Zvetseni x souradnice }
        jc       @hor_pos_dy_L2         { Presahli jsme jeden byte ? }
        or       AL, AH                 { Uprav masku byte }
        loop     @hor_pos_dy            { Dalsi bod }
        jmp      @hor_pos_dy_lastbyte   { Dosli jsme do koncoveho bodu }
  @hor_pos_dy_L2:
        out      DX, AL                 { Nastavi novou bitovou masku }
        mov      BL, BH
        xchg     [DI], BL               { Nakresli body jednoho byte }
        mov      AL, AH                 { Inicializuje bitovou masku }
        inc      DI                     { Zvetsi x souradnici }
        loop     @hor_pos_dy            { Byl to posledni bod ? }
        jmp      @l_done                {        => konec kresleni cary }
  @hor_pos_dy_lastbyte:
        xor      AL, AH                 { Posledni bit je neplatny, odstranit }
        out      DX, AL                 { Nastaveni bitove masky }
        mov      BL, BH                 { BL = Barva cary }
        xchg     [DI], BL               { Nakresleni bodu }
        jmp      @l_done                { Ukonceni kresleni cary }

  @hor_neg_dy_init:                     { Zaporna smernice, y souradnice je zmensovana }
        mov     BL, ES: ActColor        { BL = Barva cary }
        mov     BH, BL                  { BH = Barva cary }
        mov     AL, AH                  { AL = Bitova maska prvniho bodu }

  @hor_neg_dy:
        cmp      BP, 0                  { Test predikce }
        jng      @hor_neg_dy_L1
        out      DX, AL                 { predikce >= 0 }
        mov      BL, BH                 { BL = Barva bodu }
        xchg     [DI], BL               { Nakresli bod }
        xor      AL, AL                 { Vynuluje stradanou masku }
        sub      BP, ES: delta          { Upravi predikaci P = P - 2*deltax }
        sub      DI, BytesPerRow        { Zmenseni y souradnice }
  @hor_neg_dy_L1:
        add      BP, SI                 { Uprava predikce P = P + 2*deltay }
        ror      AH, 1                  { Zvetseni x souradnice }
        jc       @hor_neg_dy_L2         { Presahli jsme jeden byte ? }
        or       AL, AH                 { Uprav masku byte }
        loop     @hor_neg_dy            { Dalsi bod }
        jmp      @hor_neg_dy_lastbyte   { Dosli jsme do koncoveho bodu }
  @hor_neg_dy_L2:
        out      DX, AL                 { Nastavi novou bitovou masku }
        mov      BL, BH
        xchg     [DI], BL               { Nakresli body jednoho byte }
        mov      AL, AH                 { Inicializuje bitovou masku }
        inc      DI                     { Zvetsi x souradnici }
        loop     @hor_neg_dy            { Byl to posledni bod ? }
        jmp      @l_done                {        => konec kresleni cary }
  @hor_neg_dy_lastbyte:
        xor      AL, AH                 { Posledni bit je neplatny, odstranit }
        out      DX, AL                 { Nastaveni bitove masky }
        mov      BL, BH                 { BL = Barva cary }
        xchg     [DI], BL               { Nakresleni bodu }
        jmp      @l_done                { Ukonceni kresleni cary }


  @vertical_dir:                        { Vypocet adresy 1. bodu }
        test    BP, neg_dy              { Kreslime shora dolu ? }
        jz      @vert_init
        xchg    AX, CX                  { Prohozeni souradnic }
        xchg    BX, DX
        xor     BP, neg_dx              { Uprava znamenka }

  @vert_init:
        mov     CX, AX                  { CX = X1 }
        xchg    AX, BX                  { AX = Y1, BX = X1 }
        mov     DX, BytesPerRow         { DX = Pocet byte na radku (80) }
        mul     DX                      { AX:DX = Y1 * 80 }
        shr     BX, PixelsPerByte       { BX = X1 div 8 }
        add     BX, AX                  { BX = Offset adresy 1. bodu }

        mov     AX, DS                  { Ulozi datovy segment do ES }
        mov     ES, AX

        mov     AX, VRAMSegment         { DS = Segment obrazove pameti }
        mov     DS, AX
        mov     DX, GraphicsAR             { Port adresoveho registru graf. kontroleru }
        mov     AX, ModeRegister + $0200   { Nastavi zapisovaci mod 2 }
        out     DX, AX

        mov     AL, DataRotate          { Nastaveni zpusobu kombinovani dat z CPU }
        mov     AH, ES: WriteMode       { s latch-registry                        }
        out     DX, AX

        and     CX, ModMask             { CX = X1 and 7 }
        mov     AH, BMask               { Bitova maska pro levy bod (80h) }
        shr     AH, CL                  { Rotaci vytvori spravnou masku }

        mov     DX, BP                  { Ulozi znamenka do DX }

        mov     CX, SI                  { CX = Delka cary }
        inc     CX
                                        { Urceni predikace }
        shl     DI, 1                   { DI = 2*deltax }
        add     BP, DI                  { BP = 2*deltax }
        sub     BP, SI                  { BP = 2*deltax - deltay }
        shl     SI, 1                   { SI = 2*deltay }

        mov     ES: delta, SI           { ulozi 2*deltay }
        mov     SI, DI                  { SI = 2*deltax }

        mov     DI, BX                  { DI = Offset adresy 1. bodu }

        test    DX, neg_dx              { Zjisteni smeru }
        mov     DX, GraphicsAR          { DX = adresovy port graf. kontroleru }
        jnz     @vert_neg_dx_init       { Rozskok podle znamenka smernice }

  @vert_pos_dx_init:                    { Kladna smernice }
        mov     BL, ES: ActColor        { BL = Barva cary }
        mov     BH, BL                  { BH = Barva cary }
        mov     AL, BitMask             { Index Bit mask registru }
        out     DX, AX
        inc     DX                      { DX = datovy registr graf. kontroleru }
        mov     AL, AH                  { AL = Bitova maska }

  @vert_pos_dx:
        mov     BL, BH                  { BL = Barva cary }
        xchg    [DI], BL                { Nakresli bod }
        cmp     BP, 0                   { Test predikce }
        jng     @vert_pos_dx_L1
        sub     BP, ES: delta           { P >= 0, P = P - 2*deltay }
        ror     AL, 1                   { Zvys souradnici x }
        adc     DI, 0                   { Presun do dalsiho byte }
        out     DX, AL                  { Nastav novou bitovou masku }

  @vert_pos_dx_L1:
        add     BP, SI                  { P = P + 2*deltax }
        add     DI, BytesPerRow         { Zvys souradnici y }
        loop    @vert_pos_dx            { Dalsi bod }
        jmp     @l_done                 { Konec cary }

  @vert_neg_dx_init:                    { Zaporna smernice }
        mov     BL, ES: ActColor        { BL = Barva cary }
        mov     BH, BL                  { BH = Barva cary }
        mov     AL, BitMask             { Index Bit mask registru }
        out     DX, AX
        inc     DX                      { DX = datovy registr graf. kontroleru }
        mov     AL, AH                  { AL = Bitova maska }

  @vert_neg_dx:
        mov     BL, BH                  { BL = Barva cary }
        xchg    [DI], BL                { Nakresli bod }
        cmp     BP, 0                   { Test predikce }
        jng     @vert_neg_dx_L1
        sub     BP, ES: delta           { P >= 0, P = P - 2*deltay }
        rol     AL, 1                   { Zmensi souradnici x }
        sbb     DI, 0                   { Presun do dalsiho byte }
        out     DX, AL                  { Nastav novou bitovou masku }

  @vert_neg_dx_L1:
        add     BP, SI                  { P = P + 2*deltax }
        add     DI, BytesPerRow         { Zvys souradnici y }
        loop    @vert_neg_dx            { Dalsi bod }
        jmp     @l_done                 { Konec cary }

  @l_done:
        mov     DX, GraphicsAR          { Smazani bitove masky }
        mov     AX, BitMask
        out     DX, AX
        mov     AX, ModeRegister
        out     DX, AX                  { Nastavi zapisovaci mod 0 }
        mov     AX, DataRotate
        out     DX, AX                  { Vypne rotyci dat, standardni kombinovani dat }

  @exit:
        pop      DS                     { Obnovi obsah registru DS }
        pop      BP                     {                       BP }
end;

procedure SetMode( Mode: byte); assembler;
{ Nastavi aktualni zobrazovaci rezim pomoci sluzby 00h BIOS }
asm
        mov     AH, 00h
        mov     AL, Mode
        int     10h
end;
function GetMode: byte; assembler;
{ Sluzbou BIOS zjisti zobrazovaci rezim }
asm
        mov     AH, 0fh
        int     10h
end;

procedure InitGraph;
{ Inicializace grafiky }
begin
  if not IsGraphicsMode then
    OldVMode := GetMode;        { Ulozi cislo aktivniho zobrazovaciho rezimu }
  SetMode( $12);                { Nastavi zobrazovaci rezim 640 x 480, 16 barev }
  IsGraphicsMode := True;       { Nastavi priznak prepnuti do grafiky }
end;

procedure CloseGraph;
{ Ukonci graficky rezim }
begin
  if IsGraphicsMode then SetMode( OldVMode);    { Obnovi puvodni zobr. rezim }
  IsGraphicsMode := False;
end;

procedure SetColor( Color: byte);
{ Nastavi barvu pouzitou pro kresleni }
begin
  ActColor := Color;
end;

procedure SetWriteMode( Mode: byte);
{ Nastavi zapisovaci rezim pro kresleni }
begin
  WriteMode := Mode;
end;

begin
end.


Pouziti graficke knihovny demonstruje nasledujici kratky
program, ktery navic vyuziva nektere z registru karty VGA
k snadne implementaci vertikalniho scrolovani dvema smery
zaroven. Vsimnete si, ze scrolovani bude stejne rychle na
vsech pocitacich, bez ohledu na jejich rychlost. Je to
zpusobeno tim, ze jednotlive animacni kroky jsou oddeleny
cekanim na vertikalni zpetny chod, ktery je v tomto
zobrazovacim rezimu generovan s frekvenci priblizne 60 Hz.
Program pro svoji spravnou cinnost potrebuje kartu VGA.


Uses
    Sgraph,                     { Pouzva jednotku SGraph }
    Crt;                        {               a Crt    }

procedure SetStartAdr( Adr: word); assembler;
{ Procedura nastavujici registry CRTC pocatecni adresa index 0ch a 0dh }
asm
        mov     DX, 3dah         { Vstupni stavovy registr 1 }
  @wait_retrace:
        in      AL, DX
        and     AL, 8
        jz      @wait_retrace    { Cekani na vertikalni zpetny chod }
  @wait_display:
        in      AL, DX
        and     AL, 8
        jnz     @wait_display    { Cekani na aktivni zobrazovaci signal }

        mov     cx, Adr
        mov     dx, 03d4h       { CRTC adresovy registr }
        mov     al, 0ch         { index pocatecni adresa - vyssi byte }
        mov     ah, ch
        out     dx, ax
        inc     al              { index pocatecni adresa - nizsi byte }
        mov     ah, cl          { nizsi byte adresy }
        out     dx, ax
end;

procedure SetLineComp( Line: word); assembler;
{ Nastavi registr CRTC porovnani radky vcetne 9. a 10. bitu }
asm
        mov     DX, 03d4h       { CRTC adresovy registr }
        mov     AL, 18h         { index registru porovnani radky }
        out     DX, AL
        mov     CX, Line
        mov     AL, CL
        inc     DX
        out     DX, AL          { zapis 8 nizsich byte cisla radky }
        dec     DX
        mov     AL, 07h         { index registru preteceni }
        out     DX, AL
        inc     DX
        in      AL, DX          { cte nastaveni registru preteceni }
        mov     CL, CH
        and     CL, 1
        shl     CL, 4
        and     AL, 11101111b   { smaze 9. bit cisla radky pro porovnani }
        or      AL, CL          { nastavi 9. bit }
        out     DX, AL          { zapise registr }
        dec     DX
        mov     AL, 09h         { registr pocet radek na znak - obsahuje 10. bit }
        out     DX, AL
        inc     DX
        in      AL, DX
        shr     CH, 1
        and     CH, 1
        shl     CH, 6
        and     AL, 10111111b   { smazani stare hodnoty 10. bitu }
        or      AL, CH          { nova hodnota 10. bitu }
        out     DX, AL          { zapsani registru }
end;

const
     MaxX = 639;                { Maximalni hodnota souradnice x }
     MaxY = 479;                { Maximalni hodnota souradnice y }

var
   i: word;                     { Pomocne promenne }
   x: word;

begin
  InitGraph;                    { Inicializace grafiky }
  SetWriteMode( XORPut);        { Nastaveni zapisovaciho zpusobu }

  { Nakresleni jednoducheho obrazce pres celou obrazovku }
  for x := 0 to MaxX do
    Line( MaxX div 2, MaxY div 2, x, 0);
  for x := 0 to MaxY do
    Line( MaxX div 2, MaxY div 2, MaxX, x);
  for x := MaxX downto 0 do
    Line( MaxX div 2, MaxY div 2, x, MaxY);
  for x := MaxY downto 0 do
    Line( MaxX div 2, MaxY div 2, 0, x);

  { Jednoducha animace }
  for i := 0 to MaxY div 2 do
  begin
    SetLineComp( i);                    { Scrolovani dolu pomoci porovnani radky }
    SetStartAdr( i* 80);                { Zaroven scrolovani nahoru pomoci pocatecni adresy }
  end;

  { Obraceny smer animace }
  for i := MaxY div 2 downto 0 do
  begin
    SetLineComp( i);
    SetStartAdr( i* 80);
  end;

  Delay( 300);                  { Mala pauza nakonec }
  SetLineComp( 1023);           { Nastaveni standardni hodnoty }
  CloseGraph;                   { Ukonceni grafiky, predchozi zobr. rezim }
end.


Literatura:

[1]  Kliewer, B.D. - EGA/VGA A programmer's reference guide
     371 stran, McGraw-Hill Publishing Company, 1990

[2]  Zara, J. - Pocitacova grafika - principy a algoritmy
     472 stran, Grada a.s., 1992

[3]  Brown, R. - Interrupt List - freewarova el. prirucka
     1993


Copyright © Jiri Kosek