Направо към съдържанието

Асемблерен език

от Уикипедия, свободната енциклопедия
Тази статия е за програмния език. За системния софтуер вижте Асемблер.

Асемблерен език
Motorola MC6800 асемблерен файл, показва оригиналния асемблерен език
Парадигмаимперативно програмиране
Реализиране през1947 (преди 77 г.)
Асемблерен език в Общомедия

Асемблерен език е език за програмиране от ниско ниво, който е свързан с конкретен процесор и не може да бъде пренесен без значителни изменения към машина с различна компютърна архитектура и набор от инструкции. Това е в контраст с повечето езици за програмиране от високо ниво, които са съвместими с различни архитектури, но изискват превеждане или компилиране. Специализираният системен софтуер, който преобразува инструкциите от асемблерен език в машинен код, се нарича асемблер, а процесът на преобразуване е познат като асемблиране на кода.

За представянето на всяка процесорна операция или инструкция от ниско ниво в асемблерния език се използва мнемоника. Типичните операции изискват един или повече операнди, за да образуват цялостна инструкция и повечето асемблери за целта може да приемат като операнди етикети, символи и изрази, за да освободят програмиста от досадните изчисления на ръка. Макро-асемблерите съдържат улесняващи макрокоманди, така че инструкциите на асемблерен език се представят обобщено чрез едно име и това име да може да се използва за вмъкване в друг код. Много асемблери предоставят допълнителни инструменти за улесняване на разработката на програми, за контролиране на процеса на асемблиране и за подпомагане на дебъгването.

Вижте долу раздела с Терминология за информация относно непоследователната употреба на термините асемблерен език и асемблер

Асемблерният език датира от въвеждането на програмируемия компютър. Първите асемблери са създадени от Катлийн Буут през 1947 за нуждите на ARC2[1] и от Дейвид Уилър (David Wheeler) през 1948 за нуждите на EDSAC[2]. По онова време не се използва терминът „асемблер“, а „множество от базови команди“ (на английски: basic order set) и „начални команди“ (на английски: initial orders).[3] За първи път терминът „асемблер“ започва да се използва в по-късните отчети по EDSAC.

В началните етапи от развитието на програмирането е въведено понятието автокод – програмен език, чиито изрази по структура са подобни на командите и обработваните данни на конкретния машинен код[4]. В днешно време терминът на практика не се използва.

Исторически погледнато, ако машинният код се приеме за първо поколение програмен език, то асемблерният език може да се разглежда като второ поколение. На свой ред, неговите недостатъци, например, сложността на разработване на големи програми, водят до появата на програмните езици от второ поколение – програмни езици от високо ниво (като Фортран, Лисп, Кобол, Паскал, C и други).

В началото на развитието на компютрите множество програми са написани на асемблерни езици. Натаниел Рочестър пише асемблера на IBM 701. SOAP (Оптимално Символно Сглобяваща Програма – Symbolic Optimal Assembly Program) (1955) асемблерът е написан от Стан Полей за IBM 650.[5]

Операционните системи са били писани изцяло на асемблерен език до въвеждането на MCP от Burroughs Corporation (1961), написана на ESPOL (Executive Systems Problem Oriented Language), диалект на ALGOL. Много комерсиални приложения също са писани на асемблерни езици, включително голямо количество от софтуера за IBM мейнфрейм. В крайна сметка езиците COBOL, FORTRAN и PL/I изместват асемблерните езици, въпреки че редица големи организации ги задържат в голяма част от приложенията си и през 90-те.

Типични примери за големи програми на асемблерен език от това време са операционната система DOS за IBM PC и ранните приложения като програмата за електронни таблици Lotus 1-2-3. Дори през 90-те години, повечето конзолни видео игри се пишат на асемблерен език, включително повечето игри за Mega Drive/Genesis и Super Nintendo Entertainment System. Според някои хора запознати с производството, това е най-подходящият програмен език за извличане на най-доброто представяне от конзолата Sega Saturn, за която е пословично трудно да се разработи игра.[6] Аркадната игра NBA Jam (1993) е друг пример.

Асемблерният език дълго време е основен език за разработка за много популярни домашни компютри от 80-те и 90-те (като Sinclair ZX Spectrum, Commodore 64, Commodore Amiga и Atari ST). Това се обяснява с факта, че интерпретираните диалекти на BASIC, използвани на тези системи, предлагат недостатъчна скорост на изпълнение и малко инструменти, за да се използва пълноценно наличният хардуер. Някои системи дори имат интегрирана среда за разработка (IDE) с много напреднало отстраняване на грешки и макро инструменти.

Синтаксис на асемблерния език

[редактиране | редактиране на кода]

Асемблерният език използва мнемоника, за да представи всяка машинна инструкция или код на ниско ниво, всяка директива, обикновено също всеки архитектурен регистър, флаг и т.н. Някои от мнемониките могат да бъдат вградени, други се дефинират от потребителя. Много операции изискват един или повече операнди, за да формират пълна инструкция. Повечето асемблери позволяват именувани константи, регистри и етикети за програмни и паметови позиции и могат да изчисляват изрази за операнди. По този начин програмистите са освободени от досадни повтарящи се изчисления и асемблерните програми са много по-четливи от машинния код. В зависимост от архитектурата, тези елементи могат също да бъдат комбинирани за специфични инструкции или режими на адресиране, като се използват отмествания или други данни, както и фиксирани адреси. Много асемблери предлагат допълнителни механизми за улесняване на разработването на програми, за контрол на процеса на асемблиране и за подпомагане на отстраняването на грешки.

