Limbajul de programare asamblare. Scufundați-vă în asamblator. Un curs complet despre programare în Asma de la ][. Limbaje mașină, limbaje de asamblare și

Am cultivat această idee de mult timp. Probabil că am luat-o cu asalt din toate părțile timp de câțiva ani și de fiecare dată când ne-a ieșit ceva în cale. Pe de o parte, asamblatorul este la fel de cool pe cât poate fi abilitatea de a comunica cu un computer în limbajul său pentru cititorul-hacker (cracker, reverser). Pe de altă parte, există suficiente manuale actuale despre ASCM, inclusiv publicațiile acestui secol, iar zilele acestea sunt liberale, hackerii web și iubitorii de JS s-ar putea să nu ne înțeleagă sau să ne aprobe. 🙂 Succesul a pus capăt disputei dintre fizicieni, textieri, vechi credincioși, nikonieni, hackeri web și crackeri ai muncii. S-a dovedit că acum, în secolul XXI, crackerii forței de muncă încă nu au renunțat la pozițiile lor și cititorii noștri sunt interesați de acest lucru!

Dar ce este programarea în sine în esența sa, indiferent de orice limbaj? Varietatea răspunsurilor este uimitoare. Cel mai adesea puteți auzi această definiție: programarea este compilarea de instrucțiuni sau comenzi pentru execuția secvențială de către o mașină pentru a rezolva o anumită problemă. Acest răspuns este destul de corect, dar, în opinia mea, nu reflectă plenitudinea, de parcă am numi literatura o compilație de cuvinte din propoziții pentru citire secvențială de către cititor. Înclin să cred că programarea este mai aproape de creativitate, de artă. Ca orice tip de artă - expresia gândurilor creative, ideilor, programarea este o reflectare a gândirii umane. O idee poate fi atât genial, cât și complet mediocră.

Dar, indiferent de tipul de programare pe care îl facem, succesul depinde de abilitățile practice combinate cu cunoștințele fundamentale si teorii. Teorie și practică, studiu și muncă - acestea sunt pietrele de temelie pe care se bazează succesul.

ÎN În ultima vreme assembler este nemeritat în umbra altor limbi. Acest lucru se datorează comercializării globale care vizează maximizarea timp scurt obține cât mai mult profit posibil din produs. Cu alte cuvinte, caracterul de masă a prevalat asupra elitismului. Și assembler, după părerea mea, este mai aproape de acesta din urmă. Este mult mai profitabil să antrenezi un student în, de exemplu, limbaje precum C++, C#, PHP, Java, JavaScript, Python într-un timp relativ scurt, astfel încât să fie mai mult sau mai puțin capabil să creeze software de calitate pentru consumator. fără să-și pună întrebări de ce și de ce face asta decât să elibereze bun specialistîn asamblator. Un exemplu în acest sens este piața vastă pentru tot felul de cursuri de programare în orice limbă, cu excepția asamblarii. Aceeași tendință poate fi observată atât în ​​predarea la universități, cât și în literatura educațională. În ambele cazuri, până la astăzi cea mai mare parte a materialului se bazează pe procesoarele din seria 8086 timpurie, pe așa-numitul mod de funcționare „real” pe 16 biți, Mediul de operare MS-DOS! Este posibil ca unul dintre motive să fie că, pe de o parte, odată cu apariția computerelor IBM PC, profesorii au fost nevoiți să treacă la această platformă specială din cauza inaccesibilității altora. Pe de altă parte, pe măsură ce s-a dezvoltat linia 80x86, a rămas capacitatea de a rula programe în modul DOS, ceea ce a făcut posibilă economisirea banilor la achiziționarea unora noi calculatoare educaționaleși compilarea manualelor pentru studierea arhitecturii noilor procesoare. Cu toate acestea, acum o astfel de alegere a platformei de studiu este complet inacceptabilă. MS-DOS ca mediu de execuție a programelor a fost depășit fără speranță la mijlocul anilor 90, iar odată cu trecerea la procesoare pe 32 de biți, începând cu procesorul 80386, sistemul de comandă în sine a devenit mult mai logic. Deci, este inutil să petreci timp studiind și explicând ciudateniile arhitecturii în mod real, care, evident, nu vor mai apărea niciodată pe niciun procesor.

În ceea ce privește alegerea unui mediu de operare pentru învățarea asamblatorului, dacă vorbim despre sistemul de instrucțiuni pe 32 de biți, alegerea este relativ mică. Acestea fie funcționează sisteme Windows, sau reprezentanți ai familiei UNIX.

De asemenea, ar trebui să spuneți câteva cuvinte despre ce asamblator să alegeți pentru un anumit mediu de operare. După cum știți, două tipuri de sintaxă de asamblare sunt utilizate pentru a funcționa cu procesoarele x86 - sintaxa AT&T și sintaxa Intel. Aceste sintaxe reprezintă aceleași comenzi în moduri complet diferite. De exemplu, comanda din sintaxa Intel arată astfel:

Mutare eax, ebx

Sintaxa AT&T va avea o formă diferită:

Movl %eax,%ebx

În mediul UNIX OS, sintaxa de tip AT&T este mai populară, dar nu există tutoriale despre ea; este descrisă exclusiv în literatura de referință și tehnică. Prin urmare, este logic să alegeți un asamblator bazat pe sintaxa Intel. Pentru sistemele UNIX există doi asamblatori principali - NASM (Netwide Assembler) și FASM (Flat Assembler). Pentru linii de ferestre FASM și MASM (Macro Assembler) de la Microsoft, și a existat și TASM (Turbo Assembler) de la Borland, care cu destul de mult timp în urmă a refuzat să-și susțină propria creație.

În această serie de articole, vom studia în mediul Windows bazat pe limbajul de asamblare MASM (pur și simplu pentru că îmi place mai mult). Mulți autori, în stadiul inițial al studierii asamblatorului, îl introduc în shell-ul limbajului C, pe baza considerațiilor care merg la exemple practiceîn mediul de operare se presupune că este destul de dificil: trebuie să cunoașteți atât elementele de bază ale programării în acesta, cât și comenzile procesorului. Cu toate acestea, această abordare necesită și cel puțin cele mai mici rudimente de cunoaștere în limbajul C. Încă de la început, această serie de articole se va concentra doar asupra asamblatorului în sine, fără a confunda cititorul cu orice altceva care este de neînțeles pentru el, deși în viitor va fi urmărită o legătură cu alte limbi.

Trebuie remarcat faptul că atunci când învățați elementele de bază ale programării, iar acest lucru se aplică nu numai programării de asamblare, este extrem de util să aveți o înțelegere a culturii aplicațiilor console. Și este complet nedorit să începeți imediat să învățați prin crearea de ferestre, butoane, adică cu aplicații cu ferestre. Există o părere că consola este o relicvă arhaică a trecutului. Cu toate acestea, nu este. Aplicație de consolă este aproape lipsit de orice dependență externă de shell-ul ferestrei și este concentrat în principal pe îndeplinirea unei sarcini specifice, care oferă o oportunitate excelentă, fără a fi distras de nimic altceva, de a se concentra pe învățarea elementelor fundamentale atât ale programării, cât și ale asamblatorului în sine, inclusiv familiarizarea cu algoritmii și dezvoltarea acestora pentru a rezolva probleme practice. Și până când va veni momentul să treceți la familiarizarea cu aplicațiile ferestre, veți avea deja o cantitate impresionantă de cunoștințe în spate, o înțelegere clară a funcționării procesorului și, cel mai important, conștientizarea acțiunilor dvs.: cum și ce funcționează, de ce și de ce.

Ce este asamblatorul?

Cuvântul în sine asamblator(assembler) este tradus din engleză ca „assembler”. De fapt, acesta este numele unui program de traducere care ia ca text de intrare care conține simboluri ale comenzilor mașinii care sunt convenabile pentru oameni și traduce aceste simboluri într-o secvență de coduri de comandă a mașinii corespunzătoare care sunt înțelese de procesor. Spre deosebire de instrucțiunile mașinii, convențiile lor, numite și mnemonice, sunt relativ ușor de reținut deoarece sunt abrevieri ale cuvintelor englezești. În cele ce urmează, pentru simplitate, ne vom referi la mnemonici ca instrucțiuni de asamblare. Limba simboluri si se numeste limbaj de asamblare.

În zorii erei computerelor, primele computere ocupau încăperi întregi și cântăreau mai mult de o tonă, cu o capacitate de memorie de mărimea creierului unei vrăbii, sau chiar mai mică. Singura modalitate de a programa în acele vremuri era să scrieți programul direct în memoria computerului. formă digitală, comutare întrerupătoare, cablare și butoane. Numărul acestor comutatoare putea ajunge la câteva sute și creștea pe măsură ce programele deveneau mai complexe. A apărut întrebarea despre economisirea timpului și a banilor. Prin urmare, următorul pas în dezvoltare a fost apariția la sfârșitul anilor patruzeci ai secolului trecut a primului traducător-asamblator, care a făcut posibilă scrierea comodă și simplă a comenzilor mașinii în limbajul uman și, ca urmare, automatizarea întregului procesul de programare, simplifică și accelerează dezvoltarea programelor și depanarea acestora. Apoi au venit limbi de nivel înalt și compilatoare(generatoare de cod mai inteligente dintr-un limbaj mai citit de om) și interpreți(executorii unui program scris de om din mers). S-au îmbunătățit și s-au îmbunătățit - și în cele din urmă s-a ajuns la punctul în care puteți programa pur și simplu cu mouse-ul.

Deci, asamblatorul este mecanic orientat spre limbaj programare, care vă permite să lucrați direct cu computerul, unul la unul. De aici formularea sa completă - un limbaj de programare de a doua generație de nivel scăzut (după codul mașinii). Comenzile de asamblare corespund unu la unu comenzilor procesorului, dar din moment ce există diverse modele procesoare cu propriul set de instrucțiuni, apoi, în consecință, există varietăți, sau dialecte, de limbaj de asamblare. Prin urmare, utilizarea termenului „limbaj de asamblare” poate duce la concepția greșită că există un singur limbaj de nivel scăzut sau cel puțin un standard pentru astfel de limbi. Nu exista. Prin urmare, atunci când denumim limba în care este scris un anumit program, este necesar să se clarifice ce arhitectură este destinat și ce dialect al limbii este scris. Deoarece asamblatorul este legat de dispozitivul procesor, iar tipul de procesor determină strict setul de comenzi disponibile în limbajul mașinii, programele de asamblare nu sunt portabile la alte arhitecturi de computer.

Deoarece assemblerul este doar un program scris de o persoană, nimic nu împiedică un alt programator să-și scrie propriul asambler, ceea ce se întâmplă adesea. De fapt, nu este atât de important ce limbaj de asamblare să studiezi. Principalul lucru este să înțelegeți însuși principiul de funcționare la nivelul comenzilor procesorului și atunci nu va fi dificil să stăpâniți nu numai un alt asamblator, ci și orice alt procesor cu propriul set de comenzi.

Sintaxă

Nu există un standard general acceptat pentru sintaxa limbajelor de asamblare. Cu toate acestea, majoritatea dezvoltatorilor de limbaje de asamblare urmează abordări tradiționale generale. Principalele astfel de standarde sunt Sintaxa IntelȘi Sintaxa AT&T.

Formatul general pentru instrucțiunile de înregistrare este același pentru ambele standarde:

[etichetă:] opcode [operanzi] [;comentar]

Un opcode este de fapt o comandă de asamblare, un mnemonic al instrucțiunilor către procesor. Pot fi adăugate prefixe (de exemplu, repetări, modificări ale tipului de adresare). Operanzii pot fi constante, nume de registre, adrese din RAM și așa mai departe. Diferențele dintre standardele Intel și AT&T se referă în principal la ordinea în care operanzii sunt listați și la sintaxa acestora pentru diferite metode de adresare.

Comenzile folosite sunt de obicei aceleași pentru toate procesoarele din aceeași arhitectură sau familie de arhitecturi (printre cele cunoscute sunt comenzi pentru procesoare și controlere Motorola, ARM, x86). Acestea sunt descrise în specificațiile procesorului.

Un tip de limbaj de programare de nivel scăzut, vedeți mai multe despre originile și utilizarea termenului.

Comenzile din limbajul de asamblare corespund una la una comenzilor procesorului și, de fapt, reprezintă o formă simbolică convenabilă de scriere (cod mnemonic) pentru comenzi și argumente. De asemenea, limbajul de asamblare asigură legarea părților de program și a datelor prin etichete, care se realizează în timpul asamblarii (se calculează o adresă pentru fiecare etichetă, după care fiecare apariție a etichetei este înlocuită cu această adresă).

Fiecare model de procesor, în principiu, are propriul set de instrucțiuni și un limbaj de asamblare (sau dialect) corespunzător.

De obicei, programele sau secțiunile de cod sunt scrise în limbaj de asamblare în cazurile în care este esențial pentru dezvoltator să optimizeze parametri precum performanța (de exemplu, la crearea driverelor) și dimensiunea codului (sectoare de pornire, software pentru microcontrolere și procesoare cu resurse limitate). , viruși, atașamente).

