Język programowania - Assembly language

język programowania
Motorola 6800 Asembler Language.png
Typowe wtórne wyjście z asemblera — pokazujący oryginalny język asemblera (po prawej) dla Motoroli MC6800 i złożonej formy
Paradygmat Bezwzględny , nieustrukturyzowany
Po raz pierwszy pojawiły się 1949 ; 72 lata temu ( 1949 )

W programowaniu komputerowym , asemblerze (lub asemblerze ), czasami skróconą ASM to dowolny język niskiego poziomu , w którym istnieje bardzo silny związek między instrukcję języka i architektura w kodzie maszynowym instrukcji . Ponieważ asembler zależy od instrukcji kodu maszynowego, każdy język asemblera jest zaprojektowany dla dokładnie jednej określonej architektury komputera. Język asemblera może być również nazywany symbolicznym kodem maszynowym .

Kod asemblera jest konwertowany na wykonywalny kod maszynowy przez program narzędziowy zwany asemblerem . Proces konwersji jest określany jako zespół , jak w montażu z kodu źródłowego . Język asemblerowy zwykle ma jedną instrukcję na instrukcję maszynową (1:1), ale stałe, komentarze , dyrektywy asemblera , symboliczne etykiety programów i lokalizacji pamięci oraz makra są ogólnie obsługiwane.

Termin „asembler” jest ogólnie przypisywany Wilkesowi , Wheelerowi i Gillowi w ich książce „Przygotowanie programów dla elektronicznego komputera cyfrowego” z 1951 roku , którzy jednak używali tego terminu w znaczeniu „programu, który składa inny program składający się z kilku części w jeden pojedynczy program".

Każdy język asemblera jest specyficzny dla konkretnej architektury komputera i czasami dla systemu operacyjnego . Jednak niektóre języki asemblerowe nie dostarczają specyficznej składni dla wywołań systemu operacyjnego, a większość języków asemblerowych może być używana uniwersalnie z dowolnym systemem operacyjnym, ponieważ język zapewnia dostęp do wszystkich rzeczywistych możliwości procesora , na których ostatecznie opierają się wszystkie mechanizmy wywołań systemowych . W przeciwieństwie do języków asemblerowych, większość języków programowania wysokiego poziomu jest ogólnie przenośna w wielu architekturach, ale wymaga interpretacji lub kompilacji , co jest znacznie bardziej skomplikowanym zadaniem niż asemblacja.

Krok obliczeniowy, kiedy asembler przetwarza program, nazywa się czasem asemblacji .

Składnia języka asemblera

Język asemblera używa mnemonika do reprezentowania każdej niskopoziomowej instrukcji maszynowej lub kodu operacyjnego , zazwyczaj również każdego rejestru architektonicznego , flagi , itp. Wiele operacji wymaga jednego lub więcej operandów w celu utworzenia kompletnej instrukcji. Większość asemblerów zezwala na nazwane stałe, rejestry i etykiety dla programów i lokalizacji pamięci oraz może obliczać wyrażenia dla operandów. W ten sposób programiści są uwolnieni od żmudnych, powtarzalnych obliczeń, a programy asemblerowe są znacznie bardziej czytelne niż kod maszynowy. W zależności od architektury, elementy te mogą być również łączone w celu uzyskania określonych instrukcji lub trybów adresowania z wykorzystaniem przesunięć lub innych danych, jak również stałych adresów. Wiele asemblerów oferuje dodatkowe mechanizmy ułatwiające rozwój programu, kontrolujące proces asemblacji i wspomagające debugowanie .

Terminologia

  • Makro monter jest monter, który zawiera makroinstrukcję obiektu tak, że (sparametryzowany) tekst asemblera może być reprezentowane przez nazwy, a nazwa ta może być używany do wprowadzania tekstu w rozszerzoną inny kod.
  • Asembler krzyżowy (zobacz także kompilator krzyżowy ) to asembler, który jest uruchamiany na komputerze lub systemie operacyjnym (systemie hosta ) innego typu niż system, na którym ma działać wynikowy kod ( system docelowy ). Cross-asembling ułatwia tworzenie programów dla systemów, które nie mają zasobów do wspierania rozwoju oprogramowania, takich jak system wbudowany lub mikrokontroler . W takim przypadku wynikowy kod obiektowy musi zostać przesłany do systemu docelowego poprzez pamięć tylko do odczytu (ROM, EPROM itp.), programator (gdy pamięć tylko do odczytu jest zintegrowana z urządzeniem, tak jak w mikrokontrolerach) , lub łącze danych wykorzystujące dokładną kopię bit po bicie kodu wynikowego lub tekstową reprezentację tego kodu (np. Intel hex lub Motorola S-record ).
  • Asembler wysokiego poziomu to program, który dostarcza abstrakcje językowe częściej kojarzone z językami wysokiego poziomu, takie jak zaawansowane struktury kontrolne ( IF/THEN/ELSE , DO CASE, itp.) i abstrakcyjne typy danych wysokiego poziomu, w tym struktury/ rekordy, związki, klasy i zbiory.
  • Microassembler to program, który pomaga przygotować mikroprogramowanych o nazwie firmware , aby kontrolować działania niskiego poziomu komputera.
  • Meta-assembler jest „program, który akceptuje składniowej i semantycznej opis w języku asemblera i generuje asemblera dla tego języka”. Asemblery „Meta-Symbol” dla komputerów serii SDS 9 i SDS Sigma są meta-asemblerami. Sperry Univac dostarczył również Meta-Assembler dla serii UNIVAC 1100/2200 .
  • asembler inline (lub osadzony asembler ) to kod asemblera zawarty w programie w języku wysokiego poziomu. Jest to najczęściej stosowane w programach systemowych, które wymagają bezpośredniego dostępu do sprzętu.

Kluczowe idee

Monter

Program asemblera tworzy kod obiektowy , tłumacząc kombinacje mnemoników i składni dla operacji i trybów adresowania na ich odpowiedniki numeryczne. Ta reprezentacja zazwyczaj zawiera kod operacji („ kod operacyjny ”), a także inne bity sterujące i dane. Asembler oblicza również stałe wyrażenia i rozwiązuje symboliczne nazwy dla lokalizacji pamięci i innych jednostek. Stosowanie odniesień symbolicznych jest kluczową cechą asemblerów, pozwalającą na zaoszczędzenie żmudnych obliczeń i ręcznej aktualizacji adresów po modyfikacjach programu. Większość asemblerów zawiera również udogodnienia makr do wykonywania podstawień tekstowych – np. do generowania wspólnych krótkich sekwencji instrukcji jako inline , zamiast zwanych podprogramów .

