Utasításkészlet

Az utasításkészlet, vagy utasításkészlet-architektúra (Instruction Set Architecture, ISA) a számítógép-architektúra programozáshoz kötődő része, ami magába foglalja a bitszélességet, a használható natív adattípusokat, gépi kódú utasításokat, regisztereket, címzési módokat, memóriaarchitektúrát, megszakítás- és kivételkezelést és a külső I/O-t.  Az ISA specifikációjához tartoznak az opkódok (a gépi kód) és az adott processzor natív parancsai.

Az utasításkészlet-architektúra különbözik a mikroarchitektúrától, ami az utasításkészletet megvalósító processzortervezési technikák összessége. Nagyon különböző mikroarchitektúrájú számítógépeknek is megegyezhet az utasításkészletük. Például az Intel Pentium és az AMD Athlon az x86 utasításkészlet csaknem azonos változatát valósítja meg, de radikálisan különböző belső felépítéssel.

Egyes virtuális gépek utasításkészlete támogatja a Smalltalk, Java virtuális gép vagy a Microsoft Common Language Runtime-jának a bájtkódját oly módon, hogy a gyakrabban végrehajtott kódrészleteket natív gépi kódra fordítják, a kevésbé gyakoriakat pedig interpretálva futtatják (lásd Just-in-time compilation). A Transmeta hasonlóan valósította meg az x86 utasításkészlet futtatását VLIW processzorokon.

Utasításkészletek osztályozása

Az utasításkészletek osztályozhatók:
 típus szerint (CISC, RISC, VLIW, EPIC)
 bitszélesség szerint (jellemzően 8 bit, 16 bit, 32 bit, 64 bit)
 regiszterek típusa és száma szerint
 címzési módok szerint
 operandusok száma szerint
 aszerint, hogy egy vagy több utasítás egy vagy több adaton végez-e műveletet (Flynn-féle osztályozás)
 egyéb szempontok lehetnek: van-e verem, megszakításkezelés, kivételkezelés, támogatott-e a hardveres virtualizáció stb.

A komplex utasításkészlettel rendelkező számítógépek (CISC) sok olyan, specializált utasítással bírnak, melyeket a programok csak ritkán használnak. A csökkentett utasításkészletű számítógépeket (RISC) úgy egyszerűsítették le, hogy csak azokat az utasításokat valósították meg bennük, melyek gyakran előfordulnak a programokban; a ritkább műveleteket szubrutinokkal oldják meg, a ritkán használt utasítások nagyobb futási idejét pedig ellensúlyozza az egyszerűbb felépítés. Elméleti fontosságú még a minimális utasításkészletű számítógép (MISC) és az egy utasításos számítógép; ilyenek kereskedelmi forgalomba nem kerültek. További variációk a nagyon hosszú utasításszavú (VLIW) architektúrák, ahol a processzor számos utasítása van egyetlen hosszú utasításszóba belekódolva, és a VLIW utódjának tekintett utasításkészlet nélküli számítástechnika (NISC).

Gépi kód
A gépi kód vagy gépi nyelv diszkrét állításokból vagy utasításokból épül fel. Egy-egy gépi nyelvű utasítás a feldolgozó architektúrán belül az elvégzendő feladaton túl kijelölhet:
 meghatározott regisztereket aritmetika, címzési vagy vezérlési funkciókhoz
 meghatározott memóriacímeket vagy offsetet (eltolást)
 az operandusok értelmezését meghatározó címzési módokat.

A bonyolultabb műveletek ezeknek az egyszerűbb elemek kombinációjaként építhetők fel. A gépi kódú utasítások egymás után hajtódnak végre, vagy ahogy a  programlefolyás-vezérlő utasítások meghatározzák.

