Język symboliczny (assembler) ASSM

Z MERA 400 wiki
Skocz do: nawigacja, szukaj

Pierwowzorem języka symbolicznego ASSM jest język symboliczny ASSK opracowany wiele lat temu dla maszyny K-202. Oba te języki mają podobną składnię, translatory obu są jednoprzebiegowe. Pierwotna wersja języka była wielokrotnie ulepszana i uzupełniana, jednak podstawowe reguły składni jak i zasadniczy szkielet translatora pozostały.

Zasadnicze właściwości języka

Program w języku ASSM składa się ze znaków repertuaru A.S.C.I.I. (ISO-7). W tekstach programów można używać dla poprawy ich czytelnosci znaków odstępu (spacji), tabulatora poziomego 'HT', zmiany wiersza 'LF', tabulatora pionowego 'VT', nowej strony 'FF' oraz powrotu karetki 'CR'. Znakami tymi mozna dowolnie rozdzielać elementy tworzonego programu, pamiętając jednak, że znak 'CR' jest przez ekstrakody rozpoznawany jako 'koniec wiersza' i powoduje przekazanie wiersza jako całości translatorowi. Ze względu na rozmiar bufora długość wiersza nie może przekraczać 150 znaków.

Oprogramowanie podstawowe systemu CROOK-4 jest tak zorganizowane, że nie ma w nim programu łącząco-ładującego (link-loader). Programy w języku symbolicznym (assemblerze) ASSM tłumaczone są w jednym przebiegu (przy jednokrotnym przejrzeniu kodu źródłowego) wprost na gotowy do wykonania binarny kod wynikowy na dysku. Wszystkie potrzebne procedury i programy biblioteczne są dołączane w postaci źrodłowej i rownież tłumaczone. Dzięki odpowiedniej konstrukcji translatora języka ASSM oraz innych programów i makrozleceń systemu cały ten proces jest bardzo sprawny i szybki. Jego wadą jest brak możliwości łatwego łączenia modułów wynikowych otrzymanych za pomocą translatora ASSM z modułami wynikowymi wyprodukowanymi przez kompilatory innych języków.

Translator ASSM jest zaopatrzony w mechanizmy umożliwiające translację warunkową, parametryzację programów oraz tworzenie programów nakładkowanych. Nie posiada natomiast wbudowanego makrogeneratora.

Zasady tworzenia programu źródłowego

Program źródłowy w języku ASSM zawiera właściwy program - odpowiednio zapisane rozkazy i inne elementy (dane, tablice), ktore mają po przetworzeniu przez translator utworzyć kod wynikowy; oraz dyrektywy, sterujące pracą translatora i nie produkujące bezpośrednio kodu wynikowego. Terminatorami elementów programu i dyrektyw są znaki: gwiazdka '*' (tylko dla dyrektyw bez parametrów), kropka '.', nawias zamykający ')' (dla rozkazów dwusłowowych) oraz dwukropek ':' (dla etykiet). Po terminatorze a przed pierwszym znakiem następnego elementu można umieszczać wspomniane wyżej znaki justujące dla uzyskania pożądanej postaci graficznej programu. Można również w tych miejscach umieszczać komentarze. Komentarze są dwóch rodzajów: zwykłe, zamknięte w nawiasy kwadratowe '[' i ']' oraz wyprowadzane, zamknięte w znaki '<' i '>'. Te ostatnie tym się różnią od zwykłych, że zostaną w czasie translacji wyprowadzone na strumień wyjściowy translatora.

Język ASSM pozwala na tworzenie struktury blokowej programów, co najwyżej trójpoziomowej. Służą do tego dyrektywy, mające charakter par nawiasów kontekstowych. Blok najwyższego poziomu jest zamknięty w nawiasy kontekstowe PROG* i FINPROG*, wewnątrz niego mogą znajdować się bloki zamknięte w nawiasy SEG* i FINSEG* oraz nawiasy MACRO* i FINMACRO*. Te ostatnie bloki mogą się również znajdować wewnątrz bloków ograniczonych nawiasami SEG* i FINSEG*.

Zmienne, stałe, wyrażenia

W programach można używać zmiennych, które są obiektami translatora, a nie programu wynikowego. Do programu wynikowego mogą one trafić wyłącznie drogą użycia w wyrażeniu, stanowiącym element programu.

Nazwa zmiennej jest identyfikatorem co najwyżej sześcioznakowym (dalsze są ignorowane) - ciągiem liter i cyfr, zaczynającym się literą. Nazwa zmiennej może się również zaczynać od jednego z trzech znaków: '#', '%' lub '$' - wówczas drugim znakiem może być cyfra, a nazwa może mieć od dwóch do siedmiu znaków. Zmienne tworzone przez programistę są dwóch rodzajów - zwykłe i adresowe. Wartość zmiennej zwykłej może być zmieniana, zmiennej adresowej - nie - próba powtórnej definicji takiej zmiennej jest błędem. Definicja lub nadanie nowej wartości zmiennej prostej ma postać:

<nazwa> = <wyrażenie określone>.

gdzie <wyrażenie określone> to wyrażenie, którego wszystkie składniki są w danym momencie translatorowi znane (ich definicje znajdowały się wcześniej w tekście źródłowym). Definicja zmiennej adresowej ma postać:

<nazwa>:

Skutkiem wystąpienia takiej definicji w tekście programu jest nadanie zmiennej <nazwa> wartości aktualnej zawartości licznika bieżącego adresu. Licznik ten jest zmienną wewnętrzną translatora. Jego nazwa - S - jest zastrzeżona i nie może być używana do innych celów. Zawartość licznika (wartość zmiennej S) jest po wyprodukowaniu przez translator elementu programu wynikowego zwiększana o długość tego elementu.

Stałe są to liczby 16-bitowe. Język umożliwia rozmaite sposo by zapisu stałych, co pozwala nieraz pokazać intencję użycia i przeznaczenie tych stałych. Liczby ujemne sa przedstawiane w notacji uzupełnienia dwójkowego - wynika stąd, że mogą przyjmować wartości od -32768 do +32767. Jeżeli liczba zaczyna się cyfrą '0' (zero), translator traktuje ją jako liczbę ósemkową (oktalną). Napis w postaci wykrzyknika '!', po którym następują dwa dowolne znaki oznacza słowo 16-bitowe utworzone z numerów porządkowych ISO-7 tych znaków - pierwszego na starszym byte'cie, drugiego na młodszym. Można tez uzyskać byte o wartości zero ('NUL'), pisząc na miejscu jednego ze znaków znak wykrzyknika '!'. Można także uzyskać słowo z trzech upakowanych znaków kodu R-40 (litery, cyfry i spacja), pisząc trzy takie znaki poprzedzone znakiem waluty '$'.

Jeszcze innym sposobem zapisu stałych są stałe bitowe. Stała taka składa się ze znaku zapytania '?', po którym następuje od jednego do szesnastu znaków spośród następujacych:

ZMVCLEGYX1234567

Znaki te odpowiadają oznaczeniom bitów od bitu 0 do bitu 15 rejestru R0. Utworzona przez translator stała ma jedynki na tych bitach, których nazwy występują po znaku zapytania, zaś zera na pozostałych bitach.

Translator ASSM dopuszcza jedynie wyrażenia o bardzo prostej postaci. Składnikami tych wyrażeń mogą być zmienne i stałe (zapisane w dowolnej podanej wyżej notacji), a jedynymi dopuszczalnymi operacjami są dodawanie i odejmowanie. Stosowanie nawiasów w wyrażeniach nie jest dozwolone. Każdy składnik wyrażenia może być ponadto skalowany. Polega to na tym, że po składniku znajduje się znak '/' a po nim bądz liczba całkowita dzięsiętna bez znaku z przedziału od 0 do 15, bądż zmienna (której wartośc musi być w momencie użycia określona) o wartości z tego przedziału. Skutek skalowania jest taki, że translator utworzy wartość, powstałą z pierwotnej wartości składnika przesuniętej w lewo o 15-n bitów, gdzie 'n' jest wartością skali.

Składnikiem wyrażeń może być również wartość licznika bieżącego adresu (zmienna wewnętrzna translatora S)