Niektóre asemblery mogą również być w stanie wykonać pewne proste typy optymalizacji specyficznych dla zestawu instrukcji . Jednym z konkretnych przykładów mogą być wszechobecne assemblery x86 od różnych dostawców. Nazywane skokiem wielkości , większość z nich jest w stanie na żądanie wykonać zamianę instrukcji skoku (skoki długie zastępowane skokami krótkimi lub względnymi) w dowolnej liczbie przebiegów. Inne mogą nawet przeprowadzać proste przegrupowanie lub wstawianie instrukcji, na przykład niektóre asemblery dla architektur RISC, które mogą pomóc zoptymalizować rozsądne planowanie instrukcji w celu jak najefektywniejszego wykorzystania potoku procesora .

Assemblery są dostępne od lat pięćdziesiątych jako pierwszy krok ponad język maszynowy i przed językami programowania wysokiego poziomu, takimi jak Fortran , Algol , COBOL i Lisp . Istnieje również kilka klas translatorów i półautomatycznych generatorów kodu o właściwościach podobnych do języków asemblera i języków wysokiego poziomu, przy czym Speedcode jest prawdopodobnie jednym z bardziej znanych przykładów.

Może istnieć kilka asemblerów o różnej składni dla określonej architektury procesora lub zestawu instrukcji . Na przykład, instrukcja dodawania danych pamięci do rejestru w procesorze z rodziny x86 mogłaby być add eax,[ebx]w oryginalnej składni Intela , podczas gdy byłaby napisana addl (%ebx),%eaxw składni AT&T używanej przez GNU Assembler . Pomimo różnych wyglądów, różne formy składniowe zazwyczaj generują ten sam numeryczny kod maszynowy . Pojedynczy asembler może również mieć różne tryby w celu obsługi wariacji form składniowych, jak również ich dokładnych interpretacji semantycznych (takich jak FASM -składnia, TASM -składnia, tryb idealny itp., w szczególnym przypadku programowania w asemblerze x86 ).

Liczba przejazdów

Istnieją dwa typy asemblerów w zależności od tego, ile przejść przez źródło jest potrzebnych (ile razy asembler czyta źródło) do wytworzenia pliku obiektowego.

  • Asemblery jednoprzebiegowe przechodzą przez kod źródłowy raz. Każdy symbol użyty przed zdefiniowaniem będzie wymagał „erraty” na końcu kodu wynikowego (lub przynajmniej nie wcześniej niż w miejscu, w którym symbol jest zdefiniowany) mówiącego linkerowi lub ładującemu, aby „wrócił” i nadpisał symbol zastępczy, który został pozostawiony w miejscu, w którym użyto jeszcze niezdefiniowanego symbolu.
  • Asemblery wieloprzebiegowe tworzą tabelę ze wszystkimi symbolami i ich wartościami w pierwszych przejściach, a następnie używają tabeli w kolejnych przejściach do generowania kodu.

W obu przypadkach asembler musi być w stanie określić rozmiar każdej instrukcji w początkowych przejściach, aby obliczyć adresy kolejnych symboli. Oznacza to, że jeśli rozmiar operacji odnoszącej się do operandu zdefiniowanego później zależy od typu lub odległości operandu, asembler dokona pesymistycznego oszacowania przy pierwszym napotkaniu operacji i jeśli to konieczne, uzupełni ją jednym lub więcej " nie " -operacja " instrukcje w późniejszym przejściu lub errata. W asemblerze z optymalizacją peephole , adresy mogą być przeliczane między przejściami, aby umożliwić zastąpienie pesymistycznego kodu kodem dostosowanym do dokładnej odległości od celu.

Pierwotnym powodem stosowania jednoprzebiegowych asemblerów był rozmiar pamięci i szybkość asemblacji – często drugi przebieg wymagałby przechowywania tablicy symboli w pamięci (w celu obsługi odwołań do przodu ), przewijania i ponownego czytania źródła programu na taśmie lub ponownego odczytu talia kart lub dziurkowana taśma papierowa . Późniejsze komputery ze znacznie większą pamięcią (zwłaszcza pamięcią dyskową) miały miejsce na wykonanie wszystkich niezbędnych operacji bez takiego ponownego czytania. Zaletą asemblera wieloprzebiegowego jest to, że brak erraty przyspiesza proces łączenia (lub ładowanie programu, jeśli asembler bezpośrednio tworzy kod wykonywalny).

Przykład: w poniższym fragmencie kodu jednoprzebiegowy asembler byłby w stanie określić adres odwołania wstecznego BKWD podczas składania instrukcji S2 , ale nie byłby w stanie określić adresu odwołania do przodu FWD podczas składania instrukcji rozgałęzienia S1 ; w rzeczywistości FWD może być niezdefiniowane. Asembler dwuprzebiegowy określi oba adresy w pierwszym przejściu, więc będą one znane podczas generowania kodu w drugim przejściu.

S1   B    FWD
  ...
FWD   EQU *
  ...
BKWD  EQU *
  ...
S2    B   BKWD

Monterzy wysokiego szczebla

Bardziej wyrafinowane asemblery wysokiego poziomu zapewniają abstrakcje językowe, takie jak:

  • Deklaracje i wywołania procedur/funkcji wysokiego poziomu
  • Zaawansowane struktury kontrolne (IF/THEN/ELSE, SWITCH)
  • Abstrakcyjne typy danych wysokiego poziomu, w tym struktury/rekordy, związki, klasy i zbiory
  • Zaawansowane przetwarzanie makr (chociaż dostępne w zwykłych asemblerach od późnych lat pięćdziesiątych dla np. serii IBM 700 i serii IBM 7000 , a od lat sześćdziesiątych dla IBM System/360 (S/360) między innymi)
  • Funkcje programowania obiektowego, takie jak klasy , obiekty , abstrakcja , polimorfizm i dziedziczenie

Zobacz Projekt języka poniżej, aby uzyskać więcej informacji.

język programowania

Program napisany w języku asemblerowym składa się z serii instrukcji procesora mnemonicznego i meta-instrukcji (znanych jako dyrektywy, pseudoinstrukcje i pseudooperacje), komentarzy i danych. Instrukcje języka asemblerowego zwykle składają się z mnemonika kodu operacji , po którym następuje operand , który może być listą danych, argumentów lub parametrów. Niektóre instrukcje mogą być „dorozumiane”, co oznacza, że ​​dane, na których działa instrukcja, są domyślnie zdefiniowane przez samą instrukcję — taka instrukcja nie przyjmuje argumentu. Wynikowa instrukcja jest tłumaczona przez asembler na instrukcje języka maszynowego, które można załadować do pamięci i wykonać.

Na przykład poniższa instrukcja mówi procesorowi x86 / IA-32 , aby przeniósł natychmiast 8-bitową wartość do rejestru . Kod binarny dla tej instrukcji jest 10110, a następnie przez identyfikator 3-bitowym, dla którego rejestrację w użyciu. Identyfikator dla rejestru AL to 000, więc poniższy kod maszynowy ładuje do rejestru AL dane 01100001.