Utasításfajták
Számos utasításkészletben megtalálhatók a következő alapvető műveletek:

 Adatkezelő és memóriakezelő műveletek
 értékadás (set): egy regiszter feltöltése egy konstans értékkel
 mozgatás (move): adatmozgatás memóriacímről egy regiszterbe, vagy fordítva. Célja egy regiszter tartalmának vagy egy művelet eredményének eltárolása a memóriában, vagy a tárolt adat betöltése későbbi műveletvégzés céljából.
 írás és olvasás (I/O, read-write) hardvereszközökről; az adatoknak a gépbe történő bevitelét, kiírását, illetve a külső perifériák adatforgalmának vezérlését végzik
 Az aritmetikai-logikai egységet (ALU) vezérlő utasítások
 két regiszter értékének összeadása, kivonása, szorzása vagy elosztása (add, subtract, multiply, divide), az eredmény egy regiszterbe kerül, a státusregiszter egy vagy több bitjének (flagjének) értékét potenciálisan megváltoztatva
 bitműveletek, két regiszter megfelelő bitjei között végzett logikai és, logikai vagy, vagy egy regiszter bitjein végzett negáció.
 két regiszter értékének összehasonlítása (compare); pl. annak vizsgálata, hogy az egyik értéke kisebb-e a másikénál, netán egyenlőek
 programlefolyás-vezérlő (vezérlésátadó) utasítások, melyekkel elérhető, hogy az utasítások végrehajtási sorrendje eltérjen azok tárolási sorrendjétől
 feltétel nélküli elágazás a program egy más címére és az ottani utasítások elvégzése
 feltételes elágazás egy másik memóriacímre, ha egy feltétel teljesül
 számított ugrás vagy indirekt elágazás egy másik címre, a következő utasítás címének (a visszatérési címnek) elmentésével (szubrutinhívás)
 A feldolgozó egység állapotát befolyásoló utasítások a processzor speciális regisztereit módosítják, oly módon, hogy a folyamat további értelmezésére hatnak, például újraindítják vagy elaltatják a processzort, védett módba kapcsolják a feldolgozást stb.
 Egyéb utasítások, például várakozás vagy NOP (No OPeration), azaz üres utasítás.

Komplex utasítások
Egyes platformok utasításkészlete „komplex” utasításokat is tartalmaz. Egyetlen komplex utasítás elvégzi, ami más számítógépeken sok normál utasítás feladata lenne. Az ilyen utasítások jellemzői, hogy több lépésben hajtódnak végre, számos végrehajtó egységet foglalkoztatnak vagy más módon nagyobb léptékűek az ugyanazon a processzoron futó egyszerű utasításoknál. Néhány példa komplex utasításokra:

 egyszerre több regiszter mentése a verembe
 nagy memóriablokkok mozgatása
 bonyolult és/vagy lebegőpontos aritmetikai műveletek (szögfüggvények, négyzetgyökvonás stb.)
 atomi test-and-set utasítás (memóriába írás és a korábbi érték visszaadása egy műveletben)
 utasítások, amik az ALU értékét egy memóriából vett operandussal kombinálják (regiszterérték helyett)

Mostanában népszerű komplex utasításfajták a SIMD-, azaz Single-Instruction Stream Multiple-Data Stream (egy utasításfolyam, több adatfolyam), illetve vektorműveletek, melyek ugyanazt az aritmetikai műveletet végzik el egy időben nagy mennyiségű adaton. A SIMD segítségével lehetséges nagy vektorok vagy mátrixok manipulációja rövid idő alatt. A SIMD utasítások a hang-, kép- és videofeldolgozásban alkalmazott algoritmusok könnyű párhuzamosítását teszik lehetővé. Számos SIMD-implementáció jelent meg a piacon olyan kódneveken, mint  MMX, 3DNow! vagy AltiVec.

Speciális célú processzorok, pl. GPU-k is tartalmaznak komplex utasításkészleteket. Mindazonáltal az ilyen egyedi processzorokhoz tartozó komplex utasításkészletek (és a natív assembly nyelv) gyakran nem hozzáférhetők publikusan a gyártói hardver zártságából fakadóan, így a szoftverfejlesztők szabványosított magasabb szintű nyelveken és API-kon keresztül programozhatják csak őket. Az OpenGL virtuális utasításkészlet és virtuális asszembly nyelve (ARB assembly language) és a CUDA példázzák az ilyen, natív utasításkészlet fölött létrehozott hardverabsztrakciós rétegeket.

