Indi Informatyk

„Kukiełka Marionetkarza: o byciu ekspertem w programowaniu komputerowym”


MARJONETKA KVKIEŁKARZA

albo

O BYCIU EKſPERTEM
W ZAUTOMATYZOWANYM
PRZETWARZANIU DANYCH
ZA POMOCĄ PROGRAMOWANYA
MASZYN KALKULUJĄCYCH

F.N.


CAPUT I
NARODZINY TYRANII
Z DUCHA SPRAWCZOŚCI

Kiedy zaczynałem swoją pierwszą pracę jako programista, najbardziej popularnym kalkulatorem w naszym kraju była Wisła 64. Posiadała ona zawrotne 65536 adresowalnych rejestrów pamięciowych oraz jednostkę arytmetyczno-logiczną wspierającą operacje na 16-cyfrowych słowach binarnych. Model programowy był wyjątkowo przyjazny, ponieważ dla niemal wszystkich operacji adres rejestru danych był kodowany w ostatnich dwóch oktetach instrukcji. Był to dramatyczny przeskok względem kalkulatora Orzeł 8, który dominował tuż przed Wisłą. Mimo 8-cyfrowych binarnych operacji arytmetycznych Orzeł umożliwiał adresowanie 16384 oktetów pamięci, ale opierało się to na skomplikowanym systemie banków pamięci. Dla starych wyg Orła, którzy opanowali jego model, ta zmiana faworyta była niemal jak śmierć przyjaciela. Koniec końców otrząsnęli się oni z żałoby i zaczęli wykorzystywać potencjał nowej maszyny. Miałem to szczęście, że wydział na którym studiowałem matematykę stosowaną ze specjalizacją w systemach cyfrowych zdążył zaopatrzyć się w Wisłę 64, gdy byłem na ostatnim roku mojego programu. Dzięki czemu udało mi się skosztować nieco świeżonki przed wylądowaniem na rynku pracy.
Co prawda moim szczytem wykorzystania tego systemu było rysowanie drzewek w konsoli terminala. Uczelniani operatorzy, którym donosiłem karty z wyperforowanym programem, sarkastycznie pytali, czy stworzyłem nowy program albo znowu jest to tylko rysowanie Fryckowych krzaków. Żółć, która mnie od tego zalewała, odparowywała w kontakcie z entuzjazmem wynikającym z obcowania z cudem ludzkości, jakim była Wisła 64.

Spółka PolACME, w której zatrudniłem się jako inżynier, dostarczała rozwiązań obliczeniowych podmiotom w całym kraju oraz powoli zdobywała zagraniczne rynki. Naszym sztandarowym produktem był program Drzwi. Dedykowany był on na systemy oparte o Wisła 64 i stanowił wejście do wirtualnej przestrzeni operatorskiej, która umożliwiała wykonywanie wielu pomniejszych programów naraz oraz dostarczała szereg wygodnych narzędzi do zarządzania pamięcią operacyjną, jak i pamięcią pomocniczą. Wspierała zarówno taśmy magnetyczne, jak i magazyny talerzowe. Był to produkt tak świetny, że używali go nawet nasi inżynierowie i projektanci.
Dział, w którym pracowałem, opracowywał innowacyjne urządzenie rozszerzające do Wisła 64, które miało umożliwić bezpośrednią wymianę danych między dwiema maszynami na znaczne odległości. Roboczo zostało nazwane Piast, ponieważ miało połączyć maszyny kalkulujące, niczym pierwsi królowie Korony Polskiej połączyli nadwiślańskie plemiona.
Pierwsze prezentacje okazały się nie lada sukcesem. Kierownictwo zaczęło naciskać, żeby możliwe było podłączenie wielu takich urządzeń do jednej maszyny Wisła 64, dzięki czemu mogłaby się ona komunikować w wiele stron naraz. Ponadto dodano wymaganie, aby komunikacja działała również w trybie pośrednika(coś jak zabawa w "podaj dalej"). Przekroczeniem Rubikonu była jednak decyzja na zaadaptowanie produktu pod zagraniczne maszyny liczące. Podjęta ona została, ponieważ dyrekcja nawiązała współpracę biznesową z Dortmundzką spółką NWRW(Nordhein-Westfallen Rechenwerke GmbH) i podpisała kontrakt na ich autorską technologię o nazwie WindRad. Reklamowana była jako rewolucja w dziedzinie programowania obliczeniowego, ponieważ miała umożliwić uniwersalny zapis programu, który będzie możliwy do translacji na dowolny system obliczeniowy. Z założenia miało to odciążyć od projektowania programu na bazie konkretnej maszyny matematycznej, której niuanse wymagały podejmowania wielu decyzji dotyczących mało istotnych szczegółów.
Symbolika marki opisywała to jako podmuch wiatru na młyn napędzający inżynierię superwydajnych systemów obliczeniowego przetwarzania danych. My jednak zrobiliśmy sobie z niej obiekt żartów o tym, że wyraża ona podświadomą tęsknotę zarządu NWRW za reżimowym ustrojem, który ich naród sam obalił pokolenie wcześniej.