Някои асемблери използват синтаксис с колони за специфичните полета; това е обичайно за машините с перфокарти през 50-те и началото на 60-те години. Други асемблери имат синтаксис в свободна форма, с полета, разделени с разделители, напр. пунктуация, интервал (white space). Трети са хибридни, например с поставяне на етикетите в специфична колона, а другите полета са отделени с разделители; това става по-често срещано през 60-те години.

Всички асемблери на IBM за System/360 по подразбиране имат етикет в колона 1, полета, разделени с разделители в колони 2 – 71, индикатор за продължение в колона 72 и пореден номер в колони 73 – 80. Разделителят за етикет, код за операция, операнди и коментари е интервал, докато отделните операнди са разделени със запетаи и скоби.

  • Макроасемблерът е асемблер, който включва средство за обработка на макроси, така че част от кода да може да бъде представен с едно име и то да може да се вмъква в друг код.
    • Отворен код се отнася до всеки вход на асемблер извън дефиницията на макроси.
  • Кръстосан асемблер (вижте също кръстосан компилатор ) е асемблер, който се изпълнява на компютър или операционна система ( хост системата) от различен тип от системата, на която трябва да се изпълнява полученият код ( целевата система ). Кръстосаното сглобяване улеснява разработването на програми за системи, които нямат ресурсите да поддържат разработката на софтуер, като например вградена система или микроконтролер. В такъв случай полученият обектен код трябва да бъде прехвърлен към целевата система чрез памет само за четене (ROM, EPROM и т.н.), програматор (когато паметта само за четене е интегрирана в устройството, както при микроконтролерите), или връзка за данни, използваща или точно копие бит по бит на обектния код, или текстово представяне на този код (като Intel hex или Motorola S-record ).
  • Асемблерът от високо ниво е програма, която предоставя езикови абстракции, по-често свързани с езиците на високо ниво, като разширени контролни структури ( IF/THEN/ELSE, DO CASE и т.н.) и абстрактни типове данни на високо ниво, включително структури/ записи, съюзи, класове и набори.
  • Микроасемблерът е програма, която помага да се подготви фърмуер, за контролиране на ниско ниво на работа на компютър.
  • Мета-асемблерът е „програма, която приема синтактичното и семантичното описание на асемблерния език и генерира асемблер за този език“ или която приема изходен файл на асемблер заедно с такова описание и асемблира изходния файл в съответствие с това описание. Асемблерите „Meta-Symbol“ за серията компютри SDS 9 и SDS Sigma са мета-асемблери. [7] [бел 1] Sperry Univac също предостави мета-асемблер за серията UNIVAC 1100/2200. [8]
  • вграден асемблер (или embedded assembler ) е код на асемблерен език, съдържащ се в програма на език от високо ниво. [9] Той най-често се използва в системни програми, които се нуждаят от директен достъп до хардуера.

Програмата асемблер създава обектен код, като превежда комбинацията от мнемоника и синтаксис за операциите и режимите на адресиране в техните числени еквиваленти. Това представяне обикновено включва код на операция („operation code“), както и други контролни битове и данни. Асемблерът също изчислява константните изрази и дешфрира символните имена за места в паметта и други обекти. Използването на символни имена е ключова характеристика на асемблерите, като спестява досадни изчисления и ръчна актуализация на адресите след програмни модификации. Повечето асемблери също така включват макрокоманди за извършване на текстово заместване – например, за генериране на общи кратки последователности от инструкции като вградени, вместо извиквани подпрограми.

Някои асемблери може също да са в състояние да изпълняват някои прости типове оптимизации, специфични за набора от инструкции. Един конкретен пример са вездесъщите x86 асемблери от различни доставчици. Наричани „jump-sizing“, повечето от тях са в състояние при поискване да извършват замествания на инструкции на „скокове“ в произволен брой преминавания. Други могат дори да правят просто пренареждане или вмъкване на инструкции, като например някои асемблери за RISC архитектури, които могат да помогнат за оптимизиране на разумно планиране на инструкции, за да се използва възможно най-ефективно конвейера на процесора. [10]

От 50-те години на XX век асемблерите се прилагат като първа стъпка над машинния език и преди езиците за програмиране на високо ниво като Fortran, Algol, COBOL и Lisp. Съществуват и няколко класа транслатори и полуавтоматични генератори на код със свойства, подобни както на асемблерните, така и на езиците от високо ниво, като Speedcode е може би един от по-известните примери.

За конкретен централен процесор или набор от инструкции може да има няколко асемблера с различен синтаксис. Например, инструкция за добавяне на данни от паметта към регистър в процесор от семейство x86 може да бъде add eax,[ebx] в оригиналния синтаксис на Intel, докато същото ще бъде addl (%ebx),%eax в синтаксиса AT&T, използван от GNU Assembler. Въпреки различния запис, различните синтактични форми обикновено генерират един и същ цифров машинен код. Един асемблер може също да има различни режими, за да поддържа вариации в синтактичните форми, както и в техните точни семантични интерпретации (като FASM синтаксис, TASM синтаксис, идеален режим и т.н., в специалния случай на x86 асемблерно програмиране).

