Masinakood (masinakeel) — määratlus, opkoodid ja binaarne struktuur
Masinakood (masinakeel): opkoodid, operandid ja binaarne struktuur — kuidas kõrgetasemelised keeled kompileeritakse masinkoodiks ja kuidas käskude opkoodid töötavad.
Masinakood on arvutiprogramm, mis on kirjutatud masinakeeles — otseselt protsessori täidetavates nullide ja ühtede vormis esinevates käskudes. Masinakood kasutab konkreetse arvutiarhitektuuri käsukirjakomplekti (instruction set). Tavaliselt esitatakse see binaarses kujus (bitijada), kuigi inimloetavaks muutmiseks kasutatakse sageli heksade või binaari tekstiesitust. Masinakood on tarkvara madalaim tase: kõik kõrgemtasemelised programmeerimiskeeled tõlgitakse või kompileeritakse lõpuks masinkoodiks, et protsessor saaks juhiseid täita.
Mis koosneb käsust?
Käsk (instruktsioon) ütleb protsessorile, millist toimingut sooritada. Üldjuhul koosneb käsk kahest osast:
- Opkood (operatsioonikood) — määrab tehtava operatsiooni (nt liitmine, liigutamine registritesse, tingimuslik hüppamine).
- Operand(id) — andmed või viited andmetele, millega opkood töötab. Operandid võivad olla registreid, mäluaadresse, konstante (otseandmeid) või mälli/registreid identifitseerivaid viiteid.
Käskude komplekt on loetelu opkoodidest, mis on antud arhitektuuris toetatud. Masinakood on see vorm, milleks koostekood ja muud programmeerimiskeeled lõpuks kompileeritakse või milleks neid tõlgitakse, et protsessor saaks programmi täita.
Instruktsiooniformaat ja binaarne struktuur
Masinkoodi binaarne struktuur varieerub arhitektuuri järgi, kuid tüüpilised kujundid sisaldavad:
- fikseeritud või muutuva pikkusega käsud (nt 32-bitised fikseeritud instruktsioonid RISC-arkitektuurides või erineva pikkusega CISC-instruktsioonid);
- bitiväljad opkoodi, lähte- ja sihtregistrite, adreseerimisrežiimide ning otseste konstantide jaoks;
- mälu- ja registriaadressimise bitikoodid ning tingimuslipud ja seisundiindikaatorid.
Näiteks lihtsas kujuteldavas 16-bitises instruktsioonis võivad esimesed 6 bitti olla opkood, järgnevad 5 bitti lähteregistri valimiseks ja viimased 5 bitti sihtregi või adreksi jaoks. Tegelikes protsessorites on sõnumformaatid ja bitipositsioonid dokumenteeritud arhitektuuri spetsifikatsioonis.
Erinevad adreseerimisviisid
- Registriaadressimine — operand on registris;
- Otsene mäluaadressimine — operand asub märgitud mäluaadressil;
- Indekseerimine ja nihkearvestus — mäluaadress arvutatakse registri ja nihke põhjal;
- Implicit (implitsiitne) — operand on ettenähtud registris või operaator kasutab fikseeritud andmeid;
- Immediataadressimine — operand on osa instruktsioonist (konstant).
Kuidas masinakood tekib ja milliseid tööriistu kasutatakse?
Masinakoodi loovad või genereerivad tavaliselt:
- Programmi koostajad (assmblerid) — tõlgivad assemblerkoodi (inimloetav mnemoonika) otse masinkoodiks;
- Kompleerijad ja lingijad — kompileerivad kõrgemas keeles kirjutatud lähtekoodi (nt C, C++) masinakeelseks objektkoodiks ning lingivad erinevad objektfailid ja teegid käivitatavaks failiks (nt ELF, PE);
- Tõlgid (interpreters) — mõnikord häälestavad käskude seeria reaalajas tõlke- või JIT-koodina masinakeelde.
Valminud masinkood salvestatakse sageli käivitatavatesse failidesse (nt Windowsi PE, Linuxi ELF), kuid protsessis kasutatav binaarne kujul mälus on sisuliselt sama.
Masinakood vs assembler ja kõrgemad keeled
- Assembler-kood on inimloetavam esitus masinakoodist, kus opkoodidel on mnemoonilised nimed (nt MOV, ADD) — assembleri abil saab lihtsasti masinkoodi genereerida.
- Kõrgema taseme keeled (nt Python, Java, C) annavad abstraktsiooni ja kirjutusmugavuse; need vajavad kompilaatorit või tõlki, et tekitada masinkoodi või virtuaalmasina bitijada.
- Mõnikord kutsutakse masinkoodi emakeelseks koodiks, eriti kui käsitletakse programmi, mis töötab ainult konkreetsel riistvaral või arhitektuuril.
Arhitektuurilised erinevused: RISC ja CISC
RISC (Reduced Instruction Set Computer) ja CISC (Complex Instruction Set Computer) erinevused mõjutavad masinkoodi ülesehitust:
- RISC: lihtsam ja tavaliselt fikseeritud pikkusega instruktsioonid, väike arv opkoode, millele järgnevad registrioperandid — tulemuseks eeldatavalt kiirem ja optimeeritavam masinkood;
- CISC: rohkem ja keerukamaid instruktsioone, muutuva pikkusega kood, mitmed adreseerimisviisid — tihti kompileeritakse kõrgema taseme keele konstruktsioonid otse keerukamateks instruktsioonide jadadeks.
Bitijada ja endianness
Binaarne esitlus sõltub ka arvuti endianness'ist — kuidas mitme baitilise väärtuse baitid mällu paigutatakse:
- Little-endian — madalaima suurusjärgu bait (least significant byte) salvestatakse madalamale aadressile;
- Big-endian — kõrgeima suurusjärgu bait salvestatakse madalamale aadressile.
Endianness ei muuda opsüntaksi, kuid mõjutab mälust lugemist/kirjutamist madalama taseme andmetöötluses ja binaarsete failide ülekandmises.
Turvalisus, disassembel ja analüüs
Masinakood võib sisaldada ka pahatahtlikku koodi; seetõttu kasutatakse tööriistu nagu disassemblerid (nt IDA Pro, Ghidra) ja dekompilaatorid, et analüüsida käivitatavat binaari. Disassembleerimine teisendab masinkoodi tagasi assemblerilaadseks mnemoonikaks, mis aitab aru saada programmi struktuurist ja käitumisest.
Lõpetuseks — miks masinakood oluline on
Masinakood on oluline, sest see on otseselt see, mida riistvara täidab. Mõistes masinakoodi struktuuri, opkoode ja adreseerimisviise, saavad arendajad optimeerida jõudlust, parandada turvalisust ja teha madala taseme tõrkeotsingut. Lisaks annab masinakood teadmise selle kohta, kuidas tarkvara ja riistvara omavahel suhtlevad ning milliste piirangutega tuleb arvestada süsteemitasandil.
Masinakoodi kirjutamine
Masinakoodi saab kirjutada erineval kujul:
- Kasutades mitmeid lüliteid. See tekitab jada 1 ja 0. Seda kasutati arvutite algusaegadel. Alates 1970. aastatest seda enam ei kasutata.
- Hexredaktori kasutamine. See võimaldab käsu numbri asemel kasutada opkoode.
- Assembleri kasutamine. Assembleri keeled on lihtsamad kui opkoodid. Nende süntaks on lihtsamini mõistetav kui masinakeel, kuid raskem kui kõrgtasemel keeled. Assembler tõlgib lähtekoodi ise masinkoodiks.
- Kõrgetasemelise programmeerimiskeele kasutamine võimaldab programme, mis kasutavad koodi, mida on lihtsam lugeda ja kirjutada. Need programmid tõlgitakse masinkoodiks. Tõlkimine võib toimuda mitmes etapis. Java-programmid optimeeritakse kõigepealt baatkoodiks. Seejärel tõlgitakse see kasutamisel masinakeelde.

