Zbirni jezik
Montažni jezik je programski jezik, s katerim lahko računalniku neposredno poveste, kaj naj naredi. Montažni jezik je skoraj povsem podoben strojni kodi, ki jo razume računalnik, le da namesto številk uporablja besede. Računalnik v resnici ne more neposredno razumeti programa assemblerja. Vendar pa lahko program enostavno spremeni v strojno kodo tako, da besede v programu nadomesti s številkami, ki jih te pomenijo. Program, ki to počne, se imenuje assembler.
Programi, napisani v skupnem jeziku, so običajno sestavljeni iz navodil, ki so majhna opravila, ki jih računalnik izvede med izvajanjem programa. Imenujejo se navodila, ker jih programer uporablja, da računalniku naroči, kaj naj naredi. Del računalnika, ki sledi navodilom, je procesor.
Sestavni jezik računalnika je jezik nizke ravni, kar pomeni, da se lahko uporablja le za preprosta opravila, ki jih računalnik razume neposredno. Če želimo izvesti bolj zapletene naloge, moramo računalniku povedati vsako od preprostih nalog, ki so del zapletene naloge. Računalnik na primer ne razume, kako natisniti stavek na svoj zaslon. Namesto tega mu mora program, napisan v zbirniku, povedati, kako naj izvede vse majhne korake, ki so vključeni v tiskanje stavka.
Takšen program bi bil sestavljen iz številnih navodil, ki bi skupaj naredila nekaj, kar se človeku zdi zelo preprosto in osnovno. Zaradi tega ljudje težko berejo program v zbirniku. Nasprotno pa lahko programski jezik visoke ravni vsebuje eno samo navodilo, na primer PRINT "Hello, world!", ki računalniku naroči, naj namesto vas opravi vsa majhna opravila.
Razvoj zbirnega jezika
Ko so računalničarji prvič izdelali programirljive stroje, so jih programirali neposredno v strojni kodi, ki je niz številk, ki računalniku narekujejo, kaj naj naredi. Pisanje strojnega jezika je bilo zelo težavno in dolgotrajno, zato so sčasoma izdelali zbirni jezik. Skupinski jezik človek lažje bere in ga lahko hitreje napiše, vendar ga človek še vedno veliko težje uporablja kot programski jezik visoke ravni, ki poskuša posnemati človeški jezik.
Programiranje v strojni kodi
Za programiranje v strojni kodi mora programer vedeti, kako je vsako navodilo videti v binarni (ali šestnajstiški) obliki. Čeprav računalnik zlahka hitro ugotovi, kaj strojna koda pomeni, je to za programerja težko. Vsako navodilo ima lahko več oblik, ki so ljudem videti kot skupek številk. Vsaka napaka, ki jo nekdo naredi pri pisanju strojne kode, se opazi šele, ko računalnik naredi napačno stvar. Ugotoviti napako je težko, saj večina ljudi s pogledom na strojno kodo ne more ugotoviti, kaj pomeni. Primer, kako je videti strojna koda:
05 2A 00
Ta šestnajstiška strojna koda sporoča procesorju računalnika x86, da v akumulator doda 42. Človek jo zelo težko prebere in razume, tudi če pozna strojno kodo.
Uporaba zbirnega jezika namesto tega
V jeziku sestavljanja lahko vsako navodilo zapišemo kot kratko besedo, imenovano mnemotehnika, ki ji sledijo druge stvari, kot so številke ali druge kratke besede. Mnemotehnika se uporablja zato, da si programerju ni treba zapomniti natančnih številk v strojni kodi, ki so potrebne za to, da računalnik nekaj naredi. Primera mnemotehnik v zbirnem jeziku sta add, ki doda podatke, in mov, ki premakne podatke z enega mesta na drugo. Ker je beseda "mnemonik" redka, se namesto nje včasih uporablja besedna zveza vrsta navodila ali samo navodilo, pogosto napačno. Besede in številke za prvo besedo dajejo več informacij o tem, kaj je treba storiti. Na primer, stvari, ki sledijo besedi dodaj, lahko povedo, kateri dve stvari je treba sešteti, stvari, ki sledijo besedi mov, pa povedo, kaj je treba premakniti in kam to postaviti.
Strojno kodo iz prejšnjega poglavja (05 2A 00) lahko na primer zapišemo v zbirniku kot:
Skupinski jezik programerjem omogoča tudi lažje zapisovanje dejanskih podatkov, ki jih program uporablja. Večina asemblerskih jezikov ima podporo za enostavno izdelavo številk in besedila. V strojni kodi bi bilo treba vsako različno vrsto števila, na primer pozitivno, negativno ali decimalno, ročno pretvoriti v binarno, besedilo pa bi bilo treba opredeliti po eno črko naenkrat kot številke.
Skupinski jezik je tako imenovana abstrakcija strojne kode. Pri uporabi assemblerja programerjem ni treba poznati podrobnosti o tem, kaj številke pomenijo za računalnik, to namesto njih ugotovi assembler. Z zbirnim jezikom lahko programer dejansko še vedno uporablja vse funkcije procesorja, ki bi jih lahko s strojno kodo. V tem smislu ima zbirni jezik zelo dobro, redko lastnost: ima enako sposobnost izražanja stvari kot stvar, od katere abstrahira (strojna koda), hkrati pa je veliko lažji za uporabo. Zaradi tega se strojna koda skoraj nikoli ne uporablja kot programski jezik.
Demontaža in razhroščevanje
Ko so programi končani, so že pretvorjeni v strojno kodo, tako da jih lahko procesor dejansko izvede. Včasih pa bodo programerji, če je v programu napaka, želeli vedeti, kaj posamezni deli strojne kode počnejo. Disassemblerji so programi, ki programerjem pri tem pomagajo tako, da strojno kodo programa pretvorijo nazaj v zbirni jezik, ki ga je veliko lažje razumeti. Disassemblerji, ki strojno kodo spremenijo v zbirni jezik, so nasprotno od assemblerjev, ki zbirni jezik spremenijo v strojno kodo.
Računalniška organizacija
Da bi razumeli, kako so računalniki organizirani in kako delujejo na zelo nizki ravni, je treba razumeti, kako deluje program v asemblerju. Na najbolj poenostavljeni ravni imajo računalniki tri glavne dele:
- glavni pomnilnik ali RAM, ki hrani podatke in navodila,
- procesor, ki obdeluje podatke z izvajanjem navodil, in
- vhod in izhod (včasih skrajšano I/O), ki računalniku omogočata komunikacijo z zunanjim svetom in shranjevanje podatkov zunaj glavnega pomnilnika, da jih lahko pozneje dobi nazaj.
Glavni pomnilnik
V večini računalnikov je pomnilnik razdeljen na bajte. Vsak bajt vsebuje 8 bitov. Vsak bajt v pomnilniku ima tudi naslov, ki je številka, ki pove, kje v pomnilniku se bajt nahaja. Prvi bajt v pomnilniku ima naslov 0, naslednji ima naslov 1 in tako naprej. Če pomnilnik razdelimo na bajte, ga je mogoče nasloviti na bajte, saj ima vsak bajt edinstven naslov. Naslovov bajtnih pomnilnikov ni mogoče uporabiti za sklicevanje na posamezen bit bajta. Bajt je najmanjši del pomnilnika, ki ga je mogoče nasloviti.
Čeprav se naslov nanaša na določen bajt v pomnilniku, procesorji omogočajo uporabo več bajtov pomnilnika zaporedoma. Najpogostejša uporaba te funkcije je uporaba 2 ali 4 bajtov v vrsti za predstavitev števila, običajno celega števila. Včasih se za predstavitev celih števil uporabljajo tudi posamezni bajti, a ker so dolgi le 8 bitov, lahko vsebujejo le 2 8ali 256 različnih možnih vrednosti. Z uporabo 2 ali 4 bajtov v vrstici se število različnih možnih vrednosti poveča na 2 16, 65536 oziroma 2 32, 4294967296.
Ko program uporabi bajt ali več bajtov v vrsti za predstavitev nečesa, na primer črke, številke ali česa drugega, se ti bajti imenujejo objekt, ker so vsi del iste stvari. Čeprav so vsi objekti shranjeni v enakih bajtih pomnilnika, jih obravnavamo, kot da imajo "tip", ki pove, kako naj se bajti razumejo: kot celo število, znak ali kakšen drug tip (na primer neceloštevilska vrednost). Strojno kodo si lahko predstavljamo tudi kot tip, ki se interpretira kot navodila. Pojem tipa je zelo, zelo pomemben, saj določa, katere stvari se lahko in katere ne smejo početi z objektom in kako interpretirati bajte objekta. Na primer, negativnega števila ni dovoljeno shraniti v objekt s pozitivnim številom, prav tako ni dovoljeno shraniti ulomka v celo število.
Naslov, ki kaže na (je naslov) večbajtnega objekta, je naslov prvega bajta tega objekta - bajta, ki ima najnižji naslov. Ob tem je treba opozoriti, da po naslovu ne morete ugotoviti, kakšna je vrsta predmeta - ali celo njegova velikost. Pravzaprav niti s pogledom ne morete ugotoviti, kakšne vrste je objekt. Program v zbirnem jeziku mora spremljati, na katerih pomnilniških naslovih se nahajajo posamezni objekti in kako veliki so ti objekti. Program, ki to počne, je varen glede na tip, saj z objekti počne le stvari, ki so glede na njihov tip varne. Program, ki tega ne počne, verjetno ne bo deloval pravilno. Upoštevajte, da večina programov dejansko ne shranjuje izrecno, kakšen je tip objekta, temveč do objektov dostopajo dosledno - isti objekt se vedno obravnava kot isti tip.
Procesor
Procesor izvaja navodila, ki so kot strojna koda shranjena v glavnem pomnilniku. Večina procesorjev ima poleg dostopa do pomnilnika za shranjevanje tudi nekaj majhnih, hitrih prostorov fiksne velikosti za shranjevanje predmetov, s katerimi se trenutno dela. Ti prostori se imenujejo registri. Procesorji običajno izvajajo tri vrste ukazov, čeprav so lahko nekateri ukazi kombinacija teh vrst. Spodaj je nekaj primerov posameznih vrst v zbirnem jeziku x86.
Navodila za branje ali pisanje v pomnilnik
Naslednje navodilo jezika zbirke x86 prebere (naloži) dvobajtni objekt iz bajta na naslovu 4096 (0x1000 v šestnajstiškem sistemu) v 16-bitni register z imenom "ax":
V tem jeziku sestavljanja oglati oklepaji okrog številke (ali imena registra) pomenijo, da je treba številko uporabiti kot naslov za podatke, ki jih je treba uporabiti. Uporaba naslova za kazanje na podatke se imenuje indirekcija. V naslednjem primeru je brez oglatih oklepajev v drug register, bx, dejansko naložena vrednost 20.
Ker ni bila uporabljena nobena indirekcija, je bila v register vnesena dejanska vrednost.
Če so operandi (stvari, ki so za mnemotehniko) prikazani v obratnem vrstnem redu, se ukaz, ki nekaj naloži iz pomnilnika, namesto da bi to zapisal v pomnilnik:
V tem primeru dobi pomnilnik na naslovu 1000h vrednost ax. Če se ta primer izvede takoj za prejšnjim, bosta 2 bajta na naslovih 1000h in 1001h 2 bajta celega števila z vrednostjo 20.
Navodila, ki izvajajo matematične ali logične operacije.
Nekatera navodila izvajajo stvari, kot je odštevanje, ali logične operacije, kot je ne:
Primer strojne kode iz prejšnjega članka bi bil to v zbirnem jeziku:
V tem primeru se seštejeta 42 in ax, rezultat pa se shrani nazaj v ax. Tudi v zbirki x86 je mogoče združiti dostop do pomnilnika in matematično operacijo, kot je ta:
Ta ukaz doda vrednost dvobajtnega celega števila, shranjenega na 1000h, k ax in odgovor shrani v ax.
To navodilo izračuna ali vsebine registrov ax in bx ter rezultat shrani nazaj v ax.
Navodila, ki odločajo o tem, katero bo naslednje navodilo.
Navadno se navodila izvajajo v vrstnem redu, v katerem so prikazana v pomnilniku, kar je tudi vrstni red, v katerem so vnesena v kodo zbirnika. Procesor jih izvaja enega za drugim. Da pa lahko procesorji izvajajo zapletene stvari, morajo izvajati različna navodila glede na to, kakšni so podatki, ki so jih dobili. Sposobnost procesorjev, da izvajajo različna navodila glede na izid nečesa, se imenuje vejitev. Navodila, ki odločajo o tem, kakšno naj bo naslednje navodilo, se imenujejo navodila za vejitev.
Predpostavimo, da želi nekdo izračunati količino barve, ki jo bo potreboval za pobarvanje kvadrata z določeno dolžino stranice. Vendar mu zaradi ekonomije obsega trgovina z barvami ne bo prodala manjše količine barve, kot je potrebna za pobarvanje kvadrata 100 x 100.
Da bi ugotovili, koliko barve bodo potrebovali glede na dolžino kvadrata, ki ga želijo pobarvati, si zamislijo ta sklop korakov:
- od dolžine stranice odštejemo 100
- če je odgovor manjši od nič, nastavite dolžino stranice na 100
- pomnožite dolžino stranice s samim seboj
Ta algoritem lahko izrazimo z naslednjo kodo, kjer je ax dolžina stranice.
Ta primer uvaja več novih stvari, vendar sta prvi dve navodili znani. Kopirata vrednost ax v bx in nato od bx odštejeta 100.
Ena od novih stvari v tem primeru se imenuje nalepka, koncept, ki ga najdemo v jezikih sestavljanja na splošno. Etikete so lahko vse, kar želi programer (razen če gre za ime navodila, kar bi zmedlo asembler). V tem primeru je oznaka "continue". Assembler jo razume kot naslov navodila. V tem primeru je to naslov mult ax.
Novost je tudi koncept zastavic. V procesorjih x86 številna navodila v procesorju nastavijo "zastavice", ki jih lahko naslednje navodilo uporabi pri odločanju, kaj storiti. V tem primeru, če je bil bx manjši od 100, bo sub nastavil zastavico, ki pove, da je bil rezultat manjši od nič.
Naslednje navodilo je jge, kar je kratica za "Jump if Greater than or Equal to". To je ukaz za vejitev. Če je rezultat večji ali enak nič, procesor namesto na naslednji ukaz preskoči na ukaz z oznako continue, ki je mul ax.
Ta primer deluje dobro, vendar ga večina programerjev ne bi napisala. Ukaz za odštevanje pravilno nastavi zastavico, vendar spremeni tudi vrednost, na kateri deluje, zaradi česar je bilo treba ax prekopirati v bx. Večina jezikov za sestavljanje omogoča primerjalna navodila, ki ne spremenijo nobenega od posredovanih argumentov, vendar še vedno pravilno nastavijo zastavice, in sestavljanje x86 ni nobena izjema.
Namesto da bi odšteli 100 od ax, preverili, ali je to število manjše od nič, in ga pripisali nazaj ax, ostane ax nespremenjen. Zastave so še vedno nastavljene na enak način in skok se izvede v enakih primerih.
Vhod in izhod
Čeprav sta vhod in izhod temeljni del računalništva, ju v zbirnem jeziku ni mogoče izvesti na en sam način. To je zato, ker je način delovanja vhoda in izhoda odvisen od konfiguracije računalnika in operacijskega sistema, ki ga uporablja, in ne le od vrste procesorja, ki ga ima. V razdelku s primeri primer Hello World uporablja klice operacijskega sistema MS-DOS, primer za njim pa klice BIOS.
Vhod/izhod je mogoče izvajati v zbirnem jeziku. V zbirnem jeziku lahko na splošno izrazimo vse, kar je računalnik sposoben narediti. Vendar pa kljub temu, da v jeziku montaže obstajajo navodila za dodajanje in vejanje, ki vedno naredijo isto stvar, v jeziku montaže ni navodil, ki bi vedno izvajala I/O.
Pomembno je poudariti, da način delovanja vhodno-izhodnega sistema ni del nobenega jezika zbirke, saj ni del delovanja procesorja.
Skupinski jeziki in prenosljivost
Čeprav jezika montaže ne izvaja neposredno procesor, temveč strojna koda, ima z njim še vedno veliko opraviti. Vsaka družina procesorjev podpira različne funkcije, navodila, pravila za to, kaj lahko navodila počnejo, in pravila za to, katere kombinacije navodil so dovoljene. Zaradi tega različne vrste procesorjev še vedno potrebujejo različne zbirne jezike.
Ker je vsaka različica zbirnega jezika vezana na družino procesorjev, mu manjka nekaj, čemur pravimo prenosljivost. Nekaj, kar je prenosljivo ali prenosljivo, je mogoče zlahka prenesti z ene vrste računalnika na drugo. Medtem ko so druge vrste programskih jezikov prenosljive, pa jezika assemblerja na splošno ni.
Zbirni jezik in jeziki visoke ravni
Čeprav zbirni jezik omogoča enostavno uporabo vseh funkcij procesorja, se v sodobnih projektih programske opreme ne uporablja iz več razlogov:
- Za izražanje preprostega programa v skupnem jeziku je potrebnega veliko truda.
- Čeprav ni tako nagnjen k napakam kot strojna koda, montažni jezik še vedno ponuja zelo malo zaščite pred napakami. Skoraj vsi jeziki za sestavljanje ne uveljavljajo varnosti tipov.
- Zbirni jezik ne spodbuja dobrih programskih praks, kot je modularnost.
- Čeprav je vsako posamezno navodilo jezika montaže razumljivo, je težko ugotoviti, kakšen je bil namen programerja, ki ga je napisal. Pravzaprav je programski jezik zbirnika tako težko razumeti, da podjetja ne skrbijo za to, da bi ljudje razstavljali njihove programe (pridobivali programski jezik zbirnika).
Zaradi teh pomanjkljivosti se za večino projektov uporabljajo jeziki visoke ravni, kot so pascal, C in C++. V njih lahko programerji bolj neposredno izrazijo svoje zamisli, namesto da bi morali na vsakem koraku procesorju sporočati, kaj naj naredi. Imenujejo se visokonivojski, ker so ideje, ki jih lahko programer izrazi v enaki količini kode, bolj zapletene.
Programerji, ki pišejo kodo v sestavljenih jezikih visoke ravni, uporabljajo program, imenovan prevajalnik, ki njihovo kodo pretvori v zbirni jezik. Prevajalnike je veliko težje napisati kot sestavljalnike. Poleg tega jeziki visoke ravni programerjem ne omogočajo vedno uporabe vseh funkcij procesorja. To je zato, ker so jeziki visoke ravni zasnovani tako, da podpirajo vse družine procesorjev. V nasprotju z jeziki sestavljanja, ki podpirajo samo eno vrsto procesorja, so jeziki visoke ravni prenosljivi.
Čeprav so sestavljavci bolj zapleteni kot sestavljavci, so jih desetletja izdelovanja in raziskovanja sestavljavcev naredila zelo dobre. Zdaj za večino projektov ni več veliko razlogov za uporabo zbirnega jezika, saj lahko sestavljalniki običajno ugotovijo, kako izraziti programe v zbirnem jeziku, enako dobro ali bolje kot programerji.
Primeri programov
Program Hello World, napisan v skupščini x86:
Funkcija, ki natisne številko na zaslon z uporabo prekinitev BIOS-a, napisana v sestavku NASM x86. Modularno kodo je mogoče napisati v zbirki, vendar je za to potreben dodaten napor. Upoštevajte, da je vse, kar je v vrstici za podpičjem, komentar in ga sestavljalnik ne upošteva. Vnašanje komentarjev v kodo v asemblerju je zelo pomembno, ker je velike programe v asemblerju težko razumeti.
Vprašanja in odgovori
V: Kaj je zbirni jezik?
O: Jezik montaže je programski jezik, s katerim lahko računalniku neposredno povemo, kaj naj naredi. Je skoraj povsem podoben strojni kodi, ki jo razume računalnik, le da namesto številk uporablja besede.
V: Kako računalnik razume program v sestavljenem jeziku?
O: Računalnik ne more neposredno razumeti assemblerskega programa, lahko pa ga preprosto spremeni v strojno kodo, tako da besede v programu nadomesti s številkami, ki jih pomenijo. Ta postopek se izvede z uporabo asemblerja.
V: Kaj so navodila v asemblerskem jeziku?
O: Navodila v jeziku montaže so majhna opravila, ki jih računalnik izvaja med izvajanjem programa. Imenujejo se navodila, ker dajejo računalniku navodila, kaj naj naredi. Del računalnika, ki je odgovoren za izvajanje teh navodil, se imenuje procesor.
V: Katera vrsta programskega jezika je montažni jezik?
O: Montažni jezik je nizkonivojski programski jezik, kar pomeni, da se lahko uporablja le za izvajanje preprostih nalog, ki jih računalnik lahko razume neposredno. Za izvajanje kompleksnejših nalog je treba vsako nalogo razdeliti na posamezne komponente in za vsako komponento posebej zagotoviti navodila.
V: V čem se razlikuje od jezikov visoke ravni?
O: Jeziki visoke ravni imajo lahko posamezne ukaze, kot je PRINT "Hello, world!", ki računalniku sporočijo, naj samodejno izvede vsa ta majhna opravila, ne da bi jih bilo treba določiti posebej, kot bi bilo to treba storiti pri programu za sestavljanje. Zaradi tega je jezike visoke ravni lažje brati in razumeti kot sestavljene programe, ki so sestavljeni iz številnih posameznih ukazov.
V: Zakaj bi lahko bilo za ljudi težko prebrati program za sestavljanje?
O: Ker je treba za zapleteno nalogo, kot je tiskanje nečesa na zaslon ali izvajanje izračunov na podatkovnih nizih - stvari, ki se zdijo zelo osnovne in preproste, če so izražene v naravnem človeškem jeziku - določiti veliko posameznih navodil, zato je lahko eno navodilo sestavljeno iz več vrstic kode, zaradi česar ljudje, ki ne vedo, kako računalniki delujejo na tako nizki ravni, težko sledijo in razlagajo, kaj se v njih dogaja.