Има два вида асемблери въз основа на това колко преминавания през изходния код са необходими (колко пъти асемблерът го чете), за да създаде обектния файл.

  • Асемблерите с един ход обработват изходния код само веднъж. За символи, използвани преди да бъдат дефинирани, асемблерът ще излъчи „errata“ след евентуалната дефиниция, казвайки на линкера или товарача да поправи местата, където са били използвани все още недефинираните символи.
  • Многоходовите асемблери създават таблица с всички символи и техните стойности в първите преминавания, след което използват таблицата в следващите преминавания, за да генерират код.

И в двата случая асемблерът трябва да може да определи размера на всяка инструкция при първоначалните преминавания, за да изчисли адресите на следващите символи. Това означава, че ако размерът на операция, отнасяща се до операнд, дефиниран по-късно, зависи от типа или разстоянието на операнда, асемблерът ще направи песимистична оценка, когато срещне операцията за първи път, и ако е необходимо, ще я допълни с една или повече инструкции " не-операция " в по-късен ход или при „errata“. В асемблер с Peephole optimization, адресите могат да бъдат преизчислени между преминаванията, за да се позволи замяна на песимистичен код с код, съобразен с точното разстояние от целта.

Първоначалните съображения за използването на асемблери само с един ход са свързани с ограниченията в размера на паметта и скоростта на асемблиране – често второ преминаване би изисквало съхраняване на символната таблица в паметта (за обработка на препратки напред ), пренавиване и повторно четене на програмата от лента или пък повторно четене на перфокарти или перфолента. По-късните компютри разполагат с много по-голяма памет (особено при дисково съхранение) и вече имат възможност да извършат цялата необходима обработка без повторно четене. Предимството на многоходовия асемблер е, че липсата на грешка прави по-бърз процеса на свързване (или зареждане на програмата, когато асемблерът директно произвежда изпълним код).

Пример: в следния кодов фрагмент едноходов асемблер би могъл да определи адреса на обратната препратка BKWD при асемблиране на оператора S2, но не би могъл да определи адреса на препратка напред FWD при асемблирането на оператора за разклоняване S1 ; наистина FWD може да е недефиниран. Двуходовият асемблер ще определи и двата адреса при ход 1, така че те ще бъдат известни при генериране на код в ход 2.

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

Асемблери от високо ниво

[редактиране | редактиране на кода]

По-сложните асемблери от високо ниво предоставят езикови абстракции като:

  • Декларации и извиквания на процедури/функции от високо ниво
  • Разширени контролни структури (IF/THEN/ELSE, SWITCH)
  • Абстрактни типове данни от високо ниво, включително структури/записи, обединения, класове и множества
  • Усъвършенствана обработка на макроси (въпреки че се предлага и на обикновените асемблери от края на 50-те години на XX век за, напр. серията IBM 700 и серия IBM 7000, и от 60-те години на XX век за IBM System/360 (S/360), наред с други машини)
  • Характерните за обектно-ориентираното програмиране класове, обекти, абстракция, полиморфизъм и наследяване[11]

Написаната на асемблер програма се състои от серии от (мнемонични) процесорни инструкции и мета изрази (познати още като директиви или псевдо инструкции), коментари и данни. Инструкциите на асемблерния език обикновено се състоят от мнемоничен код следван от списък от данни, аргументи или параметри. Тези инструкции се преобразуват от асемблер до инструкции на машинен език, които могат да бъдат прехвърлени в паметта и изпълнени.

Например инструкцията по-долу казва на един x86/IA-32 процесор да прехвърли една пряка 8-битова стойност (01100001) в регистър (временна клетка от паметта, която се намира в ядрото на процесора). Бинарният код на тази инструкция е 10110 последван от 3-битов идентификатор за регистъра, който да се използва. Идентификаторът за регистър AL е 000, така че следния машинен код прехвърля в регистър AL стойността 01100001:[12] 10110000 01100001

Този бинарен компютърен код може да бъде направен по-лесно четим, ако бъде представен в шестнадесетичен формат както следва:

B0 61

Тук B0 означава 'Прехвърли копие на следната стойност в регистър AL', а 61 е шестнадесетичното представяне на стойността 01100001, която в десетичен формат е числото 97. Асемблерният език за процесори от фамилия 8086 въвежда мнемоничния код MOV (абревиатура на move – местя) за инструкции като тази, така че машинният код по-горе може да бъде записан по следния начин на асемблерен език, допълнен от обяснителен коментар след точка и запетая, ако се изисква такъв. Това е много по-лесно за прочитане и запаметяване:

MOV AL, 61h; Зарежда регистъра  AL с десетично 97 (шестнадесетично 61)

В някои асемблерни езици същият мнемоничен код като MOV може да бъде използван за фамилия от близки инструкции за зареждане, копиране и прехвърляне на данни, независимо дали стойностите са преки (кодирани в самата инструкция), стойности в регистри или места в паметта, към които сочат стойности в регистри. Други асемблери може да използват отделни операционни мнемонични кодове, като например L за „прехвърли памет в регистър“, ST за „прехвърли регистър в памет“, LR за „прехвърли регистър в регистър“, MVI за „прехвърли пряк операнд в памет“ и т.н.т.

