   #Start Contents

                  Rysowanie w trybie graficznym pod Linuksem

   W Linuksie oczywiscie nie mozna rysowac bezposrednio, czyli
   programowac karte graficzna lub zapisywac do pamieci ekranu (dostepnej
   na przyklad w DOSie pod segmentem A000h). Zamiast tego, wiekszosc
   roboty wykonaja za nas biblioteki oraz moduly jadra, odpowiedzialne za
   urzadzenia (jesli programujemy na przyklad framebuffer). W tym
   artykule wykorzystam mozliwosci biblioteki SVGAlib, ze wzgledu na
   prostote jej opanowania, oraz z biblioteki Allegro ze wzgledu na jej
   wieloplatformowosc, latwosc uzywania i mozliwosc zapisania obrazow do
   pliku.
     _________________________________________________________________

SVGAlib

   (przeskocz do SVGAlib z mapowaniem pamieci)

   Aby moc korzystac z SVGAlib, musicie zainstalowac pakiety svgalib oraz
   svgalib-devel lub po prostu samemu skompilowac biblioteke, jesli
   pakiety nie sa dostepne.

   Bedziemy sie zajmowac dwoma trybami (ale nic nie stoi na przeszkodzie,
   aby skorzystac z dowolnego innego). Beda to: tryb 320x200 w 256
   kolorach i oczywiscie tryb tekstowy (ten, do ktorego wrocimy po
   zakonczeniu programu). Do ustawienia biezacego trybu sluzy funkcja
   vga_setmode. Przyjmuje ona jeden argument - numer trybu (G320x200x256
   = 5, TEXT = 0).

   Do zmiany biezacego koloru sluzy funkcja vga_setcolor. Jedynym jej
   argumentem jest numer koloru (na przyklad 1-niebieski, 2-zielony,
   3-jasnoniebieski, 4-czerwony, 5-fioletowy, 6-brazowy, 7-bialy).

   Do narysowania pojedynczego piksela sluzy funkcja vga_drawpixel.
   Przyjmuje ona dwa argumenty. Od lewej (ostatni wkladany na stos) sa
   to: wspolrzedna X oraz wspolrzedna Y punktu do zapalenia. Punkt o
   wspolrzednych (0,0) to lewy gorny rog ekranu.

   Wspolrzedna X rosnie w prawo, a Y - w dol ekranu.

   Do narysowania linii sluzy funkcja vga_drawline. Przyjmuje ona 4
   argumenty. Od lewej (ostatni wkladany na stos) sa to: wspolrzedna X
   poczatku linii, wspolrzedna Y poczatku linii, wspolrzedna X konca
   linii, wspolrzedna Y konca linii.

   Aby nasz rysunek byl widoczny choc przez chwile, skorzystamy z funkcji
   systemowej sys_nanosleep, podajac jej adres struktury timespec
   mowiacej, jak dluga przerwe chcemy. Wiecej szczegolow w innych
   artykulach oraz w opisie przerwania int 80h.

   Do dzialania programow pod X-ami potrzebne moga byc uprawnienia do
   pliku /dev/console a pod konsola tekstowa - do pliku /dev/mem.

   Jak widac, teoria nie jest trudna, wiec przejdzmy od razu do
   przykladowych programow.

   Pierwszy z nich ma zaprezentowac rysowanie pojedynczego piksela oraz
   dowolnych linii. Zwroccie uwage na sposob kompilacji. Korzystamy z
   bibliotek dostepnych dla programistow jezyka C, wiec do laczenia
   programu w calosc najlepiej uzyc GCC - zajmie sie on dolaczeniem
   wszystkich niezbednych bibliotek. A skoro uzywamy gcc, to funkcja
   glowna zamiast _start, musi sie nazywac main - tak samo jak funkcja
   glowna w programach napisanych w C. I tak samo, zamiast funkcji
   wychodzenia z programu, mozemy uzyc komendy RET, aby zamknac program.
   (przeskocz program rysujacy linie)
