   #Start Contents

                       Tryb graficzny w jezyku asembler

   Na poczatek uprzedzam: jesli myslicie o wysokich rozdzielczosciach, to
   sie zawiedziecie, gdyz ten kurs nie bedzie takich omawial. Jesli
   naprawde wolicie wysokie rozdzielczosci, to poszukajcie w Internecie
   opisu standardu VESA lub DirectX API.

   A my, zamiast bawic sie w te wszystkie skomplikowane sprawy, zajmiemy
   sie trybem 13h. Ten tryb oferuje rozdzielczosc 320x200 w 256 kolorach
   (co widac tez w Liscie Przerwan Ralfa Brown'a - RBIL).
   Ale najwazniejsza jego cecha jest to, ze 320x200 = 64000 < 64kB, wiec
   caly ekran miesci sie w jednym segmencie, co znacznie ulatwia prace.

   Ekran w trybie graficznym miesci sie w segmencie 0A000h oraz:
   0A000:0000 - pierwszy piksel (bajt, 256 mozliwosci)
   0A000:0001 - drugi piksel
   0A000:0002 - trzeci piksel
   ...

   Do zmiany trybu graficznego uzywa sie przerwania 10h, funkcji 0 (opis
   wyjety z Listy przerwan Ralfa Brown'a):
   (przeskocz opis int 10h, ah=0)
        INT 10 - VIDEO - SET VIDEO MODE
                AH = 00h
                AL = desired video mode (see #00010)
        Return: AL = video mode flag (Phoenix, AMI BIOS)
                    20h mode > 7
                    30h modes 0-5 and 7
                    3Fh mode 6
                AL = CRT controller mode byte (Phoenix 386 BIOS v1.10)
        Desc:   specify the display mode for the currently active display
                 adapter

   Jak widac, zmiana trybu graficznego na omawiany tryb 13h nie jest
   trudniejsza niz:
        mov ax, 13h
        int 10h

   Powrot do tradycyjnego trybu tekstowego 80x25 wyglada tak:
        mov ax, 3
        int 10h

   Pytanie brzmi: jak cokolwiek narysowac?
   Nic prostszego! Po prostu pod adres:
   wiersz*320 + kolumna
   zapisujemy odpowiedni bajt, na przyklad tak (skladnia TASM):
        mov ax, 0a000h
        mov es, ax
        xor di,di
        mov byte ptr es:[di],15         ; NASM/FASM:   mov byte [es:di],15

   No ale 1 piksel to za malo na nasze ambicje, prawda?
   Sprobujmy narysowac pozioma linie (NASM):
   (przeskocz program rysujacy linie pozioma)
        ; nasm -O999 -o liniapoz.com -f bin liniapoz.asm

        org 100h

                mov ax, 13h
                int 10h                 ; uruchom tryb graficzny 13h

                mov ax, 0a000h
                mov es, ax
                xor di, di

                mov al, 15
                mov cx, 10

                rep stosb               ; przenies 10 bajtow wartosci 15 pod
                                        ; es:di = 0a000:0000

                xor ah, ah
                int 16h

                mov ax, 3
                int 10h                 ; powrot do trybu tekstowego

                mov ax, 4c00h
                int 21h

   To chyba nie bylo zbyt trudne, prawda? No to sprobujmy cos
   trudniejszego: linia pionowa.

   Cala "filozofia" w tym przypadku polega na tym, aby po narysowaniu
   piksela przejsc o 1 wiersz nizej (czyli o 320 bajtow dalej). Piszmy
   wiec (NASM):
   (przeskocz program rysujacy linie pionowa)
        ; nasm -O999 -o liniapio.com -f bin liniapio.asm

        org 100h

                mov     ax, 13h
                int     10h

                mov     ax, 0a000h
                mov     es, ax
                xor     di, di

                mov     al, 15
                mov     cx, 100

        rysuj:
                mov     [es:di], al
                add     di, 320
                loop    rysuj

                xor     ah, ah
                int     16h

                mov     ax, 3
                int     10h

                mov     ax, 4c00h
                int     21h

   Na razie bylo latwo: rysowac zaczynalismy w lewym gorny rogu, wiec DI
   byl rowny 0. A co, jesli chcemy wyswietlac piksele gdzies indziej?
   Coz, sa dwie mozliwosci:
    1. W czasie pisania programu (czyli przed kompilacja) znasz dokladna
       pozycje, gdzie bedziesz rysowac.
       W takim przypadku kompilator policzy DI za ciebie, wystarczy
       wpisac cos takiego:
                mov di, wiersz*320 + kolumna
       wstawiajac w miejsce slow "wiersz" i "kolumna" znane przez siebie
       wartosci.
    2. Pozycja, gdzie bedziesz rysowac jest zmienna i zalezy na przyklad
       od tego, co wpisze uzytkownik.
       Tutaj jest gorzej. Trzeba wpisac do programu instrukcje, ktore
       przemnoza wiersz przez 320 i dodadza kolumne. Nalezy raczej unikac
       powolnej instrukcji (I)MUL. Ten problem rozwiazemy tak (wiersz i
       kolumna to 2 zmienne po 16 bitow):
                mov ax, [wiersz]
                mov bx, [wiersz]        ; BX = AX
                shl ax, 8               ; AX = AX*256
                shl bx, 6               ; BX = BX*64 = AX*64
                add ax, bx              ; AX = AX*256 + AX*64 = AX*320 =
                                        ;     = wiersz*320
                add ax, [kolumna]       ; AX = wiersz*320 + kolumna

                mov di,ax

   Ostatni przyklad: rysowanie okregu (no, w kazdym razie czegos co mialo
   byc okregiem a ma ksztalt bardziej przypominajacy elipse...). Program
   ten wykorzystuje koprocesor do policzenia sinusow i kosinusow dla
   katow od 0 do 360 stopni, przerobionych na radiany. Komentarze obok
   instrukcji FPU oznaczaja stan stosu, od st(0) z lewej.
   (przeskocz program rysujacy kolo)
        ; nasm -O999 -o kolo.com -f bin kolo.asm

        org 100h

                mov ax, 13h
                int 10h

                mov ax, 0a000h
                mov es, ax

                mov cx, 360

                finit
                fldpi
                fild word [sto80]

                fdivp st1, st0          ; pi/180

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

                mov al, 15

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

                fmul st4                ; kat w radianach
                mov di, 100*320 + 160   ; srodek ekranu

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

                fistp word [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 word [szer]       ; kat, r, 1, pi/180

                add di, [szer]          ; dodajemy odleglosc pozioma

                mov dx, [wys]
                mov bx, dx
                shl dx, 8
                shl bx, 6
                add dx, bx              ; dx = wys*320

                sub di, dx              ; odejmujemy odleglosc pionowa

                mov [es:di], al         ; wyswietlamy piksel

                fadd st0, st2           ; kat += 1

                dec cx
                jnz rysuj

                finit

                xor ah, ah
                int 16h

                mov ax, 3
                int 10h

                mov ax, 4c00h
                int 21h

        r       dw      50
        szer    dw      0
        wys     dw      0
        sto80   dw      180

   Podobnie, uzywajac FSIN i FCOS, mozna rysowac na przyklad linie
   ukosne, ktore pominalem w tym kursie.

   Mam nadzieje, ze po lekturze tego odcinka kazdy bez problemow bedzie
   rysowal w tym dosc prostym (zwlaszcza do nauki) trybie graficznym.

   Milego eksperymentowania!

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