При x86 асемблерите операционният код 10110000 (B0) копира една 8-битова стойност в регистъра AL, докато 10110001 (B1) я прехвърля в регистъра CL, а 10110010 (B2) я прехвърля в регистъра DL. Примери от езика асемблер са дадени по-долу:

MOV AL, 1h; Зарежда регистъра  AL с пряка стойност  1
MOV CL, 2h; Зарежда регистъра  CL с пряка стойност  2
MOV DL, 3h; Зарежда регистъра  DL с пряка стойност  3

Синтаксисът на MOV може също да бъде по-комплексен, както показват следните примери:[13]

MOV EAX, [EBX]; 4 байта от паметта на адреса, съдържащ се в EBX прехвърли в EAX
MOV [ESI+EAX], CL; Прехвърли  съдържанието на CL в байта на адрес ESI+EAX

При всички случаи, мнемоничният код MOV се преобразува директно в операционен код в интервалите 88-8E, A0-A3, B0-B8, C6 или C7 от един асемблер и не е нужно програмистът да ги знае или запомня.[12]

Преобразуването от асемблерен език в машинен код се извършва от асемблер, а обратният процес на преобразуване на команди от машинен език в асемблерни инструкции поне частично може да бъде постигнато с дизасемблер. За разлика от програмните езици от високо ниво при асемблерните езици обикновено има пълно съответствие между езиковия израз и процесорните инструкции. Понякога даден асемблер може да притежава псевдоинструкции (основно макроси), които обхващат няколко процесорни инструкции, за да осигурят желаната функционалност. Например за процесор, при който липсва инструкцията „ако е по-голямо или равно“, един асемблер може да предостави псевдоинструкция, която се разширява до процесорните инструкции „ако не е по-малко“ и „ако е равно“. Повечето напълно оборудвани асемблери осигуряват богат макро език, който се използва от производителите и програмистите за генериране на по-комплексен код и последователности от данни.

Всяка компютърна архитектура притежава свой собствен машинен език. Компютрите се различават по броя и типа на операциите, които поддържат, по различния размер и брой на регистрите и представянето на съхраняваните данни. Докато повечето компютри с обща употреба са способни да изпълняват по същество еднаква функционалност, начините, по които правят това се различават. Съответстващите асемблерни езици отразяват тези разлики.

За определена група инструкции, типично използвани в различни асемблерни програми, може да съществуват множество групи от мнемонични кодове или асемблерни езикови синтаксиси. В тези случаи, производителят обикновено доставя и използва в документацията най-разпространения от тях.

Съществува голяма степен от различия в начина, по които авторите на асемблери категоризират инструкциите и номенклатурата, която използват. В частност, някои не описват нищо повече от машинни мнемонични кодове или разширени мнемонични кодове като псевдо операции. Един типичен асемблерен език се състои от три типа инструкции, които се използват за дефиниране на програмните операции:

  • Операционни мнемонични кодове
  • Дефиниции на данни
  • Асемблерни директиви

Операционни мнемонични кодове и разширени мнемонични кодове

[редактиране | редактиране на кода]

Инструкциите в асемблерния език в повечето случаи са доста опростени, за разлика от същите в езиците от високо ниво. Обикновено мнемоничният код представлява символно наименование на една изпълнима на машинен език инструкция (операционен код) и съществува поне по един операционен мнемоничен код, дефиниран за всяка инструкция на машинен език. Всяка инструкция обикновено се състои от операция или операционен код плюс нула или повече операнди. Повечето инструкции се отнасят към единична стойност или двойка стойности. Операндите могат да бъдат преки (със стойност, кодирана в самата инструкция), регистрово определени в инструкцията или съдържащи се в нея, или могат да бъдат адреси на данни, които се съхраняват някъде другаде. Това се определя от скритата процесорна архитектура. Асемблерът просто отразява как тази архитектура работи. Разширените мнемонични кодове често се използват, за да определят комбинации от операционни кодове и специфични операнди. Например System/360 асемблерите използват В като разширен мнемоничен код за BC с маска 15 и NOP („NO OPeration“ – не прави нищо една стъпка) за BC с маска 0.

Разширените мнемонични кодове често се използват, за да поддържат специализирани употреби на инструкции, често за цели, които не са разбираеми от името на инструкцията. Например, много процесори не притежават експлицитна NOP инструкция, но имат инструкции, които могат да се използват за целта. В 8086 процесорите инструкцията xchg ax,ax е била използвана вместо NOP, след като NOP е бил псевдо операционен код за декодиране на инструкцията xchg ax,ax. Някои дизасемблери разпознават това и биха декодирали xchg ax,ax инструкцията като nop. По подобен начин IBM асемблерите за System/360 и System/370 използват разширените мнемонични кодове NOP и NOPR за BC и BCR с маски 0. За SPARC архитектурата, тези кодове са познати като синтетични инструкции.

Освен това някои асемблери поддържат несложни вградени макро инструкции, които генерират две или повече машинни инструкции. Например, при някои Z80 асемблери инструкцията ld hl,bc е прието да генерира ld l,c последвана от ld h,b. Тези са познати още като псевдооперационни кодове.

