Assemblerkeel

Assembleri keel on programmeerimiskeel, mida saab kasutada selleks, et öelda otse arvutile, mida teha. Assembleri keel on peaaegu täpselt nagu masinkood, millest arvuti saab aru, ainult et selles kasutatakse numbrite asemel sõnu. Arvuti ei saa assembleriprogrammist otse aru. Siiski saab ta programmi hõlpsasti muuta masinkoodiks, asendades programmi sõnad numbritega, mida need tähistavad. Programmi, mis seda teeb, nimetatakse assembleriks.

Assembleri keeles kirjutatud programmid koosnevad tavaliselt käskudest, mis on väikesed ülesanded, mida arvuti programmi käivitamisel täidab. Neid nimetatakse käskudeks, sest programmeerija kasutab neid, et anda arvutile juhiseid, mida teha. Arvuti osa, mis järgib juhiseid, on protsessor.

Arvuti koostekeel on madala taseme keel, mis tähendab, et sellega saab teha ainult lihtsaid ülesandeid, millest arvuti saab otseselt aru. Keerulisemate ülesannete täitmiseks tuleb arvutile öelda iga lihtsa ülesande osa, mis on osa keerulisest ülesandest. Näiteks ei saa arvuti aru, kuidas printida ekraanile lause. Selle asemel peab assembleris kirjutatud programm talle ütlema, kuidas teha kõiki väikseid samme, mis on seotud lause trükkimisega.

Selline koosteprogramm koosneks paljudest, paljudest käskudest, mis koos teevad midagi, mis tundub inimesele väga lihtne ja elementaarne. Seetõttu on inimesel raske assemblerprogrammi lugeda. Seevastu kõrgetasemelises programmeerimiskeeles võib olla üks käsk, näiteks PRINT "Hello, world!", mis ütleb arvutile, et ta täidaks teie eest kõik väikesed ülesanded.

Assamblee keele areng

Kui arvutiteadlased esimest korda programmeeritavaid masinaid ehitasid, programmeerisid nad neid otse masinakoodiga, mis on numbrite rida, mis andis arvutile juhised, mida teha. Masinakeele kirjutamine oli väga raske ja võttis kaua aega, nii et lõpuks tehti assembleri keel. Assamblee keelt on inimesel lihtsam lugeda ja seda saab kirjutada kiiremini, kuid seda on inimesel siiski palju raskem kasutada kui kõrgetasemelist programmeerimiskeelt, mis püüab jäljendada inimkeelt.

Programmeerimine masinkoodis

Masinakoodis programmeerimiseks peab programmeerija teadma, kuidas iga käsk binaarselt (või heksadekaalis) välja näeb. Kuigi arvutil on lihtne kiiresti aru saada, mida masinkood tähendab, on see programmeerijale raske. Igal käsul võib olla mitu vormi, mis kõik näevad inimeste jaoks lihtsalt välja nagu hunnik numbreid. Iga viga, mille keegi teeb masinkoodi kirjutamisel, märkab alles siis, kui arvuti teeb valesti. Vea väljaselgitamine on raske, sest enamik inimesi ei oska seda vaadates öelda, mida masinkood tähendab. Näide sellest, kuidas masinkood välja näeb:

05 2A 00

See heksadekvaliteetne masinakood annab x86-arvuti protsessorile käsu lisada akumulaatorisse 42. Seda on inimesel väga raske lugeda ja mõista, isegi kui ta tunneb masinakoodi.

Assembleri keele kasutamine selle asemel

Assembleri keeles saab iga käsu kirjutada lühikese sõnana, mida nimetatakse mnemoonikaks, millele järgnevad muud asjad, näiteks numbrid või muud lühikesed sõnad. Mnemoonikat kasutatakse selleks, et programmeerija ei peaks meeles pidama täpseid numbreid masinakoodis, mida on vaja, et käskida arvutil midagi teha. Assembleri keele mnemoonika näited on näiteks add, mis lisab andmeid, ja mov, mis liigutab andmeid ühest kohast teise. Kuna "mnemoonika" on ebatavaline sõna, kasutatakse selle asemel mõnikord, sageli valesti, väljendit "käsu tüüp" või lihtsalt "käsk". Esimese sõna järel olevad sõnad ja numbrid annavad rohkem teavet selle kohta, mida teha. Näiteks võivad liitmise järel olevad asjad öelda, milliseid kahte asja kokku liita, ja liikumisele järgnevad asjad ütlevad, mida liigutada ja kuhu panna.

