Na początek uprzedzam: jeśli myślicie o wysokich rozdzielczościach, to się zawiedziecie, gdyż ten kurs nie będzie takich omawiał. Jeśli naprawdę wolicie wysokie rozdzielczości, to poszukajcie w Internecie opisu standardu VESA lub DirectX API.
A my, zamiast bawić się w te wszystkie skomplikowane sprawy, zajmiemy się trybem 13h.
Ten tryb oferuje rozdzielczość 320x200 w 256 kolorach (co widać też w
Liście Przerwań Ralfa Brown'a -
RBIL).
Ale najważniejszą jego cechą jest to, że 320x200 = 64000 < 64kB, więc cały ekran mieści się w
jednym segmencie, co znacznie ułatwia pracę.
Ekran w trybie graficznym mieści się w segmencie 0A000h oraz:
0A000:0000 - pierwszy piksel (bajt, 256 możliwości)
0A000:0001 - drugi piksel
0A000:0002 - trzeci piksel
...
Do zmiany trybu graficznego używa się przerwania 10h, funkcji 0 (opis wyjęty z Listy przerwań Ralfa Brown'a):
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 widać, zmiana trybu graficznego na omawiany tryb 13h nie jest trudniejsza niż:
mov ax, 13h int 10h
Powrót do tradycyjnego trybu tekstowego 80x25 wygląda tak:
mov ax, 3 int 10h
Pytanie brzmi: jak cokolwiek narysować?
Nic prostszego! Po prostu pod adres:
wiersz*320 + kolumna
zapisujemy odpowiedni bajt, na przykład tak (składnia 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 mało na nasze ambicje, prawda?
Spróbujmy narysować poziomą linię (NASM):
; 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 ; przenieś 10 bajtów wartości 15 pod ; es:di = 0a000:0000 xor ah, ah int 16h mov ax, 3 int 10h ; powrót do trybu tekstowego mov ax, 4c00h int 21h
To chyba nie było zbyt trudne, prawda? No to spróbujmy coś trudniejszego: linia pionowa.
Cała filozofia
w tym przypadku polega na tym, aby po
narysowaniu piksela przejść o 1 wiersz niżej (czyli o 320 bajtów dalej). Piszmy więc (NASM):
; 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 było łatwo:
rysować zaczynaliśmy w lewym górny rogu, więc DI był równy 0. A co, jeśli
chcemy wyświetlać piksele gdzieś indziej?
Cóż, są dwie możliwości:
W takim przypadku kompilator policzy DI za ciebie, wystarczy wpisać coś takiego:
mov di, wiersz*320 + kolumna
wstawiając w miejsce słów wiersz
i kolumna
znane przez siebie wartości.
Tutaj jest gorzej. Trzeba wpisać do programu instrukcje, które przemnożą wiersz przez 320 i dodadzą kolumnę. Należy raczej unikać powolnej instrukcji (I)MUL. Ten problem rozwiążemy tak (wiersz i kolumna to 2 zmienne po 16 bitów):
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 przykład: rysowanie okręgu (no, w każdym razie czegoś co miało być okręgiem a ma kształt bardziej przypominający elipsę...). Program ten wykorzystuje koprocesor do policzenia sinusów i kosinusów dla kątów od 0 do 360 stopni, przerobionych na radiany. Komentarze obok instrukcji FPU oznaczają stan stosu, od st(0) z lewej.
; 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 ; kąt=0, r, 1, pi/180 mov al, 15 rysuj: fld st0 ; kąt, kąt, r, 1, pi/180 fmul st4 ; kąt w radianach mov di, 100*320 + 160 ; środek ekranu fsin ; sin(kąt), kąt, r, 1, pi/180 fmul st2 ; sin(kąt)*r, kąt, r, 1, pi/180 fistp word [wys] ; kąt, r, 1, pi/180 fld st0 ; kąt, kąt, r, 1, pi/180 fmul st4 ; kąt w radianach fcos ; cos(kąt), kąt, r, 1, pi/180 fmul st2 ; r*cos(kąt), kąt, r, 1, pi/180 fistp word [szer] ; kąt, r, 1, pi/180 add di, [szer] ; dodajemy odległość poziomą mov dx, [wys] mov bx, dx shl dx, 8 shl bx, 6 add dx, bx ; dx = wys*320 sub di, dx ; odejmujemy odległość pionową mov [es:di], al ; wyświetlamy 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, używając FSIN i FCOS, można rysować na przykład linie ukośne, które pominąłem w tym kursie.
Mam nadzieję, że po lekturze tego odcinka każdy bez problemów będzie rysował w tym dość prostym (zwłaszcza do nauki) trybie graficznym.
Miłego eksperymentowania!