Umieszczanie słów w programie wynikowym

Chcąc utworzyć słowo (liczbę szesnastobitową) i umieścić je w programie wynikowym należy w programie żródłowym napisać odpowiednie wyrażenie w dowolnej dopuszczalnej przez translator postaci i zakończyć je kropką. Translator w czasie tłumaczenia programu obliczy wartość tego wyrażenia, utworzy z niego słowo szesnastobitowe, umieści je w programie wynikowym pod adresem zgodnym z aktualną wartością licznika bieżacego adresu, oraz zwiększy o 1 zawartość licznika bieżącego adresu (by przygotować go do umieszczenia w programie wynikowym następnego słowa). Jeżeli w chwili wykonywania wyżej opisanej operacji jakaś zmienna wchodząca w skład wyrażenia jest nie określona (nie została jej wcześniej w tekście źródłowym nadana wartość) - translator sobie ten fakt zaznaczy i póżniej to słowo odpowiednio uzupełni - tak, jakby wartość zmiennej była w momencie tłumaczenia znana. To ostatnie nie dotyczy skali - ta musi być w chwili tłumaczenia określona. Jeżeli w programie źródłowym wystąpi wyrażenie puste zakończone kropką (czyli sama kropka), translator przyjmie jako wartość wyrażenia zero i umieści w programie wynikowym zerowe słowo.

Poniżej podano przykłady kodowania wyrażeń zawierających stałe w różnych notacjach, tak, jak mogłoby to wyglądać w programie źródłowym. Wyrażenia znajdujące się pod sobą (w tych samych kolumnach) mają takie same wartości i zostaną przez translator zamienione na słowa o takiej samej zawartości - w pamięci translator umieści 32 słowa, cztery razy identyczny ciąg ośmiu słów:

$   .  !!1.    !1!. 0177400.   0377.  256.   32767.  -32768.
   0.   49.  !!1/7.    -1/7. -1+1/7.  1/7.  -1+1/0.     1/0.
   0.  061.   49/7.    -256.    255. 0400.  077777. 0100000.
    . ?237. 030400.  0377/7.    255.  4/9.  +32767.      ?Z.

I drugi przykład - zakodowano tu tablicę, której każda pozycja składa się z czterech słow - w pierwszych trzech jest nazwa liczby (uzupełniona na końcu byte'ami zerowymi), a w czwartym wartość liczby. Zmienna zwykła LENTAB będzie po przetłumaczeniu tego fragmentu programu miała wartość odpowiadającą długości (w słowach) tablicy; zmienna adresowa TABLE - adresowi po czątku tablicy. Zmiennych tych można oczywiście używać w innych miejscach programu. Należy zwrócić uwagę, że w przypadku umieszczenia w programie dodatkowych elementów tablicy zmienna LENTAB będzie miała odpowiednią (zwiększoną o długość wprowadzonych dodatkowych elementów) wartość, ponieważ translator obliczy ją właściwie w czasie tłumaczenia programu:

TABLE:  !ZE. !RO. 0. 0.
!ON. !E!. 0. 1.
!TW. !O!. 0. 2.
!TH. !RE. !E!. 3
!FO. !UR. 0. 4.
LENTAB = S-TABLE.

Ponieważ umieszczanie w programie wynikowym dłuższych tekstów za pomocą wyżej opisanej notacji z użyciem wykrzykników jest dość niepraktyczne i może być źródłem niepotrzebnych dodatkowych pomyłek, przewidziano specjalny sposób notacji dla tekstów. Jeżeli mianowicie translator napotka w programie źródłowym ciąg dowolnych (z wyjątkiem cudzysłowu) znaków ISO-7 zamknięty w znaki cudzysłowu '"', umieści te znaki w kolejnych byte'ach programu wynikowego (czyli po dwa w słowie - wcześniejszy znak na starszym, póżniejszy na młodszym byte'cie), dodając jeszcze "od siebie" dwa znaki - jeden na początku tekstu, a drugi na końcu. Po umieszczeniu tekstu translator zwiększy oczywiście również wartość zmiennej S o liczbę słów jaką ten tekst (łącznie ze znakami dodatkowymi) zajął. Znaki dodawane przez translator można ustalić za pomocą dyrektywy:

TEXT* <wyrażenie określone>.

Starszy (lewy) byte wartości wyrażenia, które jest argumentem dyrektywy TEXT* definiuje znak, dostawiany na początkach tekstów, młodszy (prawy) byte - znak, dostawiany na końcach tekstów. Jeżeli wartość starszego byte'u wynosi zero, to na początkach tekstów nie będą dostawiane żadne znaki. Jeżeli w programie nie wystąpiła dyrektywa TEXT*, translator zachowuje się tak, jakby wykonał taką dyrektywę z argumentem o wartości 128 (0200) - na początkach tekstów nie będzie dostawiał żadnych znaków, zaś na końcach będzie dostawiał byte o wartości 128 (0200).

Jest jeszcze jeden skrótowy zapis, pozwalający na inicjalizację większych obszarów pamięci programu wynikowego. Wystąpienie w programie żródłowym dyrektywy postaci:

RES* <wyrażenie określone 1>[,<wyrażenie określone 2>].

spowoduje umieszczenie w programie wynikowym liczby słów równej pierwszemu wyrażeniu (począwszy od aktualnej wartości licznika bieżącego adresu) o wartości równej drugiemu wyrażeniu. Jeżeli drugie wyrażenie w dyrektywie nie występuje, wtedy słowa te są zerowane. Po wykonaniu dyrektywy translator zwiększy zawartość licznika bieżącego adresu (zmiennej S) o długość zainicjalizowanego obszaru. Poniższy fragment programu żródłowego

BUF1:  S* S+20.
BUFTX: RES* 60, -1.
BUF2:  RES* 24.

spowoduje

  • "wypuszczenie" dwudziestu słów bez inicjalizacji;
  • umieszczenie w następnych 60 słowach liczby -1 (wszystkie 16 bitów równe jedności);
  • wyzerowanie następnych kolejnych 24 słów.

Poszczególne obszary zostały opatrzone etykietami, do których można się odwoływać w innych miejscach programu.

W powyższym przykładzie pokazano zastosowanie dyrektywy, zmieniającej zawartość zmiennej S - licznika bieżącego adresu, mającej postać:

S* <adres>.

Argumentem dyrektywy musi być wyrażenie określone. Wartośc tego wyrażenia zostanie dyrektywą nadana zmiennej S.

Kodowanie rozkazów