PolACME utworzyło specjalny dział, który miał pomóc NWRW w opracowaniu translatorów WindRad na maszyny Wisła 64 oraz Orzeł 8. Już po kilku miesiącach otrzymaliśmy prototypy, na bazie których mieliśmy opracowywać WindRadową wersję programu urządzenia komunikacyjnego Piast.
Czuć było w tym pewną inercję. Dokumentacja techniczna WindRada była opasła i poruszanie się po niej wymagało nieco wprawy. Pobieżne zapamiętanie spisu treści nieco przyspieszało ten proces. Sama treść dokumentacji dawała po sobie znać, że została przetłumaczona przez kogoś, kto średnio rozumiał język polski.

Wszystkie słowa kluczowe WindRada pochodziły z niemieckiego. Nie miałem z tym większego problemu, mój ojciec był z pochodzenia Niemcem. Ożenił się jednak z Polką i z tego powodu w Polsce się osiedlił. Pracował jako pianista w regionalnej filharmonii. Przekonywał często matkę, że nadał mi imię na cześć Chopina, jednak pod jej nieobecność opowiadał mi o chwale Cesarstwa Pruskiego oraz wielkości Świętego Cesarstwa Rzymskiego za panowania Barbarossy. Nie omieszkał przy tym wtrącać różne szlacheckie aforyzmy. "Pamiętaj synu, najpotężniejsza armia nie uczyni z ciebie władcy, jeżeli kupisz ją za cenę suwerenności."
Nie trafiało to wtedy do mnie, bo byliśmy prostą rodziną z miasta. Najpotężniejszym żołnierzem naszego domu była matka uzbrojona w tłuczek do kotletów. I bez tego była w stanie zrobić sobie z ojca podnóżek. Do końca życia nawijał o Czystości i tym, że liczy się tylko pismo i każdy ma takie same prawo jego interpretacji. Od czasu do czasu przebąkiwał coś o rzymskich uzurpatorach, którzy są diabłami świecącymi w ciemnościach. Jednak dla ukochanej Gracji ugiął się i ochrzcił jedynego syna po katolicku. Może to właśnie ta rozdwojona wizja na moje życie zaszczepiła we mnie jakiś dualizm, który uniemożliwił poważne podejście do obu z rodzimych wyznań. Poza tym nie potrafił wytrzymać choćby jednego przyjęcia bez tyrady o tym, jaki to ród von Nischwitzów nie był wpływowy, mimo że nie utrwalił się szczególnie na kartach historii. Wobec czego był prześmiewczo nazywany za plecami baronem Terenzem.
Ja również nie podzielałem tej sympatii, więc irytowało mnie gdy koledzy z pracy przychodzili do mnie z pytaniami o wpływ niuansów języka niemieckiego na WindRad, bo nie znałem go wcale aż tak dobrze.

Notacja, którą zapewnił nam WindRad od czasu do czasu pokrywała się ze schematami powtarzających się przypadków projektu Piast. Te schematy dokumentowaliśmy w formie biblioteki zawierającej schematy list instrukcji, które były opisane pod kątem problemu, jaki rozwiązywały. Notacja WindRada przypominała nieco notatki boczne tych dokumentów z tym, że nie były one tylko pustymi słowami, bo translator weryfikował spójność programu pod kątem użytych notacji. Niestety po przepisaniu bazowej części komunikacyjnej Piasta do WindRad maksymalna przepustowość danych spadła o niemal 30 procent. Wzbudziło to alarm u kierownictwa działu, bo kampania marketingowa zaczęła już obiecywać pewne liczby, a teraz okazały się one nieosiągalne.
Dlatego PolACME wystąpiło z podaniem do NWRW, aby zapewnili nam szkolenie w kwestii efektywniejszego używania WindRada. Producent WindRada zebrał wywiad od spółek używających ich produktu i spisał opasłe kompendium pełne różnorodnych technik i zasad użycia. Kopia woluminów trafiła do naszego działu i kierownictwo wymagało od nas ich studiowania i stosowania się do ich przykazu. Zorganizowanie sobie na to czasu oraz systemu współdzielenia dostępu do ksiąg pozostało kwestią przemilczaną. Napięcie wzrosło jeszcze bardziej, gdy dział rozwijający Drzwi zaczął adaptować konkurencyjną technologię. Ich kierownictwo zdecydowało się na wschodnie rozwiązanie o nazwie Szczipcy(ros. щипцы). Reklamowana była jako coś co pozwoli złapać rozgrzaną do bieli ideę programu i kuć ją póki gorąca. Poza tym miało się to kojarzyć z pewnym i bezpiecznym chwytem.
W rzeczywistości koledzy, którzy zaczęli je używać byli głównie uszczypliwi wobec wszystkich dookoła. Zaczęli nam suszyć głowy, że rozwiązania, które piszemy pod WindRad są podatne na awarie i manipulacje z zewnątrz, więc przezwaliśmy ich kultem czerwonego kraba.

Kuriozum osiągnęło punkt krytyczny w momencie, w którym doszło do sprzeczki między mną, a kolegą z działu o to jaki zapis w WindRadzie użyć. Popchnęło mnie to do przeprowadzenia iście szaleńczego badania, którym chciałem wykazać swoją rację, a w konsekwencji doprowadziło do niebagatelnych wniosków.
Przedstawię to za pomocą zapisów moich kontemplacji, które ukażą je z perspektywy bliższej temu, co myślałem i czułem w ciągu wydarzeń.

