Registry Registry procesoru jsou paměťová místa umístěná na čipu procesoru. Od procesoru 80386 jsou všechny registry (kromě segmentových) rozšířeny z původních 16b na 32b. Tyto „rozšířené“ registry poznáme podle toho, že jejich název je rozšířen o E (z angl. Extended). – GPR (General Purpose Registers) – registry obecného použití; dále se dělí na dvě 8b části (viz ilustrace) – (E)AX (Accumulator) – střádač – (E)BX (Base register) – používá se pro přístup k datům v datovém segmentu – (E)CX (Counter register) – čítač (řídicí proměnná pro cykly, bitové posuvy,…) – (E)DX (Data register) – zvláštní účel při ukládání částí výsledků operací MUL/DIV (celočíselného násobení a dělení) 31
0
EAX 15
0
AX 15
8 7
AH
0
AL
– indexové a ukazatelové registryu – (E)SP (Stack Pointer) – ukazatel na vrchol zásobníku (adresa SS:(E)SP) – (E)BP (Base Pointer) – využívá se pro přístup k datům uloženým na zásobníku (lokální proměnné a argumenty podprogramu) – (E)SI (Source Index) – (E)DI (Destination Index) – oba indexregistry se využívají při práci s řetězcovými instrukcemi a při výpočtu efektivní adresy – (E)IP (Instruction Pointer) – ukazuje do kódového segmentu paměti na následující vykonávanou instrukci (tedy na adresu CS:(E)IP) – segmentové registry – kódový segment - obsahuje instrukce programu – CS (Code Segment) – zásobníkový segment – SS (Stack Segment) – datové segmenty – DS (Data Segment), ES (Extra data Segment), FS a GS – (E)FLAGS – příznakový registr procesoru – IF (Interrupt enable Flag) – povoluje přerušení – OF (Overflow Flag) – značí přetečení aritmetické operace ALU – DF (Direction Flag) – udává směr průchodu při práci s řetězcovými instrukcemi – SF (Sign Flag) – nastaven při záporném výsledku – ZF (Zero Flag) – pokud je výsledek roven 0, pak je nastaven – AF (Auxiliary carry Flag) – nastavuje se podle přenosu ze spodního nibblu (nibble = polovina bytu) … – PF (odd Parity Flag) – nastaven při sudém (slov. párnom) počtu bitů v log. 1 – CF (Carry Flag) – příznak přenosu (při přenosu mimo rozsah operandu)
Segment : offset (v 16b režimu procesoru) Jedná se o přístup k adresaci paměti z dob 8086, kdy jsme pomocí 16b registrů nebyli schopni přistupovat do celého 1 MiB paměťového prostoru. Pro adresaci takového paměťového prostoru je potřeba 20b (2 20 B = 1 MiB). Adresu do paměti získáme posunutým součtem dvou 16b hodnot, které nazýváme … 19
segment
15
4
0
O
O
O
offset
– segment – představme si ho jako „okno“, přes které můžeme přistupovat jen k určité ohraničené části paměti – ukazuje na spodní okraj tohoto pomyslného „okna“ – je 16b číselná hodnota, která reprezentuje 20b hodnotu (spodní čtveřici bitů považujeme za nulovou) – segmenty mohou začínat na adrese dělitelné 16 (opět díky té pomyslné spodní čtveřici bitů) – offset – pomocí něj se dá relativně adresovat celý paměťový prostor uvnitř segmentu – můžeme si odvodit maximální velikost jednoho segmentu – při 16b offsetu to bude 216 B = 64 KiB (u 80286 a jejích následníků může být i menší, ale to je jiná pohádka...). – různými kombinacemi hodnot segmentů a offsetů můžeme adresovat stejné místo v paměti
O
Základní operandy Důležité je vědět, že znaménkové operandy jsou ukládány v tzv. doplňkovém kódu. V tomto kódu poznáme zápornou hodnotu na první pohled díky jedničce v MSB (Most Significant Bit) tedy v nevýznamnějším bitu (to jest ten nejvíce vlevo) operandu. Algoritmus pro převod mezi zápornými a kladnými hodnotami uloženými v doplňkovém kódu: – začínám zprava opisovat nuly až do doby, než narazím na první jedničku – tuto jedničku také opíši – zbylé bity operandu při zapisování neguji název
rozsah bezznaménkových operandů
byte
8b
0
…
255
word
16b
0
…
65 535
doubleword
32b
0
…
4 294 967 295
rozsah znaménkových operandů −128
…
127
−32 768
…
32 767
−2 147 483 648
…
2 147 483 647
Little-Endian – jedná se o způsob ukládání vícebytových operandů v paměti využívaný Intelem – nejméně významné byty jsou v paměti ukládány na nižší paměťové adresy – označení Big-endian a Little-endian převzato z románu Jonathana Swifta Gulliverovy cesty: nepochopené nařízení vládce rozbíjet vejce na menším konci, zatímco tradičně se vejce rozbíjelo na konci větším
Binary Coded Decade – Unpacked BCD - v jedné slabice je uložena jedna číslice v BCD kódu (hodnota 0-9) a horní nibble je nulový; tento tvar je vhodný pro převod do kódu ASCII, kdy stačí jen přičíst číslo 48 (logický součet s číslem 0x30). – Packed BCD – na bytu jsou uloženy dvě BCD číslice (rozsah až 99); procesor řady x86 s nimi neumí přímo pracovat, ale umí je převést do typického binárního čísla (viz. instrukce AAA, AAD, AAM, AAS, DAA, DAS)
Výpočet EA (Efektivní Adresy) v 16b režimu Effective Address = ( báze + index + displacement ) báze = { BX, BP } index = { SI, DI } displacement = { d8b, d16b } Nachází-li se ve výrazu BP jako báze, přistupujeme k zásobníku (pohybujeme se uvnitř SS)! V ostatních případech, kdy není segment vyjádřen explicitně (viz následující tabulka), se přistupuje k datovému segmentu (DS). Tyto segmenty jsou nutné znát pro výpočet FA (Fyzické Adresy).
Obsah segmentového registru typ odkazu
implicitní seg. reg.
explicitní seg. reg.
offset
instrukce
CS (Code Segment)
-
IP (Instruction Pointer)
zásobník
SS (Stack Segment)
-
SP (Stack Pointer)
zdrojová řetězová data
DS (Data Segment)
ano
SI (Source Index)
cílová řetězová data
ES (Extra Segment)
-
DI (Destination Index)
data pomocí BP (Base Pointeru)
SS (Stack Segment)
ano
EA (Effective Address)
standardní data
DS (Data Segment)
ano
EA (Effective Address)
Přímý a nepřímý přístup k hodnotám v paměti Jako vhodný příklad mi poslouží jedno z počítačových cvičení, kde jsme vytvářeli procedury, které dělali jednoduché operace s maticemi o rozměrech 3x3. Analogii můžeme pozorovat v ukazatelích bits 16 %include “rw.asm“ segment code ..start: prologue ...
mov mov
AX, _matrix_A AX, _A1
; návěští u těchto dvou instrukcí se překrývají, tak mají obě instrukce stejný efekt, … ; … tj. do AX ukládají offset prvního prvku matice
mov
AX, [_A2]
; přímý přístup k prvku v prvním sloupci na druhém řádku (AX = 9)
mov mov
BX, [_A] AX, [BX]
; nepřímý přístup – BX obsahuje offset návěští _A1 ; nyní přistoupíme k hodnotě, kam odkazuje návěští _A1 (AX = 5)
... ...
...
epilogue ...
segment data ...
_matrix_A _A1 _A2 _A3 _A
; jedna matice uložená v DS dw dw dw dw
5, 2, 3 9, 1, 1 3, 2, 8 _A1, _A2, _A3
Přehled základní instrukční sady x86 (nejen) přenosové instrukce (nenastavují příznaky)
MOV cíl, zdroj (MOVe) – zkopíruje obsah zdroje do cíle – množina operandů = {(reg, reg), (reg, mem), (reg, hod), (mem, hod), (mem, reg), (seg, reg16), (seg, mem16), (reg16, seg), (mem16, seg)} XCHG cíl, zdroj (eXCHanGe) – zamění obsah zdroje a cíle – množina parametrů = {(reg, reg), (reg, mem), (mem, reg)} LAHF (Load Flags into AH register) – do AH uloží některé příznaky SF
ZF
0
AF
0
PF
1
CF
SAHF (Store Flags from AH register) – z AH načte některé příznaky – párová instrukce k LAHF – využívá se pro přenos výsledků porovnávacích instrukcích při práci s FPU (není potřeba u instrukcí FCOMI a FCOMIP) LDS reg16, mem16:16 (Load far pointer into Data Segment register) – naplní DS registr a cílový registr (reg16) ukazatelem (segment : offset) z paměti adresované zdrojovým operandem LES reg16, mem16:16 (Load far pointer into Extra Segment register) – naplní ES registr a cílový registr (reg16) ukazatelem (segment : offset) z paměti adresované zdrojovým operandem LEA cíl, zdroj (Load Effective Address) – naplní cílový registr efektivní adresou zdrojového operandu – vypočítá offset operandu (pouze pro konstantní offset) – množina operandů = {(reg16, mem)} LEA MOV
DI, [BX + 5] DI, [BA + 5]
; vypočítá adresu paměti ; přečte data z paměti !
CBW (Convert Byte to Word) CWD (Convert Word to Doubleword) CDQ (Convert Doubleword to Quadword) – tato skupina instrukcí provádí znaménkové roztažení – 8b registr AL roztáhne do 16b registru AX – původní AX = xxxx xxxx snnn nnnn – roztažené AX = ssss ssss snnn nnnn – obdobně se chovají její CWD (16b AX → 32b DX:AX) a CDQ (32b EAX → 64b EDX:EAX) NOP (No OPeration) – prázdná operace – provádí instrukci XCHG AX, AX instrukce pro práci se zásobníkem (nenastavují příznaky)
Zásobník je segment v paměti, který se používá pro dočasné uložení např. obsahu registrů. Velikost ukládaných operandů vždy musí odpovídat režimu procesoru (ve 32b režimu nelze ukládat např. obsah registru AL nebo word z paměti). Vrcholem zásobníku je nejvyšší volná pozice v zásobníku. Po uložení dat na tuto pozici je vrchol zásobníku snížen o 2 nebo 4 (dle režimu procesoru). PUSH zdroj (PUSH onto stack) – na vrchol zásobníku uloží obsah zdroje – množina operandů = {reg, mem, hod, seg} POP cíl (POP a value from stack) – odebere hodnotu z vrcholu zásobníku, kterou uloží do cíle – množina operandů = {reg, mem, seg[ mimo CS! ]} PUSHA (PUSH All general-purpose registers onto stack) – uloží na zásobník (E)AX, (E)BX, (E)CX, (E)DX, původní (E)SP, (E)BP, (E)SI a (E)DI POPA (POP All general-purpose registers from stack) – ze zásobníku obnoví registry uložené pomocí instrukce PUSHA – párová instrukce k PUSHA [ mimo SP! ] PUSHF (PUSH Flags register onto stack) – uloží na vrchol zásobníku příznakový registr POPF (POP Flags register from stack) – výběr příznakového registru z vrcholu zásobníku
instrukce binární aritmetiky (nastavují příznaky !)
INC cíl (INCrement) – k cíli přičte 1 (bez kontroly přetečení → nenastavuje CF!)
OF, SF, ZF, AF a PF
ADD cíl, zdroj (integer ADD) OF, SF, ZF, AF, PF a CF – součet zdroje a cíle je uložen do cíle – pokud je zdroj menší (8b zdroj a 16b cíl), tak na něj před součtem aplikuje znaménkové roztažení (platí i pro ADC) – množina operandů = {(reg, reg), (reg, hod), (reg, mem), (mem, reg), (mem, hod)} ADC cíl, zdroj (ADd with Carry) – viz. ADD společně s připočtením obsahu CF (příznakem přetečení) k cíli DEC cíl (DECrement) – od cíle odečte 1 (nenastavuje CF!)
OF, SF, ZF, AF, PF a CF
OF, SF, ZF, AF a PF
SUB cíl, zdroj (SUBtraction) OF, SF, ZF, AF, PF a CF – odečte zdroj od cíle a výsledek uloží do cíle – pokud je zdroj menší (8b zdroj a 16b cíl), tak na něj před součtem aplikuje znaménkové roztažení (platí i pro SBB) – množina operandů = {(reg, reg), (reg, hod), (reg, mem), (mem, reg), (mem, hod)} SBB cíl, zdroj ( SuBtraction with Burrow) – viz. SUB se zohledněním hodnoty CF
OF, SF, ZF, AF, PF a CF
MUL zdroj (unsigned MULtiplication) nedef.: SF, ZF, AF a PF ! – bezznaménkové násobení zdroje se střádačem – pokud lze výsledek násobení uložit do operandu o dané velikosti, tak jsou příznaky CF i OF nastaveny na 0 (platí i pro IMUL) – výsledek je uložen do (o typu násobení určuje označení místa zdroje) – 8b → AX – 16b → DX:AX – 32b → DX:EAX – množina operandů = {reg, mem} IMUL zdroj (sIgned MULtiplication) – znaménkové násobení (viz. MUL)
nedef.: SF, ZF, AF a PF !
DIV zdroj ( unsigned DIVision) nedef.: OF, SF, ZF, AF, PF a CF ! – bezznaménkové dělení střádače se zdrojem (o poloviční délce) – pro dělení dvou stejně velkých hodnot uložíme do horní poloviny dělence (AH, DX nebo EDX) nulu – výsledky jsou uloženy do (o typu násobení určuje označení místa zdroje) – 16b/8b AX / zdroj → celočíselný podíl do AL → zbytek do AH – 32b/16b DX:AX / zdroj → celočíselný podíl do AX → zbytek do DX – 64b/32b EDX:EAX / zdroj → celočíselný podíl do EAX → zbytek do EDX – co se zbytkem – zahodit – zaokrouhlit podle něj DIV ADD CMP JB INC .NEXT:
BL AH, AH AH, BL .NEXT AL
; double reminder (zdvojnásobeni zbytku po celočíselném dělení) ; test for rounding (kontrola zaokrouhleni)
– množina operandů = {reg, mem} IDIV zdroj ( sIgned DIVision) nedef.: OF, SF, ZF, AF, PF a CF ! – znaménkové dělení (viz. DIV) – pro dělení dvou stejně velkých hodnot provedeme znaménkové roztažení střádače (instrukce CBW a její alternativy) NEG cíl (two's complement NEGation) – if (cíl == 0) {CF = 0;} else {cíl = -cíl; CF = 1;} – množina operandů = {reg, mem}
OF, SF, ZF, AF a PF
CMP cíl, zdroj (CoMPare two operands) OF, SF, ZF, AF, PF a CF – provede nedestruktivní (výsledek zahodí, jen dle něj nastavuje příznaky) odečtení zdroje od cíle a tím je porovná – pokud je zdroj menší (8b zdroj a 16b cíl), tak na něj před porovnáním aplikuje znaménkové roztažení – množina operandů = {(reg, reg), (reg, hod), (reg, mem), (mem, reg), (mem, hod)}
logické instrukce (kromě instrukce NOT nastavují příznaky!)
– pracují po jednotlivých bitech – nastavují příznaky (vyjma instrukce NOT)
příznak
akce
příznak přenosu (CF)
nuluje (0 → CF)
příznak přetečení (OF)
nuluje (0 → OF)
nulový příznak (ZF)
nastavuje dle výsledku
znaménkový příznak (SF)
nastavuje dle výsledku
bit parity (PF)
nastavuje dle výsledku
Auxiliary carry Flag (AF)
ničí (není definována)
– časté použití logických operací je při tzv. maskování – hodnota masky slouží k násilnému nastavení některých bitů operandu na log. 0 (AND) nebo log. 1 (OR) – převod znaku v AL z malého znaku na veliký (nastavení 5. bitu do log. 0) MOV AND
; AL = 01100001b ; AL = 01000001b (AND nuluje)
AL, 'a' AL, 11011111b
– převod BCD číslice do jejího ASCII znázornění MOV OR
; AL = 00000110b ; AL = 00110110b (OR nastavuje log.1)
AL, 6 AL, 00110000b
– rychlé vynulování registru XOR
; AL = 00000000b
AL, AL
– pravdivostní tabulka logických operací vstupy logických operací
výstupy jednotlivých logických operací
x1
x2
negace NOT x1
logický součin x1 AND x2
logický součet x1 OR x2
nonekvivalence x1 XOR x2
O
O
I
O
O
O
O
I
I
O
I
I
I
O
O
O
I
I
I
I
O
I
I
O
AND cíl, zdroj (AND) – logický součin – pouze logický součin dvou log. 1 dá za výsledek log. 1 – pokud je zdroj menší (8b zdroj a 16b cíl), tak na něj před součtem aplikuje znaménkové roztažení – množina operandů = {(reg, reg), (reg, hod), (reg, mem), (mem, reg), (mem, hod)}
viz. tabulka
OR cíl, zdroj (OR) – logický součet – pouze logický součet dvou log. 0 dá za výsledek log. 0 – pokud je zdroj menší (8b zdroj a 16b cíl), tak na něj před součtem aplikuje znaménkové roztažení – množina operandů = {(reg, reg), (reg, hod), (reg, mem), (mem, reg), (mem, hod)}
viz. tabulka
XOR cíl, zdroj (eXclussive OR) – nonekvivalence – výsledkem nonekvivalence je log. 1, která signalizuje přechod mezi log. stavy, tj. z log. 1 do log. 0 a naopak – nejvýstižnější je hláška, „XOR detekuje změny“ – pokud je zdroj menší (8b zdroj a 16b cíl), tak na něj před součtem aplikuje znaménkové roztažení – množina operandů = {(reg, reg), (reg, hod), (reg, mem), (mem, reg), (mem, hod)}
viz. tabulka
NOT cíl (Negation of One complemenT) – logická negace cílového operandu – záměna log. stavu, tj. všechny log. 0 jsou nastaveny do log. 1 a naopak – množina operandů = {reg, mem} TEST cíl, zdroj (TEST of and) – nedestruktivní logický součin (nastaví příznaky dle výsledku, ale ten neukládá) tzn., nemění cílový operand – téměř shodný s instrukcí AND
nemění příznaky
viz. AND
instrukce posuvů a rotací
SHL cil, [počet] (SHift Left) – posune bity doleva o hodnotu operandu počet; zprava doplňuje nulami; bit s nejvyšší vahou (dále MSB) přesune do CF – pokud není uveden operand počet, bere se v úvahu hodnota uložená v registru CL CF – použití – rychlé násobení mocninou 2 počet – packing data – spojení dvou nibblů (4bitů) – množina operandů = {(reg, -), (reg, hod), ( mem, -), (mem, hod)}
CF
SHR cil, [počet] (SHift Right) – posune bity doprava o hodnotu operandu počet; zleva doplňuje nulami; bit s nejnižší vahou (dále LSB) přesune do CF – pokud není uveden operand počet, bere se v úvahu hodnota uložená v registru CL – použití CF – rychlé dělení mocninou 2 počet – packing data – spojení dvou nibblů – množina operandů = {(reg, -), (reg, hod), ( mem, -), (mem, hod)}
CF
SAR cil, [počet] (Shift Arithmetic Right) – posune bity doprava o hodnotu operandu počet; zleva doplňuje hodnotou z MSB; LSB přesouvá do CF – pokud není uveden operand počet, bere se v úvahu hodnota uložená v registru CL – použití – rychlé dělení mocninou 2 počet pro znaménková čísla – množina operandů = {(reg, -), (reg, hod), ( mem, -), (mem, hod)}
CF
ROL cil, počet (ROtate Left) – rotuje bity v cíli zprava doleva; v každém kroku uloží MSB do CF – množina operandů = {(reg, -), (reg, hod), ( mem, -), (mem, hod)}
CF CF
ROR cil, počet (ROtate Right) – rotuje bity v cíli zleva doprava; v každém kroku uloží LSB do CF – množina operandů = {(reg, -), (reg, hod), ( mem, -), (mem, hod)} RCL cil, počet (Rotate trough Carry flag Left) – rotuje bity v cíli zprava doleva; CF se používá jako 9. (resp. 17.) bit cíle, čili MSB – množina operandů = {(reg, -), (reg, hod), ( mem, -), (mem, hod)} RCR cil, počet (Rotate trough Carry flag Right) – rotuje bity v cíli zleva doprava; CF se k cíli „přimkne“ zprava (bere se jako LSB) – množina operandů = {(reg, -), (reg, hod), ( mem, -), (mem, hod)}
CF
CF CF
CF CF
CF CF
instrukce pro ovládání příznaků
STC (SeT Carry flag) – nastavuje příznak přenosu do log. 1 CLC – nuluje příznak přenosu
(CLear Carry flag)
CMC – negace příznaku přenosu
(CompleMent Carry flag)
STD (SeT Direction flag) – nastaví příznak směru průchodu řetězcových dat do log. 1 – řetězce se procházejí od konce k počátku CLD (CLear Direction flag) – nuluje příznak směru průchodu řetězcových dat STI (SeT Interrupt enable flag) – nastaví příznak povolení přerušení do log. 1 – povoluje maskovatelná přerušení CLI (CLear Interrupt enable flag) – nuluje příznak povolení přerušení
I → CF
O → CF
-CF → CF
I → DF
O → DF
I → IF
O → IF
instrukce nepodmíněných skoků
– intra-segmentové (v rámci segmentu) skoky (E)IP = (E)IP + relative_displacement – short jump (krátký skok) – 2B instrukce (1B displacement) umožňuje skočit na cíl v rozsahu -128 … +127 bytů od místa, které následuje za příkazem – near jump (blízký skok) – 3B instrukce (2B displacement), která dovoluje skákat v rámci ±32 KiB od následující instrukce v rámci běžného kódového segmentu (pokryje celou oblast segmentu) – inter-segmentové (mezi-segmentové) skoky CS:(E)IP = target_segment:target_offset – far jump (daleký skok) – 5B instrukce, která dovoluje skočit na kterékoli místo v adresní prostoru JMP [SHORT] návěští krátký relativní skok JMP návěští blízký relativní skok JMP reg16/mem16 blízký nepřímý absolutní skok JMP seg:off daleký absolutní skok JMP mem16:16 daleký nepřímý absolutní skok instrukce podmíněných skoků
– některé instrukce (logické instrukce, aritmetické instrukce a CMP) nastavují příznaky – podmínka je vyhodnocena podle aktuálního stavu příznaků (SF, ZF, CF, PF a OF) – při splnění – skoč na návěští (dopředu nebo dozadu) tzn., změní (E)IP na adresu instrukce, kam se má skočit – při nesplnění – procesor pokračuje ve vykonávání instrukcí v pořadí (standardní inkrementace (E)IP) – odvození jmen (jejich jednotlivých písmen) podmíněných skoků po porovnání (nemusíme znát stav příznaků) –J (Jump) – instrukce skoku –N (Not) – nepovinná negace podmínky – A/B nebo G/L (Above/Below nebo Greater/Less) bez znaménkové hodnoty
znaménkové hodnoty
je větší než
význam podmínky
A
G
je menší než
B
L
([or] Equal) – rovnost porovnaných hodnot (umožňuje společně s N dvojí tvar instrukce, např. JNA a JBE jsou shodné instrukce) – podmíněné skoky podle stavu příznaků –E
J[N]C (Jump if [Not] Carry flag) – skok podmíněný stavem příznaku přenosu J[N]O (Jump if [Not] Overflow flag) – skok podmíněný stavem příznaku přetečení J[N]S (Jump if [Not] Sign flag) – skok podmíněný stavem příznaku znaménka (MSB) J[N]Z (Jump if [Not] Zero flag) – skok podmíněný hodnotou nulového příznaku JCXZ (Jump if CX is Zero) JECXZ (Jump if ECX is Zero) – skok podmíněný nulovou hodnotou registru CX nebo ECX (jedná se o dvě různé instrukce) – používají se pro smyčky instrukce smyček
LOOP návěští (LOOP) – kombinace instrukcí DEC CX a JNZ (snižuje obsah registru (E)CX a dokud není nulový, skáče na návěští) – při snížení (E)CX na 0 pokračuje ve vykonávání následujících instrukcí LOOPE návěští (LOOP while Equal) LOOPZ návěští (LOOP while Zero) – instrukce LOOP rozšířená o testování nulového příznaku – skáče na návěští při splnění podmínky (((E)CX != 0) && (ZF = 1)) LOOPNE návěští (LOOP while Not Equal) LOOPNZ návěští (LOOP while Not Zero) – analogie k předchozí instrukci – skáče na návěští při splnění podmínky (((E)CX != 0) && (ZF = 0))
předpony řetězcových instrukcí
REP (REPeating string operation prefix) – pokračuj s následující instrukcí, dokud je (E)CX nenulové a postupně snižuj jeho hodnotu – nedá se na rozdíl od následujících předpon použít s instrukcemi CMPS, SCAS a jejich ekvivalenty (nedali by se využít jejich porovnávací potenciál) REPE/REPZ (REPeating string operation prefix while Equal/Zero) – podobně jako REP, který je ukončen i při snulovaném ZF REPNE/REPNZ (REPeating string operation prefix while Not Equal/Zero) – podobně jako REP, který je ukončen i při ZF nastaveném do log. 1 řetězcové instrukce
– pracují s řetězcovými daty uloženými v paměti – zdrojová data se nachází na adrese DS:(E)SI, ale jejich segmentový registr lze v NASM změnit segmentovým prefixem – cílová data se vždy nachází na adrese ES:(E)DI – směr průchodu řetězcových dat je závislý na hodnotě příznaku DF (Direction Flag) – při DF = O se indexovací registry (Source Index a Destination Index) zvyšují – při DF = I se indexovací registry snižují – NASM nepodporuje instrukce bez určení velikosti dat (např. MOVS, CMPS, …) – indexovací registry se mění o velikost danou dle posledního písmene instrukce (B/W/D … 1/2/4) – do registru (E)CX se před řetězcovou instrukcí musí uložit hodnota, která určuje počet hodnot v řetězci dat MOVS (MOVe data from String to string) MOVSB/MOVSW/MOVSD – kopíruje zdrojová data na adresu cílových dat CMPS (CoMPare String operands) CMPSB/CMPSW/CMPSD – porovnává řetězcová data (provádí nedestruktivní rozdíl zdroj – cíl) a podle výsledků nastavuje příznaky – nelze použít s předponou REP!
OF, SF, ZF, AF, PF a CF
SCAS (SCAn String) SCASB/SCASW/SCASD – porovnává hodnotu cílových dat s hodnotou uloženou ve střádači (AL/AX/EAX) – nelze použít s předponou REP!
OF, SF, ZF, AF, PF a CF
LODS (LOaD String) LODSB/LODSW/LODSD – načte hodnotu z adresy zdrojových dat do střádače STOS (STOre String) STOSB/STOSW/STOSD – uloží obsah střádače na adresu cílových dat INS (INput from port to String) INSB/INSW/INSD – přenese hodnotu z I/O portu adresovaného obsahem registru DX na adresu cílových dat OUTS (OUTput String to port) OUTSB/OUTSW/OUTSD – přenese hodnotu z adresy zdrojových dat na I/O port adresovaný obsahem registru DX
Překladač NASM symbolické instrukce
[návěští: ] [symbolická_instrukce ] [; poznámka] návěští
– identifikátor reprezentující adresu první slabiky instrukce – NASM je Case Sensitive (rozlišuje v návěštích malá a velká písmena) – může obsahovat – číslice, malá a velká písmena anglické abecedy – následující znaky: '_', '$', '#', '@', '~', '.' a '?' zápis paměťových operandů mov mov mov mov mov mov mov
AX, alpha AX, [alpha] [beta], AX AX, [ES:BX+10] [BX], byte 5 [BX], word 5 [BX], dword 5
; do registru AX uloží offset (efektivní adresu) symbolu/jména alpha ; do registru AX uloží hodnotu (16b) operandu v paměti na adrese DS:alpha ; do slova v paměti s adresou DS:beta uloží obsah regisru AX ; do registru AX uloží hodnotu operandu uloženého v paměti na EA [ES:BX+10] ; do bytu v paměti na adrese uložené v registru BX uloží hodnotu 5 ; do 16b v paměti na adrese uložené v registru BX uloží hodnotu 5 ; do 32b v paměti na adrese uložené v registru BX uloží hodnotu 5
možné zápisy prefixů pro změnu segmentových registrů mov ES
AX, [ES:BX+SI] mov
ES mov
; zápis požadovaného segmentového registru uvnitř závorek uvozujících adresní výraz
AX, [BX+10] ; na počátku řádku před identifikátorem instrukce
; na samostatném řádku před instrukcí AX, [BX+10]
direktivy
– definice inicializovaných dat – db, dw, dd, dq, dt shrtint byte string word off_a
db db db dw dw
-43 01011101b, 0xA2, 0A2h 'ahojky', 13, 10, '$' 37654 alpha
; řetězec s odřádkováním a ukončovacím znakem (9B v paměti)
seg_a
dw
seg alpha
; segment symbolu alpha
double
dd
12345678h
; offset symbolu alpha
; definice proměnných pro FPU single double extended
dd dq dt
1.02 2.71e15 1.56e-15
– definice neinicializovaných dat – resb, resw, resd, resq, rest resb 10 resw
– definice konstant – musí mít návěští a může být v programu předefinována ! – equ dve
equ
2
; délka řetězce uloženého v paměti (znak $ ukazuje na aktuální adresu, tj. adresu návěští str_len) str str_len
db 'ahojky' equ $ - str
– opakování definic dat – times zerobuf
times 100 db 0
; do stovky po sobě jdoucích bytů uloží nuly
– specifikace režimu procesoru – bits hodnota – operand hodnota může nabývat 16 či 32 (udává počet bitů) – definice segmentů (section/segment) – segment jméno ≡ section jméno – pro jména segmentů platí stejná omezení, jako pro jména direktiv, různé výstupní formáty a spolupracující programy však mohou vyžadovat konkrétní jména ! – definice počátku programu – ..start:
operátory adresových výrazů
– pouze pro ordinální typy operandů! – časté použití u maker – binární operátory seřazené vzestupně podle priority operace
operátor
příklad použití
bitový logický součet
|
mov AL, 0x32|5
; AL = 0x37
bitová nonekvivalence
^
mov AL, 0x15^9
; AL = 0x1C
bitový logický součin
&
mov AL, 0x9F&15
; AL = 0x0F
bitový posun vlevo
<<
mov AL, 5<<3
; AL = 0x28
bitový posun vpravo
>>
mov AL, 0x16>>3
; AL = 2
součet
+
rozdíl
-
součin
*
bez znaménkové dělení
/ //
znaménkové dělení zbytek po bez znaménkovém dělení zbytek po znaménkovém dělení
% %%
unární operátory operátor
účinek operátoru
+
nedělá nic ☺
-
negace operandu (dvojkový komplement)
~
jedničkový komplement operandu
seg
vrací segment použitého symbolu (mov AX, seg alpha)
odkazy
–$ – zastupuje adresu prvního bytu symbolické instrukce uvedené na stejném řádku – $$ – zastupuje začátek příslušné sekce/segmentu ; příklad využití odkazu $ str str.len
db db
'Ahojky', 13, 10, '$' $ - str
; na adrese str.len je na 1 bytu uložena délka řetězce str tedy (snad) 9
Makra jednořádková makra
– makro %define – %define mess mess
název_makra[(parametry)] tělo_makra
%define CRLF 13,10 db 'hello', CRLF, '$' db 'hello', 13,10, '$'
; definice makra bez parametrů ; volání makra ve zdrojovém kódu ; překlad
%define param(b,i,d) [b+i+d] mov AX, param(BX,SI,10) mov AX, [BX+SI+10]
; definice makra s parametry ; volání makra s parametry ; překlad
– makro %assign – %assign
jméno_proměnné numerická_hodnota
%assign i 5 %assign i i+1
; definice ; příklad volání (viz. makro %rep)
– makro pro vložení zdrojového kódu z externího souboru – %include “jméno_souboru“ %include “rw.asm“ víceřádková makra
– základní definice víceřádkového makra %macro jméno_makra počet_parametrů tělo_makra
%endmacro – volání víceřádkového makra jméno_makra parametry
; příklad makra s jedním parametrem %macro prologue 1 push BP mov BP, SP sub SP, %1 %endmacro
; definice víceřádkového makra s jedním parametrem
prologue 10
; volání makra s parametrem 10
push mov sub
; překlad makra
BP BP, SP SP, 10
; příklad víceřádkového makra bez parametrů
; použití prvního zadaného parametru v těle makra
%macro retz 0 jnz %%skip ret %%skip %endmacro
; definice víceřádkového makra bez parametrů
retz ... retz
; volání makra
jnz ..@0001 ret ..@0001: ... jnz ..@0002 ret ..@0002:
; překlad ; symbol ..@xxxx: je formální – ve skutečnosti je nahrazen konkrétní hodnotou
; lokální návěští pro každé volání makra
– makro pro opakování (využívá se např. u rotujících parametrů) %rep počet_opakování tělo_makra
%endrep ; definice makra pro opakování %assign i 0 %rep 3 dw i %assign i i+1 %endrep dw 0 dw 1 dw 2
; překlad
– makra s neznámým počtem parametrů – Greedy parametry %macro jméno_makra počet_parametrů+ tělo_makra
%endmacro ; definice makra vyuzivajiciho greedy parametry %macro write_to_file 2+ jmp %%endstr %%str: db %2 %%endstr: mov DX, %%str mov CX, %%endstr - %%str mov BX, %1 mov AH, 0x40 int 0x21 %endmacro write_to_file filehandle, “hello“,13,10
; definice řetězce předaného v parametru ; počátek řetězce ; délka řetězce ; filehandle (popisovač souboru) ; volání služby DOSu ; volání greedy makra
– Rotující parametry ; příklad definice makra pro uložení GP registrů na zásobník realizovaný pomocí rotujících parametrů %macro multipush 1 - * ; * zastupuje libovolný (předem nedefinovaný) počet parametrů %rep %0 ; %0 vrací skutečný počet parametrů zadaných při volání makra push %1 %rotate 1 %endrep %endmacro multipush SI,DI,DS,ES
; definice párového makra k předchozímu %macro multipop 1 - * %rep %0 %rotate -1 pop %1 %endrep %endmacro multipop SI,DI,DS,ES
; rotuje parametry o jedno místo doleva
; volání makra
; rotuje parametry o jedno místo doprava
; volání makra (obnoví parametry ve správném pořadí k multipush viz víše)
– default parametry makra %macro jméno_makra min - max [(max-min) předdefinovaných parametrů] tělo_makra
%endmacro ; definice makra s default parametrem (znak nula) %macro errmess 0 – 1 '0' write_to_file 2, %1,10,13,'$' mov AH, 0x4C int 0x21 %endmacro errmess 1
; volání makra s parametrem
errmess
; volání makra bez parametru – použije se default parametr
– spojování parametrů makra s okolním textem xxx%1 %1yyyy xxx%1yyyy
– v případě nejednoznačnosti se číslo parametru vkládá do složených závorek %13 %{1}3
; parametr číslo 13 ; spojení prvního parametru s číslicí 3
– podmínkové kódy jako parametry makra... ; definice makra s podmínkovým kódem %macro retcc 1 j%-1 %%skip ret %%skip: %endmacro retcc a jna ..@0001 ret ..@0001:
; %+1 označuje podmínkový kód a %-1 jeho negaci
; volání makra ; překlad
– podmíněný překlad částí makra – logické operátory považují nenulové hodnoty za pravdivé – využití při psaní univerzálních ovladačů na více verzí určitého HW %if <podmínka_1> ; zdrojový kód, který je přeložen při splnění podmínky_1 %elif <podmínka_2> ; zdrojový kód, který je přeložen při nesplnění podmínky_1 při současném splnění podmínky_2 %else ; zdrojový kód, který je přeložen při nesplnění všech předchozích podmínek %endif operátor(y)
význam
=
==
rovnost
<>
!=
nerovnost
<
je menší než
>
je větší než
<=
je menší nebo rovno
>=
je větší nebo rovno
&&
logický součin
||
logický součet
^^
nonekvivalence
– makro pro definování a instance struktur ; definice struktury struc mytype n1: resw 1 n2: resb 1 n3: resb 10 endstruc
; mytype = 0 ; n1 = 0 ; n2 = 2 ; n3 = 3 ; mytype_size = 13
; vytvoření instance struktury mystruc istruc mytype at n1, dw -65 at n2, db 9 at n3, db 'hello‘,13,10,’$’ iend
; použití instance struktury mov ah, [mystruc+n2] mov dx, mystruc+n3
Programové moduly – předávání řízení ; modul1.asm ; ---------------
; modul2.asm ; ---------------
bits 16 extern PROC1 ; import.sym. segment CSEG ... call (seg PROC1):PROC1 ; vzdálené... ... ; ...volání proc.
bits 16 global segment ... PROC1: ... retf ...
PROC1 aaa
; definice globální (sdílené) proměnné ; nesmějí se krýt jména návěští uvnitř modulů ; globální návěští ; vzdálený návrat místo blízkého
– předávání dat ; modul1.asm ; ----------------
; modul2.asm ; ----------------
bits 16 extern message_1 global alpha segment CSEG ... mov AX, seg message_1 mov DS, AX mov DX, message_1 mov AH, 9 int 0x21 segment DATA alpha: dw 12345
bits 16 extern alpha global message_1 segment code_aaa ... mov AX, seg alpha mov ES, AX mov AX, [ES:alpha] ... segment data_aaa message_1: db 'hello',10,13,'$'
Služby BIOSu – přehled služeb BIOSu přerušení
služba BIOSu
0x10
Služby obrazovky
0x11
Seznam vybavení
0x12
Velikost volné paměti
0x13
Diskový I/O
0x14
I/O sériových portů
0x15
Rozšířené služby AT
0x16
I/O klávesnice
0x17
I/O tiskárny
0x18
ROM-BASIC
0x19
Zavaděč OS
0x1A
Časovač a RTC
0x1B
Ctrl–Break
0x1C
Uživatelský časovač
– některé služby obrazovky (INT 0x10) stav AH
Služba obrazovky
stav AH
Služba obrazovky
0x00
Nastavení režimu obrazovky
0x0A
Výpis znaku na pozici kurzoru
0x01
Nastavení velikosti/tvaru kurzoru
0x0B
Výběr barevné palety/barvy pozadí
0x02
Nastavení pozice kurzoru
0x0C
Vykreslení grafického bodu
0x03
Zjištění pozice a velikosti kurzoru
0x0D
Načtení grafického bodu
0x04
Zjištění pozice světelného pera
0x0E
Výpis znaku na aktivní stránku obrazovky
0x05
Výběr aktivní stránky obrazovky
0x0F
Zjištění režimu obrazovky
0x06
Posun okna nahoru (nebo smazání okna)
0x10
<EGA> Funkce palety
0x07
Posun okna dolů
0x11
<EGA> Funkce znakového generátoru
0x08
Zjištění znaku/atributu na pozici kurzoru
0x12
<EGA> Speciální funkce
0x09
Výpis znaku/atributu na pozici kurzoru
0x13
Výpis řetězce (jen pro <EGA>)
– popis některých video služeb Zjištění režimu obrazovky
AH = 0x0F vstup
-
výstup
vstup
AH
počet znakových sloupců na obrazovce
BH
číslo právě aktivní stránky
BH
číslo právě aktivní stránky
DH
řádek (nastavit na 25 pro ukrytí kurzoru)
DL
sloupec
Výpis znaku/atributu na pozici kurzoru
AH = 0x09 vstup
výstup
aktuální režim
Nastavení pozice kurzoru
AH = 0x02
výstup
AL
-
BH
číslo právě aktivní stránky
AL
znak pro vypsání
BL
atribut zobrazení
CX
počet opakování
Služby DOSu – přehled služeb DOSu stav AH
služba DOSu
stav AH
služba DOSu
stav AH
služba DOSu
0x00
Ukončení procesu
0x24
Nastavení přímé adresy bloku v souboru
0x44
Ovládání vstupně/výstupních souborů
0x01
Vstup z klávesnice s echem
0x25
Nastavení vektoru přerušení
0x45
Duplikuj handle souboru
0x02
Zobrazení znaku
0x26
Vytvoření prefixu prog. segmentu (PSP)
0x46
Přesměrování handlu souboru
0x03
Vstup z AUX
0x27
Čtení bloku ze souboru
0x47
Zjištění aktuálního adresáře
0x04
Vystup na AUX
0x28
Zápis bloku do souboru
0x48
Alokace paměti
0x05
Výstup na tiskárnu
0x29
Rozbor jména souboru
0x49
Uvolnění paměti
0x06
Vstup/výstup přes konzoli
0x2A
Zjištění systémového data
0x4A
Realokace bloku paměti
0x07
Nefiltrovaný vstup bez echa
0x2B
Nastavení systémového data
0x4B
Spuštění programu
0x08
Vstup bez echa
0x2C
Zjištění systémového času
0x4C
Ukonči program - EXIT
0x09
Zobrazení řetězce
0x2D
Nastavení systémového času
0x4D
Zjisti výstup. kód programu - WAIT
0x0A
Bufferovaný vstup řetězce
0x2E
Nastavení přepínače verifikace
0x4E
Hledání prvního souboru
0x0B
Kontrola vstupního stavu
0x2F
Zjištění přenosové adresy disku (DTA)
0x4F
Hledání dalšího souboru
0x0C
Vstup s vymazáním bufferu
0x30
Zjištění verze systému
0x54
Zjištění stavu přepínače verifikace
0x0D
Reset disku
0x31
Rezidentní proces
0x56
Přejmenování souboru
0x0E
Výběr disku
0x32
Informace o disku
0x57
Datum a čas souboru
0x0F
Otevření souboru (FileControlBlock)
0x33
Zjištění režimu testování Break
0x58
Alokační strategie
0x10
Zavření souboru (FCB)
0x34
Aktivita DOSu
0x59
Rozšířený chybový kód
0x11
Hledání prvního souboru (FCB)
0x35
Zjištění vektoru přerušení
0x5A
Vytvoření dočasného souboru
0x12
Hledání dalšího souboru (FCB)
0x36
Zjištění volného místa na disku
0x5B
Vytvoření nového souboru
0x13
Zruš soubor (FCB)
0x37
Znak přepínače (glob. proměnná DOSu)
0x5C
Uzamčení souboru
0x14
Sekvenční čtení (FCB)
0x38
Informace o národním prostředí
0x5D
Síťové/různé akce
0x15
Sekvenční zápis (FCB)
0x39
Vytvoření adresáře
0x5E
Síťové funkce
0x16
Založení souboru (FCB)
0x3A
Zrušení adresáře
0x5F
Přesměrování v síti
0x17
Přejmenování souboru (FCB)
0x3B
Výběr adresáře
0x60
Standardizace jména souboru
0x19
Zjištění aktuálního disku
0x3C
Založení souboru
0x62
Čtení adresy PSP
0x1A
Nastavení Disk Transport Address (DTA)
0x3D
Otevření souboru
0x65
Rozšířené info o národním prostředí
0x1B
FAT info aktuálního disku
0x3E
Uzavření souboru
0x66
Globální kódová stránka
0x1C
FAT info disku
0x3F
Čtení ze souboru
0x67
Nastavení počtu souborových handlů
0x21
Info o aktuálním disku
0x40
Zápis do souboru
0x68
Zápis souboru (z bufferu) na disk
0x21
Čtení záznamu ze souboru
0x41
Odstranění souboru
0x69
Sériové číslo disku (svazku)
0x22
Zápis záznamu do souboru
0x42
Nastavení pozice v souboru
0x23
Zjištění velikosti souboru (FCB)
0x43
Atributy souboru
– popis některých služeb DOSu AH = 0x01
Vstup z klávesnice (čeká na) s echem
AH = 0x08
Vstup z klávesnice (čeká na) bez echa
vstup
-
vstup
-
výstup
AL znak načtený ze standardního vstup. zařízení
výstup
AL znak načtený ze standardního vstup. zařízení
Znak je vypsán na standardní výstupní zařízení. Rozšířené ASCII kódy potřebují dvě zavolání této služby – první zavolání navrátí AL = 0.
Rozšířené ASCII kódy potřebují dvě zavolání této služby.
AH = 0x02
Zobrazení znaku (na standardní výstupní zařízení)
AH = 0x09
vstup
DL znak pro zobrazení
vstup
výstup
-
výstup
Zobrazení řetězce (na standardní výstupní zařízení) DS:DX -
adresa řetězce zakončeného znakem '$'
FPU (Floating Point Unit) Jednotka vykonávající instrukce nad čísly s pohyblivou desetinnou čárkou. Bývá označována jako koprocesor. – instrukce určené pro FPU jsou odlišeny tzv. escape prefixem (27d = 0x1B) – pro práci s operandy má FPU vlastní relativně adresovaný zásobník, který je tvořen osmi 80b registry – vrchol zásobníku je ST(0) – uložení hodnoty na zásobník – všechny hodnoty na zásobníku jsou „posunuty“ dále od jeho vrcholu, kam je uložen nově načtený operand – ve skutečnosti jsou jen přejmenovány jednotlivé registry (z ST(0) se stane ST(1) atd.) – Stack Overflow – chyba programátora, při níž dochází k přepsání operandu na dně zásobníku (v ST(7)) – odebrání hodnoty z vrcholu zásobníku – všechny operandy uložené na zásobníku jsou posunuty blíže k vrcholu – Stack Underflow – chyba při pokusu o odebrání operandu z vrcholu prázdného zásobníku – uložení hodnoty na zásobník pomocí instrukce FLD1 a následné odebrání vrcholu zásobníku pomocí instrukce FADDP ST(1), ST(0) je znázorněno na následující ilustraci R7
2,40e12
ST(3)
2,40e12
ST(4)
2,40e12
ST(3)
R6
-1,23e-4
ST(2)
-1,23e-4
ST(3)
-1,23e-4
ST(2)
R5
0
ST(1)
0
ST(2)
0
ST(1)
R4
3,14
ST(0)
3,14
ST(1)
4,14
ST(0)
R3
ST(7)
1
ST(0)
1
ST(7)
R2
ST(6)
ST(7)
ST(6)
R1
ST(5)
ST(6)
ST(5)
R0
ST(4)
ST(5)
ST(4)
– další registry FPU – Tag Register – 16b registr známek – uchovává informace o hodnotách uložených v jednotlivých registrech zásobníku FPU Tag
v daném registru RX je
OO
Platné nenulové číslo
OI
Nula
IO
Nekonečno, denormalizované číslo nebo neplatná hodnota
II
Volno (jedná se o prázdný registr)
– Status Register – stavový registr 15
14
B
C3
13
11
TOP
10
9
8
7
6
5
4
3
2
1
0
C2
C1
C0
ES
SF
PE
UE
OE
ZE
DE
IE
– B (Busy) – indikuje činnost FPU – C0 – C3 (Condition code) – podmínkový kód – TOP – aktuální vrchol zásobníku (při TOP = IOO je ST(0) na registru R4) – ES (Error Summary status) – stav žádosti o výjimky – SF (Stack Fault) – chyba zásobníku – PE (Precision Exception) – výjimka přesnosti (je možné maskovat pomocí bitu PM v Control Registru) – UE (Underflow Exception) – podtečení zásobníku – OE (Overflow Exception) – přetečení zásobníku – ZE (Zero divide Exception) – dělení nulou – DE (Denormalized operand Exception) – denormalizovaný operand – IE (Invalid operation Exception) – neplatná operace – Control Register – řídicí registr 15
-
-
13
12
-
IR
11
10
9
RC
8
PC
7
6
5
4
3
2
1
0
-
-
PM
UM
OM
ZM
DM
IM
– IC (Infinity Control) – řízení nekonečna – pouze pro koprocesory 8087 a 80287 – RC (Rounding Control) – řízení zaokrouhlení RC
zaokrouhlení k
-1.8
-1.2
1.2
1.8
OO
nejbližšímu číslu
-2
-1
1
2
OI
−∞
-2
-2
1
1
IO
+∞
-1
-1
2
2
II
0
-1
-1
1
1
– PC (Precision Control) – řízení přesnosti udává počet bitů pro uložení platné mantisy – bity maskování žádostí o jednotlivé výjimky (viz Status Register) – FPU Instruction Pointer – 48b vzdálený ukazatel na instrukci pro FPU – FPU Operand Pointer – 48b vzdálený ukazatel slouží pro načítání/zápis operandů z/do hlavní paměti
– datové typy podporované FPU ( • - mohou být použity přímo jako paměťové operandy výpočetních instrukcí) – FPU vnitřně pracuje s datovým typem extended (tomu odpovídá i velikost zásobníkových registrů) word / short celočíselné operandy BCD reálné operandy
16b
−32 768
…
32 767
•
short / int
32b
−2 ∙ 10 9
…
2 ∙ 10 9
•
long
64b
−9 ∙ 10 18
…
9 ∙ 10 18
packed decimal
80b
−1 ∙ 10 18
…
1 ∙ 10 18
single / float
32b
23b mantisa, 8b exponent
•
double
64b
52b mantisa, 11b exponent
•
temporary / extended
80b
64b mantisa, 15b exponent
– formáty plovoucí řádové čárky dle normy IEEE 754
(-1)Sign ∙ 1, Fraction ∙ 2Exponent - bias – formát si popíšeme na operandu s jednoduchou přesností označovaném (single precision) známém jako float z programovacích jazyků jako C, C++, Java, … 31
Sign
30
23 22
0
Exponent
Fraction
– Sign – znaménkový bit – udává znaménko operandu – pro záporné operandy je tento bit v log. 1 – Exponent – uložen v aditivním kódu (kódu lichého posunutí nuly), což umožňuje rychlé porovnání dvou operandů
– nula je kódována jako 0x7F = OIII IIIIb (bývá označována jako bias) – Fraction – mantisa – je vždy kladná – uložena v přímém kódu – od MSB mají jednotlivé bity váhu – 2 -1, 2 -2, 2 -3, … – hodnoty, jakých může float nabývat – denormalizovaný tvar znamená, že operand je příliš malý a operace s ním ztrácí přesnost exponent
mantisa
hodnota
0
0
0
0
nenulová
denormalizovaný tvar
( 0 ; 0xFF )
libovolná
normální tvar
0xFF
0
±∞
0xFF
nenulová
NaN (Not a Number)
– aritmetické operace s formáty IEEE 754 – součet a rozdíl X ± Y = (FractionX · 2 ExponentX - ExponentY ± FractionY) · 2 ExponentY – vyrovnám exponenty (mantisu menší hodnoty posouvám doprava a zvyšuji její exponent) – provedu součet/rozdíl mantis – normalizuji výsledek do základního tvaru – součin a podíl X · Y = (FractionX · FractionY) · 2 ExponentX + ExponentY – provedu součet/rozdíl exponentů – provedu součin/podíl mantis – normalizuji do základního tvaru
Přehled instrukční sady x87 přenosové instrukce FPU
– –
všechny instrukce, které ukládají nějakou hodnotu na vrchol zásobníku, způsobí posunutí všech hodnot na zásobníku hodnoty příznaků C0, C2 a C3 nejsou definovány
FLD zdroj (FPU real LoaD) – na vrchol zásobníku uloží číslo s plovoucí desetinou čárkou načtené z paměti nebo zásobníku – množina operandů = {mem32real, mem64real, mem80real, ST(i)}
I → C1 (Stack Overflow)
FILD zdroj (FPU Integer LoaD) – na vrchol zásobníku uloží celé číslo načtené z paměti – množina operandů = {mem16int, mem32int, mem64int}
I → C1 (Stack Overflow)
FLD1 (FPU LoaD 1) – na vrchol zásobníku uloží jedničku
I → C1 (Stack Overflow)
FLDPI (FPU LoaD PI) – na vrchol zásobníku uloží Ludolfovo číslo
I → C1 (Stack Overflow)
FLDZ (FPU LoaD Zero) – na vrchol zásobníku uloží nulu
I → C1 (Stack Overflow)
FST cíl (FPU real STore) – na cílovou pozici uloží číslo s plovoucí desetinou čárkou – množina operandů = {mem32real, mem64real, ST(i)}
O → C1 (Stack Underflow)
FIST cíl (FPU Integer STore) – na cílovou pozici uloží celé číslo – množina operandů = {mem16int, mem32int}
O → C1 (Stack Underflow)
FSTP – – –
cíl (FPU real STore and Pop) na cílovou pozici uloží číslo s plovoucí desetinou čárkou a odebere vrchol zásobníku párová instrukce k FLD množina operandů = {mem32real, mem64real, mem80real, ST(i)}
FISTP cíl (FPU Integer STore and Pop) – na cílovou pozici uloží celé číslo a odebere vrchol zásobníku – párová instrukce k FILD – množina operandů = {mem16int, mem32int} FXCH cíl (FPU eXCHange register contents) – zamění vrchol zásobníku s jinou hodnotou uloženou v zásobníku – množina operandů = {ST(i)}
O → C1 (Stack Underflow)
O → C1 (Stack Underflow)
O → C1
aritmetické instrukce FPU
–
hodnoty příznaků C0, C2 a C3 nejsou definovány FADD – – –
[cíl,] zdroj (FPU ADD) výsledek součtu cíle se zdrojem se uloží místo cíle implicitní cíl je vrchol zásobníku množina operandů = {(-,mem32real ), (-,mem64real ), (ST(0),ST(i)), (ST(i),ST(0))}
O → C1 (Stack Underflow)
FADDP cíl, zdroj (FPU ADD and Pop) – výsledek součtu cíle a zdroje se uloží do cíle a odebere vrchol zásobníku – množina operandů = {(ST(i), ST(0))}
O → C1 (Stack Underflow)
FIADD zdroj (FPU Integer ADD) – výsledek součtu cíle s hodnotou z vrcholu zásobníku se uloží na vrchol zásobníku – množina operandů = {(-,mem16int ), (-,mem32int )}
O → C1 (Stack Underflow)
FSUB – – –
[cíl,] zdroj (FPU SUBtraction) od cíle se odečte zdroj a výsledek tohoto rozdílu se uloží místo cíle implicitní cíl je vrchol zásobníku množina operandů = {(-,mem32real ), (-,mem64real ), (ST(0),ST(i)), (ST(i),ST(0))}
O → C1 (Stack Underflow)
FSUBP cíl, zdroj (FPU SUBtraction and Pop) O → C1 (Stack Underflow) – výsledek rozdílu dvou operandů ze zásobníku se uloží na místo cíle a následně se odebere vrchol zásobníku – množina operandů = {(ST(i), ST(0))} FISUB zdroj (FPU Integer SUBtraction) – od vrcholu zásobníku se odečte zdroj a výsledek se uloží na vrchol zásobníku – množina operandů = {(-,mem16int ), (-,mem32int )}
O → C1 (Stack Underflow)
FSUBR [cíl,] zdroj (FPU SUBtraction-Reversed) – od zdroje se odečte cíl a výsledek tohoto rozdílu se uloží místo cíle – implicitní cíl je vrchol zásobníku – množina operandů = {(-,mem32real ), (-,mem64real ), (ST(0),ST(i)), (ST(i),ST(0))}
O → C1 (Stack Underflow)
FSUBRP cíl, zdroj (FPU SUBtraction-Reversed and Pop) O → C1 (Stack Underflow) – výsledek reverzního rozdílu dvou operandů ze zásobníku se uloží na místo cíle a následně se odebere vrchol zásobníku – množina operandů = {(ST(i), ST(0))} FISUBR zdroj (FPU Integer SUBtraction-Reversed) – od zdroje se odečte vrcholu zásobníku, kam se zároveň uloží výsledek – množina operandů = {(-,mem16int ), (-,mem32int )} FMUL – – –
[cíl,] zdroj (FPU MULtiplication) výsledek součinu cíle se zdrojem se uloží místo cíle implicitní cíl je vrchol zásobníku množina operandů = {(-,mem32real ), (-,mem64real ), (ST(0),ST(i)), (ST(i),ST(0))}
O → C1 (Stack Underflow)
O → C1 (Stack Underflow)
FMULP cíl, zdroj (FPU MULtiplication and Pop) – výsledek součinu cíle a zdroje se uloží do cíle a odebere vrchol zásobníku – množina operandů = {(ST(i), ST(0))}
O → C1 (Stack Underflow)
FIMUL zdroj (FPU Integer MULtiplication) – výsledek součinu cíle s hodnotou z vrcholu zásobníku se uloží na vrchol zásobníku – množina operandů = {(-,mem16int ), (-,mem32int )}
O → C1 (Stack Underflow)
FDIV [cíl,] zdroj – – –
(FPU DIVision)
O → C1 (Stack Underflow)
cíl se podělí zdrojem a výsledek tohoto podílu se uloží místo cíle implicitní cíl je vrchol zásobníku množina operandů = {(-,mem32real ), (-,mem64real ), (ST(0),ST(i)), (ST(i),ST(0))}
FDIVP cíl, zdroj (FPU DIVision and Pop) O → C1 (Stack Underflow) – výsledek podílu dvou operandů ze zásobníku se uloží na místo cíle a následně se odebere vrchol zásobníku – množina operandů = {(ST(i), ST(0))} FIDIV zdroj (FPU Integer DIVision) – vrchol zásobníku se podělí zdrojem a výsledek se uloží na vrchol zásobníku – množina operandů = {(-,mem16int ), (-,mem32int )}
O → C1 (Stack Underflow)
FDIVR [cíl,] zdroj (FPU DIVision-Reversed) – zdroj se podělí cílem a výsledek tohoto rozdílu se uloží místo cíle – implicitní cíl je vrchol zásobníku – množina operandů = {(-,mem32real ), (-,mem64real ), (ST(0),ST(i)), (ST(i),ST(0))}
O → C1 (Stack Underflow)
FDIVRP cíl, zdroj (FPU DIVision-Reversed and Pop) O → C1 (Stack Underflow) – výsledek reverzního podílu dvou operandů ze zásobníku se uloží na místo cíle a následně se odebere vrchol zásobníku – množina operandů = {(ST(i), ST(0))} FIDIVR zdroj (FPU Integer DIVision-Reversed) – zdroj se podělí hodnotou z vrcholu zásobníku, na jejíž pozici je uložen výsledek podílu – množina operandů = {(-,mem16int ), (-,mem32int )}
O → C1 (Stack Underflow)
FABS (FPU ABSolute value) – absolutní hodnota na vrchol zásobníku
O → C1
FCHS (FPU CHange Sign) – změna znaménka hodnoty na vrcholu zásobníku
O → C1
FSQRT (FPU SQuare RooT) – nahradí hodnotu na vrcholu zásobníku za její druhou odmocninu
O → C1 (Stack Underflow)
porovnávací instrukce FPU
– –
používají se podmíněné skoky pro bez znaménkové hodnoty (JA, JB a jejich variace), ačkoliv se porovnávají znaménkoví čísla jedině instrukce FCOMI a FCOMIP nastavují podle svého výsledku přímo příznaky procesoru; u všech ostatních instrukcí musejí být tyto příznaky (kvůli podmíněným skokům) nastaveny podle příznaků FPU (C0, C2 a C3) FCOM zdroj (FPU real COMpare) – porovná vrchol zásobníku se zdrojem – množina operandů = {mem32real, mem64real, ST(i)}
O → C1
FCOMP zdroj (FPU real COMpare and Pop) – porovná zdroj s vrcholem zásobníku a odebere vrchol zásobníku – množina operandů = {mem32real, mem64real, ST(i)}
O → C1
FCOMPP (FPU real COMpare and PoP twice) – porovná dvě hodnoty z vrcholu zásobníku a obě odtud odstraní
O → C1
FICOM zdroj (FPU integer COMpare) – porovná celočíselný zdroj s vrcholem zásobníku – množina operandů = {mem16int, mem32int}
O → C1
FICOMP zdroj (FPU integer COMpare and Pop) – porovná celočíselný zdroj s vrcholem zásobníku a odebere vrchol zásobníku – množina operandů = {mem16int, mem32int}
O → C1
FCOMI zdroj (FPU real COMpare and set EFLAGS) – porovná hodnotu ze zásobníku s vrcholem zásobníku – podle porovnání rovnou nastavuje příznaky procesoru ! – množina operandů = {ST(i)}
O → C1
FCOMIP zdroj (FPU real COMpare, set EFLAGS and Pop) – porovná hodnotu ze zásobníku s vrcholem zásobníku a odebere vrchol zásobníku – podle porovnání rovnou nastavuje příznaky procesoru ! – množina operandů = {ST(i)}
O → C1
FXAM (FPU eXAMination) – prozkoumá hodnotu uloženou na vrcholu zásobníku
znaménkový bit z ST(0) → C1
výsledek porovnání
FTST –
C3
C2
C0
nepodporovaný tvar
0
0
0
NaN (Not a Number)
0
0
1
normální tvar
0
1
0
nekonečno (Infinity)
0
1
1
nula (Zero)
1
0
0
prázdný vrchol zásobníku
1
0
1
denormalizovaný tvar
1
1
0
(FPU TeST) porovná vrchol zásobníku s nulou a podle toho nastaví příznaky C3, C2 a C0
O → C1
výsledek porovnání
C3
C2
C0
ST(0) > 0.0
0
0
0
ST(0) < 0.0
0
0
1
ST(0) = 0.0
1
0
0
unordered
1
1
1
transcendentní instrukce FPU
FSIN (FPU SINe) – vypočítá sinus argumentu z vrcholu zásobníku a místo něj uloží výsledek – argument v absolutní hodnotě nesmí přesahovat hodnotu 263
O → C1 (Stack Underflow), I → C2 (přesah argumentu)
FCOS (FPU COSine) – vypočítá cosinus argumentu z vrcholu zásobníku a místo něj uloží výsledek – argument v absolutní hodnotě nesmí přesahovat hodnotu 263
O → C1 (Stack Underflow), I → C2 (přesah argumentu)
FSINCOS (FPU SINe and COSine) C1 (Stack Underflow/Overflow), I → C2 (přesah argumentu) – vypočítá sinus a cosinus argumentu z vrcholu zásobníku a výsledné hodnoty uloží na zásobník – na vrcholu zásobníku se po skončení instrukce cosinus – argument v absolutní hodnotě nesmí přesahovat hodnotu 263 FPTAN (FPU Partial TANgent) C1 (Stack Underflow/Overflow), I → C2 (přesah argumentu) – vypočítá tangens argumentu z vrcholu zásobníku a místo něj uloží výsledek snížený o 1 – na vrchol zásobníku následně uloží 1 – výsledná hodnota tangensu se získá sečtením dvou hodnot z vrcholu zásobníku FPATAN (FPU Partial Arcus TANgent) O → C1 (Stack Underflow), nedef.: C0, C2, C3 – vypočítá arcus tangens podílu ST(1) a argumentu z vrcholu zásobníku, obě tyto hodnoty odebere ze zásobníku a na jeho vrchol uloží výsledek operace – inverzní instrukce k instrukci FPTAN řídicí instrukce FPU
FINIT (FPU INITialisation) – provede inicializaci FPU – nejdříve zpracuje čekající FPU výjimky – 0x37F → Řídící registr, 0x0F → Známkový registr, 0x00 → ostatních registrů
O → C0, C1, C2, C3
FLDCW zdroj (FPU LoaD Control Word) – načte řídící registr FPU z paměti – množina operandů = {mem16}
nedef.: C0, C1, C2, C3
FSTCW cíl (FPU STore Control Word) – uloží řídící registr FPU na dva byty v paměti – nejdříve zpracuje čekající FPU výjimky – množina operandů = {mem16}
nedef.: C0, C1, C2, C3
FLDENV zdroj (FPU LoaD ENVironment) – načte prostředí FPU (všechny registry FPU kromě zásobníku) z operační paměti – množina operandů = {mem14B, mem28B} – podle režimu procesoru
nedef.: C0, C1, C2, C3
FSTENV cíl (FPU STore ENVironment) – uloží prostředí FPU (všechny registry FPU kromě zásobníku) do operační paměti – nejdříve zpracuje čekající FPU výjimky – množina operandů = {mem14B, mem28B} – podle režimu procesoru
nedef.: C0, C1, C2, C3
FSTSW cíl (FPU STore Status Word) – uloží řídící registr FPU na dva byty v paměti – nejdříve zpracuje čekající FPU výjimky – množina operandů = {AX, mem16}
nedef.: C0, C1, C2, C3
FWAIT (WAIT for FPU) – slouží k synchronizaci práce procesoru a FPU – assembler ji automaticky vkládá na potřebná místa
nedef.: C0, C1, C2, C3
FRSTOR cíl (FPU status ReSTORe) – načte stav FPU (prostředí + zásobník) z paměti – používá se pro Multi-Tasking – množina operandů = {mem94B, mem108B} – podle režimu procesoru
nedef.: C0, C1, C2, C3
FSAVE cíl (FPU status SAVE) – uloží stav FPU (prostředí + zásobník) do paměti a provede jeho inicializaci – používá se pro Multi-Tasking – nejdříve zpracuje čekající FPU výjimky – množina operandů = {mem94B, mem108B} – podle režimu procesoru
nedef.: C0, C1, C2, C3
Ukázkové příklady k půlsemestrální zkoušce… Při řešení příkladů uvažujme, že v datovém segmentu se na pozici udané offsetem c_r nachází číselná hodnota řady a na c_s se nachází číslo sloupce, kde sedíte na zkoušce. mov mov mov mul
AL, c_r BL, c_s CL, 100 CL
add mov div add sub cmp jae add
AH, DL, DL AH, AL, AL, @n1 AL,
BL 60 AL BL 128 128
; AL = 7 = 00000111b ; BL = 26 = 00011010b ; CL = 100 = 01100100b ; AX = AL * CL bez znaménka --\ AL = 10111100b = 188 ; --/ AH = 00000010b = 2 ; AH = AH + BL = 00011100b = 28 ; DL = 60 ; AX / DL; AX = AH:AL = 0001110010111100b = 7356, po dělení: AL = 122, AH = 36 ; AH = AH + AL = 122 + 36 = 158 ; AL = AL − BL = 122 − 26 = 96 ; skoč, pokud je AL >= 128, to není, takže neskáčeme ; AL = AL + 128 = 224
@n1: mov v1, AL
mov mov mov add test jz and
AL, BL, CL, CL, CL, @n2 CL,
mov rol
AH, CL AX, CL
; v1 = 224
c_r c_s BL 5 8 0f7h
; AL = 7 = 00000111b ; BL = 26 = 00011010b ; CL = BL = 26 = 00011010b ; CL = CL + 5 = 31 = 00011111b ; nastaví příznaky jako (CL and 8 = 00001000b), ponechá původní hodnotu v CL ; skoč, pokud ZF = 1, ale poslední instrukce ZF snulovala, a tak nedojde ke skoku ; CL = CL and 0f7h = 00011111b and 11110111b = 00010111b = 23
@n2:
xchg AL, CL or CL, 128 mov v2, CL mov mov mov mov or and
CL, AH, AL, CH, CL, CL,
c_r c_s AH 0 2 3
shr inc inc loop stc rcr mov
AX, 1 AX AX @n3
mov cbw mov mov push push mov pop xor xor xor pop inc xor mov
AL, c_r
; AH = 23 ; rotuj AX o CL (=23) bitů vlevo, AX = AH:AL = 0001011100000111b,… ; … po rotaci 1000001110001011b = 33675 ; vyměň AL a CL, v CL tedy bude 10001011b = 139 ; CL = CL or 128 = 10001011b = 139 ; v2 = 139 ; CL = 7 = 00000111b ; AH = 26 = 00011010b ; AL = 26 = 00011010b, tedy AX = AH:AL = 0001101000011010b = 6682 ; CH = 0 ; CL = CL or 2 = 00000111b or 00000010b = 00000111b ; CL = CL and 3 = 00000011b = 3, CX = CH:CL = 0000000000000011b = 3, následující… ; … cyklus tedy proběhne 3x
@n3:
AL, 1 v3, AL
CH, 0 CL, c_s CX AX [alpha], AX AX AX, [alpha] [alpha], AX AX, [alpha] DX DX AX, DX v4, al
alpha resw 1
; posun AX o 1 bit doprava ; AX = AX + 2 ; po skončení cyklu bude AX = 00000001101000110b ; CF = 1 ; rotace AL o 1 bit doprava přes CF (AL = 01000110b), po rotaci bude AL = 10100011b = 163 ; v3 = 163 ; AL = 7 = 00000111b ; provede znaménkové roztažení akumulátoru; AX = AL = 7 ; CH = 0 ; CL = 26 = 00011010b ; ulož na zásobník obsah registru CX ; ulož na zásobník obsah registru AX ; [alpha] = 7 ; hodnotu z vrcholu zásobníku ulož do AX, takže AX = 7 ; jelikož [alpha] = AX, potom AX = 0 ; [alpha] = [alpha] xor AX, [alpha] = 7 ; AX = AX xor [alpha], AX = 7 ; ulož hodnotu z vrcholu zásobníku do DX, takže DX = 26 ; DX = 27 = 00011011b ; AX = AX xor DX = 00000111b xor 00011011b = 00011100b = 28 ; v4 = 28
|word 26 |..| |word 7 |word 26 |..| |word 26 |..|
|..|
; proměnná v datovém segmentu
Odkazy [cze] Mistic's blog - http://www.mystik.wz.cz/progr/doc/8086nemecek.txt [eng] sandpile.org -- IA-32 architecture - http://www.sandpile.org/ia32/index.htm [eng] The 8086 / 80286 / 80386 / 80486 Instruction Set - http://home.comcast.net/~fbui/intel.html [eng] Intel® 64 and IA-32 Architectures Software Developer's Manuals - http://www.intel.com/products/processor/manuals/
Propojení assembleru s jazykem C Důležité je zapamatovat si, že jazyk C ukládá argumenty na zásobník zprava doleva, což umožňuje použití funkcí s proměnným počtem parametrů. extern void fce (int x, int *pointer);
EBP + 12
// takto vypadá prototyp funkce fce v jazyce C
2. argument
vyšších 16b
int *pointer
nižších 16b
CPU Intel využívají tzv. Little-Endian
1. argument int x
EBP + 8
návratová adresa původní hodnota EIP
EBP
původní hodnota EBP místo na lokální
EBP – 2
proměnnou funkce normálně využívaný zásobník …
SS
někam do této oblasti ukazuje ESP
; >>> DEMONSTRAČNÍ UKÁZKA KÓDU (vztahuje se k ilustraci) – v úvahu berme 32b režim CPU <<< bits 32 global _fce [section .code use32 class=code] _fce: push
EBP
; uložení původního EBP, abychom přes něj mohli přistupovat k argumentům funkce fce ; a lokálním proměnným aniž bychom zničili jeho původní hodnotu
mov
EBP, ESP
; inicializace hodnoty registru EBP pro přístup na zásobník
sub . . . add
ESP, 2
; touto operací jsme si vyhradili na zásobníku 2B (= 16b) pro lokální proměnnou(é)
ESP, 2
; „odstranění“ lokální proměnné ze zásobníku
pop
EBP
; obnovení původní hodnoty EBP těsně před ukončením podprogramu
ret
; v této oblasti kódu můžete normálně využívat zásobníku...
; návrat z podprogramu (funkce) ; obnoví ze zásobníku EIP a pokračuje v provádění programu, který zavolal tento podprogram ; jazyk C nejčastěji používá konvenci volání __cdecl, kdy zásobník uklízí volající funkce
Pokud by jste si chtěli tento příklad kódu doplnit a modifikovat ho, tak si klidně poslužte. Pro překlad assembleru doporučuji nasm. Pro překlad zdrojových textů v jazyce C a linkování s moduly psanými v assembleru doporučuji překladač gcc. Překlad jsem zkoušel pod OS MS Windows XP. nasm -fwin32 -o modul.obj modul.asm gcc -std=c99 -Wall -pedantic -c prog.c gcc -std=c99 -Wall -pedantic prog.o modul.obj Jak jste si mohli všimnout v prototypu, který se píše do jazyka C, chybí počáteční podtržítko. Nejde o žádný překlep ale o zavedenou konvenci. Linker se již postará o spárování všech volání funkce fce s návěštím _fce v modulu psaném v assembleru.