10110000 01100001

Ten binarny kod komputerowy można uczynić bardziej czytelnym dla człowieka, wyrażając go w systemie szesnastkowym w następujący sposób.

B0 61

Tutaj B0oznacza „Przenieś kopię następującej wartości do AL i 61jest szesnastkową reprezentacją wartości 01100001, która jest liczbą dziesiętną 97 . Język asemblerowy dla rodziny 8086 dostarcza mnemoniczny MOV (skrót od move ) dla instrukcji takich jak ta, więc powyższy kod maszynowy może być napisany w języku asemblerowym w następujący sposób, wraz z komentarzem wyjaśniającym, jeśli jest to wymagane, po średniku. Jest to o wiele łatwiejsze do odczytania i zapamiętania.

MOV AL, 61h       ; Load AL with 97 decimal (61 hex)

W niektórych językach asemblerowych (w tym w tym) ten sam mnemonik, taki jak MOV, może być użyty dla rodziny powiązanych instrukcji do ładowania, kopiowania i przenoszenia danych, niezależnie od tego, czy są to wartości bezpośrednie, wartości w rejestrach, czy lokalizacje pamięci wskazywane przez wartości w rejestrach lub adresach bezpośrednich (inaczej bezpośrednich). Inne asemblery mogą używać oddzielnych mnemoników opcode, takich jak L dla "przenieś pamięć do rejestru", ST dla "przenieś rejestr do pamięci", LR dla "przenieś rejestr do rejestru", MVI dla "przenieś natychmiastowy operand do pamięci" itp.

Jeśli ten sam mnemonik jest używany dla różnych instrukcji, oznacza to, że mnemonik odpowiada kilku różnym kodom instrukcji binarnych, z wyłączeniem danych (np. 61hw tym przykładzie), w zależności od argumentów następujących po mnemoniku. Na przykład, dla procesorów x86/IA-32, składnia języka asemblera Intel MOV AL, AHreprezentuje instrukcję, która przenosi zawartość rejestru AH do rejestru AL . Szesnastkowa forma tej instrukcji to:

88 E0

Pierwszy bajt, 88h, identyfikuje ruch między rejestrem wielkości bajtów a innym rejestrem lub pamięcią, a drugi bajt, E0h, jest zakodowany (z trzema polami bitowymi), aby określić, że oba operandy są rejestrami, źródłem jest AH , a miejscem docelowym jest AL .

W takim przypadku, gdy ten sam mnemonik może reprezentować więcej niż jedną instrukcję binarną, asembler określa, którą instrukcję wygenerować, badając operandy. W pierwszym przykładzie operand 61hjest poprawną szesnastkową stałą liczbową i nie jest poprawną nazwą rejestru, więc tylko B0instrukcja może mieć zastosowanie. W drugim przykładzie operand AHjest prawidłową nazwą rejestru, a nie prawidłową stałą numeryczną (szesnastkową, dziesiętną, ósemkową lub binarną), więc tylko 88instrukcja może mieć zastosowanie.

