29.09.2024
Mem uznający język asemblera za najlepszy język programowania skłania do przyjrzenia się językom maszynowym i asemblerowym.
Mem uznający język asemblera za najlepszy język programowania skłania do przyjrzenia się językom maszynowym i asemblerowym.
Na Facebooku odbyła się dyskusja na temat różnych języków programowania, w tym języka asemblera, i ich wartości w świecie programowania.
Dyskusja została zainspirowana memem przedstawiającym „Język asemblera” na stronie, z „C” i „C++” po obu stronach i wieloma innymi językami kłaniającymi się im.
Ten mem stał się inspiracją dla publikacji debatujących nad wartością i zastosowaniami różnych języków. W dyskusjach pojawiło się wiele nieporozumień, które mam nadzieję wyjaśnić w prosty sposób.
Po pierwsze, język maszynowy to jedynki i zera, które są przesyłane do jednostki sterującej (CU) procesora (CPU). W miarę upływu czasu instrukcje są pobierane z pamięci i przedstawiane jednostce centralnej do wykonania. Część instrukcji mówi jednostce centralnej, co ma zrobić; inne części instrukcji zazwyczaj mówią jednostce centralnej, skąd pobrać lub umieścić dane lub jak traktować dane.
W komputerach o zredukowanym zestawie instrukcji (RISC) na cykl zegara przypada zazwyczaj jedna instrukcja, która mówi jednostce centralnej, co ma robić. Niektóre z najwcześniejszych komputerów były komputerami RISC, ponieważ dość łatwo było je zbudować z komponentów dyskretnych (przekaźników, lamp, tranzystorów dyskretnych) tamtych czasów.
Ponieważ ludzie nie są zbyt dobrzy w zapamiętywaniu jedynek i zer, wymyślono mnemotechniki (takie jak ADD) do przedstawiania grup jedynek i zer zamiast konieczności zapamiętywania jedynek i zer (być może 0110) samej instrukcji. Jeśli instrukcja miała dodawać (ADD) rejestr ONE do rejestru TWO, instrukcja może być zakodowana jako ADD R2, R1 zamiast 0110 010 001, ale zazwyczaj wczesne języki asemblera (różniące się od jedynek i zer języków maszynowych) byłyby jedną instrukcją mnemoniczną asemblera dla jednej instrukcji języka maszynowego.
Asembler (program konwertujący język asemblera na język maszynowy) dodałby inne funkcje. Na przykład liczyłby długość instrukcji języka maszynowego i śledziłby adres, pod którym rozpoczynałaby się każda instrukcja. Zamiast przeskakiwać do jakiegoś adresu binarnego w przeszłości lub przyszłości, można było przeskoczyć do etykiety, a asembler śledziłby, gdzie znajduje się ta etykieta. Używając etykiet określających lokalizację danych, można było teraz uzyskać dostęp do wartości w pamięci głównej, nie wiedząc, gdzie te dane będą się znajdować w czasie wykonywania. Z biegiem czasu asembler pozwoli programowi na relokację w pamięci zamiast utrwalania go w określonym miejscu (jak miałoby to miejsce w przypadku zakodowania go w stałych jedynkach i zerach).
Do tej pory mówiłem o jednej instrukcji języka asemblera dla jednej instrukcji języka maszynowego, ale w końcu pojawiła się nowa koncepcja zwana „makrami”, która pozwoliła programiście grupować instrukcje, które mogły być używane wielokrotnie, tak że jedna instrukcja makra mogła generować wiele instrukcji języka asemblera i ostatecznie pozwalała na podstawienia w wykonaniu makra, które rozszerzały się na rzeczywiste instrukcje w miarę ich rozszerzania. Mimo to programista miał pełną kontrolę. Wiedział, jak makro będzie się rozwijać i jakie instrukcje będzie generować. Języki asemblera/maszynowe nie były przenośne. Zostały stworzone dla jednej architektury maszyny.
Były też „pseudoinstrukcje”. Te instrukcje asemblera nie pasowałyby do żadnych instrukcji języka maszynowego. Przekazywałyby one asemblerowi informacje o tym, jak przetłumaczyć asembler na język maszynowy. Pseudoinstrukcje mogą informować asembler, w jakim formacie przechowywać dane (liczba całkowita, bajt, liczba zmiennoprzecinkowa itp.) lub podawać mnożnik danych, dzięki czemu wystarczy zakodować (na przykład) 100 DATA 11011010, zamiast kodować DATA 11011010 100 razy.
Wreszcie, asemblery wyznaczały zmienne „lokalne” i „globalne”, które umożliwiały innym modułom dostęp do miejsc pobierania danych lub przeskakiwania w celu wykonania kodu poza pisanym modułem. Umożliwiło to pisanie funkcji i podprogramów, co ostatecznie doprowadziło do powstania bibliotek podprogramów.
Do tej pory mówiłem głównie o komputerach RISC, w których każda instrukcja języka maszynowego wykonuje jedną konkretną czynność w każdym cyklu zegara maszyny. Sir Maurice Wilkes zasugerował, że komputery ze złożonym zestawem instrukcji (CISC) mogą być bardziej wydajne poprzez tworzenie instrukcji, które wykonują serię kroków, zwanych „mikrokodem”, dostosowanych do określonej klasy problemów.
Programista nie mógł uzyskać dostępu do mikrokodu, a jedynie do instrukcji języka asemblera wyższego poziomu. Uważano, że dzięki temu kodowanie w języku maszynowym będzie znacznie łatwiejsze, a wynikowy kod będzie działał na maszynie znacznie szybciej.
Zarówno asemblery RISC, jak i CISC były uważane za programowanie „niskopoziomowe”. Języki „wysokopoziomowe” obejmowały FORTRAN, COBOL, C++, Lisp i wiele innych. Nawet C jest uważany za język wysokiego poziomu, choć odsłaniający znaczną część architektury maszyny. nnn
Jon „maddog” Hall jest autorem, wykładowcą, informatykiem i jednym z pionierów Wolnego Oprogramowania. Od 1994 roku, kiedy po raz pierwszy spotkał Linusa Torvaldsa i ułatwił przeniesienie jądra na systemy 64-bitowe, pozostaje orędownikiem Linuksa. Obecnie jest prezesem Linux International®.