; Program do rysowania linii dowolnej z wykorzystaniem SVGAlib
;
; Autor: Bogdan D., bogdandr (at) op.pl
;
; kompilacja:
; nasm -O999 -f elf -o graf1.o graf1.asm
; gcc -o graf1 graf1.o -lvga


section .text
global  main

; reszta trybow dostepna w /usr/include/vga.h (wymagany svgalib-devel)
%define TEXT         0
%define G320x200x256 5

; deklaracje funkcji zewnetrznych:

extern  vga_setmode
extern  vga_setcolor
extern  vga_drawline
extern  vga_drawpixel

main:
        push    dword G320x200x256
        call    vga_setmode             ; ustawiamy tryb graficzny:
                                        ; 320x200 w 256 kolorach
        add     esp, 4                  ; zdejmujemy argument ze stosu

        push    dword 5                 ; ustawiamy kolor (5=fioletowy)
        call    vga_setcolor
        add     esp, 4

        push    dword 100               ; wspolrzedna y punktu
        push    dword 160               ; wspolrzedna x punktu
        call    vga_drawpixel           ; rysujemy piksel
        add     esp, 8

        push    dword 6                 ; ustawiamy kolor (6=brazowy)
        call    vga_setcolor
        add     esp, 4

        push    dword 160
        push    dword 320
        push    dword 0
        push    dword 0
        call    vga_drawline            ; linia od lewego gornego
                                        ; naroznika do srodka prawego boku
        add     esp, 16

        push    dword 7                 ; ustawiamy kolor (7=bialy)
        call    vga_setcolor
        add     esp, 4

        push    dword 10
        push    dword 20
        push    dword 110
        push    dword 50
        call    vga_drawline
        add     esp, 16

        mov     dword [t1+timespec.tv_nsec], 0
        mov     dword [t1+timespec.tv_sec], 5           ; czekaj 5 sekund

        mov     eax, 162                ; sys_nanosleep
        mov     ebx, t1                 ; adres struktury mowiacej,
                                        ; ile chcemy czekac
        mov     ecx, 0
        int     80h                     ; robimy przerwe...

        push    dword TEXT
        call    vga_setmode             ; ustawiamy tryb tekstowy 80x25
        add     esp, 4

        xor     eax, eax                ; zerowy kod zakonczenia (bez bledu)
        ret                             ; powrot z funkcji main i
                                        ; zakonczenie programu

section .data


struc timespec
        .tv_sec:        resd 1
        .tv_nsec:       resd 1
endstruc

t1              istruc timespec

   Drugi program rysuje okrag. Srodek tego okregu jest w srodku ekranu,
   kolejne punkty (lacznie bedzie ich 360) obliczam nastepujaco:
   wspolrzedna x = wspolrzedna x srodka + r*cos(t), y = y_srodka +
   r*sin(t), po przerobieniu kata t na radiany. Do liczenia tych sinusow
   i kosinusow wykorzystuje FPU.
   (przeskocz program rysujacy okrag)
; Program do rysowania okregu z wykorzystaniem SVGAlib
;
; Autor: Bogdan D., bogdandr (at) op.pl
;
; kompilacja:
; nasm -O999 -f elf -o kolo_linux.o kolo_linux.asm
; gcc -o kolo_linux kolo_linux.o -lvga


section .text
global  main

; reszta trybow dostepna w /usr/include/vga.h (wymagany svgalib-devel)
%define TEXT         0
%define G320x200x256 5

extern  vga_setmode
extern  vga_setcolor
extern  vga_drawpixel

main:
        push    dword G320x200x256
        call    vga_setmode             ; ustawiamy tryb graficzny:
                                        ; 320x200 w 256 kolorach
        add     esp, 4                  ; zdejmujemy argument ze stosu

        push    dword 2                 ; ustawiamy kolor
        call    vga_setcolor
        add     esp, 4

        mov     ebx, 360

        finit                           ; ponizej bede zapisywal stan
                                        ; rejestrow FPU, od st0 do st7
        fldpi                           ; pi
        fild    word [sto80]            ; 180, pi

        fdivp   st1, st0                ; pi/180

        fld1                            ; 1, pi/180
        fild    word [r]                ; r, 1, pi/180
        fldz                            ; kat=0, r, 1, pi/180