Мнемоничните кодове са произволни символи. IEEE публикува Standard 694 през 1985 г. на постоянен набор от мнемонични кодове, които да се използват от всички асемблери. Впоследствие този стандарт е бил отменен.

Директиви за данни

[редактиране | редактиране на кода]

Съществуват инструкции, които се използват, за да определят елементи на данни да съдържат данни и променливи. Те определят типа от данни, дължината и подреждането на тези данни. Тези инструкции могат също да определят дали тези данни да са на разположение на външни програми (програми, които са асемблирани отделно) или да са на разположение само на програмата, в която сегментът от данни е дефиниран. Някои асемблери класифицират тези инструкции като псевдооперации.

Асемблерни директиви

[редактиране | редактиране на кода]

Асемблерните директиви, наричани още псевдооперационни кодове или псевдооперации, представляват инструкции, които се изпълняват от асемблера по време на асемблиране, а не от процесора по време на действие. Имената на псевдооперациите често започват с точка, за да бъдат разграничавани от машинните инструкции. Псевдо-операциите могат да направят асемблирането на програмата зависимо от параметрите, които се въвеждат от програмиста, така че една програма може да бъде асемблирана по различни начини, евентуално за различни приложения. Или, псевдооперациите могат да бъдат използвани, за да се промени представянето на дадена програма, за да бъде направена тя по лесна за четене и поддръжка. Друго обичайно приложение на псевдооперациите е да запазват място за съхранение на работните данни и като опция – да инициализират тяхното съдържание с известни стойности.

Символните асемблери позволяват на програмистите да обединяват произволни имена (етикети или символи) с места в паметта и различни константи. В един изпълним код, името на всяка подпрограма се свързва с нейната входна точка, така че всяко извикване на една подпрограма може да използва нейното име. Вътре в подпрограмите, на GOTO командите се дават етикети. Някои асемблери поддържат локални символи, които са лексикално различни от нормалните символи (например, използването на „10$“ като GOTO команда).

Някои асемблери като NASM осигуряват гъвкаво боравене със символите, позволявайки на програмистите да си служат с различни неймспейси, да изчисляват автоматично отместванията в структури от данни, и да поставят етикети, отнасящи се до буквени стойности или резултата от несложни изчисления, извършвани от асемблера. Етикетите също може да бъдат използвани за инициализиране на константи и променливи с преместваеми адреси.

Асемблерните езици, както повечето компютърни езици, позволяват да бъдат добавени коментари към програмния изходен код, които по време на асемблирането биват игнорирани. Разумното коментиране е съществено при програмите на асемблерен език, тъй като значението и целта на поредицата от бинарни машинни инструкции трудно могат да бъдат определени. „Суровият“ (не коментиран) асемблерен език, генериран от компилаторите или дизасемблерите, е доста труден за четене, когато трябва да се правят промени по него.

Много асемблери поддържат предефинирани макроси, а други поддържат макроси, които се дефинират от програмиста (поддаващи се на повторно дефиниране) и включват поредици от текстови редове, в които са променливите и константите. Тази поредица може да включва операционни кодове или директиви. Веднъж щом макросът е дефиниран, неговото име би могло да се използва на мястото на мнемоничен код. Когато асемблерът обработва такава инструкция, той замества инструкцията с текстовите редове, свързани с този макрос, след което ги обработва също както ако съществуваха във файла на сорс кода. Макроси от такъв вид е имало в програмите Autocoder на IBM още през 50-те години на XX век.

Тази дефиниция за „макрос“ малко се различава от употребата на този термин в други контексти. Например макроси в програмният език C, създадени чрез #директивата за дефиниране, обикновено са само на един ред, или най-много на няколко реда. Асемблерните макроинструкции може да са с дължината на програма, която се изпълнява при преобразуване от асемблера по време на асемблирането.

При положение, че макросите могат да имат кратки имена, но да се разширяват до няколко или множество редове код, те могат да направят програмите на асемблерен език да изглеждат доста по-къси, изискващи по-малко редове сорс код, както е при езиците от високо ниво. Освен това те могат да бъдат използвани, за да се добавят структури от по-високо ниво към асемблерните програми, като опция да въвеждат запечатан код за дебъгване посредством параметри и други подобни свойства.

Макроасемблерите често позволяват макросите да приемат параметри. Някои асемблери включват доста усъвършенствани макро езици, като съчетават такива елементи на езиците от високо ниво, като незадължителни параметри, символни променливи, условности, обработка на символни низове и аритметични операции, които се използват при изпълнение на даден макрос и позволяващи на макроса да запазва конкретна или променяща се информация. По този начин един макрос може да генерира множество инструкции на асемблерен език или дефиниции на данни въз основа на параметрите на макросите.

Поддържане на структурно програмиране

[редактиране | редактиране на кода]