Egy utasítás részei

A hagyományos architektúrák utasításai a következőkből épülnek föl: az elvégzendő utasítást meghatározó opkódból (pl. „add a memóriacím tartalmát egy regiszterhez”) és nulla vagy több operandusból (regiszterek, memóriacímek vagy konstans értékek). Az operandusoknak lehetnek az értelmezésüket meghatározó különböző címzési módjaik vagy fix mezőkön helyezkednek el.

A nagyon hosszú utasításhosszú (VLIW) architektúrákon, amik közé több mikrokód-architektúra tartozik, egyetlen utasítás több egyidejűleg végrehajtandó opkódot és azok operandusát tartalmazhatja.

Egyes egzotikus utasításkészletekben az opkód mező nem létezik (ilyen a Transport Triggered Architecture – TTA – és a  Forth virtuális gép), csak operandus(oka)t.
Más szokatlan, zéróoperandusú utasításkészletek nem tartalmaznak operandusmezőket, ilyen néhány veremgép, köztük a NOSC .

Utasításhossz
Az utasítások hossza vagy mérete az architektúrák között széles tartományban változhat: egyes mikrokontrollerek négy bitjétől a VLIW rendszerek több száz bitjéig. A személyi számítógépek, mainframe-ek és szuperszámítógépek tipikus CPU-nak utasításhossza 8–64 bit között van. Az x86 architektúrában a leghosszabb lehetséges utasítás 15 bájtos (120 bites). Egy utasításkészleten belül az egyes utasítások különböző hosszúak is lehetnek. Bizonyos architektúrákon, jellemzően a legtöbb RISC gépen, az utasítások fix hosszúak, általában az adott architektúra szóhosszától függően. Más architektúrákon az utasítások különböző hosszúak lehetnek, tipikusan bájt vagy félszó többszörösei.

Megjelenítés
Egy programot alkotó utasításokat ritkán adnak meg  eredeti, numerikus formájukban. A programozók általában assembly nyelven adják meg őket, vagy még gyakrabban, fordítóprogram állítja elő őket.

Tervezés
Az utasításkészletek tervezése komplex feladat. A mikroprocesszor fejlődésének két fontos állomása kapcsolódik ehhez. Az első a CISC (Complex Instruction Set Computer, komplex utasításkészletű számítógép), ami számos különböző utasítással rendelkezett. Az 1970-es években a mérnökök arra jutottak, hogy számos, ritkán használt utasítás egyszerűen elhagyható lenne. Az eredmény a RISC (Reduced Instruction Set Computer, csökkentett utasításkészletű számítógép) lett, egy kevesebb utasítást használó architektúra. Az egyszerűbb utasításkészlet előnye a magasabb elérhető órajel, csökkent processzorméret és energiafogyasztás. A komplexebb utasításkészlet ugyanakkor segítheti egyes utasítások optimalizálását, javíthatja a gyorsítótár hatékonyságát, egyszerűsítheti a programozást.

Egyes utasításkészletek fejlesztői fenntartottak egy vagy több opkódot egyfajta rendszerhívás vagy szoftvermegszakítás számára. Például a MOS Technology 6502 erre a 00H kódot, a Zilog Z80 a C7,CF,D7,DF,E7,EF,F7,FFH nyolcbájtos kódot, míg a Motorola 68000 az A000..AFFFH közötti kódokat használja erre a célra.

A gyors virtuális gépek megvalósítását nagyban támogatja, ha egy utasításkészlet megfelel a Popek- és Goldberg-féle virtualizációs követelményeknek.