Języki asemblerowe są zawsze projektowane tak, aby ten rodzaj jednoznaczności był powszechnie wymuszony przez ich składnię. Na przykład, w języku asemblerowym Intel x86, stała szesnastkowa musi zaczynać się od cyfry, tak aby liczba szesnastkowa 'A' (równa dziesiętnej dziesiątce) była zapisywana jako 0Ahlub 0AH, nie AH, konkretnie, aby nie wyglądała jak nazwa rejestru AH . (Ta sama zasada zapobiega również niejednoznaczności z nazwami rejestrów BH , CH i DH , a także z dowolnym symbolem zdefiniowanym przez użytkownika, który kończy się na literę H i zawiera tylko znaki będące cyframi szesnastkowymi, takie jak słowo „BEACH ".)

Wracając do oryginalnego przykładu, podczas gdy kod operacji x86 10110000 ( B0) kopiuje 8-bitową wartość do rejestru AL , 10110001 ( B1) przenosi ją do CL, a 10110010 ( B2) robi to do DL . Poniżej znajdują się przykłady języka asemblera.

MOV AL, 1h        ; Load AL with immediate value 1
MOV CL, 2h        ; Load CL with immediate value 2
MOV DL, 3h        ; Load DL with immediate value 3

Składnia MOV może być również bardziej złożona, jak pokazują poniższe przykłady.

MOV EAX, [EBX]	  ; Move the 4 bytes in memory at the address contained in EBX into EAX
MOV [ESI+EAX], CL ; Move the contents of CL into the byte at address ESI+EAX
MOV DS, DX        ; Move the contents of DX into segment register DS

W każdym przypadku mnemonik MOV jest tłumaczony bezpośrednio na jeden z opkodów 88-8C, 8E, A0-A3, B0-BF, C6 lub C7 przez asembler, a programista zwykle nie musi wiedzieć ani pamiętać którego.

Przekształcenie języka asemblera w kod maszynowy jest zadaniem asemblera, a odwrotność może przynajmniej częściowo zostać osiągnięta przez deasembler . W przeciwieństwie do języków wysokiego poziomu istnieje zależność jeden do jednego między wieloma prostymi instrukcjami asemblera i instrukcjami języka maszynowego. Jednak w niektórych przypadkach asembler może dostarczać pseudoinstrukcje (zasadniczo makra), które rozwijają się do kilku instrukcji języka maszynowego, aby zapewnić powszechnie potrzebną funkcjonalność. Na przykład, dla maszyny, która nie ma instrukcji "rozgałęzienia, jeśli większe lub równe", asembler może dostarczyć pseudoinstrukcję, która rozwija się do "ustaw, jeśli mniejszy niż" i "rozgałęzienia, jeśli zero (w wyniku instrukcji set)" . Większość w pełni funkcjonalnych asemblerów zapewnia również bogaty język makr (omówiony poniżej), który jest używany przez dostawców i programistów do generowania bardziej złożonego kodu i sekwencji danych. Ponieważ informacje o pseudoinstrukcji i makrach zdefiniowanych w środowisku asemblera nie są obecne w programie obiektowym, deasembler nie może zrekonstruować wywołań makr i pseudoinstrukcji, ale może jedynie zdeasemblować rzeczywiste instrukcje maszynowe, które asembler wygenerował z tych abstrakcyjnych jednostek języka asemblera. Podobnie, ponieważ komentarze w pliku źródłowym języka asemblera są ignorowane przez asembler i nie mają wpływu na generowany przez niego kod wynikowy, deasembler jest zawsze całkowicie niezdolny do odzyskania komentarzy źródłowych.

Każda architektura komputera ma swój własny język maszynowy. Komputery różnią się liczbą i typem obsługiwanych operacji, rozmiarami i liczbą rejestrów oraz reprezentacją danych w pamięci. Podczas gdy większość komputerów ogólnego przeznaczenia jest w stanie realizować zasadniczo te same funkcje, sposoby, w jakie to robią, różnią się; odpowiednie języki asemblera odzwierciedlają te różnice.

Wiele zestawów mnemotechnik lub składni języka asemblera może istnieć dla pojedynczego zestawu instrukcji, zazwyczaj tworzonego w różnych programach asemblera. W takich przypadkach najpopularniejszym jest zwykle ten dostarczany przez producenta procesora i używany w jego dokumentacji.

Dwa przykłady procesorów, które mają dwa różne zestawy mnemoników, to rodzina Intel 8080 i Intel 8086/8088. Ponieważ Intel rościł sobie prawa autorskie do swoich mnemotechnik języka asemblerowego (przynajmniej na każdej stronie swojej dokumentacji opublikowanej w latach 70. i wczesnych 80.), niektóre firmy, które niezależnie produkowały procesory kompatybilne z zestawami instrukcji Intela, wymyśliły własne mnemoniki. Zilog Z80 CPU, akcesorium z Intel 8080A , wspiera wszystkie instrukcje 8080A plus wiele więcej; Zilog wynalazł całkowicie nowy język asemblera, nie tylko dla nowych instrukcji, ale także dla wszystkich instrukcji 8080A. Na przykład, gdy Intel wykorzystuje mnemotechniki MOV , MVI , LDA , STA , LXI , LDAX , Stax , LHLD i SHLD dla różnych instrukcji transferu danych, Z80 montaż język używa mnemoniczny LD dla nich wszystkich. Podobny przypadek to procesory NEC V20 i V30 , odpowiednio ulepszone kopie Intel 8086 i 8088. Podobnie jak Zilog w Z80, NEC wynalazł nowe mnemoniki dla wszystkich instrukcji 8086 i 8088, aby uniknąć oskarżeń o naruszenie praw autorskich Intela. (Wątpliwe jest, czy takie prawa autorskie mogą być ważne, a późniejsze firmy produkujące procesory, takie jak AMD i Cyrix, ponownie opublikowały mnemoniki instrukcji Intel x86/IA-32 bez pozwolenia ani kary prawnej.) Wątpliwe jest, czy w praktyce wielu ludzi, którzy zaprogramowali V20 a V30 faktycznie pisał w asemblerze NEC, a nie w Intelu; ponieważ dowolne dwa języki asemblera dla tej samej architektury zestawu instrukcji są izomorficzne (trochę jak angielski i Pig Latin ), nie ma wymogu używania własnego opublikowanego języka asemblera z produktami tego producenta.

Projektowanie języka

Podstawowe elementy

Istnieje duży stopień różnorodności w sposobie kategoryzowania wypowiedzi przez autorów asemblerów oraz stosowanej przez nich nomenklaturze. W szczególności, niektórzy opisują wszystko inne niż mnemonik maszynowy lub mnemonik rozszerzony jako pseudooperację (pseudo-operację). Typowy język asemblerowy składa się z 3 typów instrukcji instrukcji, które są używane do definiowania operacji programu:

  • oPCODE mnemoniki
  • Definicje danych
  • Wytyczne montażowe

Mnemotechnika opcode i rozszerzona mnemonika

Instrukcje (instrukcje) w asemblerze są generalnie bardzo proste, w przeciwieństwie do tych w językach wysokiego poziomu . Ogólnie rzecz biorąc, mnemonik to nazwa symboliczna dla pojedynczej wykonywalnej instrukcji języka maszynowego ( opcode ), a dla każdej instrukcji języka maszynowego zdefiniowany jest co najmniej jeden mnemonik. Każda instrukcja zazwyczaj składa się z operacji lub kodu operacji plus zero lub więcej operandów . Większość instrukcji odnosi się do pojedynczej wartości lub pary wartości. Argumenty mogą być natychmiastowe (wartość zakodowana w samej instrukcji), rejestry określone w instrukcji lub domniemane lub adresy danych znajdujących się w innym miejscu pamięci. Jest to określone przez podstawową architekturę procesora: asembler jedynie odzwierciedla sposób działania tej architektury. Rozszerzone mnemoniki są często używane do określenia kombinacji opkodu z określonym operandem, np. asemblery System/360 używają Bjako rozszerzonego mnemotechniki dla BCz maską 15 i NOP(„NO Operation” – nie rób nic dla jednego kroku) dla BCz maska ​​0.

Rozszerzone mnemoniki są często używane do wspierania wyspecjalizowanych zastosowań instrukcji, często do celów nieoczywistych z nazwy instrukcji. Na przykład wiele procesorów nie ma wyraźnej instrukcji NOP, ale ma instrukcje, które mogą być użyte w tym celu. W procesorach 8086 instrukcja jest używana do , będąc pseudo-kodem do kodowania instrukcji . Niektóre deasemblery rozpoznają to i zdekodują instrukcję jako . Podobnie, asemblery IBM dla System/360 i System/370 używają rozszerzonej mnemoniki oraz dla i z zerowymi maskami. W architekturze SPARC są one znane jako instrukcje syntetyczne . xchg ax,axnopnopxchg ax,axxchg ax,axnopNOPNOPRBCBCR

Niektóre asemblery obsługują również proste wbudowane makroinstrukcje, które generują dwie lub więcej instrukcji maszynowych. Na przykład, w niektórych asemblerach Z80 ld hl,bcrozpoznaje się, że instrukcja generuje, ld l,cpo której następuje ld h,b. Są one czasami znane jako pseudo-kody operacyjne .

Mnemoniki to arbitralne symbole; w 1985 roku IEEE opublikował Standard 694 dla jednolitego zestawu mnemoników do użytku przez wszystkich asemblerów. Od tego czasu standard został wycofany.

Dyrektywy danych

Istnieją instrukcje służące do definiowania elementów danych do przechowywania danych i zmiennych. Określają rodzaj danych, długość i wyrównanie danych. Instrukcje te mogą również określać, czy dane są dostępne dla programów zewnętrznych (programów składanych oddzielnie), czy tylko dla programu, w którym zdefiniowana jest sekcja danych. Niektóre asemblery klasyfikują je jako pseudo-operacje.

Wytyczne montażowe

Dyrektywy asemblera, zwane także pseudo-opcodemi, pseudo-operacjami lub pseudo-operacjami, to polecenia wydawane asemblerowi „kierujące go do wykonywania operacji innych niż instrukcje asemblera”. Dyrektywy wpływają na działanie asemblera i "mogą wpływać na kod wynikowy, tablicę symboli, plik listingu i wartości wewnętrznych parametrów asemblera". Czasami termin pseudo-opcode jest zarezerwowany dla dyrektyw generujących kod wynikowy, takich jak te, które generują dane.

Nazwy pseudo-operacji często zaczynają się od kropki, aby odróżnić je od instrukcji maszynowych. Pseudooperacje mogą uzależnić asemblację programu od parametrów wprowadzonych przez programistę, tak że jeden program może być asemblowany na różne sposoby, być może dla różnych zastosowań. Lub pseudooperacja może być użyta do manipulowania prezentacją programu, aby ułatwić jego czytanie i konserwację. Innym powszechnym zastosowaniem pseudooperacji jest rezerwowanie obszarów pamięci dla danych w czasie wykonywania i opcjonalnie inicjowanie ich zawartości do znanych wartości.

Asemblery symboliczne pozwalają programistom kojarzyć dowolne nazwy ( etykiety lub symbole ) z lokalizacjami pamięci i różnymi stałymi. Zwykle każda stała i zmienna ma nazwę, dzięki czemu instrukcje mogą odwoływać się do tych lokalizacji według nazwy, co promuje samodokumentowanie kodu . W kodzie wykonywalnym nazwa każdego podprogramu jest powiązana z jego punktem wejścia, więc każde wywołanie podprogramu może używać jego nazwy. Wewnątrz podprogramów miejsca docelowe GOTO otrzymują etykiety. Niektóre asemblery wspierają lokalne symbole, które są leksykalnie różne od normalnych symboli (np. użycie "10$" jako celu GOTO).

Niektóre asemblery, takie jak NASM , zapewniają elastyczne zarządzanie symbolami, pozwalając programistom zarządzać różnymi przestrzeniami nazw , automatycznie obliczać przesunięcia w strukturach danych i przypisywać etykiety, które odnoszą się do wartości dosłownych lub wyników prostych obliczeń wykonywanych przez asembler. Etykiety mogą być również używane do inicjowania stałych i zmiennych z relokowalnymi adresami.

Języki asemblera , podobnie jak większość innych języków komputerowych, pozwalają na dodawanie komentarzy do kodu źródłowego programu, które będą ignorowane podczas asemblacji. Rozsądne komentowanie jest niezbędne w programach asemblerowych, ponieważ znaczenie i cel sekwencji binarnych instrukcji maszynowych może być trudne do określenia. "Surowy" (niezakomentowany) język asemblerowy generowany przez kompilatory lub deasemblery jest dość trudny do odczytania, gdy trzeba wprowadzić zmiany.

Makra

Wiele asemblerów obsługuje predefiniowane makra , a inne wspierają zdefiniowane przez programistę (i wielokrotnie przedefiniowane) makra obejmujące sekwencje linii tekstu, w których osadzone są zmienne i stałe. Definicja makra jest najczęściej mieszaniną instrukcji asemblera, np. dyrektyw, symbolicznych instrukcji maszynowych i szablonów instrukcji asemblera. Ta sekwencja wierszy tekstu może zawierać kody lub dyrektywy. Po zdefiniowaniu makra jego nazwa może być używana zamiast mnemonika. Kiedy asembler przetwarza taką instrukcję, zastępuje ją liniami tekstu powiązanymi z tym makrem, a następnie przetwarza je tak, jakby istniały w pliku kodu źródłowego (w tym, w niektórych asemblerach, rozwijanie wszelkich makr istniejących w tekście zastępującym) . W tym sensie makra pochodzą z autokoderów IBM z lat pięćdziesiątych.

W języku asemblerowym, termin "makro" reprezentuje bardziej wszechstronną koncepcję niż w niektórych innych kontekstach, takich jak preprocesor w języku programowania C , gdzie jego dyrektywa #define jest zwykle używana do tworzenia krótkich jednoliniowych makr. Instrukcje makr asemblera, podobnie jak makra w PL/I i niektórych innych językach, mogą być długimi "programami" samodzielnie wykonywanymi przez interpretację przez asembler podczas asemblacji.

Ponieważ makra mogą mieć "krótkie" nazwy, ale rozszerzać się do kilku lub nawet wielu linii kodu, mogą być użyte do tego, aby programy w języku asemblerowym wydawały się być znacznie krótsze, wymagając mniej linii kodu źródłowego, jak w przypadku języków wyższego poziomu. Mogą być również używane do dodawania wyższych poziomów struktury do programów asemblera, opcjonalnie wprowadzać osadzony kod debugowania poprzez parametry i inne podobne funkcje.

Asemblery makr często pozwalają makram na przyjmowanie parametrów . Niektóre asemblery zawierają dość wyrafinowane języki makr, zawierające takie elementy języka wysokiego poziomu, jak parametry opcjonalne, zmienne symboliczne, warunki warunkowe, manipulacje łańcuchami i operacje arytmetyczne, wszystkie użyteczne podczas wykonywania danego makra i umożliwiające makram zapisywanie kontekstu lub wymianę informacji . Tak więc makro może generować liczne instrukcje języka asemblerowego lub definicje danych, w oparciu o argumenty makr. Może to być wykorzystane na przykład do generowania struktur danych w stylu rekordów lub „ rozwiniętych ” pętli lub może generować całe algorytmy oparte na złożonych parametrach. Na przykład makro „sort” może akceptować specyfikację złożonego klucza sortowania i generować kod stworzony dla tego konkretnego klucza, bez konieczności przeprowadzania testów w czasie wykonywania, które byłyby wymagane w przypadku ogólnej procedury interpretującej specyfikację. Organizacja używająca języka asemblera, który został mocno rozbudowany przy użyciu takiego zestawu makr może być uważana za pracującą w języku wyższego poziomu, ponieważ tacy programiści nie pracują z elementami koncepcyjnymi najniższego poziomu komputera. Podkreślając ten punkt, makra zostały użyte do zaimplementowania wczesnej maszyny wirtualnej w SNOBOL4 (1967), który został napisany w SNOBOL Implementation Language (SIL), języku asemblera dla maszyny wirtualnej. Maszyna docelowa przetłumaczy to na swój kod natywny za pomocą asemblera makr . Pozwoliło to na wysoki stopień przenośności jak na tamte czasy.

Makra były używane do dostosowywania systemów oprogramowania na dużą skalę dla określonych klientów w erze komputerów mainframe, a także były wykorzystywane przez personel klienta do zaspokajania potrzeb pracodawców poprzez tworzenie określonych wersji systemów operacyjnych producenta. Dokonali tego na przykład programiści systemów pracujący z IBM Conversational Monitor System/Virtual Machine ( VM/CMS ) oraz z dodatkami IBM do „przetwarzania transakcji w czasie rzeczywistym”, Customer Information Control System CICS oraz ACP / TPF , linia lotnicza/system finansowy, który rozpoczął się w latach 70. i nadal obsługuje wiele dużych komputerowych systemów rezerwacji (CRS) i systemów kart kredytowych.

Możliwe jest również wykorzystanie wyłącznie możliwości przetwarzania makr w asemblerze do generowania kodu napisanego w zupełnie innych językach, na przykład do wygenerowania wersji programu w języku COBOL przy użyciu czystego programu asemblera makr zawierającego wiersze kodu w języku COBOL wewnątrz operatorów czasu asemblera nakazanie asemblerowi wygenerowania dowolnego kodu. IBM OS/360 używa makr do generowania systemu . Użytkownik określa opcje, kodując serię makr asemblera. Składanie tych makr generuje strumień zadań do budowy systemu, w tym język sterowania zadaniami i instrukcje sterowania narzędziami .

Dzieje się tak, ponieważ, jak zdano sobie sprawę w latach 60. XX wieku, koncepcja „przetwarzania makr” jest niezależna od koncepcji „asemblacji”, przy czym ta pierwsza oznacza we współczesnych terminach bardziej przetwarzanie tekstu, przetwarzanie tekstu niż generowanie kodu wynikowego. Koncepcja przetwarzania makr pojawiła się i pojawia się w języku programowania C, który obsługuje „instrukcje preprocesora” do ustawiania zmiennych i przeprowadzania warunkowych testów ich wartości. W przeciwieństwie do niektórych poprzednich procesorów makr wewnątrz asemblerów, preprocesor C nie jest kompletny pod względem Turinga, ponieważ nie ma możliwości zapętlenia ani „przechodzenia do”, co pozwala programom na zapętlenie.

Pomimo mocy przetwarzania makr, wyszedł z użycia w wielu językach wysokiego poziomu (główne wyjątki to C , C++ i PL/I), pozostając wiecznym narzędziem dla asemblerów.

Podstawianie parametrów makra odbywa się ściśle według nazwy: w czasie przetwarzania makra wartość parametru jest tekstowo zastępowana jego nazwą. Najsłynniejszą klasą powstałych błędów było użycie parametru, który sam w sobie był wyrażeniem, a nie prostą nazwą, gdy autor makr oczekiwał nazwy. W makrze:

foo: macro a
load a*b

intencją było, aby wywołujący dostarczył nazwę zmiennej, a zmienna „globalna” lub stała b byłaby używana do pomnożenia „a”. Jeśli foo zostanie wywołane z parametrem a-c, load a-c*bnastąpi rozwinięcie makra . Aby uniknąć wszelkich możliwych niejasności, użytkownicy procesorów makr mogą umieszczać w nawiasach parametry formalne w definicjach makr lub wywołujący mogą umieszczać w nawiasach parametry wejściowe.

Wsparcie dla programowania strukturalnego

Napisano pakiety makr, które zapewniają ustrukturyzowane elementy programistyczne do kodowania przepływu wykonania. Najwcześniejszym przykładem tego podejścia był zestaw makr Concept-14 , pierwotnie zaproponowany przez Harlana Millsa (marzec 1970) i ​​zaimplementowany przez Marvina Kesslera w Federal Systems Division firmy IBM, który dostarczał IF/ELSE/ENDIF i podobne bloki przepływu sterowania dla systemu operacyjnego. /360 programów asemblera. Był to sposób na zredukowanie lub wyeliminowanie użycia operacji GOTO w kodzie asemblera, jednego z głównych czynników powodujących kod spaghetti w języku asemblera. To podejście było powszechnie akceptowane we wczesnych latach 80-tych (ostatnie dni używania języka asemblera na dużą skalę). Pakiet IBM High Level Assembler Toolkit zawiera taki pakiet makr.

Ciekawym projektem był A-natural , "zorientowany strumieniowo" asembler dla 8080/ Z80 , procesory firmy Whitesmiths Ltd. (twórcy podobnego do Uniksa systemu operacyjnego Idris , który był uważany za pierwszy komercyjny kompilator C ). Język został sklasyfikowany jako asembler, ponieważ działał z surowymi elementami maszyny, takimi jak kody operacji , rejestry i odwołania do pamięci; ale zawierał składnię wyrażenia, aby wskazać kolejność wykonania. Nawiasy i inne specjalne symbole wraz z blokowymi strukturami programowania sterowały sekwencją generowanych instrukcji. A-natural został zbudowany jako język obiektowy kompilatora C, a nie do ręcznego kodowania, ale jego logiczna składnia zdobyła niektórych fanów.

Nie było widocznego zapotrzebowania na bardziej wyrafinowane asemblery od czasu upadku rozwoju języka asemblera na dużą skalę. Mimo to wciąż są one rozwijane i stosowane w przypadkach, gdy ograniczenia zasobów lub specyfika architektury systemu docelowego uniemożliwiają efektywne wykorzystanie języków wyższego poziomu.

Asemblery z silnym silnikiem makr umożliwiają programowanie strukturalne za pomocą makr, takich jak makro przełącznika dostarczane z pakietem Masm32 (ten kod jest kompletnym programem):

include \masm32\include\masm32rt.inc	; use the Masm32 library

.code
demomain:
  REPEAT 20
	switch rv(nrandom, 9)	; generate a number between 0 and 8
	mov ecx, 7
	case 0
		print "case 0"
	case ecx				; in contrast to most other programming languages,
		print "case 7"		; the Masm32 switch allows "variable cases"
	case 1 .. 3
		.if eax==1
			print "case 1"
		.elseif eax==2
			print "case 2"
		.else
			print "cases 1 to 3: other"
		.endif
	case 4, 6, 8
		print "cases 4, 6 or 8"
	default
		mov ebx, 19		     ; print 20 stars
		.Repeat
			print "*"
			dec ebx
		.Until Sign?		 ; loop until the sign flag is set
	endsw
	print chr$(13, 10)
  ENDM
  exit
end demomain

Używanie języka asemblera

Perspektywa historyczna

Języki asemblera nie były dostępne w czasie, gdy komputer z przechowywanymi programami został wprowadzony. Kathleen Booth „przypisuje się wynalezienie języka asemblera” opartego na pracach teoretycznych, które rozpoczęła w 1947 roku, pracując nad ARC2 w Birkbeck na Uniwersytecie Londyńskim po konsultacji Andrew Bootha (później jej męża) z matematykiem Johnem von Neumannem i fizykiem Hermanem Goldstine w Institute for Advanced Study .

Pod koniec 1948 r. elektroniczny kalkulator automatycznego opóźnionego przechowywania (EDSAC) miał asembler (o nazwie „wstępne zamówienia”) zintegrowany z programem ładowania początkowego . Używał jednoliterowych mnemotechnik opracowanych przez Davida Wheelera , który jest uznawany przez IEEE Computer Society za twórcę pierwszego „asemblera”. Sprawozdania dotyczące EDSAC wprowadziły pojęcie „asembler” dla procesu łączenia pól w słowo rozkazowe. SOAP ( Symbolic Optimal Assembly Program ) był językiem asemblera dla komputera IBM 650 napisanym przez Stana Poleya w 1955 roku.

Języki asemblera eliminują wiele podatnego na błędy, żmudnego i czasochłonnego programowania pierwszej generacji potrzebnego w najwcześniejszych komputerach, uwalniając programistów od nudy, takiej jak zapamiętywanie kodów numerycznych i obliczanie adresów.

Języki asemblera były kiedyś powszechnie używane do wszelkiego rodzaju programowania. Jednak w latach 80. (1990 na mikrokomputerach ) ich użycie zostało w dużej mierze wyparte przez języki wyższego poziomu w poszukiwaniu lepszej produktywności programowania . Obecnie język asemblerowy jest nadal używany do bezpośredniej manipulacji sprzętem, dostępu do wyspecjalizowanych instrukcji procesora lub do rozwiązywania krytycznych problemów z wydajnością. Typowe zastosowania to sterowniki urządzeń , niskopoziomowe systemy wbudowane i systemy czasu rzeczywistego.

Historycznie wiele programów zostało napisanych w całości w języku asemblera. Burroughs MCP (1961) był pierwszym komputerem, dla których system operacyjny nie został opracowany w całości w asemblerze; został napisany w Executive Systems Problem Oriented Language (ESPOL), dialekcie Algol. Wiele aplikacji komercyjnych zostało napisanych również w języku asemblerowym, w tym duża ilość oprogramowania IBM mainframe napisanego przez duże korporacje. COBOL , FORTRAN i niektóre PL/I ostatecznie wyparły większość tych prac, chociaż wiele dużych organizacji zachowało infrastruktury aplikacji w języku asemblera jeszcze w latach 90-tych.

Większość wczesnych mikrokomputerów opierała się na ręcznie kodowanym języku asemblera, w tym w większości systemów operacyjnych i dużych aplikacji. Wynikało to z faktu, że systemy te miały poważne ograniczenia zasobów, narzucały specyficzne architektury pamięci i wyświetlania oraz zapewniały ograniczone, wadliwe usługi systemowe. Być może ważniejszy był brak najwyższej klasy kompilatorów języka wysokiego poziomu, odpowiednich do użytku na mikrokomputerach. Pewną rolę mógł również odegrać czynnik psychologiczny: pierwsze pokolenie programistów mikrokomputerów zachowało postawę hobbystyczną, „przewody i szczypce”.

W bardziej komercyjnym kontekście, największymi powodami używania języka asemblera były minimalne rozdęcie (rozmiar), minimalne obciążenie, większa szybkość i niezawodność.

Typowymi przykładami dużych programów w języku asemblerowym z tamtych czasów są systemy operacyjne IBM PC DOS , kompilator Turbo Pascal i wczesne aplikacje, takie jak program arkusza kalkulacyjnego Lotus 1-2-3 . Język asemblera został użyty, aby uzyskać najlepszą wydajność z Sega Saturn , konsoli, która była notorycznie trudna do tworzenia i programowania gier. Innym przykładem jest gra zręcznościowa NBA Jam z 1993 roku .

Język asemblera od dawna jest podstawowym językiem programowania dla wielu popularnych komputerów domowych w latach 80. i 90. (takich jak MSX , Sinclair ZX Spectrum , Commodore 64 , Commodore Amiga i Atari ST ). Wynikało to w dużej mierze z tego, że dialekty interpretowane w języku BASIC w tych systemach oferowały niewystarczającą szybkość wykonywania, a także niewystarczające udogodnienia, aby w pełni wykorzystać dostępny sprzęt w tych systemach. Niektóre systemy mają nawet zintegrowane środowisko programistyczne (IDE) z wysoce zaawansowanymi funkcjami debugowania i makr. Niektóre kompilatory dostępne dla Radio Shack TRS-80 i jego następców miały możliwość łączenia źródeł asemblera inline z wysokopoziomowymi instrukcjami programu. Podczas kompilacji wbudowany asembler tworzył wbudowany kod maszynowy.

Bieżące użycie

Zawsze były debaty nad użytecznością i wydajnością języka asemblerowego w stosunku do języków wysokiego poziomu.

Chociaż asembler ma specyficzne zastosowania niszowe tam, gdzie jest to ważne (patrz poniżej), istnieją inne narzędzia do optymalizacji.

Od lipca 2017 r. indeks popularności języka programowania TIOBE plasuje język asemblera na 11, wyprzedzając na przykład Visual Basic . Asembler może być używany do optymalizacji pod kątem szybkości lub optymalizacji pod kątem rozmiaru. W przypadku optymalizacji szybkości, nowoczesne kompilatory optymalizujące mają renderować języki wysokiego poziomu do kodu, który może działać tak szybko, jak ręcznie napisany asembler, pomimo kontrprzykładów, które można znaleźć. Złożoność nowoczesnych procesorów i podsystemów pamięci sprawia, że ​​efektywna optymalizacja staje się coraz trudniejsza zarówno kompilatorom, jak i programistom w asemblerze. Co więcej, rosnąca wydajność procesorów oznacza, że ​​większość procesorów przez większość czasu pozostaje bezczynna, a opóźnienia spowodowane są przewidywalnymi wąskimi gardłami, takimi jak braki w pamięci podręcznej, operacje we/wy i stronicowanie . To sprawiło, że szybkość wykonywania surowego kodu nie stanowi problemu dla wielu programistów.

Jest kilka sytuacji, w których programiści mogą zdecydować się na użycie języka asemblera:

  • Pisanie kodu dla systemów ze starszymi procesorami, które mają ograniczone opcje języków wysokiego poziomu, takich jak Atari 2600 , Commodore 64 i kalkulatory graficzne . Programy dla tych komputerów z lat 70. i 80. często pisane są w kontekście subkultur demoscenowych lub retrogamingowych .
  • Kod, który musi wchodzić w bezpośrednią interakcję ze sprzętem, na przykład w sterownikach urządzeń i procedurach obsługi przerwań .
  • We wbudowanym procesorze lub procesorze DSP przerwania o wysokiej powtarzalności wymagają najkrótszej liczby cykli na przerwanie, na przykład przerwanie występujące 1000 lub 10000 razy na sekundę.
  • Programy, które muszą używać instrukcji specyficznych dla procesora, które nie są zaimplementowane w kompilatorze. Typowym przykładem jest instrukcja rotacji bitowej w rdzeniu wielu algorytmów szyfrowania, a także zapytanie o parzystość bajtu lub 4-bitowe przeniesienie dodatku.
  • Samodzielny wykonywalny niewielkich rozmiarów jest wymagane, które należy wykonać, bez uciekania się do czasu wykonania części lub bibliotek związanych z językiem wysokiego poziomu. Przykłady obejmowały oprogramowanie układowe telefonów, samochodowych układów paliwowych i zapłonowych, układów sterowania klimatyzacją, systemów bezpieczeństwa i czujników.
  • Programy z pętlami wewnętrznymi wrażliwymi na wydajność, w których język asembler zapewnia możliwości optymalizacji, które są trudne do osiągnięcia w języku wysokiego poziomu. Na przykład algebra liniowa z BLAS lub dyskretna transformacja kosinusowa (np. wersja złożenia SIMD z x264 ).
  • Programy, które tworzą funkcje wektorowe dla programów w językach wyższego poziomu, takich jak C. W języku wyższego poziomu jest to czasami wspomagane przez wewnętrzne funkcje kompilatora, które mapują bezpośrednio do mnemoniki SIMD, ale mimo to skutkują konwersją asemblera jeden do jednego dla danego procesora wektorowego.
  • Programy czasu rzeczywistego , takie jak symulacje, systemy nawigacji lotniczej i sprzęt medyczny. Na przykład w systemie fly-by-wire telemetria musi być interpretowana i podejmowana w ramach ścisłych ograniczeń czasowych. Takie systemy muszą eliminować źródła nieprzewidywalnych opóźnień, które mogą być tworzone przez (niektóre) interpretowane języki, automatyczne usuwanie śmieci , operacje stronicowania lub wielozadaniowość z wywłaszczaniem . Jednak niektóre języki wyższego poziomu zawierają składniki czasu wykonywania i interfejsy systemu operacyjnego, które mogą wprowadzać takie opóźnienia. Wybór języków asemblera lub języków niższego poziomu dla takich systemów daje programistom większą widoczność i kontrolę nad szczegółami przetwarzania.
  • Algorytmy kryptograficzne, których wykonanie musi zawsze trwać dokładnie tyle samo, co zapobiega atakom czasowym .
  • Modyfikuj i rozszerzaj starszy kod napisany dla komputerów mainframe IBM.
  • Sytuacje, w których wymagana jest pełna kontrola nad środowiskiem, w sytuacjach o wyjątkowo wysokim poziomie bezpieczeństwa, w których nic nie może być brane za pewnik .
  • Wirusy komputerowe , programy ładujące , niektóre sterowniki urządzeń lub inne elementy bardzo zbliżone do sprzętu lub systemu operacyjnego niskiego poziomu.
  • Symulatory zestawu instrukcji do monitorowania, śledzenia i debugowania, w których dodatkowe koszty są ograniczone do minimum.
  • Sytuacje, w których nie istnieje język wysokiego poziomu, na nowym lub wyspecjalizowanym procesorze, dla którego nie jest dostępny kompilator krzyżowy .
  • Inżynieria wsteczna i modyfikacja plików programów, takich jak:
    • istniejące pliki binarne, które mogły, ale nie musiały być oryginalnie napisane w języku wysokiego poziomu, na przykład podczas próby odtworzenia programów, dla których kod źródłowy nie jest dostępny lub został utracony, lub złamanie ochrony przed kopiowaniem zastrzeżonego oprogramowania.
    • Gry wideo (nazywane również hackowaniem ROM ), co jest możliwe na kilka sposobów. Najczęściej stosowaną metodą jest zmiana kodu programu na poziomie języka asemblerowego.

Język asemblera jest nadal nauczany w większości programów informatycznych i inżynierii elektronicznej . Chociaż niewielu programistów dzisiaj regularnie pracuje z asemblerem jako narzędziem, podstawowe koncepcje pozostają ważne. Takie podstawowe zagadnienia, jak arytmetyka binarna , alokacja pamięci , przetwarzanie stosu , kodowanie zestawu znaków, przetwarzanie przerwań i projektowanie kompilatorów byłyby trudne do szczegółowego zbadania bez zrozumienia, jak komputer działa na poziomie sprzętowym. Ponieważ zachowanie komputera jest fundamentalnie zdefiniowane przez jego zestaw instrukcji, logicznym sposobem nauczenia się takich pojęć jest studiowanie języka asemblera. Większość nowoczesnych komputerów ma podobne zestawy instrukcji. Dlatego nauka pojedynczego języka asemblera jest wystarczająca, aby nauczyć się: I) podstawowych pojęć; II) rozpoznać sytuacje, w których użycie asemblera może być właściwe; i III) aby zobaczyć, jak wydajny kod wykonywalny można stworzyć z języków wysokiego poziomu.