Някои асемблери имат включени елементи на структурно програмиране за декодиране на потока на изпълнение. Най-ранният пример за такава разработка е бил в набора от макроси Concept-14, първоначално разработен от д-р H.D. Mills (Март 1970) и осъществен на практика от Marvin Kessler (IBM Federal Systems Division), който разширил макрос асемблера S/360 с IF/ELSE/ENDIF и подобни блокове за контрол на потока.[14] Това е бил начин за намаляване или елиминиране на употребата на GOTO операциите в асемблерния код – една от основните причини за образуване на спагети код в асемблерния език. Тази разработка е намерила широко приложение в началото на 80-те години на XX век (последните времена на широко използване на асемблерния език). 

Любопитна е постройката на A-natural – ориентиран към потока асемблер за процесорите 8080/Z80 от Whitesmiths Ltd. (разработчиците на подобната на Unix операционна система Idris и на обявения като първи комерсиален С компилатор). Езикът бе класифициран като асемблер, защото работеше със сурови машинни елементи като операционни кодове, регистри и референции към паметта, но включваше и изразителен синтаксис, за да показва реда на изпълнение. Кръгли скоби и други специални символи, заедно с понятия на блоково ориентираното структурно програмиране, контролират последователността на генерираните инструкции. A-natural бе създаден по-скоро като обектен език за C компилатор, отколкото за набиране на код на ръка, но неговия логически синтаксис му спечели доста фенове. 

Съществувало е известно търсене на по-усъвършенствани асемблери преди залеза на широката употреба на асемблерния език. Въпреки това, те все още се разработват и прилагат в случаи на ограничени ресурси или специфики на системната архитектура, които възпрепятстват ефективното използване на езици от високо ниво.

Използване на асемблерния език

[редактиране | редактиране на кода]

Винаги е имало дебати за полезността и ефективността на асемблер в сравнение с езиците от по-високо ниво. В днешни дни асемблерният език все още се използва за директна манипулация на хардуера, достъп до специализирани процесорни инструкции или за решаване на критични проблеми с производителността. Типичната употреба са драйвери за устройствата, вградени системи, както и за изчисления в реално време. Асемблер има приложение в специфична ниша, където е важно (виж по-долу). Асемблер може да се използва, за оптимизиране на скоростта или за оптимизиране на размер. В случай на оптимизация на скоростта, са направени модерни оптимизиращи компилатори,[15] за да преработват езици с високо ниво в код, който може да работи толкова бързо, колкото като написан на ръка, въпреки контра-примери, които могат да бъдат намерени.[16][17][18] Сложността на съвременните процесори и подсистеми на паметта прави ефективната оптимизация все по-трудна за компилаторите, както и за програмисти на асемблер.[19][20] Освен това, увеличаването на производителността на процесора означава, че повечето процесори бездействат голямата част от времето,[21] със забавяне, причинено от предвидими ботълнек места като липса на кеш, I/O операции и виртуална памет. Това прави скоростта на изпълнение на кода безпроблемна за много програмисти.