Conectarea codului de asamblare la alte limbi

Majoritatea compilatoarelor moderne vă permit să combinați codul scris în diferite limbaje de programare într-un singur program. Acest lucru vă permite să scrieți rapid programe complexe folosind un limbaj de nivel înalt, fără a pierde performanța în sarcinile critice de timp, folosind piese scrise în limbaj de asamblare pentru acestea. Combinarea se realizează în mai multe moduri:

Inserarea fragmentelor în limbaj de asamblare în textul programului (folosind directive speciale de limbaj) sau de scriere a procedurilor în limbaj de asamblare. Metoda este bună pentru transformări simple de date, dar cod de asamblare complet - cu date și subrutine, inclusiv subrutine cu multe intrări și ieșiri care nu sunt acceptate limbi de nivel înalt, nu o poți face cu ea.
Compilare modulară. Majoritatea compilatoarelor moderne funcționează în două etape. În prima etapă, fiecare fișier de program este compilat într-un modul obiect. Și pe al doilea, modulele obiect sunt legate (conectate) în program gata făcut. Frumusețea compilației modulare este că fiecare modul obiect al unui program viitor poate fi scris în întregime în propriul său limbaj de programare și compilat cu propriul compilator (asamblator).

Sintaxă

Nu există un standard unic pentru sintaxa limbajelor de asamblare; un anumit dezvoltator este liber să-și stabilească propriile reguli de sintaxă. Cu toate acestea, există abordări tradiționale pe care limbajele de asamblare le urmează pentru cele mai comune arhitecturi de procesoare, un fel de standard de facto. Deci standardele principale sunt standardele - Intel și AT&T.

Fiecare instrucțiune este scrisă pe o linie separată.

Formatul complet al fiecărei linii de instrucțiuni este următorul:

Etichetă: cod ; cometariu

Unde etichetă este numele etichetei; cod - de fapt, o instrucțiune în limbaj de asamblare; comentariu - comentariu.

În acest caz, una sau două componente ale liniei pot lipsi, adică linia poate consta, de exemplu, doar dintr-un comentariu sau poate conține doar o etichetă sau o instrucțiune.

Obiectele asupra cărora sunt efectuate acțiuni sunt registrele procesorului și zonele RAM. Notațiile pentru acestea fac, de asemenea, parte din sintaxă.

O instrucțiune de asamblare constă dintr-un mnemonic de comandă și o listă de argumente separate prin virgulă (unu, două sau trei în funcție de instrucțiune). Mnemonicul unei comenzi este abrevierile din trei sau patru litere ale omologilor săi, de obicei în Limba engleză, De exemplu:

Jmp - continuă execuția de la o nouă adresă de memorie (din engleză jump - jump)
mov - mutați datele (din engleză move - move)
sub - obțineți diferența a două valori (din engleză subtract - subtract)
xchg - valori de schimb în registre/celule de memorie (din schimbul englez - schimb)

Sintaxa argumentului se schimbă de la asamblator la asamblator, dar mnemonicii rămân de obicei aceleași (așa cum sunt utilizate în specificația originală a procesorului), cu două excepții: dacă asamblatorul folosește sintaxa AT&T multiplatformă, atunci mnemonicile originale sunt convertite în sintaxa AT&T.

Dacă inițial existau două standarde pentru înregistrarea mnemotecilor (sistemul de comandă a fost moștenit de la un procesor de la alt producător).

De exemplu, procesorul Zilog Z80 a moștenit sistemul de instrucțiuni Intel i8080, l-a extins și a schimbat mnemonicii (și desemnările registrului) în felul său. De exemplu, am schimbat „mov” de la Intel în „ld” (comanda de mișcare a datelor). Procesoarele Motorola Fireball au moștenit sistemul de instrucțiuni Z80, reducându-l oarecum. În același timp, Motorola a revenit oficial la mnemonicii Intel. Si in acest moment jumătate dintre asamblatorii pentru Fireball funcționează cu mnemonice Intel și jumătate cu mnemonice Zilog.

Textul programului poate fi completat cu directive de asamblare (parametri care afectează procesul de asamblare și proprietățile fișierului de ieșire).

Fiecare asamblator are propriile sale directive.

Macro-urile sunt folosite pentru a simplifica și accelera scrierea programelor în limbaj de asamblare.

Avantajele limbajului de asamblare

Maxim utilizare optimă procesor înseamnă, utilizarea a mai puține instrucțiuni și accesări la memorie și, ca rezultat - o viteză mai mare și dimensiune mai mică programe
Folosind seturi extinse de instrucțiuni ale procesorului (MMX, SSE, SSE2, SSE3)
Acces la porturile I/O și registrele speciale ale procesorului (în majoritatea sistemelor de operare, această caracteristică este disponibilă numai la nivelul modulelor și driverelor kernelului)
Capacitatea de a utiliza cod auto-modificabil (inclusiv relocabil) (în multe platforme această oportunitate nu este disponibilă, deoarece scrierea pe paginile de cod este interzisă, inclusiv în hardware, cu toate acestea, în majoritatea sistemelor disponibile public, din cauza deficiențelor lor inerente, este posibilă executarea codului conținut în segmentul (secțiunile) de date în care este permisă scrierea)
„Potrivire” maximă pentru platforma dorită

NB: Cele mai recente tehnologii de securitate introduse în sistemele de operare și compilatoare nu permit crearea de cod auto-modificabil, deoarece exclud posibilitatea executării și scrierii simultane a programului în aceeași zonă de memorie (tehnologia W^X).