APPENDIX A
WYBRANE FRAGMENTY DOKUMENTACJI WINDRAD

WindRad jest jedną z technologii kalkulatorowych opracowanych przez Nordhein-Westfallen Rechenwerke GmbH(NWRW).
Jego zadaniem jest dostarczanie sposobu zapisu programów w sposób uniwersalny i przetłumaczalny na dowolną programowalną maszynę cyfrową.
Program zgodny z WindRad składa się z wierszy cyfrowego tekstu. Na wiersz składają się słowa oddzielone przerwami. Przerwa jest dowolnym znakiem, który w druku robi pustą przestrzeń i jest zależna od kodowania cyfrowego tekstu użytego do zapisu programu. Przerwy na początku i na końcu wiersza nie rozróżniają programów, wielokrotne puste znaki pomiędzy słowami stanowią pojedynczą przerwę.
Wysokość liter jest rozróżnialna w WindRad. Słowa o odgórnie nadanym znaczeniu składają się z samych wielkich liter.
Wiersze opisują operacje przetwarzania danych, kształt struktur sterujących oraz zmiany przepływu sterowania. Możliwe zapisy określone są w postaci składni. Każdy wiersz ma skończoną złożoność określoną odgórnie, zależną od pierwszorzędnej składni.
Program jest wykonywany wierszami od góry w dół, a każdy wiersz słowami od lewej do prawej.
Pewne operacje nie są niezgodne z gramatyką WindRad, ale ich skutek jest niemożliwy do zdefiniowania. Takie przypadki określa się jako Grauzone(pol. szara strefa) i należy unikać doprowadzenia do nich.

Dane w Windrad podlegają typowaniu. Typy dzielą się na podstawowe i złożone. Typy złożone określa się mianem "agregat".

Typy podstawowe mają określoną wartość zeropodobną oraz porządek wartości. Wartości znajdujące się dalej w porządku uznaje się za większe.
Typy podstawowe można używać w formie wartości bezpośredniej.

Typ ZAHL służy do reprezentowania wartości całkowitoliczbowych zapisywanych za pomocą cyfr arabskich.

Typ KOMMA służy do reprezentowania liczb pseudorzeczywistych zapisywanych w formie dziesiętnej z przecinkiem oddzielającym wartość całkowitą od ułamkowej.
Bezpośrednia wartość KOMMA podlega błędowi obcięcia wynikającego z precyzji kodowania liczb przecinkowych na jaki przetłumaczony zostanie program. Wyniki operacji na wartościach KOMMA również podlegają błędom precyzji.

Oba typy liczbowe mogą reprezentować wartości ujemne zaczynające się od znaku minusa i nieposiadające przerwy między minusem, a znakami liczby.

Wartość KETTE rozpoczęta i zakończona znakiem cytatu stanowi jedno słowo.
Porządek wartości KETTE zależy przede wszystkim od długości wartości. Wartości zawierające więcej znaków są większe.
W przypadku wartości o równej liczbie znaków relacja porządku zależy od porządku znaków wynikających z użytego kodowania tekstu. Znaki używane w odgórnie określonych słowach WindRad powinny zajmować początkowe pozycje porządku.
Sugerowany porządek to:

  1. duże litery alfabetu łacińskiego
  2. małe litery alfabetu łacińskiego
  3. cyfry arabskie
  4. znaki specjalne używane jako słowa operacji tj. dwukropek, znak równości, znaki mniejsze/większe

Typ LOGIK reprezentuje wartości logiczne. Przyjmuje tylko wartość WAHR oraz FALSCH oznaczających odpowiednio prawdę oraz fałsz.
Wartością zeropodobną jest fałsz, a prawda jest większa w relacji do fałszu.
LOGIK_NULL = FALSCH
WAHR > FALSCH

Operacje na wartościach wykonywane są zgodne z typowym kierunkiem przebiegu programu tj. od lewej do prawej, ale ich wynik jest przekazywany wstecz, do lewej. Dlatego muszą zostać poprzedzone składnią, która skorzysta z wyprodukowanej wartości.
Jako operand można użyć wyłącznie wartości bezpośredniej lub symbolu. Nie można zagnieździć kolejnej operacji jako operandu.

Dla wszystkich typów podstawowych można dokonywać operacji porównania.
Operacje porównania skutkują wytworzeniem wartości typu LOGIK. Możliwe porównania to równość(=), lewe większe(>) oraz prawe większe(<).

Każdy symbol może zostać poddany operacji pobierania adresu.
Polega ono na wykorzystaniu znaku (@) po lewej stronie nazwy symbolu, bez przerwy.
Operacja wytwarza wartość adresu jako typ ZAHL.

Dla typów liczbowych można używać dwuwartościowych operacji arytmetycznych takich jak dodawanie(+), odejmowanie(-), mnożenie(*), dzielenie(/).
Pomiędzy operandami, a znakiem operacji musi występować przerwa.
Odejmowanie może występować w formie jednowartościowej w którym występuje tylko prawy operand. Produkuje ono wartość przeciwną.

Wartości KETTE można łączyć ze sobą poprzez operację konkatenacji, która funkcjonuje pod znakiem pionowej kreski(|).