Dobrze jest, jeśli programista piszący program w języku ASSM zdaje sobie sprawę z tego, jak zbudowany jest rozkaz maszyny MERA-400. Przypomnimy zatem pokrótce znaczenie poszczególnych pól rozkazu, podając jednocześnie sposób ich kodowania przy pisaniu programów w języku ASSM.

  • Bity od 0 do 5 (sześć najbardziej znaczących bitów) są zajmowane przez kod operacyjny rozkazu. Przy kodowaniu rozkazów w języku ASSM używamy zamiast nich literowego skrótu mnemonicznego.
  • Bit 6 to bit D-modyfikacji. Jeżeli jest on równy jeden w wykonywanym własnie rozkazie, wtedy argument (po wszystkich innych modyfikacjach) nie jest jeszcze argumentem efektywnym, a dopiero jego adresem. Obecność tego bitu w rozkazie koduje się w ten sposób, że umieszcza się znak apostrofu tuż przed kropką lub nawiasem będącymi terminatorami rozkazu w języku ASSM.
  • Bity 7, 8,9 to pole 'A' rozkazu. Jego wartość (w czasie wykonywania rozkazu, i to dla rozkazów, które tak to pole interpretują) jest numerem rejestru, którego rozkaz dotyczy (będziemy ten rejestr oznaczali rA). Pole 'A' koduje się w języku ASSM w ten sposób, że umieszcza się cyfrę o wartości od 0 do 7 lub zmienną zwykłą (której wartość musi być określona) za kodem mnemonicznym rozkazu, oddzieloną od niego przecinkiem.
  • Bity 10, 11, 12 to pole 'B' rozkazu. Jego zawartość określa numer rejestru rB, którego zawartością zostanie zmodyfikowana w czasie wykonywania rozkazu wartość argumentu pierwotnego (po ewentualnej premodyfikacji). Jeśli wartość tego pola jest zerowa - rozkaz nie ma B-modyfikacji. Koduje się B-modyfikację w ten sposób, że zapisuje się numer rejestru rB - cyfrę od 1 do 7 albo zmienną zwykłą (której wartość musi zawierać się w podanym wyżej przedziale i musi być określona), poprzedzoną znakiem '&' tuż przed znakiem kropki lub nawiasu ')', kończącym rozkaz lub przed znakiem apostrofu oznaczającym D-modyfikację (jeśli takowa w rozkazie występuje).
  • Bity 13, 14 i 15 na koniec tworzą pole 'C' rozkazu, określające argument pierwotny rozkazu. Jeśli pole to jest równe zeru, wtedy argument pierwotny bezpośredni znajduje się w drugim słowie rozkazu (rozkaz ma długość dwóch słów). Koduje się argument pierwotny bezpośredni w ten sposób, że za zakodowanym numerem rejestru rA (albo za kodem mnemonicznym operacji, jeśli rozkaz nie ma pola 'A') umieszcza się nawias otwierający '(', a za nim wyrażenie (niekoniecznie określone), którego wartość będzie wartością argumentu pierwotnego rozkazu. Terminatorem rozkazu o argumencie bezpośrednim jest nawias zamykający ')'. Jeśli pole 'C' ma wartość od 1 do 7, wtedy określa ono numer rejestru rC, którego zawartość będzie w czasie wykonywania argumentem pierwotnym rozkazu. Koduje się to w ten sposób, że numer rejestru rC (cyfra od 1 do 7 lub zmienna zwykła, podobnie jak dla rejestrów rA i rB) zapisuje się za numerem rejestru rA albo za kodem mnemonicznym (jeśli pole 'A' nie występuje), oddzielając go od nich przecinkiem.

Zobaczmy teraz, jak te pozornie skomplikowane reguły wyglądają w praktyce. Poniżej podane są przykłady kodowania rozkazów o opisanej budowie w języku ASSM. Za każdym rozkazem podano objaśnienie - to mianowicie, co dany rozkaz zrobi podczas wykonywania programu wynikowego.

LW, 3,5.

umieszczenie zawartości rejestru R5 w rejestrze R3 (skopiowanie zawartości R5 do R3).

AW, 3,3&3.

Zawartość rejestru R3 zmodyfikuj zawartością rejestru R3 i utworzony w ten sposób argument efektywny dodaj do zawartości rejestru R3. Oznacza to po prostu pomnożenie zawartości R3 przez trzy (będzie ono poprawne, jeśli dodawanie uzyskane za pomocą B-modyfikacji nie dałoby nadmiaru, bo nadmiar przy modyfikacjach nie jest przez procesor maszyny uwzględniany).

LW, 4(-1200)
LW, 4,0. -1200.

Umieszczenie liczby -1200 w rejestrze R4 - oba powyższe wiersze dadzą taki sam kod wynikowy. Pokazano tu inny sposób zakodowania rozkazu z argumentem bezpośrednim, gdzie rozkaz dwusłowowy został zapisany w postaci dwóch oddzielnych, wyrażnie widocznych słów.