A zavartűrő programozásban (Immunity Aware Programming) is használt NOP-csúszdák megvalósítása egyszerűbb, ha a memória definiálatlan, „nem programozott” állapotát NOP utasításnak értelmezi a processzor.

Többprocesszoros rendszereken könnyebb megvalósítani  nem blokkoló szinkronizációs algoritmusokat, ha az utasításkészlet támogat olyan jellegű kombinált utasításokat, mint a fetch-and-add, a load-link/store-conditional (LL/SC) vagy az atomi compare and swap.

Utasításkészlet-megvalósítások
Egy adott utasításkészlet hardveres (mikroarchitekturális) megvalósítása sokféle formát ölthet. Ezek egymással binárisan kompatibilisek, de különböző kompromisszumokat képviselhetnek költség, teljesítmény, energiafelhasználás, méret stb. tekintetében.

Egy processzor mikroarchitektúrájának tervezésekor a mérnökök olyan (gyakran külön megtervezett) bedrótozott elektronikus áramköri blokkokat használnak fel, mint az összeadó, szorzó, számláló áramkörök, regiszterek, ALU-k stb. Valamiféle, a regiszterek közti adatmozgást leíró nyelv (register transfer language) segítségével modellezik az ISA utasításainak dekódolását és kibocsátását. Két fő módja van az utasításkészletet megvalósító vezérlőegység (Control Unit) implementálásának (bár a kettő között számos középút létezik):
 A korai számítógépek és néhány egyszerűbb RISC számítógép „bedrótozta” a teljes utasításdekódolást és -kibocsátást (és a mikroarchitektúra többi részét is).
 Más megoldásokban mikrokódrutinok és/vagy táblák valósítják ezt meg – tipikusan ROM és/vagy PLA csipeken (bár korábban különálló RAM-okat is használtak erre a célra).

Néhány új CPU-design is a CPU-n belüli írható RAM-ba vagy flash memóriába fordítja le az utasításkészletet (ilyen a Rekursiv processzor és az Imsys Cjipje), mások egy FPGA-ra (újrakonfigurálható számítás). Egy korábbi példa a  Western Digital MCP-1600, ami különálló ROM modullal rendelkezik a mikrokód tárolására.

Egy utasításkészlet-architektúra szoftveresen is emulálható értelmező program segítségével. Természetesen az interpretáció költsége miatt ez lassabb, mint közvetlenül az emulált hardver-eredetin futtatni a programokat – kivéve, ha az emulátor nagyságrenddel gyorsabb az eredeti platfomnál. Manapság általánosan elterjedt gyakorlat, hogy a gyártók az új ISA-kat vagy mikroarchitektúrákat először szoftveres emuláció útján tesztelik és teszik a szoftverfejlesztők számára elérhetővé, még a hardveres implementáció elkészülte előtt.

Gyakran az implementáció lehetőségei visszahatnak az utasításkészletben alkalmazott utasításokra. Például az utasítás-futószalag sok megvalósításában utasításonként csak egyetlen memóriából való betöltés (load) vagy memóriába mentés (store) lehetséges, ami load-store architektúrához (RISC) vezet.

Egy másik példában az utasítás-futószalag megvalósításának korai módozatai az elágazási késleltetési réshez (delay slot) vezettek.

A nagysebességű digitális jelfeldolgozás követelményei ellentétes irányú hatást fejtenek ki – az utasítások egy bizonyos módon való megvalósításának irányába hatnak. Például, a különböző digitális szűrők kellően gyors lefuttatásához egy tipikus DSP MAC utasítását olyan Harvard-architektúrának kell megvalósítania, amely képes egy utasítást és két adatszót egy időben betölteni és rendelkezik egy órajelciklus alatt lefutó szorzás-összeadás (multiply–accumulate) műveletre képes bináris szorzó egységgel.