rysuj:
        fld     st0                     ; kat, kat, r, 1, pi/180

        fmul    st4                     ; kat w radianach

        fsin                            ; sin(kat), kat, r, 1, pi/180
        fmul    st2                     ; sin(kat)*r, kat, r, 1, pi/180

        fistp   dword [wys]             ; kat, r, 1, pi/180

        fld     st0                     ; kat, kat, r, 1, pi/180
        fmul    st4                     ; kat w radianach
        fcos                            ; cos(kat), kat, r, 1, pi/180
        fmul    st2                     ; r*cos(kat), kat, r, 1, pi/180

        fistp   dword [szer]            ; kat, r, 1, pi/180

        mov     eax, [wys]
        mov     edx, [szer]
        add     eax, 100                ; dodajemy wspolrzedna y srodka
        add     edx, 160                ; dodajemy wspolrzedna x srodka
        push    eax                     ; umieszczamy wspolrzedne na stosie
        push    edx
        call    vga_drawpixel           ; rysujemy piksel
        add     esp, 8

        fadd    st0, st2                ; kat = kat + 1

        dec     ebx
        jnz     rysuj

        mov     dword [t1+timespec.tv_nsec], 0
        mov     dword [t1+timespec.tv_sec], 5           ; 5 sekund

        mov     eax, 162                ; sys_nanosleep
        mov     ebx, t1                 ; adres struktury mowiacej,
                                        ; ile chcemy czekac
        mov     ecx, 0
        int     80h                     ; robimy przerwe...

        push    dword TEXT
        call    vga_setmode             ; ustawiamy tryb tekstowy 80x25
        add     esp, 4

        xor     eax, eax                ; zerowy kod zakonczenia (bez bledu)
        ret                             ; powrot z funkcji main

section .data


struc timespec
        .tv_sec:        resd 1
        .tv_nsec:       resd 1
endstruc

t1              istruc timespec

r               dw      50              ; promien okregu
szer            dd      0
wys             dd      0
sto80           dw      180
     _________________________________________________________________

