11. diel - Assembler - Registre procesora
V minulej lekcii, Assembler - Kombinácia skokov a príznaky , sme sa naučili kombinovať podmienené a nepodmienené skoky, zistili, ako skokové inštrukcie získajú výsledok z CMP a ako porovnávať signed čísla.
V dnešnom ASM tutoriále si ukážeme skoro všetky registre procesorov x86 a x64.
Na registre sme už v našom assembler kurze narazili a vieme, že sú to "premenné", napevno vytvorené priamo v procesore. Sú veľmi rýchle, pretože sa ušetrí cesta medzi procesorom a pamäťou RAM. V miniatúrnom procesora je samozrejme tiež veľmi obmedzené miesto, preto je registrov vytvorených len obmedzené množstvo a majú špecifické určenia. Registre sa tiež výrazne líši podľa architektúry procesora.
V dnešnom ASM tutoriálu si skoro celé registre CPU konečne zmapujeme. Na začiatku kurzu by to pre nás boli príliš vyčerpávajúce informácie, teraz je už však dokážeme využiť.
Všeobecné registre
Pri programovaní najčastejšie pracujeme so všeobecnými registrami (anglicky general-purpose). Do nich možno ukladať celé čísla a alebo adresy (ukazovatele do pamäte). Používajú sa pri výpočtoch, na ukladanie dočasných hodnôt alebo lokálnych premenných.
16-bitové
V ére MS-DOS a Windows 3.X boli registre iba 16-bitové. To komplikovalo nielen výpočty s väčšími číslami, ale hlavne prístupy do pamäti, pretože 64 KB čoskoro prestalo aplikáciám stačiť. V procesora je týchto 8 všeobecných registrov:
AX
- akumulátor (akumulačné register)BX
- bázový registerCX
- čítač (counter)DX
- dátový registerSI
- index pre zdroj (Source Index)DI
- index pre cieľ (Destination Index)BP
- ukazovateľ na záznam aktívny procedúry na zásobníku (Base Pointer)SP
- ukazovateľ vrcholu zásobníka (Stack Pointer)
Registre BP
a SP
sa používajú pri volaní
funkcií. Ostatné registre sú naozaj všeobecné a môžeme si do nich
ukladať, čo chceme. Ich pomenovanie potom slúži k lepšej orientácii v
kóde, napr. Do AX
uložíme výsledok nejaké aritmetické
operácie a do CX
budeme ukladať počítadlo cyklu.
S registre AX
, BX
, CX
, DX
môžeme pracovať tiež tak, že si ich rozdelíme na dva menšie osembitové
registre. Hornej polovice sa označujú AH
, BH
,
CH
, DH
(High). Dolnej polovice sú potom
AL
, BL
, CL
, DL
(Low).
Nasledujúci príklad ukazuje, ktorá časť registra AX
sa
zmení, ak zapíšeme hodnotu do AH
alebo AL
:
mov ax, 1122h ;ax=1122h mov al, 33h ;ax=1133h mov ah, 44h ;ax=4433h
32-bitové
Dostávame sa do čias Windows 95 - XP. Pri rozšírení všetkých
16-bitových registrov na 32-bitové bol k názvu pridaný prefix E
(Extended) - EAX
, EBX
, ECX
atď. Do
týchto registrov potom môžeme uložiť väčšie čísla. V počítači
môžeme mať až 4 GB pamäte.
Keď v 32-bitovej aplikáciu zapíšeme hodnotu do 16-bitového registra, potom sa zmení dolných 16 bitov z 32-bitového registra, zatiaľ čo horných 16 bitov sa nezmení. Ukážme si príklad:
mov eax,12345678h ;eax=12345678h mov ax,99aah ;eax=123499aah mov al,bbh ;eax=123499bbh mov ax,cch ;eax=123400cch
64-bitové
Dostávame sa k súčasným procesorom. V 64-bitových aplikáciách máme dvakrát viac všeobecných registrov.
Rozšírenie pôvodných registrov
Pôvodných 8 registrov bolo rozšírené a majú prefix R
-
RAX
, RBX
atď.
Nové registre
K tomu pribudli registre R8
až R15
. K tým možno
pridať sufixy D
, W
a B
pre prístup k
nižším 32
, 16
a 8
bitom. Napr.
R8D
, R8W
, R8B
.
V 32-bitových aplikáciách sme mali často nedostatok registrov a lokálne premenné sme museli ukladať do pamäte, čo bolo pomalé. V 64-bitových aplikáciách máme také množstvo registrov, že sa do nich zmestí väčšina lokálnych premenných a odovzdávajú sa v nich aj parametre funkcií.
Nasledujúci príklad ukazuje, ako sa mení register RAX
, ak
zapisujeme hodnoty do AH
, AL
, AX
,
EAX
:
mov rax,1111222233334444h ;rax=1111222233334444h mov ah,55h ;rax=1111222233335544h mov al,66h ;rax=1111222233335566h mov ax,7777h ;rax=1111222233337777h mov eax,88888888h ;rax=0000000088888888h mov eax,-1 ;rax=00000000ffffffffh
Všimnite si, že pri zápise 8-bitové alebo 16-bitové hodnoty sa vyššia bajty registra nemení. Ale pri zapísanie 32-bitové hodnoty sa ostatní bajty vynulujú. To môžeme využívať na optimalizáciu. Ak chceme do 64-bitového registra uložiť malé nezáporné číslo, môžeme použiť 32-bitový register.
X87 registre
Pri výpočtoch s reálnymi číslami sa používajú 80-bitové registre
ST(0)
až ST(7)
. K nim možno pristupovať ako k
zásobníku. To znamená, že pri výpočtoch sa môžeme odkazovať na
konkrétne registre, ale často nám stačí len pridávať čísla na vrchol
zásobníka a vykonávať s nimi matematické operácie.
MMX registre
Registre MM0
až MM7
sú synonymá pre
ST(0)
až ST(7)
, ale používajú sa k výpočtom s
celými číslami. V dnešnej dobe už nemá zmysel ich používať, pretože
SSE je lepšie, viď ďalej.
SSE registre
Do šestnástich 128-bitových registrov XMM0
až
XMM15
môžeme ukladať reálne aj celé čísla. Do každého
registra sa zmestí viac čísel naraz (dve 64-bitová čísla alebo štyri
32-bitová). Raz inštrukcií tak možno vykonať operáciu s niekoľkými
hodnotami naraz. To sa nazýva SIMD (Single Instruction, Multiple Data). Tejto
technológie využijeme pri práci s vektormi alebo spracovanie veľkých dát.
Získame tým samozrejme vyšší výkon. Vo 32-bitových aplikáciách je
prístupná len polovica registrov (XMM0
až
XMM7
).
Okrem dátových registrov existuje ešte stavový a riadiaci register
MXCSR
.
AVX registre
Novšie procesory majú SSE registre rozšírené na 256 bitov a nazývajú
sa YMM0
až YMM15
. Ešte novšie procesory majú
512-bitové registre ZMM0
až ZMM15
a navyše opmask
registre K0
až K7
.
Príznaky - Register EFLAGS
O príznakoch sme si už hovorili v lekcii Assembler -
Kombinácia skokov a príznaky. Môžu mať hodnotu buď 0
alebo 1
a označujú nejakú situáciu, ku ktorej pri vykonávaní
inštrukcií došlo. Môžeme je nasledujúci inštrukcií prečítať a na stav
programu reagovať.
Register EFLAGS
teda obsahuje niekoľko jednobitových hodnôt.
Niektoré z nich nemôžeme sami v aplikácii meniť a má k nim prístup len
operačný systém. Pre nás sú najdôležitejšie aritmetické príznaky
CF
, ZF
, SF
a OF
, ktoré sme
si už popísali.
Dnes si ukážme úplnejší zoznam:
- 0.
CF
(Carry) - aritmetické pretečenie s číslami bez znamienka, alebo pri bitových posuvoch a rotáciách - 2.
PF
(Parity) - párne parita, dolná osmice bitov výsledku obsahuje párny počet jednotiek - 4.
AF
(Auxiliary Carry) - pretečeniu medzi 3. a 4. bitom pri BCD aritmetike - 6.
ZF
(Zero) - nulový výsledok - 7.
SF
(Sign) - záporný výsledok - 8.
TF
(Trap) - krokovanie po inštrukciách pri ladení - 9.
IF
(Interruption) - povolená hardvérová prerušenia - 10.
DF
(Direction) - reťazcové inštrukcie v opačnom smere (napr.MOVSB
znižujeSI
,DI
) - 11.
OF
(Overflow) - aritmetické pretečenie s číslami so znamienkom - 12-13.
IOPL
(I / O Privilege Level) - úroveň povolení pre inštrukcieIN
/OUT
- 14.
NT
(Nested Task) - inštrukcieIRET
pri návrate z prerušenia prepne na ďalší proces - 16.
RF
(Resume) - maskuje opakovanie ladiaceho prerušenia - 17.
VM
(Virtual-8086 Mode) - virtualizácia 16-bitových aplikácií v 32-bitovom operačnom systéme - 18.
AC
(Alignment Check) - kontrola zarovnanie, vyvolá prerušenie pri prístupe na adresu, ktorá nie je deliteľné dĺžkou hodnoty - 19.
VIF
(Virtual Interrupt) - príznakIF
pri virtualizácii - 20.
VIP
(Virtual Interrupt Pending) - bolo vyvolané prerušenia pri virtualizácii - 21.
ID
(Identification) - ak sa dá nastaviť, je podporovaná inštrukcieCPUID
Segmentové registre
Segmenty sa používali v 16-bitových aplikáciách. Pamäť bola rozdelená na segmenty o veľkostiach 64 KB:
CS
- segment kóduDS
- dátový segmentES
- extra segmentFS
- ďalšie extra segmentGS
- ďalšie extra segmentSS
- segment zásobníka (stack)
Ak aplikácia potrebovala viac pamäte, musela si segmenty prepínať. Vo
32-bitových aplikáciách sú segmenty úplne zbytočné, pretože aplikácia
môže pristupovať až k 4 GB pamäte. V 64-bitových aplikáciách potom boli
úplne zrušené segmenty CS
, DS
, ES
,
SS
. Zostali iba segmenty FS
a GS
,
pretože operačné systémy ich používajú na ukladanie dát pre bežiaci
vlákno (thread local storage) alebo k informáciám o výnimkách
(exceptions).
Register IP
Instruction Pointer ukazuje na práve vykonávanú inštrukciu. Hodnotu tohto
registra meníme napríklad inštrukciami skoku (JMP
,
CALL
a RET
). V 64-bitových aplikáciách môžeme
používať register RIP
k relatívnemu adresovanie.
Debug registre
Pri ladení môžeme nastavovať až 4 dátové hraničnými na premenné.
Program sa potom zastaví, keď sa premenná zmení. V registroch
DR0
, DR1
, DR2
, DR3
sú
adresy Breakpoint. Podľa stavového registra DR6
sa určí, ktorý
z tých 4 Breakpoint bol vyvolaný. V kontrolnom registra DR7
sa
nastavuje, ktoré hraničnými sú aktívne, či sú pre zápis alebo pre
čítanie a koľko bajtov premenná zaberá.
Kontrolné registre
Registre CR0
, CR2
, CR3
,
CR4
, CR8
používa operačný systém pri
virtualizácii, stránkovanie, nastavenie zabezpečenia alebo prepínanie
rôznych režimov procesora. Ich význam je nad rámec tejto lekcie.
V budúcej lekcii, Funkcie v MASM , budeme vytvárať funkcie s parametrami a lokálnymi premennými.