Az ANY használata


Még ha nem is használta saját programjaiban az ember az ANY paraméter típust, de gyakran dolgozik mások által írt programokkal hibakeresés, üzemeltetés, kisebb-nagyobb módosítások elvégzése miatt, szinte biztos hogy találkozni fog vele.
Bizonyos rendszerhívások ilyen típusú paramétert kérnek esetleg adnak vissza.Ilyen hívás pl. az SFC20 (BLKMOV).


Az ANY paramétertípus című írásban érintettem már a témát. Aki meg akarja érteni az ANY lényegét, az olvassa el azt is.
Az ANY haszna az univerzalitásában rejlik. Továbbá egy kis varázslattal indirekt címzést is megvalósíthatunk vele. Bár erre általában célszerűbb pointereket és address regisztert használni.
Ezért van a blokk mozgató SFC20-nak ANY típusú paramétere.
Ez lehetővé teszi, hogy ugyanazzal a hívással mozgassunk byte-okat, word-öket, stb. Ha nem így lenne, külön hívás kellene a byte, word, dword, típusú adatok másolásához. Arról nem is beszélve, hogy nem csak adattípus van sokféle, hanem adatterület is. Az ANY-nek köszönhetően másolhatunk merker területről (M) DB-be, input területről (I) merkerbe (M), vagy ahogy a szükség hozza.
A képen látható SFC20 paramétere:
P#DB1.DBX 6.0 INT 10 és P#DB1.DBX 430.0 INT 10
Ahol:
P# jelzi, hogy pointert adunk meg, DB1 mondja meg, hogy DB1-es adatblokkra hivatkozunk. A DBX6.0 egy bit címe, nevezetesen a DB1-es blokk 6. című byte-jának 0. bitjére mutat. Fontos, hogy az ANY pointer mindig bitekre hivatkozik akkor is, ha a művelet amit a segítségével végre akarunk hajtani byte, word, dword, int, dint típusú adattal történik.
Ez azért van így, mert az ANY pointer szintaxisa uniformizált. Vagyis ugyanolyan szabályok szerint kell hivatkozni bitre, byte-ra, word-re vagy DWORD-re. Ez szükséges ahhoz, hogy sokoldalú legyen.
Bizonyos műveletek, amik nem bitekkel dolgoznak és ANY pointert kell megadni nekik, a pointer bit címének nullának kell lennie. Ilyen az SFC20 is.
Ha az SFC20 DSTBLK paraméterében bit cím nem nulla, akkor 8329H hibakódot kapjuk RET_VAL-ban. Ha SRCBLK-ban nem nulla a bit cím, akkor 8128H hibakód lesz belőle. A másolás egyik esetben sem történik meg.

A fent említett Az ANY paramétertípus című írás egy példával mutatja be hogyan lehet egy ANY pointert összeállítani az SFC20 számára.
Ez az írás ennek az ellenkezőjével foglalkozik: Hogyan boncoljunk szét egy ANY típusú pointert, hogy az abban megadott terület, címet kezelni tudjuk.

A példaprogram működése
Egy jó kis példaprogram többet ér ezer szónál, ezért egy olyan blokkot mutatok be, aminek ANY bemenő paramétere van és egy másik paraméterben megadott byte-ot keres az ANY által meghatározott területen, majd visszaad egy logikai eredményt az ENO kimenetén, hogy megtalálta vagy sem. Van egy további logikai típusú kimenete, amiből kiderül, hogy keresés közben történt-e hiba.
Erre azért van szükség, mert ha a keresést valami miatt nem tudta végrehajtani (pl. olyan adatterületet adtunk meg ami vagy aminek egy része nem létezik) akkor kiderüljön, hogy a blokk ENO kimenő paraméterének értékét nem lehet figyelembe venni. Hiba esetén NO természetesen FALSE állpotú lesz, de ha nem lenne más információ csak ez, akkor nem lehetne eldönteni, hogy egy hiba miatt FALSE, vagy azért, mert a keresett byte-ot a keresési terület nem tartalmazza.
A blokk így fest híváskor:


A blokk a fenti paraméterezéssel a következőt fogja csinálni:
A DB2-ben az 502-es byte címtől kezdve 230 byte-on át (tehát DB2.DBB732-ig) keresi az 1-et. Ha az adott területen belül bármelyik bytet tartalma 1, akkor a blokk ENO kimenete TRUE lesz és bekapcsolja M0.1-es merkert. Ha a terület egyetlen egy 1-es értéket sem tartalmaz, akkor ENO FALSE marad.
A blokk az ANY paramétertípusnak köszönhetően el tudja végezni ugyanezt a keresést más adattípusra és más memória területen is (már ami a keresési terület megadását illeti, hiszen ettől függetlenül a mutatott területen lehetnek más típusú, akár vegyes adatok is). Pl. kereshetünk adott byte tartalmat MW10-MW40-ig stb...
Viszont mindig byte-ot fog keresni, ami meg egyrészt attól független, hogy a keresési területet az ANY paraméterben milyen típusúnak deklaráltuk, másrészt meg attól, hogy a keresési területen valójában milyen típusú adatok vannak. Szóval akkor is byte-ot fog keresni, ha S7Timer vagy épp REAL típusú adatok vagy vegyes adatok vannak azon a területen ahol keres!
Az #Ok kimenet pedig TRUE állapotú, ha a keresést sikerrel hajtotta végre.

Ez így egyszerű, de mi történik a blokkon belül?
Hogy az ANY paramétert, amivel a keresési területet adjuk meg értelmezni tudjuk, ismernünk kell annak felépítését.



Néhámy példa a szintaxisra:
P#DB6.DBX54.2 BYTE 30
P#DB204.DBX544.7 INT 4
P#M6.0 REAL 2
P#Q4.0 WORD 6


A blokkban lévő program megnézi milyen típusra vonatkozik az ismétlési faktor. Az ugyanis, hogy hány byte adatot kell végigellenőrizni ettől és az ismétlési faktortól függ.
A példában a típus BYTE az ismétlési faktor pedig 230. Tehát 230 BYTE-ot fog végignézni.
Ha azonban INT, WORD, S5TIME, vagy DATE a típus, akkor kétszer annyi byteot kell átnézni, mert ezek a típusok 2 byteosak.
REAL, DWORD, DINT, TIME, TIME_OF_DAY típusok esetén ha az ismétlési faktor szintén 230 lenne, akkor már 920 byteot kellene átolvasni.
Az adattípusból és az ismétlési faktorból kiderül tehát hány byteos területen kell keresni.