SVGAlib z mapowaniem pamieci

   (przeskocz do Allegro)

   Aby moc korzystac z SVGAlib, musicie zainstalowac pakiety svgalib oraz
   svgalib-devel lub po prostu samemu skompilowac biblioteke, jesli
   pakiety nie sa dostepne.

   UWAGA: zmieni sie sposob kompilacji programu w stosunku do
   tradycyjnych, asemblerowych programow. Korzystamy z bibliotek
   dostepnych dla programistow jezyka C, wiec do laczenia programu w
   calosc najlepiej uzyc GCC - zajmie sie on dolaczeniem wszystkich
   niezbednych bibliotek. A skoro uzywamy gcc, to funkcja glowna zamiast
   _start, musi sie nazywac main - tak samo jak funkcja glowna w
   programach napisanych w C. I tak samo, zamiast funkcji wychodzenia z
   programu, mozemy uzyc komendy RET, aby zamknac program. Sama
   kompilacja przebiega nastepujaco:
        nasm -O999 -f elf -o graf2.o graf2.asm
        gcc -o graf2 graf2.o -lvga

   Bedziemy sie zajmowac dwoma trybami (ale nic nie stoi na przeszkodzie,
   aby skorzystac z dowolnego innego). Beda to: tryb 320x200 w 256
   kolorach i oczywiscie tryb tekstowy (ten, do ktorego wrocimy po
   zakonczeniu programu). Do ustawienia biezacego trybu sluzy funkcja
   vga_setmode. Przyjmuje ona jeden argument - numer trybu (G320x200x256
   = 5, TEXT = 0).

   Przed rozpoczeciem pracy ustawiamy tryb graficzny 320x200, wykonujac
        extern  vga_setmode
                ...
                push    dword 5                 ; G320x200x256
                call    vga_setmode             ; ustawiamy tryb graficzny:
                                                ; 320x200 w 256 kolorach
                add     esp, 4                  ; zdejmujemy argument ze stosu

   Ale teraz zajmiemy sie rysowaniem bez funkcji SVGAlib, poprzez zapis
   do odpowiednich komorek pamieci. Pamiec trybow graficznych znajduje
   sie w segmencie A000, co odpowiada liniowemu adresowi A0000, liczac od
   adresu 0. Oczywiscie system, ze wzgledow bezpieczenstwa, nie pozwoli
   nam bezposrednio pisac pod ten adres, wiec musimy sobie poradzic w
   inny sposob. Sposob ten polega na otwarciu specjalnego pliku
   urzadzenia, ktory symbolizuje cala pamiec w komputerze - /dev/mem. Na
   wiekszosci systemow otwarcie tego pliku wymaga uprawnien
   administratora.

   Po otwarciu pliku mamy dwie mozliwosci. Pierwsza to poruszac sie po
   nim funkcjami do zmiany pozycji w pliku, oraz odczytywac i zapisywac
   funkcjami odczytu i zapisu danych z i do pliku. Moze to byc powolne,
   ale sposob jest. Druga mozliwosc to zmapowac plik do pamieci, po czym
   korzystac z niego jak ze zwyklej tablicy. Te mozliwosc opisze teraz
   szczegolowo.

   Otwieranie pliku odbywa sie za pomoca tradycyjnego wywolania:
        mov     eax, 5          ; sys_open
        mov     ebx, pamiec     ; adres nazwy pliku "/dev/mem", 0
        mov     ecx, 2          ; O_RDWR, zapis i odczyt
        mov     edx, 666o       ; pelne prawa
        int     80h
        ...
        pamiec          db      "/dev/mem", 0

   Drugim krokiem jest zmapowanie naszego otwartego pliku do pamieci.
   Odbywa sie to za pomoca funkcji systemowej sys_mmap2. Przyjmuje ona 6
   argumentow:
    1. EBX = adres, pod jaki chcielibysmy zmapowac plik. Najlepiej podac
       zero, wtedy system sam wybierze dogodny adres
    2. ECX = dlugosc mapowanego obszaru pliku, w bajtach. Podamy to
       100000h, by na pewno objac obszar zaczynajacy sie A0000 i dlugosci
       64000 bajtow (tyle, ile trzeba na jeden ekran w trybie 320x200)
    3. EDX = tryb dostepu do zmapowanej pamieci. Jesli chcemy odczyt i
       zapis, podamy tutaj PROT_READ=1 + PROT_WRITE=2
    4. ESI = tryb wspoldzielenia zmapowanej pamieci. Podamy tu
       MAP_SHARED=1 (wspoldzielona, nie prywatna)
    5. EDI = deskryptor otwartego pliku, ktory chcemy zmapowac
    6. EBP = adres poczatkowy w pliku, od ktorego mapowac. Adres ten jest
       podawany w jednostkach strony systemowej, ktorej wielkosc moze byc
       rozna na roznych systemach. Najlatwiej podac tu zero, a do adresow
       dodawac potem A0000

   Po pomyslnym wykonaniu, system zwroci nam w EAX adres zmapowanego
   obszaru pamieci, ktorego mozemy uzywac (w przypadku bledu otrzymujemy
   wartosc od -4096 do -1 wlacznie). Przykladowe wywolanie wyglada wiec
   tak:
        mov     eax, 192                ; sys_mmap2
        xor     ebx, ebx                ; jadro wybierze adres
        mov     ecx, 100000h            ; dlugosc mapowanego obszaru
        mov     edx, 3                  ; PROT_READ | PROT_WRITE, mozliwosc
                                        ; zapisu i odczytu
        mov     esi, 1                  ; MAP_SHARED - tryb wspoldzielenia
        mov     edi, [deskryptor]       ; deskryptor pliku pamieci, otrzymany
                                        ; z sys_open w poprzednim kroku
        mov     ebp, 0                  ; adres poczatkowy w pliku
        int     80h

   Teraz wystarczy juz korzystac z otrzymanego wskaznika, na przyklad:
        mov     byte [eax+0a0000h], 7

   Kolejne adresy w pamieci oznaczaja kolejne piksele okreslonego
   wiersza. Po przekroczeniu 320 bajtow, kolejny bajt oznacza pierwszy
   piksel kolejnego wiersza i tak dalej.

   Bajty zapisywane w pamieci (czyli kolory pikseli) maja takie same
   wartosci, jak w tradycyjnym podejsciu: 1-niebieski, 2-zielony,
   3-jasnoniebieski, 4-czerwony, 5-fioletowy, 6-brazowy, 7-bialy.

   Zmiany, ktore zapiszemy w pamieci, moga jednak nie od razu pojawic sie
   w pliku (czyli na ekranie w tym przypadku). Aby wymusic fizyczny zapis
   danych, korzysta sie z funkcji sys_msync. Przyjmuje ona 3 argumenty:
    1. EBX = adres poczatku danych do synchronizacji
    2. ECX = liczba bajtow do zsynchronizowania
    3. EDX = 0 lub zORowane flagi: MS_ASYNC=1 (wykonaj asynchronicznie),
       MS_INVALIDATE=2 (uniewaznij obszar po zapisaniu), MS_SYNC (wykonaj
       synchronicznie)

   Przykladowe wywolanie wyglada wiec tak:
        mov     eax, 144                ; sys_msync
        mov     ebx, 0a0000h            ; adres startowy
        mov     ecx, 4000               ; ile zsynchronizowac
        mov     edx, 0                  ; flagi
        int     80h

   Po zakonczeniu pracy nalezy przywrocic tryb tekstowy:
        push    dword 0                 ; TEXT
        call    vga_setmode             ; ustawiamy tryb tekstowy 80x25
        add     esp, 4

   oraz odmapowac plik:
        mov     eax, 91                 ; sys_munmap
        mov     ebx, [wskaznik]         ; wskaznik otrzymany z sys_mmap2
        mov     ecx, 100000h            ; liczba bajtow
        int     80h

   i zamknac go:
        mov     eax, 6                  ; sys_close
        mov     ebx, [deskryptor]       ; deskryptor pliku "/dev/mem"
        int     80h

   Jesli Wasza grafika ma czesto sie zmieniac (na przyklad jest to
   animacja), to pisanie bezposrednio do zmapowanej pamieci (lub pamieci
   wideo, jesli macie dostep) moze byc zbyt powolne, by efekty graficzne
   byly zadowalajace. Ale mozna to obejsc na dwa sposoby: uruchamiac
   sys_msync dopiero po zapelnieniu calego ekranu lub caly ekran najpierw
   zbudowac sobie w osobnym buforze, po czym jednym ruchem wrzucic caly
   ten bufor do zmapowanej pamieci czy pamieci wideo.

   Jak widac, mapowanie plikow do pamieci jest wygodne, gdyz nie trzeba
   ciagle skakac po pliku funkcja sys_lseek i wykonywac kosztownych
   czasowo wywolan innych funkcji systemowych. Warto wiec sie z tym
   zaznajomic. Nalezy jednak pamietac, ze nie wszystkie pliki czy
   urzadzenia daja sie zmapowac do pamieci - nie nalezy wtedy zamykac
   swojego programu z bledem, lecz korzystac z tradycyjnego interfejsu
   funkcji plikowych.
     _________________________________________________________________