Tehnologia W^X este folosită în OpenBSD (unde a apărut), în alte sisteme BSD, în Linux; V Microsoft Windows(începând cu Windows XP SP2) este utilizată o tehnologie similară DEP.

Defecte

Cantități mari de cod, un număr mare de sarcini mici suplimentare, mai puține biblioteci disponibile pentru utilizare în comparație cu limbile de nivel înalt
Complexitatea citirii și găsirii erorilor (deși foarte mult depinde de comentarii și stilul de programare)
Adesea, un compilator de limbaj de nivel înalt, datorită algoritmilor moderni de optimizare, oferă mai mult program eficient(din punct de vedere al raportului calitate/timp de dezvoltare).
Nu este portabil pe alte platforme (cu excepția celor compatibile).
Limbajul de asamblare este mai dificil pentru proiectele de colaborare.

Exemplu de program în limbaj de asamblare

Exemplu de program de operație sisteme DOS pe un procesor din familia Intel x86, afișând un salut pe ecran (scris în TASM):

Mov bx,1 ; indicând direcția de ieșire (către ecran)
mov cx,13 ; specificarea numărului de caractere dintr-o linie
mov dx, mesaj offset; pune offset-ul liniei în registrul DX
mov ah,40h; selectând o funcție de ieșire de linie
int 21h; apelând întreruperea DOS „Set de rutină” pentru a imprima o linie
int 20h; apel întreruperea DOS (încheierea programului)

Mesaj DB „Salut, lume!$”

Msg este o etichetă (identificator) care simplifică accesul la date.
Originile și critica termenului „limbaj de asamblare”

Acest tip de limbaj își trage numele de la numele traducătorului (compilatorului) din aceste limbi - assembler (asambler englez). Numele acestuia din urmă se datorează faptului că nu existau limbaje de nivel superior pe computerele antice, iar singura alternativă la crearea de programe folosind assembler era programarea directă în coduri.

Limba de asamblare în rusă este adesea numită „asamblator” (și ceva legat de acesta - „asamblator”), care, potrivit traducere in engleza cuvintele sunt incorecte, dar se încadrează în regulile limbii ruse. Cu toate acestea, asamblatorul însuși (programul) este numit și simplu „asamblator”, și nu „compilator limbaj de asamblare”, etc.

Utilizarea termenului „limbaj de asamblare” poate duce, de asemenea, la concepția greșită că există un singur limbaj de nivel scăzut, sau cel puțin un standard pentru astfel de limbi. La denumirea limbii în care este scris un anumit program, este recomandabil să clarificați pentru ce arhitectură este destinat și ce dialect al limbii este scris.

Introducere.

Se numește limba în care este scris programul sursă Intrare limba și limba în care este tradus pentru executare de către procesor este in zilele libere limbă. Procesul de conversie a limbajului de intrare în limbaj de ieșire este numit difuzat. Deoarece procesoarele sunt capabile să execute programe în limbaj mașină binar, care nu este folosit pentru programare, este necesară traducerea tuturor programelor sursă. Cunoscut doua feluri emisiuni: compilare și interpretare.

La compilare programul sursă este mai întâi tradus complet într-un program echivalent în limbajul de ieșire, numit obiect program și apoi executat. Acest proces este implementat cu ajutorul unui program special programe, numit compilator. Un compilator pentru care limbajul de intrare este o formă simbolică de reprezentare a limbajului mașină (de ieșire) al codurilor binare se numește asamblator.

La interpretări Fiecare linie de text din programul sursă este analizată (interpretată) și comanda specificată în acesta este imediat executată. Implementarea acestei metode îi este încredințată program de interpret. Interpretarea durează mult. Pentru a-și crește eficiența, în loc să proceseze fiecare linie, interpretul le convertește mai întâi pe toate echipășiruri în caractere (

). Secvența generată de simboluri este utilizată pentru a îndeplini funcțiile atribuite programului original.

Limbajul de asamblare discutat mai jos este implementat folosind compilare.

Caracteristicile limbii.

Principalele caracteristici ale asamblatorului:

● în loc de coduri binare, limba folosește nume simbolice - mnemonice. De exemplu, pentru comanda de adăugare (

) sunt folosite mnemonice

Scăderi (

inmultire (

Diviziuni (

etc. Numele simbolice sunt, de asemenea, folosite pentru adresa celulelor de memorie. Pentru a programa în limbaj de asamblare, în loc de coduri și adrese binare, trebuie să cunoașteți doar nume simbolice pe care asamblatorul le traduce în coduri binare;

fiecare afirmatie corespunde o comandă de mașină(cod), adică există o corespondență unu-la-unu între comenzile mașinii și operatori într-un program în limbaj de asamblare;

● limba oferă acces la toate obiectele si echipe. Limbile de nivel înalt nu au această abilitate. De exemplu, limbajul de asamblare vă permite să verificați biți din registrul steagului și limbajul de nivel înalt (de exemplu,

) nu are această capacitate. Rețineți că limbajele de programare a sistemelor (de exemplu, C) ocupă adesea o poziție intermediară. În ceea ce privește accesibilitatea, acestea sunt mai apropiate de limbajul de asamblare, dar au sintaxa unui limbaj de nivel înalt;

● limbaj de asamblare nu este un limbaj universal. Fiecare grup specific de microprocesoare are propriul său asamblator. Limbile de nivel înalt nu au acest dezavantaj.

Spre deosebire de limbajele de nivel înalt, scrierea și depanarea unui program în limbaj de asamblare necesită mult timp. În ciuda acestui fapt, limbajul de asamblare a primit utilizare largă datorita urmatoarelor circumstante:

● un program scris în limbaj de asamblare este mult mai mic și rulează mult mai rapid decât un program scris într-un limbaj de nivel înalt. Pentru unele aplicații, acești indicatori joacă un rol principal, de exemplu, mulți programe de sistem(inclusiv compilatoare), programe pe cărți de credit, celulare, drivere de dispozitiv etc.;

● unele proceduri necesită acces complet la hardware, ceea ce este de obicei imposibil de realizat într-un limbaj de nivel înalt. Acest caz include întreruperi și gestionari de întreruperi în sisteme de operare, precum și controlere de dispozitiv în sistemele încorporate care funcționează în timp real.

În majoritatea programelor, doar un mic procent din codul total este responsabil pentru un procent mare din timpul de execuție al programului. De obicei, 1% din program este responsabil pentru 50% din timpul de execuție, iar 10% din program este responsabil pentru 90% din timpul de execuție. Prin urmare, pentru a scrie un program specific în condiții reale, se utilizează atât asamblatorul, cât și unul dintre limbajele de nivel înalt.

Format operator în limbaj de asamblare.

Un program în limbaj de asamblare este o listă de comenzi (instrucțiuni, propoziții), fiecare dintre acestea ocupând o linie separată și conține patru câmpuri: un câmp de etichetă, un câmp de operare, un câmp de operand și un câmp de comentariu. Fiecare câmp are o coloană separată.

Câmp de etichetă.

Coloana 1 este alocată pentru câmpul etichetă. Eticheta este un nume simbolic sau un identificator, adrese memorie. Este necesar pentru a putea:

● face o trecere condiționată sau necondiționată la comandă;

● obțineți acces la locația în care sunt stocate datele.

Astfel de declarații sunt prevăzute cu o etichetă. Pentru a indica un nume, sunt folosite litere (majuscule) din alfabetul englez și numere. Numele trebuie să aibă o literă la început și un separator de două puncte la sfârșit. Eticheta două puncte poate fi scrisă pe o linie separată, iar opcode-ul poate fi scris pe următoarea linie din coloana 2, ceea ce simplifică munca compilatorului. Absența două puncte nu permite distingerea unei etichete de un cod de operație dacă acestea sunt situate pe linii separate.

În unele versiuni ale limbajului de asamblare, două puncte sunt plasate numai după etichetele de instrucțiuni, nu după etichetele de date, iar lungimea etichetei poate fi limitată la 6 sau 8 caractere.

Nu ar trebui să existe nume identice în câmpul de etichetă, deoarece eticheta este asociată cu adrese de comandă. Dacă în timpul execuției programului nu este nevoie să apelați o comandă sau date din memorie, atunci câmpul de etichetă rămâne gol.

Câmpul pentru codul operațiunii.

Acest câmp conține codul mnemonic pentru o comandă sau pseudo-comandă (vezi mai jos). Codul mnemonic al comenzii este ales de dezvoltatorii limbajului. În limbaj de asamblare

mnemonic este selectat pentru a încărca un registru din memorie

), și pentru a salva conținutul registrului în memorie - un mnemonic

). În limbaje de asamblare

pentru ambele operațiuni puteți folosi, respectiv, același nume

Dacă alegerea numelor mnemonice poate fi arbitrară, atunci necesitatea de a utiliza două instrucțiuni de mașină este determinată de arhitectura procesorului

Mnemotecnia registrelor depinde și de versiunea de asamblare (Tabelul 5.2.1).

Câmp operand.

Aici se află Informații suplimentare, necesare efectuarii operatiei. În câmpul operand pentru comenzile de săritură este indicată adresa la care trebuie efectuată săritura, precum și adresele și registrele care sunt operanzi pentru comanda mașinii. Ca exemplu, oferim operanzi care pot fi utilizați pentru procesoare pe 8 biți

● date numerice,

prezentat în diverse sisteme Socoteala. Pentru a indica sistemul de numere utilizat, constanta este urmată de una dintre litere latine: IN,

În consecință, sistemele de numere binar, octal, hexazecimal, zecimal (

Nu trebuie să-l scrieți). Dacă prima cifră număr hexazecimal sunt A, B, C,

Apoi se adaugă un 0 (zero) nesemnificativ în față;

● coduri ale registrelor interne ale microprocesorului și celulelor de memorie

M (surse sau receptori de informații) sub forma literelor A, B, C,

M sau adresele acestora în orice sistem de numere (de exemplu, 10B - adresa de înregistrare

V sistem binar);

● identificatori,

pentru perechi de aeronave de înregistrare,

Primele litere sunt B,

N; pentru o pereche de acumulator și registru de caracteristici -

; pentru contorul de programe -

;pentru indicatorul stivei -

● etichete care indică adresele operanzilor sau instrucțiunile următoare din condițional

(dacă este îndeplinită condiția) și tranziții necondiționate. De exemplu, operandul M1 din comandă

înseamnă necesitatea unei tranziții necondiționate la comandă, a cărei adresă în câmpul de etichetă este marcată cu identificatorul M1;

● expresii,

care sunt construite prin legarea datelor discutate mai sus folosind aritmetica și operatori logici. Rețineți că metoda de rezervare a spațiului de date depinde de versiunea limbii. Dezvoltatori de limbaj de asamblare pentru

Definiți cuvântul), iar ulterior a introdus o opțiune alternativă.

