Prof.nadzw.dr hab.inż. Władysław Brzozowski Cz*stochowa-Gliwice, 15.02.1999 r.
Politechnika Częstochowska
Instytut Elektroenergetyki
Wykłady z przedmiotu:
TECHNIKA PROGRAMOWANIA
studia magisterskie, kierunek Elektrotechnika,
specjalno** Informatyka w Elektroenergetyce, sem.VI
Wyk*ad 5. J*zyk Turbo-Pascal. Grafika w systemie Turbo-Pascal 7.0. Przerwania
5.1. Inicjowanie trybu graficznego
Podobnie jak to by*o z innymi elementami systemu, nie bed* omawiane wszystkie elementy grafiki, a jedynie ciekawsze lub mo*e trudniejsze.
Aby program mog* dzia*a* w trybie graficznym, do pami*ci operacyjnej mikrokomputera musi zosta* wczytany program sterownika tego trybu, czyli tzw. driver. Program ten dla karty graficznej EGA/VGA/SVGA nosi nazwe EGAVGA.BGI.
Ka*dy tego typu sterownik mo*e organizowa* kilka tryb*w graficznych ekranowych, cechuj*cych si* r**n* liczb* punkt*w graficznych ekranu tzw. pixli (pixel) oraz stron ekranowych. W danym momencie czasu wizualizowana jest jedna strona ekranowa, a pozosta*e mog* by* generowane (tworzone). Prze**czanie stron dokonuje si* b*yskawicznie, w spos*b niezauwa*alny dla u*ytkownika. Strony ekranowe wykorzystuje si* szczeg*lnie w animacji komputerowej, np. w grach. Im dany tryb ekranowy dopuszcza wi*cej stron, tym z regu*y daje gorszy obraz (mniejsza liczba pixli).
Sterownik trybu graficznego zostaje za*adowany do pami*ci operacyjnej instrukcj* - procedur* standardow* InitGraph. W systemie Borland Pascal 7.0 przewidziano mo*liwo** za*adowania w*asnego programu - sterownika graficznego u*ytkownika, inn* instrukcj*, mianowicie InstallUserDriver. Oczywi*cie sterownik taki musi by* wcze*niej napisany przez u*ytkownika i musi prawid*owo wsp**pracowa* z kart* graficzn* mikrokomputera.
5.2. Organizacja ekranu. Strony ekranowe i animacja komputerowa
Ekran graficzny w trybie graficznym jest okre*lony liczb* pixli wzd*u* osi x oraz y. Numer pixla po y narasta z g*ry na d** (odwrotnie ni* w geometrii). Wszystkie instrukcje graficzne operuj* wy**cznie numerami pixli po x i po y. Ekran monitora nie mo*e r*wnocze*nie pracowa* w trybie graficznym i w trybie znakowym w tym sensie, *e cz*** ekranu pracuje w trybie graficznym a cz*** w tekstowym.
Mo*na okre*li* okno graficzne, ale tylko jako wyr**nion* cz*** og*lnego ekranu graficznego. Okno graficzne generowane jest przez procedur* standardow* SetViewPort.
Strony ekranowe sa numerowane liczbami 0, 1, 2 itd. Chc*c aby na ekranie zosta*a zwizualizowana strona o numerze np. 1 nale*y wywo*a* procedur* standardow* SetVisualPage(1). W czasie tej wizualizacji, je*li chcemy generowa* np. stron* 3, nale*y wcze*niej wywo*a* procedur* standardow* SetActivePage(3). Prze**czenie tych dwu stron polega na powt*rnym wywo*aniu w/w procedur, ale z zamienionymi numerami stron, czyli
SetVisualPage(3);
SetActivePage(1);
Generuj*c i prze**czaj*c kolejne strony ekranowe z nieznaczn* zmian* po*o*enia jakich* przedmiot*w lub wizerunk*w ludzi w okre*lonych kierunkach, realizujemy tzw. animacj* komputerow*. Numery stron w procedurach jw. mo*na r*wnie* podstawia* jako zmienne typu word.
5.3. Graficzne procedury tekstowe
S*owo „tekstowe” nie oznacza tu trybu tekstowego, a jedynie wyprowadzanie napis*w w trybie graficznym. Podstawowymi procedurami w tym zakresie s* SetTextStyle oraz OutTextXY z odpowiednimi parametrami.
Je*li do wyprowadzenia napisu u*yto niepodstawowego kroju czcionki, np. SmallFont, to zbi*r z parametrami tego kroju, w tym przypadku o nazwie litt.chr musi znajdowa* si* w katalogu bie**cym. Je*li zbioru tego niema, to napis zostanie wyprowadzony ale podstawowym krojem czcionki.
Tekst na ekranie mo*na dowolnie zwi*ksza* lub zmniejsza*. Aby to zrealizowa*, to przed wyprowadzeniem napisu nale*y wywo*a* procedur* standardow* SetUserCharSize z odpowiednimi parametrami.
5.4. Kopiowanie fragment*w obrazu graficznego
Mo*liwe jest skopiowanie do pami*ci operacyjnej mikrokomputera dowolnego prostok*tnego wycinka ekranu w trybie graficznym. Realizuje si* to procedur* standardow* GetImage z odpowiednimi parametrami. Wycinek ten mo*na p**niej odtworzy* wielokrotnie w dowolnych miejscach ekranu, procedura PutImage.
Procedury te w szczeg*lno*ci b*dziemy wykorzystywa* przy animacji komputerowej, gdy chodzi o maksymalne skr*cenie czasu generowania obrazu (np. w grach komputerowych).
Powy*ej om*wiono, i to pobie*nie, jedynie niekt*re elementy trybu graficznego w systemie Turbo Pascal 7.0. Aby m*c programowa* z wykorzystaniem tego trybu konieczna jest *mudna osobista nauka praktyczna na komputerze, na podstawie szczeg**owego podr*cznika systemu (uwaga dotyczy generalnie ca*ego procesu programowania).
5.5. Przyk*adowy program dydaktyczny wykorzystuj*cy tryb graficzny i animacj* komputerow* (zegar.pas)
Uwaga: tekst tego programu pod edytorem Word (podobnie jak innych program*w dydaktycznych za**czonych do wyk*adu) mo*na wykorzysta* jako plik *r*d*owy systemu Turbo Pascal, zaznaczaj*c tekst i zapisuj*c go na dysk jako tekst ASCII ze znakami zmiany wiersza oraz z rozszerzeniem .pas).
{$A+,B-,D+,E+,F-,I+,L+,N-,O-,R-,S+,V+}
{$M 16384,0,655360}
program ZEGAR;
{Program graficzny symulujacy dzialanie zegara.
Autor: Brzozowski W., 1990 r.
Program przeznaczony na komputer z karta VGA/SVGA.}
uses Crt,Dos,Graph;
const
Opozn=100;
{Stala Opozn nalezy korygowac w zaleznosci od szybkosci uzytego
komputera - dla AT 386 dobiera sie ok. 100. Dla komputera szybszego
wiecej, wolniejszego mniej (jednak nie mniej niz 0). O wielkosc Opozn
w milisekundach zostanie dodatkowo opozniony czas wizualizacji strony
tak aby skok wskazowki sekundowej wynosil dokladnie 1s.
Niezaleznie od wartosci Opozn program dziala poprawnie jednakze
skok wskazowki sekundowej nie jest rowny 1s.}
var
Sterownik,Tryb:integer;
Znak:char;
StrWidz,StrZap,SkokKata,SzerokPodz,WskazGodz,WskazMin,WskazSek,X0,Y0,
Prom,DeltaX,DeltaY,I,I1,I2,I3,I4 :integer;
PromNogi,DeltaXNogi,DeltaYNogi: integer;
Kat,KatGodz,KatMin,KatSek,X1 :real;
Opis :string[2];
CzasNum: string[11];
Godz,Min,Sek,Setna :word;
label
Nawrot;
procedure Korekta;
begin
if Copy(Opis,1,1)=' '
then
Opis:='0'+Copy(Opis,2,1);
if Copy(Opis,2,1)=' '
then
Opis:=Copy(Opis,1,1)+'0'
end; {procedure Korekta}
begin
ClrScr;
while KeyPressed do Znak:=ReadKey;
Sterownik:=Vga;
Tryb:=VgaMed;
InitGraph(Sterownik,Tryb,'D:\Pascal');
ClearDevice;
StrWidz:=0;
StrZap:=1;
Nawrot:
{generowanie strony zapisywanej - wizualizacja strony widzialnej}
SetActivePage(StrZap);
SetVisualPage(StrWidz);
SetViewPort(0,0,GetMaxX,GetMaxY,Clipon);
SetBkColor(Black);
ClearViewPort;
{generowanie ksztaltu zegara i tarczy godzinowej}
X0:=360;
Y0:=150;
Prom:=130; {wewnetrzny okreg podzialki}
WskazGodz:=80;
WskazMin:=100;
WskazSek:=120;
SzerokPodz:=10;
DeltaXNogi:=90;
DeltaYNogi:=123;
PromNogi:=20;
SetLineStyle(SolidLn,0,ThickWidth);
SetColor(Red);
Circle(X0-DeltaXNogi,Y0+DeltaYNogi,Promnogi);
SetLineStyle(SolidLn,0,ThickWidth);
Circle(X0+DeltaXNogi,Y0+DeltaYNogi,PromNogi);
SetLineStyle(SolidLn,0,ThickWidth);
Circle(X0,Y0,Prom+35); {zewnetrzny okrag obudowy}
SetColor(Green);
SetLineStyle(SolidLn,0,NormWidth);
Circle(X0,Y0,Prom+30); {wewnetrzny okrag obudowy}
SetColor(Magenta);
Circle(X0,Y0,Prom+SzerokPodz); {zewnetrzny okrag podzialki}
Circle(X0,Y0,Prom); {wewnetrzny okrag podzialki}
SetLineStyle(SolidLn,0,NormWidth);
X1:=1.33;
{wspolczynnik korekcyjny wymiarow zalezny od typu karty graficznej
i trybu graficznego - dla VGA, VgaMed - 1.33}
SetColor(LightBlue);
for SkokKata:=1 to 60 do
begin
Kat:=SkokKata*Pi/30;
I1:=Round(Prom*Sin(Kat));
I2:=Round(Prom*Cos(Kat)/X1);
I3:=Round((Prom+SzerokPodz)*Sin(Kat));
I4:=Round((Prom+SzerokPodz)*Cos(Kat)/X1);
Line(X0+I1,Y0-I2,X0+I3,Y0-I4);
end; {for SkokKata:=1 to 60 do}
SetColor(LightGreen);
SetLineStyle(SolidLn,0,ThickWidth);
for SkokKata:=1 to 12 do
begin
SetColor(White);
Kat:=SkokKata*Pi/6;
Line(X0+Round(Prom*Sin(Kat)),
Y0-Round(Prom*Cos(Kat)/X1),
X0+Round((Prom+SzerokPodz)*Sin(Kat)),
Y0-Round((Prom+SzerokPodz)*Cos(Kat)/X1));
Str(SkokKata:2,Opis);
DeltaX:=-10;
DeltaY:=0;
if (SkokKata<3) or (SkokKata>10)
then
DeltaY:=10;
if SkokKata=3
then
DeltaX:=DeltaX+3;
if (SkokKata=9) or (SkokKata=10)
then
DeltaX:=DeltaX-3;
if (SkokKata=11) or (SkokKata=12)
then
DeltaY:=DeltaY-3;
OutTextXY(X0+DeltaX+Round((Prom+SzerokPodz+5)*Sin(Kat)),
Y0-DeltaY-Round((Prom+SzerokPodz+5)*Cos(Kat)/X1),
Opis)
end; {for SkokKata:=1 to 12 do}
{nazwa firmy}
OutTextXY(X0-20,Y0+10,'SEIKO');
OutTextXY(X0-22,Y0+20,'QUARTZ');
{odczyt czasu}
GetTime(Godz,Min,Sek,Setna);
{generowanie czasu numerycznego}
Str(Godz:2,Opis);
Korekta;
CzasNum:=Opis;
Str(Min:2,Opis);
Korekta;
CzasNum:=CzasNum+'.'+Opis;
Str(Sek:2,Opis);
Korekta;
CzasNum:=CzasNum+'.'+Opis;
OutTextXY(X0-33,Y0-50,CzasNum);
{generowanie czasu graficznego}
{wskazowka godzinowa}
SetColor(LightCyan);
KatGodz:=((Godz mod 12)*30+Min/2)*2*Pi/360;
SetLineStyle(SolidLn,0,ThickWidth);
Line(
X0-Round(WskazGodz/5*Sin(KatGodz)),
Y0+Round(WskazGodz/5*Cos(KatGodz)/X1),
X0+Round(WskazGodz*Sin(KatGodz)),
Y0-Round(WskazGodz*Cos(KatGodz)/X1));
{wskazowka minutowa}
SetColor(LightMagenta);
KatMin:=(Min*6+Sek*0.1)*2*Pi/360;
Line(
X0-Round(WskazMin/5*Sin(KatMin)),
Y0+Round(WskazMin/5*Cos(KatMin)/X1),
X0+Round(WskazMin*Sin(KatMin)),
Y0-Round(WskazMin*Cos(KatMin)/X1));
{wskazowka sekundowa}
SetColor(Yellow);
KatSek:=(Sek*6+Setna*0.06)*2*Pi/360;
SetLineStyle(SolidLn,0,NormWidth);
Line(
X0-Round(WskazSek/5*Sin(KatSek)),
Y0+Round(WskazSek/5*Cos(KatSek)/X1),
X0+Round(WskazSek*Sin(KatSek)),
Y0-Round(WskazSek*Cos(KatSek)/X1));
Delay(Opozn);
{komunikat}
OutTextXY(10,330,'Celem wyjscia z programu przycisnij dowolny klawisz');
if KeyPressed
then
begin
CloseGraph;
Halt
end
else
{zamiana stron z zapisywanej na widzialna i vice versa}
begin
I:=StrWidz;
StrWidz:=StrZap;
StrZap:=I;
goto Nawrot
end; {if KeyPressed}
end. {koniec programu ZEGAR}
5.6. Przerwanie
Przerwaniem nazywa si* chwilowe zawieszenie realizacji programu celem wykonania przez mikroprocesor okre*lonej operacji. Operacj* t* mo*e by* np. przyj*cie z portu szeregowego danych (bajt*w) wynikaj*cych z uruchomienia myszy przez u*ytkownika. Poniewa* dane takie powinny zosta* przyj*te (gdy* domniemywa si*, *e w danym programie dzia*anie myszy jest istotne), realizacja dotychczasowego programu musi by* chwilowo wstrzymana, a po dokonaniu operacji przywr*cona - bez *adnej zmiany, tak jakby przerwania nie by*o.
W dowolnym momencie czasu stan komputera mo*na precyzyjnie odtworzy*, je*li odtworzy si* stan (zawarto**) wszystkich rejestr*w mikroprocesora. Zatem w momencie wyst*pienia przerwania mikroprocesor przepisuje do pami*ci operacyjnej stany swoich rejestr*w, a po realizacji (m*wimy: obs*udze) przerwania odtwarza stan tych rejestr*w odczytuj*c te stany z pami*ci operacyjnej. Ka*de przerwanie ma sw*j w*asny, *ci*le okre*lony adres pami*ci operacyjnej, pod kt*rym nast*puje zapis stan*w rejestr*w. R*wnocze*nie pod tym adresem rezyduje w pami*ci operacyjnej podprogram obs*ugi przerwania.
Stany wszystkich rejestr*w mikroprocesora nazywamy wektorem przerwania (sk*adowymi tego wektora s* poszczeg*lne rejestry). Wektor przerwa* w j*zyku Borland Pascal jest typem rekordowym o predefiniowanej nazwie Registers. Polami tego rekordu s* stany poszczeg*lnych rejestr*w mikroprocesora.
Pola te s* oznaczone symbolami: AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags, AL, AH, BL, BH, CL, CH, DL, DH (opis i funkcje rejestr*w mikroprocesora - [4], cz. Ic, pkt. 20).
Wszystkie przerwania dzielimy na sprz*towe i programowe.
*r*d*em przerwa* sprz*towych s* urz*dzenia wej*cia - wyj*cia.
*r*d*em przerwa* programowych s* programy, rozumiej*c tu: program BIOS, system operacyjny DOS, *rodowisko Windows oraz programy u*ytkowe (aplikacje).
W mikrokomputerze PC zdefiniowano standardowy zbi*r 256 przerwa* o kolejnych numerach. Przyk*adowo: przerwanie o numerze $0C (liczba heksadecymalna r*wna dziesi*tnej 12) pochodzi od portu szeregowego COM1 (jak na wst*pie).
Ka*demu z powy*szych przerwa* przypisany jest adres pami*ci operacyjnej, jak opisano powy*ej. Adres ten mo*na zlokalizowa* procedur* standardow* GetIntVec.
Programista, pisz*c program w j*zyku Borland Pascal, mo*e:
zrealizowa* okre*lone przerwanie programowe standardowe i w ten spos*b zmusi* komputer do wykonania odpowiedniej funkcji BIOS lub DOS, wykorzystuj*c procedur* standardow* Intr. Zostanie to zilustrowane dalej przyk*adem podprogramu KopEkr realizuj*cego hard copy (zrzut na drukark*) ekranu w trybie graficznym (drukarka laserowa). W programie tym przesy*a si* bajty do drukarki za po*rednictwem programu BIOS wykorzystuj*c przerwanie o numerze $17;
napisa* w*asn* procedur* obs*ugi przerwania programowego, kt*ra zast*pi procedur* standardow* tego przerwania (przyk*ad takiej procedury [4], cz. Ib, str. 644).
5.7. Przyk*adowy podprogram realizuj*cy przerwanie programowe
procedure KopEkr(X0,Y0{parametry skrajnego punktu wydruku na papierze w dotach, np. X0=200, Y0=100},Rozdziel{rozdzielczosc drukarki laserowej, np. Rozdziel=0}: integer);
{Procedura realizuje zrzut ekranu (tzw. hard copy) w trybie graficznym na drukarke
laserowa LaserJet5L lub wczesniejsza. Autor: Brzozowski W., 1998 (rozbudowany podprogram z ksiazki Waclawek R.: Programowa obsluga drukarek laserowych)}
const
WspPoXHCMed: real=1.3; {wspolczynnik korekcyjny szerokosci wydruku po x - dobrac eksperymentalnie}
WspPoYHCMed: real=1.1; {wspolczynnik korekcyjny szerokosci wydruku po y - dobrac eksperymentalnie}
KolTlSzk:= word=0; {kolor tla ekranu w trybie graficznym - tu black}
EC=#27;
Waga: array[0..7] of byte=(1,2,4,8,16,32,64,128);
var
DrajwerGraf,TrybGraf: integer;
Lst_Result: boolean;
Max_X,Max_Y,X,Y: integer;
SL_Bajtow,LSter,LX0,LY0: string;
Bajt: byte;
procedure WriteLst(Lpt: byte; Tekst: string);
var
Rejestry: Registers;
I: integer;
begin
Lst_Result:=True;
with Rejestry do
for I:=1 to Length(Tekst) do
begin
{ustawienie rejestrow mikroprocesora}
AH:=0;
DX:=Lpt-1;
AL:=Byte(Tekst[I]);
Intr($17,Rejestry); {instrukcja przerwania}
{zwrotna zawartosc rejestrow mikroprocesora}
if (AH and $38)<>$10
then
Lst_Result:=False
end {for I:=1 to Length(Tekst) do}
end; {procedure WriteLst(Lpt{numer portu rownoleglego}: byte; Tekst: string)}
begin
DrajwerGraf:=Detect;
Max_X:=Round(GetMaxX*WsPoXHCMed);
Max_Y:=Round(GetMaxY*WsPoYHCMed);
Str((Max_Y div 8)+1,SL_Bajtow);
if (Rozdziel<>75) and (Rozdziel<>100) and (Rozdziel<>150) and
(Rozdziel<>300)
then
Rozdziel:=75;
Str(Rozdziel,LSter);
X0:=X0+(Max_X+1)*(300 div Rozdziel);
ReWrite(Lst);
WriteLst(1,EC+'E'+#27+'&l1O'); {orientation landscape}
WriteLst(1,EC+'*t'+LSter+'R'); {ustalenie rozdzielczosci}
Str(X0,LX0);
Str(Y0,LY0);
WriteLst(1,EC+'*p'+LX0+'x'+LY0+'Y'+EC+'*r1A'); {punkt poczatkowy bitmapy}
for X:=Max_X downto 0 do
begin
Bajt:=0;
WriteLst(1,EC+'*b'+SL_Bajtow+'W');
for Y:=0 to Max_Y do
begin
if GetPixel(Round(X/WsPoXHCMed),Round(Y/WsPoYHCMed))<>KolTlSzk
then
Bajt:=Bajt+Waga[7-(Y mod 8)];
if (Y mod 8)=7
then
begin
WriteLst(1,Chr(Bajt));
Bajt:=0
end {if (Y mod 8)=7}
end; {for Y:=0 to Max_Y do}
if (Y mod 8)<>7
then
WriteLst(1,Chr(Bajt));
end; {for X:=Max_X downto 0 do}
WriteLst(1,EC+'*rB'); {koniec bitmapy}
WriteLst(1,#12);
Close(Lst)
end; {procedure KopEkr(X0,Y0,Rozdziel: integer)}
Koniec wyk*adu 5
Plik W5TEPR99.DOC
Edytor WORD 6.0 PL