Има някои ситуации, в които разработчиците могат да изберат да използват асемблер:

  • Самостоятелно изпълняващ се и с компактен размер код е необходимо да се изпълни, без да се прибягва до допълнителни компоненти за стартиране или библиотеки, свързани с езици на високо ниво; това е може би най-често срещаната ситуация. Например, фърмуер за телефони, разход на гориво на колите и стартиращи системи, системи за контрол на климатични, системи за сигурност, както и сензори.
  • Код, който трябва да си взаимодейства директно с хардуера, например в драйверите на устройствата и прекъсващите рутинни услуги.
  • Програми, които имат нужда да използват процесорни специфични инструкции, не изпълнявани в компилатор. Типичен пример е ротацията на инструкция в основата на много алгоритми за криптиране.
  • Програми, които създават векторни функции за езици от по-високо ниво, като например C. При езиците от по-високо ниво това понякога е подпомогнато от компилаторно присъщи функции, които директно картографират на SIMD мнемоника, но въпреки това резултата е едно-към-едно асембли преобразование за специфичния векторен процесор.
  • Програми, изискващи изключителна оптимизация, например вътрешна линия в процесорно-интензивен алгоритъм. Програмисти на игри се възползват от възможностите на хардуерните характеристики на системи, позволяващи игрите да работят по-бързо. Също големи научни симулации изискват силно оптимизирани алгоритми, например линейна алгебра с BLAS[16][22] или дискретна косинусова трансформация (например SIMD сглобяване версия от x264.[23]
  • Ситуации, в които не съществува език на високо ниво, при нов или специализиран процесор, например.
  • Програми, които се нуждаят от точен график като:
    • Изчисления в реално време, като примерно симулации на полет, навигационни системи и медицинско оборудване. Например, в една система за автопилот, телеметрията трябва да се интерпретира и да се предприемат действия в строги времеви ограничения. Тези системи трябва да елиминират източници на непредвидими закъснения, които могат да бъдат създадени от (някои) интерпретиращи езици, garbage collector, пейджинг операции, или превантивен мултитаскинг. Въпреки това, някои езици от по-високо ниво включват компоненти при стартиране и интерфейс на операционна система, които могат да въведат такива закъснения. Изборът на асемблер или езици от по-ниско ниво за такива системи дава на програмистите по-голяма видимост и контрол върху обработката на детайли.
    • Криптографски алгоритми, които винаги трябва да вземат стриктно същото време за да се изпълнят, предотвратява времеви атаки.
  • Ситуации, при които се изисква пълен контрол върху околната среда, в ситуации изискващи изключително висока сигурност, където нищо не може да се приема за даденост.
  • Компютърни вируси, bootloaders, някои драйвери на устройства, или други ситуации, които са много близо до хардуера или операционна система на ниско ниво.
  • Симулатори на набор от инструкции за наблюдение, проследяване и отстраняване на грешки, където допълнителното натоварване се свежда до минимум,
  • Обратно инженерство и промяна на програмни файлове, като например
    • Съществуващите бинарни файлове, без значение първоначално на какъв език са написани, например, когато се опитва да пресъздаде програми, за които изходния код не е в наличност или е бил загубен, или кракване на защитата на софтуера.
    • Видеоигри (наричано още ROM хакване), което е възможно чрез няколко метода. Най-широко използван е промяна на програмния код на ниво асемблер.
    • Само-модифициращ се код, към който асемблер се поддава.
    • Игри и друг софтуер – графични калкулатори.[24]

Асемблер все още се преподава в повечето компютърни науки и в програмата на електронните инженери. Въпреки че все по-малко програмисти днес редовно работят с асемблер като инструмент, базисните концепции остават много важни. Такива основни теми като двоична аритметика, заделяне на памет, обработка на стака, кодиране на набор от символи, прекъсване на обработка и дизайн на компилатора ще бъдат трудни да се изучават в детайли, без възможността за разбиране на това как един компютър работи на хардуерно ниво. Тъй като поведението на компютъра е фундаментално определен от неговия набор инструкции, логичният път за да се научат тези понятия е да се учи на асемблер. Повечето съвременни компютри имат подобни комплекти с инструкции. Затова изучаването на езика асемблер е достатъчно, за да научи:

  1. Основните понятия;
  2. Да се определи правилните ситуации, в които използването на асемблер може да е подходящо;
  3. За да видите колко ефективен може да бъде код създаден на езици от високо ниво.[25] Това е аналогично на деца, които трябва да научат основните аритметични операции (например делене на големи числа), въпреки че калкулаторите са широко използвани за всичко, с изключение на най-незначителните изчисления.

Типични приложения

[редактиране | редактиране на кода]
  • Типично приложение на асемблерен език е в стартиращия код на системата (system’s boot code), който инициализира и тества системния хардуер преди да зареди операционната система и често се съхранява в ROM паметта (примери са CP/M и BIOS в IBM съвместимите системи)
  • Някои компилатори превеждат езиците от високо ниво до асемблерен език преди да ги сглобят напълно, позволявайки отстраняване на грешките и оптимизиране.
  • Езиците от сравнително ниско ниво, като Паскал и C, позволяват на програмиста да вгради асемблер директно в изходния код. Това позволява програмите, които използват такива инструменти, впоследствие да създадат абстракции с различен асемблерен език, според хардуерната платформа. Тогава в случай на преносим код на системата специфичните процесорни компоненти могат да се използват чрез единен интерфейс.
  • Асемблерният език е полезен и при обратното инженерство. Много програми се разпространяват само в машинен код, от който е лесно да се превърнат в асемблерен код чрез дизасемблер, но е по-трудно да се преведат на език от високо ниво. Инструменти като Interactive Disassembler улесняват дизасемблирането. Хакерите, както и конкурентните производители, използват тази техника, за да разбиват комерсиален софтуер и да доставят софтуер с подобни функционалности на оригинала.
  • Асемблерният език може да се използва за генериране на блокове данни, които да бъдат използвани от друг код, без да се прибягва към форматиран и коментиран изходящ код на език от високо ниво.

Примерен асемблерен програмен код

[редактиране | редактиране на кода]

Следното е частичен списък генериран от NASM, на асемблер за 32-битови процесори Intel x86. Кодът е за подпрограма, не за завършена програма.

;-----------------------------------------------------------
                     ; zstr_count:
                     ; Броя на zero-terminated ASCII символни низове за определяне на техния размер
                     ; in:   eax = начален адрес на zero terminated string
                     ; out:  ecx = брой = дължина на символния низ

                     zstr_count:                   ; Начало
00000030 B9FFFFFFFF mov ecx, -1 ; Инициализиране на брояча, предекремент
                                                   ;  за да компенсира инкремента
                     .loop:
00000035 41 inc ecx ; Добавяне на 1 към брояча
00000036 803C0800 cmp byte [eax + ecx], 0 ; Сравняване на стойността на символните низове
                                                   ;  [начален адрес в паметта Плюс
                                                   ;  отместването на цикъла], от нула
0000003A 75F9 jne .loop ; Ако стойността на паметта не е нулаI,
                                                   ;  тогава прескача до етикет '.loop',
                                                   ;  в противен слушай продължава към следващия ред
                     .done:
                                                   ; Не извършваме последно увеличаване на стойността (инкрементация),
                                                   ;  въпреки че броят е с основа 1,
                                                   ;  не сме включили нулевия terminator в
                                                   ;  дължината на символния низ
0000003C C3 ret ; Връщане в извикващата програма

Първата колана (отляво) е просто номера на реда в списъка и не носи друг смисъл. Втората колона е относителния адрес, в шестнайсетичен код, къде кода ще бъде поставен в паметта. Третата колона е действителния компилаторен код. За инстанцията, B9 e x86 операционен код за MOVECX инструкция; FFFFFFFF е стойност -1 в допълнена с два символа двоична форма.

Наставката на имената с двоеточие (:) са символни етикети; етикетите не създават код, те са просто начин да се каже на асемблера, че тези места са символни имена. Етикета .done присъства само за яснота къде приключва програмата, тя няма друга цел. Представката точка (.) върху етикет е функция на асемблер, за деклариране на етикета като локален за подпрограмата.

  1. Използван като мета-асемблер, той позволява на потребителя с минимални усилия да създава свои програмни езици и да генерира средствата за обработката им
  1. General Considerations in the Design of an All Purpose Electronic Digital Computer Архив на оригинала от 2020-03-24 в Wayback Machine. by Andrew D. Booth and Kathleen H. V. Britten. 2nd. Edition. August 1947.
  2. 1985 Computer Pioneer Award „For assembly language programming.“
  3. Salomon. Assemblers and Loaders. с. 7. Посетен на 17 януари 2012.
  4. ГОСТ 19781 – 83 Вычислительная техника. Терминология: Справочное пособие. Выпуск 1, М., Издательство стандартов, 1989, isbn=5-7050-0155-X
  5. The IBM 650 Magnetic Drum Calculator // Посетен на 17 януари 2012.
  6. Eidolon's Inn: SegaBase Saturn, архив на оригинала от 13 юли 2008, https://web.archive.org/web/20080713074116/http://www.eidolons-inn.net/tiki-index.php?page=SegaBase+Saturn, посетен на 30 септември 2015 
  7. Xerox Data Systems. Xerox Meta-Symbol Sigma 5 – 9 Computers Language and Operations Reference Manual. Oct 1975. с. vi. Посетен на June 7, 2020.
  8. Sperry Univac Computer Systems. Sperry Univac Computer Systems Meta-Assembler (MASM) Programmer Reference. 1977. Посетен на June 7, 2020.
  9. How to Use Inline Assembly Language in C Code // gnu.org. Посетен на Nov 5, 2020.
  10. Finlayson, Ian. Improving processor efficiency by statically pipelining instructions // Proceedings of the 14th ACM SIGPLAN/SIGBED conference on Languages, compilers and tools for embedded systems. 2013. ISBN 9781450320856. DOI:10.1145/2465554.2465559. с. 33 – 44.
  11. Hyde, Randall. „Chapter 12 – Classes and Objects“. The Art of Assembly Language, 2nd Edition. No Starch Press. © 2010.
  12. а б Intel Architecture Software Developer's Manual, Volume 2: Instruction Set Reference. Intel Corporation, 1999. с. 442 and 35. Посетен на 18 ноември 2010.
  13. Evans, David. x86 Assembly Guide // University of Virginia, 2006. Посетен на 18 ноември 2010.
  14. Concept 14 Macros // MVS Software. Посетен на 25 май 2009.
  15. Rusling, David A. The Linux Kernel // Посетен на Mar 11, 2012.
  16. а б Writing the Fastest Code, by Hand, for Fun: A Human Computer Keeps Speeding Up Chips // New York Times, John Markoff, 28 ноември 2005. Посетен на 4 март 2010.
  17. Bit-field-badness // hardwarebug.org, 30 януари 2010. Архивиран от оригинала на 5 февруари 2010. Посетен на 4 март 2010.
  18. GCC makes a mess // HardwareBug.org, 13 май 2009. Архивиран от оригинала на 16 март 2010. Посетен на 4 март 2010.
  19. Randall Hyde. The Great Debate // Архивиран от оригинала на 2008-06-16. Посетен на 3 юли 2008.
  20. Code sourcery fails again // hardwarebug.org, 30 януари 2010. Архивиран от оригинала на 2 април 2010. Посетен на 4 март 2010.
  21. Click, Cliff. A Crash Course in Modern Hardware // Посетен на 1 май 2014.
  22. BLAS Benchmark-August2008 // eigen.tuxfamily.org, 1 август 2008. Посетен на 4 март 2010.
  23. x264.git/common/x86/dct-32.asm // git.videolan.org, 29 септември 2010. Архивиран от оригинала на 2012-03-04. Посетен на 29 септември 2010.
  24. 68K Programming in Fargo II // Архивиран от оригинала на 2 юли 2008. Посетен на 3 юли 2008.
  25. Hyde, Randall. Foreword („Why would anyone learn this stuff?“), op. cit. // 30 септември 1996. Архивиран от оригинала на 2010-03-25. Посетен на 5 март 2010.
  Тази страница частично или изцяло представлява превод на страницата Assembly language и страницата „Язык ассемблера“ в Уикипедия на английски и руски език. Оригиналните текстове, както и този превод, са защитени от Лиценза „Криейтив Комънс – Признание – Споделяне на споделеното“, а за творби, създадени преди юни 2009 година – от Лиценза за свободна документация на ГНУ. Прегледайте историята на редакциите на оригиналните страници тук и тук, за да видите списъка на техните съавтори. ​

ВАЖНО: Този шаблон се отнася единствено до авторските права върху съдържанието на статията. Добавянето му не отменя изискването да се посочват конкретни източници на твърденията, които да бъдат благонадеждни.