Kódsűrűség
A számítástechnika hőskorában a memória nagyon drága volt, ezért a programkódnak lehetőleg kompaktnak kellett lennie, hogy elférjen a korlátozott memóriában. Egy adott feladat végrehajtására szolgáló programkód teljes hosszúsága, a „kódsűrűség” is fontos jellemzője volt bármely utasításkészletnek. A magas kódsűrűségű számítógépek gyakran komplex utasításokkal rendelkeztek parametrizált visszatérésekre, ciklusok szervezésére stb. (ezért nevezték el őket utólagosan komplex utasításkészletű, azaz CISC számítógépeknek). Azonban a tipikusabb, illetőleg gyakrabban előforduló CISC utasítások csak összekombinálnak egy egyszerű ALU utasítást (mint pl. „ADD”) egy vagy több memóriában lévő operandus elérésével (különböző címzési módok, mint direkt, indirekt, indexelt stb.) segítségével. Egyes architektúrák megengedik, hogy (az eredményt beleértve) két vagy három operandus is memóriában legyen, vagy olyan funkciók elvégzésére képesek, mint pl. az automatikus mutató-inkrementálás. A szoftveresen megvalósított utasításkészletekben még komplexebb és hathatósabb utasítások találhatók.

A csökkentett utasításkészletű számítógépek (RISC) széles körű elterjedése a gyorsan növekvő méretű memóriaalrendszerek időszakára esett. A RISC-ek feláldozták a kódsűrűséget a hardveres megvalósítás egyszerűsítése érdekében, így a teljesítményt az órajel-frekvencia és a regiszterek számának megnövelésével próbálták elérni. A RISC utasítások tipikusan egyetlen műveletet hajtanak végre, például regiszterek összeadását („ADD”) vagy egy memóriacím tartalmának regiszterbe betöltését („LOAD”); általában fix utasításhosszal dolgoznak, míg egy tipikus CISC utasításkészletben számos olyan utasítás van, ami rövidebb ennél a fix hosszúságnál. A fix hosszúságú utasítások kezelése több okból is egyszerűbb a változó hosszúságú utasításokhoz képest (ennek egyik oka, hogy nem kell külön odafigyelni az utasítások gyorsítótár-vonalon vagy virtuálismemória-laphatáron való átnyúlására), így valamivel könnyebb  a sebességre való optimalizálás. Mivel azonban a RISC számítógépek adott feladathoz általában több és nagyobb méretű utasítást (így nagyobb méretű kódot) igényelnek, ezért kevésbé optimálisan használják ki a rendelkezésre álló busz-sávszélességet és gyorsítótár-memóriákat.

A minimális utasításkészletű számítógépek (MISC) olyan veremgépek, ahol csak néhány (16-64) különböző utasítás létezik, így több utasítás kódja elfér egyetlen gépi szóban. Az ilyen architektúrák vezérlői egyszerűen, kevés kapuval megvalósíthatók, így könnyen előállíthatók FPGA vagy többmagos formában. A kódsűrűség a RISC-ével összemérhető, a megnövelt utasítássűrűséget ellensúlyozza, hogy a primitívebb utasításokból több darabra van szükség a feladat megoldásához.

A kódsűrűség javításának egyik lehetséges eszköze a futtatható állományok tömörítése (EXE-tömörítés). Az ezzel kapcsolatos kihívásokkal és korlátokkal matematikailag a Kolmogorov-bonyolultság területe foglalkozik.

Az operandusok száma
Az utasításkészletek csoportosíthatók az utasításokban explicit módon meghatározott operandusok maximális száma szerint.