Näiteks eelmises punktis esitatud masinakoodi (05 2A 00) saab assembleris kirjutada järgmiselt:

 lisa ax,42

Assembleri keel võimaldab programmeerijatel ka lihtsamalt kirjutada tegelikke andmeid, mida programm kasutab. Enamikul assembleri keeltel on toetus numbrite ja teksti lihtsaks koostamiseks. Masinakoodis tuleks iga eri tüüpi arv, nagu positiivne, negatiivne või kümnendarv, käsitsi teisendada binaarseks ja tekst tuleks defineerida üks täht korraga, nagu numbrid.

Assembleri keel pakub nn masinkoodi abstraktsiooni. Assembleri kasutamisel ei pea programmeerija teadma, mida numbrid arvutile tähendavad, selle asemel arvutab seda assembler. Assembleri keel võimaldab programmeerijal tegelikult ikkagi kasutada kõiki protsessori funktsioone, mida ta saaks kasutada masinkoodiga. Selles mõttes on assembleri keelel väga hea ja haruldane omadus: ta suudab samu asju väljendada kui see, mida ta abstraheerib (masinkood), olles samas palju lihtsamini kasutatav. Seetõttu ei kasutata masinkoodi peaaegu kunagi programmeerimiskeelena.

Demonteerimine ja vigade kõrvaldamine

Kui programmid on valmis, on need juba muundatud masinkoodiks, nii et protsessor saab neid tegelikult käivitada. Mõnikord aga, kui programmis on mingi viga (viga), tahavad programmeerijad teada, mida iga masinkoodi osa teeb. Disassemblerid on programmid, mis aitavad programmeerijatel seda teha, muutes programmi masinkoodi tagasi assemblerikeeleks, mis on palju lihtsamini arusaadav. Disassemblerid, mis muudavad masinkoodi assemblerikeeleks, teevad vastupidist assembleritele, mis muudavad assemblerikeele masinkoodiks.

Arvutikorraldus

Assembleri programmi toimimise mõistmiseks on vaja arusaamist sellest, kuidas arvutid on korraldatud, kuidas nad näiliselt väga madalal tasemel töötavad. Kõige lihtsamal tasemel on arvutitel kolm peamist osa:

  1. põhimälu ehk RAM, milles hoitakse andmeid ja käske,
  2. protsessor, mis töötleb andmeid, täites käske, ja
  3. sisend ja väljund (mõnikord lühendatult I/O), mis võimaldavad arvutil suhelda välismaailmaga ja salvestada andmeid väljaspool põhimälu, et neid hiljem tagasi saada.

Põhimälu

Enamikus arvutites on mälu jagatud baitideks. Iga bait sisaldab 8 bitti. Igal mälu baidil on ka aadress, mis on number, mis ütleb, kus see bait mälus asub. Esimese baidi aadress mälus on 0, järgmise baidi aadress on 1 jne. Mälu jagamine baitideks muudab selle baitide kaupa adresseeritavaks, sest iga bait saab unikaalse aadressi. Baitmälu aadressid ei saa viidata baitide üksikule bitile. Bait on väikseim adresseeritav mäluosa.

Kuigi aadress viitab konkreetsele baidile mälus, võimaldavad protsessorid kasutada mitu baiti järjestikku. Kõige sagedamini kasutatakse seda funktsiooni 2 või 4 baiti järjest, et kujutada numbrit, tavaliselt täisarvu. Mõnikord kasutatakse täisarvude esitamiseks ka üksikuid baite, kuid kuna need on ainult 8 bitti pikad, saavad need sisaldada ainult 2 8või 256 erinevat võimalikku väärtust. Kasutades 2 või 4 baiti reas, suureneb erinevate võimalike väärtuste arv vastavalt 2 16, 65536 või 2 32, 4294967296.