Allegro

   Biblioteka Allegro powinna na wiekszosci systemow byc dostepna jako
   gotowy pakiet, ale w razie czego mozna ja pobrac na przyklad ze strony
   alleg.sf.net.

   Pierwszymi funkcjami, jakie w ogole nalezy uruchomic przez
   rozpoczeciem czegokolwiek sa install_allegro (w jezyku C -
   allegro_init) i install_keyboard.

   Pierwsza sluzy do inicjalizacji biblioteki. Jako parametry oczekuje:
   liczbe SYSTEM_AUTODETECT=0, adres zmiennej do przechowywania bledow
   (ale uwaga, proba deklaracji i uzycia errno w naszym programie moze
   skonczyc sie bledem linkera, wiec lepiej podac adres jakiegos naszego
   wlasnego DWORDa) oraz wskaznika na elementy uruchamiane przy wyjsciu
   (u nas wpiszemy NULL). Druga funkcja nie przyjmuje zadnych argumentow,
   a sluzy do instalacji funkcji odpowiedzialnych za dzialanie
   klawiatury. Allegro samo zajmuje sie klawiatura, wiec standardowe
   funkcje czytania z klawiatury moga nie dzialac. Do czytania klawiszy
   sluzy funkcja readkey. Nie przyjmuje ona zadnych argumentow, a zwraca
   wartosc przeczytanego klawisza.

   Biblioteka pozwala na ustawienie wielu rozdzielczosci, my zajmiemy sie
   rozdzielczoscia 640x480 w 8-bitowej glebi kolorow. Do ustawienia glebi
   kolorow sluzy funkcja set_color_depth przyjmujaca jeden argument -
   wartosc owej glebi, czyli w naszym przypadku 8.

   Po inicjalizacji biblioteki, instalacji klawiatury i ustawieniu glebi
   kolorow mozna przystapic do ustawienia trybu graficznego. Robi sie to
   za pomoca funkcji set_gfx_mode. Przyjmuje ona 5 argumentow: sterownik
   (u nas bedziemy korzystac z autowykrywania, wpisujac tu liczbe
   GFX_AUTODETECT=0), szerokosc zadanego trybu w pikselach, wysokosc
   trybu, szerokosc okna widoku i wysokosc okna widoku. U nas oknem
   widoku bedzie caly ekran, wiec ostatnie dwa parametry przyjma wartosc
   zero, a cale wywolanie (w jezyku C) bedzie mialo postac:
                set_gfx_mode ( GFX_AUTODETECT, 640, 480, 0, 0 );

   Jesli ustawienie rozdzielczosci sie nie powiedzie, wywolanie funkcji
   zwroci wartosc niezerowa.

   Po skonczeniu pracy z Allegro nalezy wywolac funkcje allegro_exit w
   celu zamkniecia i odinstalowania biblioteki z programu. Funkcja ta nie
   przyjmuje zadnych argumentow.

   Aby ustawic domyslna palete kolorow, wywolujemy funkcje set_palette.
   Jako jej jedyny parametr podajemy zewnetrzna (pochodzaca z Allegro)
   zmienna default_palette.

   Do czyszczenia ekranu (a wlasciwie wypelnienia go okreslonym kolorem)
   sluzy funkcja clear_to_color. Jej pierwszy parametr mowi, co ma zostac
   wyczyszczone - u nas chcemy wyczyscic caly ekran, wiec bedzie to
   zmienna z Allegro o nazwie screen. Drugi parametr tej funkcji to
   kolor, jakim chcemy wypelnic ekran. Zero oznacza czarny.

   Do wyswietlania tekstu na ekranie w trybie tekstowym sluzy funkcja
   allegro_message. Jej jedyny argument to tekst do wyswietlenia. Aby
   wyswietlic tekst w trybie graficznym, najpierw nalezy podjac decyzje,
   czy tekst ma byc na tle, czy tlo ma go przykryc. Jesli tlo ma byc pod
   tekstem, nalezy jednorazowo wywolac text_mode, jako parametr podajac
   liczbe -1 (minus jeden). Potem mozna juz wyswietlac tekst funkcja
   textout. Przyjmuje ona 5 argumentow: gdzie wyswietlic (u nas znow
   screen), jaka czcionka (skorzystamy z domyslnej czcionki w zmiennej
   Allegro o nazwie font), co wyswietlic (adres naszego napisu),
   wspolrzedna X, wspolrzedna Y oraz zadany kolor.

   Wspolrzedna X rosnie w prawo, a Y - w dol ekranu.

   Ale przejdzmy wreszcie do wyswietlania podstawowych elementow.
   Linie wyswietla sie funkcja line, przyjmujaca 6 argumentow: gdzie
   wyswietlic (tak, znowu screen), wspolrzedna X poczatku, wspolrzedna Y
   poczatku, wspolrzedna X konca, wspolrzedna Y konca, kolor.
   Kolor, jak w kazdej innej funkcji, mozemy podawac recznie jako liczbe,
   ale mozemy tez uruchomic funkcje makecol, podajac jej wartosci od 0 do
   255 kolejno dla kolorow: czerwonego, zielonego, niebieskiego, a wynik
   tej funkcji podajemy tam, gdzie podalibysmy kolor.
   Okregi wyswietla sie funkcja circle, przyjmujaca 5 argumentow: gdzie
   wyswietlic (i znowu screen), wspolrzedna X srodka, wspolrzedna Y
   srodka, promien i kolor.

   Po omowieniu tego, co ma byc w programie jeszcze dwa slowa o tworzeniu
   programu. O ile kompilacja pliku w asemblerze jest taka jak zawsze, to
   linkowanie najlepiej przeprowadzic za pomoca GCC. Normalnie nasza
   funkcje glowna nazwalibysmy main, ale Allegro posiada wlasna funkcje
   main, a oczekuje, ze nasza funkcja glowna bedzie sie nazywac
   _mangled_main (z podkresleniem z przodu). Ponadto, Allegro oczekuje,
   ze zadeklarujemy zmienna globalna _mangled_main_address i wpiszemy do
   niej adres _mangled_main. W jezyku C robi to za nas makro END_OF_MAIN.

   Niektore wersje biblioteki Allegro moga od nas jednak wymagac, abysmy
   to my mieli funkcje main - jesli w czasie linkowania wystapi blad, to
   wystarczy wlaczyc dwie odpowiednio oznaczone linijki w ponizszym
   programie.

   Program linkuje sie nastepujaca komenda:
                gcc -o program program.o `allegro-config --libs`

   Zwroccie uwage na odwrotne apostrofy. Sprawia one, ze wynik zawartej w
   nich komendy (a wiec niezbedne biblioteki) zostanie przekazany do GCC,
   dzieki czemu znajdzie on wszystko, co potrzeba.

   A oto przykladowy program. Wyswietla on tekst, linie i okrag, po czym
   czeka na nacisniecie jakiegokolwiek klawisza. Po nacisnieciu klawisza
   biblioteka Allegro jest zamykana i program sie konczy.
   (przeskocz przykladowy program Allegro)