(A következő példákban a, b és c (közvetlen vagy számított) memóriacímek, míg reg1 és a továbbiak regisztereket jelentenek.)

 0-operandusú (zérócímű gépek), az úgynevezett veremgépek: minden aritmetikai művelet a verem felső egy vagy két címének felhasználásával megy végbe: push a, push b, add, pop c. A veremgépeknél a „0-operandusú” vagy „zérócímű” kifejezések az aritmetikai utasításokra vonatkoznak, de valamennyi utasításra nem, hiszen 1-operandusú push és pop utasításokkal érik el a memóriát.
 1-operandusú (egycímű gépek), az úgynevezett akkumulátorgépek közé tartoznak a hőskor számítógépei mellett sok kis mikrokontroller is: a legtöbb utasítás egyetlen jobb operandust határoz meg (egy konstanst, regisztert vagy memóriacímet), az implicit bal operandus pedig az akkumulátor (és a célcím, ha az utasításnak van ilyenje): load a, add b, store c. Idetartozó kategória még a praktikus veremgépeké, melyek az aritmetikai utasításoknál gyakran megengednek egyetlen explicit operandust: push a, add b, pop c.
 2-operandusú – számos CISC és RISC gép tartozik ehhez a kategóriához:
 CISC – gyakori a load a,reg1; add reg1,b; store reg1,c megoldás olyan gépeken, melyek utasításonként egy memóriaoperandusra vannak korlátozva
 CISC – move a->c; add c+=b.
 RISC – explicit memóriakezelésre van szükség, így az utasítsok: load a,reg1; load b,reg2; add reg1,reg2; store reg2,c
 3-operandusú, az adatok jobb újrahasznosítását lehetővé tevő:
 CISC – vagy egyetlen utasítás lesz: add a,b,c, vagy jellemzőbben: move a,reg1; add reg1,b,c, hiszen a legtöbb gép legfeljebb két memóriaoperandust enged meg.
 RISC – az aritmetikai műveletek csak regisztereket használhatnak, ezért explicit, kétoperandusú load/store műveletekre van szükség: load a,reg1; load b,reg2; add reg1+reg2->reg3; store reg3,c; a 2-operandusútól és az 1-operandusútól eltérően ez mind a három felhasznált értéket meghagyja az a, b és c regiszterekben.
 több operandusú – egyes CISC gépek háromnál több operandusú (regiszter vagy memória) címzési módokat is ismernek, mint a VAX „POLY” polinomkiértékelő utasítása.

Mivel egy háromoperandusú utasítás három regiszterének kódolásához sok bitre van szükség, a 16 bites RISC processzorok kivétel nélkül kétoperandusú gépek, ilyenek pl. az Atmel AVR, a TI MSP430 és az ARM Thumb egyes verziói.
A 32 bites RISC processzorok általában háromoperandusú gépek, ilyenek a Power Architektúra, a SPARC architektúra, a MIPS, az ARM architektúra és az AVR32 architektúra processzorai. Az utasítások explicit módon meghatároznak egy vagy több operandust (regisztereket, memóriacímeket vagy konstans értékeket). Ezen túl egyes utasítások egy operandust vagy mindkettőt implicit módon tartalmazzák, implicit regiszterként, vagy pl. az által, hogy a verem tetejéhez nyúlnak hozzá. Ha az operandusok egy része implicit módon meghatározott, az utasítás operandusainak száma kisebb lehet a művelet matematikai értelemben vett aritásánál (változószám). Ha a céloperandus explicit módon meghatározza az eredmény címét, az utasítás operandusainak száma nagyobb lehet a művelet aritásánál. 
Az operandusok tehát vagy az utasítások opkódjában, vagy az utasítást követő értékek vagy címek formájában vannak meghatározva.
Egyes utasításkészletekben különböző utasításokhoz különböző számú operandus tartozhat.

Kapcsolódó szócikkek
 Számítógép-architektúra
 Emulátor
 Register transfer language (RTL)
 Utasításkészletek listája

ISA-kategóriák

 EPIC: Explicitly Parallel Instruction Computing
 Vektorprocesszor
 SIMD: Single Instruction Multiple Data
 Flynn-féle osztályozás
 Ortogonális utasításkészlet

Jegyzetek

Fordítás

További információk
 Mark Smotherman's Historical Computer Designs Page
 Microprocessor Instruction Set Cards
 A Set of Standard Microprocessor Programming Cards by Jonathan Bowen
 Randy Hyde's discussion on ISA

Mikroprocesszorok
Utasításkészlet-architektúrák