Kui programm kasutab baiti või mitut baiti järjestikku, et kujutada midagi, näiteks tähte, numbreid või midagi muud, nimetatakse neid baite objektiks, sest nad kõik on osa ühest ja samast asjast. Kuigi kõik objektid on salvestatud ühesugustes mälubaitides, käsitletakse neid nii, nagu oleks neil "tüüp", mis ütleb, kuidas neid baite tuleb mõista: kas täisarvuna või märgina või mingi muu tüübina (näiteks mitte täisarvu väärtus). Masinakoodi võib mõelda ka tüübina, mida tõlgendatakse käskudena. Tüübi mõiste on väga-väga oluline, sest see määrab, milliseid asju saab ja ei saa objektiga teha ja kuidas objekti baite tõlgendada. Näiteks ei ole lubatud negatiivset arvu salvestada positiivse arvu objekti ja ei ole lubatud murdarvu salvestada täisarvu objekti.

Mitme baidiga objektile osutav (on selle aadress) aadress on selle objekti esimese baidi aadress - kõige madalama aadressiga baidi aadress. Kõrvalmärkusena olgu öeldud, et aadressi järgi ei saa öelda, mis tüüpi objekt on - või isegi selle suurust -. Tegelikult ei saa te isegi seda, mis tüüpi objekt on, seda vaadates öelda. Assembleri programm peab jälgima, millistel mäluaadressidel millised objektid asuvad ja kui suured need objektid on. Programm, mis seda teeb, on tüübikindel, sest ta teeb objektidega ainult selliseid asju, mis on nende tüübi järgi turvalised. Programm, mis seda ei tee, tõenäoliselt ei tööta korralikult. Pange tähele, et enamik programme ei salvesta tegelikult selgesõnaliselt, mis on objekti tüüp, nad lihtsalt pääsevad objektidele järjepidevalt ligi - sama objekti käsitletakse alati sama tüübina.

Protsessor

Protsessor töötab (täidab) käske, mis on salvestatud masinakoodina põhimällu. Lisaks sellele, et enamikul protsessoritel on võimalik kasutada mälu salvestamiseks, on neil ka mõned väikesed, kiired ja fikseeritud suurusega ruumid, kus hoitakse objekte, millega parajasti töötatakse. Neid ruume nimetatakse registriteks. Protsessorid täidavad tavaliselt kolme tüüpi käske, kuigi mõned käsud võivad olla nende tüüpide kombinatsioon. Allpool on toodud mõned näited igast tüübist x86 assembleri keeles.

Käsklused, mis loevad või kirjutavad mälu

Järgmine x86 assembleri käsk loeb (laeb) 2baidise objekti aadressilt 4096 (0x1000 heksadekaalis) 16-bitisse registrisse nimega 'ax':

        mov ax, [1000h]

Selles koostekeeles tähendavad numbri (või registri nime) ümber olevad nurksulgud seda, et numbrit tuleb kasutada aadressina andmete jaoks, mida tuleks kasutada. Aadressi kasutamist andmetele osutamiseks nimetatakse indirektsiooniks. Järgmises näites, ilma nurksulgudeta, laetakse teise registrisse, bx, tegelikult väärtus 20.

        mov bx, 20

Kuna indirektsiooni ei kasutatud, siis tegelik väärtus ise pandi registrisse.

Kui operandid (asjad, mis tulevad pärast mnemonüümi), esinevad vastupidises järjekorras, siis käsk, mis laeb midagi mälust, kirjutab selle hoopis mällu:

        mov [1000h], ax

Siinkohal saab mälu aadressil 1000h väärtuse ax. Kui see näide käivitatakse kohe pärast eelmist, siis on 2 baiti aadressidel 1000h ja 1001h 2 baiti täisarvu väärtusega 20.

Matemaatilisi või loogilisi operatsioone sooritavad käsud

Mõned juhised teevad selliseid asju nagu lahutamine või loogilised operatsioonid nagu mitte:

Selle artikli varasem masinakoodi näide oleks see assembleri keeles:

        lisa kirves, 42

Siin liidetakse 42 ja ax kokku ja tulemus salvestatakse tagasi axi. x86 assembleris on ka võimalik ühendada mäluühendust ja matemaatilist operatsiooni selliselt:

        add ax, [1000h]

See käsk lisab 1000h-sse salvestatud kahebaidilise täisarvu väärtuse ax-i ja salvestab vastuse ax-i.

        või ax, bx