LW, 3(TABLE&5')

Wartość TABLE (adres) zostanie zmodyfikowana zawartością rejestru R5 i spod tak utworzonego adresu pobrane z pamięci słowo zostanie umieszczone w rejestrze R3.

F=7. RJ, F(PROC1)

Aktualna zawartość licznika rozkazów maszyny IC (czyli adres następnego za właśnie wykonywanym rozkazu, zostanie umieszczona w rejestrze R7, zaś w liczniku rozkazów IC zostanie umieszczona wartość (adres), jaką miała w wyniku translacji zmienna PROC1. Należy zwrócić uwagę, że jako numeru rejestru rA użyto tu zmiennej zwykłej translatora F. Takie kodowanie umożliwia parametryzację użytkowania rejestrów w programie i należy je zawsze stosować (chyba, że numer rejestru wynika bezpośrednio ze sposobu działania maszyny).

JG(LAB1-BEGN&E)

Jest to rozkaz warunkowy, który będzie efektywny (tzn. wykona się) jeżeli w momencie wykonywania bit 'G' rejestru R0 będzie równy 1. Wyrażenie LAB1-BEGN translator obliczy i umieści jako argument pierwotny bezpośredni w drugim słowie rozkazu. Zmienna E musi być w chwili translacji określona i mieć wartość dodatnią nie większą niż 7 (jest ona tu numerem rejestru B-modyfikacji rB). Jeżeli rozkaz będzie w czasie wykonywania efektywny, to argument pierwotny zostanie zmodyfikowany zawartością rejestru E i tak otrzymany adres maszyna umieści w liczniku rozkazów 'IC' - nastąpi skok do tego adresu. Ponieważ rozkaz ten dotyczy rejestru specjalnego - 'IC' - nie ma on pola 'A' (numeru rejestru uniwersalnego, którego operacja dotyczy), pole to jest użyte na rozszerzenie kodu operacji.

MD(24&6)

Rozkaz premodyfikacji. Argument pierwotny bezpośredni (liczba 24) zostanie zmodyfikowany zawartością rejestru R6 i otrzymany w ten sposób argument efektywny posłuży do premodyfikacji argumentu pierwotnego następnego rozkazu.

Wszystkie rozkazy w wyżej podanych przykładach miały argument pierwotny tworzony według takich samych zasad. Maszyna MERA-400 ma jeszcze rozkazy o innej budowie, w których niektóre pola rozkazu są interpretowane w inny niż to podano wyżej sposób (jeden taki przypadek już poznaliśmy - pole 'A' używane jako rozszerzenie kodu operacji).

W rozkazach z "krótkim" argumentem bezpośrednim pole 'D' (1 bit) przeznaczone jest na przechowanie znaku argumentu, zaś pola 'B' i 'C' łącznie (6 bitów) na wartośc (moduł) argumentu pierwotnego. Wynika stąd, że argument "krótki" może przyjmować wartości od -63 do +63. Ma to jeszcze inne konsekwencje - rozkazy z "krótkim" argumentem bezpośrednim nie mogą mieć B-modyfikacji ani D-modyfikacji. Koduje się argument "krótki" bezpośredni w ten sposób, że stałą lub zmienną oznaczającą argument pisze się za numerem rejestru lub za kodem operacji (jeśli rozkaz jest z tych, co nie używają rejestru rA), oddzieloną od niego przecinkiem. Rozkaz kończy jak zwykle kropka. Jeżeli argumentem rozkazu jest wyrażenie zawierające w chwili translacji elementy nie określone, wtedy pozostała (określona) część wyrażenia musi mieć taką wartość, by translator mógł ją pomieścić w polu przeznaczonym na "krótki" argument (tzn. nie mniejszą niż -63 i nie większą niż +63). Elementy wyrażenia tworzącego "krótki" argument nie mogą być skalowane. A oto przykłady.

CWT, 1,023.

Porównanie zawartości rejestru R1 z liczbą 023 (ósemkowo) i ustawienie zgodnie z wynikiem porownania bitów 'L', 'E' i 'G' rejestru R0. Zawartość rejestru R1 i innych bitów R0 pozostaje bez zmian.

AWT, 4,!!a-!!A.

Dodanie do zawartości rejestru R4 różnicy między kodami małej litery 'a' i dużej litery 'A' - czyli, jeśli R4 zawiera kod dowolnej dużej litery - rozkaz zamieni ten kod na kod odpowiadającej jej małej litery. Należy zwrócić uwagę, że zastosowany tu zapis wartości stałej ma tę zaletę, że wskazuje wyrażnie na sens argumentu rozkazu i polepsza w ten sposób czytelność programu (a można by to również zapisać po prostu w postaci liczby 32 lub 040).

Istnieje kilka rozkazów, w których dopiero co omówiony argument bezpośredni "krótki" jest traktowany jako adres względny (względem zawartości licznika rozkazów 'IC'). Translator ASSM pozwala na kodowanie argumentów takich rozkazów zarówno w postaci liczb (z zakresu -63 do +63), jak również w postaci wyrażeń, których wartościami są adresy. W tym drugim przypadku translator obliczy właściwie różnicę adresów i umieści ją w polu przeznaczonym na "krótki" argument.

UJS, 0.

Rozkaz "nic nie rób" - skok do następnego za tym właśnie rozkazu.

JVS, NADM2+2.

Jeżeli wskaźnik nadmiaru 'V' jest równy zero, rozkaz jest nieefektywny. Jeżeli natomiast wskaźnik 'V' będzie równy 1 (co oznacza, że w którejś z poprzednio wykonanych operacji arytmetycznych wystąpił nadmiar), wtedy nastąpi wyzerowanie bitu 'V' i skok do adresu o 2 słowa dalszego (większego) od komórki oznaczonej w programie żródłowym etykietą NADM2. Adres ten nie może być oczywiście oddalony od adresu następnego za omawianym rozkazem o więcej niż 63 słowa (błąd taki translator wykryje w czasie tłumaczenia programu).

Grupa rozkazów bez argumentu wykonuje operacje (m.in.logiczne, przesunięcia) na jednym z rejestrów uniwersalnych R1...R7 bądź na rejestrze R0. Koduje się te rozkazy w języku ASSM pisząc kod mnemoniczny rozkazu, a po nim, oddzielony przecinkiem numer rejestru rA i kończąc zapis rozkazu kropką. Rozkazy te nie mają B-modyfikacji ani D-modyfikacji, a odpowiednie pola, łącznie z polem argumentu pierwotnego 'C' są użyte na rozszerzenie kodu operacji. Oto przykłady.

SLZ, 2.

Przesunięcie zawartości rejestru R2 o 1 bit w lewo, z wprowadzeniem na wolne miejsce (bit 15) zera. Odpowiada to pomnożeniu zawartości R2 przez dwa.

RIC, 5.

Umieszczenie zawartości licznika rozkazów 'IC' (czyli adresu następnego rozkazu) w rejestrze R2.

ZLB, 7.

Wyzerowanie lewego (starszego) byte'u R7.

NGA, 2. NGC, 1.

Negacja arytmetyczna zawartości R2, a po niej negacja logiczna zawartości R1 z dodaniem przeniesienia. Odpowiada to negacji arytmetycznej (utworzeniu liczby przeciwnego znaku) liczby 32-bitowej ("długiej") znajdującej się w parze rejestrów R1, R2.

Rozkazy z argumentem byte'owym. Rozkazy te mają argument pierwotny byte'owy na ośmiu najmniej znaczących bitach słowa rozkazowego, lewy (bardziej znaczący) byte jest zajęty przez kod operacji.

EXL, END.

Wykonanie ekstrakodu END (zakończenie wykonywania programu). Nazwy ekstrakodów przechowywane są przez translator w oddzielnej części słownika - użycie takich samych nazw jako nazw zmiennych nie powoduje kolizji.

BLC,?CZ. AWT, 3,1.

Do zawartości R3 zostanie dodana jedność, jeśli w chwili wykonywania rozkazu 'BLC' bity 'C' i 'Z' rejestru R0 będą oba równe jedności. W przypadku tego jednego rozkazu 'BLC' jako wartość argumentu pierwotnego byte'owego translator bierze starszy byte 16-bitowego rozwinięcia wartości wyrażenia, będącego argumentem rozkazu 'BLC' w programie żródłowym.

Jest jeden rozkaz, który ma jeszcze inny dozwolony zakres wartości argumentu pierwotnego - od zera do 15. Jest to rozkaz przesunięcia cyklicznego zawartości rejestru. Tak więc

SHC, 3,8.

oznacza przesunięcie cykliczne (rotację) zawartości R3 o osiem miejsc w prawo. Przy translacji argumentu tego rozkazu translator bierze wartość wyrażenia mającego być argumentem modulo 16.

Ostatnia wreszcie grupa rozkazów to rozkazy, w których całe słowo rozkazowe jest kodem operacji. Rozkazy te koduje się, pisząc po kodzie mnemonicznym przecinek, cyfrę '0' (zero) i kropkę. Jedynym rozkazem tej grupy legalnym w trybie użytkowym pracy maszyny jest rozkaz

NLZ, 0.

dokonujący normalizacji liczby zmiennopozycyjnej zawartej w rejestrach R1, R2, R3. Pozostałe rozkazy tego typu są legalne wyłacznie przy pracy maszyny w trybie uprzywilejowanym (systemu operacyjnego).

Zasięg zmiennych -- słownik

Jak już to wcześniej mówiliśmy, translator przetwarza sekwencyjnie tekst źródłowy, produkując kod wynikowy umieszczany na dysku. Sposób postępowania translatora jest zdeterminowany przez tekst źródłowy oraz przez stan samego translatora, a szczególnie przez jego wydzieloną część zwaną słownikiem. Słownik translatora jest strukturą dynamiczną, w której przechowywane są nazwy i informacje o nich. Ze słownika w czasie tłumaczenia programu translator bardzo intensywnie korzysta. Dwie części słownika są częściami stałymi, nie zmieniającymi się w czasie procesu tłumaczenia. Są to słownik kodów mnemonicznych rozkazów oraz słownik ekstrakodów. Pozostała część słownika zawiera informacje o zdefiniowanych zmiennych - nazwy zmiennych i odpowiadające im wartości, jak również dane dodatkowe - rodzaj zmiennej i poziom kontekstowy na którym zmienna została zdefiniowana. Oprócz tego przechowywane są w tej części słownika dane o niezrealizowanych odwołaniach - miejscach programu wynikowego, w których translator pozostawił nie uzupełnione słowa lub pola, gdyż nie znał w chwili tłumaczenia wartości potrzebnych zmiennych. Realizacja odwołań odbywa się następująco. Jeśli w chwili tłumaczenia fragmentu programu żródłowego zawierającego odwołanie do zmiennej (wystąpienie zmiennej w wyrażeniu) zmienna ta jest zdefiniowana na aktualnym poziomie kontekstowym, wtedy translator używa znalezionej w słowniku wartości zmiennej w miejsce tej zmiennej. Jeżeli zmienna nie jest zdefiniowana - translator tworzy pozycję słownika zawierającą nie zrealizowane odwołanie. Jednocześnie obliczana jest pozostała (określona) część wyrażenia zawierającego niezdefiniowaną zmienną (bądź niezdefiniowane zmienne) i umieszczana w polu przeznaczonym na pełną wartość wyrażenia. Jeśli rozmiar pola nie pozwala na pomieszczenie tej wartości - translator sygnalizuje bląd. Przy napotkaniu w programie żródłowym nawiasu zamykającego kontekst (FINMACRO*, FINSEG* lub FINPROG*) translator realizuje wszystkie niezrealizowane odwołania tego kontekstu, dla których zmienne są w tym momencie na danym poziomie kontekstowym zdefiniowane. Następnie przenosi pozostałe niezrealizowane odwołania tego kontekstu na wyższy poziom, w którym zamykany właśnie poziom był zanurzony (chyba,że zamykany jest poziom PROG - wtedy pozostałe nie zrealizowane odwołania są błędami), po czem usuwa ze słownika wszystkie zmienne zamykanego poziomu. Dzięki temu mechanizmowi można do każdego programu dołączyć dowolny inny fragment programu (podprogram, procedurę) i nie obawiać się kolizji nazw z pozostałą częścią programu - pod warunkiem zamknięcia dołączanego fragmentu w nawiasy kontekstowe.

Istnieje możliwość "promowania" zmiennej - poprzedzenie definicji zmiennej dyrektywą ALL* spowoduje, że będzie ona znana w całym programie, tak jakby była zdefiniowana na poziomie PROG.

Warto jeszcze wiedzieć, że dyrektywa PROG* inicjalizuje translator - usuwa ze słownika wszystkie definicje zmiennych, zeruje wskaźnik wystąpienia błedu i t.p. Nie jest natomiast zmieniana wartość licznika bieżącego adresu 'S'.

Zbiór programu wynikowego

W programie żródłowym, na jego początku (a dokładniej, przed jakimkolwiek napisem produkującym kod wynikowy), musi znajdować się dyrektywa określająca zbiór wynikowy. Postać tej dyrektywy jest następująca.

FILE*<tytuł zbioru>[,<typ>[,<adres>[,<atrybut>]]].

Pierwszy argument tej dyrektywy (tytuł zbioru) jest obowiązkowy. W zbiorze o tym tytule utworzonym lub dołączonym w wyniku wykonania dyrektywy translator będzie umieszczał kod programu wynikowego.

Pozostałe trzy argumenty muszą być wyrażeniami określonymi, można je opuszczać "od końca".

Jeżeli opuścimy argument <typ>, translator przyjmie jego wartość jako równą zeru. Zbiorowi programu wynikowego zostanie nadany typ określony wartością tego argumentu.

Argument <adres> będzie adresem bazowym programu wynikowego - żadne słowo programu wynikowego nie może być umieszczone pod adresem mniejszym od adresu bazowego. Jeżeli nie podamy tego argumentu - translator przyjmie wartość adresu bazowego równą zeru.

Czwarty argument omawianej dyrektywy to atrybut - jeśli go nie podamy, translator przyjmie wartość 050.

Dyrektywy wejścia/wyjścia translatora

Po uruchomieniu translatora rozpoczyna on pobieranie informacji z końcówki użytkownika. Pozwala to np. wprowadzić z końcówki dyrektywę FILE*. Program jednakże mamy zazwyczaj przygotowany w zbiorze (choćby po to, by móc go poprawiać i modyfikować edytorem). Do przełączenia strumienia wejściowego translatora używamy dyrektywy o postaci

INT* <tytuł zbioru>.

Skutek wykonania tej dytektywy jest taki, jak byśmy wstawili treść podanego zbioru w jej miejsce. Wynika z tego, że dyrektywy INT* mogą być zagnieżdżane - w zbiorze, włączonym dyrektywą INT* mogą się znajdować inne dyrektywy INT*, właczające w tekst żródłowy treść innych zbiorów. Napotkanie jednakże w tekście żródłowym napisu FINPROG* powoduje zawsze przełączenie (powrót) strumienia wejściowego translatora na końcówkę oraz, jeśli w tłumaczonym programie nie było błędów - zakończenie pracy translatora i przejście do wykonywania zleceń języka operatora.

W czasie tłumaczenia programu translator wyprowadza informację pozwalającą zorientować się w przebiegu tłumaczenia, jak również uzyskać listę wartości, nadanych w czasie tłumaczenia zmiennym. Zbiór, do którego translator będzie zapisywał to wszystko (jak również komunikaty o błędach, które znalazł w programie żródłowym) można określić dyrektywą OUT* <tytuł zbioru>.

Format natomiast i zakres wyprowadzanej informacji określa dyrektywa

LAB* <wyrażenie określone>.

Jeżeli wartość argumentu tej dyrektywy jest większa od zera, wtedy definiuje ona liczbę kolumn produkowanego przez translator wydruku, a wyprowadzane są:

  • komentarze ujęte w nawiasy kątowe;
  • napotkane w tekście żródłowym nawiasy kontekstowe, z podaniem zawartości jaką miał licznik bieżącego adresu w chwili ich napotkania (daje to odwzorowanie struktury blokowej programu);
  • nazwy zmiennych i odpowiadające im wartości, w chwili ich definicji, a dla zmiennych zwykłych również w chwilach ewentualnych póżniejszych zmian ich wartości. Zmienne przeniesione do najbardziej zewnętrznego kontekstu PROG prefiksem ALL* są oznaczane na wydruku gwiazdką '*'. Zmienne adresowe są oddzielone od wartości znakiem dwukropka ':', zaś zmienne zwykłe znakiem równości '=';
  • błędy stwierdzone przez translator w czasie tłumaczenia - podawany jest numer błedu i adres wystąpienia (wartość licznika bieżacego adresu), a niekiedy dwa adresy, mianowicie wartość licznika bieżącego adresu w momencie stwierdzenia błędu oraz adres słowa o niewłaściwej wskutek wystąpienia błędu zawartości.

Jeżeli wartość argumentu wynosi zero, wtedy translator wyprowadza tylko informację o wystąpieniu nawiasów kontekstowych i o błędach oraz komentarze wyprowadzane.

Napotkanie przez translator w tekście żródłowym dyrektywy

NLAB*

powoduje, że translator będzie wyprowadzał jedynie komentarze wyprowadzane, błędy oraz informacje wynikające z przetworzenia dyrektyw PROG* i FINPROG*.

Warunkowe przetwarzanie tekstu źródłowego

Język ASSM rozpoznaje cztery dyrektywy, które pozwalają na pomijanie lub przetwarzanie fragmentów tekstu żródłowego zależnie od statusu zmiennej w słowniku. Dyrektywa

IF UNK* <nazwa>.

jest ignorowana, jeśli w chwili jej napotkania zmienna, której nazwa jest argumentem dyrektywy nie występuje w słowniku, ani w postaci definicji, ani też też nie zrealizowanego odwołania. Jeżeli natomiast zmienna ta w słowniku występuje, wtedy tekst programu będzie ignorowany aż do napotkania w nim dyrektywy

FI*

mającej charakter nawiasu zamykającego.

Dyrektywa o postaci

IF UND* <nazwa>.

zostanie zignorowana, jeśli zmienna której nazwa jest argumentem dyrektywy występuje w słowniku, lecz tylko w nie zrealizowanych odwołaniach (tzn. nie ma jeszcze nadanej wartości). Jeżeli zmienna ta jest zdefiniowana, wtedy począwszy od tej dyrektywy, tekst żródłowy będzie ignorowany aż do napotkania nawiasu zamykającego 'FI*'.

Napotkanie w tekście żródłowym dyrektywy

IF DEF* <nazwa>.

spowoduje ignorowanie tekstu żródłowego aż do napotkania napisu 'FI*', jeśli zmienna, której nazwa jest argumentem dyrektywy nie będzie miała wartości. Jeśli zmienna ta będzie zdefiniowana w chwili przetwarzania dyrektywy, dyrektywa zostanie zignorowana.

Napis 'FI*' ma w tekście żródłowym znaczenie tylko wtedy, gdy tekst żródłowy jest właśnie ignorowany w wyniku wystąpienia wcześniej jednej z dyrektyw 'IF'. W przeciwnym przypadku napis ten jest przez translator ignorowany.

Mechanizmy dla programów nakładkowanych

W języku ASSM można pisać programy nakładkowane, pozwalające na wykonanie dużych programów, których w inny sposób nie dałoby się pomieścić w pamięci operacyjnej.

W translatorze oprócz zmiennej S istnieje jeszcze druga zmienna zastrzeżona o podobnym charakterze. Jest to zmienna o nazwie #S, której wartością jest licznik bieżacego adresu dyskowego - adres dyskowy, pod którym translator umieszcza aktualnie program wynikowy. Adresy dyskowe potrzebne są do kodowania rozkazów ekstrakodu, które będą w czasie wykonywania programu ściągać nakładki z dysku.

Początek nakładki oznaczamy w programie żródłowym dyrektywą

SS* <adres>.

Argument tej dyrektywy musi być wyrażeniem określonym, zostanie on w wyniku wykonania dyrektywy umieszczony w liczniku bieżącego adresu (zmiennej S). Jednocześnie następne słowo programu wynikowego znajdzie się na początku nowego (następnego) sektora na dysku. Przy wykonywaniu tej dyrektywy licznik bieżącego adresu S musi zawierać adres słowa, znajdującego się bezpośrednio za ostatnim słowem poprzedniej utworzonej przez translator nakładki. Wystąpienie w programie dyrektywy SS* stanowi również dla translatora informację, że tłumaczony przezeń program jest programem nakładkowanym.

Do uzyskania największego adresu, potrzebnego przy ustalaniu granic poziomów nakładkowania, służy dyrektywa

MAX* <zmienna>,<adres> [{,<adres>}...].

Zmienna (pierwszy argument) musi być zmienną zwykłą określoną, zaś pozostałe argumenty wyrażeniami określonymi. W wyniku wykonania takiej dyrektywy, zmiennej (pierwszemu argumentowi) zostanie nadana wartość największego spośród wszystkich argumentów (z pierwszym włącznie).

Do ustalenia długości pierwszej ściaganej w celu wykonania programu nakładki lub części rezydującej programu służy dyrektywa

LEN* <długość>.

której argument musi być wyrażeniem określonym i oznacza długość, w słowach, pierwszej nakładki lub części rezydującej programu. W programie nakładkowanym dyrektywa LEN* musi wystąpić, w przeciwnym wypadku utworzony przez translator na dysku program wynikowy nie będzie poprawny.

Inne dyrektywy translatora

Wielkość pamięci operacyjnej dostępnej programowi w czasie wykonywania jest zdeterminowana informacją, zapisaną na dysku przez translator w czasie tłumaczenia programu, a wynikającą z największego adresu, pod jakim translator umieścił jakiś element tłumaczonego programu. Czasem jednak program potrzebuje większych obszarów roboczych - większej przestrzeni adresowej niż by to wynikało z wielkości samego programu. Efekt taki można uzyskać za pomocą dyrektywy

MEM* <adres>.

Argument tej dyrektywy, który musi być wyrażeniem określonym, ma znaczenie największego adresu, jaki będzie dostępny programowi wynikowemu w czasie wykonywania.

Dyrektywa o postaci

OS*

powoduje zakończenie pracy translatora (bez zmiany jego stanu) i przejście do wykonywania zleceń języka interpretacji zleceń OSL (pod kontrolę systemu operacyjnego).

Uruchomienie translatora

W celu uruchomienia translatora ASSM należy w kontekście języka operatora systemu CROOK-4 wprowadzić zlecenie postaci

ASSM <spacja lub przecinek> <tekst>

gdzie <tekst> jest dowolnym napisem akceptowanym przez translator języka ASSM. Tekst ten zostanie przez język operatora przekazany translatorowi do interpretacji. Zazwyczaj będzie to dyrektywa FILE* lub dyrektywa INT*, bądź też obie te dyrektywy.

Dodatek 1 - Lista błędów translatora ASSM

Błąd Opis
01 niedozwolony (nie rozpoznawany przez translator) znak
02 błędny znak kończący nazwę lub stałą
03 brak argumentu po znaku ',', '(', apostrofie lub '/'
04 błędny znak w liczbie
05 błędny znak oznaczający bit R0 (po znaku '?')
06 znakiem po '@', '%' lub '#' nie jest litera ni cyfra
07 niedozwolony znak w tytule zbioru
010 argument deklaracji LAB* spoza zakresu (0...7)
011 wyrażenie nieokreślone w miejscu określonego
012 skalowany składnik w wyrażeniu tworzącym "krótki" argument lub numer rejestru
013 błąd w kodzie mnemonicznym rozkazu
014 błąd składni rozkazu
015 błąd składni argumentu bezpośredniego 16-bitowego
016 zły znak kończący wyrażenie określone
017 nieprawidłowa deklaracja zmiennej zwykłej
020 powtórna deklaracja zmiennej adresowej
021 nieznana dyrektywa
022 zła deklaracja kontekstu (np. SEG* na poziomie MACRO)
023 zerowa lub ujemna liczba słów w dyrektywie RES*
025 nadmiar bloku COMMON w FORTRANie
026 zbyt duża liczba nakładek
027 zły bit argumentu BLC lub BRC albo nadmiar argumentu EXL
030 nadmiar arytmetyczny
031 numer rejestru spoza zakresu lub nie zdefiniowany
032 nadmiar argumentu bezpośredniego "krótkiego" (7-bitowego)
033 nadmiar przy obliczaniu wyrażenia
034 przejście adresu od 0177777 do 0 (lub przepełnienie słownika przy wołaniu przez kompilator języka 'C')
040 skala nie określona
041 skala spoza zakresu (0...15)

Dodatek 2 - Zestawienie dyrektyw translatora ASSM

FILE* <tyt. zbioru> [,<typ> [,<adr. bazowy> [,<atrybut>]]].

Ustalenie tytułu (i typu) zbioru programu wynikowego oraz jego adresu bazowego (najniższego adresu przestrzeni adresowej programu) i atrybutu. Typ podaje się w postacitrzech znaków kodu R-40, poprzedzonych znakiem '$'. Wartości przyjmowane przez zaniedbanie: typ 0 (pusty), adres bazowy 0, atrybut 050.

INT* <tytuł zbioru>.

Ustalenie lub przełączenie (z pozostawieniem poprzedniego otwartym) strumienia, z którego translator pobiera tekst źródłowy. Przy uruchomieniu - końcówka.

OUT* <tytuł zbioru>.

Ustalenie strumienia (zbioru) na który translator będzie wyprowadzał informację o przebiegu translacji.

LAB* <liczba kolumn>.

Polecenie wyprowadzania informacji o przebiegu translacji z wyprowadzaniem etykiet w podanej liczbie kolumn. Wydanie dyrektywy z argumentem zerowym powoduje wstrzymanie wyprowadzania etykiet.

NLAB*

Polecenie wstrzymania wydruku etykiet. Wyprowadzane są tylko komentarze ujęte w nawiasy '<' i '>' i błędy.

S* <adres>.

Nadanie nowej wartości licznikowi bieżącego adresu umieszczania.

PROG*

Inicjalizacja słownika translatora i otwarcie zewnętrznego nawiasu kontekstowego programu.

SEG*, MACRO*

Nawiasy otwierające konteksty wewnętrzne programu. Najbardziej zanurzony (najbardziej wewnętrzny) może być kontekst MACRO.

FINSEG*, FINMACRO*

Nawiasy zamykające konteksty wewnętrzne.

FINPROG*

Nawias zamykający kontekst programu, stanowiący dla translatora informację o kompletności programu.

RES* <liczba słów> [,<wyrażenie określone>].

Polecenie utworzenia obszaru o podanej liczbie słów, zainicjalizowanych podaną wartością. Nie podanie drugiego argumentu powoduje wyzerowanie tego obszaru.

TEXT* <wyrażenie określone>.

Ustalenie znaków dodawanych przez translator na początku (starszy byte wyrażenia) i na końcu (młodszy byte) tekstów ujętych w znaki cudzysłowu. Jeśli starszy byte wyrażenia jest zerowy - translator nie będzie dodawał znaku na początku tekstów. Wartość przyjmowana przez zaniedbanie: 0200 (128).

MEM* <adres>.

Ustalenie największego adresu dostępnego programowi (obszaru danych za kodem programu) (inaczej - nadanie wartości parametrowi MEM zbioru).

IF UNK* <nazwa>.

Polecenie ignorowania tekstu żródłowego, jeśli <nazwa> jest translatorowi znana (nawet jeśli nie ma jeszcze nadanej wartości).

IF UND* <nazwa>.

Polecenie ignorowania tekstu żródlowego, jeśli <nazwa> jest translatorowi znana i ma nadaną wartość.

IF DEF* <nazwa>.

Polecenie ignorowania tekstu źródlowego, jeśli <nazwa> nie jest translatorowi znana, albo, jeśli jest znana - nie ma jeszcze nadanej wartości.

FI*

Polecenie wznowienia przetwarzania tekstu żródłowego (jeśli jest właśnie ignorowany dzięki którejś z dyrektyw IF).

SS* <adres>.

Ustalenie początku nakładki (z równoczesnym nadaniem wartości licznikowi bieżącego adresu umieszczania).

MAX* <zmienna zwykła>, <wyr. określ.> [{,<wyr. określ.>}...].

Nadanie zmiennej zwykłej wartości największego z argumentów (z poprzednią wartością zmiennej włącznie).

LEN* <długość nakładki w słowach>.

Podanie translatorowi długości części rezydującej programu lub pierwszej nakładki.

OS*

Zakończenie pracy translatora i przejście do interpretacji zleceń języka OSL.

Uruchomienie translatora ASSM. Należy w kontekście interpretacji zleceń OSL wydać zlecenie:

ASSM [,<tekst, który zostanie przekazany translatorowi>]

Dodatek 3 - Pola parametrów ekstrakodów

Wiele ekstrakodów wymaga przesłania im z programu wołającego zespołu parametrów. Dokonuje się tego w taki sposób, że przed wywołaniem ekstrakodu umieszcza się w rejestrze r4 adres tablicy, zawierającej potrzebne parametry w odpowiedniej liczbie i kolejności (samą tablicę parametrów trzeba również, oczywiście, wypełnić bądż zaktualizować przed wywołaniem ekstrakodu). Niektóre ekstrakody umieszczają w odpowiednich polach takiej tablicy informacje zwrotne dla programu wołającego. Poniższe zestawienie podaje formaty takich tablic używanych przez różne grupy ekstrakodów. Podano znaczenie poszczególnych słów (pól) tablic. Numery paragrafów i rozdziałów stanowią odsyłacze do odpowiednich miejsc opisu systemu CROOK-4, gdzie znajdują się nieco bardziej szczegółowe informacje.

TIM: pole daty (czasu) (par. III.1.7.3)

  • 0 - rok (godzina)
  • 1 - miesiąc (minuta)
  • 2 - dzień (sekunda)

PROC: pole stanu procesu (par. III.1.7.1.)

  • 0 - słowo błędów, jeżeli początkowa zawartość (przy wywołaniu) jest różna od zera, to wystąpienie błędu spowoduje alarm, w przeciwnym wypadku program po wystąpieniu błedu będzie wykonywał się dalej, a w słowo błędów wpisany zostanie numer alarmu (rozdz. I.5);
  • 1 - identyfikator procesu potomnego - lokalny dla procesu wywołującego ekstrakod;
  • 2 - zawartość licznika rozkazów IC procesu potomnego;
  • 3 - zawartość rejestru R0 procesu potomnego;
  • 4 - przy ekstrakodzie DEFP priorytet definiowanego procesu, względem procesu wykonującego ekstrakod, przy pobieraniu rejestrów - zawartość słowa stanu procesu, przy ładowaniu rejestrów - ignorowane;
  • 5-11 - zawartość rejestrów R1-R7 procesu potomnego.

PINF: opis programu i procesu (par. III.1.7.5.)

  • 0 - numer generacji systemu;
  • 1 - starszy byte - rozmiar dostępnej pamięci operacyjnej (po 4K), młodszy - kod uprawnień użytkownika;
  • 2 - priorytet procesu;
  • 3 - długość zbioru specjalnego (w sektorach);
  • 4 - adres startowy (adres ładowania);
  • 5 - wielkość zajmowanej pamięci operacyjnej;
  • 6 - kod uprawnień i atrybuty procesu;
  • 7 - nazwa obszaru;
  • 8-9 - nazwa użytkownika;
  • 10-11- nazwa programu (procesu).

ERR: opis własnego alarmu (par. III.1.7.3.)

  • 0 - adres procedury obsługi alarmów;
  • 1 - adres wystąpienia alarmu;
  • 2 - numer alarmu (młodszy byte).

STR: opis strumienia

  • 0 - słowo błędów;
  • 1 - identyfikator strumienia.

DIR: opis użytkownika (par. III.3.3.2)

  • 0 - liczba obszarów dyskowych; dalsze słowa: nieparzyste - systemowy kod użytkownika, parzyste - systemowy kod aktualnego skorowidza.

FIL: opis zbioru i strumienia (par. III.3.2.1)

  • 0 - słowo błędów, jeżeli początkowa zawartość (przy wywołaniu) jest różna od zera, to wystąpienie błędu spowoduje alarm, w przeciwnym wypadku program po wystąpieniu błedu będzie wykonywał się dalej, a w słowo błędów wpisany zostanie numer alarmu (rozdz. I.5);
  • 1 - identyfikator strumienia (liczba 16-bitowa);
  • 2 - typ zbioru;
  • 3 - długość zbioru w słowach;
  • 4 - parametr 1;
  • 5 - parametr 2;
  • 6 - bity 0-5 zawierają informację o zezwoleniach do pracy z danym zbiorem dla poszczególnych użytkowników, bity 6-11 zawierają atrybuty, bity 12^:-15 zawierają parametr MEM;
  • 7-11 - tytuł obiektu.

Tytuły obiektów:

  • 0 STR (w kodzie R40);
    • 1, 2 nie używane;
    • 3 identyfikator strumienia;
    • 4 nie używane.
  • 0 numer kolejny lub nazwa obszaru dyskowego;
    • 1 dyskowy adres początku podobszaru;
    • 2 dyskowy adres końca (wyłącznie);
    • 3 0;
    • 4 nie używane.
  • 0 nazwa obszaru dyskowego;
    • 1, 2 nazwa skorowidza;
    • 3, 4 nazwa zbioru.
  • 0 DEV;
    • 1, 2 nie używane;
    • 3, 4 nazwa urządzenia; lub 3: numer kolejny urządzenia; 4: 0.
  • 0 MES;
    • 1 adres początku komunikatu (lub treść komunikatu);
    • 2 długość komunikatu w słowach (lub 0);
    • 3 identyfikator procesu odbierającego (lub -1 gdy adresatem jest przodek procesu nadającego);
    • 4 słowo nie używane.
  • 0 RAM;
    • 1, 2 nie używane;
    • 3, 4 nazwa zbioru.

MET: opis metryki dysku (par. III.3.3.1)

  • 0 - numer lub nazwa talerza dyskowego;
  • 1 - dyskowy adres początku zbioru DICDIC (adres bazowy);
  • 2 - dyskowy adres początku zbioru FILDIC;
  • 3 - dyskowy adres początku zbioru MAPA;
  • 4 - długość obszaru dyskowego (w sektorach).

REC: opis transmisji znakowej (par. III.4.1)

  • 0 - wskaźnik znaku w buforze, określający przy wejściu do ekstrakodu numer znaku w buforze od którego rozpocznie się transmisja, zwiększany o jeden po przetransmitowaniu każdego znaku;
  • 1 - identyfikator strumienia przywiązanego uprzednio odpowiednim ekstrakodem do urządzenia lub zbioru dyskowego;
  • 2 - adres bufora w pamięci operacyjnej do/z którego transmitowane są znaki (upakowane po dwa w słowo);
  • 3 - słowo, którego starszy byte zawiera kod znaku kończącego transmisję, a młodszy maksymalną liczbę znaków, która może być transmitowana (zerowa wartość młodszego byte'u oznacza transmisję bez ograniczenia długości);
  • 4 - słowo występujące tylko w ekstrakodzie PINP i uwzględniane tylko przy czytaniu z urządzenia dwukierunkowego (monitor ekranowy, drukarka z klawiaturą) -- słowo to zawiera kody dwóch znaków które będą wyprowadzane na urządzenie przed czytaniem, a ponadto pierwszy z tych znaków zostanie umieszczony w buforze.

BLOCK: opis transmisji blokowej (par. III.4.4)

  • 0 - wskaźnik ustawiany po powrocie z ekstrakodu, podający liczbę przetransmitowanych słów;
  • 1 - dla READ i WRIt -- identyfikator strumienia przywiązanego do zbioru, dla REAP i WRIP - identyfikator procesu, dla OVL - ignorowany;
  • 2 - adres początkowy transmisji w pamięci operacyjnej;
  • 3 - liczba słów transmisji;
  • 4 - adres sektora dyskowego (względem początku zbioru) od którego rozpocznie się transmisja.

TMEM: opis dodatkowego bloku pamięci (par. III.3.1.8)

  • 0 - wskaźnik błędu - interpretowany jest tak jak w innych ekstrakodach;
  • 1 - identyfikator strumienia przywiązanego do zbioru pamięciowego;
  • 2 - adres - podanie adresu równego zeru oznacza polecenie zwolnienia wskazanego bloku pamięci, adres różny od zera zostanie, po wyzerowaniu 12 najmłodszych bitów, nadany pierwszemu słowu dołączanego bloku (o rozmiarze 4 Ksłow);
  • 3 - nie używane;
  • 4 - wskazanie bloku do zwolnienia - kolejny (od 0 poczynając) numer bloku 4K w zbiorze. Jeśli wartość tego słowa wynosi -1, oznacza to polecenie zwolnienia bloku 4K, którego adres początkowy równy jest adresowi podanemu w słowie 2 powyżej, po wyzerowaniu jego ostatnich dwunastu bitów.

MT: opis operacji na taśmie magnetycznej (par. III.4.8)

  • 0 - słowo zawierające parametr zwracany przez ekstrakod:
    • bit 0=1 koniec taśmy,
    • bit 1=1 początek taśmy przy ruchu wstecz,
    • bit 2=1 znacznik zbioru (FILE MARK),
    • bity 3-15 długość przetransmitowanego bloku w słowach;
  • 1 - numer logiczny jednostki (pozycja w tablicy konfiguracji);
  • 2 - adres początkowy transmisji w pamieci operacyjnej;
  • 3 - żądana długość (liczba słów) transmisji.

PI: opis operacji PI (par. III.4.6)

  • 0 - słowo komunikacyjne.
  • 1 - kod operacji i adres PI Poszczególne bity zawierają:
    • 0-1 - kod odpowiedzi (O):
      • 0 - rozkaz wykonany (OK),
      • 1 - zajętość (EN),
      • 2 - błąd (PE),
      • 3 -brak odpowiedzi;
    • 2-4-kod funkcji (F):
      • 1 - czytaj z pakietu pierwsze słowo,
      • 2 - czytaj zgłoszenie przerwania,
      • 3 - czytaj z pakietu drugie słowo,
      • 4 - funkcja dodatkowa,
      • 5 - pisz do pakietu pierwsze słowo,
      • 6 - funkcja dodatkowa,
      • 7 - pisz do pakietu drugie słowo;
    • 5-7 - numer logiczny kanału PI (0-gdy tylko jeden kanał);
    • 8-11 - adres kasety;
    • 12-15 - adres pakietu.

Gdy F=0 wykonywany jest rozkaz do kanału, bity 11-13 określają treść rozkazu:

  • 0 - zeruj kanał;
  • 1 - blokuj przerwania do procesora;
  • 3 - ustaw rejestr masek;
  • 4 - sprawdź istnienie bloku sprzężenia;
  • 5 - podaj stan zgłoszeń z kaset;
  • 6 - podaj specyfikację przerwania o najwyższym piorytecie;
  • 7 - ustaw numer procesora (alokuj).

CAM: opis operacji CAMAC (par. III.4.7)

  • 0 - słowo zawierające na bitach 4^:-7 rodzaj operacji (zadawany przy wejśćiu do ekstrakodu) , zaś na bitach 0^:-2 pole odpowiedzi ustawiane przez ekstrakod:
    • bit 0=1 brak odpowiedzi z kanału,
    • bit 1=1 brak akceptacji rozkazu (X=0),
    • bit 2=1 odpowiedż negatywna (Q=0),
    • bit 4=1 operacja lub transmisja 24-bitowa,
    • bit 5=1 transmisja ADDRESS SCAN,
    • bit 6=1 transmisja STOP,
    • bit 7=1 zawieś proces i czekaj na zgłoszenie LAM.
Jeśli ekstrakod dotyczy transmisji to po jej zakończeniu na bitach 3-15 umieszczana jest liczba przetransmitowanych słow;
  • 1 - adres i funkcja CAMAC:
    • bity 0-1 numer logiczny kasety (C),
    • bity 2-6 kod funkcji (F),
    • bity 7-10 adres wewnętrzny (A),
    • bity 11-15 numer stanowiska (N);
  • 2 - adres danych w pamięci operacyjnej (używany jest tylko przy operacjach przesłania i transmisjach);
  • 3 - liczba słów transmisji (tylko przy transmisji).

Dodatek 4 - Wykaz ekstrakodów

Ekstrakod Nazwa Pole param. Opis
0200 ASG FIL przywiąż strumień do zbioru
0201 CASG FIL utwórz zbiór i przwiąż strumień
0202 SETP FIL ustal parametry zbioru
0203 LOAP FIL pobierz parametry zbioru
0204 TMEM TMEM dołącz pamięć do procesu
0205 NASG FIL dołącz strumień do zbioru
0206 ERF STR usuń zbiór
0207 ERS STR usuń strumień
0210 ERAS usuń strumienie
0211 FBOF STR cofnij wskaźnik dostępu na początek
0212 INPR REC wczytaj rekord
0213 PRIR REC wyprowadż rekord
0214 PINP REC wyprowadż dwa znaki i czytaj rekord
0215 PRIN REC wyprowadż rekord bez zn. kończącego
0216 EOF STR wyprowadż znacznik końca
0217 FEOF STR ustaw wskaźnik dostępu na koniec
0220 INAM REC pobierz parametr z bufora
0221 INUM REC pobierz liczbę z bufora
0222 WADR BLOCK zapisz pola adresowe na dysku
0223 OVL BLOCK wczytaj nakładkę ze zbioru
0224 REAP BLOCK czytaj ze zbioru specjalnego
0225 WRIP BLOCK pisz do zbioru specjalnego procesu
0226 READ BLOCK czytaj blok
0227 WRIT BLOCK pisz blok
0230 OES ERR zgłoszenie własnej obsługi alarmów
0231 ERR obsłuż mój ostatni alarm
0232 CORE r4 przydziel mi wskazaną pamięć
0233 CPRF FIL utwórz mi zbiór specjalny
0234 JUMP PROC przenieś proces do osobnego bloku
0235 SDIR DIR ustal parametry skorowidza
0236 TDIR DIR pobierz parametry skorowidza
0237 CDIR DIR zmień parametry skorowidza
0240 DEFP PROC definiuj proces potomny
0241 DELP PROC usuń proces potomny
0242 SREG PROC ustal rejestry procesu potomnego
0243 TREG PROC pobierz rejestry procesu potomnego
0244 RUNP PROC uruchom proces potomny
0245 HANG PROC zatrzymaj proces potomny
0246 TERR r4 pobierz proces z listy alarmów
0247 WAIT r4 czekaj wskazaną liczbę kwantów czasu
0250 STOP r4 stop
0251 RELD zwolnij urządzenia
0252 DATE TIM podaj aktualną datę
0253 TIME TIM podaj aktualny czas
0255 CHPI r4 zmień mój priorytet
0256 WAIS.n r4 opuść semafor
0257 SIGN.n r4 podnieś semafor
0260 TLAB MET podaj metrykę dysku
0261 PINF PINF podaj informacje o procesie
0262 CSUM r4 sprawdż sumę kontrolną systemu
0263 CSYS r4 zmień system
0264 UNL r4 odłącz obszar dyskowy
0265 LOD r4 dołącz obszar dyskowy
0266 TAKS STR opuść semafor strumienia
0267 RELS STR podnieś semafor strumienia
0270 GMEM r4 dodaj bloki pamięci
0271 RMEM r4 zwolnij bloki pamięci
0272 LRAM tabl podaj parametry zbioru RAM
0275 OPPI PI wykonaj operację PI
0276 WFPI PI pobierz przerwanie PI
0277 CAMAC CAM wykonaj operację CAMAC
0300 RWMT MT przewiń taśmę do początku
0301 FBMT MT cofnij taśmę o zbiór
0302 FFMT MT przesuń taśmę o zbiór
0303 BBMT MT cofnij taśmę o blok
0304 BFMT MT przesuń taśmę o blok
0305 FMMT MT zapisz znacznik zbioru
0306 REMT MT czytaj blok z taśmy
0307 WRMT MT pisz blok na taśmie

Ekstrakody pomocnicze programu XOSL

Ekstrakod Nazwa Pole param. Opis
0371 SCON reje ustaw bity słowa stanu programu XOSL
0372 TCON reje pobierz do R4 słowo stanu XOSL
0373 END r4 zakończenie programu
0374 BACK wykonuj program w tle
0375 ABO r4 usuń program
0376 KILL r4 usuń program z komunikatem
0377 EOSL r4 zakończ program wyprowadż komunikat

Ekstrakody pomocnicze programu INI (r4 nr końcówki)

Ekstrakod Opis
0373 odrzucenie zgłoszenia operatora
0374 zakończenie sesji
0375 zakończenie sesji
0376 zakończenie sesji
0377 zakończenie sesji

Źródło: Język symboliczny (assembler) ASSM dla minikomputera MERA-400 pod systemem operacyjnym CROOK-4, Włodzimierz J. Martin, Gdańsk 1986