Pascal

Tartalom

1. Bevezetés
2. A keretrendszer
2.1. A szerkesztő parancsai
2.2. A szövegszerkesztő
2.3. A Pascal előre programozott funkcióbillentyűi
3. A HiSoft Pascal nyelvi elemei
3.1. A nyelv jelkészlete
3.2. A foglalt szavak
3.3. A program utasításai és sorai
3.4. Azonosítók
3.5. Számok
3.6. Szövegkonstansok
3.7. Címkék
3.8. Operandusok
3.9. Operátorok
3.10. Kifejezések
3.11. Megjegyzések
4. A Pascal program szerkezete
5. Adattípusok és deklarációk
5.1. Egyszerű adattípusok
5.1.1. Numerikus adatok tárolása
5.1.2. Logikai információk tárolása
5.1.3. Szöveges információk tárolása
5.1.4. Sorszámozott típusok
5.2. Mutatótípusok
5.3. Strukturált típusok
5.3.1. Tömbtípusok
5.3.2. Rekordtípusok
5.3.3. Halmaztípusok
5.4. Típusnevek létrehozása (TYPE)
5.4.1. A TYPE használata felsorolt típus esetén
5.4.2. A TYPE használata résztartománytípus esetén
5.4.3. A TYPE használata tömbök esetén
5.4.4. A TYPE használata rekordok esetén
5.4.5. A TYPE használata halmazok esetén
5.5. Konstansnevek létrehozása (CONST)
5.6. Azonos és kompatibilis típusok
5.6.1. Típusok azonossága
5.6.2. Típusok kompatibilitása
5.6.3. Értékadási kompatibilitás
5.7. Típuskonverzió
6. Pascal kifejezések
6.1. Egy- és kétoperandusú műveletek
6.2. Elsőbbségi szabályok
6.3. A műveletek csoportosítása
6.3.1. Aritmetikai műveletek
6.3.1.1. Amire figyelni kell: a fordító optimalizálása
6.3.2. Logikai műveletek
6.3.3. Összehasonlító (relációs) műveletek
6.3.4. Műveletek mutatókkal
6.3.5. Halmazműveletek
7. A Pascal nyelv utasításai
7.1. Egyszerű utasítások
7.1.1. Az üres utasítás
7.1.2. Az értékadó utasítás
7.1.3. A GOTO utasítás
7.1.4. Eljáráshívás
7.2. Strukturált utasítások
7.2.1. Összetett utasítás (blokk)
7.2.2. Feltételes utasítások
7.2.3. Ciklusutasítások
7.2.4. A WITH utasítás

8. Alapvető I/O műveletek
8.1. Írás képernyőre - a WRITE és WRITELN eljárások
8.1.1. Szöveg kiírása a képernyőre
8.1.2. Egész típusú adatok megjelenítése
8.1.3. Valós értékek kiírása
8.1.4. Logikai értékek kiírása
8.1.5. Kimeneti eszközök
8.1.6. Képernyőtörlés
8.2. Olvasás billentyűzetről - a READ és READLN eljárások
9. Eljárások és függvények
9.1 Az alprogramok helye a Pascal programban
9.2. Eljárások
9.2.1. Az eljárás paraméterei
9.2.2. A lokális deklarációk
9.2.3. Az eljárás törzse
9.2.4. Előzetes (FORWARD) deklaráció
9.3 Függvények
9.3.1. A függvények és az eljárások összehasonlítása
9.3.2. A függvények előzetes deklarációja
9.4. Rekurzív alprogramok
9.5. Az azonosítók érvényességi tartománya
10. Memóriahasználat
10.1. A mutató típus és a dinamikus változók
10.1.1 Tömb a halomterületen
10.2. A memória közvetlen elérése
10.2.1. A POKE eljárés és a PEEK függvény
10.2.2. Az ADDR és a SIZE függvény
10.3. Memóriatartalom kimentése, betöltése
10.4. Gépi kódú rutinok beépítése a Pascal programba
11. A HiSoft Pascal előre definiált függvényei
11.1. Konverziós függvények
11.2. Matematikai függvények
11.3. Sorszámozott típusra használható egyéb függvények
11.4. Egyéb függvények
11.5. Külső függvénygyűjtemény
12. Sztringek a HiSoft Pascal-ban
12.1. Sztringkezelő függvények megvalósítása
13. Kapcsolat az operációs rendszerrel
13.1 Az EXOS függvény
13.2. A MAKESTR függvény
13.3. A használt csatornák
13.4. Operációs rendszer funkciók
13.5. Grafika a HiSoft Pascal-ban
13.5.1. Írás tetszőleges videócsatornára
13.5.2. Teknőcgrafika
13.6. Hangkeltés
14. Az 1.2-es verzió
14.1. Példaprogram
A. Függelék: Hibakódok
B. Függelék: Fordítási direktívák

1. Bevezetés

Niklaus Emil Wirth svájci informatikus rész vett az ALGOL továbbfejlesztésében. Miután nézeteltérésbeli okokból elhagyta a project-et, úgy döntött, maga készít egy programozási nyelvet, elsősorban oktatási célokra. Hangsúlyozandó, programozás oktatására szánta a nyelvet: célja a helyes programozási gyakorlatok ösztönzése strukturált programozás és adatstrukturálás segítségével. Az ALGOL felhasználásával és továbbfejlesztésével készült nyelv leírása 1968-ban készült el és a Pascal nevet kapta Blaise Pascal a francia matematikus-filozófus tiszteletére. 1970-ben készült el az első fordítóprogram a nyelvhez, az erről szóló publikáció 1971-ben jelent meg. 1973-ban látott napvilágot a javított (Revised Report) kiadása. Ekkor definiálták a Standard Pascal nyelvet, amely a nyelv különböző implementációjának alapja lett. Több egyetemen is kifejlesztették a nyelv változatait, ereje azonban akkor mutatkozott meg igazán, amikor a megjelentek a személyi számítógépek. A PASCAL a '80-as évek első felének legnépszerűbb programozási nyelvévé vált.
A Hisoft cég terméke először 1983-ban jelent meg Spectrum-ra, Később készült belőle CPC és Enterprise változat is. Sajnos az eredeti dokumentációból semmit nem tudunk meg a nyelvről, így bár sok embernek megvolt az első kazettái vagy lemezei egyikén, senki nem tudta használni. Pedig a maga nemében igen figyelemreméltó implementáció! A Hisoft Pascal a standard Pascal többé-kevésbé teljes implementációja, de mivel eredetileg magnós rendszerbe készült, nincs benne file-kezelés. Saját, beépített editorral dolgozik - mely teljesen más logika mentén működik mint a Turbo Pascal keretrendszere. A nyelvi korlátokért kárpótol a lefordított program sebessége, mely gyakorlatilag gépi kódú program sebességével fut. A Hisoft Pascal közvetlenül a memóriában is fordít, a program azonnal futtatható, ugyanakkor a kész programból önállóan betölthető, futtatható kód állítható elő.
Az alábbi ismertető az utolsó hivatalos, 1.1-es verzió alapján készült. PoviSoft által módosított 1.2-es verzió újdonságait a 14. fejezetben tárgyaljuk.

2. A keretrendszer

A számítógép kikapcsolt állapotában helyezzük a PASCAL-t tartalmazó cartridge-t a "ROM BAY" feliratú portba, majd kapcsoljuk be a gépet. Attól függően, hogy melyik EPROM helyen (szegmensen) van a PASCAL-t tartalmazó EPROM megeshet, hogy nem a PASCAL jelentkezik be. Ebben az esetben adjunk ki egy ":HP" parancsot. Ha nem rendelkezünk Pascal cartridge-al, az 5-ös fejlécű futtatható változatot használhatjuk.
A PASCAL induláskor a következő képernyővel jelentkezik be:

A felül látható copyright üzenet alatt egy lista látható, a szerkesztő parancsaival. Ez egy "H" parancs kiadásával bármikor kiíratható. A szövegben található nagybetűket kell kiadnia kívánt parancs indításához. A szerkesztő parancsainak szintaktikája:

<parancs> <N1>,<N2>,<S1>,<S2>

Ahol: <parancs> mindig egy karakter (betű). A rendszer a kis és nagybetűket egyaránt felismeri. <N1> és <N2> 1 és 32767 közötti egész szám. <S1> és <S2> maximálisan 20 karakterből álló karaktersorozat. Amennyiben a két szám operandusára nincs szükség (a parancs nem kívánja), a helyüket akkor is jelezni kell üres határolójelekkel. (Általában a "," karakter. )

2.1. A szerkesztő parancsai

Általános parancsok:

HELP alak: H
 
Kiírja a már említett segítő üzenetet. Itt kell megemlíteni, hogy az F1 billentyű erre a parancsra van beprogramozva, vagyis segítségkéréshez elég leütni az 1. funkcióbillentyűt.
Width alak: W<szélesség>
 
A szerkesztő képernyőjének szélessége. 40 esetén 40 karakteres, 80 esetén 80 karakteres lesz a képernyő üzemmódja.
default Y alak: Y
 
A parancs kiadása esetén a képernyőre íródnak az alapértelmezések, a következő alakban:
<határoló>
<N1> <N2>
<S1>
<S2>
A határoló az egyes operandusok közötti elválasztót jelenti (delimiter). Ez általában a vessző, de ez megváltoztatható ( lásd a Q parancsnál ). A többi alapértelmezésről a parancsszintaktika ismertetésnél már volt szó, itt csak annyit, hogy ha számot nem írunk be, akkor az alapértelmezést felelteti meg neki. Ha viszont a parancsnál megadunk egy számot, akkor ez lesz az új alapértelmezés. Például: S 1,600,SZ1,SZ2 ekkor ezek lesznek az alapértelmezések. Ha ezután kiadunk egy üres P parancsot, akkor azt a rendszer a következővé egészíti ki: P 1,600,SZ1,SZ2. Ez nem jelenik meg a képernyőn, de így hajtódik végre.
Qdelim alak: Q,,<delimiter>
 
Beállítja az új határolójelet. Például:Q,,! Ezután az operandusokat már nem vesszővel, hanem felkiáltójellel kell egymástól elválasztani. Tehát, ha kiadnánk mégegyszer ugyanezt a parancsot, hibaüzenetet kapnánk! Visszaállítani a vessző határolójelet a Q!! paranccsal lehet.
Alter alak: A
  Használatával a COMPILER opciókat lehet átállítani. Sorban:

  • Symbol table size ( xxxx )? A szimbólumtábla mérete.
  • Translate stack ( xxxx )? A fordítási verem helye.
  • Compiler stack ( xxxx )? A COMPILER verem helye.

xxxx egy hexadecimális számot jelöl, a választ is így kell megadni. ENTER esetén nem változnak az opciók. Nem célszerű az opciók átállítása, kivéve a szimbólumtábla méretének nagyobbra állítását hosszú program esetén. A szimbólumtáblába kerülnek a programban használt változók, konstansok stb.

A szimbólum tábla mérete alapesetben 1667h byte, itt vannak eltárolva a deklarált változók, függvények és eljárások neve, típusa, stb. A táblázat kezdőcíme fix, ez az 1.1-es verzióban a 4CD1H címen kezdődik, a szimbólum tábla kezdőcímének értéke a 4A41H címen van eltárolva. Ehhez hozzáadva a szimbólumtábla méretét (alapesetben a 1667H-et), megkapjuk a forráskódunk kezdőcímét: 6338H (aminek értéke egyébként a 48A3 címen van eltárolva).
A compiler és a translate stack a fordításkor (memóriába, ill. lemezre) beállított verem értékét mutatja. Ez a cím ugye a verem tetejét mutatja, tehát az első elem a 0FFFh címen lesz (3-as lap teteje). A változóknak is ide foglal memóriát a Pascal. Nézzünk egy példát:

VAR I:INTEGER;
    CH:CHAR;

  • A fenti deklaráció esetében, mivel az integer 2 byte-ot foglal, az I változónk a 0FFFEH és 0FFFFH címen lesz eltárolva (erről meggyőződhetünk az ADDR(I) függvénnyel is).
  • A char egy byte-ot foglal, ezért a CH változónk a 0FFFDH címen lesz tárolva.
  • Ez alatt kezdődik a verem (vagyis a 0FFFCH címtől lefelé).

Ha a compiler, vagy a translate stack-et átírjuk, akkor ott lesz a verem (és a változóknak foglalt hely) teteje, tehát, ha pl. C000h-ra írjuk, akkor pl. az elsőként deklarált 1 byte-os változónk a 0BFFF címen lesz, vagyis a 3-as lapunkat nem használja a Pascal, ezért szabadon lapozható.

Input / Output műveletek:

Get text alak: G,,<flie-név>
  A <file-név> nevű file-t megkeresi, majd betölti a szerkesztő munkaterületére. Ha már volt program a szerkesztőben, az újonnan betöltött programsorokat a legutolsó meglévő sor után helyezi el. Például:

G,,prog.pas
G,,TAPE:program

Put text alak: P<sorsz1>,<sorsz2>,<file-név>
  Az előző parancs megfordítása, kimenti a forrásszöveg egy részét. A kimentendő blokk kezdősorszámát a <sorsz1>, a végsorszámát a <sorsz2> adja meg. Például:

P1,123,program

Kimenti az 1. sorszámtól a 123. sorszámig PROGRAM néven a forrásszöveg egy részét. Itt említjük meg, hogy a PASCAL-ban alkalmazható legnagyobb sorszám a 32767, így a teljes forrásszöveg kimentése a P1,32767,<file-név> paranccsal történhet.
Verify alak: V<sorsz1>,<sorsz2>,<file-név>
 
A P paranccsal kimentett forrásszöveget ellenőrzi. Amennyiben megelőzte egy forrásszöveg kimentés, akkor elhagyhatók az operandusok. Ekkor összehasonlítja a MYPROG nevű file-t a memóriában találhatóval. Ha megegyezik, akkor a "VERIFIED" ha nem, akkor a "FAILED" hibaüzenet jelenik meg.
load eXos module alak: X,,<file-név>
  Betölti a <file-név> nevű file-t, amennyiben az EXOS modul. Ellenkező esetben hibaüzenetet ad.
Például:

X,,gen

Betölti a GEN nevű programot. Itt érdemes megjegyezni, hogy a ":" (kettőspont) után tetszőleges rendszer-parancsot írva a gép végrehajtja azt! (Pl.: :DIR)

Szövegszerkesztési parancsok:

List alak: L<sorsz1>,<sorsz2>
 
Kilistázza a <sorsz1> sorszámtól a <sorsz2> sorszámig terjedő programsorokat. Hasonlít a BASIC LIST parancsához. Ha nem adunk meg paramétert, az egész programot kilistázza. Például: L 1,100
Insert I<sorsz1>,<lépésköz>
  Beszúrást kezdeményez a <sorsz1> sorszámú sortól kezdve <lépésköz> növekménnyel. Ha már van ilyen sor, akkor az nem törlődik, hanem eggyel nagyobb sorszámot kap. Ha szükséges, a mögötte lévő sorokat is átsorszámozza. Például:

I10,10

Automatikus sorszámozás 10-től kezdve, 10-es lépésközzel. A szolgáltatást a STOP billentyű megnyomásával kapcsolhatjuk ki. Némiképp emlékeztet a BASIC AUTO parancsára.
Edit alak: E<sorsz>
  Kiírja a <sorsz> sorszámú programsort, amit javítani lehet. Például: E123 Lehívja a 123-as sort.
Delete alak: D<sorsz1>,<sorsz2>
  Törli a forrásszövegben a <sorsz1> sorszámtól a <sorsz2> sorszámig tartó szövegblokk valamennyi sorát. Ebben a parancsban (a véletlen törlések kiküszöbölésére) mindkét sorszámot meg kell adni. Például:

D1,32767

Törli a teljes forrásszöveget. Megfelel a BASIC DELETE parancsának.

reNumber alak: N<sorsz>,<lépésköz>
  Átsorszámozza a teljes forrásszöveget úgy, hogy a kezdősorszám a <sorsz>, a növekmény a <lépésköz> lesz. Hatása azonos a BASIC RENUMBER parancsával. Például:

N1,3

Az első sorszám az 1, a második a 4 lesz, stb.

Upper line alak: U
  Kiírja a forrásszövegben előforduló legnagyobb sorszámot.
Buffer left alak: B
  Kiírja a memóriában még szabadon lévő byteok számát ( hexadecimálisan ).

Blokkműveletek:

Move alak: M<sorsz1>,<sorsz2>,<cél>
  Átmozgatja a <sorsz1> sorszámtól a <sorsz2> sorszámig terjedő blokkot a <cél> sorszámú helyre úgy, hogy az eredeti helyen lévő blokk törlődik. Például:

M2,5,10

A 2...5 sorokat átmozgatja a 10 sortól kezdődően, majd törli a 2...5 sorokat, így a 2...5 sorok a 10...13 sorok helyén lesznek.

Keresés és szövegkicserélés:

Find alak: F<sorsz1>,<sorsz2>,<szöveg>
  A <sorsz1> sorszámtól kezdődő és a <sorsz2> sorszámig tartó blokkban keresi a <szöveg> szöveget. Ha megtalálta kiírja azt a sort, ahol sikeres volt a keresés. Ez a parancs az, ahol érdemes a határolójelet megváltoztatni abban az esetben, ha a vessző karakter is szerepel a keresni kívánt szövegben. Például:

F1,32767,begin

Substitute alak: S<sorsz1>,<sorsz2>,<szöveg1>,<szöveg2>
  A <sorsz1> és <sorsz2> által megadott blokkban kicseréli a <szöveg1> stringet <szöveg2>-re. Amennyiben talált <szöveg1>-et, akkor kiírja a sort a képernyőre. Az ENTER billentyű leütésekor kicseréli a stringeket, majd folytatja a keresést. Például:

S1,32767,begn,begin

Fordítás, futtatás:

Compile alak: C
 
Lefordítja a szöveg-pufferben lévő forrásszöveget. Hiba esetén kiírja az aktuális sort, és megjelöli a hibás részt. Hibátlan fordítás esetén engedélyt kér a futtatásra (Run?), amire "Y" választ adva lefuttatja a programot, egyéb válasz esetén visszatér parancs üzemmódba.
Run alak: R
  Ha a memóriában van lefordított program, lefuttatja azt. Egyébként hatástalan.
Translate alak: T,,<file-név>
 
Lefordítja a forrásszöveget, de nem futtatható formában, hanem létrehoz egy <file-én> nevű programot, ami egy 5-ös formátumú EXOS modul. Ez a kimentett program később visszatölthető, futásához nem szükséges a PASCAL COMPILER-nek a rendszerben lennie.
A T(ranslate) paranccsal lefordított és kimentett program, futás közben előforduló hibák esetén, vagy a programfutás befejeztével visszatér egy primitív parancsértelmezőbe. Először megjelenik a "Run?" kérdés, erre "Y"-t adva válaszként a program újra lefut. Minden más karakter esetén további karakter leütésekre vár, kivéve a ":" (kettőspont) karaktert, amikor utána egy EXOS parancs stringet lehet beírni, így ezt használhatjuk kilépésre is.
Ha kevés a memória, érdemes a programot 5-ös fejlécű programként futtatni, mert ha a HiSoft Pascalból ROM-verziót használunk, akkor is induláskor RAM-ba másolja magát (Spectrum-os örökség).

2.2. A szövegszerkesztő
A PASCAL nyelven megírt programot (a továbbiakban: forrásszöveg) a PASCAL saját szövegszerkesztőjével írhatjuk be. Szerkesztés és szövegbevitel közben az EXOS megszokott szerkesztő funkcióit használhatjuk. Az egyes sorokat itt - a Pascal implementációktól eltérően és a BASIC változatokhoz hasonlóan - sorszámozzuk. A programsorokat így tetszőleges sorrendben írhatjuk be - hasonlóan a BASIC-hez - és a sorok végén az ENTER megnyomásával le kell zárni a sor bevitelét. Ha egy üres sorszámot írunk be, akkor az adott sor törlődik (feltéve, ha létezik). Ha egy üres sort szeretnénk beszúrni, legalább egy szóközt kell írni a sorszám után. A sor elején (sorszám és az első utasítás között) tetszőleges számú szóközt helyezhetünk el tagolandó a programunkat, de programhiba miatt, ha 13 szóköz van a sor elején, a sort rosszul tokenizálja a HiSoft Pascal (a sor elején álló 13 db szóközt sorvége jelnek értelmezi)!
A sorszám nem része a forrásnyelvi programnak. Bármilyen lépésközzel is sorszámozzuk a programot, a következő betöltéskor újra 1-től kezdve folyamatos sorszámozással listázza ki a szerkesztő az elmentett programot.
A forráskód kezdőcímét a 48A3H-n lévő word típusú érték mondja meg, ez a 6338H címen kezdődik. A sorok a következőképpen épülnek fel:

2.3. A Pascal előre programozott funkcióbillentyűi
A HiSoft Pascal-ban a BASIC rendszerhez hasonlóan 8 funkcióbillentyűnek előre programozott funkciójuk van:

  Normál SHIFT + funcióbillentyű