See käsk arvutab registrite ax ja bx sisu või ja salvestab tulemuse tagasi axi.

Juhised, mis otsustavad, milline saab olema järgmine juhis

Tavaliselt täidetakse käsud selles järjekorras, milles nad ilmuvad mälus, mis on järjekorras, milles nad on sisestatud assemblerikoodis. Protsessor täidab neid lihtsalt üksteise järel. Kuid selleks, et protsessorid saaksid teha keerulisi asju, peavad nad täitma erinevaid käske vastavalt neile antud andmetele. Protsessorite võimet täita erinevaid käske sõltuvalt millegi tulemusest nimetatakse hargnemiseks. Juhiseid, mis otsustavad, milline peaks olema järgmine juhis, nimetatakse hargnemisjuhisteks.

Oletame, et keegi soovib arvutada värvikoguse, mida ta vajab teatava küljepikkusega ruudu värvimiseks. Mastaabisäästu tõttu ei müü värvipood talle aga vähem värvi kui 100 x 100 suuruse ruudu värvimiseks vajalik kogus.

Selleks, et välja arvutada värvikogus, mida nad vajavad, lähtudes ruutude pikkusest, mida nad tahavad värvida, leiavad nad järgmised sammud:

  • lahutatakse 100 küljepikkusest
  • kui vastus on väiksem kui null, määrata külje pikkuseks 100.
  • korrutatakse küljepikkus iseendaga

Seda algoritmi saab väljendada järgmise koodiga, kus ax on küljepikkus.

        mov bx, ax     sub bx, 100    jge continue   mov ax, 100 continue: mul ax

See näide tutvustab mitmeid uusi asju, kuid kaks esimest juhendit on tuttavad. Nad kopeerivad väärtuse ax väärtusesse bx ja seejärel lahutatakse bx-st 100.

Üks uus asi selles näites on nn silt, mis on assembleri keeltes üldiselt esinev mõiste. Märgistus võib olla mis tahes, mida programmeerija tahab (välja arvatud juhul, kui see on käsu nimi, mis ajab assembleri segadusse). Selles näites on silt 'continue'. Assembler tõlgendab seda kui käsu aadressi. Antud juhul on see mult axi aadress.

Teine uus kontseptsioon on lipud. x86-protsessoritel seavad paljud käsud protsessoris "lipud", mida järgmine käsk saab kasutada, et otsustada, mida teha. Antud juhul, kui bx oli väiksem kui 100, siis sub seab lipu, mis ütleb, et tulemus oli väiksem kui null.

Järgmine käsk on jge, mis on lühend sõnast 'Jump if Greater than or Equal to'. See on hargnemiskäsk. Kui protsessori lipud näitavad, et tulemus oli suurem või võrdne nulliga, siis järgmise käsu asemel hüppab protsessor järgmise käsu juurde, mis on mul ax.

See näide töötab hästi, kuid see ei ole see, mida enamik programmeerijaid kirjutaks. Subtraktsioonikäsk kehtestas lipu õigesti, kuid see muudab ka väärtust, millega ta töötab, mis nõudis, et ax kopeeritaks bx-sse. Enamik assemblerkeeli lubab võrdluskäsklusi, mis ei muuda ühtegi argumenti, mida neile edastatakse, kuid seavad lipud ikkagi õigesti ja x86 assembler ei ole erandiks.

        cmp ax, 100    jge continue   mov ax, 100 continue: Mul ax

Nüüd, selle asemel, et lahutada axist 100, vaadata, kas see arv on väiksem kui null, ja määrata see tagasi axile, jäetakse ax muutumatuks. Lipud seatakse endiselt samamoodi ja hüpe toimub endiselt samades olukordades.

Sisend ja väljund

Kuigi sisend ja väljund on arvutite põhiline osa, ei ole assembleri keeles ühte ja sama viisi, kuidas neid teha. Selle põhjuseks on see, et see, kuidas I/O töötab, sõltub arvuti seadistusest ja operatsioonisüsteemist, mitte ainult sellest, milline protsessor tal on. Näiteosas toodud Hello World näide kasutab MS-DOSi operatsioonisüsteemi kõnesid ja sellele järgnev näide kasutab BIOSi kõnesid.