care a fost în limbajul pentru procesoare încă de la început

În versiune lingvistică

folosit

Definiți o constantă).

Procesoarele procesează operanzi de diferite lungimi. Pentru a-l defini, dezvoltatorii de asamblare au adoptat solutii diferite, De exemplu:

II registre de diferite lungimi au nume diferite: EAX - pentru plasarea operanzilor pe 32 de biți (tip

); AX - pentru 16 biți (tip

și AN - pentru 8 biți (tip

● pentru procesoare

La fiecare cod de operare se adaugă sufixe: sufix

Pentru tip

; sufixul „.B” pentru tip

diferite opcode sunt utilizate pentru operanzi de diferite lungimi, de exemplu, pentru a încărca un octet, jumătate de cuvânt (

) și cuvinte într-un registru de 64 de biți folosind opcodes

respectiv.

Câmp de comentarii.

Acest câmp oferă explicații despre acțiunile programului. Comentariile nu afectează funcționarea programului și sunt destinate oamenilor. Ele pot fi necesare pentru a modifica un program, care fără astfel de comentarii poate fi complet de neînțeles chiar și pentru programatorii experimentați. Un comentariu începe cu un simbol și este folosit pentru a explica și documenta programe. Caracterul de început al unui comentariu poate fi:

● punct și virgulă (;) în limbile pentru procesoarele companiei

● semnul exclamării (!) în limbi pentru

Fiecare linie de comentariu separată este precedată de un caracter principal.

Pseudo-comenzi (directive).

În limbajul de asamblare există două tipuri principale de comenzi:

de bază instrucțiuni care sunt echivalentul codului mașinii procesorului. Aceste comenzi efectuează toate procesările prevăzute de program;

pseudo-comenzi sau directive, conceput pentru a deservi procesul de traducere a unui program într-un limbaj de combinare de coduri. Ca exemplu în tabel. 5.2.2 prezintă câteva pseudo-comenzi de la asamblator

pentru familie

.

La programare, sunt situații în care, conform algoritmului, același lanț de comenzi trebuie repetat de mai multe ori. Pentru a ieși din această situație puteți:

● scrieți secvența necesară de comenzi ori de câte ori apare. Această abordare duce la o creștere a volumului programului;

● aranjați această secvență într-o procedură (subrutină) și apelați-o dacă este necesar. Această ieșire are dezavantajele sale: de fiecare dată trebuie să executați o comandă de apel de procedură specială și o comandă de returnare, care, dacă secvența este scurtă și utilizată frecvent, poate reduce foarte mult viteza programului.

Cel mai simplu și metoda eficienta repetarea repetată a unui lanț de comenzi constă în folosirea macro, care poate fi gândit ca o pseudo-comandă menită să retransmite un grup de comenzi des întâlnite într-un program.

O macrocomandă sau macrocomandă se caracterizează prin trei aspecte: macrodefiniție, macroinversie și macroextensie.

Definiție macro

Aceasta este o desemnare pentru o secvență repetată în mod repetat de comenzi de program, folosită pentru referințe în textul programului.

Definiția macro are următoarea structură:

Lista de expresii; Definiție macro

În structura dată de macro-definiție, se pot distinge trei părți:

● titlu

macro, inclusiv numele

Pseudo-comandă

și un set de parametri;

● marcate cu puncte corp macro;

● echipa

absolvire

definiții macro.

Setul de parametri de definiție macro conține o listă cu toți parametrii dați în câmpul operand pentru grupul de instrucțiuni selectat. Dacă acești parametri au fost indicați mai devreme în program, atunci nu trebuie să fie indicați în antetul definiției macro.

Pentru a reasambla grupul selectat de comenzi, se folosește un apel constând din nume

comenzi macro și listă de parametri cu alte valori.

Când asamblatorul întâlnește o definiție de macro în timpul procesului de compilare, o stochează în tabelul de definiții de macro. La aparițiile ulterioare în programul numelui (

) a unei macrocomenzi, asamblatorul o înlocuiește cu corpul macro-ului.

Utilizarea unui nume de macrocomandă ca opcode este apelată macro-inversare(apel macro) și înlocuirea acestuia cu corpul macro-ului - extinderea macro.

Dacă un program este reprezentat ca o secvență de caractere (litere, numere, spații, semne de punctuație și întoarceri de cărucior pentru a trece la o nouă linie), atunci extinderea macro constă în înlocuirea unor lanțuri din această secvență cu alte lanțuri.

Extinderea macro-ului are loc în timpul procesului de asamblare, nu în timpul execuției programului. Le sunt atribuite metode de manipulare a șirurilor de caractere macro înseamnă.

Procesul de asamblare este efectuat in doua treceri:

● La prima trecere, toate definițiile macro sunt păstrate, iar apelurile macro sunt extinse. În acest caz, programul original este citit și convertit într-un program în care toate definițiile macro sunt eliminate și fiecare apel de macro este înlocuit cu corpul macro-ului;

● a doua trecere procesează programul rezultat fără macro-uri.

Macro-uri cu parametri.

Pentru a lucra cu secvențe repetate de comenzi, ai căror parametri pot lua valori diferite, sunt furnizate definiții macro:

● cu real parametrii care sunt plasați în câmpul operand al apelului macro;

● cu formal parametrii. În timpul extinderii macro, fiecare parametru formal care apare în corpul macro-ului este înlocuit cu parametrul real corespunzător.

folosind macro-uri cu parametri.

Programul 1 conține două secvențe similare de comenzi, care diferă prin faptul că prima schimbă P și

Iar al doilea

Programul 2 include o macro cu doi parametri formali P1 și P2. În timpul extinderii macro, fiecare caracter P1 din corpul macro este înlocuit cu primul parametru real (P,

), iar simbolul P2 este înlocuit cu al doilea parametru real (

) din programul nr. 1. În apelul macro

programul 2 este marcat: P,

Primul parametru real,

Al doilea parametru real.

Programul 1

Programul 2

MOV EBX,Q MOV EAX,Pl

MOV Q,EAX MOV EBX,P2

MOV P,EBX MOV P2,EAX

Capabilitati extinse.

Să ne uităm la câteva caracteristici avansate ale limbii

Dacă o macrocomandă care conține o comandă de salt condiționat și o etichetă la care trebuie sărită este apelată de două sau de mai multe ori, eticheta va fi duplicată (problema cu eticheta duplicată), ceea ce va cauza o eroare. Prin urmare, fiecare apel atribuie o etichetă separată ca parametru (de către programator). În limbaj

eticheta este declarată locală (

) și datorită capabilităților avansate, asamblatorul generează automat o etichetă diferită de fiecare dată când macro-ul este extins.

vă permite să definiți macrocomenzi în interiorul altor macrocomenzi. Această caracteristică avansată este foarte utilă în combinație cu legarea condiționată a unui program. Sa luam in considerare

IF WORDSIZE GT 16 M2 MACRO

Macro-ul M2 poate fi definit în ambele părți ale declarației

Cu toate acestea, definiția depinde de procesorul pe care este asamblat programul: 16 biți sau 32 de biți. Dacă M1 nu este apelat, atunci macro M2 nu va fi definită deloc.

O altă caracteristică avansată este că macrocomenzile pot apela alte macrocomenzi, inclusiv ele însele - recursiv apel. În acest din urmă caz, pentru a evita o buclă fără sfârșit, macro-ul trebuie să-și transmită un parametru care se modifică cu fiecare extindere și, de asemenea, Verifica acest parametru și încheie recursiunea când parametrul atinge o anumită valoare.

Despre utilizarea mijloacelor macro în asamblare.

Când utilizați macrocomenzi, asamblatorul trebuie să poată îndeplini două funcții: salvați definițiile macroȘi extinde provocările macro.

Salvarea definițiilor macro.

Toate numele macrocomenzilor sunt stocate într-un tabel. Fiecare nume este însoțit de un indicator către macrocomanda corespunzătoare, astfel încât să poată fi apelat dacă este necesar. Unii asamblatori au masa separata pentru nume de macro, altele - un tabel general în care, împreună cu numele de macro, sunt situate toate comenzile și directivele de mașină.

Când întâlniți o macrocomandă în timpul asamblarii este creat:

element nou Mese cu numele macro-ului, numărul de parametri și un pointer către un alt tabel de definire a macrocomenzii în care va fi stocat corpul macro-ului;

● listă formal parametrii.

Corpul macrocomenzii, care este pur și simplu un șir de caractere, este apoi citit și stocat în tabelul de definire a macrocomenzii. Parametrii formali găsiți în corpul unei bucle sunt marcați cu un simbol special.

Reprezentarea internă a unei macrocomenzi

din exemplul de mai sus pentru programul 2 (p. 244) este:

MOV EAX, MOV EBX, MOV MOV &

unde punctul și virgulă este folosit ca caracter de returnare a căruciorului, iar ampersand & este folosit ca caracter parametru formal.

Extinderea apelurilor macro.

Ori de câte ori o definiție macro este întâlnită în timpul asamblarii, aceasta este stocată în tabelul macro. Când este apelată o macrocomandă, asamblatorul oprește temporar citirea datelor de intrare de pe dispozitivul de intrare și începe să citească corpul macro-ului stocat. Parametrii formali extrași din corpul macro sunt înlocuiți cu parametri reali și furnizați de apel. Parametrii ampersand & before permit asamblatorului să-i recunoască.

În ciuda faptului că există multe versiuni de asamblare, procesele de asamblare au caracteristici comune și sunt similare în multe privințe. Funcționarea unui asamblator cu două treceri este discutată mai jos.

Asamblator cu două treceri.

Un program constă dintr-un număr de declarații. Prin urmare, s-ar părea că la asamblare, puteți utiliza următoarea secvență de acțiuni:

● traducerea acestuia în limbajul mașinii;

● transfer primit Codul mașinii la un fișier, iar partea corespunzătoare a listei - la un alt fișier;

● repetați procedurile enumerate până când întregul program este tradus.

Cu toate acestea, această abordare nu este eficientă. Un exemplu este așa-numita problemă link direct. Dacă prima instrucțiune este un salt la instrucțiunea P, situată la sfârșitul programului, atunci asamblatorul nu o poate traduce. Mai întâi trebuie să determine adresa operatorului P, iar pentru a face acest lucru trebuie să citească întregul program. Fiecare citire completă a programului sursă este apelată trecere. Să arătăm cum puteți rezolva problema link-ului anticipat folosind două treceri:

la prima trecere ar trebui colectareași stocați toate definițiile simbolurilor (inclusiv etichetele) în tabel, iar la a doua trecere, citiți și asamblați fiecare operator. Această metodă este relativ simplă, dar o a doua trecere prin programul original necesită timp suplimentar petrecut cu operațiunile I/O;

● la prima trecere ar trebui convertit programul într-o formă intermediară și salvați-l într-un tabel și efectuați a doua trecere nu conform programului original, ci conform tabelului. Această metodă de asamblare economisește timp, deoarece a doua trecere nu efectuează operațiuni I/O.

Prima trecere.

Gol din prima pasă- construiți un tabel de simboluri. După cum sa menționat mai sus, un alt obiectiv al primei treceri este păstrarea tuturor definițiilor macro și extinderea apelurilor pe măsură ce apar. În consecință, atât definirea simbolului, cât și extinderea macro au loc într-o singură trecere. Simbolul poate fi oricare eticheta, sau sens, căruia i se atribuie un nume specific folosind directiva -you:

;Valoare - dimensiunea tamponului

Atribuind semnificații numelor simbolice în câmpul de etichetă a comenzii, asamblatorul specifică în esență adresele pe care fiecare comandă le va avea în timpul execuției programului. În acest scop, asamblatorul stochează în timpul procesului de asamblare contor de adrese de instrucțiuni(

) ca variabilă specială. La începutul primei treceri, valoarea variabilei speciale este setată la 0 și crește după fiecare comandă procesată de lungimea acelei comenzi. Ca exemplu în tabel. 5.2.3 prezintă un fragment de program care indică lungimea comenzilor și valorile contorului. La prima trecere, sunt generate tabele nume simbolice, directiveȘi coduri de operare, iar dacă este necesar literal masa. Un literal este o constantă pentru care asamblatorul își rezervă automat memorie. Să notăm imediat că procesoare moderne conțin comenzi cu adrese imediate, astfel încât asamblatorii lor nu acceptă literale.

Tabel cu nume de simbol

conține câte un element pentru fiecare nume (Tabelul 5.2.4). Fiecare element al tabelului de nume simbolice conține numele în sine (sau un indicator către acesta), valoarea sa numerică și, uneori, câteva informații suplimentare, care pot include:

● lungimea câmpului de date asociat simbolului;

● biți de realocare a memoriei (care indică dacă valoarea unui simbol se modifică dacă programul este încărcat la o adresă diferită de cea prevăzută de asamblator);

● informații despre dacă simbolul poate fi accesat din afara procedurii.

Numele simbolice sunt etichete. Ele pot fi specificate folosind operatori (de exemplu,

Tabel directiv.

Acest tabel listează toate directivele, sau pseudo-comenzile, care sunt întâlnite la asamblarea unui program.

Tabelul de coduri de operare.

Pentru fiecare cod de operare, tabelul are coloane separate: denumirea codului de operare, operandul 1, operandul 2, valoarea hexazecimală a codului de operare, lungimea comenzii și tipul comenzii (Tabelul 5.2.5). Codurile de operare sunt împărțite în grupuri în funcție de numărul și tipul operanzilor. Tipul de comandă determină numărul grupului și specifică procedura care este apelată pentru a procesa toate comenzile din acel grup.

A doua trecere.

Golul celei de-a doua pasi- realizarea unui program obiect si tiparirea, daca este cazul, a protocolului de asamblare; informațiile de ieșire necesare pentru linker pentru a lega procedurile care au fost asamblate la momente diferite într-un singur fișier executabil.

În a doua trecere (ca și în prima), rândurile care conțin declarațiile sunt citite și procesate unul câte unul. Declarația originală și cea derivată din ea în sistem hexazecimal zi libera obiect Codul poate fi tipărit sau plasat într-un buffer pentru imprimare ulterioară. După resetarea contorului de adrese de instrucțiune, este apelată următoarea instrucțiune.

Programul sursă poate conține erori, de exemplu:

simbolul dat nu este definit sau este definit de mai multe ori;

● este prezentat codul tranzacției nume invalid(din cauza greșelii), nu are destui operanzi sau are prea mulți operanzi;

● fără operator

Unii asamblatori pot detecta un simbol nedefinit și îl pot înlocui. Cu toate acestea, în majoritatea cazurilor, când întâlnește o declarație de eroare, asamblatorul afișează un mesaj de eroare pe ecran și încearcă să continue procesul de asamblare.

Articole dedicate limbajului de asamblare.

Limbaj de programare

Assembler este un limbaj de programare de nivel scăzut, care este un format pentru înregistrarea comenzilor mașinii care este convenabil pentru percepția umană.

Comenzile din limbajul de asamblare corespund una la una comenzilor procesorului și, de fapt, reprezintă o formă simbolică convenabilă de înregistrare (cod mnemonic) a comenzilor și a argumentelor acestora. Limbajul de asamblare oferă, de asemenea, abstracții de programare de bază: legarea părților programului și a datelor prin etichete și directive denumite simbolic.

Directivele de asamblare vă permit să includeți blocuri de date (descrise explicit sau citite dintr-un fișier) într-un program; repetați un anumit fragment de un anumit număr de ori; compilați fragmentul în funcție de condiție; setați adresa de execuție a unui fragment, modificați valorile etichetelor în timpul procesului de compilare; utilizați definiții macro cu parametri etc.

Fiecare model de procesor, în principiu, are propriul set de instrucțiuni și un limbaj de asamblare (sau dialect) corespunzător.

Avantaje și dezavantaje

  • cantitate minimă de cod redundant (folosirea mai puține instrucțiuni și accesări la memorie). Rezultatul este o viteză mai mare și o dimensiune mai mică a programului.
  • cantități mari de cod, un număr mare de sarcini mici suplimentare
  • lizibilitate slabă a codului, greu de suportat (depanare, adăugare de caracteristici)
  • dificultatea implementării paradigmelor de programare și orice alte convenții oarecum complexe, dificultatea dezvoltării în comun
  • mai puține biblioteci disponibile, compatibilitatea lor scăzută
  • acces direct la hardware: porturi I/O, registre speciale de procesor
  • capacitatea de a scrie cod cu auto-modificare (adică metaprogramare, fără a fi nevoie de un interpret software)
  • „potrivire” maximă pentru platforma dorită (utilizarea instrucțiunilor speciale, caracteristicile tehnice ale hardware-ului)
  • neportabilitatea pe alte platforme (cu excepția celor compatibile cu binar).

Sintaxă

Nu există un standard general acceptat pentru sintaxa limbajelor de asamblare. Cu toate acestea, există standarde de facto - abordări tradiționale la care aderă majoritatea dezvoltatorilor de limbaje de asamblare. Principalele astfel de standarde sunt sintaxa Intel și sintaxa AT&T.

Formatul general pentru instrucțiunile de înregistrare este același pentru ambele standarde:

`[eticheta:] opcode [operanzi] [;comment]`

Un opcode este un mnemonic direct al instrucțiunilor către procesor. Pot fi adăugate prefixe (repetări, modificări ale tipului de adresare etc.). Operanzii pot fi constante, nume de registre, adrese din RAM etc. Diferențele dintre standardele Intel și AT&T se referă în principal la ordinea în care sunt listați operanzii și la sintaxa lor atunci când diverse metode adresarea.

Mnemoteciile utilizate sunt de obicei aceleași pentru toate procesoarele din aceeași arhitectură sau familie de arhitecturi (printre cele mai cunoscute sunt mnemonice pentru procesoare și controlere Motorola, ARM, x86). Acestea sunt descrise în specificațiile procesorului.

De exemplu, procesorul Zilog Z80 a moștenit sistemul de instrucțiuni Intel i8080, l-a extins și a schimbat mnemonicii (și desemnările registrului) în felul său. De exemplu, am schimbat mov Intel la ld. Procesoarele Motorola Fireball au moștenit sistemul de instrucțiuni Z80, reducându-l oarecum. În același timp, Motorola a revenit oficial la mnemonicii Intel. iar în acest moment, jumătate dintre asamblatorii pentru Fireball lucrează cu mnemonice Intel și jumătate cu mnemonice Zilog.

Directive

Pe lângă instrucțiuni, un program poate conține directive: comenzi care nu sunt traduse direct în instrucțiuni de mașină, dar controlează funcționarea compilatorului. Setul și sintaxa lor variază semnificativ și depind nu de platforma hardware, ci de compilatorul utilizat (generând dialecte ale limbilor în cadrul aceleiași familii de arhitecturi). Setul de directive include:

  • definirea datelor (constante și variabile)
  • gestionarea organizării programului în memorie și a parametrilor fișierului de ieșire
  • setarea modului de operare al compilatorului
  • tot felul de abstracții (adică elemente de limbaje de nivel înalt) - de la proiectarea procedurilor și funcțiilor (pentru a simplifica implementarea paradigmei programare procedurală) la constructe și bucle condiționate (pentru paradigma programare structurată)
  • macro-uri

Originile și critica termenului „limbaj de asamblare”

Acest tip de limbaj își trage numele de la numele traducătorului (compilatorului) din aceste limbi - assembler (asambler englez). Numele acestuia din urmă se datorează faptului că pe primele computere nu existau limbaje de nivel superior, iar singura alternativă la crearea de programe folosind assembler era programarea directă în coduri.

Limba de asamblare în limba rusă este adesea numită „asamblator” (și ceva legat de acesta „asamblator”), care, conform traducerii în engleză a cuvântului, este incorectă, dar se încadrează în regulile limbii ruse. Cu toate acestea, asamblatorul însuși (programul) este numit și simplu „asamblator” și nu „compilator limbaj de asamblare”, etc.

Utilizarea termenului „limbaj de asamblare” poate duce, de asemenea, la concepția greșită că există un singur limbaj de nivel scăzut, sau cel puțin un standard pentru astfel de limbi. La denumirea limbii în care este scris un anumit program, este recomandabil să clarificați pentru ce arhitectură este destinat și ce dialect al limbii este scris.

Elemente de sintaxă:

Exemple:

Salut Lume!:

Exemplu pentru versiunile Intel x86 (IA32).

mov ax , cs mov ds , axe mov ah , 9 mov dx , offset Hello int 21h xor ax , ax int 21h Hello : db "Hello World!", 13, 10, "$"

Salut Lume!:

Exemplu pentru versiunile Amiga

mișcare. l #DOS muta . l 4. w , a6 jsr - $0198(a6) ; Mutare OldOpenLibrary . l d0 , a6 beq . s. Mișcă afară. l #HelloWorld , d1 A ) moveq #13, d2 jsr - $03AE (a6 ) ; WriteChars B ) jsr - $03B4 ; PutStr muta . l a6, a1 mutare. l 4. w , a6 jsr - $019E (a6 ) ; Închideți Biblioteca. Out rts DOS dc. b "dos.library" , 0 HelloWorld dc . b „Bună lume!” , $A , 0

Salut Lume!:

Exemplu pentru versiunile AtariST

mișcare. l #helloworld , - (A7 ) mutare #9, - (A7 ) capcană #1 addq . l #6, A7 mutare #0, - (A7) capcană #1 helloworld : dc . b „Bună lume!”, $0d, $0a, 0

Salut Lume!:

Exemplu pentru versiunile Intel x86 (IA32).

NASM Linux, se folosește sintaxa Intel. Compilare și legare:

  • nasm –f elf –o hello.o hello.asm
  • ld -o salut alo.o

SECȚIUNE. data msg db „Bună ziua, lume!”, 0xa len equ $ - msg SECTION . text global _start _start : ; Punct de intrare program mov eax, 4; apel de sistem „scriere” mov ebx, 1 mov ecx, msg; Pointer la date mov edx, len; Număr de date int 0x80 ; Kernel call mov eax, 1; Apel de sistem „_exit” mov ebx , 0 ; Returnează 0 (totul este bine) int 0x80 ; Apelarea nucleului

Salut Lume!:

Exemplu pentru versiunile PDP-8

/ - comentarii.

/Hello World în assembler pentru DEC PDP - 8 * 200 hello , cla cll tls /tls setează steagul de imprimare. tad charac / creează un registru de index dca ir1 / pentru a primi caractere tad m6 / setează contor pentru dca count / introducere de caractere. în continuare, tad i ir1 / get symbol. jms tip / tipul său. isz count / face altceva? jmp următor / nu, introduceți un alt tip de caracter hlt , 0 / tip de subrutină tsf jmp . - 1 tls cla jmp i tip charac , . / este folosit ca valoare inițială a lui ir1. 310 / H 305 / E 314 / L 314 / L 317 / O 254 / , 240 / 327 / L 317 / O 322 / R 314 / L 304 / D 241 / ! m6 , - 15 count , 0 ir1 = 10 $

Salut Lume!:

Exemplu pentru versiunile PDP-11

Programul este scris în macro-asamblatorul MACRO-11. Pentru a compila și rula acest program în sistemul de operare RT-11, comandăm:

MACRO SALUT

ERORI DETECTE: 0

LINK SALUT -- Link. RUN HELLO -- Lansați

TITLUL HELLO WORLD ; Nume . MCALL. TTYOUT,. EXIT HELLO :: MOV #MSG , R1 ; Adresa de pornire a liniei 1$: MOVB (R1) + , R0 ; Obținem următorul simbol BEQ DONE; Dacă este zero, ieșim din buclă. TTYOUT; Altfel tipărim simbolul BR 1$ ; Repetați ciclul TERMINAT: . EXIT MSG: . ASCIZ /Bună, lume!/; Linia Bună, lume! . SFÂRȘIT SALUT; Sfârșitul programului HELLO

Salut Lume!:

Exemplu pentru versiunile System/360, System/370

IBM System/360/370/390 Basic Assembler Language.

// EXEC ASSEMBLY START MAIN BALR 2, 0 FOLOSIND *, 2 OPEN PRINT MVC BUF, HW PUT PRINT CLOSE PRINT EOJ HW DC CL132 "HELLO WORLD" BUF DS CL132 PRINT DTFPR IOAREA1 = BUF, DEVADDR = BLKSISLST *, =132 DEVICE = 3203 , CONTROL = YES , PRINTOV = YES END MAIN /* // EXEC LNKEDT // EXEC /* /&

Salut Lume!:

Exemplu pentru versiunile Apple II

* HELLO WORLD PENTRU 6502 APPLE ][ * ******************************** STROUT EQU $DB3A LDY #> HELLO LDA #< HELLO JMP STROUT HELLO ASC "HELLO WORLD !", 00

Salut Lume!:

Exemplu pentru versiunile PDP-10

CHTTYO - Toate I/O se fac folosind canale I/O. Cel mai bine este să creați nume simbolice pentru canalele pe care le utilizați și să le începeți cu CH. Definiți aceste nume folosind operatorul MIDAS == .

CALL este o notație simbolică pentru apelarea unui apel de sistem. Formatul său este .CALL.

OPEN deschide un canal I/O pentru utilizare. Necesită doi parametri - numărul canalului și numele dispozitivului în SIXBIT.

LOSE %LSFIL este un apel de sistem care tipărește un mesaj de eroare I/O dacă apare brusc.

IOT este un apel de sistem care se ocupă de fapt de I/O. Ca parametru, trebuie să specificați canalul și adresa care conțin codul simbol pentru ieșire. De exemplu, „H reprezintă H.

TITLUL PRINTHELLO A = 1 CHTTYO == 1 ; Canal de ieșire. START: ; Deschiderea unui canal TTY. . Apelați [ SETZ ? SIXBIT/OPEN/[. UAO, CHTTYO]? [SIXBIT/TTY/] ((SETZ))] . PIERDE %LSFIL . IOT CHTTYO ,[ "H ] ; Tipăriți HELLO WORLD caracter cu caracter. . IOT CHTTYO ,[ "E ] . IOT CHTTYO ,[ "L ]. IOT CHTTYO ,[ "L ] . IOT CHTTYO ,[ "O ]. IOT CHTTYO ,[ ^M ] ; Simbol linie nouă. IOT CHTTYO ,[ "W ]. IOT CHTTYO ,[ "O ] . IOT CHTTYO ,[ "R ]. IOT CHTTYO ,[ "L ] . IOT CHTTYO ,[ "D ] . VALUE ; Program, stop :) END START

numerele Fibonacci:

Exemplu pentru versiunile MIPS32

Emulator MARS. Ieșirea consolei MARS:

Numerele Fibonacci sunt: ​​1 1 2 3 5 8 13 21 34 55 89 144 -- programul s-a terminat --

Programul afișează 15 numere Fibonacci. Numărul de numere poate fi modificat în secțiunea .data.

Spațiu de date: .asciiz " " head: .asciiz „Numerele Fibonacci sunt:\n” fib: .word 0 : 15 size : .word 15 .text main: la $t0 , fib la $t5 , size lw $t5 , 0 ($t5 ) li $t2 , 1 add.d $f0 , $f2 , $ f4 sw $t2 , 0 ($t0 ) sw $t2 , 4 ($t0 ) adi $t1 , $t5 , - 2 buclă : lw $t3 , 0 ($t0 ) lw $t4 , 4 ($t0 ) adăuga $ t2 , $t3 , $t4 sw $t2 , 8 ($t0 ) addi $t0 , $t0 , 4 addi $t1 , $t1 , - 1 bgtz $t1 , bucla la $a0 , fib muta $a1 , $t5 jal print li $v0 , 10 syscall print : add $t0 , $zero , $a0 add $t1 , $zero , $a1 la $a0 , head li $v0 , 4 syscall out : lw $a0 , 0 ($t0 ) li $v0 , 1 syscall la $a0 , space li $v0 , 4 syscall addi li $v0 , 1 la $a0 , ($t2 ) syscall la $a0 , string1 li $v0 , 4 syscall mult $t1 , $t2 mflo $ t1 li $v0 , 1 la $a0 , ($t1 ) syscall la $a0 , string2 li $v0 , 4 syscall addiu $t2 , $t2 , 1 beq $t2 , 16 , endloop j loop endloop: li $v0 , 10 syscall



Nu se poate spune că assembler este unul dintre limbajele de programare nemeritat uitate, cu toate acestea, o opinie negativă despre capacitățile și fezabilitatea sa de utilizare a devenit foarte răspândită. Dacă acest punct de vedere este susținut de programatori începători sau „utilizatori avansați”, atunci acest lucru poate fi atribuit lipsei de abilități practice și cunoștințelor profesionale limitate. Dar afirmațiile negative care pot fi găsite în unele cărți și articole mă nedumeresc personal. Mai mult, cred că o astfel de critică neconstructivă la adresa oricărei limbi este pur și simplu dăunătoare. Această împrejurare a servit drept motiv pentru a scrie aceste note.

Este logic să comparăm limbajul de comandă cu limbajele algoritmice numai în raport cu complexitatea și intensitatea muncii a procesului de programare. Desigur, în această comparație, assemblerul este inferior limbajelor algoritmice, dar asta nu înseamnă că nu ar trebui să-l cunoașteți și să îl puteți utiliza dacă este necesar.

După cum știți, un limbaj de programare universal nu a fost încă inventat și necesitatea acestuia nu este deloc evidentă. Prin urmare, pentru a extinde domeniul de activitate și a îmbunătăți nivelul profesional, un programator trebuie să vorbească mai multe limbi (să fie poliglot) și este de dorit ca una dintre ele să fie asamblator. Mai jos voi încerca să susțin acest punct de vedere.

Puțină istorie

Limbajul de asamblare (asambler) este un limbaj de programare orientat către mașină. Tradus din engleză, cuvântul asamblare înseamnă asamblare, asamblare, instalare etc. Alegerea acestui nume se explică prin faptul că primii traducători din Algol, Fortran, Cobol și PL/I au folosit ca limbaj intermediar assembler la asamblarea unui program. din module individuale.

La un moment dat, crearea unui asamblator a fost primul și foarte important pas către automatizarea procesului de programare. I-a eliberat pe programatori de a scrie texte de program ca o secvență de coduri octale sau hexazecimale. Acum este posibil să folosiți nume și Simboluri speciale pentru a indica instrucțiunile mașinii, operanzii și adresele acestora. În plus, traducătorii și compilatorii mai târziu au făcut posibilă utilizarea directivelor speciale pentru descrierea variabilelor, formatarea definițiilor macro și programele de segmentare. Toate acestea au fost mult simplificate muncă de rutină programator

Limbajul de asamblare nu a rămas mult timp în grupul de conducere; a fost înlocuit de limbaje algoritmice. Au avut nevoie de o pregătire mai puțin specială din partea programatorului, iar independența mașinii lor a făcut posibilă organizarea unui schimb larg de algoritmi și programe și publicarea lor în tipărire. Limbajul de asamblare a devenit treptat limbajul profesioniștilor.

Odată cu venirea calculatoare personale(PC) s-a balansat pendulul reversulși, de ceva vreme, asamblatorul a fost din nou printre lideri. Acest lucru s-a întâmplat deoarece capacitățile limitate ale primelor modele de PC nu permiteau utilizarea compilatoarelor din limbaje de nivel înalt. Dar progresul tehnologic a pus treptat totul la locul său, iar programatorii au început din nou să acorde preferință limbajelor algoritmice.

Diferite limbaje de asamblare apar, dezvoltă și dispar împreună cu familiile de procesoare sau microprocesoare pe care sunt proiectate să le programeze. Prin urmare, versiunile compilatoarelor corespunzătoare sunt actualizate atunci când devin disponibile noi modele de procesoare sau microprocesor. Și editurile interne și străine lansează regulat noi cărți despre programare în limbaj de asamblare.

Caracteristicile specifice ale limbajului

Limbajul de asamblare este un limbaj de programare de nivel scăzut; un program compilat în el este o secvență de comenzi pentru un anumit computer, adesea o anumită familie de computere, scrise într-o anumită formă mnemotică convențională. Orientarea mașinii este cea care determină avantajele și dezavantajele acestui limbaj.

Un avantaj incontestabil al asamblatorului este capacitatea de a compune programe care utilizează rațional toate caracteristicile sistemului de instrucțiuni ale unui anumit computer. Oferă posibilități nelimitate pentru diverse tipuri de trucuri (în sensul bun al cuvântului), totul depinde de abilitățile profesionale ale programatorului și de ingeniozitatea acestuia.

O altă proprietate pozitivă este versatilitatea limbajului - vă permite să creați un program pentru orice problemă care are o soluție și poate fi rezolvată pe mașinile acestei familii. Această afirmație se bazează pe faptul evident că orice program scris într-un limbaj de nivel înalt este convertit într-o secvență de instrucțiuni de mașină atunci când este compilat.

Dezavantajul evident este nivelul scăzut de abstractizare de la caracteristicile unui anumit computer, necesitatea de a cunoaște și de a lua în considerare aceste caracteristici. În timp ce lucrează cu limbaje de nivel înalt, programatorul își poate concentra complet atenția asupra caracteristicilor algoritmului implementat.

Un alt dezavantaj este numărul mare, și uneori foarte mare, de comenzi executate de o anumită mașină. De exemplu, microprocesorul Pentium 4 are aproximativ 500 dintre ele! Acest lucru vă obligă să utilizați cărți de referință speciale atunci când lucrați, dar să nu păstrați în cap toată varietatea de nume de comenzi și caracteristici ale executării lor.

Asamblator și macro-asamblator

Din punctul de vedere al unui programator, limbajul de asamblare este un subset al limbajului de asamblare macro. Acesta din urmă compilează în mod necesar setul complet de instrucțiuni pentru o anumită familie de microprocesoare și, în plus, execută set mare operatori și directive (denumite în continuare macro mijloace) în diverse scopuri. Programatori care lucrează pentru calculatoare IBM PC, cele mai populare asamblatoare de macro sunt MASM ( Microsoft) și TASM (compania Borland), care au aproximativ aceleași capacități și sunt în mare măsură (dar nu complet) compatibile. ÎN aceasta sectiune vom vorbi despre caracteristicile MASM.

În primul rând, trebuie remarcat faptul că, în forma sa pură, limbajul de asamblare este utilizat numai atunci când se creează inserții în textele programului compilate în limbaje de nivel înalt. Fără utilizarea instrumentelor macro este imposibil să compuneți chiar și o simplă subrutină, ca să nu mai vorbim de programe complete, dar fără a utiliza comenzile de asamblare acest lucru este posibil.

Sub anumite conditii text original program care face suficient actiuni complexe, poate conține doar mijloace macro. Nu va fi o singură comandă de asamblare în ea! Despre ce fel de complexitate și intensitate de muncă a programării putem vorbi în astfel de cazuri?

Manualele MASM, inclusiv HELP, descriu de obicei 8 tipuri de operatori și 12 tipuri de directive; începând cu versiunea MASM 6.10, numărul acestora nu sa schimbat. În cadrul acestui articol, nu are sens să cităm Descriere completa macro înseamnă, deci ne vom limita la caracteristicile lor generale.

Directivele de asamblare macro sunt concepute pentru a efectua trei categorii principale de acțiuni:

  1. gestionarea distribuției RAM se realizează prin directive menite să descrie variabile simple, structuri de date, înregistrări și segmentarea programelor;
  2. gestionarea lucrărilor cu subrutine externe și interne, fragmente de program și definiții macro se realizează prin directive de descriere și apelare explicită sau preliminară a acestora;
  3. Procesul de compilare a programului este controlat de directivele de compilare condiționată, printre care există nume familiare fiecărui programator, cum ar fi IF, ELSE, FOR, WHILE, REPEAT, GOTO.

Indiferent de scopul lor specific, toate directivele și operatorii sunt executați în etapa de compilare și ei înșiși nu sunt convertiți în comenzi. Ca urmare a executării lor, grupurile de comenzi situate în textul sursă al programului sau în biblioteci externe sunt incluse sau nu în modulul obiect. În plus, modulul obiect include datele necesare linkerului (LINK) pentru a asambla și construi sarcina.

Astfel, atunci când discutăm despre avantajele și dezavantajele programării în limbaj de asamblare, nu trebuie să uităm că, în practică, lucrăm, de regulă, cu limbajul de asamblare macro, iar acest lucru este departe de același lucru.

Tehnica de programare

Călcâiul lui Ahile al asamblatorului este complexitatea programării în acest limbaj. Ce poate face un programator pentru a-și simplifica munca?

Singurul soluție posibilă este acumularea și utilizarea experienței proprii și a altora, formatată sub formă de biblioteci sau module individuale, de preferință cu o evaluare critică a acestei experiențe. Asamblatorul macro vă permite să utilizați module sursă și obiect gata făcute atunci când dezvoltați o sarcină. Primele sunt incluse în textul sursă al programului folosind directiva Include, iar cele din urmă sunt conectate la lista de module obiect în etapa de construire a sarcinii de către linker. Mânca surse diferite module obiect și sursă, unele dintre ele mai accesibile, altele mai puțin. Alegerea depinde de capacitățile tale.

  1. Parte versiunea completa Un sistem de programare a macroasamblerilor (pachet de distribuție) include un set de macrodefiniții și subrutine pentru diverse scopuri și exemple de utilizare a acestora. Uitați-vă cu atenție prin toate subdirectoarele versiunii dvs. de asamblare de macro-uri; ele vor conține probabil soluții utile și interesante care pot fi folosite în munca dvs.
  2. Nu uitați că atunci când compuneți programe puteți utiliza module de bibliotecă care fac parte din sistemele de programare în limbaje de nivel înalt, de exemplu, C. Adevărat, pentru aceasta trebuie să aveți o descriere a acestor biblioteci, dar fără ea este imposibil să le folosiți în cadrul sistemului de programare pentru care au fost create.
  3. Dacă aveți acces la rețele de internet, apoi pe diferitele sale site-uri web puteți găsi multe soluții gata făcute pentru literalmente toate ocaziile. Doar acordați atenție datelor de dezvoltare ale programelor și subrutinelor. Multe dintre ele sunt învechite și pot fi simplificate semnificativ folosind noile instrucțiuni ale microprocesorului.
  4. Pe măsură ce lucrați cu assembler, veți acumula treptat un set de soluții proprii care vă vor fi utile în noile dezvoltări. Pentru comoditatea utilizării lor ulterioare, atunci când scrieți programe, încercați să compuneți cât mai multe subrutine, concepute ca module separate, de preferință originale mai degrabă decât obiecte - primele sunt mai ușor de modificat dacă este necesar. Utilizarea subrutinelor crește ușor dimensiunea sarcinii și încetinește procesul de finalizare a acesteia. Dar, în schimb, obțineți capacitatea de a depana program mareîn părți și utilizați subrutinele depanate în celelalte dezvoltări ale dvs.
  5. Nu uitați de existența unui instrument atât de eficient precum macro-urile - definiții macro și apeluri macro. Acestea vă permit să reduceți nu numai textul sursă al programului, ci și numărul de erori posibile la introducerea acestuia. În plus, apelurile macro sunt convenabile de utilizat atunci când solicitați funcțiile sistemului care fac multe acțiuni utile cum ar fi lucrul cu ferestre, cu Sistemul de fișiere, date de intrare și ieșire etc.

Astfel, dacă ai experiență și abilități practice, costurile cu forța de muncă la programarea în limbaj de asamblare pot fi reduse la un nivel la care se va putea dezvolta suficient proiecte mari. Desigur, înainte de a începe să le implementați, trebuie să cântăriți toate argumentele pro și contra și să luați în considerare posibilitățile reale.

Când nu există alternativă la asamblare

În mod tradițional, asamblatorul este folosit pentru a scrie subrutine și inserții în programe scrise în limbaje algoritmice. În ultimii ani, fezabilitatea unei astfel de utilizări nu numai că a crescut, ci și-a dobândit un nou sens. Acest lucru se explică prin faptul că sarcinile obținute prin programare în limbaje algoritmice folosesc extrem de ineficient resursele microprocesoarelor moderne. Desigur, aceasta nu este vina limbajelor, ci a compilatorilor, care nu acceptă lucrul cu operațiuni de grup și nu țin cont de caracteristicile procesării pipeline. Toate eforturile inginerilor și proiectanților care vizează creșterea performanței microprocesoarelor se dovedesc a fi inutile, la fel și costurile financiare pentru achiziționarea de noi echipamente.

Nu are sens să dai vina pe dezvoltatorii de compilatori pentru acest lucru - problema nu este atât de banală, iar pentru a o rezolva este necesar nu numai îmbunătățirea tehnicilor de compilare, ci și dezvoltarea unor instrumente fundamentale de limbaj noi. Suntem obișnuiți cu faptul că operațiile aritmetice și logice se efectuează pe o pereche de operanzi, uneori pe un singur operand (schimbare de semn sau negație). Și operațiunile de grup ale microprocesoarelor moderne (nu numai familia Pentium) pot funcționa cu mai multe perechi de operanzi simultan, al căror număr și adâncimea de biți nu sunt fixe.

În această situație, pur și simplu nu există alternativă la asamblare. Nu e de mirare înăuntru documentatie tehnica„Compilatorul Intel C++” recomandă trei opțiuni pentru a lucra cu comenzi noi:

  1. inserări directe de text de asamblare în corpul unui program scris în C;
  2. întocmirea de proceduri (subrutine) în limbaj de asamblare;
  3. utilizarea intrinseci (funcții încorporate).

Acestea din urmă sunt o formă alternativă de înregistrare operațiuni de grupși, în esență, ele nu simplifică (mai degrabă complică) sarcina programatorului.

Pe lângă introducerea operațiunilor de grup, procesarea instrucțiunilor prin pipeline este utilizată pentru a îmbunătăți performanța microprocesoarelor moderne. Pentru a-și extinde capacitățile, microarhitectura devine în mod constant mai complexă, ceea ce crește inevitabil costul produselor.

A profita de procesarea pipeline nu este atât de ușor pe cât ar părea. Când compunem un program, indiferent de limbă, pornim de la presupunerea că mașina execută mai întâi o comandă și abia apoi execută alta. De fapt, procesoarele moderne nu au funcționat așa de mult timp - încearcă să combine total sau parțial execuția mai multor comenzi. Evident, sarcinile obișnuite nu sunt potrivite pentru acest lucru și atunci când le efectuează, performanța procesorului nu corespunde capacităților sale reale.

În acest sens, vechea problemă de optimizare are aspecte noi: este necesară identificarea și modificarea acelor părți ale problemei în care performanța procesorului este subestimată, este de dorit să se excludă ramurile condiționate din program sau să le minimizeze (de exemplu, pe baza pe rezultatul comparării operanzilor), etc. Rezolvare probleme similareÎn procesul de compilare a unui program scris într-un limbaj algoritmic, nu este încă posibil și ne întoarcem din nou la asamblator.

Intel a dezvoltat și vinde un analizor special de performanță cu microprocesor sarcina specifica(VTuneTM Performance Analyzer 6.0). Vă permite să identificați puncte slabe(secțiuni critice ale codului sarcinii), apoi corectați-le folosind inserții de asamblare.

Puteți citi mai multe despre noile capabilități ale microprocesoarelor în articolul meu „Pentium prin ochii unui programator”. Este publicat pe internet pe pagina http://www.macro.aaanet.ru/apnd_4.html

La începutul articolului, am scris deja că în procesul de lucru, un programator din când în când trebuie să învețe noi limbi. În același timp, poate urmări diferite scopuri, de la pur și simplu satisfacerea curiozității și lărgirea orizontului până la cele pur utilitare sub forma rezolvării problemelor pentru care a fost creat un nou limbaj.

Dacă aveți de gând să vă extindeți orizonturile, atunci amintiți-vă afirmația lui E. Dijkstra despre influența limbajului asupra modului de gândire. Stăpânind doar limbaje algoritmice, te obișnuiești să gândești în categorii formale bazate pe sintaxă și semantică atunci când programezi. Nu cred că este rău, dimpotrivă, este bine, dar oarecum unilateral. Până la urmă, nu avem de-a face cu tehnologie virtuală, ci cu tehnologie reală și este indicat să ne imaginăm cum funcționează. În acest caz, vă va fi mai ușor să înțelegeți ce se poate face și ce și de ce nu se poate face folosind instrumentele unui anumit limbaj algoritmic.

Pentru a rămâne în fruntea progresului tehnologic și a putea crea sarcini care să utilizeze rațional capacitățile celor mai moderne computere, trebuie să stăpânești arta programării computerelor. În opinia mea, este imposibil să stăpânești această artă fără cunoștințe de limbaj de asamblare.