Na wartościach logicznych można dokonywać operacji dwuwartościowych, czyli iloczynu(UND) i sumy(ODER). Jak również jednooperandowego zaprzeczenia logicznego(NICHT), które przyjmuje tylko prawy operand.

Dane można przetwarzać pod postacią symboli. Symbol nadaje danym semantyczną nazwę oraz umożliwia zapamiętanie wartości.
Użycie symbolu poprzedza jego deklaracja. Deklaracja określa nazwę symbolu oraz typ danych. Nazwa symbolu może składać się z dużych i małych liter oraz musi zaczynać się od małej litery.
Wiersz deklaracji symbolu rozpoczyna się słowem specjalnym DATEN.
DATEN *nazwa* *typ*
Symbole typu podstawowego tuż po deklaracji mają niezdefiniowaną wartość.
Deklarację symbolu można zakończyć słowem specjalnym KONST.
Taki symbol nie może zmienić wartości w dalszym przebiegu programu. Jego wartość musi zostać nadana w wierszu następującym po deklaracji.

Zmiana wartości symbolu może nastąpić w wyniku operacji przypisania.
Przypisanie z prawej do lewej notuje się wokół operatora := przyjmującego z lewej strony symbol, którego wartość jest zmieniana. Z prawej strony operatora podaje się wartość bezpośrednią, symbol albo operację zgodną z symbolem z lewej strony.
Wartość wyliczona z prawej strony operatora kopiowana jest do symbolu po lewej stronie.

Typy złożone agregują określony typ podstawowy.
Każdy element agregatu działa jak osobny symbol, więc może zostać użyty jako wartość jak i może mu zostać nadana nowa wartość.

Typ FOLGE reprezentuje ciągi wartości. Używa się go poprzez połączenie z typem podstawowym poprzez znak podkreślenia np. FOLGE_ZAHL.
Agregaty FOLGE wykorzystują równoodległość elementów wynikającą ze stałego rozmiaru symboli typów podstawowych oraz przyległości ich wartości w pamięci.
Deklaracja agregatu typu FOLGE wymaga określenia rozmiaru ciągu w nazwie zmiennej. Bazową cześć nazwy i liczbę elementów separuje się znakiem podkreślenia.
W ramach deklaracji wszystkie elementy ciągu uzupełniane są wartością zeropodobną.
Można ustalić wartość n pierwszych elementów ciągu używając wiersza ANFANG tuż po deklaracji agregatu FOLGE.
Za wierszem ANFANG następują wiersze z wartościami typu elementu. Każdy wiersz uzupełnia wartość pod kolejnym indeksem dolnym. Może być to wartość bezpośrednia jak i symbol.
Za ostatnią wartością wstępnego uzupełnienia umieszcza się wiersz ENDE.
Odniesienie do konkretnego elementu ciągu następuje poprzez określenie jego indeksu za znakiem podkreślenia bazowej części nazwy. Elementy indeksuje się wartościami całkowitoliczbowymi.
Indeks dolny można przekazać jako wartość bezpośrednią lub poprzez symbol. Wartość bezpośrednia nie może być ujemna. W przypadku symbolu wartość ujemna w trakcie ewaluacji ulega przesunięciu, którego wynik jest zależny od rozmiaru liczb całkowitych systemu docelowego.
Wskazanie na indeks poza rozmiarem ciągu jest przypadkiem Grauzone.

Typ ZIPP reprezentuje pary danych w relacji klucz-wartość.
Konkretny ZIPP opisuje się formacie zgodnie z przykładem.
Wstępne wartości agregatu można nadać między wierszami ANFANG- ENDE następującymi tuż po deklaracji. Poszczególne pary podaje się w wierszach w formacie według przykładu.
DATEN przykladZipp ZIPP[KETTE -> LOGIK]
ANFANG
"abc" -> WAHR
"def" -> FALSCH
"xyz" -> FALSCH
ENDE
Odniesienie do wartości pod kluczem następuje poprzez wskazanie klucza w nawiasach kwadratowych za nazwą symbolu o typie agregatu. Dostęp do wartości kluczem powinien mieć złożoność nie wyższą niż logarytmiczna.
Użycie nieistniejącego klucza do odczytu spowoduje ewaluację do wartości zeropodobnej typu wartości.

Przepływ sterowania programu można kształtować za pomocą struktur sterujących i podprogramów.
Symbole zadeklarowane wewnątrz struktur sterujących przestając funkcjonować za ich zamknięciem. Ich nazwy nie mogą być takie same jak nazwy symboli zadeklarowanych w wierszach poprzedzających strukturę sterującą.

Struktury wyboru służą do wybiórczego wykonywania wierszy programu w zależności od jego stanu.

Struktura FLIPPER pozwala na wykonanie operacji od wybranego wiersza w zależności od wartości wejściowej.
Wejście do struktury FLIPPER może być tylko wartości ZAHL.
Strukturę otwiera wiersz ze słowem specjalnym FLIPPER i następującą po nim wartością typu ZAHL. Może być ona zarówno symbolem jak i wartością bezpośrednią.
Wewnątrz struktury znajdują się wiersze operacji oraz wiersze REIN oznaczające początek operacji dla danej wartości wejścia.
Wiersz REIN wymaga podania bezpośredniej wartości typu ZAHL tuż za słowem REIN.
Wartości poszczególnych REIN-ów nie mogą się powtarzać. Pierwszy wiersz wewnątrz FLIPPERA musi być REIN-em.
Po dopasowaniu wejścia do REIN-u wykonane zostaną wszystkie wiersze za REIN-em aż do końca FLIPPER-a. Wcześniejsze przekierowanie sterowania na zewnątrz struktury FLIPPER jest możliwe za pomocą wiersza RAUS.