On võimalik teha I/O-d assembleri keeles. Assembleri keel võib üldiselt väljendada kõike, mida arvuti suudab teha. Kuid kuigi assembleri keeles on olemas käsud lisamiseks ja hargnemiseks, mis teevad alati sama asja, ei ole assembleri keeles ühtegi käsku, mis teeksid alati I/O-d.

Oluline on märkida, et see, kuidas I/O töötab, ei ole osa ühestki assembleri keelest, sest see ei ole osa protsessori tööpõhimõtetest.

Assamblee keeled ja teisaldatavus

Kuigi assembleri keelt ei kasuta otseselt protsessor, vaid masinkood, on sellega siiski palju pistmist. Iga protsessoriperekond toetab erinevaid funktsioone, käske, reegleid selle kohta, mida käsud võivad teha, ja reegleid selle kohta, millised käskude kombinatsioonid on kus lubatud. Selle tõttu vajavad eri tüüpi protsessorid ikkagi erinevaid assemblerikeeli.

Kuna assembleri keele iga versioon on seotud protsessoriperekonnaga, puudub tal midagi, mida nimetatakse teisaldatavuseks. Midagi, mis on kaasaskantav või teisaldatav, saab hõlpsasti üle kanda ühest arvutitüübist teise. Kui muud tüüpi programmeerimiskeeled on teisaldatavad, siis assembleri keel üldiselt ei ole.

Assembleri keel ja kõrgkeeled

Kuigi assembleri keel võimaldab hõlpsasti kasutada kõiki protsessori funktsioone, ei kasutata seda tänapäevastes tarkvaraprojektides mitmel põhjusel:

  • Lihtsa programmi väljendamine assembleris nõuab palju vaeva.
  • Kuigi assembleri keel ei ole nii vigaohtlik kui masinkood, pakub see siiski väga vähe kaitset vigade eest. Peaaegu kõik assembleri keeled ei taga tüübikindlus.
  • Assembleri keel ei soodusta häid programmeerimistavasid nagu modulaarsus.
  • Kuigi iga üksikut assembleri käsku on lihtne mõista, on raske öelda, milline oli selle kirjutanud programmeerija kavatsus. Tegelikult on programmi assemblerikeelest nii raske aru saada, et ettevõtted ei muretse selle pärast, et inimesed oma programme lahti monteerivad (saavad assembleri keelest aru).

Nende puuduste tõttu kasutatakse enamikus projektides selle asemel kõrgetasemelisi keeli nagu Pascal, C ja C++. Need võimaldavad programmeerijatel oma ideid otsesemalt väljendada, selle asemel, et muretseda, kuidas protsessorile igal sammul öelda, mida ta peab tegema. Neid nimetatakse kõrgetasemeliseks, sest ideed, mida programmeerija saab sama palju koodiga väljendada, on keerulisemad.

Programmeerijad, kes kirjutavad koodi kompileeritud kõrgtasemelistes keeltes, kasutavad programmi, mida nimetatakse kompilaatoriks, et muuta oma kood assembleri keeleks. Kompilaatorite kirjutamine on palju raskem kui assemblerite kirjutamine. Samuti ei võimalda kõrgetasemelised keeled programmeerijatel alati kasutada kõiki protsessori funktsioone. See on tingitud sellest, et kõrgtasandi keeled on loodud nii, et nad toetavad kõiki protsessoriperekondi. Erinevalt assemblerikeeltest, mis toetavad ainult ühte tüüpi protsessoreid, on kõrgetasemelised keeled kaasaskantavad.

Kuigi kompilaatorid on keerulisemad kui assemblerid, on aastakümneid kestnud kompilaatorite valmistamine ja uurimine muutnud need väga heaks. Nüüd ei ole enamasti enam põhjust kasutada assemblerikeelt enamasti projektide jaoks, sest kompilaatorid oskavad tavaliselt sama hästi või paremini kui programmeerijad aru saada, kuidas programme assemblerkeeles väljendada.

Näidisprogrammid

x86 Assembleris kirjutatud programm Hello World:

adosseg .model small .stack 100h .data hello_message db 'Hello, World! ',0dh,0ah,'$' .code main proc mov ax,@data mov ds,ax mov ah,9 mov dx,offset hello_message int 21h mov ax,4C00h int 21h main endp end main