; Program demonstracyjny biblioteki Allegro
;
; Autor: Bogdan D., bogdandr (at) op.pl
;
; kompilacja:
; nasm -O999 -f elf -o graf2.o graf2.asm
; gcc -o graf2 graf2.o `allegro-config --libs`

section .text
; wymagane przez Allegro:
;global main            ; TO WLACZYC, JESLI ALLEGRO WYMAGA FUNKCJI MAIN
global _mangled_main
global _mangled_main_address

; deklaracje elementow zewnetrznych:
extern  install_allegro
extern  install_keyboard
extern  set_color_depth
extern  set_gfx_mode
extern  allegro_exit
extern  text_mode
extern  set_palette
extern  default_palette
extern  clear_to_color
extern  screen
extern  textout
extern  font
extern  line
extern  makecol
extern  circle
extern  readkey

%define GFX_AUTODETECT 0                ; autowykrywanie sterownika

;main:                  ; TO WLACZYC, JESLI ALLEGRO WYMAGA FUNKCJI MAIN
_mangled_main:
        ; inicjalizacja biblioteki:
        push    dword 0
        push    err                     ; nasza zmienna do bledow
        push    dword 0
        call    install_allegro
        add     esp, 3*4                ; zdjecie parametrow ze stosu

        ; instalacja klawiatury
        call    install_keyboard

        ; ustawienie glebi kolorow:
        push    dword 8
        call    set_color_depth
        add     esp, 1*4                ; zdjecie parametrow ze stosu

        ; ustawienie rozdzielczosci:
        push    dword 0                 ; wysokosc okna
        push    dword 0                 ; szerokosc okna
        push    dword 480               ; wysokosc calego trybu
        push    dword 640               ; szerokosc calego trybu
        push    dword GFX_AUTODETECT
        call    set_gfx_mode
        add     esp, 5*4

        ; sprawdz, czy sie udalo
        cmp     eax, 0
        jne     koniec

        ; ustaw tlo pod tekstem
        push    dword -1
        call    text_mode
        add     esp, 1*4

        ; ustaw domyslna palete
        push    dword default_palette
        call    set_palette
        add     esp, 1*4

        ; wyczysc ekran
        push    dword 0                 ; czysc na czarno
        push    dword [screen]          ; co czyscic
        call    clear_to_color
        add     esp, 2*4

        ; wyswietl napis
        push    dword 15                ; kolor
        push    dword 10                ; wspolrzedna Y
        push    dword 10                ; wspolrzedna X
        push    dword napis             ; napis do wyswietlenia
        push    dword [font]            ; czcionka
        push    dword [screen]          ; gdzie wyswietlic
        call    textout
        add     esp, 6*4

        ; stworz kolor bialy do narysowania linii
        push    dword 255               ; skladowa niebieska
        push    dword 255               ; skladowa zielona
        push    dword 255               ; skladowa czerwona
        call    makecol
        add     esp, 3*4

        ; narysuj linie
        push    eax                     ; kolor
        push    dword 240               ; wspolrzedna Y konca
        push    dword 320               ; wspolrzedna X konca
        push    dword 400               ; wspolrzedna Y poczatku
        push    dword 540               ; wspolrzedna X poczatku
        push    dword [screen]
        call    line
        add     esp, 6*4

        ; stworz kolor zielony do narysowania kola
        push    dword 0
        push    dword 255
        push    dword 0
        call    makecol
        add     esp, 3*4

        ; narysuj kolo
        push    eax                     ; kolor
        push    dword 20                ; promien
        push    dword 240               ; wspolrzedna Y srodka
        push    dword 320               ; wspolrzedna X srodka
        push    dword [screen]
        call    circle
        add     esp, 5*4

        ; czekaj na klawisz
        call    readkey

koniec:
        ; zamknij Allegro
        call    allegro_exit
        ; powroc z naszej funkcji glownej
        ret

section .data
napis                   db      "Allegro", 0    ; napis do wyswietlenia
_mangled_main_address   dd      _mangled_main   ; wymagane
err                     dd      0               ; nasza zmienna bledow

   Jak widac, biblioteka Allegro jest tylko troche trudniejsza od
   SVGAlib, ale jej mozliwosci sa znacznie wieksze. Tutaj pokazalem tylko
   ulamek grafiki dwuwymiarowej. Allegro potrafi tez wyswietlac grafike
   trojwymiarowa, wyliczac transformacje, zapisywac wyswietlane obrazy do
   pliku oraz odtwarzac muzyke (w koncu to jest biblioteka do gier, nie
   tylko graficzna). Jak widzicie, jest jeszcze wiele mozliwosci przed
   Wami do odkrycia. Milej zabawy!

   Spis tresci off-line (klawisz dostepu 1)
   Spis tresci on-line (klawisz dostepu 2)
   Ulatwienia dla niepelnosprawnych (klawisz dostepu 0)