Typowe aplikacje

  • Język asemblera jest zwykle używany w kodzie rozruchowym systemu , niskopoziomowym kodzie, który inicjuje i testuje sprzęt systemowy przed rozruchem systemu operacyjnego i jest często przechowywany w pamięci ROM . ( BIOS na kompatybilny z IBM PC i systemy CP / M jest przykładem).
  • Język asemblera jest często używany w kodzie niskopoziomowym, na przykład w jądrach systemu operacyjnego , które nie mogą polegać na dostępności wcześniej istniejących wywołań systemowych i muszą faktycznie zaimplementować je dla konkretnej architektury procesora, na której system będzie działał.
  • Niektóre kompilatory tłumaczą języki wysokiego poziomu na asembler przed pełną kompilacją, umożliwiając przeglądanie kodu asemblera w celu debugowania i optymalizacji.
  • Niektóre kompilatory dla języków stosunkowo niskiego poziomu, takie jak Pascal lub C , pozwalają programiście na osadzenie języka asemblerowego bezpośrednio w kodzie źródłowym (tzw. asembler inline ). Programy używające takich udogodnień mogą następnie konstruować abstrakcje używając różnych języków asemblerowych na każdej platformie sprzętowej. Systemu przenośny kod może następnie wykorzystać te elementy specyficzne dla procesora poprzez interfejs jednolity.
  • Język asemblera jest przydatny w inżynierii wstecznej . Wiele programów jest dystrybuowanych tylko w postaci kodu maszynowego, który jest prosty do przetłumaczenia na język asemblerowy przez deasembler , ale trudniejszy do przetłumaczenia na język wyższego poziomu przez dekompilator . Narzędzia takie jak Interactive Disassembler szeroko wykorzystują deasemblację w tym celu. Ta technika jest wykorzystywana przez hakerów do łamania komercyjnego oprogramowania, a konkurenci do tworzenia oprogramowania o podobnych wynikach od konkurencyjnych firm.
  • Język asemblera jest używany do zwiększenia szybkości wykonywania, szczególnie we wczesnych komputerach osobistych z ograniczoną mocą przetwarzania i pamięcią RAM.
  • Asemblery mogą być używane do generowania bloków danych, bez narzutu języka wysokiego poziomu, z sformatowanego i zakomentowanego kodu źródłowego, do wykorzystania przez inny kod.

Zobacz też

Uwagi

Bibliografia

Dalsza lektura

Zewnętrzne linki