F1 HELP READ (
F2 PROGRAM WRITE (
F3 CONST INTEGER
F4 TYPE WRITELN
F5 VAR WHILE
F6 BEGIN UNTIL
F7 END REPEAT
F8 PROCEDURE FUNCTION

3. A HiSoft Pascal nyelvi elemei

3.1. A nyelv jelkészlete
A HiSoft Pascal az ASCII karakterkészletből az alábbi szimbólumokat használja:

Minden esetben, amikor a szintaktika megköveteli a kettőskereszt (#) használatát, angol billentyűzetű gépen a fontjelet (£) kell helyette használni, így a továbbiakban mi is erre hivatkozunk.
A Pascal-ban bizonyos karakterpárok speciális jelentést hordoznak:

:=   <=   >=   <>   ..   (*   *)

Az ilyen speciális szimbólumok esetén fontos a karakterek megadási sorrendje. A nyelv nem engedi meg az =<, =>, ><, stb. szimbólumok használatát. Hibajelzést kapunk akkor is, ha ezekben a szimbólumokban a karakterek közé szóközt teszünk.

3.2. A foglalt szavak
A foglalt (fenntartott) szavaknak a Pascal nyelv utasításaiban és deklarációiban szereplő kulcsszavakat nevezzük, vagyis a nyelv szókincsét. A foglalt szavakat csak a nyelvben rögzített szabályoknak megfelelően szabad használni.
Az alábbiakban összefoglaltuk a HiSoft Pascal nyelv foglalt szavait:

AND ARRAY BEGIN CASE CONST
DIV DO DOWNTO ELSE END
FOR FORWARD FUNCTION GOTO IF
IN LABEL MOD NIL NOT
OF OR PACKED PROCEDURE PROGRAM
RECORD REPEAT RA RB RC
RD RBC RDE SET THEN
TO TYPE UNTIL VAR WHILE
WITH        

A szabványos Pascal PACKED kulcsszava a HiSoft Pascal-ban hatástalan.
A foglalt szavak mellett a HiSoft Pascal nyelv ún. előre definiált azonosítókat is tartalmaz. A foglalt szavakkal ellentétben az azonosítókat saját névként is használhatjuk a programban, bár ez nem javasolt. Az előre definiált azonosítókat külön táblázatban foglaljuk össze:

Eljárás: HALT INLINE MARK NEW
  OUT PAGE POKE RANDSEED
  READ READLN RELEASE TIN
  TOUT USER WRITE WRITELN
Fügvény: ABS ADDR ARCTAN CHR
  COS ENTIER EOLN EXOS
  EXP FRAC INCH INP
  LN MAKESTR ODD ORD
  PEEK PRED RANDOM ROUND
  SIN SIZE SQR SQRT
  SUCC TAN TRUNC  

A HiSoft Pascal megkülönbözteti a kis- és a nagybetűket, a programban a foglalt szavakat és az előre definiált azonosítókat csak nagybetűvel írhatjuk.

3.3. A program utasításai és sorai
A Pascal program az élő nyelvekhez hasonlóan mondatokból épül fel, amelyeket a programozási nyelvekben utasításoknak nevezünk. Az utasításokat pontosvesszővel (;) zárjuk. Kivételt képez a programot lezáró end utasítás, amely után pontot (.) kell tenni.
Az utasításokat a program sorai tartalmazzák. A Pascal-ban egyetlen utasítást több sorba is írhatunk, illetve egy sorban akár több utasítást is megadhatunk. A program áttekinthetősége érdekében azonban ajánlott minden egyes utasítást külön sorban elhelyezni.
A foglalt szavakon kívül az utasítások még számos más összetevőt is tartalmaznak. Ezért mielőtt az utasításokat ismertetnénk, meg kell ismerkednünk az azonosítók fogalmával, különböző típusú számkonstansok ábrázolásával, a műveletek végrehajtásához szükséges operátorokkal és a program működését magyarázó megjegyzésekkel.

3.4. Azonosítók
A Pascal programban az általunk létrehozott elemeknek (változóknak, konstansoknak, típusoknak, eljárásoknak, függvényeknek, a programnak és a rekordmezőknek) nevet kell adni, hogy hivatkozni tudjunk rájuk. Természetesen a névnek egyedinek kell lennie, például nem adhatjuk ugyanazt a nevet egy változónak és egy függvénynek. Ha ez mégis megtörténne (sajnos) nem kapunk hibaüzenetet, hanem az utoljára definiált elem eltakarja a korábbi definíciót. Az általunk megadott neveket (azonosítókat) a program különböző összetevőinek azonosítására használjuk.
Az azonosítók bármilyen hosszúak lehetnek, azonban csak az első 10 karaktert veszi figyelembe a fordítóprogram. Vagyis az első 10 karakter alapján minden azonosítónak egyedinek kell lennie. A név képzésekor célszerű törekedni arra, hogy az azonosítók nevei utaljanak a bennük tárolt tartalomra.
Az azonosítók képzése során nem használhatjuk a teljes ASCII karakterkészletet. Mint mindennek, a névalkotásnak is vannak saját szabályai. Az azonosítónak betűvel kell kezdődnie és nem tartalmazhat szóközt. Az első karakter után további betűk, számok következhetnek. A Hisoft Pascal az azonosítókban megkülönbözteti a kis- és nagybetűket, ezért pl. az 'SZAM' és 'Szam' azonosítók különböző elemeket jelölnek.

3.5. Számok
A Pascal nyelvben egész és valós számokat egyaránt használhatunk. A programban elhelyezett számértékeket számkonstansnak nevezzük, melyek megadására a matematikától némileg eltérő szabályok vonatkoznak.
Egész szám megadása az előjellel (a pozitív előjel elhagyható) és az azt követő számjegyekkel történik:

2 -3 +10 1992

Egész szám hexadecimális számrendszerben is megadható a £0000 és £7FFF értékhatárokon belül. A szám előtt álló £ jel jelzi, hogy hexadecimális megadást használunk.
Valós számokat tizedes törtként (például 123.45) és hatványkitevős alakban (1.2345E2) egyaránt megadhatunk. (A számok megadásakor nem tizedesveszőt, hanem tizedespontot használunk.) A hatványkitevős konstansok az alábbi elemekből épülnek fel:

A megadásban a hatvány jel (az E betű) 10 hatványát jelöli (a kitevőben a pozitív előjelet nem kötelező használni). Az alábbi példákban a különböző számmegadási módokat mutatjuk be:

-12.56E-5 = -0.0001256 = -1.25600E-04
2.3E+3 = 2300 = 2.3E3 = 2.30000E+03

Ha elhagyjuk az előjelet, akkor a szám pozitív lesz. A valós számban a tizedespont és a hatványrész közül legalább az egyiknek szerepelnie kell. Az egész részt mindig meg kell adnunk, míg a törtrész el is hagyható.

3.6. Szövegkonstansok
A szövegkonstanst (sztringkonstanst) egyszeres idézőjelek, aposztrófok (') között kell megadni. Ha szövegen belül aposztróf szerepel, akkor azt duplázni kell (például 'Rock"n"roll'). Ha a sztring semmit sem tartalmaz az aposztrófok között ("), akkor ún. üres sztringről beszélünk. HiSoft Pascal-ban az üres szting jelölésére nem alkalmazható a '' jelölés, helyette a CHR(0) használható. A sztringekben nem használhatók a 128-as CHR-kód fölötti karakterek (vagyis a grafikus karakterek).
A sztring hossza az aposztrófok között megadott karakterek számának felel meg. Például, az

'ez egy sztring'

karaktersorozat hossza 14 karakter.

3.7. Címkék
A Pascal programban a címkék az utasítások megjelölésére szolgálnak. A címkéket a később bemutatásra kerülő goto utasításban használjuk. (A goto utasítás megszakítja a program végrehajtásának folyamatát, és a vezérlést átadja az utasításban megadott címkével megjelölt utasításra.)
A programban szereplő címkéket szintén deklarálni kell. Ehhez a címkéket a deklarációs részben a label kulcsszó után kell felsorolnunk. A címke számokból álló szekvencia (0-32767), ahol a kezdő nullákat figyelmen kívül hagyja a rendszer (tehát a 0999 és a 999 ugyanazt a címkét jelöli). A címke nem keverendő össze a programsor számával!

3.8. Operandusok
A kifejezésekben szereplő változókat, konstansokat (számokat, sztringeket, stb.) és függvényhívásokat operandusoknak nevezzük.

A+B*C/SQR(C)+1.34

A fenti aritmetikai kifejezés operandusai: az A, B, C és X változók, az SQR függvényhívás (négyzetre emelő függvény), valamint az 1.34 számkonstans.

3.9. Operátorok
A kifejezésekben szereplő változókat, konstansokat és függvényhívásokat összekapcsoló műveleti jeleket operátoroknak nevezzük.
Az operátorok jelölhetnek aritmetikai (+, -, /, *, stb.) és logikai műveleteket (and, or, stb.), illetve relációkat (<, >, <=, stb.). Az operátorokkal és a műveletek közötti elsőbbségi szabályokkal a későbbiekben részletesen foglalkozunk.

A+B*C/SQR(C)+1.34

A fenti aritmetikai kifejezés operátorai: a +, *, / aritmetikai műveleti jelek.

3.10. Kifejezések
A kifejezés operátorok és operandusok sorozatából épül fel. A kifejezéseket két alapvető csoportját az aritmetikai (A+3.2*SIN(Y)) és a logikai kifejezések ((X< 2) AND (Y> 3)) alkotják. Az aritmetikai kifejezés kiértékelésének eredménye mindig egy számérték, míg a logikai kifejezés értéke egy logikai érték (ami igaz vagy hamis).

3.11. Megjegyzések
A program utasításai között bárhol tetszőleges hosszúságú megjegyzést, magyarázatot helyezhetünk el. Megjegyzéseket az alábbi két módszer közül választva adhatunk meg:

A Pascal programban a szóközt, a sorvéget és a megjegyzéseket tagolójelként használjuk. Az azonosítók, a számok és a speciális szimbólumok kivételével a programban bárhol tetszőleges számú tagolójel állhat.
A megfelelő helyeken elhelyezett megjegyzések növelik a program olvashatóságát és kényelmessé teszik a későbbi időpontban történő javítását és módosítását. A megjegyzések használatával a program dokumentációját is kiegészíthetjük.

4. A Pascal program szerkezete
Minden Pascal nyelven írt program három fő részre tagolható. A programfejet, a deklarációs (definíciós) részt és a főprogram törzsét a felsorolás sorrendjében kell a forrásprogramban elhelyezni. Az alábbi ábrán a Pascal programok általános szerkezete látható. (Az ábrán feltüntettük az egyes programrészekben használt foglalt szavakat is.)

{ programfej }
   { globális hatású fordítási direktívák }
   PROGRAM programnév;

{ deklarációs rész }
   { lokális hatású fordítási direktívák }
   LABEL { címkék deklarációja };
   CONST { konstansok deklarációja };
   TYPE { típusok deklarációja };
   VAR { változók deklarációja };

   PROCEDURE eljárásnév(paraméterek);
   {lokális deklarációk}
   BEGIN
      { az eljárás törzsének utasításai };
   END;

   FUNCTION függvénynév(paraméterek);
   {lokális deklarációk}
   BEGIN
      { az függvény törzsének utasításai };
      függvénynév:=utasítás { a függvény értéke }
   END;

{ programtörzs }
   BEGIN
      { globális hatású fordítási direktívák }
      { a főprogram blokkjának utasításai };
   END.

A Pascal program feje a PROGRAM foglalt szóból és a programnévből áll. A programnév képzésére vonatkozó szabályok teljes mértékben megegyeznek az azonosítóknál ismertetett szabályokkal. Például:

PROGRAM PROBA;

A programfejet a deklarációs rész követi, ahol mindent olyan azonosítót fel kell felsorolnunk, amit a programunk használ.
A deklarációs részben definiálhatjuk a főprogram által használt címkéket (LABEL) és konstansokat (CONST). A Pascal-ban a szabványos típusokon kívül saját típusokat is létrehozhatunk (TYPE). A deklarációs részben definiált változók (VAR) globálisak a teljes programra (főmodulra) nézve. A változók lehetséges típusait a következő fejezetben ismertetjük.
Szintén a deklarációs részhez tartoznak a felhasználó által készített alprogramok (eljárások és függvények.) Ezek felépítése hasonló a főprograméhoz, mivel szintén fejrészből, deklarációkból és végrehajtható utasításokból (utasításblokkból) állnak. Lényeges különbség azonban, hogy az alprogramban deklarált ún. lokális változókat csak az alprogramból használhatjuk.
A LABEL, CONST, VAR definíciós szavakkal kijelölt szekciók csak a felsorolt sorrendben szerepelhetnek a deklarációs részben, utánuk a függvény- és eljárásdeklarációk tetszőleges sorrendben következhetnek.

A program deklarációs része ott fejeződik be, ahol a főprogram törzse (blokkja) kezdődik. A programtörzs a BEGIN és az END. szavak között végrehajtható utasításokat tartalmaz. (A Pascal nyelv utasításait a 7. fejezet ismerteti.)
A programban az ún. fordítási direktívák megadásával a fordító működését vezérelhetjük. A direktívák hatása kiterjedhet a teljes programra (globális), vagy a program adott részére (lokális).
A legegyszerűbb Pascal programokban a deklarációs rész hiányozhat, azonban a programfejet és program törzsét mindig meg kell adni. A legrövidebb Pascal program - amely még semmit nem csinál -, tehát a következő:

PROGRAM MIN;
BEGIN
END.

5. Adattípusok és deklarációk

Számítógépes programok többsége a futása során megadott adatokat valamilyen módon feldolgozza és megjeleníti az eredményeket. A program által kezelt adatok sokfélék lehetnek (számok, szövegek, stb.). A programban az adatokat változókban tároljuk. Az adattípus határozza meg, hogy egy változó milyen értéket vehet fel és milyen műveletek végezhetők rajta. A változók számára a fordítóprogram egy vagy több bájtot foglal le a memóriában. Ez a tárterület tartalmazza a változó aktuális értékét, ami a program futása során meg is változhat. Ha megvizsgáljuk egy változó tartalmát, akkor pillanatnyi (aktuális) értékről beszélünk. A programban a változókra névvel hivatkozunk, ezért minden változót azonosítóval kell ellátni. Az ún. változóazonosítók kialakítására az azonosítókra vonatkozó szabályok érvényesek.
Minden változóhoz tartozik egy adattípus (röviden tipus), amely előírja a változó számára lefoglalandó memóriaterület nagyságát és a változóban tárolt adat értelmezését. A típus azt is meghatározza, hegy egy változó milyen értéket vehet fel. A változó nevének és típusának összerendelését a változó deklarációjának (leírásának) nevezzük. A Pascal programban minden változót (de csakis egyszer) deklarálni kell. A deklarálást a program deklarációs részében a VAR foglalt szó után végezhetjük el. A deklarációban a változó nevét kettősponttal elválasztva követi a változó típusa, mely után pontosvessző áll. Ha egy típushoz több változó tartozik, akkor azok neveit vesszővel elválasztva kell felsorolni.

VAR A,B:INTEGER;
    X:REAL;

A HiSoft Pascal többféle beépített adattípussal rendelkezik, melyek segítségével magunk is létrehozhatunk típusokat. A HiSoft Pascal nyelvben használatos adattípusokat az alábbiakban foglaltuk össze:

5.1. Egyszerű adattípusok
Az egyszerű adattípusok közös jellemzője, hogy az ilyen típusú változók egyszerre csak egyetlen adatot (egy számot, egy karaktert) tárolnak.

5.1.1. Numerikus adatok tárolása
Először azokkal az egyszerű adattípusokkal foglalkozunk, amelyek numerikus adatokat, számokat tárolnak. Numerikus adatokat tároló változókkal aritmetikai műveleteket végezhetünk.

Egész típus
A Pascal programokban leggyakrabban az INTEGER típust használjuk, amelyben a -32768..32767 számtartományba eső egész számokat tárolhatjuk. A HiSoft Pascal az egész típusú számokat 2 byte-on tárolja, kettes komplemens formátumban.
A programunk tervezésénél gondosan kell megválasztani a változók típusát, mivel nem megfelelő típus esetén a program helytelen eredményt adhat. Például, a következő programrészletet végrehajtva a program futása a C:=A*B utasításnál megszakad túlcsordulás (OVERFLOW) hibával:

VAR A,B,C:INTEGER;
BEGIN
  A:=500;B:=300;
  C:=A*B;
END.

A hiba oka, hogy a szorzással kilépünk az integer tartományból (túlcsordul a szorzat). Ha előre tudjuk, hogy nagyobb egész számokkal is dolgozunk, akkor mind a három változó típusát ajánlott valósra cserélni.
A HiSoft Pascal a legnagyobb integer típusú számot (32767) a MAXINT konstansban tárolja.

Az alábbi programmal bármely Pascal implementációban meghatározhatjuk, hány biten ábrázolja az egész típusú számokat:

PROGRAM INTEGER;
VAR ERTEK,MERET:INTEGER;
BEGIN
  ERTEK:=MAXINT;MERET:=1;
  REPEAT
    ERTEK:=ERTEK DIV 2;
    MERET:=MERET+1
  UNTIL ERTEK=0;
  WRITELN('AZ EGESZ SZAMOK MERETE = ',MERET,' BIT.')
END.

Bár a korabeli implementációk nem ismerték a WORD típust (mely előjel nélküli 16 bites egész szám tárolására alkalmas 0..65535 intervallumban), ha egy programrészletben nincs szükség negatív egész szám tárolására, a HiSoft Pascal INTEGER típusát "átalakíthatjuk" WORD típussá. Ehhez mindössze két utasítást kell elhelyezni a programban:

POKE(£05BE,CHR(£18));
POKE(£05E7,CHR(£18));

Az első POKE csak akkor kell, ha formázott kiírást akarunk (vezető space-ekkel), különben elhagyható.
Szükséges továbbá a túlcsordulás ellenőrzés kikapcsolása és szükséges lehet a relációs műveletek kiértékelő algoritmusának beállítása (részletesebben lásd B függelék):

{$O+ }
{$I+ }

Ez esetben a 32767-nél nagyobb konstansokat csak hexadecimális alakban lehet megadni!
Az eredeti állapotot (INTEGER típust) a

POKE(£05BE,CHR(£28));
POKE(£05E7,CHR(£28));

utasításokkal lehet visszaállítani. Így egy programban - pl. különböző eljárásokon belül - szükség szerint vegyesen használhatunk INTEGER vagy WORD típust.
Példaprogram:

PROGRAM WORDTYPE;
VAR A,B,C:INTEGER;
{$O+ }
{$I+ }
BEGIN
  POKE(£05BE,CHR(£18));
  POKE(£05E7,CHR(£18));
  A:=£9C40; B:=15000;
  C:=A+B;
  WRITELN(A,'+',B,'=',C);
  POKE(£05BE,CHR(£28));
  POKE(£05E7,CHR(£28))
END.

Mivel 9C40H = 40000, C értéke 55000 lesz.

Valós típus
Ha nem egész, számokat , vagy az integer típus ábrázolási tartományán kívüli egész számokat kell tárolnunk, a valós (REAL) típust kell használnunk. A valós számokat 32 biten (8 bit karakterisztika, 24 bit a mantissza, ebből 1 bit az előjel) tárolja a HisSoft Pascal. A HiSoft Pascal még az IEEE754 szabvány előtt készült, tehát nem kompatibilis a single formátummal (ami szintén 4 byte-os). A kitevőt 1 byte-on tárolja, 2-es komplemens alakban, értéke -127..127-ig lehet (a -128-ra megáll overflow hibával). Az előjelbit (H regiszter 7. bitje), és marad 23 bit a mantisszára. Viszont nem használható mind a 2^23 variáció, mert ha a mantissza legfelső bitje (a H regiszter 6. bitje) 0, akkor az egész szám 0, függetlenül attól, hogy mi a többi számjegy. A legnagyobb ábrázolható valós szám: 3.4E38, a legkisebb (még) ábrázolható: 5.9E-39
A HiSoft Pascal gyorsaságának egyik oka épp az, hogy míg a Turbo Pascal 6 bájton tárolja a valós számokat, a HiSoft Pascal csak 4 bájton, vagyis a HiSoft Pascal gyorsabban számol, de kevésbé pontosan. Pl. a négyzetgyök számítás: a HiSoft és a Turbo Pascal is babilóniai módszerrel (Heron-eljárás) számítja, de a a HiSoft Pascal csak fixen 4 lépést használ, és már az is elegendő ahhoz a pontossághoz, amit 4 byte-os lebegőpontos számábrázolással el lehet érni.

Bármely Pascal implementáció valós ábrázolási pontosságát és a mantissza bitjeinek számát meghatározhatjuk az alábbi programmal:

PROGRAM REALPONTOSSAG;
VAR R:REAL;
B:INTEGER;
BEGIN
  R:=1/2;B:=0;
  REPEAT
    R:=R/2;B:=B+1;
  UNTIL 1+R=1;
  WRITELN('RELATIV PONTOSSAG: ',2*R);
  WRITELN('MANTISSZA BITJEINEK SZAMA: ',B);
  WRITELN('ERTEKES DECIMALIS SZAMJEGYEK SZAMA LEGFELJEBB:',
           TRUNC(-LN(R+R)/LN(10)))
END.

5.1.2. Logikai információk tárolása
A logikai típusú változókat logikai értékek tárolására és logikai műveletek végzésére használjuk. A szabványos Pascal a BOOLEAN logikai típust használja erre a célra, melynek tárolására egy bájtot használ fel (melyben egyetlen biten ábrázolja a logikai értéket). A logikai változók csak kétféle értéket vehetnek fel: TRUE (igaz) és FALSE (hamis).

5.1.3. Szöveges információk tárolása
A standard Pascal-ban (így a HiSoft Pascal-ban is) egyetlen előre definiált típus használható szöveges információk tárolására: a CHAR típus. Az egybájtos char típusú változók csak egyetlenegy karakter tárolására alkalmasak. A char típus sorszámozott típus. Minden karakterhez tartozik egy sorszám (a karakter kódja: 0..255), amely kijelöli a karakter helyét az ASCII kódtáblában. Például az alábbi példában a C1 és C2 változók értéke ugyanaz lesz, mert az 'A' betű kódja 65:

VAR C1,C2:CHAR;
BEGIN
  C1:='A';
  C2:=CHR(65)
END.

5.1.4. Sorszámozott típusok
Az előzőekben megismerkedtünk a skalár adattípussal. Az egyszerű adattípusokat tovább osztályozhatjuk sorszámozott illetve nem sorszámozott típusokra.
A fentiekben bemutatott egyszerű típusok közül a valós (real) típus nem tartozik a sorszámozott típusok csoportjába. Ennek oka, hogy ezen típus értékkészlete és az egész számok között nem alakítható ki egyértelmű összerendelés.
Sorszámozott típusok közé tartoznak a már megismert integer, boolean és char típusok. Ebbe a csoportba soroljuk még a felsorolt típust és a bármelyik sorszámozott típus elemeiből felépített résztartománytípust.

Ismerkedjünk meg három olyan függvénnyel (ORD, SUCC, PRED), amelyek segítségével megvizsgálhatjuk a sorszámozott típusú konstansok és változók értékét.
A sorszámozott típusú értékhez mindig rendelhető egy egész szám, amely megadja az érték helyét a típushoz tartozó értékkészletben. Az ORD függvény a megadott paraméterértékhez rendelt sorszámot szolgáltatja. Nézzünk néhány példát az ORD függvény használatára!

ORD(FALSE)
ORD(TRUE)
ORD(26)
ORD('A')
= 0
= 1
= 26
= 65

A SUCC függvény segítségével a típus értékkészletéből megkapjuk a bemenő paraméter értéke után következő (eggyel nagyobb sorszámú) értéket. Ezzel szemben a PRED függvény megadja a paraméter értéke előtt álló (tőle eggyel kisebb sorszámú) értéket. Az alábbi példákból jól látható a két függvény működése:

SUCC(FALSE)
PRED(TRUE)
SUCC('C')
PRED('B')
SUCC(7)
PRED(30)
= TRUE
= FALSE
= 'D'
= 'A'
= 8
= 29

A SUCC és a PRED függvények azonos kódra fordulnak, mint az integer+1 és az integer-1 kifejezések:

ld hl,(variable)
inc hl
ld (variable),hl

tehát pl. az i:=i+1 azonos idő alatt fut le, mint a i:=succ(i), időmegtakarítás - mivel mindkét esetben a lehető leggyorsabb kódot állítja elő a fordító - nem érhető el.

A felsorolt típus
A Pascal-ban lehetőség van arra, hogy magunk is létrehozzunk sorszámozott típust. Ilyen, ún. felsorolt típust úgy készíthetünk, hogy kerek zárójelben, vesszővel elválasztva felsoroljuk az egyedi azonosítókat. Ezek az azonosítók alkotják a típus értékkészletét. A felsorolás egyben nagyság szerinti sorrendet is jelent, ahol az első elem sorszáma 0. (Az egyedi azonosítókat nem szabad aposztrófok közé tenni, mivel ezek nem sztringet jelölnek.)

VAR NYELVEK:(ANGOL,NEMET,SPANYOL,OLASZ);

A fenti deklaráció esetén az ORD föggvény érékei:

ORD(ANGOL)
ORD(SPANYOL)
= 0
= 2

A felsorolt típus értékkészletét definiáló szimbólumoknak egyedieknek kell lenniük, ugyanaz a név nem szerepelhet két különböző típus elemeként (ellenkező esetben az utolsó deklaráció eltakarja a korábbiakat). Erre a szabályra a műveletek egyértelműsége miatt van szükség.

A résztartománytípus
Résztartománytípust bármely sorszámozott típus tetszőleges értéksorozatával definiálhatunk. A definícióban a részsorozatot, mint intervallumot, az alsó és a felső határral adjuk meg. A határokat két ponttal (..) választjuk el egymástól.

VAR A:0..255;
    KISBETU:'a'..'z';

A legtöbb Pascal implementációban a résztartomány típusok értékhatár ellenőrzése ki van kapcsolva, mert a vizsgálat lassítja a programvégrehajtást. A HiSoft Pascal-ban azonban nincs is olyan direktíva, amellyel ezt bekapcsolhatnánk. A HiSoft Pacal-ban csak a kompatibilitás miatt van jelentősége a résztartomány típusnak.

5.2. Mutatótípusok
A számítógép memóriája a programok egyik legértékesebb erőforrása. A memória jobb kihasználása érdekében a programozási nyelvek többsége lehetőséget biztosít az ún. dinamikus tárkezelésre. Ennek során nem a fordítóprogram, hanem a program készítője gondoskodik helyről a (dinamikus) változók számára. A dinamikus memóriakezelés a mutatók (pointerek) használatára épül.
A mutatót a ^típus alakú típusleírás segítségével deklarálhatunk:

VAR PTR:^INTEGER;

5.3. Strukturált típusok
A strukturált (összetett) típusok közös jellemzője, hogy más típusokból (ún. komponenstípusból) épülnek fel. Az összetett típusú változók egynél több komponensértéket is tárolhatnak. A Hisoft Pascal összetett típusai: a tömb, a rekord, és a halmaz adattípusok.

5.3.1. Tömbtípusok
A tömb adott számú, azonos típusú elemet tartalmazó adattípus. A tömb számára a memóriában lefoglalt terület méretét az elemszám és az elem típusának megfelelő elemméret szorzatával számíthatjuk ki. Például, egy 10 elemű integer (2 bájt) típusú tömb számára 10x2, azaz 20 bájtot foglal le a fordítóprogram.
A tömbtípust a deklarációkban az ARRAY foglalt szó vezeti be. Ezt szögletes zárójelek között követi az indexek típusa (ami általában résztartomány). A típusleírást az OF foglalt szó utáni elemtípus zárja. Példaként nézzünk néhány tömbdeklarációt!

VAR ABC[1..26] OF CHAR;
    NUM:ARRAY['A'..'Z'] OF INTEGER;
    TABLA:ARRAY[-5..5,1..5] OF INTEGER;

Az elemtípus határozza meg, hogy a tömb elemei milyen adatokat tartalmaznak. Az elemek típusa lehet egyszerű, összetett vagy akár a felhasználó által definiált típus is.
Valamely tömb lehet egy-, két- vagy többkiterjedésű (dimenziós). A szögletes zárójelek között annyi indextípust kell veszővel elválasztva megadni, ahány dimenziós tömböt kívánunk létrehozni. Az egydimenziós tömböt vektornak, míg a kétdimenziós tömböt mátrixnak is szokás nevezni.
Az alábbi Y tömb 10 darab egész típusú elemet tartalmaz:

VAR Y:ARRAY[1..10] OF INTEGER;

Az y tömb egyes elemeire a tömbváltozó nevével és a megadott határok közé eső indexszel hivatkozunk:

Y[1], Y[2], Y[3], ... Y[10]

Az index lehet sorszámozott típusú változó, konstans vagy kifejezés. Ha például, az I és J egész típusú változók, melyek értéke 2 illetve 3, akkor

Y[I] az Y tömb második elemét jelenti, mivel az i változó tartalma 2,
Y[I+J] az Y tömb ötödik elemét jelöli, mivel I+J kifejezés értéke 5,
Y[2*2] az Y tömb negyedik elemére hivatkozik.

Deklaráljunk egy valós számokat tartalmazó 2x3-as mátrixot! A deklaráció kétféleképpen is megadható:

VAR X:ARRAY[1..2,1..3] OF REAL;

vagy

VAR X:ARRAY[1..2] OF ARRAY[1..3] OF REAL;

A x kétdimenziós tömbnek 2 sora és 3 oszlopa van. Az első indexe a sorindex, a második indexe pedig az oszlopindex. A második sor, harmadik oszlopának elemét mindkét deklaráció esetén a következőképpen választhatjuk ki:

X[2,3] vagy X[2][3]

Amennyiben a j:=2; k:=2; akkor az X[J,K+1] hivatkozással ugyancsak az X[2,3] elemet érjük el.

Minden tömb a memória egy-egy folytonos területén helyezkedik el. Az egydimenziós tömb esetén az elemek egyszerűen az indexek növekvő sorrendjében követik egymást.
Kétdimenziós tömbök tárolása sorfolytonosan (soronként) történik. A fenti példában szereplő x mátrix esetén az elemek elhelyezkedése a memóriában:

X[1,1], X[1,2], X[1,3], X[2,1], X[2,2], X[2,3]

Többdimenziós tömb elemeinek tárolása esetén mindig először a legutolsó index fut körbe, majd az utolsó elötti, stb... A példában minden elem 6 bájtos, így az elemek elhelyezkedése az alábbi táblázattal szemléltethető.

A Pascal nyelv összetett típusaival kapcsolatosan fontos tisztázni az azonos típus fogalmát. Az alábbi példában, bár látszólag mindhárom tömbváltozó ugyanolyan típusú, a Pascal csak a T1 és a T2 tömböket tekinti azonos típusúnak.

PROGRAM PROBA;
VAR T1,T2:ARRAY[1..3] OF INTEGER;
    T3:ARRAY[1..3] OF INTEGER;
BEGIN
  T1[1]:=1;T1[2]:=2;T1[3]:=3;
  T3:=T1; {típuskeveredési hiba!}
END.

A típusazonosság fontos szerepet játszik a tömbök közötti értékadás művelete esetén, illetve alprogramok tömbparaméterrel történő hívásakor. A későbbiekben típusdefinícióval biztosítjuk a típusok azonosságát. A Pascal-ban azonos típusú tömbök esetén értelmezett a tömbök közötti értékadás és az egyenlőségvizsgálat művelete. Például:

T2:=T1;

A művelet elvégzése után a T2 tömb tartalma meg fog egyezni a T1 tömb tartalmával. Természetesen a feladatot elemenkénti értékadással is megoldhatjuk:

T2[1]:=T1[1];
T2[2]:=T1[2];
...
T2[10]:=T1[10];

A standard Pascal-hoz hasonlóan (sajnos) a HiSoft Pascal sem ismeri a string típust. Megoldást jelent a karaktertömbök használata.

5.3.2. Rekordtípusok
A rekord a legrugalmasabb Pascal adatszerkezet, mivel benne tetszőleges számú, különböző tulajdonságú (típusú) rész szerepelhet. Nem hozhatunk létre azonban a Hisoft Pascal-ban változó rekordokat. A rekord típusdeklarációja a RECORD és az END foglalt szavak között helyezkedik el. A két foglalt szó között a közönséges változókhoz hasonló módon definiáljuk a rekord elemeit (mezőit).
Például, egy dátumbejegyzést rekordtípus segítségével a következőképpen írhatunk le:

VAR DATUM:RECORD
      EV:1900..2100;
      HONAP:1..12;
      NAP:1..31;
      KOD:CHAR;
    END;

A DATUM rekordváltozónak négy mezője van. Az EV, a HONAP és a NAP mezők résztartomány típusúak. A dátumhoz egy (egy karakteres) megjegyzés kódot is fűzhetünk fűzhetünk a char típusú KOD mezőben.
A rekord elemeire (mezőire) úgy hivatkozhatunk, hogy a rekordváltozó neve után ponttal elválasztva írjuk a mező azonosítóját:

DATUM.EV:=1973;
DATUM.HONAP:=12;
DATUM.NAP:=24;
DATUM.KOD:='K';

5.3.3. Halmaztípusok
Bizonyos feladatok legtermészetesebb módon halmazok használatával oldhatók meg. A halmaz bizonyos tulajdonságú elemek összessége. A Pascal nyelv a halmazok használatát a SET (halmaz) típussal támogatja.
A halmazok deklarációjának általános formája:

VAR halmazváltozó: SET OF alaptípus;

Egy halmaz maximum 256 elemet tartalmazhat, és az alaptípus csak sorszámozott típus lehet. Például:

VAR ABC: SET OF 'a'..'z';
    SZAMOK: SET OF 0..100;
    ASCII:  SET OF CHAR;

A fentiekben deklarált abc halmaz az alábbi értékadásokban szerepelhet:

ABC:= []; {üres halmaz}
ABC:=['a'..'z']; {a teljes halmaz}
ABC:=['b','d']; {a b és d betűket tartalmazó halmaz}

5.4. Típusnevek létrehozása (TYPE)
Az előbbi alfejezetekben láttuk, hogy minden változó deklarációjában meg kellett nevezni a változó típusát. A Pascal a típusdefiníciós részben a TYPE foglalt szó után saját típusnevek létrehozását is támogatja. A típusdefiníciót a következő módon kell megadni:

TYPE új_típusnév=létező típus;

Először megadjuk a létrehozandó új típus azonosítóját (nevét), majd az egyenlőségjel után egy érvényes típusleírást helyezünk el, amely lehet tetszőleges létező típus, de megadhatunk akár már korábban definiált típusnevet is. Nézzünk néhány példát az elmondottakra:

TYPE EGESZ=INTEGER;
    VALOS=REAL;
    STR8=ARRAY[1..8] OF CHAR;
VAR I,J:EGESZ;
    A,B:VALOS;
    FILENEV:ST8;

A fenti példában szereplő egyszerű típusoknál sokkal gyakrabban használjuk a TYPE definíciókat bonyolultabb típusok létrehozására. A típusalkotás lehetőségét elsősorban a felsorolt, a résztartomány és a strukturált típusok esetén alkalmazzuk. Ennek oka, hogy az eljárások és függvények ilyen típusú paramétereinek deklarációjában csak egyetlen típusnév szerepelhet.

5.4.1. A TYPE használata felsorolt típus esetén
Felsorolt típusok megadása és ezen típusok deklarációban való felhasználása az alábbiak szerint történik:

TYPE TANTARGY=(MAGYAR,FIZIKA,MATEMATIKA,TORTENELEM);
    NYELVEK=(ANGOL,NEMET,SPANYOL,OLASZ,KINAI);
    SZINEK=(PIROS,KEK,ZOLD,SARGA,FEKETE,FEHER);
VAR ORA:TANTARGY;
    NYELV:NYELVEK;
    RUHA:SZINEK;

Természetesen a fenti felsorolt típusok esetén is használhatjuk az ORD, a SUCC, és a PRED szabványos függvényeket. Ugyanakkor az a szabály is teljesen egyértelmű, hogy minden nyelvek típusú változó (így a nyelv is) csak az angol, a nemet, a spanyol és az olasz értékek valamelyikét veheti fel. Nézzünk példát néhány helyes értékadásra!

RUHA:=FEKETE;
ORA:=FIZIKA;
NYELV:=SPANYOL;

5.4.2. A TYPE használata résztartománytípus esetén
A résztartomány típust valamely létező sorszámozott típus értékeinek részintervallumából állítjuk elő. A típusdefiniálás a résztartomány típus azonosítójával kezdődik, majd az egyenlőségjel után az intervallum alsó és felső határát kell megadni két ponttal elválasztva.

TYPE KISBETU='a'..'z';
    SZAM='0'..'9';
    SZINEK=(PIROS,KEK,ZOLD,SARGA,FEKETE,FEHER);
    SZINRESZ=KEK..FEKETE;
    INDEX=1..1000;
VAR I,J:INDEX;
    ALAP:SZINRESZ;

A példában szereplő szinresz típust a korábban definiált felsorolt típusú színek résztartományaként adtuk meg. Így az alap változó csak a kek, a zold, a sarga és a fekete színek egyikét veheti fel értékként.

5.4.3. A TYPE használata tömbök esetén
Az előző két típushoz hasonlóan tömbtípust is definiálhatunk saját típusként. Nézzük meg az alábbi tömbtípusok változódeklarációban való felhasználását!

TYPE SZINEK=(PIROS,KEK,ZOLD,SARGA,FEKETE,FEHER);
    VEKTOR=ARRAY[1..100] OF REAL;
    MATRIX=ARRAY[1..10,1.. 10] OF INTEGER;
    ADAT=ARRAY[1..50] OF REAL;
    ABC=ARRAY['a'..'z'] OF CHAR;
    SZINTABLA=ARRAY[1..8,1..8] OF SZINEK;
VAR VA,VB:VEKTOR;
    MX,MY:MATRIX;
    KODTABLA:ABC;
    CT:SZINTABLA;
    MZ:MATRIX;

Típusdefinícióval egyszerűen biztosíthatjuk a különböző változók típusazonosságát. A példában az mx, az my és az mz tömbök azonos típusúak, annak ellenére, hogy nem ugyanabban a deklarációban szerepelnek.

5.4.4. A TYPE használata rekordok esetén
A típusdefiníció használata különösen javasolt rekordtípusok esetén. Ezzel a típusazonosság biztosítása mellett rekordokat tartalmazó bonyolultabb adatstruktúrákat (például, rekordtömböt, egymásba skatulyázott rekordokat, stb.) is egyszerűen készíthetünk.

TYPE DATUM=RECORD
      EV:1900..2100;
      HONAP:1..12;
      NAP:1..31;
      KOD:CHAR;
    END;
VAR SZULETESNAP=DATUM;

5.4.5. A TYPE használata halmazok esetén
A halmaz típusú név deklarálása az alábbi formában történik:

TYPE halmaz_típus_név=SET OF alaptípus

Halmazok használatához fontos tisztáznunk a különbséget a halmaztípus, a halmazváltozó és a halmaz elemei között. A halmaztípus meghatározza, hogy az adott típusú halmaz milyen elemeket tartalmazhat. A programban a halmaz elemei a halmazváltozóban tárolódnak. Az elemek azonban nem a deklaráció, hanem az értékadás során kerülnek a halmazba.
Az elmondottakat illusztrálja az alábbi példaprogram:

PROGRAM PROBA;
TYPE NAPOK=(HETFO,KEDD,SZERDA,CSUTORTOK,PENTEK,SZOMBAT,VASARNAP);
    HET=SET OF NAPOK;
    ASCII=SET OF CHAR;
    JEGYEK=SET OF 0..9;
VAR MUNKANAPOK,PIHENONAPOK,TELJESHET:HET;
    ASCIITABLA:ASCII;
    DECJEGYEK:JEGYEK;
    X,Y:INTEGER;
BEGIN
  PIHENONAPOK:=[SZOMBAT,VASARNAP];
  ASCIITABLA:=[CHR(0)..CHR(255)];
  X:=5;Y:=9;
  DECJEGYEK:=[1,5,X+1..Y-1];
  MUNKANAPOK:=[HETFO..PENTEK];
END.

5.5. Konstansnevek létrehozása (CONST)
A konstansnevek olyan azonosítóval (névvel) ellátott értékek, amelyek nem változnak meg a program futása során. (Következésképpen konstansnevek nem szerepelhetnek az értékadó utasítás bal oldalán!)
A konstansdefiníciókat a deklarációs részben a CONST foglalt szó után adjuk meg. A definícióban az egyenlőségjel bal oldalán szerepel a konstans neve, a jobb oldalán pedig a hozzárendelni kívánt érték (konstans kifejezés). A konstansnevet minden olyan értékhez definiálhatunk, amit a programban konstans értékként is megadhatunk. Egyaránt készíthetünk egész és valós numerikus, karakteres és logikai konstansokat:

CONST EV=2013
      MIN=1E-5;
      IGAZ=TRUE;
      BETU='A';

A fentiekben bemutatott konstansnevek számára a fordító nem foglal helyet a memóriában, így ezeket a fordító csak a fordítás folyamán használja. A konstansnevekre a deklarációs részben és a programban egyaránt hivatkozhatunk:

CONST MAXN=100;
TYPE TTOMB=ARRAY[1..MAXN] OF REAL;

Konstansnevek használatával programunk olvashatóbbá válik, és bizonyos változtatások is könnyebben elvégezhetők. Konstansnevek alkalmazását elsősorban az alábbi esetekben ajánljuk:

5.6. Azonos és kompatibilis típusok
A típusokkal kapcsolatosan még három kérdésre kell választ találnunk. Az első a típusazonosság, a második a típus-kompatibilitás, a harmadik pedig az értékadás-kompatibilitás kérdése. Vannak esetek, amikor típusok egyezését (azonosságát) írja elő a Pascal nyelv. Más esetekben viszont elegendő, ha típusok kompatibilisek egymással, vagy ha csupán értékadás-kompatibilisek.

5.6.1. Típusok azonossága
A típusazonosság eldöntéséhez az alábbi szabályokat kell figyelembe venni:

Az alábbi típusdefiníciók különbözőek, mivel a típusleírásban nem egyetlen típusnév szerepel:

TYPE ST1=SET OF CHAR;
     ST2=SET OF CHAR;

Ha két változót ugyanabban a deklarációban adunk meg, akkor azok azonos típusúak lesznek:

VAR SV1,SV2:SET OF CHAR;

Amennyiben két külön deklarációban szerepelnek a változók, akkor a helyzet megváltozik:

VAR SV1:SET OF CHAR;
    SV2:SET OF CHAR;
    V1:INTEGER;
    V2:INTEGER;

A példában a V1 és V2 azonos típusú, míg az SV1 és SV2 változók különböző típusúak.

5.6.2. Típusok kompatibilitása
Vannak esetek, amikor szükség van a típusok közötti kompatibilitás biztosítására. Ilyenek például, a kifejezések kiértékelése vagy az összehasonlító műveletek végzése. A típus-kompatibilitás egyben az értékadás-kompatibilitás előfeltétele is.
Két típus kompatibilisnek tekintünk, ha az alábbi feltételek közül legalább az egyik teljesül:

5.6.3. Értékadási kompatibilitás
Értékadási kompatibilitásra akkor van szükség, amikor az értékadó utasításban, illetve a paraméterátadás során értéket adunk valaminek.
A T2 típusú érték akkor értékadás-kompatibilis a TI típusú értékkel (vagyis megengedett a T1 típusú változó := T2 típusú kifejezés; értékadás), ha az alábbi feltételek egyike teljesül:

A fordítás során hibajelzést kapunk, ha szükséges az értékadás kompatibilitása, de az előző lista egyik pontja sem teljesül.

5.7. Típuskonverzió
A Pascal erősen típusos nyelv. Ez a megállapítás azt jelenti, hogy a nyelv mentes a nem típusos nyelvekben oly gyakori automatikus típusátalakításoktól. A fentiektől eltérő szituációkban típuskeveredési hibával megszakad a fordítás. Vannak azonban olyan esetek, amikor programunkban olyan típusátalakításra kényszerülünk, amelyet nem támogat a Pascal nyelv. Szabványos függvények segítségével végezzük el az átalakítást:

ROUND(5.6) = 6 valós egész átalakítás kerekítéssel
TRUNC(5.6) = 5 valós egész átalakítás a törtrész elhagyásával
ORD('A') = 65 karakter egész átalakítás

6. Pascal kifejezések

A kifejezések operátorok és operandusok sorozatából állnak. Operátoroknak nevezzük a különféle műveleti jeleket, amelyek összekapcsolják a kifejezésben szereplő operandusokat (változókat, konstansokat és függvényhívásokat). Az operátorok által előírt műveletek a program futása során (futási időben) kerülnek végrehajtásra. Egy kifejezés kiértékelése (kiszámítása) a benne szereplő összes művelet elvégzését jelenti.

6.1. Egy- és kétoperandusú műveletek
A műveleteket csoportosíthatjuk az operandusok száma szerint. Ez alapján a Pascal-ban minden művelet egy- vagy kétoperandusú.
Azt a műveleti jelet, amely az egyetlen operandusa előtt szerepel, ún. egyoperandusú operátornak nevezzük. Ilyen művelet például az előjelváltás:

B:=-A;

Amennyiben az operandusok közrefogják a műveleti jelet, úgy ún. kétoperandusú műveletről (és operátorról) beszélünk. Kétoperandusú műveletek például, az összeadás és a szorzás:

C:=A+B;
Y:=X*2;

6.2. Elsőbbségi szabályok
Annak érdekében, hogy a bonyolultabb kifejezéseket is helyesen használjuk Pascal nyelven, meg kell ismerkednünk azokkal a szabályokkal, amelyeket a Pascal fordító alkalmaz a kifejezések kiértékelése során. Az ún. elsőbbségi (precedencia) szabályok pontosan meghatározzák a kifejezésekben a műveletek kiértékelési sorrendjét.

operátorok
precedencia
művelet típusa
NOT, -, +
1
egyoperandusú
*, /, DIV, MOD, AND
2
multiplikatív
+, -, OR, XOR
3
additív
=, <>, <, >, <=, >=, IN
4
összehasonlító

A kifejezések kiértékelésénél a következő három elsőbbségi szabályt veszi figyelembe a fordítóprogram:

  1. Két különböző precedenciájú operátor esetén először a magasabb precedenciájú operátor által előírt művelet kerül végrehajtásra.
  2. Az azonos precedenciájú operátorok a felírásuk sorrendjében (balról jobbra haladva) értékelődnek ki.
  3. A zárójelezés megváltoztatja a műveletek kiértékelési sorrendjét. Először mindig a zárójelben megadott műveletek hajtódnak végre.

6.3. A műveletek csoportosítása
A Pascal nyelvben bizonyos operátorokhoz többféle műveletet is társul. Például a + operátor egyaránt jelölhet előjelet, összeadást vagy halmazok egyesítését. Azt, hogy egy operátor az adott műveletben mit is jelöl, az operandusok száma és típusa határozza meg. A műveleteket az operandusok típusa alapján az alábbi csoportokba sorolhatjuk:

6.3.1. Aritmetikai műveletek
Az aritmetikai műveleteket az aritmetikai kifejezésekben használjuk. Az ilyen műveletek operandusai egyaránt lehetnek egész és valós típusúak. Természetesen az eredmény típusa függ az operandusok típusától. Az alábbi táblázatban összefoglaltuk az aritmetikai operátorokat, illetve az operandusok és az eredmény típusát.

operátor művelet operandusok
típusa
eredmény
típusa
+
pozitív előjel egész egész
    valós valós
-
előjelváltás egész egész
    valós valós
+
összeadás egész egész
    valós valós
-
kivonás egész egész
    valós valós
*
szorzás egész egész
    valós valós
/
valós osztás egész valós
    valós valós
DIV
egész osztás egész egész
MOD
maradékképzés egész egész

Az egyoperandusú operátorok, a pozitív előjel és az előjelváltás eredménye egész típusú operandusok esetén egész, míg valós típus esetében valós lesz. A +a kifejezés értéke megegyezik a értékével, a -a kifejezés értéke azonban ellenkező előjelű lesz. A kétoperandusú aritmetikai operátorok a négy alapműveleten túlmenően, lehetővé teszik egész számok hányadosának és osztási maradékának meghatározását.
Egész és valós operandusok esetén vizsgáljuk meg a különböző műveletek eredményének típusát. Legyen általánosan a két operandus a és b.

5 DIV 2 = 2
5 MOD 2 = 1

Tetszőleges a és b (b<>0) egészre igaz az alábbi összefüggés:

a=(a div b)*b+a mod b

A mod operátor segítségével egyszerűen vizsgálhatjuk az egész számok oszthatóságát. Ha az a mod b művelet eredménye 0, akkor a maradék nélkül osztható b-vel.

Matematikai képletekben az ismertetett aritmetikai operátorokon túlmenően gyakran matematikai függvények is szerepelnek. A Pascal nyelv lehetővé teszi az ilyen függvények programból való elérését (hívását)

6.3.1.1. Amire figyelni kell: a fordító optimalizálása
Nézzük meg a következő példaprogramot:

PROGRAM TEST;
VAR A:INTEGER;
BEGIN
  A:=MAXINT;
  WRITELN('A+4 = ',A+4);
  WRITELN('4+A = ',4+A)
END.

A szemfüles olvasó rögtön gyanakodhat, hisz a művelet eredménye biztosan nem fog beleférni az integer által ábrázolható számtartományba: az eredmény nagyobb, mint 32767. A még szemfülesebb olvasó, aki már olvasgatta a HiSoft Pascal leírásának a "Compiler Options" című fejezetét, pedig azt mondja: "ha be van-e kapcsolva a túlcsordulást ellenőrző fordítási opció - márpedig alapértelmezés szerint be van kapcsolva -, akkor overflow hibaüzenettel le fog állni a program!"
A fentiek alapján, tehát azt várjuk, hogy a program az első WriteLn sor környékén le fog állni egy overfiow hibaüzenettel. Ehelyett azonban az első WriteLn sor hiba nélkül lefutott, pedig be van kapcsolva a túlcsordulást vizsgáló opció! (Természetesen a kiírt eredmény rossz.) Nézzük meg, milyen kódot állít elő a fordító a második esetre:

ld hl,4
push hl
ld hl,(0xfffe)
pop de
or a
adc hl,de
call pe,0x6a6
; HL = 4
; HL a verembe
; HL = 4
; DE = 4 (a verem tetejéről kivesszük)
; Carry flag törlése
; HL = HL+DE+Cary
; overflow esetén ugrás 0x6a6-ra

Első ránézésre furcsa lehet, hogy miért a lassabb ADC utasítást (15 órajel) használja a fordító ADD helyett (11 órajel), ráadásul így még kénytelen a Carry flag-et is törölni előtte (plusz 4 órajel), hogy jó eredményt kapjunk. A magyarázat az, hogy az ADD utasítás nem állítja az overflow flag-et, az ADC pedig igen, és bekapcsolt túlcsordulás vizsgálat mellett nekünk pont erre van szükségünk! Túlcsordulás esetén a 0x6a6 címen lévő rutin kiírja a hibaüzenetet, és abbahagyja a program futását.
Kikapcsolt túlcsordulás vizsgálat esetében a fordító természetesen már a gyorsabb ADD utasítást fogja használni:

ld hl,4
push hl
ld hl,(0xfffe)
pop de
add hl,de
; HL = 4
; HL a verembe
; HL = 4
; DE = 4 (a verem tetejéről kivesszük)
; HL = HL+DE

De térjünk vissza az első esetre (a+4), ami bekapcsolt túlcsordulás vizsgálat esetében is lefutott:

ld hl,(0xfffe)
inc hl
inc hl
inc hl
inc hl
; HL = a
; HL = HL+1
; HL = HL+1
; HL = HL+1
; HL = HL+1

A fordító felismeri, hogy a négy darab inc hl utasítás gyorsabb (24 órajel), mint ha a maga kacifántos módján a DE regiszterbe berakná a négyest (31 órajel), majd összeadást (11 órajel) végezne. A probléma csak az, hogy a nagy optimalizációban elfeledkezett arról, hogy a túlcsordulást vizsgáló opció be van kapcsolva, a fordító mindig a fenti kódot állítja elő, függetlenül attól, hogy mi az O opció értéke.
Ezek alapján ez egy hiba a fordítóban, ezért jó, ha tudunk róla. Ha szükségünk van a túlcsordulás vizsgálatra, és az összeadás egyik tagja konstans 1, 2, 3 vagy 4, akkor azt a bal oldalra írjuk! Kikapcsolt túlcsordulás vizsgálat esetében a konstanst írjuk a jobb oldalra, így gyorsabb kódot állíthat elő a fordító!
Nézzük a következő esetet:

PROGRAM TEST;
VAR A:INTEGER;
BEGIN
  A:=20000;
  WRITELN('A*2 = ',A*2);
  WRITELN('2*A = ',2*A)
END.

A Pascal dokumentáció alapján egész (integer) szorzás esetében mindig történik ellenőrzés túlcsordulásra, függetlenül az O opció értékétől. Ehelyett azt látjuk, hogy az első szorzás lefut, annak ellenére, hogy az eredmény nagyobb, mint 32767. A második WriteLn sorunk viszont tényleg leállt overfiow hibaüzenettel. A program futásának mindig ugyanez lesz az eredménye, fordítási opciótól függetlenül, az első szorzás mindig lefut, a második mindig leáll túlcsordulással. Mi a különbség a két szorzásban?
Nézzük meg, mit állít elő a fordító a 2*a esetben:

ld hl,2
push hl
ld hl,(0xfffe)
pop de
call 0x077e
; HL = 2
; HL a verembe
; HL = A (0xfffe címen van az A változónk)
; DE = 2 (a verem tetejéről kivesszük)
; szorzó rutin: HL = HL*DE

Az első négy sor hasonló, mint az összeadás esetében volt: a két operandust belerakja a HL és DE regiszterekbe, végül elvégzi a műveletet: meghívja a 0x077e címen található rutint, ami az egész szorzást végzi (és kezeli az esetleges túlcsordulást is), majd az eredményt a HL regiszterben kapjuk vissza.
Fordított tényezők (a*2) esetében az alábbi kódot állítja elő a fordító:

ld hl,(0xfffe)
add hl,hl
; HL = A
; HL = HL+HL

Itt is optimalizáció történt: felismeri a fordító, hogy sokkal gyorsabb kódot tud előállítani, ha nem hívja meg a szorzó rutint, hanem csak szimplán összeadással végzi el a kettővel valós szorzást. A dolog egyetlen szépséghibája, hogy bár a dokumentáció szerint egész szorzás esetében mindig van túlcsordulás vizsgálat, ez ebben az esetben elmarad, függetlenül attól, hogy mi van beállítva az O opcióban.
Mit tehetünk ez ellen? Ha ragaszkodunk a túlcsordulás vizsgálathoz, és a szorzatunk egyik tagja konstans, akkor azt tegyük a bal oldalra, ebben az esetben a fordító mindig olyan kódot fog előállítani, ami a szorzó rutint használja. Ha viszont szeretnénk kihasználni az optimalizációból adódó sebességnövekedést, akkor a konstans tényezőnket tegyük a szorzat jobb oldalára! Természetesen nem minden esetben lesz optimalizáció, csak a 2, 3, 4, 5, 6, 8, 9, 10, 12,16 és 20 esetében.

6.3.2. Logikai műveletek
A Pascal nyelv bizonyos utasításaiban logikai kifejezések segítségével szabályozzuk az utasítás végrehajtását. A logikai kifejezések általában logikai műveletekkel összekapcsolt összehasonlításokat (relációkat) tartalmaznak. A boolean típusú operandusokat logikai operátorokkal összekapcsolva olyan kifejezéshez jutunk, melynek típusa szintén boolean. Ennélfogva a logikai kifejezések kiértékelésének eredménye csak TRUE vagy FALSE lehet.
Az alábbi táblázatban összefoglaltuk a logikai operátorokat és az egyes műveletek precedenciaszintjét:

operátor művelet precedencia
NOT logikai tagadás (NEM) 1
AND logikai ÉS (logikai szorzás) 2
OR logikai VAGY (logikai összeadás) 3
XOR logikai kizáró VAGY 4

Az egyoperandusú (NOT) és a kétoperandusú (AND, OR, XOR) logikai műveletek működését ún. igazságtáblák segítségével szokás jellemezni. A táblázatokból kiolvasható, hogy adott operandusértékek esetén mi lesz a művelet eredménye.

NOT A A tagadás azt jelenti, hogy a NOT operátor mögött megadott operandus értéke az ellenkezőjére változik. (Ha az operandus értéke igaz volt, akkor a kifejezés értéke hamis lesz, ha pedig hamis volt, akkor igaz lesz.)

A
NOT A
true
false
false
true
A AND B A logikai ÉS művelet csak akkor ad igaz eredményt, ha mindkét operandus (A és B) értéke igaz. Hamis lesz a kifejezés értéke, ha a két operandus közül legalább az egyik hamis.

A
B
A AND B
true
true
true
true
false
false
false
true
false
false
false
false
A OR B A logikai VAGY művelet igaz eredményt ad, ha a két operandus közül legalább az egyik igaz. A kifejezés értéke csak akkor lesz hamis, ha mindkét operandus hamis értékű.

A
B
A OR B
true
true
true
true
false
true
false
true
true
false
false
false
A XOR B A logikai kizáró VAGY művelet akkor ad igaz eredményt, ha a két operandus közül pontosan az egyik értéke igaz. Amennyiben az operandusok értéke megegyezik, akkor a kifejezés értéke hamis lesz.

A
B
A XOR B
true
true
false
true
false
true
false
true
true
false
false
false

Látható, hogy

A HiSoft Pascal nem ismeri az integer típusú értékeken bitenként elvégezhető logikai műveleteket.

6.3.3. Összehasonlító (relációs) műveletek
A relációs műveleteket kétféle egymással kompatibilis típusú kifejezések (aritmetikai, karakteres, stb.) összehasonlítására szolgálnak. Az összehasonlítás eredménye igaz (TRUE), ha a reláció teljesül, és hamis (FALSE) ellenkező esetben. Az operátorok mindegyike kétoperandusú és a műveletek a legalacsonyabb precedenciaszinttel rendelkeznek.

operátor művelet
= egyenlő
<> nem egyenlő
< kisebb
> nagyobb
<= kisebb vagy egyenlő
>= nagyobb vagy egyenlő

Numerikus adatok összehasonlítása
A relációs operátorokat leggyakrabban különböző matematikai feltételek felírásához használjuk. Például, annak eldöntésére, hogy a valós x változó értéke kisebb-e mint -5, az alábbi kifejezés használható:

X<-5

Nehézségekbe ütközünk azonban, ha azt kívánjuk megvizsgálni, hogy x a (-5,5) intervallumban helyezkedik-e el. Ebben az esetben a matematikai felírást (-5<=x<5) nem tudjuk közvetlenül alkalmazni. A képletet értelmezve: 'x nagyobb egyenlő mint -5 és kisebb mint 5'. A Pascal kifejezéssé való átírás ezt követően már nem jelent gondot:

X>=-5 AND X<5

Azonban csalódnunk kell, hiszen a programba írva a kifejezést fordítási hibát kapunk. A hiba oka, hogy megfeledkeztünk arról, hogy a relációs operátorok elsőbbségi szintje a legalacsonyabb. A hiba egyszerűen korrigálható zárójelek elhelyezésével:

(X>=-5) AND (X<5)

Amennyiben arra vagyunk kíváncsiak, hogy A' kívül esik-e a fenti intervallumon, akkor az eddigiek ismeretében többféle módon is felírhatjuk a feltételt. Első lehetőségként tagadjuk a "bent van-e" vizsgálat feltételét a NOT operátorral (nem szabad megfeledkezni a precedenciáról):

NOT((X>=-5) AND (X<5))

Matematikai logikában jártas Olvasók a tagadást elvégezhetik az ott megismert szabályok alapján, nevezetesen: a relációk megfordulnak és az OR-ból AND, az AND-ból pedig OR lesz:

(X<-5) OR (X>=5)

A HiSoft Pascal-ban, alapértelmezett beállítással, egész típusú kifejezések összehasonlításánál, ha a két szám közötti különbség nagyobb, mint 32767, akkor hibás eredményt kapunk. Például, ha I, és J változó INTEGER típusú, az alábbi vizsgálat eredményeképpen megjelenik a 'Rossz eredmeny' felirat:

I:=-768;J:=32000;
IF I>J THEN WRITELN('Rossz eredmeny');

A pontos, de lassabb kiértékelő algoritmust az I direktívával kapcsolhatjuk be. (Ld. fordítási direktívákat.)

Szövegek összehasonlítása
A relációs operátorok karakterek összehasonlítására is felhasználhatók. Ekkor az összehasonlítás a karakterek kódja alapján történik. Például, az 'A'<'B' feltétel azért igaz, mivel az ORD('A') (65) valóban kisebb mint az ORD('B') (66).
A relációs operátorok - azonos elemszámú - karaktertömbökre szintén alkalmazhatók. Az összehasonlítás ekkor a megfelelő helyen álló karakterek páronként történő összevetésével történik. A vizsgálat akkor fejeződik be, ha az éppen összehasonlított karakterpár különböző karakterekből áll. Az karaktertömb a "kisebb", amelyik a párból kisebb kódú karaktert tartalmazza. Ha nincs eltérő karakterpár a két karaktertömbben, akkor a két karaktertömb azonos.

6.3.4. Műveletek mutatókkal
A HiSoft Pascal a mutatók használatához az indirekt hivatkozás (^) (egyoperandusú) operátorát tartalmazza. A művelet elsődleges precedenciával rendelkezik. A ^ műveleti jelet pointerváltozó esetén használjuk a mutató által kijelölt memóriaterület közvetett elérésére.

6.3.5. Halmazműveletek
A Pascal nyelv speciális lehetőségei közé tartozik a halmazok nyelvi szinten történő támogatása. A korábbi fejezetekben már megismerkedtünk a halmaz típussal. Most a figyelmet a halmazokon elvégezhető műveletekre fordítjuk.
A halmazműveleteket két csoportra osztjuk. Az első csoport az ún. Boole-algebra műveleteit tartalmazza. Ezen műveletek segítségével meghatározhatjuk a halmazok közös részét, unióját és különbségét.
Halmazok esetén is használhatjuk a már megismert relációs operátorokat (kibővítve az elemvizsgálat operátorával). Természetesen az összehasonlító műveletek értelmezése valamelyest eltér az eddig megismerttől.

Halmazok egyesítése, metszete és különbsége
A halmazműveleteket az alábbi táblázatban foglaltuk össze. A kétoperandusú műveletek operandusai csak kompatibilis halmazok lehetnek, a műveletek eredménye pedig szintén halmaz típusú lesz.

operátor művelet precedencia
*
halmazok metszete (közös része) 1
+
halmazok egyesítése (uniója) 2
-
halmazok különbsége 2

Az egyes műveletek bemutatása során az X és Y halmazokra hivatkozunk. Definiáljuk mindkét halmazt az alábbi programrészletnek megfelelően!

TYPE ELEMEK=(a,b,c,d,e,f,g,h);
     HALMAZ=SET OF ELEMEK;
VAR X,Y:HALMAZ
BEGIN
  X:=[a,b,c,d];
  Y:=[a,c,e,f];
  ...

Halmazokra vonatkozó vizsgálatok
Két halmazról megállapíthatjuk, hogy egyenlőek-e (azonos elemeket tartalmaznak-e) vagy sem. Halmazok esetén különböző tartalmazás-vizsgálatokat is végezhetünk. Megvizsgálhatjuk, hogy egy adott érték eleme-e a halmaznak, illetve azt, hogy egy halmaz minden eleme megtalálható-e egy másik halmazban. Ha az utóbbi vizsgálat igaz eredményt ad, akkor azt mondjuk, hogy az egyik halmaz a másik részhalmaza.

operátor művelet
= azonos
<> nem azonos
<= részhalmaz - a jel bal oldalán álló halmaz a jobb oldalon álló halmaz részhalmaza
>= részhalmaz - a jobb oldalon álló halmaz a bal oldalon álló halmaz részhalmaza
in elemvizsgálat

Nézzük meg részletesen annak vizsgálatát, hogy egy érték eleme-e a megadott halmaznak! Ha (halmazelem IN halmaz) kifejezés értéke igaz (TRUE), akkor a halmazelem benne van a halmazban. Ellenkező esetben a halmaz nem tartalmazza a halmazelemet.
Érdemes megjegyezni, hogy a halmazműveletek általában gyorsabbak, és a forráskódot is tömörebbé teszik, mint a hagyományos megoldások. Példaként írjunk fel egy olyan kifejezést, mely csak akkor igaz, ha a CH változó értéke az 'i', 'I', 'n' és 'N' karakterek közül kerül ki!
A megoldás egyenlőségvizsgálat és logikai VAGY műveletek segítségével:

(CH='i') OR (CH='I') OR (CH='n') OR (CH='N')

Sokkal áttekinthetőbb megoldás az, amely az IN operátort és egy halmazkonstanst tartalmazza:

CH IN ['i','I','n','N']

Halmaz elemeinek kiírása
Halmazt nem tudunk a WRITE utasítással kiírni. A halmaz kiírása úgy történik, hogy az alaphalmaz összes eleméről levizsgáljuk, benne van-e a megadott halmazban. Ha igen, akkor kiírjuk. Mivel az alaphalmaz minden esetben sorszámozott típus, a kiíratást egy növekményes ciklussal elvégezhetjük. A ciklus természetéből adódik a kiírt elemek rendezettsége.
Példaként kérjünk be karaktereket a billentyűzetről, mindaddig, amíg ESC-et nem ütnek. Ekkor jelenítsük meg ABC sorrendben először a leütött billentyűket, majd azokat, melyeket nem ütöttek le. A feladatban nagybetűket kell gyűjtenünk egy betűhalmazba, végül a halmazt és annak komplementerét meg kell jeleníteni. Az alaphalmaz az összes nagybetű: 'A'..'Z'.

PROGRAM BEGYUJTES;
VAR BETUK: SET OF 'A'..'Z';
    KAR:CHAR;
BEGIN
  BETUK:=[];
  WRITELN('Nagybetuket kerek (vege: ESC):');
  REPEAT
    REPEAT
      KAR:=INCH;
    UNTIL KAR<>CHR(0);
    IF KAR IN ['A'..'Z'] THEN BEGIN
      BETUK:=BETUK+[KAR];
      WRITE(KAR,' ')
    END;
  UNTIL KAR=CHR(27);
  WRITELN;WRITELN;WRITELN('Leutott billentyuk:');
  FOR KAR:='A' TO 'Z' DO
    IF KAR IN BETUK THEN WRITE(KAR);
  WRITELN;WRITELN;WRITELN('Le nem utott billentyuk:');
  FOR KAR:='A' TO 'Z' DO
    IF NOT (KAR IN BETUK) THEN WRITE(KAR);
END.

A HALMAZ.PAS programban tanulmányozhatjuk a halmaztípus használatát és a halmazon végzett műveleteket:

PROGRAM HALMAZ;
TYPE NAPOK=(hetfo,kedd,szerda,csutortok,pentek,szombat,vasarnap);
    HET=SET OF NAPOK;
VAR MUNKANAP,PIHENONAPOK,TELJESHET:HET;
    RENDEL,NEMRENDEL,SZABADVAGYOK:HET;
BEGIN
  TELJESHET:=[hetfo..vasarnap];
  PIHENONAPOK:=[szombat..vasarnap];
  SZABADVAGYOK:=[szerda];
  IF TELJESHET>=PIHENONAPOK THEN
    WRITELN('A teljes het a pihenonapokat is tartalmazza');
  MUNKANAP:=TELJESHET-PIHENONAPOK;
  IF MUNKANAP<=TELJESHET THEN
    WRITELN('A munkanap reszhalmaza a teljes hetnek');
  RENDEL:=[hetfo,szerda,pentek];
  NEMRENDEL:=MUNKANAP-RENDEL;
  IF csutortok IN NEMRENDEL THEN
    WRITELN('Szabadnap');
  IF szerda IN RENDEL THEN
    WRITELN('Van rendeles');
  IF RENDEL*SZABADVAGYOK<>[] THEN
    WRITELN('Rendelesre mehetek');
END.

7. A Pascal nyelv utasításai

A Pascal nyelven írt program végrehajtható (futtatható) része elvégzendő tevékenységek (utasítások) sorozatából áll. A Pascal programban az egyszerű utasítássorozaton (szekvencián) túlmenően bonyolultabb programstruktúrák megvalósítására is van lehetőség. Ezen megoldások két fő csoportba, a kiválasztás (szelekció) és az ismétlés (iteráció) csoportokba sorolhatók. Az első csoportot a Pascal feltételes utasításai, míg a másodikat a ciklusutasításai képviselik. A Pascal nyelv utasításainak egy másik lehetséges csoportosítása:

A következőkben részletesen bemutatjuk az egyes utasításfajtákat.

7.1. Egyszerű utasítások
Az egyszerű utasítások segítségével egyetlen lépésben elvégezhető műveleteket írhatunk elő a programunk számára.

7.1.1. Az üres utasítás
Az üres utasítás hatására semmilyen tevékenység sem történik. Az üres utasítást akkor használjuk, ha a programban csak a szintaxis előírásai miatt kell egy utasítást elhelyeznünk. Az üres utasítás a nyelv egyetlen olyan szerkezete, amely nem igényli a nyelvi szimbólumok használatát.

7.1.2. Az értékadó utasítás
A leggyakrabban használt Pascal utasítás az értékadás:

változónév:=kifejezés;

Az utasítás végrehajtása során a 'változónév' azonosítójú változó felveszi a 'kifejezés' kiszámított értékét. A 'kifejezés' lehet konstans, változó vagy Pascal kifejezés. Az utasítás két oldalának azonos típusúnak kell lennie. Az alábbi példák az értékadó utasítás lehetséges formáit szemléltetik:

X:=3.5;
Y:=W;
Z:=X*Y+SQR(X);
X:=X+1;

Aritmetikai értékadás
Az aritmetikai értékadás során a bal oldalon szereplő változó numerikus értéket vesz fel. Az értékadó utasítások használata során gyakran előforduló hiba a típuskeveredés, amikor a bal és a jobb oldal típusa nem értékadás kompatibilis. A másik, szintén gyakori hiba, hogy a bal oldalon szereplő változónak nagyobb értéket adunk, mint a típusának megfelelő maximális érték. Valamely változók típusának a megválasztásakor mindig át kell gondolni, hogy a változó milyen értékeket vehet fel. Ugyancsak meg kell vizsgálni, hogy a műveletek következtében a program futása során hogyan alakulnak az értékhatárok.
Például, ha a V integer típusú változó (2 bájton tárolt előjeles egész, melynek értéktartománya -32768..32767), akkor az alábbi értékadások programhibához vezetnek:

V:=23456;
V:=2*V;

Különös figyelemmel kell eljárnunk a változó típusának megválasztása során. Ha előre tudjuk, hogy a változó a típusánál nagyobb (kisebb) értékeket is felvehet, akkor a tágabb értékkészlettel rendelkező REAL típust kell használnunk.
Turbo Pascal-ban az alábbi programrészlet hibás eredményt adna az összeadásra:

VAR V1,V2:INTEGER;
    SUM:REAL;
BEGIN
  V1:=30000;V2:=5000;
  SUM:=V1+V2
END.

Bár a változók típusa és az eredmény típusa elegendő az értékeik tárolására, Turbo Pascal-ban az eredmény mégis hibás lesz. A hibát jobb oldalon álló kifejezés kiértékelése okozza. Kétoperandusú művelet elvégzéséhez a Turbo Pascal az operandusok típusának megfelelő típusú munkaterületet használ. A bal oldalon szereplő változóba csak a művelet elvégzése után kerül az eredmény.
HiSoft Pascal-ban erre a hibaforrásra nem kell figyelnünk.

Példa:

PROGRAM ERTEKADAS;
VAR A,B,SZK,MK:REAL;
BEGIN
  WRITE('1. szam: ');READ(A);
  WRITE('2. szam: ');READ(B);
  SZK:=(A+B)/2;
  MK:=SQRT(A*B);
  WRITELN('Szamtani kozepe: ',SZK:6:1);
  WRITELN('Mertani kozepe: ',MK:6:1)
END.

Logikai értékadás
Boolean típusú változók TRUE (igaz) vagy FALSE (hamis) logikai értéket vehetnek fel. A logikai értékadásnál a bal oldalon boolean típusú változó áll, a jobb oldalon pedig boolean típusú konstans, változó vagy logikai kifejezés állhat. (A boolean változók perifériáról, tehát a read és a readln utasításokkal nem kaphatnak értéket.)

Példa:

B1:=TRUE;
B2:=I<J;
B3.= B1 AND B2;

A változók kezdőértéke
Egy változó tartalma a program indulásakor meghatározatlan. értéke attól függ, mire használta a program előzőleg azt a memóriaterületet, amit most ehhez a változóhoz rendelt. A Pascal-ban nincsen automatikus kezdőértékadás. Hibakereséskor erre különösen figyeljünk, mert egy kifelejtett értékadás nehezen felderíthető, rendellenes működést eredményez.

7.1.3. A GOTO utasítás
A GOTO utasítás feltétel nélküli vezérlésátadást valósít meg az utasításban szereplő címkével jelölt programsorra:

GOTO címke;

(A címkét a label foglalt szó után deklarálni kell a program deklarációs részében.) A strukturált nyelvekben - így a Pascal-ban is - mindig el lehet kerülni a GOTO utasítás használatát. Akkor szép egy Pascal program felépítése, ha nem tartalmaz GOTO utasítást, amely a programot áttekinthetetlenné teszi.
A GOTO utasítást csak abban az utasításblokkban ajánlott használni, amely a címkét tartalmazza. A GOTO utasítással nem szabad blokkba beugrani, de a blokkból való kiugrás megengedett. Függvénybe és eljárásba sem belépni, sem pedig kilépni nem lehet a GOTO segítségével.

Példa:

PROGRAM SZAMOL;
LABEL 1;
VAR A:INTEGER;
BEGIN
  A:=0
  1: A:=A+1
  WRITELN(A);
  IF A<10 THEN GOTO 1
END.

7.1.4. Eljáráshívás
Az eljárás olyan névvel ellátott programrész, amely egy adott tevékenységsorozatot összefog. Az eljáráshívás folyamán a hívott programrész végrehajtódik, majd a vezérlés visszakerül a hívás utáni utasításra. A hívás lehetséges formái:

eljárásnév(paraméterek);
eljárásnév;

A Pascal nyelvi szókészlete oly szűk, hogy még az alapvető beviteli-, kiviteli műveletek is eljárásokban vannak megvalósítva. Nézzük meg példaként egy jól ismert Pascal eljárás hívását:

WRITELN('HiSoft Pascal');

7.2. Strukturált utasítások
A legegyszerűbb programok menete lehet szekvenciális, azaz az utasítások sorban egymás után hajtódnak végre. Ilyenkor a vezérlés közvetlenül az őt lövető utasításnak adódik át. Legtöbb esetben azonban a feladat megkívánja a program strukturálását. Lehet, hogy valamely feltételtől függően más és más utasítást vagy utasításcsoportot kell végrehajtani. Előfordul, hogy egy utasítást vagy utasításcsoportot többször egymás után kell végrehajtani. Vezérlésekre (elágazásokra, ismétlésekre) minden programozási nyelvben lehetőség van.
A strukturált utasítások más egyszerű és strukturált utasításokból épülnek fel. Az utasítások által definiált tevékenységek sorban (az összetett és a WITH utasításokban), feltételtől függően (az IF és a CASE utasításokban) vagy pedig ciklikusan ismétlődve (a FOR, a WHILE és a REPEAT utasításokban) hajtódnak végre.

7.2.1. Összetett utasítás (blokk)
A BEGIN és az END foglalt szavak között megadott utasítások sorozatát összetett utasításnak (utasításblokknak) nevezzük.

BEGIN
   utasítás1;
   utasítás2
   ...
   utasításN;
END;

A Pascal programban az összetett utasítást akkor használjuk, ha több utasítás végrehajtására van szükség, de formailag csak egyetlen utasítás adható meg. Ilyen megkötésekkel a fejezet további részeiben gyakran találkozunk. Az összetett utasításban a BEGIN és az END foglalt szavakat szokás utasítás-zárójelnek is nevezni.

7.2.2. Feltételes utasítások
Az egyszerűbb programok felépítése olyan, hogy a sikeres működéshez elegendő a program sorait egymás után végrehajtani. Ha azonban a programunkat "érzékennyé" szeretnénk tenni a futás közben bekövetkező eseményekre (mint például egy kifejezés kiértékelésének eredménye vagy hibás bemeneti adat), akkor szükségessé válik ezen esetek létrejöttének vizsgálata. Attól függően, hogy milyen esemény keletkezik, a programunk más és más úton halad tovább - "elágazik".
A Pascal nyelvben a program futásának elágaztatására feltételes utasításokat (IF és CASE) használunk. Ezen utasítások segítségével a vizsgált feltétel eredményétől függően a program egyik vagy másik része kerül végrehajtásra.

Az IF utasítás
Az IF - THEN - ELSE utasítás kétirányú feltételes elágazást hajt végre. Az if utasítás általános formája:

IF feltétel THEN utasítás1 ELSE utasítás2;

Az IF után álló feltétel egy logikai kifejezés, melynek értékétől függ, hogy melyik utasítás hajtódik végre. Ha a feltétel értéke igaz (TRUE), akkor a THEN utáni 'utasítás1', ha pedig hamis (FALSE), akkor az ELSE utáni 'utasítás2' kerül végrehajtásra.
Az IF - THEN - ELSE szerkezet egyetlen strukturált utasítás, melynek része mindhárom kulcsszó, mégpedig ebben a sorrendben. A szintaktika a THEN illetve az ELSE utasítás után egy-egy utasítást vár (ez lehet összetett utasítás is). A pontosvessző lezárja az if utasítást, így az else elé nem szabad pontosvesszőt tenni, mert szintaktikai hibát eredményezne (ELSE szóval egyetlen utasítás sem kezdődik).

Az IF utasítás rövid alakja:

IF feltétel THEN utasítás;

Ebben az esetben, ha a 'feltétel' nem teljesül, akkor a vezérlés egyszerűen átadódik az IF utasítást követő utasításra.
Mind a THEN, mind pedig az ELSE-ágban formailag csak egy utasítás szerepelhet. Ha például a THEN-ágban egyetlen utasítást sem kívánunk megadni, akkor az üres utasítást kell használnunk:

IF feltétel THEN ELSE utasítás;

Természetesen a fenti vizsgálat egyszerűbb alakban is elvégezhető a feltétel tagadásával:

IF NOT feltétel THEN utasítás;

Ha egynél több utasítást kívánunk elhelyezni valamelyik ágon, akkor az utasításokat BEGIN - END utasítás-zárójelek közé kell zárni (összetett utasítás):

IF feltétel THEN BEGIN
   utasítások
END
ELSE BEGIN
   utasítások
END;
IF feltétel THEN BEGIN
   utasítások
END;

AZ IF utasításokat egymásba is lehet ágyazni. Erre mutat példát a FELTETEL.PAS program:

PROGRAM FELTETEL;
VAR T:INTEGER;
BEGIN
  WRITELN('A viz halmazallapotanak vizsgalata');
  WRITE('homerseklet: ');READ(T);
  IF T>0 THEN BEGIN
    IF T>=100 THEN WRITELN('goz')
    ELSE WRITELN('viz')
  END
  ELSE WRITELN('jeg')
END.

A CASE utasítás
A CASE utasítás segítségével könnyen megoldhatjuk a programunk többirányú elágazását. Használata olyan esetben célszerű, ahol kettőnél több IF utasítást kellene egymásba ágyaznunk.

CASE kifejezés OF
   érték1: utasítás1;
   érték2: utasítás2;
   ...
   értékN: utasításN
   ELSE utasítás;

Az utasításban a CASE foglalt szó után álló kifejezés (az utasítás szelektora) csak sorszámozott típusú lehet. A szelektor értékétől függően történik a program további utasításainak (eseteknek) a kiválasztása. Az utasítások előtt szereplő 'érték1, ..., értékN' sorszámozott típusú konstansok (ún. esetkonstansok) a kifejezés lehetséges értékeit jelölik.
Amennyiben a kifejezés értéke megegyezik a felsorolt konstansok egyikével, akkor a vezérlés a konstans után kettősponttal elválasztva megadott utasításra adódik. Ha azonban a kifejezés értéke egyik konstanssal sem egyenlő, akkor az ELSE-ág utasítása hajtódik végre. Az ELSE-ág használata nem kötelező. Ha nincs ELSE ág, a CASE szerkezetet az END szó zárja (ha van ELSE ág, nem szabad kitenni az END-et):

CASE kifejezés OF
   érték1: utasítás1;
   érték2: utasítás2;
   ...
   értékN: utasításN
END;

Az adott ágban szereplő utasítás végrehajtása után a program a CASE szerkezetet követő (az ELSE ág, vagy az END szó utáni) utasítással folytatódik.
Mint említettük, a szelektor és az egyes eseteket azonosító konstansok csak sorszámozott típusúak lehetnek. A VALASZT1.PAS programban a CASE utasítás szelektora egész típusú:

PROGRAM VALASZT1;
VAR JEGY:INTEGER;
BEGIN
  WRITE('A vizsga eredmenye: ');READ(JEGY);
  CASE JEGY OF
    1: WRITELN('elegtelen');
    2: WRITELN('elegseges');
    3: WRITELN('kozepes');
    4: WRITELN('jo');
    5: WRITELN('jeles')
    ELSE WRITELN('hibas erdemjegy');
END.

Ha több esethez ugyanaz az utasítás tartozik, akkor az eset konstansokat vesszővel elválasztva is megadhatjuk. A NAPOS függvény megadja a paraméterként megadott hónapról, hogy hány napos.

FUNCTION SZOKOEV(E:INTEGER):BOOLEAN;
BEGIN
  SZOKOEV:=FALSE;
  IF (EV MOD 4=0) AND (EV MOD 100<>0) OR (EV MOD 400=0) THEN
    SZOKOEV:=TRUE
END;

FUNCTION NAPOS(H:INTEGER):INTEGER;
BEGIN
  CASE H OF
    4,6,9,11: NAPOS:=30;
    2: IF SZOKOEV(EV) THEN NAPOS:=29 ELSE NAPOS:=28
    ELSE NAPOS:=31
END;

7.2.3. Ciklusutasítások
Programozás során gyakran találkozunk olyan feladattal, amikor egy tevékenységet egymás után többször kell elvégezni. Az ilyen ismétlődő tevékenységek programnyelven történő megfogalmazására használjuk a ciklusutasításokat.
A Pascal nyelv a ciklusok szervezését három ciklusutasítással támogatja. Ha előre (a ciklusba való belépés előtt) ismerjük az ismétlések számát, akkor a FOR utasítás használata javasolt. Amennyiben a ciklust valamilyen feltétel vezérli (vagyis az ismétlések száma attól függ, hogy mikor válik a feltétel igazzá vagy hamissá), akkor a WHILE vagy a REPEAT utasításokat használjuk.
A ciklusokat szokás a belépési (illetve a kilépési) feltétel ellenőrzésének helye alapján is csoportosítani. A Pascal ciklusok közül a FOR és a WHILE a ciklusmag végrehajtása előtt tesztel (elöltesztelő ciklus), míg a REPEAT a ciklusmag lefutása után ellenőrzi a feltételt (hátultesztelő ciklus).

A FOR utasítás
A FOR ciklusutasítást akkor használjuk, ha pontosan ismerjük az ismétlések számát. Az utasítás két formában is megadható:

FOR ciklusváltozó:=kezdőérték TO végérték DO utasítás;

illetve

FOR ciklusváltozó:=kezdőérték DOWNTO végérték DO utasítás;

A DO foglalt szó után megadott utasítás (amely lehet összetett utasítás is) alkotja a ciklusmagot. A ciklusmag ismétlésének száma pontosan meghatározható:

végérték-kezdőérték+1

A ciklusváltozó, a kezdőérték és a végérték csak sorszámozott típusú lehet.
A FOR utasítás első formája (az ún. növekvő ciklus) csak akkor hajtja végre a ciklusmag utasításait, ha a kezdőérték kisebb vagy egyenlő mint a végérték. A ciklusváltozó értéke a kezdőértékről indulva a ciklusmag minden lefutása után a eggyel nő. A ciklus akkor fejezi be működését, ha a ciklusváltozó értéke eléri a végértéket.
Az alábbi ciklus 1-től 10-ig kiírja az egész számokat:

FOR I:=1 TO 10 DO WRITELN(I);

A másik FOR utasítás (az ún. csökkenő ciklus) csak akkor kezd el működni, ha teljesül a kezdőérték >= végérték feltétel. A ciklusváltozó értéke ebben az esetben a ciklusmag minden lefutása után a eggyel csökken.
Az alábbi ciklus fordított sorrendben jeleníti meg az angol ABC kisbetűit (CH változó char típusú):

FOR CH:='z' DOWNTO 'a' DO WRITE(CH,' ');

Ha egynél több utasítást kívánunk a ciklusban végrehajtani, akkor a ciklusmagban az összetett utasítást (BEGIN - END) kell használnunk:

FOR ciklusváltozó:=kezdőérték TO végérték DO BEGIN
   utasítások
end;

Külön ki kell emelnünk a ciklusváltozó szerepét. A FOR ciklusból kilépve a ciklusváltozó értéke megőrződik. Ha a ciklusváltozó értékét a ciklusmagon belül megváltoztatjuk, akkor a ciklus működése kiszámíthatatlanná válhat.
A ciklusok - így a FOR ciklus is - egymásba ágyazhatók. Pl.:

PROGRAM SZORZOTABLA;
VAR I,J:INTEGER;
BEGIN
  FOR I:=1 TO 10 DO BEGIN
    FOR J:=1 TO 9 DO
      WRITE(I*J:4);
    WRITELN
  END
END.

Mint említettük, a FOR ciklusban tetszőleges sorszámozott típusú ciklusváltozót használhatunk. Nézzünk meg példaként egy felsorolt típusú ciklusváltozót:

VAR A:(hetfo,kedd,szerda,csutortok,pentek,szombat,vasarnap);
BEGIN
  FOR A:=hetfo TO szombat DO
  ...

A FOR utasításban a ciklusmag egyetlen üres utasításból is állhat. Ilyen ciklus igazán semmire sem jó, azonban akaratlanul könnyen előállítható, ha a DO foglalt szó után véletlenül pontosvesszőt teszünk.

A WHILE utasítás
Ha ciklusszervezés során nem ismerjük az ismétlések számát, akkor a feltételes ciklusutasítások egyikét kell használnunk. Az esetek többségében a programozási feladat bármelyik feltételes ciklussal megoldható. Ha azonban a megadott utasítást legalább egyszer végre kell hajtani, akkor a REPEAT utasítás használata javasolt. Ellenkező esetben a WHILE ciklussal érjük el célunkat, melynek formája:

WHILE feltétel DO utasítás;

A WHILE utasításban szereplő belépési feltétel (logikai kifejezés) vezérli a ciklus végrehajtását. A ciklusmag csak akkor hajtódik végre, ha a feltétel igaz. Ha a feltétel hamisra változik, akkor a ciklus befejezi a működését és a vezérlés a WHILE ciklusutasítást követő utasításra kerül. Abban az esetben, ha a feltétel értéke a ciklusba való belépéskor FALSE, a ciklusmag egyszer sem hajtódik végre. Ha a belépési feltétel TRUE értéke sosem változik meg a ciklus működése során, akkor ún. végtelen ciklust kapunk.
Egynél több utasítás WHILE ciklusban történő végrehajtásához az összetett utasítást kell használnunk:

WHILE feltétel DO BEGIN
   utasítások
END;

A WHILE ciklusok egymásba ágyazásával bonyolultabb feladatok is megoldhatók. Példaprogram:

PROGRAM TORZSTENYEZOK;
VAR SZAM,OSZTO,ELOZO,HATV:INTEGER;
    ELSO:BOOLEAN;
BEGIN
  WRITELN('Torsztenyezokre bontas');
  WRITE('Szam: ');READ(SZAM);
  WRITE('Torzstenyezoi: ');
  ELOZO:=2;HATV:=0;ELSO:=TRUE;
  WHILE SZAM<>1 DO BEGIN
    OSZTO:=2;
    WHILE (SZAM MOD OSZTO)<>0 DO
      OSZTO:=OSZTO+1;
    IF ELSO THEN BEGIN
      ELOZO:=OSZTO;ELSO:=FALSE;
      WRITE(OSZTO)
    END;
    IF ELOZO=OSZTO THEN
      HATV:=HATV+1
    ELSE
      IF HATV>1 THEN BEGIN
        WRITE('^ ',HATV,'* ',OSZTO);
        HATV:=1
      END
      ELSE
        WRITE('* ',OSZTO);
    SZAM:=SZAM DIV OSZTO;
    IF (SZAM=1) AND (ELOZO=OSZTO) AND (HATV>1) THEN
      WRITE('^ ',HATV);
    ELOZO:=OSZTO
  END
END.

A WHILE ciklus magjaként megadott üres utasítás sokkal több problémát okoz, mint a FOR ciklusnál. Az üres utasítás következtében a ciklus feltétele sohasem lesz hamis, így végtelen ciklust kapunk.

A REPEAT utasítás
A feltételes ciklusutasítás másik formája a REPEAT - UNTIL ciklus:

REPEAT
   utasítás1;
   utasítás2;
   ...
   utasításN;
UNTIL feltétel;

A REPEAT utasításban a ciklusmag legalább egyszer mindig végrehajtódik, mivel a feltétel vizsgálata a ciklus végén áll. Az utasítást vezérlő feltételt kilépési feltételnek hívjuk, mivel a ciklus akkor fejezi be működését, ha a feltétel TRUE értékűvé válik. A WHILE és a REPEAT ciklusutasítások feltételei tehát egymás ellentettjei. A másik két ciklusutasítástól eltérően a REPEAT - UNTIL ciklusban az összetett utasítás nélkül is több utasítás elhelyezhető. A ciklusmag határait a REPEAT - UNTIL utasításpáros egyértelműen meghatározza.
Adjuk össze az első húsz egész számot a REPEAT ciklus felhasználásával:

PROGRAM OSSZEG;
VAR SZAM,OSSZEG:INTEGER;
BEGIN
  SZAM:=1;OSSZEG:=0;
  REPEAT
    OSSZEG:=OSSZEG+SZAM;
    SZAM:=SZAM+1;
  UNTIL SZAM>=21;
  WRITELN('Az osszeg REPEAT ciklussal: ',OSSZEG);
END.

7.2.4. A WITH utasítás
A WITH utasítás használata megkönnyíti a rekordváltozó mezőire való hivatkozást. A WITH utasítás általános formája:

WITH rekordváltozó DO utasítás;

ahol az utasítás helyén tetszőleges egyszerű, vagy strukturált utasítás állhat. A rekordváltozó.mezőnév hivatkozás helyett a WITH utasításon belül elegendő csak a mezőnevet megadni. A mezők ekkor automatikusan a WITH utasításban megadott rekordváltozóhoz tartoznak.
Például az alábbi deklaráció után

VAR DATUM:RECORD
      EV:1900..2100;
      HONAP:1..12;
      NAP:1..31;
      KOD:CHAR;
    END;

a rekordnak adhatunk értéket a következő formában is:

DATUM.EV:=1973;
DATUM.HONAP:=12;
DATUM.NAP:=24;
DATUM.KOD:='K';

vagy WITH utasítással is:

WITH DATUM DO BEGIN
  EV:=1973;
  HONAP:=12;
  NAP:=24;
  KOD:='K'
END;

A WITH foglalt szó után több rekordváltozót is felsorolhatunk, mindaddig, amíg ez nem okoz a rekordmezők nevei között kétértelműséget. A

WITH RV1,RV2, ..., RVN DO utasítás;

programszerkezettel ekvivalens az alábbi struktúra:

WITH RV1 DO
   WITH RV2 DO
      ...
         WITH RVN DO
            utasítás;

8. Alapvető I/O műveletek

Minden program egyik legfontosabb része a felhasználóval való kapcsolattartás (kommunikáció). Ennek az a legegyszerűbb módja az, hogy a program adatokat kér a felhasználótól, majd megjeleníti a futási eredményeket. Alaphelyzetben a bemeneti (input) adatok a billentyűzetről érkeznek, míg a program kimeneti (output) eredményei a képernyőn jelennek meg. A Pascal nyelven az adatbevitelt a READ és a READLN, az adatmegjelentést pedig a WRITE és a WRITELN eljárások segítségével végezzük. Ezeket az eljárásokat az eddigi példaprogramokban magyarázat nélkül használtuk, most tekintsük át a működésüket!

8.1. Írás képernyőre - a WRITE és WRITELN eljárások
A Pascal programozásban a szabványos kimenet (standard output) periféria a képernyő. A képernyőre két eljárással írhatunk:

WRITE(paraméterek);
WRITELN(paraméterek);

A vesszővel elválasztott paraméterek az alábbiak közül kerülhetnek ki:

A WRITELN eljárás paraméterek nélkül is megadható:

WRITELN;

Hatására az aktuális képernyő-pozíciót jelölő kurzor a következő sor elejére áll. Amennyiben a WRITELN eljárás hívása előtt a kurzor sor elején állt, úgy a hívás hatására üres sor keletkezik a képernyőn.
A WRITE és a WRITELN eljárások közötti különbségek az alábbiak szerint foglalhatók össze:

A Pascal nyelv lehetőséget biztosít arra, hogy a programunk kimenete tetszetős és áttekinthetőbb legyen. A WRITE és a WRITELN eljárások hívása során a paraméterek alakja az alábbiak szerint adható meg:

kifejezés
kifejezés:W
kifejezés:W:D

A kifejezés tetszőleges numerikus (egész vagy valós), szöveges (karakter vagy karaktertömb), illetve logikai típusú Pascal kifejezés. Más típusú adat (halmaz, felsorolt, stb.) ily módon történő kiírására nincs lehetőség.
A paraméterek első alakját használva az adatok alapértelmezés szerinti formában, a legkevesebb helyet elfoglalva jelennek meg a képernyőn.
A másik két esetben ún. formázott kiírást használunk. Ekkor lehetőségünk van arra, hogy kijelöljük azt a legkisebb mezőszélességet (W), amelyben a kiírt adatokat látni szeretnénk. Amennyiben az általunk kijelölt karakterpozíción nem fér el a kiírandó adat, akkor a rendszer figyelmen kívül hagyja a W előírást. A pozitív egész mezőszélességet tetszőleges kiírható típusú adat esetén megadhatjuk. Ha a mezőszélesség nagyobb, mint amennyi az adat kiírásához minimálisan szükséges, akkor a karakterek a mező jobb széléhez igazítva jelennek meg a képernyőn.
Valós típusú kifejezés esetén a :W és a :W:D formátumot egyaránt választhatjuk. Első esetben a valós szám hatványkitevős alakban jelenik meg. A második esetben a kiírás tizedes tört formájában történik, ahol a D a tizedesjegyek számát határozza meg (vagyis a W mezőszélességből D a tizedesjegyek száma).

8.1.1. Szöveg kiírása a képernyőre
A WRITE és a WRITELN eljárások paramétereként megadott szövegkonstansokat aposztrófok (') között kell megadni.
A szövegek kiírása során - a számokhoz hasonlóan - megadhatjuk annak a mezőnek a szélességét, amelybe a szöveget jobbra igazítva kívánjuk elhelyezni:

WRITELN('Pascal':10)

A példában szereplő szöveg egy 10 karakteres mező jobb oldalán helyezkedik el.

8.1.2. Egész típusú adatok megjelenítése
Az egész típusú eredmények kiírása egyszerűen elvégezhető a WRITE és a WRITELN eljárások segítségével. Az eljárások paramétereként megadhatunk egész konstanst, változót vagy tetszőleges egész típusú kifejezést:

WRITELN(I);
WRITELN(3+4,7-I);

Több paraméter megadásakor azonban fennáll annak a veszélye, hogy a képernyőn más eredményt látunk, mint amit várunk a programtól. Ennek oka, hogy a WRITE és a WRITELN eljárások alaphelyzetben minden egész szám mögé egy szóközt is raknak. Ez sokszor kényelmes, hisz nem nekünk kell a kiírt számokat szóközzel elválasztani egymástól, néha azonban zavaró a számok utáni szóköz.
Erre is megoldást biztosíthat azonban a formázott kiírás:

WRITELN(1:1,I:6,J:3);

A formázott kiírás során a mezőszélességgel definiáljuk, hogy a paraméter értéke hány karakteres mezőben jelenjen meg a képernyőn. Ha a mezőszélesség nagyobb, mint a szám jegyeinek száma (beleértve az esetleges negatív előjelet is), akkor a kiírt szám előtt szóközök jelennek meg (jobbra igazítás). A formázott kiírás segítségével az eredmény tagolásán túlmenően egyszerűen megoldhatjuk az adatok oszlopokba szervezését.
Ha a megadott szélességű mezőben nem fér el a szám, akkor a Pascal automatikusan az alapértelmezés szerinti kiírási formátumot használja. Ekkor a paraméter értéke úgy jelenik meg a képernyőn, mintha meg se adtuk volna a mezőszélességet.

Tetszőleges INTEGER típusú számot kiírathatunk hexadecimális alakban, az alábbi formát használva:

WRITELN(kifejezés:m:H)

Ahol m a mezőszélességet adja meg. Ha m=1, vagy m=2, akkor csak az legkisebb helyi értéken lévő 1, vagy 2 számjegyet írja ki. Ha m=3 vagy m=4, akkor 4 számjegyet jelenít meg. Ha m nagyobb, mint négy, a szám elé vezető szóközök is kerülnek. Pl.:

WRITELN(MAXINT:4:H);

8.1.3. Valós értékek kiírása
A valós (lebegőpontos) számok kiírásának alapértelmezés szerinti formátuma a hatványkitevős alak. Alaphelyzetben a valós szám normál alakban jelenik meg a képernyőn, ami azt jelenti, hogy a tizedespont előtt mindig egy egészjegy áll. A Alapértelmezés szerint a törtrész 5 jegyen, míg az E betű után álló hatványkitevő két helyi értéken kerül kiírásra. Nézzünk néhány példát az elmondottakra!

X
alapértelmezett megjelenítési formátum
0.001
-1.2
11.3
92560.43
22/7
1.00000E-03
-1.20000E+00
1.13000E+01
9.25604E+04
3.14286E+00

A valós számok kiírásánál is megadhatunk mezőszélességet, mint például:

WRITELN(22/7:18);
WRITELN(0.0001:W);

A mezőszélesség kiszámításánál az alábbiakat kell figyelembe venni. Három hely szükséges a szám előjele, az egész része és a tizedespont számára, a törtrész 5 karakter, a hatványjel és a kitevő 4 karakter. Tehát a mezőszélességnek nagyobbnak kell lenni 12-nél. Ha a kiszámolt értéknél kisebb vagy egyenlő mezőszélességet adunk meg, akkor a rendszer automatikusan az alapértelmezés szerint formát használja.

A valós számok sokkal gyakrabban használt kiírási formátuma a tizedesjegyek számát (D) is tartalmazza. Ekkor a valós szám tizedes törtként kerül ki a képernyőre.

WRITELN(22/7:24:21);
WRITELN =0.0001:W:D);

Ebben az esetben a mezőszélesség (W) magában foglalja az előjelet, az egész jegyek számát, a tizedespontot és a tizedesjegyek számát. A tizedesjegyek maximális száma 21.

X:W:D
tizedes tört forma
0.001:10:4
0.001:10:1
-1.2:6:2
11.3:7:4
92560.43:9:2
22/7:11:8
0.0010
0.0
-1.20
11.3000
92560.42
3.14285707

A tizedes tört forma használata során mindig a tizedesjegyek számának a betartása az elsődleges. Előfordulhat olyan eset, lásd a fenti táblázat második sorát), amikor a kiírás során elveszítjük a szám értékes jegyeit.

8.1.4. Logikai értékek kiírása
A logikai konstansok, változók és kifejezések értékének kiírásakor kétféle eredményt kaphatunk a képernyőn: a TRUE vagy a FALSE szót. Az ilyen eredmények kiíratásánál is megadhatunk mezőszélességet, ekkor a TRUE és a FALSE szavak jobbra igazítva jelennek meg.

WRITELN(I>J:8);

8.1.5. Kimeneti eszközök
A HiSoft Pascal a programunk kimeneti eredményei alapértelmezés szerint a standard kimeneti eszközön, azaz a képernyőn jelennek meg. Ezen kívül még egy kimeneti eszközt választhatunk: a nyomtatót. A két eszköz között a CHR(16) kód kiírásával lehet váltani.
A nyomtatót a 123-as csatornán keresztül éri el a HiSoft Pascal.

8.1.6. Képernyőtörlés
A HiSoft Pascal eredeti Spectrum verziójában a PAGE eljárás szolgál a képernyő törlésére. A PAGE eljárás a kimeneti terminálra CHR(12) kódot ír ki, ami ugyan Spectrum-on képernyőtörlést eredményez, de az Enterprise verzióban csak a nyomtatón történő soremelésre szolgál. A képernyőtörlés azonban erre szolgáló eljárás hiányában sem sokkal bonyolultabb:

WRITE(CHR(26));

8.2. Olvasás billentyűzetről - a READ és a READLN eljárások
A Pascal-ban a szabványos adatbeviteli (standard input) periféria a billentyűzet. A READ eljárás felfüggeszti a program futását és a billentyűzetről az <ENTER> lenyomásáig beolvassák az adatokat:

READ(paraméterek);

A paraméterek csak numerikus vagy karakteres változók lehetnek, amelyek ilyen módon kapnak értéket. Ennek megfelelően az eljárást csak egész és valós számok, karakterek beolvasására használhatjuk. (A BOOLEAN típusú változók értékét nem lehet billentyűzetről beolvasni!) A

READ(V1,V2,...,Vn)

utasítás egyenértékű a

BEGIN READ(V1);READ(V2);...;READ(Vn) END;

összetett utasítással (ahol V1, V2, Vn karakter, integer, valós vagy - előre nem definiált - string típusú).

A READ eljárás felfüggeszti a program futását és várakozik az adatok billentyűzetről történő bevitelére. A READ utasítás a begépelt adatokból sorra értéket ad a paramétereknek. Ha kevesebb adatot adtunk meg, mint ahány paraméter a paraméterlistán szerepel, akkor további adatok megadására vár az eljárás. Ha viszont több adatot gépeltünk be, mint amennyi szükséges, akkor a fel nem dolgozott adatokat a következő READ eljárás kapja meg!
A beolvasás során a számokat az egész és a valós konstansok képzésénél ismertetett szabályok szerint kell megadnunk. Több numerikus adat beadásánál figyelni kell arra, hogy az adatokat legalább egy szóköz vagy az <ENTER> billentyű lenyomása válassza el egymástól.
A READLN eljárás működése eltér a Pascal-ban hagyományos funkciójától. A HiSoft Pascal-ban szövegfüzérek beolvasáséra szolgál, ezért a 12 fejezetben lesz róla szó.

9. Eljárások és függvények

A programozás során elég gyakran találkozunk olyan szituációval, amikor ugyanazt a tevékenységet a program különböző pontján kell elvégezni. Eddigi ismereteink alapján a tevékenységhez tartozó programrészek ismételt megírásával (másolásával) oldhatjuk meg a feladatot. A másolás következtében azonban nagyméretű, áttekinthetetlen és nehezen módosítható programhoz jutunk.
Az igazi megoldást csak az alprogramok bevezetése biztosítja számunkra. Az alprogram egy olyan névvel ellátott utasításblokk, amelyet valamilyen konkrét feladat elvégzésére készítünk. Az alprogramot a program különböző helyeiről a neve segítségével aktivizáljuk (hívjuk). Az alprogram a hívás hatására végrehajtja a benne rögzített feladatot, majd visszaadja a vezérlést a hívó programnak.
Az alprogramok használatának előnyei:

A Pascal nyelven az alprogramokat eljárások (PROCEDURE) vagy függvények (FUNCTION) formájában állíthatjuk elő. A Pascal nyelv beépített eljárásainak és függvényeinek egy részével már találkoztunk, például a kiírást a szabványos WRITELN eljárás segítségével végeztük, és számok négyzetgyökének kiszámítására az SQRT függvényt használtuk.
Ahhoz, hogy különböző értékeket tudjunk megjelenhetni a képernyőn, illetve hogy különböző számok gyökét számítsuk, a WRITELN és az SQRT alprogramok hívásakor paramétereket kellett megadnunk.

9.1 Az alprogramok helye a Pascal programban
A Pascal nyelvvel való ismerkedésünk legelején tisztáztuk, hogy minden azonosítót a felhasználás helye előtt deklarálni kell. Ugyanez a szabály vonatkozik az eljárások és a függvények nevére is, ezért az alprogramokat a program (alprogram) deklarációs részében kell elhelyezni.
Minden eljárás és függvény tartalmazhat saját (lokális) deklarációkat, vagyis alprogramon belül szintén deklarálhatunk címkéket, konstansokat, típusokat, változókat, sőt eljárásokat és függvényeket is. A lokális deklaráció felépítése teljesen megegyezik a deklarációs rész felépítésével.

9.2. Eljárások
A Pascal nyelvben eljárásnak (PROCEDURE) hívjuk azokat a névvel ellátott programrészeket, amelyek egy-egy jól körülhatárolható feladat (részfeladat) megoldására készülnek. Az eljárások felépítésének általános alakja:

PROCEDURE eljárásnév(formális paraméterlista);
{ lokális deklarációs rész:
LABEL, CONST, TYPE, VAR, PROCEDURE, FUNCTION deklarációk }
BEGIN
   { az eljárás törzse: utasítások }
END;

Az eljárások szerkezete sokban hasonlít a Pascal program felépítéséhez. Eltérés csak az eljárás fejlécében van és abban, hogy az eljárás törzsét nem pont, hanem pontosvessző zárja. Az eljárás fejlécében a PROCEDURE foglalt szó után meg kell adni az alprogram azonosítóját (nevét), amit a paraméterek deklarációja követ (ha van paramétere az eljárásnak). Paraméterek nélküli eljárás esetén a fejléce sokkal egyszerűbb alakot ölt:

PROCEDURE eljárásnév;

Az eljárásnév képzésére a harmadik fejezetben elmondott szabályok érvényesek. Az eljárást az eljárásnév segítségével aktivizáljuk (hívjuk). Az eljáráshívás önálló Pascal utasítás, melynek általános formája:

eljárásnév(aktuális paraméterlista);

Ha az eljárásnak nincs paramétere, akkor a hívás a következőképpen végezhető el:

eljárásnév;

Az eljáráshívás hatására a vezérlés a hívott alprogramhoz kerül, és mindaddig ott is marad, amíg az alprogram be nem fejezi működését. Egy eljárás futása általában akkor ér véget, ha az eljárás törzsében megadott utasítások mindegyike végrehajtódott, és elértük az utasításblokkot záró END foglalt szót. Ezt követően a vezérlés visszakerül a hívó programhoz, az eljáráshívást követő utasításra.

9.2.1. Az eljárás paraméterei
A paramétereket az eljárás fejlécében a név után, kerek zárójelek között kell felsorolnunk. A paraméterek lehetővé teszik, hogy ugyanazt a programrészt kicsit másképpen, más adatokkal futtassuk. Az alprogramok használatában rejlő előnyök teljes kiaknázásához paraméteres eljárásokat kell írnunk, azonban készíthetünk olyan eljárást is, amelynek nincsenek paraméterei.

Paraméter nélküli eljárások
A paraméter nélküli eljárások általában mindig ugyanazt a műveletet végzik. (Pl. törli a képernyőt.) Ha egy paraméter nélküli eljárás működését mégis kívülről kívánjuk vezérelni, akkor globális változókat használhatunk az alprogrammal való kommunikációra. (Első megközelítésben globális változók alatt az alprogramon kívül definiált változókat értjük.) Általánosságban elmondható, hogy a paraméter nélküli eljárások és a globális változók együttes használata rossz programozási technikát takar.

A paraméter nélküli eljárásokra példa a HiSoft Pascal előre definiált HALT eljárása, amely megállítja a program futását, kiír egy "Halt at PC=nnnn" sort (nnnn a memóriacím, ahol a HALT megállított a programfutást), majd visszatér az editorhoz. Az eljárást jellemzően hibakereséshez és a program teszteléséhez használhatjuk.

Érték paraméterek
Az eljárás fejlécében szereplő paramétereket formális paramétereknek nevezzük. Ezek meghatározzák, hogy az eljárás hívásakor hány és milyen típusú adatot (hívási paramétert) kell megadnunk. Az eljárás törzsében a formális paraméterekre mint helyi (lokális) változókra hivatkozhatunk.
A paraméterek deklarációját a változódeklarációhoz hasonlóan kell elvégezni: a paraméternevet kettősponttal elválasztva követi a paraméter típusa:

PROCEDURE KIIR(X:REAL);
BEGIN
  WRITELN(X:10:3);
END;

Ha több paramétert használunk, akkor az egyes paraméter-leírások közé pontosvesszőt kell tennünk:

PROCEDURE KIIR(X:INTEGER; Y:INTEGER; VALUE:REAL);

A közönséges változókhoz hasonlóan, az azonos típusú paramétereket együtt is deklarálhatjuk:

PROCEDURE KIIR(X,Y:INTEGER; VALUE:REAL);

A HiSoft Pascal nem engedi meg, hogy típusleírást (mint például az ARRAY[1..10] OF INTEGER) helyezzünk el a paraméterlistán. Az ilyen és hasonló típusokhoz először típusnevet kell rendelnünk (TYPE), amit aztán már minden korlátozás nélkül felhasználhatunk.

Értékparaméterek
A fenti eljárásfejlécekben megadott paraméterek mindegyike értékparaméter. Az értékparaméter csak egyirányú kommunikációt tesz lehetővé a hívó program és a hívott eljárás között. Eljáráshíváskor minden egyes aktuális (hívási) paraméter értéke a paraméterlista azonos helyén álló formális paraméterbe másolódik. Az eljáráson belül az átadott értékeket a formális paraméteren keresztül érhetjük el és használhatjuk fel.
A fordítóprogram az eljárás hívásakor ellenőrzi, hogy az aktuális és a formális paraméterek száma és típusa megegyezik-e. Ha a paraméterek száma különböző, vagy ha a két típus nem értékadás-kompatibilis (ami enyhébb a típusazonosság feltételénél), akkor hibajelzést kapunk.

Az értékparaméter összetett típusú is lehet. Ekkor a paraméterdeklarációban használt típusnévvel deklaráljuk a hívási paraméterlistában megadott változókat is. Az alábbi eljárás (HKIIR) a megadott karakterhalmaz elemeit jeleníti meg a képernyőn:

PROGRAM HALMAZKI;
TYPE THALMAZ=SET OF 'A'..'Z';
VAR HALMC,HALMV:THALMAZ;
PROCEDURE HKIIR(H:THALMAZ);
VAR C:CHAR;
BEGIN
  FOR C:='A' TO 'Z' DO
    IF C IN H THEN WRITE(C:2);
  WRITELN
END;
BEGIN
  HALMC:=['A','D'..'G','Z'];
  HKIIR(HALMC);
  HALMV:=HALMC-['E','G']+['A','B','X'..'Z'];
  HKIIR(HALMV)
END.

Változó-paraméterek
A programozás során gyakran találkozhatunk olyan feladattal, amikor egy eljárás a paraméterein fejti ki hatását (például csökkenti, növeli vagy felcseréli azokat). Ekkor a bemenő (egyirányú) értékparaméterek használatával nem érjük el a célunkat, hiszen az ilyen paramétereken elvégzett műveletek hatása csak az eljáráson belül érzékelhető. Megoldást az ún. változó-paraméterek bevezetése jelenti számunkra. A változó-paraméter kétirányú adatcserét biztosít a hívó program és a hívott eljárás között.
Az eljárás formális paraméterlistáján bármelyik értékparamétert változó-paraméterré alakíthatjuk, ha a paraméter neve elé a VAR foglalt szót tesszük.

PROCEDURE SWP(VAR X,Y:INTEGER);
VAR TMP:INTEGER;
BEGIN
  TMP:=X;X:=Y;Y:=TMP
END;

Összetett típusú változó-paramétert használata esetén nem szabad megfeledkeznünk a típusnév előállításáról!
A formális paraméterlista egyaránt tartalmazhat érték- és változó-paramétereket. A kétféle paraméter közötti különbség igazából az eljárás hívásakor érzékelhető.
A változó-paramétert úgy kell elképzelni, hogy az eljárás hívásakor maga az aktuális paraméterként megadott változó (nem pedig annak értéke) adódik át az alprogramnak. Az eljárás hívásakor aktuális változó-paraméterként csak változót adhatunk meg, konstanst értelemszerűen nem. További megkötés, hogy a változó típusának meg kell egyeznie a formális paraméter leírásában használt típussal.

9.2.2. A lokális deklarációk
Minden eljárás rendelkezhet egy ún. saját (lokális) deklarációs résszel, ahol az eljárásban felhasználni kívánt címkéket, konstansokat, típusokat, változókat és alprogramokat deklarálhatjuk. (Az előző példában bemutatott SWP eljárás - mely két változó értékét cseréli fel - a TMP változót hozta létre "saját használatra".) A lokális deklarációs rész az eljárás fejléce és a törzse között helyezkedik el. A lokálisan deklarált azonosítók az eljáráson kívülről nem érhetők el.
A lokális változók a paraméterekhez hasonlóan csak az eljárás hívásakor jönnek létre, és megszűnnek létezni, ha az eljárás visszatér a hívó programhoz. A legtöbb programozási nyelv az ilyen jellegű objektumait egy speciálisan kezelt adatterületen, a veremben (stack) tárolja. Ezzel szemben a főprogram szintjén létrehozott változók a program adatszegmensében helyezkednek el, és statikus élettartamúak. A statikus élettartamú változó a program indításakor jönnek létre, és csak a programból való kilépéskor semmisülnek meg.

9.2.3. Az eljárás törzse
Az eljárás törzse (melyet a BEGIN ... END foglalt szavak határolnak) tartalmazza azokat a Pascal utasításokat, amelyek az eljárás hívásakor végrehajtódnak. Az eljárások akkor fejezik be működésüket, amikor a blokk utolsó utasítása is végrehajtódott.

9.2.4. Előzetes (FORWARD) deklaráció
Mint ismeretes, a Pascal nyelvben minden azonosítót a felhasználása előtt deklarálni (definiálni) kell. Eddigi ismereteink alapján ez a szabály mindig betartható volt. Abban az esetben azonban, ha két egymást kölcsönösen hívó alprogramot készítünk, azzal a problémával találjuk szemben magunkat, hogy az első alprogram a később definiált alprogram hívását tartalmazza. A probléma egyszerűen megoldható a második alprogram előzetes deklarációjával.
Az előzetes deklaráció csupán az alprogram fejlécét tartalmazza, amelyet a FORWARD Pascal direktíva és egy pontosvessző követ. Az előzetesen deklarált alprogramra ugyanúgy hivatkozhatunk más alprogramokból, mint a már definiáltakra. Az előzetesen deklarált alprogram "teste", vagyis az alprogram teljes deklarációja (definíciója) a deklarációs részben bárhol megadható.
Az előzetesen deklarált eljárás teljes deklarációjában elegendő a PROCEDURE foglalt szó után az eljárás nevét megadni.

Példa:

PROCEDURE WI(N,A,H,K:INTEGER);FORWARD;
...
PROCEDURE WI;
BEGIN
...
END;

9.3 Függvények
A programozási gyakorlatban gyakran előfordul, hogy bizonyos feladatok (számítások) elvégzésének egyetlen érték az eredménye, amit általában kifejezésekben kívánunk felhasználni. Az ilyen feladatok eljárások segítségével mindig megoldhatók, hiszen az értéket változó-paraméterben visszaadhatjuk a hívó program valamely változójának. A visszaadott változó már szerepelhet további számításokban és összehasonlításokban.
Példaként tekintsük szögek tangensének meghatározását:

PROGRAM SEC;
VAR SV:REAL;
PROCEDURE SEC(A:REAL;VAR X:REAL);
BEGIN
  X:=1/COS(A)
END;
BEGIN
  SEC(3.141/4,SV);
  WRITELN(SV);
END.

A Pascal nyelv rendelkezik olyan eszközzel, amely lehetővé teszi, hogy az egyetlen értéket visszaadó alprogramokat a matematikában megszokott módon függvényként hívjuk. A fenti programot módosítva jól láthatók a függvényhasználat előnyei:

PROGRAM PTAN;
FUNCTION SEC(A:REAL):REAL;
BEGIN
  SEC:=1/COS(A)
END;
BEGIN
  WRITELN(SEC(3.141/4))
END.

A függvények általános felépítése:

FUNCTION függvénynév(formális paraméterlista):függvénytípus;
{ lokális deklarációs rész:
LABEL, CONST, TYPE, VAR, PROCEDURE, FUNCTION deklarációk }
BEGIN
   { az eljárás törzse: utasítások }
   függvénynév:=kifejezés;
END;

9.3.1. A függvények és az eljárások összehasonlítása
A Pascal nyelvben alprogramokat eljárásként (PROCEDURE) és függvényként (FUNCTION) egyaránt készíthetünk. A kétféle alprogram között nincs lényegi különbség, az alkalmazásuk módja határozza meg, hogy melyiket választjuk. A függvények sok mindenben (például a paraméterezés, a lokális deklarációk használata stb.) hasonlítanak az eljárásokra, azonban három lényeges dologban különböznek is tőlük:

  1. Minden függvény a nevével értéket ad vissza. A függvényérték típusát (a függvény típusát) a függvény fejlécében kell rögzítetni. A Pascal csak sorszámozott, valós és mutató típusú függvények készítését támogatja.
  2. Az eljáráshívások önálló utasításai a Pascal nyelvnek. Ezzel szemben a függvényhívás csak olyan kifejezésben szerepelhet, melynek típusa kompatibilis a függvény típusával.
  3. A függvényérték visszaadásáról magunknak kell gondoskodni. A függvényből való kilépés előtt a függvény nevét (mint egy közönséges változót) az értékadás bal oldalán használva definiálhatjuk a visszatérési értéket.

9.3.2. A függvények előzetes deklarációja
Az eljárásokhoz hasonlóan, a kölcsönösen egymást hívó függvények esetén szintén a FORWARD Pascal direktívát használjuk az előzetes deklarációk elkészítésére. Amennyiben a MAX maximumkereső függvény előzetes deklarációja:

FUNCTION MAX(A,B:INTEGER;):INTEGER;FORWARD;

akkor a teljes deklaráció az alábbi formában adható meg:

FUNCTION MAX;
BEGIN
  IF A>B THEN MAX:= A
     ELSE MAX:=B;
END;

9.4. Rekurzív alprogramok
A matematikában lehetőség van bizonyos adatok és műveletek rekurzív definiálására. A rekurzív algoritmusokban a soron következő lépéshez az előző lépések elvégzésekor kapott eredményeket használjuk fel. A programozási nyelvekben a rekurzióról akkor beszélünk, amikor egy alprogram saját magát hívja (önrekurzió), vagy ha két (vagy több) alprogram felváltva hívja egymást (kölcsönös rekurzió). A rekurzív problémák rekurzív alprogrammal viszonylag egyszerűen megoldhatók, azonban ez a megoldás általában idő- és memóriaigényesebb. Minden rekurzív problémának létezik iteratív megoldása is, amely általában nehezebben programozható, de több szempontból hatékonyabb lehet a rekurzív megoldásnál.
A rekurzió alkalmazásának klasszikus példája, a Leonardo de Pisa (Fibonacci) nyúl feladata:

Hány nyúl párunk lesz 3,4,5,...,n hónap múlva, ha egy nyúl pár kéthónapos kortól kezdve havonta egy-egy új párt hoz világra? Feltételezzük, hogy az új párok is ezen törvény alapján szaporodnak, és mind életben maradnak.

A megoldást a Fibonacci számok sora tartalmazza (ha a 0 kezdőelemet figyelmen kívül hagyjuk):

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, ...

A sor n-edik elemének meghatározása az alábbi rekurziós szabály szerint történik:

a(0)=0
a(1)=1
a(n)=a(n-1)+a(n-2),     n=2, 3, 4, ...

Kihasználva azt, hogy a Pascal nyelvben egy alprogram önmagát is meghívhatja, az alábbi elegáns megoldáshoz jutunk:

PROGRAM FIBONACCI;
VAR I:INTEGER;
FUNCTION FIB(N:INTEGER):INTEGER;
BEGIN
  IF N<=1 THEN FIB:=N
    ELSE FIB:=FIB(N-1)+FIB(N-2)
END;
BEGIN
  FOR I:=0 TO 20 DO
    WRITE(FIB(I))
END.

A rekurzív megoldás általában rövidebb és áttekinthetőbb, mint az iteratív (ciklusos) megoldás. Ez a megállapítás azonban csak a programírásra érvényes.
A program futása során az alprogram minden újabb hívásakor újabb memóriaterület kerül lefoglalásra a veremben a paraméterek és a lokális változók számára, ami a memória elfogyásához vezethet. Az ismétlődő hívások másik kellemetlen következménye a számítási idő jelentős növekedése. A rekurzív alprogramok készítésékor mindig mérlegelni kell a megoldás memória- és időigényét. Ha egy feladat rekurzív megoldásának létezik egy viszonylag jól programozható iteratív párja is, akkor általában az utóbbi használata javasolt.

A rekurzió alkalmazása az esetek többségében nem indokolt, azonban vannak olyan speciális problémák, amikor a feladat iteratív megoldása túl bonyolult lenne. Az ilyen feladatok egyike a Hanoi tornyai nevet viseli:

Adott három rúd: egy réz (A), arany (B) és egy ezüst (C). A réz (A) rúdon induláskor N darab különböző átmérőjű lyukas korong helyezkedik el, az átmérők szerinti csökkenő sorrendben. A feladat a korongok átrakása az ezüst (C) rúdra, az alábbi szabályok figyelembevételével:

  • az arany rúd csak közbenső tárolásra használható,
  • minden lépésben csak egy korong mozgatható,
  • minden korong csak nálánál nagyobb átmérőjű korongra helyezhető.

(A feladat egy legendán alapul, amely szerint Hanoi közelében található kolostorban 64 aranykorongból álló tornyot raknak át a szerzetesek, akik minden nap egyetlen korongot mozgatnak. A legenda szerint akkor lesz vége a világnak, amikor az utolsó korong is átkerül a másik rúdra.)
A feladat megoldása:

PROGRAM HANOI;
VAR KORONG:INTEGER;
PROCEDURE HANOI(N:INTEGER;HONNAN,MIVEL,HOVA:CHAR);
BEGIN
  IF N>0 THEN BEGIN
    HANOI(N-1,HONNAN,HOVA,MIVEL);
    WRITELN(N:3,'. korongot tedd ',HONNAN,' rudrol ',HOVA,' rudra.');
    HANOI(N-1,MIVEL,HONNAN,HOVA)
  END
END;
BEGIN
  WRITELN(CHR(26),'Hanoi tornyai');
  WRITELN('A korongok szama: ');READ(KORONG);
  WRITELN;
  HANOI(KORONG,'A','B','C')
END.

A rekurziónak létezik egy sokkal ritkábban használt változata is, az ún. kölcsönös rekurzió. A fentiekben bemutatott önrekurziót megvalósító példáktól eltérően a kölcsönös rekurzió során az alprogram nem közvetlenül önmagát hívja meg, hanem két vagy több rutin, kölcsönösen hívja körbe-körbe egymást.
Kölcsönös rekurzió programozása esetén elkerülhetetlen az előzetes (FORWARD) deklaráció alkalmazása.

9.5. Az azonosítók érvényességi tartománya
A Pascal programot a főprogram és a tetszőleges mélységben egymásba ágyazott alprogramok (blokkok) hierarchikus struktúrája alkotja. A blokkszerkezet lehetővé teszi, hogy azonosítókat nemcsak a főprogram, hanem bármely alprogram szintjén deklaráljunk (definiáljunk).
Mielőtt egy Pascal objektumot (változót, alprogramot stb.) felhasználnánk, két kérdésre kell választ keresnünk:

Az első kérdésre a választ a változók élettartama, míg a másodikra az azonosítók érvényességi tartománya (köre) adja.

Az azonosítók érvényességi tartománya a Pascal program azon részeit jelöli, ahonnan elérhetjük az azonosítóval jelölt objektumot. Ahhoz, hogy a programon belül helyesen használjuk az azonosítóinkat, az alábbi szabályokat kell szem előtt tartanunk:

  1. Minden azonosítót a felhasználás helye előtt deklarálni kell.
  2. A Pascal-ban egy azonosító érvényességi tartománya arra a blokkra terjed ki, amelyben az azonosítót deklaráltuk.
  3. Egy blokkon belül minden objektumot egyedi azonosítóval (névvel) kell ellátnunk.
  4. Ugyanazzal a névvel különböző blokkokban különböző objektumokat deklarálhatunk.
  5. Ha a saját készítésű alprogram neve megegyezik valamelyik szabványos alprogram nevével, akkor a szabványos eljárás vagy függvény elérhetetlenné válik abból a blokkból, amelyik a saját alprogram definícióját tartalmazza.

A változók élettartama szoros kapcsolatban van a blokkszerkezettel. Egy változó akkor jön létre, amikor a programunk belép a változót definiáló blokkba. A blokkból való kilépéskor a változó megsemmisül. Legtovább a főprogram szintjén definiált változók élnek. Ezeket, a program teljes futása alatt elérhető változókat globális élettartamú vagy statikus változóknak hívjuk.

10. Memóriahasználat

10.1. A mutató típus és a dinamikus változók
Az eddig használt egyszerű és strukturált típusú változók közös tulajdonsága, hogy rájuk a deklarálás során megadott névvel hivatkozunk. Az így definiált változók mérete a program fordításakor dől el, és a program futása során nem módosítható. A Pascal nyelvben ezeket a változókat statikus helyfoglalású változóknak nevezzük.
A hatékony memória-felhasználás érdekében szükség van arra, hogy bizonyos memóriaterülettel a programozó szabadon gazdálkodjon. A Pascal nyelv rendelkezik olyan eszközökkel, amelyek segítségével a programozó maga foglalhat memóriaterületet a változói számára. A lefoglalt területet fel is szabadíthatja, ha már nincs szüksége a változóra. A felszabadított terület egy következő memóriafoglalás során újra felhasználható. A memóriafoglalás és -felszabadítás műveleteivel a memória dinamikus felhasználását valósítjuk meg. A dinamikus helyfoglalású változók számára rendelkezésre álló területet a Pascal-ban halomterületnek (heapnek) nevezzük. A következőkben megismerkedünk a dinamikus memóriakezelés eszköztárával.
Mint ahogy azt az előző fejezetekben láttuk, a

VAR A:INTEGER;

deklaráció hatására a fordítóprogram egy 2 bájt méretű területet foglal le a memóriában az A változó számára. Az A változóra a nevének felhasználásával hivatkozhatunk:

A:=22;A:=A+10;

Ahhoz, hogy a memóriafoglalás fenti lépéseit a program futása során végezzük el, meg kell ismerkednünk egy új változótípussal, a mutatóval (pointerrel). A mutató olyan speciális változó, amely memóriacímet tartalmaz. A Pascal nyelvben a mutatók lényeges tulajdonsága, hogy típussal rendelkeznek. A típus kijelöli, hogy a dinamikus helyfoglalás során mekkora területet kell a mutatóhoz hozzárendelni. A mutatók deklarálásának általános formája:

VAR változónév:^típusnév;

ahol a típusnév tetszőleges szabványos vagy felhasználó által definiált típus neve. Például:

VAR AP:^INTEGER;

Az AP mutató a deklarálás után semmilyen meghatározott címet sem tartalmaz, tehát még "sehova se mutat". A programon belüli helyfoglalás elvégzésére a NEW eljárást használhatjuk, amely a lefoglalt terület címét a paraméterként átadott mutatóba tölti:

NEW(AP);

A Pascal-ban létezik egy előre definiált NIL konstans, amelyet a nulla (üres) mutató jelölésére használunk:

AP:=NIL;

Ha a memóriafoglalás sikeres volt (rendelkezésre állt még elég memória), következhet a lefoglalt terület felhasználása. A mutató által kijelölt memóriaterületre a mutató nevével és az utána megadott ^ jellel együtt hivatkozunk (a ^ jel az Algol nyelvben használt nyíl karaktert idézi):

AP^:=22;
AP^:=AP^+10;

Jól látható a fejezet elején definiált A változó és az AP^ hivatkozás közötti analógia. A mutató neve után álló ^ jel hatására nem a mutatót, hanem az általa kijelölt memóriaterületet érjük el.
Ha már nincs szükségünk az AP^ egész típusú változóra (a dinamikusan lefoglalt területre), akkor azt a RELEASE eljárás hívásával törölhetjük:

RELEASE(AP);

A strukturált típusokhoz csak akkor definiálhatunk mutatót, ha a típushoz egyetlen típusnevet rendelünk a deklarációs rész TYPE szekciójában.

TYPE TTOMB=ARRAY[-5..5] OF INTEGER;
VAR PTOMB:^TTOMB;

A Hisoft Pascal-ban, egy típusdefiníciós részben az alaptípus definíciója nem állhat a rá mutató típus után. Ezért pl. láncolt listás adatszerkezet definiálására nem használható a szabványos Pascal-ban és Turbo Pascal-ban megszokott forma:

type nevt=string[nevhossz];
     szamt=string[szamhossz];
     mutato=^elem;
     elem=record
          nev:nevt;
          telszam:szamt;
          kovetkezo:mutato
     end;

Helyette az alábbi definíció használható:

TYPE NEVT=ARRAY[1..NEVHOSSZ] OF CHAR;
     SZAMT=ARRAY[1..SZAMHOSSZ] OF CHAR;
     ELEM=RECORD
          NEV:NEVT;
          TELSZAM:SZAMT;
          KOVETKEZO:^ELEM
     END;
     MUTATO=^ELEM;

A MARK eljárás segítségével egy mutatóban feljegyezhetjük a heap aktuális állapotát. Ezt a feljegyzett heap-állapotot a RELEASE eljárás hívásával egyszerűen visszaállíthatjuk. Másképp fogalmazva a RELEASE hívással a MARK után lefoglalat összes memóriablokkot egyetlen lépésben felszabadíthatjuk. A MARK eljárás hívása:

MARK (PTR);

A PTR egy tetszőleges típusra mutató változó neve. A Mark eljárás végrehajtása következtében a PtrVar változó új értéke a halom pillanatnyi teteje lesz.

10.1.1 Tömb a halomterületen
A dinamikus helyfoglalás előnye természetesen akkor tűnik ki igazán, ha nem néhány bájtos, hanem több kilobájtos dinamikus változót (például tömböt, rekordot) használunk. A TOMBT típusú tömböt a TPTR mutató segítségével helyezzük el a heap-en. A tömböt ebben az esetben a TPTR^ hivatkozással érjük el, a tömb elemeire pedig a TPTR^[I] kifejezéssel hivatkozhatunk. A strukturált típusra mutató pointer deklarációjában a strukturált típust felhasználó által definiált típusnévvel azonosítjuk.

PROGRAM PT1;
CONST MAX=5000;
TYPE TOMB=ARRAY[1..MAX] OF REAL;
TOMBPTR=^TOMB;
VAR TPTR:TOMBPTR;
    I:INTEGER;
BEGIN
  NEW(TPTR);
  FOR I:=1 TO MAX DO
    TPTR^[I]:=I*2;
  I:=TRUNC(RANDOM*MAX+1);
  WRITELN(I,TPTR^[I]:6:0);
  RELEASE(TPTR)
END.

10.2. A memória közvetlen elérése
Az írható-olvasható memória az egyik legfontosabb erőforrása minden számítógépes rendszernek. Az elkövetkezőkben megnézzük, hogyan használhatjuk minél hatékonyabban számítógépünk memóriáját.

10.2.1. A POKE eljárés és a PEEK függvény
A POKE eljárással a belapozott memória tetszőleges címére helyezhetünk el egy értéket. Az eljárás hívása:

POKE(memóriacím,érték);

A memóriacím integer típusú kifejezés. Valamennyi, a memóriát közvetlenül címző utasításnál nehézséget jelent, hogy az integer típus ábrázolási tartománya (-32768..32767) nem esik egybe a megcímezhető memóriaterület méretével (0-65535). Ezért ezen eljárásokban és függvényekben a decimálisan megadott, 32767-nél (7FFFh) nagyobb értékek negatív számként vannak ábrázolva, illetve nekünk is így kell megadnunk. Pl. a C000h memóriacímet a -16384 értékkel címezhetjük meg. Ezért érdemes ilyen esetekben hexadecimális formában megadni a forráskódban a címeket. Pl. a

POKE(-16384,'A');

helyett a

POKE(£C000,'A');

formát érdemesebb használni.
A memóriacímen elhelyezett érték - eltérően a POKE hagyományos BASIC-beli értelmezésétől - egész, logikai, karakter típusú kifejezés, sőt akár valós típus vagy szövegfüzér is lehet. A POKE eljárás a tárolást a megadott típusnak megfelelő számú byte-on végzi el. Pl:

POKE(£4E00,22/7);
POKE(19968,£BFFF);
POKE(-16384,'A');

A PEEK függvénnyel a memória tetszőleges címének tartalmát olvashatjuk ki:

PEEK(memóriacím,típus);

Természetesen a tárolt adatnak megfelelő típust nekünk kell megadnunk, de az eredeti típustól eltérő formában is visszaolvashatjuk a tárolt értékeket. Pl:

POKE(£4E00,'Pascal');
WRITELN(PEEK(£4E00,ARRAY[1..6] OF CHAR));

10.2.2. Az ADDR és a SIZE függvény
Az ADDR függvénnyel bármely változó memóriában elfoglalt címét lekérdezhetjük.

PROGRAM CIM;
VAR I:INTEGER;
BEGIN
  I:=5;
  POKE(ADDR(I)),10);
  WRITELN(I);
END.

A program által kiírt érték: 10.
A SIZE függvény megadja, hogy a paraméterként megadott változó - típusának függvényében - hány byte tárhelyet foglal. Pl:

SIZE(R) = 4, ha R változó valós típusú.

A következő program 2000-et ír ki a képernyőre, mert az 1000 elemű INTEGER típusú tömb tárolásához 2000 byte szükséges:

PROGRAM TOMB;
VAR TB:ARRAY[1..1000] OF INTEGER;
BEGIN
  WRITELN(SIZE(TB))
END.

10.3. Memóriatartalom kimentése, betöltése
A HisSoft Pascal-ban nem létezik file típus. A Pascal program külvilággal való kapcsolattartását mindössze két eljárás hivatott szolgálni. Ezekkel a - Spectrum-os hagyományoknak megfelelően, fapadosan, de nagyszerűen egyszerű és hatékony módon - pótolhatjuk eme hiányosságot.

A TOUT eljárással a memória tetszőleges területét menthetjük ki háttértárolóra:

TOUT(filenév,memóriacím,hossz)

Az eljárás a 'memóriacím'-től kezdődően összesen 'hossz' byte-ot ment ki. Mindkét kifejezés INTEGER típusú. A 'filenév' tetszőleges szövegfüzér lehet, amely érvényes file nevet definiál (tartalmazhat eszköznévre történő hivatkozást is). Az eljárás automatikusan létrehozza a file-t (ha már létező file-t nyitunk meg, azt felülírja), majd a művelet végén lezárja azt.
Az eljárás elsősorban nagyobb adatmennyiséget tároló tömbváltozók kimentésére alkalmazható hatékonyan és pofonegyszerűen az előző pontban ismertetett függvények segítségével. Példaképpen a 12. fejezetben szereplő NEVEK.PAS programban a NAME nevű karaktertömböt egyetlen utasítással kimenthetjük. Próbaképpen egészítsük ki a programot (a programot lezáró END előtti sorba):

TOUT('LISTA.TXT',ADDR(NAME),SIZE(NAME));

A Spectrum verzióval szemben Ep-n tetszőleges típusú tömbváltozót kimenthetünk, majd visszatölthetünk. Ha több változót is el akarunk menteni, nem célszerű egymás után külön-külön file-ba menteni ezeket. Ilyenkor célszerűbb az összes változót egy file-ba menteni. Ezt úgy tehetjük meg, hogy a kimentendő változókat sorban egymás után deklaráljuk a programban (a változók a deklarálásuk sorrendjében helyezkednek el a memóriában), majd a kimentést a legelőször deklarált változó kezdőcímétől kezdjük, a kimentendő terület hosszának pedig a változók összméretét kell megadnunk (eddigi ismereteink alapján már nem okozhat nehézséget ezt összeadni).
Az eljárás hátránya, hogy ha egy nagyobb méretű tömbben amúgy csak pár elemet használunk, az üres mezőket is elmentjük. Ilyenkor a kimentendő memóriaterület méretét az utolsó használt mező sorszámának függvényében is megadhatjuk, de ez már nagyobb körültekintést igényel (elkerülendő az esetleges adatvesztést).
Nézzünk erre egy konkrét példát!

Egy telefonszám nyilvántartó programban a következő definíciók szerepelnek:

CONST NEVHOSSZ=30;SZAMHOSSZ=13;TABLAHOSSZ=100;
      FNEV='TELEFON.DAT';
TYPE NEVT=ARRAY[1..NEVHOSSZ] OF CHAR;
     SZAMT=ARRAY[1..SZAMHOSSZ] OF CHAR;
VAR UTOLSO:INTEGER;
    TABLA:ARRAY[1..TABLAHOSSZ] OF RECORD
      NEV:NEVT;
      TELSZAM:SZAMT;
    END;
...

A neveket és a hozzájuk tartozó telefonszámokat a TABLA rekord típusú tömbben tároljuk (a tábla első elemétől kezdve folyamatosan helyezkednek el a bejegyzések), az UTOLSO - INTEGER típusú változóban a telefonkönyvben szereplő bejegyzések számát tároljuk. A program befejezésekor az UTOLSO változó értékét, és a tábla azon mezőit kell elmenteni, melyek adatot tárolnak:

TOUT(FNEV,ADDR(UTOLSO),(NEVHOSSZ+SZAMHOSSZ)*UTOLSO+2)

A kimentett állományokat a TIN eljárással olvashatjuk vissza:

TIN(filenév,memóriacím)

Az eljárás a 'memóriacím'-től kezdődően, a megadott file-ból feltölti a memóriát. A memóriaterület méretét nem kell és nem is tudjuk megadni, tehát minden esetben az egész file betöltésre kerül.
Ha elmentett (tömb)változót olvasunk vissza, az ADDR függvény segítségével megint egyszerű dolgunk van. A NEVEK.PAS példában elmentett NAME tömböt az alábbi módon olvashatjuk vissza:

TIN('LISTA.TXT',ADDR(NAME));

Értelemszerűen a kimentett adatokat általában olyan típusú változó memóriaterületére célszerű betölteni, amilyenből kimentettük.
A telefonszám nyilvántartó példa esetében a betöltés az alábbi módon végezhetjük el:

TIN(FNEV,ADDR(UTOLSO));

Mindkét eljárás (TIN, TOUT) a 122-es csatornát használja, figyeljünk arra, hogy az eljárások hívása előtt a 122-es csatorna ne legyen megnyitva (magyarul: mi ne használjuk ezt a csatornát)!
A TIN és TOUT eljárások hátránya, hogy nem végeznek hibakezelést, így I/O hiba esetén megszakad a program futása, ami adatvesztést eredményezhet. Hibabiztos file-műveleteket ezért csak az operációs rendszeren keresztül végezhetünk, amire a 14.1. fejezetben láthatunk egy egyszerűbb példát!

10.4. Gépi kódú rutinok beépítése a Pascal programba
Az INLINE utasítással gépi kódú rutinokat közvetlenül a Pascal programba helyezhetjük, megkerülve ezzel a fordítót. Az INLINE utasításban zárójelek között, veszővel tagolva kell megadnunk az egybájtos kódokat (csak konstansokat enged írni). A gépi kódú rutin oda épül be, ahol az INLINE utasítás szerepel. Példa:

PROCEDURE SETVAR(VALTOZO,ERTEK:INTEGER);
BEGIN
  INLINE(£06,£01)     {LD B,1};
  INLINE(£DD,£4E,£04) {LD C,(IX+04)};
  INLINE(£DD,£56,£02) {LD D,(IX+02)};
  INLINE(£F7,£10)     {EXOS 16};
END;

A kódok előállításában és a hibák keresésében a Pascal fordító nem ad támogatást. Ezért a gépi kódú programok közvetlen beépítése csak akkor célszerű, ha rövid, csupán néhány utasításból álló kódról van szó.
Gépi kódú rutinok közvetlen hívására szolgál az USER eljárás. Az USER eljárás a megadott címen lévő utasításra ugrik, onnét folytatja a program futását. Pl.:

PROCEDURE MODE80;
BEGIN
  POKE(£14FD,CHR(40));
  USER(£1EEC);
END;

PROCEDURE MODE40;
BEGIN
  POKE(£14FD,CHR(80));
  USER(£1EEC);
END;

A két eljárás a 40 és 80 karakteres képernyőmódok közötti váltásra szolgál. A programrész, ami a váltást csinálja, nem a runtime-részben van, ezért a Translate-tel lefordított programban ez a megoldás nem fog működni.

Itt kell még megemlíteni az OUT eljárást, amely egy adott port-ra küld egy byte-ot:

OUT(port,érték)

ahol a 'port' integer, az 'érték' karakter típusú érték. A byte típus hiánya itt is okoz némi kavarodást a paraméterek megadásában. Pl. a 255-ös szegmens belapozása a 2-es lapra így történik:

OUT(£B2,CHR(255));

OUT használatánál elsősorban arra kell figyelni, hogy ne olyan lapot módosítson, amit a Pascal is használ. Célszerűbb lehet az Alter paranccsal C000h alá korlátozni a Pascal program területét, akkor a 3. lapra bármikor tetszőleges szegmens kerülhet.
Az OUT párja az INP függvény,amely a Z80-as porton lévő értéket adja meg. A visszaadott érték karakter típusú, a bemeneti paraméter (a port száma) INTEGER típusú.

11. A HiSoft Pascal előre definiált függvényei

A HiSoft Pascal több előre definiált függvénnyel és eljárással rendelkezik. Ezek az alprogramok minden programból elérhetőek anélkül, hogy erről külön intézkednénk. A Pascal programban a függvények hívását kifejezésekben adhatjuk meg, Az alábbiakban csoportosítjuk az elérhető függvényeket, melyek közül többet már bemutattunk a korábbi fejezetekben.

11.1. Konverziós függvények

CHR(X)
Visszatér az X ASCII-kódnak meglelő karakterrel. X egész típusú kifejezés, az eredmény karakter típusú. A függvény csak az X paraméter alsó byte-ját vizsgálja, így 255 többszörösére is kapunk eredményt. Pl.:

CHR(65) = 'A'
CHR(577) = 'A'

ENTIER(X)
Egészrész függvény. Megadja a legnagyobb egész számot, amely kisebb, vagy egyenlő mint az X paraméter. X egész (bár ez esetben a függvény alkalmazásának nincs értelme) vagy valós típusú kifejezés lehet. Az eredmény egész típusú. Pl:

ENTIER(11.7) = 11
ENTIER(-6.5) = -7

ORD(X)
A sorszámozott típusú paraméter sorszámát adja. X csak sorszámozott (jellemzően karakter) típusú kifejezés lehet. Pl:

ORD('a') = 97

ROUND(X)
Az X valós típusú kifejezést a legközelebbi egészre kerekít. X egész típusú kifejezés is lehet, bár ez esetben a függvény alkalmazása értelmetlen. Az eredmény minden esetben egész típusú. Pl:

ROUND(-6.5) = -6
ROUND(11.7) = 12
ROUND(-6.51) = -7
ROUND(23.5) = 24

TRUNC(X)
Valós típusú kifejezést a legközelebbi egészre kerekít a törtrész elhagyásával. X egész típusú kifejezés is lehet, bár ez esetben a függvény alkalmazása értelmetlen. Az eredmény minden esetben egész típusú. Pl:

TRUNC(1.1) = 1
TRUNC(1.9) = 1
TRUNC(-1.5) = -1

11.2. Matematikai függvények

ABS(X)
Az X paraméter abszolút értékét adja. X egész vagy valós típusú kifejezés lehet.

ARCTAN(X)
Megadja az X paraméter arkusz tangensét. Egy szám arkusz tangense az a szög, amelynek tangense a szám. A visszatérési szögértéket - mint minden szögföggvény esetében - radiánban kapjuk meg, értéke -PI/2 és PI/2 közötti lehet. X egész vagy valós típusú kifejezés lehet, a függvény értéke minen esetben valós típusú.
Az ARCTAN függvénnyel kiszámíthatjuk PI közelítő értékét:

PI:=4*ARCTAN(1)

COS(X)
Megadja az X paraméter koszinuszát. A visszatérési szögértéket radiánban kapjuk meg. X egész vagy valós típusú kifejezés lehet, a függvény értéke minen esetben valós típusú.

EXP(X)
Megadja az X paraméter exponenciális értékét, azaz az e szám hatványát (e értéke körülbelül 2,718281828). X egész vagy valós típusú kifejezés lehet, a függvény értéke minen esetben valós típusú.

FRAC(X)
Megadja az X valós típusú kifejezés törtrészét. A törtészt a következő kifejezés szerint határozza meg: X=X-ENTIER(X).
X egész típusú kifejezés is lehet, bár ez esetben a függvény alkalmazásának nincs értelme. Az eredmény valós típusú. Pl:

FRAC(1.5) = 0.5
FRAC(-12.56) = 0.44

LN(X)
Megadja az X paraméter természetes alapú logaritmusát. X egész vagy valós típusú kifejezés lehet, a függvény értéke minen esetben valós típusú.

RANDOM
Valós típusú véletlen számot generál. A függvénynek nincs paramétere, a generált szám a nulla vagy nullánál nagyobb, de egynél kisebb számtartományba eshet. Természetesen 1 és N közé eső egész (INTEGER típusú) véletlen számot is generálhatunk:

TRUNC(RANDOM*N)+1

A véletlenszám generátor minden programfuttatás előtt automatikusan inicializálódik. Ha mégis manuálisan szeretnénk fix értékekkel inicializálni (pl. a programtesztelés során, hogy mindig ugyanazokat a "véletlenszámokat" kapjuk), ezt a RANSEED eljárással tetetjük meg. Ennek formája:

RANSEED(X,Y,Z)

X, Y és Z integer típusú érték, melyeket a 05B1h, 05AAh és 05A3h címekre tölti fel (ezt a területet használja a RANDOM utasítás).

SIN(X)
Megadja az X paraméter szinuszát. A visszatérési szögértéket radiánban kapjuk meg. X egész vagy valós típusú kifejezés lehet, a függvény értéke minen esetben valós típusú.

SQR(X)
Az X egész vagy valós típusú kifejezés négyzetét adja. Az eredmény azonos típusú a függvény paraméterével.

SQRT(X)
Az X valós vagy egész típusú kifejezés négyzetgyökét adja meg. Az eredmény minden esetben valós típus.

TAN(X)
Megadja az X paraméter tangensét. A visszatérési szögértéket radiánban kapjuk meg. X egész vagy valós típusú kifejezés lehet, a függvény értéke minen esetben valós típusú.

11.3. Sorszámozott típusra használható egyéb függvények

ICNH
Az eljárásnak nincs paramétere. Megvizsgálja, van-e leütött billentyű. Ha van, visszaadja annak kódját, egyéb esetben CHR(0) értéket ad. A visszaadott érték karakter típusú. Pl.:

FUNCTION KEY:CHAR;
VAR CH:CHAR;
BEGIN
  REPEAT
    CH:=INCH;
  UNTIL CH<>CHR(0);
  KEY:=CH
END;

ODD(X)
Megvizsgálja, hogy a bemeneti paraméter páratlan szám-e. Ha a szám páratlan, a függvény igaz (TRUE) értéket, ha a szám páros, hamis (FALSE) értéked ad vissza. X csak egész típusú lehet. Pl:

ODD(2) = FALSE
ODD(3) = TRUE

PRED(X)
A bemeneti paraméter típusának értékkészletéből megkapjuk a bemenő paraméter értéke előtti (eggyel kisebb sorszámú) értéket. X csak sorszámozott típusú lehet. Pl.:

SUCC('B') = 'A'

SUCC(X)
A bemeneti paraméter típusának értékkészletéből megkapjuk a bemenő paraméter értéke után következő (eggyel nagyobb sorszámú) értéket. X csak sorszámozott típusú lehet. Pl.:

SUCC('A') = 'B'

11.4. Egyéb függvények

ADDR(X)
A megadott változó memóriában elfoglalt címét adja meg. A visszaadott érték INTEGER típusú.

EOLN
Paraméter nélküli függvény, amely logikai értéket ad vissza. Értéke TRUE, ha az éppen beolvasott karakter után sor vége jelet (CHR(13)) érzékel, egyéb esetben FALSE.

EXOS(X)
Végrehajtja az X számú EXOS funkcióhívást, majd visszaadja az állapotregiszter értékét. A bemeneti paraméter (a funkcióhívás száma) INTEGER típusú kifejezés, a visszaadott érték karakter típusú.

INP(X)
A Z80-as porton lévő értéket adja meg. A visszaadott érték karakter típusú, a bemeneti paraméter INTEGER típusú.

MAKESTR(X)
A függvény a 03F4h címtől a memóriába másolja a megadott X szövegfüzért, a generált hosszbájttal együtt. (Az első bájt lesz a hosszbájt). A függvény visszatérési értéke mindig 1012 lesz (03F4H), azaz a szövegfüzér kezdőcíme. (ld. 13.2.)

PEEK(X,típus)
A PEEK függvénnyel a memória tetszőleges X címének tartalmát olvashatjuk ki a megadott memóriatípusban.

SIZE(X)
A SIZE függvény megadja, hogy a paraméterként megadott változó - típusának függvényében - hány byte tárhelyet foglal. A visszaadott érték INEGER típusú.

11.5. Külső függvénygyűjtemény
A TOOLS.HPU rutingyűjtemény (ezek használatát ld. B. fügelék, F direktíva) néhány további függvényt tartalmaz.

MAX(X,Y)
A két INTEGER típusú érték közül a nagyobbikat adja függvényértékként. Pl. négy szám közül a legnagyobbat kiírhatjuk így is: Writeln(Max(a,Max(b,Max(c,d))));

MIN(X,Y)
A két INTEGER típusú érték közül a kisebbiket adja függvényértékként.

RND(X)
A függvény egy 0 és X közötti egész véletlenszámot ad. A visszaadott érték 0 lehet, de X-et nem éri el. A bemenő paraméter és a visszaadott érték INTEGER típusú.

UPCASE(CH)
A CH kisbetű karaktert nagybetűvé alakítja. Ha a karakter nincs az 'a'..'z' intervallumban, akkor a függvény változatban formában adja vissza a paraméter értékét. A bemenő paraméter és a visszaadott érték CHAR típusú.

LCASE(CH)
A CH nagybetű karaktert kisbetűvé alakítja. Ha a karakter nincs az 'A'..Z' intervallumban, akkor a függvény változatban formában adja vissza a paraméter értékét. A bemenő paraméter és a visszaadott érték CHAR típusú.

SWP(X,Y)
Az eljárás felcseréli a két INTEGER típusú változó tartalmát.

12. Sztringek a HiSoft Pascal-ban

A HiSoft Pascal a standard Pascal-hoz hasonlóan nem ismeri a string típust. Megoldást a karaktertömbök használata jelent, mely azonban korántsem teljeskörű megoldás. Karakterfüzér tárolására (is) alkalmas karaktertömböt pl. a következőképpen hozhatunk létre:

VAR STR38:ARRAY[1..38] OF CHAR;

Célszerűbb azonban típusneveket létrehozni:

TYPE STRING=ARRAY[1..38] OF CHAR;

Természetesen többdimenziós tömböket is létrehozhatunk. Egy kétdimenziós karaktertömb egydimenzios string-tömbnek felel meg.

VAR NAME:ARRAY[1..MAX] OF STRING;

Miben különbözik az így létrehozott változó a Turbo Pascal string-típusú változóitól? Míg a "rendes" string-változók nulladik tömbeleme tartalmazza a string hosszát, addig itt a szövegfüzér hosszának nyilvántartása nem történik meg. Ennek az az ára, hogy a hagyományos stringkezelő műveletek nem működnek, így a HiSoft Pascal ilyeneket nem is tartalmaz, igény szerint nekünk kell ezeket elkészíteni.
Az értékadás is nehézségekbe ütközik: Egy 16 elemű (azaz 16 karakter tárolására alkalmas) karaktertömbbe csak 16 karakterből álló szövegfüzért lehet értékadó utasítással betölteni, ellenkező esetben típuseltérés-hibát kapunk. Ha a változóba tölteni kívánt szövegfüzér ennél rövidebb, gondoskodnunk kell annak hosszának feltöltéséről pl. CHR(0) karakterekkel. Esetleg, ha tudjuk előre a használni kívánt szövegfüzérek hosszát (ami azért egészen speciális eset), annyiféle karaktertömböt kell tipizálnunk, ahány különböző hosszúságút akarunk használni. Példa:

VAR NAME:ARRAY[1..13] OF CHAR;

deklaráció esetén helyes a következő értékadás:

NAME:=('HiSoft Pascal');

de helytelen a következő:

NAME:=('Pascal');

A billentyűzetről beolvasás egyszerűbben megoldható. A READLN utasítás karakterfüzérek beolvasására alkalmas a következő formában:

READLN;READ(NAME);

Ha a megadott karakterfüzér rövidebb, mint a változó típusa, a fel nem használt karakterek CHR(0) kóddal kerülnek feltöltésre. Ha hosszabbat adunk meg, a szövegfüzér csonkul, a vége elvész.
Karaktertömbökön csak a relációs műveletek működnek. Az összehasonlítás a következőképpen történik: Először mindkét karakterláncból az első karakterpár kerül összehasonlításra. Az a karakterlánc a nagyobb, melyben az első karakter nagyobb (két karakter közül az a nagyobb, melynek ASCII kódja nagyobb). Ha ezek egyenlőek, akkor a második karakterek összehasonlítása következik. Amelyikben ezen a helyen nagyobb karakter áll, az a nagyobb karakterlánc. Ha ezek is egyformák, akkor az összehasonlítás tovább folytatódik. Ha az egyik karakterlánc közben véget ér, akkor a hosszabb karakterlánc lesz a nagyobb. Ha a két karakterlánc egyforma hosszú, és a karaktereik is rendre egyenlőek, akkor a két karakterlánc egyenlő.
Az elmondottak szemléltetésére szolgál az alábbi példaprogram, mely 10 nevet rendez sorrendbe, fésűs rendezéssel:

PROGRAM NEVEK;
CONST MAX=10;
TYPE STRING=ARRAY[1..20] OF CHAR;
VAR NAME:ARRAY[1..MAX] OF STRING;
PROCEDURE KI;
VAR I:INTEGER;
BEGIN
  WRITELN;
  FOR I:=1 TO MAX DO
    WRITELN(NAME[I])
END;
PROCEDURE BE;
VAR I:INTEGER;
BEGIN
  FOR I:=1 TO MAX DO BEGIN
    WRITE(I:2,'. nev: ');READLN;READ(NAME[I])
  END
END;
PROCEDURE RENDEZ;
VAR GAP,I:INTEGER;
    SW:BOOLEAN;
    T:STRING;
BEGIN
  GAP:=MAX;SW:=TRUE;
  WHILE (GAP>1) OR SW DO BEGIN
    GAP:=TRUNC(GAP/1.3);
    IF GAP<1 THEN GAP:=1;
    SW:=FALSE;
    FOR I:=1 TO MAX-GAP DO
      IF NAME[I]>NAME[I+GAP] THEN BEGIN
        T:=NAME[I];NAME[I]:=NAME[I+GAP];NAME[I+GAP]:=T;
        SW:=TRUE
      END
  END
END;
BEGIN
  WRITE(CHR(26));
  BE;
  RENDEZ;
  KI
END.

12.1. Sztringkezelő függvények megvalósítása
A Pascal nyelvben tehát egy adott karakterlánc mindig azonos számú karaktert tárol, ezt a típusa definiálásakor az indexhatár megadásakor rögzítjük. Ha mégis olyan stringet akarunk használni, amelynek hossza változik a programfutása során, akkor erre ajánlható például a következő rekord típus:

CONST MAXLGTH=78;
TYPE STR=RECORD
        LGTH:INTEGER;
        TEXT:ARRAY[1..MAXLGTH] OF CHAR;
END;

A MAXLGTH konstans a használt stringek maximális hosszát adja meg. A rekord LGTH mezőjében tároljuk, hogy pillanatnyilag a szöveg mező első hány eleme tárolja a tényleges szöveget.
Egy ilyen típusú NEV változó kényelmesen kiírható a már ismert módon:

WRITELN(NEV.TEXT);

A változó értékének beolvasásakor gondoskodnunk kell a beolvasott szövegfüzér hosszának tárolásáról:

PROCEDURE READSTR(VAR ST:STR);
VAR I:INTEGER;
BEGIN
  READLN;READ(ST.TEXT);
  IF (ST.TEXT[MAXLGTH]<>CHR(0)) THEN ST.LGTH:=MAXLGTH
  ELSE BEGIN
    I:=0;
    REPEAT
      I:=I+1
    UNTIL ST.TEXT[I]=CHR(0);
    ST.LGTH:=I-1
  END
END;

Ha a NEV nevű, STR típusú változóba szeretnénk beolvasni szövegfüzért, az eljárást egyszerűen így hívhatjuk meg:

READSTR(NEV);

Két azonos, STR típusú változó közötti értékadás így már egyszerű:

NEV2:=NEV;

A string-kezelő eljárások és függvényeket is elkészíthetjük, ezek közül a legegyszerűbb a változóban tárolt szövegfüzér hosszának lekérdezése:

FUNCTION LENGTH(ST:STR):INTEGER;
BEGIN
  LENGTH:=ST.LGTH
END;

Egy STR típusú változó értékét az alább eljárással törölhetjük:

PROCEDURE ERASESTR(VAR ST:STR);
VAR I:INTEGER;
BEGIN
  FOR I:=1 TO MAXLGTH DO
    ST.TEXT[I]:=CHR(0);
  ST.LGTH:=0
END;

Két STR típusú változó tartalmát összefűzhetjük a CONCAT eljárással, az eredmény az első paraméterként megadott változóban képződik.

PROCEDURE CONCAT(VAR ST1,ST2:STR);
VAR I:INTEGER;
BEGIN
  I:=1;
  WHILE (ST1.LGTH<MAXLGTH) AND (I<=ST2.LGTH) DO BEGIN
    ST1.TEXT[ST1.LGTH+1]:=ST2.TEXT[I];
    ST1.LGTH:=ST1.LGTH+1;I:=I+1
  END
END;

A COPYSTR eljárással egy string-változó megadott részletét egy másik string-változóba másolhatjuk. Alakja: COPYSTR(VÁLTOZÓ1,n1,n2,VÁLTOZÓ2)
A másolást az n1. karaktertől, n2. karakterig végzi, az eredmény a VÁLTOZÓ2-ben jön létre, annak korábbi tartalma felülíródik.

PROCEDURE COPYSTR(ST1:STR;POS1,POS2:INTEGER;VAR ST2:STR);
VAR I:INTEGER;
BEGIN
  ERASESTR(ST2);
  IF POS1<1 THEN POS1:=1;
  IF POS2>ST1.LGTH THEN POS2:=ST1.LGTH;
  FOR I:=POS1 TO POS2 DO
    ST2.TEXT[I]:=ST1.TEXT[I];
  ST2.LGTH:=POS2-POS1+1;
  IF ST2.LGTH<0 THEN ST2.LGTH:=0
END;

A DELSTR eljárással egy string-változó tartalmából törölhetjük a megadott részletet. Alakja: DELSTR(VÁLTOZÓ,n1,n2). A törlést az n1 karaktertől n2 karakterig végzi. A COPYSTR és DELSTR eljárás "bolondbiztos", azaz ellenőrzi a megadott paramétereket.

PROCEDURE DELSTR(VAR ST:STR;POS1,POS2:INTEGER);
VAR I,J:INTEGER;
BEGIN
  IF POS1<1 THEN POS1:=1;
  IF POS2>ST.LGTH THEN POS2:=ST.LGTH;
  IF POS2>=POS1 THEN BEGIN
    J:=0;
    FOR I:=POS2+1 TO ST.LGTH DO BEGIN
      ST.TEXT[POS1+J]:=ST.TEXT[I];J:=J+1
    END;
    FOR I:=ST.LGTH-(POS2-POS1) TO ST.LGTH DO
      ST.TEXT[I]:=CHR(0);
    ST.LGTH:=ST.LGTH-(POS2-POS1)-1
  END
END;

A DELSTR eljárás felhasználásával könnyen elkészíthetjük az LTRIM eljárást is, amely egy szövegfüzér elején álló szóközöket törli.

PROCEDURE LTRIM(VAR ST:STR);
VAR I:INTEGER;
BEGIN
  I:=1;
  WHILE (I<ST.LGTH) AND (ST.TEXT[I]=' ') DO
    I:=I+1;
  IF I>1 THEN DELSTR(ST,1,I-1)
END;

Az RTRIM eljárás - mely a szövegfüzér végén lévő szóközöket törli - hasonlóképen megoldható:

PROCEDURE RTRIM(VAR ST:STR);
VAR I:INTEGER;
BEGIN
  I:=ST.LGTH;
  WHILE (I>1) AND (ST.TEXT[I]=' ') DO
    I:=I-1;
  IF I<>ST.LGTH THEN DELSTR(ST,I+1,ST.LGTH)
END;

Az UCASE eljárás egy string-változót nagybetűsít (a kisbetűket nagybetűkre cseréli, minden más karaktert változatlanul hagy):

PROCEDURE UCASE(VAR ST:STR);
VAR I:INTEGER;
BEGIN
  FOR I:=1 TO ST.LGTH DO
    IF ST.TEXT[I] IN ['a'..'z'] THEN
       ST.TEXT[I]:=CHR(ORD(ST.TEXT[I])-32)
END;

Az LCASE eljárás egy string-változót kisbetűsít:

PROCEDURE LCASE(VAR ST:STR);
VAR I:INTEGER;
BEGIN
  FOR I:=1 TO ST.LGTH DO
    IF ST.TEXT[I] IN ['A'..'Z'] THEN
      ST.TEXT[I]:=CHR(ORD(ST.TEXT[I])+32)
END;

A POS föggvény az ST2 változóban tárolt szövegrészlet első előfordulásának pozícióját keresi meg az ST1 változóban tárolt szövegfüzérben. Ha a keresett szövegrészlet nem található, a függvény visszaadott értéke nulla.

FUNCTION POS(ST1,ST2:STR):INTEGER;
VAR I,J:INTEGER;
BEGIN
  I:=1;J:=1;
  REPEAT
    IF ST1.TEXT[I]=ST2.TEXT[J] THEN J:=J+1 ELSE J:=1;
    I:=I+1;
  UNTIL (I>ST1.LGTH) OR (J>ST2.LGTH);
  IF J=ST2.LGTH+1 THEN POS:=I-J+1 ELSE POS:=0
END;

Tetszőleges karaktersorozatot tartalmazó, de nem nulla hosszúságú (üres) karakterfüzért INTEGER típusú számmá alakíthatunk pl. a következő függvénnyel. Az egyszerűség miatt a legnagyobb konvertálható szám 32759.

FUNCTION VAL(ST:STR):INTEGER;
VAR I,SZ:INTEGER;
    N:BOOLEAN;
BEGIN
  I:=0;SZ:=0;N:=FALSE;
  REPEAT
    I:=I+1;
    IF ST.TEXT[I]='-' THEN N:=TRUE;
  UNTIL (ST.TEXT[I] IN ['0'..'9']) OR (I=ST.LGTH);
  IF ST.TEXT[I] IN ['0'..'9'] THEN BEGIN
    SZ:=ORD(ST.TEXT[I])-48;
    FOR I:=I+1 TO ST.LGTH DO
      IF (ST.TEXT[I] IN ['0'..'9']) AND (SZ<32760) THEN
        SZ:=SZ*10+(ORD(ST.TEXT[I])-48)
  END;
  IF N THEN VAL:=-SZ ELSE VAL:=SZ
END;

13. Kapcsolat az operációs rendszerrel

Ez Enterprise operációs rendszere (az EXOS) számos funkcióval segíti a programozó dolgát. Ezen funkciókat ún. funkcióhívásokkal érhetjük el. A HiSoft Pascal csak a legelemibb funkciót valósítja meg, a funkcióhívást, az összes többi szolgáltatást a programozónak kell elkészítenie.
Az operációs rendszer funkcióinak elérésének hatékony módja az escape-szekvenciák megfelelő csatornára írása. Problémába ütközünk azonban azon Spectrum-örökség miatt, hogy a CHR(16) kóddal a kimeneti perifériát váltogathatjuk a nyomtató és a képernyő között (lásd 8.1.5. pontban). Mivel escape-szekvenciákban bármely karakterkód szerepelhet, az escape-szekvenciák kiküldése előtt ki kell iktatni a CHR(16) kódot vizsgáló részt:

POKE(£0308,CHR(£18));

Ha ezután a nyomtatót is szeretnénk használni, az eredeti állapotot visszaállítani a

POKE(£0308,CHR(£20));

utasítással lehet.

13.1 Az EXOS függvény
A különböző EXOS-hívásokat egy 1 byte-os funkciókód határozza meg, ami közvetlenül az RST 30h utasítást követi. Az EXOS-hívások számára a paramétereket az A-, BC- és DE-regiszterekben adjuk át és ugyanezekben kapjuk vissza az eredményeket is. Az A-regiszter mindig állapotértéket hoz vissza, ami nulla, ha a hívás sikeres volt, és nem nulla, ha hiba vagy váratlan helyzet állt elő.
A HiSoft Pascal-ban a funkcióhívásokhoz az EXOS függvényt használhatjuk. A függvény meghívása előtt gondoskodnunk kell a Z80-as regiszterek megfelelő paraméterekkel történő feltöltéséről, amit a Pascal a regiszterek neveinek megfelelő előre definiált változókkal támogat:

Természetesen ezen változókba íráskor nem közvetlenül a regiszterekbe töltjük be az értékeket (elrontanánk a regiszterek értékeit), ezt az EXOS funkcióhívás fogja megtenni a megfelelő időben.
A függvény az A (állapotregiszter) értékét adja vissza karakter típusú kifejezésben. Értéke CHR(0) lesz, ha a funkcióhívás eredményes volt, egyéb esetben a hibakódot adja vissza. A bemeneti paraméter (a funkcióhívás száma) INTEGER típusú kifejezés. Pl. a 10.4. fejezetben bemutatott SETVAR eljárást így is meg lehet írni:

PROCEDURE SETVAR(VALTOZO,ERTEK:INTEGER);
VAR TMP:CHAR;
BEGIN
  POKE(£0308,CHR(£18));
  RB:=CHR(1);RC:=CHR(VALTOZO);
  RD:=CHR(ERTEK);
  TMP:=EXOS(16)
  POKE(£0308,CHR(£20));
END;

Csatorna megnyitására az 1-es EXOS funkcióhívás szolgál, melynek paraméterei: A = csatornaszám (nem lehet 255), DE = mutató a perifériára ill. file-név jelsorozatra. Ennek megfelelően:

POKE(£15AE,chr(hosszúság));POKE(£15AF,'eszköznév:');
RA:=CHR(csatornaszám);RDE:=£15AE;
TMP:=EXOS(1)

A 'hosszúság' az eszköznév karaktereinek száma (a kettőspontot is beleszámítva). Ha pl. a SOUND: eszköznek meg szeretnénk nyitni a 103-as csatornát:

POKE(£15AE,CHR(6));POKE(£15AF,'SOUND:');
RA:=CHR(103);RDE:=£15AE;
TMP:=EXOS(1)

A csatorna lezárására a 3-as EXOS funkcióhívás szolgál:

RA:=CHR(csatornaszám);
TMP:=EXOS(3)

Mivel RA, RB, RC, RD, RE char típusú változók, például ha az RA:=CHR(105) helyett RA:='i'-t írunk (a kis i betű ASCII kódja 105), akkor a fordító 7 órajellel gyorsabb kódot állít elő. Ennek ellenére ez a megoldás nem javasolt, mert a forráskód olvashatóságának és karbantarthatóságának rovásával jár.

A hibakezelést az operációs rendszer támogatja. Erre különösen I/O műveletek esetén van szükség, mivel a TIN és TOUT eljárások nem az EXOS-t használják (Spectrum örökségek), így nem is hibabiztosak. A funkcióhívás után az EXOS függvény és RA változó értéke az esetleges hibakód (ha nem történt hiba természetesen ez az érték nulla), feladatunk nagyon egyszerű: Minden funkcióhívás után ellenőrizni kell az RA-ban visszakapott értéket, ha ez nem nulla, a hiba típusának megfelelően kell folytatni a programvégrehajtást.
Legkézenfekvőbb hibakezelés a hibakódhoz tartozó üzenet megjelenítése. A hibakód értelmezésére a 28-as EXOS-funkcióhávás szolgál. Ennek paramétere RA-ban a hibakód (ami már eleve ott van), RDE-ben pedig a puffer címe, ahová az értelmező szöveget töltheti az EXOS. A kijelölendő 256 byte-os puffer azonban fejtörést okoz. Ismerjük ugyan a 03F4H-tól kezdődő 33 byte-os memóriaterületet, melyet a MAKESTR függvény használ, de ez nem elegendő hely. A 15AEH címtől kezdődő 381 byte-os terület használható puffernek, és TRANSLATE-tel lefordított programok esetében is működik. Tekintettel kell lennünk azonban arra, hogy előbb-utóbb valami más (pl. hibaüzenet, az editorból utolsónak beolvasott sor stb.) fölül fogja írni e puffer tartalmát. A pufferbe írt hibaüzenetet rögtön meg is jeleníthetjük pl. az alapértelmezett 121-es EDITOR csatornán az EXOS 8 (blokk írása) funkcióhívással. Ennek paraméterei: RA-ban a csatornaszám, RDE-ben a kiírandó szöveg kezdőcíme, RBC-ben a kiírandó byte-ok (karakterek) száma.

PROGRAM HIBA;
VAR TMP:CHAR;
    I:INTEGER;
BEGIN
  RA:=CHR(101);
  TMP:=EXOS(3); { megprobaljuk bezarni a 101-es csatornat }
  IF RA<>CHR(0) THEN BEGIN { hiba? }
    RDE:=£15AE;
    TMP:=EXOS(28);
    RA:=CHR(121);
    RDE:=£15AF;
    RBC:=ORD(PEEK(#15AE,CHAR));
    TMP:=EXOS(8);
    WRITELN('.') { az uzenet vegere kiirjuk a pontot is }
  END;
  WRITELN('Program vege.')
END.

A 14.1. fejezetben további példát láthatunk az EXOS-hívások használatára.

13.2. A MAKESTR függvény
A szövegfüzérben átadandó paraméterek (pl. eszköznév, file-név) megadását könnyíteti meg az eredeti ismertetőben nem dokumentált MAKESTR függvény. A függvény a 03F4h címtől a memóriába másolja a megadott szövegfüzért, a generált hosszbájttal együtt. (Az első bájt lesz a hosszbájt). A függvény visszatérési értéke mindig 1012 lesz (03F4H), azaz a szövegfüzér kezdőcíme.
A szövegfüzért csak az első szóközig (CHR(32)), vagy CHR(13) karakterkódig másolja be, tehát a 'Hello world!' szövegfüzérnek csak az első 5 karakterét másolja be. Ez tulajdonképpen nem nevezhető hibának a felhasználás tervezett módját figyelembe véve (file-név, eszköznév nem tartalmazhat szóközt).
Nagyobb hiányosság (vélhetően ezért nem is lett dokumentálva a függvény), hogy a szövegfüzér számára biztosított tárhely 33 bájt, azaz max. 32 bájt hosszú lehet a string (hosszbájttal együtt 33). A szöveg hosszát azonban nem ellenőrzi másolás előtt, így ha túl hosszú szövegfüzért adunk meg paraméternek, a szövegfüzér vége a futtatható kódot írja fölül! Ezért az eljárás használata különös figyelmet igényel!
Pl.:

RA:=CHR(1);
RDE:=MAKESTR('PROBA.TXT');
TMP:=EXOS(2);

A MAKESTR függvénnyel egyszerű lenne a rendszerparancsok kiadása is, ha nem csak az első szóközig dolgozná fel a szövegfüzéreket:

RDE:=MAKESTR('DIR');
TMP:=EXOS(26);

A probléma megoldásához a MAKESTR végrehajtó részét kell módosítanunk, úgy hogy space (CHR(32)) helyett a nul karakterig (CHR(0)) dolgozza fel a szövegfüzért. Ezt egyetlen byte (03E6h címen) ideiglenes módosításával megtehetjük:

POKE(£03E6,CHR(0));
RDE:=MAKESTR('DIR *.pas /w');
TMP:=EXOS(26);
POKE(£03E6,CHR(32));

A szövegfüzér létrehozása után vissza kell állítani az eredeti állapotot (a POKE(£03E6,CHR(32)); utasítással), mert ezt a rutint az {F } direktíva (ld. B függelék) is használja, és annak pedig szükséges, hogy a space benne legyen, mint elválasztó karakter.
A MAKESTR függvény használatával egyszerűsödik pl. a csatornanyitás. Az előző pontban megadott csatornanyitás írható így is:

RA:=CHR(103);RDE:=MAKESTR('SOUND:');
EXOS(1)

13.3. A használt csatornák
A Pacal alapértelmezés szerint a 120-as videócsatornát és a 121-es editor-csatornát használja.
A billentyűzetet a 105-ös csatornán figyeli, a nyomtatót a 123-as csatornán keresztül éri el.
A TIN és TOUT eljárások a 122-es csatornát nyitják meg a file-művelet elvégzésének idejére.

13.4. Operációs rendszer funkciók
A HiSoft Pascal külső rutingyűjteménnyel érheti el az Enterprise operációs rendszerének funkcióit. Erre szolgál az OS.HPU. Használatához közvetlenül a változódeklarációs rész után kell beilleszteni programunkba a következő utasítással:

{$F OS.HPU }

A használható eljárások:

SETVAR(változó,érték)
A 'változó' számú EXOS-változóba beírja a megadott értéket. Mindkét paraméter INTEGER típusú.

TOGGLE(változó)
A meagdott számú változó értékét átbillenti. A paraméter INTEGER típusú.

GETTIME(óra,perc,másodperc)
A három INTEGER típusú változóba betölti a rendszeridőt 24 órás alakban.

GETDATE(év,hónap,nap)
A három INTEGER típusú változóba betölti az aktuális rendszerdátumot. Az évet négy számjegyen adja.

SETTIME(óra,perc,másodperc)
A rendszeridő beállítása. Minden változó INTEGER típusú. Az órát 24 órás alakban kell megadni.

SETDATE(év,hónap,nap)
A rendszerdátum beállítása. Minden változó INTEGER típusú, az évet négy számjeggyel kell megadni.

CLRSCR
Törli a képernyőt. Az eljárásnak nincs paramétere.

GOTOXY(x,y)
Az eljárás az (x,y) koordinátájú karakterpozícióba állítja a kurzort. A képernyő bal felső sarkának koordinátája (1,1); a vízszintes pozíciók (X) balról jobbra, a függőlegesek (Y) pedig felülről lefelé növekednek. Ha valamelyik érték 0, akkor ugyanabban az oszlopban / sorban marad a kurzor. Az x és az y INTEGER típusú kifejezés.
Bár a Hisoft Pascal eljárásai között nincs TAB(X) (a kurzor tabulálását végző) eljárás, a GOTOXY eljárással pótolhatjuk. Ha ugyanis a sor vagy oszlop sorszámának nullát adunk meg, a kurzor az aktuális sorban / oszlopban marad:

PROGRAM PASCAL;
CONST SOR=12;
VAR C,I,K:INTEGER;
{$F OS.HPU }
BEGIN
  CLRSCR;
  FOR I:=0 TO SOR DO BEGIN
    GOTOXY(37-I*3,0);
    C:=1;
    FOR K:=0 TO I DO BEGIN
      WRITE(C:6);
      C:=C*(I-K) DIV (K+1)
    END;
    WRITELN
  END
END.

SYSINFO(b0,b1,b2,b3,b4,b5,b6,ver)
Rendszerállapot lekérdezése. A paraméterként megadott, karakter típusú változókba tölti a rendszer pillanatnyi állapotának paramétereit:

Példaprogram:

PROGRAM INFO;
VAR SEG0,SEG1,SEG2,SEG3,SEG4,SEG5,SEG6,SEG7,VER:CHAR;
{$F OS.HPU }
BEGIN
SYSINFO(SEG0,SEG1,SEG2,SEG3,SEG4,SEG5,SEG6,VER);
  WRITELN('Az osszes mukodo RAM szegmensek: ',ORD(SEG5):3);
  WRITELN('A szabad szegmensek szama:       ',ORD(SEG1):3);
  WRITELN('A megosztott szegmensek szama:   ',ORD(SEG0):3);
  WRITELN('A felhasznalonak kiutalt szegm.: ',ORD(SEG2):3);
  WRITELN('A periferiaknak kiutalt szegm.:  ',ORD(SEG3):3);
  WRITELN('A rendszer reszere kiutalt:      ',ORD(SEG4):3);
  WRITELN('EXOS verzioszam: ',ORD(VER))
END.

CLEARFONT
Alapállapotba állítja vissza a karakterkészletet. Az eljárásnak nincs paramétere.

A használható függvények:

BCD(NR)
Az INTEGER típusú BCD számot decimális alakba konvertálja. Az eredményt is INTEGER formában adja.

TOBCD(NR)
Az INTEGER típusú decimális számot BCD alakba konvertálja. Az eredményt is INTEGER formában adja.

ASK(N)
Az n számú rendszerváltozó állapotát adja vissza. N és az eredmény INTEGER típusú érték.

GET(CH)
A CH számú megnyitott csatornáról beolvas egy karaktert és visszaadja annak kódját INTEGER típusú értékként. CH INTEGER típusú kifejezés! Ha nincs a csatornában karakter, akkor -1-et ad vissza. A GET grafikus képernyőn is működik, de itt nem a szín kódját adja vissza, hanem a grafikus laphoz tartozó szín-sorszámot.

JOY(NR)
Az NR számú botkormány állapotát olvassa le. A visszaadott érték a botkormány helyzetétől függ:
1 - jobbra dől;
2 - balra dől;
4 - lefelé;
8 - felfelé;
16 - a tűzgomb benyomva.
Ha a botkormány átlósan áll vagy a tűzgomb is be van nyomva, akkor függvény az egyes értékek összegét adja. A beépített botkormány száma 0, ezenkívül az 1 (EXT1) és a 2 (EXT2) használható. A beépített botkormánynál a szóközbillentyű a tűzgomb. A bemenő paraméter és a visszaadott érték is INEGER típusú.

KEYPRESSED
A függvény TRUE értéked ad vissza, ha a billentyűpufferben van lenyomott billentyű. Ellenkező esetben FALSE értéked ad. A függvénynek nincs paramétere. A puffer tartalma változatlan marad, a függvény onnan nem vesz ki karaktert. A billentyűlenyomással előállított karaktert pl. a READKEY függvény segítségével olvashatjuk ki.

READKEY
A függvény karaktert olvas a billentyűzetről. Ha a billentyűpuffer üres (nincs lenyomott billentyű), a függvény várakozik egy billentyű lenyomásáig. Az eljárásnak nincs paramétere, a visszaadott paraméter CHAR típusú.

A WHEREXY.HPU az operációs rendszer három olyan funkcióját valósítja meg, mely az IS-BASIC-ben nincs implementálva, speciális esetekben mégis hasznosak lehetnek. A bővítmény csak az OS.HPU-val együtt használható, előbb azt kell beilleszteni a programba.

CURXY(CH,X,Y)
Az eljárással lekérdezhetjük a kurzor pillanatnyi pozícióját tetszőleges szöveges videólapon. A csatornaszámot a CH-ban kell megadni, az eljárás X és Y változókba tölti a kurzor vízszintes és függőleges pozícióját. Valamennyi paraméter INTEGER típusú.

GRXY(CH,X,Y)
Az eljárással lekérdezhetjük a kurzor pillanatnyi pozícióját tetszőleges grafikus videólapon. Az eljárás paraméterei azonosak mint a CURXY esetében.

MAXXY(CH,MAXX,MAXY)
Az eljárás a MAXX és MAXY változókba tölti a CH videólap utolsó oszlopának illetve sorának számát. A visszaadott értékek természetesen a videólap méretétől függenek, és az eljárás tetszőleges típusú szöveges vagy grafikus videólapon működik. Valamennyi paraméter INTEGER típusú.

A funkcióbillentyűk programozására nincs definiálva eljárás, ezt egyszerűbb megvalósítani az alábbi programrészlettel (a példa az 1.2-es verzióval működik):

RA:=Chr(105); {billentyuzet csatorna szama}
RB:=Chr(8);   {specialis funkcio szama}
RC:=Chr(0);   {funkciobillenyu szama: 0-15}
Poke(£03E6,CHR(0));
RDE:=Makestr('START');
Poke(£03E6,CHR(32));
Exos(11);

A két POKE eljárás csak akkor kell, ha a szövegfüzér tartalmaz szóközt.
A program befejeztekor a keretprogram visszadefiniálja a funkcióbillentyűket.

13.5. Grafika a HiSoft Pascal-ban
A HiSoft Pascal nem tartalmaz semmilyen grafikus utasítást sem, és - szemben a Spectrum verzióval - külső rutingyűjtemény sem készült hivatalosan az Enterprise-változathoz. Ezt a hiányosságot pótolta PoviSoft grafikus-rutingyűjteménye, amely tartalmazza az összes EXOS által támogatott grafikus funkciót (és néhány további, összetettebb funkciót is).
A rutingyűjteményt az F direktívával (lásd a B függeléket), illeszthetjük a programunkba, mivel azonban használja az előző pontban ismertetett SETVAR eljárást, az OS.HPU rutingyűjteményt is be kell tölteni előtte:

{$F OS.HPU }
{$F GRAFCS.HPU }

Abban az esetben, ha egyébként más eljárást nem használnánk az OS.HPU-ból a programunkban, készült egy GRAFCSX.HPU változat is, amely csak abban különbözik, hogy tartalmazza a SETVAR eljárást is. Ha tehát a GRAFCSX.HPU-t használjuk, nem kell (és nem is lehet) az OS.HPU-t betölteni.
A rutingyűjtemény a következő eljárásokat használja (az eljárásokban szereplő valamennyi paraméter INTEGER típusú, így ezt a továbbiakban nem említjük meg!):

GRAFON(CH,MOD,COLOR,XSIZE,YSIZE)
Megnyitja CH csatornán a MODE videó módú, COLOR színüzemmódú, XSIZE és YSIZE méretű videólapot.

GRAFOFF(CH)
Lezárja a korábban megnyitott CH csatornát (CH csatornaszámú videólapot). Nem csak grafikus, de bármely csatorna lezárására is használható! A program befejezése előtt zárjuk le a megnyitott videólapokat!

DISPLAY(CH,AT,FROM,TO)
Megjeleníti a már megnyitott CH csatornaszámú videólapot a képernyő AT sorától kezdve. A videólap a FROM-adik sorától TO számú sort jelenít meg.

GOXY(CH,X,Y)
A ch csatornaszámú videólapon a szövegkurzort az x. sor y. oszlopába állítja. A grafikus sugármutató pozíciója változatlan marad.

CLEAR(ch)
Törli a ch csatornaszámú videólap tartalmát.

MODE40
A HiSoft Pascal által használt alapértelmezett (120-as számú) szöveges videó lapot 40 karakteres üzemmódban nyitja meg.

MODE80
A HiSoft Pascal által használt alapértelmezett (120-as számú) szöveges videó lapot 80 karakteres üzemmódban nyitja meg.

PLOT(CH,X,Y)
A CH csatornaszámú grafikus videólap X, Y pozíciójába mozgatja a rajzolósugarat. Ha a rajzolósugarat bekapcsolt állapotban hagyjuk, vonalat húzunk az előző rajzolósugár pozícióból.

PLOTREL(CH,X,Y)
A CH csatornaszámú grafikus videólapon az aktuális pozícióból az X és Y távolságba mozgatja a rajzolósugarat. Azaz x és y relatív koordináták, így negatív értéket is felvehetnek. Ha a rajzolósugarat bekapcsolt állapotban hagyjuk, vonalat húzunk az előző rajzolósugár pozícióból.

BEAMON(CH)
Bekapcsolja a rajzolósugarat a CH csatornaszámú videólapon.

BEAMOFF(CH)
Kikapcsolja a rajzolósugarat a CH csatornaszámú videólapon.

MOVETO(CH,X,Y)
A rajzolósugarat az X,Y pozícióba mozgatja, anélkül, hogy vonalat húzna.

MOVEREL(CH,X,Y)
Az aktuális rajzolósugár-pozíció áthelyezése relatív koordinátákkal megadott pontba: A CH csatornaszámú grafikus videólapon az aktuális pozícióból az X és Y távolságba mozgatja a rajzolósugarat, anélkül, hogy vonalat húzna.

DRAW(CH,X1,Y1,X2,Y2)
Vonalat húz X1,Y1 és X2,Y2 koordinátájú pontok között, a CH csatornaszámú videólapon.

BOX(CH;X1,Y1,X2,Y2)
Négyszöget rajzol a CH csatornaszámú videólapon. A négyszög két átellenes sarka az X1,Y1 és X2,Y2 koordinátájú pont.

ELLIPSE(CH,X,Y)
Ellipszist rajzol a CH csatornaszámú videólapon, melynek középpontja az aktuális rajzolósugár-pozíció. X az ellipszis vízszintes sugara, Y a függőleges sugara, Ha X és Y egyenlő, kört kapunk. Ha nem akarjuk, hogy az ellipszis közepén levő pont is megjelenjen a képernyőn, akkor a középpont kijelölése előtt kapcsoljuk ki a rajzolósugarat.

PALETTE(CH,C0,C1,C2,C3,C4,C5,C6,C7)
A CH csatornaszámú videólapra beállítja a színpalettát. 2 és 4 színes üzemmódban és meg kell adni C0-C7 értékeket is.

INK(CH,COLOR)
A tinta színét állítja be a CH csatornaszámú videólapon a színpaletta COLOR sorszámú színére.

PAPER(CH,COLOR)
A papír színét állítja be a CH csatornaszámú videólapon a színpaletta COLOR sorszámú színére.

LINESTYLE(CH,MODE)
Szaggatott vonalak rajzolását teszi lehetővé a MODE paraméter (1-14) értékélől függően a CH csatornaszámú videólapon.

LINEMODE(CH,MODE)
A régi és az új képernyősorok színeinek egymásra hatását adja meg a CH csatornaszámú videólapon. Nullás módban (alapértelmezés) az új sorok színei felülírják a régieket, a többi módban viszont a kettő kombinációja alakul ki a következők szerint:
mód 1 - vagy (OR)
mód 2 - és (AND)
mód 3 - kizáró vagy (XOR).

FILL(CH)
Zárt alakzat kitöltése az aktuális tintaszínnel, a CH csatornaszámú videólap aktuális sugárpozíciójából kiindulva.

CHARACTER(N,R1,R2,R3,R4,R5,R6,R7,R8,R9)
Az N kódú (ASCII) karakter alakját határozza meg. Minden további paraméter az alak egy-egy sorát adja, felülről lefelé haladva.

VSYNCWAIT(N)
A NICK órajelétől függő várakozást tesz lehetővé. Mivel ez független a processzor órajelétől, a turbós / nem turbós gépeken is azonos ideig függeszti fel a program végrehajtását. Ha a hívások között futó kód vagy az EXOS megszakítás kezelése lassú, akkor elveszhetnek megszakítások, ilyenkor a CPU sebességétől is függhet kis mértékben az időzítés. N 8 bites paraméter, 0 a 256-nak (a leghosszabb várakozásnak) felel meg.

Az ATTRIBUTES videófeltételhez nem készült eljárás, mert ez a gyakorlatban mindössze három byte kiírását jelenti az adott videócsatornára: Chr(27),'a',Chr(x)
Ahol x a beállítani kívánt érték.

Példaprogram a Felhasználói Kézikönyv bemutató programja alapján:

PROGRAM CSATORNA;
VAR X,Y,A,A1,C,C1,R:INTEGER;
{$F GRAFCSX.HPU }
PROCEDURE TUZLABDA(SZIN,X1,Y1:INTEGER);
VAR JEL,SUGAR:INTEGER;
BEGIN
  LINEMODE(101,3);
  INK(101,SZIN);BEAMON(101);
  FOR JEL:=1 TO 2 DO
    FOR SUGAR:=1 TO 21 DO BEGIN
      PLOT(101,X1,Y1);ELLIPSE(101,SUGAR*30,SUGAR*30)
    END;
  LINEMODE(101,0)
END;
BEGIN
  X:=640;Y:=360;
  GRAFON(101,1,3,40,20);
  DISPLAY(101,1,1,20);
  FOR R:=1 TO 255 DO BEGIN
    INK(101,R);
    A:=X-R-220;A1:=Y-R-50;
    C:=X+R+220;C1:=Y+R+50;
    BOX(101,A,A1,C,C1)
  END;
  MOVETO(101,X,Y);
  REPEAT
    TUZLABDA(TRUNC(RANDOM*254+1),X,Y);
  UNTIL INCH<>CHR(0);
  GRAFOFF(101);USER(£01EC)
END.

A példaprogramhoz még egy magyarázatot kell hozzáfőzni: A program befejezte előtt újra meg kell jeleníteni az alapértelmezett szöveges képernyőt. Ezt végzi el az utolsó előtti sorban az USER(£01EC) rutinhívás.

A GRAPH16.HPU az EXOS-tól független grafikai rutinok gyűjteménye (az 1.2-es Pascal verzióhoz készült, ld. később). A műveletek végrehajtása gyorsabb, mint az EXOS rajzoló rutinjai (kivéve a Fill eljárást), viszont csak egyetlen 16 színüzemmódú videolapot kezel. A rutingyűjtemény nem EXOS-csatornát használ, hanem lefoglal egy szegmenst és belapozza a 3. lapra. Tehát a Pascal program is közvetlenül hozzáférhet a video adathoz C000H kezdőcímtől. A képernyő mérete 40x22 karakter (160x198 pixel), ez a legnagyobb, ami egy szegmensen elfér. Valójában lehetne 204 soros is, de az nem egész számú karakter. A rutingyűjtemény támogatja az EnterMice egérkezelést és sprite-kezelést is. A gépi kódú rutinokat a GRAPH16.BIN file tartalmazza. A rutingyűjtemény használata előtt helyet kell biztosítani a gépikódú rutinoknak az Alter paranccsal az alábbiak szerint:

Symbol Table size: 164D (alapértelmezés)
Translate Stack: BA00
Compiler Stack: BA00

A használható eljárások (valamennyi paraméter integer típusú, az egyetlen kivétel jelezve van):

GrafInit
Betölti a graph16.bin file-t, azaz inicializálja a rutingyűjteményt.

GrafOn
Megnyitja a 40x22 karakter méretű, 16 színű a grafikus lapot.

GrafOff
Lezárja a megnyitott grafikus lapot.

Clear(i)
Letörli a videolapot az i színnel.

VSyncWait(n)
Azonos a GRAFCS.HPU-nél ismertetett azonos nevű eljárással.

Ink(i)
Beállítja a rajzoláshoz használt színt.

PlotC(i,x,y)
Képpontott rajzol az x,y koordinátába i színnel.

Plot(x,y)
Képpontot rajzol az x,y koordinátába az aktuális színnel.

Circle(i,x,y,r)
r sugarú kört rajzol i színnel. A kör középpontja x,y koordinátába esik.

Draw(i,x1,y1,x2,y2)
Vonalat húz x1,y1 és x2,y2 képpontok között i színnel.

Color(i,c)
A színpaletta i-edik színébe a c színt tölti. Nem frissíti automatikusan az LPT-t.

UpdateLPT
Csak a Color (akár többszöri) használata után van jelentősége, az LPT-t frissíti a módosított színekkel.

Palette(c0,c1,c2,c3,c4,c5,c6,c7)
Beállítja a színpalettát, az LPT-t is frissíti.

PaletteRL
A paletta színeit balra rotálja Az LPT-t is frissíti.

PaletteRR
A paletta színeit jobbra rotálja Az LPT-t is frissíti.
A színpalettát rotáló két paranccsal egyszerűen lehet megoldani olyan animációkat, melyek a paletta színeinek cserélgetésével működnek.

Fill(i,x,y)
A képernyő egy zárt területét x,y pozícióból kezdve kitölti i színnel.

Border(c)
Beállítja a keret színét.

Bias(c)
Beállítja a BIAS-t.

Rnd8Init
Inicializálja a rutingyűjtemény saját véletlenszám-generátorát. Ld. következő eljárás.

SpriteInit(p,s,n,c)
Sprite inicializálása.
p: konvertált sprite adat címe, a mérete N*6*21*4 byte,
s: (a SpriteInit után már nem használt) forrás adat címe, a mérete N*6*21 byte,
n: animáció fázisok száma,
c: átlátszó szín a forrás adatban (0..15),

SpriteNew(b,p)
Sprite számára inicializálja a 'b' paraméterben megadott kezdőcímű (ennek párosnak kell lennie), 130 byte méretű területet. Ugyanezt a paramétert használják a SpriteDraw és SpriteClr későbbi hívásai. A 'p' a SpriteInit rutinnal konvertált pixel adatra mutat, a mérete N*6*21*4 byte Több sprite is használhatja ugyanazt a grafikát, bár 4 MHz-es gépen csak 2-3 jeleníthető meg villogás nélkül.

SpriteClr(b)
Korábban megjelenített sprite törlése, visszaállítja a mentett hátteret.
b: sprite puffer (az aktuális állapot és a háttér mentése) címe, csak páros lehet, a mérete 130 byte.

IRQDisable
Az EXOS megszakítás kezelésének tiltása a 0038h cím felülírásával.

IRQEnable
Az EXOS megszakítás kezelésének engedélyezése a 0038h cím felülírásával.

ResetMouse(x,y,w)
Egér koordináták beállítása x,y pozícióba. A 'w' a görgetés pozíciója, ami valójában csak 8 bites.

DrawChar(i,x,y,c)
c karakter (c char típusú adat)rajzolása a megadott x,y pozícióban, az 'i' a paletta szín (0..15) vagy -1. Az előbbi esetben pixelenkénti Plot() történik a 2 színű karaktert vízszintesen nagyítva, a háttérszínű pixelek változatlanok maradnak. Ha a szín -1, akkor byte alapú a rajzolás, ami gyorsabb, és 16 színű (felhasználó által definiált) karakterkészletet tételez fel. Ez a mód csak akkor működik helyesen, ha az 'x' páros, és a teljes karakter látható (nem fordulnak elő érvénytelen koordináták).

A rutingyűjtemény függvényei:

Rnd8(l,m)
Egyszerű véletlenszám generátor, 8 bites értéket ad vissza a második paraméterrel végzett AND művelet után, illetve az elsőnél mindig kisebbet (pl. Rnd8(5,7) = 0 és 4 közötti érték).

GetPixel(x,y)
Pixel lekérdezése a megadott pozícióban, a visszatérési érték a szín (0..15), vagy -1 érvénytelen koordináták esetén.

SpriteDraw(b,n,x,y)
Sprite megjelenítése x,y pozícióban. A 'b' paraméter a puffer kezdőcíme, az 'n' pedig az animáció fázis 0 és a korábban megadott n-1 között. Érvénytelen koordináták esetén nem jelenik meg semmi. Az 'n'-hez 128-at adva engedélyezett a háttérrel való ütközés vizsgálata, ami lassabb, ilyenkor a visszatérési érték a háttér takarásban levő részei között végzett OR művelet eredménye, egyébként 0.

GetKey(n)
Billentyű lekérdezése, n = mátrix pozíció (sor*8+oszlop +128 ha külső joystick). 0-t ad vissza ha a billentyű nincs lenyomva, egyébként -1-et.

ReadMouse
EnterMice kompatibilis egér lekérdezése, 0 ha sikeres volt, -1 ha nincs egér. Frissíti az alábbi függvények számára a tárolt állapotot.

GetMouseX
A pointer aktuális X pozíciója. X előjeles 16 bites érték, 16 színű módban kétszeres felbontású.

GetMouseY
A pointer aktuális Y pozíciója.

GetMouseW
Görgetés pozíció, előjeles 8 bites érték 16 bitre kiegészítve.

MouseBtn(n)
Egér gomb (n = 0..3) aktuális állapota, -1 ha le van nyomva, egyébként 0. Az n-hez 4-et hozzáadva "élérzékeny" módon figyelhetők a gombok, ilyenkor a függvény csak a lenyomás eseményét jelzi.

Egyszerűbb példaprogram a rajzoló műveletekre:

PROGRAM Dither;
{$L- }
VAR x,y,z:integer;
{$F graph16.hpu }
BEGIN
  GrafInit;
  GrafOn;
  Rnd8Init;
  SetVar(26,1);
  Circle(4,80,99,98);
  Fill(1,80,99);
  Fill(5,10,10);
  for z:=0 to 65 do begin
    Draw(6,0,0,159,z*3);
    Draw(6,0,0,159-z*2,197)
  end;
  for z:=0 to 188 do
    Circle(z mod 7,80,99,z);
  for y:=0 to 197 do
    for x:=0 to 159 do
      PlotC(Rnd8(8,7),x,y);
  REPEAT
    VSyncWait(1);
  UNTIL Inch=Chr(27);
  SetVar(26,0);
  GrafOff;
  {User(£01EC)}
END.

Példaprogram az egérkezelésre:

PROGRAM Mouse;
{$L- }
VAR x,y,w,x0,y0,w0,c,z:integer;
{$F graph16.hpu }
BEGIN
  GrafInit;
  GrafOn;
  SetVar(26,1);
  IRQDisable;
  x:=80; y:=99; w:=20; c:=4;
  x0:=x; y0:=y; w0:=1;
  ResetMouse(x*2,y,w);
  repeat
    VSyncWait(1);
    z:=ReadMouse;
    x:=GetMouseX div 2; y:=GetMouseY;
    w:=GetMouseW;
    if w<1 then w:=1;
    if w>50 then w:=50;
    if MouseBtn(4)<>0 then ResetMouse(160,99,20);
    if (x<>x0) or (y<>y0) or (w<>w0) or (MouseBtn(5)<>0) then begin
      if MouseBtn(5)<>0 then
        c:=(c mod 7)+1;
      Circle(0,x0,y0,w0);
      Circle(c,x,y,w);
      x0:=x; y0:=y; w0:=w
    end;
  until GetKey(31)<>0;
  IRQEnable;
  SetVar(26,0);
  GrafOff;
  {User(£01EC)}
END.

13.5.1. Írás tetszőleges videócsatornára
A WRITE és WRITELN eljárás a HiSoft Pascal-ban az alapértelmezett 121-es EDITOR csatornára írnak. A WRITE, WRITELN és a GOTOXY (az 1.2-es verzióban a CLRSCR) eljárások hatáskörét azonban tetszőleges videólapra átállíthatjuk az alábbi utasítással:

POKE(£0328,CHR(CSAT));

Ettől kezdve a megadott csatornára írhatunk, amíg egy következő POKE(£0328,...) utasítással át nem állítjuk az alapértelmezett videólap számát. A program befejezése előtt azonban vissza kell állítani az alapértelmezett állapotot (121 az editor csatornaszáma):

POKE(£0328,CHR(121));

13.5.2. Teknőcgrafika
A TURTLE.HPU bővítmény a Logo-ból (és IS-BASIC-ből) ismert teknőcgrafikus utasításokat tartalmazza. A bővítmény használja a GRAFCS.HPU eljárásait, ezért azt is be kell illesztenünk a lefordítandó programba, még a TURTLE.HPU előtt. A bővítmény használja az integer típusú ANGLE (INEGER típusú) változót, amely azt tartalmazza, hogy a rajzolósugár merre nézzen, szögekben kifejezve. A 0 fok a képernyő jobb széle felé, a 90 fok felfelé, a 180 fok balra és a 270 fok lefelé mutat. A deklarációk kötött sorrendje miatt azonban a bővítmény nem tartalmazza az ANGLE változó deklarációját, azt nekünk kell beilleszteni a programba.
A bővítmény eljárásai:

FORWD(CH,D)
A CH csatornaszámú videólapon a rajzolósugár D pozíciót halad előre (amerre az ANGLE változó "mutat").

BACK(CH,D)
A ch csatornaszámú videólapon a rajzolósugár d pozíciót halad hátrafelé (ellenkező irányba, mint amerre az ANGLE változó "mutat").

RIGHT(A)
A rajzolósugár A fokkal jobbra fordul.

LEFT(A)
A rajzolósugár A fokkal balra fordul. A LEFT és RIGHT utasítás az adott szög többszörösét is elfogadja és értelmezi (pl. 10 fok helyett 370 fokot).

DEG(A)
A függvény megadja az A radián szög értékét fokban.

RAD(A)
A függvény megadja az A fok értékét radiánban.

Példaprogram:

PROGRAM FRACTALTREE;
VAR ANGLE:INTEGER;
{$F GRAFCSX.HPU }
{$F TURTLE.HPU }
PROCEDURE TREE(N:INTEGER);
BEGIN
  IF N>24 THEN BEGIN
    FORWD(1,N);RIGHT(25);
    TREE(TRUNC(N*0.75));
    LEFT(50);
    TREE(TRUNC(N*0.75));
    RIGHT(25);BACK(1,N)
  END
END;
BEGIN
  GRAFON(1,1,0,40,20);
  PALETTE(1,0,170,0,0,0,0,0,0);
  DISPLAY(1,1,1,20);
  PLOT(1,640,10);BEAMON(1);
  ANGLE:=90;
  TREE(200);
  REPEAT UNTIL INCH<>CHR(0);
  GRAFOFF(1);USER(£01EC)
END.

13.6. Hangkeltés
Az 1.2-es verzióhoz készült SOUND.HPU rutingyűjtemény felhasználásával az EXOS-ból elérhető hangkeltési lehetőségeket használhatjuk. Ez a megoldás az IS-BASIC-hez hasonló színvonalú hangprogramozást tesz lehetővé. A bővítmény az alábbi eljárásokat tartalmazza:

SOUNDINIT
Megnyitja a 103-as csatornát a SOUND eszköznek. A HiSoft Pascal ezt nem teszi meg automatikusan (mint a BASIC), ezért bárminemű hangkeltési próbálkozás előtt ez kell legyen az első teendőnk.

PING
A BASIC PING parancsával azonos ping-szerű hangjelzést ad ki.

SOUND (env,pitch,durat,left,right,style,source,flag)
Hangot ad ki a megadott paramétereknek megfelelően. Valamennyi paraméter integer típusú érték:

ENVELOPE(env,ep,er)
Hangburkoló definiálásának kezdetét jelzi. Valamennyi paraméter integer típusú érték:

ENVPHASE(cpitch,cleft,cright,durat)
A hangburkoló egy fázisát definiálja. Az ENVELOPE eljárást annyi ENVPHASE eljárásnak kell követnie, ahány fázisból összesen áll a burkoló (ep-ben megadott érték). Ha az er értéke nem 255, az utolsó er számú ENVPHASE eljárás a lecsengés egy-egy fázisát definiálja. Valamennyi paraméter integer típusú érték:

CLEARQUEUE
Megállítja valamennyi hangforrás működését, és kitöröli valamennyi várakozó SOUND kérést (azaz töröl, minden sorban várakozó hangot).

CLOSESOUND
Lezárja a korábban megnyitott 103-as csatornát. "Jólnevelt" program nem hagyja ki ezt a lépést a programfutás befejezése előtt.

A lefordított PASCAL program sokkal gyorsabban "eteti" a SOUND-eszközt, mint a BASIC, ezért problémát okozhat, hogy a sorban álló hangoknak fenntartott puffer betelik. Ilyenkor alapértelmezés szerint a SOUND eljárás várakozik addig, amíg a sorban felszabadul hely a berakni kívánt hangnak. Ez a PASCAL program drasztikus lassulását vonhatja maga után.
Ennek a helyzetnek feloldására szolgál a 13 EXOS változó: ha ennek értéke 1..255 között intervallumban van, nincs várakozás, helyette a SOUND eszköz .SQFUL hibát ad vissza ra-ban. Ezt a hibát lekezelendő újra azt a hangot kell a sorba raknunk, amit az előző próbálkozásnál nem sikerült.

14. Az 1.2-es verzió

Bár a Hisoft Pascal utolsó hivatalos 1.1-es változata is igen hatékony eszköz, de PoviSoft által készített 1.2-es változata nagyon hasznos módosításokat és javításokat tartalmaz:

14.1. Példaprogram
Az Exos-hívások használatára, és az operációs rendszeren keresztül megvalósítható file-kezelésre lássunk egy egyszerűbb példát: listázzuk ki egy tetszőleges szöveges file tartalmát. A file kiválasztására a FILE-rendszerbővítőt használjuk, hogy ne kelljen file-neveket beírni. A példa az 1.2-es verzióhoz való, mert abban sokkal egyszerűbb az EXOS eljárás használata.

A FILE-rendszerbővítő használatához az alábbi parancsfüzért kell kiadni:

FILE (puffer címe)[ösvény][/opció]

A bővítőnek szüksége van 256 byte pufferre a 0. lapon, ide kerül a visszaadott file-név is, a hosszbyte-tal az elején. Ha a rendszerbővítőből a STOP megnyomásával lépünk ki, üres stringet ad vissza. Rendszerbővítőt a 26-os funkcióhívással lehet meghívni.
Az első megoldandó probléma a parancsfüzér elhelyezése a memória egy címén. A kijelölendő 256 byte-os puffer-nek a - már ismert - 15AEH címtől kezdődő 381 byte-os terület tökéletes. Használjuk e memóriaterületet a parancsfüzér elhelyezésére, majd a bővítő meghívása után ugyanez legyen a felhasznált puffer is! Vagyis a 15AEH címtől kezdve a következő füzért kell eltárolnunk:

cím:
15AE
15AF
15B0
15B1
15B2
15B3
15B4
15B5
érték:
7
'F'
'I'
'L'
'E'
' '
15AEH

A SelectFile eljárás a fenti parancsfüzért helyezi el a memóriában, majd végrehajt egy EXOS 26 funkcióhívást. Ezt követően megvizsgáljuk és lekezeljük azt az esetet, ha nincs FILE-bővítés a rendszerben, valamint ha a FILE-rendszerbővítő üres stringet adott vissza file-név helyet (vagyis a STOP megnyomásával léptünk ki belőle). Ha van file név, EXOS 1 funkcióhívással megnyitjuk az állományt, melyhez az 1-es csatornát rendeljük hozzá. Ezt követően ciklusban karaktert olvasunk be a megnyitott állományból (EXOS 5 funkcióhívás), mindaddig, amíg file-olvasás közben nem következik be hiba (pl. elértük a file végét) - majd kiírjuk a 121-es EDITOR: csatornára (EXOS 7 funkcióhívás).
A sorokat - feltételezve, hogy 80 karakternél nem hosszabb sorokat tartalmazó szöveges állományt listázunk - a CHR(13)-as kód figyelésével számoljuk a 'sor' nevű változóban.
Egy esetet kezelünk még le: nem szöveges fájlokra is számítva (ha a felhasználó nem szöveges állományt nyit meg) a többi 32 alatti, ill. 159 feletti karaktert pontra cseréljük.
Utolsó lépésben természetesen lezárjuk a file olvasásához megnyitott egyes csatornát.

PROGRAM ListFile;
VAR sor:integer;
PROCEDURE SelectFile;
BEGIN
  Poke(£15AE,chr(7));Poke(£15AF,'FILE ');Poke(£15B4,£15AE);
  rde:=£15AE;
  Exos(26)
END;
BEGIN
  ClrScr;
  SelectFile;
  IF ra=Chr(240) THEN BEGIN
    Writeln('Nincs FILE bovites!');Halt
  END;
  IF Peek(£15AE,char)=chr(0) THEN
    Writeln('Muvelet megszakitva!')
  ELSE BEGIN
    ra:=chr(1);rde:=£15AE;Exos(1);
    sor:=1;
    REPEAT
      ra:=chr(1);Exos(5);
      IF ra=Chr(0) THEN BEGIN
        IF NOT(rb IN [Chr(32)..Chr(159),Chr(10),Chr(13)]) THEN
          rb:='.';
        IF rb=Chr(13) THEN sor:=sor+1;
        ra:=Chr(121);Exos(7);
        IF sor=23 THEN BEGIN
          REPEAT UNTIL Inch<>chr(0);
          sor:=1
        END
      END;
    UNTIL ra<>Chr(0);
    IF ra<>Chr(228) THEN
      Writeln('A listazas olvasasi hiba miatt megszakadt!');
    ra:=chr(1);Exos(3)
  END
END.

A FILE-bővítő azonban lehetőséget ad file-maszk beállítására is, így azt is megtehetjük, hogy csak a .TXT kiterjesztésű állományokból válogathatunk. A SELECTFILE eljárás akkor így módosul:

PROCEDURE SelectFile;
BEGIN
  Poke(£15AE,chr(13));Poke(£15AF,'FILE ');Poke(£15B4,£15AE);
  Poke(£15B6,'\*.TXT');
  rde:=£15AE;
  Exos(26)
END;

A. Függelék: Hibakódok

Amennyiben fordítás közben a rendszer hibát észlel megáll, kiírja a hibás sort, valamint az alatta léve sorban a hiba számát (ERROR x). A hiba okának felderítését segítendő egy felfelé mutató nyíl ( a "^" karakter ) mutat a hibás szóra. Ekkor a programozó három dolgot tehet:

  1. Megnyom egy billentyűt, vagyis folytatja a fordítást.
  2. Az "E" billentyűvel javíthatja az aktuális sort.
  3. A "P" billentyűvel javíthatja az előző sort.

A fordítás közben előforduló hibakódok:

  1. Túl nagy szám.
  2. Hiányzó pontosvessző.
  3. Nem deklarált azonosító.
  4. Hiányzó azonosító.
  5. Konstans deklarációban "=" szükséges, nem ":="
  6. Hiányzó "=".
  7. Ez az azonosító nem kezdheti az utasítást.
  8. Hiányzó ":=".
  9. Hiányzó " )".
  10. Hibás típus.
  11. Hiányzó ".".
  12. Hiányzó tényező.
  13. Hiányzó konstans.
  14. Ez az azonosító nem konstans.
  15. Hiányzó "THEN".
  16. Hiányzó "DO".
  17. Hiányzó "TO" vagy " DOWNTO ".
  18. Hiányzó " ( ".
  19. Ilyen típusú kifejezés nem írható.
  20. Hiányzó "OF".
  21. Hiányzó vessző.
  22. Hiányzó kettőspont.
  23. Hiányzó "PROGRAM".
  24. Hiányzik a változó, mivel a paraméter változó paraméter.
  25. Hiányzó "BEGIN".
  26. A "READ" hívásában hiányzó változó.
  27. Ilyen típusú kifejezések nem hasonlíthatók össze.
  28. Vagy INTEGER vagy REAL típus szükséges.
  29. Ilyen típusú változó nem olvasható be.
  30. Ez az azonosító nem típus.
  31. Hiányzó kitevő a valós számban.
  32. Skalár kifejezést vár, nem numerikusat.
  33. Az üres string nem megengedett ( használja a CHR(0)-t ).
  34. Hiányzó "[".
  35. Hiányzó "]".
  36. A tömb index típusának skalárnak kell lennie.
  37. Hiányzó "..".
  38. A tömbdeklarációban hiányzik a "]" vagy a ",".
  39. Az alsó határ nagyobb a felsőnél.
  40. Túl nagy az elemkészlet ( több, mint 256 lehetséges elem ).
  41. A függvény eredményének típusazonosítónak kell lennie.
  42. Az elemkészletben hiányzik a "]" vagy a ",".
  43. Az elemkészletben hiányzik a ".." vagy a "," vagy a "]".
  44. A paraméter típusának típus azonosítónak kell lennie.
  45. Egy nem kijelölő utasításban a nullkészlet nem lehet az első tényező.
  46. Hiányzó skalár ( beleértve a valóst is ).
  47. Hiányzó skalár ( nem beleértve a valóst ).
  48. Inkompatibilis készletek.
  49. Készletek összehasonlításához a "<" és a ">" nem megfelelő.
  50. Hiányzó "FORWARD" , "LABEL", "CONST", "VAR", "TYPE" vagy "BEGIN".
  51. Hiányzó hexadecimális számjegy.
  52. POKE készletek nem lehetségesek.
  53. Túl nagy tömb ( nagyobb, mint 64k ).
  54. A RECORD definícióban az "END" vagy pontosvessző hiányzik.
  55. Hiányzó mezőazonosító.
  56. Hiányzó változó a "WITH" után.
  57. A "WITH"-ben a változónak RECORD típusúnak kell lennie.
  58. A mezőazonosító nem társítható a "WITH" utasítással.
  59. Hiányzó előjel nélküli egész a "LABEL" után.
  60. Hiányzó előjel nélküli egész a "GOTO" után.
  61. Ez a címke rossz szinten van.
  62. Nem deklarált címke.
  63. A "SIZE" paraméterének változónak kell lennie.
  64. Pointerek vizsgálatához csak egyenlőség használható.
  65. INTEGER -ek esetén az egyetlen két kettőspontos írásparaméter az e:m:H.
  66. A stringek sor vége karaktert nem tartalmazhatnak.
  67. A "NEW", "MARK" és "RELEASE" paraméterének pointer típusúnak kell lennie.
  68. Az "ADDR" paraméterének változónak kell lennie.

Szövegszerkesztés közben előfordulható üzenetek:

No room - Elfogyott a program számára fenntartott hely.
Failed! - A kimentett program az ellenőrzés során hibásnak bizonyult.
Verified A kimentett program az ellenőrzés során helyes volt.

Futás közben előforduló hibák:
Ha futás közben hibát észlel a gép, akkor visszatér egy "<hibaüzenet> at PC=xxxx" üzenettel, ahol "xxxx" a hiba előfordulási helye (memóriacím!). A hibaüzenet az alábbiak lehetnek.

HALT Megállás.
Overflow Túlcsordulás.
Out of RAM Elfogyott a memória.
/ by zero Osztás nullával ( a "DIV" is produkálhatja ).
Index too low Túl kicsi index.
Index too high Túl nagy index.
Maths Call Error Matematikai függvény hibás hívása.
Number too large Túl nagy szám.
Number expected Hiányzó szám.
Line too long Túl hosszú sor.
Exponent expected Hiányzó kitevő.

B. Függelék: Fordítási direktívák

A forrásprogram fordításának menetét többféle direktíva használatával vezérelhetjük. Ezek a direktívák, mint speciális megjegyzés jelennek meg a Pascal programban, és minden olyan helyre elhelyezhetők, ahol utasítás állhat. A fordítási direktívában a megjegyzés kezdő kapcsos zárójelét a dollárjel ($) követi, amelyet szintén szóköz nélkül követ a direktíva neve. A direktíva nevét és az esetleges paraméterét legalább egy szóköznek kell elválasztania, akárcsak a záró kapcsos zárójelet.
A direktívák - kettő kivételével - ún. kapcsoló direktíva, melyek a fordító különböző működési módjait kapcsolják be vagy ki. A bekapcsolt állapotot a direktíva nevét közvetlenül követő '+', míg a kikapcsoltat a '-' karakter jelöli.

A direktíva
{$A- }
{$A+ }
Tömb indexhatárainak ellenőrzése. Bekapcsolt állapotban, ha egy tömb indexhatárain kívülre hivatkozunk, a lefordított program futása 'Index too low' vagy 'Index too high' hibaüzenettel megszakad. Kikapcsolt állapotban ez az ellenőrzés elmarad. Alapértelmezés szerint az ellenőrzés bekapcsolt állapotban van. Az indexhatár ellenőrzést csak a már letesztelt, "belőtt" programban javasolt kikapcsolni, mivel ez egy igen nehezen felderíthető hibafajta.

F direktíva
{F filenév }
Állomány beépítése. A fordító a megadott nevű forrásállományt befordítja a programba, a direktíva helyére. Beilleszthetjük vele a programba az előzőleg megírt alprogram-könyvtárakat, forrásnyelvi programrészeket. A direktíva helyének meghatározásakor figyelembe kell venni a deklarációk kötött sorendjét (lásd 4. fejezet)!

I direktíva
{$I- }
{$I+ }
Az egész típusú kifejezéseken végrehajtott relációs műveletek kiértékelő algoritmusát választja ki. Az alapértelmezett, kikapcsolt direktíva esetében a gyorsabb algoritmust fordítja a programba, ilyenkor, ha a két vizsgálandó kifejezés értéke közötti különbség nagyobb, mint 32767, hibás eredményt kapunk. Bekapcsolt állapotban lassabb és hosszabb kódot fordít, amely azonban minden esetben helyes eredményt ad.

L direktíva
{$L- }
{$L+ }

A programsorok fordítás közbeni listázását kapcsolja ki vagy be. Kikapcsolt állapotban csak azokat a sorokat listázza ki a fordító, amelyekben hibát észlelt. Alapértelmezés szerint a listázás be van kapcsolva. Mivel a fordítást a folyamatos listázás jelentősen lassítja, a már "belőtt" programrészletekre javasolt kikapcsolni a listázást.

O direktíva
{$O- }
{$O+ }

Bekapcsolt állapotban a rendszer ellenőrzi a túlcsordulást integer összeadásnál és kivonásnál. A valós műveletek az egész szorzás, a DIV és MOD mindig tesztelve van túlcsordulás szempontjából, arra nem vonatkozik a direktíva. Alapértelmezés szerint a direktíva be van kapcsolva. Ha kikapcsoljuk, egy kicsit rövidebb és gyorsabb kódot lehet előállítani:

{$O+ }
I:=32000+1000;
WRITELN(I);

a fenti esetben az összeadás így fordítódik le:

LD HL,32000
LD DE,1000
OR A
ADC HL,DE
CALL PE,06A6H


; (4T) reset carry bit
; (15T)
; (17/10T) if P/V is set, then writes
;"Overflow at PC=" and end program

futási eredmény:
Overflow at PC=xxxx

{$O- }
I:=32000+1000;
WRITELN(I);

ez esetben az összeadás így fordítódik le:

LD HL,32000
LD DE,1000
ADD HL,DE


; (11T)

futási eredmény:
-32536

P direktíva
{$P filenév }
{$P eszköznév }

A direktívát követő sortól kezdve a megadott file-ba, vagy eszközre (jellemzően PRINTER:) listázza a Pascal a fordítás eredményét. Vagyis minden programsort kilistáz, előtte a memóriacímet, ahova fordította azt. Futás idejű hibák felderítésekor lehet hasznos (amikor csak a hibás utasítás memóriacímét kapjuk meg). Ha a fordítás közben hiba lép fel, csak a hibát okozó sorig listáz.

S direktíva
{$S- }
{$S+ }

Bekapcsolt állapotban a rendszer minden eljárás- és funkcióhíváskor ellenőrzi, a verem esetleges túlcsordulását. Alapértelmezés szerint a direktíva bekapcsolt állapotú, ilyenkor, ha túlcsordulás követezik be, Out of RAM hibaüzenettel megszakad a programvégrehajtás.

Vissza