Opcjonalnie za wartością dla REIN można umieścić drugorzędną składnię powodującą przepływ sterowania na zewnątrz.
Strukturę FLIPPER zamyka się wierszem z odwróconym słowem specjalnym - REPPILF.

Struktura WENN pozwala na wykonanie operacji jeżeli spełniony zostanie warunek.
Struktura rozpoczyna się od wiersza zaczynającego się słowem WENN po którym występuje wartość bezpośrednia, symbol albo operacja typu LOGIK.
Warunek uznaje się za spełniony jeżeli ewaluuje się do wartości WAHR.
Blok warunkowy zamyka się wierszem NNEW, DOCH albo DOCH WENN. Wiersz DOCH otwiera blok, który wykonuje się jeśli poprzedni warunek nie został spełniony. Wiersz DOCH WENN pozwala na zdefiniowanie kolejnego bloku, którego sprawdzenie nastąpi jedynie jeśli poprzedni warunek nie został spełniony.

Struktury iterujące służą do wielokrotnego wykonywania tych samych wierszy.

Struktura BIS służy do n-krotnego wykonania bloku programu.
Podstawowa składnia przyjmuje liczbę iteracji. Wiersz rozpoczynający można rozszerzyć składnią VON określającą wartość pierwszego numeru iteracji, domyślnie jest to jeden.
Można również rozszerzyć ją składnią ALS, która określa nazwę tymczasowego symbolu ZAHL przechowującego wartość aktualnej iteracji. Nazwa symbolu nie może się pokrywać z wcześniej zadeklarowanymi i istnieje tylko do końca struktury.
BIS 10
BIS 15 VON 5
BIS 10 ALS iteracja
BIS 9 VON 1 ALS iteracja
Strukturę zamyka się wierszem ze słowem SIB.

Każdy blok składni można wyizolować zamykając go w podprogram.
Deklaracja podprogramu rozpoczyna się słowem UNTERPROGRAMM. Po nim następuje opcjonalny typ wartości zwrotnej podprogramu. Dalej jest nazwa podprogramu, która podlega tym samym regułom co nazwy symboli. Nazwy zarówno symboli jak i podprogramów nie mogą się dublować.
W kolejnych wierszach deklaruje się symbole stanowiące parametry programu. Każdy wiersz składa się z nazwy symbolu parametru i jego typu. Nazwa parametru nie może być tożsama nazwie podprogramu.
Po liście parametrów następuje wiersz STARTE za którymi znajdują się wiersze instrukcji podprogramu. Za ostatnią instrukcją umieszcza się wiersz ENDE.
Wewnątrz podprogramu nie można deklarować kolejnych podprogramów. Nie można deklarować podprogramów wewnątrz struktur sterujących.

Słowo specjalne ZUR funkcjonuje jako zmiana przepływu sterowania. Składnia ta powoduje wyjście na zewnątrz wykonywanego podprogramu, czyli tak zwany powrót.
W przypadku funkcji zwracającej wartość funkcjonuje również jako symbol. Wartość do niego przypisana stanowi wartość zwrotną funkcji. W przypadku przypisywania od prawej do lewej ze słowem ZUR po lewej, po prawej stronie może wystąpić jedynie wartość bezpośrednia lub symbol.

W podprogramie funkcjonują wyłącznie symbole zadeklarowane jako jego parametry oraz symbole zadeklarowane wewnątrz wierszy podprogramu.
Nazwa symbolu w podprogramie może być taka sama jak w innych podprogramach oraz wierszach głównego programu.

Chcąc wykonać podprogram należy rozpocząć wiersz słowem specjalnym TUN.
Wywołanie może nastąpić wyłącznie za deklaracją podprogramu. Za słowem TUN podaje się nazwę podprogramu i bez przerwy otwiera nawiasy w których podaje się wartości argumentów oddzielone przerwami.
Jako argumenty podprogramu można przekazać wyłącznie symbole i wartości bezpośrednie. Wartość symboli przekazanych jako argumenty jest kopiowana do symboli zadeklarowanych jako parametry podprogramu.
Wartość zwrotną podprogramu można skopiować do symbolu używając operatora =: za wywołaniem podprogramu i symbolu po prawej stronie tego operatora.

Istnieje szereg podprogramów, które powinny być dostępne do wywołania w każdym programie i dostosowane pod system docelowy.

Grupa podprogramów pamięciowych służy do tymczasowego przydzielania bloków pamięci przez system docelowy.
Wśród nich znajdują się podprogramy do zapisu oraz odczytu wartości każdego typu podstawowego z adresu wskazanego jako typ ZAHL.

Podprogram ZEIT służy do pobierania czasu, który upłynął od początku uruchomienia programu.
Podprogram nie deklaruje żadnych parametrów.
Podprogram zwraca ciąg wartości typu FOLGE_ZAHL, którego rozmiar i znaczenie elementów zależą od systemu docelowego.

