Język programowania systemów
Paradygmaty | proceduralne , imperatywne , ustrukturyzowane |
---|---|
Rodzina | ALGOL |
Po raz pierwszy pojawiły się | 1972 |
Pod wpływem | |
ALGOL 60 , ESPOL | |
Pod wpływem | |
ZSPL, Micro-SPL, Action! |
Systems Programming Language , często skracany do SPL , ale czasami znany jako SPL/3000 , był zorientowanym proceduralnie językiem programowania napisanym przez Hewlett-Packard dla linii minikomputerów HP 3000 i wprowadzonym po raz pierwszy w 1972 roku. SPL był używany do napisania podstawowego systemu operacyjnego HP 3000 system , Multi-Programming Executive (MPE). Podobne języki na innych platformach były ogólnie określane jako systemowe języki programowania , mylące sprawy.
Pierwotnie znany jako Alpha Systems Programming Language , nazwany na cześć projektu rozwojowego, w ramach którego wyprodukowano serię 3000, język SPL został zaprojektowany w celu wykorzystania konstrukcji procesora Alpha opartej na stosie . Jest wzorowany na ESPOL , podobnym języku wywodzącym się z ALGOL , używanym przez systemy mainframe Burroughs B5000 , który również wpłynął na wiele języków z lat 60., takich jak PL360 i JOVIAL .
W połowie lat siedemdziesiątych sukces systemów HP zaowocował szeregiem odgałęzień SPL. Przykłady obejmują ZSPL dla procesora Zilog Z80 i Micro-SPL dla Xerox Alto . Później zainspirowana Akcja! dla rodziny 8-bitowych Atari , która odniosła spory sukces. Ten ostatni był bardziej zgodny ze Pascala , tracąc niektóre cechy charakterystyczne SPL.
SPL był szeroko stosowany w okresie istnienia oryginalnych wersji platformy HP 3000 opartych na układach scalonych . W latach 80. HP 3000 i MPE zostały ponownie zaimplementowane w emulatorze działającym na platformach HP 9000 opartych na PA-RISC . HP promował Pascal jako preferowany język systemowy w PA-RISC i nie dostarczył kompilatora SPL. Spowodowało to z konserwacją kodu i w celu zaspokojenia tej potrzeby wprowadzono kompilatory SPL innych firm.
Historia
Firma Hewlett-Packard wprowadziła na rynek swoje pierwsze minikomputery , serię HP 2100 , w 1967 roku. Maszyny zostały pierwotnie zaprojektowane przez zewnętrzny zespół pracujący dla Union Carbide i były przeznaczone głównie do wbudowanych zastosowań przemysłowych, a nie do szerszego rynku przetwarzania danych. Firma HP uznała to za naturalne dopasowanie do istniejącej działalności w zakresie oprzyrządowania i początkowo przedstawiła ją tym użytkownikom. Mimo to firma HP stwierdziła, że stosunek ceny do wydajności maszyny zapewnia jej coraz większe sukcesy na rynku biznesowym.
W tym okresie koncepcja współdzielenia czasu stawała się popularna, zwłaszcza gdy spadły koszty pamięci rdzeniowej i systemy zaczęły być dostarczane z większą ilością pamięci. W 1968 roku firma HP wprowadziła system pakietowy wykorzystujący dwie maszyny z serii 2100 z systemem HP Time-Shared BASIC , który zapewniał kompletny system operacyjny oraz język programowania BASIC . Te dwumaszynowe systemy, znane pod wspólną nazwą HP 2000s, odniosły natychmiastowy sukces. HP BASIC był bardzo wpływowy przez wiele lat, a jego składnię można zobaczyć w wielu BASICach mikrokomputerów , w tym Palo Alto TinyBASIC , Integer BASIC , North Star BASIC , Atari BASIC i inne.
Projektanci w HP zaczęli się zastanawiać: „Skoro jesteśmy w stanie stworzyć tak dobry system z podziałem czasu przy użyciu tandetnego komputera, takiego jak 2116, pomyślcie, co moglibyśmy osiągnąć, gdybyśmy zaprojektowali własny komputer”. W tym celu w 1968 roku firma zaczęła zbierać większy zespół do projektowania nowej architektury średniej wielkości. Wśród nowych członków zespołu znaleźli się ci, którzy pracowali nad mainframe Burroughs i IBM , a wynikające z nich koncepcje były bardzo podobne do bardzo udanego systemu Burroughs B5000 . B5000 wykorzystywał maszyny stosowej , który umożliwiał wieloprogramowanie prostszy do wdrożenia, i ta sama architektura została również wybrana dla nowej koncepcji HP.
Rozważano dwie implementacje, 32-bitową maszynę na komputer typu mainframe, znaną jako Omega, oraz 16-bitową konstrukcję znaną jako Alpha. Prawie cały wysiłek był skierowany na Omegę, ale w czerwcu 1970 roku Omega została anulowana. Doprowadziło to do szeroko zakrojonego przeprojektowania Alpha, aby odróżnić ją od 2100, i ostatecznie pojawiły się plany jeszcze bardziej agresywnego projektu systemu operacyjnego. Omega zamierzała działać w trybie wsadowym i używać mniejszego komputera, „interfejsu”, do przetwarzania interakcji z użytkownikiem. To była ta sama koncepcja obsługi, co w przypadku serii 2000. Jednak kolejne 2000 nie wystarczyłoby dla Alfy i podjęto decyzję o stworzeniu jednego działającego w trybie wsadowym, interaktywnego, a nawet działanie w czasie rzeczywistym .
Aby to zadziałało, potrzebował zaawansowanej magistrali komputerowej z rozbudowanym bezpośrednim dostępem do pamięci (DMA) i zaawansowanego systemu operacyjnego (OS), który zapewniałby szybkie reakcje na działania użytkownika. B5000 był również wyjątkowy jak na swoje czasy, ponieważ jego system operacyjny i podstawowe narzędzia zostały zaprogramowane w języku wysokiego poziomu , ESPOL . ESPOL był pochodną języka ALGOL dostosowanego do pracy na B5000, koncepcji, która miała duży wpływ w latach 60-tych i doprowadziła do powstania nowych języków, takich jak JOVIAL , PL/360 i BCPL . Zespół HP zdecydował, że będzie również używał języka wywodzącego się z ALGOL-a do działania swoich systemów operacyjnych. Podobny język HP był początkowo znany jako język programowania Alpha Systems.
Opracowanie Alpha zajęło kilka lat, zanim pojawiła się w 1972 roku jako HP 3000. Maszyna była dostępna na rynku zaledwie kilka miesięcy, zanim stało się jasne, że po prostu nie działa prawidłowo, a HP został zmuszony do wycofania wszystkich sprzedanych już 3000. Został ponownie wprowadzony pod koniec 1973 roku, a większość jego problemów została naprawiona. Poważna aktualizacja całego systemu, maszyny CX i działającego na niej MPE-C, zreformowała jego wizerunek, a 3000 odniósł kolejny duży sukces w drugiej połowie lat 70.
Ten sukces sprawił, że SPL był prawie tak rozpowszechniony, jak BASIC z serii 2000 i podobnie jak ten język, SPL zaowocował wieloma wersjami na inne platformy. Godny uwagi wśród nich był Micro-SPL, wersja napisana dla Xerox Alto . Ta maszyna pierwotnie używała BCPL jako podstawowego języka, ale niezadowolenie z jej wydajności skłoniło Henry'ego Bakera do zaprojektowania nierekurencyjnego języka, który zaimplementował wraz z Clintonem Parkerem w 1979 roku. Następnie Clinton dalej modyfikował Micro-SPL, aby stworzyć Action! dla rodziny 8-bitowych Atari w 1983 roku.
HP ponownie wdrożył system HP 3000 na chipsecie PA-RISC, uruchamiając nową wersję systemu operacyjnego znaną jako MPE/iX. MPE / iX miał dwa tryby, w „trybie natywnym” uruchamiał aplikacje, które zostały ponownie skompilowane dla PA-RISC przy użyciu nowszych kompilatorów Pascala, podczas gdy w „trybie zgodności” mógł uruchamiać całe istniejące oprogramowanie poprzez emulację. Firma HP nie dostarczyła kompilatora trybu natywnego dla MPE/iX, więc przeniesienie istniejącego oprogramowania na nową platformę nie było łatwym procesem. Aby zaspokoić tę potrzebę, konsultanci Allegro napisali język zgodny z SPL o nazwie „SPlash!” który można skompilować do oryginalnego kodu HP 3000 w celu uruchomienia w emulatorze lub w trybie natywnym. To oferowało ścieżkę przenoszenia dla istniejącego oprogramowania SPL.
Język
Podstawowa składnia
SPL generalnie przestrzega konwencji składniowych ALGOL 60 i będzie znany każdemu, kto ma doświadczenie w ALGOL lub jego potomkach, takich jak Pascal i Modula-2 . Podobnie jak te języki, instrukcje programu mogą obejmować wiele linii fizycznych i kończyć się średnikiem. Komentarze są oznaczane słowem COMMENT
lub otaczają tekst komentarza znakami << i >>.
Instrukcje są pogrupowane w bloki za pomocą BEGIN i END, chociaż, podobnie jak w Pascalu, po KONIEC programu musi następować kropka. Program jako całość jest otoczony przez BEGIN i END., podobnie jak w Pascalu, ale brakuje mu słowa kluczowego PROGRAM lub podobnego stwierdzenia na górze. Powodem tego jest to, że SPL pozwala na użycie dowolnego bloku kodu jako samodzielnego programu lub wkompilowanie go w inny program, aby działał jako biblioteka. Tworzenie kodu jako programu lub podprogramu nie było częścią samego języka, zamiast tego było obsługiwane przez umieszczenie dyrektywy kompilatora $CONTROL SUBPROGRAM
na początku pliku.
Język dostarczył słowo kluczowe INTRINSIC
, aby zapewnić łatwą metodę deklarowania procedur zewnętrznych, unikając w ten sposób konieczności deklarowania przez programistę typów i kolejności parametrów procedury. Wszystkie dostępne dla użytkownika deklaracje usług systemowych (takie jak system plików, obsługa procesów, komunikacja i tym podobne) były dostępne za pośrednictwem tego mechanizmu, a użytkownicy mogli również dodawać własne deklaracje procedur do listy INTRINSIC
systemu . Jest to podobne do mechanizmu #include, ale bardziej wyrafinowanego i węższego niż w języku C.
W przeciwieństwie do Pascala, gdzie PROCEDURA
i FUNKCJA
były odrębnymi pojęciami, SPL stosuje podejście bardziej podobne do C , w którym każdą PROCEDURĘ
można poprzedzić typem, aby przekształcić ją w funkcję. Zgodnie ze składnią innych języków podobnych do ALGOL, typy parametrów zostały wymienione po nazwie, a nie jej części. Na przykład:
PROCEDURA LICZB CAŁKOWITYCH FACT(N); WARTOŚĆ N; LICZBA CAŁKOWITA N;
Deklaruje funkcję FACT, która przyjmuje wartość N będącą liczbą całkowitą. WARTOŚĆ dlatego
jej zmiany w procedurze nie będą widoczne dla wywołującego. Procedura ustawia wartość zwracaną przez instrukcję przypisania do swojej nazwy:
FAKT := wyrażenie;
Chociaż ALGOL i Pascal nie były mile widziane, umożliwiły oznaczanie kodu za pomocą wiodącej nazwy kończącej się dwukropkiem, która może być następnie używana jako cel pętli i instrukcji GO TO .
Jedna niewielka różnica polega na tym, że SPL wymagał zadeklarowania nazw etykiet w sekcji zmiennych za pomocą słowa kluczowego LABEL
.
SPL dodał do tej koncepcji instrukcję ENTRY
, która umożliwiła dalsze zdefiniowanie tych etykiet jako „punktów wejścia”, do których można uzyskać dostęp z wiersza poleceń. Etykiety nazwane w instrukcjach wejściowych były widoczne dla systemu operacyjnego i można je było wywołać z polecenia RUN. Na przykład można napisać program zawierający funkcje łańcuchowe do konwersji na wielkie lub małe litery, a następnie podać punkty WEJŚCIA dla tych dwóch. Można to wywołać z wiersza poleceń jako RUN $STRINGS,TOUPPER
.
Typy danych
formacie big endian z 3000 .
INTEGER jest typem 16-bitowym ze znakiem, z 15 bitami wartości i najmniej znaczącym
bitem jako znakiem. DOUBLE
to 32-bitowa liczba całkowita, a nie liczba zmiennoprzecinkowa. REAL
to 32-bitowa wartość zmiennoprzecinkowa z 22 bitami mantysy i 9 bitami dla wykładnika, podczas gdy LONG
to 64-bitowa wartość zmiennoprzecinkowa z 54 bitami mantysy i 9 bitami wykładnika.
BYTE
służy do przetwarzania znaków, składającego się z 16-bitowego słowa maszynowego zawierającego pojedynczy 8-bitowy znak w najmniej znaczących bitach. Tablice typu BYTE zawierają dwa 8-bitowe znaki na 16-bitowe słowo maszynowe. LOGICZNY
to 16-bitowa liczba całkowita bez znaku, która użyta w wyrażeniu warunkowym zwraca wartość true, jeśli najmniej znaczącym bitem jest 1, w przeciwnym razie zwraca wartość false. Arytmetyka liczb całkowitych bez znaku może być wykonywana na danych LOGICZNYCH, a wszelkie przepełnienia są ignorowane. Nie ma odpowiednika PACKED
, jaki można znaleźć w Pascalu, więc LOGICZNIE
jest nieco marnotrawstwem pamięci, gdy jest używany tylko do przechowywania pojedynczej cyfry binarnej, chociaż SPL oferuje alternatywę dla manipulacji ciągiem bitów.
Podobnie jak C, dane są słabo wpisane , lokalizacje pamięci i przechowywanie zmiennych są pojęciami mieszanymi, a dostęp do wartości można uzyskać bezpośrednio poprzez ich lokalizacje. Na przykład kod:
LICZBA CAŁKOWITA A,B,C LOGICZNE D=A+2
definiuje trzy 16-bitowe zmienne całkowite, A, B i C, a następnie LOGICZNĄ, również wartość 16-bitową. The =
, podobnie jak Pascal, oznacza „jest odpowiednikiem”, a nie „otrzymuje wartość”, który używa :=
w językach podobnych do Algola. Tak więc druga linia stwierdza „zadeklaruj zmienną D, która znajduje się w tej samej lokalizacji pamięci co A + 2”, co w tym przypadku jest również lokalizacją zmiennej C. Pozwala to na odczytanie tej samej wartości jako liczby całkowitej przez C lub logiczne przez D.
Ta składnia może wydawać się dziwna dla współczesnych czytelników, w których pamięć jest na ogół czarną skrzynką , ale ma wiele ważnych zastosowań w programowaniu systemów, w których określone lokalizacje pamięci przechowują wartości z bazowego sprzętu. W szczególności pozwala zdefiniować zmienną wskazującą początek tabeli wartości, a następnie zadeklarować dodatkowe zmienne wskazujące poszczególne wartości w tabeli. Jeśli zmieni się położenie tabeli, tylko jedna wartość musi się zmienić, adres początkowy i wszystkie indywidualne zmienne automatycznie podążą za odpowiednimi względnymi przesunięciami.
Wskaźniki zostały zadeklarowane przez dodanie modyfikatora POINTER
do dowolnej deklaracji zmiennej, a lokalizacja pamięci zmiennej została wyłuskana za pomocą @
. Zatem INTEGER POINTER P:=@A
deklaruje wskaźnik, którego wartość zawiera adres zmiennej A, a nie wartość A. @
może być użyte po obu stronach przypisania; @P:=A
umieszcza wartość A w P, co prawdopodobnie skutkuje wiszącym wskaźnikiem , @P:=@A
sprawia, że P wskazuje na A, podczas gdy P:=A
umieszcza wartość A w miejscu aktualnie wskazywanym przez P .
W podobny sposób SPL obejmuje obsługę tablic typu C, w której zmienna indeksu jest przesunięciem liczby słów od miejsca w pamięci ustawionego dla zmiennej początkowej. W przeciwieństwie do C, SPL zapewniał tylko tablice jednowymiarowe i używał nawiasów zamiast nawiasów. Zmienne można również zadeklarować GLOBAL
, w którym to przypadku nie zarezerwowano dla nich pamięci lokalnej i założono, że przechowywanie jest zadeklarowane w innej bibliotece. Odzwierciedla to extern
w C.
Literały mogą być określone z różnymi sufiksami, a te bez sufiksu są traktowane jako INTEGER
. Na przykład 1234
będzie interpretowane jako INTEGER
, podczas gdy 1234D
będzie DOUBLE
. E
oznaczało REAL
,
a L
LONG . Stałe łańcuchowe były oddzielone podwójnymi cudzysłowami, a podwójne cudzysłowy w wierszu były poprzedzone drugim podwójnym cudzysłowem.
Deklaracje zmiennych mogą używać stałych do definiowania wartości początkowej, jak w INTEGER A:=10
. Zwróć uwagę na użycie przypisania do zamiast is-a. Ponadto SPL miał EQUATE
, które umożliwiało zdefiniowanie ciągu tekstowego jako zmiennej, a następnie zastępowanie wszelkich wystąpień tej zmiennej w kodzie ciągiem literalnym podczas kompilacji. Jest to podobne do const
w C.
Segmentacja pamięci
Klasyczny HP 3000 zorganizował pamięć fizyczną w 1, 2, 4, 8 lub 16 banków 64K (65536) 16-bitowych słów (128K bajtów). Kod (współdzielony, niemodyfikowalny) i dane były od siebie oddzielone i przechowywane w segmentach o zmiennej długości, z których każdy zawierał do 32 000 słów. W ramach procesu adresy danych, takie jak wskaźniki, były 16-bitowymi przesunięciami od podstawowego rejestratora (znanego jako DB) lub względnymi przesunięciami od rejestru wskaźników (Q lub S), co skutkowało prawidłowym adresem w obszarze danych procesu (tzw. stos). Chociaż głównie 16-bitowy zorientowany na słowa, system obsługiwał adresowanie poszczególnych bajtów w tablicy przy użyciu adresu słowa, w którym bajt był przechowywany, przesuniętego w lewo o 1 bit, a następnie dodania 0, aby uzyskać dostęp do górnego bajtu lub 1 dla dolnego bajtu. Zatem adresy bajtowe były oddzielne i różne od adresów słownych, a interpretacja adresu była czysto kontekstowa. Okazało się to dość kłopotliwe i było źródłem wielu błędów zarówno w kodzie systemowym, jak i użytkownika. Należało zachować ostrożność, aby traktować adresy bajtowe jako 16-bitowe bez znaku (to znaczy typu LOGICAL), gdy używa się ich na przykład w obliczeniach długości, ponieważ w przeciwnym razie adres bajtowy 2^16 lub więcej byłby traktowany jako adres 2s- uzupełnienie wartości ze znakiem, co skutkuje błędnym obliczeniem długości lub przesunięć.
Pojedynczy proces miał dostęp do maksymalnie 254 segmentów kodu, z których każdy zawierał do 32 000 słów. Segmenty kodu zostały podzielone na dwie domeny: pierwsze 191 to segmenty „systemowe” współdzielone przez wszystkie procesy, a pozostałe 63 to segmenty „użytkownikowe” współdzielone przez wszystkie uruchamiające ten sam program. Przekazanie sterowania określało albo numer procedury w ramach bieżącego segmentu, albo numer segmentu zewnętrznego i numer procedury w ramach tego segmentu. Mała tabela na końcu segmentu zawierała adres punktu wejścia dla procedury.
Kod procesu działał na danych w stosie, pojedynczym segmencie prywatnym, również o rozmiarze do 32 000 słów. W przeciwieństwie do stosów w innych architekturach, stos HP 3000 był używany do globalnych procesów, zachowania stanu, lokalnych procedur (obsługa zagnieżdżonych wywołań i ponownego wejścia) oraz obliczeń/ocen numerycznych. System operacyjny zapewniał ułatwienia dostępu do dodatkowych segmentów danych opartych na pamięci (niestosowych), ale nie były one natywnie adresowane przez zestaw instrukcji, więc program był odpowiedzialny za przenoszenie danych zi do takich „dodatkowych segmentów danych” w razie potrzeby .
SPL zawierał różne systemy wsparcia, aby umożliwić łatwą segmentację programów, a następnie uczynić tę segmentację względnie niewidoczną w kodzie. Podstawowym mechanizmem było użycie dyrektywy kompilatora $CONTROL SEGMENT=asegmentname
, która definiowała segment, w którym należy umieścić następujący kod. Domyślnie był to MAINLINESEG
, ale programista mógł dodać dowolną liczbę dodatkowych nazwanych segmentów, aby uporządkować kod w bloki.
Inne funkcje
SPL zawierał funkcję „wyodrębniania bitów”, która umożliwiała uproszczone manipulowanie bitami . Dostęp do dowolnego bitu lub ciągu bitów w słowie można uzyskać za pomocą .(x:y)
, gdzie x i y były początkowymi i końcowymi pozycjami bitów od 0 do 15. (Co istotne, x i y muszą być znanymi stałymi w czasie kompilacji.) W ten sposób A.(8:15)
zwrócił niższy bajt słowa przechowującego A. Ten format może być używany do dzielenia i łączenia bitów w razie potrzeby. Dodatkowo zapewniono dodatkowe operacje dla przesunięć i obrotów, które można było zastosować do dowolnej zmiennej z &
, na przykład A:=A & LSR(3)
.
Przykład
Ten prosty program z wersji podręcznika z 1984 roku pokazuje większość cech języka SPL.
Program jako całość jest podzielony między POCZĄTEK
i KONIEC.
. Zaczyna się od zdefiniowania szeregu zmiennych globalnych A, B i C, definiuje pojedynczą procedurę, a następnie wywołuje ją dwadzieścia razy. Należy zauważyć, że procedura nie ma własnego BEGIN i END, ponieważ zawiera tylko jedną linię rzeczywistego kodu, X:=X*(Y+Z);
INTEGER X,Y,Z
nie jest uważana za część samego kodu, wskazuje typ trzech parametrów przekazywanych w powyższym wierszu i jest uważana za część tego wiersza.
POCZĄTEK LICZBY CAŁKOWITEJ A:=0, B, C:=1; PROCEDURA N(X,Y,Z); LICZBA CAŁKOWITA X, Y, Z; X:=X*(Y+Z); DLA B:=1 DO 20 WYKONAJ N(A,B,C); KONIEC.
Cytaty
Bibliografia
- Edler, Christopher (listopad 1995). „Najsilniejszy zamek: powstanie, upadek i powstanie HP 3000” . Silnik analityczny . 3 (1). ISSN 1071-6351 . Zarchiwizowane od oryginału w dniu 3 lutego 2002 r.
- Zielony, Bob (2004). Ewolucja HP3000 . Robelle .
- Podręcznik dotyczący języka programowania systemów (PDF) . Hewlett Packard. luty 1984.