home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1997 #3 / amigamamagazinepolishissue03-1 / ma_1995 / 04 / ami036.txt < prev    next >
Text File  |  1997-04-06  |  14KB  |  423 lines

  1. /* wyraûenia zawarte w >...< proszë pogrubiê */
  2.  
  3. AMIGA E 8^)
  4.  
  5. <lead>Witajcie kursanty! Jak to czas leci! Juû od póî roku i dwóch
  6. miesiëcy tîuczemy sië z Amiga E... Jest kwiecieï, warto wyjôê na
  7. powietrze, pojeúdziê na rowierze, oderwaê sië od komputerów...
  8. Jeûeli jednak tego nie moûesz zrobiê, polecam pîytkë Sheryl Crow
  9. o tytule "Tuesday Night Music Club". Album jest bardzo dobry,
  10. znalazîem na nim co najmniej trzy utwory o poziomie
  11. przewyûszajâcym zjechany do biaîoôci hit "All I Wanna Do". Poza
  12. tym panienka jest nie tylko îadna, ale teû umie ôpiewaê...
  13.  
  14. <a>Rafaî Wiosna
  15.  
  16. <txt> Tak BTW*1: Naukowcy z Uniwersytetu Warszawskiego twierdzâ,
  17. ûe koderzy, którzy w przerwach w czasie programowania grajâ w grë
  18. Deluxe Galaga 2.5, sâ o 37% bardziej efektywni i robiâ o 17,3%
  19. mniej bîëdów. Zadziwiajâce, nieprawdaû?... 8^)
  20.  
  21. <sr>Ciâgi
  22.  
  23. <txt> Dziô zajmë sië gîëbiej typem danych, zwanych ciâgiem lub, z
  24. angielskiego, stringiem. Jest to jedna z najprostszych postaci
  25. tablicy, typu danych, którego omówienie zostawimy sobie na
  26. przyszîoôê. >Ciâg< w Amiga E to zestaw nastëpujâcych po sobie w
  27. pamiëci bajtów, bëdâcych najczëôciej normalnym napisem w kodzie
  28. ASCII, zakoïczony bajtem o wartoôci 0 ('\0'). Zmienna
  29. reprezentujâca ciâg ma wartoôê odpowiadajâcâ jego poîoûeniu
  30. (adresowi) w pamiëci.
  31.  
  32. Spójrz na poniûszy program:
  33.  
  34. <l>PROC main()
  35.  
  36.     DEF s:PTR TO CHAR, i
  37.  
  38.     s:='To jest ciâg znaków'
  39.  
  40.     FOR i:=0 TO 19
  41.  
  42.         WriteF('$\h : \z\d[3] "\c"\n',s+i,s[i],s[i])
  43.  
  44.     ENDFOR
  45.  
  46.     WriteF('\n')
  47.  
  48. ENDPROC
  49.  
  50. <txt>W drugiej linii definiowana jest zmienna s jako wskaúnik do
  51. znaku. W nastëpnej jest jej nadawana wartoôê, bëdâca adresem
  52. ciâgu znaków, zawartego miëdzy apostrofami. Teraz, korzystajâz z
  53. pëtli FOR...ENDFOR, poznanej miesiâc temu, moûesz wypisaê
  54. nastëpujâce wartoôci: adres w zapisie heksadecymalnym, wartoôê
  55. bajtu wskazywanego przez ten adres oraz znak kodu ASCII, który
  56. reprezentuje të wartoôê. Zainteresowanym mogë powiedzieê, ûe
  57. zapis s[i] oznacza indeks tablicy (tutaj, i jest mnoûone przez 1,
  58. czyli dîugoôê jednego jej elementu; gdybyômy zadeklarowali s jako
  59. PTR TO WORD, kaûde i zostaîoby przemnoûone przez dwa -- czyli
  60. dîugoôê typu WORD).
  61.  
  62. Amiga E wprowadza specjalny typ zmiennej: >E-ciâg< (E-string). Od
  63. normalnego ciâgu róûni sië wîaôciwie tylko nazwâ, gdyû E-ciâg
  64. moûe zastâpiê ciâg wszëdzie, gdzie zaûyczy sobie tego programista
  65. (w skîadni wywoîaï funkcji, które póúniej bëdâ zamieszczone,
  66. napis "ciâg" moûna dowolnie zamieniaê na "E-ciâg"). Nie moûna
  67. tego zrobiê odwrotnie -- w konstrukcjach, które wymagâjâ E-ciâgu,
  68. uûycie normalnego ciâgu jest niemoûliwe. Najwaûniejszâ róûnicâ
  69. jest fakt, ûe na E-ciâgu moûna wykonywaê dowolne operacje
  70. zmieniajâce jego dîugoôê, a na zwykîym ciâgu -- nie.  E-ciâg wie,
  71. jakie sâ jego granice, a dokîadniej, pamiëta o tym generowany
  72. przez kompilator kod.
  73.  
  74. E-ciâg definiowany jest jako typ STRING w nastëpujâcy sposób:
  75.  
  76. <l>PROC main()
  77.  
  78.     DEF s[20]:STRING, i
  79.  
  80.     StrCopy(s,'To jest ciâg znaków',ALL)
  81.  
  82.     FOR i:=0 TO 19
  83.  
  84.         WriteF('\h : \z\d[3] "\c"\n',s+i,s[i],s[i])
  85.  
  86.     ENDFOR
  87.  
  88.     WriteF('\n')
  89.  
  90. ENDPROC
  91.  
  92. <txt>Jak widzisz, definicja E-ciâgu wymusza na programiôcie
  93. okreôlenie jego maksymalnej dîugoôci. Spróbuj zmieniê liczbë 20
  94. na 10 i zobacz, co sië stanie...
  95.  
  96. Niektórzy AMOS-owcy mogâ zapytaê "co to za funkcja StrCopy() i
  97. dlaczego nie napisaîeô po prostu s:='To jest ciâg znaków'?" Jest
  98. to charakterystyczne pytanie dla programistów, majâcych stycznoôê
  99. z BASIC-em. Takie przyzwyczajenia mogâ prowadziê do powaûnych
  100. kîopotów z sercem oraz podwyûszonego poziomu agresji u
  101. programisty 8^). Otóû dlatego, ûe nie moûna PRZYPISYWAÊ CIÂGÓW do
  102. zmiennych zdefiniowanych jako E-ciâg. Zauwaû, ûe gdybym tak
  103. zrobiî, zmienna s wskazywaîaby wprawdzie na ciâg (a nie na
  104. E-ciâg), ale na tym by sië skoïczyîo -- ûadna operacja na
  105. zmiennej s, traktowanej jako wskaúnik do E-ciâgu, nie byîaby
  106. wykonalna, tzn. mogîaby prowadziê do zawieszenia sië komputera
  107. (ciâg nie ma jednej okreôlonej dîugoôci, której nie moûna
  108. zmieniaê; gdyby dodaê do niego drugi ciâg, okazaîoby sië, ûe po
  109. tej operacji zamazaîby kod znajdujâcy sië w pamiëci zaraz po
  110. nim). Funkcjë StrCopy() omówië juû zaraz.
  111.  
  112. Moûe juû zauwaûyîeô, ûe nastëpnâ róûnicâ miëdzy E-ciâgiem a
  113. normalnym ciâgiem jest fakt, ûe przy definicji E-ciâgu zmienna
  114. automatycznie przybiera odpowiedniâ wartoôê, czyli wskazuje na
  115. tenûe E-ciâg. Przy definicji ciâgu zmienna, aû do chwili
  116. przypisania jej tegoû ciâgu, ma wartoôê nieokreôlonâ. Z tym wiâûe
  117. sië teû to, ûe nowo zdefiniowany E-giâg jest pusty, natomiast
  118. nowo zdefiniowany ciâg jest NIEOKREÔLONY. Chcesz sprawdziê?
  119. Zmieï drugâ linië ostatniego programu na:
  120.  
  121. <l>DEF s:PTR TO CHAR, i
  122.  
  123. <txt>Twoje szczëôcie, ûe kompilator E nie ustawi tej zmiennej na
  124. 0, a gdzieô na obszar stosu, przeznaczonego dla tego programu,
  125. gdyû trzecia linia spowodowaîaby przepiëknâ wizytë naszego
  126. ulubionego Guru... (dla ambitnych: wyjaônijcie sobie dlaczego).
  127.  
  128. Amiga E ma kilka zdefiniowanych funkcji, przeznaczonych do
  129. manipulacji na E-ciâgach. Pierwszâ z nich jest funkcja
  130. >String(MAXSIZE)<. Rezerwuje ona pamiëê na E-ciâg o dîugoôci
  131. MAXSIZE. Wartoôciâ funkcji jest adres kawaîka pamiëci. Jest
  132. interesujâce, ûe korzystajâc z tej funkcji, moûna zmieniê
  133. nieokreôlonâ zmiennâ typu PTR TO CHAR (ciâg) na zainicjowanâ
  134. zmiennâ typu STRING (E-ciâg). Gdyby dodaê linië:
  135.  
  136. <l>s:=String(20)
  137.  
  138. <txt>do ostatniej wersji naszego przykîadowego programu (tej z
  139. DEF s:PTR TO CHAR _ORAZ_ StrCopy()), wpisujâc jâ miëdzy linie 2 i
  140. 3, okaûe sië, ûe zmienna s jest teraz wskaúnikiem do pustego
  141. E-ciâgu. Ûeby nie byîo nieporozumieï, zapis:
  142.  
  143. <l>DEF s[20]:STRING
  144.  
  145. <txt>moûna zastâpiê dwoma liniami:
  146.  
  147. <l>DEF s:PTR TO CHAR
  148.  
  149. s:=String(20)
  150.  
  151. <txt>Funkcja ta umoûliwia dynamicznâ alokacjë E-ciâgów, których
  152. dîugoôci jeszcze nie znamy.
  153.  
  154. Waûne jest to, ûeby sprawdziê, czy przypadkiem wynikiem funkcji
  155. nie bëdzie staîa NIL, która w Amiga E ma wartoôê 0. Jeûeli tak
  156. sië stanie, oznacza to fakt braku pamiëci na E-ciâg. Jeûeli to
  157. zignorujesz, moûesz sië spodziewaê szybkiego zawieszenia sië
  158. programu w sytuacji, gdy zabraknie miejsca. Moûna tego bardzo
  159. îatwo uniknâê, dodajâc linië:
  160.  
  161. <l>RAISE BLAD_BRAKPAMIECI IF String()=NIL
  162.  
  163. <txt>na poczâtku programu. (Pamiëtaj, ûeby zdefiniowaê ten numer
  164. bîëdu w poczâtkowych ENUM-ach oraz odpowiednio na niego reagowaê
  165. w handlerze bîëdów). Czyû obsîuga sytuacji podbramkowych w Amiga
  166. E nie jest piëkna?  Moûe nie, ale na pewno pozwala programiôcie
  167. skoncentrowaê sië na treôci, a nie na formie...
  168.  
  169. Funkcjë StrCopy() juû raz zastosowaîem. Ma ona nastëpujâcâ
  170. skîadnië >StrCopy(E-ciâg,ciâg,dîugoôê=ALL)<. Jej zadaniem jest
  171. skopiowanie ciâgu (lub E-ciâgu) do E-ciâgu. Funkcja zwraca
  172. wskaúnik do wyniku. Spójrz na przykîady jej uûycia:
  173.  
  174. <l>DEF s[20]:STRING
  175.  
  176. StrCopy(s,'To jest ciâg znaków',ALL)
  177.  
  178. StrCopy(s,'A to jest ciâg dluûszy niû 20 znaków',ALL)
  179.  
  180. StrCopy(s,'1234567890',7)
  181.  
  182. StrCopy(s,'',ALL)
  183.  
  184. <txt>Pierwszy przykîad skopiuje znaki z ciâgu, bëdâcego drugim
  185. parametrem funkcji, do E-ciâgu, bëdâcego jej pierwszym
  186. parametrem. Skopiowane zostanâ wszystkie znaki ciâgu, a to dziëki
  187. temu, ûe ostatni parametr ma umownâ wartoôê ALL.
  188.  
  189. W drugim przykîadzie, do E-ciâgu s zostanie skopiowanych tylko 20
  190. pierwszych znaków ciâgu "A to jest...". Dlaczego? Dlatego, ûe
  191. kompilator pamiëta o definicji E-ciâgu s, którego dîugoôê nie
  192. moûe przekraczaê 20 bajtów.
  193.  
  194. Trzeci przykîad skopiuje tylko 7 znaków z ciâgu, zawierajâcego
  195. cyferki. Jeûeli przykîady 2. i 3. zostanâ wykonane w programie
  196. po sobie, E-ciâg s po tym bëdzie miaî dîugoôê 7 znaków, a nie 20.
  197. StrCopy() kopiuje ciâgi, a nie wkleja ich!
  198.  
  199. Ostatni przykîad kopiuje ciâg pusty. W wyniku tego E-ciâg s
  200. bëdzie miaî dîugoôê 0.
  201.  
  202. Umowna dyrektywa ALL nie jest konieczna w Amiga E 3.x, które
  203. wprowadziîo parametry domyôlne. Dziëki temu jeûeli w funkcji
  204. StrCopy() sâ tylko dwa parametry, kompilator rozszerza jâ o
  205. trzeci, domyôlny, którego wartoôciâ jest ALL. Zaoszczëdza to
  206. pisania:
  207.  
  208. <l>StrCopy(s,'ciâg',ALL)
  209.  
  210. to to samo w Amiga E 3.x co
  211.  
  212. StrCopy(s,'ciâg')
  213.  
  214.  
  215. <txt>Modyfikacjâ StrCpy() jest funkcja
  216. >RightStr(E-ciâg1,E-ciâg2,dîugoôê), która kopiuje znaki z koïca
  217. drugiego E-ciâgu do pierwszego. Przykîad:
  218.  
  219. <l>DEF s[20]:STRING, t[20]:STRING
  220.  
  221. StrCopy(t,'1234567890')
  222.  
  223. RightStr(s,t,1)
  224.  
  225. WriteF('"\s"\n',s) -> "0"
  226.  
  227. RightStr(s,t,7)
  228.  
  229. WriteF('"\s"\n',s) -> "4567890"
  230.  
  231. <txt>Komentarze pokazujâ postaê E-ciâgu s po wykonaniu funkcji
  232. RightStr(). Nie moûna w niej uûyê dyrektywy ALL zamiast
  233. konkretnego okreôlenia dîugoôci kopiowanych znaków. Byîoby to bez
  234. sensu i stanowiîoby powielenie funkcji StrCopy().
  235.  
  236. Jeûeli juû mówimy o dîugoôci ciâgów, warto poznaê funkcjë
  237. pozwalajâcâ na jej zmierzenie(?). >StrLen(ciâg)< w wyniku zwraca
  238. dîugoôê ciâgu (lub E-ciâgu). W wypadku E-ciâgów lepiej uûyê
  239. funkcji >EstrLen(E-ciâg)<, która nie bëdzie poszukiwaîa bajtu o
  240. wartoôci 0 w podanym ciâgu, a zwróci jego dîugoôê natychmiast.
  241. Jest to nastëpna zaleta E-ciâgów.
  242.  
  243. Podobnâ funkcjâ jest >StrMax(E-ciâg)<, która to zwraca maksymalnâ
  244. dîugoôê E-ciâgu bëdâcego parametrem. W wiëkszoôci wypadków, jak
  245. moûna sië tego spodziewaê, prawdziwa dîugoôê nie bëdzie równa
  246. maksymalnej.
  247.  
  248. Bardzo przydatnâ funkcjâ jest >StrCmp(ciâg,ciâg,dîugoôê=ALL)<.
  249. Porównuje ona dwa (E-)ciâgi i zwraca wartoôê TRUE, jeûeli sâ one
  250. jednakowe, lub FALSE, jeûeli sâ róûne. Po co taka funkcja? Czy
  251. nie proôciej napisaê IF s=e THEN... Otóû znowu tak moûna
  252. zorganizowaê porównywanie ciâgów w BASIC-u, a nie w Amiga E. Tak
  253. sformuîowana instrukcja warunkowa porówna ADRESY (E-)ciâgów, a
  254. nie ich ZAWARTOÔC!
  255.  
  256. Najlepiej dziaîanie funkcji zobrazujâ przykîady. Wszystkie poniûsze
  257. wywoîania StrCmp() dajâ w wyniku wartoôê TRUE:
  258.  
  259. <l>StrCmp('ABC',  'ABC')
  260.  
  261. StrCmp('ABC',  'ABC', ALL)
  262.  
  263. StrCmp('ABCd', 'ABC',    3)
  264.  
  265. StrCmp('ABCde','ABCxxjs',3)
  266.  
  267. <txt>A teraz przykîady przy których StrCmp() zwraca FALSE:
  268.  
  269. <l>StrCmp('ABC',  'ABc')
  270.  
  271. StrCmp('ABC',  'ABc', ALL)
  272.  
  273. StrCmp('ABCd', 'ABC', ALL)
  274.  
  275. <txt>Kolejnâ funkcjâ jest >StrAdd(E-ciâg,ciâg,dîugoôê=ALL), która
  276. dodaje (E-)ciâg do E-ciâgu:
  277.  
  278. <l>DEF s[20]:STRING
  279.  
  280. StrCopy(s,'12345')
  281.  
  282. StrAdd(s,'67890',ALL)
  283.  
  284. WriteF('"\s"\n',s) -> "1234567890"
  285.  
  286. StrAdd(s,'ABDEF',3)
  287.  
  288. WriteF('"\s"\n',s) -> "1234567890ABD"
  289.  
  290. <txt>Bardziej zaawansowanâ funkcjâ operujâcâ na (E-)ciâgach jest
  291. >MidStr(E-ciâg,ciâg,pozycja,dîugoôê=ALL). Ma ona za zadanie
  292. skopiowanie znaków z (E-)ciâgu, bëdâcego drugim parametrem, do
  293. pierwszego parametru -- E-ciâgu. Skopiowane zostanie tyle znaków,
  294. ile okreôlimy w czwartym parametrze. Z (E-)ciâgu bedâcego drugim
  295. parametrem bëdâ one kopiowane od pozycji okreôlonej parametrem
  296. trzecim. Najlepiej pokaûâ to przykîady:
  297.  
  298. <l>DEF s[20]:STRING
  299.  
  300. MidStr(s,'1234567890',2,ALL)
  301.  
  302. WriteF('"\s"\n',s) -> "34567890"
  303.  
  304. MidStr(s,'1234567890',6,3)
  305.  
  306. WriteF('"\s"\n',s) -> "789"
  307.  
  308. <txt>Jak widaê, pozycje w ciâgu liczy sië od zera.
  309.  
  310. Bardzo przydatnâ funkcjâ jest >InStr(ciâg1,ciâg2,pozycja=0)<.
  311. Przeszukuje ona (E-)ciâg1 pierwszy i sprawdza, czy nie wystâpiî
  312. (E-)ciâg2. Poszukiwania moûna zaczâê od okreôlonej trzecim
  313. parametrem pozycji (E-)ciâgu1 (jeûeli tego parametru brak,
  314. szukanie zacznie sië od samego poczâtku). InStr() zwraca pozycjë,
  315. na której zostaî znaleziony (E-)ciâg2 (UWAGA! moûe to byê 0!) lub
  316. -1, jeûeli go nie znaleziono. Jak moûna sië domyôliê, przy
  317. poszukiwaniu rozróûniane sâ litery duûe i maîe (case sensitive).
  318.  
  319. A propos wielkoôci liter, to dwie funkcje, >LowerStr(ciâg)< i
  320. >UpperStr(ciâg)< zamieniâ twój (E-)ciâg odpowiednio na maîe i
  321. duûe literki.
  322.  
  323. Równieû przydatnâ funkcjâ jest >TrimStr(ciâg)<. Zwraca ona adres
  324. pierwszego "drukowalnego" znaku w (E-)ciâgu bëdâcym jej
  325. parametrem. Przeskakiwane sâ spacje i znaki poniûej tej funkcji w
  326. tabeli ASCII (czyli poniûej 32). Oto przykîad:
  327.  
  328. <l>DEF s[20]:STRING
  329.  
  330. StrCopy(s,'  \n \t  HEJ',ALL)
  331.  
  332. WriteF('"\s"\n',s) -> "{cr}      HEJ"
  333.  
  334. WriteF('"\s"\n',TrimStr(s)) -> "HEJ"
  335.  
  336. <txt>W miejscu oznaczonym {cr} kursor przesunie sië na poczâtek
  337. nastëpnej linii.
  338.  
  339. Mniej wykorzystywanâ funkcjâ jest >SetStr(E-giâg,dîugoôê)<.
  340. Zmienia ona dîugoôê E-ciâgu na okreôlonâ drugim parametrem. Oto
  341. przykîad uûycia:
  342.  
  343. <l>DEF s[10]:STRING
  344.  
  345. StrCopy(s,'1234567890')
  346.  
  347. WriteF('"\s"\n',s) -> "1234567890"
  348.  
  349. SetStr(s,5)
  350.  
  351. WriteF('"\s"\n',s) -> "12345"
  352.  
  353. SetStr(s,15)
  354.  
  355. WriteF('"\s"\n',s) -> "12345"
  356.  
  357. StrCopy(s,'12345678901234567890')
  358.  
  359. WriteF('"\s"\n',s) -> "1234567890"
  360.  
  361. <txt>Ostatniâ omówionâ przeze mnie funkcjâ, zwiâzanâ z
  362. (E-)ciâgami jest >Val(ciâg,adres=NIL)<. Ma ona za zadanie zamianë
  363. ciâgu znaków, w którym znajduje sië liczba na wartoôê typu LONG.
  364. Val() zignoruje wszystkie spacje na poczâtku, a znaki "%" i "$"
  365. przed liczbami zinterpretuje jako poczâtek zapisu dwójkowego lub
  366. heksadecymalnego. Funkcja zwraca wartoôê odczytanâ z (E-)ciâgu.
  367. Jeûeli drugi argument jest podany, to zmiennej WSKAZYWANEJ przez
  368. niego zostanie wpisana liczba znaków, które funkcja Val() musiaîa
  369. przejrzeê, aby obliczyê zwróconâ wartoôê.
  370.  
  371. Na pierwszy rzut oka wyglâda to na bardzo skomplikowane, ale w
  372. "praniu" okazuje sië, ûe nie taki diabeî straszny. Przeôledúmy
  373. przykîadowy kawaîek programu:
  374.  
  375. <l>DEF s[30]:STRING, value, chars, p:PTR TO CHAR
  376.  
  377. StrCopy(s,' \t \n 10 \t $3F -%0101010')
  378.  
  379. value:=Val('abcde 10 20',{chars}) -> ADRES zmiennej chars!
  380.  
  381. <txt>Po wykonaniu powyûszej linii zmienna "value", jak równieû
  382. zmienna chars, bëdzie miaîa wartoôê 0. Dlaczego? Poniewaû 'abcde'
  383. to nie jest liczba dla Val(). (Byîaby, gdyby dodaê na poczâtku
  384. "$"!).
  385.  
  386. <l>value:=Val(s, {chars})
  387.  
  388. <txt>Teraz "value" ma wartoôê 10, a chars -- 7 (liczba znaków
  389. przejrzanych przez funkcjë Val()).
  390.  
  391. <l>p:=s+chars
  392.  
  393. <txt>Teraz ciâg p wskazuje na siódmy znak E-ciâgu s.
  394.  
  395. <l>value:=Val(p,{chars})
  396.  
  397. <txt>Po tym "value" przybierze wartoôê 63 (czyli $3F), a zmienna
  398. chars -- 6 (przejrzano nastëpne 6 znaków).
  399.  
  400. <l>p:=p+chars
  401.  
  402. <txt>Teraz ciâg p wskazuje na spacjë zaraz po "$3F" w E-ciâgu s.
  403.  
  404. <l>value:=Val(p,{chars})
  405.  
  406. <txt>Teraz "value" ma wartoôê -42 (-%0101010), a chars -- 10.
  407.  
  408. Powyûszy przykîad bardzo îadnie ilustruje, gdzie naleûy uûyê
  409. ciâgu, a gdzie E-ciâgu.
  410.  
  411. <sr>*
  412.  
  413. Oops, to by byîo na tyle z (E-)ciâgami. Sâ jeszcze dwie funkcje z nimi
  414. zwiâzane (ReadStr() i StringF()), ale zajmë sië nimi dopiero przy omawianiu
  415. operacji wejôcia/wyjôcia. Na dziô to juû koniec. Przepraszam, ûe nie ma
  416. comiesiëcznego listingu do wklepania, ale ta czëôê kursu E tak sië
  417. rozrosîa, ûe nie ma na niego juû miejsca... BYE4NOW!
  418.  
  419. <przyp>*1 BTW -- By The Way, odpowiednik polskiego "swojâ drogâ".
  420.  
  421. Literatura: Jason R. Hulance, plik "Beginners.guide" rozprowadzany z Amiga
  422. E 3.x.
  423.