Varase minicomputi esipaneel, millel on lülitid masinakoodi sisestamiseks
Tüüpilised masinakoodi juhised
Tavaliselt leidub käsukirjakomplektis palju erinevaid juhiseid:
- Aritmeetilised operatsioonid: Liitmine, lahutamine, korrutamine, jagamine.
- Loogilised operatsioonid: Konjunktsioon, disjunktsioon, eitus.
- Üksikute bittidega toimivad operatsioonid: Bitide nihutamine vasakule või paremale.
- Mäluga toimivad operatsioonid: väärtuse kopeerimine ühest registrist teise.
- Operatsioonid, mis võrdlevad kahte väärtust: suurem kui, väiksem kui, võrdne.
- Operatsioonid, mis kombineerivad teisi operatsioone: liitmine, võrdlemine ja kopeerimine, kui see on võrdne mingi väärtusega (ühe operatsioonina), hüpata programmi mingisse punkti, kui register on null.
- Operatsioonid, mis toimivad programmivoolus: hüpata mõnele aadressile.
- Operatsioonid, mis teisendavad andmetüüpe: nt 32-bitise täisarvu teisendamine 64-bitiseks täisarvuks, ujukomaarvu teisendamine täisarvuks (kärpimise teel).
Paljud kaasaegsed protsessorid kasutavad mõnede käskude jaoks mikrokoodi. Keerulisemad käsud kipuvad seda kasutama. Seda tehakse sageli CISC-arhitektuuride puhul.
Juhised
Igal protsessoril või protsessoriperekonnal on oma käsukomplekt. Juhised on bittide mustrid, mis vastavad erinevatele käskudele, mida saab masinale anda. Seega on käsukomplekt spetsiifiline protsessoriklassile, mis kasutab (enamasti) sama arhitektuuri.
Uuemad protsessorikonstruktsioonid sisaldavad sageli kõiki eelkäija käske ja võivad lisada täiendavaid käske. Mõnikord kaotab uuem disain või muudab käsu koodi tähendust (tavaliselt seetõttu, et seda on vaja uutel eesmärkidel), mis mõjutab koodi ühilduvust; isegi peaaegu täielikult ühilduvad protsessorid võivad mõne käsu puhul veidi erinevalt käituda, kuid see on harva probleemiks.
Süsteemid võivad erineda ka muude üksikasjade, näiteks mälu paigutuse, operatsioonisüsteemide või välisseadmete poolest. Kuna programm sõltub tavaliselt sellistest teguritest, ei tööta eri süsteemid tavaliselt sama masinkoodi, isegi kui kasutatakse sama tüüpi protsessorit.
Enamikul käskudel on üks või mitu opkoodivälja. Need määravad käsu põhitüübi. Teised väljad võivad anda operandide tüübi, adresseerimisviisi jne. Samuti võib olla erilisi juhiseid, mis sisalduvad opkoodis endas. Neid juhiseid nimetatakse vahetuteks.
Protsessori disain võib olla ka muul viisil erinev. Erinevad juhised võivad olla erineva pikkusega. Samuti võib neil olla sama pikkus. Kui kõik juhised on sama pikkusega, võib see lihtsustada disaini.
Näide
MIPS-arhitektuuril on 32-bitised käsud. Selles jaotises on koodinäited. Käskude üldine tüüp on op (operatsioon) väljal. See on kõrgeim 6 bitti. J-tüüpi (hüpe) ja I-tüüpi (vahetu) juhised on täielikult antud op. R-tüüpi (register) käsud sisaldavad väljal funct. See määrab koodi täpse operatsiooni. Nendes tüüpides kasutatavad väljad on järgmised:
6 5 5 5 5 5 5 6 bitti [ op | rs | rt | rd |shamt| funct] R-tüüp [ op | rs | rt | aadress/immediate] I-tüüp [ op | sihtaadressi ] J-tüüprs, rt ja rd tähistavad registrioperandi. shamt annab nihkesumma. Aadressi või vahetu väljad sisaldavad otse operandi.
Näide: lisada registrid 1 ja 2. Tulemus paigutatakse registrisse 6. See on kodeeritud:
[ op | rs | rt | rd |shamt| funct] 0 1 2 6 0 0 32 detsimaalselt 000000 00001 00010 00110 00000 100000 binaarseltLaadige väärtus registrisse 8. Võtke see mälurakust 68 rakku pärast registris 3 loetletud asukohta:
[ op | rs | rt | address/immediate] 35 3 8 68 detsimaalselt 100011 00011 01000 00000 00001 000100 binaarseltHüppa aadressile 1024:
[ op | sihtaadressi ] 2 1024 kümnendsüsteemi 000010 00000 00000 00000 00000 10000 000000 binaarsüsteemiSeotud leheküljed
- Binaarne numbrite süsteem
- Kvantarvutid
- Käskude komplekt
- Vähendatud käsureaga arvuti
Küsimused ja vastused
K: Mis on masinakood?
V: Masinakood on arvutiprogramm, mis on kirjutatud masinakeeles, kasutades konkreetse arvutiarhitektuuri käsukirjakomplekti, ja on tavaliselt kirjutatud binaarselt.
K: Mis on tarkvara madalaim tase?
V: Masinakood on tarkvara madalaim tase.
K: Kuidas täidavad arvutid teisi programmeerimiskeeli?
V: Muud programmeerimiskeeled tõlgitakse masinkoodiks, mida arvuti saab täita.
K: Millest koosneb käsk masinkoodis?
V: Käsk masinakoodis koosneb opkoodist (operatsioonikoodist) ja operand(idest). Operandid on tavaliselt mäluaadressid või andmed.
K: Mis on käskude kogum?
V: Käskude komplekt on arvutile kättesaadavate opkoodide loetelu.
K: Mida teevad programmi koostajad koodiga?
V: Programmi koostajad muudavad koodi teiseks keeleks või masinkoodiks.
K: Mis on masinkoodi teine nimetus?
V: Masinakoodi nimetatakse mõnikord emakeelseks koodiks, mida kasutatakse, kui räägitakse asjadest, mis töötavad ainult mõnes arvutis.
Otsige