Program tłumaczący może udostępniać również podprogramy charakterstyczne dla systemu docelowego.

APPENDIX B
WYBRANE FRAGMENTY DOKUMENTACJI WISŁA 64

Wisła 64 udostępnia ponad 64 tysiące adresowalnych komórek pamięci.

W przypadku wartości całkowitoliczbowych cyfry binarne poszczególnych oktetów są kodowane od prawej do lewej jako od najmniej do najbardziej znaczącej.

Instrukcje programu Wisła 64 kodowane są jako kolejne binarne oktety w pamięci. Każda instrukcja koduje operację w pierwszym oktecie oraz może użyć od 0 do 3 następujących oktetów jako operandy.
Cyfry oktetu operacji opisane są od lewej do prawej. Pierwsze cztery cyfry binarne kodują bazową operację w formie liczby porządkowej. Kolejne cztery to flagi charakterystyki operacji. Każda bazowa operacja wspiera inną kombinację charakterystyk.
Model programowy pozwala na użycie dwóch rejestrów. Rejestru roboczego oraz rejestru szczytu stosu wykonania. Rejestr szczytu stosu wykonania zawiera adres bloku pamięci przechowującego informacje o ramkach procedur oraz danych z nimi związanych.
Używany rejestr koduje się na piątej cyfrze binarnej w taki sposób, że 0 to rejestr roboczy, a 1 to rejestr stosu.
Cyfra numer 6 określa w jakim zakresie przetwarza się rejestr. Instrukcja może działać w trybie pełnym(0) albo połowicznym(1), czyli odpowiednio dwu albo jednooktetowym.
Cyfra numer 7 oznacza, czy rejestr używany jest w trybie wartości(0) czy trybie wskaźnika(1). Tryb wartości to operowanie na wartości rejestru wprost. Tryb wskaźnika oznacza, że wartość w rejestrze wyznacza adres pod którym znajduje się wartość do użycia.
W trybie wskaźnika wymagane jest podanie dodatkowego oktetu operandu tuż za oktetem operacji, który koduje przesunięcie względem adresu pod rejestrem. Wartość operandu kodowana jest w formacie uzupełnień do dwóch.
Ostatnia cyfra oznacza, czy operand główny jest adresem(0) czy wartością(1). Operand adresowy zawsze zajmuje dwa oktety, operand wartościowy może być jednooktetowy, jeżeli wybrano przetwarzanie w trybie połowicznym.
Dokładne działanie zależne jest od operacji bazowej.

