Trochu věcí okolo Neţ budeme moci začít psát vlastní program, musíme si říct pár věcí, bez kterých to asi nepůjde.
2.1) Paměť Sám bych nevěřil, jak je důleţité znát alespoň trochu uspořádání paměti. (Alespoň v programování pro DOS, tedy 16bitové aplikace) Přeskočím věci o maximálním adresovatelném prostoru a různých omezeních v případě reálného módu paměti apod. V dnešní době je to stejně na nic, protoţe dnes je to o něčem trochu jiném. Zaměřím se teď na vysvětlení pojmů segment a offset. Kdysi dávno, v době kdy počítače byly tak trochu ještě v plenkách a procesory se označovaly čísly jako 8088, si někdo řekl, ţe nikdy nebude třeba, aby počítače pouţívali více neţ 1MB paměti . V té době se to všem jevilo jako dostatečná rezerva, a proto se přistoupilo k následující adresaci paměti: 1MB je, jak jistě víte 220. To znamená, ţe abyste mohli poukázat na jakékoli místo v této paměti, potřebujete 20bitovou paměť, v tomto případě registr procesoru (viz. kapitola Registry procesoru). Tehdy neměli o 32bitových registrech ani ponětí a 16bitů bylo očividně málo. Sloţení dvou registrů by dalo 32bitů, tedy adresaci 232 = 4294967296 Byte = 4GB. To bylo na tehdejší dobu zbytečně moc. Proto se vzaly dva registry, které společně tvořili 20bitovou adresu. Nějak takto: 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | ----- Segmentový registr (CS, DS, ES, FS, GS, SS) ------ | | --------- Offsetový registr (SP, BP, SI, DI) ---------- |
Segmentový registr je tedy ta část adresy, která je posunuta o 4 bity doleva a offsetový registr upřesňuje ony čtyři bity, které se do segmentové části nevešly. Absolutní adresu v paměti tedy získáme následovně: Segment * 16 + Offset = 20bitová adresa
0 1 0 0 1 1 0 1 0 0 0 0 0 1 1 0 - - - - - - - 0 1 0 0 1 0 0 0 1 0 0 1 1 0 1 1 ------------------------------------------0 1 0 0 1 0 0 1 1 0 0 0 1 1 1 1 1 0 1 1
Není to jednoduché? Kaţdých 16 byte v paměti začíná nový segment a offsetová část upřesňuje byte, který máme namysli. Absolutní adresa se tedy rovná Segment * 16 + Offset a udává se ve tvaru SEGMENT:OFFSET. Všimněte si, ţe na jednu a tutéţ absolutní adresu můţe ukazovat několik různých kombinací Segmentu a Offsetu. Je to samozřejmě způsobeno tím, ţe se tato čísla překrývají. A teď pro zajímavost. Jaké maximální číslo můţete pomocí kombinace Segment:Offset můţete dostat? Víte, ţe segment i offset jsou 16bitová čísla, tedy nejvyšší hodnota obou je 2 16 neboli 65536. A teď trochu výpočtů: 65535*16 + 65535 = 1114095. Pokud si vzpomenete 1MB má ale pouze 1048576 byte. Ta trocha paměti, co máte s tímto způsobem adresace navíc se nazývá "HIGH MEMORY". (V DOSovských dobách jste museli mít ovladač HIMEM.sys abyste měli tuto část přístupnou.) To samozřejmě umět nemusíte. Uvádím to pouze abyste o tom prostě věděli.
2.2) Režimy procesoru To, co jste se dozvěděli o paměti v předcházejících odstavcích není vţdy úplně pravda. A pokud se ptáte proč tak abych vám odpověděl vrátíme se trochu zpět do minulosti. Představte si doby, kdy procesory byly procesory ještě malé a moc toho neuměly. Poté přišly nové a konstrukčně lepší. Samozřejmě spousty programů bylo psáno pro ty staré a tak by si nový procesor moc lidí nekoupilo. Proto byly ty novější procesory vybaveny takovou vychytávka, která se nazývá reţimy procesoru. Rozeznáváme 2 reţimy procesoru: Real mode. Neboli reálný reţim procesoru Protected mode. Neboli chráněný reţim procesoru Co to znamená? Ţe lepší procesor, například Pentium, pokud je přepnuto do Real módu se chová, úplně stejně jako například stařičký procesor 8086. Pokud ovšem je procesor přepnut do Protected módu, uţ si můţete vychutnat všechny nové a vylepšené funkce
onoho procesoru. No a jaký je konkrétní rozdíl mezi těmito módy? Tak začněme ve stručnosti Realným módem: Realný mód procesoru Co je to nejdůleţitější, v prvních fázích této učebnice se budeme zabývat právě programováním v reálném módu procesoru. V tomto módu je maximální adresovatelná kapacita programu maximálně 1MB. Pouţívají se pro její adresaci výše zmíněné Segmenty a Offsety. Další podrobnosti moţná aţ příště. Chráněný mód procesoru Zde uţ je správa paměti trochu jiná. Pouţívají se mechanismy, které se nazývají segmentace a stránkování. Dále je moţno adresovat aţ 4GB (232 byte) a pár dalších věcí, které nejsou pro nás momentálně podstatné.
2.3) Registry Registr je jakási rychlá paměť, která je přímo v procesoru a kterou budete hojně vyuţívat. U šestnáctibitových počítačů (286) měly registry velikost 16bitů a od 386ek je velikost registrů 32bitů. S lepšími procesory vzrůstá i počet registrů, ale pro nás jsou důleţité pouze tyto.
2.3.1) Obecné registry - AX, BX, CX, DX Tyto registry jsou nejpouţívanější a mají 16bitů. Ve skutečnosti jsou ale 32bitové. Potom je označujeme jako EAX, EBX, ECX, EDX. Názvy registrů prý nejsou takovéto pouze náhodou. (Někdo si asi pohrál a vymyslel čtyři abecedně po sobě jdoucí slova, kterými nazval ony registry.) AX - Accumulator, neboli střádač BX - Base, neboli bázový registr CX - Counter, neboli počítač cyklů DX - Data, neboli datový registr. Jak uţ bylo řečeno, registry jsou 32bitové, pod označením AX, BX, CX, DX je přístupných pouze 16 dolních bitů. Ty ještě rozdělujeme na AH, AL, BH, BL, CH, CL, DH a DL, neboli horních (high ) 8 bitů a dolních (low) 8 bitů. Ţe je to trochu nesrozumitelné? Tak snad pomůţe obrázek. Takto je rozdělen registr EAX: čísla bitů:
31 15 7 0 | --------------- EAX ---------------- | |------- AX ------ | |-- AH --||-- AL --|
AL je tedy spodních 8bitů registru EAX, AH je druhých 8bitů a AX je AH a AL dohromady. Změníme-li tedy hodnotu AX, změníme tím zákonitě i AH a AL. Dejme tomu, ţe zadáme: AH:= FFH AL := 00H Potom hodnotu AX získáme sloţením AH a AL. AX = FF00H Dejme tomu, ţe chci aby: AH = 35H AL = 09H Lze to udělat v jenom řádku a to změnou celého AX: AX := 3509H
2.3.2) Obecné registry - SP, BP, SI, DI Platí pro ně to samé jako pro registry AX, BX, CX, DX. Jsou 32bitové (pokud je před nimi ještě E) a SP, BP, SI, DI jsou jejich 16bitové části. Tyto registry se pouţívají pro uloţení offsetu (viz výše). SP - obsahuje offset adresy vrcholu zásobníku. (Stack Pointer) BP - určen pro ukládání offsetu při práci se zásobníkem (Base Pointer) SI - určen pro uloţení offsetu zdroje (Source Index)
- určen pro uložení offsetu cíle (Destination Index) U těchto registrů se nedostanete přímo k 8bitové části. (Nemají žádné SL a SH apod.) DI
2.3.2) Segmentové registry - CS, DS, ES, FS, GS, SS Tyto registry jsou 16bitové. Pouţívají se pro uloţení segmentové části. CS - Code Segment. Segment kódu programu. Nelze přímo číst ani do něj zapisovat. DS - Data Segment. Segment dat programu. (U *.com programu platí CS=DS=ES - dá se předělat) ES - Extra segment FS - Volné použití. GS - Volné použití SS - Segment zásobníku. (SS:SP - kompletní adresa na vrchol zásobníku.) Pozor!: Všechny segmentové registry nelze přímo měnit.
2.3.3) Speciální registry - IP a FLAGS Tyto registry jsou kaţdý čímsi zvláštní. IP - Instruction Pointer. Ukazuje offset právě vykonávané instrukce. Přímo nelze měnit, ale oklikou ano. (CS:IP je kompletní adresa právě vykonávané instrukce.) FLAGS - VELMI důležitý registr. Je 32bitový, ale pro naše programování postačí pouze 16bitová část, ostatně jako u všech předešlých registrů. U tohoto registru nás nezajímá přímo hodnota registru, ale hodnota jednotlivých bitů(vlaječek). Ty jsou v něm rozděleny takto (pouze dolních 16bitů): čísla bitů:
15 0
14 NT
13-12 IOPL
11 OF
10 DF
9 IF
8 TF
7 SF
6 ZF
5 0
4 AF
3 0
2 PF
1 1
0 CF
Bity mají následující význam: OF - Overflow Flag. Vlajka přetečení. Nastaví se na 1, je-li výsledek aritmetické operace větší neţ cílová hodnota DF - Direction Flag. Řídí směr zpracovávání řetězových operací. Při hodnotě 1 se řetězce zpracovávají odpředu. (SI a DI se zvyšuje). Je-li tento příznak (bit, vlaječka, jak chcete) na 0 postupuje se naopak. Nuluje se instrukcí CLD. Nastavuje STD. IF - Interrupt Flag. Je-li 1 je povoleno přerušení (Mimo nemaskovaného přerušení viz dále), jinak je přerušení zakázáno. Nastavuje se instrukcí CLI - nulování. STI - nastavení. TF - Trap Flag. Vyuţívá se při krokování. Je-li tento příznak nastaven volá se po kaţdé instrukci INT 3. SF - Sign Flag. Je to samé jako nejvyšší bit výsledku. Pro záporná čísla platí SF=1. ZF - Zero Flag. Jeli výsledek operace 0, ZF=1. AF - Auxiliary Carry Flag. Pomocná vlajka přenosu. Rozšíření CF. Pokud je 1, došlo k přenosu z dolní poloviny 8 nebo 16bitového čísla. PF - Parity Flag. Hodnota tohoto příznaku je 1 pokud dolních 8 bitů právě provedené operace obsahuje sudý počet "1". CF - Carry flag. Vlajka přenosu. Slouţí pro přenos mezi čísly o více slovech. Tak to je stručně o registrech prozatím vše.
2.4) Přerušení Přerušení je signál, který procesoru vyšle nějaký hardware nebo program, aby si zabral procesor pro sebe. Krásným příkladem je stisk klávesy na klávesnici. Mikroprocesor musí přerušit svou činnost, aby udělal to, co má stisk klávesy udělat. Rozeznáváme tři typy přerušení. Hardwarové, softwarové a nemaskované (NMI - Not Masked Interrupt). Hardwarové přerušení vyvolává nějaké zařízení v počítači. Disk (Int 13H), klávesnice při stisku klávesy (Int 9) apod. V případě, ţe je IF, neboli vlaječka přerušení nastavena na 1, procesor přeruší svou činnost a zpracuje událost, kvůli které je přerušení voláno. Softwarové přerušení je vyvoláno programem. Programátor si toto přerušení musí napsat do svého programu pomocí instrukce INT. Díky těmto přerušením si programátor v assembleru můţe vychutnat komfort (poznáte o čem mluvím) funkcí DOSu (přerušení Int 21H) BIOSu (Int 15H) atd. Poslední přerušení se vlastně ani mezi druhy přerušení nezařazuje. Je to jediné přerušení, které nelze zakázat nastavením příznaku (vlaječky) IF na 0. Jedná se totiţ o přerušení vyvolané výpadkem proudu, ranou kladivem do procesoru, vyhozením z okna apod.
Všechny moderní mikroprocesory mají takzvaný vektorový systém přerušení. To znamená, ţe kaţdé přerušení má své číslo. Na určitém místě v paměti je uloţena tabulka vektorů přerušení. Vektor přerušení, který je dán číslem přerušení se "podívá" do té tabulky, ve které je zapsána adresa podprogramu, který má ono přerušení zpracovat. (Výhodou této tabulky je, ţe si tuto adresu můţete změnit a zpracovávat si takováto přerušení sami.) Pro ilustraci; v praxi to vypadá tak, ţe N-té přerušení spustí tedy přes N-tý vektor N-tý podprogram, který přerušení zpracuje.
2.5) Překladač Bez něčeho, co nám přeloţí náš zdrojový kód do spustitelného programu bychom se asi neobešli. Takţe potřebujeme nějaký kompilátor, který bude vytvářet *.com soubory. Osobně doporučuji TASM a TLINK. Najdete je klasicky s Turbo Pascalem nebo Borland C. Téţ si je můţete stáhnou na mých stránkách, nebo kdekoli jinde. Pro správnou kompilaci (do *.com) je nutné zadat do příkazové řádky následující příkazy: TASM Název_Vašeho_Souboru.asm /z TLINK Název_Vašeho_Souboru.obj /t /x Pokud chcete do (*.exe) Stačí pustit Tasm a Tlink a ty vám jiţ vypíšou podrobnější nápovědu. Jinak assembler můţete psát v poznámkovém bloku, protoţe ţádné prostředí ve kterém by se dal programovat neexistuje. Soubor nazvěte třeba "Hello.asm". Napište kód, který chcete. Soubor uloţte. Do příkazové řádky zadejte výše uvedené parametry a měli byste mít spustitelný soubor na světě. Pro ulehčení doporučuji si vytvořit dávkový soubor, nebo si stáhněte
ASMEditor.
Y1) A co tam vlastně je ? Předpokládám, ţe uţ jste opustili stádium: "Co je to to nahoře? Jakţe? To není televizor? Vţdyť to tak hezky bliká. Jo monitor!! A co to vlastně je ten monitor?", přesto začnu skoro od začátku. Popisem monitoru, klávesnice reproduktorů apod. se nebudeme zabývat a podíváme se do oné záhadné bedýnky. Co se tam nachází? Základní deska obsahuje: o o o o o o o
CPU (Central procesor unit) - hezky česky procesor BUS - sběrnice Přídavné sloty ROM paměť a CMOS paměť Sloty na paměť Konektory Spousty cestiček a malých destiček
Rozšiřující karty Zdroj Disketová mechanika Pevný disk CD-ROM mechanika Větráky
Obrázek motherboardu + popisky
A k čemu tam všechno vlastně je? Začněme, jak jinak, neţ od začátku.
1) Základní deska 1.1 Procesor Provádí veškeré výpočetní operace počítače. Můţeme se setkat s různými druhy procesorů. Nejznámější jsou od firmy Intel. Ten vyrobil velké mnoţství procesorů. Například 80286, 8086, 80486 a Pentium I, II, III, a IV. Ovšem na trhu jsou i jiné značky které vyrábějí výkonné procesory a v případě firmy AMD se jim to úspěšně daří. Výkonnost CPU značně ovlivňuje výkonnost celého počítače. Výkon procesoru ovlivňuje: a) Takt, neboli rychlost CPU. Jako kdyţ klavírista hraje do taktu, i procesor počítá "do taktu". Takt určují hodiny CPU. Ty tikají obvykle milionkrát aţ miliardkrát za sekundu. Takt se proto udává v megahertzích. (Mhz), či
nově v gigahertzích (Ghz). 1MHz je milion "tiknutí" za sekundu, 1 Ghz je miliarda "tiknutí" za sekundu. b) Účinnost mikrokódu . Kaţdý procesor má své vlastní instrukce (obvykle jde o aritmetické operace jako +,-,*,/, posuny čísel, ukládání registrů a mnoho jiných, ale o těch aţ později), které dokáţe vykonávat. Jejich vyšší účinnost zvyšuje výkon procesoru. Např. vynásobení dvou čísel můţe trvat 10 taktů, nebo jenom 5, coţ zrychlí procesor o 50%, i kdyţ rychlost procesoru bude stejná. To jak moc času to procesoru zabere záleţí právě na tom, jak účinně a na kolik taktů dokáţe ta dvě čísla vynásobit. c) Šířka slova. Největší číslo, které je procesor schopen zpracovat během jedné operace. Udává se v Bitech. Obvykle se pohybuje na 64-Bitech, starší počítače mají méně. Pro pochopení jiţ zmiňovaný příklad s násobením. Výsledek příkladu 8*5 řeknete prakticky okamţitě. Ale 18*13 vám vezme mnohem víc času. Nejdříve vynásobíte 10*13, poté 8*13 a výsledky sečtete. Teprve potom řeknete 234. Stejně pracuje i procesor. Je-li 64-Bitový (Pentium I a výše), zpracovává maximálně číslo o hodnotě 264 . Můţe proto sečíst dvě 64-bitová čísla během jedné operace. Zatímco procesor 32-bitový (80386DX a všechny 80486) čísla samozřejmě také sečte, ale trvá mu to déle, neboť si je musí rozdělit na 32-bitová čísla. d) Šířka toku dat. Je sice moc hezké, kdyţ počítač dokáţe zpracovat 32-bitů najednou, ale je to vcelku k ničemu, kdyţ k němu přicházejí čísla po 16-bitech. (pokud je šířka sběrnice procesoru je jen 16 bitů, pokud je šířka 32-Bitů, "běhají" po ní čísla 32-bitová).Chceme-li poslat procesoru číslo větší neţ 65536 (216) po 16-bitové sběrnici, zabere tento proces dva takty, zatímco na 32-bitové pouze jeden (pokud je číslo menší nebo rovno 2 32). Šířka toku dat je tedy největší číslo, které se dá přesunout během jedné operace. e) Maximum RAM paměti. Kolik paměti můţe počítač pouţívat. Je to spíše problém starších počítačů, které nedokázaly adresovat více neţ 1MB (Intel 8088, 8086 …), 16MB (80286) a to ještě jen po 64kb. Dnešních počítačů se jiţ tento problém prakticky netýká. 1.2 BUS, neboli sběrnice Představte si následující situaci: V okolí se začne rozvíjet několik měst. Vyvstanou poţadavky tyto města navzájem pospojovat. Jaké máme moţnosti, jak to udělat? Asi nejjednodušší by bylo spojit kaţdé město s kaţdým. Asi takhle:
Metody adresace Místo (offset) v paměti označuje vždy určitá hodnota zapsaná v hranatých závorkách. Instrukce MOV BYTE PTR ES:[$100F], 10 znamená: na adresu slabiky offset 100F ($ označuje použití hexadecimální soustavy) v segmentu určeném adresou v ES, dosaď hodnotu 10. Jestliže segment nespecifikujeme označením a dvojtečkou, vztahuje se adresa k segmentu v DS. V praxi by tato metoda omezovala programátora v rozletu. Proto ASM86 umožňuje i další metody adresace. Ale popořadě . . . Přímá adresa MOV AH, ES:[$1A40] - do registru AH předej 8 bitů z adresy určené ES a číslem Tuto metodu použijeme, jestliže předem víme adresu hledaného místa v paměti. Na pomoc v Turbo Pascalu jsou operátory: o OFFSET proměnná - vrací offsetovou adresu proměnné o SEG proměnná - vrací segmentovou adresu proměnné (pro globální proměnné vrací vždy obsah DS) Jejich použití umožní zjistit adresu proměnných deklarovaných v části var (const . . .). Příklad: var promenna: byte; begin asm MOV BYTE PTR [offset promenna], 10 {na adresu slabiky proměnné dosaď 10} end; end. Segmentová adresa se v tomto příkladu nemusí určit. Je v DS, a ten se nemusí uvádět. Překladač Pascalu tuto metodu používá i pro naše globální proměnné. Při překladu je totiž každé proměnné přiděleno místo v paměti s pevnou offsetovou adresou (takže zápis OFFSET proměnná nese právě tuto adresu). Specifikace, jestli se jedná o slabiku, nebo slovo, je nutná, protože jinak by procesor nevěděl, jestli má číslem obsadit jednu, nebo dvě slabiky.
Nepřímá adresa MOV AH, ES:[BX] - do registru AH předej obsah pam. místa specifikovaného adresou v BX Pozor! Do registru AH je uložen obsah v paměti na adrese v BX, ne obsah registru BX. Offsetová část adresy je uložena v některém z adresových registrů BX, BP, SP, SI, DI. Vzhledem k tomu, že obsah těchto registrů můžeme měnit, použijeme tuto metodu v případě pohybu po paměti. Příklad: var promenna: byte; begin asm MOV BX, offset promenna {do BX dosaď adresu proměnné} MOV BYTE PTR [BX], 10 {na její adresu dosaď hodnotu 10} end; end. Bázová adresa MOV AH, [BX + adresa] - k registru BX přičti konstantu adresa, výsledná hodnota je adresou odkud se má načíst do registru AH Bázová adresa se tvoří s pomocí obsahu jednoho z bázových registrů BP, BX. Výraz v závorce se vyhodnotí, přitom označení registrů zastupuje jejich obsahy. Tento druh adresy používáme při zjišťování hodnot parametrů určených pro podprogramy (případně k přístupu k lokálním proměnným). Indexovaná adresa MOV AH, ES:[adresa + SI] <=> (je shodné) MOV AH, adresa[SI] - registr SI sečti s konstantou adresa, výsledek je hodnota adresy offsetu do paměti Tento způsob adresace je obdobou předchozí tvorby adresy. Používá se však při práci s bloky v paměti. Zde jsou k dispozici indexové registry SI, DI. Příklad: var pole: array [0..9] of byte; begin asm MOV SI, 0 {nuluj registr SI} MOV BYTE PTR offset pole[SI], 10{adr. pole sečti s SI a dosaď 10} end; end. Program dosadí na první místo pole hodnotu. Protože registr SI můžeme zvyšovat, budeme tímto způsobem realizovat pohyb v poli. Kombinovaná adresa báze + index MOV AH, [BX + SI] <=> MOV AH, [BX][SI] - obsahy registrů BX a SI sečti, výsledek je hodnota offsetu odkud se má číst Kombinovaná adresa umožňuje pracovat s adresou, která se skládá ze součtu dvou registrů (jednoho bázového BX, BP a jednoho indexového SI, DI). Příklad: var pole: array [0..9] of byte; begin asm MOV BX, offset pole {do registru BX dosaď adresu pole} MOV SI, 0 {do registru SI dosaď 0} MOV BYTE PTR [BX][SI], 10 {na první prvek v poli ulož 10} end; end. Kombinovaná adresa přímá + báze + index MOV AH, [adresa + BX + SI] <=> MOV AH, adresa[BX][SI] - sečti registry BX, SI a přičti hodnotu adresa, výsledek je hodnota offsetu Tuto adresaci použijeme například při práci s hlavičkovými soubory. Bázový registr nastavíme na počátek bloku paměti vyčleněného k uložení souboru. Indexový registr vynulujeme. Konstantní hodnota (adresa) může být rovna délce hlavičky. Zvyšováním hodnoty v indexovém registru se pohybujeme v datech hlavičkového souboru. Další možné použití této adresace je při pohybu v dvourozměrných polích. Hodnoty v obou registrech jsou indexy pole. Konstantní adresa je adresou počátku pole.