Obsah
1. Činnost mikroprocesoru bez záhad, aneb jde to i bez trpaslíků
2. Hodinový signál
3. Inicializace mikroprocesoru pomocí signálu Reset
4. Načítání instrukcí z operační paměti a role registru PC
5. Operační kódy instrukcí (opkódy), strojový kód a assembler
6. Podrobnější popis instrukčního souboru
7. Instrukce pro přesuny dat mezi registry
8. Obsah následující části tohoto seriálu
1. Činnost mikroprocesoru bez záhad, aneb jde to i bez trpaslíků
V předchozí části tohoto seriálu jsme si ukázali schéma velmi jednoduchého „učebního“ mikroprocesoru, na kterém si nyní budeme vysvětlovat jednotlivé funkce, které mikroprocesory při své práci provádí. Také jsme si vypsali takzvanou instrukční sadu tohoto mikroprocesoru, tj. operační kódy (opkódy) instrukcí spolu s jejich mnemotechnickými zkratkami, které náš jednoduchý procesor dokáže zpracovat. Dnes již začneme s podrobnějším popisem jednotlivých instrukcí, včetně vysvětlení způsobu provádění těchto instrukcí uvnitř mikroprocesoru, za využití všech interních bloků, které se v něm nachází, tj. aritmeticko-logické jednotky, mikroprogramového řadiče, pracovních registrů, pomocných registrů a interní sběrnice. Průběh jednotlivých kroků bude doplněn animacemi; ovšem možná budete zklamáni, protože se v nich žádní trpaslíci, kterými se tak často zaklínají někteří uživatelé (a také pár učitelů), nebudou vyskytovat. Učební mikroprocesor má následující architekturu:
Interní schéma „učebního“ mikroprocesoru
2. Hodinový signál
Každá instrukce, kterou mikroprocesor provádí, je interně rozložena do jedné nebo několika jednodušších akcí, například do přenosu dat mezi pracovním registrem a pomocným registrem, vykonáním aritmetické či logické operace v ALU, zvýšení hodnoty registru PC o jedničku či zápisu dat do operační paměti. Jednotlivé akce musí být vzájemně synchronizovány, k čemuž slouží takzvané hodinové signály přiváděné do mikroprocesoru pomocí vstupu nazývaného celkem příhodně Clock, popř. CLK či φ. Samotné generování hodinového signálu s obdélníkovým průběhem (jedná se tedy o binární signál) je prováděno oscilátorem postaveným například na astabilním či monostabilním klopném obvodu, Schmittově klopném obvodu doplněném krystalem, popř. také na oscilátorech harmonických signálů doplněných Schmittovým klopným obvodem, který z původně sinusových průběhů vytvoří žádoucí obdélníkový signál. Nejjednodušším generátorem je Schmittův klopný obvod doplněný o zpětnovazební odpor a kondenzátor zapojený mezi vstupní signál a zem. Hodnota R a C potom určuje periodu:
Velmi jednoduchý oscilátor založený na Schmittově klopném obvodu
Některé mikroprocesory a zejména mikrořadiče mají oscilátor zabudovaný přímo na čipu a většinou pro svoji činnost vyžadují pouze připojení externího krystalu pro dosažení přesné taktovací frekvence – krystal je totiž poměrně velká a přitom cizorodá součástka (navíc s mechanicky namáhanými částmi), která se přímo do mikroprocesoru, tj. na vlastní polovodičový čip, špatně integruje. Starší mikroprocesory vyžadovaly více hodinových signálů označovaných například φ1 a φ2, které byly interně použity v řadiči pro generování příkazů pomocí klopných obvodů typu JK.
Na tomto místě je vhodné podotknout, že samotná frekvence hodinového signálu vcelku nic nevypovídá o výkonnosti mikroprocesoru, tj. může se stát, že mikroprocesor s frekvencí hodinového signálu 200 MHz bude výkonnější než mikroprocesor s frekvencí 500 MHz. V úvahu je nutné brát jak šířku zpracovaných dat, tak i průměrný počet strojových instrukcí zpracovaných v jednom taktu (popř. naopak počet taktů nutných pro zpracování jedné instrukce), dostupnost některých operací, samotnou architekturu použitého mikroprocesoru (vektorové zpracování instrukcí, VLIW, SIMD) atd.
Stále poměrně jednoduchý (a nepřesný) oscilátor vytvořený z logických hradel
Navíc se ukazuje, že sázka některých firem na uvádění výkonu svých mikroprocesorů pomocí hodnoty frekvence hodinového signálu (a nikoli na jiných jednotkách, například syntetizovaných benchmarcích) se často obrací proti svým tvůrcům, zejména při nástupu mikroprocesorů s více jádry (cores) – to je však téma, kterým se budeme zabývat v některé z dalších částí tohoto seriálu (ostatně se ukazuje, že lidé často a rádi porovnávají vlastnosti různých výrobků pomocí jediného čísla, které však vůbec nemusí výrobek dostatečně popisovat: výkon motorů v automobilech se často uvádí pouze v KW, vlastnosti fotoaparátu pouze údajem o počtu megapixelů atd.).
Další možnosti zapojení jednoduchých oscilátorů (bez krystalu)
3. Inicializace mikroprocesoru pomocí signálu Reset
Mikroprocesor je velmi složitý integrovaný obvod, který uvnitř obsahuje poměrně velké množsví navzájem propojených kombinačních i sekvenčních logických obvodů. Při zapnutí mikroprocesoru, tj. přivedení napájecího napětí a hodinového signálu, se jednotlivé obvody uvniř mikroprocesoru obecně nachází v neurčeném stavu. Samozřejmě je nutné, aby se mikroprocesor (jako celek) po zapnutí dostal do přesně stanoveného výchozího stavu. Většinou také potřebujeme mikroprocesor kdykoli restartovat, tj. dostat ho do výchozího stavu, ve kterém se nacházel po zapnutí. K těmto funkcím slouží signál Reset, na který reaguje řadič mikroprocesoru. U jednodušších počítačů bývá signál Reset generován tlačítkem doplněným o monostabilní integrovaný obvod, který zajistí automatické vygenerování tohoto signálu po připojení napájení (většina mikroprocesorů totiž vyžaduje, aby signál Reset trval určitou minimální dobu, například deset taktů hodinového signálu).
Ve chvíli, kdy řadič detekuje signál Reset, provede několik operací. Především sám sebe nastaví do výchozího stavu, ve kterém může na signál Reset adekvátně reagovat. Posléze vyšle na interní sběrnici mikroprocesoru sekvenci řídicích signálů, pomocí kterých nastaví obsah pracovních registrů na požadované výchozí hodnoty. Pracovní registry A a B jsou vynulovány, oba dva příznaky, tj. Carry flag a Zero flag jsou taktéž vynulovány, programový čítač PC je nastaven na adresu 0×0000, tj. na první buňku operační paměti´, a ukazatel na vrchol zásobníku SP naopak na adresu 0×ffff, tj. poslední buňku operační paměti.
Poznámka: veškeré adresy i operační kódy instrukcí budou zapisovány v hexadecimální soustavě, přičemž budu dodržovat „céčkovský“ způsob zápisu s prefixem 0×. Vzhledem k šestnáctibitové šířce všech registrů nesoucích adresu, tj. registrů PC a SP, je adresový rozsah mikroprocesoru roven 0–216-1, tj. hexadecimálně 0×0000 až 0×ffff.
Reakce řadiče mikroprocesoru na příchod signálu Reset
4. Načítání instrukcí z operační paměti a role registru PC
Jelikož je náš ukázkový mikroprocesor postaven na von Neumannově architektuře, je celá jeho činnost řízená programem zapsaným v operační paměti. Mikroprocesor je pomocí adresové sběrnice, datové sběrnice a řídicí sběrnice připojený k paměťovému subsystému, tj. jednomu či několika čipům, na nichž je vytvořena paměť typu RWM/RAM (pro čtení i zápis) a paměť typu ROM (pouze pro čtení). O paměťovém subsystému a různých technologiích pamětí si ještě něco řekneme v navazujících částech tohoto seriálu, dnes si však musíme ukázat, jakým způsobem mikroprocesor může načítat údaje uložené v těchto pamětech, především nás zajímá čtení programového kódu, tj. jak instrukcí tak i dat, které jsou k instrukcím přidružena. Po stránce vnější komunikace mikroprocesoru s pamětí je situace jednoduchá: procesor na adresovou sběrnici zapíše požadovanou adresu, ze které potřebuje instrukci číst a na řídicí sběrnici pošle příkaz pro čtení (R/Read). Po předem známé době paměť vypíše požadovaný údaj na datovou sběrnici, odkud ho mikroprocesor přečte.
Nyní tedy zbývá vyřešit, jaké adresy bude mikroprocesor zapisovat na adresovou sběrnici. Von Neumann mimo jiné předepsal, že procesor má instrukce vykonávat postupně tak, jak jsou zapsány v paměti a jediný způsob, jak tuto posloupnost instrukcí změnit, je použití příkazů pro skoky (popř. použití přerušení). Mikroprocesor si pamatuje adresu instrukce, která se má přečíst z operační paměti, v registru nazvaném PC (Program Counter). V případě, že není použita instrukce skoku, se hodnota tohoto registru postupně zvyšuje o hodnotu odpovídající délce právě přečtené instrukce. Jinými slovy – pokud má instrukce délku jednoho bytu, je PC zvýšen o jedničku, pokud je instrukce delší, například dva byty, je PC zvýšen o dvojku. Počáteční hodnota PC po restartu mikroprocesoru je rovna nule, tj. mikproprocesor začíná načítat program od začátku (nulté adresy) operační paměti (jiné mikroprocesory však mohou používat odlišnou startovací adresu).
Na následující animaci je ukázáno, jakým způsobem mikroprocesor zvyšuje hodnotu registru PC o jedničku. Vzhledem k tomu, že je náš mikroprocesor opravdu velmi jednoduchý, neobsahuje specializovaný obvod, který by toto zvyšování prováděl autonomně (například paralelně s jinou operací), ale je zapotřebí použít univerzální ALU, která obsahuje požadovanou instrukci INC, tj. zvýšení hodnoty prvního pomocného registru o jedničku. Toto řešení je sice jednoduché a šetří zdroje (tranzistory, hradla), na druhou stranu však může znamenat zpomalení provádění instrukcí, což nás však prozatím nemusí příliš trápit (také nás netrápí zbytečné „rozředění“ operačních kódů instrukcí a jejich adresních částí, jak si ostatně ukážeme v následujících třech kapitolách).
Způsob zvýšení hodnoty registru PC o jedničku pomocí ALU
5. Operační kódy instrukcí (opkódy), strojový kód a assembler
Mikroprocesor zpracovává program zapsaný ve strojovém jazyku neboli strojovém kódu. Jedná se o binární formát složený z jednotlivých operačních kódů instrukcí doplněných o adresní část, popř. konstanty. Každý mikroprocesor, resp. každá navzájem kompatibilní vývojová řada mikroprocesorů, má svůj vlastní strojový kód, který není obecně přenositelný na ostatní mikroprocesory. Operační kód instrukce jednoznačně určuje, jakou operaci má mikroprocesor provést, pomocí adresní části se specifikuje, s jakými operandy (registry, místy paměti, periferními zařízeními) se má daná operace provést, a konečně konstanty mohou určovat hodnotu, která se zúčastní aritmetické operace, absolutní či relativní adresu, na kterou se má provést skok, hodnotu posunu, která se přičte k adrese uložené v registru atd. Program zapsaný ve strojovém kódu může vypadat po výpisu v hexadecimální soustavě a doplněním sloupce s adresami následovně:
0000: B81300
0003: CD10
0005: 6800A0
0008: 07
0009: 33FF
000B: B106
000D: C706750100E0
0013: BE4001
0016: B520
0018: 33C0
001A: 8BE8
001C: 93
001D: 8BC5
001F: D3F8
0021: F7E8
0023: 50
0024: 8BC3
0026: D3F8
0028: F7E8
002A: 268905
002D: 8BC5
002F: D3F8
0031: C1FB05
Což zajisté není příliš čitelné. Z důvodu snadnější tvorby programů byl vyvinut jazyk symbolických instrukcí (JSI), někdy také nazývaný jazyk symbolických adres (JSA), ovšem nejčastěji se setkáme s poněkud nepřesným názvem assembler či assembly language. Jedná se o jazyk, ve kterém je program zapsán pomocí mnemotechnických zkratek instrukcí a ne jejich číselnými kódy. Také je možné používat takzvaná návěští (labels), což jsou vlastně pojmenované adresy, na kterých může ležet buď začátek nějaké funkce, zpracovávaná data nebo cíl skoku. Kromě toho mnoho assemblerů podporuje i pseudoinstrukce a také makra, která jsou mnohdy poměrně vysokoúrovňová. Výše uvedený program by v assembleru mikroprocesoru 80386 v šestnáctibitovém režimu vypadal následovně (v levé části je pro porovnání zobrazen i strojový kód, který je generovaný z assemblerovského zdrojového kódu):
0000: B81300 mov ax,00013
0003: CD10 int 010
0005: 6800A0 push 0A000
0008: 07 pop es
0009: 33FF xor di,di
000B: B106 mov cl,006
000D: C706750100E0 mov w,[00175],0E000
0013: BE4001 mov si,00140
0016: B520 mov ch,020
0018: 33C0 xor ax,ax
001A: 8BE8 mov bp,ax
001C: 93 xchg bx,ax
001D: 8BC5 mov ax,bp
001F: D3F8 sar ax,cl
0021: F7E8 imul ax
0023: 50 push ax
0024: 8BC3 mov ax,bx
0026: D3F8 sar ax,cl
0028: F7E8 imul ax
002A: 268905 mov es:[di],ax
002D: 8BC5 mov ax,bp
002F: D3F8 sar ax,cl
0031: C1FB05 sar bx,005
Poněkud matoucí může být to, že program napsaný v assembleru se překládá překladačem nazvaným assembler, v praxi však je z okolností patrné, o čem se mluví: „píšu to v assembleru“ vs. „přeložil jsem ten program assemblerem“.
Načtení operačního kódu instrukce do řadiče
Na rozhraní mezi assemblerem a vyššími programovacími jazyky (například Céčkem, Pascalem atd.) leží takzvaný autokód. Jedná se vlastně o velmi jednoduchý programovací jazyk, ve kterém je však možné používat pouze ty objekty, se kterými se pracuje v samotném assembleru. Mj. to znamená, že sice existuje možnost zápisu jednoduchých aritmetických výrazů:
A=A+B (odpovídá add A, B v assembleru)
nebo podmínek:
if A go to L1:L2:L3 (když A<0, skok na L1, když A=0 skok na L2, jinak skok na L3)
ale žádné vyšší operace či datové typy již není možné použít. Autokód se v současnosti příliš často nepoužívá, místo toho se (v odůvodněných případech) kombinuje zápis části programu v assembleru s programem napsaným v některém plnohodnotném vyšším programovacím jazyce, například v céčku. V minulosti byla snaha o vytvoření jakéhosi hybrida mezi céčkem a assemblerem pro architekturu x86 – jednalo se o jazyk C–, který byl poměrně vynalézavý, například znal operátor >< – ovšem dnes už pravděpodobně není aktivně vyvíjen (poslední verzi si pamatuji ještě z dob DOSu).
6. Podrobnější popis instrukčního souboru
V předchozí části tohoto seriálu byl uveden celý instrukční soubor, tj. sada instrukcí, kterou náš ukázkový mikroprocesor dokáže provádět. Jedná se o celkem 32 instrukcí rozdělených do sedmi skupin:
Kód instrukce (hex) | Mnemotechnická zkratka instrukce | Význam |
---|---|---|
Aritmetické instrukce | ||
00 | ADD | součet obsahu registrů A a B |
01 | ADC | součet obsahu registrů s přenosem |
02 | SUB | rozdíl obsahu registrů A a B |
03 | SBB | rozdíl obsahu registrů s výpůjčkou |
04 | INC | zvýšení obsahu registru A či B o 1 |
05 | DEC | snížení obsahu registru A či B o 1 |
Logické instrukce | ||
06 | AND | operace bitového součinu nad všemi korespondujícími bity registrů A a B |
07 | OR | operace bitového součtu nad všemi korespondujícími bity registrů A a B |
08 | XOR | operace bitové nonekvivalence nad všemi korespondujícími bity registrů A a B |
09 | COM | negace všech bitů jednoho z registrů A či B |
Posuvy a rotace | ||
0a | RL | rotace obsahu registru A či B doleva |
0b | RLC | rotace obsahu registru A či B doleva přes příznak přenosu |
0c | RR | rotace obsahu registru A či B doprava |
0d | RRC | rotace obsahu registru A či B doprava přes příznak přenosu |
0e | ASR | aritmetický posun obsah registru A či B doprava |
Testování a porovnání | ||
0f | CMP | aritmetické porovnání obsahu registrů a ovlivnění příznaků |
10 | TEST | bitové porovnání obsahu registrů a ovlivnění příznaků |
Přesuny mezi pamětí a registry | ||
11 | LD | načtení konstanty či obsahu adresy z paměti do registru A či B |
12 | ST | uložení obsahu registru A či B na danou adresu paměti |
13 | MOV | přesun dat mezi registry |
14 | PUSH | uložení obsahu registru A či B na zásobník |
15 | POP | obnovení obsahu registru A či B ze zásobníku |
Skokové a návratové instrukce | ||
16 | JMP | nepodmíněný skok na zadanou adresu |
17 | CALL | volání podprogramu |
18 | RET | návrat z podprogramu |
19 | IRET | návrat z přerušení (interrupt) |
1a | JC | podmíněný skok za předpokladu, že je nastaven příznak přenosu (carry flag) |
1b | JNC | podmíněný skok za předpokladu, že je vynulován příznak přenosu (carry flag) |
1c | JZ | podmíněný skok za předpokladu, že je nastaven příznak nulovosti (zero flag) |
1d | JNZ | podmíněný skok za předpokladu, že je vynulován příznak nulovosti (zero flag) |
Nezařazené zbývající instrukce | ||
1e | NOP | neprovádí se žádná operace, mikroprocesor přejde na další instrukci |
1f | HALT | mikroprocesor se zastaví a čeká na příchod externího přerušení |
V prvním sloupci výše uvedené tabulky je uveden takzvaný operační kód instrukce, což je v tomto případě hexadecimální hodnota bytu načítaného z operační paměti. Ve druhém sloupci je zapsáno jméno instrukce, neboli mnemotechnická zkratka anglického názvu instrukce. U jednotlivých procesorů, popř. výrobních řad procesorů, jsou použity různé délky mnemotechnických zkratek, od dnes již málo používaných jednopísmenných zkratek (L-load, A-add, R-move to register) přes pravděpodobně nejčastěji používané třípísmenové zkratky (ADD, SUB-subtract, MOV-move, JMP-jump) po názvy instrukcí složených z celých anglických slov, nejenom jejich zkratek (ADD, LOAD, STORE, MOVE, JUMP). V následující kapitole bude popsána instrukce MOV sloužící pro přesuny dat mezi registry A a B.
7. Instrukce pro přesuny dat mezi registry
Mezi často používané instrukce patří instrukce sloužící pro přesun dat mezi jednotlivými pracovními registry mikroprocesoru. Vzhledem k tomu, že náš ukázkový mikroprocesor obsahuje pouze dva pracovní registry označené A a B, jsou k dispozici pouze dvě instrukce pro přesun dat: první slouží pro přesun hodnoty z registru B do registru A a druhá pro opačný přesun, tj. z registru A do registru B. Instrukce pro přesun dat má mnemotechnickou zkratku mov se základním operačním kódem 0×13. Rozhodnutí o cílovém registru se v assembleru rozlišuje zápisem instrukce:
mov A,B ; přesun z registru B do registru A
mov B,A ; přesun z registru A do registru B
V závislosti na tom, ze kterého registru se data čtou a do kterého registru se zapisují, se musí rozšířit i samotná instrukce o adresní část. V případě, že byte následující za operačním kódem ve strojovém kódu obsahuje hodnotu 0×01, bude se provádět přesun z registru A do registru B (cílem je B), v případě hodnoty 0×00 bude přesun opačný, tj. z registru B do registru A (cílem je A). Možná vás napadlo, že vzhledem k tomu, že náš mikroprocesor obsahuje pouze 32 instrukcí, které lze kódovat pomocí pěti bitů (25=32), je možné onen jediný bit rozhodující o tom, která ze dvou přenosových operací se skutečně použije, vložit přímo do operačního kódu. Je to myšlenka správná a prakticky všechny mikroprocesory mají svoji instrukční sadu „hutnější“ než náš mikroprocesor, ovšem pro účely snazšího čtení strojového kódu ponechám operační kód a adresní část v samostatných bytech, i když to podstatným způsobem program prodlužuje:
Strojový kód | Význam |
---|---|
13 00 | mov A,B |
13 01 | mov B,A |
U některých mikroprocesorů, které obsahují větší množství pracovních registrů, samozřejmě stoupá i počet různých „cest“, kterými mohou data putovat. Například u mikroprocesoru Intel 8080 je celkem 63 operačních kódů z 256, tj. celá čtvrtina, rezervovaná právě pro přesuny dat, přičemž bylo možné kódovat i instrukce typu mov b,b, tj. přesun z registru do toho samého registru (v podstatě se jedná o jinak zakódovanou instrukci nop). Samotná implementace přesunu je uvnitř mikroprocesoru celkem jednoduchá, jak ostatně ukazují následující dvě animace.
Způsob provedení instrukce mov A, B
Ještě dodám, že existují i mikroprocesory, které nemohou pro přesuny použít interní sběrnici – v tomto případě jdou veškerá data přes ALU, která musí obsahovat operaci, která pouze převede vstupní data na výstup bez jejich úpravy. Tato zdánlivá nelogičnost má svoje opodstatnění, protože se zjednoduší jak řízení celého mikroprocesoru, tak i jeho interní struktura.
Způsob provedení instrukce mov B, A
8. Obsah následující části tohoto seriálu
I v další části seriálu budeme pokračovat v popisu funkcí, které provádí mikroprocesor při zpracování instrukcí, přesněji řečeno operačních kódů (opkódů), načítaných z operační paměti. Především si vysvětlíme všechny aritmetické a logické instrukce a jejich použití (spolu s instrukcemi skoku a příznakovým registrem) pro řízení běhu programu.