Program na Wisłę 64 można zapisywać w postaci notacji pomocniczej. Istnienie programu przekładającego tę notację na kodowanie obrazu programu jest zależne od systemu na którym przetwarzany jest zapis. Każda instrukcja zapisywana jest w osobnym wierszu.
Można tworzyć wiersze funkcjonujące jako alias adresu. Taki wiersz rozpoczyna się znakiem zakreślonego a - @. Wartość aliasu jest wyliczana w trakcie kodowania obrazu programu.
Program kodujący powinien umożliwić określenie wartości adresu pierwszej instrukcji programu. Względem tej wartości przeliczane są wszystkie aliasy powstałe w ramach oznaczenia wiersza. Możliwe jest również nadanie stałej wartości aliasu adresowego. Każda operacja bazowa ma swój mnemonik w notacji pomocniczej. Mnemoniki pisze się w formie małych liter.
Notacja pomocnicza pozwala na korzystanie z tzw. adresów pomocniczych - auxilar. Notuje się je w skrócie dużych liter AUX za którymi znajduje się liczba porządkowa.
Możliwe jest bezpośrednie kodowanie wartości w obrazie programu. Służą do tego wiersze zaczynające się od znaku krzyżyka(#). Kodują one tyle oktetów ile jest minimalnie potrzebne dla zapisanej wartości. Domyślnie wartości są w formie dziesiętnej np. #127, ale mogą być również zapisywane w formie binarnej np. #b1001 albo heksadecymalnej np. #hf0a1.
Zakodowany obraz programu powinien zawierać dodatkowe podwójne sekwencje zerowych oktetów o liczbie równej największej liczbie porządkowej użytej jako adres pomocniczy. Oktety te powinny znaleźć się za oktetami kodującymi ostatnią instrukcję.

Wiersz instrukcji rozpoczyna się od mnemonika operacji bazowej. Po nim następuje mnemonik rejestru(R - roboczy, S - stosu). Nie każda operacja bazowa używa rejestru.
Chcąc użyć rejestru w trybie adresowym należy tuż za jego mnemonikiem, bez przerwy umieścić znak zakreślonego a oraz wartość przesunięcia. Wartość przesunięcia przyjmuje wartości od -128 do 127. Kolejnym możliwym słowem jest wartość operandu głównego. Tryb połowiczny wymusza się poprzez prefiks operandu znakiem dużego P. Wartości bezpośrednie w trybie połowicznym muszą być jednooktetowe.
Operand adresowy zapisuje się jako wartość liczbową lub wyciąga z aliasu.

Operand wartościowy poprzedza się znakiem krzyżyka(#). Wartości można podawać tak samo jak w przypadku wierszy kodujących dane z tym, że nie mogą być większe niż na dwa oktety.
Możliwe jest użycie aliasu jako operacja na dosłownej wartości adresu, a nie wartości znajdującej się pod nim.
Notacja pomocnicza udostępnia również specjalną składnię "rel" służącą do tworzenia wartości względnej do aliasu adresu na potrzeby skoków. Wartość liczona jest względem końca instrukcji i zmniejszana o jeden, aby uwzględnić jeden oktet instrukcji skoku, która ma ją wykorzystać.

lista operacji:

nazwakodmnemoniktryby rejestrutryby operandu
pusta operacja0nicnie dotyczynie dotyczy
wstawienie do rejestru1wstwartość/wskaźnikadres/wartość
przechowanie w pamięci2przewartość/wskaźnikadres
iloczyn binarny3orazwartośćadres/wartość
suma binarna4lubwartośćadres/wartość
negacja binarna5niewartośćnie dotyczy
dodawanie6dodwartośćadres/wartość
odejmowanie7odewartośćadres/wartość
mnożenie8mnwartośćadres/wartość
Skok bezwarunkowy9sknie dotyczynie dotyczy
porównanie10porwartość/wskaźnikadres/wartość
Skok gdy nierówne11sgnnie dotyczynie dotyczy
Skok gdy mniejsze12sgmnie dotyczynie dotyczy
Wykonanie procedury13czynnie dotyczynie dotyczy
Powrót z procedury14pwrnie dotyczynie dotyczy
Zatrzymanie maszyny15stopnie dotyczynie dotyczy

Pusta operacja skutkuje przejściem do następnej instrukcji. Wykonuje puste cykle zegara bez wpływu na stan pamięci i rejestrów.

Operacje binarne oraz arytmetyczne zapisują otrzymany wynik w użytym rejestrze.

Operacje skoku używają wartości rejestru roboczego jako przesunięcie względne w oktetach. Przesunięcie następuje względem końca instrukcji skoku.
Skoki "gdy" bazują na wyniku ostatniej operacji porównania. Operacja porównania porównuje rejestr do operandu, więc przykładowo skok gdy mniejsze dokona skoku, gdy w ramach ostatniego porównania wartość rejestrowa była mniejsza niż wartość operandowa.

Operacja wykonania procedury wykorzystuje wartość w rejestrze roboczym jako adres pod którym znajduje się pierwsza instrukcja procedury.
Operacja wykonania procedury powoduje wpisanie adresu początku następnej instrukcji na wierzch stosu i zwiększenie jego rejestru o dwa. Analogicznie operacja powrotu korzysta z dwóch oktetów poniżej wartości rejestru stosu jako adres skoku. Następnie zmniejsza wartość rejestru stosu o dwa.

Operacja zatrzymania maszyny powoduje całkowite zatrzymanie przebiegu przetwarzania instrukcji.

Pusta operacja skutkuje przejściem do następnej instrukcji. Wykonuje puste cykle zegara bez wpływu na stan pamięci i rejestrów. Operacje binarne oraz arytmetyczne zapisują otrzymany wynik w użytym rejestrze.
Operacje skoku używają wartości rejestru roboczego jako przesunięcie względne w oktetach. Przesunięcie następuje względem końca instrukcji skoku.
Skoki "gdy" bazują na wyniku ostatniej operacji porównania. Operacja porównania porównuje rejestr do operandu, więc przykładowo skok gdy mniejsze dokona skoku, gdy w ramach ostatniego porównania wartość rejestrowa była mniejsza niż wartość operandowa. Operacja wykonania procedury wykorzystuje wartość w rejestrze roboczym jako adres pod którym znajduje się pierwsza instrukcja procedury.
Operacja wykonania procedury powoduje wpisanie adresu początku następnej instrukcji na wierzch stosu i zwiększenie jego rejestru o dwa. Analogicznie operacja powrotu korzysta z dwóch oktetów poniżej wartości rejestru stosu jako adres skoku. Następnie zmniejsza wartość rejestru stosu o dwa.
Operacja zatrzymania maszyny powoduje całkowite zatrzymanie przebiegu przetwarzania instrukcji.
Zapis wartości rozpościerających się na szereg adresów następuje zgodnie ze wzrostem adresów. Oktety uszeregowane są przeciwnie do adresacji pod względem wyższości. W notacji pomocniczej dwuoktetowych wartości liczbowych lewy oktet zawiera bardziej znaczącą część, w tym cyfrę znaku pod najbardziej lewą cyfrą. Operacja połowiczna na wartości rejestru wpływa tylko na prawy oktet. Operacja połowiczna na pamięci traktuje wartość pod adresem jako jednooktetową.

Po uruchomieniu Wisła 64 rozpoczyna wykonywanie instrukcji począwszy od adresu zerowego.

Ostatnie 32 oktety pamięci są służą do programowego monitorowania oraz kontroli podstawowego funkcjonowania maszyny.

Maszyna zlicza swój czas działania i udostępnia go w trzech licznikach kodujących nieujemne wartości całkowite
#hff80 - dwuoktetowy licznik mikrosekund, który przekręca się co 10 tys. mikrosekund
#hff82 - dwuoktetowy licznik setnych sekundy, który przekręca się co 600 sekund
#hff84 - jednooktetowy licznik jednej szóstej godzin, który przekręca się co 42 godziny

APPENDIX C
WYBRANE FRAGMENTY DOKUMENTACJI DRZWI

Drzwi to program nadzorujący dedykowany na maszyny kalkulujące Wisła 64 oraz podobnej mocy obliczeniowej.
To również ekosystem w którym programy mogą być wykonywane współbieżnie. Dostarcza on szeregu funkcjonalności ogólnego zastosowania, dzięki czemu programy nie muszą powielać powszechnych procedur.

Programy wykonywane pod nadzorem Drzwi mają przydzielane segmenty pamięci. Pierwszy program uruchomiony pod nadzorem znajduje się w segmencie zaraz za segmentem programu nadzorowania. Na końcu puli pamięci znajdują się oktety, które program nadzorujący przydziela na żądanie. Taka pamięć może być współdzielona między programami.
Segment programu wykonywanego składa się z następujących części.

  1. tabela adresów procedur
  2. instrukcje główne
  3. adresy pomocnicze
  4. stos wykonania programu

Dla wszystkich procedur znajdujących się w obrazie programu należy utworzyć tabelę adresów początkowych.
Do wywołania procedury należy użyć adresu rekordu w tabeli adresów początkowych procedur. Dzięki temu program nadzorujący Drzwi może przeliczyć wartości adresów po umieszczeniu obrazu w pamięci operacyjnej.

Wykonanie programu wymaga wskazania jego obrazu. Obraz programu wykonywalnego pod programem nadzorującym Drzwi musi zawierać nagłówek danych.

  1. wartość adresu pierwszej instrukcji programu - 2 oktety
  2. adres tabeli adresów procedur adekwatny do adresu pierwszej instrukcji - 2 oktety
  3. liczba procedur w tabeli - 1 oktet
  4. liczba oktetów na adresy pomocnicze - 1 oktet

Schemat notacji pomocniczej Wisła 64 z której wygeneruje się obraz programu.

#h0100
#@tabelaAdresowProcedur
#02
#0a
[...]
@instrukcje
[...]
stop
@tabelaAdresowProcedur
@adresProcedury1
#@procedura1
@adresProcedury2
#@procedura2
@procedury
@procedura1
wst R #0
pwr

Operandy adresowe większe niż 84(h) są rekalkulowane względem adresu pierwszej instrukcji obrazu po jego umieszczeniu w segmencie pamięci.

Program oraz jego procedury wykonywane pod nadzorem Drzwi powinny dokonywać transferu wartości w następujący sposób. Parametry programu muszą mieć określoną kolejność. Ich wartości należy odłożyć na stos w tej właśnie kolejności przed wykonaniem procedury.
Wartość zwrotna programu musi zostać umieszczona w rejestrze roboczym przed powrotem z procedury.

Ekosystem Drzwi dostarcza sposobu kodowania oraz procedur przetwarzania liczb przecinkowych.
Jest to zmiennoprzecinkowy format o podstawie dwójkowej z rozróżnieniem dodatnie/ujemne, 9-cyfrową mantysą i 6-cyfrową eksponentą.
Pierwsza cyfra od lewej koduje kierunek względem zera. Wartość 0 oznacza liczby dodatnie, a 1 ujemne.
Cyfry od drugiej do dziesiątej kodują mantysę. Każda cyfra to składnik kolejnej ujemnej potęgi dwa począwszy od dwa do minus pierwszej. Cyfry od jedenastej do szesnastej kodują wykładnik w formacie uzupełnień do dwóch z najmniej znaczącą najbardziej prawą cyfrą.

Program nadzorujący Drzwi dostarcza procedury wszechstronnego użytku.
Pozycje poszczególnych procedur bazowych w tabeli adresów są stałe. Rozszerzenie do programu kodującego nadaje im aliasy o stałych wartościach.

Aliasy procedur w notacji pomocniczej:

Drzwi-Zmiennoprzecinkowe-dodaj
Drzwi-Zmiennoprzecinkowe-odejmij
Drzwi-Zmiennoprzecinkowe-mnoz
Drzwi-Zmiennoprzecinkowe-dziel

Drzwi-Pamiec-rezerwuj
Drzwi-Pamiec-zapisz
Drzwi-Pamiec-czytaj
Drzwi-Pamiec-zwolnij

Drzwi-DrzewoBinarne-rezerwuj
Drzwi-DrzewoBinarne-wstaw
Drzwi-DrzewoBinarne-znajdz
Drzwi-DrzewoBinarne-zwolnij

procedura Drzwi-DrzewoBinarne-rezerwuj
parametr 1
rodzaj wartości przeszukiwania oraz rodzaj wartości w liściach - 1 oktet pierwsza połowa oktetu koduje rodzaj przeszukiwania, a druga rodzaj wartości w liściach
rodzaje danych:
1 - liczba całkowita
2 - liczba zmiennoprzecinkowa
3 - łańcuch znaków

wartość zwracana
adres pod którym znajdują się dane drzewa

procedura Drzwi-DrzewoBinarne-wstaw
parametr 1
wartość wyszukiwania - 2 oktety

parametr 2
wartość liścia - 2 oktety

wartość zwracana
brak

procedura Drzwi-DrzewoBinarne-znajdz
parametr 1
wartość przeszukiwania - 2 oktety

wartość zwracana
znaleziona wartość lub zera w przypadku nieistniejącej wartości wyszukiwania