HTML · Slides · PDF | Přehled předmětu
Cíle
-
Seznámit se s instrukční sadou vybraných procesorů
-
Poznat a pochopit principy programování v jazyku symbolických adres (assembleru)
Počítač vykonává instrukce ve svém strojovém jazyce. Lidsky čitelný zápis těchto instrukcí se nazývá jazyk symbolických adres neboli assembly language; assembler je program, který tento zápis překládá do strojového kódu.
Programování v jazyce symbolických adres
-
Znalost tzv. programátorského modelu procesoru
-
Soupis instrukcí procesoru
-
Znalost metod určování operandů
-
Mít k dispozici assembler, tedy překladač z jazyka symbolických adres do strojového kódu
Programátorský model
-
Programátorský model je abstrakce popisující, jak software vidí a ovládá procesor: jeho registry, datové typy, instrukce, adresování a stav.
-
Jeho znalost je důležitá při vývoji operačních systémů, překladačů, debuggerů a vestavěného softwaru i při analýze výkonu. Běžný aplikační kód však obvykle překládá optimalizující překladač.
-
Kvůli jednoduchosti začneme 8bitovým procesorem; základní principy platí také pro 16bitové, 32bitové a 64bitové procesory.
Obecný programátorský model 8-bitového CPU
Procesor Intel 8080
Základní registry procesoru
-
A (Accumulator) — 8bitový střádač,
-
PC (Program Counter) — 16bitový čítač instrukcí,
-
SP (Stack Pointer) — 16bitový ukazatel zásobníku,
-
šest 8bitových univerzálních registrů B, C, D, E, H, L,
-
registry lze pro 16bitové operace seskupit do párů BC, DE a HL,
-
příznakové bity (flags).
Příznakové bity
| Z (Zero) |
1 při nulovém výsledku operace, jinak 0 |
| S (Sign) |
kopie nejvyššího, tedy znaménkového bitu výsledku |
| CY (Carry) |
přenos z nejvyššího řádu výsledku |
| AC (Auxiliary Carry) |
přenos mezi bity 3 a 4 výsledku |
| P (Parity) |
1 při sudém počtu jedničkových bitů výsledku, jinak 0 |
Skryté registry
Programátorovi neviditelné:
| IR |
instrukční registr (8bitový); je napojen na dekodér instrukcí (řadič), |
| DR |
datový registr (8 bitový); registr pro čtení/zápis dat z/do paměti, |
| AR |
adresový registr (16 bitový); adresa pro čtení/zápis z/do paměti, |
| TA |
Temporary Address register (skládá se z TA(H) (TA High - 8 bitů), TA(L) (TA Low - 8 bitů)). |
Instrukce procesoru
Zápis instrukce MOV
MOV X,Y
Zápis instrukce LDA
-
Význam: načtení jednoho bytu z uvedené 16bitové adresy paměti do registru
A -
Zápis v symbolickém jazyku
-
LDA adresa -
Příklady:
LDA 0010H,LDA 1234H
-
Příklad programu
-
Na adrese X=0000000000010000b a adrese Y=0000000000010001b jsou operandy x = 00000010 a y = 00000011
-
Vypočtěte
x := x + y
LDA 0000000000010000b ;načtení x do registru A
MOV B,A ;přesun obsahu A do B
LDA 0000000000010001b ;načtení y do registru A
ADD B ;sečtení obsahu A a B
STA 0000000000010000b ;uložení obsahu registru A na adresu X
Program v paměti
Vykonávání a instrukce programu
-
Doba provedení instrukce se popisuje počtem taktů; může záviset na instrukci, operandech, přístupu do paměti i stavu mikroarchitektury. U procesoru Intel 8080 se skládá z několika stavů T a strojových cyklů.
-
Z pohledu programu se instrukce dokončují tak, aby odpovídaly pořadí určenému čítačem instrukcí, skoky, voláními a přerušeními. Moderní procesor je přitom může interně zpracovávat paralelně nebo mimo pořadí.
-
Po resetu začne CPU načítat instrukce z adresy definované danou architekturou. U procesoru Intel 8080 je resetovací adresa
0000H; jiné architektury ji mohou mít odlišnou. Procesor může být také zastaven instrukcíHLT, čekat na událost nebo přejít do úsporného stavu.
Přesun dat (Data Transfer Instructions)
Slouží k přesunu dat mezi registry, pamětí a akumulátorem.
| MOV dest, src |
přesun dat mezi registry |
| MVI reg, data |
vložení konstanty do registru |
| LXI rp, data16 |
načtení 16bitové konstanty do páru registrů |
| LDA addr / STA addr |
načtení/uložení akumulátoru z/do paměti |
| LHLD addr / SHLD addr |
načtení/uložení páru HL z/do paměti |
| XCHG |
výměna HL a DE |
Aritmetické instrukce
Provádějí sčítání, odčítání a další operace.
| ADD reg / ADI data |
sčítání s akumulátorem |
| SUB reg / SUI data |
odčítání od akumulátoru |
| INR reg / DCR reg |
inkrementace/dekrementace |
| DAD rp |
sčítání páru registrů s HL |
| DAA |
úprava akumulátoru pro BCD |
Logické instrukce
Provádějí logické operace jako AND, OR, XOR.
| ANA reg / ANI data |
AND s akumulátorem |
| ORA reg / ORI data |
OR s akumulátorem |
| XRA reg / XRI data |
XOR s akumulátorem |
| CMP reg / CPI data |
porovnání s akumulátorem |
| RLC, RRC, RAL, RAR |
rotace bitů v akumulátoru |
Řídicí instrukce
Slouží k řízení toku programu.
| JMP addr |
skok na adresu |
| JC, JNC, JZ, JNZ, JP, JM, JPE, JPO |
podmíněné skoky |
| CALL addr / RET |
volání a návrat z podprogramu |
| CC, CNC, CZ, CNZ, CP, CM, CPE, CPO |
podmíněná volání podprogramu |
| RC, RNC, RZ, RNZ, RP, RM, RPE, RPO |
podmíněné návraty z podprogramu |
Instrukce se zásobníkem
| PUSH rp / POP rp |
práce se zásobníkem |
| SPHL |
nastavení SP podle HL |
| XTHL |
výměna HL se zásobníkem |
Vstupně-výstupní instrukce
| IN port |
načtení dat z I/O portu do akumulátoru |
| OUT port |
odeslání dat z akumulátoru do I/O portu |
|
Řídicí instrukce
| NOP |
žádná operace |
| HLT |
zastavení procesoru |
| DI / EI |
zakázání/povolení přerušení |
Příklad výpočtu stejného výrazu na různých architekturách
-
Výpočet stejného výrazu s různým počtem explicitních operandů instrukce.
-
Více explicitních operandů může snížit počet potřebných instrukcí, ale obvykle zvětšuje jejich kódování. Rychlost závisí také na mikroarchitektuře, přístupech do paměti a práci překladače; z počtu operandů ji nelze určit.
Počet explicitních operandů instrukce
-
Nyní opustíme svět procesoru Intel 8080 a porovnáme instrukce podle počtu explicitně zapsaných operandů. Nejde o adresovací režimy a konkrétní ISA nemusí nabízet všechny uvedené varianty.
| Tříoperandové |
instrukce se třemi explicitními operandy (např. |
| Dvouoperandové |
instrukce se dvěma explicitními operandy (např. |
| Jednooperandové |
instrukce s jedním explicitním operandem (např. |
| Nulaoperandové |
operandy jsou implicitně na zásobníku; například |
Tříoperandová instrukce
-
Y = A * B + (C - D) * E / F-
Násob:
A * B → P1 -
Odečti:
C - D → R -
Násob:
R * E → R -
Děl:
R / F → R -
Sečti:
R + P1 → Y
-
Dvouoperandová instrukce
-
Y = A * B + (C - D) * E / F
-
Násob A B:
A * B → R; výsledek jde vždy do R -
Zapiš do P1:
R → P1; zapisuje se R do P1 -
Odečti C D:
C - D → R; výsledek jde vždy do R -
Násob E:
R * E → R; výsledek jde vždy do R -
Děl F:
R / F → R -
Přičti P1:
R + P1 → R -
Zapiš do Y:
R → Y
Jednooperandová instrukce
-
Y = A * B + (C - D) * E / F
-
Načti A:
A → R -
Násob B:
R * B → R -
Zapiš do P1:
R → P1 -
Načti C:
C → R -
Odečti D:
R - D → R -
Násob E:
R * E → R -
Děl F:
R / F → R -
Přičti P1:
R + P1 → R -
Zapiš do Y:
R → Y
Realizace výpočtů v Intel 8080
-
sčítání (ADD, ADI) a odčítání (SUB, SUI) je přímo podporováno instrukcemi procesoru
-
násobení a dělení je realizováno pomocí podprogramů, protože instrukce procesoru je nepodporují
-
dnešní CPU (např. x86-64, Arm/AArch64) obvykle umí násobit i dělit přímo v hardwaru; celočíselné dělení nemusí být podporováno u jednodušších vestavěných jader (např. Cortex-M0), kde se realizuje softwarově
Násobení (8bit × 8bit)
-
Vstupy: činitelé v registrech A a B
-
Inicializuj výsledek na 0.
-
Opakuj B-krát:
-
Přičti A k výsledku.
-
Použij registr jako čítač smyčky.
-
Výsledek je 16bitový, protože součin dvou 8bitových neznaménkových čísel může být až 65025 (
255 × 255).
Násobení v assembleru Intel 8080
; Vstup: A = první činitel, B = druhý činitel
; Výstup: DE = 16bitový výsledek
; Mění také registry A, C a H
MOV H, A ; H = původní první činitel
MOV C, B ; C = čítač smyčky
MVI D, 00H ; D = vyšší byte výsledku
MVI E, 00H ; E = nižší byte výsledku
MOV A, C
ORA A ; nastav Z, pokud je druhý činitel nulový
JZ MUL_DONE
MUL_LOOP:
MOV A, E
ADD H ; přičti první činitel k nižšímu bytu
MOV E, A
JNC NO_CARRY
INR D ; přenos do vyššího bytu
NO_CARRY:
DCR C
JNZ MUL_LOOP
MUL_DONE: ; DE obsahuje výsledek
Dělení
-
Dělení
A / Bvrací podíl vCa zbytek vA. -
Výsledek je: C = podíl (kolikrát se dělitel
Bvejde do dělence) A = zbytek po dělení
Algoritmus dělení pomocí odčítání
-
Inicializuj podíl na 0.
-
Dokud je dělenec větší nebo roven děliteli:
-
Odečti dělitel od dělence.
-
Inkrementuj podíl.
-
Zbytek je hodnota, která zůstane z dělence.
Dělení dvou 8-bitových čísel v assembleru Intel 8080
-
Podprogram vrací podíl v registru
Ca zbytek v registruA. -
Vstupní hodnoty jsou dělenec v registru
Aa dělitel v registruB.
; Dělení dvou 8bitových čísel
; vstup:
; A = dělenec
; B = dělitel
; výstup:
; C = podíl
; A = zbytek
;
; předpoklad: B != 0
DIV8:
MVI C, 00H ; podíl = 0
LOOP: CMP B ; porovnej A s B
JC DONE ; A < B -> konec
SUB B ; A = A - B
INR C ; podíl++
JMP LOOP
DONE: ; C = podíl, A = zbytek
Použití podprogramu
MVI A, 25H ; dělenec
MVI B, 06H ; dělitel
CALL DIV8 ; výsledek: C = podíl, A = zbytek
; zde C = 06H a A = 01H
Moderní architektury: programátorský model x86-64
-
Nyní podrobněji k architektuře x86-64 a.k.a. Intel x64, přesněji AMD64.
-
Základní x86-64 má 16 obecných 64bitových registrů:
RAX,RBX,RCX,RDX,RSI,RDI,RSP,RBPaR8ažR15. -
Rozšíření Intel APX definuje dalších 16 obecných registrů; software je smí použít jen tehdy, když je podporují procesor, operační systém, ABI a nástroje.
-
Registry mají pojmenované podčásti, například
RAX,EAX,AXaAL. Zápis do 32bitové podčásti, napříkladEAX, v 64bitovém režimu nuluje horních 32 bitů registru.
Instrukční sada x86-64
-
Architekturu původně uvedla společnost AMD jako AMD64; Intel používá kompatibilní označení Intel 64.
-
Zachovává proměnnou délku instrukcí a rozsáhlou zpětnou kompatibilitu x86.
-
Volitelná rozšíření, například AVX, AVX2, AVX-512 nebo APX, je nutné před použitím detekovat; nejsou přítomna v každém procesoru.
Paměťový model
-
Lineární adresy mají v 64bitovém režimu 64bitový formát, ale musejí být kanonické.
-
Čtyřúrovňové stránkování překládá 48 bitů lineární adresy; volitelné pětistupňové stránkování LA57 překládá 57 bitů.
-
Šířka fyzické adresy je implementační vlastnost zjišťovaná instrukcí
CPUID; architektura ji omezuje nejvýše na 52 bitů. -
V 64bitovém režimu se obvykle používá plochý paměťový model. Báze segmentů
CS,DS,ESaSSse fakticky neuplatňují, zatímcoFSaGSmohou mít nenulovou bázi.
Režimy běhu
-
Legacy mode používá tradiční 16bitové nebo 32bitové režimy x86.
-
IA-32e/long mode zahrnuje dva podrežimy:
-
compatibility mode pro vybraný 16bitový nebo 32bitový kód pod 64bitovým operačním systémem,
-
64-bit mode pro 64bitový kód a 64bitové obecné registry.
-
Stav procesoru
-
Registr
RFLAGSobsahuje stavové a řídicí příznaky, například Zero, Carry a Overflow. -
Řídicí registry
CR0,CR2,CR3,CR4aCR8spravují mimo jiné režim procesoru, stránkování a přerušení. -
EFER(Extended Feature Enable Register) je modelově specifický registr MSR, nikoli člen řady řídicích registrůCR.
Výpočty s plovoucí řádovou čárkou
-
Historická jednotka x87 používá zásobník registrů
ST(0)ažST(7). -
V moderním 64bitovém kódu se skalární a vektorové výpočty obvykle provádějí instrukcemi SSE/SSE2 a podle dostupnosti AVX, AVX2 nebo AVX-512.
Obecné registry
Většinu registrů lze použít pro výpočty, data i adresy. Jejich zvláštní role často vyplývá z konkrétní instrukce nebo aplikačního binárního rozhraní (ABI).
RAX
|
tradiční akumulátor; ABI jej obvykle používá také pro návratovou hodnotu |
RBX
|
obecný registr s historickou rolí bázového registru |
RCX
|
obecný registr a implicitní čítač některých instrukcí |
RDX
|
obecný registr používaný implicitně například při násobení, dělení a I/O |
RSI
|
obecný registr a implicitní zdrojový index řetězcových instrukcí |
RDI
|
obecný registr a implicitní cílový index řetězcových instrukcí |
RSP
|
ukazatel zásobníku |
RBP
|
obecný registr, který lze podle ABI a nastavení překladače použít jako ukazatel rámce |
R8–R15
|
další obecné registry základní architektury x86-64 |
|
Použitelné podčásti nejsou u všech registrů pojmenovány stejně. Například |
Ukazatel instrukce a příznaky
RIP
|
Instruction Pointer, určuje adresu následující instrukce podle aktuálního toku řízení |
RFLAGS
|
registr příznaků |
ZF
|
Zero Flag, výsledek operace je nula |
CF
|
Carry Flag, přenos při aritmetice |
SF
|
Sign Flag, znaménko výsledku |
OF
|
Overflow Flag, přetečení se znaménkem |
Vektorové registry
XMM0–XMM15
|
128bitové registry základního 64bitového prostředí, používané instrukcemi SSE/SSE2 |
YMM0–YMM15
|
256bitové rozšíření registrů XMM dostupné s AVX |
ZMM0–ZMM31
|
512bitové registry AVX-512; jejich nižší části se zároveň označují XMM a YMM
|
Řídicí a systémové registry
-
Řídicí registry a registry MSR jsou určeny především pro operační systém a privilegovaný software.
-
Dostupnost konkrétních registrů a funkcí se zjišťuje pomocí
CPUID; nelze ji předpokládat pouze podle označení x86-64.
Doporučené zdroje
Na další kapitolu: Paměti
HTML · Slides · PDF | Přehled předmětu