Ezután meghatározza az #FArea-ból, hogy DB-t adtunk-e meg. Ha igen, akkor megvizsgálja, hogy a DB létezik-e a PLC-ben. Ha nem, akkor FALSE-ra állítja #Ok kimenetet és kilép. Ha van olyan DB, akkor az #FArea paraméter típus, kezdőcím és az imént az ismétlési faktorból, valamint adattípusból meghatározott byteok számából kideríti elég elemből áll-e a DB blokk, amiben a keresést kell végezni.
Ha a keresési terület túlmutat a DB határain, hibával kilép (#Ok=FALSE).
Ha a DB létezik és elég nagy, akkor megnyitja azt.

A blokk egy ciklus segítségével egymás után csökkenő címsorrendben (vagyis a keresést hátulról, a magasabb című byte felől kezdve) beolvassa a keresési terület bytejainak tartalmát és összehasonlítja #B paraméterben megadott tartalommal. Ha egyezik, ENO kimenetet TRUE-ra álítja és kilép a ciklusból. A keresési területet tehát csak akkor olvassa teljesen végig, ha a keresett byte nem szerepel a területen, vagy annak első byte-ja az (vagyis az utolsóként vizsgált).
A ciklus ciklusváltozója a fenti módon az adattípusból és ismétlési faktorból számított byte számnál indul és nulláig vagy találatig csökken. A ciklusváltozót a keresési terület megcímzése előtt minden vizsgálatnál hozzáadja az #FArea paraméterben lévő kezdőcímhez.

A példaprogram

A blokk paraméterei és belső változói (interface):





Az első network szétszedi az #FArea paramétert külön belső változókra.
Mivel a keresés FC blokkban van megvalósítva, az input paraméterek nem kerülnek instance DB-be ahonnan byte-onként közvetlenül is kiolvashatnánk az #FArea pointer 10 byteját. Ehhez egy kis trükkre van szükség.

L P##FArea utasítás betölti ACCU1-be az #FArea-ra mutató pointert, majd ez LAR1 után az 1-es címregiszterben landol.
AR1 regiszter tehát most egy memóriacímet tartalmaz, ami pontosan az ANY paraméterünk első bytejára mutat. Mivel AR1 történetesen címregiszter, címezhetünk vele:
 L B [AR1,P#0.0] sor azt csinálja, hogy az ACCU1-be tölti azt a byte-ot, (B) amire az AR1+0.0 mutat. P#0.0 egy eltolás, ahol a pont előtti szám a byte cím eltolása, a pont utáni a bit cím eltolása.
Mivel az első sorban 0.0 eltolást alkalmaztunk, az ACCU1-ben a blokknak paraméterben megadott ANY pointer első byte-ja fog betöltődni, hiszen AR1 regiszterben annak kezdőcíme van.
Mit találunk az ANY pointer első bytejában? 10H-t, ami mindig 10H S7 300/400 PLC esetében (Syntax ID).
Megtehetnénk, hogy egy összehasonlítással megvizsgáljuk hogy ez tényleg 10H-e, és kilépünk ha nem, mert ha a blokkunknak nem konstans ANY pointert adunk, hanem egy "összelapátolt" ANY pointert, akkor itt valami más is lehet elvileg, de ez csak ennek a mondatnak a leírása közben jutott eszembe és most már semmi kedvem új képet csinálni a network 1-ről, amiben ez is benne van.
Az ANY pointer második byteját az  L B [AR1,P#1.0] utasítással címezhetjük meg, ami az adattípust azonosítja (lásd a fenti ábrát). A P#1.0 mondja meg, hogy az AR1 regiszter tartalmához, ami az ANY elejére mutat, hozzá kell adni egyet, hogy a második byte-ot kapjuk (0 eltolás= első byte, 1 eltolás=második byte, stb).
Ennek megfelelően az első network az ANY pointert alkotórészeire bontja és az egyes részeket külön belső változóba helyezi.

A következő network meghatározza hány byte adatot kell átböngészni a megadott adatterületen:



A #Repeat ismétlési faktort vagy változatlanul hagyja egy byte-os adattípusok esetén, vagy kettővel szorozza két byteos típusoknál, vagy 4-el szorozza 4 byte-os típusoknál, stb.
Az eredmény a #Repeat változóba kerül vissza, így a következő network-től a #Repeat megmondja hány byte adatot kell összehasonlítani.

A 3-as network azzal bíbelődik, hogy először is kiderítse adatblokkban kell-e majd keresni vagy sem.


Ha nem, akkor elugrik a network végére, átugorva ezzel az adatblokkra vonatkozó műveleteket
Másodszor, azzal foglalkozik a NW, hogy ha adatblokkban van a keresési terület, akkor az az adatblokk létezik-e egyáltalán, vagy nem.
Ehhez először az SFC 24 hívással megtudakol pár dolgot az adatblokkról.


Az eredményt további belső változókba helyezi el:
#SFCRV egy hibakódot ad vissza, ha az adatblokk nem létezik, vagy valamilyen okból a blokkhoz nem fért hozzá.
Nulla hibakódot kapunk, ha a blokk létezik és rendben van.
A #DBLen változóban a DB teljes méretét kapjuk byte-ban mérve.
#WrProtect pedig megmondja hogy a DB írásvédett-e, ami minket most marhára nem érdekel, de az SFC hívásnak meg kell adni egy változót, amibe beleteszi ezt az információt.
Ezután jöhet a blokk által szolgáltatott adatok kiértékelése:



Az L LD 6 utasítás betölti ACCU1-be a 6-os lokális változóterületen lévő duplaszót.
Ha megnézzük mi van ott, azt látjuk a blokkunk interface részénél ahol a változódeklaráció van, hogy az LD 6 címen éppen az #Area, #Addr1, #Addr2, #Addr3 byte típusú változóink vannak:



Ez azért kell, mert ki kell számolnunk, hogy az #FArea által mutatott terület belefér-e a DB-be, amire hivatkozik. Ennek megállapításához három adat szükséges:
  1. A DB hossza, amit már megszereztünk és jelenleg épp a #DBLen nevű változóban üldögél.
  2. A keresési terület mérete, amit szintén kiszámítottunk és a #Repeat változó őrzi.
  3. A keresési cím kezdete az adatblokkban.
Ezt a harmadikat kell még megszerezni. Ehhez egy kicsit gépészkedni kell, mert csak egy byte címre van szükségünk és az ANY pointer a bit címet is tartalmazza így azokat le kell vágni. Tulajdonképpen csak az #Addr1, #Addr2, #Addr3 változó tartalma kell, mert ezekben van a cím. Kényelmesebb kezelni ha együtt vannak, ezért betöltjük az ACCU1-be, de oda csak 1, 2, vagy 4 byte-ot lehet betölteni így kerül oda az #Area is.

ACCU1 L LD6 után az #FARea ANY pointer 6, 7, 8, 9-es byte-ját tartalmazza:


Az #Area-ra a byte cím kiszámolásához nincs szükség, meg is szabadulunk tőle a jó öreg AND műveletes maszkolás segítségével:
 
L 2#111111111111111111111111 
 AD  

Betölti a bináris 1111 1111 1111 1111 1111 1111 -et (ami 00FFFFFFh-nak felel meg) vagyis a felső byte 0 a többi 1-es biteket tartalmaz. Ezután AND műveletet hajt végre a két akkumulátor között (a load miatt az ACCU2-ben most az LD 6-al betöltött adat van) és az eredményt ACCU1-be teszi.
Az eredményben minden ACCU2-ben lévő bit változatlanul megmarad ami ACCU1-ben 1 volt és törlődnek azok, ahol ACCU1-ben 0 volt. Egy szó mint száz, ez a két sor törli az ACCU1 felső byte-ját, ahol épp az #Area változó tartalma volt eddig. A helyén nullák lesznek.
Most már csak el kell tüntetni a bit címet és megkapjuk 16 biten a byte címet. A bit cím eltüntetéséhez az SRD 3 utasítást használjuk, ami 3 bittel jobbra lépteti az ACCU1-ben lévő duplaszavunkat. Az eredetileg 7-es byte alsó 3 bitje átvándorol a valamikori 8-as byte felső 3 bitjébe és a hajdani 9-es byte alsó három bitje, ami a bit cím volt, kiesik. A művelet után az ACCU1 alsó 16 bitje tartalmazza a tiszta byte címet, ami megadja a keresési terület kezdőcímét.
Ehhez hozzá kell adni a terület méretét, vagyis a #Repeat változó tartalmát, és megkapjuk, hogy a keresésben résztvevő utolsó byte hol van az adatblokkban. Mivel ez a byte cím nem eshet a DB-n kívülre, ezért össze kell hasonlítani a DB hosszával (#DBLen) és kilépni ha a DB hossza kisebb. Ezt a műveletsort hajtja végre a network további része.
A JC ERR utasítás fog elugrani a hibajelzéshez és kilépéshez ha a DB nem elég nagy.
A network további része (amennyiben az ugrás nem történt meg) megnézi mi van az #SFCRV változóban. Ha nem nulla, akkor a DB nem létezik, vagy valami más miatt nem érhető el. Ez az eset egy másik ugrás segítségével megint a hibajelzéshez vezet (JC ERR).



Ha nem volt hiba, ugrás nélkül fut tovább, ahol az #Area változó tartalma segítségével megnézi hogy shared vagy instance DB-t kell-e megnyitni.



Ha a program ezen is átvergődik anélkül, hogy elugrana az ERR nevű címkére hiba miatt, akkor már biztosan tudjuk hogy:
Ez elég ahhoz, hogy végre megkezdhessük a tényleges keresést.
Kell egy ciklus, ami a NEXT: címkénél kezdődik. Fel kell tölteni a ciklusváltozót, ami meghatározza hogy a ciklus hányszor fusson le.



Ezután újra betöltjük a 6-os dupla szót (L LD 6), ahol #Area, #Addr1, #Addr2, #Addr3 változók vannak, de ezúttal nem szabadulunk meg az #Area részétől, mert erre is szükség van.
Ez a négy byte így ahogy van, pontosan megfelel a címregiszterben lévő cím felépítésének (ez is oda fog kerülni, hogy címezni tudjunk vele). Ez egyszerűsíti a dolgunkat, de egy kicsi kavarodást így sem tudunk elkerülni.
A kavarodás oka hasonló az előbbihez.
Az éppen vizsgált byte címhez mindig hozzá kell adnunk a ciklusváltozó -1 értéket. A pointer formátum által tartalmazott bit címek miatt azonban ezt nem lehet. Ezért el kell tolni a tartalmat 3 bittel jobbra, elvégezni az összeadást, és kivonást, majd visszatolni 3 bittel balra.
Igaz, hogy a duplaszó tetején a legmagasabb helyiértékű byte-on (azaz az eltolás után 3 bittel lejjebb) ott ül az #Area változónk tartalma, de az összeadás nem fogja zavarni, amíg kétmilliónál kisebb számot adunk hozzá. Erre nem fog sor kerülni, mert ekkora címek nincsenek S7-ben.
A balra tolás eredményeképpen a duplaszóba balról belép három 0 tartalmú bit a bit cím helyébe. Ha az #FArea nem nulla bit címet tartalmazott (Pl. P#DB2.DBX6.5 BYTE 40) akkor a bit cím elvész a rotálás után. Ez nem baj, sőt kifejezetten hasznos, mert a blokkunk csak byte címekkel tud dolgozni, a bit címnek nullának kell lennie.


A műveletsor vége egy LAR1 utasítás, ami az előkészített pointert a címregiszterbe tölti. A címregiszterrel már meg lehet címezni a keresési területet:



Betöltünk egy byte-ot az L B [AR1,P#0.0] utasítással.
Az ACCU1-be azt a byteot tölti, aminek a címe az AR1 címregiszterben van. Mivel az AR1-ben lévő címhez nincs eltolás, az eltolás értéke nulla lesz (P#0.0).
Ezt Area-Crossing Addressing-nek nevezik, mert a címzendő memóriaterületet is a címregiszter tartalma mondja meg. Azt hogy byte-ot akarunk tölteni, a B betű mondja meg.
Ezt most már csak össze kell hasonlítani a blokk #B paraméterében érkező byte tartalmával.
Ha azonos a kettő, akkor megtaláltuk amit kerestünk, TRUE állapotúra lehet állítani a blokk ENO kimenetét és ki lehet lépni. Ezt végzi a JC E050 feltételes ugrás.
Ha nem egyforma, akkor róni kell tovább a köröket. Be kell tölteni a ciklusváltozót (#Index) ACCU1-be a LOOP utasítás számára. A LOOP egyel csökkenti ACCU1-et és ha nem nulla lett, visszaugrik a ciklus elejére (a NEXT címkére). Ott újra eltesszük a már egyel kisebb ciklusváltozót az ACCUe1-ből az #Indexbe, újra kiszámítjuk a címet ezúttal a már egyel kisebb #Index tartalmával újra címezzük a keresési területet összehasonlítunk, stb.
Ha a ciklus teljesen lefut, tehát #Index eléri a nullát, akkor a LOOP nem fog visszaugrani és a program ráfut a CLR, SAVE, SET = #Ok utasítássorra, ami beállítja a kész jelzést törli a találat jelzést, hiszen ha ez történik, akkor a keresési terület nem tartalmazta a keresett byteot.

Már csak egy network van hátra.
Ebben van az a két címke, amire hiba esetén és találat esetén ugrik:


A kereső blokk

Kapcsolódó írások:
Az ANY paramétertípus
Pointerek
S7 300/400 indirekt címzés, pointerek
S7-300/400 címtartományok, adattípusok


A kereső blokk teljes forrása:
FUNCTION FC 11 : VOID
TITLE =
//Keresés.
//A blokk #FArea pointer által meghatározott blokkban keresi #B byte tartalmát.
//Ha megtalálja, a blokk ENO kimenete TRUE lesz, ha nem találja meg, vagy
//hiba keletkezik, akkor FALSE.
//#Ok kimenet TRUE, ha a blokk hiba nélkül lefutott és FALSE ha hiba keletkezett.
//Lehetséges hibák: Az FArea olyan DB blokkra mutat, ami nem létezik. FArea által
//megadott terület túlmutat a DB határán.
//
//A blokk DB cím esetén megvizsgálja, hogy a DB létezik-e és #FArea nem mutat-e
//túl annak méretén, de nem végez vizsgálatot más adatterületek túlcémzésére (pl.
//Merker terület).
//
//A blokk bármilyen adatterületen és típusban hajlandó keresni.
AUTHOR : Szirty
VERSION : 0.1


VAR_INPUT
FArea : ANY ; //A terület, amin keresni kell
B : BYTE ; //A keresett byte
END_VAR
VAR_OUTPUT
Ok : BOOL ; //A keresés sikerességének jelzése (TRUE=sikeres, FALSE=hiba történt)
END_VAR
VAR_TEMP
S710H : BYTE ; //Syntax ID, S7-nél mindig 10h
DType : BYTE ; //Adattípus
Repeat : INT ; //Ismétlési faktor
DBNum : WORD ; //DB száma, ha a keresés DB-re vonatkozik
Area : BYTE ; //Az adatterület (memóriaterület) azonosítója
Addr1 : BYTE ; //A cím első része
Addr2 : BYTE ; //A cím második része
Addr3 : BYTE ; //A cím harmadik része
Index : WORD ; //Ciklusváltozó
SFCRV : INT ; //SFC hívás visszatérési értéke
DBLen : WORD ; //A DB hossza (byte-ban)
WrProtect : BOOL ; //Írásvédett DB jelzése
END_VAR
BEGIN
NETWORK
TITLE =
//Az ANY pointer szétszerelése
L P##FArea;
LAR1 ;
L B [AR1,P#0.0];
T #S710H; // S7-nél ez mindig 10H
L B [AR1,P#1.0];
T #DType; // Adat típusa
L W [AR1,P#2.0];
T #Repeat; // Ismétlési faktor
L W [AR1,P#4.0];
T #DBNum; // DB száma
L B [AR1,P#6.0];
T #Area; // Az adatterület (memóriaterület) azonosítója
L B [AR1,P#7.0];
T #Addr1; // A cím első része
L B [AR1,P#8.0];
T #Addr2; // A cím második része
L B [AR1,P#9.0];
T #Addr3; // A cím harmadik része
CLR ;
= #Ok;
NETWORK
TITLE =
//Az adattípus vizsgálata és annak alapján a keresési terület méretének
//beállítása
L #DType; // Ha az adattípus NULL pointer (NIL), vagy BOOL, akkor kilép
L 2;
<I ;
JC ERR;
O( ;
L #DType;
L 4;
==I ; // Ha az adattípus WORD (04)
) ;
O( ;
L #DType;
L 5;
==I ; // vagy INT (05)
) ;
O( ;
L #DType;
L 9;
==I ; // vagy DATE (09)
) ;
O( ;
L #DType;
L 12;
==I ; // vagy S5TIME (0C)
) ;
JCN L004;
L #Repeat;
L 2;
*I ; // akkor az ismétlési faktort megszorozza 2-vel
T #Repeat;

L004: O( ;
L #DType;
L 6;
==I ; // Ha az adattípus DWORD (06)
) ;
O( ;
L #DType;
L 7;
==I ; // vagy DINT (07)
) ;
O( ;
L #DType;
L 8;
==I ; // vagy REAL (08)
) ;
O( ;
L #DType;
L 11;
==I ; // vagy TIME (0B)
) ;
O( ;
L #DType;
L 10;
==I ; // vagy TIME_OF_DAY (0A)
) ;
JCN L005;
L #Repeat;
L 4;
*I ; // akkor az ismétlési faktort megszorozza 4-el
T #Repeat;

L005: L #DType;
L 15;
==I ; // Ha az adattípus DATE_AND_TIME (0E)
JCN L030;
L #Repeat;
L 8;
*I ; // akkor az ismétlési faktort megszorozza 8-al
T #Repeat;
NETWORK
TITLE =
//Ellenőrzés, hogy az ANY paraméterben megadott keresési terület nem mutat-e túl
//az adatblokk határán, ha a keresés DB-ben történik.
L030: NOP 0;
O( ;
L #Area;
L B#16#84;
==I ; // Ha az adatterület DB (84)
) ;
O( ;
L #Area;
L B#16#85;
==I ; // vagy DI (85)
) ;
JCN L033; // #FArea nem adatblokkra hivatkozik, akkor ugrik

CALL SFC 24 (// SFC 24 segítségével megvizsgálja, hogy ha a keresés DB-ben történik,
DB_NUMBER := #DBNum,// akkor a DB létezik-e és FArea annak területén belül van-e
RET_VAL := #SFCRV,
DB_LENGTH := #DBLen,
WRITE_PROT := #WrProtect);
L LD 6; // Adatterület és cím betöltése
L 2#111111111111111111111111; // Maszk betöltése
AD ; // A meóriaterület kimaszkolása
SRD 3; // A bit címek levágása. Accu1-ben most a terület kezdetének byte címe van
L #Repeat; // Ismétlési faktor (keresési terület mérete)
+D ; // + a keresés kezdőcíme
L #DBLen;
>D ; // nagyobb, mint a DB hossza, kilép
JC ERR;
L #SFCRV; // Ha SFC 24 hibával tért vissza, kilép
L 0;
<>I ;
JC ERR;

L #Area;
L B#16#84; // Ha az adatterület DB
JCN L032;
OPN DB [#DBNum]; // Megnyitja a shared DB-t
JU L033;
L032: L #Area;
L B#16#85; // Ha az adatterület DI
JCN L030;
OPN DI [#DBNum]; // Megnyitja az instance DB-t
L033: NOP 0;
NETWORK
TITLE =
//Kereső ciklus
L #Repeat; // Ismétlési faktor betöltése a ckilus változóba
Next: T #Index;
L LD 6; // Adatterület és cím betöltése
SRD 3; // Bit címek levágása
L #Index; // Ciklusváltozó hozzáadása a címhez
+D ;
L L#1;
-D ;
SLD 3; // Accu1 Pointer formátummá alakítása
LAR1 ;
L B [AR1,P#0.0];
L #B;
==I ; // Keresett byte összehasonlítása
JC E050; // Ha megtalálta, kilép
L #Index;
LOOP Next;
CLR ;
SAVE ;
SET ;
= #Ok;
BE ; //Kilépés, amikor nincs találat
NETWORK
TITLE =

E050: SET ; // Kilépés, amikor van találat
= #Ok;
SAVE ;
BE ;
ERR: CLR ; // Kilépés, amikor hiba keletkezik
= #Ok;
SAVE ;
END_FUNCTION




Szirty