Assembler - 1.část. poslední změna této stránky: Zpět

Podobné dokumenty
ISU Cvičení 3. Marta Čudová

Registry 32 bitové pro všeobecné použití: EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP.

ISU Cvičení 7. Marta Čudová

Petr Krajča. Katedra informatiky Univerzita Palackého v Olomouci. Petr Krajča (UP) KMI/YOS: Přednáška I / 21

Assembler - 5.část. poslední změna této stránky: Zpět

x86 assembler and inline assembler in GCC

Předmluva 13 Použité konvence 14. KAPITOLA 1 Základní číselné soustavy a pojmy Číselné soustavy a převody 15 1.

KATEDRA INFORMATIKY UNIVERZITA PALACKÉHO ASSEMBLER VÝVOJ TOHOTO UČEBNÍHO TEXTU JE SPOLUFINANCOVÁN

Strojový kód k d a asembler procesoru MIPS SPIM. MIPS - prostředí NMS NMS. 32 ks 32bitových registrů ( adresa registru = 5 bitů).

a operačních systémů

Procesor z pohledu programátora

Assembler - 2.část. poslední změna této stránky: Zpět

Assembler - 4.část. poslední změna této stránky: Zpět

Pohled do nitra mikroprocesoru Josef Horálek

Strojový kód. Instrukce počítače

Úroveň strojového kódu procesor Intel Pentium. Adresovanie pamäte

Obsah. Předmluva 13 Zpětná vazba od čtenářů 14 Zdrojové kódy ke knize 15 Errata 15

Struktura a architektura počítačů (BI-SAP) 7

Činnost CPU. IMTEE Přednáška č. 2. Několik úrovní abstrakce od obvodů CPU: Hodinový cyklus fáze strojový cyklus instrukční cyklus

Assembler - 3.část. poslední změna této stránky: Zpět

REbejs. 1. workshop (draft0)

Pointery II. Jan Hnilica Počítačové modelování 17

- dělají se také pomocí #define - podobné (použitím) funkcím - předpřipravená jsou např. v ctype.h. - jak na vlastní makro:

Základní datové typy, proměnné - deklarujeme předem - C je case sensitive rozlišuje malá a velká písmena v názvech proměnných a funkcí

ISU Cvičení 2. Marta Čudová

Assembler x86. Studijní text pro předmět: Strojově orientované jazyky Petr Olivka. Katedra informatiky VŠB-TU Ostrava

Operační systémy 2. Přednáška číslo 1. Úvod do OS

Opakování programování

MSP 430F1611. Jiří Kašpar. Charakteristika

Princip funkce počítače

8. Laboratoř: Aritmetika a řídicí struktury programu

Semestrální práce z předmětu ÚPA MIPS

