PORTY (3) To juū ostatnia czėōź podróūy po mėtnych wodach exec.library. Dziō dobijemy do portu. Cromax/Alchemy exec.library/ReplyMsg ZARYS ReplyMsg (message)/(A1) -- umieōź wiadomoōź w porcie zwrotnym. FUNKCJA Funkcja ta umieszcza komunikat w jego porcie zwrotnym. Dzieje siė tak zazwyczaj, gdy odbiorca komunikatu skoļczyī jego analizė i chce go zwróciź do nadawcy (gdzie moūe zostaź ponownie uūyty lub gdzie moūe zostaź zwolniona zajmowana przez niego pamiėź czy cokolwiek). Funkcjė moūna wywoīaź z przerwaļ. WEJŌCIE message -- Wskaśnik do komunikatu. exec.library/WaitPort ZARYS message (D0) = WaitPort (port)/(A0) -- czekaj, aū dany port nie bėdzie pusty FUNKCJA Ta funkcja z kolei czeka, aū dany port przestanie byź pusty (tzn. aū pojawiā siė w nim jakieō komunikaty). Jeōli zajdzie taka potrzeba, zostanie wywoīana funkcja Wait(), czekajāca na sygnaīy portu. Jeūeli w porcie znajduje siė jakaō wiadomoōź, funkcja natychmiast wróci. Wartoōź zwrotna jest zawsze wskaśnikiem do pierwszego dostėpnego komunikatu w kolejce (ale nie jest on usuwany z kolejki). UWAGA: W porcie moūe siė znajdowaź wiėcej niū jeden komunikat, gdy funkcja wróci. Wskazane jest wiėc wywoīywaź funkcjė GetMsg() w pėtli, dopóki wszystkie komunikaty nie zostanā odczytane, potem zaō znowu czekaź na nastėpne komunikaty. Aby czekaź na wiėcej niū jeden port, naleūy poīāczyź bity sygnaīowe z kaūdego portu do jednego wywoīania funkcji Wait(), potem uūywajāc GetMsg() w pėtli wydobyź wszystkie komunikaty. Moūe siė zdarzyź, ūe otrzymasz sygnaī dla portu BEZ komunikatu. Naleūy to przewidzieź. WEJŌCIE port -- Wskaśnik do portu komunikacyjnego WYJŌCIE message -- Wskaśnik do pierwszego dostėpnego komunikatu exec.library/FindPort ZARYS port (D0) = FindPort (name)/(A1) FUNKCJA Zadaniem tej funkcji jest znalezienie na systemowej liōcie portów komunikacyjnych portu o podanej nazwie. Zwrócony zostanie pierwszy port o takiej samej nazwie, znajdujācy siė na liōcie. Lista portów nie zostanie rozīāczona. Funkcja ta MUSI byź chroniona parā funkcji Forbid()/Permit()! WEJŌCIE name -- Nazwa portu do znalezienia. WYJŌCIE port -- Wskaśnik do portu komunikacyjnego lub zero, jeōli brak takiego na liōcie. exec.library/CreateMsgPort (V36) ZARYS MsgPort (D0) = CreateMsgPort() -- utwórz i zainicjalizuj nowy port komunikacyjny FUNKCJA Alokuje i inicjalizuje nowy port komunikacyjny. Struktura mp_List w nowo utworzonym porcie takūe zostanie przygotowana do uūytku (przez NewList). Zostanie zaalokowany bit sygnaīowy, port zaō bėdzie ustawiony na sygnalizowanie Twojego zadania, gdy przybėdzie komunikat (PA_SIGNAL). Ūeby skasowaź port utworzony przez CreateMsgPort(), MUSISZ uūyź DeleteMsPort(), WYJŌCIE MsgPort -- Nowa struktura MsgPort gotowa do uūycia lub NULL (zero), jeōli zabrakīo pamiėci czy sygnaīów. Jeūeli zamierzasz dodaź ten port do publicznej listy portów, wypeīnij pola ln_Name i ln_Pri, potem wywoīaj AddPort(). Nie zapomnij uūyź RemPort()! exec.library/DeleteMsgPort (V36) ZARYS DeleteMsgPort (MsgPort)/(A0) -- zwolnij port komunikacyjny, stworzony przez CreateMsgPort(). FUNKCJA Zwalnia port komunikacyjny stworzony dziėki wywoīaniu CreateMsgPort(). Na wszystkie komunikaty, które mogā znajdowaź siė w porcie, naleūy wczeōniej odpowiedzieź. WEJŌCIE MsgPort -- Wskaśnik do portu komunikacyjnego. NULL (zero) -- brak akcji. * Uff, trochė juū chyba wiemy, przyszīa wiėc pora, aby sprawdziź nasze wiadomoōci. Jeszcze tylko jedno sīówko o funkcjach _LVOForbid()/_LVOPermit(). Pierwsza z nich zatrzymuje multitasking, tzn. powstrzymuje system przed przeīāczaniem obiektów w listach systemowych, usuwaniem ich etc. Druga przywraca normalny stan. Po co nam te dwie funkcje? Wyobraśmy sobie, ūe uūywamy _LVOFindPort(). Zaraz po tym, gdy znaleśliōmy poszukiwany port, program, który go zaīoūyī, usuwa go z listy. Czyli w sumie mamy adres do nieistniejācego juū portu. Hmm... zatem jeōli chcemy wysīaź jakikolwiek komunikat do portu publicznego, naleūy: 1. uūyź _LVOForbid(); 2. nastėpnie _LVOFindPort(); 3. wysīaź komunikat _LVOPutMsg(); 4. przywróciź multitasking _LVOPermit(). No, teraz juū do roboty! Mamy tu listingi dwóch programów. Pierwszy z nich zakīada publiczny port o nazwie "Amsterdam" i czeka na wiadomoōź [plik WaitPort.s]. Drugi szuka na liōcie portów publicznych w/w portu i jeōli go znajduje, wysyīa mu komunikat [plik Put.s]. Najlepiej najpierw je przepisz -- nie sā bardzo dīugie -- i uruchom, a potem ewentualnie wróź do lektury. Zanim jednak je wykonasz, otwórz dwa okienka Shell (czy CLI). Teraz dopiero w jednym uruchom pierwszy program (Wait), w drugim zaō program Put, najlepiej z parametrami (dowolnymi), np.: 2> Put Józek ma drewniany wózek Po zapoznaniu siė z dziaīaniem obu programów oraz przyswojeniu sobie wiedzy na temat funkcjonowania procedur uūytych w przykīadach, sugerujė poszerzenie wiedzy dziėki lekturze poniūszego materiaīu, który traktuje o czekaniu na komunikaty... jednoczeōnie w kilku portach naraz! A co, Amiga rules! W powyūszych opisach juū zetknāīeō siė z takimi wyrazami, jak "signal", "mp_SigBit", "_LVOWait()" etc. Wróźmy zatem znowu do naszego morskiego portu, gdzie w powietrzu unoszā siė mewy, a bosmani klnāc, zachrypniėtym gīosem poganiajā majtków... Zaczynasz siė rozglādaź i nagle spostrzegasz latarniė morskā. Na jej czubku siedzi facet i ciāgle obserwuje otoczenie. Kiedy widzi, ūe do portu przybija statek albo ūe zbiera siė na burzė, czy teū spostrzega jeszcze coō innego, wychyla siė przez barierkė i wrzeszczy jak opėtany. Krótko mówiāc, daje sygnaī. Jak pamiėtasz, przybicie statku do portu byīo dotarciem komunikatu. Natomiast ten krzyk niezrównowaūonego latarnika jest niczym innym, jak sygnalizowaniem. Cóū to jest, to tajemnicze sygnalizowanie? Wróźmy jeszcze na chwilė do naszego krzykacza... Powiedziano mu, ūe jeōli zaobserwuje zawijajācy statek, to ma krzyczeź STATEK W PORCIE! Dwa -- jeōli zobaczy burzė, to ma krzyczeź IDZIE BURZA! Jeōli natomiast zobaczy ūóītā īódś podwodnā, to niech krzyczy YELLOW SUBMARINE! Ale to jest wszystko, o czym potrafi zasygnalizowaź. Jeōli mu nie powiemy, ūe gdy zobaczy ufo, to ma krzyczeź UFO!, to nawet gdyby doszīo do inwazji, facet bėdzie milczaī wypatrujāc statków, burzy czy īodzi podwodnych. Przekīadajāc to na bardziej komputerowy sposób rozumowania -- jeōli zajdzie zdarzenie A, to daj sygnaī nr 1, gdy dojdzie do wydarzenia B, zasygnalizuj 2 etc. Otóū dotarcie wiadomoōci do portu jest pewnym zdarzeniem. Jeōli teraz w polu mp_SigBit bėdzie siė znajdowaīa prawidīowo (o tym póśniej) okreōlona wartoōź, to zostanie ona zasygnalizowana (moūna powiedzieź, ūe ta wartoōź jest treōciā komunikatu, jakā ma wykrzyczeź latarnik). Czyli powiedzmy ten facet wstanie i krzyknie STATEK! Jak pamiėtasz, funkcja _LVOWaitPort() czekaīa, aū w porcie pojawi siė komunikat. Natomiast _LVOWait() czeka na okreōlony sygnaī. Wyobraś sobie, ūe pod latarniā poīoūyīo siė spaź dwóch marynarzy. Jeden z nich budzi siė na dświėk zdania STATEK W PORCIE A!, drugi zaō STATEK W PORCIE B!. Tych dwoje marynarzy, to nasza funkcja _LVOWait(). Zauwaū, ūe jeōli latarnik krzyknie YELLOW SUBMARINE!, to ūaden z nich siė nie obudzi (chyba, ūe poīoūymy tam jeszcze jednego marynarza reagujācego na ten okrzyk). Poīāczmy to teraz z portami. Niech -- tak jak powyūej -- zawiniėcie statku do portu A bėdzie zdarzeniem A, sīowa STATEK W PORCIE A! bėdā sygnaīem nr 1 itd. Weśmy teraz dwa róūne porty (A i B). Do jednego, do pola mp_SigBit wpisujemy Sygnaī nr 1 (tzn., ūe jak wpīynie do niego Message, to zasygnalizowany bėdzie Sygnaī nr 1, czyli okrzyk STATEK W PORCIE A!), do drugiego zaō -- Sygnaī nr 2. Oczywiōcie w polu mp_SigBit moūe siė znajdowaź tylko jeden sygnaī. Teraz z pól mp_SigBit bierzemy oba sygnaīy, podajemy je jako argument do _LVOWait(), nasz program zatrzymuje siė i... i jeūeli do jednego z nich wpīynie Message, to nasz program pójdzie dalej! Krótko mówiāc -- _LVOWaitPort() umoūliwiaīa nam oczekiwanie na komunikat, ale tylko w jednym porcie, natomiast _LVOWait() umoūliwia czekanie na komunikaty w jednym i wiėcej portach naraz! No, tak naprawdė, to _LVOWait() czeka na okreōlone sygnaīy, które jeōli sā ustawione w porcie, bėdā zasygnalizowane, gdy przybėdzie wiadomoōź (lub gdy sami procedurā _LVOSignal() taki sygnaī wyōlemy do okreōlonego tasku). Oczywiōcie, jeōli ustawimy w mp_SigBit numer sygnaīu, ale nie podamy go do _LVOWait(), to choźby wpīynėīo tam tysiāc komunikatów, nie bėdziemy o tym informowani. Skoro -- jak mam nadziejė -- ogólna zasada dziaīania sygnaīów zostaīa wyjaōniona, moūna przejōź do czėōci bardziej ōcisīej. Otóū dla kaūdego tasku przewidziane sā 32 róūne sygnaīy, na które moūe oczekiwaź _LVOWait(). Szesnaōcie z nich jest przeznaczonych dla sygnaīów systemowych, np. sygnaī jest wysyīany po naciōniėciu klawiszy [Ctrl][C], [D], [E], [F] oraz z powodu innych zdarzeļ. Pozostaīe 16 sygnaīów moūemy juū dowolnie wykorzystaź. Piszāc nasz program, moūemy takūe czekaź na sygnaīy systemowe i po ich otrzymaniu stosownie do nich siė zachowaź (np. przerwaź wykonywanie programu). Sygnaīy sā reprezentowane przez bity (jeden ustawiony bit to jeden sygnaī), przy czym systemowe sygnaīy, to 16 mīodszych bitów. Reszta, to bity (sygnaīy), które moūemy wykorzystywaź wedle naszych potrzeb. Ūeby móc wykorzystaź gdzieō sygnaī, trzeba go sobie zaalokowaź (czyli przydzieliź). Sīuūy do tego funkcja _LVOAllocSignal(). Podaje siė jej jako argument numer bitu, który ma byź przydzielony nam jako sygnaī (od 0 do 31). Jeūeli nie ma juū wolnych sygnaīów albo numer, który podaliōmy jest juū w uūyciu, zostanie nam zwrócone -1. Najrozsādniejsze wydaje siė alokowanie kolejnego wolnego sygnaīu (bitu). W tym celu zamiast konkretnego numeru bitu na wejōciu podajemy po prostu -1. Na wyjōciu dostaniemy albo numer zaalokowanego sygnaīu, albo -1 w wypadku bīėdu. Numer sygnaīu jest zapisany w bajcie i bajt równieū wynosi dīugoōź pola mp_SigBit, gdzie wpisujemy nasz poprawnie zaalokowany sygnaī. Jeōli korzystamy z kilku portów, powtarzamy wyūej opisane czynnoōci. Jako parametr dla _LVOWait() podajemy nie numery bitów, ale dīugie sīowo, gdzie bity o zaalokowanych numerach musimy sami ustawiź. Dajmy na to, ūe zaalokowaliōmy sygnaīy $14, $15 i $16, musimy zatem ustawiź te bity w rejestrze d0 przed wywoīaniem _LVOWait(), czyli binarnie d0 wyglādaīoby nastėpujāco: d0 = %00000000.01110000.00000000.00000000. Teraz dopiero moūemy wywoīaź _LVOWait(). W wyniku dostajemy ustawienie bitów, które zostaīy zasygnalizowane (moūe siė zdarzyź, ūe pojawi siė kilka sygnaīów naraz; naleūy o tym pamiėtaź). Trzeba teraz sprawdziź, czy numer bitu z kaūdego portu jest ustawiony na wyjōciu i przedsiėwziāź odpowiedniā akcjė. exec.library/Wait ZARYS signals (D0.L) = Wait (signalSet)/(D0.L) -- czekaj na jeden lub wiėcej sygnaīów. FUNKCJA Ta funkcja spowoduje zawieszenie bieūācego zadania w oczekiwaniu na jeden lub wiėcej sygnaīów. Jeōli pojawi siė jeden lub wiėcej ze wyspecyfikowanych sygnaīów, zadanie powróci do stanu gotowoōci, te sygnaīy zaō zostanā wyczyszczone. Jeōli sygnaī pojawi siė, zanim zostanie wywoīana opisywana funkcja, to wywoīana wróci ona natychmiast, zwracajāc sygnaīy, które juū siė pojawiīy, zadanie zaō bėdzie kontynuowaīo pracė bez ūadnych opóśnieļ. UWAGA: Funkcji tej nie moūna wywoīywaź z przerwaļ lub w trybie nadzorcy! Oprócz tego _LVOWait() przerywa dziaīanie procedur _LVOForbid() czy _LVODisable(). WEJŌCIE signalSet -- Kombinacja sygnaīów, na które trzeba czekaź. Kaūdy bit reprezentuje poszczególny sygnaī. WYJŌCIE signals -- Kombinacja sygnaīów, które zostaīy uaktywnione. exec.library/AllocSignal ZARYS signalNum (D0.B) = AllocSignal (signalNum)/(D0.B) FUNKCJA Funkcja alokuje bit sygnaīowy z puli bitów sygnaīowych bieūācego zadania -- moūe to byź zarówno okreōlony numer, jak i nastėpny wolny bit. Sygnaī poīāczony w ten sposób z bitem bėdzie poprawnie zainicjalizowany (zgaszony). Dla kaūdego zadania dostėpnych jest 16 sygnaīów uūytkownika. Poza tym, zanim zadanie skoļczy swojā pracė, wszystkie zaalokowane sygnaīy powinny zostaź zwolnione. Jeōli sygnaī jest akurat w uūyciu lub jeōli nie ma juū wolnych sygnaīów, funkcja zwróci -1. Zaalokowane sygnaīy sā zdatne do uūycia tylko w tym zadaniu, które je zaalokowaīo. UWAGA: Sygnaīów nie moūna zwolniź z "exception handling code" (czyli z kodu, którego adres wpisany zostaī do struktury Task zamiast procedury standardowej do obsīugi stanów wyjātkowych -- przyp. tīum.) WEJŌCIE signalNum -- Ūādany numer sygnaīu (od 0 do 31) lub -1, jeōli wszystko jedno. WYJŌCIE signalNum -- Zaalokowany numer sygnaīu (0..31). Jeōli nie zostaīy wolne ūadne sygnaīy, to zostanie zwrócone -1. exec.library/FreeSignal ZARYS FreeSignal (signalNum)/(D0.B) FUNKCJA Ta funkcja zwalnia wczeōniej zaalokowany bit sygnaīowy do ponownego uūycia. Wywoīanie tej funkcji musi siė odbyź z tego samego zadania, które ten sygnaī zaalokowaīo. UWAGA: Sygnaīy nie mogā byź zwalniane bādś alokowane z kodu obsīugujācego stany wyjātkowe. NOTKA: Poczynajāc od V37, próba zwolnienia sygnaīu -1 jest nieszkodliwa. WEJŌCIE signalNum -- Numer sygnaīu do zwolnienia (0..31). Przykīad wykorzystania procedury _LVOWait() znajduje siė poniūej [plik Wait.s]. Ūeby wyjōź z programu, moūemy nacisnāź przy aktywnym okienku klawisz [Esc], gadūet WindowClose lub programem Put z pierwszego przykīadu wysīaź polecenie "quit" (maīe litery). Kaūdy inny wyraz zostanie zapisany (podobnie jak w przykīadzie WaitPort) do output (czyli najczėōciej na okno Shell). Dodatkowo naciskajāc klawisze kursora przy aktywnym oknie, moūemy nim przesuwaź. Poniewaū ten przykīad takūe zakīada port "Amsterdam", zatem nie da siė uruchomiź WaitPort, jak i Wait równoczeōnie. Choź przekracza to materiaī artykuīu, to jednak naleūy wspomnieź jeszcze o procedurach _LVOSetSignal() oraz _LVOSignal(), z których pierwsza pozwala na ustawianie lub odczytywanie sygnaīów tasku, druga na wysīanie do okreōlonego tasku sygnaīów (i przerwanie czekania _LVOWait(), jeōli sygnalizowany task akurat byī uōpiony). Opis jednakūe zarówno tych procedur, jak i systemu komunikacji za pomocā sygnaīów nie jest czėōciā tego artykuīu...