int21h
Pseudopole v pascalu
Co je pole, je jasné. Pascal ale zná i tzv. pseudopole. Z klasického Turbo pascalu určitě znáte pseudopole
Port a PortW. Zde je na místě otázka, co je to vlastně zač. Není to blok paměti, ale jakýsi virtualizovaný přístup ke skrytým procedurám. To může být užitečné i v mnoha jiných případech, ale bohužel, Turbo pascal neumožňuje tvořit si vlastní pseudopole. To je rys, pro který bývá pascal vysmívaný programátory jiných jazyků, tedy, že programátor nemůže vytvořit analogie některých zabudovaných konstrukcí. A z toho odvozují, že pascal není plnohodnotný programovací jazyk. To je samozřejmě nesmysl a navíc pro Freepascal neplatí ani první část námitky. Ve Freepascalu totiž je možné tvořit vlastní pseudopole a umí to obě generace:
1.x.x i
2.x.x.>
Pseudoproměnné
Než se pustíme do pseudopolí, začneme trochu jednoduššími pseudoproměnnými. Pozadí této syntaxe je dosti složité a v tomto článku se nebudu pouštět do rozsáhlého výkladu a omezím se jen na minimum. Teorii a systematický výklad syntaxe si můžete přečíst z manuálu k Freepascalu nebo z jakékoliv lepší knihy o Delphi.
Pseudoproměnné jsou odvozeny od jedné vlastnosti
tříd z objektového programování. Přípomeňme si, že TP i BP znají tzv.
objekty, což je seskupení procedur, funkcí a proměnných, ve kterých procedury a funkce objektu společně sdílí proměnné objektu. Freepascal (a Delphi) kromě objektů znají i třídy (
class), které koncept objektů v různých aspektech rozšířují. Jedním z rozšíření jsou tzv.
properties, což se do češtiny překládá jako
vlastnosti.
Ukažme si prográmek:
type TTrida = class
public
Procedure PriradCislo(i:longint);
Function ZjistiCislo:longint;
property cislo:longint read ZjistiCislo write PriradCislo;
protected
vnitrnicislo:shortint;
end;
Procedure TTrida.PriradCislo(i:longint);
begin
if i<(-128) then i:=-128;
if i>127 then i:=127;
vnitrnicislo:=i;
end;
Function TTrida.ZjistiCislo:longint;
begin
ZjistiCislo:=vnitrnicislo;
end;
var trida:TTrida;
begin
trida:=TTrida.Create;
trida.cislo:=4543;
writeln(trida.cislo);
readln;
end.
Před sebou máme ne zcela triviální kód, který přiřadí proměnné
vnitrnicislo hodnotu. Možná se ptáte, proč to dělat tak složitě, když by prostě stačilo ponechat
vnitrnicislo
v režimu
public a provést přiřazení:
trida.vnitrnicislo:=HODNOTA
No jo, jenže všimněte si, že
vnitrnicislo
je jenom shortint a lehko bychom mohli přiřadit hodnotu mimo rozsah, což by vedlo k neočekávanému chování programu. Oklika přes
pseudoproměnnou cislo
umožňuje provést kontrolu rozsahu a podobným věcem zamezit. A nejen to. Obsah obslužných funkcí
PriradCislo
a
ZjistiCislo
může být libovolný a zdaleka se nemusí jen omezovat na kontroly rozsahů. Jako dobrý způsob použití mě napadá ukládání přiřazovaných hodnot do logovacího souboru, což by umožňovalo snadnější analýzu chyb.
Pseudopole
Pseudopole jsou dalším drobným rozšířením vymoženosti
property, potažmo pseudoproměnných. Bez dlouhých řečí si ukažme kód, který se podobá standardní pascalovské definici pseudopole
Port
type TBrana = class
public
Procedure DoPortu(p:word;b:byte);
Function ZPortu(p:word):byte;
property B[p:word]:byte read ZPortu write Doportu;
end;
Procedure TBrana.DoPortu(p:word;b:byte);assembler;
asm
mov dx,p
mov al,b
out dx,al
end;
Function TBrana.ZPortu(p:word):byte;assembler;
asm
mov dx,p
in al,dx
end;
var brana:TBrana;
i:longint;
a:byte;
begin
brana:=TBrana.Create;
writeln('Po zmacknuti Enteru program pocka 700 snimkovych behu, tedy 10 sekund');
readln;
writeln('Start!');
for i:=1 to 700 do
begin
while Brana.B[$3da] and 8 <> 0 do;
while Brana.B[$3da] and 8 = 0 do;
end;
writeln('Cas uplynul!');
readln;
end.
Docela se to už podobá pascalovskému pseudopoli Port, že? Ale jak odstranit ono ohyzné
"Brana.B" a nahradit to prostým
"Brana"? Jde to? Ano, jde.
Do deklarace
property jde totiž vsunout kouzelný modifikátor
default, který umožní vynechat část
".B"
Takže do třetice krátký prográmek, který připraví pseudopole přesně tak, jak jsme zvyklí. Opakovat příklad z porty by ale bylo nudné, takže si ukážeme, jak virtualizovat obrazovku do dvourozměrného pseudopole, a připomeneme si, jak ve Freepascalu provést přímý zápis do videopaměti.
const SIRKAOBRAZOVKY:longint = 80;
VYSKAOBRAZOVKY:longint = 25;
type
TObr = class
private
Function RozsahOK(x,y:byte):boolean;
Procedure ZapisZnak(x,y:byte;c:char);
Function PrectiZnak(x,y:byte):char;
public
property ZZ[x,y:byte]:char read PrectiZnak write ZapisZnak;default;
end;
Function TObr.RozsahOK(x,y:byte):boolean;
begin
RozsahOK:=(x>0) and (y>0) and (x<=SIRKAOBRAZOVKY) and (y<=VYSKAOBRAZOVKY);
end;
Procedure TObr.ZapisZnak(x,y:byte;c:char);
begin
if RozsahOK(x,y) then
asm
movzx ebx,y
movzx eax,x
dec ebx
dec eax
imul ebx,SIRKAOBRAZOVKY
add ebx,eax
shl ebx,1
add ebx,0B8000h
mov al,c
mov fs:[ebx],al
end;
end;
Function TObr.PrectiZnak(x,y:byte):char;
begin
if RozsahOK(x,y) then
asm
movzx ebx,y
movzx eax,x
dec ebx
dec eax
imul ebx,SIRKAOBRAZOVKY
add ebx,eax
shl ebx,1
add ebx,0B8000h
mov al,fs:[ebx]
end;
end;
var Obr:TObr;
x,y:byte;
begin
for y:=10 to 20 do
for x:=20 to 60 do
Obr[x,y]:='*';
readln;
end.
Properties mají pochopitelně mnohem širší použití než jen tvorba psedopolí. Velice užiteční jsou například v tvorbě užívatelských rozhraní (GUI), kdy je jejich pomocí možné dosáhnout toho aby se při změně parametrů grafického prvku změna okamžitě projevila na obrazovce. Například zavoláním...
Tlacitko.text:='zmeneny text';
...patřičně obaleným v
properties dosáhneme toho, aby měnící procedura rovnou zavolala
Tlacitko.Draw a změna se tak ihned projevila na obrazovce.
A to je vše.