for (i = 0, j = 5; i < 10; i++) { // tělo cyklu }

Úvod do jazyka C. Ing. Jan Fikejz (KST, FEI) Fakulta elektrotechniky a informatiky Katedra softwarových technologií

Operační systémy. Jednoduché stránkování. Virtuální paměť. Příklad: jednoduché stránkování. Virtuální paměť se stránkování. Memory Management Unit

5 Přehled operátorů, příkazy, přetypování

Architektura počítačů. Instrukce a návrh instrukční sady. Lubomír Bulej KDSS MFF UK

Petr Krajča. 26. říjen, 2012

Simulátory aplikačně specifických instrukčních procesorů Jazyk LISA. Masařík Karel

MIKROPROCESORY PRO VÝKONOVÉ SYSTÉMY

Přednáška. Strojový kód a data. 4. Přednáška ISA J. Buček, R. Lórencz

Semestrální práce z předmětu. Jan Bařtipán / A03043 bartipan@studentes.zcu.cz

Kubatova Y36SAP 8. Strojový kód Jazyk symbolických instrukcí asembler JSA pro ADOP a AVR Kubátová Y36SAP-strojový kód 1

Programování v C++ Úplnej úvod. Peta (maj@arcig.cz, SPR AG )

Adresní mody procesoru

IUJCE 07/08 Přednáška č. 6

Úvod do programovacích jazyků (Java)

Přednáška. Správa paměti II. Katedra počítačových systémů FIT, České vysoké učení technické v Praze Jan Trdlička, 2012

1. lekce. do souboru main.c uložíme následující kód a pomocí F9 ho zkompilujeme a spustíme:

Jako pomůcka jsou v pravém dolním rohu vypsány binární kódy čísel od 0 do 15 a binární kódy příkazů, které máme dispozici (obr.21). Obr.

Paralelní programování

ISU Cvičení 2. Marta Čudová

Úroveň strojového kódu procesor Intel Pentium Úvod

Jak v Javě primitivní datové typy a jejich reprezentace. BD6B36PJV 002 Fakulta elektrotechnická České vysoké učení technické

Popis instrukční sady procesoru ADOP

VÝUKOVÝ MATERIÁL. Bratislavská 2166, Varnsdorf, IČO: tel Číslo projektu

1.1 Struktura programu v Pascalu Vstup a výstup Operátory a některé matematické funkce 5

Reprezentace dat v informačních systémech. Jaroslav Šmarda

Čtvrtek 8. prosince. Pascal - opakování základů. Struktura programu:

Faculty of Nuclear Sciences and Physical Engineering Czech Technical University in Prague

PŘETĚŽOVÁNÍ OPERÁTORŮ

Zápis programu v jazyce C#

ALGORITMIZACE A PROGRAMOVÁNÍ

CZ.1.07/1.5.00/

Principy počítačů a operačních systémů

Assembler RISC RISC MIPS. T.Mainzer, kiv.zcu.cz

Platforma x64 a přechod na 64 bitů. Aleš Keprt Univerzita Palackého, Olomouc

Assembler DRUHÁ ČÁST OBSAH.

Operátory, výrazy. Tomáš Pitner, upravil Marek Šabo

Pole stručný úvod do začátku, podrobně později - zatím statická pole (ne dynamicky) - číslují se od 0

C2115 Praktický úvod do superpočítání

Faculty of Nuclear Sciences and Physical Engineering Czech Technical University in Prague

Object Pascal je přísně typový procedurální jazyk, který umožňuje jak strukturované, tak objektově orientované programování.

Paměť počítače. alg2 1

Základy programování (IZP)

Správné vytvoření a otevření textového souboru pro čtení a zápis představuje

Programovací jazyky. imperativní (procedurální) neimperativní (neprocedurální) assembler (jazyk symbolických instrukcí)

Programování v C++ 2, 4. cvičení

Př. další použití pointerů

Ukazatel (Pointer) jako datový typ - proměnné jsou umístěny v paměti na určitém místě (adrese) a zabírají určitý prostor (počet bytů), který je daný

Maturitní otázky z předmětu PROGRAMOVÁNÍ

Vyučovací hodina. 1vyučovací hodina: 2vyučovací hodiny: Opakování z minulé hodiny. Procvičení nové látky

Algoritmizace a programování

MAXScript výukový kurz

Programovací jazyk Pascal

Jazyk symbolických adres

Programování v C++ 1, 1. cvičení

VÝUKOVÝ MATERIÁL. Bratislavská 2166, Varnsdorf, IČO: tel Číslo projektu

Program převod z desítkové na dvojkovou soustavu: /* Prevod desitkove na binarni */ #include <stdio.h>

Pole a Funkce. Úvod do programování 1 Tomáš Kühr

Architektury CISC a RISC, uplatnění v personálních počítačích

Lekce 6 IMPLEMENTACE OPERAČNÍHO SYSTÉMU LINUX DO VÝUKY INFORMAČNÍCH TECHNOLOGIÍ JAZYK C

Základy programování (IZP)

Algoritmy I. Cvičení č. 2, 3 ALGI 2018/19

Programovací jazyky. imperativní (procedurální) neimperativní (neprocedurální) assembler (jazyk symbolických instrukcí)

Výrazy a operátory. Operátory Unární - unární a unární + Např.: a +b

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Klimatizace. Třída: 4.C. Střední Průmyslová Škola Elektrotechnická Havířov Protokol do MIT. Skupina: 3. Zpráva číslo: 3

1. lekce. do souboru main.c uložíme následující kód a pomocí F9 ho zkompilujeme a spustíme:

int => unsigned int => long => unsigned long => float => double => long double - tj. bude-li:

Transkript:

1 z 13 19.2.2007 7:49 Assembler - 1.část poslední změna této stránky: 9.2.2007 Zpět Vítejte u první části učebních materiálů k Assembleru. Tyto učební texty vznikly na jaře roku 2000 jako doprovodný materiál ke cvičením. Původní texty nebyly určeny k samostudiu, ale jen jako doplňkový text k tomu, co se studenti dověděli na hodinách. Během let jsem Assembler několikrát střídavě učil a neučil v denním i kombinovaném studiu a tyto neustálé změny způsobily i postupné úpravy těchto Současná verze stále nemůže být pokládána za dostačující primární zdroj samostudia, nicméně alespoň je text stále aktualizován, aby reflektoval nové prvky a změny vždy v nejnovější verzi překladače Assembleru. Seznam doporučené studijní literatury (čili něco ke čtení :-) je uveden na hlavní stránce, dole pod nabídkou, ze které jste se zřejmě dostali na tuto stránku. 1. Úvodní seznámení s assemblerem Protože všichni by měli znát C++, nejsnazší cesta k assembleru je ukázat si rozdíly oproti C++. Hned na úvod je také potřeba si říci o nejzákladnější a největší a nejčastější chybě, kterou programátoři v Assembleru dělají: Píší podivné konstrukce, jakoby neznali "normální" styl programování v C++, Pascalu nebo jiném jazyku. Přitom v Assembleru byste měli 90% kódu napsat úplně stejným způsobem jako v jiném programovacím jazyku, pouze to má jinou syntaxi. :-) Pojďme tedy na rozdíly Assembleru oproti C++. Ten základní je už v samotném psaní příkazů. V assembleru se totiž píše každý příkaz na samostatný řádek. Každý takový příkaz má následující podobu: instrukce operand1,operand2. Čili na začátku řádku je příkaz, kterému se říká instrukce a za mezerou jsou parametry, zvané operandy. Počet operandů u příkazu je 0-3. 1. 2. 3. 4. instrukce bez operandu: př.: cld instrukce s 1 operandem - operand se používá jako parametr i pro zápis výsledku: př.: inc eax instrukce se 2 operandy - oba operandy jsou parametry, výsledek se uloží do 1.operandu: add eax,1 instrukce se 3 operandy - 1.operand je vyhrazen pro výsledek, zbylé dva jsou parametry: imul eax,ebx,34 Nejjednodušší zápis assembleru je přímo do kódu C/C++. Většina překladačů C++ to umožňuje, ovšem ne všechny mají stejnou syntaxi. Zde je příklad, který si můžete vyzkoušet ve Visual C++:

2 z 13 19.2.2007 7:49 void main() { _asm { imul eax,ebx,34 add eax,1 inc eax cld Všimněte si červeně vyznačených řádků. Příkazy v assembleru (dále jen asm) se píší do deklarace _asm {... Nutno dodat, že výše uvedený program nemá žádný efekt - je to ukázka, jak se zapisují instrukce se třemi, dvěma, jedním a žádným operandem. V následující tabulce je srovnání konstrukcí a operací v C++ a assembleru. C++ asm operandy název instrukce poznámka = mov 2 move += add 2 add -= sub 2 subtract *= /= mul (u) imul (s) div (u) idiv (s) 1 1,2,3 1 1 multiply divide ++ inc 1 increment -- dec 1 decrement >>= shr (u) sar (s) 2 2 shift right shift arithmetic right u=unsigned s=signed u=unsigned s=signed u=unsigned s=signed <<= shl 2 shift left &= and 2 (bitwise) and = or 2 (bitwise) or ^= xor 2 (bitwise) xor ~= not 1 (bitwise) not - neg 1 negate změní znaménko čísla

3 z 13 19.2.2007 7:49 goto jmp 1 jump return ret label: @@label: - // ; - jednořádková poznámka & offset - reference (vrací pointer na proměnnou) * [ ] - dereference (vrací proměnnou, na kterou ukazuje pointer) *(char*)& byte ptr - (tvrdé) přetypování na 1bajtovou hodnotu *(short*)& word ptr - (tvrdé) přetypování na 2bajtovou hodnotu *(long*)& dword ptr - (tvrdé) přetypování na 4bajtovou hodnotu V tabulce nejsou podmíněné příkazy (if) a cykly (for, while, do). Důvodem je to, že v assembleru se tyto věci dělají úplně jinak než v C++. Pozor! V jedné istrukci můžete pouze jedenkrát adresovat paměť. To znamená, že např. příkaz a=33; zapíšete normálně jako mov a,33, ale příkaz a=b; takto jednoduše napsat nejde. Byly by zde dvě proměnné, čili dvojí adresace paměti v jedné instrukci. Tyto případy jsou poměrně časté a vyřešíte je pomocí registrů (viz následující kapitola). Přitom dvojí přístup do paměti v jedné instrukci je možný, pokud nejde o dvojí adresaci. Např. příkaz a++; se zapíše jednoduše inc a. Tato instrukce je provedena ve třech krocích: 1. CPU přečte z paměti hodnotu proměnné a. 2. CPU zvýší hodnotu o jedničku. 3. CPU zapíše novou hodnotu proměnné a zpět do paměti. Pozn.: Tento příklad taky ukazuje, že inc a není atomická instrukce ve víceprocesorových systémech. Velká a malá písmena se v assembleru standardně nerozlišují, ale lze to zapnout. Pokud chcete kombinovat assembler s C++, bez rozlišování velkých a malých písmen by to ani nešlo. Proto překladače obvykle nabízejí možnost velká a malá písmena rozlišovat jen u exportovaných funkcí, zatímco interně je assembler nerozlišuje. Možné kombinace operandů při násobení a dělení: násobení unsigned: dělení unsigned: mul r/m8 ax=al*op1 div r/m8 al=ax/op1, ah=zbytek

4 z 13 19.2.2007 7:49 mul r/m16 dx:ax=ax*op1 div r/m16 ax=dx:ax/op1, dx=zbytek mul r/m32 edx:eax=eax*op1 div r/m32 eax=edx:eax/op1, edx=zbytek násobení signed: dělení signed: před dělením: imul r/m8 ax=al*op1 idiv r/m8 al=ax/op1, ah=zbytek cbw - rozšíří al->ax imul r/m16 dx:ax=ax*op1 idiv r/m16 ax=dx:ax/op1, dx=zbytek cwd - rozšíří ax->dx:ax imul r/m32 edx:eax=eax*op1 idiv r/m32 eax=edx:eax/op1, edx=zbytek cdq - rozšíří eax->edx:eax imul reg16,r/m16 op1=op1*op2 cwde - rozšíří ax->eax imul reg32,r/m32 op1=op1*op2 vysvětlivky: imul reg16,imm8/16 op1=op1*op2 reg = registr s uvedeným počtem bitů imul reg32,imm8/32 op1=op1*op2 mem = paměť (proměnná) s uvedeným počtem bitů imul reg16,r/m16,imm8/16 op1=op2*op3 r/m = registr nebo paměť s uvedeným počtem bitů imul reg32,r/m32,imm8/32 op1=op2*op3 imm = konstanta (immediate) s uvedeným počtem bitů Pozor! Instrukce násobení se třemi operandy (poslední dva řádky této tabulky) vyžadují, aby třetím operandem byla přímá hodnota (tj. konstntna-číslo, ne proměnná, ne registr). Pokud tam všam napíšete něco neplatného, překladač ve Visual C++ nenapíše chybu. Program však sprvně fungovat nebude. (Je to chyba Visual C++.) 2. Registry Registry jsou paměťové buňky přímo v CPU. Práce s nimi je rychlejší a nemusí se při tom adresovat paměť. Tzn. příklad z předchozího odstavce (příkaz a=b; v C++) můžeme v assembleru napsat pomocí registru eax (je to jeden z mnoha registrů, klidně můžete použít i jiný - záleží především na typu proměnné). mov eax,b mov a,eax A je to. ---

5 z 13 19.2.2007 7:49 Ale registry je potřeba se naučit podrobně. Na přednášce struktury počítačů nebo operačních systémů byste měli probírat všechny podrobnosti. Zde se podívámě na to důležité. Základní aritmetické registry (použitelné pro počítání) jsou eax, ebx, ecx, edx. Adresovací registry jsou esi, edi, ebp, esp. Segmentové registry jsou cs, ds, es, fs, gs, ss. Registry s trojpísmenným jménem e.. jsou 32bitové. Segmenty jsou 16bitové a používají se většinou jen v 16bit programech (tj. v DOSu). Ve Widnows 95/NT (dále jen Win32) a Linuxu je 32bitový kód a segmenty nejsou většinou potřeba, protože paměť je podle potřeby operačním systémem mapována do virtuálního prostoru každého procesu. Podíváme se nejprve na registr eax, totéž platí pro ebx, ecx, edx. eax ax ah al Celý registr eax má 32 bitů (4 bajty). Registr ax odpovídá zápisu (short)eax v C++, je to tedy spodních 16 bitů z registru eax (dva bajty ze čtyř, tedy polovina). Horní polovinu eax takto adresovat nelze. Můžete však příkazem rol eax,16 přehodit horní a dolní polovinu registru. Dále můžete použít čtvrtinu, tj. 8 bitů (čili jeden bajt) pod názvem al. Druhý bajt (horní polovina ax) je přístupný jako ah. Adresovací registry esi, edi, ebp, esp fungují podobně, ovšem nemají 8bitové ekvivalenty. Tzn. jsou to 32bitové registry a mají 16bitové ekvivalenty si, di, bp, sp. Registr ebp má specifickou úlohu při adresování lokálních proměnných, takže ho nebudete ve svých programech nijak zvlášť měnit. Registr esp (stack pointer) ukazuje na vrchol programového zásobníku, takže jeho použití v programu je už prakticky úplně vyloučeno. Další dva jsou volně použitelné registry. 3. Programový zásobník Každý mikroprocesor používá při své činnosti zásobník. Zásobník (datová struktura typu LIFO) je umístěn někde v paměti a na jeho vrchol ukazuje registr esp. Zásobník vždy roste dolů, tj. přidáváním hodnot na zásobník se snižuje hodnota esp. Můžete použít tyto jednoduché instrukce: push op1 - uloží hodnotu op1 na zásobník (vždy 4 bajty) pop op1 - vytáhne hodnotu ze zásobníku a uloží do op1 (vždy 4 bajty) mov op1,[esp] - uloží do op1 hodnotu na vrcholu zásobníku (4 bajty), zásobník se nezmění Zásobník vždy pracuje po 4 bajtech (v 16bitovém režimu v DOSu po 2 bajtech). Teď si můžeme ukázat malý příklad. Co program dělá, zkuste poznat sami.

6 z 13 19.2.2007 7:49 #include <math.h> int pythagoras(int a,int b) { int result; _asm { mov eax,a ;eax=a imul eax,a ;eax*=a mov ebx,b ;ebx=b imul ebx,b ;ebx*=b add eax,ebx ;eax+=ebx mov result,eax ;result=eax return (int)sqrt((double)result); Všechny instrukce mají dva operandy, použité registry jsou (32bitové) eax a ebx. Kvůli tomu, že jsme použili tyto dva registry, C++ je už nemůže použít pro sebe. Kdyby v nich něco na začátku bylo, náš asm program by to zrušil. Proto je vhodné program upravit následujícím způsobem, aby hodnoty registrů byly zachovány. #include <math.h> int pyth(int a,int b) { int result; _asm { push eax push ebx mov eax,a ;eax=a imul eax,a ;eax*=a mov ebx,b ;ebx=b imul ebx,b ;ebx*=b add eax,ebx ;eax+=ebx mov result,eax ;result=eax pop ebx pop eax return (int)sqrt((double)result); Instrukce push a pop pracují se zásobníkem CPU a sdílejí tak paměť s voláním funkce. Pokud tedy zapomenete zavolat pop přesně odpovídající k předchozímu push, program se zhroutí (buďte rádi, že máte Windows NT a nespadne celý počítač...). Pozor! Ve Visual C++ můžete libovolně používat registry eax, ebx, ecx, edx, esi, edi. Překladač sám přidá potřebné instrukce push a pop. V

7 z 13 19.2.2007 7:49 Borland C++ 3.1 (DOS) můžete používat pouze 16bit registry ax, bx, cx, dx. Indexové registry můžete použít, vypnete-li optimalizace. Anebo prostě musíte použít push-pop. Registr ebp se VŽDY používá pro adresování lokálních proměnných, takže ten nemůžete použít nikde, kde jsou nějaké lokální proměnné. Nemáte-li lokální proměnné, můžete ebp použít, ale musíte jej chránit pomocí push-pop. Segmentové registry zatím na nic nepotřebujete, aspoň tedy dodám, že jejich použití je možné jen opatrně (musíte také použít push-pop). 4. Přímé adresování Přímé adresování je tam, kde jsou adresy známé přímo při překladu, tzn. při práci se statickými nebo globálními proměnnými. Přímé adresování je naprosto jasné a velmi se podobná C++. V asm máte ale ještě nějaké možnosti navíc. static int thevar,thevar2; _asm { mov eax,thevar ;eax=thevar mov eax,thevar+4 ;eax=thevar2 mov eax,[offset thevar+4] ;eax=thevar2 Z příkladu je vidět, že názvy proměnných se v asm vyhodnocují vždy jako adresy (jakoby pointery), se kterými ještě můžete provádět základní aritmetické operace. Příklad počítá s tím, že proměnné thevar a thevar2 jsou v paměti bezprostředně za sebou. Proměnné jsou deklarovány static, protože s lokálními proměnnými ještě pracovat neumíme. 5. Nepřímé adresování Jednou ze základních konstrukcí C++ jsou pointery, bez kterých byste mnohé věci ani nemohli napsat. V assembleru tomu odpovídá nepřímé adresování. Zatímco u přímého adresování známe přesnou adresu proměnné již při překladu, u nepřímého adresování máme jen pointer, přesněji řečeno známe vzdálenost proměnné od nějakého pevného bodu. Přímé adresování paměti je tedy práce s běžnými proměnnými. mov eax,thevar ;přímé adresování nějaké globální proměnné jménem thevar mov eax,[thevar] ;totéž jako předchozí řádek

8 z 13 19.2.2007 7:49 Pokud je thevar běžná globální proměnná typu int, pak oba tyto příkazy dělají totéž, čili do eax přiřadí hodnotu thevar. Proč tam jsou ty hranaté závorky: Pomůckou k pochopení, proč se název proměnné píše takto do hranatých závorek, může být představa paměti počítače jako jednoho velkého pole, do kterého přistupujeme pomocí těchto hranatých závorek stejně, jak to děláme ve vyšších programovacích jazycích při práci s poli. Toto srovnání můžete chápat doslova. Za "přímé adresování" pak označujeme situaci, kdy v době překladu programu víme, kde přesně je ona "proměnná" thevar v paměti. To u globální proměnné thevar víme vždy. Do oněch hranatých závorek se tedy při překladu dosadí nějaké číslo, které udává doslova "kolikátý bajt od začátku paměti" chceme načíst do registru eax. Za "nepřímé adresování" označujeme situaci, kdy v době překladu programu ještě přesnou pozici oné "proměnné" nevíme. To platí například pro situace, kdy k datům přistupujeme přes pointery. Mějme tedy nějakou globální proměnnou typu pointer jménem thepointer a zkusme následující kód. mov ebx,thepointer ;ebx=thepointer mov esi,2 ;esi=2 mov al,[ebx+esi] ;ax=*(bx+si)=*(thepointer+2)=thepointer[2] mov ah,thepointer[esi] ;toto nefunguje! Tato pro začátečníky méně jasná konstrukce je příkladem nepřímého adresování. Nejprve si načteme pointer do registru ebx - toto je opět přímé adresování. Jenže místo hodnoty proměnné, na kterou thepointer ukazuje, máme v ebx pouze její adresu. Do registru esi dáme číslo 2. Potom načteme do al hodnotu na adrese ebx+esi, což odpovídá práci s pointerem nebo prostě čtení druhého prvku pole charů, na které ukazuje thepointer. Ano, první řádky výše uvedeného kódu odpovídají tomu, když ve vyšším jazyku máme pointer na pole charů a přečteme z něj buňku pomocí thepointer[2]. Poslední řádek ukazuje alternativní možnost, kdy adresa pointeru není načtena do ebx. Tento příkaz je neplatný, protože má dvě adresování paměti. Nejprve požaduje načíst obsah thepointer a potom ještě obsah vypočítané adresy thepointer+esi. Toto je tedy příklad dvojí adresace paměti v jedné instrukci, o kterém již víme, že prostě není možná. Instrukce lea lea reg,address "load effective address" Instrukce lea vypočítá efektivní adresu a její hodnotu uloží do nějakého registru. Čili třetí řádek posledního příkladu můžete nahradit dvojicí:

9 z 13 19.2.2007 7:49 lea ebx,[ebx+esi] ;base=ebx, index=esi, scale=1 mov al,[ebx] ;base=ebx V uvedeném příkladě to samozřejmě nemá smysl, jelikož by stačilo napsat add ebx,esi, ale při složitějším adresování může být použití lea výhodné. Podíváme se tedy na to, co všechno lze v adresování využít. Obecný tvar nepřímé adresy je base+(index*scale)+displacement kde scale je konstanta (může být pouze 1, 2, 4, 8). Jednotlivé položky adresy jsou pochopitelně nepovinné, viz. poznámky u výše uvedeného příkladu. Pozor! V 16bitovém řežimu (v DOSu) máte ovšem daleko horší situaci: bx+index+displacement, kde index může být jedině si nebo di. Všechny položky jsou pochopitelně nepovinné. Instrukci lea můžete také použít pro jednoduché násobení. Takové násobení je nejrychlejší možné rychlé (1 takt). Násobit můžete 2x, 3x, 4x, 5x, 8x, 9x. lea eax,[eax+8*eax] ;eax=9*eax (unsigned) Při práci s nepřímými adresami musíte dávat pozor na datové typy. V C++ takové problémy nejsou: long pole[20]; long *pointer=pole+12; //pointer na prvek pole[12] _asm { lea ebx,[pole+12] //ebx=pointer na prvek pole[3] = 12 bajtů za začátkem pole mov eax,[ebx] //eax=pole[3] Co z toho plyne? Při nepřímém adresování pokud možno využívejte násobení konstantami vždy, když typ prvku pole je short nebo long. long pole[20]; _asm { mov esi,3 lea ebx,[pole+esi*4] //ebx=pointer na prvek pole[3] = 12 bajtů za začátkem pole lea ebx,[pole+3*4] //totéž mov eax,[ebx] //eax=pole[3] Na obrázku vidíte oficiální popis výpočtu efektivní adresy: offset=base+(index*scale)+displacement. Uvedené hodnoty scale 1,2,3,4 jsou

10 z 13 19.2.2007 7:49 bohužel chybné - správně má být 1,2,4,8 (jak vidět i mistr tesař se někdy utne - obrázek je z "Intel Architecture Software Developer's Manual"). Další obrázek ukazuje seznam datových typů podporovaných procesory x86 a jejich umístění v paměti (tzv. LSB = lowest significat byte first - důležité u vícebajtových proměnných). Proměnné mohou začínat na libovolné adrese, ovšem pokud budou patřičně zarovnány, vykonávaný program se tím výrazně zrychlí. Všechny tyto vlastnosti se 100% shodují s chováním C++ a dalších vyšších programovacích jazyků, proto nebudu zacházet do podrobností.

11 z 13 19.2.2007 7:49 Poznámka: Ačkoliv procesory Pentium a novější jsou hardwarově 64bitové, pouze malá část instrukční sady je skutečně 64bitová a většina instrukcí pracuje se 32 nebo méně bity. Tento zdánlivý paradox je způsoben tím, že činnost RISC jádra je nezávislá na činnosti 64bitové paměťové jednotky (na obrázku jako "bus interface unit"). Viz následující velmi stručný diagram:

12 z 13 19.2.2007 7:49 6. Disassembler Vaše dotazy často zodpoví disassembler. Disassembler je opak assembleru - je to program, který převádí binární strojový kód do symbolického tvaru v assembleru.

13 z 13 19.2.2007 7:49 Napište příkaz ve Visual C++, potom ho spusťte pomocí F10 a stiskem Ctrl+Alt+D zobrazte dissassembly window (dříve Alt+8). Můžete se tak například podívat, jak se pracuje s poli. Uvědomte si ale, že pokud nekompilujete váš program s optimalizacemi (release build), tak často uvidíte poměrně krkolomný kód, který byste správně v assembleru nikdy neměli napsat (dlouhý a pomalý). 7. Inline a external assembler Inline assembler je ten, který píšete do souborů C++ (nebo jiných vyšších jazyků). External assembler je ten, který je v samostatných souborech s příponou ASM. Zatím se budeme věnovat pouze inline assembleru, protože se tím výhodně vyhneme některým potížím. Ukázka malého dema v assembleru je tady (100 řádků) a zkompilovaný program je tady (200 bajtů). Je to program pro MS-DOS, takže ve Windows nemusí běžet stoprocentně dobře. Zpět