Funktsioon, mis väljastab ekraanile numbri, kasutades BIOSi katkestusi, mis on kirjutatud NASM x86 assembleris. Modulaarkoodi on võimalik kirjutada assembleris, kuid see nõuab lisatööd. Pange tähele, et kõik, mis tuleb pärast semikoolonit real, on kommentaar ja assembler ignoreerib seda. Kommentaaride lisamine assembleri koodile on väga oluline, sest suuri assembleri programme on väga raske mõista.

; void printn(int number, int base); printn: push    bp      mov     bp, sp push    ax         push    bx      push    cx      push    dx      push    si      mov     si, 0   mov        ax, [bp + 4]   ; number       mov     cx, [bp + 6]   ; alus gloop:  inc     si               ; stringi pikkus       mov     dx, 0          ; null dx      div     cx               ; jagab baasiga        cmp     dx, 10         ; on see ge 10?        jge        num     add     dx, '0'        ; lisab dx-le nulli    jmp     anum num:      add        dx, ('A'- 10)  ; heksaväärtus, lisa 'A' dx - 10. anum:       push    dx              ; pane dx virna.       cmp     ax, 0          ; kas peaksime jätkama?        jne        gloop  mov     bx, 7h         ; katkestuseks tloop:  pop     ax             ; saada selle väärtus    mov     ah, 0eh        ; katkestuse jaoks     int     10h             ; kirjuta märk dec     si             ; vabaneda tähemärgist         jnz        tloop  pop     si      pop     dx      pop     cx      pop     bx      pop     ax         pop     bp      ret    

Küsimused ja vastused

K: Mis on assamblee?


V: Assembleri keel on programmeerimiskeel, mida saab kasutada selleks, et öelda otse arvutile, mida teha. See on peaaegu täpselt nagu masinkood, millest arvuti saab aru, ainult et see kasutab numbrite asemel sõnu.

K: Kuidas saab arvuti assemblerprogrammist aru?


V: Arvuti ei saa assemblerprogrammist tegelikult otse aru, kuid ta saab programmi kergesti muuta masinkoodiks, asendades programmi sõnad numbritega, mida need tähistavad. See protsess toimub assembleri abil.

K: Mis on käsud assembleri keeles?


V: Assembleri keeles olevad käsud on väikesed ülesanded, mida arvuti programmi käivitamisel täidab. Neid nimetatakse juhisteks, sest nad annavad arvutile juhiseid, mida teha. Arvuti osa, mis vastutab nende juhiste täitmise eest, nimetatakse protsessoriks.

K: Mis tüüpi programmeerimiskeel on assembler?


V: Assembleri keel on madala taseme programmeerimiskeel, mis tähendab, et sellega saab teha ainult lihtsaid ülesandeid, millest arvuti saab otseselt aru. Keerulisemate ülesannete täitmiseks tuleb iga ülesanne jaotada üksikuteks komponentideks ja anda iga komponendi jaoks eraldi juhised.

K: Mille poolest erineb see kõrgetasemelistest keeltest?


V: Kõrgetasemelistes keeltes võivad olla üksikud käsud, näiteks PRINT "Hello, world!", mis ütleb arvutile, et ta täidab kõik need väikesed ülesanded automaatselt, ilma et neid oleks vaja eraldi täpsustada, nagu seda tuleks teha assemblerprogrammi puhul. See muudab kõrgtasandi keeled inimestele kergemini loetavaks ja arusaadavaks kui assemblerprogrammid, mis koosnevad paljudest üksikutest käskudest.

K: Miks võib inimesel olla raske assemblerprogrammi lugeda?


V: Sest keerulise ülesande, näiteks millegi ekraanile printimise või andmekogumitega arvutuste tegemise jaoks - asjad, mis tunduvad loomulikus inimkeeles väljendatuna väga lihtsad ja lihtsad - tuleb täpsustada palju üksikuid juhiseid, nii et ühe juhise koostamisel võib olla palju koodiridu, mis teevad inimestele, kes ei tea, kuidas arvutid sisemiselt nii madalal tasemel töötavad, raskeks nende jälgimise ja tõlgendamise.

AlegsaOnline.com - 2020 / 2023 - License CC3